diff --git a/Code/.DS_Store b/Code/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..8f7b3328c74ea79382b39f6d9b3a74f32c351f55
Binary files /dev/null and b/Code/.DS_Store differ
diff --git a/Code/VisIVODesktop-vtk6_qt5.pro b/Code/VisIVODesktop-vtk6_qt5.pro
new file mode 100644
index 0000000000000000000000000000000000000000..777be4a5e60508aed208d308eab108e3623acb14
--- /dev/null
+++ b/Code/VisIVODesktop-vtk6_qt5.pro
@@ -0,0 +1,251 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2015-05-18T11:03:07
+#
+#-------------------------------------------------
+
+QT       += core gui network printsupport xml  widgets concurrent webkitwidgets
+#CONFIG   += static
+QMAKE_MAC_SDK = macosx10.13
+CONFIG-=app_bundle
+
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
+
+TARGET = VisIVODesktop-vtk6_qt5
+TEMPLATE = app
+
+
+ICON = logo.icns
+#win32:RC_ICONS += your_icon.ico
+
+
+INCLUDEPATH +=   /opt/vtk6.20/include/vtk-6.2/
+INCLUDEPATH +=   /opt/cfitsio3_1_0/include/ \
+                /opt/local/include \
+                /opt/boost_1_63_0/include/
+
+INCLUDEPATH += /Users/fxbio6600/OACT/VisIVOServer_svn_locale/branches/2.3/
+INCLUDEPATH +=/opt/hdf5-1.10.0-patch1/include
+LIBS  +=  /opt/cfitsio3_1_0/lib/libcfitsio.a
+
+
+LIBS += -L/opt/vtk6.20/lib/
+LIBS += -lvtkChartsCore-6.2 -lvtkCommonColor-6.2 -lvtkCommonComputationalGeometry-6.2 -lvtkCommonCore-6.2 -lvtkCommonDataModel-6.2 -lvtkCommonExecutionModel-6.2 -lvtkCommonMath-6.2 -lvtkCommonMisc-6.2 -lvtkCommonSystem-6.2 -lvtkCommonTransforms-6.2 -lvtkDICOMParser-6.2 -lvtkDomainsChemistry-6.2 -lvtkFiltersAMR-6.2 -lvtkFiltersCore-6.2 -lvtkFiltersExtraction-6.2 -lvtkFiltersFlowPaths-6.2 -lvtkFiltersGeneral-6.2 -lvtkFiltersGeneric-6.2 -lvtkFiltersGeometry-6.2 -lvtkFiltersHybrid-6.2 -lvtkFiltersHyperTree-6.2 -lvtkFiltersImaging-6.2 -lvtkFiltersModeling-6.2 -lvtkFiltersParallel-6.2 -lvtkFiltersParallelImaging-6.2 -lvtkFiltersProgrammable-6.2 -lvtkFiltersSMP-6.2 -lvtkFiltersSelection-6.2 -lvtkFiltersSources-6.2 -lvtkFiltersStatistics-6.2 -lvtkFiltersTexture-6.2 -lvtkFiltersVerdict-6.2 -lvtkGUISupportQt-6.2 -lvtkGUISupportQtOpenGL-6.2 -lvtkGUISupportQtSQL-6.2 -lvtkGUISupportQtWebkit-6.2 -lvtkGeovisCore-6.2 -lvtkIOAMR-6.2 -lvtkIOCore-6.2 -lvtkIOEnSight-6.2 -lvtkIOExodus-6.2 -lvtkIOExport-6.2 -lvtkIOGeometry-6.2 -lvtkIOImage-6.2 -lvtkIOImport-6.2 -lvtkIOInfovis-6.2 -lvtkIOLSDyna-6.2 -lvtkIOLegacy-6.2 -lvtkIOMINC-6.2 -lvtkIOMovie-6.2 -lvtkIONetCDF-6.2 -lvtkIOPLY-6.2 -lvtkIOParallel-6.2 -lvtkIOParallelXML-6.2 -lvtkIOSQL-6.2 -lvtkIOVideo-6.2 -lvtkIOXML-6.2 -lvtkIOXMLParser-6.2 -lvtkImagingColor-6.2 -lvtkImagingCore-6.2 -lvtkImagingFourier-6.2 -lvtkImagingGeneral-6.2 -lvtkImagingHybrid-6.2 -lvtkImagingMath-6.2 -lvtkImagingMorphological-6.2 -lvtkImagingSources-6.2 -lvtkImagingStatistics-6.2 -lvtkImagingStencil-6.2 -lvtkInfovisCore-6.2 -lvtkInfovisLayout-6.2 -lvtkInteractionImage-6.2 -lvtkInteractionStyle-6.2 -lvtkInteractionWidgets-6.2 -lvtkNetCDF-6.2 -lvtkNetCDF_cxx-6.2 -lvtkParallelCore-6.2 -lvtkRenderingAnnotation-6.2 -lvtkRenderingContext2D-6.2 -lvtkRenderingContextOpenGL-6.2 -lvtkRenderingCore-6.2 -lvtkRenderingFreeType-6.2 -lvtkRenderingFreeTypeOpenGL-6.2 -lvtkRenderingGL2PS-6.2 -lvtkRenderingImage-6.2 -lvtkRenderingLIC-6.2 -lvtkRenderingLOD-6.2 -lvtkRenderingLabel-6.2 -lvtkRenderingOpenGL-6.2 -lvtkRenderingQt-6.2 -lvtkRenderingVolume-6.2 -lvtkRenderingVolumeOpenGL-6.2 -lvtkViewsContext2D-6.2 -lvtkViewsCore-6.2 -lvtkViewsInfovis-6.2 -lvtkViewsQt-6.2 -lvtkalglib-6.2 -lvtkexoIIc-6.2 -lvtkexpat-6.2 -lvtkfreetype-6.2 -lvtkftgl-6.2 -lvtkgl2ps-6.2 -lvtkhdf5-6.2 -lvtkhdf5_hl-6.2 -lvtkjpeg-6.2 -lvtkjsoncpp-6.2 -lvtklibxml2-6.2 -lvtkmetaio-6.2 -lvtkoggtheora-6.2 -lvtkpng-6.2 -lvtkproj4-6.2 -lvtksqlite-6.2 -lvtksys-6.2 -lvtktiff-6.2 -lvtkverdict-6.2 -lvtkzlib-6.2
+#LIBS += -lvtkChartsCore-6.2 -lvtkCommonColor-6.2 -lvtkCommonComputationalGeometry-6.2 -lvtkCommonCore-6.2 -lvtkCommonDataModel-6.2 -lvtkCommonExecutionModel-6.2 -lvtkCommonMath-6.2 -lvtkCommonMisc-6.2 -lvtkCommonSystem-6.2 -lvtkCommonTransforms-6.2 -lvtkDICOMParser-6.2 -lvtkDomainsChemistry-6.2 -lvtkFiltersAMR-6.2 -lvtkFiltersCore-6.2 -lvtkFiltersExtraction-6.2 -lvtkFiltersFlowPaths-6.2 -lvtkFiltersGeneral-6.2 -lvtkFiltersGeneric-6.2 -lvtkFiltersGeometry-6.2 -lvtkFiltersHybrid-6.2 -lvtkFiltersHyperTree-6.2 -lvtkFiltersImaging-6.2 -lvtkFiltersModeling-6.2 -lvtkFiltersParallel-6.2 -lvtkFiltersParallelImaging-6.2 -lvtkFiltersProgrammable-6.2 -lvtkFiltersSMP-6.2 -lvtkFiltersSelection-6.2 -lvtkFiltersSources-6.2 -lvtkFiltersStatistics-6.2 -lvtkFiltersTexture-6.2 -lvtkFiltersVerdict-6.2 -lvtkGUISupportQt-6.2 -lvtkGUISupportQtOpenGL-6.2 -lvtkGUISupportQtSQL-6.2 -lvtkGUISupportQtWebkit-6.2 -lvtkGeovisCore-6.2 -lvtkIOAMR-6.2 -lvtkIOCore-6.2 -lvtkIOEnSight-6.2 -lvtkIOExodus-6.2 -lvtkIOExport-6.2 -lvtkIOGeometry-6.2 -lvtkIOImage-6.2 -lvtkIOImport-6.2 -lvtkIOInfovis-6.2 -lvtkIOLSDyna-6.2 -lvtkIOLegacy-6.2 -lvtkIOMINC-6.2 -lvtkIOMovie-6.2 -lvtkIONetCDF-6.2 -lvtkIOPLY-6.2 -lvtkIOParallel-6.2 -lvtkIOParallelXML-6.2 -lvtkIOSQL-6.2 -lvtkIOVideo-6.2 -lvtkIOXML-6.2 -lvtkIOXMLParser-6.2 -lvtkImagingColor-6.2 -lvtkImagingCore-6.2 -lvtkImagingFourier-6.2 -lvtkImagingGeneral-6.2 -lvtkImagingHybrid-6.2 -lvtkImagingMath-6.2 -lvtkImagingMorphological-6.2 -lvtkImagingSources-6.2 -lvtkImagingStatistics-6.2 -lvtkImagingStencil-6.2 -lvtkInfovisCore-6.2 -lvtkInfovisLayout-6.2 -lvtkInteractionImage-6.2 -lvtkInteractionStyle-6.2 -lvtkInteractionWidgets-6.2 -lvtkNetCDF-6.2 -lvtkNetCDF_cxx-6.2 -lvtkParallelCore-6.2 -lvtkRenderingAnnotation-6.2 -lvtkRenderingContext2D-6.2 -lvtkRenderingContextOpenGL-6.2 -lvtkRenderingCore-6.2 -lvtkRenderingFreeType-6.2 -lvtkRenderingFreeTypeFontConfig-6.2 -lvtkRenderingFreeTypeOpenGL-6.2 -lvtkRenderingGL2PS-6.2 -lvtkRenderingImage-6.2 -lvtkRenderingLIC-6.2 -lvtkRenderingLOD-6.2 -lvtkRenderingLabel-6.2 -lvtkRenderingOpenGL-6.2 -lvtkRenderingQt-6.2 -lvtkRenderingVolume-6.2 -lvtkRenderingVolumeOpenGL-6.2 -lvtkViewsContext2D-6.2 -lvtkViewsCore-6.2 -lvtkViewsInfovis-6.2 -lvtkViewsQt-6.2 -lvtkalglib-6.2 -lvtkexoIIc-6.2 -lvtkexpat-6.2 -lvtkfreetype-6.2 -lvtkftgl-6.2 -lvtkgl2ps-6.2 -lvtkhdf5-6.2 -lvtkhdf5_hl-6.2 -lvtkjpeg-6.2 -lvtkjsoncpp-6.2 -lvtklibxml2-6.2 -lvtkmetaio-6.2 -lvtkoggtheora-6.2 -lvtkpng-6.2 -lvtkproj4-6.2 -lvtksqlite-6.2 -lvtksys-6.2 -lvtktiff-6.2 -lvtkverdict-6.2 -lvtkzlib-6.2
+#LIBS += -L/Users/fxbio6600/OACT/develop/VisIVODesktop6/trunk/lib/ -lsamp -lVOApps -lVO -lVOTable -lVOClient
+LIBS += -lm -lc  -lpthread -lcurl
+LIBS += /Users/fxbio6600/OACT/VisIVOServer_svn_locale/branches/2.3/API_LIGHT/libVisIVOApi.a
+LIBS += -L/opt/hdf5-1.10.0-patch1/lib/ -lhdf5
+LIBS += /usr/lib/libc++.dylib
+
+macx:LIBS +=  -framework \
+    Foundation \
+    -framework \
+    Cocoa \
+    -framework \
+    GLUT \
+    -framework \
+    QTKit \
+    -framework \
+    OpenGL \
+    -framework \
+    AGL \
+    -framework \
+    IOKit \
+    -framework \
+    QtPrintSupport
+
+
+
+SOURCES += src/main.cpp\
+           src/mainwindow.cpp \
+    src/treemodel.cpp \
+    src/treeitem.cpp \
+    src/vtkfitsreader.cpp \
+    src/vispoint.cpp \
+    src/observedobject.cpp \
+    src/operationqueue.cpp \
+    src/sednode.cpp \
+    src/sed.cpp \
+    src/pointspipe.cpp \
+    src/pipe.cpp \
+    src/vtkellipse.cpp \
+    src/base64.cpp \
+    src/astroutils.cpp \
+    src/libwcs/fitsfile.c \
+    src/libwcs/hget.c \
+    src/libwcs/fileutil.c \
+    src/libwcs/fitswcs.c \
+    src/libwcs/distort.c \
+    src/libwcs/hput.c \
+    src/libwcs/iget.c \
+    src/libwcs/imio.c \
+    src/libwcs/imhfile.c \
+    src/libwcs/dateutil.c \
+    src/libwcs/wcsinit.c \
+    src/libwcs/dsspos.c \
+    src/libwcs/wcs.c \
+    src/libwcs/wcstrig.c \
+    src/libwcs/wcscon.c \
+    src/libwcs/lin.c \
+    src/libwcs/platepos.c \
+    src/libwcs/tnxpos.c \
+    src/libwcs/wcslib.c \
+    src/libwcs/cel.c \
+    src/libwcs/proj.c \
+    src/libwcs/sph.c \
+    src/libwcs/worldpos.c \
+    src/vialacteasource.cpp \
+    src/vlkbquery.cpp \
+    src/loadingwidget.cpp \
+    src/sedvisualizerplot.cpp \
+    src/qcustomplot.cpp \
+    src/sedplotpointcustom.cpp \
+    src/sedfitgrid_thin.cpp \
+    src/sedfitgrid_thick.cpp \
+    src/luteditor.cpp \
+    src/vtktoolswidget.cpp \
+    src/color.cpp \
+    src/vtkfitstoolswidget.cpp \
+    src/higalselectedsources.cpp \
+    src/plotwindow.cpp \
+    src/vlkbquerycomposer.cpp \
+    src/vlkbtable.cpp \
+    src/fitsimagestatisiticinfo.cpp \
+    src/vlkbsimplequerycomposer.cpp \
+    src/dbquery.cpp \
+    src/xmlparser.cpp \
+    src/vialactea_fileload.cpp \
+    src/selectedsourcefieldsselect.cpp \
+    src/downloadmanager.cpp \
+    src/viewselectedsourcedataset.cpp \
+    src/vialactea.cpp \
+    src/contour.cpp \
+    src/histogram.cpp \
+    src/lutselector.cpp \
+    src/vtkwindow_new.cpp \
+    src/vtkfitstoolwidget_new.cpp \
+    src/vtkfitstoolwidgetobject.cpp \
+    src/vialacteainitialquery.cpp \
+    src/settingform.cpp \
+    src/aboutform.cpp \
+    src/selectedsourcesform.cpp \
+    src/vtklegendscaleactor.cpp \
+    src/lutcustomize.cpp \
+    src/vtkextracthistogram.cpp \
+    src/extendedglyph3d.cpp \
+ #   src/vosamp.cpp \
+    src/visivoimporterdesktop.cpp \
+    src/vstabledesktop.cpp \
+    src/visivoutilsdesktop.cpp \
+    src/vsobjectdesktop.cpp \
+    src/visivofilterdesktop.cpp \
+    src/filtercustomize.cpp \
+    src/vialacteastringdictwidget.cpp
+
+
+HEADERS  += src/mainwindow.h \
+            src/singleton.h \
+    src/treemodel.h \
+    src/treeitem.h \
+    src/vtkfitsreader.h \
+    src/vispoint.h \
+    src/observedobject.h \
+    src/operationqueue.h \
+    src/sednode.h \
+    src/sed.h \
+    src/pointspipe.h \
+    src/pipe.h \
+    src/vtkellipse.h \
+    src/base64.h \
+    src/astroutils.h \
+    src/libwcs/fitsfile.h \
+    src/libwcs/wcs.h \
+    src/vialacteasource.h \
+    src/vlkbquery.h \
+    src/loadingwidget.h \
+    src/sedvisualizerplot.h \
+    src/qcustomplot.h \
+    src/sedplotpointcustom.h \
+    src/sedfitgrid_thin.h \
+    src/sedfitgrid_thick.h \
+    src/luteditor.h \
+    src/vtktoolswidget.h \
+    src/color.h \
+    src/vtkfitstoolswidget.h \
+    src/higalselectedsources.h \
+    src/plotwindow.h \
+    src/vlkbquerycomposer.h \
+    src/vlkbtable.h \
+    src/fitsimagestatisiticinfo.h \
+    src/vlkbsimplequerycomposer.h \
+    src/dbquery.h \
+    src/xmlparser.h \
+    src/extendedglyph3d.h \
+    src/vialactea_fileload.h \
+    src/selectedsourcefieldsselect.h \
+    src/downloadmanager.h \
+    src/viewselectedsourcedataset.h \
+    src/vialactea.h \
+    src/contour.h   \
+    src/histogram.h \
+    src/lutselector.h \
+    src/vtkwindow_new.h \
+    src/vtkfitstoolwidget_new.h \
+    src/vtkfitstoolwidgetobject.h \
+    src/vialacteainitialquery.h \
+    src/settingform.h \
+    src/aboutform.h \
+    src/selectedsourcesform.h \
+    src/vtklegendscaleactor.h \
+    src/lutcustomize.h \
+    src/vtkextracthistogram.h \
+    src/osxhelper.h \
+   # src/vosamp.h \
+    src/visivoimporterdesktop.h \
+    src/vstabledesktop.h \
+    src/visivoutilsdesktop.h \
+    src/vsobjectdesktop.h \
+    src/visivofilterdesktop.h \
+    src/filtercustomize.h \
+    src/vialacteastringdictwidget.h
+
+
+FORMS    += ui/mainwindow.ui \
+    ui/operationqueue.ui \
+    ui/vtkwindow.ui \
+    ui/vlkbquery.ui \
+    ui/loadingwidget.ui \
+    ui/sedvisualizerplot.ui \
+    ui/sedfitgrid_thin.ui \
+    ui/sedfitgrid_thick.ui \
+    ui/vtktoolswidget.ui \
+    ui/vtkfitstoolswidget.ui \
+    ui/higalselectedsources.ui \
+    ui/plotwindow.ui \
+    ui/vlkbquerycomposer.ui \
+    ui/fitsimagestatisiticinfo.ui \
+    ui/vlkbsimplequerycomposer.ui \
+    ui/dbquery.ui \
+    ui/vialactea_fileload.ui \
+    ui/selectedsourcefieldsselect.ui \
+    ui/viewselectedsourcedataset.ui \
+    ui/vialactea.ui \
+    ui/contour.ui \
+    ui/vtkwindow_new.ui \
+    ui/vtkfitstoolwidget_new.ui \
+    ui/vialacteainitialquery.ui \
+    ui/settingform.ui \
+    ui/aboutform.ui \
+    ui/selectedsourcesform.ui \
+    ui/lutcustomize.ui \
+    ui/filtercustomize.ui \
+    src/vialacteastringdictwidget.ui
+
+RESOURCES += \
+   visivo.qrc
+
+DISTFILES +=
+
+OBJECTIVE_SOURCES += \
+    src/osxhelper.mm
diff --git a/Code/VisIVODesktop-vtk6_qt5.pro.user b/Code/VisIVODesktop-vtk6_qt5.pro.user
new file mode 100644
index 0000000000000000000000000000000000000000..f62c868f3582485f2a962a0a503d0234d65dccc1
--- /dev/null
+++ b/Code/VisIVODesktop-vtk6_qt5.pro.user
@@ -0,0 +1,339 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorProject>
+<!-- Written by QtCreator 4.2.1, 2018-06-20T13:09:34. -->
+<qtcreator>
+ <data>
+  <variable>EnvironmentId</variable>
+  <value type="QByteArray">{d7bdcdd1-3e2c-4559-a568-85e4bf761de3}</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.ActiveTarget</variable>
+  <value type="int">0</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.EditorSettings</variable>
+  <valuemap type="QVariantMap">
+   <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
+   <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
+   <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
+    <value type="QString" key="language">Cpp</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
+    </valuemap>
+   </valuemap>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
+    <value type="QString" key="language">QmlJS</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
+    </valuemap>
+   </valuemap>
+   <value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
+   <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
+   <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
+   <value type="int" key="EditorConfiguration.IndentSize">4</value>
+   <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
+   <value type="int" key="EditorConfiguration.MarginColumn">80</value>
+   <value type="bool" key="EditorConfiguration.MouseHiding">true</value>
+   <value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
+   <value type="int" key="EditorConfiguration.PaddingMode">1</value>
+   <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
+   <value type="bool" key="EditorConfiguration.ShowMargin">false</value>
+   <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
+   <value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
+   <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
+   <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
+   <value type="int" key="EditorConfiguration.TabSize">8</value>
+   <value type="bool" key="EditorConfiguration.UseGlobal">true</value>
+   <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
+   <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
+   <value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
+   <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
+   <value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.PluginSettings</variable>
+  <valuemap type="QVariantMap"/>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.0</variable>
+  <valuemap type="QVariantMap">
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{fefc9313-5518-4942-9856-e8ccfb4406bc}</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/fxbio6600/OACT/develop/VisIVODesktop6/build-VisIVODesktop-vtk6_qt5-Desktop-Debug</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges">
+     <value type="QString">LC_NUMERIC=en_US.UTF-8</value>
+    </valuelist>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Debug</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/fxbio6600/OACT/develop/VisIVODesktop6/build-VisIVODesktop-vtk6_qt5-Desktop-Release</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/fxbio6600/OACT/develop/VisIVODesktop6/build-VisIVODesktop-vtk6_qt5-Desktop-Profile</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">true</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Profile</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
+    <value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
+    <value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
+    <value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
+    <value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
+    <value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
+    <value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
+    <value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
+    <value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
+    <value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
+    <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
+    <value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
+    <value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
+    <value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
+     <value type="int">0</value>
+     <value type="int">1</value>
+     <value type="int">2</value>
+     <value type="int">3</value>
+     <value type="int">4</value>
+     <value type="int">5</value>
+     <value type="int">6</value>
+     <value type="int">7</value>
+     <value type="int">8</value>
+     <value type="int">9</value>
+     <value type="int">10</value>
+     <value type="int">11</value>
+     <value type="int">12</value>
+     <value type="int">13</value>
+     <value type="int">14</value>
+    </valuelist>
+    <value type="int" key="PE.EnvironmentAspect.Base">2</value>
+    <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">VisIVODesktop-vtk6_qt5</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/Users/fxbio6600/OACT/develop/VisIVODesktop6/trunk/VisIVODesktop-vtk6_qt5.pro</value>
+    <value type="bool" key="QmakeProjectManager.QmakeRunConfiguration.UseLibrarySearchPath">true</value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">VisIVODesktop-vtk6_qt5.pro</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix">false</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseTerminal">false</value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory.default">/Users/fxbio6600/OACT/develop/VisIVODesktop6/build-VisIVODesktop-vtk6_qt5-Desktop-Release</value>
+    <value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
+    <value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
+    <value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.TargetCount</variable>
+  <value type="int">1</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
+  <value type="int">18</value>
+ </data>
+ <data>
+  <variable>Version</variable>
+  <value type="int">18</value>
+ </data>
+</qtcreator>
diff --git a/Code/VisIVODesktop-vtk6_qt5.pro.user.a9018b3 b/Code/VisIVODesktop-vtk6_qt5.pro.user.a9018b3
new file mode 100644
index 0000000000000000000000000000000000000000..6dc29e650dc877e4c7699d8e4766be0f92869ced
--- /dev/null
+++ b/Code/VisIVODesktop-vtk6_qt5.pro.user.a9018b3
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorProject>
+<!-- Written by QtCreator 3.3.1, 2017-03-29T18:00:33. -->
+<qtcreator>
+ <data>
+  <variable>EnvironmentId</variable>
+  <value type="QByteArray">{a9018b3b-7afd-4732-92b3-1651299ec816}</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.ActiveTarget</variable>
+  <value type="int">0</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.EditorSettings</variable>
+  <valuemap type="QVariantMap">
+   <value type="bool" key="EditorConfiguration.AutoIndent">true</value>
+   <value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
+   <value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
+    <value type="QString" key="language">Cpp</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
+    </valuemap>
+   </valuemap>
+   <valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
+    <value type="QString" key="language">QmlJS</value>
+    <valuemap type="QVariantMap" key="value">
+     <value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
+    </valuemap>
+   </valuemap>
+   <value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
+   <value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
+   <value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
+   <value type="int" key="EditorConfiguration.IndentSize">4</value>
+   <value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
+   <value type="int" key="EditorConfiguration.MarginColumn">80</value>
+   <value type="bool" key="EditorConfiguration.MouseHiding">true</value>
+   <value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
+   <value type="int" key="EditorConfiguration.PaddingMode">1</value>
+   <value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
+   <value type="bool" key="EditorConfiguration.ShowMargin">false</value>
+   <value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
+   <value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
+   <value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
+   <value type="int" key="EditorConfiguration.TabSize">8</value>
+   <value type="bool" key="EditorConfiguration.UseGlobal">true</value>
+   <value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
+   <value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
+   <value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
+   <value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
+   <value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.PluginSettings</variable>
+  <valuemap type="QVariantMap"/>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Target.0</variable>
+  <valuemap type="QVariantMap">
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.4.1 clang 64bit</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.4.1 clang 64bit</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.54.clang_64_kit</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">1</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
+   <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/fxbio6600/OACT/develop/VisIVODesktop6/build-VisIVODesktop-vtk6_qt5-Desktop_Qt_5_4_1_clang_64bit-Debug</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibraryAuto">true</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Debug</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
+   </valuemap>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Users/fxbio6600/OACT/develop//build-VisIVODesktop-vtk6_qt5-Desktop_Qt_5_4_1_clang_64bit-Release</value>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibraryAuto">true</value>
+      <value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
+      <value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
+     </valuemap>
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
+    </valuemap>
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
+     <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
+      <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
+      <valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
+       <value type="QString">-w</value>
+       <value type="QString">-r</value>
+      </valuelist>
+      <value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
+      <value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
+     </valuemap>
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
+    <value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
+    <valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges">
+     <value type="QString">LC_NUMERIC=en_US.UTF-8</value>
+    </valuelist>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
+    <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">2</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
+    <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
+     <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
+    </valuemap>
+    <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy locally</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
+   <valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
+    <value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
+    <value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
+    <value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
+    <value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
+    <value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
+    <value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
+    <value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
+    <value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
+    <value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
+    <value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">/opt/valgrind/bin/valgrind</value>
+    <valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
+     <value type="int">0</value>
+     <value type="int">1</value>
+     <value type="int">2</value>
+     <value type="int">3</value>
+     <value type="int">4</value>
+     <value type="int">5</value>
+     <value type="int">6</value>
+     <value type="int">7</value>
+     <value type="int">8</value>
+     <value type="int">9</value>
+     <value type="int">10</value>
+     <value type="int">11</value>
+     <value type="int">12</value>
+     <value type="int">13</value>
+     <value type="int">14</value>
+    </valuelist>
+    <value type="int" key="PE.EnvironmentAspect.Base">2</value>
+    <valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">VisIVODesktop-vtk6_qt5</value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
+    <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:/Users/fxbio6600/OACT/develop/VisIVODesktop6/trunk/VisIVODesktop-vtk6_qt5.pro</value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.CommandLineArguments"></value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.ProFile">VisIVODesktop-vtk6_qt5.pro</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseDyldImageSuffix">false</value>
+    <value type="bool" key="Qt4ProjectManager.Qt4RunConfiguration.UseTerminal">false</value>
+    <value type="QString" key="Qt4ProjectManager.Qt4RunConfiguration.UserWorkingDirectory"></value>
+    <value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
+    <value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
+    <value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
+    <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
+   </valuemap>
+   <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
+  </valuemap>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.TargetCount</variable>
+  <value type="int">1</value>
+ </data>
+ <data>
+  <variable>ProjectExplorer.Project.Updater.FileVersion</variable>
+  <value type="int">18</value>
+ </data>
+ <data>
+  <variable>Version</variable>
+  <value type="int">18</value>
+ </data>
+</qtcreator>
diff --git a/Code/customprocess.cpp b/Code/customprocess.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..049202855696f1f8193ba58cd33977bfa928a77b
--- /dev/null
+++ b/Code/customprocess.cpp
@@ -0,0 +1,24 @@
+#include "customprocess.h"
+#include <QProcess>
+#include <QDebug>
+#include <QCoreApplication>
+
+CustomProcess::CustomProcess()
+{
+
+
+    QProcess *process = new QProcess();
+      connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onFinished()));
+      process->start("ls", QStringList());
+      if (!process->waitForStarted())
+          qDebug() << "error";
+
+
+
+}
+
+CustomProcess::~CustomProcess()
+{
+
+}
+
diff --git a/Code/customprocess.h b/Code/customprocess.h
new file mode 100644
index 0000000000000000000000000000000000000000..d9cdea0b8bfd56cee46ddad2fcc5158a6e076c74
--- /dev/null
+++ b/Code/customprocess.h
@@ -0,0 +1,13 @@
+#ifndef CUSTOMPROCESS_H
+#define CUSTOMPROCESS_H
+
+#include <QObject>
+
+class CustomProcess : public QObject
+{
+public:
+    CustomProcess();
+    ~CustomProcess();
+};
+
+#endif // CUSTOMPROCESS_H
diff --git a/Code/icons/.DS_Store b/Code/icons/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..24154484bd613f6e2b458b27a646d1a320b9b64b
Binary files /dev/null and b/Code/icons/.DS_Store differ
diff --git a/Code/icons/CHECK_OFF.bmp b/Code/icons/CHECK_OFF.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..29a0e4bccd08104e2c7a8d4ad6fa64a9a01b4137
Binary files /dev/null and b/Code/icons/CHECK_OFF.bmp differ
diff --git a/Code/icons/CHECK_ON.bmp b/Code/icons/CHECK_ON.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..f51be532e7d0959975bcd3478cf073aa38f7ccef
Binary files /dev/null and b/Code/icons/CHECK_ON.bmp differ
diff --git a/Code/icons/CLOSE_SASH.bmp b/Code/icons/CLOSE_SASH.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..b4ea916b0b3ba8b5b60f3a97a4c2caa9cd25b87c
Binary files /dev/null and b/Code/icons/CLOSE_SASH.bmp differ
diff --git a/Code/icons/DISABLED.bmp b/Code/icons/DISABLED.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..babe589514b1c1605d54a4f19216078de74d0b32
Binary files /dev/null and b/Code/icons/DISABLED.bmp differ
diff --git a/Code/icons/FILE_NEW.bmp b/Code/icons/FILE_NEW.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..2f0377fdcfc37898437a817a765dcd539342e710
Binary files /dev/null and b/Code/icons/FILE_NEW.bmp differ
diff --git a/Code/icons/FILE_OPEN.bmp b/Code/icons/FILE_OPEN.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..25baa29bf17df47237ecd8c79edf7b5dbe9a72d9
Binary files /dev/null and b/Code/icons/FILE_OPEN.bmp differ
diff --git a/Code/icons/FILE_SAVE.bmp b/Code/icons/FILE_SAVE.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..ddd83208b753f7857165057900284e815e0ef221
Binary files /dev/null and b/Code/icons/FILE_SAVE.bmp differ
diff --git a/Code/icons/FLYTO.bmp b/Code/icons/FLYTO.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..1c2f9bdf8afb2b1b2b15a44bab17f5dc9938be4d
Binary files /dev/null and b/Code/icons/FLYTO.bmp differ
diff --git a/Code/icons/INFO.bmp b/Code/icons/INFO.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..85956128e33a0f6bf7bc0330503ef8ee598f3457
Binary files /dev/null and b/Code/icons/INFO.bmp differ
diff --git a/Code/icons/MOVIE_RECORD.bmp b/Code/icons/MOVIE_RECORD.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..3aa0a925c0eaf753d4030f43b30206373d1e0549
Binary files /dev/null and b/Code/icons/MOVIE_RECORD.bmp differ
diff --git a/Code/icons/NODE_BLUE.bmp b/Code/icons/NODE_BLUE.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..44498e105bf608af9c319c34e83400d564c37b02
Binary files /dev/null and b/Code/icons/NODE_BLUE.bmp differ
diff --git a/Code/icons/NODE_GRAY.bmp b/Code/icons/NODE_GRAY.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..6cafe9b79e7addb008a554fe0904d64b9a09ed16
Binary files /dev/null and b/Code/icons/NODE_GRAY.bmp differ
diff --git a/Code/icons/NODE_RED.bmp b/Code/icons/NODE_RED.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..070a08503f54cdb2f435e7bef7956993eccba913
Binary files /dev/null and b/Code/icons/NODE_RED.bmp differ
diff --git a/Code/icons/NODE_YELLOW.bmp b/Code/icons/NODE_YELLOW.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..7ffb562c31c41f0941252101d36719405bad304e
Binary files /dev/null and b/Code/icons/NODE_YELLOW.bmp differ
diff --git a/Code/icons/OPAddVector.png b/Code/icons/OPAddVector.png
new file mode 100644
index 0000000000000000000000000000000000000000..fc59ef0948a12ab106afd0775b2ab2e5e88a0e92
Binary files /dev/null and b/Code/icons/OPAddVector.png differ
diff --git a/Code/icons/OPEN_LOCAL.png b/Code/icons/OPEN_LOCAL.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d531f9cca7c4aa311cb7cd6d8c09d17e8fe4187
Binary files /dev/null and b/Code/icons/OPEN_LOCAL.png differ
diff --git a/Code/icons/OPEN_REMOTE.png b/Code/icons/OPEN_REMOTE.png
new file mode 100644
index 0000000000000000000000000000000000000000..097a3937b69219d743f47a05dfae77684f9a36c7
Binary files /dev/null and b/Code/icons/OPEN_REMOTE.png differ
diff --git a/Code/icons/OP_COPY.bmp b/Code/icons/OP_COPY.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..8ca09f05a1605609dfaa158cbe8bf76491b8a2d1
Binary files /dev/null and b/Code/icons/OP_COPY.bmp differ
diff --git a/Code/icons/OP_CUT.bmp b/Code/icons/OP_CUT.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..04ab372862f0652d938fbfc13c4245fdd030d999
Binary files /dev/null and b/Code/icons/OP_CUT.bmp differ
diff --git a/Code/icons/OP_MakePlot.png b/Code/icons/OP_MakePlot.png
new file mode 100644
index 0000000000000000000000000000000000000000..65079fc09bd6cb76cd810d92d7bbcd1f2262b566
Binary files /dev/null and b/Code/icons/OP_MakePlot.png differ
diff --git a/Code/icons/OP_Merger.png b/Code/icons/OP_Merger.png
new file mode 100644
index 0000000000000000000000000000000000000000..ebcaa730b528c318ba3c770ce77d9afa1ce5ce94
Binary files /dev/null and b/Code/icons/OP_Merger.png differ
diff --git a/Code/icons/OP_PASTE.bmp b/Code/icons/OP_PASTE.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..10f5cae64e108c265f6444de33d80b3e554289ec
Binary files /dev/null and b/Code/icons/OP_PASTE.bmp differ
diff --git a/Code/icons/OP_Parser.png b/Code/icons/OP_Parser.png
new file mode 100644
index 0000000000000000000000000000000000000000..f13b5a6086b8746f109afc71580aae26d19aa691
Binary files /dev/null and b/Code/icons/OP_Parser.png differ
diff --git a/Code/icons/OP_REDO.bmp b/Code/icons/OP_REDO.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..f2e84505e9782b7f8d826f75fda2c8b778bc9699
Binary files /dev/null and b/Code/icons/OP_REDO.bmp differ
diff --git a/Code/icons/OP_UNDO.bmp b/Code/icons/OP_UNDO.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..9431efd81b06e05e6f46acd6e3f93afdc22b7e80
Binary files /dev/null and b/Code/icons/OP_UNDO.bmp differ
diff --git a/Code/icons/PIC_BACK.bmp b/Code/icons/PIC_BACK.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..732dfa8f6a5667628e5acf763fb3edbea5d0abb2
Binary files /dev/null and b/Code/icons/PIC_BACK.bmp differ
diff --git a/Code/icons/PIC_BOTTOM.bmp b/Code/icons/PIC_BOTTOM.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..d9b1d47b736f0bdaeabc2365abb53bc7b409431b
Binary files /dev/null and b/Code/icons/PIC_BOTTOM.bmp differ
diff --git a/Code/icons/PIC_FRONT.bmp b/Code/icons/PIC_FRONT.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..f13d3c622fb79f1d558a671aed1be0e1ce7bc2c9
Binary files /dev/null and b/Code/icons/PIC_FRONT.bmp differ
diff --git a/Code/icons/PIC_LEFT.bmp b/Code/icons/PIC_LEFT.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..3b41281b55b0a47a1b9a0e265f5b76ce6b1fb942
Binary files /dev/null and b/Code/icons/PIC_LEFT.bmp differ
diff --git a/Code/icons/PIC_RIGHT.bmp b/Code/icons/PIC_RIGHT.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..9fcbca74f68439733c42f5e6558c81eac212446a
Binary files /dev/null and b/Code/icons/PIC_RIGHT.bmp differ
diff --git a/Code/icons/PIC_TOP.bmp b/Code/icons/PIC_TOP.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..5f590c690ca3d9e773dd8f76de8c9c25ce270667
Binary files /dev/null and b/Code/icons/PIC_TOP.bmp differ
diff --git a/Code/icons/PRINT.bmp b/Code/icons/PRINT.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..19cf42a415405c197075196745d9455621d6d6d1
Binary files /dev/null and b/Code/icons/PRINT.bmp differ
diff --git a/Code/icons/PRINT_PREVIEW.bmp b/Code/icons/PRINT_PREVIEW.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..d21f96e87bf79ee7189fbef11689ec050e1fd935
Binary files /dev/null and b/Code/icons/PRINT_PREVIEW.bmp differ
diff --git a/Code/icons/RADIO_OFF.bmp b/Code/icons/RADIO_OFF.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..4abe8cf57e50486528ed16abcb76b203c5a282ce
Binary files /dev/null and b/Code/icons/RADIO_OFF.bmp differ
diff --git a/Code/icons/RADIO_ON.bmp b/Code/icons/RADIO_ON.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..c4890794a40f18c4c37045978862326873e775d3
Binary files /dev/null and b/Code/icons/RADIO_ON.bmp differ
diff --git a/Code/icons/ROLLOUT_CLOSE.bmp b/Code/icons/ROLLOUT_CLOSE.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..0ab35c79f6524fa7736d69bf8a1a15d2b9d120a5
Binary files /dev/null and b/Code/icons/ROLLOUT_CLOSE.bmp differ
diff --git a/Code/icons/ROLLOUT_OPEN.bmp b/Code/icons/ROLLOUT_OPEN.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..843f233e2ac4be30f752e65102195ecb63ba4a17
Binary files /dev/null and b/Code/icons/ROLLOUT_OPEN.bmp differ
diff --git a/Code/icons/TIME_BEGIN.bmp b/Code/icons/TIME_BEGIN.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..6c7270344ec273fc6b93c31ece367941c35f311c
Binary files /dev/null and b/Code/icons/TIME_BEGIN.bmp differ
diff --git a/Code/icons/TIME_END.bmp b/Code/icons/TIME_END.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..55d8382202b09ca7fdd9c42310dbc9c1d53da0d2
Binary files /dev/null and b/Code/icons/TIME_END.bmp differ
diff --git a/Code/icons/TIME_NEXT.bmp b/Code/icons/TIME_NEXT.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..54ad4c41d93b5baac5ff648a3c38e02a3c6e1291
Binary files /dev/null and b/Code/icons/TIME_NEXT.bmp differ
diff --git a/Code/icons/TIME_PLAY.bmp b/Code/icons/TIME_PLAY.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..67d0f2c9613908ce8402e172427f25d86a69ea3f
Binary files /dev/null and b/Code/icons/TIME_PLAY.bmp differ
diff --git a/Code/icons/TIME_PREV.bmp b/Code/icons/TIME_PREV.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..422d27c7017d43862c8aec58427395b88a21414f
Binary files /dev/null and b/Code/icons/TIME_PREV.bmp differ
diff --git a/Code/icons/TIME_STOP.bmp b/Code/icons/TIME_STOP.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..7d504edf208f1329f0f07109a8a797a1fdab862c
Binary files /dev/null and b/Code/icons/TIME_STOP.bmp differ
diff --git a/Code/icons/VBT_CLOUD.bmp b/Code/icons/VBT_CLOUD.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..9c812170e4de38752c430716cde81125d42420f2
Binary files /dev/null and b/Code/icons/VBT_CLOUD.bmp differ
diff --git a/Code/icons/VBT_Table.bmp b/Code/icons/VBT_Table.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..6bee44e26be46b0c4c403f442ba42226a4357736
Binary files /dev/null and b/Code/icons/VBT_Table.bmp differ
diff --git a/Code/icons/VBT_Volume.bmp b/Code/icons/VBT_Volume.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..0d832a990cc3c194743558c0c42374f978397fd2
Binary files /dev/null and b/Code/icons/VBT_Volume.bmp differ
diff --git a/Code/icons/VME_FEM.bmp b/Code/icons/VME_FEM.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..8366b5bf00801c351c3d605f89c400c738c70a77
Binary files /dev/null and b/Code/icons/VME_FEM.bmp differ
diff --git a/Code/icons/VME_IMAGE.bmp b/Code/icons/VME_IMAGE.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..11e61bc79a414c851c459cd2732ec0dc195da6dd
Binary files /dev/null and b/Code/icons/VME_IMAGE.bmp differ
diff --git a/Code/icons/VME_LANDMARK.bmp b/Code/icons/VME_LANDMARK.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..8553c4d29bff7c7e82032736d17b3865bbd6f8eb
Binary files /dev/null and b/Code/icons/VME_LANDMARK.bmp differ
diff --git a/Code/icons/VME_SURFACE.bmp b/Code/icons/VME_SURFACE.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..c6b63caa295d203b73771dc97081b6d6512711e2
Binary files /dev/null and b/Code/icons/VME_SURFACE.bmp differ
diff --git a/Code/icons/VME_VOLUME.bmp b/Code/icons/VME_VOLUME.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..2b595cb4705add9bf06c5ecb7c77f27d4607fb68
Binary files /dev/null and b/Code/icons/VME_VOLUME.bmp differ
diff --git a/Code/icons/VisIVODesktop.icns b/Code/icons/VisIVODesktop.icns
new file mode 100644
index 0000000000000000000000000000000000000000..f97b15c68669a362f48fc92ca99739aa56d04c52
Binary files /dev/null and b/Code/icons/VisIVODesktop.icns differ
diff --git a/Code/icons/ZOOM.bmp b/Code/icons/ZOOM.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..f3fe21cf4bcea102bb5f065a3b6f38392c214e1a
Binary files /dev/null and b/Code/icons/ZOOM.bmp differ
diff --git a/Code/icons/ZOOM_ALL.bmp b/Code/icons/ZOOM_ALL.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..2f05fca348522a517c58583583df398aaafe1cfb
Binary files /dev/null and b/Code/icons/ZOOM_ALL.bmp differ
diff --git a/Code/icons/ZOOM_SEL.bmp b/Code/icons/ZOOM_SEL.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..bcc0a92734adc468ffcf45ded75d00e42b074954
Binary files /dev/null and b/Code/icons/ZOOM_SEL.bmp differ
diff --git a/Code/icons/colorize.png b/Code/icons/colorize.png
new file mode 100644
index 0000000000000000000000000000000000000000..72f18df87e212a0b2da34a6b2b25fa89acc42b4d
Binary files /dev/null and b/Code/icons/colorize.png differ
diff --git a/Code/icons/ellipse-select.png b/Code/icons/ellipse-select.png
new file mode 100644
index 0000000000000000000000000000000000000000..2575584c95e6539736043a35471055ccff544944
Binary files /dev/null and b/Code/icons/ellipse-select.png differ
diff --git a/Code/icons/filter.png b/Code/icons/filter.png
new file mode 100644
index 0000000000000000000000000000000000000000..f724b68b22376f1dfbeccabea6e3349192511da3
Binary files /dev/null and b/Code/icons/filter.png differ
diff --git a/Code/icons/logo3b_vl.jpg b/Code/icons/logo3b_vl.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..da054efd443905b76edb9a71e8cfb5798d19d78b
Binary files /dev/null and b/Code/icons/logo3b_vl.jpg differ
diff --git a/Code/icons/mafVMEVector.bmp b/Code/icons/mafVMEVector.bmp
new file mode 100644
index 0000000000000000000000000000000000000000..68bccefc578289229f947de15fd9f9bef2e8e790
Binary files /dev/null and b/Code/icons/mafVMEVector.bmp differ
diff --git a/Code/icons/rect-select.png b/Code/icons/rect-select.png
new file mode 100644
index 0000000000000000000000000000000000000000..866b602a89b6afd40aff72016d9a34e21ba66b0e
Binary files /dev/null and b/Code/icons/rect-select.png differ
diff --git a/Code/icons/visivo.png b/Code/icons/visivo.png
new file mode 100644
index 0000000000000000000000000000000000000000..552d7e3e17c132b4985acd2c4d5a71fab51d7159
Binary files /dev/null and b/Code/icons/visivo.png differ
diff --git a/Code/images/splash.png b/Code/images/splash.png
new file mode 100644
index 0000000000000000000000000000000000000000..75b3e982866f6b06a834c4e506d99641a4449d60
Binary files /dev/null and b/Code/images/splash.png differ
diff --git a/Code/lib/.DS_Store b/Code/lib/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6
Binary files /dev/null and b/Code/lib/.DS_Store differ
diff --git a/Code/lib/libVO.a b/Code/lib/libVO.a
new file mode 100644
index 0000000000000000000000000000000000000000..019a31294fe9b229a223e6804d01b183482bef58
Binary files /dev/null and b/Code/lib/libVO.a differ
diff --git a/Code/lib/libVOApps.a b/Code/lib/libVOApps.a
new file mode 100644
index 0000000000000000000000000000000000000000..44a7e6d66d27537229ca3930941446445404cd01
Binary files /dev/null and b/Code/lib/libVOApps.a differ
diff --git a/Code/lib/libVOClient.a b/Code/lib/libVOClient.a
new file mode 100644
index 0000000000000000000000000000000000000000..a2cd11104a3e02359861ddccd1cc3a5730a3e601
Binary files /dev/null and b/Code/lib/libVOClient.a differ
diff --git a/Code/lib/libVOTable.a b/Code/lib/libVOTable.a
new file mode 100644
index 0000000000000000000000000000000000000000..7d84812d0bc1a3194f21c6798deee4f3ebfefbdd
Binary files /dev/null and b/Code/lib/libVOTable.a differ
diff --git a/Code/lib/libcfitsio.a b/Code/lib/libcfitsio.a
new file mode 100644
index 0000000000000000000000000000000000000000..23bd7579ac54a1712a39a765d69a5ea77b004d6c
Binary files /dev/null and b/Code/lib/libcfitsio.a differ
diff --git a/Code/lib/libcurl.a b/Code/lib/libcurl.a
new file mode 100644
index 0000000000000000000000000000000000000000..9a6daffbeb768f402568f79f9c2e031c0830a76a
Binary files /dev/null and b/Code/lib/libcurl.a differ
diff --git a/Code/lib/libexpat.a b/Code/lib/libexpat.a
new file mode 100644
index 0000000000000000000000000000000000000000..61bfad4b2d843e48109f49b7187d91276b99a891
Binary files /dev/null and b/Code/lib/libexpat.a differ
diff --git a/Code/lib/libsamp.a b/Code/lib/libsamp.a
new file mode 100644
index 0000000000000000000000000000000000000000..274d66caa7f4dfa6151de73afeb7d4d0d63d9f3c
Binary files /dev/null and b/Code/lib/libsamp.a differ
diff --git a/Code/logo.icns b/Code/logo.icns
new file mode 100644
index 0000000000000000000000000000000000000000..6d26a11c023271a009d36cd82e4dddf99c9ff778
Binary files /dev/null and b/Code/logo.icns differ
diff --git a/Code/script_idl_mv/.DS_Store b/Code/script_idl_mv/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..1097fa5c6b6ea8ca8b3cc1039c6baa4eff2ce550
Binary files /dev/null and b/Code/script_idl_mv/.DS_Store differ
diff --git a/Code/script_idl_mv/astrolib/.idlwave_catalog b/Code/script_idl_mv/astrolib/.idlwave_catalog
new file mode 100644
index 0000000000000000000000000000000000000000..55907894aace370a30440f0e90f41a58851fb422
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/.idlwave_catalog
@@ -0,0 +1,583 @@
+;;
+;; IDLWAVE catalog for library Astrolib
+;; Automatically Generated -- do not edit.
+;; Created by idlwave_catalog on Wed May 25 13:21:39 2016
+;;
+(setq idlwave-library-catalog-libname "Astrolib")
+(setq idlwave-library-catalog-routines
+ '(("spc" fun nil (lib "factor.pro" nil "Astrolib") "Result = %s(n, text)" (nil ("character") ("help") ("notrim")))
+   ("print_fact" pro nil (lib "factor.pro" nil "Astrolib") "%s, p, n" (nil ("help")))
+   ("factor" pro nil (lib "factor.pro" nil "Astrolib") "%s, x, p, n" (nil ("debug") ("help") ("quiet") ("try")))
+   ("ad2xy" pro nil (lib "ad2xy.pro" nil "Astrolib") "%s, a, d, astr, x, y" (nil))
+   ("add_distort" pro nil (lib "add_distort.pro" nil "Astrolib") "%s, hdr, astr" (nil))
+   ("adstring" fun nil (lib "adstring.pro" nil "Astrolib") "Result = %s(ra_dec, dec, precision)" (nil ("PRECISION") ("TRUNCATE")))
+   ("adxy" pro nil (lib "adxy.pro" nil "Astrolib") "%s, hdr, a, d, x, y" (nil ("ALT") ("PRINT")))
+   ("airtovac" pro nil (lib "airtovac.pro" nil "Astrolib") "%s, wave_air, wave_vac" (nil))
+   ("aitoff" pro nil (lib "aitoff.pro" nil "Astrolib") "%s, l, b, x, y" (nil))
+   ("AITOFF_GRID" pro nil (lib "aitoff_grid.pro" nil "Astrolib") "%s, DLONG, DLAT" (nil ("_EXTRA") ("CHARSIZE") ("CHARTHICK") ("FONT") ("LABEL") ("NEW")))
+   ("altaz2hadec" pro nil (lib "altaz2hadec.pro" nil "Astrolib") "%s, alt, az, lat, ha, dec" (nil))
+   ("aper" pro nil (lib "aper.pro" nil "Astrolib") "%s, image, xc, yc, mags, errap, sky, skyerr, phpadu, apr, skyradii, badpix" (nil ("CLIPSIG") ("CONVERGE_NUM") ("EXACT") ("FLUX") ("MAXITER") ("MEANBACK") ("MINSKY") ("Nan") ("PRINT") ("READNOISE") ("SETSKYVAL") ("SILENT")))
+   ("arcbar" pro nil (lib "arcbar.pro" nil "Astrolib") "%s, hdr, arclen" (nil ("COLOR") ("DATA") ("FONT") ("LABEL") ("NORMAL") ("POSITION") ("SECONDS") ("SIZE") ("THICK")))
+   ("arrows" pro nil (lib "arrows.pro" nil "Astrolib") "%s, h, xcen, ycen" (nil ("arrowlen") ("charsize") ("color") ("Data") ("font") ("Normal") ("NotVertex") ("thick")))
+   ("asinh" fun nil (lib "asinh.pro" nil "Astrolib") "Result = %s(x)" (nil))
+   ("AstDisp" pro nil (lib "astdisp.pro" nil "Astrolib") "%s, x, y, ra, dec, DN" (nil ("Coords") ("silent")))
+   ("astro" pro nil (lib "astro.pro" nil "Astrolib") "%s, selection" (nil ("EQUINOX") ("FK4")))
+   ("ASTROLIB" pro nil (lib "astrolib.pro" nil "Astrolib") "%s" (nil))
+   ("AUTOHIST" pro nil (lib "autohist.pro" nil "Astrolib") "%s, V, ZX, ZY, XX, YY" (nil ("_EXTRA") ("NOPLOT")))
+   ("AVG" fun nil (lib "avg.pro" nil "Astrolib") "Result = %s(ARRAY, DIMENSION)" (nil ("DOUBLE") ("NAN")))
+   ("baryvel" pro nil (lib "baryvel.pro" nil "Astrolib") "%s, dje, deq, dvelh, dvelb" (nil ("JPL")))
+   ("BIWEIGHT_MEAN" fun nil (lib "biweight_mean.pro" nil "Astrolib") "Result = %s(Y, SIGMA, WEIGHTs)" (nil))
+   ("BLINK" pro nil (lib "blink.pro" nil "Astrolib") "%s, wndw, t" (nil))
+   ("BLKSHIFT" pro nil (lib "blkshift.pro" nil "Astrolib") "%s, UNIT, POS0, DELTA0" (nil ("BUFFERSIZE") ("ERRMSG") ("NOZERO") ("TO")))
+   ("BOOST_ARRAY" pro nil (lib "boost_array.pro" nil "Astrolib") "%s, DESTINATION, APPEND" (nil))
+   ("boxave" fun nil (lib "boxave.pro" nil "Astrolib") "Result = %s(array, xsize, ysize)" (nil))
+   ("Bprecess" pro nil (lib "bprecess.pro" nil "Astrolib") "%s, ra, dec, ra_1950, dec_1950" (nil ("EPOCH") ("MU_RADEC") ("PARALLAX") ("RAD_VEL")))
+   ("BREAK_PATH" fun nil (lib "break_path.pro" nil "Astrolib") "Result = %s(PATHS)" (nil ("NOCURRENT")))
+   ("Bsort" fun nil (lib "bsort.pro" nil "Astrolib") "Result = %s(Array, Asort)" (nil ("INFO") ("REVERSE")))
+   ("calz_unred" pro nil (lib "calz_unred.pro" nil "Astrolib") "%s, wave, flux, ebv, funred" (nil ("R_V")))
+   ("ccm_UNRED" pro nil (lib "ccm_unred.pro" nil "Astrolib") "%s, wave, flux, ebv, funred" (nil ("R_V")))
+   ("check_FITS" pro nil (lib "check_fits.pro" nil "Astrolib") "%s, im, hdr, dimen, idltype" (nil ("ERRMSG") ("FITS") ("NOTYPE") ("SDAS") ("SILENT") ("UPDATE")))
+   ("checksum32" pro nil (lib "checksum32.pro" nil "Astrolib") "%s, array, checksum" (nil ("FROM_IEEE") ("NOSAVE")))
+   ("cic" fun nil (lib "cic.pro" nil "Astrolib") "Result = %s(value, posx, nx, posy, ny, posz, nz)" (nil ("AVERAGE") ("ISOLATED") ("NO_MESSAGE") ("WRAPAROUND")))
+   ("cirrange" pro nil (lib "cirrange.pro" nil "Astrolib") "%s, ang" (nil ("RADIANS")))
+   ("CleanPlot" pro nil (lib "cleanplot.pro" nil "Astrolib") "%s" (nil ("ShowOnly") ("silent")))
+   ("cntrd" pro nil (lib "cntrd.pro" nil "Astrolib") "%s, img, x, y, xcen, ycen, fwhm" (nil ("DEBUG") ("EXTENDBOX") ("KeepCenter") ("SILENT")))
+   ("co_aberration" pro nil (lib "co_aberration.pro" nil "Astrolib") "%s, jd, ra, dec, d_ra, d_dec" (nil ("eps")))
+   ("co_nutate" pro nil (lib "co_nutate.pro" nil "Astrolib") "%s, jd, ra, dec, d_ra, d_dec" (nil ("d_eps") ("d_psi") ("eps")))
+   ("co_refract_forward" fun nil (lib "co_refract.pro" nil "Astrolib") "Result = %s(a)" (nil ("P") ("T")))
+   ("co_refract" fun nil (lib "co_refract.pro" nil "Astrolib") "Result = %s(a)" (nil ("altitude") ("epsilon") ("pressure") ("temperature") ("To_observed")))
+   ("compare_struct" fun nil (lib "compare_struct.pro" nil "Astrolib") "Result = %s(struct_A, struct_B, Struct_Name)" (nil ("BRIEF") ("EXCEPT") ("FULL") ("NaN") ("RECUR_A") ("RECUR_B")))
+   ("concat_dir" fun nil (lib "concat_dir.pro" nil "Astrolib") "Result = %s(dirname, filnam)" (nil))
+   ("CONS_DEC" fun nil (lib "cons_dec.pro" nil "Astrolib") "Result = %s(DEC, X, ASTR, ALPHA)" (nil))
+   ("CONS_RA" fun nil (lib "cons_ra.pro" nil "Astrolib") "Result = %s(RA, Y, ASTR, DELTA)" (nil))
+   ("convolve" fun nil (lib "convolve.pro" nil "Astrolib") "Result = %s(image, psf)" (nil ("AUTO_CORRELATION") ("CORRELATE") ("FT_IMAGE") ("FT_PSF") ("NO_FT") ("NO_PAD")))
+   ("copy_struct" pro nil (lib "copy_struct.pro" nil "Astrolib") "%s, struct_From, struct_To, NT_copied, Recur_Level" (nil ("EXCEPT_TAGS") ("RECUR_From") ("RECUR_TANDEM") ("RECUR_TO") ("SELECT_TAGS")))
+   ("copy_struct_inx" pro nil (lib "copy_struct_inx.pro" nil "Astrolib") "%s, struct_From, struct_To, NT_copied, Recur_Level" (nil ("EXCEPT_TAGS") ("INDEX_From") ("INDEX_To") ("RECUR_From") ("RECUR_TANDEM") ("RECUR_To") ("SELECT_TAGS")))
+   ("correl_images" fun nil (lib "correl_images.pro" nil "Astrolib") "Result = %s(image_A, image_B)" (nil ("MAGNIFICATION") ("MONITOR") ("NUMPIX") ("REDUCTION") ("XOFFSET_B") ("XSHIFT") ("YOFFSET_B") ("YSHIFT")))
+   ("correl_optimize" pro nil (lib "correl_optimize.pro" nil "Astrolib") "%s, image_A, image_B, xoffset_optimum, yoffset_optimum" (nil ("MAGNIFICATION") ("MONITOR") ("NUMPIX") ("PLATEAU_TRESH") ("PRINT") ("XOFF_INIT") ("YOFF_INIT")))
+   ("corrmat_analyze" pro nil (lib "corrmat_analyze.pro" nil "Astrolib") "%s, correl_mat, xoffset_optimum, yoffset_optimum, max_corr, edge, plateau" (nil ("MAGNIFICATION") ("PLATEAU_THRESH") ("PRINT") ("REDUCTION") ("XOFF_INIT") ("YOFF_INIT")))
+   ("cosmo_param" pro nil (lib "cosmo_param.pro" nil "Astrolib") "%s, Omega_m, Omega_Lambda, Omega_k, q0" (nil))
+   ("cr_reject" pro nil (lib "cr_reject.pro" nil "Astrolib") "%s, input_cube, rd_noise_dn, dark_dn, gain, mult_noise, combined_image, combined_noise, combined_npix" (nil ("BIAS") ("DFACTOR") ("DILATION") ("EXPTIME") ("INIT_MEAN") ("INIT_MED") ("INIT_MIN") ("INPUT_MASK") ("MASK_CUBE") ("MEAN_LOOP") ("MEDIAN_LOOP") ("MINIMUM_LOOP") ("NOCLEARMASK") ("NOISE_CUBE") ("NOSKYADJUST") ("NSIG") ("NULL_VALUE") ("RESTORE_SKY") ("SKYBOX") ("SKYVALS") ("TRACKING_SET") ("VERBOSE") ("WEIGHTING") ("XMEDSKY")))
+   ("create_struct" pro nil (lib "create_struct.pro" nil "Astrolib") "%s, struct, strname, tagnames, tag_descript" (nil ("CHATTER") ("DIMEN") ("NODELETE")))
+   ("cspline" fun nil (lib "cspline.pro" nil "Astrolib") "Result = %s(xx, yy, tt)" (nil ("Deriv")))
+   ("CT2LST" pro nil (lib "ct2lst.pro" nil "Astrolib") "%s, lst, lng, tz, tme, day, mon, year" (nil))
+   ("curs" pro nil (lib "curs.pro" nil "Astrolib") "%s, sel" (nil))
+   ("curval" pro nil (lib "curval.pro" nil "Astrolib") "%s, hd, im" (nil ("ALT") ("Filename") ("OFFSET") ("ZOOM")))
+   ("DAO_VALUE" fun nil (lib "dao_value.pro" nil "Astrolib") "Result = %s(XX, YY, GAUSS, PSF, DVDX, DVDY)" (nil))
+   ("daoerf" pro nil (lib "daoerf.pro" nil "Astrolib") "%s, x, y, a, f, pder" (nil))
+   ("DATE" fun nil (lib "date.pro" nil "Astrolib") "Result = %s(YEAR, DAY)" (nil))
+   ("date_conv" fun nil (lib "date_conv.pro" nil "Astrolib") "Result = %s(date, type)" (nil ("BAD_DATE")))
+   ("DAYCNV" pro nil (lib "daycnv.pro" nil "Astrolib") "%s, XJD, YR, MN, DAY, HR" (nil))
+   ("DB_ENT2EXT" pro nil (lib "db_ent2ext.pro" nil "Astrolib") "%s, ENTRY" (nil))
+   ("DB_ENT2HOST" pro nil (lib "db_ent2host.pro" nil "Astrolib") "%s, ENTRY, DBNO" (nil))
+   ("db_info" fun nil (lib "db_info.pro" nil "Astrolib") "Result = %s(request, dbname)" (nil))
+   ("db_item" pro nil (lib "db_item.pro" nil "Astrolib") "%s, items, itnum, ivalnum, idltype, sbyte, numvals, nbytes" (nil ("errmsg")))
+   ("db_item_info" fun nil (lib "db_item_info.pro" nil "Astrolib") "Result = %s(request, itnums)" (nil))
+   ("db_or" fun nil (lib "db_or.pro" nil "Astrolib") "Result = %s(list1, list2)" (nil))
+   ("db_titles" pro nil (lib "db_titles.pro" nil "Astrolib") "%s, fnames, titles" (nil))
+   ("dbbuild" pro nil (lib "dbbuild.pro" nil "Astrolib") "%s, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50" (nil ("NOINDEX") ("SILENT") ("STATUS")))
+   ("dbcircle" fun nil (lib "dbcircle.pro" nil "Astrolib") "Result = %s(ra_cen, dec_cen, radius, dis, sublist)" (nil ("COUNT") ("GALACTIC") ("SILENT") ("TO_B1950") ("TO_J2000")))
+   ("dbclose" pro nil (lib "dbclose.pro" nil "Astrolib") "%s, dummy" (nil))
+   ("dbcompare" pro nil (lib "dbcompare.pro" nil "Astrolib") "%s, list1, list2, items" (nil ("DIFF") ("TEXTOUT")))
+   ("dbcreate" pro nil (lib "dbcreate.pro" nil "Astrolib") "%s, name, newindex, newdb, maxitems" (nil ("EXTERNAL") ("Maxentry")))
+   ("dbdelete" pro nil (lib "dbdelete.pro" nil "Astrolib") "%s, list, name" (nil ("DEBUG")))
+   ("widgetedit_event" pro nil (lib "dbedit.pro" nil "Astrolib") "%s, event" (nil))
+   ("widedit" pro nil (lib "dbedit.pro" nil "Astrolib") "%s" (nil))
+   ("dbedit" pro nil (lib "dbedit.pro" nil "Astrolib") "%s, list, items" (nil ("bytenum")))
+   ("dbedit_basic" pro nil (lib "dbedit_basic.pro" nil "Astrolib") "%s, list, items" (nil))
+   ("dbext" pro nil (lib "dbext.pro" nil "Astrolib") "%s, list, items, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12" (nil))
+   ("dbext_dbf" pro nil (lib "dbext_dbf.pro" nil "Astrolib") "%s, list, dbno, sbyte, nbytes, idltype, nval, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18" (nil ("item_dbno")))
+   ("dbext_ind" pro nil (lib "dbext_ind.pro" nil "Astrolib") "%s, list, item, dbno, values" (nil))
+   ("dbfind" fun nil (lib "dbfind.pro" nil "Astrolib") "Result = %s(spar, listin)" (nil ("Count") ("errmsg") ("fullstring") ("SILENT")))
+   ("dbfind_entry" pro nil (lib "dbfind_entry.pro" nil "Astrolib") "%s, type, svals, nentries, values" (nil ("Count")))
+   ("dbfind_sort" pro nil (lib "dbfind_sort.pro" nil "Astrolib") "%s, it, type, svals, list" (nil ("COUNT") ("FULLSTRING")))
+   ("dbfparse" pro nil (lib "dbfparse.pro" nil "Astrolib") "%s, spar, items, stype, values" (nil))
+   ("dbget" fun nil (lib "dbget.pro" nil "Astrolib") "Result = %s(item, values, listin)" (nil ("Count") ("FULLSTRING") ("SILENT")))
+   ("dbhelp" pro nil (lib "dbhelp.pro" nil "Astrolib") "%s, flag" (nil ("sort") ("TEXTOUT")))
+   ("dbindex" pro nil (lib "dbindex.pro" nil "Astrolib") "%s, items" (nil))
+   ("dbindex_blk" fun nil (lib "dbindex_blk.pro" nil "Astrolib") "Result = %s(unit, nb, bsz, ofb, dtype)" (nil))
+   ("dbmatch" fun nil (lib "dbmatch.pro" nil "Astrolib") "Result = %s(item, values, listin)" (nil ("FULLSTRING")))
+   ("dbopen" pro nil (lib "dbopen.pro" nil "Astrolib") "%s, name, update" (nil ("UNAVAIL")))
+   ("dbprint" pro nil (lib "dbprint.pro" nil "Astrolib") "%s, list, items" (nil ("Adjustformat") ("FORMS") ("NoHeader") ("TEXTOUT")))
+   ("dbput" pro nil (lib "dbput.pro" nil "Astrolib") "%s, item, val, entry" (nil))
+   ("dbrd" pro nil (lib "dbrd.pro" nil "Astrolib") "%s, enum, entry, available, dbno" (nil ("noconvert")))
+   ("dbsearch" pro nil (lib "dbsearch.pro" nil "Astrolib") "%s, type, svals, values, good" (nil ("COUNT") ("FULLSTRING")))
+   ("dbsort" fun nil (lib "dbsort.pro" nil "Astrolib") "Result = %s(list, items)" (nil ("REVERSE")))
+   ("dbtarget" fun nil (lib "dbtarget.pro" nil "Astrolib") "Result = %s(target, radius, sublist)" (nil ("DIS") ("SILENT") ("TO_B1950")))
+   ("dbtitle" fun nil (lib "dbtitle.pro" nil "Astrolib") "Result = %s(c, f)" (nil))
+   ("dbupdate" pro nil (lib "dbupdate.pro" nil "Astrolib") "%s, list, items, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14" (nil ("NOINDEX")))
+   ("dbval" fun nil (lib "dbval.pro" nil "Astrolib") "Result = %s(entry, item)" (nil))
+   ("dbwrt" pro nil (lib "dbwrt.pro" nil "Astrolib") "%s, entry, index, append" (nil ("noconvert")))
+   ("dbxput" pro nil (lib "dbxput.pro" nil "Astrolib") "%s, val, entry, idltype, sbyte, nbytes" (nil))
+   ("dbxval" fun nil (lib "dbxval.pro" nil "Astrolib") "Result = %s(entry, idltype, nvalues, sbyte, nbytes)" (nil ("bswap")))
+   ("delvarx" pro nil (lib "delvarx.pro" nil "Astrolib") "%s, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9" (nil ("free_mem")))
+   ("deredd" pro nil (lib "deredd.pro" nil "Astrolib") "%s, Eby, by, m1, c1, ub, by0, m0, c0, ub0" (nil ("update")))
+   ("DETABIFY" fun nil (lib "detabify.pro" nil "Astrolib") "Result = %s(CHAR_STR)" (nil))
+   ("dist_circle" pro nil (lib "dist_circle.pro" nil "Astrolib") "%s, im, n, xcen, ycen" (nil ("DOUBLE")))
+   ("dist_ellipse" pro nil (lib "dist_ellipse.pro" nil "Astrolib") "%s, im, n, xc, yc, ratio, pos_ang" (nil ("DOUBLE")))
+   ("eci2geo" fun nil (lib "eci2geo.pro" nil "Astrolib") "Result = %s(ECI_XYZ, JDtim)" (nil))
+   ("eq2hor" pro nil (lib "eq2hor.pro" nil "Astrolib") "%s, ra, dec, jd, alt, az, ha" (nil ("_extra") ("aberration_") ("altitude") ("B1950") ("lat") ("lon") ("nutate_") ("obsname") ("precess_") ("refract_") ("verbose") ("WS")))
+   ("eqpole" pro nil (lib "eqpole.pro" nil "Astrolib") "%s, l, b, x, y" (nil ("southpole")))
+   ("EQPOLE_GRID" pro nil (lib "eqpole_grid.pro" nil "Astrolib") "%s, DLONG, DLAT" (nil ("_EXTRA") ("CHARSIZE") ("CHARTHICK") ("LABELS") ("NEW") ("SOUTHPOLE")))
+   ("EULER" pro nil (lib "euler.pro" nil "Astrolib") "%s, AI, BI, AO, BO, SELECT" (nil ("FK4") ("RADIAN") ("SELECT")))
+   ("expand_tilde" fun nil (lib "expand_tilde.pro" nil "Astrolib") "Result = %s(name)" (nil))
+   ("extast" pro nil (lib "extast.pro" nil "Astrolib") "%s, hdr, astr, noparams" (nil ("alt")))
+   ("extgrp" pro nil (lib "extgrp.pro" nil "Astrolib") "%s, hdr, par" (nil))
+   ("f_format" fun nil (lib "f_format.pro" nil "Astrolib") "Result = %s(minval, maxval, factor, length)" (nil))
+   ("al_legend" pro nil (lib "al_legend.pro" nil "Astrolib") "%s, items" (nil ("background_color") ("BOTTOM_LEGEND") ("BOX") ("BTHICK") ("CENTER_LEGEND") ("CHARSIZE") ("CHARTHICK") ("CLEAR") ("COLORS") ("CORNERS") ("DATA") ("DELIMITER") ("DEVICE") ("FILL") ("FONT") ("HELP") ("HORIZONTAL") ("LEFT_LEGEND") ("LINESTYLE") ("LINSIZE") ("MARGIN") ("NORMAL") ("NUMBER") ("OUTLINE_COLOR") ("POSITION") ("PSPACING") ("PSYM") ("RIGHT_LEGEND") ("SPACING") ("SYMSIZE") ("TEXTCOLORS") ("THICK") ("TOP_LEGEND") ("USERSYM") ("VECTORFONT") ("VERTICAL") ("WINDOW")))
+   ("fdecomp" pro nil (lib "fdecomp.pro" nil "Astrolib") "%s, filename, disk, dir, name, qual, version" (nil ("OSfamily")))
+   ("filter_image" fun nil (lib "filter_image.pro" nil "Astrolib") "Result = %s(image)" (nil ("ALL_PIXELS") ("FWHM_GAUSSIAN") ("ITERATE_SMOOTH") ("MEDIAN") ("NO_FT_CONVOL") ("PSF") ("SMOOTH")))
+   ("find" pro nil (lib "find.pro" nil "Astrolib") "%s, image, x, y, flux, sharp, roundness, hmin, fwhm, roundlim, sharplim" (nil ("MONITOR") ("PRINT") ("SILENT")))
+   ("FIND_ALL_DIR" fun nil (lib "find_all_dir.pro" nil "Astrolib") "Result = %s(PATH)" (nil ("PATH_FORMAT") ("PLUS_REQUIRED") ("RESET")))
+   ("FIND_WITH_DEF" fun nil (lib "find_with_def.pro" nil "Astrolib") "Result = %s(FILENAME, PATHS, EXTENSIONS)" (nil ("NOCURRENT") ("RESET")))
+   ("FindPro" pro nil (lib "findpro.pro" nil "Astrolib") "%s, Proc_Name" (nil ("DirList") ("NoPrint") ("ProList")))
+   ("chisq_fitexy" fun nil (lib "fitexy.pro" nil "Astrolib") "Result = %s(B_angle)" (nil))
+   ("fitexy" pro nil (lib "fitexy.pro" nil "Astrolib") "%s, x, y, A_intercept, B_slope, sigma_A_B, chi_sq, q" (nil ("TOLERANCE") ("X_SIGMA") ("Y_SIGMA")))
+   ("fits_add_checksum" pro nil (lib "fits_add_checksum.pro" nil "Astrolib") "%s, hdr, im" (nil ("FROM_IEEE") ("no_timestamp")))
+   ("fits_ascii_encode" fun nil (lib "fits_ascii_encode.pro" nil "Astrolib") "Result = %s(sum32)" (nil))
+   ("fits_cd_fix" pro nil (lib "fits_cd_fix.pro" nil "Astrolib") "%s, hdr" (nil ("REVERSE")))
+   ("fits_close" pro nil (lib "fits_close.pro" nil "Astrolib") "%s, fcb" (nil ("message") ("no_abort")))
+   ("fits_help" pro nil (lib "fits_help.pro" nil "Astrolib") "%s, file_or_fcb" (nil))
+   ("fits_info" pro nil (lib "fits_info.pro" nil "Astrolib") "%s, filename" (nil ("extname") ("N_ext") ("SILENT") ("TEXTOUT")))
+   ("fits_open" pro nil (lib "fits_open.pro" nil "Astrolib") "%s, filename, fcb" (nil ("append") ("fpack") ("hprint") ("message") ("no_abort") ("update") ("write")))
+   ("fits_read" pro nil (lib "fits_read.pro" nil "Astrolib") "%s, file_or_fcb, data, header, group_par" (nil ("data_only") ("enum") ("exten_no") ("extlevel") ("extname") ("extver") ("first") ("group") ("header_only") ("last") ("message") ("no_abort") ("no_pdu") ("no_unsigned") ("noscale") ("pdu") ("xtension")))
+   ("fits_test_checksum" fun nil (lib "fits_test_checksum.pro" nil "Astrolib") "Result = %s(hdr, data)" (nil ("ERRMSG") ("FROM_IEEE")))
+   ("fits_write" pro nil (lib "fits_write.pro" nil "Astrolib") "%s, file_or_fcb, data, header_in" (nil ("extlevel") ("extname") ("extver") ("header") ("message") ("no_abort") ("no_data") ("xtension")))
+   ("fitsdir" pro nil (lib "fitsdir.pro" nil "Astrolib") "%s, directory" (nil ("alt1_keywords") ("alt2_keywords") ("alt3_keywords") ("exten") ("Keywords") ("nosize") ("NoTelescope") ("TEXTOUT")))
+   ("FITSRGB_to_TIFF" pro nil (lib "fitsrgb_to_tiff.pro" nil "Astrolib") "%s, path, rgb_files, tiff_name" (nil ("BLUE") ("BY_PIXEL") ("GREEN") ("PREVIEW") ("RED")))
+   ("flegendre" fun nil (lib "flegendre.pro" nil "Astrolib") "Result = %s(x, m)" (nil))
+   ("flux2mag" fun nil (lib "flux2mag.pro" nil "Astrolib") "Result = %s(flux, zero_pt)" (nil ("ABwave")))
+   ("fm_unred" pro nil (lib "fm_unred.pro" nil "Astrolib") "%s, wave, flux, ebv, funred" (nil ("avglmc") ("c1") ("c2") ("c3") ("c4") ("ExtCurve") ("gamma") ("lmc2") ("R_V") ("x0")))
+   ("forprint" pro nil (lib "forprint.pro" nil "Astrolib") "%s, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18" (nil ("COMMENT") ("FORMAT") ("NoCOMMENT") ("NUMLINE") ("SILENT") ("STARTLINE") ("STDOUT") ("SUBSET") ("TEXTOUT") ("WIDTH")))
+   ("frebin" fun nil (lib "frebin.pro" nil "Astrolib") "Result = %s(image, nsout, nlout)" (nil ("total")))
+   ("ftab_delrow" pro nil (lib "ftab_delrow.pro" nil "Astrolib") "%s, filename, rows" (nil ("EXTEN_NO") ("NEWFILE")))
+   ("ftab_ext" pro nil (lib "ftab_ext.pro" nil "Astrolib") "%s, file_or_fcb, columns, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v45, v46, v47, v48, v49, v50" (nil ("EXTEN_NO") ("ROWS")))
+   ("ftab_help" pro nil (lib "ftab_help.pro" nil "Astrolib") "%s, file_or_fcb" (nil ("EXTEN_NO") ("TEXTOUT")))
+   ("ftab_print" pro nil (lib "ftab_print.pro" nil "Astrolib") "%s, filename, columns, rows" (nil ("EXTEN_NO") ("FMT") ("num_header_lines") ("nval_per_line") ("TEXTOUT")))
+   ("ftaddcol" pro nil (lib "ftaddcol.pro" nil "Astrolib") "%s, h, tab, name, idltype, tform, tunit, tscal, tzero, tnull" (nil))
+   ("ftcreate" pro nil (lib "ftcreate.pro" nil "Astrolib") "%s, MAXCOLS, MAXROWS, H, TAB" (nil))
+   ("ftdelcol" pro nil (lib "ftdelcol.pro" nil "Astrolib") "%s, h, tab, name" (nil))
+   ("ftdelrow" pro nil (lib "ftdelrow.pro" nil "Astrolib") "%s, h, tab, rows" (nil))
+   ("ftget" fun nil (lib "ftget.pro" nil "Astrolib") "Result = %s(hdr_or_ftstr, tab, field, rows, nulls)" (nil))
+   ("fthelp" pro nil (lib "fthelp.pro" nil "Astrolib") "%s, h" (nil ("TEXTOUT")))
+   ("fthmod" pro nil (lib "fthmod.pro" nil "Astrolib") "%s, h, field, parameter, value" (nil))
+   ("ftinfo" pro nil (lib "ftinfo.pro" nil "Astrolib") "%s, h, ft_str" (nil ("Count")))
+   ("ftkeeprow" pro nil (lib "ftkeeprow.pro" nil "Astrolib") "%s, h, tab, subs" (nil))
+   ("ftprint" pro nil (lib "ftprint.pro" nil "Astrolib") "%s, h, tab, columns, rows" (nil ("textout")))
+   ("ftput" pro nil (lib "ftput.pro" nil "Astrolib") "%s, h, tab, field, row, values, nulls" (nil))
+   ("ftsize" pro nil (lib "ftsize.pro" nil "Astrolib") "%s, h, tab, ncols, nrows, tfields, ncols_all, nrows_all" (nil ("ERRMSG")))
+   ("ftsort" pro nil (lib "ftsort.pro" nil "Astrolib") "%s, h, tab, hnew, tabnew, field" (nil ("reverse")))
+   ("FXADDPAR_CONTPAR" pro nil (lib "fxaddpar.pro" nil "Astrolib") "%s, VALUE, CONTINUED" (nil))
+   ("FXADDPAR_CONTWARN" pro nil (lib "fxaddpar.pro" nil "Astrolib") "%s, HEADER, NAME" (nil))
+   ("FXADDPAR" pro nil (lib "fxaddpar.pro" nil "Astrolib") "%s, HEADER, NAME, VALUE, COMMENT" (nil ("AFTER") ("BEFORE") ("ERRMSG") ("FORMAT") ("MISSING") ("NOCONTINUE") ("NOLOGICAL") ("NULL")))
+   ("FXBADDCOL" pro nil (lib "fxbaddcol.pro" nil "Astrolib") "%s, INDEX, HEADER, ARRAY, TTYPE, COMMENT" (nil ("BIT") ("DCOMPLEX") ("ERRMSG") ("LOGICAL") ("NO_TDIM") ("TCUNI") ("TDELT") ("TDESC") ("TDISP") ("TDMAX") ("TDMIN") ("TNULL") ("TROTA") ("TRPIX") ("TRVAL") ("TSCAL") ("TUNIT") ("TZERO") ("VARIABLE")))
+   ("FXBCLOSE" pro nil (lib "fxbclose.pro" nil "Astrolib") "%s, UNIT" (nil ("ERRMSG")))
+   ("FXBCOLNUM" fun nil (lib "fxbcolnum.pro" nil "Astrolib") "Result = %s(UNIT, COL)" (nil ("ERRMSG")))
+   ("FXBCREATE" pro nil (lib "fxbcreate.pro" nil "Astrolib") "%s, UNIT, FILENAME, HEADER, EXTENSION" (nil ("ERRMSG")))
+   ("FXBDIMEN" fun nil (lib "fxbdimen.pro" nil "Astrolib") "Result = %s(UNIT, COL)" (nil ("ERRMSG")))
+   ("FXBFIND" pro nil (lib "fxbfind.pro" nil "Astrolib") "%s, P1, KEYWORD, COLUMNS, VALUES, N_FOUND, DEFAULT" (nil ("COMMENTS")))
+   ("FXBFINDLUN" fun nil (lib "fxbfindlun.pro" nil "Astrolib") "Result = %s(UNIT)" (nil))
+   ("FXBFINISH" pro nil (lib "fxbfinish.pro" nil "Astrolib") "%s, UNIT" (nil ("ERRMSG")))
+   ("FXBGROW" pro nil (lib "fxbgrow.pro" nil "Astrolib") "%s, UNIT, HEADER, NROWS" (nil ("BUFFERSIZE") ("ERRMSG") ("NOZERO")))
+   ("FXBHEADER" fun nil (lib "fxbheader.pro" nil "Astrolib") "Result = %s(UNIT)" (nil))
+   ("FXBHELP" pro nil (lib "fxbhelp.pro" nil "Astrolib") "%s, UNIT" (nil))
+   ("FXBHMAKE" pro nil (lib "fxbhmake.pro" nil "Astrolib") "%s, HEADER, NROWS, EXTNAME, COMMENT" (nil ("DATE") ("ERRMSG") ("EXTLEVEL") ("EXTVER") ("INITIALIZE")))
+   ("FXBISOPEN" fun nil (lib "fxbisopen.pro" nil "Astrolib") "Result = %s(UNIT)" (nil))
+   ("FXBOPEN" pro nil (lib "fxbopen.pro" nil "Astrolib") "%s, UNIT, FILENAME0, EXTENSION, HEADER" (nil ("ACCESS") ("ERRMSG") ("NO_TDIM") ("REOPEN")))
+   ("FXBPARSE" pro nil (lib "fxbparse.pro" nil "Astrolib") "%s, ILUN, HEADER" (nil ("ERRMSG") ("NO_TDIM")))
+   ("FXBREAD" pro nil (lib "fxbread.pro" nil "Astrolib") "%s, UNIT, DATA, COL, ROW" (nil ("DIMENSIONS") ("ERRMSG") ("NANVALUE") ("NOIEEE") ("NOSCALE") ("VIRTUAL")))
+   ("FXBREADM_CONV" pro nil (lib "fxbreadm.pro" nil "Astrolib") "%s, BB, DD, CTYPE, PERROW, NROWS" (nil ("DEFAULT_FLOAT") ("NANVALUE") ("NOIEEE") ("NOSCALE") ("TNULL_FLAG") ("TNULL_VALUE") ("TSCAL") ("TZERO") ("VARICOL")))
+   ("FXBREADM" pro nil (lib "fxbreadm.pro" nil "Astrolib") "%s, UNIT, COL, D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, D30, D31, D32, D33, D34, D35, D36, D37, D38, D39, D40, D41, D42, D43, D44, D45, D46, D47" (nil ("BUFFERSIZE") ("DEFAULT_FLOAT") ("DIMENSIONS") ("ERRMSG") ("NANVALUE") ("NOIEEE") ("NOSCALE") ("PASS_METHOD") ("POINTERS") ("ROW") ("STATUS") ("VIRTUAL") ("WARNMSG")))
+   ("FXBSTATE" fun nil (lib "fxbstate.pro" nil "Astrolib") "Result = %s(UNIT)" (nil))
+   ("FXBTDIM" fun nil (lib "fxbtdim.pro" nil "Astrolib") "Result = %s(TDIM_KEYWORD)" (nil))
+   ("FXBTFORM" pro nil (lib "fxbtform.pro" nil "Astrolib") "%s, HEADER, TBCOL, IDLTYPE, FORMAT, NUMVAL, MAXVAL" (nil ("ERRMSG")))
+   ("FXBWRITE" pro nil (lib "fxbwrite.pro" nil "Astrolib") "%s, UNIT, DATA, COL, ROW" (nil ("BIT") ("ERRMSG") ("NANVALUE")))
+   ("FXBWRITM" pro nil (lib "fxbwritm.pro" nil "Astrolib") "%s, UNIT, COL, D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, D30, D31, D32, D33, D34, D35, D36, D37, D38, D39, D40, D41, D42, D43, D44, D45, D46, D47, D48, D49" (nil ("BUFFERSIZE") ("ERRMSG") ("NANVALUE") ("NOIEEE") ("NOSCALE") ("PASS_METHOD") ("POINTERS") ("ROW") ("STATUS") ("WARNMSG")))
+   ("FXFINDEND" pro nil (lib "fxfindend.pro" nil "Astrolib") "%s, UNIT, EXTENSION" (nil))
+   ("FXHCLEAN" pro nil (lib "fxhclean.pro" nil "Astrolib") "%s, HEADER" (nil ("ERRMSG")))
+   ("FXHMAKE" pro nil (lib "fxhmake.pro" nil "Astrolib") "%s, HEADER, DATA" (nil ("DATE") ("ERRMSG") ("EXTEND") ("INITIALIZE") ("XTENSION")))
+   ("FXHMODIFY" pro nil (lib "fxhmodify.pro" nil "Astrolib") "%s, FILENAME, NAME, VALUE, COMMENT" (nil ("AFTER") ("BEFORE") ("ERRMSG") ("EXTENSION") ("FORMAT") ("NOGROW")))
+   ("FXHREAD" pro nil (lib "fxhread.pro" nil "Astrolib") "%s, UNIT, HEADER, STATUS" (nil))
+   ("FXMOVE" fun nil (lib "fxmove.pro" nil "Astrolib") "Result = %s(UNIT, EXTEN)" (nil ("ERRMSG") ("EXT_NO") ("SILENT")))
+   ("FXPAR" fun nil (lib "fxpar.pro" nil "Astrolib") "Result = %s(HDR, NAME, ABORT)" (nil ("COMMENT") ("COUNT") ("DATATYPE") ("MISSING") ("NAN") ("NOCONTINUE") ("NULL") ("POSTCHECK") ("PRECHECK") ("START")))
+   ("FXPARPOS" fun nil (lib "fxparpos.pro" nil "Astrolib") "Result = %s(KEYWRD, IEND)" (nil ("AFTER") ("BEFORE")))
+   ("FXPOSIT" fun nil (lib "fxposit.pro" nil "Astrolib") "Result = %s(XFILE, EXT_NO)" (nil ("COMPRESS") ("ERRMSG") ("EXTNUM") ("FPACK") ("HEADERONLY") ("LUNIT") ("NO_FPACK") ("readonly") ("SILENT") ("UNIXPIPE")))
+   ("FXREAD" pro nil (lib "fxread.pro" nil "Astrolib") "%s, FILENAME, DATA, HEADER, P1, P2, P3, P4, P5" (nil ("AVERAGE") ("COMPRESS") ("ERRMSG") ("EXTENSION") ("NANVALUE") ("NODATA") ("NOSCALE") ("NOUPDATE") ("PROMPT") ("YSTEP")))
+   ("FXWRITE" pro nil (lib "fxwrite.pro" nil "Astrolib") "%s, FILENAME, HEADER, DATA" (nil ("APPEND") ("ERRMSG") ("NANVALUE") ("NOUPDATE")))
+   ("GAL_FLAT" fun nil (lib "gal_flat.pro" nil "Astrolib") "Result = %s(IMAGE, ANG, INC, CEN)" (nil ("INTERP")))
+   ("gal_uvw" pro nil (lib "gal_uvw.pro" nil "Astrolib") "%s, u, v, w" (nil ("dec") ("distance") ("LSR") ("plx") ("pmdec") ("pmra") ("ra") ("vrad")))
+   ("dtdz" fun nil (lib "galage.pro" nil "Astrolib") "Result = %s(z)" (nil ("lambda0") ("q0")))
+   ("galage" fun nil (lib "galage.pro" nil "Astrolib") "Result = %s(z, zform)" (nil ("h0") ("k") ("lambda0") ("Omega_m") ("q0") ("SILENT")))
+   ("gaussian" fun nil (lib "gaussian.pro" nil "Astrolib") "Result = %s(xi, parms, pderiv)" (nil ("DOUBLE")))
+   ("gcirc" pro nil (lib "gcirc.pro" nil "Astrolib") "%s, u, ra1, dc1, ra2, dc2, dis" (nil))
+   ("gcntrd" pro nil (lib "gcntrd.pro" nil "Astrolib") "%s, img, x, y, xcen, ycen, fwhm" (nil ("DEBUG") ("keepcenter") ("maxgood") ("SILENT")))
+   ("geo2eci" fun nil (lib "geo2eci.pro" nil "Astrolib") "Result = %s(incoord, JDtim)" (nil))
+   ("geo2geodetic" fun nil (lib "geo2geodetic.pro" nil "Astrolib") "Result = %s(gcoord)" (nil ("EQUATORIAL_RADIUS") ("PLANET") ("POLAR_RADIUS")))
+   ("geo2mag" fun nil (lib "geo2mag.pro" nil "Astrolib") "Result = %s(incoord)" (nil))
+   ("geodetic2geo" fun nil (lib "geodetic2geo.pro" nil "Astrolib") "Result = %s(ecoord)" (nil ("EQUATORIAL_RADIUS") ("PLANET") ("POLAR_RADIUS")))
+   ("GET_COORDS" pro nil (lib "get_coords.pro" nil "Astrolib") "%s, Coords, PromptString, NumVals" (nil ("InString") ("Quiet")))
+   ("get_date" pro nil (lib "get_date.pro" nil "Astrolib") "%s, dte, in_date" (nil ("OLD") ("TIMETAG")))
+   ("GET_EQUINOX" fun nil (lib "get_equinox.pro" nil "Astrolib") "Result = %s(HDR, CODE)" (nil ("ALT")))
+   ("get_juldate" pro nil (lib "get_juldate.pro" nil "Astrolib") "%s, jd" (nil))
+   ("getopt" fun nil (lib "getopt.pro" nil "Astrolib") "Result = %s(input, type, numopt)" (nil ("count")))
+   ("getpro" pro nil (lib "getpro.pro" nil "Astrolib") "%s, proc_name" (nil))
+   ("getpsf" pro nil (lib "getpsf.pro" nil "Astrolib") "%s, image, xc, yc, apmag, sky, ronois, phpadu, gauss, psf, idpsf, psfrad, fitrad, psfname" (nil ("DEBUG")))
+   ("getrot" pro nil (lib "getrot.pro" nil "Astrolib") "%s, hdr, rot, cdelt" (nil ("ALT") ("DEBUG") ("SILENT")))
+   ("gettok" fun nil (lib "gettok.pro" nil "Astrolib") "Result = %s(st, char)" (nil ("exact") ("notrim")))
+   ("RHOTHETA" fun nil (lib "rhotheta.pro" nil "Astrolib") "Result = %s(P, T, e, a, i, Omega, omega2, t2)" (nil))
+   ("glactc" pro nil (lib "glactc.pro" nil "Astrolib") "%s, ra, dec, year, gl, gb, j" (nil ("degree") ("fk4") ("SuperGalactic")))
+   ("glactc_pm" pro nil (lib "glactc_pm.pro" nil "Astrolib") "%s, ra, dec, mu_ra, mu_dec, year, gl, gb, mu_gl, mu_gb, j" (nil ("degree") ("fk4") ("mustar") ("SuperGalactic")))
+   ("GROUP" pro nil (lib "group.pro" nil "Astrolib") "%s, X, Y, RCRIT, NGROUP" (nil))
+   ("GSSS_StdAst" pro nil (lib "gsss_stdast.pro" nil "Astrolib") "%s, h, xpts, ypts" (nil))
+   ("GSSSadxy" pro nil (lib "gsssadxy.pro" nil "Astrolib") "%s, gsa, ra, dec, x, y" (nil ("PRINT")))
+   ("GSSSExtAst" pro nil (lib "gsssextast.pro" nil "Astrolib") "%s, h, astr, noparams" (nil))
+   ("GSSSxyad" pro nil (lib "gsssxyad.pro" nil "Astrolib") "%s, gsa, xin, yin, ra, dec" (nil ("PRINT")))
+   ("hadec2altaz" pro nil (lib "hadec2altaz.pro" nil "Astrolib") "%s, ha, dec, lat, alt, az" (nil ("WS")))
+   ("hastrom" pro nil (lib "hastrom.pro" nil "Astrolib") "%s, oldim, oldhd, newim, newhd, refhd" (nil ("CUBIC") ("DEGREE") ("ERRMSG") ("INTERP") ("MISSING") ("NGRID") ("SILENT")))
+   ("hboxave" pro nil (lib "hboxave.pro" nil "Astrolib") "%s, oldim, oldhd, newim, newhd, box" (nil ("ERRMSG")))
+   ("hcongrid" pro nil (lib "hcongrid.pro" nil "Astrolib") "%s, oldim, oldhd, newim, newhd, newx, newy" (nil ("ALT") ("CUBIC") ("ERRMSG") ("HALF_HALF") ("INTERP") ("OUTSIZE")))
+   ("HEADFITS" fun nil (lib "headfits.pro" nil "Astrolib") "Result = %s(filename)" (nil ("Compress") ("ERRMSG") ("EXTEN") ("SILENT")))
+   ("HELIO" pro nil (lib "helio.pro" nil "Astrolib") "%s, JD, LIST, HRAD, HLONG, HLAT" (nil ("RADIAN")))
+   ("helio_jd" fun nil (lib "helio_jd.pro" nil "Astrolib") "Result = %s(date, ra, dec)" (nil ("B1950") ("TIME_DIFF")))
+   ("helio_rv" fun nil (lib "helio_rv.pro" nil "Astrolib") "Result = %s(HJD, T, P, V0, K, e, omega)" (nil))
+   ("hermite" fun nil (lib "hermite.pro" nil "Astrolib") "Result = %s(xx, ff, x)" (nil ("FDERIV")))
+   ("heuler" pro nil (lib "heuler.pro" nil "Astrolib") "%s, h_or_astr" (nil ("alt_in") ("alt_out") ("celestial") ("ecliptic") ("Galactic")))
+   ("hextract" pro nil (lib "hextract.pro" nil "Astrolib") "%s, oldim, oldhd, newim, newhd, x0, x1, y0, y1" (nil ("ALT") ("ERRMSG") ("SILENT")))
+   ("hgrep" pro nil (lib "hgrep.pro" nil "Astrolib") "%s, header, substring" (nil ("keepcase") ("linenum")))
+   ("HISTOGAUSS" pro nil (lib "histogauss.pro" nil "Astrolib") "%s, SAMPLE, A, XX, YY, GX, GY" (nil ("_EXTRA") ("CHARSIZE") ("FONT") ("NOFIT") ("NOPLOT") ("Window")))
+   ("hor2eq" pro nil (lib "hor2eq.pro" nil "Astrolib") "%s, alt, az, jd, ra, dec, ha" (nil ("_extra") ("aberration_") ("altitude") ("B1950") ("lat") ("lon") ("nutate_") ("obsname") ("precess_") ("refract_") ("verbose") ("WS")))
+   ("host_to_ieee" pro nil (lib "host_to_ieee.pro" nil "Astrolib") "%s, data" (nil ("IDLTYPE")))
+   ("HPRECESS" pro nil (lib "hprecess.pro" nil "Astrolib") "%s, HDR, YEARF" (nil))
+   ("hprint" pro nil (lib "hprint.pro" nil "Astrolib") "%s, h, firstline" (nil))
+   ("hrebin" pro nil (lib "hrebin.pro" nil "Astrolib") "%s, oldim, oldhd, newim, newhd, newx, newy" (nil ("ALT") ("ERRMSG") ("OUTSIZE") ("SAMPLE") ("TOTAL")))
+   ("hreverse" pro nil (lib "hreverse.pro" nil "Astrolib") "%s, oldim, oldhd, newim, newhd, subs" (nil ("ERRMSG") ("SILENT")))
+   ("hrot" pro nil (lib "hrot.pro" nil "Astrolib") "%s, oldim, oldhd, newim, newhd, angle, xc, yc, int" (nil ("CUBIC") ("ERRMSG") ("INTERP") ("MISSING") ("PIVOT")))
+   ("hrotate" pro nil (lib "hrotate.pro" nil "Astrolib") "%s, oldim, oldhd, newim, newhd, direction" (nil ("ERRMSG")))
+   ("ieee_to_host" pro nil (lib "ieee_to_host.pro" nil "Astrolib") "%s, data" (nil ("IDLTYPE")))
+   ("imcontour" pro nil (lib "imcontour.pro" nil "Astrolib") "%s, im, hdr" (nil ("_EXTRA") ("NOerase") ("OVERLAY") ("PUTINFO") ("SUBTITLE") ("TYPE") ("window") ("XDELTA") ("XMID") ("XTITLE") ("YDELTA") ("YMID") ("YTITLE")))
+   ("imdbase" pro nil (lib "imdbase.pro" nil "Astrolib") "%s, hdr, catalogue, list" (nil ("ALT") ("SILENT") ("SUBLIST") ("XPOS") ("XRANGE") ("YPOS") ("YRANGE")))
+   ("imf" fun nil (lib "imf.pro" nil "Astrolib") "Result = %s(mass, expon, mass_range)" (nil))
+   ("imlist" pro nil (lib "imlist.pro" nil "Astrolib") "%s, image, xc, yc" (nil ("DESCRIP") ("DX") ("DY") ("OFFSET") ("TEXTOUT") ("WIDTH")))
+   ("irafdir" pro nil (lib "irafdir.pro" nil "Astrolib") "%s, directory" (nil ("TEXTOUT")))
+   ("irafrd" pro nil (lib "irafrd.pro" nil "Astrolib") "%s, im, hd, filename" (nil ("SILENT")))
+   ("irafwrt" pro nil (lib "irafwrt.pro" nil "Astrolib") "%s, image, hd, filename" (nil ("PIXDIR")))
+   ("is_ieee_big" fun nil (lib "is_ieee_big.pro" nil "Astrolib") "Result = %s" (nil))
+   ("GETWRD" fun nil (lib "getwrd.pro" nil "Astrolib") "Result = %s(TXTSTR, NTH, MTH)" (nil ("delimiter") ("help") ("last") ("location") ("notrim") ("nwords")))
+   ("ismeuv" fun nil (lib "ismeuv.pro" nil "Astrolib") "Result = %s(wave, Hcol, HeIcol, HeIIcol)" (nil ("Fano")))
+   ("JDCNV" pro nil (lib "jdcnv.pro" nil "Astrolib") "%s, YR, MN, DAY, HR, JULIAN" (nil))
+   ("jplephinterp_calc" pro nil (lib "jplephinterp.pro" nil "Astrolib") "%s, info, raw, obj, t, x, y, z, vx, vy, vz" (nil ("tbase") ("velocity")))
+   ("jplephinterp_denew" pro nil (lib "jplephinterp.pro" nil "Astrolib") "%s, info, raw, obj, t, x, y, z, vx, vy, vz" (nil ("tbase") ("velocity")))
+   ("jplephinterp" pro nil (lib "jplephinterp.pro" nil "Astrolib") "%s, info, raw, t, x, y, z, vx, vy, vz" (nil ("center") ("decode_obj") ("earth") ("objectname") ("pos_vel_factor") ("posunits") ("sun") ("tbase") ("velocity") ("velunits") ("xobjnum")))
+   ("jplephpar" fun nil (lib "jplephread.pro" nil "Astrolib") "Result = %s(header, parname)" (nil ("default") ("fatal")))
+   ("jplephval" fun nil (lib "jplephread.pro" nil "Astrolib") "Result = %s(names, values, name)" (nil ("default") ("fatal")))
+   ("jplephread" pro nil (lib "jplephread.pro" nil "Astrolib") "%s, filename, info, raw, jdlimits" (nil ("errmsg") ("status")))
+   ("jplephtest" pro nil (lib "jplephtest.pro" nil "Astrolib") "%s, ephfile, testfile" (nil ("pause")))
+   ("jprecess" pro nil (lib "jprecess.pro" nil "Astrolib") "%s, ra, dec, ra_2000, dec_2000" (nil ("EPOCH") ("MU_RADEC") ("PARALLAX") ("RAD_VEL")))
+   ("JULDATE" pro nil (lib "juldate.pro" nil "Astrolib") "%s, DATE, JD" (nil ("PROMPT")))
+   ("ksone" pro nil (lib "ksone.pro" nil "Astrolib") "%s, data, func_name, d, prob" (nil ("_EXTRA") ("PLOT") ("Window")))
+   ("kstwo" pro nil (lib "kstwo.pro" nil "Astrolib") "%s, data1, data2, D, prob" (nil))
+   ("kuiperone" pro nil (lib "kuiperone.pro" nil "Astrolib") "%s, data, func_name, d, prob" (nil ("_EXTRA") ("PLOT") ("WINDOW")))
+   ("kuipertwo" pro nil (lib "kuipertwo.pro" nil "Astrolib") "%s, data1, data2, D, prob" (nil ("_EXTRA") ("PLOT") ("WINDOW")))
+   ("PERMUTE" fun nil (lib "permute.pro" nil "Astrolib") "Result = %s(N, Seed)" (nil))
+   ("isarray" fun nil (lib "isarray.pro" nil "Astrolib") "Result = %s(a)" (nil))
+   ("lineid_plot" pro nil (lib "lineid_plot.pro" nil "Astrolib") "%s, wave, flux, wline, text1, text2" (nil ("_EXTRA") ("extend") ("lcharsize") ("lcharthick") ("window")))
+   ("linmix_atanh" fun nil (lib "linmix_err.pro" nil "Astrolib") "Result = %s(x)" (nil))
+   ("linmix_robsig" fun nil (lib "linmix_err.pro" nil "Astrolib") "Result = %s(x)" (nil))
+   ("loglik_mixerr" fun nil (lib "linmix_err.pro" nil "Astrolib") "Result = %s(x, y, xvar, yvar, xycov, delta, theta, pi, mu, tausqr, Glabel)" (nil))
+   ("logprior_mixerr" fun nil (lib "linmix_err.pro" nil "Astrolib") "Result = %s(mu, mu0, tausqr, usqr, wsqr)" (nil))
+   ("linmix_metro_update" fun nil (lib "linmix_err.pro" nil "Astrolib") "Result = %s(logpost_new, logpost_old, seed, log_jrat)" (nil))
+   ("linmix_metro_results" pro nil (lib "linmix_err.pro" nil "Astrolib") "%s, arate, ngauss" (nil))
+   ("linmix_err" pro nil (lib "linmix_err.pro" nil "Astrolib") "%s, x, y, post" (nil ("delta") ("maxiter") ("metro") ("miniter") ("ngauss") ("silent") ("xsig") ("xycov") ("ysig")))
+   ("linterp" pro nil (lib "linterp.pro" nil "Astrolib") "%s, Xtab, Ytab, Xint, Yint" (nil ("MISSING") ("NoInterp")))
+   ("LIST_WITH_PATH" fun nil (lib "list_with_path.pro" nil "Astrolib") "Result = %s(FILENAME, PATHS)" (nil ("COUNT") ("NOCURRENT")))
+   ("lsf_rotate" fun nil (lib "lsf_rotate.pro" nil "Astrolib") "Result = %s(deltav, vsini)" (nil ("EPSILON") ("VELGRID")))
+   ("ldist" fun nil (lib "lumdist.pro" nil "Astrolib") "Result = %s(z)" (nil ("lambda0") ("q0")))
+   ("lumdist" fun nil (lib "lumdist.pro" nil "Astrolib") "Result = %s(z)" (nil ("h0") ("k") ("Lambda0") ("Omega_m") ("q0") ("Silent")))
+   ("mag2flux" fun nil (lib "mag2flux.pro" nil "Astrolib") "Result = %s(mag, zero_pt)" (nil ("ABwave")))
+   ("mag2geo" fun nil (lib "mag2geo.pro" nil "Astrolib") "Result = %s(incoord)" (nil))
+   ("make_2d" pro nil (lib "make_2d.pro" nil "Astrolib") "%s, x, y, xx, yy" (nil))
+   ("make_astr" pro nil (lib "make_astr.pro" nil "Astrolib") "%s, astr" (nil ("AXES") ("CD") ("CRPIX") ("CRVAL") ("CTYPE") ("DATE_OBS") ("DELTA") ("EQUINOX") ("LATPOLE") ("LONGPOLE") ("MJD_OBS") ("NAXIS") ("pv1") ("PV2") ("RADECSYS")))
+   ("match" pro nil (lib "match.pro" nil "Astrolib") "%s, a, b, suba, subb" (nil ("COUNT") ("epsilon") ("SORT")))
+   ("match2" pro nil (lib "match2.pro" nil "Astrolib") "%s, a, b, suba, subb" (nil))
+   ("max_entropy" pro nil (lib "max_entropy.pro" nil "Astrolib") "%s, data, psf, deconv, multipliers" (nil ("FT_PSF") ("LINEAR") ("LOGMIN") ("NO_FT") ("RE_CONVOL_IMAGE")))
+   ("Max_Likelihood" pro nil (lib "max_likelihood.pro" nil "Astrolib") "%s, data, psf, deconv, Re_conv" (nil ("FT_PSF") ("GAUSSIAN") ("NO_FT") ("POSITIVITY_EPS") ("UNDERFLOW_ZERO")))
+   ("MEANCLIP" pro nil (lib "meanclip.pro" nil "Astrolib") "%s, Image, Mean, Sigma" (nil ("CLIPSIG") ("CONVERGE_NUM") ("DOUBLE") ("MAXITER") ("SUBS") ("VERBOSE")))
+   ("medarr" pro nil (lib "medarr.pro" nil "Astrolib") "%s, inarr, outarr, mask, output_mask" (nil))
+   ("MEDSMOOTH" fun nil (lib "medsmooth.pro" nil "Astrolib") "Result = %s(ARRAY, WINDOW)" (nil))
+   ("minF_bracket" pro nil (lib "minf_bracket.pro" nil "Astrolib") "%s, xa, xb, xc, fa, fb, fc" (nil ("DIRECTION") ("FUNC_NAME") ("POINT_NDIM")))
+   ("minF_conj_grad" pro nil (lib "minf_conj_grad.pro" nil "Astrolib") "%s, p_min, f_min, conv_factor" (nil ("FUNC_NAME") ("INITIALIZE") ("QUADRATIC") ("TOLERANCE") ("USE_DERIV")))
+   ("call_func_deriv" fun nil (lib "minf_parabol_d.pro" nil "Astrolib") "Result = %s(func_name, x, deriv)" (nil ("DIRECTION") ("POINT_NDIM")))
+   ("minF_parabol_D" pro nil (lib "minf_parabol_d.pro" nil "Astrolib") "%s, xa, xb, xc, xmin, fmin" (nil ("DIRECTION") ("FUNC_NAME") ("MAX_ITERATIONS") ("POINT_NDIM") ("TOLERANCE")))
+   ("minF_parabolic" pro nil (lib "minf_parabolic.pro" nil "Astrolib") "%s, xa, xb, xc, xmin, fmin" (nil ("DIRECTION") ("FUNC_NAME") ("MAX_ITERATIONS") ("POINT_NDIM") ("TOLERANCE")))
+   ("minmax" fun nil (lib "minmax.pro" nil "Astrolib") "Result = %s(array, subs)" (nil ("DIMEN") ("NAN")))
+   ("mkhdr" pro nil (lib "mkhdr.pro" nil "Astrolib") "%s, header, im, naxisx" (nil ("EXTEND") ("IMAGE")))
+   ("mlinmix_chol_invert" fun nil (lib "mlinmix_err.pro" nil "Astrolib") "Result = %s(L)" (nil))
+   ("mlinmix_posdef_invert" pro nil (lib "mlinmix_err.pro" nil "Astrolib") "%s, A" (nil))
+   ("mlinmix_err" pro nil (lib "mlinmix_err.pro" nil "Astrolib") "%s, x, y, post" (nil ("delta") ("maxiter") ("miniter") ("ngauss") ("silent") ("xvar") ("xycov") ("yvar")))
+   ("mmm" pro nil (lib "mmm.pro" nil "Astrolib") "%s, sky_vector, skymod, sigma, skew" (nil ("DEBUG") ("HIGHBAD") ("INTEGER") ("MAXITER") ("MINSKY") ("Nsky") ("ReadNoise") ("SILENT")))
+   ("MODFITS" pro nil (lib "modfits.pro" nil "Astrolib") "%s, filename, data, header" (nil ("ERRMSG") ("EXTEN_NO") ("EXTNAME")))
+   ("month_cnv" fun nil (lib "month_cnv.pro" nil "Astrolib") "Result = %s(MonthInput)" (nil ("Low") ("Short") ("Up")))
+   ("MOONPOS" pro nil (lib "moonpos.pro" nil "Astrolib") "%s, jd, ra, dec, dis, geolong, geolat" (nil ("RADIAN")))
+   ("mphase" pro nil (lib "mphase.pro" nil "Astrolib") "%s, jd, k" (nil))
+   ("mrandomn" fun nil (lib "mrandomn.pro" nil "Astrolib") "Result = %s(seed, covar, nrand)" (nil ("STATUS")))
+   ("mrd_hread" pro nil (lib "mrd_hread.pro" nil "Astrolib") "%s, unit, header, status" (nil ("ERRMSG") ("FIRSTBLOCK") ("NO_BADHEADER") ("SILENT") ("SKIPDATA")))
+   ("mrd_skip" pro nil (lib "mrd_skip.pro" nil "Astrolib") "%s, unit, nskip" (nil))
+   ("mrd_struct" fun nil (lib "mrd_struct.pro" nil "Astrolib") "Result = %s(names, values, nrow)" (nil ("no_execute") ("old_struct") ("silent") ("structyp") ("tempdir")))
+   ("mrd_fxpar" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, hdr, xten, nfld, nrow, rsize, fnames, fforms, scales, offsets" (nil))
+   ("mrd_dofn" fun nil (lib "mrdfits.pro" nil "Astrolib") "Result = %s(name, index, use_colnum)" (nil ("alias")))
+   ("mrd_doff" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, form, dim, type" (nil))
+   ("mrd_chkfn" fun nil (lib "mrdfits.pro" nil "Astrolib") "Result = %s(name, namelist, index)" (nil ("silent")))
+   ("mrd_unsigned_offset" fun nil (lib "mrdfits.pro" nil "Astrolib") "Result = %s(type)" (nil))
+   ("mrd_chkunsigned" fun nil (lib "mrdfits.pro" nil "Astrolib") "Result = %s(bitpix, scale, zero)" (nil ("unsigned")))
+   ("mrd_unsignedtype" fun nil (lib "mrdfits.pro" nil "Astrolib") "Result = %s(data)" (nil))
+   ("mrd_version" fun nil (lib "mrdfits.pro" nil "Astrolib") "Result = %s" (nil))
+   ("mrd_atype" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, form, type, slen" (nil))
+   ("mrd_read_ascii" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, unit, range, nbytes, nrows, nfld, typarr, posarr, lenarr, nullarr, table" (nil ("old_struct") ("rows")))
+   ("mrd_ascii" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, header, structyp, use_colnum, range, table, nbytes, nrows, nfld, typarr, posarr, lenarr, nullarr, fnames, fvalues, scales, offsets, scaling, status" (nil ("alias") ("columns") ("outalias") ("rows") ("silent")))
+   ("mrd_columns" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, table, columns, fnames, fvalues, vcls, vtpes, scales, offsets, scaling" (nil ("silent") ("structyp")))
+   ("mrd_read_image" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, unit, range, maxd, rsize, table" (nil ("rows") ("status") ("unixpipe")))
+   ("mrd_axes_trunc" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, naxis, dims, silent" (nil))
+   ("mrd_image" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, header, range, maxd, rsize, table, scales, offsets, scaling, status" (nil ("rows") ("silent") ("unsigned")))
+   ("mrd_ptrscale" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, array, scale, offset" (nil))
+   ("mrd_string" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, table, header, typarr, fnames, fvalues, nrec" (nil ("silent") ("structyp")))
+   ("mrd_scale" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, type, scales, offsets, table, header, fnames, fvalues, nrec" (nil ("dscale") ("silent") ("structyp")))
+   ("mrd_varcolumn" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, vtype, array, heap, off, siz" (nil))
+   ("mrd_fixcolumn" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, vtype, array, heap, off, siz" (nil))
+   ("mrd_read_heap" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, unit, header, range, fnames, fvalues, vcls, vtpes, table, structyp, scaling, scales, offsets, status" (nil ("columns") ("fixed_var") ("pointer_var") ("rows") ("silent")))
+   ("mrd_read_table" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, unit, range, rsize, structyp, nrows, nfld, typarr, table" (nil ("rows") ("unixpipe")))
+   ("mrd_tdim" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, header, index, flen, arrstr" (nil ("no_tdim")))
+   ("mrd_table" pro nil (lib "mrdfits.pro" nil "Astrolib") "%s, header, structyp, use_colnum, range, rsize, table, nrows, nfld, typarr, fnames, fvalues, vcls, vtpes, scales, offsets, scaling, status" (nil ("alias") ("columns") ("emptystring") ("no_tdim") ("outalias") ("rows") ("silent") ("unsigned")))
+   ("mrdfits" fun nil (lib "mrdfits.pro" nil "Astrolib") "Result = %s(file, extension, header)" (nil ("alias") ("columns") ("compress") ("dscale") ("emptystring") ("error_action") ("extnum") ("fixed_var") ("fpack") ("fscale") ("no_fpack") ("no_tdim") ("outalias") ("pointer_var") ("range") ("rows") ("silent") ("status") ("structyp") ("unsigned") ("use_colnum") ("version")))
+   ("multinom" fun nil (lib "multinom.pro" nil "Astrolib") "Result = %s(n, p, nrand)" (nil ("seed")))
+   ("multiplot" pro nil (lib "multiplot.pro" nil "Astrolib") "%s, pmulti" (nil ("default") ("doxaxis") ("doyaxis") ("gap") ("help") ("initialize") ("mtitle") ("mTitOffset") ("mTitSize") ("mxTitle") ("mxTitOffset") ("mxTitSize") ("myTitle") ("myTitOffset") ("myTitSize") ("reset") ("rowmajor") ("square") ("verbose") ("xgap") ("xtickformat") ("ygap") ("ytickformat")))
+   ("mwr_version" fun nil (lib "mwrfits.pro" nil "Astrolib") "Result = %s" (nil))
+   ("mwr_unsigned_offset" fun nil (lib "mwrfits.pro" nil "Astrolib") "Result = %s(type)" (nil))
+   ("chk_and_upd" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, header, key, value, comment" (nil ("nological")))
+   ("mwr_checktype" fun nil (lib "mwrfits.pro" nil "Astrolib") "Result = %s(tag)" (nil ("alias")))
+   ("mwr_ascii" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, input, siz, lun, bof, header" (nil ("alias") ("ascii") ("bscale") ("iscale") ("lscale") ("no_comment") ("no_types") ("null") ("separator") ("silent") ("terminator") ("use_colnum")))
+   ("mwr_dummy" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, lun" (nil))
+   ("mwr_validptr" fun nil (lib "mwrfits.pro" nil "Astrolib") "Result = %s(vtypes, nfld, index, array)" (nil))
+   ("mwr_tablehdr" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, lun, input, header, vtypes" (nil ("alias") ("bit_cols") ("logical_cols") ("nbit_cols") ("no_comment") ("no_types") ("silent") ("use_colnum")))
+   ("mwr_retable" fun nil (lib "mwrfits.pro" nil "Astrolib") "Result = %s(input, vtypes)" (nil))
+   ("mwr_writeheap" fun nil (lib "mwrfits.pro" nil "Astrolib") "Result = %s(lun, vtypes)" (nil))
+   ("mwr_tabledat" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, lun, input, header, vtypes" (nil))
+   ("mwr_pscale" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, grp, header" (nil ("pscale") ("pzero")))
+   ("mwr_findscale" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, flag, array, nbits, scale, offset, error" (nil))
+   ("mwr_scale" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, array, scale, offset" (nil ("bscale") ("iscale") ("lscale") ("null")))
+   ("mwr_header" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, lun, header" (nil))
+   ("mwr_groupinfix" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, data, group, hdr" (nil))
+   ("mwr_groupscale" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, maxval, group, hdr" (nil))
+   ("mwr_image" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, input, siz, lun, bof, hdr" (nil ("bscale") ("group") ("iscale") ("lscale") ("no_comment") ("null") ("pscale") ("pzero") ("silent")))
+   ("mwrfits" pro nil (lib "mwrfits.pro" nil "Astrolib") "%s, xinput, file, header" (nil ("alias") ("ascii") ("bit_cols") ("bscale") ("create") ("group") ("iscale") ("logical_cols") ("lscale") ("nbit_cols") ("no_comment") ("no_copy") ("no_types") ("null") ("pscale") ("pzero") ("separator") ("silent") ("status") ("terminator") ("use_colnum") ("version")))
+   ("N_bytes" fun nil (lib "n_bytes.pro" nil "Astrolib") "Result = %s(a)" (nil))
+   ("ngp" fun nil (lib "ngp.pro" nil "Astrolib") "Result = %s(value, posx, nx, posy, ny, posz, nz)" (nil ("AVERAGE") ("NO_MESSAGE") ("WRAPAROUND")))
+   ("nint" fun nil (lib "nint.pro" nil "Astrolib") "Result = %s(x)" (nil ("LONG")))
+   ("nstar" pro nil (lib "nstar.pro" nil "Astrolib") "%s, image, id, xc, yc, mags, sky, group, phpadu, readns, psfname, errmag, iter, chisq, peak" (nil ("DEBUG") ("PRINT") ("SILENT") ("VARSKY")))
+   ("nulltrim" fun nil (lib "nulltrim.pro" nil "Astrolib") "Result = %s(st)" (nil))
+   ("nutate" pro nil (lib "nutate.pro" nil "Astrolib") "%s, jd, nut_long, nut_obliq" (nil))
+   ("observatory" pro nil (lib "observatory.pro" nil "Astrolib") "%s, obsname, obs_struct" (nil ("print")))
+   ("one_arrow" pro nil (lib "one_arrow.pro" nil "Astrolib") "%s, xcen, ycen, angle, label" (nil ("arrowsize") ("charsize") ("color") ("data") ("font") ("linestyle") ("normal") ("thick")))
+   ("one_ray" pro nil (lib "one_ray.pro" nil "Astrolib") "%s, xcen, ycen, len, angle, terminus" (nil ("_EXTRA") ("data") ("nodraw") ("normal")))
+   ("oploterror" pro nil (lib "oploterror.pro" nil "Astrolib") "%s, x, y, xerr, yerr" (nil ("_EXTRA") ("ADDCMD") ("ERRCOLOR") ("ERRSTYLE") ("ERRTHICK") ("HATLENGTH") ("HIBAR") ("LOBAR") ("NOCLIP") ("NOHAT") ("NSKIP") ("Nsum") ("THICK") ("WINDOW")))
+   ("ordinal" fun nil (lib "ordinal.pro" nil "Astrolib") "Result = %s(num)" (nil))
+   ("partvelvec" pro nil (lib "partvelvec.pro" nil "Astrolib") "%s, velx, vely, posx, posy, x, y" (nil ("_EXTRA") ("COLOR") ("FRACTION") ("LENGTH") ("NOCLIP") ("OVER") ("VECCOLORS") ("WINDOW")))
+   ("PCA" pro nil (lib "pca.pro" nil "Astrolib") "%s, data, eigenval, eigenvect, percentages, proj_obj, proj_atr" (nil ("COVARIANCE") ("MATRIX") ("SILENT") ("SSQ") ("TEXTOUT")))
+   ("pent" fun nil (lib "pent.pro" nil "Astrolib") "Result = %s(p, t, x, m, n)" (nil))
+   ("pixcolor" pro nil (lib "pixcolor.pro" nil "Astrolib") "%s, pix_value, color" (nil))
+   ("Arc" fun nil (lib "pixwt.pro" nil "Astrolib") "Result = %s(x, y0, y1, r)" (nil))
+   ("Chord" fun nil (lib "pixwt.pro" nil "Astrolib") "Result = %s(x, y0, y1)" (nil))
+   ("Oneside" fun nil (lib "pixwt.pro" nil "Astrolib") "Result = %s(x, y0, y1, r)" (nil))
+   ("Intarea" fun nil (lib "pixwt.pro" nil "Astrolib") "Result = %s(xc, yc, r, x0, x1, y0, y1)" (nil))
+   ("Pixwt" fun nil (lib "pixwt.pro" nil "Astrolib") "Result = %s(xc, yc, r, x, y)" (nil))
+   ("pkfit" pro nil (lib "pkfit.pro" nil "Astrolib") "%s, f, scale, x, y, sky, radius, ronois, phpadu, gauss, psf, errmag, chi, sharp, niter" (nil ("DEBUG")))
+   ("planck" fun nil (lib "planck.pro" nil "Astrolib") "Result = %s(wave, temp)" (nil))
+   ("planet_coords" pro nil (lib "planet_coords.pro" nil "Astrolib") "%s, date, ra, dec" (nil ("jd") ("jpl") ("planet")))
+   ("ploterror" pro nil (lib "ploterror.pro" nil "Astrolib") "%s, x, y, xerr, yerr" (nil ("_EXTRA") ("ERRCOLOR") ("ERRSTYLE") ("ERRTHICK") ("HATLENGTH") ("NOCLIP") ("NOHAT") ("NSKIP") ("NSUM") ("TYPE") ("WINDOW") ("XLOG") ("XRANGE") ("YLOG") ("YRANGE")))
+   ("plothist" pro nil (lib "plothist.pro" nil "Astrolib") "%s, arr, xhist, yhist" (nil ("_EXTRA") ("AUTOBin") ("axiscolor") ("BIN") ("Boxplot") ("Color") ("FCOLOR") ("Fill") ("FLINE") ("FORIENTATION") ("FPATTERN") ("FSPACING") ("FTHICK") ("Halfbin") ("LINESTYLE") ("NAN") ("NOPLOT") ("OVERPLOT") ("Peak") ("PSYM") ("rotate") ("THICK") ("WINDOW") ("xlog") ("XSTYLE") ("ylog") ("yrange") ("YSTYLE")))
+   ("plotsym" pro nil (lib "plotsym.pro" nil "Astrolib") "%s, psym, psize" (nil ("Color") ("FILL") ("thick")))
+   ("poidev" fun nil (lib "poidev.pro" nil "Astrolib") "Result = %s(xm)" (nil ("SEED")))
+   ("polint" pro nil (lib "polint.pro" nil "Astrolib") "%s, xa, ya, x, y, dy" (nil))
+   ("POLREC" pro nil (lib "polrec.pro" nil "Astrolib") "%s, R, A, X, Y" (nil ("degrees") ("help")))
+   ("poly_smooth" fun nil (lib "poly_smooth.pro" nil "Astrolib") "Result = %s(data, width)" (nil ("COEFFICIENTS") ("DEGREE") ("DERIV_ORDER") ("NLEFT") ("NRIGHT")))
+   ("polyleg" fun nil (lib "polyleg.pro" nil "Astrolib") "Result = %s(x, coeff)" (nil))
+   ("POSANG" pro nil (lib "posang.pro" nil "Astrolib") "%s, u, ra1, dc1, ra2, dc2, angle" (nil))
+   ("positivity" fun nil (lib "positivity.pro" nil "Astrolib") "Result = %s(x)" (nil ("DERIVATIVE") ("EPSILON")))
+   ("precess" pro nil (lib "precess.pro" nil "Astrolib") "%s, ra, dec, equinox1, equinox2" (nil ("FK4") ("PRINT") ("RADIAN")))
+   ("PRECESS_CD" pro nil (lib "precess_cd.pro" nil "Astrolib") "%s, cd, epoch1, epoch2, crval_old, crval_new" (nil ("FK4")))
+   ("precess_xyz" pro nil (lib "precess_xyz.pro" nil "Astrolib") "%s, x, y, z, equinox1, equinox2" (nil))
+   ("premat" fun nil (lib "premat.pro" nil "Astrolib") "Result = %s(equinox1, equinox2)" (nil ("FK4")))
+   ("prime" fun nil (lib "prime.pro" nil "Astrolib") "Result = %s(n)" (nil ("help")))
+   ("print_struct" pro nil (lib "print_struct.pro" nil "Astrolib") "%s, structure, Tags_to_print, title, string_matrix" (nil ("FILE") ("FORM_FLOAT") ("FRANGE") ("LUN_OUT") ("MAX_ELEMENTS") ("NO_TITLE") ("STRINGS") ("TNUMS") ("TRANGE") ("WHICH_TO_PRINT")))
+   ("prob_ks" pro nil (lib "prob_ks.pro" nil "Astrolib") "%s, D, N_eff, probks" (nil))
+   ("prob_kuiper" pro nil (lib "prob_kuiper.pro" nil "Astrolib") "%s, D, N_eff, probks" (nil))
+   ("psf_gaussian" fun nil (lib "psf_gaussian.pro" nil "Astrolib") "Result = %s(parameters)" (nil ("CENTROID") ("DOUBLE") ("FWHM") ("NDIMENSION") ("NORMALIZE") ("NPIXEL") ("ST_DEV") ("XY_CORREL")))
+   ("putast" pro nil (lib "putast.pro" nil "Astrolib") "%s, hdr, astr, crpix, crval, ctype" (nil ("ALT") ("CD_TYPE") ("EQUINOX") ("NAXIS")))
+   ("QDCB_GRID" pro nil (lib "qdcb_grid.pro" nil "Astrolib") "%s, DLONG, DLAT" (nil ("LABELS") ("LINESTYLE")))
+   ("qget_string" fun nil (lib "qget_string.pro" nil "Astrolib") "Result = %s(dummy)" (nil))
+   ("qsimp" pro nil (lib "qsimp.pro" nil "Astrolib") "%s, func, A, B, S" (nil ("_EXTRA") ("EPS") ("MAX_ITER")))
+   ("qtrap" pro nil (lib "qtrap.pro" nil "Astrolib") "%s, func, A, B, S" (nil ("_EXTRA") ("EPS") ("MAX_ITER")))
+   ("quadterp" pro nil (lib "quadterp.pro" nil "Astrolib") "%s, xtab, ytab, xint, yint" (nil ("MISSING")))
+   ("QueryDSS" pro nil (lib "querydss.pro" nil "Astrolib") "%s, target, Image, Header" (nil ("ESO") ("IMSIZE") ("NED") ("OUTFILE") ("STSCI") ("SURVEY") ("VERBOSE")))
+   ("Querygsc" fun nil (lib "querygsc.pro" nil "Astrolib") "Result = %s(target, dis)" (nil ("BOX") ("HOURS") ("magrange") ("VERBOSE")))
+   ("QuerySimbad" pro nil (lib "querysimbad.pro" nil "Astrolib") "%s, name, ra, de, id" (nil ("CADC") ("CFA") ("ERRMSG") ("Found") ("Hmag") ("Jmag") ("Kmag") ("NED") ("parallax") ("Print") ("Server") ("SILENT") ("Verbose") ("Vmag")))
+   ("Queryvizier" fun nil (lib "queryvizier.pro" nil "Astrolib") "Result = %s(catalog, target, dis)" (nil ("ALLCOLUMNS") ("CANADA") ("CFA") ("CONSTRAINT") ("SILENT") ("VERBOSE")))
+   ("radec" pro nil (lib "radec.pro" nil "Astrolib") "%s, ra, dec, ihr, imin, xsec, ideg, imn, xsc" (nil ("hours")))
+   ("randomchi" fun nil (lib "randomchi.pro" nil "Astrolib") "Result = %s(seed, dof, nrand)" (nil))
+   ("randomdir" fun nil (lib "randomdir.pro" nil "Astrolib") "Result = %s(seed, alpha, nrand)" (nil))
+   ("randomgam" fun nil (lib "randomgam.pro" nil "Astrolib") "Result = %s(seed, alpha, beta, nrand)" (nil))
+   ("randomp" pro nil (lib "randomp.pro" nil "Astrolib") "%s, x, pow, n" (nil ("range_x") ("seed")))
+   ("randomwish" fun nil (lib "randomwish.pro" nil "Astrolib") "Result = %s(seed, dof, S, nrand)" (nil))
+   ("rdfits_struct" pro nil (lib "rdfits_struct.pro" nil "Astrolib") "%s, filename, struct" (nil ("EXTEN") ("HEADER_ONLY") ("SILENT")))
+   ("rdfloat" pro nil (lib "rdfloat.pro" nil "Astrolib") "%s, name, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19" (nil ("COLUMNS") ("DOUBLE") ("NUMLINE") ("SILENT") ("SKIPLINE")))
+   ("RESET_RDPLOT" pro nil (lib "rdplot.pro" nil "Astrolib") "%s" (nil))
+   ("RDPLOT" pro nil (lib "rdplot.pro" nil "Astrolib") "%s, x, y, WaitFlag" (nil ("ACCUMULATE") ("BACKGROUND") ("CHANGE") ("COLOR") ("CROSS") ("CURSOR_STANDARD") ("DATA") ("DEVICE") ("DOWN") ("Err") ("FULLCURSOR") ("LINESTYLE") ("NOCLIP") ("NORMAL") ("NOWAIT") ("PRINT") ("THICK") ("WAIT") ("XTITLE") ("XVALUES") ("YTITLE") ("YVALUES")))
+   ("rdpsf" pro nil (lib "rdpsf.pro" nil "Astrolib") "%s, psf, hpsf, psfname" (nil))
+   ("read_fmr" fun nil (lib "read_fmr.pro" nil "Astrolib") "Result = %s(filename)" (nil ("columns") ("help") ("missingvalue") ("use_colnum")))
+   ("read_key" fun nil (lib "read_key.pro" nil "Astrolib") "Result = %s(wait)" (nil))
+   ("readcol" pro nil (lib "readcol.pro" nil "Astrolib") "%s, name, v1, V2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50" (nil ("COMMENT") ("COMPRESS") ("COUNT") ("DEBUG") ("DELIMITER") ("FORMAT") ("NAN") ("NLINES") ("NUMLINE") ("PRESERVE_NULL") ("QUICK") ("SILENT") ("SKIPLINE") ("STRINGSKIP")))
+   ("READFITS" fun nil (lib "readfits.pro" nil "Astrolib") "Result = %s(filename, header, heap)" (nil ("CHECKSUM") ("COMPRESS") ("EXTEN_NO") ("FPACK") ("HBUFFER") ("NaNvalue") ("NO_UNSIGNED") ("NOSCALE") ("NSLICE") ("NUMROW") ("POINTLUN") ("SILENT") ("STARTROW") ("UNIXpipe")))
+   ("readfmt" pro nil (lib "readfmt.pro" nil "Astrolib") "%s, name, fmt, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25" (nil ("DEBUG") ("NUMLINE") ("SILENT") ("SKIPLINE")))
+   ("recpol" pro nil (lib "recpol.pro" nil "Astrolib") "%s, x, y, r, a" (nil ("degrees") ("help")))
+   ("rem_dup" fun nil (lib "rem_dup.pro" nil "Astrolib") "Result = %s(a, flag)" (nil))
+   ("remchar" pro nil (lib "remchar.pro" nil "Astrolib") "%s, st, char" (nil))
+   ("remove" pro nil (lib "remove.pro" nil "Astrolib") "%s, index, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25" (nil))
+   ("repchr" fun nil (lib "repchr.pro" nil "Astrolib") "Result = %s(In_String, OldChar, NewChar)" (nil))
+   ("repstr" fun nil (lib "repstr.pro" nil "Astrolib") "Result = %s(obj, in, out)" (nil))
+   ("RESISTANT_Mean" pro nil (lib "resistant_mean.pro" nil "Astrolib") "%s, Y, CUT, Mean, Sigma, Num_Rej" (nil ("dimension") ("double") ("goodvec") ("Silent") ("sumdim") ("wused")))
+   ("RINTER" fun nil (lib "rinter.pro" nil "Astrolib") "Result = %s(P, X, Y, DFDX, DFDY)" (nil ("INITIALIZE")))
+   ("ROB_CHECKFIT" fun nil (lib "rob_checkfit.pro" nil "Astrolib") "Result = %s(Y, YFIT, EPS, DEL, SIG, FRACDEV, NGOOD, W, B)" (nil ("BISQUARE_LIMIT")))
+   ("ROBUST_LINEFIT" fun nil (lib "robust_linefit.pro" nil "Astrolib") "Result = %s(XIN, YIN, YFIT, SIG, SS)" (nil ("BISECT") ("Bisquare_Limit") ("Close_Factor") ("NUMIT")))
+   ("ROBUST_POLY_FIT" fun nil (lib "robust_poly_fit.pro" nil "Astrolib") "Result = %s(X, Y, NDEG, YFIT, SIG)" (nil ("DOUBLE") ("NUMIT")))
+   ("ROBUST_SIGMA" fun nil (lib "robust_sigma.pro" nil "Astrolib") "Result = %s(Y)" (nil ("GOODVEC") ("ZERO")))
+   ("select_w_event" pro nil (lib "select_w.pro" nil "Astrolib") "%s, event" (nil))
+   ("select_w" pro nil (lib "select_w.pro" nil "Astrolib") "%s, items, iselected, comments, command_line, only_one" (nil ("columns") ("Count") ("GROUP_LEADER") ("selectin") ("y_scroll_size")))
+   ("get_pipe_filesize" pro nil (lib "get_pipe_filesize.pro" nil "Astrolib") "%s, unit, nbytes" (nil ("buffer")))
+   ("sigma_filter" fun nil (lib "sigma_filter.pro" nil "Astrolib") "Result = %s(image, box_width)" (nil ("ALL_PIXELS") ("DEVIATION_IMAGE") ("ITERATE") ("KEEP_OUTLIERS") ("MONITOR") ("N_CHANGE") ("N_SIGMA") ("RADIUS") ("VARIANCE_IMAGE")))
+   ("SIGRANGE" fun nil (lib "sigrange.pro" nil "Astrolib") "Result = %s(ARRAY)" (nil ("FRACTION") ("MISSING") ("RANGE")))
+   ("sixlin" pro nil (lib "sixlin.pro" nil "Astrolib") "%s, xx, yy, a, siga, b, sigb" (nil ("weight")))
+   ("sixty" fun nil (lib "sixty.pro" nil "Astrolib") "Result = %s(scalar)" (nil ("Trailsign")))
+   ("sky" pro nil (lib "sky.pro" nil "Astrolib") "%s, image, skymode, skysig" (nil ("_EXTRA") ("CIRCLERAD") ("MEANBACK") ("NAN") ("SILENT")))
+   ("EXTRAP" pro nil (lib "skyadj_cube.pro" nil "Astrolib") "%s, Deg, X, Y, Y2" (nil ("LIMS")))
+   ("SKYADJ_CUBE" pro nil (lib "skyadj_cube.pro" nil "Astrolib") "%s, Datacube, Skyvals, Totsky" (nil ("EDEGREE") ("EXTRAPR") ("INPUT_MASK") ("NOEDIT") ("REGION") ("SELECT") ("VERBOSE") ("XMEDSKY")))
+   ("spec_dir" fun nil (lib "spec_dir.pro" nil "Astrolib") "Result = %s(filename, extension)" (nil))
+   ("sphdist" fun nil (lib "sphdist.pro" nil "Astrolib") "Result = %s(long1, lat1, long2, lat2)" (nil ("degrees") ("help")))
+   ("srcor" pro nil (lib "srcor.pro" nil "Astrolib") "%s, x1in, y1in, x2in, y2in, dcr, ind1, ind2" (nil ("count") ("magnitude") ("option") ("silent") ("spherical")))
+   ("st_diskread" pro nil (lib "st_diskread.pro" nil "Astrolib") "%s, infiles" (nil ("DUMP")))
+   ("st_disk_data" pro nil (lib "st_diskread.pro" nil "Astrolib") "%s, unit, h, data, name, gcount, dimen, opsize, nbytes, itype" (nil))
+   ("st_disk_table" pro nil (lib "st_diskread.pro" nil "Astrolib") "%s, unit, h, data, table_available" (nil))
+   ("st_disk_geis" pro nil (lib "st_diskread.pro" nil "Astrolib") "%s, h, data, htab, tab, table_available, name, gcount, dimen, opsize, nbytes_g, itype" (nil))
+   ("starast" pro nil (lib "starast.pro" nil "Astrolib") "%s, ra, dec, x, y, cd" (nil ("hdr") ("projection") ("righthanded")))
+   ("STORE_ARRAY" pro nil (lib "store_array.pro" nil "Astrolib") "%s, DESTINATION, INSERT, INDEX" (nil))
+   ("STR_INDEX" fun nil (lib "str_index.pro" nil "Astrolib") "Result = %s(str, substr, offset)" (nil))
+   ("strcompress2" fun nil (lib "strcompress2.pro" nil "Astrolib") "Result = %s(str, chars)" (nil))
+   ("strn" fun nil (lib "strn.pro" nil "Astrolib") "Result = %s(number)" (nil ("FORMAT") ("LENGTH") ("PADCHAR") ("PADTYPE")))
+   ("strnumber" fun nil (lib "strnumber.pro" nil "Astrolib") "Result = %s(st, val)" (nil ("hex") ("L64") ("NaN")))
+   ("substar" pro nil (lib "substar.pro" nil "Astrolib") "%s, image, x, y, mag, id, psfname" (nil ("VERBOSE")))
+   ("sunpos" pro nil (lib "sunpos.pro" nil "Astrolib") "%s, jd, ra, dec, longmed, oblt" (nil ("RADIAN")))
+   ("sunsymbol" fun nil (lib "sunsymbol.pro" nil "Astrolib") "Result = %s" (nil ("FONT")))
+   ("sxaddhist" pro nil (lib "sxaddhist.pro" nil "Astrolib") "%s, history, header" (nil ("blank") ("comment") ("location") ("pdu")))
+   ("sxaddpar" pro nil (lib "sxaddpar.pro" nil "Astrolib") "%s, Header, Name, Value, Comment, Location" (nil ("after") ("before") ("format") ("missing") ("null") ("pdu") ("savecomment")))
+   ("sxdelpar" pro nil (lib "sxdelpar.pro" nil "Astrolib") "%s, h, parname" (nil))
+   ("sxginfo" pro nil (lib "sxginfo.pro" nil "Astrolib") "%s, h, par, type, sbyte, nbytes" (nil))
+   ("sxgpar" fun nil (lib "sxgpar.pro" nil "Astrolib") "Result = %s(h, par, name, type, sbyte, nbytes)" (nil))
+   ("sxgread" fun nil (lib "sxgread.pro" nil "Astrolib") "Result = %s(unit, group)" (nil))
+   ("sxhcopy" pro nil (lib "sxhcopy.pro" nil "Astrolib") "%s, h, keyword1, keyword2, hout" (nil))
+   ("sxhmake" pro nil (lib "sxhmake.pro" nil "Astrolib") "%s, data, groups, header" (nil))
+   ("sxhread" pro nil (lib "sxhread.pro" nil "Astrolib") "%s, name, header" (nil))
+   ("sxhwrite" pro nil (lib "sxhwrite.pro" nil "Astrolib") "%s, name, h" (nil))
+   ("sxmake" pro nil (lib "sxmake.pro" nil "Astrolib") "%s, unit, File, Data, Par, Groups, Header" (nil ("PSIZE")))
+   ("SXOPEN" pro nil (lib "sxopen.pro" nil "Astrolib") "%s, unit, fname, header, history, access" (nil))
+   ("SXPAR" fun nil (lib "sxpar.pro" nil "Astrolib") "Result = %s(hdr, name, abort)" (nil ("COMMENT") ("COUNT") ("IFound") ("MISSING") ("NAN") ("NoContinue") ("NULL") ("SILENT")))
+   ("sxread" fun nil (lib "sxread.pro" nil "Astrolib") "Result = %s(unit, group, par)" (nil))
+   ("SXWRITE" pro nil (lib "sxwrite.pro" nil "Astrolib") "%s, Unit, Data, Par" (nil))
+   ("ymd2dn" fun nil (lib "ymd2dn.pro" nil "Astrolib") "Result = %s(yr, m, d)" (nil ("help")))
+   ("t_aper" pro nil (lib "t_aper.pro" nil "Astrolib") "%s, image, fitsfile, apr, skyrad, badpix" (nil ("EXACT") ("NEWTABLE") ("PRINT") ("SETSKYVAL") ("SILENT")))
+   ("t_find" pro nil (lib "t_find.pro" nil "Astrolib") "%s, image, im_hdr, fitsfile, hmin, fwhm, sharplim, roundlim" (nil ("PRINT") ("SILENT")))
+   ("t_getpsf" pro nil (lib "t_getpsf.pro" nil "Astrolib") "%s, image, fitsfile, idpsf, psfrad, fitrad, psfname" (nil ("DEBUG") ("NEWTABLE")))
+   ("t_group" pro nil (lib "t_group.pro" nil "Astrolib") "%s, fitsfile, rmax" (nil ("NEWTABLE") ("xpar") ("ypar")))
+   ("t_nstar" pro nil (lib "t_nstar.pro" nil "Astrolib") "%s, image, fitsfile, psfname, groupsel" (nil ("DEBUG") ("NEWTABLE") ("PRINT") ("SILENT") ("VARSKY")))
+   ("t_substar" pro nil (lib "t_substar.pro" nil "Astrolib") "%s, image, fitsfile, id, psfname" (nil ("NOPSF") ("VERBOSE")))
+   ("sip_eval" fun nil (lib "sip_eval.pro" nil "Astrolib") "Result = %s(xy)" (nil))
+   ("file_launch" pro nil (lib "file_launch.pro" nil "Astrolib") "%s, file" (nil ("bUseJava") ("Nowait") ("ojDesktop") ("quiet")))
+   ("TPV_eval" fun nil (lib "tpv_eval.pro" nil "Astrolib") "Result = %s(xy)" (nil))
+   ("TNX_eval" fun nil (lib "tnx_eval.pro" nil "Astrolib") "Result = %s(xy)" (nil))
+   ("xi_solve_tpv" fun nil (lib "solve_astro.pro" nil "Astrolib") "Result = %s(xpixel, ypixel, pv1)" (nil ("TPVINFO")))
+   ("eta_solve_tpv" fun nil (lib "solve_astro.pro" nil "Astrolib") "Result = %s(xpixel, ypixel, pv2)" (nil ("TPVINFO")))
+   ("eta_solve_tnx" fun nil (lib "solve_astro.pro" nil "Astrolib") "Result = %s(xpixel, ypixel, params)" (nil ("TNXINFO")))
+   ("xi_solve_tnx" fun nil (lib "solve_astro.pro" nil "Astrolib") "Result = %s(xpixel, ypixel, params)" (nil ("TNXINFO")))
+   ("solve_astro" fun nil (lib "solve_astro.pro" nil "Astrolib") "Result = %s(radeg, decdeg, xpixel, ypixel)" (nil ("CRVAL") ("DISTORT") ("ETAORDER") ("ETARESID") ("ETARMS") ("n_tpvterms") ("NAXIS1") ("NAXIS2") ("NITER") ("NORTERMS") ("NREJ") ("REJECT") ("SUCCESS") ("VERBOSE") ("WFIT") ("XIORDER") ("XIRESID") ("XIRMS") ("XTERMS")))
+   ("TABINV" pro nil (lib "tabinv.pro" nil "Astrolib") "%s, XARR, X, IEFF" (nil ("FAST")))
+   ("tag_exist" fun nil (lib "tag_exist.pro" nil "Astrolib") "Result = %s(str, tag)" (nil ("index") ("quiet") ("recurse") ("top_level")))
+   ("tbdelcol" pro nil (lib "tbdelcol.pro" nil "Astrolib") "%s, h, tab, name" (nil))
+   ("tbdelrow" pro nil (lib "tbdelrow.pro" nil "Astrolib") "%s, h, tab, rows" (nil))
+   ("tbget" fun nil (lib "tbget.pro" nil "Astrolib") "Result = %s(hdr_or_tbstr, tab, field, rows, nulls)" (nil ("CONTINUE") ("NOSCALE")))
+   ("tbhelp" pro nil (lib "tbhelp.pro" nil "Astrolib") "%s, h" (nil ("TEXTOUT")))
+   ("tbinfo" pro nil (lib "tbinfo.pro" nil "Astrolib") "%s, h, tb_str" (nil ("errmsg") ("NOSCALE")))
+   ("tbprint" pro nil (lib "tbprint.pro" nil "Astrolib") "%s, hdr_or_tbstr, tab, columns, rows" (nil ("fmt") ("num_header_lines") ("nval_per_line") ("textout")))
+   ("tbsize" pro nil (lib "tbsize.pro" nil "Astrolib") "%s, h, tab, ncols, nrows, tfields, ncols_all, nrows_all" (nil))
+   ("tdb2tdt_calc" fun nil (lib "tdb2tdt.pro" nil "Astrolib") "Result = %s(jd)" (nil ("deriv") ("tbase")))
+   ("tdb2tdt" fun nil (lib "tdb2tdt.pro" nil "Astrolib") "Result = %s(jd)" (nil ("deriv") ("tbase")))
+   ("ten" fun nil (lib "ten.pro" nil "Astrolib") "Result = %s(dd, mm, ss)" (nil))
+   ("tenv" fun nil (lib "tenv.pro" nil "Astrolib") "Result = %s(dd, mm, ss)" (nil))
+   ("textclose" pro nil (lib "textclose.pro" nil "Astrolib") "%s" (nil ("textout")))
+   ("TEXTOPEN" pro nil (lib "textopen.pro" nil "Astrolib") "%s, PROGRAM" (nil ("MORE_SET") ("SILENT") ("STDOUT") ("TEXTOUT") ("WIDTH")))
+   ("tic_one" pro nil (lib "tic_one.pro" nil "Astrolib") "%s, min, pixx, incr, min2, tic1" (nil ("RA")))
+   ("ticlabels" pro nil (lib "ticlabels.pro" nil "Astrolib") "%s, minval, numtics, incr, ticlabs" (nil ("DELTA") ("FONT") ("RA")))
+   ("ticpos" pro nil (lib "ticpos.pro" nil "Astrolib") "%s, deglen, pixlen, ticsize, incr, units" (nil))
+   ("tics" pro nil (lib "tics.pro" nil "Astrolib") "%s, radec_min, radec_max, numx, ticsize, incr" (nil ("RA")))
+   ("TO_HEX" fun nil (lib "to_hex.pro" nil "Astrolib") "Result = %s(D, NCHAR)" (nil))
+   ("transform_coeff" fun nil (lib "transform_coeff.pro" nil "Astrolib") "Result = %s(coeff, alpha, beta)" (nil))
+   ("trapzd" pro nil (lib "trapzd.pro" nil "Astrolib") "%s, func, a, b, s, step" (nil ("_EXTRA")))
+   ("tsc" fun nil (lib "tsc.pro" nil "Astrolib") "Result = %s(value, posx, nx, posy, ny, posz, nz)" (nil ("AVERAGE") ("ISOLATED") ("NO_MESSAGE") ("WRAPAROUND")))
+   ("TSUM" fun nil (lib "tsum.pro" nil "Astrolib") "Result = %s(X, Y, IMIN, IMAX)" (nil ("NAN")))
+   ("tvbox" pro nil (lib "tvbox.pro" nil "Astrolib") "%s, width, x, y, color" (nil ("_EXTRA") ("ANGLE") ("Color") ("DATA") ("DEVICE") ("SQUARE")))
+   ("Tvcircle" pro nil (lib "tvcircle.pro" nil "Astrolib") "%s, radius, xc, yc, color" (nil ("_Extra") ("COLOR") ("DATA") ("Device") ("FILL")))
+   ("tvellipse" pro nil (lib "tvellipse.pro" nil "Astrolib") "%s, rmax, rmin, xc, yc, pos_ang, color" (nil ("_Extra") ("COLOR") ("DATA") ("DEVICE") ("FILL") ("MAJOR") ("MINOR") ("NPOINTS")))
+   ("TVLASER" pro nil (lib "tvlaser.pro" nil "Astrolib") "%s, hdr, Image" (nil ("BARPOS") ("BOTTOMDW") ("CARROWS") ("CLABELS") ("COLORPS") ("COMMENTS") ("CSIZE") ("CTITLE") ("DX") ("DY") ("ENCAP") ("FILENAME") ("HEADER") ("HELP") ("IMAGEOut") ("INTERP") ("MAGNIFY") ("NCOLORSDW") ("NO_PERS_INFO") ("NoClose") ("NODELETE") ("NOEIGHT") ("NOPRINT") ("NORETAIN") ("PORTRAIT") ("PRINTER") ("REVERSE") ("SCALE") ("TITLE") ("TrueColor") ("XDIM") ("XSTART") ("YDIM") ("YSTART")))
+   ("tvlist" pro nil (lib "tvlist.pro" nil "Astrolib") "%s, image, dx, dy" (nil ("OFFSET") ("TEXTOUT") ("ZOOM")))
+   ("unzoom_xy" pro nil (lib "unzoom_xy.pro" nil "Astrolib") "%s, xtv, ytv, xim, yim" (nil ("OFFSET") ("ZOOM")))
+   ("update_distort" pro nil (lib "update_distort.pro" nil "Astrolib") "%s, distort, xcoeff, ycoeff" (nil))
+   ("uvbybeta" pro nil (lib "uvbybeta.pro" nil "Astrolib") "%s, xby, xm1, xc1, xHbeta, xn, Te, MV, eby, delm0, radius" (nil ("eby_in") ("name") ("print") ("prompt") ("TEXTOUT")))
+   ("vactoair" pro nil (lib "vactoair.pro" nil "Astrolib") "%s, wave_vac, wave_air" (nil))
+   ("valid_num" fun nil (lib "valid_num.pro" nil "Astrolib") "Result = %s(string, value)" (nil ("INTEGER")))
+   ("VECT" fun nil (lib "vect.pro" nil "Astrolib") "Result = %s(vctr, form)" (nil ("delim") ("Format")))
+   ("VSYM" pro nil (lib "vsym.pro" nil "Astrolib") "%s, Nvert" (nil ("FILL") ("POLYGON") ("ROT") ("SKELETON") ("STAR") ("THICK")))
+   ("wcssph2xy_plot" pro nil (lib "wcs_demo.pro" nil "Astrolib") "%s, file_unit, map, param1, param2" (nil))
+   ("inversion_error" pro nil (lib "wcs_demo.pro" nil "Astrolib") "%s, file_unit, map, param1, param2" (nil))
+   ("wcs_rot" pro nil (lib "wcs_demo.pro" nil "Astrolib") "%s, file_unit, map, param1, param2" (nil))
+   ("wcs_demo" pro nil (lib "wcs_demo.pro" nil "Astrolib") "%s" (nil))
+   ("WCS_GETPOLE" pro nil (lib "wcs_getpole.pro" nil "Astrolib") "%s, crval, lonpole, theta0, alpha_p, delta_p" (nil ("AT_POLE") ("LATPOLE")))
+   ("wcs_rotate" pro nil (lib "wcs_rotate.pro" nil "Astrolib") "%s, longitude, latitude, phi, theta, crval" (nil ("LATPOLE") ("LONGPOLE") ("ORIGIN") ("PV1") ("REVERSE") ("THETA0")))
+   ("wcssph2xy" pro nil (lib "wcssph2xy.pro" nil "Astrolib") "%s, longitude, latitude, x, y, map_type" (nil ("badindex") ("crval") ("crxy") ("ctype") ("face") ("latpole") ("longpole") ("north_offset") ("pv1") ("pv2") ("south_offset")))
+   ("wcsxy2sph" pro nil (lib "wcsxy2sph.pro" nil "Astrolib") "%s, x, y, longitude, latitude, map_type" (nil ("crval") ("crxy") ("ctype") ("face") ("Latpole") ("longpole") ("pv1") ("pv2")))
+   ("MimeType" pro nil (lib "webget.pro" nil "Astrolib") "%s, Header, Class, Type, Length" (nil))
+   ("webget" fun nil (lib "webget.pro" nil "Astrolib") "Result = %s(url)" (nil ("COPYFILE") ("HTTP10") ("POST") ("SILENT") ("timeout")))
+   ("wfpc2_metric" pro nil (lib "wfpc2_metric.pro" nil "Astrolib") "%s, xin, yin, xout, yout, chip" (nil ("FILTER") ("GLOBAL") ("Header") ("RADec") ("YEAR")))
+   ("wfpc2_read" pro nil (lib "wfpc2_read.pro" nil "Astrolib") "%s, filename, chip1, header1, chip2, header2, chip3, header3, chip4, header4" (nil ("batwing") ("num_chip") ("path") ("trim")))
+   ("where_Tag" fun nil (lib "where_tag.pro" nil "Astrolib") "Result = %s(Struct, Nfound)" (nil ("ISELECT") ("NOPRINT") ("RANGE") ("TAG_NAME") ("TAG_NUMBER") ("VALUES")))
+   ("WHERENAN" fun nil (lib "wherenan.pro" nil "Astrolib") "Result = %s(ARRAY, COUNT)" (nil))
+   ("writefits" pro nil (lib "writefits.pro" nil "Astrolib") "%s, filename, data, header, heap" (nil ("Append") ("CheckSum") ("compress") ("NaNValue")))
+   ("XDISPSTR_EVENT" pro nil (lib "xdispstr.pro" nil "Astrolib") "%s, Event" (nil))
+   ("XDISPSTR_CLEANUP" pro nil (lib "xdispstr.pro" nil "Astrolib") "%s, Id" (nil))
+   ("XDISPSTR" pro nil (lib "xdispstr.pro" nil "Astrolib") "%s, Array" (nil ("BLOCK") ("FONT") ("GROUP_LEADER") ("HEIGHT") ("POS") ("TITLE") ("top_line") ("WIDTH")))
+   ("XMEDSKY" pro nil (lib "xmedsky.pro" nil "Astrolib") "%s, Image, Bkg" (nil ("CLIP") ("Nsig")))
+   ("xy2ad" pro nil (lib "xy2ad.pro" nil "Astrolib") "%s, x, y, astr, a, d" (nil))
+   ("xyad" pro nil (lib "xyad.pro" nil "Astrolib") "%s, hdr, x, y, a, d" (nil ("ALT") ("CELESTIAL") ("ECLIPTIC") ("GALACTIC") ("PRECISION") ("PRINT")))
+   ("xyxy" pro nil (lib "xyxy.pro" nil "Astrolib") "%s, hdra, hdrb, xa, ya, xb, yb" (nil))
+   ("xyz" pro nil (lib "xyz.pro" nil "Astrolib") "%s, date, x, y, z, xvel, yvel, zvel" (nil ("equinox")))
+   ("YDN2MD" pro nil (lib "ydn2md.pro" nil "Astrolib") "%s, YR, DY, M, D" (nil ("help")))
+   ("zang" fun nil (lib "zang.pro" nil "Astrolib") "Result = %s(dl, z)" (nil ("h0") ("k") ("Lambda0") ("Omega_m") ("q0") ("SILENT")))
+   ("ZBRENT" fun nil (lib "zbrent.pro" nil "Astrolib") "Result = %s(x1, x2)" (nil ("_EXTRA") ("FUNC_NAME") ("MAX_ITERATIONS") ("TOLERANCE")))
+   ("ZENPOS" pro nil (lib "zenpos.pro" nil "Astrolib") "%s, date, ra, dec" (nil))
+   ("zoom_xy" pro nil (lib "zoom_xy.pro" nil "Astrolib") "%s, xim, yim, xtv, ytv" (nil ("OFFSET") ("ZOOM")))
+   ("zparcheck" pro nil (lib "zparcheck.pro" nil "Astrolib") "%s, progname, parameter, parnum, types, dimens, message" (nil))
+   ("al_legendtest" pro nil (lib "al_legendtest.pro" nil "Astrolib") "%s" (nil))
+   ("wcs_check_ctype" pro nil (lib "wcs_check_ctype.pro" nil "Astrolib") "%s, ctype, projection_type, coord_type" (nil))
+   ("query_irsa_cat" fun nil (lib "query_irsa_cat.pro" nil "Astrolib") "Result = %s(targetname_OR_coords)" (nil ("catalog") ("change_null") ("DEBUG") ("outfile") ("radius") ("radunits")))
+   ("read_ipac_table" fun nil (lib "read_ipac_table.pro" nil "Astrolib") "Result = %s(filename)" (nil ("change_null") ("debug")))
+   ("read_ipac_var" fun nil (lib "read_ipac_var.pro" nil "Astrolib") "Result = %s(textvar)" (nil ("change_null") ("debug")))
+   ("write_ipac_table" pro nil (lib "write_ipac_table.pro" nil "Astrolib") "%s, in_struct, outfile" (nil ("exact_format") ("format") ("short_format")))
+   ("errtype" fun nil (lib "safe_correlate.pro" nil "Astrolib") "Result = %s(err, bad_err_msg)" (nil))
+   ("vet_err" pro nil (lib "safe_correlate.pro" nil "Astrolib") "%s, err, errtype, n, bad_err_msg" (nil))
+   ("generate_data" fun nil (lib "safe_correlate.pro" nil "Astrolib") "Result = %s(v, err, type, n, nsim, dbl, seed)" (nil))
+   ("safe_correlate" fun nil (lib "safe_correlate.pro" nil "Astrolib") "Result = %s(x, y, xerr, yerr)" (nil ("nsim") ("seed")))))
diff --git a/Code/script_idl_mv/astrolib/ad2xy.pro b/Code/script_idl_mv/astrolib/ad2xy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ef148b0c390e238c3c4843d4c09bce4c957a32bb
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ad2xy.pro
@@ -0,0 +1,326 @@
+pro ad2xy, a, d, astr, x, y
+;+
+; NAME:
+;     AD2XY
+; PURPOSE:
+;     Compute X and Y from native coordinates and a FITS  astrometry structure
+; EXPLANATION:
+;     If a WCS projection (Calabretta & Greisen 2002, A&A, 395, 1077) is 
+;     present, then the procedure WCSXY2SPH is used to compute native 
+;     coordinates.   If distortion is present then this is corrected.  
+;     In all cases, the inverse of the CD matrix is applied and offset 
+;     from the reference pixel to obtain X and Y. 
+;
+;     AD2XY is generally meant to be used internal to other procedures.   For 
+;     interactive purposes, use ADXY.
+;
+; CALLING SEQUENCE:
+;     AD2XY, a ,d, astr, x, y   
+;
+; INPUTS:
+;     A -     R.A. or longitude in DEGREES, scalar or vector.    
+;     D -     Dec. or longitude in DEGREES, scalar or vector
+;             If the input A and D are arrays with 2 or more dimensions,
+;             they will be converted to a 1-D vectors.
+;     ASTR - astrometry structure, output from EXTAST procedure containing:
+;        .CD   -  2 x 2 array containing the astrometry parameters CD1_1 CD1_2
+;               in DEGREES/PIXEL                                   CD2_1 CD2_2
+;        .CDELT - 2 element vector giving increment at reference point in
+;               DEGREES/PIXEL
+;        .CRPIX - 2 element vector giving X and Y coordinates of reference pixel
+;               (def = NAXIS/2) in FITS convention (first pixel is 1,1)
+;        .CRVAL - 2 element vector giving coordinates of the reference pixel 
+;               in DEGREES
+;        .CTYPE - 2 element vector giving projection types 
+;        .LONGPOLE - scalar longitude of north pole (default = 180) 
+;        .PV2 - Vector of additional parameter (e.g. PV2_1, PV2_2) needed in 
+;               some projections
+;
+;     Fields added for version 2:
+;      .PV1 - Vector of projection parameters associated with longitude axis
+;      .AXES  - 2 element integer vector giving the FITS-convention axis 
+;               numbers associated with astrometry, in ascending order. 
+;               Default [1,2].
+;      .REVERSE - byte, true if first astrometry axis is Dec/latitude
+;      .COORDSYS - 1 or 2 character code giving coordinate system, including
+;                 'C' = RA/Dec, 'G' = Galactic, 'E' = Ecliptic, 'X' = unknown.
+;      .RADECSYS - String giving RA/Dec system e.g. 'FK4', 'ICRS' etc.
+;      .EQUINOX  - Double giving the epoch of the mean equator and equinox
+;      .DATEOBS  - Text string giving (start) date/time of observations
+;      .MJDOBS   - Modified julian date of start of observations.
+;      .X0Y0     - Implied offset in intermediate world coordinates if user has
+;                  specified a non-standard fiducial point via PV1 and also
+;                  has set PV1_0a =/ 0 to indicate that the offset should be
+;                  applied in order to place CRVAL at the IWC origin.
+;                  Should be *added* to the IWC derived from application of
+;                  CRPIX, CDELT, CD to the pixel coordinates.
+;
+;        .DISTORT - Optional substructure specifying distortion parameters
+;
+; OUTPUTS:
+;     X     - row position in pixels, scalar or vector
+;     Y     - column position in pixels, scalar or vector
+;
+;     X,Y will be in the standard IDL convention (first pixel is 0), and
+;     *not* the FITS convention (first pixel is 1)
+; NOTES:
+;      AD2XY tests for presence of WCS coordinates by the presence of a dash 
+;      in the 5th character position in the value of CTYPE (e.g 'DEC--SIN').
+; COMMON BLOCKS:
+;      BROYDEN_COMMON - Used when solving for a reverse distortion tranformation
+;        (either SIP or TGV) by iterating on the forward transformation.
+; PROCEDURES USED:
+;       CGErrorMsg (from Coyote Library)
+;       TAG_EXIST(), WCSSPH2XY
+; REVISION HISTORY:
+;     Converted to IDL by B. Boothman, SASC Tech, 4/21/86
+;     Use astrometry structure,  W. Landsman      Jan. 1994   
+;     Do computation correctly in degrees  W. Landsman       Dec. 1994
+;     Only pass 2 CRVAL values to WCSSPH2XY   W. Landsman      June 1995
+;     Don't subscript CTYPE      W. Landsman       August 1995        
+;     Understand reversed X,Y (X-Dec, Y-RA) axes,   W. Landsman  October 1998
+;     Consistent conversion between CROTA and CD matrix W. Landsman October 2000
+;     No special case for tangent projection W. Landsman June 2003
+;     Work for non-WCS coordinate transformations W. Landsman Oct 2004
+;     Use CRVAL reference point for non-WCS transformation  W.L. March 2007
+;     Use post V6.0 notation  W.L. July 2009
+;     Allows use of Version 2 astrometry structure & optimised for
+;     large input arrays. Wrap test for cylindrical coords. J. P. Leahy July 2013
+;     Wrap test failed for 2d input arrays 
+;                              T. Ellsworth-Bowers/W.Landsman July 2013
+;     Tweaked to restore shape of arrays on exit JPL Aug 2013.
+;     ..and make them scalars if input is scalar JPL Aug 2013
+;     Iterate when forward SIP coefficients are supplied but not the reverse
+;     coefficients.    Don't compute poles if not a cylindrical system
+;              W. Landsman           Dec 2013
+;     Evaluate TPV distortion (SCAMP) if present  W. Landsman  Jan 2014
+;     Support IRAF TNX projection M. Sullivan U. of Southhamptom  Mar 2014
+;     No longer check that CDELT[0] differs from 1 W. Landsman Apr 2015
+;     
+;-
+
+ compile_opt idl2
+ common broyden_coeff, xcoeff, ycoeff
+ 
+
+ if N_params() lT 4 then begin
+        print,'Syntax -- AD2XY, a, d, astr, x, y'
+        return
+ endif
+
+ Catch, theError
+ IF theError NE 0 then begin
+     Catch,/Cancel
+     void = cgErrorMsg(/quiet)
+     RETURN
+     ENDIF
+
+  if tag_exist(astr,'DISTORT') && ((astr.distort.name EQ 'TPV') || (astr.distort.name EQ 'TNX')) then $
+  ctype = strmid(astr.ctype,0,4) + '-TAN' else ctype = astr.ctype 
+ crval = astr.crval
+
+ testing = 0B
+ size_a = SIZE(a)
+ ndima = size_a[0]
+
+ astr2 = TAG_EXIST(astr,'AXES') ; version 2 astrometry structure
+ IF astr2 THEN reverse = astr.reverse ELSE BEGIN
+     coord = strmid(ctype,0,4)
+     reverse = ((coord[0] EQ 'DEC-') && (coord[1] EQ 'RA--')) || $
+               ((coord[0] EQ 'GLAT') && (coord[1] EQ 'GLON')) || $
+               ((coord[0] EQ 'ELAT') && (coord[1] EQ 'ELON'))
+ ENDELSE
+ if reverse then crval = rotate(crval,2)        ;Invert CRVAL?
+
+ if (ctype[0] EQ '') then begin   
+      ctype = ['RA---TAN','DEC--TAN']
+      message,'No CTYPE specified - assuming TANgent projection',/INF
+ endif      
+     
+  spherical = strmid(astr.ctype[0],4,1) EQ '-'
+  if spherical then begin
+      IF astr2 THEN BEGIN
+          cylin = WHERE(astr.projection EQ ['CYP','CAR','MER','CEA','HPX'],Ncyl)
+          IF Ncyl GT 0 THEN BEGIN	
+	  testing = 1
+          size_d = SIZE(d)
+          ndimd = size_d[0]
+          IF ndima GT 1 THEN a = REFORM(a, size_a[ndima+2], /OVERWRITE)
+          IF ndimd GT 1 THEN d = REFORM(d, size_d[ndimd+2], /OVERWRITE)
+          a0 = [a, 0d0,180d0]  & d0 = [d, 0d0, 0d0] ; test points
+          wcssph2xy, a0, d0, xsi, eta, CTYPE = ctype, PV1 = astr.pv1, $
+              PV2 = astr.pv2, CRVAL = crval, CRXY = astr.x0y0 
+	  ENDIF ELSE BEGIN
+	  pv1 = astr.pv1
+	  pv2 = astr.pv2
+          if tag_exist(astr,'DISTORT') then $
+	      if astr.distort.name EQ 'TPV' then begin 
+	           pv1 = [0.0d,0,90.0d,180d,90d]    ;Tangent projection
+	           pv2 = [0.0,0.0]
+	      ENDIF   
+          wcssph2xy, a, d, xsi, eta, CTYPE = ctype, PV1 = pv1, $
+              PV2 = pv2, CRVAL = crval, CRXY = astr.x0y0 
+	   ENDELSE
+      ENDIF ELSE wcssph2xy, a, d, xsi, eta, CTYPE = ctype, PV2 = astr.pv2, $
+        LONGPOLE = astr.longpole, CRVAL = crval, LATPOLE = astr.latpole
+  endif else begin
+        xsi = a - crval[0] & eta = d - crval[1]
+  endelse	
+  cd = astr.cd
+  cdelt = astr.cdelt
+
+  cd[0,0] *= cdelt[0] & cd[0,1] *= cdelt[0]
+  cd[1,1] *= cdelt[1] & cd[1,0] *= cdelt[1]
+     
+ if reverse then begin
+     temp = TEMPORARY(xsi) &  xsi = TEMPORARY(eta) & eta = TEMPORARY(temp)
+ endif
+
+   if tag_exist(astr,'DISTORT') && (astr.distort.name EQ 'TPV') then begin
+            ctype = strmid(astr.ctype,0,4) + '-TAN'
+            xcoeff = astr.pv1
+	    ycoeff = astr.pv2
+	    x0 = xcoeff[0]
+	     y0 = ycoeff[0]  
+	    for i=0, N_elements(xsi)-1 do begin	
+	      	xcoeff[0] = x0 - xsi[i]
+	     	ycoeff[0] = y0 - eta[i]    
+	      	res = broyden([xsi[i],eta[i]], 'TPV_EVAL' )	     
+	      	xsi[i] = res[0]
+	      	eta[i] = res[1]
+	      endfor
+       ENDIF
+   if tag_exist(astr,'DISTORT') && (astr.distort.name EQ 'TNX') then begin
+            ctype = strmid(astr.ctype,0,4) + '-TAN'
+            xcoeff = astr.distort.lngcor
+	    ycoeff = astr.distort.latcor
+            x0 = xcoeff.coeff[0]
+            y0 = ycoeff.coeff[0]  
+	    for i=0, N_elements(xsi)-1 do begin	
+	      	xcoeff.coeff[0] = x0 - xsi[i]
+	     	ycoeff.coeff[0] = y0 - eta[i]    
+	      	res = broyden([xsi[i],eta[i]], 'TNX_EVAL' )	     
+	      	xsi[i] = res[0]
+	      	eta[i] = res[1]
+	      endfor
+       ENDIF
+
+ crpix = astr.crpix - 1
+ 
+ cdinv = invert(cd)
+ x = ( cdinv[0,0]*xsi + cdinv[0,1]*eta  )
+ y = ( cdinv[1,0]*TEMPORARY(xsi) + cdinv[1,1]*TEMPORARY(eta)  )
+
+ if tag_exist(astr,'DISTORT') && ( astr.distort.name EQ 'SIP') then begin
+           distort  = astr.distort
+           ap = distort.ap
+           bp = distort.bp
+           na = ((size(ap,/dimen))[0])
+; If reverse SIP coefficients are not supplied we iterate on the forward 
+; coefficients (using BROYDEN).	   
+           if na LE 1 then begin     
+	      xcoeff = distort.a
+	      ycoeff = distort.b	
+	      x0 = xcoeff[0]
+	      y0 = ycoeff[0]  
+	      for i=0, N_elements(x)-1 do begin	
+	      	xcoeff[0] = x0 - x[i]
+	     	ycoeff[0] = y0 - y[i]    
+	      	res = broyden([x[i],y[i]], 'SIP_EVAL' )	     
+	      	x[i] = res[0]
+	      	y[i] = res[1]
+	      endfor
+	   endif else begin   
+           xdif1 = x
+           ydif1 = y	   
+           for i=0,na-1 do begin
+               for j=0,na-1 do begin
+                  if ap[i,j] NE 0.0 then xdif1 += x^i*y^j*ap[i,j]            
+                  if bp[i,j] NE 0.0 then ydif1 += x^i*y^j*bp[i,j]
+           endfor
+           endfor
+
+           x = xdif1
+           y = ydif1
+         ENDELSE
+ ENDIF
+
+ x += crpix[0] 
+ y += crpix[1] 
+
+; Check for wrapping in cylindrical projections: since the same phi
+; appears at regular intervals in (x,y), depending on the location of
+; the reference point on the pixel grid, some of the returned pixel 
+; values may be offset by 360 degrees from the ones we want.
+;
+; The pixel grid may be rotated relative to intermediate world coords, 
+; so the offset may have both x and y components in pixel space. 
+;
+; Doesn't try if native and astronomical poles are misaligned
+; as this fix doesn't work in that case.
+
+ IF testing THEN BEGIN
+     npt = N_ELEMENTS(a)
+     x0 = x[npt:npt+1] & y0 = y[npt:npt+1]
+     x  = x[0:npt-1]   & y  = y[0:npt-1]
+     
+         crval = astr.crval
+         IF astr.reverse THEN crval = REVERSE(crval)
+         WCS_GETPOLE, crval, astr.pv1[3]-astr.pv1[1], astr.pv1[2], $
+                      alpha_p, delta_p, $
+                      LATPOLE = astr.pv1[4], AT_POLE = at_pole
+         IF at_pole THEN BEGIN
+             naxis = astr.naxis
+             offmap = WHERE(x LT 0 OR y LT 0 OR $
+                            x GT naxis[0] OR y GT naxis[1], noff)          
+             IF offmap[0] NE -1 THEN BEGIN
+                                 ; 360 degree shift
+                 x360 = 2d0*(x0[1] - x0[0])
+                 y360 = 2d0*(y0[1] - y0[0])
+                 IF x360 LT 0 THEN BEGIN
+                     x360 *= -1d0
+                     y360 *= -1d0
+                 ENDIF
+                 xshift = x360 NE 0d0
+                 yshift = y360 NE 0d0
+                             ; Figure out which direction shift is
+                 IF xshift THEN BEGIN
+                     IF (MIN(x[offmap],/NAN) LT 0) THEN BEGIN
+                         x[offmap] += x360
+                         IF yshift THEN y[offmap] += y360
+                     ENDIF ELSE IF MAX(x[offmap],/NAN) GT naxis[0] THEN BEGIN
+                         x[offmap] -= x360
+                         IF yshift THEN y[offmap] -= y360
+                     ENDIF
+                 ENDIF ELSE BEGIN
+                     IF y360 LT 0 THEN BEGIN
+                         x360 *= -1d0
+                         y360 *= -1d0
+                     ENDIF
+                     IF (MIN(y[offmap],/NAN) LT 0) THEN BEGIN
+                         IF xshift THEN x[offmap] += x360
+                         y[offmap] += y360
+                     ENDIF ELSE BEGIN
+                         IF xshift THEN x[offmap] -= x360
+                         y[offmap] -= y360
+                     ENDELSE
+                 ENDELSE
+             ENDIF
+         ENDIF
+     ENDIF
+ 
+
+ IF ndima GT 1 THEN BEGIN
+    a = REFORM(a, size_a[1:ndima], /OVERWRITE)
+    d = REFORM(d, size_a[1:ndima], /OVERWRITE)
+    x = REFORM(x, size_a[1:ndima], /OVERWRITE)
+    y = REFORM(y, size_a[1:ndima], /OVERWRITE)
+ ENDIF ELSE if ndima EQ 0 THEN BEGIN
+    a = a[0]
+    d = d[0]
+    x = x[0]
+    y = y[0]
+ ENDIF
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/add_distort.pro b/Code/script_idl_mv/astrolib/add_distort.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ca871fddb88e5a93bb3eecf4e80611d7fae716bc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/add_distort.pro
@@ -0,0 +1,161 @@
+ pro add_distort, hdr, astr
+; NAME:
+;    ADD_DISTORT
+; PURPOSE:
+;    Add the distortion parameters in an astrometry structure to a FITS header.
+; EXPLANATION:
+;    Called by PUTAST to add SIP (http://fits.gsfc.nasa.gov/registry/sip.html )
+;    or TNX ( http://fits.gsfc.nasa.gov/registry/tnx.html ) distortion 
+;    parameters in an astrometry structure to a FITS header
+;     
+;    Prior to April 2012, PUTAST did not add distortion parameters so one
+;    had to call ADD_DISTORT after PUTAST. 
+;
+;    IDL> putast,h ,astr0
+;    IDL> add_distort,h,astr0
+;
+; CALLING SEQUENCE:
+;     add_distort, hdr, astr    
+;
+; INPUTS:
+;     HDR -  FITS header, string array.   HDR will be updated to contain
+;             the supplied astrometry.
+;     ASTR - IDL structure containing values of the astrometry parameters
+;            CDELT, CRPIX, CRVAL, CTYPE, LONGPOLE, PV2, and DISTORT
+;            See EXTAST.PRO for more info about the structure definition
+;
+; PROCEDURES USED:
+;       SXADDPAR, TAG_EXIST()
+; REVISION HISTORY:
+;       Written by W. Landsman  May 2005
+;       Enforce i+j = n for ij coefficients of order n  W. Landsman April 2012
+;       Support IRAF TNX distortion M. Sullivan  March 2014
+;;-
+ npar = N_params()
+
+ if ( npar LT 2 ) then begin    ;Was header supplied?
+        print,'Syntax: ADD_DISTORT, Hdr, astr'
+        return
+ endif
+
+ add_distort = tag_exist(astr,'distort')
+ IF(~ add_distort)THEN RETURN
+ 
+ IF(astr.distort.name EQ 'SIP') then begin 
+    
+    sxaddpar,hdr,'CTYPE1','RA---TAN-SIP' 
+    sxaddpar,hdr,'CTYPE2','DEC--TAN-SIP' 
+    distort = astr.distort
+    a_dimen = size(distort.a,/dimen) 
+    b_dimen = size(distort.b,/dimen)
+    ap_dimen = size(distort.ap,/dimen) 
+    bp_dimen = size(distort.bp,/dimen)
+    
+    if a_dimen[0] GT 0 then begin
+       a_order = a_dimen[0]-1 
+       sxaddpar, hdr, 'A_ORDER', a_order, /savec, $
+                 'polynomial order, axis 1, detector to sky '
+       for i=0, a_order do begin
+          for j = 0, a_order-i do begin
+             aij = distort.a[i,j]
+             if aij NE 0.0 then $
+               sxaddpar, hdr, 'A_' + strtrim(i,2)+ '_' + strtrim(j,2), aij, $
+                         ' distortion coefficient', /savec
+          endfor
+       endfor
+    endif
+    
+    if b_dimen[0] GT 0 then begin
+       b_order = b_dimen[0]-1 
+       sxaddpar, hdr, 'B_ORDER', a_order, /savec , $
+                 'polynomial order, axis 2, detector to sky'
+       for i=0, b_order do begin
+          for j = 0, b_order-i do begin
+             bij = distort.b[i,j]
+             if bij NE 0.0 then $
+               sxaddpar, hdr, 'B_' + strtrim(i,2)+ '_' + strtrim(j,2), bij, $
+                         ' distortion coefficient', /savec
+          endfor
+       endfor
+    endif
+    
+    if ap_dimen[0] GT 0 then begin
+       ap_order = ap_dimen[0]-1 
+       sxaddpar, hdr, 'AP_ORDER', a_order, /savec, $
+                 ' polynomial order, axis 1, sky to detector '
+       for i=0, ap_order do begin
+          for j = 0, ap_order-i do begin
+             apij = distort.ap[i,j]
+             if apij NE 0.0 then $
+               sxaddpar, hdr, 'AP_' + strtrim(i,2)+ '_' + strtrim(j,2), apij, $
+                         ' distortion coefficient', /savec
+          endfor
+       endfor
+    endif
+    
+    
+    if bp_dimen[0] GT 0 then begin
+       bp_order = bp_dimen[0]-1 
+       sxaddpar, hdr, 'BP_ORDER', a_order, /savec, $
+                 ' polynomial order, axis 2, sky to detector '
+       for i=0, bp_order do begin
+          for j = 0, bp_order-i do begin
+             bpij = distort.bp[i,j]
+             if bpij NE 0.0 then $
+               sxaddpar, hdr, 'BP_' + strtrim(i,2)+ '_' + strtrim(j,2), bpij, $
+                         ' distortion coefficient', /savec
+          endfor
+       endfor
+    endif
+    
+ ENDIF ELSE IF(astr.distort.name EQ 'TNX')THEN BEGIN
+
+    sxaddpar, hdr,'WAT0_001','system=image'
+
+    string1='wtype=tnx axtype=ra lngcor = "3.'
+    string1+= ' '+STRN(astr.distort.lngcor.xiorder,FORMAT='(F2.0)')
+    string1+= ' '+STRN(astr.distort.lngcor.etaorder,FORMAT='(F2.0)')
+    string1+= ' '+STRN(astr.distort.lngcor.xterms,FORMAT='(F2.0)')
+    string1+= ' '+STRN(astr.distort.lngcor.ximin,FORMAT='(F19.16)')
+    string1+= ' '+STRN(astr.distort.lngcor.ximax,FORMAT='(F19.16)')
+    string1+= ' '+STRN(astr.distort.lngcor.etamin,FORMAT='(F19.16)')
+    string1+= ' '+STRN(astr.distort.lngcor.etamax,FORMAT='(F19.16)')
+    FOR i=0,N_ELEMENTS(astr.distort.lngcor.coeff)-1 DO BEGIN
+       string1+=' '+STRN(astr.distort.lngcor.coeff[i],FORMAT='(F19.16)')
+    ENDFOR
+    string1+= '"'
+
+    string2='wtype=tnx axtype=dec latcor = "3. '
+    string2+= ' '+STRN(astr.distort.latcor.xiorder,FORMAT='(F2.0)')
+    string2+= ' '+STRN(astr.distort.latcor.etaorder,FORMAT='(F2.0)')
+    string2+= ' '+STRN(astr.distort.latcor.xterms,FORMAT='(F2.0)')
+    string2+= ' '+STRN(astr.distort.latcor.ximin,FORMAT='(F19.16)')
+    string2+= ' '+STRN(astr.distort.latcor.ximax,FORMAT='(F19.16)')
+    string2+= ' '+STRN(astr.distort.latcor.etamin,FORMAT='(F19.16)')
+    string2+= ' '+STRN(astr.distort.latcor.etamax,FORMAT='(F19.16)')
+    FOR i=0,N_ELEMENTS(astr.distort.latcor.coeff)-1 DO BEGIN
+       string2+= ' '+STRN(astr.distort.latcor.coeff[i],FORMAT='(F19.16)')
+    ENDFOR
+    string2+= '"'
+
+    len1=STRLEN(string1)
+    n1=len1/70
+    IF(len1 MOD 68 GT 0)THEN n1++
+    FOR i=0,n1-1 DO BEGIN
+       s=STRMID(string1,i*68,68)
+;       PRINT,'WAT1_'+STRN(i+1,FORMAT='(I3.3)'),' ',s
+       sxaddpar, hdr,'WAT1_'+STRN(i+1,FORMAT='(I3.3)'),s
+    ENDFOR
+    len2=STRLEN(string2)
+    n2=len2/70
+    IF(len2 MOD 68 GT 0)THEN n2++
+    FOR i=0,n2-1 DO BEGIN
+       s=STRMID(string2,i*68,68)
+;       PRINT,'WAT1_'+STRN(i+1,FORMAT='(I3.3)'),' ',s
+       sxaddpar, hdr,'WAT2_'+STRN(i+1,FORMAT='(I3.3)'),s
+    ENDFOR
+
+ ENDIF
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/adstring.pro b/Code/script_idl_mv/astrolib/adstring.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3e0ba1332ea861078cea8b845d7714053ef302db
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/adstring.pro
@@ -0,0 +1,208 @@
+Function adstring,ra_dec,dec,precision, TRUNCATE = truncate,PRECISION=prec
+;+
+; NAME:
+;       ADSTRING
+; PURPOSE:
+;       Return RA and Dec as character string(s) in sexagesimal format.
+; EXPLANATION:
+;       RA and Dec may be entered as either a 2 element vector or as
+;       two separate vectors (or scalars).  One can also specify the precision 
+;       of the declination in digits after the decimal point.
+;
+; CALLING SEQUENCE
+;       result = ADSTRING( ra_dec, precision, /TRUNCATE )           
+;               or
+;       result = ADSTRING( ra,dec,[ precision, /TRUNCATE ] )
+;               or
+;       result = ADSTRING( dec, [ PRECISION= ]   
+;
+; INPUTS:
+;       RA_DEC - 2 element vector giving the Right Ascension and declination
+;               in decimal degrees.
+;                     or
+;       RA     - Right ascension in decimal degrees, numeric scalar or vector
+;       DEC    - Declination in decimal degrees, numeric scalar or vector
+;
+;     If only one parameter is supplied then it must be either a scalar (which
+;     is converted to sexagesimal) or a two element [RA, Dec] vector.
+; OPTIONAL INPUT:
+;       PRECISION  - Integer scalar (0-4) giving the number of digits after the 
+;               decimal of DEClination.   The RA is automatically 1 digit more.
+;               This parameter may either be the third parameter after RA,DEC 
+;               or the second parameter after [RA,DEC].  If only DEC is supplied 
+;               then precision must be supplied as a keyword parameter.   If no
+;               PRECISION parameter or keyword is passed, a  precision of 1 for
+;               both RA and DEC is returned to maintain  compatibility with past
+;               ADSTRING versions.    Values of  precision larger than 4 will 
+;               be truncated to 4.    If PRECISION is 3 or 4, then RA and Dec 
+;               should be input as double precision.
+; OPTIONAL INPUT KEYWORD:
+;       /TRUNCATE - if set, then the last displayed digit in the output is 
+;               truncated in precision rather than rounded.   This option is
+;               useful if ADSTRING() is used to form an official IAU name 
+;               (see http://vizier.u-strasbg.fr/Dic/iau-spec.htx) with 
+;               coordinate specification.   The IAU name will typically be
+;               be created by applying STRCOMPRESS/REMOVE) after the ADSTRING()
+;               call, e.g. 
+;              strcompress( adstring(ra,dec,0,/truncate), /remove)   ;IAU format
+;        PRECISION = Alternate method of supplying the precision parameter, 
+; OUTPUT:
+;       RESULT - Character string(s) containing HR,MIN,SEC,DEC,MIN,SEC formatted
+;               as ( 2I3,F5.(p+1),2I3,F4.p ) where p is the PRECISION 
+;               parameter.    If only a single scalar is supplied it is 
+;               converted to a sexagesimal string (2I3,F5.1).
+;
+; EXAMPLE:
+;       (1) Display CRVAL coordinates in a FITS header, H
+;
+;       IDL> crval = sxpar(h,'CRVAL*')  ;Extract 2 element CRVAL vector (degs)
+;       IDL> print, adstring(crval)     ;Print CRVAL vector sexagesimal format
+;
+;       (2)  print,adstring(30.42,-1.23,1)  ==>  ' 02 01 40.80  -01 13 48.0'
+;            print,adstring(30.42,+0.23)    ==>  ' 02 01 40.8   +00 13 48.0'    
+;            print,adstring(+0.23)          ==>  '+00 13 48.0'
+;
+;       (3) The first two calls in (2) can be combined in a single call using
+;           vector input
+;              print,adstring([30.42,30.42],[-1.23,0.23], 1)
+; PROCEDURES CALLED:
+;       RADEC, SIXTY()
+;
+; REVISION HISTORY:
+;       Written   W. Landsman                      June 1988
+;       Addition of variable precision and DEC seconds precision fix. 
+;       ver.  Aug. 1990 [E. Deutsch]
+;       Output formatting spiffed up       October 1991 [W. Landsman]
+;       Remove ZPARCHECK call, accept 1 element vector  April 1992 [W. Landsman]
+;       Call ROUND() instead of NINT()    February 1996  [W. Landsman]
+;       Check roundoff past 60s           October 1997   [W. Landsman]
+;       Work for Precision =4             November 1997  [W. Landsman]
+;       Major rewrite to allow vector inputs   W. Landsman  February 2000
+;       Fix possible error in seconds display when Precision=0 
+;                               P. Broos/W. Landsman April 2002
+;       Added /TRUNCATE keyword, put leading zeros in seconds display
+;                               P. Broos/W. Landsman September 2002
+;       Fix declination zero values under vector processing W.Landsman Feb 2004
+;       Fix possible problem in leading zero display W. Landsman June 2004
+;       Assume since V5.4, omit fstring() call  W. Landsman April 2006
+;       Fix significant bug when round a declination with -1<dec<0 
+;          Add PRECISION keyword    W.L. Aug 2008
+;       Use formatting for "+" and "0"  W. L.    May 2009
+;       Allow formatting of longitudes >99.99  W. L.  Sep 2012
+;-
+  On_error,2
+  compile_opt idl2
+
+  Npar = N_params()
+ 
+
+  case N_elements(ra_dec) of 
+
+     1: if ( Npar EQ 1 ) then dec = ra_dec else ra = ra_dec
+     2: begin
+        if (N_elements(dec) LT 2) then begin 
+              ra = ra_dec[0] mod 360.
+              if N_elements(dec) EQ 1 then begin 
+              precision = dec & Npar=3 & endif
+              dec = ra_dec[1]
+        endif else ra = ra_dec
+        end
+   else: begin
+        If (Npar Eq 1) then message, $
+	'ERROR - first parameter must be either a scalar or 2 element vector'
+        ra = ra_dec 
+        end
+   endcase
+
+  if N_elements(prec) EQ 1 then precision = prec
+  
+  if ( Npar GE 2 ) then $
+        if N_elements(dec) NE N_elements(ra) then message, $
+      'ERROR - RA and Declination do not have equal number of elements'
+
+  if N_elements(ra) EQ N_elements(dec) then begin
+
+    badrange = where( (dec LT -90.) or (dec GT 90.), Nbad)
+    if Nbad GT 0 then message, /INF, $
+      'WARNING - Some declination values are out of valid range (-90 < dec <90)'
+     radec, ra, dec, ihr, imin, xsec, ideg, imn, xsc
+     if N_elements(precision) EQ 0 then precision = 0
+     precision = precision > 0 < 4         ;No more than 4 decimal places
+ if ~keyword_set(truncate) then begin
+     roundsec = [59.5,59.95,59.995,59.9995,59.99995,59.999995]
+     carry = where(xsec GT roundsec[precision+1], Ncarry)
+     if Ncarry GT 0 then begin
+        imin[carry] = imin[carry] + 1
+        xsec[carry] = 0.0
+        mcarry = where(imin[carry] EQ 60, Nmcarry)
+        if Nmcarry GT 0 then begin
+                ic = carry[mcarry]
+                ihr[ic] = (ihr[ic] + 1) mod 24
+                imin[ic] = 0
+        endif
+     endif
+  endif else xsec = (long(xsec*10L^(precision+1)))/10.0d^(precision+1)
+
+     secfmt = '(F0' + string( 3+precision+1,'(I1)' ) + '.' + $
+                     string(   precision+1,'(I1)' ) + ')'
+     result = string(ihr,'(I3.2)') + string(imin,'(I3.2)') + ' ' +$
+              strtrim(string(xsec,secfmt),2) + '  ' 
+    if N_elements(precision) EQ 0 then precision = 1
+
+  endif else begin
+
+     x = sixty(dec)
+     if N_elements(precision) EQ 0 then precision = 1
+     ideg = fix(x[0]) & imn = fix(x[1]) & xsc = x[2]
+     result = ''
+
+  endelse
+
+   imn = abs(imn)  & xsc = abs(xsc)
+   if ( precision EQ 0 ) then begin 
+           secfmt = '(I03.2)' 
+           if ~keyword_set(truncate) then begin
+           xsc = round(xsc)
+           carry = where(xsc EQ 60, Ncarry)
+           if Ncarry GT 0 then begin                 ;Updated April 2002
+                  xsc[carry] = 0
+                  imn[carry] = imn[carry] + 1
+           endif
+           endif
+   endif else begin
+
+         secfmt = '(F0' + string( 3+precision,'(I1)') + '.' + $
+                         string(   precision,'(I1)') + ')'
+			 
+         if ~keyword_set(truncate) then begin
+         ixsc = fix(xsc + 0.5/10^precision)
+         carry = where(ixsc GE 60, Ncarry)
+         if Ncarry GT 0 then begin
+             xsc[carry] = 0.
+             imn[carry] = imn[carry] + 1
+         endif
+         endif else $
+              xsc = (long(xsc*10^precision))/10.0d^precision
+  endelse
+
+   pos = dec GE 0 
+   carry = where(imn EQ 60, Ncarry)
+   if Ncarry GT 0  then begin
+       ideg[carry] = ideg[carry] -1 + 2*pos[carry]
+        imn[carry] = 0
+   endif
+ 
+   deg = string(ideg,'(I+3.2)')
+   big = where(abs(ideg) ge 100, Nbig)
+   if Nbig GT 0 then deg[big] = string(ideg[big],'(I+4.3)')
+   zero = where(ideg EQ 0, Nzero)
+   if Nzero GT 0 then begin
+       negzero = where( dec[zero] LT 0, Nneg)
+       if Nneg GT 0 then deg[zero[negzero]] = '-00' 
+    endif
+
+    
+   return, result + deg + string(imn,'(I3.2)') + ' ' +  $
+            strtrim(string(xsc,secfmt),2)
+
+   end
diff --git a/Code/script_idl_mv/astrolib/adxy.pro b/Code/script_idl_mv/astrolib/adxy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..736a772d934ce31e155988ff57b4ce22c93b922b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/adxy.pro
@@ -0,0 +1,139 @@
+pro adxy, hdr, a, d, x, y, PRINT = print, ALT = alt        ;Ra, Dec to X,Y
+;+
+; NAME:
+;       ADXY
+; PURPOSE:
+;       Use a FITS header to convert astronomical to pixel coordinates
+; EXPLANATION:
+;       Use an image header to compute X and Y positions, given the
+;       RA and Dec (or longitude, latitude) in decimal degrees.  
+;
+; CALLING SEQUENCE:
+;       ADXY, HDR               ;Prompt for Ra and DEC 
+;       ADXY, hdr, a, d, x, y, [ /PRINT, ALT= ]
+;
+; INPUTS:
+;       HDR - FITS Image header containing astrometry parameters
+;
+; OPTIONAL INPUTS:
+;       A - Right ascension in decimal DEGREES, scalar or vector
+;       D - Declination in decimal DEGREES, scalar or vector        
+;
+;       If A and D are not supplied, user will be prompted to supply
+;       them in either decimal degrees or HR,MIN,SEC,DEG,MN,SC format.
+;
+; OPTIONAL OUTPUT:
+;       X     - row position in pixels, same number of elements as A and D
+;       Y     - column position in pixels
+;
+;       X and Y will be in standard IDL convention (first pixel is 0) and not
+;       the FITS convention (first pixel is 1).      As in FITS an integral
+;       value corresponds to the center of a pixel.
+; OPTIONAL KEYWORD INPUT:
+;       /PRINT - If this keyword is set and non-zero, then results are displayed
+;               at the terminal.
+;       ALT -  single character 'A' through 'Z' or ' ' specifying an alternate 
+;             astrometry system present in the FITS header.    The default is
+;             to use the primary astrometry or ALT = ' '.   If /ALT is set, 
+;             then this is equivalent to ALT = 'A'.   See Section 3.3 of 
+;             Greisen & Calabretta (2002, A&A, 395, 1061) for information about
+;             alternate astrometry keywords.
+;
+; OPERATIONAL NOTES:
+;       If less than 5 parameters are supplied, or if the /PRINT keyword is
+;       set, then the X and Y positions are displayed at the terminal.
+;
+;       If the procedure is to be used repeatedly with the same header,
+;       then it would be faster to use AD2XY.
+;
+; PROCEDURES CALLED:
+;       AD2XY, ADSTRING(), EXTAST, GETOPT(), TEN()
+;
+; REVISION HISTORY:
+;       W. Landsman                 HSTX          January, 1988
+;       Use astrometry structure   W. Landsman   January, 1994  
+;       Changed default ADSTRING format   W. Landsman    September, 1995
+;       Check if latitude/longitude reversed in CTYPE keyword W. L. Feb. 2004
+;       Added ALT keyword   W. Landsman   September 2004
+;       Work for non-spherical coordinate transformation W. Landsman May 2005 
+;       More informative error message if astrometry missing W.L. Feb 2008
+;       Cosmetic updates W.L. July 2011
+;       Use version 2 astrometry structure J. P. Leahy July 2013       
+;-
+ Compile_opt idl2
+ On_error,2
+
+ npar = N_params()
+
+ if ( npar EQ 0 ) then begin
+        print,'Syntax - ADXY, hdr, [a, d, x, y, /PRINT, ALT= ]'
+        print,'If supplied, A and D must be in decimal DEGREES'
+        return
+ endif                                                                  
+ 
+ extast, hdr, astr, noparams, ALT = alt   ;Extract astrometry from FITS header
+  if ( noparams LT 0 ) then begin
+        if alt EQ '' then $
+        message,'ERROR - No astrometry info in supplied FITS header' $
+	else  message, $
+	'ERROR  - No alt=' + alt + ' astrometry info in supplied FITS header'
+  endif	
+
+ astr2 = TAG_EXIST(astr,'AXES') ; Version 2 structure
+ 
+ if npar lt 3 then begin
+   RD: print,'Coordinates must be entered in either decimal (2 parameter) ' 
+   print,'  or sexagesimal (6 parameter) format'
+   inp = ''
+   read,'ADXY: Enter coordinates: ',inp
+   radec = getopt(inp,'F')
+   case N_elements(radec) of 
+      2: begin 
+         a = radec[0] & d = radec[1]
+         end
+      6: begin
+         a = ten(radec[0:2]*15.) & d = ten(radec[3:5])
+         end
+   else: begin
+         print,'ADXY: ERROR - Either 2 or 6 parameters must be entered'
+         return
+         end
+   endcase 
+ endif
+
+ case strmid( astr.ctype[0], 5,3) of
+ 'GSS': gsssadxy, astr, a, d, x, y       ;HST Guide star astrometry
+ else:  ad2xy, a, d, astr, x, y          ;All other cases
+ endcase
+
+ if (npar lt 5) || keyword_set( PRINT ) then begin
+        npts = N_elements(a)
+        tit = strmid(astr.ctype,0,4)
+         spherical = strmid(astr.ctype[0],4,1) EQ '-'
+	if spherical then begin
+        fmt = '(2F9.4,A,2X,2F8.2)'
+        str = adstring(a,d,1)
+        tit = strmid(astr.ctype,0,4)
+        tit = repchr(tit,'-',' ')
+        flip = astr2 ? astr.reverse : $
+        (tit[0] EQ 'DEC ') || (tit[0] EQ 'ELAT') || (tit[0] EQ 'GLAT') 
+        if flip then tit = rotate(tit,2)
+        print,'    ' + tit[0] + '    ' + tit[1] + '       ' + tit[0]  + $
+              '         ' + tit[1]  + '        X       Y'
+        for i = 0l, npts-1 do $
+        print,FORMAT = fmt, a[i], d[i], str[i], x[i], y[i] 
+        endif else begin
+	 unit1 = strtrim( sxpar( hdr, 'CUNIT1'+alt,count = N_unit1),2)
+	 if N_unit1 EQ 0 then unit1 = ''
+	 unit2 = strtrim( sxpar( hdr, 'CUNIT2'+alt,count = N_unit2),2)
+	 if N_unit2 EQ 0 then unit2 = ''
+	 print,'   ' + tit[0] + '     ' + tit[1] + '         X       Y'
+	 if (N_unit1 GT 0) || (N_unit2 GT 0) then $
+	     print,unit1 ,unit2,f='(t5,a,t14,a)'
+	     for i=0l, npts-1 do $
+	 print, a[i], d[i], x[i], y[i], f='(2F9.4,2X,2F8.2)'
+       endelse
+  endif
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/airtovac.pro b/Code/script_idl_mv/astrolib/airtovac.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1cbdd01d0f443f0970928ce0b08d6d0cf98ac40e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/airtovac.pro
@@ -0,0 +1,67 @@
+pro airtovac,wave_air, wave_vac                  
+;+
+; NAME:
+;       AIRTOVAC
+; PURPOSE:
+;       Convert air wavelengths to vacuum wavelengths 
+; EXPLANATION:
+;       Wavelengths are corrected for the index of refraction of air under 
+;       standard conditions.  Wavelength values below 2000 A will not be 
+;       altered.  Uses relation of Ciddor (1996).
+;
+; CALLING SEQUENCE:
+;       AIRTOVAC, WAVE_AIR, [ WAVE_VAC]
+;
+; INPUT/OUTPUT:
+;       WAVE_AIR - Wavelength in Angstroms, scalar or vector
+;               If this is the only parameter supplied, it will be updated on
+;               output to contain double precision vacuum wavelength(s). 
+; OPTIONAL OUTPUT:
+;        WAVE_VAC - Vacuum wavelength in Angstroms, same number of elements as
+;                 WAVE_AIR, double precision
+;
+; EXAMPLE:
+;       If the air wavelength is  W = 6056.125 (a Krypton line), then 
+;       AIRTOVAC, W yields an vacuum wavelength of W = 6057.8019
+;
+; METHOD:
+;	Formula from Ciddor 1996, Applied Optics 62, 958
+;
+; NOTES: 
+;       Take care within 1 A of 2000 A.   Wavelengths below 2000 A *in air* are
+;       not altered.       
+; REVISION HISTORY
+;       Written W. Landsman                November 1991
+;       Use Ciddor (1996) formula for better accuracy in the infrared 
+;           Added optional output vector, W Landsman Mar 2011
+;       Iterate for better precision W.L./D. Schlegel  Mar 2011
+;-
+   On_error,2
+   compile_opt idl2
+
+  if N_params() EQ 0 then begin
+      print,'Syntax - AIRTOVAC, WAVE_AIR, [WAVE_VAC]'
+      print,'WAVE_AIR (Input) is the air wavelength in Angstroms'
+       return
+  endif
+
+    wave_vac = double(wave_air)
+    g = where(wave_vac GE 2000, Ng)     ;Only modify above 2000 A
+    
+    if Ng GT 0 then begin 
+ 
+  for iter=0, 1 do begin
+  sigma2 = (1d4/double(wave_vac[g]) )^2.     ;Convert to wavenumber squared
+
+; Compute conversion factor
+  fact = 1.D +  5.792105D-2/(238.0185D0 - sigma2) + $
+                            1.67917D-3/( 57.362D0 - sigma2)
+    
+
+  wave_vac[g] = wave_air[g]*fact              ;Convert Wavelength
+  endfor
+  if N_params() EQ 1 then wave_air = wave_vac
+  endif
+  
+  return            
+  end
diff --git a/Code/script_idl_mv/astrolib/aitoff.pro b/Code/script_idl_mv/astrolib/aitoff.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6e7fbee43321e80eab083cc72b7915d3670001d5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/aitoff.pro
@@ -0,0 +1,56 @@
+pro aitoff,l,b,x,y
+;+
+; NAME:
+;       AITOFF
+; PURPOSE:
+;       Convert longitude, latitude to X,Y using an AITOFF projection.
+; EXPLANATION:
+;       This procedure can be used to create an all-sky map in Galactic 
+;       coordinates with an equal-area Aitoff projection.  Output map 
+;       coordinates are zero longitude centered.
+;
+; CALLING SEQUENCE:
+;       AITOFF, L, B, X, Y 
+;
+; INPUTS:
+;       L - longitude - scalar or vector, in degrees
+;       B - latitude - same number of elements as L, in degrees
+;
+; OUTPUTS:
+;       X - X coordinate, same number of elements as L.   X is normalized to
+;               be between -180 and 180
+;       Y - Y coordinate, same number of elements as L.  Y is normalized to
+;               be between -90 and 90.
+;
+; NOTES:
+;       See AIPS memo No. 46, page 4, for details of the algorithm.  This
+;       version of AITOFF assumes the projection is centered at b=0 degrees.
+;
+; REVISION HISTORY:
+;       Written  W.B. Landsman  STX          December 1989
+;       Modified for Unix:
+;               J. Bloch        LANL SST-9      5/16/91 1.1
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ if N_params() ne 4 then begin
+     print,'Syntax - AITOFF, L, B, X, Y'
+     return
+ endif
+
+ sa = l
+ if N_elements(sa) eq 1 then sa = fltarr(1) + sa
+ x180 = where (sa gt 180.0)
+ if x180[0] ne -1 then sa[x180]  = sa[x180] - 360.
+ alpha2 = sa/(2*!RADEG)
+ delta = b/!RADEG   
+ r2 = sqrt(2.)    
+ f = 2*r2/!PI     
+ cdec = cos(delta)    
+ denom =sqrt(1. + cdec*cos(alpha2))
+ x = cdec*sin(alpha2)*2.*r2/denom
+ y = sin(delta)*r2/denom
+ x = x*!radeg/f
+ y = y*!radeg/f
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/aitoff_grid.pro b/Code/script_idl_mv/astrolib/aitoff_grid.pro
new file mode 100644
index 0000000000000000000000000000000000000000..62e45f91499c2de56cf260567d2d2ae0b71f8e5e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/aitoff_grid.pro
@@ -0,0 +1,144 @@
+;+
+; NAME:
+;       AITOFF_GRID
+;
+; PURPOSE:
+;       Produce an overlay of latitude and longitude lines over a plot or image
+; EXPLANATION:
+;       The grid is plotted on the current graphics device. AITOFF_GRID 
+;       assumes that the ouput plot coordinates span the x-range of 
+;       -180 to 180 and the y-range goes from -90 to 90.
+;
+; CALLING SEQUENCE:
+;
+;       AITOFF_GRID [,DLONG,DLAT, LABEL=, /NEW, CHARTHICK=, CHARSIZE=, 
+;                     FONT=, _EXTRA=]
+;
+; OPTIONAL INPUTS:
+;
+;       DLONG   = Optional input longitude line spacing in degrees. If left
+;                 out, defaults to 30.
+;       DLAT    = Optional input latitude line spacing in degrees. If left
+;                 out, defaults to 30.
+;
+; OPTIONAL INPUT KEYWORDS:
+;
+;       LABEL           = Optional keyword specifying that the latitude and
+;                         longitude lines on the prime meridian and the
+;                         equator should be labeled in degrees. If LABELS is
+;                         given a value of 2, i.e. LABELS=2, then the longitude
+;                         labels will be in hours instead of degrees.
+;        CHARSIZE       = If /LABEL is set, then CHARSIZE specifies the size
+;                         of the label characters (passed to XYOUTS)
+;        CHARTHICK     =  If /LABEL is set, then CHARTHICK specifies the 
+;                         thickness of the label characters (passed to XYOUTS)
+;       FONT          =   scalar font graphics keyword (-1,0 or 1) for text
+;       /NEW          =   If this keyword is set, then AITOFF_GRID will create
+;                         a new plot grid, rather than overlay an existing plot.
+;
+;       Any valid keyword to OPLOT such as COLOR, LINESTYLE, THICK can be 
+;       passed to AITOFF_GRID (though the _EXTRA facility) to to specify the
+;       color, style, or thickness of the grid lines.
+; OUTPUTS:
+;       Draws grid lines on current graphics device.
+;
+; EXAMPLE:
+;       Create a labeled Aitoff grid of the Galaxy, and overlay stars at 
+;       specified Galactic longitudes, glong and latitudes, glat
+;
+;       IDL> aitoff_grid,/label,/new        ;Create labeled grid
+;       IDL> aitoff, glong, glat, x,y      ;Convert to X,Y coordinates
+;       IDL> plots,x,y,psym=2              ;Overlay "star" positions
+;
+; PROCEDURES USED:
+;       AITOFF
+; NOTES:
+;       If labeling in hours (LABEL=2) then the longitude spacing should be
+;       a multiple of 15 degrees
+;
+; AUTHOR AND MODIFICATIONS:
+;
+;       J. Bloch        1.2     6/2/91
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Create default plotting coords, if needed   W. Landsman  August 2000
+;       Added _EXTRA, CHARTHICK, CHARSIZE keywords  W. Landsman  March 2001
+;       Several tweaks, plot only hours not minutes W. Landsman January 2002
+;       Allow FONT keyword to be passed to XYOUTS.  T. Robishaw Apr. 2006
+;-
+PRO AITOFF_GRID,DLONG,DLAT,LABEL=LABEL, NEW = new, _EXTRA= E, $
+     CHARSIZE = charsize, CHARTHICK =charthick, FONT=font
+
+        if  N_elements(dlong) EQ 0 then dlong = 30.0
+        if  N_elements(dlat) EQ 0 then dlat = 30.0
+        if  N_elements(font) EQ 0 then font = !p.font
+
+; If no plotting axis has been defined, then create a default one
+
+        new = keyword_set(new)
+        if not new then new =  (!X.crange[0] EQ 0) and (!X.crange[1] EQ 0)
+        if new then plot,[-180,180],[-90,90],/nodata,xsty=5,ysty=5
+;
+;       Do lines of constant longitude
+;
+        lat=findgen(181)-90
+        lng=fltarr(181,/nozero)
+        lngtot = long(180.0/dlong)
+
+        for i=0,lngtot do begin
+                replicate_inplace, lng, -180.0 + (i*dlong)
+                aitoff,lng,lat,x,y
+                oplot,x,y,_extra=e
+                oplot,-x,y,_extra=e
+        endfor
+;
+;       Do lines of constant latitude
+;
+        lng = findgen(361)-180.0
+        lat = fltarr(361,/nozero)
+        lattot=long(180.0/dlat)
+        for i=1,lattot do begin
+                replicate_inplace, lat, -90. + (i*dlat)
+                aitoff,lng,lat,x,y
+                oplot,x,y,_extra=e
+         endfor
+;
+;       Do labeling if requested
+;
+        if keyword_set(label) then begin
+
+;
+;       Label equator
+;
+          if (!d.name eq 'PS') and (font eq 0) then hr = '!Uh!N' else hr='h'
+             xoff = 2*dlong/30.
+            for i=0,2*lngtot-1 do begin
+                lng =  (180 + (i*dlong)) mod 360
+                if (lng ne 0.0) and (lng ne 180.0) then begin
+                    aitoff,lng,0.0,x,y
+                    if label eq 1 then xyouts,x[0]+xoff,y[0]+1,$
+                        strcompress(string(lng,format="(I4)"),/remove_all), $
+                        charsize = charsize, charthick = charthick,font=font $
+                    else begin
+                         tmp = lng/15.
+                         xyouts,round(x[0])+xoff,round(y[0])+1,string(tmp[0],$
+                            format='(I2)') + hr, font=font,$
+                            charsize = charsize, charthick = charthick
+                    endelse
+                endif
+            endfor
+;
+;       Label prime meridian
+;
+            lat = -90 + (indgen(lattot-1)+1)*dlat
+            aitoff,fltarr(lattot-1),lat,x,y
+            slat = strtrim(round(lat),2)
+            pos = where(lat GT 0, Npos)
+            if Npos GT 0 then slat[pos] = '+' + slat[pos] 
+            for i=0,lattot-2 do begin
+                 xyouts,x[i]+2,y[i]+1, slat[i], font=font, $
+                        charsize = charsize, charthick = charthick
+            endfor
+        endif
+
+        return
+end
diff --git a/Code/script_idl_mv/astrolib/al_legend.pro b/Code/script_idl_mv/astrolib/al_legend.pro
new file mode 100644
index 0000000000000000000000000000000000000000..74c02f60e5f167b61d8163a5cadfcff9b5b549ae
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/al_legend.pro
@@ -0,0 +1,572 @@
+;+
+; NAME:
+;       AL_LEGEND
+; PURPOSE:
+;       Create an annotation legend for a plot.
+; EXPLANATION:
+;           
+;       This procedure makes a legend for a plot.  The legend can contain
+;       a mixture of symbols, linestyles, Hershey characters (vectorfont),
+;       and filled polygons (usersym).  A test procedure, al_legendtest.pro,
+;       shows legend's capabilities.  Placement of the legend is controlled
+;       with keywords like /right, /top, and /center or by using a position
+;       keyword for exact placement (position=[x,y]) or via mouse (/position).
+;
+;       The procedure CGLEGEND in the Coyote library provides a similar 
+;       capability.     https://www.idlcoyote.com/idldoc/cg/cglegend.html
+; CALLING SEQUENCE:
+;       AL_LEGEND [,items][,keyword options]
+; EXAMPLES:
+;       The call:
+;               al_legend,['Plus sign','Asterisk','Period'],psym=[1,2,3]
+;         produces:
+;               -----------------
+;               |               |
+;               |  + Plus sign  |
+;               |  * Asterisk   |
+;               |  . Period     |
+;               |               |
+;               -----------------
+;         Each symbol is drawn with a cgPlots command, so they look OK.
+;         Other examples are given in optional output keywords.
+;
+;       lines = indgen(6)                       ; for line styles
+;       items = 'linestyle '+strtrim(lines,2)   ; annotations
+;       al_legend,items,linestyle=lines         ; vertical legend---upper left
+;       items = ['Plus sign','Asterisk','Period']
+;       sym = [1,2,3]
+;       al_legend,items,psym=sym                   ; ditto except using symbols
+;       al_legend,items,psym=sym,/horizontal       ; horizontal format
+;       al_legend,items,psym=sym,box=0             ; sans border
+;       al_legend,items,psym=sym,delimiter='='     ; embed '=' betw psym & text
+;       al_legend,items,psym=sym,margin=2          ; 2-character margin
+;       al_legend,items,psym=sym,position=[x,y]    ; upper left in data coords
+;       al_legend,items,psym=sym,pos=[x,y],/norm   ; upper left in normal coords
+;       al_legend,items,psym=sym,pos=[x,y],/device ; upper left in device coords
+;       al_legend,items,psym=sym,/position         ; interactive position
+;       al_legend,items,psym=sym,/right            ; at upper right
+;       al_legend,items,psym=sym,/bottom           ; at lower left
+;       al_legenditems,psym=sym,/center           ; approximately near center
+;       al_legend,items,psym=sym,number=2          ; plot two symbols, not one
+;     Plot 3 filled colored squares
+;       al_legend,items,/fill,psym=[8,8,8],colors=['red','green','blue']
+;
+;        Another example of the use of AL_LEGEND can be found at 
+;        http://www.idlcoyote.com/cg_tips/al_legend.php
+; INPUTS:
+;       items = text for the items in the legend, a string array.
+;               For example, items = ['diamond','asterisk','square'].
+;               You can omit items if you don't want any text labels.  The
+;               text can include many LaTeX symbols (e.g. $\leq$) for a less
+;               than equals symbol) as described in cgsymbol.pro. 
+; OPTIONAL INPUT KEYWORDS:
+;
+;       linestyle = array of linestyle numbers  If linestyle[i] < 0, then omit
+;               ith symbol or line to allow a multi-line entry.     If 
+;               linestyle = -99 then text will be left-justified.  
+;       psym = array of plot symbol numbers or names.  If psym[i] is negative, 
+;               then a line connects pts for ith item.  If psym[i] = 8, then the
+;               procedure USERSYM is called with vertices defined in the
+;               keyword usersym.   If psym[i] = 88, then use the previously
+;               defined user symbol.    If 11 <= psym[i] <= 46 then David
+;               Fanning's function CGSYMCAT() will be used for additional 
+;               symbols.   Note that PSYM=10 (histogram plot mode) is not 
+;               allowed since it cannot be used with the cgPlots command.
+;       vectorfont = vector-drawn characters for the sym/line column, e.g.,
+;               ['!9B!3','!9C!3','!9D!3'] produces an open square, a checkmark,
+;               and a partial derivative, which might have accompanying items
+;               ['BOX','CHECK','PARTIAL DERIVATIVE'].
+;               There is no check that !p.font is set properly, e.g., -1 for
+;               X and 0 for PostScript.  This can produce an error, e.g., use
+;               !20 with PostScript and !p.font=0, but allows use of Hershey
+;               *AND* PostScript fonts together.
+;       N. B.: Choose any of linestyle, psym, and/or vectorfont.  If none is
+;               present, only the text is output.  If more than one
+;               is present, all need the same number of elements, and normal
+;               plot behaviour occurs.
+;               By default, if psym is positive, you get one point so there is
+;               no connecting line.  If vectorfont[i] = '',
+;               then cgPlots is called to make a symbol or a line, but if
+;               vectorfont[i] is a non-null string, then cgText is called.
+;       /help = flag to print header
+;       /horizontal = flag to make the legend horizontal
+;       /vertical = flag to make the legend vertical (D=vertical)
+;       background_color - color name or number to fill the legend box.
+;              Automatically sets /clear.    (D = -1)
+;       box = flag to include/omit box around the legend (D=include)
+;		  outline_color = color of box outline (D = !P.color)
+;       bthick = thickness of the legend box (D = !P.thick)
+;       charsize = just like !p.charsize for plot labels
+;       charthick = just like !p.charthick for plot labels
+;       clear = flag to clear the box area before drawing the legend
+;       colors = array of colors names or numbers for plot symbols/lines 
+;          See cgCOLOR for list of color names.   Default is 'Opposite'
+;          If you are using index colors (0-255), then supply color as a byte,
+;          integer or string, but not as a long, which will be interpreted as 
+;          a decomposed color. See http://www.idlcoyote.com/cg_tips/legcolor.php
+;       delimiter = embedded character(s) between symbol and text (D=none)
+;       font = scalar font graphics keyword (-1,0 or 1) for text
+;       linsize = Scale factor for line length (0-1), default = 1
+;                 Set to 0 to give a dot, 0.5 give half default line length   
+;       margin = margin around text measured in characters and lines
+;       number = number of plot symbols to plot or length of line (D=1)
+;       spacing = line spacing (D=bit more than character height)
+;       position = data coordinates of the /top (D) /left (D) of the legend
+;       pspacing = psym spacing (D=3 characters) (when number of symbols is
+;             greater than 1)
+;       textcolors = array of color names or numbers for text.  See cgCOLOR
+;          for a list of color names.   Default is 'Opposite' of background
+;       thick = array of line thickness numbers (D = !P.thick), if used, then 
+;               linestyle must also be specified
+;       normal = use normal coordinates for position, not data
+;       device = use device coordinates for position, not data
+;       /window - if set then send legend to a resizeable graphics window
+;       usersym = 2-D array of vertices, cf. usersym in IDL manual. 
+;             (/USERSYM =square, default is to use existing USERSYM definition)
+;       /fill = flag to fill the usersym
+;       /left_legend = flag to place legend snug against left side of plot
+;                 window (D)
+;       /right_legend = flag to place legend snug against right side of plot
+;               window.    If /right,pos=[x,y], then x is position of RHS and
+;               text runs right-to-left.
+;       /top_legend = flag to place legend snug against top of plot window (D)
+;       /bottom = flag to place legend snug against bottom of plot window
+;               /top,pos=[x,y] and /bottom,pos=[x,y] produce same positions.
+;
+;       If LINESTYLE, PSYM, VECTORFONT, SYMSIZE, THICK, COLORS, or 
+;       TEXTCOLORS are supplied as scalars, then the scalar value is set for 
+;       every line or symbol in the legend.
+; Outputs:
+;       legend to current plot device
+; OPTIONAL OUTPUT KEYWORDS:
+;       corners = 4-element array, like !p.position, of the normalized
+;         coords for the box (even if box=0): [llx,lly,urx,ury].
+;         Useful for multi-column or multi-line legends, for example,
+;         to make a 2-column legend, you might do the following:
+;           c1_items = ['diamond','asterisk','square']
+;           c1_psym = [4,2,6]
+;           c2_items = ['solid','dashed','dotted']
+;           c2_line = [0,2,1]
+;           al_legend,c1_items,psym=c1_psym,corners=c1,box=0
+;           al_legend,c2_items,line=c2_line,corners=c2,box=0,pos=[c1[2],c1[3]]
+;           c = [c1[0]<c2[0],c1[1]<c2[1],c1[2]>c2[2],c1[3]>c2[3]]
+;         cgplots,[c[0],c[0],c[2],c[2],c[0]],[c[1],c[3],c[3],c[1],c[1]],/norm
+;
+;         Useful also to place the legend.  Here's an automatic way to place
+;         the legend in the lower right corner.  The difficulty is that the
+;         legend's width is unknown until it is plotted.  In this example,
+;         the legend is plotted twice: the first time in the upper left, the
+;         second time in the lower right.
+;
+;         al_legend,['1','22','333','4444'],linestyle=indgen(4),corners=corners
+;                       ; BOGUS LEGEND---FIRST TIME TO REPORT CORNERS
+;           xydims = [corners[2]-corners[0],corners[3]-corners[1]]
+;                       ; SAVE WIDTH AND HEIGHT
+;           chdim=[!d.x_ch_size/float(!d.x_size),!d.y_ch_size/float(!d.y_size)]
+;                       ; DIMENSIONS OF ONE CHARACTER IN NORMALIZED COORDS
+;           pos = [!x.window[1]-chdim[0]-xydims[0] $
+;                       ,!y.window[0]+chdim[1]+xydims[1]]
+;                       ; CALCULATE POSITION FOR LOWER RIGHT
+;           cgplot,findgen(10)    ; SIMPLE PLOT; YOU DO WHATEVER YOU WANT HERE.
+;           al_legend,['1','22','333','4444'],linestyle=indgen(4),pos=pos
+;                       ; REDO THE LEGEND IN LOWER RIGHT CORNER
+;         You can modify the pos calculation to place the legend where you
+;         want.  For example to place it in the upper right:
+;           pos = [!x.window[1]-chdim[0]-xydims[0],!y.window[1]-xydims[1]]
+; Common blocks:
+;       none
+; Procedure:
+;       If keyword help is set, call doc_library to print header.
+;       See notes in the code.  Much of the code deals with placement of the
+;       legend.  The main problem with placement is not being
+;       able to sense the length of a string before it is output.  Some crude
+;       approximations are used for centering.
+; Restrictions:
+;       Here are some things that aren't implemented.
+;       - An orientation keyword would allow lines at angles in the legend.
+;       - An array of usersyms would be nice---simple change.
+;       - An order option to interchange symbols and text might be nice.
+;       - Somebody might like double boxes, e.g., with box = 2.
+;       - Another feature might be a continuous bar with ticks and text.
+;       - There are no guards to avoid writing outside the plot area.
+;       - There is no provision for multi-line text, e.g., '1st line!c2nd line'
+;         Sensing !c would be easy, but !c isn't implemented for PostScript.
+;         A better way might be to simply output the 2nd line as another item
+;         but without any accompanying symbol or linestyle.  A flag to omit
+;         the symbol and linestyle is linestyle[i] = -1.
+;       - There is no ability to make a title line containing any of titles
+;         for the legend, for the symbols, or for the text.
+; Notes:
+;       This procedure was originally named LEGEND, but a distinct LEGEND() 
+;       function was introduced into IDL V8.0.   Therefore, the      
+;       original LEGEND procedure was renamed to AL_LEGEND to avoid conflict.  
+;
+; Modification history:
+;       write, 24-25 Aug 92, F K Knight (knight@ll.mit.edu)
+;       allow omission of items or omission of both psym and linestyle, add
+;         corners keyword to facilitate multi-column legends, improve place-
+;         ment of symbols and text, add guards for unequal size, 26 Aug 92, FKK
+;       add linestyle(i)=-1 to suppress a single symbol/line, 27 Aug 92, FKK
+;       add keyword vectorfont to allow characters in the sym/line column,
+;         28 Aug 92, FKK
+;       add /top, /bottom, /left, /right keywords for automatic placement at
+;         the four corners of the plot window.  The /right keyword forces
+;         right-to-left printing of menu. 18 Jun 93, FKK
+;       change default position to data coords and add normal, data, and
+;         device keywords, 17 Jan 94, FKK
+;       add /center keyword for positioning, but it is not precise because
+;         text string lengths cannot be known in advance, 17 Jan 94, FKK
+;       add interactive positioning with /position keyword, 17 Jan 94, FKK
+;       allow a legend with just text, no plotting symbols.  This helps in
+;         simply describing a plot or writing assumptions done, 4 Feb 94, FKK
+;       added thick, symsize, and clear keyword Feb 96, W. Landsman HSTX
+;               David Seed, HR Wallingford, d.seed@hrwallingford.co.uk
+;       allow scalar specification of keywords, Mar 96, W. Landsman HSTX
+;       added charthick keyword, June 96, W. Landsman HSTX
+;       Made keyword names  left,right,top,bottom,center longer,
+;                                 Aug 16, 2000, Kim Tolbert
+;       Added ability to have regular text lines in addition to plot legend 
+;       lines in legend.  If linestyle is -99 that item is left-justified.
+;       Previously, only option for no sym/line was linestyle=-1, but then text
+;       was lined up after sym/line column.    10 Oct 2000, Kim Tolbert
+;       Make default value of thick = !P.thick  W. Landsman  Jan. 2001
+;       Don't overwrite existing USERSYM definition  W. Landsman Mar. 2002
+;	     Added outline_color BT 24 MAY 2004
+;       Pass font keyword to cgText commands.  M. Fitzgerald, Sep. 2005
+;       Default spacing, pspacing should be relative to charsize. M. Perrin, July 2007
+;       Don't modify position keyword  A. Kimball/ W. Landsman Jul 2007
+;       Small update to Jul 2007 for /NORMAL coords.  W. Landsman Aug 2007
+;       Use SYMCAT() plotting symbols for 11<=PSYM<=46   W. Landsman  Nov 2009
+;       Make a sharper box edge T. Robishaw/W.Landsman July 2010
+;       Added BTHICK keyword W. Landsman October 2010
+;       Added BACKGROUND_COLOR keyword  W. Landsman February 2011
+;       Incorporate Coyote graphics  W. Landsman  February 2011
+;       Added LINSIZE keyword W.L./V.Gonzalez   May 2011
+;       Fixed a small problem with Convert_Coord when the Window keyword is set. 
+;                         David Fanning, May 2011.
+;       Fixed problem when /clear and /Window are set J. Bailin/WL   May 2011
+;       CGQUERY was called instead of CGCONTROL   W.L.  June 2011
+;       Fixed typo preventing BTHICK keyword from working W.L. Dec 2011
+;       Remove call to SYMCAT() W.L. Dec 2011
+;       Changed the way the WINDOW keyword adds commands to cgWindow, and
+;       now default to BACKGROUND for background color. 1 Feb 2012 David Fanning
+;       Allow 1 element SYMSIZE for vector input, WL Apr 2012.
+;       Allow to specify symbols by cgSYMCAT() name WL Aug 2012 
+;       Fixed bug when linsize, /right called simultaneously, Dec 2012, K.Stewart
+;       Added a check for embedded symbols in the items string array. March 2013. David Fanning
+;       
+;-
+pro al_legend, items, BOTTOM_LEGEND=bottom, BOX = box, CENTER_LEGEND=center, $
+    CHARTHICK=charthick, CHARSIZE = charsize, CLEAR = clear, COLORS = colorsi, $
+    CORNERS = corners, DATA=data, DELIMITER=delimiter, DEVICE=device, $
+    FILL=fill, HELP = help, HORIZONTAL=horizontal,LEFT_LEGEND=left, $
+    LINESTYLE=linestylei, MARGIN=margin, NORMAL=normal, NUMBER=number, $
+    POSITION=position,PSPACING=pspacing, PSYM=psymi, RIGHT_LEGEND=right, $
+    SPACING=spacing, SYMSIZE=symsizei, TEXTCOLORS=textcolorsi, THICK=thicki, $
+    TOP_LEGEND=top, USERSYM=usersym,  VECTORFONT=vectorfonti, $
+    VERTICAL=vertical,OUTLINE_COLOR = outline_color, FONT = font, $
+    BTHICK=bthick, background_color = bgcolor, WINDOW=window,LINSIZE = linsize
+;
+;       =====>> HELP
+;
+compile_opt idl2
+;On_error,2
+if keyword_set(help) then begin & doc_library,'al_legend' & return & endif
+; Should this commnad be added to a resizeable graphics window?
+IF (Keyword_Set(window)) && ((!D.Flags AND 256) NE 0) THEN BEGIN
+    
+        cgWindow, 'al_legend', items, BOTTOM_LEGEND=bottom, BOX = box, CENTER_LEGEND=center, $
+            CHARTHICK=charthick, CHARSIZE = charsize, CLEAR = clear, COLORS = colorsi, $
+            CORNERS = corners, DATA=data, DELIMITER=delimiter, DEVICE=device, $
+            FILL=fill, HELP = help, HORIZONTAL=horizontal,LEFT_LEGEND=left, $
+            LINESTYLE=linestylei, MARGIN=margin, NORMAL=normal, NUMBER=number, $
+            POSITION=position,PSPACING=pspacing, PSYM=psymi, RIGHT_LEGEND=right, $
+            SPACING=spacing, SYMSIZE=symsizei, TEXTCOLORS=textcolorsi, THICK=thicki, $
+            TOP_LEGEND=top, USERSYM=usersym,  VECTORFONT=vectorfonti, $
+            VERTICAL=vertical,OUTLINE_COLOR = outline_color, FONT = font, $
+            BTHICK=thick, background_color = bgcolor, LINSIZE = linsize, ADDCMD=1
+                            
+         RETURN
+    ENDIF
+    ;
+
+;
+;       =====>> SET DEFAULTS FOR SYMBOLS, LINESTYLES, AND ITEMS.
+;
+ ni = n_elements(items)
+ np = n_elements(psymi)
+ nl = n_elements(linestylei)
+ nth = n_elements(thicki)
+ nsym = n_elements(symsizei)
+ nv = n_elements(vectorfonti)
+ nlpv = max([np,nl,nv])
+ n = max([ni,np,nl,nv])                                  ; NUMBER OF ENTRIES
+strn = strtrim(n,2)                                     ; FOR ERROR MESSAGES
+if n eq 0 then message,'No inputs!  For help, type al_legend,/help.'
+if ni eq 0 then begin
+  items = replicate('',n)                               ; DEFAULT BLANK ARRAY
+endif else begin
+  if size(items,/TNAME) NE 'STRING' then message, $
+      'First parameter must be a string array.  For help, type al_legend,/help.'
+  if ni ne n then message,'Must have number of items equal to '+strn
+endelse
+
+items = cgCheckForSymbols(items) ; Check for embedded symbols in the items array.
+symline = (np ne 0) || (nl ne 0)                        ; FLAG TO PLOT SYM/LINE
+ if (np ne 0) && (np ne n) && (np NE 1) then message, $
+        'Must have 0, 1 or '+strn+' elements in PSYM array.'
+ if (nl ne 0) && (nl ne n) && (nl NE 1) then message, $
+         'Must have 0, 1 or '+strn+' elements in LINESTYLE array.'
+ if (nth ne 0) && (nth ne n) && (nth NE 1) then message, $
+         'Must have 0, 1 or '+strn+' elements in THICK array.'
+
+ case nl of 
+ 0: linestyle = intarr(n)              ;Default = solid
+ 1: linestyle = intarr(n)  + linestylei
+ else: linestyle = linestylei
+ endcase 
+ 
+  case nsym of 
+ 0: symsize = replicate(!p.symsize,n)      ;Default = !P.SYMSIZE
+ 1: symsize = intarr(n) + symsizei
+ else: symsize = symsizei
+ endcase 
+
+ 
+ case nth of 
+ 0: thick = replicate(!p.thick,n)      ;Default = !P.THICK
+ 1: thick = intarr(n) + thicki
+ else: thick = thicki
+ endcase
+ 
+ if size(psymi,/TNAME) EQ 'STRING' then begin
+    psym = intarr(n)
+    for i=0,N_elements(psymi)-1 do psym[i] = cgsymcat(psymi[i])
+ endif else begin    
+     
+ case np of             ;Get symbols
+ 0: psym = intarr(n)    ;Default = solid
+ 1: psym = intarr(n) + psymi
+ else: psym = psymi
+ endcase 
+ endelse
+
+ case nv of 
+ 0: vectorfont = replicate('',n)
+ 1: vectorfont = replicate(vectorfonti,n)
+ else: vectorfont = vectorfonti
+ endcase 
+;
+;       =====>> CHOOSE VERTICAL OR HORIZONTAL ORIENTATION.
+;
+if n_elements(horizontal) eq 0 then $              ; D=VERTICAL
+  setdefaultvalue, vertical, 1 else $
+  setdefaultvalue, vertical, ~horizontal
+
+;
+;       =====>> SET DEFAULTS FOR OTHER OPTIONS.
+;
+ setdefaultvalue, box, 1
+ if N_elements(bgcolor) NE 0 then clear = 1
+ setdefaultvalue, bgcolor, 'BACKGROUND'
+ setdefaultvalue, clear, 0
+ setdefaultvalue, linsize, 1.
+ setdefaultvalue, margin, 0.5
+ setdefaultvalue, delimiter, ''
+ setdefaultvalue, charsize, !p.charsize
+ setdefaultvalue, charthick, !p.charthick
+ if charsize eq 0 then charsize = 1
+ setdefaultvalue, number, 1
+; Default color is opposite the background color
+ case N_elements(colorsi) of 
+ 0: colors = replicate('opposite',n)    
+ 1: colors = replicate(colorsi,n)
+ else: colors = colorsi
+ endcase 
+
+ case N_elements(textcolorsi) of 
+ 0: textcolors = replicate('opposite',n)     
+ 1: textcolors = replicate(textcolorsi,n)
+ else: textcolors = textcolorsi
+ endcase 
+ fill = keyword_set(fill)
+if n_elements(usersym) eq 1 then usersym = 2*[[0,0],[0,1],[1,1],[1,0],[0,0]]-1
+
+;
+;       =====>> INITIALIZE SPACING
+;
+setdefaultvalue, spacing, 1.2*charsize
+setdefaultvalue, pspacing , 3*charsize
+xspacing = !d.x_ch_size/float(!d.x_size) * (spacing > charsize)
+yspacing = !d.y_ch_size/float(!d.y_size) * (spacing > charsize)
+ltor = 1                                        ; flag for left-to-right
+if n_elements(left) eq 1 then ltor = left eq 1
+if n_elements(right) eq 1 then ltor = right ne 1
+ttob = 1                                        ; flag for top-to-bottom
+if n_elements(top) eq 1 then ttob = top eq 1
+if n_elements(bottom) eq 1 then ttob = bottom ne 1
+xalign = ltor ne 1                              ; x alignment: 1 or 0
+yalign = -0.5*ttob + 1                          ; y alignment: 0.5 or 1
+xsign = 2*ltor - 1                              ; xspacing direction: 1 or -1
+ysign = 2*ttob - 1                              ; yspacing direction: 1 or -1
+if ~ttob then yspacing = -yspacing
+if ~ltor then xspacing = -xspacing
+;
+;       =====>> INITIALIZE POSITIONS: FIRST CALCULATE X OFFSET FOR TEXT
+;
+xt = 0
+if nlpv gt 0 then begin                         ; SKIP IF TEXT ITEMS ONLY.
+if vertical then begin                          ; CALC OFFSET FOR TEXT START
+  for i = 0,n-1 do begin
+    if (psym[i] eq 0) and (vectorfont[i] eq '') then num = (number + 1) > 3 else num = number
+    if psym[i] lt 0 then num = number > 2       ; TO SHOW CONNECTING LINE
+    if psym[i] eq 0 then expand = linsize else expand = 2
+    thisxt = (expand*pspacing*(num-1)*xspacing)
+    if ltor then xt = thisxt > xt else xt = thisxt < xt
+    endfor
+endif   ; NOW xt IS AN X OFFSET TO ALIGN ALL TEXT ENTRIES.
+endif
+;
+;       =====>> INITIALIZE POSITIONS: SECOND LOCATE BORDER
+;
+
+if !x.window[0] eq !x.window[1] then begin
+  cgplot,/nodata,xstyle=4,ystyle=4,[0],/noerase
+endif
+;       next line takes care of weirdness with small windows
+pos = [min(!x.window),min(!y.window),max(!x.window),max(!y.window)]
+
+case n_elements(position) of
+ 0: begin
+  if ltor then px = pos[0] else px = pos[2]
+  if ttob then py = pos[3] else py = pos[1]
+  if keyword_set(center) then begin
+    if ~keyword_set(right) && ~keyword_set(left) then $
+      px = (pos[0] + pos[2])/2. - xt
+    if ~keyword_set(top) && ~keyword_set(bottom) then $
+      py = (pos[1] + pos[3])/2. + n*yspacing
+    endif
+  nposition = [px,py] + [xspacing,-yspacing]
+  end
+ 1: begin       ; interactive
+  message,/inform,'Place mouse at upper left corner and click any mouse button.'
+  cursor,x,y,/normal
+  nposition = [x,y]
+  end
+ 2: begin       ; convert upper left corner to normal coordinates
+ 
+  ; if keyword window is set, get the current graphics window.
+  if keyword_set(window) then begin
+     wid = cgQuery(/current)
+     WSet, wid
+  endif
+  if keyword_set(data) then $
+    nposition = convert_coord(position,/to_norm) $
+  else if keyword_set(device) then $
+    nposition = convert_coord(position,/to_norm,/device) $
+  else if ~keyword_set(normal) then $
+    nposition = convert_coord(position,/to_norm) else nposition= position
+  end
+ else: message,'Position keyword can have 0, 1, or 2 elements only. Try al_legend,/help.'
+endcase
+
+yoff = 0.25*yspacing*ysign                      ; VERT. OFFSET FOR SYM/LINE.
+
+x0 = nposition[0] + (margin)*xspacing            ; INITIAL X & Y POSITIONS
+y0 = nposition[1] - margin*yspacing + yalign*yspacing    ; WELL, THIS WORKS!
+;
+;       =====>> OUTPUT TEXT FOR LEGEND, ITEM BY ITEM.
+;       =====>> FOR EACH ITEM, PLACE SYM/LINE, THEN DELIMITER,
+;       =====>> THEN TEXT---UPDATING X & Y POSITIONS EACH TIME.
+;       =====>> THERE ARE A NUMBER OF EXCEPTIONS DONE WITH IF STATEMENTS.
+;
+for iclr = 0,clear do begin
+  y = y0                                                ; STARTING X & Y POSITIONS
+  x = x0
+  if ltor then xend = 0 else xend = 1           ; SAVED WIDTH FOR DRAWING BOX
+
+ if ttob then ii = [0,n-1,1] else ii = [n-1,0,-1]
+
+ for i = ii[0],ii[1],ii[2] do begin
+  if vertical then x = x0 else y = y0           ; RESET EITHER X OR Y
+  x = x + xspacing                              ; UPDATE X & Y POSITIONS
+  y = y - yspacing
+  if nlpv eq 0 then goto,TEXT_ONLY              ; FLAG FOR TEXT ONLY
+  num = number
+  if (psym[i] eq 0) && (vectorfont[i] eq '') then num = (number + 1) > 3 
+  if psym[i] lt 0 then num = number > 2         ; TO SHOW CONNECTING LINE
+  if psym[i] eq 0 then expand = 1 else expand = 2
+  xp = x + expand*pspacing*indgen(num)*xspacing
+  if (psym[i] gt 0) && (num eq 1) && vertical then xp = x + xt/2.
+  yp = y + intarr(num)
+  if vectorfont[i] eq '' then yp +=  yoff
+  if psym[i] eq 0 then begin
+      if ltor eq 1 then xp = [min(xp),max(xp) -(max(xp)-min(xp))*(1.-linsize)]   
+      if ltor ne 1 then xp = [min(xp) +(max(xp)-min(xp))*(1.-linsize),max(xp)]
+      yp = [min(yp),max(yp)]                      ; DITTO
+  endif
+  if (psym[i] eq 8) && (N_elements(usersym) GT 1) then $
+                usersym,usersym,fill=fill,color=colors[i]
+;; extra by djseed .. psym=88 means use the already defined usersymbol
+ if psym[i] eq 88 then p_sym =8 else $
+ if psym[i] EQ 10 then $
+         message,'PSYM=10 (histogram mode) not allowed to al_legend.pro' $
+ else p_sym= psym[i]
+
+  if vectorfont[i] ne '' then begin
+;    if (num eq 1) && vertical then xp = x + xt/2      ; IF 1, CENTERED.
+     cgText,xp,yp,vectorfont[i],width=width,color=colors[i], $
+      size=charsize,align=xalign,charthick = charthick,/norm,font=font
+    xt = xt > width
+    xp = xp + width/2.
+  endif else begin
+    if symline and (linestyle[i] ge 0) then cgPlots,xp,yp,color=colors[i] $
+      ,/normal,linestyle=linestyle[i],psym=p_sym,symsize=symsize[i], $
+      thick=thick[i]
+  endelse
+
+  if vertical then x += xt else if ltor then x = max(xp) else x = min(xp)
+  if symline then x += xspacing
+  
+  TEXT_ONLY:
+  if vertical && (vectorfont[i] eq '') && symline && (linestyle[i] eq -99) then x=x0 + xspacing
+  cgText,x,y,delimiter,width=width,/norm,color=textcolors[i], $
+         size=charsize,align=xalign,charthick = charthick,font=font	 
+  x += width*xsign
+  if width ne 0 then x += 0.5*xspacing
+  cgText,x,y,items[i],width=width,/norm,color=textcolors[i],size=charsize, $
+            align=xalign,charthick=charthick,font=font
+  x += width*xsign
+  if ~vertical && (i lt (n-1)) then x += 2*xspacing; ADD INTER-ITEM SPACE
+  xfinal = (x + xspacing*margin)
+  if ltor then xend = xfinal > xend else xend = xfinal < xend   ; UPDATE END X
+ endfor
+
+ if (iclr lt clear ) then begin
+;       =====>> CLEAR AREA
+        x = nposition[0]
+        y = nposition[1]
+        if vertical then bottom = n else bottom = 1
+        ywidth = - (2*margin+bottom-0.5)*yspacing
+        corners = [x,y+ywidth,xend,y]
+        cgColorfill,[x,xend,xend,x,x],y + [0,0,ywidth,ywidth,0],/norm, $
+	   color=bgcolor
+;       cgPlots,[x,xend,xend,x,x],y + [0,0,ywidth,ywidth,0], $
+;                 thick=2
+ endif else begin
+
+;
+;       =====>> OUTPUT BORDER
+;
+        x = nposition[0]
+        y = nposition[1]
+        if vertical then bottom = n else bottom = 1
+        ywidth = - (2*margin+bottom-0.5)*yspacing
+        corners = [x,y+ywidth,xend,y]
+        if box then cgPlots,[x,xend,xend,x,x,xend],y + [0,0,ywidth,ywidth,0,0],$
+	        /norm, color = outline_color,thick=bthick
+        return
+ endelse
+endfor
+
+end
diff --git a/Code/script_idl_mv/astrolib/al_legendtest.pro b/Code/script_idl_mv/astrolib/al_legendtest.pro
new file mode 100644
index 0000000000000000000000000000000000000000..55e33be9fedd4d5f03659a82533e858c81914095
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/al_legendtest.pro
@@ -0,0 +1,85 @@
+
+;+
+; NAME:
+;	AL_LEGENDTEST
+; PURPOSE:
+;	Demo program to show capabilities of  the al_legend procedure.
+; CALLING SEQUENCE:
+;	al_legendtest
+; INPUTS:
+;	none
+; OPTIONAL INPUTS:
+;	none
+; KEYWORDS:
+;	none
+; OUTPUTS:
+;	legends of note
+; COMMON BLOCKS:
+;	none
+; SIDE EFFECTS:
+;	Sets !20 font to symbol if PostScript and !p.font=0.
+; RESTRICTIONS:
+;	With the vectorfont test, you'll get different results for PostScript
+;	depending on the value of !p.font.
+; MODIFICATION HISTORY:
+;	write, 27 Aug 92, F.K.Knight (knight@ll.mit.edu)
+;	add test of /left,/right,/top,/bottom keywords, 21 June 93, FKK
+;	update based on recent changes to legend, 7 Feb 94, FKK
+;       Fix ambiguous CHAR keyword  W. Landsman Sep 2007
+;       Use Coyote graphics routines  W. Landsman Jan 2011
+;-
+pro al_legendtest
+if (!d.name eq 'PS') && (!p.font eq 0) then device,/Symbol,font_index=20
+items = ['diamond','asterisk','square']
+explanation = ['The al_legend procedure annotates plots---' $
+  ,'  either using text alone,' $
+  ,'  or text with plot symbols, lines, and special characters.' $
+  ,'The following are some examples.' $
+  ,'Hit return to continue.']
+psym = [4,2,6]
+lineitems = ['solid','dotted','DASHED']
+linestyle = [0,1,2]
+citems = 'color '+strtrim(string(indgen(8)),2)
+colors = ['red','blue','violet','green','yellow','brown','black','cyan']
+usersym,[-1,1,1,-1,-1],[-1,-1,1,1,-1],/fill
+z =	['al_legend,explanation,charsize=1.5' $
+	,'al_legend,items,psym=[4,2,6]' $
+	,'cgplot,findgen(10) & al_legend,items,psym=[4,2,6] & al_legend,items,psym=[4,2,6],/bottom,/right' $
+	,'al_legend,lineitems,linestyle=linestyle,/right,/bottom' $
+	,'al_legend,items,psym=psym,/horizontal,chars=1.5	; horizontal format' $
+	,'al_legend,[items,lineitems],psym=[psym,0,0,0],line=[0,0,0,linestyle],/center,box=0		; sans border' $
+	,'al_legend,items,psym=psym,margin=1,spacing=2,chars=2,delimiter="=",/top,/center; delimiter & larger margin' $
+	,'al_legend,lineitems,line=linestyle,pos=[.3,.5],/norm,chars=2,number=4	; position of legend' $
+	,'al_legend,items,psym=-psym,number=2,line=linestyle,/right; plot two symbols, not one' $
+	,'al_legend,citems,/fill,psym=15+intarr(8),colors=colors,chars=2; 8 filled squares' $
+	,'al_legend,[citems[0:4],lineitems],/fill,psym=[15+intarr(5),0*psym],line=[intarr(5),linestyle],colors=colors,chars=2,text=colors' $
+	,"al_legend,['Absurd','Sun Lover','Lucky Lady','Fishtail Palm'],vector=['ab!9r!3','!9nu!3','!9Wf!3','!9cN!20K!3'],charsize=2,/pos,psp=3"$
+	]
+prompt = 'Hit return to continue:'
+for i = 0,n_elements(z) - 1 do begin
+  cgerase
+  stat = execute(z[i])
+  cgtext,.01,.15,'COMMAND TO MAKE LEGEND:',charsize=1.7,/norm
+  cgtext,.01,.05,z[i],/norm,charsize=1.2
+  print,'Command: ',z[i]
+  print,prompt,format='($,a)'
+  a = get_kbrd(1)
+  print
+  endfor
+;stop
+cgerase
+!p.charsize=2
+c1_items = ['Plus','Asterisk','Period','Diamond','Triangle','Square','X']
+c1_psym = indgen(7)+1
+c2_items = ['Solid','Dotted','Dashed','Dash Dot','Dash Dot Dot Dot','Long Dashes']
+c2_line = indgen(6)
+al_legend,c1_items,psym=c1_psym,corners=c1,box=0
+al_legend,c2_items,line=c2_line,corners=c2,box=0,pos=[c1[2],c1[3]],/norm
+c = [c1[0]<c2[0],c1[1]<c2[1],c1[2]>c2[2],c1[3]>c2[3]]
+cgplots,[c[0],c[0],c[2],c[2],c[0]],[c[1],c[3],c[3],c[1],c[1]],/norm
+!p.charsize=0
+cgtext,.01,.05,$
+  'Multiple columns---type "al_legend,/help" for details.',/norm,charsize=1.2
+return
+end
+
diff --git a/Code/script_idl_mv/astrolib/altaz2hadec.pro b/Code/script_idl_mv/astrolib/altaz2hadec.pro
new file mode 100644
index 0000000000000000000000000000000000000000..96d543b33baedc546e9f2ea80f05d6f3503984fa
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/altaz2hadec.pro
@@ -0,0 +1,69 @@
+PRO altaz2hadec, alt, az, lat, ha, dec
+;+
+;  NAME:
+;    ALTAZ2HADEC
+; PURPOSE:
+;    Convert Horizon (Alt-Az) coordinates to Hour Angle and Declination.
+; EXPLANATION::
+;    Can deal with the NCP singularity.    Intended mainly to be used by
+;    program hor2eq.pro
+; CALLING SEQUENCE:
+;   ALTAZ2HADEC, alt, az, lat, ha, dec
+;
+; INPUTS
+;   alt - the local apparent altitude, in DEGREES, scalar or vector
+;   az  - the local apparent azimuth, in DEGREES, scalar or vector,
+;         measured EAST of NORTH!!!  If you have measured azimuth west-of-south
+;        (like the book MEEUS does), convert it to east of north via:
+;                       az = (az + 180) mod 360
+;
+;   lat -  the local geodetic latitude, in DEGREES, scalar or vector.
+;
+; OUTPUTS
+;   ha  -  the local apparent hour angle, in DEGREES.  The hour angle is the 
+;          time that right ascension of 0 hours crosses the local meridian.  
+;          It is unambiguously defined.
+;   dec -  the local apparent declination, in DEGREES.
+;
+; EXAMPLE:
+;     Arcturus is observed at an apparent altitude of 59d,05m,10s and an 
+;     azimuth (measured east of north) of 133d,18m,29s while at the 
+;     latitude of +43.07833 degrees.
+;     What are the local hour angle and declination of this object?
+;
+;     IDL> altaz2hadec, ten(59,05,10), ten(133,18,29), 43.07833, ha, dec
+;     ===> Hour angle ha = 336.683 degrees
+;          Declination, dec = 19.1824 degrees
+;
+;       The widely available XEPHEM code gets:
+;                 Hour Angle = 336.683
+;                 Declination = 19.1824
+;
+; REVISION HISTORY:
+;    Written  Chris O'Dell Univ. of Wisconsin-Madison May 2002
+;-
+
+ if N_params() LT 4 then begin
+     print,'Syntax - ALTAZ2HADEC, alt, az, lat, ha, dec'
+     return
+ endif
+ d2r = !dpi/180.0d
+ alt_r  = alt*d2r
+ az_r = az*d2r
+ lat_r = lat*d2r
+
+;******************************************************************************
+; find local HOUR ANGLE (in degrees, from 0. to 360.)
+ ha = atan( -sin(az_r)*cos(alt_r), $
+           -cos(az_r)*sin(lat_r)*cos(alt_r)+sin(alt_r)*cos(lat_r))
+ ha = ha / d2r
+ w = where(ha LT 0.)
+ if w[0] ne -1 then ha[w] = ha[w] + 360.
+ ha = ha mod 360.
+
+; Find declination (positive if north of Celestial Equator, negative if south)
+ sindec = sin(lat_r)*sin(alt_r) + cos(lat_r)*cos(alt_r)*cos(az_r)
+ dec = asin(sindec)/d2r  ; convert dec to degrees
+
+
+END
diff --git a/Code/script_idl_mv/astrolib/aper.pro b/Code/script_idl_mv/astrolib/aper.pro
new file mode 100644
index 0000000000000000000000000000000000000000..940bb0cf0a97d91d5ad986949a0f3a509433cb8b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/aper.pro
@@ -0,0 +1,476 @@
+pro aper,image,xc,yc,mags,errap,sky,skyerr,phpadu,apr,skyradii,badpix, $
+       SETSKYVAL = setskyval,PRINT = print, SILENT = silent, FLUX=flux, $
+       EXACT = exact, Nan = nan, READNOISE = readnoise, MEANBACK = meanback, $
+       CLIPSIG=clipsig, MAXITER=maxiter,CONVERGE_NUM=converge_num, $
+       MINSKY = minsky
+;+
+; NAME:
+;      APER
+; PURPOSE:
+;      Compute concentric aperture photometry (adapted from DAOPHOT) 
+; EXPLANATION:
+;     APER can compute photometry in several user-specified aperture radii.  
+;     A separate sky value is computed for each source using specified inner 
+;     and outer sky radii.   
+;
+; CALLING SEQUENCE:
+;     APER, image, xc, yc, [ mags, errap, sky, skyerr, phpadu, apr, skyrad, 
+;                       badpix, /NAN, /EXACT, /FLUX, PRINT = , /SILENT, 
+;                       /MEANBACK, MINSKY=, SETSKYVAL = ]
+; INPUTS:
+;     IMAGE -  input image array
+;     XC     - vector of x coordinates. 
+;     YC     - vector of y coordinates
+;
+; OPTIONAL INPUTS:
+;     PHPADU - Photons per Analog Digital Units, numeric scalar.  Converts
+;               the data numbers in IMAGE to photon units.  (APER assumes
+;               Poisson statistics.)  
+;     APR    - Vector of up to 12 REAL photometry aperture radii.
+;     SKYRAD - Two element vector giving the inner and outer radii
+;               to be used for the sky annulus.   Ignored if the SETSKYVAL
+;              keyword is set.
+;     BADPIX - Two element vector giving the minimum and maximum value
+;               of a good pixel.   If badpix is not supplied or if BADPIX[0] is
+;               equal to BADPIX[1] then it is assumed that there are no bad
+;               pixels.     Note that fluxes will not be computed for any star
+;               with a bad pixel within the aperture area, but that bad pixels
+;               will be simply ignored for the sky computation.    The BADPIX
+;               parameter is ignored if the /NAN keyword is set.
+;
+; OPTIONAL KEYWORD INPUTS:
+;     CLIPSIG - if /MEANBACK is set, then this is the number of sigma at which 
+;             to clip the background.  Default=3
+;     CONVERGE_NUM:  if /MEANBACK is set then if the proportion of 
+;           rejected pixels is less than this fraction, the iterations stop.  
+;           Default=0.02, i.e., iteration stops if fewer than 2% of pixels 
+;           excluded.
+;     /EXACT -  By default, APER counts subpixels, but uses a polygon 
+;             approximation for the intersection of a circular aperture with
+;             a square pixel (and normalizes the total area of the sum of the
+;             pixels to exactly match the circular area).   If the /EXACT 
+;             keyword, then the intersection of the circular aperture with a
+;             square pixel is computed exactly.    The /EXACT keyword is much
+;             slower and is only needed when small (~2 pixels) apertures are
+;             used with very undersampled data.    
+;     /FLUX - By default, APER uses a magnitude system where a magnitude of
+;               25 corresponds to 1 flux unit.   If set, then APER will keep
+;              results in flux units instead of magnitudes.    
+;     MAXITER if /MEANBACK is set then this is the ceiling on number of 
+;             clipping iterations of the background.  Default=5
+;     /MEANBACK - if set, then the background is computed using the 3 sigma 
+;             clipped mean (using meanclip.pro) rather than using the mode 
+;             computed with mmm.pro.    This keyword is useful for the Poisson 
+;             count regime or where contamination is known  to be minimal.
+;      MINSKY - Integer giving mininum number of sky values to be used with MMM
+;             APER will not compute a flux if fewer valid sky elements are 
+;               within the sky annulus.   Default = 20.
+;     /NAN  - If set then APER will check for NAN values in the image.   /NAN
+;             takes precedence over the BADPIX parameter.   Note that fluxes 
+;             will not be computed for any star with a NAN pixel within the 
+;             aperture area, but that NAN pixels will be simply ignored for 
+;             the sky computation.
+;     PRINT - if set and non-zero then APER will also write its results to
+;               a file aper.prt.   One can specify the output file name by
+;               setting PRINT = 'filename'.
+;     READNOISE - Scalar giving the read noise (or minimum noise for any
+;              pixel.   This value is passed to the procedure mmm.pro when
+;              computing the sky, and is only need for images where
+;              the noise is low, and pixel values are quantized.   
+;     /SILENT -  If supplied and non-zero then no output is displayed to the
+;               terminal.
+;     SETSKYVAL - Use this keyword to force the sky to a specified value 
+;               rather than have APER compute a sky value.    SETSKYVAL 
+;               can either be a scalar specifying the sky value to use for 
+;               all sources, or a 3 element vector specifying the sky value, 
+;               the sigma of the sky value, and the number of elements used 
+;               to compute a sky value.   The 3 element form of SETSKYVAL
+;               is needed for accurate error budgeting.
+;
+; OUTPUTS:
+;     MAGS   -  NAPER by NSTAR array giving the magnitude for each star in
+;               each aperture.  (NAPER is the number of apertures, and NSTAR
+;               is the number of stars).   If the /FLUX keyword is not set, then
+;               a flux of 1 digital unit is assigned a zero point magnitude of 
+;               25.
+;     ERRAP  -  NAPER by NSTAR array giving error for each star.  If a 
+;               magnitude could not be determined then  ERRAP = 9.99 (if in 
+;                magnitudes) or ERRAP = !VALUES.F_NAN (if /FLUX is set).
+;     SKY  -    NSTAR element vector giving sky value for each star in 
+;               flux units
+;     SKYERR -  NSTAR element vector giving error in sky values
+;
+; EXAMPLE:
+;       Determine the flux and error for photometry radii of 3 and 5 pixels
+;       surrounding the position 234.2,344.3 on an image array, im.   Compute
+;       the partial pixel area exactly.    Assume that the flux units are in
+;       Poisson counts, so that PHPADU = 1, and the sky value is already known
+;       to be 1.3, and that the range [-32767,80000] for bad low and bad high
+;       pixels
+;      
+;
+;       IDL> aper, im, 234.2, 344.3, flux, eflux, sky,skyerr, 1, [3,5], -1, $
+;            [-32767,80000],/exact, /flux, setsky = 1.3
+;       
+; PROCEDURES USED:
+;       GETOPT, MMM, PIXWT(), STRN(), STRNUMBER()
+; NOTES:
+;       Reasons that a valid magnitude cannot be computed include the following:
+;      (1) Star position is too close (within 0.5 pixels) to edge of the frame
+;      (2) Less than 20 valid pixels available for computing sky
+;      (3) Modal value of sky could not be computed by the procedure MMM
+;      (4) *Any* pixel within the aperture radius is a "bad" pixel
+;      (5) The total computed flux is negative.     In this case the negative
+;          flux and error are returned.
+;
+;
+;       For the case where the source is fainter than the background, APER will
+;       return negative fluxes if /FLUX is set, but will otherwise give 
+;       invalid data (since negative fluxes can't be converted to magnitudes) 
+; 
+;       APER was modified in June 2000 in two ways: (1) the /EXACT keyword was
+;       added (2) the approximation of the intersection of a circular aperture
+;       with square pixels was improved (i.e. when /EXACT is not used) 
+; REVISON HISTORY:
+;       Adapted to IDL from DAOPHOT June, 1989   B. Pfarr, STX
+;       FLUX keyword added                       J. E. Hollis, February, 1996
+;       SETSKYVAL keyword, increase maxsky       W. Landsman, May 1997
+;       Work for more than 32767 stars           W. Landsman, August 1997
+;       Don't abort for insufficient sky pixels  W. Landsman  May 2000
+;       Added /EXACT keyword                     W. Landsman  June 2000 
+;       Allow SETSKYVAL = 0                      W. Landsman  December 2000 
+;       Set BADPIX[0] = BADPIX[1] to ignore bad pixels W. L.  January 2001     
+;       Fix chk_badpixel problem introduced Jan 01 C. Ishida/W.L. February 2001
+;       Set bad fluxes and error to NAN if /FLUX is set  W. Landsman Oct. 2001 
+;       Remove restrictions on maximum sky radius W. Landsman  July 2003
+;       Added /NAN keyword  W. Landsman November 2004
+;       Set badflux=0 if neither /NAN nor badpix is set  M. Perrin December 2004
+;       Added READNOISE keyword   W. Landsman January 2005
+;       Added MEANBACK keyword   W. Landsman October 2005
+;       Correct typo when /EXACT and multiple apertures used.  W.L. Dec 2005
+;       Remove VMS-specific code W.L. Sep 2006
+;       Add additional keywords if /MEANBACK is set W.L  Nov 2006
+;       Allow negative fluxes if /FLUX is set  W.L.  Mar 2008
+;       Previous update would crash if first star was out of range W.L. Mar 2008
+;       Fix floating equality test for bad magnitudes W.L./J.van Eyken Jul 2009
+;       Added MINSKY keyword W.L. Dec 2011
+;       Don't ever modify input skyrad variable  W. Landsman Aug 2013
+;       Avoid integer overflow for very big images W. Landsman/R. Gutermuth   Mar 2016
+;-
+ COMPILE_OPT IDL2
+ On_error,2
+;             Set parameter limits
+ ;Smallest number of pixels from which the sky may be determined
+ if N_elements(minsky) EQ 0 then minsky = 20   
+ maxsky = 10000         ;Maximum number of pixels allowed in the sky annulus.
+;                                
+if N_params() LT 3 then begin    ;Enough parameters supplied?
+  print, $
+  'Syntax - APER, image, xc, yc, [ mags, errap, sky, skyerr, phpadu, apr, '
+  print,'             skyrad, badpix, /EXACT, /FLUX, SETSKYVAL = ,PRINT=, ]'
+  print,'             /SILENT, /NAN, MINSKY='
+  return
+endif 
+
+ s = size(image)
+ if ( s[0] NE 2 ) then message, $
+       'ERROR - Image array (first parameter) must be 2 dimensional'
+ ncol = s[1] & nrow = s[2]           ;Number of columns and rows in image array
+
+  silent = keyword_set(SILENT)
+
+ if ~keyword_set(nan) then begin
+ if (N_elements(badpix) NE 2) then begin ;Bad pixel values supplied
+GET_BADPIX:  
+   ans = ''
+   print,'Enter low and high bad pixel values, [RETURN] for defaults'
+   read,'Low and high bad pixel values [none]: ',ans
+   if ans EQ  '' then badpix = [0,0] else begin
+   badpix = getopt(ans,'F')
+   if ( N_elements(badpix) NE 2 ) then begin
+        message,'Expecting 2 scalar values',/continue
+        goto,GET_BADPIX
+   endif
+   endelse
+ endif 
+
+ chk_badpix = badpix[0] LT badpix[1]     ;Ignore bad pixel checks?
+ endif
+
+ if ( N_elements(apr) LT 1 ) then begin              ;Read in aperture sizes?
+   apr = fltarr(10)
+   read, 'Enter first aperture radius: ',ap
+   apr[0] = ap
+   ap = 'aper'
+   for i = 1,9 do begin                                                   
+GETAP: 
+      read,'Enter another aperture radius, [RETURN to terminate]: ',ap
+      if ap EQ '' then goto,DONE  
+      result = strnumber(ap,val)
+      if result EQ 1 then apr[i] = val else goto, GETAP   
+   endfor
+DONE: 
+   apr = apr[0:i-1]
+ endif
+
+
+ if N_elements(SETSKYVAL) GT 0 then begin
+     if N_elements( SETSKYVAL ) EQ 1 then setskyval = [setskyval,0.,1.]
+     if N_elements( SETSKYVAL ) NE 3 then message, $
+        'ERROR - Keyword SETSKYVAL must contain 1 or 3 elements'
+     skyrad = [ 0., max(apr) + 1]
+ endif else begin
+   if N_elements(skyradii) NE 2 then begin
+     skyrad = fltarr(2)
+     read,'Enter inner and outer sky radius (pixel units): ',skyrad
+  endif else skyrad = float(skyradii)
+  endelse
+
+ if ( N_elements(phpadu) LT 1 ) then $ 
+   read,'Enter scale factor in Photons per Analog per Digital Unit: ',phpadu
+
+ Naper = N_elements( apr )                        ;Number of apertures
+ Nstars = min([ N_elements(xc), N_elements(yc) ])  ;Number of stars to measure
+
+ ms = strarr( Naper )       ;String array to display mag for each aperture
+ if keyword_set(flux) then $
+          fmt = '(F8.1,1x,A,F7.1)' else $           ;Flux format
+          fmt = '(F9.3,A,F5.3)'                  ;Magnitude format
+ fmt2 = '(I5,2F8.2,F7.2,1x,3A,3(/,28x,4A,:))'       ;Screen format
+ fmt3 = '(I4,5F8.2,1x,6A,2(/,44x,9A,:))'            ;Print format
+
+ mags = fltarr( Naper, Nstars) & errap = mags           ;Declare arrays
+ sky = fltarr( Nstars )        & skyerr = sky     
+ area = !PI*apr*apr                 ;Area of each aperture
+
+ if keyword_set(EXACT) then begin
+      bigrad = apr + 0.5
+      smallrad = apr/sqrt(2) - 0.5 
+ endif
+     
+
+ if N_elements(SETSKYVAL) EQ 0 then begin
+
+     rinsq =  (skyrad[0]> 0.)^2 
+     routsq = skyrad[1]^2
+ endif 
+
+ if keyword_set(PRINT) then begin      ;Open output file and write header info?
+   if size(PRINT,/TNAME) NE 'STRING'  then file = 'aper.prt' $
+                                   else file = print
+   message,'Results will be written to a file ' + file,/INF
+   openw,lun,file,/GET_LUN
+   printf,lun,'Program: APER: '+ systime(), '   User: ', $
+      getenv('USER'),'  Host: ',getenv('HOST')
+   for j = 0, Naper-1 do printf,lun, $
+               format='(a,i2,a,f4.1)','Radius of aperture ',j,' = ',apr[j]
+   if N_elements(SETSKYVAL) EQ 0  then begin
+   printf,lun,f='(/a,f4.1)','Inner radius for sky annulus = ',skyrad[0]
+   printf,lun,f='(a,f4.1)', 'Outer radius for sky annulus = ',skyrad[1]
+   endif else printf,lun,'Sky values fixed at ', strtrim(setskyval[0],2)
+   if keyword_set(FLUX) then begin
+       printf,lun,f='(/a)', $
+           'Star   X       Y        Sky   SkySig    SkySkw   Fluxes'
+      endif else printf,lun,f='(/a)', $
+           'Star   X       Y        Sky   SkySig    SkySkw   Magnitudes'
+ endif
+ print = keyword_set(PRINT)
+
+;         Print header
+ if ~SILENT then begin
+    if KEYWORD_SET(FLUX) then begin
+       print, format="(/1X,'Star',5X,'X',7X,'Y',6X,'Sky',8X,'Fluxes')"
+    endif else print, $ 
+       format="(/1X,'Star',5X,'X',7X,'Y',6X,'Sky',8X,'Magnitudes')" 
+ endif
+
+;  Compute the limits of the submatrix.   Do all stars in vector notation.
+
+ lx = long(xc-skyrad[1]) > 0           ;Lower limit X direction
+ ux = long(xc+skyrad[1]) < (ncol-1)    ;Upper limit X direction
+ nx = ux-lx+1                         ;Number of pixels X direction
+ ly = long(yc-skyrad[1]) > 0           ;Lower limit Y direction
+ uy = long(yc+skyrad[1]) < (nrow-1);   ;Upper limit Y direction
+ ny = uy-ly +1                        ;Number of pixels Y direction
+ dx = xc-lx                         ;X coordinate of star's centroid in subarray
+ dy = yc-ly                         ;Y coordinate of star's centroid in subarray
+
+ edge = (dx-0.5) < (nx+0.5-dx) < (dy-0.5) < (ny+0.5-dy) ;Closest edge to array
+ badstar = ((xc LT 0.5) or (xc GT ncol-1.5) $  ;Stars too close to the edge
+        or (yc LT 0.5) or (yc GT nrow-1.5))
+;
+ badindex = where( badstar, Nbad)              ;Any stars outside image
+ if ( Nbad GT 0 ) then message, /INF, $
+      'WARNING - ' + strn(nbad) + ' star positions outside image'
+      if keyword_set(flux) then begin 
+          badval = !VALUES.F_NAN
+	  baderr = badval
+      endif else begin 
+          badval = 99.999
+	  baderr = 9.999
+      endelse	  	  
+ 
+ for i = 0L, Nstars-1 do begin           ;Compute magnitudes for each star
+   apmag = replicate(badval, Naper)   & magerr = replicate(baderr, Naper) 
+   skymod = 0. & skysig = 0. &  skyskw = 0.  ;Sky mode sigma and skew
+   if badstar[i] then goto, BADSTAR         
+   error1=apmag   & error2 = apmag   & error3 = apmag
+
+   rotbuf = image[ lx[i]:ux[i], ly[i]:uy[i] ] ;Extract subarray from image
+;  RSQ will be an array, the same size as ROTBUF containing the square of
+;      the distance of each pixel to the center pixel.
+
+ 
+    dxsq = ( findgen( nx[i] ) - dx[i] )^2
+    rsq = fltarr( nx[i], ny[i], /NOZERO )
+   for ii = 0, ny[i]-1 do rsq[0,ii] = dxsq + (ii-dy[i])^2
+
+
+ if keyword_set(exact) then begin 
+       nbox = lindgen(nx[i]*ny[i])
+       xx = reform( (nbox mod nx[i]), nx[i], ny[i])
+       yy = reform( (nbox/nx[i]),nx[i],ny[i])
+       x1 = abs(xx-dx[i]) 
+       y1 = abs(yy-dy[i])
+  endif else begin 
+   r = sqrt(rsq) - 0.5    ;2-d array of the radius of each pixel in the subarray
+ endelse
+
+;  Select pixels within sky annulus, and eliminate pixels falling
+;       below BADLO threshold.  SKYBUF will be 1-d array of sky pixels
+ if N_elements(SETSKYVAL) EQ 0 then begin
+
+ skypix = ( rsq GE rinsq ) and ( rsq LE routsq )
+ if keyword_set(nan) then skypix = skypix and finite(rotbuf) $
+ else if chk_badpix then skypix = skypix and ( rotbuf GT badpix[0] ) and $
+        (rotbuf LT badpix[1] )
+ sindex =  where(skypix, Nsky) 
+ Nsky =   Nsky < maxsky   ;Must be less than MAXSKY pixels
+ if ( nsky LT minsky ) then begin                       ;Sufficient sky pixels?
+    if ~silent then $
+        message,'There aren''t enough valid pixels in the sky annulus.',/con
+    goto, BADSTAR
+ endif
+  skybuf = rotbuf[ sindex[0:nsky-1] ]     
+
+  if keyword_set(meanback) then $
+   meanclip,skybuf,skymod,skysig, $ 
+         CLIPSIG=clipsig, MAXITER=maxiter, CONVERGE_NUM=converge_num  else $
+     mmm, skybuf, skymod, skysig, skyskw, readnoise=readnoise,minsky=minsky
+           
+ 
+
+;  Obtain the mode, standard deviation, and skewness of the peak in the
+;      sky histogram, by calling MMM.
+
+ skyvar = skysig^2    ;Variance of the sky brightness
+ sigsq = skyvar/nsky  ;Square of standard error of mean sky brightness
+
+;If the modal sky value could not be determined, then all apertures for this
+; star are bad
+
+ if ( skysig LT 0.0 ) then goto, BADSTAR 
+
+ skysig = skysig < 999.99      ;Don't overload output formats
+ skyskw = skyskw >(-99)<999.9
+ endif else begin
+    skymod = setskyval[0]
+    skysig = setskyval[1]
+    nsky = setskyval[2]
+    skyvar = skysig^2
+    sigsq = skyvar/nsky
+    skyskw = 0
+endelse
+
+
+
+ for k = 0,Naper-1 do begin      ;Find pixels within each aperture
+
+   if ( edge[i] GE apr[k] ) then begin    ;Does aperture extend outside the image?
+     if keyword_set(EXACT) then begin
+       mask = fltarr(nx[i],ny[i])
+       good = where( ( x1 LT smallrad[k] ) and (y1 LT smallrad[k] ), Ngood)
+       if Ngood GT 0 then mask[good] = 1.0
+       bad = where(  (x1 GT bigrad[k]) or (y1 GT bigrad[k] ))   ;Fix 05-Dec-05
+       mask[bad] = -1
+
+       gfract = where(mask EQ 0.0, Nfract) 
+       if Nfract GT 0 then mask[gfract] = $
+		PIXWT(dx[i],dy[i],apr[k],xx[gfract],yy[gfract]) > 0.0
+       thisap = where(mask GT 0.0)
+       thisapd = rotbuf[thisap]
+       fractn = mask[thisap]
+     endif else begin
+;
+       thisap = where( r LT apr[k] )   ;Select pixels within radius
+       thisapd = rotbuf[thisap]
+       thisapr = r[thisap]
+       fractn = (apr[k]-thisapr < 1.0 >0.0 ) ;Fraction of pixels to count
+       full = fractn EQ 1.0
+       gfull = where(full, Nfull)
+       gfract = where(1 - full)
+       factor = (area[k] - Nfull ) / total(fractn[gfract])
+      fractn[gfract] = fractn[gfract]*factor
+    endelse
+
+;     If the pixel is bad, set the total counts in this aperture to a large
+;        negative number
+;
+   if keyword_set(NaN) then $
+      badflux =  min(finite(thisapd)) EQ 0   $
+   else if chk_badpix then begin
+     minthisapd = min(thisapd, max = maxthisapd)
+     badflux = (minthisapd LE badpix[0] ) or ( maxthisapd GE badpix[1])
+   endif else badflux = 0
+  
+   if ~badflux then $ 
+                 apmag[k] = total(thisapd*fractn) ;Total over irregular aperture
+  endif
+endfor ;k
+   if keyword_set(flux) then g = where(finite(apmag), Ng)  else $
+                             g = where(abs(apmag - badval) GT 0.01, Ng)
+   if Ng GT 0 then begin 
+  apmag[g] = apmag[g] - skymod*area[g]  ;Subtract sky from the integrated brightnesses
+
+; Add in quadrature 3 sources of error: (1) random noise inside the star 
+; aperture, including readout noise and the degree of contamination by other 
+; stars in the neighborhood, as estimated by the scatter in the sky values 
+; (this standard error increases as the square root of the area of the
+; aperture); (2) the Poisson statistics of the observed star brightness;
+; (3) the uncertainty of the mean sky brightness (this standard error
+; increases directly with the area of the aperture).
+
+   error1[g] = area[g]*skyvar   ;Scatter in sky values
+   error2[g] = (apmag[g] > 0)/phpadu  ;Random photon noise 
+   error3[g] = sigsq*area[g]^2  ;Uncertainty in mean sky brightness
+   magerr[g] = sqrt(error1[g] + error2[g] + error3[g])
+
+  if ~keyword_set(FLUX) then begin
+    good = where (apmag GT 0.0, Ngood)     ;Are there any valid integrated fluxes?
+    if ( Ngood GT 0 ) then begin               ;If YES then compute errors
+      magerr[good] = 1.0857*magerr[good]/apmag[good]   ;1.0857 = log(10)/2.5
+      apmag[good] =  25.-2.5*alog10(apmag[good])  
+   endif
+ endif  
+ endif
+
+ BADSTAR:   
+ 
+;Print out magnitudes for this star
+
+ for ii = 0,Naper-1 do $              ;Concatenate mags into a string
+
+    ms[ii] = string( apmag[ii],'+-',magerr[ii], FORM = fmt)
+   if PRINT then  printf,lun, $      ;Write results to file?
+      form = fmt3,  i, xc[i], yc[i], skymod, skysig, skyskw, ms
+   if ~SILENT then print,form = fmt2, $       ;Write results to terminal?
+          i,xc[i],yc[i],skymod,ms
+
+   sky[i] = skymod    &  skyerr[i] = skysig  ;Store in output variable
+   mags[0,i] = apmag  &  errap[0,i]= magerr
+ endfor                                              ;i
+
+ if PRINT then free_lun, lun             ;Close output file
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/arcbar.pro b/Code/script_idl_mv/astrolib/arcbar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b331d29c44f576baeb412474ccf922230956169f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/arcbar.pro
@@ -0,0 +1,155 @@
+Pro arcbar, hdr, arclen, LABEL = label, SIZE = size, THICK = thick, DATA =data, $
+            COLOR = color, POSITION = position, NORMAL = normal, $
+            SECONDS=SECONDS, FONT=font
+;+
+; NAME:
+;       ARCBAR
+; PURPOSE:
+;       Draw an arc bar on an image showing the astronomical plate scale
+;
+; CALLING SEQUENCE:
+;       ARCBAR, hdr, arclen,[  COLOR= , /DATA, LABEL= , /NORMAL, POSITION=, 
+;                              /SECONDS, SIZE=, THICK=, FONT= ]
+;
+; INPUTS:
+;       hdr - image FITS header with astrometry, string array
+; OPTIONAL INPUT:
+;       arclen - numeric scalar giving length of bar in arcminutes (default)
+;               or arcseconds (if /SECONDS is set).    Default is 1 arcminute 
+;
+; OPTIONAL KEYWORD INPUTS:
+;       COLOR - name  or integer scalar specifying the color to draw the arcbar 
+;               See cgColor for a list of available color names
+;       /DATA - if set and non-zero, then the POSITION keyword and the arc 
+;               length is given in data units
+;       LABEL - string giving user defined label for bar.  Default label is size
+;               of bar in arcminutes
+;       /NORMAL - if this keyword is set and non-zero, then POSITION is given in
+;               normalized units
+;       POSITION - 2 element vector giving the (X,Y) position in device units 
+;               (or normalized units if /NORMAL is set, or data units if /DATA
+;               is set) at which to place the  scale bar.   If not supplied, 
+;               then the user will be prompted to place the cursor at the 
+;               desired position
+;       /SECONDS - if set, then arlen is specified in arcseconds rather than
+;               arcminutes
+;       SIZE  - scalar specifying character size of label, default = 1.0
+;       THICK -  Character thickness of the label, default = !P.THICK
+;       FONT - scalar font graphics keyword (-1,0 or 1) for text
+;
+; EXAMPLE:
+;       Suppose one has an image array, IM, and FITS header, HDR, with 
+;       astrometry.    Display the image and place a 3' arc minute scale bar 
+;       at position 300,200 of the current image window
+;
+;       IDL> cgimage, IM, /scale,/save   ;Use /SAVE to set data coordinates
+;       IDL> arcbar, HDR, 3, pos = [300,200],/data
+;
+; RESTRICTIONS:
+;       When using using a device with scalable pixels (e.g. postscript)
+;       the data coordinate system must be established before calling ARCBAR.
+;       If data coordinates are not set, then ARCBAR assumes that the displayed
+;       image size is given by the NAXIS1 keyword in the FITS header.
+; PROCEDURE CALLS:
+;       AD2XY, EXTAST, GSSSADXY, SXPAR(), SETDEFAULTVALUE, cgPlot, cgText
+; REVISON HISTORY:
+;       written by L. Taylor (STX) from ARCBOX (Boothman)
+;       modified for Version 2 IDL,                     B. Pfarr, STX, 4/91
+;       New ASTROMETRY structures               W.Landsman,  HSTX, Jan 94
+;       Recognize a GSSS header                 W. Landsman June 94
+;       Added /NORMAL keyword                   W. Landsman Feb. 96
+;       Use NAXIS1 for postscript if data coords not set,  W. Landsman Aug 96
+;       Fixed typo for postscript W. Landsman   Oct. 96
+;       Account for zeropoint offset in postscript  W. Landsman   Apr 97
+;       Added /DATA, /SECONDS keywords   W. Landsman    July 1998
+;       Use device-independent label offset  W. Landsman   August 2001
+;       Allow font keyword to be passed.  T. Robishaw Apr. 2006
+;       Remove obsolete TVCURSOR command  W. Landsman Jul 2007
+;       Use Coyote Graphics W. Landsman  February 2011
+;       Fix problem using data coordinates when not in postscript 
+;                 W. Landsman January 2013
+;-
+;
+ compile_opt idl2
+ On_error,2                                  ;Return to caller
+
+ if N_params() LT 1 then begin
+      print, 'Syntax - ARCBAR, hdr,[ arclen, COLOR= '
+      print, '         /DATA, LABEL=, /NORM, POS=, /SECONDS, SIZE=, THICK= ]'
+      return
+ endif
+
+ extast, hdr, bastr, noparams   ;extract astrom params in deg.
+
+ if N_params() LT 2 then arclen = 1      ;default size = 1 arcmin
+
+ setdefaultvalue, size, 1.0
+ setdefaultvalue, thick, !P.THICK
+ setdefaultvalue, font, !P.FONT
+
+ a = bastr.crval[0]
+ d = bastr.crval[1]
+ if keyword_set(seconds) then factor = 3600.0d else factor = 60.0
+ d1 = d + (1/factor)             ;compute x,y of crval + 1 arcmin
+
+ proj = strmid(bastr.ctype[0],5,3)
+  
+ case proj of 
+        'GSS': gsssadxy, bastr, [a,a], [d,d1], x, y
+        else:  ad2xy, [a,a], [d,d1], bastr, x, y 
+ endcase
+
+ dmin = sqrt( (x[1]-x[0])^2 + (y[1]-y[0])^2 ) ;det. size in pixels of 1 arcmin
+
+ if ((!D.FLAGS AND 1) EQ 1) || keyword_set(data) then begin          ;Device have scalable pixels?
+        if !X.s[1] NE 0 then begin
+                dmin = convert_coord( dmin, 0, /DATA, /TO_DEVICE) - $ 
+                       convert_coord(    0, 0, /DATA, /TO_DEVICE)  ;Fixed Apr 97
+                dmin = dmin[0]
+        endif else dmin = dmin/sxpar(hdr, 'NAXIS1' )     ;Fixed Oct. 96
+ endif 
+
+ dmini2 = round(dmin * arclen)
+
+ if ~keyword_set( POSITION) then begin
+          print,'Position the cursor where you want the bar to begin'
+          print,'Hit right mouse button when ready'
+          cursor,xi,yi,1,/device
+ endif else begin 
+        if keyword_set(NORMAL) then begin
+                posn = convert_coord(position,/NORMAL, /TO_DEVICE) 
+                xi = posn[0] & yi = posn[1]
+        endif else if keyword_set(DATA) then begin
+                posn = convert_coord(position,/DATA, /TO_DEVICE) 
+                xi = posn[0] & yi = posn[1]
+        endif else begin
+                xi = position[0]   & yi = position[1]
+        endelse         
+ endelse
+
+ xf = xi + dmini2
+ dmini3 = dmini2/10             ;Height of vertical end bars = total length/10.
+
+ cgPlots,[xi,xf],[yi,yi], COLOR=color, /DEV, THICK=thick
+ cgPlots,[xf,xf],[ yi+dmini3, yi-dmini3 ], COLOR=color, /DEV, THICK=thick
+ cgPlots,[xi,xi],[ yi+dmini3, yi-dmini3 ], COLOR=color, /DEV, THICK=thick
+
+ if ~keyword_set(Seconds) then begin
+ if (!D.NAME EQ 'PS') && (FONT EQ 0) then $        ;Postscript Font?
+        arcsym='!9'+string(162B)+'!X' else arcsym = "'" 
+ endif else begin
+ if (!D.NAME EQ 'PS') && (FONT EQ 0) then $        ;Postscript Font?
+        arcsym = '!9'+string(178B)+'!X' else arcsym = "''" 
+ endelse
+ if ~keyword_set( LABEL) then begin
+     if (arclen LT 1) then arcstr = string(arclen,format='(f4.2)') $
+        else arcstr = string(arclen)
+     label = strtrim(arcstr,2) + arcsym 
+ endif
+
+ yoffset = round(!D.Y_CH_SIZE/2.)
+ cgTEXT,(xi+xf)/2, yi+yoffset, label, SIZE = size,COLOR=color,/DEV,  $
+       alignment=0.5, CHARTHICK=thick, FONT=font
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/arrows.pro b/Code/script_idl_mv/astrolib/arrows.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f1c785420831bfaaac74552794cb0cef3b44a130
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/arrows.pro
@@ -0,0 +1,138 @@
+pro arrows,h,xcen,ycen,thick=thick,charsize=charsize,arrowlen=arrowlen, $
+             color=color,NotVertex=NotVertex,Normal = normal,Data=data,font=font
+;+
+; NAME:
+;      ARROWS
+; PURPOSE:
+;      To display "weathervane" directional arrows on an astronomical image 
+; EXPLANATION:
+;      Overlays a graphic showing orientation of North and East.
+;
+; CALLING SEQUENCE:
+;      ARROWS,h, [ xcen, ycen, ARROWLEN= , CHARSIZE=  COLOR= , /DATA
+;                              FONT=, /NORMAL, /NOTVERTEX, THICK=  ]
+;
+; INPUTS:
+;       h - FITS header array, must include astrometry
+;
+; OPTIONAL INPUTS:
+;       xcen,ycen - numeric scalars, specifying the center position of
+;		arrows.   Position in device units unless the /NORMALIZED 
+;		keyword is specified.   If not supplied, then ARROWS
+;		will prompt for xcen and ycen
+;
+; OPTIONAL KEYWORD INPUTS:
+;       arrowlen  - length of arrows in terms of normal Y size of vector-drawn
+;                     character,  default  = 3.5, floating point scalar
+;       charsize  - character size, default = 2.0, floating point scalar
+;       color     -  color name or number for the arrows and NE letters.  See
+;                 cgCOLOR() for a list of color names.                    
+;       Data - if this keyword is set and nonzero, the input center (xcen,
+;                 ycen) is understood to be in data coordinates
+;       font - IDL vector font number (1-20) to use to display NE letters.
+;                 For example, set font=13 to use complex italic font.
+;       NotVertex - Normally (historically) the specified xcen,ycen indicated
+;                   the position of the vertex of the figure.  If this
+;                   keyword is set, the xcen,ycen coordinates refer to a sort
+;                   of 'center of mass' of the figure.  This allows the
+;                   figure to always appear with the area irregardless of
+;                   the rotation angle.
+;       Normal - if this keyword is set and nonzero, the input center 
+;                (xcen,ycen) is taken to be in normalized coordinates.   The
+;                default is device coordinates.
+;       thick     - line thickness, default = 2.0, floating point scalar
+; OUTPUTS:
+;       none
+; EXAMPLE:
+;       Draw a weathervane at (400,100) on the currently active window, 
+;       showing the orientation of the image associated with a FITS header, hdr
+;
+;       IDL> arrows, hdr, 400, 100
+;
+; METHOD:
+;       Uses EXTAST to EXTract ASTrometry from the FITS header.   The 
+;       directions of North and East are computed and the procedure
+;       ONE_ARROW called to create the "weathervane".
+;
+; PROCEDURES USED:
+;       GETROT - Computes rotation from the FITS header
+;       ONE_ARROW - Draw a labeled arrow	
+;       ZPARCHECK
+; REVISON HISTORY:
+;       written by B. Boothman 2/5/86 
+;       Recoded with new procedures ONE_ARROW, ONE_RAY.  R.S.Hill,HSTX,5/20/92
+;       Added separate determination for N and E arrow to properly display
+;         arrows irregardless of handedness or other peculiarities and added
+;         /NotVertex keyword to improve positioning of figure. E.Deutsch 1/10/93
+;       Added /DATA and /NORMAL keywords W. Landsman      July 1993
+;       Recognize GSSS header    W. Landsman       June 1993
+;       Added /FONT keyword W. Landsman           April 1995
+;       Modified to work correctly for COLOR=0  J.Wm.Parker, HITC   1995 May 25
+;       Work correctly for negative CDELT values   W. Landsman   Feb. 1996
+;       Use GETROT to compute rotation   W. Landsman    June 2003
+;       Restored /NotVertex keyword which was not working after June 2003 change
+;                  W. Landsman  January 2004
+;-
+
+  On_error,2                            ;Return to caller
+
+  if (N_params() LT 1) then begin 
+    print,'Syntax - ' + $
+             'ARROWS, hdr, [ xcen, ycen, ARROWLEN= , CHARSIZE=  COLOR= , /DATA'
+    print,'                        FONT=, /NORMAL, /NotVertex, THICK=  ]'
+    print,'         hdr - FITS header with astrometry'
+    return
+  endif else zparcheck,'ARROWS',h,1,7,1,'FITS header array'
+
+  if ( N_params() LT 3 ) then $
+    read,'Enter x, y values for center of arrows: ',xcen,ycen
+
+  setdefaultvalue, thick, 2.0
+  setdefaultvalue, charsize, 2.0
+  setdefaultvalue, arrowlen, 3.5
+  setdefaultvalue, NotVertex, 0
+
+;  Derive Position Angles for North and East separately
+
+  getrot,h,npa, cdelt,/SILENT
+  sgn = 1 - 2*(cdelt[0]*cdelt[1] GT 0) 
+  epa = npa + sgn*90   
+
+;  Make arrows reasonable size depending on device
+
+  arrowlen_dev = arrowlen*!D.y_ch_size
+  arrowsize = [arrowlen_dev, arrowlen_dev/3.5, 35.0]  ; See one_arrow.pro
+
+  if keyword_set( NORMAL) then begin
+	newcen = convert_coord( xcen, ycen, /NORMAL, /TO_DEVICE)
+        xcent = newcen[0]
+        ycent = newcen[1]
+  endif else if keyword_set( DATA) then begin
+	newcen = convert_coord( xcen, ycen, /DATA, /TO_DEVICE)
+        xcent = newcen[0]
+        ycent = newcen[1]
+  endif else begin
+         xcent=xcen & ycent=ycen
+  endelse 
+
+;  Adjust Center to 'Center of Mass' if NotVertex set
+ if NotVertex then begin
+    rot = npa/!RADEG
+    dRAdX = cdelt[0]*cos(rot)
+    dRAdY = cdelt[1]*sin(rot)
+    dDECdX = cdelt[0]*sin(rot) 
+    dDECdY = cdelt[1]*cos(rot)
+    RAnorm = sqrt( dRAdX^2 + dRAdY^2 )
+    DECnorm = sqrt(dDECdX^2 + dDECdY^2 )
+    xcent = xcen - (dRAdX+dDECdX)/2/RAnorm*arrowsize[0]
+    ycent = ycen - (dRAdY+dDECdY)/2/DECnorm*arrowsize[0]
+    endif
+
+;  Draw arrows
+  one_arrow, xcent, ycent,  90+NPA, 'N', font= font, $
+    charsize=charsize, thick=thick, color=color, arrowsize=arrowsize
+  one_arrow, xcent, ycent, 90+EPA, 'E', font = font, $
+    charsize=charsize, thick=thick, color=color, arrowsize=arrowsize
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/asinh.pro b/Code/script_idl_mv/astrolib/asinh.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0083d4645276de605bfea43f59ad3ed181ccade6
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/asinh.pro
@@ -0,0 +1,40 @@
+function asinh, x
+;+
+; NAME:
+;     ASINH
+; PURPOSE:
+;     Return the inverse hyperbolic sine of the argument
+; EXPLANATION:
+;     The inverse hyperbolic sine is used for the calculation of asinh 
+;     magnitudes, see Lupton et al. (1999, AJ, 118, 1406)
+;
+; CALLING SEQUENCE
+;     result = asinh( x) 
+; INPUTS:
+;     X - hyperbolic sine, numeric scalar or vector or multidimensional array 
+;        (not complex) 
+;
+; OUTPUT:
+;     result - inverse hyperbolic sine, same number of elements as X
+;              double precision if X is double, otherwise floating pt.
+;
+; METHOD:
+;     Expression given in  Numerical Recipes, Press et al. (1992), eq. 5.6.7 
+;     Note that asinh(-x) = -asinh(x) and that asinh(0) = 0. and that
+;     if y = asinh(x) then x = sinh(y).     
+;
+; REVISION HISTORY:
+;     Written W. Landsman                 February, 2001
+;     Work for multi-dimensional arrays  W. Landsman    August 2002
+;     Simplify coding, and work for scalars again  W. Landsman October 2003
+;-
+ On_error,2
+ 
+ y = alog( abs(x) + sqrt( x^2 + 1.0) )
+
+ index = where(x LT 0 ,count)
+ if count GT 0 then y[index] = -y[index]
+
+ return, y
+
+ end
diff --git a/Code/script_idl_mv/astrolib/astdisp.pro b/Code/script_idl_mv/astrolib/astdisp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1521c0585e739e3b8f2aca963bf3220bb4313315
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/astdisp.pro
@@ -0,0 +1,98 @@
+pro AstDisp, x, y, ra, dec, DN, Coords=Coords, silent=silent
+;+
+; NAME:
+;	ASTDISP
+;
+; PURPOSE:
+;	Print astronomical and pixel coordinates in a standard format
+; EXPLANATION:
+;	This procedure (ASTrometry DISPlay) prints the astronomical and
+;	pixel coordinates in a standard format.  X,Y must be supplied.  RA,DEC
+;	may also be supplied, and a data number (DN) may also be 
+;	supplied.   With use of the Coords= keyword, a string containing the 
+;	formatted data can be returned in addition or instead (with /silent) 
+;	of printing.
+;
+; CALLING SEQUENCE:
+;	ASTDISP, x, y, [Ra, Dec, DN, COORD = , /SILENT ]
+;
+; INPUT:
+;	X  - The X pixel coordinate(s), scalar or vector
+;	Y  - The Y pixel coordinate(s), scalar or vector
+;
+; OPTIONAL INPUTS:
+;	RA -  Right Ascension in *degrees*, scalar or vector
+;	DEC - DEClination in *degrees*, scalar or vector (if RA is supplied, DEC must also be supplied)
+;	DN -  Data Number or Flux values
+;
+;	Each of the inputs X,Y, RA, DEC, DN should have the same number of 
+;		elements
+; OPTIONAL INPUT KEYWORDS:
+;	SILENT    Prevents printing.  Only useful when used with Coords=
+; OUTPUT:
+;	Printed positions in both degrees and sexagesimal format
+;	All passed variables remain unchanged
+; OPTIONAL KEYWORD OUTPUT:
+;	COORDS    Returns the formatted coordinates in a string
+; PROCEDURES CALLED:
+;	ADSTRING - used to format the RA and Dec
+; HISTORY:
+;	10-AUG-90 Version 1 written by Eric W. Deutsch
+;	20-AUG-91 Converted to standard header.  Vectorized Code.  E. Deutsch
+;	20-NOV-92 Added Coords= and /silent.  E.Deutsch
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+  On_error,2
+
+  arg = N_params()
+  if (arg lt 2) then begin
+    print,'Call: IDL> AstDisp,x_pixel,y_pixel,[RA,DEC],[DN],[/silent,coords=]'
+    print,'e.g.: IDL> AstDisp,x,y,ra,dec'
+    return
+    endif
+
+  if (arg eq 3) then message,'ERROR - Both RA and Dec values must be supplied'
+
+  silent = keyword_set(SILENT)
+
+; X and Y must be supplied
+
+  hdr = '    X        Y'
+  fmt = '(f8.2,1x,f8.2'
+  if (arg le 2) then begin & type=0 & goto,PRN & endif
+
+; Ra and Dec can be optionally supplied
+
+  hdr = hdr+'         RA       DEC           RA           DEC'
+  fmt = fmt+',2x,F9.4,1x,F9.4,2x,A'
+  if (arg le 4) then begin & type=1 & goto,PRN & endif
+
+; A data number can be optionally supplied
+
+  hdr = hdr+'           DN'
+  fmt = fmt+',3x,f9.3'
+  type = 2
+
+PRN:
+  if not SILENT then print,hdr
+  Coords = strarr( N_elements(x)+1 )
+  Coords[0] = hdr
+
+  for i = 0, N_elements(x)-1 do begin
+
+	case type of 
+
+	0: out = string(format=fmt+')',x[i],y[i],/print)
+	1: out = string(format=fmt+')',x[i],y[i],ra[i],dec[i], $
+		 adstring(ra[i],dec[i],2),/print)
+	2: out = string(format=fmt+')',x[i],y[i],ra[i],dec[i], $
+		 adstring(ra[i],dec[i],2),DN[i],/print)
+	endcase
+
+	if not SILENT then print,out
+	Coords[i+1] = out
+
+   endfor
+
+  return
+ end
diff --git a/Code/script_idl_mv/astrolib/astro.pro b/Code/script_idl_mv/astrolib/astro.pro
new file mode 100644
index 0000000000000000000000000000000000000000..994a68d1079e99119a7be017b383492109c075bc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/astro.pro
@@ -0,0 +1,175 @@
+pro astro, selection, EQUINOX = equinox, FK4 = FK4   
+;+
+; NAME:
+;     ASTRO
+; PURPOSE:
+;     Interactive utility for precession and coordinate conversion.
+;
+; CALLING SEQUENCE:
+;     ASTRO, [ selection, EQUINOX =, /FK4]
+;
+; OPTIONAL INPUT:
+;      SELECTION - Scalar Integer (0-6) giving the the particular astronomical
+;              utility to be used.  (0) Precession, (1) RA, Dec (2000) to Galactic 
+;              coordinates, (2) Galactic to RA,Dec (2000) (3) RA,Dec (2000) to 
+;              Ecliptic, (4) Ecliptic to RA, Dec, (5) Ecliptic to Galactic, (6) Galactic
+;              to Ecliptic.   Program will prompt for SELECTION if this 
+;              parameter is omitted.
+;
+; OPTIONAL KEYWORD INPUT:
+;       EQUINOX - numeric scalar specifying the equinox to use when converting 
+;               between celestial and other coordinates.    If not supplied, 
+;               then the RA and Dec will be assumed to be in EQUINOX J2000.   
+;               This keyword is ignored by the precession utility.   For 
+;               example, to convert from RA and DEC (J1975) to Galactic 
+;               coordinates:
+;
+;               IDL> astro, 1, E=1975
+;       /FK4 - If this keyword is set and nonzero, then calculations are done
+;              in the FK4 system.    For example, to convert from RA and Dec
+;              (B1975) to Galactic coordinates
+;
+;               IDL> astro,1, E=1975,/FK4 
+; METHOD:
+;      ASTRO uses PRECESS to compute precession, and EULER to compute
+;      coordinate conversions.   The procedure GET_COORDS is used to
+;      read the coordinates, and ADSTRING to format the RA,Dec output.
+;
+; NOTES:
+;      (1) ASTRO temporarily sets !QUIET to suppress compilation messages and
+;      keep a pretty screen display.   
+;
+;      (2) ASTRO was changed in December 1998 to use J2000 as the default 
+;      equinox, **and may be incompatible with earlier calls.***
+;      
+;      (3) A nice online page for coordinate conversions is available at
+;       http://heasarc.gsfc.nasa.gov/cgi-bin/Tools/convcoord/convcoord.pl   
+; PROCEDURES USED:
+;      Procedures: GET_COORDS, EULER       Function: ADSTRING
+; REVISION HISTORY
+;      Written, W. Landsman November 1987
+;      Code cleaned up       W. Landsman   October 1991
+;      Added Equinox keyword, call to GET_COORDS, W. Landsman   April, 1992
+;      Allow floating point equinox input J. Parker/W. Landsman  July 1996
+;      Make FK5 the default, add FK4 keyword
+;-
+ On_error,2                    ;Return to caller
+
+ input_type =   [0,0,1,0,2,2,1]     ;0= RA,Dec  1= Galactic   2 = Ecliptic
+ output_type =  [0,1,0,2,0,1,2]        
+
+ sv_quiet = !quiet & !quiet = 1 ;Don't display compiled procedures
+
+
+ if keyword_set(FK4) then begin
+       if not keyword_set(EQUINOX) then equinox = 1950
+       fk = 'B'
+       ref_year = 1950  
+       yeari = 1950 & yearf = 1950
+ endif else begin
+       if not keyword_set(EQUINOX) then equinox = 2000
+       fk = 'J'  
+       ref_year = 2000 
+       yeari = 2000 & yearf = 2000
+ endelse
+      eqname = fk + string(equinox,f='(f6.1)') + ')'
+
+ select = ['(0) Precession: (RA, Dec)',                  $
+           '(1) Conversion: (RA, Dec ' + eqname + ' --> Galactic', $
+           '(2) Conversion: Galactic --> (RA, Dec ' + eqname, $
+           '(3) Conversion: (RA, Dec ' + eqname + ' --> Ecliptic', $
+           '(4) Conversion: Ecliptic --> (RA, Dec ' + eqname, $
+           '(5) Conversion: Ecliptic --> Galactic',       $
+           '(6) Conversion: Galactic --> Ecliptic']
+
+ npar = N_params()       
+
+ SELECTOR: if (npar EQ 0 ) then begin
+
+        print,'Select astronomical utility'
+        for i = 0,6 do print, select[i]
+        selection = 0
+        print,' '
+        read,'Enter Utility Number: ',selection 
+        print,' '
+
+     endif
+
+ if ( selection LT 0 ) or ( selection GT 6 ) then begin
+
+       print,selection,' is not an available option'
+       npar = 0
+       goto, SELECTOR
+
+ endif
+
+ print, select[selection]
+
+ if keyword_set(EQUINOX) and (input_type[selection] EQ 0) then yeari =equinox
+ if keyword_set(EQUINOX) and (output_type[selection] EQ 0) then yearf = equinox
+
+ if ( selection EQ 0 ) then read, $
+     'Enter initial and final equinox (e.g. 1975,2000): ',yeari,yearf
+
+
+ case output_type[selection] of
+
+   0:  OutName = " RA Dec (" + fk + string( yearf, f= "(F6.1)" ) + "):  "
+   1:  OutName = " Galactic longitude and latitude: "
+   2:  OutName = " Ecliptic longitude and latitude: (" +  $
+                  fk + string( yearf, f= "(F6.1)" ) + ")"
+ endcase 
+
+ case input_type[selection] of 
+
+  0:  InName = "RA Dec (" + fk + string(yeari ,f ='(F6.1)' ) + ')'
+  1:  InName = "Galactic longitude and latitude: "
+  2:  InName = "Ecliptic longitude and latitude: (" + fk + $
+                string(yeari ,f ='(F6.1)' ) + ')'
+
+ endcase
+ 
+ HELP_INP: if ( input_type[selection] EQ 0 ) then begin
+
+  print,format='(/A)',' Enter RA, DEC with either 2 or 6 parameters '
+  print,format='(A/)',' Either RA, DEC (degrees) or HR, MIN, SEC, DEG, MIN SEC'
+
+ endif
+
+ READ_INP: 
+
+     get_coords,coords,'Enter '+ InName, Numcoords 
+
+ if ( coords[0] EQ -999 ) then begin        ;Normal Return
+        print,' '
+        if Numcoords GT 0 then goto, READ_INP
+        !quiet = sv_quiet
+        return
+ endif
+
+ ra = coords[0] & dec = coords[1]
+ if Numcoords EQ 6 then ra = ra*15.
+
+ if ( selection EQ 0 ) then begin 
+
+         precess, ra , dec , yeari, yearf, FK4 = fk4    ;Actual Calculations
+         newra = ra & newdec = dec
+
+ endif else begin 
+       if yeari NE ref_year then precess, ra, dec, yeari, ref_year,FK4=fk4
+       euler, ra, dec, newra, newdec, selection, fk4 = FK4
+       if yearf NE ref_year then precess, newra,newdec, ref_year, yearf,FK4=fk4
+ endelse
+
+ if newra LT 0 then newra = newra + 360.
+
+ if output_type[selection] EQ 0 then $
+     print, outname + adstring( [newra,newdec], 1) $
+
+ else  print, FORM = '(A,2F7.2,A,F7.2 )', $
+      outname, newra, newdec
+
+ print,' '
+ goto, READ_INP      
+
+ end            
diff --git a/Code/script_idl_mv/astrolib/astrolib.pro b/Code/script_idl_mv/astrolib/astrolib.pro
new file mode 100644
index 0000000000000000000000000000000000000000..99d61f92e72880d13af85c8f6b922d8a40a3f63e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/astrolib.pro
@@ -0,0 +1,51 @@
+PRO ASTROLIB
+;+
+; NAME:
+;       ASTROLIB
+; PURPOSE:
+;       Add the non-standard system variables used by the IDL Astronomy Library
+; EXPLANATION: 
+;       Also defines the environment variable ASTRO_DATA pointing to the 
+;       directory containing data files  associated with the IDL Astronomy 
+;       library (system dependent -- user must edit the third line in the
+;       program below).
+;
+; CALLING SEQUENCE:
+;       ASTROLIB
+;
+; INPUTS:
+;       None.
+;
+; OUTPUTS:
+;       None.
+;
+; METHOD:
+;       The non-standard system variables !PRIV, !TEXTUNIT, and 
+;       !TEXTOUT are added using DEFSYSV.
+;
+; REVISION HISTORY:
+;       Written, Wayne Landsman, July 1986.
+;       Use DEFSYSV instead of ADDSYSVAR           December 1990
+;       Test for system variable existence before definition    July 2001
+;       Assume since V55, remove VMS support  W. Landsman   Sep 2006
+;       Remove !Debug, comment out ASTRO_DATA definition  WL  Jan 2009 
+;-
+  On_error,2
+  compile_opt idl2
+
+;  User should edit the folowing line and uncomment it to give the location of 
+;  ASTRO_DATA on their own system (or define it in their .cshrc or .bashrc file).     
+;  setenv,'ASTRO_DATA=/export/home/ftp/pub/data/'
+
+  defsysv, '!PRIV', exist = exist 
+     if ~exist then defsysv, '!PRIV', 0
+  defsysv, '!TEXTUNIT', exist = exist
+     if ~exist then  defsysv, '!TEXTUNIT', 0
+  defsysv, '!TEXTOUT', exist = exist 
+     if ~exist then defsysv, '!TEXTOUT', 1 
+
+   message,'Astronomy Library system variables have been added',/INF
+
+  return
+  end
+ 
diff --git a/Code/script_idl_mv/astrolib/autohist.pro b/Code/script_idl_mv/astrolib/autohist.pro
new file mode 100644
index 0000000000000000000000000000000000000000..66bff440abc69fd2804aec7b8f404aaf80fa1ece
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/autohist.pro
@@ -0,0 +1,106 @@
+PRO AUTOHIST,V, ZX,ZY,XX,YY, NOPLOT=whatever,_EXTRA = _extra
+;
+;+
+; NAME:
+;       AUTOHIST
+;
+; PURPOSE:
+;       Draw a histogram using automatic bin-sizing.
+; EXPLANATION
+;       AUTOHIST chooses a number of bins (initially, SQRT(2*N). If this leads 
+;       to a histogram in which > 1/5 of the central 50% of the bins are empty,
+;       it decreases the number of bins and tries again. The minimum # bins is 
+;       5. The max=199.     Called by HISTOGAUSS and HALFAGAUSS.
+;
+; CALLING SEQUENCE:
+;       AUTOHIST, Sample, XLines, Ylines, XCenters, YCenters, [/NOPLOT, ]
+;                             ...Plotting Keywords
+; INPUT:
+;       Sample = the vector to be histogrammed
+;
+; OUTPUT:
+;       XLINES = vector of x coordinates of the points that trace the rectangular 
+;               histogram bins
+;       YLINES = vector of y coordinates. To draw the histogram plot YLINES vs 
+;                 XLINES
+;       XCENTERS = the x values of the bin centers
+;       YCENTERS = the corresponding y values
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /NOPLOT  If set, nothing is drawn
+;
+;       Any plotting keywords (e.g. XTITLE) may be supplied to AUTOHIST through
+;       the _EXTRA facility. 
+; REVISION HISTORY:
+;       Written,   H. Freudenreich, STX, 1/91
+;       1998 March 17 - Changed shading of histogram.  RSH, RSTX
+;       V5.0 update, _EXTRA keywords  W. Landsman    April 2002
+;       Added NOCLIP keyword for POLYFILL call C. Paxson/W. Landsman July 2003
+;       Use Coyote graphics   W. Landsman  Feb 2011
+;-
+
+ ON_ERROR,2
+ compile_opt idl2 
+ 
+ if N_params() LT 1 then begin
+    print,'Syntax - AUTOHIST, Sample, XLines, Ylines, XCenters, YCenters, [ '
+    print,'                           /NOPLOT, Plotting keywords... ]'
+    return
+ endif
+
+ MINBIN=5
+
+ N = N_ELEMENTS(V)
+ NB = FIX(SQRT(2.*N)) < 199
+ NB = NB > MINBIN
+
+ X1 = MIN(V, MAX = X2)
+
+tryagain:
+
+ DX = (X2-X1)/NB
+ XX = FINDGEN(NB)*DX + DX/2. + X1
+
+ IND = (V-X1)/DX > 0 <(NB-1)
+
+;  Compute the histogram for the current binning 
+
+ YY = HISTOGRAM(IND,MIN=0,MAX = NB-1)
+
+; Count the fraction of empty bins in the middle half of the histogram:
+ X14 = (XX[NB-1]-XX[0])/4.+X1
+ X34 = XX[NB-1]-(XX[NB-1]-XX[0])/4.
+ Q=WHERE( (YY EQ 0.) AND (XX GT X14) AND (XX LT X34), COUNT )
+ IF (COUNT GT NB/10) AND (NB GT MINBIN) THEN BEGIN  ; 20% EMPTY
+   NB = 3*NB/4
+   IF NB LT (2*N) THEN GOTO,tryagain
+ENDIF
+
+; Fill in ZX,ZY:
+ MB = 2*NB+2
+ ZX = FLTARR(MB)  &  ZY = FLTARR(MB)
+ IT = INDGEN(NB)*2 + 1
+
+ ZY[IT] = YY   &  ZY[IT+1] = YY
+
+ ZX[0] = X1       
+ ZX[IT] = XX - DX/2. &   ZX[IT+1] = XX + DX/2.
+ ZX[MB-1] = X2 
+
+IF KEYWORD_SET(WHATEVER) THEN RETURN
+
+; Plot, then fill, the bins:
+ YTOP = MAX(YY[1:NB-2])
+ YY[0] = YY[0] < YTOP
+ YY[NB-1] = YY[NB-1] < YTOP
+ cgPLOT,XX,YY,XRAN=[X1-DX,X2+DX],YRAN=[0.,1.1*YTOP],PSYM=10,_EXTRA=_extra
+ FOR J=0,NB-1 DO BEGIN
+  IF YY[J] GT 0 THEN BEGIN
+     A=[XX[J]-DX/2.,XX[J]+DX/2.,XX[J]+DX/2.,XX[J]-DX/2.] 
+     B=[0.,0.,YY[J],YY[J]]
+     cgcolorFILL,A,B,orientation=45,noclip=0
+  ENDIF
+ENDFOR
+
+RETURN
+END
diff --git a/Code/script_idl_mv/astrolib/avg.pro b/Code/script_idl_mv/astrolib/avg.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8f1a242d64891ea858b7861f925248ca7d29a6e7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/avg.pro
@@ -0,0 +1,111 @@
+FUNCTION AVG,ARRAY,DIMENSION, NAN = NAN, DOUBLE = DOUBLE
+;+
+; NAME:
+;       AVG
+; PURPOSE:
+;       Return the average value of an array, or 1 dimension of an array
+; EXPLANATION:
+;       Calculate the average value of an array, or calculate the average
+;       value over one dimension of an array as a function of all the other
+;       dimensions.
+;
+;       In 2009, a DIMENSION keyword was added to the IDL MEAN() function,
+;       giving it the same capability as AVG().  Thus, the use of AVG() is now
+;       **deprecated** in favor of the MEAN() function.    
+; CALLING SEQUENCE:
+;       RESULT = AVG( ARRAY, [ DIMENSION, /NAN, /DOUBLE ] )
+;
+; INPUTS:
+;       ARRAY = Input array.  May be any type except string.
+;
+; OPTIONAL INPUT PARAMETERS:
+;       DIMENSION = Optional dimension to do average over, integer scalar
+;
+; OPTIONAL KEYWORD INPUT:
+;      /NAN - Set this keyword to cause the routine to check for occurrences of
+;            the IEEE floating-point value NaN in the input data.  Elements with
+;            the value NaN are treated as missing data.
+;      /DOUBLE - By default, if the input Array is double-precision, complex, 
+;                or double complex, the result is of the same type;  64 bit
+;                integers are also returned as double.   Otherwise the result
+;                the  result is floating-point.   Use of the /DOUBLE keyword 
+;                forces a double precision output.   Note that internal 
+;                computations are always done in double precision.
+; OUTPUTS:
+;       The average value of the array when called with one parameter.
+;
+;       If DIMENSION is passed, then the result is an array with all the
+;       dimensions of the input array except for the dimension specified,
+;       each element of which is the average of the corresponding vector
+;       in the input array.
+;
+;       For example, if A is an array with dimensions of (3,4,5), then the
+;       command B = AVG(A,1) is equivalent to
+;
+;                       B = FLTARR(3,5)
+;                       FOR J = 0,4 DO BEGIN
+;                               FOR I = 0,2 DO BEGIN
+;                                       B[I,J] = TOTAL( A[I,*,J] ) / 4.
+;                               ENDFOR
+;                       ENDFOR
+;
+; RESTRICTIONS:
+;       Dimension specified must be valid for the array passed; otherwise the
+;       input array is returned as the output array.
+; PROCEDURE:
+;       AVG(ARRAY) = TOTAL(ARRAY, /DOUBLE)/N_ELEMENTS(ARRAY) when called with 
+;       one parameter.
+; MODIFICATION HISTORY:
+;       William Thompson        Applied Research Corporation
+;       July, 1986              8201 Corporate Drive
+;                               Landover, MD  20785
+;       Converted to Version 2      July, 1990
+;       Replace SUM call with TOTAL    W. Landsman    May, 1992
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added /NAN keyword   W. Landsman      July 2000
+;       Accept a scalar input value    W. Landsman/jimm@berkeley   November 2000
+;       Internal calculations always in double precision W. Landsman March 2002
+;       Return NAN if all values in array are NAN  W. Landsman April 2002
+;       Fixed coding bug if all values in array are NAN W. Landsman Jan 2004
+;-
+ ON_ERROR,2
+ S = SIZE(ARRAY,/STR)
+ IF S.N_ELEMENTS EQ 1 THEN RETURN, array[0]
+ IF S.N_ELEMENTS EQ 0 THEN $
+        MESSAGE,'Variable must be an array, name= ARRAY'
+;
+    IF N_PARAMS() EQ 1 THEN BEGIN
+        IF KEYWORD_SET(NAN) THEN NPTS = TOTAL(FINITE(ARRAY) ) $
+                            ELSE NPTS = N_ELEMENTS(ARRAY)
+        IF NPTS EQ 0 THEN AVERAGE = !VALUES.F_NAN ELSE $
+                          AVERAGE = TOTAL(ARRAY, NAN=NAN,/DOUBLE) / NPTS
+    ENDIF ELSE BEGIN
+        IF ((DIMENSION GE 0) AND (DIMENSION LT S.N_DIMENSIONS)) THEN BEGIN
+                AVERAGE = TOTAL(ARRAY,DIMENSION+1,NAN=NAN,/DOUBLE) 
+; Install a bug workaround since TOTAL(A,/NAN) returns 0 rather than NAN if 
+; all A values are NAN. 
+                IF KEYWORD_SET(NAN) THEN BEGIN
+                     NPTS = TOTAL(FINITE(ARRAY),DIMENSION+1 ) 
+                     BAD = WHERE(NPTS EQ 0, NBAD)
+                     AVERAGE = AVERAGE/(NPTS>1)
+                     IF NBAD GT 0 THEN AVERAGE[BAD] = !VALUES.D_NAN
+                 ENDIF ELSE AVERAGE = AVERAGE/S.DIMENSIONS[DIMENSION]
+                   
+        END ELSE $
+                MESSAGE,'*** Dimension out of range, name= ARRAY'
+    ENDELSE
+
+; Convert to floating point unless of type double, complex, or L64, or
+; if /DOUBLE is set.
+
+ IF ~KEYWORD_SET(DOUBLE) THEN BEGIN 
+    CASE S.TYPE OF
+     5: RETURN, AVERAGE
+     6: RETURN, COMPLEXARR( FLOAT(AVERAGE), FLOAT(IMAGINARY(AVERAGE)) )
+     9: RETURN, AVERAGE
+    14: RETURN, AVERAGE
+    15: RETURN, AVERAGE
+    ELSE: RETURN, FLOAT(AVERAGE)
+  ENDCASE
+  ENDIF ELSE RETURN, AVERAGE
+ END
diff --git a/Code/script_idl_mv/astrolib/baryvel.pro b/Code/script_idl_mv/astrolib/baryvel.pro
new file mode 100644
index 0000000000000000000000000000000000000000..132532e044b02788cb93dc77cb453a69bca01fcc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/baryvel.pro
@@ -0,0 +1,340 @@
+pro baryvel, dje, deq, dvelh, dvelb, JPL = JPL
+;+
+; NAME:
+;       BARYVEL
+; PURPOSE:
+;       Calculates heliocentric and barycentric velocity components of Earth.
+;
+; EXPLANATION:
+;       BARYVEL takes into account the Earth-Moon motion, and is useful for 
+;       radial velocity work to an accuracy of  ~1 m/s.
+;
+; CALLING SEQUENCE:
+;       BARYVEL, dje, deq, dvelh, dvelb, [ JPL =  ] 
+;
+; INPUTS:
+;       DJE - (scalar) Julian ephemeris date.
+;       DEQ - (scalar) epoch of mean equinox of dvelh and dvelb. If deq=0
+;               then deq is assumed to be equal to dje.
+; OUTPUTS: 
+;       DVELH: (vector(3)) heliocentric velocity component. in km/s 
+;       DVELB: (vector(3)) barycentric velocity component. in km/s
+;
+;       The 3-vectors DVELH and DVELB are given in a right-handed coordinate 
+;       system with the +X axis toward the Vernal Equinox, and +Z axis 
+;       toward the celestial pole.      
+;
+; OPTIONAL KEYWORD SET:
+;       JPL - if /JPL set, then BARYVEL will call the procedure JPLEPHINTERP
+;             to compute the Earth velocity using the full JPL ephemeris.   
+;             The JPL ephemeris FITS file JPLEPH.405 must exist in either the 
+;             current directory, or in the directory specified by the 
+;             environment variable ASTRO_DATA.   Alternatively, the JPL keyword
+;             can be set to the full path and name of the ephemeris file.
+;             A copy of the JPL ephemeris FITS file is available in
+;                 http://idlastro.gsfc.nasa.gov/ftp/data/         
+; PROCEDURES CALLED:
+;       Function PREMAT() -- computes precession matrix
+;       JPLEPHREAD, JPLEPHINTERP, TDB2TDT - if /JPL keyword is set
+; NOTES:
+;       Algorithm taken from FORTRAN program of Stumpff (1980, A&A Suppl, 41,1)
+;       Stumpf claimed an accuracy of 42 cm/s for the velocity.    A 
+;       comparison with the JPL FORTRAN planetary ephemeris program PLEPH
+;       found agreement to within about 65 cm/s between 1986 and 1994
+;
+;       If /JPL is set (using JPLEPH.405 ephemeris file) then velocities are 
+;       given in the ICRS system; otherwise in the FK4 system.   
+; EXAMPLE:
+;       Compute the radial velocity of the Earth toward Altair on 15-Feb-1994
+;          using both the original Stumpf algorithm and the JPL ephemeris
+;
+;       IDL> jdcnv, 1994, 2, 15, 0, jd          ;==> JD = 2449398.5
+;       IDL> baryvel, jd, 2000, vh, vb          ;Original algorithm
+;               ==> vh = [-17.07243, -22.81121, -9.889315]  ;Heliocentric km/s
+;               ==> vb = [-17.08083, -22.80471, -9.886582]  ;Barycentric km/s
+;       IDL> baryvel, jd, 2000, vh, vb, /jpl   ;JPL ephemeris
+;               ==> vh = [-17.07236, -22.81126, -9.889419]  ;Heliocentric km/s
+;               ==> vb = [-17.08083, -22.80484, -9.886409]  ;Barycentric km/s
+;
+;       IDL> ra = ten(19,50,46.77)*15/!RADEG    ;RA  in radians
+;       IDL> dec = ten(08,52,3.5)/!RADEG        ;Dec in radians
+;       IDL> v = vb[0]*cos(dec)*cos(ra) + $   ;Project velocity toward star
+;               vb[1]*cos(dec)*sin(ra) + vb[2]*sin(dec) 
+;
+; REVISION HISTORY:
+;       Jeff Valenti,  U.C. Berkeley    Translated BARVEL.FOR to IDL.
+;       W. Landsman, Cleaned up program sent by Chris McCarthy (SfSU) June 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added /JPL keyword  W. Landsman   July 2001
+;       Documentation update W. Landsman Dec 2005
+;-
+ On_Error,2
+ compile_opt idl2
+
+ if N_params() LT 4 then begin
+        print,'Syntax: BARYVEL, dje, deq, dvelh, dvelb'
+        print,'    dje - input Julian ephemeris date'
+        print,'    deq - input epoch of mean equinox of dvelh and dvelb'
+        print,'    dvelh - output vector(3) heliocentric velocity comp in km/s' 
+        print,'    dvelb - output vector(3) barycentric velocity comp in km/s'
+        return
+ endif
+
+ if keyword_set(JPL) then begin
+      if size(jpl,/TNAME) EQ 'STRING' then jplfile = jpl else $
+            jplfile = find_with_def('JPLEPH.405','ASTRO_DATA')
+      if jplfile EQ '' then message,'ERROR - Cannot find JPL ephemeris file' 
+      JPLEPHREAD,jplfile, pinfo, pdata, [long(dje), long(dje)+1]
+      JPLEPHINTERP, pinfo, pdata, dje, x,y,z,vx,vy,vz, /EARTH,/VELOCITY, $
+                 VELUNITS = 'KM/S'
+      dvelb = [vx,vy,vz]
+      JPLEPHINTERP, pinfo, pdata, dje, x,y,z,vx,vy,vz, /SUN,/VELOCITY, $
+                 VELUNITS = 'KM/S'
+      dvelh = dvelb - [vx,vy,vz]
+      if deq NE 2000 then begin
+             if deq EQ 0 then begin
+                     DAYCNV, dje , year, month, day, hour
+                     deq = year + month/12.d + day/365.25d + hour/8766.0d
+             endif
+             prema = premat(2000.0d,deq )
+             dvelh =  prema # dvelh 
+             dvelb =  prema # dvelb 
+      endif         
+      return
+ endif
+
+;Define constants
+  dc2pi = 2*!DPI 
+  cc2pi = 2*!PI 
+  dc1 = 1.0D0
+  dcto = 2415020.0D0
+  dcjul = 36525.0D0                     ;days in Julian year
+  dcbes = 0.313D0
+  dctrop = 365.24219572D0               ;days in tropical year (...572 insig)
+  dc1900 = 1900.0D0
+  AU = 1.4959787D8
+
+;Constants dcfel(i,k) of fast changing elements.
+  dcfel = [1.7400353D00, 6.2833195099091D02,  5.2796D-6 $
+          ,6.2565836D00, 6.2830194572674D02, -2.6180D-6 $
+          ,4.7199666D00, 8.3997091449254D03, -1.9780D-5 $
+          ,1.9636505D-1, 8.4334662911720D03, -5.6044D-5 $
+          ,4.1547339D00, 5.2993466764997D01,  5.8845D-6 $
+          ,4.6524223D00, 2.1354275911213D01,  5.6797D-6 $
+          ,4.2620486D00, 7.5025342197656D00,  5.5317D-6 $
+          ,1.4740694D00, 3.8377331909193D00,  5.6093D-6 ]
+  dcfel = reform(dcfel,3,8)
+
+;constants dceps and ccsel(i,k) of slowly changing elements.
+  dceps = [4.093198D-1, -2.271110D-4, -2.860401D-8 ]
+  ccsel = [1.675104E-2, -4.179579E-5, -1.260516E-7 $
+          ,2.220221E-1,  2.809917E-2,  1.852532E-5 $
+          ,1.589963E00,  3.418075E-2,  1.430200E-5 $
+          ,2.994089E00,  2.590824E-2,  4.155840E-6 $
+          ,8.155457E-1,  2.486352E-2,  6.836840E-6 $
+          ,1.735614E00,  1.763719E-2,  6.370440E-6 $
+          ,1.968564E00,  1.524020E-2, -2.517152E-6 $
+          ,1.282417E00,  8.703393E-3,  2.289292E-5 $
+          ,2.280820E00,  1.918010E-2,  4.484520E-6 $
+          ,4.833473E-2,  1.641773E-4, -4.654200E-7 $
+          ,5.589232E-2, -3.455092E-4, -7.388560E-7 $
+          ,4.634443E-2, -2.658234E-5,  7.757000E-8 $
+          ,8.997041E-3,  6.329728E-6, -1.939256E-9 $
+          ,2.284178E-2, -9.941590E-5,  6.787400E-8 $
+          ,4.350267E-2, -6.839749E-5, -2.714956E-7 $
+          ,1.348204E-2,  1.091504E-5,  6.903760E-7 $
+          ,3.106570E-2, -1.665665E-4, -1.590188E-7 ]
+  ccsel = reform(ccsel,3,17)
+
+;Constants of the arguments of the short-period perturbations.
+  dcargs = [5.0974222D0, -7.8604195454652D2 $
+           ,3.9584962D0, -5.7533848094674D2 $
+           ,1.6338070D0, -1.1506769618935D3 $
+           ,2.5487111D0, -3.9302097727326D2 $
+           ,4.9255514D0, -5.8849265665348D2 $
+           ,1.3363463D0, -5.5076098609303D2 $
+           ,1.6072053D0, -5.2237501616674D2 $
+           ,1.3629480D0, -1.1790629318198D3 $
+           ,5.5657014D0, -1.0977134971135D3 $
+           ,5.0708205D0, -1.5774000881978D2 $
+           ,3.9318944D0,  5.2963464780000D1 $
+           ,4.8989497D0,  3.9809289073258D1 $
+           ,1.3097446D0,  7.7540959633708D1 $
+           ,3.5147141D0,  7.9618578146517D1 $
+           ,3.5413158D0, -5.4868336758022D2 ]
+  dcargs = reform(dcargs,2,15)
+
+;Amplitudes ccamps(n,k) of the short-period perturbations.
+  ccamps = $
+    [-2.279594E-5,  1.407414E-5,  8.273188E-6,  1.340565E-5, -2.490817E-7 $
+    ,-3.494537E-5,  2.860401E-7,  1.289448E-7,  1.627237E-5, -1.823138E-7 $
+    , 6.593466E-7,  1.322572E-5,  9.258695E-6, -4.674248E-7, -3.646275E-7 $
+    , 1.140767E-5, -2.049792E-5, -4.747930E-6, -2.638763E-6, -1.245408E-7 $
+    , 9.516893E-6, -2.748894E-6, -1.319381E-6, -4.549908E-6, -1.864821E-7 $
+    , 7.310990E-6, -1.924710E-6, -8.772849E-7, -3.334143E-6, -1.745256E-7 $
+    ,-2.603449E-6,  7.359472E-6,  3.168357E-6,  1.119056E-6, -1.655307E-7 $
+    ,-3.228859E-6,  1.308997E-7,  1.013137E-7,  2.403899E-6, -3.736225E-7 $
+    , 3.442177E-7,  2.671323E-6,  1.832858E-6, -2.394688E-7, -3.478444E-7 $
+    , 8.702406E-6, -8.421214E-6, -1.372341E-6, -1.455234E-6, -4.998479E-8 $
+    ,-1.488378E-6, -1.251789E-5,  5.226868E-7, -2.049301E-7,  0.E0 $
+    ,-8.043059E-6, -2.991300E-6,  1.473654E-7, -3.154542E-7,  0.E0 $
+    , 3.699128E-6, -3.316126E-6,  2.901257E-7,  3.407826E-7,  0.E0 $
+    , 2.550120E-6, -1.241123E-6,  9.901116E-8,  2.210482E-7,  0.E0 $
+    ,-6.351059E-7,  2.341650E-6,  1.061492E-6,  2.878231E-7,  0.E0 ]
+  ccamps = reform(ccamps,5,15)
+
+;Constants csec3 and ccsec(n,k) of the secular perturbations in longitude.
+  ccsec3 = -7.757020E-8
+  ccsec = [1.289600E-6, 5.550147E-1, 2.076942E00 $
+          ,3.102810E-5, 4.035027E00, 3.525565E-1 $
+          ,9.124190E-6, 9.990265E-1, 2.622706E00 $
+          ,9.793240E-7, 5.508259E00, 1.559103E01 ]
+  ccsec = reform(ccsec,3,4)
+
+;Sidereal rates.
+  dcsld = 1.990987D-7                   ;sidereal rate in longitude
+  ccsgd = 1.990969E-7                   ;sidereal rate in mean anomaly
+
+;Constants used in the calculation of the lunar contribution.
+  cckm = 3.122140E-5
+  ccmld = 2.661699E-6
+  ccfdi = 2.399485E-7
+
+;Constants dcargm(i,k) of the arguments of the perturbations of the motion
+; of the moon.
+  dcargm = [5.1679830D0,  8.3286911095275D3 $
+           ,5.4913150D0, -7.2140632838100D3 $
+           ,5.9598530D0,  1.5542754389685D4 ]
+  dcargm = reform(dcargm,2,3)
+
+;Amplitudes ccampm(n,k) of the perturbations of the moon.
+  ccampm = [ 1.097594E-1, 2.896773E-7, 5.450474E-2,  1.438491E-7 $
+           ,-2.223581E-2, 5.083103E-8, 1.002548E-2, -2.291823E-8 $
+           , 1.148966E-2, 5.658888E-8, 8.249439E-3,  4.063015E-8 ]
+  ccampm = reform(ccampm,4,3)
+
+;ccpamv(k)=a*m*dl,dt (planets), dc1mme=1-mass(earth+moon)
+  ccpamv = [8.326827E-11, 1.843484E-11, 1.988712E-12, 1.881276E-12]
+  dc1mme = 0.99999696D0
+
+;Time arguments.
+  dt = (dje - dcto) / dcjul
+  tvec = [1d0, dt, dt*dt]
+
+;Values of all elements for the instant(aneous?) dje.
+  temp = (tvec # dcfel) mod dc2pi
+  dml = temp[0]
+  forbel = temp[1:7]
+  g = forbel[0]                         ;old fortran equivalence
+
+  deps = total(tvec*dceps) mod dc2pi
+  sorbel = (tvec # ccsel) mod dc2pi
+  e = sorbel[0]                         ;old fortran equivalence
+
+;Secular perturbations in longitude.
+dummy=cos(2.0)
+  sn = sin((tvec[0:1] # ccsec[1:2,*]) mod cc2pi)
+
+;Periodic perturbations of the emb (earth-moon barycenter).
+  pertl = total(ccsec[0,*] * sn) + dt*ccsec3*sn[2]
+  pertld = 0.0
+  pertr = 0.0
+  pertrd = 0.0
+  for k=0,14 do begin
+    a = (dcargs[0,k]+dt*dcargs[1,k]) mod dc2pi
+    cosa = cos(a)
+    sina = sin(a)
+    pertl = pertl + ccamps[0,k]*cosa + ccamps[1,k]*sina
+    pertr = pertr + ccamps[2,k]*cosa + ccamps[3,k]*sina
+    if k lt 11 then begin
+      pertld = pertld + (ccamps[1,k]*cosa-ccamps[0,k]*sina)*ccamps[4,k]
+      pertrd = pertrd + (ccamps[3,k]*cosa-ccamps[2,k]*sina)*ccamps[4,k]
+    endif
+  endfor
+
+;Elliptic part of the motion of the emb.
+  phi = (e*e/4d0)*(((8d0/e)-e)*sin(g) +5*sin(2*g) +(13/3d0)*e*sin(3*g))
+  f = g + phi
+  sinf = sin(f)
+  cosf = cos(f)
+  dpsi = (dc1 - e*e) / (dc1 + e*cosf)
+  phid = 2*e*ccsgd*((1 + 1.5*e*e)*cosf + e*(1.25 - 0.5*sinf*sinf))
+  psid = ccsgd*e*sinf / sqrt(dc1 - e*e)
+
+;Perturbed heliocentric motion of the emb.
+  d1pdro = dc1+pertr
+  drd = d1pdro * (psid + dpsi*pertrd)
+  drld = d1pdro*dpsi * (dcsld+phid+pertld)
+  dtl = (dml + phi + pertl) mod dc2pi
+  dsinls = sin(dtl)
+  dcosls = cos(dtl)
+  dxhd = drd*dcosls - drld*dsinls
+  dyhd = drd*dsinls + drld*dcosls
+
+;Influence of eccentricity, evection and variation on the geocentric
+; motion of the moon.
+  pertl = 0.0
+  pertld = 0.0
+  pertp = 0.0
+  pertpd = 0.0
+  for k = 0,2 do begin
+    a = (dcargm[0,k] + dt*dcargm[1,k]) mod dc2pi
+    sina = sin(a)
+    cosa = cos(a)
+    pertl = pertl + ccampm[0,k]*sina
+    pertld = pertld + ccampm[1,k]*cosa
+    pertp = pertp + ccampm[2,k]*cosa
+    pertpd = pertpd - ccampm[3,k]*sina
+  endfor
+
+;Heliocentric motion of the earth.
+  tl = forbel[1] + pertl
+  sinlm = sin(tl)
+  coslm = cos(tl)
+  sigma = cckm / (1.0 + pertp)
+  a = sigma*(ccmld + pertld)
+  b = sigma*pertpd
+  dxhd = dxhd + a*sinlm + b*coslm
+  dyhd = dyhd - a*coslm + b*sinlm
+  dzhd= -sigma*ccfdi*cos(forbel[2])
+
+;Barycentric motion of the earth.
+  dxbd = dxhd*dc1mme
+  dybd = dyhd*dc1mme
+  dzbd = dzhd*dc1mme
+  for k=0,3 do begin
+    plon = forbel[k+3]
+    pomg = sorbel[k+1]
+    pecc = sorbel[k+9]
+    tl = (plon + 2.0*pecc*sin(plon-pomg)) mod cc2pi
+    dxbd = dxbd + ccpamv[k]*(sin(tl) + pecc*sin(pomg))
+    dybd = dybd - ccpamv[k]*(cos(tl) + pecc*cos(pomg))
+    dzbd = dzbd - ccpamv[k]*sorbel[k+13]*cos(plon - sorbel[k+5])
+
+  endfor
+
+;Transition to mean equator of date.
+  dcosep = cos(deps)
+  dsinep = sin(deps)
+  dyahd = dcosep*dyhd - dsinep*dzhd
+  dzahd = dsinep*dyhd + dcosep*dzhd
+  dyabd = dcosep*dybd - dsinep*dzbd
+  dzabd = dsinep*dybd + dcosep*dzbd
+
+;Epoch of mean equinox (deq) of zero implies that we should use
+; Julian ephemeris date (dje) as epoch of mean equinox.
+  if deq eq 0 then begin
+    dvelh = AU * ([dxhd, dyahd, dzahd])
+    dvelb = AU * ([dxbd, dyabd, dzabd])
+    return
+  endif
+
+;General precession from epoch dje to deq.
+  deqdat = (dje-dcto-dcbes) / dctrop + dc1900
+   prema = premat(deqdat,deq,/FK4)
+
+  dvelh = AU * ( prema # [dxhd, dyahd, dzahd] )
+  dvelb = AU * ( prema # [dxbd, dyabd, dzabd] )
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/biweight_mean.pro b/Code/script_idl_mv/astrolib/biweight_mean.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2ecd438b16a90b12ae9b28890e3f08b4298b7724
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/biweight_mean.pro
@@ -0,0 +1,88 @@
+FUNCTION  BIWEIGHT_MEAN,Y,SIGMA, WEIGHTs
+;
+;+
+; NAME:
+;	BIWEIGHT_MEAN 
+;
+; PURPOSE:
+;	Calculate the center and dispersion (like mean and sigma) of a 
+;	distribution using bisquare weighting.
+;
+; CALLING SEQUENCE:
+;	Mean = BIWEIGHT_MEAN( Vector, [ Sigma, Weights ] ) 
+;
+; INPUTS:
+;	Vector = Distribution in vector form
+;
+; OUTPUT:
+;	Mean - The location of the center.
+;
+; OPTIONAL OUTPUT ARGUMENTS:
+;
+;	Sigma = An outlier-resistant measure of the dispersion about the 
+;	      center, analogous to the standard deviation. 
+;
+;	Weights = The weights applied to the data in the last iteration, 
+;                 floating point vector
+;
+; NOTES:
+;       Since a sample mean  scaled by sigma/sqrt(N), has a Student's T 
+;       distribution, the half-width of the  95% confidence interval for 
+;       the sample mean  can be determined as follows: 
+;          ABS( T_CVF( .975, .7*(N-1) )*SIGMA/SQRT(N) ) 
+;       where N = number of  points, and  0.975 = 1 - (1 - 0.95)/2. 
+; PROCEDURES USED:
+;       ROBUST_SIGMA()
+; REVISION HISTORY
+;	Written,  H. Freudenreich, STX, 12/89
+;	Modified 2/94, H.T.F.: use a biweighted standard deviation rather than
+;		median absolute deviation.
+;	Modified 2/94, H.T.F.: use the fractional change in SIGMA as the 
+;		convergence criterion rather than the change in center/SIGMA.
+;       Modified May 2002  Use MEDIAN(/EVEN)
+;       Modified October 2002, Faster computation of weights 
+;       Corrected documentation on 95% confidence interval of mean 
+;                 P.Broos/W. Landsman   July 2003 
+;-
+
+  ON_ERROR,2
+  maxit = 20 ; Allow 20 iterations, this should nearly always be sufficient
+  eps = 1.0e-24
+
+  n = n_elements(y)
+  close_enough =.03*sqrt(.5/(n-1)) ; compare to fractional change in width
+
+  diff = 1.0e30
+  itnum = 0
+
+; As an initial estimate of the center, use the median:
+  y0=median(y,/even)
+
+; Calculate the weights:
+  dev = y-y0
+  sigma = ROBUST_SIGMA( dev ) 
+
+  if sigma lt EPS then begin
+;    The median is IT. Do we need the weights?
+     if arg_present(weights)  then begin
+;       Flag any value away from the median:
+        limit=3.*sigma
+        weights = float(abs(dev) LE limit)
+     endif
+     diff = 0. ; (skip rest of routine)
+  endif
+
+; Repeat:
+  while( (diff gt close_enough) and (itnum lt maxit) )do begin
+    itnum = itnum + 1
+    uu = ( (y-y0)/(6.*sigma) )^2
+    uu = uu < 1.
+    weights=(1.-uu)^2       & weights=weights/total(weights)
+    y0 = total( weights*y ) 
+    dev = y-y0
+    prev_sigma = sigma      & sigma = robust_sigma( dev,/zero )
+    if sigma gt eps then diff=abs(prev_sigma-sigma)/prev_sigma else diff=0.
+  endwhile
+
+return,y0
+end
diff --git a/Code/script_idl_mv/astrolib/blink.pro b/Code/script_idl_mv/astrolib/blink.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0fd34c24b7b893db5937b449878c1a59168a049e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/blink.pro
@@ -0,0 +1,114 @@
+PRO BLINK, wndw, t
+;+
+; NAME:
+;	BLINK
+; PURPOSE:
+;	To allow the user to alternatively examine two or more windows within
+;	a single window.
+;
+; CALLING SEQUENCE:
+;	BLINK, Wndw [, T]
+;
+; INPUTS:
+;	Wndw  A vector containing the indices of the windows to blink.
+;	T     The time to wait, in seconds, between blinks.  This is optional
+;	      and set to 1 if not present.  
+;
+; OUTPUTS:
+;	None.
+;
+; PROCEDURE:
+;	The images contained in the windows given are written to a pixmap.
+;	The contents of the the windows are copied to a display window, in 
+;	order, until a key is struck.
+;
+; EXAMPLE:
+;	Blink windows 0 and 2 with a wait time of 3 seconds
+;
+;	IDL> blink, [0,2], 3 
+;
+; MODIFICATION HISTORY:
+;	Written by Michael R. Greason, STX, 2 May 1990.
+;	Allow different size windows   Wayne Landsman    August, 1991
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;			Check the parameters.
+;
+On_error,2                             ;Return to caller
+n = n_params(0)
+cflg = 0
+IF (n LT 2) THEN BEGIN
+	IF (n LT 1) THEN cflg = 1
+	t = 1.0
+ENDIF
+IF (cflg NE 1) THEN BEGIN
+	s = size(wndw)
+	cflg = 2
+	IF (s[0] GT 0) THEN BEGIN
+		IF (s[1] GT 1) THEN cflg = 0
+                n_wndw = s[1]
+	ENDIF
+ENDIF
+;
+;			Check to see if a window is open.  If so, save the 
+;			index for later use. 
+;
+IF (cflg EQ 0) THEN BEGIN
+	whld = !d.window
+	IF (whld LT 0) THEN cflg = 3
+ENDIF
+;
+;			If not enough or incorrect parameters were given, 
+;			complain and return.
+;
+IF (cflg NE 0) THEN BEGIN
+	IF (cflg EQ 1) THEN BEGIN
+		print, " Insufficient parameters given to BLINK."
+		print, " Syntax:  BLINK, WIN_INDICES [, TIME]"
+	ENDIF
+	IF (cflg EQ 2) THEN print, " The array of window indices is invalid."
+	IF (cflg EQ 3) THEN print, " No windows are open."
+ENDIF ELSE BEGIN
+;
+;
+;			Get the size of each window in the array.
+;
+device, window = opnd
+ncol = intarr(n_wndw)
+nrow = ncol
+for i=0,n_wndw-1 do begin
+        if ~opnd[wndw[i]] then $
+            message,'ERROR - Window '+ strtrim(wndw[i],2) + ' is not open'
+	wset, wndw[i]
+	ncol[i] = !d.x_vsize
+	nrow[i] = !d.y_vsize
+endfor
+;
+;			Write a message explaining how to terminate BLINK.
+;
+	print, "     "
+	print, "To exit BLINK, strike any key."
+	print, "     "
+;
+;			Create the display window and display the images.
+;
+	window, /free, retain=2, xsize = max(ncol), ysize=max(nrow), $
+                   xpos=0, ypos=0, $ 
+                   title="Blink window - Press any key to exit"
+        whd = !d.window
+	i = 0L
+	WHILE (get_kbrd(0) EQ '') DO BEGIN
+		device, copy=[0, 0, ncol[i], nrow[i], 0, 0, wndw[i]]
+		i = (i + 1) mod n_wndw
+		wait, t
+	ENDWHILE
+;
+;			Clear up and terminate.  Close windows/pixmaps and
+;			restore the originally active window.
+;
+	wdelete, whd
+	wset, whld
+ENDELSE
+;
+RETURN
+END
diff --git a/Code/script_idl_mv/astrolib/blkshift.pro b/Code/script_idl_mv/astrolib/blkshift.pro
new file mode 100644
index 0000000000000000000000000000000000000000..faa8234c0d47e508478bf73868ffc83ed496ad49
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/blkshift.pro
@@ -0,0 +1,231 @@
+;+
+; NAME:
+;   BLKSHIFT
+;
+; PURPOSE:
+;   Shift a block of data to a new position in a file (possibly overlapping)
+;
+; CALLING SEQUENCE:
+;
+;   BLKSHIFT, UNIT, POS, [ DELTA, TO=TO, /NOZERO, ERRMSG=ERRMSG, 
+;             BUFFERSIZE=BUFFERSIZE ]
+;
+; DESCRIPTION:
+;
+;  BLKSHIFT moves a block of data forward or backward, to a new
+;  position in a data file.  The old and new positions of the block
+;  can overlap safely.
+;
+;  The new position can be specified with either the DELTA parameter,
+;  which gives the number of bytes to move forward (positive delta) or
+;  backward (negative delta); or the TO keyword, which give the new
+;  absolute starting position of the block.
+;
+;  The block can be moved beyond the current end of file point, in
+;  which case the intervening gap is filled with zeros (optionally).
+;  The gap left at the old position of the block is also optionally
+;  zero-filled.    If a set of data up to the end of the file is being
+;  moved forward (thus making the file smaller) then
+;  the file is truncated at the new end.using TRUNCATE_LUN.
+;
+; INPUTS:
+;
+;   UNIT - a logical unit number, opened for reading and writing.
+;
+;   POS - POS[0] is the position of the block in the file, in bytes,
+;         before moving.  POS[1], if present, is the size of the block
+;         in bytes.  If POS[1] is not given, then the block is from
+;         POS[0] to the end of the file.
+;
+;   DELTA - the (optional) offset in bytes between the old and new
+;           positions, from the start of the block.  Positive values
+;           indicate moving the data forward (toward the end of file),
+;           and negative values indicate moving the data backward
+;           (toward the beginning of the file).  One of DELTA and TO
+;           must be specified; DELTA overrides the TO keyword.
+;
+;           Attempts to move the block beyond the end of the file will
+;           succeed.  A block can never be moved beyond the beginning
+;           of the file; it will be moved to the beginning instead.
+;
+; KEYWORD PARAMETERS:
+;
+;   TO - the absolute file offset in bytes for the new start of the
+;        block.  One of DELTA and TO must be specified; DELTA
+;        overrides the TO keyword.
+;
+;   /NOZERO - if set, then newly created gaps will not be explicitly
+;            zeroed.   Note that in same systems (e.g. MacOS) the gaps will
+;            always be zeroed whether or not /NOZERO is set.
+;
+;   ERRMSG - If defined and passed, then any error messages will be
+;            returned to the user in this parameter rather than
+;            depending on the MESSAGE routine in IDL.  If no errors
+;            are encountered, then a null string is returned.  
+;
+;			BLKSHIFT, UNIT, POS, DElTA, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+;   BUFFERSIZE - the maximum buffer size for transfers, in bytes.
+;                Larger values of this keyword impose larger memory
+;                requirements on the application; smaller values will
+;                lead to more transfer operations.
+;                Default: 32768 (bytes)
+;
+; ORIGINAL AUTHOR:
+;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
+;   craig.markwardt@nasa.gov
+;
+; MODIFICATION HISTORY:
+;
+;   Written, CM, Apr 2000
+;   Documented and re-written, CM, 20 Jul 2000
+;   Renamed from FXSHIFT to BLKSHIFT, CM, 21 Jul 2000
+;   Documentation, CM, 12 Dec 2002
+;   Truncate if moving data block forward from  the end of file 
+;             using TRUNCATE_LUN   W. Landsman Feb. 2005 
+;   Assume since V5.5, remove VMS support  W. Landsman  Sep 2006
+;   Assume since V5.6, TRUNCATE_LUN available  W. Landsman Sep 2006
+;   MacOS can point beyond EOF    W. Landsman   Aug 2009
+;   Use V6.0 notation  W. Landsman Aprl 2014
+;-
+PRO BLKSHIFT, UNIT, POS0, DELTA0, NOZERO=NOZERO0, ERRMSG=ERRMSG, $
+              BUFFERSIZE=BUFFERSIZE0, TO=TO0
+
+  ;; Default error handling
+  compile_opt idl2
+  on_error, 2
+  on_ioerror, IO_FINISH
+  if n_params() LT 3 then begin
+      message = 'BLKSHIFT, UNIT, POS, DELTA'
+      goto, ERRMSG_OUT
+  endif
+
+  ;; Make sure file is open for writing, and begin parameter
+  ;; processing
+  fs = fstat(unit)
+  if fs.open EQ 0 OR fs.write EQ 0 then begin
+      message = 'File '+fs.name+' is not open for writing'
+      goto, ERRMSG_OUT
+  endif
+  nozero = keyword_set(nozero0)
+  pos_beg = floor(pos0[0])
+  if n_elements(pos0) GT 1 then pos_fin = floor(pos0[1])
+  if n_elements(pos_fin) EQ 0 then pos_fin = fs.size - 1L
+
+  if pos_beg GE fs.size then goto, GOOD_FINISH
+  if n_elements(to0) EQ 0 AND n_elements(delta0) EQ 0 then begin
+      message = 'Must specify DELTA or TO'
+      goto, ERRMSG_OUT
+  endif
+
+  ;; Parse the delta value, and enforce the file positioning
+  if n_elements(delta0) GT 0 then begin
+      delta = floor(delta0[0])
+      ;; Can't move beyond beginning of file
+      delta = ((pos_beg + delta) > 0L) - pos_beg 
+  endif else begin
+      delta = (floor(to0[0]) > 0L) - pos_beg
+  endelse
+      
+  if delta EQ 0 then goto, GOOD_FINISH
+  if pos_fin GE fs.size then pos_fin = fs.size - 1L
+  if pos_fin LT pos_beg then goto, GOOD_FINISH
+
+  if n_elements(buffersize0) EQ 0 then buffersize0 = 32768L
+  buffersize = long(buffersize0[0])
+  if buffersize LE 0 then buffersize = 32768L
+
+  ;; Seek to end of file and add zeroes (if needed)
+  pos_fin +=  1L
+
+  ;; Unless /Nozero set, the zeroes will be explicitly written
+  if (delta GT 0) && (nozero EQ 0) && (pos_fin+delta GT fs.size) then begin
+      point_lun, unit, fs.size
+      nleft = (pos_fin-fs.size) + delta
+      while nleft GT 0 do begin
+          ntrans = nleft < buffersize
+          if n_elements(bb0) NE ntrans then bb0 = bytarr(ntrans)
+          writeu, unit, bb0, transfer_count=cc
+          if cc EQ 0 then goto, IO_FINISH
+          nleft -= cc
+      endwhile
+  endif
+
+  ;; Now shift the data forward or backward
+  if delta GT 0 then begin
+
+      ;; Shift forward (toward end of file)
+      edat = pos_fin    ;; End of to-be-copied data segment
+      while edat GT pos_beg do begin
+          ntrans = (edat - pos_beg) < buffersize
+          if n_elements(bb0) NE ntrans then bb0 = bytarr(ntrans)
+          point_lun, unit, edat - ntrans
+          readu, unit, bb0, transfer_count=cc
+          if cc NE ntrans then goto, IO_FINISH
+          point_lun, unit, edat - ntrans + delta
+          writeu, unit, bb0, transfer_count=cc
+          if cc NE ntrans then goto, IO_FINISH
+          edat -= ntrans
+      endwhile
+  endif else begin
+
+      ;; Shift backward (toward beginning of file)
+      bdat = pos_beg   ;; Beginning of to-be-copied data segment
+      while bdat LT pos_fin do begin
+          ntrans = (pos_fin - bdat) < buffersize
+          if n_elements(bb0) NE ntrans then bb0 = bytarr(ntrans)
+          point_lun, unit, bdat
+          readu, unit, bb0, transfer_count=cc
+          if cc NE ntrans then goto, IO_FINISH
+          point_lun, unit, bdat - abs(delta)
+          writeu, unit, bb0, transfer_count=cc
+          if cc NE ntrans then goto, IO_FINISH
+          bdat += ntrans
+      endwhile
+      if pos_fin EQ fs.size  then begin 
+                  Truncate_Lun, unit
+                  goto, GOOD_FINISH
+      endif
+  endelse
+  bb0 = [0b] & dummy = temporary(bb0)
+
+  ;; Finally, zero out the gap we created
+  if nozero EQ 0 then begin
+      if delta GT 0 then begin
+          point_lun, unit, pos_beg  ;; also, to be sure data is flushed
+          z_fin = pos_fin < (pos_beg + delta)
+          nleft = (z_fin - pos_beg)
+      endif else begin
+          z_beg = (pos_fin - abs(delta)) > pos_beg
+          nleft = (pos_fin - z_beg)
+          point_lun, unit, z_beg
+      endelse
+      while nleft GT 0 do begin
+          i = nleft < buffersize
+          if n_elements(bb0) NE i then bb0 = bytarr(i)
+          writeu, unit, bb0, transfer_count=cc
+          if cc EQ 0 then goto, IO_FINISH
+          nleft -= cc
+      endwhile
+  endif
+  point_lun, unit, pos_beg  ;; again, to be sure data is flushed
+
+  GOOD_FINISH:
+  if arg_present(errmsg) then errmsg = ''
+  return
+
+  IO_FINISH:
+  on_ioerror, NULL
+  message = 'ERROR: BLKSHIFT operation failed because of an I/O error'
+  ;; fallthrough...
+
+  ;; Error message processing.  Control does not pass through here.
+  ERRMSG_OUT:
+  if arg_present(errmsg) then begin
+      errmsg = message
+      return
+  endif
+  message, message
+END
+
diff --git a/Code/script_idl_mv/astrolib/boost_array.pro b/Code/script_idl_mv/astrolib/boost_array.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d12290335d43bf20ef58de28d32267b6c64e117d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/boost_array.pro
@@ -0,0 +1,130 @@
+	PRO BOOST_ARRAY, DESTINATION, APPEND
+;+
+; NAME:
+;	BOOST_ARRAY
+; PURPOSE:
+;	Append one array onto a destination array
+; EXPLANATION:
+;	Add array APPEND to array DESTINATION, allowing the dimensions of
+;	DESTINATION to adjust to accommodate it.  If both input arrays have the
+;	same number of dimensions, then the output array will have one
+;	additional dimension.  Otherwise, the last dimension of DESTINATION
+;	will be incremented by one.
+; CATEGORY:
+;	Utility
+; CALLING SEQUENCE:
+;	BOOST_ARRAY, DESTINATION, APPEND
+; INPUT:
+;	DESTINATION	= Array to be expanded.
+;	APPEND		= Array to append to DESTINATION.
+; OUTPUTS:
+;	DESTINATION	= Expanded output array.
+; RESTRICTIONS:
+;	DESTINATION and APPEND have to be either both of type string or both of
+;	numerical types.
+;
+;	APPEND cannot have more dimensions than DESTINATION.
+;
+; MODIFICATION HISTOBY:
+;	Written Aug'88 (DMZ, ARC)
+;	Modified Sep'89 to handle byte arrays (DMZ)
+;	Modifed to version 2, Paul Hick (ARC), Feb 1991
+;	Removed restriction to 2D arrays, William Thompson (ARC), Feb 1992.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+	ON_ERROR, 2			;On error, return to caller
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 2 THEN MESSAGE,	$
+		'Syntax:  BOOST_ARRAY, DESTINATION, APPEND'
+;
+;  Make sure APPEND is defined.
+;
+	IF N_ELEMENTS(APPEND) EQ 0 THEN MESSAGE,	$
+		'Array to be appended (APPEND) not defined'
+;
+;  If DESTINATION is not defined, then set it equal to APPEND.
+;
+	IF N_ELEMENTS(DESTINATION) EQ 0 THEN BEGIN
+		DESTINATION = APPEND
+		RETURN
+	ENDIF
+;
+;  Get the array types and dimensions of DESTINATION and APPEND.
+;
+	SD = SIZE(DESTINATION)
+	SA = SIZE(APPEND)
+	D_NDIM = SD[0]
+	A_NDIM = SA[0]
+	IF D_NDIM EQ 0 THEN D_DIM = 1 ELSE D_DIM = SD[1:D_NDIM]
+	IF A_NDIM EQ 0 THEN A_DIM = 1 ELSE A_DIM = SA[1:A_NDIM]
+	D_TYPE = SD[N_ELEMENTS(SD)-2]
+	A_TYPE = SA[N_ELEMENTS(SA)-2]
+;
+;  Treat scalars as one-dimensional arrays.
+;
+	D_NDIM = D_NDIM > 1
+	A_NDIM = A_NDIM > 1
+; 
+;  Check to see if both arrays are of type string or numeric.
+;
+	IF D_TYPE EQ 7 THEN D_STRING = 1  ELSE D_STRING = 0
+	IF A_TYPE EQ 7 THEN A_STRING = 1  ELSE A_STRING = 0
+	IF D_STRING NE A_STRING THEN MESSAGE,	$
+		'Data arrays should be either both string or both non-string'
+;
+;  Calculate the number of dimensions in the output array.  If both arrays have
+;  the same number of dimensions, then create a new array with an extra
+;  dimension of two.  Otherwise, make sure that DESTINATION has more dimensions
+;  than APPEND.
+;
+	IF D_NDIM EQ A_NDIM THEN BEGIN
+		R_DIM = [D_DIM > A_DIM, 2]
+	END ELSE IF D_NDIM LT A_NDIM THEN BEGIN
+		MESSAGE,'APPEND has more dimensions than DESTINATION'
+;
+;  Otherwise, merge the dimensions of DESTINATION and APPEND, and add one to
+;  the final dimension.
+;
+	END ELSE BEGIN
+		R_DIM = D_DIM
+		FOR I = 0,A_NDIM-1 DO R_DIM[I] = D_DIM[I] > A_DIM[I]
+		R_DIM[D_NDIM-1] = R_DIM[D_NDIM-1] + 1
+	ENDELSE
+;
+;  Create the output array with the correct number of elements, and the greater
+;  of the types of DESTINATION and APPEND.
+;
+	OUTPUT = MAKE_ARRAY(DIMENSION=R_DIM, TYPE=(D_TYPE > A_TYPE))
+;
+;  Store DESTINATION in the output array.
+;
+	R_NDIM = N_ELEMENTS(R_DIM)
+	CASE R_NDIM OF
+		2:  OUTPUT[0,0] = DESTINATION
+		3:  OUTPUT[0,0,0] = DESTINATION
+		4:  OUTPUT[0,0,0,0] = DESTINATION
+		5:  OUTPUT[0,0,0,0,0] = DESTINATION
+		6:  OUTPUT[0,0,0,0,0,0] = DESTINATION
+		7:  OUTPUT[0,0,0,0,0,0,0] = DESTINATION
+	ENDCASE
+;
+;  Add APPEND at the end.
+;
+	LAST = R_DIM[R_NDIM-1] - 1
+	CASE R_NDIM OF
+		2:  OUTPUT[0,LAST] = APPEND
+		3:  OUTPUT[0,0,LAST] = APPEND
+		4:  OUTPUT[0,0,0,LAST] = APPEND
+		5:  OUTPUT[0,0,0,0,LAST] = APPEND
+		6:  OUTPUT[0,0,0,0,0,LAST] = APPEND
+		7:  OUTPUT[0,0,0,0,0,0,LAST] = APPEND
+	ENDCASE
+;
+;  Replace DESTINATION with OUTPUT, and return.
+;
+	DESTINATION = OUTPUT
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/boxave.pro b/Code/script_idl_mv/astrolib/boxave.pro
new file mode 100644
index 0000000000000000000000000000000000000000..899de45a515a0d98aba165b1735abe2d17d584b6
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/boxave.pro
@@ -0,0 +1,128 @@
+function boxave, array, xsize, ysize
+;+
+; NAME:
+;       BOXAVE
+; PURPOSE:
+;       Box-average a 1 or 2 dimensional array.   
+; EXPLANATION:
+;       This procedure differs from the intrinsic REBIN function in the follow 
+;       2 ways: 
+;
+;       (1) the box size parameter is specified rather than the output 
+;               array size
+;       (2) for INTEGER arrays, BOXAVE computes intermediate steps using REAL*4 
+;               (or REAL*8 for 64bit integers) arithmetic.   This is 
+;               considerably slower than REBIN but avoids integer truncation
+;
+; CALLING SEQUENCE:
+;       result = BOXAVE( Array, Xsize,[ Ysize ] )     
+;
+; INPUTS:
+;       ARRAY - Two dimensional input Array to be box-averaged.  Array may be 
+;               one or 2 dimensions and of any type except character.   
+;
+; OPTIONAL INPUTS:
+;       XSIZE - Size of box in the X direction, over which the array is to
+;               be averaged.  If omitted, program will prompt for this 
+;               parameter.  
+;       YSIZE - For 2 dimensional arrays, the box size in the Y direction.
+;               If omitted, then the box size in the X and Y directions are 
+;               assumed to be equal
+;
+; OUTPUT:
+;       RESULT - Output array after box averaging.  If the input array has 
+;               dimensions XDIM by YDIM, then RESULT has dimensions
+;               XDIM/NBOX by YDIM/NBOX.  The type of RESULT is the same as
+;               the input array.  However, the averaging is always computed
+;               using REAL arithmetic, so that the calculation should be exact.
+;               If the box size did not exactly divide the input array, then
+;               then not all of the input array will be boxaveraged.
+;
+; PROCEDURE:
+;       BOXAVE boxaverages all points simultaneously using vector subscripting
+;
+; NOTES:
+;       If im_int is a 512 x 512 integer (16 bit) array, then the two statements
+;
+;               IDL> im = fix(round(rebin(float(im_int), 128, 128)))
+;               IDL> im  = boxave( im_int,4)
+;
+;       give equivalent results.   The use of REBIN is faster, but BOXAVE is
+;       is less demanding on virtual memory, since one does not need to make
+;       a floating point copy of the entire array.      
+;
+; REVISION HISTORY:
+;       Written, W. Landsman, October 1986
+;       Call REBIN for REAL*4 and REAL*8 input arrays, W. Landsman Jan, 1992
+;       Removed /NOZERO in output array definition     W. Landsman 1995
+;       Fixed occasional integer overflow problem      W. Landsman Sep. 1995
+;       Allow unsigned data types                      W. Landsman Jan. 2000
+;       Assume since V5.4, Allow 64bit integers        W. Landsman Apr  2006
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() EQ 0 then $
+     message,'Syntax -   out =  BOXAVE( array, xsize, [ysize ])',/NoName
+
+ s = size(array)
+ if ( s[0] NE 1 ) and ( s[0] NE 2 ) then $
+     message,'Input array (first parameter) must be 1 or 2 dimensional'
+
+ if N_elements(xsize) EQ 0 then read,'BOXAVE: Enter box size: ',xsize 
+ if N_elements(ysize) EQ 0 then ysize = xsize
+
+ s = size(array)
+ ninx = s[1]                                  
+ noutx = ninx/xsize     
+ type = s[ s[0] + 1]
+ integer = (type LT 4) or (type GE 12)
+
+ if s[0] EQ 1 then begin                ; 1 dimension?
+
+     if integer then begin 
+
+        if xsize LT 2 then return, array
+        counter = lindgen(noutx)*xsize
+        output = array[counter]
+        for i=1,xsize-1 do output = output + array[counter + i]
+        if type GE 14 then nboxsq = double(xsize) else nboxsq = float(xsize)
+
+      endif else return, rebin( array, noutx)     ;Use REBIN if not integer
+
+  endif else begin              ; 2 dimensions
+
+        niny = s[2]
+        nouty = niny/ysize
+        if integer then begin                        ;Byte, Integer, or Long
+
+           if type GE 14 then begin 
+               nboxsq = double( xsize*ysize )
+               output = dblarr( noutx, nouty)     ;Create output array 
+           endif else begin
+                nboxsq = float( xsize*ysize )
+                output = fltarr( noutx, nouty)     ;Create output array 
+           endelse
+           counter = lindgen( noutx*nouty )     
+           counter = xsize*(counter mod noutx) + $
+                    (ysize*ninx)*long((counter/noutx))
+
+           for i = 0L,xsize-1 do $
+           for j = 0L,ysize-1 do $
+                 output = output + array[counter + (i + j*ninx)]
+
+        endif else $
+           return, rebin( array, noutx, nouty)       ;Use REBIN if not integer
+ endelse
+
+ case type of 
+ 12:  return, uint(round( output/nboxsq ))               ;Unsigned Integer
+ 13:  return, ulong( round(output/nboxsq))               ;Unsigned Long
+ 14:  return, round(output/nboxsq, /L64)                 ;64bit integer
+ 15:  return, ulong64(round(output/nboxsq,/L64))         ;Unsigned 64bit  
+  2:  return, fix( round( output/ nboxsq ))              ;Integer
+  3:  return, round( output / nboxsq )                   ;Long
+  1:  return, byte( round( output/nboxsq) )              ;Byte
+ endcase
+
+ end
diff --git a/Code/script_idl_mv/astrolib/bprecess.pro b/Code/script_idl_mv/astrolib/bprecess.pro
new file mode 100644
index 0000000000000000000000000000000000000000..cf812a62a2db89ad8d18ddae67a347d7748d28ca
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/bprecess.pro
@@ -0,0 +1,219 @@
+pro Bprecess, ra, dec, ra_1950, dec_1950, MU_RADEC = mu_radec,  $
+                  PARALLAX = parallax,  RAD_VEL = rad_vel, EPOCH = epoch
+;+
+; NAME:
+;       BPRECESS
+; PURPOSE:
+;       Precess positions from J2000.0 (FK5) to B1950.0 (FK4)
+; EXPLANATION:
+;       Calculates the mean place of a star at B1950.0 on the FK4 system from
+;       the mean place at J2000.0 on the FK5 system.    
+;
+; CALLING SEQUENCE:
+;       bprecess, ra, dec, ra_1950, dec_1950, [ MU_RADEC = , PARALLAX = 
+;                                       RAD_VEL =, EPOCH =   ]
+;
+; INPUTS:
+;       RA,DEC - Input J2000 right ascension and declination in *degrees*.
+;               Scalar or N element vector
+;
+; OUTPUTS:
+;       RA_1950, DEC_1950 - The corresponding B1950 right ascension and 
+;               declination in *degrees*.    Same number of elements as
+;               RA,DEC but always double precision.
+;
+; OPTIONAL INPUT-OUTPUT KEYWORDS
+;       MU_RADEC - 2xN element double precision vector containing the proper 
+;                  motion in seconds of arc per tropical *century* in right 
+;                  ascension and declination.
+;       PARALLAX - N_element vector giving stellar parallax (seconds of arc)
+;       RAD_VEL  - N_element vector giving radial velocity in km/s
+;
+;       The values of MU_RADEC, PARALLAX, and RADVEL will all be modified
+;       upon output to contain the values of these quantities in the
+;       B1950 system.  The parallax and radial velocity will have a very 
+;       minor influence on the B1950 position.   
+;
+;       EPOCH - scalar giving epoch of original observations, default 2000.0d
+;           This keyword value is only used if the MU_RADEC keyword is not set.
+; NOTES:
+;       The algorithm is taken from the Explanatory Supplement to the 
+;       Astronomical Almanac 1992, page 186.
+;       Also see Aoki et al (1983), A&A, 128,263
+;
+;       BPRECESS distinguishes between the following two cases:
+;       (1) The proper motion is known and non-zero
+;       (2) the proper motion is unknown or known to be exactly zero (i.e.
+;               extragalactic radio sources).   In this case, the reverse of 
+;               the algorithm in Appendix 2 of Aoki et al. (1983) is used to 
+;               ensure that the output proper motion is  exactly zero. Better 
+;               precision can be achieved in this case by inputting the EPOCH 
+;               of the original observations.
+;
+;       The error in using the IDL procedure PRECESS for converting between
+;       B1950 and J1950 can be up to 12", mainly in right ascension.   If
+;       better accuracy than this is needed then BPRECESS should be used.
+;
+;       An unsystematic comparison of BPRECESS with the IPAC precession 
+;       routine (http://nedwww.ipac.caltech.edu/forms/calculator.html) always 
+;       gives differences less than 0.15".
+; EXAMPLE:
+;       The SAO2000 catalogue gives the J2000 position and proper motion for
+;       the star HD 119288.   Find the B1950 position. 
+;
+;       RA(2000) = 13h 42m 12.740s      Dec(2000) = 8d 23' 17.69''  
+;       Mu(RA) = -.0257 s/yr      Mu(Dec) = -.090 ''/yr
+;
+;       IDL> mu_radec = 100D* [ -15D*.0257, -0.090 ]
+;       IDL> ra = ten(13, 42, 12.740)*15.D 
+;       IDL> dec = ten(8, 23, 17.69)
+;       IDL> bprecess, ra, dec, ra1950, dec1950, mu_radec = mu_radec
+;       IDL> print, adstring(ra1950, dec1950,2)
+;               ===> 13h 39m 44.526s    +08d 38' 28.63"
+;
+; REVISION HISTORY:
+;       Written,    W. Landsman                October, 1992
+;       Vectorized, W. Landsman                February, 1994
+;       Treat case where proper motion not known or exactly zero  November 1994
+;       Handling of arrays larger than 32767   Lars L. Christensen, march, 1995
+;       Fixed bug where A term not initialized for vector input 
+;            W. Landsman        February 2000
+;       Use V6.0 notation  W. Landsman Mar 2011
+;       
+;-   
+  On_error,2
+  compile_opt idl2
+
+  if N_params() LT 4 then begin
+     print,'Syntax - BPRECESS, ra,dec, ra_1950, dec_1950, [MU_RADEC =' 
+     print,'                            PARALLAX = , RAD_VEL = ]'
+     print,'  Input RA and Dec should be given in DEGREES for J2000'
+     print,'  Proper motion, MU_RADEC, (optional) in arc seconds per *century*'
+     print,'  Parallax (optional) in arc seconds'      
+     print,'  Radial Velocity (optional) in km/s'
+     return
+
+  endif
+
+  N = N_elements( ra )
+  if N EQ 0 then message,'ERROR - First parameter (RA vector) is undefined'
+
+  if ~keyword_set( RAD_VEL) then rad_vel = dblarr(N) else begin
+        rad_vel = rad_vel*1.
+        if N_elements( RAD_VEL) NE N then message, $
+        'ERROR - RAD_VEL keyword vector must contain ' + strtrim(N,2) +' values'
+  endelse
+
+  if keyword_set( MU_RADEC) then begin
+         if (N_elements( mu_radec) NE 2*N ) then message, $
+    'ERROR - MU_RADEC keyword (proper motion) be dimensioned (2,' + $
+                 strtrim(N,2) + ')'
+        mu_radec = mu_radec*1.
+  endif
+
+  if ~keyword_set( Parallax) then parallax = dblarr(N) else $
+        parallax = parallax*1.
+
+  if ~keyword_set(Epoch) then epoch = 2000.0d0
+
+  radeg = 180.D/!DPI
+  sec_to_radian = 1.d0/radeg/3600.d0
+
+ M =  [ [+0.9999256795D, -0.0111814828D, -0.0048590040D,  $
+         -0.000551D,  -0.238560D,     +0.435730D     ], $
+       [ +0.0111814828D, +0.9999374849D, -0.0000271557D,  $ 
+         +0.238509D,     -0.002667D,      -0.008541D     ], $
+       [ +0.0048590039D, -0.0000271771D, +0.9999881946D , $
+         -0.435614D,      +0.012254D,      +0.002117D      ], $
+       [ -0.00000242389840D, +0.00000002710544D, +0.00000001177742D, $
+         +0.99990432D,    -0.01118145D,    -0.00485852D    ], $
+       [ -0.00000002710544D, -0.00000242392702D, +0.00000000006585D, $
+         +0.01118145D,     +0.99991613D,    -0.00002716D    ], $
+       [ -0.00000001177742D, +0.00000000006585D,-0.00000242404995D, $
+         +0.00485852D,   -0.00002717D,    +0.99996684D] ] 
+
+ A_dot = 1D-3*[1.244D, -1.579D, -0.660D ]           ;in arc seconds per century
+
+ ra_rad = ra/radeg       &      dec_rad = dec/radeg
+ cosra =  cos( ra_rad )  &       sinra = sin( ra_rad )
+ cosdec = cos( dec_rad ) &      sindec = sin( dec_rad )
+
+ dec_1950 = dec*0.
+ ra_1950 = ra*0.
+
+ for i = 0L, N-1 do begin
+
+; Following statement moved inside loop in Feb 2000.
+ A = 1D-6*[ -1.62557D, -0.31919D, -0.13843D]        ;in radians
+
+ r0 = [ cosra[i]*cosdec[i], sinra[i]*cosdec[i], sindec[i] ]
+
+ if keyword_set(mu_radec) then begin
+
+ mu_a = mu_radec[ 0, i ]
+ mu_d = mu_radec[ 1, i ]
+ r0_dot = [ -mu_a*sinra[i]*cosdec[i] - mu_d*cosra[i]*sindec[i] , $ ;Velocity vector
+             mu_a*cosra[i]*cosdec[i] - mu_d*sinra[i]*sindec[i] , $
+             mu_d*cosdec[i] ] + 21.095d * rad_vel[i] * parallax[i] * r0
+
+ endif else r0_dot = [0.0d0, 0.0d0, 0.0d0]
+
+  R_0 = [ r0, r0_dot ]
+  R_1 =  M # R_0
+
+ ; Include the effects of the E-terms of aberration to form r and r_dot.
+
+ r1 = R_1[0:2]  
+ r1_dot = R_1[3:5] 
+
+ if ~keyword_set(Mu_radec) then begin
+        r1 = r1 + sec_to_radian * r1_dot * (epoch - 1950.0d)/100.
+        A = A + sec_to_radian * A_dot * (epoch - 1950.0d)/100.
+ endif
+
+ x1 = R_1[0]   &   y1 = R_1[1]    &  z1 = R_1[2]
+ rmag = sqrt( x1^2 + y1^2 + z1^2 )
+
+
+ s1 = r1/rmag    & s1_dot = r1_dot/rmag
+
+ s = s1
+ for j = 0,2 do begin
+    r = s1 + A - (total(s * A))*s
+    s = r/rmag
+ endfor 
+ x = r[0]          & y = r[1]     &  z = r[2]  
+ r2 = x^2 + y^2 + z^2
+ rmag = sqrt( r2 )
+ 
+ if keyword_set(Mu_radec) then begin
+         r_dot = s1_dot + A_dot - ( total( s * A_dot))*s
+         x_dot = r_dot[0]  & y_dot= r_dot[1]  &  z_dot = r_dot[2]
+         mu_radec[0,i] = ( x*y_dot - y*x_dot) / ( x^2 + y^2)
+         mu_radec[1,i] = ( z_dot* (x^2 + y^2) - z*(x*x_dot + y*y_dot) ) /  $
+                     ( r2*sqrt( x^2 + y^2) )
+ endif
+
+ dec_1950[i] = asin( z / rmag)
+ ra_1950[i] = atan( y, x)
+
+  if parallax[i] GT 0. then begin
+      rad_vel[i] = ( x*x_dot + y*y_dot + z*z_dot )/ (21.095*Parallax[i]*rmag)
+      parallax[i] = parallax[i] / rmag
+  endif
+ endfor
+
+ neg = where( ra_1950 LT 0, NNeg )
+ if Nneg GT 0 then ra_1950[neg] = ra_1950[neg] + 2.D*!DPI
+
+ ra_1950 = ra_1950*radeg & dec_1950 = dec_1950*radeg
+
+; Make output scalar if input was scalar
+
+ sz = size(ra)
+ if sz[0] EQ 0 then begin
+        ra_1950 = ra_1950[0]     &      dec_1950 = dec_1950[0]
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/break_path.pro b/Code/script_idl_mv/astrolib/break_path.pro
new file mode 100644
index 0000000000000000000000000000000000000000..703c381a6ec31ae5d2263836870fa5f490f52d3a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/break_path.pro
@@ -0,0 +1,140 @@
+        FUNCTION BREAK_PATH, PATHS, NOCURRENT=NOCURRENT
+;+
+; NAME: 
+;    BREAK_PATH()
+;
+; PURPOSE: 
+;     Breaks up a path string into its component directories.
+;
+; CALLING SEQUENCE: 
+;     Result = BREAK_PATH( PATHS [ /NoCurrent])
+;
+; INPUTS: 
+;     PATHS   = A string containing one or more directory paths.  The
+;               individual paths are separated by commas, although in UNIX, 
+;               colons can also be used.  In other words, PATHS has the same 
+;               format as !PATH, except that commas can be used as a separator 
+;               regardless of operating system.
+;
+;               A leading $ can be used in any path to signal that what follows 
+;               is an environmental variable, but the $ is not necessary.    
+;               Environmental variables can themselves contain multiple paths.
+;
+; OUTPUT: 
+;      The result of the function is a string array of directories.
+;      Unless the NOCURRENT keyword is set, the first element of the array is 
+;      always the null string, representing the current directory.  All the 
+;      other directories will end in the correct separator character for the 
+;      current operating system.
+;
+; OPTIONAL INPUT KEYWORD:
+;      /NOCURRENT = If set, then the current directory (represented by
+;               the null string) will not automatically be prepended to the
+;               output.
+;
+; PROCEDURE CALLS:
+;      None.
+;
+; REVISION HISTORY:
+;       Version 1, William Thompson, GSFC, 6 May 1993.
+;               Added IDL for Windows compatibility.
+;       Version 2, William Thompson, GSFC, 16 May 1995
+;               Added keyword NOCURRENT
+;       Version 3, William Thompson, GSFC, 29 August 1995
+;               Modified to use OS_FAMILY
+;       Version 4, Zarro, GSFC, 4 August 1997
+;               Added trim to input
+;       Fix directory character on Macintosh system   A. Ferro   February 2000
+;       Use STRSPLIT instead of STR_SEP()   W. Landsman    July 2002
+;       Remove VMS support    W. Landsman   September 2006
+;-
+;
+        ON_ERROR, 2
+;
+;  Check the number of parameters:
+;
+        IF SIZE(PATHS,/TNAME) NE 'STRING' THEN MESSAGE,       $
+                'Syntax:  Result = BREAK_PATH( PATHS )'
+;
+;  Reformat PATHS into an array.  The first element is the null string.  In
+;  Unix, both the comma and colon character can be separators, so two passes
+;  are needed to extract everything.  The same is true for Microsoft Windows
+;  and semi-colons.
+;
+        sep = path_sep(/SEARCH_PATH) 
+        PATH = ['',STRSPLIT(PATHS,SEP + ',',/EXTRACT)] 
+;
+;  For each path, see if it is really an environment variable.  If so, then
+;  decompose the environmental variable into its constituent paths.
+;
+        I = 0
+        WHILE I LT N_ELEMENTS(PATH) DO BEGIN
+;
+;  First, try the path by itself.  Remove any trailing "/", "\", or ":"
+;  characters.  
+ 
+                CHAR = STRMID(PATH[I],STRLEN(PATH[I])-1,1)
+                IF (CHAR EQ '/') OR (CHAR EQ '\') OR (CHAR EQ ':') THEN $
+                        PATH[I] = STRMID(PATH[I],0,STRLEN(PATH[I])-1)
+                TEMP = PATH[I]
+                TEST = GETENV(TEMP)
+;
+;  If that doesn't yield anything, and the path begins with the $ prompt, then
+;  try what follows after the $.
+;
+                IF TEST EQ '' THEN IF STRMID(PATH[I],0,1) EQ '$' THEN BEGIN
+                        FOLLOWING = STRMID(TEMP,1,STRLEN(TEMP)-1)
+                        TEST = GETENV(FOLLOWING)
+		ENDIF	
+;
+;
+;  If something was found, then decompose this into whatever paths it may
+;  contain.
+;
+                IF TEST NE '' THEN BEGIN
+                        PTH = STRSPLIT(TEST,SEP+',',/EXTRACT) 
+;
+;  Insert this sublist into the main path list.
+;
+                        IF N_ELEMENTS(PATH) EQ 1 THEN BEGIN
+                                PATH = PTH
+                        END ELSE IF I EQ 0 THEN BEGIN
+                                PATH = [PTH,PATH[1:*]]
+                        END ELSE IF I EQ N_ELEMENTS(PATH)-1 THEN BEGIN
+                                PATH = [PATH[0:I-1],PTH]
+                        END ELSE BEGIN
+                                PATH = [PATH[0:I-1],PTH,PATH[I+1:*]]
+                        ENDELSE
+;
+;  Otherwise, check whether or not the path ends in the correct character.  
+;  In Unix, if the path does not end in "/" then append it.  Do the same with
+;  the "\" character in Microsoft Windows.  This step is only taken once the
+;  routine has completely decomposed this part of the path list.
+;
+                END ELSE BEGIN
+                        IF PATH[I] NE '' THEN BEGIN
+                            LAST = STRMID(PATH[I], STRLEN(PATH[I])-1, 1)
+                            CASE !VERSION.OS_FAMILY OF
+                                'Windows':  IF LAST NE '\' THEN $
+                                                PATH[I] = PATH[I] + '\'
+                                'MacOS': IF LAST NE ':' THEN $
+                                 			PATH[I] = PATH[I] + ':'
+                                ELSE:  IF LAST NE '/' THEN      $
+                                                PATH[I] = PATH[I] + '/'
+                            ENDCASE
+                        ENDIF
+;
+;  Advance to the next path, and continue.
+;
+                        I = I + 1
+                ENDELSE
+        ENDWHILE
+;
+;  If the NOCURRENT keyword was set, then remove the first element which
+;  represents the current directory
+;
+        IF KEYWORD_SET(NOCURRENT) AND (N_ELEMENTS(PATH) GT 1) THEN      $
+                PATH = PATH[1:*]
+;
+        RETURN, PATH
+        END
diff --git a/Code/script_idl_mv/astrolib/bsort.pro b/Code/script_idl_mv/astrolib/bsort.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b420f0a1c2f43f8dd642e6538df979e36421ab3e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/bsort.pro
@@ -0,0 +1,103 @@
+function Bsort, Array, Asort, INFO=info, REVERSE = rev
+;+
+; NAME:
+;       BSORT
+; PURPOSE:
+;       Function to sort data into ascending order, like a simple bubble sort.
+; EXPLANATION:
+;       Original subscript order is maintained when values are equal (stable sort).
+;       (This differs from the IDL SORT routine alone, which may rearrange 
+;       order for equal values)
+;
+;       A faster algorithm (radix sort) for numeric data is described at 
+;       http://idldatapoint.com/2012/04/19/an-lsd-radix-sort-algorithm-in-idl/
+;       and available at
+;       https://github.com/mgalloy/mglib/blob/master/src/analysis/mg_sort.pro
+; CALLING SEQUENCE:  
+;       result = bsort( array, [ asort, /INFO, /REVERSE ] )
+;
+; INPUT:
+;       Array - array to be sorted
+;
+; OUTPUT:
+;       result - sort subscripts are returned as function value
+;
+; OPTIONAL OUTPUT:
+;       Asort - sorted array
+;
+; OPTIONAL KEYWORD INPUTS:
+;       /REVERSE - if this keyword is set, and non-zero, then data is sorted
+;                 in descending order instead of ascending order.
+;       /INFO = optional keyword to cause brief message about # equal values.
+;
+; HISTORY
+;       written by F. Varosi Oct.90:
+;       uses WHERE to find equal clumps, instead of looping with IF ( EQ ).
+;       compatible with string arrays, test for degenerate array 
+;       20-MAY-1991     JKF/ACC via T AKE- return indexes if the array to 
+;                       be sorted has all equal values.
+;       Aug - 91  Added  REVERSE keyword   W. Landsman      
+;       Always return type LONG    W. Landsman     August 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+        N = N_elements( Array )
+        if N lt 1 then begin
+                print,'Input to BSORT must be an array'
+                return, [0L]
+           endif
+
+        if N lt 2 then begin
+            asort = array       ;MDM added 24-Sep-91
+            return,[0L]    ;Only 1 element
+        end
+;
+; sort array (in descending order if REVERSE keyword specified )
+;
+        subs = sort( Array )
+        if keyword_set( REV ) then subs = rotate(subs,5)  
+        Asort = Array[subs]
+;
+; now sort subscripts into ascending order
+; when more than one Asort has same value
+;
+             weq = where( (shift( Asort, -1 ) eq Asort) , Neq ) 
+
+        if keyword_set( info ) then $
+                message, strtrim( Neq, 2 ) + " equal values Located",/CON,/INF
+
+        if (Neq EQ n) then return,lindgen(n) ;Array is degenerate equal values
+
+        if (Neq GT 0) then begin
+
+                if (Neq GT 1) then begin              ;find clumps of equality
+
+                        wclump = where( (shift( weq, -1 ) - weq) GT 1, Nclump )
+                        Nclump++
+
+                  endif else Nclump = 1
+
+                if (Nclump LE 1) then begin
+                        Clump_Beg = 0
+                        Clump_End = Neq-1
+                  endif else begin
+                        Clump_Beg = [0,wclump+1]
+                        Clump_End = [wclump,Neq-1]
+                   endelse
+
+                weq_Beg = weq[ Clump_Beg ]              ;subscript ranges
+                weq_End = weq[ Clump_End ] + 1          ; of Asort equalities.
+
+                if keyword_set( info ) then message, strtrim( Nclump, 2 ) + $
+                                " clumps of equal values Located",/CON,/INF
+
+                for ic = 0L, Nclump-1 do begin          ;sort each clump.
+
+                        subic = subs[ weq_Beg[ic] : weq_End[ic] ]
+                        subs[ weq_Beg[ic] ] = subic[ sort( subic ) ]
+                  endfor
+
+                if N_params() GE 2 then Asort = Array[subs]     ;resort array.
+           endif
+
+return, subs
+end
diff --git a/Code/script_idl_mv/astrolib/calz_unred.pro b/Code/script_idl_mv/astrolib/calz_unred.pro
new file mode 100644
index 0000000000000000000000000000000000000000..de407895da29eb563ebab20e2b49d0a3e9e37615
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/calz_unred.pro
@@ -0,0 +1,79 @@
+pro calz_unred, wave, flux, ebv, funred, R_V = R_V
+;+
+; NAME:
+;     CALZ_UNRED
+; PURPOSE:
+;     Deredden a galaxy spectrum using the Calzetti et al. (2000) recipe
+; EXPLANATION:
+;     Calzetti et al.  (2000, ApJ 533, 682) developed a recipe for dereddening 
+;     the spectra of galaxies where massive stars dominate the radiation output,
+;     valid between 0.12 to 2.2 microns.     (CALZ_UNRED extrapolates between
+;     0.12 and 0.0912 microns.)   
+;
+; CALLING SEQUENCE:
+;     CALZ_UNRED, wave, flux, ebv, [ funred, R_V = ]
+; INPUT:
+;      WAVE - wavelength vector (Angstroms)
+;      FLUX - calibrated flux vector, same number of elements as WAVE
+;               If only 3 parameters are supplied, then this vector will
+;               updated on output to contain the dereddened flux.
+;      EBV  - color excess E(B-V), scalar.  If a negative EBV is supplied,
+;               then fluxes will be reddened rather than deredenned.
+;               Note that the supplied color excess should be that derived for 
+;               the stellar  continuum, EBV(stars), which is related to the 
+;               reddening derived from the gas, EBV(gas), via the Balmer 
+;               decrement by EBV(stars) = 0.44*EBV(gas)
+;
+; OUTPUT:
+;      FUNRED - unreddened flux vector, same units and number of elements
+;               as FLUX.   FUNRED values will be zeroed outside valid domain
+;               Calz_unred (0.0912 - 2.2 microns).
+;           
+; OPTIONAL INPUT KEYWORD:
+;       R_V - Ratio of total to selective extinction, default = 4.05.  
+;             Calzetti et al. (2000) estimate R_V = 4.05 +/- 0.80 from optical
+;             -IR observations of 4 starbursts.
+; EXAMPLE:
+;       Estimate how a flat galaxy spectrum (in wavelength) between 1200 A 
+;       and 3200 A is altered by a reddening of E(B-V) = 0.1.   
+;
+;       IDL> w = 1200 + findgen(40)*50      ;Create a wavelength vector
+;       IDL> f = w*0 + 1                    ;Create a "flat" flux vector
+;       IDL> calz_unred, w, f, -0.1, fnew  ;Redden (negative E(B-V)) flux vector
+;       IDL> plot,w,fnew                   
+;
+; NOTES:
+;       Use the 4 parameter calling sequence if you wish to save the 
+;               original flux vector.
+; PROCEDURE CALLS:
+;      POLY()
+; REVISION HISTORY:
+;       Written   W. Landsman        Raytheon  ITSS   December, 2000
+;-
+ On_error, 2
+
+ if N_params() LT 3 then begin
+     print,'Syntax: CALZ_UNRED, wave, flux, ebv, [ funred, R_V=]'
+    return
+ endif
+
+ if N_elements(R_V) EQ 0 then R_V = 4.05
+ w1 = where((wave GE 6300) AND (wave LE 22000), c1)
+ w2 = where((wave GE  912) AND (wave LT  6300), c2)
+ x  = 10000.0/wave                      ;Wavelength in inverse microns
+
+ IF (c1 + c2) NE N_elements(wave) THEN message,/INF, $
+       'Warning - some elements of wavelength vector outside valid domain'
+
+ klam = 0.0*flux
+
+ IF c1 GT 0 THEN $
+    klam[w1] = 2.659*(-1.857 + 1.040*x[w1]) + R_V
+   
+ IF c2 GT 0 THEN $
+    klam[w2] = 2.659*(poly(x[w2], [-2.156, 1.509d0, -0.198d0, 0.011d0])) + R_V
+ 
+ funred = flux*10.0^(0.4*klam*ebv)
+ if N_params() EQ 3 then flux = funred
+
+ end
diff --git a/Code/script_idl_mv/astrolib/ccm_unred.pro b/Code/script_idl_mv/astrolib/ccm_unred.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7aacc109c24ff300fe275f52be9ad62c74d64e1b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ccm_unred.pro
@@ -0,0 +1,147 @@
+pro ccm_UNRED, wave, flux, ebv, funred, R_V = r_v
+;+
+; NAME:
+;     CCM_UNRED
+; PURPOSE:
+;     Deredden a flux vector using the CCM 1989 parameterization 
+; EXPLANATION:
+;     The reddening curve is that of Cardelli, Clayton, and Mathis (1989 ApJ.
+;     345, 245), including the update for the near-UV given by O'Donnell 
+;     (1994, ApJ, 422, 158).   Parameterization is valid from the IR to the 
+;     far-UV (3.5 microns to 0.1 microns).    
+;
+;     Users might wish to consider using the alternate procedure FM_UNRED
+;     which uses the extinction curve of Fitzpatrick (1999).
+; CALLING SEQUENCE:
+;     CCM_UNRED, wave, flux, ebv, funred, [ R_V = ]      
+;             or 
+;     CCM_UNRED, wave, flux, ebv, [ R_V = ]      
+; INPUT:
+;     WAVE - wavelength vector (Angstroms)
+;     FLUX - calibrated flux vector, same number of elements as WAVE
+;             If only 3 parameters are supplied, then this vector will
+;             updated on output to contain the dereddened flux.
+;     EBV  - color excess E(B-V), scalar.  If a negative EBV is supplied,
+;             then fluxes will be reddened rather than deredenned.
+;
+; OUTPUT:
+;     FUNRED - unreddened flux vector, same units and number of elements
+;             as FLUX
+;
+; OPTIONAL INPUT KEYWORD
+;     R_V - scalar specifying the ratio of total selective extinction
+;             R(V) = A(V) / E(B - V).    If not specified, then R_V = 3.1
+;             Extreme values of R(V) range from 2.75 to 5.3
+;
+; EXAMPLE:
+;     Determine how a flat spectrum (in wavelength) between 1200 A and 3200 A
+;     is altered by a reddening of E(B-V) = 0.1.   Assume an "average"
+;     reddening for the diffuse interstellar medium (R(V) = 3.1)
+;
+;       IDL> w = 1200 + findgen(40)*50      ;Create a wavelength vector
+;       IDL> f = w*0 + 1                    ;Create a "flat" flux vector
+;       IDL> ccm_unred, w, f, -0.1, fnew  ;Redden (negative E(B-V)) flux vector
+;       IDL> plot,w,fnew                   
+;
+; NOTES:
+;     (1) The CCM curve shows good agreement with the Savage & Mathis (1979)
+;             ultraviolet curve shortward of 1400 A, but is probably
+;             preferable between 1200 and 1400 A.
+;     (2)  Many sightlines with peculiar ultraviolet interstellar extinction 
+;             can be represented with a CCM curve, if the proper value of 
+;             R(V) is supplied.
+;     (3)  Curve is extrapolated between 912 and 1000 A as suggested by
+;             Longo et al. (1989, ApJ, 339,474)
+;     (4) Use the 4 parameter calling sequence if you wish to save the 
+;               original flux vector.
+;     (5) Valencic et al. (2004, ApJ, 616, 912) revise the ultraviolet CCM
+;             curve (3.3 -- 8.0 um-1).    But since their revised curve does
+;             not connect smoothly with longer and shorter wavelengths, it is
+;             not included here.
+;
+; REVISION HISTORY:
+;       Written   W. Landsman        Hughes/STX   January, 1992
+;       Extrapolate curve for wavelengths between 900 and 1000 A   Dec. 1993
+;       Use updated coefficients for near-UV from O'Donnell   Feb 1994
+;       Allow 3 parameter calling sequence      April 1998
+;       Converted to IDLV5.0                    April 1998
+;-
+
+ On_error, 2
+
+ if N_params() LT 3 then begin
+     print,'Syntax: CCM_UNRED, wave, flux, ebv, funred,[ R_V = ]'
+     return
+ endif
+
+ if not keyword_set(R_V) then R_V = 3.1
+
+ x = 10000./ wave                ; Convert to inverse microns 
+ npts = N_elements( x )
+ a = fltarr(npts)  
+ b = fltarr(npts)
+;******************************
+
+ good = where( (x GT 0.3) and (x  LT 1.1), Ngood )       ;Infrared
+ if Ngood GT 0 then begin
+      a[good] =  0.574 * x[good]^(1.61)
+      b[good] = -0.527 * x[good]^(1.61)
+ endif
+
+;******************************
+
+ good = where( (x GE 1.1) and (x LT 3.3) ,Ngood)           ;Optical/NIR
+ if Ngood GT 0 then begin             ;Use new constants from O'Donnell (1994)
+     y = x[good] - 1.82
+;     c1 = [ 1. , 0.17699, -0.50447, -0.02427,  0.72085,    $ ;Original
+;                 0.01979, -0.77530,  0.32999 ]               ;coefficients
+;     c2 = [ 0.,  1.41338,  2.28305,  1.07233, -5.38434,    $ ;from CCM89
+;                -0.62251,  5.30260, -2.09002 ]
+      c1 = [ 1. , 0.104,   -0.609,    0.701,  1.137,    $    ;New coefficients
+                 -1.718,   -0.827,    1.647, -0.505 ]        ;from O'Donnell
+      c2 = [ 0.,  1.952,    2.908,   -3.989, -7.985,    $    ;(1994)
+                 11.102,    5.491,  -10.805,  3.347 ]
+
+     a[good] = poly( y, c1)
+     b[good] = poly( y, c2)
+ endif
+;******************************
+
+ good = where( (x GE 3.3) and (x LT 8) ,Ngood)           ;Mid-UV
+ if Ngood GT 0 then begin
+
+    y = x[good]
+    F_a = fltarr(Ngood)    & F_b = fltarr(Ngood)
+    good1 = where( (y GT 5.9), Ngood1 )
+    if Ngood1 GT 0 then begin
+       y1 = y[good1] - 5.9
+       F_a[ good1] = -0.04473 * y1^2 - 0.009779 * y1^3
+       F_b[ good1] =   0.2130 * y1^2  +  0.1207 * y1^3
+    endif
+    
+   a[good] =  1.752 - 0.316*y - (0.104 / ( (y-4.67)^2 + 0.341 )) + F_a
+   b[good] = -3.090 + 1.825*y + (1.206 / ( (y-4.62)^2 + 0.263 )) + F_b
+ endif
+
+;   *******************************
+
+ good = where( (x GE 8) and (x LE 11), Ngood )         ;Far-UV
+ if Ngood GT 0 then begin
+    y = x[good] - 8.
+    c1 = [ -1.073, -0.628,  0.137, -0.070 ]
+    c2 = [ 13.670,  4.257, -0.420,  0.374 ]
+    a[good] = poly(y, c1)
+    b[good] = poly(y, c2)
+ endif
+
+;   *******************************
+
+; Now apply extinction correction to input flux vector
+
+  A_V = R_V * EBV
+  A_lambda = A_V * (a + b/R_V)
+  if N_params() EQ 3 then flux = flux * 10.^(0.4*A_lambda) else $
+        funred = flux * 10.^(0.4*A_lambda)       ;Derive unreddened flux
+
+ return     
+ end                               
diff --git a/Code/script_idl_mv/astrolib/check_fits.pro b/Code/script_idl_mv/astrolib/check_fits.pro
new file mode 100644
index 0000000000000000000000000000000000000000..000bffa2545abc7ea33875483918444766bbee46
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/check_fits.pro
@@ -0,0 +1,227 @@
+pro check_FITS, im, hdr, dimen, idltype, UPDATE = update, NOTYPE = notype, $
+                   SDAS = sdas, FITS = fits, SILENT = silent, ERRMSG = errmsg
+;+
+; NAME:
+;       CHECK_FITS
+; PURPOSE:
+;       Check that keywords in a FITS header array match the associated data  
+; EXPLANATION:
+;       Given a FITS array IM, and a associated FITS header HDR, this
+;       procedure will check that
+;               (1) HDR is a string array, and IM is defined and numeric   
+;               (2) The NAXISi values in HDR are appropriate to the dimensions 
+;                   of IM
+;               (3) The BITPIX value in HDR is appropriate to the datatype of IM
+;       If the /UPDATE keyword is present, then the FITS header will be 
+;       modified, if necessary, to force agreement with the image array
+;
+; CALLING SEQUENCE:
+;       check_FITS, im, hdr, [ dimen, idltype, /UPDATE, /NOTYPE, /SILENT
+;                              ERRMSG = ]'
+;
+; INPUT PARAMETERS:
+;       IM -  FITS array, e.g. as read by READFITS
+;       HDR - FITS header (string array) associated with IM
+;
+; OPTIONAL OUTPUTS:
+;       dimen - vector containing actual array dimensions
+;       idltype- data type of the FITS array as specified in the IDL SIZE
+;               function (1 for BYTE, 2 for INTEGER*2, 3 for INTEGER*4, etc.)
+;
+; OPTIONAL KEYWORD INPUTS:
+;       /NOTYPE - If this keyword is set, then only agreement of the array
+;               dimensions with the FITS header are checked, and not the 
+;               data type.
+;       /UPDATE - If this keyword is set then the BITPIX, NAXIS and NAXISi
+;               FITS keywords will be updated to agree with the array
+;       /FITS, /SDAS -  these are obsolete keywords that now do nothing 
+;       /SILENT - If keyword is set and nonzero, the informational messages 
+;               will not be printed
+; OPTIONAL KEYWORD OUTPUT:
+;       ERRMSG  = If this keyword is present, then any error messages will be
+;                 returned to the user in this parameter rather than
+;                 depending on the MESSAGE routine in IDL.  If no errors are
+;                 encountered, then a null string is returned.  
+;
+; PROCEDURE:
+;       Program checks the NAXIS and NAXISi keywords in the header to
+;       see if they match the image array dimensions, and checks whether
+;       the BITPIX keyword agrees with the array type.
+;
+; PROCEDURE CALLS:
+;       FXADDPAR, FXPAR(), SXDELPAR
+; MODIFICATION HISTORY:
+;       Written, December 1991  W. Landsman Hughes/STX to replace CHKIMHD
+;       No error returned if NAXIS=0 and IM is a scalar   W. Landsman  Feb 93
+;       Fixed bug for REAL*8 STSDAS data W. Landsman July 93
+;       Make sure NAXIS agrees with NAXISi  W. Landsman  October 93
+;        Converted to IDL V5.0   W. Landsman   September 1997
+;       Allow unsigned data types   W. Landsman December 1999
+;       Allow BZERO = 0 for unsigned data types   W. Landsman January 2000
+;       Added ERRMSG keyword, W. Landsman February 2000
+;       Use FXADDPAR to put NAXISi in proper order   W. Landsman August 2000
+;       Improper FXADDPAR call for DATATYPE keyword  W. Landsman December 2000
+;       Remove explicit setting of obsolete !err W. Landsman February 2004
+;       Remove SDAS support   W. Landsman       November 2006
+;       Fix dimension errors introduced Nov 2006
+;       Work again for null arrays W. Landsman/E. Hivon May 2007
+;       Use V6.0 notation  W.L.  Feb. 2011 
+;- 
+ compile_opt idl2
+ On_error,2
+
+ if N_params() LT 2 then begin
+    print,'Syntax - CHECK_FITS, im, hdr, dimen, idltype, '
+    print,'            [ /UPDATE, /NOTYPE, ERRMSG=, /SILENT ]'
+    return
+ endif
+
+ if arg_present(errmsg) then errmsg = ''       
+
+ if size(hdr,/TNAME) NE 'STRING' then begin        ;Is hdr of string type?
+        message= 'FITS header is not a string array'
+        if  N_elements(ERRMSG) GT 0 then errmsg = message else $
+             message, 'ERROR - ' + message, /CON
+             return 
+ endif
+
+ im_info = size(im,/struc)
+ ndimen = im_info.n_dimensions
+ if ndimen GT 0 then dimen = im_info.dimensions[0:ndimen-1]
+ idltype = im_info.type
+
+ 
+ nax = fxpar( hdr, 'NAXIS', Count = N_naxis ) 
+ if N_naxis EQ 0 then begin
+        message = 'FITS header missing NAXIS keyword'
+        if  N_elements(errmsg) GT 0 then errmsg = message else $
+             message,'ERROR - ' + message,/CON 
+             return 
+ endif
+        
+ if ndimen EQ 0  then $             ;Null primary array
+     if nax EQ 0 then return else begin
+         message = 'FITS array is not defined'
+         if  N_elements(errmsg) GT 0 then errmsg = message else $
+             message,'ERROR - ' +message,/con 
+             return 
+     endelse
+
+ 
+ naxis = fxpar( hdr, 'NAXIS*')
+ naxi = N_elements( naxis )
+ if nax GT naxi then begin                 ;Does NAXIS agree with # of NAXISi?
+        if keyword_set( UPDATE) then begin
+                fxaddpar, hdr, 'NAXIS', naxi
+                if ~keyword_set(SILENT) then message, /INF, $
+        'NAXIS changed from ' + strtrim(nax,2) + ' to ' + strtrim(naxi,2)
+        endif else begin 
+                message =  'FITS header has NAXIS = ' + strtrim(nax,2) + $
+                ', but only ' + strtrim(naxi, 2) + ' axes defined'
+                if  N_elements(ERRMSG) GT 0 then errmsg = message else $
+                    message, 'ERROR - ' + message
+                return
+        endelse
+ endif
+
+ last = naxi-1                        ;Remove degenerate dimensions
+ while ( (naxis[last] EQ 1) && (last GE 1) ) do last--
+ if last NE nax-1 then begin
+     naxis = naxis[ 0:last]
+ endif 
+
+ if ( ndimen NE last + 1 ) then begin
+    if ~keyword_set( UPDATE) THEN begin
+        message = $
+        '# of NAXISi keywords does not match # of array dimensions'
+        if  N_elements(ERRMSG) GT 0 then errmsg = message else $
+                                     message,'ERROR - ' + message,/CON 
+        return 
+ 
+     endif else goto, DIMEN_ERROR
+ endif
+
+ for i = 0,last do begin
+      if naxis[i] NE dimen[i] then begin
+      if ~keyword_set( UPDATE ) then begin
+          message =  'Invalid NAXIS' + strtrim( i+1,2 ) + $
+	             ' keyword value in header'
+          if  N_elements(ERRMSG) GT 0 then errmsg = message else $ 
+                                       message,'ERROR - ' + message,/CON
+          return 
+      endif else goto, DIMEN_ERROR
+    endif
+ endfor
+
+BITPIX:     
+
+ if ~keyword_set( NOTYPE ) then begin
+
+ 
+  bitpix = fxpar( hdr, 'BITPIX')
+  
+    case idltype of
+
+     1: if bitpix NE 8 then goto, BITPIX_ERROR
+     2: if bitpix NE 16 then goto, BITPIX_ERROR  
+     4: if bitpix NE -32 then goto, BITPIX_ERROR       
+     3: if bitpix NE 32 then goto, BITPIX_ERROR 
+     5: if bitpix NE -64 then goto, BITPIX_ERROR 
+     12:if bitpix NE 16 then goto, BITPIX_ERROR
+     13: if bitpix NE 32 then goto, BITPIX_ERROR
+     
+     else: begin
+              message = 'Data array is not a valid FITS datatype'
+             if  N_elements(ERRMSG) GT 0 then errmsg = message else $
+                                          message,'ERROR - ' + message,/CON
+             return 
+      end
+
+   endcase
+
+ endif
+
+ return
+
+BITPIX_ERROR:
+    if keyword_set( UPDATE ) then begin
+    bpix = [0, 8, 16, 32, -32, -64, 32, 0, 0, 0, 0, 0, 16,32 ]
+    comm = ['',' Character or unsigned binary integer', $
+               ' 16-bit twos complement binary integer', $
+               ' 32-bit twos complement binary integer', $
+               ' IEEE single precision floating point', $
+               ' IEEE double precision floating point', $
+               ' 32-bit twos complement binary integer','','','','','', $
+               ' 16-bit unsigned binary integer', $
+               ' 32-bit unsigned binary integer' ]
+    bitpix = bpix[idltype]
+    comment = comm[idltype]
+    if ~keyword_set(SILENT) then message, /INF, $
+        'BITPIX value of ' + strtrim(bitpix,2) +  ' added to FITS header'
+    fxaddpar, hdr, 'BITPIX', bitpix, comment
+    return
+
+  endif else begin 
+       message = 'BITPIX value of ' + strtrim(bitpix,2) + $
+                 ' in FITS header does not match array'
+      if  N_elements(ERRMSG) GT 0  then errmsg = message else  $
+          message,'ERROR - ' + message,/CON
+      return
+ endelse
+
+DIMEN_ERROR:
+   if keyword_set( UPDATE ) then begin
+        fxaddpar, hdr, 'NAXIS', ndimen, before = 'NAXIS1'
+	naxis = 'NAXIS' + strtrim(indgen(ndimen)+1,2)
+        for i = 1, ndimen do fxaddpar, hdr, naxis[i-1], dimen[i-1], $
+                'Number of positions along axis ' + strtrim(i,2), $
+                after = 'NAXIS' + strtrim(i-1,2)          
+        if naxi GT ndimen then begin
+                for i = ndimen+1, naxi do sxdelpar, hdr, 'NAXIS'+strtrim(i,2)
+        endif
+        if ~keyword_set(SILENT) then message, /INF, $
+                'NAXIS keywords in FITS header have been updated'
+        goto, BITPIX
+   endif
+
+ end
diff --git a/Code/script_idl_mv/astrolib/checksum32.pro b/Code/script_idl_mv/astrolib/checksum32.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2e23c540a26fc3080ebe540547ae4a4df0063cb8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/checksum32.pro
@@ -0,0 +1,125 @@
+pro checksum32, array, checksum, FROM_IEEE = from_IEEE, NOSAVE = nosave
+;+
+; NAME:
+;       CHECKSUM32
+;
+; PURPOSE:
+;       To compute the 32bit checksum of an array (ones-complement arithmetic)
+;
+; EXPLANATION:
+;       The 32bit checksum is adopted in the FITS Checksum convention
+;       http://fits.gsfc.nasa.gov/registry/checksum.html
+;
+; CALLING SEQUENCE:
+;       CHECKSUM32, array, checksum, [/FROM_IEEE, /NoSAVE]
+;
+; INPUTS:
+;       array - any numeric idl array.  If the number of bytes in the array is 
+;               not a multiple of four then it is padded with zeros internally
+;               (the array is returned unchanged).   Convert a string array 
+;               (e.g. a FITS header) to bytes prior to calling CHECKSUM32.
+;
+; OUTPUTS:
+;       checksum - unsigned long scalar, giving sum of array elements using 
+;                  ones-complement arithmetic
+; OPTIONAL INPUT KEYWORD:
+;
+;      /FROM_IEEE - If this keyword is set, then the input is assumed to be in
+;           big endian format (e.g. an untranslated FITS array).   This keyword
+;           only has an effect on little endian machines (e.g. Linux boxes).
+;
+;      /NoSAVE - if set, then the input array is not saved upon exiting.   Use 
+;           the /NoSave keyword to save time if the input array is not needed 
+;           in further computations. 
+; METHOD:
+;       Uses TOTAL() to sum the array into an unsigned integer variable.  The
+;       overflow bits beyond 2^32 are then shifted back to the least significant
+;       bits.    The summing is done in chunks. of 2^31 numbers to avoid loss
+;      of precision.    Adapted from FORTRAN code in
+;      heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/general/checksum/node30.html
+;
+;      Could probably be done in a cleverer way (similar to the C
+;      implementation) but then the array-oriented TOTAL() function could not 
+;      be used.
+; RESTRICTIONS:
+;       (1) Not valid for object or pointer data types
+; EXAMPLE:
+;       Find the 32 bit checksum of the array x = findgen(35)
+;
+;       IDL> checksum32, x, s    ===> s =  2920022024
+; FUNCTION CALLED:
+;       HOST_TO_IEEE, IS_IEEE_BIG(), N_BYTES()
+; MODIFICATION HISTORY:
+;       Written    W. Landsman          June 2001
+;       Work correctly on little endian machines, added /FROM_IEEE and /NoSave
+;                  W. Landsman          November 2002
+;       Pad with zeros when array size not a multiple of 4 W.Landsman Aug 2003
+;       Always copy to new array, somewhat slower but more robust algorithm
+;           especially for Linux boxes   W. Landsman Sep. 2004 
+;       Sep. 2004 update not implemented correctly (sigh) W. Landsman Dec 2004         
+;       No need to byteswap 4 byte datatypes on little endian W. L. May 2009
+;       Use /INTEGER keyword to TOTAL() function W.L. June 2009
+;       
+;-
+ if N_params() LT 2 then begin
+      print,'Syntax - CHECKSUM32, array, checksum, /FROM_IEEE, /NoSAVE'
+      return
+ endif
+ idltype = size(array,/type)
+
+; Convert data to byte.  If array size is not a multiple of 4, then we pad with
+; zeros 
+
+ N = N_bytes(array)
+ Nremain = N mod 4
+ if Nremain GT 0 then begin 
+     if keyword_set(nosave) then $
+           uarray = [ byte(temporary(array),0,N), bytarr(4-Nremain)]  $
+           else uarray =  [ byte(array,0,N), bytarr(4-Nremain)] 
+      N = N + 4 - Nremain 
+ endif else  begin 
+      if keyword_set(nosave) then $
+           uarray =  byte( temporary(array) ,0,N) else $
+           uarray =  byte( array ,0,N) 
+ endelse
+ 	    
+; Get maximum number of base 2 digits available in an unsigned long array, 
+; without losing any precision.    Since we will sum unsigned longwords, the 
+; original  array must be byteswapped as longwords.
+
+ maxnum = long64(2)^31       
+ Niter =  (N-1)/maxnum
+ checksum = long64(0)
+  word32 =  long64(2)^32
+  bswap  = ~is_ieee_big()
+  if bswap then begin
+       if ~keyword_set( from_ieee) then begin 
+            if (idltype NE 3) && (idltype NE 4) then begin 
+	         if idltype NE 1 then host_to_ieee, uarray,idltype=idltype   
+                 byteorder,uarray,/NTOHL
+	   endif	 
+       endif else byteorder,uarray,/NTOHL	     
+ endif
+ 
+ for i=0, Niter do begin
+
+   if i EQ Niter then begin 
+           nbyte = (N mod maxnum) 
+           if nbyte EQ 0 then nbyte = maxnum
+   endif else nbyte = maxnum
+
+   checksum += total(ulong(  uarray,maxnum*i,nbyte/4), /integer)
+; Fold any overflow bits beyond 32 back into the word.
+
+   hibits = long(checksum/word32)
+   while hibits GT 0 do begin
+     checksum = checksum - (hibits*word32) + hibits    
+     hibits = long(checksum/word32)
+  endwhile
+
+   checksum = ulong(checksum)
+
+ endfor
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/cic.pro b/Code/script_idl_mv/astrolib/cic.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b1ff45bf3ad6dd0c11bb45c3d963c95d21bb7e08
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/cic.pro
@@ -0,0 +1,417 @@
+FUNCTION cic,value,posx,nx,posy,ny,posz,nz, $
+             AVERAGE=average,WRAPAROUND=wraparound,ISOLATED=isolated, $
+             NO_MESSAGE=no_message
+;+
+; NAME:
+;       CIC
+;
+; PURPOSE:
+;       Interpolate an irregularly sampled field using Cloud in Cell method
+;
+; EXPLANATION:
+;       This function interpolates an irregularly sampled field to a
+;       regular grid using Cloud In Cell (nearest grid point gets
+;       weight 1-dngp, point on other side gets weight dngp, where
+;       dngp is the distance to the nearest grid point in units of the
+;       cell size).
+;
+; CATEGORY:
+;       Mathematical functions, Interpolation
+;
+; CALLING SEQUENCE:
+;       Result = CIC, VALUE, POSX, NX[, POSY, NY, POSZ, NZ, 
+;                     AVERAGE = average, WRAPAROUND =  wraparound,
+;                     ISOLATED = isolated, NO_MESSAGE = no_message]
+;
+; INPUTS:
+;       VALUE: Array of sample weights (field values). For e.g. a
+;              temperature field this would be the temperature and the
+;              keyword AVERAGE should be set. For e.g. a density field
+;              this could be either the particle mass (AVERAGE should
+;              not be set) or the density (AVERAGE should be set).
+;       POSX:  Array of X coordinates of field samples, unit indices: [0,NX>.
+;       NX:    Desired number of grid points in X-direction.
+;       
+; OPTIONAL INPUTS:
+;      POSY: Array of Y coordinates of field samples, unit indices: [0,NY>.
+;      NY:   Desired number of grid points in Y-direction.
+;      POSZ: Array of Z coordinates of field samples, unit indices: [0,NZ>.
+;      NZ:   Desired number of grid points in Z-direction.
+;
+; KEYWORD PARAMETERS:
+;       AVERAGE:    Set this keyword if the nodes contain field samples
+;                   (e.g. a temperature field). The value at each grid
+;                   point will then be the weighted average of all the
+;                   samples allocated to it. If this keyword is not
+;                   set, the value at each grid point will be the
+;                   weighted sum of all the nodes allocated to it
+;                   (e.g. for a density field from a distribution of
+;                   particles). (D=0). 
+;       WRAPAROUND: Set this keyword if you want the first grid point
+;                   to contain samples of both sides of the volume
+;                   (see below).
+;       ISOLATED:   Set this keyword if the data is isolated, i.e. not
+;                   periodic. In that case total `mass' is not conserved.
+;                   This keyword cannot be used in combination with the
+;                   keyword WRAPAROUND.
+;       NO_MESSAGE: Suppress informational messages.
+;
+; Example of default allocation of nearest grid points: n0=4, *=gridpoint.
+;
+;     0   1   2   3     Index of gridpoints
+;     *   *   *   *     Grid points
+;   |---|---|---|---|   Range allocated to gridpoints ([0.0,1.0> --> 0, etc.)
+;   0   1   2   3   4   posx
+;
+; Example of ngp allocation for WRAPAROUND: n0=4, *=gridpoint.
+;
+;   0   1   2   3         Index of gridpoints
+;   *   *   *   *         Grid points
+; |---|---|---|---|--     Range allocated to gridpoints ([0.5,1.5> --> 1, etc.)
+;   0   1   2   3   4=0   posx
+;
+;
+; OUTPUTS:
+;       Prints that a CIC interpolation is being performed of x
+;       samples to y grid points, unless NO_MESSAGE is set. 
+;
+; RESTRICTIONS:
+;       Field data is assumed to be periodic with the sampled volume
+;       the basic cell, unless ISOLATED is set.
+;       All input arrays must have the same dimensions.
+;       Position coordinates should be in `index units' of the
+;       desired grid: POSX=[0,NX>, etc.
+;       Keywords ISOLATED and WRAPAROUND cannot both be set.
+;
+; PROCEDURE:
+;       Nearest grid point is determined for each sample.
+;       CIC weights are computed for each sample.
+;       Samples are interpolated to the grid.
+;       Grid point values are computed (sum or average of samples).
+; NOTES:
+;       Use tsc.pro for a higher-order interpolation scheme, ngp.pro for a lower
+;       order interpolation scheme.    A standard reference for these 
+;       interpolation methods is:   R.W. Hockney and J.W. Eastwood, Computer 
+;       Simulations Using Particles (New York: McGraw-Hill, 1981).
+; EXAMPLE:
+;       nx=20
+;       ny=10
+;       posx=randomu(s,1000)
+;       posy=randomu(s,1000)
+;       value=posx^2+posy^2
+;       field=cic(value,posx*nx,nx,posy*ny,ny,/average)
+;       surface,field,/lego
+;
+; MODIFICATION HISTORY:
+;       Written by Joop Schaye, Feb 1999.
+;       Avoid integer overflow for large dimensions P.Riley/W.Landsman Dec. 1999
+;-
+
+nrsamples=n_elements(value)
+nparams=n_params()
+dim=(nparams-1)/2
+
+IF dim LE 2 THEN BEGIN
+    nz=1
+    IF dim EQ 1 THEN ny=1
+ENDIF
+nxny=long(nx)*long(ny)
+
+
+;---------------------
+; Some error handling.
+;---------------------
+
+on_error,2  ; Return to caller if an error occurs.
+
+IF NOT (nparams EQ 3 OR nparams EQ 5 OR nparams EQ 7) THEN BEGIN
+    message,'Incorrect number of arguments!',/continue
+    message,'Syntax: CIC, VALUE, POSX, NX[, POSY, NY, POSZ, NZ,' + $
+      ' AVERAGE = average, PERIODIC =  periodic]'
+ENDIF 
+
+IF (nrsamples NE n_elements(posx)) OR $
+  (dim GE 2 AND nrsamples NE n_elements(posy)) OR $
+  (dim EQ 3 AND nrsamples NE n_elements(posz)) THEN $
+  message,'Input arrays must have the same dimensions!'
+
+IF keyword_set(isolated) AND keyword_set(wraparound) THEN $
+  message,'Keywords ISOLATED and WRAPAROUND cannot both be set!'
+
+IF NOT keyword_set(no_message) THEN $
+  print,'Interpolating ' + strtrim(string(nrsamples,format='(i10)'),1) $
+  + ' samples to ' + strtrim(string(nxny*nz,format='(i10)'),1) + $
+  ' grid points using CIC...'
+
+
+;-----------------------
+; Calculate CIC weights.
+;-----------------------
+
+; Compute weights per axis, in order to reduce memory (everything
+; needs to be in memory if we compute all nearest grid points first).
+
+;*************
+; X-direction.
+;*************
+
+; Coordinates of nearest grid point (ngp).
+IF keyword_set(wraparound) THEN ngx=fix(posx+0.5) $
+ELSE ngx=fix(posx)+0.5
+
+; Distance from sample to ngp.
+dngx=ngx-posx
+
+; Index of ngp.
+IF keyword_set(wraparound) THEN kx1=temporary(ngx) $
+ELSE kx1=temporary(ngx)-0.5
+; Weight of ngp.
+wx1=1.0-abs(dngx)
+
+; Other side.
+left=where(dngx LT 0.0,nrleft)  ; samples with ngp to the left.
+; The following is only correct if x(ngp)>posx (ngp to the right).
+kx2=kx1-1
+; Correct points where x(ngp)<posx (ngp to the left).
+IF nrleft NE 0 THEN kx2[left]=kx2[left]+2
+wx2=abs(temporary(dngx))
+
+; Free memory.
+left=0
+
+; Periodic boundary conditions.
+; Note that kx2 can be both -1 and nx at this point, regardless of
+; wraparound or not. The reason is that dngx can be exactly zero.
+bad=where(kx2 EQ -1,count)
+IF count NE 0 THEN BEGIN
+    kx2[bad]=nx-1
+    IF keyword_set(isolated) THEN wx2[bad]=0.
+ENDIF
+bad=where(kx2 EQ nx,count)
+IF count NE 0 THEN BEGIN
+    kx2[bad]=0
+    IF keyword_set(isolated) THEN wx2[bad]=0.
+ENDIF
+IF keyword_set(wraparound) THEN BEGIN
+    bad=where(kx1 EQ nx,count)
+    IF count NE 0 THEN kx1[bad]=0
+ENDIF
+bad=0  ; Free memory.
+
+
+;*************
+; Y-direction.
+;*************
+
+IF dim GE 2 THEN BEGIN 
+    ; Coordinates of nearest grid point (ngp).
+    IF keyword_set(wraparound) THEN ngy=fix(posy+0.5) $
+    ELSE ngy=fix(posy)+0.5
+
+    ; Distance from sample to ngp.
+    dngy=ngy-posy
+
+    ; Index of ngp.
+    IF keyword_set(wraparound) THEN ky1=temporary(ngy) $
+    ELSE ky1=temporary(ngy)-0.5
+    ; Weight of ngp.
+    wy1=1.0-abs(dngy)
+
+    ; Other side.
+    left=where(dngy LT 0.0,nrleft) ; samples with ngp to the left.
+    ; The following is only correct if y(ngp)>posy (ngp to the right).
+    ky2=ky1-1
+    ; Correct points where y(ngp)<posy (ngp to the left).
+    IF nrleft NE 0 THEN ky2[left]=ky2[left]+2
+    wy2=abs(temporary(dngy))
+
+    ; Free memory.
+    left=0
+
+    ; Periodic boundary conditions.
+    bad=where(ky2 EQ -1,count)
+    IF count NE 0 THEN BEGIN
+        ky2[bad]=ny-1
+        IF keyword_set(isolated) THEN wy2[bad]=0.
+    ENDIF
+    bad=where(ky2 EQ ny,count)
+    IF count NE 0 THEN BEGIN
+        ky2[bad]=0
+        IF keyword_set(isolated) THEN wy2[bad]=0.
+    ENDIF
+    IF keyword_set(wraparound) THEN BEGIN
+        bad=where(ky1 EQ ny,count)
+        IF count NE 0 THEN ky1[bad]=0
+    ENDIF
+    bad=0  ; Free memory.
+ENDIF ELSE BEGIN
+    ky1=0
+    ky2=0
+    wy1=1
+    wy2=1
+ENDELSE
+
+
+;*************
+; Z-direction.
+;*************
+
+IF dim EQ 3 THEN BEGIN
+    ; Coordinates of nearest grid point (ngp).
+    IF keyword_set(wraparound) THEN ngz=fix(posz+0.5) $
+    ELSE ngz=fix(posz)+0.5
+
+    ; Distance from sample to ngp.
+    dngz=ngz-posz
+
+    ; Index of ngp.
+    IF keyword_set(wraparound) THEN kz1=temporary(ngz) $
+    ELSE kz1=temporary(ngz)-0.5
+    ; Weight of ngp.
+    wz1=1.0-abs(dngz)
+
+    ; Other side.
+    left=where(dngz LT 0.0,nrleft) ; samples with ngp to the left.
+    ; The following is only correct if z(ngp)>posz (ngp to the right).
+    kz2=kz1-1
+    ; Correct points where z(ngp)<posz (ngp to the left).
+    IF nrleft NE 0 THEN kz2[left]=kz2[left]+2
+    wz2=abs(temporary(dngz))
+
+    ; Free memory.
+    left=0
+
+    ; Periodic boundary conditions.
+    bad=where(kz2 EQ -1,count)
+    IF count NE 0 THEN BEGIN
+        kz2[bad]=nz-1
+        IF keyword_set(isolated) THEN wz2[bad]=0.
+    ENDIF
+    bad=where(kz2 EQ nz,count)
+    IF count NE 0 THEN BEGIN
+        kz2[bad]=0
+        IF keyword_set(isolated) THEN wz2[bad]=0.
+    ENDIF
+    IF keyword_set(wraparound) THEN BEGIN
+        bad=where(kz1 EQ nz,count)
+        IF count NE 0 THEN kz1[bad]=0
+    ENDIF
+    bad=0  ; Free memory.
+ENDIF ELSE BEGIN
+    kz1=0
+    kz2=0
+    wz1=1
+    wz2=1
+ENDELSE
+
+
+;-----------------------------
+; Interpolate samples to grid.
+;-----------------------------
+
+field=fltarr(nx,ny,nz)
+IF keyword_set(average) THEN totcicweight=fltarr(nx,ny,nz)
+
+; Cicweight adds up all cic weights allocated to a grid point, we need
+; to keep track of this in order to compute the temperature.
+; Note that total(cicweight) is equal to nrsamples and that
+; total(field)=n0^3 if sph.plot NE 'sph,temp' (not 1 because we use
+; posx=[0,n0> --> cube length different from EDFW paper).
+
+index=kx1+ky1*nx+kz1*nxny
+cicweight=wx1*wy1*wz1
+IF keyword_set(average) THEN BEGIN
+    FOR j=0l,nrsamples-1l DO BEGIN
+        field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+        totcicweight[index[j]]=totcicweight[index[j]]+cicweight[j]
+    ENDFOR
+ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+  field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+index=kx2+ky1*nx+kz1*nxny
+cicweight=wx2*wy1*wz1
+IF keyword_set(average) THEN BEGIN
+    FOR j=0l,nrsamples-1l DO BEGIN
+        field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+        totcicweight[index[j]]=totcicweight[index[j]]+cicweight[j]
+    ENDFOR
+ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+  field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+
+IF dim GE 2 THEN BEGIN
+    index=kx1+ky2*nx+kz1*nxny
+    cicweight=wx1*wy2*wz1
+    IF keyword_set(average) THEN BEGIN
+        FOR j=0l,nrsamples-1l DO BEGIN
+            field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+            totcicweight[index[j]]=totcicweight[index[j]]+cicweight[j]
+        ENDFOR
+    ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+      field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+    index=kx2+ky2*nx+kz1*nxny
+    cicweight=wx2*wy2*wz1
+    IF keyword_set(average) THEN BEGIN
+        FOR j=0l,nrsamples-1l DO BEGIN
+            field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+            totcicweight[index[j]]=totcicweight[index[j]]+cicweight[j]
+        ENDFOR
+    ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+      field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+
+    IF dim EQ 3 THEN BEGIN
+        index=kx1+ky1*nx+kz2*nxny
+        cicweight=wx1*wy1*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+                totcicweight[index[j]]=totcicweight[index[j]]+cicweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+        index=kx2+ky1*nx+kz2*nxny
+        cicweight=wx2*wy1*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+                totcicweight[index[j]]=totcicweight[index[j]]+cicweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+        index=kx1+ky2*nx+kz2*nxny
+        cicweight=wx1*wy2*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+                totcicweight[index[j]]=totcicweight[index[j]]+cicweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+        index=kx2+ky2*nx+kz2*nxny
+        cicweight=wx2*wy2*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+                totcicweight[index[j]]=totcicweight[index[j]]+cicweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+cicweight[j]*value[j]
+    ENDIF
+
+ENDIF
+
+; Free memory (no need to free any more local arrays, will not lower
+; maximum memory usage).
+index=0
+
+
+;--------------------------
+; Compute weighted average.
+;--------------------------
+
+IF keyword_set(average) THEN BEGIN
+    good=where(totcicweight NE 0,nrgood)
+    field[good]=temporary(field[good])/temporary(totcicweight[good])
+ENDIF
+
+return,field
+
+END  ; End of function cic.
diff --git a/Code/script_idl_mv/astrolib/cirrange.pro b/Code/script_idl_mv/astrolib/cirrange.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e2044059969e50d36fe534a8b362b87d30db108b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/cirrange.pro
@@ -0,0 +1,49 @@
+PRO cirrange, ang, RADIANS=rad
+;+
+; NAME:
+;       CIRRANGE
+; PURPOSE:
+;       To force an angle into the range 0 <= ang < 360.
+; CALLING SEQUENCE:
+;       CIRRANGE, ang, [/RADIANS]
+;
+; INPUTS/OUTPUT:
+;       ang     - The angle to modify, in degrees.  This parameter is
+;                 changed by this procedure.  Can be a scalar or vector.
+;                 The type of ANG is always converted to double precision
+;                 on output.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /RADIANS - If present and non-zero, the angle is specified in
+;                 radians rather than degrees.  It is forced into the range
+;                 0 <= ang < 2 PI.
+; PROCEDURE:
+;       The angle is transformed between -360 and 360 using the MOD operator.   
+;       Negative values (if any) are then transformed between 0 and 360
+; MODIFICATION HISTORY:
+;       Written by Michael R. Greason, Hughes STX, 10 February 1994.
+;       Get rid of WHILE loop, W. Landsman, Hughes STX, May 1996
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+ if N_params() LT 1 then begin 
+        print, 'Syntax:  CIRRANGE, ang, [ /RADIANS ]'
+        return
+ endif
+
+;  Determine the additive constant.
+
+ if keyword_set(RAD) then cnst = !dpi * 2.d $
+                     else cnst = 360.d
+
+; Deal with the lower limit.
+
+ ang = ang mod cnst
+
+; Deal with negative values, if any
+ 
+ neg = where(ang LT 0., Nneg)
+ if Nneg GT 0 then ang[neg] = ang[neg] + cnst
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/cleanplot.pro b/Code/script_idl_mv/astrolib/cleanplot.pro
new file mode 100644
index 0000000000000000000000000000000000000000..abcd1b11b7b929ea3f39ff32d4e331396d6e039a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/cleanplot.pro
@@ -0,0 +1,150 @@
+Pro CleanPlot, silent=silent, ShowOnly = showonly ;Reset System  Variables 
+;+
+; NAME:
+;       CLEANPLOT
+; PURPOSE:
+;       Reset all plotting system variables (!P,!X,!Y,!Z) to their default values
+; EXPLANATION:
+;       Reset all system variables (!P,!X,!Y,!Z) which are set by the user
+;       and which affect plotting to their default values.
+;
+; CALLING SEQUENCE:
+;       Cleanplot, [ /Silent, /ShowOnly]
+;
+; INPUTS:       
+;       None
+;
+; OPTIONAL KEYWORD INPUT:
+;       /SHOWONLY - If set, then CLEANPLOT will display the plotting system
+;                 variables with nondefault values, but it will not reset them.
+;               
+;       /SILENT - If set, then CLEANPLOT will not display a message giving the 
+;                 the system variables tags being reset.    One cannot set 
+;                  both /SILENT and /SHOWONLY
+; OUTPUTS:      
+;       None
+;
+; SIDE EFFECTS: 
+;       The system variables that concern plotting are reset to their default
+;       values.  A message is output for each variable changed.
+;       The !P.CLIP and CRANGE, S, WINDOW, and REGION fields of the
+;       !X, !Y, and !Z system variables are not checked since these are
+;       set by the graphics device and not by the user.   
+;
+; PROCEDURE:
+;       This does NOT reset the plotting device.
+;       This does not change any system variables that don't control plotting.
+;
+; RESTRICTIONS:
+;       If user default values for !P, !X, !Y and !Z are different from
+;       the defaults adopted below, user should change P_old etc accordingly
+;
+; MODIFICATION HISTORY:
+;       Written IDL Version 2.3.0  W. Landsman & K. Venkatakrishna May '92
+;       Handle new system variables in V3.0.0     W. Landsman   Dec 92
+;       Assume user has at least V3.0.0           W. Landsman   August 95
+;       V5.0 has 60 instead of 30 TICKV values    W. Landsman   Sep. 97
+;       Change !D.N_COLORS to !D.TABLE_SIZE for 24 bit displays
+;               W. Landsman  April 1998
+;       Added silent keyword to supress output & modified X_old to
+;       handle the new !X and !Y tags in IDL 5.4   S. Penton     July 2000
+;       Test for visual depth if > V5.1   W. Landsman     July 2000
+;       Macs can report a visual depth of 32  W. Landsman  March 2001
+;       Call device,get_visual_depth only for device which allow it 
+;                W. Landsman  June 2001
+;       Default !P.color is 16777215 for 16 bit systems 
+;                       W. Landsman/M. Hadfield   November 2001 
+;       Added ShowOnly keyword   W. Landsman      April 2002
+;       Use V6.0 notation W. Landsman April 2011
+;       
+;-
+ compile_opt idl2
+
+ On_error,2
+ silent =  keyword_set(silent) 
+ if keyword_set(showonly) then begin
+     print,'Current Plotting System Variables with non-default Values'
+     clearing = ''
+     oldvalue = ' '
+     reset = 0
+ endif else begin
+     clearing = 'Clearing '
+     oldvalue = ', old value '
+     reset = 1
+ end
+; For !X, !Y, and !Z we will assume that the default values except for MARGIN are 
+; either 0 or '', while for !P we explicitly write all default values in P_old
+
+ P_old = { BACKGROUND: 0L,CHARSIZE:0.0, CHARTHICK:0.0,  $
+          CLIP:[0L,0,639,511,0,0], $                      ;Not used
+          COLOR : !D.TABLE_SIZE-1, FONT: -1L, LINESTYLE: 0L, MULTI:lonarr(5),$
+          NOCLIP: 0L, NOERASE: 0L, NSUM: 0L, POSITION: fltarr(4),$
+          PSYM: 0L, REGION: fltarr(4), SUBTITLE:'', SYMSIZE:0.0, T:fltarr(4,4),$
+          T3D:0L, THICK: 0.0, TITLE:'', TICKLEN:0.02, CHANNEL:0L }
+ 
+ X_old=!X
+for i=0,n_tags(!X)-1 do $
+    if size(!X.(i),/type) eq 7 then X_old.(i)= '' else X_old.(i) = 0
+
+ X_old.MARGIN = [10.0,3.0]
+ 
+ Y_old = X_old
+ Y_old.MARGIN = [4.0, 2.0]
+
+ Z_old = X_old
+ Z_old.MARGIN = [0.0, 0.0]
+
+ P_var = tag_names(!P)
+
+ if !D.NAME EQ 'PS' then begin 
+          P_old.background = 255
+          P_old.color = 0 
+ endif else if  ( (!D.NAME EQ 'X') || (!D.NAME EQ 'MAC') || $
+                  (!D.NAME EQ 'WIN') ) then begin
+          device,get_visual_depth = depth  
+          if depth GT 8 then P_old.color = 16777215 else $
+                             P_old.color = 256L^(depth/8) - 1
+ endif
+ 
+; Reset !P to its default value except for !P.CLIP
+       
+   for i=0, N_elements(P_var)-1 do begin
+     if i NE 3 then begin 
+     n = N_elements(!P.(i))
+     if ~array_equal(!P.(i), P_old.(i))  then Begin
+         if ~silent then $
+            Print,clearing +  '!P.'+P_var[i]+ oldvalue +'=',!P.(i)
+        if reset then !P.(i) = P_old.(i)
+        EndIf
+    endif
+ endfor
+;                               Reset !X !Y and !Z to their default values
+ X_var = tag_names(!X)
+ Y_var = tag_names(!Y)
+ Z_var = tag_names(!Z)
+
+ for i = 0, n_tags(!X)-1 do begin
+   if total( i EQ [7,8,11,12] ) EQ 0 then begin  ;Skip S,CRANGE,WINDOW,REGION
+       n = N_elements(!X.(i))
+       if ~array_equal(!X.(i) , X_old.(i)) then Begin
+       if ~silent then $
+          Print,clearing + '!X.'+X_var[i]+ oldvalue + '=', !X.(i)
+       if reset then !X.(i) = X_old.(i)
+       EndIf
+ 
+       if ~array_equal(!Y.(i), Y_old.(i)) then Begin
+       if ~silent then $
+          Print,clearing + '!Y.'+Y_var[i]+ oldvalue + '=', !Y.(i)
+       if reset then !Y.(i) = Y_old.(i)
+       EndIf
+
+       if ~array_equal(!Z.(i), Z_old.(i)) then Begin
+       if ~silent then $
+          Print,clearing +'!Z.'+Z_var[i]+ oldvalue + '=',!Z.(i)
+       if reset then !Z.(i) = Z_old.(i)
+       EndIf
+   endif
+endfor
+
+Return                                  ;Completed
+End
diff --git a/Code/script_idl_mv/astrolib/cntrd.pro b/Code/script_idl_mv/astrolib/cntrd.pro
new file mode 100644
index 0000000000000000000000000000000000000000..04ceb814673d3010cb26871c0ff61c278fd0ab75
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/cntrd.pro
@@ -0,0 +1,245 @@
+pro cntrd, img, x, y, xcen, ycen, fwhm, SILENT= silent, DEBUG=debug, $
+       EXTENDBOX = extendbox, KeepCenter = KeepCenter
+;+
+;  NAME: 
+;       CNTRD
+;  PURPOSE:
+;       Compute the centroid  of a star using a derivative search 
+; EXPLANATION:
+;       CNTRD uses an early DAOPHOT "FIND" centroid algorithm by locating the 
+;       position where the X and Y derivatives go to zero.   This is usually a 
+;       more "robust"  determination than a "center of mass" or fitting a 2d 
+;       Gaussian  if the wings in one direction are affected by the presence
+;       of a neighboring star.
+;
+;  CALLING SEQUENCE: 
+;       CNTRD, img, x, y, xcen, ycen, [ fwhm , /KEEPCENTER, /SILENT, /DEBUG
+;                                       EXTENDBOX = ]
+;
+;  INPUTS:     
+;       IMG - Two dimensional image array
+;       X,Y - Scalar or vector integers giving approximate integer stellar 
+;             center
+;
+;  OPTIONAL INPUT:
+;       FWHM - floating scalar; Centroid is computed using a box of half
+;               width equal to 1.5 sigma = 0.637* FWHM.  CNTRD will prompt
+;               for FWHM if not supplied
+;
+;  OUTPUTS:   
+;       XCEN - the computed X centroid position, same number of points as X
+;       YCEN - computed Y centroid position, same number of points as Y, 
+;              floating point
+;
+;       Values for XCEN and YCEN will not be computed if the computed
+;       centroid falls outside of the box, or if the computed derivatives
+;       are non-decreasing.   If the centroid cannot be computed, then a 
+;       message is displayed and XCEN and YCEN are set to -1.
+;
+;  OPTIONAL OUTPUT KEYWORDS:
+;       /SILENT - Normally CNTRD prints an error message if it is unable
+;               to compute the centroid.   Set /SILENT to suppress this.
+;       /DEBUG - If this keyword is set, then CNTRD will display the subarray
+;               it is using to compute the centroid.
+;       EXTENDBOX = {non-negative positive integer}.   CNTRD searches a box with
+;              a half width equal to 1.5 sigma  = 0.637* FWHM to find the 
+;              maximum pixel.    To search a larger area, set EXTENDBOX to 
+;              the number of pixels to enlarge the half-width of the box.
+;              Default is 0; prior to June 2004, the default was EXTENDBOX= 3
+;       /KeepCenter = By default, CNTRD finds the maximum pixel in a box 
+;              centered on the input X,Y coordinates, and then extracts a new
+;              box about this maximum pixel.   Set the /KeepCenter keyword  
+;              to skip then step of finding the maximum pixel, and instead use
+;              a box centered on the input X,Y coordinates.                          
+;  PROCEDURE: 
+;       Maximum pixel within distance from input pixel X, Y  determined 
+;       from FHWM is found and used as the center of a square, within 
+;       which the centroid is computed as the value (XCEN,YCEN) at which 
+;       the derivatives of the partial sums of the input image over (y,x)
+;       with respect to (x,y) = 0.    In order to minimize contamination from
+;       neighboring stars stars, a weighting factor W is defined as unity in 
+;       center, 0.5 at end, and linear in between 
+;
+;  RESTRICTIONS:
+;       (1) Does not recognize (bad) pixels.   Use the procedure GCNTRD.PRO
+;           in this situation. 
+;       (2) DAOPHOT now uses a newer algorithm (implemented in GCNTRD.PRO) in 
+;           which centroids are determined by fitting 1-d Gaussians to the 
+;           marginal distributions in the X and Y directions.
+;       (3) The default behavior of CNTRD changed in June 2004 (from EXTENDBOX=3
+;           to EXTENDBOX = 0).
+;       (4) Stone (1989, AJ, 97, 1227) concludes that the derivative search
+;           algorithm in CNTRD is not as effective (though faster) as a 
+;            Gaussian fit (used in GCNTRD.PRO).
+;  MODIFICATION HISTORY:
+;       Written 2/25/86, by J. K. Hill, S.A.S.C., following
+;       algorithm used by P. Stetson in DAOPHOT.
+;       Allowed input vectors        G. Hennessy       April,  1992
+;       Fixed to prevent wrong answer if floating pt. X & Y supplied
+;               W. Landsman        March, 1993
+;       Convert byte, integer subimages to float  W. Landsman  May 1995
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Better checking of edge of frame David Hogg October 2000
+;       Avoid integer wraparound for unsigned arrays W.Landsman January 2001
+;       Handle case where more than 1 pixel has maximum value W.L. July 2002
+;       Added /KEEPCENTER, EXTENDBOX (with default = 0) keywords WL June 2004
+;       Some errrors were returning X,Y = NaN rather than -1,-1  WL Aug 2010
+;-      
+ On_error,2                          ;Return to caller
+ compile_opt idl2
+ 
+ if N_params() LT 5 then begin
+        print,'Syntax: CNTRD, img, x, y, xcen, ycen, [ fwhm, ' 
+        print,'              EXTENDBOX= , /KEEPCENTER, /SILENT, /DEBUG ]'
+        PRINT,'img - Input image array'
+        PRINT,'x,y - Input scalars giving approximate X,Y position'
+        PRINT,'xcen,ycen - Output scalars giving centroided X,Y position'
+        return
+ endif else if N_elements(fwhm) NE 1 then $
+      read,'Enter approximate FWHM of image in pixels: ',fwhm
+
+ sz_image = size(img)
+ if sz_image[0] NE 2 then message, $
+   'ERROR - Image array (first parameter) must be 2 dimensional'
+
+ xsize = sz_image[1]
+ ysize = sz_image[2]
+ dtype = sz_image[3]              ;Datatype
+
+;   Compute size of box needed to compute centroid
+
+ if ~keyword_set(extendbox) then extendbox = 0
+ nhalf =  fix(0.637*fwhm) > 2  ;
+ nbox = 2*nhalf+1             ;Width of box to be used to compute centroid
+ nhalfbig = nhalf + extendbox
+ nbig = nbox + extendbox*2        ;Extend box 3 pixels on each side to search for max pixel value
+ npts = N_elements(x) 
+ xcen = float(x) & ycen = float(y)
+ ix = round( x )          ;Central X pixel        ;Added 3/93
+ iy = round( y )          ;Central Y pixel
+
+ for i = 0,npts-1 do begin        ;Loop over X,Y vector
+
+ pos = strtrim(x[i],2) + ' ' + strtrim(y[i],2)
+
+ if ~keyword_set(keepcenter) then begin
+ if ( (ix[i] LT nhalfbig) || ((ix[i] + nhalfbig) GT xsize-1) || $
+      (iy[i] LT nhalfbig) || ((iy[i] + nhalfbig) GT ysize-1) ) then begin
+     if not keyword_set(SILENT) then message,/INF, $
+           'Position '+ pos + ' too near edge of image'
+     xcen[i] = -1   & ycen[i] = -1
+     goto, DONE
+ endif
+
+ bigbox = img[ix[i]-nhalfbig : ix[i]+nhalfbig, iy[i]-nhalfbig : iy[i]+nhalfbig]
+
+;  Locate maximum pixel in 'NBIG' sized subimage 
+
+ mx = max( bigbox)     ;Maximum pixel value in BIGBOX
+ mx_pos = where(bigbox EQ mx, Nmax) ;How many pixels have maximum value?
+ idx = mx_pos mod nbig          ;X coordinate of Max pixel
+ idy = mx_pos / nbig            ;Y coordinate of Max pixel
+ if NMax GT 1 then begin        ;More than 1 pixel at maximum?
+     idx = round(total(idx)/Nmax)
+     idy = round(total(idy)/Nmax)
+ endif else begin
+     idx = idx[0]
+     idy = idy[0]
+ endelse
+
+ xmax = ix[i] - (nhalf+extendbox) + idx  ;X coordinate in original image array
+ ymax = iy[i] - (nhalf+extendbox) + idy  ;Y coordinate in original image array
+ endif else begin
+    xmax = ix[i]
+    ymax = iy[i]
+ endelse
+
+; ---------------------------------------------------------------------
+; check *new* center location for range
+; added by Hogg
+
+ if ( (xmax LT nhalf) || ((xmax + nhalf) GT xsize-1) || $
+      (ymax LT nhalf) || ((ymax + nhalf) GT ysize-1) ) then begin
+     if not keyword_set(SILENT) then message,/INF, $
+           'Position '+ pos + ' moved too near edge of image'
+     xcen[i] = -1   & ycen[i] = -1
+     goto, DONE
+ endif
+; ---------------------------------------------------------------------
+
+;  Extract smaller 'STRBOX' sized subimage centered on maximum pixel 
+
+ strbox = img[xmax-nhalf : xmax+nhalf, ymax-nhalf : ymax+nhalf]
+ if (dtype NE 4) and (dtype NE 5) then strbox = float(strbox)
+
+ if keyword_set(DEBUG) then begin
+       message,'Subarray used to compute centroid:',/inf
+       print,strbox
+ endif  
+
+ ir = (nhalf-1) > 1 
+ dd = indgen(nbox-1) + 0.5 - nhalf
+; Weighting factor W unity in center, 0.5 at end, and linear in between 
+ w = 1. - 0.5*(abs(dd)-0.5)/(nhalf-0.5) 
+ sumc   = total(w)
+
+; Find X centroid
+
+ deriv = shift(strbox,-1,0) - strbox    ;Shift in X & subtract to get derivative
+ deriv = deriv[0:nbox-2,nhalf-ir:nhalf+ir] ;Don't want edges of the array
+ deriv = total( deriv, 2 )                        ;Sum X derivatives over Y direction
+ sumd   = total( w*deriv )
+ sumxd  = total( w*dd*deriv )
+ sumxsq = total( w*dd^2 )
+
+ if sumxd GE 0 then begin  ;Reject if X derivative not decreasing
+   
+   if ~keyword_set(SILENT) then message,/INF, $
+        'Unable to compute X centroid around position '+ pos
+   xcen[i]=-1 & ycen[i]=-1
+   goto,DONE
+ endif 
+
+ dx = sumxsq*sumd/(sumc*sumxd)
+ if ( abs(dx) GT nhalf ) then begin    ;Reject if centroid outside box  
+   if not keyword_set(SILENT) then message,/INF, $
+       'Computed X centroid for position '+ pos + ' out of range'
+   xcen[i]=-1 & ycen[i]=-1 
+   goto, DONE
+ endif
+
+ xcen[i] = xmax - dx    ;X centroid in original array
+
+;  Find Y Centroid
+
+ deriv = shift(strbox,0,-1) - strbox
+ deriv = deriv[nhalf-ir:nhalf+ir,0:nbox-2]
+ deriv = total( deriv,1 )
+ sumd =   total( w*deriv )
+ sumxd =  total( w*deriv*dd )
+ sumxsq = total( w*dd^2 )
+ if (sumxd GE 0) then begin  ;Reject if Y derivative not decreasing
+   if not keyword_set(SILENT) then message,/INF, $
+        'Unable to compute Y centroid around position '+ pos
+        xcen[i] = -1   & ycen[i] = -1
+        goto, DONE
+ endif
+
+ dy = sumxsq*sumd/(sumc*sumxd)
+ if (abs(dy) GT nhalf) then begin ;Reject if computed Y centroid outside box
+   if ~keyword_set(SILENT) then message,/INF, $
+       'Computed X centroid for position '+ pos + ' out of range'
+        xcen[i]=-1 & ycen[i]=-1
+        goto, DONE
+ endif 
+ 
+ ycen[i] = ymax-dy
+
+ DONE: 
+
+ endfor
+
+ return
+ end
+
+
diff --git a/Code/script_idl_mv/astrolib/co_aberration.pro b/Code/script_idl_mv/astrolib/co_aberration.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e1ea5e39070e9f23985e3b482312c6ad7ef16638
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/co_aberration.pro
@@ -0,0 +1,92 @@
+PRO co_aberration, jd, ra, dec, d_ra, d_dec, eps=eps
+;+
+;  NAME:
+;     CO_ABERRATION
+; PURPOSE:
+;     Calculate changes to Ra and Dec due to the effect of annual aberration 
+; EXPLANATION:
+;      as described in Meeus, Chap 23.
+; CALLING SEQUENCE:
+;      co_aberration, jd, ra, dec, d_ra, d_dec, [EPS = ]
+; INPUTS
+;       jd      : Julian Date [scalar or vector]
+;       ra, dec : Arrays (or scalars) of the ra  and dec's in degrees
+;   Note: if jd is a vector, then ra and dec must either be scalars, or 
+;                vectors of the same length.
+;
+; OUTPUTS
+;       d_ra, d_dec: the corrections to ra and dec due to aberration in 
+;                    arcseconds.  (These values can be added to the true RA 
+;                    and dec to get the apparent position).   Note that d_ra
+;                     is *not* multiplied by cos(dec), so that 
+;                     apparent_ra = ra + d_ra/3600. 
+; OPTIONAL INPUT KEYWORD:
+;       eps : set this to the true obliquity of the ecliptic (in radians), or
+;         it will be set for you if you don't know it (in that case, set it to
+;                 an empty variable).
+; EXAMPLE:
+;   Compute the change in RA and Dec of Theta Persei (RA = 2h46m,11.331s, Dec =
+;   49d20',54.54") due to aberration on 2028 Nov 13.19 TD
+;
+;      IDL> jdcnv,2028,11,13,.19*24,jd      ;Get Julian date
+;      IDL> co_aberration,jd,ten(2,46,11.331)*15,ten(49,20,54.54),d_ra,d_dec
+;
+;      ==> d_ra = 30.045" (=2.003s)    d_dec = 6.697"
+; NOTES:
+;  These formula are from Meeus, Chapters 23.  Accuracy is much better than 1 
+;   arcsecond.
+;
+;   The maximum deviation due to annual aberration is 20.49" and occurs when the
+;   Earth velocity is perpendicular to the direction of the star.
+;
+; REVISION HISTORY:
+;   Written, June 2002,      Chris O'Dell, U. of Wisconsin
+;   Fix error with vector input   W. Landsman   June 2009
+;   June 2009 update fixed case where JD was scalar but RA,Dec were vectors, but 
+;     broke the case when both JD and RA,Dec were vectors Aug 2012 W. Landsman
+;   Further fix when JD is 1 element vector  W. Landsman
+;-
+ compile_opt idl2
+ d2r = !dpi/180.
+ if N_elements(jd) EQ 1 then jd = jd[0]
+ T = (jd -2451545.0)/36525.0 ; julian centuries from J2000 of jd.
+ if n_elements(eps) eq 0 then begin ; must calculate obliquity of ecliptic
+        njd = n_elements(jd)
+        d_psi = dblarr(njd)
+        d_epsilon = d_psi
+        for i=0L,njd-1 do begin
+                nutate, jd[i], dp, de ; d_psi and d_epsilon in degrees
+                d_psi[i] = dp
+                d_epsilon[i] = de
+        endfor
+        eps0 = ten(23,26,21.448)*3600.d - 46.8150*T - 0.00059*T^2 +  $
+               0.001813*T^3
+        eps = (eps0 + d_epsilon)/3600.*d2r ; true obliquity of the ecliptic 
+;                                            in radians
+endif
+
+ sunpos, jd, sunra, sundec, sunlon
+
+; Earth's orbital eccentricity
+ e = 0.016708634d - 0.000042037d*T - 0.0000001267d*T^2
+; longitude of perihelion, in degrees 
+pi = 102.93735 + 1.71946*T + 0.00046*T^2 
+k = 20.49552 ;constant of aberration, in arcseconds
+
+;Useful Trig Functions
+cd = cos(dec*d2r) & sd = sin(dec*d2r)
+if N_elements(eps) EQ 1 then eps = eps[0]     ;Special scalar case
+ce = cos(eps) & te = tan(eps)
+cp = cos(pi*d2r) & sp = sin(pi*d2r)
+cs = cos(sunlon*d2r) & ss = sin(sunlon*d2r)
+ca = cos(ra*d2r) & sa = sin(ra*d2r)
+
+term1 = (ca*cs*ce+sa*ss)/cd
+term2 = (ca*cp*ce+sa*sp)/cd
+term3 = (cs*ce*(te*cd-sa*sd)+ca*sd*ss)
+term4 = (cp*ce*(te*cd-sa*sd)+ca*sd*sp)
+
+d_ra = -k * term1 + e*k * term2
+d_dec = -k * term3 + e*k * term4
+
+END
diff --git a/Code/script_idl_mv/astrolib/co_nutate.pro b/Code/script_idl_mv/astrolib/co_nutate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4371a7a65351604a832dece6d38defd943f75a84
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/co_nutate.pro
@@ -0,0 +1,115 @@
+PRO co_nutate, jd, ra, dec, d_ra, d_dec, eps=eps, d_psi=d_psi, d_eps=d_eps
+;+
+;  NAME:
+;     CO_NUTATE
+;  PURPOSE:
+;     Calculate changes in RA and Dec due to nutation of the Earth's rotation
+; EXPLANATION:
+;     Calculates necessary changes to ra and dec due to
+;     the nutation of the Earth's rotation axis, as described in Meeus, Chap 23.
+;     Uses formulae from Astronomical Almanac, 1984, and does the calculations
+;     in equatorial rectangular coordinates to avoid singularities at the
+;     celestial poles.
+;
+; CALLING SEQUENCE:
+;     CO_NUTATE, jd, ra, dec, d_ra, d_dec, [EPS=, D_PSI =, D_EPS = ]
+; INPUTS
+;    JD: Julian Date [scalar or vector]
+;    RA, DEC : Arrays (or scalars) of the ra and dec's of interest
+;
+;   Note: if jd is a vector, ra and dec MUST be vectors of the same length.
+;
+; OUTPUTS:
+;    d_ra, d_dec: the corrections to ra and dec due to nutation (must then
+;                                be added to ra and dec to get corrected values).
+; OPTIONAL OUTPUT KEYWORDS:
+;    EPS: set this to a named variable that will contain the obliquity of the 
+;             ecliptic.
+;    D_PSI: set this to a named variable that will contain the nutation in the
+;           longitude of the ecliptic
+;    D_EPS: set this to a named variable that will contain the nutation in the
+;                       obliquity of the ecliptic
+; EXAMPLE:
+;    (1) Example 23a in Meeus: On 2028 Nov 13.19 TD the mean position of Theta
+;        Persei is 2h 46m 11.331s 49d 20' 54.54".    Determine the shift in 
+;        position due to the Earth's nutation.
+;    
+;        IDL> jd = JULDAY(11,13,2028,.19*24)       ;Get Julian date
+;        IDL> CO_NUTATE, jd,ten(2,46,11.331)*15.,ten(49,20,54.54),d_ra,d_dec    
+;
+;              ====> d_ra = 15.843"   d_dec = 6.217"
+; PROCEDURES USED:
+;    NUTATE 
+; REVISION HISTORY:
+;    Written  Chris O'Dell, 2002
+;    Vector call to NUTATE   W. Landsman   June 2002
+;    Fix when JD is 1 element vector, and RA,Dec are vectors WL  May 2013
+;-
+
+ if N_Params() LT 4  then begin
+     print,'Syntax - CO_NUTATE, jd, ra, dec, d_ra, d_dec, '
+     print,'   Output keywords:     [EPS=, D_PSI =, D_EPS = ]'
+     return
+ endif
+ d2r = !dpi/180.
+ d2as = !dpi/(180.d*3600.d)
+ T = (jd -2451545.0)/36525.0 ; Julian centuries from J2000 of jd.
+
+; must calculate obliquity of ecliptic
+ nutate,jd,d_psi, d_eps 
+
+ eps0 = 23.4392911*3600.d - 46.8150*T - 0.00059*T^2 + 0.001813*T^3
+ eps = (eps0 + d_eps)/3600.*d2r ; true obliquity of the ecliptic in radians
+ if N_elements(eps) EQ 1 then eps = eps[0]
+ if N_elements(d_psi) Eq 1 then d_psi = d_psi[0]
+
+;useful numbers
+ ce = cos(eps)
+ se = sin(eps)
+
+; convert ra-dec to equatorial rectangular coordinates
+ x = cos(ra*d2r) * cos(dec*d2r)
+ y = sin(ra*d2r) * cos(dec*d2r)
+ z = sin(dec*d2r)
+
+; apply corrections to each rectangular coordinate
+ x2 = x - (y*ce + z*se)*d_psi * d2as
+ y2 = y + (x*ce*d_psi - z*d_eps) * d2as
+ z2 = z + (x*se*d_psi + y*d_eps) * d2as
+
+; convert back to equatorial spherical coordinates
+ r = sqrt(x2^2 + y2^2 + z2^2)
+ xyproj = sqrt(x2^2 + y2^2)
+
+ ra2 = x2 * 0.d
+ dec2= x2 * 0.d
+
+ w1 = where( (xyproj eq 0) AND (z ne 0) )
+ w2 = where(xyproj ne 0)
+
+; Calculate Ra and Dec in RADIANS (later convert to DEGREES)
+ if w1[0] ne -1 then begin
+	; places where xyproj=0 (point at NCP or SCP)
+	dec2[w1] = asin(z2[w1]/r[w1])
+	ra2[w1] = 0.
+ endif
+ if w2[0] ne -1 then begin
+	; places other than NCP or SCP
+	ra2[w2] = atan(y2[w2],x2[w2])
+	dec2[w2] = asin(z2[w2]/r[w2])
+ endif
+
+                  ; convert to DEGREES
+
+ ra2 = ra2 /d2r
+ dec2 = dec2 /d2r
+
+ w = where(ra2 LT 0., Nw)
+ if Nw GT 0 then ra2[w] = ra2[w] + 360.
+
+
+; Return changes in ra and dec in arcseconds
+ d_ra = (ra2 - ra) * 3600.
+ d_dec = (dec2 - dec) * 3600.
+
+END
diff --git a/Code/script_idl_mv/astrolib/co_refract.pro b/Code/script_idl_mv/astrolib/co_refract.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ec95de65e1f579b02ebc1f53ad5a9f0a3fdd456b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/co_refract.pro
@@ -0,0 +1,186 @@
+;+
+; NAME:
+;   CO_REFRACT()      
+;
+; PURPOSE:
+;   Calculate correction to altitude due to atmospheric refraction.
+;
+; DESCRIPTION:
+;   CO_REFRACT can calculate both apparent altitude from observed altitude and 
+;   vice-versa.
+;
+; CALLING SEQUENCE:
+;   new_alt  = CO_REFRACT(old_alt, [ ALTITUDE= , PRESSURE= , $
+;                                  TEMPERATURE= , /TO_OBSERVED , EPSILON= ])
+;
+; INPUT:
+;   old_alt - Observed (apparent) altitude, in DEGREES.  (apparent if keyword 
+;             /TO_OBSERVED set).    May be scalar or vector.
+;
+; OUTPUT: 
+;     Function returns apparent (observed) altitude, in DEGREES. (observed if 
+;         keyword /TO_OBSERVED set).    Will be of same type as input 
+;         altitude(s).
+;
+; OPTIONAL KEYWORD INPUTS:
+;      ALTITUDE :  The height of the observing location, in meters.  This is 
+;             only used to determine an approximate temperature and pressure, 
+;             if these are not specified separately. [default=0, i.e. sea level]
+;      PRESSURE :  The pressure at the observing location, in millibars.
+;      TEMPERATURE:    The temperature at the observing location, in Kelvin.
+;      EPSILON:  When keyword /TO_OBSERVED has been set, this is the accuracy 
+;               to  obtain via the iteration, in arcseconds [default = 0.25 
+;                arcseconds].
+;      /TO_OBSERVED:  Set this keyword to go from Apparent->Observed altitude, 
+;                 using the iterative technique.
+;
+;       Note, if altitude is set, but temperature or pressure are not, the 
+;       program will make an intelligent guess for the temperature and pressure.
+;
+; DESCRIPTION:
+;
+;   Because the index of refraction of air is not precisely 1.0, the atmosphere
+;   bends all incoming light, making a star or other celestial object appear at
+;   a slightly different altitude (or elevation) than it really is.  It is 
+;   important to understand the following definitions:
+;
+;   Observed Altitude:  The altitude that a star is SEEN to BE, with a telescope.
+;                       This is where it appears in the sky.  This is always 
+;                       GREATER than the apparent altitude.
+;
+;   Apparent Altitude:  The altitude that a star would be at, if *there were no
+;                     atmosphere* (sometimes called "true" altitude). This is 
+;                     usually calculated from an object's celestial coordinates.
+;                     Apparent altitude is always LOWER than the observed 
+;                     altitude.
+;
+;   Thus, for example, the Sun's apparent altitude when you see it right on the
+;   horizon is actually -34 arcminutes.
+;
+;   This program uses couple simple formulae to estimate the effect for most 
+;   optical and radio wavelengths.  Typically, you know your observed altitude 
+;   (from an observation), and want the apparent altitude.  To go the other way,
+;   this program uses an iterative approach.
+;
+; EXAMPLE:
+;    The lower limb of the Sun is observed to have altitude of 0d 30'.   
+;    Calculate the the true (=apparent) altitude of the Sun's lower limb using 
+;    mean  conditions of air pressure and temperature
+;
+;    IDL> print, co_refract(0.5)     ===>  0.025degrees (1.55')
+; WAVELENGTH DEPENDENCE:
+;    This correction is 0 at zenith, about 1 arcminute at 45 degrees, and 34 
+;    arcminutes at the horizon FOR OPTICAL WAVELENGTHS.  The correction is 
+;    NON-NEGLIGIBLE at all wavelengths, but is not very easily calculable.  
+;    These formulae assume a wavelength of 550 nm, and will be accurate to 
+;    about 4 arcseconds for all visible wavelengths, for elevations of 10 
+;    degrees and higher.    Amazingly, they are also ACCURATE FOR RADIO 
+;    FREQUENCIES LESS THAN ~ 100 GHz.
+;
+;    It is important to understand that these formulae really can't do better 
+;    than about 30 arcseconds of accuracy very close to the horizon, as 
+;    variable atmospheric effects become very important.
+;
+; REFERENCES:
+;    1.  Meeus, Astronomical Algorithms, Chapter 15.
+;    2.  Explanatory Supplement to the Astronomical Almanac, 1992.
+;    3.  Methods of Experimental Physics, Vol 12 Part B, Astrophysics, 
+;        Radio Telescopes, Chapter 2.5, "Refraction Effects in the Neutral 
+;        Atmosphere", by R.K. Crane.
+;
+;
+; DEPENDENCIES:
+;    CO_REFRACT_FORWARD (contained in this file and automatically compiled).
+;
+; AUTHOR:
+;   Chris O'Dell  
+;     Assistant Professor of Atmospheric Science
+;      Colorado State University
+;   Email: odell@atmos.colostate.edu
+;
+; REVISION HISTORY:
+;    version 1 (May 31, 2002)
+;    Update iteration formula,   W. Landsman    June 2002
+;    Corrected slight bug associated with scalar vs. vector temperature and 
+;               pressure inputs. 6/10/2002
+;    Fixed problem with vector input when /TO_OBSERVED set W. Landsman Dec 2005
+;    Allow arrays with more than 32767 elements W.Landsman/C.Dickinson Feb 2010
+;-
+function co_refract_forward, a, P=P, T=T
+
+; INPUTS
+;    a = The observed (apparent) altitude, in DEGREES.
+;        May be scalar or vector.
+;
+; INPUT KEYWORDS
+;    P:  Pressure [in millibars]. Default is 1010 millibars. [scalar or vector]
+;    T:  Ground Temp [in Celsius].  Default is 0 Celsius. [scalar or vector]
+
+compile_opt idl2
+d2r = !dpi/180.
+if n_elements(P) eq 0 then P = 1010.
+if n_elements(T) eq 0 then T = 283.
+
+; you have observed the altitude a, and would like to know what the "apparent" 
+; altitude is (the one behind the atmosphere).
+w = where(a LT 15.)
+R = 0.0166667/tan((a + 7.31/(a+4.4))*d2r)
+
+;R = 1.02/tan((a + 10.3/(a+5.11))*d2r)/60. 
+; this formula goes the other direction!
+
+if w[0] ne -1 then R[w] = 3.569*(0.1594 + .0196*a[w] + $
+      .00002*a[w]^2)/(1.+.505*a[w]+.0845*a[w]^2)
+tpcor = P/1010. * 283/T
+R = tpcor * R
+
+return, R
+
+END
+
+function co_refract, a, altitude=altitude, pressure=pressure,  $
+            temperature=temperature, To_observed=To_observed, epsilon=epsilon
+
+; This is the main window.  Calls co_refract_forward either iteratively or a 
+; single time depending on the direction we are going for refraction.
+
+compile_opt idl2
+o = keyword_set(To_observed)
+alpha = 0.0065 ; temp lapse rate [deg C per meter]
+
+if n_elements(altitude) eq 0 then altitude = 0.
+if n_elements(temperature) eq 0 then begin
+        if altitude GT 11000 then temperature = 211.5 $
+                             else temperature = 283.0 - alpha*altitude
+endif
+; estimate Pressure based on altitude, using U.S. Standard Atmosphere formula.
+if n_elements(pressure) eq 0 then $ 
+              pressure = 1010.*(1-6.5/288000*altitude)^5.255
+if n_elements(epsilon) eq 0 then  $
+     epsilon = 0.25 ; accuracy of iteration for observed=1 case, in arcseconds
+
+if not o then begin
+        aout = a - co_refract_forward(a,P=pressure,T=temperature)
+endif else begin
+        aout = a*0.
+        na = n_elements(a)
+; if there are multiple elevations but only one temp and pressure entered, 
+; expand those to be arrays of the same size.
+	P = pressure + a*0. & T = temperature + a*0.
+        for i=0L,na-1 do begin
+                ;calculate initial refraction guess
+                dr = co_refract_forward(a[i],P=P[i],T=T[i])
+            cur = a[i] + dr ; guess of observed location
+
+                repeat begin
+                  last = cur
+                  dr = co_refract_forward(cur,P=P[i],T=T[i])
+                  cur= a[i] + dr
+                endrep until abs(last-cur)*3600. LT epsilon
+                aout[i] = cur
+        endfor
+endelse
+
+if N_elements(aout) GT 1 then return, reform(aout) else return, aout
+
+END
diff --git a/Code/script_idl_mv/astrolib/compare_struct.pro b/Code/script_idl_mv/astrolib/compare_struct.pro
new file mode 100644
index 0000000000000000000000000000000000000000..aa497e3601d9dd5791e9ef70b30da88da34659fd
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/compare_struct.pro
@@ -0,0 +1,239 @@
+;+
+; NAME:
+;       COMPARE_STRUCT  
+; PURPOSE:
+;       Compare all matching tag names and return differences
+;
+; EXPLANATION:
+;       Compare all matching Tags names (except for "except_Tags")
+;       between two structure arrays (may have different struct.definitions),
+;       and return a structured List of fields found different.
+;
+;       The Exelis contrib library has a faster but less powerful procedure
+;       struct_equal.pro, see 
+;       http://www.exelisvis.com/Default.aspx?tabid=1540&id=1175
+;
+; CALLING SEQUENCE:
+;       diff_List = compare_struct( struct_A, struct_B [ EXCEPT=, /BRIEF,
+;                                    /FULL, /NaN, /RECUR_A, /RECUR_B )
+; INPUTS:
+;       struct_A, struct_B : the two structure arrays to compare.
+;       Struct_Name : for internal recursion use only.
+; OPTIONAL INPUT KEYWORDS:
+;               EXCEPT = string array of Tag names to ignore (NOT to compare).
+;               /BRIEF = number of differences found for each matching field
+;                                               of two structures is printed.
+;               /FULL = option to print even if zero differences found.
+;               /NaN = if set, then tag values are considered equal if they
+;                      are both set to NaN 
+;               /RECUR_A = option to search for Tag names
+;                               in sub-structures of struct_A,
+;                               and then call compare_struct recursively
+;                               for those nested sub-structures.
+;               /RECUR_B = search for sub-structures of struct_B,
+;                               and then call compare_struct recursively
+;                               for those nested sub-structures.
+;       Note:
+;               compare_struct is automatically called recursively
+;               for those nested sub-structures in both struct_A and struct_B
+;               (otherwise cannot take difference)
+; OUTPUT:
+;       Returns a structure array describing differences found.   
+;       which can be examined using print,diff_List or help,/st,diff_List.
+;       The tags are
+;       TAG_NUM_A - the tag number in structure A
+;       TAG_NUM_B - the tag number in structure B
+;       FIELD - the tag name
+;       NDIFF - number of differences (always 1 for a scalar tag).
+; PROCEDURE:
+;       Match Tag names and then use where function on tags.
+; EXAMPLE:
+;       Find the tags in the !X system variable which are changed after a 
+;       simple plot.
+;       IDL> x = !X              ;Save original values
+;       IDL> plot, indgen(25)    ;Make a simple plot
+;       IDL> help,/str,compare_struct(x,!X)    ;See how structure has changed
+;
+;            and one will see that the tags  !X.crange and !X.S are changed
+;            by the plot.
+; MODIFICATION HISTORY:
+;       written 1990 Frank Varosi STX @ NASA/GSFC (using copy_struct)
+;       modif Aug.90 by F.V. to check and compare same # of elements only.
+;       Added /NaN keyword W. Landsman  March 2004
+;       Don't test string for NaN values W. Landsman March 2008
+;-
+
+function compare_struct, struct_A, struct_B, EXCEPT=except_Tags, Struct_Name, $
+                                        FULL=full, BRIEF=brief, NaN = NaN, $
+                                        RECUR_A = recur_A, RECUR_B = recur_B
+
+   compile_opt idl2
+   common compare_struct, defined
+   if N_params() LT 2 then begin
+       print,'Syntax - diff_List = compare_struct(struct_A, struct_B '
+       print,'         [EXCEPT=, /BRIEF, /FULL, /NaN, /RECUR_A, /RECUR_B ]'
+       if N_elements(diff_List) GT 0 then return, diff_List else return, -1
+   endif
+
+        if N_elements( defined ) NE 1 then begin
+
+                diff_List = { DIFF_LIST, Tag_Num_A:0, Tag_Num_B:0, $
+                                                Field:"",  Ndiff:0L }
+                defined = N_tags( diff_List )
+          endif else diff_List = replicate( {DIFF_LIST}, 1 )
+
+        Ntag_A = N_tags( struct_A )
+        if (Ntag_A LE 0) then begin
+                message," 1st argument must be a structure variable",/CONTIN
+                return,diff_List 
+           endif
+        Ntag_B = N_tags( struct_B )
+        if (Ntag_B LE 0) then begin
+                message," 2nd argument must be a structure variable",/CONTIN
+                return,diff_List 
+           endif
+
+        N_A = N_elements( struct_A )
+        N_B = N_elements( struct_B )
+
+        if (N_A LT N_B) then begin
+
+                message,"comparing "+strtrim(N_A,2)+" of first structure",/CON
+                message,"to first "+strtrim(N_A,2)+" of "+strtrim(N_B,2)+ $
+                        " in second structure",/CONTIN
+
+                diff_List = compare_struct( struct_A, struct_B[0:N_A-1], $
+                                                EXCEPT=except_Tags, $
+                                                RECUR_A = recur_A, $
+                                                RECUR_B = recur_B, $
+                                                FULL=full, BRIEF=brief )
+                return,diff_List 
+
+          endif else if (N_A GT N_B) then begin
+
+                message,"comparing first "+strtrim(N_B,2)+" of "+ $
+                        strtrim(N_A,2)+" in first structure",/CON
+                message,"to "+strtrim(N_B,2)+" in second structure",/CONTIN
+
+                diff_List = compare_struct( struct_A[0:N_B-1], struct_B, $
+                                                EXCEPT=except_Tags, $
+                                                RECUR_A = recur_A, $
+                                                RECUR_B = recur_B, $
+                                                FULL=full, BRIEF=brief )
+                return,diff_List 
+           endif
+
+        Tags_A = tag_names( struct_A )
+        Tags_B = tag_names( struct_B )
+        wB = indgen( N_elements( Tags_B ) )
+        Nextag = N_elements( except_Tags )
+
+        if (Nextag GT 0) then begin
+
+                except_Tags = [strupcase( except_Tags )]
+
+                for t=0,Nextag-1 do begin
+
+                        w = where( Tags_B NE except_Tags[t], Ntag_B )
+                        Tags_B = Tags_B[w]
+                        wB = wB[w]
+                  endfor
+           endif
+
+        if N_elements( struct_name ) NE 1 then sname = "." $
+                                          else sname = struct_name + "." 
+
+        for t = 0, Ntag_B-1 do begin
+
+                wA = where( Tags_A EQ Tags_B[t] , nf )
+
+                if (nf GT 0) then begin
+
+                     tA = wA[0]
+                     tB = wB[t]
+
+                     NtA = N_tags( struct_A.(tA) )
+                     NtB = N_tags( struct_B.(tB) )
+
+                     if (NtA GT 0 ) AND (NtB GT 0) then begin
+
+                        if keyword_set( full ) OR keyword_set( brief ) then $
+                                                print, sname + Tags_A[tA], " :"
+
+                        diffs = compare_struct( struct_A.(tA), struct_B.(tB), $
+                                                sname + Tags_A[tA], $
+                                                EXCEPT=except_Tags, $
+                                                FULL=full, BRIEF=brief )
+                        diff_List = [ diff_List, diffs ]
+
+                      endif else if (NtA LE 0) AND (NtB LE 0) then begin
+
+                           if keyword_set(NaN) then begin
+                                  x1 = struct_b.(tB)
+                                  x2 = struct_a.(tA)
+				  if (size(x1,/tname) NE 'STRING') and $
+				     (size(x2,/tname) NE 'STRING') then begin
+                                  g = where( finite(x1) or finite(x2), Ndiff )
+                                  if Ndiff GT 0 then $
+                                    w = where( x1[g] NE x2[g], Ndiff ) 
+				    endif
+                           endif else $ 
+                            w = where( struct_B.(tB) NE struct_A.(tA) , Ndiff )
+
+                                if (Ndiff GT 0) then begin
+                                        diff = replicate( {DIFF_LIST}, 1 )
+                                        diff.Tag_Num_A = tA
+                                        diff.Tag_Num_B = tB
+                                        diff.Field = sname + Tags_A[tA] 
+                                        diff.Ndiff = Ndiff
+                                        diff_List = [ diff_List, diff ]
+                                   endif
+
+                                if keyword_set( full ) OR $
+                                  (keyword_set( brief ) AND (Ndiff GT 0)) then $
+                                   print, Tags_A[tA], Ndiff, FORM="(15X,A15,I9)"
+
+                        endif else print, Tags_A[ta], " not compared"
+
+                 endif
+          endfor
+
+        if keyword_set( recur_A ) then begin
+
+                for tA = 0, Ntag_A-1 do begin
+
+                   if N_tags( struct_A.(tA) ) GT 0 then begin
+
+                        diffs = compare_struct( struct_A.(tA), struct_B, $
+                                                sname + Tags_A[tA], $
+                                                EXCEPT=except_Tags, $
+                                                RECUR_A = recur_A, $
+                                                RECUR_B = recur_B, $
+                                                FULL=full, BRIEF=brief )
+                        diff_List = [ diff_List, diffs ]
+                     endif
+                  endfor
+          endif
+
+        if keyword_set( recur_B ) then begin
+
+                for tB = 0, Ntag_B-1 do begin
+
+                   if N_tags( struct_B.(tB) ) GT 0 then begin
+
+                        diffs = compare_struct( struct_A, struct_B.(tB), $
+                                                sname + Tags_B[tB], $
+                                                EXCEPT=except_Tags, $
+                                                RECUR_A = recur_A, $
+                                                RECUR_B = recur_B, $
+                                                FULL=full, BRIEF=brief )
+                        diff_List = [ diff_List, diffs ]
+                     endif
+                  endfor
+          endif
+
+        w = where( [diff_List.Ndiff] GT 0, np )
+        if (np LE 0) then w = [0]
+
+return, diff_List[w]
+end
diff --git a/Code/script_idl_mv/astrolib/concat_dir.pro b/Code/script_idl_mv/astrolib/concat_dir.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a89656a9edce565ad24616fb2f55e775c7cad1b8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/concat_dir.pro
@@ -0,0 +1,110 @@
+;+
+; NAME:   
+;       CONCAT_DIR()
+;               
+; PURPOSE:     
+;       To concatenate directory and file names for current OS.
+; EXPLANATION:
+;       The given file name is appended to the given directory name with the 
+;       format appropriate to the current operating system.
+;
+; CALLING SEQUENCE:               
+;       result = concat_dir( directory, file) 
+;
+; INPUTS:
+;       directory  - the directory path (string)
+;       file       - the basic file name and extension (string)
+;                                   can be an array of filenames.
+;
+; OUTPUTS:     
+;       The function returns the concatenated string.  If the file input
+;       is a string array then the output will be a string array also.
+;               
+; EXAMPLES:         
+;       IDL> pixfile = concat_dir('$DIR_GIS_MODEL','pixels.dat')
+;
+;       IDL> file = ['f1.dat','f2.dat','f3.dat']
+;       IDL> dir = '$DIR_NIS_CAL'
+;       IDL> 
+
+;
+; RESTRICTIONS: 
+;               
+;       The version of CONCAT_DIR available at 
+;       http://sohowww.nascom.nasa.gov/solarsoft/gen/idl/system/concat_dir.pro
+;       includes (mostly) additional VMS-specific keywords.
+;
+; CATEGORY    
+;        Utilities, Strings
+;               
+; REVISION HISTORY:
+;       Prev Hist. : Yohkoh routine by M. Morrison
+;       Written     : CDS version by C D Pike, RAL, 19/3/93
+;       Version     : Version 1  19/3/93
+;       Documentation modified Nov-94   W. Landsman 
+;       Add V4.0 support for Windows    W. Landsman   Aug 95
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Changed loops to long integer   W. Landsman   December 1998
+;       Added Mac support, translate Windows environment variables, 
+;       & treat case where dirname ends in '/' W. Landsman  Feb. 2000
+;       Assume since V5.5, remove VMS support W. Landsman  Sep. 2006
+;-            
+;
+function concat_dir, dirname, filnam
+;
+;  Check number of parameters
+;
+ if N_params() lt 2 then begin
+   print,'Syntax - out_string = concat_dir( directory, filename)'
+   print,' ' 
+   return,''
+ endif
+;
+;  remove leading/trailing blanks
+;
+ dir0 = strtrim(dirname, 2)     
+ n_dir = N_Elements(dir0)
+;
+;  Act according to operating system
+;  Under Windows, if the directory starts with a dollar sign, then check to see
+;  the if it's really an environment variable.  If it is, then substitute the
+;  the environment variable for the directory name.
+;
+    IF !VERSION.OS_FAMILY EQ 'Windows' THEN BEGIN
+      FOR i = 0l, n_dir-1 DO BEGIN
+         FIRST = STRMID(DIR0[I], 0, 1)
+         IF FIRST EQ '$' THEN BEGIN
+             SLASH = STRPOS(DIR0[I]+'/','/') < STRPOS(DIR0[I]+'\','\')
+             TEST = GETENV(STRMID(DIR0[I],1,SLASH-1))
+             IF TEST NE '' THEN BEGIN
+                 IF STRLEN(DIR0[I]) GT SLASH THEN TEST = TEST + $
+                         STRMID(DIR0[I],SLASH,STRLEN(DIR0[I])-SLASH)
+                 DIR0[I] = TEST
+             ENDIF
+         ENDIF
+;
+         last = STRMID(dir0[i], STRLEN(dir0[i])-1, 1)
+         IF (last NE '\') AND (last NE '/') AND (last NE ':') THEN BEGIN
+            dir0[i] = dir0[i] + '\' ;append an ending '\' 
+         ENDIF
+      ENDFOR
+
+; Macintosh/UNIX  section
+
+ endif else  begin
+   psep = path_sep()
+    for i = 0l, n_dir-1 do begin
+        last = strmid(dir0[i], strlen(dir0[i])-1, 1)
+        if(last ne psep) then dir0[i] = dir0[i] + psep  ;append path separator 
+    endfor
+endelse 
+
+;
+;  no '/' needed when using default directory
+;
+ g  = where(dirname EQ '', Ndef) 
+ if Ndef GT 0 then dir0[g] = '' 
+ 
+ return, dir0 + filnam
+
+ end
diff --git a/Code/script_idl_mv/astrolib/cons_dec.pro b/Code/script_idl_mv/astrolib/cons_dec.pro
new file mode 100644
index 0000000000000000000000000000000000000000..414a9e18f5de78b8679bc42e5a5090a225f5ab96
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/cons_dec.pro
@@ -0,0 +1,116 @@
+FUNCTION CONS_DEC,DEC,X,ASTR,ALPHA        ;Find line of constant Dec
+;+
+; NAME:
+;       CONS_DEC
+; PURPOSE:
+;       Obtain the X and Y coordinates of a line of constant declination
+; EXPLANATION:
+;       Returns a set of Y pixels values, given an image with astrometry, and 
+;            either
+;       (1)  A set of X pixel values, and a scalar declination value, or
+;       (2)  A set of declination values, and a scalar X value
+;
+;       Form (1) can be used to find the (X,Y) values of a line of constant
+;       declination.  Form (2) can be used to find the Y positions of a set
+;       declinations, along a line of constant X.
+;
+; CALLING SEQUENCE:
+;       Y = CONS_DEC( DEC, X, ASTR, [ ALPHA ])
+;
+; INPUTS:
+;       DEC - Declination value(s) in DEGREES (-!PI/2 < DEC < !PI/2).  
+;               If X is a vector, then DEC must be a scalar.
+;       X -   Specified X pixel value(s) for line of constant declination 
+;               If DEC is a vector, then X must be a scalar.
+;       ASTR - Astrometry structure, as extracted from a FITS header by the
+;               procedure EXTAST
+; OUTPUT:
+;       Y   - Computed set of Y pixel values.  The number of Y values is the
+;               same as either DEC or X, whichever is greater.
+;
+; OPTIONAL OUTPUT:
+;       ALPHA - the right ascensions (DEGREES) associated with the (X,Y) points
+;
+; RESTRICTIONS:
+;       Implemented only for the TANgent, SIN and CAR projections
+;
+; NOTES:
+;       The algorithm (and notation) is based on AIPS Memo 27 by Eric Greisen,
+;       with modifications for a coordinate description (CD) matrix as 
+;       described in Paper II of Greisen & Calabretta (2002, A&A, 395, 1077).
+;       These documents are available from 
+;       http://www.cv.nrao.edu/fits/documents/wcs/wcs.html
+;
+; REVISION HISTORY:
+;       Written, Wayne Landsman  STX Co.                          April 1988
+;       Use new astrometry structure,     W. Landsman    HSTX     Jan. 1994
+;       Use CD matrix, add SIN projection   W. Landsman  HSTX     April, 1996
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Fix case where DEC is scalar, X is vector   W. Landsman RITSS Feb. 2000
+;       Fix possible sign error introduced Jan. 2000   W. Landsman  May 2000
+;       Work for the CARee' projection W. Landsman   May 2003
+;-
+  On_error,2
+
+  if N_params() lt 3 then begin
+        print,'Syntax - Y = CONS_DEC( DEC, X, ASTR, [ALPHA] )'
+        return, 0
+  endif
+
+  RADEG = 180.0D/!DPI
+ 
+  n = N_elements(x)
+  Ndec = N_elements(dec)
+  crpix = astr.crpix -1.
+  crval = astr.crval/RADEG
+  cd =  astr.cd/RADEG
+  cdelt = astr.cdelt
+
+  A = -cd[0,0]*cdelt[0] 
+  B = -cd[0,1]*cdelt[0] 
+  C =  cd[1,0]*cdelt[1]
+  D =  cd[1,1]*cdelt[1] 
+
+  xx = x - crpix[0]          ;New coordinate origin
+  sdel0 = sin(crval[1]) & cdel0 = cos(crval[1])
+
+  ctype = strupcase( strmid(astr.ctype[0], 5,3))
+  case ctype of 
+
+'TAN': begin
+  aa = d
+  bb = (b*c-d*a)*xx*cdel0 + sdel0*b
+  sign = 2*( aa GT 0 ) - 1 
+  alpha = crval[0] + atan(bb/aa) + $ 
+      sign * asin( tan(dec/RADEG)* ( (B*C-D*A)*xx*sdel0 - B*cdel0)/ $
+        sqrt(aa^2+bb^2))
+  end
+
+'SIN': begin
+
+  aa = d
+  bb = b*sdel0
+  sign = 2*( aa GT 0 ) - 1 
+
+  denom =  cos(dec/RADEG)*sqrt(aa^2+bb^2)
+  alpha = crval[0] + atan(bb/aa) + $ 
+     sign * asin( ( (b*c-a*d)*xx - sin(dec/RADEG)*cdel0*b ) / denom )
+  end
+
+'CAR': begin
+  alpha = crval[0] + (b*c -a*d)*xx   
+  if (N_elements(alpha) EQ 1) and (Ndec GT 1) then $
+        alpha = replicate(alpha[0],Ndec)
+end
+
+ELSE: message,'ERROR - Program only works for TAN, SIN and CAR projections'
+  endcase
+
+   alpha = alpha * RADEG
+
+   if (N_elements(dec) EQ 1) and (n GT 1) then $
+   ad2xy, alpha, replicate(dec, n) , astr, x1, y else $
+   ad2xy, alpha, dec, astr, x1, y
+
+  return,y
+  end
diff --git a/Code/script_idl_mv/astrolib/cons_ra.pro b/Code/script_idl_mv/astrolib/cons_ra.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c90fcb097d9808c8a6fdd7538b251b17f1438547
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/cons_ra.pro
@@ -0,0 +1,119 @@
+FUNCTION CONS_RA,RA,Y,ASTR, DELTA      ;Find line of constant RA
+;+
+; NAME:
+;       CONS_RA
+; PURPOSE:
+;       Obtain the X and Y coordinates of a line of constant right ascension
+; EXPLANATION:
+;       Return a set of X pixel values given an image with astrometry, 
+;       and either
+;       (1) a set of Y pixel values, and a scalar right ascension (or 
+;           longitude), or
+;       (2) a set of right ascension values, and a scalar Y value.
+;
+;       In usage (1), CONS_RA can be used to determine the (X,Y) values
+;       of a line of constant right ascension.  In usage (2), CONS_RA can
+;       used to determine the X positions of specified RA values, along a
+;       line of constant Y.
+;
+; CALLING SEQUENCE:
+;       X = CONS_RA( RA, Y, ASTR, [ DEC] )
+;
+; INPUTS:         
+;       RA -  Right Ascension value in DEGREES (0 < RA < 360.).  If Y is a
+;               vector, then RA must be a scalar
+;       Y -   Specified Y pixel value(s) for line of constant right ascension
+;               If RA is a vector, then Y must be a scalar
+;       ASTR - Astrometry structure as extracted from a FITS header by the 
+;               procedure EXTAST
+; OUTPUTS
+;       X   - Computed set of X pixel values.   The number of elements of X
+;               is the maximum of the number of elements of RA and Y.
+; OPTIONAL OUTPUT:
+;       DEC - Computed set of declinations (in DEGREES) for X,Y, coordinates
+; NOTES:
+;       The algorithm (and notation) is based on AIPS Memo 27 by Eric Greisen,
+;       with modifications for a coordinate description (CD) matrix as 
+;       described in Paper II of Calabretta & Greisen (2002, A&A, 395, 1077).
+;       These documents are available from 
+;       http://www.cv.nrao.edu/fits/documents/wcs/wcs.html
+;
+; RESTRICTIONS:
+;       Implemented only for the TANgent, SIN and CARtesian projections 
+;
+; REVISION HISTORY:
+;       Written, Wayne Landsman  STX Co.        April, 1988
+;       Algorithm adapted from AIPS memo No. 27 by Eric Greisen
+;       New astrometry structure
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added SIN projection    W. Landsman   January 2000
+;       Fix possible sign error introduced Jan. 2000   W. Landsman  May 2000
+;       Work for the CARee' projection W. Landsman   May 2003
+;       For TAN projection ensure angles between -90 and 90 W. Landsman Jan 2008
+;-
+  On_error,2
+  compile_opt idl2
+
+  if ( N_params() LT 3 ) then begin
+        print,'Syntax - X = CONS_RA( RA, Y, ASTR, [ Dec ])'
+        return, 0
+  endif
+
+  radeg = 180.0/!DPI
+  n = N_elements(y)
+  nra = N_elements(ra)
+  crpix = astr.crpix - 1.
+  crval = astr.crval/RADEG
+  cdelt = astr.cdelt
+  cdelta = [ [ cdelt[0], 0.],[0., cdelt[1] ] ]
+  cd = astr.cd/RADEG
+  cdel0 = cos( crval[1] )  &    sdel0 = sin( crval[1] )
+  delra = ra/RADEG - crval[0]
+  cdelra = cos( delra )    &    sdelra = sin( delra )
+
+  ctype = strupcase( strmid(astr.ctype[0], 5,3))
+  case ctype of 
+  
+  'TAN': begin
+ 
+  cdi = invert( cdelta # cd )     ;Greisen uses invert of CD matrix
+  yy = y - ( crpix[1])    ;New coordinate origin, Unit pixel offset in CRPIX
+  delta = atan((sdel0*cdelra*cdi[1,1] - sin(delra)*cdi[1,0] + yy*cdelra*cdel0) $
+              / (cdel0*cdi[1,1] - yy*sdel0))
+	      
+  end
+  'SIN': begin
+
+  A = -cd[0,0]*cdelt[0] 
+  B = -cd[0,1]*cdelt[0] 
+  C =  cd[1,0]*cdelt[1]
+  D =  cd[1,1]*cdelt[1] 
+  yy = (y - crpix[1])*(b*c - a*d)   ;New coordinate origin
+  aa = cdel0*d
+  bb = sdel0*cdelra*d + sdelra*b
+  denom = sqrt(aa^2 + bb^2)
+  delta = atan(bb/aa)  + asin(yy/denom)
+
+  end
+
+  'CAR': begin
+  A = -cd[0,0]*cdelt[0] 
+  B = -cd[0,1]*cdelt[0] 
+  C =  cd[1,0]*cdelt[1]
+  D =  cd[1,1]*cdelt[1] 
+  delta = (y - crpix[1])*(b*c - a*d)  +crval[1]  ;New coordinate origin
+  if (N_elements(delta) EQ 1) and (Nra GT 1)  then $
+           delta = replicate(delta[0],Nra)
+
+  end
+
+  ELSE: message,'ERROR - Program only works for TAN and SIN projections'
+  endcase
+
+  delta = delta*RADEG
+  if (Nra EQ 1) and (n GT 1) then $
+  ad2xy, replicate(ra,n), delta, astr, x else $
+  ad2xy, ra, delta, astr, x
+
+  return, x
+  end
diff --git a/Code/script_idl_mv/astrolib/convolve.pro b/Code/script_idl_mv/astrolib/convolve.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f56e016a738b30892db4d4ee9b177903675baacc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/convolve.pro
@@ -0,0 +1,178 @@
+function convolve, image, psf, FT_PSF=psf_FT, FT_IMAGE=imFT, NO_FT=noft, $
+                        CORRELATE=correlate, AUTO_CORRELATION=auto, $
+			NO_PAD = no_pad
+;+
+; NAME:
+;       CONVOLVE
+; PURPOSE:
+;       Convolution of an image with a Point Spread Function (PSF)
+; EXPLANATION:
+;       The default is to compute the convolution using a product of 
+;       Fourier transforms (for speed).
+;
+;       The image is padded with zeros so that a large PSF does not
+;       overlap one edge of the image with the opposite edge of the image.
+;
+;       This routine is now partially obsolete due to the introduction of  the
+;       intrinsic CONVOL_FFT() function in IDL 8.1
+;
+; CALLING SEQUENCE:
+;
+;       imconv = convolve( image1, psf, FT_PSF = psf_FT )
+;  or:
+;       correl = convolve( image1, image2, /CORREL )
+;  or:
+;       correl = convolve( image, /AUTO )
+;
+; INPUTS:
+;       image = 2-D array (matrix) to be convolved with psf
+;       psf = the Point Spread Function, (size < or = to size of image).
+;
+;       The PSF *must* be symmetric about the point
+;       FLOOR((n_elements-1)/2), where n_elements is the number of
+;       elements in each dimension.  For Gaussian PSFs, the maximum
+;       of the PSF must occur in this pixel (otherwise the convolution
+;       will shift everything in the image).
+;
+; OPTIONAL INPUT KEYWORDS:
+;
+;       FT_PSF = passes out/in the Fourier transform of the PSF,
+;               (so that it can be re-used the next time function is called).
+;       FT_IMAGE = passes out/in the Fourier transform of image.
+;
+;       /CORRELATE uses the conjugate of the Fourier transform of PSF,
+;               to compute the cross-correlation of image and PSF,
+;               (equivalent to IDL function convol() with NO rotation of PSF)
+;
+;       /AUTO_CORR computes the auto-correlation function of image using FFT.
+;
+;       /NO_FT overrides the use of FFT, using IDL function convol() instead.
+;               (then PSF is rotated by 180 degrees to give same result)
+;
+;       /NO_PAD - if set, then do not pad the image to avoid edge effects.
+;               This will improve memory and speed of the computation at the 
+;               expense of edge effects.   This was the default method prior 
+;               to October 2009
+; METHOD:
+;       When using FFT, PSF is centered & expanded to size of image.
+; HISTORY:
+;       written, Frank Varosi, NASA/GSFC 1992.
+;       Appropriate precision type for result depending on input image
+;                               Markus Hundertmark February 2006
+;       Fix the bug causing the recomputation of FFT(psf) and/or FFT(image)
+;                               Sergey Koposov     December 2006
+;       Fix the centering bug
+;                               Kyle Penner        October 2009
+;       Add /No_PAD keyword for better speed and memory usage when edge effects
+;            are not important.    W. Landsman      March 2010
+;       Add warning when kernel type does not match integer array
+;             W. Landsman Feb 2012
+;       Don't force double precision output   W. Landsman July 2014
+;-
+        compile_opt idl2
+        sp = size( psf_FT,/str )  &  sif = size( imFT, /str )
+        sim = size( image )  
+
+
+        if (sim[0] NE 2) || keyword_set( noft ) then begin
+                if keyword_set( auto ) then begin
+                        message,"auto-correlation only for images with FFT",/INF
+                        return, image
+                 endif
+		 dtype = size(image,/type)
+		 if dtype LE 3 then if size(psf,/type) NE dtype then $
+		    message,/CON, $
+		 'WARNING - ' + size(psf,/TNAME) +  $
+		 ' kernel converted to type ' + size(image,/tname)    
+		 if keyword_set( correlate ) then $
+                                return, convol( image, psf ) $
+                 else    return, convol( image, rotate( psf, 2 ) )
+           endif
+
+       if keyword_Set(No_Pad) then begin 
+ 
+        sc = sim/2  &  npix = N_elements( image )
+        if (sif.N_dimensions NE 2) || ((sif.type NE 6) && (sif.type NE 9)) || $
+           (sif.dimensions[0] NE sim[1]) || (sif.dimensions[1] NE sim[2]) then imFT = FFT( image,-1 )
+
+        if keyword_set( auto ) then $
+         return, shift( npix*real_part(FFT( imFT*conj( imFT ),1 )), sc[1],sc[2] )
+
+        if (sp.N_dimensions NE 2) || ((sp.type NE 6) && (sp.type NE 9)) || $
+           (sp.dimensions[0] NE sim[1]) || (sp.dimensions[1] NE sim[2]) then begin
+                sp = size( psf )
+                if (sp[0] NE 2) then begin
+                        message,"must supply PSF matrix (2nd arg.)",/INFO
+                        return, image
+                   endif
+                Loc = ( sc - sp/2 ) > 0         ;center PSF in new array,
+                s = (sp/2 - sc) > 0        ;handle all cases: smaller or bigger
+                L = (s + sim-1) < (sp-1)
+                psf_FT = conj(image)*0 ;initialise with correct size+type according 
+                ;to logic of conj and set values to 0 (type of psf_FT is conserved)  
+                psf_FT[ Loc[1], Loc[2] ] = psf[ s[1]:L[1], s[2]:L[2] ]
+                psf_FT = FFT( psf_FT, -1, /OVERWRITE )
+           endif
+
+        if keyword_set( correlate ) then $
+                conv = npix * real_part(FFT( imFT * conj( psf_FT ), 1 ))  $
+          else  conv = npix * real_part(FFT( imFT * psf_FT, 1 )) 
+
+        sc = sc + (sim MOD 2)   ;shift correction for odd size images.
+
+        return, shift( conv, sc[1], sc[2] )
+   endif else begin  
+ 
+ 
+          sc = floor((sim-1)/2) & npix = n_elements(image)*4.
+        ; the spooky factor of 4 in npix is because we're going to pad the image
+        ; with zeros
+
+         if (sif.N_dimensions NE 2) || ((sif.type NE 6) && (sif.type NE 9)) || $
+           (sif.dimensions[0] NE sim[1]) || (sif.dimensions[1] NE sim[2]) then begin
+
+            ; here is where we make an array with twice the dimensions of image and
+            ; pad with zeros -- thanks to Daniel Eisenstein for this fix
+
+            image_big = make_array(type = sim[sim[0]+1], sim[1]*2, sim[2]*2)
+            image_big[0:sim[1]-1,0:sim[2]-1] = image
+            imFT = FFT( image_big,-1 )
+            npix = n_elements(image_big)
+
+        endif
+
+        if keyword_set( auto ) then begin
+         intermed = shift( npix*real_part(FFT( imFT*conj( imFT ),1 )), sc[1],sc[2] )
+         return, intermed[0:sim[1]-1,0:sim[2]-1]
+     endif
+
+
+        if (sp.N_dimensions NE 2) || ((sp.type NE 6) && (sp.type NE 9)) OR $
+           (sp.dimensions[0] NE sim[1]) || (sp.dimensions[1] NE sim[2]) then begin
+                sp = size( psf )
+                if (sp[0] NE 2) then begin
+                        message,"must supply PSF matrix (2nd arg.)",/INFO
+                        return, image
+                   endif
+                ; this obfuscated line determines the offset between the center of the
+                ; image and the center of the PSF
+                Loc = ( sc - floor((sp-1)/2) )  > 0
+
+          psf_image = make_array(type = sim[sim[0]+1],sim[1]*2,sim[2]*2)
+                psf_image[Loc[1]:Loc[1]+sp[1]-1, Loc[2]:Loc[2]+sp[2]-1] = psf
+                psf_FT = FFT(psf_image, -1)
+           endif
+
+        if keyword_set( correlate ) then begin
+                conv = npix * real_part(FFT( imFT * conj( psf_FT ), 1 ))
+                conv = shift(conv, sc[1], sc[2])
+            endif else begin
+                conv = npix * real_part(FFT( imFT * psf_FT, 1 )) 
+                conv = shift(conv, -sc[1], -sc[2])
+
+            endelse
+
+        
+        return, conv[0:sim[1]-1,0:sim[2]-1]
+      endelse
+end
diff --git a/Code/script_idl_mv/astrolib/copy_struct.pro b/Code/script_idl_mv/astrolib/copy_struct.pro
new file mode 100644
index 0000000000000000000000000000000000000000..147fc0da185a4665e9964b1a413c86963d884ad0
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/copy_struct.pro
@@ -0,0 +1,250 @@
+;+
+; NAME:
+;	COPY_STRUCT
+; PURPOSE:
+; 	Copy all fields with matching tag names from one structure to another
+; EXPLANATION:
+;       COPY_STRUCT is similar to the intrinsic STRUCT_ASSIGN procedure but 
+;       has optional keywords to exclude or specify specific tags.
+;  
+;	Fields with matching tag names are copied from one structure array to 
+;	another structure array of different type.
+;	This allows copying of tag values when equating the structures of
+;	different types is not allowed, or when not all tags are to be copied.
+;	Can also recursively copy from/to structures nested within structures.
+;	Note that the number of elements in the output structure array
+;	is automatically adjusted to equal the length of input structure array.
+;	If this not desired then use pro copy_struct_inx which allows
+;	specifying via subscripts which elements are copied where in the arrays.
+;
+; CALLING SEQUENCE:
+;
+;	copy_struct, struct_From, struct_To, NT_copied
+;	copy_struct, struct_From, struct_To, EXCEPT=["image","misc"]
+;	copy_struct, struct_From, struct_To, /RECUR_TANDEM
+;
+; INPUTS:
+;	struct_From = structure array to copy from.
+;	struct_To = structure array to copy values to.
+;
+; KEYWORDS:
+;
+;	EXCEPT_TAGS = string array of tag names to ignore (to NOT copy).
+;		Used at all levels of recursion.
+;
+;	SELECT_TAGS = tag names to copy (takes priority over EXCEPT).
+;		This keyword is not passed to recursive calls in order
+;		to avoid the confusion of not copying tags in sub-structures.
+;
+;	/RECUR_FROM = search for sub-structures in struct_From, and then
+;		call copy_struct recursively for those nested structures.
+;
+;	/RECUR_TO = search for sub-structures of struct_To, and then
+;		call copy_struct recursively for those nested structures.
+;
+;	/RECUR_TANDEM = call copy_struct recursively for the sub-structures
+;		with matching Tag names in struct_From and struct_To
+;		(for use when Tag names match but sub-structure types differ).
+;
+; OUTPUTS:
+;	struct_To = structure array to which new tag values are copied.
+;	NT_copied = incremented by total # of tags copied (optional)
+;
+; INTERNAL:
+;	Recur_Level = # of times copy_struct calls itself.
+;		This argument is for internal recursive execution only.
+;		The user call is 1, subsequent recursive calls increment it,
+;		and the counter is decremented before returning.
+;		The counter is used just to find out if argument checking
+;		should be performed, and to set NT_copied = 0 first call.
+; EXTERNAL CALLS:
+;	pro match	(when keyword SELECT_TAGS is specified)
+; PROCEDURE:
+;	Match Tag names and then use corresponding Tag numbers.
+; HISTORY:
+;	written 1989 Frank Varosi STX @ NASA/GSFC
+; 	mod Jul.90 by F.V. added option to copy sub-structures RECURSIVELY.
+;	mod Aug.90 by F.V. adjust # elements in TO (output) to equal
+;			# elements in FROM (input) & count # of fields copied.
+;	mod Jan.91 by F.V. added Recur_Level as internal argument so that
+;			argument checking done just once, to avoid confusion.
+;			Checked against Except_Tags in RECUR_FROM option.
+;	mod Oct.91 by F.V. added option SELECT_TAGS= selected field names.
+;	mod Aug.95 by W. Landsman to fix match of a single selected tag.
+;	mod Mar.97 by F.V. do not pass the SELECT_TAGS keyword in recursion.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       mod May 01 by D. Schlegel use long integers
+;-
+
+pro copy_struct, struct_From, struct_To, NT_copied, Recur_Level,            $
+						EXCEPT_TAGS  = except_Tags, $
+						SELECT_TAGS  = select_Tags, $
+						RECUR_From   = recur_From,  $
+						RECUR_TO     = recur_To,    $
+						RECUR_TANDEM = recur_tandem
+
+	if N_elements( Recur_Level ) NE 1 then Recur_Level = 0L
+
+	Ntag_from = N_tags( struct_From )
+	Ntag_to = N_tags( struct_To )
+
+	if (Recur_Level EQ 0) then begin	;check only at first user call.
+
+		NT_copied = 0L
+
+		if (Ntag_from LE 0) OR (Ntag_to LE 0) then begin
+			message,"two arguments must be structures",/INFO
+			print," "
+			print,"syntax:    copy_struct, struct_From, struct_To"
+			print," "
+			print,"keywords:	EXCEPT_TAGS= , SELECT_TAGS=,  "
+			print,"		/RECUR_From,  /RECUR_TO,  /RECUR_TANDEM"
+			return
+		   endif
+
+		N_from = N_elements( struct_From )
+		N_to = N_elements( struct_To )
+
+		if (N_from GT N_to) then begin
+
+			message," # elements (" + strtrim( N_to, 2 ) + $
+					") in output TO structure",/INFO
+			message," increased to (" + strtrim( N_from, 2 ) + $
+					") as in FROM structure",/INFO
+			struct_To = [ struct_To, $
+					replicate( struct_To[0], N_from-N_to ) ]
+
+		  endif	else if (N_from LT N_to) then begin
+
+			message," # elements (" + strtrim( N_to, 2 ) + $
+					") in output TO structure",/INFO
+			message," decreased to (" + strtrim( N_from, 2 ) + $
+					") as in FROM structure",/INFO
+			struct_To = struct_To[0:N_from-1]
+		   endif
+	   endif
+
+	Recur_Level = Recur_Level + 1		;go for it...
+
+	Tags_from = Tag_names( struct_From )
+	Tags_to = Tag_names( struct_To )
+	wto = lindgen( Ntag_to )
+
+;Determine which Tags are selected or excluded from copying:
+
+	Nseltag = N_elements( select_Tags )
+	Nextag = N_elements( except_Tags )
+
+	if (Nseltag GT 0) then begin
+
+		match, Tags_to, [strupcase( select_Tags )], mt, ms,COUNT=Ntag_to
+
+		if (Ntag_to LE 0) then begin
+			message," selected tags not found",/INFO
+			return
+		   endif
+
+		Tags_to = Tags_to[mt]
+		wto = wto[mt]
+
+	  endif else if (Nextag GT 0) then begin
+
+		except_Tags = [strupcase( except_Tags )]
+
+		for t=0L,Nextag-1 do begin
+			w = where( Tags_to NE except_Tags[t], Ntag_to )
+			Tags_to = Tags_to[w]
+			wto = wto[w]
+		  endfor
+	   endif
+
+;Now find the matching Tags and copy them...
+
+	for t = 0L, Ntag_to-1 do begin
+
+		wf = where( Tags_from EQ Tags_to[t] , nf )
+
+		if (nf GT 0) then begin
+
+			from = wf[0]
+			to = wto[t]
+
+			if keyword_set( recur_tandem ) AND		$
+			   ( N_tags( struct_To.(to) ) GT 0 ) AND	$
+			   ( N_tags( struct_From.(from) ) GT 0 ) then begin
+
+				struct_tmp = struct_To.(to)
+
+				copy_struct, struct_From.(from), struct_tmp,  $
+						NT_copied, Recur_Level,       $
+						EXCEPT=except_Tags,           $
+						/RECUR_TANDEM,                $
+						RECUR_FROM = recur_From,      $
+						RECUR_TO   = recur_To
+
+				struct_To.(to) = struct_tmp
+
+			  endif else begin
+
+				struct_To.(to) = struct_From.(from)
+				NT_copied = NT_copied + 1
+			   endelse
+		  endif
+	  endfor
+
+;Handle request for recursion on FROM structure:
+
+	if keyword_set( recur_From ) then begin
+
+		wfrom = lindgen( Ntag_from )
+
+		if (Nextag GT 0) then begin
+
+			for t=0L,Nextag-1 do begin
+			    w = where( Tags_from NE except_Tags[t], Ntag_from )
+			    Tags_from = Tags_from[w]
+			    wfrom = wfrom[w]
+			  endfor
+		   endif
+
+		for t = 0L, Ntag_from-1 do begin
+
+		     from = wfrom[t]
+
+		     if N_tags( struct_From.(from) ) GT 0 then begin
+
+			copy_struct, struct_From.(from), struct_To,        $
+						NT_copied, Recur_Level,    $
+						EXCEPT=except_Tags,        $
+						/RECUR_FROM,               $
+						RECUR_TO     = recur_To,   $
+						RECUR_TANDEM = recur_tandem
+			endif
+		  endfor
+	  endif
+
+;Handle request for recursion on TO structure:
+
+	if keyword_set( recur_To ) then begin
+
+		for t = 0L, Ntag_to-1 do begin
+
+		   to = wto[t]
+
+		   if N_tags( struct_To.(to) ) GT 0 then begin
+
+			struct_tmp = struct_To.(to)
+
+			copy_struct, struct_From, struct_tmp,              $
+						NT_copied, Recur_Level,    $
+						EXCEPT=except_Tags,        $
+						/RECUR_TO,                 $
+						RECUR_FROM = recur_From,   $
+						RECUR_TANDEM = recur_tandem
+			struct_To.(to) = struct_tmp
+		     endif
+		  endfor
+	  endif
+
+	Recur_Level = Recur_Level - 1
+end
diff --git a/Code/script_idl_mv/astrolib/copy_struct_inx.pro b/Code/script_idl_mv/astrolib/copy_struct_inx.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c162bb41f8ba41cdac069faf7c54588099063d14
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/copy_struct_inx.pro
@@ -0,0 +1,287 @@
+;+
+; NAME:
+;	COPY_STRUCT_INX
+; PURPOSE:
+;	Copy matching tags & specified indices from one structure to another
+; EXPLANATION:
+; 	Copy all fields with matching tag names (except for "except_Tags")
+;	from one structure array to another structure array of different type.
+;	This allows copying of tag values when equating the structures of
+;	different types is not allowed, or when not all tags are to be copied.
+;	Can also recursively copy from/to structures nested within structures.
+;	This procedure is same as copy_struct with option to
+;	specify indices (subscripts) of which array elements to copy from/to.
+; CALLING SEQUENCE:
+;
+;	copy_struct_inx, struct_From, struct_To, NT_copied, INDEX_FROM=subf
+;
+;	copy_struct_inx, struct_From, struct_To, INDEX_FROM=subf, INDEX_TO=subto
+;
+; INPUTS:
+;	struct_From = structure array to copy from.
+;	struct_To = structure array to copy values to.
+;
+; KEYWORDS:
+;
+;	INDEX_FROM = indices (subscripts) of which elements of array to copy.
+;		(default is all elements of input structure array)
+;
+;	INDEX_TO = indices (subscripts) of which elements to copy to.
+;		(default is all elements of output structure array)
+;
+;	EXCEPT_TAGS = string array of Tag names to ignore (to NOT copy).
+;		Used at all levels of recursion.
+;
+;	SELECT_TAGS = Tag names to copy (takes priority over EXCEPT).
+;		This keyword is not passed to recursive calls in order
+;		to avoid the confusion of not copying tags in sub-structures.
+;
+;	/RECUR_FROM = search for sub-structures in struct_From, and then
+;		call copy_struct recursively for those nested structures.
+;
+;	/RECUR_TO = search for sub-structures of struct_To, and then
+;		call copy_struct recursively for those nested structures.
+;
+;	/RECUR_TANDEM = call copy_struct recursively for the sub-structures
+;		with matching Tag names in struct_From and struct_To
+;		(for use when Tag names match but sub-structure types differ).
+;
+; OUTPUTS:
+;	struct_To = structure array to which new tag values are copied.
+;	NT_copied = incremented by total # of tags copied (optional)
+;
+; INTERNAL:
+;	Recur_Level = # of times copy_struct_inx calls itself.
+;		This argument is for internal recursive execution only.
+;		The user call is 1, subsequent recursive calls increment it,
+;		and the counter is decremented before returning.
+;		The counter is used just to find out if argument checking
+;		should be performed, and to set NT_copied = 0 first call.
+; EXTERNAL CALLS:
+;	pro match	(when keyword SELECT_TAGS is specified)
+; PROCEDURE:
+;	Match Tag names and then use corresponding Tag numbers,
+;	apply the sub-indices during = and recursion.
+; HISTORY:
+;	adapted from copy_struct: 1991 Frank Varosi STX @ NASA/GSFC
+;	mod Aug.95 by F.V. to fix match of a single selected tag.
+;	mod Mar.97 by F.V. do not pass the SELECT_TAGS keyword in recursion,
+;		and check validity of INDEX_FROM and INDEX_TO in more detail.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Use long integers W. Landsman May 2001  
+;-
+
+pro copy_struct_inx, struct_From, struct_To, NT_copied, Recur_Level,        $
+						EXCEPT_TAGS  = except_Tags, $
+						SELECT_TAGS  = select_Tags, $
+						INDEX_From   = index_From,  $
+						INDEX_To     = index_To,    $
+						RECUR_From   = recur_From,  $
+						RECUR_To     = recur_To,    $
+						RECUR_TANDEM = recur_tandem
+
+	if N_elements( Recur_Level ) NE 1 then Recur_Level = 0L
+
+	Ntag_from = N_tags( struct_From )
+	Ntag_to = N_tags( struct_To )
+
+	if (Recur_Level EQ 0) then begin	;check only at first user call.
+
+		NT_copied = 0L
+
+		if (Ntag_from LE 0) OR (Ntag_to LE 0) then begin
+			message,"two arguments must be structures",/INFO
+			print," "
+			print,"syntax:  copy_struct_inx, struct_From, struct_To"
+			print," "
+			print,"keywords:	INDEX_From= , INDEX_To="
+			print,"		EXCEPT_TAGS= , SELECT_TAGS=,  "
+			print,"		/RECUR_From,  /RECUR_To,  /RECUR_TANDEM"
+			return
+		   endif
+
+		N_from = N_elements( struct_From )
+		N_to = N_elements( struct_To )
+
+		if N_elements( index_From ) LE 0 then index_From = $
+								lindgen( N_from )
+		Ni_from = N_elements( index_From )
+		if N_elements( index_To ) LE 0 then index_To = lindgen( Ni_from )
+		Ni_to = N_elements( index_To )
+
+		if (Ni_from LT Ni_to) then begin
+
+			message," # elements (" + strtrim( Ni_to, 2 ) + $
+					") in output TO indices",/INFO
+			message," decreased to (" + strtrim( Ni_from, 2 ) + $
+					") as in FROM indices",/INFO
+			index_To = index_To[0:Ni_from-1]
+
+		  endif	else if (Ni_from GT Ni_to) then begin
+
+			message," # elements (" + strtrim( Ni_from, 2 ) + $
+					") of input FROM indices",/INFO
+			message," decreased to (" + strtrim( Ni_to, 2 ) + $
+					") as in TO indices",/INFO
+			index_From = index_From[0:Ni_to-1]
+		   endif
+
+		Mi_to = max( [index_To] )
+		Mi_from = max( [index_From] )
+
+		if (Mi_to GE N_to) then begin
+
+			message," # elements (" + strtrim( N_to, 2 ) + $
+					") in output TO structure",/INFO
+			message," increased to (" + strtrim( Mi_to, 2 ) + $
+					") as max value of INDEX_To",/INFO
+			struct_To = [ struct_To, $
+					replicate( struct_To[0], Mi_to-N_to ) ]
+		  endif
+
+ 		if (Mi_from GE N_from) then begin
+
+			w = where( index_From LT N_from, nw )
+
+			if (nw GT 0) then begin
+				index_From = index_From[w]
+				message,"max value (" + strtrim( Mi_from, 2 ) +$
+					") in FROM indices",/INFO
+				print,"decreased to " + strtrim( N_from,2 ) + $
+					") as in FROM structure",/INFO
+			 endif else begin
+				message,"all FROM indices are out of bounds",/IN
+				return
+			  endelse
+		  endif
+	   endif
+
+	Recur_Level = Recur_Level + 1		;go for it...
+
+	Tags_from = Tag_names( struct_From )
+	Tags_to = Tag_names( struct_To )
+	wto = lindgen( Ntag_to )
+
+;Determine which Tags are selected or excluded from copying:
+
+	Nseltag = N_elements( select_Tags )
+	Nextag = N_elements( except_Tags )
+
+	if (Nseltag GT 0) then begin
+
+		match, Tags_to, [strupcase( select_Tags )], mt, ms,COUNT=Ntag_to
+
+		if (Ntag_to LE 0) then begin
+			message," selected tags not found",/INFO
+			return
+		   endif
+
+		Tags_to = Tags_to[mt]
+		wto = wto[mt]
+
+	  endif else if (Nextag GT 0) then begin
+
+		except_Tags = [strupcase( except_Tags )]
+
+		for t=0L,Nextag-1 do begin
+			w = where( Tags_to NE except_Tags[t], Ntag_to )
+			Tags_to = Tags_to[w]
+			wto = wto[w]
+		  endfor
+	   endif
+
+;Now find the matching Tags and copy them...
+
+	for t = 0L, Ntag_to-1 do begin
+
+		wf = where( Tags_from EQ Tags_to[t] , nf )
+
+		if (nf GT 0) then begin
+
+			from = wf[0]
+			to = wto[t]
+
+			if keyword_set( recur_tandem ) AND		$
+			   ( N_tags( struct_To.(to) ) GT 0 ) AND	$
+			   ( N_tags( struct_From.(from) ) GT 0 ) then begin
+
+				struct_tmp = struct_To[index_To].(to)
+
+				copy_struct, struct_From[index_From].(from),  $
+						struct_tmp,                   $
+						NT_copied, Recur_Level,       $
+						EXCEPT=except_Tags,           $
+						/RECUR_TANDEM,                $
+						RECUR_FROM = recur_From,      $
+						RECUR_To   = recur_To
+
+				struct_To[index_To].(to) = struct_tmp
+
+			  endif else begin
+
+				struct_To[index_To].(to) = $
+				struct_From[index_From].(from)
+				NT_copied = NT_copied + 1
+			   endelse
+		  endif
+	  endfor
+
+;Handle request for recursion on FROM structure:
+
+	if keyword_set( recur_From ) then begin
+
+		wfrom = lindgen( Ntag_from )
+
+		if (Nextag GT 0) then begin
+
+			for t=0L,Nextag-1 do begin
+			    w = where( Tags_from NE except_Tags[t], Ntag_from )
+			    Tags_from = Tags_from[w]
+			    wfrom = wfrom[w]
+			  endfor
+		   endif
+
+		for t = 0L, Ntag_from-1 do begin
+
+		     from = wfrom[t]
+
+		     if N_tags( struct_From.(from) ) GT 0 then begin
+
+			copy_struct_inx, struct_From.(from), struct_To,        $
+						NT_copied, Recur_Level,    $
+						EXCEPT=except_Tags,        $
+						/RECUR_FROM,               $
+						INDEX_From   = index_From, $
+						INDEX_To     = index_To,   $
+						RECUR_To     = recur_To,   $
+						RECUR_TANDEM = recur_tandem
+			endif
+		  endfor
+	  endif
+
+;Handle request for recursion on TO structure:
+
+	if keyword_set( recur_To ) then begin
+
+		for t = 0L, Ntag_to-1 do begin
+
+		   to = wto[t]
+
+		   if N_tags( struct_To.(to) ) GT 0 then begin
+
+			struct_tmp = struct_To[index_To].(to)
+
+			copy_struct_inx, struct_From, struct_tmp,          $
+						NT_copied, Recur_Level,    $
+						EXCEPT=except_Tags,        $
+						/RECUR_To,                 $
+						INDEX_From   = index_From, $
+						RECUR_FROM = recur_From,   $
+						RECUR_TANDEM = recur_tandem
+			struct_To[index_To].(to) = struct_tmp
+		     endif
+		  endfor
+	  endif
+
+   Recur_Level = Recur_Level - 1
+end
diff --git a/Code/script_idl_mv/astrolib/correl_images.pro b/Code/script_idl_mv/astrolib/correl_images.pro
new file mode 100644
index 0000000000000000000000000000000000000000..de9aaa2009d4c112ca94e9224f29405c2e7e1691
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/correl_images.pro
@@ -0,0 +1,210 @@
+function correl_images, image_A, image_B, XSHIFT = x_shift,	$
+					  YSHIFT = y_shift, 	$
+					  XOFFSET_B = x_offset, $
+					  YOFFSET_B = y_offset, $
+					  REDUCTION = reducf,	$
+					  MAGNIFICATION = Magf, $
+					  NUMPIX=numpix, MONITOR=monitor
+;+
+; NAME:
+;	CORREL_IMAGES
+; PURPOSE:
+;       Compute the 2-D cross-correlation function of two images
+; EXPLANATION:
+;       Computes the 2-D cross-correlation function of two images for
+;       a range of (x,y) shifting by pixels of one image relative to the other.
+;
+; CALLING SEQUENCE:
+;       Result = CORREL_IMAGES( image_A, image_B, 
+;                        [XSHIFT=, YSHIFT=, XOFFSET_B=, YOFFSET_B=, REDUCTION=, 
+;                        MAGNIFICATION=, /NUMPIX, /MONITOR  )
+;
+; INPUTS:
+;       image_A, image_B = the two images of interest.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       XSHIFT = the + & - shift to be applied in X direction, default=7.
+;       YSHIFT = the Y direction + & - shifting, default=7.
+;
+;       XOFFSET_B = initial X pixel offset of image_B relative to image_A.
+;       YOFFSET_B = Y pixel offset, defaults are (0,0).
+;
+;       REDUCTION = optional reduction factor causes computation of
+;                       Low resolution correlation of bin averaged images,
+;                       thus faster. Can be used to get approximate optimal
+;                       (x,y) offset of images, and then called for successive
+;                       lower reductions in conjunction with CorrMat_Analyze
+;                       until REDUCTION=1, getting offset up to single pixel.
+;
+;       MAGNIFICATION = option causes computation of high resolution correlation
+;                       of magnified images, thus much slower.
+;                       Shifting distance is automatically = 2 + Magnification,
+;                       and optimal pixel offset should be known and specified.
+;                       Optimal offset can then be found to fractional pixels
+;                       using CorrMat_Analyze( correl_images( ) ).
+;
+;       /NUMPIX - if set, causes the number of pixels for each correlation
+;                       to be saved in a second image, concatenated to the
+;                       correlation image, so Result is fltarr( Nx, Ny, 2 ).
+;       /MONITOR causes the progress of computation to be briefly printed.
+;
+; OUTPUTS:
+;       Result is the cross-correlation function, given as a matrix.
+;
+; PROCEDURE:
+;       Loop over all possible (x,y) shifts, compute overlap and correlation
+;       for each shift. Correlation set to zero when there is no overlap.
+;
+; MODIFICATION HISTORY:
+;       Written, July,1991, Frank Varosi, STX @ NASA/GSFC
+;       Use ROUND instead of NINT, June 1995, Wayne Landsman HSTX
+;       Avoid divide by zero errors, W. Landsman HSTX April 1996
+;	Remove use of !DEBUG    W. Landsman   June 1997
+;       Subtract mean of entire image before computing correlation, not just 
+;          mean of overlap region   H. Ebeling/W. Landsman   June 1998
+;       Always REBIN() using floating pt arithmetic W. Landsman  Nov 2007
+;       
+;-
+ compile_opt idl2
+ if N_params() LT 2 then begin 
+        print,'Syntax  -  Result = CORREL_IMAGES( image_A, image_B,'
+	print,'[         XSHIFT=, YSHIFT=, XOFFSET_B=, YOFFSET_B=, REDUCTION=, '
+	print,'          MAGNIFICATION=, /NUMPIX, /MONITOR  )'
+	return,-1
+ endif
+ 	
+	simA = size( image_A )
+	simB = size( image_B )
+	do_int = (simA[3] LE 3) or (simA[3] GE 12) or $ 
+                 (simB[3] LE 3) or (simB[3] GE 12)
+		 
+	if (simA[0] LT 2) OR (simB[0] LT 2) then begin
+		message,"first two arguments must be images",/INFO,/CONTIN
+		return,[-1]
+	   endif
+
+	if N_elements( x_offset ) NE 1 then x_offset=0
+	if N_elements( y_offset ) NE 1 then y_offset=0
+
+	if N_elements( x_shift ) NE 1 then x_shift = 7
+	if N_elements( y_shift ) NE 1 then y_shift = 7
+	x_shift = abs( x_shift )
+	y_shift = abs( y_shift )
+
+	if keyword_set( reducf ) then begin
+
+		reducf = fix( reducf ) > 1
+		if keyword_set( monitor ) then $
+				print,"Reduction = ",strtrim( reducf, 2 )
+		simA = simA/reducf
+		LA = simA * reducf -1	;may have to drop edges of images.
+		simB = simB/reducf
+		LB = simB * reducf -1
+
+                if do_int then begin 
+		
+		imtmp_A = Rebin( float( image_A[ 0:LA[1], 0:LA[2] ]),  $
+		                       simA[1], simA[2] )
+		imtmp_B = Rebin( float( image_B[ 0:LB[1], 0:LB[2] ]),  $ 
+		                        simB[1], simB[2] )
+		endif else begin 
+		imtmp_A =Rebin( image_A[ 0:LA[1], 0:LA[2] ], simA[1], simA[2] )
+		imtmp_B =Rebin( image_B[ 0:LB[1], 0:LB[2] ], simB[1], simB[2] )
+                 endelse 
+
+		xoff = round ( x_offset/reducf )
+		yoff = round ( y_offset/reducf )
+		xs = x_shift/reducf
+		ys = y_shift/reducf
+
+		return, correl_images( imtmp_A, imtmp_B, XS=xs,YS=ys,$
+							XOFF=xoff, YOFF=yoff, $
+						MONITOR=monitor, NUMPIX=numpix )
+
+	  endif else if keyword_set( Magf ) then begin
+
+		Magf = fix( Magf ) > 1
+		if keyword_set( monitor ) then $
+				print,"Magnification = ",strtrim( Magf, 2 )
+		simA = simA*Magf
+		simB = simB*Magf
+
+		imtmp_A = rebin( image_A, simA[1], simA[2], /SAMPLE )
+		imtmp_B = rebin( image_B, simB[1], simB[2], /SAMPLE )
+
+		xoff = round( x_offset*Magf )
+		yoff = round( y_offset*Magf )
+
+		return, correl_images( imtmp_A, imtmp_B, XS=Magf+2, YS=Magf+2,$
+							XOFF=xoff, YOFF=yoff, $
+						MONITOR=monitor, NUMPIX=numpix )
+	   endif
+
+	Nx = 2 * x_shift + 1
+	Ny = 2 * y_shift + 1
+	if keyword_set( numpix ) then Nim=2 else Nim=1
+
+	correl_mat = fltarr( Nx, Ny, Nim )
+
+	xs = round( x_offset ) - x_shift
+	ys = round( y_offset ) - y_shift
+
+	sAx = simA[1]-1
+	sAy = simA[2]-1
+	sBx = simB[1]-1
+	sBy = simB[2]-1
+	meanA = total( image_A )/(simA[1]*simA[2])
+	meanB = total( image_B )/(simB[1]*simB[2])
+
+	for y = 0, Ny-1 do begin	;compute correlation for each y,x shift.
+
+	    yoff = ys + y
+	    yAmin = yoff > 0
+	    yAmax = sAy < (sBy + yoff)
+	    yBmin = (-yoff) > 0
+	    yBmax = sBy < (sAy - yoff)		;Y overlap
+
+	    if (yAmax GT yAmin) then begin
+
+	       for x = 0, Nx-1 do begin
+
+		   xoff = xs + x
+		   xAmin = xoff > 0
+		   xAmax = sAx < (sBx + xoff)
+		   xBmin = (-xoff) > 0
+		   xBmax = sBx < (sAx - xoff)		;X overlap
+
+		   if (xAmax GT xAmin) then begin
+
+			im_ov_A = image_A[ xAmin:xAmax, yAmin:yAmax ]
+			im_ov_B = image_B[ xBmin:xBmax, yBmin:yBmax ]
+			Npix = N_elements( im_ov_A )
+
+			if N_elements( im_ov_B ) NE Npix then begin
+				message,"overlap error: # pixels NE",/INFO,/CONT
+				print, Npix, N_elements( im_ov_B )
+			   endif
+
+			im_ov_A = im_ov_A - meanA
+			im_ov_B = im_ov_B - meanB			
+			totAA = total( im_ov_A * im_ov_A )
+			totBB = total( im_ov_B * im_ov_B )
+
+                        if (totAA EQ 0) or (totBB EQ 0) then $
+                        correl_mat[x,y] = 0.0 else $
+			correl_mat[x,y] = total( im_ov_A * im_ov_B ) / $
+							sqrt( totAA * totBB )
+
+			if keyword_set( numpix ) then correl_mat[x,y,1] = Npix
+		     endif
+
+	          endfor
+		endif
+
+		if keyword_set( monitor ) then print, Ny-y, FORM="($,i3)"
+	  endfor
+
+	if keyword_set( monitor ) then print," "
+
+return, correl_mat
+end
diff --git a/Code/script_idl_mv/astrolib/correl_optimize.pro b/Code/script_idl_mv/astrolib/correl_optimize.pro
new file mode 100644
index 0000000000000000000000000000000000000000..71c93951cf5bbbd60908288aac6b470f667d2e09
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/correl_optimize.pro
@@ -0,0 +1,125 @@
+pro correl_optimize, image_A, image_B, xoffset_optimum, yoffset_optimum, $
+					XOFF_INIT = xoff_init,   $
+					YOFF_INIT = yoff_init,   $
+					PRINT=print, MONITOR=monitor, $
+					NUMPIX=numpix, MAGNIFICATION=Magf, $
+					PLATEAU_TRESH = plateau
+;+
+; NAME:
+;	CORREL_OPTIMIZE
+;
+; PURPOSE:
+;	Find the optimal (x,y) pixel offset of image_B relative to image_A
+; EXPLANATION"
+;	Optimal offset is computed by means of maximizing the correlation 
+;	function of the two images.
+;
+; CALLING SEQUENCE:
+;	CORREL_OPTIMIZE, image_A, image_B, xoffset_optimum, yoffset_optimum 
+;		[ XOFF_INIT=, YOFF_INIT=, MAGNIFICATION=, /PRINT, /NUMPIX, 
+;		  /MONITOR, PLATEAU_THRESH=  ]
+;
+; INPUTS:
+;	image_A, image_B = the two images of interest.
+;
+; OPTIONAL INPUT KEYWORDS:
+;	XOFF_INIT = initial X pixel offset of image_B relative to image_A,
+;	YOFF_INIT = Y pixel offset, (default offsets are 0 and 0).
+;	MAGNIFICATION = option to determine offsets up to fractional pixels,
+;			(example: MAG=2 means 1/2 pixel accuracy, default=1).
+;	/NUMPIX: sqrt( sqrt( # pixels )) used as correlation weighting factor.
+;	/MONITOR causes the progress of computation to be briefly printed.
+;	/PRINT causes the results of analysis to be printed.
+;	PLATEAU_THRESH = threshold used for detecting plateaus in 
+;		the cross-correlation matrix near maximum, (default=0.01),
+;		used only if MAGNIFICATION > 1.    Decrease this value for
+;		high signal-to-noise data
+;
+; OUTPUTS:
+;	xoffset_optimum = optimal X pixel offset of image_B relative to image_A.
+;	yoffset_optimum = optimal Y pixel offset.
+;
+; CALLS:
+;	function  correl_images( image_A, image_B )
+;	pro  corrmat_analyze
+;
+; PROCEDURE:
+;	The combination of function correl_images( image_A, image_B ) and
+;	corrmat_analyze of the result is used to obtain the (x,y) offset
+;	yielding maximal correlation. The combination is first executed at
+;	large REDUCTION factors to speed up computation, then zooming in 
+;	recursively on the optimal (x,y) offset by factors of 2.
+;	Finally, the MAGNIFICATION option (if specified)
+;	is executed to determine the (x,y) offset up to fractional pixels.
+;	
+; MODIFICATION HISTORY:
+;	Written, July,1991, Frank Varosi, STX @ NASA/GSFC
+;	Added PLATEAU_THRESH keyword  June 1997,  Wayne Landsman  STX   
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+        if N_params() LT 2 then begin
+		print,'Syntax - CORREL_OPTIMIZE, imA, imB, Xoffset, Yoffset'
+		print,'Keywords - /Monitor, /Print, XoffInit =, YoffInit =' + $
+		      ', Magnification =, /Numpix'
+		return
+        endif
+
+	simA = size( image_A )
+	simB = size( image_B )
+
+	if (simA[0] LT 2) OR (simB[0] LT 2) then begin
+		message,"first two arguments must be images",/INFO,/CONTIN
+		return
+	   endif
+
+	if N_elements( xoff_init ) NE 1 then xoff_init=0
+	if N_elements( yoff_init ) NE 1 then yoff_init=0
+	if N_elements( plateau ) NE 1 then plateau = 0.01
+	xoff = xoff_init
+	yoff = yoff_init
+
+	reducf = min( [simA[1:2],simB[1:2]] ) / 8	;Bin average to about
+							; 8 by 8 pixel image.
+	if N_elements( Magf ) NE 1 then Magf=1
+
+	xsiz = max( [simA[1],simB[1]] )
+	ysiz = max( [simA[2],simB[2]] )
+	xshift = xsiz
+	yshift = ysiz		;shift over the whole images first correlation.
+
+	while (reducf GT 1) do begin
+
+		corrmat = correl_images( image_A, image_B, XOFF=xoff,YOFF=yoff,$
+					       NUM=numpix, XS=xshift,YS=yshift,$
+					       REDUCTION=reducf, MONIT=monitor )
+
+		corrmat_analyze, corrmat, xoff, yoff, XOFF=xoff, YOFF=yoff, $
+						PRINT=print, REDUCTION=reducf
+		xshift = 2*reducf
+		yshift = 2*reducf	;shift over coarse pixels to refine
+		reducf = reducf/2	; in further correlations.
+	  endwhile
+
+	xshift = xshift/2	;now refine offsets to actual pixels.
+	yshift = yshift/2
+	corrmat = correl_images( image_A, image_B, XOFF=xoff, YOFF=yoff,$
+				 MON=monitor, NUM=numpix, XS=xshift, YS=yshift )
+
+	corrmat_analyze, corrmat, xoffset_optimum, yoffset_optimum, $
+					XOFF=xoff, YOFF=yoff, PRINT=print
+
+	if (Magf GE 2) then begin
+
+		xoff = xoffset_optimum		;refine offsets to
+		yoff = yoffset_optimum		; fractional pixels.
+
+		corrmat = correl_images( image_A, image_B, XOFF=xoff,YOFF=yoff,$
+						MAGNIFIC=Magf, MONITOR=monitor )
+
+		corrmat_analyze, corrmat, xoffset_optimum, yoffset_optimum, $
+							XOFF=xoff,YOFF=yoff,$
+							PRINT=print, MAG=Magf, $
+							PLATEAU_THRESH = plateau
+	   endif
+return
+end
diff --git a/Code/script_idl_mv/astrolib/corrmat_analyze.pro b/Code/script_idl_mv/astrolib/corrmat_analyze.pro
new file mode 100644
index 0000000000000000000000000000000000000000..26af51caa7fc9b7fab8420c9dffea8001ecb1a69
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/corrmat_analyze.pro
@@ -0,0 +1,174 @@
+pro corrmat_analyze, correl_mat, xoffset_optimum, yoffset_optimum, $
+				max_corr, edge, plateau,           $
+				XOFF_INIT = xoff_init,             $
+				YOFF_INIT = yoff_init,             $
+				REDUCTION = reducf, MAGNIFICATION = Magf,  $
+				PRINT = print, PLATEAU_THRESH = plateau_thresh
+;+
+; NAME:
+;	CORRMAT_ANALYZE 
+; PURPOSE:
+;	Find the optimal (x,y) offset to maximize correlation of 2 images
+; EXPLANATION:
+;	Analyzes the 2-D cross-correlation function of two images
+;	and finds the optimal(x,y) pixel offsets.
+;	Intended for use with function CORREL_IMAGES.
+;
+; CALLING SEQUENCE:
+;	corrmat_analyze, correl_mat, xoffset_optimum, yoffset_optimum, 
+;		max_corr, edge, plateau, [XOFF_INIT=, YOFF_INIT=, REDUCTION=, 
+;		MAGNIFICATION=, PLATEAU_THRESH=, /PRINT]
+;
+; INPUTS:
+;	correl_mat = the cross-correlation matrix of 2 images.
+;			(as computed by function CORREL_IMAGES( imA, imB ) ).
+;
+; NOTE:
+;	If correl_mat(*,*,1) is the number of pixels for each correlation,
+;	(the case when /NUMPIX was specified in call to CORREL_IMAGES)
+;	then sqrt( sqrt( # pixels )) is used as correlation weighting factor.
+;
+; OPTIONAL INPUT KEYWORDS:
+;	XOFF_INIT = initial X pixel offset of image_B relative to image_A.
+;	YOFF_INIT = Y pixel offset, (both as specified to correl_images).
+;	REDUCTION = reduction factor used in call to CORREL_IMAGES.
+;	MAGNIFICATION = magnification factor used in call to CORREL_IMAGES,
+;		this allows determination of offsets up to fractions of a pixel.
+;	PLATEAU_THRESH = threshold used for detecting plateaus in 
+;		the cross-correlation matrix near maximum, (default=0.01),
+;		used only if MAGNIFICATION > 1
+;	/PRINT causes the result of analysis to be printed.
+;
+; OUTPUTS:
+;	xoffset_optimum = optimal X pixel offset of image_B relative to image_A.
+;	yoffset_optimum = optimal Y pixel offset.
+;	max_corr = the maximal correlation corresponding to optimal offset.
+;	edge = 1 if maximum is at edge of correlation domain, otherwise=0.
+;	plateau = 1 if maximum is in a plateau of correlation function, else=0.
+;
+; PROCEDURE:
+;	Find point of maximum cross-correlation and calc. corresponding offsets.
+;	If MAGNIFICATION > 1:
+;	the  correl_mat is checked for plateau near maximum, and if found,
+;	the center of plateau is taken as point of maximum cross-correlation.
+;
+; MODIFICATION HISTORY:
+;	Written, July-1991, Frank Varosi, STX @ NASA/GSFC
+;	Use ROUND instead of NINT, June 1995 Wayne Landsman HSTX
+;	Remove use of non-standard !DEBUG system variable   W.L. HSTX 
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+	scm = size( correl_mat )
+
+	if (scm[0] LT 2) then begin
+		message,"first argument must be at least 2-D matrix",/INFO,/CON
+		return
+	   endif
+
+	Nx = scm[1]
+	Ny = scm[2]
+	x_shift = Nx/2
+	y_shift = Ny/2
+	if N_elements( xoff_init ) NE 1 then xoff_init=0
+	if N_elements( yoff_init ) NE 1 then yoff_init=0
+
+	if (scm[0] GE 3) then begin		;weight by # of overlap pixels:
+
+		Npix_mat = correl_mat[*,*,1]
+		Maxpix = max( Npix_mat )
+		corr_mat = correl_mat[*,*,0] * sqrt( sqrt( Npix_mat/Maxpix ) )
+
+	  endif else  corr_mat = correl_mat
+
+	max_corr = max( corr_mat, maxLoc )
+	xi = (maxLoc MOD Nx)
+	yi = (maxLoc/Nx)
+
+	if N_elements( Magf ) NE 1 then Magf=1
+	if N_elements( reducf ) NE 1 then reducf=1
+	if N_elements( plateau_thresh ) NE 1 then plateau_thresh=0.01
+	plateau=0
+	edge=0
+
+	if ( reducf GT 1 ) then begin
+
+		xoffset_optimum = ( xi - x_shift + xoff_init/reducf ) * reducf
+		yoffset_optimum = ( yi - y_shift + yoff_init/reducf ) * reducf
+		xoffset_optimum = round( xoffset_optimum )
+		yoffset_optimum = round( yoffset_optimum )
+		format = "(2i5)"
+
+	 endif else if ( Magf GT 1 ) then begin
+
+		w = where( (max_corr - corr_mat) LE plateau_thresh, Npl )
+
+		if (Npl GT 1) then begin
+
+			wx = [ w MOD Nx ]
+			wy = [ w/Nx ]
+			wxmin = min( wx )
+			wymin = min( wy )
+			wxmax = max( wx )
+			wymax = max( wy )
+			npix = (wxmax - wxmin)+(wymax - wymin)
+
+			if (Npl GE npix)  AND $
+			   (xi GE wxmin) AND (xi LE wxmax) AND $
+			   (yi GE wymin) AND (yi LE wymax) then begin
+				plateau=1
+				xi = wxmin + (wxmax - wxmin)/2.
+				yi = wymin + (wymax - wymin)/2.
+				max_corr = corr_mat[xi,yi]
+			   endif
+		   endif
+
+		xoffset_optimum = xoff_init + float( xi - x_shift )/Magf
+		yoffset_optimum = yoff_init + float( yi - y_shift )/Magf
+		format = "(2f9.3)"
+
+	  endif else begin
+		xoffset_optimum = xi - x_shift + round( xoff_init )
+		yoffset_optimum = yi - y_shift + round( yoff_init )
+		format = "(2i5)"
+	   endelse
+
+	if (xi EQ 0) OR (xi EQ Nx-1) OR $
+	   (yi EQ 0) OR (yi EQ Ny-1) then edge=1
+
+	if keyword_set( print ) then begin
+
+		mincm = min( corr_mat, minLoc )
+
+		if (scm[0] GE 3) then begin
+			xm = (minLoc MOD Nx)
+			ym = (minLoc/Nx)
+			Npixmin = Long( Npix_mat[xm,ym] ) * reducf * reducf
+			Npixmax = Long( Npix_mat[xi,yi] ) * reducf * reducf
+			info_min = "  ( " + strtrim( Npixmin, 2 ) + " pixels )"
+			info_max = "  ( " + strtrim( Npixmax, 2 ) + " pixels )"
+		  endif else begin
+			info_min = ""
+			info_max = ""
+		   endelse
+
+		print," min Correlation = ", strtrim( mincm, 2 ), info_min
+		print," MAX Correlation = ", strtrim( max_corr, 2 ), info_max,$
+			"  at (x,y) offset:", $
+		    string( [ xoffset_optimum, yoffset_optimum ], FORM=format )
+
+		if (plateau) then begin
+			print," plateau of MAX Correlation:"
+			print," (Correl - MAX + " + $
+			     string( plateau_thresh, FORM="(F5.3)" ) + ") > 0"
+			print,(corr_mat - max(corr_mat) + plateau_thresh) > 0
+		   endif
+
+		if (edge) then begin
+			print," Maximum is at EDGE of shift range, " + $
+				"result is inconclusive"
+			print," try larger shift or new starting offset"
+		   endif
+	   endif
+
+return
+end
diff --git a/Code/script_idl_mv/astrolib/cosmo_param.pro b/Code/script_idl_mv/astrolib/cosmo_param.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4472c9de9dcaf235c632056c3c74658caac90a47
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/cosmo_param.pro
@@ -0,0 +1,106 @@
+pro cosmo_param,Omega_m, Omega_Lambda, Omega_k, q0
+;+
+; NAME:
+;     COSMO_PARAM
+; PURPOSE:
+;     Derive full set of cosmological density parameters from a partial set
+; EXPLANATION:
+;     This procedure is called by LUMDIST and GALAGE to allow the user a choice
+;     in defining any two of four cosmological density parameters.
+;
+;     Given any two of the four input parameters -- (1) the normalized matter 
+;     density Omega_m (2) the normalized cosmological constant, Omega_lambda 
+;     (3) the normalized curvature term, Omega_k and (4) the deceleration 
+;     parameter q0 --  this  program will derive the remaining two.     Here 
+;     "normalized" means divided by the closure density so that 
+;     Omega_m + Omega_lambda + Omega_k = 1.    For a more
+;     precise definition see Carroll, Press, & Turner (1992, ArAA, 30, 499).     
+;
+;     If less than two parameters are defined, this procedure sets default 
+;     values of Omega_k=0 (flat space), Omega_lambda = 0.7, Omega_m = 0.3
+;     and q0 = -0.55
+; CALLING SEQUENCE:
+;       COSMO_PARAM, Omega_m, Omega_lambda, Omega_k, q0
+;
+; INPUT-OUTPUTS:
+;     Omega_M - normalized matter energy density, non-negative numeric scalar
+;     Omega_Lambda - Normalized cosmological constant, numeric scalar
+;     Omega_k - normalized curvature parameter, numeric scalar.   This is zero
+;               for a flat universe
+;     q0 - Deceleration parameter, numeric scalar = -R*(R'')/(R')^2
+;          = 0.5*Omega_m - Omega_lambda
+; NOTES:
+;     If more than two parameters are defined upon input (overspecification), 
+;     then the first two defined parameters in the ordered list Omega_m, 
+;     Omega_lambda, Omega_k, q0 are used to define the cosmology.
+; EXAMPLE:
+;     Suppose one has Omega_m = 0.3, and Omega_k = 0.5 then to determine
+;     Omega_lambda and q0
+;    
+;       IDL> cosmo_param, 0.3, omega_lambda, 0.5, q0
+;   
+;       which will return omega_lambda = 0.2 and q0 = -2.45
+; REVISION HISTORY:
+;       W. Landsman         Raytheon ITSS         April 2000
+;       Better Error checking  W. Landsman/D. Syphers   October 2010
+;-
+
+ On_error,2
+ compile_opt idl2
+ 
+ if N_params() LT 3 then begin
+      print,'Syntax - COSMO_PARAM, Omega_m, Omega_lambda, Omega_k, q0'
+      return
+ endif
+ 
+ Nk = n_elements(Omega_k) < 1
+ NLambda = N_elements(Omega_lambda) < 1
+ Nomega = N_elements(Omega_m) < 1
+ Nq0 = N_elements(q0) < 1
+
+; Use must specify 0 or 2 parameters
+
+ if total(Nk + Nlambda + Nomega + Nq0,/int) EQ 1 then $
+     message,'ERROR - At least 2 cosmological parameters must be specified'
+     
+; Check which two parameters are defined, and then determine the other two
+
+ if (Nomega and Nlambda) then begin 
+       if Nk EQ 0 then Omega_k = 1 - omega_m - Omega_lambda 
+       if Nq0 EQ 0 then q0 = omega_m/2. - Omega_lambda
+ endif else $
+
+ if (Nomega and Nk) then begin 
+        if Nlambda EQ 0 then Omega_lambda = 1. -omega_m - Omega_k
+        if Nq0 EQ 0 then q0 = -1 + Omega_k + 3*Omega_m/2
+ endif else $
+
+ if (Nlambda and Nk) then begin 
+         if Nomega EQ 0 then omega_m = 1.-Omega_lambda - Omega_k
+         if Nq0 EQ 0 then q0 = (1 - Omega_k - 3.*Omega_lambda)/2.
+ endif else $
+
+ if (Nomega and Nq0) then begin
+         if Nk EQ 0 then Omega_k = 1 + q0 - 3*omega_m/2. 
+         if Nlambda EQ 0 then Omega_lambda  = 1. - omega_m - Omega_k
+ endif else $
+
+ if (Nlambda and Nq0) then begin
+         if Nk EQ 0 then Omega_k = 1 - 2*q0 - 3*Omega_lambda
+         if Nomega EQ 0 then omega_m = 1.-Omega_lambda - Omega_k
+ endif else $
+
+ if (Nk and Nq0) then begin
+         if Nomega EQ 0 then omega_m = (1 + q0 - Omega_k)*2/3.
+         if Nlambda EQ 0 then Omega_lambda = 1. - omega_m - Omega_k
+ endif
+
+;Set default values
+
+ if N_elements(Omega_k) EQ 0 then Omega_k = 0       ;Default is flat space
+ if N_elements(Omega_lambda) EQ 0 then Omega_lambda = 0.7
+ if N_elements(omega_m) EQ 0 then omega_m = 1 - Omega_lambda
+ if N_elements(q0) EQ 0 then q0 = (1 - Omega_k - 3*Omega_lambda)/2.
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/cr_reject.pro b/Code/script_idl_mv/astrolib/cr_reject.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ec0e3159dd97133812383deba8760ff21d1e1f4c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/cr_reject.pro
@@ -0,0 +1,886 @@
+PRO cr_reject, input_cube, rd_noise_dn, dark_dn, gain, mult_noise, $
+               combined_image, combined_noise, combined_npix, $
+               MASK_CUBE=mask_cube, NOISE_CUBE=noise_cube, $
+               NSIG=nsig, MEDIAN_LOOP=median_loop, MEAN_LOOP=mean_loop, $
+               MINIMUM_LOOP=minimum_loop, INIT_MED=init_med, $
+               INIT_MIN=init_min, INIT_MEAN=init_mean, EXPTIME=exptime,$
+               BIAS=bias, VERBOSE=verbose, $
+               TRACKING_SET=tracking_set, DILATION=dilation, DFACTOR=dfactor, $
+               NOSKYADJUST=noskyadjust,NOCLEARMASK=noclearmask, $
+               XMEDSKY=xmedsky, RESTORE_SKY=restore_sky, $
+               SKYVALS=skyvals, NULL_VALUE=null_value, $
+               INPUT_MASK=input_mask, WEIGHTING=weighting, SKYBOX=skybox
+;+
+; NAME:
+;     CR_REJECT
+;
+; PURPOSE:
+;     General, iterative cosmic ray rejection using two or more input images.
+;
+; EXPLANATION:
+;     Uses a noise model input by the user, rather than
+;     determining noise empirically from the images themselves.
+;
+;     The image returned has the combined exposure time of all the input
+;     images, unless the bias flag is set, in which case the mean is
+;     returned.  This image is computed by summation (or taking mean)
+;     regardless of loop and initialization options (see below).
+;
+; CALLING SEQUENCE:
+;     cr_reject, input_cube, rd_noise_dn, dark_dn, gain, mult_noise, $
+;        combined_image, combined_npix, combined_noise
+;
+; MODIFIED ARGUMENT:
+;     input_cube - Cube in which each plane is an input image.
+;                  If the noise model is used (rd_noise_dn, dark_dn,
+;                  gain), then input_cube must be in units of DN.
+;                  If an input noise cube is supplied (rd_noise_dn
+;                  <0), then the units of input_cube and noise_cube
+;                  merely need to be consistent.  
+;
+;                  This array is used as a buffer and its contents 
+;                  are not guaranteed on output (although for now, 
+;                  weighting=0 with /restore_sky should give you back 
+;                  your input unaltered).
+;
+; INPUT ARGUMENTS:
+;     rd_noise_dn - Read noise per pixel.  Units are DN.
+;                   If negative, then the user supplies an error cube
+;                   via the keyword noise_cube.  In the latter case,
+;                   mult_noise still applies, since it is basically a fudge.
+;     dark_dn     - Dark rate in DN per pixel per s.  This can be a scalar,
+;                   or it can be a dark image divided by the exposure
+;                   time.
+;     gain        - Electrons per DN.
+;     mult_noise  - Coefficient for multiplicative noise term -- helps
+;                   account for differing PSFs or subpixel image shifts.
+;
+; INPUT KEYWORDS:
+;     exptime    - If the images have different exposure times, pass
+;                  them in a vector.  You can leave this off for 
+;                  frames with the same exposure time, but dark counts
+;                  won't be treated correctly.
+;     verbose    - If set, lots of output.
+;     nsig       - Rejection limit in units of pixel-to-pixel noise
+;                  (sigma) on each input image.  Can be specified as
+;                  an array, in which case the dimension gives the
+;                  maximum number of iterations to run.  (Default = 
+;                  [8, 6, 4]
+;     dilation   - With dfactor, provides functionality similar to the
+;                  expansion of the CR with pfactor and radius in STSDAS 
+;                  crrej.  Dilate gives the size of the border to be
+;                  tested around each initially detected CR pixel.
+;                  E.g., dilate=1 searches a 9 X 9 area centered on the
+;                  original pixel.  If dfactor is set, the default is 1.
+;     dfactor    - See dilation.  This parameter is equivalent to pfactor
+;                  in STSDAS crrej.  The current threshold for rejection
+;                  is multiplied by this factor when doing the search
+;                  with the dilated mask.  If dilation is set, the default
+;                  for this parameter is 0.5.
+;     bias       - Set if combining biases (divides through by number
+;                  of images at end, since exposure time is 0).
+;     tracking_set - Subscripts of pixels to be followed through the 
+;                    computation.
+;     noskyadjust  - Sky not to be subtracted before rejection tests.  Default
+;                  is to do the subtraction.
+;     xmedsky    - Flag.  If set, the sky is computed as a 1-d array
+;                  which is a column-by-column median.  This is intended
+;                  for STIS slitless spectra.  If sky adjustment is
+;                  disabled, this keyword has no effect.
+;     input_mask - Mask cube input by the user.  Should be byte data
+;                  because it's boolean.  1 means use the pixel,
+;                  and 0 means reject the pixel - these rejections
+;                  are in addition to those done by the CR rejection
+;                  algorithm as such.
+;
+;     The following keywords control how the current guess at a CR-free
+;     "check image" is recomputed on each iteration:
+;
+;     median_loop  - If set, the check image for each iteration is
+;                    the pixel-by-pixel median. THE MEAN IS
+;                    RETURNED in combined_image even if you set
+;                    this option.  (Default is mean_loop.)
+;     minimum_loop - If set, the check image for each iteration is
+;                    the pixel-by-pixel minimum. THE MEAN IS
+;                    RETURNED in combined_image even if you set
+;                    this option.  (Default is mean_loop.)
+;     mean_loop    - If set, the check image for each iteration is
+;                    the pixel-by-pixel mean.  (Same as the default.)
+;     noclearmask  - By default, the mask of CR flags is reset before
+;                    every iteration, and a pixel that has been
+;                    rejected has a chance to get back in the game
+;                    if the average migrates toward its value.  If this
+;                    keyword is set, then any rejected pixel stays 
+;                    rejected in subsequent iterations.  Note that what 
+;                    stsdas.hst_calib.wfpc.crrej does is the same
+;                    as the default.  For this procedure, the default
+;                    was NOT to clear the flags, until 20 Oct. 1997.
+;     restore_sky  - Flag.  If set, the routine adds the sky back into
+;                    input_cube before returning.  Works only if
+;                    weighting=0.
+;     null_value   - Value to be used for output pixels to which no
+;                    input pixels contribute.  Default=0
+;     weighting    - Selects weighting scheme in final image
+;                    combination:
+;                     0 (default) - Poissonian weighting - co-add
+;                         detected DN from non-CR pixels.  (Pixel-by-
+;                         pixel scaling up to total exposure time,
+;                         for pixels in stack where some rejected.)
+;                         Equivalent to exptime weighting of rates.
+;                     1 or more - Sky and read noise weighting of rates.
+;                         Computed as weighted average of DN rates,
+;                         with total exp time multiplied back in
+;                         afterward.
+;
+;                    In all cases, the image is returned as a sum in
+;                    DN with the total exposure time of the image 
+;                    stack, and with total sky added back in.
+;
+;     The following keywords allow the initial guess at a CR-free "check
+;     image" to be of a different kind from the iterative guesses:
+;
+;     init_med  - If set, the initial check image is
+;                 the pixel-by-pixel median.  (Not permitted if
+;                 input_cube has fewer than 3 planes; default is minimum.)
+;     init_mean - If set, the initial check image is
+;                 the pixel-by-pixel mean.  (Default is minimum.)    
+;     init_min  - If set, the initial check image is
+;                 the pixel-by-pixel minimum.  (Same as the default.)    
+;  
+; OUTPUT ARGUMENTS::
+;     combined_image - Mean image with CRs removed.
+;     combined_npix  - Byte (or integer) image of same dimensions as
+;                      combined_image, with each element containing
+;                      the number of non-CR stacked pixels that
+;                      went into the  result.
+;     combined_noise - Noise in combined image according to noise model
+;                      or supplied noise cube.
+;
+; OUTPUT KEYWORDS:
+;     mask_cube      - CR masks for each input image.  1 means
+;                      good pixel; 0 means CR pixel.
+;     skyvals        - Sky value array.  For an image cube with N planes,
+;                      this array is fltarr(N) if the sky is a scalar per
+;                      image plane, and fltarr(XDIM, N), is the XMEDSKY
+;                      is set.
+;
+; INPUT/OUTPUT KEYWORD:
+;     noise_cube     - Estimated noise in each pixel of input_cube as
+;                      returned (if rd_noise_dn ge 0), or input noise
+;                      per pixel of image_cube (if rd_noise_dn lt 0).
+;     skybox         - X0, X1, Y0, Y1 bounds of image section used
+;                      to compute sky.  If supplied by user, this 
+;                      region is used.  If not supplied, the
+;                      image bounds are returned.  This parameter might
+;                      be used (for instance) if the imaging area
+;                      doesn't include the whole chip.
+;
+; COMMON BLOCKS:  none
+;
+; SIDE EFFECTS:  none
+;
+; METHOD: 
+;     
+;     COMPARISON WITH STSDAS
+;
+;     Cr_reject emulates the crrej routine in stsdas.hst_calib.wfpc.
+;     The two routines have been verified to give identical results
+;     (except for some pixels along the edge of the image) under the 
+;     following conditions:
+;
+;          no sky adjustment
+;          no dilation of CRs
+;          initialization of trial image with minimum
+;          taking mean for each trial image after first (no choice
+;             in crrej)
+;     
+;     Dilation introduces a difference between crrej and this routine
+;     around the very edge of the image, because the IDL mask
+;     manipulation routines don't handle the edge the same way as crrej
+;     does.  Away from the edge, crrej and cr_reject are the same with
+;     respect to dilation.
+;
+;     The main difference between crrej and cr_reject is in the sky
+;     computation.  Cr_reject does a DAOPHOT I sky computation on a 
+;     subset of pixels grabbed from the image, whereas crrej searches
+;     for a histogram mode.
+;
+;     REMARKS ON USAGE
+;
+;     The default is that the initial guess at a CR-free image is the
+;     pixel-by-pixel minimum of all the input images.  The pixels
+;     cut from each component image are the ones more than nsig(0) sigma
+;     from this minimum image.  The next iteration is based on the
+;     mean of the cleaned-up component images, and the cut is taken
+;     at nsig(1) sigma.  The next iteration is also based on the mean with
+;     the cut taken at nsig(2) sigma.
+;
+;     The user can specify an arbitrary sequence of sigma cuts, e.g.,
+;     nsig=[6,2] or nsig=[10,9,8,7].  The user can also specify that
+;     the initial guess is the median (/init_med) rather than the
+;     minimum, or even the mean.  The iterated cleaned_up images after
+;     the first guess can be computed as the mean or the median
+;     (/mean_loop or /median_loop).  The minimum_loop option is also
+;     specified, but this is a trivial case, and you wouldn't want
+;     to use it except perhaps for testing.
+;
+;     The routine takes into account exposure time if you want it to, 
+;     i.e., if the pieces of the CR-split aren't exactly the same.
+;     For bias frames (exposure time 0), set /bias to return the mean
+;     rather than the total of the cleaned-up component images.
+;
+;     The crrej pfactor and radius to propagate the detected CRs
+;     outward from their initial locations have been implemented
+;     in slightly different form using the IDL DILATE function.
+;
+;     It is possible to end up with output pixels to which no valid
+;     input pixels contribute.  These end up with the value
+;     NULL_VALUE, and the corresponding pixels of combined_npix are
+;     also returned as 0.  This result can occur when the pixel is
+;     very noisy across the whole image stack, i.e., if all the
+;     values are, at any step of the process, far from the stack
+;     average at that position even after rejecting the real
+;     outliers.  Because  pixels are flagged symmetrically N sigma
+;     above and below the  current combined image (see code), all
+;     the pixels at a given  position can end up getting flagged.
+;     (At least, that's how I think it happens.)
+;
+; MODIFICATION HISTORY:
+;      5 Mar. 1997 - Written.  R. S. Hill
+;     14 Mar. 1997 - Changed to masking approach to keep better
+;                    statistics and return CR-affected pixels to user.
+;                    Option to track subset of pixels added.
+;                    Dilation of initially detected CRs added.
+;                    Other small changes.  RSH
+;     17 Mar. 1997 - Arglist and treatment of exposure times fiddled
+;                    to mesh better with stis_cr.  RSH
+;     25 Mar. 1997 - Fixed bug if dilation finds nothing.  RSH
+;      4 Apr. 1997 - Changed name to cr_reject.  RSH
+;     15 Apr. 1997 - Restyled with emacs, nothing else done.  RSH
+;     18 Jun. 1997 - Input noise cube allowed.  RSH
+;     19 Jun. 1997 - Multiplicative noise deleted from final error.  RSH
+;     20 Jun. 1997 - Fixed error in using input noise cube.  RSH
+;     12 July 1997 - Sky adjustment option.  RSH
+;     27 Aug. 1997 - Dilation kernel made round, not square, and
+;                    floating-point radius allowed.  RSH
+;     16 Sep. 1997 - Clearmask added.  Intermediate as well as final
+;                    mean is exptime weighted.  RSH
+;     17 Sep. 1997 - Redundant zeroes around dilation kernel trimmed.  RSH
+;      1 Oct. 1997 - Bugfix in preceding.  RSH
+;     21 Oct. 1997 - Clearmask changed to noclearmask.  Bug in robust
+;                    array division fixed (misplaced parens).  Sky as
+;                    a function of X (option).  RSH
+;     30 Jan. 1998 - Restore_sky keyword added.  RSH
+;      5 Feb. 1998 - Quick help corrected and updated.  RSH
+;      6 Feb. 1998 - Fixed bug in execution sequence for tracking_set 
+;                    option.  RSH
+;     18 Mar. 1998 - Eliminated confusing maxiter spec.  Added
+;                    null_value keyword.  RSH
+;     15 May  1998 - Input_mask keyword.  RSH
+;     27 May  1998 - Initialization of minimum image corrected. NRC, RSH
+;      9 June 1998 - Input mask cube processing corrected.  RSH
+;     21 Sep. 1998 - Weighting keyword added.  RSH
+;      7 Oct. 1998 - Fixed bug in input_mask processing (introduced
+;                    in preceding update).  Input_mask passed to
+;                    skyadj_cube.  RSH
+;      5 Mar. 1999 - Force init_min for 2 planes.  RSH
+;      1 Oct. 1999 - Make sure weighting=1 not given with noise cube.  RSH
+;      1 Dec. 1999 - Corrections to doc; restore_sky needs weighting=0.  RSH
+;     17 Mar. 2000 - SKYBOX added.  RSH
+;-
+on_error,0
+IF n_params(0) LT 6 THEN BEGIN
+    print,'CALLING SEQUENCE:  cr_reject, input_cube, rd_noise_dn, $'
+    print,'   dark_dn, gain, mult_noise, combined_image, combined_noise, $'
+    print,'   combined_npix'
+    print,'KEYWORD PARAMETERS:  nsig, exptime, bias, verbose,'
+    print,'   tracking_set, median_loop, mean_loop, minimum_loop, '
+    print,'   init_med, init_mean, init_min,'
+    print,'   mask_cube, noise_cube, dilation, dfactor, noclearmask, '
+    print,'   noskyadjust, xmedsky, restore_sky, skyvals, null_value'
+    print,'   input_mask, weighting, skybox'
+    return
+ENDIF
+
+verbose = keyword_set(verbose)
+xmed = keyword_set(xmedsky)
+
+track = n_elements(tracking_set) GT 0
+
+sz = size(input_cube)
+IF sz[0] NE 3 THEN BEGIN
+    print,'CR_REJECT:  Input cube must have 3 dimensions.'
+    return
+ENDIF
+
+IF n_elements(input_mask) GT 0 THEN BEGIN
+    szinpm = size(input_mask)
+    wsz = where(szinpm[0:3] NE sz[0:3], cwsz)
+    IF cwsz GT 0 THEN BEGIN
+        print,'CR_REJECT:  INPUT_MASK must be same size as IMAGE_CUBE.'
+        return
+    ENDIF ELSE BEGIN
+        IF verbose THEN print,'CR_REJECT:  Using INPUT_MASK.'
+    ENDELSE
+    use_input_mask = 1b
+ENDIF ELSE BEGIN
+    use_input_mask = 0b
+ENDELSE    
+
+xdim = sz[1]
+ydim = sz[2]
+nimg = sz[3]
+npix = xdim*ydim
+
+usemedian = keyword_set(median_loop)
+usemean   = keyword_set(mean_loop)
+usemin    = keyword_set(minimum_loop)
+IF (usemean + usemedian + usemin) GT 1  THEN BEGIN
+    print,'CR_REJECT:  Specify only one of MEDIAN_LOOP, MEAN_LOOP' $
+      + ', or MINIMUM_LOOP'
+    return
+ENDIF
+IF (usemean + usemedian + usemin) EQ 0  THEN BEGIN
+    usemean = 1
+ENDIF
+
+inimed  = keyword_set(init_med)
+inimean = keyword_set(init_mean)
+inimin  = keyword_set(init_min)
+IF (inimean + inimed + inimin) GT 1  THEN BEGIN
+    print,'CR_REJECT:  Specify only one of INIT_MED, INIT_MEAN,' $
+      + ' or INIT_MIN.'
+    return
+ENDIF
+IF (inimean + inimed + inimin) EQ 0  THEN BEGIN
+    inimin = 1
+ENDIF
+IF nimg LT 3 AND inimed THEN BEGIN
+    inimed = 0
+    inimin = 1
+    IF verbose THEN BEGIN
+        print,'CR_REJECT:  INIT_MED only permitted for 3 or more ' $
+            + 'images.'
+        print,'            Forcing INIT_MIN.'
+    ENDIF
+ENDIF
+
+;
+;  Accumulation mode for bad pixels.
+;
+IF keyword_set(noclearmask) THEN BEGIN
+    clearmask = 0b
+    IF verbose THEN print,'CR_REJECT:  CR flags accumulate strictly.'
+ENDIF ELSE BEGIN
+    clearmask = 1b
+    IF verbose THEN print,'CR_REJECT:  CR flags cleared between iterations.'
+ENDELSE 
+;
+;  Default iterations.
+;
+IF (n_elements(nsig) LT 1) THEN BEGIN
+    nsig = [8, 6, 4]
+ENDIF
+sig_limit = nsig
+maxiter = n_elements(nsig)
+IF n_elements(null_value) LT 1 THEN null_value=0
+IF verbose THEN BEGIN
+    print,'CR_REJECT: Iteration spec:  '
+    print,'           nsig = ',nsig
+    print,'           maxiter = ',maxiter
+    print,'           null_value = ',null_value
+ENDIF
+;
+IF n_elements(exptime) NE 0 THEN BEGIN
+    IF n_elements(exptime) NE nimg THEN BEGIN
+        print,'CR_REJECT:  EXPTIME must have one element per input image.'
+        return
+    ENDIF
+    zexp = 0b
+    FOR i=0,nimg-1 DO zexp = zexp OR (exptime[i] LE 0.0)
+    IF zexp THEN BEGIN
+        save_expt = exptime
+        exptime = make_array(nimg, value=1.0)
+        IF verbose THEN print, $
+          'CR_REJECT:  All exposure times <= 0.'
+    ENDIF
+ENDIF ELSE BEGIN
+    zexp = 1b
+    save_expt = make_array(nimg, value=0.0)
+    exptime = make_array(nimg, value=1.0)
+ENDELSE
+etot = total(exptime)
+
+IF n_elements(weighting) GT 0 THEN BEGIN
+    wgt = weighting
+    wgt = round(wgt)
+    IF wgt ne 0 and wgt ne 1 THEN BEGIN
+        print, 'CR_REJECT:  Weighting must be 0 or 1'
+        print,'             Executing return'
+        return
+    ENDIF
+ENDIF ELSE BEGIN
+    wgt = 0
+ENDELSE
+
+IF verbose THEN BEGIN
+    print,'CR_REJECT:  gain = ',gain
+    IF n_elements(dark_dn) EQ 1 THEN BEGIN
+        print,'           dark rate = ',dark_dn
+    ENDIF ELSE BEGIN
+        print,'           dark image supplied '
+    ENDELSE
+    print,'           read noise = ',rd_noise_dn
+    print,'           multiplicative noise coefficient = ',mult_noise
+    print,'           number of images = ',nimg
+    print,'           exposure times: '
+    print,exptime
+    print,'           total exposure time = ',etot
+    CASE wgt OF
+        0:  print,'           flux to be co-added'
+        1:  print,'           weighting of rate by sky and read noise'
+    ENDCASE
+ENDIF
+
+;
+;  Process dilation specs
+;
+IF keyword_set(dilation) OR keyword_set(dfactor) THEN BEGIN
+    do_dilation = 1b
+    IF n_elements(dilation) LT 1 THEN dilation = 1
+    IF n_elements(dfactor) LT 1 THEN dfactor = 0.5
+    IF (dilation LE 0) OR (dfactor LE 0) THEN BEGIN
+        print,'CR_REJECT:  Dilation specs not valid: '
+        print,'           dilation = ',dilation
+        print,'           dfactor  = ',dfactor
+        return
+    ENDIF
+    kdim = 1 + 2*floor(dilation+1.e-4)
+    kernel = make_array(kdim, kdim, value=1b)
+    half_kern = fix(kdim/2)
+    wkz = where(shift(dist(kdim),half_kern,half_kern) $
+        GT (dilation+0.0001), ckz)
+    IF ckz GT 0 THEN kernel[wkz] = 0b
+    IF verbose THEN BEGIN
+        print,'CR_REJECT:  Dilation will be done.  Specs:'
+        print,'           dilation = ',dilation
+        print,'           dfactor  = ',dfactor
+        print,'           kernel = '
+        print,kernel
+    ENDIF      
+ENDIF ELSE BEGIN
+    do_dilation = 0b
+    IF verbose THEN print,'CR_REJECT:  Mask dilation will not be done.' 
+ENDELSE
+
+
+IF verbose THEN print,'CR_REJECT:  Initializing noise and mask cube.'
+IF rd_noise_dn GE 0 THEN BEGIN
+    IF verbose THEN print,'CR_REJECT:  Noise cube computed.'
+    supplied = 0b
+    noise_cube = 0.0*input_cube
+    FOR i=0, nimg-1 DO BEGIN
+        noise_cube[0,0,i] = sqrt((rd_noise_dn^2 $
+                                  + ((input_cube[*,*,i] $
+                                      + dark_dn*exptime[i])>0)/gain) > 0.0)
+    ENDFOR
+ENDIF ELSE BEGIN
+    IF verbose THEN print,'CR_REJECT:  Noise cube supplied.'
+    supplied = 1b
+    IF wgt EQ 1 THEN BEGIN
+        print, 'CR_REJECT:  WEIGHTING=1 incompatible with supplying ', $
+            'noise cube.'
+        print, '            Executing return.'
+        return
+    ENDIF
+ENDELSE
+;
+;  Mask flags CR with zeroes
+;
+mask_cube = make_array(xdim, ydim, nimg, value=1B)
+IF nimg LE 255 THEN ivalue=byte(nimg) ELSE ivalue=fix(nimg)
+combined_npix = make_array(xdim, ydim, value=ivalue)
+
+IF keyword_set(noskyadjust) THEN BEGIN
+    skyvals = fltarr(nimg)
+    totsky = 0
+ENDIF ELSE BEGIN
+    IF verbose THEN print,'CR_REJECT:  Sky adjustment being made.'
+    skyadj_cube, input_cube, skyvals, totsky, $
+      verbose=verbose, xmedsky=xmed, input_mask=input_mask, $
+      region=skybox
+ENDELSE
+
+IF verbose THEN print,'CR_REJECT:  Scaling by exposure time.'
+
+FOR i=0,nimg-1 DO BEGIN
+    input_cube[0,0,i] = input_cube[*,*,i]/exptime[i]
+    noise_cube[0,0,i] = noise_cube[*,*,i]/exptime[i]
+ENDFOR
+
+;
+;  Initialization of main loop.
+;
+ncut_tot = lonarr(nimg)
+cr_subs  = lonarr(npix)
+IF inimin OR usemin THEN flagval = max(input_cube)+1
+IF inimed THEN BEGIN
+    IF verbose THEN print,'CR_REJECT:  Initializing with median.'
+    IF use_input_mask THEN BEGIN
+        medarr,input_cube,combined_image,input_mask
+    ENDIF ELSE BEGIN
+        medarr,input_cube,combined_image
+    ENDELSE
+ENDIF ELSE IF inimean THEN BEGIN
+    IF verbose THEN print,'CR_REJECT:  Initializing with mean.'
+    IF use_input_mask THEN BEGIN
+        tm = total(input_mask,3) > 1e-6
+        combined_image = total(input_cube*input_mask,3)/tm
+        wz = where(temporary(tm) le 0.001, cwz)
+        IF cwz GT 0 THEN $
+            combined_image[temporary(wz)] = 0
+    ENDIF ELSE BEGIN
+        combined_image = total(input_cube,3)/nimg
+    ENDELSE
+ENDIF ELSE IF inimin THEN BEGIN
+    IF verbose THEN print,'CR_REJECT:  Initializing with minimum.'
+    IF use_input_mask THEN BEGIN
+        combined_image = make_array(xdim,ydim,value=flagval)
+        FOR i=0, nimg-1 DO BEGIN
+            indx = where(input_mask[*,*,i] gt 0, cindx)
+            IF cindx GT 0 THEN $
+                combined_image[indx] = $
+                    (combined_image < input_cube[*,*,i])[indx]
+        ENDFOR
+        wf = where(combined_image EQ flagval, cf)
+        IF cf GT 0 THEN combined_image[wf] = null_value
+    ENDIF ELSE BEGIN
+        combined_image = input_cube[*,*,0]
+        FOR i=1, nimg-1 DO BEGIN
+            combined_image = (combined_image < input_cube[*,*,i])
+        ENDFOR
+    ENDELSE
+ENDIF ELSE BEGIN
+    print,'CR_REJECT:  Logic error in program initializing check image.'
+    return
+ENDELSE
+;
+; ---------------- MAIN CR REJECTION LOOP. ------------------
+;
+iter=0
+main_loop:
+iter=iter+1
+
+IF clearmask THEN mask_cube[*]=1b
+
+IF track THEN BEGIN
+    print,'CR_REJECT:  Tracking.  Iter = ',strtrim(iter,2)
+    print,'   Combined_image:  '
+    print,combined_image[tracking_set]
+    FOR i = 0, nimg-1 DO BEGIN
+        print,'   Image ', strtrim(i,2), ':'
+        print,(input_cube[*,*,i])[tracking_set]
+        print,'   Noise ', strtrim(i,2), ':'
+        print,(noise_cube[*,*,i])[tracking_set]
+        print,'   Mask  ', strtrim(i,2), ':'
+        print,(mask_cube[*,*,i])[tracking_set]
+    ENDFOR
+ENDIF
+IF verbose THEN BEGIN
+    print,'CR_REJECT:  Beginning iteration number ',strtrim(iter,2)
+    print,'           Sigma limit = ',sig_limit[iter-1]
+ENDIF
+
+FOR i=0, nimg-1 DO BEGIN
+
+    skyarray = fltarr(xdim, ydim)
+    IF xmed THEN BEGIN  
+        FOR jl = 0,ydim-1 DO skyarray[0,jl] = skyvals[*,i]
+    ENDIF ELSE BEGIN 
+        skyarray[*] = skyvals[i]
+    ENDELSE 
+    model_image = $
+      (temporary(skyarray) + (combined_image + dark_dn)*exptime[i])>0
+    
+    IF supplied THEN BEGIN
+        current_var = noise_cube[*,*,i]^2 $
+          + ((mult_noise*temporary(model_image))/exptime[i])^2
+    ENDIF ELSE BEGIN
+        current_var = (rd_noise_dn^2 + model_image/gain $
+                       + (mult_noise*temporary(model_image))^2) $
+                       / (exptime[i]^2)
+    ENDELSE 
+
+    IF track THEN BEGIN
+        print,'CR_REJECT:  Tracking.  Iter = ',strtrim(iter,2), $
+          ' Image = ',strtrim(i,2)
+        print,'           Current_var:  '
+        print,current_var[tracking_set]
+    ENDIF
+
+    testnoise = sig_limit[iter-1] * sqrt(temporary(current_var))
+ 
+    IF track THEN BEGIN
+        print,'           Testnoise:  '
+        print,testnoise[tracking_set]
+    ENDIF
+;
+;  Absolute value used so that if you remove too much, at least you
+;  won't introduce a new bias.
+;
+    cr_subs[0] = $
+      where(abs(input_cube[*,*,i] - combined_image) $
+            GT testnoise, count)
+    IF count GT 0 THEN BEGIN
+        mask_cube[i*npix + cr_subs[0:count-1]] $
+          = replicate(0b,count)
+    ENDIF
+    IF verbose THEN print,'CR_REJECT:  ',strtrim(count,2), $
+      ' pixels flagged in image ',strtrim(i,2)
+    
+;
+;  Dilation of mask
+;
+    count2 = 0
+    IF do_dilation THEN BEGIN
+        tempw = where(dilate(1b-mask_cube[*,*,i], kernel),dct)
+        IF dct GT 0 THEN BEGIN
+            ic1 = input_cube[npix*i + tempw]
+            tn1 = testnoise[tempw]
+            cmi = combined_image[tempw]
+            tewsub = where(abs(temporary(ic1) $
+                               - temporary(cmi)) $
+                           GT (dfactor*temporary(tn1)), count2)
+            cr_subs[0] = (temporary(tempw))[temporary(tewsub)>0]
+            IF count2 GT 0 THEN BEGIN
+                mask_cube[i*npix + cr_subs[0:count2-1]] $
+                  = replicate(0b,count2)
+            ENDIF
+        ENDIF
+        IF verbose THEN print,'CR_REJECT:  Mask dilation performed.  ', $
+          strtrim(count2,2), ' pixels flagged in image ',strtrim(i,2)
+    ENDIF
+ENDFOR
+
+FOR i=0, nimg-1 DO BEGIN
+    cr_subs[0] = where(1b-mask_cube[*,*,i],count)
+;   IF verbose THEN print,'CR_REJECT:  ',strtrim(count,2), $
+;     ' accumulated flags in image ',strtrim(i,2)
+;    IF count GT 0 THEN BEGIN
+;        input_cube(i*npix + cr_subs(0:count-1)) $
+;          = combined_image(cr_subs(0:count-1))
+;        noise_cube(i*npix + cr_subs(0:count-1)) $
+;          = sqrt(current_var(cr_subs(0:count-1)))
+;    ENDIF
+ENDFOR
+
+IF use_input_mask THEN BEGIN
+    combined_npix[0,0] = total((mask_cube AND input_mask),3)
+ENDIF ELSE BEGIN
+    combined_npix[0,0] = total(mask_cube,3)
+ENDELSE
+;
+;  Loop termination condition.
+;
+IF (iter GE maxiter) THEN GOTO,end_main_loop
+
+IF usemedian THEN BEGIN
+    IF verbose THEN print,'CR_REJECT:  Taking median.'
+    IF use_input_mask THEN BEGIN
+        medarr,input_cube,combined_image,mask_cube AND input_mask
+    ENDIF ELSE BEGIN
+        medarr,input_cube,combined_image,mask_cube
+    ENDELSE
+ENDIF ELSE IF usemean THEN BEGIN
+    IF verbose THEN print,'CR_REJECT:  Taking mean.'
+    IF use_input_mask THEN BEGIN
+        maskprod = input_mask[*,*,0] AND mask_cube[*,*,0]
+        combined_image = input_cube[*,*,0]*maskprod*exptime[0]
+        combined_expt  = temporary(maskprod)*exptime[0]
+        IF nimg GT 1 THEN BEGIN
+            FOR i=1,nimg-1 DO BEGIN
+                maskprod = input_mask[*,*,i] AND mask_cube[*,*,i]
+                combined_image = combined_image $
+                  + input_cube[*,*,i]*maskprod*exptime[i]
+                combined_expt = combined_expt $
+                  + temporary(maskprod)*exptime[i]
+            ENDFOR
+        ENDIF
+        wexpt0 = where(combined_expt LE 0,cexpt0)
+        combined_image = combined_image / (combined_expt>1e-6)
+        IF cexpt0 GT 0 THEN combined_image[wexpt0] = 0
+    ENDIF ELSE BEGIN
+        combined_image = input_cube[*,*,0]*mask_cube[*,*,0]*exptime[0]
+        combined_expt  = mask_cube[*,*,0]*exptime[0]
+        IF nimg GT 1 THEN BEGIN
+            FOR i=1,nimg-1 DO BEGIN
+                combined_image = combined_image $
+                  + input_cube[*,*,i]*mask_cube[*,*,i]*exptime[i]
+                combined_expt = combined_expt $
+                  + mask_cube[*,*,i]*exptime[i]
+            ENDFOR
+        ENDIF
+        wexpt0 = where(combined_expt LE 0,cexpt0)
+        combined_image = combined_image / (combined_expt>1e-6)
+        IF cexpt0 GT 0 THEN combined_image[wexpt0] = 0
+    ENDELSE
+ENDIF ELSE IF usemin THEN BEGIN
+    IF verbose THEN print,'CR_REJECT:  Taking minimum.'
+    IF use_input_mask THEN BEGIN
+        combined_image[*] = flagval
+        FOR i=0, nimg-1 DO BEGIN
+            indx = where((input_mask[*,*,i] $ 
+                         AND mask_cube[*,*,i]) gt 0, cindx)
+            IF cindx GT 0 THEN $
+                combined_image[indx] = $
+                    (combined_image < input_cube[*,*,i])[indx]
+        ENDFOR
+        wf = where(combined_image EQ flagval, cf)
+        IF cf GT 0 THEN combined_image[wf] = null_value
+    ENDIF ELSE BEGIN
+        combined_image = input_cube[*,*,0]
+        FOR i=1, nimg-1 DO BEGIN
+            combined_image = (combined_image < input_cube[*,*,i])
+        ENDFOR
+    ENDELSE
+
+    IF use_input_mask THEN BEGIn
+        combined_image = input_cube[*,*,0]*input_mask[*,*,0]
+        FOR i=1, nimg-1 DO BEGIN
+            combined_image = (combined_image < input_cube[*,*,i] $
+                             *input_mask[*,*,i])
+        ENDFOR
+    ENDIF ELSE BEGIN
+        combined_image = input_cube[*,*,0]
+        FOR i=1, nimg-1 DO BEGIN
+            combined_image = (combined_image < input_cube[*,*,i])
+        ENDFOR
+    ENDELSE
+ENDIF ELSE BEGIN
+    print,'CR_REJECT:  Logic error in program recomputing check image.'
+    return
+ENDELSE
+
+GOTO,main_loop
+END_main_loop:
+;
+;  End of CR rejection loop.
+;
+IF verbose THEN BEGIN
+    FOR i=0,nimg-1 DO BEGIN
+        wdummy = where(1b-mask_cube[*,*,i],count) 
+        ncut_tot[i] = count
+    ENDFOR
+    print,'CR_REJECT:  Total pixels changed:  '
+    print,ncut_tot
+ENDIF
+
+IF track THEN BEGIN
+    print,'CR_REJECT:  Tracking.  After loop exit.'
+    print,'   Combined_image:  '
+    print,combined_image[tracking_set]
+;    print,'   Current_var:  '
+;    print,current_var[tracking_set]
+    FOR i = 0, nimg-1 DO BEGIN
+        print,'   Image ', strtrim(i,2), ':'
+        print,(input_cube[*,*,i])[tracking_set]
+        print,'   Noise ', strtrim(i,2), ':'
+        print,(noise_cube[*,*,i])[tracking_set]
+        print,'   Mask  ', strtrim(i,2), ':'
+        print,(mask_cube[*,*,i])[tracking_set]
+    ENDFOR
+ENDIF  
+
+;
+;   Compute weights according to scheme chosen
+;
+xrepl = make_array(dim=xdim,value=1)
+yrepl = make_array(dim=ydim,value=1)
+
+IF wgt EQ 0 THEN BEGIN
+    wgts = xrepl # exptime
+ENDIF ELSE BEGIN
+    IF xmed THEN skytmp = skyvals>1e-6 ELSE skytmp = xrepl # (skyvals>1e-6)
+    exp2tmp = xrepl # (exptime^2)
+    sky_rate_var = temporary(skytmp)/gain/exp2tmp
+    ron_rate_var = rd_noise_dn^2/temporary(exp2tmp)
+    wgts = 1.0/(temporary(sky_rate_var) + temporary(ron_rate_var))
+ENDELSE
+
+;
+;   Do the final co-addition
+;    
+wgt_coeff = fltarr(xdim, ydim)
+FOR i=0,nimg-1 DO BEGIN
+    plane_wgts = wgts[*,i] # yrepl
+    input_cube[0,0,i] = input_cube[*,*,i]*plane_wgts
+    noise_cube[0,0,i] = noise_cube[*,*,i]*plane_wgts
+    IF use_input_mask THEN BEGIN
+        mcim = (mask_cube[*,*,i] AND input_mask[*,*,i])
+    ENDIF ELSE BEGIN
+        mcim = mask_cube[*,*,i]
+    ENDELSE
+    wgt_coeff[0,0] = wgt_coeff + temporary(mcim) * temporary(plane_wgts)
+ENDFOR
+wh0 = where(combined_npix EQ 0,c0)
+wgt_coeff = etot/(wgt_coeff > 1.0e-8)
+IF c0 GT 0 THEN wgt_coeff[wh0] = 0.0
+
+IF verbose THEN BEGIN
+    IF c0 GT 0 THEN $
+      print,'CR_REJECT:  ',strtrim(c0,2),' pixels rejected on all inputs.'
+ENDIF
+
+IF use_input_mask THEN BEGIN
+    IF xmed THEN BEGIN
+        combined_image = wgt_coeff * total(input_cube $
+                                 * (mask_cube AND input_mask),3) $
+                         + totsky#yrepl
+    ENDIF ELSE BEGIN
+        combined_image = wgt_coeff * total(input_cube $
+                                 * (mask_cube AND input_mask),3) $
+                         + totsky
+    ENDELSE
+    combined_noise =  wgt_coeff * sqrt(total((noise_cube $
+                              * (mask_cube AND input_mask))^2,3))
+ENDIF ELSE BEGIN
+    IF xmed THEN BEGIN
+        combined_image = wgt_coeff * total(input_cube*mask_cube,3) $
+                                 + totsky#yrepl
+    ENDIF ELSE BEGIN
+        combined_image = wgt_coeff * total(input_cube*mask_cube,3) $
+                                 + totsky
+    ENDELSE
+    combined_noise = wgt_coeff * sqrt(total((noise_cube*mask_cube)^2,3))
+ENDELSE
+
+IF keyword_set(bias) THEN BEGIN
+    print,'CR_REJECT:  Bias flag set -- returning mean instead of total.'
+    combined_image = combined_image/nimg
+    combined_noise = combined_noise/nimg
+ENDIF
+
+IF c0 GT 0 THEN combined_image[wh0] = null_value
+
+IF keyword_set(restore_sky) THEN BEGIN
+    IF wgt EQ 0 THEN BEGIN
+        IF verbose THEN print,'CR_REJECT:  Adding sky back into data cube'
+        IF xmed THEN BEGIN
+            FOR i=0,nimg-1 DO BEGIN
+                FOR j=0, ydim-1 DO input_cube[0,j,i] = input_cube[*,j,i] $
+                                                       + skyvals[*,i]
+            ENDFOR
+        ENDIF ELSE BEGIN
+            FOR i=0,nimg-1 DO $
+                input_cube[0,0,i] = input_cube[*,*,i] + skyvals[i]
+        ENDELSE
+    ENDIF ELSE BEGIN
+        print, 'CR_REJECT:  /RESTORE_SKY ignored because weighting spec ' $
+            + 'not zero.'
+    ENDELSE
+ENDIF
+
+IF zexp THEN exptime = save_expt
+
+return
+END
diff --git a/Code/script_idl_mv/astrolib/create_struct.pro b/Code/script_idl_mv/astrolib/create_struct.pro
new file mode 100644
index 0000000000000000000000000000000000000000..602dacb6a2758695a3848a5fc9e4f5ce9b582fe7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/create_struct.pro
@@ -0,0 +1,309 @@
+pro create_struct, struct, strname, tagnames, tag_descript, DIMEN = dimen, $
+              CHATTER = chatter, NODELETE = nodelete
+;+
+; NAME:
+;       CREATE_STRUCT
+; PURPOSE:
+;       Create an IDL structure from a list of tag names and dimensions
+; EXPLANATION:
+;       Dynamically create an IDL structure variable from list of tag names 
+;       and data types of arbitrary dimensions.   Useful when the type of
+;       structure needed is not known until run time.
+;
+;       Unlike the intrinsic function CREATE_STRUCT(), this procedure does not
+;       require the user to know the number of tags before run time.   (Note
+;       there is no name conflict since the intrinsic CREATE_STRUCT() is a 
+;       function, and this file contains a procedure.)
+; CALLING SEQUENCE:
+;       CREATE_STRUCT, STRUCT, strname, tagnames, tag_descript, 
+;                             [ DIMEN = , /CHATTER, /NODELETE ]
+;
+; INPUTS:
+;       STRNAME -   name to be associated with structure (string)
+;               Must be unique for each structure created.   Set
+;               STRNAME = '' to create an anonymous structure
+;
+;       TAGNAMES -  tag names for structure elements (string or string array)
+;                Any strings that are not valid IDL tag names (e.g. 'a\2')
+;                will be converted by IDL_VALIDNAME to a valid tagname by 
+;                replacing with underscores as necessary (e.g. 'a_2')
+;
+;       TAG_DESCRIPT -  String descriptor for the structure, containing the
+;               tag type and dimensions.  For example, 'A(2),F(3),I', would
+;               be the descriptor for a structure with 3 tags, strarr(2), 
+;               fltarr(3) and Integer scalar, respectively.
+;               Allowed types are 'A' for strings, 'B' or 'L' for unsigned byte 
+;               integers, 'I' for integers, 'J' for longword integers, 
+;               'K' for 64bit integers, 'F' or 'E' for floating point, 
+;               'D' for double precision  'C' for complex, and 'M' for double 
+;               complex.   Uninterpretable characters in a format field are 
+;               ignored.
+;
+;               For vectors, the tag description can also be specified by
+;               a repeat count.  For example, '16E,2J' would specify a 
+;               structure with two tags, fltarr(16), and lonarr(2)
+;
+; OPTIONAL KEYWORD INPUTS:
+;       DIMEN -    number of dimensions of structure array (default is 1)
+;
+;       CHATTER -  If set, then CREATE_STRUCT() will display
+;                  the dimensions of the structure to be created, and prompt
+;                  the user whether to continue.  Default is no prompt.
+;
+;       /NODELETE - If set, then the temporary file created
+;                  CREATE_STRUCT will not be deleted upon exiting.   See below
+;
+; OUTPUTS:
+;       STRUCT -   IDL structure, created according to specifications 
+;
+; EXAMPLES: 
+;
+;       IDL> create_struct, new, 'name',['tag1','tag2','tag3'], 'D(2),F,A(1)'
+;
+;       will create a structure variable new, with structure name NAME
+;
+;       To see the structure of new:
+;
+;       IDL> help,new,/struc
+;       ** Structure NAME, 3 tags, 20 length:
+;          TAG1            DOUBLE         Array[2]
+;          TAG2            FLOAT          0.0
+;          TAG3            STRING         Array[1]
+;
+; PROCEDURE:
+;       Generates a temporary procedure file using input information with
+;       the desired structure data types and dimensions hard-coded.
+;       This file is then executed with CALL_PROCEDURE.
+;
+; NOTES:
+;       If CREATE_STRUCT cannot write a temporary .pro file in the current 
+;       directory, then it will write the temporary file in the getenv('HOME')
+;       directory.
+;
+;       Note that 'L' now specifies a LOGICAL (byte) data type and not a
+;       a LONG data type for consistency with FITS binary tables
+;
+; RESTRICTIONS:
+;       The name of the structure must be unique, for each structure created.
+;       Otherwise, the new variable will have the same structure as the 
+;       previous definition (because the temporary procedure will not be
+;       recompiled).  ** No error message will be generated  ***
+;
+; SUBROUTINES CALLED:
+;       REPCHR() 
+;
+; MODIFICATION HISTORY:
+;       Version 1.0 RAS January 1992
+;       Modified 26 Feb 1992 for Rosat IDL Library (GAR)
+;       Modified Jun 1992 to accept arrays for tag elements -- KLV, Hughes STX
+;       Accept anonymous structures W. Landsman  HSTX    Sep. 92
+;       Accept 'E' and 'J' format specifications   W. Landsman Jan 93
+;       'L' format now stands for logical and not long array
+;       Accept repeat format for vectors        W. Landsman Feb 93
+;       Accept complex and double complex (for V4.0)   W. Landsman Jul 95
+;       Work for long structure definitions  W. Landsman Aug 97
+;       Write temporary file in HOME directory if necessary  W. Landsman Jul 98
+;       Use OPENR,/DELETE for OS-independent file removal W. Landsman Jan 99
+;       Use STRSPLIT() instead of GETTOK() W. Landsman  July 2002
+;       Assume since V5.3 W. Landsman  Feb 2004
+;       Added RESOLVE_ROUTINE to ensure recompilation W. Landsman Sep. 2004
+;       Delete temporary with FILE_DELETE   W. Landsman Sep 2006
+;       Assume since V5.5, delete VMS reference  W.Landsman Sep 2006
+;       Added 'K' format for 64 bit integers, IDL_VALIDNAME check on tags
+;                       W. Landsman  Feb 2007
+;       Use vector form of IDL_VALIDNAME() if V6.4 or later W.L. Dec 2007
+;       Suppress compilation mesage of temporary file A. Conley/W.L. May 2009
+;       Remove FDECOMP, some cleaner coding  W.L. July 2009
+;       Do not limit string length to 1000 chars   P. Broos,  Feb 2011
+;       Assume since IDL V6.4 W. Landsman Aug 2013
+;-
+;-------------------------------------------------------------------------------
+
+ compile_opt idl2
+ if N_params() LT 4 then begin
+   print,'Syntax - CREATE_STRUCT, STRUCT, strname, tagnames, tag_descript,' 
+   print,'                  [ DIMEN = , /CHATTER, /NODELETE ]'
+   return
+ endif
+
+ if ~keyword_set( chatter) then chatter = 0        ;default is 0
+ if (N_elements(dimen) eq 0) then dimen = 1            ;default is 1
+
+ if (dimen lt 1) then begin
+  print,' Number of dimensions must be >= 1. Returning.'
+  return
+ endif
+
+; For anonymous structure, strname = ''
+  anonymous = 0b
+  if (strlen( strtrim(strname,2)) EQ 0 ) then anonymous = 1b
+
+ good_fmts = [ 'A', 'B', 'I', 'L', 'F', 'E', 'D', 'J','C','M', 'K' ]
+ fmts = ["' '",'0B','0','0B','0.0','0.0','0.0D0','0L','complex(0)', $
+           'dcomplex(0)', '0LL']
+ arrs = [ 'strarr', 'bytarr', 'intarr', 'bytarr', 'fltarr', 'fltarr', $
+          'dblarr', 'lonarr','complexarr','dcomplexarr','lon64arr']
+ ngoodf = N_elements( good_fmts )
+
+; If tagname is a scalar string separated by commas, convert to a string array
+
+ if size(tagnames,/N_dimensions) EQ 0 then begin
+            tagname = strsplit(tagnames,',',/EXTRACT) 
+ endif else tagname = tagnames
+
+ Ntags = N_elements(tagname)
+
+; Make sure supplied tag names are valid.
+ 
+ tagname = idl_validname( tagname, /convert_all )
+
+;  If user supplied a scalar string descriptor then we want to break it up
+;  into individual items.    This is somewhat complicated because the string
+;  delimiter is not always a comma, e.g. if 'F,F(2,2),I(2)', so we need
+;  to check positions of parenthesis also.
+
+ sz = size(tag_descript)
+ if sz[0] EQ 0 then begin
+      tagvar = strarr( Ntags)
+      temptag = tag_descript
+      for i = 0, Ntags - 1 do begin
+         comma = strpos( temptag, ',' )
+         lparen = strpos( temptag, '(' )
+         rparen = strpos( temptag, ')' )
+            if ( comma GT lparen ) and (comma LT Rparen) then pos = Rparen+1 $
+                                                         else pos = comma 
+             if pos EQ -1 then begin
+                 if i NE Ntags-1 then message, $
+         'WARNING - could only parse ' + strtrim(i+1,2) + ' string descriptors'
+                 tagvar[i] = temptag 
+                 goto, DONE
+             endif else begin
+                    tagvar[i] = strmid( temptag, 0, pos )
+                    temptag = strmid( temptag, pos+1)
+              endelse
+             endfor
+             DONE:
+            
+ endif else tagvar = tag_descript
+
+; create string array for IDL statements, to be written into 
+; 'temp_'+strname+'.pro'
+
+ pro_string = strarr (ntags + 2) 
+
+ if (dimen EQ 1) then begin
+
+   pro_string[0] = "struct =  { " + strname + " $"
+   pro_string[ntags+1] = " } "
+
+ endif else begin
+
+   dimen = long(dimen)                ;Changed to LONG from FIX Mar 95
+   pro_string[0] = "struct "   + " = replicate ( { " + strname + " $"
+   pro_string[ntags+1] = " } , " + string(dimen) + ")"
+
+ endelse
+
+ tagvar = strupcase(tagvar) 
+
+ for i = 0, ntags-1 do begin
+
+   goodpos = -1
+   for j = 0,ngoodf-1 do begin
+         fmt_pos = strpos( tagvar[i], good_fmts[j] )
+         if ( fmt_pos GE 0 ) then begin
+              goodpos = j
+              break
+         endif
+   endfor
+
+  if goodpos EQ -1 then begin 
+      print,' Format not recognized: ' + tagvar[i]
+      print,' Allowed formats are :',good_fmts
+      stop,' Redefine tag format (' + string(i) + ' ) or quit now'
+  endif 
+
+
+    if fmt_pos GT 0 then begin
+
+           repeat_count = strmid( tagvar[i], 0, fmt_pos )
+           if strnumber( repeat_count, value ) then begin
+                fmt = arrs[ goodpos ] + '(' + strtrim(fix(value), 2) + ')'
+           endif else begin 
+                print,' Format not recognized: ' + tagvar[i]
+                stop,' Redefine tag format (' + string(i) + ' ) or quit now'
+           endelse
+
+    endif else  begin
+
+; Break up the tag descriptor into a format and a dimension
+    tagfmts = strmid( tagvar[i], 0, 1)
+    tagdim = strtrim( strmid( tagvar[i], 1, 80),2)
+    if strmid(tagdim,0,1) NE '(' then tagdim = ''
+    fmt = (tagdim EQ '') ? fmts[goodpos] : arrs[goodpos] + tagdim 
+    endelse
+
+  if anonymous and ( i EQ 0 ) then comma = '' else comma = " , "
+
+      pro_string[i+1] = comma + tagname[i] + ": " + fmt + " $"      
+
+ endfor
+
+; Check that this structure definition is OK (if chatter set to 1)
+ 
+ if keyword_set ( Chatter )  then begin
+   ans = ''
+   print,' Structure ',strname,' will be defined according to the following:'
+   temp = repchr( pro_string, '$', '')
+   print, temp
+   read,' OK to continue? (Y or N)  ',ans
+   if strmid(strupcase(ans),0,1) eq 'N' then begin
+      print,' Returning at user request.'
+     return
+   endif
+ endif 
+
+; --- Determine if a file already exists with same name as temporary file
+
+ tempfile = 'temp_' + strlowcase( strname )
+ while file_test( tempfile + '.pro' ) do tempfile = tempfile + 'x'
+ 
+; ---- open temp file and create procedure
+; ---- If problems writing into the current directory, try the HOME directory
+
+ cd,current= prodir 
+ cdhome = 0
+ openw, unit, tempfile +'.pro', /get_lun, ERROR = err
+ if (err LT 0)  then begin
+      prodir = getenv('HOME')
+      tempfile = prodir + path_sep() + tempfile
+      while file_test( tempfile + '.pro' ) do tempfile = tempfile + 'x'
+      openw, unit, tempfile +'.pro', /get_lun, ERROR = err
+      if err LT 0 then message,'Unable to create a temporary .pro file'
+      cdhome = 1
+  endif
+ name = file_basename(tempfile)
+ printf, unit, 'pro ' +  name + ', struct'
+ printf,unit,'compile_opt hidden'
+ for j = 0,N_elements(pro_string)-1 do $
+        printf, unit, strtrim( pro_string[j] )
+ printf, unit, 'return'
+ printf, unit, 'end'
+ free_lun, unit
+
+; If using the HOME directory, it needs to be included in the IDL !PATH
+
+ if cdhome then cd,getenv('HOME'),curr=curr
+  resolve_routine, name
+  Call_procedure, name, struct
+ if cdhome then cd,curr
+
+ if keyword_set( NODELETE ) then begin
+    message,'Created temporary file ' + tempfile + '.pro',/INF
+    return
+ endif else file_delete, tempfile + '.pro'
+  
+  return
+  end         ;pro create_struct
+
+
diff --git a/Code/script_idl_mv/astrolib/cspline.pro b/Code/script_idl_mv/astrolib/cspline.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7dede40e206aa4ca965e58288db1971576f28591
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/cspline.pro
@@ -0,0 +1,79 @@
+function cspline,xx, yy, tt, Deriv = deriv
+;+
+; NAME:
+;      CSPLINE
+;
+; PURPOSE:
+;      Function to evaluate a natural cubic spline at specified data points
+; EXPLANATION:
+;      Combines the Numerical Recipes functions SPL_INIT and SPL_INTERP
+;
+; CALLING SEQUENCE:
+;      result = cspline( x, y, t, [ DERIV = ])
+;
+; INPUTS:
+;      x - vector of spline node positions, must be monotonic increasing or
+;          decreasing
+;      y - vector of node values
+;      t - x-positions at which to evaluate the spline, scalar or vector
+;
+; INPUT-OUTPUT KEYWORD:
+;      DERIV - values of the second derivatives of the interpolating function 
+;               at the node points.   This is an intermediate step in the 
+;               computation of the natural spline that requires only the X and 
+;               Y vectors.    If repeated interpolation is to be applied to 
+;               the same (X,Y) pair, then some computation time can be saved 
+;               by supplying the DERIV keyword on each call.   On the first call
+;               DERIV will be computed and returned on output.    
+;
+; OUTPUT:
+;       the values for positions t are returned as the function value
+;       If any of the input variables are double precision, then the output will
+;       also be double precision; otherwise the output is floating point.
+;
+; EXAMPLE:                               
+;       The following uses the example vectors from the SPL_INTERP documentation
+;
+;       IDL> x = (findgen(21)/20.0)*2.0*!PI ;X vector
+;       IDL> y = sin(x)                     ;Y vector
+;       IDL> t = (findgen(11)/11.0)*!PI     ;Values at which to interpolate 
+;       IDL> cgplot,x,y,psym=1                ;Plot original grid
+;       IDL> cgplot, /over, t,cspline(x,y,t),psym=2 ;Overplot interpolated values
+;
+; METHOD:
+;      The "Numerical Recipes" implementation of the natural cubic spline is 
+;      used, by calling the intrinsic IDL functions SPL_INIT and SPL_INTERP.
+;
+; HISTORY:
+;      version 1  D. Lindler  May, 1989
+;      version 2  W. Landsman April, 1997
+;      Rewrite using the intrinsic SPL_INIT & SPL_INTERP functions
+;      Converted to IDL V5.0   W. Landsman   September 1997
+;      Work for monotonic decreasing X vector    W. Landsman   February 1999
+;-
+;--------------------------------------------------------------------------
+
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 3 then begin
+        print,'Syntax:  result = cspline( x, y, t, [ DERIV = ] )'
+        return,-1
+ endif 
+                
+ n = N_elements(xx)
+ if xx[n-1] LT xx[0] then begin               ;Descending order?
+        xrev = reverse(xx)
+        yrev = reverse(yy)
+        if N_elements(Deriv) NE n then begin
+                 if min( xx - xx[1:*]) LT 0 then $
+                          message,'ERROR - Input vector not monotonic' 
+                 deriv = spl_init( xrev, yrev)
+        endif
+        return, spl_interp( xrev, yrev, deriv, tt)
+ endif
+
+ if N_elements(Deriv) NE n then deriv = spl_init( xx, yy)
+ return, spl_interp( xx, yy, deriv, tt)
+
+ end
diff --git a/Code/script_idl_mv/astrolib/ct2lst.pro b/Code/script_idl_mv/astrolib/ct2lst.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2244ce8b929d01a529406061b17c0d73be55343e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ct2lst.pro
@@ -0,0 +1,109 @@
+PRO CT2LST, lst, lng, tz, tme, day, mon, year
+;+
+; NAME:
+;     CT2LST
+; PURPOSE:
+;     To convert from Local Civil Time to Local Mean Sidereal Time.
+;
+; CALLING SEQUENCE:
+;     CT2LST, Lst, Lng, Tz, Time, [Day, Mon, Year] 
+;                       or
+;     CT2LST, Lst, Lng, dummy, JD
+;
+; INPUTS:
+;     Lng  - The longitude in degrees (east of Greenwich) of the place for 
+;            which the local sidereal time is desired, scalar.   The Greenwich 
+;            mean sidereal time (GMST) can be found by setting Lng = 0.
+;     Tz  - The time zone of the site in hours, positive East  of the Greenwich
+;           meridian (ahead of GMT).  Use this parameter to easily account 
+;           for Daylight Savings time (e.g. -4=EDT, -5 = EST/CDT), scalar
+;           This parameter is not needed (and ignored) if Julian date is 
+;           supplied.    ***Note that the sign of TZ was changed in July 2008
+;           to match the standard definition.*** 
+;     Time or JD  - If more than four parameters are specified, then this is 
+;               the time of day of the specified date in decimal hours.  If 
+;               exactly four parameters are specified, then this is the 
+;               Julian date of time in question, scalar or vector
+;
+; OPTIONAL INPUTS:
+;      Day -  The day of the month (1-31),integer scalar or vector
+;      Mon -  The month, in numerical format (1-12), integer scalar or vector
+;      Year - The 4 digit year (e.g. 2008), integer scalar or vector
+;
+; OUTPUTS:
+;       Lst   The Local Sidereal Time for the date/time specified in hours.
+;
+; RESTRICTIONS:
+;       If specified, the date should be in numerical form.  The year should
+;       appear as yyyy.
+;
+; PROCEDURE:
+;       The Julian date of the day and time is question is used to determine
+;       the number of days to have passed since 0 Jan 2000.  This is used
+;       in conjunction with the GST of that date to extrapolate to the current
+;       GST; this is then used to get the LST.    See Astronomical Algorithms
+;       by Jean Meeus, p. 84 (Eq. 11-4) for the constants used.
+;
+; EXAMPLE:
+;       Find the Greenwich mean sidereal time (GMST) on 2008 Jul 30 at 15:53 pm
+;       in Baltimore, Maryland (longitude=-76.72 degrees).   The timezone is 
+;       EDT or tz=-4
+;
+;       IDL> CT2LST, lst, -76.72, -4,ten(15,53), 30, 07, 2008
+;
+;               ==> lst =  11.356505  hours  (= 11h 21m 23.418s)
+;
+;       The Web site  http://tycho.usno.navy.mil/sidereal.html contains more
+;       info on sidereal time, as well as an interactive calculator.
+; PROCEDURES USED:
+;       jdcnv - Convert from year, month, day, hour to julian date
+;
+; MODIFICATION HISTORY:
+;     Adapted from the FORTRAN program GETSD by Michael R. Greason, STX, 
+;               27 October 1988.
+;     Use IAU 1984 constants Wayne Landsman, HSTX, April 1995, results 
+;               differ by about 0.1 seconds  
+;     Longitudes measured *east* of Greenwich   W. Landsman    December 1998
+;     Time zone now measure positive East of Greenwich W. Landsman July 2008
+;     Remove debugging print statement  W. Landsman April 2009
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 3 THEN BEGIN
+        print,'Syntax - CT2LST, Lst, Lng, Tz, Time, Day, Mon, Year' 
+        print,'                 or'
+        print,'         CT2LST, Lst, Lng, Tz, JD'
+        return
+ endif
+;                            If all parameters were given, then compute
+;                            the Julian date; otherwise assume it is stored
+;                            in Time.
+;
+
+ IF N_params() gt 4 THEN BEGIN
+   time = tme - tz
+   jdcnv, year, mon, day, time, jd 
+
+ ENDIF ELSE jd = double(tme)
+;
+;                            Useful constants, see Meeus, p.84
+;
+ c = [280.46061837d0, 360.98564736629d0, 0.000387933d0, 38710000.0 ]
+ jd2000 = 2451545.0D0
+ t0 = jd - jd2000
+ t = t0/36525
+;
+;                            Compute GST in seconds.
+;
+ theta = c[0] + (c[1] * t0) + t^2*(c[2] - t/ c[3] )
+;
+;                            Compute LST in hours.
+;
+ lst = ( theta + double(lng))/15.0d
+ neg = where(lst lt 0.0D0, n)
+ if n gt 0 then lst[neg] = 24.D0 + (lst[neg] mod 24)
+ lst = lst mod 24.D0
+;   
+ RETURN
+ END
diff --git a/Code/script_idl_mv/astrolib/curs.pro b/Code/script_idl_mv/astrolib/curs.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c6282e556311479431b95948912f42eccd4e7389
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/curs.pro
@@ -0,0 +1,135 @@
+pro curs, sel
+;+
+; NAME:
+;       CURS
+; PURPOSE:
+;       Selects an X windows cursor shape
+; CALLING SEQUENCE:
+;       curs            ;Interactively select a cursor shape.
+;       curs, sel       ;Make the given CURSOR_STANDARD value the cursor
+;                        shape.
+; OPTIONAL INPUT:
+;       sel  -  Either an integer giving the CURSOR_STANDARD value (usually an 
+;               even value between 0 and 152) indicating the cursor shape, or 
+;               a string from the following menu
+;       a -- Up arrow              
+;       b -- Left-angled arrow
+;       c -- Right-angled arrow
+;       d -- Crosshair
+;       e -- Finger pointing left 
+;       f -- Finger pointing right
+;       g -- Narrow crosshair
+;       h -- Cycle through all possible standard cursor shapes
+; 
+;       The full list of available cursor values is given in 
+;      /usr/include/X11/cursorfont.h
+; OUTPUTS:
+;       None.
+; RESTRICTIONS:
+;       Uses the CURSOR_STANDARD keyword of the DEVICE procedure.  Although 
+;       this keyword is available in Windows IDL, the values
+;       used by this procedure are specific to the X windows device.
+;
+; PROCEDURE:
+;       If the user supplies a valid cursor shape value, it is set.  Otherwise,
+;       an interactive command loop is entered; it will continue until a valid
+;       value is given.
+; MODIFICATION HISTORY:
+;       Converted to VAX 3100 workstations / IDL V2.  M. Greason, STX, May 1990.
+;       Avoid bad cursor parameter values  W. Landsman   February, 1991
+;       Don't change value of input param        W. Landsman   August 1995
+;       Use SIZE(/TNAME) instead of DATATYPE()   W. Landsman  October 2001
+;-
+On_error,2
+if !D.NAME NE 'X' then message, $
+     'ERROR - Requires an X-windows display, current device is ' + !D.NAME
+;                       Check parameter.
+;
+isel = indgen(76)*2
+nsel = n_elements(isel)
+;
+IF N_elements( sel ) EQ 0 THEN sel = 0
+;
+;                       Get the selection interactively, if not already
+;                       specified.
+;
+;                               Initialize.
+;
+mnu = ["  a -- Up arrow", "  b -- Left-angled arrow", $
+       "  c -- Right-angled arrow", "  d -- Crosshair", $
+       "  e -- Finger pointing left", "  f -- Finger pointing right", $
+       "  g -- Narrow crosshair", $
+       "  h -- Cycle through all possible standard cursor shapes", $
+       "  i -- Enter cursor shape number directly", "  j -- Quit"]
+nmnu = n_elements(mnu)
+fmt = "($,'Code ',I3,'      ',I3,' of ',I3,'      ')"
+IF size(sel,/TNAME) EQ 'STRING' then begin
+             cmd = strupcase(sel)
+             csel = -99
+ENDIF ELSE csel = sel
+;
+;                               While loop until a selection is made.
+;
+WHILE (csel LE 0) OR (csel GT isel[nsel-1]) DO BEGIN
+;
+;                                       Get command.
+;
+if csel NE -99 then begin
+        print, "Cursor selection:"
+        print, "   "
+        FOR i = 0, (nmnu-1) DO print, mnu[i]
+        print, "   "
+        cmd = ''
+        read, "Enter the letter of the desired command: ",cmd
+endif
+;
+;                                       Perform the command.
+;
+MENU:   CASE strupcase(cmd) OF
+                 'A' : csel = 22                        ; Up arrow
+                 'B' : csel = 132               ; Left arrow
+                 'C' : csel = 2                 ; Right arrow
+                 'D' : csel = 34                        ; X-hair.
+                 'E' : csel = 56                        ; Left hand.
+                 'F' : csel = 58                        ; Right hand.
+                 'G' : csel = 33                        ; Narrow crosshair.
+                 'H' : BEGIN                    ; Cycle thru all cursors.
+                          print, "  "
+                          print, "  "
+                          print, "Cycling through the possible cursors."
+                          print, "  "
+                          print, "Strike the space bar to select, any other"
+                          print, "key to reject." 
+                          print, "  "
+                          print, "  "
+                          scr_curmov, 0, 1
+                          cont = 1
+                          FOR i = 0, (nsel-1) DO BEGIN
+                                IF cont THEN BEGIN
+                                        csel = isel[i]
+                                        print, format=fmt, csel, i+1, nsel
+                                        scr_curmov, 2, 31
+                                        device, cursor_standard=csel
+                                        IF get_kbrd(1) EQ ' ' THEN cont = 0
+                                ENDIF
+                          ENDFOR
+                       END
+                 'I' : BEGIN                    ; Get # from user.
+                          print, "  "
+                          print, "  "
+                          print, format="(A14,$)", "Enter cursor #"
+                          read, csel
+                          IF (csel LE 0) OR (csel GT isel[nsel-1]) THEN $
+                                print, "Invalid entry."
+                       END
+                 'J' : csel = 34                ; Quit.  Set to X-hair.
+                ELSE : csel = 0                 ; Invalid command.
+        ENDCASE
+ENDWHILE
+;
+;                       Set the cursor shape
+;
+device, cursor_standard=csel
+;
+RETURN
+END
diff --git a/Code/script_idl_mv/astrolib/curval.pro b/Code/script_idl_mv/astrolib/curval.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7dd13ba7abce9da084ef58083b5747b40c7571f2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/curval.pro
@@ -0,0 +1,304 @@
+pro curval, hd, im, OFFSET = offset, ZOOM = zoom, Filename=Filename, ALT = alt
+;+
+; NAME:
+;       CURVAL
+; PURPOSE:   
+;       Cursor controlled display of image intensities and astronomical coords
+; EXPLANATION
+;       CURVAL displays different information depending whether the user 
+;       supplied an image array, and/or a FITS header array
+;
+;       Note that in the usual truecolor mode, the byte intensity returned by 
+;       CURVAL does not correspond to the byte scaled image value but rather 
+;       returns the maximum value in each color gun.
+; CALLING SEQUENCE(S):
+;       curval          ;Display x,y and byte intensity (inten)
+;       
+;       curval, im   ;Display x,y,inten, and also pixel value (from image array)
+;       
+;       curval, hdr, [ im, OFFSET= , ZOOM=, FILENAME=, ALT=]        
+;
+; OPTIONAL INPUTS:
+;       Hdr  = FITS Header array
+;       Im  = Array containing values that are displayed.  Any type.
+;
+; OPTIONAL KEYWORD INPUTS:
+;      ALT - single character 'A' through 'Z' or ' ' specifying an alternate
+;            astrometry system present in the FITS header.    The default is
+;            to use the primary astrometry or ALT = ' '.   If /ALT is set,
+;            then this is equivalent to ALT = 'A'.   See Section 3.3 of
+;            Greisen & Calabretta (2002, A&A, 395, 1061) for information about
+;            alternate astrometry keywords.
+;      OFFSET - 2 element vector giving the location of the image pixel (0,0) 
+;               on the window display.   OFFSET can be positive (e.g if the 
+;               image is centered in a larger window) or negative (e.g. if the
+;               only the central region of an image much larger than the window
+;               is being displayed. 
+;               Default value is [0,0], or no offset.
+;       ZOOM - Scalar specifying the magnification of the window with respect
+;               to the image variable.    Use, for example, if image has been
+;               REBINed before display.
+;       FILENAME  = name of file to where CURVAL data can be saved.
+;               Data will only be saved if left or center mouse button
+;               are pressed.
+;
+; OUTPUTS:
+;       None.
+;
+; SIDE EFFECTS:
+;       X and Y values, etc., of the pixel under the cursor are constantly
+;       displayed.  
+;       Pressing left or center mouse button prints a line of output, and 
+;       starts a new line.
+;       Pressing right mouse button exits the procedure.
+;       If the keyword FILENAME is defined, the date and time, and a heading 
+;       will be printed in the file before the data.
+;
+; PROCEDURES CALLED:
+;       ADSTRING(), EXTAST, GSSSXYAD, RADEC, SXPAR(), UNZOOM_XY, XY2AD
+; REVISION HISTORY:
+;       Written,  K. Rhode,  STX  May 1990
+;       Added keyword FILENAME  D. Alexander  June 1991
+;       Don't write to Journal file   W. Landsman    March 1993
+;       Use astrometry structure  W. Landsman      Feb 1994
+;       Modified for Mac IDL          I.   Freedman     April 1994
+;       Allow for zoomed or offset image  W. Landsman      Mar 1996
+;       Proper rounding of zoomed pixel values   W. Landsman/R. Hurt  Dec. 1997
+;       Remove unneeded calls to obsolete !ERR   W. Landsman   December 2000
+;       Replace remaining !ERR calls with !MOUSE.BUTTON W. Landsman Jan 2001
+;       Allow for non-celestial (e.g. Galactic) coordinates W. Landsman Apr 2003
+;       Work if RA/Dec reversed in CTYPE keyword  W. Landsman Feb. 2004
+;       Always call UNZOOM_XY for MOUSSE compatibility W. Landsman Sep. 2004
+;       Added ALT keyword  W. Landsman October 2004 
+;       Always test if offset/zoom supplied  W. Landsman  Feb 2008 
+;-
+ On_error,2    ;if an error occurs, return to caller
+ compile_opt idl2
+
+
+ f_header = 0b           ;True if a FITS header supplied
+ f_image =  0b           ;True if an image array supplied
+ f_astrom = 0b           ;True if FITS header contains astrometry
+ f_bscale = 0b           ;True if FITS header contains BSCALE factors
+ f_imhd   = 0b           ;True if image array is in HD (1 parameter)
+ npar = N_params()
+ fileflag=0             ;True once left or middle mouse button pressed
+
+ if !D.WINDOW EQ -1 then begin
+        message,'ERROR - No image window active',/INF
+        return
+ endif
+
+
+if (!D.FLAGS and 256) EQ 256 then wshow,!D.WINDOW  ;Bring active window to foreground
+
+; Print formats and header for different astrometry,image, BSCALE combinations
+
+ cr = string(13b)
+ line0 = '  X     Y     Byte Inten'
+ line1 = '  X     Y     Byte Inten   Value'
+ line5 = '  X     Y   ByteInten   Value   Flux'
+
+ f0 = "($,a,i4,2x,i4,6x,i4)"
+ f1 = "($,a,i4,2x,i4,6x,i4,5x,a)"
+ f2 = "($,a,i4,2x,i4,6x,i4,7x,a,1x,a)"
+ f3 = "($,a,i4,2x,i4,2x,i4,7x,a,2x,a,1x,a,3x,e9.2)"
+ f4 = "($,a,i4,2x,i4,2x,i4,7x,a,1x,a,a)"
+ f5 = "($,a,i4,2x,i4,2x,i4,3x,a,5x,e9.2)"
+
+ g0 = "(a,i4,2x,i4,6x,i4)"
+ g1 = "(a,i4,2x,i4,6x,i4,5x,a)"
+ g2 = "(a,i4,2x,i4,6x,i4,7x,a,1x,a)"
+ g3 = "(a,i4,2x,i4,2x,i4,7x,a,2x,a,1x,a,3x,e9.2)"
+ g4 = "(a,i4,2x,i4,2x,i4,7x,a,2x,a,1x,a)"
+ g5 = "(a,i4,2x,i4,2x,i4,3x,a,5x,e9.2)"
+
+if (npar gt 0) then begin
+  type = size(hd)
+  if (npar eq 1) and (type[0] eq 2) then begin
+    f_image = 1b  & f_imhd = 1b 
+    imtype = type
+  endif else if (type[2] ne 7) or (type[0] ne 1) then begin
+    print,'Syntax options: CURVAL        ;Display byte values'
+    print,'                CURVAL, IM    ;where IM is a 2-D image,'
+    print,'                CURVAL, Hdr   ;where Hdr is a FITS header,'
+    print,'            or  CURVAL, Hdr,IM'
+    return
+  endif else if (type[2] eq 7) and (type[0] eq 1) then f_header = 1b
+  if (npar eq 2) then begin
+    f_image = 1b & f_header = 1b
+    imtype = size(im)
+    if (imtype[0] lt 2) or $
+     (imtype[imtype[0]+2] ne imtype[1]*imtype[2]) then $
+       message,'Image array (second parameter) is not two dimensional.'
+  endif
+endif    
+
+; Get information from the header
+
+ if f_header then begin     
+
+  EXTAST, hd, astr, noparams, alt=alt                 ;Extract astrometry structure
+  if (noparams ge 0) then f_astrom = 1b
+
+  if f_image then begin
+  bscale = sxpar(hd,'BSCALE')
+  if (bscale ne 0) then begin
+    bzero = sxpar(hd,'BZERO')
+    bunit = sxpar(hd,'BUNIT', Count = N_Bunit)
+    if N_Bunit GE 1 then $ 
+    if f_astrom then line3 = line3 + '('+bunit+ ')' else $
+                     line5 = line5 + '('+bunit+')'
+    f_bscale = 1b
+  endif
+  endif
+ endif
+
+; Determine if an offset or zoom supplied
+ unzoom = f_image  or f_header or keyword_set(offset) or keyword_set(zoom)
+
+ if f_astrom GT 0 then begin
+  coord = strmid(astr.ctype,0,4)
+  coord = repchr(coord,'-',' ')
+  if (coord[0] EQ 'DEC ') or (coord[0] EQ 'ELAT') or $
+     (coord[0] EQ 'GLAT') then coord = rotate(coord,2)
+
+  line2 = '  X     Y     Byte Inten        '  + coord[0] + '       ' +coord[1]
+  line3 = '  X     Y   ByteInten    Value       ' + coord[0]  + '         ' + $
+             coord[1] + '           Flux' 
+  line4 = '  X     Y   ByteInten     Value      '  + coord[0] + '          ' + $
+             coord[1]
+
+  sexig = strupcase(strmid(coord[0],0,4))  EQ 'RA  ' 
+ endif
+
+ print,'Press left or center mouse button for new output line,'
+ print,'... right mouse button to exit.'  
+
+; different print statements, depending on the parameters
+
+ case 1 of
+
+(f_image eq 0b) and (f_astrom eq 0b):  begin   
+   curtype = 0 & print, line0  & end      ;No image or header info
+
+(f_image) and (f_astrom eq 0b) and (f_bscale eq 0b): begin
+   curtype = 1  & print,line1 & end       ;Only image array supplied
+
+(f_image eq 0b) and (f_astrom) and (f_bscale eq 0b): begin 
+   curtype = 2  & print,line2 & end       ;Astrometry but no image array
+
+(f_image) and (f_astrom) and (f_bscale): begin
+   curtype =3   & print,line3 & end       ;Image array + astrometry + BSCALE
+
+(f_image) and (f_astrom) and (f_bscale eq 0b): begin
+   curtype = 4  & print,line4 & end       ;Image array +astrometry
+
+(f_image) and (f_astrom eq 0b) and (f_bscale): begin
+   curtype = 5  & print,line5 & end       ;Image array + BSCALE
+
+endcase
+ if f_image then begin
+      dtype = imtype[imtype[0]+1]
+      if (dtype LT 4) or (dtype GE 12) then dfmt = '(I8)' else  dfmt = '(G8.3)'
+ endif
+
+ LOOP: sv_err = !MOUSE.BUTTON
+ !MOUSE.BUTTON = 0
+ cursor,x,y,2,/DEVICE,/CHANGE                                 
+ cr_err = !MOUSE.BUTTON
+
+ if cr_err EQ 4 then begin
+    print,' '
+    if fileflag then free_lun,lun
+    return
+
+ endif
+
+
+  x = x>0 & y = y>0
+  inten = fix(tvrd(x,y,1,1))   ; read the byte intensity 
+
+ if unzoom then unzoom_xy,x,y,offset=offset,zoom=zoom
+
+ if f_astrom then begin
+
+        case strmid(astr.ctype[0],5,3) of 
+        'GSS': gsssxyad, astr, x, y, a, d
+        else:  xy2ad, x, y, astr, a, d            ; convert to ra and dec
+        endcase
+
+        if sexig then begin 
+            str = adstring(a,d,2)
+            a = strmid(str,1,13)
+            d  = strmid(str,14,13)
+        endif else begin
+            a = string(a,'(f10.2)') + '   '
+            d = string(d,'(f10.2)') + '   '
+        endelse
+ endif
+
+ x = round(x)  & y = round(y)
+
+ if f_image then begin
+      if (x LT 0) or (x GE imtype[1]) or $
+         (y LT 0) or (y GE imtype[2]) then value = 0 else $
+      if f_imhd then value = hd[x,y] else value = im[x,y]
+      svalue = string(value,f=dfmt)
+ endif
+
+ if f_bscale  then flux = bscale*value + bzero  
+ case curtype of
+        0:  print,form=f0,cr,x,y,inten  
+        1:  print,form=f1,cr,x,y,inten,svalue 
+        2:  print,form=f2,cr,x,y,inten,a,d        
+        3:  print,form=f3,cr,x,y,inten,svalue,a,d,flux
+        4:  print,form=f4,cr,x,y,inten,svalue,a,d
+        5:  print,form=f5,cr,x,y,inten,svalue,flux
+ endcase
+
+; Were left or center buttons been pressed?
+
+ if (cr_err GE 1) and (cr_err LE 3) and (cr_err NE sv_err) then begin  
+    print,form="($,a)",string(10b)   ; print a form feed
+    if keyword_set(filename) and (not fileflag) then begin      ; open file & print table header to file
+        get_lun,lun
+        openw,lun,filename
+        printf,lun,'CURVAL:   ',systime()      ;print time and date to file
+        case 1 of               ;different print statements for file, depending on parameters
+
+        (f_image eq 0b) and (f_astrom eq 0b) : begin
+           printf, lun, line0  & end                    ;No image or header info
+
+        (f_image) and (f_astrom eq 0b) and (f_bscale eq 0b) : begin
+           printf, lun, line1 & end                     ;Only image array supplied
+
+        (f_image eq 0b) and (f_astrom) and (f_bscale eq 0b) : begin
+           printf, lun, line2 & end                     ;Astrometry but no image array
+
+        (f_image) and (f_astrom) and (f_bscale) : begin
+           printf, lun, line3 & end                     ;Image array + astrometry + BSCALE
+
+        (f_image) and (f_astrom) and (f_bscale eq 0b) : begin
+           printf, lun, line4 & end                     ;Image array + astrometry
+
+        (f_image) and (f_astrom eq 0b) and (f_bscale) : begin
+           printf, lun, line5 & end                     ;Image array + BSCALE
+        endcase
+        fileflag=1
+    endif
+    if keyword_set(filename) then begin
+        case curtype of 
+           0: printf, lun, form=g0,'', x, y, inten
+           1: printf, lun, form=g1,'', x, y, inten, svalue 
+           2: printf, lun, form=g2,'', x, y, inten, a, d
+           3: printf, lun, form=g3,'', x, y, inten, svalue, a, d, flux
+           4: printf, lun, form=g4,'', x, y, inten, svalue, a, d
+           5: printf, lun, form=g5,'', x, y, inten, svalue, flux
+        endcase
+    endif
+ endif
+
+ goto,LOOP
+
+ end
diff --git a/Code/script_idl_mv/astrolib/dao_value.pro b/Code/script_idl_mv/astrolib/dao_value.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2aaa4aa4d528ebc794c070326eae11c3e65c9078
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dao_value.pro
@@ -0,0 +1,87 @@
+FUNCTION  DAO_VALUE, XX, YY, GAUSS, PSF, DVDX, DVDY
+;+
+; NAME:
+;	DAO_VALUE
+; PURPOSE:
+;	Returns the value of a DAOPHOT point-spread function at a set of points.
+; EXPLANATION:
+;	The value of the point-spread function is the sum of a
+;	two-dimensional integral under a bivariate Gaussian function, and 
+;	a value obtained by interpolation in a look-up table.  DAO_VALUE will
+;	optionally compute the derivatives wrt X and Y
+;
+; CALLING SEQUENCE:
+;	Result = DAO_VALUE( xx, yy, gauss, psf, [ dvdx, dvdy ] )
+;
+; INPUTS:
+;	XX,YY   - the real coordinates of the desired point relative 
+;		to the centroid of the point-spread function.
+;	GAUSS  -  5 element vector describing the bivariate Gaussian
+;	GAUSS(0)- the peak height of the best-fitting Gaussian profile.
+;	GAUSS(1,2) - x and y offsets from the centroid of the point-spread 
+;		function to the center of the best-fitting Gaussian.
+;	GAUSS(3,4) - the x and y sigmas of the best-fitting Gaussian.
+;	PSF  -  a NPSF by NPSF array containing the look-up table.
+;
+; OUTPUTS:
+;    RESULT - the computed value of the point-spread function at
+;             a position XX, YY relative to its centroid (which 
+;             coincides with the center of the central pixel of the
+;             look-up table).
+;
+; OPTIONAL OUTPUTS:
+;       DVDX,DVDY - the first derivatives of the composite point-spread
+;             function with respect to x and y.
+;
+; NOTES
+; 	although the arguments XX,YY of the function DAO_VALUE
+;	are relative to the centroid of the PSF, the function RINTER which
+;	DAO_VALUE calls requires coordinates relative to the corner of the 
+;	array (see code).
+;
+; PROCEDURES CALLED:
+;	DAOERF, RINTER()
+; REVISON HISTORY:
+;	Adapted to IDL by B. Pfarr, STX, 11/17/87 from 1986 STSDAS version
+;	of DAOPHOT
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ s = size(psf)
+ npsf = s[1]
+ half = float(npsf-1)/2 
+
+ x = 2.*xx + half   ;Initialize
+ y = 2.*yy + half
+
+; X and Y are the coordinates relative to the corner of the look-up table, 
+; which has a half-pixel grid size.  
+
+ if ( (min(x) LT 1.) or ( max(x) GT npsf-2.) or  $
+      (min(y) LT 1.) or ( max(y) GT npsf-2.) ) then begin
+         message,'X,Y positions too close to edge of frame',/INF
+         return,xx*0
+  endif
+
+; Evaluate the approximating Gaussian.
+; Then add a value interpolated from the look-up table to the approximating
+; Gaussian.  Since the lookup table has a grid size of one-half pixel in each
+; coordinate, the spatial derivatives must be multiplied by two to yield
+; the derivatives in units of ADU/pixel in the big frame.
+
+ if N_params() GT 4 then begin   ;Compute derivatives?
+
+     DAOERF, xx, yy, gauss, e, pder 
+     value = e + RINTER( psf, x, y, dfdx, dfdy)
+     dvdx = 2.*dfdx - pder[*,1]
+     dvdy = 2.*dfdy - pder[*,2]           
+
+ endif else begin  
+
+     DAOERF, xx, yy, gauss, e
+     value = e + RINTER(psf,x,y)
+
+ endelse
+
+ return, value
+
+ end                                             
diff --git a/Code/script_idl_mv/astrolib/daoerf.pro b/Code/script_idl_mv/astrolib/daoerf.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f1451e6948d58734717e4eaa84b46fe7153179e5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/daoerf.pro
@@ -0,0 +1,58 @@
+pro daoerf,x,y,a,f,pder	;DAOphot ERRor function
+;+
+; NAME:
+;	DAOERF
+; PURPOSE:         
+;	Calulates the intensity, and derivatives, of a 2-d Gaussian PSF
+; EXPLANATION:
+;	Corrects for the finite size of a pixel by integrating the Gaussian
+;	over the size of the pixel.    Used in the IDL-DAOPHOT sequence.   
+;
+; CALLING SEQUENCE:
+;	DAOERF, XIN, YIN, A, F, [ PDER ] 
+;
+; INPUTS:
+;	XIN - input scalar, vector or array, giving X coordinate values
+;	YIN - input scalar, vector or array, giving Y coordinate values, must 
+;		have same number of elements as XIN.
+;	A - 5 element parameter array describing the Gaussian
+;		A(0) - peak intensity
+;		A(1) - X position of peak intensity (centroid)
+;		A(2) - Y position of peak intensity (centroid)
+;		A(3) - X sigma of the gaussian (=FWHM/2.345)         
+;		A(4) - Y sigma of gaussian
+;
+; OUTPUTS:
+;	F - array containing value of the function at each (XIN,YIN) 
+;	    The number of output elements in F and PDER is identical with
+;		the number of elements in X and Y
+;
+; OPTIONAL OUTPUTS:
+;	PDER - 2 dimensional array of size (NPTS,5) giving the analytic
+;		derivative at each value of F with respect to each parameter A.
+;
+; REVISION HISTORY:
+;	Written: W. Landsman                October, 1987
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ norm = 2.506628275		;norm = sqrt(2*!pi)
+ npts = N_elements(x) 
+
+ u2 = (x[*] - a[1] + 0.5)/a[3] & u1 = (x[*] - a[1] - 0.5)/a[3]
+ v2 = (y[*] - a[2] + 0.5)/a[4] & v1 = (y[*] - a[2] - 0.5)/a[4]
+ fx = norm*a[3]*(gaussint(u2) - gaussint(u1))
+ fy = norm*a[4]*(gaussint(v2) - gaussint(v1))
+ f =  a[0]*fx*fy
+ if N_params() le 4 then return		;Need partial derivatives ?
+
+ pder = fltarr(npts,5)
+ pder[0,0] = fx*fy
+ uplus = exp(-0.5*u2^2) & uminus = exp(-0.5*u1^2)
+ pder[0,1] = a[0]*fy*(-uplus + uminus)
+ vplus = exp(-0.5*v2^2) & vminus = exp(-0.5*v1^2)
+ pder[0,2] = a[0]*fx*(-vplus + vminus)
+ pder[0,3] = a[0]*fy*(fx/a[3] + u1*uminus - u2*uplus)
+ pder[0,4] = a[0]*fx*(fy/a[4] + v1*vminus - v2*vplus)
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/date.pro b/Code/script_idl_mv/astrolib/date.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2abd07f68f248324e96c5a53e7cd88883941ef49
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/date.pro
@@ -0,0 +1,75 @@
+FUNCTION DATE,YEAR,DAY
+;+
+; NAME:
+;	DATE
+; PURPOSE:
+;	Convert day-of-year to a DD-MMM-YYYY string
+;
+; CALLING SEQUENCE:
+;	D_String = DATE(Year, day )
+;
+; INPUTS:
+;	Year - Integer scalar specifying the year.   If the year contains only
+;		two digits, then it is assumed to indicate the number of 
+;		years after 1900. 
+;
+;	Day - Integer scalar giving number of days after Jan 0 of the 
+;		specified year.    Can be larger than 366     
+;
+; OUTPUTS:
+;	D_String - String giving date in format '13-MAR-1986'
+;
+; RESTRICTIONS:
+;	Will not work for years before 100 AD 
+; EXAMPLE:
+;	IDL> print, date(1997,279)
+;		'6-Oct-1997'
+;
+; MODIFICATION HISTORY:
+;       D.M. fecit  24 October,1983
+;	Work for years outside of the 19th century  W. Landsman  September 1997
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ IF day LE 0 THEN BEGIN
+	D_String = '%DATE-F-DAY.LE.ZERO'
+ ENDIF ELSE BEGIN
+	Last_Day = [31,59,90,120,151,181,212,243,273,304,334,365]
+	LD = [0,INTARR(11)+1]
+	Day_of_Year = Day
+	Months = 'JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'
+
+; Every year that is exactly divisible by 4 is a leap year, except for years
+; that exactly divisible by 100; these centurial years are leap years only if
+; they are exactly divisible by 400.
+
+	IF Year LT 100 THEN Yr = Year + 1900 ELSE Yr = Year
+	Leap = (((Yr MOD 4) EQ 0) AND ((Yr MOD 100) NE 0)) $
+		OR ((Yr MOD 400) EQ 0)
+	N_Days = 365 + Leap
+
+	WHILE Day_of_Year GT N_Days DO BEGIN
+		Day_of_Year = Day_of_Year - N_Days
+		Yr = Yr + 1
+		Leap = (((Yr MOD 4) EQ 0) AND ((Yr MOD 100) NE 0)) $
+			OR ((Yr MOD 400) EQ 0)
+		N_Days = 365 + Leap
+	END
+
+	End_Date = '-' + STRTRIM(YR,2)
+
+	IF Leap THEN Last_Day = Last_Day + LD
+	Last_Month = Day_of_Year LE Last_Day
+	Where_LD = WHERE(Last_Month, N_Month)
+
+	IF N_Month EQ 12 THEN BEGIN
+		D_String = STRTRIM(Day_of_Year,2) + '-JAN' + End_Date
+	ENDIF ELSE BEGIN
+		LAST_Month = Where_LD[0]
+		Month = STRMID(Months,3*Last_Month,3)
+		Day_of_Month = Day_of_Year - Last_Day[Last_Month-1]
+		D_String = STRTRIM(Day_of_Month,2) + '-' + Month + End_Date
+	END
+ END
+
+ RETURN,D_String
+ END
diff --git a/Code/script_idl_mv/astrolib/date_conv.pro b/Code/script_idl_mv/astrolib/date_conv.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e34a46d70031c7c59fb167f697bd5d2fb546bbc1
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/date_conv.pro
@@ -0,0 +1,449 @@
+function date_conv,date,type, BAD_DATE = bad_date
+;+
+; NAME:
+;     DATE_CONV
+; PURPOSE:
+;     Procedure to perform conversion of dates to one of three possible formats.
+;
+; EXPLANATION:
+;     The following date formats are allowed
+;
+;       format 1: real*8 scalar encoded as:
+;               year*1000 + day + hour/24. + min/24./60 + sec/24./60/60
+;               where day is the day of year (1 to 366)
+;       format 2: Vector encoded as:
+;               date[0] = year (eg. 2005)
+;               date[1] = day of year (1 to 366)
+;               date[2] = hour
+;               date[3] = minute
+;               date[4] = second
+;               To indicate a date only, set a negative hour.
+;       format 3: string (ascii text) encoded as
+;               DD-MON-YEAR HH:MM:SS.SS
+;               (eg.  14-JUL-2005 15:25:44.23)
+;            OR
+;               YYYY-MM-DD HH:MM:SS.SS  (ISO standard)
+;               (eg.  1987-07-14 15:25:44.23 or 1987-07-14T15:25:44.23)
+;
+;            OR 
+;               DD/MM/YY (pre-2000 option for FITS DATE keywords)
+;            Time of day segment is optional in all of these.
+;       
+;       format 4: three element vector giving spacecraft time words
+;       from a Hubble Space Telescope (HST) telemetry packet.   Based on
+;       total number of secs since midnight, JAN. 1, 1979
+;
+;       format 5: Julian day. As this is also a scalar, like format 1, 
+;       	the distinction between the two on input is made based on their
+;       	value. Numbers > 2300000 are interpreted as Julian days.
+;
+; CALLING SEQUENCE
+;       results = DATE_CONV( DATE, TYPE )
+;
+; INPUTS:
+;       DATE - input date in one of the possible formats. Must be scalar.
+;       TYPE - type of output format desired.  If not supplied then
+;               format 3 (real*8 scalar) is used.
+;                       valid values:
+;                       'REAL'  - format 1
+;                       'VECTOR' - format 2
+;                       'STRING' - format 3
+;                       'FITS' - YYYY-MM-DDTHH:MM:SS.SS'
+;                       'JULIAN' - Julian date
+;                       'MODIFIED' - Modified Julian date (JD-2400000.5)
+;               TYPE can be abbreviated to the single character strings 'R',
+;               'V', 'S', 'F', 'J', and 'M'.
+;               Nobody wants to convert TO spacecraft time (I hope!)
+; OUTPUTS:
+;       The converted date is returned as the function value.
+;       Output is -1 if date is unrecognisable. 
+;
+;       If the time of day is omitted from the input, it will also
+;       be omitted from any output string (format STRING or FITS). 
+;       Note that date-only strings are allowed by the FITS standard. 
+;       For other output formats any missing time of day is set to 
+;       00:00:00.0
+;
+; KEYWORD OUTPUTS
+;
+;        BAD_DATE set to 1B if date is unrecognisable
+;
+; EXAMPLES:
+;       IDL> print,date_conv('2006-03-13 19:58:00.00'),f='(f15.5)' 
+;             2006072.83194 
+;       IDL> print,date_conv( 2006072.8319444d,'F')
+;             2006-03-13T19:58:00.00
+;       IDL> print,date_conv( 2006072.8319444d,'V')
+;             2006.00      72.0000      19.0000      57.0000      59.9962
+;       IDL> print,date_conv( 2006072.8319444d,'J'), f='(f15.5)'
+;             2453808.33194
+;
+;
+; HISTORY:
+;      version 1  D. Lindler  July, 1987
+;      adapted for IDL version 2  J. Isensee  May, 1990
+;      Made year 2000 compliant; allow ISO format input  jls/acc Oct 1998
+;      DJL/ACC Jan 1998, Modified to work with dates such as 6-JAN-1996 where
+;               day of month has only one digit.
+;      DJL, Nov. 2000, Added input/output format YYYY-MM-DDTHH:MM:SS.SS
+;      Replace spaces with '0' in output FITS format  W.Landsman April 2006
+;      Added Julian date capabilities on input and output.  M.Perrin, July 2007
+;      Removed spurious /WARN keyword to MESSAGE W.L. Feb 2012
+;      ...and another /WARN; added BAD_DATE, drop spurious time-of-day
+;      output from strings. J. P. Leahy July 2013
+;      changed all /CONTINUE warning messages to /INFO: can be suppressed 
+;      by setting !QUIET = 1.  J. P. Leahy July 2013
+;-
+;-------------------------------------------------------------
+;
+compile_opt idl2
+; data declaration
+;
+days = [0,31,28,31,30,31,30,31,31,30,31,30,31]
+months = ['   ','JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT',$
+        'NOV','DEC']
+;
+; set default type if not supplied
+;
+if N_params() lt 2 then type = 'REAL'
+;
+; Determine type of input supplied
+;
+s = size(date) & ndim = s[0] & datatype = s[ndim+1]
+if ndim gt 0 then begin                 ;vector?
+        if ndim gt 1 then goto,notvalid
+        if (s[1] ne 5) && (s[1] ne 3) then goto,notvalid
+        if (s[1] eq 5) then form = 2 else form = 4
+   end else begin                       ;scalar input
+        if datatype eq 0 then goto,notvalid
+        if datatype eq 7 then form = 3 $        ;string
+                         else form = 1  ;numeric scalar
+end
+;
+;      -----------------------------------
+;
+;*** convert input to year,day,hour,minute,second
+;
+;      -----------------------------------
+case form of
+
+        1: begin                                        ;real scalar
+			; The 'real' input format may be interpreted EITHER
+			; a) if < 2300000
+			;    as the traditional 'real*8 encoded' format used by date_conv
+			; b) if > 2300000
+			;    as a Julian Day Number
+                idate = long(date)
+                year = long(idate/1000)
+
+				if year lt 2300 then begin
+					
+					; if year is only 2 digits, assume 1900
+	                if year lt 100 then begin
+	                   message,/INF, $
+	                     'Warning: Year specified is only 2 digits, assuming 19xx'
+	                   year=1900+year
+	                   idate=1900000+idate
+	                   date=1900000.+date
+	                end
+	                day = idate - year*1000
+	                fdate = date-idate
+	                fdate = fdate*24.
+	                hour = fix(fdate)
+	                fdate = (fdate-hour)*60.0
+	                minute = fix(fdate)
+	                sec = float((fdate-minute)*60.0)
+
+				endif else begin
+					daycnv, date, year, mn, mndy, hr
+					; convert from month/day to day of year
+					; how many days PRECEED the start of each month?
+					YDAYS = [0,31,59,90,120,151,181,212,243,273,304,334,366] 
+					LEAP =  (((YeaR MOD 4) EQ 0) AND ((YeaR MOD 100) NE 0)) OR $
+					                 ((YeaR MOD 400) EQ 0)
+			        IF LEAP THEN YDAYS[2:*] = YDAYS[2:*] + 1
+					day = ydays[mn-1]+mndy
+					
+					hour = fix(hr)
+					fmin = (hr-hour)*60
+					minute = fix(fmin)
+					sec = float((fmin-minute)*60)
+				endelse
+           end
+
+        2: begin                                        ;vector
+                year = fix(date[0])
+;
+; if year is only 2 digits, assume 1900
+;
+                if year lt 100 then begin
+                   message,/INF, $
+                    'Warning: Year specified is only 2 digits, assuming 19xx'
+                   year=1900+year
+                end
+;
+                day = fix(date[1])
+                hour = fix(date[2])
+                minute = fix(date[3])
+                sec = float(date[4])
+           end
+
+        3: begin                                        ;string
+                temp = date
+;
+; check for old type of date, DD-MMM-YYYY
+;
+                test = STRPOS(temp,'-')            
+                if test ge 0 && test le 2 then begin
+                  day_of_month = fix(gettok(temp,'-'))
+                  month_name = gettok(temp,'-')
+                  year = fix(gettok(temp,' '))
+;
+; determine month number from month name
+;
+                  month_name = strupcase(month_name)
+                  for mon = 1,12 do begin
+                        if month_name eq months[mon] then goto,found
+                  end
+                  message,/INFORMATIONAL, 'Invalid month name specified'
+                  goto, notvalid
+;
+; check for new type of date, ISO: YYYY-MM-DD
+;
+                end else if strpos(temp,'-') eq 4 then begin
+                  year = fix(gettok(temp,'-'))
+                  month_name = gettok(temp,'-')
+                  mon= FIX(month_name)
+                  day_of_month=gettok(temp,' ')
+                  if strlen(temp) eq 0 then begin
+                        dtmp=gettok(day_of_month,'T')
+                        temp=day_of_month
+                        day_of_month=dtmp
+                  end
+                  day_of_month=fix(day_of_month)
+;
+; check for DD/MM/YY
+;
+                end else if STRPOS(temp,'/') eq 2 then begin
+                  day_of_month = FIX(gettok(temp,'/'))
+                  mon = FIX(gettok(temp,'/'))
+                  year = 1900 + FIX(STRMID(temp,0,2))
+                end else goto, notvalid
+                
+    found:
+                hour = gettok(temp,':')
+                hour =  hour NE '' ? FIX(hour) : -1
+                minute = fix(gettok(temp,':'))
+                sec = float(strtrim(strmid(temp,0,5)))
+             
+                IF (mon LT 1 || mon GT 12) THEN BEGIN
+                    MESSAGE, /INFORMATIONAL, 'Invalid month specified'
+                    goto, notvalid
+                ENDIF
+;
+; if year is only 2 digits, assume 1900
+;
+                if year lt 100 then begin
+                   message,/INFORMATIONAL, $ 
+                     'Warning: Year specified is only 2 digits, assuming 19xx'
+                   year=1900+year
+                end
+;
+;
+;            convert to day of year from month/day_of_month
+;
+;            correction for leap years
+;
+;               if (fix(year) mod 4) eq 0 then days(2) = 29     ;add one to february
+                lpyr = ((year mod 4) eq 0) and ((year mod 100) ne 0) $
+                        or ((year mod 400) eq 0)
+                if lpyr eq 1 then days[2] = 29 ; if leap year, add day to Feb.
+;
+;
+;            compute day of year
+;
+                  day = fix(total(days[0:mon-1])+day_of_month)
+           end
+
+        4 : begin                       ;spacecraft time
+                SC = DOUBLE(date)
+                SC = SC + (SC LT 0.0)*65536.    ;Get rid of neg. numbers 
+;
+;            Determine total number of secs since midnight, JAN. 1, 1979
+;
+                SECS = SC[2]/64 + SC[1]*1024 + SC[0]*1024*65536.
+                SECS = SECS/8192.0D0            ;Convert from spacecraft units 
+;
+;            Determine number of years 
+;
+                MINS = SECS/60.
+                HOURS = MINS/60.
+                TOTDAYS = HOURS/24.
+                YEARS = TOTDAYS/365.
+                YEARS = FIX(YEARS)
+;
+;            Compute number of leap years past 
+;
+                LEAPYEARS = (YEARS+2)/4
+;
+;           Compute day of year 
+;
+                DAY = FIX(TOTDAYS-YEARS*365.-LEAPYEARS)
+;
+;           Correct for case of being right at end of leapyear
+;
+                IF DAY LT 0 THEN BEGIN
+                  DAY = DAY+366
+                  LEAPYEARS = LEAPYEARS-1
+                  YEARS = YEARS-1
+                END
+;
+;            COMPUTE HOUR OF DAY
+;
+                TOTDAYS = YEARS*365.+DAY+LEAPYEARS
+                HOUR = FIX(HOURS - 24*TOTDAYS)
+                TOTHOURS = TOTDAYS*24+HOUR
+;
+;            COMPUTE MINUTE
+;
+                MINUTE = FIX(MINS-TOTHOURS*60)
+                TOTMIN = TOTHOURS*60+MINUTE
+;
+;            COMPUTE SEC
+;
+                SEC = SECS-TOTMIN*60
+;
+;            COMPUTE ACTUAL YEAR
+;
+                YEAR = YEARS+79
+;
+; if year is only 2 digits, assume 1900
+;
+                if year lt 100 then begin
+                   message, /INF, $ 
+                     'Warning: Year specified is only 2 digits, assuming 19xx'
+                   year=1900+year
+                end
+;
+;
+;            START DAY AT ONE AND NOT ZERO
+;
+                DAY++
+           END
+ENDCASE
+;
+;            correction for leap years
+;
+        if form ne 3 then begin         ;Was it already done?
+           lpyr = ((year mod 4) eq 0) && ((year mod 100) ne 0) $
+                || ((year mod 400) eq 0)
+           if lpyr eq 1 then days[2] = 29 ; if leap year, add day to Feb.
+        end
+;
+;            check for valid day
+;
+        if (day lt 1) || (day gt total(days)) then begin
+            message, /INFORMATIONAL, $
+                'ERROR -- There are only ' + strtrim(fix(total(days)),2) + $
+	              ' days  in year '+strtrim(year,2)
+            goto, notvalid
+        endif    
+;
+;            find month which day occurs
+;
+        day_of_month = day
+        month_num = 1
+        while day_of_month gt days[month_num] do begin
+               day_of_month = day_of_month - days[month_num]
+               month_num = month_num+1
+        end
+;           ---------------------------------------
+;
+;   *****       Now convert to output format
+;
+;           ---------------------------------------
+;
+; is type a string
+;
+s = size(type)
+if (s[0] ne 0) or (s[1] ne 7) then $
+        message,'ERROR - Output type specification must be a string'
+;
+outcode = STRMID(STRUPCASE(type),0,1)
+IF (outcode EQ 'S' || outcode EQ 'F') && hour GE 0 THEN BEGIN
+    xsec = strmid(string(sec+100,'(f6.2)'),1,5)
+    if xsec EQ '60.00' then begin
+        minute = minute+1
+        xsec = '00.00'
+    endif
+    xminute =   string(minute,'(i2.2)')
+    if xminute EQ '60' then begin
+        hour = hour+1
+        xminute = '00'                  
+    endif          
+    tod = string(hour,'(i2.2)') +  ':' +xminute + ':'+ xsec
+ENDIF
+ 
+case outcode of
+
+        'V' : begin                             ;vector output
+                out = fltarr(5)
+                out[0] = year
+                out[1] = day
+                out[2] = hour > 0
+                out[3] = minute
+                out[4] = sec
+             end
+ 
+        'R' : begin                             ;floating point scalar
+;               if year gt 1900 then year = year-1900
+                out = sec/24.0d0/60./60. + minute/24.0d0/60. $
+                + (hour > 0)/24.0d0  +  day + year*1000d0
+              end
+
+        'S' : begin                             ;string output 
+
+                month_name = months[month_num]
+;
+;            encode into ascii_date
+;
+                out = string(day_of_month,'(i2)') +'-'+ month_name +'-' + $
+                        string(year,'(i4)')
+                        
+  ; Omit time of day from output string if not specified on input
+                IF hour GE 0 THEN out += ' '+tod
+           end
+        'F' : begin
+                out = string(year,'(i4)')+'-'+string(month_num,'(I2.2)') $
+                      + '-' +  string(day_of_month,'(i2.2)')
+                IF hour GE 0 THEN out += 'T' + tod             
+           end
+
+		'J' : begin	; Julian Date
+				ydn2md, year, day, mn, dy
+				juldate, [year, mn, dy, hour, minute, sec], rjd
+				out = rjd+2400000   ; convert from reduced to regular JD
+			  end
+		'M' : begin ; Modified Julian Date = JD - 2400000.5
+				ydn2md, year, day, mn, dy
+				juldate, [year, mn, dy, hour, minute, sec], rjd
+				out = rjd-0.5   ; convert from reduced to modified JD
+			  end
+
+        else: begin                     ;invalid type specified
+                print,'DATE_CONV-- Invalid output type specified'
+                print,' It must be ''REAL'', ''STRING'', ''VECTOR'', ''JULIAN'', ''MODIFIED'', or ''FITS''.'
+                return,-1
+              end
+endcase
+
+bad_date = 0B
+return,out
+;
+; invalid input date error section
+;
+NOTVALID:
+bad_date = 1B
+message, 'Invalid input date specified', /INFORMATIONAL
+return, -1
+end
diff --git a/Code/script_idl_mv/astrolib/daycnv.pro b/Code/script_idl_mv/astrolib/daycnv.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d0f79583ace4f5a20c7c11728e23fa14f711c004
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/daycnv.pro
@@ -0,0 +1,73 @@
+PRO DAYCNV, XJD, YR, MN, DAY, HR
+;+
+; NAME:
+;       DAYCNV
+; PURPOSE:
+;       Converts Julian dates to Gregorian calendar dates
+;
+; EXPLANATION:
+;       Duplicates the functionality of the intrinsic JUL2GREG procedure
+;       which was introduced in V8.2.1
+; CALLING SEQUENCE:
+;       DAYCNV, XJD, YR, MN, DAY, HR
+;
+; INPUTS:
+;       XJD = Julian date, positive double precision scalar or vector
+;
+; OUTPUTS:
+;       YR = Year (Integer)
+;       MN = Month (Integer)
+;       DAY = Day (Integer)
+;       HR = Hours and fractional hours (Real).   If XJD is a vector,
+;               then YR,MN,DAY and HR will be vectors of the same length.
+;
+; EXAMPLE:
+;       IDL> DAYCNV, 2440000.D, yr, mn, day, hr    
+;
+;       yields yr = 1968, mn =5, day = 23, hr =12.   
+;
+; WARNING:
+;       Be sure that the Julian date is specified as double precision to
+;       maintain accuracy at the fractional hour level.
+;
+; METHOD:
+;       Uses the algorithm of Fliegel and Van Flandern (1968) as reported in
+;       the "Explanatory Supplement to the Astronomical Almanac" (1992), p. 604
+;       Works for all Gregorian calendar dates with XJD > 0, i.e., dates after
+;       -4713 November 23.
+; REVISION HISTORY:
+;       Converted to IDL from Yeoman's Comet Ephemeris Generator, 
+;       B. Pfarr, STX, 6/16/88
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() lt 2 then begin
+    print,"Syntax - DAYCNV, xjd, yr, mn, day, hr'
+    print,'  Julian date, xjd, should be specified in double precision'
+    return
+ endif
+
+; Adjustment needed because Julian day starts at noon, calendar day at midnight
+
+ jd = long(xjd)                         ;Truncate to integral day
+ frac = double(xjd) - jd + 0.5          ;Fractional part of calendar day
+ after_noon = where(frac ge 1.0, Next)
+ if Next GT 0 then begin                ;Is it really the next calendar day?
+      frac[after_noon] = frac[after_noon] - 1.0
+      jd[after_noon] = jd[after_noon] + 1
+ endif
+ hr = frac*24.0
+ l = jd + 68569
+ n = 4*l / 146097l
+ l = l - (146097*n + 3l) / 4
+ yr = 4000*(l+1) / 1461001
+ l = l - 1461*yr / 4 + 31        ;1461 = 365.25 * 4
+ mn = 80*l / 2447
+ day = l - 2447*mn / 80
+ l = mn/11
+ mn = mn + 2 - 12*l
+ yr = 100*(n-49) + yr + l
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/db_ent2ext.pro b/Code/script_idl_mv/astrolib/db_ent2ext.pro
new file mode 100644
index 0000000000000000000000000000000000000000..987424df45a24fb687a0d1294e0d39dac0c26559
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/db_ent2ext.pro
@@ -0,0 +1,121 @@
+	PRO DB_ENT2EXT, ENTRY
+;+
+; NAME:
+;	DB_ENT2EXT
+; PURPOSE:
+;	Convert a database entry to external (IEEE) data format
+; EXPLANATION: 
+;	Converts a database entry to external (IEEE) data format prior to
+;	writing it.  Called from DBWRT.
+;
+; CALLING SEQUENCE:
+;	DB_ENT2EXT, ENTRY
+;
+; INPUTS:
+;	ENTRY	= Byte array containing a single record to be written to the
+;		  database file.
+;
+; OUTPUTS:
+;	ENTRY	= The converted array is returned in place of the input array.
+;
+; COMMON BLOCKS:
+;	DB_COM
+;
+; HISTORY:
+;	Version 1, William Thompson, GSFC/CDS (ARC), 1 June 1994
+;	Version 2, William Thompson, GSFC/CDS (ARC), 15 September 1995
+;			Fixed bug where only the first element in a
+;			multidimensional array was converted.
+;       Version 2.1 W. Landsman August 2010 Fix for multidimensional strings
+;       Version 2.2 W. Landsman Sep 2011 Work with new DB format
+;-
+;
+	ON_ERROR,2
+        COMPILE_OPT IDL2
+;
+;
+; QDB[*,i] contains the following for each data base opened
+;
+;	bytes
+;	  0-18   data base name character*19
+;	  19-79  data base title character*61
+;	  80-81  number of items (integer*2)
+;	  82-83  record length of DBF file (integer*2)
+;	  84-87  number of entries in file (integer*4)
+;	  88-89  position of first item for this file in QITEMS (I*2)
+;	  90-91  position of last item for this file (I*2)
+;	  92-95  Last Sequence number used (item=SEQNUM) (I*4)
+;	  96	 Unit number of .DBF file
+;	  97	 Unit number of .dbx file (0 if none exists)
+;	  98-99  Index number of item pointing to this file (0 for first db)
+;	  100-103 Number of entries with space allocated
+;	  104	 Update flag (0 open for read only, 1 open for update)
+;	  119	 True if database is in external (IEEE) data format
+;
+;  QITEMS[*,i] contains description of item number i with following
+;  byte assignments:
+;
+;	0-19	item name (character*20)
+;	20-21   IDL data type (integet*2)
+;	22-23 	Number of values for item (1 for scalar) (integer*2)
+;	24-25	Starting byte position in original DBF record (integer*2)
+;	26-27	Number of bytes per data value (integer*2)
+;	28	Index type
+;	29-97	Item description
+;	98-99	Print field length
+;	100	Flag set to one if pointer item
+;	101-119 Data base this item points to
+;	120-125 Print format
+;	126-170 Print headers
+;	171-172 Starting byte in record returned by DBRD
+;	173-174 Data base number in QDB
+;	175-176 Data base number this item points to
+;
+;
+; QLINK[i] contains the entry number in the second data base
+;	corresponding to entry i in the first data base.
+;
+	COMMON DB_COM,QDB,QITEMS,QLINK
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 1 THEN MESSAGE, 'Syntax:  DB_ENT2EXT, ENTRY'
+;
+;  Get some information on the data base.
+;
+	LEN = DB_INFO( 'LENGTH', 0 )		;Record length
+	N_ITEMS = DB_INFO( 'ITEMS', 0 )		;Number of items
+;
+;  Determine if ENTRY is correct.
+;
+	S = SIZE(ENTRY)
+	IF S[0] NE 1 THEN MESSAGE, 'ENTRY must be a 1-dimensional array'
+	IF S[1] NE LEN THEN MESSAGE,	$
+		'ENTRY not the proper length of ' + STRTRIM(LEN,2) + ' bytes'
+	IF S[2] NE 1 THEN MESSAGE, 'ENTRY must be a byte array'
+;
+;  Extract information about the individual items.
+;
+         newdb = qdb[118, 0]
+        
+	IDLTYPE = FIX(QITEMS[20:21,*],0,N_ITEMS)
+	NVALUES = NEWDB ? LONG(QITEMS[179:182,*],0,N_ITEMS) : $
+	                  FIX(QITEMS[22:23,*],0,N_ITEMS)
+	SBYTE	= NEWDB ? LONG(QITEMS[183:186,*],0,N_ITEMS) : $
+	                  FIX(QITEMS[24:25,*],0,N_ITEMS)
+	NBYTES	= FIX(QITEMS[26:27,*],0,N_ITEMS)*NVALUES
+        BSWAP =  (IDLTYPE NE 7) AND (IDLTYPE NE 1)
+;
+;  For each entry, convert the data into external format.
+;
+	FOR I = 0, N_ITEMS-1 DO BEGIN	      
+	    IF BSWAP[I] THEN BEGIN
+	    
+		ITEM = DBXVAL(ENTRY,IDLTYPE[I],NVALUES[I],SBYTE[I],NBYTES[I])
+		SWAP_ENDIAN_INPLACE, ITEM, /SWAP_IF_LITTLE
+		DBXPUT, ITEM, ENTRY, IDLTYPE[I], SBYTE[I], NBYTES[I]
+	    ENDIF	
+	ENDFOR
+;
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/db_ent2host.pro b/Code/script_idl_mv/astrolib/db_ent2host.pro
new file mode 100644
index 0000000000000000000000000000000000000000..522495028b2a18239b62381550bd3becce0bb566
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/db_ent2host.pro
@@ -0,0 +1,134 @@
+	PRO DB_ENT2HOST, ENTRY, DBNO
+;+
+; NAME:
+;	DB_ENT2HOST
+; PURPOSE:
+;	Converts a database entry from external data format to host format.
+; EXPLANATION:
+;	All items are extracted from the entry, and then converted to host 
+;	format, and placed back into the entry.  Called from DBRD and DBEXT_DBF.
+;
+; CALLING SEQUENCE:
+;	DB_ENT2HOST, ENTRY, DBNO
+;
+; INPUTS:
+;	ENTRY	= Byte array containing a single record read from the
+;		  database file.
+;	DBNO	= Number of the opened database file.
+;
+; OUTPUTS:
+;	ENTRY	= The converted array is returned in place of the input array.
+;
+; COMMON BLOCKS:
+;	DB_COM
+;
+; HISTORY:
+;	Version 1, William Thompson, GSFC/CDS (ARC), 1 June 1994
+;	Version 2, William Thompson, GSFC/CDS (ARC), 15 September 1995
+;			Fixed bug where only the first element in a
+;			multidimensional array was converted.
+;	Version 3, Richard Schwartz, GSFC/SDAC, 23 August 1996
+;		Allow 2 dimensional byte arrays for entries to facilitate 
+;		multiple entry processing.    Pass IDLTYPE onto IEEE_TO_HOST
+;       Version 4, 2 May 2003, W. Thompson
+;               Use BSWAP keyword to DBXVAL instead of calling IEEE_TO_HOST.
+;       Version 4.1 W. Landsman August 2010 Fix for multidimensional strings
+;       Version 4.2 W. Landsman Sep 2011 Work with new DB format
+;-
+;
+	ON_ERROR,2
+	COMPILE_OPT IDL2
+;
+;
+; QDB[*,i] contains the following for each data base opened
+;
+;	bytes
+;	  0-18   data base name character*19
+;	  19-79  data base title character*61
+;	  80-81  number of items (integer*2)
+;	  82-83  record length of DBF file (integer*2)
+;	  84-87  number of entries in file (integer*4)
+;	  88-89  position of first item for this file in QITEMS (I*2)
+;	  90-91  position of last item for this file (I*2)
+;	  92-95  Last Sequence number used (item=SEQNUM) (I*4)
+;	  96	 Unit number of .DBF file
+;	  97	 Unit number of .dbx file (0 if none exists)
+;	  98-99  Index number of item pointing to this file (0 for first db)
+;	  100-103 Number of entries with space allocated
+;	  104	 Update flag (0 open for read only, 1 open for update)
+;	  119	 True if database is in external (IEEE) data format
+;
+;  QITEMS[*,i] contains description of item number i with following
+;  byte assignments:
+;
+;	0-19	item name (character*20)
+;	20-21   IDL data type (integet*2)
+;	22-23 	Number of values for item (1 for scalar) (integer*2)
+;	24-25	Starting byte position in original DBF record (integer*2)
+;	26-27	Number of bytes per data value (integer*2)
+;	28	Index type
+;	29-97	Item description
+;	98-99	Print field length
+;	100	Flag set to one if pointer item
+;	101-119 Data base this item points to
+;	120-125 Print format
+;	126-170 Print headers
+;	171-172 Starting byte in record returned by DBRD
+;	173-174 Data base number in QDB
+;	175-176 Data base number this item points to
+;
+;
+; QLINK[i] contains the entry number in the second data base
+;	corresponding to entry i in the first data base.
+;
+	COMMON DB_COM,QDB,QITEMS,QLINK
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 2 THEN MESSAGE, 'Syntax:  DB_ENT2HOST, ENTRY, DBNO'
+;
+;  Get some information on the data base.
+;
+	LEN = DB_INFO( 'LENGTH', DBNO )		;Record length
+	N_ITEMS = DB_INFO( 'ITEMS', DBNO )	;Number of items
+;
+;  Determine if ENTRY is correct.
+;
+	S = SIZE(ENTRY)
+	IF S[0] GT 2 THEN MESSAGE, 'ENTRY must be a 1 or 2-dimensional array'
+	IF S[1] NE LEN THEN MESSAGE,	$
+		'ENTRY not the proper length of ' + STRTRIM(LEN,2) + ' bytes'
+	IF S[2] NE 1 THEN MESSAGE, 'ENTRY must be a byte array'
+;
+;  Find out which items belong to the database given by DBNO.
+;
+	N = (SIZE(QITEMS))[2]	;Number of items in combined database.
+	DB_NUM	= FIX(QITEMS[173:174,*],0,N)
+	W = WHERE(DB_NUM EQ DBNO, COUNT)
+	IF COUNT NE N_ITEMS THEN MESSAGE,	$
+		'Database inconsistency--problem with number of items'
+;
+;  Extract information about the individual items.
+;
+	newdb = qdb[118, 0]
+	IDLTYPE = FIX(QITEMS[20:21,*],0,N)  &  IDLTYPE = IDLTYPE[W]
+	NVALUES = NEWDB ? LONG(QITEMS[179:182,*],0,N) : $
+	                  FIX(QITEMS[22:23,*],0,N)  &  NVALUES = NVALUES[W]
+	SBYTE	= NEWDB ?  LONG(QITEMS[183:186,*],0,N) : $
+	                   FIX(QITEMS[24:25,*],0,N)  &  SBYTE	 = SBYTE[W]
+	NBYTES	= FIX(QITEMS[26:27,*],0,N)  &  NBYTES	 = NBYTES[W]
+	BSWAP =  (IDLTYPE NE 7) AND (IDLTYPE NE 1)
+;
+;  For each entry, convert the data into external format.
+;
+	FOR I = 0, N_ITEMS-1 DO BEGIN
+		NB = NBYTES[I]*NVALUES[I]
+		ITEM = DBXVAL(ENTRY,IDLTYPE[I],NVALUES[I],SBYTE[I],NB,$
+			BSWAP = BSWAP[I])
+
+		DBXPUT, ITEM, ENTRY, IDLTYPE[I], SBYTE[I], NB
+	ENDFOR
+
+;
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/db_info.pro b/Code/script_idl_mv/astrolib/db_info.pro
new file mode 100644
index 0000000000000000000000000000000000000000..77d0dd88991a5b26c3b4c65e065391486ccd7785
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/db_info.pro
@@ -0,0 +1,218 @@
+function db_info,request,dbname
+;+
+; NAME:
+;       DB_INFO
+; PURPOSE:
+;       Function to obtain information on opened data base file(s)
+;
+; CALLING SEQUENCES:
+;       1)  result = db_info(request)
+;       2)  result = db_info(request,dbname)
+; INPUTS (calling sequence 1):
+;
+;       request - string specifying requested value(s)
+;               value of request          value returned in result
+;                       'open'          Flag set to 1 if data base(s) are opened
+;                       'number'        Number of data base files opened
+;                       'items'         Total number of items (all db's opened)
+;                       'update'        update flag (1 if opened for update)
+;                       'unit_dbf'      Unit number of the .dbf files
+;                       'unit_dbx'      Unit number of the .dbx files
+;                       'entries'       Number of entries in the db's
+;                       'length'        Record lengths for the db's
+;                       'external'      True if the db's are in external format
+;
+; INPUTS (calling sequence 2):
+;
+;       request - string specifying requested value(s)
+;                  value of request       value returned in result
+;                       'name'          Name of the data base
+;                       'number'        Sequential number of the db
+;                       'items'         Number of items for this db
+;                       'item1'         Position of item1 for this db
+;                                       in item list for all db's
+;                       'item2'         Position of last item for this db.
+;                       'pointer'       Number of the item which points
+;                                       to this db. 0 for first or primary
+;                                       db.  -1 if link file pointers.
+;                       'length'        Record length for this db.
+;                       'title'         Title of the data base
+;                       'unit_dbf'      Unit number of the .dbf file
+;                       'unit_dbx'      Unit number of the .dbx file
+;                       'entries'       Number of entries in the db
+;                       'seqnum'        Last sequence number used
+;                       'alloc'         Allocated space (# entries)
+;                       'update'        1 if data base opened for update
+;                       'external'      True if data base in external format
+;                       'newdb'         True if new (post Oct 2010) format 
+;                                       that allows entries > 32767 bytes
+;
+;       dbname - data base name or number
+; OUTPUTS:
+;       Requested value(s) are returned as the function value.
+;
+; HISTORY:
+;       version 1  D. Lindler    Oct. 1987
+;       changed type from 1 to 7 for IDLV2, J. Isensee, Nov., 1990
+;       William Thompson, GSFC/CDS (ARC), 30 May 1994
+;               Added EXTERNAL request type.
+;       Support new DB format, add NEWDB request type W. Landsman Oct 2010
+;-
+;------------------------------------------------------------------------
+on_error,2                       ;Return to caller
+;
+; data base common block
+;
+common db_com,QDB,QITEMS,QLINK
+;
+; QDB[*,i] contains the following for each data base opened
+;
+;       bytes
+;         0-18   data base name character*19
+;         19-79  data base title character*61
+;         80-81  number of items (integer*2)
+;         82-83  record length of DBF file (integer*2), old format
+;         84-87  number of entries in file (integer*4)
+;         88-89  position of first item for this file in QITEMS (I*2)
+;         90-91  position of last item for this file (I*2)
+;         92-95  Last Sequence number used (item=SEQNUM) (I*4)
+;         96     Unit number of .DBF file
+;         97     Unit number of .dbx file (0 if none exists)
+;         98-99  Index number of item pointing to this file (0 for first db)
+;         100-103 Number of entries with space allocated
+;         104    Update flag (0 open for read only, 1 open for update)
+;         105-108  record length of DBF file (integer*4), new format
+;         119    True if database is in external (IEEE) format
+;
+;  QITEMS[*,i] contains deacription of item number i with following
+;  byte assignments:
+;
+;       0-19    item name (character*20)
+;       20-21   IDL data type (integet*2)
+;       22-23   Number of values for item (1 for scalar) (integer*2)
+;       24-25   Starting byte position in original DBF record (integer*2)
+;       26-27   Number of bytes per data value (integer*2)
+;       28      Index type
+;       29-97   Item description
+;       98-99   Print field length
+;       100     Flag set to one if pointer item
+;       101-119 Data base this item points to
+;       120-125 Print format
+;       126-170 Print headers
+;       171-172 Starting byte in record returned by DBRD, old format
+;       173-174 Data base number in QDB
+;       175-176 Data base number this item points to
+;       177-178 Item number within the specific data base
+;       179-182 Number of values for item (1 for scalar) (integer*4)
+;       183-186  Starting byte position in original DBF record (integer*4)
+;       187-190 Starting byte in record returned by DBRD
+;
+;
+; QLINK[i] contains the entry number in the second data base
+;       corresponding to entry i in the first data base.
+;-------------------------------------------------------------------------
+;
+req=strtrim(strupcase(request))         ;requested value
+s=size(qdb)
+if req eq 'OPEN' then begin
+        if s[0] eq 0 then return,0 else return,1
+end
+if s[0] eq 0 then message,'No data base file(s) opened'
+n=s[2]                                  ;number of data bases
+;
+; calling sequence 1  result=db_info(request)
+;
+newdb = qdb[118,0]
+if N_params() lt 2 then begin
+    case req of
+        'NUMBER'  : return,n                    ;number of files opened
+        'ITEMS'   : begin                       ;total number of items
+                        s=size(qitems)
+                        return,s[2]
+                    end
+        'LENGTH'  : begin
+                    len = newdb ? long( qdb[105:108,*],0,n) : $
+                                   fix(qdb[82:83,*],0,n)
+                    return,len
+                    end
+                                                ;total record length
+        'UPDATE'  : return,qdb[104,0]           ;update flag
+        'UNIT_DBF'  : return,qdb[96,*]          ;.dbf unit number
+        'UNIT_DBX'  : return,qdb[97,*]          ;.dbx unit number
+        'ENTRIES'   : return,long(qdb[84:87,*],0,n)     ;number of entries
+        'EXTERNAL'  : return,qdb[119,*] eq 1    ;external format?
+        'NEWDB'     : return,  newdb         ;New db format?                  
+        else :  message,'Invalid request for information'
+    endcase
+endif
+;
+; second calling sequence:  result=db_info(request,dbname) ----------
+;
+s=size(dbname)
+ndim=s[0]
+type=s[ndim+1]
+if (ndim gt 0) || (type eq 0) then goto,abort
+;
+; convert name to number
+;
+if type eq 7 then begin
+        db_name=strtrim(strupcase(dbname))
+        for i=0,n-1 do $
+                if db_name eq strtrim(string(qdb[0:18,i])) then goto,found
+        goto,abort                                      ;not found
+found:  dbnum=i
+   end else begin                                       ;number supplied
+        dbnum=fix(dbname)
+        if (dbnum lt 0) || (dbnum ge n) then goto,abort
+end
+newdb = qdb[118,dbnum]
+
+case req of
+        'NAME'     : return,strtrim(string(qdb[0:18,dbnum]))  ;db name
+        'NUMBER'   : return,dbnum                       ;data base number
+        'ITEMS'    : begin                              ;number of items
+                        x=fix(qdb[80:81,dbnum],0,1)
+                        return,x[0]
+                     end
+        'ITEM1'    : begin                              ;starting item number
+                        x=fix(qdb[88:89,dbnum],0,1)
+                        return,x[0]
+                     end
+        'ITEM2'    : begin                              ;last item number
+                        x=fix(qdb[90:91,dbnum],0,1)
+                        return,x[0]
+                     end
+        'POINTER'   : begin                             ;item number pointer
+                        x=fix(qdb[98:99,dbnum],0,1)
+                        return,x[0]
+                      end
+        'LENGTH'    : begin 
+                        x = newdb ? long(qdb[105:108,dbnum],0,1) : $                            ;record length
+                                   fix(qdb[82:83,dbnum],0,1)
+                      return,long(x[0])
+                      end
+        'TITLE'     : return,strtrim(string(qdb[19:79,dbnum])) ;data base title
+        'UNIT_DBF'  : return,qdb[96,dbnum]              ;.dbf unit number
+        'UNIT_DBX'  : return,qdb[97,dbnum]              ;.dbx unit number
+        'ENTRIES'   : begin                             ;number of entries
+                        x=long(qdb[84:87,dbnum],0,1)
+                        return,x[0]
+                      end
+        'SEQNUM'    : begin                             ;last sequence number
+                        x=long(qdb[92:95,dbnum],0,1)
+                        return,x[0]
+                      end
+        'ALLOC'     : begin                             ;allocated size
+                        x=long(qdb[100:103,dbnum],0,1)
+                        return,x[0]
+                      end
+        'UPDATE'    : return,qdb[104,dbnum]             ;update flag
+        'EXTERNAL'  : begin                             ;External format?
+                        x=qdb[119,*] eq 1
+                        return,x[0]
+                      end
+        'NEWDB'     :      return,  newdb         ;New db format?                  
+        else: message,'Invalid information request'
+endcase
+abort:  message,'Invalid data base name or number supplied'
+end
diff --git a/Code/script_idl_mv/astrolib/db_item.pro b/Code/script_idl_mv/astrolib/db_item.pro
new file mode 100644
index 0000000000000000000000000000000000000000..626dc071568f4b3687f94edd5bae10999433dc21
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/db_item.pro
@@ -0,0 +1,347 @@
+pro db_item,items,itnum,ivalnum,idltype,sbyte,numvals,nbytes,errmsg=errmsg
+;+
+; NAME: 
+;       DB_ITEM
+; PURPOSE:      
+;       Returns the item numbers and other info. for an item name.
+; EXPLANATION:  
+;       Procedure to return the item numbers and other information
+;       of a specified item name
+;
+; CALLING SEQUENCE:     
+;       db_item, items, itnum, ivalnum, idltype, sbyte, numvals, nbytes
+;
+; INPUTS:       
+;       items - item name or number
+;               form 1  scalar string giving item(s) as list of names
+;                       separated by commas
+;               form 2  string array giving list of item names
+;               form 3  string of form '$filename' giving name
+;                       of text file containing items (one item per
+;                       line)
+;               form 4  integer scalar giving single item number or
+;                         integer vector list of item numbers
+;               form 5  Null string specifying interactive selection
+;                       Upon return items will contain selected items
+;                       in form 1
+;               form 6  '*'     select all items
+;
+; OUTPUTS:      
+;       itnum - item number
+;       ivalnum - value(s) number from multiple valued item
+;       idltype - data type(s) (1=string,2=byte,4=i*4,...)
+;       sbyte - starting byte(s) in entry
+;       numvals - number of data values for item(s)
+;               It is the full length of a vector item unless
+;               a subscript was supplied
+;       nbytes - number of bytes for each value
+;    All outputs are vectors even if a single item is requested
+;
+; OPTIONAL INPUT KEYWORDS:      
+;       ERRMSG   = If defined and passed, then any error messages will
+;               be returned to the user in this parameter rather than depending
+;               on the MESSAGE routine in IDL.  If no errors are encountered, 
+;               then a null string is returned.  In order to use this feature, 
+;               ERRMSG must be defined first, e.g.
+;
+;                               ERRMSG = ''
+;                               DB_ITEM, ERRMSG=ERRMSG, ...
+;                               IF ERRMSG NE '' THEN ...
+;
+; PROCEDURE CALLS:
+;       DB_INFO, GETTOK, SELECT_W
+;
+; REVISION HISTORY:
+;       Written:   D. Lindler, GSFC/HRS, October 1987
+;       Version 2, William Thompson, GSFC, 17-Mar-1997
+;                       Added keyword ERRMSG
+;       Use STRSPLIT instead of GETTOK to parse form 1, W. Landsman July 2002
+;       Assume since V5.4 use FILE_EXPAND_PATH() instead of SPEC_DIR()
+;               W. Landsman April 2006
+;       Support new DB format allowing entry lengths > 32767 bytes WL Oct 2010
+;       Ignore blank lines in .items file WL February 2011
+;-
+;
+;------------------------------------------------------------------------
+ compile_opt idl2
+ On_error,2
+ if N_params() LT 2 then begin
+    print,'Syntax - DB_ITEM,items,itnum,ivalnum,idltype,sbyte,numvals,nbytes'
+    return
+ endif 
+; data base common block
+;
+common db_com,QDB,QITEMS,QLINK
+;
+; QDB[*,i] contains the following for each data base opened
+;
+;       bytes
+;         0-18   data base name character*19
+;         19-79  data base title character*61
+;         80-81  number of items (integer*2)
+;         82-83  record length of DBF file (integer*2) old DB format
+;         84-87  number of entries in file (integer*4)
+;         88-89  position of first item for this file in QITEMS (I*2)
+;         90-91  position of last item for this file (I*2)
+;         92-95  Last Sequence number used (item=SEQNUM) (I*4)
+;         96     Unit number of .DBF file
+;         97     Unit number of .dbx file (0 if none exists)
+;         98-99  Index number of item pointing to this file (0 for first db)
+;         100-103 Number of entries with space allocated
+;         104    Update flag (0 open for read only, 1 open for update)
+;         105-108  record length of DBF file (integer*4) 
+;         118    Equals 1 if database can store records larger than 32767 bytes
+;         119    Equals 1 if external data representation (IEEE) is used
+;
+;  QITEMS[*,i] contains a description of item number i with following
+;  byte assignments:
+;
+;       0-19    item name (character*20)
+;       20-21   IDL data type (integet*2)
+;       22-23   Number of values for item (1 for scalar) (integer*2)
+;       24-25   Starting byte position in original DBF record (integer*2)
+;       26-27   Number of bytes per data value (integer*2)
+;       28      Index type
+;       29-97   Item description
+;       98-99   Print field length
+;       100     Flag set to one if pointer item
+;       101-119 Data base this item points to
+;       120-125 Print format
+;       126-170 Print headers
+;       171-172 Starting byte in record returned by DBRD, old DB format
+;       173-174 Data base number in QDB
+;       175-176 Data base number this item points to
+;       177-178 Item number within the specific data base
+;       179-182 Number of values for item (1 for scalar) (integer*4)
+;       183-186 Starting byte position in original DBF record (integer*4)
+;       187-190 Starting byte in record returned by DBRD
+;
+;
+; QLINK[i] contains the entry number in the second data base
+;       corresponding to entry i in the first data base.
+;-------------------------------------------------------------------------
+if n_elements(items) eq 0 then items = ''
+;
+; check if data base open
+;
+if n_elements(qdb) lt 120 then begin
+        message = 'data base file not open'
+        goto, handle_error
+endif
+
+;
+; determine type of item list -------------------------------------------
+;
+vector=1                                        ;vector output flag
+newdb = qdb[118,0] EQ 1
+s=size(items,/str)
+ndim = s.n_dimensions
+if s.type_name eq 'STRING' then begin                     ;string(s)
+        if ndim eq 0 then begin                         ;string scalar?
+            if strtrim(items) eq '' then form=5 else $  ;null string   - form 5
+            if strmid(items,0,1) eq '$' then form=3  $  ;filename      - form 3
+                else form=1                             ;scalar list   - form 1
+            if strtrim(items) eq '*' then form=6        ;all items '*' - form 6
+         end else form=2                                ;string vector - form 2
+   end else begin                                       ;non-string
+        form=4                                          ;integer       - form 4
+end
+s=size(qitems)
+if s[0] ne 2 then begin
+        message = 'No data base opened'
+        goto, handle_error
+endif
+qnumit=s[2]
+
+;-----------------------------------------------------------------------------
+;       CONVERT INPUT ITEMS TO INTEGER LIST OR STRING LIST
+;
+;
+; Form 4 ------------------ Integer
+;
+If form eq 4 then begin
+        if ndim eq 0 then begin
+                itnum=intarr(1)+items
+                ivalnum=intarr(1)
+                ivalflag=intarr(1)
+                goto,scalar                     ;speedy method
+            end else begin
+                itnum=items
+                nitems=n_elements(itnum)
+                ivalflag=bytarr(nitems)
+                ivalnum=intarr(nitems)
+                if (min(itnum) lt 0) or (max(itnum) ge qnumit) then begin
+                        message = 'Invalid item number specified'
+                        goto, handle_error
+                endif
+                goto,vector
+        end
+end
+
+;
+; Form 3 ----------------- File name
+;
+if form eq 3 then begin
+        item_names=strarr(200)          ;input buffer
+        if strlen(items) gt 1 then filename=strmid(items,1,strlen(items)-1) $
+                               else filename=strtrim(db_info('name',0))+'.items'
+        if ~file_test(filename) then begin
+            message = 'Unable to locate file ' + FILE_EXPAND_PATH(filename) +  $
+                    ' with item list'
+            goto, handle_error
+        endif
+ 	nlines = file_lines(filename)
+        item_names = strarr(nlines)
+        openr,unit,filename,/get_lun    ;open file
+        readf,unit,item_names
+	free_lun,unit
+	item_names = strtrim(item_names,2) 
+; Remove any blank lines 	
+	good = where(strlen(item_names) GT 0, Nitems) 
+	if Nitems LT Nlines then item_names = item_names[good]	
+end
+;
+; form 1 ----------------- scalar string list  'item1,item2,item3...'
+;
+ if form eq 1 then begin
+     item_names = strsplit(items,',',/EXTRACT) 
+     nitems = N_elements(item_names)                     
+ endif
+;
+; form 2 -------------------------- string array
+;
+if form eq 2 then begin
+        item_names=items
+        nitems = N_elements(items)
+endif
+;
+; form 5 -------------------------- null string (interactive input)
+;
+if form eq 5 then begin
+        names=strtrim(qitems[0:19,*],2)
+        desc=string(qitems[29:78,*])
+        select_w,names,itnum,desc,'Select List of Items',count=count
+        if count le 0 then begin
+                message = 'No items selected'
+                goto, handle_error
+        endif
+;
+        nitems=n_elements(itnum)
+        items = strtrim(names[itnum[0]],2)
+        if nitems gt 1 then for i=1,nitems-1 do $
+                  items = items +','+strtrim(names[itnum[i]],2)
+        ivalflag=bytarr(nitems)
+        ivalnum=intarr(nitems)   
+        goto,vector
+end
+;
+; Form 4 ------------------ '*'  select all items
+;
+If form eq 6 then begin
+        nitems=db_info('items')         ;number of items
+        itnum=indgen(nitems)
+        ivalflag=bytarr(nitems)
+        ivalnum=intarr(nitems)
+        goto,vector
+end
+;
+;-------------------------------------------------------------------------
+;   CONVERT STRING LIST TO INTEGER LIST AND PULL OFF SUBSCRIPT IF SUPPLIED
+;
+;
+        names=strtrim(qitems[0:19,*],2) ;all possible item names
+        ivalnum=intarr(nitems)          ;selection of multi-value items
+        ivalflag=bytarr(nitems)         ;Flag for subscripted items
+        itnum=intarr(nitems)            ;integer item numbers
+;
+; loop on item names supplied
+;
+        for i=0,nitems-1 do begin       ;loop on items
+            st=strtrim(item_names[i],2)         ;get item
+            name=gettok(st,'(')         ;get name
+;
+;     subscript supplied
+;
+            if st ne '' then begin      ;number supplied?
+                ivalnum[i]=fix(gettok(st,')'))  ;get number
+                ivalflag[i]=1
+            end;
+;
+;     data base name supplied
+;
+            if strpos(name,'.') ge 0 then begin ;data base name supplied
+                dbname=gettok(name,'.')         ;  form is 'dbname.itemname'
+                i1=db_info('item1',dbname)      ;first item for the db
+                i2=db_info('item2',dbname)      ;last item for the db
+             end else begin                     ;search all items
+                i1=0 & i2=qnumit-1
+            end
+;
+;    search for item name
+;
+            name=strupcase(name)                ;convert to upper case
+            j = where(names[i1:i2] eq name,nmatch)
+            if nmatch eq 0 then begin
+                    message = 'Item '+ name +' is invalid'
+                    goto, handle_error
+            endif
+itnum[i] =j[0] +i1                              ;save item number
+endfor;i loop on items
+if nitems eq 1 then goto,scalar                 ;speedy method
+
+;
+;---------------------------------------------------------------------------
+;  We now have
+;       1) integer list of item numbers of length nitems
+;       2) we have list of ivalnum (subscripts) with
+;               flag(s) ivalflag if subscript supplied
+; EXTRACT OTHER PARAMETERS
+;
+
+vector:                                         ;---- vector processing
+ idltype = fix(qitems[20:21,*],0,qnumit)
+ numvals = newdb ? long(qitems[179:182,*],0,qnumit) : $
+                  fix(qitems[22:23,*],0,qnumit)
+ sbyte =  newdb ? long(qitems[187:190,*],0,qnumit) : $
+                  fix(qitems[171:172,*],0,qnumit)
+ nbytes = fix(qitems[26:27,*],0,qnumit)
+ idltype = idltype[itnum]
+ numvals = numvals[itnum]
+ sbyte = sbyte[itnum]
+ nbytes = nbytes[itnum]
+;
+; add offset for subscripted variables
+;
+sbyte=sbyte+ivalnum*nbytes
+;
+; if ivalflag is set we have subscripted item and don't want all
+;  values in vector
+;
+pos=where(ivalflag, Npos)
+if Npos GT 0 then numvals[pos]=1
+return
+;
+; -----------------------
+scalar:                                         ;------- scalar processing
+it=itnum[0]
+if (it lt 0) or (it ge qnumit) then begin
+        message = 'Invalid item number '+strtrim(it,2)+' specified'
+        goto, handle_error
+endif
+;
+idltype = fix(qitems[20:21,it],0,1)
+numvals = newdb ? long(qitems[179:182,it],0,1) : $
+                  fix(qitems[22:23,it],0,1)
+sbyte = newdb ? long(qitems[187:190,it],0,1) : $
+             fix(qitems[171:172,it],0,1)
+nbytes = fix(qitems[26:27,it],0,1)
+sbyte = sbyte+nbytes*ivalnum
+if ivalflag[0] then numvals[0]=1
+return
+;
+;  Error handling point.
+;
+HANDLE_ERROR:
+        IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = 'DB_ITEM: ' + MESSAGE $
+                ELSE MESSAGE, MESSAGE
+end
diff --git a/Code/script_idl_mv/astrolib/db_item_info.pro b/Code/script_idl_mv/astrolib/db_item_info.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1dfa2b7edf007ca9863cb353c3c83efce474cf6c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/db_item_info.pro
@@ -0,0 +1,122 @@
+function db_item_info,request,itnums
+;+
+; NAME:
+;	DB_ITEM_INFO
+; PURPOSE:
+;	routine to return information on selected item(s) in the opened
+;	data bases.
+;
+; CALLING SEQUENCE:
+;	result = db_item_info( request, itnums)
+; INPUTS:
+;	request - string giving the requested information.
+;		'name'		- item names
+;		'idltype'	- IDL data type (integers)
+;				  see documentation of intrinsic SIZE funtion
+;		'nvalues'	- vector item length (1 for scalar)
+;		'sbyte'		- starting byte in .dbf record (use bytepos
+;				  to get starting byte in record returned by
+;				  dbrd)
+;		'nbytes'	- bytes per data value
+;		'index'		- index types
+;		'description'	- description of the item
+;		'pflag'		- pointer item flags
+;		'pointer'	- data bases the items point to
+;		'format'	- print formats
+;		'flen'		- print field length
+;		'headers'	- print headers
+;		'bytepos'	- starting byte in dbrd record for the items
+;		'dbnumber'	- number of the opened data base
+;		'pnumber'	- number of db it points to (if the db is
+;					opened)
+;		'itemnumber'	- item number in the file
+;
+;	itnums -(optional) Item numbers.  If not supplied info on all items
+;		are returned.
+; OUTPUT:
+;	Requested information is returned as a vector.  Its type depends
+;	on the item requested.
+; HISTORY:
+;	version 1  D. Lindler  Nov. 1987
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Support new DB format which allows > 32767 bytes W.L. Oct 2010
+;-
+;------------------------------------------------------------------------
+; data base common block
+;               
+common db_com,QDB,QITEMS,QLINK
+;
+; QDB[*,i] contains the following for each data base opened
+;
+;	bytes
+;	  0-18   data base name character*19
+;	  19-79  data base title character*61
+;	  80-81  number of items (integer*2)
+;	  82-83  record length of DBF file (integer*2)
+;	  84-87  number of entries in file (integer*4)
+;	  88-89  position of first item for this file in QITEMS (I*2)
+;	  90-91  position of last item for this file (I*2)
+;	  92-95  Last Sequence number used (item=SEQNUM) (I*4)
+;	  96	 Unit number of .DBF file
+;	  97	 Unit number of .IND file (0 if none exists)
+;	  98-99  Index number of item pointing to this file (0 for first db)
+;	  100-103 Number of entries with space allocated
+;	  104	 Update flag (0 open for read only, 1 open for update)
+;	  119	 Equals 1 if external data representation (IEEE) is used
+;	
+;  QITEMS[*,i] contains a description of item number i with following
+;  byte assignments:
+;
+;	0-19	item name (character*20)
+;	20-21   IDL data type (integet*2)
+;	22-23 	Number of values for item (1 for scalar) (integer*2)
+;	24-25	Starting byte position in original DBF record (integer*2)
+;	26-27	Number of bytes per data value (integer*2)
+;	28	Index type
+;	29-97	Item description
+;	98-99	Print format field length
+;	100	Flag set to one if pointer item
+;	101-119 Data base this item points to
+;	120-125 Print format
+;	126-170 Print headers
+;	171-172 Starting byte in record returned by DBRD
+;	173-174 Data base number in QDB
+;	175-176 Data base number this item points to
+;	177-178 item number within file
+;       179-182 Number of values for item (1 for scalar) (integer*4)
+;       183-186   Starting byte position in original DBF record (integer*4)
+;       187-190 Starting byte in record returned by DBRD
+;
+; QLINK[i] contains the entry number in the second data base
+;	corresponding to entry i in the first data base.
+;-------------------------------------------------------------------------
+s=size(qitems) & n=s[2]
+newdb = qdb[118,0] EQ 1
+case strupcase(strtrim(request)) of
+
+	'NAME'		: x=string(qitems[0:19,*])
+	'IDLTYPE'	: x=fix(qitems[20:21,*],0,n)
+	'NVALUES'	: x = newdb? long(qitems[179:182,*],0,n) : $ 
+			             fix(qitems[22:23,*],0,n)
+	'SBYTE'		: x = newdb ? long(qitems[183:186,*],0,n) : $
+			               fix(qitems[24:25,*],0,n) 
+	'NBYTES'	: x=fix(qitems[26:27,*],0,n)
+	'INDEX'		: x=qitems[28,*]
+	'DESCRIPTION'	: x=string(qitems[29:99,*])
+	'PFLAG'		: x=qitems[100,*]
+	'POINTER'	: x=string(qitems[101:119,*])
+	'FORMAT'	: x=string(qitems[120:125,*])
+	'FLEN'		: x=fix(qitems[98:99,*],0,n)
+	'HEADERS'	: x=string(qitems[126:170,*])
+	'BYTEPOS'	: x = newdb ?  long(qitems[187:190,*],0,n) : $ 
+	                                fix(qitems[171:172,*],0,n)
+	'DBNUMBER'	: x=fix(qitems[173:174,*],0,n)
+	'PNUMBER'	: x=fix(qitems[175:176,*],0,n)
+	'ITEMNUMBER'	: x=fix(qitems[177:178,*],0,n)
+	else: begin
+		print,'DB_ITEM_INFO-- invalid information request'
+		retall
+	      end
+endcase
+if N_params() eq 1 then return,x else return,x[itnums]
+end
diff --git a/Code/script_idl_mv/astrolib/db_or.pro b/Code/script_idl_mv/astrolib/db_or.pro
new file mode 100644
index 0000000000000000000000000000000000000000..cb6cd105c21b06b7260f2ad5938d5affba242c2d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/db_or.pro
@@ -0,0 +1,52 @@
+function db_or,list1,list2
+;+
+; NAME:
+;	DB_OR
+; PURPOSE:
+;	Combine two vectors of entry numbers, removing duplicate values.
+; EXPLANATION:
+;	DB_OR can also be used to remove duplicate values from any longword 
+;	vector
+;
+; CALLING SEQUENCE:
+;	LIST = DB_OR( LIST1 )          ;Remove duplicate values from LIST1
+;		or
+;	LIST = DB_OR( LIST1, LIST2 )   ;Concatenate LIST1 and LIST2, remove dups
+;
+; INPUTS:
+;	LIST1, LIST2 - Vectors containing entry numbers, must be non-negative
+;			integers or longwords.
+; OUTPUT:
+;	LIST - Vector containing entry numbers in either LIST1 or LIST2
+;  
+; METHOD
+;	DB_OR returns where the histogram of the entry vectors is non-zero
+;
+; PROCEDURE CALLS
+;	ZPARCHECK - checks parameters  
+; REVISION HISTORY:
+;	Written,     W. Landsman             February, 1989
+;	Check for degenerate values  W.L.    February, 1993
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+  if N_params() EQ 0 then begin
+       print,'Syntax - list = db_or( list1, [ list2] )
+       return, -1
+  endif
+
+  zparcheck, 'DB_OR', list1, 1, [1,2,3], [1,2], 'First Entry Vector'
+
+  if N_params() eq 1 then begin
+       minlist1 = min( list1, max = maxlist1 )
+       if ( minlist1 EQ maxlist1 ) then return, minlist1  else $
+                   return, where( histogram( list1 ) GT 0 ) + minlist1
+  endif
+
+  zparcheck, 'DB_OR', list1, 1, [1,2,3], [1,2], 'Second Entry Vector'
+
+  list = [list1, list2]
+  minlist = min( list, max = maxlist )
+  if ( minlist EQ maxlist ) then return, minlist  else $
+                return,where( histogram( list ) GT 0 ) + minlist
+
+  end
diff --git a/Code/script_idl_mv/astrolib/db_titles.pro b/Code/script_idl_mv/astrolib/db_titles.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3cb8389ab1dcde63b84e8fc70b65f5fc93308b8d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/db_titles.pro
@@ -0,0 +1,54 @@
+pro db_titles,fnames,titles
+;+
+; NAME:
+;	DB_TITLES
+;
+; PURPOSE:
+;	Print database name and title.  Called by DBHELP
+;
+; CALLING SEQUENCE:
+;	db_titles, fnames, titles
+;
+; INPUT:
+;	fnames - string array of data base names
+;
+; SIDE EFFECT:
+;	Database name is printed along with the description in the .dbh file
+;
+; HISTORY:
+;	version 2  W. Landsman May, 1989
+;	modified to work under Unix, D. Neill, ACC, Feb 1991.
+;	William Thompson, GSFC/CDS (ARC), 1 June 1994
+;		Added support for external (IEEE) representation.
+;	William Thompson, GSFC, 3 November 1994
+;			Modified to allow ZDBASE to be a path string.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Assume since V5.5,      W. Landsman   September 2006
+;-
+;
+;-----------------------------------------------------------------------------
+ compile_opt idl2
+ n = N_elements(fnames)
+ get_lun,unit
+ b = bytarr(59)
+ npar = N_params()
+ if npar eq 2 then titles = strarr(n)
+ for i = 0,n-1 do begin
+     dbh_file = find_with_def(strtrim(fnames[i])+'.dbh', 'ZDBASE')
+     openr,unit,dbh_file,error=err
+     if err lt 0 then $               ;Does database exist?
+        printf,!TEXTUNIT,'Unable to locate database ',fnames[i] $
+ else begin
+        readu,unit,b
+        if npar eq 1 then begin
+            printf,!TEXTUNIT,format='(A,T20,A)',fnames[i],strtrim(b[19:58],2) 
+        endif else titles[i] = string(b[19:58])
+   endelse
+
+   close,unit
+
+ endfor
+
+ free_lun,unit
+ return
+end
diff --git a/Code/script_idl_mv/astrolib/dbbuild.pro b/Code/script_idl_mv/astrolib/dbbuild.pro
new file mode 100644
index 0000000000000000000000000000000000000000..58b78d11965365175840d7c2557d465aafd04cbe
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbbuild.pro
@@ -0,0 +1,168 @@
+pro dbbuild,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18, $
+    v19,v20,v21,v22,v23,v24,v25,v26,v27,v28,v29,v30,v31,v32,v33,v34,v35,v36, $
+    v37,v38,v39,v40,v41,v42,v43,v44,v45,v46,v47,v48,v49,v50, $
+    NOINDEX = noindex, STATUS=STATUS, SILENT=SILENT
+;+
+; NAME:
+;	DBBUILD
+; PURPOSE:
+;	Build a database by appending new values for every item.  
+; EXPLANATION:
+;	The database must be opened for update (with DBOPEN) before calling 
+;	DBBUILD.   
+;
+; CALLING SEQUENCE:
+;	DBBUILD, [ v1, v2, v3, v4......v50, /NOINDEX, /SILENT, STATUS =  ]
+;
+; INPUTS:
+;	v1,v2....v50 - vectors containing values for all items in the database.
+;         V1 contains values for the first item, V2 for the second, etc.
+;         The number of vectors supplied must equal the number of items
+;         (excluding entry number) in the database.  The number of elements 
+;         in each vector should be the same.   A multiple valued item
+;         should be dimensioned NVALUE by NENTRY, where NVALUE is the number
+;         of values, and NENTRY is the number of entries.
+;
+; OPTIONAL INPUT KEYWORDS:
+;	/NOINDEX - If this keyword is supplied and non-zero then DBBUILD will
+;             *not* create an indexed file.    Useful to save time if
+;             DBBUILD is to be called several times and the indexed file need
+;             only be created on the last call
+;
+;	/SILENT  - If the keyword SILENT is set and non-zero, then DBBUILD
+;	      will not print a message when the index files are generated
+;
+; OPTIONAL OUTPUT KEYWORD:
+;	STATUS - Returns a status code denoting whether the operation was
+;	      successful (1) or unsuccessful (0).  Useful when DBBUILD is
+;	      called from within other applications.
+;
+; EXAMPLE:
+;	Suppose a database named STARS contains the four items NAME,RA,DEC, and 
+;	FLUX.   Assume that one already has the four vectors containing the
+;	values, and that the database definition (.DBD) file already exists.
+;
+;	IDL> !PRIV=2                  ;Writing to database requires !PRIV=2
+;	IDL> dbcreate,'stars',1,1   ;Create database (.dbf) & index (.dbx) file
+;	IDL> dbopen,'stars',1         ;Open database for update
+;	IDL> dbbuild,name,ra,dec,flux ;Write 4 vectors into the database
+;
+; NOTES:
+;	Do not call DBCREATE before DBBUILD if you want to append entries to
+;	an existing database
+;
+;	DBBUILD checks that each value vector matches the idl type given in the
+;	database definition (..dbd) file, and that character strings are the 
+;	proper length. 
+; PROCEDURE CALLS:
+;       DBCLOSE, DBINDEX, DBXPUT, DBWRT, IS_IEEE_BIG()
+; REVISION HISTORY:
+;	Written          W. Landsman           March, 1989
+;	Added /NOINDEX keyword           W. Landsman        November, 1992
+;	User no longer need supply all items   W. Landsman  December, 1992 
+;	Added STATUS keyword, William Thompson, GSFC, 1 April 1994
+;	Added /SILENT keyword, William Thompson, GSFC, October 1995
+;	Allow up to 30 items, fix problem if first item was multiple value
+;				  W. Landsman    GSFC, July 1996
+;	Faster build of external databases on big endian machines 
+;				  W. Landsman    GSFC, November 1997  
+;       Use SIZE(/TNAME) for error mesage display  W.Landsman   July 2001
+;       Fix message display error introduced July 2001  W. Landsman   Oct. 2001 
+;       Make sure error message appears even if !QUIET is set W.L November 2006
+;       Major rewrite to use SCOPE_VARFETCH, accept 50 input items
+;                   W. Landsman    November 2006
+;      Fix warning if parameters have different # of elements W.L.  May 2010
+;      Fix warning if scalar parameter supplied W.L.  June 2010
+;      Fix for when first parameter is multi-dimensioned W.L. July 2010
+;      Check data type of first parameter W.L. Jan 2012
+;-
+  COMPILE_OPT IDL2
+  On_error,2                            ;Return to caller
+  npar = N_params()
+  if npar LT 1 then begin
+    print,'Syntax - DBBUILD, v1, [ v2, v3, v4, v5, ... v50,' 
+    print,'         /NOINDEX, /SILENT, STATUS =  ]'
+    return
+  endif
+
+ dtype = ['UNDEFINED','BYTE','INT','LONG','FLOAT','DOUBLE', $
+        'COMPLEX','STRING','STRUCT','DCOMPLEX','POINTER','OBJREF', $ 
+        'UINT', 'ULONG', 'LONG64','ULONG64']
+
+ 
+;  Initialize STATUS as unsuccessful (0).  If the routine is successful, this
+;  will be updated below.
+
+  status = 0
+
+  nitem = db_info( 'ITEMS' )
+  if nitem LE npar  then message, 'ERROR - ' + strtrim(npar,2) + $ $
+     ' variables supplied but only ' + strtrim(nitem-1,2) + ' items in database' 
+
+   items = indgen(nitem)
+   db_item, items, itnum, ivalnum, idltype, sbyte, numvals, nbyte
+   nitems = ( npar < nitem)
+   vv = 'v' + strtrim( indgen(nitems+1), 2)
+
+;Create a pointer array to point at each of the supplied variables   
+   tmp = ptrarr(nitems,/allocate_heap)
+   for i=0,nitems-1 do *tmp[i] = SCOPE_VARFETCH(vv[i+1], LEVEL=0)
+
+   ndata = N_elements(v1)/ numvals[1]   ;# of elements in last dimension
+
+   for i = 1,npar do begin    ;Get the dimensions and type of each input vector
+
+      sz = size( *tmp[i-1], /STRUCT)
+       ndatai = sz.N_elements/numvals[i]
+      if ndatai NE ndata then message, $
+          'WARNING - Parameter ' + strtrim(i,2) + ' has dimension ' +  $
+	  strjoin(strtrim( sz.dimensions[0:sz.n_dimensions-1 > 0],2),' ') ,/con
+      if sz.type_name NE dtype[idltype[i]] then begin
+        message, 'Item ' + strtrim( db_item_info('NAME',i),2) + $
+           ' - parameter '+strtrim(i,2) + ' - has an incorrect data type',/CON
+        message, 'Required data type is ' + dtype[idltype[i]], /INF
+        message, 'Supplied data type is ' + sz.type_name, /INF
+	ptr_free,tmp
+        return
+     endif
+
+  endfor
+  external = db_info('external',0)
+  noconvert = external ? is_ieee_big() : 1b
+
+  entry = make_array( DIMEN = db_info('LENGTH'),/BYTE ) ;Empty entry array
+  nvalues = long( db_item_info( 'NVALUES' ) )       ;# of values per item
+  nbyte = nbyte*nvalues                             ;Number of bytes per item
+                    
+  for i = 0l, Ndata - 1 do begin
+       i1 = i*nvalues
+       i2 = i1 + nvalues -1
+
+        dbxput,0l,entry,idltype[0],sbyte[0],nbyte[0]
+	for j = 1,nitems  do $
+	dbxput, (*tmp[j-1])[ i1[j]:i2[j] ], $
+	       entry,idltype[j], sbyte[j], nbyte[j] 
+	       
+      dbwrt,entry,noconvert=noconvert        ;Write the entry into the database
+
+  endfor
+  ptr_free,tmp
+
+  if ~keyword_set( NOINDEX ) then begin
+
+      indexed = db_item_info( 'INDEX' )      ;Need to create an indexed file?
+      if ~array_equal(indexed,0)  then begin
+	   if ~keyword_set(silent) then	$
+	           message,'Now creating indexed files',/INF
+           dbindex,items
+       endif
+
+  endif
+
+  dbclose
+
+;  Mark successful completion, and return.
+
+  status = 1
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/dbcircle.pro b/Code/script_idl_mv/astrolib/dbcircle.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8c5a44b031c77f653f5bb49235e065bfd72fd72d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbcircle.pro
@@ -0,0 +1,208 @@
+function dbcircle, ra_cen, dec_cen, radius, dis, sublist,SILENT=silent, $
+                TO_J2000 = to_J2000, TO_B1950 = to_B1950, GALACTIC= galactic, $
+		COUNT = nfound
+;+
+; NAME:
+;      DBCIRCLE
+; PURPOSE:
+;      Find sources in a database within specified radius of specified center
+; EXPLANATION:
+;      Database must include items named 'RA' (in hours) and 'DEC' (in degrees)
+;      and must have previously been opened with DBOPEN
+;
+; CALLING SEQUENCE:
+;     list = DBCIRCLE( ra_cen, dec_cen, [radius, dis, sublist, /SILENT, 
+;                                /GALACTIC, TO_B1950, /TO_J2000, COUNT= ] )   
+;
+; INPUTS:
+;       RA_CEN - Right ascension of the search center in decimal HOURS, scalar
+;       DEC_CEN - Declination of the search center in decimal DEGREES, scalar
+;               RA_CEN and DEC_CEN should be in the same equinox as the 
+;               currently opened catalog.
+;
+; OPTIONAL INPUT:
+;       RADIUS - Radius of the search field in arc minutes, scalar.
+;               DBCIRCLE prompts for RADIUS if not supplied.
+;       SUBLIST - Vector giving entry numbers in currently opened database
+;               to be searched.  Default is to search all entries
+;
+; OUTPUTS:
+;     LIST - Vector giving entry numbers in the currently opened catalog
+;            which have positions within the specified search circle
+;            LIST is set to -1 if no sources fall within the search circle
+;
+; OPTIONAL OUTPUT
+;       DIS -  The distance in arcminutes of each entry specified by LIST
+;               to the search center (given by RA_CEN and DEC_CEN)
+;
+; OPTIONAL KEYWORD INPUT:
+;       /GALACTIC - if set, then the first two parameters are interpreted as
+;                 Galactic coordinates in degrees, and is converted internally
+;                 to J2000 celestial to search the database.   
+;       /SILENT - If this keyword is set, then DBCIRCLE will not print the 
+;               number of entries found at the terminal
+;       /TO_J2000 - If this keyword is set, then the entered coordinates are
+;               assumed to be in equinox B1950, and will be converted to
+;               J2000 before searching the database
+;       /TO_B1950 - If this keyword is set, then the entered coordinates are
+;               assumed to be in equinox J2000, and will be converted to
+;               B1950 before searching the database
+;               NOTE: The user must determine on his own whether the database
+;               is in B1950 or J2000 coordinates.
+; OPTIONAL KEYWORD OUTPUT:
+;       COUNT - - Integer scalar giving the number of valid matches
+; METHOD:
+;       A DBFIND search is first performed on a square area of given radius.
+;       The list is the restricted to a circular area by using GCIRC to 
+;       compute the distance of each object to the field center.
+;
+; RESTRICTIONS;
+;       The database must have items 'RA' (in hours) and 'DEC' (in degrees).
+;       Alternatively, the database could have items RA_OBJ and DEC_OBJ 
+;      (both in degrees)
+; EXAMPLE:
+;       Find all Hipparcos stars within 40' of the nucleus of M33
+;       (at J2000 1h 33m 50.9s 30d 39' 36.7'')
+;
+;       IDL> dbopen,'hipparcos'
+;       IDL> list = dbcircle( ten(1,33,50.9), ten(3,39,36.7), 40)
+;
+; PROCEDURE CALLS:
+;       BPRECESS, DBFIND(), DBEXT, DB_INFO(), GCIRC, GLACTC, JPRECESS
+; REVISION HISTORY:
+;      Written W. Landsman     STX           January 1990
+;      Fixed search when crossing 0h         July 1990
+;      Spiffed up code a bit     October, 1991
+;      Leave DIS vector unchanged if no entries found W. Landsman July 1999
+;      Use maximum declination, rather than declination at field center to
+;      correct RA for latitude effect    W. Landsman   September 1999
+;      Added COUNT, GALACTIC keywords  W. Landsman   December 2008
+;      Fix problem when RA range exceeds 24h  W. Landsman   April 2009
+;      Work as advertised for RA_OBJ field  W. Landsman June 2010
+;      Fix occasional problem when crossing 0h  E. Donoso/W.Landsman Jan 2013
+;      Check if database has been opened W. Landsman Aug 2013
+;-                   
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 2 then begin
+    print,'Syntax - list = ' + $
+    'DBCIRCLE( ra[hours], dec[degrees], radius[arcmin], [ dis, sublist  '
+    print,'               Count=, /GALACTIC, /SILENT, /TO_J2000, /TO_B1950 ] )'
+    if N_elements(sublist) GT 0 then return, sublist else return,[-1L]
+ endif
+
+ if (N_elements(ra_cen) NE 1) || (N_elements(dec_cen) NE 1) then begin
+    print, 'DBCIRCLE: ERROR - Expecting scalar RA and Dec parameters'
+    if N_elements(sublist) GT 0 then return, sublist else return,[-1L]
+ endif
+
+ if N_params() LT 3 then read,'Enter search radius in arc minutes: ',radius
+
+ nentries = db_info( 'ENTRIES',0 ) 
+ if nentries EQ 0 then begin
+    if ~keyword_set(SILENT) then message, $
+        'ERROR - No entries in database ' + db_info("NAME",0),/INF
+    if N_elements(sublist) GT 0 then return, sublist else return,[-1]
+ endif   
+ 
+ if keyword_set(TO_J2000) then begin
+        jprecess,ra_cen*15.,dec_cen,racen,deccen 
+        racen = racen[0]/15.    &       deccen = deccen[0]
+ endif else  if keyword_set(TO_B1950) then begin
+        bprecess,ra_cen*15.,dec_cen,racen,deccen 
+        racen = racen[0]/15.    &       deccen = deccen[0]
+ endif else if keyword_set(galactic) then begin 
+         glactc,racen,deccen,2000,ra_cen*15,dec_cen,2   ;Convert from Galactic		
+ endif else begin
+        racen = ra_cen[0]    &  deccen = dec_cen[0]
+ endelse
+
+ size = radius/60.      ;Size of search field in degrees
+ decmin = double(deccen-size) > (-90.)
+ decmax = double(deccen+size) < 90.
+ bigdec = max(abs([decmin, decmax]))
+ items = strtrim(db_item_info('name'))
+ g = where(items EQ 'RA', Ncount)
+ if Ncount EQ 0 then begin 
+      g = where(items EQ 'RA_OBJ', Ncount)
+      if Ncount EQ 0 then message, $
+               'ERROR - Database must have item named RA or RA_OBJ' else begin
+	       sra = 'RA_OBJ' & sdec = 'DEC_OBJ'
+	       endelse
+ endif else begin 
+      sra = 'RA' & sdec = 'DEC'
+ endelse         	        
+ 
+ if abs(bigdec) EQ 90 then rasize = 24 else $             ;Updated Sep 1999
+       rasize = abs(size/(15.*cos(bigdec/!RADEG))) < 24.  ;Correct for latitude effect
+
+ if 2*rasize gt 24. then begin         ;Only need search on Dec?
+      st = string(decmin) + '<dec<' + string(decmax) 
+      redo = 0
+ endif else begin
+ rmin = double(racen-rasize)
+ rmax = double(racen+rasize)
+
+
+;  If minimum RA is less than 0, or maximum RA is greater than 24
+;  then we must break up into two searchs
+
+ if rmax gt 24. then begin
+        redo = 1
+        newrmax = rmax - 24.
+        newrmin = 0.
+        rmax = 24.
+ endif else if rmin lt 0 then begin
+        redo = 1
+        newrmin = 24. + rmin
+        newrmax = 24.
+        rmin = 0.
+ endif else redo = 0
+ if sra EQ 'RA_OBJ' then begin      ;Item RA_OBJ assumed to be in degrees
+	       rmin = rmin*15.
+	       rmax = rmax*15.
+ endif 	       
+
+ 
+ st = string(rmin) + '<' + sra + '<' + string(rmax) +',' + $
+      string(decmin) + '<' + sdec + '<' + string(decmax) 
+ endelse
+
+ if N_params() LT 5 then list = dbfind( st, /SIL ) else $
+                         list = dbfind( st, sublist, /SIL )
+
+ if redo then begin
+        st = string(newrmin) + '<' +sra + '<' + string(newrmax) + ',' + $
+                string(decmin) + '<' + sdec + '< ' + string(decmax)
+        if N_params() LT 5 then newlist = dbfind(st,/SIL) else $ 
+                  newlist = dbfind(st,sublist,/SIL)
+        if list[0] GT 0 then list = [ list, newlist ] else list = newlist
+ endif
+
+; Use GCIRC to compute angular distance of each source to the field center
+
+ silent = keyword_set(SILENT)
+ if ~silent then begin
+      print,' ' & print,' '
+  endif     
+
+ if max(list) GT 0 then begin                         ;Any entries found?
+        dbext, list, sra + ',' + sdec, ra_match, dec_match
+	if sra EQ 'RA_OBJ' then ra_match = ra_match/15.
+        gcirc,1, racen, deccen, ra_match, dec_match, ddis
+        good = where( ddis/3600. LT size, Nfound )
+        if Nfound GT 0 then begin
+             dis = ddis[good]/60.
+             if ~silent then $
+                 print, Nfound, ' entries found in ',db_info('name',0)
+             return, list[good] 
+        endif 
+ endif 
+
+ if ~silent then $
+       print,'No entries found by dbcircle in ', db_info( 'NAME',0 )
+ Nfound  = 0      
+ return,[-1L]
+ 
+ end
diff --git a/Code/script_idl_mv/astrolib/dbclose.pro b/Code/script_idl_mv/astrolib/dbclose.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6e2b0ef05a68e44563a0852c1c874b482fb0ccea
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbclose.pro
@@ -0,0 +1,56 @@
+pro dbclose,dummy
+;+
+; NAME:
+;       DBCLOSE
+; PURPOSE:
+;       procedure to close a data base file
+;
+; CALLING SEQUENCE:  
+;       dbclose
+;
+; INPUTS:
+;       None
+;
+; OUTPUTS
+;       None
+;
+; SIDE EFFECTS:
+;       the data base files currently opened are closed
+;
+; PROCEDURE CALLS:
+;       DB_INFO()
+; HISTORY:
+;       version 2  D. Lindler  Oct. 1987
+;       For IDL version 2      August 1990
+;       William Thompson, GSFC/CDS (ARC), 30 May 1994
+;                Added support for external (IEEE) data format
+;       Remove call to HOST_TO_IEEE   W. Landsman June 2013
+;-
+;------------------------------------------------------------------------
+ On_error,2
+ common db_com, QDB, QITEMS, QDBREC         ;Database common - see DBOPEN
+
+ if N_elements(qdb) LT 120 then return	;No db opened
+ ndb = db_info('NUMBER')		;number of data bases opened
+ update = db_info('UPDATE',0)		;opened for update?
+
+; If database open for update, write total number of entries in zeroeth record
+
+ if update EQ 1 then begin		;update header
+	output = [db_info('entries',0), db_info('seqnum',0)]
+	if qdb[119] eq 1 then $
+	     swap_endian_inplace, output, /Swap_if_little ;External format?
+        qdbrec[0] = byte(output,0,8)
+ endif
+
+ for i = 0, ndb-1 do begin		;loop on units (2 per data base)
+        unit1 = qdb[96,i]			;unit numbers
+        unit2 = qdb[97,i]			;unit numbers
+	if unit1 gt 0 then free_lun,unit1       ;Is it opened?
+	if unit2 gt 0 then free_lun,unit2       ;Is it opened?
+ endfor
+
+ qdb=0					;mark as closed
+
+ return                                                              
+ end
diff --git a/Code/script_idl_mv/astrolib/dbcompare.pro b/Code/script_idl_mv/astrolib/dbcompare.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0c830450fad46cdebb608d0cbd33999563e0d2ea
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbcompare.pro
@@ -0,0 +1,131 @@
+pro dbcompare,list1,list2, items, TEXTOUT=textout, DIFF = diff
+;+
+; NAME:
+;     DBCOMPARE
+; PURPOSE:
+;     Display two entries in an IDL database side by side in a column format
+;
+; CALLING SEQUENCE:     
+;     dbcompare, list1, list2, [items, TEXTOUT= , /DIFF]  
+;
+; INPUTS:
+;     list1 - Integer scalar giving first entry number to be compared.
+;     list2 - Integer scalar giving second entry number to be compared.
+;
+; OPTIONAL INPUT-OUTPUT:
+;     items - items to be compared, if not supplied then all items will be
+;          compared.    The items can be specified in any of the following ways:
+;
+;             form 1  scalar string giving item(s) as list of names
+;                     separated by commas
+;             form 2  string array giving list of item names
+;             form 3  string of form '$filename' giving name
+;                     of text file containing items (one item per line)                      line)
+;             form 4  integer scalar giving single item number or
+;                     integer vector list of item numbers
+;             form 5  Null string specifying interactive selection.   This
+;                     is the default if 'items' is not supplied
+;             form 6  '*'     select all items (= default)
+;
+;            If items was undefined or a null string on input, then
+;            on output it will contain the items interactively selected.
+;
+; OPTIONAL INPUT KEYWORDS:
+;     /DIFF - If this keyword is set and non-zero, then only the items 
+;             in the database that differ will be printed
+;
+;     TEXTOUT -  Scalar Integer (1-7) Used to determine output device.   See
+;               TEXTOPEN for more info.
+;
+; SYSTEM VARIABLES:
+;     Output device controlled by non-standard system variable !TEXTOUT, if 
+;     TEXTOUT keyword is not used.    
+;
+; EXAMPLE:
+;     Display entries 3624 and 3625 in column form showing only the items
+;     that differ.
+;               IDL> dbcompare,3624,3625,/diff
+;
+; PROCEDURES USED:
+;     DB_INFO(), DB_ITEM, DB_ITEM_INFO(), DBRD, DBXVAL()
+;     TEXTOPEN, TEXTCLOSE
+; HISTORY:
+;     Written,  W. Landsman            July 1996
+;     Fix documentation, add Syntax display    W. Landsman   November 1998   
+;     Replace DATATYPE() with size(/TNAME)   W. Landsman    November 2001
+;     Assume since V5.5, remove VMS call  W. Landsman       September 2006
+;     Fix problem with multiple values when /DIFF set W. Landsman April 2007
+;-
+;
+ On_error,2                                ;Return to caller
+ compile_opt idl2
+ if N_params() LT 2 then begin
+       print,'Syntax - DBCOMPARE, list1, list2, [items, TEXTOUT= ,/DIFF]'  
+       return
+ endif
+ 
+; Make list a vector
+
+ dbname = db_info( 'NAME', 0 )
+
+ nentry = db_info( 'ENTRIES', 0)
+ if list1[0] GT nentry then message, dbname + $
+     ' LIST1 entry number must be between 1 and ' + strtrim( nentry, 2 )
+
+ if list2[0] GT nentry then message, dbname + $
+     ' LIST2 entry number must be between 1 and ' + strtrim( nentry, 2 )
+
+
+; Determine items to print
+
+ if N_elements(items) EQ 0 then items = '*'
+ db_item,items, it, ivalnum, dtype, sbyte, numvals, nbytes
+ nvalues = db_item_info( 'NVALUES', it )        ;number of values in item
+ nitems = N_elements( it )                      ;number of items requested
+ qnames = db_item_info( 'NAME', it )
+ qtitle = db_info( 'TITLE', 0 )                 ;data base title
+
+; Open output text file
+
+ if not keyword_set(TEXTOUT) then textout = !textout  ;use default output dev.
+
+ textopen, dbname, TEXTOUT = textout
+ if size(TEXTOUT,/TNAME) EQ 'STRING' then text_out = 5 else $
+        text_out = textout <!TEXTUNIT
+
+; Create table listing of each item specified. -------------------------
+
+      dbrd, list1, entry1                         ; read an entry.
+      dbrd, list2, entry2                         ; read an entry.
+      printf, !TEXTUNIT, ' '                        ; print  blank line.
+
+; display name and value for each entry 
+
+      for k = 0, nitems-1  do begin
+         ;
+         ; only print entries of reasonable size... < 5 values in item.
+         ;
+         if nvalues[k] LT 5 then begin
+                value1 = dbxval(entry1,dtype[k],nvalues[k],sbyte[k],nbytes[k])
+                value2 = dbxval(entry2,dtype[k],nvalues[k],sbyte[k],nbytes[k])
+                if dtype[k] EQ 1 then begin
+                        value1 = fix(value1)
+                        value2 = fix(value2)
+                endif
+                value1 = strtrim(value1,2)
+                value2 = strtrim(value2,2)
+                if keyword_set(diff) then $
+		       doprint = total(value1 NE value2) GT 0  $
+                                      else doprint = 1
+                if doprint then printf,!textunit,it[k],') ',qnames[k],  $
+                        f = '(i,a,a,a,t55,a)',  value1,value2
+         endif                                          ;display name,value
+       endfor   ; k
+
+
+ printf,!textunit,' '                         ;Added 11/90
+ 
+ textclose, TEXTOUT = textout                   ;close text file
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dbcreate.pro b/Code/script_idl_mv/astrolib/dbcreate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..94b577625bafd9cafcbc6e68f4df59a149683d73
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbcreate.pro
@@ -0,0 +1,522 @@
+pro dbcreate,name,newindex,newdb,maxitems,EXTERNAL=EXTERNAL, Maxentry=maxentry
+;+
+; NAME: 
+;       DBCREATE
+; PURPOSE:      
+;       Create a new data base (.dbf), index (.dbx) or description (.dbh) file
+; EXPLANATION:
+;       A database definition (.dbd) file must already exist in the current
+;       directory or in a ZDBASE directory.    The new .dbf, .dbx and/or .dbh
+;       files will be written to the same directory.   So if the .dbd file is 
+;       in a ZDBASE directory, then the user must have write privilege to that 
+;       directory
+;
+;       This version allows record length to be larger than 32767 bytes
+; CALLING SEQUENCE:     
+;       dbcreate, name,[ newindex, newdb, maxitems]  [,/EXTERNAL, MAXENTRY=]  
+;
+; INPUTS:       
+;       name- name of the data base (with no qualifier), scalar string. 
+;               The description will be read from the file "NAME".dbd 
+;               Maximum length of name is 19 characters.
+;
+; OPTIONAL INPUTS:      
+;       newindex - if non-zero then a new index file is created,
+;               otherwise it is assumed that changes do not affect the
+;               index file. (default=0)
+;       newdb - if non-zero then a new data base file (.dbf) will
+;               be created. Otherwise changes are assumed not to affect
+;               the file's present format.
+;       maxitems - maximum number of items in data base.
+;               If not supplied then the number of items is
+;               limited to 1000.
+;
+; OUTPUTS:
+;       NONE.
+;
+; OPTIONAL INPUT KEYWORDS:       
+;
+;       external - If set, then the database is written with an external data
+;               representation.  This allows the database files to be used on
+;               any computer platform, e.g. through NFS mounts, but some
+;               overhead is added to reading the files.  The default is to
+;               write the data in the native format of the computer being used.
+;
+;               This keyword is only paid attention to if NEWDB or NEWINDEX
+;               are nonzero.  Otherwise, the database is opened to find
+;               out if it uses external representation or not.
+;
+;               Extreme caution should be used if this keyword is used with
+;               only NEWINDEX set to a nonzero value.  This mode is allowed so
+;               that databases written on machines which already use the
+;               external data representation format, e.g. Sun workstations, to
+;               be marked external so that other machines can read them.
+;
+;
+;       MAXENTRY - positive integer giving the maximum number of entries in the
+;               database (needed to adjust the size of the index file).   This
+;               keyword can be used to supercede the  #maxentries line in the 
+;               .dbd file (the larger of the two numbers will be used).
+; PROCEDURE CALLS:      
+;       GETTOK(), FIND_WITH_DEF(), ZPARCHECK
+;
+; RESTRICTIONS: 
+;       If newdb=0 is not specified, the changes to the .dbd file can
+;       not alter the length of the records in the data base file.
+;       and may not alter positions of current fields in the file.
+;       permissible changes are:
+;               1) utilization of spares to create a item or field
+;               2) change in field name(s)
+;               3) respecification of index items
+;               4) changes in default print formats
+;               5) change in data base title
+;               6) changes in pointer specification to other data
+;                       data bases
+;
+;       !priv must be 2 or greater to execute this routine.
+;
+;
+; SIDE EFFECTS:  
+;       data base description file ZDBASE:name.dbh is created
+;       and optionally ZDBASE:name.dbf (data file) and
+;       ZDBASE.dbx (index file) if it is a new data base.
+;
+; REVISION HISTORY:     
+;       D. Lindler, GSFC/HRS, October 1987
+;       Modified:  Version 1, William Thompson, GSFC, 29 March 1994
+;                  Version 2, William Thompson, GSFC/CDS (ARC), 28 May 1994
+;                  Added EXTERNAL keyword.
+;       Version 4, William Thompson, GSFC, 3 November 1994
+;                       Modified to allow ZDBASE to be a path string.
+;       8/14/95  JKF/ACC - allow EXTERNAL data for newindex OR newdb modes.
+;       Make sure all databases closed before starting W. Landsman June 1997
+;       Added new unsigned and 64 bit integer datatypes W. Landsman July 2001
+;       Make sure to use lowercase filenames on Unix W. Landsman May 2006
+;       Added MAXENTRY keyword   W. Landsman July 2006
+;       Assume since V5.5, remove obsolete keywords to OPEN W. Landsman Sep2006
+;       No longer required to be a ZDBASE directory  W. Landsman Feb 2008
+;       Fix Feb 2008 bug when files are in current dir W. L.  May 2008
+;       Fix May 2008 bug when files are not in current dir (sigh) W. L. May 2008
+;       Warn if database length exceeds 32767 bytes  W.L. Dec 2009
+;       Remove spurious warning that database name is too long W.L. April 2010
+;       Support entry lengths larger than 32767 bytes W.L. Oct. 2010
+;       Better testing for valid print formats W.L. Nov 2010
+;       Fix problem where descriptions of different items could overlap
+;            E.Shaya/W.L.  Oct. 2012
+;       Work better when .db files are not in current directory W.L. Oct 2013
+;       Maxitems now defaults to 1000  W.L.   Jan 2015
+;-
+;----------------------------------------------------------
+ On_error,2                         ;Return to caller
+ compile_opt idl2
+
+if N_Params() LT 1 then begin
+      print,'Syntax - dbcreate, name, [ newindex, newdb, maxitems ]'
+      print,'  Input Keywords:         /EXTERNAL, MAXENTRY= '
+      print,'  !PRIV must be 2 or greater to execute this routine'
+      return
+endif
+;
+; check privilege
+;
+if !priv LT 2 then  $
+        message,'!PRIV must be 2 or greater to execute this routine'
+;
+; check parameters
+;
+zparcheck, 'DBCREATE', name, 1, 7, 0, 'Database Name'
+if N_params() LT 2 then newindex = 0
+if N_params() LT 3 then newdb = 0
+if N_params() LT 4 then maxitems = 1000
+if N_elements(maxentry) EQ 0 then maxentry = 1
+filename = strlowcase(strtrim(name,2))
+if strlen(filename) GT 19 then message,/INF, $
+   'Warning - database name must not exceed 19 characters'
+
+ dbclose                         ;Close any databases already open
+ ;
+; open .dbd file
+;
+get_lun, unit                   ;get free unit number
+dbdname =  find_with_def(filename+'.dbd', 'ZDBASE')
+fdecomp,dbdname,disk,dir,fname
+zdir = disk+ dir 
+if zdir EQ '' then begin 
+  cd,current=zdir
+  zdir = zdir + path_sep()
+endif  
+if ~file_test(zdir,/write) then message, $
+   'ERROR - must have write privileges to directory ' + zdir
+openr, unit, dbdname,error=err
+if err NE 0 then goto, Bad_IO
+On_ioerror, BAD_IO              ;On I/O errors go to BAD_IO
+
+;
+; Decide whether or not external data representation should be used.
+;   8/14/95  JKF/ACC - allow EXTERNAL data for newindex OR newdb modes.
+;
+if ((newindex ne 0) || (newdb ne 0)) || $
+                (~file_test(zdir+ fname+'.dbh')) then begin
+        extern = keyword_set(external)
+end else begin
+        openr,tempunit,zdir +fname+'.dbh',/get_lun
+        point_lun,tempunit,119
+        extern = 0b
+        readu,tempunit,extern
+        free_lun,tempunit
+endelse
+;
+; set up data buffers
+;
+names = strarr(maxitems)                        ;names of items
+numvals = replicate(1L,maxitems)                   ;number of values
+type = intarr(maxitems)                         ;data type
+nbytes = intarr(maxitems)                       ;number of bytes in item
+desc = strarr(maxitems)                         ;descriptions of items
+sbyte = lonarr(maxitems)                        ;starting byte position
+format = strarr(maxitems)                       ;print formats
+headers = strarr(3,maxitems)                    ;print headers
+headers[*,*]='               '                  ;init headers
+title = ''                                      ;data base title
+index = intarr(maxitems)                        ;index type
+pointers = strarr(maxitems)                     ;pointer array
+npointers = 0
+maxentries = 30000L
+alloc = 100L
+;
+; first item is always entry number
+;
+names[0] = 'ENTRY'
+type[0] = 3             ;longword integer
+nbytes[0] = 4           ;four bytes
+desc[0] = 'Entry or Record Number'
+format[0] = 'I8'
+headers[1,0] = 'ENTRY'
+nitems = 1S             ;Short integer
+nextbyte = 4            ;next byte position in record
+
+;
+; read and process input data
+;
+block='TITLE'                           ;assume first block is title
+inputst=''
+while ~eof(unit) do begin            ;loop on records in the file
+;
+; process next line of input
+;
+    readf,unit,inputst
+    print,inputst
+    st=gettok(inputst,';')
+    if strtrim(st,2) eq '' then goto,next       ;skip blank lines
+    if strmid(st,0,1) eq '#' then begin
+        block=strupcase(strmid(st,1,strlen(st)-1));begin new block
+        goto,next
+    end
+;
+    case strtrim(block,2) of
+
+        'TITLE' : title=st
+
+        'MAXENTRIES' : maxentries=long(strtrim(st,2)) > maxentry
+
+        'ITEMS' : begin
+;
+;               process statement in form
+;                       <itemname> <datatype> <description>
+;
+                item_name=" "
+                item_name=strupcase(gettok(st,' '))
+                st = strtrim(st, 1)
+                item_type = " "
+                item_type=gettok(st,' ')
+                st = strtrim(st, 1)
+                desc[nitems]=st
+                if item_name eq '' then $
+                        message,'Invalid item name',/IOERROR
+                names[nitems]=gettok(item_name,'(')
+                if item_name ne '' then $               ;is it a vector
+                        numvals[nitems]=fix(gettok(item_name,')')) 
+                if item_type eq '' then $
+                  message,'Item data type not supplied for item ' + $
+                          strupcase(item_name),/IOERROR
+                data_type=strmid(strupcase(gettok(item_type,'*')),0,1)
+                num_bytes=item_type
+                if num_bytes eq '' then num_bytes='4'
+                if (data_type eq 'R') || (data_type eq 'I') || $
+                   (data_type eq 'U') then $
+                                data_type=data_type+num_bytes
+                case data_type of
+                        'B' : begin & idltype= 1 & nb=1 & ff='I6' & end
+                        'L' : begin & idltype= 1 & nb=1 & ff='I6' & end
+                        'I2': begin & idltype= 2 & nb=2 & ff='I7' & end
+                        'I4': begin & idltype= 3 & nb=4 & ff='I11' & end
+                        'I8': begin & idltype= 14 & nb=8 & ff='I22' & end
+                        'R4': begin & idltype= 4 & nb=4 & ff='G12.6' & end
+                        'R8': begin & idltype= 5 & nb=8 & ff='G20.12' & end
+                        'U2': begin & idltype= 12 & nb=2 & ff='I7' & end
+                        'U4': begin & idltype= 13 & nb=4 & ff='I11' & end
+                        'U8': begin & idltype= 15 & nb=8 & ff='I22' & end
+                        'C' : begin
+                                idltype = 7
+                                nb=fix(num_bytes)
+                                ff='A'+num_bytes
+                              end
+                        else: message,'Invalid data type "'+ item_type+ $
+                                       '" specified',/IOERROR
+                endcase
+                format[nitems]=ff                       ;default print format
+                headers[1,nitems]=names[nitems] ;default print header
+                type[nitems]=idltype            ;idl data type for item
+                nbytes[nitems]=nb               ;number of bytes for item
+                sbyte[nitems]=nextbyte          ;position in record for item
+                nextbyte=nextbyte+nb*numvals[nitems] ;next byte position
+                nitems++
+                end
+
+        'FORMATS': begin
+;
+;                process strings in form:
+;                       <item name> <format> <header1>,<header2>,<header3>
+;
+                item_name=" "
+                item_name=strupcase(gettok(st,' '))
+                item_no=0
+                while item_no lt nitems do begin
+                        if strtrim(names[item_no]) eq item_name then begin
+                                st = strtrim(st, 1)
+                                format[item_no]=gettok(st,' ')
+                                if strtrim(st,2) ne '' then begin
+                                        st = strtrim(st, 1)
+                                        headers[0,item_no]=gettok(st,',')
+                                        headers[1,item_no]=gettok(st,',')
+                                        headers[2,item_no]=strtrim(st)
+                                endif
+                        endif
+                        item_no++
+                endwhile
+                end
+
+        'POINTERS': begin
+;
+;               process record in form:
+;                       <item name> <data base name>
+;
+                item_name=strupcase(gettok(st,' '))
+                item_no=0
+                while item_no lt nitems do begin
+                        if strtrim(names[item_no]) eq item_name then $
+                                pointers[item_no]=strupcase(strtrim(st, 1))
+                        item_no++
+                endwhile
+                endcase
+
+        'INDEX': begin
+;
+;               process record of type:
+;               <item name> <index type>
+;
+                item_name=strupcase(gettok(st,' '))
+                st = strtrim(st, 1)
+                indextype=gettok(st,' ')
+                item_no=0
+                while item_no lt nitems do begin
+                        if strtrim(names[item_no]) eq item_name then begin
+                            case strupcase(indextype) of
+                                'INDEX' : index[item_no]=1
+                                'SORTED': index[item_no]=2
+                                'SORT'  : index[item_no]=3
+                                'SORT/INDEX' : index[item_no]=4
+                                else    : message,'Invalid index type',/IOERROR
+                            endcase
+                        endif
+                        item_no++
+                endwhile
+                end
+        else : begin
+                print,'DBCREATE-- invalid block specification of ',block
+                print,'   Valid values are #TITLE, #ITEMS, #FORMATS, #INDEX,'
+                print,'   #MAXENTRIES or #POINTERS'
+               end
+        endcase
+next:
+endwhile; loop on records
+
+;
+; create data base descriptor record --------------------------------------
+;
+;       byte array of 120 values
+;
+;       bytes
+;         0-18   data base name character*19
+;         19-79  data base title character*61
+;         80-81  number of items (integer*2)
+;         105-108  record length of DBF file (integer*4)
+;         84-117 values filled in by DBOPEN
+;         119    equals 1 if keyword EXTERNAL is true.
+;
+totbytes=((nextbyte+3)/4*4)  ;make record length a multiple of 4
+drec = bytarr(120)
+drec[0:79]=32b                      ;blanks
+drec[0] = byte(strupcase(filename))
+drec[19] = byte(title)
+drec[80] = byte(fix(nitems),0,2)
+drec[105] = byte(long(totbytes),0,4)
+drec[118] = 1b
+drec[119] = byte(extern)
+;
+; create item description records
+;
+;  irec[*,i] contains description of item number i with following
+;  byte assignments:
+;       0-19    item name (character*20)
+;       20-21   IDL data type (integet*2)
+;       24-25   Starting byte position i record (integer*2)
+;       26-27   Number of bytes per data value (integer*2)
+;       28      Index type
+;       29-97   Item description
+;       98-99   Field length of the print format
+;       100     Pointer flag
+;       101-119 Data base this item points to
+;       120-125 Print format
+;       126-170 Print headers
+;       179-182   Number of values for item (1 for scalar) (integer*4)
+;       183-186 Starting byte position in original DBF record (integer*4)
+;       187-199 Added by DBOPEN
+irec=bytarr(200,nitems)
+
+headers = strmid(headers,0,15)       ;Added 15-Sep-92
+
+for i=0,nitems-1 do begin
+        rec=bytarr(200)
+        rec[0:19]=32b  &  rec[101:170]=32b    ;Default string values are blanks
+        rec[29:87] = 32b
+        rec[0]  = byte(names[i])
+        rec[20] = byte(type[i],0,2)
+        rec[179] = byte(numvals[i],0,4)
+        rec[183] = byte(sbyte[i],0,4)
+        rec[26] = byte(nbytes[i],0,2)
+        rec[28] = index[i]
+        rec[29] = byte(desc[i])
+        if strtrim(pointers[i]) ne '' then rec[100]=1 else rec[100]=0
+        rec[101]= byte(strupcase(pointers[i]))
+        rec[120]= byte(format[i])
+        ff=strtrim(format[i])
+	test = strnumber(gettok(strmid(ff,1,strlen(ff)-1),'.'),val)
+        if test then flen =fix(val) else $    ;Modified Nov-10
+	   message,'Invalid print format supplied: ' + format[i],/IOERROR
+        rec[98] = byte(flen,0,2)
+        rec[126]= byte(headers[0,i]) > 32b    ;Modified Nov-91
+        rec[141]= byte(headers[1,i]) > 32b
+        rec[156]= byte(headers[2,i]) > 32b
+        irec[0,i]=rec
+
+end
+;
+; Make sure user is on ZDBASE and write description file
+;
+
+ close,unit
+ openw,unit,zdir + fname+'.dbh'
+On_ioerror, NULL 
+if extern then begin
+        tmp = fix(drec,80,1) & byteorder,tmp,/htons & drec[80] = byte(tmp,0,2)
+        tmp = long(drec,105,1) & byteorder,tmp,/htonl & drec[105] = byte(tmp,0,4)
+;
+        tmp = fix(irec[20:27,*],0,4,nitems)
+        byteorder,tmp,/htons 
+        irec[20,0] = byte(tmp,0,8,nitems)
+;
+        tmp = fix(irec[98:99,*],0,1,nitems)
+        byteorder,tmp,/htons 
+        irec[98,0] = byte(tmp,0,2,nitems)
+;
+        tmp = fix(irec[171:178,*],0,4,nitems)
+        byteorder,tmp,/htons 
+        irec[171,0] = byte(tmp,0,8,nitems)
+	
+	    tmp = long(irec[179:186,*],0,2,nitems)
+        byteorder,tmp,/htonl 
+        irec[179,0] = byte(tmp,0,8,nitems)
+
+endif
+writeu, unit, drec
+writeu, unit, irec
+;
+; if new data base create .dbf and .dbx files -----------------------------
+;
+
+if newdb then begin
+    close,unit
+    openw, unit, zdir + fname+'.dbf'
+    header = bytarr(totbytes)
+    p = assoc(unit,header)
+    p[0] = header
+end
+
+;
+; determine if any indexed items
+;
+nindex = total(index GT 0)
+;
+; create empty index file if needed
+;
+if (nindex GT 0) && (newindex) then begin
+        indexed = where(index GT 0)
+;
+; create header array
+;       header=intarr(7,nindex)
+;               header(i,*) contains values
+;               i=0     item number
+;               i=1     index type
+;               i=2     idl data type for the item
+;               i=3     starting block for header
+;               i=4     starting block for data
+;               i=5     starting block for indices (type 3)
+;               i=6     starting block for unsorted data (type 4)
+;
+        nb = (maxentries+511)/512       ;number of 512 value groups
+        nextblock = 1
+        header = lonarr(7,nindex)
+        for ii = 0, nindex-1 do begin
+                item = indexed[ii]
+                header[0,ii] = item
+                header[1,ii] = index[item]
+                header[2,ii] = type[item]
+                data_blocks = nbytes[item]*nb
+                if index[item] NE 1 $
+                             then header_blocks = (nbytes[item]*nb+511)/512 $
+                             else header_blocks = 0
+                if (index[item] eq 3) or (index[item] EQ 4) then $
+                                 index_blocks=(4*nb) else index_blocks=0
+                if index[item] EQ 4 then unsort_blocks = data_blocks else $
+                                                        unsort_blocks=0
+                header[3,ii] = nextblock
+                header[4,ii] = nextblock+header_blocks
+                header[5,ii] = header[4,ii]+data_blocks
+                header[6,ii] = header[5,ii]+index_blocks
+                nextblock = header[6,ii]+unsort_blocks
+        end
+        totblocks = nextblock
+        close, unit
+        openw, unit, zdir + fname+'.dbx'
+;
+        p = assoc(unit,lonarr(2))
+        tmp = [long(nindex),maxentries]
+        if extern then byteorder, tmp,/htonl
+        p[0] = tmp
+;
+        p = assoc(unit,lonarr(7,nindex),8)
+        tmp = header
+        if extern then byteorder, tmp,/htonl
+        p[0] = tmp
+endif
+free_lun, unit
+return
+;
+BAD_IO: free_lun,unit
+print, !ERROR_STATE.MSG_PREFIX + !ERROR_STATE.MSG
+print, !ERROR_STATE.MSG_PREFIX + !ERROR_STATE.SYS_mSG
+
+return
+;
+end
diff --git a/Code/script_idl_mv/astrolib/dbdelete.pro b/Code/script_idl_mv/astrolib/dbdelete.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f145b0b18243208278a00b48470c48bc4eef8516
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbdelete.pro
@@ -0,0 +1,142 @@
+pro dbdelete, list, name, DEBUG = debug
+;+
+; NAME:
+;       DBDELETE
+; PURPOSE:
+;       Deletes specified entries from data base
+;
+; CALLING SEQUENCE:
+;       DBDELETE, list, [ name, /DEBUG ]   
+;
+; INPUTS:
+;       list - list of entries to be deleted, scalar or vector
+;       name - optional name of data base, scalar string.  If not specified
+;               then the data base file must be previously opened for update 
+;               by DBOPEN.
+;
+; OPERATIONAL NOTES:
+;       !PRIV must be at least 3 to execute.
+;
+; SIDE EFFECTS:
+;       The data base file (ZDBASE:name.dbf) is modified by removing the
+;       specified entries and reordering the remaining entry numbers
+;       accordingly (ie. if you delete entry 100, it will be replaced
+;       by entry 101 and the database will contain 1 less entry.
+;
+; EXAMPLE:
+;        Delete entries in a database STARS where RA=DEC = 0.0
+;
+;        IDL> !PRIV= 3                           ;Set privileges
+;        IDL> dbopen,'STARS',1                   ;Open for update
+;        IDL> list = dbfind('ra=0.0,dec=0.0')    ;Obtain LIST vector
+;        IDL> dbdelete, list             ;Delete specified entries from db
+;
+; NOTES:
+;       The procedure is rather slow because the entire database is re-
+;       created with the specified entries deleted.
+; OPTIONAL KEYWORD INPUT:
+;        DEBUG - if this keyword is set and non-zero, then additional 
+;               diagnostics will be printed as each entry is deleted.
+; COMMON BLOCKS:
+;       DBCOM
+; PROCEDURE CALLS:
+;       DBINDEX, DB_INFO(), DBOPEN, DBPUT, ZPARCHECK
+; HISTORY
+;       Version 2  D. Lindler  July, 1989
+;       Updated documentation   W. Landsman    December 1992
+;       William Thompson, GSFC, 28 February 1995
+;                       Fixed bug when external representation used.
+;       Fixed for case where second parameter supplied W. Landsman April 1996
+;       Use keyword DEBUG rather than !DEBUG   W. Landsman    May 1997
+;       Don't call DBINDEX if no indexed items  W. Landsman May 2006  
+;       Use TRUNCATE_LUN if V5.6 or later W. Landsman   Sep 2006 
+;       Fix problem when deleting last entry   W. Landsman Mar 2007
+;       Assume since V5.6 so TRUNCATE_LUN is available   W. Landsman
+;       
+;-
+;-------------------------------------------------------------------------------
+  On_error,2
+  compile_opt idl2
+
+  if N_params() EQ 0 then begin
+      print,'Syntax - DBDELETE, entry, [ dbname ]'
+      return
+  endif 
+
+; data base common block
+
+ common db_com,QDB,QITEMS,QDBREC
+
+; Check parameters
+
+ zparcheck, 'DBDELETE', list, 1, [1,2,3], [0,1], 'entry list'
+ if N_params() GT 1 then $
+        zparcheck, 'dbdelete', name, 2, 7, 0, 'data base name'
+ 
+ if !PRIV lt 3 then $
+        message,'!priv must be at least 3 to execute'
+
+; Open data base if name supplied
+
+  if N_params() GT 1 then dbopen,name,1 else begin    ;Open specified database
+
+     if not db_info( 'OPEN') then $
+        message,'No database open for update'
+     if not db_info('update') then $
+            message,'Database '+ db_info('NAME',0) + ' not open for update'
+  
+   endelse
+
+; Determine whether or not the database uses external data representation.
+
+ external = qdb[119] eq 1
+
+
+; Create vector if list is a scalar
+
+  outrec = 0L                           ; Create counter of output record
+  len = db_info('length')
+ 
+; loop on entries in data base
+
+  qnentry = db_info('ENTRIES',0)
+  
+  for i = 1L, qnentry do begin
+
+        ; Is it to be kept?
+
+        found = where( list EQ i, Nfound)
+
+        if keyword_set(debug) then print,i,nfound           ; allow diags.
+
+        if ( Nfound LE 0 ) then begin
+                outrec = outrec + 1                ; increment counter
+                if ( outrec NE i ) then begin
+                        entry = qdbrec[i]
+                        tmp = outrec
+                        if external then byteorder,tmp,/htonl
+                        dbput, 0, tmp, entry   ; modify entry number
+                        qdbrec[outrec] = entry
+                endif
+        endif
+  endfor
+
+; Update adjusted total number of entries.
+
+  qdb[84] = byte( outrec,0,4 )
+
+; Truncate the .dbf file at the current position.
+
+  unit = db_info('unit_dbf')
+  point_lun, unit, long64(outrec+1)*len
+  truncate_lun, unit
+
+; Update index file
+
+  indextype = db_item_info( 'INDEX')
+  if total(indextype) NE 0 then dbindex
+
+  if N_params() GT 1 then dbclose
+
+  return  ; dbdelete
+  end  ; dbdelete
diff --git a/Code/script_idl_mv/astrolib/dbedit.pro b/Code/script_idl_mv/astrolib/dbedit.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1f439fd2354f0c9ba0710de349bf6ad03c32cccd
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbedit.pro
@@ -0,0 +1,395 @@
+;+
+; NAME:
+;      DBEDIT
+;
+; PURPOSE:
+;       Interactively edit specified fields in an IDL database. 
+; EXPLANATION:
+;       The value of each field is displayed, and the user has the option
+;       of changing or keeping the value.  Widgets will be used if they
+;       are available.
+;
+; CALLING SEQUENCE:
+;       dbedit, list, [ items ]
+;
+; INPUTS:
+;       list - scalar or vector of database entry numbers.  Set list = 0 to 
+;       interactively add a new entry to a database.  Set list = -1 to edit 
+;       all entries.
+;
+; OPTIONAL INPUTS:
+;       items - list of items to be edited.  If omitted, all fields can be 
+;               edited.      
+;
+; KEYWORDS:
+;       BYTENUM = If set, treat byte variables as numbers instead of
+;                 characters.
+;
+; COMMON BLOCKS:
+;       DB_COM -- contains information about the opened database.
+;       DBW_C -- contains information intrinsic to this program.
+;
+; SIDE EFFECTS:
+;       Will update the database files.
+;
+; RESTRICTIIONS:
+;       Database must be opened for update prior to running
+;       this program.  User must be running DBEDIT from an 
+;       account that has write privileges to the databases.  
+;
+;       If one is editing an indexed item, then after all edits are complete,
+;       DBINDEX will be called to reindex the entire item.    This may
+;       be time consuming.
+;
+;       Cannot be used to edit items with multiple values
+;
+; EXAMPLE:
+;       Suppose one had new parallaxes for all stars fainter than 5th magnitude
+;       in the Yale Bright Star Catalog and wanted to update the PRLAX and
+;       PRLAX_CODE fields with these new numbers
+;
+;       IDL> !priv=2                    
+;       IDL> dbopen, 'yale_bs', 1            ;Open catalog for update
+;       IDL> list = dbfind( 'v>5')     ;Find fainter than 5th magnitude
+;       IDL> dbedit, list, 'prlax, prlax_code'   ;Manual entry of new values
+;
+; PROCEDURE:
+;       (1) Use the cursor and point to the value you want to edit.   
+;       (2) Type the new field value over the old field value.
+;       (3) When you are done changing all of the field values for each entry
+;       save the entry to the databases by pressing 'SAVE ENTRY TO DATABASES'.
+;       Here all of the values will be checked to see if they are the correct
+;       data type.  If a field value is not of the correct data type, it will
+;       not be saved.  
+;
+;       Use the buttons "PREV ENTRY" and "NEXT ENTRY" to move between entry 
+;       numbers.  You must save each entry before going on to another entry in 
+;       order for your changes to be saved.
+;
+;       Pressing "RESET THIS ENTRY" will remove any unsaved changes to the 
+;       current entry.
+;
+;REVISION HISTORY:
+;       Adapted from Landsman's DBEDIT
+;       added widgets,  Melissa Marsh, HSTX, August 1993
+;       do not need to press return after entering each entry,
+;                       fixed layout problem on SUN,
+;                       Melissa Marsh, HSTX, January 1994
+;       Only updates the fields which are changed. Joel Offenberg, HSTX, Mar 94
+;       Corrected test for changed fields  Wayne Landsman  HSTX, Mar 94
+;       Removed a couple of redundant statements W. Landsman HSTX Jan 96
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Replace DATAYPE() with size(/TNAME)   W. Landsman   November 2001
+;       Work for entry numbers > 32767     W. Landsman   December 2001
+;       Added /BYTENUM  William Thompson        13-Mar-2006
+;       Use DIALOG_MESSAGE for error messages  W. Landsman  April 2006
+;       Assume since V5.5, remove VMS support  W. Landsman  Sep 2006
+;-
+
+;----------------------------------------------------------------
+
+
+;event handler for main part of program
+
+pro widgetedit_event,event
+
+common db_com,qdb,QITEMS,QDBREC
+
+common dbw_c,liston,main,holder,widlabel,widtext,middle,nitems,names,$
+        it,itnum,dtype,numvals,sbyte,nbytes,buts,prevbut,but2,resetbut,$
+        endbut,nextbut,mid,minlist,maxlist,savebut,bigmid,entry,wid_warn,$
+        holder0,widtext0,widlabel0,thislist,nlist,wereat,newflag,bytenum
+
+CASE event.id OF
+
+    endbut: widget_control,event.top,/destroy ;destory main widget--end session
+
+    prevbut:begin       ;go to previous entry
+        if wereat ne 0 then wereat= wereat-1
+        liston = thislist[wereat]
+        widedit
+    end
+
+    nextbut:begin       ;go to next entry
+        if wereat lt nlist-1 then wereat = wereat+1 else $
+              widget_control,event.top,/destroy          ;end session
+        liston = thislist[wereat]
+        widedit
+    end
+
+    resetbut:begin      ;reset this entry
+        liston = liston
+        widedit 
+    end
+
+    savebut: begin      ;save entry to databases
+          ;update database
+        for i = 0, nitems -1 do begin
+          widget_control,widtext[i],get_value=val
+          ;test value
+          valid = 0
+           oldval = dbxval(entry,dtype[i],numvals[i],sbyte[i],nbytes[i])
+
+          on_ioerror,BADVAL
+          IF (strtrim(oldval[0],2) ne (strtrim(val[0],2))) THEN BEGIN
+              oldval[0] = strtrim(val,2)
+              valid = 1
+              dbxput,oldval,entry,dtype[i],sbyte[i],nbytes[i]
+              print,strcompress('Entry ' + string(liston) +':  '  + $
+              names[i] + ' = ' + string(val))
+              newflag[ wereat, i ] = 1b
+    BADVAL:     if (not valid) then begin
+                result = dialog_message(title='Bad Value',/ERROR, $
+                   'Item '+ strcompress(names[i],/rem) + $ 
+                        ' must be of type ' + size(oldval[0],/TNAME) )
+                str = dbxval(entry,dtype[i],numvals[i],sbyte[i],nbytes[i])
+                if (dtype[i] eq 1) and keyword_set(bytenum) then str=fix(str)
+                str = '    '+string(str[0])
+                widget_control,widtext[i],set_value=str         
+                endif
+          endIF 
+          on_ioerror,NULL
+        endfor
+        
+        if (liston EQ 0) then begin
+                 dbwrt,entry,0,1        ;new entry
+        endif else begin
+                 dbwrt,entry
+        endelse
+        widedit
+        ;create widget telling the user that the changes have been made.
+    end
+
+    else: ;donothing
+   
+     endcase
+end
+
+;--------------------------------------------------------------------
+pro widedit
+;program that makes "middle" of main widget (field values)
+
+
+common db_com,qdb,QITEMS,QDBREC
+                           
+
+common dbw_c,liston,main,holder,widlabel,widtext,middle,nitems,names,$
+        it,itnum,dtype,numvals,sbyte,nbytes,buts,prevbut,but2,resetbut,$
+        endbut,nextbut,mid,minlist,maxlist,savebut,bigmid,entry,wid_warn,$
+        holder0,widtext0,widlabel0,thislist,nlist,wereat,newflag,bytenum
+
+
+;get entry number
+ dbrd, liston, entry
+
+;get field values for this entry
+ widget_control, widtext0, set_value=string(liston)
+ for i = 0,nitems-1 do begin
+        str = dbxval(entry,dtype[i],numvals[i],sbyte[i],nbytes[i])
+        if (dtype[i] eq 1) and keyword_set(bytenum) then str=fix(str)
+        str = '    '+string(str[0])
+        widget_control,widtext[i],set_value=str
+ endfor
+
+;check to see if this entry is the minimum or maximum entry 
+ if (liston EQ minlist) then widget_control,prevbut,sensitive=0 else $
+                widget_control,prevbut,sensitive=1 
+ if (liston EQ maxlist) then widget_control,nextbut,sensitive=0 else $
+                widget_control,nextbut,sensitive=1
+
+ end
+;-------------------------------------------------------------------------
+;main program
+
+pro dbedit,list,items,bytenum=k_bytenum
+
+ compile_opt idl2
+common db_com,qdb,QITEMS,QDBREC
+
+;Nitems - Number elements in input list
+;Thislist - Sorted list of entry numbers
+;Minlist - Minimum input entry number
+;Maxlist - Maximum input entry number
+;Liston - The current entry number being edited (scalar)
+;wereat - The index of ThisList vector being edited, i.e. Thislist(wereat)=LIston
+;dtype - data type(s) (1=string,2=byte,4=i*4,...)
+;sbyte - starting byte(s) in entry
+;numvals - number of data values for item(s)
+;    NOTE: dtype, sbyte, numvals are dimensioned for *all* entries 
+
+common dbw_c,liston,main,holder,widlabel,widtext,middle,nitems,names,$
+        it,itnum,dtype,numvals,sbyte,nbytes,buts,prevbut,but2,resetbut,$
+        endbut,nextbut,mid,minlist,maxlist,savebut,bigmid,entry,wid_warn,$
+        holder0,widtext0,widlabel0,thislist,nlist,wereat,newflag,bytenum
+                          
+ On_error,2
+ if N_params() LT 1 then begin
+        print,'Syntax - dbedit, list, [ items ]'
+        return
+ endif
+        
+;Set the value of bytenum
+bytenum = keyword_set(k_bytenum)
+
+;make sure widgets are available
+ if (!D.FLAGS AND 65536) EQ 0 then begin  
+        dbedit_basic, list, items
+        return
+ endif
+
+;check to make sure database is open
+    ;first check to see if there is an open database
+    s = size(qdb)
+    if (s[0] EQ 0) then begin
+    
+           result = dialog_message(/ERROR, title='NOT OPEN FOR UPDATE', $
+	        'No database has been opened')
+            goto, PROEND  
+    endif
+;check to make sure the database is opened for update
+    dbname = db_info('NAME',0)
+    if not db_info('UPDATE') then begin
+
+        result = dialog_message(/ERROR, title='NOT OPEN FOR UPDATE', $
+	        'Database ' + dbname + ' must be opened for update.')
+        goto,PROEND
+
+    endif
+
+
+    ;check parameters
+    zparcheck, 'DBEDIT', list, 1, [1,2,3], [0,1], 'Database entry numbers'
+
+    ;get items.  If items not specified use all items except ENTRY
+    if ( N_params() LT 2 ) then begin       
+        nitems = db_info('ITEMS',0) -1       
+        items = indgen(nitems) + 1
+    endif
+
+    nlist = N_elements(list)
+
+    if nlist gt 1 then begin ;sort entry numbers
+
+        sar = sort(list)
+        thislist = list[sar]
+
+    endif else begin
+
+        thislist = lonarr(1) 
+        thislist[0] = list
+
+    endelse
+
+    ;edit all entries?  get number of entries
+    if ( list[0] EQ -1 ) then begin          
+        nlist = db_info('ENTRIES',0)           
+        if nlist le 0 then begin
+           print,'Empty database cannot be edited. Use list=0 to add new entry'
+           goto, PROEND
+        endif
+        thislist = lindgen(nlist) + 1
+    endif
+
+    minlist = min(thislist, max = maxlist)
+
+
+    nentry = db_info('ENTRIES',0)
+    if (maxlist gt nentry) then begin
+        result = dialog_message(title='INVALID ENTRY NUMBER',/ERROR, $
+           dbname + ' entry numbers must be less than ' + strtrim(nentry+1,2) )
+         goto, PROEND
+    endif
+
+    nitems = db_info('ITEMS',0) -1
+    allitems = indgen(nitems) + 1
+
+    ;get information about items
+    db_item,allitems,itnum,ivalnum,dtype,sbyte,numvals,nbytes
+    nvalues = db_item_info('nvalues')
+
+    db_item,items,it
+
+    nit = n_elements(it)                      ;Number of items to be edited
+    names = db_item_info('name',itnum)        ;Get names of each item
+    newflag = bytarr(nlist,nitems)  ;Keeps track of fields actually updated
+
+    wereat = 0
+    liston = thislist[wereat]
+    dbrd,liston,entry
+
+    ;create widget and display
+    main = widget_base(/COLUMN,title='Widgetized Database Editor')
+    w1 = widget_label(main,value='******  '  + dbname + '  ******')
+    bigmid = widget_base(main,/column,x_scroll_size=325,y_scroll_size=650)
+
+
+    butbase = widget_base(main,/column,/frame)
+    savebut = widget_button(butbase,value='SAVE THIS ENTRY')
+    buts = widget_base(butbase,/row)
+    prevbut = widget_button(buts,value='<- PREV ENTRY')
+    but2 = widget_base(buts,/column)
+    resetbut = widget_button(but2,value='RESET THIS ENTRY')
+    endbut = widget_button(but2,value='END SESSION')
+    nextbut = widget_button(buts,value='NEXT ENTRY ->')
+
+    widlabel = lonarr(nitems+1)
+    widtext = lonarr(nitems+1)
+    holder = lonarr(nitems+1)
+
+    mid = widget_base(bigmid,/column)
+
+    holder0 = widget_base(mid,/row)
+    widlabel0 =widget_label(holder0,value='  ENTRY NUMBER  ',/frame)
+    num = string(liston)
+    widtext0 = widget_label(holder0,value=num)
+
+    middle = widget_base(mid,/column)
+
+    for i = 0,nitems-1 do begin
+        ed = 'N'
+        str1 = names[i]
+
+        for j = 0, N_elements(it)-1 do begin
+                if it[j] EQ itnum[i] then ed = 'Y'
+        endfor
+
+        str = dbxval(entry,dtype[i],numvals[i],sbyte[i],nbytes[i])
+        if (dtype[i] eq 1) and keyword_set(bytenum) then str=fix(str)
+        str = '    ' + string(str[0])
+        if ed eq 'Y' then  begin
+                holder[i] = widget_base(middle,/row)
+                widlabel[i] = widget_label(holder[i],value = str1,/frame)
+                widtext[i] = widget_text(holder[i],/frame,value=str,/edit)
+        endif else begin
+                holder[i] = widget_base(middle,/row)
+                widlabel[i] = widget_label(holder[i],value = str1,/frame)
+                widtext[i] = widget_label(holder[i],value=str)
+        endelse 
+    endfor
+
+    if (liston EQ minlist) then widget_control,prevbut,sensitive=0 else $
+                widget_control,prevbut,sensitive=1
+    if (liston EQ maxlist) then widget_control,nextbut,sensitive=0 else $
+                widget_control,nextbut,sensitive=1
+
+    widget_control,main,/realize
+    xmanager,'widgetedit',main
+
+ newitem = total(newflag, 1)
+ indexnum = where(newitem, nindex)
+
+   if ( nindex GT 0 ) then begin                          ;Any mods made?
+      indexnum = itnum[indexnum]
+      indextype = db_item_info('INDEX',indexnum);Index type of modified fields 
+      good = where(indextype GE 1, Ngood)         ;Which fields are indexed?
+      if Ngood GT 0 then begin 
+        message, 'Now updating index file', /INF
+        dbindex, indexnum[good]
+      endif
+      dbopen,strlowcase(dbname),1
+    endif
+
+PROEND:
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dbedit_basic.pro b/Code/script_idl_mv/astrolib/dbedit_basic.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d934c87f8736fcdd5307ac12addb2112ba82e93f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbedit_basic.pro
@@ -0,0 +1,157 @@
+pro dbedit_basic,list,items
+;+
+; NAME:
+;       DBEDIT_BASIC
+; PURPOSE:
+;       Subroutine of DBEDIT_BASIC to edit a database on a dumb terminal.
+; EXPLANATION:
+;       Interactively edit specified fields in a database.  The
+;       value of each field is displayed, and the user has the option
+;       of changing or keeping the value.
+;
+; CALLING SEQUENCE:
+;       dbedit_basic, list, [ items ]
+;
+; INPUTS:
+;       list - scalar or vector of database entry numbers.  Set LIST=0
+;               to interactively add a new entry to a database.
+;
+; OPTIONAL INPUTS
+;       items - list of items to be edited.  If not supplied, then the
+;               value of every field will be displayed.
+;
+; NOTES:
+;       (1) Database must be opened for update (dbopen,<dbname>,1) before
+;       calling DBEDIT_BASIC.  User must have write privileges on the database
+;       files.
+;       (2) User gets a second chance to look at edited values, before
+;       they are actually written to the database
+;
+; PROMPTS:
+;       The item values for each entry to be edited are first displayed
+;       User is the asked "EDIT VALUES IN THIS ENTRY (Y(es), N(o), or Q(uit))?
+;       If user answers 'Y' or hits RETURN, then each item is displayed
+;       with its current value, which the user can update.  If user answered
+;       'N' then DBEDIT_BASIC skips to the next  entry.   If user answers 'Q'
+;       then DBEDIT will exit, saving all previous changes.
+;
+; EXAMPLE:
+;       Suppose V magnitudes (V_MAG) in a database STARS with unknown values 
+;       were assigned a value of 99.9.  Once the true values become known, the
+;       database can be edited
+;
+;       IDL> !PRIV=2 & dbopen,'STARS',1         ;Open database for update
+;       IDL> list =  dbfind('V_MAG=99.9')       ;Get list of bad V_MAG values
+;       IDL> dbedit,list,'V_MAG'       ;Interactively insert good V_MAG values
+;
+; REVISION HISTORY:
+;       Written  W. Landsman     STX        April, 1989
+;       Rename DBEDIT_BASIC from DBEDIT            July, 1993
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Change DATATYPE() to size(/TNAME)  W. Landsman   November 2001
+;-
+ On_error,2
+
+ zparcheck, 'DBEDIT_BASIC', list, 1, [1,2,3], [0,1], 'Database entry numbers'
+
+ dbname = db_info( 'NAME', 0 )                ;Database name
+ if not db_info( 'UPDATE' ) then $
+     message, 'Database ' + dbname + ' must be opened for update
+
+ if ( N_params() LT 2 ) then begin         ;Did user specify items string?
+     nitems = db_info( 'ITEMS', 0 ) -1     ;If not then use every item but ENTRY
+     items = indgen(nitems) + 1
+ endif 
+
+ nlist = N_elements(list)
+
+ if ( list[0] EQ -1 ) then begin            ;Edit all entries?
+    nlist = db_info( 'ENTRIES', 0 )         ;Get number of entries
+    list = lindgen(nlist) + 1
+ endif
+
+ db_item, items, itnum, ivalnum, dtype, sbyte, numvals, nbytes
+
+ nitems = N_elements(itnum)                ;Number of items to be edited
+ names = db_item_info( 'NAME', itnum )     ;Get names of each item
+ newflag = bytarr(nlist,nitems)        ;Keeps track of fields actually updated
+ yesno = ''
+
+for i = 0, nlist-1 do begin            ;Loop over each entry to be edited
+    ll = list[i]
+
+    if ll GT 0 then begin             ;Existing entry?
+      dbprint,ll,'*',TEXT = 1
+      read,'Edit values in this entry (Y(es),N(o),Q(uit), def=Y)? ',yesno
+      yesno = strupcase(strmid(yesno,0,1))
+      if yesno eq 'Q' then goto, UPDATE $
+        else if yesno EQ 'N' then goto, ENTRY_DONE   
+    endif else message,'Adding new entry to database '+dbname,/inform
+
+    print,'Hit [RETURN] to leave values unaltered'
+    READVAL:  dbrd,ll,entry
+    for j = 0,nitems - 1 do begin
+        val = ''
+        name = strtrim(names[j],2)
+        curval = dbxval( entry, dtype[j], numvals[j], sbyte[j], nbytes[j] )
+;       Convert byte to integer to avoid string conversion problems
+        if (dtype[j] EQ 1) and ( N_elements(curval) EQ 1 ) then $ 
+            curval = fix(curval)       
+        if ( numvals[j] EQ 1 ) then oldval = strtrim(curval,2) else $
+                                oldval = strtrim(curval[0],2) + '...'
+        read,name+' New Value (' + oldval + '): ',val
+        TESTVAL: 
+           if ( val NE '' ) then begin
+           oldval = make_array( size = [1,numvals[j],dtype[j],numvals[j]] )
+           On_IOerror, BADVAL 
+           oldval[0] = val
+           On_IOerror, NULL 
+           newflag[i,j] = 1
+           dbxput, oldval, entry, dtype[j], sbyte[j], nbytes[j]
+        endif    
+    endfor
+
+    if ( total(newflag[i,*]) GT 0 ) then begin
+    print,'' & print,'Updated Values' & print,''
+
+    for j = 0,nitems-1 do begin
+         name = strtrim(names[j],2)
+         print,name,': ',dbxval( entry,dtype[j],numvals[j],sbyte[j],nbytes[j] )
+    endfor
+         print,''
+         yesno = ''
+         read,' Are these values correct [Y]? ', yesno
+         if ( strupcase(yesno) NE 'N' ) then begin
+            if ( ll EQ 0 ) then begin 
+                dbwrt,entry,0,1 
+                ll = db_info('entries',0) + 1
+            endif else dbwrt,entry
+            print,'' & print,'Entry ',strtrim(ll,2), ' now updated   
+         endif else begin 
+            newflag[i,*] = 0
+            goto, READVAL
+         endelse
+    endif else print,'No values updated for entry',ll
+    ENTRY_DONE:     
+endfor
+
+UPDATE: 
+ newitem = total(newflag, 1)
+ indexnum = where(newitem, nindex)
+
+ if ( nindex GT 0 ) then begin                          ;Any mods made?
+      indexnum = itnum[indexnum]
+      indextype = db_item_info('INDEX',indexnum)  ;Index type of modified fields 
+      good = where(indextype GE 1, ngood)         ;Which fields are indexed?
+      if ngood GT 0 then dbindex,indexnum[good]
+      dbopen,dbname,1
+      dbprint,list,[0,itnum],TEXT=1
+ endif
+ return
+BADVAL:  
+  print,'Item '+name+ ' must be of type '+ size(oldval[0],/TNAME)
+         val = ''
+         j = j-1
+         goto, TESTVAL      
+
+ end
diff --git a/Code/script_idl_mv/astrolib/dbext.pro b/Code/script_idl_mv/astrolib/dbext.pro
new file mode 100644
index 0000000000000000000000000000000000000000..28250cf561a29bc4cbad2bf3cb3e8b343ec09756
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbext.pro
@@ -0,0 +1,85 @@
+pro dbext,list,items,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12
+;+
+; NAME:
+;       DBEXT
+; PURPOSE:
+;       Extract values of up to 12 items from an IDL database 
+; EXPLANATION:
+;       Procedure to extract values of up to 12 items from
+;       data base file, and place into IDL variables
+;
+; CALLING SEQUENCE:
+;       dbext,list,items,v1,[v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12]
+;
+; INPUTS:
+;       list - list of entry numbers to be printed, vector or scalar
+;               If list = -1, then all entries will be extracted.
+;               list may be converted to a vector by DBEXT 
+;       items - standard item list specification.  See DBPRINT for 
+;               the 6 different ways that items may be specified. 
+;
+; OUTPUTS:
+;       v1...v12 - the vectors of values for up to 12 items.
+;
+; EXAMPLE:
+;       Extract all RA and DEC values from the currently opened database, and
+;       place into the IDL vectors, IDLRA and IDLDEC.
+;
+;               IDL> DBEXT,-1,'RA,DEC',idlra,idldec
+;
+; HISTORY
+;       version 2  D. Lindler  NOV. 1987
+;       check for INDEXED items   W. Landsman   Feb. 1989
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Avoid EXECUTE() call for V6.1 or later  W. Landsman   December 2006
+;       Assume since V6.1   W. Landsman June 2009
+;-
+;*****************************************************************
+ On_error,2
+ compile_opt idl2
+
+ if N_params() lt 3 then begin
+        print,'Syntax - dbext, list, items, v1, [ v2, v3....v12 ]'
+        return
+ endif
+
+ zparcheck,'DBEXT',list,1,[1,2,3,4,5],[0,1],'Entry List'
+
+ db_item,items,it,ivalnum,idltype,sbyte,numvals,nbytes
+
+ nitems = N_elements(it)
+ nentries = db_info('entries')
+ if max(list) GT nentries[0] then $
+         message,db_info('name',0)+' entry numbers must be between 1 and ' + $
+         strtrim(nentries[0],2)
+ if nitems GT N_params()-2 then $
+        message,'Insufficient output variables supplied'
+ if nitems LT N_params()-2 then message, /INF, $
+        'WARNING - More output variables supplied than items specified'
+
+; get item info.
+
+ dbno = db_item_info('dbnumber',it)
+ if max(dbno) eq 0 then dbno=0 $                ;flag that it is first db only
+                  else dbno=-1
+ index = db_item_info('index',it)
+ ind = where( (index ge 1) and (index ne 3), Nindex ) 
+
+ if (Nindex eq nitems) and (dbno eq 0) then begin     ;All indexed items?
+
+        if N_elements(list) eq 1 then list = lonarr(1) + list
+        for i=0,nitems - 1 do begin                         ;Get indexed items
+          itind = it[ind[i]]
+   	  dbext_ind,list,itind,dbno,scope_varfetch('v' + strtrim(ind[i]+1,2))
+       endfor
+
+ endif else begin     
+
+         nvalues = db_item_info('nvalues',it)
+         dbext_dbf,list,dbno,sbyte,nbytes*nvalues,idltype,nvalues, $
+                    v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12
+
+ endelse
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dbext_dbf.pro b/Code/script_idl_mv/astrolib/dbext_dbf.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d56cadeac51ed8d58c993a0026b4ef786e0dfcb8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbext_dbf.pro
@@ -0,0 +1,152 @@
+pro dbext_dbf,list,dbno,sbyte,nbytes,idltype,nval,v1,v2,v3,v4,v5,v6, $
+        v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18, item_dbno=item_dbno
+	
+;+
+; NAME:
+;       DBEXT_DBF
+; PURPOSE:
+;       Subroutine of DBEXT to extract values of up to 18 items from a database 
+; EXPLANATION:
+;       This is a subroutine of DBEXT, which is the routine a user should 
+;       normally use.
+;
+; CALLING SEQUENCE:
+;       dbext_dbf,list,dbno,sbyte,nbytes,idltype,nval,v1,[ v2,v3,v4,v5,v6,v7,
+;                  v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18 ITEM_DBNO = ]
+;
+; INPUTS:
+;       list - list of entry numbers to extract desired items.   It is the 
+;               entry numbers in the primary data base unless dbno is greater 
+;               than or equal to -1.  In that case it is the entry number in 
+;               the specified data base.
+;       dbno - number of the opened db file
+;               if set to -1 then all data bases are included
+;       sbyte - starting byte in the entry.  If single data base then it must 
+;               be the starting byte for that data base only and not the 
+;               concatenation of db records 
+;       nbytes - number of bytes in the entry
+;       idltype - idl data type of each item to be extracted
+;       nval - number of values per entry of each item to be extracted
+;
+; OUTPUTS:
+;       v1...v18 - the vectors of values for up to 18 items
+;
+; OPTIONAL INPUT KEYWORD:
+;       item_dbno - A vector of the individual database numbers for each item.
+;               Simplifies the code for linked databases
+; PROCEDURE CALLS:
+;       DB_INFO(), DB_ITEM_INFO(), DBRD, DBXVAL(), IS_IEEE_BIG(), IEEE_TO_HOST
+; HISTORY
+;       version 1  D. Lindler  Nov. 1987
+;       Extract multiple valued entries    W. Landsman   May 1989
+;       William Thompson, GSFC/CDS (ARC), 1 June 1994
+;               Added support for external (IEEE) representation.
+;       Work with multiple element string items  W. Landsman  August 1995
+;       Increase speed for external databases on IEEE machines WBL August 1996
+;       IEEE conversion implemented on blocks of entries using BIG
+;       Added keyword ITEM_DBNO     R. Schwartz, GSFC/SDAC, August 1996
+;       Return a vector even if only 1 value W. Thompson  October 1996
+;       Change variable name of BYTESWAP to BSWAP  W. Thompson Mar 1997
+;       Use /OVERWRITE with reform   W. Landsman   May 1997
+;       Increase maximum number of items to 18  W. Landsman  November 1999
+;       2 May 2003, W. Thompson, Use DBXVAL with BSWAP instead of IEEE_TO_HOST.
+;       Avoid EXECUTE() for V6.1 or later  W. Landsman Jan 2007 
+;       Assume since V6.1  W. Landsman June 2009
+;       Change arrays to LONG to support entries >32767 bytes WL Oct 2010
+;-
+;
+ compile_opt idl2
+;*****************************************************************
+;
+COMMON db_com,qdb,qitems,qdbrec
+nitems=n_elements(sbyte)                                ;number of items
+external = db_info('external')                          ;External format?
+bswap = external * (~IS_IEEE_BIG() )              ;Need to byteswap?
+if dbno ge 0 then bswap = bswap[dbno] + bytarr(nitems) else $
+        if n_elements(item_dbno) eq nitems then bswap=bswap[item_dbno] $
+        else begin
+        sbyte1 = db_item_info('bytepos')
+        itnums = intarr(nitems)
+        for i=0,nitems-1 do itnums[i] = (where( sbyte[i] eq sbyte1))[0]
+        dbno1  = db_item_info('dbnumber', itnums)
+        bswap  = bswap[dbno1]
+endelse
+        
+scalar=0
+if n_elements(list) eq 1 then begin
+        scalar=1
+        savelist=list
+        list=lonarr(1)+list
+        if list[0] eq -1 then list=lindgen(db_info('entries',0))+1
+end
+nlist=n_elements(list)
+;
+; create a big array to hold all extracted values in
+; byte format
+;
+totbytes=total(nbytes)
+big=bytarr(totbytes,nlist)
+;
+; generate vector of bytes in entries to extract
+;
+index=lonarr(totbytes)
+ipos=0
+for i=0,nitems-1 do begin
+     for j=0,nbytes[i]-1 do index[ipos+j]=sbyte[i]+j
+     ipos=ipos+nbytes[i]
+endfor
+;
+; generate vector of byte positions in big for each item
+;
+bpos=lonarr(nitems)
+if nitems gt 1 then for i=1,nitems-1 do bpos[i]=bpos[i-1]+nbytes[i-1]
+;
+; loop on records and extract info into big
+;
+if dbno ge 0 then begin
+        ;
+        ; bypass dbrd for increased performance
+        ;
+        if dbno eq 0 then begin
+                for i=0L,nlist-1 do begin
+                    if list[i] ge 0 then begin
+                        entry=qdbrec[list[i]]
+                        big[0,i] = entry[index]
+                    endif
+                endfor
+            end else begin      ;mapped I/O
+                unit=db_info('unit_dbf',dbno)
+                rec_size=db_info('length',dbno)
+                for i=0L,nlist-1 do begin
+                    if list[i] ge 0 then begin
+                        p=assoc(unit,bytarr(rec_size,/nozero),rec_size*list[i])
+                        entry=p[0]
+                        big[0,i] = entry[index]
+                    end
+                endfor
+        end
+   end else begin
+        for i = 0L, nlist-1 do begin
+           if list[i] GE 0 then begin
+                dbrd,list[i],entry, /noconvert
+                big[0,i] = entry[index]
+            endif
+        end
+end
+;
+; now extract each value and convert to correct type
+;
+last = bpos + nbytes -1
+
+for i = 0,nitems-1 do begin
+    item = dbxval(big, idltype[i], nval[i], bpos[i], nbytes[i], bswap=bswap[i])
+    st = 'v' + strtrim(i+1,2)
+    if nlist GT 1 then $
+       (SCOPE_VARFETCH(st)) = reform(item,/overwrite) else $
+       (SCOPE_VARFETCH(st)) = [item]
+
+  endfor;for i loop on items
+;
+if scalar then list=savelist    ;restore scalar value
+return
+end
diff --git a/Code/script_idl_mv/astrolib/dbext_ind.pro b/Code/script_idl_mv/astrolib/dbext_ind.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a9466e70af9ca788046b9b5e0b4efaff881915a8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbext_ind.pro
@@ -0,0 +1,143 @@
+pro dbext_ind,list,item,dbno,values
+;+
+; NAME:
+;       DBEXT_IND
+; PURPOSE:
+;       routine to read a indexed item values from index file
+;
+; CALLING SEQUENCE:  
+;       dbext_ind,list,item,dbno,values
+;
+; INPUTS:
+;       list - list of entry numbers to extract values for
+;               (if it is a scalar, values for all entries are extracted)
+;       item - item to extract
+;       dbno - number of the opened data base
+;
+; OUTPUT:
+;       values - vector of values returned as function value
+; HISTORY:
+;       version 1  D. Lindler  Feb 88
+;       Faster processing of string values    W. Landsman   April, 1992
+;       William Thompson, GSFC/CDS (ARC), 30 May 1994
+;               Added support for external (IEEE) data format
+;       Allow multiple valued (nonstring) index items W. Landsman  November 2000      
+;       Use 64bit integer index for large databases W. Landsman  February 2001
+;       Fix sublisting of multiple valued index items W. Landsman  March 2001
+;       Check whether any supplied entries are valid W. Landsman Jan 2009
+;       Remove IEEE_TO_HOST    W. Landsman  Apr 2016
+;-
+On_error,2
+compile_opt idl2
+;
+if N_params() LT 4 then begin
+     print,'Syntax - DBEXT_IND, list, item, dbno, values'
+     return
+endif
+
+; Determine first and last block to extract
+;
+s=size(list) & ndim=s[0]
+if (ndim GT 0) then if (list[0] EQ -1) then ndim=0
+zeros = 0                               ;flag if zero's present in list
+if ndim EQ 0 then begin
+        minl = 1
+        maxl = db_info('ENTRIES',dbno)
+    end else begin
+        minl = min(list)
+        if minl EQ 0 then begin ;any zero values in list
+                zeros = 1
+                nonzero = where(list GT 0, Ngood, comp=bad)
+		if Ngood EQ 0 then message,'ERROR - No valid entry numbers supplied'
+                minl = min(list[nonzero])
+        endif
+        maxl=max(list)
+ end
+;
+; get item info
+;
+db_item,item,it,ivalnum,dtype,sbyte,numvals,nbytes
+nbytes = nbytes[0]
+if N_elements(it) GT 1 then $
+        message,'ERROR - Only one item can be extracted by dbext_ind'
+
+itnum = db_item_info('itemnumber',it[0])  ;item number in this dbno
+;
+; determine if indexed
+;
+index_type = db_item_info('index',it[0])
+if index_type EQ 0  then $
+        message,'ERROR - Requested item is not indexed'
+
+if index_type EQ 3 then $
+        message,'ERROR - Unsorted values of item not recorded in index file'
+;
+; get unit number of index file and read header info
+;
+ unit=db_info('UNIT_DBX',dbno)
+ external = db_info('EXTERNAL',dbno)     ;External (IEEE) data format?
+ p=assoc(unit,lonarr(2))
+ h=p[0]
+ if external then swap_endian_inplace,h,/swap_if_little
+ p = assoc(unit,lonarr(7,h[0]),8)
+ header = p[0]
+ if external then swap_endian_inplace,header,/swap_if_little
+ items = header[0,*]
+ pos = where(items EQ itnum, Nindex) & pos=pos[0]
+ if Nindex LT 1 then $
+        message,'Item not indexed, DBNO may be wrong'
+
+;
+; find starting location to read
+;
+if index_type NE 4 then sblock=header[4,pos] else sblock=header[6,pos]
+;
+numvals = numvals[0]
+sbyte = 512LL*sblock
+sbyte = sbyte+(minl-1L)*nbytes*numvals
+nv = (maxl-minl+1L) ;number of bytes to extract
+;            
+; create mapped i/o variable
+;
+dtype = dtype[0]
+
+if dtype NE 7 then begin
+   if numvals GT 1 then $ 
+   p = assoc(unit, make_array(size=[2,numvals,nv,dtype,0],/NOZERO), sbyte ) else $
+   p = assoc(unit, make_array(size=[1,nv,dtype,0],/NOZERO), sbyte ) 
+ endif else  p = assoc(unit, make_array(size=[2,nbytes,nv,1,0],/NOZERO), sbyte )
+
+;
+; read values from file
+; Modified, April 92 to delay conversion to string until the last step WBL
+;
+values = p[0]
+if external then swap_endian_inplace,values,/swap_if_little
+;
+; if subset list specified perform extraction
+;
+
+if ndim NE 0 then begin
+        if zeros then begin                     ;zero out bad values
+                if dtype NE 7 then begin        ;not a string?
+                        if numvals EQ 1 then begin
+                             values = values[(list-minl)>0 ]
+                             values[bad]=0
+                        endif else begin 
+                             values = values[*,(list-minl)>0 ]
+                             values[*,bad] = intarr(numvals)
+                        endelse
+                   end else begin                       ;string 
+                        values = values[*, (list-minl)>0 ]
+                        if N_elements(bad) EQ 1 then bad = bad[0]
+                        values[0,bad] = replicate( 32b, nbytes )
+                   endelse
+           end else begin
+                  if (dtype EQ 7) || (numvals GT 1) then  $
+                            values = values[*, list-minl] $
+                      else  values = values[ list-minl ]
+        end
+end
+if dtype EQ 7 then values = string(values)
+return
+end
diff --git a/Code/script_idl_mv/astrolib/dbfind.pro b/Code/script_idl_mv/astrolib/dbfind.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f2bc14469e06fa96d54f1d86595f7f1f69b1f0ec
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbfind.pro
@@ -0,0 +1,382 @@
+function dbfind,spar,listin,SILENT=silent,fullstring = Fullstring,      $
+        errmsg=errmsg, Count = count
+;+
+; NAME: 
+;    DBFIND()
+; PURPOSE:      
+;     Search data base for entries with specified characteristics
+; EXPLANATION:  
+;     Function to search data base for entries with specified
+;     search characteristics.
+;
+; CALLING SEQUENCE:     
+;     result = dbfind(spar,[ listin, /SILENT, /FULLSTRING, ERRMSG=, Count = ])
+;
+; INPUTS:       
+;     spar - search_parameters (string)...each search parameter 
+;               is of the form:
+;
+;               option 1) min_val < item_name < max_val
+;               option 2) item_name = value
+;               option 3) item_name = [value_1, value_10]
+;                       Note: option 3 is also the slowest.
+;               option 4) item_name > value
+;               option 5) item_name < value
+;               option 6) item_name = value(tolerance) ;eg. temp=25.0(5.2)
+;               option 7) item_name                     ;must be non-zero
+;
+;               Multiple search parameters are separated by a comma.
+;               eg.     'cam_no=2,14<ra<20'
+;
+;               Note: < is interpreted as less than or equal.
+;                     > is interpreted as greater than or equal.
+;       
+;               RA and DEC keyfields are stored as floating point numbers 
+;               in the data base may be entered as HH:MM:SEC and
+;               DEG:MIN:SEC. Where:
+;
+;                       HH:MM:SEC   equals  HH + MM/60.0  + SEC/3600.
+;                       DEG:MIN:SEC equals DEG + MIN/60.0 + SEC/3600.
+;                       
+;               For example:
+;                       40:34:10.5 < dec < 43:25:19 , 8:22:1.0 < ra < 8:23:23.0
+;
+;               Specially encoded date/time in the data base may
+;               be entered by  CCYY/DAY:hr:min:sec which is
+;               interpreted as  
+;                       CCYY*1000+DAY+hr/24.0+min/24.0/60.+sec/24.0/3600.
+;               If a two digit year is supplied and YY GE 40 then it is 
+;               understood to refer to year 1900 +YY;  if YY LT 40 then it is 
+;               understood to refer to year 2000 +YY
+
+;               For example
+;                       1985/201:10:35:30<date_time<1985/302:10:33:33.4
+;               would specify all entries between:
+;                       year 1985 day 201 at 10:35:30 to
+;                       day 302 at 10:33:33.4
+;               The date/time may also be encoded as:
+;                       DD-MMM-YEAR HH::MM:SS.SS        
+;                       eg.  12-JUL-86 10:23:33.45
+;               (this is the format of system variable !stime)
+;
+;               Multiple search parameters may be stored in a string
+;               array (one parameter per array element) instead of
+;               concatenating them with commas in a single string.
+;               Example:
+;                       input_array = strarr(2)
+;                       input_array[0] = '14<ra<16'   ; 14-16 hrs of ra.
+;                       input_array[1] = '8<dec<20'   ; + 8-20 deg. decl.
+;
+; OPTIONAL INPUT:       
+;       listin - gives list of entries to be searched.  If not supplied or 
+;               set to -1 then all entries are searched.
+;
+; OUTPUT:       
+;       List of ENTRY numbers satisfying search characteristics
+;               is returned as the function value.
+;
+; OPTIONAL INPUT KEYWORDS:      
+;       /SILENT  - If the keyword SILENT is set and non-zero, then DBFIND
+;               will not print the number of entries found.
+;
+;       /FULLSTRING - By default, one has a match if a search string is 
+;               included in any part of a database value (substring match).   
+;               But if /FULLSTRING is set, then all characters in the database
+;               value must match the search string (excluding leading and 
+;               trailing blanks).    Both types of string searches are case
+;               insensitive.
+;
+;       ERRMSG   = If defined and passed, then any error messages will
+;                  be returned to the user in this parameter rather
+;                  than depending on the MESSAGE routine in IDL.  If no
+;                  errors are encountered, then a null string is
+;                  returned.  In order to use this feature, ERRMSG must
+;                  be defined first, e.g.
+;
+;                       ERRMSG = ''
+;                       DB_ITEM, ERRMSG=ERRMSG, ...
+;                       IF ERRMSG NE '' THEN ...;
+;
+; OPTIONAL OUTPUT KEYWORD:
+;       COUNT - Integer scalar giving the number of valid matches
+; PROCEDURE CALLS:
+;       DB_INFO, DB_ITEM, DB_ITEM_INFO, DBEXT, DBEXT_IND, DBFIND_ENTRY,
+;       DBFIND_SORT, DBFPARSE, DBRD, DBSEARCH, ZPARCHECK,IS_IEEE_BIG
+;
+; RESTRICTIONS: 
+;       The data base must be previously opened with DBOPEN.
+;
+; SIDE EFFECTS: 
+;       The obsolete system variable !ERR is set to number of entries found
+;
+; REVISION HISTORY:
+;       Written     :   D. Lindler, GSFC/HRS, November 1987
+;       Version 2, Wayne Landsman, GSFC/UIT (STX), 1 April 1994
+;                       Added FULLSTRING keyword.
+;       Version 3, William Thompson, GSFC, 1 April 1994
+;                       Added check for empty database
+;       Version 4, William Thompson, GSFC, 5 April 1994
+;                       Changed so that !ERR is zero when database is empty,
+;                       and LISTIN is returned, based on discussion with Wayne
+;                       Landsman.
+;       Version 5, Wayne Landsman, GSFC/UIT (STX), 26 May 1994
+;                       Added error message when database is empty.
+;       Version 6, William Thompson, GSFC, 14 March 1995
+;                       Added FULLSTRING keyword to DBFIND_SORT call
+;       Version 7, Richard Schwartz, GSFC/SDAC 23 August 1996
+;                       Move external to host conversion from DBRD to
+;                       operation on extracted values only.
+;       Version 8, William Thompson, GSFC, 3 December 1996
+;                       Renamed BYTESWAP variable to BSWAP--appeared to be
+;                       conflicting with function of same name.
+;       Version 9, William Thompson, GSFC, 17-Mar-1997
+;                       Added keyword ERRMSG
+;       Version 10, July, 1997  W. Landsman, added CATCH errors
+;       Converted to IDL V5.0   W. Landsman   October 1997
+;       Update documentation for new Y2K compliant DBFPARSE W. Landsman Nov 1998
+;       Suppress empty database message with /SILENT, W. Landsman Jan 1999
+;       Added COUNT keyword, deprecate !ERR        W. Landsman March 2000
+;       Added new unsigned & 64bit datatypes       W. Landsman July 2001
+;       Fix possible floating illegand operand error W. Landsman July 2009
+;       Change arrays to LONG to support entries >32767 bytes W.L. Oct. 2010
+;       Delay warning now for 10000 instead of 2000 entries W.L. Aug 2014
+;-
+;
+; ---------------------------------------------------------------------
+
+On_error,2                          ;return to caller
+;
+; Check parameters.  If LISTIN supplied, make sure all entry values are
+; less than total number of entries.
+;
+ count = 0
+ zparcheck,'dbfind',spar,1,7,[0,1],'search parameters'
+
+ catch, error_status
+ if error_status NE 0 then begin 
+        print,!ERR_STRING
+        if N_elements(listin) NE 0 then return,listin else return, -1
+ endif
+ nentries = db_info( 'ENTRIES',0 )              ;number of entries
+ if ( N_params() LT 2 ) then listin = -1  else begin
+      zparcheck,'dbfind',listin,2,[1,2,3],[0,1],'entry list'
+      maxlist = max(listin)
+      if ( maxlist GT nentries ) then begin
+         message = 'Entry list values (second parameter) must be less than '+ $
+                strtrim(nentries,2)
+         goto, handle_error
+      endif
+ endelse
+ if nentries eq 0 then begin                    ;Return if database is empty
+        !err = 0 
+        if not keyword_set(SILENT) then message, $
+            'ERROR - No entries in database ' + db_info("NAME",0),/INF
+        return,listin
+ endif
+;
+; parse search parameter string
+;
+ dbfparse,spar,items,stype,search_values
+ nitems = N_elements(items)             ;number of items
+;
+; set up initial search list
+;
+list  = listin
+s=size(list) & ndim=s[0]
+if ndim EQ 0 then list=lonarr(1)+list
+;
+; get some item info
+;
+db_item,items,it,ivalnum,idltype,sbyte,numvals,nbytes,errmsg=errmsg
+IF N_ELEMENTS(ERRMSG) NE 0 THEN IF ERRMSG NE '' THEN BEGIN
+        MESSAGE = ERRMSG
+        GOTO, HANDLE_ERROR
+ENDIF
+index = db_item_info('INDEX',it)                        ;index type
+dbno = db_item_info('DBNUMBER',it)                      ;data base number
+                                                        ; particular db.
+;
+; get info on the need to byteswap item by item
+;
+external = db_info('external')                          ;External format?
+bswap = external * (not IS_IEEE_BIG() )              ;Need to byteswap?
+dbno1  = db_item_info('dbnumber', it)
+bswap  = bswap[dbno1]
+
+done=bytarr(nitems)                                     ;flag for completed
+                                                        ; items
+;----------------------------------------------------------------------
+; ENTRY number is a search parameter?
+;
+for pos = 0,nitems-1 do begin
+    if (it[pos] eq 0) then begin
+        dbfind_entry,stype[pos],search_values[pos,*],nentries,list,count=count
+        done[pos]=1                           ;flag as done
+        if count LT 1 then goto, FINI            ;any found
+     end
+end     
+;----------------------------------------------------------------------
+;
+; perform search on sorted items in the first db
+;
+
+for pos=0,nitems-1 do begin
+     if(not done[pos]) and (dbno[pos] eq 0) and $
+        (index[pos] ge 2) then begin
+                dbfind_sort,it[pos],stype[pos],search_values[pos,*],list, $
+                        fullstring=fullstring, Count = count
+                if !err ne -2 then begin
+                        if count lt 1 then goto,FINI 
+                        done[pos]=1
+                end
+     end
+end
+; ------------------------------------------------------------------------
+; Perform search on items in lookup file (indexed items) in first db
+;
+if total(done) eq nitems then goto,FINI
+for pos=0,nitems-1 do begin
+    if(not done[pos]) and (dbno[pos] eq 0) and (index[pos] ne 0) then begin
+            dbext_ind,list,it[pos],0,values
+            dbsearch, stype[pos], search_values[pos,*], values, good, $
+                Fullstring = fullstring, Count = count
+            if !err eq -2 then begin 
+                print,'DBFIND - Illegal search value for item ', $
+                       db_item_info('name',it[pos])
+                       return,listin
+            endif
+            if count lt 1 then goto, FINI        ;any found
+            if list[0] ne -1 then list=list[good] else list=good+1
+            done[pos]=1                         ; DONE with that item
+    end
+end
+
+;------------------------------------------------------------------------
+;
+; search index items in other opened data bases (if any)
+;
+found=where( (index gt 0) and (dbno ne 0 ), Nfound)
+if Nfound gt 0 then begin
+      db = dbno[ where(dbno NE 0) ]
+      for i = 0, n_elements(db)-1 do begin
+;
+; find entry numbers of second database corresponding to entry numbers
+; in the first data base.
+;
+        pointer=db_info('pointer',db[i])        ;item which points to it
+;
+        dbext,list,pointer,list2        ;extract entry numbers in 2nd db
+        good=where(list2 ne 0,ngood)    ;is there a valid pointer
+        if ngood lt 1 then goto, FINI 
+        if list[0] eq -1 then list=good+1 else list=list[good]
+        list2=list2[good]
+        for pos=0,nitems-1 do begin
+            if (not done[pos]) and (dbno[pos] eq db[i]) and (index[pos] ne 0) $
+                              and (index[pos] ne 3) then begin
+                    dbext_ind,list2,it[pos],dbno[pos],values
+                    dbsearch, stype[pos], search_values[pos,*], values, good, $
+                        fullstring = fullstring, count = count
+                    if !err eq -2 then begin
+                       message = 'Illegal search value for item ' + $
+                               db_item_info('name',it[pos])
+                       goto, handle_error
+                    endif
+                    if count lt 1 then goto, FINI        ;any found
+                    if list[0] ne -1 then list=list[good] else list=good+1
+                    list2=list2[good]
+                    done[pos]=1                         ; DONE with that item
+            endif
+        endfor
+     endfor
+endif           
+;---------------------------------------------------------------------------
+; search remaining items
+;
+
+  if list[0] eq -1 then list= lindgen(nentries)+1       ;Fixed WBL Feb. 1989
+  count = N_elements(list)
+  !err = count
+  if total(done) eq nitems then goto, FINI      ;all items searched
+
+  nlist     = N_elements(list)        ;number of entries to search
+  if nlist GT 10000 then begin
+        print,'Non-indexed search on ',strtrim(nlist,2),' entries'
+        print,'Expect Delay'
+  end
+;
+; Create array to hold values of all remaining items...a big one.
+;
+  left = where( done EQ 0, N_left )           ;items left
+  nbytes = nbytes[left]
+  sbyte = sbyte[left]
+  idltype = idltype[left]
+  bswap = bswap[left]
+  totbytes  = total(nbytes)           ;total number of bytes to extract
+  big  = bytarr(totbytes,nlist)   ;array to store values of the items
+;
+; generate starting position in big for each item
+;
+  bpos  = lonarr(N_left)        ;starting byte in bpos of each item
+  if N_left GT 1 then for i=1,N_left-1 do bpos[i] = bpos[i-1]+nbytes[i-1]
+
+  index = lonarr(totbytes)      ;indices of bytes to extract
+  ipos  = 0                     ;position in index array
+  for i = 0,N_left-1 do begin   ;loop on items
+    for j=0,nbytes[i]-1 do index[ipos+j]=sbyte[i]+j     ;position in entry
+    ipos = ipos + nbytes[i]
+  end;for
+
+;
+; loop on entries and extract info
+;
+  for ii = 0L, nlist-1L do begin
+    dbrd,list[ii],entry, /noconvert                 ;read entry
+    big[0,ii]= entry[index]
+  endfor
+
+;
+; now extract values for each item and search for valid ones
+;
+  stillgood  = lindgen( nlist )
+
+  for i = 0l,N_left-1 do begin
+        if i Eq 0 then val = big[ bpos[i]:bpos[i]+nbytes[i]-1, 0:nlist-1 ] else $
+        val = big[ bpos[i]:bpos[i]+nbytes[i]-1, stillgood ]
+        if bswap[i] then ieee_to_host, val, idltype=idltype[i]
+       case idltype[i] of
+                1: v = byte(val,0,nlist)        ;byte
+                2: v = fix(val,0,nlist)         ;i*2
+                3: v = long(val,0,nlist)        ;i*4
+                4: v = float(val,0,nlist)       ;r*4
+                5: v = double(val,0,nlist)      ;r*8
+                7: v = string(val)               ;string
+                12: v = uint(val,0,nlist)         ;u*2
+               13: v = ulong(val,0,nlist)        ;u*4
+               14: v = long64(val,0,nlist)       ;i*8
+               15: v = ulong64(val,0,nlist)      ;u*8
+         endcase
+        dbsearch, stype[left[i]], search_values[left[i],*], v, good, $
+                Fullstring = fullstring, count = count
+        if count LT 1 then goto, FINI 
+        stillgood=stillgood[good]
+	nlist = count
+  endfor
+  list = list[stillgood]
+  count = N_elements(list) & !ERR = count
+
+FINI:
+if not keyword_set(SILENT) then begin
+  print,' ' & print,' '
+  if count LE 0  then $
+        print,'No entries found by dbfind in '+ db_info('name',0) $
+  else $
+        print,count,' entries found in '+ db_info('name',0)
+endif
+if count LE 0 then return,intarr(1) else return,list[sort(list)]
+;
+;  Error handling point.
+;
+HANDLE_ERROR:
+        IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = 'DBFIND: ' + MESSAGE $
+                ELSE MESSAGE, MESSAGE
+end
diff --git a/Code/script_idl_mv/astrolib/dbfind_entry.pro b/Code/script_idl_mv/astrolib/dbfind_entry.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f15fdbd00f19e5afd74958b50347fc4506039e7a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbfind_entry.pro
@@ -0,0 +1,117 @@
+pro dbfind_entry,type,svals,nentries,values,Count = count
+;+
+; NAME:
+;       DBFIND_ENTRY
+; PURPOSE:
+;       Subroutine of DBFIND to perform an entry number search 
+; EXPLANATION:
+;       This is a subroutine of dbfind and is not a standalone procedure
+;       It performs a entry number search.
+;
+; CALLING SEQUENCE:
+;       dbfind_entry, type, svals, nentries, values, [COUNT = ]
+;
+; INPUTS: 
+;       type - type of search (output from dbfparse)
+;       svals - search values (output from dbfparse)
+;       values - array of values to search
+; OUTPUT:
+;       good - indices of good values
+; OPTIONAL OUTPUT KEYWORD:
+;       Count - integer scalar giving the number of valid matches
+; SIDE EFFECTS"
+;       The obsolete system variable !err is set to number of good values
+;
+; REVISION HISTORY:
+;       D. Lindler  July,1987
+;       Fixed test for final entry number  W. Landsman    Sept. 95       
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added COUNT keyword, deprecate !ERR  W. Landsman   March 2000
+;       Better checking of out of range values  W. Landsman February 2002
+;-
+sv0=long(strtrim(svals[0],2)) & sv1=long(strtrim(svals[1],2))
+
+if values[0] eq -1 then begin           ;start with all entries
+    case type of
+
+         0:  begin
+                if (sv0 gt 0) and (sv0 le nentries) then begin  ;Update Sep 95
+                        values=lonarr(1)+sv0
+                        count=1
+                   end else count= 0
+             end
+        -1: begin
+                 if nentries LT sv0 then count = 0 else begin
+                    values=lindgen(nentries-sv0+1) + sv0   ;value>sv0
+                    count=nentries-sv0+1
+                 endelse
+            end
+        -2: begin
+                values= lindgen(sv1>1<nentries)+1       ;value<sv1
+                count=sv1>1<nentries
+            end
+        -3: begin                                       ;sv0<value<sv1
+            if sv1 lt sv0 then begin
+                temp=sv0
+                sv0=sv1
+                sv1=temp
+            end
+            if (sv1 LT 1) or (sv0 GT nentries) then count = 0 else begin
+               sv0=sv0>1
+               sv1=sv1<nentries
+               values=lindgen(sv1-sv0+1)+sv0
+               count=sv1-sv0+1
+            endelse 
+            end         
+        -5: begin                               ;sv1 is tolerance
+            minv=(sv0-abs(sv1))>1
+            maxv=(sv0+abs(sv1))<nentries
+            values=lindgen(maxv-minv+1)+minv
+            count=maxv-minv+1
+            end
+        -4:                                     ;non-zero
+        else: begin                             ;set of values
+              sv=lonarr(type)
+              for i=0L,type-1 do sv[i]=long(strtrim(svals[i],2))
+              good=where((sv gt 0) and (sv le nentries), count)
+              if count gt 0 then values=sv[good]
+              end
+    endcase
+    if count GT 0 then !ERR = count else !ERR = -1
+  end else begin                                        ;input list supplied
+    case type of
+ 
+        0:  good=where(values eq sv0, count)            ;value=sv0
+        -1: good=where(values ge sv0, count)            ;value>sv0
+        -2: good=where(values le sv1, count)            ;value<sv1
+        -3: begin                               ;sv0<value<sv1
+            if sv1 lt sv0 then begin
+                temp=sv0
+                sv0=sv1
+                sv1=temp
+            end
+            good=where((values ge sv0) and (values le sv1), count)
+            end         
+        -5: begin                               ;sv1 is tolerance
+            minv=sv0-abs(sv1)
+            maxv=sv0+abs(sv1)
+            good=where((values ge minv) and (values le maxv), count)
+            end
+        -4: good=where(values, count)                   ;non-zero
+        else: begin                             ;set of values  
+              count=0                              ;number found
+              for i=0L,type-1 do begin          ;loop on possible values    
+                g=where(values eq long(strtrim(svals[i],2)), nfound)
+                if nfound gt 0 then begin
+                        if nf eq 0 then good=g else good=[good,g]
+                        count = count +nfound
+                end
+              end
+              !err=count
+              end
+    endcase
+    if count le 0 then return
+    values=values[good]
+end
+return
+end
diff --git a/Code/script_idl_mv/astrolib/dbfind_sort.pro b/Code/script_idl_mv/astrolib/dbfind_sort.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6ec5fc124e580bfb013c04c1c1eadc252f68fea6
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbfind_sort.pro
@@ -0,0 +1,275 @@
+pro dbfind_sort,it,type,svals,list, FULLSTRING = fullstring, COUNT = number
+;+
+; NAME:
+;       DBFIND_SORT   
+; PURPOSE:
+;       Subroutine of DBFIND to perform a search using sorted values 
+; EXPLANATION:
+;       This is a subroutine of dbfind and is not a standalone procedure
+;       It is used to limit the search using sorted values  
+;
+; CALLING SEQUENCE:
+;       dbfind_sort, it, type, svals, list, [/FULLSTRING, COUNT = ]
+;
+; INPUT: 
+;       it - item number, scalar
+;       type - type of search (output from dbfparse)
+;       svals - search values (output from dbfparse)
+;
+; INPUT/OUTPUT:
+;       list - found entries
+;
+; OPTIONAL INPUT KEYWORD:
+;       /FULLSTRING - By default, one has a match if a search string is 
+;               included in any part of a database value (substring match).   
+;               But if /FULLSTRING is set, then all characters in the database
+;               value must match the search string (excluding leading and 
+;               trailing blanks).    Both types of string searches are case
+;               insensitive.
+; OPTIONAL OUTPUT KEYWORD
+;       Count - Integer scalar giving the number of matches found
+; SYSTEM VARIABLES:
+;       The obsolete system variable !err is set to number of good values
+;       !ERR = -2 for an invalid search
+; PROCEDURES CALLED:
+;       DB_INFO(), DB_ITEM_INFO(), DBSEARCH() 
+; REVISION HISTORY:
+;       D. Lindler  July,1987
+;       William Thompson, GSFC/CDS (ARC), 30 May 1994
+;               Added support for external (IEEE) data format
+;       William Thompson, GSFC, 14 March 1995 Added keyword FULLSTRING
+;       Minimize use of obsolete !ERR variable   W. Landsman  February 2000
+;       Added COUNT keyword, deprecate !ERR W. Landsman  March 2000
+;       Use 64 bit integers V5.2 or later
+;       Include new IDL unsigned & 64 bit integer datatypes W.Landsman July 2001
+;       Make sure returned list vector is LONG  W. Landsman August 2001
+;       Work on string items   W. Landsman November 2009
+;       Don't use VALUE_LOCATE on a single value  W. Landsman November 2009
+;       Use VALUE_LOCATE even for equal values W. Landsman December 2009
+;       Fix bug allowing negative FIRST values, William Thompson, 10 May 2010
+;-
+;----------------------------------------------------------------------------
+;       READ EVERY 512TH VALUE IN SORTED VALUES
+;
+; get item info
+;
+itnum = db_item_info('itemnumber',it)   ;item number in this dbno
+index_type = db_item_info('index',it)
+;
+; get unit number of index file and read header info
+;
+unit = db_info('UNIT_DBX',0)
+external = db_info('EXTERNAL',0)
+pi = assoc(unit,lonarr(2))
+h = pi[0]
+if external then swap_endian_inplace,h,/swap_if_little
+pi = assoc(unit,lonarr(7,h[0]),8)
+header = pi[0]
+if external then swap_endian_inplace,header,/swap_if_little
+items = header[0,*]
+pos = where(items EQ itnum) & pos=pos[0]
+; 
+; find starting location to read
+;
+sblock = header[3,pos]
+sbyte = 512LL*sblock
+nv = (db_info('ENTRIES',0)+511)/512
+nbytes = db_item_info('NBYTES',it)
+;
+; create mapped i/o variable
+;
+dtype = db_item_info('IDLTYPE',it)
+if dtype NE 7 then  $
+  p = assoc(unit,make_array( size=[1,nv,dtype[0],0],/NOZERO), sbyte) else $
+    p = assoc(unit,make_array( size=[2,nbytes,nv,1,0],/NOZERO), sbyte)
+    
+numbyte = [0,1,2,4,4,8,0,nbytes,16,0,0,0,2,4,8,8]
+num_bytes = numbyte[ dtype[0] ]
+;
+; read values from file (for every 512th entry)
+;
+
+values=p[0]
+if dtype EQ 7 then values = string(values) else $
+if external then swap_endian_inplace,values,/swap_if_little
+;
+;------------------------------------------------------------------
+; CONVERT INPUT SVALS TO CORRECT DATA TYPE
+;
+; determine data type of values to be searched
+;
+s=size(values) & nv = N_elements(values)
+;
+; convert svals
+;
+nvals = type>2
+sv=replicate(values[0],nvals)
+for i=0L,nvals-1 do sv[i]=strtrim(svals[i],2)
+sv0 = sv[0] & sv1 = sv[1]
+
+;
+;--------------------------------------------------------------------------
+; FIND RANGE OF VALID SUBSCRIPTS IN LIST
+;
+;
+if nv EQ 1 then begin 
+    first = 0 & last = 1
+endif else begin     
+
+case type of
+ 
+        0: begin                                ;value=sv0
+               first = value_locate(values,sv0) > 0  
+	       last = (first +1) < nv 
+	       while values[first] EQ sv0 do begin 
+		    if first EQ 0 then break
+	            first = first-1
+	       endwhile	
+	                 
+           end
+
+        -1: begin                               ;value>sv0
+                first = value_locate(values,sv0) > 0                
+                last = nv
+	        while values[first] EQ sv0 do begin 
+		    if first EQ 0 then break
+	            first = first-1
+	        endwhile	
+            end
+
+        -2: begin                               ;value<sv1
+                first = 0
+		last = (value_locate(values,sv1) + 1) < nv > first 
+	        while values[first] EQ sv0 do begin 
+		    if first EQ 0 then break
+	            first = first-1
+	        endwhile	
+             end
+
+        -3: begin                               ;sv0<value<sv1
+           
+
+            if sv1 LT sv0 then begin
+                temp = sv0
+                sv0 = sv1
+                sv1 = temp
+            end
+                 first = value_locate(values,sv0) > 0                
+ 		 last = (value_locate(values,sv1) + 1) < nv > 0
+	         while values[first] EQ sv0 do begin 
+		    if first EQ 0 then break
+	            first = first-1
+	         endwhile	
+  
+             end 
+        -5: begin                               ;sv1 is tolerance
+
+            minv = sv0-abs(sv1)
+            maxv = sv0+abs(sv1)
+                good = where(values LT minv, N)
+                if N LT 1 then first=0 else first=N-1
+                good = where(values GT maxv, N)
+                if N LT 1 then last=nv else last=good[0]
+	       while values[first] EQ sv0 do begin 
+		    if first EQ 0 then break
+	            first = first-1
+	       endwhile	
+            end
+
+        -4: begin                       ;non-zero
+                if values[0] EQ 0 then begin
+                        good=where(values EQ 0, N)
+                        first=N-1
+                        last=nv
+                 end else begin ;not allowed
+                        !err=-2
+                        return
+                end
+           end
+        else: begin                             ;set of values
+              sv0 = min(sv[0:type-1]) & sv1 = max(sv[0:type-1])
+                good=where(values LT sv0, N)
+                if N LT 1 then first=0 else first=N-1
+                good=where(values GT sv1, N)
+                if N LT 1 then last=nv else last=good[0]
+              end
+endcase
+endelse
+;-----------------------------------------------------------------------------
+; we now know valid values are between index numbers first*512 to last*512
+;
+if first EQ last then begin
+        !err=0
+        return
+end
+;
+; extract data values for blocks first to last
+;
+sblock=header[4,pos]            ;starting block for sorted data
+sbyte=512LL*sblock               ;starting byte
+first=first*512L+1
+last=(last*512L) < db_info('entries',0)
+number=last-first+1
+if dtype NE 7 then $
+p = assoc(unit,make_array(size=[1,number,dtype,0],/nozero), $
+                                             sbyte+(first-1)*num_bytes) else $
+    p = assoc(unit,make_array( size=[2,nbytes,number,1,0],/NOZERO), $
+			      sbyte+(first-1)*num_bytes)
+			      
+values=p[0]
+
+if dtype EQ 7 then values = string(values) else $
+if external then swap_endian_inplace,values,/swap_if_little
+;
+; if index type is 2, data base is sorted on this item, first and last
+; give range of valid entry numbers
+;
+
+if index_type EQ 2 then begin
+        if list[0] EQ -1 then begin
+                list=lindgen(number)+first
+           end else begin
+                good=where((list ge first) and (list le last), number)
+                if number GT  0 then begin
+                         list=list[good]
+                         values=values[list-first]
+                endif
+        end
+;
+; if index type wasn't 2 the item was sorted and index numbers must
+;       be read
+;
+
+end else begin
+;
+; find starting location to read
+;
+        sblock=header[5,pos]
+        sbyte=512LL*sblock
+;
+; read values from file
+;
+p = assoc(unit,make_array(size=[1,number,3,0],/nozero),sbyte+(first-1)*4)
+        if list[0] EQ -1 then begin
+                list=p[0]
+                if external then byteorder,list, /NTOHL
+           end else begin
+                list2=p[0]
+                if external then byteorder,list2,/NTOHL   ;Fixed typo Jan 2010
+                match,list,list2,suba,subb, Count = number
+                if number GT 0 then begin
+                         list=list[suba]
+                        values=values[subb]
+                end
+        end
+end
+;
+; now search indiviual entries
+;
+if number GT 0 then begin
+        dbsearch,type,svals,values,good,fullstring=fullstring, Count = number
+        if number GT 0 then list=list[good]
+end
+!err=number
+return
+end
diff --git a/Code/script_idl_mv/astrolib/dbfparse.pro b/Code/script_idl_mv/astrolib/dbfparse.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0218c20d93b7430bfaec16d7ded5ea6fe5e52ba4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbfparse.pro
@@ -0,0 +1,240 @@
+pro dbfparse, spar, items, stype, values
+;+
+; NAME:
+;     DBFPARSE
+; PURPOSE:
+;     Parse the search string supplied to DBFIND.   Not a standalone routine
+;
+; CALLING SEQUENCE:
+;     DBFPARSE, [ spar, items, stype, values ]
+;
+; INPUTS:
+;     spar - search parameter specification, scalar string
+;
+; OUTPUTS:
+;     items - list of items to search on
+;     stype - search type, numeric scalar
+;               0    item=values[j,0]
+;               -1   item>values[j,0]
+;               -2   item<values[j,1]
+;               -3   values[j,0]<item<values[j,1]
+;               -4   item is non zero
+;               -5   item=values[j,0] within tolerance values[j,1]
+;               0<   items in list values[j,i] for i=0,stype-1
+;     values - search values, 20 x 10 string array, can parse a string
+;               with up to 20 items specifications, each item can have 10
+;               values
+;
+; REVISION HISTORY:  
+;     D. Lindler NOV, 1987
+;     Check for valid numeric values before assuming a date string
+;     W. Landsman                    July, 1993
+;     Accept four digit years when in ccyy/doy format W. Landsman   October 1998
+;     Don't do DATE/Time test for string items  W. Landsman   July 2006
+;     No tolerance search allowed for strings, so allow parenthesis within string
+;         W. Landsman Feb 2015
+;-
+;--------------------------------------------------------------
+ On_error,2
+;
+; parse string array search parameters into a single string.
+;
+  par  = strjoin( strtrim( spar, 2),',')    ;Make into a scalar if necessary
+ 
+  items = strarr(20)                 ;array of items
+  values = strarr(20,10)              ;range limited to 10 elements/item.
+  stype = intarr(20)                  ;search type for item j
+                                        ;   0    item=values(j,0)
+                                        ;   -1   item>values(j,0)
+                                        ;   -2   item<values(j,1)
+                                        ;   -3   values(j,0)<item<values(j,1)
+                                        ;   -4   item is non zero
+                                        ;   -5   item=values(j,0) within
+                                        ;         tolerance values(j,1)
+                                        ;   0<   items in list values(j,i)
+                                        ;             for i=0,stype-1
+;
+; parse par
+;
+nitems  = 0
+while par ne '' do begin
+               
+  ;
+  ;  Concatenated array. A normal seach involves using comma's as
+  ;    delimiter. For concatenation array, the brackets must be
+  ;    found (both beginning and end) prior to extracting item
+  ;    search information. This is done once at a time as each
+  ;    search item is deciphered.
+  ;
+    strparam = strpos(par,'[')
+    if (strparam lt strpos(par,',')) and (strparam gt 0) then begin
+       next = gettok(par,']')             ; just the concatenation portion.
+       next = next + ']'                  ; put it back.
+       par=strtrim(par,2)                 ; trim blanks
+       par  = strmid(par,1,strlen(par)-1) ; eat next comma.
+    end else next=gettok(par,',')         ; get next search item
+    par=strtrim(par,2)                    ;trim blanks
+
+    case 1 of
+
+    ;
+    ;    Concatenation array...
+    ;       item=[value1,value2,...]
+    ;
+    (strpos(next,'[') gt 0): begin       ; explicit range.
+             items[nitems]=gettok(next,'='); get item name
+           ;
+           ; that leaves brackets and indices.
+           ;
+             junk = gettok( next, '[' )
+             vals = gettok( next, ']' )
+             nvals=0
+             while vals ne '' do begin
+                values[nitems,nvals]=gettok(vals,',')
+                nvals++
+                if nvals GE 10 then message, $ 
+    'No more than 10 values/item allowed; use DBMATCH or DBGET instead'
+             endwhile
+             stype[nitems] = nvals
+             end
+             
+    ;
+    ;  item=value(tolerance) 
+    ;  Updated Feb 2015 to allow parenthesis within string searches 
+    ;
+    (strpos(next,'=') gt 0): begin      ; equality specified
+             items[nitems]=gettok(next,'='); get item name
+             db_item,items[nitems],itnum,ivalnum,idltype
+             if idltype EQ 7 then begin
+               values[nitems,0] = next
+               stype[nitems] = 0
+             endif else begin  
+             values[nitems,0]=gettok(next,'('); value for item
+             stype[nitems]=0
+  
+             if next ne '' then begin   ;tolerance supplied
+                values[nitems,1]=gettok(next,')')
+                stype[nitems] = -5
+             endif
+             endelse
+             end
+    ;
+    ; minimum supplied?   item>value
+    ;
+    (strpos(next,'>') gt 0): begin
+             items[nitems]=gettok(next,'>');get item name
+             values[nitems,0]=next         ;get minimum value
+             stype[nitems]=-1
+             end
+    ;
+    ;  Range specified or maximum specified.
+    ;
+      (strpos(next,'<') gt 0): begin    ; form is min<item<max
+             ltpos=strpos(next,'<')
+             if strpos(next,'<',ltpos+1) ge 0 then begin
+        ;
+        ;  range specified   value1<item<value2
+        ;
+                values[nitems,0]  = gettok(next,'<')    ;minimum value
+                items[nitems] = gettok(next,'<')        ; get item name.
+                values[nitems,1]=next                   ;whats left is max.
+                stype[nitems]=-3
+               end else begin
+        ;
+        ;  maximum specified
+        ;
+                items[nitems] = gettok(next,'<')
+                values[nitems,1]=next
+                stype[nitems]=-2
+             end
+           end
+        ;
+        ; non zero value specified  item not equal to 0
+        ;               
+      else: begin
+                items[nitems]=next
+                stype[nitems]=-4
+                end
+      endcase
+      nitems=nitems+1
+  end; while
+
+;
+; truncate arrays down to proper number of items.
+;
+  items  = items[0:nitems-1]
+  values = values[0:nitems-1,*]
+
+; convert data/time and ra, dec to real numbers (special user mode).
+
+ n = N_elements(values)
+ db_item,items,it,ivalnum,idltype
+ idltype = rebin(idltype,n)
+; loop on elements in vals
+
+ for i = 0,n-1 do begin
+        if idltype[i] NE 7 then begin
+        v = strtrim(values[i])
+
+; is it of the form DD-MMM-YYYY hh:mm:ss.ss
+
+        if (strpos(v,':') gt 0) and (strpos(v,'-') gt 0) then begin
+                val = date_conv(v)
+                v = string(val,'(d22.14)')
+        end
+
+; is it of form ccyy/ddd/hh:mm:sss?   (Two digit years are interpreted as 
+; 1900 + YY if YY GT 40, and 2000 + YY if YY LE 40.)
+
+        if strpos(v,'/') gt 0 then begin
+                v1 = v
+                val = 0.0d0
+                yr = strtrim( gettok( v1,'/'), 2 )
+                if yr EQ '' then goto, DATE
+                if strnumber( yr, num) then begin
+                        if num LT 40 then num = num + 2000 else $
+                        if ((num GT 40) and (num LT 100)) then num = num + 1900
+                        val = val + num*1000d0
+                        day = strtrim(gettok(v1,':'),2)
+                        if day EQ '' then goto,DATE
+                        if strnumber(day,num) then begin  
+                           val = val + num
+                           hr = strtrim(gettok( v1,':'),2)
+                           if hr EQ '' then goto,DATE
+                           if strnumber( hr, num) then begin
+                                val = val + num/24.0d0
+                                mn = strtrim( gettok(v1,':'),2)
+                                if mn EQ '' then goto,DATE
+                                if strnumber( mn, num) then begin
+                                        val = val + num/24.0d0/60.0
+                                        sc = strtrim(v1,2)
+                                        if sc EQ '' then goto, DATE
+                                        if strnumber(sc,num) then begin 
+                                           val = val + num/24.0d0/3600.0
+                                           goto, DATE
+                                         endif
+                                endif
+                            endif
+                         endif
+                      endif
+                 goto, NOT_DATE
+DATE:           v = string(val,'(d22.14)')
+        endif
+NOT_DATE:
+;
+; is it of form hh:min:sec or deg:min:sec
+;
+        if strpos(v,':') gt 0 then begin
+                val  =0.0d0           
+                val = val+gettok(v,':')
+                sign = 1
+                if(val lt 0.0) then sign = (-1)
+                val = val+gettok(v,':')/60.0*sign
+                val = val+strtrim(v)/3600.0d0*sign
+                v = val
+        endif
+        values[i]=v
+ endif
+ endfor
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dbget.pro b/Code/script_idl_mv/astrolib/dbget.pro
new file mode 100644
index 0000000000000000000000000000000000000000..87e7f40406e59183b838d9d01c350aad5454d47a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbget.pro
@@ -0,0 +1,106 @@
+function dbget,item,values,listin,SILENT=silent, FULLSTRING = fullstring, $
+               Count = count
+;+
+; NAME:
+;       DBGET
+; PURPOSE:
+;       Find entry numbers which contain specified values of a given item.
+; EXPLANATION:
+;       DBGET() is useful as an alternative to DBFIND() when the desired 
+;       search values are not easily expressed as a string.  
+;
+; CALLING SEQUENCE:
+;       list = dbget( item, values, [ listin ], /SILENT, /FULLSTRING )
+;
+; INPUTS:
+;       item - Item name or number
+;       values -  scalar or vector containing item values to search for.
+;
+; OPTIONAL INPUTS:
+;       listin - list of entries to be searched.  If not supplied, or
+;               set to -1, then all entries are searched
+;
+; OUTPUT:
+;       list - vector giving the entry number of entries containing desired
+;               item values.  The number of elements in  LIST may be different 
+;               from that of VALUE, since a value might be located zero, once, 
+;               or many times in the database.  Use the function DBMATCH if a 
+;               one to one correspondence is desired between VALUES and LIST. 
+; OPTIONAL INPUT KEYWORDS:
+;       /SILENT - If this keyword is set, then DBGET will not display
+;               the number of entries found
+;       /FULLSTRING - By default, one has a match if a search string is 
+;               included in any part of a database value (substring match).   
+;               But if /FULLSTRING is set, then all characters in the database
+;               value must match the search string (excluding leading and 
+;               trailing blanks).    Both types of string searches are case
+;               insensitive.
+; OPTIONAL OUTPUT KEYWORD:
+;       COUNT - Integer scalar giving the number of valid matches
+;
+; RESTRICTIONS:
+;       When linked databases are opened together, DBGET can only be used to
+;       search on items in the primary database.
+; EXAMPLE:
+;       Get info on selected HD stars in Bright Star catalogue
+;
+;       IDL> dbopen, 'YALE_BS' 
+;       IDL> hdno = [1141,2363,3574,4128,6192,6314,6668]    ;Desired HD numbers
+;       IDL> list = dbget( 'HD', hdno )        ;Get corresponding entry numbers
+;
+; SYSTEM VARIABLES:
+;       The obsolete system variable !ERR is set to number of entries found
+; REVISION HISTORY:
+;       Written,    W. Landsman      STX     February, 1989
+;       William Thompson, GSFC, 14 March 1995 Added keyword FULLSTRING
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added COUNT keyword, deprecate !ERR        W. Landsman March 2000
+;       Fix bug introduced March 2000              W. Landsman November 2000
+;       Fix possible bug when sublist supplied    W. Landsman August 2008
+;-
+;
+ On_error,2                                   ;Return to caller
+ compile_opt idl2
+
+ if N_params() LT 2 then begin
+   print,'Syntax --  list = ' + $
+          'DBGET( item, values, [listin, /SILENT, /FULLSTRING, Count=]'
+   return,-1
+ endif
+   
+ if N_params() LT 3 then listin = lonarr(1)-1
+
+ nvals = N_elements(values)
+
+ if nvals EQ 0 then message,'No search values supplied'
+
+ db_item, item, itnum
+ index = db_item_info( 'INDEX', itnum)
+ list = listin
+ 
+ if nvals EQ 1 then val = [values,values] $  ;Need at least 2 elements
+               else val = values 
+
+ if index[0] GE 2 then begin                              ;Sorted item
+    if N_elements(list) EQ 1 then list = lonarr(1) + list
+    dbfind_sort, itnum[0], nvals, val, list, $
+            FULLSTRING = fullstring, Count =count
+
+ endif else begin                                        ;Non-sorted item
+    dbext, list, itnum, itvals
+    dbsearch, nvals, val, itvals, good, FULLSTRING = fullstring, Count = count
+    if count GT 0 then $     ;Updated Aug 2008
+        if list[0] NE -1 then list = list[good] else list = good+1
+ endelse
+
+ if count LE 0 then begin
+     if not keyword_set(SILENT) then $
+         print, 'No entries found by DBGET in ' + db_info( 'NAME',0 )
+     list = intarr(1)  
+
+ endif else  if not keyword_set( SILENT ) then $
+          print,count,' entries found in '+db_info('name',0)
+
+ return, list[ sort(list) ]
+
+ end
diff --git a/Code/script_idl_mv/astrolib/dbhelp.pro b/Code/script_idl_mv/astrolib/dbhelp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e2bb8b5ab4bccef95f6f7674b253d62645c175fd
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbhelp.pro
@@ -0,0 +1,275 @@
+pro dbhelp,flag,TEXTOUT=textout,sort=sort
+;+
+; NAME:
+;     DBHELP
+; PURPOSE:
+;     List available databases or items in the currently open database
+; EXPLANATION:
+;     Procedure to either list available databases (if no database is 
+;     currently open) or the items in the currently open database.
+;
+; CALLING SEQUENCE:  
+;     dbhelp, [ flag , TEXTOUT=, /SORT ]
+;
+; INPUT:
+;     flag - (optional) if set to nonzero then item or database
+;             descriptions are also printed
+;             default=0
+;             If flag is a string, then it is interpreted as the
+;             name of a data base (if no data base is opened) or a name 
+;             of an item in the opened data base.   In this case, help
+;             is displayed only for the particular item or database
+;
+; OUTPUTS:
+;      None
+; OPTIONAL INPUT KEYWORDS:
+;      TEXTOUT  - Used to determine output device.  If not present, the
+;                value of !TEXTOUT system variable is used (see TEXTOPEN )
+;
+;               textout=0       Nowhere
+;               textout=1       if a TTY then TERMINAL using /more option
+;                                   otherwise standard (Unit=-1) output
+;               textout=2       if a TTY then TERMINAL without /more option
+;                                   otherwise standard (Unit=-1) output
+;               textout=3       <program>.prt
+;               textout=4       laser.tmp
+;               textout=5      user must open file
+;               textout=7      same as 3 but text is appended to <program>.prt
+;                               file if it already exists.
+;               textout = filename (default extension of .prt)
+;
+;        /SORT - If set and non-zero, then the help items will be displayed
+;               sorted alphabetically.    If more than one database is open,
+;               then this keyword does nothing.
+; METHOD:
+;       If no data base is opened then a list of data bases are
+;       printed, otherwise the items in the open data base are printed.
+;
+;       If a string is supplied for flag and a data base is opened
+;       flag is assumed to be an item name.  The information for that
+;       item is printed along with contents in a optional file
+;       zdbase:dbname_itemname.hlp
+;       if a string is supplied for flag and no data base is opened,
+;       then string is assumed to be the name of a data base file.
+;       only information for that file is printed along with an
+;       optional file zdbase:dbname.hlp.
+; PROCEDURES USED:
+;       DB_INFO(),DB_ITEM_INFO(),FIND_WITH_DEF(), TEXTOPEN, TEXTCLOSE, UNIQ()
+; IDL VERSION:
+;       V5.3 or later (uses vectorized FDECOMP)
+; HISTORY:
+;       Version 2  D. Lindler  Nov 1987 (new db format)
+;       Faster printing of title desc. W. Landsman  May 1989 
+;       Keyword textout added, J. Isensee, July, 1990
+;       Modified to work on Unix, D. Neill, ACC, Feb 1991.
+;       William Thompson, GSFC/CDS (ARC), 1 June 1994
+;               Added support for external (IEEE) representation.
+;       William Thompson, GSFC, 3 November 1994
+;               Modified to allow ZDBASE to be a path string.
+;       Remove duplicate database names  Wayne Landsman    December 1994
+;       8/17/95 jkf/acc - force lowercase filenames for .hlp files.
+;       Added /SORT keyword  J. Sandoval/W. Landsman     October 1998
+;       V5.3 version use vectorized FDECOMP   W. Landsman   February 2001
+;       Recognize 64 bit, unsigned integer datatypes W. Landsman September 2001
+;       Fix display of number of bytes with /SORT W. Landsman February 2002
+;       Assume since V5.2                 W. Landsman February 2002  
+;       Assume since V5.5                 W. Landsman 
+;       Define !TEXTOUT if not already defined W. Landsman  April 2016  
+;-
+;****************************************************************************
+
+   defsysv,'!TEXTUNIT',exist=i
+  if i EQ 0 then astrolib
+  
+;
+; get flag value
+;
+
+  stn=''
+  if N_params() GT 0 then begin
+      if size(flag,/TNAME) EQ 'STRING' then $   ;item name or db name
+             stn=strtrim(flag) 
+  endif else flag = 0    ;flag not supplied
+;
+; Are any data bases opened?
+;
+opened = db_info('OPEN')
+if opened then begin
+        if stn EQ '' then xtype=1 $             ;all items
+                     else xtype=2               ;single item
+   end else begin
+        if stn EQ '' then xtype=3 $             ;all db's
+                     else xtype=4               ;single db
+end
+;
+; determine where user wants output...default terminal.
+;
+if N_elements(textout) EQ 0 then textout = !textout  ;use default output dev.
+;
+textopen,'dbhelp',textout=textout
+;
+;--------------------------------------------------------------------
+; if data base open then print info for it
+;
+if opened then begin                    ;data base opened?
+;
+; get list of items to print
+;
+        if xtype eq 1 then begin                ;all items?
+                nitems=db_info('items') ;number of items
+                itnums=indgen(nitems)
+            end else begin
+                nitems=1
+                db_item,stn,itnums
+        end
+;
+; get information on the items
+;
+     names = db_item_info('NAME',itnums)         ;item names
+     idltype = db_item_info('IDLTYPE',itnums)    ;data type
+     nbytes = db_item_info('NBYTES',itnums)      ;number of bytes
+     desc = db_item_info('DESCRIPTION',itnums)   ;description
+     pointer = db_item_info('POINTER',itnums)    ;file it points to
+     index = db_item_info('INDEX',itnums)        ;index type
+     pflag = db_item_info('PFLAG',itnums)        ;pointer item flag
+     dbnumber = db_item_info('DBNUMBER',itnums)  ;opened data base number
+     pnumber = db_item_info('PNUMBER',itnums)    ;opened data base it points to
+     nvalues = db_item_info('NVALUES',itnums)    ;number of values for vector
+     if keyword_set(sort) && (max(dbnumber) EQ 0) then begin 
+          nsort = sort(names)
+          names = names[nsort]
+          idltype = idltype[nsort]
+          desc = desc[nsort]
+          nvalues = nvalues[nsort]
+          nbytes = nbytes[nsort]
+     endif
+;
+; get names and descriptions of opened db's
+;
+        
+     if flag then begin         ;print descrip.?
+             desc = strtrim(desc)
+             printf,!textunit,' '
+             printf,!textunit,'----- '+db_info('name',dbnumber[0]) +'  '+ $
+                                 db_info('title',dbnumber[0])
+             printf,!textunit,'   ITEM               TYPE            DESCRIPTION'
+             for i=0,nitems-1 do begin
+                 if i NE 0 then if dbnumber[i] ne dbnumber[i-1] then begin
+                            printf,!textunit,' '
+                            printf,!textunit,'----- '+db_info('name',dbnumber[i]) +'  '+ $
+                                         db_info('title',dbnumber[i])
+                            printf,!textunit,'   ITEM              TYPE            DESCRIPTION'
+                 end
+                  case idltype[i] of
+                       1: type = 'byte'
+                       2: type = 'int*2'
+                       3: type = 'int*4'
+                       4: type = 'real*4'
+                       5: type = 'real*8'
+                       7: type = 'char*'+strtrim(nbytes[i],2)
+                       12: type = 'uint*2'
+                       13: type = 'uint*4'
+                       14: type = 'int*8'
+                       15: type = 'uint*8'
+                        end
+                   while strlen(type) lt 8 do type=type+' '
+                   qname = names[i]
+                   if nvalues[i] GT 1 then begin
+                           qname=strtrim(qname)
+                           qname=qname+'('+strtrim(nvalues[i],2)+')'
+                           while strlen(qname) lt 20 do qname=qname+' '
+                  end
+                  printf,!textunit,strmid(qname,0,18),' ',type,' ', desc[i]
+                end
+        end else begin                  ;just print item names
+                printf,!textunit,form='(1x,7a11)',names
+        end
+;
+; print index information -----------------------------------------
+;
+        if (xtype EQ 1) && (total(index) GT 0) then begin
+                if xtype EQ 1 then begin
+                        printf,!textunit,' '
+                        printf,!textunit,'-------  Indexed Items ------'
+                        indexed=where(index)
+                        printf,!textunit,names[indexed]
+                   end else begin
+                        printf,!textunit,'The item is indexed'
+                end
+        end
+;
+; print pointer information ----------------------------------------
+;
+        if (total(pflag) GT 0) && (xtype EQ 1) then begin
+                good = where( pflag, n)
+                printf,!textunit,' '
+                printf,!textunit,'----- Pointer Information ----'
+                for i=0,n-1 do begin
+                    pos=good[i]
+                    if pnumber[pos] GT 0 then popen=' (presently opened)' $
+                                         else popen=''
+                    printf,!textunit,strtrim(db_info('name',dbnumber[pos]))+ $
+                                '.'+strtrim(names[pos])+' ---> '+ $
+                                strtrim(pointer[pos])+popen
+                end
+        end
+;
+; print information on data base size ----------------------------
+;
+        printf,!textunit,' '
+        if xtype EQ 1 then printf,!textunit,'data base contains', $
+                        db_info('ENTRIES',0),' entries'
+;
+; print data base information --------------------------------
+;
+  end else begin                        ;list data bases
+        if stn EQ '' then begin
+                names=list_with_path('*.dbh', 'ZDBASE', COUNT=n) ;get list
+                if n EQ 0 then message,'No databases found in ZDBASE directory'
+       endif else begin
+                names=list_with_path(stn+'*.dbh', 'ZDBASE', COUNT=n) ;get list
+                if n EQ 0 then message,'Unable to locate database '+stn
+       endelse
+       fdecomp,names,disk,dir,fnames
+       fsort = uniq(fnames,sort(fnames))
+        n = N_elements(fsort)
+        if flag then  begin                ;print description from .DBH file
+             get_lun,unit
+             names = names[fsort]
+             b=bytarr(79)              ;Database title is 79 bytes
+             for i=0,n-1 do begin
+                  openr,unit,names[i],error=err
+                  if err NE 0 then message,/CON, 'Error opening ' + names[i]
+                  readu,unit,b
+                  printf,!TEXTUNIT,strtrim(b[0:78],2) 
+                  close,unit
+             endfor
+             free_lun,unit
+       endif else  $                            ;just print names
+                printf,!textunit,form='(A,T20,A,T40,A,T60,A)',fnames[fsort]
+endelse
+;
+; now print aux help file info if flag was a string ---------------------
+;
+if stn NE '' then begin
+        if xtype EQ 4 then file=find_with_def(stn+'.hlp', 'ZDBASE') $
+                      else file=find_with_def(strlowcase( $
+                                strtrim(db_info( 'NAME', dbnumber[0]))+ $
+                                '_' + strtrim(names[0]) + '.hlp'), 'ZDBASE')
+        openr,unit,strlowcase(file),error=err,/get_lun
+        if err EQ 0 then begin
+          st=''
+          while not eof(unit) do begin
+                readf,unit,st
+                printf,!textunit,st
+          end; while
+          free_lun,unit
+        endif
+end
+;
+; close unit opened by TEXTOPEN
+;
+textclose, TEXTOUT = textout
+
+return
+end
diff --git a/Code/script_idl_mv/astrolib/dbindex.pro b/Code/script_idl_mv/astrolib/dbindex.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8359e9bba586b2519e9b31fc59ab804cdb12863b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbindex.pro
@@ -0,0 +1,218 @@
+pro dbindex,items
+;+                      
+; NAME:
+;       DBINDEX
+; PURPOSE:
+;       Procedure to create index file for data base 
+;
+; CALLING SEQUENCE:     
+;       dbindex, [ items ]
+;
+; OPTIONAL INPUT:
+;       items - names or numbers of items to be index -- if not supplied,
+;               then all indexed fields will be processed.  
+;
+; OUTPUT:
+;       Index file <name>.dbx is created on disk location ZDBASE:
+;
+; OPERATIONAL NOTES:
+;       (1) Data base must have been previously opened for update
+;       by DBOPEN 
+;
+;       (2) Only 18 items can be indexed at one time.   If the database has
+;       more than 18 items, then two separate calls to DBINDEX are needed.
+; PROCEDURES CALLED:
+;       DBINDEX_BLK, DB_INFO(), DB_ITEM, DB_ITEM_INFO(), IS_IEEE_BIG()
+; HISTORY:
+;       version 2  D. Lindler  Nov 1987 (new db format)
+;       W. Landsman    added optional items parameter Feb 1989 
+;       William Thompson, GSFC/CDS (ARC), 30 May 1994
+;               Added support for external (IEEE) data format
+;       Test if machine is bigendian  W. Landsman     May, 1996
+;       Change variable name of BYTESWAP to BSWAP  W. Thompson  Mar, 1997
+;       Increased number of fields to 15   W. Landsman   June, 1997
+;       Increase number of items to 18     W. Landsman  November 1999
+;       Allow multiple valued (nonstring) index items W. Landsman November 2000
+;       Use 64 bit integers for V5.2 or later  W. Landsman February 2001
+;       Do not use EXECUTE() for V6.1 or later, improve efficiency 
+;                W. Landsman   December 2006
+;       Automatically enlarge .dbx file if needed, fix major bug in last
+;             update    W. Landsman Dec 2006
+;       Assume since V6.1    W. Landsman   June 2009
+;       Allow sorted string items   W. Landsman   October 2009
+;       Use Swap_Endian_Inplace instead of IEEE_TO_HOST W. Landsman April 2016
+;-                                         
+;*****************************************************************
+ On_error,2                ;Return to caller
+ compile_opt idl2
+
+; Check to see if data base is opened for update
+
+ if db_info('UPDATE') EQ 0 then message, $
+        'Database must be opened for update'
+
+; Extract index items from data base
+
+ if N_params() EQ 1 then db_item,items,itnum else begin 
+      nitems = db_info('ITEMS',0)
+      itnum = indgen(nitems)
+ endelse
+
+ indextype = db_item_info('INDEX',itnum)
+ indexed = where(indextype, Nindex)                 ;Select only indexed items
+ if Nindex LE 0 then begin
+        message,'Database has no indexed items',/INF
+        return
+ endif else if Nindex GT 18 then begin
+        message,'ERROR - Only 18 items can be indexed at one time',/INF
+        return
+ endif
+
+ indextype = indextype[indexed]
+ if N_params() EQ 1 then indexed = itnum[indexed]
+
+; get info on indexed items
+
+ nbytes = db_item_info('NBYTES',indexed)         ;Number of bytes
+ idltype = db_item_info('IDLTYPE',indexed)       ;IDL type
+ sbyte = db_item_info('SBYTE',indexed)           ;Starting byte
+ nval = db_item_info('NVALUES',indexed)          ;Number of values per entry
+
+; get db info
+
+ nentries = db_info('ENTRIES',0)
+ if nentries EQ 0 then begin
+  message, 'ERROR - database contains no entries',/INF
+  return
+ endif
+ unit = db_info('UNIT_DBX',0)                      ;unit number of index file
+ external = db_info('EXTERNAL',0)                  ;external format?
+ bswap = external ? not IS_IEEE_BIG() : 0
+
+; read header info of index file (mapped file)
+
+ reclong = assoc(unit,lonarr(2),0)
+ h = reclong[0]  ;first two longwords
+ if bswap then swap_endian_inplace,h,/swap_if_little
+ maxentries = h[1]      ;max allowed entries
+; If necessary, enlarge the size of the .dbx file.    All indexed items must
+; then be reindexed.
+ if maxentries lt nentries then begin
+        message,'Enlarging index (.dbx) file to support ' +  $
+	         strtrim(nentries,2) + ' entries',/INF
+	dbname = db_info('name',0)	 
+        dbcreate,dbname,1,maxentry=nentries,external=db_info('external')
+	dbopen, dbname, 1
+        nitems = db_info('ITEMS',0)
+        itnum = indgen(nitems)   
+ endif
+ 
+ nindex2 = h[0] ;number of indexed items
+ if nindex2 LT nindex then goto, NOGOOD   
+ reclong = assoc(unit,lonarr(7,nindex2),8)
+ header = reclong[0]            ;index header
+ if bswap then swap_endian_inplace,header,/swap_if_little
+ hitem = header[0,*]            ;indexed item numbers
+ hindex = header[1,*]           ;index type
+ htype = header[2,*]            ;idl data type
+ hblock = header[3,*]           ;starting block of header
+ sblock = header[4,*]           ;starting block of data values
+ iblock = header[5,*]           ;starting block of indices (type=3)
+ ublock = header[6,*]           ;starting block of unsorted data (type=4)
+
+; extract index items...maximum of 18 indexed fields.
+
+ list = lindgen(nentries)+1l
+ dbext_dbf,list,0,sbyte,nbytes*nval,idltype,nval, $
+               v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18
+
+ for i = 0,nindex-1 do begin
+        ;
+        ; place item in variable v
+        ;
+        v = (scope_varfetch('v' + strtrim(i+1,2))) 
+        pos = where(hitem EQ indexed[i], N_found)
+        if N_found LE 0 then goto, NOGOOD    
+        pos = pos[0]
+        if hindex[pos] NE indextype[i] then goto, NOGOOD  
+        if ( idltype[i] EQ 7 ) then v = byte(v)
+;
+; process according to index type ---------------------------------------
+;
+        reclong = assoc(unit,lonarr(1),(iblock[pos]*512LL))
+        case indextype[i] of
+ 
+        1: begin                                ;indexed (unsorted)
+
+                datarec = dbindex_blk(unit, sblock[pos], 512, 0, idltype[i])
+   		datarec[0] =  bswap ? swap_endian(v,/swap_if_little) : v
+           end
+; 
+        2: begin                                ;values are already sorted
+
+                nb=(nentries+511L)/512          ;number of 512 value blocks
+                ind=indgen(nb)*512LL             ;position at start of each block
+                sval=v[ind]                     ;value at start of each block
+;
+                datarec = dbindex_blk(unit, hblock[pos], 512, 0, idltype[i])
+                datarec[0] = bswap ? swap_endian(sval,/swap_if_little) : sval
+ ;
+                datarec = dbindex_blk(unit, sblock[pos], 512, 0, idltype[i])
+   		datarec[0] =  bswap ? swap_endian(v,/swap_if_little) : v
+           end
+ 
+        3: begin                                ; sort item before storage
+                
+                if idltype[i] EQ 7 then begin 
+		    svv = string(v)
+		    sub= bsort(svv) 
+		    v = byte(svv[sub])
+		endif     else begin 
+		   sub=bsort(v)                    ;sort values
+                   v=v[sub]
+                endelse
+		nb=(nentries+511)/512           ;number of 512 value blocks
+                ind=l64indgen(nb)*512LL             ;position at start of each block
+                if idltype[i] EQ 7 then sval=v[*,ind] else sval = v[ind] 
+		                    ;value at start of each block
+                datarec = dbindex_blk(unit, hblock[pos], 512, 0, idltype[i])
+ 		datarec[0] = bswap ? swap_endian(sval,/swap_if_little) : sval
+;
+                datarec = dbindex_blk(unit, sblock[pos], 512, 0, idltype[i])
+  		datarec[0] =  bswap ? swap_endian(v,/swap_if_little) : v
+                reclong[0] = bswap ? swap_endian(sub+1,/swap_if_little) : sub+1                ;indices
+           end
+        4: begin                                ; sort item before storage
+                
+                datarec = dbindex_blk(unit, ublock[pos], 512, 0, idltype[i])
+ 		datarec[0] =  bswap ? swap_endian(v,/swap_if_little) : v
+                if idltype[i] EQ 7 then begin 
+		    svv = string(v)
+		    sub= bsort(svv) 
+		    v = byte(svv[sub])
+		endif     else begin 
+		   sub=bsort(v)                    ;sort values
+                   v=v[sub]
+                endelse
+   
+   
+                  nb=(nentries+511)/512           ;number of 512 value blocks
+                ind=l64indgen(nb)*512LL             ;position at start of each block
+                if idltype[i] EQ 7 then sval=v[*,ind] else sval = v[ind] 
+		                    ;value at start of each block
+                datarec = dbindex_blk(unit, hblock[pos], 512, 0, idltype[i])
+                datarec[0] = bswap ? swap_endian(sval,/swap_if_little) : sval
+ ;
+ 		datarec = dbindex_blk(unit, sblock[pos], 512, 0, idltype[i])
+		datarec[0] =  bswap ? swap_endian(v,/swap_if_little) : v
+;
+                 reclong[0] = bswap ?swap_endian(sub+1,/swap_if_little) : sub+1                ;indices
+	   end
+        endcase
+endfor
+return
+NOGOOD:    
+        print,'DBINDEX-- Inconsistency in .dbh and .dbx file'
+        print,'Run dbcreate to create a new index file'
+        return
+end
diff --git a/Code/script_idl_mv/astrolib/dbindex_blk.pro b/Code/script_idl_mv/astrolib/dbindex_blk.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7048570264a1cbc554a522983f5fe1403ecc25b7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbindex_blk.pro
@@ -0,0 +1,49 @@
+FUNCTION dbindex_blk, unit, nb, bsz, ofb, dtype
+;+
+; NAME:
+;       DBINDEX_BLK
+; PURPOSE:
+;       Subroutine of DBINDEX to create associated variable of correct datatype
+; EXPLANATION:
+;       DBINDEX_BLK will offset into the file by a specified amount in 
+;       preparation for writing to the file.   V5.2 or later
+;
+; CALLING SEQUENCE:
+;       res = dbindex_blk(unit, nb, bsz, ofb, dtype)
+;
+; INPUTS:
+;       unit   The unit number assigned to the file.
+;       nb     The number of blocks to offset into the file.
+;       bsz    The size of each block, in bytes, to offset into the file.
+;       ofb    The offset into the block, in bytes.
+;       dtype  The IDL datatype as defined in the SIZE function
+;
+; OUTPUTS:
+;       res    The returned variable.  This is an associated variable.
+;
+; RESTRICTIONS:
+;       The file must have been previously opened.
+;
+; MODIFICATION HISTORY:
+;       Written by Michael R. Greason, STX, 14 June 1990.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use 64 bit integer for very large databases  W. Landsman February 2001
+;       Added new unsigned & 64bit integer datatypes    W. Landsman July 2001
+;-
+offset = long64(nb) * long64(bsz) + long64(ofb)
+case dtype of
+        7: datarec=assoc(unit,bytarr(1),offset)         ; string
+        1: datarec=assoc(unit,bytarr(1),offset)         ; byte
+        2: datarec=assoc(unit,intarr(1),offset)         ; integer
+        4: datarec=assoc(unit,fltarr(1),offset)         ; floating point
+        3: datarec=assoc(unit,lonarr(1),offset)         ; longword
+        5: datarec=assoc(unit,dblarr(1),offset)         ; double
+        6: datarec=assoc(unit,complexarr(1),offset)     ; complex
+       12: datarec=assoc(unit,uintarr(1),offset)        ; unsigned integer
+       13: datarec=assoc(unit,ulonarr(1),offset)        ; unsigned longword
+       14: datarec=assoc(unit,lon64arr(1),offset)       ; 64 bit longword
+       15: datarec=assoc(unit,ulon64arr(1),offset)   ; unsigned 64bit longword
+endcase
+;
+RETURN, datarec
+END
diff --git a/Code/script_idl_mv/astrolib/dbmatch.pro b/Code/script_idl_mv/astrolib/dbmatch.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e733a5e518714c2719df05dfaadcac291582daa8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbmatch.pro
@@ -0,0 +1,173 @@
+function dbmatch, item, values, listin, FULLSTRING = fullstring
+;+
+; NAME:
+;       DBMATCH
+; PURPOSE:
+;       Find the entry number in a database for each element of item values
+; EXPLANATION:
+;       DBMATCH() is especially useful for finding a one-to-one 
+;       correspondence between entries in different databases, and thus to 
+;       create the vector needed for database pointers.
+;
+; CALLING SEQUENCE:
+;       list = DBMATCH( item, values, [ listin, /FULLSTRING ] )
+;
+; INPUTS:
+;       ITEM - Item name or number, scalar
+;       VALUES -  scalar or vector containing item values to search for.
+;
+; OPTIONAL INPUTS:
+;       LISTIN - list of entries to be searched.  If not supplied, or
+;               set to -1, then all entries are searched
+; OUTPUT:
+;       LIST - vector of entry numbers with the same number of elements as 
+;               VALUES.  Contains a value of 0 wherever the corresponding item
+;               value was not found.
+;
+; OPTIONAL INPUT:
+;       /FULLSTRING - By default, one has a match if a search string is 
+;               included in any part of a database value (substring match).   
+;               But if /FULLSTRING is set, then all characters in the database
+;               value must match the search string (excluding leading and 
+;               trailing blanks).    Both types of string searches are case
+;               insensitive.
+;
+; NOTES:
+;       DBMATCH is meant to be used for items which do not have duplicate values
+;       in a database (e.g. catalog numbers).  If more than one entry is found
+;       for a particular item value, then only the first one is stored in LIST.
+;
+;       When linked databases are opened together, DBMATCH can only be 
+;       used to search on items in the primary database.
+;
+; EXAMPLE:
+;       Make a vector which points from entries in the Yale Bright Star catalog
+;       to those in the Hipparcos catalog, using the HD number
+;
+;       IDL> dbopen, 'yale_bs'            ;Open the Yale Bright star catalog
+;       IDL> dbext, -1, 'HD', hd          ;Get the HD numbers
+;       IDL> dbopen, 'hipparcos'          ;Open the Hipparcos catalog
+;       IDL> list = dbmatch( 'HD', HD)    ;Get entries in Hipparcos catalog 
+;                                         ;corresponding to each HD number.
+; PROCEDURE CALLS:
+;       DB_ITEM, DB_ITEM_INFO(), DBEXT, DBFIND_SORT()
+; REVISION HISTORY:
+;       Written,    W. Landsman      STX     February, 1990
+;       Fixed error when list in parameter used May, 1992
+;       Faster algorithm with sorted item when listin parameter supplied 
+;       Added keyword FULLSTRING,check for empty database, William Thompson, 
+;               GSFC, 15 March 1995
+;       Work for more than 32767 values, added CATCH W. Landsman   July 1997
+;       Change some loop variables to type LONG,  W. Landsman  July 1999
+;       Remove loop for substring searches (faster)  W. landsman August 1999
+;       Replace DATATYPE() with size(/TNAME)  W. Landsman  November 2001
+;       Fixed typo when search on sorted items W. Landsman February 2002
+;       Fixed bug from Nov 2001 where /FULLSTRING was always set.  W.L Feb 2007
+;-
+ On_error,2
+
+ if N_params() LT 2 then begin
+     print,'Syntax --  list = DBMATCH( item, values, [ listin, /FULLSTRING] )'
+     return,-1
+ endif 
+
+
+ catch, error_status
+ if error_status NE 0 then begin 
+        print,!ERR_STRING
+        if N_elements(listin) NE 0 then return,listin else return, -1
+ endif
+
+ nvals = N_elements( values )
+ if nvals EQ 0 then message, $ 
+       'ERROR - No search values (second parameter) supplied'
+
+ if N_params() LT 3 then listin = lonarr(1) - 1
+
+ db_item,item,itnum
+ index = db_item_info( 'INDEX', itnum)           ;Get index type of item
+ list = lonarr( nvals )
+
+ nentries = db_info('entries')
+ if nentries[0] eq 0 then begin                 ;Return if database is empty
+        message,'ERROR - No entries in database ' + db_info("NAME",0),/INF
+        return,listin*0
+ endif 
+
+ if index[0] GE 2 then begin                      ;Sorted item
+
+    if listin[0] NE -1 then min_listin = min( listin, MAX = max_listin)
+
+    for i = 0l,nvals-1 do begin
+
+        val = [values[i],values[i]]
+
+;       We don't supply the LISTIN parameter directly to DBFIND_SORT.  Since
+;       we know that we need only 1 match for each item value, we can do
+;       the restriction to the LISTIN values faster than DBFIND_SORT can
+
+        tmplist = -1
+        dbfind_sort,itnum[0],1,val, tmplist, $    ;Search all entries to start
+                fullstring=fullstring, Count = Nmatch_sort
+ 
+           if ( listin[0] NE -1 ) then begin
+
+                if Nmatch_sort EQ 0 then goto, FOUND_MATCH
+
+                good = where( ( tmplist LE max_listin ) and $ 
+                              ( tmplist GE min_listin ), Ngood)
+
+                if ( Ngood EQ 0 ) then goto, FOUND_MATCH
+
+                tmplist = tmplist[good]
+
+                for j = 0L, Ngood - 1  do begin
+                   test = where( listin EQ tmplist[j], Nfound ) 
+                   if Nfound GE 1 then begin
+                         list[i] = tmplist[j]
+                         goto, FOUND_MATCH
+                   endif
+                endfor 
+
+         endif else if ( Nmatch_sort GT 0 ) then list[i] = tmplist[0]
+ 
+        FOUND_MATCH:
+   endfor
+
+  endif else begin                                 ;Non-sorted item
+
+    if listin[0] EQ -1 then tmplist = lindgen( nentries[0] )+1 else $
+                            tmplist = listin
+    dbext, tmplist, itnum, itvals
+    typ = size(itvals,/TNAME)
+    if typ EQ 'STRING' then begin
+                itvals = strupcase( strtrim(itvals,2) )
+                vals   = strupcase( strtrim(values,2) )
+    endif else vals = values
+    for i=0L,nvals-1 do begin             
+       if typ NE 'STRING' then begin                  ;Fixed Feb 2007
+               good = where( itvals EQ vals[i], Nfound ) 
+               if Nfound GT 0 then list[i] = tmplist[ good[0] ]  ;Fixed May-92
+
+        endif else begin                 ;Can't use WHERE on string arrays
+                                         ;unless FULLSTRING is set
+
+               if keyword_set(fullstring) then begin
+                   good = where( itvals EQ vals[i], Nfound)
+                   if Nfound GT 0 then list[i] = tmplist[ good[0] ]
+                end else begin
+                      good = where(strpos( itvals, vals[i]) GE 0, Nfound) 
+                      if Nfound GT 0 then begin
+                             list[i] = tmplist[good[0]]
+                             goto, DONE
+                       endif
+                    
+                endelse
+             endelse
+    DONE:       
+    endfor
+endelse
+
+return,list
+
+end
diff --git a/Code/script_idl_mv/astrolib/dbopen.pro b/Code/script_idl_mv/astrolib/dbopen.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2b10da69a1beb2f6de40550eab8caf670e482bfc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbopen.pro
@@ -0,0 +1,411 @@
+pro dbopen,name,update,UNAVAIL=unavail   
+;+
+; NAME:
+;       DBOPEN
+; PURPOSE:
+;       Routine to open an IDL database
+;
+; CALLING SEQUENCE:
+;       dbopen, name, update
+;
+; INPUTS:
+;       name - (Optional) name or names of the data base files to open.
+;               It has one of the following forms:
+;
+;               'name'          -open single data base file
+;               'name1,name2,...,nameN' - open N files which are
+;                               connected via pointers.
+;               'name,*'        -Open the data base with all data
+;                               bases connected via pointers
+;               ''              -Interactively allow selection of
+;                               the data base files.
+;
+;               If not supplied then '' is assumed.
+;               name may optionally be a string array with one name
+;               per element.
+;
+;       update - (Optional) Integer flag specifying opening for update.
+;               0       - Open for read only
+;               1       - Open for update
+;               2       - Open index file for update only
+;               !PRIV must be 2 or greater to open a file for update.
+;               If a file is opened for update only a single data base
+;               can be specified.
+;
+; OUTPUTS:
+;       none
+;
+; INPUT-OUTPUT KEYWORD:
+;       UNAVAIL - If present, a "database doesn't exit" flag is returned
+;                 through it.  0 = the database exists and was opened (if
+;                 no other errors arose).  1 = the database doesn't exist.
+;                 Also if present, the error message for non-existent databases
+;                 is suppressed.  The action, however, remains the same.  
+; SIDE EFFECTS:
+;       The .DBF and .dbx files are opened using unit numbers obtained by
+;       GET_LUN.  Descriptions of the files are placed in the common block
+;       DB_COM.
+;
+; PROCEDURES CALLED:
+;       DBCLOSE, DB_INFO(), SELECT_W, ZPARCHECK
+; HISTORY:
+;       For IDL Version 2  W. Landsman May 1990 -- Will require further 
+;           modfication once SCREEN_SELECT is working
+;       Modified to work under Unix, D. Neill, ACC, Feb 1991.
+;       UNAVAIL keyword added.  M. Greason, Hughes STX, Feb 1993.
+;       William Thompson, GSFC/CDS (ARC), 1 June 1994
+;               Added support for external (IEEE) representation.
+;       William Thompson, GSFC, 3 November 1994
+;                       Modified to allow ZDBASE to be a path string.
+;       8/29/95 JKF/ACC - forces lowercase for input database names.
+;       W. Landsman, Use CATCH to catch errors    July, 1997
+;       W. Landsman Use vector call to FDECOMP, STRSPLIT()    Sep 2006
+;       W. Landsman Remove obsolete keywords to OPEN   Sep 2006
+;       Replace SCREEN_SELECT with SELECT_W, remove IEEE_TO_HOST  WL  Jan 2009
+;       Fix typos in BYTEORDER introduced Jan 2009 G. Scandariato/W.L.Feb. 2009
+;       Support new DB format which allows entry lengths > 32767 bytes 
+;              W.L. October 2010
+;       William Thompson, fixed bug opening multiple databases Dec 2010
+;       Fix problem with external databases WL Sep 2011
+;       Use tooltips when no parameters called  WL Aug 2013
+;
+;-
+;
+;------------------------------------------------------------------------
+On_error,2
+;
+; data base common block
+;
+common db_com,QDB,QITEMS,QDBREC
+;
+; QDB[*,i] contains the following for each data base opened
+;
+;       bytes
+;         0-18   data base name character*19
+;         19-79  data base title character*61
+;         80-81  number of items (integer*2)
+;         82-83  record length of DBF file (integer*2)
+;         84-87  number of entries in file (integer*4)
+;         88-89  position of first item for this file in QITEMS (I*2)
+;         90-91  position of last item for this file (I*2)
+;         92-95  Last Sequence number used (item=SEQNUM) (I*4)
+;         96     Unit number of .DBF file
+;         97     Unit number of .dbx file (0 if none exists)
+;         98-99  Index number of item pointing to this file (0 for first db)
+;         100-103 Number of entries with space allocated
+;         104    Update flag (0 open for read only, 1 open for update)
+;         105-108  record length of DBF file (integer*4)
+;         118    Equals 1 if more 32767 bytes can be stored in database (new format)
+;         119    Equals 1 if external data representation (IEEE) is used
+;
+;  QITEMS[*,i] contains description of item number i with following
+;  byte assignments:
+;
+;       0-19    item name (character*20)
+;       20-21   IDL data type (integer*2)
+;       22-23   Number of values for item (1 for scalar) (integer*2)
+;               in bytes 179-182 in new format
+;       24-25   Starting byte position in original DBF record 
+;                In bytes 183-186 (integer*2) New DB format
+;       26-27   Number of bytes per data value (integer*2)
+;       28      Index type
+;       29-97   Item description
+;       98-99   print format field length
+;       100     flag (1 if this items points to a data base)
+;       101-119 Data base this item points to
+;       120-125 Print format
+;       126-170 Print headers
+;       171-172 Starting byte in record returned by DBRD
+;       173-174 Data base number in QDB
+;       175-176 Data base number this item points to
+;       177-178 Item number within the specific data base
+;       179-182 Number of values for item (1 for scalar) (integer*4)
+;       183-186  Starting byte position in original DBF record (integer*4)
+;       187-190 Starting byte in record returned by DBRD
+;
+;       
+;-------------------------------------------------------------------------
+;
+;
+; check for valid input parameters
+;
+if N_params() lt 1 then name=''
+if N_params() lt 2 then update=0
+ catch, error_status
+ if error_status NE 0 then begin 
+       print,!ERR_STRING
+       return
+  endif
+
+zparcheck,'DBOPEN',name,1,7,[0,1],'Data base name[s]'
+zparcheck,'DBOPEN',update,2,[1,2,3,4,5],0,'Update flag'
+;
+; check privilege
+;
+if update && (!priv lt 2) then  $
+        message,'!PRIV must be 2 or greater to open with update'
+;
+; check UNAVAIL
+;
+unav_flg = arg_present(unavail) 
+unavail = 0
+totret = 1
+;---------------------------------------------------------------------
+;       PROCESS INPUT NAMES (CREATE STRING ARRAY)
+;
+; Process scalar name
+;
+s=size(name) & ndim=s[0]
+if ndim eq 0 then begin
+;
+; process name=''
+;
+    if strtrim(name) EQ '' then begin
+        names = list_with_path('*.dbh', 'ZDBASE', Count = N)
+        if n EQ 0 then message, $
+           'No database (.dbh) files found in ZDBASE or current directory'
+        fdecomp,names,disk,dir,fnames,qual
+        db_titles, fnames, titles
+        select_w,fnames,isel,titles, $
+                'Select data base file to open',1
+        fnames=fnames[intarr(1)+isel]
+      end else $
+;
+; separate names into string array
+;
+        fnames = strlowcase( strsplit(name,',',/extract))
+   end else begin
+;
+; name is already a string vector
+;
+    fnames=name
+end
+;
+; if update, only one data base can be opened
+;
+if update then if N_elements(fnames) gt 1 then $
+        message,'Only one file can be specified if mode is update'
+;
+;---------------------------------------------------------------
+;
+;       LOOP AND OPEN EACH DATA BASE
+;
+; close any data bases already open
+;
+dbclose
+;
+;
+offset=0                ;byte offset in dbrd record for data base
+tot_items=0             ;total number of items all opened data bases
+get_lun,unit            ;get unit number to use for .dbh files
+dbno=0                  ;present data base number
+while dbno lt n_elements(fnames) do begin
+    dbname=strtrim(fnames[dbno])
+;
+; process * if second in list  -----------------------
+;
+    if dbname eq '*' then begin         ;get data base names from pointers
+        if dbno ne 1 then begin         ;* must be second data base
+            message,'Invalid use of * specification',/continue
+            goto,ABORT   
+        endif
+        pointers=qitems[100,*]          ;find pointer items
+        good=where(pointers,n)
+        if n eq 0 then goto,done        ;no pointers
+        pnames=string(qitems[101:119,*]);file names for pointers
+        fnames=[fnames[0],pnames[good]] ;new file list
+        dbname=strtrim(fnames[1])       ;new second name
+    end
+;
+; open .dbh file and read contents ------------------------
+;
+    dbhname = find_with_def(dbname+'.dbh', 'ZDBASE')
+
+    openr,unit,dbhname,ERROR=err     
+
+    if err NE 0 then begin
+        if unav_flg EQ 0 then begin
+                message,'Error opening .dbh file '+ dbname,/CONTINUE
+                print,!SYSERR_STRING
+        endif else totret = 0
+        unavail = 1
+        goto, ABORT 
+    end
+    db=bytarr(120)
+    readu,unit,db
+    
+    external = db[119] eq 1     ;Is external data rep. being used?
+    newdb = db[118] eq 1        ; New db format allowing longwords
+    totbytes = newdb ? long(db,105,1) :  fix(db,82,1)
+    totbytes = totbytes[0]      ;Make sure is scalar
+     nitems=fix(db,80,1) & nitems=nitems[0] ;number of items or fields in file
+
+    if external then begin
+        if newdb then begin
+        byteorder, totbytes, /NTOHL  &  db[105] = byte(totbytes,0,4) 
+	endif else begin
+        byteorder, totbytes, /NTOHS  &  db[82] = byte(totbytes,0,2)
+	endelse
+        byteorder, nitems,/NTOHS   &  db[80] = byte(nitems,0,2)
+    endif
+    items=bytarr(200,nitems)
+    readu,unit,items
+    close,unit
+    if external then begin
+        tmp = fix(items[20:27,*],0,4,nitems)
+        byteorder,tmp, /ntohs
+        items[20,0] = byte(tmp,0,8,nitems)
+;
+        tmp = fix(items[98:99,*],0,1,nitems)
+        byteorder,tmp,/NTOHS
+        items[98,0] = byte(tmp,0,2,nitems)
+;
+        tmp = fix(items[171:178,*],0,4,nitems)
+        byteorder,tmp,/NTOHS
+        items[171,0] = byte(tmp,0,8,nitems)     
+	
+	if newdb then begin
+        tmp = long(items[179:186,*],0,2,nitems)
+        byteorder,tmp,/NTOHL
+
+        items[179,0] = byte(tmp,0,8,nitems)
+	endif
+    endif
+
+;
+; add computed information to items ---------------------------
+;
+    sbyte = newdb ?  long(items[183:186,*],0,nitems)+offset : $ 
+                     fix(items[24:25,*],0,nitems)+offset 
+
+    for i=0,nitems-1 do begin
+        if newdb then items[187,i]= byte(sbyte[i],0,4)  else $
+	              items[171,i] = byte(sbyte[i],0,2)
+	            ;starting byte in DBRD record
+        items[173,i]=byte(dbno,0,2)     ;data base number
+        items[177,i]=byte(i,0,2)        ;item number
+    end
+    offset=offset+totbytes
+;
+; open .dbf file ---------------------------------
+;
+    get_lun,unitdbf
+    dbf_file = find_with_def(dbname+'.dbf', 'ZDBASE')
+
+    if update eq 1 then $
+         openu,unitdbf,dbf_file else $ 
+         openr,unitdbf,dbf_file,error=err
+    if err ne 0 then begin
+        message,'Error opening '+dbname+'.dbf',/continue
+        free_lun,unitdbf
+        goto,abort
+    end
+
+    p=assoc(unitdbf,lonarr(2))
+    head = p[0]
+    if external then byteorder, head, /NTOHL
+    db[96]=unitdbf                      ;unit number of .dbf file
+    db[84]=byte(head[0],0,4)            ;number of entries
+    db[92]=byte(head[1],0,4)            ;last seqnum used
+    db[88]=byte(tot_items,0,2)          ;starting item number for this db
+    tot_items=tot_items+nitems          ;new total number of items
+    db[90]=byte(tot_items-1,0,2)        ;last item number for this db
+    db[104]=update                      ;opened for update
+;
+; open index file if necessary -----------------------------
+;
+
+    index=where(items[28,*] gt 0,nindex)        ;indexed items
+   
+    if nindex gt 0 then begin           ;need to open index file.
+        get_lun,unitind
+        dbx_file = find_with_def(dbname+'.dbx', 'ZDBASE')
+        if update gt 0 then $
+                  openu,unitind,dbx_file,error=err $
+           else openr,unitind,dbx_file,error=err
+        if err ne 0 then begin
+                message,'Error opening index file for '+dbname,/continue
+                free_lun,unitdbf
+                free_lun,unitind
+                goto,abort
+        endif
+        db[97]=unitind                  ;unit number for index file
+    end
+;
+; add to common block ---------------------
+;
+
+    if dbno eq 0 then begin
+        qdb=db
+        qitems=items
+      end else begin
+        old=qdb
+        qdb=bytarr(120,dbno+1)
+        qdb[0,0] = old
+        qdb[0,dbno] = db
+        old=qitems
+        qitems=bytarr(200,tot_items)
+        qitems[0,0] = old
+        qitems[0,tot_items-nitems] = items
+    end
+;
+    dbno=dbno+1
+end; loop on data bases
+done: free_lun,unit
+
+
+;--------------------------------------------------------------------
+;               LINK PROCESSING
+;
+; determine linkages between data bases
+;
+numdb = N_elements(fnames)
+if numdb gt 1 then begin
+    pnames=strupcase(qitems[101:119,*])
+    for i=1,numdb-1 do begin
+        dbname=strupcase(qdb[0:18,i])   ;name of the data base
+        for j=0,tot_items-1 do if pnames[j] eq dbname then goto,found
+;
+; if we made it here we can not link the file -----------
+;
+        message,'Unable to link data base file '+dbname,/continue
+        goto,abort
+;
+; found linkage item ------------------------------------
+;
+
+found:
+        item_number=j           ;number of item supplying link
+        item_db=fix(qitems[173:174,item_number],0,1) & item_db=item_db[0]
+        if item_db ge i then begin
+                message,'Unable to link data base '+dbname + $
+                        'to previous data base.',/continue
+                print,' Possible incorrect ordering of input data bases'
+                goto,abort
+        endif
+        qitems[175,item_number]=byte(i,0,2)     ;data base number pointed to
+        qdb[98,i]=byte(item_number,0,2)         ;item number pointing to this db
+nextdb:
+    endfor
+endif
+
+;
+; create an assoc variable for the first db
+;
+
+unit=db_info('unit_dbf',0)
+len=db_info('length',0)
+qdbrec=assoc(unit,bytarr(len))
+;----------------------------------------------------------------------------
+; done
+;
+
+return
+;
+; abort
+;
+abort:
+dbclose                         ;close any open data bases
+free_lun,unit
+if (totret NE 0) then retall else return
+end
diff --git a/Code/script_idl_mv/astrolib/dbprint.pro b/Code/script_idl_mv/astrolib/dbprint.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6229081b4819d44168800284f9fa31fbacf5f150
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbprint.pro
@@ -0,0 +1,318 @@
+pro dbprint,list,items, FORMS=forms, TEXTOUT=textout, NoHeader = noheader, $
+           Adjustformat = adjustformat
+;+
+; NAME:
+;     DBPRINT
+; PURPOSE:
+;     Procedure to print specified items from a list of database entries
+;
+; CALLING SEQUENCE:     
+;     dbprint, list, [items, FORMS= , TEXTOUT= , /AdjustFormat, /NoHeader]  
+;
+; INPUTS:
+;     list  - list of entry numbers to be printed, vector or scalar 
+;               if list = -1, then all entries will be printed.
+;               An error message is returned if any entry number is larger
+;               than the number of entries in the database
+;
+; OPTIONAL INPUT-OUTPUT:
+;     items - items to be printed, specified in any of the following ways:
+;
+;               form 1  scalar string giving item(s) as list of names
+;                       separated by commas
+;               form 2  string array giving list of item names
+;               form 3  string of form '$filename' giving name
+;                       of text file containing items (one item per
+;                       line)
+;               form 4  integer scalar giving single item number or
+;                         integer vector list of item numbers
+;               form 5  Null string specifying interactive selection.   This
+;                       is the default if 'items' is not supplied
+;               form 6  '*'     select all items, printout will be in
+;                       table format. 
+;
+;            If items was undefined or a null string on input, then
+;            on output it will contain the items interactively selected.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /ADJUSTFORMAT -  If set, then the format length for string items will
+;               be adjusted to the maximum length for the entries to be printed.
+;               This option will slow down DBPRINT because it requires the 
+;               string items be extracted and their maximum length determined 
+;               prior to any printing.   However, it enables the display of
+;               string items without any truncation or wasted space. 
+;
+;       FORMS - The number of printed lines per page. If forms is not 
+;               present, output assumed to be in PORTRAIT form, and 
+;               a heading and 47 lines are printed on each page, with
+;               a page eject between each page.  For LANDSCAPE form with
+;               headings on each page, and a page eject between pages, set 
+;               forms = 34.  For a heading only on the first page, and no
+;               page eject, set forms = 0.   This is the default for output
+;               to the terminal.
+;
+;       TEXTOUT - Integer (0-7) or string used to determine output device (see 
+;               TEXTOPEN for more info).  If not present, the !TEXTOUT system 
+;               variable is used.
+;               textout=0       Nowhere
+;               textout=1       if a TTY then TERMINAL using /more option
+;                                   otherwise standard (Unit=-1) output
+;               textout=2       if a TTY then TERMINAL without /more option
+;                                   otherwise standard (Unit=-1) output
+;               textout=3       dbprint.prt (file)
+;               textout=4       laser.tmp
+;               textout=5       user must open file
+;               textout=7      same as 3 but text is appended to <program>.prt
+;               textout = filename   (default extension of .prt)
+;
+;       /NOHEADER - If this keyword is set, then the column headers will not
+;               be printed
+;
+; EXAMPLE:
+;       The following example shows how a multiple valued item DATAMAX can be 
+;       printed as separate columns.   In the WFPC2 target database, DATAMAX
+;       is an item with 4 values, one for each of the 4 chips
+;
+;       IDL> dbopen,'wflog'
+;       IDL> dbprint,list,'entry,datamax(0),datamax(1),datamax(2),datamax(3)'
+;
+; SYSTEM VARIABLES:
+;       Output device controlled by non-standard system varaible !TEXTOUT, if 
+;       TEXTOUT keyword is not used.    
+;
+; NOTES:
+;       Users may want to adjust the default lines_per_page value given at
+;       the beginning of the program for their own particular printer.
+; PROCEDURE CALLS:
+;       db_info(), db_item_info(), dbtitle(), dbxval(), textopen, textclose
+;       zparcheck
+; HISTORY:
+;       version 2  D. Lindler  Nov. 1987 (new db format)
+;       Test if user pressed 'Q' in response to /MORE W. Landsman  Sep 1991
+;       Apply STRTRIM to free form (table) output    W. Landsman   Dec 1992
+;       Test for string value of TEXTOUT         W. Landsman   Feb 1994
+;       William Thompson, GSFC, 3 November 1994
+;                       Modified to allow ZDBASE to be a path string.
+;       W. Landsman, GSFC, July, 1997, Use CATCH to catch errors
+;       Removed STRTRIM in table format output to handle byte values April 1999
+;       Fixed occasional problem when /NOHEADER is supplied   Sep. 1999
+;       Only byteswap when necessary for improved performance  Feb. 2000
+;       Change loop index for table listing to type LONG  W. Landsman Aug 2000
+;       Entry vector can be any integer type   W. Landsman Aug. 2001
+;       Replace DATATYPE() with size(/TNAME)   W. Landsman  Nov. 2001
+;       No page eject for TEXTOUT =5           W. Landsman  Nov. 2001
+;       No initial page eject                  W. Landsman  Jan. 2002
+;       Added AdjustFormat keyword             W. Landsman  Sep. 2002
+;       Assume since V5.3 (STRJOIN)            W. Landsman Feb. 2004
+;       Fix display on GUI terminals           W. Landsman March 2006
+;       Remove VMS statements                  W. Landsman Sep 2006
+;       Remove EXECUTE statement               W. Landsman Jan 2007
+;       Fix display of multi element items     W. Landsman  Aug 2010
+;       Fix problem with linked databases      W. Landsman Dec 2011
+;-
+;
+ On_error,2                                ;Return to caller
+ compile_opt idl2
+
+ if N_params() EQ 0 then begin
+       print,'Syntax - DBPRINT, list, items, '
+       print,'             [ FORMS = , TEXTOUT =, /NoHeader, /AdjustFormat ]'
+       return
+ endif
+
+ lines_per_page = 47                 ;Default # of lines per page
+ zparcheck, 'DBPRINT', list, 1, [1,2,3,4,5,12,13,14,15], [0,1],  $
+            'Entry List Vector'
+
+ catch, error_status
+ if error_status NE 0 then begin 
+       print,!ERR_STRING
+       return
+  endif
+
+
+; Make list a vector
+
+ nentry = db_info( 'ENTRIES', 0)
+ if nentry EQ 0 then message,'ERROR - Database contains no entries'
+ if list[0] EQ -1 then list = lindgen(nentry) + 1 
+ dbname = strlowcase( db_info( 'NAME', 0 ))
+
+ if max(list) GT nentry then message, dbname + $
+     ' entry numbers must be between 1 and ' + strtrim( nentry, 2 )
+  nv = N_elements(list)                 ;number of entries requested
+
+; No need for byteswapping if data is not external or it is a big endian machine
+
+   noconvert = ~db_info('EXTERNAL',0) || is_ieee_big()      ;Updated Dec 11
+    
+; Determine items to print
+
+ if N_params() EQ 1 then begin
+
+      file = find_with_def(dbname +'.items', 'ZDBASE')
+      if file NE '' then items = '$' + file else items = '' 
+
+ endif
+ 
+ db_item, items, it, ivalnum, dtype, sbyte, numvals, nbytes
+ numvals = numvals<1                    ;can't print vectors
+ nvalues = db_item_info( 'NVALUES', it )        ;number of values in item
+ qnumit = db_info( 'ITEMS' )                    ;number of items
+ nitems = N_elements( it )                      ;number of items requested
+ qnames = db_item_info( 'NAME', it )
+ qtitle = db_info( 'TITLE', 0 )         ;data base title
+
+; Open output text file
+
+ if ~keyword_set(TEXTOUT) then textout = !textout  ;use default output dev.
+textopen, dbname, TEXTOUT = textout, more_set = more_set
+ if size(TEXTOUT,/TNAME) EQ 'STRING' then text_out = 5 else text_out = textout
+ if (nitems EQ qnumit)  then begin
+
+; Create table listing of each item specified. -------------------------
+
+ for i = 0L, nv-1 do begin
+      dbrd, list[i], entry, noconvert = noconvert   ; read an entry.
+      printf, !TEXTUNIT, ' '                        ; print  blank line.
+
+; display name and value for each entry 
+
+      for k = 0, qnumit-1  do begin
+         ;.
+         ; only print entries of reasonable size... < 5 values in item.
+       
+         if ( nvalues[k] LT 5 ) then begin
+            somvar = $        
+	    dbxval(entry,dtype[k],nvalues[k],sbyte[k],nvalues[k]*nbytes[k]) 
+            if dtype[k] EQ 1 then somvar=fix(somvar)
+            printf,!textunit,k,') ',qnames[k], strtrim(somvar,2)
+                                                        ;display name,value
+         endif                                               
+       endfor   ; k
+
+    endfor      ; i
+
+ printf,!textunit,' '                         ;Added 11/90
+ 
+ end else begin
+
+; get info on items
+
+   formats = db_item_info( 'FORMAT', it )
+   flen = db_item_info( 'FLEN', it )            ;field lengths
+   nvals = db_item_info( 'NVALUES', it )        ;larger than one for vector items
+;
+; If /AdjustFormat set, then extract all string vectors and find their maximum
+; length.   Then update the formats and flen vectors accordingly
+;
+   if keyword_set(adjustFormat) then begin
+     stringvar = where(dtype EQ 7, Nstring)
+     if Nstring GT 0 then begin
+       alen = intarr(Nstring)
+       varnames = 'v' + strtrim(indgen(Nstring)+1,2)
+       stringitems = strjoin(varnames,',') 
+       for i=0, Nstring-1 do begin
+            dbext,list,it[stringvar[i]], vv
+            alen[i] = max(strlen(strtrim(temporary(vv),2)))
+     endfor
+       flen[stringvar] = alen
+       formats[stringvar] = 'A' + strtrim(alen,2)
+     endif
+  endif
+
+; Set up format array
+
+   form = '(' + strtrim(formats,2)      + ')'   ;remove blanks, and add paren
+
+   linelength = total(flen) + nitems            ;length of output lines
+   dash = byte('-') & dash = dash[0]
+   dashes = ' '+string( replicate( dash, linelength ) )
+;
+   if ~keyword_set( NoHeader) then begin
+
+      title = string( replicate(byte(32), linelength>42) )
+      strput, title, qtitle, (linelength-40)/2>1           ;center title
+
+; Extract headers
+
+    headers = db_item_info( 'HEADERS', it )
+    c1 = strmid( headers,0,15 )
+    c2 = strmid( headers,15,15 )
+    c3 = strmid( headers,30,15 )
+
+; Place value numbers for multiple valued items in h3
+    for i = 0,nitems-1 do begin
+          if nvals[i] GT 1 then $       ;multiple values?
+             c3[i] = '[' + strtrim(string(ivalnum[i]),2) + ']'
+    endfor        ;i
+
+    h1 = dbtitle( c1,flen )
+    h2 = dbtitle( c2,flen )
+    h3 = dbtitle( c3,flen )
+
+ endif
+
+; Loop on entries
+
+ hardcopy = (text_out GE 2) and (text_out NE 5)     ;Keep track of page eject?
+ if ( N_elements(forms) GT 0 ) then begin
+        if ( forms GT 0 ) then pcount = forms $ ;lines per page
+        else pcount = N_elements(list)          ;no page breaks
+ endif else if not hardcopy then pcount = N_elements(list) $
+      else pcount = lines_per_page                ;Portrait form default
+ limit = pcount - 1
+
+  for j = 0L, N_elements(list)-1 do begin
+
+   if not keyword_set( NoHeader) then begin
+
+        if pcount GT limit then begin           ;new page?
+                pcount = 0
+                if (j GT 0) and hardcopy then $
+                            printf,!textunit,string(byte(12))   $;eject
+                       else printf,!textunit,' '
+                printf,!textunit,title                  ;print title
+                printf,!textunit,dashes                 ;print headings
+                printf,!textunit,h1
+                printf,!textunit,h2
+                printf,!textunit,h3
+                printf,!textunit,dashes
+        endif
+
+    endif
+        dbrd, list[j], entry, noconvert = noconvert        ;read entry
+        ;
+        ; loop on items
+        ;
+        st = ''                                 ;output string
+        for i = 0,nitems-1 do  begin
+
+                val = dbxval(entry,dtype[i],numvals[i],sbyte[i],nbytes[i])
+                if dtype[i] EQ 1 then val = fix(val)
+                if dtype[i] EQ 7 then begin
+                   b = byte(val)
+                   bad = where(b EQ 0, nbad)
+                   if nbad GT 0 then begin
+                       b[bad] = 32b
+                       val = string(b)
+                   endif
+                endif
+                st = st+' ' + string(val,form[i])
+
+        endfor
+
+        printf, !TEXTUNIT, st                   ;print line
+        if more_set then  $       ;Did user press 'Q' in /MORE ?
+                if ( !ERR EQ 1 ) then return
+        pcount = pcount+1            ;increment line counter
+    end                              ; loop on entries
+
+ endelse                             ; N_params > 1
+
+; Clean up
+
+ textclose, TEXTOUT = textout                   ;close text file
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dbput.pro b/Code/script_idl_mv/astrolib/dbput.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9dfe5d21c767d4e026275490a1b8985a15c5b667
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbput.pro
@@ -0,0 +1,78 @@
+pro dbput,item,val,entry
+;+
+; NAME:
+;	DBPUT
+; PURPOSE:
+;	Procedure to place a new value for a specified item into
+;	a data base file entry.  
+;
+; CALLING SEQUENCE:	
+;	dbput, item, val, entry
+;
+; INPUTS:
+;	item - item name or number
+;	val - item value(s)
+;
+; INPUT/OUTPUT:
+;	entry - entry (byte array) or scalar entry number.
+;	        if entry is a scalar entry number then the data
+;	        base file will be updated.  Otherwise the change
+;	        will be only made to the entry array which must
+;	        be written latter using DBWRT.
+;
+; OPERATIONAL NOTES:
+;	If entry is a scalar entry number or the input file name
+;	is supplied, the entry in the data base will be updated
+;	instead of a supplied entry variable.  In this case, !priv
+;	must be greater than 1.
+; EXAMPLE:
+;       IDL> dbput,'WAVELEN',1215.6,entry
+; PROCEDURES USED:
+;       DB_ITEM, DBRD, DBXPUT, DBWRT
+; HISTORY:
+;	version 2  D. Lindler  Feb 1988 (new db formats)
+;	modified to convert blanks into zeros correctly D. Neill Jan 1991
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       V5.2 version support unsigned, 64bit integers W. Landsman  Sep. 2001
+;-
+;-----------------------------------------------------------------------
+;
+; get item number
+;
+ db_item, item, inum, ivalnum, dtype, sbyte, numvals, nbytes
+;   
+; convert val to correct type and check size
+;
+ if (dtype[0] NE 7) and ( size(val,/type) EQ 7) then val = strtrim(val)
+ case dtype[0] of
+	1: v = byte(fix(val))
+	2: v = fix(val)
+	3: v = long(val)
+	4: v = float(val)
+	5: v = double(val)
+	7: v = string(val)
+	12: v = uint(val)
+	13: v = ulong(val)
+	14: v = long64(val)
+	15: v = ulong64(val)
+ endcase
+;
+ if N_elements(v) NE numvals[0] then begin
+	print,'DBPUT - Invalid number of data values'
+	print,'Item '+item+' requires ',strtrim(numvals[0],2),' values'
+	print,'DBPUT aborting'
+	retall
+ endif
+;
+; determine if entry number supplied
+;
+ if size(entry,/n_dimen) EQ 0 then begin      ;scalar entry number supplied
+	dbrd,entry,e
+	dbxput,v,e,dtype[0],sbyte[0],nbytes[0]*numvals[0] ;update entry
+	dbwrt,e					;update file
+  end else begin				;array supplied, just update it
+	dbxput,v,entry,dtype[0],sbyte[0],nbytes[0]*numvals[0]
+ end
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dbrd.pro b/Code/script_idl_mv/astrolib/dbrd.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0697ddd5bda20d40011e0889cdaf5ef5d8866320
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbrd.pro
@@ -0,0 +1,115 @@
+pro dbrd,enum,entry,available,dbno, noconvert=noconvert
+;+
+; NAME:
+;	DBRD
+; PURPOSE:
+;	procedure to read an entry from a data base file or from
+;	linked multiple databases.
+;
+; CALLING SEQUENCE:
+;	dbrd, enum, entry, [available, dbno, /NoConvert]
+;
+; INPUTS:
+;	enum - entry number to read, integer scalar
+;
+; OUTPUT:
+;	entry - byte array containing the entry
+;
+; OPTIONAL OUTPUT:
+;	available - byte array with length equal to number of data
+;		bases opened.  available(i) eq 1 if an entry (pointed
+;		to) is available.  It always equals 1 for the first 
+;		data base, otherwise it is an error condition.
+;
+; OPTIONAL  INPUT:
+;	dbno - specification of the data base number to return.  If
+;		supplied, only the record for the requested data base
+;		number is returned in entry.  Normally this input should
+;		not be supplied.  dbno is numbered for 0 to n-1 and gives
+;		the number of the data base opened.  The data bases are 
+;		numbered in the order supplied to dbopen.  If dbno is supplied 
+;		then the entry number refers to that data base and not the
+;		primary or first data base. If set to -1, then it means all
+;		data bases opened (same as not supplying it)
+; OPTIONAL INPUT KEYWORD:
+;	noconvert - if set then don't convert external to host format.
+;		Assumes that calling program will take care of this
+;		requirement.
+; OPERATIONAL NOTES:
+;	If multiple data base files are opened, the records are
+;	concatenated with each other
+; HISTORY
+;	version 2  D. Lindler  Nov. 1987
+;	William Thompson, GSFC/CDS (ARC), 1 June 1994
+;		Added support for external (IEEE) representation.
+;	Version 3, Richard Schwartz, GSFC/SDAC, 23-Aug-1996
+;			Add noconvert keyword
+;
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Version 4, 2 May 2003, W. Thompson
+;               Use BSWAP keyword to DBXVAL instead of calling IEEE_TO_HOST.
+;-
+;
+;-----------------------------------------------------------------------
+On_error,2
+
+ if N_params() LT 2 then begin
+     print,'Syntax - dbrd, enum, entry, [available, dbno, /NoConvert]'
+     return
+ endif
+
+ COMMON db_com,qdb,qitems,qdbrec
+
+; Find out if databases are in external format.
+ externali= db_info('EXTERNAL')
+ external = externali * (1-keyword_set(noconvert))
+ if N_params() LT 4 then dbno = -1
+
+ if dbno GE 0 then begin		;get only requeseted data base entry
+	available = bytarr(1)+1b
+    if dbno EQ 0 then begin
+	entry = qdbrec[enum]
+	if external[0] then db_ent2host, entry, 0
+      end else begin
+	len = db_info( 'LENGTH', dbno)
+	unit = db_info( 'UNIT_DBF', dbno)
+	p = assoc(unit,bytarr(len, /NOZERO), enum)
+	entry = p[0]		;read entry
+	if external[dbno] then db_ent2host, entry, dbno
+    end
+    return
+ end
+
+; get info on open data bases
+
+ len = db_info( 'LENGTH' )	;record lengths
+ units = db_info( 'UNIT_DBF' ) 	;unit numbers
+ n = N_elements(len)		;number of db's opened
+ entry = qdbrec[enum]		;read entry for first db
+ if external[0] then db_ent2host, entry, 0
+ irec = enum			;record number
+ available = bytarr(n)+1B		;entry available
+
+ if n GT 1 then begin
+	for i = 1,n-1 do begin	;loop on db's
+		pointer = db_info('pointer',i)		;what points to it
+		db_item, pointer,itnum,ival,dtype,sb,nv,nb
+		
+		;Make sure irec is in internal format!
+		if externali[db_item_info('dbnumber',itnum[0])] and keyword_set(noconvert) $
+			 then bswap=1 else bswap=0
+		irec = dbxval(entry,dtype[0],1,sb[0],nb[0],bswap=bswap)
+		if irec GT 0 then begin
+			p = assoc( units[i], bytarr( len[i],/NOZERO ))
+			tmp = p[irec]
+			if external[i] then db_ent2host, tmp, i
+			entry = [ entry, tmp ]	;add to end
+		   end else begin
+			available[i] = 0B
+			entry = [ entry, bytarr(len[i])]
+		end
+	end
+ end
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dbsearch.pro b/Code/script_idl_mv/astrolib/dbsearch.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4c955e85ae59959234a204c0d7bd6a8baeb46c3f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbsearch.pro
@@ -0,0 +1,139 @@
+pro dbsearch,type,svals,values,good, FULLSTRING = fullstring, COUNT = count
+;+
+; NAME:
+;	DBSEARCH
+; PURPOSE:
+;	Subroutine of DBFIND() to search a vector for specified values
+;
+; CALLING SEQUENCE:
+;	dbsearch, type, svals, values, good, [ /FULLSTRING, COUNT = ] 
+;
+; INPUT: 
+;	type - type of search (output from dbfparse)
+;	svals - search values (output from dbfparse)
+;	values - array of values to search
+;
+; OUTPUT:
+;	good - indices of good values
+;
+; OPTIONAL INPUT KEYWORD:
+;	/FULLSTRING - By default, one has a match if a search string is 
+;		included in any part of a database value (substring match).   
+;		But if /FULLSTRING is set, then all characters in the database
+;		value must match the search string (excluding leading and 
+;		trailing blanks).    Both types of string searches are case
+;		insensitive.
+; OPTIONAL OUTPUT KEYWORD:
+;       COUNT  - Integer scalar giving the number of valid matches
+;  SIDE EFFECTS:
+;	The obsolete system variable !ERR is set to number of good values
+; REVISION HISTORY:
+;	D. Lindler  July,1987
+;       Added COUNT keyword, deprecate !ERR   W. Landsman   March 2000
+;      Some speed improvements W.L. August 2008
+;       Add compound operators, slightly faster WL November 2009
+;       D. Lindler  Aug 2013, added strtrim on values for a string search
+;       Fix problem with "less than" string searches WL November 2014
+;       November 2014 fix actually broke things, reverting  WL January 2015
+;-
+;-----------------------------------------------------------
+ On_error,2
+ compile_opt idl2
+ 
+ svals = strupcase(svals)
+;
+; determine data type of values to be searched
+;
+ datatype=size(values,/type) & nv = N_elements(values)
+ 
+;
+; convert svals to correct data type
+;
+ nvals = type>2
+ if datatype NE 7 then sv = replicate(values[0],nvals) else $
+                      sv = replicate(' ',nvals)
+ On_ioerror, BADVAL              ;Trap any type conversions
+ sv[0]= svals[0:nvals-1]
+ On_ioerror, NULL
+ sv0=sv[0] & sv1=sv[1]
+;
+; -----------------------------------------------------------
+;      STRING SEARCHES (Must use STRPOS to search for substring match)
+;
+if datatype EQ 7 then begin
+    values = strupcase(strtrim(values))
+    case type of
+						
+         0: if keyword_set(FULLSTRING) then $            ;Exact string match?
+	    valid = strtrim(values,2) EQ strtrim(sv0,2) else $
+	    valid = strpos(values,strtrim(sv0,2)) GE 0   ;substring search
+        -1: valid = values GE sv0                        ;greater than
+        -2: valid = values LE sv1                        ;less than
+	    -3: valid = (values GE sv0) and (values LE sv1)  ;in range
+	    -4: valid = strtrim(values) NE ''       ;non zero (i.e. not null)
+        -5: message, $                                  ;Tolerance value
+               ' Tolerance specification for strings is not valid'
+         else:  begin
+                sv = strtrim(sv,2)
+		        sv = sv[uniq(sv,sort(sv))]     ;Remove duplicates
+		        type = N_elements(sv)
+                valid = bytarr(nv)
+
+		        if keyword_set(FULLSTRING) then begin
+		           values = strtrim(values,2)
+                   for ii = 0l,type-1 do valid OR= (values EQ sv[ii]) 
+
+                endif else begin
+
+                for ii=0L,type-1 do begin               ;within set of substring
+             		valid OR= (strpos(values,sv[ii]) GE 0)		
+                endfor
+
+		        endelse
+                end
+	endcase
+	good = where(valid, count)
+	return
+end
+;
+;---------------------------------------------------------------------
+;		ALL OTHER DATA TYPES
+
+case type of
+ 
+	 0: good = where( values EQ sv0, count )               ;value=sv0
+	-1: good = where( values GE sv0, count )		;value>sv0
+	-2: good = where( values LE sv1, count )		;value<sv1
+	-3: begin				;sv0<value<sv1
+	    if sv1 lt sv0 then begin
+	        temp=sv0
+		    sv0=sv1
+		    sv1=temp
+	    endif
+	    good=where((values GE sv0) and (values LE sv1), count)
+	    end 	
+	-5: begin				;sv1 is tolerance
+	    minv=sv0-abs(sv1)
+	    maxv=sv0+abs(sv1)
+	    good=where( (values GE minv) and (values LE maxv), count)
+	    end
+	-4: good=where(values, count)		;non-zero
+	else: begin				;set of values	
+            sv = sv[uniq(sv,sort(sv))]     ;Remove duplicates
+	      type = N_elements(sv)
+	      valid = bytarr(nv) 
+
+	      for i=0L,type-1 do begin		;loop on possible values  
+	         valid OR= (values EQ sv[i])
+	      endfor
+	      good = where(valid, count) 	    
+  
+
+              if count EQ 0 then good = intarr(1)-1   ;Make sure good is defined
+	      !err=count
+	      end
+endcase
+return
+BADVAL: !ERR=-2       ;Illegal search value supplied
+return
+end
diff --git a/Code/script_idl_mv/astrolib/dbsort.pro b/Code/script_idl_mv/astrolib/dbsort.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f91c84b8843c9abbfb64501e96b6aeb85c948d28
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbsort.pro
@@ -0,0 +1,122 @@
+function dbsort,list,items,REVERSE = rev
+;+
+; NAME:
+;       DBSORT
+; PURPOSE:
+;       Routine to sort list of entries in data base
+;
+; CALLING SEQUENCE: 
+;       result = dbsort( list, items , [ REVERSE = ])
+;
+; INPUTS:
+;       list - list of entry numbers to sort
+;               -1 to sort all entries
+;       items - list of items to sort (up to 9 items)
+;
+; OUTPUT:
+;       result - numeric vector giving input list sorted by items
+;
+; OPTIONAL KEYWORD INPUT:
+;       REVERSE - scalar or vector with the same number of elements as the
+;         the number of items to sort.  If the corresponding element of REVERSE 
+;         is non-zero then that item is sorted in descending rather than 
+;         ascending order.
+;
+; EXAMPLE:
+;       Sort an astronomical catalog with RA as primary sort, and declination
+;       as secondary sort (used when RA values are equal)
+;
+;          IDL> NEWLIST = DBSORT( -1, 'RA,DEC' )
+;
+;       If for some reason, one wanted the DEC sorted in descending order, but
+;       the RA in ascending order
+;
+;          IDL> NEWLIST = DBSORT( -1, 'RA,DEC', REV = [ 0, 1 ] )
+;
+; METHOD:
+;       The list is sorted such that each item is sorted into
+;       asscending order starting with the last item.
+; COMMON BLOCKS:
+;       DBCOM
+; PROCEDURES USED:
+;       ZPARCHECK, BSORT, DBEXT, DB_ITEM
+; HISTORY
+;       VERSION 1  D. Lindler  Oct. 86
+;       Added REVERSE keyword   W. Landsman        August, 1991
+;       Avoid use of EXECUTE() for V6.1 or later   W. Landsman Dec 2006
+;       Assume since V6.1   W. Landsman   June 2009
+;       Add TEMPORARY call  W. Lnadsman  July 2009
+;-
+ On_error,2
+ compile_opt idl2
+ if N_params() LT 2 then begin
+     print,'Syntax: newlist = dbsort( list, items, [ REVERSE = ] )'
+     return, -1
+ endif
+;---------------------------------------------------------
+; data base common block, see DBOPEN for meanings
+
+ common db_com,QDB,QITEMS,QLINK
+
+; check parameters
+
+ zparcheck, 'DBSORT', list, 1, [1,2,3], [0,1], 'entry list'
+ zparcheck, 'DBSORT', items, 2, [1,2,3,7], [0,1], 'item list'
+
+; extract values of items
+
+ db_item, items, it
+ nitems = N_elements(it)                    ;Number of items
+ if nitems GT 9 then message, $
+        'ERROR -  Can only sort on nine items or less'
+
+                                            ;Verify REVERSE vector
+ if not keyword_set(REV) then rev = bytarr(nitems) else $
+         if N_elements(rev) NE nitems then $
+             message,'ERROR - REVERSE vector must contain ' + $
+                   strtrim(nitems,2) + ' elements'
+
+; make list vector
+
+ qnentry = long(qdb,84)
+ if list[0] EQ -1 then vlist = lindgen(qnentry)+1 else vlist = list
+
+; create line to execute in the form:
+;       dbext, vlist, it, v1,v2,...,v(nitems)
+ case nitems of 
+        1: dbext, vlist, it, v1
+        2: dbext, vlist, it, v1, v2
+        3: dbext, vlist, it, v1, v2, v3
+        4: dbext, vlist, it, v1, v2, v3, v4
+        5: dbext, vlist, it, v1, v2, v3, v4, v5
+        6: dbext, vlist, it, v1, v2, v3, v4, v5, v6
+        7: dbext, vlist, it, v1, v2, v3, v4, v5, v6, v7
+        8: dbext, vlist, it, v1, v2, v3, v4, v5, v6, v7, v8
+        9: dbext, vlist, it, v1, v2, v3, v4, v5, v6, v7, v8, v9
+ endcase
+
+; sort on each item
+
+ sub = lindgen(N_elements(vlist))               ;list of subscripts
+ for i = 0,nitems-1 do begin
+
+; get item
+
+        j = nitems-i
+        vv = 'v' + strtrim(j,2) 
+        v = temporary(scope_varfetch(vv, level=0))
+
+; perform previous sorts on item
+
+        if i GT 0 then v = v[sub]
+         
+; sort item
+
+        sub = sub[ bsort( v, REVERSE = rev[j-1] ) ]
+
+ end
+
+; return sorted list
+
+ return, vlist[sub]
+ end
diff --git a/Code/script_idl_mv/astrolib/dbtarget.pro b/Code/script_idl_mv/astrolib/dbtarget.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8c7f8f8e6ad52a0fea225c22943bd8cdbcb28685
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbtarget.pro
@@ -0,0 +1,93 @@
+function dbtarget, target, radius, sublist,SILENT=silent, $
+                 TO_B1950 = to_B1950, DIS = dis
+;+
+; NAME:
+;      DBTARGET
+; PURPOSE:
+;      Find sources in a database within specified radius of specified target
+; EXPLANATION:
+;      Uses QuerySimbad to translate target name to RA and Dec, and then uses
+;      DBCIRCLE() to find any entries within specified radius.   Database must 
+;      include items named 'RA' (in hours) and 'DEC' (in degrees) and must 
+;      have previously been opened with DBOPEN
+;
+; CALLING SEQUENCE:
+;     list = DBTARGET(target, [radius, sublist, /SILENT, DIS= ,/TO_B1950 ] )   
+;
+; INPUTS:
+;      TARGET - A scalar string giving an astronomical target name, which 
+;          will be  translated into J2000 celestial coordinates by QuerySimbad 
+;
+; OPTIONAL INPUT:
+;       RADIUS - Radius of the search field in arc minutes, scalar.
+;                Default is 5 arc minutes
+;       SUBLIST - Vector giving entry numbers in currently opened database
+;               to be searched.  Default is to search all entries
+;
+; OUTPUTS:
+;     LIST - Vector giving entry numbers in the currently opened catalog
+;            which have positions within the specified search circle
+;            LIST is set to -1 if no sources fall within the search circle
+;            !ERR is set to the number sources found.
+;
+; OPTIONAL OUTPUT
+;       DIS -  The distance in arcminutes of each entry specified by LIST
+;               to the search center specified by the target.
+;
+; OPTIONAL KEYWORD INPUT:
+;       /SILENT - If this keyword is set, then DBTARGET will not print the 
+;               number of entries found at the terminal
+;       /TO_B1950 - If this keyword is set, then the SIMBAD J2000 coordinates 
+;               are converted to B1950 before searching the database
+;               NOTE: The user must determine on his own whether the database
+;               is in B1950 or J2000 coordinates.
+;
+; RESTRICTIONS;
+;       The database must have items 'RA' (in hours) and 'DEC' (in degrees).
+;       Alternatively, the database could have items RA_OBJ and DEC_OBJ 
+;      (both in degrees)
+; EXAMPLE:
+;       (1) Use the HST_CATALOG database to find all  HST observations within 
+;           5' (the default) of M33
+;
+;       IDL> dbopen,'hst_catalog'
+;       IDL> list = dbtarget('M33')
+;
+;      (2) As above but restrict targets within 2' of the nucleus using the
+;          WFPC2 camara
+;
+;       IDL> dbopen,'hst_catalog'
+;       IDL> sublist = dbfind('config=WFPC2')
+;       IDL> list = dbtarget('M33',2,sublist)
+;
+;
+; PROCEDURE CALLS:
+;       QuerySimbad, DBCIRCLE()
+; REVISION HISTORY:
+;      Written W. Landsman     SSAI          September 2002
+;      Propagate /SILENT keyword to QuerySimbad    W. Landsman Oct 2009
+;      Make sure a database is open  W.L. Oct 2010
+;-                   
+ On_error,2
+
+ if N_params() LT 1 then begin
+    print,'Syntax - list = DBTARGET( targetname_or_coord, [radius, sublist  '
+    print,'                           DIS =, /SILENT, /TO_B1950 ] )'
+    if N_elements(sublist) GT 0 then return, sublist else return,lonarr(1)-1
+ endif
+ 
+  if ~db_info('open') then message,'ERROR - No database open'
+
+  QuerySimbad, target, ra,dec, Found = Found,Silent=silent
+  if found EQ 0 then message,'Target name ' + target + $
+  	     ' could not be translated by SIMBAD'
+  ra = ra/15.
+ 
+ if N_elements(radius) EQ 0 then radius = 5
+ if n_elements(sublist) EQ 0 then $
+ return, dbcircle(ra, dec, radius, dis, SILENT=silent, $
+                   TO_B1950 = to_b1950 )
+ return, dbcircle(ra, dec, radius, dis, sublist, SILENT=silent, $
+                   TO_B1950 = to_b1950 )
+  
+ end
diff --git a/Code/script_idl_mv/astrolib/dbtitle.pro b/Code/script_idl_mv/astrolib/dbtitle.pro
new file mode 100644
index 0000000000000000000000000000000000000000..18232b9ffb23cda701d8b04930d3feae65d55b4c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbtitle.pro
@@ -0,0 +1,38 @@
+function dbtitle,c,f
+;+
+; NAME:
+;	DBTITLE
+; PURPOSE:
+;	function to create title line for routine dbprint
+;
+; CALLING SEQUENCE:
+;	result = dbtitle( c, f )
+;
+; INPUTS:
+;	c = string array of titles for each item
+;	f = field length of each item
+;
+; OUTPUT:
+;	header string returned as function value
+;
+; OPERATIONAL NOTES:
+;	this is a subroutine of DBPRINT.
+;
+; HISTORY:
+;	version 1  D. Lindler  Sept 86
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;------------------------------------------------------------
+n=n_elements(c)
+h=' '
+com = strtrim(c,0)              ;header for item with trailing blanks removed
+ncom = strlen(com)
+for i=0,n-1 do begin		;loop on items
+	flen=f[i]		;field length
+	st=string(replicate(byte(32),flen+1));blank field
+	ipos=((flen-ncom[i]+1)/2)>1	;starting position in field for comment
+	strput,st,com[i],ipos	;insert into field
+	h=h+st			;add to header
+end; loop on items
+return,h			;return header
+end
diff --git a/Code/script_idl_mv/astrolib/dbupdate.pro b/Code/script_idl_mv/astrolib/dbupdate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..73d252e7f31b3d480d80fcdeb9e971ef48e7cc66
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbupdate.pro
@@ -0,0 +1,163 @@
+pro dbupdate,list,items,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14, $
+             NOINDEX = noindex
+;+
+; NAME:
+;	DBUPDATE
+; PURPOSE:
+;	Update columns of data in a database  -- inverse of DBEXT
+; EXPLANATION:
+;	Database must be open for update before calling DBUPDATE
+;
+; CALLING SEQUENCE:
+;	dbupdate, list, items, v1, [ v2, v3, v4......v14 ]
+;
+; INPUTS:
+;	list - entries in database to be updated, scalar or vector
+;		If list=-1 then all entries will be updated
+;	items -standard list of items that will be updated.  
+;	v1,v2....v14 - vectors containing values for specified items.  The
+;		number of vectors supplied must equal the number of items
+;		specified.   The number of elements in each vector should be
+;		the same.
+;
+; OPTIONAL KEYWORD INPUT:
+;       /NOINDEX - If set, then DBUPDATE will not update the index file.   This
+;               keyword is useful to save if additional updates will occur,
+;               and the index file need only be updated on the last call.
+;            
+; EXAMPLES:
+;	A database STAR contains RA and DEC in radians, convert to degrees
+;
+;	IDL> !PRIV=2 & dbopen,'STAR',1          ;Open database for update
+;	IDL> dbext,-1,'RA,DEC',ra,dec          ;Extract RA and DEC, all entries 
+;	IDL> ra = ra*!RADEG & dec=dec*!RADEG    ;Convert to degrees
+;	IDL> dbupdate,-1,'RA,DEC',ra,dec        ;Update database with new values
+;
+; NOTES:
+;	It is quicker to update several items simultaneously rather than use
+;	repeated calls to DBUPDATE.  
+; 
+;	It is possible to update multiple valued items.  In this case, the
+;	input vector should be of dimension (NVAL,NLIST) where NVAL is the
+;	number of values per item, and NLIST is the number of entries to be
+;	updated.  This vector will be temporarily transposed by DBUPDATE but
+;	will be restored before DBUPDATE exits.
+;
+; REVISION HISTORY
+;	Written W. Landsman      STX       March, 1989
+;	Work for multiple valued items     May, 1991
+;	String arrays no longer need to be fixed length      December 1992
+;	Transpose multiple array items back on output        December 1993
+;	Faster update of external databases on big endian machines November 1997
+;	Converted to IDL V5.0   W. Landsman 24-Nov-1997
+;       Added /NOINDEX keyword  W. Landsman  July 2001
+;-
+ On_error,2                             ;Return to caller
+
+ if N_params() LT 3 then begin
+    print,'Syntax - dbupdate, list, items, v1, [ v2, v3, v4, v5,...v14 ]'
+    return
+ endif
+                                      ;Get number of entries to update
+ nlist = N_elements(list)
+ if nlist EQ 0 then message, $
+      'ERROR - no entry values supplied'
+
+ nentries = db_info( 'ENTRIES' )      ;Number of entries in database
+ external = db_info( 'EXTERNAL', 0 )
+ if external then noconvert = is_ieee_big() else noconvert = 1b
+
+ if list[0] LT 0  then begin           ;If LIST = -1, then update all entries
+       nlist = nentries[0]
+       list = lindgen(nlist) + 1
+ endif 
+
+ db_item, items, itnum, ivalnum, idltype, sbyte, numvals, nbyte
+ nitem = N_elements(itnum)            ;Number of items in database
+ if N_params() LT nitem+2 then $
+    message,'ERROR - ' + strtrim(nitem,2) + ' items specified, but only ' + $
+             strtrim(N_params()-2,2) + ' input variables supplied'
+
+;  Make sure user supplied enough values for all desired entries
+
+ for i = 0,nitem-1 do begin
+
+    ii = strtrim(i+1,2)
+    test = execute('good = N_elements(v' + ii +') EQ nlist*numvals[i]')
+    if good NE 1 then $
+        message,'Supplied values for item ' + $
+           strtrim(db_item_info('name',itnum[i]),2) + ' must contain '+ $
+                              strtrim(nlist*numvals[i],2)+' elements'  
+
+    test = execute('s=size(v' + ii +')' )
+    if s[s[0] + 1] NE idltype[i] then $
+         message,'Item ' + strtrim(db_item_info('name',itnum[i]),2)+ $
+           ' has an incorrect data type'
+
+    if numvals[i] GT 1 then begin
+         test = execute('v'+ ii + '= transpose(v'+ ii + ')' )
+    endif
+
+ endfor
+
+ nitems = (nitem GT indgen(14) )
+ nbyte = nbyte*numvals
+
+ for i = 0l,nlist-1 do begin
+
+   dbrd,list[i],entry,noconvert=noconvert
+   dbxput,v1[i,*],entry,idltype[0],sbyte[0],nbyte[0]
+     if nitems[1] then begin
+        dbxput,v2[i,*],entry,idltype[1],sbyte[1],nbyte[1]
+     if nitems[2] then begin 
+        dbxput,v3[i,*],entry,idltype[2],sbyte[2],nbyte[2]
+     if nitems[3] then begin 
+        dbxput,v4[i,*],entry,idltype[3],sbyte[3],nbyte[3]
+     if nitems[4] then begin 
+        dbxput,v5[i,*],entry,idltype[4],sbyte[4],nbyte[4]
+     if nitems[5] then begin 
+        dbxput,v6[i,*],entry,idltype[5],sbyte[5],nbyte[5]
+     if nitems[6] then begin 
+        dbxput,v7[i,*],entry,idltype[6],sbyte[6],nbyte[6]
+     if nitems[7] then begin 
+        dbxput,v8[i,*],entry,idltype[7],sbyte[7],nbyte[7]
+     if nitems[8] then begin 
+        dbxput,v9[i,*],entry,idltype[8],sbyte[8],nbyte[8]
+     if nitems[9] then begin 
+        dbxput,v10[i,*],entry,idltype[9],sbyte[9],nbyte[9]
+     if nitems[10] then begin 
+        dbxput,v11[i,*],entry,idltype[10],sbyte[10],nbyte[10]
+     if nitems[11] then begin 
+        dbxput,v12[i,*],entry,idltype[11],sbyte[11],nbyte[11]
+     if nitems[12] then begin 
+        dbxput,v13[i,*],entry,idltype[12],sbyte[12],nbyte[12]
+     if nitems[13] then $
+        dbxput,v14[i,*],entry,idltype[13],sbyte[13],nbyte[13]
+   endif & endif & endif & endif & endif & endif & endif & endif & endif
+   endif & endif & endif 
+   dbwrt,entry, noconvert = noconvert
+
+ endfor
+
+; Transpose back any multiple value items
+
+ for i = 0,nitem-1 do begin           
+    if numvals[i] GT 1 then begin
+	ii = strtrim(i+1,2)
+        test = execute('v'+ ii + '= transpose(v'+ ii + ')' )
+    endif
+ endfor
+
+;   Check if the indexed file needs to be updated
+
+ if keyword_set(NOINDEX) then return
+
+ indextype = db_item_info( 'INDEX', itnum)
+ index = where( indextype, nindex)                  ;Indexed items
+ if nindex GT 0 then begin
+     message, 'Now updating indexed file', /INFORM     
+     dbindex, itnum[index]
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dbval.pro b/Code/script_idl_mv/astrolib/dbval.pro
new file mode 100644
index 0000000000000000000000000000000000000000..747f2142dd25f9315bb3f94a9c3acbeeb0db1aca
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbval.pro
@@ -0,0 +1,50 @@
+function dbval,entry,item
+;+
+; NAME:
+;	DBVAL
+; PURPOSE:
+;	procedure to extract value(s) of the specified item from
+;	a data base file entry.
+;
+; CALLING SEQUENCE:
+;	result = dbval( entry, item )
+;
+; INPUTS:
+;	entry - byte array containing the entry, or a scalar entry number
+;	item - name (string) or number (integer) of the item
+;
+; OUTPUT:
+;	the value(s) will be returned as the function value
+;
+; EXAMPLE:
+;	Extract a flux vector from entry 28 of the database FARUV
+;	==> flux = dbval(28,'FLUX')
+;
+; HISTORY:
+;   version 2  D. Lindler Nov, 1987	(new db format)
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;-------------------------------------------------------------------
+;
+; get item info
+;
+db_item,item,itnum,ival,idltype,sbyte,numvals,nbytes
+;
+; check to see if entry is a valid array
+;
+s=size(entry)
+if s[0] gt 0 then begin		;array supplied
+	if(s[0] ne 1) then begin	;is entry a 1-d array
+		print,'entry must be a 1-d byte array, dbval aborting'
+		retall
+	endif
+	if(s[2] ne 1) then begin	;check if byte array
+		print,'entry must be a byte array, dbval aborting'
+		retall
+	endif
+	return,dbxval(entry,idltype[0],numvals[0],sbyte[0],nbytes[0])
+end else begin			;scalar supplied (assume entry number)
+	dbrd,entry,e		;read entry
+	return,dbxval(e,idltype[0],numvals[0],sbyte[0],nbytes[0]);return value(s)
+end
+end
diff --git a/Code/script_idl_mv/astrolib/dbwrt.pro b/Code/script_idl_mv/astrolib/dbwrt.pro
new file mode 100644
index 0000000000000000000000000000000000000000..34f39f4d0aa2cf316e9bd3dce8d953ad119a9263
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbwrt.pro
@@ -0,0 +1,195 @@
+pro dbwrt,entry,index,append,noconvert=noconvert
+;+
+; NAME:
+;	DBWRT
+; PURPOSE:
+;	procedure to update or add a new entry to a data base
+;
+; CALLING SEQUENCE:
+;	dbwrt, entry, [ index, append, /NoConvert ]
+;
+; INPUTS:
+;	entry - entry record to be updated or added if first
+;		item (entry number=0)
+;
+; OPTIONAL INPUTS:
+;	index - optional integer flag,  if set to non zero then index
+;		file is  updated. (default=0, do not update index file)
+;		(Updating the index file is time-consuming, and should
+;		normally be done after all changes have been made.
+;	append - optional integer flag, if set to non-zero the record
+;		is appended as a new entry, regardless of what the
+;		entry number in the record is.  The entry number will
+;		be reset to the next entry number in the file.
+; OUTPUTS:
+;	data base file is updated.                    
+;	If index is non-zero then the index file is updated.
+; OPTIONAL INPUT KEYWORD:
+;	NoConvert - If set then don't convert to host format with an external
+;		database.    Useful when the calling program decides that
+;		conversion isn't needed (i.e. on a big-endian machine), or 
+;		takes care of the conversion itself.
+; OPERATIONAL NOTES:
+;	!PRIV must be greater than 1 to execute
+; HISTORY:
+;	version 2  D. Lindler  Feb. 1988 (new db format)
+;	converted to IDL Version 2.  M. Greason, STX, June 1990.
+;	William Thompson, GSFC/CDS (ARC), 28 May 1994
+;		Added support for external (IEEE) representation.
+;	Faster handling of byte swapping  W. L.  August 2010
+;-
+;-------------------------------------------------------------------
+ COMMON db_com,qdb,qitems,qdbrec
+
+ if N_params() LT 2 then index=0
+ if N_params() LT 3 then append=0
+
+; Byte swapping is needed if database is in external format, and user is on 
+; a little endian machine, and /noconvert is not st 
+
+ bswap = (qdb[119] eq 1) && ~keyword_set(noconvert) && ~is_ieee_big()
+
+ 
+; get some info on the data base
+
+ update = db_info( 'UPDATE' )   
+ if update EQ 0 then message,'Database opened for read only'
+
+ len = db_info( 'LENGTH', 0 )	;record length
+ qnentry = db_info( 'ENTRIES', 0 )
+
+; determine if entry is correct size
+
+ s = size(entry)
+ if s[0] NE 1 then message,'Entry must be a 1-dimensional array'
+
+ if s[1] NE len then $
+	message,'Entry not the proper length of '+strtrim(len,2)+' bytes'
+
+ if s[2] NE 1 then $
+        message,'Entry vector (first parameter) must be a byte array'
+
+; get entry number
+
+ enum = append ? 0 : dbxval(entry,3,1,0,4)
+ if ( enum GT qnentry ) || ( enum LT 0 ) then $
+    message,'Invalid entry number of '+strtrim(enum,2)+' (first value in entry)'
+
+ if enum EQ 0 then begin		;add new entry
+	qnentry = qnentry+1
+	qdb[84] = byte(qnentry,0,4)
+	enum = qnentry
+	dbxput,long(enum),entry,3,0,4
+        newentry = 1b
+ endif else newentry =0b
+ if bswap then begin
+      tmp = entry 
+      db_ent2ext, tmp
+      qdbrec[enum]=tmp
+  endif else qdbrec[enum] =  entry
+ 
+; update index file if necessary
+
+ if index EQ 0 then return
+ nitems = db_info( 'ITEMS', 0 )                    ;Total number of items
+ indextype = db_item_info( 'INDEX', indgen(nitems))  ;Which ones are indexed?
+ indexed = where(indextype,nindex)
+ if nindex LE 0 then return            ;If no indexed items, then we are done
+ indextype = indextype[indexed]        ;Now contains only indexed items
+ unit = db_info( 'UNIT_DBX', 0 )
+ reclong = assoc(unit,lonarr(2),0)
+ h = reclong[0]
+ maxentries = h[1]
+ if bswap then swap_endian_inplace, maxentries
+ if newentry then $
+   if (maxentries LT qnentry) then begin   ;Enough room for new indexed items?
+     print,'DBWRT -- maxentries too small'
+     print,'Rerun DBCREATE with maxentries in .dbd file at least ',qnentry
+     return
+ endif
+
+ reclong = assoc(unit,lonarr(7,nindex),8)
+ header = reclong[0]
+ if bswap then swap_endian_inplace,header
+ hitem = header[0,*]            ;indexed item number
+ hblock = header[3,*]
+ sblock = header[4,*]  & sblock = sblock[*]
+ iblock = header[5,*]  & iblock = iblock[*]
+ ublock = header[6,*]  & ublock = ublock[*]
+ db_item, indexed, itnum, ivalnum, idltype, startbyte, numvals, nbytes
+ pos = where(hitem EQ itnum ) 
+ for i = 0, nindex-1 do begin
+     v = dbxval( entry, idltype[i], numvals[i], startbyte[i], nbytes[i] )
+     sbyte = nbytes[i] * (enum-1)  
+     isort = (indextype[i] EQ 3) || (indextype[i] EQ 4)
+
+     datarec = dbindex_blk(unit, sblock[pos[i]], 512, sbyte, idltype[i])
+     reclong = assoc(unit,lonarr(1),(iblock[pos]*512L))
+
+     case indextype[i] of
+
+	1:  datarec[0] = bswap ? swap_endian(v) : v
+	    
+
+	2:  begin
+	      datarec[0] = bswap ? swap_endian(v) : v
+	      if (qnentry mod 512) EQ 0 then begin        ;Update
+	      nb = qnentry/512
+              hbyte = nbytes[i] * nb
+              datarec = dbindex_blk(unit,hblock[pos[i]],512,hbyte,idltype[i])
+	      datarec[0] = bswap ? swap_endian(v) : v
+              endif
+      end
+	3: begin                          ;SORT
+
+	   datarec = dbindex_blk(unit,sblock[pos[i]],512,0,idltype[i])
+	   values = datarec[0:(qnentry-1)]                  ;Read in old values
+	   if bswap then swap_endian_inplace, values
+	   reclong = dbindex_blk(unit,iblock[pos[i]],512,0,3)
+	   sub = reclong[0:(qnentry-1)]                     ;Read in old indices
+	   if bswap then swap_endian_inplace, sub
+	   if enum lt qnentry then begin       		;Change an old value?
+	       sort_index = where(sub EQ enum)          ;Which value to change
+	       sort_index = sort_index[0]
+	       if values[sort_index] EQ v $      ;Value remains the same so
+                   then isort =0  $          ;don't bother sorting again
+	        else values[sort_index] = v            ;Update with new value
+	   endif else values = [values,v]            ;Append a new value
+	   end
+
+	4: begin                          ;SORT/INDEX
+
+	   values = datarec[qnentry-1,ublock*512]    ;Update index record
+	   if bswap then swap_endian_inplace, values
+	   if enum lt qnentry then begin
+	        if values[enum-1] EQ v then isort = 0 else values[enum-1] = v 
+ 	   endif else  values = [values,v]
+	   datarec = dbindex_blk(unit,ublock[pos[i]],512,sbyte,idltype[i])
+	   datarec[0] = bswap ? swap_endian(v) : v
+	   end
+
+	else:
+
+	endcase
+
+ if isort then begin                  ;resort values?
+	sub = bsort(values)
+	values = values[sub]
+	nb = (qnentry + 511)/512
+	ind = indgen(nb)*512L
+	sval = values[ind]
+;
+	datarec = dbindex_blk(unit, hblock[pos[i]], 512, 0, idltype[i])
+	datarec[0] = bswap ? swap_endian(sval) : sval
+;
+	datarec = dbindex_blk(unit, sblock[pos[i]], 512, 0, idltype[i])
+	datarec[0] = bswap ?swap_endian(values) : values
+;
+	reclong = dbindex_blk(unit, iblock[pos[i]], 512, 0, 3)
+	reclong[0] = bswap ?swap_endian(sub+1) : sub+1
+ endif
+
+ endfor
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dbxput.pro b/Code/script_idl_mv/astrolib/dbxput.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5de3f6c19edad9a6a6a19cc8cdc0a2d161e320e7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbxput.pro
@@ -0,0 +1,56 @@
+pro dbxput,val,entry,idltype,sbyte,nbytes
+;+
+; NAME:
+;	DBXPUT
+; PURPOSE:
+;	routine to replace value of an item in a data base entry
+;
+; CALLING SEQUENCE:	
+;	dbxput, val, entry, idltype, sbyte, nbytes
+;
+; INPUT:
+;	val - value(s) to be placed into entry, string values might be
+;		truncated to fit number of allowed bytes in item
+;	entry - entry or entries to be updated
+;	idltype - idl data type for item (1-7)
+;	sbyte - starting byte in record
+;	nbytes - total number of bytes in value added
+;
+; OUTPUT:
+;	entry - (updated)
+;
+; OPERATIONAL NOTES:
+;	This routine assumes that the calling procedure or user knows what he 
+;	or she is doing.  String items are truncated or padded to the fixed 
+;	size specified by the database but otherwise no validity checks are 
+;	made.
+;
+; HISTORY:
+;	version 1, D. Lindler   Aug, 1986
+;	converted to IDL Version 2.  M. Greason, STX, June 1990.
+;	Work with multiple element string items   W. Landsman  August 1995
+;	Really work with multiple element string items   
+;			R. Bergman/W. Landsman  July 1996
+;	Work with multiple entries, R. Schwartz, GSFC/SDAC August 1996
+;	Use /overwrite with REFORM() W. Landsman May 1997
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;-------------------------------------------------------
+;
+nentry = n_elements(entry[0,*])
+case idltype of		;case of data type
+
+   7: begin			;string
+	numvals = N_elements(val)                   ;Number of input values
+	nbyte = nbytes/numvals                      ;Number of bytes/value
+	val = strmid(val,0,nbyte)                   ;Truncate string
+	temp = replicate( 32b, nbyte, numvals, nentry)	    ;Array of blanks
+	for i = 0, numvals-1 do temp[0,i,0] = byte(val[i,*])     ;Fill with values
+	entry[sbyte:sbyte+nbytes-1,*] = reform(temp,nbytes,nentry, /over)  
+      end
+   1: entry[sbyte:sbyte+nbytes-1,*]=val
+   else: entry[sbyte:sbyte+nbytes-1,*] = byte(val,0,nbytes,nentry)
+
+endcase
+return
+end
diff --git a/Code/script_idl_mv/astrolib/dbxval.pro b/Code/script_idl_mv/astrolib/dbxval.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4b0693dfc4dadefc2d4cf67faf69623a91c10c4f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dbxval.pro
@@ -0,0 +1,71 @@
+function dbxval,entry,idltype,nvalues,sbyte,nbytes,bswap=bswap
+;+
+; NAME: 
+;       DBXVAL
+;
+; PURPOSE:      
+;       Quickly return a value of the specified item number     
+; EXPLANATION:
+;       Procedure to quickly return a value of the specified item number
+;       from the entry.
+;
+; CALLING SEQUENCE:     
+;       result = dbxval( entry, idltype, nvalues, sbyte, nbytes )
+;
+; INPUTS        
+;       entry - entry or entries from data base (bytarr) 
+;       idltype - idl data type (obtained with db_item_info)
+;       nvalues - number of values to return (obtained with db_item)
+;       sbyte - starting byte in the entry (obtained with db_item)
+;       nbytes - number of bytes (needed only for string type)
+;                       (obtained with db_item)
+;
+; OUTPUTS:      
+;       function value is value of the specified item in entry
+;
+; KEYWORDS:
+;       bswap - If set, then IEEE_TO_HOST is called.
+;
+; RESTRICTIONS: 
+;       To increase speed the routine assumes that entry and item are
+;       valid and that the data base is already opened using dbopen.
+;
+; REVISION HISTORY:     
+;       version 0  D. Lindler Nov. 1987  (for new db format)
+;       Version 1, William Thompson, GSFC, 28 March 1994.
+;                       Incorporated into CDS library.
+;       Version 2, Richard Schwartz, GSFC/SDAC, 23 August 1996
+;                       Allowed Entry to have 2 dimensions
+;       Version 2.1, 22 Feb 1997, JK Feggans, 
+;                               avoid reform for strings arrays.
+;       Version 2.2     Use overwrite with REFORM(),  W. Landsman,  May 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Work for multiple-valued strings   W. Landsman   October 2000
+;       Add new 64bit & unsigned integer datatypes W.Landsman   July 2001
+;       Version 3, 2-May-2003, JK Feggans/Sigma, W.T. Thompson
+;           Added BSWAP keyword to avoid floating errors on some platforms.
+;-
+;----------------------------------------------------------------
+;
+;
+nentry = n_elements(entry[0,*])
+
+case idltype of                 ;case of data type
+  1: val = byte(entry[sbyte:sbyte+nvalues-1,*],0,nvalues,nentry)
+  2: val = fix(entry[sbyte:sbyte+nvalues*2-1,*],0,nvalues,nentry)
+  3: val = long(entry[sbyte:sbyte+nvalues*4-1,*],0,nvalues,nentry)
+  4: val = float(entry[sbyte:sbyte+nvalues*4-1,*],0,nvalues,nentry)
+  5: val = double(entry[sbyte:sbyte+nvalues*8-1,*],0,nvalues,nentry)
+  7: val = string( reform( entry[sbyte:sbyte+nbytes-1,*], nbytes/nvalues, $
+                   nvalues, nentry))
+ 12: val = uint(entry[sbyte:sbyte+nvalues*2-1,*],0,nvalues,nentry)
+ 13: val = ulong(entry[sbyte:sbyte+nvalues*4-1,*],0,nvalues,nentry)
+ 14: val = long64(entry[sbyte:sbyte+nvalues*8-1,*],0,nvalues,nentry)
+ 15: val = ulong64(entry[sbyte:sbyte+nvalues*8-1,*],0,nvalues,nentry)
+endcase
+;
+if keyword_set(bswap) then ieee_to_host,val,idltype=idltype
+
+if ( nvalues EQ 1 and nentry EQ 1) then return,val[0] else $
+        if idltype eq 7 then return,val else return,reform(val,/overwrite)
+end
diff --git a/Code/script_idl_mv/astrolib/delvarx.pro b/Code/script_idl_mv/astrolib/delvarx.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c7565058111c2700755db4c8dc86075219f7aa96
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/delvarx.pro
@@ -0,0 +1,52 @@
+;+
+; NAME: 
+;	DELVARX
+; PURPOSE: 
+; 	Delete up to 10 variables for memory management (can call from routines) 
+; EXPLANATION:
+;	Like intrinsic DELVAR function, but can be used from any calling level
+;   
+;       Modified in January 2012 to always free memory associated with
+;       pointers/objects and remove the use of EXECUTE()
+;       Also look at the Coyote routine UNDEFINE
+;          http://www.idlcoyote.com/programs/undefine.pro
+; 
+; CALLING SEQUENCE:
+; 	DELVARX,  p0, [p1, p2......p9]
+;
+; INPUTS: 
+;	p0, p1...p9 - variables to delete
+;
+; OBSOLETE KEYWORD:
+;       /FREE_MEM -  formerly freed memory associated with pointers 
+;                   and objects.  Since this is now the DELVARX default this 
+;                   keyword does nothing.   
+;           
+; METHOD: 
+;	Uses HEAP_FREE and PTR_NEW(/NO_COPY) to delete variables and free
+;       memory   
+;
+; REVISION HISTORY:
+;	Copied from the Solar library, written by slf, 25-Feb-1993
+;	Added to Astronomy Library,  September 1995
+;       Modified, 26-Mar-2003, Zarro (EER/GSFC) 26-Mar-2003
+;       - added FREE_MEM to free pointer/objects
+;       Modified, 28-Jan-2012, E. Rykoff (SLAC), W. Landsman - 
+;               replace EXECUTE calls with SCOPE_VARFETCH.
+;-
+
+PRO delvarx, p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,free_mem = free_mem
+
+ npar = N_params()      ; Number of parameters
+ pp = 'p'+strtrim(indgen(npar),1)
+
+ for i=0,npar-1 do begin
+    defined = N_elements( SCOPE_VARFETCH(pp[i],LEVEL=0))   
+    if LOGICAL_TRUE(defined) then $
+             heap_free, ptr_new( SCOPE_VARFETCH(pp[i],LEVEL=0),/no_copy) 
+        
+ endfor
+
+ return
+ end
+
diff --git a/Code/script_idl_mv/astrolib/deredd.pro b/Code/script_idl_mv/astrolib/deredd.pro
new file mode 100644
index 0000000000000000000000000000000000000000..880f0d4256f631b775bf7e718e7d60954049635c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/deredd.pro
@@ -0,0 +1,55 @@
+pro deredd,Eby,by,m1,c1,ub,by0,m0,c0,ub0, update = update
+;+
+; NAME:
+;     DEREDD
+;
+; PURPOSE:
+;     Deredden stellar Stromgren parameters given for a value of E(b-y)
+; EXPLANATION:
+;     See the procedure UVBYBETA for more info.
+;
+;  CALLING SEQUENCE:
+;     deredd, eby, by, m1, c1, ub, by0, m0, c0, ub0, /UPDATE
+;
+;  INPUTS:
+;     Eby - color index E(b-y),scalar  (E(b-y) = 0.73*E(B-V) )
+;     by - b-y color (observed)
+;     m1 - Stromgren line blanketing parameter (observed)
+;     c1 - Stromgren Balmer discontinuity parameter (observed)
+;     ub - u-b color (observed)
+;
+;     These input values are unaltered unless the /UPDATE keyword is set
+;  OUTPUTS:
+;     by0 - b-y color (dereddened)
+;     m0 - Line blanketing index (dereddened)
+;     c0 - Balmer discontinuity parameter (dereddened)
+;     ub0 - u-b color (dereddened)
+;
+;  OPTIONAL INPUT KEYWORDS:
+;     /UPDATE - If set, then input parameters are updated with the dereddened
+;           values (and output parameters are not used).
+;  REVISION HISTORY:
+;     Adapted from FORTRAN routine DEREDD by T.T. Moon 
+;     W. Landsman          STX Co.        April, 1988
+;     Converted to IDL V5.0   W. Landsman   September 1997
+;-   
+ if N_Params() LT  2 then begin
+       print,'Syntax - DEREDD, eby, by, m1, c1, ub, by0, m0, c0, ub0'
+       return
+ endif            
+
+ Rm1 = -0.33 & Rc1 = 0.19 & Rub = 1.53 
+ Eby0 = Eby >0
+ if keyword_set(update) then begin
+       by = by - eby0
+       if N_elements(m1) GT 0 then m1 = m1 - Rm1*Eby0
+       if N_elements(c1) GT 0 then c1 = c1 - Rc1*Eby0
+       if N_elements(ub) GT 0 then ub = ub - Rub*Eby0
+ endif  else begin  
+       by0 = by - Eby0
+       m0 = m1 - Rm1*Eby0
+       c0 = c1 - Rc1*Eby0
+       ub0 = ub - Rub*Eby0
+ endelse
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/detabify.pro b/Code/script_idl_mv/astrolib/detabify.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c57f7c698568535a25b2699ab6c9e0c48db83a3b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/detabify.pro
@@ -0,0 +1,62 @@
+	FUNCTION DETABIFY, CHAR_STR
+;+
+; NAME:
+;	DETABIFY
+; PURPOSE:
+;	Replaces tabs in character strings with appropriate number of spaces
+; EXPLANATION:
+;	The number of space characters inserted is calculated to space
+;	out to the next effective tab stop, each of which is eight characters
+;	apart.
+;
+; CALLING SEQUENCE:
+;	Result = DETABIFY( CHAR_STR )
+;
+; INPUT PARAMETERS:
+;	CHAR_STR = Character string variable (or array) to remove tabs from.
+;
+; OUTPUT:
+;	Result of function is CHAR_STR with tabs replaced by spaces.
+;
+; RESTRICTIONS:
+;	CHAR_STR must be a character string variable.
+;
+; MODIFICATION HISTORY:
+;	William Thompson, Feb. 1992.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 1 THEN MESSAGE,'Syntax:  Result = DETABIFY(CHAR_STR)'
+;
+;  Make sure CHAR_STR is of type string.
+;
+	SZ = SIZE(CHAR_STR)
+	IF SZ[SZ[0]+1] NE 7 THEN BEGIN
+		MESSAGE,/INFORMATIONAL,'CHAR_STR must be of type string'
+		RETURN, CHAR_STR
+	ENDIF
+;
+;  Step through each element of CHAR_STR.
+;
+	STR = CHAR_STR
+	FOR I = 0,N_ELEMENTS(STR)-1 DO BEGIN
+;
+;  Keep looking for tabs until there aren't any more.
+;
+		REPEAT BEGIN
+			TAB = STRPOS(STR[I],STRING(9B))
+			IF TAB GE 0 THEN BEGIN
+				NBLANK = 8 - (TAB MOD 8)
+				STR[I] = STRMID(STR[I],0,TAB) +		$
+					STRING(REPLICATE(32B,NBLANK)) +	$
+					STRMID(STR[I],TAB+1,STRLEN(STR[I])-TAB-1)
+			ENDIF
+		ENDREP UNTIL TAB LT 0
+	ENDFOR
+;
+	RETURN, STR
+	END
diff --git a/Code/script_idl_mv/astrolib/dist_circle.pro b/Code/script_idl_mv/astrolib/dist_circle.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a5457bfdaa32f87dda0d94a0f7ca5e3e4090a38e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dist_circle.pro
@@ -0,0 +1,97 @@
+pro dist_circle ,im, n, xcen ,ycen, DOUBLE = double 
+;+
+; NAME: 
+;      DIST_CIRCLE
+; PURPOSE:      
+;      Form a square array where each value is its distance to a given center.
+; EXPLANATION:
+;      Returns a square array in which the value of each element is its 
+;      distance to a specified center. Useful for circular aperture photometry.
+;
+; CALLING SEQUENCE:
+;      DIST_CIRCLE, IM, N, [ XCEN, YCEN,  /DOUBLE ]
+;
+; INPUTS:
+;      N = either  a scalar specifying the size of the N x N square output
+;               array, or a 2 element vector specifying the size of the
+;               N x M rectangular output array.
+;
+; OPTIONAL INPUTS:
+;      XCEN,YCEN = Scalars designating the X,Y pixel center.  These need
+;               not be integers, and need not be located within the
+;               output image.   If not supplied then the center of the output
+;               image is used (XCEN = YCEN = (N-1)/2.).
+;
+; OUTPUTS:
+;       IM  - N by N (or M x N) floating array in which the value of each 
+;               pixel is equal to its distance to XCEN,YCEN
+;
+; OPTIONAL INPUT KEYWORD:
+;       /DOUBLE - If this keyword is set and nonzero, the output array will
+;               be of type DOUBLE rather than floating point.
+;
+; EXAMPLE:
+;       Total the flux in a circular aperture within 3' of a specified RA
+;       and DEC on an 512 x 512 image IM, with a header H.
+;
+;       IDL> adxy, H, RA, DEC, x, y       ;Convert RA and DEC to X,Y
+;       IDL> getrot, H, rot, cdelt        ;CDELT gives plate scale deg/pixel
+;       IDL> cdelt = cdelt*3600.          ;Convert to arc sec/pixel
+;       IDL> dist_circle, circle, 512, x, y  ;Create a distance circle image
+;       IDL> circle = circle*abs(cdelt[0])   ;Distances now given in arcseconds
+;       IDL> good = where(circle LT 180)  ;Within 3 arc minutes
+;       IDL> print,total( IM[good] )      ;Total pixel values within 3'
+;
+; RESTRICTIONS:
+;       The speed of DIST_CIRCLE decreases and the the demands on virtual
+;       increase as the square of the output dimensions.   Users should
+;       dimension the output array as small as possible, and re-use the
+;       array rather than re-calling DIST_CIRCLE
+;
+; MODIFICATION HISTORY:
+;       Adapted from DIST    W. Landsman            March 1991
+;       Allow a rectangular output array   W. Landsman     June 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Add /DOUBLE keyword, make XCEN,YCEN optional  W. Landsman Jun 1998
+;-
+ On_error,2                ;Return to caller if an error occurs
+
+ if N_params() LT 2  then begin
+     print,'Syntax - DIST_CIRCLE, im, n,[ xcen, ycen, /DOUBLE ]' 
+     print,'IM - output image array'
+     print,'N - size of the output image array, scalar or 2 element vector'
+     print,'XCEN,YCEN - position from which to specify distances'
+     return
+ endif
+
+ if N_elements(N) EQ 2 then begin
+        nx = n[0]
+        ny = n[1] 
+ endif else if N_elements(N) EQ 1 then begin
+        ny = n
+        nx = n                    ;Make a row
+ endif else message, $
+        'ERROR - Output size parameter N must contain 1 or 2 elements'
+
+
+ if N_params() LT 4 then begin
+        xcen = (nx-1)/2. & ycen = (ny-1)/2.
+ endif
+
+
+ if keyword_set(DOUBLE) then begin
+         x_2 = (dindgen(nx) - xcen) ^ 2     ;X distances (squared)
+         y_2 = (dindgen(ny) - ycen) ^ 2     ;Y distances (squared)  
+         im = dblarr( nx, ny, /NOZERO)      ;Make uninitialized output array
+ endif else begin
+         x_2 = (findgen(nx) - xcen) ^ 2     ;X distances (squared)
+         y_2 = (findgen(ny) - ycen) ^ 2     ;Y distances (squared)  
+         im = fltarr( nx, ny, /NOZERO)      ;Make uninitialized output array
+ endelse
+
+ for i = 0L, ny-1 do begin                ;Row loop
+        im[0,i] = sqrt(x_2 + y_2[i])     ;Euclidian distance
+ endfor
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/dist_ellipse.pro b/Code/script_idl_mv/astrolib/dist_ellipse.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0e23c31fb22906a6d59890f6e635de55a38766be
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/dist_ellipse.pro
@@ -0,0 +1,121 @@
+pro dist_ellipse,im,n,xc,yc,ratio,pos_ang, DOUBLE = double
+;+
+; NAME:
+;       DIST_ELLIPSE
+; PURPOSE:
+;       Create a mask array useful for elliptical aperture photemetry
+; EXPLANATION:
+;       Form an array in which the value of each element is equal to the
+;       semi-major axis of the ellipse of specified center, axial ratio, and 
+;       position  angle, which passes through that element.  Useful for 
+;       elliptical aperture photometry.
+;
+; CALLING SEQUENCE:
+;       DIST_ELLIPSE, IM, N, XC, YC, RATIO, [ POS_ANG] , /DOUBLE
+;
+; INPUTS:
+;       N = either  a scalar specifying the size of the N x N square output
+;               array, or a 2 element vector specifying the size of the
+;               M x N rectangular output array.
+;       XC,YC - Scalars giving the position of the ellipse center.   This does
+;               not necessarily have to be within the image
+;       RATIO - Scalar giving the ratio of the major to minor axis.   This 
+;               should be greater than 1 for position angle to have its 
+;               standard meaning.
+;
+; OPTIONAL INPUTS:
+;       POS_ANG - Position angle of the major axis in degrees, measured counter-clockwise
+;               from the Y axis.  For an image in standard orientation 
+;               (North up, East left) this is the astronomical position angle.
+;               Default is 0 degrees.
+;
+; OPTIONAL INPUT KEYWORD:
+;       /DOUBLE - If this keyword is set and nonzero, the output array will
+;               be of type DOUBLE rather than floating point.
+;
+; OUTPUT:
+;       IM - REAL*4 elliptical mask array, of size M x N.  THe value of each 
+;               pixel is equal to the semi-major axis of the ellipse of center
+;                XC,YC, axial ratio RATIO, and position angle POS_ANG, which 
+;               passes through the pixel.
+;
+; EXAMPLE:
+;       Total the flux in a elliptical aperture with a major axis of 3', an
+;       axial ratio of 2.3, and a position angle of 25 degrees centered on 
+;       a specified RA and DEC.   The image array, IM is 200 x 200, and has 
+;       an associated FITS header H.
+;
+;       ADXY, H, ra, dec, x, y       ;Get X and Y corresponding to RA and Dec
+;       GETROT, H, rot, cdelt        ;CDELT gives plate scale degrees/pixel
+;       cdelt = abs( cdelt)*3600.    ;CDELT now in arc seconds/pixel
+;       DIST_ELLIPSE, ell, 200, x, y, 2.3, 25  ;Create a elliptical image mask
+;       ell = ell*cdelt(0)           ;Distances now given in arcseconds
+;       good = where( ell lt 180 )   ;Within 3 arc minutes
+;       print,total( im(good) )      ;Total pixel values within 3'
+;
+; RESTRICTIONS:
+;       The speed of DIST_ELLIPSE decreases and the the demands on virtual
+;       increase as the square of the output dimensions.   Users should
+;       dimension the output array as small as possible, and re-use the
+;       array rather than re-calling DIST_ELLIPSE
+;
+; REVISION HISTORY:
+;       Written    W. Landsman             April, 1991
+;       Somewhat faster algorithm          August, 1992
+;       Allow rectangular output array     June, 1994
+;       Added /DOUBLE keyword   W. Landsman   July 2000
+;       Make POS_ANG optional, as documented  W. Landsman Aug 2015
+;-
+ On_error,2                             ;Return to caller
+
+ if N_params() LT 5 then begin
+    print,'Syntax - DIST_ELLIPSE, im, n, xc, yc, ratio, [pos_ang], /DOUBLE'
+    print,'   im - output elliptical mask image array'
+    print,'   n -  size of output image mask, scalar or 2 element vector'
+    print,'   xc,yc - coordinates of ellipse center, scalars'
+    print,'   ratio - ratio of major to minor axis of ellipse, scalar'
+    print,'   pos_ang - position angle, counterclockwise from up'
+    return
+ endif
+                                          ;Check some parameters
+ if N_elements(ratio) NE 1 then message, $
+     'ERROR - Axial ratio (fifth parameter) must be a scalar value'
+
+ if N_elements(pos_ang) GT 1 then message, $
+     'ERROR - Position angle (sixth parameter) must be a scalar value'
+
+ if N_elements(pos_ang) EQ 0 then pos_ang = 0
+ ang = pos_ang /!RADEG                      ;Convert to radians
+ cosang = cos(ang)
+ sinang = sin(ang)
+
+ if N_elements(N) EQ 2 then begin
+        nx = n[0]
+        ny = n[1] 
+ endif else if N_elements(N) EQ 1 then begin
+        ny = n
+        nx = n                    ;Make a row
+ endif else message, $
+        'ERROR - Output size parameter N must contain 1 or 2 elements'
+        
+ if keyword_set(double) then begin
+    x = dindgen(nx) - xc
+    y = dindgen(ny) - yc
+    im = dblarr(nx, ny, /NOZERO)
+ endif else begin
+    x = findgen( nx ) - xc
+    y = findgen( ny ) - yc
+    im = fltarr( nx, ny, /NOZERO )
+ endelse
+                         ;Rotate pixels to match ellipse orientation
+ xcosang = x*cosang
+ xsinang = x*sinang
+
+ for i = 0,ny-1 do begin
+   xtemp =  xcosang + y[i]*sinang
+   ytemp = -xsinang + y[i]*cosang
+   im[0,i] = sqrt( (xtemp*ratio)^2 + ytemp^2 )
+ endfor
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/eci2geo.pro b/Code/script_idl_mv/astrolib/eci2geo.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c39625ec71094c4e56ea98062b66e61994afb4d8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/eci2geo.pro
@@ -0,0 +1,81 @@
+;+
+; NAME:
+;     ECI2GEO
+;
+; PURPOSE:
+;     Convert Earth-centered inertial coordinates to geographic spherical coords
+; EXPLANATION:
+;     Converts from ECI (Earth-Centered Inertial) (X,Y,Z) rectangular 
+;     coordinates to geographic spherical coordinates (latitude, longitude, 
+;     altitude).    JD time is also needed as input.
+;
+;     ECI coordinates are in km from Earth center at the supplied time (True of
+;     Date).     Geographic coordinates are in degrees/degrees/km
+;     Geographic coordinates assume the Earth is a perfect sphere, with radius 
+;     equal to its equatorial radius.
+;
+; CALLING SEQUENCE:
+;     gcoord=eci2geo(ECI_XYZ,JDtime)
+;
+; INPUT:
+;       ECI_XYZ : the ECI [X,Y,Z] coordinates (in km), can be an array [3,n] 
+;                 of n such coordinates.    These should be at the supplied 
+;                 Julian Date (TOD - true of date).
+;       JDtime: the Julian Day time, double precision. Can be a 1-D array of n 
+;                 such times.
+;
+; KEYWORD INPUTS:
+;       None
+;
+; OUTPUT:
+;       a 3-element array of geographic [latitude,longitude,altitude], or an 
+;         array [3,n] of n such coordinates, double precision  
+;
+; COMMON BLOCKS:
+;       None
+;
+; PROCEDURES USED:
+;       CT2LST - Convert Local Civil Time to Local Mean Sidereal Time
+;
+; EXAMPLE:
+;       IDL> gcoord=eci2geo([6378.137+600,0,0], 2452343.38982663D)
+;       IDL> print,gcoord
+;       0.0000000       232.27096       600.00000
+;
+;       (The above is the geographic direction of the vernal point on 
+;       2002/03/09 21:21:21.021, in geographic coordinates. The chosen 
+;       altitude was 600 km.)
+;
+;       gcoord can be further transformed into geodetic coordinates (using 
+;       geo2geodetic.pro) or into geomagnetic coordinates (using geo2mag.pro)
+;
+; MODIFICATION HISTORY:
+;       Written by Pascal Saint-Hilaire (Saint-Hilaire@astro.phys.ethz.ch) on 
+;              2001/05/13
+;       Modified on 2002/05/13, PSH : vectorization + use of JD times  
+;       Document use of TOD epoch R. Redmon  April 2014 NOAA/NGDC     
+;-
+
+;=============================================================================
+FUNCTION eci2geo,ECI_XYZ,JDtim
+
+        Re=6378.137     ; Earth's equatorial radius, in km
+        coord=DOUBLE(ECI_XYZ)
+        JDtime= DOUBLE(JDtim)
+
+        theta=atan(coord[1,*],coord[0,*])       ; azimuth       
+        ct2lst,gst,0,0,JDtime
+        angle_sid=gst*2.*!DPI/24.        ; sidereal angle
+        lon= (theta - angle_sid ) MOD (2* !DPI)                  ;longitude      
+        r=sqrt(coord[0,*]^2+coord[1,*]^2)
+        lat=atan(coord[2,*],r)                                  ; latitude
+        alt=r/cos(lat) - Re                                     ; altitude 
+
+        lat=lat*180./(!DPI)      ; to convert from radians into degrees...
+        lon=lon*180./(!DPI)
+        ss=WHERE(lon LT 0.) 
+        IF ss[0] NE -1 THEN lon[ss]=lon[ss]+360.
+        
+        RETURN,[lat,lon,alt]
+END
+;====================================================================================
diff --git a/Code/script_idl_mv/astrolib/eq2hor.pro b/Code/script_idl_mv/astrolib/eq2hor.pro
new file mode 100644
index 0000000000000000000000000000000000000000..fdb8bf3950c3ea10926ec86c93803967cbc4a2f5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/eq2hor.pro
@@ -0,0 +1,300 @@
+;+
+; NAME:
+;   EQ2HOR
+;
+; PURPOSE:
+;    Convert celestial  (ra-dec) coords to local horizon coords (alt-az).
+;
+; CALLING SEQUENCE:
+;
+;    eq2hor, ra, dec, jd, alt, az, [ha, LAT= , LON= , /WS, OBSNAME= , $
+;                       /B1950 , PRECESS_= 0, NUTATE_= 0, REFRACT_= 0, $
+;                       ABERRATION_= 0, ALTITUDE= , /VERBOSE, _EXTRA= ]
+;
+; DESCRIPTION:
+;  This  code calculates horizon (alt,az) coordinates from equatorial
+;  (ra,dec) coords.   It is typically accurate to about 1 arcsecond or better (I
+;  have checked the output against the publicly available XEPHEM software). It
+;  performs precession, nutation, aberration, and refraction corrections.  The
+;  perhaps best thing about it is that it can take arrays as inputs, in all
+;  variables and keywords EXCEPT Lat, lon, and Altitude (the code assumes these
+;  aren't changing), and uses vector arithmetic in every calculation except
+;  when calculating the precession matrices.
+;
+; INPUT-OUTPUT VARIABLES:
+;       RA   : Right Ascension of object  (J2000) in degrees (FK5); scalar or
+;              vector.
+;       Dec  : Declination of object (J2000) in degrees (FK5), scalar or vector.
+; INPUT VARIABLES:
+;       JD   : Julian Date [scalar or vector]
+;
+;       Note: if RA and DEC are arrays, then alt and az will also be arrays.
+;             If RA and DEC are arrays, JD may be a scalar OR an array of the
+;             same dimensionality.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       lat   : north geodetic latitude of location in degrees
+;       lon   : EAST longitude of location in degrees (Specify west longitude
+;               with a negative sign.)
+;       /WS    : Set this to get the azimuth measured westward from south (not
+;               East of North).
+;       obsname: Set this to a valid observatory name to be used by the
+;              astrolib OBSERVATORY procedure, which will return the latitude
+;              and longitude to be used by this program.
+;       /B1950 : Set this if your ra and dec are specified in B1950, FK4
+;              coordinates (instead of J2000, FK5)
+;       precess_ : Set this to 1 to force precession [default], 0 for no
+;               precession correction
+;       nutate_  : Set this to 1 to force nutation [default], 0 for no nutation.
+;       aberration_ : Set this to 1 to force aberration correction [default],
+;                     0 for no correction.
+;       refract_ : Set to 1 to force refraction correction [default], 0 for no
+;                     correction.
+;       altitude: The altitude of the observing location, in meters. [default=0].
+;       verbose: Set this for verbose output.  The default is verbose=0.
+;       _extra: This is for setting TEMPERATURE or PRESSURE explicitly, which are
+;               used by CO_REFRACT to calculate the refraction effect of the
+;               atmosphere. If you don't set these, the program will make an
+;               intelligent guess as to what they are (taking into account your
+;               altitude).  See CO_REFRACT for more details.
+;
+; OUTPUT VARIABLES: (all double precision)
+;       alt    : altitude (in degrees)
+;       az     : azimuth angle (in degrees, measured EAST from NORTH, but see
+;                keyword WS above.)
+;       ha     : hour angle (in degrees) (optional)
+;
+; DEPENDENCIES:
+;       NUTATE, PRECESS, OBSERVATORY, SUNPOS, ADSTRING()
+;       CO_NUTATE, CO_ABERRATION, CO_REFRACT, ALTAZ2HADEC, SETDEFAULTVALUE
+;
+; BASIC STEPS
+;   Apply refraction correction to find apparent Alt.
+;   Calculate Local Mean Sidereal Time
+;   Calculate Local Apparent Sidereal Time
+;   Do Spherical Trig to find apparent hour angle, declination.
+;   Calculate Right Ascension from hour angle and local sidereal time.
+;   Nutation Correction to Ra-Dec
+;   Aberration correction to Ra-Dec
+;       Precess Ra-Dec to current equinox.
+;
+;
+;CORRECTIONS I DO NOT MAKE:
+;   *  Deflection of Light by the sun due to GR. (typically milliarcseconds,
+;        can be arseconds within one degree of the sun)
+;   *  The Effect of Annual Parallax (typically < 1 arcsecond)
+;   *  and more (see below)
+;
+; TO DO
+;    * Better Refraction Correction.  Need to put in wavelength dependence,
+;    and integrate through the atmosphere.
+;        * Topocentric Parallax Correction (will take into account elevation of
+;          the observatory)
+;    * Proper Motion (but this will require crazy lookup tables or something).
+;        * Difference between UTC and UT1 in determining LAST -- is this
+;          important?
+;        * Effect of Annual Parallax (is this the same as topocentric Parallax?)
+;    * Polar Motion
+;        * Better connection to Julian Date Calculator.
+;
+; EXAMPLE
+;
+;  Find the position of the open cluster NGC 2264 at the Effelsburg Radio
+;  Telescope in Germany, on June 11, 2023, at local time 22:00 (METDST).
+;  The inputs will then be:
+;
+;       Julian Date = 2460107.250
+;       Latitude = 50d 31m 36s
+;       Longitude = 06h 51m 18s
+;       Altitude = 369 meters
+;       RA (J2000) = 06h 40m 58.2s
+;       Dec(J2000) = 09d 53m 44.0s
+;
+;  IDL> eq2hor, ten(6,40,58.2)*15., ten(9,53,44), 2460107.250d, alt, az, $
+;               lat=ten(50,31,36), lon=ten(6,51,18), altitude=369.0, /verb, $
+;                pres=980.0, temp=283.0
+;
+; The program produces this output (because the VERBOSE keyword was set)
+;
+;Latitude = +50 31 36.0   Longitude = +06 51 18.0
+; ************************** 
+;Julian Date =  2460107.250000
+;LMST = +11 46 42.0
+;LAST = +11 46 41.4
+; 
+;Ra, Dec:  06 40 58.2  +09 53 44   (J2000)
+;Ra, Dec:  06 42 15.7  +09 52 19   (J2023.4422)
+;Ra, Dec:  06 42 13.8  +09 52 27   (fully corrected)
+;Hour Angle = +05 04 27.6  (hh:mm:ss)
+;Az, El =  17 42 25.6  +16 25 10   (Apparent Coords)
+;Az, El =  17 42 25.6  +16 28 23   (Observer Coords)
+;
+; Compare this with the result from XEPHEM:
+; Az, El =  17h 42m 25.6s +16d 28m 21s
+;
+; This 1.8 arcsecond discrepancy in elevation arises primarily from slight
+; differences in the way I calculate the refraction correction from XEPHEM, and
+; is pretty typical.
+;
+; AUTHOR:
+;   Chris O'Dell
+;    Assistant Professor of Atmospheric Science
+;    Colorado State University
+;    Email: odell@atmos.colostate.edu
+;   
+;  Revision History: 
+;    August 2012  Use Strict_Extra to flag spurious keywords W. Landsman
+;    May 2013   Fix case of scalar JD but vector RA, Dec W. Landsman
+;    Jun 2014   Fix case of vector JD but scalar RA, Dec W. Landsman
+;-
+
+pro eq2hor, ra, dec, jd, alt, az, ha, lat=lat, lon=lon, WS=WS, obsname=obsname,$
+     B1950 = B1950, verbose=verbose, precess_=precess_, nutate_=nutate_, $
+                refract_ = refract_, aberration_ = aberration_,  $
+                altitude = altitude, _extra= _extra
+
+ On_error,2
+ compile_opt idl2
+ 
+if N_params() LT 4 then begin
+    print,'Syntax - EQ2HOR, ra, dec, jd, alt, az, [ha, LAT= , LON= , /WS, '
+    print,'          OBSNAME= ,/B1950 , PRECESS_= 0, NUTATE_= 0, REFRACT_= 0 '
+    print,'          ABERRATION_= 0, ALTITUDE= , /VERBOSE, TEMPERATURE=, ' +$
+          'PRESSURE = ]'
+     return
+ endif
+
+;*******************************************************************************
+; INITIALIZE STUFF
+
+; If no lat or lng entered, use Pine Bluff Observatory values!
+;   (near Madison, Wisconsin, USA)
+; * Feel free to change these to your favorite observatory *
+v = keyword_set(verbose)
+if keyword_set(obsname) then begin
+        ;override lat,lon, altitude if observatory name has been specified
+        observatory, obsname, obs
+        lat = obs.latitude
+        lon = -1*obs.longitude ; minus sign is because OBSERVATORY uses west
+;                               longitude as positive.
+        altitude = obs.altitude
+endif
+if ~v && ((N_elements(lat) EQ 0 ) || N_elements(lon) Eq 0) then $
+   message,'Using latitude and longitude for Pine Bluff Observatory',/con
+setdefaultvalue, lat,   43.0783d ; (this is the declination of the zenith)
+setdefaultvalue, lon, -89.865d
+setdefaultvalue, altitude, 0.                ; [meters]
+
+setdefaultvalue, precess_, 1
+setdefaultvalue, nutate_, 1
+setdefaultvalue, aberration_, 1
+setdefaultvalue, refract_ , 1
+
+
+; conversion factors
+d2r = !dpi/180.
+h2r = !dpi/12.
+h2d = 15.d
+
+npos = N_elements(ra)
+njd = N_elements(jd)
+
+if ~((npos EQ njd) || (npos EQ 1) || (njd EQ 1)) then message,'Error - ' + $
+   'Either JD or (ra,dec) must be scalars, or have the same # of elements'
+
+if (npos EQ 1) && (njd GT 1) then begin  
+    ra_ = replicate(double(ra[0]),njd)
+    dec_ = replicate(double(dec[0]),njd)
+endif else begin 
+    ra_ = ra
+    dec_ = dec
+endelse    
+
+if keyword_set(B1950) then begin 
+     tstart = 1950.0
+     s_now='   (B1950)' 
+endif else begin 
+     tstart = 2000.0
+     s_now='   (J2000)'
+endelse 
+
+;******************************************************************************
+; PRECESS coordinates to current date
+; (uses astro lib procedure PRECESS.pro)
+J_now = (JD - 2451545.)/365.25 + 2000.0 ; compute current equinox
+if precess_ then begin
+   if njd GT 1 then begin
+       for i=0,n_elements(jd)-1 do begin
+            tmpra = ra_[i]  & tmpdec = dec_[i]
+            precess, tmpra, tmpdec, tstart, J_now[i], FK4 = keyword_set(B1950)
+	    ra_[i] = tmpra & dec_[i] = tmpdec
+        endfor
+    endif else $
+            precess, ra_, dec_, tstart, J_now, FK4 = keyword_set(B1950)
+ endif
+if v then begin      
+   rap = ra_
+   decp = dec_
+endif
+;******************************************************************************
+; calculate NUTATION and ABERRATION Corrections to Ra-Dec
+
+co_nutate, jd, ra_, dec_, dra1, ddec1, eps=eps, d_psi=d_psi
+co_aberration, jd, ra_, dec_, dra2, ddec2, eps=eps
+
+; make nutation and aberration corrections
+ra_ +=  (dra1*nutate_ + dra2*aberration_)/3600.
+dec_ +=  (ddec1*nutate_ + ddec2*aberration_)/3600.
+
+;**************************************************************************************
+;Calculate LOCAL MEAN SIDEREAL TIME
+ct2lst, lmst, lon, 0, jd  ; get LST (in hours) - note:this is independent of
+                           ;time zone  since giving jd
+lmst = lmst*h2d ; convert LMST to degrees (btw, this is the RA of the zenith)
+; calculate local APPARENT sidereal time
+LAST = lmst + d_psi *cos(eps)/3600. ; add correction in degrees
+
+;******************************************************************************
+; Find hour angle (in DEGREES)
+ha = last - ra_
+w = where(ha LT 0, Nw)
+if Nw GT 0 then ha[w] = ha[w] + 360.
+ha = ha mod 360.
+
+;******************************************************************************
+; Now do the spherical trig to get APPARENT alt,az.
+hadec2altaz, ha, dec_, lat, alt, az, WS=WS
+
+;*******************************************************************************************
+; Make Correction for ATMOSPHERIC REFRACTION
+; (use this for visible and radio wavelengths; author is unsure about other wavelengths.
+;  See the comments in CO_REFRACT.pro for more details.)
+if v then alt_app = alt
+if refract_ then alt = $
+      co_refract(alt, altitude=altitude, _strict_extra=_extra, /to_observed)
+if v then begin 
+     print, 'Latitude = ', adstring(lat), '   Longitude = ', adstring(lon)
+     for j=0,njd-1 do begin 
+	  print,' ************************** '
+
+        print, 'Julian Date = ', jd[j], format='(A,f15.6)'
+        print, 'LMST = ', adstring(lmst/15.)
+        print, 'LAST = ', adstring(last/15.)
+	print,' '
+          for i=0,npos-1 do begin
+               print, 'Ra, Dec: ', adstring(ra[i],dec[i]), s_now
+               print, 'Ra, Dec: ', adstring(rap[i],decp[i]), '   (J' + $
+                       strcompress(string(J_now),/rem)+')'
+		      
+               print, 'Ra, Dec: ', adstring(ra_[i],dec_[i]), $
+	               '   (fully corrected)'
+               print, 'Hour Angle = ', adstring(ha[i]/15.), '  (hh:mm:ss)'
+
+	       print,'Az, El = ', adstring(az[i],alt_app[i]), '   (Apparent Coords)'       
+               print,'Az, El = ', adstring(az[i],alt[i]), '   (Observer Coords)'
+               print,' '
+	  endfor 
+	  endfor
+  endif   
+  return  
+end
diff --git a/Code/script_idl_mv/astrolib/eqpole.pro b/Code/script_idl_mv/astrolib/eqpole.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e81654c69d9b57d69c9b1a289b1050a702c50238
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/eqpole.pro
@@ -0,0 +1,57 @@
+pro eqpole,l,b,x,y,southpole=southpole
+;+
+; NAME:
+;       EQPOLE
+; PURPOSE:
+;       Convert RA and Dec to X,Y using an equal-area polar projection.
+; EXPLANATION:
+;       The output X and Y coordinates are scaled to be between
+;       -90 and +90 to go from equator to pole to equator. Output map points 
+;       can be centered on the north pole or south pole.
+;
+; CALLING SEQUENCE:
+;       EQPOLE, L, B, X, Y, [ /SOUTHPOLE ]
+;
+; INPUTS:
+;       L - longitude - scalar or vector, in degrees
+;       B - latitude - same number of elements as RA, in degrees
+;
+; OUTPUTS:
+;       X - X coordinate, same number of elements as RA.   X is normalized to
+;               be between -90 and 90.
+;       Y - Y coordinate, same number of elements as DEC.  Y is normalized to
+;               be between -90 and 90.
+;
+; KEYWORDS:
+;
+;       /SOUTHPOLE      - Keyword to indicate that the plot is to be centered 
+;               on the south pole instead of the north pole.
+;
+; REVISION HISTORY:
+;       J. Bloch        LANL, SST-9     1.1     5/16/91
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+ if N_params() NE 4 then begin
+     print,'Syntax - EQPOLE,L, B, X, Y, [/SOUTHPOLE]'
+     print,'         Input longitude L, latitude B in *degrees*'
+     return
+ endif
+
+ if keyword_set(southpole) then begin
+        l1 = double(-l/!RADEG)
+        b1 = double(-b/!RADEG)
+ endif else begin
+        l1 = double(l/!RADEG)
+        b1 = double(b/!RADEG)
+ endelse
+
+ sq = 2.0d0*(1.0d0 - sin(double(b1)))
+ chk = where(sq lt 0.0d0)
+ if chk[0] ge 0 then sq[chk] = 0.0d0
+ r = 18.0d0*3.53553391d0*sqrt(sq)
+ y =r*cos(l1)
+ x =r*sin(l1)
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/eqpole_grid.pro b/Code/script_idl_mv/astrolib/eqpole_grid.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f400d174ff3f2f986edb76e9dc4c2c8d2ce141b5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/eqpole_grid.pro
@@ -0,0 +1,147 @@
+;+
+; NAME:
+;       EQPOLE_GRID
+;
+; PURPOSE:
+;       Produce an equal area polar projection grid overlay
+; EXPLANATION:
+;       Grid is written on the current graphics device using the equal area 
+;       polar projection.   EQPOLE_GRID assumes that the output plot 
+;       coordinates span the x and y ranges of -90 to 90 for a region that 
+;       covers the equator to the chosen pole. The grid is assumed to go from 
+;       the equator to the chosen pole.
+;
+; CALLING SEQUENCE:
+;
+;       EQPOLE_GRID[,DLONG,DLAT,[/SOUTHPOLE, LABEL = , /NEW, _EXTRA=]
+;
+; INPUTS:
+;
+;       DLONG   = Optional input longitude line spacing in degrees. If left
+;                 out, defaults to 30.
+;       DLAT    = Optional input lattitude line spacing in degrees. If left
+;                 out, defaults to 30.
+;
+; INPUT KEYWORDS:
+;
+;       /SOUTHPOLE       = Optional flag indicating that the output plot is
+;                         to be centered on the south rather than the north
+;                         pole.
+;       LABEL           = Optional flag for creating labels on the output
+;                         grid on the prime meridian and the equator for
+;                         lattitude and longitude lines. If set =2, then
+;                         the longitude lines are labeled in hours and minutes.
+;       CHARSIZE       = If /LABEL is set, then CHARSIZE specifies the size
+;                         of the label characters (passed to XYOUTS)
+;       CHARTHICK     =  If /LABEL is set, then CHARTHICK specifies the 
+;                         thickness of the label characters (passed to XYOUTS)
+;       /NEW          =   If this keyword is set, then EQPOLE_GRID will create
+;                         a new plot, rather than overlay an existing plot.
+;
+;       Any valid keyword to OPLOT such as COLOR, LINESTYLE, THICK can be 
+;       passed to AITOFF_GRID (though the _EXTRA facility) to to specify the
+;       color, style, or thickness of the grid lines.
+; OUTPUTS:
+;       Draws grid lines on current graphics device.
+;
+; EXAMPLE:
+;       Create a labeled equal area projection grid of the Galaxy, centered on
+;       the South pole, and overlay stars at specified Galactic longitudes, 
+;       glong and latitudes, glat
+;
+;       IDL> eqpole_grid,/label,/new,/south       ;Create labeled grid
+;       IDL> eqpole, glong, glat, x,y      ;Convert to X,Y coordinates
+;       IDL> plots,x,y,psym=2              ;Overplot "star" positions.
+;
+;
+; COPYRIGHT NOTICE:
+;
+;       Copyright 1992, The Regents of the University of California. This
+;       software was produced under U.S. Government contract (W-7405-ENG-36)
+;       by Los Alamos National Laboratory, which is operated by the
+;       University of California for the U.S. Department of Energy.
+;       The U.S. Government is licensed to use, reproduce, and distribute
+;       this software. Neither the Government nor the University makes
+;       any warranty, express or implied, or assumes any liability or
+;       responsibility for the use of this software.
+;
+; AUTHOR AND MODIFICATIONS:
+;
+;       J. Bloch        1.4     10/28/92
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Create default plotting coords, if needed   W. Landsman  August 2000
+;       Added _EXTRA, CHARTHICK, CHARSIZE keywords  W. Landsman  March 2001
+;-
+PRO EQPOLE_GRID,DLONG,DLAT,_EXTRA=E,LABELS=LABEL,SOUTHPOLE=SOUTHPOLE,NEW=NEW, $
+               CHARSIZE = charsize, CHARTHICK =charthick
+
+        if n_params() lt 2 then dlong = 30.0
+        if n_params() lt 1 then dlat = 30.0
+
+
+; If no plotting axis has been defined, then create a default one
+
+        new = keyword_set(new)
+        if not new then new =  (!X.crange[0] EQ 0) and (!X.crange[1] EQ 0)
+        if new then plot,[-130,130],[-130,130],/nodata,xsty=5,ysty=5
+
+;
+;       Do lines of constant longitude
+;
+        lat=90.0-findgen(180)
+        if keyword_set(southpole) then lat = -lat
+        lng=fltarr(180)
+        lngtot = long(360.0/dlong)
+        for i=0,lngtot do begin
+                lng[*]=-180.0+(i*dlong)
+                eqpole,lng,lat,x,y,southpole=southpole
+                oplot,x,y,_EXTRA=e
+        endfor
+;
+;       Do lines of constant latitude
+;
+        lng=findgen(360)
+        lat=fltarr(360)
+        lattot=long(180.0/dlat)
+        for i=1,lattot do begin
+                if not keyword_set(southpole) then lat[*]=90.0-(i*dlat) $
+                        else lat[*]=-90.0+(i*dlat)
+                eqpole,lng,lat,x,y,southpole=southpole
+                oplot,x,y,_EXTRA=e
+        endfor
+;
+;       Do labeling if requested
+;
+        if keyword_set(label) then begin
+;
+;       Label equator
+;
+            for i=0,lngtot-1 do begin
+                lng = (i*dlong)
+                eqpole,lng,0.0,x,y,southpole=southpole
+                if label eq 1 then xyouts,x[0],y[0],noclip=0,$
+                        charsize = charsize, charthick = charthick, $
+                        strcompress(string(lng,format="(I4)"),/remove_all) $
+                else begin
+                        tmp=sixty(lng*24.0/360.0)
+                        xyouts,x[0],y[0],noclip=0,$
+                           charsize = charsize, charthick = charthick, $
+                             strcompress(string(tmp[0],tmp[1],$
+                            format='(I2,"h",I2,"m")'),/remove_all),alignment=0.5
+                endelse
+            endfor
+;
+;       Label prime meridian
+;
+            for i=1,lattot-1 do begin
+                if not keyword_set(southpole) then $
+                        lat=90-(i*dlat) else lat=-90+(i*dlat)
+                eqpole,0.0,lat,x,y,southpole=southpole
+                xyouts,x[0],y[0],noclip=0,$
+                        charsize = charsize, charthick = charthick, $
+                        strcompress(string(lat,format="(I4)"),/remove_all)
+            endfor
+        endif
+        return
+end
+
diff --git a/Code/script_idl_mv/astrolib/euler.pro b/Code/script_idl_mv/astrolib/euler.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9ab363d05f68a10db833ab1bf7f0a4db46340a1c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/euler.pro
@@ -0,0 +1,169 @@
+PRO EULER,AI,BI,AO,BO,SELECT, FK4 = FK4, SELECT = select1, RADIAN=radian
+;+
+; NAME:
+;     EULER
+; PURPOSE:
+;     Transform between Galactic, celestial, and ecliptic coordinates.
+; EXPLANATION:
+;     Use the procedure ASTRO to use this routine interactively
+;
+; CALLING SEQUENCE:
+;      EULER, AI, BI, AO, BO, [ SELECT, /FK4, /RADIAN, SELECT = ] 
+;
+; INPUTS:
+;       AI - Input Longitude, scalar or vector.  In DEGREES unless /RADIAN
+;            is set.  If only two parameters are supplied, then  AI and BI 
+;             will be modified to contain the output longitude and latitude.
+;       BI - Input Latitude in DEGREES
+;
+; OPTIONAL INPUT:
+;       SELECT - Integer (1-6) specifying type of coordinate transformation.  
+;
+;      SELECT   From          To        |   SELECT      From            To
+;       1     RA-Dec (2000)  Galactic   |     4       Ecliptic      RA-Dec    
+;       2     Galactic       RA-DEC     |     5       Ecliptic      Galactic  
+;       3     RA-Dec         Ecliptic   |     6       Galactic      Ecliptic  
+;
+;      If not supplied as a parameter or keyword, then EULER will prompt for 
+;      the value of SELECT
+;      Celestial coordinates (RA, Dec) should be given in equinox J2000 
+;      unless the /FK4 keyword is set.
+; OUTPUTS:
+;       AO - Output Longitude in DEGREES, always double precision
+;       BO - Output Latitude in DEGREES, always double precision
+;
+; OPTIONAL INPUT KEYWORD:
+;       /FK4 - If this keyword is set and non-zero, then input and output 
+;             celestial and ecliptic coordinates should be given in equinox 
+;             B1950.
+;       /RADIAN - if set, then all input and output angles are in radians rather
+;             than degrees.
+;       SELECT  - The coordinate conversion integer (1-6) may alternatively be 
+;              specified as a keyword
+; EXAMPLE:
+;       Find the Galactic coordinates of Cyg X-1 (ra=299.590315, dec=35.201604)
+;       IDL> ra = 299.590315d
+;       IDL> dec = 35.201604d
+;       IDL> euler,ra,dec,glong,glat,1 & print,glong,glat 
+;            71.334990, 3.0668335
+; REVISION HISTORY:
+;       Written W. Landsman,  February 1987
+;       Adapted from Fortran by Daryl Yentis NRL
+;       Made J2000 the default, added /FK4 keyword  W. Landsman December 1998
+;       Add option to specify SELECT as a keyword W. Landsman March 2003
+;       Use less virtual memory for large input arrays W. Landsman June 2008
+;       Added /RADIAN input keyword  W. Landsman   Sep 2008
+;-
+ On_error,2
+ compile_opt idl2
+
+ npar = N_params()
+ if npar LT 2 then begin
+    print,'Syntax - EULER, AI, BI, A0, B0, [ SELECT, /FK4, /RADIAN, SELECT= ]'
+    print,'    AI,BI - Input longitude,latitude in degrees'
+    print,'    AO,BO - Output longitude, latitude in degrees'
+    print,'    SELECT - Scalar (1-6) specifying transformation type'
+    return
+ endif
+
+  twopi   =   2.0d*!DPI
+  fourpi  =   4.0d*!DPI
+  rad_to_deg = 180.0d/!DPI
+
+;   J2000 coordinate conversions are based on the following constants
+;   (see the Hipparcos explanatory supplement).
+;  eps = 23.4392911111d              Obliquity of the ecliptic
+;  alphaG = 192.85948d               Right Ascension of Galactic North Pole
+;  deltaG = 27.12825d                Declination of Galactic North Pole
+;  lomega = 32.93192d                Galactic longitude of celestial equator  
+;  alphaE = 180.02322d              Ecliptic longitude of Galactic North Pole
+;  deltaE = 29.811438523d            Ecliptic latitude of Galactic North Pole
+;  Eomega  = 6.3839743d              Galactic longitude of ecliptic equator              
+
+  if keyword_set(FK4) then begin 
+
+  equinox = '(B1950)' 
+  psi   = [ 0.57595865315D, 4.9261918136D,  $
+            0.00000000000D, 0.0000000000D,  $  
+            0.11129056012D, 4.7005372834D]     
+  stheta =[ 0.88781538514D,-0.88781538514D, $
+            0.39788119938D,-0.39788119938D, $
+            0.86766174755D,-0.86766174755D]    
+  ctheta =[ 0.46019978478D, 0.46019978478D, $
+            0.91743694670D, 0.91743694670D, $
+            0.49715499774D, 0.49715499774D]    
+   phi  = [ 4.9261918136D,  0.57595865315D, $
+            0.0000000000D, 0.00000000000D, $
+	    4.7005372834d, 0.11129056012d]
+
+
+ endif else begin 
+
+  equinox = '(J2000)'
+  psi   = [ 0.57477043300D, 4.9368292465D,  $
+            0.00000000000D, 0.0000000000D,  $  
+            0.11142137093D, 4.71279419371D]     
+  stheta =[ 0.88998808748D,-0.88998808748D, $
+            0.39777715593D,-0.39777715593D, $
+            0.86766622025D,-0.86766622025D]    
+  ctheta =[ 0.45598377618D, 0.45598377618D, $
+            0.91748206207D, 0.91748206207D, $
+            0.49714719172D, 0.49714719172D]    
+   phi  = [ 4.9368292465D,  0.57477043300D, $
+            0.0000000000D, 0.00000000000D, $
+            4.71279419371d, 0.11142137093d]
+
+ endelse
+;
+ if N_elements(select) EQ 0 then $
+          if N_elements(select1) EQ 1 then select=select1
+ if N_elements(select) EQ 0 then begin
+        print,' '
+        print,' 1 RA-DEC ' + equinox + ' to Galactic'
+        print,' 2 Galactic       to RA-DEC' + equinox
+        print,' 3 RA-DEC ' + equinox + ' to Ecliptic'
+        print,' 4 Ecliptic       to RA-DEC' + equinox
+        print,' 5 Ecliptic       to Galactic'
+        print,' 6 Galactic       to Ecliptic'
+;
+        select = 0
+        read,'Enter selection: ',select
+ endif
+
+ I  = select - 1                         ; IDL offset
+ if npar EQ 2 then begin
+
+      if keyword_set(radian) then begin 
+         ao = temporary(ai) - phi[i]
+         bo = temporary(bi)
+      endif else begin  
+         ao = temporary(ai)/rad_to_deg - phi[i]
+         bo = temporary(bi)/rad_to_deg
+      endelse 
+      
+      endif else begin 
+      if keyword_set(radian) then begin 
+           ao = ai - phi[i]
+	   bo = bi
+      endif else begin      
+          ao  = ai/rad_to_deg - phi[i]
+          bo = bi/rad_to_deg
+       endelse	  
+ endelse
+ sb = sin(bo) &	cb = cos(bo)
+ cbsa = cb * sin(ao)
+ bo  = -stheta[i] * cbsa + ctheta[i] * sb
+ bo    = asin(bo<1.0d)
+ if ~keyword_set(radian) then bo = bo*rad_to_deg
+;
+ ao =  atan( ctheta[i] * cbsa + stheta[i] * sb, cb * cos(ao) )
+ ao = ( (ao+psi[i]+fourpi) mod twopi) 
+ if ~keyword_set(radian) then ao = ao*rad_to_deg
+
+
+ if ( npar EQ 2 ) then begin
+	ai = temporary(ao) & bi=temporary(bo)
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/expand_tilde.pro b/Code/script_idl_mv/astrolib/expand_tilde.pro
new file mode 100644
index 0000000000000000000000000000000000000000..728bee66f14969344ec25ddfb93e0e5a8bf1eed7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/expand_tilde.pro
@@ -0,0 +1,67 @@
+;+
+; NAME:
+;      EXPAND_TILDE()
+;               
+; PURPOSE: 
+;       Expand tilde in UNIX directory names
+;               
+; CALLING SEQUENCE: 
+;       IDL> output=expand_tilde(input)
+;    
+; INPUTS: 
+;       INPUT = input file or directory name, scalar string
+;
+; OUTPUT:
+;       Returns expanded filename, scalar string
+;               
+; EXAMPLES: 
+;       output=expand_tilde('~zarro/test.doc')
+;               ---> output='/usr/users/zarro'
+;
+; NOTES:
+;       This version of EXPAND_TILDE differs from the version in the Solar
+;       Library in that it does not call the functions EXIST and IDL_RELEASE.
+;       However, it should work identically.
+; PROCEDURE CALLS:
+;       None.
+; REVISION HISTORY: 
+;       Version 1,  17-Feb-1997,  D M Zarro.  Written
+;       Transfered from Solar Library   W. Landsman   Sep. 1997
+;       Made more robust  D. Zarro/W. Landsman  Sep. 2000
+;       Made even more robust (since things like ~zarro weren't being expanded)
+;       Zarro (EITI/GSFC, Mar 2001)
+;-            
+
+ function expand_tilde,name
+ if N_elements(name) EQ 0 then return,''
+ if size(name,/TNAME) ne 'STRING' then return,name
+ tpos=strpos(name,'~')
+ if tpos eq -1 then return,name
+ apos = strpos(name,'~/')
+ bpos = strpos(name,'/~')
+
+ tilde=name
+ if apos GT -1 then begin
+    tilde = strmid(name,0,apos+1)
+    post = strmid(name,apos+1,strlen(name))
+ endif else begin
+   if bpos gt -1 then begin
+            pre = strmid(name,0,bpos+1)
+            tilde = strmid(name,bpos+1,strlen(name))
+   endif
+ endelse
+ 
+  error=0
+  catch,error
+  if error ne 0 then begin
+     catch,/cancel
+     return,name
+  endif
+ 
+ cd,tilde,curr=curr
+ cd,curr,curr=dcurr
+ tname = dcurr
+ if N_elements(pre) GT 0 then tname = pre+tname else $
+    if N_elements(post) GT 0 then tname = tname + post
+
+ return,tname & end
diff --git a/Code/script_idl_mv/astrolib/extast.pro b/Code/script_idl_mv/astrolib/extast.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b6ba118f9c58fc27087d6195dd7513fa6f0faf44
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/extast.pro
@@ -0,0 +1,714 @@
+pro extast,hdr,astr,noparams, alt=alt
+;+
+; NAME:
+;     EXTAST
+; PURPOSE:
+;     Extract ASTrometry parameters from a FITS image header.
+; EXPLANATION:
+;     Extract World Coordinate System information 
+;     ( http://fits.gsfc.nasa.gov/fits_wcs.html ) from a FITS header and 
+;     place it into an IDL structure.
+;
+; CALLING SEQUENCE:
+;     EXTAST, hdr,  astr, [ noparams, ALT= ]   
+;
+; INPUT:
+;     HDR - variable containing the FITS header (string array)
+;
+; OUTPUTS:
+;     In the following, index 1 & 2 refer to the first and second astrometry
+;     axes respectively. The actual axis numbers are stored in .AXIS
+;
+;     ASTR - Anonymous structure containing astrometry info from the FITS 
+;             header ASTR always contains the following tags (even though 
+;             some projections do not require all the parameters)
+;      .NAXIS - 2 element array giving image size
+;      .CD   -  2 x 2 array containing the astrometry parameters CD1_1 CD1_2
+;               in DEGREES/PIXEL                                 CD2_1 CD2_2
+;      .CDELT - 2 element double vector giving physical increment at the 
+;                 reference pixel
+;      .CRPIX - 2 element double vector giving X and Y coordinates of reference 
+;               pixel (def = NAXIS/2) in FITS convention (first pixel is 1,1)
+;      .CRVAL - 2 element double precision vector giving R.A. and DEC of 
+;               reference pixel in DEGREES
+;      .CTYPE - 2 element string vector giving projection types, default
+;               ['RA---TAN','DEC--TAN']
+;      .LONGPOLE - scalar giving native longitude of the celestial pole 
+;             (default = 180 for zenithal projections) 
+;      .LATPOLE - scalar giving native latitude of the celestial pole default=0)
+;      .PV2 - Vector of projection parameters associated with latitude axis
+;             PV2 will have up to 21 elements for the ZPN projection, up to 3 
+;             for the SIN projection and no more than 2 for any other 
+;             projection  
+;
+;     Fields added for version 2:
+;      .PV1 - Vector of projection parameters associated with longitude axis
+;      .AXES  - 2 element integer vector giving the FITS-convention axis 
+;               numbers associated with astrometry, in ascending order. 
+;               Default [1,2].
+;      .REVERSE - byte, true if first astrometry axis is Dec/latitude
+;      .COORD_SYS - 1 or 2 character code giving coordinate system, including
+;                 'C' = RA/Dec, 'G' = Galactic, 'E' = Ecliptic, 'X' = unknown.
+;      .PROJECTION  - 3-letter WCS projection code
+;      .KNOWN    - true if IDL WCS routines recognise this projection
+;      .RADECSYS - String giving RA/Dec system e.g. 'FK4', 'ICRS' etc.
+;      .EQUINOX  - Double giving the epoch of the mean equator and equinox
+;      .DATEOBS  - Text string giving (start) date/time of observations
+;      .MJDOBS   - Modified julian date of start of observations.
+;      .X0Y0     - Implied offset in intermediate world coordinates (x,y)
+;                  if a non-standard fiducial point is set via PV1 and also
+;                  PV1_0a =/ 0, indicating that an offset should be
+;                  applied to place CRVAL at the (x,y) origin.
+;                  Should be *added* to the IWC derived from application of
+;                  CRPIX, CDELT, CD to the pixel coordinates.
+;
+;      .DISTORT - optional substructure specifying any distortion parameters
+;                 currently implement only for "SIP" (Spitzer Imaging 
+;                 Polynomial), "TPV" (tangent PV* polynomial) distortion 
+;                 parameters, and "TNX" (tangent plus iraf distortion)
+;
+;       NOPARAMS -  Scalar indicating the results of EXTAST
+;            -1 = Failure - Header missing astrometry parameters
+;             1 = Success - Header contains CROTA + CDELT (AIPS-type) astrometry
+;             2 = Success - Header contains CDn_m astrometry, rec.    
+;             3 = Success - Header contains PCn_m + CDELT astrometry. 
+;             4 = Success - Header contains ST  Guide Star Survey astrometry
+;                           (see gsssextast.pro )
+; OPTIONAL INPUT/OUTPUT KEYWORDS:
+;       ALT -  single character 'A' through 'Z' or ' ' specifying an alternate 
+;              astrometry system present in the FITS header.    The default is
+;              to use the primary astrometry or ALT = ' '.   If /ALT is set, 
+;              then this is equivalent to ALT = 'A'.   See Section 3.3 of 
+;              Greisen & Calabretta (2002, A&A, 395, 1061) for information about
+;              alternate astrometry keywords.    If not set on input, then
+;              ALT is set to ' ' on output.
+; PROCEDURE:
+;       EXTAST checks for astrometry parameters in the following order:
+;
+;       (1) the CD matrix PC1_1,PC1_2...plus CDELT*, CRPIX and CRVAL
+;       (2) the CD matrix CD1_1,CD1_2... plus CRPIX and CRVAL.   
+;       (3) CROTA2 (or CROTA1) and CDELT plus CRPIX and CRVAL.
+;
+;       All three forms are valid FITS according to the paper "Representations 
+;       of World Coordinates in FITS by Greisen and Calabretta (2002, A&A, 395,
+;       1061 http://fits.gsfc.nasa.gov/fits_wcs.html ) although form (1) is 
+;       preferred.
+;
+; NOTES:
+;       1.  An anonymous structure is created to avoid structure definition
+;       conflicts.    This is needed because some projection systems
+;       require additional dimensions (i.e. spherical cube
+;       projections require a specification of the cube face).
+;
+;       2,  FITS headers created by SCAMP 
+;      (http://www.astromatic.net/software/scamp) contain a tangent 
+;       projection with distortion polynomial coefficients in the PV[1|2]_?
+;       keywords.    These will be flagged as being a TPV projection 
+;       (http://fits.gsfc.nasa.gov/registry/tpvwcs.html) in the 
+;       astr.projection keyword.    
+;           
+; PROCEDURES CALLED:
+;      GSSSEXTAST, ZPARCHECK
+; REVISION HISTORY
+;      Written by B. Boothman 4/15/86
+;      Accept CD001001 keywords               1-3-88
+;      Accept CD1_1, CD2_1... keywords    W. Landsman    Nov. 92
+;      Recognize GSSS FITS header         W. Landsman    June 94
+;      Get correct sign, when converting CDELT* to CD matrix for right-handed
+;      coordinate system                  W. Landsman   November 1998
+;      Consistent conversion between CROTA and CD matrix  October 2000
+;      CTYPE = 'PIXEL' means no astrometry params  W. Landsman January 2001
+;      Don't choke if only 1 CTYPE value given W. Landsman  August 2001
+;      Recognize PC00n00m keywords again (sigh...)  W. Landsman December 2001
+;      Recognize GSSS in ctype also       D. Finkbeiner Jan 2002
+;      Introduce ALT keyword              W. Landsman June 2003
+;      Fix error introduced June 2003 where free-format values would be
+;      truncated if more than 20 characters.  W. Landsman Aug 2003
+;      Further fix to free-format values -- slash need not be present Sep 2003
+;      Default value of LATPOLE is 90.0  W. Landsman February 2004
+;      Allow for distortion substructure, currently implemented only for
+;          SIP (Spitzer Imaging Polynomial)   W. Landsman February 2004 
+;      Correct LONGPOLE computation if CTYPE = ['*DEC','*RA'] W. L. Feb. 2004
+;      Assume since V5.3 (vector STRMID)  W. Landsman Feb 2004
+;      Yet another fix to free-format values   W. Landsman April 2004
+;      Introduce PV2 tag to replace PROJP1, PROJP2.. etc.  W. Landsman May 2004
+;      Convert NCP projection to generalized SIN   W. Landsman Aug 2004
+;      Add NAXIS tag to output structure  W. Landsman Jan 2007
+;      .CRPIX tag now Double instead of Float   W. Landsman  Apr 2007
+;      If duplicate keywords use the *last* value W. Landsman Aug 2008
+;      Fix typo for AZP projection, nonzero longpole N. Cunningham Feb 2009
+;      Give warning if reverse SIP coefficient not present  W. Landsman Nov 2011
+;      Allow obsolete CD matrix representations W. Landsman May 2012
+;      Work for Paritel headers with extra quotes R. Gutermuth/WL  April 2013
+;
+;      Version 2:  J. P. Leahy, July 2013
+;        - Support long & lat axes not being the first 2.
+;        - Implemented PV1 and hence non-default phi0 and theta0
+;        - Added AXES, REVERSE, COORD_SYS, PROJECTION, RADECSYS, EQUINOX,
+;          DATEOBS, MJDOBS, PV1, and X0Y0 tags to the structure.
+;        - More checks for inconsistencies in the keywords.
+;      v2.1 21/7/13 Missing mjdobs & equinox changed to NaN (was -1 & 0);
+;          Converts GLS to SFL if possible; added KNOWN tag.
+;      v2.2 21/9/13 GLS conversion fixed.
+;      v2.3 1 Dec 13 Add warning if distortions from SCAMP astrometry present
+;      v2.4.  Extract SCAMP or TPV distortion astrometry, if present Jan 2014
+;      v2.5  Fix bug when SIP parameters not recognized when NAXIS=0 May 2014
+;      v2.5.1 Make sure CROTA defined for GLS projection WL Sep 2015
+;-
+ On_error, 0
+ compile_opt idl2
+ ;
+ ; List of known map types copied from wcsxy2sph. Needs to be kept up
+ ; to date!
+ ;
+ map_types=['DEF','AZP','TAN','SIN','STG','ARC','ZPN','ZEA','AIR','CYP',$
+            'CAR','MER','CEA','COP','COD','COE','COO','BON','PCO','SFL',$
+            'PAR','AIT','MOL','CSC','QSC','TSC','SZP','HPX','HCT','XPH']
+
+ if ( N_params() LT 2 ) then begin
+     print,'Syntax - EXTAST, hdr, astr, [ noparams, ALT = ]'
+     return
+ endif
+
+ proj0 = ['CYP','CEA','CAR','MER','SFL','PAR','MOL','AIT','BON','PCO', $
+          'TSC','CSC','QSC']
+ radeg = 180.0D0/!DPI
+ keyword = STRUPCASE(strtrim(strmid( hdr, 0, 8), 2))
+
+; Extract values from the FITS header.   This is either up to the first slash
+; (free format) or first space
+
+ space = strpos( hdr, ' ', 10) + 1
+ slash = strpos( hdr, '/', 10)  > space
+ 
+ N = N_elements(hdr)
+ len = (slash -10) > 20
+ len = reform(len,1,N)
+ lvalue = strtrim(strmid(hdr, 10, len),2)
+ remchar,lvalue,"'"
+ zparcheck,'EXTAST',hdr,1,7,1,'FITS image header'   ;Make sure valid header
+ noparams = -1                                    ;Assume no astrometry to start
+
+ if N_elements(alt) EQ 0 then begin
+     alt = '' & altstr = ''
+ endif else BEGIN
+     if (alt EQ '1') then alt = 'A' else alt = strupcase(alt)
+     altstr = ' for alternate system '+alt
+ ENDELSE
+ 
+ ; Search for astrometric axes:
+ test = STREGEX(keyword,'^CTYPE[1-9][0-9]{0,2}'+alt+'$', LENGTH = ctlen)
+ typ = WHERE(test GE 0, ntyp)
+ lon = -1  & lat = -1
+ lon_form = -1 & lat_form = -1
+ 
+ IF ntyp GT 0 THEN BEGIN
+     ctlen = ctlen[typ] - STRLEN('CTYPE'+alt) ; gives # digits in axis number
+     
+     lon0 = WHERE(STRMID(lvalue[typ],0,5) EQ 'RA---')
+     lon1 = WHERE(STRMID(lvalue[typ],1,4) EQ  'LON-')
+     lon2 = WHERE(STRMID(lvalue[typ],2,4) EQ   'LN-')
+     lon = [lon0, lon1, lon2]
+     form = [REPLICATE(0,N_ELEMENTS(lon0)),REPLICATE(1,N_ELEMENTS(lon1)), $
+             REPLICATE(2,N_ELEMENTS(lon2))]
+     good = WHERE(lon GT 0, ngood)
+     IF ngood GT 1 THEN MESSAGE, /INFORMATIONAL, $
+                  'Multiple longitude axes'+altstr+': Using last.'
+     lon = MAX(lon, subs)
+     lon_form = lon GE 0 ? form[subs] : -1
+
+     lat0 = WHERE(STRMID(lvalue[typ],0,5) EQ 'DEC--')
+     lat1 = WHERE(STRMID(lvalue[typ],1,4) EQ  'LAT-')
+     lat2 = WHERE(STRMID(lvalue[typ],2,4) EQ   'LT-')
+     lat = [lat0, lat1, lat2]
+     form = [REPLICATE(0,N_ELEMENTS(lat0)),REPLICATE(1,N_ELEMENTS(lat1)), $
+             REPLICATE(2,N_ELEMENTS(lat2))]
+     good = WHERE(lat GT 0, ngood)
+     IF ngood GT 1 THEN MESSAGE, /INFORMATIONAL, $
+                  'Multiple latitude axes'+altstr+': Using last.'
+     lat = MAX(lat,subs)
+     lat_form = lat GE 0 ? form[subs] : -1
+ ENDIF
+ 
+;
+; Longitude axis data is initially stored in element 0 and latitude
+; axis data in element 1 of the various arrays. For backwards compatibility,
+; if latitude has a lower axis number in the FITS header, they will be swapped 
+; into the (latitude, longitude) order in the final structure, and the .REVERSE 
+; field will be set to true (ie. 1B).
+; 
+ lonc = lon GE 0 ? STRMID(keyword[typ[lon]],5,ctlen[lon]) : '1'
+ latc = lat GE 0 ? STRMID(keyword[typ[lat]],5,ctlen[lat]) : '2'
+
+ ctype = ['','']
+ l = where(keyword EQ 'CTYPE'+lonc+alt,  N_ctype1)
+ if N_ctype1 GT 0 then ctype[0] = lvalue[l[N_ctype1-1]]
+ l = where(keyword EQ 'CTYPE'+latc+alt,  N_ctype2)
+ if N_ctype2 GT 0 then ctype[1] = lvalue[l[N_ctype2-1]]
+ ctype = strtrim(ctype,2)
+
+ badco = lon_form NE lat_form 
+ CASE lon_form OF
+     -1: coord = 'X'  ; unknown type of coordinate
+      0: coord = 'C'  ; celestial coords, i.e. RA/Dec
+      1: BEGIN  ; longitude format is xLON where x = G, E, etc.
+          coord = STRMID(ctype[0],0,1)
+          badco = badco || coord NE STRMID(ctype[1],0,1)
+      END
+      2: BEGIN  ; longitude format is yzLN 
+          coord = STRMID(ctype[0],0,2)
+          badco = badco || coord NE STRMID(ctype[2],0,2)
+      END
+      ELSE: MESSAGE, 'Internal error: unexpected lon_form' 
+ ENDCASE
+
+ naxis = lonarr(2)
+ l = where(keyword EQ 'NAXIS'+lonc,  N_axis1)
+ if N_axis1 GT 0 then naxis[0] = lvalue[l[N_axis1-1]]
+ l = where(keyword EQ 'NAXIS'+latc,  N_axis2)
+ if N_axis2 GT 0 then naxis[1] = lvalue[l[N_axis2-1]]
+
+ tpv = strmid(ctype[0],2,3,/reverse) EQ 'TPV'
+ tnx = strmid(ctype[0],2,3,/reverse) EQ 'TNX'
+ 
+ IF (TPV || tnx) THEN BEGIN 
+    proj = 'TAN'
+ ENDIF ELSE BEGIN
+ proj = STRMID(ctype[0], 5, 3)
+ 
+ badco = badco || proj NE STRMID(ctype[1], 5, 3)
+ IF badco THEN BEGIN
+     MESSAGE, 'ERROR' + altstr + $
+      ': longitude and latitude coordinate types must match:', /CONTINUE
+     MESSAGE, 'Coords were CTYPE'+lonc+alt+': ' + ctype[0] + $
+                        '; CTYPE'+latc+alt+': ' + ctype[1]
+ ENDIF 
+  
+; If the standard CTYPE* astrometry keywords not found, then check if the
+; ST guidestar astrometry is present
+
+ check_gsss = (N_ctype1 EQ 0)
+ if N_ctype1 GE 1  then check_gsss = (strmid(ctype[0], 5, 3) EQ 'GSS')
+
+ if check_gsss then begin
+
+        l = where(keyword EQ 'PPO1'+alt,  N_ppo1)
+        if N_ppo1 EQ 1 then begin 
+                gsssextast, hdr, astr, gsssparams
+                if gsssparams EQ 0 then noparams = 4
+                return
+        endif
+        ctype = ['RA---TAN','DEC--TAN']
+ endif
+
+ if (ctype[0] EQ 'PIXEL') then return
+ if N_ctype2 EQ 1 then if (ctype[1] EQ 'PIXEL') then return
+ ENDELSE
+
+ crval = dblarr(2)
+
+ l = where(keyword EQ 'CRVAL'+lonc+alt,  N_crval1)
+ if N_crval1 GT 0 then crval[0] = lvalue[l[N_crval1-1]]
+ l = where(keyword EQ 'CRVAL'+latc+alt,  N_crval2)
+ if N_crval2 GT 0 then crval[1] = lvalue[l[N_crval2-1]]
+ if (N_crval1 EQ 0) || (N_crval2 EQ 0) then return  
+
+ crpix = dblarr(2)
+ l = where(keyword EQ 'CRPIX'+lonc+alt,  N_crpix1)
+ if N_crpix1 GT 0 then crpix[0] = lvalue[l[N_crpix1-1]]
+ l = where(keyword EQ 'CRPIX'+latc+alt,  N_crpix2)
+ if N_crpix2 GT 0 then crpix[1] = lvalue[l[N_crpix2-1]]
+ if (N_crpix1 EQ 0) || (N_crpix2 EQ 0) then return  
+
+
+ cd = dblarr(2,2)
+ cdelt = [1.0d,1.0d]
+
+GET_CD_MATRIX:
+
+ l = where(keyword EQ 'PC'+lonc+'_'+lonc + alt,  N_pc11) 
+ if N_PC11 GT 0 then begin 
+        cd[0,0]  = lvalue[l]
+        l = where(keyword EQ 'PC'+lonc+'_'+latc + alt,  N_pc12) 
+        if N_pc12 GT 0 then cd[0,1]  = lvalue[l[N_pc12-1]]
+        l = where(keyword EQ 'PC'+latc+'_'+lonc + alt,  N_pc21) 
+        if N_pc21 GT 0 then cd[1,0]  = lvalue[l[N_pc21-1]]
+        l = where(keyword EQ 'PC'+latc+'_'+latc + alt,  N_pc22) 
+        if N_pc22 GT 0 then cd[1,1]  = lvalue[l[N_pc22-1]]
+         l = where(keyword EQ 'CDELT'+lonc+ alt,  N_cdelt1) 
+        if N_cdelt1 GT 0 then cdelt[0]  = lvalue[l[N_cdelt1-1]]
+         l = where(keyword EQ 'CDELT'+latc+ alt,  N_cdelt2) 
+        if N_cdelt2 GT 0 then cdelt[1]  = lvalue[l[N_cdelt2-1]]
+        det = cd[0,0]*cd[1,1] - cd[0,1]*cd[1,0]
+        if det LT 0 then sgn = -1 else sgn = 1
+        crota  = atan(  sgn*cd[0,1],  sgn*cd[0,0] ) 
+        noparams = 3
+ endif else begin 
+
+    l = where(keyword EQ 'CD'+lonc+'_'+lonc + alt,  N_cd11) 
+    if N_CD11 GT 0 then begin        ;If CD parameters don't exist, try CROTA
+        cd[0,0]  = strtrim(lvalue[l[N_cd11-1]],2)
+        l = where(keyword EQ 'CD'+lonc+'_'+latc + alt,  N_cd12) 
+        if N_cd12 GT 0 then cd[0,1]  = lvalue[l[N_cd12-1]]
+        l = where(keyword EQ 'CD'+latc+'_'+lonc + alt,  N_cd21) 
+        if N_cd21 GT 0 then cd[1,0]  = lvalue[l[N_cd21-1]]
+        l = where(keyword EQ 'CD'+latc+'_'+latc + alt,  N_cd22) 
+        if N_cd22 GT 0 then cd[1,1]  = lvalue[l[N_cd22-1]]
+        noparams = 2
+    endif else begin
+
+; Now get rotation, first try CROTA2, if not found try CROTA1, if that
+; not found assume North-up.   Then convert to CD matrix - see Section 5 in
+; Greisen and Calabretta
+
+        l = where(keyword EQ 'CDELT'+lonc + alt,  N_cdelt1) 
+        if N_cdelt1 GT 0 then cdelt[0]  = lvalue[l[N_cdelt1-1]]
+        l = where(keyword EQ 'CDELT'+latc + alt,  N_cdelt2) 
+        if N_cdelt2 GT 0 then cdelt[1]  = lvalue[l[N_cdelt2-1]]
+        if (N_cdelt1 EQ 0) || (N_Cdelt2 EQ 0) then return   
+                                           ;Must have CDELT1 and CDELT2
+
+        l = where(keyword EQ 'CROTA'+latc + alt,  N_crota) 
+        if N_Crota EQ 0 then $
+            l = where(keyword EQ 'CROTA'+lonc + alt,  N_crota) 
+        if N_crota EQ 0 then begin
+	          l = where(keyword EQ 'PC001001', N_PC00)
+	          l = where(keyword EQ 'CD001001', N_CD00)
+	          if (N_PC00 GT 0) || (N_CD00 GT 0) then begin
+	              message,'Updating obsolete CD matrix representation',/INF
+		            FITS_CD_FIX, hdr
+		            keyword = strtrim(strmid(hdr,0,8),2)
+		            goto, GET_CD_MATRIX
+	          endif else crota = 0.0d 
+	      endif else crota = double(lvalue[l[N_crota-1]])/RADEG
+        cd = [ [cos(crota), -sin(crota)],[sin(crota), cos(crota)] ] 
+ 
+        noparams = 1           ;Signal AIPS-type astrometry found
+     
+    endelse
+ endelse
+
+; Kluge to test for non-standard PVi_j distortion terms used by SCAMP
+ scamp_distort = 0b
+ if ~tpv && (proj EQ 'TAN')  then $
+     tpv = ~array_equal(strmatch(keyword,'PV1_[5-9]'),0) &&  $  ;Updated 1-8-14
+           ~array_equal(strmatch(keyword,'PV2_[3-9]'),0)   
+	  
+;Extract PV_* keywords.   Special case for TPV distortion
+ if tpv then begin
+   g= where(strmatch(keyword,'PV1_*'), Ng)
+   key_pv1 = keyword[g]
+   temp =  gettok(key_pv1,'_')
+   key_pv1 = fix(key_pv1)      
+   pv1 = dblarr(max(key_pv1)+1)
+   pv1[key_pv1] = lvalue[g]
+
+   g= where(strmatch(keyword,'PV2_*'), Ng)
+   key_pv2 = keyword[g]
+   temp =  gettok(key_pv2,'_')
+   key_pv2 = fix(key_pv2)      
+   pv2 = dblarr(max(key_pv2)+1)       ;Corrected 13-Jan-2014
+   pv2[key_pv2] = lvalue[g]
+   
+   latpole = 90.0D
+   longpole = 180.0D
+   known = 1.0
+   x0y0 = [0d0, 0d0]
+   distort_flag = 'TPV' 
+ENDIF ELSE BEGIN
+   ;; extract the tnx coefficients from the WAT keywords
+  
+   IF(tnx)THEN BEGIN
+      g=where(strmatch(keyword,'WAT1_*'),Ng)
+      key_wat1=keyword[g]
+      val_wat1=STRTRIM(strmid(hdr[g], 10),2)
+      remchar,val_wat1,"'"
+      remchar,val_wat1,'"'
+      remchar,val_wat1,'/'
+      temp=STRMID(key_wat1,0,3,/REVERSE)
+      s=SORT(temp)
+      val_wat1=val_wat1[s]
+      val_wat1=STRJOIN(val_wat1)
+      val_wat1=STRSPLIT(val_wat1,/EXTRACT)
+      
+      g=where(strmatch(keyword,'WAT2_*'),Ng)
+      key_wat2=keyword[g]
+      val_wat2=STRTRIM(strmid(hdr[g], 10),2)
+      remchar,val_wat2,"'"
+      remchar,val_wat2,'"'
+      remchar,val_wat2,'/'
+      temp=STRMID(key_wat2,0,3,/REVERSE)
+      s=SORT(temp)
+      val_wat2=val_wat2[s]
+      val_wat2=STRJOIN(val_wat2)
+      val_wat2=STRSPLIT(val_wat2,/EXTRACT)
+      IF(val_wat1[2] NE 'lngcor' || val_wat2[2] NE 'latcor')THEN BEGIN
+         MESSAGE,'WARNING: TNX projection parameters not parsed correctly',/CON
+         ctype = ['RA---TAN','DEC--TAN']
+         tnx=0
+      ENDIF
+      IF(val_wat1[4] NE 3 || val_wat2[4] NE 3)THEN BEGIN
+         MESSAGE,'WARNING - only polynomials supported for TNX projection.',/CON
+         ctype = ['RA---TAN','DEC--TAN']
+         tnx=0
+      ENDIF
+    
+      IF(tnx)THEN BEGIN
+         ;; tnx coefficients get stored in two structures
+         ncoeff=N_ELEMENTS(val_wat1)-12
+         lngcor={functype:0,xiorder:0,etaorder:0,xterms:0,ximin:0d0,ximax:0d0,etamin:0d0,etamax:0d0,coeff:DBLARR(ncoeff)}
+         lngcor.functype=FIX(val_wat1[4])
+         lngcor.xiorder=FIX(val_wat1[5])
+         lngcor.etaorder=FIX(val_wat1[6])
+         lngcor.xterms=FIX(val_wat1[7])
+         lngcor.ximin=DOUBLE(val_wat1[8])
+         lngcor.ximax=DOUBLE(val_wat1[9])
+         lngcor.etamin=DOUBLE(val_wat1[10])
+         lngcor.etamax=DOUBLE(val_wat1[11])
+         lngcor.coeff=DOUBLE(val_wat1[12:*])
+         
+         ncoeff=N_ELEMENTS(val_wat2)-12
+         latcor={functype:0,xiorder:0,etaorder:0,xterms:0,ximin:0d0,ximax:0d0,etamin:0d0,etamax:0d0,coeff:DBLARR(ncoeff)}
+         latcor.functype=FIX(val_wat2[4])
+         latcor.xiorder=FIX(val_wat2[5])
+         latcor.etaorder=FIX(val_wat2[6])
+         latcor.xterms=FIX(val_wat2[7])
+         latcor.ximin=DOUBLE(val_wat2[8])
+         latcor.ximax=DOUBLE(val_wat2[9])
+         latcor.etamin=DOUBLE(val_wat2[10])
+         latcor.etamax=DOUBLE(val_wat2[11])
+         latcor.coeff=DOUBLE(val_wat2[12:*])
+         distort_flag = 'TNX'
+      ENDIF ELSE distort_flag=''
+   ENDIF ELSE BEGIN
+      distort_flag = strlen(ctype[0]) GE 12 ? strmid(ctype[0],9,3) : ''   
+   ENDELSE
+   case proj of 
+     'ZPN': npv = 21
+     'SZP': npv = 3
+     else:  npv = 2
+ endcase
+ 
+ index = proj EQ 'ZPN' ? strtrim(indgen(npv),2) : strtrim(indgen(npv)+1,2)     
+ pv2 = dblarr(npv)
+ if proj EQ 'HPX' then pv2[0] = [4.d,3.d]    ;Default for Healpix
+
+ for i=0,npv-1 do begin 
+     l = where(keyword EQ 'PV'+latc+ '_' + index[i] + alt,  N_pv2)
+     if N_pv2 GT 0 then pv2[i] = lvalue[l[N_pv2-1]] 
+ endfor
+ 
+ pv1 = DBLARR(5)
+ pv1_set = BYTARR(5)
+ FOR i=0,4 DO BEGIN
+     l = WHERE(keyword EQ 'PV'+lonc+'_' + STRTRIM(i,2) + alt, N_pv1)
+     pv1_set[i] = N_pv1 GT 0
+     IF pv1_set[i] THEN pv1[i] = DOUBLE(lvalue[l[N_pv1-1]])
+ ENDFOR
+ xyoff = pv1[0] NE 0d0
+ phi0  = pv1[1]
+ if pv1_set[2] THEN theta0 = pv1[2]
+ if pv1_set[3] then longpole = pv1[3] else begin
+     l = where(keyword EQ 'LONPOLE' + alt,  N_lonpole)
+     if N_lonpole GT 0 then  longpole = double(lvalue[l[N_lonpole-1]]) 
+ endelse
+ if pv1_set[4] then latpole = pv1[4] else begin
+     l = where(keyword EQ 'LATPOLE' + alt,  N_latpole)
+     latpole = N_latpole GT 0 ? double(lvalue[l[N_latpole-1]]) : 90d0 
+ endelse
+ 
+; Convert NCP projection to generalized SIN projection (see Section 6.1.2 of 
+; Calabretta and Greisen (2002)
+
+ if proj EQ 'NCP' then begin
+     ctype = repstr(ctype,'NCP','SIN')
+     proj = 'SIN'
+     PV2 = [0d0, 1d0/tan(crval[1]/radeg) ]
+     longpole = 180d0
+ endif 
+ 
+; Convert GLS projection (Sect 6.1.4, ibid), but per e-mail from Mark
+; Calabretta the correction to CRPIX and CRVAL should only be applied
+; to the second axis.
+ IF proj EQ 'GLS' THEN BEGIN
+     IF crota EQ 0d0 THEN BEGIN
+         crpix[1] -= crval[1]/cdelt[1] ; Shift reference point to dec = 0
+         crval[1] = 0d0
+         ctype = repstr(ctype,'GLS','SFL')
+         proj = 'SFL'
+     ENDIF
+ ENDIF 
+
+ test = WHERE(proj EQ map_types)
+ known = test GE 0
+ 
+ ; If LONPOLE (or PV1_3) is not defined in the header, then we must determine 
+; its default value.    This depends on the value of theta0 (the native
+; longitude of the fiducial point) of the particular projection)
+
+ conic = (proj EQ 'COP') || (proj EQ 'COE') || (proj EQ 'COD') || $
+         (proj EQ 'COO')
+
+ IF conic THEN BEGIN 
+     IF N_pv2 EQ 0 THEN message, $
+     'ERROR -- Conic projections require a PV2_1 keyword in FITS header'
+     theta_a = pv2[0]
+ ENDIF ELSE BEGIN ; Is it a zenithal projection?
+     if (proj EQ 'AZP') || (proj EQ 'SZP') || (proj EQ 'TAN') || $
+        (proj EQ 'STG') || (proj EQ 'SIN') || (proj EQ 'ARC') || $
+        (proj EQ 'ZPN') || (proj EQ 'ZEA') || (proj EQ 'AIR') || $
+        (proj EQ 'XPH') then begin
+         theta_a = 90d0
+     endif else theta_a = 0d0
+ ENDELSE
+     
+ IF ~pv1_set[2] THEN BEGIN
+     theta0 = theta_a 
+     pv1[2] = theta_a
+ ENDIF
+ 
+ if N_elements(longpole) EQ 0 then begin 
+     if crval[1] GE theta0 then longpole = 0d0 else longpole = 180d0
+     if pv1_set[1] THEN longpole += phi0
+ endif
+ 
+ pv1[3] = longpole
+ pv1[4] = latpole
+
+
+ IF xyoff && (phi0 NE 0d0 || theta0 NE theta_a) THEN BEGIN 
+ ; calculate IWC offsets x_0, y_0
+     WCSSPH2XY, phi0, theta0, x0, y0, CTYPE = ctype, PV2 = pv2
+     x0y0 = [x0, y0]
+ ENDIF ELSE x0y0 = [0d0, 0d0]
+ENDELSE
+   
+ axes = FIX([lonc,latc])
+ flip = axes[0] GT axes[1]
+ IF flip THEN BEGIN
+     naxis = REVERSE(naxis)
+     axes  = REVERSE(axes)
+     cdelt = REVERSE(cdelt)
+     crpix = REVERSE(crpix)
+     crval = REVERSE(crval)
+     ctype = REVERSE(ctype)
+     cd    = ROTATE(cd,2)
+     x0y0  = REVERSE(x0y0)
+ ENDIF
+ 
+ equinox = GET_EQUINOX( hdr,eq_code, ALT = alt) 
+ IF equinox EQ 0 THEN equinox = !values.D_NAN
+ radecsys = ''
+ mjdobs  = !values.D_NAN
+ dateobs = 'UNKNOWN'
+ l = WHERE(keyword EQ 'RADESYS' + alt,  N_rdsys)
+ IF N_rdsys GT 0 THEN radecsys = lvalue[l[N_rdsys-1]] ELSE BEGIN
+     l = WHERE(keyword EQ 'RADECSYS',  N_rdsys)
+     IF N_rdsys GT 0 THEN radecsys = lvalue[l[N_rdsys-1]]
+ ENDELSE
+ IF N_rdsys GT 0 THEN radecsys = STRUPCASE(STRTRIM(radecsys,2))
+ 
+ l = WHERE(keyword EQ 'MJD-OBS',  N_mjd)
+ IF N_mjd GT 0 THEN mjdobs = DOUBLE(lvalue[l[N_mjd-1]])
+ l = WHERE(keyword EQ 'DATE-OBS',  N_date)
+ IF N_date GT 0 THEN dateobs = STRUPCASE(lvalue[l[N_date-1]])
+ 
+ IF N_mjd GT 0 && N_date EQ 0 THEN dateobs = date_conv(mjdobs+2400000.5d0,'FITS')
+ IF N_date GT 0 THEN BEGIN
+                            ; try to convert to standard format:
+     dateobs = date_conv(dateobs,'FITS', BAD_DATE=bad_date) 
+     IF ~bad_date THEN BEGIN
+        mjdtest = date_conv(dateobs,'MODIFIED')
+        IF N_mjd EQ 0 THEN mjdobs = mjdtest ELSE $
+            IF ABS(mjdtest - mjdobs) GT 1 THEN MESSAGE, $
+               'DATE-OBS and MJD-OBS are inconsistent'
+     ENDIF ELSE dateobs = 'UNKNOWN'
+ ENDIF
+
+ IF (coord EQ 'C' || coord EQ 'E' || coord EQ 'H') THEN BEGIN
+     IF N_rdsys EQ 0 THEN CASE eq_code OF
+        -1: radecsys = 'ICRS' ; default if no header info.
+         0: radecsys = equinox GE 1984d0 ? 'FK5' : 'FK4'
+         1: radecsys = equinox GE 1984d0 ? 'FK5' : 'FK4'
+         2: radecsys = 'FK4'
+         3: radecsys = 'FK5'
+         4: ; shouldn't get here as implies radecsys exists.
+         else: MESSAGE, 'Internal error: unrecognised eq_code'
+     ENDCASE
+ ENDIF
+ 
+; Note that the dimensions and datatype of each tag must be explicit, so that
+; there is no conflict with structure definitions from different FITS headers
+ 
+ ASTR = {NAXIS:naxis, CD: cd, CDELT: cdelt, CRPIX: crpix, CRVAL: crval, $
+         CTYPE: string(ctype), $
+         LONGPOLE: double( longpole[0]),  LATPOLE: double(latpole[0]), $
+         PV2: pv2, PV1: pv1, $
+         AXES: axes, REVERSE: flip, $
+         COORD_SYS: coord, PROJECTION: proj, KNOWN: known, $
+         RADECSYS: radecsys, EQUINOX: DOUBLE(equinox), $
+         DATEOBS: dateobs, MJDOBS: DOUBLE(mjdobs), X0Y0: x0y0}
+
+; Check for any distortion keywords
+
+ 
+     case distort_flag of 
+         'SIP': begin
+             l = where(keyword EQ 'A_ORDER',  N) 
+             if N GT 0 then a_order  = lvalue[l[N-1]] else a_order = 0
+             l = where(keyword EQ 'B_ORDER',  N) 
+             if N GT 0 then b_order  = lvalue[l[N-1]] else b_order = 0
+             l = where(keyword EQ 'AP_ORDER',  N) 
+             if N GT 0 then ap_order  = lvalue[l[N-1]] else ap_order = 0
+             l = where(keyword EQ 'BP_ORDER',  N) 
+             if N GT 0 then bp_order  = lvalue[l[N-1]] else bp_order = 0
+             a = dblarr(a_order+1,a_order+1)
+             b = dblarr(b_order+1,b_order+1) 
+             ap = dblarr(ap_order+1,ap_order+1)
+             bp = dblarr(bp_order+1,bp_order+1)
+
+             for i=0, a_order do begin
+                 for j=0, a_order do begin
+             l = where(keyword EQ 'A_' + strtrim(i,2) + '_' + strtrim(j,2), N)
+             if N GT 0 then a[i,j] = lvalue[l[N-1]]
+                 endfor
+             endfor
+
+             for i=0, b_order  do begin
+                 for j=0, b_order do begin
+             l = where(keyword EQ 'B_' + strtrim(i,2) + '_' + strtrim(j,2), N)
+             if N GT 0 then b[i,j] = lvalue[l[N-1]]
+                 endfor
+             endfor
+
+             for i=0, bp_order do begin
+                 for j=0, bp_order do begin
+             l = where(keyword EQ 'BP_' + strtrim(i,2) + '_' + strtrim(j,2), N)
+             if N GT 0 then bp[i,j] = lvalue[l[N-1]]
+                 endfor
+             endfor
+
+             for i=0, ap_order do begin
+                 for j=0, ap_order do begin
+             l = where(keyword EQ 'AP_' + strtrim(i,2) + '_' + strtrim(j,2), N)
+             if N GT 0 then ap[i,j] = lvalue[l[N-1]]
+                 endfor
+             endfor
+   
+             distort = {name:distort_flag, a:a, b:b, ap:ap, bp:bp}
+             astr = create_struct(temporary(astr), 'distort', distort)
+          end
+          'TPV': begin 
+             distort = {name:'TPV', a:0.0d, b:0.0d, ap:0.0d, bp:0.0d}
+             astr = create_struct(temporary(astr), 'distort', distort)
+          end
+          'TNX' : begin
+             distort = {name:'TNX', lngcor:lngcor, latcor:latcor}
+             astr = create_struct(temporary(astr), 'distort', distort)
+          end
+       '': 	 
+     else: message,/con,'Unrecognized distortion acronym: ' + distort_flag 
+ endcase
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/extgrp.pro b/Code/script_idl_mv/astrolib/extgrp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0764cefefc43ddf5f2e6345c046cdfdc59c60081
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/extgrp.pro
@@ -0,0 +1,88 @@
+pro extgrp,hdr,par
+;+
+; NAME:
+;	EXTGRP
+; PURPOSE:
+;	Extract the group parameter information out of SXREAD output
+; EXPLANATION:
+;	This procedure extracts the group parameter information out of a 
+;	header and parameter variable obtained from SXREAD.  This allows 
+;	astrometry, photometry and other parameters to be easily SXPARed by 
+;	conventional methods and allows the image and header to be saved in 
+;	a SIMPLE format.
+;
+; CALLING SEQUENCE:
+;	ExtGrp, hdr, par
+;
+; INPUT:
+;	HDR - The header which is to be converted (input and output)
+;	PAR - The Parameter string returned from a call to SXREAD
+;
+; OUTPUT:
+;	HDR -  The converted header, string array
+;
+; OTHER PROCEDURES CALLED:
+;	SXPAR(), SXADDPAR, SXGPAR(), STRN()
+;
+; HISTORY:
+;	25-JUN-90 Version 1 written
+;	13-JUL-92 Header finally added to this ancient procedure, code spiffed up
+;	a bit.  Now 3 times faster.  Added PTYPE comment inclusion.  E. Deutsch
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+  arg=n_params(0)
+  if (arg lt 2) then begin
+    print,'Call: IDL> EXTGRP,header,params_string'
+    print,"e.g.: IDL> EXTGRP,h,par"
+    return
+    endif
+
+  h=hdr
+  pcount=sxpar(h,'PCOUNT')
+  if (pcount le 0) then begin
+    print,'[EXTGRP] Error: PCOUNT not >0 in header'
+    return
+    endif
+
+  htmp=h & ih=0
+  while (strmid(h[ih],0,4) ne 'PTYP') do ih=ih+1
+  itmp=ih & stbyt=0
+  hquick=strarr(4) & hquick[3]='END        '	; tiny temp. header for speed
+
+  for t2=0,pcount-1 do begin
+    hquick=h[ih+3*t2:ih+3*t2+2]
+
+    pty=sxpar(hquick,'PTYPE'+strn(t2+1))
+    comment=strmid(hquick[0],30,50)
+    pdty=sxpar(hquick,'PDTYPE'+strn(t2+1))
+    psz=sxpar(hquick,'PSIZE'+strn(t2+1))/8
+    pvl=sxgpar(h,par,pty,pdty,stbyt,psz)
+
+    sz=size(pvl) & stbyt=stbyt+psz
+    if (sz[1] eq 7) then pvl="'"+strn(pvl,length=18)+"'"
+    tmp=pty+'='+strn(pvl,length=21)+comment
+
+    htmp[itmp]=tmp
+    itmp=itmp+1
+    endfor
+
+  while (strmid(h[ih],0,1) eq 'P') do ih=ih+1
+
+  while (strmid(h[ih],0,3) ne 'END') do begin
+    htmp[itmp]=h[ih]
+    itmp=itmp+1
+    ih=ih+1
+    endwhile		
+
+  htmp[itmp]=h[ih]
+  hdr=htmp[0:itmp]
+
+  sxaddpar,hdr,'SIMPLE','T',' Group Parameters extracted'
+  sxaddpar,hdr,'PCOUNT',0,' All group parameters extracted'
+  sxaddpar,hdr,'PSIZE',0,' All group parameters extracted'
+  sxaddpar,hdr,'GROUPS','T'
+  sxaddpar,hdr,'GCOUNT',1,' Number of groups'
+
+  return
+end
diff --git a/Code/script_idl_mv/astrolib/f_format.pro b/Code/script_idl_mv/astrolib/f_format.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ba7814d8d0976cfd35941d9da311a12b14ccff5d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/f_format.pro
@@ -0,0 +1,112 @@
+function f_format, minval, maxval, factor, length
+;+
+; NAME:
+;	F_FORMAT
+; PURPOSE:
+;	Choose a nice floating format for displaying an array of REAL data.
+; EXPLANATION:
+;	Called by TVLIST, IMLIST.
+;
+; CALLING SEQUENCE:
+;	fmt = F_FORMAT( minval, maxval, factor, [ length ] )
+;
+; INPUTS:
+;	MINVAL - REAL scalar giving the minimum value of an array of numbers
+;		for which one desires a nice format.
+;	MAXVAL - REAL scalar giving maximum value in array of numbers
+;
+; OPTIONAL INPUT:
+;	LENGTH - length of the output F format (default = 5)
+;		must be an integer scalar > 2
+;
+; OUTPUT:
+;	FMT - an F or I format string, e.g. 'F5.1'
+;	FACTOR - factor of 10 by which to multiply array of numbers to achieve
+;		a pretty display using format FMT.
+;
+; EXAMPLE:
+;	Find a nice format to print an array of numbers with a minimum of 5.2e-3
+;	and a maximum  of 4.2e-2.
+;
+;		IDL> fmt = F_FORMAT( 5.2e-3, 4.2e-2, factor )
+;         
+;	yields fmt = '(F5.2)' and factor = .01, i.e. the array can be displayed
+;	with a F5.2 format after multiplication by 100.
+;
+; REVISION HISTORY:
+;	Written W. Landsman              December 1988
+;	Deal with factors < 1.           August 1991
+;	Deal with factors < 1. *and* a large range    October 1992
+;	Now returns In format rather than Fn.0    February, 1994
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Fix display problem for large negative numbers   W. Landsman   Mar 2016
+;-
+ On_error,2
+
+ if N_params() LT 3 then begin                                         
+    print,'Syntax  - fmt = F_FORMAT( minval, maxval, factor, [ length ])'
+    return,''
+ endif
+
+ if N_params() LT 4 then length = 5 else length = length > 2
+ factor = 1.
+
+ RANGE: if ( maxval GT 0) then begin 
+       mxlog = fix( alog10( maxval ) ) 
+       mxval =  (mxlog>0) + 1 
+ endif else if ( maxval LT 0) then begin
+       mxlog =   fix( alog10( abs( maxval ) ) ) 
+       mxval =  (mxlog>0) + 2 
+ endif else begin
+        mxlog = 0
+        mxval = 1
+ endelse
+
+ if ( minval GT 0 ) then begin 
+       mnlog = fix( alog10( minval ))
+       mnval =  (mnlog>0) + 1 
+ endif else if ( minval LT 0) then begin
+       mnlog =   fix(alog10(abs(minval))) 
+       mnval =   (mnlog>0) + 2 
+ endif else begin
+        mnlog = 0
+        mnval = 1
+ endelse
+
+ if ( mnlog LT 0 ) and ( mxlog LT 0 ) then begin        ;All numbers are < 1.0
+      expon = max( [ mnlog,mxlog ] ) -1
+      factor = factor*10.^(expon)
+      maxval = maxval / factor
+      minval = minval / factor
+      goto, RANGE  
+ endif
+
+ dif = abs( mxlog - mnlog )
+ if ( dif GE length-3 ) then begin
+     mxlen = max([mnlog,mxlog])
+     factor =  factor*10.^(mxlen-(length-3))    
+     abs = 0
+
+ endif else begin
+
+ TEST:  tpairv = abs( [mxval,mnval] ) 
+        test   = max( tpairv )          
+
+ if ( test LE length-3 ) then begin        ;No factor needed
+      abs = length - test - 2         
+ endif else begin
+     expon = min( [mxlog, mnlog] ) 
+     if expon EQ 0 then expon = 1         ;Avoid infinite loop
+     factor = factor*10.^(expon)
+     mxval -= expon
+     mnval -= expon
+     goto, TEST 
+ endelse 
+ endelse
+
+ if abs EQ 0 then begin
+        factor = factor/10
+	return,'I' + strtrim(length,2)
+ endif else return,'F' + strtrim( length, 2 ) + '.' + strtrim( abs, 2 )
+ 
+ end
diff --git a/Code/script_idl_mv/astrolib/factor.pro b/Code/script_idl_mv/astrolib/factor.pro
new file mode 100644
index 0000000000000000000000000000000000000000..683932bdc56c46c2b3b56e7d506874a4a85f0841
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/factor.pro
@@ -0,0 +1,277 @@
+;-------------------------------------------------------------
+;+
+; NAME:
+;       FACTOR
+; PURPOSE:
+;       Find prime factors of a given number.
+; CATEGORY:
+; CALLING SEQUENCE:
+;       factor, x, p, n
+; INPUTS:
+;       x = Number to factor (>1).       in
+; KEYWORD PARAMETERS:
+;       Keywords:
+;         /QUIET  means do not print factors.
+;         /DEBUG  Means list steps as they happen.
+;         /TRY    Go beyond 20000 primes.
+; OUTPUTS:
+;       p = Array of prime numbers.      out
+;       n = Count of each element of p.  out
+; COMMON BLOCKS:
+; NOTES:
+;       Note: see also prime, numfactors, print_fact.
+; MODIFICATION HISTORY:
+;       R. Sterner.  4 Oct, 1988.
+;       RES 25 Oct, 1990 --- converted to IDL V2.
+;       R. Sterner, 1999 Jun 30 --- Improved (faster, bigger).
+;       R. Sterner, 1999 Jul  7 --- Bigger values (used unsigned).
+;       R. Sterner, 1999 Jul  9 --- Tried to make backward compatable.
+;       R. Sterner, 2000 Jan 06 --- Fixed to ignore non-positive numbers.
+;       Johns Hopkins University Applied Physics Laboratory.
+;
+; Copyright (C) 1988, Johns Hopkins University/Applied Physics Laboratory
+; This software may be used, copied, or redistributed as long as it is not
+; sold and this copyright notice is reproduced on each copy made.  This
+; routine is provided as is without any express or implied warranties
+; whatsoever.  Other limitations apply as described in the file disclaimer.txt.
+;-
+;-------------------------------------------------------------
+; NAME:
+;       SPC
+; PURPOSE:
+;       Return a string with the specified number of spaces (or other char).
+; CATEGORY:
+; CALLING SEQUENCE:
+;       s = spc(n, [text])
+; INPUTS:
+;       n = number of spaces (= string length).   in 
+;        text = optional text string.              in
+;          # spaces returned is n-strlen(strtrim(text,2))
+; KEYWORD PARAMETERS:
+;       Keywords:
+;         CHARACTER=ch  Character other than a space.
+;           Ex: CHAR='-'.
+;         /NOTRIM means do not do a strtrim on text.
+; OUTPUTS:
+;       s = resulting string.                     out
+; COMMON BLOCKS:
+; NOTES:
+;       Note: Number of requested spaces is reduced by the
+;         length of given string.  Useful for text formatting.
+; MODIFICATION HISTORY:
+;       Written by R. Sterner, 16 Dec, 1984.
+;       RES --- rewritten 14 Jan, 1986.
+;       R. Sterner, 27 Jun, 1990 --- added text.
+;       R. Sterner, 1994 Sep  7 --- Allowed text arrays.
+;       R. Sterner, 1999 Jul  2 --- Added /NOTRIM keyword.
+;       Johns Hopkins University Applied Physics Laboratory.
+;
+; Copyright (C) 1984, Johns Hopkins University/Applied Physics Laboratory
+; This software may be used, copied, or redistributed as long as it is not
+; sold and this copyright notice is reproduced on each copy made.  This
+; routine is provided as is without any express or implied warranties
+; whatsoever.  Other limitations apply as described in the file disclaimer.txt.
+;-------------------------------------------------------------
+ 
+	function spc,n, text, character=char, notrim=notrim, help=hlp
+ 
+	if (n_params(0) lt 1) or keyword_set(hlp) then begin
+	  print,' Return a string with the specified number of spaces (or '+$
+	    'other char).' 
+	  print,' s = spc(n, [text])' 
+	  print, '  n = number of spaces (= string length).   in '
+	  print,'   text = optional text string.              in'
+	  print,'     # spaces returned is n-strlen(strtrim(text,2))'
+	  print,'   s = resulting string.                     out' 
+	  print,' Keywords:'
+	  print,'   CHARACTER=ch  Character other than a space.'
+	  print,"     Ex: CHAR='-'."
+	  print,'   /NOTRIM means do not do a strtrim on text.'
+	  print,' Note: Number of requested spaces is reduced by the'
+	  print,'   length of given string.  Useful for text formatting.'
+	  return, -1
+	endif
+ 
+	if n_params(0) eq 1 then begin
+	  n2 = n
+	endif else begin
+	  if keyword_set(notrim) then $
+	    ntxt=strlen(text) else ntxt=strlen(strtrim(text,2))
+;	  n2 = n - strlen(strtrim(text,2))
+	  n2 = n - ntxt
+	endelse
+ 
+	ascii = 32B
+	if n_elements(char) ne 0 then ascii = (byte(char))[0]
+ 
+	num = n_elements(n2)
+	out = strarr(num)
+	for i = 0, num-1 do begin
+	  if n2[i] le 0 then out[i] = '' else $
+	    out[i] = string(bytarr(n2[i]) + ascii)
+	endfor
+ 
+	if n_elements(out) eq 1 then out=out[0]
+	return, out
+ 
+	end
+
+
+;-------------------------------------------------------------
+; NAME:
+;       PRINT_FACT
+; PURPOSE:
+;       Print prime factors found by the factor routine.
+; CATEGORY:
+; CALLING SEQUENCE:
+;       print_fact, p, n
+; INPUTS:
+;       p = prime factors.          in
+;       n = number of each factor.  in
+; KEYWORD PARAMETERS:
+; OUTPUTS:
+; COMMON BLOCKS:
+; NOTES:
+; MODIFICATION HISTORY:
+;       R. Sterner  4 Oct, 1988.
+;       RES 25 Oct, 1990 --- converted to IDL V2.
+;       R. Sterner, 26 Feb, 1991 --- Renamed from print_factors.pro
+;       R. Sterner, 1999 Jun 30 --- Better output format.
+;       R. Sterner, 1999 Jul  7 --- Bigger values (used unsigned).
+;       R. Sterner, 1999 Jul  9 --- Made backward compatable.
+;
+; Copyright (C) 1988, Johns Hopkins University/Applied Physics Laboratory
+; This software may be used, copied, or redistributed as long as it is not
+; sold and this copyright notice is reproduced on each copy made.  This
+; routine is provided as is without any express or implied warranties
+; whatsoever.  Other limitations apply as described in the file disclaimer.txt.
+;-------------------------------------------------------------
+ 
+	pro print_fact, p, n, help=hlp
+ 
+	if (n_params(0) lt 2) or keyword_set(hlp) then begin
+	  print,' Print prime factors found by the factor routine.'
+	  print,' print_fact, p, n'
+	  print,'   p = prime factors.          in'
+	  print,'   n = number of each factor.  in'
+	  return
+	endif
+ 
+	;-------  Drop unused primes  ---------------
+	w = where(n gt 0)	; Find only primes used.
+	p2 = p[w]
+	n2 = n[w]
+ 
+	;-------  Use largest available integer type  --------------
+	flag = !version.release ge 5.2
+	if flag eq 1 then begin
+	  err=execute('t=1ULL')		; Use 64 bit int (hide from old IDL).
+	endif else begin
+	  t = 1L			; Use long int (best available in old).
+	endelse
+ 
+	;-------  Compute number from it's prime factors.  ----------
+	for i = 0, n_elements(p2)-1 do t = t * p2[i]^n2[i]
+ 
+	;-------  Prepare output  -----------------------
+	a = strtrim(t,2)+' = '			; Start factors string.
+	b = ''					; Start exponents string.
+	last = n_elements(p2)-1			; Last factors index.
+	for i=0, last do begin
+	  a = a + strtrim(p2[i],2)		; Insert next factor.
+	  lena = strlen(a)			; Length of factor string.
+	  nxtb = strtrim(n2[i],2)		; Next exponent.
+	  if nxtb eq '1' then nxtb=' '		; Weed out 1s.
+	  b = b+spc(lena,b,/notrim)+nxtb	; Insert next exponent.
+	  if i ne last then a=a+' x '		; Not last, add x.
+	endfor
+ 
+	;------  Print exponents and factors  -----------
+	print,' '
+	print,' '+b
+	print,' '+a
+ 
+	return
+	end
+
+
+ 
+	pro factor, x, p, n, quiet=quiet, debug=debug, try=try, help=hlp
+ 
+	if (n_params(0) lt 1) or keyword_set(hlp) then begin
+	  print,' Find prime factors of a given number.'
+	  print,' factor, x, p, n'
+	  print,'   x = Number to factor (>1).       in'
+	  print,'   p = Array of prime numbers.      out'
+	  print,'   n = Count of each element of p.  out'
+	  print,' Keywords:'
+	  print,'   /QUIET  means do not print factors.'
+	  print,'   /DEBUG  Means list steps as they happen.'
+	  print,'   /TRY    Go beyond 20000 primes.'
+	  print,' Note: see also prime, numfactors, print_fact.'
+	  return
+	endif
+ 
+	if x le 0 then return
+ 
+	flag = !version.release ge 5.2
+ 
+	s = sqrt(x)			; Only need primes up to sqrt(x).
+	g = long(50 + 0.13457*s)	; Upper limit of # primes up to s.
+	np = 50				; Start with np (50) primes.
+	p = prime(np)			; Find np primes.
+	n = intarr(n_elements(p))	; Divisor count.
+ 
+	if flag eq 1 then $		; Working number.
+	  err=execute('t=ulong64(x)') $	; Use best integer available.
+	  else t=long(x)		; Best pre-5.2 integer.
+	i = 0L				; Index of test prime.
+ 
+loop:	pt = p[i]			; Pull test prime.
+	if keyword_set(debug) then $
+	  print,' Trying '+strtrim(pt,2)+' into '+strtrim(t,2)
+	if flag eq 1 then $
+	  err=execute('t2=ulong64(t/pt)') $
+	  else t2=long(t/pt)
+	if t eq t2*pt then begin	; Check if it divides.
+	  if keyword_set(debug) then $
+	    print,'   Was a factor.  Now do '+strtrim(t2,2)
+	  n[i] = n[i] + 1		; Yes, count it.
+	  t = t2			; Result after division.
+	  if t2 eq 1 then goto, done	; Check if done.
+	  goto, loop			; Continue.
+	endif else begin
+	  i = i + 1			; Try next prime.
+	  if i ge np then begin
+	    s = sqrt(t)			; Only need primes up to sqrt(x).
+	    g = long(50 + 0.13457*s)	; Upper limit of # primes up to s.
+	    if g le np then goto, last	; Must be done.
+	    np = (np+50)<g		; Want 50 more primes.
+	    if (np gt 20000) and (not keyword_set(try)) then begin
+	      print,' Too hard.  Tried '+strtrim(np-50,2)+' primes.'
+	      print,' Trying to crack '+strtrim(t,2)
+	      print,' To go farther use keyword /TRY.'
+	      return
+	    endif
+	    if keyword_set(debug) then $
+	      print,' Finding more primes: '+strtrim(np,2)+ $
+	      '.  Max needed = '+strtrim(g,2)
+	    p = prime(np)		; Find primes.
+	    n = [n,intarr(50)]		; Make room for more factors.
+	  endif
+	  if i ge g then goto, last	; Nothing up to sqrt works.
+	  goto, loop			; Continue.
+	endelse
+ 
+last:	p = [p,t]			; Residue was > sqrt, must be prime.
+	n = [n,1]			; Must occur only once. (else < sqrt).
+ 
+done:	w = where(n gt 0)
+	n = n[w]			; Trim excess off tables.
+	p = p[w]
+ 
+	if not keyword_set(quiet) then print_fact, p, n
+ 
+	return
+	end
+
diff --git a/Code/script_idl_mv/astrolib/fdecomp.pro b/Code/script_idl_mv/astrolib/fdecomp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c751e0059abe699ba7e8f9b523587917d097b940
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fdecomp.pro
@@ -0,0 +1,130 @@
+pro fdecomp, filename, disk, dir, name, qual, version, OSfamily = osfamily
+;+
+; NAME:
+;     FDECOMP
+; PURPOSE:
+;     Routine to decompose file name(s) for any operating system.
+;
+; CALLING SEQUENCE:
+;     FDECOMP, filename, disk, dir, name, qual, [OSFamily = ]
+;
+; INPUT:
+;     filename - string file name(s), scalar or vector
+;
+; OUTPUTS:
+;     All the output parameters will have the same number of elements as 
+;       input filename 
+;
+;       disk - disk name, always '' on a Unix machine, scalar or vector string
+;       dir - directory name, scalar or vector string
+;       name - file name, scalar or vector string 
+;       qual - qualifier, set equal to the characters beyond the last "."
+;
+; OPTIONAL INPUT KEYWORD:
+;     OSFamily -  scalar string specifying the operating system, must be either
+;             'Windows' or 'unix'.    If not supplied,
+;             then !VERSION.OS_FAMILY is used to determine the OS.
+; EXAMPLES:
+;     Consider the following file names 
+;
+;     unix:    file = '/itt/idl71/avg.pro' 
+;     Windows: file =  'd:\itt\idl71\avg.pro'
+;       
+;     then IDL> FDECOMP,  file, disk, dir, name, qual
+;       will return the following
+;
+;                 Disk             Dir          Name        Qual    
+;       Unix:      ''            '/itt/idl71/'  'avg'       'pro'   
+;       Windows:    'd:'         \itt\idl71\    'avg'       'pro'   
+;
+; NOTES:
+;     (1) The period is removed between the name and qualifier 
+;     (2) Unlike the intrinsic FILE_BASENAME() and FILE_DIRNAME() functions,
+;         one can use FDECOMP to decompose a Windows file name on a Unix machine
+;         or a Unix filename on a Windows machine.
+;
+; ROUTINES CALLED:
+;     None.
+; HISTORY
+;     version 1  D. Lindler  Oct 1986
+;     Include VMS DECNET machine name in disk    W. Landsman  HSTX  Feb. 94
+;     Converted to Mac IDL, I. Freedman HSTX March 1994          
+;     Major rewrite to accept vector filenames V5.3   W. Landsman June 2000
+;     Fix cases where disk name not always present  W. Landsman  Sep. 2000
+;     Make sure version defined for Windows  W. Landsman April 2004
+;     Include final delimiter in directory under Windows as advertised
+;                W. Landsman   May 2006
+;     Remove VMS support, W. Landsman    September 2006
+;     Remove MacOS branch (same as Unix) W. Landsman  August 2009
+;-
+;--------------------------------------------------------
+;
+  On_error,2                            ;Return to caller
+  compile_opt idl2
+
+  if N_params() LT 2 then begin
+     print, 'Syntax - FDECOMP, filename, disk, [dir, name, qual ] '
+     return
+  endif
+    
+
+  if ~keyword_set(osfamily) then osfamily = !Version.OS_Family
+       st = filename
+     disk = st
+     replicate_inplace,disk,''
+     dir = disk
+     qual = disk
+
+
+ if OSFAMILY EQ "Windows" then begin 
+ 
+     lpos = strpos( st, ':')                 ; DOS diskdrive (i.e. c:)
+     good = where(lpos GT 0, Ngood) 
+     if Ngood GT 0 then begin
+         stg = st[good]
+         lpos = reform( lpos[good], 1, Ngood)
+         disk[good] = strmid( stg, 0, lpos+1) 
+         st[good] = strmid(stg,lpos+1 )
+     endif
+
+;  Search the path name (i.e. \dos\idl\) and locate last backslash
+
+     lpos = strpos(st,'\',/reverse_search)
+     good = where(lpos Gt 0, Ngood)
+
+ 
+ endif ELSE begin                 ;Unix
+
+ 
+; Unix directory name ends at last slash
+
+    lpos = strpos(st,'/',/reverse_search)
+    good = where(lpos GE 0, Ngood)
+ 
+  endelse
+    
+  if Ngood GT 0 then begin      ;Extract directory name if present
+          stg = st[good] 
+          lpos = reform( lpos[good],1, Ngood )
+ 
+             dir[good] = strmid(stg,0, lpos+1) 
+             st[good] = strmid(stg,lpos+1 )
+    endif
+  
+; get  name and qualifier (extension)...qual is optional
+
+    lpos = strpos(st,'.',/reverse_search)
+    good = where(lpos GE 0, Ngood)
+    name = st
+
+    if Ngood GT 0 then begin
+             stg = st[good]
+             lpos = reform(lpos[good], 1, Ngood)
+ 
+             name[good] = strmid(stg,0,lpos )
+             qual[good] = strmid(stg,lpos+1 )
+     endif
+
+
+ return
+  end
diff --git a/Code/script_idl_mv/astrolib/file_launch.pro b/Code/script_idl_mv/astrolib/file_launch.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3cf2677c235db649d5f44ab8aa626caa498fb312
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/file_launch.pro
@@ -0,0 +1,108 @@
+; docformat = 'rst'
+;+ 
+;  NAME:
+;      FILE_LAUNCH
+;
+;  PURPOSE: 
+;      Launch a file using the default application of the operating system 
+;
+;  EXPLANATION:
+;      The FILE_LAUNCH procedure procedure will launch a file (e.g. a .pdf, .docx or .html
+;      file)  using the default application of the operating system.    By default, it 
+;      first  tries to use the Java desktop class.   
+;              https://docs.oracle.com/javase/tutorial/uiswing/misc/desktop.html 
+;      If this fails, it uses the appropriate Spawn command for the oS to launch  
+;
+;  CALLING SEQUENCE:
+;     file_launch, file, [ buseJava,  ojDesktop = ojDesktop, /QUIET ] 
+;
+;  INPUT PARMAMTER:
+;     file: in, required, type=string
+;          scalar filename (with path if required) to launch   
+;
+;  OPTIONAL INPUT KEYWORD:
+;       bUseJava: in, optional, type=boolean, default=1
+;           Flag to indicate if java should be used to launch browser.
+;           True by default. Routine falls back to spawn commands if desktop is
+;           not supported.
+;
+;       /NoWait - if set, then if using Spawn, wait for the command to return
+;           This is slower but is useful for debugging
+;
+;       /quiet - if set, then don't print a message when forced to use SPAWN
+;
+;   OPTIONAL OUTPUT KEYWORD:
+;     ojDesktop : in, out, optional, type=object
+;             reference to a java AWT desktop instance
+;       
+;  EXAMPLE:
+;
+;       Open a PDF file test.pdf in the current directory
+;       IDL> file_launch, 'test.pdf'
+;
+;
+;  HISTORY:
+;     First release W. Landsman      March 2016
+;          Heavily based on code by Derek Sabatke
+;     
+;-
+;-----------------------------------------------------------------------------
+
+pro file_launch, file, ojDesktop = ojDesktop, bUseJava = bUseJava, quiet=quiet, $
+          Nowait = nowait
+  COMPILE_OPT idl2, HIDDEN
+  
+  if ~file_test(file) then begin
+    message,/CON,'ERROR -- File not found ' + file
+    return
+  endif
+  ;set option defaults
+  setdefaultvalue, bUseJava, 1L
+  setdefaultvalue, NoWait, 0
+
+  Catch,theError
+  if theError NE 0 then begin
+      Catch,/Cancel
+      if bUseJava EQ 1 then bUseJava = 0 else begin    ;If Java failed then use Spawn
+             void = cgErrorMsg(/quiet)
+             return
+      endelse       
+  endif    
+
+  ;initialize variables
+  if bUseJava && ((N_elements(ojDesktop) eq 0) || (~obj_valid(ojDesktop))) then begin 
+    oJavaAWTDesktop = OBJ_NEW('IDLJavaObject$Static$JAVA_AWT_DESKTOP', 'java.awt.Desktop')
+    if oJavaAWTDesktop.isDesktopSupported() then ojDesktop = ojavaAWTDesktop.getDesktop() $
+    else bUseJava = 0L
+  endif
+  
+  if bUseJava && ojDesktop.isDesktopSupported() then begin ; have java do the launching if possible
+    if !VERSION.OS_FAMILY NE 'WINDOWS' then fname = file_search(file,/full) $
+                                       else fname = file	
+    sCleanOutputFN = strjoin(strsplit(fname, '\\', /extract), '/') ;purge (possible) backslashes
+    oJURI = OBJ_NEW('IDLJavaObject$Static$JAVA_NET_URI', 'java.net.URI')
+    oJString = OBJ_NEW('IDLJavaObject$JAVA_LANG_STRING', 'java.lang.String', 'file://'+sCleanOutputFN)
+    oURI = oJURI.create(oJString)
+    
+    ojDesktop.browse, oURI
+
+  endif else begin; no java, so try spawning a command
+       if ~keyword_set(quiet) then message,'Using Spawn',/INF
+   if !VERSION.OS_NAME EQ 'Mac OS X' then begin 
+          cmd = 'open "'+ file +'" '
+          if ~nowait then cmd += '&'
+          spawn,cmd
+    endif else begin
+    case StrUpCase(!Version.OS_Family) OF
+      'WINDOWS': spawn, 'start "" "'+ file +'"', nowait = nowait
+         'UNIX': begin 
+                cmd = 'xdg-open "'+ file +'" '
+                if ~nowait then cmd+= '&'
+                spawn,cmd
+                end
+           else: print, 'Unable to launch ' + file + ' automatically.'
+    endcase
+    endelse
+
+  endelse
+end
diff --git a/Code/script_idl_mv/astrolib/filter_image.pro b/Code/script_idl_mv/astrolib/filter_image.pro
new file mode 100644
index 0000000000000000000000000000000000000000..22e9c56b6e1a99e9125ee5411a7f9739aa532641
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/filter_image.pro
@@ -0,0 +1,196 @@
+function filter_image, image, SMOOTH=width_smooth, ITERATE_SMOOTH=iterate, $
+                              MEDIAN=width_median, ALL_PIXELS=all_pixels, $
+                              FWHM_GAUSSIAN=fwhm, NO_FT_CONVOL=no_ft, PSF=psf
+;+
+; NAME:
+;       FILTER_IMAGE
+;
+; PURPOSE:
+;       Identical to MEDIAN or SMOOTH but handle edges and allow iterations.
+; EXPLANATION:
+;       Computes the average and/or median of pixels in moving box,
+;       replacing center pixel with the computed average and/or median,
+;       (using the IDL SMOOTH() or MEDIAN() functions).
+;       The main reason for using this function is the options to
+;       also process the pixels at edges and corners of image, and,
+;       to apply iterative smoothing simulating convolution with Gaussian,
+;       and/or to convolve image with a Gaussian kernel.    Users might also
+;       look at the function ESTIMATOR_FILTER() introduced in IDL 7.1.
+;
+; CALLING SEQUENCE:
+;       Result = filter_image( image, SMOOTH=width, MEDIAN = width, /ALL_PIXELS
+;                               /ITERATE, FWHM =,  /NO_FT_CONVOL)
+;
+; INPUT:
+;       image = 2-D array (matrix)
+;
+; OPTIONAL INPUT KEYWORDS:
+;       SMOOTH = scalar (odd) integer specifying the width of a square box 
+;               for moving average, in # pixels.  /SMOOTH  means use box 
+;               width = 3 pixels for smoothing.
+;
+;        MEDIAN = scalar (usually odd) integer specifying the width of square 
+;               moving box for median filter, in # pixels.   /MEDIAN  means use
+;               box width = 3 pixels for median filter.
+;   
+;       /ALL_PIXELS causes the edges of image to be filtered as well.   This
+;               is accomplished by reflecting pixels adjacent to edges outward
+;               (similar to the /EDGE_WRAP keyword in CONVOL).
+;               Note that this is a different algorithm from the /EDGE_TRUNCATE 
+;               keyword to SMOOTH or CONVOL, which duplicates the nearest pixel.   
+;
+;       /ITERATE means apply smooth(image,3) iteratively for a count of
+;               (box_width-1)/2 times (=radius), when box_width >= 5.
+;               This is equivalent to convolution with a Gaussian PSF
+;               of FWHM = 2 * sqrt( radius ) as radius gets large.
+;               Note that /ALL_PIXELS is automatically applied,
+;               giving better results in the iteration limit.
+;               (also, MEDIAN keyword is ignored when /ITER is specified).
+;
+;       FWHM_GAUSSIAN = Full-width half-max of Gaussian to convolve with image. 
+;                       FWHM can be a single number (circular beam),
+;                       or 2 numbers giving axes of elliptical beam.
+;
+;       /NO_FT_CONVOL causes the convolution to be computed directly,
+;               with intrinsic IDL CONVOL function.   The default is to use 
+;               FFT when factors of size are all LE 13.   Note that 
+;               external function convolve.pro handles both cases)
+;
+; OPTIONAL INPUT/OUTPUT KEYWORD:
+;     PSF = Array containing the PSF used during the convolution.   This 
+;           keyword is only active if the FWHM_GAUSSIAN keyword is also 
+;           specified.     If PSF is undefined on input, then upon output it
+;           contains the Gaussian convolution specified by the FWHM_GAUSSIAN
+;           keyword.    If the PSF array is defined on input then it is used 
+;           as the convolution kernel,  the value of the  FWHM_GAUSSIAN keyword
+;           is ignored.      Typically, on a first call set PSF to an undefined
+;           variable, which can be reused for subsequent calls to prevent 
+;           recalculation of the Gaussian PSF.
+; RESULT:
+;       Function returns the smoothed, median filtered, or convolved image.
+;       If both SMOOTH and MEDIAN are specified, median filter is applied first.
+;       If only SMOOTH is applied, then output is of same type as input.  If
+;       either MEDIAN or FWHM_GAUSSIAN is supplied than the output is at least
+;       floating (double if the input image is double). 
+;
+; EXAMPLES:
+;       To apply 3x3 moving median filter and
+;       then 3x3 moving average, both applied to all pixels:
+;
+;               Result = filter_image( image, /SMOOTH, /MEDIAN, /ALL )
+;
+;       To iteratively apply 3x3 moving average filter for 4 = (9-1)/2 times,
+;       thus approximating convolution with Gaussian of FWHM = 2*sqrt(4) = 4 :
+;
+;               Result = filter_image( image, SMOOTH=9, /ITER )
+;
+;       To convolve all pixels with Gaussian of FWHM = 3.7 x 5.2 pixels:
+;
+;               Result = filter_image( image, FWHM=[3.7,5.2], /ALL )
+;
+; EXTERNAL CALLS:
+;       function psf_gaussian
+;       function convolve
+;       pro factor
+;       function prime          ;all these called only if FWHM is specified
+;
+; PROCEDURE:
+;       If both /ALL_PIXELS (or /ITERATE)  keywords are set then
+;       create a larger image by reflecting the edges outward, then call the 
+;       IDL MEDIAN() or SMOOTH() function on the larger image, and just return 
+;       the central part (the original size image).
+;
+;       NAN values are recognized during calls to MEDIAN() or SMOOTH(), but 
+;       not for convolution with a Gaussian (FWHM keyword supplied). 
+; HISTORY:
+;       Written, 1991, Frank Varosi, NASA/GSFC.
+;       FV, 1992, added /ITERATE option.
+;       FV, 1993, added FWHM_GAUSSIAN= option.
+;       Use /EVEN call to median, recognize NAN values in SMOOTH 
+;                  W. Landsman   June 2001
+;       Added PSF keyword,   Bjorn Heijligers/WL, September 2001
+;       Keep same output data type if /ALL_PIXELS supplied A. Steffl Mar 2011
+;-
+  compile_opt idl2
+  
+  if N_params() LT 1 then begin
+      print,'Syntax - Result = filter_image( image, SMOOTH=width, /ALL_PIXELS'
+      print,'                 MEDIAN= width, ITERATE, FWHM=,  /NO_FT_CONVOL'
+      return, -1
+  endif
+
+        sim = size( image )
+        Lx = sim[1]-1
+        Ly = sim[2]-1
+
+        if (sim[0] NE 2) || (sim[4] LE 4) then begin
+                message,"input must be an image (a matrix)",/INFO
+                return,image
+           endif
+
+        if keyword_set( iterate ) then begin
+                if N_elements( width_smooth ) NE 1 then return,image
+                if (width_smooth LT 1) then return,image
+                imf = image
+                nit = (width_smooth>3)/2
+                for i=1,nit do  imf = filter_image( imf, /SMOOTH, /ALL )
+                return,imf
+           endif
+
+        box_wid = 0
+        if keyword_set( width_smooth ) then box_wid = width_smooth > 3
+        if keyword_set( width_median ) then box_wid = (width_median > box_wid)>3
+
+        if keyword_set( fwhm ) then begin
+                npix = ( 3 * fwhm[ 0: ( (N_elements( fwhm )-1) < 1 ) ] ) > 3
+                npix = 2 * fix( npix/2 ) + 1    ;make # pixels odd.
+                box_wid = box_wid > max( [npix] )
+           endif
+
+        if (box_wid LT 3) then return, image
+
+        if keyword_set(all_pixels) then begin
+                
+                box_wid = fix( box_wid )
+                radius = (box_wid/2) > 1
+                Lxr = Lx+radius
+                Lyr = Ly+radius
+                rr = 2*radius
+		imf = make_array(sim[1]+rr, sim[2]+rr, type = sim[3])
+                imf[radius,radius] = image              ; reflect edges outward
+                                                        ; to make larger image.
+                imf[  0,0] = rotate( imf[radius:rr,*], 5 )      ;Left
+                imf[Lxr,0] = rotate( imf[Lx:Lxr,*], 5 )         ;right
+                imf[0,  0] = rotate( imf[*,radius:rr], 7 )      ;bottom
+                imf[0,Lyr] = rotate( imf[*,Ly:Lyr], 7 )         ;top
+
+          endif else begin
+                radius=0
+                imf = image
+           endelse
+
+        if keyword_set( width_median ) then $
+                       imf = median(/even, imf, width_median>3 ) 
+                            
+        if keyword_set( width_smooth ) then $
+              imf = smooth( imf, width_smooth>3, /NAN )
+
+        if keyword_set( fwhm ) then begin
+
+                if N_elements( no_ft ) NE 1 then begin
+                        sim = size( imf )
+                        factor,sim[1],pfx,nfx,/quiet
+                        factor,sim[2],pfy,nfy,/quiet
+                        no_ft = max( [pfx,pfy] ) GT 13
+                   endif
+
+                if N_elements(PSF) EQ 0 then $
+                          psf=psf_gaussian( NP=npix,FWHM=fwhm,/NORM )
+			  
+                imf = convolve( imf,  NO_FT=no_ft, psf) 
+          endif
+
+    if radius GT 0 then $
+                return, imf[ radius:(Lx+radius), radius:(Ly+radius) ] $
+           else return, imf
+end
diff --git a/Code/script_idl_mv/astrolib/find.pro b/Code/script_idl_mv/astrolib/find.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f1ed8d14e278d7e01b6214dbe5f05c6370024787
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/find.pro
@@ -0,0 +1,464 @@
+pro find, image, x, y, flux, sharp, roundness, hmin, fwhm, roundlim, sharplim,$
+                      PRINT = print, SILENT=silent, MONITOR= monitor
+;+
+; NAME:
+;	FIND
+; PURPOSE:
+;	Find positive brightness perturbations (i.e stars) in an image 
+; EXPLANATION:
+;	Also returns centroids and shape parameters (roundness & sharpness).
+;	Adapted from 1991 version of DAOPHOT, but does not allow for bad pixels
+;       and uses a slightly different centroid algorithm.
+;
+;       Modified in March 2008 to use marginal Gaussian fits to find centroids       
+; CALLING SEQUENCE:
+;	FIND, image, [ x, y, flux, sharp, round, hmin, fwhm, roundlim, sharplim 
+;		PRINT= , /SILENT, /MONITOR]
+;
+; INPUTS:
+;	image - 2 dimensional image array (integer or real) for which one
+;		wishes to identify the stars present
+;
+; OPTIONAL INPUTS:
+;	FIND will prompt for these parameters if not supplied
+;
+;	hmin -  Threshold intensity for a point source - should generally 
+;		be 3 or 4 sigma above background RMS
+;	fwhm  - FWHM (in pixels) to be used in the convolve filter
+;	sharplim - 2 element vector giving low and high cutoff for the
+;		sharpness statistic (Default: [0.2,1.0] ).   Change this
+;		default only if the stars have significantly larger or 
+;		or smaller concentration than a Gaussian
+;	roundlim - 2 element vector giving low and high cutoff for the
+;		roundness statistic (Default: [-1.0,1.0] ).   Change this 
+;		default only if the stars are significantly elongated.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /MONITOR - Normally, FIND will display the results for each star 
+;                only if no output variables are supplied.   Set /MONITOR
+;                to always see the result of each individual star.
+;	/SILENT - set /SILENT keyword to suppress all output display 
+;	PRINT - if set and non-zero then FIND will also write its results to
+;		a file find.prt.   Also one can specify a different output file 
+;		name by setting PRINT = 'filename'.
+;
+; OPTIONAL OUTPUTS:
+;	x - vector containing x position of all stars identified by FIND
+;	y-  vector containing y position of all stars identified by FIND
+;	flux - vector containing flux of identified stars as determined
+;		by a Gaussian fit.  Fluxes are NOT converted to magnitudes.
+;	sharp - vector containing sharpness statistic for identified stars
+;	round - vector containing roundness statistic for identified stars
+;
+; NOTES:
+;	(1) The sharpness statistic compares the central pixel to the mean of 
+;       the surrounding pixels.   If this difference is greater than the 
+;       originally estimated height of the Gaussian or less than 0.2 the height of the
+;	Gaussian (for the default values of SHARPLIM) then the star will be
+;	rejected. 
+;
+;       (2) More recent versions of FIND in DAOPHOT allow the possibility of
+;       ignoring bad pixels.    Unfortunately, to implement this in IDL
+;       would preclude the vectorization made possible with the CONVOL function
+;       and would run extremely slowly.
+;
+;       (3) Modified in March 2008 to use marginal Gaussian distributions to 
+;       compute centroid.   (Formerly, find.pro determined centroids by locating
+;       where derivatives went to zero -- see cntrd.pro for this algorithm.   
+;       This was the method used in very old (~1984) versions of DAOPHOT. )   
+;       As discussed in more  detail in the comments to the code, the  centroid
+;       computation here is  the same as in IRAF DAOFIND but differs slightly 
+;       from the current DAOPHOT.
+; PROCEDURE CALLS:
+;	GETOPT()
+; REVISION HISTORY:
+;	Written W. Landsman, STX  February, 1987
+;	ROUND now an internal function in V3.1   W. Landsman July 1993
+;	Change variable name DERIV to DERIVAT    W. Landsman Feb. 1996
+;	Use /PRINT keyword instead of TEXTOUT    W. Landsman May  1996
+;	Changed loop indices to type LONG       W. Landsman Aug. 1997
+;       Replace DATATYPE() with size(/TNAME)   W. Landsman Nov. 2001
+;       Fix problem when PRINT= filename   W. Landsman   October 2002
+;       Fix problems with >32767 stars   D. Schlegel/W. Landsman Sep. 2004
+;       Fix error message when no stars found  S. Carey/W. Landsman Sep 2007
+;       Rewrite centroid computation to use marginal Gaussians W. Landsman 
+;                 Mar 2008
+;       Added Monitor keyword, /SILENT now suppresses all output 
+;                   W. Landsman    Nov 2008
+;       Work when threshold is negative (difference images) W. Landsman May 2010
+;-
+;
+ On_error,2                         ;Return to caller
+ compile_opt idl2
+
+ npar   = N_params()
+ if npar EQ 0 then begin
+    print,'Syntax - FIND, image,' + $
+          '[ x, y, flux, sharp, round, hmin, fwhm, roundlim, sharplim'
+    print,'                      PRINT= , /SILENT, /MONITOR ]'
+    return
+ endif
+;Determine if hardcopy output is desired
+ doprint = keyword_set( PRINT)
+ silent =  keyword_set( SILENT )
+ if N_elements(monitor) EQ 0 then $
+      monitor = (not silent) and (not arg_present(flux) ) 
+
+ maxbox = 13 	;Maximum size of convolution box in pixels 
+
+; Get information about the input image 
+
+ type = size(image)
+ if ( type[0] NE 2 ) then message, $
+     'ERROR - Image array (first parameter) must be 2 dimensional'
+ n_x  = type[1] & n_y = type[2]
+ message, NoPrint=Silent, $
+    'Input Image Size is '+strtrim(n_x,2) + ' by '+ strtrim(n_y,2),/INF
+
+ if ( N_elements(fwhm) NE 1 ) then $
+           read, 'Enter approximate FWHM: ', fwhm
+  if fwhm LT 0.5 then message, $
+        'ERROR - Supplied FWHM must be at least 0.5 pixels'	   
+
+ radius = 0.637*FWHM > 2.001             ;Radius is 1.5 sigma
+ radsq = radius^2
+ nhalf = fix(radius) < (maxbox-1)/2   	;
+ nbox = 2*nhalf + 1	;# of pixels in side of convolution box 
+ middle = nhalf          ;Index of central pixel
+
+ lastro = n_x - nhalf
+ lastcl = n_y - nhalf
+ sigsq = ( fwhm/2.35482 )^2
+ mask = bytarr( nbox, nbox )   ;Mask identifies valid pixels in convolution box 
+ g = fltarr( nbox, nbox )      ;g will contain Gaussian convolution kernel
+
+ dd = indgen(nbox-1) + 0.5 - middle	;Constants need to compute ROUND
+ dd2 = dd^2
+
+ row2 = (findgen(Nbox)-nhalf)^2
+
+ for i = 0, nhalf do begin
+	temp = row2 + i^2
+	g[0,nhalf-i] = temp         
+        g[0,nhalf+i] = temp                           
+ endfor
+
+
+ mask = fix(g LE radsq)     ;MASK is complementary to SKIP in Stetson's Fortran
+ good = where( mask, pixels)  ;Value of c are now equal to distance to center
+
+;  Compute quantities for centroid computations that can be used for all stars
+ g = exp(-0.5*g/sigsq)
+
+;  In fitting Gaussians to the marginal sums, pixels will arbitrarily be 
+; assigned weights ranging from unity at the corners of the box to 
+; NHALF^2 at the center (e.g. if NBOX = 5 or 7, the weights will be
+;
+;                                 1   2   3   4   3   2   1
+;      1   2   3   2   1          2   4   6   8   6   4   2
+;      2   4   6   4   2          3   6   9  12   9   6   3
+;      3   6   9   6   3          4   8  12  16  12   8   4
+;      2   4   6   4   2          3   6   9  12   9   6   3
+;      1   2   3   2   1          2   4   6   8   6   4   2
+;                                 1   2   3   4   3   2   1
+;
+; respectively).  This is done to desensitize the derived parameters to 
+; possible neighboring, brighter stars.
+
+
+ xwt = fltarr(nbox,nbox)
+ wt = nhalf - abs(findgen(nbox)-nhalf ) + 1
+ for i=0,nbox-1 do xwt[0,i] = wt
+ ywt = transpose(xwt) 
+  sgx = total(g*xwt,1)
+ p = total(wt)
+ sgy = total(g*ywt,2)
+ sumgx = total(wt*sgy)
+ sumgy = total(wt*sgx)
+ sumgsqy = total(wt*sgy*sgy)
+ sumgsqx = total(wt*sgx*sgx)
+ vec = nhalf - findgen(nbox) 
+ dgdx = sgy*vec
+ dgdy = sgx*vec
+ sdgdxs = total(wt*dgdx^2)
+ sdgdx = total(wt*dgdx) 
+ sdgdys = total(wt*dgdy^2)
+ sdgdy = total(wt*dgdy) 
+ sgdgdx = total(wt*sgy*dgdx)
+ sgdgdy = total(wt*sgx*dgdy)
+
+ 
+ c = g*mask          ;Convolution kernel now in c      
+ sumc = total(c)
+ sumcsq = total(c^2) - sumc^2/pixels
+ sumc = sumc/pixels
+ c[good] = (c[good] - sumc)/sumcsq
+ c1 = exp(-.5*row2/sigsq)
+ sumc1 = total(c1)/nbox
+ sumc1sq = total(c1^2) - sumc1
+ c1 = (c1-sumc1)/sumc1sq
+
+ message,/INF,Noprint=Silent, $
+    'RELATIVE ERROR computed from FWHM ' + strtrim(sqrt(total(c[good]^2)),2)
+ if N_elements(hmin) NE 1 then read, $
+    'Enter minimum value above background for threshold detection: ',hmin
+
+ if N_elements(sharplim) NE 2 then begin
+      print,'Enter low and high cutoffs, press [RETURN] for defaults:'
+GETSHARP:   
+      ans = ''
+      read, 'Image Sharpness Statistic (DEFAULT = 0.2,1.0): ', ans   
+      if ans EQ '' then sharplim = [0.2,1.0] else begin
+         sharplim = getopt(ans,'F')
+          if N_elements(sharplim) NE 2 then begin  
+              message, 'ERROR - Expecting 2 scalar values',/CON
+              goto, GETSHARP     
+          endif
+      endelse                                                      
+
+GETROUND: 
+  ans = ''
+  read, 'Image Roundness Statistic [DEFAULT = -1.0,1.0]: ',ans
+  if ans EQ '' then roundlim = [-1.,1.] else begin
+      roundlim = getopt( ans, 'F' )
+      if N_elements( roundlim ) NE 2 then begin
+           message,'ERROR - Expecting 2 scalar values',/CON
+           goto, GETROUND   
+      endif
+ endelse
+ endif 
+
+ message,'Beginning convolution of image', /INF, NoPrint=Silent
+
+ h = convol(float(image),c)    ;Convolve image with kernel "c"
+
+    minh = min(h)
+    h[0:nhalf-1,*] = minh & h[n_x-nhalf:n_x-1,*] = minh
+    h[*,0:nhalf-1] = minh & h[*,n_y-nhalf:n_y-1] = minh
+
+ message,'Finished convolution of image', /INF, NoPrint=Silent
+
+ mask[middle,middle] = 0	;From now on we exclude the central pixel
+ pixels = pixels -1      ;so the number of valid pixels is reduced by 1
+ good = where(mask)      ;"good" identifies position of valid pixels
+ xx= (good mod nbox) - middle	;x and y coordinate of valid pixels 
+ yy = fix(good/nbox) - middle    ;relative to the center
+ offset = yy*n_x + xx
+SEARCH: 			    ;Threshold dependent search begins here
+
+ index = where( h GE hmin, nfound)  ;Valid image pixels are greater than hmin
+ if nfound EQ 0 then begin          ;Any maxima found?
+
+    message,'ERROR - No maxima exceed input threshold of ' + $
+             string(hmin,'(F9.1)'),/CON
+    goto,FINISH    
+
+ endif
+
+ for i= 0L, pixels-1 do begin                             
+
+	stars = where (h[index] GE h[index+offset[i]], nfound)
+        if nfound EQ 0 then begin  ;Do valid local maxima exist?
+             message,'ERROR - No maxima exceed input threshold of ' + $
+                     string(hmin,'(F9.1)'),/CON
+             goto,FINISH  
+        endif
+	index = index[stars]
+
+ endfor 
+ 
+ ix = index mod n_x              ;X index of local maxima
+ iy = index/n_x                  ;Y index of local maxima
+ ngood = N_elements(index)       
+ message,/INF,Noprint=Silent, $
+    strtrim(ngood,2)+' local maxima located above threshold'
+
+ nstar = 0L       	;NSTAR counts all stars meeting selection criteria
+ badround = 0L & badsharp=0L  &  badcntrd=0L
+ if (npar GE 2) or (doprint) then begin 	;Create output X and Y arrays? 
+  	x = fltarr(ngood) & y = x
+ endif
+
+ if (npar GE 4) or (doprint) then begin   ;Create output flux,sharpness arrays?
+ 	flux = x & sharp = x & roundness = x
+ endif
+
+ if doprint then begin	;Create output file?
+
+         if ( size(print,/TNAME) NE 'STRING' ) then file = 'find.prt' $
+                                         else file = print
+         message,'Results will be written to a file ' + file,/INF,Noprint=Silent
+         openw,lun,file,/GET_LUN
+	printf,lun,' Program: FIND '+ systime()
+	printf,lun,format='(/A,F7.1)',' Threshold above background:',hmin
+	printf,lun,' Approximate FWHM:',fwhm
+	printf,lun,format='(2(A,F6.2))',' Sharpness Limits: Low', $
+                sharplim[0], '  High',sharplim[1]
+	printf,lun,format='(2(A,F6.2))',' Roundness Limits: Low', $
+                roundlim[0],'  High',roundlim[1]
+	printf,lun,format='(/A,i6)',' No of sources above threshold',ngood
+
+ endif                      
+
+ if (not SILENT) and MONITOR then $
+  print,format='(/8x,a)','     STAR      X      Y     FLUX     SHARP    ROUND'
+
+;  Loop over star positions; compute statistics
+
+ for i = 0L,ngood-1 do begin   
+     temp = float(image[ix[i]-nhalf:ix[i]+nhalf,iy[i]-nhalf:iy[i]+nhalf])
+     d = h[ix[i],iy[i]]                  ;"d" is actual pixel intensity        
+
+;  Compute Sharpness statistic
+
+     sharp1 = (temp[middle,middle] - (total(mask*temp))/pixels)/d
+     if ( sharp1 LT sharplim[0] ) or ( sharp1 GT sharplim[1] ) then begin
+	badsharp = badsharp + 1
+	goto, REJECT             ;Does not meet sharpness criteria
+     endif
+
+;   Compute Roundness statistic
+
+     dx = total( total(temp,2)*c1)   
+     dy = total( total(temp,1)*c1)
+     if (dx LE 0) or (dy LE 0) then begin
+         badround = badround + 1
+	 goto, REJECT           ;Cannot compute roundness
+     endif
+
+     around = 2*(dx-dy) / ( dx + dy )    ;Roundness statistic
+     if ( around LT roundlim[0] ) or ( around GT roundlim[1] ) then begin
+	badround = badround + 1
+	goto,REJECT           ;Does not meet roundness criteria
+     endif
+
+;
+; Centroid computation:   The centroid computation was modified in Mar 2008 and
+; now differs from DAOPHOT which multiplies the correction dx by 1/(1+abs(dx)). 
+; The DAOPHOT method is more robust (e.g. two different sources will not merge)
+; especially in a package where the centroid will be subsequently be 
+; redetermined using PSF fitting.   However, it is less accurate, and introduces
+; biases in the centroid histogram.   The change here is the same made in the 
+; IRAF DAOFIND routine (see 
+; http://iraf.net/article.php?story=7211&query=daofind )
+;    
+
+ sd = total(temp*ywt,2)
+
+ sumgd = total(wt*sgy*sd)
+ sumd = total(wt*sd)
+ sddgdx = total(wt*sd*dgdx)
+
+ hx = (sumgd - sumgx*sumd/p) / (sumgsqy - sumgx^2/p)
+
+; HX is the height of the best-fitting marginal Gaussian.   If this is not
+; positive then the centroid does not make sense 
+
+  if (hx LE 0) then begin
+    	badcntrd = badcntrd + 1
+	 goto, REJECT
+  endif
+
+ skylvl = (sumd - hx*sumgx)/p
+ dx = (sgdgdx - (sddgdx-sdgdx*(hx*sumgx + skylvl*p)))/(hx*sdgdxs/sigsq)
+ if abs(dx) GE nhalf then begin 
+	badcntrd = badcntrd + 1
+	 goto, REJECT
+  endif
+
+ xcen = ix[i] + dx    ;X centroid in original array
+
+; Find Y centroid                 
+
+ sd = total(temp*xwt,1)
+ 
+ sumgd = total(wt*sgx*sd)
+ sumd = total(wt*sd)
+
+ sddgdy = total(wt*sd*dgdy)
+
+ hy = (sumgd - sumgy*sumd/p) / (sumgsqx - sumgy^2/p)
+
+  if (hy LE 0) then begin
+	badcntrd = badcntrd + 1
+	 goto, REJECT
+  endif
+
+ skylvl = (sumd - hy*sumgy)/p
+ dy = (sgdgdy - (sddgdy-sdgdy*(hy*sumgy + skylvl*p)))/(hy*sdgdys/sigsq)
+ if abs(dy) GE nhalf then begin 
+	badcntrd = badcntrd + 1
+	 goto, REJECT
+  endif
+      
+ ycen = iy[i] +dy    ;Y centroid in original array
+ 
+
+;  This star has met all selection criteria.  Print out and save results
+
+   if monitor then $
+      print,FORM = '(12x,i5,2f7.1,f9.1,2f9.2)', $ 
+            nstar, xcen, ycen, d, sharp1, around
+
+   if (npar GE 2) or (doprint) then begin
+              x[nstar] = xcen & y[nstar] = ycen
+   endif
+
+   if ( npar GE 4 ) or (doprint) then begin
+	flux[nstar] = d & sharp[nstar] = sharp1 & roundness[nstar] = around
+   endif
+   
+   nstar = nstar+1
+
+REJECT: 
+ endfor
+
+ nstar = nstar-1		;NSTAR is now the index of last star found
+
+ if doprint then begin
+  printf,lun,' No. of sources rejected by SHARPNESS criteria',badsharp
+  printf,lun,' No. of sources rejected by ROUNDNESS criteria',badround
+  printf,lun,' No. of sources rejected by CENTROID  criteria',badcntrd
+ endif
+ 
+if (not SILENT) and (MONITOR) then begin 
+  print,' No. of sources rejected by SHARPNESS criteria',badsharp
+  print,' No. of sources rejected by ROUNDNESS criteria',badround
+  print,' No. of sources rejected by CENTROID  criteria',badcntrd
+endif 
+
+  if nstar LT 0 then return               ;Any stars found?
+
+  if (npar GE 2) or (doprint) then begin
+	x=x[0:nstar]  & y = y[0:nstar]
+  endif
+
+  if (npar GE 4) or (doprint) then begin
+	flux= flux[0:nstar] & sharp=sharp[0:nstar]  
+        roundness = roundness[0:nstar]
+  endif
+
+ if doprint then begin                
+   printf,lun, $
+      format = '(/8x,a)','     STAR       X       Y     FLUX     SHARP    ROUND'
+	for i = 0L, nstar do $
+	   printf,lun,format='(12x,i5,2f8.2,f9.1,2f9.2)', $
+	              i+1, x[i], y[i], flux[i], sharp[i], roundness[i]
+        free_lun, lun
+ endif
+
+FINISH:
+
+ if SILENT or (not MONITOR) then return
+
+ print,form='(A,F8.1)',' Threshold above background for this pass was',hmin
+ ans = ''
+ read,'Enter new threshold or [RETURN] to exit: ',ans
+ ans = getopt(ans,'F')              
+ if ans GT 0. then begin
+       hmin = ans
+       goto, SEARCH   
+ endif
+
+ return                                      
+ end
diff --git a/Code/script_idl_mv/astrolib/find_all_dir.pro b/Code/script_idl_mv/astrolib/find_all_dir.pro
new file mode 100644
index 0000000000000000000000000000000000000000..61ce95878f140670f3cf8f124bf4eeb0ae0a6c08
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/find_all_dir.pro
@@ -0,0 +1,202 @@
+        FUNCTION FIND_ALL_DIR, PATH, PATH_FORMAT=PATH_FORMAT,   $
+                PLUS_REQUIRED=PLUS_REQUIRED, RESET=RESET
+;+
+; NAME:
+;       FIND_ALL_DIR()
+; PURPOSE:
+;       Finds all directories under a specified directory.
+; EXPLANATION:
+;       This routine finds all the directories in a directory tree when the
+;       root of the tree is specified.  This provides the same functionality as
+;       having a directory with a plus in front of it in the environment
+;       variable IDL_PATH.
+;
+; CALLING SEQUENCE:
+;       Result = FIND_ALL_DIR( PATH )
+;
+;               PATHS = FIND_ALL_DIR('+mypath', /PATH_FORMAT)
+;               PATHS = FIND_ALL_DIR('+mypath1:+mypath2')
+;
+; INPUTS:
+;       PATH    = The path specification for the top directory in the tree.
+;               Optionally this may begin with the '+' character but the action
+;               is the same unless the PLUS_REQUIRED keyword is set.
+;
+;               One can also path a series of directories separated
+;               by the correct character ("," for VMS, ":" for Unix)
+;
+; OUTPUTS:
+;       The result of the function is a list of directories starting from the
+;       top directory passed and working downward from there.   Normally, this
+;       will be a string array with one directory per array element, but if
+;       the PATH_FORMAT keyword is set, then a single string will be returned,
+;       in the correct format to be incorporated into !PATH.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       PATH_FORMAT     = If set, then a single string is returned, in
+;                                 the format of !PATH.
+;
+;       PLUS_REQUIRED   = If set, then a leading plus sign is required
+;                       in order to expand out a directory tree.
+;                       This is especially useful if the input is a
+;                       series of directories, where some components
+;                       should be expanded, but others shouldn't.
+;
+;       RESET   = Often FIND_ALL_DIR is used with logical names.  It
+;               can be rather slow to search through these subdirectories.
+;               The /RESET keyword can be used to redefine an environment
+;               variable so that subsequent calls don't need to look for the
+;               subdirectories.
+;
+;               To use /RESET, the PATH parameter must contain the name of a
+;               *single* environment variable.  For example
+;
+;                               setenv,'FITS_DATA=+/datadisk/fits'
+;                               dir = find_all_dir('FITS_DATA',/reset,/plus)
+;
+;               The /RESET keyword is usually combined with /PLUS_REQUIRED.
+;
+; PROCEDURE CALLS:
+;       DEF_DIRLIST, FIND_WITH_DEF(), BREAK_PATH()
+;
+; RESTRICTIONS:
+;       PATH must point to a directory that actually exists.
+;
+; REVISION HISTORY:
+;               Version 11, Zarro (SM&A/GSFC), 23-March-00
+;                       Removed all calls to IS_DIR
+;               Version 12, William Thompson, GSFC, 02-Feb-2001
+;                       In Windows, use built-in expand_path if able.
+;               Version 13, William Thompson, GSFC, 23-Apr-2002
+;                       Follow logical links in Unix
+;                       (Suggested by Pascal Saint-Hilaire)
+;               Version 14, Zarro (EER/GSFC), 26-Oct-2002
+;                       Saved/restored current directory to protect against
+;                       often mysterious directory changes caused by 
+;                       spawning FIND in Unix
+;               Version 15, William Thompson, GSFC, 9-Feb-2004
+;                       Resolve environment variables in Windows.
+;
+; Version     : Version 16 W. Landsman GSFC Sep 2006
+;                        Remove VMS support
+;-
+;
+        ON_ERROR, 2
+        compile_opt idl2
+;
+        IF N_PARAMS() NE 1 THEN MESSAGE,        $
+                'Syntax:  Result = FIND_ALL_DIR( PATH )'
+
+;-- save current directory
+
+   cd,current=current
+
+;
+;  If more than one directory was passed, then call this routine reiteratively.
+;  Then skip directly to the test for the PATH_FORMAT keyword.
+;
+        PATHS = BREAK_PATH(PATH, /NOCURRENT)
+        IF N_ELEMENTS(PATHS) GT 1 THEN BEGIN
+                DIRECTORIES = FIND_ALL_DIR(PATHS[0],    $
+                        PLUS_REQUIRED=PLUS_REQUIRED)
+                FOR I = 1,N_ELEMENTS(PATHS)-1 DO DIRECTORIES =  $
+                        [DIRECTORIES, FIND_ALL_DIR(PATHS[I],    $
+                                PLUS_REQUIRED=PLUS_REQUIRED)]
+                GOTO, TEST_FORMAT
+        ENDIF
+;
+;  Test to see if the first character is a plus sign.  If it is, then remove
+;  it.  If it isn't, and PLUS_REQUIRED is set, then remove any trailing '/'
+;  character and skip to the end.
+;
+        DIR = PATHS[0]
+        IF STRMID(DIR,0,1) EQ '+' THEN BEGIN
+                DIR = STRMID(DIR,1,STRLEN(DIR)-1)
+        END ELSE IF KEYWORD_SET(PLUS_REQUIRED) THEN BEGIN
+                DIRECTORIES = PATH
+                IF STRMID(PATH,STRLEN(PATH)-1,1) EQ '/' THEN    $
+                        DIRECTORIES = STRMID(PATH,0,STRLEN(PATH)-1)
+                GOTO, TEST_FORMAT
+        ENDIF
+;
+;  For windows,  use the built-in EXPAND_PATH program.   However, first 
+;  resolve any environment variables.
+;
+        IF !VERSION.OS_FAMILY EQ 'Windows' THEN BEGIN
+                WHILE STRMID(DIR,0,1) EQ '$' DO BEGIN
+                    FSLASH = STRPOS(DIR,'/')
+                    IF FSLASH LT 1 THEN FSLASH = STRLEN(DIR)
+                    BSLASH = STRPOS(DIR,'/')
+                    IF BSLASH LT 1 THEN BSLASH = STRLEN(DIR)
+                    SLASH = FSLASH < BSLASH
+                    TEST = STRMID(DIR,1,SLASH-1)
+                    DIR = GETENV(TEST) + STRMID(DIR,SLASH,STRLEN(DIR)-SLASH)
+                ENDWHILE
+                TEMP = DIR
+                TEST = STRMID(TEMP, STRLEN(TEMP)-1, 1)
+                IF (TEST EQ '/') OR (TEST EQ '\') THEN  $
+                      TEMP = STRMID(TEMP,0,STRLEN(TEMP)-1)
+                DIRECTORIES = EXPAND_PATH('+' + TEMP, /ALL, /ARRAY)
+;
+;  On Unix machines spawn the Bourne shell command 'find'.  First, if the
+;  directory name starts with a dollar sign, then try to interpret the
+;  following environment variable.  If the result is the null string, then
+;  signal an error.
+;
+        END ELSE BEGIN
+                IF STRMID(DIR,0,1) EQ '$' THEN BEGIN
+                    SLASH = STRPOS(DIR,'/')
+                    IF SLASH LT 0 THEN SLASH = STRLEN(DIR)
+                    EVAR = GETENV(STRMID(DIR,1,SLASH-1))
+                    IF SLASH EQ STRLEN(DIR) THEN DIR = EVAR ELSE        $
+                            DIR = EVAR + STRMID(DIR,SLASH,STRLEN(DIR)-SLASH)
+                ENDIF
+;               IF IS_DIR(DIR) NE 1 THEN MESSAGE,       $
+;                       'A valid directory must be passed'
+                IF STRMID(DIR,STRLEN(DIR)-1,1) NE '/' THEN DIR = DIR + '/'
+                SPAWN,'find ' + DIR + ' -follow -type d -print | sort -', $
+                        DIRECTORIES, /SH
+;
+;  Remove any trailing slash character from the first directory.
+;
+                TEMP = DIRECTORIES[0]
+                IF STRMID(TEMP,STRLEN(TEMP)-1,1) EQ '/' THEN    $
+                        DIRECTORIES[0] = STRMID(TEMP,0,STRLEN(TEMP)-1)
+        ENDELSE
+;
+;  Reformat the string array into a single string, with the correct separator.
+;  If the PATH_FORMAT keyword was set, then this string will be used.  Also use
+;  it when the RESET keyword was passed.
+;
+TEST_FORMAT:
+        DIR = DIRECTORIES[0]
+        CASE !VERSION.OS_FAMILY OF
+                'Windows':  SEP = ';'
+                'MacOS': Sep = ','
+                ELSE:  SEP = ':'
+        ENDCASE
+        FOR I = 1,N_ELEMENTS(DIRECTORIES)-1 DO DIR = DIR + SEP + DIRECTORIES[I]
+;
+;  If the RESET keyword is set, and the PATH variable contains a *single*
+;  environment variable, then call SETENV to redefine the environment variable.
+;  If the string starts with a $, then try it both with and without the $.
+;
+        IF KEYWORD_SET(RESET) THEN BEGIN
+                EVAR = PATH
+                TEST = GETENV(EVAR)
+                IF TEST EQ '' THEN IF STRMID(EVAR,0,1) EQ '$' THEN BEGIN
+                        EVAR = STRMID(EVAR,1,STRLEN(EVAR)-1)
+                        TEST = GETENV(EVAR)
+                ENDIF
+                IF (TEST NE '') AND (TEST NE PATH) AND (DIR NE PATH) THEN $
+                        SETENV, STRTRIM(EVAR,2) + '=' + $
+			STRTRIM(STRJOIN(DIR,':'),2)
+        ENDIF
+;
+;-- restore current directory
+
+        cd,current
+
+        IF KEYWORD_SET(PATH_FORMAT) THEN RETURN, DIR ELSE RETURN, DIRECTORIES
+;
+        END
diff --git a/Code/script_idl_mv/astrolib/find_with_def.pro b/Code/script_idl_mv/astrolib/find_with_def.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1fa4ade0328f90792f2146c821d4a973e072f825
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/find_with_def.pro
@@ -0,0 +1,153 @@
+        FUNCTION FIND_WITH_DEF, FILENAME, PATHS, EXTENSIONS,    $
+                NOCURRENT=NOCURRENT, RESET=RESET
+;+
+; NAME: 
+;     FIND_WITH_DEF()    
+; PURPOSE: 
+;     Searches for files with a default path and extension. 
+; EXPLANATION:
+;     Finds files using default paths and extensions,   Using this routine
+;     together with environment variables allows an OS-independent approach
+;     to finding files.
+; CALLING SEQUENCE: 
+;     Result = FIND_WITH_DEF( FILENAME, PATHS  [, EXTENSIONS ] )
+;
+; INPUTS: 
+;     FILENAME   = Name of file to be searched for.  It may either be a
+;                    complete filename, or the path or extension could be left
+;                    off, in which case the routine will attempt to find the
+;                    file using the default paths and extensions.
+;
+;     PATHS      = One or more default paths to use in the search in case
+;                    FILENAME does not contain a path itself.  The individual
+;                    paths are separated by commas, although in UNIX, colons
+;                    can also be used.  In other words, PATHS has the same
+;                    format as !PATH, except that commas can be used as a
+;                    separator regardless of operating system.  The current
+;                    directory is always searched first, unless the keyword
+;                    NOCURRENT is set.
+;
+;                    A leading $ can be used in any path to signal that what
+;                    follows is an environmental variable, but the $ is not
+;                    necessary.  Environmental variables can themselves contain
+;                    multiple paths.
+;
+; OPTIONAL INPUTS: 
+;     EXTENSIONS = Scalar string giving one or more extensions to append to 
+;                  end of filename if the filename does not contain one (e.g. 
+;                   ".dat").  The period is optional.  Multiple extensions can 
+;                   be separated by commas or colons.
+; OUTPUTS: 
+;     The result of the function is the name of the file if successful, or
+;     the null string if unsuccessful.
+; OPTIONAL INPUT KEYWORDS: 
+;     NOCURRENT = If set, then the current directory is not searched.
+;
+;      RESET      = The FIND_WITH_DEF routine supports paths which are
+;                    preceeded with the plus sign to signal that all
+;                    subdirectories should also be searched.  Often this is
+;                    used with logical names.  It can be rather slow to search
+;                    through these subdirectories.  The /RESET keyword can be
+;                    used to redefine an environment variable so that
+;                    subsequent calls don't need to look for the
+;                    subdirectories.
+;
+;                    To use /RESET, the PATHS parameter must contain the name
+;                    of a *single* environment variable.  For example
+;
+;                     setenv,'FITS_DATA=+/datadisk/fits'
+;                     file = find_with_def('test.fits','FITS_DATA',/reset)
+;
+; EXAMPLE:
+;
+;       FILENAME = ''
+;       READ, 'File to open: ', FILENAME
+;       FILE = FIND_WITH_DEF( FILENAME, 'SERTS_DATA', '.fix' )
+;       IF FILE NE '' THEN ...
+;
+;
+; PROCEDURE CALLS: 
+;       BREAK_PATH(), FIND_ALL_DIR(), STR_SEP()
+; REVISION HISTORY: 
+;       Version 1, William Thompson, GSFC, 3 May 1993.
+;               Removed trailing / and : characters.
+;               Fixed bugs
+;               Allow for commas within values of logical names.
+;               Added keyword NOCURRENT.
+;               Changed to call BREAK_PATH
+;       Version 2, William Thompson, GSFC, 3 November 1994
+;               Made EXTENSIONS optional.
+;       Version 3, William Thompson, GSFC, 30 April 1996
+;               Call FIND_ALL_DIR to resolve any plus signs.
+;       Version 4, S.V. Haugan, UiO, 5 June 1996
+;               Using OPENR,..,ERROR=ERROR to avoid an IDL 3.6
+;               internal nesting error.
+;       Version 5, R.A. Schwartz, GSFC, 11 July 1996
+;               Use SPEC_DIR to interpret PATH under VMS
+;       Version 6, William Thompson, GSFC, 5 August 1996
+;               Took out call to SPEC_DIR (i.e., reverted to version 4).  The
+;               use of SPEC_DIR was required to support logical names defined
+;               via SETLOG,/CONFINE.  However, it conflicted with the ability
+;               to use logical names with multiple values.  Removing the
+;               /CONFINE made it unnecessary to call SPEC_DIR in this routine.
+;       Version 7, William Thompson, GSFC, 6 August 1996
+;               Added keyword RESET
+;       Converted to IDL V5.0   W. Landsman   October 1997
+;       Use STRTRIM instead of TRIM,   W. Landsman   November 1998
+;       Use STRSPLIT instead of STR_SEP  W. Landsman  July 2002
+;-
+;
+        ON_ERROR, 2
+;
+;  Check the number of parameters:
+;
+        IF N_PARAMS() LT 2 THEN MESSAGE, 'Syntax:  Result = ' + $
+                'FIND_WITH_DEF(FILENAME, PATHS [, EXTENSIONS])'
+;
+;  If there are any plus signs, then expand them.
+;
+        PATH = FIND_ALL_DIR(PATHS, /PLUS_REQUIRED, /PATH, RESET=RESET)
+;
+;  Reformat PATHS into an array.  The first element is the null string.
+;
+        PATH = BREAK_PATH(PATH)
+;
+;  If NOCURRENT was set, then remove the first (blank) entry from the PATH
+;  array.
+;
+        IF KEYWORD_SET(NOCURRENT) THEN PATH = PATH[1:*]
+;
+;  Reformat EXTENSIONS into an array.  The first element is the null string.
+;
+       EXT = '' 
+       IF N_PARAMS() EQ 3 THEN $
+            EXT = ['',STRSPLIT(EXTENSIONS,',:',/EXTRACT)] 
+;
+;  Make sure that the extensions begin with a period.
+;
+        FOR I = 0,N_ELEMENTS(EXT)-1 DO IF EXT[I] NE '' THEN     $
+                IF STRMID(EXT[I],0,1) NE '.' THEN EXT[I] = '.' + EXT[I]
+;
+;  Set up variables used by the loops below.
+;
+        I_PATH = -1
+        GET_LUN, UNIT
+	FNAME = STRTRIM(FILENAME,2) + EXT
+;
+;  Step through each of the paths.
+;
+        FOR I_PATH = 0, N_ELEMENTS(PATH)- 1 DO BEGIN 
+;
+;  If the file is found then terminate the loop and clean up.
+;
+        FILE = FILE_SEARCH(PATH[I_PATH] + FNAME, COUNT = COUNT)
+        IF COUNT GT 0 THEN BREAK
+        ENDFOR
+;
+;  Otherwise, we jump directly to here when we find a file.
+;
+DONE:
+        FREE_LUN, UNIT
+	!ERR = COUNT
+        RETURN, FILE[0]
+        END
diff --git a/Code/script_idl_mv/astrolib/findpro.pro b/Code/script_idl_mv/astrolib/findpro.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7f00a89647086f9064d0e511a3ac2825170e3a67
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/findpro.pro
@@ -0,0 +1,173 @@
+pro FindPro, Proc_Name, NoPrint=NoPrint, DirList=DirList, ProList=ProList
+;+
+; NAME:
+;     FINDPRO
+; PURPOSE:
+;     Find all locations of a procedure in the IDL !PATH
+; EXPLANATION:
+;     FINDPRO searces for the procedure name (as a .pro or a .sav file) in all 
+;     IDL libraries or directories given in the !PATH system variable.    This
+;     differs from the intrinsic FILE_WHICH() function which only finds the 
+;     first occurence of the procedure name.
+;               
+; CALLING SEQUENCE:
+;    FINDPRO, [ Proc_Name, /NoPrint, DirList = , ProList = ]
+;
+; OPTIONAL INPUT:
+;     Proc_Name - Character string giving the name of the IDL procedure or 
+;             function. Do not include the ".pro" extension. If Proc_Name is
+;             omitted, the program will prompt for PROC_NAME.  "*" wildcards
+;             are permitted.
+;
+; OPTIONAL KEYWORD INPUT:
+;     /NoPrint - if set, then the file's path is not printed on the screen and
+;             absolutely no error messages are printed on the screen.  If not
+;             set, then - since the MESSAGE routine is used - error messages 
+;             will be printed but the printing of informational messages
+;             depends on the value of the !Quiet variable.
+;
+; OPTIONAL KEYWORD OUTPUTS:
+;     DirList - The directories in which the file is located are returned in
+;             the keyword as a string array.
+;             If the procedure is an intrinsic IDL procedure, then the 
+;             value of DirList = ['INTRINSIC'].
+;             If the procedure is not found, the value of DirList = [''].
+;     ProList - The list (full pathnames) of procedures found.  Useful if you
+;             are looking for the name of a procedure using wildcards.
+;
+;     The order of the names in DirList and ProList is identical to the order
+;     in which the procedure name appears in the !PATH
+; PROCEDURE:
+;     The system variable !PATH is parsed using EXPAND_PATH into individual 
+;     directories.  FILE_SEARCH() is used to search the directories for
+;     the procedure name.  If not found in !PATH, then the name is compared 
+;     with the list of intrinsic IDL procedures given by the ROUTINE_INFO()
+;     function. 
+;
+; EXAMPLE:
+;     (1) Find the procedure CURVEFIT.  Assume for this example that the user
+;     also has a copy of the curvefit.pro procedure in her home directory
+;     on a Unix machine.
+;
+;       IDL> findpro, 'curvefit', DIRLIST=DirList
+;       Procedure curvefit.pro found in directory  /home/user/.
+;       Procedure curvefit.pro found in directory  /software/IDL/idl82/lib/
+;       IDL> help, DirList
+;       DIRLIST         STRING    = Array(2) 
+;       IDL> help, DirList[0], DirList[1]
+;       <Expression>    STRING    = '/home/user'
+;       <Expression>    STRING    = '/software/IDL/idl82/lib/' 
+;
+;     (2) Find all procedures in one's !path containing the characters "zoom" 
+;
+;       IDL> findpro,'*zoom*'
+; RESTRICTIONS:
+;       User will be unable to find a path for a native IDL function
+;       or procedure, or for a FORTRAN or C routine added with CALL_EXTERNAL.
+;       Remember that Unix is case sensitive, and most procedures will be in
+;       lower case.
+; PROCEDURES USED:
+;       FDECOMP   -- Decompose file name
+;
+; REVISION HISTORY:
+;       Based on code extracted from the GETPRO procedure, J. Parker 1994
+;       Use the intrinsic EXPAND_PATH function    W. Landsman Nov. 1994
+;       Use ROUTINE_NAMES() to check for intrinsic procs   W. Landsman Jul 95
+;       Added Macintosh, WINDOWS compatibility    W. Landsman   Sep. 95
+;       Removed spurious first element in PROLIST  W. Landsman  March 1997
+;       Don't include duplicate directories  in !PATH  WL   May 1997
+;       Use ROUTINE_INFO instead of undocumented ROUTINE_NAMES W.L. October 1998
+;       Also check for save sets   W. Landsman  October 1999 
+;       Force lower case check for VMS  W. Landsman January 2000 
+;       Only return .pro or .sav files in PROLIST   W. Landsman  January 2002 
+;       Force lower case check for .pro and .sav    D. Swain  September 2002 
+;       Use FILE_SEARCH() if V5.5 or later   W. Landsman June 2006
+;       Assume since V55, remove VMS support W. Landsman Sep. 2006
+;       Assume since V6.0, use file_basename() W.Landsman Feb 2009
+;       Specify whether an intrinsic function or procedure W.L.  Jan 2013
+;
+;-
+;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+ On_error,2                           ;Return to caller on error
+ compile_opt idl2
+
+ if (N_params() EQ 0) then begin      ;Prompt for procedure name?
+   Proc_Name = ' ' 
+   read,'Enter name of procedure for which you want the path: ',Proc_Name
+ endif else $
+   if (size(proc_name,/type) NE 7 ) && (N_elements(proc_name) NE 1) then $
+       message,'ERROR - First parameter (.pro name) must be a scalar string'
+
+ NoPrint = keyword_set(NoPrint)
+
+ Name = strtrim( file_basename(proc_name,'.pro'), 2 )  
+
+; Set up separate file and directory separators for current OS
+
+ psep = path_sep()
+
+ pathdir = expand_path(!PATH,/ARRAY, Count = N_dir)
+ cd,current = dir 
+
+; Remove duplicate directories  in !PATH but keep original order
+ path_dir = [dir]
+ for i = 0,N_dir -1 do begin
+      test = where(path_dir EQ pathdir[i], Ndup)
+      if Ndup EQ 0 then path_dir = [path_dir,pathdir[i]]
+ endfor
+ N_dir = N_elements(path_dir)
+
+; Use FILE_PATH() to search all directories for <name>.pro or <name>.sav files 
+
+   ProList = file_search(path_dir + psep + name + '.{pro,sav}', COUNT=Nfile) 
+    
+      if (Nfile ge 1) then begin                     ;Found by FILE_SEARCH?
+       fdecomp, ProList, ddisk,ddir,fname,ext
+       dirlist = ddisk + ddir
+       found = 1b
+       for j = 0,nfile-1 do begin
+          case strlowcase(ext[j]) of 
+	 'pro':  message,/Con,  NoPrint = NoPrint,/NoPrefix, /Noname, $
+                 'Procedure ' + fname[j] + ' found in directory  ' + dirlist[j]
+         'sav':  message,/Con,NoPrint = NoPrint,/NoPrefix, /Noname, $
+                'Save set ' + fname[j] + '.sav found in directory  ' + dirlist[j]
+	 endcase	  
+        endfor
+     endif  else begin     
+           
+
+; At this point !PATH has been searched.  If the procedure was not found
+; check if it is an intrinsic IDL procedure or function
+ 
+  funcnames = routine_info(/system,/func)
+  fcount = ~array_equal( funcnames NE strupcase(name), 1b )
+;  test = where ( funcnames EQ strupcase(name), fcount)    Slower method
+
+  funcnames = routine_info(/system)
+  pcount = ~array_equal( funcnames NE strupcase(name) , 1b) 
+;
+   
+   if (fcount EQ 0) && (pcount EQ 0) then begin
+        prolist = strarr(1)
+  	dirlist = strarr(1)
+       if ~NoPrint then begin
+         message, 'Procedure '+Name+' not found in a !PATH directory.', /CONT
+         message, 'Check your spelling or search individual directories.', /INF
+      endif
+   endif else begin 
+      DirList = ['INTRINSIC']
+      ProList = ['INTRINSIC']
+      if ~NoPrint then begin
+         if pcount NE 0 then $
+         message, 'Procedure ' + Name + ' is an intrinsic IDL procedure.', $
+	    /CONT else $
+	 message, 'Procedure ' + Name + ' is an intrinsic IDL function.',/CONT   
+         message, 'No path information available.', /INF
+      endif
+   endelse
+
+ endelse
+  
+ return
+ end   
diff --git a/Code/script_idl_mv/astrolib/fitexy.pro b/Code/script_idl_mv/astrolib/fitexy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5acf3127654b17b258ed6af112690bd1f5a33115
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fitexy.pro
@@ -0,0 +1,205 @@
+;+
+; NAME:
+;       FITEXY
+; PURPOSE:
+;       Best straight-line fit to data with errors in both coordinates
+; EXPLANATION:
+;       Linear Least-squares approximation in one-dimension (y = a + b*x),
+;       when both x and y data have errors   Users might be interested in 
+;       Michael Williams MPFITEXY routines which include a number of 
+;       enhancements to FITEXY. 
+;       ( http://user.astro.columbia.edu/~williams/mpfitexy/ )
+;               
+;
+; CALLING EXAMPLE:
+;       FITEXY, x, y, A, B, X_SIG= , Y_SIG= , [sigma_A_B, chi_sq, q, TOL=]
+;
+; INPUTS:
+;       x = array of values for independent variable.
+;       y = array of data values assumed to be linearly dependent on x.
+;
+; REQUIRED INPUT KEYWORDS:
+;       X_SIGMA = scalar or array specifying the standard deviation of x data.
+;       Y_SIGMA = scalar or array specifying the standard deviation of y data.
+;
+; OPTIONAL INPUT KEYWORD:
+;       TOLERANCE = desired accuracy of minimum & zero location, default=1.e-3.
+;
+; OUTPUTS:
+;       A_intercept = constant parameter result of linear fit,
+;       B_slope = slope parameter, so that:
+;                       ( A_intercept + B_slope * x ) approximates the y data.
+; OPTIONAL OUTPUT:
+;       sigma_A_B = two element array giving standard deviation of 
+;                A_intercept and B_slope parameters, respectively.
+;                The standard deviations are not meaningful if (i) the
+;                fit is poor (see parameter q), or (ii) b is so large that
+;                the data are consistent with a vertical (infinite b) line.
+;                If the data are consistent with *all* values of b, then
+;                sigma_A_B = [1e33,e33]  
+;       chi_sq = resulting minimum Chi-Square of Linear fit, scalar
+;       q - chi-sq probability, scalar (0-1) giving the probability that
+;              a correct model would give a value equal or larger than the
+;              observed chi squared.   A small value of q indicates a poor
+;              fit, perhaps because the errors are underestimated.   As 
+;              discussed by Tremaine et al. (2002, ApJ, 574, 740) an 
+;              underestimate of the errors (e.g. due to an intrinsic dispersion)
+;              can lead to a bias in the derived slope, and it may be worth
+;              enlarging the error bars to get a reduced chi_sq ~ 1
+;
+; COMMON:
+;       common fitexy, communicates the data for computation of chi-square.
+;
+; PROCEDURE CALLS:
+;       CHISQ_FITEXY()            ;Included in this file
+;       MINF_BRACKET, MINF_PARABOLIC, ZBRENT    ;In IDL Astronomy Library 
+;       MOMENT(), CHISQR_PDF()     ;In standard IDL distribution
+;
+; PROCEDURE:
+;       From "Numerical Recipes" column by Press and Teukolsky: 
+;       in "Computer in Physics",  May, 1992 Vol.6 No.3
+;       Also see the 2nd edition of the book "Numerical Recipes" by Press et al.
+;
+;       In order to avoid  problems with data sets where X and Y are of very 
+;       different order of magnitude the data are normalized before the fitting
+;       process is started.     The following normalization is used:
+;       xx = (x - xm) / xs    and    sigx = x_sigma / xs    
+;                             where xm = MEAN(x) and xs = STDDEV(x)
+;       yy = (y - ym) / ys    and    sigy = y_sigma / ys    
+;                             where ym = MEAN(y) and ys = STDDEV(y)
+;
+;
+; MODIFICATION HISTORY:
+;       Written, Frank Varosi NASA/GSFC  September 1992.
+;       Now returns q rather than 1-q   W. Landsman  December 1992
+;       Use CHISQR_PDF, MOMENT instead of STDEV,CHI_SQR1 W. Landsman April 1998
+;       Fixed typo for initial guess of slope, this error was nearly
+;             always insignificant          W. Landsman   March 2000
+;       Normalize X,Y before calculation (from F. Holland) W. Landsman Nov 2006
+;-
+function chisq_fitexy, B_angle
+;
+; NAME:
+;       chisq_fitexy
+; PURPOSE:
+;       Function minimized by fitexy  (computes chi-square of linear fit).
+;       It is called by minimization procedures during execution of fitexy.
+; CALLING SEQUENCE:
+;               chisq = chisq_fitexy( B_angle )
+; INPUTS:
+;       B_angle = arc-tangent of B_slope of linear fit.
+; OUTPUTS:
+;       Result of function = chi_square - offs  (offs is in COMMON).
+; COMMON:
+;       common fitexy, communicates the data from pro fitexy.
+; PROCEDURE:
+;       From "Numerical Recipes" column: Computer in Physics Vol.6 No.3
+; MODIFICATION HISTORY:
+;       Written, Frank Varosi NASA/GSFC 1992.
+
+  common fitexy, xx, yy, sigx, sigy, ww, Ai, offs
+
+        B_slope = tan( B_angle )
+        ww = 1/( ( (B_slope * sigx)^2 + sigy^2 ) > 1.e-30 )
+        if N_elements( ww ) EQ 1 then sumw = ww * N_elements( xx ) $
+                                 else sumw = total( ww )
+        y_Bx = yy - B_slope * xx
+        Ai = total( ww * y_Bx )/sumw
+
+return, total( ww * (y_Bx - Ai)^2 ) - offs
+end
+;-------------------------------------------------------------------------------
+pro fitexy, x, y, A_intercept, B_slope, sigma_A_B, chi_sq, q, TOLERANCE=Tol, $
+                                        X_SIGMA=x_sigma, Y_SIGMA=y_sigma
+  compile_opt idl2					
+  common fitexy, xx, yy, sigx, sigy, ww, Ai, offs
+
+  if N_params() LT 4 then begin
+     print,'Syntax -  fitexy, x, y, A, B, X_SIG=sigx, Y_SIG=sigy,' 
+     print,'                  [sigma_A_B, chi_sq, q, TOLERANCE = ]'
+     return
+  endif
+  
+; Normalize data before running fitexy
+
+  xm = (MOMENT(x, SDEV = xs, /DOUBLE))[0]
+  ym = (MOMENT(y, SDEV = ys, /DOUBLE))[0]
+  xx = (x - xm) / xs
+  yy = (y - ym) / ys
+  sigx = x_sigma / xs
+  sigy = y_sigma / ys
+ 
+   
+;Compute first guess for B_slope using standard 1-D Linear Least-squares fit,
+; where the non-linear term involving errors in x are ignored.
+; (note that Tx is a transform to reduce roundoff errors)
+
+        ww = sigx^2 + sigy^2
+        if N_elements( ww ) EQ 1 then sumw = ww * N_elements( xx ) $
+                                 else sumw = total( ww )
+        Sx = total( xx * ww )
+        Tx = xx - Sx/sumw
+        B = total( ww * yy * Tx ) / total( ww * Tx^2 )
+
+;Find the minimum chi-sq while including the non-linear term (B * sigx)^2
+; involving variance in x data (computed by function chisq_fitexy):
+; using minf_bracket (=MNBRAK) and minf_parabolic (=BRENT)
+        offs = 0
+        ang = [ 0, atan( B ), 1.571 ]
+        chi = fltarr( 3 )
+        for j=0,2 do chi[j] = chisq_fitexy( ang[j] )    ;this is for later...
+        if N_elements( Tol ) NE 1 then Tol=1.e-3
+        a0 = ang[0]
+        a1 = ang[1]
+        minf_bracket, a0,a1,a2, c0,c1,c2, FUNC="chisq_fitexy"
+        minf_parabolic, a0,a1,a2, Bang, chi_sq, FUNC="chisq_fitexy", TOL=Tol
+
+        if N_params() EQ 7 then q = 1 - chisqr_pdf( chi_sq, N_elements(x) - 2 )
+        A_intercept = Ai        ;computed in function chisq_fitexy
+        ang = [a0,a1,a2,ang]
+        chi = [c0,c1,c2,chi]
+
+;Now compute the variances of estimated parameters,
+; by finding roots of ( (chi_sq + 1) - chisq_fitexy ).
+;Note: ww, Ai are computed in function chisq_fitexy.
+
+        offs = chi_sq + 1
+        wc = where( chi GT offs, nc )
+
+        if (nc GT 0) then begin
+
+                angw = [ang[wc]]
+                d1 = abs( angw - Bang ) MOD !PI
+                d2 = !PI - d1
+                wa = where( angw LT Bang, na )
+
+                if (na GT 0) then begin
+                        d = d1[wa]
+                        d1[wa] = d2[wa]
+                        d2[wa] = d
+                   endif
+
+                Bmax = zbrent( Bang,Bang+max(d1),F="chisq_fitexy",T=Tol ) -Bang
+                Amax = Ai - A_intercept
+                Bmin = zbrent( Bang,Bang-min(d2),F="chisq_fitexy",T=Tol ) -Bang
+                Amin = Ai - A_intercept
+
+                if N_elements( ww ) EQ 1 then r2 = 2/( ww * N_elements( x ) ) $
+                                         else r2 = 2/total( ww )
+
+                sigma_A_B = [ Amin^2 + Amax^2 + r2 , Bmin^2 + Bmax^2 ]
+                sig_A_B = sqrt( sigma_A_B/2 ) / ([1,cos(Bang)^2])
+
+          endif 
+
+;Finally, transform parameters back to orignal units.
+
+
+        B_slope = tan( Bang ) *ys /xs
+        A_intercept = A_intercept*ys - tan(Bang) * ys / xs *xm + ym
+        if Nc GT 0 then sigma_A_B = [SQRT( (sig_A_B[0] * ys)^2 +  $
+                    (sig_A_B[1] * ys / xs * xm)^2 ), sig_A_B[1] * ys / xs] $
+                else sigma_A_B = [1.e33,1.e33]    
+
+return
+end
diff --git a/Code/script_idl_mv/astrolib/fits_add_checksum.pro b/Code/script_idl_mv/astrolib/fits_add_checksum.pro
new file mode 100644
index 0000000000000000000000000000000000000000..71492c776ee6669228e87613da9f2d751cf339ba
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_add_checksum.pro
@@ -0,0 +1,104 @@
+pro fits_add_checksum, hdr, im, no_timestamp = no_timestamp, $
+              FROM_IEEE=from_IEEE
+;+
+; NAME:
+;    FITS_ADD_CHECKSUM
+; PURPOSE:
+;    Add or update the CHECKSUM and DATASUM keywords in a FITS header
+; EXPLANATION: 
+;     Follows the May 2002 version of the FITS checksum proposal at 
+;     http://fits.gsfc.nasa.gov/registry/checksum.html 
+; CALLING SEQUENCE:
+;     FITS_ADD_CHECKSUM, Hdr, [ Data, /No_TIMESTAMP, /FROM_IEEE ]
+; INPUT-OUTPUT:
+;     Hdr - FITS header (string array), it will be updated with new 
+;           (or modified) CHECKSUM and DATASUM keywords 
+; OPTIONAL INPUT:
+;     Data - data array associated with the FITS header.   If not supplied, or
+;           set to a scalar, then the program checks whether there is a 
+;           DATASUM keyword already in the FITS header containing the 32bit
+;           checksum for the data.  If there is no such keyword then there 
+;           assumed to be no data array associated with the FITS header.
+; OPTIONAL INPUT KEYWORDS:
+;    /FROM_IEEE - If this keyword is set, then the input is assumed to be in 
+;             big endian format (e.g. an untranslated FITS array).    This 
+;             keyword only has an effect on little endian machines (e.g. 
+;             a Linux box).
+;    /No_TIMESTAMP - If set, then a time stamp is not included in the comment
+;             field of the CHECKSUM and DATASUM keywords.   Unless the 
+;             /No_TIMESTAMP keyword is set, repeated calls to FITS_ADD_CHECKSUM
+;             with the same header and data will yield different values of 
+;             CHECKSUM (as the date stamp always changes).   However, use of the
+;             date stamp is recommended in the checksum proposal. 
+; PROCEDURES USED:
+;     CHECKSUM32, FITS_ASCII_ENCODE(), GET_DATE, SXADDPAR, SXPAR()
+; REVISION HISTORY:
+;     W. Landsman    SSAI    December 2002
+;     Fix problem with images with a multiple of 2880 bytes.  W.L. May 2008
+;     Avoid conversion error when DATASUM is an empty string  W.L.  June 2008
+;     Don't update DATASUM if not already present and no data array supplied 
+;                       W.L. July 2008 
+;     Make sure input header array has 80 chars/line  W.L. Aug 2009
+;-
+ On_error,2
+ compile_opt idl2
+ 
+ if N_params() EQ 0 then begin 
+     print,'Syntax - FITS_ADD_CHECKSUM, Hdr, Data, /No_TIMESTAMP, /FROM_IEEE'
+     return
+ endif
+
+ datasum = sxpar(hdr,'DATASUM', Count = N_DATASUM)
+ Nim = N_elements(im)
+ datasum_update = 1b
+ if Nim GT 1 then begin
+     checksum32,im, dsum,FROM_IEEE = from_IEEE
+     remain = Nim mod 2880
+     if remain GT 0 then begin
+         exten = sxpar( hdr, 'XTENSION', Count = N_exten)
+         if N_exten GT 0 then if exten EQ 'TABLE   ' then $
+                 checksum32,[dsum,replicate(32b,2880-remain)],dsum
+    endif
+    sdsum = strtrim(dsum,2)
+    dsum_exist= 1b
+ endif else begin 
+        if N_datasum EQ 0 then begin      ;Don't update DATASUM keyword 
+	      datasum_update = 0b     
+ 	      sdsum = '         0' 
+	 endif else begin
+	   if strtrim(datasum,2) EQ '' then dsum=0 else dsum = ulong(datasum)
+           sdsum = strtrim(dsum,2)
+       endelse   
+ endelse 
+ 
+ if keyword_set(no_timestamp) then tm = '' else Get_date,tm,/timetag
+
+; Do the Checksum keywords already exist?
+
+  if N_DATASUM GT 0 then verb = 'updated ' else verb = 'created '
+  if datasum_update then sxaddpar,hdr,'DATASUM', sdsum,  $
+    ' data unit checksum ' + verb + tm
+
+ test = sxpar(hdr,'CHECKSUM', Count = N_CHECKSUM)
+ if N_CHECKSUM GT 0 then verb = 'updated ' else verb = 'created '
+ sxaddpar,hdr,'CHECKSUM','0000000000000000', $
+       ' HDU checksum ' + verb + tm   ;Initialize CHECKSUM keyword
+;Make sure each line in header is 80 characters
+ if ~array_equal(strlen(hdr),80) then begin
+     n = N_elements(hdr)
+     bhdr = replicate(32b,80,n )
+     for i=0, n-1 do bhdr[0,i] = byte(hdr[i])
+ endif else bhdr = byte(hdr)
+
+ remain = N_elements(bhdr) mod 2880 
+ if remain  NE 0 then $
+       bhdr = [reform(bhdr,N_elements(bhdr)), replicate(32b, 2880 - remain) ]
+ checksum32,bhdr, hsum, /NoSAVE
+ if N_elements(dsum) GT 0 then checksum32, [dsum,hsum], hdusum $
+                        else hdusum = hsum
+ 
+ ch = FITS_ASCII_ENCODE(not hdusum) ;ASCII encode the complement of the checksum 
+ sxaddpar,hdr,'CHECKSUM',ch
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/fits_ascii_encode.pro b/Code/script_idl_mv/astrolib/fits_ascii_encode.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1fbb628c91264b4a04565226faf7c2850bbf7489
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_ascii_encode.pro
@@ -0,0 +1,68 @@
+function fits_ascii_encode, sum32
+;+ 
+; NAME:
+;    FITS_ASCII_ENCODE()
+; PURPOSE:
+;    Encode an unsigned longword as an ASCII string to insert in a FITS header
+; EXPLANATION:
+;     Follows the July 2007 version of the FITS checksum proposal at 
+;       http://fits.gsfc.nasa.gov/registry/checksum.html
+; CALLING SEQUENCE:
+;     result = FITS_ASCII_ENCODE( sum32)
+; INPUTS:
+;     sum32 - 32bit *unsigned longword* (e.g. as returned by CHECKSUM32)
+; RESULT:
+;     A 16 character scalar string suitable for the CHECKSUM keyword
+; EXAMPLE:
+;      A FITS header/data unit has a checksum of 868229149.  Encode the 
+;      complement of this value (3426738146) into an ASCII string
+;
+;      IDL> print,FITS_ASCII_ENCODE(3426738146U)
+;           ===> "hcHjjc9ghcEghc9g"
+;
+; METHOD:
+;      The 32bit value is interpreted as a sequence of 4 unsigned 8 bit 
+;      integers, and divided by 4.    Add an offset of 48b (ASCII '0'). 
+;      Remove non-alphanumeric ASCII characters (byte values 58-64 and 91-96)
+;      by simultaneously incrementing and decrementing the values in pairs.
+;      Cyclicly shift the string one place to the right.
+;                  
+; REVISION HISTORY:
+;     Written  W. Landsman  SSAI              December 2002
+;     Use V6.0 notation  W.L.                 August 2013
+;-
+ if N_Params() LT 1 then begin
+      print,'Syntax -  result = FITS_ASCII_ENCODE( sum32)'
+      return,'0'
+ endif
+ 
+; Non-alphanumeric ASCII characters  
+ exclude = [58b,59b,60b,61b,62b,63b,64b,91b,92b,93b,94b,95b,96b]
+ ch = bytarr(16)
+ t = byte(sum32,0,4)
+ byteorder,t,/htonl
+ quot = t/4 + 48b
+ for i=0,12,4 do ch[i] = quot
+
+ remain = t mod 4
+ ch[0] = ch[0:3] + remain    ;Insert the remainder in the first 4 bytes
+
+;Step through the 16 bytes, 8 at a time, removing nonalphanumeric  characters
+ repeat begin           
+ check = 0b
+  for j=0,1 do begin
+ il = j*8
+ for i=il,il+3 do begin
+  bad = where( (exclude EQ ch[i]) or (exclude Eq ch[i+4]) , Nbad) 
+   if Nbad GT 0 then begin
+       ch[i]++
+       ch[i+4]--
+       check=1b
+  endif
+ endfor
+ endfor
+ endrep until (check EQ 0b)
+
+  return, string( shift(ch,1))
+  end
+
diff --git a/Code/script_idl_mv/astrolib/fits_cd_fix.pro b/Code/script_idl_mv/astrolib/fits_cd_fix.pro
new file mode 100644
index 0000000000000000000000000000000000000000..40a5219a71555cdaba3045e86165fea85c64ac39
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_cd_fix.pro
@@ -0,0 +1,80 @@
+pro fits_cd_fix,hdr, REVERSE = reverse
+;+
+; NAME:
+;    FITS_CD_FIX
+;
+; PURPOSE:
+;    Update obsolete representations of the CD matrix in a FITS header   
+;
+; EXPLANATION:
+;    According the paper, "Representations of Celestial Coordinates in FITS"
+;    by Calabretta & Greisen (2002, A&A, 395, 1077, available at 
+;    http://fits.gsfc.nasa.gov/fits_wcs.html) the rotation of an image from 
+;    standard coordinates is represented by a coordinate description (CD) 
+;    matrix.    The standard representation of the CD matrix are PCn_m 
+;    keywords, but CDn_m keywords (which include the scale factors) are
+;    also allowed.    However, earliers drafts of the standard allowed the
+;    keywords forms CD00n00m and PC00n00m.      This procedure will convert
+;    FITS CD matrix keywords containing zeros into the standard forms 
+;    CDn_m and PCn_m containing only underscores.
+;
+; CALLING SEQUENCE:
+;    FITS_CD_FIX, Hdr
+;
+; INPUT-OUTPUT: 
+;       HDR - FITS header, 80 x N string array.   If the header does not
+;           contain 'CD00n00m' or 'PC00n00m' keywords then it is left 
+;           unmodified.  Otherwise, the keywords containing integers are
+;           replaced with those containing underscores.
+;   
+; OPTIONAL KEYWORD INPUT
+;      /REVERSE - this keyword does nothing, but is kept for compatibility with
+;            earlier versions.
+; PROCEDURES USED:
+;    SXADDPAR, SXDELPAR, SXPAR()
+; REVISION HISTORY:
+;    Written   W. Landsman             Feb 1990
+;    Major rewrite                     Feb 1994
+;    Converted to IDL V5.0   W. Landsman   September 1997
+;    Use double precision formatting of CD matrix   W. Landsman  April 2000
+;    Major rewrite to convert only to forms recognized by the Greisen
+;       & Calabretta standard   W. Landsman   July 2003
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 1 then begin
+        print,'Syntax - FITS_CD_FIX, hdr'
+        return
+ endif
+
+ cd00 = ['CD001001','CD001002','CD002001','CD002002']
+ pc00 = ['PC001001','PC001002','PC002001','PC002002']
+
+  cd_ = ['CD1_1','CD1_2','CD2_1','CD2_2']
+  pc_ = ['PC1_1','PC1_2','PC2_1','PC2_2']
+ 
+
+ for i= 0 ,3 do begin
+   pc = sxpar(hdr,pc00[i], COUNT = N)
+   if N GE 1 then begin
+        sxaddpar,hdr,pc_[i],pc,'',pc00[i]
+        sxdelpar,hdr,pc00[i]
+        if i EQ 0 then sxaddhist,'FITS_CD_FIX:' + strmid(systime(),4,20) + $
+                  ' PC00n00m keywords changed to PCn_m',hdr
+ endif  else begin
+      
+    cd = sxpar(hdr,cd00[i], COUNT = N )
+    if N GE 1 then begin
+        sxaddpar,hdr,cd_[i],cd,'',cd00[i]
+        sxdelpar,hdr,cd00[i]
+        if i EQ 0 then sxaddhist,'FITS_CD_FIX:' + strmid(systime(),4,20) + $
+                  ' CD00n00m keywords changed to CDn_m',hdr
+ endif
+ endelse 
+ endfor
+
+ 
+ return
+ end
+                                
diff --git a/Code/script_idl_mv/astrolib/fits_close.pro b/Code/script_idl_mv/astrolib/fits_close.pro
new file mode 100644
index 0000000000000000000000000000000000000000..627f4a12a25482773626d50d220bb7bbafea3376
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_close.pro
@@ -0,0 +1,66 @@
+pro fits_close,fcb,no_abort=no_abort,message=message
+;+
+; NAME:
+;      FITS_CLOSE
+;
+;*PURPOSE:
+;       Close a FITS data file
+;
+;*CATEGORY:
+;       INPUT/OUTPUT
+;
+;*CALLING SEQUENCE:
+;       FITS_CLOSE,fcb
+;
+;*INPUTS:
+;       FCB: FITS control block returned by FITS_OPEN.
+;
+;*KEYWORD PARAMETERS:
+;       /NO_ABORT: Set to return to calling program instead of a RETALL
+;               when an I/O error is encountered.  If set, the routine will
+;               return  a non-null string (containing the error message) in the
+;               keyword MESSAGE.   If /NO_ABORT not set, then FITS_CLOSE will 
+;               print the message and issue a RETALL
+;       MESSAGE = value: Output error message
+;       
+;*EXAMPLES:
+;       Open a FITS file, read some data, and close it with FITS_CLOSE
+;
+;               FITS_OPEN,'infile',fcb
+;               FITS_READ,fcb,data
+;               FITS_READ,fcb,moredata
+;               FITS_CLOSE,fcb
+;
+;*HISTORY:
+;       Written by:     D. Lindler      August, 1995
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Do nothing if fcb an invalid structure D. Schlegel/W. Landsman Oct. 2000
+;       Return Message='' for to signal normal operation W. Landsman Nov. 2000
+;-
+;----------------------------------------------------------------------------
+;
+; print calling sequence if no parameters supplied
+;
+        if N_params() lt 1 then begin
+                print,'Syntax -  FITS_CLOSE, fcb'
+                print,'KEYWORD PARAMETERS: /No_abort, message='
+                return
+        end
+;
+; close unit
+;
+        on_ioerror,ioerror
+        message = ''
+
+        sz_fcb = size(fcb)             ;Valid structure?
+        if sz_fcb[2] EQ 8 then free_lun,fcb.unit
+        return
+;
+; error exit (probably should never occur)
+;
+ioerror:
+        message = !error_state.msg
+         if keyword_set(no_abort) then return
+        message,' ERROR: '+message,/CON
+        retall
+end
diff --git a/Code/script_idl_mv/astrolib/fits_help.pro b/Code/script_idl_mv/astrolib/fits_help.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8bd193357747895bf8599f0ac7066621c623690f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_help.pro
@@ -0,0 +1,119 @@
+pro fits_help,file_or_fcb
+;+
+; NAME:
+;       FITS_HELP
+;
+; PURPOSE:
+;       To print a summary of the primary data units and extensions in a
+;       FITS file.
+;;
+; CALLING SEQUENCE:
+;       FITS_HELP,filename_or_fcb
+;
+; INPUTS:
+;       FILENAME_OR_FCB - name of the fits file or the FITS Control Block (FCB)
+;               structure returned by FITS_OPEN.     The  file name is allowed 
+;               to be gzip compressed (with a .gz  extension)
+;
+; OUTPUTS:
+;       A summary of the FITS file is printed.   For each extension, the values
+;       of the XTENSION, EXTNAME EXTVER EXTLEVEL BITPIX GCOUNT, PCOUNT NAXIS 
+;       and NAXIS* keywords are displayed. 
+; 
+;
+; EXAMPLES:
+;       FITS_HELP,'myfile.fits'
+;
+;       FITS_OPEN,'anotherfile.fits',fcb
+;       FITS_HELP,fcb
+;
+; PROCEDURES USED:
+;       FITS_OPEN, FITS_CLOSE
+; HISTORY:
+;       Written by:     D. Lindler      August, 1995
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Don't truncate EXTNAME values at 10 chars  W. Landsman Feb. 2005
+;       Use V6.0 notation W. Landsman Jan 2012
+;-
+;-----------------------------------------------------------------------------
+ compile_opt idl2
+;
+; print calling sequence
+;
+        if N_params() eq 0 then begin
+          print,'Syntax -  FITS_HELP,file_or_fcb'
+          return
+        endif
+;
+; Open file if file name is supplied
+;
+        fcbtype = size(file_or_fcb,/type) 
+        fcbsize = n_elements(file_or_fcb)
+        if (fcbsize ne 1) || ((fcbtype ne 7) && (fcbtype ne 8)) then begin
+                message, 'Invalid Filename or FCB supplied',/con
+                return
+        end
+
+        if fcbtype eq 7 then fits_open,file_or_fcb,fcb $
+                        else fcb = file_or_fcb
+                        
+; EXTNAME will always be displayed with a length of at least 10 characters
+; but allow for possibility that lengths might be longer than this 
+
+        maxlen = max(strlen(fcb.extname)) > 10 
+        if maxlen EQ 10 then space = '' else $
+            space = string(replicate(32b, maxlen -10))                  
+;
+; print headings
+;
+        print,' '
+        print,FCB.FILENAME
+        print,' '
+        print,'     XTENSION  EXTNAME  '+ space + $
+              'EXTVER EXTLEVEL BITPIX GCOUNT  PCOUNT NAXIS  NAXIS*'
+        print,' '
+;
+; loop on extensions
+;
+        for i=0,fcb.nextend do begin
+                st = string(i,'(I4)')
+;
+; xtension, extname, extver, extlevel (except for i=0)
+;
+                if i gt 0 then begin
+                        t = fcb.xtension[i]
+                        while strlen(t) lt 8 do t += ' '
+                        st +=  ' '+ strmid(t,0,8)
+                        t = fcb.extname[i]
+                        while strlen(t) lt maxlen do t += ' '
+                        st += ' '+ strmid(t,0,maxlen)               
+                        t = fcb.extver[i]
+                        if t eq 0 then st += '     ' $
+                                  else st += string(t,'(I5)')
+                        t = fcb.extlevel[i]
+                        if t eq 0 then st +=  '        ' $
+                                  else st += string(t,'(I8)')
+                end else st += '                                 ' + space
+;
+; bitpix, gcount, pcount, naxis
+;
+                st += string(fcb.bitpix[i],'(I6)')
+                st += string(fcb.gcount[i],'(I7)')
+                st += string(fcb.pcount[i],'(I7)')
+                st += string(fcb.naxis[i],'(I6)')
+;
+; naxis*
+;
+                st += '  '
+                if fcb.naxis[i] gt 0 then begin
+                    nax1 = fcb.naxis[i] - 1
+                    st += strjoin(strtrim(fcb.axis[0:nax1,i],2),' x ')
+                endif
+;
+; print the info
+;
+                print,st
+        end
+        if fcbtype eq 7 then fits_close,fcb
+return
+end
diff --git a/Code/script_idl_mv/astrolib/fits_info.pro b/Code/script_idl_mv/astrolib/fits_info.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c0746d4472b14697848ce728cb825903eae0145d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_info.pro
@@ -0,0 +1,348 @@
+pro fits_info, filename, SILENT=silent,TEXTOUT=textout, N_ext=n_ext, extname=extname
+;+
+; NAME:
+;     FITS_INFO
+; PURPOSE:
+;     Provide information about the contents of a FITS file
+; EXPLANATION:
+;     Information includes number of header records and size of data array.
+;     Applies to primary header and all extensions.    Information can be 
+;     printed at the terminal and/or stored in a common block
+;
+;     This routine is mostly obsolete, and better results can be usually be
+;     performed with FITS_HELP (for display) or FITS_OPEN (to read FITS 
+;     information into a structure)
+;
+; CALLING SEQUENCE:
+;     FITS_INFO, Filename, [ /SILENT , TEXTOUT = , N_ext =, EXTNAME= ]
+;
+; INPUT:
+;     Filename - Scalar string giving the name of the FITS file(s)
+;               Can include wildcards such as '*.fits', or regular expressions 
+;               allowed by the FILE_SEARCH() function.     One can also search 
+;               gzip compressed  FITS files, but their extension must
+;               end in .gz or .ftz.
+; OPTIONAL INPUT KEYWORDS:
+;     /SILENT - If set, then the display of the file description on the 
+;                terminal will be suppressed
+;
+;      TEXTOUT - specifies output device.
+;               textout=1        TERMINAL using /more option
+;               textout=2        TERMINAL without /more option
+;               textout=3        <program>.prt
+;               textout=4        laser.tmp
+;               textout=5        user must open file, see TEXTOPEN
+;               textout=7       append to existing <program.prt> file
+;               textout = filename (default extension of .prt)
+;
+;               If TEXTOUT is not supplied, then !TEXTOUT is used
+; OPTIONAL OUTPUT KEYWORDS:
+;       The following keyowrds are for use when only one file is processed
+;
+;       N_ext - Returns an integer scalar giving the number of extensions in
+;               the FITS file
+;       extname - returns a list containing the EXTNAME keywords for each
+;       		extension.
+;
+; COMMON BLOCKS
+;       DESCRIPTOR =  File descriptor string of the form N_hdrrec Naxis IDL_type
+;               Naxis1 Naxis2 ... Naxisn [N_hdrrec table_type Naxis
+;               IDL_type Naxis1 ... Naxisn] (repeated for each extension) 
+;               For example, the following descriptor 
+;                    167 2 4 3839 4 55 BINTABLE 2 1 89 5
+; 
+;               indicates that the  primary header containing 167 lines, and 
+;               the primary (2D) floating point image (IDL type 4) 
+;               is of size 3839 x 4.    The first extension header contains
+;               55 lines, and the  byte (IDL type 1) table array is of size
+;               89 x 5.
+;
+;               The DESCRIPTOR is *only* computed if /SILENT is set.
+; EXAMPLE:
+;       Display info about all FITS files of the form '*.fit' in the current
+;               directory
+;
+;               IDL> fits_info, '*.fit'
+;
+;       Any time a *.fit file is found which is *not* in FITS format, an error 
+;       message is displayed at the terminal and the program continues
+;
+; PROCEDURES USED:
+;       GETTOK(), MRD_SKIP, STRN(), SXPAR(), TEXTOPEN, TEXTCLOSE 
+;
+; SYSTEM VARIABLES:
+;       The non-standard system variables !TEXTOUT and !TEXTUNIT will be  
+;       created by FITS_INFO if they are not previously defined.   
+;
+;       DEFSYSV,'!TEXTOUT',1
+;       DEFSYSV,'!TEXTUNIT',0
+;
+;       See TEXTOPEN.PRO for more info
+; MODIFICATION HISTORY:
+;       Written, K. Venkatakrishna, Hughes STX, May 1992
+;       Added N_ext keyword, and table_name info, G. Reichert
+;       Work on *very* large FITS files   October 92
+;       More checks to recognize corrupted FITS files     February, 1993
+;       Proper check for END keyword    December 1994
+;       Correctly size variable length binary tables  WBL December 1994
+;       EXTNAME keyword can be anywhere in extension header WBL  January 1998
+;       Correctly skip past extensions with no data   WBL   April 1998
+;       Converted to IDL V5.0, W. Landsman, April 1998
+;       No need for !TEXTOUT if /SILENT D.Finkbeiner   February 2002
+;       Define !TEXTOUT if needed.  R. Sterner, 2002 Aug 27
+;       Work on gzip compressed files for V5.3 or later  W. Landsman 2003 Jan
+;       Improve speed by only reading first 36 lines of header 
+;       Count headers with more than 32767 lines         W. Landsman Feb. 2003
+;       Assume since V5.3 (OPENR,/COMPRESS)   W. Landsman Feb 2004
+;       EXTNAME keyword can be anywhere in extension header again 
+;                         WBL/S. Bansal Dec 2004
+;       Read more than 200 extensions  WBL   March 2005
+;       Work for FITS files with SIMPLE=F   WBL July 2005
+;       Assume since V5.4, fstat.compress available WBL April 2006
+;       Added EXTNAME as an IDL keyword to return values. M. Perrin Dec 2007
+;       make Ndata a long64 to deal with large files. E. Hivon Mar 2008
+;       For GDL compatibility, first check if file is compressed  before using
+;          OPENR,/COMPRESS  B. Roukema/WL    Apr 2010
+;       Increased nmax (max number of extensions) from 400 to 2000   Sept 2012
+;       Correctly fills EXTNAME when SILENT is set    EH   Jan 2013
+;       Turned ptr to long64 in order to read very large files EH Dec 2013
+;       Replaced 2880 with 2880LL to work on very large files  EH Mar 2015
+;-
+ On_error,2
+ compile_opt idl2
+ COMMON descriptor,fdescript
+
+ if N_params() lt 1 then begin
+     print,'Syntax - FITS_INFO, filename, [/SILENT, TEXTOUT=, N_ext=, EXTNAME=]'
+     return
+ endif
+
+ defsysv,'!TEXTOUT',exists=ex   ; Check if !TEXTOUT exists.
+ if ex eq 0 then defsysv,'!TEXTOUT',1 ; If not define it.
+
+ fil = file_search( filename, COUNT = nfiles) 
+ if nfiles EQ 0 then message,'No files found'
+; File is gzip compressed if it ends in .gz or .ftz 
+ len = strlen(fil)
+ ext = strlowcase(strmid(fil,transpose(len-3),3))
+ compress = (ext EQ '.gz') || (ext EQ 'ftz')
+
+ silent = keyword_set( SILENT )
+ if ~silent then begin 
+     if ~keyword_set( TEXTOUT ) then textout = !TEXTOUT    
+     textopen, 'FITS_INFO', TEXTOUT=textout
+ endif
+
+ for nf = 0, nfiles-1 do begin
+
+     file = fil[nf]
+
+     openr, lun1, file, /GET_LUN, COMPRESS = compress[nf]
+     
+     N_ext = -1
+     fdescript = ''
+     nmax = 2000                ; MDP was 100, then 400
+     nbuf= nmax
+     extname = strarr(nmax)
+
+     ptr = 0LL
+     START:  
+     ON_IOerror, BAD_FILE
+     descript = ''
+;   Is this a proper FITS file?     
+     test = bytarr(8)
+     readu, lun1, test
+     
+     if N_ext EQ -1 then begin
+         if string(test) NE 'SIMPLE  ' then goto, BAD_FILE
+         simple = 1
+     endif else begin
+         if string(test) NE 'XTENSION' then goto, END_OF_FILE
+         simple = 0
+     endelse
+     point_lun, lun1, ptr
+
+;                               Read the header
+     hdr = bytarr(80, 36, /NOZERO)
+     N_hdrblock = 1
+     readu, lun1, hdr
+     ptr += 2880LL
+     hd = string( hdr > 32b)
+     
+;                               Get values of BITPIX, NAXIS etc.
+     bitpix = sxpar(hd, 'BITPIX', Count = N_BITPIX)
+     if N_BITPIX EQ 0 then $ 
+       message, 'WARNING - FITS header missing BITPIX keyword',/CON
+     Naxis = sxpar( hd, 'NAXIS', Count = N_NAXIS)
+     if N_NAXIS EQ 0 then message, $ 
+       'WARNING - FITS header missing NAXIS keyword',/CON
+     
+     exten = sxpar( hd, 'XTENSION')
+     Ext_type = strmid( strtrim( exten ,2), 0, 8) ;Use only first 8 char
+     gcount = sxpar( hd, 'GCOUNT') > 1
+     pcount = sxpar( hd, 'PCOUNT')
+     
+     if strn(Ext_type) NE '0' then begin
+         if (gcount NE 1) or (pcount NE 0) then $
+           ext_type = 'VAR_' + ext_type
+         descript += ' ' + Ext_type
+     endif
+
+     descript += ' ' + strn(Naxis)
+     
+     case BITPIX of
+           8:   IDL_type = 1      ; Byte
+          16:   IDL_type = 2     ; Integer*2
+          32:   IDL_type = 3     ; Integer*4
+         -32:   IDL_type = 4    ; Real*4 
+         -64:   IDL_type = 5    ; Real*8
+         ELSE: begin 
+             message, ' Illegal value of BITPIX = ' + strn(bitpix) + $
+                      ' in header',/CON
+             goto, SKIP
+         end
+     endcase
+
+     if Naxis GT 0 then begin
+         descript += ' ' + strn(IDL_type)
+         Nax = sxpar( hd, 'NAXIS*')
+         if N_elements(Nax) LT Naxis then begin 
+             message, $
+               'ERROR - Missing required NAXISi keyword in FITS header',/CON
+             goto, SKIP
+         endif
+         for i = 1, Naxis do descript += ' '+strn(Nax[i-1])
+     endif
+     
+     end_rec = where( strtrim(strmid(hd,0,8),2) EQ  'END')
+     
+     exname = sxpar(hd, 'extname', Count = N_extname)
+     if N_extname GT 0 then extname[N_ext+1] = exname
+     get_extname =  (N_ext GE 0) && (N_extname EQ 0)  
+  
+;  Read header records, till end of header is reached
+
+     hdr = bytarr(80, 36, /NOZERO)
+     while (end_rec[0] EQ -1) && (~eof(lun1) ) do begin
+         readu,lun1,hdr
+         ptr = ptr + 2880LL
+         hd1 = string( hdr > 32b)
+         end_rec = where( strtrim(strmid(hd1,0,8),2) EQ  'END')
+         n_hdrblock++ 
+         if get_extname then begin
+             exname = sxpar(hd1, 'extname', Count = N_extname)
+             if N_extname GT 0 then begin
+                 extname[N_ext+1] = exname
+                 get_extname = 0
+             endif
+         endif 
+     endwhile
+     
+     n_hdrec = 36L*(n_hdrblock-1) + end_rec[0] + 1L ; size of header
+     descript = strn( n_hdrec ) + descript
+     
+;  If there is data associated with primary header, then find out the size
+     
+     if Naxis GT 0 then begin
+         ndata = long64(Nax[0])
+         if naxis GT 1 then for i = 2, naxis do ndata *= Nax[i-1]
+     endif else ndata = 0
+     
+     nbytes = (abs(bitpix)/8) * gcount * (pcount + ndata)
+     nrec = long(( nbytes +2879)/ 2880)
+     
+
+ 
+; Check if all headers have been read 
+
+     if ( simple EQ 0 ) && ( strlen(strn(exten)) EQ 1) then goto, END_OF_FILE  
+
+     N_ext++ 
+     if N_ext GE (nmax-1) then begin 
+         extname = [extname,strarr(nbuf)]
+         nmax = N_elements(extname)
+     endif	
+     
+; Append information concerning the current extension to descriptor
+     
+     fdescript += ' ' + descript
+     
+; Check for EOF
+; Skip the headers and data records
+
+     ptr += nrec*2880LL
+     if compress[nf] then mrd_skip,lun1,nrec*2880LL else point_lun,lun1,ptr
+     if ~eof(lun1) then goto, START
+;
+     END_OF_FILE:  
+     
+     extname = extname[0:N_ext] ;strip off bogus first value
+                                  ;otherwise will end up with '' at end
+
+     if ~SILENT then begin
+         printf,!textunit,file,' has ',strn(N_ext),' extensions'
+         printf,!textunit,'Primary header: ',gettok(fdescript,' '),' records'
+ 
+         Naxis = gettok( fdescript,' ' ) 
+         
+         If Naxis NE '0' then begin
+             
+             case gettok(fdescript,' ') of
+                 
+                 '1': image_type = 'Byte'
+                 '2': image_type = 'Integer*2'    
+                 '3': image_type = 'Integer*4'
+                 '4': image_type = 'Real*4'
+                 '5': image_type = 'Real*8'
+                 
+             endcase
+             
+             image_desc = 'Image -- ' + image_type + ' array ('
+             for i = 0,fix(Naxis)-1 do image_desc = image_desc + ' '+ gettok(fdescript,' ')
+             image_desc = image_desc+' )'
+             
+         endif else image_desc = 'No data'
+         printf,!textunit, format='(a)',image_desc
+         
+         if N_ext GT 0 then begin
+             for i = 1,N_ext do begin
+                 
+                 printf, !TEXTUNIT, 'Extension ' + strn(i) + ' -- '+extname[i]
+                 
+                 header_desc = '               Header : '+gettok(fdescript,' ')+' records'
+                 printf, !textunit, format = '(a)',header_desc
+                 
+                 table_type = gettok(fdescript,' ')
+                 
+                 case table_type of
+                     'A3DTABLE' : table_desc = 'Binary Table'
+                     'BINTABLE' : table_desc = 'Binary Table'
+                     'VAR_BINTABLE': table_desc = 'Variable length Binary Table'
+                     'TABLE':     table_desc = 'ASCII Table'
+                     ELSE:       table_desc = table_type
+                 endcase
+
+                 table_desc = '               ' + table_desc + ' ( '
+                 table_dim = fix( gettok( fdescript,' ') )
+                 if table_dim GT 0 then begin
+                     table_type = gettok(fdescript,' ')
+                     for j = 0, table_dim-1 do $
+                             table_desc += gettok(fdescript,' ') + ' '
+                 endif
+                 table_desc += ')'
+                 
+                 printf,!textunit, format='(a)',table_desc
+             endfor
+         endif
+         
+         printf, !TEXTUNIT, ' '
+     endif 
+     SKIP: free_lun, lun1
+ endfor
+ if ~silent then textclose, TEXTOUT=textout
+ return
+ 
+ BAD_FILE:
+ message, 'Error reading FITS file ' + file, /CON
+ goto,SKIP
+end
diff --git a/Code/script_idl_mv/astrolib/fits_open.pro b/Code/script_idl_mv/astrolib/fits_open.pro
new file mode 100644
index 0000000000000000000000000000000000000000..87bb87b00fe03ca94614d78f28c8b0563bc5cea1
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_open.pro
@@ -0,0 +1,459 @@
+pro fits_open,filename,fcb,write=write,append=append,update=update, $
+                 no_abort=no_abort,message=message,hprint=hprint,fpack=fpack
+;+
+; NAME:
+;       FITS_OPEN
+;
+; PURPOSE:
+;       Opens a FITS (Flexible Image Transport System) data file.
+;
+; EXPLANATION:
+;       Used by FITS_READ and FITS_WRITE
+;
+; CALLING SEQUENCE:
+;       FITS_OPEN, filename, fcb
+;
+; INPUTS:
+;       filename : name of the FITS file to open, scalar string
+;                  FITS_OPEN can also open gzip compressed (.gz) files or Unix
+;                  compressed files *for  reading only*, although there is a 
+;                  performance penalty. FPACK (
+;                  http://heasarc.gsfc.nasa.gov/fitsio/fpack/ ) 
+;                  compressed FITS files can be read provided that the FPACK 
+;                  software is installed.
+;*OUTPUTS:
+;       fcb : (FITS Control Block) a IDL structure containing information
+;               concerning the file.  It is an input to FITS_READ, FITS_WRITE
+;               FITS_CLOSE and MODFITS.  
+; INPUT KEYWORD PARAMETERS:
+;       /APPEND: Set to append to an existing file.
+;       /FPACK - Signal that the file is compressed with the FPACK software. 
+;               http://heasarc.gsfc.nasa.gov/fitsio/fpack/ ) By default, 
+;               FITS_OPEN assumes that if the file name extension ends in 
+;               .fz that it is fpack compressed.     The FPACK software must
+;               be installed on the system 
+;       /HPRINT - print headers with routine HPRINT as they are read.
+;               (useful for debugging a strange file)
+;       /NO_ABORT: Set to quietly return to calling program when an I/O error  
+;               is encountered, and return  a non-null string
+;               (containing the error message) in the keyword MESSAGE.    
+;               If /NO_ABORT not set, then FITS_OPEN will display the error 
+;               message and return to the calling program.
+;       /UPDATE Set this keyword to open an existing file for update
+;       /WRITE: Set this keyword to open a new file for writing. 
+;
+; OUTPUT KEYWORD PARAMETERS:
+;       MESSAGE = value: Output error message.    If the FITS file was opened
+;               successfully, then message = ''.
+;       
+; NOTES:
+;       The output FCB should be passed to the other FITS routines (FITS_OPEN,
+;       FITS_READ, FITS_HELP, and FITS_WRITE).  It has the following structure
+;       when FITS_OPEN is called without /WRITE or /APPEND keywords set.
+;
+;           FCB.FILENAME - name of the input file
+;               .UNIT - unit number the file is opened to
+;               .FCOMPRESS - 1 if unit is a FPACK compressed file opened with
+;                    a pipe to SPAWN
+;               .NEXTEND - number of extensions in the file.
+;               .XTENSION - string array giving the extension type for each
+;                       extension.
+;               .EXTNAME - string array giving the extension name for each
+;                       extension. (null string if not defined the extension)
+;               .EXTVER - vector of extension version numbers (0 if not
+;                       defined)
+;               .EXTLEVEL - vector of extension levels (0 if not defined)
+;               .GCOUNT - vector with the number of groups in each extension.
+;               .PCOUNT - vector with parameter count for each group
+;               .BITPIX - BITPIX for each extension with values
+;                                  8    byte data
+;                                16     short word integers
+;                                32     long word integers
+;                               -32     IEEE floating point
+;                               -64     IEEE double precision floating point
+;               .NAXIS - number of axes for each extension.  (0 for null data
+;                       units)
+;               .AXIS - 2-D array where axis[*,N] gives the size of each axes
+;                       for extension N
+;               .START_HEADER - vector giving the starting byte in the file
+;                               where each extension header begins
+;               .START_DATA - vector giving the starting byte in the file
+;                               where the data for each extension begins
+;
+;               .HMAIN - keyword parameters (less standard required FITS
+;                               keywords) for the primary data unit.
+;               .OPEN_FOR_WRITE - flag (0= open for read, 1=open for write, 
+;                                                2=open for update)
+;               .LAST_EXTENSION - last extension number read.
+;               .RANDOM_GROUPS - 1 if the PDU is random groups format,
+;                               0 otherwise
+;               .NBYTES - total number of (uncompressed) bytes in the FITS file
+;
+;       When FITS open is called with the /WRITE or /APPEND option, FCB
+;       contains:
+;
+;           FCB.FILENAME - name of the input file
+;               .UNIT - unit number the file is opened to
+;               .NEXTEND - number of extensions in the file.
+;               .OPEN_FOR_WRITE - flag (1=open for write, 2=open for append
+;                                       3=open for update)
+;
+;
+; EXAMPLES:
+;       Open a FITS file for reading:
+;               FITS_OPEN,'myfile.fits',fcb
+;
+;       Open a new FITS file for output:
+;               FITS_OPEN,'newfile.fits',fcb,/write
+; PROCEDURES USED:
+;       GET_PIPE_FILESIZE (for Fcompress'ed files) HPRINT, SXDELPAR, SXPAR()
+; HISTORY:
+;       Written by:     D. Lindler      August, 1995
+;       July, 1996      NICMOS  Modified to allow open for overwrite
+;                               to allow primary header to be modified
+;       DJL Oct. 15, 1996   corrected to properly extend AXIS when more
+;                       than 100 extensions present
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use Message = '' rather than !ERR =1 as preferred signal of normal
+;           operation   W. Landsman  November 2000
+;       Lindler, Dec, 2001, Modified to use 64 bit words for storing byte
+;             positions within the file to allow support for very large
+;             files 
+;       Work with gzip compressed files W. Landsman    January 2003
+;       Fix gzip compress for V5.4 and earlier  W.Landsman/M.Fitzgerald Dec 2003 
+;       Assume since V5.3 (STRSPLIT, OPENR,/COMPRESS) W. Landsman Feb 2004
+;       Treat FTZ extension as gzip compressed W. Landsman Sep 2004
+;       Assume since V5.4 fstat.compress available W. Landsman Apr 2006
+;       FCB.Filename  now expands any wildcards W. Landsman July 2006
+;       Make ndata 64bit for very large files B. Garwood/W. Landsman Sep 2006
+;       Open with /SWAP_IF_LITTLE_ENDIAN, remove obsolete keywords to OPEN
+;                W. Landsman  Sep 2006
+;       Warn that one cannot open a compressed file for update W.L. April 2007
+;       Use post-V6.0 notation W.L. October 2010
+;       Support FPACK compressed files, new .FCOMPRESS tag to FCB structure
+;               W.L.  December 2010
+;       Read gzip'ed files even if gzip is not installed W.L. October 2012
+;       Handle axis sizes requiring 64 integer W.L.  April 2014
+;       Support for .Z compressed files M. Zechmeister/W.L.  April 2014
+;       Wrap filenames in "" when spawning subprocesses, to handle paths
+;       with spaces or other atypical characters. M. Perrin Nov 2014
+;-
+;--------------------------------------------------------------------
+      compile_opt idl2
+; if no parameters supplied, print calling sequence
+;
+       if N_params() LT 1 then begin
+          print,'Syntax - FITS_OPEN, filename, fcb'
+          print,' Input Keywords:  /Append, /Hprint, /No_abort, /Update, /Write'
+          print,' Output Keyword:  Message= '
+          return
+       endif
+;
+; set default keyword parameters
+;
+
+        message = ''
+        open_for_read = 1
+        open_for_update = 0
+        open_for_write = 0
+        open_for_overwrite = 0
+        if keyword_set(write) then begin
+                open_for_read = 0
+                open_for_update = 0
+                open_for_write = 1
+                open_for_overwrite = 0
+        end
+        if keyword_set(append) then begin
+                open_for_read = 0
+                open_for_write = 0
+                open_for_update = 1
+                open_for_overwrite = 0
+        end     
+        if keyword_set(update) then begin
+                open_for_read = 1 
+                open_for_write = 0
+                open_for_update = 0 
+                open_for_overwrite = 1 
+        end     
+;
+; on I/O errors goto statement ioerror:
+;
+        on_ioerror,ioerror
+;
+; open file
+;
+
+        ext = strlowcase(strmid(filename, 2, /rev))
+        docompress = (ext EQ '.gz') || (ext EQ 'ftz') 
+        fcompress = keyword_set(fpack) || ( ext EQ '.fz')
+	 zcompress = (strmid(filename, 1, /rev) EQ '.Z') 
+         if docompress && open_for_overwrite then begin 
+            message = 'Compressed FITS files cannot be open for update'
+            if ~keyword_set(no_abort) then $
+                   message,' ERROR: '+message,/CON
+            return
+       endif   
+ ;
+; open file
+;
+       if ~fcompress && ~zcompress then get_lun,unit
+       if fcompress then $
+                spawn,'funpack -S "' + filename+'"', unit=unit,/sh else $	
+       if zcompress then $	
+                spawn,'gzip -cd "'+filename+'"', unit=unit,/sh  else $	
+       if docompress then $
+                openr,unit,filename, /compress,/swap_if_little else begin
+       case 1 of
+                keyword_set(append): openu,unit,filename,/swap_if_little
+                keyword_set(update): openu,unit,filename,/swap_if_little
+                keyword_set(write) : openw,unit,filename,/swap_if_little
+                else               : openr,unit,filename,/swap_if_little
+        endcase
+        endelse
+
+        file = fstat(unit)
+        fname = file.name          ;In case the user input a wildcard
+        docompress = file.compress
+
+; Need to spawn to "gzip -l" to get the number of uncompressed bytes in a gzip
+; compressed file.  If gzip doesn't work for some reason then use 
+; get_pipe_filesize.
+
+        if fcompress then begin 
+	      get_pipe_filesize,unit, nbytes_in_file
+	      free_lun,unit
+	      spawn,'funpack -S "' + filename +'"', unit=unit,/sh
+        endif else if docompress then begin 
+	     if !VERSION.OS_FAMILY Eq 'Windows' then $
+	           fname = file_search(fname,/fully_qualify)
+             spawn,'gzip -l "' + fname+'"', output
+             output = strtrim(output,2)
+             g = where(strmid(output,0,8) EQ 'compress', Nfound)
+	     if Nfound EQ 0 then begin
+	            get_pipe_filesize, unit, nbytes_in_file
+		    close,unit
+		    openr,unit,filename, /compress,/swap_if_little
+             endif else $
+	         nbytes_in_file = long64((strsplit(output[g[0]+1],/extract))[1])
+        endif else if zcompress then begin
+	     spawn,'zcat "' + filename+'"' + ' | wc -c', nbytes_in_file
+	     if nbytes_in_file EQ 0 then message,'Unable to zcat decompress ' + fname
+	endif else nbytes_in_file = file.size
+	
+;
+; create vectors needed to store header information for each extension
+;
+        n = 100
+        xtension = strarr(n)
+        extname = strarr(n)
+        extver = lonarr(n)
+        extlevel = lonarr(n)
+        gcount = lonarr(n)
+        pcount = lonarr(n)
+        bitpix = lonarr(n)
+        naxis  = lonarr(n)
+        axis = lon64arr(20,n)
+        start_header = lon64arr(n)        ; starting byte in file for header
+        start_data = lon64arr(n)          ; starting byte in file for data
+        position = 0ULL             ; current byte position in file
+        skip = 0ULL                 ; Amount to skip from current position
+;
+; read and process each header in the file if open for read or update
+;
+        extend_number = 0               ; current extension number being
+                                        ; processed
+ 
+        if open_for_read || open_for_update then begin
+            main_header = 1             ; first header in file flag
+            h = bytarr(80,36,/nozero)   ; read buffer
+;
+; loop on headers in the file
+;
+            repeat begin
+            if skip GT 0 then if (fcompress || zcompress) then mrd_skip,unit,skip else $
+	                                     point_lun,unit,position 
+              start = position
+;
+; loop on header blocks
+;
+                first_block = 1         ; first block in header flag
+                repeat begin
+
+                    if (~fcompress && ~zcompress) && position+2879 ge nbytes_in_file then begin
+                        if extend_number eq 0 then begin
+                                message = 'EOF encountered while reading header'
+                                goto,error_exit
+                        endif
+                        print,'EOF encountered reading extension header'
+                        print,'Only '+strtrim(extend_number-1,2) + $
+                                ' extensions processed'
+                        goto,done_headers
+                    endif
+
+                    readu,unit,h
+                    position = position + 2880
+                    hdr = string(h>32b)
+                    endline = where(strmid(hdr,0,8) eq 'END     ',nend)
+                    if nend gt 0 then hdr = hdr[0:endline[0]]
+                    if first_block then begin
+;
+; check for valid header (SIMPLE keyword must be first for PDU and
+; XTENSION keyword for the extensions.
+;
+                        header = hdr 
+                        keyword = strmid(header[0],0,8)
+                        if (extend_number eq 0) && $
+                           (keyword ne 'SIMPLE  ') then begin
+                                message = 'Invalid header, no SIMPLE keyword'
+                                goto,error_exit
+                        endif
+
+                        if (extend_number gt 0) && $
+                           (keyword ne 'XTENSION') then begin
+                                print,'Invalid extension header encountered'
+                                print,'XTENSION keyword missing'
+                                print,'Only '+strtrim(extend_number-1,2) + $
+                                        ' extensions processed'
+                                goto,done_headers
+                        endif
+
+                    end else header = [header,hdr]
+                    first_block = 0
+                end until (nend gt 0)   
+
+;
+; print header if hprint set
+;
+                if keyword_set(hprint) then hprint,header
+;
+; end of loop on header blocks
+;
+; Increase size of vectors if needed
+;
+                if extend_number ge n then begin
+                        xtension = [xtension,strarr(n)]
+                        extname = [extname,strarr(n)]
+                        extver = [extver,lonarr(n)]
+                        extlevel = [extver,lonarr(n)]
+                        gcount = [gcount,lonarr(n)]
+                        pcount = [pcount,lonarr(n)]
+                        bitpix = [bitpix,lonarr(n)]
+                        naxis  = [naxis,lonarr(n)]
+                        old_axis = axis
+                        axis = lonarr(20,n*2)
+                        axis[0,0] = old_axis
+                        start_header = [start_header,lonarr(n)]
+                        start_data = [start_data,lonarr(n)]
+                        n = n*2
+                end
+;
+; extract information from header
+;
+                xtension[extend_number] = strtrim(sxpar(header,'xtension'))
+                st = sxpar(header,'extname', Count = N_extname)
+                if N_extname EQ 0 then st = ''
+                extname[extend_number] = strtrim(st,2)  
+                extver[extend_number] = sxpar(header,'extver')          
+                extlevel[extend_number] = sxpar(header,'extlevel')              
+                gcount[extend_number] = sxpar(header,'gcount')
+                pcount[extend_number] = sxpar(header,'pcount')
+                bitpix[extend_number] = sxpar(header,'bitpix')
+                nax = sxpar(header,'naxis')
+                naxis[extend_number] = nax
+                if nax gt 0 then begin 
+		    naxisi = sxpar(header,'naxis*')
+		    axis[0,extend_number] = naxisi
+		    ndata = product(naxisi,/integer)
+                endif else ndata = 0 
+		
+               start_data[extend_number] = position    
+               start_header[extend_number] = start
+;
+; if first header, save without FITS required keywords
+;
+                if extend_number eq 0 then begin
+                    hmain = header
+                    random_groups = sxpar(header,'groups')
+                    sxdelpar,hmain,['SIMPLE','BITPIX','NAXIS','NAXIS1', $
+                                    'NAXIS2','NAXIS3','NAXIS4','NAXIS5', $
+                                    'NAXIS6','NAXIS7','NAXIS8','EXTEND', $
+                                    'PCOUNT','GCOUNT','GROUPS','BSCALE', $
+                                    'BZERO','NPIX1','NPIX2','PIXVALUE']
+                        if (pcount[0] gt 0) then for i=1,pcount[0] do $
+                        sxdelpar,hmain,['ptype','pscal','pzero']+strtrim(i,2)
+                endif
+;
+; skip past data to go to next header
+;
+                nbytes = (abs(bitpix[extend_number])/8) * $
+                       (gcount[extend_number]>1)*(pcount[extend_number] + ndata)
+                skip = (nbytes + 2879)/2880*2880
+                position += skip
+
+;
+; end loop on headers
+;           
+
+                extend_number +=  1
+            end until (position ge nbytes_in_file-2879)
+        end
+;
+; point at end of file in /extend
+;
+done_headers:
+        if open_for_update then point_lun,unit,nbytes_in_file
+;
+; number of extensions
+;
+        if open_for_write then nextend = -1 $
+                          else nextend = extend_number - 1
+;
+; set up blank hmain if open for write
+;
+        if open_for_write then begin
+                hmain = strarr(1)
+                hmain[0] = 'END     '
+        end
+;
+; create output structure for the file control block
+;
+        if open_for_write or open_for_update then begin
+                fcb = {filename:fname,unit:unit,nextend:nextend, $
+                        open_for_write:open_for_write + open_for_update*2}
+           end else begin
+                nx = nextend
+               fcb = {filename:fname,unit:unit,fcompress:fcompress||zcompress, $
+		        nextend:nextend, $
+                         xtension:xtension[0:nx],extname:extname[0:nx], $
+                        extver:extver[0:nx],extlevel:extlevel[0:nx], $
+                        gcount:gcount[0:nx],pcount:pcount[0:nx], $
+                        bitpix:bitpix[0:nx],naxis:naxis[0:nx], $
+                        axis:axis[*,0:nx], $
+                        start_header:start_header[0:nx], $
+                        start_data:start_data[0:nx],hmain:hmain, $
+                        open_for_write:open_for_overwrite*3,$
+                        last_extension:-1, $
+                        random_groups:random_groups, $
+                        nbytes: nbytes_in_file }
+        end
+         if fcompress then begin	
+	       free_lun,unit	      
+               spawn,'funpack -S "' + filename+'"', unit=unit,/sh 
+         endif else if zcompress then begin 
+	       free_lun,unit
+	       spawn,'gzip -cd "' + filename+'"', unit=unit, /sh
+	endif       
+        !err = 1            ;For obsolete users still using !err
+        return
+;
+; error exit
+;
+ioerror: 
+        message = !ERROR_STATE.msg
+error_exit:
+        free_lun,unit
+        !err = -1
+        if keyword_set(no_abort) then return
+        message,' ERROR: '+message,/CON
+        return
+end
diff --git a/Code/script_idl_mv/astrolib/fits_read.pro b/Code/script_idl_mv/astrolib/fits_read.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3298d6aec482e7486167dc32daf7b09466081d3c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_read.pro
@@ -0,0 +1,573 @@
+pro fits_read,file_or_fcb,data,header,group_par,noscale=noscale, $
+                exten_no=exten_no, extname=extname, $
+                extver=extver, extlevel=extlevel, xtension=xtension, $
+                no_abort=no_abort, message=message, first=first, last=last, $
+                group=group, header_only=header_only,data_only=data_only, $
+                no_pdu=no_pdu, enum = enum, no_unsigned = no_unsigned, pdu=pdu
+
+;+
+; NAME:
+;       FITS_READ
+; PURPOSE:
+;       To read a FITS file.
+;
+; CALLING SEQUENCE:
+;       FITS_READ, filename_or_fcb, data [,header, group_par]
+;
+; INPUTS:
+;       FILENAME_OR_FCB - this parameter can be the FITS Control Block (FCB)
+;               returned by FITS_OPEN or the file name of the FITS file.  If
+;               a file name is supplied, FITS_READ will open the file with
+;               FITS_OPEN and close the file with FITS_CLOSE before exiting.
+;               When multiple extensions are to be read from the file, it is
+;               more efficient for the user to call FITS_OPEN and leave the
+;               file open until all extensions are read. FPACK 
+;               ( http://heasarc.gsfc.nasa.gov/fitsio/fpack/ ) compressed FITS 
+;               files can be read provided that the FPACK software is installed.
+;               Both Gzip compressed (.gz) and Unix compressed (*.Z) files can
+;               be read, although there is a performance penalty..
+;
+; OUTPUTS:
+;       DATA - data array.  If /NOSCALE is specified, BSCALE and BZERO
+;               (if present in the header) will not be used to scale the data.
+;               If Keywords FIRST and LAST are used to read a portion of the
+;               data or the heap portion of an extension, no scaling is done
+;               and data is returned as a 1-D vector. The user can use the IDL
+;               function REFORM to convert the data to the correct dimensions
+;               if desired.  If /DATA_ONLY is specified, no scaling is done.
+;       HEADER - FITS Header.  The STScI inheritance convention is recognized
+;               http://fits.gsfc.nasa.gov/registry/inherit/fits_inheritance.txt
+;               If an extension is read, and the INHERIT keyword exists with a 
+;               value of T, and the /NO_PDU keyword keyword is not supplied, 
+;               then the primary data unit header and the extension header will
+;                be combined.  The header will have the form:
+;
+;                       <required keywords for the extension: XTENSION, BITPIX,
+;                               NAXIS, ...>
+;                       BEGIN MAIN HEADER --------------------------------
+;                       <PDU header keyword and history less required keywords:
+;                               SIMPLE, BITPIX, NAXIS, ...>
+;                       BEGIN EXTENSION HEADER ---------------------------
+;                       <extension header less required keywords that were
+;                               placed at the beginning of the header.
+;                       END
+;               
+;               The structure of the header is such that if a keyword is
+;               duplicated in both the PDU and extension headers, routine
+;               SXPAR will print a warning and return the extension value of
+;               the keyword. 
+;
+;       GROUP_PAR - Group parameter block for FITS random groups format files
+;               or the heap area for variable length binary tables.
+;               Any scale factors in the header (PSCALn and PZEROn) are not
+;               applied to the group parameters.
+;
+; INPUT KEYWORD PARAMETERS:
+;
+;       /NOSCALE: Set to return the FITS data without applying the scale
+;               factors BZERO and BSCALE.
+;       /HEADER_ONLY: set to read the header only.
+;       /DATA_ONLY: set to read the data only.  If set, if any scale factors
+;               are present (BSCALE or BZERO), they will not be applied.
+;       /NO_PDU: By default, FITS_READ will add the primary data unit header 
+;               keywords to the output header, *if* the header includes 
+;               INHERIT = T.   Set /NO_PDU to never append the primary header.
+;       /NO_ABORT: Set to return to calling program instead of a RETALL
+;               when an I/O error is encountered.  If set, the routine will
+;               return  a non-null string (containing the error message) in the
+;               keyword MESSAGE.    (For backward compatibility, the obsolete 
+;               system variable !ERR is also set to -1 in case of an error.)   
+;               If /NO_ABORT not set, then FITS_READ will print the message and
+;               issue a RETALL
+;       /NO_UNSIGNED - By default, if  the header indicates an unsigned integer
+;              (BITPIX = 16, BZERO=2^15, BSCALE=1) then FITS_READ will output 
+;               an IDL unsigned integer data type (UINT).   But if /NO_UNSIGNED
+;               is set, then the data is converted to type LONG.  
+;       /PDU - If set, then always add the primary data unit header keywords
+;              to the output header, even if the INHERIT=T keyword is not found
+;              This was the default behavior of FITS_READ prior to April 2007
+;       EXTEN_NO - extension number to read.  If not set, the next extension
+;               in the file is read.  Set to 0 to read the primary data unit.
+;       XTENSION - string name of the xtension to read
+;       EXTNAME - string name of the extname to read
+;       EXTVER - integer version number to read
+;       EXTLEVEL - integer extension level to read
+;       FIRST - set this keyword to only read a portion of the data.  It gives
+;               the first word of the data to read
+;       LAST - set this keyword to only read a portion of the data.  It gives
+;               the last word number of the data to read
+;       GROUP - group number to read for GCOUNT>1.  (Default=0, the first group)
+;       
+; OUTPUT KEYWORD PARAMETERS:
+;       ENUM - Output extension number that was read.  
+;       MESSAGE = value: Output error message
+;
+; NOTES:
+;       Determination or which extension to read.
+;               case 1: EXTEN_NO specified. EXTEN_NO will give the number of the
+;                       extension to read.  The primary data unit is refered
+;                       to as extension 0. If EXTEN_NO is specified, XTENSION,
+;                       EXTNAME, EXTVER, and EXTLEVEL parameters are ignored.
+;               case 2: if EXTEN_NO is not specified, the first extension
+;                       with the specified XTENSION, EXTNAME, EXTVER, and
+;                       EXTLEVEL will be read.  If any of the 4 parameters
+;                       are not specified, they will not be used in the search.
+;                       Setting EXTLEVEL=0, EXTVER=0, EXTNAME='', or
+;                       XTENSION='' is the same as not supplying them.
+;               case 3: if none of the keyword parameters, EXTEN_NO, XTENSION,
+;                       EXTNAME, EXTVER, or EXTLEVEL are supplied.  FITS_READ
+;                       will read the next extension in the file.  If the
+;                       primary data unit (PDU), extension 0, is null, the
+;                       first call to FITS_READ will read the first extension
+;                       of the file.
+;
+;               The only way to read a null PDU is to use EXTEN_NO = 0.
+;
+;       If FIRST and LAST are specified, the data is returned without applying
+;       any scale factors (BSCALE and BZERO) and the data is returned in a
+;       1-D vector.  This will allow you to read any portion of a multiple
+;       dimension data set.  Once returned, the IDL function REFORM can be
+;       used to place the correct dimensions on the data.
+;
+;       IMPLICIT IMAGES: FITS_READ will construct an implicit image
+;               for cases where NAXIS=0 and the NPIX1, NPIX2, and PIXVALUE
+;               keywords are present.  The output image will be:
+;                       image = replicate(PIXVALUE,NPIX1,NPIX2)
+;
+;      FPACK compressed files are always closed and reopened when exiting 
+;      FITS_READ so that the pointer is set to the beginning of the file. (Since 
+;      FPACK files are opened with a bidirectional pipe rather than OPEN, one 
+;      cannot use POINT_LUN to move to a specified position in the file.)
+;
+; EXAMPLES:
+;       Read the primary data unit of a FITS file, if it is null read the
+;       first extension:
+;               FITS_READ, 'myfile.fits', data, header
+;
+;       Read the first two extensions of a FITS file and the extension with
+;       EXTNAME = 'FLUX' and EXTVER = 4
+;               FITS_OPEN, 'myfile.fits', fcb
+;               FITS_READ, fcb,data1, header2, exten_no = 1
+;               FITS_READ, fcb,data1, header2, exten_no = 2
+;               FITS_READ, fcb,data3, header3, extname='flux', extver=4
+;               FITS_CLOSE, fcb
+;       
+;       Read the sixth image in a data cube for the fourth extension.
+;
+;               FITS_OPEN, 'myfile.fits', fcb
+;               image_number = 6
+;               ns = fcb.axis[0,4]
+;               nl = fcb.axis[1,4]
+;               i1 = (ns*nl)*(image_number-1)
+;               i2 = i2 + ns*nl-1
+;               FITS_READ,fcb,image,header,first=i1,last=i2
+;               image = reform(image,ns,nl,/overwrite)
+;               FITS_CLOSE, fcb
+;
+; PROCEDURES USED:
+;       FITS_CLOSE, FITS_OPEN
+;       SXADDPAR, SXDELPAR, SXPAR()
+; WARNINGS:
+;       In Sep 2006, FITS_OPEN was modified to open FITS files using the
+;       /SWAP_IF_LITTLE_ENDIAN keyword to OPEN, so that subsequent routines 
+;       (FITS_READ, FITS_WRITE) did not require any byte swapping.    An error
+;       may result if an pre-Sep 2006 version of FITS_OPEN is used with a 
+;       post Sep 2006 version of FITS_READ, FITS_WRITE or MODFITS.
+; HISTORY:
+;       Written by:     D. Lindler, August 1995
+;       Avoid use of !ERR       W. Landsman   August 1999
+;       Read unsigned datatypes, added /no_unsigned   W. Landsman December 1999
+;       Don't call FITS_CLOSE unless fcb is defined   W. Landsman January 2000
+;       Set BZERO = 0 for unsigned integer data   W. Landsman  January 2000
+;       Only call IEEE_TO_HOST if needed          W. Landsman February 2000
+;       Ensure EXTEND keyword in primary header   W. Landsman April 2001
+;       Don't erase ERROR message when closing file  W. Landsman April 2002
+;       Assume at least V5.1 remove NANValue keyword  W. Landsman November 2002
+;       Work with compress files (read file size from fcb),
+;       requires updated (Jan 2003) version of FITS_OPEN W. Landsman Jan 2003
+;       Do not modify BSCALE/BZERO for  unsigned integers W. Landsman April 2006
+;       Assume FITS_OPEN has opened the file with /SWAP_IF_LITTLE_ENDIAN
+;                         W. Landsman   September 2006
+;       Fix problem with /DATA_ONLY keyword  M.Buie/W.Landsman  October 2006
+;       Only append primary header if INHERIT=T  W. Landsman  April 2007
+;       Make ndata 64bit for very large files E. Hivon/W. Landsman May 2007
+;       Added /PDU keyword to always append primary header W. Landsman June 2007
+;       Use PRODUCT to compute # of data points   W. Landsman  May 2009
+;       Make sure FIRST is long64 when computing position W.L. October 2009
+;       Read FPACK compressed files, W.L.  December 2010
+;       Don't assume FCB has a FCOMPRESS tag  W.L./Satori UeNO   September 2012
+;       Make sure opened pipes are closed if fcb not left open W.L. April 2012
+;       Fix bug with /data_only introduced Dec 2010      W. L. April 2014
+;-
+;
+;-----------------------------------------------------------------------------
+       compile_opt idl2
+; print calling sequence
+;
+        if N_params() eq 0 then begin
+          print,'Syntax - FITS_READ,file_or_fcb,data,header,group_par'
+          print,' Input Keywords: /noscale, exten_no=, extname=, '
+          print,'               extver=, extlevel=, xtension=, /no_abort, '
+          print,'               first, last, group, /header_only, /no_pdu, /pdu'
+          print,' Output Keywords: enum =, message='
+          return
+        endif
+;
+; I/O error processing
+;
+        on_ioerror,ioerror
+;
+; set defaults
+;
+        message = ''
+        if n_elements(noscale) eq 0 then noscale = 0
+        if n_elements(exten_no) eq 0 then exten_no = -1
+        if n_elements(extname) eq 0 then extname = ''
+        if n_elements(extver) eq 0 then extver = 0
+        if n_elements(extlevel) eq 0 then extlevel = 0
+        if n_elements(first) eq 0 then first = 0
+        if n_elements(last) eq 0 then last = 0
+        if n_elements(no_abort) eq 0 then no_abort = 0
+        if n_elements(group) eq 0 then group = 0
+        if n_elements(header_only) eq 0 then header_only = 0
+        if n_elements(data_only) eq 0 then data_only = 0
+        if n_elements(no_pdu) eq 0 then no_pdu = 0
+        if n_elements(pdu) eq 0 then pdu = 0
+        if n_elements(xtension) eq 0 then xtension = ''
+;
+; Open file if file name is supplied
+;
+        fcbtype = size(file_or_fcb,/type)
+        fcbsize = n_elements(file_or_fcb)
+        if (fcbsize ne 1) || ((fcbtype ne 7) && (fcbtype ne 8)) then begin
+                message = 'Invalid Filename or FCB supplied'
+                goto,error_exit
+        end
+
+        if fcbtype eq 7 then begin
+                fits_open,file_or_fcb,fcb,no_abort=no_abort,message=message
+                if message NE '' then goto,error_exit
+           end else fcb = file_or_fcb
+;
+; determine which extension to read ==========================================
+;
+; case 1: exten_no specified
+;
+
+        enum = exten_no
+        if exten_no le -1 then begin
+;
+; case 2: extname, extver, or extlevel specified
+;
+           if (extname ne '') || (extlevel ne 0) || (extver ne 0) || $
+              (xtension ne '') then begin
+;
+; find extensions with supplied extname, extver, extlevel, and xtension
+;
+                good = replicate(1b,fcb.nextend+1)
+                if extname ne '' then good = good and $
+                         (strtrim(strupcase(extname)) eq strupcase(fcb.extname))
+                if xtension ne '' then good = good and $
+                       (strtrim(strupcase(xtension)) eq strupcase(fcb.xtension))
+                if extver ne 0 then good = good and (extver eq fcb.extver)
+                if extlevel ne 0 then good = good and (extlevel eq fcb.extlevel)
+                good = where(good,ngood)
+;
+; select first one
+;
+                if ngood le 0 then begin
+                    message='No extension for given extname, extver, and/or' + $
+                            ' extlevel found'
+                    goto,error_exit
+                endif
+                enum = good[0]
+              end else begin
+;
+;       case 3: read next extension
+;
+                enum = fcb.last_extension + 1
+                if (enum eq 0) && (fcb.naxis[0] eq 0) then enum = 1
+            end
+        end
+;
+; check to see if it is a valid extension
+;
+        if enum gt fcb.nextend then begin
+                message='EOF encountered'
+                goto,error_exit
+        end
+;
+; extract information from FCB for the extension
+;
+        bitpix = fcb.bitpix[enum]
+        naxis = fcb.naxis[enum]
+        if naxis gt 0 then axis = fcb.axis[0:naxis-1,enum]
+        gcount = fcb.gcount[enum]
+        pcount = fcb.pcount[enum]
+        xtension = fcb.xtension[enum]
+	fcompress = tag_exist(fcb,'fcompress') ? fcb.fcompress : 0
+;
+; read header ================================================================
+;
+        if data_only then goto,read_data
+        h = bytarr(80,36,/nozero)
+        nbytes_in_file = fcb.nbytes
+        position = fcb.start_header[enum]
+	
+        if fcompress then mrd_skip,fcb.unit,position else $
+	                 point_lun,fcb.unit,position
+        first_block = 1         ; first block in header flag
+        repeat begin
+             if position ge nbytes_in_file then begin
+                 message = 'EOF encountered while reading header'
+                 goto,error_exit
+             endif
+
+             readu,fcb.unit,h
+             position +=  2880
+             hdr = string(h>32b)
+             endline = where(strcmp(hdr,'END     ',8),nend)
+             if nend gt 0 then hdr = hdr[0:endline[0]]
+             if first_block then header = hdr else header = [header,hdr]
+             first_block = 0
+        end until (nend gt 0)
+;
+; extract some header information
+;
+        bscale = sxpar(header,'bscale', Count = N_bscale)
+        bzero = sxpar(header,'bzero', Count = N_bzero)
+        if bscale eq 0.0 then bscale = 1.0
+        unsgn_int = (bitpix EQ 16) && (Bzero EQ 32768) && (bscale EQ 1)
+        unsgn_lng = (bitpix EQ 32) && (Bzero EQ 2147483648) && (bscale EQ 1)
+        if (unsgn_int || unsgn_lng) then $
+	        if ~keyword_set(no_unsigned) then noscale = 1
+        if (N_bscale gt 0) &&(noscale eq 0) && (data_only eq 0) && $
+           (last eq 0) && (header_only eq 0) then sxaddpar,header,'bscale',1.0
+        if (N_bzero gt 0) && (noscale eq 0) && (data_only eq 0) && $
+           (last eq 0) && (header_only eq 0) then sxaddpar,header,'bzero',0.0
+        groups = sxpar(header,'groups')
+;
+; create header with form:
+;       ! Required Keywords
+;       ! BEGIN MAIN HEADER ------------------------------------------
+;       ! Primary data unit header keywords
+;       ! BEGIN EXTENSION HEADER -------------------------------------
+;       ! Extension header keywords
+;       ! END           
+;
+;
+; add Primary Data Unit header to it portion of the header to it, unless the
+; NO_PDU keyword is set, or the INHERIT keyword is not found or set to false
+;
+	       
+	if no_pdu EQ 0 then no_pdu = 1 - (sxpar(header,'INHERIT') > 0)
+        if pdu then no_pdu = 0
+        if (no_pdu eq 0) && (enum gt 0) then begin
+	
+;
+; delete required keywords
+;
+        sxdelpar,header,['SIMPLE','BITPIX','NAXIS','NAXIS1', $
+                         'NAXIS2','NAXIS3','NAXIS4','NAXIS5', $
+                         'NAXIS6','NAXIS7','NAXIS8','EXTEND', $
+                         'PCOUNT','GCOUNT','GROUPS', $
+                         'XTENSION']
+	
+
+; create required keywords
+;
+        hreq = strarr(20)
+        hreq[0] = 'END     '
+
+        if enum eq 0 then $
+                sxaddpar,hreq,'SIMPLE','T','image conforms to FITS standard' $
+           else sxaddpar,hreq,'XTENSION',xtension,'extension type'
+
+        sxaddpar,hreq,'bitpix',bitpix,'bits per data value'
+        sxaddpar,hreq,'naxis',naxis,'number of axes'
+        if naxis gt 0 then for i=1,naxis do $
+                sxaddpar,hreq,'naxis'+strtrim(i,2),axis[i-1]
+        if (enum eq 0) && (fcb.nextend GE 1) then $
+                sxaddpar,hreq,'EXTEND','T','file may contain extensions'
+        if groups then sxaddpar,hreq,'GROUPS','T','Group format'
+        if (enum gt 0) || (pcount gt 0) then $
+                     sxaddpar,hreq,'PCOUNT',pcount,'Number of group parameters'
+        if (enum gt 0) || (gcount gt 0) then $
+                    sxaddpar,hreq,'GCOUNT',gcount,'Number of groups'
+       n0 = where(strcmp(hreq,'END     ',8)) & n0=n0[0]
+            hpdu = fcb.hmain
+            n1 = n_elements(hpdu)
+            if n1 gt 1 then begin               
+                hreq = [hreq[0:n0-1], $
+                        'BEGIN MAIN HEADER ---------------------------------', $
+                        hpdu[0:n1-2], $
+                        'BEGIN EXTENSION HEADER ----------------------------', $
+                        'END     ']
+                n0 += n1 + 1
+            end
+;
+; add extension header
+;
+        header = [hreq[0:n0-1],header]
+        end
+        if header_only then begin
+                data = 0
+                goto,done
+        endif
+;
+; Read Data ===================================================================
+;
+read_data:
+        if naxis eq 0 then begin        ;null image?
+                data = 0
+;
+; check for implicit data specified by NPIX1, NPIX2, and PIXVALUE (provided
+; the header was red, i.e. data_only was not specified)
+;
+                if data_only eq 0 then begin
+                        NPIX1 = sxpar(header,'NPIX1')
+                        NPIX2 = sxpar(header,'NPIX2')
+                        PIXVALUE = sxpar(header,'PIXVALUE')
+                        if (NPIX1*NPIX2) gt 0 then $
+                                data = replicate(pixvalue,npix1,npix2)
+                end
+                goto,done
+        endif
+
+        case BITPIX of
+           8:   IDL_type = 1          ; Byte
+          16:   IDL_type = 2          ; Integer*2
+          32:   IDL_type = 3          ; Integer*4
+         -32:   IDL_type = 4          ; Real*4
+         -64:   IDL_type = 5          ; Real*8
+        else:   begin
+                message = 'ERROR - Illegal value of BITPIX (= ' +  $
+                               strtrim(bitpix,2) + ') in FITS header'
+                goto,error_exit
+                end
+        endcase
+
+        ndata = product( axis, /integer )
+        bytes_per_word = (abs(bitpix)/8)
+        nbytes_per_group = bytes_per_word * (pcount + ndata)
+        nbytes = (gcount>1) * nbytes_per_group
+        nwords = nbytes / bytes_per_word
+;
+; starting data position
+;
+
+	skip = data_only EQ 0 ? fcb.start_data[enum] - position : 0
+        position = fcb.start_data[enum]
+;
+; find correct group
+;
+        if last eq 0 then begin
+                if group ge (gcount>1) then begin
+                        message='INVALID group number specified'
+                        goto,error_exit
+                end
+		skip += long64(group) * nbytes_per_group 
+                position += skip
+        end
+;
+; read group parameters
+;
+        if (enum eq 0) && (fcb.random_groups eq 1) && (pcount gt 0) && $
+           (last eq 0) then begin
+            if N_params() gt 3 then begin
+                group_par = make_array( dim = [pcount], type = idl_type, /nozero)
+               
+             if fcompress then mrd_skip,fcb.unit,skip else $
+	                  point_lun,fcb.unit,position
+ 
+                readu,fcb.unit,group_par
+            endif
+	    skip  =  long64(pcount) * bytes_per_word
+            position += skip
+        endif
+;
+; create data array
+;
+        if last gt 0 then begin
+;
+; user specified first and last
+;
+                if (first lt 0) || (last le 1) || (first gt last) || $
+                   (last gt nwords-1) then begin
+                        message = 'INVALID value for parameters FIRST & LAST'
+                        goto,error_exit
+                endif
+                data = make_array(dim = [last-first+1], type=idl_type, /nozero)
+                skip +=  long64(first) * bytes_per_word
+                position += skip
+            endif else begin
+;
+; full array
+;
+                if ndata eq 0 then begin
+                        data = 0
+                        goto,done
+                endif 
+                if naxis gt 8 then begin
+                        message = 'Maximum value of NAXIS allowed is 8'
+                        goto,error_exit
+                endif
+                data = make_array(dim = axis, type = idl_type, /nozero)
+        endelse
+;
+; read array
+;
+        if fcompress then mrd_skip,fcb.unit,skip else $
+	                 point_lun,fcb.unit,position
+        readu,fcb.unit,data
+	if fcompress then swap_endian_inplace,data,/swap_if_little
+        if ~keyword_set(No_Unsigned) && (~data_only) then begin
+        if unsgn_int then begin 
+                data =  uint(data) - uint(32768) 
+        endif else if unsgn_lng then begin 
+                data = ulong(data) - ulong(2147483648)
+        endif
+	endif
+;
+; scale data if header was read and first and last not used.   Do a special
+; check of an unsigned integer (BZERO = 2^15) or unsigned long (BZERO = 2^31) 
+;
+        if (data_only eq 0) && (last eq 0) && (noscale eq 0) then begin
+
+                if bitpix lt 32 then begin      ;use real*4 for bitpix<32
+                        bscale = float(bscale)
+                        bzero = float(bzero)
+                endif
+                if bscale ne 1.0 then data *= bscale
+                if bzero ne 0.0 then data +=  bzero
+ 	endif
+;
+; done
+;
+done:   
+        if fcompress then begin 
+	        free_lun,fcb.unit 
+		ff = strmid(fcb.filename,1,strlen(fcb.filename)-2)
+;Rewind the file to the beginning, if it might be used again			
+		if fcbtype NE 7 then begin 
+		 spawn,ff,unit=unit,/sh, stderr = stderr		
+		 fcb.unit = unit
+		endif
+        endif else $		
+        if fcbtype eq 7 then fits_close,fcb else file_or_fcb.last_extension=enum
+        !err = 1
+        return
+
+;
+; error exit
+;
+ioerror:
+        message = !ERROR_STATE.MSG
+error_exit:
+        if (fcbtype eq 7) && (N_elements(fcb) GT 0) then  $
+                   fits_close,fcb, no_abort=no_abort
+        !err = -1
+        if keyword_set(no_abort) then return
+        print,'FITS_READ ERROR: '+message
+        retall
+end
diff --git a/Code/script_idl_mv/astrolib/fits_test_checksum.pro b/Code/script_idl_mv/astrolib/fits_test_checksum.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0ca0e512e338f40f4e039a51bb0b77aad950bf2b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_test_checksum.pro
@@ -0,0 +1,109 @@
+ function fits_test_checksum,hdr, data, ERRMSG = errmsg,FROM_IEEE=from_ieee
+;+
+; NAME:
+;    FITS_TEST_CHECKSUM()
+; PURPOSE:
+;    Verify the values of the CHECKSUM and DATASUM keywords in a FITS header 
+; EXPLANATION: 
+;     Follows the 2007 version of the FITS checksum proposal at 
+;     http://fits.gsfc.nasa.gov/registry/checksum.html
+; 
+; CALLING SEQUENCE:
+;    result = FITS_TEST_CHECKSUM(HDR, [ DATA, ERRMSG=, /FROM_IEEE ])
+; INPUTS:
+;    HDR - FITS header (vector string)
+; OPTIONAL DATA:
+;    DATA - data array associated with the FITS header.   An IDL structure is 
+;           not allowed.    If not supplied, or
+;           set to a scalar, then there is assumed to be no data array 
+;           associated with the FITS header.
+; RESULT:
+;     An integer -1, 0 or 1 indicating the following conditions:
+;           1 - CHECKSUM (and DATASUM) keywords are present with correct values
+;           0 - CHECKSUM keyword is not present
+;          -1 - CHECKSUM or DATASUM keyword does not have the correct value
+;               indicating possible data corruption.
+; OPTIONAL INPUT KEYWORD:
+;    /FROM_IEEE - If this keyword is set, then the input is assumed to be in 
+;             big endian format (e.g. an untranslated FITS array).    This 
+;             keyword only has an effect on little endian machines (e.g. 
+;             a Linux box).
+; OPTIONAL OUTPUT KEYWORD:
+;     ERRMSG - will contain a scalar string giving the error condition.   If
+;              RESULT = 1 then ERRMSG will be an empty string.   If this 
+;              output keyword is not supplied, then the error message will be
+;              printed at the terminal.
+; NOTES:
+;     The header and data must be *exactly* as originally written in the FITS 
+;     file.  By default, some FITS readers may alter keyword values (e.g. 
+;     BSCALE) or append information (e.g. HISTORY or an inherited primary 
+;     header) and this will alter the checksum value.           
+; PROCEDURES USED:
+;    CHECKSUM32, FITS_ASCII_ENCODE(), SXPAR()
+; EXAMPLE:
+;     Verify the CHECKSUM keywords in the primary header/data unit of a FITS 
+;     file 'test.fits'
+;
+;     FITS_READ,'test.fits',data,hdr,/no_PDU,/NoSCALE
+;     print,FITS_TEST_CHECKSUM(hdr,data)
+;
+;     Note the use of the /No_PDU and /NoSCALE keywords to avoid any alteration 
+;     of the FITS header
+; REVISION HISTORY:
+;     W. Landsman  SSAI               December 2002
+;     Return quietly if CHECKSUM keywords not found W. Landsman May 2003
+;     Add /NOSAVE to CHECKSUM32 calls when possible W. Landsman Sep 2004
+;-
+  On_error,2 
+  compile_opt idl2 
+  
+  if N_Params() LT 1 then begin
+      print,'Syntax - result = FITS_TEST_CHECKSUM(Hdr, [Data,' +  $
+                               ' ERRMSG=, /FROM_IEEE ])'
+      return, 0
+  endif
+  result = 1
+  printerr = ~arg_present(errmsg)
+  checksum = sxpar(hdr,'CHECKSUM', Count = N_checksum)
+  datasum = sxpar(hdr,'DATASUM', Count = N_datasum)
+  if (N_checksum EQ 0) then begin
+      errmsg = 'CHECKSUM keyword not present in FITS header'
+      if printerr then message,/con, errmsg
+      return, 0
+  endif 
+  if N_datasum EQ 0 then datasum = '0' 
+  ch  = shift(byte(checksum),-1)
+  checksum32,ch-48b, sum32, /NOSAVE
+  bhdr = byte(hdr)
+  remain = N_elements(bhdr) mod 2880 
+  if remain  NE 0 then $
+       bhdr = [reform(bhdr,N_elements(bhdr)), replicate(32b, 2880 - remain) ]
+  checksum32,bhdr, hsum, FROM_IEEE = from_ieee, /NOSAVE
+  Ndata = N_elements(data)
+  if Ndata GT 1 then begin 
+           checksum32, data, dsum, FROM_IEEE= from_ieee
+           remain = Ndata mod 2880
+           if remain GT 0 then begin
+              exten = sxpar( hdr, 'XTENSION', Count = N_exten)
+              if N_exten GT 0 then if exten EQ 'TABLE   ' then $
+                      checksum32,[dsum,replicate(32b,2880-remain)],dsum,/NOSAVE
+           endif
+           checksum32, [dsum, hsum], hdusum, /NOSAVE
+           dsum = strtrim(dsum,2)
+           if dsum NE datasum then begin
+                  result = 1
+                  errmsg = 'Computed Datasum: ' + dsum + $
+                           ' FITS header value: ' + datasum
+                  if printerr then message,/Con, errmsg 
+           endif
+  endif else hdusum = hsum
+
+  csum = FITS_ASCII_ENCODE(not hdusum)
+  if csum NE '0000000000000000' then begin
+                  result = -1
+                  errmsg = 'Computed Checksum: ' + csum + $
+                           ' FITS header value: ' + checksum
+                   if printerr then message,/Con, errmsg 
+  endif
+  return, result
+  end
diff --git a/Code/script_idl_mv/astrolib/fits_write.pro b/Code/script_idl_mv/astrolib/fits_write.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5ce3af2bdee32b89ed61f1b53000714cbfdd485b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fits_write.pro
@@ -0,0 +1,379 @@
+pro fits_write,file_or_fcb,data,header_in,extname=extname,extver=extver, $
+		xtension=xtension, extlevel=extlevel, $
+		no_abort=no_abort, message = message, header = header, $
+		no_data = no_data
+;+
+; NAME:
+;	FITS_WRITE
+;
+; PURPOSE:
+;	To write a FITS primary data unit or extension.
+;
+; EXPLANATION:
+;       ***NOTE** This version of FITS_READ must be used with a post Sep 2006
+;          version of FITS_OPEN.
+;
+; CALLING SEQUENCE:
+;	FITS_WRITE, filename_or_fcb, data, [header_in]
+;
+; INPUTS:
+;	FILENAME_OR_FCB: name of the output data file or the FITS control
+;		block returned by FITS_OPEN (called with the /WRITE or
+;		/APPEND) parameters.
+;
+; OPTIONAL INPUTS:
+;	DATA: data array to write.  If not supplied or set to a scalar, a
+;		null image is written.
+;	HEADER_IN: FITS header keyword.  If not supplied, a minimal basic
+;		header will be created.  Required FITS keywords, SIMPLE,
+;		BITPIX, XTENSION, NAXIS, ... are added by FITS_WRITE and
+;		do not need to be supplied with the header.  If supplied,
+;		their values will be updated as necessary to reflect DATA.
+;
+; INPUT KEYWORD PARAMETERS:
+;
+;	XTENSION: type of extension to write (Default="IMAGE"). If not
+;		supplied, it will be taken from HEADER_IN.  If not in either
+;		place, the default is "IMAGE".  This parameter is ignored
+;		when writing the primary data unit.     Note that binary and
+;               and ASCII table extensions already have a properly formatted
+;               header (e.g. with TTYPE* keywords) and byte array data. 
+;	EXTNAME: EXTNAME for the extension.  If not supplied, it will be taken
+;		from HEADER_IN.  If not supplied and not in HEADER_IN, no
+;		EXTNAME will be written into the output extension.
+;	EXTVER: EXTVER for the extension.  If not supplied, it will be taken
+;               from HEADER_IN.  If not supplied and not in HEADER_IN, no
+;               EXTVER will be written into the output extension.
+;	EXTLEVEL: EXTLEVEL for the extension.  If not supplied, it will be taken
+;               from HEADER_IN.  If not supplied and not in HEADER_IN, no
+;               EXTLEVEL will be written into the output extension.
+;       /NO_ABORT: Set to return to calling program instead of a RETALL
+;               when an I/O error is encountered.  If set, the routine will
+;               return  a non-null string (containing the error message) in the
+;               keyword MESSAGE.   If /NO_ABORT not set, then FITS_WRITE will 
+;               print the message and issue a RETALL
+;	/NO_DATA: Set if you only want FITS_WRITE to write a header.  The
+;		header supplied will be written without modification and
+;		the user is expected to write the data using WRITEU to unit
+;		FCB.UNIT. When FITS_WRITE is called with /NO_DATA, the user is
+;		responsible for the validity of the header, and must write
+;		the correct amount and format of the data.  When FITS_WRITE
+;		is used in this fashion, it will pad the data from a previously
+;		written extension to 2880 blocks before writting the header.
+;
+; OUTPUT KEYWORD PARAMETERS:
+;       MESSAGE: value of the error message for use with /NO_ABORT
+;	HEADER: actual output header written to the FITS file.
+;
+; NOTES:
+;	If the first call to FITS_WRITE is an extension, FITS_WRITE will
+;	automatically write a null image as the primary data unit.
+;
+;	Keywords and history in the input header will be properly separated
+;	into the primary data unit and extension portions when constructing
+;	the output header (See FITS_READ for information on the internal
+;	Header format which separates the extension and PDU header portions).
+;	
+; EXAMPLES:
+;	Write an IDL variable to a FITS file with the minimal required header.
+;		FITS_WRITE,'newfile.fits',ARRAY
+;
+;	Write the same array as an image extension, with a null Primary data
+;	unit.
+;		FITS_WRITE,'newfile.fits',ARRAY,xtension='IMAGE'
+;
+;	Write 4 additional image extensions to the same file.
+;		FITS_OPEN,'newfile.fits',fcb
+;		FITS_WRITE,fcb,data1,extname='FLUX',extver=1
+;		FITS_WRITE,fcb,err1,extname'ERR',extver=1
+;		FITS_WRITE,fcb,data2,extname='FLUX',extver=2
+;		FITS_WRITE,fcb,err2,extname='ERR',extver=2
+;		FITS_CLOSE,FCB
+;		
+; WARNING: 
+;       FITS_WRITE currently does not completely update the file control block.
+;       When mixing FITS_READ and FITS_WRITE commands it is safer to use 
+;       file names, rather than passing the file control block.
+; PROCEDURES USED:
+;	FITS_OPEN, SXADDPAR, SXDELPAR, SXPAR()
+; HISTORY:
+;	Written by:	D. Lindler	August, 1995
+;	Work for variable length extensions  W. Landsman   August 1997
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;	PCOUNT and GCOUNT added for IMAGE extensions   J. Graham  October 1999
+;       Write unsigned data types      W. Landsman   December 1999
+;       Pad data area with zeros not blanks  W. McCann/W. Landsman October 2000
+;       Return Message='' to signal normal operation W. Landsman Nov. 2000
+;       Ensure that required extension table keywords are in proper order
+;             W.V. Dixon/W. Landsman          March 2001
+;       Assume since V5.1, remove NaNValue keyword   W. Landsman Nov. 2002
+;       Removed obsolete !ERR system variable  W. Landsman Feb 2004
+;       Check that byte array supplied with table extension W. Landsman Mar 2004
+;       Make number of bytes 64bit to avoid possible overflow W.L  Apr 2006
+;       Assume FITS_OPEN has opened the file with /SWAP_IF_LITTLE_ENDIAN
+;                         W. Landsman   September 2006
+;       Removes BZERO and BSCALE for floating point output, D. Lindler, Sep 2008
+;-
+;-----------------------------------------------------------------------------
+;
+; print calling sequence if no parameters supplied
+;
+	if n_params() lt 1 then begin
+	    print,'Calling Sequence: FITS_WRITE,file_or_fcb,data,header_in'
+	    print,'Input Keywords: extname, extver, xtension, extlevel,' + $
+                                    '/no_abort, /no_data'
+	    print,'Output Keywords:  message, header ' 
+	    return
+	end
+;
+; Open file if file name is supplied instead of a FCB
+;
+        message = ''
+        s = size(file_or_fcb) & fcbtype = s[s[0]+1]
+	fcbsize = n_elements(file_or_fcb)
+        if (fcbsize ne 1) || ((fcbtype ne 7) && (fcbtype ne 8)) then begin
+                message = 'Invalid Filename or FCB supplied'
+                goto,error_exit
+        end
+
+        if fcbtype eq 7 then begin
+		if keyword_set(no_data) then begin
+			print,'FITS_WRITE: Must have FCB supplied for NO_DATA'
+			retall
+		endif
+                fits_open,file_or_fcb,fcb,/write, $
+					no_abort=no_abort,message=message
+		if message NE '' then goto,error_exit
+           end else fcb = file_or_fcb
+;
+; if user did not pad data to 2880 blocks, pad it now
+;
+	point_lun,-fcb.unit,current_position
+	npad = 2880 - (current_position mod 2880)
+	if npad eq 2880 then npad = 0
+	if npad gt 0 then writeu,fcb.unit,bytarr(npad)
+;
+; if no_data, just go and write user header as supplied
+;
+	if keyword_set(no_data) then begin
+		header = header_in
+		goto,write_header
+	end
+;
+; if header not supplied then set it to a null header
+;
+	if n_elements(header_in) le 1 then begin
+		header = strarr(1)
+		header[0] = 'END     '
+	end else header = header_in
+
+;
+; on I/O error go to statement IOERROR
+;
+;	on_ioerror,ioerror
+;
+; verify file is open for writing
+;
+	if fcb.open_for_write eq 0 then begin
+		message,'File is not open for writing'
+		goto,error_exit
+	endif
+;
+; determine bitpix and axis information
+;
+	s = size(data)
+	naxis = s[0]
+	if naxis gt 0 then axis = s[1:naxis]
+	idltype = s[naxis+1]
+
+	if (idltype gt 5) && (idltype NE 12) && (idltype NE 13) then begin
+		message='Data array is an invalid type'
+		goto,error_exit
+	endif
+	bitpixs = [8,8,16,32,-32,-64,0,0,0,0,0,0,16,32]
+	bitpix = bitpixs[idltype]
+;
+; determine extname, extver, xtension and extlevel and delete current values
+;
+	if n_elements(xtension) gt 0 then begin
+		Axtension = xtension
+	   end else begin
+		Axtension = sxpar(header,'xtension', Count = N_Axtension)
+		if N_Axtension EQ 0 then Axtension = ''
+ 	end
+        if Axtension EQ 'BINTABLE' or (Axtension EQ 'TABLE') then $
+                if idltype GT 1 then begin
+                     message='A Byte array must be supplied with a ' + $
+                             'BINTABLE or TABLE extension'
+                     goto, error_exit
+                 endif
+
+	if n_elements(extname) gt 0 then begin
+		Aextname = extname
+	   end else begin
+		Aextname = sxpar(header,'extname', Count = N_Aextname)
+		if N_Aextname EQ 0 then Aextname = ''
+	end
+
+	if n_elements(extver) gt 0 then $
+		Aextver = extver $
+		else Aextver = sxpar(header,'extver')
+
+	if n_elements(extlevel) gt 0 then $
+		Aextlevel = extlevel $
+		else Aextlevel = sxpar(header,'extlevel')
+
+	sxdelpar,header,['XTENSION','EXTNAME','EXTVER','EXTLEVEL']
+
+;
+; separate header into main and extension header
+;
+	keywords = strmid(header,0,8)
+	hpos1 = where(keywords eq 'BEGIN MA') & hpos1 = hpos1[0] ;begin main
+	hpos2 = where(keywords eq 'BEGIN EX') & hpos2 = hpos2[0] ;begin ext.
+	hpos3 = where(keywords eq 'END     ') & hpos3 = hpos3[0] ;end of header	
+
+	if (hpos1 gt 0) && (hpos2 lt hpos1) then begin
+		message,'Invalid header BEGIN EXTENSION HEADER ... out of place'
+		goto,error_exit
+	endif
+
+	if (hpos3 lt 0) then begin
+		print,'FITS_WRITE: END missing from input header and was added'
+		header = [header,'END     ']
+		hpos2 = n_elements(header)-1
+	end
+;
+; determine if a extension was supplied and no primary data unit (PDU)
+; was written
+;
+	if (fcb.nextend eq -1) then begin		;no pdu written yet?
+	    if (hpos2 gt 0) || (Axtension ne '') || (Aextname ne '') || $
+	       (Aextver ne 0) || (Aextlevel ne 0) then begin
+;
+; write null image PDU
+;
+			if (hpos1 gt 0) && (hpos2 gt (hpos1+1)) then $
+				hmain = [header[hpos1+1:hpos2-1],'END     ']
+			fits_write,fcb,0,hmain,/no_abort,message=message
+			if message NE '' then goto,error_exit
+	    end
+	end
+;
+; For extensions, do not use PDU portion of the header
+;
+	if (hpos2 gt 0) then header = header[hpos2+1:hpos3]
+;
+; create required keywords for the header
+;
+	h = strarr(20)
+	h[0] = 'END     '
+
+	if fcb.nextend eq -1 then begin
+		sxaddpar,h,'SIMPLE','T','image conforms to FITS standard' 
+	   end else begin
+		if Axtension eq '' then Axtension = 'IMAGE   '
+		sxaddpar,h,'XTENSION',Axtension,'extension type'
+	end
+	sxaddpar,h,'BITPIX',bitpix,'bits per data value'
+	sxaddpar,h,'NAXIS',naxis,'number of axes'
+	if naxis gt 0 then for i=1,naxis do $
+		sxaddpar,h,'NAXIS'+strtrim(i,2),axis[i-1]
+	if fcb.nextend eq -1 then begin
+		sxaddpar,h,'EXTEND','T','file may contain extensions'
+	end else begin    ;PCOUNT, GCOUNT are mandatory for extensions
+ 		sxaddpar,h,'PCOUNT',0
+ 		sxaddpar,h,'GCOUNT',1
+                if (Axtension eq 'BINTABLE') || $
+                   (Axtension eq 'TABLE   ') then begin
+                       tfields = sxpar(header,'TFIELDS') > 0              
+                       sxaddpar,h,'TFIELDS',tfields
+                endif 
+		if Aextname ne '' then sxaddpar,h,'EXTNAME',Aextname
+		if Aextver gt 0 then sxaddpar,h,'EXTVER',Aextver
+		if Aextlevel gt 0 then sxaddpar,h,'EXTLEVEL',Aextlevel
+	endelse
+        if idltype EQ 12 then $
+               sxaddpar,header,'BZERO',32768,'Data is unsigned integer'
+        if idltype EQ 13 then $
+               sxaddpar,header,'BZERO',2147483648,'Data is unsigned long'
+        if idltype GE 12 then sxdelpar,header,'BSCALE'
+	if (idltype EQ 4) || (idltype EQ 5) then $
+	          sxdelpar,header,['BSCALE','BZERO']
+;
+; delete special keywords from user supplied header
+;
+	pcount = sxpar(header,'pcount')
+        groups = sxpar(header,'groups')
+        sxdelpar,header,['SIMPLE','BITPIX','NAXIS','NAXIS1','NAXIS2','NAXIS3', $
+			'NAXIS4','NAXIS5','NAXIS6','NAXIS7','NAXIS8','EXTEND', $
+                        'PCOUNT','GCOUNT','GROUPS','TFIELDS']
+        if groups then if (pcount gt 0) then for i=1,pcount do $
+                        sxdelpar,header,['ptype','pscal','pzero']+strtrim(i,2)
+;
+; combine the two headers
+;
+	last = where(strmid(h,0,8) eq 'END     ')
+	header = [h[0:last[0]-1],header]
+
+;
+; convert header to bytes and write
+;
+write_header:
+	last = where(strmid(header,0,8) eq 'END     ')
+	n = last[0] + 1
+	byte_header = replicate(32b,80,n)
+	for i=0,n-1 do byte_header[0,i] = byte(header[i])
+	writeu,fcb.unit,byte_header
+;
+; pad header to 2880 byte records
+;
+	npad = 2880 - (80L*n mod 2880)	
+	if npad eq 2880 then npad = 0
+	if (npad gt 0) then writeu,fcb.unit,replicate(32b,npad)
+	nbytes_header =  npad + n*80
+	if keyword_set(no_data) then return
+;
+; process data
+;
+	if naxis gt 0 then begin
+;
+; convert to IEEE
+;
+            unsigned = (idltype EQ 12) || (idltype EQ 13)
+            if idltype EQ 12 then newdata = fix(data - 32768)
+            if idltype EQ 13 then newdata = long(data - 2147483648)
+;
+; write the data
+;
+	    nbytes = long64(N_elements(data)) * (abs(bitpix)/8)
+	    npad = 2880 - (nbytes mod 2880)
+	    if npad eq 2880 then npad = 0
+	    if unsigned then writeu,fcb.unit,newdata else writeu,fcb.unit,data
+            if npad gt 0 then begin
+                if Axtension EQ 'TABLE   ' then padnum = 32b else padnum = 0b
+	        writeu,fcb.unit,replicate(padnum,npad)
+            endif
+	    nbytes_data = nbytes + npad
+	  end else begin
+	    nbytes_data = 0
+	end
+;
+; done, update file control block
+;
+	fcb.nextend = fcb.nextend + 1
+	if fcbtype eq 7 then fits_close,fcb else file_or_fcb = fcb
+        !err = 1
+	return
+;
+; error exit
+;
+ioerror:
+	message = !error_state.msg
+error_exit:
+	if fcbtype eq 7 then free_lun,fcb.unit
+        !err = -1
+	if keyword_set(no_abort) then return
+	message,' ERROR: '+message,/CON
+	retall
+end
diff --git a/Code/script_idl_mv/astrolib/fitsdir.pro b/Code/script_idl_mv/astrolib/fitsdir.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d674003a78df03b83cbfbdf5aafcbd190b555f05
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fitsdir.pro
@@ -0,0 +1,332 @@
+pro fitsdir ,directory, TEXTOUT = textout, Keywords = keywords, $ 
+     nosize = nosize, alt1_keywords=alt1_keywords, alt2_keywords=alt2_keywords,$
+     alt3_keywords = alt3_keywords, NoTelescope = NoTelescope,exten=exten
+;+
+; NAME:
+;     FITSDIR 
+; PURPOSE:
+;     Display selected FITS keywords from the headers of FITS files.   
+; EXPLANATION:
+;
+;     The values of either user-specified or default FITS keywords are 
+;     displayed in either the primary header and/or the first extension header.
+;     Unless the /NOSIZE keyword is set, the data size is also displayed.
+;     The default keywords are as follows (with keywords in 2nd row used if
+;     those in the first row not found, and the 3rd row if neither the keywords
+;     in the first or second rows found:)
+;
+;     DATE-OBS     TELESCOP   OBJECT    EXPTIME       
+;     TDATEOBS     TELNAME    TARGNAME  INTEG        ;First Alternative
+;     DATE         OBSERVAT             EXPOSURE     ;Second Alternative
+;                  INSTRUME             EXPTIM       ;Third Alternative
+;
+;      FITSDIR will also recognize gzip compressed files (must have a .gz 
+;      or FTZ extension).
+; CALLING SEQUENCE:
+;     FITSDIR , [ directory, TEXTOUT =, EXTEN=, KEYWORDS=, /NOSIZE, /NoTELESCOPE
+;                            ALT1_KEYWORDS= ,ALT2_KEYWORDS = ,ALT3_KEYWORDS =  
+;
+; OPTIONAL INPUT PARAMETERS:
+;     DIRECTORY - Scalar string giving file name, disk or directory to be 
+;             searched.   Wildcard file names are allowed.    Examples of 
+;             valid names include 'iraf/*.fits' (Unix) or 'd:\myfiles\f*.fits',
+;             (Windows).  
+;            
+; OPTIONAL KEYWORD INPUT PARAMETER
+;      KEYWORDS - FITS keywords to display, as either a vector of strings or as
+;                 a comma delimited scalar string, e.g.'testname,dewar,filter'
+;                 If not supplied, then the default keywords are 'DATE-OBS',
+;                 'TELESCOP','OBJECT','EXPTIME'
+;      ALT1_KEYWORDS - A list (either a vector of strings or a comma delimited
+;                 strings of alternative keywords to use if the default 
+;                 KEYWORDS cannot be found.   By default, 'TDATEOBS', is the 
+;                 alternative to DATE-OBS, 'TELNAME' for 'TELESCOP','TARGNAME'
+;                 for 'OBJECT', and 'INTEG' for EXPTIME
+;      ALT2_KEYWORDS - A list (either a vector of strings or a comma delimited
+;                 strings of alternative keywords to use if neither KEYWORDS 
+;                 nor ALT1_KEYWORDS can be found.    
+;      ALT3_KEYWORDS - A list (either a vector of strings or a comma delimited
+;                 strings of alternative keywords to use if neither KEYWORDS 
+;                 nor ALT1_KEYWORDS nor ALT2_KEYWORDS can be found.    
+;      /NOSIZE - if set then information about the image size is not displayed  
+;      TEXTOUT - Controls output device as described in TEXTOPEN procedure
+;               textout=1       TERMINAL using /more option
+;               textout=2       TERMINAL without /more option
+;               textout=3       <program>.prt
+;               textout=4       laser.tmp
+;               textout=5       user must open file
+;               textout=7       Append to existing <program>.prt file
+;               textout = filename (default extension of .prt)
+;       EXTEN - Specifies an extension number (/EXTEN works for first extension)
+;               which is  checked for the  desired keywords.    
+;       /NOTELESCOPE - If set, then if the default keywords are used, then the
+;                TELESCOPE (or TELNAME, OBSERVAT, INSTRUME) keywords are omitted
+;                to give more room for display other keywords.   The /NOTELESCOP
+;                 keyword has no effect if the default keywords are not used.
+; OUTPUT PARAMETERS:
+;       None.
+;
+; EXAMPLES:  
+;  (1) Print info on all'*.fits' files in the current  directory using default
+;          keywords.   Include information from the extension header     
+;       IDL> fitsdir,/exten
+;
+;  (2) Write a driver program to display selected keywords in HST/ACS drizzled
+;       (*drz) images
+;         pro acsdir
+;        keywords = 'date-obs,targname,detector,filter1,filter2,exptime'
+;        fitsdir,'*drz.fits',key=keywords,/exten
+;        return & end
+;
+;   (3)  Write info on all *.fits files in the Unix directory /usr2/smith, to a 
+;       file 'smith.txt' using the default keywords, but don't display the value
+;        of the TELESCOPE keyword
+;
+;       IDL> fitsdir ,'/usr2/smith/*.fits',t='smith.txt', /NoTel 
+;
+; PROCEDURE:
+;       FILE_SEARCH()  is used to find the specified FITS files.   The 
+;       header of each file is read, and the selected keywords are extracted.
+;       The formatting is adjusted so that no value is truncated on display.        
+;
+; SYSTEM VARIABLES:
+;       TEXTOPEN (called by FITSDIR) will automatically define the following 
+;       non-standard system variables if they are not previously defined:
+;
+;       DEFSYSV,'!TEXTOUT',1
+;       DEFSYSV,'!TEXTUNIT',0
+;
+; PROCEDURES USED:
+;       FDECOMP, FXMOVE, MRD_HREAD, REMCHAR
+;       TEXTOPEN, TEXTCLOSE
+; MODIFICATION HISTORY:
+;       Written, W. Landsman,  HSTX    February, 1993
+;       Search alternate keyword names    W.Landsman    October 1998
+;       Avoid integer truncation for NAXISi >32767  W. Landsman  July 2000
+;       Don't leave open unit    W. Landsman  July 2000 
+;       Added EXTEN keyword, work with compressed files, additional alternate
+;       keywords W. Landsman     December 2000
+;       Don't assume floating pt. exposure time W. Landsman   September 2001
+;       Major rewrite, KEYWORD & ALT*_KEYWORDS keywords, no truncation, 
+;             /NOSIZE keyword     W. Landsman,  SSAI   August 2002
+;       Assume V5.3 or later W. Landsman November 2002
+;       Fix case where no keywords supplied  W. Landsman January 2003
+;       NAXIS* values must be integers W. Landsman SSAI  June 2003
+;       Trim spaces off of input KEYWORD values W. Landsman March 2004
+;       Treat .FTZ extension as gzip compressed  W. Landsman September 2004
+;       Assume since V5.5, file_search() available W. Landsman Aug 2006
+;       Don't assume all images compressed or uncompressed W. L. Apr 2010
+;       Use V6.0 notation W.L. Feb 2011
+;       Don't let a corrupted file cause an abort    W.L. Feb 2014
+;-
+; On_error,2
+
+ compile_opt idl2     
+ 
+ if N_elements(directory) EQ 0 then directory = '*.fits'
+ if N_elements(exten) EQ 0 then exten = 0 
+
+ FDECOMP, directory, disk, dir, filename, ext
+ if filename EQ '' then begin 
+      directory = disk + dir + '*.fits'
+      filename = '*'
+      ext = 'fits'
+ endif else if !VERSION.OS_FAMILY EQ 'unix' then begin
+        if (strpos(filename,'*') LT 0) && (ext EQ '') then begin  
+        directory = disk + dir + filename + '/*.fits'
+        filename = '*'
+        ext = 'fits'
+        endif
+ endif
+
+ if N_elements(keywords) EQ 0 then begin
+     keywords = ['date-obs','telescop','object','exptime'] 
+     if N_elements(alt1_keywords) EQ 0 then $
+          alt1_keywords = ['tdateobs','telname','targname','integ']
+     if N_elements(alt2_keywords) EQ 0 then $
+          alt2_keywords = ['date','observat','','exposure']
+     if N_elements(alt3_keywords) EQ 0 then $
+          alt3_keywords = ['','instrume','','exptim' ]
+     if keyword_set(NoTelescope) then begin
+        ii = [0,2,3]
+        keywords = keywords[ii] & alt1_keywords = alt1_keywords[ii]
+        alt2_keywords = alt2_keywords[ii] & alt3_keywords = alt3_keywords[ii]
+      endif
+ endif
+ if N_elements(keywords) EQ 1 then $
+   keys = strtrim(strupcase(strsplit(keywords,',',/EXTRACT)),2) else $
+   keys = strupcase(keywords)
+   Nkey = N_elements(keys)
+
+  case N_elements(alt1_keywords) of
+  0: alt1_set = bytarr(Nkey)
+  1: alt1_keys = strtrim(strupcase(strsplit(alt1_keywords[0],',',/EXTRACT)),2) 
+  else: alt1_keys = strupcase(alt1_keywords) 
+  endcase
+  if N_elements(alt1_set) EQ 0 then alt1_set = strlen(strtrim(alt1_keys,2)) GT 0
+
+  case N_elements(alt2_keywords) of
+  0: alt2_set = bytarr(Nkey)
+  1: alt2_keys = strtrim(strupcase(strsplit(alt2_keywords,',',/EXTRACT)),2) 
+  else: alt2_keys = strupcase(alt2_keywords) 
+  endcase
+ if N_elements(alt2_set) EQ 0 then alt2_set = strlen(strtrim(alt2_keys,2)) GT 0
+
+  case N_elements(alt3_keywords) of
+  0: alt3_set = bytarr(Nkey)
+  1: alt3_keys = strtrim(strupcase(strsplit(alt3_keywords,',',/EXTRACT)),2) 
+   else: alt3_keys = strupcase(alt3_keywords) 
+  endcase
+  if N_elements(alt3_set) EQ 0 then alt3_set = strlen(strtrim(alt3_keys,2)) GT 0
+  
+   keylen = strlen(keys)
+ 
+  direct = spec_dir(directory)
+  files = file_search(directory,COUNT = n,/full) 
+
+ if n EQ 0 then begin                                      ;Any files found?
+       message,'No files found on '+ direct, /CON
+       return
+ endif 
+
+  good = where( strlen(files) GT 0, Ngood)
+  if Ngood EQ 0 then message,'No FITS files found on '+ directory $
+                 else files = files[good]
+
+; Set output device according to keyword TEXTOUT or system variable !TEXTOUT
+
+  defsysv,'!TEXTOUT',exists=ex                  ; Check if !TEXTOUT exists.
+  if ex eq 0 then defsysv,'!TEXTOUT',1          ; If not define it.
+  defsysv,'!TEXTUNIT',exists=ex                  ; Check if !TEXTOUT exists.
+  if ex eq 0 then defsysv,'!TEXTUNIT',1          ; If not define it.
+  if ~keyword_set( TEXTOUT ) then textout= !TEXTOUT
+
+ dir = 'dummy'
+ num = 0
+
+ get_lun,unit
+
+ fdecomp, files, disk, dir2, fname, qual     ;Decompose into disk+filename
+ fname = strtrim(fname,2)
+ keyvalue = strarr(n,nkey)
+ bignaxis = strarr(n)
+ namelen = max(strlen(fname))
+
+ for i = 0,n-1 do begin                           ;Loop over each FITS file
+     compress = (qual[i] EQ 'gz') || (strupcase(qual[i]) EQ 'FTZ') 
+     openr, unit, files[i], error = error, compress = compress 
+    if error LT 0 then goto, BADHD
+    mrd_hread, unit, h, status, /silent, ERRMSG = errmsg
+   if status LT 0 then goto, BADHD
+
+   if exten GT 0 then begin 
+         close,unit
+            openr, unit, files[i], error = error, compress = compress   
+         stat = fxmove(unit, exten, /silent)
+         mrd_hread, unit, h1, extstatus, /silent, ERRMSG = errmsg
+         if extstatus EQ 0 then h = [h1,h] 
+    endif 
+
+   keyword = strtrim( strmid(h,0,8),2 )       ;First 8 chars is FITS keyword
+   lvalue = strtrim(strmid(h,10,20),2 ) 
+   value = strtrim( strmid(h,10,68),2 )        ;Chars 10-30 is FITS value
+ 
+ if ~keyword_set(nosize) then begin
+ l= where(keyword EQ 'NAXIS',Nfound)            ;Must have NAXIS keyword
+    if Nfound GT 0 then naxis  = long( lvalue[ l[0] ] ) else goto, BADHD
+
+ if naxis EQ 0 then naxisi = '0' else begin
+
+ l = where( keyword EQ 'NAXIS1', Nfound)         ;Must have NAXIS1 keyword
+    if Nfound gt 0 then naxis1  = long( lvalue[l[0] ] ) else goto, BADHD 
+    naxisi = strtrim( naxis1,2 )
+ endelse
+
+ if NAXIS GE 2 then begin
+ l = where(keyword EQ 'NAXIS2', Nfound)          ;Must have NAXIS2 keyword
+    if Nfound gt 0 then naxis2  = long(lvalue[l[0]]) else goto, BADHD
+    naxisi = naxisi + ' ' + strtrim( naxis2, 2 )
+ endif
+
+ if NAXIS GE 3 then begin
+ l = where( keyword EQ 'NAXIS3', Nfound )          ;Must have NAXIS3 keyword
+    if Nfound GT 0 then naxis3  = long( lvalue[l[0]] ) else goto, BADHD
+    naxisi = naxisi + ' ' + strtrim( naxis3, 2 )
+ endif
+ bignaxis[i] = strtrim(naxisi)
+ endif
+
+ for k=0,nkey-1 do begin
+     l = where(keyword EQ keys[k], Nfound)
+     if Nfound EQ 0 then  if alt1_set[k] then $
+        l = where(keyword EQ alt1_keys[k], Nfound)
+     if Nfound EQ 0 then  if alt2_set[k] then $
+        l = where(keyword EQ alt2_keys[k], Nfound)
+     if Nfound EQ 0 then  if alt3_set[k] then $
+        l = where(keyword EQ alt3_keys[k], Nfound)
+     if nfound GT 0 then begin
+            kvalue = value[l[0]]
+            if strpos(kvalue,"'") GE 0 then begin
+               temp = gettok(kvalue,"'")
+               keyvalue[i,k] = strtrim(gettok(kvalue,"'"),2)
+            endif else keyvalue[i,k] = strtrim(gettok(kvalue,'/'),2) 
+     endif 
+        
+    endfor
+
+ BADHD:  
+
+ close,unit
+ if status LT 0 then begin
+      message,'Bad File: ' + files[i],/Con
+      if N_elements(errmsg) NE 0 then message,errmsg,/CON
+      endif 
+ endfor
+ DONE: 
+ free_lun, unit
+ vallen = lonarr(nkey)
+ for k=0,nkey-1 do vallen[k]  = max(strlen(keyvalue[*,k]))
+
+
+ textopen, 'fitsdir', TEXTOUT=textout,/STDOUT 
+ printf,!TEXTUNIT,' '
+ printf,!TEXTUNIT,'FITS File Directory ' + systime()
+ printf,!TEXTUNIT, direct
+  printf,!TEXTUNIT, ' '
+
+ pheader = ' NAME '
+ if namelen GT 5 then pheader += string(replicate(32b,namelen-5))
+ if ~keyword_set(nosize) then begin 
+    pheader += 'SIZE '
+    naxislen = max(strlen(bignaxis))+1
+    if naxislen GT 5 then pheader += string(replicate(32b,naxislen-5))
+ endif
+ for k=0,nkey-1 do begin
+     pheader += keys[k] + ' '
+     if vallen[k] GT keylen[k] then  $
+        pheader += string(replicate(32b,vallen[k]-keylen[k]))
+ endfor
+  printf,!TEXTUNIT, pheader
+  printf,!TEXTUNIT, ' '
+  xx = namelen + 2
+ fmt = '(A' 
+ if ~keyword_set(nosize) then begin 
+   fmt += ',T' + strtrim(xx,2)
+   xx += (naxislen>4) + 1
+ endif 
+   fmt +=  ',A'  
+ remchar,keyvalue,"'"
+
+ for k=0,nkey-1 do begin 
+  
+   fmt += ',T' + strtrim(xx,2) + ',A'
+   xx += (vallen[k]>keylen[k]) +1
+ endfor
+ fmt += ')'
+
+ for i=0,n-1 do printf, f= fmt, $
+      !TEXTUNIT,fname[i],bignaxis[i], keyvalue[i,*]
+    
+ textclose,textout=textout 
+ return      ;Normal return   
+ end
diff --git a/Code/script_idl_mv/astrolib/fitsrgb_to_tiff.pro b/Code/script_idl_mv/astrolib/fitsrgb_to_tiff.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e3b711ad49444d4c9d2f3a503a6db84ea2cc2cf8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fitsrgb_to_tiff.pro
@@ -0,0 +1,143 @@
+ PRO FITSRGB_to_TIFF, path, rgb_files, tiff_name, BY_PIXEL=by_pixel, $
+                      PREVIEW=preview, RED=r_mix, GREEN=g_mix, BLUE=b_mix
+;+
+; NAME:
+;       FITSRGB_to_TIFF
+; PURPOSE:
+;       Combine separate red, green, and blue FITS images into TIFF format
+; EXPLANATION:
+;       The output TIFF (class R) file can have colors interleaved either
+;       by pixel or image.  The colour mix is also adjustable.
+;
+; CALLING SEQUENCE:
+;       FITSRGB_to_TIFF, path, rgb_files, tiff_name [,/BY_PIXEL, /PREVIEW,
+;                         RED= , GREEN =, BLUE =]
+;
+; INPUTS:
+;       path = file system directory path to the RGB files required.
+;       rgb_files = string array with three components - the red FITS file
+;                   filename, the blue FITS file filename and the green FITS
+;                   file filename
+;
+; OUTPUTS:
+;       tiff_name = string containing name of tiff file to be produced
+;
+; OPTIONAL OUTPUT:
+;       Header = String array containing the header from the FITS file.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       BY_PIXEL = This causes TIFF file RGB to be interleaved by pixel
+;                  rather than the default of by image.
+;       PREVIEW  = Allows a 24 bit image to be displayed on the screen
+;                  to check the colour mix.
+;       RED = Real number containing the fractional mix of red
+;       GREEN = Real number containing the fractional mix of green
+;       BLUE = Real number containing the fractional mix of blue
+;
+; EXAMPLE:
+;       Read three FITS files, 'red.fits', 'blue.fits' and 'green.fits' from
+;       the directory '/data/images/space' and output a TIFF file named
+;       'colour.tiff'
+;
+;               IDL> FITSRGB_to_TIFF, '/data/images/space', ['red.fits', $
+;                    'blue.fits', 'green.fits'], 'colour.tiff'
+;
+;       Read three FITS files, 'red.fits', 'blue.fits' and 'green.fits' from
+;       the current directory and output a TIFF file named '/images/out.tiff'
+;       In this case, the red image is twice as strong as the green and the
+;       blue is a third more intense.  A preview on screen is also wanted.
+;
+;               IDL> FITSRGB_to_TIFF, '.', ['red.fits', $
+;                    'blue.fits', 'green.fits'], '/images/out.tiff', $
+;                    /PREVIEW, RED=0.5, GREEN=1.0, BLUE=0.666
+;
+;
+; RESTRICTIONS:
+;       (1) Limited to the ability of the routine READFITS
+;
+; NOTES:
+;       None
+;
+; PROCEDURES USED:
+;     Functions:   READFITS, CONCAT_DIR
+;     Procedures:  WRITE_TIFF
+;
+; MODIFICATION HISTORY:
+;     16th January 1995 - Written by Carl Shaw, Queen's University Belfast
+;	27 Jan 1995 - W. Landsman, Add CONCAT_DIR for VMS, Windows compatibility
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;    Use WRITE_TIFF instead of obsolete TIFF_WRITE  W. Landsman  December 1998
+;    Cosmetic changes  W. Landsman    February 2000
+;-
+;
+;  Make sure user has supplied valid parameters
+;
+ IF N_PARAMS() LT 3 THEN BEGIN
+      print, 'Syntax -  FITSRGB_to_TIFF, path, rgb_files, tiff_name'
+      print,'                     [/BY_PIXEL,/PREVIEW, RED=, GREEN=, BLUE= ]'
+      return
+ ENDIF
+;
+ IF N_ELEMENTS(rgb_files) LT 3 THEN $
+  MESSAGE, 'Three filenames for the colour components have not been supplied'
+;
+  by_pixel =  KEYWORD_SET(BY_PIXEL)
+;
+ IF ~KEYWORD_SET(r_mix) THEN r_mix = 1.0
+ IF ~KEYWORD_SET(g_mix) THEN g_mix = 1.0
+ IF ~KEYWORD_SET(b_mix) THEN b_mix = 1.0
+;
+;  Now load the colour components
+;
+ fname = CONCAT_DIR( path, rgb_files )
+ red = READFITS( fname[0], /SILENT)
+ green = READFITS( fname[1], /SILENT)
+ blue = READFITS( fname[2], /SILENT)
+;
+;  Data now needs to be scaled to the same byte range (0-255) and also
+;  scaled according to the RGB mix values supplied by the user
+;
+ red = red[*,*] * r_mix
+ green = green[*,*] * g_mix
+ blue = blue[*,*] * b_mix          ;scale intensity by supplied mix
+;
+ maxlim = MAX(red) > MAX(green) > MAX(blue)   ;max intensity
+ minlim = MIN(red) < MIN(green) < MIN(blue)   ;min intensity
+ red = BYTSCL(red, MIN=minlim, MAX=maxlim)
+ green = BYTSCL(green, MIN=minlim, MAX=maxlim)
+ blue = BYTSCL(blue, MIN=minlim, MAX=maxlim)  ;scale colours to same byte range
+;
+;  Preview image on window system if required
+;
+ IF keyword_set(PREVIEW) THEN BEGIN
+  window, 0, colors=256
+  wset, 0
+  tv, color_quan(red, green, blue, r, g, b, colors=255)
+  tvlct, r, g, b
+ ENDIF
+;
+; Now write out result as a tiff file
+;
+ IF by_pixel THEN BEGIN
+  ;
+  ;  Interleave by pixel
+  ;
+  extent = SIZE(red)
+  xsize = extent[1]
+  ysize = extent[2]                       ;get image size
+  interarr = FLTARR(3, xsize, ysize, /NOZERO)      ;make interleaved array
+  interarr[0, *, *] = red
+  interarr[1, *, *] = green
+  interarr[2, *, *] = blue
+  ;
+  WRITE_TIFF, tiff_name, interarr
+  ;
+ ENDIF ELSE BEGIN
+  ;
+  ;  Interleave by image
+  ;
+  WRITE_TIFF, tiff_name, RED=red, BLUE=blue, GREEN=green, PLANARCONFIG=2
+  ;
+ ENDELSE
+;
+ END
diff --git a/Code/script_idl_mv/astrolib/flegendre.pro b/Code/script_idl_mv/astrolib/flegendre.pro
new file mode 100644
index 0000000000000000000000000000000000000000..00fb5baf5ec526084820b317d3f8bbcad430c051
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/flegendre.pro
@@ -0,0 +1,74 @@
+function flegendre,x,m
+;+
+; NAME:
+;        FLEGENDRE
+; PURPOSE:
+;       Compute the first M terms in a Legendre polynomial expansion.  
+; EXPLANATION:
+;       Meant to be used as a supplied function to SVDFIT.
+;
+;       This procedure became partially obsolete in IDL V5.0 with the 
+;       introduction of the /LEGENDRE keyword to SVDFIT and the associated 
+;       SVDLEG function.    However, note that, unlike SVDLEG, FLEGENDRE works
+;       on vector values of X.     
+; CALLING SEQUENCE:
+;       result = FLEGENDRE( X, M)
+;
+; INPUTS:
+;       X - the value of the independent variable, scalar or vector
+;       M - number of term of the Legendre expansion to compute, integer scalar 
+;
+; OUTPUTS:
+;       result - (N,M) array, where N is the number of elements in X and M
+;               is the order.   Contains the value of each Legendre term for
+;               each value of X
+; EXAMPLE:
+;       (1) If x = 2.88 and M = 3 then 
+;       IDL> print, flegendre(x,3)   ==>   [1.00, 2.88, 11.9416]
+;
+;       This result can be checked by explicitly computing the first 3 Legendre
+;       terms, 1.0, x, 0.5*( 3*x^2 -1)
+;
+;       (2) Find the coefficients to an M term Legendre polynomial that gives
+;               the best least-squares fit to a dataset (x,y)
+;               IDL> coeff = SVDFIT( x,y,M,func='flegendre')
+;       
+;           The coefficients can then be supplied to the function POLYLEG to 
+;               compute the best YFIT values for any X. 
+; METHOD:
+;       The recurrence relation for the Legendre polynomials is used to compute
+;       each term.   Compare with the function FLEG in "Numerical Recipes"
+;       by Press et al. (1992), p. 674
+;
+; REVISION HISTORY:
+;       Written     Wayne Landsman    Hughes STX      April 1995                
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-      
+ On_Error,2
+
+ if N_params() LT 2 then begin
+        print,'Syntax - result = FLEGENDRE( x, m)'
+        return,0
+ endif  
+
+ if m LT 1 then message, $
+        'ERROR - Order of Legendre polynomial must be at least 1'
+ N = N_elements(x)
+ size_x = size(x)
+ leg = make_array(n, m, type = size_x[size_x[0]+1] > 4)    
+ 
+ leg[0,0] = replicate( 1., n)
+ if m GE 2 then leg[0,1] = x
+ if m GE 3 then begin
+        twox = 2.*x
+        f2 = x
+        d = 1.
+        for j=2,m-1 do begin
+                f1 = d
+                f2 = f2 + 2.*x
+                d = d+1.
+                leg[0,j] = ( f2*leg[*,j-1] - f1*leg[*,j-2] )/d
+        endfor
+ endif
+ return, leg
+ end
diff --git a/Code/script_idl_mv/astrolib/flux2mag.pro b/Code/script_idl_mv/astrolib/flux2mag.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d21d9cf0aab8d61fc64d28851e75d790bb0299a3
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/flux2mag.pro
@@ -0,0 +1,51 @@
+function flux2mag, flux, zero_pt, ABwave = abwave
+;+
+; NAME:
+;     FLUX2MAG
+; PURPOSE:
+;     Convert from flux (ergs/s/cm^2/A) to magnitudes.
+; EXPLANATION:
+;     Use MAG2FLUX() for the opposite direction.
+;
+; CALLING SEQUENCE:
+;     mag = flux2mag( flux, [ zero_pt, ABwave=  ] )
+;
+; INPUTS:
+;     flux - scalar or vector flux vector, in erg cm-2 s-1 A-1
+;
+; OPTIONAL INPUT:
+;     zero_pt - scalar giving the zero point level of the magnitude.
+;               If not supplied then zero_pt = 21.1 (Code et al 1976)
+;               Ignored if the ABwave keyword is supplied
+;
+; OPTIONAL KEYWORD INPUT:
+;     ABwave - wavelength scalar or vector in Angstroms.   If supplied, then 
+;           FLUX2MAG() returns Oke AB magnitudes (Oke & Gunn 1983, ApJ, 266,
+;           713).
+;
+; OUTPUT:
+;     mag - magnitude vector.   If the ABwave keyword is set then mag
+;           is given by the expression 
+;           ABMAG = -2.5*alog10(f) - 5*alog10(ABwave) - 2.406 
+;             
+;           Otherwise, mag is given by the expression  
+;           mag = -2.5*alog10(flux) - zero_pt
+; EXAMPLE:
+;       Suppose one is given wavelength and flux vectors, w (in Angstroms) and 
+;       f (in erg cm-2 s-1 A-1).   Plot the spectrum in AB magnitudes
+;
+;       IDL> plot, w, flux2mag(f,ABwave = w), /nozero
+;
+; REVISION HISTORY:
+;       Written    J. Hill        STX Co.       1988
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added ABwave keyword    W. Landsman   September 1998
+;-   
+
+ if ( N_params() LT 2 ) then zero_pt = 21.10        ;Default zero pt
+
+ if keyword_set(ABwave) then $
+     return, -2.5*alog10(flux) - 5*alog10(ABwave) - 2.406 else $
+     return, -2.5*alog10(flux) - zero_pt
+
+ end
diff --git a/Code/script_idl_mv/astrolib/fm_unred.pro b/Code/script_idl_mv/astrolib/fm_unred.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2bb8de241f6c800f7089f3c62491dc427afcc521
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fm_unred.pro
@@ -0,0 +1,174 @@
+pro fm_unred, wave, flux, ebv, funred, R_V = R_V, gamma = gamma, x0 = x0, $
+              c1 = c1, c2 = c2, c3 = c3, c4 = c4,avglmc=avglmc, lmc2 = lmc2, $
+              ExtCurve=ExtCurve
+;+
+; NAME:
+;     FM_UNRED
+; PURPOSE:
+;     Deredden a flux vector using the Fitzpatrick (1999) parameterization
+; EXPLANATION:
+;     The R-dependent Galactic extinction curve is that of Fitzpatrick & Massa 
+;     (Fitzpatrick, 1999, PASP, 111, 63; astro-ph/9809387 ).    
+;     Parameterization is valid from the IR to the far-UV (3.5 microns to 0.1 
+;     microns).    UV extinction curve is extrapolated down to 912 Angstroms.
+;
+; CALLING SEQUENCE:
+;     FM_UNRED, wave, flux, ebv, [ funred, R_V = , /LMC2, /AVGLMC, ExtCurve= 
+;                       gamma =, x0=, c1=, c2=, c3=, c4= ]
+; INPUT:
+;      WAVE - wavelength vector (Angstroms)
+;      FLUX - calibrated flux vector, same number of elements as WAVE
+;               If only 3 parameters are supplied, then this vector will
+;               updated on output to contain the dereddened flux.
+;      EBV  - color excess E(B-V), scalar.  If a negative EBV is supplied,
+;               then fluxes will be reddened rather than dereddened.
+;
+; OUTPUT:
+;      FUNRED - unreddened flux vector, same units and number of elements
+;               as FLUX
+;
+; OPTIONAL INPUT KEYWORDS
+;      R_V - scalar specifying the ratio of total to selective extinction
+;               R(V) = A(V) / E(B - V).    If not specified, then R = 3.1
+;               Extreme values of R(V) range from 2.3 to 5.3
+;
+;      /AVGLMC - if set, then the default fit parameters c1,c2,c3,c4,gamma,x0 
+;             are set to the average values determined for reddening in the 
+;             general Large Magellanic Cloud (LMC) field by Misselt et al. 
+;            (1999, ApJ, 515, 128)
+;      /LMC2 - if set, then the fit parameters are set to the values determined
+;             for the LMC2 field (including 30 Dor) by Misselt et al.
+;             Note that neither /AVGLMC or /LMC2 will alter the default value 
+;             of R_V which is poorly known for the LMC. 
+;             
+;      The following five input keyword parameters allow the user to customize
+;      the adopted extinction curve.    For example, see Clayton et al. (2003,
+;      ApJ, 588, 871) for examples of these parameters in different interstellar
+;      environments.
+;
+;      x0 - Centroid of 2200 A bump in microns (default = 4.596)
+;      gamma - Width of 2200 A bump in microns (default  =0.99)
+;      c3 - Strength of the 2200 A bump (default = 3.23)
+;      c4 - FUV curvature (default = 0.41)
+;      c2 - Slope of the linear UV extinction component 
+;           (default = -0.824 + 4.717/R)
+;      c1 - Intercept of the linear UV extinction component 
+;           (default = 2.030 - 3.007*c2
+;            
+; OPTIONAL OUTPUT KEYWORD:
+;      ExtCurve - Returns the E(wave-V)/E(B-V) extinction curve, interpolated
+;                 onto the input wavelength vector
+;
+; EXAMPLE:
+;       Determine how a flat spectrum (in wavelength) between 1200 A and 3200 A
+;       is altered by a reddening of E(B-V) = 0.1.   Assume an "average"
+;       reddening for the diffuse interstellar medium (R(V) = 3.1)
+;
+;       IDL> w = 1200 + findgen(40)*50      ;Create a wavelength vector
+;       IDL> f = w*0 + 1                    ;Create a "flat" flux vector
+;       IDL> fm_unred, w, f, -0.1, fnew  ;Redden (negative E(B-V)) flux vector
+;       IDL> plot,w,fnew                   
+;
+; NOTES:
+;       (1) The following comparisons between the FM curve and that of Cardelli, 
+;           Clayton, & Mathis (1989), (see ccm_unred.pro):
+;           (a) - In the UV, the FM and CCM curves are similar for R < 4.0, but
+;                 diverge for larger R
+;           (b) - In the optical region, the FM more closely matches the
+;                 monochromatic extinction, especially near the R band.
+;       (2)  Many sightlines with peculiar ultraviolet interstellar extinction 
+;               can be represented with the FM curve, if the proper value of 
+;               R(V) is supplied.
+;       (3) Use the 4 parameter calling sequence if you wish to save the 
+;               original flux vector.
+; PROCEDURE CALLS:
+;       CSPLINE(), POLY()
+; REVISION HISTORY:
+;       Written   W. Landsman        Raytheon  STX   October, 1998
+;       Based on FMRCurve by E. Fitzpatrick (Villanova)
+;       Added /LMC2 and /AVGLMC keywords,  W. Landsman   August 2000
+;       Added ExtCurve keyword, J. Wm. Parker   August 2000
+;       Assume since V5.4 use COMPLEMENT to WHERE  W. Landsman April 2006
+;       Fix calculation of EXTCurve A. Sarkisyan/W. Landsman  Jan 2014
+;
+;-
+ On_error, 2
+ compile_opt idl2
+
+ if N_params() LT 3 then begin
+     print,'Syntax: FM_UNRED, wave, flux, ebv, funred,[ R_V =, /LMC2, /AVGLMC '
+     print,'                  gamma =, x0 =, c1 =, c2 = ,c3 = ,c4 =, ExtCurve=]'
+     return
+ endif
+
+ if N_elements(R_V) EQ 0 then R_V = 3.1
+
+ x = 10000./ wave                ; Convert to inverse microns 
+ curve = x*0.
+
+; Set default values of c1,c2,c3,c4,gamma and x0 parameters
+
+ if keyword_set(LMC2) then  begin
+         if N_elements(x0) EQ 0 then x0    =  4.626
+         if N_elements(gamma) EQ 0 then gamma =  1.05	
+         if N_elements(c4) EQ 0 then c4   =  0.42   
+         if N_elements(c3) EQ 0 then c3    =  1.92	
+         if N_elements(c2) EQ 0 then c2    = 1.31
+         if N_elements(c1) EQ 0 then c1    =  -2.16
+ endif else if keyword_set(AVGLMC) then begin
+         if N_elements(x0) EQ 0 then x0 = 4.596  
+         if N_elements(gamma) EQ 0 then gamma = 0.91
+         if N_elements(c4) EQ 0 then c4   =  0.64  
+         if N_elements(c3) EQ 0 then c3    =  2.73	
+         if N_elements(c2) EQ 0 then c2    = 1.11
+         if N_elements(c1) EQ 0 then c1    =  -1.28
+  endif else begin
+         if N_elements(x0) EQ 0 then x0    =  4.596  
+         if N_elements(gamma) EQ 0 then gamma =  0.99	
+         if N_elements(c3) EQ 0 then c3    =  3.23	
+         if N_elements(c4) EQ 0 then c4   =  0.41    
+         if N_elements(c2) EQ 0 then c2    = -0.824 + 4.717/R_V
+         if N_elements(c1) EQ 0 then c1    =  2.030 - 3.007*c2
+ endelse
+
+; Compute UV portion of A(lambda)/E(B-V) curve using FM fitting function and 
+; R-dependent coefficients
+ 
+ xcutuv = 10000.0/2700.0
+ xspluv = 10000.0/[2700.0,2600.0]
+ iuv = where(x ge xcutuv, N_UV, complement = iopir, Ncomp = Nopir)
+ IF (N_UV GT 0) THEN xuv = [xspluv,x[iuv]] ELSE  xuv = xspluv
+
+    yuv = c1  + c2*xuv
+    yuv = yuv + c3*xuv^2/((xuv^2-x0^2)^2 +(xuv*gamma)^2)
+    yuv = yuv + c4*(0.5392*((xuv>5.9)-5.9)^2+0.05644*((xuv>5.9)-5.9)^3)
+    yuv = yuv + R_V
+    yspluv  = yuv[0:1]                  ; save spline points
+
+ IF (N_UV GT 0) THEN curve[iuv] = yuv[2:*]      ; remove spline points
+ 
+; Compute optical portion of A(lambda)/E(B-V) curve
+; using cubic spline anchored in UV, optical, and IR
+
+ xsplopir = [0,10000.0/[26500.0,12200.0,6000.0,5470.0,4670.0,4110.0]]
+ ysplir   = [0.0,0.26469,0.82925]*R_V/3.1 
+ ysplop   = [poly(R_V, [-4.22809e-01, 1.00270, 2.13572e-04] ), $
+             poly(R_V, [-5.13540e-02, 1.00216, -7.35778e-05] ), $
+             poly(R_V, [ 7.00127e-01, 1.00184, -3.32598e-05] ), $
+             poly(R_V, [ 1.19456, 1.01707, -5.46959e-03, 7.97809e-04, $ 
+                     -4.45636e-05] ) ]
+  
+ ysplopir = [ysplir,ysplop]
+
+ if (Nopir GT 0) then $
+          curve[iopir] = CSPLINE([xsplopir,xspluv],[ysplopir,yspluv],x[iopir])
+
+ ; Now apply extinction correction to input flux vector
+
+   curve = ebv*curve 
+   if N_params() EQ 3 then flux = flux * 10.^(0.4*curve) else $
+        funred = flux * 10.^(0.4*curve)       ;Derive unreddened flux
+
+   ExtCurve = Curve/ebv - R_V     ;Updated Jan 2014
+
+ end
diff --git a/Code/script_idl_mv/astrolib/forprint.pro b/Code/script_idl_mv/astrolib/forprint.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b529e12fa512ebab725b72918c72c651431d0b7a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/forprint.pro
@@ -0,0 +1,240 @@
+pro forprint, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, $
+      v15,v16,v17,v18,TEXTOUT = textout, FORMAT = format, SILENT = SILENT, $ 
+      STARTLINE = startline, NUMLINE = numline, COMMENT = comment, $
+      SUBSET = subset, NoCOMMENT=Nocomment,STDOUT=stdout, WIDTH=width
+;+
+; NAME:
+;       FORPRINT
+; PURPOSE:
+;       Print a set of vectors by looping over each index value.
+;
+; EXPLANATION:
+;       If W and F are equal length vectors, then the statement
+;               IDL> forprint, w, f   
+;       is equivalent to 
+;               IDL> for i = 0L, N_elements(w)-1 do print,w[i],f[i]    
+;
+; CALLING SEQUENCE:
+;       forprint, v1,[ v2, v3, v4,....v18, FORMAT = , TEXTOUT = ,STARTLINE =,
+;                                  SUBSET=, NUMLINE =, /SILENT, COMMENT= ] 
+;
+; INPUTS:
+;       V1,V2,...V18 - Arbitrary IDL vectors.  If the vectors are not of
+;               equal length then the number of rows printed will be equal
+;               to the length of the smallest vector.   Up to 18 vectors
+;               can be supplied.
+;
+; OPTIONAL KEYWORD INPUTS:
+;
+;       TEXTOUT - Controls print output device, defaults to !TEXTOUT
+;
+;               textout=1       TERMINAL using /more option if available
+;               textout=2       TERMINAL without /more option
+;               textout=3       file 'forprint.prt'
+;               textout=4       file 'laser.tmp' 
+;               textout=5      user must open file
+;               textout =      filename (default extension of .prt)
+;               textout=7       Append to <program>.prt file if it exists
+;
+;       COMMENT - String scalar or vector to write to the first line of output 
+;                file if  TEXTOUT > 2.    By default, FORPRINT will write a time
+;                stamp on the first line.   Use /NOCOMMENT if you don't want 
+;                FORPRINT to write anything in the output file.    If COMMENT
+;                is a vector then one line will be written for each element.
+;       FORMAT - Scalar format string as in the PRINT procedure.  The use
+;               of outer parenthesis is optional.   Ex. - format="(F10.3,I7)"
+;               This program will automatically remove a leading "$" from
+;               incoming format statements. Ex. - "$(I4)" would become "(I4)".
+;               If omitted, then IDL default formats are used.
+;       /NOCOMMENT  - Set this keyword if you don't want any comment line
+;               line written as the first line in a harcopy output file.
+;       /SILENT - Normally, with a hardcopy output (TEXTOUT > 2), FORPRINT will
+;                print an informational message.    If the SILENT keyword
+;               is set and non-zero, then this message is suppressed.
+;       SUBSET - Index vector specifying elements to print.   No error checking
+;               is done to make sure the indicies are valid.  The statement
+;
+;              IDL> forprint,x,y,z,subset=s
+;                       is equivalent to 
+;              IDL> for i=0,n-1 do print, x[s[i]], y[s[i]], z[s[i]]
+;
+;       STARTLINE - Integer scalar specifying the first line in the arrays
+;               to print.   Default is STARTLINE = 1, i.e. start at the
+;               beginning of the arrays.    (If a SUBSET keyword is supplied
+;               then STARTLINE refers to first element in the subscript vector.)
+;      /STDOUT - If set, the force standard output unit (=-1) if not writing 
+;               to a file.   This allows the FORPRINT output to be captured
+;               in a journal file.    Only needed for non-GUI terminals 
+;       WIDTH - Line width for wrapping, passed onto OPENW when using hardcopy.
+;
+; OUTPUTS:
+;       None
+; SYSTEM VARIABLES:
+;       If keyword TEXTOUT is not used, the default is the nonstandard 
+;       keyword !TEXTOUT.    If you want to use FORPRINT to write more than 
+;       once to the same file then set TEXTOUT=5, and open and close the 
+;       file yourself (see documentation of TEXTOPEN for more info).
+;       
+;       The non-standard system variables !TEXTOUT and !TEXTUNIT are 
+;       automatically added if not present to start with.
+; EXAMPLE:
+;       Suppose W,F, and E are the wavelength, flux, and epsilon vectors for
+;       a spectrum.   Print these values to a file 'output.dat' in a nice 
+;       format.
+;
+;       IDL> fmt = '(F10.3,1PE12.2,I7)'
+;       IDL> forprint, F = fmt, w, f, e, TEXT = 'output.dat'
+; RESTRICTIONS:
+;       Uses the EXECUTE() function and so is not compatible with the IDL
+;       virtual machine.
+; PROCEDURES CALLED:
+;       TEXTOPEN, TEXTCLOSE
+; REVISION HISTORY:
+;       Written    W. Landsman             April, 1989
+;       Keywords textout and format added, J. Isensee, July, 1990
+;       Made use of parenthesis in FORMAT optional  W. Landsman  May 1992
+;       Added STARTLINE keyword W. Landsman    November 1992
+;       Set up so can handle 18 input vectors. J. Isensee, HSTX Corp. July 1993
+;       Handle string value of TEXTOUT   W. Landsman, HSTX September 1993
+;       Added NUMLINE keyword            W. Landsman, HSTX February 1996
+;       Added SILENT keyword             W. Landsman, RSTX, April 1998
+;       Much faster printing to a file   W. Landsman, RITSS, August, 2001
+;       Use SIZE(/TNAME) instead of DATATYPE() W. Landsman SSAI October 2001
+;       Fix skipping of first line bug introduced Aug 2001  W. Landsman Nov2001
+;       Added /NOCOMMENT keyword, the SILENT keyword now controls only 
+;       the display of informational messages.  W. Landsman June 2002
+;       Skip PRINTF if IDL in demo mode  W. Landsman  October 2004
+;       Assume since V5.4 use BREAK instead of GOTO W. Landsman April 2006
+;       Add SUBSET keyword, warning if different size vectors passed. 
+;                                     P.Broos,W.Landsman. Aug 2006
+;       Change keyword_set() to N_elements W. Landsman  Oct 2006
+;       Added /STDOUT keyword  W. Landsman Oct 2006
+;       Fix error message for undefined variable W. Landsman  April 2007
+;       Added WIDTH keyword    J. Bailin  Nov 2010
+;       Allow multiple (vector) comment lines  W. Landsman April 2011
+;       Define !TEXTOUT and !TEXTUNIT if needed. W. Landsman October 2012
+;-            
+  On_error,2                               ;Return to caller
+  compile_opt idl2
+
+  npar = N_params()
+  if npar EQ 0 then begin
+      print,'Syntax - FORPRINT, v1, [ v2, v3,...v18, FORMAT =, /SILENT, SUBSET='
+      print,'      /NoCOMMENT, COMMENT =, STARTLINE = , NUMLINE =, TEXTOUT =, WIDTH =]'
+      return
+  endif
+  
+   defsysv,'!TEXTOUT',exists=ex			; Check if !TEXTOUT exists.
+  if ex eq 0 then defsysv,'!TEXTOUT',1		; If not define it.
+  defsysv,'!TEXTUNIT',exists=ex			; Check if !TEXTUNIT exists.
+  if ex eq 0 then defsysv,'!TEXTUNIT',0		; If not define it.
+
+
+  if N_elements( STARTLINE ) EQ 0 then startline = 1l else $
+         startline = startline > 1l 
+
+  fmt="F"                 ;format flag
+  npts = N_elements(v1)
+
+  if ( npts EQ 0 ) then message,'ERROR - Parameter 1 is not defined'
+
+;  Remove "$" sign from format string and append parentheses if not 
+;  already present
+
+  if N_elements( format ) EQ 1 then begin
+
+     fmt = "T"                                 ;format present
+     frmt = format            
+     if strmid(frmt,0,1) eq '$' then $
+          frmt = strmid(frmt,1,strlen(frmt)-1) ;rem. '$' from format if present
+
+     if strmid(frmt,0,1) NE '(' then frmt = '(' + frmt
+     if strmid( frmt,strlen(frmt)-1,1) NE ')' then frmt +=  ')'
+
+  endif
+
+  if npar GT 1 then begin         ;Get number of elements in smallest array
+
+      for i = 2, npar do begin 
+          tst = execute('this_npts =  N_elements(v'+strtrim(i,2)+')')
+          if this_npts EQ 0 then $
+              message,'ERROR - Parameter ' + strtrim(i,2) + ' is not defined'
+          
+          if ((npts NE this_npts) && ~keyword_set(silent)) then $
+            message,/INF,'Warning, vectors have different lengths.' 
+          
+          npts = npts < this_npts
+      endfor
+
+  endif
+
+  if keyword_set(NUMLINE) then npts = (startline + numline-1) < npts
+
+  if N_Elements(SUBSET) GT 0 then begin
+       npts = N_elements(subset) < npts
+       index = '[subset[i]]'
+  endif else index = '[i]'  
+     
+  
+  str = 'v1'  + index
+  if npar GT 1 then $
+       for i = 2, npar do str = str + ',v' + strtrim(i,2) + index
+
+; Use default output dev.
+   demo = lmgr(/demo)
+   if ~demo then begin 
+
+   if ~keyword_set( TEXTOUT ) then textout = !TEXTOUT 
+   if size( textout,/TNAME) EQ 'STRING' then text_out = 6  $      ;make numeric
+                                  else text_out = textout
+
+   textopen,'FORPRINT',TEXTOUT=textout,SILENT=silent,STDOUT=STDOUT, $
+       MORE_SET = more_set, WIDTH=width
+   if ( text_out GT 2 ) && (~keyword_set(NOCOMMENT)) then begin
+       Ncomm = N_elements(comment)
+       if Ncomm GT 0 then $
+        for i=0,ncomm-1 do printf,!TEXTUNIT,comment[i] else $
+        printf,!TEXTUNIT,'FORPRINT: ',systime()
+  endif 
+  endif
+ 
+   if fmt EQ "F" then begin            ;Use default formats
+
+   if demo then begin
+         test =  execute('for i=startline-1,npts-1 do print,' + str)
+        
+   endif else if more_set then begin      
+      for i = startline-1, npts-1 do begin 
+
+          test = execute('printf,!TEXTUNIT,' + str) 
+               if !ERR EQ 1 then BREAK       ;Did user press 'Q' key?
+
+      endfor
+   endif else test = $
+          execute('for i=startline-1,npts-1 do printf,!TEXTUNIT,' + str)
+
+   endif else begin                    ;User specified format
+
+   if demo then begin
+         test =  execute('for i=startline-1,npts-1 do print,FORMAT=frmt,' + str)
+ 
+   endif else  if more_set then begin
+
+      for i = startline-1, npts-1 do begin 
+
+         test = execute( 'printf, !TEXTUNIT,  FORMAT=frmt,' + str ) 
+                if !ERR EQ 1 then BREAK
+
+      endfor
+
+    endif else test = $
+        execute('for i=startline-1,npts-1 do printf,!TEXTUNIT,FORMAT=frmt,'+str)
+        
+
+  endelse
+
+
+  textclose, TEXTOUT = textout          ;Close unit opened by TEXTOPEN
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/frebin.pro b/Code/script_idl_mv/astrolib/frebin.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1f2e10eca98ebdc31e0bae5eaf7620c7bf390028
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/frebin.pro
@@ -0,0 +1,217 @@
+function frebin,image,nsout,nlout,total=total
+;+
+; NAME:
+;   FREBIN
+;
+; PURPOSE:
+;   Shrink or expand the size of an array an arbitrary amount using interpolation
+;
+; EXPLANATION: 
+;   FREBIN is an alternative to CONGRID or REBIN.    Like CONGRID it
+;   allows expansion or contraction by an arbitrary amount. ( REBIN requires 
+;   integral factors of the original image size.)    Like REBIN it conserves 
+;   flux by ensuring that each input pixel is equally represented in the output
+;   array.       
+;
+; CALLING SEQUENCE:
+;   result = FREBIN( image, nsout, nlout, [ /TOTAL] )
+;
+; INPUTS:
+;    image - input image, 1-d or 2-d numeric array
+;    nsout - number of samples in the output image, numeric scalar
+;
+; OPTIONAL INPUT:
+;    nlout - number of lines in the output image, numeric scalar
+;            If not supplied, then set equal to 1
+;
+; OPTIONAL KEYWORD INPUTS:
+;   /total - if set, the output pixels will be the sum of pixels within
+;          the appropriate box of the input image.  Otherwise they will
+;          be the average.    Use of the /TOTAL keyword conserves total counts.
+; 
+; OUTPUTS:
+;    The resized image is returned as the function result.    If the input
+;    image is of type DOUBLE or FLOAT then the resized image is of the same
+;    type.     If the input image is BYTE, INTEGER or LONG then the output
+;    image is usually of type FLOAT.   The one exception is expansion by
+;    integral amount (pixel duplication), when the output image is the same
+;    type as the input image.  
+;     
+; EXAMPLE:
+;     Suppose one has an 800 x 800 image array, im, that must be expanded to
+;     a size 850 x 900 while conserving the total counts:
+;
+;     IDL> im1 = frebin(im,850,900,/total) 
+;
+;     im1 will be a 850 x 900 array, and total(im1) = total(im)
+; NOTES:
+;    If the input image sizes are a multiple of the output image sizes
+;    then FREBIN is equivalent to the IDL REBIN function for compression,
+;    and simple pixel duplication on expansion.
+;
+;    If the number of output pixels are not integers, the output image
+;    size will be truncated to an integer.  The platescale, however, will
+;    reflect the non-integer number of pixels.  For example, if you want to
+;    bin a 100 x 100 integer image such that each output pixel is 3.1
+;    input pixels in each direction use:
+;           n = 100/3.1   ; 32.2581
+;          image_out = frebin(image,n,n)
+;
+;     The output image will be 32 x 32 and a small portion at the trailing
+;     edges of the input image will be ignored.
+; 
+; PROCEDURE CALLS:
+;    None.
+; HISTORY:
+;    Adapted from May 1998 STIS  version, written D. Lindler, ACC
+;    Added /NOZERO, use INTERPOLATE instead of CONGRID, June 98 W. Landsman  
+;    Fixed for nsout non-integral but a multiple of image size  Aug 98 D.Lindler
+;    DJL, Oct 20, 1998, Modified to work for floating point image sizes when
+;		expanding the image. 
+;    Improve speed by addressing arrays in memory order W.Landsman Dec/Jan 2001
+;-
+;----------------------------------------------------------------------------
+      On_error,2
+      compile_opt idl2
+
+      if N_params() LT 1 then begin
+           print,'Syntax = newimage = FREBIN(image, nsout, nlout, [/TOTAL])'  
+           return,-1
+       endif
+
+       if n_elements(nlout) eq 0 then nlout=1
+;
+; determine size of input image
+;
+	ns = n_elements(image[*,0])
+	nl = n_elements(image)/ns
+;
+; determine if we can use the standard rebin function
+;
+        dtype = size(image,/TNAME)
+	if dtype EQ 'DOUBLE' then begin
+		sbox = ns/double(nsout) 
+		lbox = nl/double(nlout)
+	   end else begin
+		sbox = ns/float(nsout) 
+		lbox = nl/float(nlout)
+	end	
+
+; Contraction by an integral amount 
+
+	if (nsout eq long(nsout)) && (nlout eq long(nlout)) then begin
+	if ((ns mod nsout) EQ 0) && ((nl mod nlout) EQ 0) then $
+                if (dtype EQ 'DOUBLE') || (dtype EQ 'FLOAT') then begin
+ 		   if keyword_set(total) then $
+		   return,rebin(image,nsout,nlout)*sbox*lbox else $
+		   return,rebin(image,nsout,nlout) 
+                endif else begin 
+ 		   if keyword_set(total) then $
+		   return,rebin(float(image),nsout,nlout)*sbox*lbox else $
+		   return,rebin(float(image),nsout,nlout)
+                endelse 
+
+
+; Expansion by an integral amount
+	if ((nsout mod ns) EQ 0) && ((nlout mod nl) EQ 0) then begin
+                xindex = long(lindgen(nsout)/(nsout/ns))
+                if nl EQ 1 then begin
+ 		if keyword_set(total) then $
+		return,interpolate(image,xindex)*sbox else $        
+		return,interpolate(image,xindex)  
+                endif
+                yindex = long(lindgen(nlout)/(nlout/nl))
+ 		if keyword_set(total) then $
+		return,interpolate(image,xindex,yindex,/grid)*sbox*lbox else $
+		return,interpolate(image,xindex,yindex,/grid)  
+	endif
+   endif
+	    ns1 = ns-1
+	    nl1 = nl-1
+
+; Do 1-d case separately
+
+  if nl EQ 1 then begin
+           if dtype eq 'DOUBLE' then result = dblarr(nsout,/NOZERO) $
+			        else result = fltarr(nsout,/NOZERO)
+	    for i=0L,nsout-1 do begin
+	    	    rstart = i*sbox	       ;starting position for each box
+	    	    istart = long(rstart)
+	    	    rstop = rstart + sbox      ;ending position for each box
+	    	    istop = long(rstop)<ns1
+	    	    frac1 = rstart-istart
+	    	    frac2 = 1.0 - (rstop-istop)
+;
+; add pixel values from istart to istop and  subtract fraction pixel 
+; from istart to rstart and fraction pixel from rstop to istop
+;
+	   	     result[i] = total(image[istart:istop]) $
+	   			- frac1 * image[istart]  $
+	   			- frac2 * image[istop] 
+	    endfor
+ 	    if keyword_set(total) then return,result $
+	    			  else return,temporary(result)/(sbox*lbox)
+ endif 
+
+; Now do 2-d case
+; First, bin in second dimension
+;
+	    if dtype eq 'DOUBLE' then temp = dblarr(ns,nlout, /NOZERO) $
+			         else temp = fltarr(ns,nlout, /NOZERO)
+
+; loop on output image lines
+;
+	    for i=0L,nlout-1 do begin
+	    	    rstart = i*lbox		;starting position for each box
+	    	    istart = long(rstart)
+	    	    rstop = rstart + lbox	;ending position for each box
+	    	    istop = long(rstop)<nl1
+	    	    frac1 = rstart-istart
+	    	    frac2 = 1.0 - (rstop-istop)
+;
+; add pixel values from istart to istop and  subtract fraction pixel 
+; from istart to rstart and fraction pixel from rstop to istop
+;
+
+                     if istart EQ istop then $
+	   	       temp[0,i] = (1.0 - frac1 - frac2)*image[*,istart] $
+                       else $
+	   	       temp[0,i] = total(image[*,istart:istop],2) $
+	   			- frac1 * image[*,istart]  $
+	   			- frac2 * image[*,istop] 
+	    endfor
+           temp = transpose(temp)
+;
+; bin in first dimension
+;
+	    if dtype eq 'DOUBLE' then result = dblarr(nlout,nsout,/NOZERO) $
+			         else result = fltarr(nlout,nsout,/NOZERO)
+
+;
+; loop on output image samples
+;
+	    for i=0L,nsout-1 do begin
+	    	    rstart = i*sbox	       ;starting position for each box
+	    	    istart = long(rstart)
+	    	    rstop = rstart + sbox      ;ending position for each box
+	    	    istop = long(rstop)<ns1
+	    	    frac1 = rstart-istart
+	    	    frac2 = 1.0 - (rstop-istop)
+;
+; add pixel values from istart to istop and  subtract fraction pixel 
+; from istart to rstart and fraction pixel from rstop to istop
+;
+
+		    if istart eq istop then $
+                        result[0,i] = (1.-frac1-frac2)*temp[*,istart] else $
+		    	result[0,i] = total(temp[*,istart:istop],2)   $
+		    		- frac1 * temp[*,istart]  $
+		    		- frac2 * temp[*,istop]
+	    end
+
+;            
+	    if keyword_set(total) then $
+                        return, transpose(result) $
+	    	   else return, transpose(result)/(sbox*lbox)
+	    			  
+end
diff --git a/Code/script_idl_mv/astrolib/ftab_delrow.pro b/Code/script_idl_mv/astrolib/ftab_delrow.pro
new file mode 100644
index 0000000000000000000000000000000000000000..355beb214a079f8cccc778a6fc4d6ecb42dc8c7a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftab_delrow.pro
@@ -0,0 +1,106 @@
+pro ftab_delrow,filename,rows,EXTEN_NO=exten_no, NEWFILE = newfile                      
+;+
+; NAME:
+;       FTAB_DELROW
+; PURPOSE:
+;       Delete rows of data from a FITS ASCII or binary table extension
+;
+; CALLING SEQUENCE:
+;       ftab_delrow, filename, rows, EXTEN_NO =, NEWFILE = ] 
+;
+; INPUTS-OUPUTS
+;       filename - scalar string giving name of the FITS file containing an
+;               ASCII or binary table extension. 
+; 
+;       rows  -  scalar or vector, specifying the row numbers to delete
+;               First row has index 0.   If a vector, it will be sorted and
+;               duplicates will be removed
+;
+; OPTIONAL KEYWORD INPUTS:
+;       EXTEN_NO - scalar integer specifying which extension number to process
+;               Default is to process the first extension
+;       NEWFILE - scalar string specifying the name of the new output FITS file
+;               FTAB_DELROW will prompt for this parameter if not supplied
+;
+; EXAMPLE:
+;       Compress the first extension of a FITS file 'test.fits' to include 
+;       only non-negative values in the 'FLUX' column
+;
+;       ftab_ext,'test.fits','flux',flux       ;Obtain original flux vector
+;       bad = where(flux lt 0)                 ;Find negative fluxes
+;       ftab_delrow,'test.fits',bad,new='test1.fits'  ;Delete specified rows
+;
+; RESTRICTIONS:
+;       Does not work for variable length binary tables
+;
+; PROCEDURES USED:
+;       FITS_CLOSE, FITS_OPEN, FITS_READ, FITS_WRITE, FTDELROW, TBDELROW        
+;
+; REVISION HISTORY:                                           
+;       Written   W. Landsman        STX Co.     August, 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use COPY_LUN if V5.6 or later     W. Landsman   February 2003
+;       Assume since V5.6, COPY_LUN available   W. Landsman   Sep 2006
+;- 
+; On_error,2
+
+ if N_params() LT 2 then begin
+     print,'Syntax - FTAB_DELROW, filename, rows, [EXTEN_NO= , NEWFILE= ] '
+     return                                                  
+ endif
+
+ if not keyword_set(exten_no) then exten_no = 1
+
+ fits_open,filename,fcb_in
+ nextend = fcb_in.nextend
+
+ if fcb_in.nextend EQ 0 then $
+        message,'ERROR - FITS file contains no table extensions
+ if fcb_in.nextend LT exten_no then $
+        message,'ERROR - FITS file contains only ' + strtrim(nextend,2) + $
+                ' extensions'
+
+
+ if (N_elements(newfile) EQ 0) then begin
+        newfile = ''
+        read,prompt='Enter name of updated FITS file: ',newfile
+ endif
+
+; Make sure specified extension contains a table and determine if it is ASCII
+; or binary
+
+ fits_read,fcb_in, tab, htab, exten_no = exten_no, /NO_PDU
+ case fcb_in.xtension[exten_no] of
+ 'A3DTABLE': binary = 1b
+ 'BINTABLE': binary = 1b
+ 'TABLE': binary = 0b
+ else: message,'ERROR - Extension type of ' + $
+                ext_type + 'is not a FITS table format'
+ endcase
+ if binary then tbdelrow,htab,tab,rows else $
+                ftdelrow,htab,tab,rows  
+
+; Copy primary header and data array unchanged to output file
+
+ fits_open, newfile, fcb_out, /write
+ filestat = fstat(fcb_in.unit)
+ hstart = fcb_in.start_header
+ point_lun,fcb_in.unit,0               ;Back to the start of the file
+          copy_lun, fcb_in.unit, fcb_out.unit,hstart[1] 
+          fcb_out.nextend = fcb_out.nextend+1       ;flag that primary header is written
+
+ for i = 1, Nextend  do begin
+        if i EQ exten_no then begin
+                fits_write, fcb_out, tab,htab
+        endif else begin
+          if i EQ Nextend then nbyte = filestat.size - hstart[i] $
+                          else nbyte = hstart[i+1] - hstart[i]
+          point_lun,fcb_in.unit,hstart[i]
+          copy_lun, fcb_in.unit, fcb_out.unit,nbyte 
+         endelse
+ endfor
+ fits_close,fcb_in
+ fits_close,fcb_out
+
+ return  
+ end
diff --git a/Code/script_idl_mv/astrolib/ftab_ext.pro b/Code/script_idl_mv/astrolib/ftab_ext.pro
new file mode 100644
index 0000000000000000000000000000000000000000..bddff5a68f9b9a6503fcbcf866ff3b67cc449fe5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftab_ext.pro
@@ -0,0 +1,124 @@
+pro ftab_ext,file_or_fcb,columns,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12, $
+    v13,v14,v15,v16,v17,v18,v19,v20, v21,v22,v23,v24,v25,v26,v27,v28,v29,v30, $
+    v31,v32,v33,v34,v35,v36,v37,v38,v39,v40,v41,v42,v43,v45,v46,v47,v48,v49, $
+    v50, ROWS=rows,EXTEN_NO = exten_no
+;+
+; NAME:
+;       FTAB_EXT
+; PURPOSE:
+;       Routine to extract columns from a FITS (binary or ASCII) table. 
+;
+; CALLING SEQUENCE:
+;       FTAB_EXT, name_or_fcb, columns, v1, [v2,..,v50, ROWS=, EXTEN_NO= ]
+; INPUTS:
+;       name_or_fcb - either a scalar string giving the name of a FITS file 
+;               containing a (binary or ASCII) table, or an IDL structure 
+;               containing as file control block (FCB) returned by FITS_OPEN 
+;               If FTAB_EXT is to be called repeatedly on the same file, then
+;               it is quicker to first open the file with FITS_OPEN, and then
+;               pass the FCB structure to FTAB_EXT
+;       columns - table columns to extract.  Can be either 
+;               (1) String with names separated by commas
+;               (2) Scalar or vector of column numbers
+;
+; OUTPUTS:
+;       v1,...,v50 - values for the columns.   Up to 50 columns can be extracted
+;
+; OPTIONAL INPUT KEYWORDS:
+;       ROWS -  scalar or vector giving row number(s) to extract
+;               Row numbers start at 0.  If not supplied or set to
+;               -1 then values for all rows are returned.    This keyword
+;               works for ASCII tables and binary tables where every column has
+;               the same number of elements, but not for variable length 
+;               binary tables.
+;       EXTEN_NO - Extension number to process.   If not set, then data is
+;               extracted from the first extension in the file (EXTEN_NO=1)
+;
+; EXAMPLES:
+;       Read wavelength and flux vectors from the first extension of a 
+;       FITS file, 'spec.fit'.   Using FTAB_HELP,'spec.fit' we find that this
+;       information is in columns named 'WAVELENGTH' and 'FLUX' (in columns 1
+;       and 2).   To read the data
+;
+;       IDL> ftab_ext,'spec.fit','wavelength,flux',w,f
+;               or
+;       IDL> ftab_ext,'spec.fit',[1,2],w,f
+;       
+; PROCEDURES CALLED:
+;       FITS_READ, FITS_CLOSE, FTINFO, FTGET(), TBINFO, TBGET()
+; HISTORY:
+;       version 1        W.   Landsman         August 1997
+;       Improve speed processing binary tables  W. Landsman   March 2000
+;       Use new FTINFO calling sequence  W. Landsman   May 2000  
+;       Don't call fits_close if fcb supplied W. Landsman May 2001 
+;       Use STRSPLIT to parse column string  W. Landsman July 2002 
+;       Cleanup pointers in TBINFO structure  W. Landsman November 2003
+;       Avoid EXECUTE() if V6.1 or later  W. Landsamn   December 2006
+;       Assume since V6.1  W. Landsman   June 2009
+;       Read up to 30 columns  W.L. Aug 2009
+;       Setting ROWS = -1 should work as documented, accept up to 50
+;               columns  W.L.  Oct 2013
+;-
+;---------------------------------------------------------------------
+ compile_opt idl2
+ if N_params() LT 3 then begin
+        print,'Syntax - FTAB_EXT, name, columns, v1, [v2,...,v50, ROWS=, EXTEN=]'
+        return
+ endif
+ N_ext = N_params() - 2
+ strng = size(columns,/TNAME) EQ 'STRING'    ;Is columns a string?
+
+ if ~keyword_set(exten_no) then exten_no = 1
+ dtype = size(file_or_fcb,/TNAME)
+ if dtype NE 'STRUCT' then fits_open,file_or_fcb,fcb else fcb=file_or_fcb
+ if fcb.nextend EQ 0 then $
+        message,'ERROR - FITS file contains no table extensions'
+ if fcb.nextend LT exten_no then $
+        message,'ERROR - FITS file contains only ' + strtrim(fcb.nextend,2) + $
+                ' extensions'
+
+ if (N_elements(rows) GT 0) && (min(rows) GE 0) then begin
+        minrow = min(rows, max = maxrow)
+        naxis1 = fcb.axis[0,exten_no]
+        first = naxis1*minrow
+        last = naxis1*(maxrow+1)-1
+        xrow = rows - minrow
+        fits_read,fcb,tab,htab,exten_no=exten_no,first=first,last=last,/no_pdu
+        tab = reform(tab,naxis1,maxrow-minrow+1,/overwrite)
+ endif else begin
+        fits_read, fcb, tab, htab, exten_no=exten_no,/no_pdu 
+        xrow = -1
+ endelse
+ if dtype NE 'STRUCT' then fits_close,fcb else $
+         file_or_fcb.last_extension = exten_no
+ ext_type = fcb.xtension[exten_no]
+
+ case ext_type of
+ 'A3DTABLE': binary = 1b
+ 'BINTABLE': binary = 1b
+ 'TABLE': binary = 0b
+ else: message,'ERROR - Extension type of ' + $
+                ext_type + 'is not a FITS table format'
+ endcase
+
+ if strng then colnames= strsplit(columns,',',/EXTRACT) else $
+               colnames = columns
+ if binary then tbinfo,htab,tb_str else ftinfo,htab,ft_str
+
+
+  vv = 'v' + strtrim( indgen(n_ext)+1,2)
+  for i = 0, N_ext-1 do begin 
+  
+         if binary then $
+         (scope_varfetch(vv[i]))  = TBGET( tb_str,tab,colnames[i],xrow,nulls) $
+        else $
+          (scope_varfetch(vv[i])) = FTGET( ft_str,tab,colnames[i],xrow,nulls)
+ endfor
+ if binary then begin
+        ptr_free, tb_str.tscal
+        ptr_free, tb_str.tzero
+ endif
+ return
+ end 
+
+
diff --git a/Code/script_idl_mv/astrolib/ftab_help.pro b/Code/script_idl_mv/astrolib/ftab_help.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a2b3d1fecf1eb5337115fd50e422ded0e8b721d3
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftab_help.pro
@@ -0,0 +1,103 @@
+pro ftab_help,file_or_fcb,EXTEN_NO = exten_no, TEXTOUT = textout
+;+
+; NAME:
+;       FTAB_HELP
+; PURPOSE:
+;       Describe the columns of a FITS binary or ASCII table extension(s).
+;
+; CALLING SEQUENCE:
+;       FTAB_HELP, filename, [ EXTEN_No = , TEXTOUT= ]
+;               or
+;       FTAB_HELP, fcb, [EXTEN_No=, TEXTOUT= ]
+;
+; INPUTS:
+;       filename - scalar string giving name of the FITS file.  
+;       fcb - FITS control block returned by a previous call to FITS_OPEN
+;
+; OPTIONAL KEYWORD INPUTS:
+;       EXTEN_NO - integer scalar or vector specifying which FITS extensions 
+;               to display.    Default is to display all FITS extension.
+;       TEXTOUT - scalar number (0-7) or string (file name) determining
+;               output device (see TEXTOPEN).  Default is TEXTOUT=1, output 
+;               to the user's terminal    
+;
+; EXAMPLE:
+;       Describe the columns in the second and fourth extensions of a FITS 
+;       file spec.fits and write the results to a file 'spec24.lis'
+;
+;       IDL> ftab_help,'spec.fits',exten=[2,4],t='spec24.lis'
+;
+; SYSTEM VARIABLES:
+;        Uses the non-standard system variables !TEXTOUT and !TEXTUNIT
+;       which must be defined (e.g. with ASTROLIB) before compilation
+; NOTES:
+;       The behavior of FTAB_HELP was changed in August 2005 to display
+;       all extensions by default, rather than just the first extension
+; PROCEDURES USED:
+;       FITS_READ, FITS_CLOSE, FITS_OPEN, FTHELP, TBHELP, TEXTOPEN, TEXTCLOSE
+; HISTORY:
+;       version 1  W. Landsman    August 1997
+;       Corrected documentation W. Landsman   September 1997
+;       Don't call fits_close if fcb supplied W. Landsman May 2001 
+;       Default now is to display all extensions, EXTEN keyword can now
+;        be a vector   W. Landsman Aug 2005
+;-
+;----------------------------------------------------------------------
+ compile_opt idl2
+ if N_params() LT 1 then begin
+        print,'Syntax - FTAB_HELP, fcb_or_filename, [EXTEN_NO=, TEXTOUT= ]'
+        return
+ endif
+ 
+ sz = size(file_or_fcb)                                                    
+ if sz[sz[0]+1] NE 8 then fits_open,file_or_fcb,fcb else fcb=file_or_fcb
+ if fcb.nextend EQ 0 then begin 
+          message,'File contains no Table extensions',/INF
+          if sz[sz[0]+1] NE 8 then fits_close,fcb else $
+                      file_or_fcb.last_extension = exten_no
+          return
+  endif
+ if N_elements(exten_no) EQ 0 then exten_no = indgen(fcb.nextend)+1
+
+ nprint  = N_elements(exten_no)
+ textopen,'ftab_help',textout=textout
+ printf,!TEXTUNIT,' '
+printf,!TEXTUNIT, 'FITS file: ' + fcb.filename 
+ printf,!TEXTUNIT,' '
+
+ for i=0, nprint-1 do begin
+
+   fits_read,fcb, dummy, htab, /header_only,/no_pdu, exten_no=exten_no[i]
+     ext_type = fcb.xtension[exten_no[i]]
+
+ image = 0b
+ case ext_type of
+ 'A3DTABLE': binary = 1b
+ 'BINTABLE': binary = 1b
+ 'TABLE': binary = 0b
+ 'IMAGE': image = 1b
+ else: message,'ERROR - Extension type of ' + $
+                ext_type + ' is not a recognized FITS extension'
+ endcase
+
+  enum = exten_no[i]
+  printf,!TEXTUNIT, 'Extension No: ' + strtrim(enum,2)
+
+ if image then begin
+     dimen = sxpar(htab,'NAXIS*')
+     printf, !TEXTUNIT,'FITS Image Extension: Size ' + $
+              strjoin(strtrim(dimen,2),' by ')
+ endif else begin   
+      
+      
+ if binary then tbhelp, htab, TEXTOUT = 5 $
+           else fthelp, htab, TEXTOUT = 5
+ printf,!TEXTUNIT,' '
+ endelse
+ endfor
+ if sz[sz[0]+1] NE 8 then fits_close,fcb else $
+         file_or_fcb.last_extension = enum
+
+  textclose, textout=textout
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ftab_print.pro b/Code/script_idl_mv/astrolib/ftab_print.pro
new file mode 100644
index 0000000000000000000000000000000000000000..63bb8f97a6224e81a00b77d162a492ea4889afc3
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftab_print.pro
@@ -0,0 +1,107 @@
+pro ftab_print,filename,columns,rows, TEXTOUT = textout, FMT = fmt, $
+        EXTEN_NO = exten_no, num_header_lines=num_header_lines, $
+	nval_per_line=nval_per_line
+;+
+; NAME:
+;       FTAB_PRINT
+; PURPOSE:
+;       Print the contents of a FITS (binary or ASCII) table extension.
+; EXPLANATION:
+;       User can specify which rows or columns to print
+;
+; CALLING SEQUENCE:
+;       FTAB_PRINT, filename, columns, rows, 
+;                   [ TEXTOUT=, FMT=, EXTEN_NO= NUM_HEADER_LINES ]
+;
+; INPUTS:
+;       filename - scalar string giving name of a FITS file containing a 
+;               binary or ASCII table
+;       columns - string giving column names, or vector giving
+;               column numbers (beginning with 1).  If a string 
+;               supplied then column names should be separated by comma's.
+;               if not supplied, then all columns are printed.
+;               If set to '*' then all columns are printed in table format 
+;               (1 row per line, binary tables only).
+;       rows - (optional) vector of row numbers to print (beginning with 0).  
+;               If not supplied or set to scalar, -1, then all rows
+;               are printed.
+; OPTIONAL KEYWORD INPUT:
+;       EXTEN_NO - Extension number to read.   If not set, then the first 
+;               extension is printed (EXTEN_NO=1)
+;       FMT = Format string for print display (binary tables only).   If not
+;               supplied, then any formats in the TDISP keyword fields will be
+;               used, otherwise IDL default formats.    For ASCII tables, the
+;               format used is always as stored in the FITS table.
+;       NUM_HEADER_LINES - Number of lines to display the column headers (default
+;               = 1).  By setting NUM_HEADER_LINES to an integer larger than 1,
+;               one can avoid truncation of the headers.   In addition, setting 
+;               NUM_HEADER_LINES will display commented lines indicating
+;               a FORMAT for reading the data, and a suggested call to 
+;              readfmt.pro.    Works for binary tables only
+;       NVAL_PER_LINE - The maximum number of values displayed from a 
+;               multivalued column when printing in table format.   Default = 6
+;       TEXTOUT - scalar number (0-7) or string (file name) determining
+;               output device (see TEXTOPEN).  Default is TEXTOUT=1, output 
+;               to the user's terminal    
+; EXAMPLE:
+;       (1) Print all rows of the first 5 columns of the first extension of the
+;       file 'wfpc.fits'
+;               IDL> ftab_print,'vizier.fits',indgen(5)+1
+; 
+;       (2) Print all columns of the first row to a file 'vizier.dat' in 
+;       'table' format
+;               IDL> ftab_print,'vizier.fits',t='vizier.dat','*',0     
+; SYSTEM VARIABLES:
+;       Uses the non-standard system variables !TEXTOUT and !TEXTUNIT
+;       which must be defined (e.g. with ASTROLIB) prior to compilation.
+; PROCEDURES USED:
+;       FITS_CLOSE, FITS_OPEN, FITS_READ, FTPRINT, TBPRINT
+; HISTORY:
+;       version 1  W. Landsman    August 1997
+;       Check whether data exists   W. Landsman    Feb 2007
+;       Check whether extension exists  W. Landsman  Mar 2010
+;       Added NUM_HEADER_LINES, NVAL_PER_LINE keywords for binary tables 
+;                  W. Landsman Apr 2010
+;-
+;----------------------------------------------------------------------
+ On_error,2
+ compile_opt idl2
+ if N_params() LT 1 then begin
+        print,'Syntax - ftab_print, filename, columns, rows,' 
+        print,'              [EXTEN_NO=, FMT= , TEXTOUT=  ]'
+        return
+ endif
+
+ if not keyword_set(exten_no) then exten_no = 1
+
+ fits_open,filename,fcb
+ if fcb.nextend LT exten_no then begin
+     message,/CON, $
+       'ERROR - Extension ' + strtrim(exten_no,2) + ' not present in FITS file'
+     return
+ endif      
+ 
+ if fcb.axis[1,exten_no] EQ 0 then begin
+     message,/CON, $
+      'ERROR - Extension ' + strtrim(exten_no,2) + ' contains no data'
+     return
+ endif    
+ fits_read,fcb,tab,htab,exten_no=exten_no
+ fits_close,fcb
+
+ ext_type = fcb.xtension[exten_no]
+
+ case ext_type of
+ 'A3DTABLE': binary = 1b
+ 'BINTABLE': binary = 1b
+ 'TABLE': binary = 0b
+ else: message,'ERROR - Extension type of ' + $
+                ext_type + ' is not a FITS table format'
+ endcase
+
+ if binary then tbprint,htab,tab,columns,rows, TEXTOUT = textout,fmt=fmt, $
+                   num_header_lines=num_header_lines,  $
+		   nval_per_line=nval_per_line         $
+           else ftprint,htab,tab,columns,rows, TEXTOUT = textout
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ftaddcol.pro b/Code/script_idl_mv/astrolib/ftaddcol.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a53d23a5eca036707924e046361c0be8951f71e7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftaddcol.pro
@@ -0,0 +1,150 @@
+pro ftaddcol,h,tab,name,idltype,tform,tunit,tscal,tzero,tnull
+;+
+; NAME:
+;      FTADDCOL
+; PURPOSE:
+;      Routine to add a field to a FITS ASCII table
+;
+; CALLING SEQUENCE:
+;      ftaddcol, h, tab, name, idltype, [ tform, tunit, tscal, tzero, tnull ]
+;
+; INPUTS:
+;      h - FITS table header.  It will be updated as appropriate
+;      tab - FITS table array.  Number of columns will be increased if
+;               neccessary.
+;      name - field name, scalar string
+;      idltype - idl data type (as returned by SIZE function) for field,
+;               For string data (type=7) use minus the string length.
+;
+; OPTIONAL INPUTS:
+;       tform - format specification 'qww.dd' where q = A, I, E, or D
+;       tunit - string giving physical units for the column.
+;       tscal - scale factor
+;       tzero - zero point for field
+;       tnull - null value for field
+;
+;       Use '' as the value of tform,tunit,tscal,tzero,tnull if you want
+;       the default or no specification of them in the table header.
+;
+; OUTPUTS:
+;       h,tab - updated to allow new column of data
+;
+; PROCEDURES USED:
+;       FTINFO, FTSIZE, GETTOK(), SXADDPAR
+; HISTORY:
+;       version 1  D. Lindler   July, 1987
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Updated call to new FTINFO   W. Landsman   April 2000
+;-
+  On_error,2
+  if N_params() LT 2 then begin
+      print,'Syntax - FTADDCOL, h, tab, name, idltype, ' 
+      print,'                [ tform, tunit, tscal, tzero, tnull ]'
+      return
+  endif
+
+; get table size
+
+ ftsize,h,tab,ncols,nrows,tfields,allcols,allrows
+
+; check to see if column name is a string
+
+ s = size(name)
+ if (s[0] NE 0) or (s[1] NE 7) then $
+        message,'Column name must be a string'
+
+; check to see if column already exists
+
+ ftinfo,h,ft_str, Count = count
+ if Count GT 0 then begin
+    g = where(strtrim(ft_str.ttype,2) EQ strupcase(name), Ng)
+    if Ng GT 0 then message,'ERROR - Column '+name+' already exists'
+ endif
+
+; set non specified inputs to ''
+
+ npar = N_params()
+ if npar lt 5 then tform = ''
+ if npar lt 6 then tunit = ''
+ if npar lt 7 then tscal = ''
+ if npar lt 8 then tzero = ''
+ if npar lt 9 then tnull = ''
+
+; create default format if not supplied
+
+ if tform eq '' then begin
+        case idltype of
+                1:      tform = 'I4'            ;byte
+                2:      tform = 'I6'            ;integer*2
+                4:      tform = 'E15.8'         ;real*4
+                3:      tform = 'I11'           ;longword
+                5:      tform = 'D23.8'         ;real*8
+                else: begin
+                        if idltype LT 0 then begin      ;string
+                            tform = 'A'+strtrim(fix(abs(idltype)),2)
+                            idltype = 7
+                          end else message,'Invalid idltype specified'
+                      end
+        end; case
+ end
+
+; get field width from format
+
+ width = fix(gettok(strmid(tform,1,strlen(tform)-1),'.'))
+
+;
+; is present allocated table size large enough?
+;
+;  If the new field is not a string, put a zero in the leftmost position
+;  of the record so that a "Type conversion error" won't occur.
+;
+ if (width+ncols) GT allcols then begin
+    tab = [ tab, replicate(32B,width,allrows)]          ;increase size  
+    if (idltype NE 7) then tab[allcols,*] = 48B
+ endif
+
+;
+; update header
+;
+ tfields = tfields+1
+ apos = strtrim(tfields,2)
+ ttype = strupcase(name)                                        ;ttype
+ while strlen(ttype) lt 8 do ttype = ttype+' '
+ sxaddpar,h,'TTYPE'+apos,ttype,'','HISTORY'
+
+;
+ sxaddpar,h,'TBCOL'+apos,ncols+1,'','HISTORY'           ;tbcol (WBL 2-88)
+
+;
+ while strlen(tform) lt 8 do tform = tform+' '          ;tform
+ sxaddpar,h,'TFORM'+apos,tform,'','HISTORY'
+
+
+ if tunit NE '' then begin                              ;tunit
+        while strlen(tunit) lt 8 do tunit = tunit+' '
+        sxaddpar,h,'tunit'+apos,tunit,'','HISTORY'
+ end
+
+ if string(tscal) NE '' then $
+        sxaddpar,h,'tscal'+apos,tscal,'','HISTORY'      ;tscal
+
+
+ if string(tzero) NE '' then $
+        sxaddpar,h,'tzero'+apos,tzero,'','HISTORY'      ;tzero
+
+ if string(tnull) NE '' then begin                      ;tnull
+        s = size(tnull) & type = s[s[0]+1]
+        if type NE 1 then stnull = string(tnull,'('+strtrim(tform)+')') $
+                     else stnull = tnull
+        while strlen(stnull) LT 8 do stnull = stnull+' '
+        sxaddpar, h, 'TNULL' + apos, stnull, '', 'HISTORY'
+ end
+
+;
+; increase table size in header
+;
+ sxaddpar,h,'TFIELDS',tfields
+ sxaddpar,h,'NAXIS1',ncols+width
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ftcreate.pro b/Code/script_idl_mv/astrolib/ftcreate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5602ed399074680b21dd26d802a959296cb94503
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftcreate.pro
@@ -0,0 +1,55 @@
+pro ftcreate, MAXCOLS,MAXROWS,H,TAB
+;+
+; NAME:
+;       FTCREATE
+; PURPOSE:
+;       Create a new (blank) FITS ASCII table and header with specified size.
+;
+; CALLING SEQUENCE:
+;       ftcreate, maxcols, maxrows, h, tab
+;
+; INPUTS:
+;       maxcols - number of character columns allocated, integer scalar
+;       maxrows - maximum number of rows allocated, integer scalar
+;
+; OUTPUTS:
+;       h - minimal FITS Table extension header, string array
+; OPTIONAL OUTPUT:
+;       tab - empty table, byte array 
+; HISTORY:
+;       version 1  D. Lindler   July. 87
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Make table creation optional, allow 1 row table, add comments to 
+;       required FITS keywords    W. Landsman    October 2001  
+;-
+;----------------------------------------------------------------------
+ On_error,2
+
+ if n_params() lt 3 then begin
+      print,'Syntax - FTCREATE, maxcols, maxrows, h, [tab]'
+      return
+ endif
+
+; Create blank table if tab output variable supplied 
+
+ if N_params() GE 4 then begin
+            tab = replicate(32B, maxcols, maxrows)
+            if maxrows EQ 1 then tab = reform(tab,maxcols,1)
+ endif
+;
+; Create header (destroy any previous contents) and add required ASCII table 
+; keywords
+;
+ h = strarr(9) + string(' ',format='(a80)')
+ h[0] = 'END' + string(replicate(32b,77))
+ sxaddpar, h, 'XTENSION', 'TABLE   ',' ASCII table extension'
+ sxaddpar, h, 'BITPIX', 8,' 8 bit bytes'
+ sxaddpar, h, 'NAXIS', 2,' 2-dimensional ASCII table'
+ sxaddpar, h, 'NAXIS1', 0,' Width of table in bytes'
+ sxaddpar, h, 'NAXIS2', 0,' Number of rows in table'
+ sxaddpar, h, 'PCOUNT', 0,' Size of special data area'
+ sxaddpar, h, 'GCOUNT', 1,' one data group (required keyword)
+ sxaddpar, h, 'TFIELDS', 0,' Number of fields in each row'
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ftdelcol.pro b/Code/script_idl_mv/astrolib/ftdelcol.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8c9fa9148c01a784f4f5eaf550d39f813d4c7273
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftdelcol.pro
@@ -0,0 +1,114 @@
+pro ftdelcol,h,tab,name                                               
+;+
+; NAME:
+;	FTDELCOL
+; PURPOSE:
+;	Delete a column of data from a FITS table
+;
+; CALLING SEQUENCE:
+;	ftdelcol, h, tab, name
+;
+; INPUTS-OUPUTS
+;	h,tab - FITS table header and data array.  H and TAB will
+;		be updated with the specified column deleted
+;
+; INPUTS:
+;	name - Either (1) a string giving the name of the column to delete
+;		or (2) a scalar giving the column number to delete (starting with 1)
+;       Only 1 column can be deleted at a time
+;
+; EXAMPLE:
+;	Suppose it has been determined that the F7.2 format used for a field
+;	FLUX in a FITS table is insufficient.  The old column must first be 
+;	deleted before a new column can be written with a new format.
+;
+;	flux = FTGET(h,tab,'FLUX')       ;Save the existing values
+;	FTDELCOL,h,tab,'FLUX'            ;Delete the existing column            
+;	FTADDCOL,h,tab,'FLUX',8,'F9.2'   ;Create a new column with larger format
+;	FTPUT,h,tab,'FLUX',0,flux        ;Put back the original values
+;
+; REVISION HISTORY:                                           
+;	Written   W. Landsman        STX Co.     August, 1988
+;	Adapted for IDL Version 2, J. Isensee, July, 1990
+;   Updated call to new FTINFO   W. Landsman  May 2000
+;   Allow specification of column number in addition to field name
+;       M. Nolan/W. Landsman  Sep 2015
+;- 
+; On_error,2
+
+ if N_params() LT 3 then begin
+     print,'Syntax - FTDELCOL, h, tab, name'
+     return
+ endif
+
+ ftsize,h,tab,ncol,nrows,tfields,allcols,allrows
+
+; Make sure column exists
+
+ ftinfo, h, ft_str     ;Get starting column and width (in bytes)
+ sz = size(name)
+ if ((sz[0] ne 0) || (sz[1] EQ 0)) then $
+      message,'Invalid field specification, it must be a scalar'
+
+ if sz[1] EQ 7 then begin        ;If a string, get the field number
+    ttype = strupcase( strtrim(ft_str.ttype,2))
+    field = where(ttype EQ strupcase(strtrim(name,2)), Npos) + 1
+    if Npos EQ 0 then message, $ 
+        'Specified field ' + strupcase(strtrim(name,2)) + ' not in FITS table'
+ endif else begin         ;Column number supplied
+    field = long(name)
+    if (field LT 1 || field GT n_elements(ft_str.ttype)) then message, $
+         'Column number must be between 1 and ' + strtrim(n_elements(ft_str.ttype),2)
+ endelse
+    
+
+; Eliminate relevant columns from TAB
+
+ field = field[0]
+ tbcol = ft_str.tbcol[field-1]-1                     ;Convert to IDL indexing
+ width = ft_str.width[field-1]
+ case 1 of 
+ tbcol eq 0: tab = tab[width:*,*]                     ;First column
+ tbcol eq ncol-width: tab = tab[0:tbcol-1,*]          ;Last column
+ else: tab = [tab[0:tbcol-1,*],tab[tbcol+width:*,*]]  ;All other columns
+ endcase
+
+; Parse the header.  Remove specified keyword from header.  Lower
+; the index of subsequent keywords.  Update the TBCOL*** index of
+; subsequent keywords
+
+ nh = N_elements(h)
+ hnew = strarr(nh)
+ j = 0
+ key = strupcase(strmid(h,0,5))
+ for i= 0,nh-1 do begin    ;Loop over each element in header
+ if (key[i] eq 'TTYPE') || (key[i] eq 'TFORM') || (key[i] eq 'TUNIT') || $
+    (key[i] eq 'TNULL') || (key[i] eq 'TBCOL') then begin
+    row = h[i]                    
+    ifield = fix(strtrim(strmid(row,5,3)))    
+    if ifield GT field then begin    ;Subsequent field?
+      if ifield le 10 then fmt = "(I1,' ')" else fmt ='(I2)'
+      strput,row,string(ifield-1,format=fmt),5
+      if key[i] eq 'TBCOL' then begin
+         value = fix(strtrim(strmid(row,10,20)))-width
+         v = string(value)
+         s = strlen(v)
+         strput,row,v,30-s                  ;Right justify
+      endif
+   endif 
+   if ifield ne field then hnew[j] = row else j--
+
+ endif else hnew[j] = h[i]      
+
+ j++
+ endfor   
+
+ sxaddpar,hnew,'TFIELDS',tfields-1 ;Reduce number of fields by 1
+ sxaddpar,hnew,'NAXIS1',ncol-width ;Reduce num. of columns by WIDTH
+
+ h = hnew[0:j-1]
+ message,'Field '+ strtrim(strupcase(name),2) + $
+         ' has been deleted from the FITS table',/INF
+
+ return  
+ end
diff --git a/Code/script_idl_mv/astrolib/ftdelrow.pro b/Code/script_idl_mv/astrolib/ftdelrow.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5e64b7e475b0ea608f1dae09620a50ae65138e11
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftdelrow.pro
@@ -0,0 +1,74 @@
+pro ftdelrow,h,tab,rows                                               
+;+
+; NAME:
+;       FTDELROW
+; PURPOSE:
+;       Delete a row of data from a FITS table
+;
+; CALLING SEQUENCE:
+;       ftdelrow, h, tab, rows
+;
+; INPUTS-OUPUTS
+;       h,tab - FITS table header and data array.  H and TAB will
+;               be updated on output with the specified row(s) deleted.
+;       rows  -  scalar or vector, specifying the row numbers to delete
+;               This vector will be sorted and duplicates removed by FTDELROW
+;
+; EXAMPLE:
+;       Compress a table to include only non-negative flux values
+;
+;       flux = FTGET(h,tab,'FLUX')       ;Obtain original flux vector
+;       bad = where(flux lt 0)           ;Find negative fluxes
+;       FTDELROW,h,tab,bad               ;Delete rows with negative fluxes
+;
+; PROCEDURE:
+;       Specified rows are deleted from the data array, TAB.  The NAXIS2
+;       keyword in the header is updated.
+;
+; PROCEDURES USED:
+;       sxaddpar
+;
+; REVISION HISTORY:                                           
+;       Written   W. Landsman        STX Co.     August, 1988
+;       Checked for IDL Version 2, J. Isensee, July, 1990
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Assume since V5.4, use BREAK instead of GOTO  W. Landsman April 2006
+;   
+;- 
+ On_error,2
+
+ if N_params() LT 3 then begin
+     print,'Syntax - ftdelrow,h,tab,rows'
+     return                                                  
+ endif
+
+ nrows = sxpar(h,'NAXIS2')            ;Original number of rows
+ if (max(rows) GE nrows) or (min(rows) LT 0) then $
+     message,'Specified rows must be between 0 and ' + strtrim(nrows-1,2)
+
+ ndel = N_elements(rows)
+ if ndel GT 1 then begin
+     rows = rows[rem_dup(rows)]            ;Sort and remove duplicate values
+     ndel = N_elements(rows)
+ endif
+
+ j = 0L
+ i = rows[0]
+ for k = long(rows[0]),nrows-1 do begin
+ if k EQ rows[j] then begin
+     j = j+1 
+     if j EQ ndel then BREAK
+ endif else begin
+     tab[0,i] = tab[*,k]
+     i = i+1
+ endelse
+ 
+ endfor
+ k = k-1
+
+ if k NE nrows-1 then tab[0,i] = tab[*,i+j:nrows-1]
+ tab = tab[*,0:nrows-ndel-1]
+ sxaddpar,h,'NAXIS2',nrows-ndel      ;Reduce number of rows
+
+ return  
+ end
diff --git a/Code/script_idl_mv/astrolib/ftget.pro b/Code/script_idl_mv/astrolib/ftget.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a5f6885fb1f49fbc9aa643ff035855b41186184e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftget.pro
@@ -0,0 +1,146 @@
+function ftget,hdr_or_ftstr,tab,field,rows,nulls
+;+
+; NAME:
+;      FTGET 
+; PURPOSE:
+;      Function to return value(s) from specified column in a FITS ASCII table
+;
+; CALLING SEQUENCE
+;      values = FTGET( h, tab, field, [ rows, nulls ] )
+;                    or
+;      values = FTGET( ft_str, tab, field. [rows, nulls]
+; INPUTS:
+;      h - FITS ASCII extension header (e.g. as returned by FITS_READ)
+;                            or
+;      ft_str - FITS table structure extracted from FITS header by FTINFO
+;                Use of the IDL structure will improve processing speed
+;      tab - FITS ASCII table array (e.g. as returned by FITS_READ)
+;      field - field name or number
+;
+; OPTIONAL INPUTS:
+;      rows -  scalar or vector giving row number(s)
+;               Row numbers start at 0.  If not supplied or set to
+;               -1 then values for all rows are returned
+;
+; OUTPUTS:
+;       the values for the row are returned as the function value.
+;       Null values are set to 0 or blanks for strings.
+;
+; OPTIONAL OUTPUT:
+;       nulls - null value flag of same length as the returned data.
+;               It is set to 1 at null value positions and 0 elsewhere.
+;               If supplied then the optional input, rows, must also 
+;               be supplied.
+;
+; EXAMPLE:
+;       Read the columns labeled 'WAVELENGTH' and 'FLUX' from the second
+;       (ASCII table) extension of a FITS file 'spectra.fit'
+;
+;       IDL> fits_read,'spectra.fit',tab,htab,exten=2     ;Read 2nd extension
+;       IDL> w = ftget( htab, tab,'wavelength')      ;Wavelength vector
+;       IDL> f = ftget( htab, tab,'flux')            ;Flux vector
+;
+;       Slightly more efficient would be to first call FTINFO
+;       IDL> ftinfo, htab, ft_str                     ;Extract structure
+;       IDL> w = ftget(ft_str, tab,'wavelength')      ;Wavelength vector
+;       IDL> f = ftget(ft_str, tab,'flux')            ;Flux vector
+;
+; NOTES:
+;       (1) Use the higher-level procedure FTAB_EXT to extract vectors 
+;               directly from the FITS file.
+;       (2) Use FTAB_HELP or FTHELP to determine the columns in a particular
+;               ASCII table.
+; HISTORY:
+;       coded by D. Lindler  July, 1987
+;       Always check for null values    W. Landsman          August 1990
+;       More informative error message  W. Landsman          Feb. 1996
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Allow structure rather than FITS header  W. Landsman   May 2000
+;       No case sensitivity in TTYPE name      W. Landsman   February 2002
+;-
+;------------------------------------------------------------------
+; On_error,2
+
+  sz = size(tab)
+  nrows = sz(2)
+
+; get characteristics of specified field
+
+ size_hdr = size(hdr_or_ftstr)
+ case size_hdr[size_hdr[0]+1] of 
+      7: ftinfo,hdr_or_ftstr,ft_str
+      8: ft_str = hdr_or_ftstr
+      else: message,'ERROR - Invalid FITS header or structure supplied' 
+ endcase
+ 
+ sz = size(field)
+ if ((sz[0] ne 0) or (sz[1] EQ 0)) then $
+      message,'Invalid field specification, it must be a scalar'
+
+ if sz[1] EQ 7 then begin
+    field = strupcase(strtrim(field,2))
+    ttype = strupcase( strtrim(ft_str.ttype,2) )
+    ipos = where(ttype EQ field, Npos)
+    if Npos EQ 0 then message, $ 
+        'Specified field ' + strupcase(strtrim(field,2)) + ' not in table'
+ endif else ipos = field -1
+ ipos = ipos[0]
+
+ tbcol = ft_str.tbcol[ipos]-1                   ;IDL starts at zero not one
+ width = ft_str.width[ipos]
+ tnull = ft_str.tnull[ipos]
+ idltype = ft_str.idltype[ipos]
+
+; if rows not supplied then return all rows
+
+ if N_params() LT 4 then rows = -1
+
+; determine if scalar supplied
+
+ row = rows
+ s = size(row) & ndim = s[0]
+ if ndim EQ 0 then begin                ;scalar?
+        if row LT 0 then begin  ; -1 get all rows
+                ndim = 1
+                row = lindgen(nrows)
+           end else begin
+                row = lonarr(1) + row
+        end
+ end
+
+; check for valid row numbers
+
+ if (min(row) lt 0) or (max(row) gt (nrows-1)) then $
+        message,'ERROR - Row numbers must be between 0 and ' + $
+                strtrim((nrows-1),2)
+
+; get column
+
+ if ndim EQ 0 then begin                                        ;scalar?
+        dd = string(tab[tbcol:tbcol+width-1,row[0]])
+        data = strarr(1)
+        data[0] = dd
+    end else begin                                      ;vector
+        data = string(tab[tbcol:tbcol+width-1,*])
+        data = data[row]
+ end
+
+; check for null values
+   n = N_elements(data)
+   d = make_array(size=[1,n,idltype,n])
+  
+ if strlen(tnull) GT 0 then begin  
+    len = strlen(data[0])       ;field size
+    while strlen(tnull) LT len do tnull = tnull + ' '   ;pad with blanks
+    if strlen(tnull) GT len then tnull = strmid(tnull,0,len)
+    nulls = data EQ tnull
+    valid = where(nulls EQ 0b, nvalid)
+
+; convert data to the correct type
+
+    if nvalid GT 0 then d[valid] = data[valid]
+
+ endif else d[0] = strtrim(data,2)
+
+    return,d
+ end
diff --git a/Code/script_idl_mv/astrolib/fthelp.pro b/Code/script_idl_mv/astrolib/fthelp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..63eb46f35d82a76ff14a995d45dfdd537245b608
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fthelp.pro
@@ -0,0 +1,96 @@
+pro fthelp,h,TEXTOUT=textout
+;+
+; NAME:
+;       FTHELP
+; PURPOSE:
+;       Routine to print a description of a FITS ASCII table extension
+;
+; CALLING SEQUENCE:
+;       FTHELP, H, [ TEXTOUT = ]
+;
+; INPUTS:
+;       H - FITS header for ASCII table extension, string array
+;
+; OPTIONAL INPUT KEYWORD
+;       TEXTOUT - scalar number (0-7) or string (file name) determining
+;               output device (see TEXTOPEN).  Default is TEXTOUT=1, output 
+;               to the user's terminal    
+;
+; NOTES:
+;       FTHELP checks that the keyword XTENSION  equals 'TABLE' in the FITS
+;               header.
+;
+; SYSTEM VARIABLES:
+;       Uses the non-standard system variables !TEXTOUT and !TEXTUNIT
+;       which must be defined (e.g. with ASTROLIB) prior to compilation.
+; PROCEDURES USED:
+;       REMCHAR, SXPAR(), TEXTOPEN, TEXTCLOSE, ZPARCHECK
+;
+; HISTORY:
+;       version 1  W. Landsman  Jan. 1988
+;       Add TEXTOUT option, cleaner format  W. Landsman   September 1991
+;       TTYPE value can be longer than 8 chars,  W. Landsman  August 1995
+;       Remove calls to !ERR, some vectorization  W. Landsman  February 2000 
+;       Slightly more compact display  W. Landsman  August 2005
+;-
+ compile_opt idl2
+ On_error,2                                  ;Return to caller
+
+ if N_params() EQ 0 then begin
+     print,'Syntax - FTHELP, hdr, [ TEXTOUT = ]'
+     return
+ endif
+
+ zparcheck,'FTHELP',h,1,7,1,'Table Header'     ;Make sure a string array
+
+ n = sxpar( h, 'TFIELDS' , Count = N_TFields) 
+ if N_TFields EQ 0 then message, $
+        'ERROR - FITS Header does not include required TFIELDS keyword'
+ if strtrim(sxpar(h,'XTENSION'),2) ne 'TABLE' then $
+        message,'WARNING - Header is not for a FITS Table',/INF
+
+ if not keyword_set(TEXTOUT) then textout = 1
+ textopen,'fthelp',TEXTOUT=textout
+
+ naxis = sxpar( h, 'NAXIS*')
+ printf,!TEXTUNIT,'FITS ASCII Table: ' +$
+        'Size ',strtrim(naxis[0],2),' by ',strtrim(naxis[1],2)
+
+ extname = sxpar(h,'EXTNAME', Count=N_ext)	
+ if N_ext GT 0 then printf,!TEXTUNIT, 'Extension Name:   ',sxpar(h,'EXTNAME')
+ extver = sxpar(h, 'EXTVER', Count = N_extver)
+ if N_extver GT 0 then printf,!TEXTUNIT,'Version: ',extver
+ printf,!TEXTUNIT,' '                         
+ printf,!TEXTUNIT,  $
+ 'Field      Name               Unit           Format     Column'
+
+ tbcol = intarr(n)
+ tform = strarr(n) & tunit = tform & ttype =tform
+ name = strmid(h,0,5)
+ number = strtrim(strmid(h,5,3),2)
+ value = strtrim(strmid(h,11,20),2)
+
+ for i = 1, N_elements(h)-1 do begin
+  case name[i] of
+   'TTYPE':  ttype[fix(number[i]-1)] = value[i]
+   'TFORM':  tform[fix(number[i]-1)] = value[i]
+   'TUNIT':  tunit[fix(number[i]-1)] = value[i]
+   'TBCOL':  tbcol[fix(number[i]-1)] = fix(value[i])
+   'END  ':  goto, DONE 
+    ELSE :
+ end
+
+ endfor
+
+DONE:                            ;Done reading FITS header
+
+ ttype = strtrim(ttype,2) & remchar,ttype,"'"
+ remchar,tunit,"'"
+ remchar,tform,"'"
+ for i = 0,n-1 do  printf,!TEXTUNIT,i+1,ttype[i],tunit[i],tform[i],tbcol[i], $
+              f='(I5,T9,A,T30,A,T47,A,T55,I8)'
+
+ textclose,TEXTOUT=textout
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/fthmod.pro b/Code/script_idl_mv/astrolib/fthmod.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2e1e8d38a86f83f87397c698dcbf3a62d7dd5d7f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fthmod.pro
@@ -0,0 +1,63 @@
+pro fthmod,h,field,parameter,value
+;+
+; NAME:
+;       FTHMOD
+; PURPOSE:
+;       Procedure to modify header information for a specified field
+;       in a FITS table.
+;
+; CALLING SEQUENCE:
+;       fthmod, h, field, parameter, value
+;       
+; INPUT:
+;       h - FITS header for the table
+;       field - field name or number
+;       parameter - string name of the parameter to modify.  Choices
+;               include:
+;                       TTYPE - field name
+;                       TUNIT - physical units for field (eg. 'ANGSTROMS')
+;                       TNULL - null value (string) for field, (eg. '***')
+;                       TFORM - format specification for the field
+;                       TSCAL - scale factor
+;                       TZERO - zero offset
+;               User should be aware that the validity of the change is
+;               not checked.  Unless you really know what you are doing,
+;               this routine should only be used to change field names,
+;               units, or another user specified parameter.
+;       value - new value for the parameter.  Refer to the FITS table
+;               standards documentation for valid values.
+;
+; EXAMPLE:
+;      Change the units for a field name "FLUX" to "Janskys" in a FITS table
+;        header,h
+;
+;      IDL> FTHMOD, h, 'FLUX', 'TUNIT','Janskys' 
+; METHOD:
+;       The header keyword <parameter><field number> is modified
+;       with the new value.
+; HISTORY:
+;       version 1, D. Lindler  July 1987
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Major rewrite to use new FTINFO call   W. Landsman   May 2000
+;-
+;-----------------------------------------------------------------------
+on_error,2
+
+ ftinfo,h,ft_str
+ sz = size(field)
+ if ((sz[0] ne 0) or (sz[1] EQ 0)) then $
+      message,'Invalid field specification, it must be a scalar'
+
+ if sz[1] EQ 7 then begin
+    field = strupcase(strtrim(field,2))
+    ttype = strtrim(ft_str.ttype,2)
+    ipos = where(ttype EQ field, Npos)
+    if Npos EQ 0 then message, $ 
+        'Specified field ' + strupcase(strtrim(field,2)) + ' not in table'
+ endif else ipos = field -1
+
+;
+ par = parameter+strtrim(ipos[0]+1,2)
+ sxaddpar,h,par,value
+return
+end
diff --git a/Code/script_idl_mv/astrolib/ftinfo.pro b/Code/script_idl_mv/astrolib/ftinfo.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c5230fd8583d4cd89e4fbd8f3f54a8657802683f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftinfo.pro
@@ -0,0 +1,116 @@
+pro ftinfo, h, ft_str, Count = tfields
+;+
+; NAME:
+;       FTINFO
+; PURPOSE:
+;       Return an informational structure from a FITS ASCII table header.
+; CALLING SEQUENCE:
+;       ftinfo,h,ft_str, [Count = ]
+;
+; INPUTS:
+;       h - FITS ASCII table header, string array
+;
+; OUTPUTS:
+;       ft_str - IDL structure with extracted info from the FITS ASCII table
+;                header.   Tags include
+;        .tbcol - starting column position in bytes
+;        .width - width of the field in bytes
+;        .idltype - idltype of field.
+;                       7 - string, 4- real*4, 3-integer, 5-real*8
+;        .tunit - string unit numbers
+;        .tscal - scale factor
+;        .tzero - zero point for field
+;        .tnull - null value for the field
+;        .tform - format for the field
+;        .ttype - field name
+;
+; OPTIONAL OUTPUT KEYWORD:
+;       Count - Integer scalar giving number of fields in the table
+; PROCEDURES USED:
+;       GETTOK(), SXPAR()
+; NOTES:
+;       This procedure underwent a major revision in May 2000, and **THE
+;       NEW CALLING SEQUENCE IS INCOMPATIBLE WITH THE OLD ONE **
+; HISTORY:
+;       D. Lindler  July, 1987
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Major rewrite, return structure   W. Landsman   April 2000
+;-
+;----------------------------------------------------------------------------
+; On_error,2
+;
+  if N_params() LT 2 then begin
+      print,'Syntax - FTINFO, header, ft_str'
+      return
+  endif
+
+; get number of fields
+
+ tfields = sxpar( h, 'TFIELDS' , Count = N_TFields)
+ if N_TFields EQ 0 then $
+        message,'Invalid FITS header. keyword TFIELDS is missing'
+
+ if tfields EQ 0 then return
+ tbcol = intarr(tfields)
+ tform = replicate(' ',tfields)
+
+; get info for specified field
+
+ ttype = sxpar(h,'ttype*',Count=N_ttype)                    ;field name
+ if N_ttype EQ 0 then ttype = strarr(tfields)
+
+ tbcol[0] = sxpar(h,'tbcol*', Count = N_tbcol)       ;starting column position
+ if N_tbcol NE tfields then message,/CON, $
+ 'Warning - Invalid FITS table header -- TBCOL not present for all fields'
+;
+ tform[0] = strtrim(sxpar(h,'tform*', Count = N_tform),2)   ; column format
+ if N_tform NE tfields then message,/CON, $
+   'Warning - Invalid FITS table header -- TFORM  not present for all fields'
+ ;                                               ; physical units
+ tunit = strarr(Tfields)
+ temp = sxpar(h, 'TUNIT*', Count = N_tunit)
+ if N_tunit GT 0 then tunit[0] = temp
+
+ tscal = fltarr(Tfields)
+ temp = sxpar(h, 'TSCAL*', Count = N_tscal)      ; data scale factor
+ if N_tscal GT 0 then tscal[0] = temp
+
+ tzero = fltarr(tfields)
+ temp = sxpar(h,'TZERO*', Count = N_tzero)       ; zero point for field
+ if N_tzero GT 0 then tzero[0] = temp
+
+ tnull = strarr(Tfields)
+ temp = sxpar(h,'TNULL*', Count = N_tnull)       ;null data value
+ if N_tnull GT 0 then tnull[0] = temp
+;
+; determine idl data type from format
+;
+ type = strmid(tform,0,1)
+ idltype = intarr(tfields)
+ for i=0,tfields-1 do begin
+ case strupcase(type[i]) of
+        'A' : idltype[i] = 7
+        'I' : idltype[i] = 3
+        'E' : idltype[i] = 4
+        'F' : idltype[i] = 4
+        'D' : idltype[i] = 5
+        else: message,'Invalid format specification for keyword ' + $
+                        'TFORM' + strtrim(i+1,2)
+ endcase
+ endfor
+;
+; get field width in characters
+;
+ decpos = strpos(tform,'.')
+ decimal = decpos GT 0
+ len = strlen(tform)
+ width = intarr(tfields)
+ for i=0, tfields-1 do begin
+     if decimal[i] then width[i] = fix(strmid(tform[i],1,decpos[i]-1)) else $
+                     width[i] = fix(strmid(tform[i],1,len[i]-1))
+ endfor
+ ft_str = {TBCOL:tbcol,WIDTH:width,IDLTYPE:idltype,TUNIT:tunit, TSCAL:tscal, $
+           TZERO:tzero, TNULL:tnull, TFORM:tform, TTYPE:ttype}
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ftkeeprow.pro b/Code/script_idl_mv/astrolib/ftkeeprow.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f02c4b16bb55430429e72e573bda70da69214b6b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftkeeprow.pro
@@ -0,0 +1,41 @@
+pro ftkeeprow,h,tab,subs
+;+
+; NAME:
+;	FTKEEPROW
+; PURPOSE:
+;	Subscripts (and reorders) a FITS table.  A companion piece to FTDELROW.
+;
+; CALLING SEQUENCE:
+;	ftkeeprow, h, tab, subs
+;
+; INPUT PARAMETERS:
+;	h    = FITS table header array
+;	tab  = FITS table data array
+;	subs = subscript array of FITS table rows.  Works like any other IDL
+;		subscript array (0 based, of course).
+;
+; OUTPUT PARAMETERS:
+;	h and tab are modified
+;
+; MODIFICATION HISTORY:
+;	Written by R. S. Hill, ST Sys. Corp., 2 May 1991.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2                          ;Return to caller
+ 
+ if N_params() LT 3 then begin
+     print,'Syntax - ftkeeprow, h, tab, subs'
+     return
+ endif
+
+ insize = sxpar(h,'NAXIS2')
+ tab = tab[*,subs]
+ outsize = N_elements(subs)
+ sxaddpar, h, 'NAXIS2', outsize
+ tag = 'FTKEEPROW '+systime(0)+': '
+ sxaddhist, tag + 'table subscripted', h
+ sxaddhist, tag + strtrim(string(insize),2) + ' rows in, ' + $
+              strtrim(string(outsize),2) + ' rows out',h
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ftprint.pro b/Code/script_idl_mv/astrolib/ftprint.pro
new file mode 100644
index 0000000000000000000000000000000000000000..71278e0b099da479c08ab8854a5f8b7ebd576728
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftprint.pro
@@ -0,0 +1,170 @@
+pro ftprint,h,tab,columns,rows,textout=textout
+;+
+;  NAME:
+;      FTPRINT
+;  PURPOSE:
+;       Procedure to print specified columns and rows of a FITS table
+;
+; CALLING SEQUENCE:
+;       FTPRINT, h, tab, columns, [ rows, TEXTOUT = ]
+;
+; INPUTS:
+;       h - Fits header for table, string array
+;       tab - table array 
+;       columns - string giving column names, or vector giving
+;               column numbers (beginning with 1).  If string 
+;               supplied then column names should be separated by comma's.
+;       rows - (optional) vector of row numbers to print.  If
+;               not supplied or set to scalar, -1, then all rows
+;               are printed.
+;
+; OUTPUTS:
+;       None
+;
+; OPTIONAL INPUT KEYWORDS:
+;       TEXTOUT controls the output device; see the procedure TEXTOPEN
+;
+; SYSTEM VARIABLES:
+;       Uses nonstandard system variables !TEXTOUT and !TEXTOPEN
+;       These will be defined (using ASTROLIB) if not already present.
+;       Set !TEXTOUT = 3 to direct output to a disk file.   The system
+;       variable is overriden by the value of the keyword TEXTOUT
+;
+; EXAMPLES:
+;
+;       ftprint,h,tab,'STAR ID,RA,DEC'    ;print id,ra,dec for all stars
+;       ftprint,h,tab,[2,3,4],indgen(100) ;print columns 2-4 for 
+;                                         ;first 100 stars
+;       ftprint,h,tab,text="stars.dat"    ;Convert entire FITS table to
+;                                         ;an ASCII file named STARS.DAT
+;
+; PROCEDURES USED:
+;       FTSIZE, FTINFO, TEXTOPEN, TEXTCLOSE
+;
+; RESTRICTIONS: 
+;       (1) Program does not check whether output length exceeds output
+;               device capacity (e.g. 80 or 132).
+;       (2) Column heading may be truncated to fit in space defined by
+;               the FORMAT specified for the column
+;       (3) Program does not check for null values
+;
+; HISTORY:
+;       version 1  D. Lindler Feb. 1987
+;       Accept undefined values of rows, columns   W. Landsman August 1997
+;       New FTINFO calling sequence    W. Landsman   May 2000
+;       Parse scalar string with STRSPLIT   W. Landsman  July 2002
+;       Fix format display of row number  W. Landsman March 2003
+;       Fix format display of row number again  W. Landsman May 2003
+;-
+; On_error,2
+  compile_opt idl2
+;
+; set defaulted parameters
+;
+ if N_params() LT 2 then begin
+   print,'Syntax -  FTPRINT, h, tab, [ columns, rows, TEXTOUT= ]'
+   return
+ endif
+
+ defsysv,'!textout',exists = i
+ if i EQ 0 then astrolib
+ 
+ if N_elements(columns) EQ 0 then columns = -1
+ if N_elements(rows) EQ 0 then rows= -1
+ if  not keyword_set(TEXTOUT)  then textout = !TEXTOUT
+
+; make sure rows is a vector
+
+ n = N_elements(rows)
+ if n EQ 1 then r = [rows] else r = long(rows)
+ ftsize,h,tab,ncols,nrows,tfields,allcols,allrows, ERRMSG = errmsg   ;table size
+ if ERRMSG NE '' then message,errmsg
+ if r[0] EQ -1 then r = lindgen(nrows)          ;default
+
+ Nr = N_elements(r)
+ good = where( (r GE 0) and (r LT nrows), Ngood)
+ if Ngood NE Nr then begin
+      if Ngood EQ 0 then message,'ERROR - No valid row numbers supplied'
+      r = r[good]
+ endif
+;
+; extract column info
+;
+ title1 = ''
+ title2 = ''
+ FTINFO,h,ft_str
+ 
+;
+; if columns is a string, change it to string array
+;
+ if size(columns,/TNAME) EQ 'STRING'  then begin 
+         colnames = strsplit(columns,',',/EXTRACT) 
+         numcol = N_elements(colnames)
+        colnames = strupcase(strtrim(colnames,2))
+        ttype = strtrim(ft_str.ttype,2)
+        colnum = intarr(numcol)
+        for i = 0,numcol-1 do begin
+             icol = where(ttype EQ colnames[i], Nfound) 
+             if Nfound EQ 0 then message, $
+               'ERROR - Field ' + colnames[i] + ' not found in FITS ASCII table'
+             colnum[i] = icol[0] 
+       endfor
+   end else begin                       ;user supplied vector
+        colnum = fix(columns) -1                ;make sure it is integer
+        numcol = N_elements(colnum)     ;number of elements
+        if numcol EQ 1 then begin
+            if colnum[0] LT 0 then begin 
+              colnum = indgen(tfields) & numcol = tfields
+        endif & endif
+ end
+
+ flen = ft_str.width[colnum]
+ colpos = ft_str.tbcol[colnum]
+ ttype = strtrim( ft_str.ttype[colnum],2)
+ tunit = strtrim( ft_str.tunit[colnum],2)
+;
+; create header lines
+;
+  for i=0,numcol-1 do begin
+        name = strn(ttype[i],padtype=2,len=flen[i] )
+        unit = strn(tunit[i],padtype=2,len=flen[i] ) 
+        title1 = title1 + ' ' + name
+        title2 = title2 + ' ' + unit
+  endfor
+;
+; open output file
+;
+ textopen,'FTPRINT',TEXTOUT=textout, MORE_SET = more_set
+
+ ifmt = fix(alog10(max(r)+1)) > 3
+ title1 = strn('ROW',padtype=2,len = ifmt) +  title1
+ title2 = string(replicate(32b,ifmt+1)) + title2
+ ifmt = strtrim(ifmt,2)
+;
+; loop on rows 
+;
+ printf,!TEXTUNIT,title1
+ printf,!TEXTUNIT,title2
+ printf,!TEXTUNIT,' '
+
+  for i = 0, Nr-1 do begin
+;
+; loop on columns
+;
+        line = string(r[i],format='(i' + ifmt + ')')     ;print line
+        for j = 0,numcol-1 do begin
+                cpos=colpos[j]-1                        ;column number
+                val = string(tab[cpos:cpos+flen[j]-1,r[i]])
+                line = line+' '+ val
+        endfor
+        printf,!TEXTUNIT,line
+        if more_set then if (!ERR EQ 1) then goto, DONE
+ endfor
+;
+; done
+;
+DONE: 
+ textclose,textout=textout
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ftput.pro b/Code/script_idl_mv/astrolib/ftput.pro
new file mode 100644
index 0000000000000000000000000000000000000000..cee3f6c5e4a724051e18415ca6b396be322908a2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftput.pro
@@ -0,0 +1,174 @@
+pro ftput,h,tab,field,row,values,nulls
+;+
+; NAME:
+;       FTPUT
+; PURPOSE:
+;       Procedure to add or update a field in an FITS ASCII table
+; CALLING SEQUENCE:
+;       FTPUT, htab, tab, field, row, values, [ nulls ]
+;
+; INPUTS:
+;       htab - FITS ASCII table header string array
+;       tab - FITS ASCII table array (e.g. as read by READFITS)
+;       field - string field name or integer field number
+;       row -  either a non-negative integer scalar giving starting row to 
+;               update, or a non-negative integer vector specifying rows to 
+;               update.   FTPUT will append a new row to a table if the value 
+;               of 'row' exceeds the number of rows in the tab array    
+;       values - value(s) to add or update.   If row is a vector
+;               then values must contain the same number of elements.
+;
+; OPTIONAL INPUT:
+;       nulls - null value flag of same length as values.
+;               It should be set to 1 at null value positions
+;               and 0 elsewhere.
+;
+; OUTPUTS:
+;       htab,tab will be updated as specified.
+;
+; EXAMPLE:
+;       One has a NAME and RA  and Dec vectors for 500 stars with formats A6,
+;       F9.5 and F9.5 respectively.   Write this information to an ASCII table 
+;       named 'star.fits'.
+;
+;       IDL> FTCREATE,24,500,h,tab       ;Create table header and (empty) data
+;       IDL> FTADDCOL,h,tab,'RA',8,'F9.5','DEGREES'   ;Explicity define the
+;       IDL> FTADDCOL,h,tab,'DEC',8,'F9.5','DEGREES'  ;RA and Dec columns
+;       IDL> FTPUT,h,tab,'RA',0,ra       ;Insert RA vector into table
+;       IDL> FTPUT,h,tab,'DEC',0,dec       ;Insert DEC vector into table
+;       IDL> FTPUT, h,tab, 'NAME',0,name   ;Insert NAME vector with default
+;       IDL> WRITEFITS,'stars.fits',tab,h ;Write to a file
+;   
+;      Note that (1) explicit formatting has been supplied for the (numeric)
+;      RA and Dec vectors, but was not needed for the NAME vector, (2) A width
+;      of 24 was supplied in FTCREATE based on the expected formats (6+9+9),
+;      though the FT* will adjust this value as necessary, and (3) WRITEFITS
+;      will create a minimal primary header  
+; NOTES:
+;       (1) If the specified field is not already in the table, then FTPUT will
+;       create a new column for that field using default formatting.   However,
+;        FTADDCOL should be called prior to FTPUT for explicit formatting.
+;
+; PROCEDURES CALLED
+;       FTADDCOL, FTINFO, FTSIZE, SXADDPAR, SXPAR()
+; HISTORY:
+;       version 1  D. Lindler July, 1987
+;       Allow E format         W. Landsman          March 1992
+;       Write in F format if E format will overflow    April 1994
+;       Update documentation W. Landsman   January 1996
+;       Allow 1 element vector  W. Landsman   March 1996
+;       Adjust string length to maximum of input string array   June 1997
+;       Work for more than 32767 elements August 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Updated call to the new FTINFO   W. Landsman   May 2000
+;       Fix case where header does not have any columns yet W.Landsman Sep 2002
+;       Assume since V5.2, omit fstring() call  W. Landsman April 2006
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 5 then begin
+    print,'Syntax - FTPUT, htab, tab, field, row, values, [nulls]'
+    return
+ endif
+
+ nrow = N_elements(row)        ;Number of elements in row vector
+
+ nullflag = N_elements(nulls) GT 0         ;Null values supplied?
+
+ ftsize,h,tab,ncols,nrows,tfields,allcols,allrows     ; Get size of table
+
+; Make values a vector if scalar supplied
+
+ s = size(values) & ndim = s[0] & type = s[ndim+1]
+
+ if ndim gt 1 then $
+        message,'Input values must be scalar or 1-D array'
+
+ sz_row = size(row)
+ scalar = sz_row[0] EQ 0
+
+ v = values
+ if nullflag then nullvals = nulls
+
+; Get info on field specified
+
+ ftinfo,h,ft_str, Count = tfields
+ if tfields EQ 0 then ipos = -1 else begin
+  if size(field,/TNAME) EQ 'STRING' then begin
+    field = strupcase(strtrim(field,2))
+    ttype = strtrim(ft_str.ttype,2)
+    ipos = where(ttype EQ field, Npos)
+ endif else ipos = field -1
+ endelse
+
+ if ipos[0] EQ -1 then begin            ;Does it exist?
+
+; Add new column if it doesn't exist
+
+          if type EQ 7 then type = (-max(strlen(v)))
+          ftaddcol, h, tab, field, type
+          ftinfo,h,ft_str
+          ftsize,h,tab,ncols,nrows,tfields,allcols,allrows
+          ipos = tfields-1
+ endif 
+
+ ipos = ipos[0]
+ tbcol = ft_str.tbcol[ipos]-1                   ;IDL starts at zero not one.
+
+; Convert input vector to string array
+
+ n = N_elements(v)
+ data = string(replicate(32b, ft_str.width[ipos], n ) )
+ if nrow GT 1 then if (nrow NE n) then $
+        message,'Number of specified rows must equal number of values'
+
+ fmt = strupcase(strtrim(ft_str.tform[ipos],2))
+ fmt1 = strmid(fmt,0,1)
+ if (fmt1 EQ 'D') or (fmt1 EQ 'E') then begin  ;Need at least 6 chars for E fmt
+        point = strpos(fmt,'.')
+        wid = fix(strmid(fmt,1,point-1))
+        decimal = fix(strmid(fmt,point+1,1000))
+        if wid-decimal LT 6 then fmt = 'F' + strmid(fmt,1,1000)
+ endif
+ fmt = '(' + fmt + ')'
+ data = string(v, FORMAT = fmt)
+
+; insert null values
+
+ if nullflag GT 5 then begin
+        bad = where(nullvals, Nbad)
+        if Nbad GT 0 then for i = 0L, Nbad-1 do data[bad[i]] = tnull
+ end
+
+;
+; Do we need to increase the number of rows in the table?
+;
+if scalar then maxrow = row+n else maxrow = max(row) + 1
+if maxrow GT allrows then begin         ;expand table size
+
+    ;
+    ;  Create a replacement table with the required number of rows.
+    ;
+    newtab = replicate(32b,allcols,maxrow)
+    newtab[0,0] = tab
+
+    ;
+    ;  Move the new table into the old table.
+    ;
+    tab = newtab
+
+end
+ if maxrow GT nrows then sxaddpar,h,'naxis2',maxrow
+
+;
+;  Now insert into table.
+;
+  if scalar then tab[tbcol,row] = byte(data) $
+  else for i = 0L,N_elements(row)-1 do tab[tbcol,row[i]] = byte(data[i])
+
+;
+;  Return to calling routine.
+;
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ftsize.pro b/Code/script_idl_mv/astrolib/ftsize.pro
new file mode 100644
index 0000000000000000000000000000000000000000..81c633c1adedcee40a1e8d829091be8f8a9e6b8c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftsize.pro
@@ -0,0 +1,73 @@
+pro ftsize,h,tab,ncols,nrows,tfields,ncols_all,nrows_all, ERRMSG = ERRMSG
+;+
+; NAME:
+;       FTSIZE
+; PURPOSE:
+;       Procedure to return the size of a FITS ASCII table.
+;
+; CALLING SEQUENCE:
+;       ftsize,h,tab,ncols,rows,tfields,ncols_all,nrows_all, [ERRMSG = ]
+;
+; INPUTS:
+;       h - FITS ASCII table header, string array
+;       tab - FITS table array, 2-d byte array
+;
+; OUTPUTS:
+;       ncols - number of characters per row in table
+;       nrows - number of rows in table
+;       tfields - number of fields per row
+;       ncols_all - number of characters/row allocated (size of tab)
+;       nrows_all - number of rows allocated
+;
+; OPTIONAL OUTPUT KEYWORD:
+;       ERRMSG  = If this keyword is present, then any error messages will be
+;                 returned to the user in this parameter rather than
+;                 depending on the MESSAGE routine in IDL.  If no errors are
+;                 encountered, then a null string is returned.  
+; HISTORY
+;       D. Lindler  July, 1987
+;       Fix for 1-row table,  W. Landsman    HSTX,     June 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added ERRMSG keyword   W. Landsman   May 2000
+;       
+;-
+;------------------------------------------------------------------------
+ On_error,2
+
+; check for valid header type
+
+ s=size(h) & ndim=s[0] & type=s[ndim+1]
+ save_err = arg_present(errmsg)
+ errmsg = ''
+
+ if (ndim ne 1) or (type ne 7) then begin 
+        errmsg = 'Invalid FITS header, it must be a string array'
+        if not save_err then message,'ERROR - ' + errmsg
+ endif
+ 
+; check for valid table array
+
+ s = size(tab) & ndim = s[0] & vtype = s[ndim+1]
+ if (vtype ne 1) then begin                  ;Mod June 1994, for degenerate dim.
+        errmsg = 'Invalid table array, it must be a 2-D byte array'
+        if not save_err then message,'ERROR - ' + errmsg
+  endif
+
+ ncols_all = s[1]                       ;allocated characters per row
+ nrows_all = s[2]                       ;allocated rows
+
+; Get number of fields
+
+ tfields = sxpar(h,'TFIELDS', Count = N)  
+ if N LT 0 then begin
+        errmsg = 'Invalid FITS ASCII table header, TFIELDS keyword missing'
+        if not save_err then message,'ERROR - ' + errmsg
+ endif
+
+; Get number of columns and rows
+
+ ncols = sxpar(h, 'NAXIS1')
+ nrows = sxpar(h, 'NAXIS2')
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ftsort.pro b/Code/script_idl_mv/astrolib/ftsort.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0e3c86b528ecb732cadc75413c27d3698f00f56a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ftsort.pro
@@ -0,0 +1,97 @@
+pro ftsort,h,tab,hnew,tabnew,field, reverse = revers
+;+
+; NAME:
+;      FTSORT
+; PURPOSE:
+;      Sort a FITS ASCII table according to a specified field
+;
+; CALLING SEQUENCE:
+;      FTSORT,h,tab,[field, REVERSE = ]               ;Sort original table header and array
+;               or
+;      FTSORT,h,tab,hnew,tabnew,[field, REVERSE =]   ;Create new sorted header
+;
+; INPUTS:
+;      H - FITS header (string array)
+;      TAB - FITS table (byte array) associated with H.  If less than 4
+;               parameters are supplied, then H and TAB will be updated to 
+;               contain the sorted table
+;
+; OPTIONAL INPUTS:
+;      FIELD - Field name(s) or number(s) used to sort the entire table.  
+;              If FIELD is a vector then the first element is used for the 
+;              primary sort, the second element is used for the secondary
+;              sort, and so forth.   (A secondary sort only takes effect when
+;              values in the primary sort  field are equal.)  Character fields
+;              are sorted using the ASCII collating sequence.  If omitted,
+;              the user will be prompted for the field name.
+;
+; OPTIONAL OUTPUTS:
+;      HNEW,TABNEW - Header and table containing the sorted tables
+;
+; EXAMPLE:
+;      Sort a FITS ASCII table by the 'DECLINATION' field in descending order
+;      Assume that the table header htab, and array, tab, have already been
+;      read (e.g. with READFITS or FITS_READ):
+
+;      IDL> FTSORT, htab, tab,'DECLINATION',/REVERSE
+; OPTIONAL INPUT KEYWORD:
+;       REVERSE - If set then the table is sorted in reverse order (maximum
+;              to minimum.    If FIELD is a vector, then REVERSE can also be
+;              a vector.   For example, REVERSE = [1,0] indicates that the
+;              primary sort should be in descending order, and the secondary
+;              sort should be in ascending order.
+;
+; EXAMPLE:
+; SIDE EFFECTS:
+;       A HISTORY record is added to the table header.
+; REVISION HISTORY:
+;      Written W. Landsman                         June, 1988
+;      Converted to IDL V5.0   W. Landsman   September 1997
+;      New FTINFO calling sequence, added REVERSE keyword, allow secondary sorts
+;                  W. Landsman   May 2000
+;-
+ On_error,2 
+ npar = N_params()
+ if npar lt 2 then begin
+        print,'Syntax:  ftsort, h, tab, [ field ]'
+        print,'    OR:  ftsort,h,tab,hnew,tabnew,[field]'
+        return
+ endif
+
+ if npar eq 3 then field = hnew 
+
+ nf = N_elements(field)
+ nr = N_elements(revers)
+ if nr EQ 0 then revers = bytarr(nf) else $
+ if nr LT nf then revers = [revers,bytarr(nf-nr)]
+
+ ftinfo,h,ft_str
+ key = ftget(ft_str,tab, field[nf-1])
+ index = sort(key)
+ if revers[nf-1] then index = reverse(index)
+ tabnew = tab[*,index]
+
+
+ if nf GT 1 then begin 
+ for i= nf-2,0 do begin
+    key = ftget(ft_str,tabnew,field[i])
+    index = bsort(key,reverse=revers[i])
+    tabnew = tabnew[*,index]
+ endfor
+ endif
+
+ str = strtrim(field[0],2)
+ if nf GT 1 then begin
+      for i = 1,nf-1 do str = str + ',' + strtrim( field[i],2) 
+      str = 'Keywords: ' + str
+ endif else str = 'Keyword: ' + str
+ if npar ge 4 then begin
+        hnew = h
+        sxaddhist,'FTSORT: '+ systime() +' Sort ' + str,hnew
+ endif else begin
+        tab = tabnew
+        sxaddhist,'FTSORT: '+ systime() +' Sort ' + str,h
+ endelse
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/fxaddpar.pro b/Code/script_idl_mv/astrolib/fxaddpar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3f40df496b9d3341dfe69ade420eb4973d244954
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxaddpar.pro
@@ -0,0 +1,718 @@
+;+
+; NAME: 
+;       FXADDPAR
+; Purpose     : 
+;       Add or modify a parameter in a FITS header array.
+; Explanation : 
+;       This version of FXADDPAR will write string values longer than 68 
+;       characters using the FITS continuation convention described at 
+;       http://heasarc.gsfc.nasa.gov/docs/heasarc/ofwg/docs/ofwg_recomm/r13.html
+; Use         : 
+;       FXADDPAR, HEADER, NAME, VALUE, COMMENT
+; Inputs      : 
+;       HEADER  = String array containing FITS header.  The maximum string
+;                 length must be equal to 80.  If not defined, then FXADDPAR
+;                 will create an empty FITS header array.
+;
+;       NAME    = Name of parameter.  If NAME is already in the header the
+;                 value and possibly comment fields are modified. Otherwise a
+;                 new record is added to the header.  If NAME is equal to
+;                 either "COMMENT" or "HISTORY" then the value will be added to
+;                 the record without replacement.  In this case the comment
+;                 parameter is ignored.
+;
+;       VALUE   = Value for parameter.  The value expression must be of the
+;                 correct type, e.g. integer, floating or string.
+;                 String values of 'T' or 'F' are considered logical
+;                 values unless the /NOLOGICAL keyword is set.  If the value is
+;                 a string and is "long" (more than 69 characters), then it 
+;                 may be continued over more than one line using the OGIP 
+;                 CONTINUE standard.
+;
+; Opt. Inputs : 
+;       COMMENT = String field.  The '/' is added by this routine.  Added
+;                 starting in position 31.  If not supplied, or set equal to ''
+;                 (the null string), then any previous comment field in the
+;                 header for that keyword is retained (when found).
+; Outputs     : 
+;       HEADER  = Updated header array.
+; Opt. Outputs: 
+;       None.
+; Keywords    : 
+;       BEFORE  = Keyword string name.  The parameter will be placed before the
+;                 location of this keyword.  For example, if BEFORE='HISTORY'
+;                 then the parameter will be placed before the first history
+;                 location.  This applies only when adding a new keyword;
+;                 keywords already in the header are kept in the same position.
+;
+;       AFTER   = Same as BEFORE, but the parameter will be placed after the
+;                 location of this keyword.  This keyword takes precedence over
+;                 BEFORE.
+;
+;       FORMAT  = Specifies FORTRAN-like format for parameter, e.g. "F7.3".  A
+;                 scalar string should be used.  For complex numbers the format
+;                 should be defined so that it can be applied separately to the
+;                 real and imaginary parts.  If not supplied, then the IDL
+;                 default formatting is used, except that double precision is
+;                 given a format of G19.12.
+;
+;       /NOCONTINUE = By default, FXADDPAR will break strings longer than 68 
+;                characters into multiple lines using the continuation
+;                convention.    If this keyword is set, then the line will
+;                instead be truncated to 68 characters.    This was the default
+;                behaviour of FXADDPAR prior to December 1999.  
+;
+;      /NOLOGICAL = If set, then the values 'T' and 'F' are not interpreted as
+;                logical values, and are simply added without interpretation.
+;
+;       /NULL   = If set, then keywords with values which are undefined, or
+;                 which have non-finite values (such as NaN, Not-a-Number) are
+;                 stored in the header without a value, such as
+;
+;                       MYKEYWD =                      /My comment
+;
+;       MISSING = A value which signals that data with this value should be
+;                 considered missing.  For example, the statement
+;
+;                       FXADDPAR, HEADER, 'MYKEYWD', -999, MISSING=-999
+;
+;                 would result in the valueless line described above for the
+;                 /NULL keyword.  Setting MISSING to a value implies /NULL.
+;                 Cannot be used with string or complex values.
+;
+;	ERRMSG	 = If defined and passed, then any error messages will be
+;		   returned to the user in this parameter rather than
+;		   depending on the MESSAGE routine in IDL, e.g.
+;
+;			ERRMSG = ''
+;			FXADDPAR, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;       DETABIFY(), FXPAR(), FXPARPOS()
+; Common      : 
+;       None.
+; Restrictions: 
+;       Warning -- Parameters and names are not checked against valid FITS
+;       parameter names, values and types.
+;
+;       The required FITS keywords SIMPLE (or XTENSION), BITPIX, NAXIS, NAXIS1,
+;       NAXIS2, etc., must be entered in order.  The actual values of these
+;       keywords are not checked for legality and consistency, however.
+;
+; Side effects: 
+;       All HISTORY records are inserted in order at the end of the header.
+;
+;       All COMMENT records are also inserted in order at the end of the
+;       header, but before the HISTORY records.  The BEFORE and AFTER keywords
+;       can override this.
+;
+;       All records with no keyword (blank) are inserted in order at the end of
+;       the header, but before the COMMENT and HISTORY records.  The BEFORE and
+;       AFTER keywords can override this.
+;
+;       All other records are inserted before any of the HISTORY, COMMENT, or
+;       "blank" records.  The BEFORE and AFTER keywords can override this.
+;
+;       String values longer than 68 characters will be split into multiple
+;       lines using the OGIP CONTINUE convention, unless the /NOCONTINUE keyword
+;       is set.    For a description of the CONTINUE convention see    
+;       http://fits.gsfc.nasa.gov/registry/continue_keyword.html
+; Category    : 
+;       Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;       William Thompson, Jan 1992, from SXADDPAR by D. Lindler and J. Isensee.
+;       Differences include:
+;
+;               * LOCATION parameter replaced with keywords BEFORE and AFTER.
+;               * Support for COMMENT and "blank" FITS keywords.
+;               * Better support for standard FITS formatting of string and
+;                 complex values.
+;               * Built-in knowledge of the proper position of required
+;                 keywords in FITS (although not necessarily SDAS/Geis) primary
+;                 headers, and in TABLE and BINTABLE extension headers.
+;
+;       William Thompson, May 1992, fixed bug when extending length of header,
+;       and new record is COMMENT, HISTORY, or blank.
+; Written     : 
+;       William Thompson, GSFC, January 1992.
+; Modified    : 
+;       Version 1, William Thompson, GSFC, 12 April 1993.
+;               Incorporated into CDS library.
+;       Version 2, William Thompson, GSFC, 5 September 1997
+;               Fixed bug replacing strings that contain "/" character--it
+;               interpreted the following characters as a comment.
+;       Version 3, Craig Markwardt, GSFC,  December 1997
+;               Allow long values to extend over multiple lines
+;	Version 4, D. Lindler, March 2000, modified to use capital E instead
+;		of a lower case e for exponential format.
+;       Version 4.1 W. Landsman April 2000, make user-supplied format uppercase
+;       Version 4.2 W. Landsman July 2002, positioning of EXTEND keyword
+;       Version 5, 23-April-2007, William Thompson, GSFC
+;       Version 6, 02-Aug-2007, WTT, bug fix for OGIP long lines
+;       Version 6.1, 10-Feb-2009, W. Landsman, increase default format precision
+;       Version 6.2  30-Sep-2009, W. Landsman, added /NOLOGICAL keyword
+;       Version 7, 13-Aug-2015, William Thompson, allow null values
+;               Add keywords /NULL, MISSING.  Catch non-finite values (e.g. NaN)
+;       Version 7.1, 22-Sep-2015, W. Thompson, No slash if null & no comment
+; Version     : 
+;       Version 7.1, 22-Sep-2015
+;-
+;
+
+; This is a utility routine, which splits a parameter into several
+; continuation bits.
+PRO FXADDPAR_CONTPAR, VALUE, CONTINUED
+  
+  APOST = "'"
+  BLANK = STRING(REPLICATE(32B,80)) ;BLANK line
+
+  ;; The value may not need to be CONTINUEd.  If it does, then split
+  ;; out the first value now.  The first value does not have a
+  ;; CONTINUE keyword, because it will be grafted onto the proper
+  ;; keyword in the calling routine.
+
+  IF (STRLEN(VALUE) GT 68) THEN BEGIN
+      CONTINUED = [ STRMID(VALUE, 0, 67)+'&' ]
+      VALUE = STRMID(VALUE, 67, STRLEN(VALUE)-67)
+  ENDIF ELSE BEGIN
+      CONTINUED = [ VALUE ]
+      RETURN
+  ENDELSE
+
+  ;; Split out the remaining values.
+  WHILE( STRLEN(VALUE) GT 0 ) DO BEGIN
+      H = BLANK
+
+      ;; Add CONTINUE keyword
+      STRPUT, H, 'CONTINUE  '+APOST
+      ;; Add the next split
+      IF(STRLEN(VALUE) GT 68) THEN BEGIN
+          STRPUT, H, STRMID(VALUE, 0, 67)+'&'+APOST, 11
+          VALUE = STRMID(VALUE, 67, STRLEN(VALUE)-67)
+      ENDIF ELSE BEGIN
+          STRPUT, H, VALUE+APOST, 11
+          VALUE = ''
+      ENDELSE
+
+      CONTINUED = [ CONTINUED, H ]
+  ENDWHILE
+
+  RETURN
+END
+
+; Utility routine to add a warning to the file.  The calling routine
+; must ensure that the header is in a consistent state before calling
+; FXADDPAR_CONTWARN because the header will be subsequently modified
+; by calls to FXADDPAR.
+PRO FXADDPAR_CONTWARN, HEADER, NAME
+
+;  By OGIP convention, the keyword LONGSTRN is added to the header as
+;  well.  It should appear before the first occurrence of a long
+;  string encoded with the CONTINUE convention.
+
+  CONTKEY = FXPAR(HEADER, 'LONGSTRN', COUNT = N_LONGSTRN)
+
+;  Calling FXADDPAR here is okay since the state of the header is
+;  clean now.
+  IF N_LONGSTRN GT 0 THEN $
+    RETURN
+
+  FXADDPAR, HEADER, 'LONGSTRN', 'OGIP 1.0', $
+    ' The OGIP long string convention may be used.', $
+    BEFORE=NAME
+
+  FXADDPAR, HEADER, 'COMMENT', $
+    ' This FITS file may contain long string keyword values that are', $
+    BEFORE=NAME
+
+  FXADDPAR, HEADER, 'COMMENT', $
+    " continued over multiple keywords.  This convention uses the  '&'", $
+    BEFORE=NAME
+
+  FXADDPAR, HEADER, 'COMMENT', $
+    ' character at the end of a string which is then continued', $
+    BEFORE=NAME
+
+  FXADDPAR, HEADER, 'COMMENT', $
+    " on subsequent keywords whose name = 'CONTINUE'.", $
+    BEFORE=NAME
+
+  RETURN
+END
+
+
+PRO FXADDPAR, HEADER, NAME, VALUE, COMMENT, BEFORE=BEFORE,      $
+              AFTER=AFTER, FORMAT=FORMAT, NOCONTINUE = NOCONTINUE, $
+              ERRMSG=ERRMSG, NOLOGICAL=NOLOGICAL, MISSING=MISSING, NULL=NULL
+
+        ON_ERROR,2                              ;Return to caller
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() LT 3 THEN BEGIN
+            MESSAGE = 'Syntax:  FXADDPAR, HEADER, NAME, VALUE [, COMMENT ]'
+            GOTO, HANDLE_ERROR
+        ENDIF
+;
+; Define a blank line and the END line
+;
+        ENDLINE = 'END' + STRING(REPLICATE(32B,77))     ;END line
+        BLANK = STRING(REPLICATE(32B,80))               ;BLANK line
+;
+;  If no comment was passed, then use a null string.
+;
+        IF N_PARAMS() LT 4 THEN COMMENT = ''
+;
+;  Check the HEADER array.
+;
+        N = N_ELEMENTS(HEADER)          ;# of lines in FITS header
+        IF N EQ 0 THEN BEGIN            ;header defined?
+                HEADER=STRARR(36)       ;no, make it.
+                HEADER[0]=ENDLINE
+                N=36
+        ENDIF ELSE BEGIN
+                S = SIZE(HEADER)        ;check for string type
+                IF (S[0] NE 1) OR (S[2] NE 7) THEN BEGIN
+                    MESSAGE = 'FITS Header (first parameter) must be a ' + $
+                      'string array'
+                    GOTO, HANDLE_ERROR
+                ENDIF
+        ENDELSE
+;
+;  Make sure NAME is 8 characters long
+;
+        NN = STRING(REPLICATE(32B,8))   ;8 char name
+        STRPUT,NN,STRUPCASE(NAME)       ;Insert name
+;
+;  Check VALUE.
+;
+        S = SIZE(VALUE)         ;get type of value parameter
+        STYPE = S[S[0]+1]
+        SAVE_AS_NULL = 0
+        IF S[0] NE 0 THEN BEGIN
+            MESSAGE = 'Keyword Value (third parameter) must be scalar'
+            GOTO, HANDLE_ERROR
+        END ELSE IF STYPE EQ 0 THEN BEGIN
+            IF (N_ELEMENTS(MISSING) EQ 1) OR KEYWORD_SET(NULL) THEN $
+              SAVE_AS_NULL = 1 ELSE BEGIN
+                MESSAGE = 'Keyword Value (third parameter) is not defined'
+                GOTO, HANDLE_ERROR
+            ENDELSE
+        END ELSE IF STYPE EQ 8 THEN BEGIN
+            MESSAGE = 'Keyword Value (third parameter) cannot be structure'
+            GOTO, HANDLE_ERROR
+        ENDIF
+;
+;  Check to see if the parameter should be saved as a null value.
+;
+        IF (STYPE NE 6) AND (STYPE NE 7) AND (STYPE NE 9) THEN BEGIN
+            IF N_ELEMENTS(MISSING) EQ 1 THEN $
+              IF VALUE EQ MISSING THEN SAVE_AS_NULL = 1
+            IF NOT SAVE_AS_NULL THEN IF NOT FINITE(VALUE) THEN BEGIN
+                IF ((N_ELEMENTS(MISSING) EQ 1) OR KEYWORD_SET(NULL)) THEN $
+                  SAVE_AS_NULL = 1 ELSE BEGIN
+                    MESSAGE = 'Keyword Value (third parameter) is not finite'
+                    GOTO, HANDLE_ERROR
+                ENDELSE
+            ENDIF
+        ENDIF
+;
+;  Extract first 8 characters of each line of header, and locate END line
+;
+        KEYWRD = STRMID(HEADER,0,8)                     ;Header keywords
+        IEND = WHERE(KEYWRD EQ 'END     ',NFOUND)
+;
+;  If no END, then add it.  Either put it after the last non-null string, or
+;  append it to the end.
+;
+        IF NFOUND EQ 0 THEN BEGIN
+                II = WHERE(STRTRIM(HEADER) NE '',NFOUND)
+                II = MAX(II) + 1
+                IF (NFOUND EQ 0) OR (II EQ N_ELEMENTS(HEADER)) THEN     $
+                        HEADER = [HEADER,ENDLINE] ELSE HEADER[II] = ENDLINE
+                KEYWRD = STRMID(HEADER,0,8)
+                IEND = WHERE(KEYWRD EQ 'END     ',NFOUND)
+        ENDIF
+;
+        IEND = IEND[0] > 0                      ;Make scalar
+;
+;  History, comment and "blank" records are treated differently from the
+;  others.  They are simply added to the header array whether there are any
+;  already there or not.
+;
+        IF (NN EQ 'COMMENT ') OR (NN EQ 'HISTORY ') OR          $
+                        (NN EQ '        ') THEN BEGIN
+;
+;  If the header array needs to grow, then expand it in increments of 36 lines.
+;
+                IF IEND GE (N-1) THEN BEGIN
+                        HEADER = [HEADER,REPLICATE(BLANK,36)]
+                        N = N_ELEMENTS(HEADER)
+                ENDIF
+;
+;  Format the record.
+;
+                NEWLINE = BLANK
+                STRPUT,NEWLINE,NN+STRING(VALUE),0
+;
+;  If a history record, then append to the record just before the end.
+;
+                IF NN EQ 'HISTORY ' THEN BEGIN
+                        HEADER[IEND] = NEWLINE          ;add history rec.
+                        HEADER[IEND+1]=ENDLINE          ;move end up
+;
+;  The comment record is placed immediately after the last previous comment
+;  record, or immediately before the first history record, unless overridden by
+;  either the BEFORE or AFTER keywords.
+;
+                END ELSE IF NN EQ 'COMMENT ' THEN BEGIN
+                        I = FXPARPOS(KEYWRD,IEND,AFTER=AFTER,BEFORE=BEFORE)
+                        IF I EQ IEND THEN I =   $
+                            FXPARPOS(KEYWRD,IEND,AFTER='COMMENT',$
+                                     BEFORE='HISTORY')
+                        HEADER[I+1] = HEADER[I:N-2]     ;move rest up
+                        HEADER[I] = NEWLINE             ;insert comment
+;
+;  The "blank" record is placed immediately after the last previous "blank"
+;  record, or immediately before the first comment or history record, unless
+;  overridden by either the BEFORE or AFTER keywords.
+;
+                END ELSE BEGIN
+                        I = FXPARPOS(KEYWRD,IEND,AFTER=AFTER,BEFORE=BEFORE)
+                        IF I EQ IEND THEN I =   $
+                            FXPARPOS(KEYWRD,IEND,AFTER='',BEFORE='COMMENT')<$
+                            FXPARPOS(KEYWRD,IEND,AFTER='',BEFORE='HISTORY')
+                        HEADER[I+1] = HEADER[I:N-2]     ;move rest up
+                        HEADER[I] = NEWLINE             ;insert "blank"
+                ENDELSE
+                RETURN
+        ENDIF                           ;history/comment/blank
+;
+;  Find location to insert keyword.  If the keyword is already in the header,
+;  then simply replace it.  If no new comment is passed, then retain the old
+;  one.
+;
+        IPOS  = WHERE(KEYWRD EQ NN,NFOUND)
+        IF NFOUND GT 0 THEN BEGIN
+                I = IPOS[0]
+                IF COMMENT EQ '' THEN BEGIN
+                        SLASH = STRPOS(HEADER[I],'/')
+                        QUOTE = STRPOS(HEADER[I],"'")
+                        IF (QUOTE GT 0) AND (QUOTE LT SLASH) THEN BEGIN
+                                QUOTE = STRPOS(HEADER[I],"'",QUOTE+1)
+                                IF QUOTE LT 0 THEN SLASH = -1 ELSE      $
+                                        SLASH = STRPOS(HEADER[I],'/',QUOTE+1)
+                        ENDIF
+                        IF SLASH NE -1 THEN     $
+                                COMMENT = STRMID(HEADER[I],SLASH+1,80) ELSE $
+                                COMMENT = STRING(REPLICATE(32B,80))
+                ENDIF
+                GOTO, REPLACE
+        ENDIF
+;
+;  Start of section dealing with the positioning of required FITS keywords.  If
+;  the keyword is SIMPLE, then it must be at the beginning.
+;
+        IF NN EQ 'SIMPLE  ' THEN BEGIN
+                I = 0
+                GOTO, INSERT
+        ENDIF
+;
+;  In conforming extensions, if the keyword is XTENSION, then it must be at the
+;  beginning. 
+;
+        IF NN EQ 'XTENSION' THEN BEGIN
+                I = 0
+                GOTO, INSERT
+        ENDIF
+;
+;  If the keyword is BITPIX, then it must follow the either SIMPLE or XTENSION
+;  keyword.
+;
+        IF NN EQ 'BITPIX  ' THEN BEGIN
+                IF (KEYWRD[0] NE 'SIMPLE  ') AND                $
+                        (KEYWRD[0] NE 'XTENSION') THEN BEGIN
+                    MESSAGE = 'Header must start with either SIMPLE or XTENSION'
+                    GOTO, HANDLE_ERROR
+                ENDIF
+                I = 1
+                GOTO, INSERT
+        ENDIF
+;
+;  If the keyword is NAXIS, then it must follow the BITPIX keyword.
+;
+        IF NN EQ 'NAXIS   ' THEN BEGIN
+                IF KEYWRD[1] NE 'BITPIX  ' THEN BEGIN
+                    MESSAGE = 'Required BITPIX keyword not found'
+                    GOTO, HANDLE_ERROR
+                ENDIF
+                I = 2
+                GOTO, INSERT
+        ENDIF
+;
+;  If the keyword is NAXIS1, then it must follow the NAXIS keyword.
+;
+        IF NN EQ 'NAXIS1  ' THEN BEGIN
+                IF KEYWRD[2] NE 'NAXIS   ' THEN BEGIN
+                    MESSAGE = 'Required NAXIS keyword not found'
+                    GOTO, HANDLE_ERROR
+                ENDIF
+                I = 3
+                GOTO, INSERT
+        ENDIF
+;
+;  If the keyword is NAXIS<n>, then it must follow the NAXIS<n-1> keyword.
+;
+        IF STRMID(NN,0,5) EQ 'NAXIS' THEN BEGIN
+                NUM_AXIS = FIX(STRMID(NN,5,3))
+                PREV = STRING(REPLICATE(32B,8))         ;Format NAXIS<n-1>
+                STRPUT,PREV,'NAXIS',0                   ;Insert NAXIS
+                STRPUT,PREV,STRTRIM(NUM_AXIS-1,2),5     ;Insert <n-1>
+                IF KEYWRD[NUM_AXIS+1] NE PREV THEN BEGIN
+                    MESSAGE = 'Required '+PREV+' keyword not found'
+                    GOTO, HANDLE_ERROR
+                ENDIF
+                I = NUM_AXIS + 2
+                GOTO, INSERT
+        ENDIF
+
+;
+;  If the keyword is EXTEND, then it must follow the last NAXIS* keyword.
+;
+
+        IF NN EQ 'EXTEND  ' THEN BEGIN
+                IF KEYWRD[2] NE 'NAXIS   ' THEN BEGIN
+                    MESSAGE = 'Required NAXIS keyword not found'
+                    GOTO, HANDLE_ERROR
+                ENDIF
+                FOR I = 3, N-2 DO $   
+                    IF STRMID(KEYWRD[I],0,5) NE 'NAXIS' THEN GOTO, INSERT 
+                   
+         ENDIF
+    
+;
+;  If the first keyword is XTENSION, and has the value of either 'TABLE' or
+;  'BINTABLE', then there are some additional required keywords.
+;
+        IF KEYWRD[0] EQ 'XTENSION' THEN BEGIN
+                XTEN = FXPAR(HEADER,'XTENSION')
+                IF (XTEN EQ 'TABLE   ') OR (XTEN EQ 'BINTABLE') THEN BEGIN
+;
+;  If the keyword is PCOUNT, then it must follow the NAXIS2 keyword.
+;
+                        IF NN EQ 'PCOUNT  ' THEN BEGIN
+                                IF KEYWRD[4] NE 'NAXIS2  ' THEN BEGIN
+                                    MESSAGE = 'Required NAXIS2 keyword not found'
+                                    GOTO, HANDLE_ERROR
+                                ENDIF
+                                I = 5
+                                GOTO, INSERT
+                        ENDIF
+;
+;  If the keyword is GCOUNT, then it must follow the PCOUNT keyword.
+;
+                        IF NN EQ 'GCOUNT  ' THEN BEGIN
+                                IF KEYWRD[5] NE 'PCOUNT  ' THEN BEGIN
+                                    MESSAGE = 'Required PCOUNT keyword not found'
+                                    GOTO, HANDLE_ERROR
+                                ENDIF
+                                I = 6
+                                GOTO, INSERT
+                        ENDIF
+;
+;  If the keyword is TFIELDS, then it must follow the GCOUNT keyword.
+;
+                        IF NN EQ 'TFIELDS ' THEN BEGIN
+                                IF KEYWRD[6] NE 'GCOUNT  ' THEN BEGIN
+                                    MESSAGE = 'Required GCOUNT keyword not found'
+                                    GOTO, HANDLE_ERROR
+                                ENDIF
+                                I = 7
+                                GOTO, INSERT
+                        ENDIF
+                ENDIF
+        ENDIF
+;
+;  At this point the location has not been determined, so a new line is added
+;  at the end of the FITS header, but before any blank, COMMENT, or HISTORY
+;  keywords, unless overridden by the BEFORE or AFTER keywords.
+;
+        I = FXPARPOS(KEYWRD,IEND,AFTER=AFTER,BEFORE=BEFORE)
+        IF I EQ IEND THEN I =                                     $
+            FXPARPOS(KEYWRD,IEND,AFTER=AFTER,BEFORE='')         < $
+            FXPARPOS(KEYWRD,IEND,AFTER=AFTER,BEFORE='COMMENT')  < $
+            FXPARPOS(KEYWRD,IEND,AFTER=AFTER,BEFORE='HISTORY')
+;
+;  A new line needs to be added.  First check to see if the length of the
+;  header array needs to be extended.  Then insert a blank record at the proper
+;  place.
+;
+INSERT:
+        IF IEND EQ (N-1) THEN BEGIN
+                HEADER = [HEADER,REPLICATE(BLANK,36)]
+                N = N_ELEMENTS(HEADER)
+        ENDIF
+        HEADER[I+1] = HEADER[I:N-2]
+        HEADER[I] = BLANK
+        IEND = IEND + 1        ; CM 24 Sep 1997
+;
+;  Now put value into keyword at line I.
+;
+REPLACE: 
+        H=BLANK                 ;80 blanks
+        STRPUT,H,NN+'= '        ;insert name and =.
+        APOST = "'"             ;quote (apostrophe) character
+        TYPE = SIZE(VALUE)      ;get type of value parameter
+;
+;  Store the value depending on the data type.  If a character string, first
+;  check to see if it is one of the logical values "T" (true) or "F" (false).
+;
+
+        IF TYPE[1] EQ 7 THEN BEGIN              ;which type?
+                UPVAL = STRUPCASE(VALUE)        ;force upper case.
+                IF ~KEYWORD_SET(NOLOGICAL)  $ 
+		   &&  ((UPVAL EQ 'T') OR (UPVAL EQ 'F')) THEN BEGIN
+                        STRPUT,H,UPVAL,29       ;insert logical value.
+;
+;  Otherwise, remove any tabs, and check for any apostrophes in the string.
+;
+                END ELSE BEGIN
+                        VAL = DETABIFY(VALUE)
+                        NEXT_CHAR = 0
+                        REPEAT BEGIN
+                                AP = STRPOS(VAL,"'",NEXT_CHAR)
+                                IF AP GE 66 THEN BEGIN
+                                        VAL = STRMID(VAL,0,66)
+                                END ELSE IF AP GE 0 THEN BEGIN
+                                        VAL = STRMID(VAL,0,AP+1) + APOST + $
+                                          STRMID(VAL,AP+1,80)
+                                        NEXT_CHAR = AP + 2
+                                ENDIF
+                        ENDREP UNTIL AP LT 0
+
+;
+;  If a long string, then add the comment as soon as possible.
+;
+; CM 24 Sep 1997
+;  Separate parameter if it needs to be CONTINUEd.
+;
+                        IF NOT KEYWORD_SET(NOCONTINUE) THEN $
+                             FXADDPAR_CONTPAR, VAL, CVAL  ELSE $
+                             CVAL = STRMID(VAL,0,68)
+                        K = I + 1
+                        ;; See how many CONTINUE lines there already are
+                        WHILE K LT IEND DO BEGIN
+                            IF STRMID(HEADER[K],0,8) NE 'CONTINUE' THEN $
+                              GOTO, DONE_CHECK_CONT
+                            K = K + 1
+                        ENDWHILE
+                        
+                        DONE_CHECK_CONT:
+                        NOLDCONT = K - I - 1
+                        NNEWCONT = N_ELEMENTS(CVAL) - 1
+
+                        ;; Insert new lines if needed
+                        IF NNEWCONT GT NOLDCONT THEN BEGIN
+                            INS = NNEWCONT - NOLDCONT
+                            WHILE IEND+INS GE N DO BEGIN
+                                HEADER = [HEADER, REPLICATE(BLANK,36)]
+                                N = N_ELEMENTS(HEADER)
+                            ENDWHILE
+                        ENDIF 
+
+                        ;; Shift the old lines properly
+                        IF NNEWCONT NE NOLDCONT THEN $
+                          HEADER[I+NNEWCONT+1] = HEADER[I+NOLDCONT+1:IEND]
+                        IEND = IEND + NNEWCONT - NOLDCONT
+
+                        ;; Blank out any lines at the end if needed
+                        IF NNEWCONT LT NOLDCONT THEN BEGIN
+                            DEL = NOLDCONT - NNEWCONT
+                            HEADER[IEND+1:IEND+DEL] = REPLICATE('', DEL)
+                        ENDIF
+
+                        IF STRLEN(CVAL[0]) GT 18 THEN BEGIN
+                            STRPUT,H,APOST+STRMID(CVAL[0],0,68)+APOST+ $
+                              ' /'+COMMENT,10
+                            HEADER[I]=H
+                                
+;  There might be a continuation of this string.  CVAL would contain
+;  more than one element if that is so.
+                            
+                            ;; Add new continuation lines
+                            IF N_ELEMENTS(CVAL) GT 1 THEN BEGIN
+                              HEADER[I+1] = CVAL[1:*]
+                            
+                            ;; Header state is now clean, so add
+                            ;; warning to header
+
+                               FXADDPAR_CONTWARN, HEADER, NAME
+                            ENDIF
+                            DONE_CONT:
+                            RETURN
+;
+;  If a short string, then pad out to at least eight characters.
+;
+                        END ELSE BEGIN
+                                STRPUT,H,APOST+CVAL[0],10
+                                STRPUT,H,APOST,11+(STRLEN(CVAL[0])>8)
+                        ENDELSE
+
+                    ENDELSE
+;
+;  If complex, then format the real and imaginary parts, and add the comment
+;  beginning in column 51.
+;
+        END ELSE IF (TYPE[1] EQ 6) OR (TYPE[1] EQ 9) THEN BEGIN
+                IF TYPE[1] EQ 6 THEN VR = FLOAT(VALUE) ELSE VR = DOUBLE(VALUE)
+                VI = IMAGINARY(VALUE)
+                IF N_ELEMENTS(FORMAT) EQ 1 THEN BEGIN   ;use format keyword
+                        VR = STRING(VR, '('+STRUPCASE(FORMAT)+')')
+                        VI = STRING(VI, '('+STRUPCASE(FORMAT)+')')
+                 END ELSE BEGIN
+                        VR = STRTRIM(VR, 2)
+                        VI = STRTRIM(VI, 2)
+                ENDELSE
+                SR = STRLEN(VR)  &  STRPUT,H,VR,(30-SR)>10
+                SI = STRLEN(VI)  &  STRPUT,H,VI,(50-SI)>30
+                STRPUT,H,' /'+COMMENT,50
+                HEADER[I] = H
+                RETURN
+;
+;  If not complex or a string, then format according to either the FORMAT
+;  keyword, or the default for that datatype.
+;
+        END ELSE BEGIN
+            IF NOT SAVE_AS_NULL THEN BEGIN
+                IF (N_ELEMENTS(FORMAT) EQ 1) THEN $ ;use format keyword
+                        V = STRING(VALUE,'('+STRUPCASE(FORMAT)+')' ) ELSE BEGIN
+			IF TYPE[1] EQ 5 THEN $
+			V = STRING(VALUE,FORMAT='(G19.12)') ELSE $
+                        V = STRTRIM(strupcase(VALUE),2)    ;default format
+			ENDELSE
+                S = STRLEN(V)                 ;right justify
+                STRPUT,H,V,(30-S)>10          ;insert
+            ENDIF
+        ENDELSE
+;
+;  Add the comment, and store the completed line in the header.  Don't
+;  add the slash if the value is null and there is no comment.
+;
+        IF (NOT SAVE_AS_NULL) OR (STRLEN(STRTRIM(COMMENT)) GT 0) THEN BEGIN
+            STRPUT,H,' /',30    ;add ' /'
+            STRPUT,H,COMMENT,32 ;add comment
+        ENDIF
+        HEADER[I]=H             ;save line
+;
+        ERRMSG = ''
+        RETURN
+;
+;  Error handling point.
+;
+HANDLE_ERROR:
+	IF ARG_PRESENT(ERRMSG) THEN ERRMSG = 'FXADDPAR: ' + MESSAGE	$
+		ELSE MESSAGE, MESSAGE
+        RETURN
+        END
+
diff --git a/Code/script_idl_mv/astrolib/fxbaddcol.pro b/Code/script_idl_mv/astrolib/fxbaddcol.pro
new file mode 100644
index 0000000000000000000000000000000000000000..fc09694dd7d9025bc0584acc6959e4d8940bfa99
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbaddcol.pro
@@ -0,0 +1,382 @@
+	PRO FXBADDCOL,INDEX,HEADER,ARRAY,TTYPE,COMMENT,TUNIT=TUNIT,	$
+		TSCAL=TSCAL,TZERO=TZERO,TNULL=TNULL,TDISP=TDISP,	$
+		TDMIN=TDMIN,TDMAX=TDMAX,TDESC=TDESC,TROTA=TROTA,	$
+		TRPIX=TRPIX,TRVAL=TRVAL,TDELT=TDELT,TCUNI=TCUNI,	$
+		NO_TDIM=NO_TDIM,VARIABLE=VARIABLE,DCOMPLEX=DCOMPLEX,	$
+		BIT=BIT,LOGICAL=LOGICAL,ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXBADDCOL
+; PURPOSE     : 
+;	Adds a column to a binary table extension.
+; EXPLANATION : 
+;	Modify a basic FITS binary table extension (BINTABLE) header array to
+;	define a column.
+; USE         : 
+;	FXBADDCOL, INDEX, HEADER, ARRAY  [, TTYPE [, COMMENT ]]
+; INPUTS      : 
+;	HEADER	= String array containing FITS extension header.
+;	ARRAY	= IDL variable used to determine the data size and type
+;		  associated with the column.  If the column is defined as
+;		  containing variable length arrays, then ARRAY must be of the
+;		  maximum size to be stored in the column.
+; Opt. Inputs : 
+;	TTYPE	= Column label.
+;	COMMENT = Comment for TTYPE
+; Outputs     : 
+;	INDEX	= Index (1-999) of the created column.
+;	HEADER	= The header is modified to reflect the added column.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	VARIABLE= If set, then the column is defined to contain pointers to
+;		  variable length arrays in the heap area.
+;	DCOMPLEX= If set, and ARRAY is complex, with the first dimension being
+;		  two (real and imaginary parts), then the column is defined as
+;		  double-precision complex (type "M").     This keyword is
+;		  only needed prior to IDL Version 4.0, when the double 
+;		  double complex datatype was unavailable in IDL
+;	BIT	= If passed, and ARRAY is of type byte, then the column is
+;		  defined as containing bit mask arrays (type "X"), with the
+;		  value of BIT being equal to the number of mask bits.
+;	LOGICAL	= If set, and array is of type byte, then the column is defined
+;		  as containing logical arrays (type "L").
+;	NO_TDIM	= If set, then the TDIMn keyword is not written out to the
+;		  header.  No TDIMn keywords are written for columns containing
+;		  variable length arrays.
+;	TUNIT	= If passed, then corresponding keyword is added to header.
+;	TSCAL	= Same.
+;	TZERO	= Same.
+;	TNULL	= Same.
+;	TDISP	= Same.
+;	TDMIN	= Same.
+;	TDMAX	= Same.
+;	TDESC	= Same.
+;	TCUNI	= Same.
+;	TROTA	= Same.
+;	TRPIX	= Same.
+;	TRVAL	= Same.
+;	TDELT	= Same.
+;	ERRMSG	= If defined and passed, then any error messages will be
+;		  returned to the user in this parameter rather than
+;		  depending on the MESSAGE routine in IDL.  If no errors are
+;		  encountered, then a null string is returned.  In order to
+;		  use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBADDCOL, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	FXADDPAR, FXPAR
+; Common      : 
+;	None.
+; Restrictions: 
+;	Warning: No checking is done of any of the parameters defining the
+;	values of optional FITS keywords.
+;
+;	FXBHMAKE must first be called to initialize the header.
+;
+;	If ARRAY is of type character, then it must be of the maximum length
+;	expected for this column.  If a character string array, then the
+;	largest string in the array is used to determine the maximum length.
+;
+;	The DCOMPLEX keyword is ignored if ARRAY is not double-precision.
+;	ARRAY must also have a first dimension of two representing the real and
+;	imaginary parts.
+;
+;	The BIT and LOGICAL keywords are ignored if ARRAY is not of type byte.
+;	BIT takes precedence over LOGICAL.
+;
+; Side effects: 
+;	If the data array is multidimensional, then a TDIM keyword is added to
+;	the header, unless either NO_TDIM or VARIABLE is set.
+;
+;	No TDIMn keywords are written out for bit arrays (format 'X'), since
+;	the dimensions would refer to bits, not bytes.
+;
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Jan 1992.
+;	W. Thompson, Feb 1992, changed from function to procedure.
+;	W. Thompson, Feb 1992, modified to support variable length arrays.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 31 May 1994
+;		Added ERRMSG keyword.
+;       Version 3, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;	Version 4, William Thompson, GSFC, 30 December 1994
+;		Added keyword TCUNI.
+;	Version 5, Wayne Landsman, GSFC, 12 Aug 1997
+;		Recognize double complex IDL datatype
+;       Version 6, Wayne Landsman, GSFC. C. Yamauchi (ISAS) 23 Feb 2006
+;               Support 64bit integers
+;       Version 7, C. Markwardt, GSFC, Allow unsigned integers, which
+;               have special TSCAL/TZERO values.  Feb 2009
+;       Version 8,  P.Broos (PSU), Wayne Landsman (GSFC) Mar 2010
+;               Do *not* force TTYPE* keyword to uppercase
+; Version     :
+;       Version 8, Mar 2010
+;-
+;
+	ON_ERROR,2
+;
+;  Check the number of parameters first.
+;
+	IF N_PARAMS() LT 3 THEN BEGIN
+		MESSAGE = 'Syntax: FXBADDCOL, INDEX, HEADER, ARRAY ' +	$
+			'[, TTYPE [, COMMENT]]'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Get the next column number.
+;
+	INDEX = FXPAR(HEADER,'TFIELDS') + 1
+;
+;  Determine the data type and size of the data array.  Use this to
+;  calculate the parameters needed for the binary table.
+;
+	S = SIZE(ARRAY)			;obtain size of array.
+	TYPE = S[S[0]+1]		;type of data.
+	N_ELEM = N_ELEMENTS(ARRAY)	;Number of elements
+;
+	CASE TYPE OF
+		0:  BEGIN
+			MESSAGE = 'Data parameter is not defined'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+			END
+;
+;  If the array is of type byte, then check to see if either the BIT or LOGICAL
+;  keywords were passed.
+;
+		1:  BEGIN
+			IF N_ELEMENTS(BIT) EQ 1 THEN BEGIN
+				N_BYTES = LONG((BIT+7)/8)
+				IF N_BYTES NE N_ELEM THEN BEGIN
+					MESSAGE = 'Number of bits does ' + $
+						'not match array size.'
+					IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+						ERRMSG = MESSAGE
+						RETURN
+					END ELSE MESSAGE, MESSAGE
+				ENDIF
+				N_ELEM = BIT
+				TFORM = "X"
+				TF_COMMENT = 'Bit array'
+			END ELSE IF KEYWORD_SET(LOGICAL) THEN BEGIN
+				N_BYTES = N_ELEM
+				TFORM = "L"
+				TF_COMMENT = 'Logical array'
+			END ELSE BEGIN
+				N_BYTES = N_ELEM
+				TFORM = "B"
+				TF_COMMENT = 'Integer*1 (byte)'
+			ENDELSE
+			END
+;
+;  If complex, then check to see if the DCOMPLEX keyword was set, and if the
+;  first dimension is two.
+;
+		5:  BEGIN
+			IF KEYWORD_SET(DCOMPLEX) THEN BEGIN
+				IF  S[1] NE 2 THEN BEGIN
+					MESSAGE = 'The first dimension ' + $
+						'of ARRAY must be two'
+					IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+						ERRMSG = MESSAGE
+						RETURN
+					END ELSE MESSAGE, MESSAGE
+				ENDIF
+				N_BYTES = 8*N_ELEM
+				N_ELEM = N_ELEM / 2
+				TFORM = "M"
+				TF_COMMENT = 'Complex*16 (double-' +	$
+					'precision complex)'
+				S = [S[0]-1,S[2:*]]
+			END ELSE BEGIN
+				N_BYTES = 8*N_ELEM
+				TFORM = "D"
+				TF_COMMENT = 'Real*8 (double precision)'
+			ENDELSE
+			END
+;
+;  Note that character string arrays are considered to have an extra first
+;  dimension, namely the (maximum) number of characters.
+;
+		7:  BEGIN
+			STR_LEN = MAX(STRLEN(ARRAY))
+			N_BYTES = STR_LEN*N_ELEM
+			N_ELEM = N_BYTES
+			TFORM = "A"
+			TF_COMMENT = 'Character string'
+			S = [S[0]+1, STR_LEN, S[1:*]]	;Add extra dimension
+			END
+;
+;  All other types are straightforward.
+;
+		2:  BEGIN
+			N_BYTES = 2*N_ELEM
+			TFORM = "I"
+			TF_COMMENT = 'Integer*2 (short integer)'
+			END
+		3:  BEGIN
+			N_BYTES = 4*N_ELEM
+			TFORM = "J"
+			TF_COMMENT = 'Integer*4 (long integer)'
+			END
+		4:  BEGIN
+			N_BYTES = 4*N_ELEM
+			TFORM = "E"
+			TF_COMMENT = 'Real*4 (floating point)'
+			END
+		6:  BEGIN
+			N_BYTES = 8*N_ELEM
+			TFORM = "C"
+			TF_COMMENT = 'Complex*8 (complex)'
+			END
+		8:  BEGIN
+			MESSAGE = "Can't write structures to FITS files"
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+			END
+		9: BEGIN
+			N_BYTES = 16*N_ELEM
+			TFORM = "M"
+			TF_COMMENT = 'Complex*16 (double-' +	$
+					'precision complex)'
+			END
+
+                12: BEGIN  
+                        ;; Unsigned 16-bit integers are stored as signed
+                        ;; integers with a TZERO offset.
+                        N_BYTES = 2*N_ELEM
+                        TFORM = "I"
+                        TF_COMMENT = 'Unsigned Integer*2 (short integer)'
+                        IF N_ELEMENTS(TSCAL) EQ 0 THEN TSCAL = 1
+                        IF N_ELEMENTS(TZERO) EQ 0 THEN TZERO = 32768
+                        IF TSCAL[0] NE 1 OR TZERO[0] NE 32768 THEN BEGIN
+                           MESSAGE = 'For 2-byte unsigned type, TSCAL/TZERO must be 1/32768'
+                           IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                              ERRMSG = MESSAGE
+                              RETURN
+                           END ELSE MESSAGE, MESSAGE
+                        ENDIF
+                     END
+                           
+                13: BEGIN
+                        ;; Unsigned 32-bit integers are stored as signed
+                        ;; integers with a TZERO offset.
+                        N_BYTES = 4*N_ELEM
+                        TFORM = "J"
+                        TF_COMMENT = 'Unsigned Integer*4 (long integer)'
+                        IF N_ELEMENTS(TSCAL) EQ 0 THEN TSCAL = 1
+                        IF N_ELEMENTS(TZERO) EQ 0 THEN TZERO = 2147483648D
+                        IF TSCAL[0] NE 1 OR TZERO[0] NE 2147483648D THEN BEGIN
+                           MESSAGE = 'For 4-byte unsigned type, TSCAL/TZERO must be 1/2147483648'
+                           IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                              ERRMSG = MESSAGE
+                              RETURN
+                           END ELSE MESSAGE, MESSAGE
+                        ENDIF
+                     END
+
+		14: BEGIN
+			N_BYTES = 8*N_ELEM
+			TFORM = "K"
+			TF_COMMENT = 'Integer*8 (long long ' +	$
+					'integer)'
+			END
+	               		
+
+
+	ENDCASE
+;
+;  If the column is to contain variable length data, then the number of bytes
+;  is 8, and TFORM has "1P" in the front, and "(<n_elem>)" in the back.
+;
+	IF KEYWORD_SET(VARIABLE) THEN BEGIN
+		N_BYTES = 8
+		TFORM = '1P' + TFORM + '(' + STRTRIM(N_ELEM,2) + ')'
+		TF_COMMENT = TF_COMMENT + ', variable length'
+;
+;  Otherwise, TFORM has "<n_elem>" in the front.
+;
+	END ELSE TFORM = STRTRIM(N_ELEM,2) + TFORM
+;
+;  Update the mandatory keywords in the header.
+;
+	NAXIS1 = FXPAR(HEADER,'NAXIS1')
+	FXADDPAR,HEADER,'NAXIS1',NAXIS1+N_BYTES
+	FXADDPAR,HEADER,'TFIELDS',INDEX
+;
+;  Add the keyword defining this column.
+;
+	COL = STRTRIM(INDEX,2)		;ASCII form of column index
+	FXADDPAR, HEADER, 'TFORM'+COL, TFORM, TF_COMMENT
+;
+;  If the TTYPE parameter has been passed, then add this keyword to the header.
+;
+	IF N_PARAMS() GE 4 THEN BEGIN
+		If N_PARAMS() EQ 4 THEN COMMENT="Label for column "+COL
+		FXADDPAR,HEADER,'TTYPE'+COL,TTYPE,COMMENT
+	ENDIF
+;
+;  If the number of dimensions of the data array are greater than one, then add
+;  the TDIM keyword.  Don't add this keyword if either the NO_TDIM, VARIABLE or
+;  BIT keyword is set.
+;
+	IF (S[0] GT 1) AND NOT (KEYWORD_SET(NO_TDIM) OR KEYWORD_SET(BIT) OR $
+			KEYWORD_SET(VARIABLE)) THEN BEGIN
+		TDIM = "(" + STRTRIM(S[1],2)
+		FOR I = 2,S[0] DO TDIM = TDIM + "," + STRTRIM(S[I],2)
+		TDIM = TDIM + ')'
+		FXADDPAR,HEADER,'TDIM'+COL,TDIM,	$
+			'Array dimensions for column '+COL
+	ENDIF
+;
+;  If the various keywords were passed, then add them to the header.
+;
+	IF N_ELEMENTS(TUNIT) EQ 1 THEN FXADDPAR,HEADER,'TUNIT'+COL,TUNIT, $
+		'Units of column '+COL
+	IF N_ELEMENTS(TSCAL) EQ 1 THEN FXADDPAR,HEADER,'TSCAL'+COL,TSCAL, $
+		'Scale parameter for column '+COL
+	IF N_ELEMENTS(TZERO) EQ 1 THEN FXADDPAR,HEADER,'TZERO'+COL,TZERO, $
+		'Zero offset for column '+COL
+	IF N_ELEMENTS(TNULL) EQ 1 THEN FXADDPAR,HEADER,'TNULL'+COL,TNULL, $
+		'Null value for column '+COL
+	IF N_ELEMENTS(TDISP) EQ 1 THEN FXADDPAR,HEADER,'TDISP'+COL,TDISP, $
+		'Display format for column '+COL
+;
+	IF N_ELEMENTS(TDMIN) EQ 1 THEN FXADDPAR,HEADER,'TDMIN'+COL,TDMIN, $
+		'Minimum value in column '+COL
+	IF N_ELEMENTS(TDMAX) EQ 1 THEN FXADDPAR,HEADER,'TDMAX'+COL,TDMAX, $
+		'Maximum value in column '+COL
+	IF N_ELEMENTS(TDESC) EQ 1 THEN FXADDPAR,HEADER,'TDESC'+COL,TDESC, $
+		'Axis labels for column '+COL
+	IF N_ELEMENTS(TCUNI) EQ 1 THEN FXADDPAR,HEADER,'TCUNI'+COL,TCUNI, $
+		'Axis units for column '+COL
+	IF N_ELEMENTS(TROTA) EQ 1 THEN FXADDPAR,HEADER,'TROTA'+COL,TROTA, $
+		'Rotation angles for column '+COL
+	IF N_ELEMENTS(TRPIX) EQ 1 THEN FXADDPAR,HEADER,'TRPIX'+COL,TRPIX, $
+		'Reference pixel for column '+COL
+	IF N_ELEMENTS(TRVAL) EQ 1 THEN FXADDPAR,HEADER,'TRVAL'+COL,TRVAL, $
+		'Reference position for column '+COL
+	IF N_ELEMENTS(TDELT) EQ 1 THEN FXADDPAR,HEADER,'TDELT'+COL,TDELT, $
+		'Axis increments for column '+COL
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbclose.pro b/Code/script_idl_mv/astrolib/fxbclose.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2c6987c084234265d3521f985ed5e2086e709fe0
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbclose.pro
@@ -0,0 +1,101 @@
+	PRO FXBCLOSE, UNIT, ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXBCLOSE
+; Purpose     : 
+;	Close a FITS binary table extension opened for read.
+; Explanation : 
+;	Closes a FITS binary table extension that had been opened for read by
+;	FXBOPEN.
+; Use         : 
+;	FXBCLOSE, UNIT
+; Inputs      : 
+;	UNIT	= Logical unit number of the file.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	None.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	ERRMSG	  = If defined and passed, then any error messages will be
+;		    returned to the user in this parameter rather than
+;		    depending on the MESSAGE routine in IDL.  If no errors are
+;		    encountered, then a null string is returned.  In order to
+;		    use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBCLOSE, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	None.
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	The file must have been opened with FXBOPEN.
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, Feb. 1992.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 June 1994
+;		Added ERRMSG keyword.
+;       Version 3, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+; Version     :
+;       Version 3, 23 June 1994
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+@fxbintable
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 1 THEN BEGIN
+		MESSAGE = 'Syntax:  FXBCLOSE, UNIT'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Find the index of the file.
+;
+	ILUN = WHERE(LUN EQ UNIT,NLUN)
+	ILUN = ILUN[0]
+	IF NLUN EQ 0 THEN BEGIN
+		MESSAGE = 'Unit ' + STRTRIM(UNIT,2) + ' not opened properly'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Make sure the file was opened for read access.
+;
+	IF STATE[ILUN] NE 1 THEN BEGIN
+		MESSAGE = 'Unit ' + STRTRIM(UNIT,2) +	$
+			' not opened for read access'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Close the file, and mark it as closed.
+;
+	FREE_LUN,UNIT
+	STATE[ILUN] = 0
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbcolnum.pro b/Code/script_idl_mv/astrolib/fxbcolnum.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c456cb563bd419c11d4598c98b4c40b3fa175cb9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbcolnum.pro
@@ -0,0 +1,124 @@
+	FUNCTION FXBCOLNUM, UNIT, COL, ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXBCOLNUM()
+; Purpose     : 
+;	Returns a binary table column number.
+; Explanation : 
+;	Given a column specified either by number or name, this routine will
+;	return the appropriate column number.
+; Use         : 
+;	Result = FXBCOLNUM( UNIT, COL )
+; Inputs      : 
+;	UNIT	= Logical unit number corresponding to the file containing the
+;		  binary table.
+;	COL	= Column in the binary table, given either as a character
+;		  string containing a column label (TTYPE), or as a numerical
+;		  column index starting from column one.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	The result of the function is the number of the column specified, or
+;	zero if no column is found (when passed by name).
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	ERRMSG	  = If defined and passed, then any error messages will be
+;		    returned to the user in this parameter rather than
+;		    depending on the MESSAGE routine in IDL.  If no errors are
+;		    encountered, then a null string is returned.  In order to
+;		    use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			Result = FXBCOLNUM( ERRMSG=ERRMSG, ... )
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	None.
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	The binary table file must have been opened with FXBOPEN.
+;
+;	If COL is passed as a number, rather than as a name, then it must be
+;	consistent with the number of columns in the table.
+;
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	None.
+; Written     : 
+;	William Thompson, GSFC, 2 July 1993.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 2 July 1993.
+;	Version 2, William Thompson, GSFC, 29 October 1993.
+;		Added error message for not finding column by name.
+;	Version 3, William Thompson, GSFC, 21 June 1994
+;		Added ERRMSG keyword.
+;       Version 4, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+; Version     :
+;       Version 4, 23 June 1994
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+@fxbintable
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 2 THEN BEGIN
+		MESSAGE = 'Syntax:  Result = FXBCOLNUM( UNIT, COL )'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN, 0
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Find the logical unit number in the FXBINTABLE common block.
+;
+	ILUN = WHERE(LUN EQ UNIT,NLUN)
+	ILUN = ILUN[0]
+	IF NLUN EQ 0 THEN BEGIN
+		MESSAGE = 'Unit ' + STRTRIM(UNIT,2) + ' not opened properly'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN, 0
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  If COL is of type string, then search for a column with that label.
+;
+	SC = SIZE(COL)
+	IF SC[SC[0]+1] EQ 7 THEN BEGIN
+		SCOL = STRUPCASE(STRTRIM(COL,2))
+		ICOL = WHERE(TTYPE[*,ILUN] EQ SCOL, NCOL)
+		ICOL = ICOL[0]
+		IF ICOL LT 0 THEN BEGIN
+			MESSAGE = 'Column "' + SCOL + '" not found'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN, 0
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+;
+;  Otherwise, a numerical column was passed.  Check its value.
+;
+	END ELSE ICOL = LONG(COL) - 1
+	IF (ICOL LT 0) OR (ICOL GE TFIELDS[ILUN]) THEN BEGIN
+		MESSAGE= 'COL must be between 1 and ' +	$
+			STRTRIM(TFIELDS[ILUN],2)
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN, 0
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Return ICOL as a number between 1 and N.
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN, ICOL + 1
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbcreate.pro b/Code/script_idl_mv/astrolib/fxbcreate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..45a2fa9dbb8ba5e817f26707a41cbfa880dac4a4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbcreate.pro
@@ -0,0 +1,190 @@
+	PRO FXBCREATE, UNIT, FILENAME, HEADER, EXTENSION, ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXBCREATE
+; Purpose     : 
+;	Open a new binary table at the end of a FITS file.
+; Explanation : 
+;	Write a binary table extension header to the end of a disk FITS file,
+;	and leave it open to receive the data.
+;
+;	The FITS file is opened, and the pointer is positioned just after the
+;	last 2880 byte record.  Then the binary header is appended.  Calls to
+;	FXBWRITE will append the binary data to this file, and then FXBFINISH
+;	will close the file.
+;
+; Use         : 
+;	FXBCREATE, UNIT, FILENAME, HEADER
+; Inputs      : 
+;	FILENAME = Name of FITS file to be opened.
+;	HEADER	 = String array containing the FITS binary table extension
+;		   header.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	UNIT	 = Logical unit number of the opened file.
+;       EXTENSION= Extension number of newly created extension.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	ERRMSG	  = If defined and passed, then any error messages will be
+;		    returned to the user in this parameter rather than
+;		    depending on the MESSAGE routine in IDL.  If no errors are
+;		    encountered, then a null string is returned.  In order to
+;		    use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBCREATE, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	FXADDPAR, FXBFINDLUN, FXBPARSE, FXFINDEND
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	The primary FITS data unit must already be written to a file.  The
+;	binary table extension header must already be defined (FXBHMAKE), and
+;	must match the data that will be written to the file.
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, Jan 1992, based on WRITEFITS by J. Woffard and W. Landsman.
+;	W. Thompson, Feb 1992, changed from function to procedure.
+;	W. Thompson, Feb 1992, removed all references to temporary files.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 July 1993.
+;		Fixed bug with variable length arrays.
+;	Version 3, William Thompson, GSFC, 21 June 1994
+;		Added ERRMSG keyword.
+;	Version 4, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;	Version 5, Antony Bird, Southampton, 25 June 1997
+;		Modified to allow very long tables 
+; Version     :
+;	Version 5, 25 June 1997
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Added EXTENSION parameter, C. Markwardt 1999 Jul 15
+;       More efficient zeroing of file, C. Markwardt, 26 Feb 2001
+;       Recompute header size if updating THEAP keyword B. Roukema April 2010
+;-
+;
+@fxbintable
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() LT 3 THEN BEGIN
+		MESSAGE = 'Syntax:  FXBCREATE, UNIT, FILENAME, HEADER'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Get a logical unit number, open the file, and find the end.
+;
+	GET_LUN,UNIT
+       	OPENU, UNIT, FILENAME, /BLOCK
+	FXFINDEND, UNIT, EXTENSION
+;
+;  Store the UNIT number in the common block, and leave space for the other
+;  parameters.  Initialize the common block if need be.  ILUN is an index into
+;  the arrays.
+;
+	ILUN = FXBFINDLUN(UNIT)
+;
+;  Store the current position as the start of the header.  Mark the file as
+;  open for write.
+;
+	POINT_LUN,-UNIT,POINTER
+	MHEADER[ILUN] = POINTER
+	STATE[ILUN] = 2
+;
+;  Determine if an END line occurs, and add one if necessary
+;
+CHECK_END:
+	ENDLINE = WHERE(STRMID(HEADER,0,8) EQ 'END     ', NEND)
+	ENDLINE = ENDLINE[0]
+	IF NEND EQ 0 THEN BEGIN
+		MESSAGE,/INF,'WARNING - An END statement has been appended ' +$
+			'to the FITS header'
+		HEADER = [HEADER, 'END' + STRING(REPLICATE(32B,77))]
+		ENDLINE = N_ELEMENTS(HEADER) - 1 
+	ENDIF
+	NMAX = ENDLINE + 1		;Number of 80 byte records
+	NHEAD = FIX((NMAX+35)/36)	;Number of 2880 byte records
+;
+;  Convert the header to byte and force into 80 character lines.
+;
+WRITE_HEADER:
+	BHDR = REPLICATE(32B, 80, 36*NHEAD)
+	FOR N = 0,ENDLINE DO BHDR[0,N] = BYTE( STRMID(HEADER[N],0,80) )
+	WRITEU, UNIT, BHDR
+;
+;  Get the rest of the information, and store it in the common block.
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+		FXBPARSE,ILUN,HEADER,ERRMSG=ERRMSG
+		IF ERRMSG NE '' THEN RETURN
+	END ELSE FXBPARSE,ILUN,HEADER
+;
+;  Check the size of the heap offset.  If the heap offset is smaller than the
+;  table, then reset it to the size of the table.
+;
+	DDHEAP = HEAP[ILUN] - NAXIS1[ILUN]*NAXIS2[ILUN]
+	IF DDHEAP LT 0 THEN BEGIN
+		MESSAGE,'Heap offset smaller than table size--resetting', $
+			/CONTINUE
+		HEAP[ILUN] = NAXIS1[ILUN]*NAXIS2[ILUN]
+		FXADDPAR,HEADER,'THEAP',HEAP[ILUN]
+		POINT_LUN, UNIT, MHEADER[ILUN]
+	
+; Have we changed position of the END keyword?
+		GOTO, CHECK_END
+	ENDIF
+;
+;  Fill out the file to size it properly.
+;
+        ;; This segment is now optimized to write out more than one
+        ;; row at a time, which is crucial for tables with many small
+        ;; rows.  The code heuristically chooses a buffer size which
+        ;; is 1% of the file, but no bigger than 512k, and always a
+        ;; multiple of the row size.
+
+
+        BUFSIZE = LONG(NAXIS1[ILUN]*NAXIS2[ILUN]/100) > NAXIS1[ILUN] < 524288L
+        BUFSIZE = (FLOOR(BUFSIZE/NAXIS1[ILUN])>1) * NAXIS1[ILUN]
+        BUFFER = BYTARR(BUFSIZE)
+        TOTBYTES = NAXIS1[ILUN]*NAXIS2[ILUN]
+
+        ;; TOTBYTES keeps count of bytes left to write
+        WHILE TOTBYTES GT 0 DO BEGIN
+            ;; Case of final rows which might not be EQ BUFSIZE
+            IF TOTBYTES LT BUFSIZE THEN BUFFER = BYTARR(TOTBYTES)
+            WRITEU,UNIT,BUFFER
+            TOTBYTES = TOTBYTES - BUFSIZE
+        ENDWHILE
+;
+;  If there's any extra space before the start of the heap, then write that out
+;  as well.
+;
+	IF DDHEAP GT 0 THEN BEGIN
+		BUFFER = BYTARR(DDHEAP)
+		WRITEU,UNIT,BUFFER
+	ENDIF
+;
+;  Initialize DHEAP, and return.
+;
+	DHEAP[ILUN] = 0
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
+
diff --git a/Code/script_idl_mv/astrolib/fxbdimen.pro b/Code/script_idl_mv/astrolib/fxbdimen.pro
new file mode 100644
index 0000000000000000000000000000000000000000..16bc619a69aca3710c9bbee55e46713a05fd7307
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbdimen.pro
@@ -0,0 +1,127 @@
+        FUNCTION FXBDIMEN, UNIT, COL, ERRMSG=ERRMSG
+;+
+; NAME:
+;     FXBDIMEN()
+;
+; PURPOSE:      
+;      Returns the dimensions for a column in a FITS binary table.
+;
+; Explanation : This procedure returns the dimensions associated with a column
+;               in a binary table opened for read with the command FXBOPEN.
+;
+; Use         : Result = FXBDIMEN(UNIT,COL)
+;
+; Inputs      : UNIT    = Logical unit number returned by FXBOPEN routine.
+;                         Must be a scalar integer.
+;
+;               COL     = Column in the binary table to read data from, either
+;                         as a character string containing a column label
+;                         (TTYPE), or as a numerical column index starting from
+;                         column one.
+;
+; Opt. Inputs : None.
+;
+; Outputs     : The result of the function is an array containing the
+;               dimensions for the specified column in the FITS binary table
+;               that UNIT points to.
+;
+; Opt. Outputs: None.
+;
+; Keywords :    ERRMSG  = If defined and passed, then any error messages will
+;                         be returned to the user in this parameter rather than
+;                         depending on the MESSAGE routine in IDL.  If no
+;                         errors are encountered, then a null string is
+;                         returned.  In order to use this feature, ERRMSG must
+;                         be defined first, e.g.
+;
+;                               ERRMSG = ''
+;                               Result = FXBDIMEN( ERRMSG=ERRMSG, ... )
+;                               IF ERRMSG NE '' THEN ...
+;
+; Calls       : FXBCOLNUM, FXBFINDLUN
+;
+; Common      : Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;               information.
+;
+; Restrictions: None.
+;
+; Side effects: The dimensions will be returned whether or not the table is
+;               still open or not.
+;
+;               If UNIT does not point to a binary table, then 0 is returned.
+;
+;               If UNIT is an undefined variable, then 0 is returned.
+;
+; Category    : Data Handling, I/O, FITS, Generic.
+;
+; Prev. Hist. : None.
+;
+; Written     : William Thompson, GSFC, 4 March 1994.
+;
+; Modified    : Version 1, William Thompson, GSFC, 4 March 1994.
+;               Version 2, William Thompson, GSFC, 21 June 1994
+;                       Added ERRMSG keyword.
+;               Version 3, William Thompson, GSFC, 23 June 1994
+;                       Modified so that ERRMSG is not touched if not defined.
+;
+; Version     : Version 3, 23 June 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+@fxbintable
+        ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() NE 2 THEN BEGIN
+                MESSAGE = 'Syntax:  Result = FXBDIMEN(UNIT,COL)'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN, 0
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+;
+;  If UNIT is undefined, then return zero.
+;
+        IF N_ELEMENTS(UNIT) EQ 0 THEN RETURN, 0
+;
+;  Check the validity of UNIT.
+;
+        IF N_ELEMENTS(UNIT) GT 1 THEN BEGIN
+                MESSAGE = 'UNIT must be a scalar'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN, 0
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+        SZ = SIZE(UNIT)
+        IF SZ[SZ[0]+1] GT 3 THEN BEGIN
+                MESSAGE = 'UNIT must be an integer'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN, 0
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+;
+;  Find the column number for the requested column.
+;
+        IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ICOL = FXBCOLNUM(UNIT,COL,ERRMSG=ERRMSG)
+                IF MESSAGE NE '' THEN RETURN, 0
+        END ELSE ICOL = FXBCOLNUM(UNIT,COL)
+        IF ICOL EQ 0 THEN BEGIN
+                MESSAGE = 'No such column'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN, 0
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+;
+;  Get the dimensions associated with UNIT and COL.
+;
+        ILUN = FXBFINDLUN(UNIT)
+        DIMS = N_DIMS[*,ICOL-1,ILUN]
+        IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+        RETURN, DIMS[1:DIMS[0]]
+;
+        END
diff --git a/Code/script_idl_mv/astrolib/fxbfind.pro b/Code/script_idl_mv/astrolib/fxbfind.pro
new file mode 100644
index 0000000000000000000000000000000000000000..530835de7f71580bd41ebdec9e43d4847079be1d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbfind.pro
@@ -0,0 +1,158 @@
+	PRO FXBFIND,P1,KEYWORD,COLUMNS,VALUES,N_FOUND,DEFAULT, $
+                    COMMENTS=COMMENTS
+;+
+; NAME: 
+;	FXBFIND
+; Purpose     : 
+;	Find column keywords in a FITS binary table header.
+; Explanation : 
+;	Finds the value of a column keyword for all the columns in the binary
+;	table for which it is set.  For example,
+;
+;		FXBFIND, UNIT, 'TTYPE', COLUMNS, VALUES, N_FOUND
+;
+;	Would find all instances of the keywords TTYPE1, TTYPE2, etc.  The
+;	array COLUMNS would contain the column numbers for which a TTYPEn
+;	keyword was found, and VALUES would contain the values.  N_FOUND would
+;	contain the total number of instances found.
+;
+; Use         : 
+;	FXBFIND, [UNIT or HEADER], KEYWORD, COLUMNS, VALUES, N_FOUND
+;		[, DEFAULT ]
+; Inputs      : 
+;	Either UNIT or HEADER must be passed.
+;
+;	UNIT	= Logical unit number of file opened by FXBOPEN.
+;	HEADER	= FITS binary table header.
+;	KEYWORD	= Prefix to a series of FITS binary table column keywords.  The
+;		  keywords to be searched for are formed by combining this
+;		  prefix with the numbers 1 through the value of TFIELDS in the
+;		  header.
+; Opt. Inputs : 
+;	DEFAULT	= Default value to use for any column keywords that aren't
+;		  found.  If passed, then COLUMNS and VALUES will contain
+;		  entries for every column.  Otherwise, COLUMNS and VALUES only
+;		  contain entries for columns where values were found.
+; Outputs     : 
+;	COLUMNS	= Array containing the column numbers for which values of the
+;		  requested keyword series were found.
+;	VALUES	= Array containing the found values.
+;	N_FOUND	= Number of values found.  The value of this parameter is
+;		  unaffected by whether or not DEFAULT is passed.
+; Opt. Outputs: 
+;	None.
+; Output Keywords    : 
+;      COMMENTS = Comments associated with each keyword, if any
+; Calls       : 
+;	FXBFINDLUN, FXPAR
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	If UNIT is passed, then the file must have been opened with FXBOPEN.
+;	If HEADER is passed, then it must be a legal FITS binary table header.
+;
+;	The type of DEFAULT must be consistent with the values of the requested
+;	keywords, i.e. both most be either of string or numerical type.
+;
+;	The KEYWORD prefix must not have more than five characters to leave
+;	room for the three digits allowed for the column numbers.
+;
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Feb. 1992.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;       Vectorized implementation improves performance, CM 18 Nov 1999
+;       Added COMMENTS keyword CM Nov 2003
+;       Remove use of obsolete !ERR system variable W. Landsman April 2010
+;       Fix error introduced April 2010  W. Landsman
+; Version     : 
+;	Version 3, April 2010.
+;-
+;
+@fxbintable
+	ON_ERROR,2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() LT 5 THEN MESSAGE,	$
+		'Syntax:  FXBFIND,[UNIT/HEADER],KEYWORD,COLUMNS,VALUES,' + $
+		'N_FOUND [,DEFAULT]'
+;
+;  Get the header.
+;
+	IF N_ELEMENTS(P1) EQ 1 THEN BEGIN
+		ILUN = FXBFINDLUN(P1)
+		HEADER = HEAD[*,ILUN]
+	END ELSE HEADER = P1
+;
+;  Get the value of TFIELDS from HEADER.
+;
+	TFIELDS0 = FXPAR(HEADER,'TFIELDS')
+	IF TFIELDS0 EQ 0 THEN MESSAGE,'No columns found in HEADER'
+
+;
+;  Extract the keyword values all in one pass
+;        
+        KEYVALUES = FXPAR(HEADER, STRTRIM(KEYWORD,2)+'*', $
+                          COMMENT=COMMENT_STRS, DATATYPE=DEFAULT, COUNT=NKEY)
+        N_FOUND = 0L
+
+;
+;  INDEX is used as an array index to fill in the final output
+;   
+        IF NKEY GT 0 THEN BEGIN
+            N_FOUND = N_ELEMENTS(KEYVALUES)
+            INDEX   = LINDGEN(N_FOUND)
+        ENDIF
+
+
+;
+;  INDEX is used as an array index to fill in the final output
+;
+        IF N_FOUND GT 0 THEN INDEX   = LINDGEN(N_FOUND)
+ 
+;
+;  If a default was given, then we are a little more careful to 
+;  reproduce the correct number of values.
+;
+        IF N_ELEMENTS(DEFAULT) GT 0 THEN BEGIN
+            ;; If no values were found we need to fill KEYVALUES with
+            ;; *something*.
+            IF N_FOUND LE 0 THEN KEYVALUES = DEFAULT
+            COLUMNS  = LINDGEN(TFIELDS0) + 1
+
+            ;; Make an array with the number of columns in the table
+            SZ_VALUE = SIZE(KEYVALUES[0])
+            VALUES   = MAKE_ARRAY(TFIELDS0, TYPE=SZ_VALUE[1], VALUE=DEFAULT)
+            COMMENTS = STRARR(TFIELDS0)
+
+            ;; Fill the columns which had this keyword
+            IF N_FOUND GT 0 THEN BEGIN
+                VALUES[INDEX] = KEYVALUES
+                COMMENTS[INDEX] = COMMENT_STRS
+            ENDIF
+
+        ENDIF ELSE BEGIN
+
+;
+;  If no default was given, we can simply return the values returned
+;  by FXPAR.
+;
+            IF N_FOUND GT 0 THEN BEGIN
+                COLUMNS = INDEX + 1
+                VALUES  = KEYVALUES
+                COMMENTS = COMMENT_STRS
+            ENDIF
+
+        ENDELSE
+        RETURN
+            
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbfindlun.pro b/Code/script_idl_mv/astrolib/fxbfindlun.pro
new file mode 100644
index 0000000000000000000000000000000000000000..78b3bb8b8d33ba14f3904b3be3f70cb7a55144f9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbfindlun.pro
@@ -0,0 +1,120 @@
+	FUNCTION FXBFINDLUN, UNIT
+;+
+; NAME: 
+;	FXBFINDLUN()
+; Purpose     : 
+;	Find logical unit number UNIT in FXBINTABLE common block.
+; Explanation : 
+;	Finds the proper index to use for getting information about the logical
+;	unit number UNIT in the arrays stored in the FXBINTABLE common block.
+;	Called from FXBCREATE and FXBOPEN.
+; Use         : 
+;	Result = FXBFINDLUN( UNIT )
+; Inputs      : 
+;	UNIT	= Logical unit number.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	The result of the function is an index into the FXBINTABLE common
+;	block.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	None.
+; Calls       : 
+;	None.
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	None.
+; Side effects: 
+;	If UNIT is not found in the common block, then it is added to the
+;	common block.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Feb. 1992.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 July 1993.
+;		Added DHEAP variable to fix bug with variable length arrays.
+;	Version 3, Michael Schubnell, University of Michigan, 22 May 1996
+;		Change N_DIMS from short to long integer.
+; Version     : 
+;	Version 3, 22 May 1996
+;   Make NAXIS1, NAXIS2, HEAP, DHEAP, BYTOFF 64-bit integers to deal with large files,
+;         E. Hivon Mar 2008
+;   Also make NHEADER a 64 bit integer   W. Landsman  May 2016
+;   
+;-
+;
+@fxbintable
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 1 THEN MESSAGE,	$
+		'Syntax:  ILUN = FXBFINDLUN( UNIT )'
+;
+;  If the common block hasn't been initialized yet, then initialize it.
+;
+	IF N_ELEMENTS(LUN) EQ 0 THEN BEGIN
+		LUN     = UNIT
+		STATE	= 0
+		HEAD	= ''
+		MHEADER	= 0L
+		NHEADER = 0LL
+		NAXIS1  = 0LL
+		NAXIS2  = 0LL
+		TFIELDS = 0L
+		HEAP	= 0LL
+		DHEAP	= 0LL
+		BYTOFF  = 0LL
+		TTYPE	= ''
+		FORMAT	= ''
+		IDLTYPE	= 0
+		N_ELEM	= 0L
+		TSCAL	= 1.
+		TZERO	= 0.
+		MAXVAL	= 0L
+		N_DIMS	= LONARR(9,2)
+		ILUN = 0
+;
+;  Otherwise, find the logical unit number in the common block.  If not found,
+;  then add it.
+;
+	END ELSE BEGIN
+		ILUN = WHERE(LUN EQ UNIT,NLUN)
+		ILUN = ILUN[0]
+		IF NLUN EQ 0 THEN BEGIN
+			LUN     = [LUN,UNIT]
+			STATE	= [STATE,  0]
+			BOOST_ARRAY,HEAD,''
+			MHEADER = [MHEADER,0]
+			NHEADER = [NHEADER,0]
+			NAXIS1  = [NAXIS1, 0]
+			NAXIS2  = [NAXIS2, 0]
+			TFIELDS = [TFIELDS,0]
+			HEAP	= [HEAP,   0]
+			DHEAP	= [DHEAP,  0]
+			BOOST_ARRAY,BYTOFF,0
+			BOOST_ARRAY,TTYPE,''
+			BOOST_ARRAY,FORMAT,''
+			BOOST_ARRAY,IDLTYPE,0
+			BOOST_ARRAY,N_ELEM,0
+			BOOST_ARRAY,TSCAL,1.
+			BOOST_ARRAY,TZERO,0.
+			BOOST_ARRAY,MAXVAL,0
+			BOOST_ARRAY,N_DIMS,LONARR(9,2)
+			ILUN = N_ELEMENTS(LUN)-1
+		ENDIF
+	ENDELSE
+;
+;  Return the index into the common block arrays.
+;
+	RETURN,ILUN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbfinish.pro b/Code/script_idl_mv/astrolib/fxbfinish.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e5c9b39ff102b54ec5659e6ec65730ca80b58a16
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbfinish.pro
@@ -0,0 +1,129 @@
+	PRO FXBFINISH, UNIT, ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXBFINISH
+; Purpose     : 
+;	Close a FITS binary table extension file opened for write.
+; Explanation : 
+;	Closes a FITS binary table extension file that had been opened for
+;	write by FXBCREATE.
+; Use         : 
+;	FXBFINISH, UNIT
+; Inputs      : 
+;	UNIT	= Logical unit number of the file.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	None.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	ERRMSG	= If defined and passed, then any error messages will be
+;		  returned to the user in this parameter rather than
+;		  depending on the MESSAGE routine in IDL.  If no errors are
+;		  encountered, then a null string is returned.  In order to
+;		  use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBFINISH, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	None.
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	The file must have been opened with FXBCREATE, and written with
+;	FXBWRITE.
+; Side effects: 
+;	Any bytes needed to pad the file out to an integral multiple of 2880
+;	bytes are written out to the file.  Then, the file is closed.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, Jan 1992.
+;	W. Thompson, Feb 1992, modified to support variable length arrays.
+;	W. Thompson, Feb 1992, removed all references to temporary files.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 July 1993.
+;		Fixed bug with variable length arrays.
+;	Version 3, William Thompson, GSFC, 31 May 1994
+;		Added ERRMSG keyword.
+;       Version 4, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+; Version     :
+;       Version 4, 23 June 1994
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+@fxbintable
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 1 THEN BEGIN
+		MESSAGE = 'Syntax:  FXBFINISH, UNIT'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Find the index of the file.
+;
+	ILUN = WHERE(LUN EQ UNIT,NLUN)
+	ILUN = ILUN[0]
+	IF NLUN EQ 0 THEN BEGIN
+		MESSAGE = 'Unit ' + STRTRIM(UNIT,2) +	$
+			' not opened properly'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Make sure the file was opened for write access.
+;
+	IF STATE[ILUN] NE 2 THEN BEGIN
+		MESSAGE = 'Unit ' + STRTRIM(UNIT,2) +	$
+			' not opened for write access'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Calculate how many bytes are needed to pad out the file.
+;
+	OFFSET = NHEADER[ILUN] + HEAP[ILUN] + DHEAP[ILUN]
+	NPAD = OFFSET MOD 2880
+	IF NPAD NE 0 THEN BEGIN
+		NPAD = 2880 - NPAD
+		POINT_LUN,UNIT,OFFSET
+		WRITEU,UNIT,BYTARR(NPAD)
+	ENDIF
+;
+;  If variable sized arrays were written out to the file, then the PCOUNT value
+;  must be updated.  It is taken for granted that PCOUNT is the sixth keyword
+;  down, and the value is inserted right justified to column 30.
+;
+	PCOUNT = HEAP[ILUN] + DHEAP[ILUN] - NAXIS1[ILUN]*NAXIS2[ILUN]
+	IF PCOUNT GT 0 THEN BEGIN
+		PCOUNT = STRTRIM(PCOUNT,2)
+		POINT_LUN,UNIT,MHEADER[ILUN] + 430 - STRLEN(PCOUNT)
+		WRITEU,UNIT,PCOUNT
+	ENDIF
+;	
+;  Close the file, mark it as closed, and return.
+;
+	FREE_LUN,UNIT
+	STATE[ILUN] = 0
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbgrow.pro b/Code/script_idl_mv/astrolib/fxbgrow.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6285bce4061ea256dd190e4adc5393021eea41e9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbgrow.pro
@@ -0,0 +1,245 @@
+        PRO FXBGROW, UNIT, HEADER, NROWS, ERRMSG=ERRMSG, NOZERO=NOZERO, $
+                     BUFFERSIZE=BUFFERSIZE0
+;+
+; NAME: 
+;        FXBGROW
+; PURPOSE     : 
+;       Increase the number of rows in a binary table.
+; EXPLANATION : 
+;       Call FXBGROW to increase the size of an already-existing FITS
+;       binary table.  The number of rows increases to NROWS; however
+;       the table cannot shrink by this operation.  This procedure is
+;       useful when a table with an unknown number of rows must be
+;       created.  The caller would then call FXBCREATE to construct a
+;       table of some base size, and follow with calls to FXBGROW to
+;       lengthen the table as needed.  The extension being enlarged
+;       need not be the last extension in the file.  If subsequent
+;       extensions exist in the file, they will be shifted properly.
+;
+; CALLING SEQUENCE :
+;       FXBGROW, UNIT, HEADER, NROWS[, ERRMSG= , NOZERO= , BUFFERSIZE= ]
+;
+; INPUT PARAMETERS :
+;       UNIT     = Logical unit number of an already-opened file.
+;       HEADER   = String array containing the FITS binary table extension
+;                  header.  The header is modified in place.
+;       NROWS    = New number of rows, always more than the previous
+;                  number.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       NOZERO   = when set, FXBGROW will not zero-pad the new data if
+;                  it doesn't have to.
+;       ERRMSG    = If defined and passed, then any error messages will be
+;                   returned to the user in this parameter rather than
+;                   depending on the MESSAGE routine in IDL.  If no errors are
+;                   encountered, then a null string is returned.  In order to
+;                   use this feature, ERRMSG must be defined first, e.g.
+;
+;                       ERRMSG = ''
+;                       FXBGROW, ERRMSG=ERRMSG, ...
+;                       IF ERRMSG NE '' THEN ...
+;       BUFFERSIZE = Size in bytes for intermediate data transfers
+;                    (default 32768)
+;
+; Calls       : 
+;       FXADDPAR, FXHREAD, BLKSHIFT
+; Common      : 
+;       Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;       information.
+; Restrictions: 
+;       The file must be open with write permission.
+;
+;       The binary table extension in question must already by written
+;       to the file (using FXBCREATE).
+;
+;       A table can never shrink via this operation.
+;
+; SIDE EFFECTS: 
+;       The FITS file will grow in size, and heap areas are
+;       preserved by moving them to the end of the file.
+;
+;       The header is modified to reflect the new number of rows.
+; CATEGORY    : 
+;       Data Handling, I/O, FITS, Generic.
+;       Initially written, C. Markwardt, GSFC, Nov 1998
+;       Added ability to enlarge arbitrary extensions and tables with
+;         variable sized rows, not just the last extension in a file,
+;         CM, April 2000
+;       Fix bug in the zeroing of the output file, C. Markwardt, April 2005
+;
+;-
+;
+@fxbintable
+        ON_ERROR, 0
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() NE 3 THEN BEGIN
+                MESSAGE = 'Syntax:  FXBGROW, UNIT, HEADER, NROWS'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+
+;
+;  Find the index of the file.
+;
+        ILUN = WHERE(LUN EQ UNIT,NLUN)
+        ILUN = ILUN[0]
+        IF NLUN EQ 0 THEN BEGIN
+                MESSAGE = 'Unit ' + STRTRIM(UNIT,2) +   $
+                        ' not opened properly'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+;
+;  Don't shrink the file.
+;
+        IF NAXIS2[ILUN] GE NROWS THEN GOTO, FINISH
+;
+;  Make sure the file was opened for write access.
+;
+        IF STATE[ILUN] NE 2 THEN BEGIN
+                MESSAGE = 'Unit ' + STRTRIM(UNIT,2) +   $
+                        ' not opened for write access'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+;
+;  Compute number of bytes and buffer size
+;
+
+        NBYTES = (NROWS-NAXIS2[ILUN])*NAXIS1[ILUN]
+        IF N_ELEMENTS(BUFFERSIZE0) EQ 0 THEN BUFFERSIZE0 = 32768L
+        BUFFERSIZE = LONG(BUFFERSIZE0[0])
+        BUFFERSIZE = FLOOR(BUFFERSIZE/NAXIS1[ILUN])*NAXIS1[ILUN]
+        IF BUFFERSIZE LE 0 THEN BUFFERSIZE = NAXIS1[ILUN]
+
+;
+;  First, shift the following extensions by block multiples
+;
+        ;; Current beginning of next extension
+        N_EXT  = NHEADER[ILUN] + HEAP[ILUN] + DHEAP[ILUN] 
+        ;; New beginning of next extension, after shifting
+        N_EXT1 = N_EXT + NBYTES
+        ;; Round to nearest block size
+        IF N_EXT  MOD 2880 NE 0 THEN N_EXT  = N_EXT  + 2880 - (N_EXT  MOD 2880)
+        IF N_EXT1 MOD 2880 NE 0 THEN N_EXT1 = N_EXT1 + 2880 - (N_EXT1 MOD 2880)
+        NBYTES1 = N_EXT1 - N_EXT
+
+        ERRMSG1 = ''
+        IF NBYTES1 GT 0 THEN BEGIN
+            BLKSHIFT, UNIT, N_EXT, NBYTES1, ERRMSG=ERRMSG1, $
+              NOZERO=KEYWORD_SET(NOZERO), BUFFERSIZE=BUFFERSIZE
+            IF ERRMSG1 NE '' THEN GOTO, RETMESSAGE
+        ENDIF
+;
+;  Next, shift the data between the end of the table and the next
+;  extension, if any.
+;
+        ;; End of table data (but before variable-sized heap data)
+        ETAB = NHEADER[ILUN] + NAXIS1[ILUN]*NAXIS2[ILUN]
+        IF N_EXT GT ETAB THEN BEGIN
+            BLKSHIFT, UNIT, [ETAB, N_EXT1-NBYTES-1L], NBYTES, ERRMSG=ERRMSG1, $
+              NOZERO=KEYWORD_SET(NOZERO), BUFFERSIZE=BUFFERSIZE
+        ENDIF
+
+        RETMESSAGE:
+        IF ERRMSG1 NE '' THEN BEGIN
+            MESSAGE = ERRMSG1
+            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ERRMSG = MESSAGE
+                RETURN
+            END ELSE MESSAGE, MESSAGE
+        ENDIF
+
+
+;
+;  Zero-fill if necessary (if the original table had no trailing
+;  extensions)
+;
+
+        FS = FSTAT(UNIT)
+        
+        IF FS.SIZE LT N_EXT1 AND NOT KEYWORD_SET(NOZERO) THEN BEGIN
+            POINT_LUN, UNIT, ETAB
+            NLEFT = N_EXT1 - ETAB
+            NBUFF = BUFFERSIZE < NLEFT
+            BB = BYTARR(NBUFF)
+
+            WHILE NLEFT GT 0 DO BEGIN
+                WRITEU, UNIT, BB
+                NLEFT = NLEFT - N_ELEMENTS(BB)
+                IF (NLEFT LT NBUFF) AND (NLEFT GT 0) THEN BB = BB[0:NLEFT-1]
+            ENDWHILE
+        ENDIF
+
+;
+;  Update the internal state.
+;
+        HEAP[ILUN] = HEAP[ILUN] + NBYTES
+        NAXIS2[ILUN] = NROWS
+
+;
+;  Modify passed copy of header
+;
+        IF N_ELEMENTS(HEADER) GT 0 THEN BEGIN
+            FXADDPAR, HEADER, 'NAXIS2', LONG(NROWS), 'Number of rows (grown)'
+            THEAP = FXPAR(HEADER, 'THEAP', COUNT=COUNT)
+            IF COUNT GT 0 THEN BEGIN
+                THEAP = THEAP + NBYTES
+                FXADDPAR, HEADER, 'THEAP', THEAP, 'Offset of heap'
+            ENDIF
+        ENDIF
+
+
+;
+;  Modify internal copy of HEADER
+;
+        XHEADER = HEAD[*,ILUN]
+        FXADDPAR, XHEADER, 'NAXIS2', LONG(NROWS), 'Number of rows (grown)'
+        THEAP = FXPAR(XHEADER, 'THEAP', COUNT=COUNT)
+        IF COUNT GT 0 THEN BEGIN
+            THEAP = THEAP + NBYTES
+            FXADDPAR, XHEADER, 'THEAP', THEAP, 'Offset of heap'
+        ENDIF
+        HEAD[*,ILUN] = XHEADER
+
+;
+;  Modify disk copy of HEADER
+;
+        POINT_LUN, UNIT, MHEADER[ILUN]
+        FXHREAD, UNIT, DHEADER, STATUS
+        IF STATUS NE 0 THEN BEGIN
+            MESSAGE = 'Could not load header from file'
+            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ERRMSG = MESSAGE
+                RETURN
+            END ELSE MESSAGE, MESSAGE
+        ENDIF
+        FXADDPAR, DHEADER, 'NAXIS2', LONG(NROWS), 'Number of rows (grown)'
+        THEAP = FXPAR(DHEADER, 'THEAP', COUNT=COUNT)
+        IF COUNT GT 0 THEN BEGIN
+            THEAP = THEAP + NBYTES
+            FXADDPAR, DHEADER, 'THEAP', THEAP, 'Offset of heap'
+        ENDIF
+        ;; Don't worry about the header increasing in size, since
+        ;; every binary table has to have NAXIS2 already.
+        SLEN = STRLEN(DHEADER[0])
+        FULL = STRING(REPLICATE(32B, 80))
+        ;; Pad with spaces
+        IF SLEN LT 80 THEN DHEADER[0] = DHEADER[0] + STRMID(FULL,0,80-SLEN)
+        BHDR = BYTE(DHEADER)
+        BHDR = BHDR[0:79,*]
+        POINT_LUN, UNIT, MHEADER[ILUN]
+        WRITEU, UNIT, BHDR
+
+FINISH:
+        IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+        RETURN
+        END
diff --git a/Code/script_idl_mv/astrolib/fxbheader.pro b/Code/script_idl_mv/astrolib/fxbheader.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6e37e19abfd31c10e43b0c26a7267e7113dbf557
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbheader.pro
@@ -0,0 +1,81 @@
+        FUNCTION FXBHEADER, UNIT
+;+
+; NAME: 
+;       FXBHEADER()
+;
+; PURPOSE: 
+;       Returns the header of an open FITS binary table.
+;
+; EXPLANATION:
+;      This procedure returns the FITS extension header of a FITS
+;         binary table opened for read with the command FXBOPEN.
+;
+; Use         : Result = FXBHEADER(UNIT)
+;
+; Inputs      : UNIT    = Logical unit number returned by FXBOPEN routine.
+;                         Must be a scalar integer.
+;
+; Opt. Inputs : None.
+;
+; Outputs     : The result of the function is a string array containing the
+;               header for the FITS binary table that UNIT points to.
+;
+; Opt. Outputs: None.
+;
+; Keywords    : None.
+;
+; Calls       : FXBFINDLUN
+;
+; Common      : Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;               information.
+;
+; Restrictions: None.
+;
+; Side effects: The string array returned always has as many elements as the
+;               largest header read by FXBOPEN.  Any extra elements beyond the
+;               true header are blank or null strings.
+;
+;               The header will be returned whether or not the table is still
+;               open or not.
+;
+;               If UNIT does not point to a binary table, then a string array
+;               of nulls is returned.
+;
+;               If UNIT is an undefined variable, then the null string is
+;               returned.
+;
+; Category    : Data Handling, I/O, FITS, Generic.
+;
+; Prev. Hist. : None.
+;
+; Written     : William Thompson, GSFC, 1 July 1993.
+;
+; Modified    : Version 1, William Thompson, GSFC, 1 July 1993.
+;
+; Version     : Version 1, 1 July 1993.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+@fxbintable
+        ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() NE 1 THEN MESSAGE,'Syntax:  Result = FXBHEADER(UNIT)'
+;
+;  If UNIT is undefined, then return the null string.
+;
+        IF N_ELEMENTS(UNIT) EQ 0 THEN RETURN, ''
+;
+;  Check the validity of UNIT.
+;
+        IF N_ELEMENTS(UNIT) GT 1 THEN MESSAGE,'UNIT must be a scalar'
+        SZ = SIZE(UNIT)
+        IF SZ[SZ[0]+1] GT 3 THEN MESSAGE,'UNIT must be an integer'
+;
+;  Get the state associated with UNIT.
+;
+        ILUN = FXBFINDLUN(UNIT)
+        RETURN, HEAD[*,ILUN]
+;
+        END
diff --git a/Code/script_idl_mv/astrolib/fxbhelp.pro b/Code/script_idl_mv/astrolib/fxbhelp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2a7c19977ab1e592ca3c284deebe5579dcca7d88
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbhelp.pro
@@ -0,0 +1,128 @@
+	PRO FXBHELP,UNIT
+;+
+; NAME: 
+;	FXBHELP
+; Purpose     : 
+;	Prints short description of columns in a FITS binary table.
+; Explanation : 
+;	Prints a short description of the columns in a FITS binary table to the
+;	terminal screen.
+; Use         : 
+;	FXBHELP, UNIT
+; Inputs      : 
+;	UNIT	= Logical unit number of file opened by FXBOPEN.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	None.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	None.
+; Calls       : 
+;	FXBFIND, FXBFINDLUN, FXPAR
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	The file must have been opened with FXBOPEN.
+; Side effects: 
+;	Certain fields may be truncated in the display.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Feb. 1992, from TBHELP by W. Landsman.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 12 May 1993.
+;		Modified to not write to a logical unit number assigned to the
+;		terminal.  This makes it compatible with IDL for Windows.
+;       Version 3, Wayne Landsman GSFC April 2010
+;                Remove use of obsolete !ERR system variable
+; Version     : 
+;	Version 3, April 2010.
+;-
+;
+@fxbintable
+	ON_ERROR,2
+	COMPILE_OPT IDL2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() LT 1 THEN MESSAGE,'Syntax:  FXBHELP, UNIT'
+;
+;  Get the header.
+;
+	ILUN = FXBFINDLUN(UNIT)
+	HEADER = HEAD[*,ILUN]
+;
+;  Get the extension name.
+;
+	EXTNAME = FXPAR(HEADER,'EXTNAME', COUNT=N_EXTNAME)
+	IF N_EXTNAME LE 0 THEN EXTNAME = ''
+;
+;  Print the labels.
+;
+	PRINT,' '
+	PRINT,'FITS Binary Table:  ' + EXTNAME
+	PRINT,'Table contains ' + STRTRIM(TFIELDS[ILUN],2) +	$
+		' columns, by ' + STRTRIM(NAXIS2[ILUN],2) + ' rows'
+	PRINT,' '
+	T_FORMAT = 26	;Starting column for Format/Size
+	T_UNITS = 46	;Starting column for Units
+	T_NULL = 58	;Starting column for Null
+	PRINT,FORMAT="('Col',2X,'Name',T" + STRTRIM(T_FORMAT,2) +	$
+		",'Type Size',T" + STRTRIM(T_UNITS,2) + ",'Units',T" + $
+		STRTRIM(T_NULL,2) + ",6X,'Null')"
+	PRINT,' '
+;
+;  Get the values of the information to be printed.
+;
+	FXBFIND,HEADER,'TDIM', COL,TDIM0, N_FOUND,''
+	FXBFIND,HEADER,'TUNIT',COL,TUNIT0,N_FOUND,''
+;
+	FXBFIND,HEADER,'TNULL',COL,TNULL0,N_FOUND
+	SNULL = STRARR(TFIELDS[ILUN])
+	IF N_FOUND GT 0 THEN FOR I = 0,N_ELEMENTS(COL)-1 DO	$
+		SNULL[COL[I]-1] = STRTRIM(TNULL0[I],2)
+;
+;  Print the column information.
+;
+	FOR ICOL = 0,TFIELDS[ILUN]-1 DO BEGIN
+		CASE FORMAT[ICOL,ILUN] OF
+			'L':  TYPE0 = 'Log'
+			'A':  TYPE0 = 'Asc'
+			'B':  TYPE0 = 'Byt'
+			'I':  TYPE0 = 'Int'
+			'J':  TYPE0 = 'Lng'
+			'E':  TYPE0 = 'Flt'
+			'D':  TYPE0 = 'Dbl'
+			'C':  TYPE0 = 'Cmp'
+			'M':  TYPE0 = 'DbC'
+			'X':  TYPE0 = 'Bit'
+		ENDCASE			
+		IF MAXVAL[ICOL,ILUN] GT 0 THEN BEGIN
+			ELEM = MAXVAL[ICOL,ILUN]
+			IF FORMAT[ICOL,ILUN] EQ 'M' THEN ELEM = ELEM/2
+			ELEM = "< " + STRTRIM(ELEM,2)
+		END ELSE IF TDIM0[ICOL] NE '' THEN BEGIN
+			ELEM = TDIM0[ICOL]
+		END ELSE BEGIN
+			ELEM = N_ELEM[ICOL,ILUN]
+			IF FORMAT[ICOL,ILUN] EQ 'M' THEN ELEM = ELEM/2
+			ELEM = STRTRIM(ELEM,2)
+		ENDELSE
+		PRINT,ICOL+1,TTYPE[ICOL,ILUN],TYPE0,ELEM,		$
+			TUNIT0[ICOL],SNULL[ICOL], FORMAT='(I3,2X,A,T' +	$
+			STRTRIM(T_FORMAT-2,2) + ',2X,A3,2X,A,T' +	$
+			STRTRIM(T_UNITS-2,2) + ',2X,A,T' +		$
+			STRTRIM(T_NULL-2,2) + ',2X,A10)'
+	ENDFOR
+	PRINT,' '
+;
+	RETURN
+	END
+
diff --git a/Code/script_idl_mv/astrolib/fxbhmake.pro b/Code/script_idl_mv/astrolib/fxbhmake.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7d9ff316702841923e037fde7c95714e4df11bfa
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbhmake.pro
@@ -0,0 +1,150 @@
+	PRO FXBHMAKE,HEADER,NROWS,EXTNAME,COMMENT,DATE=DATE,	$
+		INITIALIZE=INITIALIZE,EXTVER=EXTVER,EXTLEVEL=EXTLEVEL,	$
+		ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXBHMAKE
+; Purpose     : 
+;	Create basic FITS binary table extension (BINTABLE) header.
+; Explanation : 
+;	Creates a basic header array with all the required keywords, but with
+;	none of the table columns defined.  This defines a basic structure
+;	which can then be added to or modified by other routines.
+; Use         : 
+;	FXBHMAKE, HEADER, NROWS  [, EXTNAME  [, COMMENT ]]
+; Inputs      : 
+;	NROWS	= Number of rows in the binary table.
+; Opt. Inputs : 
+;	EXTNAME	= If passed, then the EXTNAME record is added with this value.
+;	COMMENT = Comment to go along with EXTNAME.
+; Outputs     : 
+;	HEADER = String array containing FITS extension header.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	INITIALIZE = If set, then the header is completely initialized, and any
+;		     previous entries are lost.
+;	DATE	   = If set, then the DATE keyword is added to the header.
+;	EXTVER	   = Extension version number (integer).
+;	EXTLEVEL   = Extension level number (integer).
+;	ERRMSG	   = If defined and passed, then any error messages will be
+;		     returned to the user in this parameter rather than
+;		     depending on the MESSAGE routine in IDL.  If no errors are
+;		     encountered, then a null string is returned.  In order to
+;		     use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBHMAKE, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	GET_DATE, FXADDPAR, FXHCLEAN
+; Common      : 
+;	None.
+; Restrictions: 
+;	Warning:  No checking is done of any of the parameters.
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Jan 1992.
+;	William Thompson, Sep 1992, added EXTVER and EXTLEVEL keywords.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 June 1994
+;		Added ERRMSG keyword.
+;       Version 3, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+; Version     :
+;       Version 3, 23 June 1994
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+	ON_ERROR,2
+;
+;  Check the number of parameters first.
+;
+	IF N_PARAMS() LT 2 THEN BEGIN
+		MESSAGE = 'Calling sequence:  FXBHMAKE, HEADER, NROWS ' + $
+			'[, EXTNAME [, COMMENT ]]'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  If requested, then initialize the header.
+;
+	IF KEYWORD_SET(INITIALIZE) THEN BEGIN
+		HEADER = STRARR(36)
+		HEADER[0] = 'END' + STRING(REPLICATE(32B,77))
+;
+;  Else, if undefined, then initialize the header.
+;
+	END ELSE IF N_ELEMENTS(HEADER) EQ 0 THEN BEGIN
+		HEADER = STRARR(36)
+		HEADER[0] = 'END' + STRING(REPLICATE(32B,77))
+;
+;  Otherwise, make sure that HEADER is a string array, and remove any keywords
+;  that describe the format of the file.
+;
+	END ELSE BEGIN
+		SZ = SIZE(HEADER)
+		IF (SZ[0] NE 1) OR (SZ[2] NE 7) THEN BEGIN
+			MESSAGE = 'HEADER must be a (one-dimensional) ' + $
+				'string array'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			FXHCLEAN,HEADER,ERRMSG=ERRMSG
+			IF ERRMSG EQ '' THEN RETURN
+		END ELSE FXHCLEAN,HEADER
+	ENDELSE
+;
+;  Add the required keywords.  Start out with a completely blank table, with no
+;  columns.
+;
+	FXADDPAR,HEADER,'XTENSION','BINTABLE','Written by IDL:  '+ SYSTIME()
+	FXADDPAR,HEADER,'BITPIX',8
+	FXADDPAR,HEADER,'NAXIS',2,'Binary table'
+	FXADDPAR,HEADER,'NAXIS1',0,'Number of bytes per row'
+	FXADDPAR,HEADER,'NAXIS2',LONG(NROWS),'Number of rows'
+	FXADDPAR,HEADER,'PCOUNT',0,'Random parameter count'
+	FXADDPAR,HEADER,'GCOUNT',1,'Group count'
+	FXADDPAR,HEADER,'TFIELDS',0,'Number of columns'
+;
+;  If requested, add the EXTNAME keyword to the header.
+;
+	IF N_PARAMS() GE 3 THEN BEGIN
+		IF N_PARAMS() EQ 3 THEN COMMENT = 'Extension name'
+		FXADDPAR,HEADER,'EXTNAME',EXTNAME,COMMENT
+	ENDIF
+;
+;  If requested, add the EXTVER keyword to the header.
+;
+	IF N_ELEMENTS(EXTVER) EQ 1 THEN		$
+		FXADDPAR,HEADER,'EXTVER',LONG(EXTVER),'Extension version'
+;
+;  If requested, add the EXTLEVEL keyword to the header.
+;
+	IF N_ELEMENTS(EXTLEVEL) EQ 1 THEN	$
+		FXADDPAR,HEADER,'EXTLEVEL',LONG(EXTLEVEL),'Extension level'
+;
+;  If requested, add the DATE keyword to the header, containing the current
+;  date.
+;
+	IF KEYWORD_SET(DATE) THEN BEGIN
+	        GET_DATE,DTE                    ;Get current date as CCYY-MM-DD
+        	FXADDPAR,HEADER,'DATE',DTE,'Creation date'
+	ENDIF
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbintable.pro b/Code/script_idl_mv/astrolib/fxbintable.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f4791a2d5da790b1c483cd08651790e6f0582e13
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbintable.pro
@@ -0,0 +1,71 @@
+;+
+; NAME: 
+;	FXBINTABLE
+; Purpose     : 
+;	Common block FXBINTABLE used by "FXB" routines.
+; Explanation : 
+;	This is not an IDL routine as such, but contains the definition of the
+;	common block FXBINTABLE for inclusion into other routines.  By defining
+;	the common block in one place, the problem of conflicting definitions
+;	is avoided.
+;
+;	This file is included into routines that need this common block with
+;	the single line (left justified)
+;
+;				  @fxbintable
+;
+;	FXBINTABLE contains the following arrays:
+;
+;		LUN	= An array of logical unit numbers of currently (or
+;			  previously) opened binary table files.
+;		STATE	= Array containing the state of the FITS files
+;			  associated with the logical unit numbers, where
+;			  0=closed, 1=open for read, and 2=open for write.
+;		HEAD	= FITS binary table headers.
+;		MHEADER	= Array containing the positions of the first data byte
+;			  of the header for each file referenced by array LUN.
+;		NHEADER	= Array containing the positions of the first data byte
+;			  after the header for each file referenced by array
+;			  LUN.
+;		NAXIS1	= Values of NAXIS1 from the binary table headers.
+;		NAXIS2	= Values of NAXIS2 from the binary table headers.
+;		TFIELDS	= Values of TFIELDS from the binary table headers.
+;		HEAP	= The start of the first byte of the heap area
+;			  for variable length arrays.
+;		DHEAP	= The start of the first byte of the next variable
+;			  length array, if writing.
+;		BYTOFF	= Byte offset from the beginning of the row for each
+;			  column in the binary table headers.
+;		TTYPE	= Values of TTYPE for each column in the binary table
+;			  headers.
+;		FORMAT	= Character code formats of the various columns.
+;		IDLTYPE	= IDL type code for each column in the binary table
+;			  headers.
+;		N_ELEM	= Number of elements for each column in the binary
+;			  table headers.
+;		TSCAL	= Scale factors for the individual columns.
+;		TZERO	= Zero offsets for the individual columns.
+;		MAXVAL	= For variable length arrays, contains the maximum
+;			  number of elements for each column in the binary
+;			  table headers.
+;		N_DIMS	= Number of dimensions, and array of dimensions for
+;			  each column of type string in the binary table
+;			  headers.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Feb 1992.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 July 1993.
+;		Added DHEAP variable to fix bug with variable length arrays.
+; Version     : 
+;	Version 2, 21 July 1993.
+;-
+;
+	COMMON FXBINTABLE,LUN,STATE,HEAD,MHEADER,NHEADER,NAXIS1,NAXIS2,	$
+		TFIELDS,HEAP,DHEAP,BYTOFF,TTYPE,FORMAT,IDLTYPE,N_ELEM,TSCAL, $
+		TZERO,MAXVAL,N_DIMS
diff --git a/Code/script_idl_mv/astrolib/fxbisopen.pro b/Code/script_idl_mv/astrolib/fxbisopen.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ae5fca17b2eed8a05d4e6d0bfdbbbf4d9a613266
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbisopen.pro
@@ -0,0 +1,77 @@
+        FUNCTION FXBISOPEN,UNIT
+;+
+; NAME: 
+;       FXBISOPEN()
+;
+; PURPOSE: 
+;       Returns true if UNIT points to an open FITS binary table.
+;
+; Explanation : This procedure checks to see if the logical unit number given
+;               by the variable UNIT corresponds to a FITS binary table opened
+;               for read with the command FXBOPEN, and which has not yet been
+;               closed with FXBCLOSE.
+;
+; Use         : Result = FXBISOPEN(UNIT)
+;
+;               If FXBISOPEN(UNIT) THEN ...
+;
+; Inputs      : UNIT    = Logical unit number returned by FXBOPEN routine.
+;                         Must be a scalar integer.
+;
+; Opt. Inputs : None.
+;
+; Outputs     : The result of the function is either True (1) or False (0),
+;               depending on whether UNIT points to an open binary table or
+;               not.
+;
+; Opt. Outputs: None.
+;
+; Keywords    : None.
+;
+; Calls       : FXBFINDLUN
+;
+; Common      : Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;               information.
+;
+; Restrictions: None.
+;
+; Side effects: If UNIT is an undefined variable, then False (0) is returned.
+;
+;               If UNIT points to a FITS binary table file that is opened for
+;               write, then False (0) is returned.
+;
+; Category    : Data Handling, I/O, FITS, Generic.
+;
+; Prev. Hist. : None.
+;
+; Written     : William Thompson, GSFC, 1 July 1993.
+;
+; Modified    : Version 1, William Thompson, GSFC, 1 July 1993.
+;
+; Version     : Version 1, 1 July 1993.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+@fxbintable
+        ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() NE 1 THEN MESSAGE,'Syntax:  Result = FXBISOPEN(UNIT)'
+;
+;  If UNIT is undefined, then return False.
+;
+        IF N_ELEMENTS(UNIT) EQ 0 THEN RETURN, 0
+;
+;  Check the validity of UNIT.
+;
+        IF N_ELEMENTS(UNIT) GT 1 THEN MESSAGE,'UNIT must be a scalar'
+        SZ = SIZE(UNIT)
+        IF SZ[SZ[0]+1] GT 3 THEN MESSAGE,'UNIT must be an integer'
+;
+;  Get the state associated with UNIT.
+;
+        ILUN = FXBFINDLUN(UNIT)
+        RETURN, STATE[ILUN] EQ 1
+;
+        END
diff --git a/Code/script_idl_mv/astrolib/fxbopen.pro b/Code/script_idl_mv/astrolib/fxbopen.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f3273776d82cafe9239614e5594c8dda4599816d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbopen.pro
@@ -0,0 +1,350 @@
+	PRO FXBOPEN, UNIT, FILENAME0, EXTENSION, HEADER, NO_TDIM=NO_TDIM, $
+                ERRMSG=ERRMSG, ACCESS=ACCESS, REOPEN=REOPEN
+;+
+; NAME: 
+;	FXBOPEN
+; Purpose     : 
+;	Open binary table extension in a disk FITS file for reading or updating
+; Explanation : 
+;	Opens a binary table extension in a disk FITS file for reading.  The
+;	columns are then read using FXBREAD, and the file is closed when done
+;	with FXBCLOSE.
+; Use         : 
+;	FXBOPEN, UNIT, FILENAME, EXTENSION  [, HEADER ]
+; Inputs      : 
+;       FILENAME  = Name of FITS file to be opened.  Optional
+;                   extension *number* may be specified, in either of
+;                   the following formats (using the FTOOLS
+;                   convention): FILENAME[EXT] or FILENAME+EXT, where
+;                   EXT is 1 or higher.  Such an extension
+;                   specification takes priority over EXTENSION.
+;                
+;	EXTENSION = Either the number of the FITS extension, starting with the
+;		    first extension after the primary data unit being one; or a
+;		    character string containing the value of EXTNAME to search
+;		    for.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	UNIT	  = Logical unit number of the opened file.
+; Opt. Outputs: 
+;	HEADER	  = String array containing the FITS binary table extension
+;		    header.
+; Keywords    : 
+;	NO_TDIM	  = If set, then any TDIMn keywords found in the header are
+;		    ignored.
+;
+;       ACCESS    = A scalar string describing access privileges as
+;                   one of READ ('R') or UPDATE ('RW').
+;                   DEFAULT: 'R'
+;
+;       REOPEN    = If set, UNIT must be an already-opened file unit.
+;                   FXBOPEN will treat the file as a FITS file.
+;
+;	ERRMSG	  = If defined and passed, then any error messages will be
+;		    returned to the user in this parameter rather than
+;		    depending on the MESSAGE routine in IDL.  If no errors are
+;		    encountered, then a null string is returned.  In order to
+;		    use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBOPEN, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	FXBFINDLUN, FXBPARSE, FXHREAD, FXPAR
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	The file must be a valid FITS file.
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, Feb 1992, based on READFITS by J. Woffard and W. Landsman.
+;	W. Thompson, Feb 1992, changed from function to procedure.
+;	W. Thompson, June 1992, fixed up error handling.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 27 May 1994
+;		Added ERRMSG keyword.
+;	Version 3, William Thompson, GSFC, 21 June 1994
+;		Extended ERRMSG to call to FXBPARSE
+;       Version 4, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;       Version 4, 23 June 1994
+;
+; Added ACCESS, REOPEN keywords, and FXFILTER package, CM 1999 Feb 03
+; Added FILENAME[EXT] and FILENAME+EXT extension parsing, CM 1999 Jun 28
+; Some general tidying, CM 1999 Nov 18
+;       Allow for possible 64bit integer number of bytes W. Landsman Nov 2007
+;       Make Ndata a 64bit integer to deal with larger files, E. Hivon, Mar 2008
+;       
+;
+;-
+;
+@fxbintable
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() LT 3 THEN BEGIN
+		MESSAGE = 'Syntax:  FXBOPEN, UNIT, FILENAME, EXTENSION  ' + $
+			'[, HEADER ]'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Check the type of the EXTENSION parameter.
+;
+	IF N_ELEMENTS(EXTENSION) NE 1 THEN BEGIN
+		MESSAGE = 'EXTENSION must be a scalar'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+	SZ = SIZE(EXTENSION)
+	ETYPE = SZ[SZ[0]+1]
+	IF ETYPE EQ 8 THEN BEGIN
+		MESSAGE = 'EXTENSION must not be a structure'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  If EXTENSION is of type string, then search for the proper extension by
+;  name.  Otherwise, search by number.
+;
+	IF ETYPE EQ 7 THEN BEGIN
+		S_EXTENSION = STRTRIM(STRUPCASE(EXTENSION),2)
+	END ELSE BEGIN
+		I_EXTENSION = FIX(EXTENSION)
+		IF I_EXTENSION LT 1 THEN BEGIN
+			MESSAGE = 'EXTENSION must be greater than zero'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+	ENDELSE
+;
+;  Check access parameter
+        IF N_ELEMENTS(ACCESS) EQ 0 THEN ACCESS='R'
+        SZ = SIZE(ACCESS)
+        IF SZ[SZ[0]+1] NE 7 THEN GOTO, ACCERR
+        IF STRUPCASE(ACCESS) NE 'R' AND STRUPCASE(ACCESS) NE 'RW' THEN BEGIN
+            ACCERR:
+            MESSAGE = "ACCESS must be either 'R' or 'RW'"
+            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ERRMSG = MESSAGE
+                RETURN
+            END ELSE MESSAGE, MESSAGE
+        ENDIF
+            
+;
+;  Establish the read/write state
+;
+	ST = 1                                    ; Read only
+        IF STRUPCASE(ACCESS) EQ 'RW' THEN ST = 2  ; Read/write
+
+;
+;  Get a logical unit number, and open the file.
+;
+        FILENAME = FILENAME0
+        IF NOT KEYWORD_SET(REOPEN) THEN BEGIN
+
+            ;; Check for extension name at the end of a filename
+            LEN = STRLEN(FILENAME0)
+            NEWEXT = 0L
+            BFILENAME = BYTE(FILENAME)
+            B0 = (BYTE('0'))(0) & B9 = (BYTE('9'))(0) 
+            I = LEN-1
+            BB = BFILENAME[I]
+
+            ;; First case:  FILENAME[5]
+            IF LEN GE 4 AND STRING(BB) EQ ']' THEN BEGIN ;; Count backwards
+                I = I - 1
+                IF BFILENAME[I] GE B0 AND BFILENAME[I] LE B9 THEN BEGIN
+                    WHILE I GT 0 AND $
+                      BFILENAME[I] GE B0 AND BFILENAME[I] LT B9 DO I = I - 1
+                    IF I GT 0 AND STRING(BFILENAME[I]) EQ '[' THEN BEGIN
+                        NEWEXT = LONG(STRMID(FILENAME,I+1,10))
+                        FLEN = I
+                    ENDIF
+                ENDIF
+            ENDIF
+
+            ;; Second case: FILENAME+5
+            IF LEN GE 3 AND BB GE B0 AND BB LE B9 THEN BEGIN ;; Count backwards
+                WHILE I GT 0 AND $
+                  BFILENAME[I] GE B0 AND BFILENAME[I] LT B9 DO I = I - 1
+                IF I GT 0 AND STRING(BFILENAME[I]) EQ '+' THEN BEGIN
+                    NEWEXT = LONG(STRMID(FILENAME,I+1,10))
+                    FLEN = I
+                ENDIF
+            ENDIF
+            IF NEWEXT GT 0 THEN BEGIN
+                FILENAME = STRMID(FILENAME, 0, FLEN)
+                I_EXTENSION = NEWEXT
+                ETYPE = 1
+            ENDIF
+
+            ;; Open the file
+            IF ST EQ 1 THEN $
+              OPENR, UNIT, FILENAME, /BLOCK, /GET_LUN, ERROR=ERROR $
+            ELSE $
+              OPENU, UNIT, FILENAME, /BLOCK, /GET_LUN, ERROR=ERROR
+            IF ERROR NE 0 THEN GOTO, NO_SUCH_FILE
+        ENDIF
+
+;
+;  Reopen the file if requested.  Essentially this means seeking to
+;  the start, after some error checking.
+;        
+        IF KEYWORD_SET(REOPEN) THEN BEGIN
+            SZ = SIZE(UNIT)
+            IF N_ELEMENTS(UNIT) NE 1 OR SZ[SZ[0]+1] EQ 8 THEN BEGIN
+                MESSAGE = 'UNIT must be a scalar numeric type'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                    ERRMSG = MESSAGE
+                    RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF
+
+;
+;  Error checking on file unit
+;
+            UNIT = UNIT[0]
+            FS = FSTAT(UNIT)
+            IF (FS.OPEN NE 1) OR (FS.READ NE 1) $
+              OR (ST EQ 2 AND FS.WRITE NE 1) THEN BEGIN
+                MESSAGE = 'UNIT '+strtrim(unit,2)+' must be open for reading'
+                IF ST EQ 2 THEN MESSAGE = MESSAGE + '/writing'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                    ERRMSG = MESSAGE
+                    RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF
+
+            ;; Seek to the start of the file
+            POINT_LUN, UNIT, 0L
+        ENDIF
+
+
+;
+;  Store the UNIT number in the common block, and leave space for the other
+;  parameters.  Initialize the common block if need be.  ILUN is an index into
+;  the arrays.
+;
+	ILUN = FXBFINDLUN(UNIT)
+;
+;  Mark the file as open for read or write.
+;
+	STATE[ILUN] = ST
+;
+;  Read the primary header.
+;
+	FXHREAD,UNIT,HEADER,STATUS
+	IF STATUS NE 0 THEN BEGIN
+		FREE_LUN,UNIT
+		MESSAGE = 'Unable to read primary FITS header'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+	I_EXT = 0
+;
+;  Make sure that the file does contain extensions.
+;
+	START = 0L
+	IF NOT FXPAR(HEADER,'EXTEND', START=START) THEN BEGIN
+		FREE_LUN, UNIT
+		MESSAGE = 'File ' + FILENAME + ' does not contain extensions'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Get the number of bytes taken up by the data.
+;
+NEXT_EXT:
+	BITPIX = FXPAR(HEADER,'BITPIX', START=START)
+	NAXIS  = FXPAR(HEADER,'NAXIS',  START=START)
+	GCOUNT = FXPAR(HEADER,'GCOUNT', START=START)
+	IF GCOUNT EQ 0 THEN GCOUNT = 1
+	PCOUNT = FXPAR(HEADER,'PCOUNT', START=START)
+	IF NAXIS GT 0 THEN BEGIN 
+		DIMS = FXPAR(HEADER,'NAXIS*')		;Read dimensions
+		NDATA = long64(DIMS[0])
+		IF NAXIS GT 1 THEN FOR I=2,NAXIS DO NDATA = NDATA*DIMS[I-1]
+	ENDIF ELSE NDATA = 0
+	NBYTES = LONG64(ABS(BITPIX) / 8) * GCOUNT * (PCOUNT + NDATA)
+;
+;  Read the next extension header in the file.
+;
+	NREC = (NBYTES + 2879) / 2880
+	POINT_LUN, -UNIT, POINTLUN			;Current position
+	MHEAD0 = POINTLUN + NREC*2880L
+	POINT_LUN, UNIT, MHEAD0				;Next FITS extension
+	FXHREAD,UNIT,HEADER,STATUS
+	IF STATUS NE 0 THEN BEGIN
+		FREE_LUN,UNIT
+		MESSAGE = 'Requested extension not found'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+	I_EXT = I_EXT + 1
+;
+;  Check to see if the current extension is the one desired.
+;
+	START = 0L
+	IF ETYPE EQ 7 THEN BEGIN
+		EXTNAME = STRTRIM(STRUPCASE(FXPAR(HEADER,'EXTNAME', $
+                                                  START=START)),2)
+		IF EXTNAME EQ S_EXTENSION THEN GOTO, DONE
+	END ELSE IF I_EXT EQ I_EXTENSION THEN GOTO, DONE
+	GOTO, NEXT_EXT
+;
+;  Check to see if the extension type is BINTABLE or A3DTABLE.
+;
+DONE:
+	XTENSION = STRTRIM(STRUPCASE(FXPAR(HEADER,'XTENSION', START=START)),2)
+	IF (XTENSION NE 'BINTABLE') AND (XTENSION NE 'A3DTABLE') THEN BEGIN
+		IF ETYPE EQ 7 THEN EXT = S_EXTENSION ELSE EXT = I_EXTENSION
+		FREE_LUN,UNIT
+		MESSAGE = 'Extension ' + STRTRIM(EXT,2) +		$
+			' is not a binary table'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Get the rest of the information, and store it in the common block.
+;
+	MHEADER[ILUN] = MHEAD0
+	FXBPARSE,ILUN,HEADER,NO_TDIM=NO_TDIM,ERRMSG=ERRMSG
+	RETURN
+;
+;  Error point for not being able to open the file
+;
+NO_SUCH_FILE:
+	MESSAGE = 'Unable to open file ' + STRTRIM(FILENAME,2)
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+		ERRMSG = MESSAGE
+		RETURN
+	END ELSE MESSAGE, MESSAGE
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbparse.pro b/Code/script_idl_mv/astrolib/fxbparse.pro
new file mode 100644
index 0000000000000000000000000000000000000000..92601d50869682818292953193234b52641163df
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbparse.pro
@@ -0,0 +1,162 @@
+	PRO FXBPARSE, ILUN, HEADER, NO_TDIM=NO_TDIM, ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXBPARSE
+; Purpose     : 
+;	Parse the binary table extension header.
+; Explanation : 
+;	Parses the binary table extension header, and store the information
+;	about the format of the binary table in the FXBINTABLE common
+;	block--called from FXBCREATE and FXBOPEN.
+; Use         : 
+;	FXBPARSE, ILUN, UNIT, HEADER
+; Inputs      : 
+;	ILUN	= Index into the arrays in the FXBINTABLE common block.
+;	HEADER	= FITS binary table extension header.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	None.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	NO_TDIM	  = If set, then any TDIMn keywords found in the header are
+;		    ignored.
+;	ERRMSG	  = If defined and passed, then any error messages will be
+;		    returned to the user in this parameter rather than
+;		    depending on the MESSAGE routine in IDL.  If no errors are
+;		    encountered, then a null string is returned.  In order to
+;		    use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBPARSE, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	FXBFIND, FXBTDIM, FXBTFORM, FXPAR
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	None.
+; Side effects: 
+;	Any TDIMn keywords found for bit arrays (format 'X') are ignored, since
+;	the dimensions would refer to bits, not bytes.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Feb. 1992.
+;	William Thompson, Jan. 1993, modified for renamed FXBTFORM and FXBTDIM.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 June 1994
+;		Added ERRMSG keyword.
+;       Version 3, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;	Version 4, Michael Schubnell, University of Michigan, 22 May 1996
+;		Change N_DIMS from short to long integer.
+;	Version 5, W. Landsman, GSFC, 12 Aug 1997
+;		Use double complex datatype, if needed
+;	Version 6, W. Landsman GSFC 30 Aug 1997
+;       Optimized FXPAR; call FXBFIND for speed, CM 1999 Nov 18
+;       Modify DHEAP(ILUN) when opening table now, CM 2000 Feb 22
+;       Default the TZERO/TSCAL tables to double instead of single
+;         precision floating point, CM 2003 Nov 23
+;       Make NAXIS1 and NAXIS2 64-bit integers to deal with large files,
+;         E. Hivon Mar 2008
+;       Remove use of Obsolete !ERR system variable
+;  Version 
+;       Version 8   April 2010
+;-
+;
+@fxbintable
+	ON_ERROR,2
+        COMPILE_OPT IDL2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 2 THEN BEGIN
+		MESSAGE = 'Syntax:  FXBPARSE, ILUN, HEADER'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Gather the necessary information, and store it in the common block.
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+		FXBTFORM,HEADER,BYTOFF0,IDLTYPE0,FORMAT0,N_ELEM0,MAXVAL0, $
+			ERRMSG=ERRMSG
+		IF ERRMSG NE '' THEN RETURN
+	END ELSE FXBTFORM,HEADER,BYTOFF0,IDLTYPE0,FORMAT0,N_ELEM0,MAXVAL0
+;
+	FXBFIND,HEADER,'TTYPE',COLUMNS,TTYPE0,N_FOUND,''
+	FXBFIND,HEADER,'TSCAL',COLUMNS,TSCAL0,N_FOUND,1D
+	FXBFIND,HEADER,'TZERO',COLUMNS,TZERO0,N_FOUND,0D
+	POINT_LUN,-LUN[ILUN],NHEAD0
+;
+;  Get the information from the required keywords.
+;
+	STORE_ARRAY,HEAD,HEADER,ILUN
+	NHEADER[ILUN] = NHEAD0
+	START = 0L
+	NAXIS1[ILUN]  = long64(FXPAR(HEADER,'NAXIS1', START=START))
+	NAXIS2[ILUN]  = long64(FXPAR(HEADER,'NAXIS2', START=START))
+	TFIELDS[ILUN] = FXPAR(HEADER,'TFIELDS', START=START)
+	PCOUNT        = FXPAR(HEADER,'PCOUNT', START=START)
+;
+;  If THEAP is not present, then set it equal to the size of the table.
+;
+	THEAP = FXPAR(HEADER,'THEAP', START=START, COUNT=N_THEAP)
+	IF N_THEAP LE 0 THEN THEAP = NAXIS1[ILUN]*NAXIS2[ILUN]
+	HEAP[ILUN] = THEAP
+;
+;  Modify DHEAP
+;
+        DDHEAP = PCOUNT - (THEAP - NAXIS1[ILUN]*NAXIS2[ILUN])
+        IF DDHEAP GT 0 THEN DHEAP[ILUN] = DDHEAP ELSE DHEAP[ILUN] = 0
+;
+;  Store the information about the columns.
+;
+	STORE_ARRAY,BYTOFF,BYTOFF0,ILUN
+	STORE_ARRAY,TTYPE,STRUPCASE(STRTRIM(TTYPE0,2)),ILUN
+	STORE_ARRAY,IDLTYPE,IDLTYPE0,ILUN
+	STORE_ARRAY,FORMAT,FORMAT0,ILUN
+	STORE_ARRAY,N_ELEM,N_ELEM0,ILUN
+	STORE_ARRAY,TSCAL,TSCAL0,ILUN
+	STORE_ARRAY,TZERO,TZERO0,ILUN
+	STORE_ARRAY,MAXVAL,MAXVAL0,ILUN
+	STORE_ARRAY,N_DIMS,LONARR(9,N_ELEMENTS(N_ELEM0)),ILUN
+;
+;  If not a variable length array, then get the dimensions associated with each
+;  column from the TDIMn keywords.  If not found, then assume to be the number
+;  of elements.
+;
+	FXBFIND,HEADER,'TDIM',COLUMNS,TDIMS,N_FOUND,''
+	FOR ICOL = 0,TFIELDS[ILUN]-1 DO IF MAXVAL[ICOL,ILUN] EQ 0 THEN BEGIN
+		TDIM = TDIMS[ICOL]
+		TDIM_USED = (TDIM NE '') AND (NOT KEYWORD_SET(NO_TDIM))
+		IF TDIM_USED THEN DIMS = FIX(FXBTDIM(TDIM))	$
+			     ELSE DIMS = N_ELEM[ICOL,ILUN]
+		DIMS = [N_ELEMENTS(DIMS),DIMS]
+;
+;  If the datatype is a bit array, then no dimensions are applied to the data.
+;
+		IF FORMAT[ICOL,ILUN] EQ 'X' THEN DIMS = [1,N_ELEM[ICOL,ILUN]]
+		N_DIMS[0,ICOL,ILUN] = DIMS
+;
+;  For those columns which are character strings, then the number of
+;  characters, N_CHAR, is the first dimension, and the number of elements is
+;  actually N_ELEM/N_CHAR.
+;
+		IF IDLTYPE[ICOL,ILUN] EQ 7 THEN		$
+			N_ELEM[ICOL,ILUN] = N_ELEM[ICOL,ILUN] / DIMS[1]
+	ENDIF
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbread.pro b/Code/script_idl_mv/astrolib/fxbread.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a482f0e7a4953133d1836db7224a22eb9bc129fa
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbread.pro
@@ -0,0 +1,388 @@
+	PRO FXBREAD, UNIT, DATA, COL, ROW, NOSCALE=NOSCALE, VIRTUAL=VIR, $
+		DIMENSIONS=DIMS0, NANVALUE=NANVALUE, ERRMSG=ERRMSG, $
+                NOIEEE=NOIEEE
+;+
+; NAME: 
+;	FXBREAD
+; Purpose     : 
+;	Read a data array from a disk FITS binary table file.
+; Explanation : 
+;	Each call to FXBREAD will read the data from one column and one row
+;	from the FITS data file, which should already have been opened by
+;	FXBOPEN.  One needs to call this routine for every column and every row
+;	in the binary table.  FXBCLOSE will then close the FITS data file.
+; Use         : 
+;	FXBREAD, UNIT, DATA, COL  [, ROW ]
+; Inputs      : 
+;	UNIT	= Logical unit number corresponding to the file containing the
+;		  binary table.
+;	COL	= Column in the binary table to read data from, either as a
+;		  character string containing a column label (TTYPE), or as a
+;		  numerical column index starting from column one.
+; Opt. Inputs : 
+;	ROW	= Either row number in the binary table to read data from,
+;		  starting from row one, or a two element array containing a
+;		  range of row numbers to read.  If not passed, then the entire
+;		  column is read in.
+;
+;		  Row must be passed for variable length arrays.
+;
+; Outputs     : 
+;	DATA	= IDL data array to be read from the file.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	NOSCALE	= If set, then the output data will not be scaled using the
+;		  optional TSCAL and TZERO keywords in the FITS header.
+;		  Default is to scale.
+;       NOIEEE  = If set, then the output data is not byte-swapped to 
+;                 machine order.  NOIEEE implies NOSCALE.
+;                 Default is to perform the byte-swap.
+;	VIRTUAL	= If set, and COL is passed as a name rather than a number,
+;		  then if the program can't find a column with that name, it
+;		  will then look for a keyword with that name in the header.
+;		  Such a keyword would then act as a "virtual column", with the
+;		  same value for every row.
+;	DIMENSIONS = Vector array containing the dimensions to be used to read
+;		  in the data.  Bypasses any dimensioning information stored in
+;		  the header.  Ignored for bit arrays.  If the data type is
+;		  double-precision complex, then an extra dimension of 2 is
+;		  prepended to the dimensions passed by the user.
+;	NANVALUE= Value signalling data dropout.  All points corresponding to
+;		  IEEE NaN (not-a-number) are converted to this number.
+;		  Ignored unless DATA is of type float, double-precision or
+;		  complex.
+;	ERRMSG	= If defined and passed, then any error messages will be
+;		  returned to the user in this parameter rather than
+;		  depending on the MESSAGE routine in IDL.  If no errors are
+;		  encountered, then a null string is returned.  In order to
+;		  use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBREAD, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	FXPAR, WHERE_NEGZERO, WHERENAN
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	The binary table file must have been opened with FXBOPEN.
+;
+;	The data must be consistent with the column definition in the binary
+;	table header.
+;
+;	The row number must be consistent with the number of rows stored in the
+;	binary table header.
+;
+;	The number of elements implied by the dimensions keyword must not
+;	exceed the number of elements stored in the file.
+;
+; Side effects: 
+;	If the DIMENSIONS keyword is used, then the number of data points read
+;	in may be less than the number of points stored in the table.
+;
+;	If there are no elements to read in (the number of elements is zero),
+;	then the program sets !ERR to -1, and DATA is unmodified.
+;
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, Jan 1992.
+;	W. Thompson, Feb 1992, modified to support variable length arrays.
+;	W. Thompson, Jun 1992, modified way that row ranges are read in.  No
+;			       longer works reiteratively.
+;	W. Thompson, Jun 1992, fixed bug where NANVALUE would be modified by
+;			       TSCAL and TZERO keywords.
+;	W. Thompson, Jun 1992, fixed bug when reading character strings.
+;			       Treats dimensions better when reading multiple
+;			       rows.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 30 June 1993.
+;		Added overwrite keyword to REFORM call to speed up.
+;	Version 3, William Thompson, GSFC, 21 July 1993.
+;		Fixed bug with variable length arrays.
+;	Version 4, William Thompson, GSFC, 29 October 1993.
+;		Added error message for not finding column by name.
+;	Version 5, William Thompson, GSFC, 31 May 1994
+;		Added ERRMSG keyword.
+;       Version 6, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;	Version 7, William Thompson, GSFC, 29 December 1994
+;		Fixed bug where single element dimensions were lost.
+;	Version 8, William Thompson, GSFC, 20 March 1995
+;		Fixed bug introduced in version 7.
+;	Version 9, Wayne Landsman, GSFC, 3 July 1996
+;		Fixed bug involving use of virtual keyword.
+;	Version 10, William Thompson, GSFC, 31-Jan-1997
+;		Added call to WHERE_NEGZERO.
+;	Version 11, Wayne Landsman, GSFC, 12 Aug, 1997
+;		Use IDL dcomplex datatype if needed
+;	Version 12, Wayne Landmsan, GSFC, 20 Feb, 1998
+;		Remove call to WHERE_NEGZERO (now part of IEEE_TO_HOST)
+;	Version 13, 18 Nov 1999, CM, Add NOIEEE keyword
+;	Version 14, 21 Aug 2000, William Thompson, GSFC
+;		Catch I/O errors
+;       Version 15, W. Landsman GSFC 10 Dec 2009
+;                Fix Dimension keyword, remove  IEEE_TO_HOST
+;       Version 16, William Thompson, 18-May-2016, change POINTER to ULONG
+; Version     :
+;       Version 16, 18-May-2016
+;-
+;
+@fxbintable
+	ON_ERROR, 2
+	ON_IOERROR, HANDLE_IO_ERROR
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() LT 3 THEN BEGIN
+		MESSAGE = 'Syntax:  FXBREAD, UNIT, DATA, COL  [, ROW ]'
+		GOTO, HANDLE_ERROR
+	ENDIF
+;
+;  Find the logical unit number in the FXBINTABLE common block.
+;
+	ILUN = WHERE(LUN EQ UNIT,NLUN)
+	ILUN = ILUN[0]
+	IF NLUN EQ 0 THEN BEGIN
+		MESSAGE = 'Unit ' + STRTRIM(UNIT,2) + ' not opened properly'
+		GOTO, HANDLE_ERROR
+	ENDIF
+;
+;  If COL is of type string, then search for a column with that label.
+;
+	SC = SIZE(COL)
+	VIRTUAL = 0
+	IF SC[SC[0]+1] EQ 7 THEN BEGIN
+		SCOL = STRUPCASE(STRTRIM(COL,2))
+		ICOL = WHERE(TTYPE[*,ILUN] EQ SCOL, NCOL)
+		ICOL = ICOL[0]
+		IF (ICOL LT 0) AND (NOT KEYWORD_SET(VIR)) THEN BEGIN
+			MESSAGE = 'Column "' + SCOL + '" not found'
+			GOTO, HANDLE_ERROR
+		ENDIF
+;
+;  If the column was not found, and VIRTUAL was set, then search for a keyword
+;  by that name.
+;
+		IF NCOL EQ 0 THEN BEGIN
+			IF KEYWORD_SET(VIR) THEN BEGIN
+				HEADER = HEAD[*,ILUN]
+				VALUE = FXPAR(HEADER,SCOL,COUNT=CC)
+				IF CC GT 0 THEN BEGIN
+					DATA = VALUE
+					VIRTUAL = 1
+					GOTO, CHECK_ROW
+				ENDIF
+			ENDIF
+			MESSAGE = 'Column "' + SCOL + '" not found'
+			GOTO, HANDLE_ERROR
+		ENDIF
+;
+;  Otherwise, a numerical column was passed.  Check its value.
+;
+	END ELSE ICOL = LONG(COL) - 1
+	IF (ICOL LT 0) OR (ICOL GE TFIELDS[ILUN]) THEN BEGIN
+		MESSAGE = 'COL must be between 1 and ' +	$
+			STRTRIM(TFIELDS[ILUN],2)
+		GOTO, HANDLE_ERROR
+	ENDIF
+;
+;  If there are no elements in the array, then set !ERR to -1.
+;
+	IF N_ELEM[ICOL,ILUN] EQ 0 THEN BEGIN
+		MESSAGE,'Number of elements to read in is zero',/INFORMATIONAL
+		!ERR = -1
+		RETURN
+	ENDIF
+;
+;  If ROW was not passed, then set it equal to the entire range.  Otherwise,
+;  extract the range.
+;
+CHECK_ROW:
+	IF N_PARAMS() EQ 3 THEN ROW = [1,NAXIS2[ILUN]]
+	CASE N_ELEMENTS(ROW) OF
+		1:  ROW2 = LONG(ROW[0])
+		2:  ROW2 = LONG(ROW[1])
+		ELSE:  BEGIN
+			MESSAGE = 'ROW must have one or two elements'
+			GOTO, HANDLE_ERROR
+			END
+	ENDCASE
+	ROW1 = LONG(ROW[0])
+;
+;  If ROW represents a range, then make sure that the row range is legal, and
+;  that reading row ranges is allowed (i.e., the column is not variable length.
+;
+	IF ROW1 NE ROW2 THEN BEGIN
+		MAXROW = NAXIS2[ILUN]
+		IF (ROW1 LT 1) OR (ROW1 GT MAXROW) THEN BEGIN
+			MESSAGE = 'ROW[0] must be between 1 and ' +	$
+				STRTRIM(MAXROW,2)
+			GOTO, HANDLE_ERROR
+		END ELSE IF (ROW2 LT ROW1) OR (ROW2 GT MAXROW) THEN BEGIN
+			MESSAGE = 'ROW[1] must be between ' +	$
+				STRTRIM(ROW1,2) + ' and ' + STRTRIM(MAXROW,2)
+			GOTO, HANDLE_ERROR
+		END ELSE IF NOT VIRTUAL THEN IF MAXVAL[ICOL,ILUN] GT 0 THEN $
+				BEGIN
+			MESSAGE = 'Row ranges not allowed for ' +	$
+				'variable-length columns'
+			GOTO, HANDLE_ERROR
+		ENDIF
+;
+;  Otherwise, if ROW is a single number, then just make sure it's valid.
+;
+	END ELSE BEGIN
+		IF (ROW1 LT 1) OR (ROW1 GT NAXIS2[ILUN]) THEN BEGIN
+			MESSAGE = 'ROW must be between 1 and ' +	$
+				STRTRIM(NAXIS2[ILUN],2)
+			GOTO, HANDLE_ERROR
+		ENDIF
+	ENDELSE
+;
+;  If a virtual column, then simply return the value.  If necessary, then
+;  replicate the value the correct number of times.
+;
+	IF VIRTUAL THEN BEGIN
+		IF ROW1 EQ ROW2 THEN DATA = VALUE ELSE	$
+			DATA = REPLICATE(VALUE,ROW2-ROW1+1)
+		RETURN
+	ENDIF
+;
+;  Find the position of the first byte of the data array in the file.
+;
+	OFFSET = NHEADER[ILUN] + NAXIS1[ILUN]*(ROW1-1) + BYTOFF[ICOL,ILUN]
+	POINT_LUN,UNIT,OFFSET
+;
+;  If a variable length array, then read in the number of elements, and the
+;  pointer to the variable length array.  Change the pointing.
+;
+	IF MAXVAL[ICOL,ILUN] GT 0 THEN BEGIN
+		POINTER = ULONARR(2)
+		READU,UNIT,POINTER
+		BYTEORDER, POINTER, /NTOHL
+		DIMS = POINTER[0]
+		POINT_LUN,UNIT,NHEADER[ILUN] + HEAP[ILUN] + POINTER[1]
+;
+;  If there are no elements in the array, then set !ERR to -1.
+;
+		IF DIMS EQ 0 THEN BEGIN
+			MESSAGE,'Number of elements to read in is zero', $
+				/INFORMATIONAL
+			!ERR = -1
+			RETURN
+		ENDIF
+;
+;  If the datatype is a bit array, then the array is treated as a byte array
+;  with 1/8 the number of elements.
+;
+		IF FORMAT[ICOL,ILUN] EQ 'X' THEN DIMS = LONG((DIMS+7)/8)
+;
+;  If fixed length, then get the dimensions of the output array.
+;
+	END ELSE BEGIN
+		DIMS = N_DIMS[*,ICOL,ILUN]
+		DIMS = DIMS[1:DIMS[0]]
+	ENDELSE
+;
+;  If the DIMENSIONS keyword has been passed, then use that instead of the
+;  dimensions already determined.
+;
+	IF (N_ELEMENTS(DIMS0) GT 0) AND (FORMAT[ICOL,ILUN] NE 'X')	$
+			THEN BEGIN
+		IF PRODUCT(DIMS0) GT PRODUCT(DIMS) THEN BEGIN
+			MESSAGE = 'Requested dimensions exceeds the ' +	$
+				'number of elements'
+			GOTO, HANDLE_ERROR
+		ENDIF
+		DIMS = DIMS0
+	ENDIF
+;
+;  Read in the data.  If a character string array, then read in a byte array.
+;
+	DATATYPE = IDLTYPE[ICOL,ILUN]
+	IF DATATYPE EQ 7 THEN DATATYPE = 1
+;
+;  If only reading in a single row, then the pointer should already be set.
+;  Otherwise, the pointer needs to be set for each row.
+;
+	IF ROW1 EQ ROW2 THEN BEGIN
+		DATA = MAKE_ARRAY(TYPE=DATATYPE,DIMENSION=DIMS)
+		DATA = REFORM(DATA,DIMS,/OVERWRITE)
+		READU,UNIT,DATA
+	END ELSE BEGIN
+		DIMS2 = [DIMS, ROW2-ROW1+1]
+		DATA = MAKE_ARRAY(TYPE=DATATYPE, DIMENSION=DIMS2)
+		DATA = REFORM(DATA, DIMS2, /OVERWRITE)
+		TEMPDATA = MAKE_ARRAY(TYPE=DATATYPE, DIMENSION=DIMS)
+		TEMPDATA = REFORM(TEMPDATA, DIMS, /OVERWRITE)
+		NTEMP = N_ELEMENTS(TEMPDATA)
+		FOR IROW = ROW1,ROW2 DO BEGIN
+			OFFSET = NHEADER[ILUN] + BYTOFF[ICOL,ILUN]
+			POINT_LUN,UNIT,OFFSET + NAXIS1[ILUN]*(IROW-1)
+			READU,UNIT,TEMPDATA
+			DATA[(IROW-ROW1)*NTEMP] = TEMPDATA[*]
+		ENDFOR
+	ENDELSE
+;
+;  If a character string array, then convert to type string.
+;
+	IF IDLTYPE[ICOL,ILUN] EQ 7 THEN BEGIN
+		DATA = STRING(DATA)
+		COUNT = 0
+;
+;  Otherwise, if necessary, then convert the data to the native format of the
+;  host machine.  Also, if NANVALUE is passed, then keep track of any IEEE NaN
+;  values.
+;
+	END ELSE IF IDLTYPE[ICOL,ILUN] NE 1 THEN BEGIN
+		IF (N_ELEMENTS(NANVALUE) EQ 1) AND (IDLTYPE[ICOL,ILUN] GE 4) $
+			AND (IDLTYPE[ICOL,ILUN] LE 6) THEN	$
+			W = WHERENAN(DATA,COUNT) ELSE COUNT = 0
+                IF NOT KEYWORD_SET(NOIEEE) THEN $
+		       SWAP_ENDIAN_INPLACE,DATA,/SWAP_IF_LITTLE
+	END ELSE COUNT = 0
+;
+;  If DIMS is simply the number 1, then convert DATA either to a scalar or to a
+;  simple vector, depending on how many rows were read in.
+;
+	IF (N_ELEMENTS(DIMS) EQ 1) AND (DIMS[0] EQ 1) THEN BEGIN
+		IF N_ELEMENTS(DATA) EQ 1 THEN DATA = DATA[0] ELSE	$
+			DATA = REFORM(DATA,ROW2-ROW1+1,/OVERWRITE)
+	ENDIF
+;
+;  If the parameters TZERO and TSCAL are non-trivial, then adjust the array by
+;  these values.
+;
+	IF NOT KEYWORD_SET(NOSCALE) AND NOT KEYWORD_SET(NOIEEE) THEN BEGIN
+		BZERO  = TZERO[ICOL,ILUN]
+		BSCALE = TSCAL[ICOL,ILUN]
+		IF (BSCALE NE 0) AND (BSCALE NE 1) THEN DATA *= BSCALE
+		IF BZERO NE 0 THEN DATA += BZERO
+	ENDIF
+;
+;  Store NANVALUE everywhere where the data corresponded to IEE NaN.
+;
+	IF COUNT GT 0 THEN DATA[W] = NANVALUE
+;
+        IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+;
+;  I/O error handling point.
+;
+HANDLE_IO_ERROR:
+	MESSAGE = 'I/O error reading file'
+;
+;  Error handling point.
+;
+HANDLE_ERROR:
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = MESSAGE ELSE MESSAGE, MESSAGE
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbreadm.pro b/Code/script_idl_mv/astrolib/fxbreadm.pro
new file mode 100644
index 0000000000000000000000000000000000000000..cf70a8dacf1c94a43c8acf686cee9059b01cb111
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbreadm.pro
@@ -0,0 +1,905 @@
+;+
+; NAME:
+;       FXBREADM
+; PURPOSE: 
+;       Read multiple columns/rows from a disk FITS binary table file.
+; EXPLANATION : 
+;       A call to FXBREADM will read data from multiple rows and
+;       multiple columns in a single procedure call.  Up to forty-nine
+;       columns may be read in a single pass; the number of rows is
+;       limited essentially by available memory.  The file should have
+;       already been opened with FXBOPEN.  FXBREADM optimizes reading
+;       multiple columns by first reading a large chunk of data from
+;       the FITS file directly, and then slicing the data into columns
+;       within memory.  FXBREADM can read variable-length arrays (see
+;       below).
+;
+;       The number of columns is limited to 49 if data are passed by
+;       positional argument.  However, this limitation can be overcome
+;       by having FXBREADM return the data in an array of pointers.
+;       The user should set the PASS_METHOD keyword to 'POINTER', and an 
+;       array of pointers to the data will be returned in the POINTERS keyword.
+;       The  user is responsible for freeing the pointers; however,
+;       FXBREADM will reuse any pointers  passed into the procedure, and 
+;       hence any pointed-to data will be destroyed.
+;
+;       FXBREADM can also read variable-length columns from FITS
+;       binary tables.  Since such data is not of a fixed size, it is
+;       returned as a structure.  The structure has the following
+;       elements:
+;
+;              VARICOL:    ;; Flag: variable length column (= 1)
+;              N_ELEMENTS: ;; Total number of elements returned
+;              TYPE:       ;; IDL data type code (integer)
+;              N_ROWS:     ;; Number of rows read from table (integer)
+;              INDICES:    ;; Indices of each row's data (integer array)
+;              DATA:       ;; Raw data elements (variable type array)
+;
+;       In order to gain access to the Ith row's data, one should
+;       examine DATA(INDICES(I):INDICES(I+1)-1), which is similar in
+;       construct to the REVERSE_INDICES keyword of the HISTOGRAM
+;       function.
+;
+; CALLING SEQUENCE: 
+;       FXBREADM, UNIT, COL, DATA1, [ DATA2, ... DATA48, ROW=, BUFFERSIZE = ]
+;           /NOIEEE, /NOSCALE, /VIRTUAL, NANVALUE=, PASS_METHOD = POINTERS=, 
+;           ERRMSG = , WARNMSG = , STATUS = , /DEFAULT_FLOAT]
+;
+; INPUT PARAMETERS : 
+;       UNIT    = Logical unit number corresponding to the file containing the
+;                 binary table.
+;       COL     = An array of columns in the binary table to read data
+;                 from, either as character strings containing column
+;                 labels (TTYPE), or as numerical column indices
+;                 starting from column one.
+; Outputs     : 
+;       DATA1, DATA2...DATA48 = A named variable to accept the data values, one
+;                 for each column.  The columns are stored in order of the
+;                 list in COL.  If the read operation fails for a
+;                 particular column, then the corresponding output Dn
+;                 variable is not altered.  See the STATUS keyword.
+;                 Ignored if PASS_METHOD is 'POINTER'.
+;
+; OPTIONAL INPUT KEYWORDS: 
+;       ROW     = Either row number in the binary table to read data from,
+;                 starting from row one, or a two element array containing a
+;                 range of row numbers to read.  If not passed, then the entire
+;                 column is read in.
+;       /DEFAULT_FLOAT = If set, then scaling with TSCAL/TZERO is done with
+;                 floating point rather than double precision.
+;       /NOIEEE = If set, then then IEEE floating point data will not
+;                be converted to the host floating point format (and
+;                this by definition implies NOSCALE).  The user is
+;                responsible for their own floating point conversion.
+;       /NOSCALE = If set, then the output data will not be scaled using the
+;                 optional TSCAL and TZERO keywords in the FITS header.
+;                 Default is to scale.
+;       VIRTUAL = If set, and COL is passed as a name rather than a number,
+;                 then if the program can't find a column with that name, it
+;                 will then look for a keyword with that name in the header.
+;                 Such a keyword would then act as a "virtual column", with the
+;                 same value for every row.
+;       DIMENSIONS = FXBREADM ignores this keyword.  It is here for
+;	          compatibility only.
+;       NANVALUE= Value signalling data dropout.  All points corresponding to
+;                 IEEE NaN (not-a-number) are converted to this number.
+;                 Ignored unless DATA is of type float, double-precision or
+;                 complex.
+;       PASS_METHOD = A scalar string indicating method of passing
+;                 data from FXBREADM.  Either 'ARGUMENT' (indicating
+;                 pass by positional argument), or 'POINTER' (indicating
+;                 passing an array of pointers by the POINTERS
+;                 keyword).
+;                 Default: 'ARGUMENT'
+;       POINTERS = If PASS_METHOD is 'POINTER' then an array of IDL
+;                 pointers is returned in this keyword, one for each
+;                 requested column.    Any pointers passed into FXBREADM will 
+;                 have their pointed-to data destroyed.  Ultimately the
+;                 user is responsible for deallocating pointers. 
+;       BUFFERSIZE = Raw data are transferred from the file in chunks
+;                 to conserve memory.  This is the size in bytes of
+;                 each chunk.  If a value of zero is given, then all
+;                 of the data are transferred in one pass.  Default is
+;                 32768 (32 kB).
+; OPTIONAL OUTPUT KEYWORDS:
+;       ERRMSG  = If defined and passed, then any error messages will be
+;                 returned to the user in this parameter rather than
+;                 depending on the MESSAGE routine in IDL.  If no errors are
+;                 encountered, then a null string is returned.  In order to
+;                 use this feature, ERRMSG must be defined first, e.g.
+;
+;                       ERRMSG = ''
+;                       FXBREAD, ERRMSG=ERRMSG, ...
+;                       IF ERRMSG NE '' THEN ...
+;       WARNMSG = Messages which are considered to be non-fatal
+;                 "warnings" are returned in this output string.
+;                 Note that if some but not all columns are
+;                 unreadable, this is considered to be non-fatal.
+;       STATUS  = An output array containing the status for each
+;                 column read, 1 meaning success and 0 meaning failure.
+;
+; Calls       : 
+;       FXPAR(), WHERENAN()
+; Common      : 
+;       Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;       information.
+; Restrictions: 
+;       The binary table file must have been opened with FXBOPEN.
+;
+;       The data must be consistent with the column definition in the binary
+;       table header.
+;
+;       The row number must be consistent with the number of rows stored in the
+;       binary table header.
+;
+;       Generally speaking, FXBREADM will be faster than iterative
+;       calls to FXBREAD when (a) a large number of columns is to be
+;       read or (b) the size in bytes of each cell is small, so that
+;       the overhead of the FOR loop in FXBREAD becomes significant.
+;
+; SIDE EFFECTS: 
+;       If there are no elements to read in (the number of elements is zero),
+;       then the program sets !ERR to -1, and DATA is unmodified.
+;
+; Category    : 
+;       Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;       C. Markwardt, based in concept on FXBREAD version 12 from
+;                              IDLASTRO, but with significant and
+;                              major changes to accommodate the
+;                              multiple row/column technique.  Mostly
+;                              the parameter checking and general data
+;                              flow remain.
+;       C. Markwardt, updated to read variable length arrays, and to
+;                              pass columns by handle or pointer.
+;                              20 Jun 2001
+;       C. Markwardt, try to conserve memory when creating the arrays
+;                              13 Oct 2001
+;   Handle case of GE 50 columns, C. Markwardt, 18 Apr 2002
+;   Handle case where TSCAL/TZERO changes type of column,
+;       C. Markwardt, 23 Feb 2003
+;   Fix bug in handling of FOUND and numeric columns, 
+;       C. Markwardt 12 May 2003
+;   Removed pre-V5.0 HANDLE options  W. Landsman July 2004
+;   Fix bug when HANDLE options were removed, July 2004
+;   Handle special cases of TSCAL/TZERO which emulate unsigned
+;      integers, Oct 2003
+;   Add DEFAULT_FLOAT keyword to select float values instead of double
+;      for TSCAL'ed, June 2004
+;   Read 64bit integer columns, E. Hivon, Mar 2008
+;   Add support for columns with TNULLn keywords, C. Markwardt, Apr 2010
+;   Add support for files larger than 2 GB, C. Markwardt, 2012-04-17
+;   Use V6 notation, remove IEEE_TO_HOST  W. Landsman Mar 2014
+;
+;-
+;
+
+
+;; This is a utility routine which converts the data from raw bytes to
+;; IDL variables.
+PRO FXBREADM_CONV, BB, DD, CTYPE, PERROW, NROWS, $
+                   NOIEEE=NOIEEE, NOSCALE=NOSCALE, VARICOL=VARICOL, $
+                   NANVALUE=NANVALUE, TZERO=TZERO, TSCAL=TSCAL, $
+                   TNULL_VALUE=TNULL, TNULL_FLAG=TNULLQ, $
+                   DEFAULT_FLOAT=DF
+
+  COMMON FXBREADM_CONV_COMMON, DTYPENAMES
+  IF N_ELEMENTS(DTYPENAMES) EQ 0 THEN $
+    DTYPENAMES = [ '__BAD', 'BYTE', 'FIX', 'LONG', $
+                   'FLOAT', 'DOUBLE', 'COMPLEX', 'STRING', $
+                   '__BAD', 'DCOMPLEX', '__BAD', '__BAD', '__BAD', '__BAD', 'LONG64' ]
+  
+  TYPENAME = DTYPENAMES[CTYPE]
+
+  IF CTYPE EQ 7 THEN BEGIN
+      DD = STRING(TEMPORARY(BB))
+  ENDIF ELSE BEGIN
+      DD = CALL_FUNCTION(TYPENAME, TEMPORARY(BB), 0, PERROW*NROWS)
+  ENDELSE
+  IF N_ELEMENTS(DD) EQ 1 THEN DD = [DD]
+  DD = REFORM(DD, PERROW, NROWS, /OVERWRITE)
+
+  ;; Now perform any type-specific conversions, etc.
+  COUNT = 0L
+  CASE 1 OF
+      ;; Integer types
+      (CTYPE EQ 2 || CTYPE EQ 3 || ctype eq 14): BEGIN
+          IF ~KEYWORD_SET(NOIEEE) || KEYWORD_SET(VARICOL) THEN $
+            SWAP_ENDIAN_INPLACE, DD, /SWAP_IF_LITTLE 
+          ;; Check for TNULL values
+          ;; We will convert to NAN values later (or if the user
+          ;; requested a different value we will use that)
+          IF KEYWORD_SET(TNULLQ) THEN BEGIN
+              W = WHERE(DD EQ TNULL,COUNT)
+              IF N_ELEMENTS(NANVALUE) EQ 0 THEN NANVALUE = !VALUES.D_NAN
+          ENDIF
+      END
+
+      ;; Floating and complex types
+      (CTYPE GE 4 || CTYPE LE 6 || CTYPE EQ 9): BEGIN
+          IF ~KEYWORD_SET(NOIEEE) THEN BEGIN
+              IF N_ELEMENTS(NANVALUE) GT 0 THEN W=WHERENAN(DD,COUNT)
+              SWAP_ENDIAN_INPLACE, DD, /SWAP_IF_LITTLE
+          ENDIF
+      END
+
+      ;; String types (CTYPE EQ 7) have already been converted
+      ;; in the above CALL_FUNCTION.  No further conversion
+      ;; is necessary here.
+  ENDCASE
+
+;
+;  If the parameters TZERO and TSCAL are non-trivial, then adjust the array by
+;  these values.
+;
+  IF ((~KEYWORD_SET(NOIEEE) && ~KEYWORD_SET(NOSCALE)) && $
+      (~KEYWORD_SET(VARICOL)) && $
+      (N_ELEMENTS(TZERO) EQ 1 && N_ELEMENTS(TSCAL) EQ 1)) THEN BEGIN
+
+      IF KEYWORD_SET(DF) THEN BEGIN
+          ;; Default to float
+          TSCAL = FLOAT(TSCAL)
+          TZERO = FLOAT(TZERO)
+      ENDIF
+
+      IF CTYPE EQ 2 AND TSCAL[0] EQ 1 AND TZERO[0] EQ 32768 THEN BEGIN
+          ;; SPECIAL CASE: Unsigned 16-bit integer
+          DD = UINT(DD) - UINT(32768)
+      ENDIF ELSE IF CTYPE EQ 3 AND TSCAL[0] EQ 1 AND $
+        TZERO[0] EQ 2147483648D THEN BEGIN
+          ;; SPECIAL CASE: Unsigned 32-bit integer
+          DD = ULONG(DD) - ULONG(2147483648)
+      ENDIF ELSE BEGIN
+          IF (TSCAL[0] NE 0) && (TSCAL[0] NE 1) THEN DD = TSCAL[0]*DD
+          IF TZERO[0] NE 0 THEN DD = DD + TZERO[0]
+      ENDELSE
+  ENDIF
+
+;
+;  Store NANVALUE everywhere where the data corresponded to IEEE NaN.
+;
+  IF COUNT GT 0 && N_ELEMENTS(NANVALUE) GT 0 THEN DD[W] = NANVALUE
+  
+END
+
+PRO FXBREADM, UNIT, COL, $
+              D0,  D1,  D2,  D3,  D4,  D5,  D6,  D7,  D8,  D9, $
+              D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, $
+              D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, $
+              D30, D31, D32, D33, D34, D35, D36, D37, D38, D39, $
+              D40, D41, D42, D43, D44, D45, D46, D47, $
+              ROW=ROW, VIRTUAL=VIR, DIMENSIONS=DIM, $
+              NOSCALE=NOSCALE, NOIEEE=NOIEEE, DEFAULT_FLOAT=DEFAULT_FLOAT, $
+              PASS_METHOD=PASS_METHOD, POINTERS=POINTERS, $
+              NANVALUE=NANVALUE, BUFFERSIZE=BUFFERSIZE, $
+              ERRMSG=ERRMSG, WARNMSG=WARNMSG, STATUS=OUTSTATUS
+
+@fxbintable
+        ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() LT 2 THEN BEGIN
+                MESSAGE = 'Syntax:  FXBREADM, UNIT, COL, D0, D1, ... [, ROW= ]'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+        IF N_ELEMENTS(BUFFERSIZE) EQ 0 THEN BUFFERSIZE = 32768L
+
+;
+;  COL may be one of several descriptors:
+;     * a list of column numbers, beginning with 1
+;     * a list of column names
+;
+        MYCOL = [ COL ]    ; Make sure it is an array
+
+        SC = SIZE(MYCOL)
+        NUMCOLS = N_ELEMENTS(MYCOL)
+        OUTSTATUS = LONARR(NUMCOLS)
+        COLNAMES = 'D'+STRTRIM(LINDGEN(NUMCOLS),2)
+
+;
+;  Determine whether the data is to be extracted as pointers or arguments
+;
+        IF N_ELEMENTS(PASS_METHOD) EQ 0 THEN PASS_METHOD = 'ARGUMENT'
+        PASS = STRUPCASE(STRTRIM(PASS_METHOD[0],2))
+        IF PASS NE 'ARGUMENT' AND PASS NE 'POINTER' THEN BEGIN
+            MESSAGE = 'ERROR: PASS_METHOD must be ARGUMENT or POINTER'
+            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ERRMSG = MESSAGE
+                RETURN
+            END ELSE MESSAGE, MESSAGE
+        ENDIF
+
+        NP = N_ELEMENTS(POINTERS)
+        IF PASS EQ 'POINTER' THEN BEGIN
+            IF NP EQ 0 THEN POINTERS = PTRARR(NUMCOLS, /ALLOCATE_HEAP)
+            NP = N_ELEMENTS(POINTERS)
+            SZ = SIZE(POINTERS)
+            IF SZ[SZ[0]+1] NE 10 THEN BEGIN
+                MESSAGE = 'ERROR: POINTERS must be an array of pointers'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF
+
+;
+;  Expand the pointer array if necessary
+;
+            IF NP LT NUMCOLS THEN $
+              POINTERS = [POINTERS[*], PTRARR(NUMCOLS-NP, /ALLOCATE_HEAP)]
+            NP = N_ELEMENTS(POINTERS)
+
+;
+;  Make sure there are no null pointers, which cannot be assigned to.
+;
+            WH = WHERE(PTR_VALID(POINTERS) EQ 0, CT)
+            IF CT GT 0 THEN POINTERS[WH] = PTRARR(CT, /ALLOCATE_HEAP)
+                
+        ENDIF
+
+
+;
+;  Find the logical unit number in the FXBINTABLE common block.
+;
+        ILUN = WHERE(LUN EQ UNIT,NLUN)
+        ILUN = ILUN[0]
+        IF NLUN EQ 0 THEN BEGIN
+                MESSAGE = 'Unit ' + STRTRIM(UNIT,2) +   $
+                        ' not opened properly'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+
+;
+;  Check the number of columns.  It should be fewer than 49
+;
+        IF PASS EQ 'ARGUMENT' THEN BEGIN
+            IF NUMCOLS GT 49 THEN BEGIN
+                MESSAGE = 'Maximum of 49 columns exceeded'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                    ERRMSG = MESSAGE
+                    RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF
+            IF N_PARAMS()-2 LT NUMCOLS AND N_ELEMENTS(ERRMSG) EQ 0 THEN BEGIN
+                MESSAGE, 'WARNING: number of data parameters less than columns', $
+                  /INFO
+            ENDIF
+        ENDIF
+            
+        ICOL    = LONARR(NUMCOLS)
+        VIRTUAL = BYTARR(NUMCOLS)
+        VIRTYPE = LONARR(NUMCOLS)
+        FOUND   = BYTARR(NUMCOLS)
+        VARICOL = BYTARR(NUMCOLS)
+        NOTFOUND = ''
+        NNOTFOUND = 0L
+        IF N_ELEMENTS(WARNMSG) NE 0 THEN WARNMSG = ''
+
+;
+;  If COL is of type string, then search for a column with that label.
+;
+        IF SC[SC[0]+1] EQ 7 THEN BEGIN
+            MYCOL = STRUPCASE(STRTRIM(MYCOL,2))
+            FOR I = 0, NUMCOLS-1 DO BEGIN
+                XCOL = WHERE(TTYPE[*,ILUN] EQ MYCOL[I], NCOL)
+                ICOL[I] = XCOL[0]
+;
+;  If the column was not found, and VIRTUAL was set, then search for a keyword
+;  by that name.
+;
+                IF NCOL GT 0 THEN FOUND[I] = 1
+                IF NOT FOUND[I] AND KEYWORD_SET(VIR) THEN BEGIN
+                    HEADER = HEAD[*,ILUN]
+                    VALUE = FXPAR(HEADER,MYCOL[I], Count = N_VALUE)
+                    IF N_VALUE GE 0 THEN BEGIN
+                        RESULT = EXECUTE(COLNAMES[I]+' = VALUE')
+                        SV = SIZE(VALUE)
+                        VIRTYPE[I] = SV[SV[0]+1]
+                        VIRTUAL[I] = 1
+                        FOUND[I] = 1
+                    ENDIF
+                ENDIF ELSE IF ~FOUND[I] THEN BEGIN
+                    IF NOTFOUND EQ '' THEN NOTFOUND = MYCOL[I] $
+                    ELSE NOTFOUND = NOTFOUND +', ' + MYCOL[I]
+                    NNOTFOUND++
+                ENDIF
+
+            ENDFOR
+
+            IF NNOTFOUND EQ NUMCOLS THEN BEGIN
+                MESSAGE = 'ERROR: None of the requested columns were found'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                    ERRMSG = MESSAGE
+                    RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF ELSE IF NNOTFOUND GT 0 THEN BEGIN
+                MESSAGE = 'WARNING: Columns ' + NOTFOUND + ' were not found'
+                IF N_ELEMENTS(WARNMSG) NE 0 THEN WARNMSG = MESSAGE $
+                ELSE MESSAGE, MESSAGE, /INFO
+            ENDIF
+                
+;
+;  Otherwise, a numerical column was passed.  Check its value.
+;
+        ENDIF ELSE BEGIN
+            ICOL[*] = LONG(MYCOL) - 1
+            FOUND[*] = 1
+        ENDELSE
+
+;  Step through each column index
+        MESSAGE = ''
+        FOR I = 0, NUMCOLS-1 DO BEGIN
+            IF ~FOUND[I] THEN GOTO, LOOP_END_COLCHECK
+            IF VIRTUAL[I] THEN GOTO, LOOP_END_COLCHECK
+
+            IF (ICOL[I] LT 0) OR (ICOL[I] GE TFIELDS[ILUN]) THEN BEGIN
+                MESSAGE = MESSAGE + '; COL "'+STRTRIM(MYCOL[I],2)+$
+                  '" must be between 1 and ' +  $
+                  STRTRIM(TFIELDS[ILUN],2)
+                FOUND[I] = 0
+            ENDIF
+;
+;  If there are no elements in the array, then set !ERR to -1.
+;
+            IF FOUND[I] AND N_ELEM[ICOL[I],ILUN] EQ 0 THEN BEGIN
+                FOUND[I] = 0
+                MESSAGE = MESSAGE + '; Number of elements to read in "'+$
+                  STRTRIM(MYCOL[I],2)+'" is zero'
+;                !ERR = -1
+;                RETURN
+            ENDIF
+
+;
+;  Flag variable-length columns
+;
+            IF MAXVAL[ICOL[I],ILUN] GT 0 THEN BEGIN
+                FOUND[I] = 1
+                VARICOL[I] = 1
+            ENDIF
+
+            LOOP_END_COLCHECK:
+
+        ENDFOR
+
+;
+;  Check to be sure that there are columns to be read
+;
+        W  = WHERE(FOUND EQ 1, COUNT)
+        WV = WHERE(FOUND EQ 1 OR VARICOL EQ 1, WVCOUNT)
+        IF WVCOUNT EQ 0 THEN BEGIN
+            STRPUT, MESSAGE, ':', 0
+            MESSAGE = 'ERROR: No requested columns could be read'+MESSAGE
+            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ERRMSG = MESSAGE
+                RETURN
+            END ELSE MESSAGE, MESSAGE
+        ENDIF ELSE IF MESSAGE NE '' THEN BEGIN
+            STRPUT, MESSAGE, ':', 0
+            MESSAGE = 'WARNING: Some columns could not be read'+MESSAGE
+            IF N_ELEMENTS(WARNMSG) NE 0 THEN WARNMSG = MESSAGE $
+            ELSE MESSAGE, MESSAGE, /INFO
+        ENDIF
+            
+;
+;  If ROW was not passed, then set it equal to the entire range.  Otherwise,
+;  extract the range.
+;
+        IF N_ELEMENTS(ROW) EQ 0 THEN ROW = [1LL, NAXIS2[ILUN]]
+        CASE N_ELEMENTS(ROW) OF
+                1:  ROW2 = LONG64(ROW[0])
+                2:  ROW2 = LONG64(ROW[1])
+                ELSE:  BEGIN
+                        MESSAGE = 'ROW must have one or two elements'
+                        IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                                ERRMSG = MESSAGE
+                                RETURN
+                        END ELSE MESSAGE, MESSAGE
+                        END
+        ENDCASE
+        ROW1 = LONG64(ROW[0])
+;
+;  If ROW represents a range, then make sure that the row range is legal, and
+;  that reading row ranges is allowed (i.e., the column is not variable length.
+;
+        IF ROW1 NE ROW2 THEN BEGIN
+                MAXROW = NAXIS2[ILUN]
+                IF (ROW1 LT 1) OR (ROW1 GT MAXROW) THEN BEGIN
+                        MESSAGE = 'ROW[0] must be between 1 and ' +     $
+                                STRTRIM(MAXROW,2)
+                        IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                                ERRMSG = MESSAGE
+                                RETURN
+                        END ELSE MESSAGE, MESSAGE
+                END ELSE IF (ROW2 LT ROW1) OR (ROW2 GT MAXROW) THEN BEGIN
+                        MESSAGE = 'ROW[1] must be between ' +   $
+                                STRTRIM(ROW1,2) + ' and ' + STRTRIM(MAXROW,2)
+                        IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                                ERRMSG = MESSAGE
+                                RETURN
+                        END ELSE MESSAGE, MESSAGE
+                ENDIF
+;
+;  Otherwise, if ROW is a single number, then just make sure it's valid.
+;
+        END ELSE BEGIN
+                IF (ROW1 LT 1) OR (ROW1 GT NAXIS2[ILUN]) THEN BEGIN
+                        MESSAGE = 'ROW must be between 1 and ' +        $
+                                STRTRIM(NAXIS2[ILUN],2)
+                        IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                                ERRMSG = MESSAGE
+                                RETURN
+                        END ELSE MESSAGE, MESSAGE
+                ENDIF
+        ENDELSE
+
+;
+;  Compose information about the output
+;
+        HEADER = HEAD[*,ILUN]
+        COLNDIM = LONARR(NUMCOLS)
+        COLDIM  = LONARR(NUMCOLS, 20) ;; Maximum of 20 dimensions in output
+        COLTYPE = LONARR(NUMCOLS)
+        BOFF1   = LONARR(NUMCOLS)
+        BOFF2   = LONARR(NUMCOLS)
+        TNULL_FLG = INTARR(NUMCOLS) ;; 1 if TNULLn column is present
+        TNULL_VAL = DBLARR(NUMCOLS) ;; value of TNULLn column if present
+        NROWS = ROW2-ROW1+1
+        FOR I = 0L, NUMCOLS-1 DO BEGIN
+
+            IF ~FOUND[I] THEN GOTO, LOOP_END_DIMS
+            ;;  Data type of the input.
+            IF VIRTUAL[I] THEN BEGIN
+                ; Virtual column: read from keyword itself
+                COLTYPE[I] = VIRTYPE[I] 
+                GOTO, LOOP_END_DIMS
+            ENDIF ELSE IF VARICOL[I] THEN BEGIN
+                ; Variable length column: 2-element long
+                COLTYPE[I] = 3
+                DIMS = [1L, 2L]
+            ENDIF ELSE BEGIN
+                COLTYPE[I] = IDLTYPE[ICOL[I],ILUN]
+                DIMS = N_DIMS[*,ICOL[I],ILUN]
+            ENDELSE
+            
+            NDIMS = DIMS[0]
+            DIMS  = DIMS[1:NDIMS]
+
+            IF NDIMS EQ 1 AND DIMS[0] EQ 1 THEN BEGIN
+
+                ;; Case of only one output element, try to return a
+                ;; scalar.  Otherwise, it is a vector equal to the
+                ;; number of rows to be read
+
+                COLNDIM[I] = 1L
+                COLDIM[I,0] = NROWS
+            ENDIF ELSE BEGIN
+
+                COLNDIM[I] = NDIMS
+                COLDIM[I,0:(NDIMS-1)] = DIMS
+                IF NROWS GT 1 THEN BEGIN
+                    COLDIM[I,NDIMS] = NROWS
+                    COLNDIM[I]++
+                ENDIF
+
+            ENDELSE
+            
+            ;; For strings, the number of characters is the first
+            ;; dimension.  This information is useless to us now,
+            ;; since the STRING() type cast which will appear below
+            ;; handles the array conversion automatically.
+            IF COLTYPE[I] EQ 7 THEN BEGIN
+                IF COLNDIM[I] GT 1 THEN BEGIN
+                    COLDIM[I,0:COLNDIM[I]-2] = COLDIM[I,1:COLNDIM[I]-1]
+                    COLDIM[I,COLNDIM[I]-1]   = 0
+                    COLNDIM[I] = COLNDIM[I] - 1
+                ENDIF ELSE BEGIN  ;; Case of a single row
+                    COLNDIM[I] = 1L
+                    COLDIM[I,0] = NROWS
+                ENDELSE
+            ENDIF
+
+            ;; Byte offsets
+            BOFF1[I] = BYTOFF[ICOL[I],ILUN]
+            IF ICOL[I] EQ TFIELDS[ILUN]-1 THEN $
+              BOFF2[I] = NAXIS1[ILUN]-1 $
+            ELSE $
+              BOFF2[I] = BYTOFF[ICOL[I]+1,ILUN]-1
+
+            ;; TNULLn keywords for integer type columns
+            IF (COLTYPE[I] GE 1 AND COLTYPE[I] LE 3) OR $
+              (COLTYPE[I] GE 12 AND COLTYPE[I] LE 15) THEN BEGIN
+                TNULLn = 'TNULL'+STRTRIM(ICOL[I]+1,2)
+                VALUE = FXPAR(HEADER,TNULLn, Count = N_VALUE)
+                IF N_VALUE GT 0 THEN BEGIN
+                    TNULL_FLG[I] = 1
+                    TNULL_VAL[I] = VALUE
+                ENDIF
+            ENDIF
+            
+            LOOP_END_DIMS:
+
+        ENDFOR
+
+;
+;  Construct any virtual columns first
+;
+        WC = WHERE(FOUND EQ 1 AND VIRTUAL EQ 1, WCCOUNT)
+        FOR I = 0L, WCCOUNT-1 DO BEGIN
+            ;; If it's virtual, then the value only needs to be
+            ;; replicated
+            EXTCMD = COLNAMES[WC[I]]+'= REPLICATE(D'+COLNAMES[WC[I]]+',NROWS)'
+            ;; Run the command that selects the data
+            RESULT = EXECUTE(EXTCMD)
+            IF RESULT EQ 0 THEN BEGIN
+                MESSAGE = 'ERROR: Could not extract data (column '+$
+                  STRTRIM(MYCOL[WC[I]],2)+')'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                    ERRMSG = MESSAGE
+                    RETURN
+                ENDIF ELSE MESSAGE, MESSAGE
+            ENDIF
+            OUTSTATUS[I] = 1
+        ENDFOR
+
+
+;  Skip to processing variable-length columns if all other columns are virtual
+        WC = WHERE(FOUND EQ 1 AND VIRTUAL EQ 0, WCCOUNT)
+        IF WCCOUNT EQ 0 THEN GOTO, PROC_CLEANUP
+
+;  Create NANVALUES, the template to use when a NAN is found
+        IF N_ELEMENTS(NANVALUE) GE NUMCOLS THEN BEGIN
+            NANVALUES = NANVALUE[0:NUMCOLS-1]
+        ENDIF ELSE IF N_ELEMENTS(NANVALUE) GT 0 THEN BEGIN
+            NANVALUES = REPLICATE(NANVALUE[0], NUMCOLS)
+            NANVALUES[0] = NANVALUE
+            I = N_ELEMENTS(NANVALUE)
+            IF I LT NUMCOLS THEN $
+              NANVALUES[I:*] = NANVALUE[0]
+        ENDIF
+
+;
+;  Find the position of the first byte of the data array in the file.
+;
+        OFFSET0 = NHEADER[ILUN] + NAXIS1[ILUN]*(ROW1-1LL)
+        POS = 0LL
+        NROWS0 = NROWS
+        J = 0LL
+        FIRST = 1
+        ;; Here, we constrain the buffer to be at least 16 rows long.
+        ;; If we fill up 32 kB with fewer than 16 rows, then there
+        ;; must be a lot of (big) columns in this table.  It's
+        ;; probably a candidate for using FXBREAD instead.
+        BUFFROWS = LONG((BUFFERSIZE/NAXIS1[ILUN]) > 16L)
+        IF BUFFERSIZE LE 0 THEN BUFFROWS = NROWS0
+
+;
+;  Loop through the data in chunks
+;
+        WHILE NROWS GT 0 DO BEGIN
+        J++
+        NR  = NROWS < BUFFROWS
+        OFFSET1 = NAXIS1[ILUN]*POS
+
+;
+;  Proceed by reading a byte array from the input data file
+;  FXBREADM reads all columns from the specified rows, and
+;  sorts out the details of which bytes belong to which columns
+;  in the next FOR loop.
+;
+        BB = BYTARR(NAXIS1[ILUN], NR)
+        POINT_LUN, UNIT, OFFSET0+OFFSET1
+        READU, UNIT, BB
+;        FXGSEEK, UNIT, OFFSET0+OFFSET1
+;        FXGREAD, UNIT, BB
+
+;
+;  Now select out the desired columns
+;
+        FOR I = 0, NUMCOLS-1 DO BEGIN
+           
+            ;; Extract the proper rows and columns
+            IF ~FOUND[I] THEN GOTO, LOOP_END_STORE
+            IF VIRTUAL[I]   THEN GOTO, LOOP_END_STORE
+
+            ;; Extract the data from the byte array and convert it
+            ;; The inner CALL_FUNCTION is to one of the coercion
+            ;; functions, such as FIX(), DOUBLE(), STRING(), etc.,
+            ;; which is called with an offset to force a conversion
+            ;; from bytes to the data type.
+            ;; The outer CALL_FUNCTION is to REFORM(), which makes
+            ;; sure that the data structure is correct.
+            ;;
+            DIMS = COLDIM[I,0:COLNDIM[I]-1]
+            PERROW = ROUND(PRODUCT(DIMS)/NROWS0)
+            
+            IF N_ELEMENTS(NANVALUES) GT 0 THEN $
+              EXTRA={NANVALUE: NANVALUES[I]}
+
+            FXBREADM_CONV, BB[BOFF1[I]:BOFF2[I], *], DD, COLTYPE[I], PERROW, NR,$
+              NOIEEE=KEYWORD_SET(NOIEEE), NOSCALE=KEYWORD_SET(NOSCALE), $
+              TZERO=TZERO[ICOL[I], ILUN], TSCAL=TSCAL[ICOL[I], ILUN], $
+              VARICOL=VARICOL[I], DEFAULT_FLOAT=DEFAULT_FLOAT, $
+              TNULL_VALUE=TNULL_VAL[I], TNULL_FLAG=TNULL_FLG[I], $
+              _EXTRA=EXTRA
+
+            ;; Initialize the output variable on the first chunk
+            IF FIRST THEN BEGIN
+                SZ = SIZE(DD)
+                ;; NOTE: type could have changed if TSCAL/TZERO were used
+                COLTYPEI = SZ(SZ[0]+1)  
+                RESULT = EXECUTE(COLNAMES[I]+' = 0')
+                RESULT = EXECUTE(COLNAMES[I]+' = '+$
+                                 'MAKE_ARRAY(PERROW, NROWS0, TYPE=COLTYPEI)')
+                RESULT = EXECUTE(COLNAMES[I]+' = '+$
+                         'REFORM('+COLNAMES[I]+', PERROW, NROWS0,/OVERWRITE)')
+            ENDIF
+
+            ;; Finally, store this in the output variable
+            RESULT = EXECUTE(COLNAMES[I]+'[0,POS] = DD')
+            DD = 0
+            IF RESULT EQ 0 THEN BEGIN
+                MESSAGE = 'ERROR: Could not compose output data '+COLNAMES[I]
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                    ERRMSG = MESSAGE
+                    RETURN
+                ENDIF ELSE MESSAGE, MESSAGE
+            ENDIF
+
+            OUTSTATUS[I] = 1
+
+            LOOP_END_STORE:
+        ENDFOR
+
+        FIRST = 0
+        NROWS = NROWS - NR
+        POS   = POS + NR
+        ENDWHILE
+
+;
+;  Read the variable-length columns from the heap.  Adjacent data are
+;  coalesced into one read operation.  Note: this technique is thus
+;  optimal for extensions with only one variable-length column.  If
+;  there are more than one then coalescence will not occur.
+;
+
+        ;; Width of the various data types in bytes
+        WIDARR = [0L, 1L, 2L, 4L, 4L, 8L, 8L, 1L, 0L,16L, 0L]
+        WV = WHERE(OUTSTATUS EQ 1 AND VARICOL EQ 1, WVCOUNT)
+        FOR J = 0, WVCOUNT-1 DO BEGIN
+            I = WV[J]
+            RESULT = EXECUTE('PDATA = '+COLNAMES[I])
+            NVALS = PDATA[0,*]          ;; Number of values in each row
+            NTOT  = ROUND(TOTAL(NVALS)) ;; Total number of values
+            IF NTOT EQ 0 THEN BEGIN
+                DD = {N_ELEMENTS: 0L, N_ROWS: NROWS0, $
+                      INDICES: LON64ARR(NROWS0+1), DATA: 0L}
+                GOTO, FILL_VARICOL
+            ENDIF
+
+            ;; Compute the width in bytes of the data value
+            TYPE = IDLTYPE[ICOL[I], ILUN]
+            WID = LONG64(WIDARR[TYPE < 10])
+            IF WID EQ 0 THEN BEGIN
+                OUTSTATUS[I] = 0
+                MESSAGE = 'ERROR: Column '+COLNAMES[I]+' has unknown data type'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF
+
+            ;; Coalesce the data pointers
+            BOFF1 = LONG64(PDATA[1,*])
+            BOFF2 = BOFF1 + NVALS*WID
+            WH = WHERE(BOFF1[1:*] NE BOFF2, CT)
+            IF CT GT 0 THEN BI = [-1LL, WH, N_ELEMENTS(BOFF1)-1] $
+            ELSE            BI = [-1LL,     N_ELEMENTS(BOFF1)-1]
+            CT = CT + 1
+
+            ;; Create the output array
+            BC = BOFF2[BI[1:*]] - BOFF1[BI[0:CT-1]+1] ;; Byte count
+            NB = ROUND(TOTAL(BC))                     ;; Total # bytes
+            BB = BYTARR(NB)                           ;; Byte array
+
+            ;; Initialize the counter variables used in the read-loop
+            CC = 0LL & CC1 = 0LL & K = 0LL
+            BUFFROWS = ROUND(BUFFERSIZE/WID) > 128L
+            BASE = LONG64(NHEADER[ILUN]+HEAP[ILUN])
+
+            ;; Read data from file
+            WHILE CC LT NB DO BEGIN
+                NB1 = (BC[K]-CC1) < BUFFROWS
+                BB1 = BYTARR(NB1)
+
+                POINT_LUN, UNIT, BASE+BOFF1[BI[K]+1]+CC1
+                READU, UNIT, BB1
+;                FXGSEEK, UNIT, BASE+BOFF1[BI[K]+1]+CC1
+;                FXGREAD, UNIT, BB1
+                BB[CC] = TEMPORARY(BB1)
+
+                CC  = CC  + NB1
+                CC1 = CC1 + NB1
+                IF CC1 EQ BC[K] THEN BEGIN
+                    K = K + 1
+                    CC1 = 0L
+                ENDIF
+            ENDWHILE
+
+            ;; Convert the data
+            IF N_ELEMENTS(NANVALUES) GT 0 THEN $
+              EXTRA={NANVALUE: NANVALUES[I]}
+
+            FXBREADM_CONV, BB, DD, TYPE, NTOT, 1L, $
+              NOIEEE=KEYWORD_SET(NOIEEE), NOSCALE=KEYWORD_SET(NOSCALE), $
+              TZERO=TZERO[ICOL[I], ILUN], TSCAL=TSCAL[ICOL[I], ILUN], $
+              DEFAULT_FLOAT=DEFAULT_FLOAT, _EXTRA=EXTRA
+            
+            ;; Ensure the correct dimensions, now that we know them
+            COLNDIM[I] = 1
+            COLDIM[I,0] = NTOT
+            
+            ;; Construct the indices; unfortunately we need to make an
+            ;; accumulant with a FOR loop
+            INDICES = LON64ARR(NROWS0+1)
+            FOR K = 1LL, NROWS0 DO $
+              INDICES[K] = INDICES[K-1] + NVALS[K-1]
+
+            ;; Construct a structure with additional data
+            DD = {N_ELEMENTS: NTOT, N_ROWS: NROWS0, TYPE: TYPE, $
+                  INDICES: INDICES, DATA: TEMPORARY(DD)}
+
+            FILL_VARICOL:
+            RESULT = EXECUTE(COLNAMES[I] +' = TEMPORARY(DD)')
+        ENDFOR
+
+;
+;  Compose the output columns, which might need reforming
+;
+        FOR I = 0, NUMCOLS-1 DO BEGIN
+            IF OUTSTATUS[I] NE 1 THEN GOTO, LOOP_END_FINAL
+
+            ;; Extract the dimensions and name of the column data
+            DIMS = COLDIM[I,0:COLNDIM[I]-1]
+            NEL  = PRODUCT(DIMS)
+            CNAME = COLNAMES[I]
+            IF VARICOL[I] THEN CNAME = CNAME + '.DATA'
+
+            ;; Compose the reforming part
+            IF NEL EQ 1 THEN $
+              CMD = CNAME+'[0]' $
+            ELSE $
+              CMD = 'REFORM(TEMPORARY('+CNAME+'),DIMS,/OVERWRITE)'
+
+            ;; Variable-length columns return extra information
+            IF VARICOL[I] THEN BEGIN
+                CMD = ('{VARICOL:    1,'+$
+                       ' N_ELEMENTS: '+COLNAMES[I]+'.N_ELEMENTS, '+$
+                       ' TYPE:       '+COLNAMES[I]+'.TYPE, '+$
+                       ' N_ROWS:     '+COLNAMES[I]+'.N_ROWS, '+$
+                       ' INDICES:    '+COLNAMES[I]+'.INDICES, '+$
+                       ' DATA:       '+CMD+'}')
+            ENDIF
+
+            ;; Assign to pointer, or re-assign to column
+            IF PASS EQ 'ARGUMENT' THEN $
+              CMD = COLNAMES[I]+' = ' + CMD $
+            ELSE IF PASS EQ 'POINTER' THEN $
+              CMD = '*(POINTERS[I]) = ' + CMD 
+
+            RESULT = EXECUTE(CMD)
+            LOOP_END_FINAL:
+        ENDFOR
+
+        PROC_CLEANUP:
+;
+        IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+        RETURN
+        
+        END
diff --git a/Code/script_idl_mv/astrolib/fxbstate.pro b/Code/script_idl_mv/astrolib/fxbstate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b2de4693f0d8e483e2a8cf1dd99dd70d15963efd
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbstate.pro
@@ -0,0 +1,74 @@
+        FUNCTION FXBSTATE, UNIT
+;+
+; NAME: 
+;      FXBSTATE()
+;
+; PURPOSE:
+;       Returns the state of a FITS binary table.
+;
+; Explanation : This procedure returns the state of a FITS binary table that
+;               was either opened for read with the command FXBOPEN, or for
+;               write with the command FXBCREATE.
+;
+; Use         : Result = FXBSTATE(UNIT)
+;
+; Inputs      : UNIT    = Logical unit number returned by FXBOPEN routine.
+;                         Must be a scalar integer.
+;
+; Opt. Inputs : None.
+;
+; Outputs     : The result of the function is the state of the FITS binary
+;               table that UNIT points to.  This can be one of three values:
+;
+;                       0 = Closed
+;                       1 = Open for read
+;                       2 = Open for write
+;
+; Opt. Outputs: None.
+;
+; Keywords    : None.
+;
+; Calls       : FXBFINDLUN
+;
+; Common      : Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;               information.
+;
+; Restrictions: None.
+;
+; Side effects: If UNIT is an undefined variable, then 0 (closed) is returned.
+;
+; Category    : Data Handling, I/O, FITS, Generic.
+;
+; Prev. Hist. : None.
+;
+; Written     : William Thompson, GSFC, 1 July 1993.
+;
+; Modified    : Version 1, William Thompson, GSFC, 1 July 1993.
+;
+; Version     : Version 1, 1 July 1993.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+@fxbintable
+        ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() NE 1 THEN MESSAGE,'Syntax:  Result = FXBSTATE(UNIT)'
+;
+;  If UNIT is undefined, then return False.
+;
+        IF N_ELEMENTS(UNIT) EQ 0 THEN RETURN, 0
+;
+;  Check the validity of UNIT.
+;
+        IF N_ELEMENTS(UNIT) GT 1 THEN MESSAGE,'UNIT must be a scalar'
+        SZ = SIZE(UNIT)
+        IF SZ[SZ[0]+1] GT 3 THEN MESSAGE,'UNIT must be an integer'
+;
+;  Get the state associated with UNIT.
+;
+        ILUN = FXBFINDLUN(UNIT)
+        RETURN, STATE[ILUN]
+;
+        END
diff --git a/Code/script_idl_mv/astrolib/fxbtdim.pro b/Code/script_idl_mv/astrolib/fxbtdim.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3c116e75ce2dab9ea38258fa67ccfde8e63d8b07
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbtdim.pro
@@ -0,0 +1,90 @@
+	FUNCTION FXBTDIM, TDIM_KEYWORD
+;+
+; NAME: 
+;	FXBTDIM()
+; Purpose     : 
+;	Parse TDIM-like kwywords.
+; Explanation : 
+;	Parses the value of a TDIM-like keyword (e.g. TDIMnnn, TDESC, etc.) to
+;	return the separate elements contained within.
+; Use         : 
+;	Result = FXBTDIM( TDIM_KEYWORD )
+; Inputs      : 
+;	TDIM_KEYWORD	= The value of a TDIM-like keyword.  Must be a
+;			  character string of the form "(value1,value2,...)".
+;			  If the parentheses characters are missing, then the
+;			  string is simply returned as is, without any further
+;			  processing.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	The result of the function is a character string array containing the
+;	values contained within the keyword parameter.  If a numerical result
+;	is desired, then simply call, e.g.
+;
+;		Result = FIX( FXBTDIM( TDIM_KEYWORD ))
+;
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	None.
+; Calls       : 
+;	GETTOK
+; Common      : 
+;	None.
+; Restrictions: 
+;	The input parameter must have the proper format.  The separate values
+;	must not contain the comma character.  TDIM_KEYWORD must not be an
+;	array.
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Jan. 1992.
+;	William Thompson, Jan. 1993, renamed to be compatible with DOS
+;		limitations.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+; Version     : 
+;	Version 1, 12 April 1993.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+	ON_ERROR,2
+;
+;  Make sure TDIM_KEYWORD is not an array.
+;
+	IF N_ELEMENTS(TDIM_KEYWORD) NE 1 THEN MESSAGE,	$
+		'TDIM_KEYWORD must be a scalar'
+;
+;  Remove any leading or trailing blanks from the keyword.
+;
+	TDIM = STRTRIM(TDIM_KEYWORD,2)
+;
+;  The first and last characters should be "(" and ")".  If they are not, then
+;  simply return the string as is.
+;
+	FIRST = STRMID(TDIM,0,1)
+	LAST  = STRMID(TDIM,STRLEN(TDIM)-1,1)
+	IF (FIRST NE "(") OR (LAST NE ")") THEN RETURN,TDIM
+;
+;  Otherwise, remove the parentheses characters.
+;
+	TDIM = STRMID(TDIM,1,STRLEN(TDIM)-2)
+;
+;  Get the first value.
+;
+	VALUE = GETTOK(TDIM,',')
+;
+;  Get all the rest of the values.
+;
+	WHILE TDIM NE '' DO VALUE = [VALUE,GETTOK(TDIM,',')]
+;
+;  Return the (string) array of values.
+;
+	RETURN,VALUE
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbtform.pro b/Code/script_idl_mv/astrolib/fxbtform.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c0e05d537c79ed136b6823c50786f8fedb2ca315
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbtform.pro
@@ -0,0 +1,212 @@
+	PRO FXBTFORM,HEADER,TBCOL,IDLTYPE,FORMAT,NUMVAL,MAXVAL,ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXBTFORM
+; PURPOSE     : 
+;	Returns information about FITS binary table columns.
+; EXPLANATION : 
+;	Procedure to return information about the format of the various columns
+;	in a FITS binary table.
+; Use         : 
+;	FXBTFORM,HEADER,TBCOL,IDLTYPE,FORMAT,NUMVAL,MAXVAL
+; Inputs      : 
+;	HEADER	= Fits binary table header.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	TBCOL	= Array of starting column positions in bytes.
+;	IDLTYPE	= IDL data types of columns.
+;	FORMAT	= Character code defining the data types of the columns.
+;	NUMVAL	= Number of elements of the data arrays in the columns.
+;	MAXVAL	= Maximum number of elements for columns containing variable
+;		  length arrays, or zero otherwise.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	ERRMSG	  = If defined and passed, then any error messages will be
+;		    returned to the user in this parameter rather than
+;		    depending on the MESSAGE routine in IDL.  If no errors are
+;		    encountered, then a null string is returned.  In order to
+;		    use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBTFORM, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	FXPAR
+; Common      : 
+;	None.
+; Restrictions: 
+;	None.
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, Feb. 1992, from TBINFO by D. Lindler.
+;	W. Thompson, Jan. 1993, renamed to be compatible with DOS limitations.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 June 1994
+;		Added ERRMSG keyword.
+;       Version 3, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;	Version 4, William Thompson, GSFC, 9 April 1997
+;		Modified so that variable length arrays can be read, even if
+;		the maximum array size is not in the header.
+;	Version 5  Wayne Landsman, GSFC, August 1997
+;		Recognize double complex array type if since IDL version 4.0
+;       Version 6  Optimized FXPAR call, CM 1999 Nov 18
+;       Version 7: Wayne Landsman, GSFC Feb 2006
+;               Added support for 64bit integer K format
+; Version:
+;       Version 8: Wayne Landsman GSFC Apr 2010
+;               Remove use of obsolete !ERR variable
+;-
+;
+	ON_ERROR,2
+        COMPILE_OPT IDL2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() LT 1 THEN BEGIN
+		MESSAGE = 'Syntax:  FXBTFORM,HEADER,TBCOL,IDLTYPE,FORMAT,' + $
+			'NUMVAL,MAXVAL'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Get the number of fields.
+;
+	TFIELDS = FXPAR(HEADER,'TFIELDS', START=0L, COUNT=N_TFIELDS)
+	IF N_TFIELDS LE 0 THEN BEGIN
+		MESSAGE = 'Invalid FITS header -- keyword TFIELDS is missing'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	END ELSE IF TFIELDS EQ 0 THEN BEGIN
+		MESSAGE = 'FIT binary table has no columns'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Initialize the arrays.
+;
+	WIDTH	= INTARR(TFIELDS)
+	IDLTYPE	= INTARR(TFIELDS)
+	TBCOL	= LONARR(TFIELDS)
+	FORMAT	= STRARR(TFIELDS)
+	NUMVAL	= LONARR(TFIELDS)
+	MAXVAL	= LONARR(TFIELDS)
+;
+;  Get the column formats.
+;
+	TFORM = FXPAR(HEADER,'TFORM*', COUNT=N_TFORM)
+	IF N_TFORM LE 0 THEN BEGIN
+		MESSAGE = 'Invalid FITS table header -- keyword TFORM ' + $
+			'not present'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+	TFORM =  STRUPCASE(STRTRIM(TFORM,2))
+;
+;  Parse the values of the TFORM keywords.
+;
+	LEN = STRLEN(TFORM)
+	FOR I = 0,N_ELEMENTS(TFORM)-1 DO BEGIN
+;
+;  Step through each character in the format, until a non-numerical character
+;  is encountered.
+;
+		ICHAR = 0
+NEXT_CHAR:
+		IF ICHAR GE LEN[I] THEN BEGIN
+			MESSAGE = 'Invalid format specification for ' +	$
+				'keyword TFORM ' + STRTRIM(I+1)
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+		CHAR = STRUPCASE(STRMID(TFORM[I],ICHAR,1))
+		IF ((CHAR GE '0') AND (CHAR LE '9')) THEN BEGIN
+			ICHAR = ICHAR + 1
+			GOTO, NEXT_CHAR
+		ENDIF
+;
+;  Get the number of elements.
+;
+		IF ICHAR EQ 0 THEN NUMVAL[I] = 1 ELSE	$
+			NUMVAL[I] = LONG(STRMID(TFORM[I],0,ICHAR))
+;
+;  If the character is "P" then the next character is the actual data type,
+;  followed by the maximum number of elements surrounded by quotes.
+;
+		IF CHAR EQ "P" THEN BEGIN
+			CHAR = STRUPCASE(STRMID(TFORM[I],ICHAR+1,1))
+			MAXVAL[I] = LONG(STRMID(TFORM[I],ICHAR+3,	$
+				LEN[I]-ICHAR-4))
+			IF MAXVAL[I] EQ 0 THEN MAXVAL[I] = 1
+		ENDIF
+;
+;  Get the IDL data type, and the size of an element.
+;
+		FORMAT[I] = CHAR
+		CASE CHAR OF
+			'L':  BEGIN & IDLTYPE[I] = 1 & WIDTH[I] = 1 & END
+			'A':  BEGIN & IDLTYPE[I] = 7 & WIDTH[I] = 1 & END
+			'B':  BEGIN & IDLTYPE[I] = 1 & WIDTH[I] = 1 & END
+			'I':  BEGIN & IDLTYPE[I] = 2 & WIDTH[I] = 2 & END
+			'J':  BEGIN & IDLTYPE[I] = 3 & WIDTH[I] = 4 & END
+			'E':  BEGIN & IDLTYPE[I] = 4 & WIDTH[I] = 4 & END
+			'D':  BEGIN & IDLTYPE[I] = 5 & WIDTH[I] = 8 & END
+			'C':  BEGIN & IDLTYPE[I] = 6 & WIDTH[I] = 8 & END
+			'M':  BEGIN & IDLTYPE[I] = 9 & WIDTH[I] =16 & END 
+			'K':  BEGIN & IDLTYPE[I] =14 & WIDTH[I] = 8 & END 
+;
+;
+;  Treat bit arrays as byte arrays with 1/8 the number of elements.
+;
+			'X':  BEGIN
+				IDLTYPE[I] = 1
+				WIDTH[I] = 1
+				IF MAXVAL[I] GT 0 THEN BEGIN
+					MAXVAL[I] = LONG((MAXVAL[I]+7)/8)
+				END ELSE BEGIN
+					NUMVAL[I] = LONG((NUMVAL[I]+7)/8)
+				ENDELSE
+				END
+
+			ELSE:  BEGIN
+				MESSAGE = 'Invalid format specification ' + $
+					'for keyword TFORM' + STRTRIM(I+1,2)
+				IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+					ERRMSG = MESSAGE
+					RETURN
+				END ELSE MESSAGE, MESSAGE
+				END
+		ENDCASE
+;
+;  Variable length array pointers always take up eight bytes.
+;
+		IF MAXVAL[I] GT 0 THEN WIDTH[I] = 8
+;
+;  Calculate the starting byte for each column.
+;
+		IF I GE 1 THEN TBCOL[I] = TBCOL[I-1] + WIDTH[I-1]*NUMVAL[I-1]
+	ENDFOR
+;
+        IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbwrite.pro b/Code/script_idl_mv/astrolib/fxbwrite.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0c0f4539703345765db9c0930d2b9bd82344e66e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbwrite.pro
@@ -0,0 +1,282 @@
+	PRO FXBWRITE, UNIT, DATA, COL, ROW, BIT=BIT, NANVALUE=NANVALUE,	$
+		ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXBWRITE
+; Purpose     : 
+;	Write a binary data array to a disk FITS binary table file.
+; Explanation : 
+;	Each call to FXBWRITE will write to the data file, which should already
+;	have been created and opened by FXBCREATE.  One needs to call this
+;	routine for every column and every row in the binary table.  FXBFINISH
+;	will then close the file.
+; Use         : 
+;	FXBWRITE, UNIT, DATA, COL, ROW
+; Inputs      : 
+;	UNIT	= Logical unit number corresponding to the file containing the
+;		  binary table.
+;	DATA	= IDL data array to be written to the file.
+;	COL	= Column in the binary table to place data in, starting from
+;		  column one.
+;	ROW	= Row in the binary table to place data in, starting from row
+;		  one.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	None.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	BIT	= Number of bits in bit mask arrays (type "X").  Only used if
+;		  the column is of variable size.
+;	NANVALUE= Value signalling data dropout.  All points corresponding to
+;		  this value are set to be IEEE NaN (not-a-number).  Ignored
+;		  unless DATA is of type float, double-precision or complex.
+;	ERRMSG	= If defined and passed, then any error messages will be
+;		  returned to the user in this parameter rather than
+;		  depending on the MESSAGE routine in IDL.  If no errors are
+;		  encountered, then a null string is returned.  In order to
+;		  use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBWRITE, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	None.
+; Common      : 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; Restrictions: 
+;	The binary table file must have been opened with FXBCREATE.
+;
+;	The data must be consistent with the column definition in the binary
+;	table header.
+;
+;	The row number must be consistent with the number of rows stored in the
+;	binary table header.
+;
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, Jan 1992, based on WRITEFITS by J. Woffard and W. Landsman.
+;	W. Thompson, Feb 1992, modified to support variable length arrays.
+;	W. Thompson, Feb 1992, removed all references to temporary files.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 July 1993.
+;		Fixed bug with variable length arrays.
+;	Version 3, William Thompson, GSFC, 31 May 1994
+;		Added ERRMSG keyword.
+;       Version 4, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;	Version 5, Wayne Landsman, GSFC, 12 Aug 1997
+;		Recognize IDL double complex data type
+;	Version 6, Converted to IDL V5.0   W. Landsman   September 1997
+;       Version 7, William Thompson, 18-May-2016, change POINTER to ULONG
+; Version     :
+;       Version 7, 18-May-2016
+;-
+;
+@fxbintable
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() LT 4 THEN BEGIN
+		MESSAGE = 'Syntax:  FXBWRITE, UNIT, DATA, COL, ROW'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Find the logical unit number in the FXBINTABLE common block.
+;
+	ILUN = WHERE(LUN EQ UNIT,NLUN)
+	ILUN = ILUN[0]
+	IF NLUN EQ 0 THEN BEGIN
+		MESSAGE,'Unit ' + STRTRIM(UNIT,2) +	$
+			' not opened properly'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Check the row and column parameters against the header.
+;
+	IF (COL LT 1) OR (COL GT TFIELDS[ILUN]) THEN BEGIN
+		MESSAGE = 'COL must be between 1 and ' +	$
+			STRTRIM(TFIELDS[ILUN],2)
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	END ELSE IF (ROW LT 1) OR (ROW GT NAXIS2[ILUN]) THEN BEGIN
+		MESSAGE = 'ROW must be between 1 and ' +	$
+			STRTRIM(NAXIS2[ILUN],2)
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Calculate the row and column parameters using IDL zero-based indexing.
+;
+	IROW = LONG(ROW) - 1
+	ICOL = LONG(COL) - 1
+;
+;  Check the type of the data against that defined for this column.
+;
+	SZ = SIZE(DATA)
+	TYPE = SZ[SZ[0]+1]
+	IF TYPE NE IDLTYPE[ICOL,ILUN] THEN BEGIN
+		CASE IDLTYPE[ICOL,ILUN] OF
+			1: STYPE = 'byte'
+			2: STYPE = 'short integer'
+			3: STYPE = 'long integer'
+			4: STYPE = 'floating point'
+			5: STYPE = 'double precision'
+			6: STYPE = 'complex'
+			7: STYPE = 'string'
+			9: STYPE = 'double complex'
+		ENDCASE
+		MESSAGE = 'Data type should be ' + STYPE
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Check the number of elements, depending on whether or not the column
+;  contains variable length arrays.
+;
+	IF MAXVAL[ICOL,ILUN] GT 0 THEN BEGIN
+		IF N_ELEMENTS(DATA) GT MAXVAL[ICOL,ILUN] THEN BEGIN
+			MESSAGE = 'Data array should have no more than ' + $
+				STRTRIM(N_ELEM[ICOL,ILUN],2) + ' elements'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+	END ELSE BEGIN
+		IF N_ELEMENTS(DATA) NE N_ELEM[ICOL,ILUN] THEN BEGIN
+			MESSAGE = 'Data array should have ' +	$
+				STRTRIM(N_ELEM[ICOL,ILUN],2) + ' elements'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+	ENDELSE
+;
+;  Find the position of the first byte of the data array in the file.
+;
+	OFFSET = NHEADER[ILUN] + NAXIS1[ILUN]*IROW + BYTOFF[ICOL,ILUN]
+	POINT_LUN,UNIT,OFFSET
+;
+;  If a variable length array, then test to see if the array is of type
+;  double-precision complex (M) or bit (X).
+;
+	IF MAXVAL[ICOL,ILUN] GT 0 THEN BEGIN
+		N_ELEM0 = N_ELEMENTS(DATA)
+		IF FORMAT[ICOL,ILUN] EQ "X" THEN BEGIN
+			IF N_ELEMENTS(BIT) EQ 0 THEN BEGIN
+				MESSAGE = 'Number of bits not defined'
+				IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+					ERRMSG = MESSAGE
+					RETURN
+				END ELSE MESSAGE, MESSAGE
+			END ELSE IF N_ELEMENTS(BIT) NE 1 THEN BEGIN
+				MESSAGE = 'Number of bits must be a scalar'
+				IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+					ERRMSG = MESSAGE
+					RETURN
+				END ELSE MESSAGE, MESSAGE
+			END ELSE IF LONG((BIT+7)/8) NE N_ELEM0 THEN BEGIN
+				MESSAGE = 'Number of bits does not match ' + $
+					'array size'
+				IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+					ERRMSG = MESSAGE
+					RETURN
+				END ELSE MESSAGE, MESSAGE
+			ENDIF
+			N_ELEM0 = BIT
+		ENDIF
+;
+;  Write out the number of elements, and the pointer to the variable length
+;  array.
+;
+		POINTER = ULONARR(2)
+		POINTER[0] = N_ELEM0
+		POINTER[1] = DHEAP[ILUN]
+		SWAP_ENDIAN_INPLACE,POINTER,/SWAP_IF_LITTLE
+		WRITEU,UNIT,POINTER
+		POINT_LUN,UNIT,NHEADER[ILUN] + HEAP[ILUN] + DHEAP[ILUN]
+;
+;  Update the HEAP pointer.
+;
+		CASE TYPE OF
+			1:  DDHEAP = N_ELEMENTS(DATA)		;Byte
+			2:  DDHEAP = N_ELEMENTS(DATA) * 2	;Short integer
+			3:  DDHEAP = N_ELEMENTS(DATA) * 4	;Long integer
+			4:  DDHEAP = N_ELEMENTS(DATA) * 4	;Float
+			5:  DDHEAP = N_ELEMENTS(DATA) * 8	;Double
+			6:  DDHEAP = N_ELEMENTS(DATA) * 8	;Complex
+			7:  DDHEAP = N_ELEMENTS(DATA)		;String
+			9:  DDHEAP = N_ELEMENTS(DATA) * 16      ;Dble Complex
+		ENDCASE
+		DHEAP[ILUN] = DHEAP[ILUN] + DDHEAP
+	ENDIF
+;
+;  If a byte array, then simply write out the data.
+;
+        IF TYPE EQ 1 THEN BEGIN
+		WRITEU,UNIT,DATA
+;
+;  Otherwise, if a character string array, then write out the character strings
+;  with the correct width, truncating or padding with blanks as necessary.
+;  However, if a variable length string array, then simply write it out.
+;
+	END ELSE IF TYPE EQ 7 THEN BEGIN
+		IF MAXVAL[ICOL,ILUN] GT 0 THEN BEGIN
+			WRITEU,UNIT,DATA
+		END ELSE BEGIN
+			N_CHAR = N_DIMS[1,ICOL,ILUN]
+			NEWDATA = REPLICATE(32B,N_CHAR,N_ELEMENTS(DATA))
+			FOR I=0,N_ELEMENTS(DATA)-1 DO	$
+				NEWDATA[0,I] = BYTE(STRMID(DATA[I],0,N_CHAR))
+			WRITEU,UNIT,NEWDATA
+		ENDELSE
+;
+;  Otherwise, if necessary, then byte-swap the data before writing it out.
+;  Also, replace any values corresponding data dropout with IEEE NaN.
+;
+	END ELSE BEGIN
+		IF (N_ELEMENTS(NANVALUE) EQ 1) AND (TYPE GE 4) AND	$
+				((TYPE LE 6) OR (TYPE EQ 9)) THEN BEGIN
+			W = WHERE(DATA EQ NANVALUE, COUNT)
+			CASE TYPE OF
+				4:  NAN = FLOAT(  REPLICATE('FF'XB,4),0,1)
+				5:  NAN = DOUBLE( REPLICATE('FF'XB,8),0,1)
+				6:  NAN = COMPLEX(REPLICATE('FF'XB,8),0,1)
+				9:  NAN = DCOMPLEX(REPLICATE('FF'XB,16),0,1)
+			ENDCASE
+		END ELSE COUNT = 0
+;
+		NEWDATA = DATA
+		SWAP_ENDIAN_INPLACE, NEWDATA, /SWAP_IF_LITTLE
+		IF COUNT GT 0 THEN NEWDATA[W] = NAN
+		WRITEU,UNIT,NEWDATA 
+	ENDELSE
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxbwritm.pro b/Code/script_idl_mv/astrolib/fxbwritm.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a07f508a14237eee7e3a9a2d00d69cf962f44389
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxbwritm.pro
@@ -0,0 +1,713 @@
+	PRO FXBWRITM, UNIT, COL, $
+                      D0,  D1,  D2,  D3,  D4,  D5,  D6,  D7,  D8,  D9, $
+                      D10, D11, D12, D13, D14, D15, D16, D17, D18, D19, $
+                      D20, D21, D22, D23, D24, D25, D26, D27, D28, D29, $
+                      D30, D31, D32, D33, D34, D35, D36, D37, D38, D39, $
+                      D40, D41, D42, D43, D44, D45, D46, D47, D48, D49, $
+                      NOIEEE=NOIEEE, NOSCALE=NOSCALE, $
+                      POINTERS=POINTERS, PASS_METHOD=PASS_METHOD, $
+                      ROW=ROW, NANVALUE=NANVALUE, BUFFERSIZE=BUFFERSIZE, $
+                      ERRMSG=ERRMSG, WARNMSG=WARNMSG, STATUS=OUTSTATUS
+;+
+; NAME: 
+;	FXBWRITM
+; PURPOSE: 
+;       Write multiple columns/rows to a disk FITS binary table file.
+; EXPLANATION : 
+;       A call to FXBWRITM will write multiple rows and multiple
+;       columns to a binary table in a single procedure call.  Up to
+;       fifty columns may be read in a single pass.  The file should
+;       have already been opened with FXBOPEN (with write access) or
+;       FXBCREATE.  FXBWRITM optimizes writing multiple columns by
+;       first writing a large chunk of data to the FITS file all at
+;       once.  FXBWRITM cannot write variable-length arrays; use
+;       FXBWRITE instead.
+;
+;       The number of columns is limited to 50 if data are passed by
+;       positional argument.  However, this limitation can be overcome
+;       by passing pointers to FXBWRITM.  The user should set the PASS_METHOD 
+;       keyword to 'POINTER'  as appropriate, and  an array of pointers to 
+;       the data in the POINTERS keyword.  The user is responsible for freeing 
+;        the pointers.
+;
+; CALLING SEQUENCE: 
+;	FXBWRITM, UNIT, COL, D0, D1, D2, ..., [ ROW= , PASS_METHOD, NANVALUE=
+;                               POINTERS=,  BUFFERSIZE= ]
+;    
+; INPUT PARAMETERS: 
+;	UNIT	= Logical unit number corresponding to the file containing the
+;		  binary table.
+;	D0,..D49= An IDL data array to be written to the file, one for
+;                 each column.      These parameters will be igonred if data
+;                 is passed through the POINTERS keyword.
+;	COL	= Column in the binary table to place data in.   May be either
+;                 a list of column numbers where the first column is one, or 
+;                 a string list of column names.  
+
+; OPTIONAL INPUT KEYWORDS: 
+;	ROW	= Either row number in the binary table to write data to,
+;		  starting from row one, or a two element array containing a
+;		  range of row numbers to write.  If not passed, then
+;		  the entire column is written.
+;	NANVALUE= Value signalling data dropout.  All points corresponding to
+;		  this value are set to be IEEE NaN (not-a-number).  Ignored
+;		  unless DATA is of type float, double-precision or complex.
+;       NOSCALE = If set, then TSCAL/TZERO values are ignored, and data is 
+;                 written exactly as supplied. 
+;       PASS_METHOD = A scalar string indicating method of passing
+;                 data to FXBWRITM.  One of 'ARGUMENT' (indicating
+;                 pass by positional argument),  or'POINTER' (indicating
+;                 passing an array of pointers by the POINTERS
+;                 keyword).  
+;                 Default:  'ARGUMENT'
+;       POINTERS = If PASS_METHOD is 'POINTER' then the user must pass
+;                 an array of IDL pointers to this keyword, one for
+;                 each column.    Ultimately the user is responsible for 
+;                 deallocating pointers.
+;       BUFFERSIZE = Data are transferred in chunks to conserve
+;                 memory.  This is the size in bytes of each chunk.
+;                 If a value of zero is given, then all of the data
+;                 are transferred in one pass.  Default is 32768 (32
+;                 kB).
+; OPTIONAL OUTPUT KEYWORDS:
+;	ERRMSG	= If defined and passed, then any error messages will be
+;		  returned to the user in this parameter rather than
+;		  depending on the MESSAGE routine in IDL.  If no errors are
+;		  encountered, then a null string is returned.  In order to
+;		  use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXBWRITE, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;       WARNMSG = Messages which are considered to be non-fatal
+;                 "warnings" are returned in this  output string.
+;       STATUS  = An output array containing the status for each
+;                 read, 1 meaning success and 0 meaning failure.
+;
+; PROCEDURE CALLS: 
+;      None.
+; EXAMPLE:
+;      Write a binary table 'sample.fits' giving 43 X,Y positions and a 
+;      21 x 21 PSF at each position:
+;
+;      (1) First, create sample values
+;      x = findgen(43) & y = findgen(43)+1 & psf = randomn(seed,21,21,43)
+;      
+;      (2) Create primary header, write it to disk, and make extension header
+;      fxhmake,header,/initialize,/extend,/date
+;      fxwrite,'sample.fits',header
+;      fxbhmake,header,43,'TESTEXT','Test binary table extension'
+;
+;      (3) Fill extension header with desired column names
+;      fxbaddcol,1,header,x[0],'X'             ;Use first element in each array
+;      fxbaddcol,2,header,y[0],'Y'             ;to determine column properties
+;      fxbaddcol,3,header,psf[*,*,0],'PSF'
+;
+;      (4) Write extension header to FITS file
+;      fxbcreate,unit,'sample.fits',header
+;
+;      (5) Use FXBWRITM to write all data to the extension in a single call
+;      fxbwritm,unit,['X','Y','PSF'], x, y, psf
+;      fxbfinish,unit                 ;Close the file
+;
+; COMMON BLOCKS: 
+;	Uses common block FXBINTABLE--see "fxbintable.pro" for more
+;	information.
+; RESTRICTIONS: 
+;	The binary table file must have been opened with FXBCREATE or
+;       FXBOPEN (with write access).
+;
+;	The data must be consistent with the column definition in the binary
+;	table header.
+;
+;	The row number must be consistent with the number of rows stored in the
+;	binary table header.
+;
+;       A PASS_METHOD of POINTER does not use the EXECUTE() statement and can be
+;       used with the IDL Virtual Machine.   However, the EXECUTE() statement is
+;       used when the PASS_METHOD is by arguments.      
+; CATEGORY: 
+;	Data Handling, I/O, FITS, Generic.
+; PREVIOUS HISTORY: 
+;       C. Markwardt, based on FXBWRITE and FXBREADM (ver 1), Jan 1999
+; WRITTEN: 
+;	Craig Markwardt, GSFC, January 1999.
+; MODIFIED:
+;       Version 1, Craig Markwardt, GSFC 18 January 1999.
+;               Documented this routine, 18 January 1999. 
+;       C. Markwardt, added ability to pass by handle or pointer.
+;               Some bug fixes, 20 July 2001  
+;       W. Landsman/B.Schulz  Allow more than 50 arguments when using pointers
+;       W. Landsman  Remove pre-V5.0 HANDLE options      July 2004
+;       W. Landsman Remove EXECUTE() call with POINTERS   May 2005
+;       C. Markwardt Allow the output table to have TSCAL/TZERO
+;          keyword values; if that is the case, then the passed values
+;          will be quantized to match those scale factors before being
+;          written.  Sep 2007
+;       E. Hivon: write 64bit integer and double precision columns, Mar 2008
+;       C. Markwardt Allow unsigned integers, which have special
+;          TSCAL/TZERO values.  Feb 2009
+;       C. Markwardt Add support for files larger than 2 GB, 2012-04-17
+;
+;-
+;
+        compile_opt idl2
+@fxbintable
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() LT 2 THEN BEGIN
+		MESSAGE = 'Syntax:  FXBWRITM, UNIT, COL, DATA1, DATA2, ' $
+                  +' ..., ROW=, POINTERS=, PASS_METHOD=, NANVALUE=, BUFFERSIZE='
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+        IF N_ELEMENTS(BUFFERSIZE) EQ 0 THEN BUFFERSIZE = 32768L
+
+;
+;  COL may be one of several descriptors:
+;     * a list of column numbers, beginning with 1
+;     * a list of column names
+;
+        MYCOL = [ COL ]    ; Make sure it is an array
+
+        SC = SIZE(MYCOL)
+        NUMCOLS = N_ELEMENTS(MYCOL)
+        OUTSTATUS = LONARR(NUMCOLS)
+        COLNAMES = 'D'+STRTRIM(LINDGEN(50),2)
+
+;
+;  Determine whether the data has been passed as arguments or pointers
+;
+        IF N_ELEMENTS(PASS_METHOD) EQ 0 THEN PASS_METHOD = 'ARGUMENT'
+        PASS = STRUPCASE(STRTRIM(PASS_METHOD[0],2))
+        IF PASS NE 'ARGUMENT' AND PASS NE 'POINTER'  THEN BEGIN
+            MESSAGE = 'ERROR: PASS_METHOD must be ARGUMENT or POINTER'
+            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ERRMSG = MESSAGE
+                RETURN
+            END ELSE MESSAGE, MESSAGE
+        ENDIF
+
+        NP = N_ELEMENTS(POINTERS)
+        IF PASS NE 'ARGUMENT' AND NP LT NUMCOLS THEN BEGIN
+            MESSAGE = 'ERROR: POINTERS array contains too few elements'
+            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ERRMSG = MESSAGE
+                RETURN
+            END ELSE MESSAGE, MESSAGE
+        ENDIF
+
+        IF PASS EQ 'POINTER' THEN BEGIN
+            SZ = SIZE(POINTERS)
+            IF SZ[SZ[0]+1] NE 10 THEN BEGIN
+                MESSAGE = 'ERROR: POINTERS must be an array of pointers'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF
+
+            WH = WHERE(PTR_VALID(POINTERS[0:NUMCOLS-1]) EQ 0, CT)
+            IF CT GT 0 THEN BEGIN
+                MESSAGE = 'ERROR: POINTERS contains invalid pointers'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF
+
+        ENDIF
+        
+ 
+;
+;  Find the logical unit number in the FXBINTABLE common block.
+;
+	ILUN = WHERE(LUN EQ UNIT,NLUN)
+	ILUN = ILUN[0]
+	IF NLUN EQ 0 THEN BEGIN
+		MESSAGE = 'Unit ' + STRTRIM(UNIT,2) +	$
+			' not opened properly'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+
+;
+;  Make sure the file was opened for write access.
+;
+	IF STATE[ILUN] NE 2 THEN BEGIN
+		MESSAGE = 'Unit ' + STRTRIM(UNIT,2) +	$
+			' not opened for write access'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+
+;
+;  Check the number of columns.  It should be fewer than 50
+;
+        IF (NUMCOLS GT 50) AND (PASS EQ 'ARGUMENT') THEN BEGIN
+            MESSAGE = 'Maximum of 50 columns exceeded'
+            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ERRMSG = MESSAGE
+                RETURN
+            END ELSE MESSAGE, MESSAGE
+        ENDIF
+;        Commented out because too much data is not a problem
+;        IF NUMCOLS LT N_PARAMS()-2 THEN BEGIN
+;            MESSAGE = 'ERROR: too few data parameters passed'
+;            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+;                ERRMSG = MESSAGE
+;                RETURN
+;            END ELSE MESSAGE, MESSAGE
+;        ENDIF
+
+        ICOL    = LONARR(NUMCOLS)
+        FOUND   = BYTARR(NUMCOLS)
+        NOTFOUND = ''
+        NNOTFOUND = 0L
+        IF N_ELEMENTS(WARNMSG) NE 0 THEN WARNMSG = ''
+
+;
+;  If COL is of type string, then search for a column with that label.
+;
+        IF SC[SC[0]+1] EQ 7 THEN BEGIN
+            MYCOL = STRUPCASE(STRTRIM(MYCOL,2))
+            FOR I = 0, NUMCOLS-1 DO BEGIN
+		XCOL = WHERE(TTYPE[*,ILUN] EQ MYCOL[I], NCOL)
+		ICOL[I] = XCOL[0]
+                IF NCOL GT 0 THEN FOUND[I] = 1
+                IF NOT FOUND[I] THEN BEGIN
+                    IF NOTFOUND EQ '' THEN NOTFOUND = MYCOL[I] $
+                    ELSE NOTFOUND = NOTFOUND +', ' + MYCOL[I]
+                    NNOTFOUND = NNOTFOUND + 1
+                ENDIF
+            ENDFOR
+
+            IF NNOTFOUND EQ NUMCOLS THEN BEGIN
+                MESSAGE = 'ERROR: None of the requested columns were found'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                    ERRMSG = MESSAGE
+                    RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF ELSE IF NNOTFOUND GT 0 THEN BEGIN
+                MESSAGE = 'WARNING: Columns ' + NOTFOUND + ' were not found'
+                IF N_ELEMENTS(WARNMSG) NE 0 THEN WARNMSG = MESSAGE $
+                ELSE MESSAGE, MESSAGE, /INFO
+            ENDIF
+;
+;  Otherwise, a numerical column was passed.  Check its value.
+;
+	ENDIF ELSE BEGIN
+            ICOL[*] = LONG(MYCOL) - 1
+            FOUND[ICOL] = 1
+        ENDELSE
+                
+
+;
+;  Step through each column index, and check for validity
+;
+        MESSAGE = ''
+        FOR I = 0, NUMCOLS-1 DO BEGIN
+            IF NOT FOUND[I] THEN GOTO, LOOP_END_COLCHECK
+
+            IF (ICOL[I] LT 0) OR (ICOL[I] GE TFIELDS[ILUN]) THEN BEGIN
+                MESSAGE = 'COL "'+STRTRIM(MYCOL[I],2)+$
+                  '" must be between 1 and ' +	$
+                  STRTRIM(TFIELDS[ILUN],2)
+                FOUND[I] = 0
+            ENDIF
+;
+;  If there are no elements in the array, then set !ERR to -1.
+;
+            IF FOUND[I] AND N_ELEM[ICOL[I],ILUN] EQ 0 THEN BEGIN
+                FOUND[I] = 0
+                MESSAGE = MESSAGE + '; Number of elements to write in "'+$
+                  STRTRIM(MYCOL[I],2)+'" should be zero'
+            ENDIF
+
+;
+;  Do not permit variable-length columns
+;
+            IF MAXVAL[ICOL[I],ILUN] GT 0 THEN BEGIN
+                MESSAGE = MESSAGE + 'FXBWRITM cannot write ' +	$
+                  'variable-length column "'+STRTRIM(MYCOL[I],2)+'"'
+                FOUND[I] = 0
+            ENDIF
+
+            LOOP_END_COLCHECK:
+
+        ENDFOR
+;
+;  If ROW was not passed, then set it equal to the entire range.  Otherwise,
+;  extract the range.
+;
+        IF N_ELEMENTS(ROW) EQ 0 THEN BEGIN
+            ROW = [1LL, NAXIS2[ILUN]]
+        ENDIF
+	CASE N_ELEMENTS(ROW) OF
+		1:  ROW2 = LONG64(ROW[0])
+		2:  ROW2 = LONG64(ROW[1])
+		ELSE:  BEGIN
+			MESSAGE = 'ROW must have one or two elements'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+			END
+	ENDCASE
+	ROW1 = LONG64(ROW[0])
+
+;
+;  If ROW represents a range, then make sure that the row range is legal, and
+;  that reading row ranges is allowed (i.e., the column is not variable length.
+;
+	IF ROW1 NE ROW2 THEN BEGIN
+		MAXROW = NAXIS2[ILUN]
+		IF (ROW1 LT 1) OR (ROW1 GT MAXROW) THEN BEGIN
+			MESSAGE = 'ROW[0] must be between 1 and ' +	$
+				STRTRIM(MAXROW,2)
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		END ELSE IF (ROW2 LT ROW1) OR (ROW2 GT MAXROW) THEN BEGIN
+			MESSAGE = 'ROW[1] must be between ' +	$
+				STRTRIM(ROW1,2) + ' and ' + STRTRIM(MAXROW,2)
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+;
+;  Otherwise, if ROW is a single number, then just make sure it's valid.
+;
+	END ELSE BEGIN
+		IF (ROW1 LT 1) OR (ROW1 GT NAXIS2[ILUN]) THEN BEGIN
+			MESSAGE = 'ROW must be between 1 and ' +	$
+				STRTRIM(NAXIS2[ILUN],2)
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+	ENDELSE
+
+;
+;  Check the type of the data against that defined for this column.
+;
+        COLNDIM = LONARR(NUMCOLS)
+        COLDIM  = LONARR(NUMCOLS, 8) ;; Maximum of 8 dimensions in output
+        COLTYPE = LONARR(NUMCOLS)
+        BOFF1   = LONARR(NUMCOLS)
+        BOFF2   = LONARR(NUMCOLS)
+        NOUTPUT = LONARR(NUMCOLS)
+        NROWS = ROW2-ROW1+1
+        MESSAGE = ''
+        DTYPENAMES = [ 'BAD TYPE', 'BYTE', 'FIX', 'LONG', $
+                       'FLOAT', 'DOUBLE', 'COMPLEX', 'STRING', $
+                       'BAD TYPE', 'DCOMPLEX', $
+                       'BAD TYPE', 'BAD TYPE', 'BAD TYPE', 'BAD TYPE', 'LONG64' ]
+        FOR I = 0L, NUMCOLS-1 DO BEGIN
+
+            IF NOT FOUND[I] THEN GOTO, LOOP_END_DIMS
+            ;;  Data type of the input.
+            COLTYPE[I] = IDLTYPE[ICOL[I],ILUN]
+
+            SZ = 0
+            IF PASS EQ 'ARGUMENT' THEN BEGIN
+              RESULT = EXECUTE('SZ = SIZE('+COLNAMES[I]+')') 
+              IF RESULT EQ 0 THEN BEGIN
+                MESSAGE = MESSAGE + '; Could not extract type info (column '+$
+                  STRTRIM(MYCOL[I],2)+')'
+                FOUND[I] = 0
+              ENDIF
+            ENDIF ELSE SZ = SIZE(*(POINTERS[I])) 
+
+            TSCAL1 = TSCAL[ICOL[I],ILUN]
+            TZERO1 = TZERO[ICOL[I],ILUN]
+
+            TYPE = SZ[SZ[0]+1]
+            TYPE_BAD = TYPE NE COLTYPE[I]
+            ;; Handle case of scaled data being stored in an
+            ;; integer column
+            IF NOT KEYWORD_SET(NOSCALE) AND $
+              (TSCAL1 NE 0) AND (TSCAL1 NE 1) AND $
+              (TYPE EQ 4 OR TYPE EQ 5) AND $
+              (COLTYPE[I] EQ 2 OR COLTYPE[I] EQ 3 OR COLTYPE[I] EQ 14) THEN $
+              TYPE_BAD = 0
+
+            ;; Unsigned types are OK
+            IF TSCAL1 EQ 1 AND $
+               ((COLTYPE[I] EQ 2 AND TZERO1 EQ 32768) OR $
+                (COLTYPE[I] EQ 3 AND TZERO1 EQ 2147483648D)) AND $
+               (TYPE EQ 1 OR TYPE EQ 2 OR TYPE EQ 3 OR $
+                TYPE EQ 12 OR TYPE EQ 13 OR TYPE EQ 14) THEN BEGIN
+               TYPE_BAD = 0
+            ENDIF
+            
+            IF TYPE_BAD THEN BEGIN
+                CASE COLTYPE[I] OF
+                    1: STYPE = 'byte'
+                    2: STYPE = 'short integer'
+                    3: STYPE = 'long integer'
+                    4: STYPE = 'floating point'
+                    5: STYPE = 'double precision'
+                    6: STYPE = 'complex'
+                    7: STYPE = 'string'
+                    9: STYPE = 'double complex'
+                   12: STYPE = 'unsigned integer'
+                   13: STYPE = 'unsigned long integer'
+                   14: STYPE = 'long64 integer'
+                ENDCASE
+                FOUND[I] = 0
+                MESSAGE = '; Data type (column '+STRTRIM(MYCOL[I],2)+$
+                  ') should be ' + STYPE		 
+            ENDIF
+
+            DIMS = N_DIMS[*,ICOL[I],ILUN]
+            NDIMS = DIMS[0]
+            DIMS  = DIMS[1:NDIMS]
+
+            IF NDIMS EQ 1 AND DIMS[0] EQ 1 THEN BEGIN
+
+                ;; Case of only one output element, try to return a
+                ;; scalar.  Otherwise, it is a vector equal to the
+                ;; number of rows to be read
+
+                COLNDIM[I] = 1L
+                COLDIM[I,0] = NROWS
+            ENDIF ELSE BEGIN
+
+                COLNDIM[I] = NDIMS
+                COLDIM[I,0:(NDIMS-1)] = DIMS
+                IF NROWS GT 1 THEN BEGIN
+                    COLDIM[I,NDIMS] = NROWS
+                    COLNDIM[I] = COLNDIM[I]+1
+                ENDIF
+
+            ENDELSE
+
+;
+;  Check the number of elements in the input
+;
+            NOUTP = ROUND(PRODUCT(COLDIM[I,0:COLNDIM[I]-1]))
+            IF SZ[SZ[0]+1] EQ 7 THEN BEGIN
+                NOUTP = NOUTP / COLDIM[I,0]
+                IF NOUTP NE SZ[SZ[0]+2] THEN GOTO, ERR_NELEM
+                NOUTPUT[I] = NOUTP
+            ENDIF ELSE IF SZ[SZ[0]+2] NE NOUTP THEN BEGIN
+                ERR_NELEM:
+                MESSAGE = MESSAGE+'; Data array (column '+STRTRIM(MYCOL[I],2)+$
+                  ') should have ' + STRTRIM(LONG(NOUTP),2) + ' elements'
+                FOUND[I] = 0
+            ENDIF ELSE NOUTPUT[I] = NOUTP
+
+            ;; Byte offsets
+            BOFF1[I] = BYTOFF[ICOL[I],ILUN]
+            IF ICOL[I] EQ TFIELDS[ILUN]-1 THEN BOFF2[I] = NAXIS1[ILUN]-1 $
+            ELSE BOFF2[I] = BYTOFF[ICOL[I]+1,ILUN]-1
+
+            LOOP_END_DIMS:
+
+        ENDFOR
+
+;
+;  Check to be sure that there are columns to be written
+;
+        W = WHERE(FOUND EQ 1, COUNT)
+        IF COUNT EQ 0 THEN BEGIN
+            STRPUT, MESSAGE, ':', 0
+            MESSAGE = 'ERROR: No requested columns could be written'+MESSAGE
+            IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                ERRMSG = MESSAGE
+                RETURN
+            END ELSE MESSAGE, MESSAGE
+        ENDIF ELSE IF MESSAGE NE '' THEN BEGIN
+            STRPUT, MESSAGE, ':', 0
+            MESSAGE = 'WARNING: Some columns could not be written'+MESSAGE
+            IF N_ELEMENTS(WARNMSG) NE 0 THEN WARNMSG = MESSAGE $
+            ELSE MESSAGE, MESSAGE, /INFO
+        ENDIF
+
+        ;; I construct a list of unique column names here.  Why?
+        ;; Because if *all* the columns are named, then there is no
+        ;; need to read the data from disk first.  Since columns can
+        ;; be given more than once in MYCOL, we need to uniq-ify it.
+        CC = MYCOL[UNIQ(MYCOL, SORT(MYCOL))]
+        NC = N_ELEMENTS(CC)
+
+;
+;  Find the position of the first byte of the data array in the file.
+;
+	OFFSET0 = NHEADER[ILUN] + NAXIS1[ILUN]*(ROW1-1LL)
+
+        POS = 0LL
+        NROWS0 = NROWS
+        J = 0LL
+        ;; Here, we constrain the buffer to be at least 16 rows long.
+        ;; If we fill up 32 kB with fewer than 16 rows, then there
+        ;; must be a lot of (big) columns in this table.  It's
+        ;; probably a candidate for using FXBREAD instead.
+        BUFFROWS = LONG((BUFFERSIZE/NAXIS1[ILUN]) > 16L)
+        IF BUFFERSIZE LE 0 THEN BUFFROWS = NROWS0
+
+;
+;  Loop through the data in chunks
+;
+        WHILE NROWS GT 0 DO BEGIN
+        J = J + 1
+        NR  = NROWS < BUFFROWS
+        OFFSET1 = NAXIS1[ILUN]*POS
+;
+;  Proceed by reading a byte array from the input data file
+;  FXBREADM reads all columns from the specified rows, and
+;  sorts out the details of which bytes belong to which columns
+;  in the next FOR loop.
+;
+        BB = BYTARR(NAXIS1[ILUN], NR)
+;  If *all* columns are being filled, then there is no reason to 
+;  read from the file
+        
+        IF NC LT TFIELDS[ILUN] THEN BEGIN
+            POINT_LUN,UNIT,OFFSET0+OFFSET1
+            READU, UNIT, BB
+        ENDIF
+
+;
+;  Now select out the desired columns to write
+;
+        FOR I = 0, NUMCOLS-1 DO BEGIN
+            IF NOT FOUND[I] THEN GOTO, LOOP_END_WRITE
+
+            ;; Copy data into DD
+            IF PASS EQ 'ARGUMENT' THEN BEGIN
+              RESULT = EXECUTE('DD = '+COLNAMES[I]) 
+              IF RESULT EQ 0 THEN GOTO, LOOP_END_WRITE
+            ENDIF ELSE DD = *(POINTERS[I])
+
+;             ENDIF
+            IF N_ELEMENTS(DD) EQ 1 THEN DD = [DD]
+            DD = REFORM(DD, NOUTPUT[I]/NROWS0, NROWS0, /OVERWRITE)
+            IF POS GT 0 OR NR LT NROWS0 THEN $
+              DD = DD[*,POS:(POS+NR-1)]
+
+            ;; Now any conversions to FITS format must be done
+            COUNT = 0L
+            CT = COLTYPE[I]
+
+            ;; Perform data scaling, if scaling values are available
+            IF NOT KEYWORD_SET(NOSCALE) THEN BEGIN
+                TSCAL1 = TSCAL[ICOL[I],ILUN]
+                TZERO1 = TZERO[ICOL[I],ILUN]
+                IF TSCAL1 EQ 0 THEN TSCAL1 = 1
+                ;; Handle special unsigned cases
+                IF TZERO1 EQ 32768 AND TSCAL1 EQ 1 AND CT EQ 2 THEN $
+                   ;; Unsigned integer
+                   DD = UINT(DD) - UINT(TZERO1) $
+                ELSE IF TZERO1 EQ 2147483648D AND TSCAL1 EQ 1 AND CT EQ 3 THEN $
+                   ;; Unsigned long integer
+                   DD = ULONG(DD) - ULONG(TZERO1) $
+                ELSE IF TZERO1 NE 0 THEN DD = DD - TZERO1
+                IF TSCAL1 NE 1 THEN DD = DD / TSCAL1
+            ENDIF
+            SZ = SIZE(DD)
+            TP = SZ[SZ[0]+1]
+
+            CASE 1 OF
+                ;; Integer types
+                (CT EQ 1): BEGIN
+                    ;; Type-cast may be needed if we used TSCAL/TZERO
+                    IF TP NE 1 THEN DD = BYTE(DD)
+                END
+                (CT EQ 2): BEGIN
+                    ;; Type-cast may be needed if we used TSCAL/TZERO
+                    IF TP NE 2 THEN DD = FIX(DD)
+                    IF NOT KEYWORD_SET(NOIEEE) THEN $
+		       SWAP_ENDIAN_INPLACE, DD,/SWAP_IF_LITTLE
+               END
+                (CT EQ 3): BEGIN
+                    ;; Type-cast may be needed if we used TSCAL/TZERO
+                    IF TP NE 3 THEN DD = LONG(DD)
+                    IF NOT KEYWORD_SET(NOIEEE) THEN $
+		       SWAP_ENDIAN_INPLACE, DD,/SWAP_IF_LITTLE
+		    
+                END
+                (ct eq 14): begin
+                    ;; Type-cast may be needed if we used TSCAL/TZERO
+                    IF TP NE 14 THEN DD = LONG(DD)
+                    IF NOT KEYWORD_SET(NOIEEE) THEN   $
+		       SWAP_ENDIAN_INPLACE, DD,/SWAP_IF_LITTLE                
+                end
+
+                ;; Floating and complex types
+                (CT GE 4 AND CT LE 6 OR CT EQ 9): BEGIN
+                    IF NOT KEYWORD_SET(NOIEEE) THEN BEGIN
+                        IF N_ELEMENTS(NANVALUE) EQ 1 THEN BEGIN
+                            W=WHERE(DD EQ NANVALUE,COUNT)
+                            NAN = REPLICATE('FF'XB,16)
+                            NAN = CALL_FUNCTION(DTYPENAMES,NAN,0,1)
+                        ENDIF
+			SWAP_ENDIAN_INPLACE, DD,/SWAP_IF_LITTLE                        
+                        IF COUNT GT 0 THEN DD[W] = NAN
+                    ENDIF
+                END
+
+                ;; String type, needs to be padded with spaces
+                (CT EQ 7): BEGIN
+                    N_CHAR = N_DIMS[1,ICOL[I],ILUN]
+                    ;; Largest string determines size of array
+                    MAXLEN = MAX(STRLEN(DD)) > 1
+                    ;; Convert to bytes
+                    DD = BYTE(TEMPORARY(DD))
+                    IF N_ELEMENTS(DD) EQ 1 THEN DD = [DD]
+                    DD = REFORM(DD, MAXLEN, NR, /OVERWRITE)
+
+                    ;; Put it into the output array
+                    IF MAXLEN GT N_CHAR THEN BEGIN
+                        DD = DD[0:(N_CHAR-1),*]
+                    ENDIF ELSE BEGIN
+                        DB = BYTARR(N_CHAR, NR)
+                        DB[0:(MAXLEN-1),*] = TEMPORARY(DD)
+                        DD = TEMPORARY(DB)
+                    ENDELSE
+
+                    ;; Pad any zeroes with spaces
+                    WB = WHERE(DD EQ 0b, WCOUNT)
+                    IF WCOUNT GT 0 THEN DD[WB] = 32B
+                    
+                    ;; Pretend that it is a byte array
+                    CT = 1
+                END
+            ENDCASE
+            IF CT NE 1 THEN $
+              DD = BYTE(TEMPORARY(DD),0,(BOFF2[I]-BOFF1[I]+1),NR)
+            IF N_ELEMENTS(DD) EQ 1 THEN DD = [DD]
+            DD = REFORM(DD, BOFF2[I]-BOFF1[I]+1, NR, /OVERWRITE)
+            
+            ;; Now place the data into the byte array
+            BB[BOFF1[I],0] = DD
+            
+            OUTSTATUS[I] = 1
+            LOOP_END_WRITE:
+        END
+
+        ;; Finally, write byte array to output file
+        POINT_LUN, UNIT, OFFSET0+OFFSET1
+        BB = REFORM(BB, N_ELEMENTS(BB), /OVERWRITE)
+        WRITEU, UNIT, BB
+
+        NROWS = NROWS - NR
+        POS   = POS + NR
+        ENDWHILE
+
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxfindend.pro b/Code/script_idl_mv/astrolib/fxfindend.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b33c1aaa1ed595afbca207f4d48fa7db719c2dc1
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxfindend.pro
@@ -0,0 +1,93 @@
+	PRO FXFINDEND,UNIT, EXTENSION
+;+
+; NAME: 
+;	FXFINDEND
+; Purpose     : 
+;	Find the end of a FITS file.
+; Explanation : 
+;	This routine finds the end of the last logical record in a FITS file,
+;	which may be different from that of the physical end of the file.  Each
+;	FITS header is read in and parsed, and the file pointer is moved to
+;	where the next FITS extension header would be if there is one, or to
+;	the end of the file if not.
+; Use         : 
+;	FXFINDEND, UNIT [, EXTENSION]
+; Inputs      : 
+;	UNIT	= Logical unit number for the opened file.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	None.
+; Opt. Outputs: 
+;       EXTENSION = The extension number that a new extension would
+;                   have if placed at the end of the file.
+; Keywords    : 
+;	None.
+; Calls       : 
+;	FXHREAD, FXPAR
+; Common      : 
+;	None.
+; Restrictions: 
+;	The file must have been opened for block I/O.  There must not be any
+;	FITS "special records" at the end of the file.
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Feb. 1992.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+; Version     : 
+;	Version 1, 12 April 1993.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Added EXTENSION parameter, CM 1999 Nov 18
+;       Allow for possible 64bit integer number of bytes W. Landsman Nov 2007
+;       make Ndata a long64 to deal with large files. E. Hivon Mar 2008
+;-
+;
+	ON_ERROR,2
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() EQ 0 THEN MESSAGE,'Syntax:  FXFINDEND, UNIT [,EXTENSION]'
+;
+;  Go to the start of the file.
+;
+	POINT_LUN,UNIT,0
+        EXTENSION = 0L
+;
+;  Read the next header, and get the number of bytes taken up by the data.
+;
+NEXT_EXT:
+	FXHREAD,UNIT,HEADER,STATUS
+	IF STATUS NE 0 THEN GOTO, DONE
+	BITPIX = FXPAR(HEADER,'BITPIX')
+	NAXIS  = FXPAR(HEADER,'NAXIS')
+	GCOUNT = FXPAR(HEADER,'GCOUNT')  &  IF GCOUNT EQ 0 THEN GCOUNT = 1
+	PCOUNT = FXPAR(HEADER,'PCOUNT')
+	IF NAXIS GT 0 THEN BEGIN 
+		DIMS = FXPAR(HEADER,'NAXIS*')		;Read dimensions
+		NDATA = long64(DIMS[0])
+		IF NAXIS GT 1 THEN FOR I=2,NAXIS DO NDATA = NDATA*DIMS[I-1]
+	ENDIF ELSE NDATA = 0
+	NBYTES = LONG64(ABS(BITPIX) / 8) * GCOUNT * (PCOUNT + NDATA)
+;
+;  Move to the next extension header in the file.
+;
+	NREC = (NBYTES + 2879) / 2880
+	POINT_LUN, -UNIT, POINTLUN			;Current position
+	POINT_LUN, UNIT, POINTLUN + NREC*2880L		;Next FITS extension
+        EXTENSION = EXTENSION + 1L
+	IF NOT EOF(UNIT) THEN GOTO, NEXT_EXT
+;
+;  When done, make sure that the pointer is positioned at the first byte after
+;  the last data set.
+;
+DONE:
+	POINT_LUN, UNIT, POINTLUN + NREC*2880L
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxhclean.pro b/Code/script_idl_mv/astrolib/fxhclean.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2b162ed1f026a294b74b43f464e357b056d03ad2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxhclean.pro
@@ -0,0 +1,110 @@
+	PRO FXHCLEAN,HEADER,ERRMSG=ERRMSG
+;+
+; NAME: 
+;	FXHCLEAN
+; Purpose     : 
+;	Removes required keywords from FITS header.
+; Explanation : 
+;	Removes any keywords relevant to array structure from a FITS header,
+;	preparatory to recreating it with the proper values.
+; Use         : 
+;	FXHCLEAN, HEADER
+; Inputs      : 
+;	HEADER	= FITS header to be cleaned.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	HEADER	= The cleaned FITS header is returned in place of the input
+;		  array.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	ERRMSG	= If defined and passed, then any error messages will be
+;		  returned to the user in this parameter rather than
+;		  depending on the MESSAGE routine in IDL.  If no errors are
+;		  encountered, then a null string is returned.  In order to
+;		  use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXHCLEAN, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	SXDELPAR, FXPAR
+; Common      : 
+;	None.
+; Restrictions: 
+;	HEADER must be a string array containing a properly formatted FITS
+;	header.
+; Side effects: 
+;	Warning:  when cleaning a binary table extension header, not all of the
+;	keywords pertaining to columns in the table may be removed.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Jan 1992.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 31 May 1994
+;		Added ERRMSG keyword.
+;       Version 3, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;	Version 4, William Thompson, GSFC, 30 December 1994
+;		Added TCUNIn to list of column keywords to be removed.
+; Version     :
+;       Version 4, 30 December 1994
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+	ON_ERROR, 2
+;
+;  Check the number of input parameters.
+;
+	IF N_PARAMS() NE 1 THEN BEGIN
+		MESSAGE = 'Syntax:  FXHCLEAN, HEADER'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Check the type of HEADER.
+;
+	S = SIZE(HEADER)
+	IF (S[0] NE 1) OR (S[2] NE 7) THEN BEGIN
+		MESSAGE = 'HEADER must be a (one-dimensional) string array'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  Start removing the various keywords relative to the structure of the FITS
+;  file.
+;
+	SXDELPAR,HEADER,['SIMPLE','EXTEND','XTENSION','BITPIX','PCOUNT', $
+		'GCOUNT','THEAP']
+;
+;  Get the number of axes as stored in the header.  Then, remove it, and any
+;  NAXISnnn keywords implied by it.
+;
+	NAXIS = FXPAR(HEADER,'NAXIS')
+	SXDELPAR,HEADER,'NAXIS'
+	IF NAXIS GT 0 THEN FOR I=1,NAXIS DO	$
+		SXDELPAR,HEADER,'NAXIS'+STRTRIM(I,2)
+;
+;  Get the number of columns in a binary table.  Remove any column definitions.
+;
+	TFIELDS = FXPAR(HEADER,'TFIELDS')
+	SXDELPAR,HEADER,'TFIELDS'
+	IF TFIELDS GT 0 THEN FOR I=1,TFIELDS DO SXDELPAR,HEADER,	$
+		['TFORM','TTYPE','TDIM','TUNIT','TSCAL','TZERO',	$
+		'TNULL','TDISP','TDMIN','TDMAX','TDESC','TROTA',	$
+		'TRPIX','TRVAL','TDELT','TCUNI']  + STRTRIM(I,2)
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxhmake.pro b/Code/script_idl_mv/astrolib/fxhmake.pro
new file mode 100644
index 0000000000000000000000000000000000000000..598a455ba0b3fa10676383009fdebccf9624d19a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxhmake.pro
@@ -0,0 +1,252 @@
+	PRO FXHMAKE, HEADER, DATA, EXTEND=EXTEND, DATE=DATE,	$
+		INITIALIZE=INITIALIZE, ERRMSG=ERRMSG, XTENSION=XTENSION
+;+
+; NAME: 
+;	FXHMAKE
+; Purpose     : 
+;	Create a basic FITS header array.
+; Explanation : 
+;	Creates a basic header array with all the required keywords.  This
+;	defines a basic structure which can then be added to or modified by
+;	other routines.
+; Use         : 
+;	FXHMAKE, HEADER  [, DATA ]
+; Inputs      : 
+;	None required.
+; Opt. Inputs : 
+;	DATA	= IDL data array to be written to file.    It must be in the 
+;                  primary data unit unless the XTENSION keyword is supplied.
+;		  This array is used to determine the values of the BITPIX and 
+;                 NAXIS, etc. keywords.
+;
+;		  If not passed, then BITPIX is set to eight, NAXIS is set to
+;		  zero, and no NAXISnnn keywords are included in this
+;		  preliminary header.
+; Outputs     : 
+;	HEADER = String array containing FITS header.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	INITIALIZE = If set, then the header is completely initialized, and any
+;		     previous entries are lost.
+;	EXTEND	= If set, then the keyword EXTEND is inserted into the file,
+;		  with the value of "T" (true).
+;	DATE	= If set, then the DATE keyword is added to the header.
+;	ERRMSG	= If defined and passed, then any error messages will be
+;		  returned to the user in this parameter rather than
+;		  depending on the MESSAGE routine in IDL.  If no errors are
+;		  encountered, then a null string is returned.  In order to
+;		  use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXHMAKE, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;       XTENSION - If set, then the header is appropriate for an image 
+;                  extension, rather than the primary data unit.
+; Calls       : 
+;	GET_DATE, FXADDPAR, FXHCLEAN
+; Common      : 
+;	None.
+; Restrictions: 
+;	Groups are not currently supported.
+; Side effects: 
+;	BITPIX, NAXIS, etc. are defined such that complex arrays are stored as
+;	floating point, with an extra first dimension of two elements (real and
+;	imaginary parts).
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Jan 1992, from SXHMAKE by D. Lindler and M. Greason.
+;	Differences include:
+;
+;		* Use of FITS standard (negative BITPIX) to signal floating
+;		  point numbers instead of (SDAS/Geis) DATATYPE keyword.
+;		* Storage of complex numbers as pairs of real numbers.
+;		* Support for EXTEND keyword, and for cases where there is no
+;		  primary data array.
+;		* Insertion of DATE record made optional.  Only required FITS
+;		  keywords are inserted automatically.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 21 June 1994
+;		Added ERRMSG keyword.
+;       Version 3, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;	Version 4, Wayne Landsman, GSFC, 12 August 1997
+;		Recognize double complex data type
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Version 6, William Thompson, GSFC, 22 September 2004
+;               Recognize unsigned integer types.
+;       Version 6.1, C. Markwardt, GSFC, 19 Jun 2005
+;               Add the XTENSION keyword, which writes an XTENSION
+;               keyword instead of SIMPLE.
+; Version     :
+;       Version 6.1, 19 June 2005
+;-
+;
+	ON_ERROR,2
+;
+;  Check the number of parameters first.
+;
+	IF N_PARAMS() LT 1 THEN BEGIN
+		MESSAGE = 'Calling sequence:  FXHMAKE, HEADER  [, DATA ]'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+;
+;  If no data array was passed, then set BITPIX=8 and NAXIS=0.  Otherwise,
+;  calculate these parameters.
+;
+	IF N_PARAMS() EQ 1 THEN BEGIN
+		BITPIX = 8
+		COMMENT = ''
+		S = 0
+	END ELSE BEGIN
+		S = SIZE(DATA)			;obtain size of array.
+		DTYPE = S[S[0]+1]		;type of data.
+		CASE DTYPE OF
+			0:  BEGIN
+				MESSAGE = 'Data parameter is not defined'
+				IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+					ERRMSG = MESSAGE
+					RETURN
+				END ELSE MESSAGE, MESSAGE
+				END
+			1:  BEGIN
+				BITPIX = 8
+				COMMENT = 'Integer*1 (byte)'
+				END
+			2:  BEGIN
+				BITPIX = 16
+				COMMENT = 'Integer*2 (short integer)'
+				END
+			3:  BEGIN
+				BITPIX = 32
+				COMMENT = 'Integer*4 (long integer)'
+				END
+			4:  BEGIN
+				BITPIX = -32
+				COMMENT = 'Real*4 (floating point)'
+				END
+			5:  BEGIN
+				BITPIX = -64
+				COMMENT = 'Real*8 (double precision)'
+				END
+			6:  BEGIN		;Complex*8 (complex)
+				BITPIX = -32			;Store as float
+				S = [S[0]+1, 2, S[1:*]]		;with extra dim
+				COMMENT = 'Real*4 (complex, stored as float)'
+				END
+			7:  BEGIN
+				MESSAGE = "Can't write strings to FITS files"
+				IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+					ERRMSG = MESSAGE
+					RETURN
+				END ELSE MESSAGE, MESSAGE
+				END
+			8:  BEGIN
+				MESSAGE = "Can't write structures to FITS files"
+				IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+					ERRMSG = MESSAGE
+					RETURN
+				END ELSE MESSAGE, MESSAGE
+				END
+			9:  BEGIN
+				BITPIX = -64			;Store as double
+				S = [S[0]+1, 2, S[1:*]]		;with extra dim
+				COMMENT = 'Real*8 (dcomplex, stored as double)'
+				END
+;
+;  Unsigned data types may require use of BZERO/BSCALE--handled in writer.
+;
+			12: BEGIN       ;Unsigned integer
+				BITPIX = 16
+				COMMENT = 'Integer*2 (short integer)'
+				END
+			13:  BEGIN      ;Unsigned long integer
+				BITPIX = 32
+				COMMENT = 'Integer*4 (long integer)'
+				END
+                                
+		ENDCASE
+	ENDELSE
+;
+;  If requested, then initialize the header.
+;
+	IF KEYWORD_SET(INITIALIZE) THEN BEGIN
+		HEADER = STRARR(36)
+		HEADER[0] = 'END' + STRING(REPLICATE(32B,77))
+;
+;  Else, if undefined, then initialize the header.
+;
+	END ELSE IF N_ELEMENTS(HEADER) EQ 0 THEN BEGIN
+		HEADER = STRARR(36)
+		HEADER[0] = 'END' + STRING(REPLICATE(32B,77))
+;
+;  Otherwise, make sure that HEADER is a string array, and remove any keywords
+;  that describe the format of the file.
+;
+	END ELSE BEGIN
+		SZ = SIZE(HEADER)
+		IF (SZ[0] NE 1) OR (SZ[2] NE 7) THEN BEGIN
+			MESSAGE = 'HEADER must be a (one-dimensional) ' + $
+				'string array'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			FXHCLEAN,HEADER,ERRMSG=ERRMSG
+			IF ERRMSG NE '' THEN RETURN
+		END ELSE FXHCLEAN,HEADER
+	ENDELSE
+;
+;  The first keyword must be "SIMPLE".  Normally, this has the value "T"
+;  (true).
+;
+        IF KEYWORD_SET(XTENSION) THEN BEGIN
+            FXADDPAR,HEADER,'XTENSION','IMAGE','Written by IDL:  '+ SYSTIME()
+        ENDIF ELSE BEGIN
+            FXADDPAR,HEADER,'SIMPLE','T','Written by IDL:  '+ SYSTIME()
+        ENDELSE
+;
+;  The second keyword must be "BITPIX", and the third "NAXIS".
+;
+	FXADDPAR,HEADER,'BITPIX',BITPIX,COMMENT
+	FXADDPAR,HEADER,'NAXIS',S[0]	;# of dimensions
+;
+;  If NAXIS is not zero, then add the keywords for the axes.  If the data array
+;  is complex, then add a comment to the first axis to note that this is
+;  actually the real and imaginary parts of the complex number.
+;
+	IF S[0] NE 0 THEN FOR I=1,S[0] DO BEGIN
+		IF (I EQ 1) AND (DTYPE EQ 6) THEN BEGIN
+			FXADDPAR,HEADER,'NAXIS1',S[I],	$
+				'Real and imaginary parts'
+		END ELSE BEGIN
+			FXADDPAR,HEADER,'NAXIS'+STRTRIM(I,2),S[I]
+		ENDELSE
+	ENDFOR
+;
+;  If requested, add the EXTEND keyword to the header, and set it to true.
+;
+	IF KEYWORD_SET(EXTEND) THEN	$
+		FXADDPAR,HEADER,'EXTEND','T','File contains extensions'
+;
+;  If requested, add the DATE keyword to the header, containing the current
+;  date.
+;
+	IF KEYWORD_SET(DATE) THEN BEGIN
+	        GET_DATE,DTE                    ;Get current date as CCYY-MM-DD
+        	FXADDPAR,HEADER,'DATE',DTE
+	ENDIF
+;
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxhmodify.pro b/Code/script_idl_mv/astrolib/fxhmodify.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8ac0ad27c982cbc70799536e369f0b67ca36b172
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxhmodify.pro
@@ -0,0 +1,277 @@
+PRO FXHMODIFY, FILENAME, NAME, VALUE, COMMENT, BEFORE=BEFORE,   $
+               AFTER=AFTER, FORMAT=FORMAT, EXTENSION=EXTENSION, ERRMSG=ERRMSG,$
+               NOGROW=NOGROW
+;+
+; NAME: 
+;       FXHMODIFY
+; PURPOSE     : 
+;       Modify a FITS header in a file on disk.
+; Explanation : 
+;       Opens a FITS file, and adds or modifies a parameter in the FITS header.
+;       Can be used for either the main header, or for an extension header. 
+;       The modification is performed directly on the disk file.
+; Use         : 
+;       FXHMODIFY, FILENAME, NAME, VALUE, COMMENT
+; Inputs      : 
+;       FILENAME = String containing the name of the file to be read.
+;
+;       NAME    = Name of parameter, scalar string  If NAME is already in the 
+;                 header the value and possibly comment fields are modified. 
+;                 Otherwise a new record is added to the header.  If NAME is 
+;                 equal to either "COMMENT" or "HISTORY" then the value will be 
+;                 added to the record without replacement.  In this case the 
+;                 comment parameter is ignored.
+;
+;       VALUE   = Value for parameter.  The value expression must be of the
+;                 correct type, e.g. integer, floating or string.  String
+;                 values of 'T' or 'F' are considered logical values.
+;
+; Opt. Inputs : 
+;       COMMENT = String field.  The '/' is added by this routine.  Added
+;                 starting in position 31.  If not supplied, or set equal to ''
+;                 (the null string), then any previous comment field in the
+;                 header for that keyword is retained (when found).
+; Outputs     : 
+;       None.
+; Opt. Outputs: 
+;       None.
+; Keywords    : 
+;       EXTENSION = Either the number of the FITS extension, starting with the
+;                   first extension after the primary data unit being one; or a
+;                   character string containing the value of EXTNAME to search
+;                   for.  If not passed, then the primary FITS header is
+;                   modified.           
+;
+;       BEFORE  = Keyword string name.  The parameter will be placed before the
+;                 location of this keyword.  For example, if BEFORE='HISTORY'
+;                 then the parameter will be placed before the first history
+;                 location.  This applies only when adding a new keyword;
+;                 keywords already in the header are kept in the same position.
+;
+;       AFTER   = Same as BEFORE, but the parameter will be placed after the
+;                 location of this keyword.  This keyword takes precedence over
+;                 BEFORE.
+;
+;       FORMAT  = Specifies FORTRAN-like format for parameter, e.g. "F7.3".  A
+;                 scalar string should be used.  For complex numbers the format
+;                 should be defined so that it can be applied separately to the
+;                 real and imaginary parts.
+;       ERRMSG  = If defined and passed, then any error messages will be
+;                 returned to the user in this parameter rather than
+;                 depending on the MESSAGE routine in IDL.  If no errors are
+;                 encountered, then a null string is returned.  In order to
+;                 use this feature, ERRMSG must be defined first, e.g.
+;
+;                       ERRMSG = ''
+;                       FXHMODIFY, ERRMSG=ERRMSG, ...
+;                       IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;       FXHREAD, FXPAR, FXADDPAR, BLKSHIFT
+; Restrictions: 
+;       This routine can not be used to modify any of the keywords that control
+;       the structure of the FITS file, e.g. BITPIX, NAXIS, PCOUNT, etc.  Doing
+;       so could corrupt the readability of the FITS file.
+; Example:
+;       Modify the name 'OBJECT' keyword in the primary FITS header of a FITS 
+;       file 'spec98.ccd' to contain the value 'test domeflat'
+;
+;       IDL> fxhmodify, 'spec98.ccd', 'OBJECT', 'test domeflat'
+;
+; Side effects: 
+;       If adding a record to the FITS header would increase the
+;       number of 2880 byte records stored on disk, then the file is
+;       enlarged before modification, unless the NOGROW keyword is passed.
+;  
+; Category    : 
+;       Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;       None.
+; Written     : 
+;       William Thompson, GSFC, 3 March 1994.
+; Modified    : 
+;       Version 1, William Thompson, GSFC, 3 March 1994.
+;       Version 2, William Thompson, GSFC, 31 May 1994
+;               Added ERRMSG keyword.
+;       Version 3, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;      Version 3.1 Wayne Landsman GSFC   17 March 2006
+;               Fix problem in BLKSHIFT call if primary header  extended
+;       Version 3.2 W. Landsman 14 November 204 
+;               Allow for need for 64bit number of bytes
+;       Version 4, William Thompson, GSFC, 22-Dec-2014
+;               Modified test for keyword EXTEND to only issue warning.
+;; Version     :
+;       Version 4, 22-Dec-2014
+;-
+;
+        COMPILE_OPT IDL2
+        ON_ERROR, 2
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() LT 3 THEN BEGIN
+                MESSAGE = $     ;Need at least 3 parameters
+                    'Syntax:  FXHMODIFY, FILENAME, NAME, VALUE [, COMMENT ]'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+;
+;  If passed, check the type of the EXTENSION parameter.
+;
+        IF N_ELEMENTS(EXTENSION) GT 1 THEN BEGIN
+                MESSAGE = 'EXTENSION must be a scalar'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+        END ELSE IF N_ELEMENTS(EXTENSION) EQ 1 THEN BEGIN
+                SZ = SIZE(EXTENSION)
+                ETYPE = SZ[SZ[0]+1]
+                IF ETYPE EQ 8 THEN BEGIN
+                        MESSAGE = 'EXTENSION must not be a structure'
+                        IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                                ERRMSG = MESSAGE
+                                RETURN
+                        END ELSE MESSAGE, MESSAGE
+                ENDIF
+;
+;  If EXTENSION is of type string, then search for the proper extension by
+;  name.  Otherwise, search by number.
+;
+                IF ETYPE EQ 7 THEN BEGIN
+                        S_EXTENSION = STRTRIM(STRUPCASE(EXTENSION),2)
+                END ELSE BEGIN
+                        I_EXTENSION = FIX(EXTENSION)
+                        IF I_EXTENSION LT 1 THEN BEGIN
+                                MESSAGE = 'EXTENSION must be greater than zero'
+                                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                                        ERRMSG = MESSAGE
+                                        RETURN
+                                END ELSE MESSAGE, MESSAGE
+                        ENDIF
+                ENDELSE
+        ENDIF
+;
+;  Get the UNIT number, and open the file.
+;
+        OPENU, UNIT, FILENAME, /BLOCK, /GET_LUN
+;
+;  Read in the primary FITS header.
+;
+        FXHREAD,UNIT,HEADER,STATUS
+        IF STATUS NE 0 THEN BEGIN
+                FREE_LUN,UNIT
+                MESSAGE = 'Unable to read FITS header'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                        ERRMSG = MESSAGE
+                        RETURN
+                END ELSE MESSAGE, MESSAGE
+        ENDIF
+        MHEAD0 = 0
+        I_EXT = 0
+;
+;  If the EXTENSION parameter was passed, then look for the requested
+;  extension.
+;
+        IF N_ELEMENTS(EXTENSION) EQ 1 THEN BEGIN
+;
+;  Make sure that the file does contain extensions.  However, only issue a
+;  warning if EXTEND keyword not set.
+;
+                IF ~FXPAR(HEADER,'EXTEND') THEN MESSAGE, /CONTINUE, $
+                    'Keyword EXTEND not set in file ' + FILENAME
+;
+;  Get the number of bytes taken up by the data.
+;
+NEXT_EXT:
+                BITPIX = FXPAR(HEADER,'BITPIX')
+                NAXIS  = FXPAR(HEADER,'NAXIS')
+                GCOUNT = FXPAR(HEADER,'GCOUNT')
+                IF GCOUNT EQ 0 THEN GCOUNT = 1
+                PCOUNT = FXPAR(HEADER,'PCOUNT')
+                IF NAXIS GT 0 THEN BEGIN 
+                        DIMS = FXPAR(HEADER,'NAXIS*')   ;Read dimensions
+                        NDATA = DIMS[0]
+                        IF NAXIS GT 1 THEN FOR I=2,NAXIS DO     $
+                                NDATA = NDATA*DIMS[I-1]
+                ENDIF ELSE NDATA = 0
+                NBYTES = LONG64(ABS(BITPIX) / 8) * GCOUNT * (PCOUNT + NDATA)
+;
+;  Read the next extension header in the file.
+;
+                NREC = (NBYTES + 2879) / 2880
+                POINT_LUN, -UNIT, POINTLUN              ;Current position
+                MHEAD0 = POINTLUN + NREC*2880L
+                POINT_LUN, UNIT, MHEAD0                 ;Next FITS extension
+                FXHREAD,UNIT,HEADER,STATUS
+                POINT_LUN, -UNIT, END_HEADER
+                IF STATUS NE 0 THEN BEGIN
+                        FREE_LUN,UNIT
+                        MESSAGE = 'Requested extension not found'
+                        IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                                ERRMSG = MESSAGE
+                                RETURN
+                        END ELSE MESSAGE, MESSAGE
+                ENDIF
+                I_EXT = I_EXT + 1
+;
+;  Check to see if the current extension is the one desired.
+;
+                IF ETYPE EQ 7 THEN BEGIN
+                        EXTNAME = STRTRIM(STRUPCASE(FXPAR(HEADER,'EXTNAME')),2)
+                        IF EXTNAME EQ S_EXTENSION THEN GOTO, DONE
+                END ELSE IF I_EXT EQ I_EXTENSION THEN GOTO, DONE
+                GOTO, NEXT_EXT
+DONE:
+        ENDIF ELSE POINT_LUN, -UNIT, END_HEADER
+
+;
+;  Add or modify the keyword parameter in the header, keeping track of the
+;  initial size of the header array.
+;
+        IEND = WHERE(STRMID(HEADER,0,8) EQ 'END     ')
+        N_INITIAL = 1 + IEND[0]/36
+        IF N_PARAMS() EQ 4 THEN BEGIN
+                FXADDPAR, HEADER, NAME, VALUE , COMMENT, BEFORE=BEFORE, $
+                        AFTER=AFTER, FORMAT=FORMAT
+        END ELSE BEGIN
+                FXADDPAR, HEADER, NAME, VALUE, BEFORE=BEFORE, AFTER=AFTER, $
+                        FORMAT=FORMAT
+        ENDELSE
+;
+;  If the length of the header has changed, then print an error message.
+;
+        IEND = WHERE(STRMID(HEADER,0,8) EQ 'END     ')
+        N_FINAL = 1 + IEND[0]/36
+        IF N_FINAL NE N_INITIAL THEN BEGIN
+            IF KEYWORD_SET(NOGROW) THEN BEGIN
+                MESSAGE, /CONTINUE, 'Adding parameter would increase ' + $
+                        'header length, no action taken.'
+            ENDIF ELSE BEGIN
+                ;; Increase size of the file by inserting multiples of
+                ;; 2880 bytes at the end of the current header.  Then
+                ;; resume normal operations.
+                BLKSHIFT, UNIT, END_HEADER, (N_FINAL-N_INITIAL)*36L*80L
+                GOTO, WRITE_HEADER
+            ENDELSE
+;
+;  Otherwise, rewind to the beginning of the header, and write the new header
+;  over the old header.  Convert to byte and force into 80 character lines.
+;
+        ENDIF ELSE BEGIN
+            WRITE_HEADER:
+                BHDR = REPLICATE(32B, 80, 36*N_FINAL)
+                FOR N = 0,IEND[0] DO BHDR[0,N] = BYTE(STRMID(HEADER[N],0,80))
+                POINT_LUN, UNIT, MHEAD0
+                WRITEU, UNIT, BHDR
+        ENDELSE
+;
+;  Close the file and return.
+;
+        FREE_LUN, UNIT
+        IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+        RETURN
+        END
diff --git a/Code/script_idl_mv/astrolib/fxhread.pro b/Code/script_idl_mv/astrolib/fxhread.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3a4da731b4d18777a7262ac705175a87a88f8398
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxhread.pro
@@ -0,0 +1,119 @@
+	PRO FXHREAD,UNIT,HEADER,STATUS
+;+
+; NAME: 
+;	FXHREAD
+; Purpose     : 
+;       Reads a FITS header from an opened disk file.
+; Explanation : 
+;       Reads a FITS header from an opened disk file.
+; Use         : 
+;	FXHREAD, UNIT, HEADER  [, STATUS ]
+; Inputs      : 
+;	UNIT	= Logical unit number.
+; Opt. Inputs : 
+;
+; Outputs     : 
+;	HEADER	= String array containing the FITS header.
+; Opt. Outputs: 
+;	STATUS	= Condition code giving the status of the read.  Normally, this
+;		  is zero, but is set to !ERR if an error occurs, or if the
+;		  first byte of the header is zero (ASCII null).
+; Keywords    : 
+;	None.
+; Calls       : 
+;	None.
+; Common      : 
+;	None.
+; Restrictions: 
+;	The file must already be positioned at the start of the header.  It
+;	must be a proper FITS file.
+; Side effects: 
+;	The file ends by being positioned at the end of the FITS header, unless
+;	an error occurs.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, Feb 1992, from READFITS by J. Woffard and W. Landsman.
+;	W. Thompson, Aug 1992, added test for SIMPLE keyword.
+; Written     : 
+;	William Thompson, GSFC, February 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+; Version     : 
+;	Version 1, 12 April 1993.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+;
+	ON_ERROR,2			;Return to caller
+	STATUS = 0
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() LT 2 THEN MESSAGE,	$
+		'Calling sequence:  FXHREAD, UNIT, HEADER  [, STATUS ]'
+;
+;  Find out whether one is at the beginning of the file (POSITION=0) or not.
+;
+	POINT_LUN,-UNIT,POSITION
+;
+;  Read in the first 2880 byte FITS logical block as a series of 36 card images
+;  of 80 bytes each.
+;
+	HDR = BYTARR( 80, 36, /NOZERO )
+	ON_IOERROR, RETURN_STATUS
+	READU, UNIT, HDR
+;
+;  If not the primary header, then the first eight bytes should decode to
+;  XTENSION.  If not, then set status to -1, and return.
+;
+	IF POSITION NE 0 THEN BEGIN
+		FIRST = STRING(HDR[0:7])
+		IF FIRST NE 'XTENSION' THEN BEGIN
+			MESSAGE,'XTENSION keyword not found',/CONTINUE
+			STATUS = -1
+			GOTO, DONE
+		ENDIF
+	ENDIF
+;
+;  Interpret the header as a string, and check to see if the END line has been
+;  reached.
+;
+	HEADER = STRING( HDR > 32B )
+	ENDLINE = WHERE( STRMID(HEADER,0,8) EQ 'END     ', NEND)
+	IF NEND GT 0 THEN HEADER = HEADER[ 0:ENDLINE[0] ] 
+;
+;  If the primary header (POSITION=0) and the SIMPLE keyword can't be found in
+;  the first record, then this can't be a FITS file.
+;
+	IF POSITION EQ 0 THEN BEGIN
+		SIMPLE_LINE = WHERE(STRMID(HEADER,0,8) EQ 'SIMPLE  ',N_SIMPLE)
+		IF N_SIMPLE EQ 0 THEN BEGIN
+			MESSAGE,'SIMPLE keyword not found',/CONTINUE
+			STATUS = -1
+			GOTO, DONE
+		ENDIF
+	ENDIF
+;
+;  Keep reading until the END line is reached.
+;
+	WHILE NEND EQ 0 DO BEGIN
+		READU, UNIT, HDR
+		HDR1 = STRING( HDR > 32B )
+		ENDLINE = WHERE( STRMID(HDR1,0,8) EQ 'END     ', NEND)
+		IF NEND GT 0 THEN HDR1 = HDR1[ 0:ENDLINE[0] ] 
+		HEADER = [HEADER, HDR1 ]
+	ENDWHILE
+	GOTO, DONE	
+;
+;  Error encounter.  Store the error code in status.
+;
+RETURN_STATUS:
+	STATUS = !ERR
+;
+;  Reset the ON_IOERROR condition.
+;
+DONE:
+	ON_IOERROR,NULL
+	END
diff --git a/Code/script_idl_mv/astrolib/fxmove.pro b/Code/script_idl_mv/astrolib/fxmove.pro
new file mode 100644
index 0000000000000000000000000000000000000000..02b93bf2dec7f1472c28924f431cd3da8f82d627
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxmove.pro
@@ -0,0 +1,137 @@
+FUNCTION FXMOVE, UNIT, EXTEN, SILENT = Silent, EXT_NO = ext_no, ERRMSG=errmsg
+
+;+
+; NAME:
+;     FXMOVE
+; PURPOSE:
+;     Skip to a specified extension number or name in a FITS file
+;
+; CALLING SEQUENCE:
+;     STATUS=FXMOVE(UNIT, EXT, /Silent)
+;     STATUS=FXMOVE(UNIT, EXTNAME, /Silent, EXT_NO=, ERRMSG= )
+;
+; INPUT PARAMETERS:
+;     UNIT     = An open unit descriptor for a FITS data stream.
+;     EXTEN   = Number of extensions to skip.
+;                              or
+;             Scalar string giving extension name (in the EXTNAME keyword)           
+; OPTIONAL INPUT PARAMETER:
+;     /SILENT - If set, then any messages about invalid characters in the 
+;               FITS file are suppressed.
+; OPTIONAL OUTPUT PARAMETER:
+;       ERRMSG  = If this keyword is present, then any error messages will be
+;                 returned to the user in this parameter rather than
+;                 depending on the MESSAGE routine in IDL.  If no errors are
+;                 encountered, then a null string is returned.
+;       EXT_NO - Extension number, scalar integer, useful if the user supplied 
+;                an extension name in the EXTEN parameter
+; RETURNS:
+;     0 if successful.
+;    -1 if an error is encountered.
+;
+; COMMON BLOCKS:
+;      None.
+; SIDE EFFECTS:
+;      Repositions the file pointer.
+; PROCEDURE:
+;      Each FITS header is read in and parsed, and the file pointer is moved
+;      to where the next FITS extension header until the desired
+;      extension is reached.
+; PROCEDURE CALLS:
+;      FXPAR(), MRD_HREAD, MRD_SKIP
+; MODIFICATION HISTORY:
+;      Extracted from FXPOSIT 8-March-2000 by T. McGlynn
+;      Added /SILENT keyword  14-Dec-2000 by W. Landsman
+;      Save time by not reading the full header  W. Landsman   Feb. 2003
+;      Allow extension name to be specified, added EXT_NO, ERRMSG keywords
+;         W. Landsman  December 2006
+;      Make search for EXTNAME case-independent  W.Landsman March 2007 
+;      Avoid round-off error for very large extensions N. Piskunov Dec 2007
+;      Assume since V6.1 (/INTEGER keyword available to PRODUCT() ) Dec 2007
+;      Capture error message from MRD_HREAD (must be used with post-June 2009
+;        version of MRD-HREAD)   W. Landsman  July 2009
+;-
+     On_error, 2
+     compile_opt idl2
+
+         DO_NAME = SIZE( EXTEN,/TNAME) EQ 'STRING'
+	 PRINT_ERROR = ~ARG_PRESENT(ERRMSG)
+         ERRMSG = ''
+         IF DO_NAME THEN BEGIN 
+	              FIRSTBLOCK = 0
+		      EXT_NO = 9999
+		      ENAME = STRTRIM( STRUPCASE(EXTEN), 2 )
+		      ON_IOERROR, ALLOW_PLUN
+		      POINT_LUN, -UNIT, DUM
+		      ON_IOERROR, NULL
+         ENDIF ELSE BEGIN 
+	              FIRSTBLOCK = 1
+		      EXT_NO = EXTEN
+	ENDELSE 	            
+		
+        FOR I = 1, EXT_NO DO BEGIN
+               
+;
+;  Read the next header, and get the number of bytes taken up by the data.
+; 
+
+                IF EOF(UNIT) THEN BEGIN 
+		    IF DO_NAME THEN ERRMSG = $
+	'Extension name ' + ename + ' not found in FITS file' ELSE ERRMSG = $	    
+	'EOF encountered while moving to specified extension'
+	        if PRINT_ERROR then message,errmsg	
+		RETURN, -1
+		ENDIF
+           
+                ; Can't use FXHREAD to read from pipe, since it uses
+                ; POINT_LUN.  So we read this in ourselves using mrd_hread
+
+                MRD_HREAD, UNIT, HEADER, STATUS, SILENT = Silent, $
+		    FIRSTBLOCK=FIRSTBLOCK, ERRMSG = ERRMSG
+		   
+                IF STATUS LT 0 THEN BEGIN 
+		    IF PRINT_ERROR THEN MESSAGE,ERRMSG   ;Typo fix 04/10
+		    RETURN, -1
+		ENDIF    
+                        
+                ; Get parameters that determine size of data
+                ; region.
+                IF DO_NAME THEN IF I GT 1 THEN BEGIN
+		       EXTNAME = STRTRIM(SXPAR(HEADER,'EXTNAME',COUNT=N_name),2)
+			 if N_NAME GT 0 THEN $
+			  IF ENAME EQ STRUPCASE(EXTNAME) THEN BEGIN
+			        EXT_NO= I-1
+				BLOCK = 1 + ((N_ELEMENTS(HEADER)-1)/36)
+				POINT_LUN, -UNIT, CURR_POSS
+				POINT_LUN, UNIT, CURR_POSS - BLOCK*2880 
+			        BREAK
+			ENDIF	
+		ENDIF	         
+                BITPIX = FXPAR(HEADER,'BITPIX')
+                NAXIS  = FXPAR(HEADER,'NAXIS')
+                GCOUNT = FXPAR(HEADER,'GCOUNT') 
+                IF GCOUNT EQ 0 THEN GCOUNT = 1
+                PCOUNT = FXPAR(HEADER,'PCOUNT')
+                
+                IF NAXIS GT 0 THEN BEGIN 
+                        DIMS = FXPAR(HEADER,'NAXIS*')           ;Read dimensions
+			NDATA = PRODUCT(DIMS,/INTEGER) 
+                ENDIF ELSE NDATA = 0
+                
+                NBYTES = LONG64(ABS(BITPIX) / 8) * GCOUNT * (PCOUNT + NDATA)
+;
+;  Move to the next extension header in the file.
+;
+                NREC = (NBYTES + 2879) / 2880
+                MRD_SKIP, UNIT, NREC*2880L 
+
+        ENDFOR
+	        
+        RETURN, 0
+ALLOW_PLUN:
+        
+        ERRMSG =  $
+	'Extension name cannot be specified unless POINT_LUN access is available'
+	if PRINT_ERROR then message,errmsg	
+	RETURN, -1        
+END
diff --git a/Code/script_idl_mv/astrolib/fxpar.pro b/Code/script_idl_mv/astrolib/fxpar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a456b5a107e5e57f08d3857db132648e1920b799
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxpar.pro
@@ -0,0 +1,462 @@
+        FUNCTION FXPAR, HDR, NAME, ABORT, COUNT=MATCHES, COMMENT=COMMENTS, $
+                        START=START, PRECHECK=PRECHECK, POSTCHECK=POSTCHECK, $
+                        NOCONTINUE = NOCONTINUE, DATATYPE=DATATYPE, $
+                        NULL=K_NULL, NAN=NAN, MISSING=MISSING
+;+
+; NAME: 
+;        FXPAR()
+; PURPOSE: 
+;       Obtain the value of a parameter in a FITS header.
+; EXPLANATION: 
+;       The first 8 chacters of each element of HDR are searched for a match to
+;       NAME.  If the keyword is one of those allowed to take multiple values
+;       ("HISTORY", "COMMENT", or "        " (blank)), then the value is taken
+;       as the next 72 characters.  Otherwise, it is assumed that the next
+;       character is "=", and the value (and optional comment) is then parsed
+;       from the last 71 characters.  An error occurs if there is no parameter
+;       with the given name.
+;      
+;       If the value is too long for one line, it may be continued on to the
+;       the next input card, using the CONTINUE Long String Keyword convention.
+;       For more info, http://fits.gsfc.nasa.gov/registry/continue_keyword.html
+;       
+;
+;       Complex numbers are recognized as two numbers separated by one or more
+;       space characters.
+;
+;       If a numeric value has no decimal point (or E or D) it is returned as
+;       type LONG.  If it contains more than 8 numerals, or contains the
+;       character 'D', then it is returned as type DOUBLE.  Otherwise it is
+;       returned as type FLOAT.    If an integer is too large to be stored as
+;       type LONG, then it is returned as DOUBLE.
+;
+;       If a keyword is in the header and has no value, then the default
+;       missing value is returned as explained below.  This can be
+;       distinguished from the case where the keyword is not found by the fact
+;       that COUNT=0 in that case, while existing keywords without a value will
+;       be returned with COUNT=1 or more.
+;
+; CALLING SEQUENCE: 
+;       Result = FXPAR( HDR, NAME  [, ABORT, COUNT=, COMMENT=, /NOCONTINUE ] )
+;
+;       Result = FXPAR(HEADER,'DATE')           ;Finds the value of DATE
+;       Result = FXPAR(HEADER,'NAXIS*')         ;Returns array dimensions as
+;                                               ;vector
+; REQUIRED INPUTS: 
+;       HDR     = FITS header string array (e.g. as returned by FXREAD).  Each
+;                 element should have a length of 80 characters
+;       NAME    = String name of the parameter to return.  If NAME is of the
+;                 form 'keyword*' then an array is returned containing values
+;                 of keywordN where N is an integer.  The value of keywordN
+;                 will be placed in RESULT(N-1).  The data type of RESULT will
+;                 be the type of the first valid match of keywordN
+;                 found, unless DATATYPE is given.
+; OPTIONAL INPUT: 
+;       ABORT   = String specifying that FXPAR should do a RETALL if a
+;                 parameter is not found.  ABORT should contain a string to be
+;                 printed if the keyword parameter is not found.  If not
+;                 supplied, FXPAR will return with a negative !err if a keyword
+;                 is not found.
+; OUTPUT: 
+;       The returned value of the function is the value(s) associated with the
+;       requested keyword in the header array.
+;
+;       If the parameter is complex, double precision, floating point, long or
+;       string, then the result is of that type.  Apostrophes are stripped from
+;       strings.  If the parameter is logical, 1 is returned for T, and 0 is
+;       returned for F.
+;
+;       If NAME was of form 'keyword*' then a vector of values are returned.
+;
+; OPTIONAL INPUT KEYWORDS: 
+;       DATATYPE = A scalar value, indicating the type of vector
+;                  data.  All keywords will be cast to this type.
+;                  Default: based on first keyword.
+;                  Example: DATATYPE=0.0D (cast data to double precision)
+;       START   = A best-guess starting position of the sought-after
+;                 keyword in the header.  If specified, then FXPAR
+;                 first searches for scalar keywords in the header in
+;                 the index range bounded by START-PRECHECK and
+;                 START+POSTCHECK.  This can speed up keyword searches
+;                 in large headers.  If the keyword is not found, then
+;                 FXPAR searches the entire header.  
+;
+;                 If not specified then the entire header is searched.
+;                 Searches of the form 'keyword*' also search the
+;                 entire header and ignore START.
+;
+;                 Upon return START is changed to be the position of
+;                 the newly found keyword.  Thus the best way to
+;                 search for a series of keywords is to search for
+;                 them in the order they appear in the header like
+;                 this:
+;
+;                       START = 0L
+;                       P1 = FXPAR('P1', START=START)
+;                       P2 = FXPAR('P2', START=START)
+;
+;       PRECHECK = If START is specified, then PRECHECK is the number
+;                  of keywords preceding START to be searched.
+;                  Default: 5
+;       POSTCHECK = If START is specified, then POSTCHECK is the number
+;                   of keywords after START to be searched.
+;                   Default: 20
+;       /NOCONTINUE = If set, then continuation lines will not be read, even
+;                 if present in the header
+;       MISSING = By default, this routine returns 0 when keyword values are
+;                 not found.  This can be overridden by using the MISSING
+;                 keyword, e.g. MISSING=-1.
+;       /NAN    = If set, then return Not-a-Number (!values.f_nan) for missing
+;                 values.  Ignored if keyword MISSING is present.
+;       /NULL   = If set, then return !NULL (undefined) for missing values.
+;                 Ignored if MISSING of /NAN is present, or if earlier than IDL
+;                 version 8.0.  If multiple values would be returned, then
+;                 MISSING= or /NAN should be used instead of /NULL, making sure
+;                 that the datatype is consistent with the non-missing values,
+;                 e.g. MISSING='' for strings, MISSING=-1 for integers, or
+;                 MISSING=-1.0 or /NAN for floating point.  /NAN should not be
+;                 used if the datatype would otherwise be integer.
+; OPTIONAL OUTPUT KEYWORD:
+;       COUNT   = Optional keyword to return a value equal to the number of
+;                 parameters found by FXPAR.
+;       COMMENTS= Array of comments associated with the returned values.
+;
+; PROCEDURE CALLS: 
+;       GETTOK(), VALID_NUM
+; SIDE EFFECTS: 
+;
+;       The system variable !err is set to -1 if parameter not found, 0 for a
+;       scalar value returned.  If a vector is returned it is set to the number
+;       of keyword matches found.
+;
+;       If a keyword occurs more than once in a header, a warning is given,
+;       and the first occurence is used.  However, if the keyword is "HISTORY",
+;       "COMMENT", or "        " (blank), then multiple values are returned.
+;
+; NOTES:
+;	The functions SXPAR() and FXPAR() are nearly identical, although
+;	FXPAR() has slightly more sophisticated parsing.   There is no
+;	particular reason for having two nearly identical procedures, but
+;	both are too widely used to drop either one.
+;
+; REVISION HISTORY: 
+;       Version 1, William Thompson, GSFC, 12 April 1993.
+;               Adapted from SXPAR
+;       Version 2, William Thompson, GSFC, 14 October 1994
+;               Modified to use VALID_NUM instead of STRNUMBER.  Inserted
+;               additional call to VALID_NUM to trap cases where character
+;               strings did not contain quotation marks.
+;       Version 3, William Thompson, GSFC, 22 December 1994
+;               Fixed bug with blank keywords, following suggestion by Wayne
+;               Landsman.
+;       Version 4, Mons Morrison, LMSAL, 9-Jan-98
+;               Made non-trailing ' for string tag just be a warning (not
+;               a fatal error).  It was needed because "sxaddpar" had an
+;               error which did not write tags properly for long strings
+;               (over 68 characters)
+;       Version 5, Wayne Landsman GSFC, 29 May 1998
+;               Fixed potential problem with overflow of LONG values
+;       Version 6, Craig Markwardt, GSFC, 28 Jan 1998, 
+;               Added CONTINUE parsing         
+;       Version 7, Craig Markwardt, GSFC, 18 Nov 1999,
+;               Added START, PRE/POSTCHECK keywords for better
+;               performance
+;       Version 8, Craig Markwardt, GSFC, 08 Oct 2003,
+;               Added DATATYPE keyword to cast vector keywords type
+;       Version 9, Paul Hick, 22 Oct 2003, Corrected bug (NHEADER-1)
+;       Version 10, W. Landsman, GSFC  2 May 2012
+;               Keywords of form "name_0" could confuse vector extractions
+;       Version 11 W. Landsman, GSFC 24 Apr 2014
+;               Don't convert LONG64 numbers to to double precision
+;       Version 12, William Thompson, 13-Aug-2014
+;               Add keywords MISSING, /NAN, and /NULL
+;-
+;------------------------------------------------------------------------------
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() LT 2 THEN BEGIN
+            PRINT,'Syntax:  result =  FXPAR( HDR, NAME  [, ABORT ])'
+            RETURN, -1
+        ENDIF
+;
+;  Determine the default value for missing data.
+;
+        CASE 1 OF 
+            N_ELEMENTS(MISSING) EQ 1: MISSING_VALUE = MISSING
+            KEYWORD_SET(NAN): MISSING_VALUE = !VALUES.F_NAN
+            KEYWORD_SET(K_NULL) AND !VERSION.RELEASE GE '8.': $
+              DUMMY = EXECUTE('MISSING_VALUE = !NULL')
+            ELSE: MISSING_VALUE = 0
+        ENDCASE
+        VALUE = MISSING_VALUE
+;
+;  Determine the abort condition.
+;
+        IF N_PARAMS() LE 2 THEN BEGIN
+            ABORT_RETURN = 0
+            ABORT = 'FITS Header'
+        END ELSE ABORT_RETURN = 1
+        IF ABORT_RETURN THEN ON_ERROR,1 ELSE ON_ERROR,2
+;
+;  Check for valid header.  Check header for proper attributes.
+;
+        S = SIZE(HDR)
+        IF ( S[0] NE 1 ) OR ( S[2] NE 7 ) THEN $
+            MESSAGE,'FITS Header (first parameter) must be a string array'
+;
+;  Convert the selected keyword NAME to uppercase.
+;
+        NAM = STRTRIM( STRUPCASE(NAME) )
+;
+;  Determine if NAME is of form 'keyword*'.  If so, then strip off the '*', and
+;  set the VECTOR flag.  One must consider the possibility that NAM is an empty
+;  string.
+;
+        NAMELENGTH1 = (STRLEN(NAM) - 1) > 1
+        IF STRPOS( NAM, '*' ) EQ NAMELENGTH1 THEN BEGIN    
+            NAM = STRMID( NAM, 0, NAMELENGTH1)  
+            VECTOR = 1                          ;Flag for vector output  
+            NAME_LENGTH = STRLEN(NAM)           ;Length of name 
+            NUM_LENGTH = 8 - NAME_LENGTH        ;Max length of number portion  
+            IF NUM_LENGTH LE 0 THEN MESSAGE,    $
+                'Keyword length must be 8 characters or less'
+;
+;  Otherwise, extend NAME with blanks to eight characters.
+;
+        ENDIF ELSE BEGIN
+            WHILE STRLEN(NAM) LT 8 DO NAM = NAM + ' '
+            VECTOR = 0
+        ENDELSE
+;
+;  If of the form 'keyword*', then find all instances of 'keyword' followed by
+;  a number.  Store the positions of the located keywords in NFOUND, and the
+;  value of the number field in NUMBER.
+;
+        IF N_ELEMENTS(START)     EQ 0 THEN START = -1L
+        START = LONG(START[0])
+        IF NOT VECTOR AND START GE 0 THEN BEGIN
+            IF N_ELEMENTS(PRECHECK)  EQ 0 THEN PRECHECK = 5
+            IF N_ELEMENTS(POSTCHECK) EQ 0 THEN POSTCHECK = 20
+            NHEADER = N_ELEMENTS(HDR)
+            MN = (START - PRECHECK)  > 0
+            MX = (START + POSTCHECK) < (NHEADER-1)      ;Corrected bug
+            KEYWORD = STRMID(HDR[MN:MX], 0, 8)
+        ENDIF ELSE BEGIN
+            RESTART:
+            START   = -1L
+            KEYWORD = STRMID( HDR, 0, 8)
+        ENDELSE
+
+        IF VECTOR THEN BEGIN
+            NFOUND = WHERE(STRPOS(KEYWORD,NAM) GE 0, MATCHES)
+            IF ( MATCHES GT 0 ) THEN BEGIN
+                NUMST= STRMID(HDR[NFOUND], NAME_LENGTH, NUM_LENGTH)
+                NUMBER = INTARR(MATCHES)-1
+                FOR I = 0, MATCHES-1 DO         $
+                    IF VALID_NUM( NUMST[I], NUM) THEN NUMBER[I] = NUM
+                IGOOD = WHERE(NUMBER GE 0, MATCHES)
+                IF MATCHES GT 0 THEN BEGIN
+                    NFOUND = NFOUND[IGOOD]
+                    NUMBER = NUMBER[IGOOD]
+ 		    G = WHERE(NUMBER GT 0, MATCHES)
+ 		    IF MATCHES GT 0 THEN NUMBER = NUMBER[G]     
+		ENDIF
+            ENDIF
+;
+;  Otherwise, find all the instances of the requested keyword.  If more than
+;  one is found, and NAME is not one of the special cases, then print an error
+;  message.
+;
+        ENDIF ELSE BEGIN
+            NFOUND = WHERE(KEYWORD EQ NAM, MATCHES)
+            IF MATCHES EQ 0 AND START GE 0 THEN GOTO, RESTART
+            IF START GE 0 THEN NFOUND = NFOUND + MN
+            IF (MATCHES GT 1) AND (NAM NE 'HISTORY ') AND               $
+                (NAM NE 'COMMENT ') AND (NAM NE '') THEN        $
+                MESSAGE,/INFORMATIONAL, 'WARNING- Keyword ' +   $
+                NAM + 'located more than once in ' + ABORT
+            IF (MATCHES GT 0) THEN START = NFOUND[MATCHES-1]
+        ENDELSE
+;
+;  Extract the parameter field from the specified header lines.  If one of the
+;  special cases, then done.
+;
+        IF MATCHES GT 0 THEN BEGIN
+            VALUE = MISSING_VALUE
+            LINE = HDR[NFOUND]
+            SVALUE = STRTRIM( STRMID(LINE,9,71),2)
+            IF (NAM EQ 'HISTORY ') OR (NAM EQ 'COMMENT ') OR    $
+                    (NAM EQ '        ') THEN BEGIN
+                VALUE = STRTRIM( STRMID(LINE,8,72),2)
+                COMMENTS = STRARR(N_ELEMENTS(VALUE))
+;
+;  Otherwise, test to see if the parameter contains a string, signalled by
+;  beginning with a single quote character (') (apostrophe).
+;
+            END ELSE FOR I = 0,MATCHES-1 DO BEGIN
+                IF ( STRMID(SVALUE[I],0,1) EQ "'" ) THEN BEGIN
+                    TEST = STRMID( SVALUE[I],1,STRLEN( SVALUE[I] )-1)
+                    NEXT_CHAR = 0
+                    OFF = 0
+                    VALUE = ''
+;
+;  Find the next apostrophe.
+;
+NEXT_APOST:
+                    ENDAP = STRPOS(TEST, "'", NEXT_CHAR)
+                    IF ENDAP LT 0 THEN MESSAGE,         $
+                        'WARNING: Value of '+NAME+' invalid in '+ABORT+ " (no trailing ')", /info
+                    VALUE = VALUE + STRMID( TEST, NEXT_CHAR, ENDAP-NEXT_CHAR )
+;
+;  Test to see if the next character is also an apostrophe.  If so, then the
+;  string isn't completed yet.  Apostrophes in the text string are signalled as
+;  two apostrophes in a row.
+;
+                    IF STRMID( TEST, ENDAP+1, 1) EQ "'" THEN BEGIN    
+                        VALUE = VALUE + "'"
+                        NEXT_CHAR = ENDAP+2      
+                        GOTO, NEXT_APOST
+                    ENDIF
+;
+;  Extract the comment, if any.
+;
+                    SLASH = STRPOS(TEST, "/", ENDAP)
+                    IF SLASH LT 0 THEN COMMENT = '' ELSE        $
+                        COMMENT = STRMID(TEST, SLASH+1, STRLEN(TEST)-SLASH-1)
+
+;
+; CM 19 Sep 1997
+; This is a string that could be continued on the next line.  Check this
+; possibility with the following four criteria: *1) Ends with '&'
+; (2) Next line is CONTINUE  (3) LONGSTRN keyword is present (recursive call to
+;  FXPAR) 4. /NOCONTINE is not set
+
+    IF NOT KEYWORD_SET(NOCONTINUE) THEN BEGIN
+                    OFF = OFF + 1
+                    VAL = STRTRIM(VALUE,2)
+
+                    IF (STRLEN(VAL) GT 0) AND $
+                      (STRMID(VAL, STRLEN(VAL)-1, 1) EQ '&') AND $
+                      (STRMID(HDR[NFOUND[I]+OFF],0,8) EQ 'CONTINUE') THEN BEGIN
+                       IF (SIZE(FXPAR(HDR, 'LONGSTRN',/NOCONTINUE)))[1] EQ 7 THEN BEGIN                    
+                      VALUE = STRMID(VAL, 0, STRLEN(VAL)-1)
+                      TEST = HDR[NFOUND[I]+OFF]
+                      TEST = STRMID(TEST, 8, STRLEN(TEST)-8)
+                      TEST = STRTRIM(TEST, 2)
+                      IF STRMID(TEST, 0, 1) NE "'" THEN MESSAGE, $
+                        'ERROR: Invalidly CONTINUEd string in '+ABORT
+                      NEXT_CHAR = 1
+                      GOTO, NEXT_APOST
+                    ENDIF
+                   ENDIF
+    ENDIF
+
+;
+;  If not a string, then separate the parameter field from the comment field.
+;  If there is no value field, then use the default "missing" value.
+;
+                ENDIF ELSE BEGIN
+                    VALUE = MISSING_VALUE
+                    TEST = SVALUE[I]
+                    IF TEST EQ '' THEN BEGIN
+                        COMMENT = ''
+                        GOTO, GOT_VALUE
+                    ENDIF
+                    SLASH = STRPOS(TEST, "/")
+                    IF SLASH GE 0 THEN BEGIN
+                        COMMENT = STRMID(TEST, SLASH+1, STRLEN(TEST)-SLASH-1)
+                        IF SLASH GT 0 THEN TEST = STRMID(TEST, 0, SLASH) ELSE $
+                            GOTO, GOT_VALUE
+                    END ELSE COMMENT = ''
+;
+;  Find the first word in TEST.  Is it a logical value ('T' or 'F')?
+;
+                    TEST2 = TEST
+                    VALUE = GETTOK(TEST2,' ')
+                    TEST2 = STRTRIM(TEST2,2)
+                    IF ( VALUE EQ 'T' ) THEN BEGIN
+                        VALUE = 1
+                    END ELSE IF ( VALUE EQ 'F' ) THEN BEGIN
+                        VALUE = 0
+                    END ELSE BEGIN
+;
+;  Test to see if a complex number.  It's a complex number if the value and the
+;  next word, if any, both are valid numbers.
+;
+                        IF STRLEN(TEST2) EQ 0 THEN GOTO, NOT_COMPLEX
+                        VALUE2 = GETTOK(TEST2,' ')
+                        IF VALID_NUM(VALUE,VAL1) AND VALID_NUM(VALUE2,VAL2) $
+                                THEN BEGIN
+                            VALUE = COMPLEX(VAL1,VAL2)
+                            GOTO, GOT_VALUE
+                        ENDIF
+;
+;  Not a complex number.  Decide if it is a floating point, double precision,
+;  or integer number.  If an error occurs, then a string value is returned.
+;  If the integer is not within the range of a valid long value, then it will 
+;  be converted to a double.  
+;
+NOT_COMPLEX:
+                        ON_IOERROR, GOT_VALUE
+                        VALUE = TEST
+                        IF NOT VALID_NUM(VALUE) THEN GOTO, GOT_VALUE
+                        IF (STRPOS(VALUE,'.') GE 0) OR (STRPOS(VALUE,'E') $
+                                GE 0) OR (STRPOS(VALUE,'D') GE 0) THEN BEGIN
+                            IF ( STRPOS(VALUE,'D') GT 0 ) OR $
+                                    ( STRLEN(VALUE) GE 8 ) THEN BEGIN
+                                VALUE = DOUBLE(VALUE)
+                                END ELSE VALUE = FLOAT(VALUE)
+                        ENDIF ELSE BEGIN
+                            LMAX = 2.0D^31 - 1.0D
+                            LMIN = -2.0D^31       ;Typo fixed Feb 2010
+                            VALUE = LONG64(VALUE)
+                            if (VALUE GE LMIN) and (VALUE LE LMAX) THEN $
+                                VALUE = LONG(VALUE)
+                        ENDELSE
+                            
+;
+GOT_VALUE:
+                        ON_IOERROR, NULL
+                    ENDELSE
+                ENDELSE         ; if string
+;
+;  Add to vector if required.
+;
+                IF VECTOR THEN BEGIN
+                    MAXNUM = MAX(NUMBER)
+                    IF ( I EQ 0 ) THEN BEGIN
+                        IF N_ELEMENTS(DATATYPE) EQ 0 THEN BEGIN
+                            ;; Data type determined from keyword
+                            SZ_VALUE = SIZE(VALUE)
+                        ENDIF ELSE BEGIN
+                            ;; Data type requested by user
+                            SZ_VALUE = SIZE(DATATYPE[0])
+                        ENDELSE
+                        RESULT = MAKE_ARRAY( MAXNUM, TYPE=SZ_VALUE[1])
+                        COMMENTS = STRARR(MAXNUM)
+                    ENDIF 
+                    RESULT[   NUMBER[I]-1 ] =  VALUE
+                    COMMENTS[ NUMBER[I]-1 ] =  COMMENT
+                ENDIF ELSE BEGIN
+                    COMMENTS = COMMENT
+                ENDELSE
+            ENDFOR
+;
+;  Set the value of !ERR for the number of matches for vectors, or simply 0
+;  otherwise.
+;
+            IF VECTOR THEN BEGIN
+                !ERR = MATCHES
+                RETURN, RESULT
+            ENDIF ELSE !ERR = 0
+;
+;  Error point for keyword not found.
+;
+        ENDIF ELSE BEGIN
+            IF ABORT_RETURN THEN MESSAGE,'Keyword '+NAM+' not found in '+ABORT
+            !ERR = -1
+        ENDELSE
+;
+        RETURN, VALUE
+        END
diff --git a/Code/script_idl_mv/astrolib/fxparpos.pro b/Code/script_idl_mv/astrolib/fxparpos.pro
new file mode 100644
index 0000000000000000000000000000000000000000..eb3b0ec573c1bb260a875caeb681d16a0f82f669
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxparpos.pro
@@ -0,0 +1,85 @@
+	FUNCTION FXPARPOS, KEYWRD, IEND, BEFORE=BEFORE, AFTER=AFTER
+;+
+; NAME: 
+;	FXPARPOS()
+; Purpose     : 
+;	Finds position to insert record into FITS header.
+; Explanation : 
+;	Finds the position to insert a record into a FITS header.  Called from
+;	FXADDPAR.
+; Use         : 
+;	Result = FXPARPOS(KEYWRD, IEND  [, BEFORE=BEFORE ]  [, AFTER=AFTER ])
+; Inputs      : 
+;	KEYWRD	= Array of eight-character keywords in header.
+;	IEND	= Position of END keyword.
+; Opt. Inputs : 
+;	None.
+; Outputs     : 
+;	Result of function is position to insert record.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	BEFORE	= Keyword string name.  The parameter will be placed before the
+;		  location of this keyword.  For example, if BEFORE='HISTORY'
+;		  then the parameter will be placed before the first history
+;		  location.  This applies only when adding a new keyword;
+;		  keywords already in the header are kept in the same position.
+;
+;	AFTER	= Same as BEFORE, but the parameter will be placed after the
+;		  location of this keyword.  This keyword takes precedence over
+;		  BEFORE.
+;
+;	If neither BEFORE or AFTER keywords are passed, then IEND is returned.
+;
+; Calls       : 
+;	None.
+; Common      : 
+;	None.
+; Restrictions: 
+;	KEYWRD and IEND must be consistent with the relevant FITS header.
+; Side effects: 
+;	None.
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	William Thompson, Jan 1992.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+; Version     : 
+;	Version 1, 12 April 1993.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+	ON_ERROR,2				;Return to caller
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 2 THEN MESSAGE,	$
+		'Required parameters are KEYWRD and IEND'
+;
+;  If the AFTER keyword has been entered, then find the location.
+;
+	IF N_ELEMENTS(AFTER) EQ 1 THEN BEGIN
+		KEY_AFTER = STRING(REPLICATE(32B,8))
+		STRPUT,KEY_AFTER,STRUPCASE(STRTRIM(AFTER,2)),0
+		ILOC = WHERE(KEYWRD EQ KEY_AFTER,NLOC)
+		IF NLOC GT 0 THEN RETURN, (MAX(ILOC)+1) < IEND
+	ENDIF
+;
+;  If AFTER wasn't entered or found, and if the BEFORE keyword has been
+;  entered, then find the location.
+;
+	IF N_ELEMENTS(BEFORE) EQ 1 THEN BEGIN
+		KEY_BEFORE = STRING(REPLICATE(32B,8))
+		STRPUT,KEY_BEFORE,STRUPCASE(STRTRIM(BEFORE,2)),0
+		ILOC = WHERE(KEYWRD EQ KEY_BEFORE,NLOC)
+		IF NLOC GT 0 THEN RETURN,ILOC[0]
+	ENDIF
+;
+;  Otherwise, simply return IEND.
+;
+	RETURN,IEND
+	END
diff --git a/Code/script_idl_mv/astrolib/fxposit.pro b/Code/script_idl_mv/astrolib/fxposit.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ba2263dab7c59e352c1d190ca14f778f896e6f07
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxposit.pro
@@ -0,0 +1,267 @@
+        FUNCTION FXPOSIT, XFILE, EXT_NO, readonly=readonly, COMPRESS=COMPRESS, $
+                 SILENT = Silent, EXTNUM = extnum, ERRMSG= ERRMSG, $
+		 LUNIT = lunit, UNIXPIPE= unixpipe, FPACK= fpack, $
+		 NO_FPACK = no_fpack,HEADERONLY=headeronly
+;+
+; NAME:
+;     FXPOSIT
+; PURPOSE:
+;     Return the unit number of a FITS file positioned at specified extension
+; EXPLANATION:
+;     The FITS file will be ready to be read at the beginning of the 
+;     specified extension.    Either an extension number or extension name
+;     can be specified.   Called by headfits.pro, mrdfits.pro
+;
+;     Modified in March 2009 to set the /SWAP_IF_LITTLE_ENDIAN keyword
+;     when opening a file, and **may not be compatible with earlier versions**
+; CALLING SEQUENCE:
+;     unit=FXPOSIT(FILE, EXT_NO_OR_NAME, /READONLY, COMPRESS=program, 
+;                       UNIXPIPE=, ERRMSG= , EXTNUM= , UNIT=, /SILENT
+;                        /FPACK, /NO_FPACK
+;
+; INPUT PARAMETERS:
+;     FILE    = FITS file name, scalar string.    If an empty string is supplied
+;              then the user will be prompted for the file name.   The user
+;              will also be prompted if a wild card is supplied, and more than 
+;              one file matches the wildcard.
+;     EXT_NO_OR_NAME  = Either the extension to be moved to (scalar 
+;               nonnegative integer) or the name of the extension to read 
+;               (scalar string)
+;
+; RETURNS:
+;     Unit number of file or -1 if an error is detected.
+;
+; OPTIONAL INPUT KEYWORD PARAMETER:
+;     COMPRESS - If this keyword is set and non-zero, then then treat
+;                the file as compressed.  If 1 assume a gzipped file.
+;                and use IDLs internal decompression facility.    For Unix 
+;                compressed or bzip2 compressed files spawn off a process to 
+;                decompress and use its output as the FITS stream.  If the 
+;                keyword is not 1, then use its value as a string giving the 
+;                command needed for decompression.
+;     /FPACK - Signal that the file is compressed with the FPACK software. 
+;               http://heasarc.gsfc.nasa.gov/fitsio/fpack/ ) By default, 
+;               (FXPOSIT will assume that if the file name extension ends in 
+;              .fz that it is fpack compressed.)     The FPACK software must
+;               be installed on the system 
+;     /NO_FPACK - The unit will only be used to read the FITS header.  In
+;                 that case FPACK compressed files need not be uncompressed.
+;      LUNIT -    Integer giving the file unit number.    Use this keyword if
+;                you want to override the default use of GET_LUN to obtain
+;                a unit number.
+;     /READONLY - If this keyword is set and non-zero, then OPENR rather 
+;                than OPENU will be used to open the FITS file.    Note that
+;                 compressed files are always set to /READONLY
+;     /SILENT    If set, then suppress any messages about invalid characters
+;                in the FITS file.
+;
+; OPTIONAL OUTPUT KEYWORDS:
+;       EXTNUM - Nonnegative integer give the extension number actually read
+;               Useful only if the extension was specified by name.
+;       ERRMSG  = If this keyword is present, then any error messages will be
+;                 returned to the user in this parameter rather than
+;                 depending on the MESSAGE routine in IDL.  If no errors are
+;                 encountered, then a null string is returned.
+;       UNIXPIPE - If set to 1, then the FITS file was opened with a UNIX pipe
+;                rather than with the OPENR command.    This is only required 
+;                 when reading a FPACK, bzip or Unix compressed file.   Note 
+;                 that automatic byteswapping cannnot be set for a Unix pipe, 
+;                 since the SWAP_IF_LITTLE_ENDIAN keyword is only available for the
+;                 OPEN command, and it is the responsibility of the calling 
+;                 routine to perform the byteswapping.
+; SIDE EFFECTS:
+;      Opens and returns a file unit.
+; PROCEDURE:
+;      Open the appropriate file, or spawn a command and intercept
+;      the output.
+;      Call FXMOVE to get to the appropriate extension.
+; PROCEDURE CALLS:
+;      FXMOVE()
+; MODIFICATION HISTORY:
+;      Derived from William Thompson's FXFINDEND routine.
+;      Modified by T.McGlynn, 5-October-1994.
+;       Modified by T.McGlynn, 25-Feb-1995 to handle compressed
+;          files.  Pipes cannot be accessed using FXHREAD so
+;          MRD_HREAD was written.
+;       W. Landsman 23-Apr-1997    Force the /bin/sh shell when uncompressing 
+;       T. McGlynn  03-June-1999   Use /noshell option to get rid of processes left by spawn.
+;                                  Use findfile to retain ability to use wildcards
+;       W. Landsman 03-Aug-1999    Use EXPAND_TILDE under Unix to find file
+;       T. McGlynn  04-Apr-2000    Put reading code into FXMOVE,
+;                                  additional support for compression from D.Palmer.
+;       W. Landsman/D.Zarro 04-Jul-2000    Added test for !VERSION.OS EQ 'Win32' (WinNT)
+;       W. Landsman    12-Dec-2000 Added /SILENT keyword
+;       W. Landsman April 2002     Use FILE_SEARCH for V5.5 or later
+;       W. Landsman Feb 2004       Assume since V5.3 (OPENR,/COMPRESS available)
+;       W. Landsman,W. Thompson, 2-Mar-2004, Add support for BZIP2 
+;       W. Landsman                Don't leave open file if an error occurs
+;       W. Landsman  Sep 2004      Treat FTZ extension as gzip compressed
+;       W. Landsman  Feb 2006      Removed leading spaces (prior to V5.5)
+;       W. Landsman  Nov 2006      Allow specification of extension name
+;                                  Added EXTNUM, ERRMSG keywords
+;       W. Landsman/N.Piskunov Dec 2007  Added LUNIT keyword
+;       W. Landsman     Mar 2009   OPEN with /SWAP_IF_LITTLE_ENDIAN
+;                                  Added UNIXPIPE output keyword
+;       N. Rich        May 2009    Check if filename is an empty string
+;       W. Landsman   May 2009     Support FPACK compressed files
+;                                  Added /FPACK, /HEADERONLY keywords
+;       W.Landsman    July 2009    Deprecated /HEADERONLY add /NO_FPACK
+;       W.Landsman    July 2011    Check for SIMPLE in first 8 chars 
+;               Use gunzip to decompress Unix. Z file since compress utility 
+;               often not installed anymore)
+;       W. Landsman   October 2012 Add .fz extension if /FPACK set
+;       W. Landsman   July 2013    More diagnostics if file not found
+;-
+;
+        On_Error,2
+        compile_opt idl2  
+;
+;  Check the number of parameters.
+;
+        IF N_Params() LT 2 THEN BEGIN 
+            PRINT,'SYNTAX:  UNIT = FXPOSIT(FILE, EXT_NO, /Readonly,' + $
+	                   'ERRMSG= , /SILENT, compress=prog, LUNIT = lunit)'
+            RETURN,-1
+        ENDIF
+        PRINTERR = ~ARG_PRESENT(ERRMSG)
+	ERRMSG = ''
+	UNIXPIPE=0
+; The /headeronly keyword has been replaced with /no_fpack	
+        if ~keyword_set(no_fpack) then no_fpack = keyword_set(headeronly)
+	exten = ext_no
+
+   	COUNT=0
+	IF XFILE[0] NE '' THEN BEGIN 
+             FILE = FILE_SEARCH(XFILE, COUNT=COUNT)  
+	     IF COUNT GT 1 THEN $
+	          FILE = DIALOG_PICKFILE(FILTER=XFILE, /MUST_EXIST, $
+		         TITLE = 'Please select a FITS file') $	 		 
+	    ELSE IF COUNT EQ 0 THEN BEGIN 
+	        ERRMSG = 'Specified FITS file not found: ' + XFILE[0]
+	        IF PRINTERR THEN MESSAGE,ERRMSG,/CON 
+                RETURN, -1   ; Don't print anything out, just report an error
+	     ENDIF 
+	ENDIF ELSE $
+             FILE =DIALOG_PICKFILE(FILTER=['*.fit*;*.fts*;*.img*;*.FIT*'], $
+	           TITLE='Please select a FITS file',/MUST_EXIST)
+
+            IF FILE[0] EQ '' THEN BEGIN
+	      ERRMSG = 'No FITS file specified '   
+	    IF PRINTERR THEN MESSAGE,ERRMSG,/CON 
+            RETURN, -1   ; Don't print anything out, just report an error
+	ENDIF    
+                   
+        FILE = FILE[0]
+	IF KEYWORD_SET(FPACK) then $
+	    if strlowcase(strmid(FILE,2,3,/reverse)) NE '.fz' then $
+	    FILE += '.fz'
+	    
+;
+;  Check if logical unit number is specified explicitly.
+;
+        IF KEYWORD_SET(LUNIT) THEN BEGIN 
+	   UNIT=LUNIT 
+	   GLUN = 0
+	ENDIF ELSE BEGIN 
+	    UNIT = -1
+            GLUN = 1
+       ENDELSE
+; 
+;  Check if this is a compressed file.
+;
+        UCMPRS = ' '
+	IF KEYWORD_SET(compress) THEN BEGIN
+	    IF strcompress(string(compress),/remo) eq '1' THEN BEGIN
+	        compress = 'gunzip'
+	    ENDIF
+	    UCMPRS = compress;
+	ENDIF ELSE IF KEYWORD_SET(FPACK) THEN $
+	    UCMPRS = 'funpack'      $
+	ELSE BEGIN
+        
+            LEN = STRLEN(FILE)
+            IF LEN GT 3 THEN $
+	        tail = STRLOWCASE(STRMID(file, len-3, 3))  $
+	    ELSE tail = ' '
+	    
+            IF STRMID(tail,1,2) EQ '.z'  THEN $
+                UCMPRS = 'gunzip'   $
+	    ELSE IF (tail EQ '.gz') || (tail EQ 'ftz') THEN $
+	        UCMPRS = 'gzip'       $
+	    ELSE IF tail EQ 'bz2' THEN $
+	        UCMPRS = 'bunzip2'     $
+	    ELSE IF ~KEYWORD_SET(NO_FPACK) THEN $
+	          IF tail EQ '.fz' THEN UCMPRS = 'funpack'	
+	    
+	ENDELSE
+
+;  Handle compressed files which are always opened for Read only.
+
+	IF UCMPRS EQ 'gzip' THEN BEGIN
+	        
+                OPENR, UNIT, FILE, /COMPRESS, GET_LUN=glun, ERROR = ERROR, $
+		           /SWAP_IF_LITTLE       
+                IF ERROR NE 0 THEN BEGIN
+                        IF PRINTERR THEN PRINT,!ERROR_STATE.MSG ELSE $
+			    ERRMSG = !ERROR_STATE.MSG 
+                        RETURN,-1
+                ENDIF
+ 
+	ENDIF ELSE IF UCMPRS NE ' ' THEN BEGIN
+; Handle FPACK compressed file.        If an extension name is supplied then
+; first recursively call FXPOSIT to get the extension number.    Then open 
+; the bidirectional pipe. 	
+		        if UCMPRS EQ 'funpack' then begin
+			if size(exten,/TNAME) EQ 'STRING' THEN BEGIN
+			unit = fxposit( file, ext_no, /no_fpack,extnum=extnum)
+			free_lun,unit
+			exten = extnum
+			endif 
+			SPAWN, [UCMPRS,'-S',FILE], UNIT=UNIT, /NOSHELL 
+			ENDIF else $
+                        SPAWN, [UCMPRS,'-c',FILE], UNIT=UNIT, /NOSHELL
+			UNIXPIPE = 1
+                  
+        ENDIF ELSE BEGIN
+;
+;  Go to the start of the file.
+;
+                IF KEYWORD_SET(READONLY) THEN $
+                    OPENR, UNIT, FILE, GET_LUN=glun, ERROR = ERROR, $
+                           /SWAP_IF_LITTLE ELSE                     $
+                    OPENU, UNIT, FILE, GET_LUN=glun, ERROR = ERROR, $
+                           /SWAP_IF_LITTLE 
+
+                IF ERROR NE 0 THEN BEGIN
+                        IF PRINTERR THEN PRINT,!ERROR_STATE.MSG ELSE $
+			    ERRMSG = !ERROR_STATE.MSG 
+                        RETURN,-1
+                ENDIF
+        ENDELSE
+	
+        IF SIZE(EXT_NO,/TNAME) NE 'STRING' THEN $
+	      IF EXT_NO LE 0 THEN RETURN, UNIT
+
+;For Uncompresed files test that the first 8 characters are 'SIMPLE'
+
+        IF ucmprs EQ ' ' THEN BEGIN
+          simple = BytArr(6)
+	  READU,unit,simple
+          if string(simple) NE 'SIMPLE' then begin 
+                IF ~KEYWORD_SET(LUNIT) THEN Free_Lun, unit
+	        ERRMSG = "ERROR - FITS File must begin with 'SIMPLE'" 
+		if printerr THEN MESSAGE,errmsg,/CON
+		return,-1
+           endif 	
+	point_lun,unit,0    	
+	endif
+	
+	stat = FXMOVE(unit, exten, SILENT = Silent, EXT_NO = extnum, $
+	ERRMSG=errmsg)
+
+	IF stat LT 0 THEN BEGIN
+            IF ~KEYWORD_SET(LUNIT) THEN Free_Lun, unit
+	    IF PrintErr THEN MESSAGE,ErrMsg
+	    RETURN, stat
+	ENDIF ELSE RETURN, unit
+END
diff --git a/Code/script_idl_mv/astrolib/fxread.pro b/Code/script_idl_mv/astrolib/fxread.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d609ff24cb8402ff990be5da179b929e9d82c50f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxread.pro
@@ -0,0 +1,588 @@
+	PRO FXREAD, FILENAME, DATA, HEADER, P1, P2, P3, P4, P5,     $
+		NANVALUE=NANVALUE, PROMPT=PROMPT, AVERAGE=AVERAGE,	$
+		YSTEP=Y_STEP, NOSCALE=NOSCALE, NOUPDATE=NOUPDATE,	$
+		ERRMSG=ERRMSG, NODATA=NODATA, COMPRESS = COMPRESS,      $
+		EXTENSION=EXTENSION0
+;+
+; NAME: 
+;	FXREAD
+; Purpose     : 
+;	Read basic FITS files.
+; Explanation : 
+;	Read an image array from a disk FITS file.  Optionally allows the
+;	user to read in only a subarray and/or every Nth pixel.
+; Use         : 
+;	FXREAD, FILENAME, DATA  [, HEADER  [, I1, I2  [, J1, J2 ]]  [, STEP]]
+; Inputs      : 
+;	FILENAME = String containing the name of the file to be read.
+; Opt. Inputs : 
+;	I1,I2	 = Data range to read in the first dimension.  If passed, then
+;		   HEADER must also be passed.  If not passed, or set to -1,-1,
+;		   then the entire range is read.
+;	J1,J2	 = Data range to read in the second dimension.  If passed, then
+;		   HEADER and I1,J2 must also be passed.  If not passed, or set
+;		   to -1,-1, then the entire range is read.
+;	STEP	 = Step size to use in reading the data.  If passed, then
+;		   HEADER must also be passed.  Default value is 1.  Ignored if
+;		   less than 1.
+; Outputs     : 
+;	DATA	 = Data array to be read from the file.
+; Opt. Outputs: 
+;	HEADER	 = String array containing the header for the FITS file.
+; Keywords    : 
+;       /COMPRESS - If this keyword is set and non-zero, then then treat
+;                the file as gzip compressed.    By default FXREAD assumes
+;                the file is gzip compressed if it ends in ".gz"
+;	NANVALUE = Value signalling data dropout.  All points corresponding to
+;		   IEEE NaN (not-a-number) are set to this value.  Ignored
+;		   unless DATA is of type float or double-precision.
+;       EXTENSION = FITS extension.  It can be a scalar integer,
+;                indicating the extension number (extension number 0
+;                is the primary HDU).  It can also be a scalar string,
+;                indicating the extension name (EXTNAME keyword).
+;                Default: 0 (primary HDU)
+;	PROMPT	 = If set, then the optional parameters are prompted for at the
+;		   keyboard.
+;	AVERAGE	 = If set, then the array size is reduced by averaging pixels
+;		   together rather than by subselecting pixels.  Ignored unless
+;		   STEP is nontrivial.  Note:  this is much slower.
+;	YSTEP	 = If passed, then STEP is the step size in the 1st dimension,
+;		   and YSTEP is the step size in the 2nd dimension.  Otherwise,
+;		   STEP applies to both directions.
+;	NOSCALE	 = If set, then the output data will not be scaled using the
+;		   optional BSCALE and BZERO keywords in the FITS header.
+;		   Default is to scale, if and only if BSCALE and BZERO are
+;		   present and nontrivial.
+;	NOUPDATE = If set, then the optional BSCALE and BZERO keywords in the
+;		   optional HEADER array will not be changed.  The default is
+;		   to reset these keywords to BSCALE=1, BZERO=0.  Ignored if
+;		   NOSCALE is set.
+;	ERRMSG   = If defined and passed, then any error messages will be
+;		   returned to the user in this parameter rather than
+;		   depending on the MESSAGE routine in IDL.  If no errors are
+;		   encountered, then a null string is returned.  In order to
+;		   use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXREAD, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;       NODATA   = If set, then the array is not read in, but the
+;                  primary header is read.
+;
+; Calls       : 
+;	GET_DATE, FXADDPAR, FXHREAD, FXPAR, WHERENAN
+; Common      : 
+;	None.
+; Restrictions: 
+;	Groups are not supported.
+;
+;	The optional parameters I1, I2, and STEP only work with one or
+;	two-dimensional arrays.  J1 and J2 only work with two-dimensional
+;	arrays.
+;
+;	Use of the AVERAGE keyword is not compatible with arrays with missing
+;	pixels.
+;
+; Side effects: 
+;	If the keywords BSCALE and BZERO are present in the FITS header, and
+;	have non-trivial values, then the returned array DATA is formed by the
+;	equation
+;
+;			DATA = BSCALE*original + BZERO
+;
+;	However, this behavior can overridden by using the /NOSCALE keyword.
+;
+;	If the data is scaled, then the optional HEADER array is changed so
+;	that BSCALE=1 and BZERO=0.  This is so that these scaling parameters
+;	are not applied to the data a second time by another routine.  Also,
+;	history records are added storing the original values of these
+;	constants.  Note that only the returned array is modified--the header
+;	in the FITS file itself is untouched.
+;
+;	If the /NOUPDATE keyword is set, however, then the BSCALE and BZERO
+;	keywords are not changed.  It is then the user's responsibility to
+;	ensure that these parameters are not reapplied to the data.  In
+;	particular, these keywords should not be present in any header when
+;	writing another FITS file, unless the user wants their values to be
+;	applied when the file is read back in.  Otherwise, FITS readers will
+;	read in the wrong values for the data array.
+;	
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, May 1992, based in part on READFITS by W. Landsman, and
+;			       STSUB by M. Greason and K. Venkatakrishna.
+;	W. Thompson, Jun 1992, added code to interpret BSCALE and BZERO
+;			       records, and added NOSCALE and NOUPDATE
+;			       keywords.
+;	W. Thompson, Aug 1992, changed to call FXHREAD, and to add history
+;			       records for BZERO, BSCALE.
+; Minimium IDL Version:
+;       V6.0 (uses V6.0 notation) 
+; Written     : 
+;	William Thompson, GSFC, May 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 17 November 1993.
+;		Corrected bug with AVERAGE keyword on non-IEEE compatible
+;		machines.
+;		Corrected bug with subsampling on VAX machines.
+;	Version 3, William Thompson, GSFC, 31 May 1994
+;		Added ERRMSG keyword.
+;       Version 4, William Thompson, GSFC, 23 June 1994
+;               Modified so that ERRMSG is not touched if not defined.
+;       Version 5, Zarro (SAC/GSFC), 14 Feb 1997 
+;               Added I/O error checking
+;       Version 6, 20-May-1998, David Schlegel/W. Thompson
+;               Allow a single pixel to be read in.
+;               Change the signal to read in the entire array to be -1
+;       Version 7 C. Markwardt 22 Sep 2003
+;               If the image is empty (NAXIS EQ 0), or NODATA is set, then
+;               return only the header.  
+;       Version 8 W. Landsman  29 June 2004
+;               Added COMPRESS keyword, check for .gz extension  
+;       Version 9, William Thompson, 19-Aug-2004
+;               Make sure COMPRESS is treated as a scalar
+;       Version 10, Craig Markwardt, 01 Mar 2004
+;               Add EXTENSION keyword and ability to read different
+;               extensions than the primary one.
+;       Version 11,  W. Landsman   September 2006 
+;               Assume since V5.5, remove VMS support
+;       Version 11.1,  W. Landsman   November 2007
+;               Allow for possibility number of bytes requires 64 bit integer
+;       Version 12, William Thompson, 18-Jun-2010, update BLANK value.
+;       Version 13, W. Landsman  Remove IEEE_TO_HOST, V6.0 notation
+;       Version 14, William Thompson, 25-Sep-2014, fix BSCALE bug in version 13
+;-
+;
+	ON_ERROR, 2
+;
+;  This parameter will be used later in conjunction with the average keyword.
+;
+	ALREADY_CONVERTED = 0
+        READ_OK=0
+;
+;  Parse the input parameters.
+;
+	CASE N_PARAMS() OF
+		2:  BEGIN & I1=-1 & I2=-1 & J1=-1 & J2=-1 & STEP=1  & END
+		3:  BEGIN & I1=-1 & I2=-1 & J1=-1 & J2=-1 & STEP=1  & END
+		4:  BEGIN & I1=-1 & I2=-1 & J1=-1 & J2=-1 & STEP=P1 & END
+		5:  BEGIN & I1=P1 & I2=P2 & J1=-1 & J2=-1 & STEP=1  & END
+		6:  BEGIN & I1=P1 & I2=P2 & J1=-1 & J2=-1 & STEP=P3 & END
+		7:  BEGIN & I1=P1 & I2=P2 & J1=P3 & J2=P4 & STEP=1  & END
+		8:  BEGIN & I1=P1 & I2=P2 & J1=P3 & J2=P4 & STEP=P5 & END
+		ELSE:  BEGIN
+			MESSAGE = 'Syntax:  FXREAD, FILENAME, DATA ' + $
+				'[, HEADER [, I1, I2 [, J1, J2 ] [, STEP ]]'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+			END
+	ENDCASE
+
+	;; Extension number	
+	IF N_ELEMENTS(EXTENSION0) EQ 0 THEN EXTENSION = 0L $
+	ELSE EXTENSION = EXTENSION0[0]
+
+	SZ = SIZE(EXTENSION)
+	ETYPE = SZ[SZ[0]+1]
+	IF ETYPE EQ 8 THEN BEGIN
+		MESSAGE = 'EXTENSION must not be a structure'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	ENDIF
+
+
+;
+;  Determine if file is compressed, get the UNIT number, and open the file.
+;
+        IF NOT KEYWORD_SET(COMPRESS) THEN $
+         COMPRESS = STRLOWCASE( STRMID(FILENAME, STRLEN(FILENAME)-3,3)) EQ '.gz'
+	OPENR, UNIT, FILENAME, /GET_LUN, ERROR=ERROR,COMPRESS=COMPRESS[0]
+        IF ERROR NE 0 THEN BEGIN
+	    MESSAGE='Error opening '+FILENAME
+	    IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+		ERRMSG = MESSAGE
+		RETURN
+	    END ELSE MESSAGE, MESSAGE
+        ENDIF
+;
+;  Read in the FITS header.
+;
+
+	;; Starting extension number is zero
+	I_EXT = 0L
+	FOUND_EXT = 0
+
+        WHILE NOT FOUND_EXT DO BEGIN
+            FXHREAD,UNIT,HEADER,STATUS
+            IF STATUS NE 0 THEN BEGIN
+               FREE_LUN,UNIT
+                MESSAGE = 'Unable to read requested FITS header extension'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                    ERRMSG = MESSAGE
+                    RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF
+;
+;  Extract the keywords BITPIX, NAXIS, NAXIS1, ...
+;
+            START = 0L
+            BITPIX = FXPAR(HEADER,'BITPIX', START=START)
+            NAXIS = FXPAR(HEADER,'NAXIS', START=START)
+            GCOUNT = FXPAR(HEADER,'GCOUNT', START=START)
+            IF GCOUNT EQ 0 THEN GCOUNT = 1
+            PCOUNT = FXPAR(HEADER,'PCOUNT', START=START)
+            IF NAXIS GT 0 THEN BEGIN 
+                DIMS = FXPAR(HEADER,'NAXIS*') ;Read dimensions
+                NDATA = DIMS[0]
+                IF NAXIS GT 1 THEN FOR I=2,NAXIS DO NDATA = NDATA*DIMS[I-1]
+            ENDIF ELSE NDATA = 0
+            NBYTES = LONG64(ABS(BITPIX) / 8) * GCOUNT * (PCOUNT + NDATA)
+            NREC = (NBYTES + 2879) / 2880
+            
+            IF ETYPE EQ 7 THEN BEGIN
+                EXTNAME = STRTRIM(STRUPCASE(FXPAR(HEADER,'EXTNAME', $
+                                                  START=START)),2)
+                IF EXTNAME EQ EXTENSION THEN FOUND_EXT = 1
+            END ELSE IF I_EXT EQ EXTENSION THEN FOUND_EXT = 1
+
+            IF NOT FOUND_EXT THEN BEGIN
+                ;; Check to be sure there are extensions
+                IF I_EXT EQ 0 THEN BEGIN
+                    IF NOT FXPAR(HEADER,'EXTEND', START=START) THEN BEGIN
+		        FREE_LUN,UNIT
+                        MESSAGE = 'Requested extension not found, and file ' + $
+                          FILENAME + ' does not contain extensions'
+                        IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                            ERRMSG = MESSAGE
+                            RETURN
+                        END ELSE MESSAGE, MESSAGE
+                    ENDIF
+                ENDIF
+
+	        POINT_LUN, -UNIT, POINTLUN		;Current position
+                MHEAD0 = POINTLUN + NREC*2880L
+	        POINT_LUN, UNIT, MHEAD0			;Next FITS extension
+
+                I_EXT++
+            ENDIF
+        ENDWHILE
+
+        ;;
+        ;; If we got here, then we have arrived at the requested
+        ;; extension.  We still need to be sure that it is an image
+        ;; and not a table (for extensions beyond the primary one,
+        ;; that is).
+        ;;
+        IF I_EXT GT 0 THEN BEGIN
+            XTENSION = STRTRIM(STRUPCASE(FXPAR(HEADER,'XTENSION', START=START)),2)
+            IF (XTENSION NE 'IMAGE') THEN BEGIN
+		FREE_LUN,UNIT
+                MESSAGE = 'Extension ' + STRTRIM(EXTENSION,2) +		$
+                  ' is not an image'
+                IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+                    ERRMSG = MESSAGE
+                    RETURN
+                END ELSE MESSAGE, MESSAGE
+            ENDIF
+        ENDIF
+            
+        ;; Handle case of empty image, or no data requested
+        IF NAXIS EQ 0 OR KEYWORD_SET(NODATA) THEN BEGIN
+            ;; Make DATA an undefined variable, reflecting no data
+            DATA = 0 & DUMMY = TEMPORARY(DATA)
+
+            ERRMSG = ''
+            FREE_LUN,UNIT
+            RETURN
+        ENDIF
+
+	DIMS = FXPAR(HEADER,'NAXIS*')
+	N1 = DIMS[0]
+	IF NAXIS EQ 2 THEN N2 = DIMS[1] ELSE N2 = 1
+;
+;  Determine the array type from the keyword BITPIX.
+;
+	CASE BITPIX OF
+		  8:	IDLTYPE = 1	; Byte
+		 16:	IDLTYPE = 2	; Integer*2
+		 32:	IDLTYPE = 3	; Integer*4
+		-32:	IDLTYPE = 4	; Real*4
+		-64:	IDLTYPE = 5	; Real*8
+	ENDCASE
+;
+;  Set the default values for the optional parameters.
+;
+	IF (I1 EQ -1) && (I2 EQ -1) THEN BEGIN
+           I1 = 0
+           I2 = N1-1
+        ENDIF
+	IF (J1 EQ -1) && (J2 EQ -1) THEN BEGIN
+           J1 = 0
+           J2 = N2-1
+        ENDIF
+;
+;  If the prompt keyword was set, the prompt for the parameters.
+;
+	IF KEYWORD_SET(PROMPT) THEN BEGIN
+		ANSWER = ''
+		READ,'Enter lower limit for X ['+STRTRIM(I1,2)+']: ', ANSWER
+		IF ANSWER NE '' THEN I1 = (ANSWER)
+;
+		ANSWER = ''
+		READ,'Enter upper limit for X ['+STRTRIM(I2,2)+']: ', ANSWER
+		IF ANSWER NE '' THEN I2 = LONG(ANSWER)
+;
+		ANSWER = ''
+		READ,'Enter lower limit for Y ['+STRTRIM(J1,2)+']: ', ANSWER
+		IF ANSWER NE '' THEN J1 = LONG(ANSWER)
+;
+		ANSWER = ''
+		READ,'Enter upper limit for Y ['+STRTRIM(J2,2)+']: ', ANSWER
+		IF ANSWER NE '' THEN J2 = LONG(ANSWER)
+;
+		ANSWER = ''
+		READ,'Enter step size ['+STRTRIM(STEP,2)+']: ', ANSWER
+		IF ANSWER NE '' THEN STEP = LONG(ANSWER)
+	ENDIF
+;
+;  Differentiate between XSTEP and YSTEP.
+;
+	XSTEP = STEP > 1
+	IF N_ELEMENTS(Y_STEP) EQ 1 THEN YSTEP = Y_STEP ELSE YSTEP = XSTEP
+;
+;  If any of the optional parameters were passed, then update the dimensions
+;  accordingly.  First check I1 and I2.
+;
+	IF (I1 NE 0) || (I2 NE N1-1) THEN BEGIN
+		IF NAXIS GT 2 THEN BEGIN
+			FREE_LUN,UNIT
+			MESSAGE = 'Range parameters can only be set for ' + $
+				'one or two-dimensional arrays'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+		IF (MIN([I1,I2]) LT 0) OR (MAX([I1,I2]) GE DIMS[0]) THEN BEGIN
+			FREE_LUN,UNIT
+			MESSAGE = 'I1,I2 must be in the range 0 to ' +	$
+				STRTRIM(DIMS[0]-1,2)
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		END ELSE IF I1 GT I2 THEN BEGIN
+			MESSAGE = 'I2 must be >= I1'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+		DIMS[0] = I2 - I1 + 1
+	ENDIF
+;
+;  Next, check J1 and J2.
+;
+	IF (J1 NE 0) || (J2 NE N2-1) THEN BEGIN
+		IF NAXIS NE 2 THEN BEGIN
+			FREE_LUN,UNIT
+			MESSAGE = 'J1, J2 can only be set for ' +	$
+				'two-dimensional arrays'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+		IF (MIN([J1,J2]) LT 0) OR (MAX([J1,J2]) GE DIMS[1]) THEN BEGIN
+			FREE_LUN,UNIT
+			MESSAGE = 'J1,J2 must be in the range 0 to ' +	$
+				STRTRIM(DIMS[1]-1,2)
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		END ELSE IF J1 GT J2 THEN BEGIN
+			MESSAGE = 'J2 must be >= J1'
+			IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+				ERRMSG = MESSAGE
+				RETURN
+			END ELSE MESSAGE, MESSAGE
+		ENDIF
+		DIMS[1] = J2 - J1 + 1
+	ENDIF
+;
+;  Next, check XSTEP.  Note that the dimensions of the final result are
+;  somewhat differ depending on whether the keyword AVERAGE is set or not.
+;
+	IF XSTEP GT 1 THEN BEGIN
+	    IF NAXIS GT 2 THEN BEGIN
+		FREE_LUN,UNIT
+	        MESSAGE = 'STEP can only be set for one or ' +	$
+	            'two-dimensional arrays'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	    END ELSE IF XSTEP NE LONG(XSTEP) THEN BEGIN
+		FREE_LUN,UNIT
+	        MESSAGE = 'STEP must be an integer value'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	    END ELSE IF KEYWORD_SET(AVERAGE) THEN BEGIN
+	        DIMS[0] = DIMS[0] / LONG(XSTEP)
+	    END ELSE BEGIN
+	        DIMS[0] = LONG(DIMS[0] + XSTEP - 1) / LONG(XSTEP)
+	        INDEX = LINDGEN(DIMS[0])*XSTEP
+	    ENDELSE
+	ENDIF
+;
+;  Finally, check YSTEP.  This parameter is ignored for anything other than
+;  two-dimensional arrays.
+;
+	IF (NAXIS EQ 2) && (YSTEP GT 1) THEN BEGIN
+	    IF YSTEP NE LONG(YSTEP) THEN BEGIN
+		FREE_LUN,UNIT
+	        MESSAGE = 'YSTEP must be an integer value'
+		IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+			ERRMSG = MESSAGE
+			RETURN
+		END ELSE MESSAGE, MESSAGE
+	    END ELSE IF KEYWORD_SET(AVERAGE) THEN BEGIN
+	        DIMS[1] = DIMS[1] / LONG(YSTEP)
+	    END ELSE BEGIN
+	        DIMS[1] = LONG(DIMS[1]+YSTEP-1) / LONG(YSTEP)
+	    ENDELSE
+	END ELSE YSTEP = 1
+;
+;  Make the array.
+;
+	DATA = MAKE_ARRAY(DIMENSION=DIMS,TYPE=IDLTYPE,/NOZERO)
+;
+;  Find the start of the data to be read in.
+;
+	POINT_LUN,-UNIT,OFFSET		;Current position
+	DELTA = N1*ABS(BITPIX)/8
+	IF J1 NE 0 THEN BEGIN
+		OFFSET = OFFSET + J1*DELTA
+		POINT_LUN,UNIT,OFFSET
+	ENDIF
+;
+;  If the I range, XSTEP or YSTEP is non-trivial, then read in the file line by
+;  line.  If pixel averaging, then read in YSTEP lines.
+;
+        ON_IOERROR,QUIT
+	IF (DIMS[0] NE N1) || (XSTEP GT 1) || (YSTEP GT 1) THEN BEGIN
+	    IF NAXIS EQ 1 THEN NJ = 1 ELSE NJ = DIMS[1]
+	    FOR J = 0,NJ-1 DO BEGIN
+	        IF YSTEP GT 1 THEN POINT_LUN,UNIT,OFFSET+J*YSTEP*DELTA
+	        IF (YSTEP GT 1) && KEYWORD_SET(AVERAGE) && (NAXIS EQ 2) $
+	            THEN LINE = MAKE_ARRAY(N1,YSTEP,TYPE=IDLTYPE,/NOZERO) $
+	            ELSE LINE = MAKE_ARRAY(N1,TYPE=IDLTYPE,/NOZERO)
+	        READU,UNIT,LINE
+;
+;  If I1,I2 do not match the array size, then extract the relevant subarray.
+;
+	        IF (I1 NE 0) || (I2 NE N1-1) THEN LINE = LINE[I1:I2,*]
+;
+;  Suppose that the step size is non-trivial.  If AVERAGE was set, then convert
+;  to the host format, and use REBIN to average the data.  (Note that missing
+;  pixels are not correctly handled in this case.)  Otherwise, select out the
+;  relevant portion of the data.
+;
+	        IF (XSTEP GT 1) || (YSTEP GT 1) THEN BEGIN
+	            IF KEYWORD_SET(AVERAGE) THEN BEGIN
+			SWAP_ENDIAN_INPLACE, LINE, /SWAP_IF_LITTLE
+			ALREADY_CONVERTED = 1
+	                IF NAXIS EQ 1 THEN BEGIN
+	                    DATA[0,J] = REBIN(LINE[0:XSTEP*DIMS[0]]-1,DIMS[0])
+	                END ELSE BEGIN
+	                    DATA[0,J] = REBIN(LINE[0:XSTEP*DIMS[0]-1,*],DIMS[0],1)
+	                ENDELSE
+		    END ELSE DATA[0,J] = LINE[INDEX]
+;
+;  Otherwise, if the step size is trivial, then simply store the line in the
+;  data array.
+;
+	        END ELSE BEGIN
+	            DATA[0,J] = LINE
+	        ENDELSE
+	    ENDFOR
+;
+;  Otherwise, if the file doesn't have to be read in line by line, then just
+;  read the data array.
+;
+	END ELSE READU,UNIT,DATA
+;
+;  Convert the data from IEEE to host format, keeping track of any IEEE NaN
+;  values.  Don't do this if the conversion has already taken place.
+;
+	IF ~ALREADY_CONVERTED THEN BEGIN
+		IF (N_ELEMENTS(NANVALUE) EQ 1) && (IDLTYPE GE 4) &&	$
+			(IDLTYPE LE 6) THEN W = WHERENAN(DATA,COUNT) ELSE $
+			COUNT = 0
+		SWAP_ENDIAN_INPLACE,DATA, /SWAP_IF_LITTLE
+	END ELSE COUNT = 0
+;
+;  If the parameters BZERO and BSCALE are non-trivial, then adjust the array by
+;  these values.  Also update the BLANK keyword, if present.
+;
+	IF ~KEYWORD_SET(NOSCALE) THEN BEGIN
+		BZERO  = FXPAR(HEADER,'BZERO')
+		BSCALE = FXPAR(HEADER,'BSCALE')
+                BLANK  = FXPAR(HEADER,'BLANK',COUNT=NBLANK)
+		GET_DATE,DTE
+		IF (BSCALE NE 0) && (BSCALE NE 1) THEN BEGIN
+			DATA *= BSCALE
+			IF ~KEYWORD_SET(NOUPDATE) THEN BEGIN
+                            FXADDPAR,HEADER,'BSCALE',1.
+                            FXADDPAR,HEADER,'HISTORY',DTE +		$
+                              ' applied BSCALE = '+ STRTRIM(BSCALE,2)
+                            IF NBLANK EQ 1 THEN BEGIN
+                                print, bscale, blank
+                                BLANK *= BSCALE
+                                FXADDPAR,HEADER,'BLANK',BLANK
+                            ENDIF
+			ENDIF
+		ENDIF
+		IF BZERO NE 0 THEN BEGIN
+			DATA += BZERO
+			IF ~KEYWORD_SET(NOUPDATE) THEN BEGIN
+                            FXADDPAR,HEADER,'BZERO',0.
+                            FXADDPAR,HEADER,'HISTORY',DTE +		$
+                              ' applied BZERO = '+ STRTRIM(BZERO,2)
+                            IF NBLANK EQ 1 THEN BEGIN
+                                BLANK += BZERO
+                                FXADDPAR,HEADER,'BLANK',BLANK
+                            ENDIF
+			ENDIF
+		ENDIF
+	ENDIF
+;
+;  Store NANVALUE everywhere where the data corresponded to IEE NaN.
+;
+	IF COUNT GT 0 THEN DATA[W] = NANVALUE
+;
+;  Close the file and return.
+;
+        READ_OK=1
+QUIT:   ON_IOERROR,NULL
+	FREE_LUN, UNIT
+        IF NOT READ_OK THEN BEGIN
+	    MESSAGE='Error reading file '+FILENAME
+	    IF N_ELEMENTS(ERRMSG) NE 0 THEN BEGIN
+		ERRMSG = MESSAGE
+		RETURN
+	    END ELSE MESSAGE, MESSAGE
+	ENDIF
+	IF N_ELEMENTS(ERRMSG) NE 0 THEN ERRMSG = ''
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/fxwrite.pro b/Code/script_idl_mv/astrolib/fxwrite.pro
new file mode 100644
index 0000000000000000000000000000000000000000..30e2c7c189436cf8db45077eaf970a3a323992f1
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/fxwrite.pro
@@ -0,0 +1,312 @@
+	PRO FXWRITE, FILENAME, HEADER, DATA, NANVALUE=NANVALUE,		$
+		NOUPDATE=NOUPDATE, ERRMSG=ERRMSG, APPEND=APPEND
+;+
+; NAME: 
+;	FXWRITE
+; Purpose     : 
+;	Write a disk FITS file.
+; Explanation : 
+;       Creates or appends to a disk FITS file and writes a FITS
+;       header, and optionally an image data array.
+; Use         : 
+;	FXWRITE, FILENAME, HEADER [, DATA ]
+; Inputs      : 
+;	FILENAME = String containing the name of the file to be written.
+;	HEADER	 = String array containing the header for the FITS file.
+; Opt. Inputs : 
+;	DATA	 = IDL data array to be written to the file.  If not passed,
+;		   then it is assumed that extensions will be added to the
+;		   file.
+; Outputs     : 
+;	None.
+; Opt. Outputs: 
+;	None.
+; Keywords    : 
+;	NANVALUE = Value signalling data dropout.  All points corresponding to
+;		   this value are set to be IEEE NaN (not-a-number).  Ignored
+;		   unless DATA is of type float, double-precision or complex.
+;	NOUPDATE = If set, then the optional BSCALE and BZERO keywords in the
+;		   HEADER array will not be changed.  The default is to reset
+;		   these keywords to BSCALE=1, BZERO=0.
+;       APPEND = If set, then an existing file will be appended to.
+;                Appending to a non-existent file will create it.  If
+;                a primary HDU already exists then it will be modified
+;                to have EXTEND = T.
+;	ERRMSG	 = If defined and passed, then any error messages will be
+;		   returned to the user in this parameter rather than
+;		   depending on the MESSAGE routine in IDL.  If no errors are
+;		   encountered, then a null string is returned.  In order to
+;		   use this feature, ERRMSG must be defined first, e.g.
+;
+;			ERRMSG = ''
+;			FXWRITE, ERRMSG=ERRMSG, ...
+;			IF ERRMSG NE '' THEN ...
+;
+; Calls       : 
+;	CHECK_FITS, GET_DATE, FXADDPAR, FXPAR
+; Common      : 
+;	None.
+; Restrictions: 
+;	If DATA is passed, then HEADER must be consistent with it.  If no data
+;	array is being written to the file, then HEADER must also be consistent
+;	with that.  The routine FXHMAKE can be used to create a FITS header.
+;
+;	If found, then the optional keywords BSCALE and BZERO in the HEADER
+;	array is changed so that BSCALE=1 and BZERO=0.  This is so that these
+;	scaling parameters are not applied to the data a second time by another
+;	routine.  Also, history records are added storing the original values
+;	of these constants.  (Other values of BZERO are used for unsigned
+;	integers.)
+;
+;	If the /NOUPDATE keyword is set, however, then the BSCALE and BZERO
+;	keywords are not changed.  The user should then be aware that FITS
+;	readers will apply these numbers to the data, even if the data is
+;	already converted to floating point form.
+;
+;	Groups are not supported.
+;
+; Side effects: 
+;	HEADER may be modified.  One way it may be modified is describe
+;       above under NOUPDATE.  The first header card may also be
+;       modified to conform to the FITS standard if it does not
+;       already agree (i.e. use of either the SIMPLE or XTENSION
+;       keyword depending on whether the image is the primary HDU or
+;       not).
+; Category    : 
+;	Data Handling, I/O, FITS, Generic.
+; Prev. Hist. : 
+;	W. Thompson, Jan 1992, from WRITEFITS by J. Woffard and W. Landsman.
+;	Differences include:
+;
+;		* Made DATA array optional, and HEADER array mandatory.
+;		* Changed order of HEADER and DATA parameters.
+;		* No attempt made to fix HEADER array.
+;
+;	W. Thompson, May 1992, changed open statement to force 2880 byte fixed
+;			       length records (VMS).  The software here does not
+;			       depend on this file configuration, but other
+;			       FITS readers might.
+;	W. Thompson, Aug 1992, added code to reset BSCALE and BZERO records,
+;			       and added the NOUPDATE keyword.
+; Written     : 
+;	William Thompson, GSFC, January 1992.
+; Modified    : 
+;	Version 1, William Thompson, GSFC, 12 April 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 31 May 1994
+;		Added ERRMSG keyword.
+;	Version 3, William Thompson, GSFC, 23 June 1994
+;		Modified so that ERRMSG is not touched if not defined.
+;	Version 4, William Thompson, GSFC, 12 August 1999
+;		Catch error if unable to open file.
+;       Version 4.1 Wayne Landsman, GSFC, 02 May 2000
+;               Remove !ERR in call to CHECK_FITS, Use ARG_PRESENT()
+;       Version 5, William Thompson, GSFC, 22 September 2004
+;               Recognize unsigned integer types
+;       Version 5.1 W. Landsman 14 November 2004 
+;               Allow for need for 64bit number of bytes
+;       Version 6, Craig Markwardt, GSFC, 30 May 2005
+;               Ability to append to existing files
+;       Version 7, W. Landsman GSFC, Mar 2014
+;               Remove HOST_TO_IEEE, Use V6.0 notation
+; Version     : 
+;	Version 6, 30 May 2005
+;-
+;
+	ON_ERROR, 2
+;
+;  Check the number of parameters.
+;   
+	IF N_PARAMS() LT 2 THEN BEGIN
+	    MESSAGE = 'Syntax:  FXWRITE, FILENAME, HEADER  [, DATA ]'
+	    GOTO, HANDLE_ERROR
+	ENDIF
+;
+;  Check the header against the data being written to the file.  If the data
+;  array is not passed, then NAXIS should be set to zero, and EXTEND should be
+;  true.
+;
+	IF N_PARAMS() EQ 2 THEN BEGIN
+	    IF (FXPAR(HEADER,'NAXIS') NE 0) THEN BEGIN
+		MESSAGE = 'NAXIS should be zero for no primary data array'
+		GOTO, HANDLE_ERROR
+	    END ELSE IF (~FXPAR(HEADER,'EXTEND')) THEN BEGIN
+		MESSAGE = 'EXTEND should be true for no primary data array'
+		GOTO, HANDLE_ERROR
+	    ENDIF
+	END ELSE BEGIN
+	    CHECK_FITS, DATA, HEADER, /FITS, ERRMSG = MESSAGE
+	    IF MESSAGE NE '' THEN GOTO, HANDLE_ERROR
+	ENDELSE
+;
+;  Set the BSCALE and BZERO keywords to their default values.
+;
+        SZ = SIZE(DATA)
+        TYPE = SZ[SZ[0]+1]
+        IF N_PARAMS() EQ 3 THEN NEWDATA = DATA
+	IF ~KEYWORD_SET(NOUPDATE) THEN BEGIN
+	    BZERO  = FXPAR(HEADER,'BZERO')
+	    BSCALE = FXPAR(HEADER,'BSCALE')
+	    GET_DATE,DTE
+	    IF (BSCALE NE 0) AND (BSCALE NE 1) THEN BEGIN
+		FXADDPAR,HEADER,'BSCALE',1.
+		FXADDPAR,HEADER,'HISTORY',DTE+' reset BSCALE, was '+ $
+			STRTRIM(BSCALE,2)
+            ENDIF
+;
+;  If an unsigned data type then redefine BZERO to allow all the data to be
+;  stored in the file.
+;
+            BZERO0 = 0
+            IF (TYPE EQ 12) && (~KEYWORD_SET(NOUPDATE)) THEN BEGIN
+                BZERO0 = '8000'X
+                NEWDATA = FIX(TEMPORARY(NEWDATA) - BZERO)
+            ENDIF
+            IF (TYPE EQ 13) && (~KEYWORD_SET(NOUPDATE)) THEN BEGIN
+                BZERO0 = '80000000'X
+                NEWDATA = LONG(TEMPORARY(NEWDATA) - BZERO)
+            ENDIF
+	    IF BZERO NE BZERO0 THEN BEGIN
+		FXADDPAR,HEADER,'BZERO',BZERO0
+		FXADDPAR,HEADER,'HISTORY',DTE+' reset BZERO, was '+ $
+			STRTRIM(BZERO,2)
+	    ENDIF
+	ENDIF
+;
+;  Get the UNIT number, and open the file.
+;
+       	GET_LUN, UNIT      
+       	OPENW, UNIT, FILENAME, 2880, /BLOCK, ERROR=ERR, APPEND=APPEND
+        VERB = 'creating'
+        IF KEYWORD_SET(APPEND) THEN VERB = 'appending to'
+	IF ERR NE 0 THEN BEGIN
+	    MESSAGE = 'Error '+VERB+' file '+FILENAME
+	    GOTO, HANDLE_ERROR
+        ENDIF
+
+;
+;  Special processing is required when we are appending to 
+;  the file, to ensure that the FITS standards are met.
+;  (i.e. primary HDU must have EXTEND = T, and the header
+;  to be written must have XTENSION = 'IMAGE').
+;  
+
+        POINT_LUN, -UNIT, POS
+        IF POS GT 0 THEN BEGIN
+            ;; Release the file and call FXHMODIFY to edit the
+            ;; header of the primary HDU.  It is required to have
+            ;; EXTEND=T.  FXHMODIFY calls FXADDPAR, which
+            ;; automatically places the EXTEND keyword in the
+            ;; required position.
+            FREE_LUN, UNIT
+            FXHMODIFY, FILENAME, ERRMSG=MESSAGE, $ ; (EXTENSION=0 implied)
+              'EXTEND', 'T', ' FITS dataset may contain extensions'
+            IF MESSAGE NE '' THEN GOTO, HANDLE_ERROR
+            
+            ;; Re-open the file
+            GET_LUN, UNIT      
+            OPENW, UNIT, FILENAME, 2880, /BLOCK, ERROR=ERR, APPEND=APPEND
+            IF ERR NE 0 THEN BEGIN
+                MESSAGE = 'Error re-opening file '+FILENAME
+                GOTO, HANDLE_ERROR
+            ENDIF
+            
+            ;; Revise the header so that it begins with an
+            ;; XTENSION keyword... if it doesn't already
+            IF STRMID(HEADER[0], 0, 9) EQ 'SIMPLE  =' THEN BEGIN
+                ;; Extra work to preserve the comment
+                DUMMY = FXPAR(HEADER, 'SIMPLE', COMMENT=COMMENT) 
+                FXADDPAR, DUMMYHEADER, 'XTENSION', 'IMAGE', COMMENT
+                HEADER[0] = DUMMYHEADER[0]
+            ENDIF
+
+            ;; Find last NAXIS* keyword, since PCOUNT/GCOUNT follow them
+            NAXIS = FXPAR(HEADER, 'NAXIS', COUNT=COUNT_NAXIS)
+            IF NAXIS[0] GT 0 THEN PCOUNT_AFTER='NAXIS'+strtrim(NAXIS[0],2)
+            ;; Required PCOUNT/GCOUNT keywords for following extensions
+            FXADDPAR, HEADER, 'PCOUNT', 0, ' number of random group parameters', $
+              AFTER=PCOUNT_AFTER
+            FXADDPAR, HEADER, 'GCOUNT', 1, ' number of random groups', $
+              AFTER='PCOUNT'
+            
+        ENDIF ELSE BEGIN
+            ;; In the off chance that this header was used before to
+            ;; write a header with XTENSION, make sure this *new* file
+            ;; has SIMPLE = T
+            
+            IF STRMID(HEADER[0], 0, 9) EQ 'XTENSION=' THEN BEGIN
+                ;; Extra work to preserve the comment
+                DUMMY = FXPAR(HEADER, 'XTENSION', COMMENT=COMMENT) 
+                FXADDPAR, DUMMYHEADER, 'SIMPLE', 'T', COMMENT
+                HEADER[0] = DUMMYHEADER[0]
+            ENDIF
+
+        ENDELSE
+
+
+;
+;  Determine if an END line occurs, and add one if necessary
+;
+	ENDLINE = WHERE( STRMID(HEADER,0,8) EQ 'END     ', NEND)
+	ENDLINE = ENDLINE[0]
+	IF NEND EQ 0 THEN BEGIN
+	    MESSAGE, 'WARNING - An END statement has been appended ' + $
+		'to the FITS header', /INFORMATIONAL
+	    HEADER = [HEADER, 'END' + STRING(REPLICATE(32B,77))]
+	    ENDLINE = N_ELEMENTS(HEADER) - 1 
+	ENDIF
+	NMAX = ENDLINE + 1		;Number of 80 byte records
+	NHEAD = FIX((NMAX+35)/36)	;Number of 2880 byte records
+;
+;  Convert to byte and force into 80 character lines
+;
+	BHDR = REPLICATE(32B, 80, 36*NHEAD)
+	FOR N = 0,ENDLINE DO BHDR[0,N] = BYTE( STRMID(HEADER[N],0,80) )
+	WRITEU, UNIT, BHDR
+;
+;  If passed, then write the data array.
+;
+	IF N_PARAMS() EQ 3 THEN BEGIN
+;
+;  If necessary, then byte-swap the data before writing it out.  Also, replace
+;  any values corresponding data dropout with IEEE NaN.
+;
+	    IF (N_ELEMENTS(NANVALUE) EQ 1) && (TYPE GE 4) &&	$
+		    (TYPE LE 6) THEN BEGIN
+		W = WHERE(DATA EQ NANVALUE, COUNT)
+		CASE TYPE OF
+		    4:  NAN = FLOAT(  REPLICATE('FF'XB,4),0,1)
+		    5:  NAN = DOUBLE( REPLICATE('FF'XB,8),0,1)
+		    6:  NAN = COMPLEX(REPLICATE('FF'XB,8),0,1)
+		    9:  NAN = DCOMPLEX(REPLICATE('FF'XB,16),0,1)
+		ENDCASE
+	    END ELSE COUNT = 0
+;
+	    SWAP_ENDIAN_INPLACE, NEWDATA, /SWAP_IF_LITTLE
+	    IF COUNT GT 0 THEN NEWDATA[W] = NAN
+;
+	    WRITEU,UNIT,NEWDATA
+;
+;  If necessary, then pad out to an integral multiple of 2880 bytes.
+;
+	    BITPIX = FXPAR( HEADER, 'BITPIX' )
+	    NBYTES = LONG64(N_ELEMENTS(DATA)) * (ABS(BITPIX) / 8 )
+	    NPAD = NBYTES MOD 2880
+	    IF NPAD NE 0 THEN BEGIN
+		NPAD = 2880 - NPAD
+		WRITEU,UNIT,BYTARR(NPAD)
+	    ENDIF
+	ENDIF
+;
+;  Close the file and return.
+;
+	FREE_LUN, UNIT
+	IF ARG_PRESENT(ERRMSG)  THEN ERRMSG = ''
+	RETURN
+;
+HANDLE_ERROR:
+	IF N_ELEMENTS(UNIT) EQ 1 THEN FREE_LUN, UNIT
+	IF ARG_PRESENT(ERRMSG) THEN ERRMSG = 'FXWRITE: ' + MESSAGE	$
+		ELSE MESSAGE, MESSAGE
+;
+	END
diff --git a/Code/script_idl_mv/astrolib/gal_flat.pro b/Code/script_idl_mv/astrolib/gal_flat.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d6407e283088b44e7721aa17c7c4a8be0520b136
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gal_flat.pro
@@ -0,0 +1,94 @@
+FUNCTION GAL_FLAT,IMAGE,ANG,INC,CEN,INTERP = interp
+;+
+; NAME:
+;	GAL_FLAT
+;
+; PURPOSE:
+;	Transforms the image of a galaxy so that the galaxy appears face-on
+; EXPLANATION:
+;	Either a nearest-neighbor approximations or a bilinear interpolation 
+;	may  be used.
+;
+; CALLING SEQUENCE:
+;	RESULT = GAL_FLAT( image, ang, inc, [, cen, /INTERP ] )  
+;
+; INPUTS:   
+;	IMAGE  - Image to be transformed
+;	ANG  - Angle of major axis, counterclockwise from Y-axis, degrees
+;		For an image in standard orientation (North up, East left)
+;		this is the Position Angle
+;	INC - Angle of inclination of galaxy, degrees
+;
+; OPTIONAL INPUTS:
+;	CEN - Two element vector giving the X and Y position of galaxy center
+;		If not supplied, then the galaxy center is assumed to coincide
+;		 with the image center
+;
+; INPUT KEYWORDS:
+;	INTERP - If present, and non-zero, then bilinear interpolation will be
+;		performed.  Otherwise a nearest neighbor approximation  is used.
+;
+; OUTPUTS:
+;	RESULT - the transformed image, same dimensions and type as IMAGE
+;
+; METHOD:
+;	A set of 4 equal spaced control points are corrected for inclination
+;	using the procedure POLYWARP.   These control points are used by 
+;	POLY_2D to correct the whole image.
+;
+; REVISION HISTORY:
+;	Written by R. S. Hill, SASC Technologies Inc., 4 December 1985
+;	Code cleaned up a bit    W. Landsman      December 1992
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+  if ( N_params() lt 3 ) then begin  
+      print,'Syntax - result = gal_flat( image, ang, inc, [ cen, /INTERP ])'
+      print,'ANG - Position Angle of major axis (degrees)'                
+      print,'INC - Inclination of galaxy (degrees)'
+      return, -1
+  endif 
+
+  if not keyword_set( INTERP ) then interp = 0
+
+  angr = (ang+90)/!RADEG
+  tanang = tan(angr)
+  cosang = cos(angr)
+  cosinc = cos(inc/!RADEG)
+;                                    Parameters of image
+  dims = SIZE(image)
+
+  if N_elements(cen) NE 2 then begin 
+
+      xcen = dims[1]/2.0                  ;Center
+      ycen = dims[2]/2.0
+      if not !QUIET then message,'Galaxy nucleus assumed in image center',/CONT
+
+  endif else begin
+
+      xcen = cen[0]
+      ycen = cen[1]
+
+  endelse
+;                                    Equation of rotation axis
+  b = ycen - xcen*tanang
+;                                    Fiducial grid (as in ROT_INT)   
+  gridx = xcen + [ [-1,1], [-1,1] ] * dims[1]/6.0
+  gridy = ycen + [ [-1,-1], [1,1] ] * dims[2]/6.0      
+;                                    Distorted version of grid
+  yprime = gridx*tanang + b            ;Equation of major axis
+  r0 = (gridy-yprime)*cos(angr)        ;Dist of control pts to major axis
+  delr = r0*(1.0-cosinc)               ;Correct distance for inclination
+  dely = -delr*cos(angr)               
+  delx =  delr*sin(angr)
+  distx = gridx + delx
+  disty = gridy + dely
+;                                    Parameters of undistorted grid
+  x0 = dims[1]/3.0
+  y0 = dims[2]/3.0
+  dx = x0                              ;In this case only
+  dy = y0
+;                                    Do it
+  polywarp, distx, disty, gridx, gridy, 1, kx, ky
+  RETURN,poly_2d( image, kx, ky, interp, MISSING = 0)
+  end  
diff --git a/Code/script_idl_mv/astrolib/gal_uvw.pro b/Code/script_idl_mv/astrolib/gal_uvw.pro
new file mode 100644
index 0000000000000000000000000000000000000000..69e63b970300f868258414f0a6aec3d3e9adb5e9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gal_uvw.pro
@@ -0,0 +1,130 @@
+pro gal_uvw, u, v, w, distance = distance, LSR = lsr, ra=ra,dec=dec, $
+     pmra = pmra, pmdec=pmdec, vrad = vrad, plx  = plx
+;+
+; NAME:
+;     GAL_UVW
+; PURPOSE:
+;     Calculate the Galactic space velocity (U,V,W) of star  
+; EXPLANATION:
+;     Calculates the Galactic space velocity U, V, W of star given its 
+;     (1) coordinates, (2) proper motion, (3) distance (or parallax), and 
+;     (4) radial velocity.
+; CALLING SEQUENCE:
+;     GAL_UVW, U, V, W, [/LSR, RA=, DEC=, PMRA= ,PMDEC=, VRAD= , DISTANCE= 
+;              PLX= ]
+; OUTPUT PARAMETERS:
+;      U - Velocity (km/s) positive toward the Galactic *anti*center
+;      V - Velocity (km/s) positive in the direction of Galactic rotation
+;      W - Velocity (km/s) positive toward the North Galactic Pole 
+; REQUIRED INPUT KEYWORDS:
+;      User must supply a position, proper motion,radial velocity and distance
+;      (or parallax).    Either scalars or vectors can be supplied.
+;     (1) Position:
+;      RA - Right Ascension in *Degrees*
+;      Dec - Declination in *Degrees*
+;     (2) Proper Motion
+;      PMRA = Proper motion in RA in arc units (typically milli-arcseconds/yr)
+;            If given mu_alpha --proper motion in seconds of time/year - then
+;             this is equal to 15*mu_alpha*cos(dec)
+;      PMDEC = Proper motion in Declination (typically mas/yr)
+;     (3) Radial Velocity
+;      VRAD = radial velocity in km/s
+;     (4) Distance or Parallax
+;      DISTANCE - distance in parsecs 
+;                 or
+;      PLX - parallax with same distance units as proper motion measurements
+;            typically milliarcseconds (mas)
+;
+; OPTIONAL INPUT KEYWORD:
+;      /LSR - If this keyword is set, then the output velocities will be
+;             corrected for the solar motion (U,V,W)_Sun = (-8.5, 13.38, 6.49) 
+;            (Coskunoglu et al. 2011 MNRAS) to the local standard of rest.
+;            Note that the value of the solar motion through the LSR remains
+;            poorly determined.
+;  EXAMPLE:
+;      (1) Compute the U,V,W coordinates for the halo star HD 6755.  
+;          Use values from Hipparcos catalog, and correct to the LSR
+;      ra = ten(1,9,42.3)*15.    & dec = ten(61,32,49.5)
+;      pmra = 628.42  &  pmdec = 76.65         ;mas/yr
+;      dis = 139    &  vrad = -321.4
+;      gal_uvw,u,v,w,ra=ra,dec=dec,pmra=pmra,pmdec=pmdec,vrad=vrad,dis=dis,/lsr
+;          ===>  u=141.2  v = -491.7  w = 93.9        ;km/s
+;
+;      (2) Use the Hipparcos Input and Output Catalog IDL databases (see 
+;      http://idlastro.gsfc.nasa.gov/ftp/zdbase/) to obtain space velocities
+;      for all stars within 10 pc with radial velocities > 10 km/s
+;
+;      dbopen,'hipp_new,hic'      ;Need Hipparcos output and input catalogs
+;      list = dbfind('plx>100,vrad>10')      ;Plx > 100 mas, Vrad > 10 km/s
+;      dbext,list,'pmra,pmdec,vrad,ra,dec,plx',pmra,pmdec,vrad,ra,dec,plx
+;      ra = ra*15.                 ;Need right ascension in degrees
+;      GAL_UVW,u,v,w,ra=ra,dec=dec,pmra=pmra,pmdec=pmdec,vrad=vrad,plx = plx 
+;      forprint,u,v,w              ;Display results
+; METHOD:
+;      Follows the general outline of Johnson & Soderblom (1987, AJ, 93,864)
+;      except that U is positive outward toward the Galactic *anti*center, and 
+;      the J2000 transformation matrix to Galactic coordinates is taken from  
+;      the introduction to the Hipparcos catalog.   
+; REVISION HISTORY:
+;      Written, W. Landsman                       December   2000
+;      fix the bug occuring if the input arrays are longer than 32767
+;        and update the Sun velocity           Sergey Koposov June 2008
+;	   vectorization of the loop -- performance on large arrays 
+;        is now 10 times higher                Sergey Koposov December 2008
+;      More recent value of solar motion WL/SK   Jan 2011
+;-
+ compile_opt idl2
+ if N_Params() EQ 0 then begin
+       print,'Syntax - GAL_UVW, U, V, W, [/LSR, RA=, DEC=, PMRA= ,PMDEC=, VRAD='
+       print,'                  Distance=, PLX='
+       print,'         U, V, W - output Galactic space velocities (km/s)'
+       return
+ endif
+ 
+ Nra = N_elements(ra)
+ if (nra EQ 0) or (N_elements(dec) EQ 0) then message, $
+     'ERROR - The RA, Dec (J2000) position keywords must be supplied (degrees)'
+ if N_elements(distance) GT 0 then begin 
+       bad = where(distance LE 0, Nbad)
+       if Nbad GT 0 then message,'ERROR - All distances must be > 0'
+       plx = 1e3/distance          ;Parallax in milli-arcseconds
+ endif else begin
+       if N_elements(plx) EQ 0 then message, $
+             'ERROR - Either a parallax or distance must be specified'
+       bad = where(plx LE 0.0, Nbad)
+       if Nbad GT 0 then message,'ERROR - Parallaxes must be > 0'
+ endelse
+
+ cosd = cos(dec/!RADEG)
+ sind = sin(dec/!RADEG)
+ cosa = cos(ra/!RADEG)
+ sina = sin(ra/!RADEG)
+
+ k = 4.74047     ;Equivalent of 1 A.U/yr in km/s   
+ A_G = [ [ 0.0548755604, +0.4941094279, -0.8676661490], $ 
+         [ 0.8734370902, -0.4448296300, -0.1980763734], $
+         [ 0.4838350155,  0.7469822445, +0.4559837762] ]
+
+ vec1 = vrad
+ vec2 = k*pmra/plx
+ vec3 = k*pmdec/plx
+
+ u = ( A_G[0,0]*cosa*cosd+A_G[0,1]*sina*cosd+A_G[0,2]*sind)*vec1+$
+     (-A_G[0,0]*sina     +A_G[0,1]*cosa                   )*vec2+$
+     (-A_G[0,0]*cosa*sind-A_G[0,1]*sina*sind+A_G[0,2]*cosd)*vec3
+ v = ( A_G[1,0]*cosa*cosd+A_G[1,1]*sina*cosd+A_G[1,2]*sind)*vec1+$
+     (-A_G[1,0]*sina     +A_G[1,1]*cosa                   )*vec2+$
+     (-A_G[1,0]*cosa*sind-A_G[1,1]*sina*sind+A_G[1,2]*cosd)*vec3
+ w = ( A_G[2,0]*cosa*cosd+A_G[2,1]*sina*cosd+A_G[2,2]*sind)*vec1+$
+     (-A_G[2,0]*sina     +A_G[2,1]*cosa                   )*vec2+$
+     (-A_G[2,0]*cosa*sind-A_G[2,1]*sina*sind+A_G[2,2]*cosd)*vec3
+
+ lsr_vel=[-8.5,13.38,6.49]
+ if keyword_set(lsr) then begin
+	u = u+lsr_vel[0]
+	v = v+lsr_vel[1]
+ 	w = w+lsr_vel[2]
+ end
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/galage.pro b/Code/script_idl_mv/astrolib/galage.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c42c49461c2e47c8d3ea9a4f1a35d8cbe22c355a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/galage.pro
@@ -0,0 +1,130 @@
+;+
+; NAME: 
+;   GALAGE
+;
+; PURPOSE:
+;   Determine the age of a galaxy given its redshift and a formation redshift.
+;
+; CALLING SEQUENCE:
+;   age = galage(z, [zform,  H0 =, k=, lambda0 =, Omega_m= , q0 =, /SILENT])' 
+;
+; INPUTS:
+;    z - positive numeric vector or scalar of measured redshifts 
+;    zform - redshift of galaxy formation (> z), numeric positive scalar 
+;        To determine the age of the universe at a given redshift, set zform
+;        to a large number (e.g. ~1000).
+;
+; OPTIONAL KEYWORD INPUTS: 
+;    H0 - Hubble constant in km/s/Mpc, positive scalar, default is 70
+;    /SILENT - If set, then the adopted cosmological parameters are not 
+;         displayed at the terminal.
+;
+;        No more than two of the following four parameters should be
+;        specified.   None of them need be specified -- the adopted defaults
+;        are given. 
+;    k - curvature constant, normalized to the closure density.   Default is
+;        0, (indicating a flat universe)
+;    Omega_m -  Matter density, normalized to the closure density, default
+;        is 0.3.   Must be non-negative
+;    Lambda0 - Cosmological constant, normalized to the closure density,
+;        default is 0.7
+;    q0 - Deceleration parameter, numeric scalar = -R*(R'')/(R')^2, default
+;        is -0.55
+;       
+; OUTPUTS:
+;    age -  age of galaxy in years, will have the same number of elements
+;           as the input Z vector
+;
+; EXAMPLE:
+;    (1) Determine the age of a galaxy observed at z = 1.5 in a cosmology with
+;    Omega_matter = 0.3 and Lambda = 0.0.    Assume the formation redshift was
+;    at z = 25, and use the default Hubble constant (=70 km/s/Mpc)
+;
+;    IDL> print,galage(1.5,25,Omega_m=0.3, Lambda = 0)
+;             ===> 3.35 Gyr
+;     
+;    (2) Plot the age of a galaxy in Gyr out to a redshift of z = 5, assuming 
+;        the default cosmology (omega_m=0.3, lambda=0.7), and zform = 100
+;
+;    IDL> z = findgen(50)/10.
+;    IDL> plot,z,galage(z,100)/1e9,xtit='z',ytit = 'Age (Gyr)'
+;
+; PROCEDURE:
+;    For a given formation time zform and a measured z, integrate dt/dz from 
+;    zform to z. Analytic formula of dt/dz in Gardner, PASP 110:291-305, 1998 
+;    March  (eq. 7)
+; 
+; COMMENTS:
+;    (1) Integrates using the IDL Astronomy Library procedure QSIMP.    (The 
+;    intrinsic IDL QSIMP() function is not called because of its ridiculous
+;    restriction that only scalar arguments can be passed to the integrating
+;    function.)    The function 'dtdz' is defined at the beginning of the 
+;    routine (so it can compile first).
+;    
+;    (2) Should probably be fixed to use a different integrator from QSIMP when
+;    computing age from an "infinite" redshift of formation.    But using a 
+;    large value of zform seems to work adequately.
+;
+;     (3) An alternative set of IDL procedures for computing cosmological
+;    parameters is available at 
+;            http://cerebus.as.arizona.edu/~ioannis/research/red/
+; PROCEDURES CALLED:
+;    COSMO_PARAM, QSIMP
+; HISTORY: 
+;     STIS version by P. Plait (ACC)                  June 1999
+;     IDL Astro Version   W. Landsman (Raytheon ITSS)      April 2000
+;     Avoid integer overflow for more than 32767 redshifts  July 2001
+;-
+;
+; define function dtdz
+;
+
+function dtdz, z, lambda0 = lambda0, q0 = q0
+   term1 = (1.0d + z)
+   term2 = 2.0d * (q0 + lambda0) * z + 1.0d - lambda0
+   term3 = (1.0d + z) * (1.0d +z)
+   return, 1.0 / (term1 * sqrt(term2 * term3 + lambda0))
+   end
+
+;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function galage, z, zform, h0 = h0, Omega_m=omega_m, lambda0 = lambda0, k = k, $
+                  q0 = q0, SILENT = silent
+
+ if N_params() LE 1 then begin
+    print,$
+  'Syntax: age = GALAGE(z, zform, [H0= , Omega_M = ,lambda0 =, k= , q0=, /SIL]'
+   return, 0
+ endif
+
+;
+; initialize numbers
+;
+
+  if N_elements(h0) EQ 0 then h0 = 70.0
+  COSMO_PARAM, Omega_m, lambda0, k, q0
+  if not keyword_set(silent) then $
+     print,'GALAGE: H0:', h0, ' Omega_m:', omega_m, ' Lambda0',lambda0, $
+        ' q0: ',q0, ' k: ', k, f='(A,I3,A,f5.2,A,f5.2,A,f5.2,A,F5.2)' 
+
+   nz = N_elements(z)
+   age = z*0.            ;Return same dimensions and data type as Z
+
+; 
+; use qsimp to integrate dt/dz to get age for each z
+;   watch out for null case of z >= zform 
+;
+ 
+    for i= 0L, nz-1 do begin
+      if (z[i] ge zform) then age_z = 0 else $
+          qsimp,'dtdz', z[i], zform, age_z, q0 = q0, lambda0 = lambda0
+      age[i] = age_z
+   endfor
+
+; convert units of age: km/s/Mpc to years, divide by H0
+;    3.085678e19 km --> 1 Mpc
+;    3.15567e+07 sec --> 1 year 
+
+   return, age * 3.085678e+19 / 3.15567e+7/ H0
+   end
+
diff --git a/Code/script_idl_mv/astrolib/gaussian.pro b/Code/script_idl_mv/astrolib/gaussian.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1f640a1a10cd43548d67c9fac36be9d48fad921f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gaussian.pro
@@ -0,0 +1,107 @@
+function gaussian, xi, parms, pderiv, DOUBLE=double
+;+
+; NAME:
+;       GAUSSIAN
+; PURPOSE:
+;       Compute the 1-d Gaussian function and optionally the derivative
+; EXPLANATION:
+;       Compute the 1-D Gaussian function and optionally the derivative 
+;       at an array of points.
+;
+; CALLING SEQUENCE:
+;       y = gaussian( xi, parms,[ pderiv ])
+;
+; INPUTS:
+;       xi = array, independent variable of Gaussian function.
+;
+;       parms = parameters of Gaussian, 2, 3 or 4 element array:
+;               parms[0] = maximum value (factor) of Gaussian,
+;               parms[1] = mean value (center) of Gaussian,
+;               parms[2] = standard deviation (sigma) of Gaussian.
+;               (if parms has only 2 elements then sigma taken from previous
+;               call to gaussian(), which is stored in a  common block).
+;               parms[3] = optional, constant offset added to Gaussian.
+; OUTPUT:
+;       y -  Function returns array of Gaussian evaluated at xi.    Values will
+;            be floating pt. (even if xi is double) unless the /DOUBLE keyword
+;            is set.
+;
+; OPTIONAL INPUT:
+;       /DOUBLE - set this keyword to return double precision for both
+;             the function values and (optionally) the partial derivatives.
+; OPTIONAL OUTPUT:
+;       pderiv = [N,3] or [N,4] output array of partial derivatives,
+;               computed only if parameter is present in call.
+;
+;               pderiv[*,i] = partial derivative at all xi absisca values
+;               with respect to parms[i], i=0,1,2,[3].
+;
+;
+; EXAMPLE:
+;       Evaulate a Gaussian centered at x=0, with sigma=1, and a peak value
+;       of 10 at the points 0.5 and 1.5.   Also compute the derivative
+;
+;       IDL> f = gaussian( [0.5,1.5], [10,0,1], DERIV )
+;       ==> f= [8.825,3.25].   DERIV will be a 2 x 3 array containing the
+;       numerical derivative at the two points with respect to the 3 parameters.
+; 
+; COMMON BLOCKS:
+;       None
+; HISTORY:
+;       Written, Frank Varosi NASA/GSFC 1992.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use machar() for machine precision, added /DOUBLE keyword,
+;       add optional constant 4th parameter    W. Landsman   November 2001
+;-
+  On_error,2
+  common gaussian, sigma
+
+  if N_params() LT 2 then begin
+        print,'Syntax - y = GAUSSIAN( xi, parms,[ pderiv, /DOUBLE ])'
+        print,'         parms[0] = maximum value (factor) of Gaussian'
+        print,'         parms[1] = mean value (center) of Gaussian'
+        print,'         parms[2] = standard deviation (sigma) of Gaussian'
+        print,'         parms[3] = optional constant to be added to Gaussian'
+        return, -1
+  endif
+
+  common gaussian, sigma
+
+        Nparmg = N_elements( parms )
+        npts = N_elements(xi) 
+        ptype = size(parms,/type)
+        if (ptype LE 3) or (ptype GE 12) then parms = float(parms)
+        if (Nparmg GE 3) then sigma = parms[2]
+
+        double = keyword_set(DOUBLE)
+        if double then $       ;Double precision?
+            gauss = dblarr( npts ) else $
+            gauss = fltarr( npts )
+ 
+        z = ( xi - parms[1] )/sigma
+        zz = z*z
+
+; Get smallest value expressible on computer.   Set lower values to 0 to avoid
+; floating underflow
+        minexp = alog((machar(DOUBLE=double)).xmin)     
+ 
+        w = where( zz LT -2*minexp, nw )
+        if (nw GT 0) then gauss[w] = exp( -zz[w] / 2 )
+
+        if N_params() GE 3 then begin
+
+                if double then $ 
+                pderiv = dblarr( npts, Nparmg ) else $
+                pderiv = fltarr( npts, Nparmg )
+                fsig = parms[0] / sigma
+
+                pderiv[0,0] = gauss
+                pderiv[0,1] = gauss * z * fsig
+
+                if (Nparmg GE 3) then  pderiv[0,2] = gauss * zz * fsig
+                if (Nparmg GE 4) then  pderiv[0,3] = replicate(1, npts)
+           endif
+
+ if Nparmg LT 4 then return, parms[0] * gauss else $
+                     return, parms[0] * gauss + parms[3]
+ end
diff --git a/Code/script_idl_mv/astrolib/gcirc.pro b/Code/script_idl_mv/astrolib/gcirc.pro
new file mode 100644
index 0000000000000000000000000000000000000000..06e0d717ef4b85b23102dc2c0a3fc7457d814eef
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gcirc.pro
@@ -0,0 +1,123 @@
+PRO gcirc,u,ra1,dc1,ra2,dc2,dis                         
+;+
+; NAME:
+;     GCIRC
+; PURPOSE:
+;     Computes rigorous great circle arc distances.  
+; EXPLANATION:
+;     Input position can either be either radians, sexagesimal RA, Dec or
+;     degrees.   All computations are double precision. 
+;
+; CALLING SEQUENCE:
+;      GCIRC, U, RA1, DC1, RA2, DC2, DIS
+;
+; INPUTS:
+;      U    -- integer = 0,1, or 2: Describes units of inputs and output:
+;              0:  everything radians
+;              1:  RAx in decimal hours, DCx in decimal
+;                       degrees, DIS in arc seconds 
+;              2:  RAx and DCx in degrees, DIS in arc seconds
+;      RA1  -- Right ascension or longitude of point 1
+;      DC1  -- Declination or latitude of point 1
+;      RA2  -- Right ascension or longitude of point 2
+;      DC2  -- Declination or latitude of point 2
+;
+; OUTPUTS:
+;      DIS  -- Angular distance on the sky between points 1 and 2
+;              See U above for units;  double precision  
+;
+; PROCEDURE:
+;      "Haversine formula" see 
+;      http://en.wikipedia.org/wiki/Great-circle_distance
+;
+; NOTES:
+;       (1) If RA1,DC1 are scalars, and RA2,DC2 are vectors, then DIS is a
+;       vector giving the distance of each element of RA2,DC2 to RA1,DC1.
+;       Similarly, if RA1,DC1 are vectors, and RA2, DC2 are scalars, then DIS
+;       is a vector giving the distance of each element of RA1, DC1 to 
+;       RA2, DC2.    If both RA1,DC1 and RA2,DC2 are vectors then DIS is a
+;       vector giving the distance of each element of RA1,DC1 to the 
+;       corresponding element of RA2,DC2.    If the input vectors are not the 
+;       same length, then excess elements of the longer ones will be ignored.
+;
+;       (2) The function SPHDIST provides an alternate method of computing
+;        a spherical distance.
+;
+;       (3) The haversine formula can give rounding errors for antipodal
+;       points.
+;
+; PROCEDURE CALLS:
+;      None
+;
+;   MODIFICATION HISTORY:
+;      Written in Fortran by R. Hill -- SASC Technologies -- January 3, 1986
+;      Translated from FORTRAN to IDL, RSH, STX, 2/6/87
+;      Vector arguments allowed    W. Landsman    April 1989
+;      Prints result if last argument not given.  RSH, RSTX, 3 Apr. 1998
+;      Remove ISARRAY(), V5.1 version        W. Landsman   August 2000
+;      Added option U=2                      W. Landsman   October 2006
+;      Use double precision for U=0 as advertised R. McMahon/W.L.  April 2007
+;      Use havesine formula, which has less roundoff error in the 
+;             milliarcsecond regime      W.L. Mar 2009
+;-
+ compile_opt idl2
+ On_error,2                            ;Return to caller
+
+ npar = N_params()
+ IF (npar ne 6) and (npar ne 5) THEN BEGIN
+   print,'Calling sequence:  GCIRC,U,RA1,DC1,RA2,DC2[,DIS]'
+   print,'   U = 0  ==> Everything in radians'
+   print, $
+   '   U = 1  ==> RAx decimal hours, DCx decimal degrees, DIS arc sec'
+   print,'   U = 2  ==> RAx, DCx decimal degrees, DIS arc sec'
+   RETURN
+ ENDIF
+
+
+ d2r    = !DPI/180.0d0
+ as2r   = !DPI/648000.0d0
+ h2r    = !DPI/12.0d0
+
+; Convert input to double precision radians
+ CASE u OF
+   0:  BEGIN
+          rarad1 = double(ra1)
+          rarad2 = double(ra2)
+          dcrad1 = double(dc1)
+          dcrad2 = double(dc2)
+       END
+   1:  BEGIN
+          rarad1 = ra1*h2r
+          rarad2 = ra2*h2r
+          dcrad1 = dc1*d2r
+          dcrad2 = dc2*d2r
+       END
+   2:  BEGIN  
+          rarad1 = ra1*d2r
+          rarad2 = ra2*d2r
+          dcrad1 = dc1*d2r
+          dcrad2 = dc2*d2r
+        END
+   ELSE:  MESSAGE, $
+                'U must be 0 (radians), 1 ( hours, degrees) or 2 (degrees)'
+ ENDCASE
+
+ deldec2 = (dcrad2-dcrad1)/2.0d
+ delra2 =  (rarad2-rarad1)/2.0d
+ sindis = sqrt( sin(deldec2)*sin(deldec2) + $
+	  cos(dcrad1)*cos(dcrad2)*sin(delra2)*sin(delra2) )
+ dis = 2.0d*asin(sindis) 
+
+ IF (u ne 0) THEN dis = dis/as2r
+
+ IF (npar eq 5) && (N_elements(dis) EQ 1) THEN BEGIN
+    IF (u ne 0) && (dis ge 0.1) && (dis le 1000)  $
+       THEN fmt = '(F10.4)' $
+       ELSE fmt = '(E15.8)'
+    IF (u ne 0) THEN units = ' arcsec' ELSE units = ' radians'
+    print,'Angular separation is ' + string(dis,format=fmt) + units
+ ENDIF
+
+ RETURN
+ END
+
diff --git a/Code/script_idl_mv/astrolib/gcntrd.pro b/Code/script_idl_mv/astrolib/gcntrd.pro
new file mode 100644
index 0000000000000000000000000000000000000000..344d6ca7787bb2e2753242a8ce4bdce0de538696
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gcntrd.pro
@@ -0,0 +1,326 @@
+pro gcntrd,img,x,y,xcen,ycen,fwhm, maxgood = maxgood, keepcenter=keepcenter, $
+                SILENT = silent, DEBUG = debug
+
+;+
+;  NAME: 
+;       GCNTRD
+;  PURPOSE:
+;       Compute the stellar centroid by Gaussian fits to marginal X,Y, sums 
+; EXPLANATION:
+;       GCNTRD uses the DAOPHOT "FIND" centroid algorithm by fitting Gaussians
+;       to the marginal X,Y distributions.     User can specify bad pixels 
+;       (either by using the MAXGOOD keyword or setting them to NaN) to be
+;       ignored in the fit.    Pixel values are weighted toward the center to
+;       avoid contamination by neighboring stars. 
+;
+;  CALLING SEQUENCE: 
+;       GCNTRD, img, x, y, xcen, ycen, [ fwhm , /SILENT, /DEBUG, MAXGOOD = ,
+;                            /KEEPCENTER ]
+;
+;  INPUTS:     
+;       IMG - Two dimensional image array
+;       X,Y - Scalar or vector integers giving approximate stellar center
+;
+;  OPTIONAL INPUT:
+;       FWHM - floating scalar; Centroid is computed using a box of half
+;               width equal to 1.5 sigma = 0.637* FWHM.  GCNTRD will prompt
+;               for FWHM if not supplied
+;
+;  OUTPUTS:   
+;       XCEN - the computed X centroid position, same number of points as X
+;       YCEN - computed Y centroid position, same number of points as Y
+;
+;       Values for XCEN and YCEN will not be computed if the computed
+;       centroid falls outside of the box, or if there are too many bad pixels,
+;       or if the best-fit Gaussian has a negative height.   If the centroid 
+;       cannot be computed, then a  message is displayed (unless /SILENT is 
+;       set) and XCEN and YCEN are set to -1.
+;
+;  OPTIONAL OUTPUT KEYWORDS:
+;       MAXGOOD=  Only pixels with values less than MAXGOOD are used to in
+;               Gaussian fits to determine the centroid.    For non-integer
+;               data, one can also flag bad pixels using NaN values.
+;       /SILENT - Normally GCNTRD prints an error message if it is unable
+;               to compute the centroid.   Set /SILENT to suppress this.
+;       /DEBUG - If this keyword is set, then GCNTRD will display the subarray
+;               it is using to compute the centroid.
+;       /KeepCenter  By default, GCNTRD first convolves a small region around 
+;              the supplied position with a lowered Gaussian filter, and then 
+;              finds the maximum pixel in a box centered on the input X,Y 
+;              coordinates, and then extracts a new box about this maximum 
+;              pixel.   Set the /KeepCenter keyword  to skip the convolution 
+;              and finding the maximum pixel, and instead use a box 
+;              centered on the input X,Y coordinates.                          
+;  PROCEDURE: 
+;       Unless /KEEPCENTER is set, a small area around the initial X,Y is 
+;       convolved with a Gaussian kernel, and the maximum pixel is found.
+;       This pixel is used as the  center of a square, within 
+;       which the centroid is computed as the Gaussian least-squares fit
+;       to the  marginal sums in the X and Y directions. 
+;
+;  EXAMPLE:
+;       Find the centroid of a star in an image im, with approximate center
+;       631, 48.    Assume that bad (saturated) pixels have a value of 4096 or
+;       or higher, and that the approximate FWHM is 3 pixels.
+;
+;       IDL> GCNTRD, IM, 631, 48, XCEN, YCEN, 3, MAXGOOD = 4096       
+;  MODIFICATION HISTORY:
+;       Written June 2004, W. Landsman  following algorithm used by P. Stetson 
+;             in DAOPHOT2.
+;       Modified centroid computation (as in IRAF/DAOFIND) to allow shifts of
+;      more than 1 pixel from initial guess.    March 2008
+;      First perform Gaussian convolution prior to finding maximum pixel 
+;      to smooth out noise  W. Landsman  Jan 2009
+;-      
+ On_error,2 
+ compile_opt idl2
+
+ if N_params() LT 5 then begin
+        print,'Syntax: GCNTRD, img, x, y, xcen, ycen, [ fwhm, ' 
+        print,'              /KEEPCENTER, /SILENT, /DEBUG, MAXGOOD= ]'
+        PRINT,'img - Input image array'
+        PRINT,'x,y - Input scalar integers giving approximate X,Y position'
+        PRINT,'xcen,ycen - Output scalars giving centroided X,Y position'
+        return
+ endif else if N_elements(fwhm) NE 1 then $
+      read,'Enter approximate FWHM of image in pixels: ',fwhm
+
+
+ sz_image = size(img)
+ if sz_image[0] NE 2 then message, $
+   'ERROR - Image array (first parameter) must be 2 dimensional'
+
+ xsize = sz_image[1]
+ ysize = sz_image[2]
+ dtype = sz_image[3]
+ npts = N_elements(x) 
+ maxbox = 13
+ radius = 0.637*FWHM > 2.001             ;Radius is 1.5 sigma
+ radsq = radius^2
+ sigsq = ( fwhm/2.35482 )^2
+ nhalf = fix(radius) < (maxbox-1)/2   	;
+ nbox = 2*nhalf +1 	;# of pixels in side of convolution box 
+
+ xcen = x*0. - 1 & ycen = y*0 - 1.
+ ix = round(x)          ;Central X pixel        
+ iy = round(y)          ;Central Y pixel
+
+;Create the Gaussian convolution kernel in variable "g"
+ mask = bytarr( nbox, nbox )   ;Mask identifies valid pixels in convolution box 
+  g = fltarr( nbox, nbox )      
+ row2 = (findgen(Nbox)-nhalf)^2
+ g[0,nhalf] = row2
+  for i = 1, nhalf do begin
+	temp = row2 + i^2
+	g[0,nhalf-i] = temp         
+        g[0,nhalf+i] = temp
+ endfor
+ mask = fix(g LE radsq)
+ good = where( mask, pixels)  ;Value of c are now equal to distance to center
+   g = exp(-0.5*g/sigsq)	;Make g into a Gaussian kernel
+
+; In fitting Gaussians to the marginal sums, pixels will arbitrarily be 
+; assigned weights ranging from unity at the corners of the box to 
+; NHALF^2 at the center (e.g. if NBOX = 5 or 7, the weights will be
+;
+;                                 1   2   3   4   3   2   1
+;      1   2   3   2   1          2   4   6   8   6   4   2
+;      2   4   6   4   2          3   6   9  12   9   6   3
+;      3   6   9   6   3          4   8  12  16  12   8   4
+;      2   4   6   4   2          3   6   9  12   9   6   3
+;      1   2   3   2   1          2   4   6   8   6   4   2
+;                                 1   2   3   4   3   2   1
+;
+; respectively).  This is done to desensitize the derived parameters to 
+; possible neighboring, brighter stars.
+
+
+ x_wt = fltarr(nbox,nbox)
+ wt = nhalf - abs(findgen(nbox)-nhalf ) + 1
+ for i=0,nbox-1 do x_wt[0,i] = wt
+ y_wt = transpose(x_wt) 
+ pos = strtrim(x,2) + ' ' + strtrim(y,2)
+
+if ~keyword_set(Keepcenter) then begin 
+; Precompute convolution kernel
+ c = g*mask          ;Convolution kernel now in c      
+ sumc = total(c)
+ sumcsq = total(c^2) - sumc^2/pixels
+ sumc = sumc/pixels
+ c[good] = (c[good] - sumc)/sumcsq
+endif
+
+ for i = 0,npts-1 do begin        ;Loop over number of points to centroid
+
+ if ~keyword_set(keepcenter) then begin
+ if ( (ix[i] LT nhalf) || ((ix[i] + nhalf) GT xsize-1) || $
+      (iy[i] LT nhalf) || ((iy[i] + nhalf) GT ysize-1) ) then begin
+     if ~keyword_set(SILENT) then message,/INF, $
+           'Position '+ pos[i] + ' too near edge of image'
+     goto, DONE
+ endif
+ x1 = (ix[i]-nbox) > 0 
+ x2 = (ix[i] + nbox) < (xsize-1)
+ y1 = (iy[i]-nbox)  > 0
+ y2 = (iy[i] + nbox) < (ysize-1)  
+ h = img[x1:x2, y1:y2]
+ h = convol(float(h), c)
+ h= h[ nbox-nhalf: nbox + nhalf, nbox -nhalf: nbox + nhalf]
+ d= img[ix[i]-nhalf: ix[i]+nhalf, iy[i]-nhalf:iy[i]+nhalf]
+
+ if N_elements(maxgood) GT 0 then begin
+     ig = where(d lt maxgood, Ng)
+     mx = max(d[ig],/nan)
+ endif
+ mx = max( h,/nan)     ;Maximum pixel value in BIGBOX
+
+ mx_pos = where(h EQ mx, Nmax) ;How many pixels have maximum value?
+ idx = mx_pos mod nbox          ;X coordinate of Max pixel
+ idy = mx_pos / nbox          ;Y coordinate of Max pixel
+ if NMax GT 1 then begin        ;More than 1 pixel at maximum?
+     idx = round(total(idx)/Nmax)
+     idy = round(total(idy)/Nmax)
+ endif else begin
+     idx = idx[0]
+     idy = idy[0]
+ endelse
+  xmax = ix[i] - (nhalf) + idx    ;X coordinate in original image array
+  ymax = iy[i] - (nhalf) + idy    ;Y coordinate in original image array
+  endif else begin
+    xmax = ix[i]
+    ymax = iy[i]
+ endelse
+
+; ---------------------------------------------------------------------
+; check *new* center location for range
+; added by Hogg
+
+ if ( (xmax LT nhalf) || ((xmax + nhalf) GT xsize-1) || $
+      (ymax LT nhalf) || ((ymax + nhalf) GT ysize-1) ) then begin
+     if ~keyword_set(SILENT) then message,/INF, $
+           'Position '+ pos[i] + ' moved too near edge of image'
+     xcen[i] = -1   & ycen[i] = -1
+     goto, DONE
+ endif
+; ---------------------------------------------------------------------
+
+;  Extract  subimage centered on maximum pixel 
+
+ d = img[xmax-nhalf : xmax+nhalf, ymax-nhalf : ymax+nhalf]
+ 
+
+ if keyword_set(DEBUG) then begin
+       message,'Subarray used to compute centroid:',/inf
+       imlist,img,xmax,ymax,dx = nbox,dy=nbox
+ endif  
+
+ if N_elements(maxgood) GT 0 then $ 
+           mask = (d lt maxgood) else $
+   if (dtype eq 4) || (dtype EQ 5) then mask = finite(d) else $ 
+           mask = replicate(1b, nbox, nbox)
+  maskx = total(mask,2) GT 0
+  masky = total(mask,1) GT 0
+
+; At least 3 points are needed in the partial sum to compute the Gaussian
+
+  if (total(maskx) LT 3) || (total(masky) LT 3) then begin
+  if ~keyword_set(SILENT) then message,/INF, $
+	'Position '+ pos[i] + ' has insufficient good points'
+	 goto, DONE
+  endif
+  
+  ywt = y_wt*mask
+  xwt = x_wt*mask
+  wt1 = wt*maskx
+  wt2 = wt*masky
+  
+; Centroid computation:   The centroid computation was modified in Mar 2008 and
+; now differs from DAOPHOT which multiplies the correction dx by 1/(1+abs(dx)). 
+; The DAOPHOT method is more robust (e.g. two different sources will not merge)
+; especially in a package where the centroid will be subsequently be 
+; redetermined using PSF fitting.   However, it is less accurate, and introduces
+; biases in the centroid histogram.   The change here is the same made in the 
+; IRAF DAOFIND routine (see 
+; http://iraf.net/article.php?story=7211&query=daofind )
+
+ sd = total(d*ywt,2,/nan)
+ sg = total(g*ywt,2)
+ sumg = total(wt1*sg)
+ sumgsq = total(wt1*sg*sg)
+ 
+ sumgd = total(wt1*sg*sd)
+ sumgx = total(wt1*sg)
+ sumd = total(wt1*sd)
+ p = total(wt1)
+ xvec = nhalf - findgen(nbox) 
+ dgdx = sg*xvec
+ sdgdxs = total(wt1*dgdx^2)
+ sdgdx = total(wt1*dgdx) 
+ sddgdx = total(wt1*sd*dgdx)
+ sgdgdx = total(wt1*sg*dgdx)
+
+ hx = (sumgd - sumg*sumd/p) / (sumgsq - sumg^2/p)
+
+; HX is the height of the best-fitting marginal Gaussian.   If this is not
+; positive then the centroid does not make sense 
+
+  if (hx LE 0) then begin
+  if ~keyword_set(SILENT) then message,/INF, $
+	'Position '+ pos[i] + ' cannot be fit by a Gaussian'
+	 xcen[i] = -1	& ycen[i] = -1
+	 goto, DONE
+  endif
+
+ skylvl = (sumd - hx*sumg)/p
+ dx = (sgdgdx - (sddgdx-sdgdx*(hx*sumg + skylvl*p)))/(hx*sdgdxs/sigsq)
+  if (abs(dx) GE nhalf) then begin
+  if ~keyword_set(SILENT) then message,/INF, $
+	'Position '+ pos[i] + ' is too far from initial guess'
+	 goto, DONE
+  endif
+
+
+ 
+ xcen[i] = xmax + dx    ;X centroid in original array
+
+
+;Now repeat computation for Y centroid
+
+ sd = total(d*xwt,1,/nan)
+ sg = total(g*xwt,1)
+ sumg = total(wt2*sg)
+ sumgsq = total(wt2*sg*sg)
+ 
+ sumgd = total(wt2*sg*sd)
+ sumd = total(wt2*sd)
+ p = total(wt2)
+
+ yvec = nhalf - findgen(nbox) 
+ dgdy = sg*yvec
+ sdgdys = total(wt2*dgdy^2)
+ sdgdy = total(wt2*dgdy) 
+ sddgdy = total(wt2*sd*dgdy)
+ sgdgdy = total(wt2*sg*dgdy)
+
+ hy = (sumgd - sumg*sumd/p) / (sumgsq - sumg^2/p)
+
+  if (hy LE 0) then begin
+  if ~keyword_set(SILENT) then message,/INF, $
+	'Position '+ pos[i] + ' cannot be fit by a Gaussian'
+	 goto, DONE
+  endif
+
+ skylvl = (sumd - hy*sumg)/p
+ dy = (sgdgdy - (sddgdy-sdgdy*(hy*sumg + skylvl*p)))/(hy*sdgdys/sigsq)
+  if (abs(dy) GE nhalf) then begin
+  if ~keyword_set(SILENT) then message,/INF, $
+	'Position '+ pos[i] + ' is too far from initial guess'
+	 goto, DONE
+  endif
+ ycen[i] = ymax + dy    ;Y centroid in original array
+DONE:
+
+ endfor
+
+return
+end
diff --git a/Code/script_idl_mv/astrolib/geo2eci.pro b/Code/script_idl_mv/astrolib/geo2eci.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d11208c674e569b362e5eb7ec76ef80c4de626ed
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/geo2eci.pro
@@ -0,0 +1,79 @@
+;+
+; NAME:
+;     GEO2ECI
+;
+; PURPOSE:
+;     Convert geographic spherical coordinates to Earth-centered inertial coords
+;
+; EXPLANATION:
+;     Converts from geographic spherical coordinates [latitude, longitude, 
+;     altitude] to ECI (Earth-Centered Inertial) [X,Y,Z] rectangular 
+;     coordinates.    JD time is also needed.
+;
+;     Geographic coordinates are in degrees/degrees/km
+;     Geographic coordinates assume the Earth is a perfect sphere, with radius 
+;       equal to its equatorial radius.
+;     ECI coordinates are in km from Earth center at epoch TOD (True of Date)
+;
+; CALLING SEQUENCE:
+;       ECIcoord=geo2eci(gcoord,JDtime)
+;
+; INPUT:
+;       gcoord: geographic [latitude,longitude,altitude], or a an array [3,n] 
+;                of n such coordinates
+;       JDtime: Julian Day time, double precision. Can be a 1-D array of n 
+;               such times.
+;
+; KEYWORD INPUTS:
+;       None
+;
+; OUTPUT:
+;       a 3-element array of ECI [X,Y,Z] coordinates, or an array [3,n] of 
+;                n such coordinates, double precision.    The TOD epoch is the 
+;                supplied JDtime.   
+;       
+; COMMON BLOCKS:
+;       None
+;
+; PROCEDURES USED:
+;       CT2LST - Convert Local Civil Time to Local Mean Sidereal Time
+;
+; EXAMPLES:
+;
+;       IDL> ECIcoord=geo2eci([0,0,0], 2452343.38982663D)
+;       IDL> print,ECIcoord
+;      -3902.9606       5044.5548       0.0000000
+;
+;       (The above is the ECI coordinates of the intersection of the equator and
+;       Greenwich's meridian on 2002/03/09 21:21:21.021)
+;
+; MODIFICATION HISTORY:
+;       Written by Pascal Saint-Hilaire (shilaire@astro.phys.ethz.ch) 
+;             on 2002/05/14
+;       Update documentation to specify epoch is TOD.  
+;                R. Redmon  NOAA/NGDC   April 2014
+;               
+;-
+
+;====================================================================================
+FUNCTION geo2eci,incoord,JDtim
+
+        Re=6378.137     ; Earth's equatorial radius, in km
+
+        lat = DOUBLE(incoord[0,*])*!DPI/180.
+        lon = DOUBLE(incoord[1,*])*!DPI/180.
+        alt = DOUBLE(incoord[2,*])
+        JDtime= DOUBLE(JDtim)
+        
+        ct2lst,gst,0,0,JDtime
+        angle_sid=gst*2.*!DPI/24.       ; sidereal angle
+
+        theta=lon+angle_sid             ; azimuth
+        r=(alt+Re)*cos(lat)
+        X=r*cos(theta)
+        Y=r*sin(theta)
+        Z=(alt+Re)*sin(lat)
+                
+        RETURN,[X,Y,Z]
+END
+;====================================================================================
diff --git a/Code/script_idl_mv/astrolib/geo2geodetic.pro b/Code/script_idl_mv/astrolib/geo2geodetic.pro
new file mode 100644
index 0000000000000000000000000000000000000000..225384ab7b0a10b2d5d09810ce0e8a3c97079642
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/geo2geodetic.pro
@@ -0,0 +1,153 @@
+;+
+; NAME:
+;       GEO2GEODETIC
+;
+; PURPOSE:
+;       Convert from geographic/planetographic to geodetic coordinates
+; EXPLANATION:
+;       Converts from geographic (latitude, longitude, altitude) to geodetic
+;       (latitude, longitude, altitude).  In geographic coordinates, the 
+;           Earth is assumed a perfect sphere with a radius equal to its equatorial 
+;               radius. The geodetic (or ellipsoidal) coordinate system takes into 
+;               account the Earth's oblateness.
+;
+;       Geographic and geodetic longitudes are identical.
+;               Geodetic latitude is the angle between local zenith and the equatorial plane.
+;               Geographic and geodetic altitudes are both the closest distance between 
+;               the satellite and the ground.
+;
+;       The PLANET keyword allows a similar transformation for the other 
+;       planets  (planetographic to planetodetic coordinates). 
+;
+;       The EQUATORIAL_RADIUS and POLAR_RADIUS keywords allow the 
+;       transformation for any ellipsoid.
+;
+;       Latitudes and longitudes are expressed in degrees, altitudes in km.
+;
+;       REF: Stephen P.  Keeler and Yves Nievergelt, "Computing geodetic
+;       coordinates", SIAM Rev. Vol. 40, No. 2, pp. 300-309, June 1998
+;
+;       Planetary constants from "Allen's Astrophysical Quantities", 
+;       Fourth Ed., (2000)
+;
+; CALLING SEQUENCE:
+;       ecoord=geo2geodetic(gcoord,[ PLANET=,EQUATORIAL_RADIUS=, POLAR_RADIUS=])
+;
+; INPUT:
+;       gcoord = a 3-element array of geographic [latitude,longitude,altitude],
+;                or an array [3,n] of n such coordinates.
+;
+;
+; OPTIONAL KEYWORD INPUT:
+;       PLANET = keyword specifying planet (default is Earth).   The planet
+;                may be specified either as an integer (1-9) or as one of the
+;                (case-independent) strings 'mercury','venus','earth','mars',
+;                'jupiter','saturn','uranus','neptune', or 'pluto'
+;               
+;       EQUATORIAL_RADIUS : Self-explanatory. In km. If not set, PLANET's 
+;                value is used.
+;       POLAR_RADIUS : Self-explanatory. In km. If not set, PLANET's value is 
+;                used.
+;
+; OUTPUT:
+;      a 3-element array of geodetic/planetodetic [latitude,longitude,altitude],
+;        or an array [3,n] of n such coordinates, double precision.
+;
+; COMMON BLOCKS:
+;       None
+;
+; RESTRICTIONS:
+;
+;       Whereas the conversion from geodetic to geographic coordinates is given
+;       by an exact, analytical formula, the conversion from geographic to
+;       geodetic isn't. Approximative iterations (as used here) exist, but tend 
+;       to become less good with increasing eccentricity and altitude.
+;       The formula used in this routine should give correct results within
+;       six digits for all spatial locations, for an ellipsoid (planet) with
+;       an eccentricity similar to or less than Earth's.
+;       More accurate results can be obtained via calculus, needing a 
+;       non-determined amount of iterations.
+;       In any case, 
+;          IDL> PRINT,geodetic2geo(geo2geodetic(gcoord)) - gcoord
+;       is a pretty good way to evaluate the accuracy of geo2geodetic.pro.
+;
+; EXAMPLES:
+;
+;       Locate the geographic North pole, altitude 0., in geodetic coordinates
+;       IDL> geo=[90.d0,0.d0,0.d0]  
+;       IDL> geod=geo2geodetic(geo); convert to equivalent geodetic coordinates
+;       IDL> PRINT,geod
+;       90.000000       0.0000000       21.385000
+;
+;       As above, but for the case of Mars
+;       IDL> geod=geo2geodetic(geo,PLANET='Mars')
+;       IDL> PRINT,geod
+;       90.000000       0.0000000       18.235500
+;
+; MODIFICATION HISTORY:
+;       Written by Pascal Saint-Hilaire (shilaire@astro.phys.ethz.ch), May 2002
+;       Generalized for all solar system planets by Robert L. Marcialis
+;               (umpire@lpl.arizona.edu), May 2002
+;       Modified 2002/05/18, PSH: added keywords EQUATORIAL_RADIUS and 
+;               POLAR_RADIUS
+;-
+
+;================================================================================
+FUNCTION geo2geodetic,gcoord,PLANET=planet, $
+        EQUATORIAL_RADIUS=equatorial_radius, POLAR_RADIUS=polar_radius
+
+ sz_gcoord = size(gcoord,/DIMEN)
+ if sz_gcoord[0] LT 3 then message, $
+    'ERROR - 3 coordinates (latitude,longitude,altitude) must be specified'
+
+ if N_elements(PLANET) GT 0  then begin
+        if size(planet,/tname) EQ 'STRING' then begin 
+        choose_planet=['mercury','venus','earth','mars','jupiter','saturn', $
+                       'uranus','neptune','pluto']
+        index=where(choose_planet eq strlowcase(planet))
+        index=index[0]  ; make it a scalar
+        if index eq -1 then index = 2   ; default is Earth
+        endif else index = planet-1
+ endif else index=2 
+
+        Requator = [2439.7d0,6051.8d0,6378.137D, 3397.62d0,  71492d0, $
+                 60268.d0,      25559.d0,    24764.d0,    1195.d0]
+        Rpole = [2439.7d0, 6051.8d0, 6356.752d0, 3379.3845d0, 67136.5562d0, $
+                 54890.7686d0, 24986.1354d0, 24347.6551d0, 1195.d0]
+        Re = Requator[index]            ; equatorial radius
+        Rp = Rpole[index]                    ; polar radius
+        
+        IF KEYWORD_SET(EQUATORIAL_RADIUS) THEN Re=DOUBLE(equatorial_radius[0])
+        IF KEYWORD_SET(POLAR_RADIUS) THEN Rp=DOUBLE(polar_radius[0])
+                
+                e = sqrt(Re^2 - Rp^2)/Re
+                ;f=1/298.257D   ; flattening = (Re-Rp)/Re  [not needed, here]
+
+        glat=DOUBLE(gcoord[0,*])*!DPI/180.
+        glon=DOUBLE(gcoord[1,*])
+        galt=DOUBLE(gcoord[2,*])
+
+        x= (Re+galt) * cos(glat) * cos(glon)
+        y= (Re+galt) * cos(glat) * sin(glon)
+        z= (Re+galt) * sin(glat)
+        r=sqrt(x^2+y^2)
+
+                s=(r^2 + z ^2)^0.5 * (1 - Re*((1-e^2)/((1-e^2)*r^2 + z^2))^0.5)
+                t0=1+s*(1- (e*z)^2/(r^2 + z^2) )^0.5 /Re
+                dzeta1=z * t0
+                xi1=r*(t0 - e^2)
+                rho1= (xi1^2 + dzeta1^2)^0.5
+                c1=xi1/rho1
+                s1=dzeta1/rho1
+                b1=Re/(1- (e*s1)^2)^0.5
+                u1= b1*c1
+                w1= b1*s1*(1- e^2)
+                ealt= ((r - u1)^2 + (z - w1)^2)^0.5
+                elat= atan(s1,c1)
+
+        elat=elat*180./!DPI
+        elon=glon
+
+        RETURN,[elat,elon,ealt]
+END
+;===============================================================================
diff --git a/Code/script_idl_mv/astrolib/geo2mag.pro b/Code/script_idl_mv/astrolib/geo2mag.pro
new file mode 100644
index 0000000000000000000000000000000000000000..21f878675bbb4902a5c3fadd1ba63c16af50a842
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/geo2mag.pro
@@ -0,0 +1,103 @@
+;+
+; NAME:
+;       GEO2MAG()
+;
+; PURPOSE:
+;       Convert from geographic to geomagnetic coordinates
+; EXPLANATION:
+;       Converts from GEOGRAPHIC (latitude,longitude) to GEOMAGNETIC (latitude, 
+;       longitude).   (Altitude remains the same)
+;
+;       Latitudes and longitudes are expressed in degrees.
+;
+; CALLING SEQUENCE:
+;       mcoord=geo2mag(gcoord)
+;
+; INPUT:
+;       gcoord = a 2-element array of geographic [latitude,longitude], or an 
+;                array [2,n] of n such coordinates.
+;
+; KEYWORD INPUTS:
+;       None
+;
+; OUTPUT:
+;       a 2-element array of magnetic [latitude,longitude], or an array [2,n] 
+;         of n such coordinates                     
+;
+; COMMON BLOCKS:
+;       None
+;
+; EXAMPLES:
+;       geographic coordinates of magnetic south pole
+;
+;       IDL> mcoord=geo2mag([79.3,288.59])      
+;       IDL> print,mcoord
+;       89.999992      -173.02325
+;
+; MODIFICATION HISTORY:
+;       Written by Pascal Saint-Hilaire (Saint-Hilaire@astro.phys.ethz.ch), 
+;            May 2002
+;               
+;-
+
+;====================================================================================
+FUNCTION geo2mag,incoord
+
+        ; SOME 'constants'...
+        Dlong=288.59D   ; longitude (in degrees) of Earth's magnetic south pole
+                        ;(which is near the geographic north pole!) (1995)
+        Dlat=79.30D     ; latitude (in degrees) of same (1995)
+        R = 1D          ; distance from planet center (value unimportant -- 
+                 ;just need a length for conversion to rectangular coordinates)
+
+        ; convert first to radians
+        Dlong=Dlong*!DPI/180.
+        Dlat=Dlat*!DPI/180.
+
+        glat=DOUBLE(incoord[0,*])*!DPI/180.
+        glon=DOUBLE(incoord[1,*])*!DPI/180.
+        galt=glat * 0. + R
+        
+        coord=[glat,glon,galt]
+
+        ;convert to rectangular coordinates
+        ;       X-axis: defined by the vector going from Earth's center towards
+        ;            the intersection of the equator and Greenwitch's meridian.
+        ;       Z-axis: axis of the geographic poles
+        ;       Y-axis: defined by Y=Z^X
+        x=coord[2,*]*cos(coord[0,*])*cos(coord[1,*])
+        y=coord[2,*]*cos(coord[0,*])*sin(coord[1,*])
+        z=coord[2,*]*sin(coord[0,*])
+
+        ;Compute 1st rotation matrix : rotation around plane of the equator,
+        ;from the Greenwich meridian to the meridian containing the magnetic
+        ;dipole pole.
+        geolong2maglong=dblarr(3,3)
+        geolong2maglong[0,0]=cos(Dlong)
+        geolong2maglong[0,1]=sin(Dlong)
+        geolong2maglong[1,0]=-sin(Dlong)
+        geolong2maglong[1,1]=cos(Dlong)
+        geolong2maglong[2,2]=1.
+        out=geolong2maglong # [x,y,z]
+
+        ;Second rotation : in the plane of the current meridian from geographic
+        ;                  pole to magnetic dipole pole.
+        tomaglat=dblarr(3,3)
+        tomaglat[0,0]=cos(!DPI/2-Dlat)
+        tomaglat[0,2]=-sin(!DPI/2-Dlat)
+        tomaglat[2,0]=sin(!DPI/2-Dlat)
+        tomaglat[2,2]=cos(!DPI/2-Dlat)
+        tomaglat[1,1]=1.
+        out= tomaglat # out
+
+        ;convert back to latitude, longitude and altitude
+        mlat=atan(out[2,*],sqrt(out[0,*]^2+out[1,*]^2))
+        mlat=mlat*180./!DPI
+        mlon=atan(out[1,*],out[0,*])
+        mlon=mlon*180./!DPI
+        ;malt=sqrt(out[0,*]^2+out[1,*]^2+out[2,*]^2)-R 
+;  I don't care about that one...just put it there for completeness' sake
+
+        RETURN,[mlat,mlon]
+END
+;===============================================================================
diff --git a/Code/script_idl_mv/astrolib/geodetic2geo.pro b/Code/script_idl_mv/astrolib/geodetic2geo.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0615516b34a6c711dcf80bd3efa7cd30524d6283
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/geodetic2geo.pro
@@ -0,0 +1,125 @@
+;+
+; NAME:
+;       GEODETIC2GEO
+;
+; PURPOSE:
+;       Convert from geodetic (or planetodetic) to geographic coordinates
+; EXPLANATION:
+;       Converts from geodetic (latitude, longitude, altitude) to geographic
+;       (latitude, longitude, altitude).  In geographic coordinates, the 
+;       Earth is assumed a perfect sphere with a radius equal to its equatorial 
+;       radius. The geodetic (or ellipsoidal) coordinate system takes into 
+;       account the Earth's oblateness.
+;
+;       Geographic and geodetic longitudes are identical.
+;       Geodetic latitude is the angle between local zenith and the equatorial 
+;       plane.   Geographic and geodetic altitudes are both the closest distance
+;       between the satellite and the ground.
+;
+;       The PLANET keyword allows a similar transformation for the other 
+;       planets  (planetodetic to planetographic coordinates). 
+;
+;       The EQUATORIAL_RADIUS and POLAR_RADIUS keywords allow the 
+;       transformation for any ellipsoid.
+;
+;       Latitudes and longitudes are expressed in degrees, altitudes in km.
+;
+;       REF: Stephen P.  Keeler and Yves Nievergelt, "Computing geodetic
+;       coordinates", SIAM Rev. Vol. 40, No. 2, pp. 300-309, June 1998
+;       Planetary constants from "Allen's Astrophysical Quantities", 
+;       Fourth Ed., (2000)
+;
+; CALLING SEQUENCE:
+;       gcoord = geodetic2geo(ecoord, [ PLANET= ] )
+;
+; INPUT:
+;       ecoord = a 3-element array of geodetic [latitude,longitude,altitude],
+;                or an array [3,n] of n such coordinates.
+;
+; OPTIONAL KEYWORD INPUT:
+;       PLANET = keyword specifying planet (default is Earth).   The planet
+;                may be specified either as an integer (1-9) or as one of the
+;                (case-independent) strings 'mercury','venus','earth','mars',
+;                'jupiter','saturn','uranus','neptune', or 'pluto'
+;
+;       EQUATORIAL_RADIUS : Self-explanatory. In km. If not set, PLANET's value
+;                is used.   Numeric scalar
+;       POLAR_RADIUS : Self-explanatory. In km. If not set, PLANET's value is 
+;                 used.   Numeric scalar
+;
+; OUTPUT:
+;       a 3-element array of geographic [latitude,longitude,altitude], or an
+;         array [3,n] of n such coordinates, double precision
+;
+;       The geographic and geodetic longitudes will be identical.
+; COMMON BLOCKS:
+;       None
+;
+; EXAMPLES:
+;
+;       IDL> geod=[90,0,0]  ; North pole, altitude 0., in geodetic coordinates
+;       IDL> geo=geodetic2geo(geod)
+;       IDL> PRINT,geo
+;       90.000000       0.0000000      -21.385000
+;
+;       As above, but the equivalent planetographic coordinates for Mars
+;       IDL> geod=geodetic2geo(geod,PLANET='Mars'); 
+;       IDL> PRINT,geod
+;       90.000000       0.0000000      -18.235500
+;
+; MODIFICATION HISTORY:
+;       Written by Pascal Saint-Hilaire (shilaire@astro.phys.ethz.ch),
+;                  May 2002
+;
+;       Generalized for all solar system planets by Robert L. Marcialis
+;               (umpire@lpl.arizona.edu), May 2002
+;
+;       Modified 2002/05/18, PSH: added keywords EQUATORIAL_RADIUS and 
+;                POLAR_RADIUS
+;
+;-
+;===================================================================================
+FUNCTION geodetic2geo,ecoord,PLANET=planet,     $
+        EQUATORIAL_RADIUS=equatorial_radius, POLAR_RADIUS=polar_radius
+
+ sz_ecoord = size(ecoord,/DIMEN)
+ if sz_ecoord[0] LT 3 then message, $
+    'ERROR - 3 coordinates (latitude,longitude,altitude) must be specified'
+
+ if N_elements(PLANET) GT 0  then begin
+        if size(planet,/tname) EQ 'STRING' then begin 
+        choose_planet=['mercury','venus','earth','mars','jupiter','saturn', $
+                       'uranus','neptune','pluto']
+        index=where(choose_planet eq strlowcase(planet))
+        index=index[0]  ; make it a scalar
+        if index eq -1 then index = 2   ; default is Earth
+        endif else index = planet-1
+ endif else index=2 
+
+        Requator = [2439.7d0,6051.8d0,6378.137D, 3397.62d0,  71492d0, $
+                 60268.d0,      25559.d0,    24764.d0,    1195.d0]
+        Rpole = [2439.7d0, 6051.8d0, 6356.752d0, 3379.3845d0, 67136.5562d0, $
+                 54890.7686d0, 24986.1354d0, 24347.6551d0, 1195.d0]
+                ;f=1/298.257D   ; flattening = (Re-Rp)/Re
+        Re = Requator(index)            ; equatorial radius
+        Rp = Rpole(index)                       ; polar radius
+
+        IF KEYWORD_SET(EQUATORIAL_RADIUS) THEN Re=DOUBLE(equatorial_radius[0])
+        IF KEYWORD_SET(POLAR_RADIUS) THEN Rp=DOUBLE(polar_radius[0])
+
+        e = sqrt(Re^2 - Rp^2)/Re
+        elat = DOUBLE(ecoord[0,*])*!DPI/180.
+        elon = DOUBLE(ecoord[1,*])
+        ealt = DOUBLE(ecoord[2,*])
+
+        beta=sqrt(1-(e*sin(elat))^2)
+        r=(Re/beta + ealt)*cos(elat)
+        z=(Re*(1-e^2)/beta + ealt)*sin(elat)
+
+        glat=atan(z,r)*180./!DPI
+        glon=elon
+        galt=sqrt(r^2+z^2) - Re
+
+        RETURN,[glat,glon,galt]
+END
+;===================================================================================
diff --git a/Code/script_idl_mv/astrolib/get_coords.pro b/Code/script_idl_mv/astrolib/get_coords.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0e3427c97b020bc56b0538adc289ab659bf5a901
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/get_coords.pro
@@ -0,0 +1,165 @@
+pro GET_COORDS, Coords, PromptString, NumVals, InString=InString, Quiet=Quiet
+;*******************************************************************************
+;+
+; NAME:
+;       GET_COORDS
+;
+; PURPOSE:
+;       Converts a string with angular coordinates  to floating point values.  
+; EXPLANATION:
+;       Although called by ASTRO.PRO, this is a general purpose routine.
+;       The user may input as floating point or sexagesimal.  If user inputs 
+;       calling procedure's job to convert hours to degrees if needed.
+;       Since the input string is parsed character-by-character, ANY character
+;       that is not a digit, minus sign or decimal point may be used as a 
+;       delimiter, i.e. acceptable examples of user input are:
+;
+;       1:03:55 -10:15:31
+;       1 3 55.0 -10 15 31
+;       1*3 55              -10abcd15efghij31
+;       1.065278  hello   -10.25861
+;
+; CALLING SEQUENCE:
+;       GET_COORDS, Coords, [ PromptString, NumVals, INSTRING =, /QUIET ]
+;
+; OPTIONAL INPUT:
+;       PromptString - A string to inform the user what data are to be entered
+;
+; OPTIONAL KEYWORD INPUT:
+;       InString - a keyword that, if set, is assumed to already contain the
+;               input data string to be parsed.  If this keyword is set, then
+;               the user is not prompted for any input.
+;       /Quiet - if set the program won't printout any error messages, but bad
+;               input is still flagged by Coords=[-999,-999].
+;
+; OUTPUT:
+;       Coords - a 2 element floating array containing the coordinates.  The
+;               vector [-999,-999] is returned if there has been an error.
+;
+; OPTIONAL OUTPUT:
+;       NumVals - the number of separate values entered by the user:  2 if the
+;               user entered the coordinates as floating point numbers, 6 if 
+;               the user entered the coordinates as sexagesimal numbers.  Some
+;               calling procedures might find this information useful (e.g., to
+;               to print some output in the same format as the user's input).
+;
+; REVISION HISTORY:
+;       Written by Joel Parker, 5 MAR 90
+;       Included InString and Quiet keywords.  Cleaned up some of the code and
+;       comments.  JWmP,  16 Jun 94
+;
+;*******************************************************************************
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+On_error,2
+
+if (N_params() eq 0) then begin
+      print,'Syntax - ' + $
+         'GET_COORDS, Coords, [PromptString, NumVals, INSTRING=, /QUIET]'
+      return
+endif 
+
+;
+;   Define some parameters and variables.
+;
+if (N_Params() lt 2) then PromptString = " Please input the coordinates"
+Bell     = string(7B)
+Minus    = 45      ; ascii of "-"
+Decimal  = 46      ; ascii of "."
+Zero     = 48      ; ascii of "0"
+Nine     = 57      ; ascii of "9"
+ValArr   = dblarr(6)
+SignArr  = intarr(6) + 1
+NumVals  = 0
+StartPos = -1
+
+;
+;    If the InString keyword is not set, then prompt the user for input.  If 
+; nothing is entered, return [-999,-999] as a warning flag to the calling 
+; procedure.
+;
+if keyword_set(InString) then begin
+   Coords = InString
+endif else begin
+   Coords = ""
+   print,form =  "(1X,A,$)", + PromptString + " {RETURN to exit} "
+   read, Coords
+endelse
+
+Coords = strtrim(Coords) + " "  ; The final space is needed for parsing purposes
+if (Coords eq " ") then begin
+   Coords = [-999,-999]
+   return
+endif
+
+;
+;   All's well.  Get the byte values for the characters in the input string.
+;
+BCoords = byte(Coords)
+
+;
+;   Begin the loop that parses the input string.
+;   Start by loading the byte value of the next character into the BC variable.
+; Check to see if the character is a minus sign (if so, set the flag in the 
+; SignArr array to -1).  Check to see if the character is a numeral between 0-9 
+; or a decimal (if so, then the NumFlag is set to 1).
+;
+for N = 0,(strlen(Coords)-1) do begin
+   BC = BCoords[N]
+   if (BC eq Minus) then SignArr[NumVals] = -1
+   NumFlag = ((BC ge Zero) and (BC le Nine)) or (BC eq Decimal)
+
+;
+;   If the number flag is set, but StartPos = -1, then we are starting a new 
+; value.  Load the character's position in StartPos.
+;
+   if (NumFlag and (StartPos eq -1)) then StartPos = N
+
+;
+;     If the number flag is NOT set, but StartPos > -1, then we have just 
+; finished reading a number.  Read the number from StartPos to the current 
+; position, and reset StartPos to -1.
+;   Put the resulting number in the ValArr.
+;
+   if (~(NumFlag) && (StartPos gt -1)) then begin
+      if (NumVals lt 6) then begin
+         ValArr[NumVals] = float(strmid(Coords, StartPos, (N - StartPos)))
+      endif
+      StartPos = -1
+      NumVals  = NumVals + 1
+   endif
+endfor
+
+;
+;   Coords should be a 2 or 6 element vector {depending on the type of input}.
+; It is converted to a 2 element vector such that Coords = [RA/Long, Dec/Lat].
+;
+case NumVals of
+
+   2 : Coords = (ValArr * SignArr)[0:1]
+
+   6 : begin
+      Temp = where(SignArr[0:2] eq -1)
+      if (Temp[0] eq -1) then XSign = 1 else XSign = -1
+      Temp = where(SignArr[3:5] eq -1)
+      if (Temp[0] eq -1) then YSign = 1 else YSign = -1
+      X = (ValArr[0] + (ValArr[1] / 60.) + (ValArr[2] / 3600.)) * XSign
+      Y = (ValArr[3] + (ValArr[4] / 60.) + (ValArr[5] / 3600.)) * YSign
+      Coords = [X,Y]
+   end
+
+   else : begin
+      Coords = [-999,-999]
+      if ~keyword_set(Quiet) then begin
+         print, Bell
+         print, "ERROR - Invalid Input!"
+         print, "Coordinates must be input as 2 or 6 values."
+         print, "For example:  1.568 -10.343   or  1 34 4.8  10 20 34.8"
+      endif
+   endelse
+
+endcase
+
+return
+end   ;   procedure GET_COORDS   by   Joel Parker   16 Jun 94
diff --git a/Code/script_idl_mv/astrolib/get_date.pro b/Code/script_idl_mv/astrolib/get_date.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d18e854924735b9f76f31d084c0eb995edae5ab1
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/get_date.pro
@@ -0,0 +1,109 @@
+pro get_date, dte, in_date, OLD = old, TIMETAG = timetag
+;+
+; NAME:
+;       GET_DATE
+; PURPOSE:
+;       Return the (current) UTC date in CCYY-MM-DD format for FITS headers
+; EXPLANATION:
+;       This is the format required by the DATE and DATE-OBS keywords in a 
+;       FITS header.  
+;
+; CALLING SEQUENCE:
+;       GET_DATE, FITS_date, [ in_date, /OLD, /TIMETAG ]
+; OPTIONAL INPUTS:
+;       in_date - string (scalar or vector) containing dates in IDL
+;            systime() format (e.g. 'Tue Sep 25 14:56:14 2001')
+; OUTPUTS:
+;       FITS_date = A scalar character string giving the current date.    Actual
+;               appearance of dte depends on which keywords are supplied.
+;       
+;       No Keywords supplied - dte is a 10 character string with the format
+;               CCYY-MM-DD where <CCYY> represents a calendar year, <MM> the
+;               ordinal number of a calendar month within the calendar year, 
+;               and <DD> the ordinal number of a day within the calendar month.
+;       /TIMETAG set - dte is a 19 character string with the format
+;               CCYY-MM-DDThh:mm:ss where <hh> represents the hour in the day,
+;                <mm> the minutes, <ss> the seconds, and the literal 'T' the 
+;               ISO 8601 time designator
+;       /OLD set - dte is an 8 character string in DD/MM/YY format
+;
+; INPUT KEYWORDS:
+;       /TIMETAG - Specify the time to the nearest second in the DATE format
+;       /OLD - Return the DATE format formerly (pre-1997) recommended for FITS
+;               Note that this format is now deprecated because it uses only
+;               a 2 digit representation of the year. 
+; EXAMPLE:
+;       Add the current date to the DATE keyword in a FITS header,h
+;     
+;       IDL> GET_DATE,dte
+;       IDL> sxaddpar, h, 'DATE', dte, 'Date header was created'
+;
+; NOTES:
+;       (1) A discussion of the DATExxx syntax in FITS headers can be found in
+;       http://www.cv.nrao.edu/fits/documents/standards/year2000.txt
+;
+;       (2) Those who wish to use need further flexibility in their date 
+;       formats (e.g. to use TAI time) should look at Bill Thompson's time
+;       routines in http://sohowww.nascom.nasa.gov/solarsoft/gen/idl/time
+;
+; PROCEDURES USED:
+;       DAYCNV - Convert Julian date to Gregorian calendar date
+; REVISION HISTORY:
+;       Written      W. Landsman          March 1991
+;       Major rewrite to write new DATExxx syntax  W. Landsman  August 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Work after year 2000 even with /OLD keyword W. Landsman January 2000
+;       Don't need to worry about TIME_DIFF since V5.4 W. Landsman July 2001
+;       Assume since V5.4, remove LOCAL_DIFF keyword  W. Landsman April 2006
+;-
+ On_error,2
+ compile_opt idl2
+ 
+ if N_params() LT 1 then begin
+     print,'Syntax - Get_date, FITS_date, [ in_date, /TIMETAG, /OLD ]'
+     print,'  FITS_date - output string giving date(s) in FITS format'
+     print,'  in-date - Optional input string giving date in systime() format'
+     return
+ endif
+
+ if N_elements(in_date) GT 0 then begin
+     mn = strmid(in_date,4,3)
+     month = month_cnv(mn)
+     day = fix(strmid(in_date,8,2))
+     ihr = fix(strmid(in_date,11,2))
+     imn = fix(strmid(in_date,14,2))
+     sec = fix(strmid(in_date,17,2))
+     yr = fix(strmid(in_date,20,4))
+ endif else begin
+     seconds = systime(1)          ;Number of seconds since Jan 1, 1970
+     dayseconds = 86400.D0               ;Number of seconds in a day
+     mjd = seconds/dayseconds + 40587.0D
+     jd =  2400000.5D + mjd
+     DAYCNV, jd, yr, month, day, hr
+ endelse 
+
+ if keyword_set(old) then begin
+
+ if yr GE 2000 then yr = yr - 100
+ dte =  string(day,f='(I2.2)') + '/' + string(month,f='(i2.2)') +  $
+        '/' + string( yr-1900,f='(I2.2)')                          
+
+ endif else $ 
+
+ dte =  string(yr,f='(I4.4)') + '-' + string(month,f='(i2.2)') + '-' + $
+        string(day,f='(I2.2)')
+
+ if keyword_set(TIMETAG) then begin
+ if N_elements(in_date) EQ 0 then begin
+   ihr = fix(hr)
+   mn = (hr - ihr)*60.
+   imn = fix(mn)
+   sec = round((mn - imn)*60.)
+ endif 
+
+ dte =  dte + 'T' + string(ihr,f='(I2.2)') + ':' + string(imn,f='(I2.2)') +  $
+               ':' + string(round(sec),f='(I2.2)')
+ endif
+         
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/get_equinox.pro b/Code/script_idl_mv/astrolib/get_equinox.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d1a24853cbde2432921b2e917058736c2925dedf
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/get_equinox.pro
@@ -0,0 +1,101 @@
+FUNCTION GET_EQUINOX,HDR,CODE, ALT = alt                                       
+;+
+; NAME:
+;       GET_EQUINOX
+; PURPOSE:
+;       Return the equinox value from a FITS header.  
+; EXPLANATION:
+;       Checks for 4 possibilities:
+;
+;       (1)  If the EQUINOX keyword is found and has a numeric value, then this
+;               value is returned
+;       (2)  If the EQUINOX keyword has the values 'J2000' or 'B1950', then
+;               either 2000. or 1950. is returned.
+;       (3)  If the EQUINOX keyword is not found, then GET_EQUINOX will return
+;               the EPOCH keyword value.   This usage of EPOCH is disparaged.
+;       (4)  If neither EQUINOX no EPOCH is found, then the RADESYS keyword 
+;               (or the deprecated RADECSYS keyword) is checked.   If the value 
+;               is 'ICRS' or 'FK5' then 2000 is is returned, if it is 'FK4' then
+;               1950 is returned.
+;
+;       According Calabretta & Greisen (2002, A&A, 395, 1077) the EQUINOX should
+;       be written as a numeric value, as in format (1).   However, in older 
+;       FITS headers, the EQUINOX might have been written using formats (2) or 
+;       (3).      
+; CALLING SEQUENCE:
+;       Year = GET_EQUINOX( Hdr, [ Code ] )   
+;
+; INPUTS:
+;       Hdr - FITS Header, string array, will be searched for the EQUINOX
+;               (or EPOCH) keyword.
+;
+; OUTPUT:
+;       Year - Year of equinox in FITS header, numeric scalar
+; OPTIONAL OUTPUT:
+;       Code - Result of header search, scalar
+;               -1 - EQUINOX, EPOCH or RADECSYS keyword not found in header
+;               0 - EQUINOX found as a numeric value
+;               1 - EPOCH keyword used for equinox (not recommended)
+;               2 - EQUINOX found as  'B1950'
+;               3 - EQUINOX found as  'J2000'
+;               4 - EQUINOX derived from value of RADESYS or RADECSYS keyword
+;                   'ICRS', 'FK5' ==> 2000,  'FK4' ==> 1950
+; OPTIONAL KEYWORD INPUT: 
+;       ALT -  single character 'A' through 'Z' or ' ' specifying which  
+;             astrometry system to use in the FITS header.    The default is
+;             to use the primary astrometry or ALT = ''.   If /ALT is set, 
+;             then this is equivalent to ALT = 'A'.   See Section 3.3 of 
+;             Greisen & Calabretta (2002, A&A, 395, 1061) for information about
+;             alternate astrometry keywords.
+; PROCEDURES USED:
+;       ZPARCHECK, SXPAR()  
+; NOTES:
+;       Technically, RADESYS = 'ICRS' does not specify any equinox, but can be 
+;       assumed to be equivalent to J2000 for all but highest-precision work.      
+; REVISION HISTORY:                                               
+;       Written  W. Landsman        STX              March, 1991
+;       Don't use !ERR          W. Landsman   February 2000
+;       N = 1 for check of EPOCH keyword, not 0 S. Ott July 2000
+;       Added ALT keyword, recognize RADESYS along with deprecated RADECSYS
+;              W. Landsman   Sep 2011
+;-     
+ compile_opt idl2
+ On_error,2
+ 
+ if N_elements(alt) EQ 0 then alt = '' else if (alt EQ '1') then alt = 'A' $
+    else alt = strupcase(alt)
+ zparcheck, 'GET_EQUINOX', hdr, 1, 7, 1, 'FITS Header array'
+ code = -1                      ;Not found yet
+
+ year = SXPAR( Hdr, 'EQUINOX' + alt, Count = n )    ;YEAR of Initial equinox
+ if n EQ 0 then begin
+
+     year = sxpar( Hdr, 'EPOCH', Count = n )  ;Check EPOCH if EQUINOX not found
+     if n EQ 1 then code = 1 else begin       ;EPOCH keyword found
+            
+     sys = sxpar( Hdr, 'RADESYS'+alt, Count = n)
+     if n EQ 0 then sys = sxpar( Hdr, 'RADECSYS', Count = n) 
+              if n EQ 1 then begin
+                  code = 4 
+                  case strmid(sys,0,3) of
+                  'ICR': year = 2000
+                  'FK5': year = 2000
+                  'FK4': year = 1950
+                  else: 
+                  endcase
+               endif
+         endelse
+ endif else begin  
+
+    tst = strmid(year,0,1)     ;Check for 'J2000' or 'B1950' values
+    if (tst EQ 'J') || (TST EQ 'B') then begin 
+           year = float(strmid(year,1,strlen(year)-1) )
+           if tst EQ 'J' then code = 3
+           if tst EQ 'B' then code = 2 
+    endif else code = 0
+
+ endelse     
+
+ return, year
+ end
+
diff --git a/Code/script_idl_mv/astrolib/get_juldate.pro b/Code/script_idl_mv/astrolib/get_juldate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..585cc7d8f550ec440ee72bab6b878548cf7f8e80
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/get_juldate.pro
@@ -0,0 +1,44 @@
+pro get_juldate,jd
+;+
+; NAME:
+;    GET_JULDATE
+; PURPOSE:
+;     Return the current Julian Date
+;
+; EXPLANATION:
+;     In V5.4, GET_JULDATE became completely obsolete with the introduction
+;     of the /UTC keyword to SYSTIME().   So GET_JULDATE,jd is equivalent to
+;     jd = SYSTIME(/JULIAN,/UTC).
+;
+; CALLING SEQUENCE:
+;       GET_JULDATE,jd
+;
+; INPUTS:
+;       None
+;
+; OUTPUTS:
+;       jd = Current Julian Date, double precision scalar
+;
+; EXAMPLE:
+;       Return the current hour, day, month and year as integers
+;
+;       IDL> GET_JULDATE, JD                  ;Get current Julian date
+;       IDL> DAYCNV, JD, YR, MON, DAY, HOURS  ;Convert to hour,day month & year
+;
+; METHOD:
+;       A call is made to SYSTIME(/JULIAN,/UTC).
+;
+; REVISION HISTORY:
+;       Written Wayne Landsman                March, 1991
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Assume since V5.4 Use /UTC keyword to SYSTIME()  W. Landsman April 2006
+;-
+ compile_opt idl2
+ if N_Params() LT 1 then begin
+     Print,'Syntax - GET_JULDATE, JD'
+     return
+ endif
+
+     jd = SYSTIME(/JULIAN,/UTC)
+ return    
+ end
diff --git a/Code/script_idl_mv/astrolib/get_pipe_filesize.pro b/Code/script_idl_mv/astrolib/get_pipe_filesize.pro
new file mode 100644
index 0000000000000000000000000000000000000000..743b6af9b848f9a79c4d02bf88c43509198f99b2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/get_pipe_filesize.pro
@@ -0,0 +1,57 @@
+pro get_pipe_filesize, unit, nbytes, buffer = buffer
+;+
+; NAME:
+;       GET_PIPE_FILESIZE
+;
+; PURPOSE:
+;       Determine the number of bytes in a unit opened as a pipe with SPAWN
+;
+; EXPLANATION:
+;      Reads into a buffer until the end of file is reached and then counts the
+;      number of bytes read.   Needed because the fstat.size field is not 
+;      automatically set for a unit opened as a pipe.
+;
+; CALLING SEQUENCE:
+;       GET_PIPE_FILESIZE,unit, nbytes_in_file, BUFFER = 
+;
+; INPUTS:
+;       unit -   IDL unit number of a previously opened file.    For example,
+;         an FPACK  ( http://heasarc.gsfc.nasa.gov/fitsio/fpack/ ) compressed 
+;         FITS file could be opened as follows:
+;
+;        IDL> spawn,'funpack -S test.fits.fz', unit=unit
+; OUTPUTS:
+;       nbytes_in_file - Unsigned long64 integer giving number of bytes in 
+;         the file.
+;
+; INPUT KEYWORD PARAMETERS:
+;       BUFFER  Integer giving number of bytes in the buffer.  Default = 
+;     .          1000000
+; NOTES:
+;      Unite must be opened prior to calling GET_PIPE_FILESIZE, and the number
+;      of bytes is counted from the current pointer position.  The pointer is 
+;      left at the end of the file upon return. 
+; PROCEDURES USED:
+;     SETDEFAULTVALUE  
+; REVISION HISTORY:
+;      Written, W. Landsman  Adnet   Dec 2010 
+
+ On_error,2
+ compile_opt idl2
+
+ nbytes = 0ULL
+ setdefaultvalue, buffer, 1000000
+ ON_IOerror,Done
+ b= bytarr(buffer,/noz)
+
+ while 1 do begin
+   readu,unit,b
+   nbytes += buffer
+ endwhile
+
+Done:
+ On_IOError, null
+ nbytes += (fstat(unit)).transfer_count
+ 
+  return
+ end
diff --git a/Code/script_idl_mv/astrolib/getopt.pro b/Code/script_idl_mv/astrolib/getopt.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9ad56a95c2f13e3f5cad48d3856568e3026cc822
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/getopt.pro
@@ -0,0 +1,95 @@
+function getopt,input,type,numopt,count =count
+;+
+; NAME:
+;	GETOPT
+; PURPOSE:
+;	Convert a string supplied by the user into a valid scalar or vector
+; EXPLANATION:
+;	Distinct elements in the string may be
+;	separated by either a comma or a space.  The output scalar
+;	or vector can be specified to be  either  integer or floating
+;	point.   A null string is converted to a zero.   
+; CALLING SEQUENCE:
+;     option = GETOPT( input, [ type, numopt, COUNT = ])
+;
+; INPUTS:
+;	input   - string that was input by user in response to a prompt
+;		Arithmetic operations can be included in the string (see
+;		examples)
+;
+; OPTIONAL INPUTS:
+;	type    - Either an "I" (integer) or an "F" (floating point) specifying 
+;		the datatype of the output vector.  Default is floating point
+;
+;	numopt  - number of values expected by calling procedure
+;		If less than NUMOPT values are supplied the output
+;		vector will be padded with zeros.  
+; OUTPUTS:
+;	option  - scalar or vector containing the numeric conversion of
+;		the fields in the string INPUT.  If NUMOPT is not
+;		supplied, the number of elements in OPTION will 
+;		equal the number of distinct fields in INPUT.
+; OPTIONAL INPUT KEYWORD:
+;       Count - integer giving the number of values actually returned by
+;               GETOPT.   If the input is invalid then COUNT is set to -1
+; NOTES:
+;	(1) If an input is invalid, Count is set to -1 and the result is set 
+;		to 999.
+;	(2) GETOPT uses the execute function to interpret the user string.   
+;	 	Therefore GETOPT itself cannot be called with the EXECUTE 
+;		function.
+;	(3) GETOPT has a hard limit of 10 tokens in the input string. 
+;
+; EXAMPLES:
+;	(1)   a = getopt( '3.4,5*4 ', 'I' )    yields   a = [ 3, 20]
+;	(2)   a = getopt( '5/2.', 'F', 5)      yields   a = [2.5,0.,0.,0.,0.]
+;	(3)   a = getopt( '2*3,5,6')           yields   a = [6.,5.,6.]
+;
+; REVISON HISTORY:
+;	written by B. Pfarr, STX, 5/6/87
+;	change value of !ERR W. Landsman   STX,  6/30/88
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+
+ Err = 0
+ inp = strtrim(input,2)               ;Remove leading & trailing blanks
+ comma = strpos(inp,',')              ;look for comma
+
+ if comma GT 0 then char = ',' else char = ' '  ;Delineator is comma or space
+
+ if N_params() LT 2 then option = fltarr(10) else  $
+ if strupcase(type) EQ 'I' then option = intarr(10) $
+                          else option = fltarr(10) ;Default type is float
+
+ if strlen(inp) EQ 0 then return,0.0   $            ;Null string is 0.0
+ else begin
+   i =0                                            ;Counts number of tokens
+   while inp NE '' do begin
+
+      token = strtrim( gettok(inp,char), 2 )
+      if token NE '' then begin
+
+          test = execute( 'option[i] = ' + token) 
+          if test NE 1 then begin
+                count = -1
+                return, 999.9
+          endif       
+         i = i+1
+      endif
+
+   endwhile
+ endelse
+;
+
+ if N_params() LT 3 then begin
+
+    if i EQ 1 then option = option[0] else $
+            option = option[0:i-1]    ;Trim output vector
+
+ endif else option = option[0:numopt-1] 
+
+ count = N_elements(option)
+ return,option       ;Successful completion
+
+ end
diff --git a/Code/script_idl_mv/astrolib/getpro.pro b/Code/script_idl_mv/astrolib/getpro.pro
new file mode 100644
index 0000000000000000000000000000000000000000..046080778fb62a45cad1afc99e54984d8610bd50
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/getpro.pro
@@ -0,0 +1,126 @@
+pro getpro,proc_name            ;Obtain a copy of a procedure
+;+
+; NAME:
+;     GETPRO
+; PURPOSE:
+;     Search !PATH for a procedure, and copy into user's working directory
+; EXPLANATION:
+;     Extract a procedure from an IDL Library or directory given in the 
+;     !PATH  system variable and place it in the current default directory
+;     (presumably to be edited by the user). 
+;
+; CALLING SEQUENCE:
+;     GETPRO, [ proc_name ]          ;Find PROC_NAME in !PATH and copy
+;
+; OPTIONAL INPUT:
+;     proc_name - Character string giving the name of the IDL procedure or 
+;               function.  Do not give an extension.   If omitted, 
+;               the program will prompt for PROC_NAME.   
+;
+; OUTPUTS:
+;     None.
+;
+; SIDE EFFECTS:
+;      A file with the extension .pro and a name given by PROC_NAME will
+;      be created on the user's directory.
+;
+; PROCEDURE:
+;      The FILE_WHICH() function is used to locate the procedure in the IDL
+;      !PATH.     When found, FILE_COPY is used to 
+;      copy the procedure into the user's current default directory.    If not 
+;      found in !PATH, then the ROUTINE_INFO() function is used to determine 
+;      if it is an intrinsic IDL procedure.  
+;
+; EXAMPLE:
+;       Put a copy of the USER library procedure CURVEFIT on the current
+;       directory
+;
+;       IDL> getpro, 'CURVEFIT'
+;
+; RESTRICTIONS:
+;       User will be unable to obain source code for a native IDL function
+;       or procedure, or for a FORTRAN or C routine added with CALL_EXTERNAL.
+;       User must have write privilege to the current directory
+;
+; PROCEDURE CALLS:
+;      ZPARCHECK
+; REVISION HISTORY:
+;      Written W. Landsman, STX Corp.   June 1990
+;      Now use intrinsic EXPAND_PATH() command  W. Landsman November 1994
+;      Use ROUTINE_NAMES() to check for intrinsic procs  W. Landsman July 95
+;      Update for Windows/IDL     W. Landsman      September 95
+;      Check if procedure is in current directory  W. Landsman  June 1997
+;      Use ROUTINE_INFO instead of undocumented ROUTINE_NAMES W.L. October 1998
+;      Use FILE_WHICH() to locate procedure W. Landsman May 2006 
+;      Assume since V5.5, remove VMS support  W. Landsman Sep 2006
+;      Assume since V6.0, use file_basename() W.Landsman Feb 2009
+;      Test for .sav file, more robust test for write privilege W.L. Jul 2010
+;-
+  On_error,2                                     ;Return to caller on error
+  compile_opt idl2
+
+
+  if N_params() EQ 0 then begin ;Prompt for procedure name?
+        proc_name = ' ' 
+        read,'Enter name of procedure you want a copy of: ',proc_name     
+  
+  endif else zparcheck, 'getpro', proc_name, 1, 7, 0, 'Procedure name'
+
+  name = strtrim( file_basename(proc_name,'.pro'), 2 )  
+
+;First check if procedure is already on current directory (no overwriting)
+
+  if file_test(name + '.pro') then begin
+       message,name + '.pro already exists in the current directory',/INF
+        return
+  endif 
+
+;Locate file in the user's !PATH  
+
+  fname = file_which(name + '.pro')
+  if fname NE '' then begin           ;File found? 
+
+; Now make sure user has write privileges
+        cd, current=curdir
+        if file_test(curdir,/write) NE 1 then $
+        message,curdir +  $
+	   ' has insufficient privilege or file protection violation'
+
+         file_copy,fname, name + '.pro' 
+         message,'Procedure '+ NAME + '.pro copied from '+ fname,/INF
+         return
+   endif else begin
+
+; Is it a .sav file in the !PATH? 
+  fname = file_which(name + '.sav')
+  if fname NE '' then begin           ;.Sav File found? 
+      message,'File ' + fname + ' is an IDL save set',/INF
+      return
+  endif 
+
+; Now check if it is an intrinsic IDL procedure or function.  
+
+  funcnames = routine_info(/system,/func)
+  name = strupcase(name)
+  test = where ( funcnames EQ name, fcount)
+
+  funcnames = routine_info(/system)
+  test = where ( funcnames EQ name, pcount)
+
+  if (fcount EQ 0) and (pcount EQ 0) then begin   
+
+     message,'Procedure '+NAME+' not found in the !PATH search string',/CONT
+     message,'Check your spelling or search the individual directories',/INF
+
+  endif else begin     
+
+  if fcount GT 0 then $
+       message,NAME + ' is an intrinsic IDL function',/CONT  $
+  else message,NAME + ' is an intrinsic IDL procedure',/CONT
+       message,'No source code is available',/INF
+
+  endelse
+  endelse
+  return
+  
+  end 
diff --git a/Code/script_idl_mv/astrolib/getpsf.pro b/Code/script_idl_mv/astrolib/getpsf.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d2c36f36f00cd5482d67d425f32952c16c77d865
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/getpsf.pro
@@ -0,0 +1,405 @@
+pro getpsf,image,xc,yc,apmag,sky,ronois,phpadu, gauss,psf,idpsf,psfrad, $
+            fitrad,psfname, DEBUG = debug
+;+
+; NAME:
+;	GETPSF
+; PURPOSE:
+;	To generate a point-spread function (PSF) from observed stars. 
+; EXPLANATION:
+;	The PSF is represented as a 2-dimensional Gaussian
+;	(integrated over each pixel) and a lookup table of residuals.
+;	The lookup table and Gaussian parameters are output in a FITS
+;	image file.   The PSF FITS file created by GETPSF can be
+;	read with the procedure RDPSF.      Adapted from the 1986 STSDAS 
+;	version of DAOPHOT
+;
+; CALLING SEQUENCE:
+;	GETPSF, image, xc, yc, apmag, sky, [ronois, phpadu, gauss, psf, 
+;			idpsf, psfrad, fitrad, psfname, /DEBUG ]
+;
+; INPUTS:
+;	IMAGE  - input image array
+;	XC     - input vector of x coordinates (from FIND), these should be
+;		IDL (first pixel is (0,0)) convention.
+;	YC     - input vector of y coordinates (from FIND)
+;	APMAG  - vector of magnitudes (from APER), used for initial estimate
+;		of gaussian intensity.  If APMAG is multidimensional, (more
+;		than 1 aperture was used in APER) then the first aperture
+;		is used.
+;	SKY    - vector of sky values (from APER)                
+;
+; OPTIONAL INPUTS:
+;	The user will be prompted for the following parameters if not supplied.
+;
+;	RONOIS - readout noise per pixel, (in electrons, or equivalent photons)
+;	PHPADU - photons per analog digital unit, used to scale the data
+;		numbers in IMAGE into photon units
+;	IDPSF  - subscripts of the list of stars created by 
+;		APER which will be used to define the PSF.   Stars whose
+;		centroid does not fall within PSFRAD of the edge of the frame,
+;		or for which a Gaussian fit requires more than 25 iterations,
+;		will be ignored when creating the final PSF.
+;	PSFRAD - the scalar radius, in pixels, of the circular area within
+;		which the PSF will be defined.   This should be slightly larger
+;		than the radius of the brightest star that one will be
+;		interested in.
+;	FITRAD - the scalar radius, in pixels of the circular area used in the
+;		least-square star fits.  Stetson suggest that FITRAD should
+;		approximately equal to the FWHM, slightly less for crowded
+;		fields.  (FITRAD must be smaller than PSFRAD.)
+;	PSFNAME- Name of the FITS file that will contain the table of residuals,
+;		and the best-fit Gaussian parameters.    This file is 
+;		subsequently required for use by NSTAR.  
+;
+; OPTIONAL OUTPUTS:
+;	GAUSS  - 5 element vector giving parameters of gaussian fit to the 
+;		first PSF star
+;		GAUSS(0) - height of the gaussian (above sky)
+;		GAUSS(1) - the offset (in pixels) of the best fitting gaussian
+;			and the original X centroid
+;		GAUSS(2) - similiar offset from the Y centroid 
+;		GAUSS(3) - Gaussian sigma in X
+;		GAUSS(4) - Gaussian sigma in Y
+;	PSF    - 2-d array of PSF residuals after a Gaussian fit.
+;
+; PROCEDURE:
+;	GETPSF fits a Gaussian profile to the core of the first PSF star 
+;	and generates a look-up table of the residuals of the
+;	actual image data from the Gaussian fit.  If desired, it will then
+;	fit this PSF to another star (using PKFIT) to determine its precise 
+;	centroid, scale the same Gaussian to the new star's core, and add the
+;	differences between the actual data and the scaled Gaussian to the
+;	table of residuals.   (In other words, the Gaussian fit is performed
+;       only on the first star.)
+;
+; OPTIONAL KEYWORD INPUT:
+;	DEBUG - if this keyword is set and non-zero, then the result of each
+;		fitting iteration will be displayed.
+;
+; PROCEDURES CALLED
+;	DAOERF, MAKE_2D, MKHDR, RINTER(), PKFIT, STRNUMBER(), STRN(), WRITEFITS
+;
+; REVISON HISTORY:
+;	Adapted from the 1986 version of DAOPHOT in STSDAS
+;	IDL Version 2  W Landsman           November 1988
+;	Use DEBUG keyword instead of !DEBUG  W. Landsman       May 1996
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-                                              
+ On_error,2                      ;Return to caller
+
+ common rinter,c1,c2,c3,init	;Save time in RINTER
+ init = 0                        ;Initialize the common blocks
+
+ npar = N_params()
+
+ if npar LT 5 then begin	 	;Enough parameters passed?
+   print,'Syntax -  GETPSF, image, x, y, mags, sky, '
+   print,'       [ronois, phpadu, gauss, psf, idpsf, psfrad, fitrad, ' + $
+         'psfname, /DEBUG]'
+   return
+ endif
+
+ s = size(image)    		;Get number of rows and columns in image
+ ncol = s[1] & nrow = s[2]
+ nstar = N_elements(xc)	        ;Total # of stars identified in image
+
+ if N_elements(idpsf) LT 1 then begin	;Array of PSF id's defined?
+   idpsf = intarr(25)
+   i = 0 &  id = ''
+   print,"GETPSF: Enter index of stars to be used for PSF, one index per line"
+   RD_ID:   
+   print,'Enter a stellar ID ( [RETURN] when finished) '
+   read,id
+   if id EQ '' then begin             ;Did User hit the [RETURN] key
+             if i EQ 0 then return    ;No stellar ID's supplied
+             idpsf = idpsf[0:i-1]
+             goto, GOT_ID     
+   endif else result = strnumber(id,val)
+
+   if not result then print,string(7b),'INVALID INPUT:' else $
+         if (val GE nstar) or (val LT 0) then $ 
+                print,string(7b),'INVALID ID NUMBER' else begin
+             idpsf[i] = fix(val) 
+             i = i+1
+         endelse
+   goto,RD_ID
+ endif 
+
+GOT_ID:  
+
+ if N_elements(psfrad) NE 1 then read, $
+   'Enter radius (in pixels) of circular area defining the PSF: ',psfrad
+ if N_elements(fitrad) NE 1 then read, $
+   'Enter radius (in pixels) to be used for Gaussian fitting: ',fitrad
+ if fitrad GE psfrad then $
+    message,'ERROR - Fitting radius must be smaller than radius defining PSF'
+
+ if N_elements(ronois) NE 1 then read, $
+   'Enter readout noise per pixel: ',ronois
+ if N_elements(phpadu) NE 1 then read, $
+   'Enter photons per analog digital unit: ',phpadu
+
+ numpsf = N_elements(idpsf)      ;# of stars used to create the PSF
+
+ smag = size(apmag)     ;Is APMAG multidimensional?
+ if N_elements(apmag) NE smag[1] then mag = apmag[0,*] else mag = apmag[*]
+
+ n = 2*fix(psfrad+0.5)+1  ;(Odd) width of box that contains PSF circle
+ npsf = 2*n+7             ;Lookup table has half pixel interpolation
+ nbox = n+7		 ;(Even) Width of subarray to be extracted from image
+ nhalf = nbox/2           
+
+ if keyword_set(DEBUG) then begin
+    print,'GETPSF: Fitting radius - ',string(float(fitrad),'(F5.1)')
+    print,'        PSF Radius     - ',string(float(psfrad),'(F5.1)')
+    print,'        Stellar IDs: ',idpsf   & print,' '
+ endif
+
+ boxgen = findgen(nbox)
+ make_2d, boxgen, boxgen, xgen, ygen
+
+;               Find the first PSF star in the star list.
+ nstrps = -1	;Counter for number of stars used to create PSF
+GETSTAR: 
+
+ nstrps = nstrps + 1       
+ if nstrps GE numpsf then $
+     message,'ERROR - No valid PSF stars were supplied'
+
+ istar = idpsf[nstrps]       ;ID number of first PSF star
+ ixcen = fix(xc[istar])      
+ iycen = fix(yc[istar])
+
+;  Now a subarray F will be read in from the big image, given by 
+;  IXCEN-NBOX/2+1 <= x <= IXCEN+NBOX/2, IYCEN-NBOX/2+1 <= y <= IYCEN+NBOX/2.  
+;  (NBOX is an even number.)  In the subarray, the coordinates of the centroid
+;  of the star will lie between NBOX/2 and NBOX/2+1 in each coordinate.
+
+ lx = ixcen-nhalf+1  &  ux = ixcen + nhalf  ;Upper & lower bounds in X
+ ly = iycen-nhalf+1  &  uy = iycen + nhalf
+ if ((lx LT 0)   or (ly LT 0) or $     ;Star too close to edge?
+   (ux GE ncol) or (uy GE nrow)) then begin    
+   print,'GETPSF: Star ',strn(istar),' too near edge of frame.'
+   goto, GETSTAR
+ endif                      
+
+ f = image[lx:ux,ly:uy] - sky[istar]  ;Read in subarray, subtract off sky
+
+; An integrated Gaussian function will be fit to the central part of the
+; stellar profile.  Initially, a 5x5 box centered on the centroid of the 
+; star is used, but if the sigma in one coordinate drops to less than
+; 1 pixel, then the box width of 3 will be used in that coordinate.
+; If the sigma increases to over 3 pixels, then a box width of 7 will be 
+; used in that coordinate
+
+ x = xc[istar] - lx    ;X coordinate of stellar centroid in subarray F
+ y = yc[istar] - ly    ;Y coordinate of stellar centroid in subarray F
+ ix = fix(x+0.5)       ;Index of pixel containing centroid
+ iy = fix(y+0.5) 
+;                     ;Begin least squares
+ h = max(f)  	      ;Initial guess for peak intensity
+ sigx = 2.0 & sigy = 2.0                                       
+ dxcen=0.  &  dycen=0.
+;
+ niter = 0                    ;Beginning of big iteration loop
+ v = fltarr(5)
+ c = fltarr(5,5)
+;                            Print the current star
+ fmt1 = "(/17X, 'STAR', 5X, 'X', 8X, 'Y', 5X, 'MAG  1', 5X, 'SKY')"
+ fmt2 = "(15X, I5, 2F9.2, 12F9.3)"
+ if keyword_set(DEBUG) then begin
+    print,format=fmt1          
+    print,format=fmt2,istar, xc[istar], yc[istar], mag[istar], sky[istar]
+ endif
+
+ if keyword_set(DEBUG) then print,'GETPSF: Gaussian Fit Iteration'
+
+ REPEAT BEGIN		     ;Begin the iterative loop
+
+ niter = niter + 1
+ if niter GT 100 then begin   ;No convergence after 100 iterations?
+    message,'No convergence after 100 iterations for star ' + strn(istar),/INF
+    goto, GETSTAR 
+ endif
+
+      if sigx LE 1 then nx = 1  $  ;A default box width 
+ else if sigx GT 3 then nx = 3  $
+ else                   nx = 2
+
+      if sigy LE 1 then ny = 1  $
+ else if sigy GT 3 then ny = 3  $
+ else                   ny = 2
+
+ a = [H, x+dxcen,y+dycen,sigx,sigy]
+ xin = (findgen(2*nx+1)-nx) + ix
+ yin = (findgen(2*ny+1)-ny) + iy
+ make_2d, xin, yin
+ DAOERF, xin, yin, a, g, t
+
+;  The T's are the first derivatives of the model profile with respect
+;  to the five fitting parameters H, DXCEN, DYCEN, SIGX, and SIGY.
+;  Note that the center of the best-fitting Gaussian profile is
+;  expressed as an offset from the centroid of the star.  In the case of
+;  a general, asymmetric stellar profile, the center of symmetry of the
+;  best-fitting Gaussian profile will not necessarily coincide with the
+;  centroid determined by any arbitrary centroiding algorithm.  
+
+ dh = f[ ix-nx:ix+nx, iy-ny:iy+ny] - g ;Subtract best fit Gaussian from subarray
+ for kk = 0,4 do begin
+      tk = t[*,kk]
+      v[kk] = total( dh * tk )
+      for ll = 0,4 do c[kk,ll] = total( tk * t[*,ll] )
+ endfor
+
+ c = invert(c,status)	;IDL version assumes INVERT is successful
+
+ if status EQ 1 then begin
+     message,'Singular matrix encountered fitting star ' + strn(istar),/INF
+     goto, GETSTAR
+ endif 
+
+ z = c#v         ;Multiply by vector of residuals
+
+ h = h + z[0]/(1.0+4.0*abs(z[0]/h))	;Correct the fitting parameters
+ dxcen = dxcen+z[1]/(1.0+3.0*abs(z[1]))
+ dycen = dycen+z[2]/(1.0+3.0*abs(z[2]))
+ sigx = sigx+z[3]/(1.0+4.0*abs(z[3]/sigx))
+ sigy = sigy+z[4]/(1.0+4.0*abs(z[4]/sigy))
+
+ if keyword_set(DEBUG) then print,niter,h,dxcen,dycen,sigx,sigy
+
+ endrep until $ 				;Test for convergence  
+       (abs(z[0]/h)+abs(z[3]/sigx)+abs(z[4]/sigy) LT 0.0001)
+
+;  Now that the solution has converged, we can generate an
+;  array containing the differences between the actual stellar profile
+;  and the best-fitting Gaussian analytic profile.
+
+ a = [H, x+dxcen, y+dycen, sigx,sigy]  ;Parameters for Gaussian fit
+ DAOERF,xgen,ygen,a,g                  ;Compute Gaussian
+ f = f - g                             ;Residuals (Real profile - Gaussian)
+
+ psfmag = mag[istar]
+ xpsf1 = xc[istar] & ypsf1 = yc[istar]
+
+; The look-up table is obtained by interpolation within the array of
+; fitting residuals.  We need to interpolate because we want the look-up
+; table to be centered accurately on the centroid of the star, which of 
+; course is at some fractional-pixel position in the original data.
+
+ ncen = (npsf-1)/2.
+ psfgen = (findgen(npsf) - ncen)/2.         ;Index function for PSF array
+ YY = psfgen + Y   &  XX = psfgen + X
+ make_2d,xx,yy
+ psf = RINTER(F, XX, YY)            ;Interpolate residuals onto current star
+ gauss = [h,dxcen,dycen,sigx,sigy]
+ goodstar = nstrps                   ;Index of first good star
+
+; For each additional star, determine the precise  coordinates of the 
+; centroid and the relative brightness of the star
+; by least-squares fitting to the current version of the point-spread
+; function.  Then subtract off the appropriately scaled integral under
+; the analytic Gaussian function  and add the departures of the actual 
+; data from the analytic Gaussian function to the look-up table.
+
+GETMORE:            ;Loop for additional PSF stars begins here                 
+ nstrps = nstrps+1
+ if nstrps GE numpsf then goto,WRITEOUT	;Have all the stars been done?
+
+ istar = idpsf[nstrps]
+ ixcen = fix(xc[istar])
+ iycen = fix(yc[istar])                  
+ scale = 10.^(-0.4*(mag[istar]-psfmag))
+
+; Fit the current version of the point-spread function to the data for
+; this star.
+
+ lx = ixcen-nhalf+1 & ux =ixcen + nhalf
+ ly = iycen-nhalf+1 & uy =iycen + nhalf
+ if ( (lx LT 0) or (ly LT 0) or $             ;Star too close to edge?
+    (ux GE ncol) or (uy GE nrow)) then begin  
+   print,'GETPSF: Star ',strn(istar),' too near edge of frame.'
+   goto,GETMORE
+ endif                      
+
+ if keyword_set(DEBUG) then begin
+   print,format=fmt1
+   print,format=fmt2, istar, xc[istar], yc[istar], mag[istar], sky[istar]
+ endif
+
+ f = image[lx:ux,ly:uy]
+ x = xc[istar]-lx   &   y = yc[istar]-ly   
+
+ pkfit, f, scale, x, y, sky[istar], fitrad, ronois, phpadu, $
+		gauss, psf, errmag, chi, sharp, niter, DEBUG = debug
+
+ if niter EQ 25 then begin	;Convergence in less than 25 iterations?
+      print,'GETPSF: No convergence after 25 iterations for star',istar
+      goto, GETMORE 
+ endif
+
+ a = [gauss[0], x+dxcen,y+dycen,sigx,sigy]  ;Parameters of successful fit
+ daoerf,xgen,ygen,a,e
+ f = f - scale*e -sky[istar]	           ;Compute array of residuals
+
+; Values of the array of residuals are now interpolated to an NPSF by
+; NPSF (NPSF is an odd number) array centered on the centroid of the
+; star, and added to the existing look-up table of corrections to the 
+; analytic profile 
+
+ xx = psfgen + x
+ yy = psfgen + y 
+ make_2d,xx,yy
+ psf = psf + RINTER(f,xx,yy)    
+
+; Now correct both the height of the analytic Gaussian, and the value
+; of the aperture-magnitude of the point-spread function for the
+; inclusion of the additional star.
+
+ psfmag = -2.5*alog10((1.+scale)*10^(-0.4*psfmag))
+ gauss[0] = gauss[0]*(1.+scale)
+ goodstar = [ goodstar, nstrps]
+ goto, GETMORE  
+
+WRITEOUT:   
+
+; Create FITS file containing the PSF created.
+
+ if ( N_elements(psfname) EQ  0 ) then begin
+   psfname=''
+   read,'Enter name of FITS file to contain final PSF ([RETURN] to exit): ',psfname
+ endif
+
+if ( psfname EQ  '' ) then return
+
+ mkhdr, hdr, psf         ;Create a minimal FITS header
+ sxaddpar, hdr, 'PHPADU', phpadu, 'Photons per Analog Digital Unit'
+ sxaddpar, hdr, 'RONOIS', ronois, 'Readout Noise'
+ sxaddpar, hdr, 'PSFRAD', psfrad, 'Radius where PSF is defined (pixels)'
+ sxaddpar, hdr, 'FITRAD', fitrad, 'Fitting Radius'
+ sxaddpar, hdr, 'PSFMAG', psfmag, 'PSF Magnitude'
+ sxaddpar, hdr, 'GAUSS1', gauss[0], 'Gaussian Scale Factor'
+ sxaddpar, hdr, 'GAUSS2', gauss[1], 'Gaussian X Position'
+ sxaddpar, hdr, 'GAUSS3', gauss[2], 'Gaussian Y Position'
+ sxaddpar, hdr, 'GAUSS4', gauss[3], 'Gaussian Sigma: X Direction'
+ sxaddpar, hdr, 'GAUSS5', gauss[4], 'Gaussian Sigma: Y Direction'
+
+ ngood = N_elements(goodstar)
+ sxaddhist,'GETPSF: '+ systime() + ' ' + strn(ngood) +  $
+           ' Stars Used to Create PSF',hdr
+
+  sxaddhist,'GETPSF: ID - '+ string(idpsf[goodstar[0:12<ngood-1]], $
+    format='(13i5)'),hdr
+
+ if ngood gt 13 then $
+     sxaddhist,'GETPSF: ID - '+ string(idpsf[goodstar[13:*]], $
+     format='(13i5)'),hdr
+
+ sxaddhist,'PSF Coordinates:'+ $
+    string(xpsf1, format='(F7.2)') + $
+    string(ypsf1, format='(F7.2)'), hdr
+
+ writefits,psfname,psf,hdr
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/getrot.pro b/Code/script_idl_mv/astrolib/getrot.pro
new file mode 100644
index 0000000000000000000000000000000000000000..bdae5784a3daa2a09f0e5cda8a0fbf06b7d79135
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/getrot.pro
@@ -0,0 +1,168 @@
+pro getrot, hdr, rot, cdelt, DEBUG = debug, SILENT = silent, ALT=alt      ;GET ROTation 
+;+
+; NAME:
+;    GETROT
+; PURPOSE:
+;     Return the rotation and plate scale of an image from its FITS header
+; EXPLANATION:
+;     Derive the counterclockwise rotation angle, and the X and Y scale
+;     factors of an image, from a FITS image header.   The input parameter 
+;     may be either a FITS image header or an astrometry structure (as 
+;     obtained by extast.pro)
+;
+; CALLING SEQUENCE:
+;     GETROT, Hdr, [ Rot, CDelt, /SILENT, DEBUG =  ]   
+;             or 
+;     GETROT, Astr, Rot, CDelt, /SILENT, DEBUG = ]       
+;
+; INPUT PARAMETERS:
+;     HDR - FITS Image header (string array).  Program will extract the 
+;             astrometry structure
+;              or
+;     ASTR -  ASTROMETRY structure, of the type returned by EXTAST.
+;             See the documentation for EXTAST.PRO for details.
+;
+; OPTIONAL OUTPUT PARAMETERS:
+;       ROT - Scalar giving the counterclockwise rotation of NORTH in DEGREES 
+;               from the +Y axis of the image.
+;       CDELT- 2 element vector giving the scale factors in DEGREES/PIXEL in 
+;               the X and Y directions.   CDELT[1] is always positive, whereas
+;               CDELT[0] is negative for a normal left-handed coordinate system,
+;               and positive for a right-handed system. 
+;
+;       If no output variables are supplied (or /DEBUG is set), then GETROT 
+;       will display the rotation and plate scale at the terminal.
+;
+; OPTIONAL INPUT KEYWORD
+; 
+;       ALT - single character 'A' through 'Z' or ' ' specifying an alternate
+;             astrometry system present in the FITS header.   See extast.pro
+;             for more information on the ALT keyword.    Ignored if an
+;             astrometry structure rather than FITS header is supplied.
+;       DEBUG - if DEBUG is set, GETROT will print the rotation for both the 
+;           X and Y axis when these values are unequal.  If DEBUG is set to 2, 
+;           then the output parameter ROT will contain both X and Y rotations.
+;
+;       /SILENT - if set, then do not provide a warning about a right-handed
+;           coordinate system
+; PROCEDURE:
+;       If the FITS header already contains CDELT (and CD or CROTA) keyword,
+;       (as suggested by the Calabretta & Greisen (2002, A&A, 395, 1077) FITS 
+;       standard) then this is used for the scale factor.
+;       
+;       If the header contains CD keywords but no CDELT keywords (as in IRAF
+;       headers) then the scale factor is derived from the CD matrix.
+;
+;       In case of skew (different rotations of the X and Y axes), the rotations
+;       are averaged together if they are less than 2 degrees.   Otherwise,
+;       a warning is given and the X rotation is used.  
+;
+; PROCEDURES USED:
+;       EXTAST, GSSS_EXTAST
+; REVISION HISTORY:
+;       Written W. Landsman STX January 1987 
+;       Option to return both rotations added.  J. D. Offenberg, STX, Aug 1991
+;       Use new astrometry structure   W. Landsman  Mar 1994
+;       Recognize a GSSS header        W. Landsman  June 1994
+;       Correct rotation determination with unequal CDELT values WL October 1998
+;       Consistent conversion between CROTA and CD matrix  WL  October 2000
+;       Correct CDELT computations for rotations near 90 deg WL November 2002
+;       Preserve sign in the CDELT output  WL June 2003
+;       Check if latitude/longitude reversed in CTYPE  WL  February 2004
+;       Fix problem in latitude check  M.Lombardi/W.Landsman Sep 2004
+;       Added ALT keyword W. Landsman May 2005
+;       Account for any rotation of the native system by examining the value
+;        of LONGPOLE       H. Taylor/W. Landsman
+;       Account for case where X,Y rotations differ by 2*!pi WL. Aug 2011
+;-
+ Compile_opt IDL2
+ On_error,2
+
+ if N_params() EQ 0 then begin
+        print,'Syntax: GETROT, Hdr, [ Rot, CDelt, DEBUG= , /SILENT, ALT=]'
+        print,'    OR: GETROT, Astr, [ Rot, CDelt, DEBUG= , /SILENT]'
+        return
+ endif
+
+ tpi = 2*!dpi
+ if ~keyword_set(DEBUG) then debug = 0
+ radeg = 180.0/!DPI
+ sz = size(hdr,/STR)                ;Did user supply a FITS header or a CD matrix?
+
+ if ((sz.N_dimensions eq 1) && $
+     (sz.type_name EQ 'STRING')) then begin     ;FITS header?
+
+        extast,hdr,astr,alt=alt             ;Extract astrometry from header,
+        if strmid(astr.ctype[0],5,3) EQ 'GSS' then begin
+                hdr1 = hdr
+                gsss_stdast, hdr1
+                extast, hdr1, astr
+        endif
+        cd = astr.cd/RADEG      ;then extract CD matrix from astrometry.
+        if N_elements(cd) NE 4 then $
+            message,'ERROR - Header is missing astrometry keywords CD or CDELT'
+
+endif else $
+ if ((sz.N_dimensions eq 1) and $
+     (sz.type_name EQ 'STRUCT')) then begin     ;Astrometry Structure?
+      astr = hdr
+      cd = astr.cd/RADEG
+ endif else message, $
+        'ERROR - First parameter must be an image header or astrometry structure'
+
+ if astr.cdelt[0] NE 1.0 then begin
+        cdelt = astr.cdelt
+        cd[0,0] *= cdelt[0] & cd[0,1] *= cdelt[0]
+        cd[1,1] *= cdelt[1] & cd[1,0] *= cdelt[1]
+ endif else  cd = astr.cd/RADEG                                        
+ 
+ ctype = strmid(astr.ctype[0],0,4)
+; Check if first coordinate in CTYPE is latitude
+ if (ctype EQ 'DEC-') or (strmid(ctype, 1) EQ 'LAT')  then $
+      cd = reverse(cd,1)
+ det = cd[0,0]*cd[1,1] - cd[0,1]*cd[1,0]
+ if det LT 0 then sgn = -1 else sgn = 1
+ if ~keyword_set(SILENT) then if det GT 0 then $
+   message,'WARNING - Astrometry is for a right-handed coordinate system',/INF
+ cdelt = fltarr(2)
+ if (cd[1,0] eq 0) && (cd[0,1] eq 0) then begin ;Unrotated coordinates?
+   rot = 0.
+   rot2 = 0.
+   cdelt[0] = cd[0,0]
+   cdelt[1] = cd[1,1]
+ endif else begin
+   rot  = atan(  sgn*cd[0,1],  sgn*cd[0,0] ) 
+   rot2 = atan( -cd[1,0],  cd[1,1] )
+  
+
+ if rot NE rot2 then begin          ;Handle unequal rotations
+      if keyword_set(debug) then $
+        print,'X axis rotation:',rot*!RADEG, ' Y axis rotation:',rot2*!RADEG
+      if debug eq 2 then rot = [rot,rot2] else begin
+      
+      if abs(rot - rot2)*!RADEG LT 2 then rot = (rot + rot2)/2. else $
+      if abs(rot - rot2- tpi)*!radeg lt 2  then rot = (rot +rot2 -tpi)/2. else $
+      if abs(rot - rot2 +tpi)*!radeg lt 2  then rot = (rot +rot2 +tpi)/2. else $
+         message,/INF, $
+	 'WARNING: X and Y axis rotations differ by more than 2 degrees'
+      endelse
+      
+ endif
+
+  cdelt[0] =   sgn*sqrt(cd[0,0]^2 + cd[0,1]^2)
+  cdelt[1] =   sqrt(cd[1,1]^2 + cd[1,0]^2)
+ endelse
+
+ rot = rot*RADEG   
+ if astr.longpole NE 180.0d then rot = rot + ( 180.0d - astr.longpole )
+ rot = float(rot)
+ cdelt = float(cdelt*RADEG)
+
+ if N_params() EQ 1 || keyword_set(DEBUG) then begin
+        if debug LT 2 then print,'Rotation (counterclockwise)',rot,' degrees'
+        print,'Sampling interval X axis',cdelt[0]*3600.,' arc seconds/pixel'
+        print,'                  Y axis',cdelt[1]*3600.,' arc seconds/pixel'
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/gettok.pro b/Code/script_idl_mv/astrolib/gettok.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6bee3776b719fb7f8d5f1fb09485c0df670a574f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gettok.pro
@@ -0,0 +1,84 @@
+function gettok,st,char, exact=exact, notrim=notrim
+;+
+; NAME:
+;	GETTOK                                    
+; PURPOSE:
+;	Retrieve the first part of a (vector) string up to a specified character
+; EXPLANATION:
+;	GET TOKen - Retrieve first part of string until the character char 
+;	is encountered.   
+;
+; CALLING SEQUENCE:
+;	token = gettok( st, char, [ /EXACT, /NOTRIM ] )
+;
+; INPUT:
+;	char - character separating tokens, scalar string
+;
+; INPUT-OUTPUT:
+;	st - string to get token from (on output token is removed unless
+;            /NOTRIM is set), scalar or vector
+;
+; OUTPUT:
+;	token - extracted string value is returned, same dimensions as st
+; OPTIONAL INPUT KEYWORD:
+;       /EXACT -  The default behaviour of GETTOK is to remove any leading 
+;              blanks and (if the token is a blank) convert tabs to blanks.    
+;              Set the /EXACT keyword to skip these steps and leave the 
+;              input string unchanged before searching for the  character 
+;              tokens. 
+;
+;      /NOTRIM - if set, then the input string is left unaltered 
+; EXAMPLE:
+;	If ST is ['abc=999','x=3.4234'] then gettok(ST,'=') would return
+;	['abc','x'] and ST would be left as ['999','3.4234'] 
+;
+; PROCEDURE CALLS:
+;       REPCHR()
+; HISTORY
+;	version 1  by D. Lindler APR,86
+;	Remove leading blanks    W. Landsman (from JKF)    Aug. 1991
+;       V5.3 version, accept vector input   W. Landsman February 2000
+;       Slightly faster implementation  W. Landsman   February 2001
+;       Added EXACT keyword  W. Landsman March 2004
+;       Assume since V5.4, Use COMPLEMENT keyword to WHERE W. Landsman Apr 2006
+;       Added NOTRIM keyword W. L. March 2011
+;-
+;----------------------------------------------------------------------
+  On_error,2                           ;Return to caller
+  compile_opt idl2
+
+   if N_params() LT 2 then begin
+       print,'Syntax - token = gettok( st, char, [ /EXACT, /NOTRIM] )'
+       return,-1
+   endif
+
+; if char is a blank treat tabs as blanks
+
+ if ~keyword_set(exact) then begin
+    st = strtrim(st,1)              ;Remove leading blanks and tabs
+    if char EQ ' ' then begin 
+       tab = string(9b)                 
+       if max(strpos(st,tab)) GE 0 then st = repchr(st,tab,' ')
+    endif
+  endif
+  token = st
+
+; find character in string
+
+  pos = strpos(st,char)
+  test = pos EQ -1
+  bad = where(test, Nbad, Complement = good, Ncomplement=Ngood)
+  if Nbad GT 0 && ~keyword_set(notrim) then st[bad] = ''
+ 
+; extract token
+ if Ngood GT 0 then begin
+    stg = st[good]
+    pos = reform( pos[good], 1, Ngood )
+    token[good] = strmid(stg,0,pos)
+    if ~keyword_set(notrim) then st[good] = strmid(stg,pos+1)
+ endif
+
+;  Return the result.
+
+ return,token
+ end
diff --git a/Code/script_idl_mv/astrolib/getwrd.pro b/Code/script_idl_mv/astrolib/getwrd.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e26f64223d3475e5966d9676483df21b1d9b2138
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/getwrd.pro
@@ -0,0 +1,154 @@
+;-------------------------------------------------------------
+;+
+; NAME:
+;       GETWRD
+; PURPOSE:
+;       Return the n'th word from a text string.
+; CATEGORY:
+; CALLING SEQUENCE:
+;       wrd = getwrd(txt, n, [m])
+; INPUTS:
+;       txt = text string to extract from.         in
+;         The first element is used if txt is an array.
+;       n = word number to get (first = 0 = def).  in
+;       m = optional last word number to get.      in
+; KEYWORD PARAMETERS:
+;       Keywords:
+;         LOCATION = l.  Return word n string location.
+;         DELIMITER = d. Set word delimiter (def = space & tab).
+;         /LAST means n is offset from last word.  So n=0 gives
+;           last word, n=-1 gives next to last, ...
+;           If n=-2 and m=0 then last 3 words are returned.
+;         /NOTRIM suppresses whitespace trimming on ends.
+;         NWORDS=n.  Returns number of words in string.
+; OUTPUTS:
+;       wrd = returned word or words.              out
+; COMMON BLOCKS:
+;       getwrd_com
+; NOTES:
+;       Note: If a NULL string is given (txt="") then the last string
+;             given is used.  This saves finding the words again.
+;             If m > n wrd will be a string of words from word n to
+;             word m.  If no m is given wrd will be a single word.
+;             n<0 returns text starting at word abs(n) to string end
+;             If n is out of range then a null string is returned.
+;             See also nwrds.
+; MODIFICATION HISTORY:
+;       Ray Sterner,  6 Jan, 1985.
+;       R. Sterner, Fall 1989 --- converted to SUN.
+;       R. Sterner, Jan 1990 --- added delimiter.
+;       R. Sterner, 18 Mar, 1990 --- added /LAST.
+;       R. Sterner, 31 Jan, 1991 --- added /NOTRIM.
+;       R. Sterner, 20 May, 1991 --- Added common and NULL string.
+;       R. Sterner, 13 Dec, 1992 --- Made tabs equivalent to spaces.
+;       R. Sterner,  4 Jan, 1993 --- Added NWORDS keyword.
+;       R. Sterner, 2001 Jan 15 --- Fixed to use first element if not a scalar.
+;       Johns Hopkins University Applied Physics Laboratory.
+;
+; Copyright (C) 1985, Johns Hopkins University/Applied Physics Laboratory
+; This software may be used, copied, or redistributed as long as it is not
+; sold and this copyright notice is reproduced on each copy made.  This
+; routine is provided as is without any express or implied warranties
+; whatsoever.  Other limitations apply as described in the file disclaimer.txt.
+;-
+;-------------------------------------------------------------
+ 
+ 
+	FUNCTION GETWRD, TXTSTR, NTH, MTH, help=hlp, location=ll,$
+	   delimiter=delim, notrim=notrim, last=last, nwords=nwords
+ 
+	common getwrd_com, txtstr0, nwds, loc, len
+ 
+	if (n_params(0) lt 1) or keyword_set(hlp) then begin
+	  print," Return the n'th word from a text string."
+	  print,' wrd = getwrd(txt, n, [m])'
+	  print,'   txt = text string to extract from.         in'
+	  print,'     The first element is used if txt is an array.'
+	  print,'   n = word number to get (first = 0 = def).  in'
+	  print,'   m = optional last word number to get.      in'
+	  print,'   wrd = returned word or words.              out'
+	  print,' Keywords:'
+	  print,'   LOCATION = l.  Return word n string location.'
+	  print,'   DELIMITER = d. Set word delimiter (def = space & tab).'
+	  print,'   /LAST means n is offset from last word.  So n=0 gives'
+	  print,'     last word, n=-1 gives next to last, ...'
+	  print,'     If n=-2 and m=0 then last 3 words are returned.'
+	  print,'   /NOTRIM suppresses whitespace trimming on ends.'
+	  print,'   NWORDS=n.  Returns number of words in string.'
+	  print,'Note: If a NULL string is given (txt="") then the last string'
+	  print,'      given is used.  This saves finding the words again.'
+	  print,'      If m > n wrd will be a string of words from word n to'
+	  print,'      word m.  If no m is given wrd will be a single word.'
+	  print,'      n<0 returns text starting at word abs(n) to string end'
+	  print,'      If n is out of range then a null string is returned.'
+	  print,'      See also nwrds.'
+	  return, -1
+	endif
+ 
+	if n_params(0) lt 2 then nth = 0		; Def is first word.
+	IF N_PARAMS(0) LT 3 THEN MTH = NTH		; Def is one word.
+ 
+	if strlen(txtstr[0]) gt 0 then begin
+	  ddel = ' '					; Def del is a space.
+	  if n_elements(delim) ne 0 then ddel = delim	; Use given delimiter.
+	  TST = (byte(ddel))(0)				; Del to byte value.
+	  tb = byte(txtstr[0])				; String to bytes.
+	  if ddel eq ' ' then begin		        ; Check for tabs?
+	    w = where(tb eq 9B, cnt)			; Yes.
+	    if cnt gt 0 then tb[w] = 32B		; Convert any to space.
+	  endif
+	  X = tb NE TST					; Non-delchar (=words).
+	  X = [0,X,0]					; 0s at ends.
+ 
+	  Y = (X-SHIFT(X,1)) EQ 1			; Diff=1: word start.
+	  Z = WHERE(SHIFT(Y,-1) EQ 1)			; Word start locations.
+	  Y2 = (X-SHIFT(X,-1)) EQ 1			; Diff=1: word end.
+	  Z2 = WHERE(SHIFT(Y2,1) EQ 1)			; Word end locations.
+ 
+	  txtstr0 = txtstr[0]				; Move string to common.
+	  NWDS = long(TOTAL(Y))				; Number of words.
+	  LOC = Z					; Word start locations.
+	  LEN = Z2 - Z - 1				; Word lengths.
+	endif else begin
+	  if n_elements(nwds) eq 0 then begin		; Check if first call.
+	    print,' Error in getwrd: must give a '+$
+	      'non-NULL string on the first call.'
+	    return, -1					; -1 = error flag.
+	  endif
+	endelse
+ 
+	nwords = nwds					; Set nwords
+ 
+	if keyword_set(last) then begin			; Offset from last.
+	  lst = nwds - 1
+	  in = lst + nth				; Nth word.
+	  im = lst + mth				; Mth word.
+	  if (in lt 0) and (im lt 0) then return, ''	; Out of range.
+	  in = in > 0					; Smaller of in and im
+	  im = im > 0					;  to zero.
+	  if (in gt lst) and (im gt lst) then return,'' ; Out of range.
+	  in = in < lst					; Larger of in and im
+	  im = im < lst					;  to be last.
+	  ll = loc[in]					; Nth word start.
+	  return, strtrim(strmid(txtstr0,ll,loc[im]-loc[in]+len[im]), 2) 
+	endif
+ 
+	N = ABS(NTH)					; Allow nth<0.
+	IF N GT NWDS-1 THEN RETURN,''			; out of range, null.
+	ll = loc[n]					; N'th word position.
+	IF NTH LT 0 THEN GOTO, NEG			; Handle nth<0.
+	IF MTH GT NWDS-1 THEN MTH = NWDS-1		; Words to end.
+ 
+	if keyword_set(notrim) then begin
+	  RETURN, STRMID(TXTSTR0,ll,LOC[MTH]-LOC[NTH]+LEN[MTH])
+	endif else begin
+	  RETURN, strtrim(STRMID(TXTSTR0,ll,LOC[MTH]-LOC[NTH]+LEN[MTH]), 2)
+ 	endelse
+ 
+NEG:	if keyword_set(notrim) then begin
+	  RETURN, STRMID(TXTSTR0,ll,9999)
+	endif else begin
+	  RETURN, strtrim(STRMID(TXTSTR0,ll,9999), 2)
+	endelse
+ 
+	END
diff --git a/Code/script_idl_mv/astrolib/glactc.pro b/Code/script_idl_mv/astrolib/glactc.pro
new file mode 100644
index 0000000000000000000000000000000000000000..edac6da6079a2cfc5372deb74bd353b2d821f5c9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/glactc.pro
@@ -0,0 +1,140 @@
+pro glactc,ra,dec,year,gl,gb,j, degree=degree, fk4 = fk4, $
+   SuperGalactic = superGalactic
+;+
+; NAME:  
+;       GLACTC
+; PURPOSE:
+;        Convert between celestial and Galactic (or Supergalactic) coordinates.
+; EXPLANATION:
+;       Program to convert right ascension (ra) and declination (dec) to
+;       Galactic longitude (gl) and latitude (gb) (j=1) or vice versa (j=2).
+;
+; CALLING SEQUENCE: 
+;       GLACTC, ra, dec, year, gl, gb, j, [ /DEGREE, /FK4, /SuperGalactic ]
+;
+; INPUT PARAMETERS: 
+;       year     equinox of ra and dec, scalar       (input)
+;       j        direction of conversion     (input)
+;               1:  ra,dec --> gl,gb
+;               2:  gl,gb  --> ra,dec
+;
+; INPUTS OR OUTPUT PARAMETERS: ( depending on argument J )
+;       ra       Right ascension, hours (or degrees if /DEGREES is set), 
+;                         scalar or vector
+;       dec      Declination, degrees,scalar or vector
+;       gl       Galactic longitude, degrees, scalar or vector
+;       gb       Galactic latitude, degrees, scalar or vector
+;
+;       All results forced double precision floating.
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS:
+;       /DEGREE - If set, then the RA parameter (both input and output) is 
+;                given in degrees rather than hours. 
+;       /FK4 - If set, then the celestial (RA, Dec) coordinates are assumed
+;              to be input/output in the FK4 system.    By default,  coordinates
+;              are assumed to be in the FK5 system.    For B1950 coordinates,
+;              set the /FK4 keyword *and* set the year to 1950.
+;       /SuperGalactic - If set, the GLACTC returns SuperGalactic coordinates
+;              as defined by deVaucouleurs et al. (1976) to account for the 
+;              local supercluster. The North pole in SuperGalactic coordinates 
+;              has Galactic coordinates l = 47.47, b = 6.32, and the origin is 
+;              at Galactic coordinates l = 137.37, b= 0 
+;              
+; EXAMPLES:
+;       Find the Galactic coordinates of Altair (RA (J2000): 19 50 47 
+;       Dec (J2000): 08 52 06)
+;
+;       IDL> glactc, ten(19,50,47),ten(8,52,6),2000,gl,gb,1
+;       ==> gl = 47.74, gb = -8.91
+;
+; PROCEDURE CALLS:
+;       BPRECESS, JPRECESS, PRECESS
+; HISTORY: 
+;       FORTRAN subroutine by T. A. Nagy, 21-MAR-78.
+;       Conversion to IDL, R. S. Hill, STX, 19-OCT-87.
+;       Modified to handle vector input, E. P. Smith, GSFC, 14-OCT-94
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added DEGREE keyword, C. Markwardt, Nov 1999
+;       Major rewrite, default now FK5 coordinates, added /FK4 keyword
+;       use external precession routines    W. Landsman   April 2002
+;       Add /Supergalactic keyword W. Landsman  September 2002
+;       Fix major bug when year not 2000 and /FK4 not set W. Landsman July 2003
+;-
+ On_error,2
+ compile_opt idl2
+
+if N_params() lt 6 then begin
+     print,'Syntax -  glactc, ra, dec, year, gl, gb, j, [/DEGREE, /FK4]'
+     print,'j = 1: ra,dec --> gl,gb   j = 2:  gl,gb -->ra,dec'
+     return
+endif
+radeg = 180.0d/!DPI
+;
+; Galactic pole at ra 12 hrs 49 mins, dec 27.4 deg, equinox B1950.0
+; position angle from Galactic center to equatorial pole = 123 degs.
+
+ if keyword_set(SuperGalactic) then begin
+    rapol = 283.18940711d/15.0d & decpol = 15.64407736d
+    dlon =  26.73153707 
+ endif else begin 
+   rapol = 12.0d0 + 49.0d0/60.0d0 
+   decpol = 27.4d0
+   dlon = 123.0d0
+ endelse
+   sdp = sin(decpol/radeg)
+   cdp = sqrt(1.0d0-sdp*sdp)
+   radhrs=radeg/15.0d0
+
+ ;
+; Branch to required type of conversion.   Convert coordinates to B1950 as 
+; necessary
+case j of                   
+    1:  begin
+        if ~keyword_set(degree) then  ras = ra*15.0d else ras =ra
+        decs = dec
+        if ~keyword_set(fk4) then begin
+                 if year NE 2000 then precess,ras,decs,year,2000
+                 bprecess,ras,decs,ra2,dec2
+                 ras = ra2
+                 decs = dec2
+       endif else if year NE 1950 then precess,ras,decs,year,1950,/fk4
+        ras = ras/radeg - rapol/radhrs 
+        sdec = sin(decs/radeg)
+        cdec = sqrt(1.0d0-sdec*sdec)
+        sgb = sdec*sdp + cdec*cdp*cos(ras)
+        gb = radeg * asin(sgb)
+        cgb = sqrt(1.0d0-sgb*sgb)
+        sine = cdec * sin(ras) / cgb
+        cose = (sdec-sdp*sgb) / (cdp*cgb)
+        gl = dlon - radeg*atan(sine,cose)
+        ltzero=where(gl lt 0.0, Nltzero)
+        if Nltzero ge 1 then gl[ltzero]=gl[ltzero]+360.0d0
+        return
+        end
+    2:  begin
+        sgb = sin(gb/radeg)
+        cgb = sqrt(1.0d0-sgb*sgb)
+        sdec = sgb*sdp + cgb*cdp*cos((dlon-gl)/radeg)
+        dec = radeg * asin(sdec)
+        cdec = sqrt(1.0d0-sdec*sdec)
+        sinf = cgb * sin((dlon-gl)/radeg) / cdec
+        cosf = (sgb-sdp*sdec) / (cdp*cdec)
+        ra = rapol + radhrs*atan(sinf,cosf)
+        ra = ra*15.0d
+         if ~keyword_set(fk4) then begin
+                    ras = ra & decs = dec
+                  jprecess,ras,decs,ra,dec
+                  if year NE 2000 then precess,ra,dec,2000,year
+        endif else if year NE 1950 then begin
+                  precess,ra,dec,1950,year,/fk4
+        endif 
+                   
+        gt36 = where(ra gt 360.0, Ngt36)
+        if Ngt36 ge 1 then ra[gt36] = ra[gt36] - 360.0d0
+        if ~keyword_set(degree) then      ra = ra / 15.0D0
+
+   
+        return
+        end
+endcase
+end
diff --git a/Code/script_idl_mv/astrolib/glactc_pm.pro b/Code/script_idl_mv/astrolib/glactc_pm.pro
new file mode 100644
index 0000000000000000000000000000000000000000..75c0206c9dc0602166e37fab2f733facb623f0dc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/glactc_pm.pro
@@ -0,0 +1,193 @@
+pro glactc_pm,ra,dec,mu_ra,mu_dec,year,gl,gb,mu_gl,mu_gb,j, $
+	degree=degree, fk4 = fk4, SuperGalactic = superGalactic, mustar=mustar
+;+
+; NAME:  
+;       GLACTC_PM
+; PURPOSE:
+;        Convert between celestial and Galactic (or Supergalactic) proper
+;        motion (also converts coordinates).
+; EXPLANATION:
+;       Program to convert proper motion in equatorial coordinates (ra,dec)
+;       to proper motion in Galactic coordinates (gl, gb) or Supergalacic 
+;       Coordinates  (sgl,sgb)  or back to equatorial coordinates (j=2).
+;       The proper motion unit is arbitrary, but be sure to set /MUSTAR if
+;       units are the projection of the proper motion on the RA, Dec axis.
+;       It does precession on the coordinates but does not
+;       take care of precession of the proper motions which is usually a
+;       very small effect.
+;
+; CALLING SEQUENCE: 
+;       GLACTC_PM, ra, dec, mu_ra,mu_dec,year, gl, gb, mu_gl, mu_gb, j, 
+;                  [ /DEGREE, /FK4, /SuperGalactic, /mustar ]
+;
+; INPUT PARAMETERS: 
+;       year     equinox of ra and dec, scalar       (input)
+;       j        direction of conversion     (input)
+;		1:  ra,dec,mu_ra,mu_dec --> gl,gb,mu_gl,mu_gb
+;		2:  gl,gb,mu_gl,mu_gb  --> ra,dec,mu_ra,mu_dec
+;
+; INPUTS OR OUTPUT PARAMETERS: ( depending on argument J )
+;       ra       Right ascension, hours (or degrees if /DEGREES is set), 
+;                         scalar or vector.  
+;       dec      Declination, degrees,scalar or vector
+;       mu_ra    right ascension proper motion any proper motion unit
+;					(angle/time)
+;       mu_dec    declination proper motion in any proper motion unit
+;					(angle/time)
+;       gl       Galactic longitude, degrees, scalar or vector
+;       gb       Galactic latitude, degrees, scalar or vector
+;       mu_gl    galactic longitude proper motion in any time unit
+;       mu_gb    galactic latitude proper motion in any time unit
+;       All results forced double precision floating.
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS:
+;       /DEGREE - If set, then the RA parameter (both input and output) is 
+;                given in degrees rather than hours. 
+;       /FK4 - If set, then the celestial (RA, Dec) coordinates are assumed
+;              to be input/output in the FK4 system.    By default,  coordinates
+;              are assumed to be in the FK5 system.    For B1950 coordinates,
+;              set the /FK4 keyword *and* set the year to 1950.
+;       /SuperGalactic - If set, the GLACTC returns SuperGalactic coordinates
+;              as defined by deVaucouleurs et al. (1976) to account for the 
+;              local supercluster. The North pole in SuperGalactic coordinates 
+;              has Galactic coordinates l = 47.47, b = 6.32, and the origin is 
+;              at Galactic coordinates l = 137.37, b= 0 
+;	/mustar - if set then input and output of mu_ra and mu_dec are the 
+;                projections of mu in the ra or dec direction rather than
+;		the d(ra)/dt or d(mu)/dt.  So mu_ra becomes mu_ra*cos(dec)
+;               and mu_gl becomes mu_gl*cos(gb).
+;              
+; EXAMPLES:
+;       Find the SuperGalactic proper motion of M33 given its
+;       equatorial proper motion mu* =(-29.3, 45.2) microas/yr.
+;       Where the (*) indicates ra component is actual mu_ra*cos(dec) 
+;		(Position: RA (J2000): 01 33 50.9, Dec (J2000): 30 39 36.8)
+;
+;       IDL> glactc_pm, ten(1,33,50.9),ten(30,39,36.8),-29.3,45.2, 2000,$
+;				sgl,sgb,mu_sgl,mu_sgb,1,/Supergalactic,/mustar
+;       ==> SGL = 328.46732 deg, SGB = -0.089896901 deg, 
+;			mu_sgl = 33.732 muas/yr, mu_gb = 41.996 muas/yr.
+;         And for the roundtrip:
+;       IDL> glactc_pm, ra,dec,mu_ra,mu_dec,2000,$
+;       IDL>  sgl,sgb,mu_sgl,mu_sgb,2,/Supergalactic,/mustar
+;        ==> ra=1.5641376 hrs., dec= 30.660277 deg,
+;            mu_ra= -29.300000 muas/yr, mu_dec=i 45.200000 muas/yr
+;
+; PROCEDURE CALLS:
+;       BPRECESS, JPRECESS, PRECESS
+; HISTORY: 
+;       Written                Ed Shaya, U of MD,  Oct 2009.
+;       Adapted from GLACTC  to make proper motion transformations, 
+;       Correct occasional sign error in galactic longitude E. Shaya  Nov 2011
+;       Correct occasional sign error for year not set to 1950  W. Landsman,F. Mazzi July 2015
+;-
+IF n_PARAMS() LT 6 THEN BEGIN
+	PRINT,'Syntax - glactc_pm,ra,dec,mu_ra,mu_dec,year,gl,gb,mu_gl,mu_gb, j, [/DEGREE, /FK4, /mustar]'
+	PRINT,'j = 1: ra,dec,mu_ra,mu_dec --> gl,gb,mu_gl,mu_gb'
+	PRINT, 'j = 2:  gl,gb,mu_gl,mu_gb --> ra,dec,mu_ra,mu_dec'
+	RETURN
+ENDIF
+Radeg = 180.0d/!DPI
+;
+; Galactic pole at ra 12 hrs 49 mins, dec 27.4 deg, equinox B1950.0
+; position angle from Galactic center to equatorial pole = 123 degs.
+
+IF KEYWORD_SET(SuperGalactic) THEN BEGIN
+    rapol = 283.18940711d/15.0d & decpol = 15.64407736d
+    dlon =  26.73153707 
+ENDIF ELSE BEGIN 
+   rapol = 12.0d0 + 49.0d0/60.0d0 
+   decpol = 27.4d0
+   dlon = 123.0d0
+ENDELSE
+sdp = SIN(decpol/radeg)
+cdp = SQRT(1.0d0-sdp*sdp)
+radhrs=radeg/15.0d0
+
+; Branch to required type of conversion.   Convert coordinates to B1950 as 
+; necessary
+CASE j OF                   
+    1: BEGIN
+        IF ~KEYWORD_SET(degree) THEN  ras = ra*15.0d ELSE ras =ra
+        decs = dec
+        IF ~KEYWORD_SET(fk4) THEN BEGIN
+        	IF year NE 2000 THEN precess,ras,decs,year,2000
+        	bprecess,ras,decs,ra2,dec2
+        	ras = ra2
+        	decs = dec2
+        ENDIF ELSE IF year NE 1950 THEN precess,ras,decs,year,1950,/fk4
+	raIndeg = ras
+        ras = ras/radeg - rapol/radhrs 
+        sdec = SIN(decs/radeg)
+        cdec = SQRT(1.0d0-sdec*sdec)
+        sgb = sdec*sdp + cdec*cdp*COS(ras)
+        gb = radeg * ASIN(sgb)
+        cgb = SQRT(1.0d0-sgb*sgb)
+        sine = cdec * SIN(ras) / cgb
+        cose = (sdec-sdp*sgb) / (cdp*cgb)
+        gl = dlon - radeg*ATAN(sine,cose)
+        ltzero=WHERE(gl lt 0.0, Nltzero)
+        IF Nltzero GE 1 THEN gl[ltzero]=gl[ltzero]+360.0d0
+
+; 	Calculate proper motions transforms for j = 1
+;       Take derivative of sgb line above:
+        IF ~KEYWORD_SET(mustar) THEN mu_ra = mu_ra*cdec
+	mu_gb =  mu_dec*(cdec*sdp-sdec*cdp*COS(ras))/cgb $
+		  - mu_ra*cdp*SIN(ras)/cgb
+;	Get mu_gl by using the known length of the vector.
+	mu_gl = SQRT(mu_dec^2 + mu_ra^2 - mu_gb^2)
+        IF ~KEYWORD_SET(mustar) THEN mu_gl = mu_gl/cgb
+
+;       However, sqrt gives an ambiguous sign.
+;	Determine the sign by seeing which direction it is going in gl.
+	glactc,raIndeg,decs,year,gl0,gb0,1,/degree,Supergalactic=Supergalactic
+	ra_delta = 1d-2*mu_ra/ABS(mu_ra)
+	dec_delta = 1d-2*mu_dec/ABS(mu_ra)
+	glactc, raIndeg+ra_delta, decs+dec_delta, year, gl2, gb2, 1,$
+		/degree,Supergalactic=Supergalactic
+	IF (gl2 LT gl0) THEN mu_gl = -ABS(mu_gl)
+
+
+        RETURN
+        END
+    2: BEGIN
+        sgb = SIN(gb/radeg)
+        cgb = SQRT(1.0d0-sgb*sgb)
+        sdec = sgb*sdp + cgb*cdp*COS((dlon-gl)/radeg)
+        dec = radeg * ASIN(sdec)
+        cdec = SQRT(1.0d0-sdec*sdec)
+        sinf = cgb * SIN((dlon-gl)/radeg) / cdec
+        cosf = (sgb-sdp*sdec) / (cdp*cdec)
+        ra = rapol + radhrs*ATAN(sinf,cosf)
+        ra = ra*15.0d
+
+;	Calculate proper motions for j=2, see above (j=1 case)
+        IF ~KEYWORD_SET(mustar) THEN mu_gl = mu_gl*cgb
+	mu_dec =  mu_gb*(cgb*sdp-sgb*cdp*COS((dlon-gl)/radeg))/cdec $
+		+ mu_gl*cdp*SIN((dlon-gl)/radeg)/cdec
+	mu_ra = SQRT(mu_gl^2 + mu_gb^2 - mu_dec^2)
+        IF ~KEYWORD_SET(mustar) THEN mu_ra = mu_ra/cdec
+
+;       However, sqrt gives an ambiguous sign.
+;	Determine the sign by seeing which direction it is going in gl.
+	glactc,raIndeg,decs0,year,gl,gb,2,/degree,Supergalactic=Supergalactic
+	mu_gl_delta = 1d-2*mu_gl/ABS(mu_gl)
+	mu_gb_delta = 1d-2*mu_gb/ABS(mu_gl)
+	glactc, ra2, dec2, year, gl+mu_gl_delta, gb+mu_gb_delta, 2,$
+		/degree,Supergalactic=Supergalactic
+	IF (ra2 LT raIndeg) THEN mu_ra = -ABS(mu_ra)
+
+        IF ~KEYWORD_SET(fk4) THEN BEGIN
+        	ras = ra & decs = dec
+        	jprecess,ras,decs,ra,dec
+        	IF year NE 2000 THEN precess,ra,dec,2000,year
+        ENDIF ELSE BEGIN
+		IF year NE 1950 THEN precess,ra,dec,1950,year,/fk4
+	ENDELSE 
+        gt36 = WHERE(ra GT 360.0, Ngt36)
+        IF Ngt36 GE 1 THEN ra[gt36] = ra[gt36] - 360.0d0
+        IF ~KEYWORD_SET(degree) THEN ra = ra/15.0D0
+        RETURN
+        END
+ENDCASE
+END
diff --git a/Code/script_idl_mv/astrolib/group.pro b/Code/script_idl_mv/astrolib/group.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2df2d6f4bf256371188a57128efb0804abc2a8f0
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/group.pro
@@ -0,0 +1,107 @@
+PRO GROUP, X, Y, RCRIT, NGROUP
+;+
+; NAME:
+;      GROUP
+; PURPOSE:
+;      Assign stars with non-overlapping PSF profiles into distinct groups
+; EXPLANATION:
+;      Part of the IDL-DAOPHOT sequence
+;
+; CALLING SEQUENCE:
+;      GROUP, X, Y, RCRIT, NGROUP
+;
+; INPUTS:
+;      X - vector, giving X coordinates of a set of stars.
+;      Y - vector, giving Y coordinates of a set of stars.
+;           If X and Y are input as integers, then they will be converted to 
+;           floating point
+;      RCRIT - scalar, giving minimum distance between stars of two
+;               distinct groups.  Stars less than this distance from
+;               each other are always in the same group.    Stetson suggests
+;               setting the critical distance equal to the PSF radius +
+;               the Fitting radius.
+;
+; OUTPUTS:
+;      NGROUP - integer vector, same number of elements as X and Y,
+;               giving a group number for each star position.  Group
+;               numbering begins with 0.
+;
+; METHOD:
+;      Each position is initially given a unique group number.  The distance
+;      of each star is computed against every other star.   Those distances
+;      less than RCRIT are assigned the minimum group number of the set.   A
+;      check is then made to see if any groups have merged together.
+;
+; PROCEDURES USED:
+;      REM_DUP()
+;
+; REVISION HISTORY:
+;      Written W. Landsman  STX                  April, 1988
+;      Major revision to properly merge groups together  W. Landsman   Sep 1991
+;      Work for more than 32767 points    W. Landsman  March 1997
+;      Converted to IDL V5.0   W. Landsman   September 1997
+;      Avoid overflow if X and Y are integers      W. Landsman  Feb. 1999   
+;-
+ On_error,2                                   ;Return to caller
+
+ if N_params() LT 4 then begin
+    print,'Syntax - group, x, y, rcrit, ngroup'
+    print,'   x,y -   Input position vectors'
+    print,'   rcrit - Minimum radius between stars of different groups'
+    print,'   ngroup - Output vector of group indices'
+    return
+ endif
+
+ rcrit2 = rcrit^2                            ;Don't bother taking square roots
+ npts = min( [N_elements(x), N_elements(y)] )    ;Number of stars
+
+ if npts LT 2 then message, $
+    'ERROR - Input position X,Y vectors must contain at least 2 points'
+
+ x = 1.0*x  &  y = 1.0*y   ;Make sure at least floating point
+ ngroup =  lindgen(npts)   ;Initially each star in a separate group
+
+;  Whenever the positions between two stars are less than the critical
+;  distance, assign both stars the minimum group id.   The tricky part
+;  is to recognize when distinct groups have merged together.
+
+ for i = 0l,npts-2 do begin
+      dis2 = (x[i] - x[i+1:*])^2 + (y[i] - y[i+1:*])^2
+      good =  where( dis2 LE rcrit2, ngood)
+      if ngood GT 0 then begin             ;Any stars within critical radius?
+
+                good = [i,good+i+1]
+                groupval = ngroup[good]
+                mingroup = min( groupval )
+                    if ( mingroup LT i ) then begin      ;Any groups merge?
+                       groupval = groupval[ where( groupval LT i, nval) ]
+                       if nval GT 1 then $
+                         groupval = groupval[ rem_dup(groupval) ]
+                         nval = N_elements(groupval)
+
+                           if nval GE 2 then for j= 1, nval-1 do begin  
+                             redo = where ( ngroup EQ groupval[j], ndo )
+                             if ndo GT 0 then ngroup[redo] = mingroup
+                           endfor
+
+                    endif
+                ngroup[good] = mingroup
+      endif
+endfor
+;
+; Star are now placed in distinct groups, but they are not ordered
+; consecutively.  Remove gaps in group ordering
+;
+ if max(ngroup) EQ 0 then return               ;All stars in one group ?
+
+ ghist = histogram(ngroup,min=0)
+ gmax = max(ghist)
+ val = where(ghist GE 1, ngood)  
+ if ( ngood GT 0 ) then $ 
+          for i = 0, ngood-1 do ngroup[ where( ngroup EQ val[i] ) ] = i
+
+ message,'Number of Groups: '+ strtrim(ngood,2), /INF 
+ message,'Largest group size '+ strtrim(gmax,2) + ' stars',/INF
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/gsss_stdast.pro b/Code/script_idl_mv/astrolib/gsss_stdast.pro
new file mode 100644
index 0000000000000000000000000000000000000000..12793f127f7dd8a617bf5db0af939ee40a541f18
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gsss_stdast.pro
@@ -0,0 +1,105 @@
+pro GSSS_StdAst,h,xpts,ypts
+;+
+; NAME:
+;      GSSS_STDAST
+;
+; PURPOSE:
+;      Insert the closest tangent projection astrometry into an GSSS Image
+;   
+; DESCRIPTION:
+;       This procedure takes a header with GSSS (ST Guide Star Survey) 
+;       astrometry and writes a roughly equivalent tangent projection 
+;       astrometry into the header.     One might want to do this if (1)
+;       one needs to use software which does not recognize the GSSS astrometric
+;       parameters or (2) if the the image to be transformed, since the 
+;       highly nonlinear GSSS solution does not transform easily.  
+;
+; CALLING SEQUENCE:
+;       GSSS_STDAST, H, [Xpts, Ypts]
+;
+; INPUT - OUTPUT:
+;       H -  FITS header (string array) containing GSSS astrometry.  
+;       GSSS_STDAST will write the roughly equivalent tangent projection 
+;               astrometry solution into H.
+; OPTIONAL INPUTS:
+;       xpts, ypts -- Vectors giving the X and Y positions of the three 
+;               reference points used to find approximate tangent projection.
+;               Default is Xpts = [0.2,0.8,0.5], Ypts = [0.2, 0.4, 0.8]
+; METHOD:
+;       The procedures GSSSXYAD is used to exactly determine the RA and Dec
+;       at 3 reference points.    STARAST is then used to find the tangent
+;       projection astrometry that best matches these reference points.
+;
+; NOTES:
+;       Images from the STScI server (http://archive.stsci.edu/dss/) contain
+;       both a GSSS polynomial plate solution and an approximate WCS tangent
+;       projection.    The value  of the WCSNAME keyword in the FITS header 
+;       is 'DSS'.    If WCSNAME = "DSS' then the more accurate DSS astrometry
+;       is extracted by EXTAST    This procedure changes the value of WCSNAME  
+;       to 'DSS_TANGENT' to indicate that the tangent solution should be used.
+;    
+;       Some early GSSS images (before the 1994 CD-Rom) used keywords CRPIXx
+;       rather than CNPIXx.    The GSSS astrometry in these images could be
+;       corrupted by this procedure as the CRPIXx values will be altered.
+;
+;       The tangent is only a approximation of the nonlinear GSSS astrometry,
+;       but is generally accurate to about 0.1 pixels on a 1024 x 1024 image.
+;
+; PROCEDURES USED:
+;       GSSSEXTAST, GSSSXYAD, STARAST, PUTAST, SXADDHIST, SXDELPAR
+;
+; HISTORY:
+;       13-AUG-91 Version 2 written from MAKEASTGSSS  Eric Deutsch (STScI)
+;       Delete CDELT* keywords from header   W. Landsman      May 1994
+;       Remove call to BUILDAST  W. Landsman                  Jan, 1995
+;       Added optional Xpts, Ypts parameters   E. Deutsch     Oct, 1995
+;       Add WCSNAME   W. Landsman                             Nov 2006
+;-
+  On_error,2
+  compile_opt idl2
+
+  arg = N_params()
+
+  if (arg lt 1) then begin
+    print,'Syntax - GSSS_StdAst, header, [xpts, ypts]'
+    print,'Purpose - Write tangent projection astrometry into a GSSS header'
+    return
+    endif
+
+; options for supplying of this info by Deutsch 10/5/95
+  if (n_elements(xpts) eq 0) or (n_elements(ypts) eq 0) then begin
+    NAXIS1 = sxpar(h,'NAXIS1') & NAXIS2 = sxpar(h,'NAXIS2')
+    X = [.2,.8,.5]*NAXIS1 & Y=[.2,.4,.8]*NAXIS2
+  endif else begin
+    x=xpts & y=ypts
+    endelse
+
+  GSSSExtAst,h,gsa
+  GSSSXYAD,gsa,X,Y,ra,dec
+
+  starast, RA, DEC, X, Y, cd
+  crval=[RA[0],DEC[0]] & crpix=[X[0],Y[0]]+1
+
+  sxaddpar, h, 'WCSNAME', 'DSS_TANGENT', $
+            'WCS Tangent Approximation to full plate solution' 
+  sxaddpar, h, 'CTYPE1','RA---TAN'
+  sxaddpar, h, 'CTYPE2','DEC--TAN'
+  sxaddpar, h, 'CD1_1', cd[0,0]
+  sxaddpar, h, 'CD1_2', cd[0,1]
+  sxaddpar, h, 'CD2_1', cd[1,0]
+  sxaddpar, h, 'CD2_2', cd[1,1]
+  sxaddpar, h, 'CRPIX1', crpix[0]
+  sxaddpar, h, 'CRPIX2', crpix[1]
+  sxaddpar, h, 'CRVAL1', crval[0]
+  sxaddpar, h, 'CRVAL2', crval[1]
+
+  hist = ['GSSS_STDAST: Astrometry calculated from GSSS format and written', $
+          'GSSS_STDAST: in tangent projection format: ' + systime() ]
+  sxaddhist,hist,h
+
+  sxdelpar, h, 'CDELT1'
+  sxdelpar, h, 'CDELT2'
+  
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/gsssadxy.pro b/Code/script_idl_mv/astrolib/gsssadxy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..561c644c2af8a69d13b255a68a7c52808f336ff2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gsssadxy.pro
@@ -0,0 +1,174 @@
+pro GSSSadxy,gsa,ra,dec,x,y, PRINT = print
+;+
+; NAME:
+;      GSSSADXY
+; PURPOSE:
+;       Converts RA and DEC (J2000) to (X,Y) for an STScI GuideStar image.   
+; EXPLANATION:
+;       The sky coordinates may be printed and/or returned in variables.
+;
+; CALLING SEQUENCE:
+;       GSSSADXY, GSA, Ra,Dec, [ X, Y, /Print ] 
+
+; INPUT:
+;       GSA - the GSSS Astrometry structure created by GSSSEXTAST
+;       RA  - the RA coordinate(s) in *degrees*, scalar or vector
+;       DEC - the DEC coordinate(s) in *degrees*, scalar or vector
+;
+; OPTIONAL KEYWORD INPUT:
+;       /PRINT - If this keyword is set and non-zero, then coordinates will be
+;               displayed at the terminal
+; OUTPUT:
+;       X - the corresponding X pixel coordinate(s), double precision
+;       Y - the corresponding Y pixel coordinate(s), double precision
+;
+;       X and Y will be in IDL convention (first pixel 0,0)
+; EXAMPLE:
+;       Given a FITS header, hdr, from the STScI Guidestar Survey, determine
+;       the X,Y coordinates of 3C 273 (RA = 12 29 6.7  +02 03 08)
+;
+;       IDL> GSSSEXTAST, hdr, gsa          ;Extract astrometry structure
+;       IDL> GSSSADXY, gsa, ten(12,29,6.7)*15,ten(2,3,8),/print
+;
+; NOTES:
+;       For most purpose users can simply use ADXY, which will call GSSSADXY
+;       if it is passed a GSSS header.
+;
+; PROCEDURES CALLED:
+;       ASTDISP - Print RA, Dec in standard format
+; HISTORY:
+;       10-JUL-90 Version 1 written by Eric W. Deutsch
+;               Derived from procedures written by Brian McLean
+;       Vectorized code   W. Landsman        March, 1991
+;       14-AUG-91 Fixed error which caused returned X and Y to be .5 pixels too
+;               large.  Now X,Y follows same protocol as ADXY.
+;       June 1994 - Dropped PRFLAG parameter, added /PRINT  W. Landsman (HSTX)
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       29-JUN-99 Added support for AMD[X,Y]1[2-3] for DSS images by E. Deutsch
+;       Reduce memory requirements for large arrays D. Finkbeiner April 2004
+;       Remove 
+;-
+  On_error,2
+  arg = N_params()
+  if (arg lt 5) then begin
+    print,'Syntax - GSSSADXY, GSSS_Astrom_struct, ra, dec, x, y, print_flag'
+    print,'e.g.: IDL> GSSSADXY, gsa, ra, dec, x, y, 1'
+    return
+    endif
+
+; Set Constants
+  iters = 0 & maxiters=50 & tolerance=0.0000005
+  radeg = 180.0d/!DPI  & arcsec_per_radian= 3600.0d*radeg
+
+  pltdec = gsa.crval[1]/radeg
+
+  dec_rad = dec/radeg 
+  cosd = cos(dec_rad)
+  sind = sin(temporary(dec_rad))
+  ra_dif = ra/radeg - gsa.crval[0]/radeg
+
+  div = ( sind*sin(pltdec) + cosd*cos(pltdec)*cos(ra_dif))
+  xi = cosd*sin(ra_dif)*arcsec_per_radian/div
+  eta = ( sind*cos(pltdec)-cosd*sin(pltdec)*cos(ra_dif))* $
+    (arcsec_per_radian/temporary(div))
+  ra_dif = 0
+  cosd = 0 & sind = 0
+  
+  obx = xi/gsa.pltscl
+  oby = eta/gsa.pltscl
+
+  repeat begin
+    iters++
+
+    f= gsa.amdx[0]*obx+                 $
+       gsa.amdx[1]*oby+                 $
+       gsa.amdx[2]+                     $
+       gsa.amdx[3]*obx*obx+             $
+       gsa.amdx[4]*obx*oby+             $
+       gsa.amdx[5]*oby*oby+             $
+       gsa.amdx[6]*(obx*obx+oby*oby)+   $
+       gsa.amdx[7]*obx*obx*obx+         $
+       gsa.amdx[8]*obx*obx*oby+         $
+       gsa.amdx[9]*obx*oby*oby+         $
+       gsa.amdx[10]*oby*oby*oby+             $
+       gsa.amdx[11]*obx*(obx*obx+oby*oby)+   $
+       gsa.amdx[12]*obx*(obx*obx+oby*oby)^2
+
+    fx=gsa.amdx[0]+                     $
+       gsa.amdx[3]*2.0*obx+             $
+       gsa.amdx[4]*oby+                 $
+       gsa.amdx[6]*2.0*obx+             $
+       gsa.amdx[7]*3.0*obx*obx+         $
+       gsa.amdx[8]*2.0*obx*oby+         $
+       gsa.amdx[9]*oby*oby+             $
+       gsa.amdx[11]*(3.0*obx*obx+oby*oby)+   $
+       gsa.amdx[12]*(5.0*obx^4 + 6.0*obx^2*oby^2 + oby^4)
+
+    fy=gsa.amdx[1]+                     $
+       gsa.amdx[4]*obx+                 $
+       gsa.amdx[5]*2.0*oby+             $
+       gsa.amdx[6]*2.0*oby+             $
+       gsa.amdx[8]*obx*obx+             $
+       gsa.amdx[9]*obx*2.0*oby+         $
+       gsa.amdx[10]*3.0*oby*oby+        $
+       gsa.amdx[11]*2.0*obx*oby+        $
+       gsa.amdx[12]*(4.0*obx^3*oby + 4.0*obx*oby^3)
+
+
+    g= gsa.amdy[0]*oby+                 $
+       gsa.amdy[1]*obx+                 $
+       gsa.amdy[2]+                     $
+       gsa.amdy[3]*oby*oby+             $
+       gsa.amdy[4]*oby*obx+             $
+       gsa.amdy[5]*obx*obx+             $
+       gsa.amdy[6]*(obx*obx+oby*oby)+   $
+       gsa.amdy[7]*oby*oby*oby+         $
+       gsa.amdy[8]*oby*oby*obx+         $
+       gsa.amdy[9]*oby*obx*obx+         $
+       gsa.amdy[10]*obx*obx*obx+             $
+       gsa.amdy[11]*oby*(obx*obx+oby*oby)+   $
+       gsa.amdy[12]*oby*(obx*obx+oby*oby)^2
+
+    gx=gsa.amdy[1]+                     $
+       gsa.amdy[4]*oby+                 $
+       gsa.amdy[5]*2.0*obx+             $
+       gsa.amdy[6]*2.0*obx+             $
+       gsa.amdy[8]*oby*oby+             $
+       gsa.amdy[9]*oby*2.0*obx+         $
+       gsa.amdy[10]*3.0*obx*obx+        $
+       gsa.amdy[11]*2.0*obx*oby+        $
+       gsa.amdy[12]*(4.0*obx^3*oby + 4.0*obx*oby^3)
+
+
+
+    gy=gsa.amdy[0]+                     $
+       gsa.amdy[3]*2.0*oby+             $
+       gsa.amdy[4]*obx+                 $
+       gsa.amdy[6]*2.0*oby+             $
+       gsa.amdy[7]*3.0*oby*oby+         $
+       gsa.amdy[8]*2.0*oby*obx+         $
+       gsa.amdy[9]*obx*obx+             $
+       gsa.amdy[11]*(3.0*oby*oby+obx*obx)+   $
+       gsa.amdy[12]*(5.0*oby^4 + 6.0*obx^2*oby^2 + obx^4)
+
+
+
+    f -= xi
+    g -= eta
+    deltx = (-f*gy+g*fy) / (fx*gy-fy*gx)
+    delty = (-g*fx+f*gx) / (fx*gy-fy*gx)
+    obx += deltx
+    oby += delty
+
+    ;print,deltx,delty,tolerance,iters,maxiters
+
+    endrep until (min(abs([deltx,delty])) lt tolerance) || (iters gt maxiters)
+
+    eta = 0 &  xi = 0 &  deltx = 0  & delty = 0
+    x = (gsa.ppo3-obx*1000.0)/gsa.xsz-gsa.xll - 0.5
+    y = (gsa.ppo6+oby*1000.0)/gsa.ysz-gsa.yll - 0.5
+    
+  if keyword_set(PRINT) then AstDisp, x, y, ra, dec
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/gsssextast.pro b/Code/script_idl_mv/astrolib/gsssextast.pro
new file mode 100644
index 0000000000000000000000000000000000000000..65340a6970b9ab4c5a3c929a2850bdc9d4bed8f4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gsssextast.pro
@@ -0,0 +1,99 @@
+pro GSSSExtAst, h, astr, noparams
+;+
+; NAME: 
+;      GSSSEXTAST
+;
+; PURPOSE:
+;      Extract IDL astrometry structure from a ST Guide Star Survey FITS header
+;
+; EXPLANATION:
+;      This procedure extracts the astrometry information from a ST Guide
+;      Star Survey FITS header and places it in an IDL structure for 
+;      subsequent use with GSSSxyad and GSSSadxy.
+;
+; CALLING SEQUENCE:
+;      GSSSExtast, hdr, astr, noparams
+; INPUT:
+;      h - the GSSS FITS header
+; OUTPUT:
+;      astr  - Structure containing the GSSS Astrometry information
+;               .CTYPE  =  ['RA---GSS','DEC--GSS'] 
+;               .CRVAL = plate center Ra, Dec (from PLTRAH, PLTRAM etc.)
+;               .XLL,.YLL = offsets lower lefthand corner
+;               .AMDX, .AMDY = 12 transformation coefficients
+;               .XSZ,.YSZ = X and Y pixel size in microns
+;               .PLTSCL = plate scale in arc sec/mm
+;               .PPO3, .PPO6 - orientation coefficients
+; NOTES:
+;      Most users should use EXTAST rather than this procedure.   EXTAST will
+;      call GSSSEXTAST if supplied with GSSS FITS header.
+;
+; PROCEDURES CALLED:
+;      SXPAR() - Extract parameter values from a FITS header
+; HISTORY:
+;       01-JUL-90 Version 1 written by Eric W. Deutsch
+;       Code derived from Software by Brian McLean
+;       20-AUG-91 Modified to Double Precision Variables.  E. Deutsch
+;       June 94 Change astrometry tags to better agree with EXTAST  W. Landsman
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       29-JUN-99 Added support for AMD[X,Y]1[2-3] for DSS images by E. Deutsch
+;       Eliminate use of obsolete !ERR  W. Landsman    February 2000
+;-
+ 
+ On_error,2
+
+ if N_params() lt 2 then begin
+    print,'Syntax - GSSSExtAst, header, GSSS_astrometry_structure, noparams'
+    return
+ endif
+
+ noparams = -1
+
+  astr = {gsss_astrometry, CTYPE: strarr(2), XLL:0, YLL:0, XSZ:0.0D, YSZ:0.0D, $
+                 PPO3:0.0D, PPO6:0.0D, CRVAL: dblarr(2), PLTSCL:0.0D, $
+                 AMDX:dblarr(13), AMDY:dblarr(13) }
+
+;Older GSSS headers used CRPIX1 instead of CRPIXN
+
+  astr.xll = sxpar(h,'CNPIX1', Count = N)
+        if N EQ 0 then begin            
+                astr.xll = sxpar(h, 'CRPIX1')
+                astr.yll = sxpar(h, 'CRPIX2')
+        endif else astr.yll = sxpar(h,'CNPIX2')
+
+  astr.xsz = sxpar(h,'XPIXELSZ')
+  astr.ysz = sxpar(h,'YPIXELSZ')
+  astr.ppo3 = sxpar(h,'PPO3')
+  astr.ppo6 = sxpar(h,'PPO6', Count = N)
+
+  if (N Eq 0) then message,'Header does not contain GSSS astrometry'
+
+  astr.pltscl = sxpar(h,'PLTSCALE')
+
+  pltrah = sxpar( h, 'PLTRAH' )
+  pltram = sxpar( h, 'PLTRAM' )
+  pltras = sxpar( h, 'PLTRAS' )
+  pltdecsn = sxpar( h, 'PLTDECSN' )
+  pltdecd = sxpar( h, 'PLTDECD' )
+  pltdecm = sxpar( h, 'PLTDECM' )
+  pltdecs = sxpar( h, 'PLTDECS' )
+
+  astr.crval[0] = (pltrah + pltram/60.0d + pltras/3600.0D)*15
+  astr.crval[1] = pltdecd + pltdecm/60.0d + pltdecs/3600.0d
+
+  if (strtrim(PLTDECSN,2) EQ '-') then astr.crval[1] = -astr.crval[1]
+
+  ii = strtrim(indgen(13)+1,2)
+  for i = 0,12 do begin
+
+    astr.amdx[i] = sxpar(h, 'AMDX' + ii[i] )
+    astr.amdy[i] = sxpar(h, 'AMDY' + ii[i] )
+ 
+ endfor
+
+  astr.ctype = ['RA---GSS','DEC--GSS'] 
+
+  noparams = 0             ;Successful Extraction of GSSS astrometry params
+
+  return
+ end
diff --git a/Code/script_idl_mv/astrolib/gsssxyad.pro b/Code/script_idl_mv/astrolib/gsssxyad.pro
new file mode 100644
index 0000000000000000000000000000000000000000..70d7c18e41b94b6da2788a4ab8a8e36960d110c7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/gsssxyad.pro
@@ -0,0 +1,116 @@
+pro GSSSxyad, gsa, xin, yin, ra, dec, PRINT = print
+;+ 
+; NAME:
+;       GSSSXYAD
+; PURPOSE:
+;       Convert (X,Y) coordinates in a STScI Guide Star image to RA and Dec
+; EXPLANATION:
+;       The sky coordinates may be printed and/or returned in variables.
+;
+; CALLING SEQUENCE:
+;       GSSSxyad, gsa, x, y, ra, dec, [ /PRINT ]
+; INPUT:
+;       GSA  - The GSSS Astrometry structure extracted from a FITS header 
+;              by GSSSEXTAST
+;       X - The X pixel coordinate(s) of the image, scalar or vector
+;       Y - The Y pixel coordinate(s) of the image, scalar or vector
+;
+; OUTPUT:
+;       RA - The RA coordinate of the given pixel(s) in *degrees*
+;       DEC - The DEC coordinate of the given pixel(s) in *degrees*
+;
+;       Both RA and Dec will be returned as double precision
+;
+; OPTIONAL KEYWORD INPUT:
+;       /PRINT - If this keyword is set and non-zero, then coordinates will be
+;               displayed at the terminal
+; EXAMPLE:
+;       Given a FITS header,hdr, from a GSSS image, print the astronomical
+;       coordinates of (X,Y) = (200.23, 100.16) at the terminal
+;
+;       IDL> GSSSExtast, hdr, gsa        ;Extract astrometry structure
+;       IDL> GSSSxyad, gsa, 200.23, 100.16, /print
+;
+; NOTES:
+;       For most purpose users can simply use XYAD, which will call GSSSXYAD
+;       if it is passed a GSSS header.
+;
+; PROCEDURES CALLED:
+;       ASTDISP - print RA, Dec in a standard format
+; HISTORY:
+;       01-JUL-90 Version 1 written by Eric W. Deutsch
+;       Vectorized Code   W. Landsman        March, 1991
+;       14-AUG-91 Fixed error which caused returned RA and DEC to be off by
+;       -.5 pixels in both X,Y.  Now X,Y follows same protocol as ADXY.
+;       20-AUG-91 Modified to use AstDisp procedure.
+;       June 94 Added /PRINT keyword instead of PRFLAG W. Landsman June 94
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       29-JUN-99 Added support for AMD[X,Y]1[2-3] for DSS images by E. Deutsch
+;-
+
+  arg = N_params()
+  if (arg lt 3) then begin
+    print,'Syntax - GSSSXYAD, GSSS_Astrom_struct, x, y, ra, dec, [/PRINT ]'
+    return
+    endif
+
+  x = xin + 0.5 & y = yin + 0.5
+  obx = ( gsa.ppo3-(gsa.xll+X)*gsa.xsz )/1000.0d0
+  oby = ( (gsa.yll+Y)*gsa.ysz-gsa.ppo6 )/1000.0d0
+
+  xi=gsa.amdx[0]*obx+               $
+     gsa.amdx[1]*oby+               $
+     gsa.amdx[2]+                   $
+     gsa.amdx[3]*obx^2+             $
+     gsa.amdx[4]*obx*oby+           $
+     gsa.amdx[5]*oby^2+             $
+     gsa.amdx[6]*(obx^2+oby^2)+     $
+     gsa.amdx[7]*obx^3+             $
+     gsa.amdx[8]*obx^2*oby+         $
+     gsa.amdx[9]*obx*oby^2+         $
+     gsa.amdx[10]*oby^3+               $
+     gsa.amdx[11]*obx*(obx^2+oby^2)+   $
+     gsa.amdx[12]*obx*(obx^2+oby^2)^2
+
+  eta=gsa.amdy[0]*oby+              $
+      gsa.amdy[1]*obx+              $
+      gsa.amdy[2]+                  $
+      gsa.amdy[3]*oby^2+            $
+      gsa.amdy[4]*oby*obx+          $
+      gsa.amdy[5]*obx^2+            $
+      gsa.amdy[6]*(obx^2+oby^2)+    $
+      gsa.amdy[7]*oby^3+            $
+      gsa.amdy[8]*oby^2*obx+        $
+      gsa.amdy[9]*oby*obx^2+        $
+      gsa.amdy[10]*obx^3+               $
+      gsa.amdy[11]*oby*(obx^2+oby^2)+   $
+      gsa.amdy[12]*oby*(obx^2+oby^2)^2
+
+  twopi = 2.0d*!DPI
+  radeg = 180.0d/!DPI
+  arcsec_per_radian = 360.*60.*60./twopi
+  pltra = gsa.crval[0]/radeg
+  pltdec = gsa.crval[1]/radeg
+
+  xi = xi/arcsec_per_radian
+  eta = eta/arcsec_per_radian
+
+  numerator = xi/cos(pltdec)
+  denominator = 1.0-eta*tan(pltdec)
+  ra = atan(numerator,denominator)+pltra
+
+  bad = where(ra LT 0,nbad)
+  if (nbad GT 0) then ra[bad] = ra[bad]+twopi
+  bad = where(ra GT twopi,nbad)
+  if (nbad GT 0) then ra[bad] = ra[bad]-twopi
+
+  numerator = cos(ra-pltra)
+  denominator = (1.0-eta*tan(pltdec))/(eta+tan(pltdec))
+  dec = atan(float(numerator/denominator))
+
+  ra = ra*radeg
+  dec = dec*radeg
+  if keyword_set(PRINT) then AstDisp, xin, yin, ra, dec
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/hadec2altaz.pro b/Code/script_idl_mv/astrolib/hadec2altaz.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6876ca23ce30f32c915547ffaad92377c1a431fd
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hadec2altaz.pro
@@ -0,0 +1,74 @@
+PRO hadec2altaz, ha, dec, lat, alt, az, WS=WS
+
+;+
+;  NAME:
+;     HADEC2ALTAZ
+;  PURPOSE:
+;      Converts Hour Angle and Declination to Horizon (alt-az) coordinates.
+;  EXPLANATION:
+;      Can deal with NCP/SCP singularity.    Intended mainly to be used by
+;      program EQ2HOR
+;
+; CALLING SEQUENCE:
+;      HADEC2ALTAZ, ha, dec, lat ,alt ,az [ /WS ]
+;
+; INPUTS
+;     ha -  the local apparent hour angle, in DEGREES, scalar or vector
+;     dec -  the local apparent declination, in DEGREES, scalar or vector
+;     lat -  the local latitude, in DEGREES, scalar or vector
+;
+; OUTPUTS
+;     alt - the local apparent altitude, in DEGREES.
+;     az  - the local apparent azimuth, in DEGREES, all results in double
+;           precision
+; OPTIONAL KEYWORD INPUT:
+;      /WS - Set this keyword for the output azimuth to be measured West from 
+;            South.    The default is to measure azimuth East from North.
+;
+; EXAMPLE:
+;     What were the apparent altitude and azimuth of the sun when it transited 
+;     the local meridian at Pine Bluff Observatory (Lat=+43.07833 degrees) on 
+;     April 21, 2002?   An object transits the local meridian at 0 hour angle.
+;     Assume this will happen at roughly 1 PM local time (18:00 UTC).
+;
+;     IDL> jdcnv, 2002, 4, 21, 18., jd  ; get rough Julian date to determine 
+;                                       ;Sun ra, dec.
+;     IDL> sunpos, jd, ra, dec
+;     IDL> hadec2altaz, 0., dec, 43.078333, alt, az
+;
+;       ===> Altitude alt = 58.90
+;            Azimuth  az = 180.0
+
+; REVISION HISTORY:
+;      Written  Chris O'Dell Univ. of Wisconsin-Madison May 2002
+;-
+
+if N_params() LT 4 then begin
+     print,'Syntax - HADEC2ALTAZ, ha, dec, lat ,alt ,az [ /WS ]'
+     return
+endif
+
+d2r = !dpi/180.
+
+sh = sin(ha*d2r) & ch = cos(ha*d2r)
+sd = sin(dec*d2r) & cd = cos(dec*d2r)
+sl = sin(lat*d2r) & cl = cos(lat*d2r)
+
+x = - ch * cd * sl + sd * cl
+y = - sh * cd
+z = ch * cd * cl + sd * sl
+r = sqrt(x^2 + y^2)
+; now get Alt, Az
+
+az = atan(y,x) /d2r
+alt = atan(z,r) / d2r
+
+; correct for negative AZ
+w = where(az LT 0)
+if w[0] ne -1 then az[w] = az[w] + 360.
+
+; convert AZ to West from South, if desired
+if keyword_set(WS) then az = (az + 180.) mod 360.
+
+
+END
\ No newline at end of file
diff --git a/Code/script_idl_mv/astrolib/hastrom.pro b/Code/script_idl_mv/astrolib/hastrom.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ff3c4a615635c8629c1b009a6d9892c35d9f3e39
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hastrom.pro
@@ -0,0 +1,317 @@
+pro hastrom,oldim,oldhd,newim,newhd,refhd,MISSING=missing, INTERP = interp, $
+               ERRMSG = errmsg,CUBIC = cubic, DEGREE = Degree, NGRID = Ngrid, $
+	       SILENT = silent
+;+
+; NAME:
+;       HASTROM
+; PURPOSE:
+;       Transformation of an image to align it with a reference image
+; EXPLANATION:
+;       A  transformation is applied (using POLY_2D) to an image so that   
+;       its astrometry is identical with that in a reference header.  This
+;       procedure can be used to align two images.
+;
+; CALLING SEQUENCE:
+;       HASTROM, oldim, oldhd, newim, newhd, refhd, [MISSING =, INTERP = ]
+;                            or
+;       HASTROM, oldim, oldhd, refhd, [MISSING =, INTERP ={0,1,2}, NGRID =, 
+;                                      CUBIC =, DEGREE = ]
+;
+; INPUTS:
+;       OLDIM - Image array to be manipulated.  If only 3 parameters are
+;               supplied then OLDIM and OLDHD will be modified to contain 
+;               the output image array and header
+;       OLDHD - FITS header array for OLDIM, containing astrometry parameters
+;       REFHD - Reference header, containing astrometry parameters.  OLDIM
+;               will be rotated, shifted, and compressed or expanded until
+;               its astrometry matches that in REFHD.
+; OUTPUTS:
+;       NEWIM - Image array after transformation has been performed.
+;               The dimensions of NEWIM will be identical to the NAXIS1 and 
+;               NAXIS2 keywords specified in REFHD.  Regions on the reference 
+;               image that do not exist in OLDIM can be assigned a value with
+;               the MISSING keyword.
+;       NEWHD - Updated FITS image header associated with NEWIM
+;
+; OPTIONAL INPUT KEYWORDS:
+;       CUBIC - a scalar value between -1 and 0 specifying cubic interpolation
+;               with the specified value as the cubic interpolation parameter.
+;              (see poly_2d for info).    Setting CUBIC to a value greater 
+;               than zero is equivalent to setting CUBIC = -1. 
+;       DEGREE - Integer scalar specifying the degree of the transformation.
+;               See the routine POLYWARP for more info.   Default = 
+;               1 (linear transformation) unless polynomial ('SIP') distortion 
+;               parameters are present in either the input or reference FITS
+;               header.    In that case, the default degree is equal to the
+;               degree of the distortion polynomial.     Currently, HASTROM 
+;               will force a value of degree of less than 4 (see notes)
+;       INTERP - Scalar, one of 0, 1, or 2 determining type of interpolation
+;               0 nearest neighbor, 1 (default) bilinear interpolation, 
+;               2 cubic interpolation.
+;       MISSING - Set this keyword to a scalar value which will be assigned
+;               to pixels in the output image which are out of range of the
+;               supplied imput image.  If not supplied, then linear 
+;               extrapolation is used.   See the IDL manual on POLY_2D.
+;               ***NOTE: A bug was introduced into the POLY_2D function in IDL 
+;               V5.5 (fixed in V6.1) such that the MISSING keyword
+;               may not work properly with floating point data***
+;       NGRID -  Integer scalar specifying the number of equally spaced grid 
+;               points on each axis to use to specify the transformation.   
+;               The value of NGRID must always be greater than DEGREE + 1.
+;               The default is DEGREE + 2 which equals 3 (9 total points) for
+;               DEGREE=1 (linear warping).
+;       SILENT - If set, then some informational error messages are suppressed.
+; OPTIONAL OUTPUT KEYWORD:
+;       ERRMSG - If this keyword is supplied, then any error messages will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+; NOTES:
+;       (1) The 3 parameter calling sequence is less demanding on virtual 
+;               memory.
+;       (2) The astrometry in OLDHD will be precessed to match the equinox
+;                given in REFHD.
+;       (3) If an ST Guidestar image is used for the reference header, then the
+;                output header will be converted to standard astrometry. 
+;       (4) We found (in May 2016) numerical instability in POLYWARP when 
+;             Degree is set to a value of 5 or larger.    Therefore DEGREE will
+;             be forced to a value of 4 or less (along with a warning).      Note 
+;             that in POLYWARP a DEGREE of 5 actually includes 10th order terms 
+;             like x^5*y^5
+; EXAMPLE:
+;       Suppose one has an image array, IM, and an associated FITS header H.
+;       One desires to warp the image array so that it is aligned with another
+;       image with a FITS header, HREF.    Both headers contain astrometry info.
+;       Set pixel values to 0 where there is no overlap between the input and
+;       reference image, and use linear interpolation (default)
+;
+;       IDL> hastrom, IM, H, HREF, MISSING = 0
+;
+; PROCEDURES USED:
+;       ad2xy, check_FITS, extast, get_EQUINOX(), gsssextast, hprecess,
+;       putast, sxaddpar, sxaddhist, sxpar(), xy2ad, zparcheck
+;
+; REVISION HISTORY:
+;       Written  W. Landsman, STX Co.              Feb, 1989
+;       Updated to CHECK_FITS                      Dec, 1991
+;       New astrometry keywords                    Mar, 1994
+;       Recognize GSSS header   W. Landsman        June, 1994
+;       Added CUBIC keyword     W. Landsman        March, 1997
+;       Accept INTERP=0, Convert output GSS header to standard astrometry
+;                               W. Landsman        June 1998
+;       Remove calls to obsolete !ERR system variable   March 2000
+;       Added ERRMSG output keyword  W. Landsman    April 2000
+;       Need to re-extract astrometry after precession  W. Landsman Nov. 2000
+;       Check for distortion parameters in headers, add more FITS HISTORY
+;       information                        W. Landsman   February 2005
+;       Use different coefficient for nearest neighbor to avoid half-pixel
+;       shift with POLY_2D      W. Landsman   Aug 2006
+;       Return ERRMSG if no overlap between images  W. Landsman  Nov 2007
+;       Use V6.0 notation  W. Landsman  Jan 2012
+;       Test for Degree > 4 usage in Polywarp  W. Landsman   May 2016
+;       
+;-
+ compile_opt idl2
+ On_error,2                              ;Return to caller
+ npar = N_params()
+
+ if (npar LT 3) or (npar EQ 4) then begin        ;3 parameter calling sequence?
+        print,'Syntax:  HASTROM, oldim, oldhd, refhd'
+        print,'     or  HASTROM, oldim, oldhd, newim, newhd, refhd'
+        print,'                 [ MISSING=, DEGREE=, INTERP=, NGRID=, CUBIC = ]'
+        return
+ endif  
+
+ if ( npar EQ 3 ) then begin
+        zparcheck, 'HASTROM', newim, 3, 7, 1, 'Reference FITS header'
+        refhd = newim
+ endif else  $
+        zparcheck, 'HASTROM', refhd, 5, 7, 1, 'Reference FITS header'
+
+ radeg = 180.D/!DPI                      ;Double precision !RADEG
+
+save_err = arg_present(errmsg)     ;Does user want error msgs returned?
+
+;                                    Check for valid 2-D image & header
+ check_FITS, oldim, oldhd, dimen, /NOTYPE, ERRMSG = errmsg
+  if errmsg NE '' then begin
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+  endif
+
+  if N_elements(dimen) NE 2 then begin 
+        errmsg =  'ERROR - Input image array must be 2-dimensional'
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+ endif
+
+ xsize_old = dimen[0]  &  ysize_old = dimen[1]
+
+ xsize_ref = sxpar( refhd, 'NAXIS1' )                ;Get output image size
+ ysize_ref = sxpar( refhd, 'NAXIS2' ) 
+ if (xsize_ref LT 1) || (ysize_ref LT 1) then begin 
+       errmsg = 'ERROR - Reference header must be for a 2-dimensional image'
+       if ~save_err then message,'ERROR - ' + errmsg,/CON
+       return
+ endif
+     
+
+; Extract CD, CRPIX and CRVAL value from image header and reference header
+
+ newhd = oldhd
+ extast, newhd, astr_old, par_old    
+ if ( par_old LT 0 ) then begin   
+       errmsg = 'ERROR - Input FITS Header does not contain astrometry'
+       if ~save_err then message,'ERROR - ' + errmsg,/CON
+       return
+ endif
+ extast, refhd, astr_ref, par_ref    
+ if ( par_old LT 0 ) || ( par_ref LT 0 ) then begin  
+       errmsg = 'ERROR -Reference FITS Header does not contain astrometry'
+       if ~save_err then message,'ERROR - ' + errmsg,/CON
+       return
+ endif
+
+
+;   Precess the header if necessary
+
+ refeq = get_equinox( refhd, code)
+ if code EQ -1 then message, NoPrint = Silent, $
+   'WARNING - Equinox not specified in reference header',/CON else begin
+   oldeq = get_equinox( oldhd, code)
+   if code EQ -1 then message, NoPrint = Silent, $
+      'WARNING - Equinox not specified in original header',/CON else $
+   if oldeq NE refeq then begin      ;Precess header and re-extract structure
+           hprecess, newhd, refeq
+           extast, newhd, astr_old, par_old
+   endif    
+ endelse
+
+; Make a grid of points in the reference image to be used for the transformation
+
+ if ~keyword_set( DEGREE ) then degree = 1
+    if tag_exist(astr_old,'DISTORT') then begin
+       distort = astr_old.distort
+       if distort.name EQ 'SIP' then begin
+          na = ((size(distort.ap,/dimen))[0])
+          degree = degree > (na -1 )     
+        endif
+     endif
+
+    if tag_exist(astr_ref,'DISTORT') then begin
+       distort = astr_ref.distort
+       if distort.name EQ 'SIP' then begin
+          na = ((size(distort.a,/dimen))[0])
+          degree = degree > (na -1 )     
+        endif
+     endif
+      
+ if ~keyword_set(NGRID) then ngrid = (degree + 2)
+ if ~keyword_set(CUBIC) then begin 
+        cubic = 0
+        if N_elements(INTERP) EQ 0 then Interp = 1
+ endif
+
+ nxdif = round( xsize_ref / (ngrid-1) ) + 1
+ nydif = round( ysize_ref / (ngrid-1) ) + 1
+
+ xref = lonarr(ngrid,ngrid) & yref = xref
+ xrow = [ lindgen(ngrid-1)*nxdif, xsize_ref-1. ]
+ yrow = [ lindgen(ngrid-1)*nydif, ysize_ref-1. ]
+
+ for i=0,ngrid-1 do xref[0,i] =   xrow     ;Four corners of image
+ for i=0,ngrid-1 do yref[0,i] = replicate( yrow[i], ngrid)
+
+; Find the position of the reference points in the supplied image
+
+ case strmid(astr_ref.ctype[0],5,3) of
+       'GSS': gsssxyad, astr_ref, xref, yref, ra, dec
+        else: xy2ad, xref, yref, astr_ref, ra, dec
+ endcase
+
+ case strmid(astr_old.ctype[0],5,3) of
+        'GSS': gsssadxy, astr_old, ra, dec, x, y
+        else: ad2xy, ra, dec, astr_old, x, y
+ endcase
+
+ if ( max(x) LT 0 ) || ( min(x) GT xsize_old ) || $
+    ( max(y) LT 0 ) || ( min(y) GT ysize_old ) then begin
+      errmsg = 'No overlap found between original and reference images'
+      if ~save_err then begin 
+         message,'ERROR - ' + errmsg,/CON
+         message,'Be sure you have the right headers and the right equinoxes',/CON
+      endif	 
+      return
+ endif
+ 
+ if degree GT 4 then message,/INF, $
+    'Warning - POLYWARP Polynomial degree set to 4'
+
+  if interp EQ 0 $ ;Get coefficients
+    then polywarp, x+.5, y+.5, xref, yref, degree<4, kx, ky, status = status $
+    else polywarp, x, y, xref, yref, degree<4, kx, ky ,status=status
+    case status of 
+    0: 
+    1: message,NoPrint=Silent,/INF,'Warning: Singular matrix in version in PolyWarp'
+    2: message,NoPrint=Silent,/INF,'Warning: Small Pivot element in Polywarp'
+    3: message,'Invalid Status value returned from Polywarp'
+    endcase
+  
+ 
+ if N_elements(missing) NE 1 then begin        ;Do the warping
+
+ if npar EQ 3 then $
+    oldim = poly_2d( temporary(oldim), kx, ky, Interp, xsize_ref, ysize_ref, $
+                      CUBIC = cubic) else $
+    newim = poly_2d( oldim, kx, ky, Interp, xsize_ref, ysize_ref, CUBIC = cubic)
+
+ endif else begin
+
+ if npar EQ 3 then $
+    oldim = poly_2d( temporary(oldim), kx, ky, Interp, xsize_ref, ysize_ref, $
+         MISSING=missing, CUBIC = cubic) $
+ else $
+    newim = poly_2d( oldim, kx, ky, Interp, xsize_ref, ysize_ref, $
+          MISSING=missing, CUBIC = cubic)
+
+ endelse
+ 
+ sxaddpar, newhd, 'NAXIS1', xsize_ref
+ sxaddpar, newhd, 'NAXIS2', ysize_ref
+
+ if strmid(astr_ref.ctype[0],5,3) EQ 'GSS' then begin
+        refhdnew = refhd
+        gsss_stdast,refhdnew
+        extast,refhdnew,astr_ref
+ endif
+ putast, newhd, astr_ref
+
+ label = 'HASTROM: ' + strmid(systime(),4,20)
+ image = sxpar( refhd, 'IMAGE', Count = N_image)
+ if N_image EQ 1 THEN sxaddhist,label+' Reference Image - ' + image,newhd
+ sxaddhist,label+ ' Original Image Size X: ' + strtrim(xsize_old,2) + $
+                   ' Y: '  + strtrim(ysize_old,2), newhd
+ sxaddhist,'HASTROM: Polynomial Degree used for image warping: ' + $
+            strtrim(degree<4,2), newhd
+ if cubic NE 0 then sterp = 'CUBIC = ' + strtrim(cubic,2) else $
+     sterp = (['Nearest Neighbor','Linear','Cubic'])[interp]
+ sxaddhist,'HASTROM: ' + sterp + ' interpolation',newhd
+ sxaddhist,'HASTROM: Number of grid points ' + strtrim(ngrid*ngrid,2), newhd
+
+; Update BSCALE and BZERO factors in header if necessary.   This is only an
+; approximate correction for nonlinear warping.
+
+ bscale = sxpar( newhd, 'BSCALE', Count = N_Bscale)
+ if (N_bscale GT 0 ) && ( bscale NE 1. ) then begin
+    getrot, astr_old, rot, cdelt_old, SILENT = silent 
+    getrot, astr_ref, rot, cdelt_ref, SILENT = silent
+    pix_ratio = ( cdelt_old[0]*cdelt_old[1]) / (cdelt_ref[0]*cdelt_ref[1] )
+    sxaddpar, newhd, 'BSCALE', bscale/pix_ratio
+    bzero = sxpar( newhd,'BZERO' )
+    if bzero NE 0. then sxaddpar, newhd, 'BZERO', bzero/pix_ratio
+ endif
+
+ if npar LT 4 then oldhd = newhd
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/hboxave.pro b/Code/script_idl_mv/astrolib/hboxave.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d5cfc59ff8054079b5d7e51fa6c7671254a99e6e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hboxave.pro
@@ -0,0 +1,162 @@
+pro hboxave, oldim, oldhd, newim, newhd, box, ERRMSG = errmsg   ;Boxaverage and update header
+;+
+; NAME:
+;       HBOXAVE
+; PURPOSE:
+;       Box average an image array and update the FITS header array
+; EXPLANATION:
+;       The function BOXAVE() is used.  This procedure is recommended for 
+;       integer images when photometric precision is desired, because it 
+;       performs intermediate steps using REAL*4 arithmetic.   Otherwise, the 
+;       procedure HREBIN is much faster.
+;
+; CALLING SEQUENCE:
+;       HBOXAVE, Oldim, Oldhd, Newim, Hewhd, box
+;               or
+;       HBOXAVE, Oldim, Oldhd, box
+;
+; INPUTS:
+;       Oldim - the original image array
+;       Oldhd - the original image FITS header, string array
+;
+; OPTIONAL INPUTS:
+;       box - the box size to be used, integer scalar.  If omitted, then
+;               HBOXAVE will prompt for this parameter.
+;
+; OPTIONAL OUTPUTS:
+;       Newim - the image after boxaveraging
+;       Newhd - header for newim containing updated astrometry info
+;               If output parameters are not supplied, the program
+;               will modify the input parameters OLDIM and OLDHD
+;               to contain the new array and updated header.
+; OPTIONAL KEYWORD OUTPUT:
+;       ERRMSG - If this keyword is supplied, then any error mesasges will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+;
+; PROCEDURE:
+;       The parameters BSCALE, NAXIS1, NAXIS2, CRPIX1, and CRPIX2 and
+;       the CD (or CDELT) parameters are updated for the new FITS header.
+;
+; EXAMPLE:
+;       Compress the image in a FITS file 'image.fits' by a factor of 4 and 
+;       update the astrometry in the FITS header
+;
+;       IDL> im = readfits('image.fits',hdr)    ;Read FITS file into IDL arrays
+;       IDL> hboxave, im, hdr, 4                ;Boxaverage by 4
+;       IDL> writefits,'image.fits',im,hdr      ;Write a new FITS file
+;
+; CALLED PROCEDURES:
+;       CHECK_FITS - Check that the FITS header is appropriate to the image
+;       BOXAVE() - Performs box averaging of an image
+;       SXPAR(), SXADDPAR - Read and write FITS keyword values
+;
+; MODIFICATION HISTORY:
+;       Written, Aug. 1986 W. Landsman, STI Corp.
+;       IDLV2 changes, sxaddpar format keyword added, J. Isensee, July,1990
+;       Fix 0.5 pixel offset in new CRPIX computation W. Landsman, Dec, 1991
+;       Update BSCALE even if no astrometry present   W. Landsman, May 1997
+;       Added ERRMSG keyword, Use double formatting   W. Landsman   April 2000
+;       Recognize PC matrix astrometry format    W. Landsman   December 2001
+;       Use V6.0 notation  W. Landsman  October 2012
+;- 
+ On_error,2                               ;Return to caller on error
+
+ npar = N_params()
+
+ if ( npar LT 2 ) then begin            ;Check # of parameters
+     print,'Syntax: HBOXAVE, oldim, oldhd, [ newim, newhd, box, ERRMSG = ]'
+     print,'    or  HBOXAVE, oldim, oldhd, [ box, ERRMSG =  ]'
+     return
+ endif
+
+ save_err = arg_present(errmsg)      ;Does user want to return error messages?
+;                                    Check for valid 2-D image & header
+  check_FITS, oldim, oldhd, dimen, /NOTYPE, ERRMSG = errmsg
+  if errmsg NE '' then begin
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+  endif
+
+  if N_elements(dimen) NE 2 then begin 
+           errmsg = 'Input image array must be 2-dimensional'
+           if ~save_err then message,'ERROR - ' + errmsg,/CON
+           return
+  endif
+ 
+ xsize = dimen[0]  &  ysize = dimen[1]
+ if npar EQ 3 then begin
+
+    box = newim
+
+ endif else if (npar NE 5) then begin    ;prompt for box size
+
+   print,'Boxaverage an image and update header'
+   print,'Original array size is '+ strn(xsize) + ' by ' + strn(ysize)
+   read, 'Enter width of box to be used in box average: ',box
+
+ endif
+
+ box = fix(box)                      ;Check for integer type 
+ if N_elements(box) NE 1 then begin
+    box = 0
+    read, 'Enter width of box to be used in box average: ',box
+ endif
+
+ newx = xsize/float(box)
+ newy = ysize/float(box)
+
+ if (newx*box NE xsize) || (newy*box NE ysize) then $
+    message,'ERROR - Box size does not evenly divide image size'
+
+ if npar GT 3 then newim = boxave( oldim, box) else $
+                   oldim = boxave( oldim, box)
+
+ newhd = oldhd
+ sxaddpar, newhd, 'NAXIS1', fix(newx)
+ sxaddpar, newhd, 'NAXIS2', fix(newy)
+ label = 'HBOXAVE:' + strmid( systime(), 4, 20)
+ sxaddpar, newhd, 'HISTORY', label + ' Original Image Size Was ' + $
+    strn(xsize) + ' by ' +  strn(ysize) 
+ sxaddpar, newhd, 'HISTORY',label+' Box Width: '+ strn(box)+' Pixels'
+
+; Update astrometry info if it exists
+
+ extast, oldhd, astr, noparams
+ if noparams GE 0 then begin
+
+ pix_ratio = box*box     ;Ratio of old to new pixel areas
+
+ crpix = (astr.crpix - 0.5)/box + 0.5
+ sxaddpar, newhd, 'CRPIX1', crpix[0]
+ sxaddpar, newhd, 'CRPIX2', crpix[1]
+
+ if (noparams NE 2) then begin 
+
+    cdelt = astr.cdelt
+    sxaddpar, newhd, 'CDELT1', CDELT[0]*box
+    sxaddpar, newhd, 'CDELT2', CDELT[1]*box
+
+ endif else begin       ;CDn_m Matrix
+
+    cd = astr.cd
+    sxaddpar, newhd, 'CD1_1', cd[0,0]*box
+    sxaddpar, newhd, 'CD1_2', cd[0,1]*box
+    sxaddpar, newhd, 'CD2_1', cd[1,0]*box
+    sxaddpar, newhd, 'CD2_2', cd[1,1]*box
+
+ endelse 
+ endif
+ 
+ bscale = sxpar( oldhd, 'BSCALE')
+ if ( bscale NE 0 )  && ( bscale NE 1) then $
+      sxaddpar, newhd, 'BSCALE', bscale*pix_ratio, ' CALIBRATION FACTOR'
+
+ bzero = sxpar( oldhd, 'BZERO')
+ if ( bzero NE 0) then sxaddpar, newhd, 'BZERO', bzero*pix_ratio, $
+        ' ADDITIVE CONST FOR CALIB'
+
+ if npar LT 4 then oldhd = newhd
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/hcongrid.pro b/Code/script_idl_mv/astrolib/hcongrid.pro
new file mode 100644
index 0000000000000000000000000000000000000000..68e6b5566048af2420a7c6d3bdbb29430960e7e1
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hcongrid.pro
@@ -0,0 +1,302 @@
+pro hcongrid, oldim, oldhd, newim, newhd, newx, newy, HALF_HALF = half_half, $
+              INTERP=interp, OUTSIZE = outsize, CUBIC = cubic, ERRMSG = errmsg,$
+	      ALT = alt
+;+
+; NAME:
+;       HCONGRID
+; PURPOSE:
+;       CONGRID an image and update astrometry in a FITS header
+; EXPLANATION:
+;       Expand or contract an image using CONGRID and update the 
+;       associated FITS header array.
+;
+; CALLING SEQUENCE:
+;       HCONGRID, oldhd                       ;Update FITS header only
+;       HCONGRID, oldim, oldhd, [ newim, newhd, newx, newy, /HALF_HALF
+;                                 CUBIC = , INTERP=, OUTSIZE=, ERRMSG=, ALT= ]
+;
+; INPUTS:
+;       OLDIM - the original image array
+;       OLDHD - the original image FITS header, string array
+;
+; OPTIONAL INPUTS:
+;       NEWX - size of the new image in the X direction
+;       NEWY - size of the new image in the Y direction
+;               The OUTSIZE keyword can be used instead of the 
+;               NEWX, NEWY parameters
+;
+; OPTIONAL OUTPUTS:
+;       NEWIM - the image after expansion or contraction with CONGRID
+;       NEWHD - header for newim containing updated astrometry info
+;               If output parameters are not supplied, the program
+;               will modify the input parameters OLDIM and OLDHD
+;               to contain the new array and updated header.
+;
+; OPTIONAL KEYWORD INPUTS:
+;      ALT - Single character 'A' through 'Z' or ' ' specifying which astrometry
+;          system to modify in the FITS header.    The default is to use the
+;          primary astrometry of ALT = ' '.    See Greisen and Calabretta (2002)
+;          for information about alternate astrometry keywords.
+
+;       CUBIC - If set and non-zero, then cubic interpolation is used.   Valid
+;               ranges are  -1 <= Cubic < 0.   Setting /CUBIC is equivalent to
+;               CUBIC = -1 and also equivalent to INTERP = 2.   See INTERPOLATE
+;               for more info.    Setting CUBIC = -0.5 is recommended.
+;       ERRMSG - If this keyword is supplied, then any error mesasges will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+;       /HALF_HALF - Due to edge effects, the default behaviour of CONGRID is 
+;           to introduce a slight shift in the image center.  Craig Markwardt
+;           (http://cow.physics.wisc.edu/~craigm/idl/misc.html) has written
+;           a modified version of CONGRID called CMCONGRID that when used with
+;           the /HALF_HALF keyword eliminates any shift.   The use of the 
+;           /HALF keyword emulates CMCONGRID and eliminates any shift in the
+;           image centroid. 
+;       INTERP   - 0 for nearest neighbor, 1 for bilinear interpolation
+;               (default), 2 for cubic (=-1) interpolation.   
+;       OUTSIZE - Two element integer vector which can be used instead of the
+;               NEWX and NEWY parameters to specify the output image dimensions
+; OPTIONAL KEYWORD OUTPUT:
+;       ERRMSG - If this keyword is supplied, then any error mesasges will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+; PROCEDURE:
+;       Expansion or contraction is done using the CONGRID function, unless
+;       HALF_HALF is set. 
+;
+;       The parameters BSCALE, NAXIS1, NAXIS2, CRPIX1, and CRPIX2 and
+;       the CD (or CDELT) parameters are updated for the new header.
+;
+; NOTES:
+;       A FITS header can be supplied as the first parameter without having
+;       to supply an image array.   The astrometry in the FITS header will be
+;       updated to be appropriate to the specified image size.
+;
+;       If the FITS header contains astrometry from a ST Guide Star image,
+;       then the astrometry will be converted to an approximately equivalent
+;       tangent projection before applying CONGRID.
+; EXAMPLE:
+;       Congrid an 512 x 512 image array IM and FITS header H to size 300 x 300
+;       using cubic interpolation.   Use the HALF_HALF keyword to avoid 
+;       a shift of the image centroid
+;
+;       IDL> hcongrid, IM ,H, OUT = [300, 300], CUBIC = -0.5, /HALF
+;
+;       The variables IM and H will be modified to the new image size.
+;
+; PROCEDURES CALLED:
+;       CHECK_FITS, CONGRID(), EXTAST, GSSS_STDAST, SXADDHIST, 
+;       SXADDPAR, SXPAR(), ZPARCHECK
+; MODIFICATION HISTORY:
+;       Written, Aug. 1986 W. Landsman, STI Corp.
+;       Added interp keywords, J. Isensee, July, 1990
+;       Add cubic interpolation W. Landsman HSTX   January 1994
+;       Recognize a GSSS FITS header   W. Landsman   June 1994
+;       Fix case where header but not image supplied  W. Landsman  May 1995
+;       Remove call to SINCE_VERSION()   W. Landsman   March 1996
+;       Assume since IDL V3.5, add CUBIC keyword      W. Landsman   March 1997
+;       Update BSCALE even if no astrometry present   W. Landsman   May 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added HALF_HALF keyword  W. Landsman  February 2000
+;       Added ERRMSG keyword, use double precision formatting W.L.  April 2000
+;       Recognize PC00n00m astrometry format  W. Landsman   December 2001
+;       Now works when both /INTERP and /HALF are set W. Landsman January 2002
+;       Fix output astrometry for non-equal plate scales for PC matrix or
+;       CROTA2 keyword, added ALT keyword.   W. Landsman May 2005
+;       Update distortion parameters if present  W. Landsman January 2008
+;       Don't update BSCALE/BZERO for unsigned integer W.Landsman Mar 2008
+;       Write CRPIX as Double precision if necessary W. Landsman Oct 2012
+;- 
+ On_error,2
+ compile_opt idl2
+ Npar = N_params()      ;Check # of parameters
+
+ if Npar EQ 0  then begin 
+     print,'    Syntax - HCONGRID, oldim, oldhd,[ newim, newhd, newx, newy'
+     print,'           ALT=, CUBIC = , INTERP =, /HALF, OUTSIZE = , ERRMSG=]'
+     return
+ endif
+
+ save_err = arg_present(errmsg)
+ if Npar EQ 1 then begin 
+
+        zparcheck, 'HCONGRID', oldim, 1, 7, 1, 'Image header'
+        oldhd = oldim
+        xsize = sxpar( oldhd,'NAXIS1')
+        ysize = sxpar( oldhd,'NAXIS2')
+
+ endif else begin
+;   Check for valid 2-D image & header
+  check_FITS, oldim, oldhd, dimen, /NOTYPE,ERRMSG = errmsg
+
+  if errmsg NE '' then begin
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+  endif
+  if N_elements(dimen) NE 2 then begin 
+      errmsg =  'Input image array must be 2-dimensional'
+      if ~save_err then message,'ERROR - ' + errmsg,/CON
+      return
+  endif 
+  xsize = dimen[0]  &  ysize = dimen[1]
+ endelse
+    tname = size(oldim,/tname)
+
+ if keyword_set(CUBIC) then interp = 2
+ if N_elements(interp) EQ 0 then interp = 1
+
+ case interp of 
+ 0:   type = ' Nearest Neighbor Approximation'
+ 1:   type = ' Bilinear Interpolation'
+ 2:   type = ' Cubic Interpolation'
+ else: begin 
+       errmsg = 'Illegal value of INTERP keyword, must be 0, 1, or 2'
+       if ~save_err then  message,'ERROR - ' + errmsg,/CON
+       return
+       end
+ endcase
+
+ if npar LT 6 then begin
+    if ( N_elements(OUTSIZE) NE 2 ) then begin
+      message, /INF, $
+        'Original array size is '+ strn( xsize ) + ' by ' + strn(ysize)
+      read,'Enter size of new image in the X direction: ',newx
+      read,'Enter size of new image in the Y direction: ',newy
+   endif else begin
+      newx = outsize[0]
+      newy = outsize[1]
+   endelse 
+ endif
+ 
+ if ( xsize EQ newx ) && ( ysize EQ newy ) then begin 
+       message,'Output image size equals input image size',/INF
+       return
+ endif
+
+ xratio = float(newx)/xsize
+ yratio = float(newy)/ysize
+ lambda = yratio/xratio         ;Measures change in aspect ratio.
+
+
+ if ( npar GT 1 ) then begin
+
+ if keyword_set(half_half) then begin
+   srx = (findgen(newx) + 0.5)/xratio - 0.5
+   sry = (findgen(newy) + 0.5)/yratio - 0.5
+   if interp GT 0 then begin
+      if ( npar GT 2 ) then $
+        newim = interpolate(oldim, srx,sry,/GRID, CUBIC = cubic) else $
+        oldim = interpolate(oldim, srx,sry,/GRID, CUBIC = cubic)
+   endif else begin
+        xr = float(xsize)/newx  & yr = float(ysize)/newy
+        if (npar GT 2) then $
+        newim = POLY_2D(oldim, [[xr/2.,0],[xr,0]], $
+                            [ [xr/2.,yr],[0,0] ],0,newx,newy) else $
+        oldim = POLY_2D(oldim, [[yr/2.,0],[yr,0] ], $
+                            [[ yr/2.,yr],[0,0] ],0,newx,newy) 
+   endelse
+ endif else begin
+ 
+ if ( npar GT 2 ) then $
+      newim = congrid( oldim, newx, newy, INTERP = interp, CUBIC = cubic) else $
+      oldim = congrid( temporary(oldim), newx, newy, $
+                CUBIC = cubic, INTERP=interp )
+ endelse
+
+ endif
+
+ newhd = oldhd
+ sxaddpar, newhd, 'NAXIS1', fix(newx)
+ sxaddpar, newhd, 'NAXIS2', fix(newy)
+ label = 'HCONGRID:' + strmid(systime(),4,20)
+ history =   ' Original Image Size Was '+ strn(xsize) + ' by ' + strn(ysize)
+ sxaddhist, label + history, newhd
+ if npar GT 1 then sxaddhist, label+type, newhd
+
+; Update astrometry info if it exists
+
+ extast, newhd ,astr, noparams, ALT = alt
+ if noparams GE 0 then begin
+ if strmid(astr.ctype[0],5,3) EQ 'GSS' then begin
+        gsss_stdast, newhd
+        extast, newhd, astr, noparams
+ endif
+
+ pix_ratio = xratio*yratio      ;Ratio of pixel areas
+ 
+  crpix = astr.crpix - 1.0
+  
+ if keyword_set(half_half) then begin
+     sxaddpar, newhd, 'CRPIX1' + alt, $
+                      (crpix[0]+0.5)*xratio + 0.5
+     sxaddpar, newhd, 'CRPIX2' + alt,  $
+                       (crpix[1]+0.5)*yratio + 0.5
+ endif else begin 
+     sxaddpar, newhd, 'CRPIX1' + alt , crpix[0]*xratio + 1.0
+     sxaddpar, newhd, 'CRPIX2' + alt , crpix[1]*yratio + 1.0
+ endelse 
+
+
+ if tag_exist(astr,'DISTORT') then begin
+         distort = astr.distort
+	 message,'Updating SIP distortion parameters',/INF
+         update_distort,distort, [1./xratio,0],[1./yratio,0]
+	 astr.distort= distort
+	 add_distort, newhd, astr
+   endif	 
+
+
+
+ if (noparams NE 2) then begin 
+
+    cdelt = astr.cdelt
+    sxaddpar, newhd, 'CDELT1' + alt , CDELT[0]/xratio
+    sxaddpar, newhd, 'CDELT2' + alt , CDELT[1]/yratio
+; Adjust the PC matrix if non-equal plate scales.   See equation 187 in 
+; Calabretta & Greisen (2002)
+    if lambda NE 1.0 then begin
+        cd = astr.cd
+	if noparams EQ 1 then begin
+;Can no longer  use the simple CROTA2 convention, change to PC keywords
+	 sxaddpar,newhd,'PC1_1'+alt, cd[0,0] 
+	 sxaddpar, newhd,'PC2_2'+alt, cd[1,1]
+         sxdelpar, newhd, ['CROTA2','CROTA1']
+        endif	
+        sxaddpar, newhd, 'PC1_2'+alt, cd[0,1]/lambda
+        sxaddpar, newhd, 'PC2_1'+alt, cd[1,0]*lambda
+   endif	
+
+
+ endif else begin
+
+    cd = astr.cd
+    sxaddpar, newhd, 'CD1_1' + alt, cd[0,0]/xratio
+    sxaddpar, newhd, 'CD1_2' + alt, cd[0,1]/yratio
+    sxaddpar, newhd, 'CD2_1' + alt, cd[1,0]/xratio
+    sxaddpar, newhd, 'CD2_2' + alt , cd[1,1]/yratio
+
+ endelse
+ endif 
+
+; Adjust BZERO and BSCALE for new pixel size, unless these values are used
+; to define unsigned integer data types.  
+
+ bscale = sxpar( oldhd, 'BSCALE')
+ bzero = sxpar( oldhd, 'BZERO')
+ unsgn = (tname EQ 'UINT') || (tname EQ 'ULONG') 
+
+ if ~unsgn then begin 
+ if (bscale NE 0) && (bscale NE 1) then $
+    sxaddpar, newhd, 'BSCALE', bscale/pix_ratio, 'Calibration Factor'
+ if (bzero NE 0) then sxaddpar, newhd, 'BZERO', bzero/pix_ratio, $
+       ' Additive Constant for Calibration'
+ endif 
+
+ if npar EQ 2 then oldhd = newhd else $
+       if npar EQ 1 then oldim = newhd
+
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/headfits.pro b/Code/script_idl_mv/astrolib/headfits.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c6495e40f0b00e3af24fe2eb613e29b0135b1535
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/headfits.pro
@@ -0,0 +1,118 @@
+function HEADFITS, filename, EXTEN = exten, Compress = compress, $ 
+                   ERRMSG = errmsg, SILENT = silent
+;+
+; NAME:
+;       HEADFITS
+; PURPOSE:
+;       Read a FITS (primary or extension) header into a string array.
+; EXPLANATION:
+;       HEADFITS() supports several types of compressed files including 
+;       gzip (.gz), Unix compressed (.Z), Bzip2 (.bz2) or FPACK (.fz 
+;       http://heasarc.gsfc.nasa.gov/fitsio/fpack/ )
+;
+; CALLING SEQUENCE:
+;       Result = HEADFITS(Filename/Fileunit ,[ ERRMSG =, EXTEN= , COMPRESS=, 
+;                                            /SILENT ])
+;
+; INPUTS:
+;       Filename = String containing the name of the FITS file to be read.
+;                If set to an empty string, then user will be prompted for name.
+;                File names ending in '.gz' are assumed to be gzip'ed compressed
+;                and under Unix file names ending in '.Z' are assumed to be
+;                Unix compressed, and file names ending in .bz2 are assumed to
+;                be bzip2 compressed.    If this default behaviour is not 
+;                sufficient then use the COMPRESS keyword.
+;                            or
+;       Fileunit - A scalar integer specifying the unit of an already opened
+;                  FITS file.  The unit will remain open after exiting 
+;                  HEADFITS().  There are two possible reasons for choosing 
+;                  to specify a unit number rather than a file name:
+;          (1) For a FITS file with many extensions, one can move to the 
+;              desired extensions with FXPOSIT() and then use HEADFITS().  This
+;              is more efficient that repeatedly starting at the beginning of 
+;              the file.
+;          (2) For reading a FITS file across a Web http: address after opening
+;              the unit with the SOCKET procedure.
+; OPTIONAL INPUT KEYWORDS:
+;      EXTEN  = Either an integer scalar, specifying which FITS extension to 
+;               read, or a scalar string specifying the extension name (stored
+;               in the EXTNAME keyword).   For example, to read the header of 
+;               the first extension set EXTEN = 1.   Default is to read the 
+;               primary FITS header  (EXTEN = 0).    The EXTEN keyword cannot 
+;               be used when a unit number is supplied instead of a file name.
+;     COMPRESS - If this keyword is set and non-zero, then treat the file
+;              as compressed.  If 1 assume a gzipped file.   Use IDL's
+;              internal decompression facilities for gzip files, while for 
+;              Unix or bzip2 compression spawn off a process to decompress and 
+;              use its output as the FITS stream.  If the keyword is not 1, 
+;              then use its value as a string giving the command needed for 
+;              decompression.   See FXPOSIT for more info.
+;     /SILENT - If set, then suppress any warning messages about invalid 
+;              characters in the FITS file.
+; OPTIONAL KEYWORD OUTPUT:
+;       ERRMSG	= If this keyword is present, then any error messages will be
+;                 returned to the user in this parameter rather than
+;                 depending on the MESSAGE routine in IDL.  If no errors are
+;                 encountered, then a null string is returned.  
+;
+; OUTPUTS:
+;       Result of function = FITS header, string array
+;
+; EXAMPLE:
+;       Print the main FITS header of a file 'test.fits' into a string 
+;       variable, h
+;
+;       IDL>  print, headfits( 'test.fits')
+;
+;       Print the second extension header of a gzip compressed FITS file
+;       'test.fits.gz'.  Use HPRINT for pretty format
+;
+;       IDL> hprint, headfits( 'test.fits.gz', ext=2)
+;
+;       Read the extension named CALSPEC 
+;
+;       IDL> hprint,headfits('test.fits.gz',ext='CALSPEC')
+;
+; PROCEDURES CALLED
+;       FXPOSIT(), MRD_HREAD
+; MODIFICATION HISTORY:
+;       Adapted by Frank Varosi from READFITS by Jim Wofford, January, 24 1989
+;       Option to read a unit number rather than file name W.L    October 2001
+;       Test output status of MRD_HREAD call October 2003 W. Landsman
+;       Allow extension to be specified by name Dec 2006 W. Landsman
+;       No need to uncompress FPACK compressed files  May 2009 W. Landsman
+;       Use V6.0 notation   W.L.   Feb. 2011
+;       Do not check for EOF() since MRD_HREAD does this  Nov 2014 W. Landsman  
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 1 then begin
+     print,'Syntax - header = headfits( filename,[ EXTEN=, ERRMSG=, ' + $
+                   '/SILENT, COMPRESS= ])'
+     return, -1
+ endif
+
+  printerr = ~arg_present(errmsg) 
+  errmsg = ''
+  if ~keyword_set(exten) then exten = 0
+
+  unitsupplied = size(filename,/TNAME) NE 'STRING'
+  if unitsupplied then unit = filename else begin 
+     unit = FXPOSIT( filename, exten, errmsg = errmsg, $
+                   /READONLY,compress = compress, SILENT=silent,/headeronly)
+     if unit EQ -1 then begin 
+          if printerr then  $
+	         message,'ERROR - ' + errmsg,/CON 
+       return,-1
+     endif
+  endelse
+  
+  MRD_HREAD, unit, header, status, SILENT = silent
+  if ~unitsupplied then free_lun, unit
+  if status LT 0 then begin
+         if N_elements(errmsg) GT 0 then errmsg = !ERROR_STATE.MSG else $
+          message,'ERROR - ' + !ERROR_STATE.MSG,/CON 
+          return, -1
+  endif else return, header	  
+  end
diff --git a/Code/script_idl_mv/astrolib/helio.pro b/Code/script_idl_mv/astrolib/helio.pro
new file mode 100644
index 0000000000000000000000000000000000000000..70ba4a8c81e61560d3734a080964e9120e9ac627
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/helio.pro
@@ -0,0 +1,189 @@
+PRO HELIO, JD, LIST, HRAD, HLONG, HLAT, RADIAN = radian
+;+
+; NAME: 
+;      HELIO
+; PURPOSE: 
+;      Compute (low-precision) heliocentric coordinates for the planets.
+; EXPLANATION:
+;      The mean orbital elements for epoch J2000 are used.   These are derived
+;      from a 250 yr least squares fit of the DE 200 planetary ephemeris to a 
+;      Keplerian orbit where each element is allowed to vary linearly with 
+;      time.  For dates between 1800 and 2050, this solution fits the 
+;      terrestrial planet orbits to ~25" or better, but achieves only ~600" 
+;      for Saturn.   
+;
+;      Use PLANET_COORDS (which calls HELIO) to get celestial (RA, Dec)
+;      coordinates of the planets
+; CALLING SEQUENCE: 
+;       HELIO, JD, LIST, HRAD, HLONG, HLAT, [/RADIAN]
+; INPUTS:
+;       JD = Julian date, double precision scalar or vector
+;       LIST = List of planets array.  May be a single number.
+;               1 = merc, 2 = venus, ... 9 = pluto.
+;
+; OUTPUTS:
+;       HRAD = array of Heliocentric radii (A.U).
+;       HLONG = array of Heliocentric (ecliptic) longitudes (degrees).
+;       HLAT = array of Heliocentric latitudes (degrees).
+;             These output parameters will be dimensioned Nplanet by Ndate,
+;             where Nplanet is the number of elements of list, and Ndate is 
+;             the number of elements of JD.
+;
+; OPTIONAL INPUT KEYWORD:
+;       /RADIAN - If set, then the output longitude and latitude are given in 
+;                 radians.         
+; EXAMPLE:
+;       (1) Find the current heliocentric positions of all the planets
+;
+;        IDL> GET_JULDATE, jd      ;Get current Julian date
+;        IDL> HELIO,jd,indgen(9)+1,hrad,hlong,hlat  ;Get radius, long, and lat
+;
+;       (2) Find heliocentric position of Mars on August 23, 2000 
+;         IDL> JDCNV, 2000,08,23,0,jd
+;         IDL> HELIO,JD,2,HRAD,HLONG,HLAT
+;                  ===> hrad = 1.6407 AU hlong = 124.3197 hlat = 1.7853
+;         For comparison, the JPL ephemeris gives
+;                       hrad = 1.6407 AU hlong = 124.2985 hlat = 1.7845
+;       (3) Find the heliocentric positions of Mars and Venus for every day in
+;           November 2000
+;        IDL> JDCNV, 2000, 11, 1, 0, jd    ;Julian date of November 1, 2000
+;        IDL> helio, jd+indgen(30), [4,2], hrad,hlong,hlat   ;Mars=4, Venus=2 
+;                   hrad, hlong, and hlat will be dimensioned [2,30]
+;                   first column contains Mars data, second column Venus
+; COMMON BLOCKS: 
+;       None 
+; ROUTINES USED: 
+;       CIRRANGE - force angle between 0 and 2*!PI
+; NOTES:
+;       (1) The calling sequence for this procedure was changed in August 2000
+;       (2) This program is based on the two-body model and thus neglects 
+;           interactions between the planets.   This is why the worst results
+;           are for Saturn.  Use the procedure JPLEPHINTERp for more accurate
+;           positions using the JPL ephemeris.   Also see 
+;           http://ssd.jpl.nasa.gov/cgi-bin/eph for a more accurate ephemeris 
+;           generator online.     
+;       (3) The coordinates are given for equinox 2000 and *not* the equinox
+;           of the supplied date(s)
+; MODIFICATION HISTORY: 
+;       R. Sterner.  20 Aug, 1986.
+;       Code cleaned up a bit      W. Landsman             December 1992
+;       Major rewrite, use modern orbital elements, vectorize, more accurate
+;         solution to Kepler's equation          W. Landsman August 2000
+;       Wasn't working for planet vectors        W. Landsman August 2000
+;       Work for more than 32767 positions       S. Leach Jan 2009
+;-
+ On_error,2
+ compile_opt idl2
+
+   if N_params() LT 3 then begin
+     print,'Syntax - Helio, jd, list, hrad, hlong, hlat, [/RADIAN]'
+     print,'     jd - Scalar or vector Julian date'
+     print,'     list - scalar or vector of planet numbers [1-9]'
+     print, $
+     '     hrad, hlong, hlat - output heliocentric distance, longitude latitude'    
+     return
+  endif
+
+; Mean orbital elements taken from http://ssd.jpl.nasa.gov/elem_planets.html
+; (1) semi-major axis in AU, (2) eccentricity, (3) inclination (degrees),
+; (4) longitude of the ascending node (degrees), (5) longitude of perihelion
+; (degrees) and (6) mean longitude (degrees)
+;Mercury 
+PD = [ [ 0.38709893d, 0.20563069, 7.00487,  48.33167,  77.45645, 252.25084 ], $
+;Venus  
+     [ 0.72333199d, 0.00677323, 3.39471,  76.68069, 131.53298, 181.97973 ], $ 
+;Earth
+     [ 1.00000011d, 0.01671022, 0.00005, -11.26064, 102.94719, 100.46435], $
+;Mars 
+     [ 1.52366231d, 0.09341233, 1.85061,  49.57854, 336.04084, 355.45332], $
+;Jupiter
+     [ 5.20336301d, 0.04839266, 1.30530, 100.55615,  14.75385,  34.40438], $ 
+;Saturn
+     [ 9.53707032d, 0.05415060, 2.48446, 113.71504,  92.43194,  49.94432], $
+;Uranus
+     [19.19126393d, 0.04716771, 0.76986,  74.22988, 170.96424, 313.23218], $ 
+;Neptune
+     [30.06896348d, 0.00858587, 1.76917, 131.72169,  44.97135, 304.88003], $
+;Pluto
+     [39.48168677d, 0.24880766,17.14175, 110.30347, 224.06676, 238.92881] ]
+
+; DPD gives the time rate of change of the above quantities ("/century)
+
+DPD = [  [0.00000066d, 0.00002527, -23.51, -446.30, 573.57, 538101628.29 ], $
+ [ 0.00000092d, -0.00004938, -2.86, -996.89, -108.80, 210664136.06], $
+ [-0.00000005d, -0.00003804, -46.94, -18228.25, 1198.28, 129597740.63], $ 
+ [-0.00007221d, 0.00011902, -25.47, -1020.19, 1560.78, 68905103.78 ], $
+ [0.00060737d, -0.00012880, -4.15, 1217.17, 839.93, 10925078.35 ], $
+ [-0.00301530d, -0.00036762, 6.11, -1591.05, -1948.89, 4401052.95],  $
+ [0.00152025d, -0.00019150, -2.09, -1681.40, 1312.56, 1542547.79 ], $
+ [-0.00125196d, 0.0000251, -3.64, -151.25, -844.43, 786449.21 ], $
+ [-0.00076912d, 0.00006465, 11.07, -37.33, -132.25, 522747.90] ] 
+
+ JD0 = 2451545.0d    ;Julian Date for Epoch 2000.0
+ radeg = 180/!DPI
+
+;-----------------  Days since Epoch  ---------------
+
+  T = (JD - JD0)/36525.0d          ;Time in centuries since 2000.0
+ 
+
+        ip = list-1
+        dpd[2:5,ip] = dpd[2:5,ip]/3600.0d       ;Convert arc seconds to degrees
+        ntime = N_elements(t)
+        nplanet = N_elements(list)
+        hrad = fltarr(nplanet,ntime) & hlong = hrad & hlat = hrad
+
+;-----------------  Loop over dates  --------------
+ 
+        for i =0L,ntime-1L do begin         ;SML made longword
+ 
+        pd1 = pd[*,ip] + dpd[*,ip]*T[i]
+        
+        a = pd1[0,*]                            ;semi-major axis
+        eccen = pd1[1,*]                        ;eccentricity
+        n = 0.9856076686/a/sqrt(a)/RADEG      ;mean motion, in radians/day
+        L =  pd1[5,*]/RADEG                     ;mean longitude
+        pi = pd1[4,*]/RADEG                  ;longitude of the perihelion
+        omega = pd1[3,*]/RADEG               ;longitude of the ascending node
+        inc = pd1[2,*]/RADEG          ;inclination in radians
+        
+        m = L - pi
+        cirrange,m,/RADIAN
+        e1 = m + (m + eccen*sin(m) - m)/(1 - eccen*cos(m) )
+        e = e1 + (m + eccen*sin(e1) - e1)/(1 - eccen*cos(e1) )
+        maxdif = max(abs(e-e1))
+        niter = 0
+        while (maxdif GT 1e-5) and (niter lt 10) do begin        
+             e1 = e
+             e = e1 + (m + eccen*sin(e1) - e1)/(1 - eccen*cos(e1) )
+             maxdif = max(abs(e-e1))
+             niter = niter+1
+        endwhile       
+        
+      
+        nu = 2*atan( sqrt( (1+eccen)/(1-eccen) )* tan(E/2))   ;true anomaly 
+
+        hrad[0,i] = reform( a*(1 - eccen*cos(e) ) )
+        hlong[0,i] = reform (nu + pi)                 
+         hlat[0,i] = reform( asin(sin(hlong[*,i] - omega)*sin(inc) ) )
+ endfor 
+
+       cirrange,hlong,/RADIAN
+       if not keyword_set(RADIAN) then begin 
+           hlong = hlong*RADEG
+           hlat = hlat*RADEG
+       endif
+       if N_elements(hrad) GT 1 then begin
+             hrad = reform(hrad,/over)
+             hlong = reform(hlong,/over)
+             hlat = reform(hlat,/over)
+       endif else begin
+             if N_elements(size(jd)) EQ 3 then begin      ;scalar?
+                     hrad = hrad[0]
+                     hlong = hlong[0]
+                     hlat = hlat[0]
+              endif
+       endelse 
+
+   return
+   end
diff --git a/Code/script_idl_mv/astrolib/helio_jd.pro b/Code/script_idl_mv/astrolib/helio_jd.pro
new file mode 100644
index 0000000000000000000000000000000000000000..af82fbc1998658526dcb6272291895ccb30b0f19
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/helio_jd.pro
@@ -0,0 +1,102 @@
+function helio_jd,date,ra,dec, B1950 = B1950, TIME_DIFF = time_diff
+;+
+; NAME:
+;      HELIO_JD
+; PURPOSE:
+;      Convert geocentric (reduced) Julian date to heliocentric Julian date
+; EXPLANATION:
+;      This procedure correct for the extra light travel time between the Earth 
+;      and the Sun.
+;
+;       An online calculator for this quantity is available at 
+;       http://www.physics.sfasu.edu/astro/javascript/hjd.html
+;
+;       Users requiring more precise calculations and documentation should 
+;       look at the IDL code available at 
+;       http://astroutils.astronomy.ohio-state.edu/time/
+; CALLING SEQUENCE:
+;       jdhelio = HELIO_JD( date, ra, dec, /B1950, /TIME_DIFF)
+;
+; INPUTS
+;       date - reduced Julian date (= JD - 2400000), scalar or vector, MUST
+;               be double precision
+;       ra,dec - scalars giving right ascension and declination in DEGREES
+;               Equinox is J2000 unless the /B1950 keyword is set
+;
+; OUTPUTS:
+;       jdhelio - heliocentric reduced Julian date.  If /TIME_DIFF is set, then
+;                 HELIO_JD() instead returns the time difference in seconds
+;                 between the geocentric and heliocentric Julian date.
+;                 
+; OPTIONAL INPUT KEYWORDS 
+;       /B1950 - if set, then input coordinates are assumed to be in equinox 
+;                B1950 coordinates.
+;       /TIME_DIFF - if set, then HELIO_JD() returns the time difference
+;                (heliocentric JD - geocentric JD ) in seconds 
+;
+; EXAMPLE:
+;       What is the heliocentric Julian date of an observation of V402 Cygni
+;       (J2000: RA = 20 9 7.8, Dec = 37 09 07) taken June 15, 1973 at 11:40 UT?
+;
+;       IDL> juldate, [1973,6,15,11,40], jd      ;Get geocentric Julian date
+;       IDL> hjd = helio_jd( jd, ten(20,9,7.8)*15., ten(37,9,7) )  
+;                                                            
+;       ==> hjd = 41848.9881
+;
+; Wayne Warren (Raytheon ITSS) has compared the results of HELIO_JD with the
+; FORTRAN subroutines in the STARLINK SLALIB library (see 
+; http://star-www.rl.ac.uk/).    
+;                                                  Time Diff (sec)
+;      Date               RA(2000)   Dec(2000)  STARLINK      IDL
+;
+; 1999-10-29T00:00:00.0  21 08 25.  -67 22 00.  -59.0        -59.0
+; 1999-10-29T00:00:00.0  02 56 33.4 +00 26 55.  474.1        474.1
+; 1940-12-11T06:55:00.0  07 34 41.9 -00 30 42.  366.3        370.2
+; 1992-02-29T03:15:56.2  12 56 27.4 +42 10 17.  350.8        350.9
+; 2000-03-01T10:26:31.8  14 28 36.7 -20 42 11.  243.7        243.7
+; 2100-02-26T09:18:24.2  08 26 51.7 +85 47 28.  104.0        108.8
+; PROCEDURES CALLED:
+;       bprecess, xyz, zparcheck
+;
+; REVISION HISTORY:
+;       Algorithm from the book Astronomical Photometry by Henden, p. 114
+;       Written,   W. Landsman       STX     June, 1989 
+;       Make J2000 default equinox, add B1950, /TIME_DIFF keywords, compute
+;       variation of the obliquity      W. Landsman   November 1999
+;-
+ On_error,2
+ If N_params() LT 3 then begin
+    print,'Syntax -   jdhelio = HELIO_JD( date, ra, dec, /B1950, /TIME_DIFF)'
+    print,'      date - reduced Julian date (= JD - 2400000)'
+    print,'      Ra and Dec must be in degrees'
+ endif
+
+;Because XYZ uses default B1950 coordinates, we'll convert everything to B1950
+
+ if not keyword_set(B1950) then bprecess,ra,dec,ra1,dec1 else begin
+        ra1 = ra
+        dec1 = dec
+ endelse
+ 
+ radeg = 180.0d/!DPI   
+ zparcheck,'HELIO_JD',date,1,[3,4,5],[0,1],'Reduced Julian Date'
+
+ delta_t = (double(date) - 33282.42345905d)/36525.0d
+ epsilon_sec = poly( delta_t, [44.836d, -46.8495, -0.00429, 0.00181])
+ epsilon = (23.433333d0 + epsilon_sec/3600.0d)/radeg
+ ra1 = ra1/radeg
+ dec1 = dec1/radeg
+
+ xyz, date, x, y, z
+
+;Find extra distance light must travel in AU, multiply by 1.49598e13 cm/AU,
+;and divide by the speed of light, and multiply by 86400 second/year
+
+ time = -499.00522d*( cos(dec1)*cos(ra1)*x + $
+                 (tan(epsilon)*sin(dec1) + cos(dec1)*sin(ra1))*y)
+
+ if keyword_set(TIME_DIFF) then return, time else $
+           
+       return, double(date) + time/86400.0d
+
+ end
diff --git a/Code/script_idl_mv/astrolib/helio_rv.pro b/Code/script_idl_mv/astrolib/helio_rv.pro
new file mode 100644
index 0000000000000000000000000000000000000000..cd6fe2c2e4b898e2110dd621a9170680853cd3f8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/helio_rv.pro
@@ -0,0 +1,145 @@
+function helio_rv,HJD,T,P,V0,K,e,omega
+;+ 
+; NAME:
+;    HELIO_RV
+;
+; PURPOSE:
+;     Return the heliocentric radial velocity of a spectroscopic binary
+;
+; EXPLANATION:
+;    This function will return the heliocentric radial velocity of a 
+;    spectroscopic binary star at a given heliocentric date 
+;    given its orbit.
+;
+; CALLING SEQUENCE:
+;
+;  Result = HELIO_RV ( JD ,T ,Period ,Gamma , K, [,e ,Omega ] )
+;
+; INPUT:
+;
+; JD            - Time of observation
+; T             - Time of periastron passage (max. +ve velocity
+;                 for circular orbits), same time system as JD
+; Period        - the period in same units as JD
+; Gamma         - systemic velocity
+; K             - velocity semi-amplitude in the same units as Gamma.
+; e             - eccentricity of the orbit, default is 0.
+; Omega         - longitude of periastron in degrees. Must be specified for
+;                 eccentric orbits.
+;
+; OUTPUT:
+;
+;  The predicted heliocentric radial velocity in the same units as Gamma
+;  for the date(s) specified by Reduced_HJD.
+;
+; RESTRICTIONS:
+;
+;  The user should ensure consistency with all time systems being
+;  used (i.e. JD and T should be in the same units and time system).
+;  Generally, users should reduce large time values by subtracting 
+;  a large constant offset, which may improve numerical accuracy.
+;  
+;  If using the the routines JULDATE and HELIO_JD, the reduced HJD
+;  time system must be used throughtout.
+;
+; EXAMPLES:
+;
+; Example 1
+;
+;  What was the heliocentric radial velocity of the primary component of HU Tau
+; at 1730 UT 25 Oct 1994?
+; 
+; IDL> juldate ,[94,10,25,17,30],JD                 ;Get Geocentric julian date
+; IDL> hjd = helio_jd(jd,ten(04,38,16)*15.,ten(20,41,05)) ; Convert to HJD
+; IDL> print, helio_rv(hjd,46487.5303D,2.0563056D,-6.0,59.3)
+;      -62.965569
+;
+; NB. 1. The routines JULDATE and HELIO_JD return a reduced HJD (HJD - 2400000)
+;        and so T and P must be specified in the same fashion.
+;     2. The user should be careful to use double precision format to specify
+;        T and P to sufficient precision where necessary. 
+; 
+; Example 2
+;
+;  Plot two cycles of an eccentric orbit, e=0.6, omega=45 for both
+;  components of a binary star
+;
+; IDL> phi=findgen(100)/50.0             ; Generates 100 phase points
+; IDL> plot, phi,helio_rv(phi,0,1,0,100,0.6,45),yrange=[-100,150]
+; IDL> oplot, phi,helio_rv(phi,0,1,0,50,0.6,45+180)
+; 
+; This illustrates both the use of arrays to perform multiple calculations
+; and generating radial velocities for a given phase by setting T=0 and P=1.
+; Note also that omega has been changed by 180 degrees for the orbit of the
+; second component  (the same 'trick' can be used for circular orbits).
+;
+; 
+; MODIFICATION HISTORY:
+;
+;  Written by:  Pierre Maxted CUOBS, October, 1994
+;
+;  Circular orbits handled by setting e=0 and omega=0 to allow
+;  binary orbits to be handled using omega and omega+180.
+;                                                      Pierre Maxted,Feb 95
+;  BUG - omega was altered by the routine - corrected Feb 95,Pierre Maxted
+;  Iteration for E changed to that  given by Reidel , Feb 95,Pierre Maxted
+;  /SINGLE keyword removed.                           May 96,Pierre Maxted
+;;       
+;  Removed limitation of time system on HJD, C. Markwardt, 2011-04-15
+;
+;  Change convergence test from relative to absolute precision on E
+;                                                     Pierre Maxted, Apr 12
+;-
+;
+; 
+  ON_ERROR, 2   ; Return to caller
+  compile_opt idl2
+;
+; Check suitable no. of parameters have been entered.
+;
+  if N_params() ne 5 and N_params() ne 7 then begin
+   print,'Syntax - Result = HELIO_RV (JD ,T ,Period ,Gamma, K)'
+   print,'         OR'
+   print,'         Result = HELIO_RV (JD ,T ,Period ,Gamma, K ,e ,Omega)'
+   print,'Further help - type doc_library,"HELIO_RV".'
+ endif else begin
+;
+; Circular orbits
+;
+  if ~keyword_set(omega) and ~keyword_set(e) then begin
+   e = 0.0
+   omega = 0.0
+  endif 
+;
+;
+; Calculate the approximate eccentric anomaly, E1, via the mean 
+; anomaly, M.
+; (from Heintz DW, "Double stars", Reidel, 1978)
+;
+ M=2.D*!dpi*( (HJD-T)/P MOD 1.)
+ E1=M + e*sin(M)  + ((e^2)*sin(2.0D*M)/2.0D)
+;
+; Now refine this estimate using  formulae given by Reidel.
+;
+ repeat begin
+  E0=E1 
+  M0 = E0 - e*sin(E0)
+  E1 = E0 + (M-M0)/(1.0 - e*cos(E0))
+ endrep until max(abs(E1-E0)) lt 1D-8
+;
+; Now calculate nu
+;
+ nu=2.0D*atan(sqrt((1.D0 + e)/(1.D - e))*tan(E1/2.0D))
+; nu=nu+((nu<0D)*(2D*!dpi))
+;
+; Can now calculate radial velocities
+;
+ rv = (K*(cos(nu+!dtor*omega) + (e*cos(!dtor*omega))))+V0
+ return ,rv
+;
+;
+ endelse
+;
+;
+ end
+
diff --git a/Code/script_idl_mv/astrolib/hermite.pro b/Code/script_idl_mv/astrolib/hermite.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9023f9205784d69363afc4d0abdd6cb08781ea23
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hermite.pro
@@ -0,0 +1,129 @@
+function hermite,xx,ff,x, FDERIV = fderiv
+;+
+; NAME:
+;       HERMITE
+; PURPOSE:
+;       To compute Hermite spline interpolation of a tabulated function.
+; EXPLANATION:
+;       Hermite interpolation computes the cubic polynomial that agrees with 
+;       the tabulated function and its derivative at the two nearest 
+;       tabulated points.   It may be preferable to Lagrangian interpolation 
+;       (QUADTERP) when either (1) the first derivatives are known, or (2)
+;       one desires continuity of the first derivative of the interpolated
+;       values.    HERMITE() will numerically compute the necessary
+;       derivatives, if they are not supplied.
+;       
+; CALLING SEQUENCE:
+;       F = HERMITE( XX, FF, X, [ FDERIV = ])
+;
+; INPUT PARAMETERS:
+;       XX - Vector giving tabulated X values of function to be interpolated
+;               Must be either monotonic increasing or decreasing   
+;       FF - Tabulated values of function, same number of elements as X
+;       X -  Scalar or vector giving the X values at which to interpolate
+;
+; OPTIONAL INPUT KEYWORD:
+;       FDERIV - function derivative values computed at XX.    If not supplied,
+;               then HERMITE() will compute the derivatives numerically.
+;               The FDERIV keyword is useful either when (1) the derivative
+;               values are (somehow) known to better accuracy than can be 
+;               computed numerically, or (2) when HERMITE() is called repeatedly
+;               with the same tabulated function, so that the derivatives
+;               need be computed only once.
+;
+; OUTPUT PARAMETER:
+;       F - Interpolated values of function, same number of points as X
+;
+; EXAMPLE:
+;       Interpolate the function 1/x at x = 0.45 using tabulated values
+;       with a spacing of 0.1
+;
+;       IDL> x = findgen(20)*0.1 + 0.1
+;       IDL> y = 1/x
+;       IDL> print,hermite(x,y,0.45)         
+;               This gives 2.2188 compared to the true value 1/0.45 = 2.2222
+;
+;       IDL> yprime = -1/x^2      ;But in this case we know the first derivatives
+;       IDL> print,hermite(x,y,0.45,fderiv = yprime)
+;             == 2.2219            ;and so can get a more accurate interpolation
+; NOTES:
+;       The algorithm here is based on the FORTRAN code discussed by 
+;       Hill, G. 1982, Publ Dom. Astrophys. Obs., 16, 67.   The original 
+;       FORTRAN source is U.S. Airforce. Surveys in Geophysics No 272. 
+;
+;       HERMITE() will return an error if one tries to interpolate any values 
+;       outside of the range of the input table XX
+; PROCEDURES CALLED:
+;       None
+; REVISION HISTORY:
+;       Written,    B. Dorman (GSFC) Oct 1993, revised April 1996
+;       Added FDERIV keyword,  W. Landsman (HSTX)  April 1996
+;       Test for out of range values  W. Landsman (HSTX) May 1996
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use VALUE_LOCATE instead of TABINV   W. Landsman   February 2001
+;-
+   On_error,2 
+
+   if N_Params() LT 3 then begin
+        print,'Syntax:  f = HERMITE( xx, ff, x, [FDERIV = ] )'
+        return,0
+   endif
+
+   n = N_elements(xx)           ;Number of knot points
+   m = N_elements(x)            ;Number of points at which to interpolate
+
+   l = value_locate(xx,x)       ;Integer index of interpolation points 
+
+   bad = where( (l LT 0) or (l EQ n-1), Nbad)
+        if Nbad GT 0 then message, 'ERROR - Valid interpolation range is ' + $
+        strtrim(xx[0],2) + ' to ' + strtrim(xx[n-1],2)
+
+   n1 = n - 1
+   n2 = n - 2
+
+   l1  = l + 1  
+   l2 = l1 + 1
+   lm1 = l - 1
+   h1 = double(1./(xx[l] - xx[l1]))
+   h2 = - h1
+
+; If derivatives were not supplied, then compute numeric derivatives at the 
+; two closest knot points
+
+ if N_elements(fderiv) NE 0 then begin
+        f2 = fderiv[l1]
+        f1 = fderiv[l]
+
+ endif else begin
+
+   f1 = dblarr(m)
+   f2 = dblarr(m)
+   for i = 0,m-1 do begin
+        if l[i] ne 0 then begin
+           if l[i] lt n2 then begin
+              f2[i] = (ff[l2[i]] - ff[l[i]])/(xx[l2[i]]-xx[l[i]])
+           endif else begin
+              f2[i] = (ff[n1] - ff[n2])/(xx[n1] - xx[n2])
+           endelse
+           f1[i] = ( ff[l1[i]] - ff[lm1[i]] )/( xx[l1[i]] - xx[lm1[i]] )
+        endif else begin
+           f1[i] = (ff[1] - ff[0])/(xx[1] - xx[0])
+           f2[i] = (ff[2] - ff[0])/(xx[2] - xx[0])
+        endelse
+   endfor
+ endelse
+       
+    xl1 = x - xx[l1]
+    xl  = x - xx[l]
+    s1  = xl1*h1
+    s2  = xl*h2
+
+; Now finally the Hermite interpolation formula
+
+    f   = (ff[l]*(1.-2.*h1*xl) + f1*xl)*s1*s1 + $                       
+                                        (ff[l1]*(1.-2.*h2*xl1) + f2*xl1)*s2*s2
+
+    if m eq 1 then return,f[0] else return,f
+
+    end
+
diff --git a/Code/script_idl_mv/astrolib/heuler.pro b/Code/script_idl_mv/astrolib/heuler.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2c7dd97342c82b04b2366308447150208ffa7b55
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/heuler.pro
@@ -0,0 +1,169 @@
+pro heuler,h_or_astr, Galactic = galactic, celestial = celestial, $
+                      ecliptic = ecliptic, alt_in = alt_in, alt_out = alt_out
+;+
+; NAME:
+;      HEULER 
+;
+; PURPOSE:
+;       Change the coordinate system of a FITS header or astrometry structure
+; EXPLANATION:
+;       Converts a FITS header or a astrometry structure containing WCS (world 
+;       coordinate system) information between celestial, ecliptic, and 
+;       Galactic coordinates
+;
+; CALLING SEQUENCE:
+;       HEULER, hdr, [/GALACTIC, /CELESTIAL, /ECLIPTIC, ALT_IN = , ALT_OUT=]  
+;                      or
+;       HEULER, astr, /GALACTIC, /CELESTIAL, /ECLIPTIC
+;
+; INPUT/OUTPUT PARAMETERS:
+;       hdr - FITS header (string array) containing WCS information
+;                            or
+;       Astr - Astrometry structure as extracted from a FITS header
+;             by extast.pro (See EXTAST for more info).
+;
+;       Header or astrometry structure will be modified by the program to 
+;       contain astrometry in the new coordinates system.
+; REQUIRED INPUT KEYWORDS:
+;       One of the following exclusive keywords is *required*
+;       /GALACTIC - Convert the header to Galactic coordinates
+;       /CELESTIAL - Convert the header to celestial (RA & Dec) coordinates
+;       /ECLIPTIC - Convert the header to ecliptic coordinates
+;
+; OPTIONAL INPUT KEYWORDS:
+;      The following two keywords apply if the FITS header contains multiple
+;      WCS keywords. See Section 3.3 of Greisen & Calabretta (2002, A&A, 395, 
+;      1061) for information about alternate astrometry keywords.
+;
+;      ALT_IN -  single character 'A' through 'Z' or ' ' specifying an 
+;          alternate astrometry system present in the input FITS header.  The 
+;          default isto use the primary astrometry or ALT = ' '.   If /ALT_IN 
+;          is set, then this is equivalent to ALT_IN = 'A'.
+;      ALT_OUT - single character specifying the alternate WCS keywords 
+;          to write the *output* astrometry.    If not specified, then ALT_OUT
+;          is set equal to ALT_IN.
+; RESTRICTIONS:
+;       Currently assumes that celestial and ecliptic coordinates are in
+;       J2000.   Use HPRECESS if this is not the case.
+;
+;       ST Guide Star (DSS) image headers are first converted to a standard
+;       tangent projection, prior to the coordinate conversion
+; METHOD:
+;       The algorithm used is described in Section 2.7 of Calabretta & Greisen
+;       (2002, A&A, 395, 1077).    The CRVAL coordinates are transformed
+;       directly using EULER.    The new LONPOLE and LATPOLE values are then
+;       determined by transforming the pole of the new system to the old, and
+;       converted to native coordinates using WCS_ROTATE. 
+; EXAMPLE:
+;       A FITS header, hdr, has a standard tangent projection WCS information.
+;       Add an alternate 'G' Galactic projection.    Note that the original
+;       WCS information will be left unchanged 
+;
+;       IDL> heuler, hdr, /Galactic, alt='G'
+; PROCEDURES USED:
+;       EULER, EXTAST, GSSS_STDAST, PUTAST, SXADDHIST, WCS_ROTATE
+; REVISION HISTORY:
+;       Written    W. Landsman                  June 2003
+;       Use PV2 tag in astrometry structure rather than PROJP1 W. L. May 2004
+;       Use double precision to compute new North pole  W.L. Aug 2005
+;       Check for non-standard CTYPE value W.L. Sep 2012
+;-
+compile_opt idl2
+if N_params() LT 1 then begin
+     print,'Syntax - HEULER, hdr, /GALACTIC, /CELESTIAL, /ECLIPTIC, ALT_IN=,'
+     return
+endif
+sz = size(h_or_astr,/str)
+if (sz.type_name EQ 'STRING') && (sz.N_dimensions EQ 1) then begin
+    if N_elements(alt_out) EQ 0 then if N_elements(alt_in) NE 0 then $
+       alt_out = alt_in
+    EXTAST,h_or_astr,astr,status, alt = alt_in 
+    if status LT 0 then message, $
+       'ERROR - No astrometry present in supplied FITS header' else $
+    if status EQ 4 then begin
+         GSSS_STDAST, h_or_astr
+         EXTAST, h_or_astr, astr, status, alt = alt_in
+    endif
+    
+    ctype1 = sxpar(h_or_astr,'CTYPE1')         ;Check if non-standard CTYPE was used
+   if strmid(astr.ctype[0],5,3) NE strmid(ctype1,5,3) then $
+         putast,h_or_astr,astr
+    
+endif else if sz.type_name EQ 'STRUCT' then astr = h_or_astr else message, $
+   'ERROR - First parameter must be a FITS header or astrometry structure'
+ map_types=['DEF','AZP','SZP','TAN','STG','SIN','ARC','ZPN','ZEA','AIR','CYP',$
+            'CEA','CAR','MER','SFL','PAR','MOL','AIT','COP','COE','COD','COO',$
+            'BON','PCO','GLS','TSC','CSC','QSC']
+
+ctype1 = astr.ctype[0] 
+ctype2 = astr.ctype[1]
+; Use Table 13 of Calbretta & Greisen to determine default values of theta0
+coord = strmid(ctype1,0,4)
+proj = strmid(ctype1,5,3)
+imap = where(map_types EQ proj, N_imap)
+if N_imap EQ 0 then message,'ERROR - Unrecognized map projection of ' + proj
+imap = imap[0]
+if imap LE 9 then theta0 = 90 else $
+if (imap GE 18) && (imap LE 21) then theta0 = astr.pv2[0] else theta0 = 0
+          
+if keyword_set(GALACTIC) then begin
+    case coord of
+    'RA--': select= 1
+    'ELON': select = 5
+    'GLON': begin 
+            message,/INF,'FITS header is already in Galactic: nothing changed'
+            return
+            end
+    end
+    strput,ctype1,'GLON'
+    strput,ctype2,'GLAT'
+    conv = 'Galactic'
+endif else if keyword_set(CELESTIAL) then begin 
+    case coord of
+    'RA--': begin 
+            message,/INF,'FITS header is already in Celestial: nothing changed'
+            return
+            end
+    'ELON': select = 4
+    'GLON': select = 2
+     end
+     strput,ctype1,'RA--'
+     strput,ctype2,'DEC-'
+     conv = 'Celestial'
+endif else if keyword_set(ECLIPTIC) then begin
+    case coord of
+    'RA--': select =3  
+    'ELON': begin 
+            message,/INF,'FITS header is already in Celestial: nothing changed'
+            return
+            end
+    'GLON': select = 6
+     endcase
+     strput,ctype1,'ELON'
+     strput,ctype2,'ELAT'
+     conv = 'Ecliptic'
+endif else message, $
+   'Either /CELESTIAL, /GALACTIC or /ECLIPTIC keyword must be specified'
+
+
+ EULER,astr.crval[0],astr.crval[1],ncrval1,ncrval2,select
+
+;Find new LONPOLE and LATPOLE values
+ if select mod 2 eq 0 then iselect = select-1 else iselect = select+1
+ EULER,0.0d,90.0d,lon1,lat1,iselect
+ WCS_ROTATE,lon1,lat1,lonpole, latpole, astr.crval,LONGPOLE = astr.longpole, $
+             LATPOLE = astr.latpole, THETA0 = theta0
+
+;Update astrometry structure
+ astr.ctype = [ctype1,ctype2]
+ astr.longpole = lonpole
+ astr.latpole = latpole
+ astr.crval = [ncrval1, ncrval2]
+
+ if sz.type_name EQ 'STRING' then begin        ;Update FITS header? 
+          putast, h_or_astr, astr, alt = alt_out 
+          sxaddhist, 'HEULER: ' + STRMID(systime(),4,20) +  $
+                     ' Converted to ' + conv + ' coordinates', h_or_astr
+ endif else h_or_astr = astr
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/hextract.pro b/Code/script_idl_mv/astrolib/hextract.pro
new file mode 100644
index 0000000000000000000000000000000000000000..111147a15bae4f46e49dfa8874bf39fbd5a2f1bb
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hextract.pro
@@ -0,0 +1,205 @@
+pro hextract, oldim, oldhd, newim, newhd, x0, x1, y0, y1, SILENT = silent, $
+    ERRMSG = errmsg,ALT = alt  
+;+
+; NAME:
+;       HEXTRACT
+; PURPOSE:
+;       Extract a subimage from an array and update astrometry in FITS header
+; EXPLANATION:
+;       Extract a subimage from an array and create a new FITS header with
+;       updated astrometry for the subarray
+; CALLING SEQUENCE:
+;       HEXTRACT, Oldim, Oldhd, [ Newim, Newhd, x0, x1, y0, y1, /SILENT ]
+;               or
+;       HEXTRACT, Oldim, Oldhd, [x0, x1, y0, y1, /SILENT, ERRMSG =  ]    
+;
+; INPUTS:
+;       Oldim - the original image array
+;       Oldhd - the original image header
+;
+; OPTIONAL INPUTS:
+;       x0, x1, y0, y1 - respectively, first and last X pixel, and first and
+;       last Y pixel to be extracted from the original image, integer scalars.
+;       HEXTRACT will convert these values to long integers. 
+;       If omitted,  HEXTRACT will prompt for these parameters
+;
+; OPTIONAL OUTPUTS:
+;       Newim - the new subarray extracted from the original image 
+;       Newhd - header for newim containing updated astrometry info
+;               If output parameters are not supplied or set equal to
+;               -1, then the HEXTRACT will modify the input parameters 
+;               OLDIM and OLDHD to contain the subarray and updated header.
+;
+; OPTIONAL INPUT KEYWORD:
+;      ALT - Single character 'A' through 'Z' or ' ' specifying which astrometry
+;          system to modify in the FITS header.    The default is to use the
+;          primary astrometry or ALT = ' '.    See Greisen and Calabretta (2002)
+;          for information about alternate astrometry keywords.
+;      /SILENT - If set and non-zero, then a message describing the extraction
+;               is not printed at the terminal.   This message can also be 
+;               suppressed by setting !QUIET.
+; OPTIONAL KEYWORD OUTPUT:
+;       ERRMSG - If this keyword is supplied, then any error mesasges will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+;
+; PROCEDURE:
+;       The FITS header parameters NAXIS1, NAXIS2, CRPIX1, and CRPIX2 are
+;       updated for the extracted image.
+;
+; EXAMPLE:  
+;       Read an image from a FITS file 'IMAGE', extract a 512 x 512 subimage 
+;       with the same origin, and write to a new FITS file 'IMAGENEW'
+;
+;       IDL> im = READFITS( 'IMAGE', hdr )      ;Read FITS files into IDL arrays
+;       IDL> hextract, im, h, 0, 511, 0, 511    ;Extract 512 x 512 subimage
+;       IDL> writefits, 'IMAGENEW', im ,h       ;Write subimage to a FITS file
+;
+; PROCEDURES CALLED
+;       CHECK_FITS, STRN(), SXPAR(), SXADDPAR, SXADDHIST
+; MODIFICATION HISTORY:
+;       Written, Aug. 1986 W. Landsman, STX Corp.
+;       Use astrometry structure,   W. Landsman      Jan, 1994
+;       Minor fix if bad Y range supplied   W. Landsman    Feb, 1996
+;       Added /SILENT keyword              W. Landsman     March, 1997
+;       Added ERRMSG keyword    W. Landsman   May 2000
+;       Work for dimensions larger than 32767   W.L., M.Symeonidis Mar 2007
+;       Added ALT keyword  W.L. April 2007
+;       Use V6.0 notation W.L.  October 2012
+;       Fix for SFL projection W.L.   September 2015
+;- 
+ On_error, 2
+ compile_opt idl2
+ npar = N_params()
+
+ if (npar EQ 3) || (npar LT 2) then begin       ;Check # of parameters
+    print,'Syntax - HEXTRACT, oldim, oldhd, [ newim, newhd, x0, x1, y0, y1]'
+    print,'   or    HEXTRACT, oldim, oldhd, x0, x1, y0, y1, [/SILENT, ERRMSG=]'
+    return
+ endif
+ 
+ save_err = arg_present(errmsg)      ;Does user want to return error messages?
+;                                    Check for valid 2-D image & header
+  check_FITS, oldim, oldhd, dimen, /NOTYPE, ERRMSG = errmsg
+  if errmsg NE '' then begin
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+  endif
+
+  if N_elements(dimen) NE 2 then begin 
+           errmsg = 'Input image array must be 2-dimensional'
+           if ~save_err then message,'ERROR - ' + errmsg,/CON
+           return
+  endif
+
+  xsize = dimen[0]  &  ysize = dimen[1]
+
+
+ if ( npar LT 4 ) then Update = 1 else Update = 0     ;Update old array?
+
+ if ( npar EQ 6 ) then begin                 ;Alternative calling sequence ?
+
+     if ( N_elements(newim) EQ 1 ) && ( N_elements(newhd) EQ 1 ) && $
+        ( N_elements(x0) EQ 1 ) && ( N_elements(x1) EQ 1 ) then begin
+              y0 = x0   &  y1 = x1
+              x0 = newim   &   x1 = newhd
+              Update = 1
+      endif 
+
+ endif
+
+ RDX: 
+ if ( npar LE 5 )  then begin
+
+      message, /INF, $ 
+           'Original array size is ' + strn(xsize) + ' by ' + strn(ysize) 
+      x0 = 0l & x1 = 0l
+      read,'% HEXTRACT: Enter first and last X pixel to be extracted: ',x0,x1
+
+ endif
+
+ if ( x1 LT x0 ) || ( x0 LT 0 ) || ( x1 GE xsize ) then begin
+
+     message,'ERROR - Illegal pixel range: X direction',  /CON
+     print, ' '
+     message, /INF,   $
+     ' Legal Range is 0 < First Pixel < Last Pixel < ' + strn(xsize-1)
+     if update then npar = npar < 2 else npar = npar < 4
+     goto, RDX 
+
+ endif
+
+ RDY: if (~update && ( npar LE 7 )) || (update && (npar LT 6) ) then $ 
+    read,'% HEXTRACT: Enter first and last Y pixel to be extracted: ',y0,y1
+
+ if ( y1 LT y0 ) || ( y0 LT 0 ) || ( y1 GE ysize ) then begin
+
+     message,'ERROR - Illegal pixel range: Y direction', /CON
+     message, /INF,     $ 
+      'Legal Range is 0 < First Pixel < Last Pixel < ' + strn(ysize-1)
+     if update then npar = npar < 4 else npar = npar < 6 
+     goto, RDY
+
+ endif
+
+ x0 = long(x0) & x1 = long(x1)
+ y0 = long(y0) & y1 = long(y1)                                          
+
+ naxis1 = x1 - x0 + 1 
+ naxis2 = y1 - y0 + 1   ;New dimensions
+
+ if ~keyword_set(SILENT) then message, /INF,        $
+      'Now extracting a '+ strn(naxis1) + ' by ' + strn(naxis2) + ' subarray'
+
+  if Update then oldim = oldim[ x0:x1,y0:y1 ]        $
+            else newim = oldim[ x0:x1,y0:y1 ]
+
+ newhd = oldhd
+ sxaddpar, newhd, 'NAXIS1', naxis1                                   
+ sxaddpar, newhd, 'NAXIS2', naxis2
+ label = 'HEXTRACT: ' + systime(0)
+
+ hist = [label,'Original image size was '+ strn(xsize) + ' by ' + strn(ysize), $
+         'Extracted Image: [' + strn(x0) + ':'+ strn(x1) +  $
+         ',' + strn(y0) + ':'+ strn(y1) + ']'  ]
+
+ sxaddhist, hist, newhd
+
+
+;GSSS image uses CNPIX instead of CRPIX
+   cnpix1 = sxpar( oldhd, 'CNPIX1', COUNT = Ncnpix1)
+         if ( Ncnpix1 EQ 1 ) then begin   ;Shift position of reference pixel
+
+                sxaddpar, newhd, 'CNPIX1', cnpix1+x0
+                cnpix2 = sxpar( oldhd, 'CNPIX2' )
+                sxaddpar, newhd, 'CNPIX2', cnpix2+y0
+        endif
+
+; Update astrometry info if it exists
+
+  if N_elements(alt) EQ 0 then alt = ''
+  extast, newhd, astr, noparams, ALT = alt
+
+  if noparams GE 0 then begin
+;Handle SFL projection separately in case it was originally GLS  
+  if astr.projection EQ 'SFL' then begin     
+       crpix = sxpar(newhd,'CRPIX*')       
+       sxaddpar,newhd,'CRPIX1'+alt,crpix[0]-x0
+       sxaddpar,newhd,'CRPIX2'+alt,crpix[1]-y0
+  endif else begin     
+       sxaddpar, newhd, 'CRPIX1'+alt, astr.crpix[0]-x0
+       sxaddpar, newhd, 'CRPIX2'+alt, astr.crpix[1]-y0
+  endelse 
+
+ endif 
+ if Update then begin
+
+      oldhd = newhd
+      newim = x0 & newhd = x1
+      x0 = y0 & x1 = y1
+
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/hgrep.pro b/Code/script_idl_mv/astrolib/hgrep.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b998f5d75fc17269df7565ae50f34a8b2aecbc02
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hgrep.pro
@@ -0,0 +1,65 @@
+pro hgrep, header, substring, keepcase=keepcase, linenum=linenum
+
+;+
+; NAME:
+;     HGREP
+;
+; PURPOSE:
+;       Find a substring in a FITS header (or any other string array)
+;
+; CALLING SEQUENCE:
+;       HGREP, header, substring, [/KEEPCASE, /LINENUM ]
+;
+; INPUTS: 
+;       header -  FITS header or other string array
+;       substring - scalar string to find in header; if a numeric value is 
+;                 supplied, it will be converted to type string
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /KEEPCASE: if set, then look for an exact match of the input substring 
+;                 Default is to ignore case .
+;       /LINENUM: if set, prints line number of header in which
+;                substring appears 
+;
+; OUTPUTS:
+;       None, results are printed to screen
+;
+; EXAMPLE: 
+;       Find every place in a FITS header that the word 'aperture'
+;       appears in lower case letters and print the element number 
+;       of the header array:
+;       
+;       IDL> hgrep, header, 'aperture', /keepcase, /linenum
+;
+; HISTORY: 
+;       Written, Wayne Landsman (Raytheon ITSS)      August 1998
+;       Adapted from STIS version by Phil Plait/ ACC November 14, 1997
+;       Remove trailing spaces if a non-string is supplied W. Landsman Jun 2002
+;-
+
+   if (N_params() LT 2) then begin
+      print,'Syntax - HGREP, header, substring, [/KEEPCASE, /LINENUM ]'
+      return
+   endif
+
+   if N_elements(header) eq 0 then begin
+      print,'first parameter not defined. Returning...'
+      return
+   endif
+   hh = strtrim(header,2)
+   if size(substring,/tname) NE 'STRING' then substring = strtrim(substring,2)
+
+   if keyword_set(keepcase) then $
+         flag = strpos(hh,substring) $
+   else  flag = strpos(strlowcase(hh),strlowcase(substring))
+     
+
+   g = where(flag NE -1, Ng)
+   if Ng GT 0 then $
+       if keyword_set(linenum) then $
+           for i = 0, Ng-1 do print, string(g[i],f='(i4)') + ': ' + hh[g[i]] $
+       else $
+           for i = 0, Ng-1 do print,hh[g[i]] 
+                   
+   return
+   end
diff --git a/Code/script_idl_mv/astrolib/histogauss.pro b/Code/script_idl_mv/astrolib/histogauss.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1c7401b43f06bd66c8d8152bac4862b297cdd061
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/histogauss.pro
@@ -0,0 +1,196 @@
+PRO HISTOGAUSS,SAMPLE,A,XX,YY,GX,GY,NOPLOT=noplot,NOFIT=SIMPL, $
+               CHARSIZE=CSIZE, FONT=font, _EXTRA = _extra,Window=window
+;
+;+
+;NAME:
+;       HISTOGAUSS
+;
+; PURPOSE:
+;       Histograms data and overlays it with a Gaussian. Draws the mean, sigma,
+;       and number of points on the plot.
+;
+; CALLING SEQUENCE:
+;       HISTOGAUSS, Sample, A, [XX, YY, GX, GY, /NOPLOT, /NOFIT, FONT=, 
+;                               CHARSIZE = ]
+;
+; INPUT:
+;       SAMPLE = Vector to be histogrammed
+;
+; OUTPUT ARGUMENTS:
+;       A = coefficients of the Gaussian fit: Height, mean, sigma
+;               A[0]= the height of the Gaussian
+;               A[1]= the mean
+;               A[2]= the standard deviation
+;               A[3]= the half-width of the 95% conf. interval of the standard
+;                     mean
+;               A[4]= 1/(N-1)*total( (y-mean)/sigma)^2 ) = a measure of 
+;                       normality
+;
+;       Below: superceded. The formula is not entirely reliable.
+;       A[4]= measure of the normality of the distribution. =1.0, perfectly
+;       normal. If no more than a few hundred points are input, there are
+;       formulae for the 90 and 95% confidence intervals of this quantity:
+;       M=ALOG10(N-1) ; N = number of points
+;       T90=ABS(.6376-1.1535*M+.1266*M^2)  ; = 90% confidence interval
+;       IF N LT 50 THEN T95=ABS(-1.9065-2.5465*M+.5652*M^2) $
+;                  ELSE T95=ABS( 0.7824-1.1021*M+.1021*M^2)   ;95% conf.
+;       (From Martinez, J. and Iglewicz, I., 1981, Biometrika, 68, 331-333.)
+;
+;       XX = the X coordinates of the histogram bins (CENTER)
+;       YY = the Y coordinates of the histogram bins
+;       GX = the X coordinates of the Gaussian fit
+;       GY = the Y coordinates of the Gaussian fit
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /NOPLOT - If set, nothing is drawn
+;       /FITIT   If set, a Gaussian is actually fitted to the distribution.
+;               By default, a Gaussian with the same mean and sigma is drawn; 
+;               the height is the only free parameter.
+;       CHARSIZE Size of the characters in the annotation. Default = 0.82.
+;       FONT - scalar font graphics keyword (-1,0 or 1) for text
+;       /WINDOW - set to plot to a resizeable graphics window
+;       _EXTRA - Any value keywords to the cgPLOT command (e.g. XTITLE) may also
+;               be passed to HISTOGAUSS
+; SUBROUTINE CALLS:
+;       BIWEIGHT_MEAN, which determines the mean and std. dev.
+;       AUTOHIST, which draws the histogram
+;       GAUSSFIT() (IDL Library) which does just that
+;
+; REVISION HISTORY:
+;       Written, H. Freudenreich, STX, 12/89
+;       More quantities returned in A, 2/94, HF
+;       Added NOPLOT keyword and print if Gaussian, 3/94
+;       Stopped printing confidence limits on normality 3/31/94 HF
+;       Added CHARSIZE keyword, changed annotation format, 8/94 HF
+;       Simplified calculation of Gaussian height, 5/95 HF
+;       Convert to V5.0, use T_CVF instead of STUDENT_T, GAUSSFIT instead of
+;           FITAGAUSS  W. Landsman April 2002 
+;       Correct call to T_CVF for calculation of A[3], 95% confidence interval
+;                P. Broos/W. Landsman   July 2003
+;       Allow FONT keyword to be passed.  T. Robishaw Apr. 2006
+;       Use Coyote Graphics for plotting W.L. Mar 2011
+;       Better formatting of text output W.L. May 2012
+;-
+
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 2 then begin
+    print,'Syntax - HISTOGAUSS, Sample, A, [XX, YY, GX, GY,  '
+    print,'                   /NOPLOT, /NOFIT, CHARSIZE=, Plotting keywords...]'
+    return
+ endif
+
+ if (N_elements(FONT) eq 0) then font = !p.font
+ DATA = SAMPLE
+ N = N_ELEMENTS(DATA)
+
+; First make sure that not everything is in the same bin. If most
+; data = 0, reject zeroes. If they = some other value, complain and
+; give up.
+ A = 0.
+ DATA = DATA[SORT(DATA)]  
+ N3 = 0.75*N & N1 = 0.25*N
+IF DATA[N3] EQ DATA[N1] THEN BEGIN
+   IF DATA[N/2] EQ 0. THEN BEGIN
+      Q = WHERE(DATA NE 0.,NON0)
+      IF (N-NON0) GT 15 THEN BEGIN
+         message,/INF,'Suppressing Zeroes!'
+         DATA=DATA[Q]
+         N=NON0
+      ENDIF ELSE BEGIN 
+         message,' Too Few Non-0 Values!',/CON
+         RETURN
+      ENDELSE
+      Q=0
+   ENDIF ELSE BEGIN
+      message,/CON,' Too Many Identical Values: ' + strtrim(DATA[N/2],2)
+      RETURN
+   ENDELSE
+ENDIF
+
+A = FLTARR(5) 
+
+; The "mean":
+A[1] = BIWEIGHT_MEAN(DATA,S)
+; The "standard deviation":
+A[2] = S  
+; The 95% confidence interval:
+M=.7*(N-1)  ;appropriate for a biweighted mean
+CL = 0.95
+two_tail_area = 1 - CL
+A[3]=ABS( T_CVF(1 - (two_tail_area)/2.0,M) )*S/sqrt(n)
+
+; A measure of the Gaussianness:
+A[4]=TOTAL((DATA-A[1])^2)/((N-1)*A[2]^2)
+;Q=WHERE( ABS(DATA-A(1)) LT (5.*S), COUNT )   ; "robust I" unreliable
+;ROB_I=TOTAL((DATA(Q)-A(1))^2)/((COUNT-1)*A(2)^2)
+;PRINT,A(4),ROB_I
+
+; Set bounds on the data:
+ U1 = A[1] - 5.*A[2]
+ U2 = A[1] + 5.*A[2]
+ Q = WHERE(DATA LT U1, NQ)
+ IF NQ GT 0 THEN DATA[Q] = U1
+ Q = WHERE(DATA GT U2, NQ)
+ IF NQ GT 0 THEN DATA[Q] = U2
+
+; Draw the histogram
+ font_in = !P.FONT & !P.FONT=font
+ AUTOHIST,DATA,X,Y,XX,YY,NOPLOT = noplot, _EXTRA = _extra,Window=window
+ !P.FONT=font_in
+ 
+; Check for error in AUTOHIST:
+
+M = N_ELEMENTS(X)
+MM = N_ELEMENTS(XX)
+IF M LT 2 THEN BEGIN
+   XX=0. & YY=0. & A=0.
+   RETURN ; (AUTOHIST has already screamed)
+ENDIF
+
+; Calculate the height of the Gaussian:
+Z = EXP(-.5*(X-A[1])^2/A[2]^2 )
+XQ1 = A[1] - 1.3*A[2]
+XQ2 = A[1] + 1.3*A[2]
+QQ = WHERE((X GT XQ1) AND (X LT XQ2),COUNT)
+IF COUNT GT 0 THEN HYTE = MEDIAN(Y[QQ]/Z[QQ],/EVEN) ELSE BEGIN
+   print,'HISTOGAUSS: Distribution too Weird!'
+   HYTE = MAX(SMOOTH(Y,5))
+ENDELSE
+A[0]=HYTE
+
+; Fit a Gaussian, unless the /NOFIT qualifier is present
+IF ~KEYWORD_SET(SIMPL) THEN BEGIN
+   PARM=A[0:2]
+   YFIT = GAUSSFIT(XX,YY,PARM,NTERMS=3)
+   A[0:2]=PARM
+ENDIF
+
+; It the /NOPLOT qualifier is present, we're done.
+IF KEYWORD_SET(NOPLOT) THEN RETURN
+
+; Overplot the Gaussian, 
+ DU = (U2-U1)/199.
+ GX = U1 + FINDGEN(200)*DU
+
+ Z = (GX-A[1])/A[2]
+ GY = A[0]*EXP(-Z^2/2. )
+ cgplot,/over,GX,GY,window=window
+
+; Annotate. 
+MEANST = STRING(A[1],'(G12.5)')
+SIGST = STRING(A[2],'(G12.5)')
+NUM = N_ELEMENTS(DATA)
+NUMST =STRING(N,'(I6)')
+
+IF KEYWORD_SET(CSIZE) THEN ANNOT=CSIZE ELSE ANNOT=.82
+ if FONT EQ 0 then LABL = '#, !Mm!X, !Ms!X=' else  LABL='#, !7l!6, !7r!3='
+ LABL = LABL +numst+','+meanst+','+sigst 
+X1 = !x.crange[0] + annot*(!x.crange[1]-!x.crange[0])/20./0.82 
+y1 = !y.crange[1] - annot*(!y.crange[1]-!y.crange[0])/23./0.82 
+cgtext, X1, Y1, LABL, CHARSIZE=ANNOT, FONT=font,window=window
+
+RETURN
+END
+
diff --git a/Code/script_idl_mv/astrolib/hor2eq.pro b/Code/script_idl_mv/astrolib/hor2eq.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e7b086ad15b73f174d7b37a4ae63530b72f1c1ef
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hor2eq.pro
@@ -0,0 +1,256 @@
+;+
+; NAME:
+;   HOR2EQ
+;
+; PURPOSE:
+;    Converts local horizon coords (alt-az) of something to equatorial (ra-dec).
+;
+; EXPLANATION:
+;     This is a nice code to calculate equatorial (ra,dec) coordinates from
+;     horizon (alt,az) coords.    It is typically accurate to about 1 arcsecond
+;     or better (I have checked the output against the publicly available XEPHEM
+;     software). It performs precession, nutation, aberration, and refraction
+;     corrections.  The perhaps best thing about it is that it can take arrays
+;     as inputs, in all variables and keywords EXCEPT Lat, lon, and Altitude
+;    (the code assumes these aren't changing), and uses vector arithmetic in
+;     every calculation except when calculating the precession matrices.
+;
+; CALLING SEQUENCE:
+;
+;    HOR2EQ, alt, az, jd, ra, dec, [ha, LAT= , LON= , /WS, OBSNAME= , $
+;                       /B1950 , PRECESS_= 0, NUTATE_= 0, REFRACT_= 0, $
+;                       ABERRATION_= 0, ALTITUDE= , /VERBOSE, _EXTRA= ]
+;
+;
+; INPUT VARIABLES
+;       alt  : altitude (in degrees) [scalar or vector]
+;       az   : azimuth angle (in degrees, measured EAST from NORTH, but see
+;              keyword WS below.) [scalar or vector]
+;       JD   : Julian Date [scalar or vector], double precision
+
+;       Note: if RA and DEC are arrays, then alt and az will also be arrays.
+;             If RA and DEC are arrays, JD may be a scalar OR an array of
+;              the same dimensionality.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       lat   : north geodetic latitude of location in degrees
+;       lon   : EAST longitude of location in degrees
+;               (Specify west longitude with a negative sign.)
+;       /WS   : Set this to get the azimuth measured westward from south
+;               (not East of North).
+;       obsname   : Set this to a valid observatory name to be used by the
+;               astrolib OBSERVATORY procedure, which will return the latitude
+;               and longitude to be used by this program.
+;       /B1950  : Set this if your ra and dec are specified in B1950,
+;               FK4 coordinates (instead of J2000, FK5)
+;       precess_ : Set this to 1 to force precession [default], 0 for no
+;                 precession.
+;       nutate_  : Set this to 1 to force nutation [default], 0 for no nutation.
+;       aberration_ : Set this to 1 to force aberration correction [default],
+;                 0 for no correction.
+;       refract_  : Set to 1 to force refraction correction [default], 0 for
+;                   no correction.
+;       altitude: The altitude of the observing location, in meters. [default=0].
+;       /verbose: Set this for verbose output.  The default is verbose=0.
+;   _extra: This is for setting TEMPERATURE or PRESSURE explicitly, which are
+;           used by CO_REFRACT to calculate the refraction effect of the
+;           atmosphere. If you don't set these, the program will make an
+;           intelligent guess as to what they are (taking into account your
+;            altitude).  See CO_REFRACT for more details.
+;
+; OUTPUT VARIABLES
+;       ra   : Right Ascension of object  (J2000) in degrees (FK5); scalar or
+;              vector.
+;       dec  : Declination of object (J2000) in degrees (FK5), scalar or vector.
+;       ha   : hour angle (in degrees) (optional)
+;
+; DEPENDENCIES:
+;       NUTATE, PRECESS, ADSTRING(), SUNPOS, OBSERVATORY (from the astrolib)
+;       CO_NUTATE, CO_ABERRATION, CO_REFRACT, HADEC2ALTAZ
+;
+; BASIC STEPS
+;   Precess Ra-Dec to current equinox.
+;   Nutation Correction to Ra-Dec
+;   Aberration correction to Ra-Dec
+;   Calculate Local Mean Sidereal Time
+;   Calculate Local Apparent Sidereal Time
+;   Calculate Hour Angle
+;   Do Spherical Trig to find Apparent Alt-Az
+;   Apply refraction correction to find observed Alt.
+;
+;CORRECTIONS I DO NOT MAKE:
+;   *  Deflection of Light by the sun due to GR. (typically milliarcseconds,
+;        can be arcseconds within one degree of the sun)
+;   *  The Effect of Annual Parallax (typically < 1 arcsecond)
+;   *  and more (see below)
+;
+; TO DO
+;    * Better Refraction Correction.  Need to put in wavelength dependence,
+;       and integrate through the atmosphere.
+;    * Topocentric Parallax Correction (will take into account elevation of
+;          the observatory)
+;    * Proper Motion (but this will require crazy lookup tables or something).
+;    * Difference between UTC and UT1 in determining LAST -- is this important?
+;    * Effect of Annual Parallax (is this the same as topocentric Parallax?)
+;    * Polar Motion
+;    * Better connection to Julian Date Calculator.
+;
+; EXAMPLE:
+;
+;   You are at Kitt Peak National Observatory, looking at a star at azimuth
+;   angle 264d 55m 06s and elevation 37d 54m 41s (in the visible).  Today is
+;   Dec 25, 2041 and the local time is 10 PM precisely.  What is the ra and dec
+;   (J2000) of the star you're looking at?   The temperature here is about 0
+;   Celsius, and the pressure is 781 millibars.    The Julian date for this
+;   time is 2466879.7083333
+;
+;  IDL> hor2eq, ten(37,54,41), ten(264,55,06), 2466879.7083333d, ra, dec, $
+;           /verb, obs='kpno', pres=781.0, temp=273.0
+;
+; The program produces this output (because the VERBOSE keyword was set):
+;
+; Latitude = +31 57 48.0   Longitude = *** 36  0.0   ; longitude prints weirdly b/c of negative input to ADSTRING!!
+; Julian Date =  2466879.708333
+; Az, El =  17 39 40.4  +37 54 41.0   (Observer Coords)
+; Az, El =  17 39 40.4  +37 53 39.6   (Apparent Coords)
+; LMST = +03 53 54.1
+; LAST = +03 53 53.6
+; Hour Angle = +03 38 30.1  (hh:mm:ss)
+; Ra, Dec:  00 15 23.5  +15 25  1.9   (Apparent Coords)
+; Ra, Dec:  00 15 24.2  +15 25  0.1   (J2041.9841)
+; Ra, Dec:  00 13 14.1  +15 11  0.3   (J2000)
+;
+; The star is therefore Algenib!  Compare the derived Ra, Dec with what XEPHEM
+; got:
+; Ra, Dec:      00 13 14.2  +15 11  1.0   (J2000)
+;
+; AUTHOR:
+;   Chris O'Dell
+;   Assistant Professor of Atmospheric Science
+;   Colorado State University
+;   Email: odell@atmos.colostate.edu
+; REVISION HISTORY:
+;     Made all integers type LONG  W. Landsman   September 2007
+;     Fixed for case of scalar Julian date but vector positions W L June 2009
+;-
+
+pro hor2eq, alt, az, jd, ra, dec, ha, lat=lat, lon=lon, WS=WS, obsname=obsname,$
+           B1950 = B1950, verbose=verbose, precess_=precess_, nutate_=nutate_, $
+           refract_ = refract_, aberration_ = aberration_, altitude=altitude, $
+           _extra = _extra
+
+ On_error,2
+ compile_opt idl2
+ if N_params() LT 4 then begin
+   print,'Syntax - HOR2EQ, alt, az, jd, ra, dec, [ha, LAT= , LON= , /WS, '
+   print,'        OBSNAME= ,/B1950 , PRECESS_= 0, NUTATE_= 0, REFRACT_= 0, '
+   print,'        ABERRATION_= 0, ALTITUDE= , /VERBOSE, TEMPERATURE=, PRESSURE='
+   return
+ endif
+;*******************************************************************************
+; INITIALIZE STUFF
+
+; If no lat or lng entered, use Pine Bluff Observatory values
+if n_elements(lat) eq 0 then lat = 43.0783d
+; (btw, this is the declination of the zenith)
+if n_elements(lon) eq 0 then lon = -89.865d
+
+if keyword_set(obsname) then begin
+        ;override lat,lon if observatory name has been specified
+        Observatory, obsname, obs
+        lat = obs.latitude
+        lon = -1*obs.longitude ; minus sign is becase OBSERVATORY uses west
+;                              ;longitude as positive.
+        altitude = obs.altitude
+endif
+
+if n_elements(precess_) eq 0 then precess_ = 1
+if n_elements(nutate_) eq 0 then nutate_ = 1
+if n_elements(aberration_) eq 0 then aberration_ = 1
+if n_elements(refract_) eq 0 then refract_ = 1
+v = keyword_set(verbose)
+
+; conversion factors
+d2r = !dpi/180.
+h2d = 15.
+
+alt_ = alt   ;do this so we don't change ra, dec arrays.
+az_ = az
+
+if v then print, 'Latitude = ', adstring(lat), '   Longitude = ', adstring(lon)
+if v then print, 'Julian Date = ', jd, format='(A,f15.6)'
+if v then print,'Az, El = ', adstring(az_, alt_), '   (Observer Coords)'
+
+;*******************************************************************************************
+; Make Correction for ATMOSPHERIC REFRACTION
+; (use this for visible and radio wavelengths; author is unsure about other wavelengths)
+if refract_ then alt_ = co_refract(alt_, altitude=altitude, _extra=_extra)
+if v then print,'Az, El = ', adstring(az_, alt_), '   (Apparent Coords)'
+
+if keyword_set(WS) then az_ = az_ - 180.
+
+co_nutate, jd, 45.,45., dra1, ddec1, eps=eps, d_psi=d_psi
+
+;******************************************************************************
+;Calculate LOCAL APPARENT SIDEREAL TIME
+; first get local mean sidereal time (lmst)
+; get LST (in hours) - note:this is indep of tzone since giving jd
+ct2lst, lmst, lon, 0, jd
+lmst = lmst*h2d ; convert LMST to degrees (btw, this is the RA of the zenith)
+; calculate local APPARENT sidereal time (last)
+last = lmst + d_psi *cos(eps)/3600. ; add correction in degrees
+if v then print, 'LMST = ', adstring(lmst/15.)
+if v then print, 'LAST = ', adstring(last/15.)
+
+;****************************************************************************
+; Now do the spherical trig to get APPARENT Hour Angle [degrees], and
+; declination [degrees].
+altaz2hadec, alt_, az_, lat, ha, dec
+
+; Find Right Ascension (in degrees, from 0 to 360.)
+ ra = (last - ha + 360.) mod 360.
+
+if v then print, 'Hour Angle = ', adstring(ha/15.), '  (hh:mm:ss)'
+if v then print, 'Ra, Dec: ', adstring(ra,dec), '   (Apparent Coords)'
+
+
+;*****************************************************************************
+; calculate NUTATION and ABERRATION Corrections to Ra-Dec
+co_nutate, jd, ra, dec, dra1, ddec1, eps=eps, d_psi=d_psi
+co_aberration, jd, ra, dec, dra2, ddec2, eps=eps
+
+;******************************************************************************
+; Make Nutation and Aberration Corrections (if wanted)
+ra = ra - (dra1*nutate_ + dra2*aberration_)/3600.
+dec = dec - (ddec1*nutate_ + ddec2*aberration_)/3600.
+J_now = (JD - 2451545.)/365.25 + 2000.0 ; compute current equinox
+Njd = N_elements(J_now)
+Npos = N_elements(ra)
+if (Njd EQ 1) and (Npos GT 1) then J_now = replicate(J_now, Npos) 
+if v then print, 'Ra, Dec: ', adstring(ra,dec), '   (J'+ $
+           strcompress(string(J_now),/rem)+')'
+
+;*****************************************************************************
+; PRECESS coordinates to current date
+; (uses astro lib procedure PRECESS.pro)
+
+if precess_ then begin
+        if keyword_set(B1950) then begin
+                for i=0, Npos-1 do begin
+                        ra_i = ra[i] & dec_i = dec[i]
+                        precess, ra_i, dec_i, J_now[i], 1950.0, /FK4
+                        ra[i] = ra_i & dec[i] = dec_i
+                endfor
+        endif else begin
+                for i=0, Npos-1 do begin
+                        ra_i = ra[i] & dec_i = dec[i]
+                        precess, ra_i, dec_i, J_now[i], 2000.0
+                        ra[i] = ra_i & dec[i] = dec_i
+                endfor
+        endelse
+endif
+if keyword_set(B1950) then s_now='   (J1950)' else s_now='   (J2000)'
+if v then print, 'Ra, Dec: ', adstring(ra,dec), s_now
+
+Return
+END
diff --git a/Code/script_idl_mv/astrolib/host_to_ieee.pro b/Code/script_idl_mv/astrolib/host_to_ieee.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ff17e0063c5069b29ba79033217573ee0a7c6658
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/host_to_ieee.pro
@@ -0,0 +1,98 @@
+pro host_to_ieee, data, IDLTYPE = idltype
+;+
+; NAME:
+;     HOST_TO_IEEE
+; PURPOSE:
+;     Translate an IDL variable from host to IEEE representation 
+; EXPLANATION:
+;     The variable is converted from the format used by the host architecture
+;     into IEEE-754 representation ("big endian" as used, e.g., in FITS data ).
+;
+;     Duplicates most of the functionality of the SWAP_ENDIAN_INPLACE procedure
+;     with the addition of the IDLTYPE keyword.
+; CALLING SEQUENCE:
+;     HOST_TO_IEEE, data, [ IDLTYPE = ]
+;
+; INPUT-OUTPUT PARAMETERS:
+;     data - any IDL variable, scalar or vector.   It will be modified by
+;             HOST_TO_IEEE to convert from host to IEEE representation.  Byte 
+;             and string variables are returned by HOST_TO_IEEE unchanged
+;
+; OPTIONAL KEYWORD INPUTS:
+;     IDLTYPE - scalar integer (1-15) specifying the IDL datatype according
+;               to the code given by the SIZE function.      This keyword
+;               will usually be used when supplying a byte array that needs
+;               to be interpreted as another data type (e.g. FLOAT).
+;
+; EXAMPLE:
+;     Suppose FITARR is a 2880 element byte array to be converted to a FITS
+;     record and interpreted a FLOAT data.
+;
+;       IDL> host_to_ieee, FITARR, IDLTYPE = 4
+;
+; METHOD:
+;     The BYTEORDER procedure is called with the appropriate keywords
+;
+; MODIFICATION HISTORY:
+;      Adapted from CONV_UNIX_VAX, W. Landsman   Hughes/STX    January, 1992
+;      Added new integer datatypes  C. Markwardt/W. Landsman  July 2000
+;      Use /SWAP_IF_LITTLE_ENDIAN keyword for 64bit types W. Landsman Feb 2003
+;      Do not use XDR keywords to BYTEORDER for much improved speed
+;                               W. Landsman   April 2006
+;-
+ On_error,2 
+
+ if N_params() EQ 0 then begin
+    print,'Syntax - HOST_TO_IEEE, data, [IDLTYPE = ]'
+    return
+ endif  
+
+ npts = N_elements( data )
+ if npts EQ 0 then $
+     message,'ERROR - IDL data variable (first parameter) not defined'
+
+ if N_elements( idltype) EQ 0 then idltype = size(data,/type)
+
+ case idltype of
+
+      1: return                             ;byte
+
+      2: byteorder, data, /SSWAP,/SWAP_IF_LITTLE            ;integer
+
+      3: byteorder, data, /LSWAP,/SWAP_IF_LITTLE            ;long
+
+      4: byteorder, data, /LSWAP, /SWAP_IF_LITTLE           ;float
+
+      5: byteorder,data,/L64SWAP, /SWAP_IF_LITTLE              ;double
+ 
+      6: byteorder, data, /LSWAP, /SWAP_IF_LITTLE
+     
+      7: return                             ;string
+
+      8: BEGIN                              ;structure
+
+        Ntag = N_tags( data )
+
+        for t=0,Ntag-1 do  begin
+          temp = data.(t)
+          host_to_ieee, temp
+          data.(t) = temp
+        endfor 
+       END
+
+     9: byteorder, data, /L64SWAP, /SWAP_IF_LITTLE
+ 
+     12: byteorder, data, /SSWAP, /SWAP_IF_LITTLE
+
+     13: byteorder, data, /LSWAP, /SWAP_IF_LITTLE
+
+     14: byteorder, data, /L64swap, /SWAP_IF_LITTLE
+
+     15: byteorder, data, /L64swap, /SWAP_IF_LITTLE
+
+     else: message,'Unrecognized datatype ' + strtrim(idltype,2)
+
+ ENDCASE
+
+ return
+ end 
diff --git a/Code/script_idl_mv/astrolib/hprecess.pro b/Code/script_idl_mv/astrolib/hprecess.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6dc544c0a29ee63a8c3e1df9adaf224d4bfcce58
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hprecess.pro
@@ -0,0 +1,134 @@
+PRO HPRECESS, HDR, YEARF                                      
+;+
+; NAME:
+;       HPRECESS
+; PURPOSE:
+;       Precess the astrometry in a FITS header to a new equinox
+;
+; CALLING SEQUENCE:
+;       HPRECESS, HDR, [ yearf ]      
+;
+; INPUT-OUTPUT:
+;       HDR - FITS Header, must contain the CRVAL astrometry keywords,
+;               and either an EPOCH or EQUINOX keyword.
+;               HDR will be modified to contain the precessed astrometry
+;
+; OPTIONAL INPUT:
+;       YEARF - Scalar, giving the year of the new (Final) equinox.
+;               If not supplied, user will be prompted for this value.
+;
+; METHOD:
+;       The CRVAL and CD (or CROTA) keywords are extracted from the header 
+;       and precessed to the new equinox.  The EPOCH or EQUINOX keyword in 
+;       the header is  updated.  A HISTORY record is added
+;
+; RESTRICTIONS:
+;       The FK5 reference frame is assumed for both equinoxes.
+;
+; PROCEDURES USED:
+;       EXTAST, GET_EQUINOX(), SXADDPAR, SXADDHIST, PRECESS, PRECESS_CD
+;       PUTAST, ZPARCHECK
+; REVISION HISTORY:                                               
+;       Written  W. Landsman        STX              July, 1988
+;       CD matrix precessed -                        February, 1989
+;       Update EQUINOX keyword when CROTA2 present   November, 1992
+;       Recognize a GSSS header                      June, 1994
+;       Additional Noparams value recognize for storing CDs.  RSH, 6 Apr 95
+;       Understand reversed X,Y (X-Dec, Y-RA) axes,   W. Landsman  October 1998
+;       Correct algorithm when CROTA2 is in header W. Landsman  April 2006
+;       Correct sign error introduced April 2006, include CDELT values
+;         when computing rotation of pole   W. Landsman July 2007
+;       Call hprecess/jprecess for 1950<>2000   W. L. Aug 2009
+;       Work when ASTR.LONGPOLE NE 180.0 W.L.  Aug 2014
+;-     
+ On_error, 2   
+ compile_opt idl2
+ 
+ if N_params() EQ 0 then begin       
+        print,'Syntax - HPRECESS, hdr, [ yearf]'
+        return   
+ endif else zparcheck, 'HPRECESS', hdr, 1, 7, 1, 'FITS Header Array'
+
+ yeari = GET_EQUINOX( hdr, code)    ;YEAR of Initial equinox
+ if code EQ -1 then $     
+       message,'Header does not contain EPOCH or EQUINOX keyword'
+
+ if N_params() LT 2 then begin 
+   print, 'HPRECESS: Astrometry in supplied header is in equinox ', $
+   strtrim(yeari,2)      
+   read, 'Enter year of new equinox: ',yearf 
+ endif                                             
+
+ if yeari EQ yearf then $                                           
+    message,'Astrometry in header is already in Equinox ' + strtrim(YEARF,2)
+
+ extast, hdr, astr, noparams        ;Extract astrometry from header
+
+ if noparams EQ -1 THEN $
+    message,'FITS Header does not contain CRVAL keywords'
+        
+ if strmid(astr.ctype[0],5,3) EQ 'GSS' then begin
+        gsss_stdast, hdr
+        extast, hdr, astr, noparams
+ endif
+ 
+ ctype1 = sxpar(hdr,'CTYPE1')         ;Check if non-standard CTYPE was used
+ if strmid(astr.ctype[0],5,3) NE strmid(ctype1,5,3) then putast,hdr,astr
+        
+ cd = astr.cd
+ crval = astr.crval
+ cdelt = astr.cdelt
+ if N_elements(CDELT) GE 2 then if (cdelt[0] NE 1.0) then begin
+        cd[0,0] = cd[0,0]*cdelt[0] & cd[0,1] =  cd[0,1]*cdelt[0]
+        cd[1,1] = cd[1,1]*cdelt[1] & cd[1,0] =  cd[1,0]*cdelt[1]
+ endif
+
+ coord = strmid(astr.ctype,0,4)    ;Test if RA and Dec reversed in 'CTYPE*'
+ reverse = ((coord[0] EQ 'DEC-') and (coord[1] EQ 'RA--'))
+ if reverse then crval = rotate(crval,2)
+ a = crval[0] & d = crval[1]
+ if (yeari EQ 2000.) and (yearf EQ 1950.) then begin 
+       bprecess,a,d,ai,di
+       sxaddpar,hdr,'RADECSYS','FK4'
+       a = ai & d = di
+ endif else if (yeari EQ 1950) && (yearf EQ 2000) then begin 
+       jprecess,a,d,ai,di
+       sxaddpar,hdr,'RADECSYS','FK5'
+       a = ai & d = di
+       
+ endif else precess, a, d, yeari, yearf                    ;Precess the CRVAL coordinates
+ 
+ precess_cd, cd, yeari, yearf, crval,[ a, d]    ;Precess the CD matrix
+ if N_elements(CDELT) GE 2 then if (cdelt[0] NE 1.0) then begin
+        cd[0,0] = cd[0,0]/cdelt[0] & cd[0,1] =  cd[0,1]/cdelt[0]
+        cd[1,1] = cd[1,1]/cdelt[1] & cd[1,0] =  cd[1,0]/cdelt[1]
+ endif
+
+ 
+ if reverse then begin                          ;Update CRVAL values
+    sxaddpar, hdr, 'CRVAL1', double(d)             
+    sxaddpar, hdr, 'CRVAL2', double(a)    
+ endif else begin
+    sxaddpar, hdr, 'CRVAL1', double(a)            
+    sxaddpar, hdr, 'CRVAL2', double(d)    
+ endelse
+
+ if (noparams EQ 3) || (noparams EQ 2)  then begin
+ 
+       putast, hdr, cd, EQUINOX = float(yearf)          ;Update CD values
+ endif else begin      ;or CROTA2 value
+       astr.cd= cd
+       getrot, astr, ROT  
+       if astr.longpole NE 180.0 then rot -= 180.0d - astr.longpole                             
+       sxaddpar,hdr, 'EQUINOX', yearf, ' Equinox of Ref. Coord.', 'HISTORY'
+       sxaddpar, hdr, 'CROTA2', rot
+ endelse        
+
+
+
+ sxaddhist, 'HPRECESS: ' + STRMID(systime(),4,20) +  $ 
+   ' Astrometry Precessed From Year' + string(form='(f7.1)',float(yeari)),hdr
+ message, 'Header astrometry has been precessed to ' + strtrim(yearf,2),/INF
+
+ return
+ end                                                        
diff --git a/Code/script_idl_mv/astrolib/hprint.pro b/Code/script_idl_mv/astrolib/hprint.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6b587e47d847780fceb2ab2b914996d0847d2daf
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hprint.pro
@@ -0,0 +1,100 @@
+pro hprint, h, firstline
+;+
+; NAME:
+;       HPRINT
+; PURPOSE:
+;       Display a FITS header (or other string array) 
+; EXPLANATION:
+;       On a GUI terminal, the string array is displayed using XDISPSTR.    
+;       If printing at a non-GUI terminal, the string array is  printed 1 line 
+;       at a  time, to make sure that each element of the string array is 
+;       displayed on a separate line. 
+;
+; CALLING SEQUENCE:
+;       HPRINT, h, [ firstline ]
+;
+; INPUTS:
+;       H - FITS header (or any other string array).
+;
+; OPTIONAL INPUT:
+;       FIRSTLINE - scalar integer specifying the first line to begin 
+;               displaying.   The default is FIRSTLINE = 1, i.e. display 
+;               all the lines.     If Firstline is negative, then the first
+;               line to be printed is counted backward from the last line.
+;
+; NOTES:
+;       When displaying at the terminal, HPRINT has the following differences 
+;       from the intrinsic PRINT procedure
+;
+;       (1) Arrays are printed one line at a time to avoid a space between 80
+;               character lines
+;       (2) Lines are trimmed with STRTRIM before being printed to speed up 
+;               display
+;       (3) The /more option is used for output. 
+;
+; EXAMPLE:
+;       Read the header from a FITS file named 'test.fits' and display it at the
+;       terminal beginning with line 50
+;
+;       IDL> h = headfits( 'test.fits')         ;Read FITS header
+;       IDL> hprint, h, 50                      ;Display starting at line 50
+;
+;       To print the last 25 lines of the header
+;
+;       IDL> hprint, h, -25
+;
+; REVISION HISTORY:
+;       Written W. Landsman                     July, 1990
+;       Added test for user quit                July, 1991
+;       Added optional FIRSTLINE parameter      November, 1992
+;       Modified for when STDOUT is not a TTY W. Landsman  September 1995
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Fixed printing in IDLDE, C. Gehman      August, 1998
+;       Skip PRINTF if IDL in demo mode  W. Landsman  October 2004
+;       Fixed bug on non-terminals, William Thompson, 18-Oct-2004
+;       Assume since V5.4 Use BREAK instead of GOTO  W. Landsman Apr 2006
+;       Call XDISPSTR on a GUI terminal  W. Landsman Jun 2006
+;-
+  On_error,2                        ;Return to Caller
+  compile_opt idl2
+
+  if N_params() EQ 0 then begin
+       print,'Syntax - HPRINT, h, [ firstline ]'
+       return
+  endif
+
+  n = N_elements(h)
+  if ( n EQ 0 ) then    $               ;Make sure input array is defined
+     message,'String array (first parameter) not defined'
+
+   if N_elements( firstline ) EQ 0 then firstline = 1
+  if ( firstline[0] LT 0 ) then firstline = ( n + firstline[0]) > 1 < n  $
+                                else firstline = firstline[0] > 1 < n
+
+  stdout = fstat(-1)
+  if stdout.isagui then begin 
+           xdispstr,h,tit='HPRINT',top_line=firstline-1
+           return
+  endif	   
+  if lmgr(/demo) then begin      ;in demo mode?
+      for i=firstline-1, n-1 do print,h[i]
+      return
+  endif
+
+
+; Now print the array one line at a time
+  if (stdout.isatty) then begin  ;Open with /MORE if a TTY
+      
+      openw, outunit, filepath(/TERMINAL), /MORE, /GET_LUN  
+      for i = firstline-1, n-1 do begin
+
+     printf, outunit, strtrim( h[i] )
+     if !ERR EQ 1 then BREAK     ;User entered "Q" in response to /more
+
+  endfor
+  free_lun, outunit
+
+ endif else printf,-1,strtrim(h[firstline-1:*]), FORMAT='(A)'
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/hrebin.pro b/Code/script_idl_mv/astrolib/hrebin.pro
new file mode 100644
index 0000000000000000000000000000000000000000..86b3ec25de2726d8fc12d453bf615d3ce28a4a15
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hrebin.pro
@@ -0,0 +1,277 @@
+ pro hrebin, oldim, oldhd, newim, newhd, newx, newy, TOTAL = total, $
+            SAMPLE=sample, OUTSIZE = outsize, ERRMSG = errmsg, ALT=alt
+;+
+; NAME:
+;    HREBIN
+; PURPOSE:
+;    Expand or contract a FITS image using (F)REBIN and update the header 
+; EXPLANATION:
+;    If the output size is an exact multiple of the input size then REBIN is 
+;    used, else FREBIN is used.   User can either overwrite the input array,
+;    or write to new variables.     By default, the counts/pixel is preserved,
+;    though one can preserve the total counts or surface flux by setting /TOTAL
+;
+; CALLING SEQUENCE:
+;    HREBIN, oldhd        ;Special calling sequence to just update header
+;    HREBIN, oldim, oldhd, [ newim, newhd, newx, newy, OUTSIZE = ,/SAMPLE, 
+;                            ERRMSG =  ]
+;
+; INPUTS:
+;    OLDIM - the original image array
+;    OLDHD - the original image FITS header, string array
+;
+; OPTIONAL INPUTS:
+;    NEWX - size of the new image in the X direction, integer scalar
+;    NEWY - size of the new image in the Y direction, integer scalar
+;            HREBIN will prompt for NEWX and NEWY if not supplied
+;
+; OPTIONAL OUTPUTS:
+;    NEWIM - the image after expansion or contraction with REBIN
+;    NEWHD - header for newim containing updated astrometry info
+;            If output parameters are not supplied, the program will modify
+;            the input parameters OLDIM and OLDHD to contain the new array and 
+;            updated header.
+;
+; OPTIONAL INPUT KEYWORDS:
+;    ALT - Single character 'A' through 'Z' or ' ' specifying which astrometry
+;          system to modify in the FITS header.    The default is to use the
+;          primary astrometry of ALT = ' '.    See Greisen and Calabretta (2002)
+;          for information about alternate astrometry keywords.
+;
+;    OUTSIZE - Two element integer vector which can be used instead of the
+;             NEWX and NEWY parameters to specify the output image dimensions
+;
+;    /SAMPLE - Expansion or contraction is done using REBIN which uses 
+;              bilinear interpolation when magnifying and boxaveraging when 
+;              minifying.   If the SAMPLE keyword is supplied and non-zero, 
+;              then nearest neighbor sampling is used in both cases.   Keyword
+;              has no effect when output size is not a multiple of input size.
+;
+;    /TOTAL - If set then the output image will have the same total number of counts
+;             as the input image.     Because HREBIN also updates the astrometry,
+;             use of the TOTAL keyword also preserves counts per surface area, e.g.
+;             counts/(arc sec)@    
+;
+; OPTIONAL KEYWORD OUTPUT:
+;       ERRMSG - If this keyword is supplied, then any error mesasges will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+; PROCEDURE:
+;     The parameters BSCALE, NAXIS1, NAXIS2, CRPIX1, and CRPIX2 and the CD 
+;     (or CDELT) parameters are updated for the new FITS header.
+;
+; EXAMPLE:
+;     Compress a 2048 x 2048 image array IM, with FITS header HDR, to a 
+;     724 x 724 array.   Overwrite the input variables with the compressed 
+;     image and header.
+;
+;     IDL> hrebin, im, hdr, OUT = [724, 724]
+;
+; PROCEDURES USED:
+;     CHECK_FITS, EXTAST, FREBIN, GSSS_STDAST, STRN(), SXPAR(), SXADDHIST, 
+;     SXADDPAR, ZPARCHECK
+;
+; MODIFICATION HISTORY:
+;     Written, December 1990  W. Landsman, ST System Corp.
+;     Update CD1_1 keywords   W. Landsman   November 1992
+;     Check for a GSSS header   W. Landsman  June 1994
+;     Update BSCALE even if no astrometry present   W. Landsman  May 1997
+;     Converted to IDL V5.0   W. Landsman   September 1997
+;     Use FREBIN to accept sizes that are not a integer multiple of the original
+;         size    W. Landsman     August 1998
+;     Correct for "edge" effects when expanding with REBIN W. Landsman Apr. 1999
+;     Fixed initialization of header only call broken in Apr 98 change May. 1999
+;     Remove reference to obsolete !ERR  W. Landsman   February 2000
+;     Use double precision formatting for CD matrix W. Landsman April 2000
+;     Recognize PC00n00m astrometry format   W. Landsman   December 2001
+;     Correct astrometry for integral contraction W. Landsman  April 2002
+;     Fix output astrometry for non-equal plate scales for PC matrix or
+;     CROTA2 keyword, added ALT keyword.   W. Landsman May 2005
+;     Update distortion parameters if present  W. Landsman August 2007
+;     Don't update BSCALE/BZERO for unsigned integer W.Landsman Mar 2008
+;     Use post-V6.0 notation   W. Landsman  Nov 2011
+;     Write CRPIX values as double precision if necessary W. Landsman Oct. 2012
+;     Always call FREBIN, added TOTAL keyword W. Landsman Nov 2015
+;- 
+ On_error,2
+ compile_opt idl2
+
+ npar = N_params()      ;Check # of parameters
+ if (npar EQ 3) || (npar EQ 5) || (npar EQ 0) then begin
+     print,'Syntax - HREBIN, oldim, oldhd,[ newim, newhd, OUTSIZE=, ' + $
+                           '/SAMPLE, ERRMSG= ]'
+     return
+ endif
+
+ if ~keyword_set(SAMPLE) then sample = 0
+ save_err = arg_present(errmsg)      ;Does user want to return error messages?
+
+; If only 1 parameter is supplied, then assume it is a FITS header
+
+ if ( npar EQ 1 ) then begin           
+
+        zparcheck, 'HREBIN', oldim, 1, 7, 1, 'Image header'
+        oldhd = oldim
+        xsize = sxpar( oldhd,'NAXIS1' )
+        ysize = sxpar( oldhd,'NAXIS2' )
+
+ endif else begin 
+
+     check_FITS, oldim, oldhd, dimen, /NOTYPE, ERRMSG = errmsg
+     if errmsg NE '' then begin
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+     endif
+     if N_elements(dimen) NE 2 then begin 
+           errmsg = 'Input image array must be 2-dimensional'
+           if ~save_err then message,'ERROR - ' + errmsg,/CON
+           return
+     endif
+      xsize = dimen[0]  &  ysize = dimen[1]
+ endelse
+ tname = size(oldim,/tname)
+ 
+ if ( npar LT 6 ) then begin
+
+    if ( N_elements(OUTSIZE) NE 2 ) then begin
+    tit = !MSG_PREFIX + 'HREBIN: '
+    print, tit, 'Original array size is '+ strn(xsize) + ' by ' + strn(ysize)
+    read, tit + 'Enter size of new image in the X direction: ',newx
+    read, tit + 'Enter size of new image in the Y direction: ',newy
+  endif else begin
+     newx = outsize[0]
+     newy = outsize[1]
+   endelse 
+ 
+ endif
+
+;  Modified Nov 2015 to alway call FREBIN.     FREBIN() will call the IDL REBIN()
+;  function if we are changing dimensions by an exact multiple.
+
+ if npar GT 1 then begin
+ 
+   if npar GT 2 then newim = frebin( oldim, newx, newy,total=total) $
+                else oldim = frebin( oldim, newx, newy,total=total) 
+   endif
+
+
+ if ( sample GT 0 ) then type = ' Nearest Neighbor Approximation' else begin
+          if ( newx LT xsize ) then type = ' Box Averaging' else $
+                                    type = ' Bilinear Interpolation'
+ endelse
+
+ newhd = oldhd
+ sxaddpar, newhd, 'NAXIS1', fix(newx)
+ sxaddpar, newhd, 'NAXIS2', fix(newy)
+ label = 'HREBIN: '+ strmid( systime(),4,20 )
+ sxaddpar,newhd,'history',label + ' Original Image Size Was '+ $
+         strn(xsize) +' by ' +  strn(ysize) 
+ if ( npar GT 1 ) then sxaddpar,newhd,'history',label+type
+ 
+ xratio = float(newx) / xsize   ;Expansion or contraction in X
+ yratio = float(newy) / ysize   ;Expansion or contraction in Y
+ lambda = yratio/xratio         ;Measures change in aspect ratio.
+ pix_ratio = xratio*yratio      ;Ratio of pixel areas
+
+
+; Update astrometry info if it exists
+
+ extast, newhd, astr, noparams, ALT = alt
+ if noparams GE 0 then begin
+
+ if strmid(astr.ctype[0],5,3) EQ 'GSS' then begin
+        gsss_stdast, newhd
+        extast, newhd, astr, noparams
+ endif
+
+
+; Correct the position of the reference pixel.   Note that CRPIX values are
+; given in FORTRAN (first pixel is (1,1)) convention
+
+ crpix = astr.crpix
+
+; When expanding with REBIN with bilinear interpolation (SAMPLE = 0), edge
+; effects are introduced, which require a different calculation of the updated
+; CRPIX1 and CRPIX2 values.
+
+exact = (~(xsize mod newx) || ~(newx mod xsize)) &&  $
+        (~(ysize mod newy) || ~(newy mod ysize)) 
+ if (exact) && (~keyword_set(SAMPLE)) && (xratio GT 1) then $
+      crpix1 = (crpix[0]-1.0)*xratio + 1.0                  else $
+      crpix1 = (crpix[0]-0.5)*xratio + 0.5
+
+ if (exact) && (~keyword_set(SAMPLE)) && (yratio GT 1) then $
+      crpix2 = (crpix[1]-1.0)*yratio + 1.0                  else $
+      crpix2 = (crpix[1]-0.5)*yratio + 0.5
+
+ if N_elements(alt) EQ 0 then alt = ''
+ sxaddpar, newhd, 'CRPIX1' + alt, crpix1
+ sxaddpar, newhd, 'CRPIX2' + alt, crpix2
+ 
+  if tag_exist(astr,'DISTORT') then begin
+         distort = astr.distort
+	 message,'Updating SIP distortion parameters',/INF
+         update_distort,distort, [1./xratio,0],[1./yratio,0]
+	 astr.distort= distort
+	 add_distort, newhd, astr
+   endif	 
+
+
+
+; Scale either the CDELT parameters or the CD1_1 parameters.
+
+ if (noparams NE 2) then begin 
+
+    cdelt = astr.cdelt
+    sxaddpar, newhd, 'CDELT1' + alt, CDELT[0]/xratio
+    sxaddpar, newhd, 'CDELT2' + alt, CDELT[1]/yratio
+; Adjust the PC matrix if aspect ratio has changed.   See equation 187 in 
+; Calabretta & Greisen (2002)
+    if lambda NE 1.0 then begin
+        cd = astr.cd
+	if noparams EQ 1 then begin
+;Can no longer use the simple CROTA2 convention, change to PC keywords
+	 sxaddpar,newhd,'PC1_1'+alt, cd[0,0]
+	 sxaddpar, newhd,'PC2_2'+alt, cd[1,1]
+     sxdelpar, newhd, ['CROTA2','CROTA1']
+        endif	
+        sxaddpar, newhd, 'PC1_2'+alt, cd[0,1]/lambda
+        sxaddpar, newhd, 'PC2_1'+alt, cd[1,0]*lambda
+   endif	
+
+ endif else begin     ;CDn_m Matrix format
+
+    cd = astr.cd
+    sxaddpar, newhd, 'CD1_1'+alt, cd[0,0]/xratio
+    sxaddpar, newhd, 'CD1_2'+alt, cd[0,1]/yratio
+    sxaddpar, newhd, 'CD2_1'+alt, cd[1,0]/xratio
+    sxaddpar, newhd, 'CD2_2'+alt, cd[1,1]/yratio
+
+ endelse
+ endif
+
+; Adjust BZERO and BSCALE for new pixel size, unless these values are used
+; to define unsigned integer data types.  
+
+ if ~keyword_set(TOTAL) then begin
+ bscale = sxpar( oldhd, 'BSCALE')
+ bzero = sxpar( oldhd, 'BZERO')
+ unsgn = (tname EQ 'UINT') || (tname EQ 'ULONG') 
+
+ if ~unsgn then begin 
+ if (bscale NE 0) && (bscale NE 1) then $
+    sxaddpar, newhd, 'BSCALE', bscale/pix_ratio, 'Calibration Factor'
+ if (bzero NE 0) then sxaddpar, newhd, 'BZERO', bzero/pix_ratio, $
+       ' Additive Constant for Calibration'
+ endif 
+ endif
+ 
+  pixelsiz = sxpar( oldhd,'PIXELSIZ' , Count = N_pixelsiz)
+ if N_pixelsiz GT 0 then sxaddpar, newhd, 'PIXELSIZ', pixelsiz/xratio
+
+ if npar EQ 2 then oldhd = newhd else $
+    if npar EQ 1 then oldim = newhd
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/hreverse.pro b/Code/script_idl_mv/astrolib/hreverse.pro
new file mode 100644
index 0000000000000000000000000000000000000000..446008e569a363e40ec45e2ee374184fac7ba661
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hreverse.pro
@@ -0,0 +1,165 @@
+pro hreverse, oldim, oldhd, newim, newhd, subs, SILENT = silent, ERRMSG= errmsg
+;+
+; NAME:
+;       HREVERSE
+; PURPOSE:
+;       Reverse an image about either dimension and update FITS astrometry
+; EXPLANATION:
+;       Reverse an image about either the X or Y axis, and create a new 
+;       header with updated astrometry for the reversed image.
+;
+; CALLING SEQUENCE:
+;       HREVERSE,oldim,oldhd, [ subs, /SILENT ]   ;Update input image and header
+;               or
+;       HREVERSE, oldim, oldhd, newim, newhd, [ subs, /SILENT ]   
+;
+; INPUTS:
+;       OLDIM - the original image array
+;       OLDHD - the original image header
+;
+; OPTIONAL INPUTS:
+;       SUBS - Subs equals 1 to reverse the order of the X dimension,
+;               2 to reverse Y order.  If omitted, then HREVERSE will
+;               prompt for this scalar parameter.
+;
+; OPTIONAL OUTPUTS:
+;       NEWIM - the rotated image, with the same dimensions as Oldim 
+;       NEWHD - header for newim containing updated astrometry info
+;               If output parameters are not supplied, the program
+;               will modify the input parameters OLDIM and OLDHD
+;               to contain the rotated image and updated header.
+;
+; OPTIONAL KEYWORD INPUT:
+;       SILENT - if set and non-zero, then informative messages are suppressed.
+;
+; OPTIONAL KEYWORD OUTPUT:
+;       ERRMSG - If this keyword is supplied, then any error mesasges will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+;
+; SIDE EFFECTS:
+;       A right-handed coordinate system is converted into a left-
+;       handed one, and vice-versa.
+;
+; PROCEDURE:
+;       The User's Library procedure REVERSE is used to reverse the image.
+;       The CD and CRPIX header parameters are updated for the new header.
+;       For AIPS type astrometry, the CDELT parameters are also updated.
+;       A history record is also added to the header
+;
+; PROCEDURES USED:
+;       CHECK_FITS, EXTAST, REVERSE(), STRN(), SXADDPAR 
+; MODIFICATION HISTORY:
+;       Written, Aug. 1986 W. Landsman, STI Corp.
+;       Error modifying CROTA angles corrected     9-23-88
+;       Added format keyword, J. Isensee, July, 1990
+;       Work for ST Guide Star images, W. Landsman   HSTX, May 1995
+;       Compute CRPIX1 correctly for X reversal   W. Landsman HSTX August 1995
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added ERRMSG, Use double precision formatting, W. Landsman April 2000
+;       Recognize PC00n00m astrometry matrix   W. Landsman   December 2001
+;       Use V6.0 notation W. Landsman October 2012
+;- 
+ On_error, 2
+ npar = N_params()
+ if npar LE 1 then begin
+     print,'Syntax: HREVERSE, oldim, oldhd, [ subs, /SILENT, ERRMSG = ]'
+     print,'    or  HREVERSE, oldim, oldhd, newim, newhd, [ subs, /SILENT]'
+     return
+ endif 
+
+ save_err = arg_present(errmsg)     ;Does user want error msgs returned?
+;                                    Check for valid 2-D image & header
+ check_FITS, oldim, oldhd, dimen, /NOTYPE, ERRMSG = errmsg
+  if errmsg NE '' then begin
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+  endif
+
+  if N_elements(dimen) NE 2 then begin 
+        errmsg =  'ERROR - Input image array must be 2-dimensional'
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+ endif
+
+  xsize = dimen[0]  &  ysize = dimen[1]
+
+ if npar EQ 3 then subs = newim 
+ READSUBS: if (npar NE 3) && (npar NE 5) then $
+ read,'Enter 1 to reverse X dimension, 2 to reverse Y dimension: ',subs
+ if  ( subs NE 2 ) && ( subs NE 1 ) then begin
+        message,'ERROR - Illegal Value of Subs parameter',/CON
+        if npar then npar = npar -1     ;Make npar even
+        goto, READSUBS    
+ endif
+
+ newhd = oldhd
+ axis_name = ['X','Y']
+ if ~keyword_set(SILENT) then message, /INF, $
+'Now reversing ' + strn(xsize) + ' by ' + strn(ysize) + ' image about ' + $
+    axis_name[subs-1] + ' dimension'
+
+if npar GE 4 then newim = reverse( oldim,subs ) else $
+                  oldim = reverse( oldim,subs )
+
+ label = 'HREVERSE: ' + strmid(systime(),4,20)
+ sxaddpar, newhd, 'HISTORY', label+ $ 
+        ' Reversed About '+ axis_name[SUBS-1] + ' Dimension'
+
+; Update astrometry info if it exists
+
+ extast, oldhd, astr, noparams
+ if noparams LT 0 then goto, DONE
+
+  if subs EQ 1 then begin
+
+         if strmid( astr.ctype[0],5,3) EQ 'GSS' then begin
+                cnpix = -astr.xll -xsize
+                sxaddpar, newhd, 'CNPIX1', cnpix
+                sxaddpar, newhd, 'XPIXELSZ', -astr.xsz
+         endif else begin
+                 crpix1 = xsize  - (astr.crpix[0]-1)
+                 sxaddpar, newhd, 'CRPIX1', crpix1
+
+         if (noparams LT 2) || (noparams EQ 3) then $
+                sxaddpar, newhd, 'CDELT1', -astr.cdelt[0] $
+
+         else begin           ;If so, then convert them
+
+                 sxaddpar, newhd, 'CD1_1', -astr.cd[0,0]
+                 sxaddpar, newhd, 'CD2_1', -astr.cd[1,0]
+
+         endelse 
+ endelse
+
+ endif else  begin
+
+         if strmid(astr.ctype[0],5,3) EQ 'GSS' then begin
+
+                cnpix = -astr.yll -ysize
+                sxaddpar, newhd, 'CNPIX2', cnpix
+                sxaddpar, newhd, 'YPIXELSZ', -astr.ysz
+
+         endif else begin
+                 crpix2 = ysize  - (astr.crpix[1]-1)
+                 sxaddpar, newhd, 'CRPIX2', crpix2
+
+         if (noparams LT 2) or (noparams EQ 3) then $      
+                sxaddpar, newhd, 'CDELT2', -astr.cdelt[1] $
+
+         else begin           ;If so, then convert them
+
+                 sxaddpar, newhd, 'CD1_2', -astr.cd[0,1]
+                 sxaddpar, newhd, 'CD2_2', -astr.cd[1,1]
+
+         endelse 
+         endelse
+
+ endelse
+
+DONE:
+ if npar LE 3 then oldhd = newhd                ;update old header
+
+return
+end
diff --git a/Code/script_idl_mv/astrolib/hrot.pro b/Code/script_idl_mv/astrolib/hrot.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f905ffeeb0442812019cfccfebc63c7468aca7ce
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hrot.pro
@@ -0,0 +1,251 @@
+pro hrot, oldim, oldhd, newim, newhd, angle, xc, yc, int, MISSING=missing, $
+                INTERP = interp, CUBIC = cubic, PIVOT = pivot,ERRMSG= errmsg
+;+
+; NAME:
+;       HROT
+; PURPOSE:
+;       Rotate an image and create new FITS header with updated astrometry.
+; EXPLANATION: 
+;       Cubic, bilinear or nearest neighbor interpolation can be used.
+;
+; CALLING SEQUENCE:
+;       HROT, oldim, oldhd, [ newim, newhd, angle, xc, yc, int, 
+;                       MISSING =, INTERP =, CUBIC = , /PIVOT]
+; INPUTS:
+;       OLDIM - the original image array                             
+;       OLDHD - the original FITS image header, string array
+;
+; OPTIONAL INPUTS:
+;       NEWIM - If NEWIM is set to -1, then the old image and header will
+;               be updated
+;       ANGLE - Rotation angle, degrees clockwise, scalar
+;       XC    - X Center of rotation (-1 for center of image)
+;       YC    - Y Center of rotation (-1 for center of image)
+;       INT   - 0 for nearest neighbor, 1 for bilinear interpolation
+;               2 for cubic interpolation.  
+;
+; OPTIONAL OUTPUTS:
+;       NEWIM - the rotated image, with the same dimensions as Oldim 
+;       NEWHD - header for newim containing updated astrometry info
+;               If output parameters are not supplied, the program
+;               will modify the input parameters OLDIM and OLDHD
+;               to contain the rotated image and updated header.
+;
+; OPTIONAL INPUT KEYWORD:
+;       MISSING - Set this keyword to a scalar value which will be assigned
+;               to pixels in the output image which do not correspond to 
+;               existing input images (e.g if one rotates off-center). 
+;               If not supplied then linear extrapolation is used.
+;               ***NOTE: A bug was introduced into the POLY_2D function in IDL
+;               V5.5 (fixed in V6.1) such that the MISSING keyword
+;               may not work properly with floating point data***
+;
+;       INTERP - scalar set to either 0 (nearest neighbor interpolation),
+;               1 (bilinear interpolation), or 2 (cubic interpolation).    
+;               The interpolation type can be specified by either the INTERP 
+;               keyword or the int parameter
+;             
+;       CUBIC - If set and non-zero then cubic interpolation is used (see ROT),
+;               which is equivalent to setting INT = 2.   In IDL V5.0 and later,
+;                this keyword can also be set to a value between -1 and 0.
+;
+;       /PIVOT - Setting this keyword causes the image to pivot around the point
+;		XC, YC, so that this point maps into the same point in the
+;		output image.  If this keyword is set to 0 or omitted, then the
+;		point XC, YC in the input image is mapped into the center of
+;		the output image.
+;
+; OPTIONAL OUTPUT KEYWORD:
+;       ERRMSG - If this keyword is supplied, then any error mesasges will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+; EXAMPLE:
+;       Rotate an image non-interactively 30 degrees clockwise.  Use
+;       bilinear interpolation, and set missing values to 0.
+;
+;       IDL>  HROT, im_old, h_old, im_new, h_new, 30, -1, -1, 1, MIS = 0
+;
+;       As above but update the input image and header and pivot about (100,120)
+;
+;       IDL>  HROT, im_old, h_old, -1, -1, 30, 100, 120, 1, MIS = 0, /PIVOT
+; RESTRICTIONS:
+;       Unlike the ROT procedure, HROT cannot be used to magnify or
+;       or demagnify an image. Use HCONGRID or HREBIN instead.
+;
+; PROCEDURE:
+;       The image array is rotated using the ROT procedure.
+;       The CD (or CROTA) and CRPIX parameters, if present in the FITS header,
+;       are updated for the new rotation.
+;       History records are also added to the header
+;
+; PROCEDURES USED:
+;       CHECK_FITS, EXTAST, GETOPT(), GETROT, ROT(), STRN(), SXADDPAR
+;
+; MODIFICATION HISTORY:
+;       Written, Aug. 1986 W. Landsman, ST Systems Corp.
+;       Added MISSING keyword, W. Landsman March, 1991
+;       Added cubic interpolation, use astrometry structure   Feb 1994
+;       Removed call to SINCE_VERSION()  W. Landsman  March 1996
+;       Assume at least V3.5, add CUBIC parameter       W. Landsman  March 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Fix for CROTA2 defined and CDELT1 NE CDELT2, W. Landsman  November 1998
+;       Fix documentation  to specify clockwise rotation W. Landsman Dec. 1999
+;       Added /PIVOT keyword    W. Landsman  January 2000
+;       Added ERRMSG, Use double precision formatting, W. Landsman April 2000
+;       Consistent conversion between CROTA and CD matrix W. Landsman Oct 2000
+;       Work for both CD001001 and CDELT defined  W. Landsman   March 2001
+;       Recognize PC matrix astrometry  W. Landsman December 2001
+;       Update astrometry correctly when /PIVOT applied W. Landsman March 2002
+;       Update CROTA2 astrometry correctly, approximate GSSS W.L. June 2003
+;       Work with CD1_1, PC1_1 and CROTA keywords W. L. July 2003 
+;       Work with angle as a 1 element vector  W.L.  May 2006
+;- 
+ On_error,2
+ compile_opt idl2
+ npar = N_params()
+
+ if (npar LT 2) or (npar EQ 3) then begin       ;Check # of parameters
+  print,'Syntax: HROT, oldim, oldhd, [ newim, newhd, angle, xc, yc, int,'
+  print,'                   CUBIC =, INTERP = , MISSING = ,/PIVOT, ERRMSG= ]'
+  print, 'Oldim and Oldhd will be updated if only 2 parameters supplied '
+     return
+ endif 
+
+ cdr = !DPI/180.0D              ;Change degrees to radians
+;                               Check that input header matches input image
+ save_err = arg_present(errmsg)     ;Does user want error msgs returned?
+;                                    Check for valid 2-D image & header
+ check_FITS, oldim, oldhd, dimen, /NOTYPE, ERRMSG = errmsg
+  if errmsg NE '' then begin
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+  endif
+
+  if N_elements(dimen) NE 2 then begin 
+        errmsg =  'ERROR - Input image array must be 2-dimensional'
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+ endif
+
+  xsize = dimen[0]  &  ysize = dimen[1]
+
+  xc_new = (xsize - 1)/2.
+  yc_new = (ysize - 1)/2.
+ if npar LT 8 then begin
+  if npar EQ 2 then print,'Program will modify old image and header'
+  print,'Original array size is '+ strn(xsize) + ' by ' + strn(ysize)
+  read,'Angle of rotation (degrees clockwise): ',angle
+  ans = '' 
+  read,'Enter center (x,y) of rotation ( [RETURN] for center of image): ',ans
+  center = getopt(ans,'F',2)
+  if N_elements(center) EQ 1 then begin
+      xc = -1 & yc = -1
+  endif else begin
+      xc = center[0] & yc = center[1]
+  endelse
+ endif
+
+ if keyword_set( INTERP ) then int = interp
+ if keyword_set( CUBIC ) then int = 2
+ if N_elements(int) NE 1 then $
+   read,'Enter 0 for nearest neighbor, 1 for bilinear, 2 for cubic interpolation: ',int
+
+ case int of 
+ 0: type = ' Nearest Neighbor Approximation'
+ 1: type = ' Bilinear Interpolation' 
+ 2: type = ' Cubic Interpolation'
+ else: message,'Illegal value of Interp parameter: must be 0,1, or 2'
+ endcase
+
+ if xc LT 0 then xc = xc_new
+ if yc LT 0 then yc = yc_new
+
+ if N_elements(newim) EQ 1 then $
+   if newim EQ -1 then npar = 2                                      
+
+ newhd = oldhd
+ if N_elements(cubic) EQ 0 then cubic = (int EQ 2) 
+ angle = angle[0]
+
+  if N_elements(MISSING) NE 1 then begin
+
+        if npar EQ 2 then begin 
+               oldim = rot( oldim, angle, 1, xc,yc, $
+                            CUBIC = cubic, INTERP = int, PIVOT = pivot)
+        endif else begin
+               newim = rot( oldim, angle, 1, xc,yc, $
+                            CUBIC = cubic, INTERP = int, PIVOT = pivot)
+        endelse
+ 
+ endif else begin
+
+        if npar EQ 2 then begin
+           oldim = rot( oldim,angle,1,xc,yc, $
+                CUBIC = cubic, MISSING = missing, INTERP = int, PIVOT = pivot) 
+        endif else begin
+           newim = rot( oldim, angle, 1, xc, yc, $
+                CUBIC = cubic, MISSING = missing, INTERP = int, PIVOT = pivot)
+        endelse
+  endelse
+
+ label = 'HROT:' + strmid(systime(),4,20)
+ sxaddpar, newhd, 'HISTORY', label + $ 
+   ' Rotated by' + string(float(angle), FORM = '(f7.2)') + ' Degrees'
+ sxaddpar,newhd,'history',label+type 
+
+; Update astrometry info if it exists
+
+ extast, oldhd, astr, noparams
+ if strmid(astr.ctype[0],5,3) EQ 'GSS' then begin
+        gsss_stdast, newhd
+        extast, newhd, astr, noparams
+ endif
+
+
+ if noparams GE 0 then begin    ;Astrometry parameters exist in header?
+    crpix = astr.crpix
+    cd = astr.cd
+    cdelt = astr.cdelt
+ 
+    theta = angle*cdr
+    rot_mat = [ [ cos(theta), sin(theta)], $   ;Rotation matrix
+                [-sin(theta), cos(theta)] ] 
+    
+    ncrpix =  transpose(rot_mat)#(crpix-1-[xc,yc]) + 1
+    if ~keyword_set(PIVOT) then ncrpix = [xc_new,yc_new] + ncrpix $
+                              else ncrpix = [xc,yc] + ncrpix
+    sxaddpar, newhd, 'CRPIX1', ncrpix[0]
+    sxaddpar, newhd, 'CRPIX2', ncrpix[1]
+ 
+    newcd = cd # rot_mat
+
+    if noparams EQ 3 then begin     ;Transformation matrix format
+
+        sxaddpar, newhd, 'PC1_1', newcd[0,0] 
+        sxaddpar, newhd, 'PC1_2', newcd[0,1] 
+        sxaddpar, newhd, 'PC2_1', newcd[1,0]
+        sxaddpar, newhd, 'PC2_2', newcd[1,1]
+ 
+                                  
+    endif else if noparams EQ 2 then begin
+
+        sxaddpar, newhd, 'CD1_1', newcd[0,0]
+        sxaddpar, newhd, 'CD1_2', newcd[0,1]
+        sxaddpar, newhd, 'CD2_1', newcd[1,0]
+        sxaddpar, newhd, 'CD2_2', newcd[1,1]
+
+     endif else begin  
+; Just need to update the CROTA keywords 
+        crota  = atan( -newcd[1,0],newcd[1,1] )*180.0/!DPI
+        sxaddpar, newhd,'CROTA1', crota
+        sxaddpar, newhd,'CROTA2', crota
+    
+     endelse
+
+ endif 
+
+ if npar eq 2 then oldhd = newhd                ;update old image and header
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/hrotate.pro b/Code/script_idl_mv/astrolib/hrotate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ac20f84ec7f8329702c16505540f346831fd7a84
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/hrotate.pro
@@ -0,0 +1,214 @@
+pro hrotate, oldim, oldhd, newim, newhd, direction,ERRMSG = errmsg     
+;+
+; NAME:
+;     HROTATE
+; PURPOSE:
+;     Apply the IDL ROTATE function and update astrometry in a FITS header
+; EXPLANATION:
+;     Apply the intrinsic IDL ROTATE function to an image and update 
+;     astrometry in the associated FITS header.
+;
+; CALLING SEQUENCE:
+;     HROTATE, oldim, oldhd, newim, newhd, direction
+;               or
+;     HROTATE, oldim, oldhd, direction 
+;                       
+; INPUTS:
+;     OLDIM - the original image array                             
+;     OLDHD - the original FITS image header, string array
+;     DIRECTION - Scalar integer (0-7) specifying rotation direction, 
+;               exactly as specified by the IDL ROTATE function.
+;
+;        Direction  Transpose?  Rot. CCW  X1  Y1 
+;       ---------------------------------------- 
+;       0          No          None     X0  Y0    (no change)
+;       1          No          90      -Y0  X0 
+;       2          No          180     -X0 -Y0 
+;       3          No          270      Y0 -X0 
+;       4          Yes         None     Y0  X0 
+;       5          Yes         90      -X0  Y0                   
+;       6          Yes         180     -Y0 -X0 
+;       7          Yes         270      X0 -Y0 
+;
+; OPTIONAL OUTPUTS:
+;     NEWIM - the rotated image, with the same dimensions as Oldim 
+;     NEWHD - header for newim containing updated astrometry info
+;               If output parameters are not supplied, the program
+;               will modify the input parameters OLDIM and OLDHD
+;               to contain the rotated image and updated header.
+;
+; OPTIONAL KEYWORD OUTPUT:
+;     ERRMSG - If this keyword is supplied, then any error mesasges will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+; EXAMPLE:
+;     Rotate an image exactly 90 degrees counterclockwise and update the
+;     FITS image array and header. 
+;
+;       IDL>  HROT, im, h, im_new, h_new, 1
+;
+; PROCEDURE:
+;      The image array is rotated using the ROTATE function.
+;      The CD (or CROTA) and CRPIX parameters, if present in the FITS header,
+;      are updated for the new rotation.
+;      History records are also added to the header
+;
+; RESTRICTIONS: 
+;     Does not work Guide Star Survey (GSS) astrometry.    Use GSSS_STDAST to
+;     first convert 
+; PROCEDURES USED:
+;     CHECK_FITS(), SXADDPAR, EXTAST
+;
+; MODIFICATION HISTORY:
+;     Written,  Mar 1997    W. Landsman,  Hughes STX
+;     Work for non-square images   W. Landsman   June 1998 Raytheon STX
+;     Fix for different plate scales, and CROTA2 defined, November 1998  
+;     Added ERRMSG, Use double precision formatting, W. Landsman April 2000
+;     Consistent conversion between CROTA and CD matrix W. Landsman Oct 2000
+;     Correct update when CROTA keyword present W. Landsman  June 2003
+;     Update CDELT for AIPS-style astrometry headers M. Perrin/WL Jul 2003
+;     Convert GSS astrometry to WCS W. Landsman  November 2004
+;     Work even if no astrometry present, just update NAXIS* WL June 2011
+;- 
+ On_error,2
+ npar = N_params()
+
+ if (npar NE 3) and (npar NE 5) then begin      ;Check # of parameters
+  print,'Syntax - HROTATE, oldim, oldhd, newim, newhd, direction'
+  print,'                            or '
+  print,'         HROTATE, oldim, oldhd, direction, {ERRMSG = ]'
+  return
+ endif 
+
+ if npar EQ 3 then direction = newim
+ if N_elements(direction) NE 1 then message, $
+        'ERROR - Direction parameter must be an integer scalar (0-7)'
+ dirpar = direction mod 8
+ if dirpar LT 0 then dirpar = dirpar + 8
+
+;                               Check that input header matches input image
+
+ save_err = arg_present(errmsg)     ;Does user want error msgs returned?
+;                                    Check for valid 2-D image & header
+ check_FITS, oldim, oldhd, dimen, /NOTYPE, ERRMSG = errmsg
+  if errmsg NE '' then begin
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+  endif
+
+  if N_elements(dimen) NE 2 then begin 
+        errmsg =  'ERROR - Input image array must be 2-dimensional'
+        if ~save_err then message,'ERROR - ' + errmsg,/CON
+        return
+ endif
+
+  if N_elements(dimen) NE 2 then message, $
+     'ERROR - Input image array must be 2-dimensional'
+  xsize = dimen[0]  &  ysize = dimen[1]
+  xc = (xsize-1)/2.
+  yc = (ysize-1)/2.
+ 
+ newhd = oldhd
+
+ if npar EQ 5 then newim = rotate(oldim, direction ) else $
+                   oldim = rotate(oldim, direction )
+ 
+ case dirpar of
+   0: return
+   1: rot_mat = [ [0, 1],[-1, 0] ] 
+   2: rot_mat = [ [-1,0],[ 0,-1] ]
+   3: rot_mat = [ [0,-1], [1, 0] ]
+   4: rot_mat = [ [0, 1], [-1,0] ]
+   5: rot_mat = [ [-1,0], [0, -1] ]
+   6: rot_mat = [ [0,-1], [1, 0] ]
+   7: rot_mat = [ [1, 0], [0, 1] ]
+   else: message,$
+        'ERROR - Illegal value of direction parameter, must be between 0 and 7'
+   endcase
+
+ if (xsize NE ysize) && (rot_mat[0,0] EQ 0) then begin
+        sxaddpar, newhd, 'NAXIS1', ysize
+        sxaddpar, newhd, 'NAXIS2', xsize
+ endif
+
+ label = 'HROTATE: ' + strmid(systime(),4,20)
+ sxaddhist, label + ' Image = ROTATE(Image,' + strtrim(direction,2) + ')',newhd
+
+; Update astrometry info if it exists.   If GSS astrometry is present, then
+; convert it to standard WCS astrometry
+
+ extast, oldhd, astr, noparams
+
+ if noparams GE 0 then begin    ;Astrometry parameters exist in header?
+
+ if strmid(astr.ctype[0],5,3) EQ 'GSS' then begin
+        gsss_stdast, newhd
+        extast, newhd, astr, noparams
+ endif
+
+; For non-square images, check if  X and Y axes have been flipped
+
+    crpix = astr.crpix
+    cd = astr.cd
+    cdelt = astr.cdelt
+    if cdelt[0] NE 1.0 then begin
+         cd[0,0] = cd[0,0]*cdelt[0] & cd[0,1] = cd[0,1]*cdelt[0]
+         cd[1,1] = cd[1,1]*cdelt[1] & cd[1,0] = cd[1,0]*cdelt[1]
+     endif
+       
+    ncrpix =  [xc,yc] + rot_mat#(crpix-1 -[xc,yc]) + 1
+
+    newcd =  cd # transpose(rot_mat)
+
+
+    if (dirpar EQ 4) || (dirpar EQ 6) then begin
+        ncrpix[0] = xsize - ( ncrpix[0] - 1)
+        newcd[*,0] = -newcd[*,0]
+    endif 
+
+    if (dirpar EQ 5) || (dirpar EQ 7) then begin
+        ncrpix[1] = ysize - (ncrpix[1] -1 )
+        newcd[*,1] = -newcd[*,1]
+    endif 
+
+ 
+  if (xsize NE ysize) && (rot_mat[0,0] EQ 0) then begin
+        ncrpix[0] = ncrpix[0] - xc + yc
+        ncrpix[1] = ncrpix[1] - yc + xc
+ endif
+
+
+    sxaddpar, newhd, 'CRPIX1', ncrpix[0]
+    sxaddpar, newhd, 'CRPIX2', ncrpix[1]
+
+   if noparams EQ 3 then begin     ;Transformation matrix format
+
+        sxaddpar, newhd, 'PC1_1', newcd[0,0] 
+        sxaddpar, newhd, 'PC1_2', newcd[0,1] 
+        sxaddpar, newhd, 'PC2_1', newcd[1,0]
+        sxaddpar, newhd, 'PC2_2', newcd[1,1]
+                                  
+    endif else if noparams EQ 2 then begin
+
+        sxaddpar, newhd, 'CD1_1', newcd[0,0] 
+        sxaddpar, newhd, 'CD1_2', newcd[0,1] 
+        sxaddpar, newhd, 'CD2_1', newcd[1,0]
+        sxaddpar, newhd, 'CD2_2', newcd[1,1]
+
+     endif else begin ; noparams = 1. CROTA+CDELT type
+        crota  = atan(-newcd[1,0], newcd[1,1] )*180.0/!DPI
+
+        if dirpar GE 4 then sxaddpar, newhd, 'CDELT1', -cdelt[0]
+
+        sxaddpar, newhd,'CROTA1', crota
+        sxaddpar, newhd,'CROTA2', crota
+   endelse
+      
+   
+ endif 
+
+ if npar EQ 3 then oldhd = newhd                ;update old image and header
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/ieee_to_host.pro b/Code/script_idl_mv/astrolib/ieee_to_host.pro
new file mode 100644
index 0000000000000000000000000000000000000000..cb1b1f38cd5d69366c21664923b92314b6a87730
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ieee_to_host.pro
@@ -0,0 +1,104 @@
+pro ieee_to_host, data, IDLTYPE = idltype
+;+
+; NAME:
+;     IEEE_TO_HOST
+; PURPOSE:
+;     Translate an IDL variable from IEEE-754 to host representation 
+; EXPLANATION:
+;     The variable is translated from IEEE-754 ("big-endian" as used, for
+;     example, in FITS data ), into the host machine architecture.
+;
+;     Duplicates most of the functionality of the SWAP_ENDIAN_INPLACE procedure
+;     introduced in V5.6, with the addition of the IDLTYPE keyword.
+; CALLING SEQUENCE:
+;     IEEE_TO_HOST, data, [ IDLTYPE = , ]
+;
+; INPUT-OUTPUT PARAMETERS:
+;     data - any IDL variable, scalar or vector.   It will be modified by
+;             IEEE_TO_HOST to convert from IEEE to host representation.  Byte 
+;             and string variables are returned by IEEE_TO_HOST unchanged
+;
+; OPTIONAL KEYWORD INPUTS:
+;     IDLTYPE - scalar integer (1-15) specifying the IDL datatype according
+;               to the code given by the SIZE function.     This keyword
+;               is usually when DATA is a byte array to be interpreted as
+;               another datatype (e.g. FLOAT).
+;
+; EXAMPLE:
+;       A 2880 byte array (named FITARR) from a FITS record is to be 
+;       interpreted as floating and converted to the host representaton:
+;
+;       IDL> IEEE_TO_HOST, fitarr, IDLTYPE = 4     
+;
+; METHOD:
+;       The BYTEORDER procedure is called with the appropriate keyword
+;
+; MODIFICATION HISTORY:
+;      Written, W. Landsman   Hughes/STX   May, 1992
+;      Under VMS check for IEEE -0.0 values   January 1998
+;      VMS now handle -0.0 values under IDL V5.1    July 1998
+;      Added new integer datatypes  C. Markwardt/W. Landsman  July 2000
+;      Post-V5.1 version, no VMS negative zero check  W. Landsman July 2001
+;      Use size(/type)  W. Landsman December 2002
+;      Use /SWAP_IF_LITTLE_ENDIAN keyword for 64bit types W. Landsman Feb 2003
+;      Do not use XDR keywords to BYTEORDER for much improved speed
+;                               W. Landsman   April 2006
+;      Update cosmetic typo for structures W. Landsman  October 2006
+;-
+ On_error,2 
+
+ if N_params() EQ 0 then begin
+    print,'Syntax - IEEE_TO_HOST, data, [ IDLTYPE = ]'
+    return
+ endif  
+
+ npts = N_elements( data )
+ if npts EQ 0 then $
+     message,'ERROR - IDL data variable (first parameter) not defined'
+
+ if N_elements(idltype) EQ 0 then idltype = size(data,/type)
+ 
+ case idltype of
+
+      1: return                             ;byte
+      
+            2: byteorder, data, /SSWAP,/SWAP_IF_LITTLE            ;integer
+
+      3: byteorder, data, /LSWAP,/SWAP_IF_LITTLE            ;long
+
+      4: byteorder, data, /LSWAP, /SWAP_IF_LITTLE           ;float
+
+      5: byteorder,data,/L64SWAP, /SWAP_IF_LITTLE              ;double
+ 
+      6: byteorder, data, /LSWAP, /SWAP_IF_LITTLE
+     
+      7: return                             ;string
+
+      8: BEGIN                              ;structure
+
+        Ntag = N_tags( data )
+
+        for t=0,Ntag-1 do  begin
+          temp = data.(t)
+          ieee_to_host, temp
+          data.(t) = temp
+        endfor 
+       END
+
+     9: byteorder, data, /L64SWAP, /SWAP_IF_LITTLE
+ 
+     12: byteorder, data, /SSWAP, /SWAP_IF_LITTLE
+
+     13: byteorder, data, /LSWAP, /SWAP_IF_LITTLE
+
+     14: byteorder, data, /L64swap, /SWAP_IF_LITTLE
+
+     15: byteorder, data, /L64swap, /SWAP_IF_LITTLE
+
+     else: message,'Unrecognized datatype ' + strtrim(idltype,2)
+
+ ENDCASE
+
+
+ return
+ end 
diff --git a/Code/script_idl_mv/astrolib/imcontour.pro b/Code/script_idl_mv/astrolib/imcontour.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2665ca849d633bbf67f0bf99a894d57b1cf9a8af
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/imcontour.pro
@@ -0,0 +1,335 @@
+pro imcontour, im, hdr, TYPE=type, PUTINFO=putinfo, XTITLE=xtitle,  $
+      YTITLE=ytitle, SUBTITLE = subtitle, XDELTA = xdelta, YDELTA = ydelta, $
+      _EXTRA = extra, XMID = xmid, YMID = ymid, OVERLAY = OVERLAY, $
+       NOerase = noerase,window=window
+;+
+; NAME:
+;       IMCONTOUR
+; PURPOSE:
+;       Make a contour plot labeled with astronomical coordinates.
+; EXPLANATION:
+;       The type of coordinate display is controlled by the keyword TYPE
+;       Set TYPE=0 (default) to measure distances from the center of the image
+;       (IMCONTOUR will decide whether the plotting units will be in
+;       arc seconds, arc minutes, or degrees depending on image size.)
+;       Set /TYPE for standard RA and Dec labeling
+;
+;       By using the /NODATA keyword, IMCONTOUR can also be used to simply
+;       provide astronomical labeling of a previously displayed image.
+; CALLING SEQUENCE
+;       IMCONTOUR, im, hdr,[ /TYPE, /PUTINFO, XDELTA = , YDELTA =, _EXTRA = 
+;                            XMID=, YMID= ]
+;
+; INPUTS:
+;       IM - 2-dimensional image array
+;       HDR - FITS header associated with IM, string array, must include
+;               astrometry keywords.   IMCONTOUR will also look for the
+;               OBJECT and IMAGE keywords, and print these if found and the 
+;               PUTINFO keyword is set.
+;
+; OPTIONAL PLOTTING KEYWORDS:
+;       /TYPE - the type of astronomical labeling to be displayed.   Either set
+;               TYPE = 0 (default), distance to center of the image is
+;               marked in units of Arc seconds, arc minutes, or degrees
+;
+;               TYPE = 1 astronomical labeling with Right ascension and 
+;               declination.
+;
+;       /PUTINFO - If set, then IMCONTOUR will add information about the image
+;               to the right of the contour plot.  Information includes image
+;               name, object, image center, image center, contour levels, and
+;               date plot was made
+;
+;       XDELTA, YDELTA - Integer scalars giving spacing of labels for TYPE=1.  
+;               Default is to label every major tick (XDELTA=1) but if 
+;               crowding occurs, then the user might wish to label every other
+;               tick (XDELTA=2) or every third tick (XDELTA=3)
+;
+;       XMID, YMID - Scalars giving the X,Y position from which offset distances
+;               will be measured when TYPE=0.   By default, offset distances 
+;               are measured from the center of the image.
+;       /OVERLAY - If set, then IMCONTOUR is assumed to overlay an image.
+;               This requires 1 extra pixel be included on the X and Y axis,
+;               to account for edge effects in the image display.    Setting
+;               OVERLAY provide a better match of the contour and underlying
+;               image but is not as aesthetically pleasing because the contours
+;               will not extend to the axes. 
+;               
+;
+;       Any keyword accepted by CONTOUR may also be passed through IMCONTOUR
+;       since IMCONTOUR uses the _EXTRA facility.     IMCONTOUR uses its own
+;       defaults for the XTITLE, YTITLE XMINOR, YMINOR, and SUBTITLE keywords
+;       but these may be overridden.    Note in particular the /NODATA keyword
+;       which can be used if imcontour.pro is to only provide labeling.
+;
+; NOTES:
+;       (1) The contour plot will have the same dimensional ratio as the input
+;           image array
+;       (2) To contour a subimage, use HEXTRACT before calling IMCONTOUR
+;       (3) Use the /NODATA keyword to simply provide astronomical labeling
+;           of a previously displayed image.
+;       (4) The IMCONTOUR display currently does not indicate the image 
+;           rotation in any way, but only specifies coordinates along the 
+;           edges of the image 
+;
+; EXAMPLE:
+;       Overlay the contour of an image, im2, with FITS header, h2, on top
+;       of the display of a different image, im1.   Use RA, Dec labeling, and
+;       seven equally spaced contour levels.    The use of a program like
+;       David Fanning's cgImage  http://www.idlcoyote.com/programs/cgimage.pro
+;       is suggested to properly overlay plotting and image coordinates.  The
+;       /Keep_aspect_ratio keyword must be used.
+;
+;       IDL> cgimage,im1,/keep_aspect, position = pos
+;       IDL> imcontour,im2,h2,nlevels=7,/Noerase,/TYPE,position = pos
+;
+; PROCEDURES USED:
+;       CHECK_FITS, EXTAST, GETROT, TICPOS, TICLABEL, TIC_ONE, TICS, XYAD
+;       CONS_RA(), CONS_DEC(), ADSTRING()
+;
+; REVISION HISTORY:
+;       Written   W. Landsman   STX                    May, 1989
+;       Fixed RA,Dec labeling  W. Landsman             November, 1991
+;       Fix plotting keywords  W.Landsman             July, 1992
+;       Recognize GSSS headers  W. Landsman            July, 1994
+;       Removed Channel keyword for V4.0 compatibility June, 1995
+;       Add _EXTRA CONTOUR plotting keywords  W. Landsman  August, 1995
+;       Add XDELTA, YDELTA keywords  W. Landsman   November, 1995
+;       Use SYSTIME() instead of !STIME                August, 1997
+;       Remove obsolete !ERR system variable W. Landsman   May 2000 
+;       Added XMID, YMID keywords to specify central position (default is still
+;          center of image)  W. Landsman               March 2002     
+;       Recognize Galactic coordinates, fix Levels display when /PUTINFO set
+;           W. Landsman                May 2003
+;       Correct conversion from seconds of RA to arcmin is 4 not 15.
+;       	M. Perrin					July 2003
+;       Fix integer truncation which appears with tiny images WL  July 2004
+;       Changed some keyword_set() to N_elements WL  Sep 2006
+;       Work to 1 pixels level when overlaying an image,added /OVERLAY keyword
+;        Use FORMAT_AXIS_VALUES()  W. Landsman   Jan 2008 
+;       Make /OVERLAY  always optional   W. Landsman  Feb 2008
+;       Check if RA crosses 0 hours  WL  Aug 2008
+;       Use Coyote Graphics WL Feb 2011
+;-
+  On_error,2                                 ;Return to caller
+  compile_opt idl2
+
+  if N_params() LT 2 then begin             ;Sufficient parameters?
+      print,'Syntax - imcontour, im, hdr, [ /TYPE, /PUTINFO, XDELTA=, YDELT= '
+      print,'                               XMID=, YMID = ]'
+      print,'         Any CONTOUR keyword is also accepted by IMCONTOUR'  
+     return
+  endif
+
+  ;Make sure header appropriate to image
+  check_fits, im, hdr, dimen, /NOTYPE, ERRMSG = errmsg    
+  if errmsg NE '' then message,errmsg
+
+; Set defaults if keywords not set
+
+  if ~keyword_set( TYPE ) then type = 0
+  if ~keyword_set( XDELTA ) then xdelta = 1
+  if ~keyword_set( YDELTA ) then ydelta = 1
+  
+  if N_Elements(XMINOR) EQ 0 then $
+       xminor = !X.MINOR EQ 0 ? 5 : !X.MINOR
+
+  if N_Elements(YMINOR) EQ 0 then $
+       yminor = !Y.MINOR EQ 0 ?  5 : !Y.MINOR
+
+  EXTAST, hdr, astr, noparams      ;Extract astrometry from header
+  if noparams LT 0 then $                       ;Does astrometry exist?
+      message,'FITS header does not contain astrometry'
+  if strmid( astr.ctype[0], 5, 3) EQ 'GSS' then begin
+        hdr1 = hdr
+        gsss_STDAST, hdr1
+        extast, hdr1, astr, noparams
+  endif
+  sexig = strmid(astr.ctype[0],0,4) EQ 'RA--'
+ 
+; Adjust plotting window so that contour plot will have same dimensional 
+; ratio as the image
+
+  xlength = !D.X_VSIZE &  ylength = !D.Y_VSIZE
+  xsize = fix( dimen[0] )  &   ysize = fix( dimen[1] )
+  xsize1 = xsize-1 & ysize1 = ysize-1
+     if keyword_set(OVERLAY) then begin 
+         xran  = [0,xsize]-0.5  & yran = [0,ysize]-0.5
+    endif else begin
+         xran = [0,xsize1] & yran = [0,ysize1]	 
+    endelse
+
+ xratio = xsize / float(ysize)
+  yratio = ysize / float(xsize)
+  if N_elements(XMID) EQ 0 then xmid = (xran[1] -xran[0]-1)/2.
+  if N_elements(YMID) EQ 0 then ymid = (yran[1] -yran[0]-1)/2.
+
+  if ( ylength*xratio LT xlength ) then begin
+
+    xmax = 0.15 + 0.8*ylength*xratio/xlength
+    pos = [ 0.15, 0.15, xmax, 0.95 ]
+
+  endif else begin
+
+     xmax = 0.95
+     pos = [ 0.15, 0.15, xmax, 0.15+ 0.8*xlength*yratio/ylength ]
+
+  endelse
+
+  xtics = !X.TICKS GT 0 ? abs(!X.TICKS) : 8
+  ytics = !Y.TICKS GT 0 ? abs(!Y.TICKS) : 8
+ 
+  pixx = float(xsize)/xtics            ;Number of X pixels between tic marks
+  pixy = float(ysize)/ytics            ;Number of Y pixels between tic marks
+
+  getrot,hdr,rot,cdelt               ;Get the rotation and plate scale
+
+  xyad,hdr,xsize1/2.,ysize1/2.,ra_cen,dec_cen         ;Get coordinates of image center
+  if sexig then ra_dec = adstring(ra_cen,dec_cen,1)       ;Make a nice string
+
+; Determine tic positions and labels for the different type of contour plots
+
+  if type NE 0 then begin                  ;RA and Dec labeling
+
+     xedge = [ xran[0], xran[1], xran[0]]          ;X pixel values of the four corners
+     yedge = [ yran[0], yran[0], yran[1] ]          ;Y pixel values of the four corners
+
+     xy2ad, xedge, yedge, astr, a, d
+ 
+     pixx = float(xmid*2)/xtics          ;Number of X pixels between tic marks
+     pixy = float(ymid*2)/ytics          ;Number of Y pixels between tic marks
+
+; Find an even increment on each axis, for RA check crossing of 0 hours
+     case 1 of 
+     ( a[1] GT a[0] ) and (cdelt[0] LT 0 ) : $ 
+        tics, a[0], a[1] - 360.0d , xsize, pixx, raincr, RA=sexig 
+     ( a[1] LT a[0] ) and (cdelt[0] GT 0 ) : $ 
+        tics, a[0], 360.0d + a[1], xsize, pixx, raincr, RA=sexig 
+     else: tics, a[0], a[1], xsize, pixx, raincr, RA=sexig 
+     endcase
+     tics, d[0], d[2], ysize, pixy, decincr    ;Find an even increment for Dec
+
+; Find position of first tic on each axis
+     tic_one, a[0], pixx, raincr, botmin, xtic1, RA= sexig  ;Position of first RA tic
+     tic_one, d[0], pixy, decincr,leftmin,ytic1       ;Position of first Dec tic
+
+     nx = fix( (xsize1-xtic1)/pixx )             ;Number of X tic marks
+     ny = fix( (ysize1-ytic1)/pixy )             ;Number of Y tic marks
+
+     if sexig then ra_grid = (botmin + findgen(nx+1)*raincr/4.) else $ 
+                   ra_grid = (botmin + findgen(nx+1)*raincr/60.)
+     dec_grid = (leftmin + findgen(ny+1)*decincr/60.)
+
+     ticlabels, botmin, nx+1, raincr, xlab, RA=sexig, DELTA=xdelta
+     ticlabels, leftmin, ny+1, decincr, ylab,DELTA=ydelta
+
+     xpos = cons_ra( ra_grid,0,astr )     ;Line of constant RA
+     ypos = cons_dec( dec_grid,0,astr)   ;Line of constant Dec
+
+     if sexig then begin 
+        xunits = 'Right Ascension'
+        yunits = 'Declination'
+     endif else begin
+        xunits = 'Longitude'
+        yunits = 'Latitude'
+     endelse                          
+
+  endif else begin ; label with distance from center.
+     ticpos, xsize*cdelt[0], xsize, pixx, incrx, xunits     
+     numx = fix((xmid-xran[0])/pixx)              ;Number of ticks from left edge
+     ticpos, ysize*cdelt[1], ysize, pixy, incry, yunits
+     numy = fix((ymid-yran[0])/pixy)             ;Number of ticks from bottom to center
+     nx = numx + fix((xran[1]-xmid)/pixx)    ;Total number of X ticks 
+     ny = numy + fix((yran[1]-ymid)/pixy)    ;Total number of Y ticks  
+     xpos = xmid  + (findgen(nx+1)-numx)*pixx
+     ypos = ymid   + (findgen(ny+1)-numy)*pixy
+     xlab = format_axis_values( indgen(nx+1)*incrx - incrx*numx)
+     ylab = format_axis_values( indgen(ny+1)*incry - incry*numy)
+    
+     
+  endelse
+
+; Get default values of XTITLE, YTITLE, TITLE and SUBTITLE
+
+  putinfo = keyword_set(PUTINFO)
+
+  if N_elements(xtitle) EQ 0 then $
+  xtitle = !X.TITLE eq ''? xunits : !X.TITLE
+
+  if N_elements(ytitle) EQ 0 then $
+        ytitle = !Y.TITLE eq ''? yunits : !Y.TITLE
+
+  if (~keyword_set( SUBTITLE) ) && (putinfo LT 1) then $
+      if sexig then $
+      subtitle = 'Center:  R.A. '+ strmid(ra_dec,1,13)+'  Dec ' + $
+               strmid(ra_dec,13,13) else $
+     subtitle = 'Center:  Longitude '+ strtrim(string(ra_cen,'(f6.2)'),2) + $
+                          ' Latitude ' + strtrim(string(dec_cen,'(f6.2)'),2)
+
+  if N_elements( SUBTITLE)  EQ 0 then subtitle = !P.SUBTITLE
+  cgContour,im, $
+         XTICKS = nx, YTICKS = ny, POSITION=pos, XSTYLE=1, YSTYLE=1,$
+         XTICKV = xpos, YTICKV = ypos, XTITLE=xtitle, YTITLE=ytitle, $
+         XTICKNAME = xlab, YTICKNAME = ylab, SUBTITLE = subtitle, $
+         XMINOR = xminor, YMINOR = yminor, _EXTRA = extra, XRAn=xran, $
+	 YRAN = yran,noerase=noerase,WINDOW=window
+	 
+  
+;  Write info about the contour plot if desired
+
+  if putinfo GE 1 then begin
+
+    sv = !D.NAME
+    set_plot,'null'
+    contour,im, _EXTRA = extra, PATH_INFO = info
+    set_plot,sv
+
+
+  if keyword_set(window) then cgcontrol, execute= 0	
+   xmax = xmax + 0.01
+
+     ypos = 0.92
+     object = sxpar( hdr, 'OBJECT', Count = N_object )
+     if N_object GT 0  then begin 
+           cgText, xmax, ypos, object, /NORM, addcmd=window
+           ypos = ypos-0.05
+     endif
+
+     name = sxpar( hdr, 'IMAGE', Count = N_image )
+     if N_image GT 0 then begin 
+           cgtext,xmax,ypos,name, /NORM, addcmd= window
+           ypos = ypos - 0.05
+     endif
+
+     cgText, xmax, ypos,'Center:',/NORM, addcmd=window
+     ypos = ypos - 0.05
+     if sexig then begin
+     cgText, xmax, ypos, 'R.A. '+ strmid(ra_dec,1,13),/NORM,addcmd=window
+     cgText, xmax, ypos-0.05, 'Dec '+  strmid(ra_dec,13,13),/NORM,addcmd=window
+     endif else begin
+     cgText, xmax, ypos, 'Longitude: '+ strtrim(string(ra_cen,'(f6.2)'),2), $
+             /NORM, addcmd=window
+     cgText, xmax, ypos-0.05, addcmd=window, $
+             'Latitude: '+  strtrim(string(dec_cen,'(f6.2)'),2),/NORM
+     endelse
+     ypos = ypos - 0.1
+     cgText, xmax, ypos, 'Image Size', /NORM, addcmd=window
+     cgText, xmax, ypos-0.05, 'X: ' + strtrim(xsize,2), /NORM, addcmd=window
+     cgText, xmax, ypos-0.1, 'Y: ' + strtrim(ysize,2), /NORM, addcmd=window
+     cgText, xmax, ypos- 0.15, strmid(systime(),4,20),/NORM, addcmd=window
+     cgText, xmax, ypos - 0.2, 'Contour Levels:',/NORM, addcmd=window
+
+
+    ypos = ypos - 0.25
+    val = info.value
+    val = val[uniq(val,sort(val))]
+     nlevels = N_elements(val)
+     for i = 0,(nlevels < 7)-1 do $
+          cgText,xmax,ypos-0.05*i,string(i,'(i2)') + ':' + $
+                              string(val[i]), /NORM,addcmd=window
+     if keyword_set(window) then cgcontrol, execute=1
+ 
+  endif
+  
+  return                                          
+  end                                         
diff --git a/Code/script_idl_mv/astrolib/imdbase.pro b/Code/script_idl_mv/astrolib/imdbase.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d2970eab34cff8b85164717c8f5f28218750e7b1
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/imdbase.pro
@@ -0,0 +1,205 @@
+pro imdbase,hdr,catalogue,list,XPOS=xpos,YPOS=ypos, SILENT=silent, $
+                XRANGE=xrange,YRANGE=yrange, SUBLIST = sublist, ALT = alt
+;+
+; NAME:
+;     IMDBASE
+; PURPOSE:
+;     Find the sources in an IDL database that are located on a given image.
+;
+; CALLING SEQUENCE:
+;    imdbase, hdr, [catalogue, list, ALT=, XPOS= ,YPOS=, XRANGE= ,YRANGE= , 
+;                       SUBLIST =, /SILENT ]  
+;
+; INPUTS:
+;    hdr - FITS image header containing astrometry, and the NAXIS1,
+;               NAXIS2 keywords giving the image size
+;    catalogue - string giving name of catalogue in database.   If not supplied
+;              then the currently open database is used.   The database must 
+;              contain the (preferably indexed) fields RA (in hours) and DEC.  
+;              Type DBHELP for a list of the names of available catalogues.
+;
+; OPTIONAL OUTPUT PARAMETER:
+;    LIST - A longwprd vector containing the entry numbers of sources found
+;           within the image.   This vector can then be used with other
+;           database procedures, e.g. to print specified fields (DBPRINT)
+;           or subselect with further criteria (DBFIND)
+;
+; OPTIONAL OUTPUT KEYWORD PARAMETER:
+;     XPOS - REAL*4 vector giving X positions of catalogue sources found 
+;            within the image
+;     YPOS - REAL*4 vector giving Y positions of catalogue sources found 
+;            within the image
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS
+;       ALT -  single character 'A' through 'Z' or ' ' specifying an alternate 
+;              astrometry system present in the FITS header.    The default is
+;              to use the primary astrometry or ALT = ' '.   If /ALT is set, 
+;              then this is equivalent to ALT = 'A'.   See Section 3.3 of 
+;              Greisen & Calabretta (2002, A&A, 395, 1061) for information about
+;              alternate astrometry keywords.
+;     SILENT - If set, then informational messages are suppressed
+;     SUBLIST - vector giving entries in the database to consider in the
+;               search.  If not supplied, or set equal to -1, then all entries
+;               are considered.
+;     XRANGE - 2 element vector giving the X range of the image to consider.
+;              The default is to search for catalogue sources within the entire
+;             image
+;     YRANGE - 2 element vector giving the Y range of the image to consider.
+;
+; NOTES:
+;     If an output list vector is not supplied, then the found objects are
+;     diplayed at the terminal.
+;
+; EXAMPLE:
+;      Find all existing IUE observations within the field of the FITS 
+;      file fuv0435fc.fits.  Subselect those taken with the SWP camera
+;
+;      H = HEADFITS('fuv0435f.fits')             ;Read FITS header 
+;      IMDBASE,H,'IUE',list              ;Find IUE obs. within image 
+;      list2 = DBFIND('CAM_NO=3',list)   ;Subselect on SWP images
+;
+; SIDE EFFECTS:
+;      The IDL database is left open upon exiting IMDBASE.
+; NOTES:
+;      IMDBASE checks the description of the RA item in the database for the
+;      string '1950'.    If found, the database RA and Dec are assumed to be 
+;      in equinox B1950.   Otherwise they are assumed to be in ICRS or J2000. 
+;
+; SYSTEM VARIABLES:                                                
+;      The non-standard system variable !TEXTOUT is required for use with the
+;      database procedures.   
+;
+; PROCEDURES USED:
+;      AD2XY, DBEXT, DB_ITEM, DB_ITEM_INFO(), DBOPEN, DBFIND(), EXTAST, 
+;      GET_EQUINOX(), GSSSADXY, GSSSXYAD, HPRECESS, SXPAR(), XY2AD 
+; REVISION HISTORY:
+;      Written W. Landsman            September, 1988
+;      Added SUBLIST keyword          September, 1991
+;      Updated to use ASTROMETRY structures    J.D. Offenberg, HSTX, Jan 1993
+;      Conversion for precession fixed.   R.Hill, HSTX, 22-Apr-93
+;      Check RA description for equinox   W. Landsman  Aug 96
+;      Call HPRECESS if header equinox does not match DB  W. Landsman Oct. 1998
+;      Assume Equinox J2000 if not explicitly B1950 W. Landsman Jan. 2005
+;      Added ALT keyword W. Landsman  April 2005
+;      Use open database, if no catalogue name given  W.L  April 2008
+;      Added /SILENT keyword W.L. Mar 2009
+;      Use V6.0 notation W. L. Aug 2013
+;-
+ On_error,2                                  ;Return to caller
+ compile_opt idl2
+
+ if N_params() LT 2 then begin               ;Sufficient parameters?
+     print,'Syntax - imdbase, hdr, catalogue, [ list, ALT =, SUBLIST = '
+     print,'              XPOS = , YPOS = , XRANGE =, YRANGE =, /SILENT  ]'
+     print,'Type DBHELP for available catalogues'
+     return
+ endif              
+
+; Check if catalogue has preselected output fields
+
+ if N_elements(catalogue) EQ 0 then catalogue = db_info('name',0)
+ catname = strupcase(strtrim(catalogue,2)) 
+
+ dbopen,catalogue,unavail=unavail       ;Was database found?
+ if unavail EQ 1 then message,'Database ' + catalogue + ' is unavailable'
+                
+ db_item,'ra',itnum
+ descrip = db_item_info('description',itnum[0])
+ if strpos(descrip,'1950') GE 0 then cat_year = 1950. else cat_year = 2000.
+
+; Get X and Y of 4 corners of the image
+
+ if N_elements(xrange) NE 2 then begin    
+      xmin = 0          & xmax =  sxpar(hdr,'NAXIS1') - 1
+ ENDIF ELSE BEGIN
+      xmin = xrange[0]  & xmax = xrange[1]
+ ENDELSE
+
+ if N_elements(yrange) NE 2 then BEGIN
+        ymin=0          & ymax = sxpar(hdr,'NAXIS2') - 1
+ ENDIF ELSE BEGIN
+     ymin = yrange[0]   & ymax = yrange[1]
+ ENDELSE
+
+ x = [xmin,xmax,xmax,xmin]
+ y = [ymin,ymin,ymax,ymax]
+
+; Make sure header has astrometry and convert X,Y to Ra, Dec
+
+ extast, hdr, ASTR, noparams, ALT = alt
+ if noparams LT 0 then message,'Image header does not contain astrometry'
+
+; Compare equinox of image with that of database and precess if necessary
+
+ im_year = GET_EQUINOX(hdr,code)
+ if ( code EQ -1 ) then begin 
+        message,/inf,'EQUINOX keyword not found in header, assumed to be J2000'
+        im_year = 2000.    ;Assume image in 2000 Equinox as default
+ endif
+ if ( im_year NE cat_year ) then begin      ;Need to precess header?
+      hdr1 = hdr
+      hprecess,hdr1,cat_year
+      extast,hdr1, ASTR, noparams, ALT = alt
+ endif 
+
+ proj = strmid(astr.ctype[0],5,3)           ;Astrometric projection type
+
+ case proj of
+         'GSS': gsssxyad, astr, x, y, ra,dec
+         else: xy2ad, x, y, ASTR, ra, dec
+ endcase
+
+ ra = ra/15.                            ;Convert from degrees to hours
+ ramin = min(ra)  & ramax = max(ra)     ;Get max and min RA values
+ decmin = min(dec) & decmax = max(dec)  ;Get max and min Dec values
+ if (ramax - ramin) GT 12 then begin    ;Does the RA cross 24 hours?
+     newmax = ramin
+     ramin = ramax
+     ramax = 24.
+     redo = 1
+endif else redo = 0
+if N_elements(SUBLIST) EQ 0 then sublist = -1
+
+
+ search = strtrim(ramin,2)  + ' < ra < ' + strtrim(ramax,2) + ', ' + $
+         strtrim(decmin,2) + ' < dec < ' + strtrim(decmax,2)
+if ~keyword_set(SILENT) then begin 
+ print,'IMDBASE: Now searching ',catname,' catalogue - be patient'
+ print,search
+endif
+ list = dbfind(search,sublist,/SILENT, Count = nstar)        ;Search for stars in field
+ if redo then begin
+     search = '0 < ra < ' + strtrim(newmax,2) + ', ' + $
+               strtrim(decmin,2) + '< dec <' + strtrim(decmax,2)
+     if ~keyword_set(SILENT) then print,search
+     newlist = dbfind(search,sublist,/SILENT, Count = count)
+     if count GT 0 then list = [list,newlist]
+     nstar = nstar + count
+ endif
+ if ~keyword_set(SILENT) then print,''
+
+ if nstar GT 0 then begin                     ;Any stars found?
+   dbext,list,'ra,dec',ra,dec                ;Extract RA,DEC of stars found
+   ra = ra*15. 
+
+   case proj of
+           'GSS': gsssadxy, astr,ra,dec,x,y
+            else: ad2xy,ra,dec,astr,x,y
+   endcase 
+
+   good = where( (x GT xmin) and ( x LT xmax ) $     ;Select stars within field
+             and (y GT ymin) and ( y LT ymax), ngood)
+   if ngood GT 0 then begin 
+       list = list[good]
+       xpos = x[good] & ypos = y[good]
+      if ~keyword_set(SILENT) then $
+      message,strtrim(ngood,2)+' '+ catname +' sources found within image',/INF
+       if ( N_params() LT 3 ) then dbprint,list,textout=1   ;List stars found
+   endif else GOTO,NO_MATCH
+ endif else GOTO,NO_MATCH
+return
+
+NO_MATCH: message,'No '+ catname + ' sources found within supplied image',/CON
+return
+
+end
diff --git a/Code/script_idl_mv/astrolib/imf.pro b/Code/script_idl_mv/astrolib/imf.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4c0f7e8d764c8eac6bd9c99929a6f60d1b20e577
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/imf.pro
@@ -0,0 +1,129 @@
+function imf, mass, expon, mass_range
+;+
+; NAME:
+;       IMF
+; PURPOSE:
+;       Compute an N-component power-law logarithmic initial mass function 
+; EXPLANTION:
+;       The function is normalized so that the total mass distribution 
+;       equals one solar mass.
+;
+; CALLING SEQUENCE:
+;       psi = IMF( mass, expon,  mass_range )
+;
+; INPUTS:
+;       mass - mass in units of solar masses (scalar or vector)
+;               Converted to floating point if necessary
+;       expon - power law exponent, usually negative, scalar or vector
+;               The number of values in expon equals the number of different
+;               power-law components in the IMF
+;               A Saltpeter IMF has a scalar value of expon = -1.35
+;       mass_range - vector containing the mass upper and lower limits of the 
+;               IMF and masses where the IMF exponent changes.   The number 
+;               of values in mass_range should be one more than in expon.   
+;               The values in mass_range should be monotonically increasing.
+;
+; OUTPUTS
+;       psi - mass function, number of stars per unit logarithmic mass interval
+;               evaluated for supplied masses
+;
+; NOTES:
+;       The mass spectrum f(m) giving the number of stars per unit mass 
+;       interval is related to psi(m) by  m*f(m) = psi(m).    The normalization
+;       condition is that the integral of psi(m) between the upper and lower
+;       mass limit is unity.
+;
+; EXAMPLE:
+;       (1) Print the number of stars per unit mass interval at 3 Msun 
+;               for a Salpeter (expon = -1.35) IMF, with a mass range from 
+;               0.1 MSun to 110 Msun.
+;
+;               IDL> print, imf(3, -1.35, [0.1, 110] ) / 3
+;
+;       (2) Lequex et al. (1981, A & A 103, 305) describes an IMF with an
+;               exponent of -0.6 between 0.007 Msun and 1.8 Msun, and an
+;               exponent of -1.7 between 1.8 Msun and 110 Msun.    Plot
+;               the mass spectrum f(m)
+;
+;               IDL> m = [0.01,0.1,indgen(110) + 1 ]  ;Make a mass vector
+;               IDL> expon = [-0.6, -1.7]       ;Exponent Vector
+;               IDL> mass_range = [ 0.007, 1.8, 110]    ;Mass range
+;               IDL> plot,/xlog,/ylog, m, imf(m, expon, mass_range ) / m
+;
+; METHOD
+;       IMF first calculates the constants to multiply the power-law 
+;       components such that the IMF is continuous at the intermediate masses, 
+;       and that the total mass integral is one solar mass.  The IMF is then 
+;       calculated for the supplied masses.   Also see Scalo (1986, Fund. of
+;       Cosmic Physics, 11, 1)
+;
+; PROCEDURES CALLED:
+;       None
+; REVISION HISTORY:
+;       Written    W. Landsman              August, 1989  
+;       Set masses LE mass_u rather than LT mass_u  August, 1992
+;       Major rewrite to accept arbitrary power-law components   April 1993
+;       Convert EXPON to float if necessary  W. Landsman     March 1996
+;       Remove call to DATATYPE, V5.3 version  W. Landsman  August 2000
+;-
+  On_error,2
+
+ if N_params() LT 3 then begin
+        print,'Syntax - psi = IMF( mass, expon, mass_range)'
+        return,-1
+ endif
+
+  Ncomp = N_elements(expon)
+  if N_elements( mass_range) NE Ncomp + 1 then message, $
+    'ERROR - Mass Range Vector must have ' + strtrim(Ncomp+1,2) + ' components' 
+
+ if ( min(mass_range) LE 0 ) then message, $
+        'ERROR - Mass range Vector must be positive definite'
+
+ npts = N_elements(mass)
+ if ( npts LT 1 ) then begin
+     message, 'Mass vector (first parameter) has not been defined',/CON
+     return,0
+ endif
+
+ if size(mass,/TNAME) NE 'DOUBLE' then mass = float(mass)     ;Make sure not integer
+ if size(expon,/TNAME) NE 'DOUBLE' then expon = float(expon)  
+
+; Get normalization constants for supplied power-law exponents
+
+ integ = fltarr(ncomp)
+
+;Compute the unnormalized integral over each power law section
+
+ for i = 0, Ncomp-1 do begin
+
+ if ( expon[i] NE -1 ) then integ[i] =   $
+    (mass_range[i+1]^(1+expon[i]) - mass_range[i]^(1+expon[i]))/(1+expon[i]) $
+
+ else integ[i] = alog(mass_range[i+1]/mass_range[i])
+
+ endfor 
+
+; Insure continuity where the power law functions meet
+
+ joint = fltarr(ncomp)
+ joint[0] = 1
+ if ncomp GT 1 then for i = 1,ncomp-1 do begin
+           joint[i] = joint[i-1]*mass_range[i]^( expon[i-1] - expon[i] )
+ endfor
+
+ norm = fltarr(ncomp)
+ norm[0] = 1./ total(integ*joint)
+ if ncomp GT 1 then for i = 1,ncomp-1 do norm[i] = norm[0]*joint[i]
+
+ f = mass*0.
+
+ for i = 0, Ncomp-1 do begin
+
+ test = where( (mass GT mass_range[i]) and (mass LE mass_range[i+1]), Ntest ) 
+ if ( Ntest GT 0 ) then f[test] = norm[i]*mass[test]^(expon[i])
+
+ endfor
+
+ return,f
+ end
diff --git a/Code/script_idl_mv/astrolib/imlist.pro b/Code/script_idl_mv/astrolib/imlist.pro
new file mode 100644
index 0000000000000000000000000000000000000000..58e9bd63a9f20455f9036c2532e0d1f567e2dc81
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/imlist.pro
@@ -0,0 +1,231 @@
+pro imlist, image, xc, yc, DX=dx, DY = DY, WIDTH=width, TEXTOUT = textout, $
+                           DESCRIP = descr,OFFSET = offset
+;+
+; NAME:
+;      IMLIST        
+; PURPOSE:
+;      Display pixel values on an image surrounding a specified X,Y center.
+; EXPLANATION:
+;      IMLIST is similar to TVLIST but the center pixel is supplied directly by
+;      the user, rather than being read off of the image display
+;
+; CALLING SEQUENCE:
+;      IMLIST, Image, Xc, Yc, [ TEXTOUT = , DX = , DY = ,WIDTH = ,DESCRIP = ]
+;
+; INPUTS:
+;      Image - Two-dimensional array containing the image 
+;      Xc  -   X pixel value at which to center the display, integer scalar 
+;      Yc -    Y pixel value at which to center the display, integer scalar 
+;
+; OPTIONAL INPUTS KEYWORDS:
+;      TEXTOUT - Scalar number (1-7) or string which determines output device.
+;               (see TEXTOPEN) The following dev/file is opened for output.
+;
+;               textout=1       TERMINAL using /more option
+;               textout=2       TERMINAL without /more option
+;               textout=3       <program>.prt
+;               textout=4       laser.tmp
+;               textout=5       user must open file
+;               textout=7       same as 3 but text is appended to <program>.prt
+;                               if file already exists
+;               textout = filename (default extension of .prt)
+;
+;       DX     -Integer scalar giving the number of pixels inthe  X direction 
+;               to be displayed.  If omitted then DX = 18 for byte images, and 
+;               DX = 14 for integer images.  IMLIST will display REAL data 
+;               with more significant figures if more room is available to 
+;               print.  
+;
+;       DY    - Same as DX, but in Y direction.  If omitted, then DY = DX 
+;       WIDTH - Integer scalar giving the character width of the output device.
+;               Default is 80 characters.
+;       DESCRIP =  Scalar string which will be written as a description over
+;               the output pixel values.   If DESCRIP is not supplied, and the
+;               output device specified by TEXTOUT is not a terminal, then the
+;               user will be prompted for a description.
+;       OFFSET - 2 element numeric vector giving an offset to apply to the 
+;               display of the X,Y coordinates of the image (e.g. if the 
+;               supplied image array is a subarray of a larger image).
+; OUTPUTS:
+;       None.
+;
+; PROCEDURE:
+;       Corresponding region of image is then displayed at
+;       the terminal.   If necessary, IMLIST will divide all pixel values
+;       in a REAL*4 image by a (displayed) factor of 10 to make a pretty format.
+;
+; SYSTEM VARIABLES:
+;       If the keyword TEXTOUT is not supplied, then the non-standard system
+;       variable !TEXTOUT will be read.    (The procedure ASTROLIB is used
+;       to add the non-standard system variable if not already present.)
+;
+; RESTRICTIONS:
+;       IMLIST may not be able to correctly format all pixel values if the
+;       dynamic range of the values near the center pixel is very large
+;
+; EXAMPLE:
+;       Display the pixel values of an image array IM in the vicinity of 254,111
+;
+;       IDL> imlist, IM, 254, 111
+;
+; PROCEDURES USED
+;       TEXTOPEN, F_FORMAT(), TEXTCLOSE
+; REVISION HISTORY:
+;       Written,    W. Landsman             June, 1991
+;       Added DESCRIP keyword    W. Landsman      December, 1991
+;       Treat LONG image as integer when possible, call TEXTOPEN with /STDOUT
+;       keyword, W. Landsman   April, 1996
+;       Use SYSTIME() instead of !STIME  August 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Recognize new integer types, added OFFSET keyword  W. Landsman Jan. 2000
+;       Replace DATATYPE() with size(/TNAME)  W. Landsman Nov. 2001
+;       Handle NAN values in output display W. Landsman June 2004
+;       Use V6.0 notation  W. Landsman April 2011
+;-
+ On_error,2                                   ;Return to caller
+ compile_opt idl2
+
+ if N_params() LT 3 then begin
+    print,'Syntax - IMLIST, Image, Xc, Yc, [TEXTOUT= ,DX=, DY=, WIDTH= ,DESC= ]'
+    print,'        Image - Any IDL numeric 2-d array'
+    print,'        Xc, Yc - X,Y of center pixel of region to display'
+    return
+ endif
+
+  defsysv,'!TEXTOUT',exists=ex			; Check if !TEXTOUT exists.
+  if ex eq 0 then defsysv,'!TEXTOUT',1		; If not define it.
+  defsysv,'!TEXTUNIT',exists=ex			; Check if !TEXTUNIT exists.
+  if ex eq 0 then defsysv,'!TEXTUNIT',0		; If not define it.
+
+ if N_elements( TEXTOUT ) EQ 0 then textout = !TEXTOUT      ;Use default
+ if N_elements( OFFSET) NE 2 then offset = [0,0]
+
+ if size( TEXTOUT,/TNAME ) NE 'STRING' then begin
+        textout = textout > 2                  ;Don't use /MORE
+        hardcopy =  (textout GE 3) && (textout NE 5) 
+ endif else hardcopy = 1
+
+ defsysv,'!TEXTUNIT',exist=i
+ if i EQ 0 then astrolib
+ textopen, 'IMLIST', TEXTOUT = textout, /STDOUT            ;Open output device
+
+ sz = size(image)
+ if (sz[0] LT 2) || (sz[sz[0]+2] NE sz[1]*sz[2]) then $
+                message,'Image array (first parameter) not 2-dimensional'
+
+ type = sz[ sz[0] + 1 ]      ;Byte or Integer or Float image?
+
+ if hardcopy then begin   ;Direct output to a disk file
+        printf,!TEXTUNIT,'IMLIST: ' + strmid(systime(),4,20)
+        if ~keyword_set( DESCR ) then begin
+           descr = ''
+           read,'Enter a brief description to be written to disk: ',descr
+        endif
+        printf,!TEXTUNIT,descr
+        printf,!TEXTUNIT,' '
+ endif                 
+
+ xdim = sz[1] - 1 
+ ydim = sz[2] - 1 
+
+; Make sure supplied center pixel is actually within image
+
+ if (xc LT 0) || (xc GT xdim) then $
+        message,'ERROR - X pixel center must be between 0 and '+strtrim(xdim,2)
+ if (yc LT 0) || (yc GT ydim) then $
+        message,'ERROR - Y pixel center must be between 0 and '+strtrim(ydim,2)
+
+ xim = round(xc)
+ yim = round(yc)
+ if ~keyword_set( WIDTH ) then width = 80
+
+ case type of
+ 1: fmtsz = 4
+ 2: fmtsz = 6
+12: fmtsz = 6
+else: fmtsz = 5
+endcase
+
+ if ~keyword_set(DX) then dx = fix((width - 5)/fmtsz)
+ if ~keyword_set(DY) then dy = dx
+
+; Don't try to print outside the image
+  xmax = (xim + dx/2) < xdim
+  xmin = (xim - dx/2) > 0 
+  ymax = (yim + dy/2) < ydim
+  ymin = (yim - dy/2) > 0 
+
+ dx = xmax - xmin + 1 & dy = ymax - ymin + 1
+ if fmtsz EQ 5 then  fmtsz = ( width-4 ) / dx
+ sfmt = strtrim( fmtsz,2 )
+ cdx = string(dx,'(i2)')
+ flt_to_int = 0               ;Convert floating point to integer?
+
+
+; For Integer and Byte datatypes we already know the best output format
+; For other datatypes the function F_FORMAT is used to get the best format
+; If all values of a LONG image can be expressed with 5 characters 
+; (-9999 < IM < 99999) then treat as an integer image.
+REDO:
+ case 1 of                                    ;Get proper print format
+
+   type EQ 1:  fmt = '(i4,' + cdx + 'i' + sfmt + ')'           ;byte
+
+   (type EQ 2):   fmt = '(i4,' + cdx + 'i' + sfmt + ')'        ;Integer
+   (type EQ 12):  fmt = '(i4,1x,' + cdx + 'i' + sfmt + ')'     ;Unsigned Integer
+
+   (type EQ 4) || (type EQ 3) || (type EQ 5) || (type GE 13):  begin      ;Long, Real or Double
+
+      temp = image[ xmin:xmax,ymin:ymax ]
+      minval = min( temp, MAX = maxval, /nan)
+      if (type EQ 3) || (type GE 13) then  begin
+
+                if (maxval LT 999.) && (minval GT -99.) then begin
+                type = 1 & sfmt = '4'
+                 goto, REDO
+                endif
+                if (maxval LT 9999.) && (minval GT -999.) then begin
+                type = 12 & sfmt = '5'
+                goto, REDO
+                endif
+                if (maxval LT 99999.) && (minval GT -9999.) then begin
+                type = 2  & sfmt = '6'
+                goto, REDO
+                endif
+      endif
+
+      realfmt = F_FORMAT( minval, maxval, factor, fmtsz )
+      if strmid(realfmt,0,1) EQ 'I' then flt_to_int = 1
+      fmt = '(i4,1x,' + cdx + realfmt + ')'
+      if factor NE 1 then $                                   
+       printf,!TEXTUNIT,form='(/,A,E7.1,/)',' IMLIST: Scale Factor ',factor
+
+      end
+
+    else: message,'ERROR - Unrecognized data type'
+ endcase 
+
+; Compute and print x-indices above array
+
+ index = indgen(dx) + xmin + offset[0]
+
+ if type NE 1 then $
+     printf,!TEXTUNIT,form='(A,'+ cdx + 'i' + sfmt + ')',' col ',index  $
+ else printf,!TEXTUNIT,form='(A,'+ cdx + 'i' + sfmt + ')',' col',index
+
+ printf,!TEXTUNIT,'$(A)',' row'
+ for i = ymax,ymin,-1 do begin  ;list pixel values
+
+        row = image[i*sz[1]+xmin:i*sz[1]+xmax]      ;from supplied image array
+        if type EQ 1 then row = fix(row)
+        if (type EQ 4) || (type EQ 3) || (type EQ 5) || (type GE 13) then $
+                   row = row/factor
+        if flt_to_int then row = round( row )
+        printf, !TEXTUNIT, FORM = fmt, i + offset[1], row
+
+ endfor
+ 
+ textclose, TEXTOUT=textout
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/irafdir.pro b/Code/script_idl_mv/astrolib/irafdir.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8b064eb2e289953f1a6222ac93c5079bd6168f0c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/irafdir.pro
@@ -0,0 +1,185 @@
+pro irafdir,directory,TEXTOUT=textout
+;+
+; NAME:
+;	IRAFDIR
+; PURPOSE:
+;	Provide a brief description of the IRAF images on a directory
+; CALLING SEQUENCE:
+;	IRAFDIR, [ directory, TEXTOUT = ]
+;
+; OPTIONAL INPUT PARAMETERS:
+;	DIRECTORY - Scalar string giving file name, disk or directory to 
+;		be searched 
+;
+; OPTIONAL INPUT KEYWORD:
+;	TEXTOUT - specifies output device (see TEXTOPEN)
+;		textout=1	TERMINAL using /more option
+;		textout=2	TERMINAL without /more option
+;		textout=3	<program>.prt
+;		textout=4	laser.tmp
+;		textout=5      user must open file
+;		textout=7       Append to existing <program>.prt file
+;		textout = 'filename' (default extension of .prt)
+;
+; OUTPUT PARAMETERS:
+;	None
+;
+; PROCEDURE:
+;	FINDFILE is used to find all '.imh' files in the directory. 
+;	The object name and image size (NAXIS1, NAXIS2) are extracted
+;	from the header. Each header is also searched for the parameters
+;	DATE-OBS (or TDATEOBS), TELESCOP (or OBSERVAT), EXPTIME.
+;  
+; RESTRICTIONS:
+;	(1) Some fields may be truncated since IRAFDIR uses a fixed format 
+;		output
+;	(2) No more than 2 dimension sizes are displayed 
+; SYSTEM VARIABLES:
+;	If 'textout' keyword is not specified to select an output device,
+;	!TEXTOUT will be the default.    This non-standard system variable
+;	can be added using the procedure ASTROLIB.
+;
+; PROCEDURE CALLS:
+;	EXPAND_TILDE(), FDECOMP, REMCHAR, TEXTOPEN, TEXTCLOSE
+; MODIFICATION HISTORY:
+;	Written, K. Venkatakrishna, ST Systems Corp, August 1991
+;	Work for IRAF V2.11 format   W. Landsman   November 1997
+;	Assume since V5.5 use file_search W. Landsman   Sep 2006
+;-
+
+ On_error,2                          ;Return to caller
+ 
+ ext='*.imh'
+
+ defsysv,'!TEXTUNIT',exist=i
+ if i EQ 0 THEN astrolib
+ if keyword_set(directory) then begin 
+	dir = strlowcase(directory)
+	if strpos(dir,'~') GE 0 then dir = expand_tilde(dir)
+ endif
+
+ if N_ELEMENTS(dir) eq 0 then cd,current = dir
+
+ dir = dir + path_sep()
+
+ fil = file_search( dir + ext, COUNT=nfiles)
+ if nfiles EQ 0 then begin
+    message,'No IRAF (*.imh) files found ',/CON
+    return
+ endif
+
+; Set output device according to keyword TEXTOUT or system variable !TEXTOUT
+
+ if not keyword_set(textout) then textout=!textout
+ textopen,'irafdir',TEXTOUT=textout
+
+;  Print the title header
+ printf,!textunit,format='(a,/)','IRAF file directory  '+strmid(systime(),4,20)
+ printf,!textunit,$
+'       NAME         SIZE     OBJECT      DATE-OF-OBS     TELESCOP      EXP TIME'
+
+ get_lun,lun1
+ fmt = '(a15,1x,i5,1x,i5,2x,a10,4x,a8,7x,a8,5x,a8)'
+ dir2 = 'dummy'
+ for i=0,nfiles-1 do begin                          ;Loop over each .imh file
+   file1 = fil[i]                                       
+   fdecomp,file1,disk,dir2,fname,qual             ;Decompose into disk+filename
+   openr,lun1,file1,/stream                       ;open the file
+   irafver = bytarr(5)
+   readu,lun1,irafver
+   newformat = string(irafver) EQ 'imhv2'
+   point_lun,lun1,0
+   tmp = assoc(lun1,bytarr(32))
+   hdr = tmp[0]
+ 
+   exptim ='    ?   '                ;Set default values
+   telescop = '   ?      '
+   date = '   ?      '
+
+ if not newformat then begin
+   hdr2 = hdr                                         ;Read the first 572 bytes
+   byteorder,hdr,/sswap                               ; Perform byte swaps
+   byteorder,hdr,/lswap 
+   hdrlen = fix(hdr,12)                               ;Extract header length,
+   ndim = fix(hdr,20)                                 ; number of dimensions,
+   naxis1 = long(hdr2,24)                             ; dimension vector
+   naxis2 = long(hdr2,28)
+   if hdrlen EQ 0 then begin
+		close,lun1
+		goto, PRINTER
+   endif
+   tmp1 = assoc(lun1,bytarr(hdrlen*4l,/NOZERO))               
+   hdr = tmp1[0]                                     ;Read the entire header
+   close,lun1
+   byteorder,hdr,/sswap  ;
+   nfits = (hdrlen*4l-2054)/162                     ; find the number of records
+   linelen = 162
+   index = 2052l + indgen(80)*2
+  
+ endif else begin
+
+   hdrlen = fix(hdr,8)                               ;Extract header length,
+   ndim = fix(hdr,20)                                 ; number of dimensions,
+   naxis1 = long(hdr,22)                             ; dimension vector
+   naxis2 = long(hdr,26)
+   tmp1 = assoc(lun1,bytarr(hdrlen*2l,/NOZERO))               
+   hdr = tmp1[0]                                     ;Read the entire header
+   close,lun1
+   nfits = (hdrlen*2l-2049)/81                     ; find the number of records
+   linelen = 81
+   index = 2046l + indgen(80)
+  endelse
+
+; Form the string 'hd', 
+; hd will be a FITS style header, that contains all the basic information 
+
+ if nfits EQ 0 then goto, PRINTER
+ hd = strarr(nfits)                              ; to break the header into
+ for j = 0l,nfits-1 do hd[j] = string(hdr[linelen*j + index] )   
+ 
+
+   keyword = strtrim( strmid(hd,0,8),2 )
+   value = strtrim( strmid(hd,10,20),2 )
+ l = where(keyword EQ 'TELESCOP',nfound)           ;Search for OBSERVAT keyword
+ if nfound EQ 0 then l = where(keyword EQ 'OBSERVAT', nfound)
+ if nfound GT 0 then begin
+      telescop = value[l[0]]
+      remchar,telescop,"'"
+ endif 
+
+ l = where(keyword EQ 'EXPTIME',nfound)           ;Search for EXPTIME keyword
+ if nfound GT 0 then begin
+    exptim = float(value[l[0]])
+    if exptim EQ 0. then exptim = '   ?      ' else $
+                 exptim = string(exptim,format= '(f7.1)')     
+ endif 
+
+ l = where(keyword EQ 'DATE-OBS' ,nfound)       ;Search for DATE-OBS keyword 
+ if nfound EQ 0 then l = where(keyword EQ 'TDATEOBS', nfound)
+ if nfound GT 0 then begin
+   date=value[l[0]]
+   remchar,date,"'"
+ endif 
+
+;Extract object name
+PRINTER:
+ if newformat then object = string( hdr[638 + indgen(8)]) else $
+		   object = string( hdr[732 + indgen(8)*2]) 
+
+ if dir2 NE dir then begin			;Has directory changed?   
+       if ( dir2 EQ '' ) then cd,current=dir else dir = dir2
+       printf,!textunit,format='(/a/)',disk+dir   ;Print new directory
+       dir = dir2                                  ;Save new directory
+ endif                   
+;                                                  original header 
+
+ printf,!textunit,FORMAT=fmt,fname,naxis1,naxis2,object,date,telescop,exptim
+ if textout EQ 1 then if !ERR EQ 1 then return
+ endfor
+
+ textclose, TEXTOUT=textout
+ free_lun, lun1
+
+ return
+ end
+
diff --git a/Code/script_idl_mv/astrolib/irafrd.pro b/Code/script_idl_mv/astrolib/irafrd.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c4d18bab7887a8e40116afb014d6a074ecdb7956
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/irafrd.pro
@@ -0,0 +1,300 @@
+pro irafrd,im,hd,filename, SILENT=silent    ;Read in IRAF image array and header array
+;+
+; NAME:
+;     IRAFRD
+; PURPOSE:
+;       Read an IRAF (.imh) file into IDL image and header arrays.
+; EXPLANATION:
+;       The internal IRAF format changed somewhat in IRAF V2.11 to a machine
+;       independent format, with longer filename allocations.  This version 
+;       of IRAFRD should be able to read either format. 
+;
+; CALLING SEQUENCE:
+;       IRAFRD, im, hdr, filename, [/SILENT ]  
+;
+; OPTIONAL INPUT:
+;       FILENAME -  Character string giving the name of the IRAF image 
+;               header.  If omitted, then program will prompt for the 
+;               file name.  IRAFRD always assumes the header file has an 
+;               extension '.imh'.    IRAFRD will automatically locate the
+;               ".pix" file containing the data by parsing the contents of 
+;               the .imh file.   (If the parse is unsuccesful, then IRAFRD looks
+;               in the same directory as the .imh file.)
+; OUTPUTS:
+;       IM - array containing image data
+;       HDR - string array containing header.  Basic information in the
+;               IRAF header is converted to a FITS style header
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /SILENT  - If this keyword is set and non-zero, then messages displayed
+;               while reading the image will be suppressed.  
+;
+; RESTRICTIONS:
+;       (1)  Image size and history sections of the IRAF header are copied 
+;               into the FITS header HDR.  Other information (e.g. astrometry)
+;               might not be included unless it is also in the history section
+;       (2)  IRAFRD ignores the node name when deciphering the name of the
+;               IRAF ".pix" file.
+;       (3)  Certain FITS keywords ( DATATYPE, IRAFNAME) may appear more than
+;               once in the output name
+;       (4)  Does not read the DATE keyword for the new (V2.11) IRAF files
+; NOTES:
+;       IRAFRD obtains dimensions and type of image from the IRAF header.
+;
+; PROCEDURES CALLED:
+;       FDECOMP, SXADDPAR, SXPAR()
+;
+; MODIFICATION HISTORY:
+;       Written W. Landsman, STX January 1989
+;       Converted to IDL Version 2.  M. Greason, STX, June 1990
+;       Updated for DecStation compatibility   W. Landsman   March 1992
+;       Don't leave an open LUN  W. Landsman   July 1993
+;       Don't overwrite existing OBS-DATE  W. Landsman  October 1994
+;       Don't bomb on very long FITS headers W. Landsman  April 1995
+;       Work on Alpha/OSF and Linux      W. Landsman     Dec 1995
+;       Remove /VMSIMG keyword, improve efficiency when physical and
+;               image dimensions differ   W. Landsman     April 1996
+;       Don't use FINDFILE (too slow)     W. Landsman     Oct 1996
+;       Read V2.11 files, remove some parameter checks W. Landsman Nov. 1997
+;       Fixed problem reading V2.11 files with long headers Jan. 1998
+;       Accept names with multiple extensions    W. Landsman   April 98 
+;       Test for big endian machine under V2.11 format W. Landsman Feb. 1999
+;       Don't read past the end of file for V5.4 compatilibity  W.L.  Jan. 2001
+;       Convert to square brackets W.L   May 2001
+;       Assume since V5.4, remove SPEC_DIR()   W. L.   April 2006
+;-
+ On_error,2                    ;Return to caller
+ compile_opt idl2
+ npar = N_params() 
+
+ if ( npar EQ 0 ) then begin 
+   print,'Syntax - IRAFRD, im, hdr, [filename, /SILENT ]'
+   return
+ endif 
+
+ if ( npar EQ 3 ) then $
+    if ( N_elements(filename) EQ 0 ) then message, $
+        'Third parameter (IRAF Header file name) must be a character string' $
+    else begin  
+         file_name = filename
+         goto,FINDER
+    endelse 
+
+  file_name = ''  ;Get file name if not supplied
+  read,'Enter name of IRAF data file (no quotes): ',file_name    
+  if ( file_name EQ '' ) then return
+
+FINDER: 
+  fdecomp, file_name, disk, dir, name, ext, ver  
+
+  IF ext EQ 'imh' THEN fname = file_name ELSE fname = file_name + '.imh'
+
+  openr, lun1, fname, /GET_LUN, ERROR = error  ;Open the IRAF header file
+  if error NE 0 then  $
+    message, 'Unable to find IRAF header file '+ FILE_EXPAND_PATH(fname) 
+
+; Get image size and name from IRAF header
+ irafver = bytarr(5)
+ readu, lun1, irafver
+ newformat = string(irafver) EQ 'imhv2' 
+ big_endian = is_ieee_big()
+
+ if newformat then begin
+        hdrsize = 2048
+        doffset = 2048
+ endif else begin
+        hdrsize = 572
+        doffset = 1024
+ endelse 
+
+ point_lun, lun1, 0             ;Back to top of the header
+ tmp = assoc(lun1,bytarr(hdrsize))
+ hdr = tmp[0]
+ hdr2 = hdr
+
+ if not newformat then begin       ;Old format is not machine independent
+
+        if not big_endian then begin
+                byteorder,hdr,/sswap
+                byteorder,hdr,/lswap
+        endif
+
+        hdrlen =   fix(hdr,12)         ;Length (in words) of header
+        datatype = fix(hdr,16)         ;IRAF datatype
+        ndim =  fix(hdr,20)         ;Number of dimensions
+        if ( ndim GT 5 ) then $
+                message,'Too stupid to do more than 5 dimensions'
+        if (ndim EQ 0) then message,'IRAF file contains no data (NAXIS = 0)'
+
+        dimen = long(hdr2,24,ndim)       ;Get vector of image dimensions 
+        physdim = long(hdr2,52,ndim)     ;Get vector of physical dimensions
+
+        if big_endian then pixname = string( hdr[412+indgen(80)*2] ) else $
+                           pixname = string( hdr2[413+indgen(80)*2] )
+ endif else begin
+
+        hdrlen =   long(hdr,6)         ;Length (in words) of header
+        datatype = fix(hdr,12)         ;IRAF datatype
+        ndim =   fix(hdr,20)         ;Number of dimensions
+        if big_endian then begin
+              byteorder,hdrlen,/NTOHL
+              byteorder,datatype,/NTOHS
+              byteorder,ndim,/NTOHS
+        endif
+        if ( ndim GT 7 ) then $
+                message,'Too stupid to do more than 7 dimensions'
+        if (ndim EQ 0) then message,'IRAF file contains no data (NAXIS = 0)'
+
+        dimen =  long(hdr,22,ndim)       ;Get vector of image dimensions 
+        physdim = long(hdr,50,ndim)     ;Get vector of physical dimensions
+        if big_endian then begin
+               byteorder,dimen,/NTOHL
+               byteorder,physdim, /NTOHL
+        endif
+        pixname = string(hdr[126:126+255])
+ endelse 
+
+ expos = strpos(pixname,'!')
+ pixname = strmid(pixname,expos+1,strlen(pixname))
+
+ expos = strpos(pixname,'!')
+ pixname = strmid(pixname,expos+1,strlen(pixname))
+
+ if strmid(pixname,0,4) eq 'HDR$' then begin
+        if disk + dir EQ '' then begin 
+                cd, CURRENT = curdir 
+                curdir = curdir + path_sep()
+        endif else curdir = disk+dir
+        pixname = curdir +  strmid(pixname,4,strlen(pixname))
+ endif
+
+;  Use file name found in header to open .pix file.  If this file is not
+;  found then look for a .pix file in the same directory as the header  
+ 
+ openr, lun2, pixname, ERROR=err, /GET_LUN     ; ...on given directory
+
+ if ( err LT 0 ) then begin
+     openr,lun2, name + '.pix', ERROR = err, /GET_LUN   
+     if ( err LT 0 ) then goto, NOFILE   
+ endif 
+
+ if ~keyword_set(SILENT) then begin 
+                                            
+        sdim = strtrim(dimen[0],2)
+        message,'Now reading '+strjoin(sdim,' by ')  + $
+                 ' IRAF array', /INFORM
+ endif 
+
+;       Convert from IRAF data types to IDL data types
+
+ CASE datatype OF
+        1: begin & dtype = 1  & bitpix = 8 & end            ;Byte 
+        3: begin & dtype = 2  & bitpix = 16 & end            ;Integer*2 
+        4: begin & dtype = 3  & bitpix = 32 & end            ;Integer*4 
+        5: begin & dtype = 3  & bitpix = 32 & end            ;Integer*4 
+        6: begin & dtype = 4  & bitpix = -32 & end           ;Real*4 
+        7: begin & dtype = 5  & bitpix = -64 & end            ;Real*8 
+        11: begin &dtype = 3  & bitpix = 16 & end            ;Integer*2
+        else: message,'Unknown Datatype Code ' + strtrim(datatype,2)
+ endcase 
+
+; Read the .pix file, skipping the first 1024 bytes.   The last physical 
+; dimension can be set equal to the image dimension.
+
+ physdim[ndim-1] = dimen[ndim-1]
+ tmp = assoc (lun2, make_array(DIMEN = physdim, TYPE= dtype, /NOZERO), doffset)
+ im = tmp[0]
+
+; If the physical dimension of an IRAF image is larger than the image size,
+; then extract the appropriate subimage
+
+ dimen = dimen - 1
+ pdim = physdim - 1
+ case ndim of
+        1 :
+        2 : if dimen[0] LT pdim[0] then im = im[ 0:dimen[0], *]
+        3 : if total(dimen LT pdim) then im = im[ 0:dimen[0], 0:dimen[1], * ]
+        4 : if total(dimen LT pdim) then $
+                im = im[ 0:dimen[0], 0:dimen[1], 0:dimen[2], * ]
+        5 : if total(dimen LT pdim) then $
+                im = im[ 0:dimen[0], 0:dimen[1], 0:dimen[2], 0:dimen[3], *]
+        6:  if total(dimen LT pdim) then $
+                im = im[ 0:dimen[0], 0:dimen[1], 0:dimen[2], 0:dimen[3], $
+                         0:dimen[4], *]
+        7: if total(dimen LT pdim) then $
+                im = im[ 0:dimen[0], 0:dimen[1], 0:dimen[2], 0:dimen[3], $
+                         0:dimen[4], 0:dimen[5], *]
+ endcase
+
+ hd = strarr(ndim + 5) + string(' ',format='(a80)')      ;Create empty FITS hdr
+ hd[0] = 'END' + string(replicate(32b,77))
+  
+ sxaddpar, hd, 'SIMPLE', 'T',' Read by IDL:  '+ systime()
+ sxaddpar, hd, 'BITPIX', bitpix
+ sxaddpar, hd, 'NAXIS', ndim        ;# of dimensions
+ if ( ndim GT 0 ) then $
+   for i = 1, ndim do sxaddpar,hd,'NAXIS' + strtrim(i,2),dimen[i-1]+1
+
+ sxaddpar,hd,'irafname',name + '.imh'   ;Add history records
+
+ if ( hdrlen GT 513 ) then begin    ;Add history records
+
+        if newformat then nfits = (hdrlen*2l - 2049)/81 else $
+                          nfits = (hdrlen*4l - 2054)/162
+        tmp = assoc(lun1,bytarr(hdrlen*4l < (fstat(lun1)).size ))
+        hdr = tmp[0]
+        if not newformat then if not big_endian then byteorder, hdr, /SSWAP 
+SKIP1:  
+        if newformat then $
+                object = string( hdr[638 + indgen(67)] ) else $
+                object = string( hdr[732 + indgen(67)*2] ) 
+        if (object NE '') then $
+        sxaddpar, hd, 'OBJECT', object,' Object Name'     ;Add object name
+
+        endline = where( strmid(hd,0,8) EQ 'END     ')     
+        endline = endline[0]
+        endfits = hd[endline]
+        hd = [ hd[0:endline-1], strarr(nfits+1) ]
+
+        if newformat then begin
+                index = indgen(80)
+                for i = 0l,nfits-1 do $
+                        hd[endline+i] = string( hdr[2046 + 81*i + index] )
+        endif else begin 
+                index = indgen(80)*2
+                for i = 0l,nfits-1 do $
+                        hd[endline+i] = string( hdr[ 2052 + 162*i + index] )
+        endelse
+
+        hd[endline + nfits] = endfits         ;Add back END keyword
+        
+        if not newformat then begin
+        history = string(hdr[ 892 + indgen(580)*2] )
+        st1 = gettok( history, string(10B))             
+        if big_endian then $
+                origin = gettok( strmid( st1, 1, strlen(st1)),"'") else $
+                origin = gettok( strmid( st1, 0, strlen(st1)),"'")
+        sxaddpar, hd, 'ORIGIN', origin, ' ', 'IRAFNAME'   ; Add 'ORIGIN" record
+
+        test = sxpar(hd,'HISTORY', Count = N)
+        if N EQ 0 then begin
+         while (strpos(history,string(10B)) GE 0) do begin
+
+                 hist_rec = gettok( history, string(10B) ) ; Add history comment strings
+                 sxaddpar, hd, 'HISTORY', hist_rec
+         endwhile
+       endif
+        endif
+ endif
+
+ free_lun,lun1,lun2
+
+ return                        ;Successful return
+
+NOFILE:  
+
+ message,'Unable to find IRAF pixel file ' + pixname,/CON
+ free_lun,lun1
+ return
+
+ end 
diff --git a/Code/script_idl_mv/astrolib/irafwrt.pro b/Code/script_idl_mv/astrolib/irafwrt.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c4609f3c36372b4e8609a07df124505e77eda9e8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/irafwrt.pro
@@ -0,0 +1,249 @@
+pro irafwrt, image, hd, filename, PIXDIR = pixdir
+;+
+; NAME:
+;     IRAFWRT
+; PURPOSE:
+;     Write IDL data in IRAF (OIF) format (.imh and .pix files).
+; EXPLANATION:
+;     Does the reverse of IRAFRD.    IRAFWRT writes the "old" IRAF format
+;     used prior to v2.11.   However, this "old" format is still readable by
+;     the current version of IRAF.
+;
+; CALLING SEQUENCE: 
+;    IRAFWRT, image, hdr, filename, [ PIXDIR = ]
+;
+; INPUTS:
+;     image - array containing data
+;     hdr   - The  corresponding FITS header.   Use MKHDR to create a minimal
+;             FITS header if one does not already exist.
+;     filename - Scalar string giving the name of the file to be written 
+;             Should not include the extension name, which will be supplied 
+;             by IRAFWRT.
+; OUTPUTS:
+;     None
+;
+; OPTIONAL KEYWORD INPUT:
+;      PIXDIR - scalar string specifying the directory into which to write
+;              the IRAF pixel (.pix) file.   The default is to write the pixel
+;              file to the same directory as the header (.imh) file
+;
+; SIDE EFFECTS:
+;      Image array and  FITS header are written to IRAF pixel file 
+;               'filename'.pix and header file 'filename'.imh
+;
+; EXAMPLE:
+;       Write an empty 50 x 50 array of all zeros to an IRAF file named 'EMPTY'
+;
+;       IDL> im = intarr( 50, 50)         ;Create empty array
+;       IDL> mkhdr, hdr, im               ;Create a minimal FITS header
+;       IDL> irafwrt, im, hdr, 'empty'    ;Write to a IRAF file named 'empty'
+;
+; PROCEDURE:
+;       IRAFWRT gets information about the data - image dimensions, size, 
+;       datatype, maximum and minimum pixel values - and writes it into
+;       the binary part of the header. The ASCII part of the header
+;       is directly copied after deleting records with certain keywords 
+;       A pixel file is created, with a header in the first 1024 bytes
+;
+; RESTRICTIONS:
+;       (1) The files are not created by IRAFWRT are not identical to those 
+;               created by the IRAF routine rfits.    However, the files 
+;               created by IRAFWRT appear to be compatible with all the IRAF 
+;               routines tested so far.
+;       (2)  IRAFWRT has been tested on a limited number of data types
+;       (3)  IRAFWRT has only been tested on Unix and VMS systems.
+;
+; PROCEDURES CALLED:
+;       FDECOMP, IS_IEEE_BIG(), ISARRAY(), REPCHR(), STRN(), SXDELPAR, SXPAR()
+; MODIFICATION HISTORY:
+;       Written K. Venkatakrishna, STX February 1992
+;       VMS compatibility    W. Landsman      April 1992
+;       Work with headers without DATE-OBS or ORIGIN           August 1992
+;       Preserve HISTORY records with other FITS records       March 1995    
+;       Fix case where a minimal FITS header supplied          August 1995
+;       Work under Alpha/OSF and Linux                         Dec.   1995
+;       Make sureheader has 80 char lines, use IS_IEEE_BIG()   May    1997
+;       Don't apply strlowcase to .pix name   W. Landsman      April 1999
+;       Work with double precision            W. Landsman      May 1999
+;       Minimize use of obsolete !ERR         W. Landsman      Feb. 2000
+;       Assume since V5.5, remove VMS support W. Landsman       Sep. 2006
+;-     
+ On_error,2
+
+ if N_params() LT 3 then begin
+        print,'Syntax - IRAFWRT, image, header, filename, [PIXDIR = ]'
+        return
+  endif
+;
+; Get the dimensions, vector of dimensions and the data type
+
+  imsize  =  size(image) 
+  naxis = imsize[0]
+  imdim = imsize[1:naxis]
+  type = imsize[naxis+1]
+  im_max = max(image,min=im_min)  ; find the minimum and maximum pixel values
+
+  case type of
+  1: datatype = 1
+  2: datatype = 3
+  3: datatype = 4
+  4: datatype = 6
+  5: datatype = 7
+  else: message,'ERROR - Input data type is currently unsupported'
+  endcase
+
+  fname = filename
+
+  big_endian = is_ieee_big()
+
+ header = fname+'.imh'
+ openw, lun1, header, /GET_LUN
+
+ object = sxpar( hd, 'OBJECT',Count = N_object)
+ if ( N_object EQ 0 ) or ( object EQ '' ) then object = ' '
+ origin = sxpar( hd, 'ORIGIN', Count = N_origin)
+ if ( N_origin EQ 0 ) or ( origin EQ '') then origin = ' '
+ date_obs = sxpar( hd, 'DATE-OBS', Count = N_date ) 
+ if ( N_date EQ 0 ) or ( date_obs EQ '')  then date_obs = ' '
+
+ hist_rec = where(strpos(hd,'HISTORY') EQ 0, Nhist)        ; Get history records
+ if Nhist GT 0 then history = hd[hist_rec] else $
+                    history = ' '
+
+;Copy header to new variable and leave original variable unmodified
+ xhdr = hd                   
+
+ delete_rec = ['SIMPLE', 'BITPIX', 'NAXIS ', 'NAXIS1', 'NAXIS2', 'DATATYPE', $
+            'OBJECT', 'ORIGIN', 'BSCALE', 'BZERO', 'GROUPS', $
+            'IRAFNAME', 'END']
+
+ sxdelpar, xhdr, delete_rec
+
+ nmax = N_elements(xhdr)
+ bhdr = replicate(32b, 80, nmax)         ;Make sure it is 80 bytes
+ for i = 0l,nmax-1 do bhdr[0,i] = byte(xhdr[i])
+
+ if isarray(xhdr) then $
+         hdrlen = (nmax*162 + 2056)/4 $
+    else hdrlen = 514
+
+ hdr = bytarr(hdrlen*4)             ; Create header array
+
+ inp = [ fix(hdrlen), fix(datatype), fix(naxis)]
+
+ buf = bytarr(1024)
+ hdr[12] = byte(inp,0,2)            ; write header length, data type
+ hdr[16] = byte(inp,2,2)            ; and number of dimensions into
+ hdr[20] = byte(inp,4,2)            ; header
+ buf[20] = byte(inp,4,2)
+;
+; find current time in seconds wrt Jan-01-80 00:00:00
+;
+ time_creat = systime(2)-315550800.
+ if big_endian then byteorder, hdr, /LSWAP   
+
+ min = strn(im_min,format = '(E13.6)')
+ max = strn(im_max,format = '(E13.6)') 
+ max_rec_pos = where(strpos(xhdr,'IRAF-MAX = ') EQ 0)
+ min_rec_pos = where(strpos(xhdr,'IRAF-MIN = ') EQ 0)
+ if (max_rec_pos[0] GE 0) then begin
+            max_rec = xhdr[max_rec_pos[0]]   ; write maximum
+            min_rec = xhdr[min_rec_pos[0]]   ; and minimum pixel
+            strput,max_rec,max,18        ; values
+            strput,min_rec,min,18
+            xhdr[max_rec_pos[0]] = max_rec
+            xhdr[min_rec_pos[0]] = min_rec
+ end
+;
+; write the ascii part of the header
+;
+  if hdrlen GT 514 then $
+        for i = 0, nmax-1 do begin
+            hdr[ 2052 + 162L*i + lindgen(80)*2]  =  bhdr[*,i]
+            hdr[2052+162L*i+160] = 10B
+        endfor
+
+  if big_endian then byteorder,hdr,/SSWAP
+  if not big_endian then offset = 0 else offset = 1
+  hdr[ 732 + indgen(strlen(object))*2+offset] = byte(object)
+  hdr[indgen(5)*2 + offset] = byte('imhdr')
+  hdr[24] = byte(imdim,0,4*naxis)
+  buf[24] = byte(imdim,0,4*naxis)
+  hdr[52] = byte(imdim,0,4*naxis)
+  hdr[120] = byte(im_max,0,4)
+  hdr[124] = byte(im_min[0],0,4)
+  cd,current = dir
+
+     host = getenv('HOST')
+    dir  =  dir + path_sep()
+ 
+  if keyword_set(pixdir) then dir = pixdir
+  pixname = host+'!' + dir + fname + '.pix'
+  len1 = strlen(pixname)
+  len2 = strlen(header)
+  hdr[ 412 + offset + indgen(len1[0])*2] = byte(pixname)   ; write pixel file location    
+  hdr[ 572 + offset + indgen(len2[0])*2] = byte(header)    ; into header
+; Get the history records
+;
+ ind = 893
+ hdr[ind+indgen(strlen(origin[0]))*2] = byte(origin[0])
+ ind = ind+2*strlen(origin[0])
+ hdr[ind] = 10B
+ ind = ind+2
+ hdr[ind+indgen(strlen(date_obs[0]))*2] = byte(date_obs[0])
+ ind = ind+2*strlen(date_obs[0])
+ hdr[ind] = 10B
+ ind = ind+2
+
+; write the history comment strings (as many as possible) in binary form
+; into the available 1160 bytes 
+
+ for i = 0, N_elements(history)-1 do begin
+            hist = strtrim(strmid(history[i],8,72))
+            if ( strlen(hist) EQ 0 ) then goto, SKIP    
+            if (ind + 2*strlen(hist) GT 2052 ) then goto, HIST_END    
+            hdr[ ind + indgen( strlen(hist) )*2 ] = byte(hist)
+            ind = ind+2*strlen(hist)
+            hdr[ind] = 10B
+            ind = ind+2 
+            SKIP:   
+ end
+ HIST_END:   
+ hdr[88 + 2*offset] = byte(513,0,2)
+ hdr[108] = byte(long(time_creat),0,4)       ; write time of image creation
+ buf[108] = byte(long(time_creat),0,4)       ; time of last modification
+ hdr[112] = byte(long(time_creat),0,4)       ; and time minimum and maximum 
+ hdr[116] = byte(long(time_creat),0,4)       ; pixel values were computed
+ 
+         hdr[32 + indgen(5)*4 + 3*offset] = 1
+         buf[32 + indgen(5)*4 + 3*offset] = 1
+         if big_endian then begin
+            hdr[63 + indgen(5)*4] = 1
+            buf[63 + indgen(5)*4] = 1
+         endif
+         hdr[63 + indgen(5)*4 - 3*offset] = 128
+         buf[63 + indgen(5)*4 - 3*offset] = 128
+
+  writeu,lun1,hdr
+  free_lun,lun1
+
+; Write the data into the .pix file   
+
+ buf[ offset + indgen(5)*2] = byte('impix')
+ if not big_endian then buf[12] = [65b, 58b] else $
+                              buf[14] = [58b, 65b]
+ hdrname = repchr(pixname,'pix','imh')
+ buf[ 412 + offset+ indgen(len1[0])*2 ] = byte(hdrname)
+ buf[ 572 + offset + indgen(len2[0])*2] = byte(header)
+ node = strpos( pixname, '!')
+ pixfile = strmid( pixname, node+1,strlen(pixname)-node+1 )
+
+ openw,lun2, pixfile, /GET_LUN
+
+ writeu, lun2, buf
+ writeu, lun2, image
+
+ free_lun, lun2
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/is_ieee_big.pro b/Code/script_idl_mv/astrolib/is_ieee_big.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9127dd72ea3bb1c835af6e6724e530017eb3abc5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/is_ieee_big.pro
@@ -0,0 +1,32 @@
+function is_ieee_big
+;+
+; NAME:
+;	IS_IEEE_BIG
+; PURPOSE:
+;	Determine if the current machine uses IEEE, big-endian numbers.
+; EXPLANATION:
+;       (Big endian implies that byteorder XDR conversions are no-ops).
+; CALLING SEQUENCE:
+;	flag = is_ieee_big()
+; INPUT PARAMETERS:
+;       None
+; RETURNS:
+;       1 if the machine appears to be IEEE-compliant, 0 if not.
+; COMMON BLOCKS:
+;	None.
+; SIDE EFFECTS:
+;	None
+; RESTRICTIONS:
+; PROCEDURE:
+;       The first byte of the two-byte representation of 1 is examined.
+;       If it is zero, then the data is stored in big-endian order.
+; MODIFICATION HISTORY:
+;       Written 15-April-1996 by T. McGlynn for use in MRDFITS.
+;	13-jul-1997	jkf/acc	- added calls to check_math to avoid
+;				  underflow messages in V5.0 on Win32 (NT).
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Follow RSI and just do a single test  W. Landsman   April 2003
+;-
+
+      return, 1b - (byte(1,0,1))[0]
+      end								    
diff --git a/Code/script_idl_mv/astrolib/isarray.pro b/Code/script_idl_mv/astrolib/isarray.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0e7e051b19c17e8f10b2684bd0f8afbd28bb61ee
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/isarray.pro
@@ -0,0 +1,20 @@
+;+
+; NAME:
+;       ISARRAY
+; PURPOSE:
+;       Test if the argument is an array or not.
+;
+; CALLING SEQUENCE:
+;       res = isarray(a)
+;
+; INPUTS:
+;       a - argument
+;
+; REVISION HISTORY:
+;       Rewritten from scratch, Ole Streicher, 2015
+;
+;-
+FUNCTION isarray, a
+  res = size(a)
+  return, res[0] ne 0
+END
diff --git a/Code/script_idl_mv/astrolib/ismeuv.pro b/Code/script_idl_mv/astrolib/ismeuv.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c23a501a68de747dfbc04ec139a501837e7e4f18
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ismeuv.pro
@@ -0,0 +1,176 @@
+function ismeuv,wave,Hcol,HeIcol,HeIIcol,Fano=fano
+;+
+; NAME:
+;       ISMEUV
+; PURPOSE:
+;       Compute the continuum interstellar EUV optical depth 
+;
+; EXPLANATION:
+;       The EUV optical depth is computed from the photoionization of
+;       hydrogen and helium.
+;
+; CALLING SEQUENCE:
+;       tau = ISMEUV( wave, Hcol, [ HeIcol, HeIIcol, /Fano ]
+;
+; INPUTS:
+;       wave - Vector of wavelength values (in Angstroms).   Useful range is
+;               40 - 912 A; at shorter wavelengths metal opacity should be
+;               considered, at longer wavelengths there is no photoionization.
+;       Hcol - Scalar specifying interstellar hydrogen column density in cm-2.
+;                 Typical values are 1E17 to 1E20.
+;
+; OUTPUT:
+;       tau - Vector giving resulting optical depth, same number of elements 
+;               as wave, non-negative values.   To obtain the attenuation of 
+;               an input spectrum, multiply by exp(-tau).
+;
+; OPTIONAL INPUTS:
+;       HeIcol - Scalar specifying neutral helium column density in cm-2.    
+;               Default is 0.1*Hcol (10% of hydrogen column)
+;       HeIIcol - Scalar specifying ionized helium column density in cm-2
+;               Default is 0 (no HeII)
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /FANO - If this keyword is set and non-zero, then the 4 strongest 
+;               auto-ionizing resonances of He I are included.   The shape 
+;               of these resonances is given by a Fano profile - see Rumph, 
+;               Bowyer, & Vennes 1994, AJ, 107, 2108.  If these resonances are
+;               included then the input wavelength vector should have
+;               a fine (>~0.01 A) grid between 190 A and 210 A, since the
+;               resonances are very narrow.
+; EXAMPLE:
+;       (1) One has a model EUV spectrum with wavelength, w (in Angstroms) and 
+;       flux,f .  Plot the model flux after attenuation by 1e18 cm-2 of HI, 
+;       with N(HeI)/N(HI) = N(HeII)/N(HI) = 0.05
+;
+;       IDL> Hcol = 1e18
+;       IDL> plot, w, f*exp(-ismeuv(w, Hcol, .05*Hcol, .05*Hcol))
+;
+;       (2)  Plot the cross-section of HeI from 180 A to 220 A for 1e18 cm-2
+;               of HeI, showing the auto-ionizing resonances.   This is 
+;               Figure 1 in Rumph et al. (1994)
+;
+;       IDL> w = 180 + findgen(40000)*0.001        ;Need a fine wavelength grid
+;       IDL> plot, w, ismeuv(w, 0, 1e18, /Fano)          
+;
+; NOTES:
+;       (1) The more complete program  ismtau.pro at 
+;           http://hea-www.harvard.edu/PINTofALE/pro/ extends this work
+;           to shorter wavelengths and includes metal and molecular hydrogen
+;           opacities
+;       (2) This program only compute continuum opacities, and for example,
+;           the He ionization edges at 504 A  and 228 A are blurred by
+;           converging line absorptions (Dupuis et al. 1995. ApJ, 455, 574)
+;           
+; HISTORY:
+;       Written,    W. Landsman                  October, 1994
+;       Adapted from ism.c at anonymous ftp site cea-ftp.cea.berkeley.edu
+;       by Pat Jelinsky, Todd Rumph & others.
+;       Avoid underflow messages, support double prec.  W. Landsman October 2003
+;       Fix error in He II optical Depth  J. Slavin/WL   Sep 2013
+;-
+ On_error,2
+
+ if N_params() LT 2 then begin
+        print,'Syntax - tau = ISMEUV( wave, Hcol, [ HeIcol, HeIIcol, /FANO] )'
+        return,-1
+ endif
+
+ if N_elements( HeIcol) EQ 0 then HeIcol = 0.1*Hcol
+ if N_elements( HeIIcol) EQ 0 then HeIIcol = 0.0*Hcol
+
+; Compute attenuation due to photoionization of hydrogen.   See Spitzer
+; (Physical processes in the interstellar medium), page 105
+
+ if (size(wave,/TNAME) EQ 'DOUBLE') then begin 
+          pi = !dpi 
+          double  = 1b
+  endif else  begin 
+          pi = !pi
+          double = 0b
+ endelse
+ ratio = wave/911.75
+ tauh = wave*0.
+ good = where(ratio LT 1, Ngood)
+ minexp = alog((machar(double=double)).xmin) ;Min exponent to avoid underflow
+ if Ngood GT 0 then begin
+        r = ratio[good]
+        z = sqrt( r/(1.0-r) )
+        denom = replicate(1.0, Ngood)
+        y = -2.*pi*z
+        good1 = where(y GT minexp, Ngood1)
+             if Ngood1 GT 0 then denom[good1] = (1.0 - exp(y[good1]))        
+        tauh[good] = Hcol * 3.44e-16 * (r^4)*exp(-4.0*z*atan(1/z)) /  denom
+  endif
+
+; Now compute photoionization cross-section of He II; just like hydrogen but 
+; with a nuclear charge Z = 2
+
+ tauheII = wave*0.
+ ratio = 4. * wave/911.75
+ good = where(ratio LT 1, Ngood)
+ if Ngood GT 0 then begin
+        r = ratio[good]
+        z = sqrt( r/(1.0-r) )
+        denom = replicate(4.0, Ngood)    ;Z^2  Bug fix Sep 13
+        y = -2*PI*z
+        good1 = where(y GT minexp, Ngood1)
+           if Ngood1 GT 0 then denom[good1] *= (1.0 - exp(y[good1]))
+        tauheII[good] = heiicol * 3.44e-16 * (r^4)*exp(-4.0*z*atan(1/z)) / denom
+            
+ endif
+
+; Polynomial coefficients for He I cross-section taken from experimental
+; data by Marr & West (1976)
+; c1 for wavelengths greater than 46 A
+
+ c1 = [-2.953607d+01, 7.083061d+00, 8.678646d-01,-1.221932d+00,  $
+       4.052997d-02, 1.317109d-01, -3.265795d-02, 2.500933d-03 ]
+
+; c2 for wavelengths less than 46 A.
+
+ c2 = [ -2.465188d+01, 4.354679d+00, -3.553024d+00, 5.573040d+00, $
+       -5.872938d+00, 3.720797d+00, -1.226919d+00, 1.576657d-01 ]
+
+; parameters of autoionization resonances for 4 strongest He I resonances
+; Numbers are from Oza (1986), Phys Rev. A, 33, 824 -- nu and gamma
+; and Fernley et al., J. Phys. B., 20, 6457, 1987 -- q
+
+        q  = [2.81d, 2.51d, 2.45d, 2.44d ]
+        nu = [1.610d, 2.795d, 3.817d, 4.824d ]
+        fano_gamma = [2.64061d-03, 6.20116d-04, 2.56061d-04, 1.320159d-04 ]
+        esubi = 3.0d - 1.0d/nu^2 + 1.807317d
+
+ tauHeI = wave*0.
+ good = where( wave LT 503.97, Ngood )
+ if Ngood GT 0 then begin
+
+        x = alog10(wave[good])
+        y = x*0.
+
+        good1 = where(wave LT 46.0, Ngood1 )
+        if Ngood1 GT 0 then y[good1] = poly( x[good1], c2)      
+
+        good2 = where(wave GE 46.0, Ngood2 )
+        if Ngood2 GT 0 then begin 
+
+                y[good2] = poly( x[good2], c1)
+
+        if keyword_set(fano) then begin
+                epsilon = 911.2671/wave
+                for i=0,3 do begin       ;Loop over first four HeI resonances
+                        x = 2.0 * ((epsilon-esubi[i] )/ fano_gamma[i] ) 
+                        y = y + alog10( (x - q[i])^2/ (1 + x*x ) )
+                endfor
+        endif
+        endif
+
+  tauHeI[good] = HeIcol * 10^y
+
+ endif
+
+; Total optical depth from HI, HeII and HeI
+
+ return, tauH + tauHeII + tauHeI
+
+ end
diff --git a/Code/script_idl_mv/astrolib/jdcnv.pro b/Code/script_idl_mv/astrolib/jdcnv.pro
new file mode 100644
index 0000000000000000000000000000000000000000..652dd3014c0180dc0306ae7092e599ca19fb0f69
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/jdcnv.pro
@@ -0,0 +1,67 @@
+PRO JDCNV, YR, MN, DAY, HR, JULIAN
+;+
+; NAME:
+;	JDCNV
+; PURPOSE:
+;	Converts Gregorian dates to Julian days   
+;
+; EXPLANATION:
+;       For IDL versions V5.1 or greater, this procedure is superceded by
+;       JULDAY() function in the standard IDL distribution.   Note, however,
+;       that prior to V5.1 there wasa bug in JULDAY() that gave answers off
+;       by 0.5 days. 
+;        
+; CALLING SEQUENCE:
+;	JDCNV, YR, MN, DAY, HR, JULIAN
+;
+; INPUTS:
+; 	YR = Year, integer scalar or vector
+;	MN = Month  integer (1-12) scalar or vector
+;	DAY = Day   integer 1-31) scalar or vector 
+;	HR  = Hours and fractions of hours of universal time (U.T.), scalar
+;              or vector
+;		
+; OUTPUTS:
+;	JULIAN = Julian date (double precision) 
+;
+; EXAMPLE:
+;	To find the Julian Date at 1978 January 1, 0h (U.T.)
+;
+;	IDL> JDCNV, 1978, 1, 1, 0., JULIAN
+;
+;	will give JULIAN = 2443509.5
+; NOTES:
+;	(1) JDCNV will accept vector arguments 
+;	(2) JULDATE is an alternate procedure to perform the same function
+;
+; REVISON HISTORY:
+;	Converted to IDL from Don Yeomans Comet Ephemeris Generator,
+;	B. Pfarr, STX, 6/15/88
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Added checks on valid month, day ranges W. Landsman July 2008
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 5 then begin
+	print,'Syntax -  JDCNV, yr, mn, day, hr, julian'  
+	print,'   yr - Input Year (e.g. 1978), scalar or vector'
+	print,'   mn - Input Month (1-12), scalar or vector'
+	print,'   day - Input Day (1-31), scalar or vector'
+	print,'   hr - Input Hour (0-24), scalar or vector'
+	print,'   julian - output Julian date'
+        return
+ endif
+ if max(mn) GT 12 then message,/con,  $
+     'Warning - Month number outside of expected range [1-12] '
+ if max(day) GT 31 then message,/con, $
+     'Warning - Day number outside of expected range [1-31] '
+
+ yr = long(yr) & mn = long(mn) &  day = long(day)	;Make sure integral
+ L = (mn-14)/12		;In leap years, -1 for Jan, Feb, else 0
+ julian = day - 32075l + 1461l*(yr+4800l+L)/4 + $
+         367l*(mn - 2-L*12)/12 - 3*((yr+4900l+L)/100)/4
+ julian = double(julian) + (HR/24.0D) - 0.5D
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/jplephinterp.pro b/Code/script_idl_mv/astrolib/jplephinterp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..61d10dfecffeb0b156028b78f63070658f68331c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/jplephinterp.pro
@@ -0,0 +1,745 @@
+;+
+; NAME:
+;   JPLEPHINTERP
+;
+; AUTHOR:
+;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
+;   craigm@lheamail.gsfc.nasa.gov
+;   UPDATED VERSIONs can be found on my WEB PAGE: 
+;      http://cow.physics.wisc.edu/~craigm/idl/idl.html
+;
+; PURPOSE:
+;   Interpolate position and motion of planetary bodies (JPL Ephemeris)
+;
+; MAJOR TOPICS:
+;   Planetary Orbits, Interpolation
+;
+; CALLING SEQUENCE:
+;   JPLEPHINTERP, INFO, RAWDATA, T, X, Y, Z, [VX, VY, VZ, /EARTH, /SUN,
+;         OBJECTNAME=, CENTER=, TBASE=, POSUNITS=, VELUNITS= ]
+;
+; DESCRIPTION:
+;
+;   JPLEPHINTERP interpolates the JPL DE200 or DE405 planetary
+;   ephemeris to find the positions and motions of planetary bodies.
+;
+;   This routine is the second stage of a two-stage process to
+;   interpolate the JPL ephemeris.  In this first stage, the file is
+;   opened using JPLEPHREAD, and the relevant portions of the table
+;   are read and stored into the two variables INFO and RAWDATA.  In
+;   the second stage, the user actually interpolates the ephemeris for
+;   the desired bodies and to the desired ephemeris time using
+;   JPLEPHINTERP.
+;
+;   The only independent variable which must be specified is T, the
+;   ephemeris time.  For low to moderate accuracy applications, T is
+;   simply the conventional calendar date, expressed in Julian days.
+;   See below for high precision applications.
+;
+;   Upon output, the position components of the desired body are
+;   returned in parameters X, Y and Z, and if requested velocity
+;   components are returned in parameters VX, VY and VZ.  Coordinates
+;   are referred to the ephemeris's coordinate system: FK5 for
+;   JPL-DE200 and ICRS for JPL-DE405.  By default, the origin of
+;   coordinates is the solar system barycenter (SSB), unless another
+;   origin is selected using the CENTER keyword.
+;
+;   Users must set the VELOCITY keyword to generate body velocities.
+;   By default they are not generated.
+;
+;   Users can select the desired body by using either the EARTH or SUN
+;   keywords, or the OBJECTNAME keyword.
+;
+;   By default, positions are returned in units of KM and velocities
+;   in units of KM/DAY.  However, the output units are selectable by
+;   setting the POSUNITS and VELUNITS keywords.
+;
+; High Precision Applications
+;
+;   If the required precision is finer than a few hundred meters, the
+;   user must be aware that the formal definition of the ephemeris
+;   time is the coordinate time of a clock placed at the solar system
+;   barycenter (SSB).  If the user's time is measured by a clock
+;   positioned elsewhere, then various corrections must be applied.
+;   Usually, the most significant correction is that from the
+;   geocenter to the SSB (see Fairhead & Bretagnon 1990; Fukushima
+;   1995).  Not applying this correction creates an error with
+;   amplitude ~170 nano-light-seconds ( = 50 m) on the earth's
+;   position.  (see TDB2TDT)
+;
+;   For high precision, the user should also specify the TBASE
+;   keyword.  TBASE should be considered a fixed epoch with respect to
+;   which T is measured; T should be small compared to TBASE.
+;   Internally, subtraction of large numbers occurs with TBASE first,
+;   so truncation error is minimized by specifying TBASE.
+;
+; Nutations and Librations
+;
+;   This routine also provides information about earth nutations and
+;   lunar librations, which are stored in the JPL ephemeris tables.
+;   The POSUNITS and VELUNITS keywords do not affect these
+;   computations.
+;
+;   Lunar librations in the form of three Euler angles are returned in
+;   X, Y, Z, in units of radians, and their time derivatives are
+;   returned in VX, VY, and VZ in units of radians per day.
+;
+;   The earth nutation angles psi (nutation in longitude) and epsilon
+;   (nutation in obliquity) are returned in X and Y, in units of
+;   radians.  Their time derivatives are returned in VX and VY
+;   respectively.  The quantities returned in Z and VZ are undefined.
+;
+; Verification
+;
+;   The precision routine has been verified using JPLEPHTEST, which is
+;   similar to the original JPL program EPHTEST.  For years 1950 to
+;   2050, JPLEPHINTERP reproduces the original JPL ephemeris to within
+;   1 centimeter.
+;
+; Custom Ephemerides
+;
+;   It is possible to make custom ephemerides using JPLEPHMAKE, or to
+;   augmented an existing ephemeris with additional data.  In the
+;   former case JPLEPHINTERP should automatically choose the correct
+;   object from the table and interpolate it appropriately.
+;
+;   For augmented ephemerides, the object can be specified by name,
+;   which works as expected, or by number, which has a special
+;   behavior.  For augmented files only, the new objects begin at
+;   number 100.
+;
+;
+; PARAMETERS: 
+;
+;   INFO - structure returned by JPLEPHREAD.  Users should not modify
+;          this structure.
+;
+;   RAWDATA - raw data array returned by JPLEPHREAD.  Users should not
+;             modify this data array.
+;
+;   T - ephemeris time(s) of interest, relative to TBASE (i.e. the
+;       actual interpolation time is (T+TBASE)).  May be a scalar or
+;       vector.
+;
+;   X, Y, Z - upon return, the x-, y- and z-components of the body
+;             position are returned in these parameters.  For
+;             nutations and librations see above.
+;
+;   VX, VY, VZ - upon return, the x-, y- and z-components of the body
+;                velocity are returned in these parameters, if the
+;                VELOCITY keyword is set.  For nutations and
+;                librations see above.
+;
+;
+; KEYWORD PARAMETERS:
+;
+;   EARTH, SUN - set one of these keywords if the desired body is the
+;                earth or the sun.  One of EARTH, SUN or OBJECTNAME
+;                must be specified.
+;
+;   OBJECTNAME - a scalar string or integer, specifies the planetary
+;                body of interest.  May take any one of the following
+;                integer or string values.
+;
+;                   1 - 'MERCURY'     9 - 'PLUTO'
+;                   2 - 'VENUS'      10 - 'MOON'  (earth's moon)
+;                   3 - 'EARTH'      11 - 'SUN'
+;                   4 - 'MARS'       12 - 'SOLARBARY' or 'SSB' (solar system barycenter)
+;                   5 - 'JUPITER'    13 - 'EARTHBARY' or 'EMB' (earth-moon barycenter)
+;                   6 - 'SATURN'     14 - 'NUTATIONS' (see above)
+;                   7 - 'URANUS'     15 - 'LIBRATIONS' (see above)
+;                   8 - 'NEPTUNE' 
+;
+;                For custom ephemerides, the user should specify the
+;                object name or number.
+;
+;                For augmented ephemerides, the user should specify
+;                the name.  If the number is specified, then numbers
+;                1-15 have the above meanings, and new objects are
+;                numbered starting at 100.
+;
+;   CENTER - a scalar string or integer, specifies the origin of
+;            coordinates.  See OBJECTNAME for allowed values.
+;            Default: 12 (Solar system barycenter)
+;
+;   VELOCITY - if set, body velocities are generated and returned in
+;              VX, VY and VZ.
+;              Default: unset (no velocities)
+;
+;   POSUNITS - a scalar string specifying the desired units for X, Y,
+;              and Z.  Allowed values:
+;                 'KM' - kilometers  (default)
+;                 'CM' - centimeters
+;                 'AU' - astronomical units
+;                 'LT-S' - light seconds
+;               If angles are requested, this keyword is ignored and
+;               the units are always 'RADIANS'.
+;
+;   VELUNITS - a scalar string specifying the desired units for VX, VY
+;              and VZ.  Allowed values:
+;                 'KM/DAY' - kilometers per day  (default)
+;                 'KM/S' - kilometers per second
+;                 'CM/S' - centimeters per second
+;                 'LT-S/S' or 'V/C' - light seconds per second or
+;                     unitless ratio with speed of light, V/C
+;                 'AU/DAY' - astronomical units per day
+;
+;   TBASE - a scalar or vector, specifies a fixed epoch against wich T
+;           is measured.  The ephemeris time will be (T+TBASE).  Use
+;           this keyword for maximum precision.
+;
+;
+; EXAMPLE:
+;
+;   Find position of earth at ephemeris time 2451544.5 JD.  Units are
+;   in Astronomical Units.
+;
+;   JPLEPHREAD, 'JPLEPH.200', pinfo, pdata, [2451544D, 2451545D]
+;
+;   JPLEPHINTERP, pinfo, pdata, 2451544.5D, xearth, yearth, zearth, $
+;                 /EARTH, posunits='AU'
+;     
+;
+; REFERENCES:
+;
+;   AXBARY, Arnold Rots.
+;      ftp://heasarc.gsfc.nasa.gov/xte/calib_data/clock/bary/
+;
+;   HORIZONS, JPL Web-based ephermis calculator (Ephemeris DE406)
+;      http://ssd.jpl.nasa.gov/horizons.html
+;   
+;   Fairhead, L. & Bretagnon, P. 1990, A&A, 229, 240
+;
+;   Fukushima, T. 1995, A&A, 294, 895
+;
+;   Standish, E.M. 1982, "Orientation of the JPL Ephemerides,
+;      DE200/LE200, to the Dynamical Equinox of J2000", Astronomy &
+;      Astrophysics, vol. 114, pp. 297-302.
+;
+;   Standish, E.M.: 1990, "The Observational Basis for JPL's DE200,
+;      the planetary ephemeris of the Astronomical Almanac", Astronomy
+;      & Astrophysics, vol. 233, pp. 252-271.    
+;
+; SEE ALSO
+;   JPLEPHREAD, JPLEPHINTERP, JPLEPHTEST, TDB2TDT, JPLEPHMAKE
+;   
+; MODIFICATION HISTORY:
+;   Written and Documented, CM, Jun 2001
+;   Corrected bug in name conversion of NUTATIONS and LIBRATIONS, 18
+;     Oct 2001, CM
+;   Added code to handle custom-built ephemerides, 04 Mar 2002, CM
+;   Fix bug in evaluation of velocity (only appears in highest order
+;     polynomial term); JPLEPHTEST verification tests still pass;
+;     change is of order < 0.5 cm in position, 22 Nov 2004, CM
+;   Perform more validity checking on inputs; and more informative
+;     outputs, 09 Oct 2008, CM
+;   Allow SSB and EMB as shortcuts for solar system and earth-moon
+;     bary center, 15 Oct 2008, CM
+;   TBASE now allowed to be a vector or scalar, 01 Jan 2009, CM
+;   VELFAC keyword gives scale factor between POSUNITS and VELUNITS, 
+;     12 Jan 2009, CM
+;   Add option VELUNITS='V/C' for unitless ratio with speed of light,
+;     2012-10-02, CM;
+;
+;  $Id: jplephinterp.pro,v 1.19 2012/10/02 11:32:59 cmarkwar Exp $
+;
+;-
+; Copyright (C) 2001, 2002, 2004, 2008, 2009, 2012, Craig Markwardt
+; This software is provided as is without any warranty whatsoever.
+; Permission to use, copy and distribute unmodified copies for
+; non-commercial purposes, and to modify and use for personal or
+; internal use, is granted.  All other rights are reserved.
+;-
+
+pro jplephinterp_calc, info, raw, obj, t, x, y, z, vx, vy, vz, $
+                       velocity=vel, tbase=tbase
+
+  ; '$Id: jplephinterp.pro,v 1.19 2012/10/02 11:32:59 cmarkwar Exp $'
+
+  if n_elements(tbase) EQ 0 then tbase = 0D
+  ;; Number of coefficients (x3), number of subintervals, num of rows
+  nc = info.ncoeff[obj]
+  ns = info.nsub[obj]
+  dt = info.timedel
+  nr = info.jdrows
+  jd0 = info.jdlimits[0] - tbase
+  jd1 = info.jdlimits[1] - tbase
+
+  ;; Extract coefficient data from RAW
+  if obj EQ 11 then begin
+      ;; Nutations have two components
+      ii1 = info.ptr[obj]-1
+      ii2 = ii1 + nc*ns*2L - 1
+      coeffs = reform(dblarr(nc,3,ns,nr), nc,3,ns,nr, /overwrite)
+      coeffs[0,0,0,0] = reform(raw[ii1:ii2,*],nc,2,ns,nr, /overwrite)
+  endif else begin
+      ;; All other bodies are done with three components
+      ii1 = info.ptr[obj]-1
+      ii2 = ii1 + nc*ns*3L - 1
+      coeffs = reform(raw[ii1:ii2,*],nc,3,ns,nr, /overwrite)
+  endelse
+
+  ;; Decide which interval and subinterval we are in
+  tint = (t-jd0)/dt        ;; Interval number (real)
+  ieph = floor(tint)       ;; Interval number (index = int)
+  tint = (tint-ieph)*ns    ;; Subinterval number (real)
+  nseg = floor(tint)       ;; Subinterval number (index = int)
+  ;; Chebyshev "x" (rescaled to range = [-1,1] over subinterval)
+  tseg = 2D*(tint - nseg) - 1  
+
+  ;; Below is an optimization.  If the time interval doesn't span an
+  ;; ephemeris subinterval, then we can index the coefficient array by
+  ;; a scalar, which is much faster.  Otherwise we maintain the full
+  ;; vector-level indexing.
+  mini = minmax(ieph) & minn = minmax(nseg)
+  if mini[0] EQ mini[1] AND minn[0] EQ minn[1] then begin
+      ieph = ieph[0]
+      nseg = nseg[0]
+  endif
+
+  ;; Initialize the first two Chebyshev polynomials, which are P_0 = 1
+  ;; and P_1(x) = x
+  p0 = 1D
+  p1 = tseg
+  ;; Initial polynomials for Chebyshev derivatives, V_0 = 0, V_1(x) =
+  ;; 1, V_2(x) = 4*x
+  v0 = 0D
+  v1 = 1D
+  v2 = 4D*tseg
+  tt = 2D*temporary(tseg)
+
+  x  = 0D & y  = 0D & z  = 0D
+  vx = 0D & vy = 0D & vz = 0D
+  i0 = ieph*0 & i1 = i0 + 1 & i2 = i1 + 1
+
+  ;; Compute Chebyshev functions two at a time for efficiency
+  for i = 0, nc-1, 2 do begin
+      if i EQ nc-1 then begin
+          p1 = 0
+          v1 = 0
+      endif
+      ii = i0 + i
+      jj = i0 + ((i+1) < (nc-1))
+      
+      x = x + coeffs[ii,i0,nseg,ieph]*p0 + coeffs[jj,i0,nseg,ieph]*p1
+      y = y + coeffs[ii,i1,nseg,ieph]*p0 + coeffs[jj,i1,nseg,ieph]*p1
+      z = z + coeffs[ii,i2,nseg,ieph]*p0 + coeffs[jj,i2,nseg,ieph]*p1
+
+      if keyword_set(vel) then begin
+          vx = vx + coeffs[ii,i0,nseg,ieph]*v0 + coeffs[jj,i0,nseg,ieph]*v1
+          vy = vy + coeffs[ii,i1,nseg,ieph]*v0 + coeffs[jj,i1,nseg,ieph]*v1
+          vz = vz + coeffs[ii,i2,nseg,ieph]*v0 + coeffs[jj,i2,nseg,ieph]*v1
+
+          ;; Advance to the next set of Chebyshev polynomials. For
+          ;; velocity we need to keep the next orders around
+          ;; momentarily.
+          p2 = tt*p1 - p0
+          p3 = tt*p2 - p1
+          v2 = tt*v1 - v0 + 2*p1
+          v3 = tt*v2 - v1 + 2*p2
+          
+          p0 = temporary(p2) & p1 = temporary(p3)
+          v0 = temporary(v2) & v1 = temporary(v3)
+      endif else begin
+          ;; Advance to the next set of Chebyshev polynomials.  For no
+          ;; velocity, we can re-use old variables.
+          p0 = tt*p1 - temporary(p0)
+          p1 = tt*p0 - temporary(p1)
+      endelse
+  endfor
+
+  if keyword_set(vel) then begin
+      vfac = 2D*ns/dt
+      vx = vx * vfac
+      vy = vy * vfac
+      vz = vz * vfac
+  endif
+
+  return
+end
+
+pro jplephinterp_denew, info, raw, obj, t, x, y, z, vx, vy, vz, $
+                        velocity=vel, tbase=tbase
+
+  if n_elements(tbase) EQ 0 then tbase = 0D
+  dt = info.timedel
+  nr = info.jdrows
+  jd0 = info.jdlimits[0]
+  jd1 = info.jdlimits[1]
+  c = info.c / 1000D
+  cday = 86400D*info.c/1000D
+
+  ;; Renormalize to fractional and whole days, so fractional
+  ;; component is between -.5 and +.5, as needed by barycentering
+  ;; approximation code.
+  ti  = round(t)      ;; Delta Time: integer
+  tbi = round(tbase)  ;; Base: integer
+  
+  tc = ti + tbi             ;; Total time: integer
+  tt = (t-ti) + (tbase-tbi) ;; Total time: fractional
+
+  tc = tc + round(tt)       ;; Re-round: integer
+  tt = tt - round(tt)       ;; Re-round: fractional
+  t2 = tt*tt                ;; Quadratic and cubic terms
+  t3 = t2*tt
+
+  ieph = tc - round(jd0)
+  ;; Below is an optimization.  If the time interval doesn't span an
+  ;; ephemeris subinterval, then we can index the coefficient array by
+  ;; a scalar, which is much faster.  Otherwise we maintain the full
+  ;; vector-level indexing.
+  mini = minmax(ieph)
+  if mini[0] EQ mini[1] then ieph = ieph[0]
+
+  if obj EQ 3 then begin
+      ;; Earth, stored as Taylor series coefficients per day
+      x = (raw[0,ieph] + raw[3,ieph]*tt + 0.5D*raw[6,ieph]*t2 + $
+           (raw[9,ieph]/6D)*t3)
+      y = (raw[1,ieph] + raw[4,ieph]*tt + 0.5D*raw[7,ieph]*t2 + $
+           (raw[10,ieph]/6D)*t3)
+      z = (raw[2,ieph] + raw[5,ieph]*tt + 0.5D*raw[8,ieph]*t2 + $
+           (raw[11,ieph]/6D)*t3)
+      if keyword_set(vel) then begin
+          vx = raw[3,ieph] + raw[6,ieph]*tt + 0.5D*raw[9 ,ieph]*t2
+          vy = raw[4,ieph] + raw[7,ieph]*tt + 0.5D*raw[10,ieph]*t2
+          vz = raw[5,ieph] + raw[8,ieph]*tt + 0.5D*raw[11,ieph]*t2
+      endif
+      x = reform(x, /overwrite)
+      y = reform(y, /overwrite)
+      z = reform(z, /overwrite)
+ 
+  endif else if obj EQ 11 then begin
+      ;; Sun, stored as daily components only
+      
+      x = reform(raw[12,ieph] + tt*0)
+      y = reform(raw[13,ieph] + tt*0)
+      z = reform(raw[14,ieph] + tt*0)
+      if keyword_set(vel) then $
+        message, 'ERROR: DENEW format does not provide solar velocity'
+
+  endif else if obj EQ 1000 then begin
+
+      tt = t - (jd0+jd1)/2D
+      x = spl_interp(raw[15,*], raw[16,*], raw[17,*], tt)
+      return
+      
+  endif else begin
+      message, 'ERROR: DENEW format does not contain body '+strtrim(obj,2)
+  endelse
+end
+
+pro jplephinterp, info, raw, t, x, y, z, vx, vy, vz, earth=earth, sun=sun, $
+                  objectname=obj0, velocity=vel, center=cent, tbase=tbase, $
+                  posunits=outunit0, velunits=velunit0, $
+                  pos_vel_factor=velfac, $
+                  xobjnum=objnum, decode_obj=decode
+
+  if n_params() EQ 0 then begin
+      message, 'USAGE: JPLEPHINTERP, info, rawdata, teph, x, y, z, '+$
+        'vx, vy, vz, OBJECTNAME="body", /VELOCITY, CENTER="body", '+$
+        'POSUNITS="units", VELUNITS="units", /EARTH, /SUN', /info
+      return
+  endif
+  
+  ;; The numbering convention for ntarg and ncent is:
+  ;;   1 = Mercury            8 = Neptune
+  ;;   2 = Venus              9 = Pluto
+  ;;   3 = Earth             10 = Moon
+  ;;   4 = Mars              11 = Sun
+  ;;   5 = Jupiter           12 = Solar system barycenter
+  ;;   6 = Saturn            13 = Earth-Moon barycenter
+  ;;   7 = Uranus            14 = Nutations (longitude and obliquity; untested)
+  ;;                         15 = Librations 
+  ;; This numbering scheme is 1-relative, to be consistent with the
+  ;; Fortran version.  (units are seconds; derivative units are seconds/day)
+  ;;1000 = TDB to TDT offset (s), returned in X component
+
+  sz = size(info)
+  if sz[sz[0]+1] NE 8 then message, 'ERROR: INFO must be a structure'
+  if ((info.format NE 'JPLEPHMAKE') AND $
+      (info.format NE 'BINEPH2FITS') AND $
+      (info.format NE 'DENEW')) then begin
+      message, 'ERROR: ephemeris type "'+info.format+'" is not recognized'
+  endif
+
+  ;; Handle case of custom ephemerides
+  if info.format EQ 'JPLEPHMAKE' then begin
+      if n_elements(obj0) GT 0 then begin
+          sz = size(obj0)
+          if sz[sz[0]+1] EQ 7 then begin
+              obj = strupcase(strtrim(obj0[0],2))
+              wh = where(info.objname EQ obj, ct)
+              if ct EQ 0 then $
+                message, 'ERROR: '+obj+' is an unknown object'
+              obj = wh[0] + 1
+          endif else begin
+              obj = floor(obj0[0])
+              if obj LT 1 OR obj GT n_elements(info.objname) then $
+                message, 'ERROR: Numerical OBJNAME is out of bounds'
+          endelse
+
+          ;; Interpolate the ephemeris here
+          jplephinterp_calc, info, raw, obj-1, t, velocity=vel, $
+            tbase=tbase, x, y, z, vx, vy, vz
+
+          goto, COMPUTE_CENTER
+      endif
+      message, 'ERROR: Must specify OBJNAME for custom ephemerides'
+  endif
+
+
+  ;; ----------------------------------------------------------
+  ;; Determine which body or system we will compute
+  if n_elements(obj0) GT 0 then begin
+      sz = size(obj0)
+      if sz[sz[0]+1] EQ 7 then begin
+          obj = strupcase(strtrim(obj0[0],2))
+          case obj of 
+              'EARTH':      obj = 3
+              'SOLARBARY':  obj = 12
+              'SSB':        obj = 12
+              'EARTHBARY':  obj = 13
+              'EMB':        obj = 13
+              'NUTATIONS':  obj = 14
+              'LIBRATIONS': obj = 15
+              'TDB2TDT':    obj = 1000
+              ELSE: begin
+                  wh = where(info.objname EQ obj, ct)
+                  if ct EQ 0 then $
+                    message, 'ERROR: '+obj+' is an unknown object'
+                  obj = wh[0] + 1
+                  if obj GT 11 then obj = obj + 100 - 14
+              end
+          endcase
+      endif else begin
+          obj = floor(obj0[0])
+      endelse
+  endif else begin
+      if NOT keyword_set(earth) AND NOT keyword_set(sun) then $
+        message, 'ERROR: Must specify OBJNAME, EARTH or SUN'
+  endelse
+  if keyword_set(earth) then obj = 3
+  if keyword_set(sun)   then obj = 11
+
+  ;; If the caller is merely asking us to decode the objectnumber,
+  ;; then return it now.
+  objnum = obj
+  if keyword_set(decode) then return
+  
+  jdlimits = info.jdlimits
+
+  ;; -------------------------------------------------------
+  ;; Handle case of de200_new.fits format
+  if info.format EQ 'DENEW' then begin
+      if objnum NE 3 AND objnum NE 11 AND objnum NE 1000 then $
+        message, 'ERROR: DENEW ephemeris table does not support body #'+$
+        strtrim(objnum,2)
+      
+      jplephinterp_denew, info, raw, objnum, t, x, y, z, vx, vy, vz, $
+        velocity=vel, tbase=tbase
+
+      if objnum GE 1000 then return
+      goto, DO_UNIT
+  endif
+
+  ;; -------------------------------------------------------
+  ;; Otherwise, construct the ephemeris using the Chebyshev expansion
+  case obj of
+      3: begin ;; EARTH (translate from earth-moon barycenter to earth)
+          ;; Interpolate the earth-moon and moon ephemerides
+          jplephinterp_calc, info, raw, 2, velocity=vel, tbase=tbase, $
+            t, xem, yem, zem, vxem, vyem, vzem
+          jplephinterp_calc, info, raw, 9, velocity=vel, tbase=tbase, $
+            t, xmo, ymo, zmo, vxmo, vymo, vzmo
+          emrat = info.emrat
+          
+          ;; Translate from the earth-moon barycenter to earth
+          x = xem - emrat * xmo
+          y = yem - emrat * ymo
+          z = zem - emrat * zmo
+          if keyword_set(vel) then begin
+              vx = vxem - emrat * vxmo
+              vy = vyem - emrat * vymo
+              vz = vzem - emrat * vzmo
+          endif
+          
+      end
+
+      10: begin ;; MOON (translate from earth-moon barycenter to moon)
+          jplephinterp_calc, info, raw, 9, t, velocity=vel, tbase=tbase, $
+            x, y, z, vx, vy, vz
+          ;; Moon ephemeris is geocentered.  If the center is
+          ;; explicitly earth then return immediately.  Otherwise
+          ;; follow the standard path via the solar barycenter.
+          if n_elements(cent) GT 0 then begin
+              jplephinterp, info, objectname=cent[0], tbase=tbase, $
+                xobjnum=cent1, /decode_obj
+              if cent1 EQ 3 then goto, DO_UNIT
+          endif
+
+          ;; Use solar barycenter via the earth-moon barycenter
+          jplephinterp_calc, info, raw, 2, t, velocity=vel, tbase=tbase, $
+            xem, yem, zem, vxem, vyem, vzem
+          emrat = 1d - info.emrat
+          x = xem + emrat * x
+          y = yem + emrat * y
+          z = zem + emrat * z
+          if keyword_set(vel) then begin
+              vx = vxem + emrat * vx
+              vy = vyem + emrat * vy
+              vz = vzem + emrat * vz
+          endif              
+      end
+
+      12: begin ;; SOLARBARY
+          x = t*0D & y = x & z = x
+          vx = x   & vy = x & vz = x
+      end
+
+      13: begin ;; EARTHBARY
+          jplephinterp_calc, info, raw, 2, velocity=vel, tbase=tbase, $
+            t, x, y, z, vx, vy, vz
+      end
+      
+      14: begin ;; NUTATIONS
+          ;; X = PSI, Y = EPSILON, VX = PSI DOT, VY = EPSILON DOT 
+          jplephinterp_calc, info, raw, 11, velocity=vel, tbase=tbase, $
+            t, x, y, z, vx, vy, vz
+          goto, CLEAN_RETURN
+      end
+
+      15: begin ;; LIBRATIONS
+          jplephinterp_calc, info, raw, 12, velocity=vel, tbase=tbase, $
+            t, x, y, z, vx, vy, vz
+          goto, CLEAN_RETURN
+      end
+
+      1000: begin ;; TDT to TDB conversion
+          x = tdb2tdt(t, deriv=vx, tbase=tbase)
+          if n_elements(velunit0) GT 0 then begin
+             ;; Special case of unit conversion when user asks for 
+             ;; "per second"
+             if strpos(strupcase(velunit0[0]),'/S') GE 0 then $
+                vx = vx / 86400d
+          endif
+
+          goto, CLEAN_RETURN
+      end
+
+      else: begin
+          ;; Default objects are derived from the index OBJNUM
+          if obj GE 1 AND obj LE 11 then begin
+              RESTART_OBJ:
+              jplephinterp_calc, info, raw, obj-1, t, velocity=vel, $
+                tbase=tbase, $
+                x, y, z, vx, vy, vz
+          endif else begin
+              if info.edited AND obj GT 11 then begin
+                  ;; Handle case of edited JPL ephemerides - they
+                  ;; start at a value of 100, so shift them to the end
+                  ;; of the JPL ephemeris columns
+                  obj = obj - 100 + 14
+                  if obj LE n_elements(info.objname) then $
+                    goto, RESTART_OBJ
+              endif
+              message, 'ERROR: body '+strtrim(obj,2)+' is not supported'
+          endelse
+      end
+  endcase
+
+  ;; -------------------------------------------------------
+  ;; Compute ephemeris of center, and compute displacement vector
+  COMPUTE_CENTER:
+  if n_elements(cent) GT 0 then begin
+      jplephinterp, info, raw, t, x0, y0, z0, vx0, vy0, vz0, tbase=tbase, $
+        objectname=cent, velocity=vel, posunits='KM', velunits='KM/DAY'
+      x = temporary(x) - temporary(x0)
+      y = temporary(y) - temporary(y0)
+      z = temporary(z) - temporary(z0)
+      if keyword_set(vel) then begin
+          vx = temporary(vx) - temporary(vx0)
+          vy = temporary(vy) - temporary(vy0)
+          vz = temporary(vz) - temporary(vz0)
+      endif
+  endif
+
+  DO_UNIT:
+  
+  velfac = 1d
+
+  ;; -------------------------------------------------------
+  ;; Convert positional units
+  if n_elements(outunit0) GT 0 then begin
+      pu = strupcase(strtrim(outunit0[0],2))
+      case pu of
+          'KM': km = 1 ;; Dummy statement
+          'CM': begin
+              x = x * 1D5
+              y = y * 1D5
+              z = z * 1D5
+              velfac = velfac * 1D5
+          end
+          'AU': begin
+              au = info.au*info.c/1000d
+              x = x / au
+              y = y / au
+              z = z / au
+              velfac = velfac / au
+          end
+          'LT-S': begin
+              c = info.c / 1000d
+              x = x / c
+              y = y / c
+              z = z / c
+              velfac = velfac / c
+          end
+          ELSE: message, 'ERROR: Unrecognized position units "'+pu+'"'
+      endcase
+  endif
+
+  ;; -------------------------------------------------------
+  ;; Convert velocity units
+  if n_elements(velunit0) GT 0 AND keyword_set(vel) then begin
+      vu = strupcase(strtrim(velunit0[0],2))
+      case vu of 
+          'CM/S': begin
+              vx = vx * (1D5/86400D)
+              vy = vy * (1D5/86400D)
+              vz = vz * (1D5/86400D)
+              velfac = velfac / (1D5/86400D)
+          end
+          'KM/S': begin
+              vx = vx * (1D/86400D)
+              vy = vy * (1D/86400D)
+              vz = vz * (1D/86400D)
+              velfac = velfac / (1D/86400D)
+          end
+          'LT-S/S': begin
+              c = info.c / 1000D
+              vx = vx / (c*86400D)
+              vy = vy / (c*86400D)
+              vz = vz / (c*86400D)
+              velfac = velfac / (c*86400D)
+          end
+          'V/C': begin ;; Unitless ratio V/C (same as LT-S/S
+              c = info.c / 1000D
+              vx = vx / (c*86400D)
+              vy = vy / (c*86400D)
+              vz = vz / (c*86400D)
+              velfac = velfac / (c*86400D)
+          end	  
+          'KM/DAY': km = 1 ;; Dummy statement
+          'AU/DAY': begin
+              au = info.au*info.c/1000d
+              vx = vx / au
+              vy = vy / au
+              vz = vz / au
+              velfac = velfac * au
+          end
+          ELSE: message, 'ERROR: Unrecognized velocity units "'+vu+'"'
+      endcase
+  endif
+
+CLEAN_RETURN:  
+  return
+end
diff --git a/Code/script_idl_mv/astrolib/jplephread.pro b/Code/script_idl_mv/astrolib/jplephread.pro
new file mode 100644
index 0000000000000000000000000000000000000000..841679fa5213c747edf24490bc960ce610690f41
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/jplephread.pro
@@ -0,0 +1,404 @@
+;+
+; NAME:
+;   JPLEPHREAD
+;
+; AUTHOR:
+;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
+;   craigm@lheamail.gsfc.nasa.gov
+;   UPDATED VERSIONs can be found on my WEB PAGE: 
+;      http://cow.physics.wisc.edu/~craigm/idl/idl.html
+;
+; PURPOSE:
+;   Open and read JPL DE200 or DE405 Ephemeride FITS File
+;
+; MAJOR TOPICS:
+;   Planetary Orbits, Interpolation
+;
+; CALLING SEQUENCE:
+;   JPLEPHREAD, FILENAME, INFO, RAWDATA, JDLIMITS, STATUS=, ERRMSG=
+;
+; DESCRIPTION:
+;
+;   JPLEPHREAD opens and reads the JPL DE200 or DE405 planetary
+;   ephemerides, as available in FITS format.  The user must have the
+;   IDL Astronomy Library installed to use this routine.
+;
+;   This routine is the initialization stage of a two-stage process to
+;   interpolate the JPL ephemeris.  In this first stage, the file is
+;   opened, and the relevant portions of the table are read and stored
+;   into the two variables INFO and RAWDATA.  In the second stage, the
+;   user actually interpolates the ephemeris for the desired bodies
+;   and to the desired ephemeris time using JPLEPHINTERP.
+;
+;   Users must decide ahead of time the approximate dates of interest,
+;   and pass this range in the JDLIMITS parameter.  Any date covered
+;   by the ephemeris is valid.
+;
+;   JPLEPHREAD is able to read files of the following format:
+;     DE200 - Chebyshev - FITS format - Note 1
+;     DE405 - Chebyshev - FITS format - Note 1
+;     DE200 - Taylor    - FITS format - Note 2
+;
+;   Note 1 - Chebyshev formatted FITS files are available in the
+;            AXBARY package by Arnold Rots, found here:
+;              ftp://heasarc.gsfc.nasa.gov/xte/calib_data/clock/bary/
+;            or at the Markwardt FTP site:
+;              ftp://cow.physics.wisc.edu/pub/craigm/bary/
+;
+;   Note 2 - Taylor-series based ephemerides have been available for
+;            years in the FTOOLS / LHEASOFT package produced by NASA's
+;            Goddard Space Flight Center.  The original file is
+;            de200_new.fits, which covers the years 1959-2000,
+;            inclusive.  A newer file is named
+;            de200_1950-2050_v2.fits, and covers the years 1959-2050.
+;            See Markwardt FTP site for these files.
+;
+; PARAMETERS: 
+;
+;   FILENAME - name of ephemeris file (scalar string).
+;
+;   INFO - upon completion, information about the ephemeris data is
+;          returned in this parameter in the form of a structure.
+;          Users must not modify INFO, although several fields are
+;          useful and may be accessed read-only:
+;              TSTART/TSTOP (start and stop time of data in Julian
+;                            days);
+;              C (speed of light in m/s);
+;              DENUM (development ephemeris number [200 or 405])
+;              AU (1 astronomical unit, in units of light-seconds)
+;
+;   RAWDATA - upon completion, raw ephemeris data is returned in this
+;             parameter.  Users are not meant to access this data
+;             directly, but rather to pass it to JPLEPHINTERP.
+;
+;   JDLIMITS - a two-element vector (optional), describing the desired
+;              time range of interest.  The vector should have the
+;              form [TSTART, TSTOP], where TSTART and TSTOP are the
+;              beginning and ending times of the range, expressed in
+;              Julian days.
+;              Default: entire table is read (note, this can be
+;              several megabytes)
+;
+;
+; KEYWORD PARAMETERS:
+;
+;   STATUS - upon completion, a value of 1 indicates success, and 0
+;            indicates failure.
+;
+;   ERRMSG - upon completion, an error message is returned in this
+;            keyword.  If there were no errors, then the returned
+;            value is the empty string, ''.
+;
+;
+; EXAMPLE:
+;
+;   Find position of earth at ephemeris time 2451544.5 JD.  Units are
+;   in Astronomical Units.
+;
+;   JPLEPHREAD, 'JPLEPH.405', pinfo, pdata, [2451544D, 2451545D]
+;
+;   JPLEPHINTERP, pinfo, pdata, 2451544.5D, xearth, yearth, zearth, $
+;                 /EARTH, posunits='AU'
+;     
+;
+; REFERENCES:
+;
+;   AXBARY, Arnold Rots.
+;      ftp://heasarc.gsfc.nasa.gov/xte/calib_data/clock/bary/
+;
+;   HORIZONS, JPL Web-based ephermis calculator (Ephemeris DE406)
+;      http://ssd.jpl.nasa.gov/?horizons
+;   
+;   JPL Export Ephemeris FTP Site
+;      ftp://ssd.jpl.nasa.gov/pub/eph/planets/
+;      (ephemeris files are available here, however, they must be
+;      converted to FITS format using the "bin2eph" utility found in
+;      AXBARY)
+;
+;   JPL Export Ephemeris CD-ROM - Ordering Information
+;      http://www.willbell.com/software/jpl.htm
+;
+;   Standish, E.M. 1982, "Orientation of the JPL Ephemerides,
+;      DE200/LE200, to the Dynamical Equinox of J2000", Astronomy &
+;      Astrophysics, vol. 114, pp. 297-302.
+;
+;   Standish, E.M.: 1990, "The Observational Basis for JPL's DE200,
+;      the planetary ephemeris of the Astronomical Almanac", Astronomy
+;      & Astrophysics, vol. 233, pp. 252-271.    
+;
+; SEE ALSO
+;   JPLEPHREAD, JPLEPHINTERP, JPLEPHTEST
+; PROCEDURES USED:
+;     FXBCLOSE, FXBOPEN, FXPAR(), 
+;   
+; MODIFICATION HISTORY:
+;   Written and Documented, CM, Jun 2001
+;   Use GETTOK() instead of STR_SEP()  W. Landsman  July 2002
+;   Add ephemeris file keywords to INFO, Jan 2002, CM
+;   Add fields to INFO to be consistent with JPLEPHMAKE, 04 Mar 2002, CM
+;   Correction of units for INFO.C (Thanks Mike Bernhardt), 2011-04-11, CM 
+;  $Id: jplephread.pro,v 1.10 2011/06/27 18:44:44 cmarkwar Exp $
+;
+;-
+; Copyright (C) 2001, Craig Markwardt
+; This software is provided as is without any warranty whatsoever.
+; Permission to use, copy and distribute unmodified copies for
+; non-commercial purposes, and to modify and use for personal or
+; internal use, is granted.  All other rights are reserved.
+;-
+
+
+function jplephpar, header, parname, default=default, fatal=fatal
+compile_opt idl2
+
+  ; '$Id: jplephread.pro,v 1.6 2001/07/01 03:32:02 craigm Exp $'
+
+  value = fxpar(header, parname, Count = N_value)
+  if N_value EQ 0 then begin
+      if keyword_set(fatal) then $
+        message, 'ERROR: keyword '+strupcase(parname)+' was not found'
+        return, default
+  endif
+  return, value
+end
+
+function jplephval, names, values, name, default=default, fatal=fatal
+  wh = where(names EQ strupcase(name), ct)
+  if ct EQ 0 then begin
+      if keyword_set(fatal) then $
+        message, 'ERROR: value '+strupcase(name)+' was not found in file'
+      return, default
+  endif
+  return, values[wh[0]]
+end
+
+pro jplephread, filename, info, raw, jdlimits, $
+                status=status, errmsg=errmsg
+
+  status = 0
+  printerror = 1 - arg_present(errmsg)
+  errmsg = ''
+
+  if n_params() EQ 0 then begin
+      message, 'USAGE: JPLEPHREAD, filename, info, rawdata, jdlimits', /info
+      return
+  endif
+
+;  if n_elements(jdlimits) LT 2 then begin
+;      errmsg = 'ERROR: You must specify JDLIMITS'
+;      return
+;  endif
+
+  fxbopen, unit, filename, 1, ephhead, errmsg=errmsg
+  if errmsg NE '' then $
+            if printerror then message,errmsg else return
+
+  extname = strtrim(fxpar(ephhead, 'EXTNAME'),2)
+  ttype1  = strtrim(fxpar(ephhead, 'TTYPE1'),2)
+
+  if (extname EQ 'EPHEM' AND ttype1 EQ 'EARTH') then begin
+      ;; This is the DE200_NEW format (standard FTOOLS)
+
+      nrows  = fxpar(ephhead, 'NAXIS2')
+      tstart = fxpar(ephhead, 'TSTART')
+      tstop  = fxpar(ephhead, 'TSTOP')
+      timedel = jplephpar(ephhead, 'TIMEDEL', default=1D) ;; 1-day default
+
+      ;; Constants from XTEBARYCEN.F
+      C=2.99792458D+8
+      TWOPI=6.28318530717958648D0
+      DAYSEC=1.D0/86400.D0
+      AULTSC=499.004782D0
+      GAUSS=0.01720209895D0
+      RSCHW=(GAUSS^2)*(AULTSC^3)*(DAYSEC^2)
+      SUNRAD=2.315D0
+
+      if n_elements(jdlimits) GE 2 then begin
+          if (min(jdlimits) LT tstart OR $
+              max(jdlimits) GT tstop) then begin
+              errmsg = 'ERROR: '+filename+$
+                ' does not cover the time of interest'
+              fxbclose, unit
+              return
+          endif
+          ;; Expand by one row either side
+          rowlimits = floor((jdlimits-tstart)/timedel) + [-2,2]
+          rowlimits = rowlimits > 1 < nrows
+      endif else begin
+          jdlimits  = [tstart, tstop]
+          rowlimits = [1L, nrows]
+      endelse
+
+      ;; Read raw data
+      fxbread, unit, cearth, 'EARTH', rowlimits, errmsg=errmsg
+      if errmsg EQ '' then $
+        fxbread, unit, csun, 'SUN', rowlimits, errmsg=errmsg
+      if errmsg EQ '' then $
+        fxbread, unit, ctdb2tdt, 'TIMEDIFF', rowlimits, errmsg=errmsg
+      fxbclose, unit
+      if errmsg NE '' then $
+         if printerror then message,errmsg else return
+
+      nr = rowlimits[1]-rowlimits[0]+1
+      t0 = dindgen(nr)*timedel - (jdlimits[1]-jdlimits[0])/2D
+      dtt = spl_init(t0, ctdb2tdt)
+      raw = reform(dblarr(18, nr), 18, nr, /overwrite)
+      raw[0 :11,*] = cearth * c/1000D  ;; units of lt-s
+      raw[12:14,*] = csun * c/1000D    ;; units of lt-s/day
+      raw[15,   *] = t0
+      raw[16   ,*] = ctdb2tdt
+      raw[17   ,*] = dtt
+
+      jdlimits1 = (rowlimits+[-1,0])*timedel + tstart
+
+      info = {filename: filename, edited: 0L, $
+              creation_date: '', author: '', $
+              nrows: nrows, tstart: tstart, tstop: tstop, $
+              timedel: timedel, format: 'DENEW', $
+              denum: 200L, c: c, emrat: 0.012150586D, $
+              au: aultsc, msol: rschw, sunrad: sunrad, $
+              jdlimits: jdlimits1, jdrows: nr }
+
+
+  endif else if (extname EQ 'DE1' AND ttype1 EQ 'Cname') then begin
+      ;; This is the BINEPH2FITS format (either DE200 or DE405)
+
+      ;; ---------------------------------------------
+      ;; First extension contains parameter data
+      fxbread, unit, cname, 'Cname'
+      fxbread, unit, cvalue, 'Cvalue'
+      cname = strtrim(cname,2)
+
+      denum = 0L & clight = 0D & emrat = 0D & au = 0D
+      msol = 0D & radsol = 0D
+
+      denum  = round(jplephval(cname, cvalue, 'DENUM', /fatal))
+      clight = jplephval(cname, cvalue, 'CLIGHT', /fatal)
+      emrat  = jplephval(cname, cvalue, 'EMRAT',  /fatal)
+      au     = jplephval(cname, cvalue, 'AU',     /fatal)    ; km
+      msol   = jplephval(cname, cvalue, 'GMS',    /fatal)    ; AU^3/day^2
+      radsol = jplephval(cname, cvalue, 'RADS', default=-1D) ; km
+      if radsol EQ -1D then $
+        radsol = jplephval(cname, cvalue, 'ASUN', default=-1D)
+
+      emrat = 1D / (1D + emrat)
+        
+      if clight EQ 0 then begin
+          errmsg = 'ERROR: Could not load physical constants from '+filename
+          fxbclose, unit
+          return
+      endif
+ 
+      x = au / clight                     ;; AU (lt sec)     
+      msol = msol * x * x * x / 86400D^2  ;; GM_sun (in lt sec)
+      radsol = radsol / clight            ;; Solar radius (lt sec)
+      clight = clight * 1000              ;; Speed of light (m/s)
+      
+      fxbclose, unit
+
+      ;; ---------------------------------------------
+      ;; Second extension contains accounting data
+      fxbopen, unit, filename, 2, ephhead, errmsg=errmsg
+      if errmsg NE '' then $
+            if printerror then message,errmsg else return
+
+      extname = strtrim(fxpar(ephhead, 'EXTNAME'),2)
+      if extname NE 'DE2' then begin
+          errmsg = 'ERROR: '+filename+' is not a JPL ephemeris file'
+          fxbclose, unit
+          return
+      endif
+
+      fxbread, unit, ephobj, 'Object', errmsg=errmsg
+      if errmsg EQ '' then $
+        fxbread, unit, ephptr, 'Pointer', errmsg=errmsg
+      if errmsg EQ '' then $
+        fxbread, unit, ephncoeff, 'NumCoeff', errmsg=errmsg
+      if errmsg EQ '' then $
+        fxbread, unit, ephnsub, 'NumSubIntv', errmsg=errmsg
+      fxbclose, unit
+      if errmsg NE '' then begin
+            errmsg = 'ERROR: could not read '+filename+' extension 2'
+            if printerror then message,errmsg else return
+      endif
+
+      ;; Trim each object name to first word only
+          ephobj = strupcase(gettok(ephobj, ' '))
+      
+      ;; ---------------------------------------------
+      ;; Third extension contains Chebyshev coefficients
+      fxbopen, unit, filename, 3, ephhead, errmsg=errmsg
+      if errmsg NE '' then return
+      extname = strtrim(fxpar(ephhead, 'EXTNAME'),2)
+      if extname NE 'DE3' then begin
+          errmsg = 'ERROR: '+filename+' is not a JPL ephemeris file'
+          fxbclose, unit
+          if printerror then message,errmsg else return
+      endif
+      
+      nrows  = fxpar(ephhead, 'NAXIS2')
+      tstart = fxpar(ephhead, 'TSTART')
+      tstop  = fxpar(ephhead, 'TSTOP')
+      timedel = jplephpar(ephhead, 'TIMEDEL', default=32D) ;; 32-day default
+
+      if floor((tstop-tstart + 0.5)/timedel) NE nrows then begin
+          errmsg = 'ERROR: Incorrect number of rows in '+filename
+          fxbclose, unit
+          if printerror then message,errmsg else return
+      endif
+
+      if n_elements(jdlimits) GE 2 then begin
+          if (min(jdlimits) LT tstart OR $
+              max(jdlimits) GT tstop) then begin
+              errmsg = 'ERROR: '+filename+$
+                ' does not cover the time of interest'
+              fxbclose, unit
+              if printerror then message,errmsg else return
+          endif
+          ;; Expand by two rows either side
+          rowlimits = floor((jdlimits-tstart)/timedel) + [-2,2]
+          rowlimits = rowlimits > 1 < nrows
+      endif else begin
+          jdlimits  = [tstart, tstop]
+          rowlimits = [1L, nrows]
+      endelse
+
+      ;; Read raw data
+      dims = fxbdimen(unit, 'ChebCoeffs')
+      fxbread, unit, coeffs, 'ChebCoeffs', rowlimits, errmsg=errmsg
+      fxbclose, unit
+      if errmsg NE '' then $
+         if printerror then message,errmsg else return
+
+
+      raw = reform(coeffs, [dims, rowlimits[1]-rowlimits[0]+1], /overwrite)
+
+      jdlimits1 = (rowlimits+[-1,0])*timedel + tstart
+      if (abs(min(raw[0,*]) - jdlimits1[0]) GT 1d-6 OR $
+          abs(max(raw[1,*]) - jdlimits1[1]) GT 1d-6) then begin
+          errmsg = 'ERROR: JDLIMITS and time column do not match'
+          if printerror then message,errmsg else return
+      endif
+
+      nr = rowlimits[1]-rowlimits[0]+1
+     info = {filename: filename, edited: 0L, $
+              creation_date: '', author: '', $
+              nrows: nrows, tstart: tstart, tstop: tstop, $
+              timedel: timedel, format: 'BINEPH2FITS', $
+              denum: denum, c: clight, emrat: emrat, $
+              au: au*1000/clight, msol: msol, sunrad: radsol, $
+              jdlimits: jdlimits1, jdrows: nr, $
+              objname: ephobj, ptr: ephptr, ncoeff: ephncoeff, $
+              nsub: ephnsub, keywords: cname, keyvalues: cvalue}
+;                 aufac: 1D/clight, velfac: 2D/(timedel*86400D), $
+
+  endif else begin
+      errmsg = 'ERROR: '+filename+' was not in a recognized format'
+      fxbclose, unit
+      if printerror then message,errmsg else return
+  endelse
+
+  errmsg = ''
+  status = 1
+  return
+end
diff --git a/Code/script_idl_mv/astrolib/jplephtest.pro b/Code/script_idl_mv/astrolib/jplephtest.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5f441d38cbe5d5c9d6c76668f3fd787700729c93
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/jplephtest.pro
@@ -0,0 +1,194 @@
+;+
+; NAME:
+;   JPLEPHTEST
+;
+; AUTHOR:
+;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
+;   craigm@lheamail.gsfc.nasa.gov
+;   UPDATED VERSIONs can be found on my WEB PAGE: 
+;      http://cow.physics.wisc.edu/~craigm/idl/idl.html
+;
+; PURPOSE:
+;   Test JPLEPHTEST with JPL test data set
+;
+; MAJOR TOPICS:
+;   Planetary Orbits, Interpolation
+;
+; CALLING SEQUENCE:
+;   JPLEPHTEST, EPHFILE, TESTFILE
+;
+; DESCRIPTION:
+;
+;   JPLEPHTEST tests the JPLEPHINTERP procedure for precision.  In
+;   order to function, you must have a JPL ephemeris test data set.
+;   The test data set testpo.405 is available in 
+;   ftp://idlastro.gsfc.nasa.gov/pub/data
+;
+;   The procedure opens and reads the test set, which contains
+;   precomputed data.  Every tenth value is printed on the screen.
+;   Any deviations that exceed 1.5d-13 AU = 1.5 cm are reported.
+;
+;   The columns are labelled according to the input file, except for
+;   the final column, which is the deviation between the input file
+;   and the computed value.
+;
+;
+; PARAMETERS: 
+;
+;   EPHFILE - a scalar string, specifies the name of the ephemeris
+;             file, in FITS format.    JPLEPHTEST will look in the directory
+;             $ASTRO_DATA for the file if it is not in the current directory.
+;
+;   TESTFILE - a scalar string, specifies JPL test data set to compare
+;              against.   JPLEPHTEST will look in the directory
+;             $ASTRO_DATA for the file if it is not in the current directory.
+;
+;
+; EXAMPLE:
+;
+;   Test JPL DE200 and DE405 ephemerides.  Assumes files are in the
+;   current directory.
+;
+;   JPLEPHTEST, 'JPLEPH.200', 'testpo.200'
+;   JPLEPHTEST, 'JPLEPH.405', 'testpo.405'
+;     
+;
+; REFERENCES:
+;
+;   JPL Export Ephemeris FTP Site
+;      ftp://ssd.jpl.nasa.gov/pub/eph/planets/
+;      (see test-data/ for test data sets)
+;   
+;   HORIZONS, JPL Web-based ephermis calculator (Ephemeris DE406)
+;      http://ssd.jpl.nasa.gov/horizons.html
+;
+;
+; SEE ALSO
+;   JPLEPHREAD, JPLEPHINTERP, JPLEPHTEST
+;   
+; MODIFICATION HISTORY:
+;   Written and Documented, CM, Jun 2001
+;   Removed TRANSREAD, improved output, improved docs, CM, 9 Jul 2001
+;
+;  $Id: jplephtest.pro,v 1.4 2001/07/20 13:29:53 craigm Exp $
+;
+;-
+; Copyright (C) 2001, Craig Markwardt
+; This software is provided as is without any warranty whatsoever.
+; Permission to use, copy and distribute unmodified copies for
+; non-commercial purposes, and to modify and use for personal or
+; internal use, is granted.  All other rights are reserved.
+;-
+
+pro jplephtest, ephfile, testfile, pause=pause
+
+  if n_params() EQ 0 then begin
+      message, 'USAGE: JPLEPHTEST, EPHFILE, TESTFILE', /info
+      return
+  endif
+
+  testdata = find_with_def( testfile, 'ASTRO_DATA')
+  openr, unit, testdata, /get_lun, error=err
+  if err NE 0 then begin
+      message, 'ERROR: could not open '+testdata
+      return
+  endif
+
+  ;; Read header of file, up to and including the EOT line
+  repeat begin
+      line = ''
+      readf, unit, line
+  endrep until strupcase(strmid(line,0,3)) EQ 'EOT'
+
+  ;; Read at least 20000 lines from file
+  data = replicate({denum:0L, caldate: '', jd: 0D, targ: 0L, $
+                    cent: 0L, coord: 0L, value: 0D}, 20000)
+  on_ioerror, DONE
+  readf, unit, data, format='(I5,A10,D0,I0,I0,I0,D0)'
+  DONE:
+  rc = floor((fstat(unit)).transfer_count/7)
+  on_ioerror, NULL
+  free_lun, unit
+
+  if rc LT 10 then begin
+      message, 'ERROR: could not read input data'
+  endif
+
+  ;; Cull the data out of the structure
+  data = data[0:rc-1]
+  denum = data.denum & caldate = data.caldate & jd = data.jd 
+  targ = data.targ & cent = data.cent & coord = data.coord
+  value = data.value
+  data = 0
+      
+  bad = cent*0
+
+  ephdata = find_with_def(ephfile, 'ASTRO_DATA')
+  jplephread, ephdata, pinfo, pdata, status=st, errmsg=errmsg
+  if st EQ 0 then begin
+      message, errmsg
+  endif
+  if denum[0] NE pinfo.denum then begin
+      message, 'ERROR: test file and ephemeris are not of same version'
+  endif
+
+  wh = where(jd GE pinfo.tstart AND jd LE pinfo.tstop, totct)
+  if totct EQ 0 then begin
+      message, 'ERROR: test file and ephemeris do not overlap'
+  endif
+
+  j = 0L
+  for i = 0L, totct-1 do begin
+
+      if coord[wh[i]] GE 4 then vel = 1 else vel = 0
+      if targ[wh[i]] GE 14 then vel = 1  ;; Always for nut. & libr.
+      jplephinterp, pinfo, pdata, jd[wh[i]], x, y, z, vx, vy, vz, $
+        objectname=targ[wh[i]], center=cent[wh[i]], $
+        posunits='AU', velunits='AU/DAY', velocity=vel
+
+      case coord[wh[i]] of 
+          1: newval = x
+          2: newval = y
+          3: newval = z
+          4: newval = vx
+          5: newval = vy
+          6: newval = vz
+          else: message, 'ERROR: coordinate '+coord[wh[i]]+' does not exist'
+      endcase
+
+      ;; Nutations are handled differently than PLEPH
+      if targ[wh[i]] EQ 14 AND coord[wh[i]] GT 2 then begin
+          if coord[wh[i]] EQ 3 then newval = vx $
+          else                      newval = vy
+      endif
+
+      del = abs(newval - value[wh[i]])
+      if targ[wh[i]] EQ 15 AND coord[wh[i]] EQ 3 then $
+        del = del/(0.23d0*(jd[wh[i]]-2451545.d0))
+      if del GE 1.5d-13 OR (i MOD 10) EQ 0 then begin
+          if del GE 1.5d-13 then begin
+              print, '****** WARNING: Large difference ******'
+              bad[wh[i]] = 1
+          endif
+          if j GT 300 then j = 0L
+          if j EQ 0 then $
+            print, 'REC#', 'Jul. Day', 'Targ', 'Cent', 'Coor', $
+            'Value', 'Deviation', format='(A6,A10,3(A5),1(A20),A22)'
+          print, i+1, jd[wh[i]], targ[wh[i]], cent[wh[i]], coord[wh[i]], $
+            value[wh[i]], del, $
+            format='(I6,D10.1,3(I5),1(D20.13),E22.13)'
+      endif
+
+      j = j + 1
+  endfor
+
+  if keyword_set(pause) AND total(bad) NE 0 then stop
+  wh = where(bad, ct)
+  print, ''
+  print, '***********************************'
+  print, ' Time Range (Julian Days): ', minmax(jd)
+  print, ' Number of Records: ', totct
+  print, ' Erroneous Records: ', ct
+
+end
+
diff --git a/Code/script_idl_mv/astrolib/jprecess.pro b/Code/script_idl_mv/astrolib/jprecess.pro
new file mode 100644
index 0000000000000000000000000000000000000000..bf843af8955e2b93b90b9918919304df7b763258
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/jprecess.pro
@@ -0,0 +1,226 @@
+pro jprecess, ra, dec, ra_2000, dec_2000, MU_RADEC = mu_radec,  $
+                  PARALLAX = parallax,  RAD_VEL = rad_vel, EPOCH = epoch
+;+
+; NAME:
+;      JPRECESS
+; PURPOSE:
+;      Precess astronomical coordinates from B1950 to J2000
+; EXPLANATION:
+;      Calculate the mean place of a star at J2000.0 on the FK5 system from the
+;      mean place at B1950.0 on the FK4 system.
+;
+;      Use BPRECESS for the reverse direction J2000 ==> B1950
+; CALLING SEQUENCE:
+;      jprecess, ra, dec, ra_2000, dec_2000, [ MU_RADEC = , PARALLAX = 
+;               RAD_VEL =, EPOCH =   ]
+;
+; INPUTS:
+;      RA,DEC - input B1950 right ascension and declination in *degrees*.
+;               Scalar or vector
+;
+; OUTPUTS:
+;      RA_2000, DEC_2000 - the corresponding J2000 right ascension and 
+;               declination in *degrees*.   Same number of elements as RA,DEC
+;               but always double precision. 
+;
+; OPTIONAL INPUT-OUTPUT KEYWORDS
+;      MU_RADEC - 2xN element double precision vector containing the proper 
+;                  motion in seconds of arc per tropical *century* in right 
+;                  ascension and declination.
+;      PARALLAX - N_element vector giving stellar parallax (seconds of arc)
+;      RAD_VEL  - N_element vector giving radial velocity in km/s
+;
+;       The values of MU_RADEC, PARALLAX, and RADVEL will all be modified
+;       upon output to contain the values of these quantities in the
+;       J2000 system.    Values will also be converted to double precision.  
+;       The parallax and radial velocity will have a very minor influence on 
+;       the J2000 position.
+;
+;       EPOCH - scalar giving epoch of original observations, default 1950.0d
+;           This keyword value is only used if the MU_RADEC keyword is not set.
+;  NOTES:
+;       The algorithm is taken from the Explanatory Supplement to the 
+;       Astronomical Almanac 1992, page 184.
+;       Also see Aoki et al (1983), A&A, 128,263
+;
+;       JPRECESS distinguishes between the following two cases:
+;       (1) The proper motion is known and non-zero
+;       (2) the proper motion is unknown or known to be exactly zero (i.e.
+;               extragalactic radio sources).   In this case, the algorithm
+;               in Appendix 2 of Aoki et al. (1983) is used to ensure that
+;               the output proper motion is  exactly zero.    Better precision
+;               can be achieved in this case by inputting the EPOCH of the
+;               original observations.
+;
+;       The error in using the IDL procedure PRECESS for converting between
+;       B1950 and J2000 can be up to 12", mainly in right ascension.   If
+;       better accuracy than this is needed then JPRECESS should be used.
+;
+; EXAMPLE:
+;       The SAO catalogue gives the B1950 position and proper motion for the 
+;       star HD 119288.   Find the J2000 position. 
+;
+;          RA(1950) = 13h 39m 44.526s      Dec(1950) = 8d 38' 28.63''  
+;          Mu(RA) = -.0259 s/yr      Mu(Dec) = -.093 ''/yr
+;
+;       IDL> mu_radec = 100D* [ -15D*.0259, -0.093 ]
+;       IDL> ra = ten(13,39,44.526)*15.D 
+;       IDL> dec = ten(8,38,28.63)
+;       IDL> jprecess, ra, dec, ra2000, dec2000, mu_radec = mu_radec
+;       IDL> print, adstring(ra2000, dec2000,2)
+;               ===> 13h 42m 12.740s    +08d 23' 17.69"
+;
+; RESTRICTIONS:
+;      "When transferring individual observations, as opposed to catalog mean
+;       place, the safest method is to tranform the observations back to the
+;       epoch of the observation, on the FK4 system (or in the system that was
+;       used to to produce the observed mean place), convert to the FK5 system,
+;       and transform to the the epoch and equinox of J2000.0" -- from the
+;       Explanatory Supplement (1992), p. 180
+;
+; REVISION HISTORY:
+;       Written,    W. Landsman                September, 1992
+;       Corrected a couple of typos in M matrix   October, 1992
+;       Vectorized, W. Landsman                   February, 1994
+;       Implement Appendix 2 of Aoki et al. (1983) for case where proper
+;       motion unknown or exactly zero     W. Landsman    November, 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Fixed typo in updating proper motion   W. Landsman   April 1999
+;       Make sure proper motion is floating point  W. Landsman December 2000
+;       Use V6.0 notation  W. Landsman Mar 2011
+;-   
+  On_error,2
+  compile_opt idl2
+
+  if N_params() LT 4 then begin
+       print,'Syntax - JPRECESS, ra,dec, ra_2000, dec_2000, [MU_RADEC =' 
+       print,'                            PARALLAX = , RAD_VEL = ]'
+       print,'Input RA and Dec should be given in DEGREES for B1950'
+       print,'Proper motion, MU_RADEC, (optional) in arc seconds per *century*'
+       print,'Parallax (optional) in arc seconds'      
+       print,'Radial Velocity (optional) in km/s'
+       return
+
+  endif
+
+  N = N_elements( ra )
+  if N EQ 0 then message,'ERROR - first parameter (RA vector) is undefined'
+
+  if ~keyword_set( RAD_VEL) then rad_vel = dblarr(N) else begin
+        rad_vel = rad_vel*1.
+        if N_elements( RAD_VEL ) NE N then message, $
+        'ERROR - RAD_VEL keyword vector must contain ' + strtrim(N,2) + ' values'
+  endelse
+
+  if N_elements( MU_RADEC) GT 0 then begin
+         if (N_elements( mu_radec) NE 2*N ) then message, $
+    'ERROR - MU_RADEC keyword (proper motion) be dimensioned (2,' + $
+                 strtrim(N,2) + ')'
+        mu_radec = mu_radec*1.      ;Make sure at least float
+  endif
+
+  if N_elements(epoch) EQ 0 then epoch = 1950.0d0
+
+  if N_elements( Parallax) EQ 0 then parallax = dblarr(N) else $
+        parallax = parallax*1.
+
+  radeg = 180.D/!DPI
+  sec_to_radian = 1./radeg/3600.0d0
+
+ M =  [ [+0.9999256782D, +0.0111820610D, +0.0048579479D,  $
+         -0.000551D,     +0.238514D,     -0.435623D     ], $
+       [ -0.0111820611D, +0.9999374784D, -0.0000271474D,  $ 
+         -0.238565D,     -0.002667D,      +0.012254D      ], $
+       [ -0.0048579477D, -0.0000271765D, +0.9999881997D , $
+         +0.435739D,      -0.008541D,      +0.002117D      ], $
+       [ +0.00000242395018D, +0.00000002710663D, +0.00000001177656D, $
+         +0.99994704D,    +0.01118251D,    +0.00485767D    ], $
+       [ -0.00000002710663D, +0.00000242397878D, -0.00000000006582D, $
+         -0.01118251D,     +0.99995883D,    -0.00002714D    ], $
+       [ -0.00000001177656D, -0.00000000006587D, 0.00000242410173D, $
+         -0.00485767D,   -0.00002718D,     1.00000956D] ] 
+
+ A = 1D-6*[ -1.62557D, -0.31919D, -0.13843D]        ;in radians
+ A_dot = 1D-3*[1.244D, -1.579D, -0.660D ]           ;in arc seconds per century
+
+ if epoch NE 1950.0d then $
+        A = A + sec_to_radian * A_dot * (epoch - 1950.0D)/100.0d
+
+ ra_rad = ra/radeg       &      dec_rad = dec/radeg
+ cosra =  cos( ra_rad )  &       sinra = sin( ra_rad )
+ cosdec = cos( dec_rad ) &      sindec = sin( dec_rad )
+
+ ra_2000 = ra*0.
+ dec_2000 = dec*0.
+
+ for i = 0l, N-1 do begin
+
+ r0 = [ cosra[i]*cosdec[i], sinra[i]*cosdec[i], sindec[i] ]
+
+  if ~keyword_set( MU_RADEC) then begin
+        mu_a = 0.0d0
+        mu_d = 0.0d0
+  endif else begin
+         if (N_elements( mu_radec) NE 2*N ) then message, $
+    'ERROR - MU_RADEC keyword (proper motion) must be dimensioned (2,' + $
+                strtrim(N,2) + ')'
+         mu_a = mu_radec[ 0, i]
+         mu_d = mu_radec[ 1, i ]
+  endelse
+
+ r0_dot = [ -mu_a*sinra[i]*cosdec[i] - mu_d*cosra[i]*sindec[i], $ ;Velocity vector
+             mu_a*cosra[i]*cosdec[i] - mu_d*sinra[i]*sindec[i] , $
+             mu_d*cosdec[i] ]  + 21.095 * rad_vel[i] * parallax[i] * r0
+
+ ; Remove the effects of the E-terms of aberration to form r1 and r1_dot.
+
+ r1 = r0 - A + (total(r0 * A))*r0
+ r1_dot = r0_dot - A_dot + ( total( r0 * A_dot))*r0
+
+ R_1 = [r1, r1_dot]         
+ 
+ R = M # R_1
+
+ if ~keyword_set(mu_RADEC) then begin
+         rr = [ R[0], R[1], R[2]]
+         v =  [ R[3],R[4],R[5] ]
+         t = ((epoch - 1950.0d0) - 50.00021d)/100.0d0
+         rr1 = rr + sec_to_radian*v*t
+         x = rr1[0]  & y = rr1[1]  & Z = rr1[2]  
+ endif else begin
+         x = R[0]  & y = R[1]  & Z = R[2]  
+         x_dot = R[3]  & y_dot= R[4]  &  z_dot = R[5]
+ endelse
+
+ r2 = x^2 + y^2 + z^2
+ rmag = sqrt( r2 )
+ dec_2000[i] = asin( z / rmag)
+ ra_2000[i] = atan( y, x)
+
+ if keyword_set(mu_RADEC) then begin
+         mu_radec[0, i] = ( x*y_dot - y*x_dot) / ( x^2 + y^2)
+         mu_radec[1, i] = ( z_dot* (x^2 + y^2) - z*(x*x_dot + y*y_dot) ) /  $
+                     ( r2*sqrt( x^2 + y^2) )
+ endif
+ 
+  if parallax[i] GT 0. then begin
+      rad_vel[i] = ( x*x_dot + y*y_dot + z*z_dot )/ (21.095*Parallax[i]*rmag)
+      parallax[i] = parallax[i] / rmag
+
+  endif
+ endfor 
+
+ neg = where( ra_2000 LT 0, NNeg )
+ if Nneg GT 0 then ra_2000[neg] = ra_2000[neg] + 2.D*!DPI
+
+ ra_2000 = ra_2000*radeg & dec_2000 = dec_2000*radeg
+
+; Make output scalar if input was scalar
+
+ sz = size(ra)
+ if sz[0] EQ 0 then begin
+        ra_2000 = ra_2000[0]     & dec_2000 = dec_2000[0]
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/juldate.pro b/Code/script_idl_mv/astrolib/juldate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c6d0281ea42ba436d5aa4154061226f1595f9e29
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/juldate.pro
@@ -0,0 +1,121 @@
+PRO JULDATE, DATE, JD, PROMPT = prompt
+;+                                                                  
+; NAME:
+;     JULDATE
+; PURPOSE:                                   
+;     Convert from calendar to Reduced Julian Date
+;
+; EXPLANATION:
+;     Julian Day Number is a count of days elapsed since Greenwich mean noon 
+;     on 1 January 4713 B.C.  The Julian Date is the Julian day number
+;     followed by the fraction of the day elapsed since the preceding noon. 
+;
+;     This procedure duplicates the functionality of the JULDAY() function in
+;     in the standard IDL distribution, but also allows interactive input and
+;     gives output as Reduced Julian date (=JD - 2400000.)  
+
+; CALLING SEQUENCE:
+;     JULDATE, /PROMPT           ;Prompt for calendar Date, print Julian Date
+;               or
+;     JULDATE, date, jd      
+;
+; INPUT:
+;     DATE -  3 to 6-element vector containing year,month (1-12),day, and 
+;              optionally hour, minute, and second all specified as numbers
+;              (Universal Time).   Year should be supplied with all digits.
+;              Years B.C should be entered as negative numbers (and note that
+;              Year 0 did not exist).  If Hour, minute or seconds are not 
+;              supplied, they will default to 0. 
+;
+;  OUTPUT:
+;       JD - Reduced Julian date, double precision scalar.  To convert to
+;               Julian Date, add 2400000.   JULDATE will print the value of
+;               JD at the terminal if less than 2 parameters are supplied, or 
+;               if the /PROMPT keyword is set
+;      
+;  OPTIONAL INPUT KEYWORD:
+;       /PROMPT - If this keyword is set and non-zero, then JULDATE will prompt
+;               for the calendar date at the terminal.
+;
+;  RESTRICTIONS:
+;       The procedure HELIO_JD can be used after JULDATE, if a heliocentric
+;       Julian date is required.
+;
+;  EXAMPLE:
+;       A date of 25-DEC-2006 06:25 UT may be expressed as either
+;
+;       IDL> juldate, [2006, 12, 25, 6, 25], jd       
+;       IDL> juldate, [2006, 12, 25.2673611d], jd 
+;
+;       In either case, one should obtain a Reduced Julian date of 
+;       JD = 54094.7673611
+;
+;  PROCEDURE USED:
+;       GETOPT()
+;  REVISION HISTORY
+;       Adapted from IUE RDAF (S. Parsons)                      8-31-87
+;       Algorithm from Sky and Telescope April 1981   
+;       Added /PROMPT keyword, W. Landsman    September 1992
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Make negative years correspond to B.C. (no year 0), work for year 1582
+;       Disallow 2 digit years.    W. Landsman    March 2000
+;-
+ On_error,2 
+
+ if ( N_params() EQ 0 ) and ( ~keyword_set( PROMPT ) ) then begin
+     print,'Syntax - JULDATE, date, jd          or JULDATE, /PROMPT'
+     print, $
+     '  date - 3-6 element vector containing [year,month,day,hour,minute,sec]'
+     print,'  jd - output reduced julian date (double precision)'
+     return
+ endif
+
+ if ( N_elements(date) EQ 0 ) then begin   
+
+    opt = ''                                                          
+    rd: read,' Enter Year,Month,Day,Hour, Minute, Seconds (All Numeric): ',opt
+    date = getopt( opt, 'F' )
+
+ endif
+
+ case N_elements(date) of      
+
+    6: 
+    5: date = [ date, 0.0d]
+    4: date = [ date, 0.0d,0.0d]    
+    3: date = [ date, 0.0d, 0.0d,0.0d]
+    else: message,'Illegal DATE Vector - must have a least 3 elements'
+
+  endcase   
+
+ iy = floor( date[0] ) 
+ if iy lt 0 then iy++  else $
+    if iy EQ 0 then message,'ERROR - There is no year 0'                   
+ im = fix( date[1] )
+ date = double(date)
+ day = date[2] + ( date[3] + date[4]/60.0d + date[5]/3600.0d) / 24.0d
+;
+ if ( im LT 3 ) then begin   ;If month is Jan or Feb, don't include leap day
+
+     iy-- & im = im+12 
+
+ end
+
+ a = long(iy/100)
+ ry = float(iy)
+
+ jd = floor(ry*0.25d) + 365.0d*(ry -1860.d) + fix(30.6001d*(im+1.)) + $
+      day  - 105.5d
+
+;Gregorian Calendar starts on Oct. 15, 1582 (= RJD -100830.5)
+ if jd GT -100830.5 then jd = jd + 2 - a + floor(a/4)
+
+ if N_params() LT 2 || keyword_set( PROMPT) then begin      
+    yr = fix( date[0] )
+    print, FORM='(A,I4,A,I3,A,F9.5)',$ 
+       ' Year ',yr,'    Month', fix(date[1] ),'    Day', day 
+    print, FORM='(A,F15.5)',' Reduced Julian Date:',JD                       
+ endif
+ 
+ return                               
+ end                                  ; juldate
diff --git a/Code/script_idl_mv/astrolib/ksone.pro b/Code/script_idl_mv/astrolib/ksone.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c8b2ab06fbc0191d588bb6a2d8dce291666427dd
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ksone.pro
@@ -0,0 +1,125 @@
+ pro ksone, data, func_name, d, prob, PLOT = plot, _EXTRA = extra,Window=window
+;+
+; NAME:
+;       KSONE
+; PURPOSE:
+;       Compute the one-sided Kolmogorov-Smirnov statistic
+; EXPLANATION:
+;       Returns the Kolmogorov-Smirnov statistic and associated probability for 
+;       for an array of data values and a user-supplied cumulative distribution
+;       function (CDF) of a single variable.   Algorithm from the procedure of
+;       the same name in "Numerical Recipes" by Press et al. 2nd edition (1992)
+;
+; CALLING SEQUENCE:
+;       ksone, data, func_name, D, prob, [ /PLOT ]
+;
+; INPUT PARAMETERS:
+;       data -  vector of data values, must contain at least 4 elements for the
+;               K-S statistic to be meaningful 
+;       func_name - scalar string giving the name of the cumulative distribution
+;               function.    The function must be defined to accept the data
+;               vector as its only input (see example), though keywords may be
+;               passed via the _EXTRA facility.
+;
+; OUTPUT PARAMETERS:
+;       D - floating scalar giving the Kolmogorov-Smirnov statistic.   It 
+;               specified the maximum deviation between the cumulative 
+;               distribution of the data and the supplied function 
+;       prob - floating scalar between 0 and 1 giving the significance level of
+;               the K-S statistic.   Small values of PROB show that the 
+;               cumulative distribution function of DATA is significantly 
+;               different from FUNC_NAME.
+;
+; OPTIONAL INPUT KEYWORD:
+;       /PLOT - If this keyword is set and non-zero, then KSONE will display a
+;               plot of the CDF of the data with the supplied function 
+;               superposed.   The data value where the K-S statistic is 
+;               computed (i.e. at the maximum difference between the data CDF 
+;               and the function) is indicated by a vertical line.
+;               KSONE accepts the _EXTRA keyword, so that most plot keywords
+;               (e.g. TITLE, XTITLE, XSTYLE) can also be passed to KSONE.
+;       /WINDOW - If set, the plot to a resizeable graphics window
+; EXAMPLE:
+;       Determine if a vector created by the RANDOMN function is really 
+;       consistent with a Gaussian distribution with unit variance.
+;       The CDF of a Gaussian is the error function except that a factor
+;       of 2 is included in the error function.   So we must create a special
+;       function:
+;
+;       function gauss_cdf, x
+;       return, errorf( x/sqrt(2) )
+;       end
+;
+;       IDL> data = randomn(seed, 50)          ;create data array to be tested
+;       IDL> ksone, abs(data), 'gauss_cdf', D, prob, /PLOT     ;Use K-S test
+;      
+;       A small value of PROB indicates that the cumulative distribution of 
+;        DATA is significantly different from a Gaussian
+;
+; NOTES:
+;       The code for PROB_KS is from the 2nd (1992) edition of Numerical 
+;       Recipes which includes a more accurate computation of the K-S 
+;       significance for small values of N than the first edition.
+;
+;       Since _EXTRA is used to pass extra parameters both to the user-supplied
+;       function, and to the cgPLOT command, the user-supplied function should
+;       not accept "cgPLOT" keyword names (e.g. XTITLE).
+;
+; PROCEDURE CALLS
+;       procedure PROB_KS - computes significance of K-S distribution
+;       TAG_EXIST() 
+;
+; REVISION HISTORY:
+;       Written     W. Landsman                   August, 1992
+;       Accept _EXTRA keywords   W. Landsman      September, 1995          
+;       Fixed possible bug in plot display showing position maximum difference
+;       in histogram   M. Fardal/ W. Landsman      March, 1997
+;       Documentation updates   W. Landsman   June 2003
+;       Pass _EXTRA to func_name  M. Fitzgerald    April, 2005
+;       Work for functions that do not accept keywords W. Landsman July 2009
+;       Use Coyote graphics for plotting           Feb 2011
+;-
+ On_error, 2
+ compile_opt idl2
+
+ if ( N_params() LT 3 ) then begin
+    print,'Syntax - ksone, data, func_name, D, [prob ,/PLOT]'
+    return
+ endif
+
+ N = N_elements( data )
+ if N LT 3 then message, $
+   'ERROR - Input data values (first param) must contain at least 3 values'
+
+ sortdata = data[ sort( data ) ]                                   
+
+ f0 = findgen(N)/ N
+ fn = ( findgen( N ) +1. ) / N
+ 
+ ; We need to determine if the user-supplied function accepts keyword 
+ ; arguments.    If it does not then passing the _EXTRA keyword will signal
+ ; an error.
+ resolve_routine, func_name,/is_function
+ r = routine_info(func_name,/parameter,/function)
+ if tag_exist(r,'KW_ARGS') then $
+      ff = call_function( func_name, sortdata, _EXTRA = extra) else $
+      ff = call_function( func_name, sortdata)
+
+ D = max( [ max( abs(f0-ff), sub0 ), max( abs(fn-ff), subn ) ], msub )
+
+ if keyword_set(plot) || keyword_set(WINDOW) then begin
+
+     if msub EQ 0 then begin 
+        cgplot, sortdata,f0,psym=10,_EXTRA = extra, window=window
+        cgplots, [sortdata[sub0], sortdata[sub0]], [0,1],window=window
+     endif else begin
+        cgplot, sortdata,fn,psym=10,_EXTRA = extra,window=window
+        cgplots, [sortdata[subn], sortdata[subn]], [0,1],window=window
+    endelse 
+    cgplot,/over, sortdata,ff,lines=1,window=window
+endif
+
+ PROB_KS, D, N, prob           ;Compute significance of K-S statistic
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/kstwo.pro b/Code/script_idl_mv/astrolib/kstwo.pro
new file mode 100644
index 0000000000000000000000000000000000000000..28619ce9b659cb6a8899428b33088573b48535d9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/kstwo.pro
@@ -0,0 +1,100 @@
+ pro kstwo, data1, data2, D, prob
+;+
+; NAME:
+;       KSTWO
+; PURPOSE:
+;       Return the two-sided Kolmogorov-Smirnov statistic
+; EXPLANATION:
+;       Returns the Kolmogorov-Smirnov statistic and associated probability 
+;       that two arrays of data values are drawn from the same distribution
+;       Algorithm taken from procedure of the same name in "Numerical
+;       Recipes" by Press et al., 2nd edition (1992), Chapter 14
+;
+; CALLING SEQUENCE:
+;       kstwo, data1, data2, D, prob
+;
+; INPUT PARAMETERS:
+;       data1 -  vector of data values, at least 4 data values must be included
+;               for the K-S statistic to be meaningful
+;       data2 -  second set of data values, does not need to have the same 
+;               number of elements as data1
+;
+; OUTPUT PARAMETERS:
+;       D - floating scalar giving the Kolmogorov-Smirnov statistic.   It
+;               specifies the maximum deviation between the cumulative 
+;               distribution of the data and the supplied function 
+;       prob - floating scalar between 0 and 1 giving the significance level of
+;               the K-S statistic.   Small values of PROB show that the 
+;               cumulative distribution function of DATA1 is significantly 
+;               different from DATA2
+;
+; EXAMPLE:
+;       Test whether two vectors created by the RANDOMN function likely came
+;       from the same distribution
+;
+;       IDL> data1 = randomn(seed,40)        ;Create data vectors to be 
+;       IDL> data2 = randomn(seed,70)        ;compared
+;       IDL> kstwo, data1, data2, D, prob   & print,D,prob
+;
+; PROCEDURE CALLS
+;       procedure PROB_KS - computes significance of K-S distribution
+;
+; REVISION HISTORY:
+;       Written     W. Landsman                August, 1992
+;       FP computation of N_eff      H. Ebeling/W. Landsman  March 1996
+;       Fix for arrays containing equal values J. Ballet/W. Landsman Oct. 2001
+;       Fix index when maximum difference is at array end Renbin Yan  Dec 2008
+;       Handle large number when computing N_err  D. Schnitzeler/WL  Sep 2010
+;-
+  On_error, 2
+  compile_opt idl2 
+  
+ if ( N_params() LT 4 ) then begin
+    print,'Syntax - KSTWO, data1, data2, d, prob'
+    return
+ endif
+
+ n1 = N_elements( data1 )
+ if ( N1 LE 3 ) then message, $
+   'ERROR - Input data values (first param) must contain at least 4 values'
+
+ n2 = N_elements( data2 )
+ if ( n2 LE 3 ) then message, $
+   'ERROR - Input data values (second param) must contain at least 4 values'
+
+ sortdata1 = data1[ sort( data1 ) ]        ;Sort input arrays into 
+ sortdata2 = data2[ sort( data2 ) ]        ;ascending order
+
+ fn1 = ( findgen( n1 +1 )  ) / n1          ;updated Dec 2008
+ fn2 = ( findgen( n2 +1)  ) / n2
+
+ j1 = 0l & j2 = 0l
+ id1 = lonarr(n1+n2)  & id2 = id1
+ i = 0l
+
+; Form the two cumulative distribution functions, marking points where one
+; must test their difference
+
+ while ( j1 LT N1 ) and ( j2 LT n2 ) do begin
+
+     d1 = sortdata1[j1]
+     d2 = sortdata2[j2]
+     if d1 LE d2 then j1 = j1 +1
+     if d2 LE d1 then j2 = j2 +1
+            
+     id1[i] = j1   & id2[i] = j2
+     i = i+1
+
+ endwhile
+
+ id1 = id1[0:i-1]   &  id2 = id2[0:i-1]
+
+; The K-S statistic D is the maximum difference between the two distribution
+; functions
+
+ D = max( abs( fn1[id1] - fn2[id2] ) ) 
+ N_eff =  long64(n1)*n2/ float(n1 + n2)           ;Effective # of data points
+ PROB_KS, D, N_eff, prob                ;Compute significance of statistic
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/kuiperone.pro b/Code/script_idl_mv/astrolib/kuiperone.pro
new file mode 100644
index 0000000000000000000000000000000000000000..665960b5766b9383fd8baf098e1197aaa3058725
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/kuiperone.pro
@@ -0,0 +1,126 @@
+ pro kuiperone, data, func_name, d, prob, PLOT = plot, WINDOW=window, $
+     _EXTRA = extra
+;+
+; NAME:
+;       KUIPERONE
+; PURPOSE:
+;       Compute the one-sided Kuiper statistic (invariant Kolmogorov-Smirnov)
+; EXPLANATION:
+;       Returns the Kuiper statistic and associated probability
+;       for an array of data values and a user-supplied cumulative distribution
+;       function (CDF) of a single variable.   Algorithm adapted from KSONE
+;       in "Numerical Recipes" by Press et al. 2nd edition (1992)
+;
+;       Kuiper's test is especially useful for data defined on a circle or 
+;       to search for periodicity (see Paltani 2004, A&A, 420, 789). 
+; CALLING SEQUENCE:
+;       kuiperone, data, func_name, D, prob, [ /PLOT ]
+;
+; INPUT PARAMETERS:
+;       data -  vector of data values, must contain at least 4 elements for the
+;               Kuiper statistic to be meaningful
+;       func_name - scalar string giving the name of the cumulative distribution
+;               function.    The function must be defined to accept the data
+;               vector as its only input (see example).
+;
+; OUTPUT PARAMETERS:
+;       D - floating scalar giving the Kuiper statistic.   It
+;               specifies the sum of positive and negative deviations between the
+;               cumulative distribution of the data and the supplied function
+;       prob - floating scalar between 0 and 1 giving the significance level of
+;               the Kuiper statistic.   Small values of PROB show that the
+;               cumulative distribution function of DATA is significantly
+;               different from FUNC_NAME.
+;
+; OPTIONAL INPUT KEYWORD:
+;       /PLOT - If this keyword is set and non-zero, then KUIPERONE will display a
+;               plot of the CDF of the data with the supplied function
+;               superposed.   The data values where the Kuiper statistic is
+;               computed (i.e. at the maximum difference between the data CDF
+;               and the function) are indicated by vertical dashed lines.
+;               KUIPERONE accepts the _EXTRA keyword, so that most plot keywords
+;               (e.g. TITLE, XTITLE, XSTYLE) can also be passed to KUIPERONE.
+;
+; EXAMPLE:
+;       Determine if a vector created by the RANDOMN function is really
+;       consistent with a Gaussian distribution.
+;       The CDF of a Gaussian is the error function except that a factor
+;       of 2 is included in the error function.   So we must create a special
+;       function:
+;
+;       function gauss_cdf, x
+;       return, errorf( x/sqrt(2) )
+;       end
+;
+;       IDL> data = randomn(seed, 50)          ;create data array to be tested
+;       IDL> kuiperone, data, 'gauss_pdf', D, prob, /PLOT     ;Use Kuiper test
+;
+;       A small value of PROB indicates that the cumulative distribution of
+;       DATA is significantly different from a Gaussian
+;
+; NOTES:
+;       Note that the 2nd (1992) edition of Numerical Recipes includes
+;       a more accurate computation of the K-S significance for small
+;       values of N.
+;
+; PROCEDURE CALLS
+;       procedure PROB_KUIPER - computes significance of Kuiper distribution
+;
+; REVISION HISTORY:
+;       Written     W. Landsman                   August, 1992
+;       Accept _EXTRA keywords   W. Landsman      September, 1995
+;       Fixed possible bug in plot display showing position maximum difference
+;       in histogram   M. Fardal/ W. Landsman      March, 1997
+;       Adapted from KSONE      J. Ballet     July 2003
+;       Use Coyote graphics   W. Landsman     Feb 2011
+;-
+ On_error, 2
+ compile_opt idl2
+
+ if ( N_params() LT 3 ) then begin
+    print,'Syntax - kuiperone, data, func_name, D, [prob ,/PLOT]'
+    return
+ endif
+
+ N = N_elements( data )
+ if N LT 3 then message, $
+   'ERROR - Input data values (first param) must contain at least 3 values'
+
+ sortdata = data[ sort( data ) ]
+
+ f0 = findgen(N)/ N
+ fn = ( findgen( N ) +1. ) / N
+ ff = call_function( func_name, sortdata )
+
+; Maximum distance above the reference
+ D1 = max( fn-ff, subn )
+
+; Maximum distance below the reference
+ D2 = max( ff-f0, sub0 )
+
+ D = D1 + D2
+
+ if keyword_set(plot) || keyword_set(WINDOW) then begin
+
+; Prepare the step function
+     xx = REBIN(sortdata,2*N,/SAMPLE)
+     yy = REBIN(f0,2*N,/SAMPLE)
+     yy = [yy[1:*],1.]
+
+     cgplot, xx,yy,_EXTRA = extra, WINDOW=window
+     cgplots, [sortdata[sub0], sortdata[sub0]], [0,ff[sub0]], linestyle=2, $
+         WINDOW=window
+     cgplots, [sortdata[subn], sortdata[subn]], [ff[subn],1], linestyle=2, $
+        WINDOW=window
+
+; Plot the expected cumulative distribution
+     n2 = n > 100
+     x2 = FINDGEN(n2+1)*(!X.CRANGE[1]-!X.CRANGE[0])/n2 + !X.CRANGE[0]
+     y2 = call_function( func_name, x2 )
+     cgplot,/over, x2,y2,lines=1,thick=2, WINDOW=window
+ endif
+
+ prob_kuiper, D, N, prob           ;Compute significance of Kuiper statistic
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/kuipertwo.pro b/Code/script_idl_mv/astrolib/kuipertwo.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8f9827ca66b25435a5850468507b65b4333c30fa
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/kuipertwo.pro
@@ -0,0 +1,132 @@
+ pro kuipertwo, data1, data2, D, prob, PLOT = plot, _EXTRA = extra,WINDOW=window
+;+
+; NAME:
+;       KUIPERTWO
+; PURPOSE:
+;       Compute the two-sided Kuiper statistic (invariant Kolmogorov-Smirnov)
+; EXPLANATION:
+;       Returns the Kuiper statistic and associated probability 
+;       that two arrays of data values are drawn from the same distribution
+;       Algorithm adapted from KSTWO in "Numerical
+;       Recipes" by Press et al., 2nd edition (1992), Chapter 14
+;
+; CALLING SEQUENCE:
+;       kuipertwo, data1, data2, D, prob, [ /PLOT ]
+;
+; INPUT PARAMETERS:
+;       data1 -  vector of data values, at least 4 data values must be included
+;               for the Kuiper statistic to be meaningful
+;       data2 -  second set of data values, does not need to have the same 
+;               number of elements as data1
+;
+; OUTPUT PARAMETERS:
+;       D - floating scalar giving the Kuiper statistic.   It
+;               specifies the sum of positive and negative deviations between
+;               the cumulative distributions of the two data sets
+;       prob - floating scalar between 0 and 1 giving the significance level of
+;               the Kuiper statistic.   Small values of PROB show that the 
+;               cumulative distribution function of DATA1 is significantly 
+;               different from DATA2
+;
+; OPTIONAL INPUT KEYWORD:
+;       /PLOT - If this keyword is set and non-zero, then KUIPERTWO will display
+;               a plot of the CDF of the two data sets.
+;               The data values where the Kuiper statistic is
+;               computed (i.e. at the maximum difference between the CDF of
+;               the two data sets) are indicated by vertical dashed lines.
+;               KUIPERTWO accepts the _EXTRA keyword, so that most plot keywords
+;               (e.g. TITLE, XTITLE, XSTYLE) can also be passed to KUIPERTWO.
+;       /WINDOW - If set the plot to a resizeable graphics window.
+; EXAMPLE:
+;       Test whether two vectors created by the RANDOMN function likely came
+;       from the same distribution
+;
+;       IDL> data1 = randomn(seed,40)        ;Create data vectors to be 
+;       IDL> data2 = randomn(seed,70)        ;compared
+;       IDL> kuipertwo, data1, data2, D, prob   & print,D,prob
+;
+; PROCEDURE CALLS
+;       procedure PROB_KUIPER - computes significance of Kuiper distribution
+;
+; REVISION HISTORY:
+;       Written     W. Landsman                August, 1992
+;       FP computation of N_eff      H. Ebeling/W. Landsman  March 1996
+;       Fix for arrays containing equal values J. Ballet/W. Landsman
+;       Oct. 2001
+;       Adapted from KSTWO, added PLOT keyword  J. Ballet     July 2004
+;       Use Coyote Graphics W. Landsman   Feb 2011
+;-
+  On_error, 2
+  compile_opt idl2
+
+ if ( N_params() LT 4 ) then begin
+    print,'Syntax - KUIPERTWO, data1, data2, d, prob [, /PLOT]'
+    return
+ endif
+
+ n1 = N_elements( data1 )
+ if ( N1 LE 3 ) then message, $
+   'ERROR - Input data values (first param) must contain at least 4 values'
+
+ n2 = N_elements( data2 )
+ if ( n2 LE 3 ) then message, $
+   'ERROR - Input data values (second param) must contain at least 4 values'
+
+ sortdata1 = data1[ sort( data1 ) ]        ;Sort input arrays into 
+ sortdata2 = data2[ sort( data2 ) ]        ;ascending order
+
+ fn1 = ( findgen( n1 )  ) / n1
+ fn2 = ( findgen( n2 )  ) / n2
+
+ j1 = 0l & j2 = 0l
+ id1 = lonarr(n1+n2)  & id2 = id1
+ i = 0l
+
+; Form the two cumulative distribution functions, marking points where one
+; must test their difference
+
+ while ( j1 LT n1 ) and ( j2 LT n2 ) do begin
+
+     d1 = sortdata1[j1]
+     d2 = sortdata2[j2]
+     if d1 LE d2 then j1 = j1 +1
+     if d2 LE d1 then j2 = j2 +1
+            
+     id1[i] = j1   & id2[i] = j2
+     i = i+1
+
+ endwhile
+
+ id1 = id1[0:i-1]   &  id2 = id2[0:i-1]
+
+; The Kuiper statistic D is the sum of the maximum positive and
+; negative differences between the two distribution functions
+
+ D1 = max(fn1[id1] - fn2[id2], sub1, MIN=D2, SUBSCRIPT_MIN=sub2)
+ D = D1 - D2
+ N_eff =  n1*n2/ float(n1 + n2)              ;Effective # of data points
+ PROB_KUIPER, D, N_eff, prob                 ;Compute significance of statistic
+
+ if keyword_set(plot) || keyword_set(Window) then begin
+
+; Prepare the step functions
+     xx1 = REBIN(sortdata1,2*n1,/SAMPLE)
+     yy1 = REBIN(fn1,2*n1,/SAMPLE)
+     yy1 = [yy1[1:*],1.]
+
+     xx2 = REBIN(sortdata2,2*n2,/SAMPLE)
+     yy2 = REBIN(fn2,2*n2,/SAMPLE)
+     yy2 = [yy2[1:*],1.]
+
+     cgplot, xx1, yy1, _EXTRA = extra, WINDOW=window
+     cgplot, /over, xx2, yy2, lines=1, thick=2, WINDOW=window
+     j1 = id1[sub1] - 1
+     j2 = id1[sub2]
+     cgplots, [sortdata1[j2], sortdata1[j2]], [0,fn2[id2[sub2]]], linestyle=2,$
+         WINDOW=window
+     cgplots, [sortdata1[j1], sortdata1[j1]], [fn2[id2[sub1]],1], linestyle=2,$
+        WINDOW=window
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/lineid_plot.pro b/Code/script_idl_mv/astrolib/lineid_plot.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8665e28064504ab39a0dff8e4b3a491602160b8b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/lineid_plot.pro
@@ -0,0 +1,261 @@
+pro lineid_plot,wave,flux,wline,text1,text2, extend=extend, $
+	lcharthick = lcharthick,lcharsize=lcharsize,window=window, $
+	 _EXTRA = extra
+;+
+; NAME:
+;	LINEID_PLOT
+; PURPOSE:
+;	 Plot spectrum with specified line identifications annotated at the
+;	 top of the plot.
+;
+; CALLING SEQUENCE:
+;	lineid_plot, wave, flux, wline, text1, [ text2, 
+;			LCHARSIZE=, LCHARTHICK=, EXTEND =, ...plotting keywords]
+;
+; INPUTS:
+;	wave - wavelength vector for the plot
+;	flux - flux vector
+;	wline - wavelength vector of line identifications.  (only the lines 
+;		between	the plot limits will be used)
+;	text1 - string array of text to be used to annotate each line
+;	text2 - (OPTIONAL) second string array of text to be used for
+;		line annotation.  Since the text is written with
+;		proportional spaced characters, TEXT2 can be used if
+;		you want two sets of annotation to be aligned:
+;
+;		eg:	Cr IV  1390.009
+;			Fe V   1390.049
+;			Ni IV  1390.184
+;			    instead of
+;			Cr IV 1390.009
+;			Fe V 1390.049
+;			Ni IV 1390.184
+;
+; OPTIONAL KEYWORD INPUTS:
+;	EXTEND - specifies that the annotated lines should have a dotted line 
+;		extended to the spectrum to indicate the line position.  
+;		EXTEND can be a scalar (applies to all lines) or a vector with
+;		a different value for each line.  The value of EXTEND gives 
+;		the line IDL plot line thickness for the dotted lines.
+;		If EXTEND is a vector each dotted line can have a different 
+;		thickness.  A value of 0 indicates that no dotted line is to 
+;		be drawn. (default = scalar 0)
+;	LCHARSIZE - the character size of the annotation for each line.
+;		If can be a vector so that different lines are annotated with 
+;		different size characters.  LCHARSIZE can be used to make 
+;		stronger lines have a larger annotation. (default = scalar 1.0).
+;	LCHARTHICK = the character thickness of the annotation for each line. 
+;		It can be a vector so that different lines are annotated with 
+;		characters of varying thickness.   LCHARTHICK can be used to 
+;		make stronger lines have a bolder annotation. 
+;		(default = !p.charthick)
+;
+;	LINEID_PLOT uses the _EXTRA facility to allow the use of any cgPLOT
+;	keywords (e.g. AXISCOLOR, LINESTYLE, CHARSIZE) to be passed to the 
+;       plot.
+;
+; SIDE EFFECTS:
+;	Program uses SET_VIEWPORT to set the !P.POSITION parameter to allow
+;	room for the annotation.   This system variable can be reset to the 
+;	default value by setting !P.POSTION=0 or typing SET_VIEWPORT with no 
+;	parameters
+;
+; OPERATIONAL NOTES:
+;	Once the program has completed, You can use OPLOT to draw additional
+;	plots on the display. 
+;
+;	If your annotated characters are not being rotated properly,
+;	try setting !P.FONT to a non zero value.
+; EXAMPLE:
+;	Annotate some interstellar lines between 1240 and 1270 A.
+;
+;	IDL> w = 1240+ indgen(300)*0.1    ;Make a wavelength vector
+;	IDL> f = randomn(seed,300)        ;Random flux vector
+;	IDL> id = ['N V','Si II','Si II','Si II']   ;Line IDs
+;	IDL> wl = [1242.80,1260.42,1264.74,1265.00] ;Line positions
+;	IDL> lineid_plot,w,f,wl,id,wl,/ext
+;
+;	Note that LINEID_PLOT is smart enough not to overlap the annotation
+;	for the two closely spaced lines at 1264.74 and 1265.00	
+; HISTORY:
+;	version 1  D. Lindler Jan, 1992
+;	Sept 27, 1993  DJL  fixed bug in /extend option
+;	Apr 19, 1994 DJL corrected bug in sorting of charthick (cthick)
+;	Sep 1996, W. Landsman,  added _EXTRA keyword, changed keyword names
+;		CHARTHICK==>LCHARTHICK, CHARSIZE==>LCHARSIZE
+;       Work with !P.MULTI   W. Landsman   December 2003
+;       Use Coyote graphics routines  W. Landsman February 2011
+;-
+;----------------------------------------------------------------------------
+	On_error,2
+
+	if n_params() lt 4 then begin
+	   print,'Syntax - LINEID_PLOT, wave, flux, wline, text1 [,text2, '
+	   print,'       LCHARTHICK=, EXTEND=, LCHARSIZE= ...plotting keywords]'
+	   return
+	end
+;
+; initialization
+;
+
+	setdefaultvalue, lcharsize, 1
+	n = n_elements(wline)
+	setdefaultvalue,text2,strarr(n)
+	if n_elements(lcharsize) eq 1 then csize = replicate(lcharsize,n) $
+				     else csize = lcharsize
+	setdefaultvalue, extend, 0
+	if n_elements(extend) eq 1 then ethick = replicate(extend,n) $
+				   else ethick = extend
+	if n_elements(lcharthick) eq 0 then cthick = !p.charthick $
+				      else cthick = lcharthick
+	if n_elements(cthick) eq 1 then cthick = replicate(cthick,n)
+;
+; First make a plot without any data to get the region size.    Then use
+; the position keyword to assign a plot area that allows room for the 
+; line annotation and plot the data
+;       
+        plot,wave,flux,xsty=4,ysty=4,/nodata,/noerase
+        x0 = !X.region[0]
+        y0 = !Y.region[0]
+        xsize = !X.region[1] - x0
+        ysize = !Y.region[1] - y0
+        pos = [x0+xsize*0.13,y0+ysize*0.1, x0+xsize*0.95, y0+ysize*0.65]
+	cgplot,wave,flux,_EXTRA=extra,pos = pos, Window=window       
+        if keyword_set(window) then cgcontrol,execute=0
+;
+; get data ranges
+;
+	xmin = !x.crange[0]
+	xmax = !x.crange[1]
+	ymin = !y.crange[0]
+	ymax = !y.crange[1]
+	xrange = xmax-xmin
+	yrange = ymax-ymin
+;
+; find lines within x range and sort them
+;
+	good = where((wline gt xmin) and (wline lt xmax),nlines)
+	if nlines lt 1 then return
+	wl = wline[good]
+	csize = csize[good] & cthick = cthick[good] & ethick = ethick[good]
+	txt1 = text1[good] & txt2 = text2[good] 
+
+	sub = sort(wl)
+	wl = wl[sub] & csize = csize[sub] & ethick = ethick[sub]
+	cthick = cthick[sub] 
+	txt1 = txt1[sub] & txt2 = txt2[sub] 
+	maxids = 65/(total(csize)/nlines)   ;maximum number of identifications
+	if nlines gt maxids then begin
+		print,'Too many lines to mark'
+		return
+	endif
+
+;
+; determine character height in wavelength units
+;
+	char_height = abs(xrange) / 65 * csize
+;
+; adjust wavelengths of where to print the line ids
+;
+	wlp = wl		;wavelength to print text
+;
+; test to see if we can just equally space the annotated lines
+;
+	if (nlines gt maxids*0.85) and (n_elements(charsize) eq 1) then begin
+		wlp = findgen(nlines) * (xrange/(nlines-1)) + xmin
+		goto,print_text
+	end
+;
+; iterate to find room to annotate each line
+;
+	changed = 1		;flag saying we moved a wlp position
+	niter = 0
+	factor = 0.35		;size of adjustments in text position
+	while changed do begin	;iterate
+	    changed = 0
+	    for i=0,nlines-1 do begin
+;
+; determine the difference of the annotation from the lines on the
+; left and right of it and the required separation
+;
+		if i gt 0 then begin
+			diff1 = wlp[i]-wlp[i-1]
+			separation1 = (char_height[i]+char_height[i-1])/2.0
+		    end else begin
+			diff1 = wlp[i] - xmin + char_height[i]*1.01
+			separation1 = char_height[i]
+		end
+
+		if i lt (nlines-1) then begin
+			diff2 = wlp[i+1] - wlp[i]
+			separation2 = (char_height[i]+char_height[i+1])/2.0
+		    end else begin
+			diff2 = xmax + char_height[i]*1.01 - wlp[i]
+			separation2 = char_height[i]
+		end
+;
+; determine if line annotation should be moved
+;
+		if (diff1 lt separation1) or (diff2 lt separation2) then begin
+		    if wlp[i] eq xmin then diff1 = 0
+		    if wlp[i] eq xmax then diff2 = 0
+		    if diff2 gt diff1 then $
+				wlp[i] = (wlp[i] + separation2*factor) < xmax $
+			   else wlp[i] = (wlp[i] - separation1*factor) > xmin
+		    changed = 1
+		endif
+
+	    end
+
+	    if niter eq 300 then $		; fine adjustment for 
+			factor = factor/3	; crowded field
+			
+
+	    if niter eq 1000 then changed=0	; stop at 1000 iterations
+	    niter = niter + 1
+
+	endwhile
+
+;
+; print line id's
+;
+print_text:
+	maxcsize = max(csize)
+	start_arrow = ymax + yrange/60
+	bend1 = ymax + yrange/30
+	bend2 = ymax + (yrange/30)*3
+	stop_arrow = ymax + (yrange/30)*4
+	start_text1 = stop_arrow + yrange/50*maxcsize
+	start_text2 = start_text1 +  $
+			max(strlen(strtrim(txt1,1)))*yrange/50*maxcsize
+	start_text3 = start_text2 +  $
+			max(strlen(strtrim(txt2,1)))*yrange/50*maxcsize
+
+	for i=0,nlines-1 do begin
+		cgplots,[wl[i],wl[i],wlp[i],wlp[i]], ADDCMD=window, $
+		      [start_arrow,bend1,bend2,stop_arrow]
+		cgtext,wlp[i] + char_height[i]/2, start_text1, txt1[i], $
+		    orientation = 90, size=csize[i], charthick = cthick[i],$
+		    window = window
+		cgtext,wlp[i] + char_height[i]/2, start_text2, txt2[i], $
+		  orientation = 90, size=csize[i], charthick = cthick[i],$
+		  window= window
+	endfor
+;
+; extend selected lines down to the spectrum
+;
+	good = where((ethick gt 0) and (wl gt xmin) and (wl lt xmax),n)
+	if n lt 1 then return
+	ww = wl[good]
+	ethick = ethick[good]
+	linterp,wave,flux,ww,ff
+	ymax = !y.crange[1]
+	ymin = !y.crange[0]
+	offset = (ymax-ymin)/20.0
+	for i=0,n-1 do $
+	   cgplots,[ww[i],ww[i]],[(ff[i]+offset)<ymax>ymin,ymax], $
+				line=2,thick = ethick[i],ADDCMD=window
+	if keyword_set(window) then cgcontrol,execute=1			
+
+return
+end
diff --git a/Code/script_idl_mv/astrolib/linmix_err.pro b/Code/script_idl_mv/astrolib/linmix_err.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d1b24f1d0df1c4a62ffdb4cdf38b11f9ffa5128e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/linmix_err.pro
@@ -0,0 +1,1308 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;+
+;   NAME:
+;     LINMIX_ERR
+;   PURPOSE:
+;      Bayesian approach to linear regression with errors in both X and Y
+;   EXPLANATION:
+;     Perform linear regression of y on x when there are measurement
+;     errors in both variables. the regression assumes :
+;
+;                 ETA = ALPHA + BETA * XI + EPSILON
+;                 X = XI + XERR
+;                 Y = ETA + YERR
+;
+;
+; Here, (ALPHA, BETA) are the regression coefficients, EPSILON is the
+; intrinsic random scatter about the regression, XERR is the
+; measurement error in X, and YERR is the measurement error in
+; Y. EPSILON is assumed to be normally-distributed with mean zero and
+; variance SIGSQR. XERR and YERR are assumed to be
+; normally-distributed with means equal to zero, variances XSIG^2 and
+; YSIG^2, respectively, and covariance XYCOV. The distribution of XI
+; is modelled as a mixture of normals, with group proportions PI,
+; mean MU, and variance TAUSQR. Bayesian inference is employed, and
+; a structure containing random draws from the posterior is
+; returned. Convergence of the MCMC to the posterior is monitored
+; using the potential scale reduction factor (RHAT, Gelman et
+; al.2004). In general, when RHAT < 1.1 then approximate convergence
+; is reached.
+;
+; Simple non-detections on y may also be included.
+;
+; CALLING SEQUENCE:
+;
+;     LINMIX_ERR, X, Y, POST, XSIG=, YSIG=, XYCOV=, DELTA=, NGAUSS=, /SILENT, 
+;                /METRO, MINITER= , MAXITER= 
+;
+;
+; INPUTS :
+;
+;   X - THE OBSERVED INDEPENDENT VARIABLE. THIS SHOULD BE AN
+;       NX-ELEMENT VECTOR.
+;   Y - THE OBSERVED DEPENDENT VARIABLE. THIS SHOULD BE AN NX-ELEMENT
+;       VECTOR.
+;
+; OPTIONAL INPUTS :
+;
+;   XSIG - THE 1-SIGMA MEASUREMENT ERRORS IN X, AN NX-ELEMENT VECTOR.
+;   YSIG - THE 1-SIGMA MEASUREMENT ERRORS IN Y, AN NX-ELEMENT VECTOR.
+;   XYCOV - THE COVARIANCE BETWEEN THE MEASUREMENT ERRORS IN X AND Y,
+;           AND NX-ELEMENT VECTOR.
+;   DELTA - AN NX-ELEMENT VECTOR INDICATING WHETHER A DATA POINT IS
+;           CENSORED OR NOT. IF DELTA[i] = 1, THEN THE SOURCE IS
+;           DETECTED, ELSE IF DELTA[i] = 0 THE SOURCE IS NOT DETECTED
+;           AND Y[i] SHOULD BE AN UPPER LIMIT ON Y[i]. NOTE THAT IF
+;           THERE ARE CENSORED DATA POINTS, THEN THE
+;           MAXIMUM-LIKELIHOOD ESTIMATE (THETA) IS NOT VALID. THE
+;           DEFAULT IS TO ASSUME ALL DATA POINTS ARE DETECTED, IE,
+;           DELTA = REPLICATE(1, NX).
+;   METRO - IF METRO = 1, THEN THE MARKOV CHAINS WILL BE CREATED USING
+;           THE METROPOLIS-HASTINGS ALGORITHM INSTEAD OF THE GIBBS
+;           SAMPLER. THIS CAN HELP THE CHAINS CONVERGE WHEN THE SAMPLE
+;           SIZE IS SMALL OR IF THE MEASUREMENT ERRORS DOMINATE THE
+;           SCATTER IN X AND Y.
+;   SILENT - SUPPRESS TEXT OUTPUT.
+;   MINITER - MINIMUM NUMBER OF ITERATIONS PERFORMED BY THE GIBBS
+;             SAMPLER OR METROPOLIS-HASTINGS ALGORITHM. IN GENERAL,
+;             MINITER = 5000 SHOULD BE SUFFICIENT FOR CONVERGENCE. THE
+;             DEFAULT IS MINITER = 5000. THE MCMC IS STOPPED AFTER 
+;             RHAT < 1.1 FOR ALL PARAMETERS OF INTEREST, AND THE
+;             NUMBER OF ITERATIONS PERFORMED IS GREATER THAN MINITER.
+;   MAXITER - THE MAXIMUM NUMBER OF ITERATIONS PERFORMED BY THE
+;             MCMC. THE DEFAULT IS 1D5. THE MCMC IS STOPPED
+;             AUTOMATICALLY AFTER MAXITER ITERATIONS.
+;   NGAUSS - THE NUMBER OF GAUSSIANS TO USE IN THE MIXTURE
+;            MODELLING. THE DEFAULT IS 3. IF NGAUSS = 1, THEN THE
+;            PRIOR ON (MU, TAUSQR) IS ASSUMED TO BE UNIFORM.
+;
+; OUTPUT :
+;
+;    POST - A STRUCTURE CONTAINING THE RESULTS FROM THE MCMC. EACH
+;           ELEMENT OF POST IS A DRAW FROM THE POSTERIOR DISTRIBUTION
+;           FOR EACH OF THE PARAMETERS.
+;
+;             ALPHA - THE CONSTANT IN THE REGRESSION.
+;             BETA - THE SLOPE OF THE REGRESSION.
+;             SIGSQR - THE VARIANCE OF THE INTRINSIC SCATTER.
+;             PI - THE GAUSSIAN WEIGHTS FOR THE MIXTURE MODEL.
+;             MU - THE GAUSSIAN MEANS FOR THE MIXTURE MODEL.
+;             TAUSQR - THE GAUSSIAN VARIANCES FOR THE MIXTURE MODEL.
+;             MU0 - THE HYPERPARAMETER GIVING THE MEAN VALUE OF THE
+;                   GAUSSIAN PRIOR ON MU. ONLY INCLUDED IF NGAUSS >
+;                   1.
+;             USQR - THE HYPERPARAMETER DESCRIBING FOR THE PRIOR
+;                    VARIANCE OF THE INDIVIDUAL GAUSSIAN CENTROIDS
+;                    ABOUT MU0. ONLY INCLUDED IF NGAUSS > 1.
+;             WSQR - THE HYPERPARAMETER DESCRIBING THE `TYPICAL' SCALE
+;                    FOR THE PRIOR ON (TAUSQR,USQR). ONLY INCLUDED IF
+;                    NGAUSS > 1.
+;             XIMEAN - THE MEAN OF THE DISTRIBUTION FOR THE
+;                      INDEPENDENT VARIABLE, XI.
+;             XISIG - THE STANDARD DEVIATION OF THE DISTRIBUTION FOR
+;                     THE INDEPENDENT VARIABLE, XI.
+;             CORR - THE LINEAR CORRELATION COEFFICIENT BETWEEN THE
+;                    DEPENDENT AND INDEPENDENT VARIABLES, XI AND ETA.
+;
+; CALLED ROUTINES :
+;
+;    RANDOMCHI, MRANDOMN, RANDOMGAM, RANDOMDIR, MULTINOM
+;
+; REFERENCES :
+;
+;   Carroll, R.J., Roeder, K., & Wasserman, L., 1999, Flexible
+;     Parametric Measurement Error Models, Biometrics, 55, 44
+;
+;   Kelly, B.C., 2007, Some Aspects of Measurement Error in
+;     Linear Regression of Astronomical Data, The Astrophysical
+;     Journal, 665, 1489 (arXiv:0705.2774)
+;
+;   Gelman, A., Carlin, J.B., Stern, H.S., & Rubin, D.B., 2004,
+;     Bayesian Data Analysis, Chapman & Hall/CRC
+;
+; REVISION HISTORY
+;
+;     AUTHOR : BRANDON C. KELLY, STEWARD OBS., JULY 2006
+;   - MODIFIED PRIOR ON MU0 TO BE UNIFORM OVER [MIN(X),MAX(X)] AND
+;     PRIOR ON USQR TO BE UNIFORM OVER [0, 1.5 * VARIANCE(X)]. THIS
+;     TENDS TO GIVE BETTER RESULTS WITH FEWER GAUSSIANS. (B.KELLY, MAY
+;     2007)
+;   - FIXED BUG SO THE ITERATION COUNT RESET AFTER THE BURNIN STAGE
+;     WHEN SILENT = 1 (B. KELLY, JUNE 2009)
+;   - FIXED BUG WHEN UPDATING MU VIA THE METROPOLIS-HASTING
+;     UPDATE. PREVIOUS VERSIONS DID NO INDEX MUHAT, SO ONLY MUHAT[0]
+;     WAS USED IN THE PROPOSAL DISTRIBUTION. THANKS TO AMY BENDER FOR
+;     POINTING THIS OUT. (B. KELLY, DEC 2011)
+;-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;routine to compute the hyperbolic arctangent
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function linmix_atanh, x
+
+z = 0.5d * ( alog(1 + x) - alog(1 - x) )
+
+return, z
+end
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;routine to compute a robust estimate for the standard deviation of a
+;data set, based on the inter-quartile range
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function linmix_robsig, x
+
+nx = n_elements(x)
+                                ;get inter-quartile range of x
+sorted = sort(x)
+iqr = x[sorted[3 * nx / 4]] - x[sorted[nx / 4]]
+sdev = stddev(x, /nan)
+sigma = min( [sdev, iqr / 1.34] ) ;use robust estimate for sigma
+if sigma eq 0 then sigma = sdev
+
+return, sigma
+end
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;routine to compute the log-likelihood of the data
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function loglik_mixerr, x, y, xvar, yvar, xycov, delta, theta, pi, mu, tausqr, Glabel
+
+alpha = theta[0]
+beta = theta[1]
+sigsqr = theta[2]
+
+nx = n_elements(x)
+ngauss = n_elements(pi)
+
+Sigma11 = dblarr(nx, ngauss)
+Sigma12 = dblarr(nx, ngauss)
+Sigma22 = dblarr(nx, ngauss)
+determ = dblarr(nx, ngauss)
+
+for k = 0, ngauss - 1 do begin
+
+    Sigma11[0,k] = beta^2 * tausqr[k] + sigsqr + yvar
+    Sigma12[0,k] = beta * tausqr[k] + xycov
+    Sigma22[0,k] = tausqr[k] + xvar
+
+    determ[0, k] = Sigma11[*,k] * Sigma22[*,k] - Sigma12[*,k]^2
+
+endfor
+
+det = where(delta eq 1, ndet, comp=cens, ncomp=ncens) ;any non-detections?
+
+loglik = dblarr(nx)
+
+if ndet gt 0 then begin
+                                ;compute contribution to
+                                ;log-likelihood from the detected
+                                ;sources
+    for k = 0, ngauss - 1 do begin
+        
+        gk = where(Glabel[det] eq k, nk)
+
+        if nk gt 0 then begin
+
+            zsqr = (y[det[gk]] - alpha - beta * mu[k])^2 / Sigma11[det[gk],k] + $
+              (x[det[gk]] - mu[k])^2 / Sigma22[det[gk],k] - $
+              2d * Sigma12[det[gk],k] * (y[det[gk]] - alpha - beta * mu[k]) * $
+              (x[det[gk]] - mu[k]) / (Sigma11[det[gk],k] * Sigma22[det[gk],k])
+            
+            corrz = Sigma12[det[gk],k] / sqrt( Sigma11[det[gk],k] * Sigma22[det[gk],k] )
+            
+            loglik[det[gk]] = -0.5d * alog(determ[det[gk],k]) - 0.5 * zsqr / (1d - corrz^2)
+
+        endif
+
+    endfor
+
+endif
+
+if ncens gt 0 then begin
+                                ;compute contribution to the
+                                ;log-likelihood from the
+                                ;non-detections
+    for k = 0, ngauss - 1 do begin
+
+        gk = where(Glabel[cens] eq k, nk)
+
+        if nk gt 0 then begin
+            
+            loglikx = -0.5 * alog(Sigma22[cens[gk],k]) - $
+              0.5 * (x[cens[gk]] - mu[k])^2 / Sigma22[cens[gk],k]
+            
+                                ;conditional mean of y, given x and
+                                ;G=k
+            cmeany = alpha + beta * mu[k] + Sigma12[cens[gk],k] / Sigma22[cens[gk],k] * $
+              (x[cens[gk]] - mu[k])
+                                ;conditional variance of y, given x
+                                ;and G=k
+            cvary = Sigma11[cens[gk],k] - Sigma12[cens[gk],k]^2 / Sigma22[cens[gk],k]
+
+                                ;make sure logliky is finite
+            logliky = alog(gauss_pdf( (y[cens[gk]] - cmeany) / sqrt(cvary) )) > (-1d300) 
+            
+            loglik[cens[gk]] = loglikx + logliky
+        
+        endif
+
+    endfor
+    
+endif
+
+loglik = total(loglik)
+
+return, loglik
+end
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;routine to compute the log-prior of the data
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function logprior_mixerr, mu, mu0, tausqr, usqr, wsqr
+
+ngauss = n_elements(mu)
+
+if ngauss gt 1 then begin
+
+    logprior_mu = -0.5 * alog(usqr) - 0.5 * (mu - mu0)^2 / usqr
+    logprior_mu = total(logprior_mu)
+    
+    logprior_tausqr = 0.5 * alog(wsqr) - 1.5 * alog(tausqr) - 0.5 * wsqr / tausqr
+    logprior_tausqr = total(logprior_tausqr)
+
+    logprior = logprior_mu + logprior_tausqr
+
+endif else logprior = 0d ;if ngauss = 1 then uniform prior
+
+return, logprior
+end
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;routine to perform the Metropolis update for the scale parameter in
+;the Gibbs sampler
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function linmix_metro_update, logpost_new, logpost_old, seed, log_jrat
+
+lograt = logpost_new - logpost_old
+
+if n_elements(log_jrat) gt 0 then lograt = lograt + log_jrat
+
+accept = 0
+
+if lograt gt 0 then accept = 1 else begin
+    
+    u = randomu(seed)
+
+    if alog(u) le lograt then accept = 1
+    
+endelse
+
+return, accept
+end
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;routine to acceptance rates for metropolis-hastings algorithm
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+pro linmix_metro_results, arate, ngauss
+
+print, ''
+print, 'Metropolis-Hastings Acceptance Rates:'
+
+print, '(ALPHA, BETA) : ' + strtrim(arate[0], 1)
+print, 'SIGMA^2 : ' + strtrim(arate[1], 1)
+print, ''
+for k = 0, ngauss - 1 do begin
+
+    print, 'GAUSSIAN ' + strtrim(k+1,1)
+    print, '   MEAN : ' + strtrim(arate[2+k], 1)
+    print, '   VARIANCE : ' + strtrim(arate[2+k+ngauss], 1)
+
+endfor
+
+if ngauss gt 1 then begin
+
+    print, ''
+    print, 'Mu0 : ' + strtrim(arate[2+2*ngauss], 1)
+    print, 'u^2 : ' + strtrim(arate[3+2*ngauss], 1)
+    print, 'w^2 : ' + strtrim(arate[4+2*ngauss], 1)
+
+endif
+
+print, ''
+
+return
+end
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;                                                                     ;
+;                             MAIN ROUTINE                            ;
+;                                                                     ;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+pro linmix_err, x, y, post, xsig=xsig, ysig=ysig, xycov=xycov, delta=delta, $
+                ngauss=ngauss, metro=metro, silent=silent, miniter=miniter, $
+                maxiter=maxiter
+
+if n_params() lt 3 then begin
+
+    print, 'Syntax- LINMIX_ERR, X, Y, POST, XSIG=XSIG, YSIG=YSIG, XYCOV=XYCOV,'
+    print, '                    DELTA=DELTA, NGAUSS=NGAUSS, /SILENT, /METRO, '
+    print, '                    MINITER=MINITER, MAXITER=MAXITER'
+    return
+
+endif
+
+;check inputs and setup defaults
+
+nx = n_elements(x)
+if n_elements(y) ne nx then begin
+    print, 'Y and X must have the same size.'
+    return
+endif
+
+if n_elements(xsig) eq 0 and n_elements(ysig) eq 0 then begin
+    print, 'Must supply at least one of XSIG or YSIG.'
+    return
+endif
+
+if n_elements(xsig) eq 0 then begin
+    xsig = dblarr(nx)
+    xycov = dblarr(nx)
+endif
+if n_elements(ysig) eq 0 then begin
+    ysig = dblarr(nx)
+    xycov = dblarr(nx)
+endif
+if n_elements(xycov) eq 0 then xycov = dblarr(nx)
+
+if n_elements(xsig) ne nx then begin
+    print, 'XSIG and X must have the same size.'
+    return
+endif
+if n_elements(ysig) ne nx then begin
+    print, 'YSIG and X must have the same size.'
+    return
+endif
+if n_elements(xycov) ne nx then begin
+    print, 'XYCOV and X must have the same size.'
+    return
+endif
+
+if n_elements(delta) eq 0 then delta = replicate(1, nx)
+if n_elements(delta) ne nx then begin
+    print, 'DELTA and X must have the same size.'
+    return
+endif
+
+bad = where(finite(x) eq 0 or finite(y) eq 0 or finite(xsig) eq 0 or $
+            finite(ysig) eq 0 or finite(xycov) eq 0, nbad)
+
+if nbad gt 0 then begin
+    print, 'Non-finite input detected.'
+    return
+endif
+
+det = where(delta eq 1, ndet, comp=cens, ncomp=ncens) ;get detected data points
+
+if ncens gt 0 then begin
+
+    cens_noerr = where(ysig[cens] eq 0, ncens_noerr)
+    if ncens_noerr gt 0 then begin
+        print, 'NON-DETECTIONS FOR Y MUST HAVE NON-ZERO MEASUREMENT ERROR VARIANCE.'
+        return
+    endif
+
+endif
+
+ ;find data points without measurement error
+xnoerr = where(xsig eq 0, nxnoerr, comp=xerr, ncomp=nxerr)
+ynoerr = where(ysig eq 0, nynoerr, comp=yerr, ncomp=nyerr)
+
+if nxerr gt 0 then ynoerr2 = where(ysig[xerr] eq 0, nynoerr2) else nynoerr2 = 0L
+if nyerr gt 0 then xnoerr2 = where(xsig[yerr] eq 0, nxnoerr2) else nxnoerr2 = 0L
+
+xvar = xsig^2
+yvar = ysig^2
+xycorr = xycov / (xsig * ysig)
+if nxnoerr gt 0 then xycorr[xnoerr] = 0d
+if nynoerr gt 0 then xycorr[ynoerr] = 0d
+
+if not keyword_set(metro) then metro = 0
+if metro then gibbs = 0 else gibbs = 1
+if not keyword_set(silent) then silent = 0
+if n_elements(ngauss) eq 0 then ngauss = 3
+
+if ngauss le 0 then begin
+    print, 'NGAUSS must be at least 1.'
+    return
+endif
+
+if n_elements(miniter) eq 0 then miniter = 5000L ;minimum number of iterations that the 
+                                                 ;Markov Chain must perform
+if n_elements(maxiter) eq 0 then maxiter = 100000L ;maximum number of iterations that the 
+                                                   ;Markov Chain will perform
+
+;; perform MCMC
+
+nchains = 4                    ;number of markov chains
+checkiter = 100               ;check for convergence every 100 iterations
+iter = 0L
+
+;use BCES estimator for initial guess of theta = (alpha, beta, sigsqr)
+beta = ( correlate(x, y, /covar) - mean(xycov) ) / $
+  ( variance(x) - mean(xvar) )
+alpha = mean(y) - beta * mean(x)
+
+sigsqr = variance(y) - mean(yvar) - beta * (correlate(x,y, /covar) - mean(xycov))
+sigsqr = sigsqr > 0.05 * variance(y - alpha - beta * x)
+
+                                ;get initial guess of mixture
+                                ;parameters prior
+mu0 = median(x)
+wsqr = variance(x) - median(xvar)
+wsqr = wsqr > 0.01 * variance(x)
+
+;now get MCMC starting values dispersed around these initial guesses
+
+Xmat = [[replicate(1d, nx)], [x]]
+Vcoef = invert( Xmat ## transpose(Xmat), /double ) * sigsqr
+
+coef = mrandomn(seed, Vcoef, nchains)
+chisqr = randomchi(seed, 4, nchains)
+
+;randomly disperse starting values for (alpha,beta) from a
+;multivariate students-t distribution with 4 degrees of freedom
+alphag = alpha + coef[*,0] * sqrt(4d / chisqr)
+betag = beta + coef[*,1] * sqrt(4d / chisqr)
+
+                                ;draw sigsqr from an Inverse scaled
+                                ;chi-square density
+sigsqrg = sigsqr * (nx / 2) / randomchi(seed, nx / 2, nchains)
+
+;get starting values for the mixture parameters, first do prior
+;parameters
+   
+                                ;mu0 is the global mean
+
+mu0min = min(x) ;prior for mu0 is uniform over mu0min < mu0 < mu0max
+mu0max = max(x)
+
+repeat begin
+    
+    mu0g = mu0 + sqrt(variance(x) / nx) * randomn(seed, nchains) / $
+      sqrt(4d / randomchi(seed, 4, nchains))
+
+    pass = where(mu0g gt mu0min and mu0g lt mu0max, npass)
+
+endrep until npass eq nchains
+
+                                ;wsqr is the global scale
+wsqrg = wsqr * (nx / 2) / randomchi(seed, nx / 2, nchains)
+
+usqrg = replicate(variance(x) / 2d, nchains)
+
+;now get starting values for mixture parameters
+
+tausqrg = dblarr(ngauss, nchains) ;initial group variances
+for k = 0, ngauss - 1 do tausqrg[k,*] = 0.5 * wsqrg * 4 / $
+  randomchi(seed, 4, nchains)
+
+mug = dblarr(ngauss, nchains)   ;initial group means
+for k = 0, ngauss - 1 do mug[k,*] = mu0g + sqrt(wsqrg) * randomn(seed, nchains)
+
+;get initial group proportions and group labels
+
+pig = dblarr(ngauss, nchains)
+Glabel = intarr(nx, nchains)
+
+if ngauss eq 1 then Glabel = intarr(nx, nchains) else begin
+
+    for i = 0, nchains - 1 do begin
+        
+        for j = 0, nx - 1 do begin
+                                ;classify sources to closest centroid
+            dist = abs(mug[*,i] - x[j])
+            mindist = min(dist, minind)
+            
+            pig[minind,i] = pig[minind,i] + 1
+            Glabel[j,i] = minind
+            
+        endfor
+
+    endfor
+
+endelse
+                                ;get initial values for pi from a
+                                ;dirichlet distribution, with
+                                ;parameters based on initial class
+                                ;occupancies
+if ngauss eq 1 then pig = transpose(replicate(1d, nchains)) else $
+  for i = 0, nchains - 1 do pig[*,i] = randomdir(seed, pig[*,i] + 1)
+
+alpha = alphag
+beta = betag
+sigsqr = sigsqrg
+mu = mug
+tausqr = tausqrg
+pi = pig
+mu0 = mu0g
+wsqr = wsqrg
+usqr = usqrg
+
+eta = dblarr(nx, nchains)
+for i = 0, nchains - 1 do eta[*,i] = y ;initial values for eta
+
+nut = 1 ;degrees of freedom for the prior on tausqr
+nuu = 1 ;degrees of freedom for the prior on usqr
+
+;number of parameters to monitor convergence on
+npar = 6
+
+if metro then begin
+;get initial variances for the jumping kernels
+
+    jvar_coef = Vcoef
+    log_ssqr = alog( sigsqr[0] * nx / randomchi(seed, nx, 1000) )
+    jvar_ssqr = variance(log_ssqr) ;get variance of the jumping density
+                                   ;for sigsqr
+
+                                ;get variances for prior variance
+                                ;parameters
+    jvar_mu0 = variance(x) / ngauss
+    jvar_wsqr = variance( alog(variance(x) * nx / randomchi(seed, nx, 1000)) )
+    jvar_usqr = jvar_wsqr
+
+    naccept = lonarr(5 + 2 * ngauss)
+    
+    logpost = dblarr(nchains)
+                                ;get initial values of the
+                                ;log-posterior
+    for i = 0, nchains - 1 do begin
+        
+        theta = [alpha[i], beta[i], sigsqr[i]]
+        loglik = loglik_mixerr( x, y, xvar, yvar, xycov, delta, theta, $
+                                pi[*,i], mu[*,i], tausqr[*,i], Glabel[*,i] )
+        logprior = logprior_mixerr(mu[*,i], mu0[i], tausqr[*,i], usqr[i], wsqr[i])
+        logpost[i] = loglik + logprior
+        
+    endfor
+    
+endif
+
+convergence = 0
+
+;stop burn-in phase after BURNSTOP iterations if doing
+;Metropolis-Hastings jumps, update jumping kernels every BURNITER
+;iterations
+
+burnin = metro ? 1 : 0
+burniter = 250
+burnstop = 500 < (miniter / 2 > 100)
+                                ;start Markov Chains
+if not silent then print, 'Simulating Markov Chains...'
+if not silent and metro then print, 'Doing Burn-in First...'
+
+ygibbs = y
+xi = x
+umax = 1.5 * variance(x) ;prior for usqr is uniform over 0 < usqr < umax
+
+if metro then begin
+                                ;define arrays now so we don't have to
+                                ;create them every MCMC iteration
+    Sigma11 = dblarr(nx, ngauss)
+    Sigma12 = dblarr(nx, ngauss)
+    Sigma22 = dblarr(nx, ngauss)
+    determ = dblarr(nx, ngauss)
+
+endif
+
+gamma = dblarr(nx, ngauss)
+nk = fltarr(ngauss)
+
+repeat begin
+    
+    for i = 0, nchains - 1 do begin ;do markov chains one at-a-time
+        
+        if gibbs then begin
+            
+            if ncens gt 0 then begin
+                                ;first get new values of censored y
+                for j = 0, ncens - 1 do begin
+                    
+                    next = 0
+                    repeat ygibbs[cens[j]] = eta[cens[j],i] + $
+                      sqrt(yvar[cens[j]]) * randomn(seed) $
+                      until ygibbs[cens[j]] le y[cens[j]]
+                    
+                endfor
+                
+            endif
+            
+;need to get new values of Xi and Eta for Gibbs sampler
+            
+            if nxerr gt 0 then begin
+                                ;first draw Xi|theta,x,y,G,mu,tausqr
+                xixy = x[xerr] + xycov[xerr] / yvar[xerr] * (eta[xerr,i] - ygibbs[xerr])
+                if nynoerr2 gt 0 then xixy[ynoerr2] = x[xerr[ynoerr2]]
+                xixyvar = xvar[xerr] * (1 - xycorr[xerr]^2)
+                
+                for k = 0, ngauss - 1 do begin ;do one gaussian at-a-time
+                    
+                    group = where(Glabel[xerr,i] eq k, ngroup)
+                    
+                    if ngroup gt 0 then begin
+                        
+                        xihvar = 1d / (beta[i]^2 / sigsqr[i] + 1d / xixyvar[group] + $
+                                       1d / tausqr[k,i])
+                        xihat = xihvar * $
+                          (xixy[group] / xixyvar[group] + $
+                           beta[i] * (eta[xerr[group],i] - alpha[i]) / sigsqr[i] + $
+                           mu[k,i] / tausqr[k,i])
+                        
+                        xi[xerr[group]] = xihat + sqrt(xihvar) * randomn(seed, ngroup)
+                        
+                    endif
+                    
+                endfor
+
+            endif
+            
+            if nyerr gt 0 then begin
+                                ;now draw Eta|Xi,x,y,theta
+                etaxyvar = yvar[yerr] * (1d - xycorr[yerr]^2)
+                etaxy = ygibbs[yerr] + xycov[yerr] / xvar[yerr] * (xi[yerr] - x[yerr])
+                if nxnoerr2 gt 0 then etaxy[xnoerr2] = ygibbs[yerr[xnoerr2]]
+                etahvar = 1d / (1d / sigsqr[i] + 1d / etaxyvar)
+                etahat = etahvar * (etaxy / etaxyvar + $
+                                    (alpha[i] + beta[i] * xi[yerr]) / sigsqr[i])
+                
+                eta[yerr,i] = etahat + sqrt(etahvar) * randomn(seed, nyerr)
+
+            endif
+
+        endif
+
+                                ;now draw new class labels
+        if ngauss eq 1 then Glabel[*,i] = 0 else begin
+            
+            if gibbs then begin
+                                ;get unnormalized probability that
+                                ;source i came from Gaussian k, given
+                                ;xi[i]
+                for k = 0, ngauss - 1 do $  
+                  gamma[0,k] = pi[k,i] / sqrt(2d * !pi * tausqr[k,i]) * $
+                  exp(-0.5 * (xi - mu[k,i])^2 / tausqr[k,i])
+                
+            endif else begin
+                
+                for k = 0, ngauss - 1 do begin
+                    
+                    Sigma11[0,k] = beta[i]^2 * tausqr[k,i] + sigsqr[i] + yvar
+                    Sigma12[0,k] = beta[i] * tausqr[k,i] + xycov
+                    Sigma22[0,k] = tausqr[k,i] + xvar
+                    
+                    determ[0, k] = Sigma11[*,k] * Sigma22[*,k] - Sigma12[*,k]^2
+                    
+                endfor
+                
+                if ndet gt 0 then begin
+                                ;get unnormalized probability that
+                                ;source i came from Gaussian k, given
+                                ;x[i] and y[i]
+                    for k = 0, ngauss - 1 do begin
+                        
+                        zsqr = (y[det] - alpha[i] - beta[i] * mu[k,i])^2 / Sigma11[det,k] + $
+                          (x[det] - mu[k,i])^2 / Sigma22[det,k] - $
+                          2d * Sigma12[det,k] * (y[det] - alpha[i] - beta[i] * mu[k,i]) * $
+                              (x[det] - mu[k,i]) / (Sigma11[det,k] * Sigma22[det,k])
+                        
+                        corrz = Sigma12[det,k] / sqrt( Sigma11[det,k] * Sigma22[det,k] )
+                        
+                        lognorm = -0.5d * alog(determ[det,k]) - 0.5 * zsqr / (1d - corrz^2)
+                        
+                        gamma[det,k] = pi[k,i] * exp(lognorm) / (2d * !pi)
+                            
+                    endfor
+                    
+                endif
+                
+                if ncens gt 0 then begin
+                                ;get unnormalized probability that
+                                ;source i came from Gaussian k, given
+                                ;x[i] and y[i] > y0[i]
+                    for k = 0, ngauss - 1 do begin
+                        
+                        gamma[cens,k] = pi[k,i] / sqrt(2d * !pi * Sigma22[cens,k]) * $
+                          exp(-0.5 * (x[cens] - mu[k,i])^2 / Sigma22[cens,k])
+                        
+                                ;conditional mean of y, given x
+                        cmeany = alpha[i] + beta[i] * mu[k,i] + Sigma12[cens,k] / Sigma22[cens,k] * $
+                          (x[cens] - mu[k,i])
+                                ;conditional variance of y, given x
+                        cvary = Sigma11[cens,k] - Sigma12[cens,k]^2 / Sigma22[cens,k]
+                                ;make sure logliky is finite
+                        gamma[cens,k] = gamma[cens,k] * gauss_pdf( (y[cens] - cmeany) / sqrt(cvary) )
+                        
+                    endfor
+                    
+                endif
+                
+            endelse
+            
+            norm = total(gamma, 2)
+            
+            for j = 0, nx - 1 do begin
+                
+                gamma0 = reform(gamma[j,*]) / norm[j] ;normalized probability that the i-th data point 
+                                                      ;is from the k-th Gaussian, given the observed
+                                                      ;data point 
+                Gjk = multinom(1, gamma0, seed=seed)
+                Glabel[j,i] = where(Gjk eq 1)
+                
+            endfor
+            
+        endelse
+        
+;now draw new values of regression parameters, theta = (alpha, beta,
+;sigsqr)
+        
+        if gibbs then begin
+                                ;use gibbs sampler to draw alpha,beta|Xi,Eta,sigsqr
+            Xmat = [[replicate(1d, nx)], [xi]]
+            Vcoef = invert( Xmat ## transpose(Xmat), /double ) * sigsqr[i]
+            
+            coefhat = linfit( xi, eta[*,i] )
+            coef = coefhat + mrandomn(seed, Vcoef)
+            
+            alpha[i] = coef[0]
+            beta[i] = coef[1]
+            
+        endif else begin
+
+            theta = [alpha[i], beta[i], sigsqr[i]]
+
+            loglik = loglik_mixerr( x, ygibbs, xvar, yvar, xycov, delta, theta, $
+                                    pi[*,i], mu[*,i], tausqr[*,i], Glabel[*,i] )
+            logprior = logprior_mixerr(mu[*,i], mu0[i], tausqr[*,i], usqr[i], wsqr[i])
+
+            logpost[i] = loglik + logprior ;log-posterior for current parameter values
+
+                                ;use metropolis update to get new
+                                ;values of the coefficients
+            coef = [alpha[i], beta[i]] + mrandomn(seed, jvar_coef)
+            
+            theta = [coef[0], coef[1], sigsqr[i]]
+            loglik_new = loglik_mixerr( x, ygibbs, xvar, yvar, xycov, delta, theta, $
+                                        pi[*,i], mu[*,i], tausqr[*,i], Glabel[*,i] )
+            logprior_new = logprior_mixerr(mu[*,i], mu0[i], tausqr[*,i], usqr[i], wsqr[i])
+            
+            logpost_new = loglik_new + logprior_new
+            
+            accept = linmix_metro_update( logpost_new, logpost[i], seed )
+            
+            if accept then begin
+                
+                naccept[0] = naccept[0] + 1L
+                alpha[i] = coef[0]
+                beta[i] = coef[1]
+                logpost[i] = logpost_new
+                
+            endif
+            
+        endelse
+                                ;now get sigsqr
+        if gibbs then begin
+            
+            ssqr = total( (eta[*,i] - alpha[i] - beta[i] * xi)^2 ) / (nx - 2)
+            sigsqr[i] = (nx - 2) * ssqr / randomchi(seed, nx - 2.0)
+            
+        endif else begin
+                                ;do metropolis update
+            log_ssqr = alog(sigsqr[i]) + sqrt(jvar_ssqr) * randomn(seed)
+            ssqr = exp(log_ssqr)
+            
+            theta = [alpha[i], beta[i], ssqr]
+            
+            loglik_new = loglik_mixerr( x, ygibbs, xvar, yvar, xycov, delta, theta, $
+                                        pi[*,i], mu[*,i], tausqr[*,i], Glabel[*,i] )
+            logprior_new = logprior_mixerr(mu[*,i], mu0[i], tausqr[*,i], usqr[i], wsqr[i])
+            
+            logpost_new = loglik_new + logprior_new + log_ssqr
+            logpost_old = logpost[i] + alog(sigsqr[i])
+            
+            accept = linmix_metro_update( logpost_new, logpost_old, seed )
+            
+            if accept then begin
+                
+                naccept[1] = naccept[1] + 1L
+                sigsqr[i] = ssqr
+                logpost[i] = loglik_new + logprior_new
+                
+            endif
+            
+        endelse 
+
+;now do mixture model parameters, psi = (pi,mu,tausqr)
+        
+        if gibbs then begin
+
+            for k = 0, ngauss - 1 do begin 
+
+                group = where(Glabel[*,i] eq k, ngroup)
+                nk[k] = ngroup
+
+                if ngroup gt 0 then begin
+
+                                ;get mu|Xi,G,tausqr,mu0,usqr
+
+                    if ngauss gt 1 then begin
+
+                        muhat = ngroup * mean(xi[group]) / tausqr[k,i] + mu0[i] / usqr[i]
+                        
+                        muvar = 1d / (1d / usqr[i] + ngroup / tausqr[k,i])
+
+                    endif else begin
+
+                        muhat = ngroup * mean(xi[group]) / tausqr[k,i]
+
+                        muvar = tausqr[k,i] / ngroup
+
+                    endelse
+
+                    muhat = muvar * muhat
+                    
+                    mu[k,i] = muhat + sqrt(muvar) * randomn(seed)
+                    
+                                ;get tausqr|Xi,G,mu,wsqr,nut
+
+                    if ngauss gt 1 then begin
+                        
+                        nuk = ngroup + nut
+                        tsqr = (nut * wsqr[i] + total( (xi[group] - mu[k,i])^2 )) / nuk
+                        
+                    endif else begin
+
+                        nuk = ngroup
+                        tsqr = total( (xi[group] - mu[k,i])^2 ) / nuk
+
+                    endelse
+                
+                    tausqr[k,i] = tsqr * nuk / randomchi(seed, nuk)
+
+                endif else begin
+
+                    mu[k,i] = mu0[i] + sqrt(usqr[i]) * randomn(seed)
+                    tausqr[k,i] = wsqr[i] * nut / randomchi(seed, nut)
+
+                endelse
+
+            endfor
+                                ;get pi|G
+            if ngauss eq 1 then pi[*,i] = 1d else $
+              pi[*,i] = randomdir(seed, nk + 1)
+
+        endif else begin
+                                ;do metropolis-hastings updating using
+                                ;approximate Gibbs sampler
+
+            for k = 0, ngauss - 1 do begin
+                
+                group = where(Glabel[*,i] eq k, ngroup)
+                nk[k] = ngroup
+
+                if ngroup gt 0 then begin
+                                ;get proposal for mu[k], do
+                                ;approximate Gibbs sampler
+                    muprop = mu[*,i]
+
+                    muvarx = (tausqr[k,i] + mean(xvar[group]))
+
+                    muvar = ngauss gt 1 ? 1d / (1d / usqr[i] + ngroup / muvarx) : $
+                      muvarx / ngroup
+
+                    muhat = muprop
+                    
+                    chisqr = randomchi(seed, 4)
+                                ;draw proposal for mu from Student's t
+                                ;with 4 degrees of freedom
+                    muprop[k] = muhat[k] + sqrt(muvar * 4 / chisqr) * randomn(seed)
+
+                endif else begin
+                    
+                    muprop = mu[*,i]
+                    muprop[k] = mu[k,i] + sqrt(usqr[i]) * randomn(seed)
+
+                endelse
+
+                theta = [alpha[i], beta[i], sigsqr[i]]
+                
+                loglik_new = loglik_mixerr( x, ygibbs, xvar, yvar, xycov, delta, theta, $
+                                            pi[*,i], muprop, tausqr[*,i], Glabel[*,i] )
+                logprior_new = logprior_mixerr(muprop, mu0[i], tausqr[*,i], usqr[i], wsqr[i])
+                
+                logpost_new = loglik_new + logprior_new
+                
+                accept = linmix_metro_update( logpost_new, logpost[i], seed )
+                
+                if accept then begin
+                    
+                    naccept[2+k] = naccept[2+k] + 1L
+                    mu[k,i] = muprop[k]
+                    logpost[i] = logpost_new
+                    
+                endif
+
+                                ;get proposal for tausqr[k], do
+                                ;approximate Gibbs sampler
+                tsqrprop = tausqr[*,i]
+ 
+                dof = ngroup > 1
+
+                tsqrprop[k] = tausqr[k,i] * dof / randomchi(seed, dof)
+
+                log_jrat = (dof + 1d) * alog(tsqrprop[k] / tausqr[k,i]) + $
+                  dof / 2d * (tausqr[k,i] / tsqrprop[k] - tsqrprop[k] / tausqr[k,i])
+
+                loglik_new = loglik_mixerr( x, ygibbs, xvar, yvar, xycov, delta, theta, $
+                                            pi[*,i], mu[*,i], tsqrprop, Glabel[*,i] )
+                logprior_new = logprior_mixerr(mu[*,i], mu0[i], tsqrprop, usqr[i], wsqr[i])
+                
+                logpost_new = loglik_new + logprior_new
+                
+                accept = linmix_metro_update( logpost_new, logpost[i], seed, log_jrat)
+                
+                if accept then begin
+                    
+                    naccept[2 + k + ngauss] = naccept[2 + k + ngauss] + 1L
+                    tausqr[k,i] = tsqrprop[k]
+                    logpost[i] = logpost_new
+                    
+                endif
+                
+            endfor
+                                ;get pi|G, can do exact Gibbs sampler
+                                ;for this
+            if ngauss eq 1 then pi[*,i] = 1d else $
+              pi[*,i] = randomdir(seed, nk + 1)
+
+        endelse
+        
+;finally, update parameters for prior distribution, only do this if
+;more than one gaussian
+        
+        if ngauss gt 1 then begin
+
+            if gibbs then begin
+                
+                repeat mu0[i] = mean(mu[*,i]) + sqrt(usqr[i] / ngauss) * randomn(seed) $
+                  until (mu0[i] gt mu0min) and (mu0[i] lt mu0max)
+
+            endif else begin
+                
+                loglik = loglik_mixerr( x, ygibbs, xvar, yvar, xycov, delta, theta, $
+                                        pi[*,i], mu[*,i], tausqr[*,i], Glabel[*,i] )
+                
+                muprop = mu0[i] + sqrt(jvar_mu0) * randomn(seed)
+
+                if muprop gt mu0min and muprop lt mu0max then begin
+
+                    logprior_old = logprior_mixerr(mu[*,i], mu0[i], tausqr[*,i], usqr[i], wsqr[i])
+                    logprior_new = logprior_mixerr(mu[*,i], muprop, tausqr[*,i], usqr[i], wsqr[i])
+                    
+                    logpost_new = loglik + logprior_new
+                    logpost_old = loglik + logprior_old
+                    
+                    accept = linmix_metro_update( logpost_new, logpost_old, seed )
+                    
+                    if accept then begin
+                        
+                        naccept[2 + 2 * ngauss] = naccept[2 + 2 * ngauss] + 1L
+                        mu0[i] = muprop
+                        logpost[i] = loglik + logprior_new
+                        
+                    endif
+                
+                endif
+
+            endelse
+
+            if gibbs then begin
+                
+                nu = ngauss + nuu
+                usqr0 = (nuu * wsqr[i] + total( (mu[*,i] - mu0[i])^2 )) / nu
+                
+                repeat usqr[i] = usqr0 * nu / randomchi(seed, nu) $
+                  until usqr[i] le umax
+                
+            endif else begin
+                                ;do metropolis update
+
+                log_usqr = alog(usqr[i]) + sqrt(jvar_usqr) * randomn(seed)
+                usqr0 = exp(log_usqr)
+                
+                if usqr0 le umax then begin
+
+                    logprior_old = logprior_mixerr(mu[*,i], mu0[i], tausqr[*,i], usqr[i], wsqr[i])
+                    
+                    logpost[i] = loglik + logprior_old ;update posterior after gibbs step for mu0
+                    
+                    logprior_new = logprior_mixerr(mu[*,i], mu0[i], tausqr[*,i], usqr0, wsqr[i])
+                    
+                    logpost_new = loglik + logprior_new
+                    logpost_old = loglik + logprior_old
+                    
+                    log_jrat = log_usqr - alog(usqr[i])
+                    
+                    accept = linmix_metro_update( logpost_new, logpost_old, seed, log_jrat )
+                    
+                    if accept then begin
+                        
+                        naccept[3 + 2 * ngauss] = naccept[3 + 2 * ngauss] + 1L
+                        usqr[i] = usqr0
+                        logpost[i] = loglik + logprior_new
+                        
+                    endif
+
+                endif
+                
+            endelse
+            
+            if gibbs then begin
+                
+                alphaw = ngauss * nut / 2d + 1
+                betaw = 0.5 * nut * total(1d / tausqr[*,i])
+                
+                wsqr[i] = randomgam(seed, alphaw, betaw)
+                
+            endif else begin
+                
+                log_wsqr = alog(wsqr[i]) + sqrt(jvar_wsqr) * randomn(seed)
+                wsqr0 = exp(log_wsqr)
+                
+                logprior_old = logprior_mixerr(mu[*,i], mu0[i], tausqr[*,i], usqr[i], wsqr[i])
+                logprior_new = logprior_mixerr(mu[*,i], mu0[i], tausqr[*,i], usqr[i], wsqr0)
+                
+                logpost_new = loglik + logprior_new + log_wsqr
+                logpost_old = loglik + logprior_old + alog(wsqr[i])
+                
+                accept = linmix_metro_update( logpost_new, logpost_old, seed )
+                
+                if accept then begin
+                    
+                    naccept[4 + 2 * ngauss] = naccept[4 + 2 * ngauss] + 1L
+                    wsqr[i] = wsqr0
+                    logpost[i] = loglik + logprior_new
+                    
+                endif
+                
+            endelse
+
+        endif
+
+    endfor
+
+                                ;save Markov Chains
+    if iter eq 0 then begin
+        
+        alphag = alpha
+        betag = beta
+        sigsqrg = sigsqr
+        
+        pig = pi
+        mug = mu
+        tausqrg = tausqr
+
+        if ngauss gt 1 then begin
+
+            mu0g = mu0
+            usqrg = usqr
+            wsqrg = wsqr
+        
+        endif
+
+        if metro then logpostg = logpost
+
+    endif else begin
+        
+        alphag = [alphag, alpha]
+        betag = [betag, beta]
+        sigsqrg = [sigsqrg, sigsqr]
+
+        pig = [[pig], [pi]]
+        mug = [[mug], [mu]]
+        tausqrg = [[tausqrg], [tausqr]]
+        
+        if ngauss gt 1 then begin
+        
+            mu0g = [mu0g, mu0]
+            usqrg = [usqrg, usqr]
+            wsqrg = [wsqrg, wsqr]
+
+        endif
+        
+        if metro then logpostg = [logpostg, logpost]
+
+    endelse
+        
+    iter = iter + 1L
+    
+;check for convergence
+    
+    if iter ge 4 and iter eq checkiter and not burnin then begin
+
+        if not silent and metro then linmix_metro_results, $
+          float(naccept) / (nchains * iter), ngauss
+
+        Bvar = dblarr(npar)     ;between-chain variance
+        Wvar = dblarr(npar)     ;within-chain variance
+        
+        psi = dblarr(iter, nchains, npar)
+        
+        psi[*,*,0] = transpose(reform(alphag, nchains, iter))
+        psi[*,*,1] = transpose(reform(betag, nchains, iter))
+        psi[*,*,2] = transpose(reform(sigsqrg, nchains, iter))
+
+        pig2 = reform(pig, ngauss, nchains, iter)
+        mug2 = reform(mug, ngauss, nchains, iter)
+        tausqrg2 = reform(tausqrg, ngauss, nchains, iter)
+
+        psi[*,*,3] = transpose( total(pig2 * mug2, 1) ) ;mean of xi
+                                ;variance of xi
+        psi[*,*,4] = transpose( total(pig2 * (tausqrg2 + mug2^2), 1) ) - psi[*,*,3]^2
+                                ;linear correlation coefficient
+                                ;between xi and eta
+        psi[*,*,5] = psi[*,*,1] * sqrt(psi[*,*,4] / (psi[*,*,1]^2 * psi[*,*,4] + psi[*,*,2]))
+                                ;do normalizing transforms before
+                                ;monitoring convergence
+        psi[*,*,2] = alog(psi[*,*,2])
+        psi[*,*,4] = alog(psi[*,*,4])
+        psi[*,*,5] = linmix_atanh(psi[*,*,5])
+
+        psi = psi[iter/2:*,*,*] ;discard first half of MCMC
+
+        ndraw = iter / 2
+                                ;calculate between- and within-sequence
+                                ;                  variances
+        for j = 0, npar - 1 do begin
+            
+            psibarj = total( psi[*,*,j], 1 ) / ndraw
+            psibar = mean(psibarj)
+            
+            sjsqr = 0d
+            for i = 0, nchains - 1 do $
+              sjsqr = sjsqr + total( (psi[*, i, j] - psibarj[i])^2 ) / (ndraw - 1.0)
+            
+            Bvar[j] = ndraw / (nchains - 1.0) * total( (psibarj - psibar)^2 )
+            Wvar[j] = sjsqr / nchains
+            
+        endfor
+        
+        varplus = (1.0 - 1d / ndraw) * Wvar + Bvar / ndraw
+        Rhat = sqrt( varplus / Wvar ) ;potential variance scale reduction factor
+        
+        if total( (Rhat le 1.1) ) eq npar and iter ge miniter then convergence = 1 $
+        else if iter ge maxiter then convergence = 1 else begin
+            
+            if not silent then begin
+                print, 'Iteration: ', iter
+                print, 'Rhat Values for ALPHA, BETA, log(SIGMA^2), mean(XI), ' + $
+                  'log(variance(XI), atanh(corr(XI,ETA)) ): '
+                print, Rhat
+            endif
+            
+            checkiter = checkiter + 100L
+            
+        endelse
+
+    endif
+    
+    if (burnin) and (iter eq burniter) then begin
+;still doing burn-in stage, get new estimates for jumping kernel
+;parameters
+        
+        jvar_ssqr = linmix_robsig( alog(sigsqrg) )^2
+
+                                ;now modify covariance matrix for
+                                ;coefficient jumping kernel
+        coefg = [[alphag], [betag]]
+        
+        jvar_coef = correlate( transpose(coefg), /covar)
+
+        if ngauss gt 1 then begin
+
+            jvar_mu0 = linmix_robsig(mu0g)^2 * 2.4^2
+
+            jvar_usqr = linmix_robsig( alog(usqrg) )^2 * 2.4^2
+    
+            jvar_wsqr = linmix_robsig( alog(wsqrg) )^2 * 2.4^2
+
+        endif
+        
+        if iter eq burnstop then burnin = 0
+        
+        if not burnin then begin
+
+           if not silent then print, 'Burn-in Complete'
+
+           iter = 0L
+
+        endif
+
+        naccept = lonarr(5 + 2 * ngauss)
+        burniter = burniter + 250L
+        
+    endif
+    
+endrep until convergence
+
+ndraw = iter * nchains / 2
+
+;save posterior draws in a structure
+
+if ngauss gt 1 then begin
+
+    post = {alpha:0d, beta:0d, sigsqr:0d, pi:dblarr(ngauss), mu:dblarr(ngauss), $
+            tausqr:dblarr(ngauss), mu0:0d, usqr:0d, wsqr:0d, ximean:0d, xisig:0d, $
+            corr:0d}
+
+endif else begin
+    
+    post = {alpha:0d, beta:0d, sigsqr:0d, pi:dblarr(ngauss), mu:dblarr(ngauss), $
+            tausqr:dblarr(ngauss), ximean:0d, xisig:0d, corr:0d}
+
+endelse
+
+post = replicate(post, ndraw)
+
+post.alpha = alphag[(iter*nchains+1)/2:*]
+post.beta = betag[(iter*nchains+1)/2:*]
+post.sigsqr = sigsqrg[(iter*nchains+1)/2:*]
+post.pi = pig[*,(iter*nchains+1)/2:*]
+post.mu = mug[*,(iter*nchains+1)/2:*]
+post.tausqr = tausqrg[*,(iter*nchains+1)/2:*]
+
+if ngauss gt 1 then begin
+
+    post.mu0 = mu0g[(iter*nchains+1)/2:*]
+    post.usqr = usqrg[(iter*nchains+1)/2:*]
+    post.wsqr = wsqrg[(iter*nchains+1)/2:*]
+
+endif
+
+post.ximean = total(post.pi * post.mu, 1) ;mean of xi   
+post.xisig = total(post.pi * (post.tausqr + post.mu^2), 1) - post.ximean^2
+post.xisig = sqrt(post.xisig)   ;standard deviation of xi
+
+                                ;get linear correlation coefficient
+                                ;between xi and eta
+post.corr = post.beta * post.xisig / sqrt(post.beta^2 * post.xisig^2 + post.sigsqr)
+
+return
+end
diff --git a/Code/script_idl_mv/astrolib/linterp.pro b/Code/script_idl_mv/astrolib/linterp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..95ead98b30839b05beb60e6fb67a61a0d3368c14
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/linterp.pro
@@ -0,0 +1,119 @@
+pro linterp, Xtab, Ytab, Xint, Yint, MISSING = missing, NoInterp = NoInterp
+;+
+; NAME:   
+;       LINTERP  
+; PURPOSE: 
+;       Linearly interpolate tabulated 1-d data from one grid to a new one.
+; EXPLANATION:
+;       The results of LINTERP are numerically equivalent to the IDL intrinsic
+;       INTERPOL() function, but note the following:
+;         (1) LINTERP is a procedure rather than a function
+;         (2) INTERPOL() extrapolates beyond the end points whereas LINTERP
+;             truncates to the endpoints (or uses the MISSING keyword)
+;         (3) LINTERP (unlike INTERPOL) uses the intrinsic INTERPOLATE function
+;                 and thus may have a speed advantage
+;         (4) Prior to V8.2.3 LINTERP converted the new grid vector to floating point 
+;                (because INTERPOLATE does this) whereas INTERPOL() and post-V8.2.3 
+;                LINTERP will keep double precision if supplied.
+;
+;       Use QUADTERP for quadratic interpolation.
+;
+; CALLING SEQUENCE:
+;       LINTERP, Xtab, Ytab, Xint, Yint, [MISSING =, /NoInterp ]   
+;
+; INPUT PARAMETERS: 
+;       Xtab -  Vector containing the current independent variable grid.
+;               Must be monotonic increasing or decreasing
+;       Ytab -  Vector containing the current dependent variable values at 
+;               the XTAB grid points.
+;       Xint -  Scalar or vector containing the new independent variable grid 
+;               points for which interpolated value(s) of the dependent 
+;               variable are sought.    Note that -- due to a limitation of the
+;               intrinsic INTERPOLATE() function -- Xint is always converted to
+;               floating point internally.
+;
+; OUTPUT PARAMETERS:
+;       Yint  -  Scalar or vector with the interpolated value(s) of the 
+;               dependent variable at the XINT grid points.
+;               YINT is double precision if XTAB or YTAB are double,
+;               otherwise YINT is float
+;
+; OPTIONAL INPUT KEYWORD:
+;       MISSING - Scalar specifying YINT value(s) to be assigned, when Xint
+;               value(s) are outside of the range of Xtab.     Default is to
+;               truncate the out of range YINT value(s) to the nearest value 
+;               of YTAB.   See the help for the INTERPOLATE function.
+;       /NoINTERP - If supplied then LINTERP returns the YTAB value(s) 
+;               associated with the closest XTAB value(s)rather than 
+;               interpolating.
+;
+; EXAMPLE:
+;       To linearly interpolate from a spectrum wavelength-flux pair
+;       Wave, Flux to another wavelength grid defined as:
+;       WGrid = [1540., 1541., 1542., 1543., 1544, 1545.]
+;   
+;       IDL>  LINTERP, Wave, Flux, WGrid, FGrid  
+;
+;       FGRID will be a 6 element vector containing the values of Flux
+;       linearly interpolated onto the WGrid wavelength scale
+;
+; PROCEDURE: 
+;       Uses TABINV to calculate the effective index of the values
+;       in Xint in the table Xtab.  The resulting index is used
+;       with the intrinsic INTERPOLATE function to find the corresponding 
+;       Yint value in Ytab.  Unless the MISSING keyword is supplied, out
+;       of range Yint values are truncated to the nearest value of Ytab.
+;
+; PROCEDURES CALLED:
+;       TABINV, ZPARCHECK
+; MODIFICATION HISTORY:
+;       Adapted from the IUE RDAF,  W. Landsman      October, 1988
+;       Modified to use the new INTERPOLATE function        June, 1992
+;       Modified to always return REAL*4             October, 1992
+;       Added MISSING keyword                        August, 1993
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added NoInterp keyword  W. Landsman      July 1999
+;       Work for unsigned, 64 bit integers  W. Landsman  October 2001
+;       Call INTERPOLATE with /DOUBLE if V8.2.3 W. Landsman Feb 2015
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 4 then begin
+   print,'Syntax - LINTERP, Xtab, Ytab, Xint, Yint, [ MISSING = ]' 
+   print,'    Xtab, Ytab - Input X and Y vectors'
+   print,'    Xint - Input X value (scalar or vector) at which to interpolate'
+   print,'    Yint - Output interpolated Y value(s)'
+   return
+ endif
+
+ numeric = [indgen(5)+1,12,13,14,15]      ;Numeric datatypes
+ zparcheck, 'LINTERP', Xtab, 1, numeric, 1, 'Current X Vector' 
+ zparcheck, 'LINTERP', Ytab, 2, numeric, 1, 'Current Y Vector' 
+ zparcheck, 'LINTERP', Xint, 3, numeric, [0,1], 'New X Vector or Scalar'
+
+; Determine index of data-points from which interpolation is made
+
+ npts = min( [ N_elements(Xtab), N_elements(Ytab) ] )
+ tabinv, Xtab, Xint, r                                    
+ if keyword_set(NoInterp) then Yint = Ytab[round(r)] else begin
+ ytype = size( Ytab, /TYPE)
+
+; Perform linear interpolation
+
+ if (ytype LE 3) || (ytype GE 12) then  $             ;Integer or byte input?
+     Yint = interpolate( float(Ytab), r) else $
+     if !VERSION.RELEASE GE '8.2.3' then $
+     Yint = interpolate( Ytab, r, DOUBLE = (ytype EQ 5) ) else $
+     Yint = interpolate( Ytab, r)
+
+ endelse 
+
+ if N_elements(missing) EQ 1 then begin
+        Xmin = min( [ Xtab[0],Xtab[npts-1] ], max = Xmax)
+        bad = where( (Xint LT Xmin) or (Xint GT Xmax ), Nbad)
+        if Nbad GT 0 then Yint[bad] = missing
+ endif
+        
+ return
+ end                                        
diff --git a/Code/script_idl_mv/astrolib/list_with_path.pro b/Code/script_idl_mv/astrolib/list_with_path.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0814d5390f9d4ce508b70b45bf064ff4795ed47c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/list_with_path.pro
@@ -0,0 +1,70 @@
+	FUNCTION LIST_WITH_PATH, FILENAME, PATHS, NOCURRENT=NOCURRENT, $
+		COUNT = COUNT
+;+
+; NAME: 
+;	LIST_WITH_PATH
+; PURPOSE: 
+;	Search for files in a specified directory path.
+; EXPLANATION:
+;	Lists files in a set of default paths, similar to using FILE_SEARCH,
+;	except that a list of paths to be searched can be given.
+;
+; CALLING SEQUENCE: 
+;	Result = LIST_WITH_PATH( FILENAME, PATHS )
+;
+; INPUTS: 
+;	FILENAME   = Name of file to be searched for.  It may contain wildcard
+;		     characters, e.g. "*.dat".
+;
+;	PATHS	   = One or more default paths to use in the search in case
+;		     FILENAME does not contain a path itself.  The individual
+;		     paths are separated by commas, although in UNIX, colons
+;		     can also be used.  In other words, PATHS has the same
+;		     format as !PATH, except that commas can be used as a
+;		     separator regardless of operating system.  The current
+;		     directory is always searched first, unless the keyword
+;		     NOCURRENT is set.
+;
+;		     A leading $ can be used in any path to signal that what
+;		     follows is an environmental variable, but the $ is not
+;		     necessary.    Environmental variables can themselves 
+;                    contain multiple paths.
+;
+; OUTPUTS: 
+;	The result of the function is a list of filenames.
+; EXAMPLE:
+;	FILENAME = ''
+;	READ, 'File to open: ', FILENAME
+;	FILE = LIST_WITH_PATH( FILENAME, 'SERTS_DATA', '.fix' )
+;	IF FILE NE '' THEN ...
+; PROCEDURE CALLS: 
+;	BREAK_PATH, CONCAT_DIR()
+; Category    : 
+;	Utilities, Operating_system
+; REVISION HISTORY:
+;	Version 1, William Thompson, GSFC, 3 November 1994
+;	Documentation modified Wayne Landsman HSTX  November 1994
+;	Assume since V5.5, vector call to FILE_SEARCH()  W. Landsman Sep 2006
+;       Restore pre-Sep 2006 behavior of not searching subdirectories 
+;                      W.Landsman. Feb 2007
+;-
+;
+        COMPILE_OPT IDL2
+	ON_ERROR, 2
+;
+;  Check the number of parameters:
+;
+	IF N_PARAMS() NE 2 THEN MESSAGE, 'Syntax:  Result = ' + $
+		'LIST_WITH_PATH(FILENAME, PATHS)'
+
+       PATH = BREAK_PATH(PATHS)
+;
+;  If NOCURRENT was set, then remove the first (blank) entry from the PATH
+;  array.
+;
+	IF KEYWORD_SET(NOCURRENT) THEN PATH = PATH[1:*]
+
+	FILES = FILE_SEARCH( CONCAT_DIR(PATH, FILENAME), COUNT=COUNT) 
+;
+	RETURN, FILES
+	END
diff --git a/Code/script_idl_mv/astrolib/lsf_rotate.pro b/Code/script_idl_mv/astrolib/lsf_rotate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9914869d9192a5565025c0de15fae54d0ac9fd88
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/lsf_rotate.pro
@@ -0,0 +1,80 @@
+ function lsf_rotate, deltav, vsini, EPSILON = epsilon, VELGRID = velgrid
+;+
+; NAME:
+;     LSF_ROTATE:
+;
+; PURPOSE:
+;     Create a 1-d convolution kernel to broaden a spectrum from a rotating star
+;
+; EXPLANATION:
+;     Can be used to derive the broadening effect (line spread function; LSF) 
+;     due to  rotation on a synthetic stellar spectrum.     Assumes constant 
+;     limb darkening across the disk.
+;
+; CALLING SEQUENCE
+;     lsf = LSF_ROTATE(deltav, vsini, EPSILON=, VELGRID=)
+;
+; INPUT PARAMETERS:
+;    deltaV - numeric scalar giving the step increment (in km/s) in the output 
+;             rotation kernel.  
+;    Vsini - the rotational velocity projected  along the line of sight (km/s)
+;
+; OUTPUT PARAMETERS:
+;    LSF - The convolution kernel vector for the specified rotational velocity.
+;          The  number of points in LSF will be always be odd (the kernel is
+;          symmetric) and equal to  either ceil(2*Vsini/deltav) or 
+;          ceil(2*Vsini/deltav) +1 (whichever number is odd).    LSF will 
+;          always be of type FLOAT.
+;
+;          To actually compute the broadening. the spectrum should be convolved
+;          with the rotational LSF. 
+; OPTIONAL INPUT PARAMETERS:
+;    Epsilon - numeric scalar giving the limb-darkening coefficient, 
+;          default = 0.6 which is typical for  photospheric lines.    The
+;          specific intensity I at any angle theta from the specific intensity
+;          Icen at the center of the disk is given by:
+;  
+;          I = Icen*(1-epsilon*(1-cos(theta))
+;                    
+; OPTIONAL OUTPUT PARAMETER:
+;     Velgrid - Vector with the same number of elements as LSF 
+; EXAMPLE:
+;    (1) Plot the LSF for a star rotating at 90 km/s in both velocity space and
+;        for a central wavelength of 4300 A.    Compute the LSF every 3 km/s
+;
+;       IDL> lsf = lsf_rotate(3,90,velgrid=vel)      ;LSF will contain 61 pts
+;       IDL> plot,vel,lsf                    ;Plot the LSF in velocity space
+;       IDL> wgrid = 4300*(1+vel/3e5)       ;Speed of light = 3e5 km/s
+;       IDL> oplot,wgrid,lsf                ;Plot in wavelength space
+;
+; NOTES:
+;    Adapted from rotin3.f in the SYNSPEC software of Hubeny & Lanz 
+;        .http://nova.astro.umd.edu/index.html    Also see Eq. 17.12 in 
+;    "The Observation and Analysis of Stellar Photospheres" by D. Gray (1992)
+; REVISION HISTORY:
+;    Written,   W. Landsman                November 2001
+;-
+    On_error,2
+    compile_opt idl2
+    if N_params() LT 1 then begin
+         print,'Syntax - rkernel = lsf_rotate(deltav, vsini)'
+         print,'      Input Keyword: Epsilon'
+         print,'      Output Keyword: Velgrid'
+         return,-1
+    endif
+
+    if N_elements(epsilon) EQ 0 then epsilon = 0.6
+    e1 = 2.0d*(1.0d - epsilon)
+    e2 = !dpi*epsilon/2.0d
+    e3 = !dpi*(1.0d - epsilon/3.0d)
+
+    npts = ceil(2*vsini/deltav)
+    if npts mod 2 EQ 0 then npts = npts +1
+    nwid = npts/2
+    x = (dindgen(npts)- nwid)
+    x = x*deltav/vsini  
+    if arg_present(velgrid) then velgrid = x*vsini
+    x1 = abs(1.0d - x^2)
+    return, float((e1*sqrt(x1) + e2*x1)/e3)
+   
+    end   
diff --git a/Code/script_idl_mv/astrolib/lumdist.pro b/Code/script_idl_mv/astrolib/lumdist.pro
new file mode 100644
index 0000000000000000000000000000000000000000..17113123ae8cbe9cd0fc68ef91aa85737673432d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/lumdist.pro
@@ -0,0 +1,123 @@
+;+
+; NAME: 
+;    LUMDIST
+;       
+; PURPOSE: 
+;    Calculate luminosity distance (in Mpc) of an object given its redshift 
+; EXPLANATION:
+;    The luminosity distance in the Friedmann-Robertson-Walker model is 
+;    taken from  Caroll, Press, and Turner (1992, ARAA, 30, 499), p. 511
+;    Uses a closed form (Mattig equation) to compute the distance when the 
+;    cosmological constant is zero.   Otherwise integrates the function using
+;    QSIMP.	
+; CALLING SEQUENCE: 
+;    result = lumdist(z, [H0 = , k = , Omega_M =, Lambda0 = , q0 = ,/SILENT])
+;      
+; INPUTS:
+;    z = redshift, positive scalar or vector
+;
+; OPTIONAL KEYWORD INPUTS: 
+;    /SILENT - If set, the program will not display adopted cosmological
+;        parameters at the terminal.
+;    H0: Hubble parameter  in km/s/Mpc, default is 70
+;
+;        No more than two of the following four parameters should be
+;        specified.   None of them need be specified -- the adopted defaults
+;        are given.
+;    k - curvature constant, normalized to the closure density.   Default is
+;        0, indicating a flat universe
+;    Omega_m -  Matter density, normalized to the closure density, default
+;        is 0.3.   Must be non-negative
+;    Lambda0 - Cosmological constant, normalized to the closure density,
+;        default is 0.7
+;    q0 - Deceleration parameter, numeric scalar = -R*(R'')/(R')^2, default
+;        is -0.55
+;       
+; OUTPUTS:
+;    The result of the function is the luminosity distance (in Mpc) for each 
+;    input value of z.
+;
+; EXAMPLE:
+;    (1) Plot the distance of a galaxy in Mpc as a function of redshift out 
+;        to z = 5.0, assuming the default cosmology (Omega_m=0.3, Lambda = 0.7,
+;        H0 = 70 km/s/Mpc)
+;
+;        IDL> z = findgen(50)/10.
+;        IDL> plot,z,lumdist(z),xtit='z',ytit='Distance (Mpc)'
+;
+;        Now overplot the relation for zero cosmological constant and 
+;        Omega_m=0.3
+;        IDL> oplot,z,lumdist(z,lambda=0,omega=0.3),linestyle=1
+; COMMENTS:
+;    (1) Integrates using the IDL Astronomy Version procedure QSIMP.    (The 
+;    intrinsic IDL QSIMP function is not called because of its ridiculous
+;    restriction that only scalar arguments can be passed to the integrating
+;    function.)
+;    (2) Can fail to converge at high redshift for closed universes with
+;    non-zero lambda.   This can presumably be fixed by replacing QSIMP with
+;    an integrator that can handle a singularity 
+; PROCEDURES CALLED:
+;    COSMO_PARAM, QSIMP   
+; REVISION HISTORY:
+;    Written   W. Landsman        Raytheon ITSS       April 2000
+;    Avoid integer overflow for more than 32767 redshifts  July 2001
+;    Use double precision J. Moustakas/W. Landsman April 2008
+;-                                   
+ function ldist, z, q0 = q0, lambda0 = lambda0
+   term1 = (1.+z)^2
+   term2 =  1.+2.*(q0+lambda0)*z
+   term3 = z*(2.+z)*lambda0
+   denom = (term1*term2 - term3)
+   out = z*0.  
+   good = where(denom GT 0.0, Ngood)
+   if Ngood GT 0 then out[good] = 1./sqrt(denom[good])
+   return, out
+ end 
+
+ FUNCTION  lumdist, z, h0=h0, k = k, Lambda0 = lambda0, Omega_m = Omega_m, $
+                    q0 = q0, Silent = silent
+
+ compile_opt idl2
+ if N_params() eq 0 then begin
+	print,'Syntax: result = lumdist(z, H0 = ,k=, Lambda0 = ])'
+	print,'Returns luminosity distance in Mpc'
+	return, 0.
+  endif
+
+ n = N_elements(z)
+ cosmo_param,Omega_m,Lambda0, k, q0 
+
+; Check keywords 
+  c = 2.99792458D5                  ;  speed of light in km/s
+  if N_elements(H0) EQ 0 then H0 = 70
+  if not keyword_set(silent) then $
+     print,'LUMDIST: H0:', h0, ' Omega_m:', omega_m, ' Lambda0',lambda0, $
+        ' q0: ',q0, ' k: ', k, f='(A,I3,A,f5.2,A,f5.2,A,f5.2,A,F5.2)' 
+ 
+; For the case of Lambda = 0, we use the closed form from equation 5.238 of
+; Astrophysical Formulae (Lang 1998).   This avoids terms that almost cancel
+; at small q0*z better than the more familiar Mattig formula.
+;
+ if lambda0 EQ  0 then begin
+    denom = sqrt(1+2*q0*z) + 1 + q0*z 
+    dlum = (c*z/h0)*(1 + z*(1-q0)/denom)
+    return,dlum
+ 
+; For non-zero lambda 
+endif else begin 
+    dlum = z*0.0 
+    for  i=0L,N-1 do begin
+         if z[i] LE 0.0 then dlum[i] = 0.0 else begin
+         qsimp,'LDIST',0,z[i], lz,q0 = q0, Lambda0 = lambda0
+         dlum[i] = lz
+         endelse
+    endfor
+
+    if k GT 0 then $
+         dlum = sinh(sqrt(k)*dlum)/sqrt(k) $
+    else if k LT 0 then $
+          dlum = sin(sqrt(-k)*dlum)/sqrt(-k) > 0 
+    return, c*(1+z)*dlum/h0
+ endelse   
+
+ end
diff --git a/Code/script_idl_mv/astrolib/mag2flux.pro b/Code/script_idl_mv/astrolib/mag2flux.pro
new file mode 100644
index 0000000000000000000000000000000000000000..030e405ff0e709b2e22267b5852905adcbd28253
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mag2flux.pro
@@ -0,0 +1,51 @@
+function mag2flux, mag, zero_pt, ABwave = ABwave
+;+
+; NAME:
+;	MAG2FLUX
+; PURPOSE:
+;	Convert from magnitudes to flux (ergs/s/cm^2/A). 
+; EXPLANATION:
+;	Use FLUX2MAG() for the opposite direction.
+;
+; CALLING SEQUENCE:
+;	flux = mag2flux( mag, [ zero_pt, ABwave = ] )
+;
+; INPUTS:
+;	mag - scalar or vector of magnitudes
+;
+; OPTIONAL INPUT:
+;	zero_pt - scalar giving the zero point level of the magnitude.
+;		If not supplied then zero_pt = 21.1 (Code et al. 1976)
+;               Ignored if the ABwave keyword is set.
+;
+; OPTIONAL KEYWORD INPUT:
+;     ABwave - wavelength scalar or vector in Angstroms.   If supplied, then 
+;              the input vector, mag, is assumed to contain Oke AB magnitudes
+;              (Oke & Gunn 1983, ApJ, 266, 713)
+;
+; OUTPUT:
+;	flux - scalar or vector flux vector, in erg cm-2 s-1 A-1
+;              If the ABwave keyword is set, then the flux is given by
+;
+;              f = 10^(-0.4*(mag +2.406 + 4*alog10(ABwave)))     
+;
+;              Otherwise the flux is given by
+;              f =  10^(-0.4*(mag + zero_pt))
+;
+; EXAMPLE:
+;       Suppose one is given vectors of wavelengths and AB magnitudes, w (in
+;       Angstroms) and mag.   Plot the spectrum in erg cm-2 s-1 A-1
+;
+;       IDL> plot, w, mag2flux(mag,ABwave = w)
+; REVISION HISTORY:
+;	Written    J. Hill        STX Co.       1988
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Added ABwave keyword,   W. Landsman   September 1998
+;-   
+ if ( N_params() lt 2 ) then zero_pt = 21.10
+
+ if keyword_set(ABwave) then $
+           return, 10^(-0.4*(mag + 2.406 + 5*alog10(ABwave))) else $
+           return, 10^(-0.4*( mag + zero_pt))
+
+ end
diff --git a/Code/script_idl_mv/astrolib/mag2geo.pro b/Code/script_idl_mv/astrolib/mag2geo.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7cada6305846eb0988fb2b67970deae91bc5bbd4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mag2geo.pro
@@ -0,0 +1,97 @@
+;+
+; NAME:
+;      MAG2GEO()
+;
+; PURPOSE:
+;     Convert from geomagnetic to geographic coordinates
+;
+; EXPLANATION:
+; 
+;     Converts from GEOMAGNETIC (latitude,longitude) to GEOGRAPHIC (latitude,
+;    longitude).    (altitude remains the same)
+;
+; CALLING SEQUENCE:
+;       gcoord=mag2geo(mcoord)
+;
+; INPUT:
+;       mcoord = a 2-element array of magnetic [latitude,longitude], or an 
+;                array [2,n] of n such coordinates.
+;
+; KEYWORD INPUTS:
+;               None
+;
+; OUTPUT:
+;       a 2-element array of geographic [latitude,longitude], or an array [2,n]
+;            of n such coordinates                   
+;
+; COMMON BLOCKS:
+;               None
+;
+; EXAMPLES:
+;       IDL> gcoord=mag2geo([90,0])       ; coordinates of magnetic south pole
+;       IDL> print,gcoord
+;       79.300000      -71.409990
+;
+; MODIFICATION HISTORY:
+;       Written by Pascal Saint-Hilaire (Saint-Hilaire@astro.phys.ethz.ch), 
+;        May 2002
+;-
+;====================================================================================
+FUNCTION mag2geo,incoord
+
+        ; SOME 'constants'...
+        Dlong=288.59D   ; longitude (in degrees) of Earth's magnetic south pole
+                        ; (which is near the geographic north pole!) (1995)
+        Dlat=79.30D     ; latitude (in degrees) of same (1995)
+        R = 1D          ; distance from planet center (value unimportant -- 
+                ;just need a length for conversion to rectangular coordinates)
+
+        ; convert first to radians
+        Dlong=Dlong*!DPI/180.
+        Dlat=Dlat*!DPI/180.
+
+        mlat=DOUBLE(incoord[0,*])*!DPI/180.
+        mlon=DOUBLE(incoord[1,*])*!DPI/180.
+        malt=mlat * 0. + R
+        
+        coord=[mlat,mlon,malt]
+
+        ;convert to rectangular coordinates
+        ;       X-axis: defined by the vector going from Earth's center towards
+        ;            the intersection of the equator and Greenwich's meridian.
+        ;       Z-axis: axis of the geographic poles
+        ;       Y-axis: defined by Y=Z^X
+        x=coord[2,*]*cos(coord[0,*])*cos(coord[1,*])
+        y=coord[2,*]*cos(coord[0,*])*sin(coord[1,*])
+        z=coord[2,*]*sin(coord[0,*])
+
+        ;First rotation : in the plane of the current meridian from magnetic 
+        ;pole to geographic pole.
+        togeolat=dblarr(3,3)
+        togeolat[0,0]=cos(!DPI/2-Dlat)
+        togeolat[0,2]=sin(!DPI/2-Dlat)
+        togeolat[2,0]=-sin(!DPI/2-Dlat)
+        togeolat[2,2]=cos(!DPI/2-Dlat)
+        togeolat[1,1]=1.
+        out= togeolat # [x,y,z]
+
+        ;Second rotation matrix : rotation around plane of the equator, from
+        ;the meridian containing the magnetic poles to the Greenwich meridian.
+        maglong2geolong=dblarr(3,3)
+        maglong2geolong[0,0]=cos(Dlong)
+        maglong2geolong[0,1]=-sin(Dlong)
+        maglong2geolong[1,0]=sin(Dlong)
+        maglong2geolong[1,1]=cos(Dlong)
+        maglong2geolong[2,2]=1.
+        out=maglong2geolong # out
+
+        ;convert back to latitude, longitude and altitude
+        glat=atan(out[2,*],sqrt(out[0,*]^2+out[1,*]^2))
+        glat=glat*180./!DPI
+        glon=atan(out[1,*],out[0,*])
+        glon=glon*180./!DPI
+        ;galt=sqrt(out[0,*]^2+out[1,*]^2+out[2,*]^2)-R  ; I don't care about that one...just put it there for completeness' sake 
+
+        RETURN,[glat,glon]
+END
+;====================================================================================
diff --git a/Code/script_idl_mv/astrolib/make_2d.pro b/Code/script_idl_mv/astrolib/make_2d.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0b75a1985ab42b18f49d0cdb2e496ca8d4ec8472
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/make_2d.pro
@@ -0,0 +1,57 @@
+pro make_2d,x,y,xx,yy
+;+
+; NAME:
+;       MAKE_2D
+; PURPOSE:
+;       Change from 1-d indexing to 2-d indexing
+; EXPLANATION:
+;       Convert an N element X vector, and an M element Y vector, into
+;       N x M arrays giving all possible combination of X and Y pairs.
+;       Useful for obtaining the X and Y positions of each element of
+;       a regular grid.
+;
+; CALLING SEQUENCE:
+;       MAKE_2D, X, Y, [ XX, YY ]
+;
+; INPUTS:
+;       X - N element vector of X positions
+;       Y - M element vector of Y positions
+;
+; OUTPUTS:
+;       XX - N x M element array giving the X position at each pixel
+;       YY - N x M element array giving the Y position of each pixel
+;               If only 2 parameters are supplied then X and Y will be
+;               updated to contain the output arrays
+;
+; EXAMPLE:
+;       To obtain the X and Y position of each element of a 30 x 15 array
+;
+;       IDL> x = indgen(30)  &  y = indgen(15)     
+;       IDL> make_2d, x, y 
+; REVISION HISTORY:
+;       Written,    Wayne Landsman    ST Systems Co.    May, 1988
+;       Added /NOZERO keyword       W. Landsman         Mar, 1991
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Improved speed          P. Broos      July 2000
+;-          
+ On_error,2
+ if N_params() LT 2 then begin
+     print,'Syntax - make_2d, x, y, [xx, yy]'
+     print,'         x,y - Input X,Y vectors'
+     print,'         xx,yy - Output arrays specifying X and Y indices'
+     return
+ endif
+
+ ny = N_elements(y)
+ nx = N_elements(x)
+
+ xx = rebin(reform(x, nx,  1,/OVERWRITE), nx, ny, /SAMPLE)
+ yy = rebin(reform(y,  1, ny,/OVERWRITE), nx, ny, /SAMPLE)
+
+ if N_params() LT 3 then begin  ;Update X and Y vectors
+     x = temporary(xx)
+     y = temporary(yy)
+ endif     
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/make_astr.pro b/Code/script_idl_mv/astrolib/make_astr.pro
new file mode 100644
index 0000000000000000000000000000000000000000..20177f4ea11180707df7fb2606b03730c664f35a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/make_astr.pro
@@ -0,0 +1,258 @@
+pro make_astr,astr, CD=cd, DELTA = cdelt, CRPIX = crpix, CRVAL = crval, $
+                    CTYPE = ctype, LATPOLE = LATPOLE, LONGPOLE = longpole, $ 
+                    PV2 = pv2, NAXIS = naxis, AXES = axes, pv1 = pv1, $
+                    RADECSYS = radecsys, EQUINOX = equinox, $
+                    DATE_OBS = dateobs, MJD_OBS = mjdobs
+;+
+; NAME:
+;       MAKE_ASTR
+; PURPOSE:
+;       Build an astrometry structure from input parameter values
+; EXPLANATION:
+;       This structure can be subsequently placed in a FITS header with 
+;       PUTAST
+;
+; CALLING SEQUENCE:
+;       MAKE_ASTR, astr, CRPIX =, CRVAL =, [CD = , DELT =,  CTYPE =,    $
+;               LATPOLE = , LONGPOLE =, PV2 =, NAXIS =, AXES =, PV1 =,  $
+;               RADECSYS =, EQUINOX =, DATEOBS =, MJDOBS =]
+;
+; OUTPUT PARAMETER:
+;       ASTR - Anonymous structure containing astrometry info.  See the 
+;              documentation for EXTAST for descriptions of the individual
+;              tags
+;
+; REQUIRED INPUT KEYWORDS
+;       CRPIX - 2 element vector giving X and Y coordinates of reference pixel
+;               (def = NAXIS/2).  VALUES MUST BE IN FITS CONVENTION (first pixel
+;               is [1,1]) AND NOT IDL CONVENTION (first pixel is [0,0]).
+;       CRVAL - 2 element double precision vector giving R.A. and DEC of 
+;               reference pixel in DEGREES
+; OPTIONAL INPUT KEYWORDS
+;       CD -  2 x 2 array containing the astrometry parameters CD1_1 CD1_2
+;              in DEGREES/PIXEL                                CD2_1 CD2_2
+;       DELT - 2 element vector giving physical increment at reference pixel
+;              in DEGREES/PIXEL default = [-1.0D, 1.0D]/3600.  (1 arcsec/pixel)
+;       CTYPE - 2 element string vector giving projection types, default
+;              ['RA---TAN','DEC--TAN']
+;       LATPOLE - Scalar latitude of the north pole, default = +90
+;       LONGPOLE - scalar longitude of north pole
+;       PV2 - Vector of projection parameters associated with latitude axis.   
+;             Not required for some projections (e.g. TAN) and optional for 
+;             others (e.g. SIN).
+;             Usually a 2 element vector, but may contain up to 21 elements
+;             for the Zenithal Polynomial (ZPN) projection.   Corresponds to 
+;             the keywords PV2_1, PV2_2...  Defaults to 0.0
+;
+;      Added for version 2 astrometry structure:
+;      AXES  - 2 element integer vector giving the FITS-convention axis 
+;              numbers associated with astrometry, in ascending order. 
+;              Default [1,2].
+;      NAXIS - 2 element integer vector giving number of pixels on each axis
+;      PV1 -  Vector of projection parameters associated with longitude axis
+;             Elements 4 & 5 (if present) are equivalent to LONGPOLE & LATPOLE 
+;             and take precedence if both are specified, i.e. LONGPOLE & LATPOLE
+;             in the structure are forced to agree with PV1.
+;      RADECSYS - String giving RA/Dec system e.g. 'FK4', 'ICRS' etc.
+;      EQUINOX  - Double giving the epoch of the mean equator and equinox
+;      DATEOBS  - Text string giving (start) date/time of observations
+;      MJDOBS   - Modified julian date of start of observations.
+;              (specify one or other of DATEOBS or MJDOBS)
+;
+; NOTES:
+;       (1) An anonymous structure is created to avoid structure definition
+;               conflicts.    This is needed because some projection systems
+;               require additional dimensions (i.e. spherical cube
+;               projections require a specification of the cube face).
+;       (2) The name of the keyword for the CDELT parameter is DELT because
+;               the IDL keyword CDELT would conflict with the CD keyword
+;       (3) The astrometry structure definition was slightly modified in 
+;               July 2003; all angles are now double precision, and the 
+;               LATPOLE tag was added.   In April 2007 the CRPIX tag was also
+;               changed to double precision.
+; REVISION HISTORY:
+;       Written by   W. Landsman              Mar. 1994
+;       Added LATPOLE, all angles double precision  W. Landsman July 2003
+;       Use PV2 keyword rather than PROJP1, PROJP2 W. Landsman May 2004
+;       Make .CRPIX tag double precision, change CDELT default to 1"/pixel
+;                      W. Landsman April 2007
+;        Default plate scale is now 1"/pixel (not 1 deg/pix)  WL  Oct. 2010
+;        Oct 2010 change should only apply when CD matrix not given 
+;                     M. Cushing/W.L.  Aug 2011
+;        added v2 parameters; more filling out of defaults; default 
+;        LATPOLE changed to 90 (FITS standard) J. P. Leahy Jul 2013
+;-
+ On_error, 0
+ compile_opt idl2
+
+ if ( N_params() LT 1 ) then begin
+	print,'Syntax - MAKE_ASTR, astr, CRPIX =, CRVAL =, [CD = , DELT =,  '
+        print,'	       CTYPE =, LATPOLE= , LONGPOLE =, PV2=, NAXIS =, AXES=,'
+        print,'        PV1=, RADECSYS= , EQUINOX=, DATEOBS=, MJDOBS= ]'
+	return
+ endif
+
+;
+; List of known map types copied from wcsxy2sph. Needs to be kept up
+; to date!
+;
+ map_types=['DEF','AZP','TAN','SIN','STG','ARC','ZPN','ZEA','AIR','CYP',$
+            'CAR','MER','CEA','COP','COD','COE','COO','BON','PCO','SFL',$
+            'PAR','AIT','MOL','CSC','QSC','TSC','SZP','HPX','HCT','XPH']
+
+; If neither CD nor CDELT keywords present then assume 1"/pixel
+; If CD supplied but not CDELT then set CDELT = [1.0,1.0] 
+
+ if N_elements( cd ) EQ 0 then begin 
+     cd = [ [1.,0.], [0.,1.] ]
+     if N_elements( cdelt) EQ 0 then cdelt = [-1.0D, 1.0D]/3600.0d
+ endif else if N_elements( cdelt) EQ 0 then cdelt = [1.0D, 1.0D]    
+     
+ if N_elements( crpix) EQ 0 then message, $
+	'ERROR - CRPIX is a required keyword for a new astrometry structure'
+ 
+ if N_elements( crval) EQ 0 then message, $
+	'ERROR - CRVAL is a required keyword for a new astrometry structure'
+
+ if N_elements( ctype)  EQ 0 then ctype = ['RA---TAN','DEC--TAN']
+
+ N_pv2 = N_elements(pv2) 
+ IF N_pv2 EQ 0 then pv2 = 0.0D
+ 
+ if N_elements(axes) EQ 0 then axes = [1,2]
+ 
+ ; Search astrometric axes:
+ lon0 = WHERE(STRMID(ctype,0,5) EQ 'RA---')
+ lon1 = WHERE(STRMID(ctype,1,4) EQ  'LON-')
+ lon2 = WHERE(STRMID(ctype,2,4) EQ   'LN-')
+ lon = [lon0, lon1, lon2]
+ form = [REPLICATE(0,N_ELEMENTS(lon0)),REPLICATE(1,N_ELEMENTS(lon1)), $
+         REPLICATE(2,N_ELEMENTS(lon2))]
+ good = WHERE(lon GE 0, ngood)
+ IF ngood GT 1 THEN MESSAGE, 'Both axis types are longitude!'
+ lon  = ngood EQ 1 ? lon[good] : -1
+ lon_form = ngood EQ 1 ? form[good] : -1 
+
+ lat0 = WHERE(STRMID(ctype,0,5) EQ 'DEC--')
+ lat1 = WHERE(STRMID(ctype,1,4) EQ  'LAT-')
+ lat2 = WHERE(STRMID(ctype,2,4) EQ   'LT-')
+ lat = [lat0, lat1, lat2]
+ form = [REPLICATE(0,N_ELEMENTS(lat0)),REPLICATE(1,N_ELEMENTS(lat1)), $
+         REPLICATE(2,N_ELEMENTS(lat2))]
+ good = WHERE(lat GE 0, ngood)
+ IF ngood GT 1 THEN MESSAGE, 'Both axis types are latitude"
+ lat  = ngood EQ 1 ? lat[good] : -1
+ lat_form = ngood EQ 1 ? form[good] : -1 
+ 
+ badco = lon_form NE lat_form 
+ CASE lon_form OF
+     -1: coord = 'X'  ; unknown type of coordinate
+      0: coord = 'C'  ; celestial coords, i.e. RA/Dec
+      1: BEGIN  ; longitude format is xLON where x = G, E, etc.
+          coord = STRMID(ctype[0],0,1)
+          badco = badco || coord NE STRMID(ctype[1],0,1)
+      END
+      2: BEGIN  ; longitude format is yzLN 
+          coord = STRMID(ctype[0],0,2)
+          badco = badco || coord NE STRMID(ctype[2],0,2)
+      END
+      ELSE: MESSAGE, 'Internal error: unexpected lon_form' 
+ ENDCASE
+ 
+ flip = lat[0] LT lon[0]
+ 
+ proj = STRMID(ctype[0], 5, 3)
+ badco = badco || proj NE STRMID(ctype[1], 5, 3)
+ IF badco THEN MESSAGE, 'ERROR: longitude and latitude coordinate types must match:'
+ 
+ test = WHERE(proj EQ map_types)
+ known = test GE 0
+ 
+ npv1 = N_ELEMENTS(pv1)
+ IF npv1 EQ 5 THEN latpole  = pv1[4]
+ IF npv1 GE 4 THEN longpole = pv1[3]
+ IF npv1 GE 3 THEN theta0 = pv1[2] 
+ IF npv1 GE 2 THEN phi0 = pv1[1] ELSE phi0 = 0
+ IF npv1 GE 2 THEN xyoff = pv1[0] NE 0 ELSE xyoff = 0
+ 
+ IF N_ELEMENTS(latpole) EQ 0 THEN latpole = 90
+ 
+ conic = (proj EQ 'COP') || (proj EQ 'COE') || (proj EQ 'COD') || $
+         (proj EQ 'COO')
+
+ IF conic THEN BEGIN 
+     IF N_pv2 EQ 0 THEN message, $
+     'ERROR -- Specify PV2 for conic projections'
+     theta_a = pv2[0]
+ ENDIF ELSE BEGIN ; Is it a zenithal projection?
+     if (proj EQ 'AZP') || (proj EQ 'SZP') || (proj EQ 'TAN') || $
+        (proj EQ 'STG') || (proj EQ 'SIN') || (proj EQ 'ARC') || $
+        (proj EQ 'ZPN') || (proj EQ 'ZEA') || (proj EQ 'AIR') || $
+        (proj EQ 'XPH') then begin
+         theta_a = 90d0
+     endif else theta_a = 0d0
+ ENDELSE
+     
+ IF N_ELEMENTS(theta0) EQ 0 THEN theta0 = theta_a 
+ 
+ IF N_ELEMENTS(longpole) EQ 0 THEN BEGIN
+     if crval[1] GE theta0 then longpole = 0d0 else longpole = 180d0
+     longpole += phi0
+ ENDIF
+ 
+ pv1 = [xyoff, phi0, theta0, longpole, latpole]
+ 
+ x0y0 = [0d0, 0d0]
+ IF xyoff && (phi0 NE 0d0 || theta0 NE theta_a) THEN BEGIN 
+ ; calculate IWC offsets x_0, y_0
+     WCSSPH2XY, phi0, theta0, x0, y0, CTYPE = ctype, PV2 = pv2
+     x0y0 = [x0, y0]
+ ENDIF 
+ 
+ N_rdsys = N_ELEMENTS(radecsys)
+ IF N_rdsys EQ 0 THEN radecsys = '' ELSE $
+     radecsys = STRUPCASE(STRTRIM(radecsys,2))
+ N_mjd = N_ELEMENTS(mjdobs) 
+ IF N_mjd EQ 0 THEN mjdobs   = !values.D_NAN
+ N_date = N_ELEMENTS(dateobs)
+ IF N_date EQ 0 THEN dateobs  = 'UNKNOWN' ELSE $
+     dateobs = STRUPCASE(STRTRIM(dateobs,2))
+ 
+ IF N_mjd GT 0 && N_date EQ 0 THEN dateobs = date_conv(mjdobs+2400000.5d0,'FITS')
+ IF N_date GT 0 THEN BEGIN
+     dateobs = date_conv(dateobs,'FITS', BAD_DATE=bad_date) ; try to convert to standard format
+     IF ~bad_date THEN BEGIN
+        mjdtest = date_conv(dateobs,'MODIFIED')
+        IF N_mjd EQ 0 THEN mjdobs = mjdtest ELSE $
+            IF ABS(mjdtest - mjdobs) GT 1 THEN MESSAGE, $
+               'DATE-OBS and MJD-OBS are inconsistent'
+     ENDIF ELSE dateobs = 'UNKNOWN'
+ ENDIF
+
+ N_Eq = N_ELEMENTS(equinox)
+ IF N_Eq EQ 0 THEN equinox = !values.D_NAN
+ IF (coord EQ 'C' || coord EQ 'E' || coord EQ 'H') THEN BEGIN
+     IF N_rdsys EQ 0 THEN BEGIN
+         IF N_eq EQ 0 THEN radecsys = 'ICRS' $
+         ELSE radecsys = equinox GE 1984d0 ? 'FK5' : 'FK4'
+     ENDIF ELSE IF N_eq EQ 0 THEN CASE STRMID(radecsys,0,3) OF
+           'FK4': equinox = 1950d0
+           'FK5': equinox = 2000d0
+           'ICR': equinox = 2000d0
+           ELSE: equinox = 0d0
+     ENDCASE
+ ENDIF
+ 
+ IF N_ELEMENTS(naxis) NE 2 THEN naxis = [0,0]
+ 
+ ASTR = {NAXIS:naxis, CD: cd, CDELT: cdelt, CRPIX: crpix, CRVAL: crval, $
+         CTYPE: string(ctype), $
+         LONGPOLE: double( longpole[0]),  LATPOLE: double(latpole[0]), $
+         PV2: pv2, PV1: pv1, $
+         AXES: axes, REVERSE: flip, $
+         COORD_SYS: coord, PROJECTION: proj, KNOWN: known, $
+         RADECSYS: radecsys, EQUINOX: DOUBLE(equinox), $
+         DATEOBS: dateobs, MJDOBS: DOUBLE(mjdobs), X0Y0: x0y0}
+ 
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/match.pro b/Code/script_idl_mv/astrolib/match.pro
new file mode 100644
index 0000000000000000000000000000000000000000..af66c90bc8da63d6077d86d956d017b1590b007c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/match.pro
@@ -0,0 +1,170 @@
+pro match, a, b, suba, subb, COUNT = count, SORT = sort, epsilon=epsilon
+;+
+; NAME:
+;       MATCH
+; PURPOSE:
+;       Routine to match values in two vectors.
+;
+; CALLING SEQUENCE:
+;       match, a, b, suba, subb, [ COUNT =, /SORT, EPSILON =  ]
+;
+; INPUTS:
+;       a,b - two vectors to match elements, numeric or string data types
+;
+; OUTPUTS:
+;       suba - subscripts of elements in vector a with a match
+;               in vector b
+;       subb - subscripts of the positions of the elements in
+;               vector b with matchs in vector a.
+;
+;       suba and subb are ordered such that a[suba] equals b[subb]
+;
+; OPTIONAL INPUT KEYWORD:
+;       /SORT - By default, MATCH uses two different algorithm: (1) the
+;               /REVERSE_INDICES keyword to HISTOGRAM is used for integer data,
+;               while (2) a sorting algorithm is used for non-integer data.  The
+;               histogram algorithm is usually faster, except when the input
+;               vectors are sparse and contain very large numbers, possibly
+;               causing memory problems.   Use the /SORT keyword to always use
+;               the sort algorithm.
+;       epsilon - if values are within epsilon, they are considered equal. Used only
+;               only for non-integer matching.  Note that input vectors should 
+;               be unique to within epsilon to provide one-to-one mapping.. 
+;               Default=0.
+;
+; OPTIONAL KEYWORD OUTPUT:
+;       COUNT - set to the number of matches, integer scalar
+;
+; SIDE EFFECTS:
+;       The obsolete system variable !ERR is set to the number of matches;
+;       however, the use !ERR is deprecated in favor of the COUNT keyword
+;
+; RESTRICTIONS:
+;       The vectors a and b should not have duplicate values within them.
+;       You can use rem_dup function to remove duplicate values
+;       in a vector
+;
+; EXAMPLE:
+;       If a = [3,5,7,9,11]   & b = [5,6,7,8,9,10]
+;       then
+;               IDL> match, a, b, suba, subb, COUNT = count
+;
+;       will give suba = [1,2,3], subb = [0,2,4],  COUNT = 3
+;       and       a[suba] = b[subb] = [5,7,9]
+;
+;
+; METHOD:
+;       For non-integer data types, the two input vectors are combined and
+;       sorted and the consecutive equal elements are identified.   For integer
+;       data types, the /REVERSE_INDICES keyword to HISTOGRAM of each array
+;       is used to identify where the two arrays have elements in common.
+; HISTORY:
+;       D. Lindler  Mar. 1986.
+;       Fixed "indgen" call for very large arrays   W. Landsman  Sep 1991
+;       Added COUNT keyword    W. Landsman   Sep. 1992
+;       Fixed case where single element array supplied   W. Landsman Aug 95
+;       Use a HISTOGRAM algorithm for integer vector inputs for improved
+;             performance                W. Landsman         March 2000
+;       Work again for strings           W. Landsman         April 2000
+;       Use size(/type)                  W. Landsman         December 2002
+;       Work for scalar integer input    W. Landsman         June 2003
+;       Assume since V5.4, use COMPLEMENT to WHERE() W. Landsman Apr 2006
+;       Added epsilon keyword            Kim Tolbert         March 14, 2008
+;-
+;-------------------------------------------------------------------------
+ On_error,2
+ compile_opt idl2
+
+ if N_elements(epsilon) EQ 0 then epsilon = 0
+
+ if N_params() LT 3 then begin
+     print,'Syntax - match, a, b, suba, subb, [ COUNT =, EPSILON=, /SORT]'
+     print,'    a,b -- input vectors for which to match elements'
+     print,'    suba,subb -- output subscript vectors of matched elements'
+     return
+ endif
+
+ da = size(a,/type) & db =size(b,/type)
+ if keyword_set(sort) then hist = 0b else $
+ hist = (( da LE 3 ) || (da GE 12)) &&  ((db LE 3) || (db GE 12 ))
+
+ if ~hist then begin           ;Non-integer calculation
+
+ na = N_elements(a)              ;number of elements in a
+ nb = N_elements(b)             ;number of elements in b
+
+; Check for a single element array
+
+ if (na EQ 1) || (nb EQ 1) then begin
+        if (nb GT 1) then begin
+                subb = where(b EQ a[0], nw)
+                if (nw GT 0) then suba = replicate(0,nw) else suba = [-1]
+        endif else begin
+                suba = where(a EQ b[0], nw)
+                if (nw GT 0) then subb = replicate(0,nw) else subb = [-1]
+        endelse
+        count = nw
+        return
+ endif
+
+ c = [ a, b ]                   ;combined list of a and b
+ ind = [ lindgen(na), lindgen(nb) ]       ;combined list of indices
+ vec = [ bytarr(na), replicate(1b,nb) ]  ;flag of which vector in  combined
+                                         ;list   0 - a   1 - b
+
+; sort combined list
+
+ sub = sort(c)
+ c = c[sub]
+ ind = ind[sub]
+ vec = vec[sub]
+
+; find duplicates in sorted combined list
+
+ n = na + nb                            ;total elements in c
+ if epsilon eq 0. then $
+    firstdup = where( (c EQ shift(c,-1)) and (vec NE shift(vec,-1)), Count ) $
+ else $
+    firstdup = where( (abs(c - shift(c,-1)) lt epsilon) and (vec NE shift(vec,-1)), Count )
+
+ if Count EQ 0 then begin               ;any found?
+        suba = lonarr(1)-1
+        subb = lonarr(1)-1
+        return
+ end
+
+ dup = lonarr( Count*2 )                     ;both duplicate values
+ even = lindgen( N_elements(firstdup))*2     ;Changed to LINDGEN 6-Sep-1991
+ dup[even] = firstdup
+ dup[even+1] = firstdup+1
+ ind = ind[dup]                         ;indices of duplicates
+ vec = vec[dup]                         ;vector id of duplicates
+ subb = ind[ where( vec, complement = vzero) ]             ;b subscripts
+ suba = ind[ vzero]
+
+ endif else begin             ;Integer calculation using histogram.
+
+ minab = min(a, MAX=maxa) > min(b, MAX=maxb) ;Only need intersection of ranges
+ maxab = maxa < maxb
+
+;If either set is empty, or their ranges don't intersect:
+;  result = NULL (which is denoted by integer = -1)
+  !ERR = -1
+  suba = -1
+  subb = -1
+  COUNT = 0L
+ if (maxab lt minab) || (maxab lt 0) then return
+
+ ha = histogram([a], MIN=minab, MAX=maxab, reverse_indices=reva)
+ hb = histogram([b], MIN=minab, MAX=maxab, reverse_indices=revb)
+
+ r = where((ha ne 0) and (hb ne 0), count)
+ if count gt 0 then begin
+  suba = reva[reva[r]]
+  subb = revb[revb[r]]
+ endif
+ endelse
+
+ return
+
+ end
diff --git a/Code/script_idl_mv/astrolib/match2.pro b/Code/script_idl_mv/astrolib/match2.pro
new file mode 100644
index 0000000000000000000000000000000000000000..16b33cea8f4f349fa1ae14eda65312797d3f9c1a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/match2.pro
@@ -0,0 +1,169 @@
+;+
+; NAME:
+;       MATCH2
+; PURPOSE:
+;       Routine to cross-match values in two vectors (including non-matches)
+; EXPLANATION:
+;       MATCH2 reports matching elements of two arrays.
+
+;       This procedure *appears* similar to MATCH of the IDL astronomy
+;       library.  However, this routine is quite different in that it
+;       reports an index value for each element of the input arrays.
+;       In other words, while MATCH reports the *existence* of
+;       matching elements in each array, MATCH2 reports explicitly
+;       *which* elements match.
+;
+;       Furthermore, while MATCH reports only unique matching
+;       elements, MATCH2 will always report a cross-match for every
+;       element in each array, even if it is a repeat.
+;
+;       In cases where no match was found, an index of -1 is
+;       reported.  
+;
+; CALLING SEQUENCE:
+;       match2, a, b, suba, subb
+;
+; INPUTS:
+;       a,b - two vectors to match elements, numeric or string data
+;             types.  (See below for RESTRICTIONS on A and B)
+;
+;
+; OUTPUTS:
+;       suba - vector with same number of elements as A, such that
+;              A EQ B[SUBA], except non-matches which are indicated
+;              by SUBA EQ -1
+;       subb - vector with same number of elements as B, such that
+;              B EQ A[SUBB], except non-matches which are indicated
+;              by SUBB EQ -1
+;
+;
+; RESTRICTIONS:
+; 
+;       The vectors A and B are allowed to have duplicates in them,
+;       but for matching purposes, only the first one found will
+;       be reported.
+;
+;       If A and B are string arrays, then non-printable ASCII values
+;       1B and 2B will confuse the algorithm.  Don't use these
+;       non-printable characters in strings.
+;
+; EXAMPLE:
+;      A = [0,7,14,23,24,30]
+;      B = [7,8,14,25,14]
+;      IDL> match2, a, b, suba, subb
+;     --> suba = [ -1 ,  0,  4,  -1, -1, -1 ]
+;     (indicates that A[1] matches B[1] and A[3] matches B[2])
+;     --> subb = [  1 , -1,  2,  -1,  2 ]
+;     (indicates that B[1] matches A[1] and B[2] matches A[3])
+;
+;  Compare to the results of the original MATCH procedure,
+;    
+;      IDL> match, a, b, suba, subb
+;     --> suba = [  1,  3]
+;  (indicates that A[1] and A[3] match elements in B, but not which ones)
+;     --> subb = [  1,  2]
+;  (indicates that B[1] and B[2] match elements in A, but not which ones)
+;
+; MODIFICATION HISTORY
+;   Derived from the IDL Astronomy Library MATCH, 14 Feb 2007
+;   Updated documentation, 17 Jul 2007
+;   More updated documentation (example), 03 Sep 2007
+;   Bug fix for string arrays with numerical contents; the subset
+;   string is now 1B and 2B; this is now documented, 2014-10-20 CM
+;   
+; 
+;-
+;-------------------------------------------------------------------------
+pro match2, a, b, suba, subb
+
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 3 then begin
+     print,'Syntax - match2, a, b, suba, subb'
+     print,'    a,b -- input vectors for which to match elements'
+     print,'    suba,subb -- match index lists'
+     return
+ endif
+
+ da = size(a,/type) & db =size(b,/type)
+ 
+ na = N_elements(a)              ;number of elements in a
+ nb = N_elements(b)             ;number of elements in b
+ suba = lonarr(na)-1 & subb = lonarr(nb)-1
+
+; Check for a single element array
+
+ if (na EQ 1) or (nb EQ 1) then begin
+        if (nb GT 1) then begin
+            wh = where(b EQ a[0], nw)
+            if nw GT 0 then begin
+                subb[wh] = 0L
+                suba[0]  = wh[0]
+            endif
+        endif else begin
+            wh = where(a EQ b[0], nw)
+            if nw GT 0 then begin
+                suba[wh] = 0L
+                subb[0]  = wh[0]
+            endif
+        endelse
+        return
+ endif
+        
+ c = [ a, b ]                   ;combined list of a and b
+ ind = [ lindgen(na), lindgen(nb) ]       ;combined list of indices
+ vec = [ intarr(na), replicate(1,nb) ]  ;flag of which vector in  combined 
+                                         ;list   0 - a   1 - b
+
+; sort combined list
+
+ if da EQ 7 OR db EQ 7 then begin
+     vecstr = [string(1b), string(2b)]
+     ;; String sort (w/ double key)
+     sub = sort(c+vecstr[vec])
+ endif else begin
+     ;; Number sort (w/ double key)
+     eps = (machar(/double)).eps
+     sub = sort(double(c)*(1d + vec*eps))
+ endelse
+
+ c = c[sub]
+ ind = ind[sub]
+ vec = vec[sub]
+ 
+ n = na + nb                    ;total elements in c
+ wh = where( c[1:*] NE c, ct)
+ if ct EQ 0 then begin
+     whfirst = [0]
+     whlast  = [n-1]
+ endif else begin
+     whfirst = [0, wh+1]
+     whlast  = [wh, n-1]
+ endelse
+ 
+ vec0 = vec[whfirst]
+ vec1 = vec[whlast]
+ ;; 0 = present in A but not B
+ ;; 1 = can't occur (since the array was sorted on 'VEC')
+ ;; 2 = present in both
+ ;; 3 = present in B but not A
+ matchtype = vec0 + vec1*2
+
+ nm = n_elements(matchtype)
+ mm = ind*0L & wa = mm & wb = mm
+ for i = 0, nm-1 do begin
+     mm[whfirst[i]:whlast[i]] = matchtype[i]
+     wa[whfirst[i]:whlast[i]] = ind[whfirst[i]]
+     wb[whfirst[i]:whlast[i]] = ind[whlast[i]]
+ endfor
+
+ suba = lonarr(na)-1 & subb = lonarr(nb)-1
+
+ wh = where(mm EQ 2 AND vec EQ 0, ct)
+ if ct GT 0 then suba[ind[wh]] = wb[wh]
+ wh = where(mm EQ 2 AND vec EQ 1, ct)
+ if ct GT 0 then subb[ind[wh]] = wa[wh]
+
+ return
+end
diff --git a/Code/script_idl_mv/astrolib/max_entropy.pro b/Code/script_idl_mv/astrolib/max_entropy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4c99ea30db7f19fda285d90e467d81b24fe11566
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/max_entropy.pro
@@ -0,0 +1,79 @@
+;+
+; NAME:
+;	MAX_ENTROPY
+;
+; PURPOSE:
+;	Deconvolution of data by Maximum Entropy analysis, given the PSF
+; EXPLANATION:
+;	Deconvolution of data by Maximum Entropy analysis, given the 
+;	instrument point spread response function (spatially invariant psf).
+;	Data can be an observed image or spectrum, result is always positive.
+;	Default is convolutions using FFT (faster when image size = power of 2).
+;
+; CALLING SEQUENCE:
+;	for i=1,Niter do begin
+;	Max_Entropy, image_data, psf, image_deconv, multipliers, FT_PSF=psf_ft
+;
+; INPUTS:
+;	data = observed image or spectrum, should be mostly positive,
+;					with mean sky (background) near zero.
+;	psf = Point Spread Function of instrument (response to point source,
+;							must sum to unity).
+;	deconv = result of previous call to Max_Entropy,
+;	multipliers = the Lagrange multipliers of max.entropy theory
+;		(on first call, set = 0, giving flat first result).
+;
+; OUTPUTS:
+;	deconv = deconvolution result of one more iteration by Max_Entropy.
+;	multipliers = the Lagrange multipliers saved for next iteration.
+;
+; OPTIONAL INPUT KEYWORDS:
+;	FT_PSF = passes (out/in) the Fourier transform of the PSF,
+;		so that it can be reused for the next time procedure is called,
+;      /NO_FT overrides the use of FFT, using the IDL function convol() instead.
+;      /LINEAR switches to Linear convergence mode, much slower than the
+;		default Logarithmic convergence mode.
+;	LOGMIN = minimum value constraint for taking Logarithms (default=1.e-9).
+; EXTERNAL CALLS:
+;	function convolve( image, psf ) for convolutions using FFT or otherwise.
+; METHOD:
+;	Iteration with PSF to maximize entropy of solution image with
+;	constraint that the solution convolved with PSF fits data image.
+;	Based on paper by Hollis, Dorband, Yusef-Zadeh, Ap.J. Feb.1992,
+;	which refers to Agmon, Alhassid, Levine, J.Comp.Phys. 1979.
+;
+;       A more elaborate image deconvolution program using maximum entropy is 
+;       available at 
+;       http://sohowww.nascom.nasa.gov/solarsoft/gen/idl/image/image_deconvolve.pro
+; HISTORY:  
+;	written by Frank Varosi at NASA/GSFC, 1992.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+pro max_entropy, data, psf, deconv, multipliers, FT_PSF=psf_ft, NO_FT=noft, $
+			LINEAR=Linear, LOGMIN=Logmin, RE_CONVOL_IMAGE=Re_conv
+
+	if N_elements( multipliers ) LE 1 then begin
+		multipliers = data
+		multipliers[*] = 0
+	   endif
+
+	deconv = exp( convolve( multipliers, psf, FT_PSF=psf_ft, $
+						 /CORREL, NO_FT=noft ) )
+	totd = total( data )
+	deconv = deconv * ( totd/total( deconv ) )
+
+	Re_conv = convolve( deconv, psf, FT_PSF=psf_ft, NO_FT=noft )
+	scale = total( Re_conv )/totd
+
+	if keyword_set( Linear ) then begin
+
+		multipliers = multipliers + (data * scale - Re_conv)
+
+	  endif else begin
+
+		if N_elements( Logmin ) NE 1 then Logmin=1.e-9
+		multipliers = multipliers + $
+			aLog( ( ( data * scale )>Logmin ) / (Re_conv>Logmin) )
+	   endelse
+end
diff --git a/Code/script_idl_mv/astrolib/max_likelihood.pro b/Code/script_idl_mv/astrolib/max_likelihood.pro
new file mode 100644
index 0000000000000000000000000000000000000000..11e82804bd804eafa5abb6076908471449e68f2a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/max_likelihood.pro
@@ -0,0 +1,93 @@
+;+
+; NAME:
+;	MAX_LIKELIHOOD
+;
+; PURPOSE:
+;	Maximum likelihood deconvolution of an image or a spectrum.
+; EXPLANATION:
+; 	Deconvolution of an observed image (or spectrum) given the 
+;	instrument point spread response function (spatially invariant psf).
+;	Performs iteration based on the Maximum Likelihood solution for
+;	the restoration of a blurred image (or spectrum) with additive noise.
+;	Maximum Likelihood formulation can assume Poisson noise statistics
+;	or Gaussian additive noise, yielding two types of iteration.
+;
+; CALLING SEQUENCE:
+;	for i=1,Niter do Max_Likelihood, data, psf, deconv, FT_PSF=psf_ft
+;
+; INPUTS PARAMETERS:
+;	data = observed image or spectrum, should be mostly positive,
+;				with mean sky (background) near zero.
+;	psf = Point Spread Function of the observing instrument,
+;				(response to a point source, must sum to unity).
+; INPUT/OUTPUT PARAMETERS:
+;	deconv = as input: the result of previous call to Max_Likelihood,
+;		(initial guess on first call, default = average of data),
+;		as output: result of one more iteration by Max_Likelihood.
+;	Re_conv = (optional) the current deconv image reconvolved with PSF
+;		for use in next iteration and to check convergence.
+;
+; OPTIONAL INPUT KEYWORDS:
+;      /GAUSSIAN causes max-likelihood iteration for Gaussian additive noise
+;		to be used,  otherwise the default is Poisson statistics.
+;	FT_PSF = passes (out/in) the Fourier transform of the PSF,
+;		so that it can be reused for the next time procedure is called,
+;      /NO_FT overrides the use of FFT, using the IDL function convol() instead.
+;	POSITIVITY_EPS = value of epsilon passed to function positivity,
+;			default = -1 which means no action (identity).
+;	UNDERFLOW_ZERO = cutoff to consider as zero, if numbers less than this.
+;
+; EXTERNAL CALLS:
+;	function convolve( image, psf ) for convolutions using FFT or otherwise.
+;	function positivity( image, EPS= ) to make image positive.
+;
+; METHOD:
+;	Maximum Likelihood solution is a fixed point of an iterative eq.
+;	(derived by setting partial derivatives of Log(Likelihood) to zero).
+;	Poisson noise case was derived by Richardson(1972) & Lucy(1974).
+;	Gaussian noise case is similar with subtraction instead of division.
+; NOTES:
+;       WARNING: The Poisson case may not conserve flux for an odd image size.  
+;       This behavior is being investigated.
+; HISTORY:
+;	written: Frank Varosi at NASA/GSFC, 1992.
+;	F.V. 1993, added optional arg. Re_conv (to avoid doing it twice).
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Use COMPLEMENT keyword to WHERE()   W. Landsman  Jan 2008
+;-
+
+pro Max_Likelihood, data, psf, deconv, Re_conv, FT_PSF=psf_ft, NO_FT=noft, $
+						GAUSSIAN=gaussian, $
+						POSITIVITY_EPS=epsilon, $
+						UNDERFLOW_ZERO=under
+        compile_opt idl2
+	if N_elements( deconv ) NE N_elements( data ) then begin
+		deconv = data
+		deconv[*] = total( data )/N_elements( data )
+		Re_conv = 0
+	   endif
+
+	if N_elements( under ) NE 1 then under = 1.e-22
+	if N_elements( epsilon ) NE 1 then epsilon = -1
+	if N_elements( Re_conv ) NE N_elements( deconv ) then $
+		Re_conv = convolve( positivity( deconv, EPS=epsilon ), psf, $
+						  FT_PSF=psf_ft, NO_FT=noft )
+	if keyword_set( gaussian ) then begin
+
+		deconv = deconv + convolve( data - Re_conv, psf, /CORREL, $
+						   FT_PSF=psf_ft, NO_FT=noft )
+	  endif else begin
+		wp = where( Re_conv GT under, npos, $
+		     ncomplement=nneg,complement=wz)
+              
+		if (npos GT 0) then Re_conv[wp] = ( data[wp]/Re_conv[wp] ) > 0
+		if (nneg GT 0) then Re_conv[wz] = 1.
+		deconv = deconv * convolve( Re_conv, psf, FT_PSF=psf_ft, $
+							/CORREL, NO_FT=noft )
+	   endelse
+	   
+	if N_params() GE 4 then $
+		Re_conv = convolve( positivity( deconv, EPS=epsilon ), psf, $
+						FT_PSF = psf_ft, NO_FT = noft )
+						
+ end
diff --git a/Code/script_idl_mv/astrolib/meanclip.pro b/Code/script_idl_mv/astrolib/meanclip.pro
new file mode 100644
index 0000000000000000000000000000000000000000..995011c08c277caa37f6e5327ccaf1b51935c25a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/meanclip.pro
@@ -0,0 +1,86 @@
+PRO MEANCLIP, Image, Mean, Sigma, CLIPSIG=clipsig, MAXITER=maxiter, $
+    CONVERGE_NUM=converge_num, VERBOSE=verbose, SUBS=subs,DOUBLE=double
+;+
+; NAME:
+;       MEANCLIP
+;
+; PURPOSE:
+;       Computes an iteratively sigma-clipped mean on a data set
+; EXPLANATION:
+;       Clipping is done about median, but mean is returned.
+;       Called by SKYADJ_CUBE
+;
+; CATEGORY:
+;       Statistics
+;
+; CALLING SEQUENCE:
+;       MEANCLIP, Data, Mean, [ Sigma, SUBS =
+;              CLIPSIG=, MAXITER=, CONVERGE_NUM=, /VERBOSE, /DOUBLE ]
+;
+; INPUT POSITIONAL PARAMETERS:
+;       Data:     Input data, any numeric array
+;       
+; OUTPUT POSITIONAL PARAMETERS:
+;       Mean:     N-sigma clipped mean.
+;       Sigma:    Standard deviation of remaining pixels.
+;
+; INPUT KEYWORD PARAMETERS:
+;       CLIPSIG:  Number of sigma at which to clip.  Default=3
+;       MAXITER:  Ceiling on number of clipping iterations.  Default=5
+;       CONVERGE_NUM:  If the proportion of rejected pixels is less
+;           than this fraction, the iterations stop.  Default=0.02, i.e.,
+;           iteration stops if fewer than 2% of pixels excluded.
+;       /VERBOSE:  Set this flag to get messages.
+;       /DOUBLE - if set then perform all computations in double precision.
+;                 Otherwise double precision is used only if the input
+;                 data is double
+; OUTPUT KEYWORD PARAMETER:
+;       SUBS:     Subscript array for pixels finally used.
+;
+;
+; MODIFICATION HISTORY:
+;       Written by:     RSH, RITSS, 21 Oct 98
+;       20 Jan 99 - Added SUBS, fixed misplaced paren on float call, 
+;                   improved doc.  RSH
+;       Nov 2005   Added /DOUBLE keyword, check if all pixels are removed  
+;                  by clipping W. Landsman 
+;-
+
+IF N_params() LT 1 THEN BEGIN
+    print, 'CALLING SEQUENCE:  MEANCLIP, Image, Mean, Sigma'
+    print, 'KEYWORD PARAMETERS:  CLIPSIG[=3], MAXITER[=5], CONVERGE_NUM[=0.02], ' $
+        + '/VERBOSE, SUBS, /DOUBLE'
+    RETURN
+ENDIF
+
+prf = 'MEANCLIP:  '
+
+verbose = keyword_set(verbose)
+IF n_elements(maxiter) LT 1 THEN maxiter = 5
+IF n_elements(clipsig) LT 1 THEN clipsig = 3
+IF n_elements(converge_num) LT 1 THEN converge_num = 0.02
+
+subs = where(finite(image),ct)
+iter=0
+REPEAT BEGIN
+    skpix = image[subs]
+    iter = iter + 1
+    lastct = ct
+    medval = median(skpix)
+    mom = moment(skpix,max=2,double=double)
+    sig = sqrt(mom[1])
+    wsm = where(abs(skpix-medval) LT clipsig*sig,ct)
+    IF ct GT 0 THEN subs = subs[wsm]         
+ENDREP UNTIL (float(abs(ct-lastct))/lastct LE converge_num) $
+          OR (iter GT maxiter) or (ct EQ 0)
+mom = moment(image[subs],double=double,max=2)
+mean = mom[0]
+sigma = sqrt(mom[1])
+IF verbose THEN BEGIN
+    print, prf+strn(clipsig)+'-sigma clipped mean'
+    print, prf+'Mean computed in ',iter,' iterations'
+    print, prf+'Mean = ',mean,',  sigma = ',sigma
+ENDIF
+
+RETURN
+END
diff --git a/Code/script_idl_mv/astrolib/medarr.pro b/Code/script_idl_mv/astrolib/medarr.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a3f0b7be35989a1b5cbe032e10084802f6a5a885
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/medarr.pro
@@ -0,0 +1,132 @@
+PRO medarr, inarr, outarr, mask, output_mask
+;+
+; NAME:
+;       MEDARR
+; PURPOSE:
+;       Compute the median at each pixel across a set of 2-d images
+; EXPLANATION:
+;       Each pixel in the output array contains  the median of the 
+;       corresponding pixels in the input arrays.   Useful, for example to 
+;       combine a stack of CCD images, while removing cosmic ray hits.
+;
+;       This routine has been mostly obsolete since V5.6 with the introduction
+;       of the DIMENSION keyword to the intrinsic MEDIAN() function.   However,
+;       it is  still useful for integer images if bad pixels need to be flagged
+;       in a mask parameter.  (For floating point images, it is much 
+;       faster to set invalid pixels to NaN values.)
+; CALLING SEQUENCE:
+;       MEDARR, inarr, outarr, [ mask, output_mask ]
+; INPUTS:
+;       inarr  -- A three dimensional array [Nx,Ny, N] containing the input 
+;                images.    Each image is size Nx by Ny, and there are N
+;                images.    
+;
+; OPTIONAL INPUT:
+;       mask   -- Same structure as inarr, byte array with 1b where
+;                 pixels are to be included, 0b where they are to be
+;                 excluded.    For floating point images, it is much faster to 
+;                 set masked pixels in inarr equal to !VALUES.F_NAN (see below),
+;                 rather than use the mask parameter.
+;                
+; OUTPUTS:
+;       outarr -- The output array.  It will have dimensions equal to the
+;                 first two dimensions of the input array.
+;
+; OPTIONAL OUPUT:
+;       output_mask -- Same structure as outarr, byte array with 1b where
+;                      pixels are valid, 0b where all the input pixels
+;                      have been masked out.
+; RESTRICTIONS:
+;        This procedure is *SLOW* when using the Mask parameter because it has
+;        to loop over  each pixel of the image.  
+;
+; EXAMPLE:
+;       Suppose one wants to combine three floating point 1024 x 1024 bias 
+;       frames which have been read into the IDL variables im1,im2,im3
+;
+;       IDL> bigim = fltarr(1024,1024,3)        ;Create big array to hold images
+;       IDL> bigim[0,0,0] = im1 & bigim[0,0,1] = im2 & bigim[0,0,2] = im2  
+;       IDL> medarr, bigim, avgbias
+;
+;       The variable avgbias will be the desired 1024x 1024 float image.
+; PROCEDURE:
+;       If the MASK parameter is not set, then MEDARR is just a wrapper for 
+;       MEDIAN(/EVEN, dimension = 3).    If the MASK parameter is set,
+;       a scalar median function over the third dimension is looped over 
+;       each pixel of the first two dimensions.   The /EVEN keyword is used
+;       with MEDIAN (which averages the two middle values), since this avoids 
+;       biasing the output for an even number of images.
+;
+;       Any values set to NAN (not a number) are ignored when computing the
+;       median.    If all values for a pixel location are NAN, then the median
+;       is also returned as NAN.
+;
+; MODIFICATION HISTORY:
+;       Written by Michael R. Greason, STX, 12 June 1990.
+;       Don't use MEDIAN function for even number of images.
+;          W. Landsman Sep 1996
+;       Mask added.  RS Hill, HSTX, 13 Mar. 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use /EVEN keyword to MEDIAN    W. Landsman  September 1997
+;       Rearranged code for faster execution   W. Landsman January 1998
+;       Faster execution for odd number of images   W. Landsman July 2000
+;       V5.4 fix for change in SIZE() definition of undefined variable 
+;                W. Landsman/E. Young   May 2001
+;       Use MEDIAN(/DIMEN) for V5.6 or later   W. Landsman   November 2002
+;       Use keyword_set() instead of ARG_present() to test for presence of mask
+;           parameter  D. Hanish/W. Landsman   June 2003
+;       Assume since V5.6  W. Landsman  Feb 2004
+; 
+;-
+ On_error,2
+;                       Check parameters.
+
+ if N_params() LT 2 then begin                  ; # parameters.
+        print, "Syntax -  MEDARR, inputarr, outputarr [, maskarr, output_mask]"
+        return
+ endif
+ 
+ s = size(inarr)
+ if s[0] NE 3 then $                    ; Input array size.
+        message, "Input array must have 3 dimensions"
+ if (N_elements(mask) EQ 0) then begin
+        outarr = median(inarr,dimension=3,/even)
+        return
+ endif
+
+;                       Create the output array.
+ ncol = s[1]
+ nrow = s[2]
+ narr = s[3]
+ type = s[s[0] + 1]
+ outarr = make_array( dimen = [ncol,nrow], /NOZERO, TYPE = type )
+ if arg_present(output_mask) then $
+     output_mask = make_array (dimen = [ncol,nrow], VALUE = 1b)
+
+;                       Combine the input arrays into the output array.
+
+  sm = size(mask)
+  if N_elements(mask) LT 4 then $ 
+	 message,'Input mask not valid... must have 3 dimensions'
+  if array_equal(sm[0:3], s[0:3] )  then $	 
+     mask_given = 1b  $
+     else message,'Mask not valid... must be same shape as input cube.'
+
+ for j = 0l, (nrow-1) do begin    
+        for i = 0l, (ncol-1) do begin
+                good_pixels = 1b   
+                       wmask = where(mask[i,j,*],cwm)
+                       if cwm gt 0 then begin
+                          marr = inarr[i,j,wmask] 
+                       endif else begin
+                          good_pixels = 0b
+                          output_mask[i,j] = 0b
+                       endelse
+  
+                if good_pixels then outarr[i,j] = median(marr,/EVEN)
+          
+        endfor
+ endfor
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/medsmooth.pro b/Code/script_idl_mv/astrolib/medsmooth.pro
new file mode 100644
index 0000000000000000000000000000000000000000..15d95931259d69444f1a5fd280b557fe1865aa32
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/medsmooth.pro
@@ -0,0 +1,71 @@
+FUNCTION MEDSMOOTH,ARRAY,WINDOW
+;+
+; NAME:
+;       MEDSMOOTH
+;
+; PURPOSE:
+;       Median smoothing of a vector, including points near its ends.
+;
+; CALLING SEQUENCE:
+;       SMOOTHED = MEDSMOOTH( VECTOR, WINDOW_WIDTH )
+;
+; INPUTS:
+;       VECTOR  = The (1-d numeric) vector to be smoothed
+;       WINDOW = Odd integer giving the full width of the window over which 
+;               the median is determined for each point.     (If WINDOW is
+;               specified as an even number, then the effect is the same as
+;               using WINDOW+1)   
+;
+; OUTPUT:
+;       Function returns the smoothed vector
+;
+; PROCEDURE:
+;       Each point is replaced by the median of the nearest WINDOW of points.
+;       The width of the window shrinks towards the ends of the vector, so that
+;       only the first and last points are not filtered. These points are 
+;       replaced by forecasting from smoothed interior points.
+;
+; EXAMPLE:
+;       Create a vector with isolated high points near its ends
+;       IDL> a = randomn(seed,40) & a[1] = 10  & a[38] = 10
+;       Now do median smoothing with a 7 point window 
+;       IDL> b = medsmooth(a,7)
+;       Note that, unlike MEDIAN(), that MEDSMOOTH will remove the isolated
+;       high points near the ends.
+; REVISION HISTORY:
+;       Written, H. Freudenreich, STX, 12/89
+;       H.Freudenreich, 8/90: took care of end-points by shrinking window.
+;       Speed up using vector median when possible  W. Landsman February 2002
+;-
+
+ LEND = N_ELEMENTS(ARRAY)-1
+ IF (LEND+1) LT WINDOW THEN BEGIN
+   message,/CON, $
+         'ERROR - Size of smoothing window must be smaller than array size'
+   RETURN,ARRAY
+ ENDIF
+
+ OFFSET = FIX(WINDOW/2)
+
+ smoothed = median(array, window )
+
+; Fix the ends:
+ NUMLOOP = (WINDOW-1)/2 - 1
+ IF NUMLOOP GT 0 THEN BEGIN
+   FOR J=1,NUMLOOP DO BEGIN 
+
+     LEN = 2*J+1
+     SMOOTHED[J] = MEDIAN(ARRAY[0:LEN-1])
+     SMOOTHED[LEND-J] =  MEDIAN(ARRAY[LEND-LEN+1:LEND]) 
+
+   ENDFOR
+ENDIF
+
+; Now replace the very last and first points:
+ Y0 = 3.*ARRAY[0]-2.*ARRAY[1]         ; Predicted value of point -1
+ SMOOTHED[0] = MEDIAN([Y0,ARRAY[0],ARRAY[1]])
+ Y0 = 3.*ARRAY[LEND]-2.*ARRAY[LEND-1] ; Predicted value of point LEND+1
+ SMOOTHED[LEND] = MEDIAN([Y0,ARRAY[LEND],ARRAY[LEND-1]])
+               
+ RETURN,SMOOTHED
+ END
diff --git a/Code/script_idl_mv/astrolib/minf_bracket.pro b/Code/script_idl_mv/astrolib/minf_bracket.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a0de52c5866aa996ee8002fec02d96d941dc69da
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/minf_bracket.pro
@@ -0,0 +1,130 @@
+pro minF_bracket, xa,xb,xc, fa,fb,fc, FUNC_NAME=func_name, $
+                                                POINT_NDIM=pn, DIRECTION=dirn
+;+
+; NAME:
+;       MINF_BRACKET
+; PURPOSE:
+;       Bracket a local minimum of a 1-D function with 3 points,
+; EXPLANATION:
+;       Brackets a local minimum of a 1-d function with 3 points,
+;       thus ensuring that a minimum exists somewhere in the interval.
+;       This routine assumes that the function has a minimum somewhere....
+;       Routine can also be applied to a scalar function of many variables,
+;       for such case the local minimum in a specified direction is bracketed,
+;       This routine is called by minF_conj_grad, to bracket minimum in the 
+;       direction of the conjugate gradient of function of many variables
+; CALLING EXAMPLE:
+;       xa=0  & xb=1                                    
+;       minF_bracket, xa,xb,xc, fa,fb,fc, FUNC_NAME="name"      ;for 1-D func.
+;  or:
+;       minF_bracket, xa,xb,xc, fa,fb,fc, FUNC="name",     $
+;                                         POINT=[0,1,1],   $
+;                                         DIRECTION=[2,1,1]     ;for 3-D func.
+; INPUTS:
+;       xa = scalar, guess for point bracketing location of minimum.
+;       xb = scalar, second guess for point bracketing location of minimum.
+; KEYWORDS:
+;       FUNC_NAME = function name (string)
+;               Calling mechanism should be:  F = func_name( px )
+;               where:
+;                       px = scalar or vector of independent variables, input.
+;                       F = scalar value of function at px.
+;       POINT_NDIM = when working with function of N variables,
+;               use this keyword to specify the starting point in N-dim space.
+;               Default = 0, which assumes function is 1-D.
+;       DIRECTION = when working with function of N variables,
+;               use this keyword to specify the direction in N-dim space
+;               along which to bracket the local minimum, (default=1 for 1-D).
+;               (xa,xb,xc) are then relative distances from POINT_NDIM.
+; OUTPUTS:
+;       xa,xb,xc = scalars, 3 points which bracket location of minimum,
+;               that is, f(xb) < f(xa) and f(xb) < f(xc), so minimum exists.
+;               When working with function of N variables
+;               (xa,xb,xc) are then relative distances from POINT_NDIM,
+;               in the direction specified by keyword DIRECTION,
+;               with scale factor given by magnitude of DIRECTION.
+; OPTIONAL OUTPUT:
+;       fa,fb,fc = value of function at 3 points which bracket the minimum,
+;                       again note that fb < fa and fb < fc if minimum exists.
+; PROCEDURE:
+;       algorithm from Numerical Recipes (by Press, et al.), sec.10.1 (p.281).
+; MODIFICATION HISTORY:
+;       Written, Frank Varosi NASA/GSFC 1992.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+        goldm = (sqrt(5)+1)/2           ;golden mean factor to march with.
+        glimit = 100                    ;maximum factor to try.
+        tiny = 1.e-19                   ;a tiny number to avoid divide by zero.
+
+        if N_elements( pn ) LE 0 then begin
+                pn = 0
+                dirn = 1
+           endif
+
+        if (xa EQ xb) then xb = xa + 1
+        fa = call_function( func_name, pn + xa * dirn )
+        fb = call_function( func_name, pn + xb * dirn )
+
+        if (fb GT fa) then begin
+                x = xa  &  xa = xb  &  xb = x
+                f = fa  &  fa = fb  &  fb = f
+           endif
+
+        xc = xb + goldm * (xb-xa)
+        fc = call_function( func_name, pn + xc * dirn )
+
+        while (fb GE fc) do begin
+
+                zba = xb-xa
+                zbc = xb-xc
+                r = zba * (fb-fc)
+                q = zbc * (fb-fa)
+                delta = q-r
+                sign = 1 - 2 * (delta LT 0)
+                xu = xb - (zbc * q - zba * r)/(2* sign * (abs( delta ) > tiny) )
+                ulim = xb + glimit * (xc-xb)
+
+                if ( (xb-xu)*(xu-xc) GT 0 ) then begin
+
+                        fu = call_function( func_name, pn + xu * dirn )
+
+                        if (fu LT fc) then begin
+                                xa = xb  &  xb = xu
+                                fa = fb  &  fb = fu
+                                return
+                          endif else if (fu GT fb) then begin
+                                xc = xu
+                                fc = fu
+                                return
+                           endif
+
+                        xu = xc - goldm * zbc
+                        fu = call_function( func_name, pn + xu * dirn )
+
+                 endif else if ( (xc-xu)*(xu-ulim) GT 0 ) then begin
+
+                        fu = call_function( func_name, pn + xu * dirn )
+
+                        if (fu LT fc) then begin
+                                xb = xc  &  fb = fc
+                                xc = xu  &  fc = fu
+                                xu = xc + goldm * (xc-xb)
+                                fu = call_function( func_name, pn + xu * dirn )
+                           endif
+
+                  endif else if ( (ulim-xc)*(xu-ulim) GE 0 ) then begin
+
+                        xu = ulim
+                        fu = call_function( func_name, pn + xu * dirn )
+
+                   endif else begin
+
+                        xu = xc + goldm * (xc-xb)
+                        fu = call_function( func_name, pn + xu * dirn )
+                    endelse
+        
+                xa = xb  &  xb = xc  &  xc = xu
+                fa = fb  &  fb = fc  &  fc = fu
+          endwhile
+return
+end
diff --git a/Code/script_idl_mv/astrolib/minf_conj_grad.pro b/Code/script_idl_mv/astrolib/minf_conj_grad.pro
new file mode 100644
index 0000000000000000000000000000000000000000..81a32bccf059ec5348914d29929dd07b60486596
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/minf_conj_grad.pro
@@ -0,0 +1,127 @@
+pro minF_conj_grad, p_min, f_min, conv_factor, FUNC_NAME=func_name, $
+                                        TOLERANCE=tol, USE_DERIV=use, $
+                                        INITIALIZE=initialize, QUADRATIC=quad
+;+
+; NAME:
+;        MINF_CONJ_GRAD
+; PURPOSE:
+;       Find the local minimum of a scalar function using conjugate gradient
+; EXPLANATION:
+;       Find the local minimum of a scalar function of several variables using 
+;       the Conjugate Gradient method (Fletcher-Reeves-Polak-Ribiere algorithm).
+;       Function may be anything with computable partial derivatives.
+;       Each call to minF_conj_grad performs one iteration of algorithm,
+;       and returns an N-dim point closer to the local minimum of function.
+; CALLING EXAMPLE:
+;       p_min = replicate( 1, N_dim )
+;       minF_conj_grad, p_min, f_min, conv_factor, FUNC_NAME="name",/INITIALIZE
+;
+;       while (conv_factor GT 0) do begin
+;               minF_conj_grad, p_min, f_min, conv_factor, FUNC_NAME="name"
+;       endwhile
+; INPUTS:
+;       p_min = vector of independent variables, location of minimum point
+;               obtained from previous call to minF_conj_grad, (or first guess).
+; KEYWORDS:
+;       FUNC_NAME = function name (string)
+;               Calling mechanism should be:  F = func_name( px, gradient )
+;         where:
+;               F = scalar value of function at px.
+;               px = vector of independent variables, input.
+;               gradient = vector of partial derivatives of the function
+;                       with respect to independent variables, evaluated at px.
+;                       This is an optional output parameter:
+;                       gradient should not be calculated if parameter is not
+;                       supplied in call (Unless you want to waste some time).
+;      /INIT must be specified on first call (whenever p_min is a guess),
+;                       to initialize the iteration scheme of algorithm.
+;      /USE_DERIV causes the directional derivative of function to be used
+;                       in the 1-D minimization part of algorithm
+;                       (default is not to use directional derivative).
+;       TOLERANCE = desired accuracy of minimum location, default=sqrt(1.e-7).
+;      /QUADRATIC runs simpler version which works only for quadratic function.
+; OUTPUTS:
+;       p_min = vector giving improved solution for location of minimum point.
+;       f_min = value of function at p_min.
+;       conv_factor = gives the current rate of convergence (change in value),
+;                       iteration should be stopped when rate gets near zero.
+; EXTERNAL CALLS:
+;       pro minF_bracket,  to find 3 points which bracket the minimum in 1-D.
+;       pro minF_parabolic,  to find minimum point in 1-D.
+;       pro minF_parabol_D,  to find minimum point in 1-D, using derivatives.
+; COMMON BLOCKS:
+;       common minf_conj_grad, grad_conj, grad_save, gs_norm
+;       (to keep conjugate gradient, gradient and norm from previous iteration)
+; PROCEDURE:
+;       Algorithm adapted from Numerical Recipes, sec.10.6 (p.305).
+;       Conjugate gradient is computed from gradient, which then gives
+;       the best direction (in N-dim space) in which to proceed to find
+;       the minimum point. The function is then minimized along
+;       this direction of conjugate gradient (a 1-D minimization).
+;       The algorithm is repeated starting at the new point by calling again.
+; MODIFICATION HISTORY:
+;       Written, Frank Varosi NASA/GSFC 1992.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2 
+ 
+ if N_params() LT 3 then begin  
+   print,'Syntax - minF_conj_grad, p_min, f_min, conv_factor, FUNC_NAME = 
+   print,'         [ TOLERANCE=, USE_DERIV=, INITIALIZE= , QUADRATIC= ]
+   return
+ endif
+
+  common minf_conj_grad, grad_conj, grad_save, gs_norm
+
+        fp = call_function( func_name, p_min, gradient )
+
+;Compute conjugate gradient direction:
+
+        if keyword_set( initialize ) then begin
+
+                grad_conj = -gradient
+                gs_norm = total( gradient * gradient )
+                if NOT keyword_set( quad ) then grad_save = gradient
+
+          endif else begin
+
+                grad_norm = total( gradient * gradient )
+
+                if (grad_norm EQ 0) then begin
+                        f_min = fp
+                        conv_factor = 0
+                        return
+                   endif
+
+                if keyword_set( quad ) then gamma = grad_norm/gs_norm else begin
+
+                    gamma = ( grad_norm - total( grad_save*gradient ) )/gs_norm
+                        grad_save = gradient
+                  endelse
+
+                grad_conj = gamma * grad_conj - gradient
+                gs_norm = grad_norm
+           endelse
+
+;Now find mininum along direction of conjugate gradient:
+
+        xa = 0
+        xb = 1/sqrt( gs_norm )
+
+        minF_bracket, xa,xb,xc, fa,fb,fc, FUNC_NAME=func_name, POINT=p_min, $
+                                                        DIRECTION=grad_conj
+        if keyword_set( use ) then begin
+
+                minF_parabol_D, xa,xb,xc, x_min, f_min, FUN=func_name, TOL=tol,$
+                                                POINT=p_min, DIRECTION=grad_conj
+          endif else begin
+
+                minF_parabolic, xa,xb,xc, x_min, f_min, FUN=func_name, TOL=tol,$
+                                                POINT=p_min, DIRECTION=grad_conj
+           endelse
+
+        conv_factor = 2*abs( f_min - fp )/( (abs(f_min) + abs(fp)) > 1.e-9 )
+
+        p_min = p_min + x_min * grad_conj
+return
+end
diff --git a/Code/script_idl_mv/astrolib/minf_parabol_d.pro b/Code/script_idl_mv/astrolib/minf_parabol_d.pro
new file mode 100644
index 0000000000000000000000000000000000000000..313a043a9b4ce680308e56c50131df2b73c695d1
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/minf_parabol_d.pro
@@ -0,0 +1,173 @@
+; Procedure minF_parabol_D,
+; first, a utility function which gets derivative in 1-D:
+;------------------------------------------------------------------------------
+function call_func_deriv, func_name, x, deriv, POINT_NDIM=pn, DIRECTION=dirn
+
+        f = call_function( func_name, pn + x * dirn, grad )
+
+        deriv = total( [grad * dirn] )
+
+return, f
+end
+;------------------------------------------------------------------------------
+pro minF_parabol_D, xa,xb,xc, xmin, fmin, FUNC_NAME=func_name,    $
+                                          MAX_ITERATIONS=maxit,   $
+                                          TOLERANCE=TOL,          $
+                                          POINT_NDIM=pn, DIRECTION=dirn
+;+
+; NAME:
+;       MINF_PARABOL_D
+; PURPOSE:
+;       Minimize a function using a modified  Brent's method with derivatives
+; EXPLANATION:
+;       Based on the procedure DBRENT in Numerical Recipes by Press et al.
+;       Finds a local minimum of a 1-D function up to specified tolerance,
+;       using the first derivative of function in the algorithm.
+;       This routine assumes that the function has a minimum nearby.
+;       (recommend first calling minF_bracket, xa,xb,xc, to bracket minimum).
+;       Routine can also be applied to a scalar function of many variables,
+;       for such case the local minimum in a specified direction is found,
+;       This routine is called by minF_conj_grad, to locate minimum in the 
+;       direction of the conjugate gradient of function of many variables.
+;
+; CALLING EXAMPLES:
+;       minF_parabol_D, xa,xb,xc, xmin, fmin, FUNC_NAME="name"  ;for 1-D func.
+;  or:
+;       minF_parabol_D, xa,xb,xc, xmin, fmin, FUNC="name", $
+;                                         POINT=[0,1,1],   $
+;                                         DIRECTION=[2,1,1]     ;for 3-D func.
+; INPUTS:
+;       xa,xb,xc = scalars, 3 points which bracket location of minimum,
+;               that is, f(xb) < f(xa) and f(xb) < f(xc), so minimum exists.
+;               When working with function of N variables
+;               (xa,xb,xc) are then relative distances from POINT_NDIM,
+;               in the direction specified by keyword DIRECTION,
+;               with scale factor given by magnitude of DIRECTION.
+; KEYWORDS:
+;       FUNC_NAME = function name (string)
+;               Calling mechanism should be:  F = func_name( px, gradient )
+;               where:
+;                       px = scalar or vector of independent variables, input.
+;                       F = scalar value of function at px.
+;                       gradient = derivative of function, a scalar if 1-D,
+;                               a gradient vector if N-D,
+;                               (should only be computed if arg. is present).
+;
+;       POINT_NDIM = when working with function of N variables,
+;               use this keyword to specify the starting point in N-dim space.
+;               Default = 0, which assumes function is 1-D.
+;       DIRECTION = when working with function of N variables,
+;               use this keyword to specify the direction in N-dim space
+;               along which to bracket the local minimum, (default=1 for 1-D).
+;               (xa, xb, xc, x_min are then relative distances from POINT_NDIM)
+;       MAX_ITER = maximum allowed number iterations, default=100.
+;       TOLERANCE = desired accuracy of minimum location, default=sqrt(1.e-7).
+;
+; OUTPUTS:
+;       xmin = estimated location of minimum.
+;               When working with function of N variables,
+;               xmin is the relative distance from POINT_NDIM,
+;               in the direction specified by keyword DIRECTION,
+;               with scale factor given by magnitude of DIRECTION,
+;               so that min. Loc. Pmin = Point_Ndim + xmin * Direction.
+;       fmin = value of function at xmin (or Pmin).
+; PROCEDURE:
+;       Brent's method to minimize a function by using parabolic interpolation
+;       and using first derivative of function,
+;       from Numerical Recipes (by Press, et al.), sec.10.3 (p.287),
+; MODIFICATION HISTORY:
+;       Written, Frank Varosi NASA/GSFC 1992.
+;-
+        zeps = 1.e-7    ;machine epsilon, smallest addition.
+        if N_elements( TOL ) NE 1 then TOL = sqrt( zeps )
+        if N_elements( maxit ) NE 1 then maxit = 100
+
+        if N_elements( pn ) LE 0 then begin
+                pn = 0
+                dirn = 1
+           endif
+
+        xLo = xa < xc
+        xHi = xa > xc
+        xmin = xb
+        fmin = call_func_deriv( func_name, xmin, dx, POINT=pn, DIR=dirn )
+        xv = xmin  &  xw = xmin
+        fv = fmin  &  fw = fmin
+        dv = dx  &  dw = dx
+        es = 0.
+
+        for iter = 1,maxit do begin
+
+                xm = (xLo + xHi)/2.
+                TOL1 = TOL * abs(xmin) + zeps
+                TOL2 = 2*TOL1
+
+                if ( abs( xmin - xm ) LE ( TOL2 - (xHi-xLo)/2. ) ) then return
+
+                if (abs( es ) GT TOL1) then begin
+
+                        d1 = 2*(xHi-xLo)
+                        d2 = d1
+                        if (dw NE dx) then d1 = (xw-xmin)*dx/(dx-dw)
+                        if (dv NE dx) then d2 = (xv-xmin)*dx/(dx-dv)
+                        u1 = xmin + d1
+                        u2 = xmin + d2
+                        ok1 = ((xLo-u1)*(u1-xHi) GT 0) AND (dx*d1 LE 0)
+                        ok2 = ((xLo-u2)*(u2-xHi) GT 0) AND (dx*d2 LE 0)
+                        olde = es
+                        es = ds
+
+                        if NOT (ok1 OR ok2) then goto,BISECT
+
+                        if (ok1 AND ok2) then begin
+
+                            if (abs( d1 ) LT abs( d2 )) then ds=d1 else ds=d2
+
+                         endif else if (ok1) then ds=d1 else ds=d2
+
+                        if (abs( ds ) LE abs( olde/2 )) then begin
+
+                                xu = xmin + ds
+
+                                if ((xu-xLo) LT TOL2) OR $
+                                   ((xHi-xu) LT TOL2) then $
+                                             ds = TOL1 * (1-2*((xm-xmin) LT 0))
+                                goto,STEP
+                          endif
+                   endif
+
+        BISECT: if (dx GE 0) then  es = xLo-xmin  else  es = xHi-xmin
+                ds = es/2
+
+        STEP:   sign = 1 - 2*(ds LT 0)
+                xu = xmin + sign * ( abs( ds ) > TOL1 )
+                fu = call_func_deriv( func_name, xu, du, POINT=pn, DIR=dirn )
+
+                if (fu GT fmin) AND (abs( ds ) LT TOL1) then return
+
+                if (fu LE fmin) then begin
+
+                        if (xu GE xmin) then xLo=xmin else xHi=xmin
+                        xv = xw  &  fv = fw  &  dv = dw
+                        xw = xmin  &  fw = fmin  &  dw = dx
+                        xmin = xu  &  fmin = fu  &  dx = du
+                
+                  endif else begin
+
+                        if (xu LT xmin) then xLo=xu else xHi=xu
+
+                        if (fu LE fw) OR (xw EQ xmin) then begin
+
+                                xv = xw  &  fv = fw  &  dv = dw
+                                xw = xu  &  fw = fu  &  dw = du
+
+                          endif else if (fu LE fv) OR (xv EQ xmin) $
+                                                   OR (xv EQ xw) then begin
+                                xv = xu  &  fv = fu  &  dv = du
+                           endif
+                   endelse
+          endfor
+
+        message,"exceeded maximum number of iterations: "+strtrim(iter,2),/INFO
+return
+end
diff --git a/Code/script_idl_mv/astrolib/minf_parabolic.pro b/Code/script_idl_mv/astrolib/minf_parabolic.pro
new file mode 100644
index 0000000000000000000000000000000000000000..eff8345c31ab593c090599ca929fed3f075f1333
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/minf_parabolic.pro
@@ -0,0 +1,147 @@
+pro minF_parabolic, xa,xb,xc, xmin, fmin, FUNC_NAME=func_name,    $
+                                          MAX_ITERATIONS=maxit,   $
+                                          TOLERANCE=TOL,          $
+                                          POINT_NDIM=pn, DIRECTION=dirn
+;+
+; NAME:
+;       MINF_PARABOLIC
+; PURPOSE:
+;       Minimize a function using Brent's method with parabolic interpolation
+; EXPLANATION:
+;       Find a local minimum of a 1-D function up to specified tolerance.
+;       This routine assumes that the function has a minimum nearby.
+;       (recommend first calling minF_bracket, xa,xb,xc, to bracket minimum).
+;       Routine can also be applied to a scalar function of many variables,
+;       for such case the local minimum in a specified direction is found,
+;       This routine is called by minF_conj_grad, to locate minimum in the 
+;       direction of the conjugate gradient of function of many variables.
+;
+; CALLING EXAMPLES:
+;       minF_parabolic, xa,xb,xc, xmin, fmin, FUNC_NAME="name"  ;for 1-D func.
+;  or:
+;       minF_parabolic, xa,xb,xc, xmin, fmin, FUNC="name", $
+;                                         POINT=[0,1,1],   $
+;                                         DIRECTION=[2,1,1]     ;for 3-D func.
+; INPUTS:
+;       xa,xb,xc = scalars, 3 points which bracket location of minimum,
+;               that is, f(xb) < f(xa) and f(xb) < f(xc), so minimum exists.
+;               When working with function of N variables
+;               (xa,xb,xc) are then relative distances from POINT_NDIM,
+;               in the direction specified by keyword DIRECTION,
+;               with scale factor given by magnitude of DIRECTION.
+; INPUT KEYWORDS:
+;      FUNC_NAME = function name (string)
+;               Calling mechanism should be:  F = func_name( px )
+;               where:
+;                       px = scalar or vector of independent variables, input.
+;                       F = scalar value of function at px.
+;
+;      POINT_NDIM = when working with function of N variables,
+;               use this keyword to specify the starting point in N-dim space.
+;               Default = 0, which assumes function is 1-D.
+;      DIRECTION = when working with function of N variables,
+;               use this keyword to specify the direction in N-dim space
+;               along which to bracket the local minimum, (default=1 for 1-D).
+;               (xa, xb, xc, x_min are then relative distances from POINT_NDIM)
+;      MAX_ITER = maximum allowed number iterations, default=100.
+;      TOLERANCE = desired accuracy of minimum location, default=sqrt(1.e-7).
+; OUTPUTS:
+;       xmin = estimated location of minimum.
+;               When working with function of N variables,
+;               xmin is the relative distance from POINT_NDIM,
+;               in the direction specified by keyword DIRECTION,
+;               with scale factor given by magnitude of DIRECTION,
+;               so that min. Loc. Pmin = Point_Ndim + xmin * Direction.
+;       fmin = value of function at xmin (or Pmin).
+; PROCEDURE:
+;       Brent's method to minimize a function by using parabolic interpolation.
+;       Based on function BRENT in Numerical Recipes in FORTRAN (Press et al. 
+;       1992),  sec.10.2 (p. 397).
+; MODIFICATION HISTORY:
+;       Written, Frank Varosi NASA/GSFC 1992.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+        zeps = 1.e-7                    ;machine epsilon, smallest addition.
+        goldc = 1 - (sqrt(5)-1)/2       ;complement of golden mean.
+
+        if N_elements( TOL ) NE 1 then TOL = sqrt( zeps )
+        if N_elements( maxit ) NE 1 then maxit = 100
+
+        if N_elements( pn ) LE 0 then begin
+                pn = 0
+                dirn = 1
+           endif
+
+        xLo = xa < xc
+        xHi = xa > xc
+        xmin = xb
+        fmin = call_function( func_name, pn + xmin * dirn )
+        xv = xmin  &  xw = xmin
+        fv = fmin  &  fw = fmin
+        es = 0.
+
+        for iter = 1,maxit do begin
+
+                goldstep = 1
+                xm = (xLo + xHi)/2.
+                TOL1 = TOL * abs(xmin) + zeps
+                TOL2 = 2*TOL1
+
+                if ( abs( xmin - xm ) LE ( TOL2 - (xHi-xLo)/2. ) ) then return
+
+                if (abs( es ) GT TOL1) then begin
+
+                        r = (xmin-xw) * (fmin-fv)
+                        q = (xmin-xv) * (fmin-fw)
+                        p = (xmin-xv) * q + (xmin-xw) * r
+                        q = 2 * (q-r)
+                        if (q GT 0) then p = -p
+                        q = abs( q )
+                        etemp = es
+                        es = ds
+
+                        if (p GT q*(xLo-xmin)) AND $
+                           (p LT q*(xHi-xmin)) AND $
+                           (abs( p ) LT abs( q*etemp/2 )) then begin
+                                ds = p/q
+                                xu = xmin + ds
+                                if (xu-xLo LT TOL2) OR (xHi-xu LT TOL2) then $
+                                        ds = TOL1 * (1-2*((xm-xmin) LT 0))
+                                goldstep = 0
+                           endif
+                   endif
+
+                if (goldstep) then begin
+                        if (xmin GE xm) then  es = xLo-xmin  else  es = xHi-xmin
+                        ds = goldc * es
+                   endif
+
+                xu = xmin + (1-2*(ds LT 0)) * ( abs( ds ) > TOL1 )
+                fu = call_function( func_name, pn + xu * dirn )
+
+                if (fu LE fmin) then begin
+
+                        if (xu GE xmin) then xLo=xmin else xHi=xmin
+                        xv = xw  &  fv = fw
+                        xw = xmin  &  fw = fmin
+                        xmin = xu  &  fmin = fu
+                
+                  endif else begin
+
+                        if (xu LT xmin) then xLo=xu else xHi=xu
+
+                        if (fu LE fw) OR (xw EQ xmin) then begin
+
+                                xv = xw  &  fv = fw
+                                xw = xu  &  fw = fu
+
+                          endif else if (fu LE fv) OR (xv EQ xmin) $
+                                                   OR (xv EQ xw) then begin
+                                xv = xu  &  fv = fu
+                           endif
+                   endelse
+          endfor
+
+        message,"exceeded maximum number of iterations: "+strtrim(iter,2),/INFO
+return
+end
diff --git a/Code/script_idl_mv/astrolib/minmax.pro b/Code/script_idl_mv/astrolib/minmax.pro
new file mode 100644
index 0000000000000000000000000000000000000000..71b8e37ffe86e3cd39dfa693c17ff875ae110d22
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/minmax.pro
@@ -0,0 +1,71 @@
+function minmax,array,subs,NAN=nan, DIMEN=dimen
+;+
+; NAME:
+;      MINMAX
+; PURPOSE:
+;      Return a 2 element array giving the minimum and maximum of an array
+; EXPLANATION:
+;      Using MINMAX() is faster than doing a separate MAX and MIN.
+;
+;      The procedure MAXMIN in http://www.idlcoyote.com/programs/maxmin.pro
+;      has a similar purpose but uses a procedure call rather than a function.
+; CALLING SEQUENCE:
+;      value = minmax( array, [subs, /NAN, DIMEN= ] )
+; INPUTS:
+;      array - an IDL numeric scalar, vector or array.
+;
+; OUTPUTS:
+;      value = a two element vector (if DIMEN is not supplied)
+;            value[0] = minimum value of array
+;            value[1] = maximum value of array
+;
+;            If the DIMEN keyword is supplied then value will be a 2 x N element
+;            array where N is the number of elements in the specified
+;            dimension
+;              
+; OPTIONAL OUTPUT PARAMETER:
+;       subs - two-dimensional vector; the first element gives the subscript
+;              of the minimum value, the second element gives the subscript
+;              of the maximum value.     
+;
+; OPTIONAL INPUT KEYWORD:
+;      /NAN   - Set this keyword to cause the routine to check for occurrences
+;            of the IEEE floating-point value NaN in the input data.  Elements 
+;            with the value NaN are treated as missing data.
+;
+;      DIMEN -  integer (either 1 or 2) specifying which dimension of a 2-d 
+;            array to  take the minimum and maximum.   Note that (unlike the
+;            DIMENSION keyword to the MIN() function) DIMEN is only valid
+;            for a 2-d array, larger dimensions are  not supported.  
+; EXAMPLE:
+;     (1)  Print the minimum and maximum of an image array, im
+; 
+;            IDL> print, minmax( im )
+;
+;     (2) Given a 2-dimension array of (echelle) wavelengths w, print the
+;         minimum and maximum of each order
+;
+;         print,minmax(w,dimen=1)
+;
+; PROCEDURE:
+;      The MIN function is used with the MAX keyword
+;
+; REVISION HISTORY:
+;      Written W. Landsman                January, 1990
+;      Added NaN keyword.      M. Buie       June 1998
+;      Added DIMEN keyword    W. Landsman  January 2002
+;      Added SUBSCRIPT_MIN and SUBSCRIPT_MAX  BT Jan 2005
+;      Added optional subs output parameter  W. Landsman July 2009
+;-
+ On_error,2
+ compile_opt idl2
+ if N_elements(DIMEN) GT 0 then begin
+      amin = min(array, MAX = amax, NAN = nan, DIMEN = dimen,cmin,sub=cmax) 
+      if arg_present(subs) then subs = transpose([[cmin], [cmax]])
+      return, transpose([[amin],[amax] ])
+ endif else  begin 
+     amin = min( array, MAX = amax, NAN=nan, cmin, sub=cmax)
+      if arg_present(subs) then subs = [cmin, cmax]
+     return, [ amin, amax ]
+ endelse
+ end
diff --git a/Code/script_idl_mv/astrolib/mkhdr.pro b/Code/script_idl_mv/astrolib/mkhdr.pro
new file mode 100644
index 0000000000000000000000000000000000000000..bf130b1c76b49fa10ed0fc0d3d3a119578308ae1
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mkhdr.pro
@@ -0,0 +1,169 @@
+pro mkhdr, header, im, naxisx, IMAGE = image, EXTEND = extend
+;+
+; NAME:
+;       MKHDR
+; PURPOSE:
+;       Make a minimal primary (or IMAGE extension) FITS header
+; EXPLANATION:
+;       If an array is supplied,  then the created FITS header will be 
+;       appropriate to the supplied array.  Otherwise, the user can specify 
+;       the dimensions and datatype.
+;
+;       To update an *existing* FITS header with a new image array, instead 
+;       use check_FITS, /Update 
+;
+; CALLING SEQUENCE:
+;       MKHDR, header                   ;Prompt for image size and type
+;               or
+;       MKHDR, header, im, [ /IMAGE, /EXTEND ]
+;               or
+;       MKHDR, header, type, naxisx, [/IMAGE, /EXTEND ]         
+;
+; OPTIONAL INPUTS:
+;       IM - If IM is a vector or array then the header will be made
+;               appropriate to the size and type of IM.  IM does not have
+;               to be the actual data; it can be a dummy array of the same
+;               type and size as the data.    Set IM = '' to create a dummy
+;               header with NAXIS = 0. 
+;       TYPE - If 2 parameters are supplied, then the second parameter
+;               is interpreted as an integer giving the IDL datatype e.g. 
+;               1 - Byte, 2 - 16 bit integer, 4 - float, 3 - Long
+;       NAXISX - Vector giving the size of each dimension (NAXIS1, NAXIS2, 
+;               etc.).  
+;
+; OUTPUT:
+;       HEADER - image header, (string array) with required keywords
+;               BITPIX, NAXIS, NAXIS1, ... Further keywords can be added
+;               to the header with SXADDPAR. 
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /IMAGE   = If set, then a minimal header for a FITS IMAGE extension
+;               is created.    An IMAGE extension header is identical to
+;               a primary FITS header except the first keyword is 
+;               'XTENSION' = 'IMAGE' instead of 'SIMPLE  ' = 'T'
+;       /EXTEND  = If set, then the keyword EXTEND is inserted into the file,
+;               with the value of "T" (true).    The EXTEND keyword can 
+;               optionally be included in a primary header, if the FITS file 
+;               contains extensions.
+;
+; RESTRICTIONS:
+;       (1)  MKHDR should not be used to make an STSDAS header or a FITS
+;               ASCII or Binary Table extension header.   Instead use
+;
+;               SXHMAKE - to create a minimal STSDAS header
+;               FXBHMAKE - to create a minimal FITS binary table header
+;               FTCREATE - to create a minimal FITS ASCII table header
+;
+;       (2)  Any data already in the header before calling MKHDR
+;               will be destroyed.
+; EXAMPLE:
+;       Create a minimal FITS header, Hdr, for a 30 x 40 x 50 INTEGER*2 array
+;
+;             IDL> mkhdr, Hdr, 2, [30,40,50]
+;
+;       Alternatively, if the array already exists as an IDL variable, Array,
+;
+;              IDL> mkhdr, Hdr, Array
+;
+; PROCEDURES CALLED:
+;       SXADDPAR, GET_DATE
+;
+; REVISION HISTORY:
+;       Written November, 1988               W. Landsman
+;       May, 1990, Adapted for IDL Version 2.0, J. Isensee
+;       Aug, 1997, Use SYSTIME(), new DATE format  W. Landsman
+;       Allow unsigned data types    W. Landsman   December 1999
+;       Set BZERO = 0 for unsigned integer data  W. Landsman January 2000
+;       EXTEND keyword must immediately follow last NAXISi W. Landsman Sep 2000
+;       Add FITS definition COMMENT to primary headers W. Landsman Oct. 2001
+;       Allow (nonstandard) 64 bit integers   W. Landsman  Feb. 2003
+;       Add V6.0 notation W. Landsman July 2012
+;-                          
+ On_error,2
+ compile_opt idl2
+
+ npar = N_params()
+ if npar LT 1 then begin
+   print,'Syntax:  MKHDR, header, [ im, /IMAGE, /EXTEND ]'
+   print,'    or   MKHDR, header, [ type, naxisx, /IMAGE, /EXTEND ]'
+   print,'   header - output FITS header to be created'
+   return
+ endif
+
+ if (npar eq 1) then begin               ;Prompt for keyword values
+    read,'Enter number of dimensions (NAXIS): ',naxis
+    s = lonarr(naxis+2)
+    s[0] = naxis
+    if ( naxis GT 0 ) then begin       ;Make sure not a dummy header
+    for i = 1,naxis do begin       ;Get dimension of each axis
+      keyword = 'NAXIS' + strtrim(i,2)
+      read,'Enter size of dimension '+ strtrim(i,2) + ' ('+keyword+'): ',nx
+      s[i] = nx                            
+    endfor
+  endif
+
+  print,'Allowed datatypes are (1) Byte, (2) 16 bit integer, (3) 32 bit integer'
+  print,'                      (4) 32bit floating, (5) 64 bit double precision' 
+  print,'                  or (14) 64bit integer'
+  read,'Enter datatype: ',stype
+  s[s[0] + 1] = stype
+
+ endif else $
+     if ( npar EQ 2 ) then s = size(im) $  ;Image array supplied
+          else  s = [ N_elements(naxisx),naxisx, im ] ;Keyword values supplied
+
+ stype = s[s[0]+1]              ;Type of data    
+        case stype of
+        0:      message,'ERROR: Input data array is undefined'
+        1:      bitpix = 8  
+        2:      bitpix = 16  
+        3:      bitpix = 32  
+        4:      bitpix = -32 
+        5:      bitpix = -64 
+        6:      message,'Complex types not allowed as FITS primary arrays'  
+        7:      bitpix = 8
+       12:      bitpix = 16
+       13:      bitpix = 32
+       14:      bitpix = 64
+        else:   message,'ERROR: Illegal Image Datatype'
+        endcase
+
+ header = strarr(s[0] + 7) + string(' ',format='(a80)')      ;Create empty array
+ header[0] = 'END' + string(replicate(32b,77))
+
+ if keyword_set( IMAGE) then $
+    sxaddpar, header, 'XTENSION', 'IMAGE   ',' IMAGE extension' $
+ else $
+    sxaddpar, header, 'SIMPLE', 'T',' Written by IDL:  '+ systime()
+
+ sxaddpar, header, 'BITPIX', bitpix, ' Number of bits per data pixel'
+ sxaddpar, header, 'NAXIS', S[0],' Number of data axes'       ;# of dimensions
+
+ if ( s[0] GT 0 ) then begin
+   for i = 1, s[0] do sxaddpar,header,'NAXIS' + strtrim(i,2),s[i]
+ endif
+
+ if keyword_set( IMAGE) then begin
+     sxaddpar, header, 'PCOUNT', 0, ' No Group Parameters'
+     sxaddpar, header, 'GCOUNT', 1, ' One Data Group'
+ endif else begin
+     if keyword_set( EXTEND) or (s[0] EQ 0) then $
+          sxaddpar, header, 'EXTEND', 'T', ' FITS data may contain extensions'
+     Get_date, dte                       ;Get current date as CCYY-MM-DD
+     sxaddpar, header, 'DATE', dte, $
+           ' Creation UTC (CCCC-MM-DD) date of FITS header'
+  endelse
+
+ if stype EQ 12 then sxaddpar, header,'O_BZERO',32768, $
+            ' Original Data is Unsigned Integer'
+ if stype EQ 13 then sxaddpar, header,'O_BZERO',2147483648, $
+            ' Original Data is Unsigned Long'
+ header = header[0:s[0]+7]
+
+ if ~keyword_set(IMAGE) then begin   ;Add FITS definition for primary header
+     sxaddpar,header,'COMMENT ', $
+      "FITS (Flexible Image Transport System) format is defined in 'Astronomy"
+     sxaddpar,header,'COMMENT ', $
+      "and Astrophysics', volume 376, page 359; bibcode 2001A&A...376..359H"
+ endif 
+ end
diff --git a/Code/script_idl_mv/astrolib/mlinmix_err.pro b/Code/script_idl_mv/astrolib/mlinmix_err.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b74da78d3ae732b44423101e6dd333f50ee47db9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mlinmix_err.pro
@@ -0,0 +1,878 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;+
+;   NAME:
+;     MLINMIX_ERR
+;   PURPOSE:
+;      Bayesian approach to multiple linear regression with errors in  X and Y
+;   EXPLANATION:
+; PERFORM LINEAR REGRESSION OF Y ON X WHEN THERE ARE MEASUREMENT
+; ERRORS IN BOTH VARIABLES. THE REGRESSION ASSUMES :
+;
+;                 ETA = ALPHA + BETA ## XI + EPSILON
+;                 X = XI + XERR
+;                 Y = ETA + YERR
+;
+; HERE, (ALPHA, BETA) ARE THE REGRESSION COEFFICIENTS, EPSILON IS THE
+; INTRINSIC RANDOM SCATTER ABOUT THE REGRESSION, XERR IS THE
+; MEASUREMENT ERROR IN X, AND YERR IS THE MEASUREMENT ERROR IN
+; Y. EPSILON IS ASSUMED TO BE NORMALLY-DISTRIBUTED WITH MEAN ZERO AND
+; VARIANCE SIGSQR. XERR AND YERR ARE ASSUMED TO BE
+; NORMALLY-DISTRIBUTED WITH MEANS EQUAL TO ZERO, COVARIANCE MATRICES
+; XVAR^2 FOR X, VARIANCES YSIG^2 FOR Y, AND COVARIANCE VECTORS
+; XYCOV. THE DISTRIBUTION OF XI IS MODELLED AS A MIXTURE OF NORMALS,
+; WITH GROUP PROPORTIONS PI, MEANS MU, AND COVARIANCES T. BAYESIAN
+; INFERENCE IS EMPLOYED, AND A STRUCTURE CONTAINING RANDOM DRAWS FROM
+; THE POSTERIOR IS RETURNED. CONVERGENCE OF THE MCMC TO THE POSTERIOR
+; IS MONITORED USING THE POTENTIAL SCALE REDUCTION FACTOR (RHAT,
+; GELMAN ET AL.2004). IN GENERAL, WHEN RHAT < 1.1 THEN APPROXIMATE
+; CONVERGENCE IS REACHED.
+;
+; SIMPLE NON-DETECTIONS ON Y MAY ALSO BE INCLUDED
+;
+; AUTHOR : BRANDON C. KELLY, STEWARD OBS., JULY 2006
+;
+; INPUTS :
+;
+;   X - THE OBSERVED INDEPENDENT VARIABLES. THIS SHOULD BE AN
+;       [NX, NP]-ELEMENT ARRAY.
+;   Y - THE OBSERVED DEPENDENT VARIABLE. THIS SHOULD BE AN NX-ELEMENT
+;       VECTOR.
+;
+; OPTIONAL INPUTS :
+;
+;   XVAR - THE COVARIANCE MATRIX OF THE X ERRORS, AND
+;          [NX,NP,NP]-ELEMENT ARRAY. XVAR[I,*,*] IS THE COVARIANCE
+;          MATRIX FOR THE ERRORS ON X[I,*]. THE DIAGONAL OF
+;          XVAR[I,*,*] MUST BE GREATER THAN ZERO FOR EACH DATA POINT.
+;   YVAR - THE VARIANCE OF THE Y ERRORS, AND NX-ELEMENT VECTOR. YVAR
+;          MUST BE GREATER THAN ZERO.
+;   XYCOV - THE VECTOR OF COVARIANCES FOR THE MEASUREMENT ERRORS
+;           BETWEEN X AND Y.
+;   DELTA - AN NX-ELEMENT VECTOR INDICATING WHETHER A DATA POINT IS
+;           CENSORED OR NOT. IF DELTA[i] = 1, THEN THE SOURCE IS
+;           DETECTED, ELSE IF DELTA[i] = 0 THE SOURCE IS NOT DETECTED
+;           AND Y[i] SHOULD BE AN UPPER LIMIT ON Y[i]. NOTE THAT IF
+;           THERE ARE CENSORED DATA POINTS, THEN THE
+;           MAXIMUM-LIKELIHOOD ESTIMATE (THETA) IS NOT VALID. THE
+;           DEFAULT IS TO ASSUME ALL DATA POINTS ARE DETECTED, IE,
+;           DELTA = REPLICATE(1, NX).
+;   SILENT - SUPPRESS TEXT OUTPUT.
+;   MINITER - MINIMUM NUMBER OF ITERATIONS PERFORMED BY THE GIBBS
+;             SAMPLER. IN GENERAL, MINITER = 5000 SHOULD BE SUFFICIENT
+;             FOR CONVERGENCE. THE DEFAULT IS MINITER = 5000. THE
+;             GIBBS SAMPLER IS STOPPED AFTER RHAT < 1.1 FOR ALPHA,
+;             BETA, AND SIGMA^2, AND THE NUMBER OF ITERATIONS
+;             PERFORMED IS GREATER THAN MINITER.
+;   MAXITER - THE MAXIMUM NUMBER OF ITERATIONS PERFORMED BY THE
+;             MCMC. THE DEFAULT IS 1D5. THE GIBBS SAMPLER IS STOPPED
+;             AUTOMATICALLY AFTER MAXITER ITERATIONS.
+;   NGAUSS - THE NUMBER OF GAUSSIANS TO USE IN THE MIXTURE
+;            MODELLING. THE DEFAULT IS 3. 
+;
+; OUTPUT :
+;
+;    POST - A STRUCTURE CONTAINING THE RESULTS FROM THE GIBBS
+;           SAMPLER. EACH ELEMENT OF POST IS A DRAW FROM THE POSTERIOR
+;           DISTRIBUTION FOR EACH OF THE PARAMETERS.
+;
+;             ALPHA - THE CONSTANT IN THE REGRESSION.
+;             BETA - THE SLOPES OF THE REGRESSION.
+;             SIGSQR - THE VARIANCE OF THE INTRINSIC SCATTER.
+;             PI - THE GAUSSIAN WEIGHTS FOR THE MIXTURE MODEL.
+;             MU - THE GAUSSIAN MEANS FOR THE MIXTURE MODEL.
+;             T - THE GAUSSIAN COVARIANCE MATRICES FOR THE MIXTURE
+;                 MODEL.
+;             MU0 - THE HYPERPARAMETER GIVING THE MEAN VALUE OF THE
+;                   GAUSSIAN PRIOR ON MU.
+;             U - THE HYPERPARAMETER DESCRIBING FOR THE PRIOR
+;                 COVARIANCE MATRIX OF THE INDIVIDUAL GAUSSIAN
+;                 CENTROIDS ABOUT MU0.
+;             W - THE HYPERPARAMETER DESCRIBING THE `TYPICAL' SCALE
+;                 MATRIX FOR THE PRIOR ON (T,U).
+;             XIMEAN - THE MEAN OF THE DISTRIBUTION FOR THE
+;                      INDEPENDENT VARIABLE, XI.
+;             XIVAR - THE STANDARD COVARIANCE MATRIX FOR THE
+;                     DISTRIBUTION OF THE INDEPENDENT VARIABLE, XI.
+;             XICORR - SAME AS XIVAR, BUT FOR THE CORRELATION MATRIX.
+;             CORR - THE LINEAR CORRELATION COEFFICIENT BETWEEN THE
+;                    DEPENDENT AND INDIVIDUAL INDEPENDENT VARIABLES,
+;                    XI AND ETA.
+;             PCORR - SAME AS CORR, BUT FOR THE PARTIAL CORRELATIONS.
+;
+; CALLED ROUTINES :
+;
+;    RANDOMCHI, MRANDOMN, RANDOMWISH, RANDOMDIR, MULTINOM
+;
+; REFERENCES :
+;
+;   Carroll, R.J., Roeder, K., & Wasserman, L., 1999, Flexible
+;     Parametric Measurement Error Models, Biometrics, 55, 44
+;
+;   Kelly, B.C., 2007, Some Aspects of Measurement Error in
+;     Linear Regression of Astronomical Data, ApJ, In press
+;     (astro-ph/0705.2774)
+;
+;   Gelman, A., Carlin, J.B., Stern, H.S., & Rubin, D.B., 2004,
+;     Bayesian Data Analysis, Chapman & Hall/CRC
+;-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;routine to compute the inverse of the lower triangular matrix output
+;from the Cholesky decomposition
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function mlinmix_chol_invert, L
+
+n = n_elements(L[*,0])
+
+X = dblarr(n, n) ;X is the matrix inverse of L
+
+for i = 0, n - 1 do begin
+
+    X[i,i] = 1d / L[i,i]
+
+    if i lt n - 1 then begin
+
+        for j = i + 1, n - 1 do begin
+
+            sum = 0d
+            for k = i, j - 1 do sum = sum - L[k,j] * X[i,k]
+            X[i,j] = sum / L[j,j]
+
+        endfor
+
+    endif
+
+endfor
+
+return, X
+end
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;routine to compute the inverse of a symmetric positive-definite
+;matrix via the Cholesky decomposition
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+pro mlinmix_posdef_invert, A
+
+dim = n_elements(A[*,0])
+diag = lindgen(dim) * (dim + 1L)
+
+choldc, A, P, /double
+
+for j = 0, dim - 1 do for k = j, dim - 1 do A[k,j] = 0d
+
+A[diag] = P
+
+A = mlinmix_chol_invert(A)
+
+A = transpose(A) ## A
+
+return
+end
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;                                                                     ;
+;                             MAIN ROUTINE                            ;
+;                                                                     ;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+pro mlinmix_err, x, y, post, xvar=xvar, yvar=yvar, xycov=xycov, silent=silent, $
+                 delta=delta, miniter=miniter, maxiter=maxiter, ngauss=ngauss
+
+if n_params() lt 3 then begin
+
+    print, 'Syntax- MLINMIX_ERR, X, Y, POST, XVAR=XVAR, YVAR=YVAR, XYCOV=XYCOV,'
+    print, '                    NGAUSS=NGAUSS, /SILENT, DELTA=DELTA, '
+    PRINT, '                    MINITER=MINITER, MAXITER=MAXITER'
+    return
+
+endif
+
+;check inputs and setup defaults
+
+nx = size(x)
+
+if nx[0] ne 2 then begin
+    print, 'X must be an [NX,NP]-element array.'
+    return
+endif
+
+np = nx[2]
+nx = nx[1]
+
+if n_elements(y) ne nx then begin
+    print, 'Y and X must have the same size.'
+    return
+endif
+
+if n_elements(xvar) eq 0 and n_elements(yvar) eq 0 then begin
+    print, 'Must supply at least one of XVAR or YVAR.'
+    return
+endif
+
+xvar_size = size(xvar)
+
+if (xvar_size[0] ne 3) or (xvar_size[1] ne nx) or (xvar_size[2] ne np) or $
+  (xvar_size[3] ne np) then begin
+    print, 'XVAR must be an [NX,NP,NP]-element array.'
+    return
+endif
+
+if n_elements(yvar) ne nx then begin
+    print, 'YVAR and Y must have the same size.'
+    return
+endif
+
+if n_elements(xycov) eq 0 then xycov = dblarr(nx, np)
+
+if n_elements(xycov[*,0]) ne nx or n_elements(xycov[0,*]) ne np then begin
+    print, 'XYCOV must be an [NX,NP]-element array.'
+    return
+endif
+
+if n_elements(delta) eq 0 then delta = replicate(1, nx)
+if n_elements(delta) ne nx then begin
+    print, 'DELTA and X must have the same size.'
+    return
+endif
+
+diag = lindgen(np) * (np + 1)
+diag2 = lindgen(np+1) * (np + 2)
+
+zero = where(xvar[diag] eq 0 or yvar eq 0, nzero)
+if nzero gt 0 then begin
+    print, 'Measurement Errors in X and Y have to have non-zero variance.'
+    return
+endif
+
+det = where(delta eq 1, ndet, comp=cens, ncomp=ncens) ;get detected data points
+
+if not keyword_set(silent) then silent = 0
+if n_elements(miniter) eq 0 then miniter = 5000 ;minimum number of iterations that the 
+                                                ;Markov Chain must perform
+if n_elements(maxiter) eq 0 then maxiter = 100000L ;maximum number of iterations that the 
+                                                   ;Markov Chains will perform
+
+if n_elements(ngauss) eq 0 then ngauss = 3
+
+if ngauss le 0 then begin
+    print, 'NGAUSS must be at least 1.'
+    return
+endif
+
+;store covariance matrices for (x,y) measurement errors
+
+xyvar = dblarr(nx,np+1,np+1)
+
+xyvar[*,0,0] = yvar
+xyvar[*,1:*,0] = xycov
+xyvar[*,0,1:*] = xycov
+xyvar[*,1:*,1:*] = xvar
+
+;; perform MCMC
+
+nchains = 4                     ;number of markov chains to use
+checkiter = 100            ;check for convergence every 100 iterations
+iter = 0L
+
+;;;;;;;;;;;; get initial guesses for the MCMC
+
+;; first use moment correction method to estimate regression
+;; coefficients and intrinsic dispersion
+
+Xmat = [[replicate(1d, nx)], [x]]
+denom = matrix_multiply(Xmat, Xmat, /atranspose)
+Vcoef = denom
+denom[1:*,1:*] = denom[1:*,1:*] - median(xvar, dim=1)
+
+denom_diag = (denom[1:*,1:*])[diag]
+denom_diag = denom_diag > 0.025 * (Vcoef[1:*,1:*])[diag]
+denom[diag2[1:*]] = denom_diag
+numer = y ## transpose(Xmat) - [0d, median(xycov, dim=1)]
+
+choldc, denom, P, /double ;solve by cholesky decomposition
+coef = cholsol( denom, P, numer, /double )
+
+alpha = coef[0]
+beta = coef[1:*]
+
+sigsqr = variance(y) - mean(yvar) - $
+  beta ## (correlate(transpose(x), /covar) - median(xvar, dim=1)) ## transpose(beta)
+sigsqr = sigsqr[0] > 0.05 * variance(y - alpha - beta ## x)
+
+; randomly disperse starting values for (alpha, beta) from a
+; multivariate students-t distribution with 4 degrees of freedom
+
+mlinmix_posdef_invert, Vcoef
+Vcoef = Vcoef * sigsqr * 4d
+
+coef = mrandomn(seed, Vcoef, nchains)
+chisqr = randomchi(seed, 4, nchains)
+
+alphag = alpha + coef[*,0] * sqrt(4d / chisqr)
+betag = dblarr(np, nchains)
+for i = 0, nchains - 1 do betag[*,i] = beta + coef[i,1:*] * sqrt(4d / chisqr[i])
+
+;draw sigsqr from an Inverse scaled chi-square density
+sigsqrg = sigsqr * (nx / 2) / randomchi(seed, nx / 2, nchains)
+
+;; now get initial guesses for the mixture and prior parameters, do
+;; this one chain at a time
+
+pig = dblarr(ngauss, nchains)
+mug = dblarr(np, ngauss, nchains)
+Tg = dblarr(np, np, ngauss, nchains)
+mu0g = dblarr(np, nchains)
+Ug = dblarr(np, np, nchains)
+Wg = dblarr(np, np, nchains)
+
+dist = dblarr(nx, ngauss)
+Glabel = intarr(nx, nchains)
+
+for i = 0, nchains - 1 do begin
+    
+                                ;randomly choose NGAUSS data points,
+                                ;set these to the group means
+    ind = lindgen(nx)
+    unif = randomu(seed, nx)
+    ind = (ind[sort(unif)])[0:ngauss-1]
+
+    mug[*,*,i] = transpose(x[ind,*])
+    
+    if ngauss gt 1 then begin
+                                ;get distance of data points to each
+                                ;centroid
+        for k = 0, ngauss - 1 do $
+          dist[0,k] = total((x - mug[*,k,i] ## replicate(1d, nx))^2, 2)
+        
+        mindist = min(dist, Glabel0, dim=2) ;classify to closest centroid
+        
+        Glabel0 = Glabel0 / nx
+
+    endif else Glabel0 = intarr(nx)
+
+    Glabel[0,i] = Glabel0
+
+;now get initial guesses for PI and T
+
+    for k = 0, ngauss - 1 do begin
+
+        gk = where(Glabel0 eq k, nk)
+        
+        if nk gt np then begin
+
+            pig[k,i] = float(nk) / nx
+            Tg[*,*,k,i] = correlate(transpose(x[gk,*]), /covar)
+
+        endif else begin
+
+            pig[k,i] = (1d > nk) / nx
+            Tg[*,*,k,i] = correlate(transpose(x), /covar)
+
+        endelse
+
+    endfor
+
+    pig[*,i] = pig[*,i] / total(pig[*,i]) ;make sure Pi sums to unity
+
+;now get initial guesses for prior parameters
+
+    mu0g[*,i] = ngauss eq 1 ? mug[*,0,i] : total(mug[*,*,i], 2) / ngauss
+    Smat = correlate(transpose(x), /covar)
+    Ug[*,*,i] = randomwish(seed, nx, Smat / nx)
+
+    Wg[*,*,i] = randomwish(seed, nx, Smat / nx)
+
+endfor
+
+alpha = alphag
+beta = betag
+sigsqr = sigsqrg
+pi = pig
+mu = mug
+T = Tg
+mu0 = mu0g
+U = Ug
+W = Wg
+                                ;get inverses of XYVAR
+xyvar_inv = xyvar
+for i = 0, nx - 1 do begin
+    
+    xyvar_inv0 = reform(xyvar[i,*,*])
+    mlinmix_posdef_invert, xyvar_inv0
+    xyvar_inv[i,*,*] = xyvar_inv0
+    
+endfor
+                                ;get staring values for eta
+eta = dblarr(nx, nchains)
+for i = 0, nchains - 1 do eta[*,i] = y
+
+nut = np ;degrees of freedom for the prior on T
+nuu = np ;degrees of freedom for the prior on U
+
+npar = 2 + np ;number of parameters to monitor convergence on
+
+convergence = 0
+                                ;start Markov Chains
+if not silent then print, 'Simulating Markov Chains...'
+
+ygibbs = y
+                                ;define arrays now so we don't have to
+                                ;create them every MCMC iteration
+xi = dblarr(nx, np, nchains)
+for i = 0, nchains - 1 do xi[*,*,i] = x
+xstar = dblarr(nx, np)
+mustar = dblarr(nx, np)
+gamma = dblarr(nx, ngauss)
+nk = fltarr(ngauss)
+Tk_inv = dblarr(np, np, ngauss, nchains)
+U_inv = dblarr(np, np, nchains)
+
+                                ;get various matrix inverses before
+                                ;staring markov chain
+for i = 0, nchains - 1 do begin
+
+    for k = 0, ngauss - 1 do begin
+        
+        Tk_inv0 = T[*,*,k,i]
+        mlinmix_posdef_invert, Tk_inv0
+        
+        Tk_inv[*,*,k,i] = Tk_inv0
+        
+    endfor
+    
+    U_inv0 = U[*,*,i]
+    mlinmix_posdef_invert, U_inv0
+    U_inv[*,*,i] = U_inv0
+
+endfor
+
+repeat begin
+ 
+    for i = 0, nchains - 1 do begin ;do markov chains one at-a-time
+
+        W_inv = W[*,*,i]
+        mlinmix_posdef_invert, W_inv
+
+;do Gibbs sampler
+        if ncens gt 0 then begin
+                                ;first get new values of censored y
+            for j = 0, ncens - 1 do begin
+                
+                next = 0
+                repeat ygibbs[cens[j]] = eta[cens[j],i] + $
+                  sqrt(yvar[cens[j]]) * randomn(seed) $
+                  until ygibbs[cens[j]] le y[cens[j]]
+                
+            endfor
+            
+        endif
+        
+;need to get new values of Xi and Eta for Gibbs sampler
+        
+                                ;now draw Xi|mu,covar,x, do this for
+                                ;each covariate at a time
+        
+        for j = 0, np - 1 do begin
+            
+            case j of
+                
+                0 : inactive = indgen(np - 1) + 1L
+                np - 1 : inactive = indgen(np - 1)
+                else : inactive = [indgen(j), indgen(np - j - 1) + j + 1]
+                
+            endcase
+            
+            xstar[*,j] = x[*,j]
+            xstar[*,inactive] = x[*,inactive] - xi[*,inactive,i]
+            
+            zstar = [[ygibbs - eta[*,i]], [xstar]]
+            
+            zmu = total(xyvar_inv[*,*,j+1] * zstar, 2)
+            
+            for k = 0, ngauss - 1 do begin ;do one gaussian at-a-time
+                
+                gk = where(Glabel[*,i] eq k, ngk)
+                
+                if ngk gt 0 then begin
+                    
+                    mustar[gk,j] = mu[j,k,i]
+                    for l = 0, np - 2 do mustar[gk,inactive[l]] = $
+                      mu[inactive[l],k,i] - xi[gk,inactive[l],i]
+                    
+                    mmu = Tk_inv[*,j,k,i] ## mustar[gk,*]
+                    
+                    etamu = eta[gk,i] - alpha[i] - beta[inactive,i] ## xi[gk,inactive,i]
+                    
+                    xihvar = 1d / (xyvar_inv[gk,j+1,j+1] + Tk_inv[j,j,k,i] + $
+                                   beta[j,i]^2 / sigsqr[i])
+                    
+                    xihat = xihvar * (zmu[gk] + mmu + beta[j,i] * etamu / (sigsqr[i]))
+                    
+                    xi[gk,j,i] = xihat + sqrt(xihvar) * randomn(seed, nx)
+                    
+                endif
+                
+            endfor
+            
+        endfor
+                                ;now draw Eta|Xi,alpha,beta,sigsqr,y
+        zstar = [[ygibbs], [x - xi[*,*,i]]]
+        
+        zmu = total(xyvar_inv[*,*,0] * zstar, 2)
+        
+        ximu = (alpha[i] + beta[*,i] ## xi[*,*,i]) / sigsqr[i]
+        
+        etahvar = 1d / (xyvar_inv[*,0,0] + 1d / sigsqr[i])
+        etahat = etahvar * (zmu + ximu)
+        
+        eta[*,i] = etahat + sqrt(etahvar) * randomn(seed, nx)
+        
+                                ;now draw new class labels
+        if ngauss eq 1 then Glabel[*,i] = 0 else begin
+                                ;get unnormalized probability that
+                                ;source i came from Gaussian k, given
+                                ;xi[i]
+            for k = 0, ngauss - 1 do begin
+                
+                xicent = xi[*,*,i] - mu[*,k,i] ## replicate(1, nx)
+                gamma[0,k] = $
+                  pi[k,i] / ((2d*!pi)^(np/2d) * determ(T[*,*,k,i], /double)) * $
+                  exp(-0.5 * total(xicent * (Tk_inv[*,*,k,i] ## xicent), 2))
+                
+            endfor
+            
+            norm = total(gamma, 2)
+            
+            for j = 0, nx - 1 do begin
+                
+                gamma0 = reform(gamma[j,*]) / norm[j] ;normalized probability that the i-th 
+                                                      ;data point is from the k-th Gaussian, 
+                                                      ;given the observed data point 
+                Gjk = multinom(1, gamma0, seed=seed)
+                
+                Glabel[j,i] = where(Gjk eq 1)
+                
+            endfor
+            
+        endelse
+
+;; now draw new values of alpha, beta, and sigsqr
+        
+                                ;first do alpha,beta|Xi,Eta,sigsqr
+        
+        Xmat[*,1:*] = xi[*,*,i]
+        
+        hatmat = matrix_multiply(Xmat, Xmat, /atranspose)
+        Vcoef = hatmat
+        
+        choldc, hatmat, P, /double ;solve by cholesky decomposition
+        coefhat = cholsol( hatmat, P, eta[*,i] ## transpose(Xmat), /double )
+        
+        mlinmix_posdef_invert, Vcoef
+        Vcoef = Vcoef * sigsqr[i]
+        
+        coef = coefhat + mrandomn(seed, Vcoef)
+        
+        alpha[i] = coef[0]
+        beta[*,i] = coef[1:*]
+        
+                                ;now do sigsqr|xi,eta,alpha,beta, 
+                                ;draw sigsqr from a scaled
+                                ;Inverse-chi-square density
+        resid = eta[*,i] - alpha[i] - beta[*,i] ## xi[*,*,i]
+        ssqr = total( resid^2 ) / (nx - 2d)
+        
+        sigsqr[i] = ssqr * (nx - 2d) / randomchi(seed, nx - 2)
+
+;; now do mixture model parameters, psi = (pi,mu,tausqr)
+
+        for k = 0, ngauss - 1 do begin 
+            
+            gk = where(Glabel[*,i] eq k, ngk)
+            nk[k] = ngk
+           
+            if ngk gt 0 then begin
+                                ;get mu|Xi,G,tausqr,mu0,U
+                
+                muvar = U_inv[*,*,i] + ngk * Tk_inv[*,*,k,i]
+                mlinmix_posdef_invert, muvar
+                
+                xibar = total(xi[gk,*,i], 1) / ngk
+                
+                muhat = (mu0[*,i] ## U_inv[*,*,i] + $
+                         ngk * (xibar ## Tk_inv[*,*,k,i])) ## muvar
+                
+                mu[*,k,i] = muhat + mrandomn(seed, muvar)
+                
+            endif else mu[*,k,i] = mu0[*,i] + mrandomn(seed, U[*,*,i])
+
+                                ;get T|Xi,G,mu,W,nut
+            nuk = ngk + nut
+
+            if ngk gt 0 then begin
+                
+                xicent = xi[gk,*,i] - mu[*,k,i] ## replicate(1d, ngk)
+                
+                Smat = W[*,*,i] + xicent ## transpose(xicent)
+                
+                Smat_inv = Smat
+                mlinmix_posdef_invert, Smat_inv
+
+            endif else begin
+
+                Smat = W
+                Smat_inv = W_inv
+
+            endelse
+
+            Tmat = randomwish(seed, nuk, Smat_inv)
+            
+            Tk_inv[*,*,k,i] = Tmat
+            mlinmix_posdef_invert, Tmat
+            T[*,*,k,i] = Tmat
+            
+        endfor
+                                ;get pi|G
+        if ngauss eq 1 then pi[*,i] = 1d else $
+          pi[*,i] = randomdir(seed, nk + 1)
+        
+;; now, finally update the prior parameters    
+        
+                                ;first update mean of gaussian
+                                ;centroids
+        mu0[*,i] = ngauss eq 1 ? mu[*,0,i] + mrandomn(seed, U[*,*,i]) : $
+          total(mu[*,*,i], 2) / ngauss + mrandomn(seed, U[*,*,i] / ngauss)
+
+                                ;update centroid covariance matrix, U
+        nu = ngauss + nuu
+        
+        mucent = ngauss eq 1 ? transpose(mu[*,0,i] - mu0[*,i]) : $
+          transpose(mu[*,*,i]) - mu0[*,i] ## replicate(1d, ngauss)
+        
+        Uhat = W[*,*,i] + mucent ## transpose(mucent)
+        
+        mlinmix_posdef_invert, Uhat
+        Umat = randomwish(seed, nu, Uhat)
+        
+        U_inv[*,*,i] = Umat
+        mlinmix_posdef_invert, Umat
+        U[*,*,i] = Umat
+ 
+                                ;update the common scale matrix, W
+        nuw = (ngauss + 2) * np + 1
+        What = ngauss eq 1 ? U_inv[*,*,i] + Tk_inv[*,*,0,i] : $
+          U_inv[*,*,i] + total(Tk_inv[*,*,*,i], 3)
+        
+        mlinmix_posdef_invert, What
+        
+        W[*,*,i] = randomwish(seed, nuw, What)
+
+    endfor
+                                ;save Markov Chains
+    if iter eq 0 then begin
+        
+        alphag = alpha
+        betag = beta[*]
+        sigsqrg = sigsqr
+        
+        pig = pi[*]
+        mug = mu[*]
+        Tg = T[*]
+        
+        mu0g = mu0[*]
+        Ug = U[*]
+        Wg = W[*]
+    
+    endif else begin
+        
+        alphag = [alphag, alpha]
+        betag = [betag, beta[*]]
+        sigsqrg = [sigsqrg, sigsqr]
+
+        pig = [pig, pi[*]]
+        mug = [mug, mu[*]]
+        Tg = [Tg, T[*]]
+
+        mu0g = [mu0g, mu0[*]]
+        Ug = [Ug, U[*]]
+        Wg = [Wg, W[*]]
+
+    endelse
+    
+    iter = iter + 1L
+    
+;check for convergence
+    
+    if iter ge 4 then begin
+        
+        Bvar = dblarr(npar)     ;between-chain variance
+        Wvar = dblarr(npar)     ;within-chain variance
+        
+        ndraw = n_elements(alphag) / nchains
+        
+        psi = dblarr(npar, nchains, ndraw)
+        psi[0,*,*] = reform(alphag, nchains, ndraw)
+        psi[1:np,*,*] = reform(betag, np, nchains, ndraw)
+        psi[np+1,*,*] = alog(reform(sigsqrg, nchains, ndraw))
+        
+        psi = psi[*,*,(ndraw+1)/2:*]
+        ndraw = ndraw / 2
+                                ;calculate between- and within-sequence
+                                ;                  variances
+        for j = 0, npar - 1 do begin
+            
+            psibarj = total( psi[j,*,*], 3 ) / ndraw
+            psibar = mean(psibarj)
+            
+            sjsqr = 0d
+            for i = 0, nchains - 1 do $
+              sjsqr = sjsqr + total( (psi[j, i, *] - psibarj[i])^2 ) / (ndraw - 1.0)
+            
+            Bvar[j] = ndraw / (nchains - 1.0) * total( (psibarj - psibar)^2 )
+            Wvar[j] = sjsqr / nchains
+            
+        endfor
+        
+        varplus = (1.0 - 1d / ndraw) * Wvar + Bvar / ndraw
+        Rhat = sqrt( varplus / Wvar ) ;potential variance scale reduction factor
+        
+    endif
+    
+    if iter eq checkiter then begin 
+;maximum iterations reached, now assess convergence
+
+        if (total( (Rhat le 1.1) ) eq npar and iter ge miniter) or $
+          iter ge maxiter then convergence = 1 $
+        else begin
+            
+            if not silent then begin
+                print, 'Iteration: ', iter
+                print, 'Rhat Values (ALPHA, BETA, SIGSQR) : '
+                print, Rhat
+            endif
+            
+            checkiter = checkiter + 100L
+            
+        endelse
+
+    endif
+    
+endrep until convergence
+
+ndraw = n_elements(alphag) / nchains
+
+alphag = reform(alphag, nchains, ndraw)
+betag = reform(betag, np, nchains, ndraw)
+sigsqrg = reform(sigsqrg, nchains, ndraw)
+
+pig = reform(pig, ngauss, nchains, ndraw)
+mug = reform(mug, np, ngauss, nchains, ndraw)
+Tg = reform(Tg, np, np, ngauss, nchains, ndraw)
+
+mu0g = reform(mu0g, np, nchains, ndraw)
+Ug = reform(Ug, np, np, nchains, ndraw)
+Wg = reform(Wg, np, np, nchains, ndraw)
+
+;only keep second half of markov chains
+alphag = alphag[*,(ndraw+1)/2:*]
+betag = betag[*,*,(ndraw+1)/2:*]
+sigsqrg = sigsqrg[*,(ndraw+1)/2:*]
+pig = pig[*,*,(ndraw+1)/2:*]
+mug = mug[*,*,*,(ndraw+1)/2:*]
+Tg = Tg[*,*,*,*,(ndraw+1)/2:*]
+mu0g = mu0g[*,*,(ndraw+1)/2:*]
+Ug = Ug[*,*,*,(ndraw+1)/2:*]
+Wg = Wg[*,*,*,(ndraw+1)/2:*]
+
+if not silent then begin
+    print, 'Iteration: ', iter
+    print, 'Rhat Values (ALPHA, BETA, SIGSQR) : ', Rhat
+endif
+
+;save posterior draws in a structure
+ndraw = ndraw / 2
+
+
+if ngauss gt 1 then $
+  post = {alpha:0d, beta:dblarr(np), sigsqr:0d, pi:dblarr(ngauss), mu:dblarr(np,ngauss), $
+          T:dblarr(np,np,ngauss), mu0:dblarr(np), U:dblarr(np,np), W:dblarr(np,np), $
+          ximean:dblarr(np), xivar:dblarr(np,np), xicorr:dblarr(np,np), corr:dblarr(np), $
+          pcorr:dblarr(np)} $
+else $
+  post = {alpha:0d, beta:dblarr(np), sigsqr:0d, pi:0d, mu:dblarr(np), $
+          T:dblarr(np,np), mu0:dblarr(np), U:dblarr(np,np), W:dblarr(np,np), $
+          ximean:dblarr(np), xivar:dblarr(np,np), xicorr:dblarr(np,np), corr:dblarr(np), $
+          pcorr:dblarr(np)}
+
+post = replicate(post, ndraw * nchains)
+
+post.alpha = alphag[*]
+post.beta = reform(betag, np, ndraw * nchains)
+post.sigsqr = sigsqrg[*]
+
+if ngauss gt 1 then begin
+
+    post.pi = reform(pig, ngauss, ndraw * nchains)
+    post.mu = reform(mug, np, ngauss, ndraw * nchains)
+    post.T = reform(Tg, np, np, ngauss, ndraw * nchains)
+
+endif else begin
+
+    post.pi = reform(pig, ndraw * nchains)
+    post.mu = reform(mug, np, ndraw * nchains)
+    post.T = reform(Tg, np, np, ndraw * nchains)
+
+endelse
+
+post.mu0 = reform(mu0g, np, ndraw * nchains)
+post.U = reform(Ug, np, np, ndraw * nchains)
+post.W = reform(Wg, np, np, ndraw * nchains)
+
+;get posterior draws of moments of distribution
+
+if not silent then print, 'Getting Posterior Draws for Various Moments...'
+
+corrmat = dblarr(np+1,np+1)
+
+for i = 0, ndraw * nchains - 1 do begin
+                                ;average value of Xi
+    post[i].ximean = ngauss gt 1 ? post[i].pi ## post[i].mu : post[i].mu
+
+    if ngauss eq 1 then post[i].xivar = post[i].T else begin
+
+        for k = 0, ngauss - 1 do post[i].xivar = post[i].xivar + $
+          post[i].pi[k] * (post[i].T[*,*,k] + transpose(post[i].mu[*,k]) ## post[i].mu[*,k])
+                                ;covariance matrix of Xi
+        post[i].xivar = post[i].xivar - transpose(post[i].ximean) ## post[i].ximean
+
+    endelse       
+    
+    xivar = post[i].xivar
+    
+                                ;variance in Eta
+    etavar = post[i].beta ## post[i].xivar ## transpose(post[i].beta) + post[i].sigsqr
+                                ;correlation coefficients between Eta
+                                ;and Xi
+    post[i].corr = post[i].beta ## post[i].xivar / $
+      sqrt( etavar[0] * post[i].xivar[diag] )
+                                ;correlation matrix of the covariates
+    post[i].xicorr = xivar * ( transpose(1d / sqrt(xivar[diag])) ## (1d / sqrt(xivar[diag])) )
+                                ;now get partial correlations, need
+                                ;full correlation matrix first
+    corrmat[0,0] = 1d
+    corrmat[1:*,0] = post[i].corr
+    corrmat[0,1:*] = post[i].corr
+    corrmat[1:*,1:*] = post[i].xicorr
+
+    mlinmix_posdef_invert, corrmat
+
+    post[i].pcorr = -1d * corrmat[1:*,0] / sqrt(corrmat[0,0] * corrmat[diag2[1:*]])
+
+endfor
+
+return
+end
diff --git a/Code/script_idl_mv/astrolib/mmm.pro b/Code/script_idl_mv/astrolib/mmm.pro
new file mode 100644
index 0000000000000000000000000000000000000000..cd0dd15d2f1298646fe880a15c90c823fe46fbc9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mmm.pro
@@ -0,0 +1,310 @@
+pro mmm, sky_vector, skymod, sigma , skew, HIGHBAD = highbad, DEBUG = debug, $
+           ReadNoise = readnoise, Nsky = nsky, INTEGER = discrete, $
+	   MAXITER = mxiter, SILENT = silent, MINSKY = minsky
+;+
+; NAME:
+;       MMM
+; PURPOSE: 
+;       Estimate the sky background in a stellar contaminated field.
+; EXPLANATION:  
+;       MMM assumes that contaminated sky pixel values overwhelmingly display 
+;       POSITIVE departures from the true value.  Adapted from DAOPHOT 
+;       routine of the same name.
+;
+; CALLING SEQUENCE:
+;       MMM, sky, [ skymod, sigma, skew, HIGHBAD = , READNOISE=, /DEBUG, 
+;                  MINSKY=, NSKY=, /INTEGER,/SILENT]
+;
+; INPUTS:
+;       SKY - Array or Vector containing sky values.  This version of
+;               MMM does not require SKY to be sorted beforehand.  SKY
+;               is unaltered by this program.
+;
+; OPTIONAL OUTPUTS:
+;       skymod - Scalar giving estimated mode of the sky values (float)
+;       SIGMA -  Scalar giving standard deviation of the peak in the sky
+;               histogram.  If for some reason it is impossible to derive
+;               skymod, then SIGMA = -1.0
+;       SKEW -   Scalar giving skewness of the peak in the sky histogram
+;
+;               If no output variables are supplied or if /DEBUG is set
+;               then the values of skymod, SIGMA and SKEW will be printed.
+;
+; OPTIONAL KEYWORD INPUTS:
+;       HIGHBAD - scalar value of the (lowest) "bad" pixel level (e.g. cosmic 
+;                rays or saturated pixels) If not supplied, then there is 
+;                assumed to be no high bad pixels.
+;       MINSKY - Integer giving mininum number of sky values to be used.   MMM
+;                will return an error if fewer sky elements are supplied.
+;                Default = 20.
+;       MAXITER - integer giving maximum number of iterations allowed,default=50
+;       READNOISE - Scalar giving the read noise (or minimum noise for any 
+;                pixel).     Normally, MMM determines the (robust) median by 
+;                averaging the central 20% of the sky values.     In some cases
+;                where the noise is low, and pixel values are quantized a
+;                larger fraction may be needed.    By supplying the optional
+;                read noise parameter, MMM is better able to adjust the
+;                fraction of pixels used to determine the median.                
+;       /INTEGER - Set this keyword if the  input SKY vector only contains
+;                discrete integer values.    This keyword is only needed if the
+;                SKY vector is of type float or double precision, but contains 
+;                only discrete integer values.     (Prior to July 2004, the
+;                equivalent of /INTEGER was set for all data types)
+;       /DEBUG - If this keyword is set and non-zero, then additional 
+;               information is displayed at the terminal.
+;       /SILENT - If set, then error messages will be suppressed when MMM
+;                cannot compute a background.    Sigma will still be set to -1
+; OPTIONAL OUTPUT KEYWORD:
+;      NSKY - Integer scalar giving the number of pixels actually used for the
+;             sky computation (after outliers have been removed).
+; NOTES:
+;       (1) Program assumes that low "bad" pixels (e.g. bad CCD columns) have
+;       already been deleted from the SKY vector.
+;       (2) MMM was updated in June 2004 to better match more recent versions
+;       of DAOPHOT.
+;       (3) Does not work well in the limit of low Poisson integer counts
+;       (4) MMM may fail for strongly skewed distributions.
+; METHOD:
+;       The algorithm used by MMM consists of roughly two parts:
+;       (1) The average and sigma of the sky pixels is computed.   These values
+;       are used to eliminate outliers, i.e. values with a low probability
+;       given a Gaussian with specified average and sigma.   The average
+;       and sigma are then recomputed and the process repeated up to 20
+;       iterations:
+;       (2) The amount of contamination by stars is estimated by comparing the 
+;       mean and median of the remaining sky pixels.   If the mean is larger
+;       than the median then the true sky value is estimated by
+;       3*median - 2*mean
+;         
+; REVISION HISTORY:
+;       Adapted to IDL from 1986 version of DAOPHOT in STSDAS, 
+;       W. Landsman, STX Feb 1987
+;       Added HIGHBAD keyword, W. Landsman January, 1991
+;       Fixed occasional problem with integer inputs    W. Landsman  Feb, 1994
+;       Avoid possible 16 bit integer overflow   W. Landsman  November 2001
+;       Added READNOISE, NSKY keywords,  new median computation   
+;                          W. Landsman   June 2004
+;       Added INTEGER keyword W. Landsman July 2004
+;       Improve numerical precision  W. Landsman  October 2004
+;       Fewer aborts on strange input sky histograms W. Landsman October 2005
+;       Added /SILENT keyword  November 2005
+;       Fix too many /CON keywords to MESSAGE  W.L. December 2005
+;       Fix bug introduced June 2004 removing outliers when READNOISE not set
+;         N. Cunningham/W. Landsman  January 2006
+;       Make sure that MESSAGE never aborts  W. Landsman   January 2008
+;       Add mxiter keyword and change default to 50  W. Landsman August 2011
+;       Added MINSKY keyword W.L. December 2011
+;       Always return floating point sky mode  W.L.  December 2015
+;-
+ compile_opt idl2
+ On_error,2               ;Return to caller
+ if N_params() EQ 0 then begin          
+        print,'Syntax:  MMM, sky, skymod, sigma, skew, [/INTEGER, /SILENT' 
+        print,'              [HIGHBAD = , READNOISE =, /DEBUG, MXITER=, NSKY=] '
+        return
+ endif
+
+ silent = keyword_set(SILENT)
+              ;Maximum number of iterations allowed
+ if N_elements(mxiter) EQ 0 then mxiter = 50
+ if N_elements(minsky) Eq 0 then minsky = 20   ;Minimum number of legal sky elements
+ nsky = N_elements( sky_vector )            ;Get number of sky elements 
+ 
+  if nsky LT minsky then begin
+      sigma=-1.0 &  skew = 0.0
+      message,/CON, NoPrint= Silent, $
+    'ERROR -Input vector must contain at least '+strtrim(minsky,2)+' elements'
+      return
+ endif
+ 
+ nlast = nsky-1                        ;Subscript of last pixel in SKY array
+ if keyword_set(DEBUG) then $
+     message,'Processing '+strtrim(nsky,2) + ' element array',/INF
+ sz_sky = size(sky_vector,/structure)
+ integer = keyword_set(discrete)
+ if ~integer then integer = (sz_sky.type LT 4) or (sz_sky.type GT 11) 
+ sky = sky_vector[ sort( sky_vector ) ]    ;Sort SKY in ascending values
+
+ skymid = 0.5*sky[(nsky-1)/2] + 0.5*sky[nsky/2] ;Median value of all sky values  
+       
+ cut1 = min( [skymid-sky[0],sky[nsky-1] - skymid] ) 
+ if N_elements(highbad) EQ 1 then cut1 = cut1 < (highbad - skymid)
+ cut2 = skymid + cut1
+ cut1 = skymid - cut1
+         
+; Select the pixels between Cut1 and Cut2
+
+ good = where( (sky LE cut2) and (sky GE cut1), Ngood ) 
+ if ( Ngood EQ 0 ) then begin
+      sigma=-1.0 &  skew = 0.0   
+      message,/CON, NoPrint=Silent, $ 
+           'ERROR - No sky values fall within ' + strtrim(cut1,2) + $
+	   ' and ' + strtrim(cut2,2)           
+      return
+   endif
+  
+ delta = sky[good] - skymid  ;Subtract median to improve arithmetic accuracy
+ sum = total(delta,/double)                     
+ sumsq = total(delta^2,/double)
+
+ maximm = max( good,MIN=minimm )  ;Highest value accepted at upper end of vector
+ minimm = minimm -1               ;Highest value reject at lower end of vector
+
+; Compute mean and sigma (from the first pass).
+
+ skymed = 0.5*sky[(minimm+maximm+1)/2] + 0.5*sky[(minimm+maximm)/2 + 1] ;median 
+ skymn = float(sum/(maximm-minimm))                            ;mean       
+ sigma = sqrt(sumsq/(maximm-minimm)-skymn^2)             ;sigma          
+ skymn = skymn + skymid         ;Add median which was subtracted off earlier 
+
+
+;    If mean is less than the mode, then the contamination is slight, and the
+;    mean value is what we really want.
+skymod =  (skymed LT skymn) ? 3.*skymed - 2.*skymn : skymn
+
+; Rejection and recomputation loop:
+
+ niter = 0
+ clamp = 1
+ old = 0                            
+START_LOOP:
+   niter = niter + 1                     
+   if ( niter GT mxiter ) then begin
+      sigma=-1.0 &  skew = 0.0   
+      message,/CON, NoPrint=Silent, $ 
+           'ERROR - Too many ('+strtrim(mxiter,2) + ') iterations,' + $
+           ' unable to compute sky'
+      return
+   endif
+
+   if ( maximm-minimm LT minsky ) then begin    ;Error? 
+
+      sigma = -1.0 &  skew = 0.0   
+      message,/CON,NoPrint=Silent, $
+            'ERROR - Too few ('+strtrim(maximm-minimm,2) +  $
+           ') valid sky elements, unable to compute sky'
+      return
+   endif 
+
+; Compute Chauvenet rejection criterion.
+
+    r = alog10( float( maximm-minimm ) )      
+    r = max( [ 2., ( -0.1042*r + 1.1695)*r + 0.8895 ] )
+
+; Compute rejection limits (symmetric about the current mode).
+
+    cut = r*sigma + 0.5*abs(skymn-skymod)   
+    if integer then cut = cut > 1.5 
+    cut1 = skymod - cut   &    cut2 = skymod + cut
+; 
+; Recompute mean and sigma by adding and/or subtracting sky values
+; at both ends of the interval of acceptable values.
+      
+    redo = 0B
+    newmin = minimm             
+    tst_min = sky[newmin+1] GE cut1      ;Is minimm+1 above current CUT?
+    done = (newmin EQ -1) and tst_min    ;Are we at first pixel of SKY?
+    if ~done then  $
+        done =  (sky[newmin>0] LT cut1) and tst_min
+    if ~done then begin
+        istep = 1 - 2*fix(tst_min)
+        repeat begin
+                newmin = newmin + istep
+                done = (newmin EQ -1) || (newmin EQ nlast)
+                if ~done then $
+                    done = (sky[newmin] LE cut1) and (sky[newmin+1] GE cut1)
+        endrep until done
+        if tst_min then delta = sky[newmin+1:minimm] - skymid $
+                   else delta = sky[minimm+1:newmin] - skymid
+        sum = sum - istep*total(delta,/double)
+        sumsq = sumsq - istep*total(delta^2,/double)
+        redo = 1b
+        minimm = newmin
+     endif
+;       
+   newmax = maximm
+   tst_max = sky[maximm] LE cut2           ;Is current maximum below upper cut?
+   done = (maximm EQ nlast) and tst_max    ;Are we at last pixel of SKY array?
+   if ~done then $   
+       done = ( tst_max ) && (sky[(maximm+1)<nlast] GT cut2) 
+    if ~done then begin                 ;Keep incrementing NEWMAX
+       istep = -1 + 2*fix(tst_max)         ;Increment up or down?
+       Repeat begin
+          newmax = newmax + istep
+          done = (newmax EQ nlast) or (newmax EQ -1)
+          if ~done then $
+                done = ( sky[newmax] LE cut2 ) and ( sky[newmax+1] GE cut2 )
+       endrep until done
+       if tst_max then delta = sky[maximm+1:newmax] - skymid $
+               else delta = sky[newmax+1:maximm] - skymid
+       sum = sum + istep*total(delta,/double)
+       sumsq = sumsq + istep*total(delta^2,/double)
+       redo = 1b
+       maximm = newmax
+    endif
+;       
+; Compute mean and sigma (from this pass).
+;
+   nsky = maximm - minimm
+   if ( nsky LT minsky ) then begin    ;Error? 
+       sigma = -1.0 &  skew = 0.0   
+       message,NoPrint=Silent, /CON, $ 
+               'ERROR - Outlier rejection left too few sky elements'
+       return		 		 
+   endif 
+
+   skymn = float(sum/nsky)       
+   sigma = float( sqrt( (sumsq/nsky - skymn^2)>0 ))
+    skymn = skymn + skymid 
+                
+
+;  Determine a more robust median by averaging the central 20% of pixels.
+;  Estimate the median using the mean of the central 20 percent of sky
+;  values.   Be careful to include a perfectly symmetric sample of pixels about
+;  the median, whether the total number is even or odd within the acceptance
+;  interval
+    
+        center = (minimm + 1 + maximm)/2.
+        side = round(0.2*(maximm-minimm))/2.  + 0.25
+        J = round(CENTER-SIDE)
+        K = round(CENTER+SIDE)
+
+;  In case  the data has a large number of of the same (quantized) 
+;  intensity, expand the range until both limiting values differ from the 
+;  central value by at least 0.25 times the read noise.
+
+        if keyword_set(readnoise) then begin        
+          L = round(CENTER-0.25)
+          M = round(CENTER+0.25)
+          R = 0.25*readnoise
+          while ((J GT 0) && (K LT Nsky-1) && $
+            ( ((sky[L] - sky[J]) LT R) || ((sky[K] - sky[M]) LT R))) do begin
+             J--
+             K++
+        endwhile
+        endif 
+   skymed = total(sky[j:k])/(k-j+1)
+   
+;  If the mean is less than the median, then the problem of contamination
+;  is slight, and the mean is what we really want.
+
+   dmod = skymed LT skymn ?  3.*skymed-2.*skymn-skymod : skymn - skymod
+
+; prevent oscillations by clamping down if sky adjustments are changing sign
+   if dmod*old LT 0 then clamp = 0.5*clamp
+   skymod = skymod + clamp*dmod 
+   old = dmod     
+   if redo then goto, START_LOOP
+
+;       
+ skew = float( (skymn-skymod)/max([1.,sigma]) )
+ nsky = maximm - minimm 
+
+ if keyword_set(DEBUG) or ( N_params() EQ 1 ) then begin
+        print, '% MMM: Number of unrejected sky elements: ', strtrim(nsky,2), $
+              '    Number of iterations: ',  strtrim(niter,2)
+        print, '% MMM: Mode, Sigma, Skew of sky vector:', skymod, sigma, skew   
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/modfits.pro b/Code/script_idl_mv/astrolib/modfits.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a922f9beb1e538a56a5cb19afed90f9b04f0c366
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/modfits.pro
@@ -0,0 +1,321 @@
+pro MODFITS, filename, data, header, EXTEN_NO = exten_no, ERRMSG = errmsg, $
+    EXTNAME = extname
+           
+;+
+; NAME:
+;      MODFITS
+; PURPOSE:
+;      Modify a FITS file by updating the header and/or data array.  
+; EXPLANATION:
+;      Update the data and/or header in a specified FITS extension or primary
+;      HDU.
+;    
+;      The size of the supplied FITS header or data array does not
+;      need to match the size of the existing header or data array.
+;
+; CALLING SEQUENCE:
+;      MODFITS, Filename_or_fcb, Data, [ Header, EXTEN_NO =, EXTNAME= , ERRMSG=]
+;
+; INPUTS:
+;      FILENAME/FCB = Scalar string containing either the name of the FITS file  
+;                  to be modified, or the IO file control block returned after 
+;                  opening the file with FITS_OPEN,/UPDATE.   The explicit
+;                  use of FITS_OPEN can save time if many extensions in a 
+;                  single file will be updated.
+;
+;      DATA - data array to be inserted into the FITS file.   Set DATA = 0
+;               to leave the data portion of the FITS file unmodified.   Data
+;               can also be an IDL structure (e.g. as returned by MRDFITS). 
+;               provided that it does not include IDL pointers.
+;
+;      HEADER - FITS header (string array) to be updated in the FITS file.
+;
+; OPTIONAL INPUT KEYWORDS:
+;      A specific extension can be specified with either the EXTNAME or
+;      EXTEN_NO keyword
+; 
+;      EXTEN_NO - scalar integer specifying the FITS extension to modified.  For
+;               example, specify EXTEN = 1 or /EXTEN to modify the first 
+;               FITS extension.
+;      EXTNAME - string name of the extension to modify.   
+;
+;
+; OPTIONAL OUTPUT KEYWORD:
+;       ERRMSG - If this keyword is supplied, then any error mesasges will be
+;               returned to the user in this parameter rather than depending on
+;               on the MESSAGE routine in IDL.   If no errors are encountered
+;               then a null string is returned.               
+;
+; EXAMPLES:
+;     (1) Modify the value of the DATE keyword in the primary header of a 
+;             file TEST.FITS.
+;
+;              IDL> h = headfits('test.fits')      ;Read primary header
+;              IDL> sxaddpar,h,'DATE','2015-03-23' ;Modify value of DATE 
+;              IDL> modfits,'test.fits',0,h        ;Update header only
+;
+;       (2) Replace the values of the primary image array in 'test.fits' with 
+;               their absolute values
+;
+;               IDL> im = readfits('test.fits')    ;Read image array
+;               IDL> im = abs(im)                  ;Take absolute values
+;               IDL> modfits,'test.fits',im        ;Update image array
+;
+;       (3) Add some HISTORY records to the FITS header in the first extension
+;               of a file 'test.fits'
+;       
+;               IDL> h = headfits('test.fits',/ext)  ;Read first extension hdr
+;               IDL> sxaddhist,['Comment 1','Comment 2'],h
+;               IDL> modfits,'test.fits',0,h,/ext    ;Update extension hdr
+;
+;       (4) Change 'OBSDATE' keyword to 'OBS-DATE' in every extension in a 
+;           FITS file.    Explicitly open with FITS_OPEN to save compute time.
+;
+;               fits_open,'test.fits',io,/update    ;Faster to explicity open
+;               for i = 1,nextend do begin          ;Loop over extensions
+;                   fits_read,io,0,h,/header_only,exten_no=i,/No_PDU ;Get header     
+;                   date= sxpar(h,'OBSDATE')         ;Save keyword value
+;                   sxaddpar,h,'OBS-DATE',date,after='OBSDATE' 
+;                   sxdelpar,h,'OBSDATE'             ;Delete bad keyword
+;                   modfits,io,0,h,exten_no=i        ;Update header
+;               endfor
+;
+;           Note the use of the /No_PDU keyword in the FITS_READ call -- one 
+;           does *not* want to append the primary header, if the STScI 
+;           inheritance convention is adopted.
+;
+; NOTES:
+;       Uses the BLKSHIFT procedure to shift the contents of the FITS file if 
+;       the new data or header differs in size by more than 2880 bytes from the
+;       old data or header.    If a file control block (FCB) structure is 
+;       supplied, then the values of START_HEADER, START_DATA and NBYTES may 
+;       be modified if the file size changes.
+;
+;       Also see the procedures FXHMODIFY to add a single FITS keyword to a 
+;       header in a FITS files, and FXBGROW to enlarge the size of a binary 
+;       table.
+;       
+; RESTRICTIONS:
+;       (1) Cannot be used to modify the data in FITS files with random 
+;           groups or variable length binary tables.   (The headers in such
+;           files *can* be modified.)
+;
+;       (2) If a data array but no FITS header is supplied, then MODFITS does 
+;           not check to make sure that the existing header is consistent with
+;           the new data.
+;
+;       (3) Does not work with compressed files
+;
+;       (4) The Checksum keywords will not be updated if the array to be 
+;           updated is supplied as a structure (e.g. from MRDFITS). 
+; PROCEDURES USED:
+;       Functions:   N_BYTES(), SXPAR()
+;       Procedures:  BLKSHIFT, CHECK_FITS, FITS_OPEN, FITS_READ. SETDEFAULTVALUE
+;
+; MODIFICATION HISTORY:
+;       Written,    Wayne Landsman          December, 1994
+;       Fixed possible problem when using WRITEU after READU   October 1997
+;       New and old sizes need only be the same within multiple of 2880 bytes
+;       Added call to IS_IEEE_BIG()     W. Landsman   May 1999
+;       Added ERRMSG output keyword     W. Landsman   May 2000
+;       Update tests for incompatible sizes   W. Landsman   December 2000
+;       Major rewrite to use FITS_OPEN procedures W. Landsman November 2001
+;       Add /No_PDU call to FITS_READ call  W. Landsman  June 2002
+;       Update CHECKSUM keywords if already present in header, add padding 
+;       if new data  size is smaller than old  W.Landsman December 2002
+;       Only check XTENSION value if EXTEN_NO > 1   W. Landsman Feb. 2003
+;       Correct for unsigned data on little endian machines W. Landsman Apr 2003
+;       Major rewrite to allow changing size of data or header W.L. Aug 2003
+;       Fixed case where updated header exactly fills boundary W.L. Feb 2004
+;       More robust error reporting W.L. Dec 2004
+;       Make sure input header ends with a END W.L.  March 2006
+;       Assume since V5.5, remove VMS support, assume FITS_OPEN will
+;           perform byte swapping   W.L. Sep 2006 
+;       Update FCB structure if file size changes W.L. March 2007
+;       Fix problem when data size must be extended W.L. August 2007
+;       Don't assume supplied FITS header is 80 bytes W. L. Dec 2007
+;       Check for new END position after adding CHECKSUM  W.L. July 2008
+;       Added EXTNAME input keyword  W.L. July 2008
+;       Allow data to be an IDL structure  A. Conley/W.L. June 2009
+;       Use V6.0 notation, add /NOZERO to BLKSHIFT W.L. Feb 2011
+;       Don't try to update Checksums when structure supplied W.L. April 2011
+;       Allow structure with only 1 element  W.L.  Feb 2012
+;       Don't require that a FITS header is supplied W.L.  Feb 2016
+;-
+  On_error,2                    ;Return to user
+  compile_opt idl2
+
+; Check for filename input
+
+   if N_params() LT 1 then begin                
+      print,'Syntax - ' + $
+        'MODFITS, Filename, Data, [ Header, EXTEN_NO=, EXTNAME=, ERRMSG= ]'
+      return
+   endif
+
+   setdefaultvalue, exten_no, 0
+   setdefaultvalue, Header, 0
+   nheader = N_elements(Header)
+   updated = 0b
+
+;Make sure END statement is the last line in supplied FITS header   
+   
+   if nheader GT 1 then begin
+         endline = where( strmid(Header,0,8) EQ 'END     ', Nend)
+         if Nend EQ 0 then begin
+         message,/INF,  $
+	  'WARNING - An END statement has been appended to the FITS header'
+         Header = [ Header, 'END' + string( replicate(32b,77) ) ]
+	 endif else header = header[0:endline]  
+   endif 
+   
+   ndata = N_elements(data)
+   dtype = size(data,/TNAME)
+   printerr =  ~arg_present(ERRMSG) 
+   fcbsupplied = size(filename,/TNAME) EQ 'STRUCT'
+
+   if (nheader GT 1) && (ndata GT 1) && (dtype NE 'STRUCT') then begin
+        check_fits, data, header, /FITS, ERRMSG = MESSAGE
+        if message NE '' then goto, BAD_EXIT
+   endif
+
+; Open file and read header information
+         
+   if (exten_no EQ 0) && (~keyword_set(EXTNAME)) then begin 
+         if nheader GT 0 then $
+             if strmid( header[0], 0, 8)  NE 'SIMPLE  ' then begin 
+                 message = $
+                'Input header does not contain required SIMPLE keyword'
+                 goto, BAD_EXIT
+             endif
+   endif else begin
+         if nheader GT 1 then $
+             if strmid( header[0], 0, 8)  NE 'XTENSION' then begin 
+              message = $
+             'Input header does not contain required XTENSION keyword'
+              goto, BAD_EXIT
+              endif
+   endelse
+
+; Was a file name or file control block supplied?
+
+   if ~fcbsupplied then begin 
+       fits_open, filename, io,/update,/No_Abort,message=message
+       if message NE '' then GOTO, BAD_EXIT
+    endif else begin 
+       if filename.open_for_write EQ 0 then begin
+             message = 'FITS file is set for READONLY, cannot be updated'
+             goto, BAD_EXIT
+       endif
+       io = filename
+   endelse
+
+; Getting starting position of data and header
+
+   if keyword_set(extname) then begin 
+       exten_no = where( strupcase(io.extname) EQ strupcase(extname), Nfound)
+       if Nfound EQ  0 then begin       
+          message,'Extension name ' + extname + ' not found in FITS file'
+	  GOTO, BAD_EXIT
+       endif
+   endif    	   
+   unit = io.unit
+   start_d = io.start_data[exten_no]
+   if exten_no NE io.nextend then begin
+        start_h = io.start_header[exten_no+1] 
+        nbytes = start_h - start_d
+   endif else nbytes = N_BYTES(data)
+
+   FITS_READ,Io,0,oldheader,/header_only, exten=exten_no, /No_PDU, $
+       message = message,/no_abort
+   if message NE '' then goto, BAD_EXIT
+    dochecksum = sxpar(oldheader,'CHECKSUM', Count = N_checksum)
+   checksum = N_checksum GT 0  
+   
+
+; Update header, including any CHECKSUM keywords if present 
+
+   if nheader GT 1 then begin
+        noldheader = start_d - io.start_header[exten_no]
+ 
+        if dtype EQ 'UINT' then $
+              sxaddpar,header,'BZERO',32768,'Data is unsigned integer'
+        if dtype EQ 'ULONG' then $
+              sxaddpar,header,'BZERO',2147483648,'Data is unsigned long'
+        if checksum then begin 
+               if (Ndata GT 1) && (dtype NE 'STRUCT') then $
+	        FITS_ADD_CHECKSUM, header, data else $
+                FITS_ADD_CHECKSUM, header 
+        endif
+; Position of 'END' card may have changed - Bug fix July 2008	
+        endline = where( strmid(Header,0,8) EQ 'END     ', Nend)
+
+        newbytes = N_elements(header)*80 
+        block = (newbytes-1)/2880 - (Noldheader-1)/2880
+        if block NE 0 then begin  
+            BLKSHIFT, io.unit, start_d, block*2880L, /NOZERO
+            start_d += block*2880L
+	    io.start_data[exten_no:*] += block*2880L
+            io.nbytes += block*2880L
+            if exten_no NE io.nextend then begin
+                    start_h += block*2880L
+		    io.start_header[exten_no+1:*] += block*2880L
+	     endif		
+        endif
+        point_lun, unit, io.start_header[exten_no]      ;Position header start  
+        bhdr = replicate(32b, newbytes)
+        for n = 0l, endline[0] do bhdr[80*n] = byte( header[n] )
+         writeu, unit, bhdr
+        remain = newbytes mod 2880
+	if remain GT 0 then writeu, unit, replicate( 32b, 2880 - remain)
+	updated = 1b
+ 
+   endif 
+
+   if (ndata GT 1) || (dtype EQ 'STRUCT') then begin
+ 
+        newbytes = N_BYTES(data)    ;total number of bytes in supplied data
+        block = (newbytes-1)/2880 - (nbytes-1)/2880
+        if (block NE 0) && (exten_no NE io.nextend) then begin
+              BLKSHIFT, io.unit, start_h, block*2880L,/NOZERO
+	      io.nbytes += block*2880L
+	      io.start_header[exten_no+1:*] += block*2880L
+	      io.start_data[exten_no+1:*] += block*2880L 
+        endif
+      
+        if (nheader EQ 0) && (dtype NE 'STRUCT') then begin
+                check_fits,data,oldheader,/FITS,ERRMSG = message
+                if message NE '' then goto, BAD_EXIT
+        endif
+ 
+        junk = fstat(unit)   ;Need this before changing from READU to WRITEU
+        point_lun, unit, start_d
+        if dtype EQ 'UINT' then newdata = fix(data - 32768)
+        if dtype EQ 'ULONG' then newdata = long(data - 2147483648)
+         if N_elements(newdata) GT 0 then writeu, unit, newdata  else $
+                                         writeu, unit ,data
+        remain = newbytes mod 2880
+	if remain GT 0 then begin
+             padnum = 0b
+             if exten_no GT 0 then begin 
+                 exten = sxpar( oldheader, 'XTENSION')
+	         if exten EQ 'TABLE   ' then padnum = 32b
+             endif
+	     writeu, unit, replicate( padnum, 2880 - remain)
+	endif
+	updated = 1b
+    endif       
+
+   if ~fcbsupplied then FITS_CLOSE,io  else filename = io
+   if ~updated then message,'FITS file not modified',/INF    
+   
+         
+   return 
+
+BAD_EXIT:
+    if N_elements(io) GT 0 then if ~fcbsupplied then fits_close,io
+    if printerr then message,'ERROR - ' + message,/CON else errmsg = message
+    if fcbsupplied then fname = filename.filename else fname = filename
+    message,'FITS file ' + fname + ' not modified',/INF
+    return
+   end 
diff --git a/Code/script_idl_mv/astrolib/month_cnv.pro b/Code/script_idl_mv/astrolib/month_cnv.pro
new file mode 100644
index 0000000000000000000000000000000000000000..39a771b4cc8dd57797fcaeb3fce8e31bcc17cf60
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/month_cnv.pro
@@ -0,0 +1,68 @@
+function month_cnv, MonthInput, Up=Up, Low=Low, Short=Short
+;\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
+;+
+; NAME:
+;       MONTH_CNV
+; PURPOSE:
+;       Convert between a month name and  the equivalent number 
+; EXPLANATION: (e.g.,
+;       For example, converts from 'January' to 1  or vice-versa.
+; CALLING SEQUENCE:
+;       Result = MONTH_CNV( MonthInput, [/UP, /LOW, /SHORT ] )
+; INPUTS:
+;       MonthInput - either a string ('January', 'Jan', 'Decem', etc.) or
+;               an number from 1 to 12.  Scalar or array. 
+; OPTIONAL KEYWORDS:
+;       UP - if set and if a string is being returned, it will be in all
+;               uppercase letters.
+;       LOW - if set and if a string is being returned, it will be in all 
+;               lowercase letters.
+;       SHORT - if set and if a string is being returned, only the first
+;               three letters are returned.
+;       
+; OUTPUTS:
+;       If the input is a string, the output is the matching month number.If
+;               an input string isn't a valid month name, -1 is returned.
+;       If the input is a number, the output is the matching month name.  The
+;               default format is only the first letter is capitalized.
+; EXAMPLE:
+;       To get a vector of all the month names:
+;               Names = month_cnv(indgen(12)+1)
+;
+; MODIFICATION HISTORY:
+;       Written by:     Joel Wm. Parker, SwRI, 1998 Dec 9
+;-
+;/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+NumElem = n_elements(MonthInput)
+
+MonthNames = ['  ', 'January', 'February', 'March', 'April', 'May', 'June', $
+          'July', 'August', 'September', 'October', 'November', 'December']
+MonthShort = strupcase(strmid(MonthNames,0,3))
+
+
+if size(MonthInput,/TNAME) EQ 'STRING' then begin
+   Result = intarr(NumElem) - 1
+   ShortInput = strupcase(strmid(strtrim(MonthInput,2),0,3))
+   for N=1,12 do begin
+      Mask = where(MonthShort[N] eq ShortInput)
+      if (Mask[0] ne -1) then Result[Mask] = N
+   endfor
+endif else begin
+   if ( (min(MonthInput) lt 1) or (max(MonthInput) gt 12) ) then begin
+      message, /CON, "Bad input values.  Month numbers must be 1-12."
+      Result = ''
+   endif else begin
+      Result = MonthNames[MonthInput]
+      if keyword_set(Short) then Result = strmid(Result,0,3)
+      if keyword_set(Up) then Result = strupcase(Result)
+      if keyword_set(Low) then Result = strlowcase(Result)
+   endelse
+endelse
+
+if (NumElem eq 1) then Result = Result[0]
+
+return, Result
+end   ;   function MONTH_CNV
+
+   
diff --git a/Code/script_idl_mv/astrolib/moonpos.pro b/Code/script_idl_mv/astrolib/moonpos.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3b026f8f8ac7a8b777474e975ee76bb038200a19
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/moonpos.pro
@@ -0,0 +1,250 @@
+  PRO MOONPOS, jd, ra, dec, dis, geolong, geolat, RADIAN = radian
+;+
+; NAME:                                     
+;       MOONPOS
+; PURPOSE:
+;       To compute the RA and Dec of the Moon at specified Julian date(s).
+;
+; CALLING SEQUENCE:
+;       MOONPOS, jd, ra, dec, dis, geolong, geolat, [/RADIAN ]
+;
+; INPUTS:
+;       JD - Julian ephemeris date, scalar or vector, double precision suggested
+;
+; OUTPUTS:
+;       Ra  - Apparent right ascension of the moon in DEGREES, referred to the
+;               true equator of the specified date(s) 
+;       Dec - The declination of the moon in DEGREES 
+;       Dis - The Earth-moon distance in kilometers (between the center of the
+;             Earth and the center of the Moon).
+;       Geolong - Apparent longitude of the moon in DEGREES, referred to the
+;               ecliptic of the specified date(s)
+;       Geolat - Apparent longitude of the moon in DEGREES, referred to the
+;               ecliptic of the specified date(s)
+;
+;       The output variables will all have the same number of elements as the
+;       input Julian date vector, JD.   If JD is a scalar then the output 
+;       variables will be also.
+;
+; OPTIONAL INPUT KEYWORD:
+;       /RADIAN - If this keyword is set and non-zero, then all output variables 
+;               are given in Radians rather than Degrees
+;
+; EXAMPLES:
+;       (1) Find the position of the moon on April 12, 1992
+;
+;       IDL> jdcnv,1992,4,12,0,jd    ;Get Julian date
+;       IDL> moonpos, jd, ra ,dec     ;Get RA and Dec of moon
+;       IDL> print,adstring(ra,dec,1)
+;               ==> 08 58 45.23  +13 46  6.1
+;
+;       This is within 1" from the position given in the Astronomical Almanac
+;       
+;       (2) Plot the Earth-moon distance for every day at 0 TD in July, 1996
+;
+;       IDL> jdcnv,1996,7,1,0,jd                   ;Get Julian date of July 1
+;       IDL> moonpos,jd+dindgen(31), ra, dec, dis  ;Position at all 31 days
+;       IDL> plot,indgen(31),dis, /YNOZ
+;
+; METHOD:
+;       Derived from the Chapront ELP2000/82 Lunar Theory (Chapront-Touze' and
+;       Chapront, 1983, 124, 50), as described by Jean Meeus in Chapter 47 of
+;       ``Astronomical Algorithms'' (Willmann-Bell, Richmond), 2nd edition, 
+;       1998.    Meeus quotes an approximate accuracy of 10" in longitude and
+;       4" in latitude, but he does not give the time range for this accuracy.
+;
+;       Comparison of this IDL procedure with the example in ``Astronomical
+;       Algorithms'' reveals a very small discrepancy (~1 km) in the distance 
+;       computation, but no difference in the position calculation.
+;
+;       This procedure underwent a major rewrite in June 1996, and the new
+;       calling sequence is *incompatible with the old* (e.g. angles now 
+;       returned in degrees instead of radians).
+;
+; PROCEDURES CALLED:
+;       CIRRANGE, ISARRAY(), NUTATE, TEN()  - from IDL Astronomy Library
+;       POLY() - from IDL User's Library
+; MODIFICATION HISTORY:
+;       Written by Michael R. Greason, STX, 31 October 1988.
+;       Major rewrite, new (incompatible) calling sequence, much improved 
+;               accuracy,       W. Landsman   Hughes STX      June 1996
+;       Added /RADIAN keyword  W. Landsman August 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use improved expressions for L',D,M,M', and F given in 2nd edition of
+;            Meeus (very slight change),  W. Landsman    November 2000
+;       Avoid 32767 overflow   W. Landsman January 2005
+;       
+;-
+ compile_opt idl2 
+ On_error,2
+
+ if N_params() LT 3 then begin
+        print,'Syntax - MOONPOS, jd, ra, dec, dis, geolong, geolat, [/RADIAN]' 
+        print,'Output angles in DEGREES unless /RADIAN is set'
+        return
+ endif
+
+ npts = N_elements(jd)
+ dtor = !DPI/180.0d
+
+ ;  form time in Julian centuries from 1900.0
+
+ t = (jd[*] - 2451545.0d)/36525.0d0
+
+ d_lng = [0,2,2,0,0,0,2,2,2,2,0,1,0,2,0,0,4,0,4,2,2,1,1,2,2,4,2,0,2,2,1,2,0,0, $
+ 2,2,2,4,0,3,2,4,0,2,2,2,4,0,4,1,2,0,1,3,4,2,0,1,2,2]
+
+ m_lng = [0,0,0,0,1,0,0,-1,0,-1,1,0,1,0,0,0,0,0,0,1,1,0,1,-1,0,0,0,1,0,-1,0, $
+ -2,1,2,-2,0,0,-1,0,0,1,-1,2,2,1,-1,0,0,-1,0,1,0,1,0,0,-1,2,1,0,0]
+
+ mp_lng = [1,-1,0,2,0,0,-2,-1,1,0,-1,0,1,0,1,1,-1,3,-2,-1,0,-1,0,1,2,0,-3,-2,$
+ -1,-2,1,0,2,0,-1,1,0,-1,2,-1,1,-2,-1,-1,-2,0,1,4,0,-2,0,2,1,-2,-3,2,1,-1, $
+  3,-1]
+
+ f_lng = [0,0,0,0,0,2,0,0,0,0,0,0,0,-2,2,-2,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0, $
+ 0,0,0,-2,2,0,2,0,0,0,0,0,0,-2,0,0,0,0,-2,-2,0,0,0,0,0,0,0,-2]
+
+ sin_lng = [6288774,1274027,658314,213618,-185116,-114332,58793,57066,53322, $
+ 45758,-40923,-34720,-30383,15327,-12528,10980,10675,10034,8548,-7888,-6766, $
+ -5163,4987,4036,3994,3861,3665,-2689,-2602,2390,-2348,2236,-2120,-2069,2048, $
+ -1773,-1595,1215,-1110,-892,-810,759,-713,-700,691,596,549,537,520,-487, $
+  -399,-381,351,-340,330,327,-323,299,294,0.0d]
+
+ cos_lng = [-20905355,-3699111,-2955968,-569925,48888,-3149,246158,-152138, $
+  -170733,-204586,-129620,108743,104755,10321,0,79661,-34782,-23210,-21636, $
+   24208,30824,-8379,-16675,-12831,-10445,-11650,14403,-7003,0,10056,6322, $
+  -9884,5751,0,-4950,4130,0,-3958,0,3258,2616,-1897,-2117,2354,0,0,-1423, $
+  -1117,-1571,-1739,0,-4421,0,0,0,0,1165,0,0,8752.0d]
+
+ d_lat = [0,0,0,2,2,2,2,0,2,0,2,2,2,2,2,2,2,0,4,0,0,0,1,0,0,0,1,0,4,4,0,4,2,2,$
+    2,2,0,2,2,2,2,4,2,2,0,2,1,1,0,2,1,2,0,4,4,1,4,1,4,2]
+
+ m_lat = [0,0,0,0,0,0,0,0,0,0,-1,0,0,1,-1,-1,-1,1,0,1,0,1,0,1,1,1,0,0,0,0,0,0,$
+    0,0,-1,0,0,0,0,1,1,0,-1,-2,0,1,1,1,1,1,0,-1,1,0,-1,0,0,0,-1,-2]
+
+ mp_lat = [0,1,1,0,-1,-1,0,2,1,2,0,-2,1,0,-1,0,-1,-1,-1,0,0,-1,0,1,1,0,0,3,0, $ 
+   -1,1, -2,0,2,1,-2,3,2,-3,-1,0,0,1,0,1,1,0,0,-2,-1,1,-2,2,-2,-1,1,1,-1,0,0]
+
+ f_lat =[ 1,1,-1,-1,1,-1,1,1,-1,-1,-1,-1,1,-1,1,1,-1,-1,-1,1,3,1,1,1,-1,-1,-1, $
+     1,-1,1,-3,1,-3,-1,-1,1,-1,1,-1,1,1,1,1,-1,3,-1,-1,1,-1,-1,1,-1,1,-1,-1, $
+     -1,-1,-1,-1,1]
+
+ sin_lat = [5128122,280602,277693,173237,55413,46271,32573,17198,9266,8822, $
+     8216,4324,4200,-3359,2463,2211,2065,-1870,1828,-1794,-1749,-1565,-1491, $
+     -1475,-1410,-1344,-1335,1107,1021,833,777,671,607,596,491,-451,439,422, $
+     421,-366,-351,331,315,302,-283,-229,223,223,-220,-220,-185,181,-177,176, $
+    166,-164,132,-119,115,107.0d]
+
+; Mean longitude of the moon referred to mean equinox of the date
+
+ coeff0 = [218.3164477d, 481267.88123421d, -0.0015786d0, 1.0d/538841.0d, $
+         -1.0d/6.5194d7 ]
+ lprimed = poly(T, coeff0)
+ cirrange, lprimed
+ lprime = lprimed*dtor
+
+; Mean elongation of the Moon
+
+  coeff1 = [297.8501921d, 445267.1114034d, -0.0018819d, 1.0d/545868.0d, $
+           -1.0d/1.13065d8 ]
+  d = poly(T, coeff1)
+  cirrange,d
+  d = d*dtor
+
+; Sun's mean anomaly
+
+   coeff2 = [357.5291092d, 35999.0502909d, -0.0001536d, 1.0d/2.449d7 ]
+   M = poly(T,coeff2) 
+   cirrange, M 
+   M = M*dtor
+
+; Moon's mean anomaly
+
+   coeff3 = [134.9633964d, 477198.8675055d, 0.0087414d, 1.0/6.9699d4, $
+             -1.0d/1.4712d7 ]
+   Mprime = poly(T, coeff3) 
+   cirrange, Mprime
+   Mprime = Mprime*dtor
+
+; Moon's argument of latitude
+
+    coeff4 = [93.2720950d, 483202.0175233d, -0.0036539, -1.0d/3.526d7, $
+             1.0d/8.6331d8 ]
+    F = poly(T, coeff4 ) 
+    cirrange, F
+    F = F*dtor
+
+; Eccentricity of Earth's orbit around the Sun
+
+    E = 1 - 0.002516d*T - 7.4d-6*T^2
+    E2 = E^2
+
+    ecorr1 = where(abs(m_lng) EQ 1)
+    ecorr2 = where(abs(m_lat) EQ 1)
+    ecorr3 = where(abs(m_lng) EQ 2)
+    ecorr4 = where(abs(m_lat) EQ 2)
+
+; Additional arguments
+
+    A1 = (119.75d + 131.849d*T) * dtor
+    A2 = (53.09d + 479264.290d*T) * dtor
+    A3 = (313.45d + 481266.484d*T) * dtor
+    suml_add = 3958*sin(A1) + 1962*sin(lprime - F) + 318*sin(A2)
+    sumb_add =  -2235*sin(lprime) + 382*sin(A3) + 175*sin(A1-F) + $ 
+              175*sin(A1 + F) + 127*sin(Lprime - Mprime) - $
+              115*sin(Lprime + Mprime)
+
+; Sum the periodic terms 
+
+ geolong = dblarr(npts) & geolat = geolong & dis = geolong
+
+ for i=0L,npts-1 do begin
+
+   sinlng = sin_lng & coslng = cos_lng & sinlat = sin_lat
+
+   sinlng[ecorr1] = e[i]*sinlng[ecorr1]
+   coslng[ecorr1] = e[i]*coslng[ecorr1]
+   sinlat[ecorr2] = e[i]*sinlat[ecorr2]
+   sinlng[ecorr3] = e2[i]*sinlng[ecorr3]
+   coslng[ecorr3] = e2[i]*coslng[ecorr3]
+   sinlat[ecorr4] = e2[i]*sinlat[ecorr4]
+
+   arg = d_lng*d[i] + m_lng*m[i] +mp_lng*mprime[i] + f_lng*f[i]
+   geolong[i] = lprimed[i] + ( total(sinlng*sin(arg)) + suml_add[i] )/1.0d6
+
+   dis[i] = 385000.56d + total(coslng*cos(arg))/1.0d3
+
+   arg = d_lat*d[i] + m_lat*m[i] +mp_lat*mprime[i] + f_lat*f[i]
+   geolat[i] = (total(sinlat*sin(arg)) + sumb_add[i])/1.0d6
+       
+ endfor
+
+ nutate, jd, nlong, elong                     ;Find the nutation in longitude
+ geolong= geolong + nlong/3.6d3
+ cirrange,geolong
+ lambda = geolong*dtor
+ beta = geolat*dtor
+
+;Find mean obliquity and convert lambda,beta to RA, Dec 
+
+ c = [21.448,-4680.93,-1.55,1999.25,-51.38,-249.67,-39.05,7.12,27.87,5.79,2.45d]
+ epsilon = ten(23,26) + poly(t/1.d2,c)/3600.d
+ eps = (epsilon + elong/3600.d )*dtor          ;True obliquity in radians
+
+ ra = atan( sin(lambda)*cos(eps) - tan(beta)* sin(eps), cos(lambda) )
+ cirrange,ra,/RADIAN
+ dec = asin( sin(beta)*cos(eps) + cos(beta)*sin(eps)*sin(lambda) )
+
+ if not isarray(jd) then begin
+        ra = ra[0] & dec = dec[0] & dis = dis[0]
+        geolong = geolong[0]  & geolat = geolat[0]
+ endif
+
+ if not keyword_set(RADIAN) then begin
+        ra = ra/dtor & dec = dec/dtor
+ endif else begin
+        geolong = lambda & geolat = beta
+ endelse
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/mphase.pro b/Code/script_idl_mv/astrolib/mphase.pro
new file mode 100644
index 0000000000000000000000000000000000000000..40840796a9f66705a54ca6f6bdcae679e89a3754
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mphase.pro
@@ -0,0 +1,56 @@
+pro mphase,jd, k
+;+
+; NAME:
+;       MPHASE
+; PURPOSE:
+;       Return the illuminated fraction of the Moon at given Julian date(s) 
+;
+; CALLING SEQUENCE:
+;       MPHASE, jd, k
+; INPUT:
+;       JD - Julian date, scalar or vector, double precision recommended
+; OUTPUT:
+;       k - illuminated fraction of Moon's disk (0.0 < k < 1.0), same number
+;           of elements as jd.   k = 0 indicates a new moon, while k = 1 for
+;           a full moon.
+; EXAMPLE:
+;       Plot the illuminated fraction of the moon for every day in July 
+;       1996 at 0 TD (~Greenwich noon).
+;
+;       IDL> jdcnv, 1996, 7, 1, 0, jd         ;Get Julian date of July 1
+;       IDL> mphase, jd+dindgen(31), k        ;Moon phase for all 31 days
+;       IDL> plot, indgen(31),k               ;Plot phase vs. July day number
+;
+; METHOD:
+;       Algorithm from Chapter 46 of "Astronomical Algorithms" by Jean Meeus
+;       (Willmann-Bell, Richmond) 1991.   SUNPOS and MOONPOS are used to get
+;       positions of the Sun and the Moon (and the Moon distance).   The
+;       selenocentric elongation of the Earth from the Sun (phase angle)
+;       is then computed, and used to determine the illuminated fraction.
+; PROCEDURES CALLED:
+;       MOONPOS, SUNPOS
+; REVISION HISTORY:
+;       Written W. Landsman     Hughes STX           June 1996
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use /RADIAN keywords to MOONPOS, SUNPOS internally  W. Landsman Aug 2000
+;-
+ On_error,2
+
+ if N_params() LT 2 then begin
+        print,'Syntax - MPHASE, jd, k'
+        return
+ endif
+ diss = 1.49598e8         ;Earth-Sun distance (1 AU)
+
+ moonpos, jd, ram, decm, dism, /RADIAN
+ sunpos, jd, ras, decs, /RADIAN
+
+; phi - geocentric elongation of the Moon from the Sun
+; inc - selenocentric (Moon centered) elongation of the Earth from the Sun
+
+ phi = acos( sin(decs)*sin(decm) + cos(decs)*cos(decm)*cos(ras-ram) )
+ inc = atan( diss * sin(phi), dism - diss*cos(phi) )
+ k = (1 + cos(inc))/2.
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/mrandomn.pro b/Code/script_idl_mv/astrolib/mrandomn.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1d976ca748b364e25c4247dd605bcbbf59b57103
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mrandomn.pro
@@ -0,0 +1,80 @@
+function mrandomn, seed, covar, nrand, STATUS = status
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;+
+;  NAME:
+;     MRANDOMN
+; PURPOSE:
+; Function to draw NRAND random deviates from a multivariate normal
+; distribution with zero mean and covariance matrix COVAR.
+; 
+; AUTHOR : Brandon C. Kelly, Steward Obs., Sept. 2004
+;
+; INPUTS : 
+;
+;    SEED - The random number generator seed, the default is IDL's
+;           default in RANDOMN()
+;    COVAR - The covariance matrix of the multivariate normal
+;            distribution.    
+; OPTIONAL INPUTS :
+;
+;    NRAND - The number of randomn deviates to draw. The default is
+;            one.
+; OUTPUT :
+;
+;    The random deviates, an [NRAND, NP] array where NP is the
+;    dimension of the covariance matrix, i.e., the number of
+;    parameters.
+;
+; OPTIONAL OUTPUT:
+;     STATUS - status of the Cholesky decomposition.   If STATUS = 0 then 
+;         the computation was successful.  If STATUS > 0 then the 
+;         input covariance matrix is not positive definite  (see LA_CHOLDC),
+;         and MRANDOMN
+;         Note that if a STATUS keyword is supplied then no error message 
+;         will be printed.
+; REVISION HISTORY:
+;     Oct. 2013  -- Use LA_CHOLDC instead of CHOLDC to enable use of STATUS
+;           keyword.    W. Landsman
+;-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+if n_params() lt 2 then begin
+    print, 'Syntax- Result = mrandomn( seed, covar, [nrand] , STATUS = )'
+    return, 0
+endif
+
+printerr = ~arg_present(errmsg)
+errmsg = '' 
+
+
+;check inputs and set up defaults
+if n_elements(nrand) eq 0 then nrand = 1
+if size(covar, /n_dim) ne 2 then begin
+    print, 'COVAR must be a matrix.'
+    return, 0
+endif
+
+np = (size(covar))[1]
+if (size(covar))[2] ne np then begin
+    print, 'COVAR must be a square matrix.'
+    return, 0
+endif
+
+epsilon = randomn(seed, nrand, np) ;standard normal random deviates (NP x NRAND matrix)
+
+A = covar  ;store covariance into dummy variable for input into TRIRED
+
+  la_choldc, A, /double, status=status        ;do Cholesky decomposition
+  if status NE 0 then begin
+     message,'Array is not positive definite, STATUS = ' + strtrim(status,2),/CON 
+     return,-1
+  endif   
+
+for i = 0, np - 2  do A[i+1:*,i] = 0d        ;Zero out upper triangular portion
+
+;transform standard normal deviates so they have covariance matrix COVAR
+epsilon = A ## epsilon
+
+return, epsilon
+end
diff --git a/Code/script_idl_mv/astrolib/mrd_hread.pro b/Code/script_idl_mv/astrolib/mrd_hread.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f464e98cd2c4960e8fc24da5953895807036b493
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mrd_hread.pro
@@ -0,0 +1,135 @@
+pro mrd_hread, unit, header, status, SILENT = silent, FIRSTBLOCK = firstblock, $
+    ERRMSG = errmsg,SKIPDATA=skipdata,NO_BADHEADER=no_badheader
+;+
+; NAME: 
+;     MRD_HREAD
+;
+; PURPOSE: 
+;     Reads a FITS header from an opened disk file or Unix pipe
+; EXPLANATION:
+;     Like FXHREAD but also works with compressed Unix files
+;
+; CALLING SEQUENCE: 
+;     MRD_HREAD, UNIT, HEADER  [, STATUS, /SILENT, ERRMSG =, /FIRSTBLOCK ]
+; INPUTS: 
+;     UNIT    = Logical unit number of an open FITS file
+; OUTPUTS: 
+;     HEADER  = String array containing the FITS header.
+; OPT. OUTPUTS: 
+;     STATUS  = Condition code giving the status of the read.  Normally, this
+;                 is zero, but is set to -1 if an error occurs, or if the
+;                 first byte of the header is zero (ASCII null).
+; OPTIONAL KEYWORD INPUT:
+;      /FIRSTBLOCK - If set, then only the first block (36 lines or less) of 
+;                the FITS header are read into the output variable.   If only
+;                size information (e.g. BITPIX, NAXIS) is needed from the
+;                header, then the use of this keyword can save time.  The 
+;                file pointer is still positioned at the end of the header,
+;                even if the /FIRSTBLOCK keyword is supplied.
+;      /SILENT - If set, then warning messages about any invalid characters in
+;                the header are suppressed.
+;      /SKIPDATA - If set, then the file point is positioned at the end of the
+;                HDU after the header is read, i.e. the following data block
+;                is skipped.   Useful, when one wants to the read the headers
+;                of multiple extensions.
+;      /NO_BADHEADER - if set, returns if FITS header has illegal characters
+;                By default, MRD_HREAD replaces bad characters with blanks,
+;                issues a warning, and continues.
+; OPTIONAL OUTPUT PARAMETER:
+;       ERRMSG  = If this keyword is present, then any error messages will be
+;                 returned to the user in this parameter rather than
+;                 depending on the MESSAGE routine in IDL.  If no errors are
+;                 encountered, then a null string is returned.
+; RESTRICTIONS: 
+;      The file must already be positioned at the start of the header.  It
+;      must be a proper FITS file.
+; SIDE EFFECTS: 
+;       The file ends by being positioned at the end of the FITS header, unless
+;       an error occurs.
+; REVISION HISTORY:
+;      Written,  Thomas McGlynn                     August 1995
+;      Modified, Thomas McGlynn		     January 1996
+;         Changed MRD_HREAD to handle Headers which have null characters
+;          A warning message is printed out but the program continues.
+;          Previously MRD_HREAD would fail if the null characters were
+;          not in the last 2880 byte block of the header.  Note that
+;          such characters are illegal in the header but frequently
+;          are produced by poor FITS writers.
+;      Added /SILENT keyword   W. Landsman   December 2000
+;      Added /FIRSTBLOCK keyword  W. Landsman   February 2003
+;      Added ERRMSG, SKIPDATA keyword W. Landsman          April 2009
+;      Close file unit even after error message   W.L.  October 2010
+;      Added /NO_BADHEADER  Zarro (ADNET), January 2012
+;-
+  On_error,2
+  compile_opt idl2
+  printerr = ~arg_present(errmsg)
+  errmsg = ''
+
+  block = string(replicate(32b, 80, 36))
+		
+  Nend = 0                  ;Signal if 'END     ' statement is found
+  nblock = 0
+
+  while Nend EQ 0 do begin
+		
+; Shouldn't get eof in middle of header.
+       if eof(unit) then begin
+                errmsg = 'EOF encountered in middle of FITS header'
+		if printerr then message,errmsg,/CON
+		free_lun, unit
+		status = -1
+		return
+		endif
+		
+	on_ioerror, error_return
+	readu, unit, block
+	on_ioerror, null
+
+; Check that there aren't improper null characters in strings that are causing 
+; them to be truncated.   Issue a warning but continue if problems are
+; found (unless /NO_BADHEADER is set)
+
+	w = where(strlen(block) ne 80, Nbad)
+	if (Nbad GT 0) then begin
+                warning='Warning-Invalid characters in header'
+		if ~keyword_set(SILENT) then message,warning,/INF
+                if keyword_set(NO_BADHEADER) then begin
+                  status=-1 & errmsg=warning & free_lun,unit & return
+                endif
+		block[w] = string(replicate(32b, 80))
+	endif	       
+	w = where(strmid(block, 0, 8) eq 'END     ', Nend)
+        if nblock EQ 0 then begin
+               header = Nend GT 0 ?  block[ 0:w[0] ] : block
+	       nblock =1
+        endif else $
+	       if ~keyword_set(firstblock) then $
+	         header = Nend GT 0 ? [header,block[0:w[0]]] : [header, block]
+			
+	endwhile
+		
+        if keyword_set(skipdata) then begin 
+                bitpix = fxpar(header,'bitpix')
+                naxis  = fxpar(header,'naxis')
+                gcount = fxpar(header,'gcount') 
+                if gcount eq 0 then gcount = 1
+                pcount = fxpar(header,'pcount')
+               
+                if naxis gt 0 then begin 
+                        dims = fxpar(header,'naxis*')           ;read dimensions
+  			ndata = product(dims,/integer)
+                endif else ndata = 0
+                
+                nbytes = long64(abs(bitpix) / 8) * gcount * (pcount + ndata)
+	        mrd_skip, unit, nbytes
+	endif	
+	status = 0
+	return
+error_return:
+        status = -1
+	errmsg = 'END Statement not found in FITS header'
+        if printerr then message, 'ERROR ' + errmsg	
+	return
+end
+			
diff --git a/Code/script_idl_mv/astrolib/mrd_skip.pro b/Code/script_idl_mv/astrolib/mrd_skip.pro
new file mode 100644
index 0000000000000000000000000000000000000000..40744eef5a385c46bf3aa396e6e42e98d1d23974
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mrd_skip.pro
@@ -0,0 +1,72 @@
+pro mrd_skip, unit, nskip
+;+
+; NAME:
+;       MRD_SKIP
+; PURPOSE:
+;       Skip a number of bytes from the current location in a file or a pipe
+; EXPLANATION:
+;       First tries using POINT_LUN and if this doesn't work, perhaps because
+;       the unit is a pipe or a socket, MRD_SKIP will just read in the 
+;       requisite number  of bytes.    
+; CALLING SEQUENCE:
+;       MRD_SKIP, Unit, Nskip
+;
+; INPUTS:
+;       Unit - File unit for the file or pipe in question, integer scalar
+;       Nskip - Number of bytes to be skipped, positive integer
+; NOTES:
+;       This routine should be used in place of POINT_LUN wherever a pipe
+;       or socket may be the input unit (see the procedure FXPOSIT for an 
+;       example).   Note that it assumes that it can only work with nskip >= 0 
+;       so it doesn't even try for negative values.      
+;
+;       For reading a pipe, MRD_SKIP currently uses a maximum buffer size
+;       of 8 MB.   This chunk value can be increased for improved efficiency
+;       (or decreased if you really have little memory.)
+; REVISION HISTORY:
+;       Written, Thomas A. McGlynn    July 1995
+;	Don't even try to skip bytes on a pipe with POINT_LUN, since this
+;	might reset the current pointer     W. Landsman        April 1996
+;       Increase buffer size, check fstat.compress W. Landsman  Jan 2001
+;       Only a warning if trying read past EOF   W. Landsman   Sep 2001
+;       Use 64bit longword for skipping in very large files W. Landsman Sep 2003
+;       Assume since V5.4, fstat.compress available W. Landsman April 2006
+;       POINT_LUN for compressed files is as fast as any W. Landsman Oct 2006
+;       Don't try to use POINT_LUN on compressed files W. Landsman Dec. 2006
+;       
+;-
+        On_error,2
+
+	if nskip le 0 then return
+        compress = (fstat(unit)).compress
+
+; We try to use POINT_LUN but if an error ocurrs, we just read in the bytes 
+
+          if ~compress then begin
+ 	  On_IOerror, byte_read
+	  point_lun, -unit, curr_pos
+	  On_IOerror, null
+          if curr_pos NE -1 then point_lun, unit, long64(curr_pos) + nskip
+           return
+	  endif 
+
+; Otherwise, we have to explictly read the number of bytes to skip
+; If the number is very large we don't want to create a array so skip
+; in chunks of 8 Megabyte
+
+byte_read:
+
+        chunk = 8000000L
+	buf = bytarr(nskip<chunk, /nozero)
+	nleft = nskip
+	on_ioerror, DONE
+	while (nleft gt 0) do begin
+		readu, unit, buf
+		nleft = nleft - chunk
+	        if (nleft gt 0) && (nleft lt chunk) then buf = buf[0:nleft-1]	
+	endwhile
+	return
+DONE:  message,'Warning - Byte padding in FITS file may not be correct',/CON
+       return		
+end
+
diff --git a/Code/script_idl_mv/astrolib/mrd_struct.pro b/Code/script_idl_mv/astrolib/mrd_struct.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c6295965e5250ee78c58d7cb405803d24ec1ac96
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mrd_struct.pro
@@ -0,0 +1,219 @@
+;+
+; NAME:
+;       MRD_STRUCT
+; PURPOSE:
+;       Return a structure as defined in the names and values data.
+; CALLING SEQUENCE:
+;       struct = MRD_STRUCT(NAMES, VALUES, NROW, STRUCTYP='name' )
+; INPUT PARAMETERS:
+;       NAMES   = A string array of names of structure fields.
+;       VALUES  = A string array giving the values of the structure
+;                 fields.  See examples below.
+;       NROW    = The number of elements in the structure array.
+;       
+; RETURNS:
+;       A structure as described in the parameters or 0 if an error
+;       is detected.
+;
+; OPTIONAL KEYWORD PARAMETERS:
+;       /NO_EXECUTE - If set then the use of the EXECUTE() statement is avoided.
+;                  By default, the NO_EXECUTE pathway is used if IDL is 
+;                  running under the Virtual Machine.    Note if  /NO_EXECUTE
+;                  is set, then the user cannot supply arbitrary values, but
+;                  all possible values used by MRDFITS will be allowed.
+;       STRUCTYP = The structure type.  Since IDL does not allow the
+;                  redefinition of a named structure it is an error
+;                  to call MRD_STRUCT with different parameters but
+;                  the same STRUCTYP in the same session.  If this
+;                  keyword is not set an anonymous structure is created.
+; COMMON BLOCKS:
+;       MRD_COMMON
+; SIDE EFFECTS:                                                            
+;       May create a temporary file if the structure definition is too long 
+;       for the EXECUTE function and using old style structures
+;
+; RESTRICTIONS:
+;       By default, the program defines the structure in a long string
+;       which is executed with CREATE_STRUCT within a single EXECUTE statement.
+;
+;       If program is being run in the IDL Virtual machine (EXECUTE statement
+;       not allowed), then a separate CREATE_STRUCT statement is called
+;       for each tag.   This mode does not have the full capabilities of the
+;       normal mode, but should be sufficient for use with MRDFITS().
+; PROCEDURE:
+;       A structure definition is created using the parameter values.
+;       MRD_NSTRUCT is called  and generates the structure in pieces using the
+;       execute and create_struct keywords.
+;
+; EXAMPLES:
+;       (1) str = mrd_struct(['fld1', 'fld2'], ['0','dblarr(10,10)'],3)
+;           print, str(0).fld2(3,3)
+;       Note that "0" is always considered short integer even if the default
+;       integer is set to long.
+;          
+;
+;       (2) str = mrd_struct(['a','b','c','d'],['1', '1.', '1.d0', "'1'"],1)
+;               ; returns a structure with integer, float, double and string
+;               ; fields.
+; PROCEDURE CALLS:
+;       GETTOK() - needed for virtual machine mode only
+; MODIFICATION HISTORY:
+;       Created by T. McGlynn October, 1994.
+;       Modified by T. McGlynn September, 1995.
+;          Added capability to create substructures so that structure
+;          may contain up to 4096 distinct elements.  [This can be
+;          increased by futher iteration of the process used if needed.]
+;       Removed V4.0 reference to common block  October 1997
+;       Allowed unlimited number of structure elements if the version
+;       is greater than 5.0.  Put back in code to handle prior versions.
+;       The [] will need to be translated back to () for this to
+;       work.  T. McGlynn December 15 1998.
+;       Add MRD_NSTRUCT since IDL has mysterious problems compiling
+;       very large structures.
+;       Removed TEMPDIR and OLD_STRUCT keywords  W. Landsman October 2003   
+;       Alternate pathway without EXECUTE for V6.0 virtual machine, D. Lindler
+;       Removed limit on EXECUTE statement.  W. Landsman  October 2003
+;       Restore EXECUTE limit (sigh...), added NO_EXECUTE keyword
+;                         W. Landsman July 2004
+;       Fix use of STRUCTYP with /NO_EXECUTE  W. Landsman June 2005
+;       Assume since V6.0 (lmgr function available), remove 131 string length
+;             limit for execute    W. Landsman Jun 2009 
+;       Restore EXECUTE limit (sigh...)   W. Landsman July 2009 
+;       Make sure "0" is a short integer even with compile_opt idl2  July 2010
+;       Added "0.0", "0.0d", "0u", "0ul", and "0ull" as valid tags
+;             for /NO_EXECUTE  E. Rykoff May 2012
+;       Fix for create long64 arrays with /no_execute  E. Rykoff Sep 2013
+;-
+
+; Check that the number of names is the same as the number of values.
+
+function mrd_struct, names, values, nrow, no_execute = no_execute,  $
+    structyp=structyp,  tempdir=tempdir, silent=silent, old_struct=old_struct
+
+compile_opt idl2
+
+; Keywords TEMPDIR, SILENT and OLD_STRUCT no longer do anything but are kept
+; for backward compatibility.
+
+
+ noexecute = keyword_set(no_execute) || lmgr(/vm) 
+
+ if noexecute then begin
+
+    ntags = n_elements(names)
+    for i=0,ntags-1 do begin
+;
+; create variable with the specified data type
+;
+	case strlowcase(values[i]) of 
+;
+; scalar values
+;
+	    '0b': v = 0B
+	    '0' : v = 0S
+            '0u' : v = 0US
+            '0us': v = 0US
+	    '0l': v = 0L
+	    '0ll' : v = 0LL
+            '0ul' : v = 0UL
+            '0ull' : v = 0ULL
+	    '0.': v = 0.0
+            '0.0': v = 0.0
+            '0.0d': v = 0.0d0
+	    '0.0d0': v = 0.0d0
+ 	    '0.d0': v = 0.0d0
+             '" "': v = " "          ;Added July 2004
+	    'complex(0.,0.)': v = complex(0.,0.)
+	    'dcomplex(0.d0,0.d0)': v = dcomplex(0.d0,0.d0)
+;
+; strings and arrays
+;`
+	    else: begin	     
+	        value = values[i]
+		remchar,value,"'"
+		remchar,value,'"'   
+		if strlen(value) EQ 1 then v= value else begin 
+	        type = gettok(value,'(')
+		if type eq 'string' then $
+			junk = gettok(value,',')      ;remove "replicate(32b"
+		dimen_string = gettok(value,')')	
+		dimen = long(strsplit(dimen_string,',',/extract))
+		case type of
+		    'bytarr': v = make_array(dimen=dimen,/byte)
+		    'intarr': v = make_array(dimen=dimen,/int)
+		    'fltarr': v = make_array(dimen=dimen,/float)
+		    'lonarr': v = make_array(dimen=dimen,/long)
+		    'lon64arr': v = make_array(dimen=dimen,/l64)  ;Fix Sep 13
+		    'dblarr': v = make_array(dimen=dimen,/double)
+		    'complexarr': v = make_array(dimen=dimen,/complex)
+		    'dcomplexarr': v = make_array(dimen=dimen,/dcomplex)
+                    'ptr_new': v = ptr_new()
+		    'string': begin
+		    		ndimen = n_elements(dimen)-1
+				if ndimen gt 0 then begin
+					v = make_array(dimen=dimen[1:*],/string)
+					v[*] = string(replicate(32B,dimen[0]))
+		    		end else v = string(replicate(32B,dimen[0]))
+			      end
+	            else: message,'ERROR - Invalid field value: ' + values[i]		      
+		endcase
+		        endelse 
+
+	    end
+	endcase     	
+	if i eq 0 then struct = create_struct(names[i],v) $
+		  else struct = create_struct(temporary(struct),names[i],v)
+    end; for i    
+
+endif else begin
+
+; Build up the structure use a combination of execute and
+; create_struct calls.  Basically we build as many rows as
+; will fit in an execute call and create that structure.  Then
+; we append that structure to whatever we've done before using
+; create_struct
+
+nel = N_elements(names)
+strng = "a={"
+
+comma = ' '
+for i=0,nel-1 do  begin
+    fval = values[i]
+    if (fval eq '0') then fval = '0s'
+  
+   ; Now for each element put in a name/value pair.
+    tstrng = strng + comma+names[i] + ':' + fval
+    
+    ; The nominal max length of the execute is 131
+    ; We need one chacacter for the "}"
+    if strlen(tstrng) gt 130 then begin
+        strng = strng + "}"
+        res = execute(strng)
+	if  res eq 0 then return, 0
+        struct = n_elements(struct) eq 0 ? a: $
+	         create_struct(temporary(struct), a)
+	strng = "a={" + names[i] + ":" + fval
+	
+    endif else strng = tstrng
+    comma = ","
+
+endfor
+	
+
+if strlen(strng) gt 3 then begin
+    strng = strng + "}"
+    res = execute(strng)
+     if  res eq 0 then return, 0
+     struct = n_elements(struct) eq 0 ? a : create_struct(temporary(struct), a)
+ endif
+ 
+endelse
+if keyword_set(structyp) then $
+     struct = create_struct(temporary(struct), name=structyp)
+
+
+if nrow le 1 then return, struct $
+             else return, replicate(struct, nrow)
+
+end
+
diff --git a/Code/script_idl_mv/astrolib/mrdfits.pro b/Code/script_idl_mv/astrolib/mrdfits.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2edcd8bc75d233d4f40822adcca21afe3ca9137b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mrdfits.pro
@@ -0,0 +1,2801 @@
+;+
+; NAME:
+;     MRDFITS
+;
+; PURPOSE:
+;     Read all standard FITS data types into arrays or structures.
+;
+; EXPLANATION:
+;      Further information on MRDFITS is available at
+;      http://idlastro.gsfc.nasa.gov/mrdfits.html 
+;
+;      **This version requires a post March 2009 version of fxposit.pro**
+; CALLING SEQUENCE:
+;      Result = MRDFITS( Filename/FileUnit,[Exten_no/Exten_name, Header],
+;                       /FPACK, /NO_FPACK, /FSCALE , /DSCALE , /UNSIGNED,
+;                       ALIAS=strarr[2,n], /USE_COLNUM,
+;                       /NO_TDIM, ROWS = [a,b,...], $
+;                       /POINTER_VAR, /FIXED_VAR, EXTNUM= 
+;                       RANGE=[a,b], COLUMNS=[a,b,...]), ERROR_ACTION=x,
+;                       COMPRESS=comp_prog, STATUS=status, /VERSION, 
+;                       /EMPTYSTRING )
+;
+; INPUTS:
+;      Filename = String containing the name of the file to be read or
+;                 file number of an open unit.  If an empty string is supplied,
+;                 then user will be prompted for the file name.    The user
+;                 will also be prompted if a wild card is given in the file
+;                 name, and there is more than one file name match.
+;                 If the file name ends in .gz or .fz (or .Z on Unix systems)
+;                 the file will be dynamically decompressed.
+;                                    or
+;      FiluUnit = An integer file unit which has already been
+;                 opened for input.  Data will be read from this
+;                 unit and the unit will be left pointing immediately
+;                 after the HDU that is read.  Thus to read a compressed
+;                 file with many HDU's a user might do something like:
+;                      lun=fxposit(filename, 3)  ; Skip the first three HDU's
+;                      repeat begin
+;                          thisHDU = mrdfits(lun, 0, hdr, status=status)
+;                          ... process the HDU ...
+;                      endrep until status lt 0
+;
+;      Exten_no= Extension number to be read, 0 for primary array.
+;                 Assumed 0 if not specified.
+;                 If a unit rather than a filename
+;                 is specified in the first argument, this is
+;                 the number of HDU's to skip from the current position.
+;      Exten_name - Name of the extension to read (as stored in the EXTNAME
+;                 keyword).   This is a slightly slower method then specifying
+;                 the extension number.
+; OUTPUTS:
+;      Result = FITS data array or structure constructed from
+;               the designated extension.  The format of result depends
+;               upon the type of FITS data read.
+;             Non-group primary array or IMAGE extension:
+;               A simple multidimensional array is returned with the
+;               dimensions given in the NAXISn keywords.
+;             Grouped image data with PCOUNT=0.
+;               As above but with GCOUNT treated as NAXIS(n+1).
+;             Grouped image data with PCOUNT>0.
+;               The data is returned as an array of structures.  Each
+;               structure has two elements.  The first is a one-dimensional
+;               array of the group parameters, the second is a multidimensional
+;               array as given by the NAXIS2-n keywords.
+;             ASCII and BINARY tables.
+;               The data is returned as a structure with one column for
+;               each field in the table.  The names of the columns are
+;               normally taken from the TTYPE keywords (but see USE_COLNUM).
+;               Bit field columns
+;               are stored in byte arrays of the minimum necessary
+;               length.  Spaces and invalid characters are replaced by 
+;               underscores, and other invalid tag names are converted using
+;               the IDL_VALIDNAME(/CONVERT_ALL) function.
+;               Columns specified as variable length columns are stored
+;               with a dimension equal to the largest actual dimension
+;               used.  Extra values in rows are filled with 0's or blanks.
+;               If the size of the variable length column is not
+;               a constant, then an additional column is created giving the 
+;               size used in the current row.  This additional column will 
+;               have a tag name of the form L#_"colname" where # is the column
+;               number and colname is the column name of the variable length
+;               column.   If the length of each element of a variable length 
+;               column is 0 then the column is deleted.
+;
+;
+; OPTIONAL OUTPUT:
+;       Header = String array containing the header from the FITS extension.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       ALIAS    The keyword allows the user to specify the column names
+;                to be created when reading FITS data.  The value of
+;                this keyword should be a 2xn string array.  The first
+;                value of each pair of strings should be the desired
+;                tag name for the IDL column.  The second should be
+;                the FITS TTYPE value.  Note that there are restrictions
+;                on valid tag names.  The order of the ALIAS keyword
+;                is compatible with MWRFITS.
+;       COLUMNS - This keyword allows the user to specify that only a
+;                subset of columns is to be returned.  The columns
+;                may be specified either as number 1,... n or by
+;                name or some combination of these two.
+;                If /USE_COLNUM is specified names should be C1,...Cn.
+;                The use of this keyword will not save time or internal
+;                memory since the extraction of specified columns
+;                is done after all columns have been retrieved from the
+;                FITS file.      Structure columns are returned in the order
+;                supplied in this keyword.
+;       COMPRESS - This keyword allows the user to specify a
+;                decompression program to use to decompress a file that
+;                will not be automatically recognized based upon
+;                the file name.
+;       /DSCALE - As with FSCALE except that the resulting data is
+;                stored in doubles.
+;       /EMPTYSTRING - There was a bug in memory management for IDL versions 
+;                 prior to V8.0, causing a memory leak when reading
+;                 empty strings in a FITS table.   Setting /EMPTYSTRING will
+;                 avoid this problem by first reading strings into bytes and
+;                 then converting.   However, there is a performance penalty.                 
+;       ERROR_ACTION - Set the on_error action to this value (defaults
+;                to 2).
+;       /FIXED_VAR- Translate variable length columns into fixed length columns
+;                and provide a length column for truly varying columns.
+;                This was only behavior prior to V2.5 for MRDFITS and remains
+;                the default (see /POINTER_VAR)
+;       /FPACK - If set, then assume the FITS file uses FPACK compression 
+;                (http://heasarc.gsfc.nasa.gov/fitsio/fpack/).     To read
+;                an FPACK compressed file, either this must be set or the 
+;                file name must end in ".fz"
+;       /NO_FPACK - If present, then MRDFITS will not uncompress an extension
+;                compressed with FPACK (i.e with a .fz extension), but will 
+;                just read the compressed binary stream. 
+;       /FSCALE - If present and non-zero then scale data to float
+;                numbers for arrays and columns which have either
+;                non-zero offset or non-unity scale.
+;                If scaling parameters are applied, then the corresponding
+;                FITS scaling keywords will be modified.
+;       NO_TDIM  - Disable processing of TDIM keywords.  If NO_TDIM
+;                is specified MRDFITS will ignore TDIM keywords in
+;                binary tables.
+;       /POINTER_VAR- Use pointer arrays for variable length columns.
+;                In addition to changing the format in which
+;                variable length arrays are stored, if the pointer_var
+;                keyword is set to any value other than 1 this also disables
+;                the deletion of variable length columns. (See /FIXED_VAR)
+;                Note that because pointers may be present in the output
+;                structure, the user is responsible for memory management
+;                when deleting or reassigning the structure (e.g. use HEAP_FREE
+;                first).
+;       RANGE  - A scalar or two element vector giving the start
+;                and end rows to be retrieved.  For ASCII and BINARY
+;                tables this specifies the row number.  For GROUPed data
+;                this will specify the groups.  For array images, this
+;                refers to the last non-unity index in the array.  E.g.,
+;                for a 3 D image with NAXIS* values = [100,100,1], the
+;                range may be specified as 0:99, since the last axis
+;                is suppressed.  Note that the range uses IDL indexing
+;                So that the first row is row 0.
+;                If only a single value, x, is given in the range,
+;                the range is assumed to be [0,x-1].
+;       ROWS -  A scalar or vector specifying a  specific row or rows to read 
+;               (first row is 0).   For example to read rows 0,
+;               12 and 23 only, set ROWS=[0,12,23].   Valid for images, ASCII 
+;               and binary tables, but not GROUPed data.   For images
+;               the row numbers refer to the last non-unity index in the array.
+;               Note that the use of the ROWS will not improve the speed of
+;               MRDFITS since the entire table will be read in, and then subset
+;               to the specified rows.     Cannot be used at the same time as 
+;               the RANGE keyword
+;       /SILENT - Suppress informative messages.
+;       STRUCTYP - The structyp keyword specifies the name to be used
+;                for the structure defined when reading ASCII or binary
+;                tables.  Generally users will not be able to conveniently
+;                combine data from multiple files unless the STRUCTYP
+;                parameter is specified.  An error will occur if the
+;                user specifies the same value for the STRUCTYP keyword
+;                in calls to MRDFITS in the same IDL session for extensions
+;                which have different structures.
+;       /UNSIGNED - For integer data with appropriate zero points and scales
+;                read the data into unsigned integer arrays.
+;       /USE_COLNUM - When creating column names for binary and ASCII tables
+;                MRDFITS attempts to use the appropriate TTYPE keyword
+;                values.  If USE_COLNUM is specified and non-zero then
+;                column names will be generated as 'C1, C2, ... 'Cn'
+;                for the number of columns in the table.
+;       /VERSION Print the current version number
+;
+; OPTIONAL OUTPUT KEYWORDS:
+;       EXTNUM - the number of the extension actually read.   Useful if the
+;                 user specified the extension by name.
+;       OUTALIAS - This is a 2xn string array where the first column gives the
+;                actual structure tagname, and the second gives the
+;                corresponding FITS keyword name (e.g. in the TTYPE keyword).   
+;                This array can be passed directly to
+;                the alias keyword of MWRFITS to recreate the file originally
+;                read by MRDFITS.
+;       STATUS - A integer status indicating success or failure of
+;                the request.  A status of >=0 indicates a successful read.
+;                Currently
+;                    0 -> successful completion
+;                   -1 -> error
+;                   -2 -> end of file
+;
+; EXAMPLES:
+;       (1) Read a FITS primary array:
+;               a = mrdfits('TEST.FITS')    or
+;               a = mrdfits('TEST.FITS', 0, header)
+;       The second example also retrieves header information.
+;
+;       (2) Read rows 10-100 of the second extension of a FITS file.
+;               a = mrdfits('TEST.FITS', 2, header, range=[10,100])
+;
+;       (3) Read a table and ask that any scalings be applied and the
+;       scaled data be converted to doubles.  Use simple column names,
+;       suppress outputs.
+;               a = mrdfits('TEST.FITS', 1, /dscale, /use_colnum, /silent)
+;
+;       (4) Read rows 3, 34 and 52 of a binary table and request that 
+;           variable length columns be stored as a pointer variable in the 
+;           output structure
+;              a = mrdfits('TEST.FITS',1,rows=[3,34,52],/POINTER)
+  
+; RESTRICTIONS:
+;       (1)     Cannot handle data in non-standard FITS formats.
+;       (2)     Doesn't do anything with BLANK or NULL values or
+;               NaN's.  They are just read in.  They may be scaled
+;               if scaling is applied.
+;       (3)     Does not automatically detect a FPACK compressed file.  Either
+;               the file name must end in .fz, or the /FPACK keyword must
+;               be set
+; NOTES:
+;       This multiple format FITS reader is designed to provide a
+;       single, simple interface to reading all common types of FITS data.
+;       MRDFITS DOES NOT scale data by default.  The FSCALE or DSCALE
+;       parameters must be used.
+;
+;       Null values in an FITS ASCII table are converted to NaN (floating data),
+;       or -2147483647L (longwords) or '...' (strings).   
+;
+; PROCEDURES USED:
+;       The following procedures are contained in the main MRDFITS program.
+;           MRD_IMAGE           -- Generate array/structure for images.
+;           MRD_READ_IMAGE      -- Read image data.
+;           MRD_ASCII           -- Generate structure for ASCII tables.
+;           MRD_READ_ASCII      -- Read an ASCII table.
+;           MRD_TABLE           -- Generate structure for Binary tables.
+;           MRD_READ_TABLE      -- Read binary table info.
+;           MRD_READ_HEAP       -- Read variable length record info.
+;           MRD_SCALE           -- Apply scaling to data.
+;           MRD_COLUMNS         -- Extract columns.
+;
+;        Other ASTRON Library routines used
+;           FXPAR(), FXADDPAR, FXPOSIT, FXMOVE(), MATCH, MRD_STRUCT(), MRD_SKIP 
+;
+; MODIfICATION HISTORY:
+;       V1.0 November 9, 1994 ----  Initial release.
+;          Creator: Thomas A. McGlynn
+;       V1.1 January 20, 1995 T.A. McGlynn
+;          Fixed bug in variable length records.
+;          Added TDIM support -- new routine mrd_tdim in MRD_TABLE.
+;       V1.2
+;          Added support for dynamic decompression of files.
+;          Fixed further bugs in variable length record handling.
+;       V1.2a
+;          Added NO_TDIM keyword to turn off TDIM processing for
+;          those who don't want it.
+;          Bug fixes: Handle one row tables correctly, use BZERO rather than
+;               BOFFSET.     Fix error in scaling of images.  
+;       V1.2b 
+;          Changed MRD_HREAD to handle null characters in headers.
+;       V2.0 April 1, 1996
+;          -Handles FITS tables with an arbitrary number of columns.
+;          -Substantial changes to MRD_STRUCT to allow the use of
+;          substructures when more than 127 columns are desired.
+;          -All references to table columns are now made through the
+;          functions MRD_GETC and MRD_PUTC.  See description above.
+;          -Use of SILENT will now eliminate compilation messages for
+;          temporary functions.
+;          -Bugs in handling of variable length columns with either
+;          a single row in the table or a maximum of a single element
+;          in the column fixed.
+;          -Added support for DCOMPLEX numbers in binary tables (M formats) for
+;          IDL versions above 4.0.  
+;          -Created regression test procedure to check in new versions.
+;          -Added error_action parameter to allow user to specify
+;          on_error action.  This should allow better interaction with
+;          new CHECK facility.  ON_ERROR statements deleted from
+;          most called routines.
+;          - Modified MRDFITS to read in headers containing null characters
+;          with a warning message printed.
+;       V2.0a April 16, 1996
+;          - Added IS_IEEE_BIG() checks (and routine) so that we don't
+;          worry about IEEE to host conversions if the machine's native
+;          format is IEEE Big-endian.
+;       V2.1 August 24, 1996
+;          - Use resolve_routine for dynamically defined functions
+;          for versions > 4.0
+;          - Fix some processing in random groups format.
+;          - Handle cases where the data segment is--legally--null.
+;          In this case MRDFITS returns a scalar 0.
+;          - Fix bugs with the values for BSCALE and BZERO (and PSCAL and
+;          PZERO) parameters set by MRDFITS.
+;       V2.1a April 24, 1997  Handle binary tables with zero length columns
+;       V2.1b May 13,1997 Remove whitespace from replicate structure definition
+;       V2.1c May 28,1997 Less strict parsing of XTENSION keyword
+;       V2.1d June 16, 1997 Fixed problem for >32767 entries introduced 24-Apr
+;       V2.1e Aug 12, 1997 Fixed problem handling double complex arrays
+;       V2.1f Oct 22, 1997 IDL reserved words can't be structure tag names
+;       V2.1g Nov 24, 1997 Handle XTENSION keywords with extra blanks.
+;       V2.1h Jul 26, 1998 More flexible parsing of TFORM characters
+;       V2.2 Dec 14, 1998 Allow fields with longer names for
+;                        later versions of IDL.
+;                        Fix handling of arrays in scaling routines.
+;                        Allow >128 fields in structures for IDL >4.0
+;                        Use more efficient structure copying for
+;                        IDL>5.0
+;       V2.2b June 17, 1999 Fix bug in handling case where
+;                           all variable length columns are deleted
+;                           because they are empty.
+;       V2.3 March 7, 2000 Allow user to supply file handle rather
+;                          than file name.
+;                          Added status field.
+;                          Now needs FXMOVE routine
+;       V2.3b April 4, 2000
+;                          Added compress option (from D. Palmer)
+;       V2.4  July 4, 2000 Added STATUS=-1 for "File access error" (Zarro/GSFC)
+;       V2.4a May 2, 2001  Trim binary format string   (W. Landsman)
+;       V2.5 December 5, 2001 Add unsigned, alias, 64 bit integers. version, $
+;                           /pointer_val, /fixed_var.
+;       V2.5a Fix problem when both the first and the last character
+;            in a TTYPEnn value are invalid structure tag characters
+;       V2.6 February 15, 2002 Fix error in handling unsigned numbers, $
+;                           and 64 bit unsigneds. (Thanks to Stephane Beland)
+;       V2.6a September 2, 2002 Fix possible conflicting data structure for
+;                          variable length arrays (W. Landsman)
+;       V2.7 July, 2003  Added Rows keyword (W. Landsman)
+;       V2.7a September  2003 Convert dimensions to long64 to handle huge files
+;       V2.8 October 2003 Use IDL_VALIDNAME() function to ensure valid tag names
+;                         Removed OLD_STRUCT and TEMPDIR keywords W. Landsman
+;       V2.9 February 2004 Added internal MRD_FXPAR procedure for faster
+;                     processing of binary table headers E. Sheldon
+;       V2.9a March 2004 Restore ability to read empty binary table W. Landsman
+;             Swallow binary tables with more columns than given in TFIELDS
+;       V2.9b Fix to ensure order of TFORMn doesn't matter
+;       V2.9c Check if extra degenerate NAXISn keyword are present W.L. Oct 2004 
+;       V2.9d Propagate /SILENT to MRD_HREAD, more LONG64 casting W. L. Dec 2004
+;       V2.9e Add typarr[good] to fix a problem reading zero-length columns
+;             A.Csillaghy, csillag@ssl.berkeley.edu (RHESSI)
+;       V2.9f Fix problem with string variable binary tables, possible math 
+;             overflow on non-IEEE machines  WL Feb. 2005 
+;       V2.9g Fix problem when setting /USE_COLNUM   WL Feb. 2005
+;       V2.10 Use faster keywords to BYTEORDER  WL May 2006
+;       V2.11  Add ON_IOERROR, CATCH, and STATUS keyword to MRD_READ_IMAGE to
+;             trap EOF in compressed files DZ  Also fix handling of unsigned 
+;             images when BSCALE not present  K Chu/WL   June 2006 
+;       V2.12 Allow extension to be specified by name, added EXTNUM keyword
+;                     WL    December 2006
+;       V2.12a Convert ASCII table column to DOUBLE if single precision is
+;                 insufficient  
+;       V2.12b Fixed problem when both /fscale and /unsigned are set 
+;                  C. Markwardt    Aug 2007
+;       V2.13  Use SWAP_ENDIAN_INPLACE instead of IEEE_TO_HOST and IS_IEEE_BIG
+;                W. Landsman Nov 2007
+;       V2.13a One element vector allowed for file name W.L. Dec 2007
+;       V2.13b More informative error message when EOF found W.L. Jun 2008
+;       V2.14  Use vector form of VALID_NUM(), added OUTALIAS keyword
+;                                       W.L. Aug 2008
+;       V2.15  Use new FXPOSIT which uses on-the-fly byteswapping W.L. Mar 2009
+;       V2.15a Small efficiency updates to MRD_SCALE W.L. Apr 2009
+;       V2.15b Fixed typo introduced Apr 2009
+;       V2.15c Fix bug introduced Mar 2009  when file unit used W.L. July 2009
+;       V2.16  Handle FPACK compressed files    W. L. July 2009
+;       V2.17  Use compile_opt hidden on all routines except mrdfits.pro W.L. July 2009
+;       V2.18  Added /EMPTYSTRING keyword W. Landsman August 2009
+;       V2.18a Fix Columns keyword output, A. Kimball/ W. Landsman Feb 2010
+;       V2.18b Fix bug with /EMPTYSTRING and multidimensional strings
+;                             S. Baldridge/W.L. August 2010
+;       V2.18c Fix unsigned bug caused by compile_opt idl2 WL  Nov 2010
+;       V2.19  Use V6.0 operators WL Nov 2010
+;       V2.19a Fix complex data conversion in variable length tables WL Dec 2010 
+;       V2.19b Fix bug with /FSCALE introduced Nov 2010 WL Jan 2011
+;       V2.19c Fix bug with ROWS keyword introduced Nov 2010 WL Mar 2011
+;       V2.20  Convert Nulls in ASCII tables, better check of duplicate keywords
+;                                            WL May 2011
+;       V2.20a Better error checking for FPACK files  WL October 2012
+;       V2.20b Fix bug in MRD_SCALE introduced Nov 2010 (Sigh) WL Feb 2013
+;       V2.21  Create unique structure tags when FITS column names differ 
+;              only in having a different case   R. McMahon/WL   March 2013
+;       V2.22  Handle 64 bit variable length binary tables WL   April 2014
+;       V2.23  Test version for very large files   
+;-
+PRO mrd_fxpar, hdr, xten, nfld, nrow, rsize, fnames, fforms, scales, offsets
+compile_opt idl2, hidden
+;
+;  Check for valid header.  Check header for proper attributes.
+;
+  S = SIZE(HDR)
+  IF ( S[0] NE 1 ) || ( S[2] NE 7 ) THEN $
+    MESSAGE,'FITS Header (first parameter) must be a string array'
+
+  xten = fxpar(hdr, 'XTENSION')
+  nfld = fxpar(hdr, 'TFIELDS')
+  nrow = long64(fxpar(hdr, 'NAXIS2'))
+  rsize = long64(fxpar(hdr, 'NAXIS1'))
+
+  ;; will extract these for each
+  names = ['TTYPE','TFORM', 'TSCAL', 'TZERO']
+  nnames = n_elements(names)
+
+;  Start by looking for the required TFORM keywords.    Then try to extract it 
+;  along with names (TTYPE), scales (TSCAL), and offsets (TZERO)
+   
+ keyword = STRMID( hdr, 0, 8)
+
+;
+;  Find all instances of 'TFORM' followed by
+;  a number.  Store the positions of the located keywords in mforms, and the
+;  value of the number field in n_mforms
+;
+
+  mforms = WHERE(STRPOS(keyword,'TFORM') GE 0, n_mforms)
+  if n_mforms GT nfld then begin
+        message,/CON, $
+        'WARNING - More columns found in binary table than specified in TFIELDS'
+        n_mforms = nfld
+        mforms = mforms[0:nfld-1]
+  endif
+
+
+  IF ( n_mforms GT 0 ) THEN BEGIN
+      numst= STRMID(hdr[mforms], 5 ,3)      
+ 
+      igood = WHERE(VALID_NUM(numst,/INTEGER), n_mforms)
+      IF n_mforms GT 0 THEN BEGIN
+          mforms = mforms[igood]
+          number = fix( numst[igood])
+          numst = numst[igood]
+      ENDIF
+
+  ENDIF ELSE RETURN              ;No fields in binary table
+
+  ;; The others
+  fnames = strarr(n_mforms)
+  fforms = strarr(n_mforms) 
+  scales = dblarr(n_mforms)
+  offsets = dblarr(n_mforms)
+
+  ;;comments = strarr(n_mnames)
+
+  fnames_names  = 'TTYPE'+numst
+  scales_names  = 'TSCAL'+numst
+  offsets_names = 'TZERO'+numst
+  number = number -1    ;Make zero-based
+
+
+  match, keyword, fnames_names, mkey_names, mnames, count = N_mnames
+
+  match, keyword, scales_names, mkey_scales, mscales, count = N_mscales
+
+  match, keyword, offsets_names, mkey_offsets, moffsets,count = N_moffsets
+
+  FOR in=0L, nnames-1 DO BEGIN 
+
+      CASE names[in] OF
+          'TTYPE': BEGIN 
+              tmatches = mnames 
+              matches = mkey_names
+              nmatches = n_mnames
+              result = fnames
+          END 
+          'TFORM': BEGIN 
+              tmatches = lindgen(n_mforms)
+              matches = mforms
+              nmatches = n_mforms
+              result = fforms
+          END 
+          'TSCAL': BEGIN 
+              tmatches = mscales
+              matches = mkey_scales
+              nmatches = n_mscales
+              result = scales
+          END 
+          'TZERO': BEGIN 
+              tmatches = moffsets
+              matches = mkey_offsets
+              nmatches = n_moffsets
+              result = offsets
+          END 
+          ELSE: message,'What?'
+      ENDCASE 
+
+      ;;help,matches,nmatches
+
+;
+;  Extract the parameter field from the specified header lines.  If one of the
+;  special cases, then done.
+;
+      IF nmatches GT 0 THEN BEGIN 
+          
+          ;; "matches" is a subscript for hdr and keyword.
+          ;; get just the matches in line
+          
+          line = hdr[matches]
+          svalue = STRTRIM( STRMID(line,9,71),2)
+          
+          FOR i = 0, nmatches-1 DO BEGIN
+              IF ( STRMID(svalue[i],0,1) EQ "'" ) THEN BEGIN
+
+                  ;; Its a string
+                  test = STRMID( svalue[i],1,STRLEN( svalue[i] )-1)
+                  next_char = 0
+                  off = 0
+                  value = ''
+;
+;  Find the next apostrophe.
+;
+NEXT_APOST:
+                  endap = STRPOS(test, "'", next_char)
+                  IF endap LT 0 THEN MESSAGE,         $
+                    'WARNING: Value of '+nam+' invalid in '+ " (no trailing ')", /info
+                  value = value + STRMID( test, next_char, endap-next_char )
+;
+;  Test to see if the next character is also an apostrophe.  If so, then the
+;  string isn't completed yet.  Apostrophes in the text string are signalled as
+;  two apostrophes in a row.
+;
+                  IF STRMID( test, endap+1, 1) EQ "'" THEN BEGIN    
+                      value = value + "'"
+                      next_char = endap+2      
+                      GOTO, NEXT_APOST
+                  ENDIF
+
+                 
+;
+;  If not a string, then separate the parameter field from the comment field.
+;
+              ENDIF ELSE BEGIN
+                  ;; not a string
+                  test = svalue[I]
+                  slash = STRPOS(test, "/")
+                  IF slash GT 0 THEN  test = STRMID(test, 0, slash)
+                 
+;
+;  Find the first word in TEST.  Is it a logical value ('T' or 'F')?
+;
+                  test2 = test
+                  value = GETTOK(test2,' ')
+                  test2 = STRTRIM(test2,2)
+                  IF ( value EQ 'T' ) THEN BEGIN
+                      value = 1
+                  END ELSE IF ( value EQ 'F' ) THEN BEGIN
+                      value = 0
+                  END ELSE BEGIN
+;
+;  Test to see if a complex number.  It's a complex number if the value and the
+;  next word, if any, both are valid numbers.
+;
+                      IF STRLEN(test2) EQ 0 THEN GOTO, NOT_COMPLEX
+                      test2 = GETTOK(test2,' ')
+                      IF VALID_NUM(value,val1) && VALID_NUM(value2,val2) $
+                        THEN BEGIN
+                          value = COMPLEX(val1,val2)
+                          GOTO, GOT_VALUE
+                      ENDIF
+;
+;  Not a complex number.  Decide if it is a floating point, double precision,
+;  or integer number.  If an error occurs, then a string value is returned.
+;  If the integer is not within the range of a valid long value, then it will 
+;  be converted to a double.  
+;
+NOT_COMPLEX:
+                      ON_IOERROR, GOT_VALUE
+                      value = test
+                      IF ~VALID_NUM(value) THEN GOTO, GOT_VALUE
+
+                      IF (STRPOS(value,'.') GE 0) || (STRPOS(value,'E') $
+                                                      GE 0) || (STRPOS(value,'D') GE 0) THEN BEGIN
+                          IF ( STRPOS(value,'D') GT 0 ) || $
+                            ( STRLEN(value) GE 8 ) THEN BEGIN
+                              value = DOUBLE(value)
+                          END ELSE value = FLOAT(value)
+                      ENDIF ELSE BEGIN
+                          lmax = long64(2)^31 - 1
+                          lmin = -long64(2)^31
+                          value = long64(value)
+                          if (value GE lmin) && (value LE lmax) THEN $
+                            value = LONG(value)
+                      ENDELSE
+                      
+;
+GOT_VALUE:
+                      ON_IOERROR, NULL
+                  ENDELSE
+              ENDELSE           ; if string
+;
+;  Add to vector if required.
+;
+              
+              result[tmatches[i]] = value
+             
+          ENDFOR
+
+          CASE names[in] OF
+              'TTYPE': fnames[number] = strtrim(result, 2)
+              'TFORM': fforms[number] = strtrim(result, 2)
+              'TSCAL': scales[number] = result
+              'TZERO': offsets[number] = result
+              ELSE: message,'What?'
+          ENDCASE 
+
+;
+;  Error point for keyword not found.
+;
+      ENDIF
+;
+
+
+
+  ENDFOR 
+END
+  
+
+; Get a tag name give the column name and index
+function  mrd_dofn, name, index, use_colnum, alias=alias
+compile_opt idl2, hidden
+    ; Check if the user has specified an alias.
+
+    name = N_elements(name) EQ 0 ? 'C' + strtrim(index,2) : strtrim(name) 
+    if keyword_set(alias) then begin
+	sz = size(alias)
+	
+	if (sz[0] eq 1 || sz[0] eq 2) && (sz[1] eq 2) && (sz[sz[0]+1] eq 7) $
+	   then begin
+	    w = where( name eq alias[1,*], Nw)
+	    if Nw GT 0 then name = alias[0,w[0]];
+	endif
+    endif
+    ; Convert the string name to a valid variable name.  If name 
+    ; is not defined generate the string Cnn when nn is the index 
+    ; number.
+
+    table = 0
+     if ~use_colnum && (N_elements(name) GT 0)  then begin 
+        if size(name,/type) eq 7 then begin 
+            str = name[0] 
+        endif else str = 'C'+strtrim(index,2) 
+     endif else str = 'C'+strtrim(index,2) 
+  
+    return, IDL_VALIDNAME(str,/CONVERT_ALL) 
+ 
+end 
+
+;***************************************************************
+
+
+
+; Parse the TFORM keyword and return the type and dimension of the 
+; data. 
+pro mrd_doff, form, dim, type 
+compile_opt idl2, hidden 
+    ; Find the first non-numeric character. 
+ 
+    len = strlen(form) 
+ 
+    if len le 0 then return 
+ 
+    i = stregex( form, '[^0-9]')       ;Position of first non-numeric character
+ 
+    if i lt 0 then return              ;Any non-numeric character found?
+ 
+    if i gt 0 then begin 
+        dim = long(strmid(form, 0, i)) 
+        if dim EQ 0l then dim = -1l
+    endif else dim = 0
+ 
+    type = strmid(form, i, 1) 
+end 
+
+
+
+;*********************************************************************
+
+;  Check that this name is unique with regard to other column names.
+
+function mrd_chkfn, name, namelist, index, silent=silent
+  compile_opt idl2, hidden
+     ;
+     ;
+
+     maxlen = 127
+
+     if strlen(name) gt maxlen then name = strmid(name, 0, maxlen)
+     ; make case insensitive since structure tags are case insensitive
+     ; (rgm 2013-03-03)
+     ;if ~array_equal(namelist eq name,0b ) then begin
+     if ~array_equal(strupcase(namelist) eq strupcase(name),0b ) then begin
+
+         oldname=name
+         name = 'gen$name_'+strcompress(string(index+1),/remove_all)
+
+         ; report the column name conflict
+         if ~keyword_set(silent) then print, 'Column name conflict: ', $
+           index, ': ', oldname, ' -> ', name
+
+     endif
+
+     return, name
+end     
+
+; Find the appropriate offset for a given unsigned type.
+; The type may be given as the bitpix value or the IDL
+; variable type.
+
+function mrd_unsigned_offset, type
+compile_opt idl2, hidden
+    	    
+    if (type eq 12) || (type eq 16) then begin
+	return, uint(32768)
+    endif else if (type eq 13) || (type eq 32) then begin
+	return, ulong('2147483648')
+    endif else if (type eq 15) || (type eq 64) then begin
+	return, ulong64('9223372036854775808');
+    endif
+    return, 0
+end
+
+
+
+; Can we treat this data as unsigned?
+
+function mrd_chkunsigned, bitpix, scale, zero, unsigned=unsigned
+compile_opt idl2, hidden
+    if ~keyword_set(unsigned) then return, 0
+    
+    ; This is correct but we should note that
+    ; FXPAR returns a double rather than a long.
+    ; Since the offset is a power of two
+    ; it is an integer that is exactly representable
+    ; as a double.  However, if a user were to use
+    ; 64 bit integers and an offset close to but not
+    ; equal to 2^63, we would erroneously assume that
+    ; the dataset was unsigned...
+
+    if scale eq 1 then begin
+	if (bitpix eq 16 && zero eq 32768L) ||                   $
+	   (bitpix eq 32 && zero eq 2147483648UL) ||        $
+	   (bitpix eq 64 && zero eq 9223372036854775808ULL) then return,1
+    endif
+    
+    return, 0
+end
+
+; Is this one of the IDL unsigned types?
+function mrd_unsignedtype, data
+ compile_opt idl2, hidden      
+ type = size(data,/type) 
+ 
+    if (type eq 12) || (type eq 13) || (type eq 15) then return, type $
+                                                    else return, 0
+    
+end
+    
+; Return the currrent version string for MRDFITS
+function mrd_version
+compile_opt idl2, hidden
+    return, '2.23 '
+end
+;=====================================================================
+; END OF GENERAL UTILITY FUNCTIONS ===================================
+;=====================================================================
+
+
+; Parse the TFORM keyword and return the type and dimension of the
+; data.
+pro mrd_atype, form, type, slen
+compile_opt idl2, hidden
+
+    ; Find the first non-numeric character.
+
+
+    ; Get rid of blanks.
+    form = strcompress(form,/remove_all)
+    len = strlen(form)
+    if len le 0 then return
+
+    type = strmid(form, 0,1)
+    length = strmid(form,1,len-1)
+    ;
+    ; Ignore the number of decimal places.  We assume that there
+    ; is a decimal point.
+    ;
+    p = strpos(length, '.')
+    if p gt 0 then length = strmid(length,0,p)
+
+   if strlen(length) gt 0 then slen = fix(length) else slen = 1
+   if (type EQ 'F') || (type EQ 'E') then $     ;Updated April 2007
+        if (slen GE 8) then type = 'D'
+
+end
+
+
+; Read in the table information.
+pro mrd_read_ascii, unit, range, nbytes, nrows, nfld, typarr, posarr,   $
+     lenarr, nullarr, table, old_struct=old_struct, rows=rows
+compile_opt idl2, hidden
+    ;
+    ; Unit          Unit to read data from.
+    ; Range         Range of  to be read
+    ; Nbytes        Number of bytes per row.
+    ; Nrows         Number of rows.
+    ; Nfld          Number of fields in structure.
+    ; Typarr        Array indicating type of variable.
+    ; Posarr        Starting position of fields (first char at 0)
+    ; Lenarr        Length of fields
+    ; Nullarr       Array of null values
+    ; Table         Table to read information into.
+    ; Old_struct    Should recursive structure format be used?
+
+    bigstr = bytarr(nbytes, range[1]-range[0]+1)
+
+    if range[0] gt 0 then mrd_skip, unit, nbytes*range[0]
+    readu,unit, bigstr
+    if N_elements(rows) GT 0 then bigstr = bigstr[*,rows-range[0]] 
+
+    ; Skip to the end of the data area.
+
+    nSkipRow = nrows - range[1] - 1
+    nskipB = 2880 - (nbytes*nrows) mod 2880
+    if nskipB eq 2880 then nskipB = 0
+
+    mrd_skip, unit, nskipRow*nbytes+nskipB
+
+    s1 = posarr-1
+    s2 = s1 + lenarr - 1
+    for i=0, nfld-1 do begin
+        flds = strtrim(bigstr[s1[i]:s2[i],* ]) 
+       if nullarr[i] ne '' then begin
+	    
+		curr_col = table.(i)
+           w = where(flds NE strtrim(nullarr[i]), Ngood)
+	 
+            if Ngood GT 0 then begin
+                if N_elements(w) EQ 1 then w = w[0]
+                if typarr[i] eq 'I' then begin
+                    curr_col[w] = long(flds[w])
+                endif else if typarr[i] eq 'E' || typarr[i] eq 'F' then begin
+                    curr_col[w] = float(flds[w])
+                endif else if typarr[i] eq 'D' then begin
+                    curr_col[w] = double(flds[w])
+                endif else if typarr[i] eq 'A' then begin
+                    curr_col[w] = flds[w]
+                endif
+            endif
+
+                table.(i) = curr_col
+                
+        endif else begin
+                
+   
+
+           if typarr[i] eq 'I' then begin
+                    table.(i) =  long(flds)
+            endif else if typarr[i] eq 'E' || typarr[i] eq 'F' then begin
+                    table.(i) = float(flds)
+            endif else if typarr[i] eq 'D' then begin
+                    table.(i) = double(flds)
+             endif else if typarr[i] eq 'A' then begin
+                    table.(i) = flds
+            endif
+        endelse
+    endfor
+
+end
+
+
+; Define a structure to hold a FITS ASCII table.
+pro mrd_ascii, header, structyp, use_colnum,   $
+    range, table, $
+    nbytes, nrows, nfld, typarr, posarr, lenarr, nullarr, $
+    fnames, fvalues, scales, offsets, scaling, status, rows = rows, $
+    silent=silent, columns=columns, alias=alias, outalias=outalias
+compile_opt idl2, hidden
+    ;
+    ; Header                FITS header for table.
+    ; Structyp              IDL structure type to be used for
+    ;                       structure.
+    ; Use_colnum            Use column numbers not names.
+    ; Range                 Range of rows of interest
+    ; Table                 Structure to be defined.
+    ; Nbytes                Bytes per row
+    ; Nrows                 Number of rows in table
+    ; Nfld                  Number of fields
+    ; Typarr                Array of field types
+    ; Posarr                Array of field offsets
+    ; Lenarr                Array of field lengths
+    ; Nullarr               Array of field null values
+    ; Fname                 Column names
+    ; Fvalues               Formats for columns
+    ; Scales/offsets        Scaling factors for columns
+    ; Scaling               Do we need to scale?
+    ; Status                Return status.
+
+    table = 0
+
+    types  = ['I', 'E', 'F', 'D', 'A']
+; Set default 'null' values   
+    sclstr = ['-2147483647L', '!VALUES.f_nan', '!VALUES.f_nan', '!VALUES.d_nan', '...']
+    status = 0
+
+    if strmid(fxpar(header, 'XTENSION'),0,8) ne 'TABLE   ' then begin
+        message, 'ERROR - Header is not from ASCII table.',/CON
+        status = -1;
+        return
+    endif
+
+    nfld = fxpar(header, 'TFIELDS')
+    nrows = long64( fxpar(header, 'NAXIS2'))
+    nbytes = long64( fxpar(header, 'NAXIS1'))
+ 
+    if range[0] ge 0 then begin
+        range[0] = range[0] < (nrows-1)
+        range[1] = range[1] < (nrows-1)
+    endif else begin
+        range[0] = 0
+        range[1] = nrows-1
+    endelse
+
+    if N_elements(rows) EQ 0 then nrows = range[1] - range[0] + 1 else begin
+          bad = where(rows GT nrows, Nbad)
+          if Nbad GT 0  then begin 
+             message,/CON,'ERROR: Row numbers must be between 0 and ' + $
+                      strtrim(nrows-1,2)
+             status = -1
+             return
+          endif      
+          nrows = N_elements(rows)
+     endelse
+
+    if nrows le 0 then begin
+        if ~keyword_set(silent) then begin
+            print,'MRDFITS: ASCII table.  ',strcompress(string(nfld)),  $
+                  ' columns, no rows'
+        endif
+        return
+    endif
+
+    ;
+    ;  Loop over the columns
+
+    typarr  = strarr(nfld)
+    lenarr  = intarr(nfld)
+    posarr  = intarr(nfld)
+    nullarr = strarr(nfld)
+    fnames  = strarr(nfld)
+    fvalues = strarr(nfld)
+    scales  = dblarr(nfld)
+    offsets = dblarr(nfld)
+    tname  =  strarr(nfld)
+
+    for i=0, nfld-1 do begin
+        suffix = strcompress(string(i+1), /remove_all)
+        fname = fxpar(header, 'TTYPE' + suffix, count=cnt)
+	tname[i] = fname
+	if cnt eq 0 then xx = temporary(fname)
+        fform = fxpar(header, 'TFORM' + suffix)
+        fpos = fxpar(header, 'TBCOL' + suffix)
+        fnull = fxpar(header, 'TNULL' + suffix, count=cnt)
+	if cnt eq 0 then fnull = ''
+        scales[i] = fxpar(header, 'TSCAL' + suffix)
+        if scales[i] eq 0.0d0 then scales[i] = 1.0d0
+        offsets[i] = fxpar(header, 'TZERO'+suffix)
+        
+        fname = strupcase( mrd_dofn(fname,i+1, use_colnum, alias=alias))
+
+        if i GT 0 then fname = mrd_chkfn(fname, fnames, i, SILENT=silent) ;Check for duplicates
+	fnames[i] = fname
+        
+        mrd_atype, fform, ftype, flen
+        typarr[i] = ftype
+        lenarr[i] = flen
+        posarr[i] = fpos
+        nullarr[i] = fnull
+        
+ 
+       j = where(types EQ ftype, Nj) 
+       if Nj EQ 0 then begin 
+                message, 'Invalid format code:'+ ftype + ' for column ' + $
+		    strtrim(i+1,2),/CON
+                status = -1
+                return
+       endif	       
+       fvalues[i] = ftype NE 'A' ? sclstr[j] : $
+	                  'string(replicate(32b,'+strtrim(flen,2)+'))'
+                                               
+         
+    endfor
+    
+    if scaling then $
+        scaling = ~array_equal(scales,1.0d0) || ~array_equal(offsets,0.0)
+   
+    if ~scaling && ~keyword_set(columns) then begin
+        table = mrd_struct(fnames, fvalues, nrows, structyp=structyp, $
+           silent=silent)
+    endif else begin
+        table = mrd_struct(fnames, fvalues, nrows, silent=silent)
+    endelse
+
+    if ~keyword_set(silent) then begin
+        print,'MRDFITS: ASCII table.  ',strcompress(string(nfld)),  $
+         ' columns by ',strcompress(string(nrows)), ' rows.'
+    endif
+    
+    outalias = transpose([ [tag_names(table)],[tname] ] )
+    status = 0
+    return
+
+end
+
+
+; Eliminate columns from the table that do not match the
+; user specification.
+pro  mrd_columns, table, columns, fnames, fvalues, $
+    vcls, vtpes, scales,  offsets, scaling,        $
+    structyp=structyp, silent=silent
+compile_opt idl2, hidden
+
+
+
+    type = size(columns,/type)
+    nele = N_elements(columns)
+    if type eq 8 || type eq 6 || type eq 0 then return  ; Can't use structs
+                                                    ; or complex.
+
+    if type eq 4 || type eq 5 then tcols = fix(columns)
+    if type eq 1 || type eq 2 || type eq 3 then tcols = columns
+
+    ; Convert strings to uppercase and compare with column names.
+
+    if type eq 7 then begin
+       match, strupcase(columns), strupcase(fnames), tmp, tcols,count=nmatch 
+       if Nmatch GT 0 then begin 
+              s = sort(tmp)             ;Sort order of supplied column name
+              tcols = tcols[s] + 1
+       endif     
+     endif
+
+    ; Subtract one from column indices and check that all indices >= 0.
+    if n_elements(tcols) gt 0 then begin
+        tcols = tcols-1
+        w = where(tcols ge 0, Nw)
+        if Nw EQ 0 then dummy = temporary(tcols)
+    endif
+
+    if n_elements(tcols) le 0 then begin
+        print, 'MRDFITS:  No columns match'
+        
+        ; Undefine variables.  First ensure they are defined, then
+        ; use temporary() to undefine them.
+        table = 0
+        fnames = 0
+        fvalues = 0
+        vcls = 0
+        vtpes = 0
+        scales = 0
+        offsets = 0
+        dummy = temporary(fnames)
+        dummy = temporary(fvalues)
+        dummy = temporary(vcls)
+        dummy = temporary(vtpes)
+        dummy = temporary(scales)
+        dummy = temporary(offsets)
+        scaling = 0
+        
+    endif else begin
+
+        ; Replace arrays with only desired columns.
+        
+        fnames = fnames[tcols]
+        fvalues = fvalues[tcols]
+        
+        ; Check if there are still variable length columns.
+        if n_elements(vcls) gt 0 then begin
+            vcls = vcls[tcols]
+            vtpes = vtpes[tcols]
+            w = where(vcls eq 1, Nw)
+            if Nw EQ 0 then begin
+                dummy = temporary(vcls)
+                dummy = temporary(vtpes)
+            endif
+        endif
+        
+        ; Check if there are still columns that need scaling.
+        if n_elements(scales) gt 0 then begin
+            scales = scales[tcols]
+            offsets = offsets[tcols]
+	    scaling = ~array_equal(scales,1.d0) || ~array_equal(offsets,0.0)
+         endif
+        
+
+        ndim = n_elements(table)
+        
+        if scaling || n_elements(vcls) gt 0 then begin
+            tabx = mrd_struct(fnames, fvalues, ndim, silent=silent )
+        endif else begin
+            tabx = mrd_struct(fnames, fvalues, ndim, structyp=structyp, silent=silent )
+        endelse
+        
+        for i=0, n_elements(tcols)-1 do $
+                tabx.(i) = table.(tcols[i]);
+ 
+        table = temporary(tabx)
+    endelse
+    
+end
+
+
+; Read in the image information. 
+pro mrd_read_image, unit, range, maxd, rsize, table, rows = rows,status=status, $
+     unixpipe = unixpipe
+ compile_opt idl2, hidden
+    ; 
+    ; Unit          Unit to read data from. 
+    ; Table         Table/array to read information into. 
+    ; 
+
+    error=0
+    catch,error
+    if error ne 0 then begin
+     catch,/cancel
+     status=-2
+     return
+    endif
+
+    ; If necessary skip to beginning of desired data. 
+
+    if range[0] gt 0 then mrd_skip, unit, range[0]*rsize
+
+    status=-2
+    if rsize eq 0 then return 
+
+    on_ioerror,done
+    readu, unit, table
+
+    if N_elements(rows) GT 0 then begin
+    row1 = rows- range[0]
+    case size(table,/n_dimen) of 
+    1: table = table[row1]
+    2: table = table[*,row1]
+    3: table = table[*,*,row1]
+    4: table = table[*,*,*,row1]
+    5: table = table[*,*,*,*,row1]
+    6: table = table[*,*,*,*,*,row1]
+    7: table = table[*,*,*,*,*,*,row1]
+    8: table = table[*,*,*,*,*,*,*,row1]
+    else: begin 
+          print,'MRDFITS: Subscripted image must be between 1 and 8 dimensions'
+          status = -1
+          return
+          end
+    endcase
+    endif
+
+    ; Skip to the end of the data
+
+    skipB = 2880 - (maxd*rsize) mod 2880
+    if skipB eq 2880 then skipB = 0
+
+    if range[1] lt maxd-1 then $
+        skipB += (maxd-range[1]-1)*rsize
+ 
+    mrd_skip, unit, skipB
+    if unixpipe then swap_endian_inplace, table,/swap_if_little
+
+    ; Fix offset for unsigned data
+    type = mrd_unsignedtype(table)
+    if type gt 0 then $
+	table -= mrd_unsigned_offset(type)
+    
+    status=0
+    done:
+
+;-- probably an EOF 
+
+    if status ne 0 then begin 
+          message,!ERROR_STATE.MSG,/CON
+         free_lun,unit
+    endif
+
+    return
+end 
+
+; Truncate superfluous axes.
+
+pro mrd_axes_trunc,naxis, dims, silent
+compile_opt idl2, hidden
+    mysilent = silent
+    for i=naxis-1,1,-1 do begin 
+
+        if dims[i] eq 1 then begin
+            if ~mysilent then begin
+                print, 'MRDFITS: Truncating unused dimensions'
+                mysilent = 1
+            endif
+            dims = dims[0:i-1] 
+            naxis = naxis - 1 
+        
+        endif else return
+     
+    endfor 
+ 
+    return
+end
+
+; Define structure/array to hold a FITS image. 
+pro mrd_image, header, range, maxd, rsize, table, scales, offsets, scaling, $
+  status, silent=silent, unsigned=unsigned, rows = rows
+ compile_opt idl2, hidden
+    ; 
+    ; Header                FITS header for table. 
+    ; Range                 Range of data to be retrieved. 
+    ; Rsize                 Size of a row or group. 
+    ; Table                 Structure to be defined. 
+    ; Status                Return status
+    ; Silent=silent         Suppress info messages?
+ 
+    table = 0
+
+    ; type    0         1           2         3         4         5  6  7  8  9 10 11        12         13          14          15
+    lens =  [ 0,        1,          2,        4,        4,        8, 0, 0, 0, 0, 0, 0,        2,         4,          8,          8] 
+    typstrs=['',   'Byte',    'Int*2',  'Int*4', 'Real*4', 'Real*8','','','','','','', 'UInt*2',  'Uint*4',    'Int*8',    'Uint*8']
+    typarr= ['', 'bytarr',   'intarr', 'lonarr', 'fltarr', 'dblarr','','','','','','','uintarr', 'ulonarr', 'lon64arr', 'ulon64arr'] 
+ 
+    status = 0 
+ 
+ 
+    naxis = fxpar(header, 'NAXIS') 
+    bitpix= fxpar(header, 'BITPIX') 
+    if naxis gt 0 then begin 
+          dims = long64(fxpar(header, 'NAXIS*', Count = N_axis)) 
+          if N_axis GT naxis then begin
+; Check if extra NAXISn keywords are present (though this is not legal FITS)
+                   nextra = N_axis - naxis
+                   dim_extra = dims[naxis:N_axis-1]
+                   if total(dim_extra) EQ nextra then $
+                        dims = dims[0:naxis-1] else $
+                   message,'ERROR - NAXIS = ' + strtrim(naxis,2) +  $
+                          ' but NAXIS' + strtrim(N_axis,2) + ' keyword present'
+          endif
+   endif else dims = 0
+    
+    gcount = fxpar(header, 'GCOUNT') 
+    pcount = fxpar(header, 'PCOUNT')
+    isgroup = fxpar(header, 'GROUPS')
+    gcount = long(gcount)
+
+    xscale = fxpar(header, 'BSCALE', count=cnt)
+    if cnt eq 0 then xscale = 1      ;Corrected 06/29/06
+    
+    xunsigned = mrd_chkunsigned(bitpix,  xscale, $
+				fxpar(header, 'BZERO'), unsigned=unsigned)
+    ; Note that type is one less than the type signifier returned in the size call.
+    type = -1
+    
+    if ~xunsigned then begin 
+ 
+        if bitpix eq 8        then type = 1     $ 
+        else if bitpix eq  16 then type = 2     $ 
+        else if bitpix eq  32 then type = 3     $ 
+        else if bitpix eq -32 then type = 4     $ 
+        else if bitpix eq -64 then type = 5     $
+        else if bitpix eq  64 then type = 14
+
+    endif else begin
+
+	if bitpix eq 16       then type = 12     $
+	else if bitpix eq  32 then type = 13     $
+	else if bitpix eq  64 then type = 15
+	
+    endelse
+
+    if type eq -1 then begin
+	print,'MRDFITS: Error: Invalid BITPIX: '+strtrim(bitpix)
+	table = 0
+	return
+    endif
+
+    ; Note that for random groups data we must ignore the first NAXISn keyword. 
+    if isgroup GT 0  then begin 
+
+
+        range[0] = range[0] > 0
+        if (range[1] eq -1) then begin
+            range[1] = gcount-1
+        endif else begin
+            range[1] = range[1] < gcount - 1
+        endelse
+	
+	maxd = gcount
+        
+        if (n_elements(dims) gt 1) then begin
+            dims = dims[1:*]
+            naxis = naxis-1
+        endif else begin
+            print, 'MRDFITS: Warning: No data specified for group data.'
+            dims = [0]
+            naxis = 0
+        endelse
+        
+        ; The last entry is the scaling for the sample data.
+        
+        if (pcount gt 0) then begin
+            scales  = dblarr(pcount+1)
+            offsets = dblarr(pcount+1)
+        endif
+        
+        values = strarr(2)
+        
+        
+        mrd_axes_trunc, naxis, dims, keyword_set(silent)
+        
+        values[0] = typarr[type] + "("+string(pcount)+")" 
+        rsize = dims[0] 
+        sarr = "(" + strcompress(string(dims[0]), /remo )
+         
+        for i=1, naxis-1 do begin
+	    
+            sarr = sarr + "," + strcompress(string(dims[i]),/remo)
+            rsize = rsize*dims[i]
+	    
+        endfor 
+         
+        sarr = sarr + ")"
+
+        if ~keyword_set(silent) then print,'MRDFITS--Image with groups:', $
+          ' Ngroup=',strcompress(string(gcount)),' Npar=',                   $
+          strcompress(string(pcount),/remo), ' Group=', sarr, '  Type=',typstrs[type]
+
+        sarr = typarr[type] + sarr
+        values[1] = sarr 
+        rsize = (rsize + pcount)*lens[type] 
+         
+        table = mrd_struct(['params','array'], values, range[1]-range[0]+1, $
+                           silent=silent)
+
+	if xunsigned then begin
+	    fxaddpar,header, 'BZERO', 0, 'Reset by MRDFITS v'+mrd_version()
+	endif
+           
+
+        for i=0, pcount-1 do begin
+	    
+            istr = strcompress(string(i+1),/remo)
+	    
+            scales[i] = fxpar(header, 'PSCAL'+istr)
+            if scales[i] eq 0.0d0 then scales[i] =1.0d0
+	    
+            offsets[i] = fxpar(header, 'PZERO'+istr)
+	    
+            scales[pcount] = fxpar(header, 'BSCALE')
+            if scales[pcount] eq 0.0d0 then scales[pcount] = 1.0d0
+            offsets[pcount] = fxpar(header, 'BZERO')
+	    
+        endfor
+  
+     if scaling then $
+        scaling = ~array_equal(scales,1.0d0) || ~array_equal(offsets,0.0)
+         
+    endif else begin 
+ 
+        if naxis eq 0 then begin
+	
+            rsize = 0 
+            table = 0
+            if ~keyword_set(silent) then $
+                print, 'MRDFITS: Null image, NAXIS=0'
+            return
+	    
+        endif 
+         
+        if gcount gt 1 then begin 
+            dims = [dims, gcount] 
+            naxis = naxis + 1 
+        endif 
+         
+        mrd_axes_trunc, naxis, dims, keyword_set(silent)
+
+                
+        maxd = dims[naxis-1] 
+         
+        if range[0] ne -1 then begin 
+            range[0] = range[0]<(maxd-1) 
+            range[1] = range[1]<(maxd-1) 
+        endif else begin 
+            range[0] = 0 
+            range[1] = maxd - 1 
+        endelse 
+
+        Nlast = dims[naxis-1]   
+        dims[naxis-1] = range[1]-range[0]+1
+        pdims = dims
+        if N_elements(rows) GT 0 then begin
+             if max(rows) GE Nlast then begin 
+               print, 'MRDFITS: Row numbers must be between 0 and ' + $
+                      strtrim(Nlast-1,2)
+               status = -1 & rsize = 0
+               return
+             endif
+             pdims[naxis-1] = N_elements(rows)
+        endif 
+ 
+        if ~keyword_set(silent) then begin
+            str = '('
+            for i=0, naxis-1 do begin
+                if i ne 0 then str = str + ','
+                str = str + strcompress(string(pdims[i]),/remo)
+            endfor
+            str = str+')'
+            print, 'MRDFITS: Image array ',str, '  Type=', typstrs[type]
+        endif
+         
+        rsize = 1
+	
+        if naxis gt 1 then for i=0, naxis - 2 do rsize=rsize*dims[i] 
+        rsize = rsize*lens[type] 
+        sz = lonarr(naxis+3) 
+        sz[0] = naxis 
+        sz[1:naxis] = dims 
+
+	nele = product(dims,/integer)
+         
+        sz[naxis+1] = type   
+        sz[naxis+2] = nele 
+  
+        table = nele GT 0 ? make_array(size=sz) : 0
+	
+        scales = dblarr(1)
+        offsets = dblarr(1)
+
+	if xunsigned then begin
+	    fxaddpar,header, 'BZERO', 0, 'Updated by MRDFITS v'+mrd_version()
+	endif
+	
+        scales[0] = fxpar(header, 'BSCALE')
+        offsets[0] = fxpar(header, 'BZERO')
+	
+        if scales[0] eq 0.0d0 then scales[0] = 1.0d0
+        if scaling && (scales[0] eq 1.0d0) && (offsets[0] eq 0.0d0) then  $
+	          scaling = 0
+    endelse 
+         
+    status = 0 
+    return 
+ 
+end
+
+; Scale an array of pointers
+pro mrd_ptrscale, array, scale, offset
+compile_opt idl2, hidden
+    for i=0, n_elements(array)-1 do begin
+        if ptr_valid(array[i]) then begin
+	    array[i] = ptr_new(*array[i] * scale + offset)
+	endif
+    endfor
+end
+
+; Scale a FITS array or table.
+pro mrd_string, table, header, typarr, $
+               fnames, fvalues, nrec, structyp=structyp, silent=silent
+compile_opt idl2, hidden
+    ;
+    ; Type:         FITS file type, 0=image/primary array
+    ;                               1=ASCII table
+    ;                               2=Binary table
+    ;
+    ; scales:       An array of scaling info
+    ; offsets:      An array of offset information
+    ; table:        The FITS data.
+    ; header:       The FITS header.
+    ; dscale:       Should data be scaled to R*8?
+    ; fnames:       Names of table columns.
+    ; fvalues:      Values of table columns.
+    ; nrec:         Number of records used.
+    ; structyp:     Structure name.
+ 
+    w = where( typarr EQ 'A', Nw, $
+                complement=ww, Ncomplement = Nww)
+		
+    if Nw EQ 0 then return    ;No tags require string conversion? 
+
+; First do ASCII and Binary tables.    We need to create a new structure 
+; because scaling will change the tag data types.
+
+          sclr = "' '"
+          vc = 'strarr'
+                
+           for i=0, Nw-1 do begin
+                col = w[i]
+                sz = size(table[0].(col),/str)
+
+		; Handle pointer columns
+		if sz.type eq 10 then begin
+		    fvalues[col] = 'ptr_new()'
+
+		; Scalar columns
+		endif else if sz.N_dimensions eq 0 then begin
+                    fvalues[col] = sclr
+
+		; Vectors
+                endif else begin
+		    dim = sz.dimensions[0:sz.N_dimensions-1]
+                    fvalues[col] = vc + $
+		      '(' + strjoin(strtrim(dim,2),',') + ')'
+		    
+                endelse
+            endfor
+        tabx = mrd_struct(fnames, fvalues, nrec, structyp=structyp, silent=silent )
+
+; First copy the unscaled columns indexed by ww.     This is actually more 
+; efficient than using STRUCT_ASSIGN since the tag names are all identical,
+; so STRUCT_ASSIGN would copy everything (scaled and unscaled).
+ 	    
+       for i=0, Nww - 1 do tabx.(ww[i]) = table.(ww[i])
+       
+; Now copy the string items indexed by w after converting the byte array        
+       
+        for i=0, Nw - 1 do begin	    
+ 		
+		str = size(tabx.(w[i]),/str)
+		dim = [1,str.dimensions[0:str.N_dimensions-1]]
+                if str.n_dimensions GT 1 then $
+                tabx.(w[i]) = string(reform(table.(w[i]),dim)) else $
+		tabx.(w[i]) = string(table.(w[i]))
+			    
+        endfor
+
+        table = temporary(tabx)   ;Remove original structure from memory
+  
+end
+
+
+; Scale a FITS array or table.
+pro mrd_scale, type, scales, offsets, table, header,  $
+               fnames, fvalues, nrec, dscale = dscale, structyp=structyp, silent=silent
+compile_opt idl2, hidden
+    ;
+    ; Type:         FITS file type, 0=image/primary array
+    ;                               1=ASCII table
+    ;                               2=Binary table
+    ;
+    ; scales:       An array of scaling info
+    ; offsets:      An array of offset information
+    ; table:        The FITS data.
+    ; header:       The FITS header.
+    ; dscale:       Should data be scaled to R*8?
+    ; fnames:       Names of table columns.
+    ; fvalues:      Values of table columns.
+    ; nrec:         Number of records used.
+    ; structyp:     Structure name.
+ 
+    w = where( (scales ne 1.d0)  or (offsets ne 0.d0), Nw, $ 
+                complement=ww, Ncomplement = Nww)
+		
+    if Nw EQ 0 then return    ;No tags require scaling? 
+
+; First do ASCII and Binary tables.    We need to create a new structure 
+; because scaling will change the tag data types.
+
+    if type ne 0 then begin
+        
+        if type eq 1 then begin
+	    fvalues[w] = keyword_set(dscale) ? '0.0d0' : '0.0 
+        endif else if type eq 2 then begin
+
+            if keyword_set(dscale) then begin
+                sclr = '0.d0'
+                vc = 'dblarr'
+            endif else begin
+                sclr = '0.0'
+                vc = 'fltarr'
+            endelse
+                
+           for i=0, Nw-1 do begin
+                col = w[i]
+                sz = size(table[0].(col),/str)
+
+		; Handle pointer columns
+		if sz.type eq 10 then begin
+		    fvalues[col] = 'ptr_new()'
+
+		; Scalar columns
+		endif else if sz.N_dimensions eq 0 then begin
+                    fvalues[col] = sclr
+
+		; Vectors
+                endif else begin
+		    dim = sz.dimensions[0:sz.N_dimensions-1]
+                    fvalues[col] = vc + $
+		      '(' + strjoin(strtrim(dim,2),',') + ')'
+		    
+                endelse
+            endfor
+        endif
+
+        tabx = mrd_struct(fnames, fvalues, nrec, structyp=structyp, silent=silent )
+
+; First copy the unscaled columns indexed by ww.     This is actually more 
+; efficient than using STRUCT_ASSIGN since the tag names are all identical,
+; so STRUCT_ASSIGN would copy everything (scaled and unscaled).
+ 	    
+       for i=0, Nww - 1 do tabx.(ww[i]) = table.(ww[i])
+       
+; Now copy the scaled items indexed by w after applying the scaling.        
+       
+        for i=0, Nw - 1 do begin	    
+ 		
+		dtype = size(tabx.(w[i]),/type)
+		if dtype eq 10 then $
+		    mrd_ptrscale, table.(w[i]), scales[w[i]], offsets[w[i]]
+		
+                tabx.(w[i]) = table.(w[i])*scales[w[i]] + offsets[w[i]]
+		
+            istr = strtrim(w[i]+1,2)
+            fxaddpar, header, 'TSCAL'+istr, 1.0, ' Set by MRD_SCALE'
+            fxaddpar, header, 'TZERO'+istr, 0.0, ' Set by MRD_SCALE'
+	    
+        endfor
+
+        table = temporary(tabx)   ;Remove original structure from memory
+    endif else begin
+    ; Now process images and random groups.
+
+        sz = size(table[0])
+        if sz[sz[0]+1] ne 8 then begin
+            ; Not a structure so we just have an array of data.
+            if keyword_set(dscale) then begin
+                table = temporary(table)*scales[0]+offsets[0]
+            endif else begin
+                table = temporary(table)*float(scales[0]) + float(offsets[0])
+            endelse
+            fxaddpar, header, 'BSCALE', 1.0, 'Set by MRD_SCALE'
+            fxaddpar, header, 'BZERO', 0.0, 'Set by MRD_SCALE'
+
+        endif else begin
+            ; Random groups.  Get the number of parameters by looking
+            ; at the first element in the table.
+            nparam = n_elements(table[0].(0))
+            if keyword_set(dscale) then typ = 'dbl' else typ='flt'
+            s1 = typ+'arr('+string(nparam)+')'
+            ngr = n_elements(table)
+            sz = size(table[0].(1))
+            if sz[0] eq 0 then dims = [1] else dims=sz[1:sz[0]]
+            s2 = typ + 'arr('
+            for i=0, n_elements(dims)-1 do begin 
+                if i ne 0 then s2 = s2+ ','
+                s2 = s2+string(dims[i])
+            endfor
+            s2 = s2+')'
+            tabx = mrd_struct(['params', 'array'],[s1,s2],ngr, silent=silent)
+
+            for i=0, nparam-1 do begin
+                istr = strcompress(string(i+1),/remo)
+                fxaddpar, header, 'PSCAL'+istr, 1.0, 'Added by MRD_SCALE'
+                fxaddpar, header, 'PZERO'+istr, 0.0, 'Added by MRD_SCALE'
+                tabx.(0)[i] = table.(0)[i]*scales[i]+offsets[i]
+            endfor
+	    
+            tabx.(1) = table.(1)*scales[nparam] + offsets[nparam]
+            fxaddpar, header, 'BSCALE', 1.0, 'Added by MRD_SCALE'
+            fxaddpar, header, 'BZERO', 0.0, 'Added by MRD_SCALE'
+            table = temporary(tabx)
+        endelse
+    endelse
+
+end
+
+; Read a variable length column into a pointer array.
+pro mrd_varcolumn, vtype, array, heap, off, siz
+compile_opt idl2, hidden
+
+    ; Guaranteed to have at least one non-zero length column
+    w   = where(siz gt 0)
+    nw  = n_elements(w)
+    
+    if vtype eq 'X' then siz = 1 + (siz-1)/8
+    
+    siz = siz[w]
+    off = off[w]
+
+    unsigned = 0
+    if vtype eq '1' then begin
+	unsigned = 12
+    endif else if vtype eq '2' then begin
+	unsigned = 13
+    endif else if vtype eq '3' then begin
+	unsigned = 15;
+    endif
+    unsigned = mrd_unsigned_offset(unsigned)
+    
+
+    for j=0, nw-1 do begin
+
+        case vtype of
+
+            'L': array[w[j]] = ptr_new(  byte(heap,off[j],siz[j]) )
+            'X': array[w[j]] = ptr_new(  byte(heap,off[j],siz[j]) )
+            'B': array[w[j]] = ptr_new(  byte(heap,off[j],siz[j]) )
+	
+            'I': array[w[j]] = ptr_new(  fix(heap, off[j], siz[j]) )
+            'J': array[w[j]] = ptr_new(  long(heap, off[j], siz[j]) )
+            'K': array[w[j]] = ptr_new(  long64(heap, off[j], siz[j]) )
+			  
+            'E': array[w[j]] = ptr_new(  float(heap, off[j], siz[j]) )
+            'D': array[w[j]] = ptr_new(  double(heap, off[j], siz[j]) )
+			  
+            'C': array[w[j]] = ptr_new(  complex(heap, off[j], siz[j]) )
+            'M': array[w[j]] = ptr_new(  dcomplex(heap, off[j], siz[j]) )
+			   
+            '1': array[w[j]] = ptr_new(  uint(heap, off[j], siz[j]) )
+            '2': array[w[j]] = ptr_new(  ulong(heap, off[j], siz[j]) )
+            '3': array[w[j]] = ptr_new(  ulong64(heap, off[j], siz[j]) )
+      
+        endcase
+
+	; Fix endianness.
+        if (vtype ne 'B') && (vtype ne 'X') && (vtype ne 'L') then begin
+	    swap_endian_inplace, *array[w[j]],/swap_if_little
+        endif
+
+	; Scale unsigneds.
+	if unsigned gt 0 then *array[w[j]] = *array[w[j]] - unsigned
+	
+    endfor
+end
+
+; Read a variable length column into a fixed length array.
+pro mrd_fixcolumn, vtype, array, heap, off, siz
+compile_opt idl2, hidden
+
+    w   = where(siz gt 0, nw)
+    if nw EQ 0 then return
+    
+    if vtype eq 'X' then siz = 1 + (siz-1)/8
+    
+    siz = siz[w]
+    off = off[w]
+
+    for j=0, nw-1 do begin
+        case vtype of
+            'L': array[0:siz[j]-1,w[j]] = byte(heap,off[j],siz[j])  
+            'X': array[0:siz[j]-1,w[j]] = byte(heap,off[j],siz[j])
+            'B': array[0:siz[j]-1,w[j]] = byte(heap,off[j],siz[j]) 
+	
+            'I': array[0:siz[j]-1,w[j]] = fix(heap, off[j], siz[j]) 
+            'J': array[0:siz[j]-1,w[j]] = long(heap, off[j], siz[j]) 
+            'K': array[0:siz[j]-1,w[j]] = long64(heap, off[j], siz[j]) 
+			  
+            'E': begin                  ;Delay conversion until after byteswapping to avoid possible math overflow   Feb 2005
+	         temp = heap[off[j]: off[j] + 4*siz[j]-1 ]
+		 byteorder, temp, /LSWAP, /SWAP_IF_LITTLE	 
+	         array[0:siz[j]-1,w[j]] = float(temp,0,siz[j]) 
+                 end			  
+           'D': begin 
+	         temp = heap[off[j]: off[j] + 8*siz[j]-1 ]
+		 byteorder, temp, /L64SWAP, /SWAP_IF_LITTLE		 
+	         array[0:siz[j]-1,w[j]] = double(temp,0,siz[j]) 
+                 end			  
+            'C': array[0:siz[j]-1,w[j]] = complex(heap, off[j], siz[j]) 
+            'M': array[0:siz[j]-1,w[j]] = dcomplex(heap, off[j], siz[j]) 
+			   
+            'A': array[w[j]] = string(byte(heap,off[j],siz[j])) 
+
+            '1': array[0:siz[j]-1,w[j]] = uint(heap, off[j], siz[j]) 
+            '2': array[0:siz[j]-1,w[j]] = ulong(heap, off[j], siz[j])
+            '3': array[0:siz[j]-1,w[j]] = ulong64(heap, off[j], siz[j])
+      
+        endcase
+
+    endfor
+
+    ; Fix endianness for datatypes with more than 1 byte
+    if  ~stregex(vtype,'[^ABXLDE]') then $ 
+	swap_endian_inplace, array, /swap_if_little
+ 
+    ; Scale unsigned data
+    case vtype of
+    '1': unsigned = 12
+    '2': unsigned = 13
+    '3': unsigned = 15
+    else: unsigned = 0
+    endcase
+   
+    if unsigned gt 0 then $
+        unsigned = mrd_unsigned_offset(unsigned)
+   
+    if unsigned gt 0 then begin
+        for j=0, nw-1 do begin
+            array[0:siz[j]-1,w[j]] = array[0:siz[j]-1,w[j]] - unsigned
+	endfor
+    endif
+
+
+end
+		
+; Read the heap area to get the actual values of variable 
+; length arrays. 
+pro mrd_read_heap, unit, header, range, fnames, fvalues, vcls, vtpes, table, $ 
+   structyp, scaling, scales, offsets, status, silent=silent,                $
+   columns=columns, rows = rows, pointer_var=pointer_var, fixed_var=fixed_var
+compile_opt idl2, hidden
+    ; 
+    ; Unit:         FITS unit number. 
+    ; header:       FITS header. 
+    ; fnames:       Column names. 
+    ; fvalues:      Column values. 
+    ; vcols:        Column numbers of variable length columns. 
+    ; vtypes:       Actual types of variable length columns 
+    ; table:        Table of data from standard data area, on output 
+    ;               contains the variable length data. 
+    ; structyp:     Structure name. 
+    ; scaling:      Is there going to be scaling of the data?
+    ; status:       Set to -1 if an error occurs.
+    ;
+    typstr = 'LXBIJKAEDCM123' 
+    prefix = ['bytarr(', 'bytarr(', 'bytarr(', 'intarr(',     $ 
+              'lonarr(', 'lon64arr(', 'string(bytarr(', 'fltarr(',         $ 
+              'dblarr(', 'complexarr(', 'dcomplexarr(',            $
+	      'uintarr(', 'ulonarr(', 'ulon64arr(']
+    
+    status = 0 
+
+    ; Convert from a list of indicators of whether a column is variable
+    ; length to pointers to only the variable columns.
+
+    vcols = where(vcls eq 1)
+    vtypes = vtpes[vcols]
+
+    nv = n_elements(vcols) 
+ 
+    ; Find the beginning of the heap area. 
+ 
+    heapoff = long64(fxpar(header, 'THEAP')) 
+    sz = fxpar(header, 'NAXIS1')*fxpar(header, 'NAXIS2')
+    
+    if (heapoff ne 0) && (heapoff lt sz) then begin 
+        print, 'MRDFITS: ERROR Heap begins within data area' 
+        status = -1 
+        return 
+    endif
+
+    ; Skip to beginning.
+    if (heapoff > sz) then begin
+        mrd_skip, unit, heapoff-sz
+    endif
+ 
+    ; Get the size of the heap. 
+    pc = long64(fxpar(header, 'PCOUNT')) 
+    if heapoff eq 0 then heapoff = sz 
+    hpsiz = pc - (heapoff-sz) 
+ 
+    if (hpsiz gt 0) then heap = bytarr(hpsiz) 
+ 
+ 
+    ; Read in the heap 
+    readu, unit, heap
+
+    ; Skip to the end of the data area.
+    skipB = 2880 - (sz+pc) mod 2880
+    if skipB ne 2880 then begin
+        mrd_skip, unit, skipB
+    endif
+ 
+    ; Find the maximum dimensions of the arrays. 
+    ; 
+    ; Note that the variable length column currently has fields which 
+    ; are I*4 2-element arrays where the first element is the 
+    ; length of the field on the current row and the second is the 
+    ; offset into the heap. 
+
+    vdims = lonarr(nv)
+    for i=0, nv-1 do begin 
+        col = vcols[i]
+        curr_col = table.(col)
+        vdims[i] = max(curr_col[0,*])
+        w = where(curr_col[0,*] ne vdims[i])
+        if w[0] ne -1 then begin
+            if n_elements(lencols) eq 0 then begin
+                lencols = [col]
+            endif else begin
+                lencols=[lencols,col]
+            endelse
+        endif
+
+        if vtypes[i] eq 'X' then vdims[i]=(vdims[i]+7)/8 
+        ind = strpos(typstr, vtypes[i])
+    
+        ; Note in the following that we ensure that the array is
+        ; at least one element long.
+
+        fvalues[col] = prefix[ind] + string((vdims[i] > 1)) + ')'
+        if vtypes[i] eq 'A' then fvalues[col] = fvalues[col] + ')' 
+    
+    endfor 
+ 
+    nfld = n_elements(fnames) 
+
+    ; Get rid of columns which have no actual data. 
+    w= intarr(nfld) 
+    w[*] = 1
+    corres = indgen(nfld)
+
+    
+    ; Should we get rid of empty columns?
+    delete = 1
+    if keyword_set(pointer_var) then delete = pointer_var eq 1
+
+    if delete then begin
+	
+        ww = where(vdims eq 0, N_ww) 
+        if N_ww GT 0 then  begin
+            w[vcols[ww]] = 0
+            if ~keyword_set(silent) then $
+                print, 'MRDFITS: ', strcompress(string(n_elements(ww))),  $
+                  ' unused variable length columns deleted'
+        endif
+
+        ; Check if all columns have been deleted...
+        wx = where(w gt 0, N_wx)
+        if N_wx EQ 0 then begin
+            if ~keyword_set(silent) then $
+                print, 'MRDFITS: All columns have been deleted'
+ 	    table = 0
+	    return
+        endif
+    
+
+        ; Get rid of unused columns.
+        corres = corres[wx]
+        fnames = fnames[wx] 
+        fvalues = fvalues[wx]
+        scales = scales[wx]
+        offsets = offsets[wx]
+
+        wx = where(vdims gt 0)
+    
+        if (wx[0] eq -1) then begin
+            vcols=[-9999]
+	    x=temporary(vtypes)
+	    x=temporary(vdims)
+        endif else begin 
+            vcols = vcols[wx]
+            vtypes = vtypes[wx]
+            vdims = vdims[wx]
+        endelse
+    endif
+
+    if ~keyword_set(pointer_var) then begin
+        ; Now add columns for lengths of truly variable length records.
+        if n_elements(lencols) gt 0 then begin
+            if ~keyword_set(silent) then $
+                print, 'MRDFITS: ', strcompress(string(n_elements(lencols))), $
+                  ' length column[s] added'
+         
+
+            for i=0, n_elements(lencols)-1 do begin
+                col = lencols[i]
+                w = where(col eq corres)
+                ww = where(col eq vcols)
+                w = w[0]
+                ww = ww[0]
+                fvstr = '0L' ; <-- Originally, '0l'; breaks under the virtual machine!
+                fnstr = 'L'+strcompress(string(col),/remo)+'_'+fnames[w]
+                nf = n_elements(fnames)
+                
+                ; Note that lencols and col refer to the index of the
+                ; column before we started adding in the length
+                ; columns.
+                
+                if w eq nf-1 then begin
+                    ; Subtract -1 for the length columns so 0 -> -1 and
+                    ; we can distinguish this column.
+
+                    corres = [corres, -col-1 ]
+                    fnames = [fnames, fnstr ]
+                    fvalues = [fvalues, fvstr ]
+                    scales = [scales, 1.0d0 ]
+                    offsets = [offsets, 0.0d0 ]
+		    
+                endif else begin
+		    
+                    corres = [corres[0:w],-col-1,corres[w+1:nf-1] ]
+                    fnames = [fnames[0:w],fnstr,fnames[w+1:nf-1] ]
+                    fvalues = [fvalues[0:w],fvstr,fvalues[w+1:nf-1] ]
+                    scales = [scales[0:w], 1.0d0, scales[w+1:nf-1] ]
+                    offsets = [offsets[0:w],0.0d0, offsets[w+1:nf-1] ]
+                endelse
+            endfor
+        endif
+	
+    endif else begin
+	
+        ; We'll just read data into pointer arrays.
+	for i=0,n_elements(lencols)-1 do begin
+	    col = lencols[i]
+	    if vtpes[col] eq 'A' then begin
+	        fvalues[col] = '" "'
+	    endif else begin
+	        fvalues[col] = 'ptr_new()'
+	    endelse
+	endfor
+	
+    endelse
+	
+
+
+    ; Generate a new table with the appropriate structure definitions 
+    if ~scaling && ~keyword_set(columns) then begin
+        tablex = mrd_struct(fnames, fvalues, n_elements(table), structyp=structyp, $
+                            silent=silent)
+    endif else begin
+        tablex = mrd_struct(fnames, fvalues, n_elements(table), silent=silent)
+    endelse
+
+
+    if N_elements(rows) EQ 0 then nrow = range[1]-range[0]+1 $
+                             else nrow = N_elements(rows)
+    
+    ; I loops over the new table columns, col loops over the old table.
+    ; When col is negative, it is a length column.
+    for i=0, n_elements(fnames)-1 do begin
+        
+        col = corres[i]
+                
+        if col ge 0 then begin
+	    
+            w = where(vcols eq col)
+                
+            ; First handle the case of a column that is not
+            ; variable length -- just copy the column.
+                
+            if w[0] eq -1 then begin
+		    
+                     tablex.(i) = table.(col)
+ 		    
+            endif else begin
+		
+                vc = w[0]
+                ; Now handle the variable length columns
+                        
+                ; If only one row in table, then
+                ; IDL will return curr_col as one-dimensional.
+                ; Since this is a variable length pointer column we
+                ; know that the dimension of the column is 2.
+                    curr_col = table.(col)
+		
+                if (nrow eq 1) then curr_col = reform(curr_col,2,1)
+                siz = curr_col[0,*] 
+                off = curr_col[1,*] 
+                    
+                ; Now process each type.
+                    curr_colx = tablex.(i)
+                    sz = size(curr_colx)
+                    if (sz[0] lt 2) then begin
+                        curr_colx = reform(curr_colx, 1, n_elements(curr_colx), /overwrite)
+                    endif
+                           
+                    
+                ; As above we have to worry about IDL truncating
+                ; dimensions.  This can happen if either
+                ; nrow=1 or the max dimension of the column is 1.
+                    
+
+                    sz = size(tablex.(i))
+ 
+                nel = sz[sz[0]+2]
+                if (nrow eq 1) && (nel eq 1) then begin
+                    curr_colx = make_array(1,1,value=curr_colx)
+                endif else if nrow eq 1 then begin
+                    curr_colx = reform(curr_colx,[nel, 1], /overwrite)
+                endif else if nel eq 1 then begin
+                    curr_colx = reform(curr_colx,[1, nrow], /overwrite)
+                endif
+
+		vtype = vtypes[vc]
+		varying = 0
+		if n_elements(lencols) gt 0 then begin
+		    varying = where(lencols eq col)
+		    if varying[0] eq -1 then varying=0 else varying=1
+		endif
+		
+		if varying && keyword_set(pointer_var) && (vtype ne 'A') then begin
+		    mrd_varcolumn, vtype, curr_colx, heap, off, siz
+		endif else begin
+		    mrd_fixcolumn, vtype, curr_colx, heap, off, siz
+		endelse
+
+
+                
+                if nel eq 1 and nrow eq 1 then begin
+                    curr_colx = curr_colx[0]
+                endif else if nrow eq 1 then begin
+                    curr_colx = reform(curr_colx, nel, /overwrite)
+                endif else if nel eq 1 then begin
+                    curr_colx = reform(curr_colx, nrow, /overwrite)
+                endif
+
+                    sz = size(curr_colx)
+                    if sz[1] eq 1 then begin
+                         sz_tablex = size(tablex.(i))
+                         sdimen = sz_tablex[1:sz_tablex[0]]
+                         tablex.(i) = reform(curr_colx,sdimen)
+                    endif else begin
+                        tablex.(i) = curr_colx
+                    endelse
+                 
+            endelse
+                
+        endif else begin
+            ; Now handle the added columns which hold the lengths
+            ; of the variable length columns.
+                
+            ncol = -col - 1 ; Remember we subtracted an extra one.
+                xx = table.(ncol)
+                tablex.(i) = reform(xx[0,*])
+       endelse
+    endfor 
+ 
+    ; Finally get rid of the initial table and return the table with the 
+    ; variable arrays read in. 
+    ; 
+    table = temporary(tablex) 
+    return 
+end 
+
+; Read in the binary table information. 
+pro mrd_read_table, unit, range, rsize, structyp, nrows, nfld, typarr, table, rows = rows, $
+     unixpipe = unixpipe
+compile_opt idl2, hidden 
+    ; 
+    ; 
+    ; Unit          Unit to read data from. 
+    ; Range         Desired range 
+    ; Rsize         Size of row. 
+    ; structyp      Structure type. 
+    ; Nfld          Number of fields in structure. 
+    ; Typarr        Field types 
+    ; Table         Table to read information into.
+    ; 
+
+    if range[0] gt 0 then mrd_skip, unit, rsize*range[0]
+    readu,unit, table
+    if N_elements(rows) GT 0 then table = table[rows- range[0]]
+
+    ; Move to the beginning of the heap -- we may have only read some rows of
+    ; the data.
+    if range[1] lt nrows-1 then begin
+        skip_dist = (nrows-range[1]-1)*rsize
+        mrd_skip, unit, skip_dist
+    endif
+
+    
+
+    ; If necessary then convert to native format.
+    if unixpipe then swap_endian_inplace,table,/swap_if_little
+	
+
+    ; Handle unsigned fields.
+    for i=0, nfld-1 do begin
+
+	    type = mrd_unsignedtype(table.(i))
+
+	    if type gt 0 then begin	    
+	        table.(i) = table.(i) - mrd_unsigned_offset(type)
+	    endif
+	    
+	
+    endfor
+ end
+
+
+; Check the values of TDIM keywords to see that they have valid
+; dimensionalities.  If the TDIM keyword is not present or valid
+; then the a one-dimensional array with a size given in the TFORM
+; keyword is used.
+
+pro mrd_tdim, header, index, flen, arrstr, no_tdim=no_tdim
+compile_opt idl2, hidden
+    ; HEADER        Current header array.
+    ; Index         Index of current parameter
+    ; flen          Len given in TFORM keyword
+    ; arrstr        String returned to be included within paren's in definition.
+    ; no_tdim       Disable TDIM processing
+
+    arrstr = strcompress(string(flen),/remo)
+
+    if keyword_set(no_tdim) then return
+
+    tdstr = fxpar(header, 'TDIM'+strcompress(string(index),/remo))
+    if tdstr eq '' then return
+
+    ;
+    ; Parse the string.  It should be of the form '(n1,n2,...nx)' where
+    ; all of the n's are positive integers and the product equals flen.
+    ;
+    tdstr = strcompress(tdstr,/remo)
+    len = strlen(tdstr)
+    if strmid(tdstr,0,1) ne '(' && strmid(tdstr,len-1,1) ne ')' || len lt 3 then begin
+        print, 'MRDFITS: Error: invalid TDIM for column', index
+        return
+    endif
+
+    ; Get rid of parens.
+    tdstr = strmid(tdstr,1,len-2)
+    len = len-2
+
+    nind = 0
+    cnum = 0
+
+    for nchr=0, len-1 do begin
+        c = strmid(tdstr,nchr, 1)
+        
+        if c ge '0' &&  c le '9' then begin
+            cnum = 10*cnum + long(c)
+                
+        endif else if c eq ',' then begin
+        
+            if cnum le 0 then begin
+                print,'MRDFITS: Error: invalid TDIM for column', index
+                return
+            endif
+                
+            if n_elements(numbs) eq 0 then  $
+                 numbs = cnum $
+            else    numbs = [numbs,cnum]
+                
+            cnum = 0
+                
+       endif else begin
+       
+            print,'MRDFITS: Error: invalid TDIM for column', index
+            return
+                
+       endelse
+
+    endfor
+
+    ; Handle the last number.
+    if cnum le 0 then begin
+        print,'MRDFITS: Error: invalid TDIM for column', index
+        return
+    endif
+
+    if n_elements(numbs) eq 0 then numbs = cnum else numbs = [numbs,cnum]
+
+    prod = 1
+
+    for i=0, n_elements(numbs)-1 do prod = prod*numbs[i]
+ 
+    if prod ne flen then begin
+        print,'MRDFITS: Error: TDIM/TFORM dimension mismatch'
+        return
+    endif
+
+    arrstr = tdstr
+end
+ 
+; Define a structure to hold a FITS binary table. 
+pro mrd_table, header, structyp, use_colnum,           $ 
+    range, rsize, table, nrows, nfld, typarr, fnames, fvalues,   $ 
+    vcls, vtpes, scales, offsets, scaling, status, rows = rows, $
+    silent=silent, columns=columns, no_tdim=no_tdim, $
+    alias=alias, unsigned=unsigned, outalias=outalias,emptystring=emptystring
+ compile_opt idl2, hidden
+    ; 
+    ; Header                FITS header for table. 
+    ; Structyp              IDL structure type to be used for 
+    ;                       structure. 
+    ; N_call                Number of times this routine has been called. 
+    ; Table                 Structure to be defined. 
+    ; Status                Return status.
+    ; No_tdim               Disable TDIM processing.
+
+    table = 0
+
+    types =  ['L', 'X', 'B', 'I', 'J', 'K', 'A', 'E', 'D', 'C', 'M', 'P','Q']
+    arrstr = ['bytarr(', 'bytarr(', 'bytarr(', 'intarr(', 'lonarr(', 'lon64arr(',      $ 
+              'string(replicate(32b,', 'fltarr(', 'dblarr(', 'complexarr(',            $ 
+              'dcomplexarr(', 'lonarr(2*','lon64arr(2*']
+    bitpix = [  0,   0,   0,  16,  32,  64,   0,  0,   0,   0,   0,   0, 0]
+
+    sclstr = ["'T'", '0B', '0B', '0', '0L', '0LL', '" "', '0.', '0.d0', 'complex(0.,0.)', $ 
+              'dcomplex(0.d0,0.d0)', 'lonarr(2)','lon64arr(2)']
+    if keyword_set(emptystring) then begin 
+        sclstr[6] = '0B'
+        arrstr[6] = 'bytarr(' 
+    endif 	
+    unsarr = ['', '', '', 'uintarr(', 'ulonarr(', 'ulon64arr('];
+    unsscl = ['', '', '', '0US',        '0UL',      '0ULL']
+ 
+
+    status = 0 
+
+; NEW WAY: E.S.S.
+
+    ;; get info from header. Using vectors is much faster
+    ;; when there are many columns
+
+    mrd_fxpar, header, xten, nfld, nrow, rsize, fnames, fforms, scales, offsets
+    nnames = n_elements(fnames)
+
+    tname = fnames
+    ;; nrow will change later
+    nrows = nrow
+
+    ;; Use scale=1 if not found
+    if nnames GT 0 then begin
+      wsc=where(scales EQ 0.0d,nwsc)
+      IF nwsc NE 0 THEN scales[wsc] = 1.0d
+    endif
+
+    xten = strtrim(xten,2)
+    if xten ne 'BINTABLE' and xten ne 'A3DTABLE' then begin 
+        print, 'MRDFITS: ERROR - Header is not from binary table.' 
+        nfld = 0 & status = -1 
+        return 
+    endif 
+ 
+    if range[0] ge 0 then begin 
+        range[0] = range[0] < (nrow-1) 
+        range[1] = range[1] < (nrow-1) 
+    endif else begin 
+        range[0] = 0 
+        range[1] = nrow - 1 
+    endelse
+    
+    nrow = range[1] - range[0] + 1 
+    if nrow le 0 then begin
+        if ~keyword_set(silent) then $
+            print, 'MRDFITS: Binary table. ', $
+             strcompress(string(nfld)), ' columns, no rows.'
+        return
+    endif
+
+    if N_elements(rows) EQ 0 then nrowp  = nrow else begin 
+          bad = where((rows LT range[0]) or (rows GT range[1]), Nbad)
+          if Nbad GT 0 then begin 
+             print,'MRDFITS: Row numbers must be between 0 and ' + $
+                    strtrim(nrow-1,2)      
+             status = -1
+             return
+           endif
+           nrowp = N_elements(rows)
+    endelse
+;    rsize = fxpar(header, 'NAXIS1') 
+ 
+    ; 
+    ;  Loop over the columns           
+ 
+    typarr   = strarr(nfld) 
+    
+    fvalues  = strarr(nfld) 
+    dimfld   = strarr(nfld)
+    
+    vcls     = intarr(nfld)
+    vtpes    = strarr(nfld)
+ 
+    fnames2 = strarr(nfld)
+
+    for i=0, nfld-1 do begin
+	
+        istr = strcompress(string(i+1), /remo)
+
+        fname = fnames[i]
+
+        ;; check for a name conflict
+        fname = mrd_dofn(fname, i+1, use_colnum, alias=alias)
+	
+        ;; check for a name conflict
+        fname = mrd_chkfn(fname, fnames2, i, SILENT=silent)
+
+        ;; copy in the valid name
+        fnames[i] = fname
+        ;; for checking conflicts
+        fnames2[i] = fname
+	
+        fform = fforms[i]
+
+        mrd_doff, fform, dim, ftype
+        
+        ; Treat arrays of length 1 as scalars.
+        if dim eq 1 then begin
+            dim = 0
+        endif else if dim EQ -1 then begin 
+            dimfld[i] = -1
+        endif else begin
+            mrd_tdim, header, i+1, dim, str, no_tdim=no_tdim
+            dimfld[i] = str
+        endelse
+                
+        typarr[i] = ftype 
+        
+        
+        ; Find the number of bytes in a bit array. 
+ 
+        if ftype eq 'X' && (dim gt 0) then begin
+            dim = (dim+7)/8 
+            dimfld[i] = strtrim(string(dim),2)
+        endif
+         
+        ; Add in the structure label. 
+        ; 
+         
+        ; Handle variable length columns. 
+        
+        if (ftype eq 'P') || (ftype eq 'Q') then begin 
+ 
+            if (dim ne 0)  && (dim ne 1) then begin 
+                print, 'MRDFITS: Invalid dimension for variable array column '+string(i+1) 
+                status = -1 
+                return 
+            endif
+	    
+            ppos = ftype eq 'P' ? strpos(fform, 'P') : strpos(fform, 'Q')
+            vf = strmid(fform, ppos+1, 1); 
+            if strpos('LXBIJKAEDCM', vf) eq -1 then begin 
+                print, 'MRDFITS: Invalid type for variable array column '+string(i+1) 
+                status = -1 
+                return 
+            endif 
+
+            vcls[i] = 1
+	    
+	    
+	    xunsigned = mrd_chkunsigned(bitpix[ppos], scales[i],       $
+				       offsets[i], $
+				       unsigned=unsigned)
+
+	    if (xunsigned) then begin
+		
+		if      vf eq 'I' then vf = '1' $
+		else if vf eq 'J' then vf = '2' $
+		else if vf eq 'K' then vf = '3'
+		
+	    endif
+							   
+            vtpes[i] = vf
+            dim = 0
+                         
+        endif 
+         
+
+        for j=0, n_elements(types) - 1 do begin
+	    
+            if ftype eq types[j] then begin
+
+                xunsigned = mrd_chkunsigned(bitpix[j], scales[i], $
+                                            offsets[i], $
+                                            unsigned=unsigned)
+
+		if xunsigned then begin		     
+		    fxaddpar, header, 'TZERO'+istr, 0, 'Modified by MRDFITS V'+mrd_version()
+                    offsets[i] = 0 ;; C. Markwardt Aug 2007 - reset to zero so offset is not applied twice'
+	        endif
+                if dim eq 0 then begin
+
+                   fvalues[i] = xunsigned ? unsscl[j] : sclstr[j]
+		    
+                endif else begin
+
+		    line = xunsigned ?  unsarr[j] : arrstr[j]
+		    
+                    line += dimfld[i] + ')'
+                    if ~keyword_set(emptystring) then $
+		         if ftype eq 'A' then line += ')' 
+                    fvalues[i] = line
+		    
+                endelse
+		
+                goto, next_col
+		
+            endif
+	    
+        endfor 
+         
+        print, 'MRDFITS: Invalid format code:',ftype, ' for column ', i+1 
+        status = -1 
+        return 
+  next_col: 
+    endfor 
+
+    ; Check if there are any variable length columns.  If not then
+    ; undefine vcls and vtpes
+    w = where(vcls eq 1, N_w)
+    if N_w eq 0 then begin
+        dummy = temporary(vcls)
+        dummy = temporary(vtpes)
+        dummy = 0
+    endif
+
+    if scaling then begin 
+        w = where( (scales ne 1.0d0) or (offsets ne 0.0d0), Nw)
+        scaling = Nw GT 0
+    endif
+
+    zero = where(long(dimfld) LT 0L, N_zero)
+    if N_zero GT 0 then begin
+	
+        if N_zero Eq nfld then begin
+            print,'MRDFITS: Error - All fields have zero length'
+            return
+        endif
+	
+        for i=0, N_zero-1 do begin
+	    print,'MRDFITS: Table column ' + fnames[zero[i]] + ' has zero length'
+	endfor
+	
+        nfld    = nfld - N_zero
+        good    = where(dimfld GE 0)
+        fnames  = fnames[good]
+        fvalues = fvalues[good]
+        typarr = typarr[good]      ;Added 2005-1-6   (A.Csillaghy)
+        tname = tname[good]        
+	
+    endif
+
+    if n_elements(vcls) eq 0  &&  (~scaling) && ~keyword_set(columns) then begin
+	
+        table = mrd_struct(fnames, fvalues, nrow, structyp=structyp,  silent=silent )
+	
+    endif else begin
+	
+        table = mrd_struct(fnames, fvalues, nrow, silent=silent )
+	
+    endelse
+
+    if ~keyword_set(silent) then begin
+        print, 'MRDFITS: Binary table. ',strcompress(string(nfld)), ' columns by ',  $
+          strcompress(string(nrowp)), ' rows.'
+        if n_elements(vcls) gt 0 then begin
+                print, 'MRDFITS: Uses variable length arrays'
+        endif
+    endif
+
+    outalias = transpose([[tag_names(table)],[tname] ])
+    status = 0 
+    return 
+ 
+end 
+
+function mrdfits, file, extension, header,      $
+        structyp = structyp,                    $
+        use_colnum = use_colnum,                $
+        range = range,                          $
+        dscale = dscale, fscale=fscale,         $
+        fpack = fpack, no_fpack = no_fpack,     $
+        silent = silent,                        $
+        columns = columns,                      $
+        no_tdim = no_tdim,                      $
+        error_action = error_action,            $
+ 	compress=compress,                      $
+	alias=alias,                            $
+        rows = rows,                        $
+	unsigned=unsigned,                      $
+	version=version,                        $
+	pointer_var=pointer_var,                $
+	fixed_var=fixed_var,                    $
+	outalias = outalias,                     $
+	emptystring = emptystring,               $
+        status=status, extnum = extnum
+
+    compile_opt idl2    
+    ;   Let user know version if MRDFITS being used.
+    if keyword_set(version) then $
+        print,'MRDFITS: Version '+mrd_version() + 'April 24, 2014'
+        
+      
+    if N_elements(error_action) EQ 0 then error_action = 2
+    On_error, error_action
+   
+    ; Check positional arguments.
+
+    if n_params() le 0  || n_params() gt 3 then begin
+	if keyword_set(version) then return, 0
+        print, 'MRDFITS: Usage'
+        print, '   a=mrdfits(file/unit, [exten_no/exten_name, header], /version $'
+        print, '       /fscale, /dscale, /unsigned, /use_colnum, /silent    $'
+        print, '       range=, rows= , structyp=, columns=, $'
+	print, '       /pointer_var, /fixed_var, error_action=, status= )'
+        return, 0
+    endif
+   
+    if n_params() eq 1 then extension = 0
+   
+    ; Check optional arguments.
+    ;
+    ;  *** Structure name ***
+
+    if keyword_set(structyp) then begin
+        sz = size(structyp)
+        if sz[0] ne 0 then begin
+            ; Use first element of array
+            structyp = structyp[0]
+            sz = size(structyp[0])
+        endif
+	
+        if sz[1] ne 7 then begin
+            print, 'MRDFITS: stucture type must be a string'
+            return, 0
+        endif
+    endif
+
+    ;  *** Use column numbers not names?
+    use_colnum = keyword_set(use_colnum)
+
+    ;  *** Get only a part of the FITS file.
+    if N_elements(rows) GT 0 then begin
+        range1 = min(rows,max=range2)
+        range = [range1,range2]
+    endif
+    if keyword_set(range) then begin
+        if n_elements(range) eq 2 then arange = range $
+        else if n_elements(range) eq 1 then arange = [0,range[0]-1] $
+        else if n_elements(range) gt 2 then arange = range[0:1] $
+        else if n_elements(range) eq 0 then arange = [-1,-1]
+	
+    endif else begin
+	arange = [-1,-1]
+    endelse
+
+    arange = long64(arange)
+
+    ; Open the file and position to the appropriate extension then read
+    ; the header.
+
+    if (N_elements(file) GT 1 ) then begin
+        print, 'MRDFITS: Vector input not supported'
+        return, 0
+    endif
+
+    inputUnit = 0
+   
+    dtype = size(file,/type)
+    if (dtype gt 0) && (dtype lt 4) then begin    ;File unit number specified
+	
+        inputUnit = 1
+        unit = file
+        unixpipe =  (fstat(unit)).size EQ 0     ;Unix pipes have no files size    
+        if fxmove(unit,extension) lt 0 then return, -1
+    
+    endif else begin                         ;File name specified
+
+        unit = fxposit(file, extension, compress=compress, unixpipe=unixpipe, $
+	               /readonly,extnum=extnum, errmsg= errmsg, fpack=fpack)
+
+        if unit lt 0 then begin
+            message, 'File access error',/CON
+	    if errmsg NE '' then message,errmsg,/CON
+	    if scope_level() GT 2 then help,/trace
+            status = -1
+            return, 0
+        endif
+    endelse
+
+    if eof(unit) then begin
+        message,'ERROR - Extension past EOF',/CON
+	if inputUnit eq 0 then free_lun,unit 
+	status = -2
+	return, 0
+    endif
+
+    mrd_hread, unit, header, status, SILENT = silent, ERRMSG = errmsg
+    
+    if status lt 0 then begin
+	message,'ERROR - ' +errmsg,/CON
+        message, 'ERROR - FITS file may be invalid or corrupted',/CON
+ 	if inputUnit eq 0 then free_lun,unit
+        return, 0
+    endif
+
+;	     
+    ; If this is primary array then XTENSION will have value
+    ; 0 which will be converted by strtrim to '0'
+
+    xten = strtrim( fxpar(header,'XTENSION'), 2)
+    if xten eq '0' || xten eq 'IMAGE' then type = 0 $
+    else if xten eq 'TABLE' then type = 1 $
+    else if xten eq 'BINTABLE' || xten eq 'A3DTABLE' then type = 2 $
+    else begin 
+        message, 'Unable to process extension type:' + strtrim(xten,2),/CON
+	if inputUnit eq 0 then free_lun,unit
+	status = -1
+        return, 0
+    endelse
+
+    scaling = keyword_set(fscale) || keyword_set(dscale)
+
+    if type eq 0 then begin
+
+        ;*** Images/arrays
+        
+        mrd_image, header, arange, maxd, rsize, table, scales, offsets, $
+          scaling, status, silent=silent, unsigned=unsigned, $
+           rows= rows
+       if (status ge 0) && (rsize gt 0) then begin
+           mrd_read_image, unit, arange, maxd, rsize, table, rows = rows,$
+            status=status, unixpipe=unixpipe
+        endif
+       size = rsize
+    endif else if type eq 1 then begin
+
+        ;*** ASCII tables.
+        
+        mrd_ascii, header, structyp, use_colnum,                              $
+            arange, table, nbytes, nrows, nfld, rows=rows,                    $
+            typarr, posarr, lenarr, nullarr, fnames, fvalues,                 $
+            scales, offsets, scaling, status, silent=silent,                  $
+            columns=columns, alias=alias, outalias=outalias
+        size = nbytes*nrows
+        
+        if (status ge 0)   &&  (size gt 0)  then begin
+        
+            ;*** Read data.
+            mrd_read_ascii, unit,  arange, nbytes, nrows,   $
+              nfld, typarr, posarr, lenarr, nullarr, table,  rows= rows
+              
+            ;*** Extract desired columns.
+            if (status ge 0) && keyword_set(columns) then                  $
+                mrd_columns, table, columns, fnames, fvalues, vcls, vtps, $
+                  scales, offsets, scaling, structyp=structyp, silent=silent
+        endif
+        
+    endif else begin
+
+        ; *** Binary tables.
+
+        mrd_table, header, structyp, use_colnum,                            $
+          arange, rsize, table, nrows, nfld, typarr,                        $ 
+          fnames, fvalues, vcls, vtpes, scales, offsets, scaling, status,   $
+          silent=silent, columns=columns, no_tdim=no_tdim, $
+          alias=alias, unsigned=unsigned, rows = rows, outalias = outalias, $
+	  emptystring=emptystring
+
+        size = nfld*(arange[1] - arange[0] + 1)
+        if (status ge 0)  &&  (size gt 0)  then begin
+
+            ;*** Read data.
+            mrd_read_table, unit, arange, rsize,  rows = rows, $
+              structyp, nrows, nfld, typarr, table, unixpipe=unixpipe
+
+            if (status ge 0) && keyword_set(columns) then begin
+        
+                ;*** Extract desired columns.
+                mrd_columns, table, columns, fnames, fvalues,                  $
+                  vcls, vtpes, scales, offsets, scaling, structyp=structyp,    $
+                  silent=silent
+	    
+	    endif
+         
+             if keyword_set(emptystring) then $
+	      mrd_string, table, header, typarr, $
+               fnames, fvalues,  1+arange[1]-arange[0], structyp=structyp, silent=silent
+
+            if (status ge 0) && n_elements(vcls) gt 0 then begin 
+          
+                ;*** Get variable length columns
+                mrd_read_heap, unit, header, arange, fnames, fvalues,             $
+                  vcls, vtpes, table, structyp, scaling, scales, offsets, status, $
+                  silent=silent, pointer_var=pointer_var, fixed_var=fixed_var, rows= rows
+		
+	    endif else begin
+
+	        ; Skip remainder of last data block
+	        sz = long64(fxpar(header, 'NAXIS1'))* $
+                     long64(fxpar(header,'NAXIS2')) +  $
+		       long64(fxpar(header, 'PCOUNT'))
+	        skipB = 2880 - sz mod 2880
+	        if (skipB ne 2880) then mrd_skip, unit, skipB
+            endelse
+		     
+        endif
+
+    endelse
+
+
+    ; Don't tie up a unit number that we allocated in this routine.
+    if (unit gt 0) && (inputUnit eq 0) then free_lun, unit
+
+; If any of the scales are non-unity, or any of the offsets are nonzero then 
+; apply scalings.
+
+    if  (status ge 0)  &&  scaling  &&  (size gt 0)  then begin
+	noscale = array_equal(scales,1.d0) &&  array_equal(offsets,0.0) 
+        
+        if ~noscale then mrd_scale, type, scales, offsets, table, header,  $
+            fnames, fvalues, 1+arange[1]-arange[0], structyp=structyp,       $
+            dscale=dscale, silent=silent
+    endif
+
+    ; All done. Check the status to see if we ran into problems on the way.
+    
+    if status ge 0 then return, table else return,0
+ 
+end
diff --git a/Code/script_idl_mv/astrolib/multinom.pro b/Code/script_idl_mv/astrolib/multinom.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d11fd0dbee2000c74de71a320cae7e62ef4f6539
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/multinom.pro
@@ -0,0 +1,81 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;+
+; NAME:
+;   MULTINOM
+; PURPOSE:
+; SIMULATE MULTINOMIAL RANDOM VARIABLES
+;
+; AUTHOR : BRANDON C. KELLY, STEWARD OBS., APR 2006
+;
+; INPUTS :
+;
+;   N - THE NUMBER OF TRIALS
+;   P - A K-ELEMENT VECTOR CONTAINING THE PROBABILITIES FOR EACH
+;       CLASS.
+;
+; OPTIONAL INPUTS :
+;
+;   NRAND - THE NUMBER OF RANDOM VARIABLES TO DRAW
+;   SEED - THE SEED FOR THE RANDOM NUMBER GENERATOR
+;
+; OUTPUT :
+;   NRAND RANDOM DRAWS FROM A MULTINOMIAL DISTRIBUTION WITH PARAMETERS
+;   N AND P.
+;-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function multinom, n, p, nrand, seed=seed
+
+if n_params() lt 2 then begin
+    print, 'Syntax- theta = multinom( n, p,[ nrand, seed=seed] )'
+    return, 0
+endif
+
+k = n_elements(p)
+
+bad = where(p lt 0 or p gt 1, nbad)
+if nbad gt 0 then begin
+    print, 'All element of p must be 0 <= p <= 1.'
+    return, 0
+endif
+
+if n lt 1 then begin
+    print, 'N must be at least 1.'
+    return, 0
+endif
+
+if n_elements(nrand) eq 0 then nrand = 1
+
+                                ;check if binomial
+if k eq 2 then begin
+
+    binom = randomu(seed, nrand, binomial=[n, p[0]], /double)
+    multi = [[binom], [n - binom]]
+
+    return, transpose(multi)
+
+endif
+
+multi = lonarr(k, nrand)
+
+for i = 0L, nrand - 1 do begin
+    
+    multi[0,i] = randomu(seed, 1, binomial=[n, p[0]], /double)
+    j = 1L
+    nj = n - total(multi[0:j-1,i])
+
+    while nj gt 0 do begin
+        
+        pj = p[j] / total(p[j:*])
+
+        multi[j,i] = randomu(seed, 1, binomial=[nj,pj], /double)
+
+        j = j + 1
+        nj = n - total(multi[0:j-1,i])
+
+    endwhile
+
+endfor
+
+return, multi
+end
diff --git a/Code/script_idl_mv/astrolib/multiplot.pro b/Code/script_idl_mv/astrolib/multiplot.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0dea9200d861145628df8cc921e98ca4d36feb09
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/multiplot.pro
@@ -0,0 +1,555 @@
+;+
+; Name:
+;   MULTIPLOT
+;
+; Purpose:
+;	Create multiple plots with simple control over the gaps between plots.
+;   By default, the gap is zero but this can be set with the
+;   gap= keyword, or xgap=, ygap= for individual control over different axes.
+;   You can also place a single title along the x, y and top axes of the
+;   matrix of plots using the mtitle, mxtitle and mytitle keywords.
+;
+;	It is good for data with one or two shared axes and retains all the
+;	versatility of the plot commands (e.g. all keywords and log scaling).
+;	The plots are connected with the shared axes, which saves space by
+;	omitting redundant ticklabels and titles.  Multiplot does this by
+;	setting !p.position, !x.tickname and !y.tickname automatically.
+;	A call (multiplot,/reset) restores original values.
+;
+;   Coyote graphics users can find similar functionality in CGLAYOUT
+;        http://www.idlcoyote.com/idldoc/cg/cglayout.html
+;   Users of the post-8.0 IDL function graphics can find similar functionality
+;   in Paulo Penteado's routine PP_MULTIPLOT
+;        http://ppenteado.net/idl/pp_lib/doc/pp_multiplot__define.html
+; CALLING SEQUENCE:
+;	multiplot, pmulti, 
+;       gap=, xgap=, ygap=, 
+;       /square, 
+;       /doxaxis, /doyaxis, 
+;       mTitle=, mTitSize=, mTitOffset=, 
+;       mxTitle=, mxTitSize=, mxTitOffset=, 
+;       myTitle=, myTitSize=, myTitOffset=, 
+;       xtickformat=, ytickformat=
+;       /default, /reset, /rowmajor, /initialize
+;
+; INPUTS:
+;   pmulti: Optional input. [Nx,Ny] array describing the shape of the
+;       matrix of plots.  This is equivalent to the 2nd and 3rd elements
+;       of !p.multi.  Or you can send all 5 elements of the !p.multi.
+;
+; KEYWORD INPUTS:
+;   gap=: Set the gap between plots in normalized units.  Default is 0.
+;       This input overrides the xgap and ygap inputs.
+;   xgap=: Gap between plots in the x direction. Default 0. To set both
+;       x and y gap to the same value just use the gap keyword.
+;   ygap=: Gap between plots in the y direction. Default 0. To set both
+;       x and y gap to the same value just use the gap keyword.
+;
+;   mTitle: A single title to go across the top of the matrix of plots,
+;       as opposed to the plot over single plots you generate with the
+;       plot command for example. 
+;   mTitSize: The font size of the top title. Default is 1.25*!p.charsize
+;   mTitOffset: Offset of the title in the y-direction.
+;   mxTitle, mxTitSize, mxTitOffset: same as above but for the x-axis title
+;   myTitle, myTitSize, myTitOffset: same as above but for the y-axis title
+;
+;   xtickformat, ytickformat: Set the default tick formats when the ticks
+;       are plotted. This allows the user to avoid sending this to each
+;       plotting command which can have unexpected results if that axis
+;       was not to get tick labels in a given point in the matrix.
+;
+; KEYWORDS SWITCHES:
+;   /square: Force the axis ratio of each plot to be square. Note if
+;       xgap and ygap are set to different values, this axis ratio will
+;       not be preserved.  It will be preserved if gap= is used.
+;
+;   /doxaxis: Put axis labels, etc on the axis. Default is to place labels
+;       only on the left side and bottom sides of the plot matrix, but may
+;       be useful when some cells are empty; for example the x-axis of
+;       a 2x2 grid when only 3 total plots will be created.
+;   /doyaxis: Put axis labels, etc on the yxis.  Default is to place labels
+;       only on the left side and bottom sides of the plot matrix, but may
+;       be useful when some cells are empty; for example the x-axis of
+;       a 2x2 grid when only 3 total plots will be created.
+;
+;   /rowmajor: Like setting 5th element of !p.multi to 1. 
+;   /reset: Set plotting parameters to their saved values from before
+;       multiplot was initially called.
+;   /default: Set plotting parameters to IDL defaults.  This is useful
+;       when the saved parameters get in a whacky state.
+;   /initialize: Just do the initialization. This is what happends when
+;       you first call multiplot anyway.
+;
+; EXAMPLES:
+;   ; Make an array of plots [4,3] with a gap of 0.1 (in norm. coords.)
+;   ; and overall titles along the x and y axes as given.  Force the
+;   ; plots to be square.
+;
+;       cgerase & multiplot, [4,3], /square, gap=0.1, mXtitle='R', mYtitle='F(R)'
+;       for i=0,4*3-1 do begin
+;           cgplot, struct[i].x, struct[i].y, psym=4
+;           multiplot
+;       endfor
+;       multiplot,/reset
+;
+; Side Effects:
+;   Multiplot sets a number of system variables: !p.position, !p.multi,
+;	!x.tickname, !y.tickname, !P.noerase---but all can be reset with
+;	the call: multiplot,/reset  
+;
+;   Things can get out of wack if your program crashes in the middle of 
+;   making a matrix of plots, and often /reset will not fix it.  In those 
+;   cases, calling multiplot,/default will often fix the problem.
+;
+; Restrictions:
+;	1. If you use !p.multi as the method of telling how many plots
+;	are present, you have to set !p.multi at the beginning each time you
+;	use multiplot or call multiplot with the /reset keyword.
+;	2. There is no way to make plots of different sizes; each plot
+;	covers the same area on the screen or paper.
+;
+; Modification history:
+;	write, 21-23 Mar 94, Fred Knight (knight@ll.mit.edu)
+;	alter plot command that sets !x.window, etc. per suggestion of
+;	  Mark Hadfield (hadfield@storm.greta.cri.nz), 7 Apr 94, FKK
+;	add a /default keyword restore IDL's default values of system vars,
+;	  7 Apr 94, FKK
+;	modify two more sys vars !x(y).tickformat to suppress user-formatted
+;	  ticknames, per suggestion of Mark Hadfield (qv), 8 Apr 94, FKK
+;       
+;   2001-03-20    Added /square keyword
+;       Work in device coordinates so we can force aspect ratio to be square 
+;       if requested. Erin Scott Sheldon UMichigan
+;       
+;   2007-06-18
+;       Can now place titles on the overall x and y axes, as well as a 
+;       top title using these new keywords. 
+;           mTitle=, mTitSize=, mTitOffset=, 
+;           mxTitle=, mxTitSize=, mxTitOffset=, 
+;           myTitle=, myTitSize=, myTitOffset=, 
+;       Can also control overall tick formats. Useful because can just call
+;       multiplot initially and set this, while calling on each call to
+;       the plotting program will have unexpected results if the ticks
+;       are not to be labelled for that place in the matrix.
+;           xtickformat, ytickformat
+;       Erin Sheldon, NYU
+;   2007-08-28:
+;       Can now add gaps between the plots with these keywords:
+;           gap=, xgap=, ygap=
+;       where the values are in normalized coordinates. Erin Sheldon, NYU
+;   2009-11-23
+;       Initialize common block if M[X/Y]TITLE set W. Landsman 
+;   2011-02-07
+;        Use Coyote Graphics  W. Landsman    
+;   2012-03-21
+;        Use cgplot on initial call to get right background  W.L.
+;   2014-02-04
+;        Handle  [X/Y].OMargin   A. Negri, Bologna
+;
+;-
+
+PRO multiplot, pmulti, help=help, $
+        initialize=initialize, reset=reset, default=default, $
+        rowmajor=rowmajor,verbose=verbose, square=square, $
+        gap=gap_in, xgap=xgap_in, ygap=ygap_in, $
+        doxaxis=doxaxis, doyaxis=doyaxis, $
+        xtickformat=xtickformat_in, ytickformat=ytickformat_in, $
+        mtitle=mtitle, mTitSize=mTitSize, mTitOffset=mTitOffset, $
+        mxTitle=mxTitle, mxTitSize=mxTitSize, mxTitOffset=mxTitOffset, $
+        myTitle=myTitle, myTitSize=myTitSize, myTitOffset=myTitOffset
+
+
+
+
+    common multiplot $
+        ,nplots $                   ; [# of plots along x, # of plots along y]
+        ,nleft $                    ; # of plots remaining---like the first element of !p.multi
+        ,pdotmulti $                ; saved value of !p.multi
+        ,margins $                  ; calculated margins based on !p.multi or pmulti
+        ,pposition $                ; saved value of !p.position
+        ,colmajor $                 ; flag for column major order
+        ,noerase $                  ; saved value of !p.noerase
+        ,sqplot $                   ; should be make it square?
+        ,xtickname $                ; Original value
+        ,ytickname $                ; Original value
+        ,xtickformat_orig $         ; Original value
+        ,ytickformat_orig $        
+        ,xtickformat $              ; Value we will use
+        ,ytickformat $
+        ,gap  $
+        ,xgap $
+        ,ygap
+
+    ; help message
+    if keyword_set(help) then begin
+        doc_library,'multiplot' 
+        return 
+    endif
+   
+    
+    ; restore idl's default values (kill multiplot's influence)
+    if keyword_set(default) then begin
+        !p.position = 0
+        !x.tickname = ''
+        !y.tickname = ''
+        !x.tickformat = ''
+        !y.tickformat = ''
+        !p.multi = 0
+        !p.noerase = 0
+        nleft = 0
+        nplots = [1,1]
+        pdotmulti = !p.multi
+        margins = 0
+        sqplot=0
+        pposition = !p.position
+        noerase = !p.noerase
+        xtickname = !x.tickname
+        ytickname = !y.tickname
+        xtickformat = !x.tickformat
+        ytickformat = !y.tickformat
+
+        gap=0.0
+        xgap=0.0
+        ygap=0.0
+        if keyword_set(verbose) then begin
+            message,/inform,$
+                'Restore IDL''s defaults for affected system variables.'
+            message,/inform,$
+                'Reset multiplot''s common to IDL''s defaults.'
+        endif
+        return
+    endif
+
+    ; restore saved system variables
+    if keyword_set(reset) then begin
+         if n_elements(pposition) gt 0 then begin
+             !p.position = pposition
+             !x.tickname = xtickname
+             !y.tickname = ytickname
+             !x.tickformat = xtickformat_orig
+             !y.tickformat = ytickformat_orig
+             !p.multi = pdotmulti
+             !p.noerase = noerase
+             sqplot=0
+        endif
+        nleft = 0
+        if keyword_set(verbose) then begin
+            coords = '['+string(!p.position,form='(3(f4.2,","),f4.2)')+']'
+            multi = '['+string(!p.multi,form='(4(i2,","),i2)')+']'
+            message,/inform,'Reset.  !p.position='+coords+', !p.multi='+multi
+        endif
+        gap=0.0
+        xgap=0.0
+        ygap=0.0
+        return
+    endif
+
+    ;
+    ;  Now the user inputs
+    ;
+
+    ; How big are the gaps between the plots?
+    if n_elements(gap) eq 0 then begin
+        ; initial set up of common block values
+        xgap=0.0
+        ygap=0.0
+        gap=0.0
+    endif
+
+    if n_elements(xgap_in) ne 0 then xgap=xgap_in
+    if n_elements(ygap_in) ne 0 then ygap=ygap_in
+
+    ; gap will override any previously set values
+    if n_elements(gap_in) ne 0 then begin
+        gap=gap_in
+        xgap=gap
+        ygap=gap
+    endif
+
+
+    ;
+    ; Set up the plot layout
+    ;
+
+    ; Shall we force the individual plots to be square?
+    if keyword_set(square) then sqplot=1 else begin
+        if n_elements(sqplot) eq 0 then sqplot=0
+    endelse 
+
+
+    ; number of plots left in the grid
+    if n_elements(nleft) eq 1 then init = (nleft eq 0) else init = 1
+    if (n_elements(pmulti) eq 2) or (n_elements(pmulti) eq 5) then init = 1
+    if (n_elements(!p.multi) eq 5) then begin
+        if (!p.multi[1] gt 0) and (!p.multi[2] gt 0) then begin
+            init = (!p.multi[0] eq 0) 
+        endif
+    endif
+  
+    if ~init then init = keyword_set(mxtitle) || keyword_set(mytitle) || $
+                         keyword_set(mtitle)
+
+    ; initialize if we are on the first plot
+   
+    if init or keyword_set(initialize) then begin
+        case n_elements(pmulti) of
+            0:begin
+                if n_elements(!p.multi) eq 1 then return ; NOTHING TO SET
+                if n_elements(!p.multi) ne 5 then begin
+                    message,'Bogus !p.multi; aborting.'
+                endif
+                nplots = !p.multi[1:2] > 1
+                if keyword_set(rowmajor) then begin
+                    colmajor = 0 
+                endif else begin
+                    colmajor = !p.multi[4] eq 0
+                endelse
+            end
+            2:begin
+                nplots = pmulti
+                colmajor = not keyword_set(rowmajor) 
+            end
+            5:begin
+                nplots = pmulti[1:2]
+                if keyword_set(rowmajor) then begin
+                    colmajor = 0 
+                endif else begin
+                    colmajor = pmulti[4] eq 0
+                endelse
+            end
+            else: message,'pmulti can only have 0, 2, or 5 elements.'
+        endcase
+	
+        pposition = !p.position   ; save sysvar to be altered
+        xtickname = !x.tickname
+        ytickname = !y.tickname
+
+        ; keep original values for resetting
+        xtickformat_orig = !x.tickformat
+        ytickformat_orig = !y.tickformat
+
+        ; what will we actually plot when ticks are exposed?
+        if n_elements(xtickformat_in) ne 0 then begin
+            xtickformat=xtickformat_in
+        endif else begin
+            xtickformat=xtickformat_orig
+        endelse
+        if n_elements(ytickformat_in) ne 0 then begin
+            ytickformat=ytickformat_in
+        endif else begin
+            ytickformat=ytickformat_orig
+        endelse
+
+        pdotmulti = !p.multi
+        nleft = nplots[0]*nplots[1] ; total # of plots
+ 
+        !p.position = 0           ; reset
+        !p.multi = 0
+
+        ; set window & region
+
+        cgplot,/nodata,xstyle=4,ystyle=4,!x.range,!y.range,/noerase	
+
+        px = !x.window*!d.x_vsize
+        py = !y.window*!d.y_vsize
+        xsize = px[1] - px[0]
+        ysize = py[1] - py[0]
+
+        ; in normlized coordinates
+
+        ;Andrea Negri modification
+        nmargins = [min(!x.window)-min(!x.region)  $
+                     +!d.x_ch_size*!x.omargin[0]/double(!d.x_vsize), $
+                    min(!y.window)-min(!y.region)  $
+                     +!d.y_ch_size*!y.omargin[0]/double(!d.y_vsize), $
+                    max(!x.region)-max(!x.window)  $
+                     +!d.x_ch_size*!x.omargin[1]/double(!d.x_vsize), $
+                    max(!y.region)-max(!y.window)  $
+                     +!d.y_ch_size*!y.omargin[1]/double(!d.y_vsize)]
+
+        ;in device coord
+        margins = nmargins
+        margins[0] = nmargins[0]*!d.x_vsize
+        margins[2] = nmargins[2]*!d.x_vsize
+        margins[1] = nmargins[1]*!d.y_vsize
+        margins[3] = nmargins[3]*!d.y_vsize
+
+        noerase = !p.noerase
+        !p.noerase = 1            ; !p.multi does the same
+        if keyword_set(verbose) then begin
+            major = ['across then down (column major).',$
+                     'down then across (row major).']
+                 if colmajor then index = 0 else index = 1
+                 message,/inform,'Initialized for '+strtrim(nplots[0],2) $
+                     +'x'+strtrim(nplots[1],2)+', plotted '+major[index]
+        endif
+
+        if keyword_set(initialize) then return
+    endif
+
+    ;
+    ; Define the plot region without using !p.multi.
+    ;
+
+    cols = nplots[0]              ; for convenience
+    rows = nplots[1]
+    nleft = nleft - 1             ; decrement plots remaining
+    cur = cols*rows - nleft       ; current plot #: 1 to cols*rows
+
+    ; device coords per plot
+    idx = [(!d.x_vsize-margins[0]-margins[2])/cols, $
+           (!d.y_vsize-margins[1]-margins[3])/rows] 
+
+    ;; force to be square if requested
+    if sqplot then begin 
+        if idx[0] lt idx[1] then idx[1]=idx[0] else idx[0]=idx[1]
+    endif 
+
+    if colmajor then begin        ; location in matrix of plots
+        col = cur mod cols
+        if col eq 0 then col = cols
+        row = (cur-1)/cols + 1
+    endif else begin              ; here (1,2) is 1st col, 2nd row
+        row = cur mod rows
+        if row eq 0 then row = rows
+        col = (cur-1)/rows + 1
+    endelse
+
+
+    pos = $
+        [(col-1)*idx[0], (rows-row)*idx[1],   $
+         col*idx[0],     (rows-row+1)*idx[1]] $
+       +                                      $
+        [margins[0], margins[1],              $
+         margins[0], margins[1]]
+  
+    ; back to normalized coords
+    pos[0] = pos[0]/!d.x_vsize
+    pos[2] = pos[2]/!d.x_vsize
+    pos[1] = pos[1]/!d.y_vsize
+    pos[3] = pos[3]/!d.y_vsize
+
+    ; add gaps
+    pos[0] = pos[0] + xgap
+    pos[2] = pos[2] - xgap
+
+    pos[1] = pos[1] + ygap
+    pos[3] = pos[3] - ygap
+
+    ;
+    ; Finally set the system variables; user shouldn't change them.
+    ;
+    
+    !p.position = pos
+    onbottom = (row eq rows) or (rows eq 1)
+    onleft = (col eq 1) or (cols eq 1)
+    IF keyword_set(doxaxis) THEN onbottom=1
+    IF keyword_set(doyaxis) THEN onleft=1
+    if onbottom then begin
+        !x.tickname = xtickname 
+    endif else begin
+        !x.tickname = replicate(' ',30)
+    endelse
+    if onleft then !y.tickname = ytickname else !y.tickname = replicate(' ',30)
+    if onbottom then !x.tickformat = xtickformat else !x.tickformat = ''
+    if onleft then !y.tickformat = ytickformat else !y.tickformat = ''
+    if keyword_set(verbose) then begin
+        coords = '['+string(pos,form='(3(f4.2,","),f4.2)')+']'
+        plotno = 'Setup for plot ['+strtrim(col,2)+','+strtrim(row,2)+'] of ' $
+            +strtrim(cols,2)+'x'+strtrim(rows,2)
+        message,/inform,plotno+' at '+coords
+    endif
+
+
+
+    ; Add titles to overall axes
+
+    ; area covered by entire plot field in device coords
+    allpos = $
+        [0,                       0,      cols*idx[0], rows*idx[1]] +   $
+        [margins[0],     margins[1],        margins[0], margins[1]]
+    ;; back to normalized coords
+    allpos[0] = allpos[0]/!d.x_vsize
+    allpos[2] = allpos[2]/!d.x_vsize
+    allpos[1] = allpos[1]/!d.y_vsize
+    allpos[3] = allpos[3]/!d.y_vsize
+
+    xCharSizeNorm = float(!d.x_ch_size) / float(!d.x_size)
+    yCharSizeNorm = float(!d.y_ch_size) / float(!d.y_size)
+
+    ; top title
+    if n_elements(mTitle) ne 0 then begin
+        if n_elements(mTitSize) eq 0 then mTitSize = 1.0
+        if n_elements(mTitOffset) eq 0 then mTitOffset = 0.0
+
+        ; align middle of region in x
+        xpos = (allpos[2] - allpos[0])/2.0 + nmargins[0]
+        ; align relative to the top.  Default is right there plus 
+        ; one character size.
+        ypos = allpos[3] + (mTitOffset+1.0)*yCharSizeNorm
+
+        ; correct for gaps
+        ypos = ypos - ygap
+         cgtext, $
+            xpos, $
+            ypos, $
+            mTitle, $
+            /normal, $
+            align = 0.5, $
+            charsize = 1.25 * mTitSize
+    endif
+
+    ; x title
+    if n_elements(mxTitle) ne 0 then begin
+        if n_elements(mxTitSize) eq 0 then mxTitSize = 1.0
+        if n_elements(mxTitOffset) eq 0 then mxTitOffset = 0.0
+
+        ; align middle of region in x
+        xpos = (allpos[2] - allpos[0])/2.0 + nmargins[0]
+
+        ; align middle of region in x
+        ypos = allpos[1] - (mxTitOffset+3.0)*yCharSizeNorm
+
+        ; correct for gaps
+        ypos = ypos + ygap
+        cgtext, $
+            xpos, $
+            ypos, $
+            mxTitle, $
+            /normal, $
+            align = 0.5, $
+            charsize = mxTitSize
+    endif
+
+
+
+    ; y title
+    if n_elements(myTitle) ne 0 then begin
+        if n_elements(myTitSize) eq 0 then myTitSize = 1.0
+        if n_elements(myTitOffset) eq 0 then myTitOffset = 0.0
+
+        ; align relative to the left side.  Default is right there plus 
+        ; one character size.
+        xpos = allpos[0] - (myTitOffset+6.0)*xCharSizeNorm
+        ;xpos = allpos[0] - (myTitOffset+4.0)*xCharSizeNorm
+
+        ; align middle of region in x
+        ypos = (allpos[3] - allpos[1])/2.0 + nmargins[1]
+
+
+        ; correct for gaps
+        xpos = xpos + xgap
+
+        cgtext, $
+            xpos, $
+            ypos, $
+            myTitle, $
+            /normal, $
+            align = 0.5, $
+            orientation = 90.0, $
+            charsize = myTitSize
+    endif
+
+
+return
+end
diff --git a/Code/script_idl_mv/astrolib/mwrfits.pro b/Code/script_idl_mv/astrolib/mwrfits.pro
new file mode 100644
index 0000000000000000000000000000000000000000..28f71020860b306710ddbfa17dfff420e7ecd9ba
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/mwrfits.pro
@@ -0,0 +1,1731 @@
+;+
+; NAME:
+;       MWRFITS
+; PURPOSE:
+;       Write all standard FITS data types from input arrays or structures.
+;
+; EXPLANATION:
+;       Must be used with a post-September 2009 version of FXADDPAR.
+;
+; CALLING SEQUENCE:
+;       MWRFITS, Input, Filename, [Header],
+;                       /LSCALE , /ISCALE, /BSCALE, 
+;                       /USE_COLNUM, /Silent, /Create, /No_comment, /Version, $
+;                       Alias=, /ASCII, Separator=, Terminator=, Null=,
+;                       /Logical_cols, /Bit_cols, /Nbit_cols, 
+;                       Group=, Pscale=, Pzero=, Status=
+;
+; INPUTS:
+;       Input = Array or structure to be written to FITS file.
+;
+;               -When writing FITS primary data or image extensions
+;                input should be an array.
+;               --If data is to be grouped
+;                 the Group keyword should be specified to point to
+;                 a two dimensional array.  The first dimension of the
+;                 Group array will be PCOUNT while the second dimension
+;                 should be the same as the last dimension of Input.
+;               --If Input is undefined, then a dummy primary dataset
+;                 or Image extension is created [This might be done, e.g.,
+;                 to put appropriate keywords in a dummy primary
+;                 HDU].
+;
+;               -When writing an ASCII table extension, Input should
+;                be a structure array where no element of the structure
+;                is a structure or array (except see below).
+;               --A byte array will be written as A field.  No checking
+;                 is done to ensure that the values in the byte field
+;                 are valid ASCII.
+;               --Complex numbers are written to two columns with '_R' and
+;                 '_I' appended to the TTYPE fields (if present).  The
+;                 complex number is enclosed in square brackets in the output.
+;               --Strings are written to fields with the length adjusted
+;                 to accommodate the largest string.  Shorter strings are
+;                 blank padded to the right.
+;
+;               -When writing a binary table extension, the input should
+;                be a structure array with no element of the structure
+;                being a substructure.
+;
+;               If a structure is specified on input and the output
+;               file does not exist or the /CREATE keyword is specified
+;               a dummy primary HDU is created.
+;
+;       Filename = String containing the name of the file to be written.
+;                By default MWRFITS appends a new extension to existing
+;                files which are assumed to be valid FITS.  The /CREATE
+;                keyword can be used to ensure that a new FITS file
+;                is created even if the file already exists.
+;
+; OUTPUTS:
+;
+; OPTIONAL INPUTS:
+;       Header = Header should be a string array.  Each element of the
+;                array is added as a row in the FITS  header.  No
+;                parsing is done of this data.  MWRFITS will prepend
+;                required structural (and, if specified, scaling)
+;                keywords before the rows specified in Header.
+;                Rows describing columns in the table will be appended
+;                to the contents of Header.
+;                Header lines will be extended or truncated to
+;                80 characters as necessary.
+;                If Header is specified then on return Header will have
+;                the header generated for the specified extension.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       ALias=   Set up aliases to convert from the IDL structure
+;                to the FITS column name.  The value should be
+;                a STRARR(2,*) value where the first element of
+;                each pair of values corresponds to a column
+;                in the structure and the second is the name
+;                to be used in the FITS file.
+;                The order of the alias keyword is compatible with
+;                use in MRDFITS.
+;       ASCII  - Creates an ASCII table rather than a binary table.
+;                This keyword may be specified as:
+;                /ASCII - Use default formats for columns.
+;                ASCII='format_string' allows the user to specify
+;                  the format of various data types such using the following
+;                  syntax 'column_type:format, column_type:format'.  E.g.,
+;                ASCII='A:A1,I:I6,L:I10,B:I4,F:G15.9,D:G23.17,C:G15.9,M:G23.17'
+;                gives the default formats used for each type.  The TFORM
+;                fields for the real and complex types indicate will use corresponding
+;                E and D formats when a G format is specified.
+;                Note that the length of the field for ASCII strings and
+;                byte arrays is automatically determined for each column.
+;       BIT_COLS=   An array of indices of the bit columns.   The data should
+;                comprise a byte array with the appropriate dimensions.
+;                If the number of bits per row (see NBIT_COLS)
+;                is greater than 8, then the first dimension of the array 
+;                should match the number of input bytes per row.
+;       BSCALE   Scale floats, longs, or shorts to unsigned bytes (see LSCALE)
+;       /CREATE   If this keyword is non-zero, then a new FITS file will
+;                be created regardless of whether the file currently
+;                exists.  Otherwise when the file already exists,
+;                a FITS extension will be appended to the existing file
+;                which is assumed to be a valid FITS file.
+;       GROUP=   This keyword indicates that GROUPed FITS data is to
+;                be generated.
+;                Group should be a 2-D array of the appropriate output type.
+;                The first dimension will set the number of group parameters.
+;                The second dimension must agree with the last dimension
+;                of the Input array.
+;       ISCALE   Scale floats or longs to short integer (see LSCALE)
+;       LOGICAL_COLS=  An array of indices of the logical column numbers.
+;                These should start with the first column having index *1*.
+;                The structure element should either be an array of characters
+;                with the values 'T' or 'F', or an array of bytes having the 
+;                values byte('T')=84b, byte('F')=70b or 0b.     The use of bytes
+;                allows the specification of undefined values (0b).
+;       LSCALE   Scale floating point numbers to long integers.
+;                This keyword may be specified in three ways.
+;                /LSCALE (or LSCALE=1) asks for scaling to be automatically
+;                determined. LSCALE=value divides the input by value.
+;                I.e., BSCALE=value, BZERO=0.  Numbers out of range are 
+;                given the value of NULL if specified, otherwise they are given
+;                the appropriate extremum value.  LSCALE=(value,value)
+;                uses the first value as BSCALE and the second as BZERO
+;                (or TSCALE and TZERO for tables).
+;       NBIT_COLS=  The number of bits actually used in the bit array.
+;                This argument must point to an array of the same dimension
+;                as BIT_COLS.
+;       /NO_COPY = By default, MWRFITS makes a copy of the input variable
+;                before any modifications necessary to write it to a FITS
+;                file.    If you have a large array/structure, and don't 
+;                require it for subsequent processing, then /NO_COPY will
+;                save memory.
+;       NO_TYPES  If the NO_TYPES keyword is specified, then no TTYPE
+;                keywords will be created for ASCII and BINARY tables.
+;       No_comment Do not write comment keywords in the header
+;       NULL=    Value to be written for integers/strings which are
+;                undefined or unwritable.
+;       PSCALE=  An array giving scaling parameters for the group keywords.
+;                It should have the same dimension as the first dimension
+;                of Group.
+;       PZERO=   An array giving offset parameters for the group keywords.
+;                It should have the same dimension as the first dimension
+;                of Group.
+;       Separator= This keyword can be specified as a string which will
+;                be used to separate fields in ASCII tables.  By default
+;                fields are separated by a blank.
+;       /SILENT   Suppress informative messages.  Errors will still
+;                be reported.
+;       Terminator= This keyword can be specified to provide a string which
+;                will be placed at the end of each row of an ASCII table.
+;                No terminator is used when not specified.
+;                If a non-string terminator is specified (including
+;                when the /terminator form is used), a new line terminator
+;                is appended.
+;       USE_COLNUM  When creating column names for binary and ASCII tables
+;                MWRFITS attempts to use structure field name
+;                values.  If USE_COLNUM is specified and non-zero then
+;                column names will be generated as 'C1, C2, ... 'Cn'
+;                for the number of columns in the table.
+;       Version   Print the version number of MWRFITS.
+;
+; OPTIONAL OUTPUT KEYWORD:
+;       Status - 0 if FITS file is successfully written, -1 if there is a
+;                a problem (e.g. nonexistent directory, or no write permission)
+; EXAMPLE:
+;       Write a simple array:
+;            a=fltarr(20,20)
+;            mwrfits,a,'test.fits'
+;
+;       Append a 3 column, 2 row, binary table extension to file just created.
+;            a={name:'M31', coords:(30., 20.), distance:2}
+;            a=replicate(a, 2);
+;            mwrfits,a,'test.fits'
+;
+;       Now add on an image extension:
+;            a=lonarr(10,10,10)
+;            hdr=("COMMENT  This is a comment line to put in the header", $
+;                 "MYKEY    = "Some desired keyword value")
+;            mwrfits,a,'test.fits',hdr
+;
+; RESTRICTIONS:
+;       (1)     Variable length columns are not supported for anything
+;               other than simple types (byte, int, long, float, double).
+;       (2)     Empty strings are converted to 1 element blank strings (because
+;               IDL refuses to write an empty string (0b) from a structure)
+; NOTES:
+;       This multiple format FITS writer is designed to provide a
+;       single, simple interface to writing all common types of FITS data.
+;       Given the number of options within the program and the
+;       variety of IDL systems available it is likely that a number
+;       of bugs are yet to be uncovered. 
+;
+; PROCEDURES USED:
+;       FXPAR(), FXADDPAR
+; MODIfICATION HISTORY:
+;       Version 0.9: By T. McGlynn   1997-07-23
+;              Initial beta release.
+;       Dec 1, 1997, Lindler, Modified to work under VMS.
+;       Version 0.91: T. McGlynn  1998-03-09
+;               Fixed problem in handling null primary arrays.
+;       Version 0.92: T. McGlynn 1998-09-09
+;               Add no_comment flag and keep user comments on fields.
+;               Fix handling of bit fields.
+;       Version 0.93: T. McGlynn 1999-03-10
+;               Fix table appends on VMS.
+;       Version 0.93a  W. Landsman/D. Schlegel
+;               Update keyword values in chk_and_upd if data type has changed 
+;       Version 0.94: T. McGlynn 2000-02-02
+;               Efficient processing of ASCII tables.
+;               Use G rather than E formats as defaults for ASCII tables
+;                and make the default precision long enough that transformations
+;                binary to/from ASCII are invertible.
+;               Some loop indices made long.
+;               Fixed some ends to match block beginnings.
+;       Version 0.95: T. McGlynn 2000-11-06
+;               Several fixes to scaling.  Thanks to David Sahnow for
+;               documenting the problems.
+;               Added PCOUNT,GCOUNT keywords to Image extensions.
+;               Version numbers shown in SIMPLE/XTENSION comments
+;       Version 0.96: T. McGlynn 2001-04-06
+;               Changed how files are opened to handle ~ consistently
+;       Version 1.0: T. McGlynn 2001-12-04
+;               Unsigned integers,
+;               64 bit integers.
+;               Aliases
+;               Variable length arrays
+;               Some code cleanup
+;       Version 1.1: T. McGlynn 2002-2-18
+;               Fixed major bug in processing of unsigned integers.
+;               (Thanks to Stephane Beland)
+;       Version 1.2: Stephane Beland 2003-03-17
+;               Fixed problem in creating dummy dataset when passing undefined
+;               data, caused by an update to FXADDPAR routine.
+;       Version 1.2.1 Stephane Beland 2003-09-10
+;               Exit gracefully if write privileges unavailable
+;       Version 1.3 Wayne Landsman 2003-10-24
+;               Don't use EXECUTE() statement if on a virtual machine
+;       Version 1.3a Wayne Landsman 2004-5-21
+;               Fix for variable type arrays
+;       Version 1.4 Wayne Landsman 2004-07-16
+;               Use STRUCT_ASSIGN when modifying structure with pointer tags
+;       Version 1.4a Wayne Landsman 2005-01-03
+;               Fix writing of empty strings in binary tables 
+;       Version 1.4b Wayne Landsman 2006-02-23
+;               Propagate /SILENT keyword to mwr_tablehdr
+;       Version 1.5 Wayne Landsman  2006-05-24
+;               Open file using /SWAP_IF_LITTLE_ENDIAN keyword 
+;               Convert empty strings to 1 element blank strings before writing            
+;       Version 1.5a Wayne Landsman 2006-06-29
+;               Fix problem introduced 2006-05-24 with multidimensional strings
+;       Version 1.5b K. Tolbert 2006-06-29
+;               Make V1.5a fix work pre-V6.0
+;       Version 1.5c I.Evans/W.Landsman 2006-08-08
+;               Allow logical columns to be specified as bytes 
+;       Version 1,5d K. Tolbert 2006-08-11 
+;               Make V1.5a fix work for scalar empty string
+;       Version 1.6  W. Landsman  2006-09-22
+;               Assume since V5.5, remove VMS support
+;       Version 1.6a  W. Landsman  2006-09-22
+;               Don't right-justify strings 
+;       Version 1.7  W. Landsman  2009-01-12
+;               Added STATUS output keyword
+;       Version 1.7a W. Landsman 2009-04-10
+;               Since V6.4 strings are no longer limited to 1024
+;               elements 
+;       Version 1.8 Pierre Chanial 2009-06-23
+;               trim alias, implement logical TFORM 'L', don't
+;               add space after tform key.
+;       Version 1.9 W. Landsman 2009-07-20
+;               Suppress compilation messages of supporting routines
+;       Version 1.10 W. Landsman 2009-09-30
+;               Allow TTYPE values of 'T' and 'F', fix USE_COLNUM for bin tables
+;       Version 1.11 W. Landsman 2010-11-18
+;               Allow LONG64 number of bytes, use V6.0 notation 
+;       Version 1.11a W. Landsman 2012-08-12
+;               Better documentation, error checking for logical columns
+;       Version 1.11b M. Haffner/W.L. 2012-10-12
+;               Added /No_COPY keyword, fix problem with 32 bit overflow
+;       Version 1.12 W. Landsman  2014-04-23
+;       Version 1.12a W.Landsman/M. Fossati 2014-10-14
+;               Fix LONG overflow for very large files
+;       Version 1.12b I. Evans 2015-07-27
+;               Fix value check for byte('T'), byte('F'), or 0b for logical
+;               columns with null values
+;       Version 1.13 W. Landsman 2016-02-24
+;               Abort if a structure supplied with more than 999 tags 
+;-
+
+; What is the current version of this program?
+function mwr_version
+     compile_opt idl2,hidden
+    return, '1.13'
+end
+    
+
+; Find the appropriate offset for a given unsigned type
+; or just return 0 if the type is not unsigned.
+
+function mwr_unsigned_offset, type
+     compile_opt idl2,hidden
+     
+    case type of            
+    12: return, 32768US
+    13: return, 2147483648UL
+    15: return, 9223372036854775808ULL
+    else: return,0
+    endcase
+end
+
+
+; Add a keyword as non-destructively as possible to a FITS header
+pro chk_and_upd, header, key, value, comment, nological=nological
+     compile_opt idl2,hidden
+
+
+    xcomm = ""
+    if n_elements(comment) gt 0 then xcomm = comment
+    if n_elements(header) eq 0 then begin
+      
+        fxaddpar, header, key, value, xcomm
+       
+    endif else begin
+       
+        oldvalue = fxpar(header, key, count=count, comment=oldcomment)
+        if (count eq 1) then begin
+
+           qchange = 0 ; Set to 1 if either the type of variable or its
+                       ; value changes.
+            size1 = size(oldvalue,/type) & size2 = size(value,/type)
+            if size1 NE size2 then qchange = 1 $
+            else if (oldvalue ne value) then qchange = 1
+
+            if (qchange) then begin
+
+               if n_elements(oldcomment) gt 0 then xcomm = oldcomment[0]
+               fxaddpar, header, key, value, xcomm,nological=nological
+              
+           endif
+           
+       endif else begin
+           
+            fxaddpar, header, key, value, xcomm,nological=nological
+        endelse
+       
+    endelse
+end
+
+; Get the column name appropriate for a given tag
+function mwr_checktype, tag, alias=alias
+     compile_opt idl2,hidden
+
+    if ~keyword_set(alias) then return, tag
+
+    sz = size(alias,/struc)
+    ; 1 or 2 D string array with first dimension of 2
+    if (sz.type_name EQ 'STRING') && (sz.dimensions[0] EQ 2) && $
+       (sz.N_dimensions LE 2)  then begin 
+       w = where(tag eq strtrim(alias[0,*],2),N_alias)
+       if N_alias EQ 0 then return,tag else return,alias[1,w[0]]
+    endif else begin
+       print,'MWRFITS: Warning: Alias values not strarr(2) or strarr(2,*)'
+    endelse
+    return, tag
+end
+
+; Create an ASCII table
+pro mwr_ascii, input, siz, lun, bof, header,     $
+        ascii=ascii,                             $
+       null=null,                               $
+       use_colnum = use_colnum,                 $
+       lscale=lscale, iscale=iscale,               $
+       bscale=bscale,                           $
+       no_types=no_types,                      $
+       separator=separator,                     $
+       terminator=terminator,                   $
+        no_comment=no_comment,                   $
+       silent=silent,                           $
+       alias=alias
+     compile_opt idl2,hidden
+       
+    ; Write the header and data for a FITS ASCII table extension.
+  
+    types=  ['A',   'I',   'L',   'B',   'F',    'D',      'C',     'M',     'K']
+    formats=['A1',  'I6',  'I10', 'I4',  'G15.9','G23.17', 'G15.9', 'G23.17','I20']
+    lengths=[1,     6,     10,     4,    15,     23,       15,      23,      20]
+
+    ; Check if the user is overriding any default formats.
+    sz = size(ascii)
+
+    if sz[0] eq 0 and sz[1] eq 7 then begin
+        ascii = strupcase(strcompress(ascii,/remo))
+        for i=0, n_elements(types)-1  do begin
+            p = strpos(ascii,types[i]+':')
+            if p ge 0 then begin
+
+               q = strpos(ascii, ',', p+1)
+               if q lt p then q = strlen(ascii)+1
+               formats[i] = strmid(ascii, p+2, (q-p)-2)
+               len = 0
+           
+               reads, formats[i], len, format='(1X,I)'
+               lengths[i] = len
+            endif
+        endfor
+    endif
+
+    i0      = input[0]
+    ntag    = n_tags(i0)
+    tags    = tag_names(i0)
+    ctypes  = lonarr(ntag)
+    strmaxs = lonarr(ntag)
+
+    if ~keyword_set(separator) then separator=' '
+    slen = strlen(separator)
+
+    offsets = 0
+    tforms = ''
+    ttypes = ''
+    offset = 0
+
+    totalFormat = ""
+    xsep = "";
+
+    for i=0, ntag-1 do begin
+
+        totalFormat = totalFormat + xsep;
+    
+        sz = size(i0.(i))
+        if (sz[0] ne 0) && (sz[sz[0]+1] ne 1) then begin
+            print, 'MWRFITS Error: ASCII table cannot contain arrays'
+           return
+        endif
+
+        ctypes[i] = sz[1]
+
+        xtype = mwr_checktype(tags[i], alias=alias)
+    
+        ttypes = [ttypes, xtype+' ']
+
+        if sz[0] gt 0 then begin
+            ; Byte array to be handled as a string.
+           nelem = sz[sz[0]+2]
+           ctypes[i] = sz[sz[0]+1]
+            tf = 'A'+strcompress(string(nelem))
+            tforms = [tforms, tf]
+           offsets = [offsets, offset]
+            totalFormat = totalFormat + tf
+           offset = offset + nelem
+       
+        endif else if sz[1] eq 7 then begin
+            ; Use longest string to get appropriate size.
+           strmax = max(strlen(input.(i)))
+           strmaxs[i] = strmax
+           tf = 'A'+strcompress(string(strmax), /remo)
+           tforms = [tforms, tf]
+           offsets = [offsets, offset]
+            totalFormat = totalFormat + tf
+           ctypes[i] = 7
+           offset = offset + strmax
+       
+        endif else if (sz[1] eq 6 ) || (sz[1] eq 9) then begin
+            ; Complexes handled as two floats.
+           offset++
+       
+           if sz[1] eq 6 then indx = where(types eq 'C')
+           if sz[1] eq 9 then indx = where(types eq 'M')
+           indx = indx[0]
+           fx = formats[indx]
+           if strcmp(fx,'g',1,/fold) then begin
+               if (sz[1] eq 6) then begin
+                   fx = "E"+strmid(fx,1 )
+               endif else begin
+                  fx = "D"+strmid(fx,1 )
+               endelse
+           endif
+           tforms = [tforms, fx, fx]
+            offsets = [offsets, offset, offset+lengths[indx]+1]
+           nel = n_elements(ttypes)
+           ttypes = [ttypes[0:nel-2], xtype+'_R', xtype+'_I']
+           offset = offset + 2*lengths[indx] + 1
+
+            totalFormat = totalFormat + '"[",'+formats[indx]+',1x,'+formats[indx]+',"]"'
+            offset = offset+1
+       
+        endif else begin
+         
+            if sz[1] eq 1 then indx = where(types eq 'B')                      $
+           else if (sz[1] eq 2) || (sz[1] eq 12) then indx = where(types eq 'I')  $
+           else if (sz[1] eq 3) || (sz[1] eq 13) then indx = where(types eq 'L')  $
+           else if sz[1] eq 4 then indx = where(types eq 'F')                 $
+           else if sz[1] eq 5 then indx = where(types eq 'D')                 $
+           else if (sz[1] eq 14) || (sz[1] eq 15) then indx = where(types eq 'K') $
+           else begin
+               print, 'MWRFITS Error: Invalid type in ASCII table'
+               return
+           endelse
+       
+           indx = indx[0]
+           fx = formats[indx]
+           if (strmid(fx, 0, 1) eq 'G' || strmid(fx, 0, 1) eq 'g') then begin
+               if sz[1] eq 4 then begin
+                   fx = 'E'+strmid(fx, 1, 99)
+               endif else begin
+                   fx = 'D'+strmid(fx, 1, 99)
+               endelse
+           endif
+       
+           tforms = [tforms, fx]
+           offsets = [offsets, offset]
+            totalFormat = totalFormat + formats[indx]
+           offset = offset + lengths[indx]
+        endelse
+        if i ne ntag-1 then begin
+            offset = offset + slen
+        endif
+
+        xsep = ", '"+separator+"', "
+    
+    endfor
+    
+
+    if  keyword_set(terminator) then begin
+        sz = size(terminator);
+        if sz[0] ne 0 || sz[1] ne 7 then begin
+            terminator= string(10B)
+        endif
+    endif
+
+
+    if keyword_set(terminator) then offset = offset+strlen(terminator)
+    ; Write required FITS keywords.
+
+    chk_and_upd, header, 'XTENSION', 'TABLE', 'ASCII table extension written by MWRFITS '+mwr_version()
+    chk_and_upd, header, 'BITPIX', 8,'Required Value: ASCII characters'
+    chk_and_upd, header, 'NAXIS', 2,'Required Value'
+    chk_and_upd, header, 'NAXIS1', offset, 'Number of characters in a row'
+    chk_and_upd, header, 'NAXIS2', n_elements(input), 'Number of rows'
+    chk_and_upd, header, 'PCOUNT', 0, 'Required value'
+    chk_and_upd, header, 'GCOUNT', 1, 'Required value'
+    chk_and_upd, header, 'TFIELDS', n_elements(ttypes)-1, 'Number of fields'
+
+    ; Recall that the TTYPES, TFORMS, and OFFSETS arrays have an
+    ; initial dummy element.
+
+
+    ; Write the TTYPE keywords.
+    
+    if ~keyword_set(no_types) then begin
+        for i=1, n_elements(ttypes)-1 do begin
+            key = 'TTYPE'+ strcompress(string(i),/remo)
+            if keyword_set(use_colnum) then begin
+               value = 'C'+strcompress(string(i),/remo)
+           endif else begin
+               value = ttypes[i]+' '
+           endelse
+           chk_and_upd, header, key, value
+        endfor
+        if (~keyword_set(no_comment)) then $
+	    sxaddhist, [' ',' *** Column names ***',' '],header, $
+	        /comment,location='TTYPE1'
+     
+    endif
+
+    ; Write the TBCOL keywords.
+
+    for i=1, n_elements(ttypes)-1 do begin
+        key= 'TBCOL'+strcompress(string(i),/remo)
+        chk_and_upd, header, key, offsets[i]+1
+    endfor
+
+    if ~keyword_set(no_comment) then $
+        sxaddhist,[' ',' *** Column offsets ***',' '],header,/comm, $
+	           location = 'TBCOL1'
+
+    ; Write the TFORM keywords
+
+    for i=1, n_elements(ttypes)-1 do begin
+        key= 'TFORM'+strcompress(string(i),/remo)
+        chk_and_upd, header, key, tforms[i]
+    endfor
+
+    if ~keyword_set(no_comment) then $
+        sxaddhist,[' ',' *** Column formats ***',' '],header, $
+	    /COMMENT, location = 'TFORM1'
+ 
+    ; Write the header.
+
+    mwr_header, lun, header
+
+    ;  Write out the data applying the field formats
+
+    totalFormat = "("+totalFormat+")";
+    
+     strings = string(input, format=totalFormat)
+     if keyword_set(terminator) then strings = strings+terminator
+     writeu, lun, strings
+ 
+    ; Check to see if any padding is required.
+
+    nbytes = long64(n_elements(input))*offset
+    padding = 2880 - nbytes mod 2880
+    if padding ne 0 then writeu, lun, replicate(32b, padding)
+    
+   return
+end
+
+; Write a dummy primary header-data unit.
+pro mwr_dummy, lun
+     compile_opt idl2,hidden
+
+    fxaddpar, header, 'SIMPLE', 'T','Dummy Created by MWRFITS v'+mwr_version()
+    fxaddpar, header, 'BITPIX', 8, 'Dummy primary header created by MWRFITS'
+    fxaddpar, header, 'NAXIS', 0, 'No data is associated with this header'
+    fxaddpar, header, 'EXTEND', 'T', 'Extensions may (will!) be present'
+
+    mwr_header, lun, header
+end
+
+; Check if this is a valid pointer array for variable length data.
+function mwr_validptr, vtypes, nfld, index, array
+     compile_opt idl2,hidden
+    
+    type = -1
+    offset = 0L
+    for i=0, n_elements(array)-1 do begin
+       if ptr_valid(array[i]) then begin
+           
+           sz = size(*array[i])
+           if sz[0] gt 1 then begin
+              print,'MWRFITS: Error: Multidimensional Pointer array'
+              return, 0
+           endif
+           if type eq -1 then begin
+              type = sz[sz[0] + 1]
+           endif else begin
+              if sz[sz[0] + 1] ne type then begin
+                  print,'MWRFITS: Error: Inconsistent type in pointer array'
+                  return, 0
+              endif
+           endelse
+           xsz = sz[1]
+           if sz[0] eq 0 then xsz = 1
+           offset = offset + xsz
+       endif
+    endfor
+    if type eq -1 then begin
+        ; If there is no data assume an I*2 type
+       type = 2
+    endif
+
+    if  (type lt 1 || type gt 5) &&(type lt 12 || type gt 15) then begin
+       print,'MWRFITS: Error: Unsupported type for variable length array'
+    endif
+
+    types = 'BIJED      IJKK'
+    sizes = [1,2,4,4,8,0,0,0,0,0,0,2,4,8,8]
+    
+    if n_elements(vtypes) eq 0 then begin
+       
+        vtype = {status:0, data:array,           $
+            type: strmid(types, type-1, 1),            $
+            itype: type, ilen: sizes[type-1],          $
+            offset:offset }
+    
+       vtypes = replicate(vtype, nfld)
+
+    endif else begin
+       ; This ensures compatible structures without
+       ; having to used named structures.
+       
+       vtype = vtypes[0]
+       vtype.status = 0
+       vtype.data   = array
+       vtype.type   = strmid(types, type-1, 1)
+       vtype.itype  = type
+       vtype.ilen   = sizes[type-1]
+       vtype.offset = offset
+       vtypes[index] = vtype
+       
+       
+    endelse
+    vtypes[index].status = 1;
+
+    return, 1
+end
+       
+; Handle the header for a binary table.
+pro mwr_tablehdr, lun, input, header, vtypes,     $
+              no_types=no_types,                $
+              logical_cols = logical_cols,         $
+              bit_cols = bit_cols,                $
+              nbit_cols= nbit_cols,             $
+                no_comment=no_comment,            $
+              alias=alias,                      $
+              silent=silent,                     $
+	      use_colnum = use_colnum
+     compile_opt idl2,hidden
+
+    if ~keyword_set(no_types) then no_types = 0
+
+    nfld = n_tags(input[0])
+    if nfld le 0 then begin
+       print, 'MWRFITS Error: Input contains no structure fields.'
+       return
+    endif
+
+    tags = tag_names(input)
+
+    ; Get the number of rows in the table.
+
+    nrow = n_elements(input)
+
+    dims    = lonarr(nfld)
+    tdims   = strarr(nfld)
+    types   = strarr(nfld)
+    pointers= lonarr(nfld)
+
+    ; offsets = null...  Don't want to define this
+    ; in advance since reference to ulon64 won't word with IDL < 5.2
+    ;
+    ; Get the type and length of each column.  We do this
+    ; by examining the contents of the first row of the structure.
+    ;
+
+    nbyte = 0ULL
+
+    islogical = bytarr(nfld)
+    if keyword_set(logical_cols) then islogical[logical_cols-1] = 1b
+   
+    for i=0, nfld-1 do begin
+
+       a = input[0].(i)
+
+       sz = size(a)
+       
+       nelem    = ulong64(sz[sz[0]+2])
+       type_ele = sz[sz[0]+1]
+       if type_ele EQ 7 then maxstr = max(strlen(input.(i)) > 1)
+       
+       if islogical[i]  then begin        
+          if (type_ele EQ 1) then begin
+          gg = (input.(i) EQ 84b) or (input.(i) EQ 70b) or (input.(i) EQ 0b) 
+	       if ~array_equal(gg,1b) then begin 
+	       islogical[i] = 0b
+	       message,/CON, 'Warning - ' + $ 
+	  "Allowed Logical Column byte values are byte('T'), byte('F'), or 0b"
+	   endif
+	endif else if (type_ele EQ 7) then begin   	  	 
+           gg =  (input.(i) eq 'T') or (input.(i) eq 'F')
+	       if ~array_equal(gg,1b) then begin
+	       islogical[i] = 0b
+	       message,/CON, 'Warning - ' + $ 
+	  'Allowed Logical column string values are "T" and "F"'
+	   endif
+	endif else begin 
+	    message,/CON, $
+	    'Warning - Logical Columns must be of type string or byte'
+	    islogical[i] = 0b
+	 endelse   	  
+       endif               
+       dims[i] = nelem
+       
+        if (sz[0] lt 1) || (sz[0] eq 1 && type_ele ne 7) then begin
+           tdims[i] = ''
+       endif else begin
+           tdims[i] = '('
+           
+           if type_ele eq 7 then begin
+               tdims[i] += strcompress(string(maxstr), /remo) + ','
+           endif
+           
+           for j=1, sz[0] do begin
+               tdims[i] += strcompress(sz[j])
+               if j ne sz[0] then tdims[i] += ','
+           endfor
+           
+           tdims[i] +=  ')'
+       endelse
+             
+       case type_ele of
+          1:        begin
+                     types[i] = 'B'
+                     nbyte += nelem
+              end
+          2:       begin
+                         types[i] = 'I'
+                     nbyte += 2*nelem
+              end
+          3:       begin
+                     types[i] = 'J'
+                     nbyte += 4*nelem
+              end
+          4:       begin
+                        types[i] = 'E'
+                     nbyte += 4*nelem
+               end
+          5:       begin
+                     types[i] = 'D'
+                     nbyte += 8*nelem
+              end
+          6:       begin
+                        types[i] = 'C'
+                     nbyte += 8*nelem
+              end
+          7:       begin
+                     maxstr = max(strlen(input.(i)) > 1 )
+                     types[i] = 'A'
+                     nbyte += maxstr*nelem
+                     dims[i] = maxstr*nelem		    
+              end
+          9:   begin
+                       types[i] = 'M'
+                     nbyte += 16*nelem
+              end
+
+         10:   begin
+                       if ~mwr_validptr(vtypes, nfld, i, input.(i)) then begin
+                         return
+                     endif
+                     
+                       types[i] = 'P'+vtypes[i].type
+                     nbyte += 8
+                     dims[i] = 1
+
+                     test = mwr_unsigned_offset(vtypes[i].itype)
+                     if test gt 0 then begin
+                         if (n_elements(offsets) lt 1) then begin
+                             offsets = ulon64arr(nfld)
+                         endif
+                         offsets[i] = test
+                     endif
+                     
+               end
+
+         12:   begin
+                      types[i] = 'I'
+                     if (n_elements(offsets) lt 1) then begin
+                         offsets = ulon64arr(nfld)
+                     endif
+                     offsets[i] = mwr_unsigned_offset(12);
+                     nbyte += 2*nelem
+              end
+
+         13:   begin
+                      types[i] = 'J'
+                     if (n_elements(offsets) lt 1) then begin
+                         offsets = ulon64arr(nfld)
+                     endif
+                     offsets[i] = mwr_unsigned_offset(13);
+                     nbyte += 4*nelem
+              end
+              
+                ; 8 byte integers became standard FITS in December 2005
+         14:   begin
+                     types[i] = 'K'
+                     nbyte += 8*nelem
+               end
+
+         15:   begin
+                      types[i] = 'K'
+                     nbyte += 8*nelem
+                     if (n_elements(offsets) lt 1) then begin
+                         offsets = ulon64arr(nfld)
+                     endif
+                     offsets[i] = mwr_unsigned_offset(15)
+               end
+                  
+          0:   begin
+                        print,'MWRFITS Error: Undefined structure element??'
+                     return
+              end
+              
+          8:   begin
+                        print, 'MWRFITS Error: Nested structures'
+                     return
+              end
+              
+          else:begin
+                        print, 'MWRFITS Error: Cannot parse structure'
+                     return
+              end
+       endcase
+    endfor
+
+    ; Put in the required FITS keywords.
+    chk_and_upd, header, 'XTENSION', 'BINTABLE', 'Binary table written by MWRFITS v'+mwr_version()
+    chk_and_upd, header, 'BITPIX', 8, 'Required value'
+    chk_and_upd, header, 'NAXIS', 2, 'Required value'
+    chk_and_upd, header, 'NAXIS1', nbyte, 'Number of bytes per row'
+    chk_and_upd, header, 'NAXIS2', n_elements(input), 'Number of rows'
+    chk_and_upd, header, 'PCOUNT', 0, 'Normally 0 (no varying arrays)'
+    chk_and_upd, header, 'GCOUNT', 1, 'Required value'
+    chk_and_upd, header, 'TFIELDS', nfld, 'Number of columns in table'
+
+    ;
+    ; Handle the special cases.
+    ;
+    g = where(islogical,Nlogic)
+    if Nlogic GT 0 then types[g] = 'L'
+       
+    if keyword_set(bit_cols) then begin
+       nb = n_elements(bit_cols)
+       if nb ne n_elements(nbit_cols) then begin
+           print,'WARNING: Bit_cols and Nbit_cols not same size'
+           print,'         No bit columns generated.'
+          goto, after_bits
+       endif
+       for i = 0, nb-1 do begin
+           nbyte = (nbit_cols[i]+7)/8
+           icol = bit_cols[i]
+           if types[icol-1] ne 'B'  || (dims[icol-1] ne nbyte) then begin
+              print,'WARNING: Invalid attempt to create bit column:',icol
+                    goto, next_bit
+           endif
+           types[icol-1] = 'X'
+           tdims[icol-1] = ''
+           dims[icol-1] = nbit_cols[i]
+  next_bit:
+       endfor
+  after_bits:
+    endif
+
+
+
+    ; Write scaling info as needed.
+    if n_elements(offsets) gt 0 then begin
+        w = where(offsets gt 0)
+
+        for i=0, n_elements(w) - 1 do begin
+            key = 'TSCAL'+strcompress(string(w[i])+1,/remo)
+           chk_and_upd, header, key, 1
+        endfor
+    
+        for i=0, n_elements(w) - 1 do begin
+           key = 'TZERO'+strcompress(string(w[i]+1),/remo)
+           chk_and_upd, header, key, offsets[w[i]]
+        endfor
+    
+        if ~keyword_set(no_comment) then begin
+            key = 'TSCAL'+strcompress(string(w[0])+1,/remo)
+	   sxaddhist,[' ',' *** Unsigned integer column scalings *',' '], $
+	        header,/COMMENT,location = key
+    endif
+    endif
+  
+    ; Now add in the TFORM keywords
+    for i=0, nfld-1 do begin
+       if dims[i] eq 1 then begin
+           form = types[i]
+       endif else begin
+           form=strcompress(string(dims[i]),/remove) + types[i]
+        endelse
+       
+       tfld = 'TFORM'+strcompress(string(i+1),/remove)
+       
+       ; Check to see if there is an existing value for this keyword.
+       ; If it has the proper value we will not modify it.
+       ; This can matter if there is optional information coded
+       ; beyond required TFORM information.
+              
+       oval = fxpar(header, tfld)
+       oval = strcompress(string(oval),/remove_all)
+       if (oval eq '0')  ||  (strmid(oval, 0, strlen(form)) ne form) then begin
+           chk_and_upd, header, tfld, form
+       endif
+    endfor
+
+    if ~keyword_set(no_comment) then $
+        sxaddhist,[' ',' *** Column formats ***',' '],header, $
+	    /COMMENT, location='TFORM1'
+ 
+    ; Now write TDIM info as needed.
+    for i=nfld-1, 0,-1 do begin
+        if tdims[i] ne '' then begin
+            fxaddpar, header, 'TDIM'+strcompress(string(i+1),/remo), tdims[i],after=tfld
+        endif
+    endfor
+
+    w=where(tdims ne '',N_tdims)
+    if (N_tdims GT 0) && ~keyword_set(no_comment) then begin
+        fxaddpar, header, 'COMMENT', ' ', after=tfld
+        fxaddpar, header, 'COMMENT', ' *** Column dimensions (2 D or greater) ***', after=tfld
+        fxaddpar, header, 'COMMENT', ' ', after=tfld
+    endif
+
+    for i=0, nfld-1 do begin
+        if tdims[i] ne '' then begin
+            chk_and_upd, header, 'TDIM'+strcompress(string(i+1),/remo), tdims[i]
+        endif
+    endfor
+
+    if n_elements(vtypes) gt 0 then begin
+        fxaddpar, header, 'THEAP', nbyte*n_elements(input), 'Offset of start of heap'
+        offset = 0L
+        for i=0,n_elements(vtypes)-1 do begin
+           if vtypes[i].status then offset = offset + vtypes[i].offset*vtypes[i].ilen
+        endfor
+        fxaddpar, header, 'PCOUNT', offset, 'Size of heap'
+    endif
+
+    ;
+    ; Last add in the TTYPE keywords if desired.
+    ;
+    if ~no_types then begin
+       for i=0, nfld - 1 do begin
+           key = 'TTYPE'+strcompress(string(i+1),/remove)
+           if ~keyword_set(use_colnum) then begin
+               value= mwr_checktype(tags[i],alias=alias)
+           endif else begin
+               value = 'C'+strmid(key,5,2) + ' '
+           endelse
+          chk_and_upd, header, key, value, /nological
+       endfor
+       
+        if ~keyword_set(no_comment) then $
+	   sxaddhist,[' ',' *** Column names *** ',' '],header,/comment, $
+	        location = 'TTYPE1'
+     endif
+
+    if ~keyword_set(no_comment) then begin
+        fxaddpar, header, 'COMMENT', ' ', after='TFIELDS'
+        fxaddpar, header, 'COMMENT', ' *** End of mandatory fields ***', after='TFIELDS'
+        fxaddpar, header, 'COMMENT', ' ', after='TFIELDS'
+    endif
+
+    ; Write to the output device.
+    mwr_header, lun, header
+
+end
+
+; Modify the structure to put the pointer column in.
+function mwr_retable, input, vtypes
+
+     compile_opt idl2,hidden
+
+    offset = 0L
+    tags = tag_names(input);
+;Create an output structure identical to the input structure but with pointers replaced
+; by a 2 word lonarr to point to the heap area
+
+      if vtypes[0].status then begin
+        output = CREATE_STRUCT(tags[0],lonarr(2))
+      endif else begin
+         output = CREATE_STRUCT(tags[0],input[0].(0))
+      endelse
+      for i=1, n_elements(tags) -1 do begin
+         if vtypes[i].status then begin
+           output = CREATE_STRUCT(temporary(output), tags[i], lonarr(2))
+         endif else begin
+           output = CREATE_STRUCT(temporary(output), tags[i], input[0].(i))
+         endelse
+      endfor
+      output = replicate(temporary(output), N_elements(input) )
+      struct_assign, input, output      ;Available since V5.1
+
+    for i=0, n_elements(tags)-1 do begin
+       if vtypes[i].status then begin
+           for j=0, n_elements(input)-1 do begin
+              ptr = input[j].(i)
+              if ptr_valid(ptr) then begin
+                  sz = size(*ptr)
+                  if sz[0] eq 0 then xsz = 1 else xsz= sz[1]
+
+                  output[j].(i)[0] = xsz
+                  output[j].(i)[1] = offset
+                  
+                  offset = offset + vtypes[i].ilen*xsz
+              endif
+           endfor
+       endif
+    endfor
+    return,output
+end
+
+; Write the heap data.
+function mwr_writeheap, lun, vtypes
+
+    offset = 0L
+    
+    for i=0, n_elements(vtypes)-1 do begin
+       if vtypes[i].status then begin
+           
+           itype = vtypes[i].itype
+           unsigned = mwr_unsigned_offset(itype)
+           
+           ptrs = vtypes[i].data
+           
+           for j=0,n_elements(ptrs)-1 do begin
+              if ptr_valid(ptrs[j]) then begin
+                  if (unsigned gt 0) then begin
+                     *ptrs[j] = *ptrs[j] + unsigned
+                  endif
+
+                      writeu, lun, *ptrs[j]
+                  
+                  sz = size(*ptrs[j])
+                  xsz = 1 > sz[1]
+                  offset = offset + xsz * vtypes[i].ilen
+              endif
+           endfor
+       endif
+    endfor
+
+    return, offset
+    
+end
+
+; Write the binary table.
+pro mwr_tabledat, lun, input, header, vtypes
+     compile_opt idl2,hidden
+    ;
+    ; file              -- unit to which data is to be written.
+    ; Input              -- IDL structure
+    ; Header       -- Filled header
+
+    nfld = n_tags(input)
+
+    ; Any special processing?
+
+    typ = intarr(nfld)
+    for i=0, nfld-1 do begin
+        
+        typ[i] = size(input.(i),/type)
+	    if (typ[i] eq 7) then begin
+
+             dim = size(input.(i),/dimen) >1
+             siz = max(strlen(input.(i))) > 1
+	     input.(i) = $
+	        strmid( input.(i) + string(replicate(32b, siz)), 0, siz)
+
+       endif
+ 
+       unsigned = mwr_unsigned_offset(typ[i])
+       if (unsigned gt 0) then begin
+           input.(i) = input.(i) + unsigned
+       endif
+       
+    endfor
+
+    if n_elements(vtypes) gt 0 then begin
+          
+      
+        input = mwr_retable(input, vtypes)
+    endif
+
+    ; Write the data segment.
+    ;
+    writeu, lun, input
+
+    nbyte = long64(fxpar(header, 'NAXIS1'))
+    nrow  = n_elements(input)
+
+    heap = 0
+    if n_elements(vtypes) gt 0 then $
+        heap = mwr_writeheap(lun, vtypes)
+
+    siz   = nbyte*nrow + heap
+    padding = 2880 - (siz mod 2880)
+    if padding eq 2880 then padding = 0
+
+    ;
+    ; If necessary write the padding.
+    ;
+    if padding gt 0 then begin
+        pad = bytarr(padding)  ; Should be null-filled by default.
+        writeu, lun, pad
+    endif
+
+end
+
+
+; Scale parameters for GROUPed data.
+pro mwr_pscale, grp, header, pscale=pscale, pzero=pzero
+     compile_opt idl2,hidden
+    
+
+; This function assumes group is a 2-d array.
+
+    if ~keyword_set(pscale) && ~keyword_set(pzero) then return
+
+    if ~keyword_set(pscale) then begin
+        pscale = dblarr(sizg[1])
+        pscale[*] = 1.
+    endif
+
+    w = where(pzero eq 0.d0)
+
+    if w[0] ne 0 then begin
+        print, 'MWRFITS  Warning: PSCALE value of 0 found, set to 1.'
+        pscale[w] = 1.d0
+    endif
+
+    if keyword_set(pscale) then begin
+        for i=0L, sizg[1]-1 do begin
+            key= 'PSCAL' + strcompress(string(i+1),/remo)
+            chk_and_upd, header, key, pscale[i]
+        endfor
+    endif
+
+    if ~keyword_set(pzero) then begin
+        pzero = dblarr(sizg[1])
+        pzero[*] = 0.
+    endif else begin
+        for i=0L, sizg[1]-1 do begin
+            key= 'PZERO' + strcompress(string(i+1),/remo)
+            chk_and_upd, header, key, pscale[i]
+        endfor
+    endelse
+
+    for i=0L, sizg[1]-1 do begin
+        grp[i,*] = grp[i,*]/pscale[i] - pzero[i]
+    endfor
+
+end
+
+
+; Find the appropriate scaling parameters.
+pro mwr_findscale, flag, array, nbits, scale, offset, error
+
+     compile_opt idl2,hidden
+
+    error = 0
+    if n_elements(flag) eq 2 then begin
+         scale  = double(flag[0])
+        offset = double(flag[1])
+    endif else if n_elements(flag) eq 1 and flag[0] ne 1 then begin
+         minmum = min(array, max=maxmum)
+        offset = 0.d0
+        scale  = double(flag[0])
+    endif else if n_elements(flag) ne 1 then begin
+         print, 'MWRFITS Error: Invalid scaling parameters.'
+        error  = 1
+        return
+    endif else begin
+        
+         minmum = min(array, max=maxmum)
+        scale  = (maxmum-minmum)/(2.d0^nbits)
+        amin   = -(2.d0^(nbits-1))
+        if (amin gt -130) then amin = 0  ; looking for -128
+        offset = minmum - scale*amin
+        
+    endelse
+    return
+end
+
+; Scale and possibly convert array according to information
+; in flags.
+pro mwr_scale, array, scale, offset, lscale=lscale, iscale=iscale,  $
+   bscale=bscale, null=null
+
+     compile_opt idl2,hidden
+
+    ; First deallocate scale and offset
+    if n_elements(scale)  gt 0 then xx = temporary(scale)
+    if n_elements(offset) gt 0 then xx = temporary(offset)
+
+    if ~keyword_set(lscale) && ~keyword_set(iscale) &&  $
+       ~keyword_set(bscale) then return
+
+    siz = size(array)
+    if keyword_set(lscale) then begin
+
+        ; Doesn't make sense to scale data that can be stored exactly.
+        if siz[siz[0]+1] lt 4 then return
+        amin = -2.d0^31
+        amax = -(amin + 1)
+    
+        mwr_findscale, lscale, array, 32, scale, offset, error
+
+    endif else if keyword_set(iscale) then begin
+        if siz[siz[0]+1] lt 3 then return
+        amin = -2.d0^15
+        amax = -(amin + 1)
+    
+        mwr_findscale, iscale, array, 16, scale, offset, error
+
+    endif else begin
+        if siz[siz[0]+1] lt 2 then return
+    
+        amin = 0
+        amax = 255
+    
+        mwr_findscale, bscale, array, 8, scale, offset, error
+    endelse
+
+    ; Check that there was no error in mwr_findscale
+    if error gt 0 then return
+
+    if scale le 0.d0 then begin
+        print, 'MWRFITS Error: BSCALE/TSCAL=0'
+        return
+    endif
+
+    array = round((array-offset)/scale)
+
+    w = where(array gt amax)
+    if w[0] ne -1 then $
+        array[w] = keyword_set(null) ? null : amax
+ 
+    w = where(array lt amin)
+    if w[0] ne -1 then $
+        array[w] = keyword_set(null) ? null : amin
+ 
+    if keyword_set(lscale) then      array = long(array) $
+    else if keyword_set(iscale) then array = fix(array)  $
+    else                             array = byte(array)
+    
+end
+
+; Write a header
+pro mwr_header, lun, header
+
+     compile_opt idl2,hidden
+    ; Fill strings to at least 80 characters and then truncate.
+
+    space = string(replicate(32b, 80))
+    header = strmid(header+space, 0, 80)
+
+    w = where(strcmp(header,"END     ",8), Nw)
+
+    if Nw eq 0 then begin
+
+       header = [header, strmid("END"+space,0,80)]
+       
+    endif else begin
+        if (Nw gt 1) then begin 
+           ; Get rid of extra end keywords;
+           print,"MWRFITS Warning: multiple END keywords found."
+           for irec=0L, n_elements(w)-2 do begin
+              header[w[irec]] = strmid('COMMENT INVALID END REPLACED'+  $
+                space, 0, 80)
+           endfor
+       endif
+
+       ; Truncate header array at END keyword.
+       header = header[0:w[n_elements(w)-1]]
+    endelse
+
+    nrec = n_elements(header)
+    if nrec mod 36 ne 0 then header = [header, replicate(space,36 - nrec mod 36)]
+
+    writeu, lun, byte(header)
+end
+
+
+; Move the group information within the data.
+pro mwr_groupinfix, data, group, hdr
+     compile_opt idl2,hidden
+
+    siz = size(data)
+    sizg = size(group)
+
+    ; Check if group info is same type as data 
+
+    if siz[siz[0]+1] ne sizg[3] then begin
+        case siz[siz[0]+1] of
+         1: begin
+               mwr_groupscale, 127.d0, group, hdr
+               group = byte(group)
+           end
+         2: begin
+               mwr_groupscale, 32767.d0, group, hdr
+               group = fix(group)
+           end
+         3: begin
+               mwr_groupscale, 2147483647.d0, group, hdr
+               group = long(group)
+           end
+         4: group = float(group)
+         5: group = double(group)
+      else: begin
+                print,'MWRFITS Internal error: Conversion of group data'
+               return
+            end
+        endcase
+    endif
+
+    nrow = 1
+    for i=1, siz[0]-1 do begin
+        nrow = nrow*siz[i]
+    endfor
+
+    data = reform(data, siz[siz[0]+2])
+    for i=0L, siz[siz[0]] - 1 do begin
+        if i eq 0 then begin
+            gdata = group[*,0]
+           gdata = reform(gdata)
+            tdata = [ gdata , data[0:nrow-1]]
+        endif else begin
+            start = nrow*i
+           fin = start+nrow-1
+           gdata = group[*,i]
+            tdata = [tdata, gdata ,data[start:fin]]
+       endelse
+    endfor
+
+    data = temporary(tdata)
+end
+
+; If an array is being scaled to integer type, then
+; check to see if the group parameters will exceed the maximum
+; values allowed.  If so scale them and update the header.
+pro mwr_groupscale, maxval, group, hdr
+     compile_opt idl2,hidden
+
+    sz = size(group)
+    for i=0L, sz[1]-1 do begin
+         pmax = max(abs(group[i,*]))
+         if (pmax gt maxval) then begin
+             ratio = pmax/maxval
+            psc = 'PSCAL'+strcompress(string(i+1),/remo)
+            currat = fxpar(hdr, psc)
+            if (currat ne 0) then begin
+                fxaddpar, hdr, psc, currat*ratio, 'Scaling overriden by MWRFITS'
+            endif else begin
+                fxaddpar, hdr, psc, ratio, ' Scaling added by MWRFITS'
+            endelse
+             group[i,*] = group[i,*]/ratio
+         endif
+    endfor
+end
+        
+        
+; Write out header and image for IMAGE extensions and primary arrays.
+pro mwr_image, input, siz, lun, bof, hdr,       $
+       null=null,                              $
+       group=group,                            $
+       pscale=pscale, pzero=pzero,             $
+       lscale=lscale, iscale=iscale,              $
+       bscale=bscale,                          $
+        no_comment=no_comment,                  $
+       silent=silent
+
+
+    compile_opt idl2,hidden
+    type = siz[siz[0] + 1]
+
+    bitpixes=[8,8,16,32,-32,-64,-32,0,0,-64,0,0,16,32,64,64]
+
+    ; Convert complexes to two element real array.
+
+    if type eq 6 || type eq 9 then begin
+ 
+        if ~keyword_set(silent) then begin
+            print, "MWRFITS Note: Complex numbers treated as arrays"
+        endif
+    
+        array_dimen=(2)
+        if siz[0] gt 0 then array_dimen=[array_dimen, siz[1:siz[0]]] 
+        if siz[siz[0]+1] eq 6 then data = float(input,0,array_dimen)  $
+        else data = double(input,0,array_dimen)
+
+    ; Convert strings to bytes.
+    endif else if type eq 7 then begin
+        data = input
+        len = max(strlen(input))
+        if len eq 0 then begin
+            print, 'MWRFITS Error: strings all have zero length'
+           return
+        endif
+
+        for i=0L, n_elements(input)-1 do begin
+            t = len - strlen(input[i])
+           if t gt 0 then input[i] = input[i] + string(replicate(32B, len))
+        endfor
+    
+        ; Note that byte operation works on strings in a special way
+        ; so we don't go through the subterfuge we tried above.
+    
+        data = byte(data)
+    
+    endif else if n_elements(input) gt 0 then data = input
+
+
+    ; Do any scaling of the data.
+    mwr_scale, data, scalval, offsetval, lscale=lscale, $
+      iscale=iscale, bscale=bscale, null=null
+
+    ; This may have changed the type.
+    siz  = size(data)
+    type = siz[siz[0]+1]
+
+
+    ; If grouped data scale the group parameters.
+    if keyword_set(group) then mwr_pscale, group, hdr, pscale=pscale, pzero=pzero
+
+    if bof then begin
+        chk_and_upd, hdr, 'SIMPLE', 'T','Primary Header created by MWRFITS v'+mwr_version()
+        chk_and_upd, hdr, 'BITPIX', bitpixes[type]
+        chk_and_upd, hdr, 'NAXIS', siz[0]
+        chk_and_upd, hdr, 'EXTEND', 'T', 'Extensions may be present'
+    endif else begin
+        chk_and_upd, hdr, 'XTENSION', 'IMAGE','Image Extension created by MWRFITS v'+mwr_version()
+        chk_and_upd, hdr, 'BITPIX', bitpixes[type]
+        chk_and_upd, hdr, 'NAXIS', siz[0]
+        chk_and_upd, hdr, 'PCOUNT', 0
+        chk_and_upd, hdr, 'GCOUNT', 1
+    endelse
+
+
+    if keyword_set(group) then begin
+        group_offset = 1
+    endif else group_offset = 0
+
+    if keyword_set(group) then begin
+       chk_and_upd, hdr, 'NAXIS1', 0
+    endif
+
+    for i=1L, siz[0]-group_offset do begin
+        chk_and_upd, hdr, 'NAXIS'+strcompress(string(i+group_offset),/remo), siz[i]
+    endfor
+
+
+    if keyword_set(group) then begin
+        chk_and_upd, hdr, 'GROUPS', 'T'
+        sizg = size(group)
+        if sizg[0] ne 2 then begin
+            print,'MWRFITS Error: Group data is not 2-d array'
+           return
+        endif
+        if sizg[2] ne siz[siz[0]] then begin
+            print,'MWRFITS Error: Group data has wrong number of rows'
+           return
+        endif
+        chk_and_upd,hdr,  'PCOUNT', sizg[1]
+        chk_and_upd, hdr, 'GCOUNT', siz[siz[0]]
+    endif
+    
+    if n_elements(scalval) gt 0 then begin
+    
+        chk_and_upd, hdr, 'BSCALE', scalval
+        chk_and_upd, hdr, 'BZERO', offsetval
+    
+    endif else begin
+       
+       ; Handle unsigned offsets
+       bzero = mwr_unsigned_offset(type)
+       if bzero gt 0 then begin
+           chk_and_upd,hdr,'BSCALE', 1
+           chk_and_upd, hdr, 'BZERO', bzero
+           data += bzero
+        endif
+       
+    endelse
+
+    if keyword_set(group) then begin
+        if keyword_set(pscale) then begin
+            if n_elements(pscale) ne sizg[1] then begin
+               print, 'MWRFITS Warning: wrong number of PSCALE values'
+           endif else begin
+                for i=1L, sizg[1] do begin
+                    chk_and_upd, hdr, 'PSCALE'+strcompress(string(i),/remo)
+               endfor
+           endelse
+        endif
+        if keyword_set(pzero) then begin
+            if n_elements(pscale) ne sizg[1] then begin
+               print, 'MWRFITS Warning: Wrong number of PSCALE values'
+           endif else begin
+                for i=1L, sizg[1] do begin
+                    chk_and_upd, hdr, 'PZERO'+strcompress(string(i),/remo)
+               endfor
+           endelse
+        endif
+    endif
+
+    bytpix=abs(bitpixes[siz[siz[0]+1]])/8             ; Number of bytes per pixel.
+    npixel = n_elements(data) + n_elements(group)     ; Number of pixels.
+
+    if keyword_set(group) then mwr_groupinfix, data, group, hdr
+
+    ; Write the FITS header
+    mwr_header, lun, hdr
+
+    ; This is all we need to do if input is undefined.
+    if (n_elements(input) eq 0) || (siz[0] eq 0) then return
+
+    ; Write the data.
+    writeu, lun, data
+
+    nbytes = long64(bytpix)*npixel
+    filler = 2880 - nbytes mod 2880
+    if filler eq 2880 then filler = 0
+  
+    ; Write any needed filler.
+    if filler gt 0 then writeu, lun, replicate(0B,filler)
+end
+
+
+; Main routine -- see documentation at start
+pro mwrfits, xinput, file, header,              $
+        ascii=ascii,                            $
+       separator=separator,                    $
+       terminator=terminator,                  $
+       create=create,                          $
+       null=null,                              $
+       group=group,                            $
+       pscale=pscale, pzero=pzero,             $
+       alias=alias,                            $
+       use_colnum = use_colnum,                $
+       lscale=lscale, iscale=iscale,              $
+       no_copy = no_copy,                      $
+       bscale=bscale,                          $
+       no_types=no_types,                      $
+       silent=silent,                          $
+       no_comment=no_comment,                  $
+       logical_cols=logical_cols,              $
+       bit_cols=bit_cols,                      $
+       nbit_cols=nbit_cols,                    $
+       status = status,                        $
+       version=version
+
+
+    ; Check required keywords.
+    compile_opt idl2
+    status = -1                     ;Status changes to 0 upon completion
+    if keyword_set(Version) then begin
+        print, "MWRFITS V"+mwr_version()+":  February 24, 2016"
+    endif
+
+    if n_elements(file) eq 0 then begin
+        if ~keyword_set(Version) then begin
+            print, 'MWRFITS: Usage:'
+            print, '    MWRFITS, struct_name, file, [header,] '
+            print, '             /CREATE, /SILENT, /NO_TYPES, /NO_COMMENT, '
+            print, '             GROUP=, PSCALE=, PZERO=,'
+            print, '             LSCALE=, ISCALE=, BSCALE=,'
+            print, '             LOGICAL_COLS=, BIT_COLS=, NBIT_COLS=,'
+            print, '             ASCII=, SEPARATOR=, TERMINATOR=, NULL='
+           print, '             /USE_COLNUM, ALIAS=, STATUS='
+        endif
+        return
+    endif
+
+    if size(xinput,/TNAME) EQ 'STRUCT' then $
+        if N_tags(xinput) GT 999 then begin
+        message,'ERROR - Input structure contains ' + strtrim(N_tags(xinput),2) + ' tags',/CON
+        message,'ERROR - FITS files are limited to 999 columns',/CON
+        return
+    endif     
+
+    ; Save the data into an array/structure that we can modify.
+ 
+    if n_elements(xinput) gt 0 then $
+        if keyword_set(no_copy) then input = temporary(xinput) $
+                                else input = xinput
+
+    on_ioerror, open_error
+
+    ; Open the input file.    If it exists, and the /CREATE keyword is not 
+    ; specified, then we append to to the existing file.
+     ;
+
+    if  ~keyword_set(create) && file_test(file) then begin
+        openu, lun, file, /get_lun, /append,/swap_if_little
+        if ~keyword_set(silent) then $
+	    message,/inf,'Appending FITS extension to file ' + file
+        bof = 0
+    endif else begin 
+        openw, lun, file, /get_lun, /swap_if_little
+         bof = 1
+    endelse 	 
+    on_ioerror, null
+
+
+    siz = size(input) 
+     if siz[siz[0]+1] ne 8 then begin
+
+        ; If input is not a structure then call image writing utilities.
+        mwr_image, input, siz, lun, bof, header,    $
+         null=null,                              $
+         group=group,                            $
+         pscale=pscale, pzero=pzero,             $
+         lscale=lscale, iscale=iscale,              $
+         bscale=bscale,                          $
+         no_comment=no_comment,                  $
+         silent=silent
+
+    endif else if keyword_set(ascii) then begin
+
+        if bof then mwr_dummy, lun
+        ; Create an ASCII table.
+	mwr_ascii, input, siz, lun, bof, header,     $
+         ascii=ascii,                             $
+         null=null,                               $
+         use_colnum = use_colnum,                 $
+         lscale=lscale, iscale=iscale,               $
+         bscale=bscale,                           $
+         no_types=no_types,                      $
+         separator=separator,                     $
+         terminator=terminator,                   $
+         no_comment=no_comment,                   $
+         alias=alias,                             $
+         silent=silent
+
+    endif else begin
+
+        if bof then mwr_dummy, lun
+
+        ; Create a binary table.
+        mwr_tablehdr, lun, input, header, vtypes,    $
+          no_types=no_types,                        $
+          logical_cols = logical_cols,                    $
+          bit_cols = bit_cols,                           $
+          nbit_cols= nbit_cols,                     $
+          alias=alias,                              $
+          no_comment=no_comment,                    $
+	  silent=silent,                             $
+	  use_colnum = use_colnum
+       
+        mwr_tabledat, lun, input, header, vtypes
+
+    endelse
+
+    free_lun, lun
+    status=0
+    return
+    
+    ; Handle error in opening file.
+  open_error:
+    on_ioerror, null
+    print, 'MWRFITS Error: Cannot open output: ', file
+	 print,!ERROR_STATE.SYS_MSG
+    if n_elements(lun) gt 0 then free_lun, lun
+    
+    return
+end
diff --git a/Code/script_idl_mv/astrolib/n_bytes.pro b/Code/script_idl_mv/astrolib/n_bytes.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4e73c561a1f0e95b3cae2122ca94edd63178b404
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/n_bytes.pro
@@ -0,0 +1,52 @@
+function N_bytes,a
+;+
+; NAME:
+;       N_bytes()
+;
+; PURPOSE:
+;       To return the total number of bytes in data element
+;
+; CALLING SEQUENCE:
+;       result = N_bytes(a)
+;
+; INPUTS:
+;       a - any idl data element, scalar or array
+;
+; OUTPUTS:
+;       total number of bytes in a is returned as the function value
+;       (64bit longword scalar)
+; NOTES:
+;       (1) Not valid for object or pointer data types
+;       (2) For a string array, the number of bytes is computed after conversion
+;           with the BYTE() function, i.e. each element has the same length,
+;           equal to the maximum individual string length.
+;
+; MODIFICATION HISTORY:
+;       Version 1  By D. Lindler  Oct. 1986
+;       Include new IDL data types    W. Landsman          June 2001
+;       Now return a 64bit integer    W. Landsman          April 2006
+;-
+;-----------------------------------------------------
+;
+ dtype = size(a,/type)                      ;data type
+ if dtype EQ 0 then return,0            ;undefined
+ nel = N_elements(a)
+ case dtype of
+        1: nb = 1                            ;Byte
+        2: nb = 2                            ;Integer*2
+        3: nb = 4                            ;Integer*4
+        4: nb = 4                            ;Real*4
+        5: nb = 8                            ;Real*8
+        6: nb = 8                            ;Complex
+        7: nb = max(strlen(a))               ;String                      
+        8: nb = N_tags(a,/length)            ;Structure
+        9: nb = 16                           ;Double Complex
+       12: nb = 2                            ;Unsigned Integer*2
+       13: nb = 4                            ;Unsigned Integer*4
+       14: nb = 8                            ;64 bit integer
+       15: nb = 8                            ;Unsigned 64 bit integer
+     else: message,'ERROR - Object or Pointer data types not valid'
+ endcase
+
+ return,long64(nel)*nb
+ end
diff --git a/Code/script_idl_mv/astrolib/ngp.pro b/Code/script_idl_mv/astrolib/ngp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..301ec63f35197310df1fb7c2015ffb878bd0f237
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ngp.pro
@@ -0,0 +1,201 @@
+FUNCTION ngp,value,posx,nx,posy,ny,posz,nz, $
+             AVERAGE=average,WRAPAROUND=wraparound,NO_MESSAGE=no_message
+;+
+; NAME:
+;       NGP
+;
+; PURPOSE:
+;       Interpolate an irregularly sampled field using Nearest Grid Point
+;
+; EXPLANATION:
+;       This function interpolates irregularly gridded points to a
+;       regular grid using Nearest Grid Point.
+;
+; CATEGORY:
+;       Mathematical functions, Interpolation
+;
+; CALLING SEQUENCE:
+;       Result = NGP, VALUE, POSX, NX[, POSY, NY, POSZ, NZ, 
+;                     /AVERAGE, /WRAPAROUND, /NO_MESSAGE]
+;
+; INPUTS:
+;       VALUE: Array of sample weights (field values). For e.g. a
+;              temperature field this would be the temperature and the
+;              keyword AVERAGE should be set. For e.g. a density field
+;              this could be either the particle mass (AVERAGE should
+;              not be set) or the density (AVERAGE should be set).
+;       POSX:  Array of X coordinates of field samples, unit indices: [0,NX>.
+;       NX:    Desired number of grid points in X-direction.
+;       
+; OPTIONAL INPUTS:
+;      POSY: Array of Y coordinates of field samples, unit indices: [0,NY>.
+;      NY:   Desired number of grid points in Y-direction.
+;      POSZ: Array of Z coordinates of field samples, unit indices: [0,NZ>.
+;      NZ:   Desired number of grid points in Z-direction.
+;
+; KEYWORD PARAMETERS:
+;       AVERAGE:    Set this keyword if the nodes contain field samples
+;                   (e.g. a temperature field). The value at each grid
+;                   point will then be the average of all the samples
+;                   allocated to it. If this keyword is not set, the
+;                   value at each grid point will be the sum of all the
+;                   nodes allocated to it (e.g. for a density field from
+;                   a distribution of particles). (D=0). 
+;       WRAPAROUND: Set this keyword if the data is periodic and if you
+;                   want the first grid point to contain samples of both
+;                   sides of the volume (see below). (D=0).
+;       NO_MESSAGE: Suppress informational messages.
+;
+; Example of default NGP allocation: n0=4, *=gridpoint.
+;
+;     0   1   2   3     Index of gridpoints
+;     *   *   *   *     Grid points
+;   |---|---|---|---|   Range allocated to gridpoints ([0.0,1.0> --> 0, etc.)
+;   0   1   2   3   4   posx
+;
+; Example of NGP allocation for WRAPAROUND: n0=4, *=gridpoint.
+;
+;   0   1   2   3         Index of gridpoints
+;   *   *   *   *         Grid points
+; |---|---|---|---|--     Range allocated to gridpoints ([0.5,1.5> --> 1, etc.)
+;   0   1   2   3   4=0   posx
+;
+;
+; OUTPUTS:
+;       Prints that a NGP interpolation is being performed of x
+;       samples to y grid points, unless NO_MESSAGE is set. 
+;
+; RESTRICTIONS:
+;       All input arrays must have the same dimensions.
+;       Position coordinates should be in `index units' of the
+;       desired grid: POSX=[0,NX>, etc.
+;
+; PROCEDURE:
+;       Nearest grid point is determined for each sample.
+;       Samples are allocated to nearest grid points.
+;       Grid point values are computed (sum or average of samples).
+;
+; EXAMPLE:
+;       nx = 20
+;       ny = 10
+;       posx = randomu(s,1000)
+;       posy = randomu(s,1000)
+;       value = posx^2+posy^2
+;       field = ngp(value,posx*nx,nx,posy*ny,ny,/average)
+;       surface,field,/lego
+;
+; NOTES:
+;       Use tsc.pro or cic.pro for a higher order interpolation schemes.    A 
+;       standard reference for these interpolation methods is:   R.W. Hockney 
+;       and J.W. Eastwood, Computer Simulations Using Particles (New York: 
+;       McGraw-Hill, 1981).
+; MODIFICATION HISTORY:
+;       Written by Joop Schaye, Feb 1999.
+;       Check for LONG overflow  P. Riley/W. Landsman   December 1999
+;-
+
+nrsamples=n_elements(value)
+nparams=n_params()
+dim=(nparams-1)/2
+
+IF dim LE 2 THEN BEGIN
+    nz=1
+    IF dim EQ 1 THEN ny=1
+ENDIF
+nxny = long(nx)*long(ny)
+
+
+;---------------------
+; Some error handling.
+;---------------------
+
+on_error,2  ; Return to caller if an error occurs.
+
+IF NOT (nparams EQ 3 OR nparams EQ 5 OR nparams EQ 7) THEN BEGIN
+    message,'Incorrect number of arguments!',/continue
+    message,'Syntax: NGP, VALUE, POSX, NX[, POSY, NY, POSZ, NZ,' + $
+      ' /AVERAGE, /WRAPAROUND, /NO_MESSAGE]'
+ENDIF 
+
+IF (nrsamples NE n_elements(posx)) OR $
+  (dim GE 2 AND nrsamples NE n_elements(posy)) OR $
+  (dim EQ 3 AND nrsamples NE n_elements(posz)) THEN $
+  message,'Input arrays must have the same dimensions!'
+
+IF NOT keyword_set(no_message) THEN $
+  print,'Interpolating ' + strtrim(string(nrsamples,format='(i10)'),1) $
+  + ' samples to ' + strtrim(string(nxny*nz,format='(i10)'),1) + $
+  ' grid points using NGP...'
+
+
+;-----------------------------
+; Compute nearest grid points.
+;-----------------------------
+
+IF keyword_set(wraparound) THEN BEGIN
+    ; Coordinates of nearest grid point (ngp).
+    ngx=fix(posx+0.5)
+    ; Periodic boundary conditions.
+    bad=where(ngx EQ nx,count)
+    IF count NE 0 THEN ngx[bad]=0
+    IF dim GE 2 THEN BEGIN 
+        ngy=fix(posy+0.5)
+        bad=where(ngy EQ ny,count)
+        IF count NE 0 THEN ngy[bad]=0
+        IF dim EQ 3 THEN BEGIN
+            ngz=fix(posz+0.5)
+            bad=where(ngz EQ nz,count)
+            IF count NE 0 THEN ngz[bad]=0
+        ENDIF
+    ENDIF 
+    bad=0  ; Free memory.
+ENDIF ELSE BEGIN
+    ; Coordinates of nearest grid point (ngp).
+    ngx=fix(posx)
+    IF dim GE 2 THEN BEGIN  
+        ngy=fix(posy)
+        IF dim EQ 3 THEN ngz=fix(posz)
+    ENDIF
+ENDELSE
+
+; Indices of grid points to which samples are assigned.
+CASE dim OF
+    1: index=temporary(ngx)
+    2: index=temporary(ngx)+temporary(ngy)*nx
+    3: index=temporary(ngx)+temporary(ngy)*nx+temporary(ngz)*nxny
+ENDCASE
+
+
+;-------------------------------
+; Interpolate samples to grid.
+;-------------------------------
+
+field=fltarr(nx,ny,nz)
+
+FOR i=0l,nrsamples-1l DO field[index[i]]=field[index[i]]+value[i]
+
+
+;--------------------------
+; Compute weighted average.
+;--------------------------
+
+IF keyword_set(average) THEN BEGIN
+    ; Number of samples per grid point.
+    frequency=histogram(temporary(index),min=0,max=nxny*nz-1l)
+    
+    ; Normalize.
+    good=where(frequency NE 0,nrgood)
+    field[good]=temporary(field[good])/temporary(frequency[good])
+ENDIF
+
+return,field
+
+END  ; End of function ngp.
+
+
+
+
+
+
+
+
diff --git a/Code/script_idl_mv/astrolib/nint.pro b/Code/script_idl_mv/astrolib/nint.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3b54e2f49c7fbdb006768c6bf5a80c2bc13fee36
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/nint.pro
@@ -0,0 +1,55 @@
+function nint, x, LONG = long             ;Nearest Integer Function
+;+
+; NAME:
+;	NINT
+; PURPOSE:
+;	Nearest integer function.
+; EXPLANATION:   
+;	NINT() is similar to the intrinsic ROUND function, with the following
+;	two differences:
+;	(1) if no absolute value exceeds 32767, then the array is returned as
+;		as a type INTEGER instead of LONG
+;	(2) NINT will work on strings, e.g. print,nint(['3.4','-0.9']) will
+;		give [3,-1], whereas ROUND() gives an error message
+;
+; CALLING SEQUENCE:
+;	result = nint( x, [ /LONG] )
+;
+; INPUT:
+;	X - An IDL variable, scalar or vector, usually floating or double
+;		Unless the LONG keyword is set, X must be between -32767.5 and 
+;		32767.5 to avoid integer overflow
+;
+; OUTPUT
+;	RESULT - Nearest integer to X
+;
+; OPTIONAL KEYWORD INPUT:
+;	LONG - If this keyword is set and non-zero, then the result of NINT
+;		is of type LONG.   Otherwise, the result is of type LONG if
+;		any absolute values exceed 32767, and type INTEGER if all
+;		all absolute values are less than 32767.
+; EXAMPLE:
+;	If X = [-0.9,-0.1,0.1,0.9] then NINT(X) = [-1,0,0,1]
+;
+; PROCEDURE CALL:
+;	None:
+; REVISION HISTORY:
+;	Written W. Landsman        January 1989
+;	Added LONG keyword         November 1991
+;	Use ROUND if since V3.1.0  June 1993
+;	Always start with ROUND function    April 1995
+;	Return LONG values, if some input value exceed 32767
+;		and accept string values   February 1998 
+;       Use size(/TNAME) instead of DATATYPE()      October 2001
+;-
+ xmax = max(x,min=xmin)
+ xmax = abs(xmax) > abs(xmin)
+ if (xmax gt 32767) or keyword_set(long) then begin
+    if size(x,/TNAME) eq 'STRING' then b = round(float(x)) else b = round(x)
+ end else begin
+    if size(x,/TNAME) eq 'STRING' then b = fix(round(float(x))) else	$
+	    b = fix(round(x))
+ endelse
+
+  return, b 
+  end
diff --git a/Code/script_idl_mv/astrolib/nstar.pro b/Code/script_idl_mv/astrolib/nstar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9552aaf1187b43c09ca867146daaaaed80c1b0eb
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/nstar.pro
@@ -0,0 +1,485 @@
+pro nstar,image,id,xc,yc,mags,sky,group,phpadu,readns,psfname,DEBUG=debug, $  
+          errmag,iter,chisq,peak,PRINT=print,SILENT=silent, VARSKY = varsky
+;+
+; NAME:
+;       NSTAR
+; PURPOSE:
+;       Simultaneous point spread function fitting (adapted from DAOPHOT)
+; EXPLANATION:
+;       This PSF fitting algorithm is based on a very old (~1987) version of 
+;       DAOPHOT, and much better algorithms (e.g. ALLSTAR) are now available
+;       -- though not in IDL.
+;       
+; CALLING SEQUENCE:
+;       NSTAR, image, id, xc, yc, mags, sky, group, [ phpadu, readns, psfname,
+;               magerr, iter, chisq, peak, /PRINT , /SILENT, /VARSKY, /DEBUG ]
+;
+; INPUTS:
+;       image - image array
+;       id    - vector of stellar ID numbers given by FIND
+;       xc    - vector containing X position centroids of stars (e.g. as found
+;               by FIND)
+;       yc    - vector of Y position centroids
+;       mags  - vector of aperture magnitudes (e.g. as found by APER)
+;               If 9 or more parameters are supplied then, upon output
+;               ID,XC,YC, and MAGS will be modified to contain the new
+;               values of these parameters as determined by NSTAR.
+;               Note that the number of output stars may be less than 
+;               the number of input stars since stars may converge, or 
+;               "disappear" because they are too faint.
+;       sky   - vector of sky background values (e.g. as found by APER)
+;       group - vector containing group id's of stars as found by GROUP
+;
+; OPTIONAL INPUT:
+;       phpadu - numeric scalar giving number of photons per digital unit.  
+;               Needed for computing Poisson error statistics.   
+;       readns - readout noise per pixel, numeric scalar.   If not supplied, 
+;               NSTAR will try to read the values of READNS and PHPADU from
+;               the PSF header.  If still not found, user will be prompted.
+;       psfname - name of FITS image file containing the point spread
+;               function residuals as determined by GETPSF, scalar string.  
+;               If omitted, then NSTAR will prompt for this parameter.
+;
+; OPTIONAL OUTPUTS:
+;       MAGERR - vector of errors in the magnitudes found by NSTAR
+;       ITER - vector containing the number of iterations required for
+;               each output star.  
+;       CHISQ- vector containing the chi square of the PSF fit for each
+;               output star.
+;       PEAK - vector containing the difference of the mean residual of
+;               the pixels in the outer half of the fitting circle and
+;               the mean residual of pixels in the inner half of the
+;               fitting circle
+;
+; OPTIONAL KEYWORD INPUTS:
+;       /SILENT - if set and non-zero, then NSTAR will not display its results
+;               at the terminal
+;       /PRINT - if set and non-zero then NSTAR will also write its results to
+;               a file nstar.prt.   One also can specify the output file name
+;               by setting PRINT = 'filename'.
+;       /VARSKY - if this keyword is set and non-zero, then the sky level of
+;               each group is set as a free parameter.
+;       /DEBUG - if this keyword is set and non-zero, then the result of each
+;               fitting iteration will be displayed.
+;
+; PROCEDURES USED:
+;       DAO_VALUE(), READFITS(), REMOVE, SPEC_DIR(), STRN(), SXPAR()
+;
+; COMMON BLOCK:
+;       RINTER - contains pre-tabulated values for cubic interpolation
+; REVISION HISTORY
+;       W. Landsman                 ST Systems Co.       May, 1988
+;       Adapted for IDL Version 2, J. Isensee, September, 1990
+;       Minor fixes so that PRINT='filename' really prints to 'filename', and
+;       it really silent if SILENT is set.  J.Wm.Parker HSTX 1995-Oct-31
+;       Added /VARSKY option   W. Landsman   HSTX      May 1996
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Replace DATATYPE() with size(/TNAME)  W. Landsman November 2001
+;       Assume since V5.5, remove VMS calls W. Landsman September 2006
+;-
+ compile_opt idl2
+ common rinter,c1,c2,c3,init            ;Save time in RINTER()
+ npar = N_params()
+ if npar LT 7 then begin
+   print,'Syntax - NSTAR, image, id, xc, yc, mags, sky, group, [phpadu, '
+   print, $
+     '   [readns, psfname, magerr, iter, chisq, peak, /SILENT, /PRINT, /VARSKY]'
+   return
+ endif                                                        
+
+ if ( N_elements(psfname) EQ 0 ) then begin
+   psfname=''
+   read,'Enter name of FITS file containing PSF: ',psfname
+ endif else zparcheck,'PSFNAME',psfname,10,7,0,'PSF disk file name'
+
+ psf_file = file_search( psfname, COUNT = n)
+ if n EQ 0 then message, $
+      'ERROR - Unable to locate PSF file ' + spec_dir(psfname)
+
+ if npar LT 9 then begin                                                     
+   ans = ''
+   read, $
+     'Do you want to update the input vectors with the results of NSTAR? ',ans
+   if strmid(strupcase(ans),0,1) EQ 'Y' then npar = 9
+ endif
+
+ if npar LT 9 then $
+    message,'Input vectors ID,XC,YC and MAGS will not be updated by NSTAR',/INF
+
+; Read in the FITS file containing the PSF
+
+ s = size(image)
+ icol = s[1]-1 & irow = s[2]-1  ;Index of last row and column
+ psf = readfits(psfname, hpsf)
+ if  N_elements(phpadu) EQ 0 then begin 
+    par = sxpar(hpsf,'PHPADU', Count = N_phpadu)
+    if N_phpadu eq 0 $
+        then read, 'Enter photons per analog digital unit: ',phpadu $
+        else phpadu = par
+endif
+
+ if ( N_elements(readns) EQ 0 ) then begin 
+    par = sxpar(hpsf,'RONOIS', Count = N_ronois)
+    if N_ronois EQ 0 $
+        then read, 'Enter the readout noise per pixel: ',readns $
+        else readns = par
+ endif
+
+ gauss = sxpar(hpsf,'GAUSS*')
+ psfmag = sxpar(hpsf,'PSFMAG')
+ psfrad = sxpar(hpsf,'PSFRAD')
+ fitrad = sxpar(hpsf,'FITRAD')
+ npsf = sxpar(hpsf,'NAXIS1')
+;                               Compute RINTER common block arrays
+ p_1 = shift(psf,1,0) & p1 = shift(psf,-1,0) & p2 = shift(psf,-2,0)
+ c1 = 0.5*(p1 - p_1)
+ c2 = 2.*p1 + p_1 - 0.5*(5.*psf + p2)
+ c3 = 0.5*(3.*(psf-p1) + p2 - p_1)
+ init = 1
+
+ ronois = readns^2
+ radsq = fitrad^2   &  psfrsq = psfrad^2
+ sepmin = 2.773*(gauss[3]^2+gauss[4]^2)
+
+;      PKERR will be used to estimate the error due to interpolating PSF
+;      Factor of 0.027 is estimated from good-seeing CTIO frames
+
+ pkerr = 0.027/(gauss[3]*gauss[4])^2     
+ sharpnrm = 2.*gauss[3]*gauss[4]/gauss[0]
+ if (N_elements(group) EQ 1) then groupid = group[0] else $
+     groupid = where(histogram(group,min=0))    ;Vector of distinct group id's
+
+ mag = mags                        ;Save original magnitude vector
+ bad = where( mag GT 99, nbad )     ;Undefined magnitudes assigned 99.9
+ if nbad GT 0 then mag[bad] = psfmag + 7.5
+ mag = 10.^(-0.4*(mag-psfmag)) ;Convert magnitude to brightness, scaled to PSF
+ fmt = '(I6,2F9.2,3F9.3,I4,F9.2,F9.3)'
+
+ SILENT = keyword_set(SILENT)
+ VARSKY = keyword_set(VARSKY)
+
+ if keyword_set(PRINT) then begin
+     if ( size(print,/TNAME) NE 'STRING' ) then file = 'nstar.prt' $
+                                           else file = print
+    message,'Results will be written to a file '+ file,/INF
+     openw,lun,file,/GET_LUN
+     printf,lun,'NSTAR:    '+ getenv('USER') + ' '+ systime()
+     printf,lun,'PSF File:',psfname
+ endif 
+ PRINT = keyword_set(PRINT)
+
+ hdr='   ID      X       Y       MAG     MAGERR   SKY   NITER     CHI     SHARP'
+ if not(SILENT) then print,hdr
+ if PRINT then printf,lun,hdr
+
+ for igroup = 0, N_elements(groupid)-1 do begin
+
+ index = where(group EQ groupid[igroup],nstr) 
+ if not SILENT then print,'Processing group ', $
+               strtrim(groupid[igroup],2),'    ',strtrim(nstr,2),' stars'
+ if nstr EQ 0 then stop
+ magerr = fltarr(nstr)
+ chiold = 1.0
+ niter = 0
+ clip = 0b
+ nterm = nstr*3 + varsky
+ xold = dblarr(nterm)
+ clamp = replicate(1.,nterm)
+ xb = double(xc[index])  &   yb = double(yc[index])
+ magg = double(mag[index]) & skyg = double(sky[index])
+ idg = id[index]
+ skybar = total(skyg)/nstr
+ reset = 0b
+;
+START_IT : 
+   niter = niter+1
+RESTART: 
+ case 1 of              ;Set up critical error for star rejection
+   niter GE 4 : wcrit = 1
+   niter GE 8 : wcrit = 0.4444444
+   niter GE 12: wcrit = 0.25             
+   else       : wcrit = 400                                                   
+ endcase
+
+ if reset EQ 1b then begin
+     xb = xg + ixmin & yb = yg + iymin
+ endif
+
+ reset = 1b
+ xfitmin = fix(xb - fitrad) > 0
+ xfitmax = fix(xb + fitrad)+1 < (icol-1)
+ yfitmin = fix(yb - fitrad) > 0
+ yfitmax = fix(yb + fitrad)+1 < (irow-1)
+ nfitx = xfitmax - xfitmin + 1
+ nfity = yfitmax - yfitmin + 1
+ ixmin = min(xfitmin)& iymin = min(yfitmin)
+ ixmax = max(xfitmax)& iymax = max(yfitmax)
+ nx = ixmax-ixmin+1 & ny = iymax-iymin+1
+ dimage = image[ixmin:ixmax,iymin:iymax]
+ xfitmin = xfitmin -ixmin & yfitmin = yfitmin-iymin
+ xfitmax = xfitmax -ixmin & yfitmax = yfitmax-iymin
+;                                        Offset to the subarray
+ xg = xb-ixmin & yg = yb-iymin
+ j = 0
+
+ while (j LT nstr-1) do begin
+   sep = (xg[j] - xg[j+1:*])^2 + (yg[j] - yg[j+1:*])^2
+   bad = where(sep LT sepmin,nbad)
+   if nbad GT 0 then begin      ;Do any star overlap?
+      for l = 0,nbad-1 do begin
+      k = bad[l] + j + 1
+      if magg[k] LT magg[j] then imin = k else imin = j ;Identify fainter star
+      if ( sep[l] LT 0.14*sepmin) or  $
+         ( magerr[imin]/magg[imin]^2 GT wcrit ) then begin
+      if  imin EQ j then imerge = k else imerge = j
+      nstr = nstr - 1
+      if not SILENT then print, $
+       'Star ',strn(idg[imin]),' has merged with star ',strn(idg[imerge])
+      totmag = magg[imerge] + magg[imin]
+      xg[imerge] = (xg[imerge]*magg[imerge] + xg[imin]*magg[imin])/totmag
+      yg[imerge] = (yg[imerge]*magg[imerge] + yg[imin]*magg[imin])/totmag
+      magg[imerge] = totmag     
+      remove,imin,idg,xg,yg,magg,skyg,magerr    ;Remove fainter star from group
+      nterm = nstr*3 + varsky                   ;Update matrix size
+      xold = dblarr(nterm) 
+      clamp = replicate(1.,nterm)               ;Release all clamps
+      clip = 0b
+      niter = niter-1                           ;Back up iteration counter
+      goto, RESTART 
+      endif
+   endfor
+   endif
+   j = j+1
+ endwhile
+
+ xpsfmin = (fix (xg - psfrad+1)) > 0
+ xpsfmax = (fix (xg + psfrad  )) < (nx-1)
+ ypsfmin = (fix (yg - psfrad+1)) > 0
+ ypsfmax = (fix (yg + psfrad  )) < (ny-1)
+ npsfx = xpsfmax-xpsfmin+1 & npsfy = ypsfmax-ypsfmin+1
+ wt = fltarr(nx,ny)
+ mask = bytarr(nx,ny)
+ nterm = 3*nstr + varsky
+ chi = fltarr(nstr) & sumwt = chi & numer = chi & denom = chi
+ c = fltarr(nterm,nterm) & v = fltarr(nterm)
+
+ for j = 0,nstr-1 do begin   ;Mask of pixels within fitting radius of any star
+     x1 = xfitmin[j]  &  y1 = yfitmin[j]
+     x2 = xfitmax[j]  &  y2 = yfitmax[j]
+     rpixsq = fltarr(nfitx[j],nfity[j])
+     xfitgen2 = (findgen(nfitx[j]) + x1 - xg[j])^2
+     yfitgen2 = (findgen(nfity[j]) + y1 - yg[j])^2
+     for k=0,nfity[j]-1 do rpixsq[0,k] = xfitgen2 + yfitgen2[k]
+     temp = (rpixsq LE 0.999998*radsq)
+     mask[x1,y1] = mask[x1:x2,y1:y2] or temp
+     good = where(temp)
+     rsq = rpixsq[good]/radsq
+     temp1 = wt[x1:x2,y1:y2] 
+     temp1[good] = temp1[good] > (5./(5.+rsq/(1.-rsq)) )
+     wt[x1,y1] = temp1
+ endfor
+
+ igood = where(mask, ngoodpix)
+ x = dblarr(ngoodpix,nterm)
+ if varsky then x[0, nterm-1] = replicate(-1.0d, ngoodpix)
+
+ psfmask = bytarr(ngoodpix,nstr)
+ d = dimage[igood] - skybar
+ for j = 0,nstr-1 do begin ;Masks of pixels within PSF radius of each star
+     x1 = xpsfmin[j]   &    y1 = ypsfmin[j]
+     x2 = xpsfmax[j]   &    y2 = ypsfmax[j]
+     xgen = lindgen(npsfx[j]) + x1 - xg[j]
+     ygen = lindgen(npsfy[j]) + y1 - yg[j]
+     xgen2 = xgen^2 & ygen2 = ygen^2
+     rpxsq = fltarr( npsfx[j],npsfy[j] )
+     for k = 0,npsfy[j]-1 do rpxsq[0,k] = xgen2 + ygen2[k]
+     temp =  mask[x1:x2,y1:y2] and (rpxsq LT psfrsq)
+     temp1 = bytarr(nx,ny)
+     temp1[x1,y1] = temp 
+     goodfit = where(temp1[igood])
+     psfmask[goodfit+ngoodpix*j] = 1b
+     good = where(temp)
+     xgood = xgen[good mod npsfx[j]] & ygood = ygen[good/npsfx[j]]
+     model = dao_value(xgood,ygood,gauss,psf,dvdx,dvdy)
+     d[goodfit] = d[goodfit] - magg[j]*model
+     x[goodfit + 3*j*ngoodpix] = -model
+     x[goodfit + (3*j+1)*ngoodpix] = magg[j]*dvdx
+     x[goodfit + (3*j+2)*ngoodpix] = magg[j]*dvdy
+ endfor
+
+ wt = wt[igood] & idimage = dimage[igood]
+ dpos = (idimage-d) > 0
+ sigsq = dpos/phpadu + ronois + (0.0075*dpos)^2 + (pkerr*(dpos-skybar))^2
+
+ relerr = abs(d)/sqrt(sigsq)
+ if clip then begin   ;Reject pixels with 20 sigma errors (after 1st iteration)
+        bigpix = where(relerr GT 20.*chiold, nbigpix)
+        if ( nbigpix GT 0 ) then begin
+              keep = indgen(ngoodpix)
+              for i = 0,nbigpix-1 do keep = keep[ where( keep NE bigpix[i]) ]
+              wt= wt[keep] & d = d[keep] & idimage = idimage[keep] 
+              igood= igood[keep]  & relerr = relerr[keep]
+              psfmask = psfmask[keep,*]   &  x = x[keep,*]
+        endif
+ endif
+
+ sumres = total(relerr*wt)
+ grpwt = total(wt)
+
+ dpos = ((idimage-skybar) > 0) + skybar
+ sig = dpos/phpadu + ronois + (0.0075*dpos)^2 + (pkerr*(dpos-skybar))^2
+ for j = 0,nstr-1 do begin
+     goodfit = where(psfmask[*,j])
+     chi[j] = total(relerr[goodfit]*wt[goodfit])
+     sumwt[j] = total(wt[goodfit])
+     xgood = igood[goodfit] mod nx & ygood = igood[goodfit]/nx
+     rhosq = ((xg[j] - xgood)/gauss[3])^2  +  ((yg[j] - ygood)/gauss[4])^2
+     goodsig = where(rhosq LT 36)     ;Include in sharpness index only
+     rhosq = 0.5*rhosq[goodsig]       ;pixels within 6 sigma of centroid
+     dfdsig = exp(-rhosq)*(rhosq-1.)
+     sigpsf = sig[goodfit[goodsig]] & dsig = d[goodfit[goodsig]]
+     numer[j] = total(dfdsig*dsig/sigpsf)
+     denom[j] = total(dfdsig^2/sigpsf)
+ endfor
+
+ wt = wt/sigsq
+ if clip then $  ;After 1st iteration, reduce weight of a bad pixel
+   wt = wt/(1.+(0.4*relerr/chiold)^8) 
+
+ v = d * wt # x
+ c = transpose(x) # ( ( wt # replicate(1.,nterm) ) * x )
+
+ if grpwt GT 3 then begin
+        chiold = 1.2533*sumres*sqrt(1./(grpwt*(grpwt-3.)))
+        chiold = ((grpwt-3.)*chiold+3.)/grpwt
+ endif
+
+ i = where(sumwt GT 3, ngood)
+ if ngood GT 0 then begin 
+     chi[i] = 1.2533*chi[i]*sqrt(1./((sumwt[i]-3.)*sumwt[i]))
+     chi[i] = ((sumwt[i]-3.)*chi[i]+3.)/sumwt[i]
+ endif
+
+chibad = where(sumwt LE 3, ngood)
+if ngood GT 0 then chi[chibad] = chiold
+
+ c = invert(c)
+ x = c # transpose(v)
+
+ if (not clip) or (niter LE 1) then redo = 1b else redo = 0b 
+ if varsky then begin
+        skybar = skybar - x[nterm-1]
+        if abs(x[nterm-1]) GT  0.01 then redo = 1b
+ endif
+ clip = 1b
+
+ j = 3*indgen(nstr) & k = j+1 & l=j+2
+ sharp = sharpnrm*numer/(magg*denom)
+ if not redo then begin
+   redo = max(abs(x[j]) GT ( (0.05*chi*sqrt(c[j+nterm*j])) > 0.001*magg) )
+   if redo EQ 0 then redo = max( abs([x[k], x[l]]) GT 0.01)
+ endif
+
+ sgn = where( xold[j]*x[j]/magg^2 LT -1.E-37, Nclamp )  
+ if Nclamp GT 0 then clamp[j[sgn]] = 0.5*clamp[j[sgn]]
+ sgn = where( xold[k]*x[k]        LT -1.E-37, Nclamp )
+ if Nclamp GT 0 then clamp[k[sgn]] = 0.5*clamp[k[sgn]]
+ sgn = where( xold[l]*x[l]        LT -1.E-37, Nclamp )
+ if Nclamp GT 0 then clamp[l[sgn]] = 0.5*clamp[l[sgn]]
+
+ magg = magg-x[j] / (1.+ ( (x[j]/(0.84*magg)) > (-x[j]/(5.25*magg)) )/ clamp[j] )
+ xg = xg - x[k]   /(1.+abs(x[k])/( clamp[k]*0.5))
+ yg = yg - x[l]   /(1.+abs(x[l])/( clamp[l]*0.5))
+ xold = x
+
+ magerr = c[j+nterm*j]*(nstr*chi^2 + (nstr-1)*chiold^2)/(2.*nstr-1.)
+
+ dx = (-xg) > ( (xg - nx) > 0.) ;Find stars outside subarray
+ dy = (-yg) > ( (yg-  ny) > 0.)
+ badcen = where(    $                     ;Remove stars with bad centroids
+     (dx GT 0.001) or (dy GT 0.001) or ( (dx+1)^2 + (dy+1)^2 GE radsq ), nbad)
+ if nbad GT 0 then begin
+        nstr = nstr - nbad
+        print,strn(nbad),' stars eliminated by centroid criteria'
+        if nstr LE 0 then goto, DONE_GROUP 
+        remove, badcen, idg, xg, yg, magg, skyg, magerr
+        nterm = nstr*3 + varsky
+        redo = 1b
+ endif
+
+ faint = 1        
+ toofaint =  where (magg LE 1.e-5,nfaint) 
+                              ;Number of stars 12.5 mags fainter than PSF star
+ if nfaint GT 0 then begin        
+         faint = min( magg[toofaint], min_pos )
+         ifaint = toofaint[ min_pos ]
+         magg[toofaint] = 1.e-5
+         goto, REM_FAINT                ;Remove faintest star
+ endif else begin
+         faint = 0.
+         ifaint = -1
+         if (not redo) or (niter GE 4) then $
+            faint = max(magerr/magg^2, ifaint) else $ 
+            goto,START_IT 
+ endelse
+
+ if keyword_set(DEBUG) then begin 
+   err = 1.085736*sqrt(magerr)/magg
+    for i=0,nstr-1 do  print,format=fmt,idg[i],xg[i]+ixmin,yg[i]+iymin, $
+          psfmag-1.085736*alog(magg[i]),err[i],skyg[i],niter,chi[i],sharp[i]
+ endif
+
+ if redo and (niter LE 50) and (faint LT wcrit) then goto,START_IT   
+REM_FAINT: 
+ if (faint GE 0.25) or (nfaint GT 0) then begin
+         if not SILENT then $
+               message,'Star '+ strn(idg[ifaint]) + ' is too faint',/INF
+         nstr = nstr-1
+         if nstr LE 0 then goto,DONE_GROUP  
+         remove,ifaint,idg,xg,yg,magg,skyg,magerr
+         nterm = nstr*3 + varsky
+         xold = dblarr(nterm)
+         clamp = replicate(1.,nterm)
+         clip = 0b
+         niter = niter-1
+         goto,RESTART  
+ endif
+
+ err = 1.085736*sqrt(magerr)/magg
+ magg = psfmag - 1.085736*alog(magg)
+ sharp = sharp > (-99.999) < 99.999
+ xg = xg+ixmin & yg = yg+iymin
+
+; Print results to terminal and/or file
+
+ if not SILENT then for i = 0,nstr-1 do print,format=fmt, $
+     idg[i],xg[i],yg[i],magg[i],err[i],skyg[i],niter,chi[i],sharp[i]
+ if PRINT then for i = 0,nstr-1 do printf,lun,format=fmt, $
+     idg[i],xg[i],yg[i],magg[i],err[i],skyg[i],niter,chi[i],sharp[i]
+
+ if ( npar GE 9 ) then begin                  ;Create output vectors?
+   if ( N_elements(newid) EQ 0 ) then begin  ;Initialize output vectors?
+       newid = idg &  newx = xg  &  newy = yg & newmag = magg
+       iter = replicate(niter,nstr) & peak = sharp & chisq = chi
+       errmag = err
+   endif else begin           ;Append current group to output vector
+       newid = [newid,idg] & newx = [newx ,xg] & newy = [newy,yg]
+       newmag = [newmag,magg] & iter = [iter,replicate(niter,nstr)]
+       peak = [peak,sharp]     & chisq = [chisq,chi] & errmag = [errmag,err]
+   endelse
+ endif
+
+DONE_GROUP: 
+ endfor
+
+ if  ( npar GE 9 ) then begin
+    if N_elements(newid) GT 0 then begin
+       id = newid &  xc = newx &  yc = newy  & mags = newmag
+    endif else $
+     message,'ERROR - There are no valid stars left, variables not updated',/CON
+ endif
+
+ if PRINT then free_lun,lun
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/nulltrim.pro b/Code/script_idl_mv/astrolib/nulltrim.pro
new file mode 100644
index 0000000000000000000000000000000000000000..47dadbdf8b7ef754e04e112e1d00aa100bd733a8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/nulltrim.pro
@@ -0,0 +1,26 @@
+function nulltrim,st
+;+
+; NAME:
+;	NULLTRIM
+; PURPOSE:
+;	Trim a string of all characters after and including the first null
+; EXPLANATION:
+;	The null character is an ascii 0b
+;
+; CALLING SEQUENCE:
+;	result = nulltrim( st )
+;
+; INPUTS:
+;	st = input string
+; OUTPUTS:
+;	trimmed string returned as the function value.
+; HISTORY:
+;	D. Lindler  July, 1987
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;--------------------------------------------------------------------
+;
+ b = byte(st)
+ null = where( b eq 0, nfound )
+ if nfound lt 1 then return, st else return, strmid( st,0,null[0] )
+ end
diff --git a/Code/script_idl_mv/astrolib/nutate.pro b/Code/script_idl_mv/astrolib/nutate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9502438b686d54019cfd8314470a5715a698c995
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/nutate.pro
@@ -0,0 +1,145 @@
+pro nutate, jd, nut_long, nut_obliq
+;+
+; NAME:
+;       NUTATE
+; PURPOSE:
+;       Return the nutation in longitude and obliquity for a given Julian date
+;
+; CALLING SEQUENCE:
+;       NUTATE, jd, Nut_long, Nut_obliq
+;
+; INPUT:
+;       jd - Julian ephemeris date, scalar or vector, double precision  
+; OUTPUT:
+;       Nut_long - the nutation in longitude, same # of elements as jd
+;       Nut_obliq - nutation in latitude, same # of elements as jd
+;
+; EXAMPLE:
+;       (1) Find the nutation in longitude and obliquity 1987 on Apr 10 at Oh.
+;              This is example 22.a from Meeus
+;        IDL> jdcnv,1987,4,10,0,jul
+;        IDL> nutate, jul, nut_long, nut_obliq
+;             ==> nut_long = -3.788    nut_obliq = 9.443
+;            
+;       (2) Plot the large-scale variation of the nutation in longitude 
+;               during the 20th century
+;
+;       IDL> yr = 1900 + indgen(100)     ;Compute once a year        
+;       IDL> jdcnv,yr,1,1,0,jul          ;Find Julian date of first day of year
+;       IDL> nutate,jul, nut_long        ;Nutation in longitude
+;       IDL> plot, yr, nut_long
+;
+;       This plot will reveal the dominant (18.6 year) period, but a finer
+;       grid is needed to display the shorter periods in the nutation.
+; METHOD:
+;       Uses the formula in Chapter 22 of ``Astronomical Algorithms'' by Jean 
+;       Meeus (1998, 2nd ed.) which is based on the 1980 IAU Theory of Nutation
+;       and includes all terms larger than 0.0003".
+;
+; PROCEDURES CALLED:
+;       POLY()                       (from IDL User's Library)
+;       CIRRANGE, ISARRAY()          (from IDL Astronomy Library)
+;
+; REVISION HISTORY:
+;       Written, W.Landsman (Goddard/HSTX)      June 1996       
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Corrected minor typos in values of d_lng W. Landsman  December 2000
+;       Updated typo in cdelt term              December 2000
+;       Avoid overflow for more than 32767 input dates W. Landsman January 2005
+;-
+ compile_opt idl2
+ On_error,2
+ 
+ if N_params() LT 2 then begin
+        print,'Syntax - NUTATE, jd, nut_long, nut_obliq'
+        return
+ endif
+
+ dtor = !DPI/180.0d
+ ;  form time in Julian centuries from 1900.0
+
+ t = (jd[*] - 2451545.0d)/36525.0d0
+
+
+; Mean elongation of the Moon
+
+   coeff1 = [297.85036d,  445267.111480d, -0.0019142, 1.d/189474d0 ]
+  d = poly(T, coeff1)*dtor
+  cirrange,d,/rad
+
+; Sun's mean anomaly
+
+   coeff2 = [357.52772d, 35999.050340d, -0.0001603d, -1.d/3d5 ]
+   M = poly(T,coeff2)*dtor
+   cirrange, M,/rad
+
+; Moon's mean anomaly
+
+   coeff3 = [134.96298d, 477198.867398d, 0.0086972d, 1.0/5.625d4 ]
+   Mprime = poly(T,coeff3)*dtor
+   cirrange, Mprime,/rad
+
+; Moon's argument of latitude
+
+    coeff4 = [93.27191d, 483202.017538d, -0.0036825, -1.0d/3.27270d5 ]
+    F = poly(T, coeff4 )*dtor 
+    cirrange, F,/RAD
+
+; Longitude of the ascending node of the Moon's mean orbit on the ecliptic,
+;  measured from the mean equinox of the date
+
+  coeff5 = [125.04452d, -1934.136261d, 0.0020708d, 1.d/4.5d5]
+  omega = poly(T, coeff5)*dtor
+  cirrange,omega,/RAD
+
+ d_lng = [0,-2,0,0,0,0,-2,0,0,-2,-2,-2,0,2,0,2,0,0,-2,0,2,0,0,-2,0,-2,0,0,2,$
+   -2,0,-2,0,0,2,2,0,-2,0,2,2,-2,-2,2,2,0,-2,-2,0,-2,-2,0,-1,-2,1,0,0,-1,0,0, $
+     2,0,2]
+
+ m_lng = [0,0,0,0,1,0,1,0,0,-1,intarr(17),2,0,2,1,0,-1,0,0,0,1,1,-1,0, $
+  0,0,0,0,0,-1,-1,0,0,0,1,0,0,1,0,0,0,-1,1,-1,-1,0,-1]
+
+ mp_lng = [0,0,0,0,0,1,0,0,1,0,1,0,-1,0,1,-1,-1,1,2,-2,0,2,2,1,0,0,-1,0,-1, $
+   0,0,1,0,2,-1,1,0,1,0,0,1,2,1,-2,0,1,0,0,2,2,0,1,1,0,0,1,-2,1,1,1,-1,3,0]
+
+ f_lng = [0,2,2,0,0,0,2,2,2,2,0,2,2,0,0,2,0,2,0,2,2,2,0,2,2,2,2,0,0,2,0,0, $
+   0,-2,2,2,2,0,2,2,0,2,2,0,0,0,2,0,2,0,2,-2,0,0,0,2,2,0,0,2,2,2,2]
+
+ om_lng = [1,2,2,2,0,0,2,1,2,2,0,1,2,0,1,2,1,1,0,1,2,2,0,2,0,0,1,0,1,2,1, $
+   1,1,0,1,2,2,0,2,1,0,2,1,1,1,0,1,1,1,1,1,0,0,0,0,0,2,0,0,2,2,2,2]
+
+ sin_lng = [-171996, -13187, -2274, 2062, 1426, 712, -517, -386, -301, 217, $
+    -158, 129, 123, 63, 63, -59, -58, -51, 48, 46, -38, -31, 29, 29, 26, -22, $
+     21, 17, 16, -16, -15, -13, -12, 11, -10, -8, 7, -7, -7, -7, $
+     6,6,6,-6,-6,5,-5,-5,-5,4,4,4,-4,-4,-4,3,-3,-3,-3,-3,-3,-3,-3 ]
+ 
+ sdelt = [-174.2, -1.6, -0.2, 0.2, -3.4, 0.1, 1.2, -0.4, 0, -0.5, 0, 0.1, $
+     0,0,0.1, 0,-0.1,dblarr(10), -0.1, 0, 0.1, dblarr(33) ] 
+
+
+ cos_lng = [ 92025, 5736, 977, -895, 54, -7, 224, 200, 129, -95,0,-70,-53,0, $
+    -33, 26, 32, 27, 0, -24, 16,13,0,-12,0,0,-10,0,-8,7,9,7,6,0,5,3,-3,0,3,3,$
+     0,-3,-3,3,3,0,3,3,3, intarr(14) ]
+
+ cdelt = [8.9, -3.1, -0.5, 0.5, -0.1, 0.0, -0.6, 0.0, -0.1, 0.3, dblarr(53) ]
+
+
+; Sum the periodic terms 
+
+ n = N_elements(jd)
+ nut_long = dblarr(n)
+ nut_obliq = dblarr(n)
+ arg = d_lng#d + m_lng#m +mp_lng#mprime + f_lng#f +om_lng#omega
+ sarg = sin(arg)
+ carg = cos(arg)
+ for i=0L,n-1 do begin
+        nut_long[i] =  0.0001d*total( (sdelt*t[i] + sin_lng)*sarg[*,i] )
+        nut_obliq[i] = 0.0001d*total( (cdelt*t[i] + cos_lng)*carg[*,i] )
+ end
+ if ~isarray(jd) then begin
+        nut_long = nut_long[0]
+        nut_obliq = nut_obliq[0]
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/observatory.pro b/Code/script_idl_mv/astrolib/observatory.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9e16ccf9addb641008751b9cb53550de8eb0bc94
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/observatory.pro
@@ -0,0 +1,440 @@
+pro observatory,obsname,obs_struct, print = print
+;+
+; NAME:
+;       OBSERVATORY
+; PURPOSE:
+;       Return longitude, latitude, altitude & time zones of an observatory
+; EXPLANATION:
+;       Given an observatory name, returns a structure giving the longitude,
+;       latitude, altitude, and time zone 
+;
+; CALLING SEQUENCE:
+;       Observatory, obsname, obs_struct, [ /PRINT ]
+;
+; INPUTS:
+;       obsname - scalar or vector string giving abbreviated name(s) of 
+;             observatories for which location or time information is requested.
+;             If obsname is an empty string, then information is returned for 
+;             all observatories in the database.     See the NOTES: section
+;             for the list of 41 recognized observatories.   The case of the 
+;             string does not matter  
+; OUTPUTS:
+;       obs_struct - an IDL structure containing information on  the specified
+;                 observatories.   The structure tags are as follows: 
+;       .observatory - abbreviated observatory name
+;       .name - full observatory name  
+;       .longitude - observatory longitude in degrees *west* 
+;       .latitude - observatory latitude in degrees
+;       .altitude - observatory altitude in meters above sea level
+;       .tz - time zone, number of hours *west* of Greenwich
+;
+; OPTIONAL INPUT KEYWORD:
+;     /PRINT - If this keyword is set, (or if only 1 parameter is supplied)
+;             then OBSERVATORY will display information about the specified
+;             observatories at the terminal
+; EXAMPLE:
+;     Get the latitude, longitude and altitude of Kitt Peak National Observatory
+;
+;     IDL> observatory,'kpno',obs
+;     IDL> print,obs.longitude  ==> 111.6 degrees west 
+;     IDL> print,obs.latitude  ==> +31.9633 degrees
+;     IDL> print,obs.altitude  ==> 2120 meters above sea level
+;
+; NOTES:
+;   Observatory information is taken from noao$lib/obsdb.dat file in IRAF 2.11
+;   Currently recognized observatory names are as follows:
+;
+;  'kpno': Kitt Peak National Observatory
+;  'ctio': Cerro Tololo Interamerican Observatory
+;  'eso': European Southern Observatory
+;  'lick': Lick Observatory
+;  'mmto': MMT Observatory
+;  'cfht': Canada-France-Hawaii Telescope
+;  'lapalma': Roque de los Muchachos, La Palma
+;  'mso': Mt. Stromlo Observatory
+;  'sso': Siding Spring Observatory
+;  'aao': Anglo-Australian Observatory
+;  'mcdonald': McDonald Observatory
+;  'lco': Las Campanas Observatory
+;  'mtbigelow': Catalina Observatory: 61 inch telescope
+;  'dao': Dominion Astrophysical Observatory
+;  'spm': Observatorio Astronomico Nacional, San Pedro Martir
+;  'tona': Observatorio Astronomico Nacional, Tonantzintla
+;  'Palomar': The Hale Telescope
+;  'mdm': Michigan-Dartmouth-MIT Observatory
+;  'NOV': National Observatory of Venezuela
+;  'bmo': Black Moshannon Observatory
+;  'BAO': Beijing XingLong Observatory
+;  'keck': W. M. Keck Observatory
+;  'ekar': Mt. Ekar 182 cm. Telescope
+;  'loiano': Bologna Astronomical Observatory, Loiano - Italy
+;  'apo': Apache Point Observatory
+;  'lowell': Lowell Observatory
+;  'vbo': Vainu Bappu Observatory
+;  'flwo': Whipple Observatory
+;  'oro': Oak Ridge Observatory
+;  'lna': Laboratorio Nacional de Astrofisica - Brazil
+;  'saao': South African Astronomical Observatory
+;  'casleo': Complejo Astronomico El Leoncito, San Juan
+;  'bosque': Estacion Astrofisica Bosque Alegre, Cordoba
+;  'rozhen': National Astronomical Observatory Rozhen - Bulgaria
+;  'irtf': NASA Infrared Telescope Facility
+;  'bgsuo': Bowling Green State Univ Observatory
+;  'ca': Calar Alto Observatory
+;  'holi': Observatorium Hoher List (Universitaet Bonn) - Germany
+;  'lmo': Leander McCormick Observatory
+;  'fmo': Fan Mountain Observatory
+;  'whitin': Whitin Observatory, Wellesley College
+;  'mgio': Mount Graham International Observatory
+;
+; PROCEDURE CALLS:
+;    TEN()             
+; REVISION HISTORY:
+;    Written   W. Landsman                 July 2000
+;    Corrected sign error for 'holi'   W.L/ Holger Israel    Mar 2008
+;    Correctly terminate when observatory name not recognized 
+;                                              S. Koposov, July 2008
+;-
+
+ On_error,2                                  ;Return to caller
+ compile_opt idl2
+
+ if N_params() LT 1 then begin
+    print,'Observatory, obsname, obs_struct, [/print]'
+    return
+ endif
+ 
+obs=[ 'kpno','ctio','eso','lick','mmto','cfht','lapalma','mso','sso','aao', $
+  'mcdonald','lco','mtbigelow','dao','spm','tona','Palomar','mdm','NOV','bmo',$
+   'BAO','keck','ekar','loiano','apo','lowell','vbo','flwo','oro','lna','saao',$
+   'casleo','bosque','rozhen','irtf','bgsuo','ca','holi','lmo','fmo','whitin',$
+   'mgio']
+
+ if N_elements(obsname) EQ 1 then if obsname eq '' then obsname = obs
+ nobs = N_elements(obsname)
+ obs_struct = {observatory:'',name:'', longitude:0.0, latitude:0.0, $
+   altitude:0.0, tz:0.0}
+ if Nobs GT 1 then obs_struct = replicate(obs_struct,Nobs)
+ obs_struct.observatory = obsname
+
+
+for i=0,Nobs-1 do begin
+case strlowcase(obsname[i]) of 
+"kpno": begin
+	name = "Kitt Peak National Observatory"
+	longitude = [111,36.0]
+	latitude = [31,57.8]
+	altitude = 2120.
+	tz = 7
+        end
+"ctio": begin
+	name = "Cerro Tololo Interamerican Observatory"
+	longitude = 70.815
+	latitude = -30.16527778
+	altitude = 2215.
+	tz = 4
+        end
+"eso":  begin
+	name = "European Southern Observatory"
+	longitude = [70,43.8]
+	latitude =  [-29,15.4]
+	altitude = 2347.
+	tz = 4
+        end
+"lick": begin
+	name = "Lick Observatory"
+	longitude = [121,38.2]
+	latitude = [37,20.6]
+	altitude = 1290.
+	tz = 8
+        end
+"mmto": begin
+	name = "MMT Observatory"
+	longitude = [110,53.1]
+	latitude = [31,41.3]
+	altitude = 2600.
+	tz = 7
+        end
+"cfht": begin
+	name = "Canada-France-Hawaii Telescope"
+	longitude = [155,28.3]
+	latitude = [19,49.6]
+	altitude = 4215.
+	tz = 10
+        end        
+"lapalma": begin
+	name = "Roque de los Muchachos, La Palma"
+	longitude = [17,52.8]
+	latitude = [28,45.5]
+	altitude = 2327
+	tz = 0
+        end
+"mso":  begin
+	name = "Mt. Stromlo Observatory"
+	longitude = [210,58,32.4]
+	latitude = [-35,19,14.34]
+	altitude = 767
+	tz = -10
+        end
+"sso":  begin
+	name = "Siding Spring Observatory"
+	longitude = [210,56,19.70]
+	latitude = [-31,16,24.10]
+	altitude = 1149
+	tz = -10
+        end
+"aao":  begin
+	name = "Anglo-Australian Observatory"
+	longitude = [210,56,2.09]
+	latitude = [-31,16,37.34]
+	altitude = 1164
+	tz = -10
+        end
+"mcdonald": begin
+	name = "McDonald Observatory"
+	longitude = 104.0216667
+	latitude = 30.6716667
+	altitude = 2075
+	tz = 6
+        end
+"lco":  begin
+	name = "Las Campanas Observatory"
+	longitude = [70,42.1]
+	latitude = [-29,0.2]
+	altitude = 2282
+	tz = 4
+        end
+"mtbigelow": begin
+	name = "Catalina Observatory: 61 inch telescope"
+	longitude = [110,43.9]
+	latitude = [32,25.0]
+	altitude = 2510.
+	tz = 7
+        end
+"dao":  begin
+	name = "Dominion Astrophysical Observatory"
+	longitude = [123,25.0]
+	latitude = [48,31.3]
+	altitude = 229.
+	tz = 8
+        end
+ "spm":  begin
+	name = "Observatorio Astronomico Nacional, San Pedro Martir"
+	longitude = [115,29,13]
+	latitude = [31,01,45]
+	altitude = 2830.
+	tz = 7
+        end
+ "tona": begin
+	name = "Observatorio Astronomico Nacional, Tonantzintla"
+	longitude = [98,18,50]
+	latitude = [19,01,58]
+	tz = 8
+        altitude = -999999    ; Altitude not supplied
+        end
+ "palomar": begin
+	name = "The Hale Telescope"
+	longitude = [116,51,46.80]
+	latitude = [33,21,21.6]
+	altitude = 1706.
+	tz = 8
+        end
+ "mdm": begin
+	name = "Michigan-Dartmouth-MIT Observatory"
+	longitude = [111,37.0]
+	latitude = [31,57.0]
+	altitude = 1938.5
+	tz = 7
+        end
+ "nov": begin
+	name = "National Observatory of Venezuela"
+	longitude = [70,52.0]
+	latitude = [8,47.4]
+	altitude = 3610
+	tz = 4
+        end
+ "bmo": begin
+	name = "Black Moshannon Observatory"
+	longitude = [78,00.3]
+	latitude = [40,55.3]
+	altitude = 738.
+	tz = 5
+         end
+ "bao": begin
+	name = "Beijing XingLong Observatory"
+	longitude = [242,25.5]
+	latitude = [40,23.6]
+	altitude = 950.
+	tz = -8
+        end
+ "keck": begin
+	name = "W. M. Keck Observatory"
+	longitude = [155,28.7]
+	latitude = [19,49.7]
+	altitude = 4160.
+	tz = 10
+        end
+ "ekar": begin
+	name = "Mt. Ekar 182 cm. Telescope"
+	longitude = [348,25,07.92]
+	latitude = [45,50,54.92]
+	altitude = 1413.69
+	tz = -1
+        end
+ "loiano": begin
+        name = "Bologna Astronomical Observatory, Loiano - Italy"
+        longitude = [348,39,58]
+        latitude = [44,15,33]
+        altitude = 785.
+        tz = -1
+        end
+ "apo":  begin
+	name = "Apache Point Observatory"
+	longitude = [105,49.2]
+	latitude = [32,46.8]
+	altitude = 2798.
+	tz = 7 
+        end
+ "lowell": begin
+	name = "Lowell Observatory"
+	longitude = [111,32.1]
+	latitude = [35,05.8]
+	altitude = 2198. 
+	tz = 7 
+        end
+ "vbo": begin
+	name = "Vainu Bappu Observatory"
+	longitude = 281.1734
+	latitude = 12.57666
+	altitude = 725. 
+	tz = -5.5
+         end
+ "flwo": begin
+        name = "Whipple Observatory"
+        longitude = [110,52,39]
+        latitude = [31,40,51.4]
+        altitude = 2320.
+        tz = 7
+        end
+ "oro": begin
+	name = "Oak Ridge Observatory"
+        longitude = [71,33,29.32]
+        latitude =  [42,30,18.94]
+        altitude =  184.
+        tz = 5
+        end
+
+ "lna":  begin
+        name = "Laboratorio Nacional de Astrofisica - Brazil"
+        longitude = 45.5825
+        latitude = [-22,32,04]
+        altitude = 1864.
+        tz = 3
+        end
+
+ "saao": begin
+	name = "South African Astronomical Observatory"
+	longitude = [339,11,21.5]
+	latitude =  [-32,22,46]
+	altitude =  1798.
+	tz = -2
+         end
+ "casleo": begin
+        name = "Complejo Astronomico El Leoncito, San Juan"
+        longitude = [69,18,00] 
+        latitude = [-31,47,57]
+        altitude = 2552
+        tz = 3
+        end
+ "bosque": begin
+        name = "Estacion Astrofisica Bosque Alegre, Cordoba"
+        longitude = [64,32,45]
+        latitude = [-31,35,54]
+        altitude = 1250
+        tz = 3
+        end
+ "rozhen": begin
+        name = "National Astronomical Observatory Rozhen - Bulgaria"
+	longitude = [335,15,22]
+	latitude = [41,41,35]
+	altitude = 1759
+	tz = -2
+        end
+ "irtf": begin
+	name        = "NASA Infrared Telescope Facility"
+	longitude   = 155.471999
+	latitude    = 19.826218
+	altitude    = 4168
+	tz    = 10
+        end
+ "bgsuo": begin
+        name = "Bowling Green State Univ Observatory"
+        longitude = [83,39,33]
+        latitude = [41,22,42]
+        altitude = 225.
+        tz = 5
+        end
+ "ca":   begin
+	name = "Calar Alto Observatory"
+	longitude = [2,32,46.5]
+	latitude = [37,13,25]
+	altitude = 2168
+	tz = -1
+        end
+ "holi": begin
+        name = "Observatorium Hoher List (Universitaet Bonn) - Germany"
+        longitude = 353.15     ;Corrected sign error March 2008
+        latitude = 50.16276
+        altitude = 541
+        tz = -1
+       end
+ "lmo":  begin
+        name = "Leander McCormick Observatory"
+        longitude = [78,31,24]
+        latitude =  [38,02,00]
+        altitude = 264
+        tz = 5
+        end
+ "fmo": begin
+        name = "Fan Mountain Observatory"
+        longitude = [78,41,34]
+        latitude =  [37,52,41]
+        altitude = 556 
+        tz = 5
+       end
+ "whitin": begin
+	name = "Whitin Observatory, Wellesley College"
+	longitude = 71.305833
+	latitude = 42.295
+	altitude = 32
+	tz = 5
+        end
+ "mgio": begin
+	name = "Mount Graham International Observatory"
+	longitude = [109,53,31.25]
+	latitude = [32,42,04.69]
+	altitude = 3191.0
+	tz = 7
+        end
+ else: message,'Unable to find observatory ' + obsname + ' in database'
+ endcase
+
+ obs_struct[i].longitude = ten(longitude)
+ obs_struct[i].latitude = ten(latitude)
+ obs_struct[i].tz = tz
+ obs_struct[i].name = name
+ obs_struct[i].altitude = altitude
+
+ if N_params() EQ 1 or keyword_set(print) then begin
+     print,' '
+     print,'Observatory: ',obsname[i]
+     print,'Name: ',name
+     print,'longitude:',obs_struct[i].longitude
+     print,'latitude:',obs_struct[i].latitude
+     print,'altitude:',altitude
+     print,'time zone:',tz
+  endif
+ endfor
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/one_arrow.pro b/Code/script_idl_mv/astrolib/one_arrow.pro
new file mode 100644
index 0000000000000000000000000000000000000000..98d64f43e12bfe9ec9305900acb05af9cfb1e87e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/one_arrow.pro
@@ -0,0 +1,115 @@
+pro one_arrow,xcen,ycen,angle,label, linestyle = linestyle, $
+              charsize=charsize,thick=thick,color=color, $
+              arrowsize=arrowsize,font = font, data=data, normal=normal
+;+
+; NAME:
+;       ONE_ARROW
+; PURPOSE:
+;       Draws an arrow labeled with a single character on the current device
+; EXPLANATION:
+;       ONE_ARROW is called, for example, by ARROWS to create a
+;       "weathervane" showing the N-E orientation of an image.
+;
+; CALLING SEQUENCE:
+;       one_arrow, xcen, ycen, angle, label, CHARSIZE = , THICK = , COLOR = 
+;                       ARROWSIZE=, FONT =  ]
+; INPUT PARAMETERS:
+;    xcen, ycen = starting point of arrow, floating point scalars,
+;                 In device coordinates unless /DATA or /NORMAL set
+;    angle      = angle of arrow in degrees counterclockwise from +X direction
+;    label      = single-character label (may be blank)
+;
+; OUTPUT PARAMETERS:  none
+;
+; OPTIONAL INPUT PARAMETERS:
+;       ARROWSIZE  = 3-element vector defining appearance of arrow.
+;               For device coordinates the default is  [30.0, 9.0, 35.0], 
+;               meaning arrow is 30 pixels long; arrowhead lines 9 pixels 
+;               long and inclined 35 degrees from arrow shaft.     For 
+;               normalized coordinates the default is divided by 512., for 
+;               data coordinates the default is multiplied by 
+;               (!X.crange[1] - !X.crange[0])/512..
+;       CHARSIZE   = usual IDL meaning, default = 2.0
+;       COLOR      = name or number give the color to draw the arrow.  See
+;             cgCOLOR for a list of color names.
+;       /DATA - If set, then the input position (xcen, ycen) and the ARROWSIZE
+;                lengths are interpreted as being in data coordinates
+;       FONT - IDL vector font number to use (1-20).   For example, to write
+;               the 'N' and 'E' characters in complex script, set font=13
+;       /NORMAL - If set, then the input position (xcen, ycen) and the ARROWSIZE
+;                lengths are interpreted as being in normal coordinates
+;       THICK      = usual IDL meaning, default = 2.0
+; EXAMPLE:
+;       Draw an triple size arrow emanating from the point (212,224)
+;       and labeled with the character 'S'
+;
+;       IDL> one_arrow,212,224,270,'S',charsize=3
+; PROCEDURE:  
+;       Calls one_ray to vector-draw arrow.
+; MODIFICATION HISTORY:
+;       Written by R. S. Hill, Hughes STX Corp., 20-May-1992.
+;       Added font keyword, W.B. Landsman Hughes STX Corp. April 1995
+;       Modified to work correctly for COLOR=0  J.Wm.Parker, HITC   1995 May 25
+;       Add /NORMAL and /DATA keywords  W.Landsman    November 2006
+;       Work with Coyote graphics W. Landsman  February 2011
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 4 then begin
+      print,'Syntax - one_arrow, xcen, ycen, angle, label, CHARSIZE = , FONT=' 
+      print,'                [ /DATA, /NORMAL, THICK= , COLOR=, ARROWSIZE = ]'
+      return
+ endif 
+
+  if (n_elements(arrowsize) ge 1) and (n_elements(arrowsize) ne 3) then begin
+   print,'Error in ONE_ARROW:  returning to main level.'
+   print,'Arrowsize is [length, head_length, head_angle]'
+   print,'Defaults are [30.0,9.0,35.0]'
+   return
+ endif
+
+ setdefaultvalue, charsize, 2.0
+ setdefaultvalue, thick, 2.0
+   if keyword_set(data) then scale = (!X.CRANGE[1] - !X.CRANGE[0])/512. $
+    else if keyword_set(normal) then scale = 1/512. else scale = 1.
+ if N_elements(arrowsize) eq 0 then $
+    arrowsize=[30.0*scale,9.0*scale,35.0] else $
+    arrowsize = [arrowsize[0]*scale, arrowsize[1]*scale, arrowsize[2] ]
+
+ device = ~keyword_set(data) && ~keyword_set(normal)
+ label = strmid(strtrim(label,2),0,1)
+ if keyword_set(font) then label = '!' + strtrim(font,2) + label + '!X '
+ len       = arrowsize[0]
+ headlen   = arrowsize[1]
+ headangle = arrowsize[2]
+ baseline  = (!d.y_ch_size+!d.x_ch_size)/2.0
+ char_cen_offset  = baseline*charsize
+ if keyword_set(data) then char_cen_offset = $
+        convert_coord(char_cen_offset,0,/device,/to_data) - $ 
+        convert_coord(0,0,/device,/to_data)
+ if keyword_set(normal) then char_cen_offset = $ 
+       convert_coord(char_cen_offset,0,/device,/to_normal) - $
+       convert_coord(0,0,/device,/to_normal)
+ char_cen_offset = char_cen_offset[0]           
+ char_orig_len    = char_cen_offset/2.0
+ char_orig_angle  = 225.0
+;  Draw shaft of arrow
+one_ray,xcen,ycen,len,angle,terminus,thick=thick,color=color,data= data, $
+   normal=normal,linestyle=linestyle
+
+;  Draw head of arrow
+one_ray,terminus[0],terminus[1],headlen,(angle+180.0+headangle),t2, $
+   thick=thick,color=color,data=data,normal=normal,linestyle=linestyle
+one_ray,terminus[0],terminus[1],headlen,(angle+180.0-headangle),t2, $
+   thick=thick,color=color,data = data, normal = normal,linestyle=linestyle
+
+;  Draw label
+one_ray,xcen,ycen,len+char_cen_offset,angle,terminus,/nodraw
+one_ray,terminus[0],terminus[1],char_orig_len,char_orig_angle,char_orig,/nodraw
+cgtext, char_orig[0], char_orig[1], label, charthick=thick, color=color, $
+ charsize=charsize, device=device, normal=normal
+
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/one_ray.pro b/Code/script_idl_mv/astrolib/one_ray.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6714878b8aec27403a7885af59d2c32107176062
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/one_ray.pro
@@ -0,0 +1,62 @@
+pro one_ray,xcen,ycen,len,angle,terminus,nodraw=nodraw, _EXTRA=_extra, $
+    data = data, normal = normal
+;+
+; NAME:
+;       ONE_RAY
+; PURPOSE:
+;       Draw a line with a specified starting point, length, and  angle
+;
+; CALLING SEQUENCE:
+;       one_ray, xcen, ycen, len, angle, terminus, /NODRAW ]
+;
+; INPUT PARAMETERS:
+;       xcen, ycen = starting point in device coordinates, floating point 
+;                       scalars
+;       len        = length in pixels, device coordinates
+;       angle      = angle in degrees counterclockwise from +X direction
+;
+; OUTPUT PARAMETERS:
+;       terminus = two-element vector giving ending point of ray in device
+;               coordinates
+;
+; OPTIONAL KEYWORD INPUT PARAMETERS:
+;       /nodraw   if non-zero, the ray is not actually drawn, but the terminus
+;               is still calculated
+;
+;        Any valid keyword to cgPLOTS can also be passed ot ONE_RAY.   In
+;        particular, COLOR, THICK, and LINESTYLE control the color, thickness
+;        and linestyle of the drawn line.
+; EXAMPLE:
+;       Draw a double thickness line of length 32 pixels from (256,256) 
+;       45 degrees counterclockwise from the X axis
+;
+;       IDL> one_ray, 256, 256, 32, 45 ,term, THICK = 2
+;
+; PROCEDURE:  straightforward matrix arithmetic
+;
+; MODIFICATION HISTORY:
+;    Written by R. S. Hill, Hughes STX Corp., 20-May-1992.
+;    Modified to work correctly for COLOR=0  J.Wm.Parker  HITC   1995 May 25
+;    Added _EXTRA keywords to PLOT   W. Landsman   November 2006
+;    Work with Coyote Graphcis W. Landsman February 2011
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 3 then begin
+    print,'Syntax -  one_ray, xcen, ycen, len, angle, [terminus,] ' + $
+               '[ /DATA, /NORMAL, THICK= ,COLOR =, /NODRAW ]'
+ endif
+
+ device = ~keyword_set(normal) &&  ~keyword_set(data)
+ sina = sin(angle/!radeg)
+ cosa = cos(angle/!radeg)
+ rot_mat = [ [ cosa, sina ], [-sina, cosa ] ]
+ terminus =  (rot_mat # [len, 0.0]) + [xcen, ycen]
+
+ if ~keyword_set(nodraw) then $
+   cgplots, [xcen, terminus[0]], [ycen, terminus[1]], $
+      DEVICE=device, Normal=Normal,_STRICT_Extra= _extra
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/oploterror.pro b/Code/script_idl_mv/astrolib/oploterror.pro
new file mode 100644
index 0000000000000000000000000000000000000000..742f2143ca9a47c93b7f725503a625b5dd2ec793
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/oploterror.pro
@@ -0,0 +1,308 @@
+PRO  oploterror, x, y, xerr, yerr, NOHAT=nohat, HATLENGTH=hln, ERRTHICK=eth, $
+      ERRSTYLE=est, THICK = thick, NOCLIP=noclip, ERRCOLOR = ecol, Nsum = nsum,$
+      NSKIP=nskip, LOBAR=lobar, HIBAR=hibar, ADDCMD=addcmd, WINDOW=window, $
+       _EXTRA = pkey
+;+
+; NAME:
+;      OPLOTERROR
+; PURPOSE:
+;      Over-plot data points with accompanying X or Y error bars.
+; EXPLANATION:
+;      For use instead of PLOTERROR when the plotting system has already been
+;      defined. 
+;
+; CALLING SEQUENCE:
+;      oploterror, [ x,]  y, [xerr], yerr,   
+;            [ /NOHAT, HATLENGTH= , ERRTHICK =, ERRSTYLE=, ERRCOLOR =, 
+;              /LOBAR, /HIBAR, NSKIP = , NSUM = , /ADDCMD, ... OPLOT keywords ]
+; INPUTS:
+;      X = array of abscissas, any datatype except string
+;      Y = array of Y values, any datatype except string
+;      XERR = array of error bar values (along X)
+;      YERR = array of error bar values (along Y)
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS:
+; 
+;      /ADDCMD    = Set this keyword if you want to add this command to 
+;                   a cgWindow.
+;      /NOHAT     = if specified and non-zero, the error bars are drawn
+;                  without hats.
+;      HATLENGTH = the length of the hat lines used to cap the error bars.
+;                  Defaults to !D.X_VSIZE / 100).
+;      ERRTHICK  = the thickness of the error bar lines.  Defaults to the
+;                  THICK plotting keyword.
+;      ERRSTYLE  = the line style to use when drawing the error bars.  Uses
+;                  the same codes as LINESTYLE.
+;     ERRCOLOR =  String (e.g. 'red') or scalar integer (0 - !D.N_TABLE)
+;              specifying the color to use for the error bars.   See CGCOLOR()
+;              for a list of possible color names.  See 
+;              http://www.idlcoyote.com/cg_tips/legcolor.php
+;              for a warning about the use of indexed color
+;      NSKIP = Positive Integer specifying the error bars to be plotted.   
+;            For example, if NSKIP = 2 then every other error bar is 
+;            plotted; if NSKIP=3 then every third error bar is plotted.   
+;            Default is to plot every error bar (NSKIP = 1)
+;      NSUM =  Number of points to average over before plotting, default = 
+;             !P.NSUM  The errors are also averaged, and then divided by 
+;             sqrt(NSUM).   This approximation is meaningful only when the 
+;             neighboring error bars have similar sizes.
+; 
+;      /LOBAR = if specified and non-zero, will draw only the -ERR error bars.
+;      /HIBAR = if specified and non-zero, will draw only the +ERR error bars.
+;                  If neither LOBAR or HIBAR are set _or_ if both are set,
+;                  you will get both error bars.  Just specify one if you
+;                  only want one set.
+;      /WINDOW - A synonum for ADDCMD (since OPLOTERROR will never open a 
+;                new window).
+;     Any valid keywords to the OPLOT command (e.g. PSYM, YRANGE) are also 
+;     accepted by OPLOTERROR via the _EXTRA facility.
+;
+; NOTES:
+;     If only two parameters are input, they are taken as Y and YERR.  If only
+;     three parameters are input, they will be taken as X, Y and YERR, 
+;     respectively.
+;
+; EXAMPLE:
+;      Suppose one has X and Y vectors with associated errors XERR and YERR
+;      and that a plotting system has already been defined:
+;
+;       (1) Overplot Y vs. X with both X and Y errors and no lines connecting
+;           the points
+;                  IDL> oploterror, x, y, xerr, yerr, psym=3
+;
+;       (2) Like (1) but overplot only the Y error bars and omits "hats"
+;                  IDL> oploterror, x, y, yerr, psym=3, /NOHAT
+;
+;       (3) Like (2) but suppose one has a positive error vector YERR1, and 
+;               a negative error vector YERR2 (asymmetric error bars)
+;                  IDL> oploterror, x, y, yerr1, psym=3, /NOHAT,/HIBAR
+;                  IDL> oploterror, x, y, yerr2, psym=3, /NOHAT,/LOBAR
+;
+; PROCEDURE:
+;      A plot of X versus Y with error bars drawn from Y - YERR to Y + YERR
+;      and optionally from X - XERR to X + XERR is written to the output device
+;
+; WARNING:
+;      This an enhanced version of the procedure OPLOTERR in the standard RSI
+;      library.    It was renamed to OPLOTERROR in June 1998 in the IDL 
+;      Astronomy library.
+;
+; MODIFICATION HISTORY:
+;      Adapted from the most recent version of PLOTERR.  M. R. Greason,
+;            Hughes STX, 11 August 1992.
+;      Added COLOR keyword option to error bars W. Landsman   November 1993
+;      Add ERRCOLOR, use _EXTRA keyword,           W. Landsman, July 1995
+;      Remove spurious call to PLOT_KEYWORDS     W. Landsman, August 1995
+;      OPLOT more than 32767 error bars          W. Landsman, Feb 1996
+;      Added NSKIP keyword                       W. Landsman, Dec 1996
+;      Added HIBAR and LOBAR keywords, M. Buie, Lowell Obs., Feb 1998
+;      Rename to OPLOTERROR    W. Landsman    June 1998
+;      Ignore !P.PSYM when drawing error bars   W. Landsman   Jan 1999
+;      Handle NSUM keyword correctly           W. Landsman    Aug 1999
+;      Check limits for logarithmic axes       W. Landsman    Nov. 1999
+;      Work in the presence of  NAN values     W. Landsman    Dec 2000
+;      Improve logic when NSUM or !P.NSUM is set  W. Landsman      Jan 2001
+;      Remove NSUM keyword from PLOTS call    W. Landsman      March 2001
+;      Only draw error bars with in XRANGE (for speed)  W. Landsman Jan 2002
+;      Fix Jan 2002 update to work with log plots  W. Landsman Jun 2002
+;      Added STRICT_EXTRA keyword   W. Landsman     July 2005
+;      W. Landsman Fixed case of logarithmic axes reversed Mar 2009
+;      Update for Coyote Graphics  W. Landsman     Feb. 2011 
+;      Hats were not being plotted by default  W. Landsman Apr 2011 
+;      With latest CGPLOT, no need to deal special case of only a single point
+;               W. Landsman October 2012 
+;      Work with a cgWindow, /WINDOW a synonum for /ADDCMD W. Landsman Feb 2013
+;-
+;                  Check the parameters.
+;
+ On_error, 2
+ compile_opt idl2
+ np = N_params()
+ IF (np LT 2) THEN BEGIN
+      print, "OPLOTERR must be called with at least two parameters."
+      print, "Syntax: oploterr, [x,] y, [xerr], yerr, [..oplot keywords... "
+      print,'     /NOHAT, HATLENGTH = , ERRTHICK=, ERRSTLYE=, ERRCOLOR='
+      print,'     /LOBAR, /HIBAR, /ADDCMD, NSKIP= ]'
+      RETURN
+ ENDIF
+
+ ; Add it to a cgWindow, if required.
+
+   addcmd = Keyword_Set(addcmd) || keyword_set(window)
+   IF (Keyword_Set(addcmd)) && ((!D.Flags AND 256) NE 0) THEN BEGIN
+    
+      void = cgQuery(Count=count)
+      IF count EQ 0 THEN Message, 'No cgWindow currently exists to add this command to.'
+      cgWindow, 'oploterror', x, y, xerr, yerr, NOHAT=nohat, HATLENGTH=hln, ERRTHICK=eth, $
+          ERRSTYLE=est, THICK = thick, NOCLIP=noclip, ERRCOLOR = ecol, Nsum = nsum,$
+          NSKIP=nskip, LOBAR=lobar, HIBAR=hibar, ADDCMD=1, _EXTRA = pkey
+            
+      RETURN
+ ENDIF
+ 
+ 
+; Error bar keywords (except for HATLENGTH; this one will be taken care of 
+; later, when it is time to deal with the error bar hats).
+
+  setdefaultvalue, thick, !P.THICK
+  setdefaultvalue, eth, thick
+  setdefaultvalue, est, 0        ;Error line style
+  setdefaultvalue, noclip, 0
+ if ~keyword_set(NSKIP) then nskip = 1
+  setdefaultvalue, nsum , !P.NSUM
+ if (N_elements(ecol) EQ 0) && (N_elements(pkey) GT 0) then $
+    if tag_exist(pkey,'COLOR') then  ecol = pkey.color
+ if ~keyword_set(lobar) && ~keyword_set(hibar) then begin
+      lobar=1
+      hibar=1
+ endif else if keyword_set(lobar) && keyword_set(hibar) then begin
+      lobar=1
+      hibar=1
+ endif else if keyword_set(lobar) then begin
+      lobar=1
+      hibar=0
+ endif else begin
+      lobar=0
+      hibar=1
+ endelse
+;
+; If no X array has been supplied, create one.  Make sure the rest of the 
+; procedure can know which parameter is which.
+;
+ IF np EQ 2 THEN BEGIN                  ; Only Y and YERR passed.
+      yerr = y
+      yy = x
+      xx = indgen(n_elements(yy))
+      xerr = make_array(size=size(xx))
+
+ ENDIF ELSE IF np EQ 3 THEN BEGIN       ; X, Y, and YERR passed.
+        yerr = xerr
+        yy = y
+        xx = x
+
+ ENDIF ELSE BEGIN                        ; X, Y, XERR and YERR passed.
+      yy = y
+      g = where(finite(xerr))
+      xerr[g] = abs(xerr[g])
+      xx = x
+ ENDELSE
+
+ g = where(finite(yerr))
+ yerr[g] = abs(yerr[g])
+
+;
+;                  Determine the number of points being plotted.  This
+;                  is the size of the smallest of the three arrays
+;                  passed to the procedure.  Truncate any overlong arrays.
+;
+
+ n = N_elements(xx) < N_elements(yy)
+
+ IF np GT 2 then n = n < N_elements(yerr)   
+ IF np EQ 4 then n = n < N_elements(xerr)
+
+ xx = xx[0:n-1]
+ yy = yy[0:n-1]
+ yerr = yerr[0:n-1]
+ IF np EQ 4 then xerr = xerr[0:n-1]
+
+; If NSUM is greater than one, then we need to smooth ourselves (using FREBIN)
+
+ if NSum GT 1 then begin
+      n1 = float(n) / nsum
+      n  = long(n1)
+      xx = frebin(xx, n1)
+      yy = frebin(yy, n1)
+      yerror = frebin(yerr,n1)/sqrt(nsum)
+      if NP EQ 4 then xerror = frebin(xerr,n1)/sqrt(nsum)
+  endif else begin
+      yerror = yerr
+      if NP EQ 4 then xerror = xerr
+  endelse
+
+ ylo = yy - yerror*lobar
+ yhi = yy + yerror*hibar
+
+ if Np EQ 4 then begin
+     xlo = xx - xerror*lobar
+     xhi = xx + xerror*hibar
+ endif
+ 
+;
+;                  Plot the positions.
+;
+  window = cgquery(/current) GE 0
+  cgPlot, xx, yy, NOCLIP=noclip,THICK = thick,_STRICT_EXTRA = pkey,/over
+
+;;
+;; Plot the error bars.   Compute the hat length in device coordinates
+;; so that it remains fixed even when doing logarithmic plots.
+;;
+
+ data_low = convert_coord(xx,ylo,/TO_DEVICE)
+ data_hi = convert_coord(xx,yhi,/TO_DEVICE)
+ if NP EQ 4 then begin
+    x_low = convert_coord(xlo,yy,/TO_DEVICE)
+    x_hi = convert_coord(xhi,yy,/TO_DEVICE)
+ endif
+ 
+ ycrange = !Y.CRANGE   &  xcrange = !X.CRANGE
+   if !Y.type EQ 1 then ylo = ylo > 10^min(ycrange)    
+	                    
+    if (!X.type EQ 1) && (np EQ 4) then xlo = xlo > 10^min(xcrange) 
+
+ sv_psym = !P.PSYM & !P.PSYM = 0     ;Turn off !P.PSYM for error bars
+; Only draw error bars for X values within XCRANGE
+    if !X.TYPE EQ 1 then xcrange = 10^xcrange
+    g = where((xx GT xcrange[0]) and (xx LE xcrange[1]), Ng)
+    if (Ng GT 0) && (Ng NE n) then begin  
+          istart = min(g, max = iend)  
+    endif else begin
+          istart = 0L & iend = n-1
+    endelse
+    
+    ; Set plotting color.
+    ecol = cgDefaultColor(ecol, Default='opposite')
+    IF Size(ecol, /TNAME) EQ 'STRING' THEN ecol = cgColor(ecol)
+    
+ FOR i = istart, iend, Nskip DO BEGIN
+
+    Plots, [xx[i],xx[i]], [ylo[i],yhi[i]], LINESTYLE=est,THICK=eth,  $
+           NOCLIP = noclip, COLOR = ecol
+
+    ; Plot X-error bars 
+    ;
+    if np EQ 4 then $
+       Plots, [xlo[i],xhi[i]],[yy[i],yy[i]],LINESTYLE=est, $
+              THICK=eth, COLOR = ecol, NOCLIP = noclip
+
+    IF ~keyword_set(nohat) THEN BEGIN
+       IF (N_elements(hln) EQ 0) THEN hln = !D.X_VSIZE/100. 
+       exx1 = data_low[0,i] - hln/2.
+       exx2 = exx1 + hln
+       if lobar then $
+          Plots, [exx1,exx2], [data_low[1,i],data_low[1,i]],COLOR=ecol, $
+                 LINESTYLE=est,THICK=eth,/DEVICE, noclip = noclip
+       if hibar then $
+          Plots, [exx1,exx2], [data_hi[1,i],data_hi[1,i]], COLOR = ecol,$
+                 LINESTYLE=est,THICK=eth,/DEVICE, noclip = noclip
+;                                          
+       IF np EQ 4 THEN BEGIN
+          IF (N_elements(hln) EQ 0) THEN hln = !D.Y_VSIZE/100.
+             eyy1 = x_low[1,i] - hln/2.
+             eyy2 = eyy1 + hln
+             if lobar then $
+                Plots, [x_low[0,i],x_low[0,i]], [eyy1,eyy2],COLOR = ecol, $
+                       LINESTYLE=est,THICK=eth,/DEVICE, NOCLIP = noclip
+             if hibar then $
+                Plots, [x_hi[0,i],x_hi[0,i]], [eyy1,eyy2],COLOR = ecol, $
+                       LINESTYLE=est,THICK=eth,/DEVICE, NOCLIP = noclip
+          ENDIF
+       ENDIF
+    NOPLOT:
+ENDFOR
+ !P.PSYM = sv_psym 
+
+;
+RETURN
+END
diff --git a/Code/script_idl_mv/astrolib/ordinal.pro b/Code/script_idl_mv/astrolib/ordinal.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c0f4f1ea9d7bd9e158da68eecf52f892dbe75f9f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ordinal.pro
@@ -0,0 +1,37 @@
+FUNCTION ordinal,num
+;+
+; NAME:
+;	ORDINAL
+; PURPOSE:
+;	Convert an integer to a correct English ordinal string:
+; EXPLANATION:
+;	The first four ordinal strings are "1st", "2nd", "3rd", "4th" ....
+;
+; CALLING SEQUENCE:
+;	result = ordinal( num )
+;
+; INPUT PARAMETERS:
+;	num = number to be made an ordinal.  If float, will be FIXed.
+;
+; OUTPUT PARAMETERS:
+;	result = string such as '1st' '3rd' '164th' '87th', etc.
+;
+; MODIFICATION HISTORY:  
+;	Written by R. S. Hill, STX, 8 Aug. 1991
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+On_error,2
+num = fix(num)
+CASE num MOD 100 OF
+   11:  suffix = 'th'
+   12:  suffix = 'th'
+   13:  suffix = 'th'
+   ELSE:  CASE num MOD 10 OF
+          1:  suffix = 'st'
+          2:  suffix = 'nd'
+          3:  suffix = 'rd'
+          ELSE: suffix = 'th'
+          ENDCASE
+ENDCASE
+RETURN,strtrim(string(num),2)+suffix
+END
diff --git a/Code/script_idl_mv/astrolib/partvelvec.pro b/Code/script_idl_mv/astrolib/partvelvec.pro
new file mode 100644
index 0000000000000000000000000000000000000000..69a64ee56dd1146e8a0e0f3f3549d776c57a6e81
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/partvelvec.pro
@@ -0,0 +1,250 @@
+;+
+; NAME:
+;      PARTVELVEC
+;
+; PURPOSE:
+;       Plot the velocity vectors of particles at their positions
+; EXPLANATION:
+;       This procedure plots the velocity vectors of particles (at the
+;       positions of the particles).
+;
+;       For a similar procedure look at cgDrawVectors 
+;        http://www.idlcoyote.com/idldoc/cg/cgdrawvectors.html
+; CATEGORY:
+;       Plotting, Two-dimensional.
+;
+; CALLING SEQUENCE:
+;       PARTVELVEC, VELX, VELY, POSX, POSY [, X, Y]
+;
+; INPUTS:
+;       VELX:  An array of any dimension, containing the x-components
+;              of the particle velocities.   Can include NaN values
+;       VELY:  An array of the same dimension as velx, containing the
+;              y-components of the particle velocities. 
+;       POSX:  An array of the same dimension as velx, containing the
+;              x-components of the particle positions.
+;       POSY:  An array of the same dimension as velx, containing the
+;              y-components of the particle positions.
+;
+; OPTIONAL INPUTS:
+;       X:   Optional abscissa values. X must be a vector.
+;       Y:   Optional ordinate values. Y must be a vector. If only X
+;            is specified, then Y is taken equal to be equal to X.
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS:
+;       FRACTION:   The fraction of the vectors to plot. They are
+;                   taken at random from the complete sample.    Default is
+;              FRACTION = 1.0, use all vectors
+;
+;       LENGTH:     The maximum vectorlength relative to the plot data
+;                   window.   Default = 0.08
+;
+;       COLOR:      Color for the vectors, axes and titles by string name or
+;                   number (see cgCOLOR).   Note that if VECCOLORS is 
+;                   supplied, then the COLOR keyword still specifies the 
+;                   color of the axes and title.    Default  = 'Opposite'
+;
+;       OVER:       Plot over the previous plot
+;
+;       VECCOLORS:  The vector colors. Must be either a scalar, or
+;                   a vector (nmeric or string) the same size as VELX. 
+;                   Set to COLOR by default.
+;       WINDOW - Set this keyword to plot to a resizeable graphics window
+;
+;       Plot        All other keywords available to cgPlot (e.g. AXISCOLOR,
+;       Keywords:   LINESTYLE, XRANGE) are available (via _EXTRA)
+;
+; OUTPUTS:
+;       This procedure plots the velocity vectors (VELX,VELY) at the
+;       positions of the particles, (POSX,POSY). If X and Y are not
+;       specified, then the size of the plot is such that all vectors
+;       just fit within in the plot data window.
+;
+; SIDE EFFECTS:
+;       Plotting on the current device is performed.
+;
+; EXAMPLE:
+;       Generate some particle positions and velocities.
+;
+;         POSX=RANDOMU(seed,200)
+;         POSY=RANDOMU(seed,200)
+;         VELX=RANDOMU(seed,200)-0.5
+;         VELY=RANDOMU(seed,200)-0.5
+;
+;       Plot the particle velocities.
+;
+;         PARTVELVEC, VELX, VELY, POSX, POSY
+;
+;       Example using vector colors.
+;
+;         POSX=RANDOMU(seed,200)
+;         POSY=RANDOMU(seed,200)
+;         VELX=RANDOMU(seed,200)-0.5
+;         VELY=RANDOMU(seed,200)-0.5
+;         magnitude = SQRT(velx^2 + vely^2)
+;         LOADCT, 5, NCOLORS=254, BOTTOM=1 ; Load vector colors
+;         colors = BytScl(magnitude, Top=254) + 1B
+;         PARTVELVEC, VELX, VELY, POSX, POSY, COLOR='green', VECCOLORS=colors
+;
+; MODIFICATION HISTORY:
+;       Written by:  Joop Schaye (jschaye@astro.rug.nl), Sep 1996.
+;       Added /OVER keyword   Theo Brauers (th.brauers@fz-juelich.de) Jul 2002
+;       Added VECCOLORS keyword. David Fanning (david@dfanning.com) March, 2005
+;       Incorporate the Coyote Graphics (cg) plot programs  WL  January 2011
+;       Allow VELX, VELY to include NaN values P. Blitzer/WL March 2013
+;       Allow NOCLIP=0 when overplotting  A. Negri    October 2014
+;-
+
+PRO partvelvec,velx,vely,posx,posy,x,y, OVER = over, VECCOLORS=vecColors, $
+               FRACTION=fraction,LENGTH=length,COLOR=color,WINDOW=window, $
+	       NOCLIP=noclip, _EXTRA=extra
+
+
+;---------------------------------------------
+; Various settings, modify these to customize
+;---------------------------------------------
+
+c = {customize, $
+   length: 0.08, $     ; Maximum vector length relative to plot region. (*)
+   lengtharrow: 0.3, $ ; Length of arrowhead legs relative to vectorlength.
+   angle: 22.5 }       ; 1/2 times the angle between the arrowhead legs.
+
+; (*) Not used if keyword LENGTH is present
+
+
+;---------------------
+; Some error handling
+;---------------------
+
+on_error,2  ; Return to caller if an error occurs.
+
+nparams=n_params()
+IF nparams NE 4 THEN BEGIN
+    IF (nparams NE 5 AND nparams NE 6) THEN BEGIN
+        message,'Wrong number of parameters!',/continue
+        message,'Syntax: PARTVELVEC, VELX, VELY, POSX, POSY [, X, Y]', $
+          /noname,/noprefix
+    ENDIF
+    IF nparams EQ 5 THEN y=x
+    sizex = size(x)
+    sizey = size(y)
+    IF (sizex[0] NE 1 || sizey[0] NE 1) THEN $
+      message,'X and Y must be vectors!'
+ENDIF
+
+sizevelx = size(velx)
+sizevely = size(vely)
+sizeposx = size(posx)
+sizeposy = size(posy)
+
+IF (total(sizevelx[0:sizevelx[0]]-sizevely[0:sizevelx[0]]) NE 0 $
+    || total(sizevelx[0:sizevelx[0]]-sizeposx[0:sizevelx[0]]) NE 0 $
+    || total(sizevelx[0:sizevelx[0]]-sizeposy[0:sizevelx[0]]) NE 0) THEN $
+  message,'All arguments must have the same dimension and size!'
+
+IF n_elements(fraction) GT 0 THEN $
+  IF (fraction LT 0.0 || fraction GT 1.0) THEN $
+  message,'Fraction has to be between 0.0 and 1.0.'
+
+
+;--------------
+; Prepare plot
+;--------------
+
+ nvecs = n_elements(velx)  ; Number of particles.
+ vel = sqrt(velx^2+vely^2)  ; Total velocity.
+ maxvel = max(vel,/nan)  ; Maximum velocity.
+
+; Compute maximum length of vectors.
+IF n_elements(length) LE 0 THEN length=c.length
+minposx = min(posx)
+maxposx = max(posx)
+minposy = min(posy)
+maxposy = max(posy)
+length = length*((maxposx-minposx) > (maxposy-minposy))
+
+; Convert velocities.
+vx = length*velx/maxvel
+vy = length*vely/maxvel
+vel = length*temporary(vel)/maxvel
+
+; Make sure no vectors extend beyond the plot data window.
+x1 = posx+vx  ; End of vector.
+y1 = posy+vy
+IF nparams EQ 4 THEN BEGIN
+    minposx = min(x1)<minposx
+    maxposx = max(x1)>maxposx
+    minposy = min(y1)<minposy
+    maxposy = max(y1)>maxposy
+ENDIF
+
+angle = c.angle*!dtor  ; Convert from degrees to radians.
+sinangle = sin(angle)  ; Need these.
+cosangle = cos(angle)
+
+
+;-----------
+; Plot axes
+;-----------
+
+if N_elements(color) EQ 0 then color = cgcolor('opposite')
+IF n_elements(veccolors) EQ 0 THEN BEGIN
+   veccolors = Replicate(cgcolor('opposite'), nvecs)
+ENDIF ELSE BEGIN
+   nvc = N_Elements(veccolors)
+   CASE nvc OF
+      1: veccolors = Replicate(veccolors, nvecs)
+      nvecs:
+      ELSE: Message, 'Vector color array VECCOLORS must be same size as VELX.'
+   ENDCASE
+ENDELSE
+IF n_elements(over) EQ 0 THEN BEGIN
+IF nparams EQ 4 THEN $
+  cgPlot,[minposx,maxposx],[minposy,maxposy], axiscolor=color,$
+  /nodata,/xstyle,/ystyle,COLOR=color,window=window,_EXTRA=extra $
+ELSE cgPlot,x,y,/nodata,/xstyle,/ystyle,COLOR=color,window=window,_EXTRA=extra
+ENDIF
+if keyword_set(window) then cgcontrol,execute=0
+;--------------
+; Plot vectors
+;--------------
+
+IF (n_elements(fraction) GT 0) && (fraction NE 1.0) THEN BEGIN
+   nrgood=long(fraction*nvecs)  ; # of vectors to plot.
+    IF nrgood EQ 0 THEN return
+    ; Compute indices of vectors to plot. I use two lines to get more
+    ; random "random numbers".
+    good=long(randomu(seed,nrgood+1)*(nvecs-1.0))
+    good=good[1:*]
+    vx = temporary(vx[good])
+    vy = temporary(vy[good])
+    px = posx[good]  ; Can't use temporary if we want to keep the data.
+    py = posy[good]
+    x1 = temporary(x1[good])
+    y1 = temporary(y1[good])
+    nvecs=nrgood
+ENDIF ELSE BEGIN
+    px=posx
+    py=posy
+ENDELSE
+
+FOR i=0l,nvecs-1l DO BEGIN  ; Loop over particles.
+    ; Note that we cannot put the next three lines outside the loop,
+    ; because we want the arrow size to be relative to the vector length.
+    r = c.lengtharrow*vel[i]  ; Length of arrow head.
+    rsin = r*sinangle
+    rcos = r*cosangle
+    ; Draw basis, arrow leg, same arrow leg, other arrow leg.
+    ; One arrow leg is drawn twice, because we need to return to the end
+    ; of the vector to draw the other leg.
+
+    cgPlots,[px[i],x1[i],x1[i]-(vx[i]*rcos+vy[i]*rsin)/vel[i], $
+           x1[i],x1[i]-(vx[i]*rcos-vy[i]*rsin)/vel[i]], $
+          [py[i],y1[i],y1[i]-(vy[i]*rcos-vx[i]*rsin)/vel[i], $
+           y1[i],y1[i]-(vy[i]*rcos+vx[i]*rsin)/vel[i]],COLOR=veccolors[i],$
+	   ADDCMD = window, noclip=noclip
+	
+ENDFOR
+ if keyword_set(window) then cgcontrol,execute=1
+ return
+END  ; End of procedure PARTVELVEC.
diff --git a/Code/script_idl_mv/astrolib/pca.pro b/Code/script_idl_mv/astrolib/pca.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6004f48d8ae54c0ac48cff604a882474b1933598
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/pca.pro
@@ -0,0 +1,264 @@
+PRO PCA, data, eigenval, eigenvect, percentages, proj_obj, proj_atr, $
+      MATRIX=AM,TEXTOUT=textout,COVARIANCE=cov,SSQ=ssq,SILENT=silent
+     
+;+
+; NAME:
+;    PCA
+;
+; PURPOSE:
+;    Carry out a Principal Components Analysis (Karhunen-Loeve Transform)
+; EXPLANATION:
+;    Results can be directed to the screen, a file, or output variables
+;    See notes below for comparison with the intrinsic IDL function PCOMP.
+;
+; CALLING SEQUENCE:
+;    PCA, data, eigenval, eigenvect, percentages, proj_obj, proj_atr, 
+;             [MATRIX =, TEXTOUT = ,/COVARIANCE, /SSQ, /SILENT ]
+;
+; INPUT PARAMETERS:
+;     data -  2-d data matrix, data(i,j) contains the jth attribute value
+;               for the ith object in the sample.    If N_OBJ is the total
+;               number of objects (rows) in the sample, and N_ATTRIB is the 
+;               total number of attributes (columns) then data should be
+;               dimensioned N_OBJ x N_ATTRIB.         
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS:
+;     /COVARIANCE - if this keyword is set, then the PCA will be carried out
+;              on the covariance matrix (rare), the default is to use the
+;              correlation matrix
+;     /SILENT - If this keyword is set, then no output is printed
+;     /SSQ - if this keyword is set, then the PCA will be carried out on
+;               on the sums-of-squares & cross-products matrix (rare)
+;     TEXTOUT - Controls print output device, defaults to !TEXTOUT
+;
+;              textout=1       TERMINAL using /more option
+;              textout=2       TERMINAL without /more option
+;              textout=3       <program>.prt
+;              textout=4       laser.tmp
+;              textout=5      user must open file
+;              textout = filename (default extension of .prt)
+;
+; OPTIONAL OUTPUT PARAMETERS:
+;     eigenval -  N_ATTRIB element vector containing the sorted eigenvalues
+;     eigenvect - N_ATRRIB x N_ATTRIB matrix containing the corresponding 
+;               eigenvectors
+;     percentages - N_ATTRIB element containing the cumulative percentage 
+;             variances associated with the principal components
+;     proj_obj - N_OBJ by N_ATTRIB matrix containing the projections of the 
+;             objects on the principal components
+;     proj_atr - N_ATTRIB by N_ATTRIB matrix containing the projections of 
+;               the attributes on the principal components
+;
+; OPTIONAL OUTPUT PARAMETER
+;      MATRIX   = analysed matrix, either the covariance matrix if /COVARIANCE
+;              is set, the "sum of squares and cross-products" matrix if
+;              /SSQ is set, or the (by default) correlation matrix.    Matrix
+;              will have dimensions N_ATTRIB x N_ATTRIB
+;
+; NOTES:
+;      This procedure performs Principal Components Analysis (Karhunen-Loeve
+;      Transform) according to the method described in "Multivariate Data 
+;      Analysis" by Murtagh & Heck [Reidel : Dordrecht 1987], pp. 33-48.
+;      See  http://www.classification-society.org/csna/mda-sw/pca.f
+;
+;      Keywords /COVARIANCE and /SSQ are mutually exclusive.
+;
+;      The printout contains only (at most) the first seven principle 
+;      eigenvectors.    However, the output variables EIGENVECT contain 
+;      all the eigenvectors
+;       
+;      Different authors scale the covariance matrix in different ways.
+;      The eigenvalues output by PCA may have to be scaled by 1/N_OBJ or
+;      1/(N_OBJ-1) to agree with other calculations when /COVAR is set.
+;
+;      PCA uses the non-standard system variables !TEXTOUT and !TEXTUNIT.
+;      These can be added to one's session using the procedure ASTROLIB.
+;
+;      The intrinsic IDL function PCOMP  duplicates most
+;      most of the functionality of PCA, but uses different conventions and
+;      normalizations.   Note the following:
+;
+;   (1) PCOMP requires a N_ATTRIB x N_OBJ input array; this is the transpose
+;         of what PCA expects
+;   (2) PCA uses standardized variables for the correlation matrix:  the input 
+;        vectors are set to a  mean of zero and variance of one and divided by 
+;        sqrt(n); use the /STANDARDIZE keyword to PCOMP for a direct comparison.
+;   (3) PCA (unlike PCOMP) normalizes the eigenvectors by the square root
+;         of the eigenvalues.
+;   (4) PCA returns cumulative percentages; the VARIANCES keyword of PCOMP
+;         returns the variance in each variable
+;   (5) PCOMP divides the eigenvalues by (1/N_OBJ-1) when the covariance matrix
+;          is used.
+;
+; EXAMPLE:
+;      Perform a PCA analysis on the covariance matrix of a data matrix, DATA,
+;      and write the results to a file
+;
+;      IDL> PCA, data, /COVAR, t = 'pca.dat'
+;
+;      Perform a PCA analysis on the correlation matrix.   Suppress all 
+;      printing, and save the eigenvectors and eigenvalues in output variables
+;
+;      IDL> PCA, data, eigenval, eigenvect, /SILENT
+;
+; PROCEDURES CALLED:
+;      TEXTOPEN, TEXTCLOSE
+;
+; REVISION HISTORY:
+;      Immanuel Freedman (after Murtagh F. and Heck A.).     December 1993
+;      Wayne Landsman, modified I/O              December 1993
+;      Fix MATRIX output, remove GOTO statements   W. Landsman August 1998      
+;      Changed some index variable to type LONG    W. Landsman March 2000
+;      Fix error in computation of proj_atr, see Jan 1990 fix in 
+;       http://astro.u-strasbg.fr/~fmurtagh/mda-sw/pca.f   W. Landsman Feb 2008
+;- 
+  compile_opt idl2
+  On_Error,2     ;return to user if error
+
+; Constants
+  TOLERANCE = 1.0E-5       ; are array elements near-zero ?
+
+; Dispatch table
+
+ IF N_PARAMS() EQ 0  THEN BEGIN
+  print,'Syntax  - PCA, data, [eigenval, eigenvect, percentages, proj_obj, proj_atr,'
+  print,'               [MATRIX =, /COVARIANCE, /SSQ, /SILENT, TEXTOUT=]'
+  RETURN
+ ENDIF 
+
+;Define nonstandard system variables if not already present
+
+  defsysv, '!TEXTUNIT', exist = exist
+     if ~exist then  defsysv, '!TEXTUNIT', 0
+  defsysv, '!TEXTOUT', exist = exist
+     if ~exist then defsysv, '!TEXTOUT', 1
+
+  
+  if size(data,/N_dimen)  NE 2 THEN BEGIN 
+    HELP,data
+    MESSAGE,'ERROR - Data matrix is not two-dimensional'
+  ENDIF
+
+  dimen = size(data,/dimen) 
+  Nobj = dimen[0]   &  Mattr = dimen[1]      ;Number of objects and attributes
+
+
+  IF KEYWORD_SET(cov) THEN BEGIN
+        msg = 'Covariance matrix will be analyzed'
+; form column-means
+        column_mean = total( data,1 )/Nobj
+	temp = replicate(1.0, Nobj)
+        X = (data - temp # transpose(column_mean))
+  ENDIF ELSE $
+  IF KEYWORD_SET(ssq) THEN BEGIN
+
+        msg = 'Sum-of-squares & cross-products matrix will be analyzed'
+        X = data 
+
+   ENDIF ELSE BEGIN
+        msg = 'Default: Correlation matrix will be analyzed' 
+; form column-means
+        temp = replicate( 1.0, Nobj )
+        column_mean = (temp # data)/ Nobj
+        X = (data - temp # transpose(column_mean))
+        S = sqrt(temp # (X*X)) & X = X/(temp # S)
+         
+   ENDELSE
+
+ A = transpose(X) # X
+ if arg_present(AM) then AM = A
+
+; Carry out eigenreduction
+ trired, A, D, E              ; D contains diagonal, E contains off-diagonal
+ triql, D, E, A               ; D contains the eigen-values, A(*,i) -vectors
+
+; Use TOLERANCE to decide if eigenquantities are sufficiently near zero
+
+ index = where(abs(D) LE TOLERANCE*MAX(abs(D)),count) 
+ if count NE 0 THEN D[index]=0
+ index = where(abs(A) LE TOLERANCE*MAX(abs(A)),count) 
+ if count NE 0 THEN A[index]=0
+
+ index = sort(D)                   ; Order by increasing eigenvalue
+ D = D[index] & E=E[index]
+ A = A[*,index]
+
+; Eigenvalues expressed as percentage variance and ...
+ W1 = 100.0 * reverse(D)/total(D)
+
+;... Cumulative percentage variance
+ W = total(W1, /cumul)
+
+;Define returned parameters
+ eigenval = reverse(D)
+ eigenvect = reverse(transpose(A))
+ percentages = W
+
+; Output eigen-values and -vectors 
+
+  if ~keyword_set(SILENT) then begin
+;       Open output file 
+        if ~keyword_set( TEXTOUT ) then TEXTOUT = textout
+        textopen,'PCA', TEXTOUT = textout
+        printf,!TEXTUNIT,'PCA: ' + systime()
+        sz1 = strtrim( Nobj,2) & sz2 = strtrim( Mattr, 2 )
+        printf,!TEXTUNIT, 'Data  matrix has '+ sz1 + ' objects with up to ' + $
+                 sz2 + ' attributes'
+        printf,!TEXTUNIT, msg 
+        printf,!TEXTUNIT, " "
+        printf,!TEXTUNIT, $ 
+                '   Eigenvalues     As Percentages       Cumul. percentages'
+        for i = 0L, Mattr-1 do $
+        printf,!TEXTUNIT, eigenval[i], W1[i], percentages[i] ,f = '(3f15.4)'
+        printf,!TEXTUNIT," "
+        printf,!TEXTUNIT, 'Corresponding eigenvectors follow...'
+        Mprint = Mattr < 7
+        header = ' VBLE  '
+        for i = 1, Mprint do header = header + '  EV-' + strtrim(i,2) + '   '
+        printf,!TEXTUNIT, header
+        for i = 1L, Mattr do printf,!TEXTUNIT, $
+                 i, eigenvect[0:Mprint-1,i-1],f='(i4,7f9.4)'
+  endif
+
+; Obtain projection of row-point on principal axes  (Murtagh & Heck convention)
+ projx = X # A
+
+; Use TOLERANCE again...
+ index = where(abs(projx) LE TOLERANCE*MAX(abs(projx)),count)
+ if count NE 0 THEN projx[index]=0
+ proj_obj = reverse( transpose(projx) )
+
+ if ~keyword_set( SILENT ) then begin
+         printf,!TEXTUNIT,' '
+         printf,!TEXTUNIT, 'Projection of objects on principal axes ...'
+         printf,!TEXTUNIT,' '
+         header = ' VBLE  '
+         for i = 1, Mprint do header = header + 'PROJ-' + strtrim(i,2) + '   '
+         printf,!TEXTUNIT, header 
+         for i = 0L, Nobj-1 do printf,!TEXTUNIT, $
+                i+1, proj_obj[0:Mprint-1,i], f='(i4,7f9.4)'
+ endif
+
+; Obtain projection of column-points on principal axes
+ projy = transpose(projx)#X
+
+; Use TOLERANCE again...
+ index = where(abs(projy) LE TOLERANCE*MAX(abs(projy)),count)
+ if count NE 0 THEN projy[index] = 0
+
+; scale by square root of eigenvalues...
+ temp = replicate( 1.0, Mattr )
+ proj_atr = reverse(projy)/(sqrt(eigenval)#temp)
+
+ if ~keyword_set( SILENT ) then begin
+        printf,!TEXTUNIT,' '
+        printf,!TEXTUNIT,'Projection of attributes on principal axes ...'
+        printf,!TEXTUNIT,' '
+        printf,!TEXTUNIT, header
+        for i = 0L, Mattr-1 do printf,!TEXTUNIT, $
+                i+1, proj_atr[0:Mprint-1,i], f='(i4,7f9.4)'
+         textclose, TEXTOUT = textout           ; Close output file  
+ endif
+
+ RETURN
+ END
diff --git a/Code/script_idl_mv/astrolib/pent.pro b/Code/script_idl_mv/astrolib/pent.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7461f684ad90967a3260b1871b32690684d8fc83
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/pent.pro
@@ -0,0 +1,145 @@
+ function pent,p,t,x,m,n
+;+
+; NAME:
+;       PENT
+; PURPOSE:
+;       Return the information entropy of a time series
+; EXPLANATION:
+;       This function will return S, the information entropy of a time series
+;       for a set of trial periods 
+;
+; CATEGORY:
+;       Time series analysis, period finding, astronomical utilities.
+;
+; CALLING SEQUENCE:
+;       Result = PENT(P, T, X, [N, M ] )
+;
+; INPUTS:
+;       P - array of trial period values.
+;       T - array of observation times (same units as P).
+;       X - array of observations.
+;
+; OPTIONAL INPUTS:
+;       N   - If  four parameters are given then the 4th parameter is assumed
+;               to be N. Then NxN boxes are used to calculate S.
+;       M,N - If five parameters are given then parameter 4 is M and parameter
+;               5 is N. S is then calculated using MxN boxes - M partitions for the
+;               phase and N partitions for the data.
+;       
+; OUTPUTS:
+;       This function returns S, the information entropy of the time series for
+;       the periods given in P as defined by Cincotta, Me'ndez & Nu'n~ez
+;       (Astrophysical Journal 449, 231-235, 1995). The minima of S occur at
+;       values of P where X shows periodicity.
+;   
+; PROCEDURE:
+;       The procedure involves dividing the phase space into N^2 partitions 
+;       (NxN boxes) and then calculating:
+;       
+;               __ N^2
+;         S = - \        mu_i . ln(mu_i)  for all mu_i <> 0
+;               /_  
+;                 i = 1 
+;
+;       where  mu_i is the number of data points in partition i normalised by 
+;       the number of partitions.
+;
+;       The option of using MxN boxes is an additional feature of this routine.
+;
+; EXAMPLE:
+;
+;       To generate a similar synthetic data set to Cincotta et al. we
+;        do the following:
+;
+;       IDL> P0 = 173.015                        ; Fundamental period
+;       IDL> T = randomu(seed,400)*15000         ; 400 random observation times
+;       IDL> A0 = 14.0                           ; Mean magnitude
+;       IDL> M0 = -0.5  * sin(2*!pi*T/P0)        ; Fundamental mode
+;       IDL> M1 = -0.15 * sin(4*!pi*T/P0)        ; 1st harmonic
+;       IDL> M2 = -0.05 * sin(6*!pi*T/P0)        ; 2nd harmonic
+;       IDL> sig = randomu(seed,400)*0.03        ; noise
+;       IDL> U = A0 + M0 + M1 + M2 + sig         ; Synthetic data
+;       IDL> Ptest = 100. + findgen(2000)/2.     ; Trial periods 
+;       IDL> S = pent(Ptest,T,U)                 ; Calculate S
+;               ... this takes a few seconds ...
+;       IDL> plot,Ptest,S,xtitle="P",ytitle="S"  ; plot S v. P
+;       IDL> print,Ptest(where(S eq min(S)))     ; Print best period (+/- 0.5)
+;
+;       The plot produced should be similar to Fig. 2 of Cincotta et al.
+;
+; RESTRICTIONS:
+;
+;       My own (limited) experience with this routine suggests that it is not
+;       as good as other techniques for finding  weak,  multi-periodic signals in 
+;       poorly sampled  data, but is good for establishing periods of eclipsing
+;       binary stars when M is quite large (try MxN = 64x16, 128x16 or even 
+;       256x16).  This suggests it may be good for other periodic light curves 
+;       (Cepheids, RR Lyrae etc.).
+;       I would be glad to receive reports of other peoples experience with
+;       this technique (e-mail pflm@bro730.astro.ku.dk).
+;
+; MODIFICATION HISTORY:
+;       Written by:   Pierre Maxted, 14Sep95
+;       Modifications:
+;       Normalisation of S corrected, T-min(T) taken out of loop.
+;               -  Pierre Maxted, 15Sep95
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+ on_error,2 ; return to caller
+
+; Check suitable no. of parameters have been entered.
+
+  case N_params() of 
+   3 : begin
+        n = 8.0 
+        m = 8.0
+       end
+   4 : begin
+        n = float(fix(m)) 
+        m = n
+       end
+   5 : begin
+        m = float(fix(m)) 
+        n = float(fix(n))
+       end
+  else : message,/noname,' Syntax - Result = ( P, T, X [ [,M ] ,N ])'
+  endcase
+
+  nbox = m*n
+  np = n_elements(p)
+  npts = n_elements(x)
+
+  if n_elements(t) ne  npts  then message , $
+     'Input arrays T and X  must have same number of elements'
+
+  if npts lt 3 then message,' Insufficient data in input arrays'
+
+  npts = float(npts)
+
+  S = fltarr(np)
+ 
+   norm = (X - min(X))/(max(x) - min(x))   ; normalised data
+   norm = norm - (norm eq 1.0)*(0.1/n) ; norm = 1 -> norm = 0.99..
+   ni = 1 + n*(floor(norm*n))
+
+   Tplus = T-min(T)  ; take this operation out of the loop
+ 
+  for j = 0l,np - 1l do begin
+ 
+   phi = ( Tplus / P[j] ) mod  1.0
+ 
+   mu = histogram(floor(phi*m) + ni,max=nbox,min=0.0)/(npts)
+ 
+   mu = mu[where(mu gt 0.0)]
+   S[j] = -total(mu*alog(mu))
+   
+  endfor
+  
+  S = S/alog(nbox) ; normalise S
+
+  return,S
+
+end   ; That's all folks
+
+
diff --git a/Code/script_idl_mv/astrolib/permute.pro b/Code/script_idl_mv/astrolib/permute.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7baea7474e4dbce7479c27a6eb80f07eaf989979
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/permute.pro
@@ -0,0 +1,122 @@
+;+
+; NAME:
+;	PERMUTE
+;
+; PURPOSE:
+;	This function returns an array containing the numbers
+;	[0, ..., N-1] in random order.  They are useful as indices
+;	when permuting a dataset, for example in a balanced bootstrap
+;	Monte Carlo algorithm.      
+;
+; CATEGORY:
+;	Statistics.
+;
+; CALLING SEQUENCE:
+;
+;	Result = PERMUTE(N)
+;
+; INPUTS:
+;	N:	The number of items to be permuted.
+;
+; OPTIONAL INPUTS:
+;	SEED:	A random number seed, see RANDOMU.
+;
+; OUTPUTS:
+;	This function returns an N-element array containing a random
+;	permutation of the integers from 0 through N-1.
+;
+; SIDE EFFECTS:
+;	Unless Seed is specified, IDL's global random number
+;	seed is changed. 
+;
+; PROCEDURE:
+;	This is an in-place swapping algorithm.  It starts with an
+;	index array.  For each position in the array, it swaps the
+;	occupant of that position with the occupant of a random
+;	position from there (inclusive) to the end of the array.  The
+;	last iteration is not necessary to compute, since it swaps
+;	with itself.
+;
+;	See http://www.techuser.net/randpermgen.html for a proof.  The
+;	2-line code there has been optimized for IDL's vector
+;	architecture.  This is a linear-time algorithm.
+;
+; EXAMPLE:
+; Show some permutations of 6 numbers:
+;	print, permute(6)
+;	    0           2           1           3           4           5
+;	print, permute(6)
+;           2           4           3           5           1           0
+;	print, permute(6)
+;	    0           4           3           1           2           5
+;
+; Permute the array [2, 4, 6, 8]
+;	a = [2, 4, 6, 8]
+;	print, a[permute(4)]
+;       4       8       6       2
+;
+; Test randomness (results should be close to k):
+; m = 6l
+; k = 10000l
+; n = m * k
+; a = lonarr(m, n)
+; for i = 0l, n-1, 1 do a[*, i] = permute(m)
+; for i = 0l, m-1, 1 do print, histogram(a[i, *])
+;         9885       10062       10051        9915       10028       10059
+;        10096       10087       10094        9913        9933        9877
+;        10041       10013        9968        9958        9911       10109
+;         9880        9858       10166       10049       10081        9966
+;        10093        9915        9800       10166        9969       10057
+;        10005       10065        9921        9999       10078        9932
+;
+; Time the algorithm:
+; maxn = 7
+; t = dblarr(maxn)
+; n = 10L^(indgen(maxn)+1)
+; for i = 0, maxn-1, 1 do begin &$
+;   t1 = systime(/s) &$
+;   print, n[i] &$
+;   a = permute(n[i]) &$
+;   t2 = systime(/s) &$
+;   t[i] = t2-t1 &$
+; endfor
+; print, '        Elements         Seconds   Elements Per Second'
+; print, transpose([[n], [t], [t/n]])
+; 
+;         Elements         Seconds   Elements Per Second
+;        10.000000   0.00012397766   1.2397766e-05
+;        100.00000   0.00015020370   1.5020370e-06
+;        1000.0000    0.0011651516   1.1651516e-06
+;        10000.000     0.018178225   1.8178225e-06
+;        100000.00      0.13504505   1.3504505e-06
+;        1000000.0       1.3817160   1.3817160e-06
+;        10000000.       14.609985   1.4609985e-06
+;
+; These times are for a 2.071 GHz AMD Athlon 2800+ CPU.
+;
+; MODIFICATION HISTORY:
+; 	Written by:	Joseph Harrington, Cornell.  2006-03-22
+;			jh@alum.mit.edu
+;-
+function PERMUTE, N, Seed
+
+; Don't stop here!
+on_error, 2
+
+; test inputs
+if n eq 1 then return, 0L
+if n lt 1 then message, 'N = ' + strtrim(n, 2) + ', must be 1 or more.'
+
+ar  = lindgen(n)
+rar = reverse(ar[0 : n - 2]) + 2
+r   = (n - 1) - long( randomu(seed, n - 1) * rar )
+
+for i = 0L, n - 2, 1 do begin
+  t = ar[i]
+  ar[i] = ar[r[i]]
+  ar[r[i]] = t
+endfor
+
+return, ar
+end
+
diff --git a/Code/script_idl_mv/astrolib/pixcolor.pro b/Code/script_idl_mv/astrolib/pixcolor.pro
new file mode 100644
index 0000000000000000000000000000000000000000..764086370e17f34a1428cb7d21d29f0d8f1aa265
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/pixcolor.pro
@@ -0,0 +1,100 @@
+pro pixcolor, pix_value, color
+;+
+; NAME:
+;	PIXCOLOR
+; PURPOSE:
+;	Assign colors to specified pixel values in a color lookup table
+; EXPLANATION:
+;       Colors can be specified either from the list in cgcolor 
+;       (http://www.idlcoyote.com/programs/cgcolor.pro ) or as 1 letter 
+;       abbreviations for 8 common colors.
+;
+; CALLING SEQUENCE:
+;      	PIXCOLOR, pixvalue, color         ;Set color at specified pixel values
+;
+; INPUT PARMETERS:
+;	pixvalue - value or range of pixel values whose color will be modified.
+;		A single pixel value may be specified by an integer
+;		If a range of values is specified, then it must be written
+;		as a string, with a colon denoting the range (e.g.'102:123')
+;		If omitted, program will prompt for this parameter.
+;
+;  OPTIONAL INPUT PARAMETER
+;	color - scalar string specifying either a full color name available in
+;               CGCOLOR, or a  single character string giving one of the 
+;               specified colors: 'R' (red), 'B' (blue), 'G' (green)
+;		'Y' (yellow), 'T' (turquoise), 'V' (violet), 'W' (white)
+;		or 'D' (dark).  If omitted, program will prompt for this 
+;		parameter.
+;
+; OUTPUTS:
+;	None
+; PROCEDURE:
+;	TVLCT is used in RGB mode to load the specified pixel values.
+;
+; EXAMPLE:
+;	Set pixel values of 245 to a color of red
+;
+;	IDL> pixcolor,245,'R'
+;
+;       Set pixel values 120 to 150 to Magenta
+;
+;       IDL> pixcolor,'120:150','Magenta'      
+; REVISION HISTORY:
+;	Written, W. Landsman ST Systems Corp.		February, 1987
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Allow specification of cgcolor names   April 2011
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() EQ 0 then begin 
+     print,'Syntax - pixcolor, value, color_name'
+     return
+ endif 
+     
+ if ( N_elements(pix_value) EQ 0) then begin
+	pix_value = ''
+	print,'Enter pixel value(s) to be assigned a color value'
+	print,'Value may be either number or a range (e.g. 102:123)'
+	read,'Pixel Value(s): ',pix_value
+ endif
+
+ type = size(pix_value)
+ if ( type[1] EQ 7 ) then begin
+	pixmin = fix(gettok(pix_value,':')) >0
+	if strlen(pix_value) eq 0 then pixmax = fix(pixmin)  $
+		else pixmax = fix(pix_value) > pixmin < 255
+ endif else begin                                               
+	pixmin = fix(pix_value)>0<255
+	pixmax = pixmin
+ endelse 
+ npts = pixmax - pixmin + 1
+
+GETCOL: if ( N_params() LT 2 ) then begin
+	color = ''
+	print,'Enter color name to which pixel(s) will be asssigned'
+	print,'Available 1 character options are '
+	print,'Red (R), Blue (B), Green (G), Yellow (Y), Turquoise (T),
+	print,'Violet (V), White (W), or Dark (D)
+        read,color
+ endif
+
+ case strupcase(color) of
+	'R': col = 'red'
+	'G': col = 'green'
+	'B': col = 'blue'
+	'Y': col = 'yellow'
+	'T': col = 'turquoise'
+	'V': col = 'violet
+	'W': col = 'white'
+	'D': col = 'black'
+	else: col = color
+ endcase
+
+ cc = cgcolor(col,/triple)
+ if npts GT 1 then cc = rebin(cc,npts,3)
+ tvlct,cc,pixmin
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/pixwt.pro b/Code/script_idl_mv/astrolib/pixwt.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3dc8233fb523d83dbae2c425297db2befb92f6e7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/pixwt.pro
@@ -0,0 +1,257 @@
+;+
+; NAME:
+;	PIXWT
+; PURPOSE: 
+;	Circle-rectangle overlap area computation.
+; DESCRIPTION:
+;	Compute the fraction of a unit pixel that is interior to a circle.
+;	The circle has a radius r and is centered at (xc, yc).  The center of
+;	the unit pixel (length of sides = 1) is at (x, y).
+;
+; CATEGORY:
+;	CCD data processing
+; CALLING SEQUENCE:
+;	area = Pixwt( xc, yc, r, x, y )
+; INPUTS:
+;	xc, yc : Center of the circle, numeric scalars
+;	r      : Radius of the circle, numeric scalars
+;	x, y   : Center of the unit pixel, numeric scalar or vector
+; OPTIONAL INPUT PARAMETERS:
+;	None.
+; KEYWORD PARAMETERS:
+;	None.
+; OUTPUTS:
+;	Function value: Computed overlap area.
+; EXAMPLE:
+;       What is the area of overlap of a circle with radius 3.44 units centered
+;       on the point 3.23, 4.22 with the pixel centered at [5,7]
+;
+;       IDL> print,pixwt(3.23,4.22,3.44,5,7)  ==>  0.6502
+; COMMON BLOCKS:
+;    None.
+; PROCEDURE:
+;	Divides the circle and rectangle into a series of sectors and
+;	triangles.  Determines which of nine possible cases for the
+;	overlap applies and sums the areas of the corresponding sectors
+;	and triangles.    Called by aper.pro
+;
+; NOTES:
+;      If improved speed is needed then a C version of this routines, with
+;      notes on how to linkimage it to IDL is available at   
+;       ftp://ftp.lowell.edu/pub/buie/idl/custom/
+;
+; MODIFICATION HISTORY:
+;     Ported by Doug Loucks, Lowell Observatory, 1992 Sep, from the
+;    routine pixwt.c, by Marc Buie.
+;-
+; ---------------------------------------------------------------------------
+; Function Arc( x, y0, y1, r )
+;
+; Compute the area within an arc of a circle.  The arc is defined by
+; the two points (x,y0) and (x,y1) in the following manner:  The circle
+; is of radius r and is positioned at the origin.  The origin and each
+; individual point define a line which intersects the circle at some
+; point.  The angle between these two points on the circle measured
+; from y0 to y1 defines the sides of a wedge of the circle.  The area
+; returned is the area of this wedge.  If the area is traversed clockwise
+; then the area is negative, otherwise it is positive.
+; ---------------------------------------------------------------------------
+FUNCTION Arc, x, y0, y1, r
+RETURN, 0.5 * r*r * ( ATAN( FLOAT(y1)/FLOAT(x) ) - ATAN( FLOAT(y0)/FLOAT(x) ) )
+END
+
+
+; ---------------------------------------------------------------------------
+; Function Chord( x, y0, y1 )
+;
+; Compute the area of a triangle defined by the origin and two points,
+; (x,y0) and (x,y1).  This is a signed area.  If y1 > y0 then the area
+; will be positive, otherwise it will be negative.
+; ---------------------------------------------------------------------------
+FUNCTION Chord, x, y0, y1
+RETURN, 0.5 * x * ( y1 - y0 )
+END
+
+
+; ---------------------------------------------------------------------------
+; Function Oneside( x, y0, y1, r )
+;
+; Compute the area of intersection between a triangle and a circle.
+; The circle is centered at the origin and has a radius of r.  The
+; triangle has verticies at the origin and at (x,y0) and (x,y1).
+; This is a signed area.  The path is traversed from y0 to y1.  If
+; this path takes you clockwise the area will be negative.
+; ---------------------------------------------------------------------------
+FUNCTION Oneside, x, y0, y1, r
+
+true = 1
+size_x  = SIZE( x )
+
+CASE size_x[ 0 ] OF
+   0    : BEGIN
+      IF x EQ 0 THEN RETURN, x
+      IF ABS( x ) GE r THEN RETURN, Arc( x, y0, y1, r )
+      yh = SQRT( r*r - x*x )
+      CASE true OF
+         ( y0 LE -yh ) : BEGIN
+            CASE true OF
+               ( y1 LE -yh ) : RETURN, Arc( x, y0, y1, r )
+               ( y1 LE  yh ) : RETURN, Arc( x, y0, -yh, r ) $
+                               + Chord( x, -yh, y1 )
+               ELSE          : RETURN, Arc( x, y0, -yh, r ) $
+                               + Chord( x, -yh, yh ) + Arc( x, yh, y1, r )
+            ENDCASE
+         END
+
+         ( y0 LT  yh ) : BEGIN
+            CASE true OF
+               ( y1 LE -yh ) : RETURN, Chord( x, y0, -yh ) $
+                               + Arc( x, -yh, y1, r )
+               ( y1 LE  yh ) : RETURN, Chord( x, y0, y1 )
+               ELSE          : RETURN, Chord( x, y0, yh ) + Arc( x, yh, y1, r )
+            ENDCASE
+         END
+
+         ELSE          : BEGIN
+            CASE true OF
+               ( y1 LE -yh ) : RETURN, Arc( x, y0, yh, r ) $
+                               + Chord( x, yh, -yh ) + Arc( x, -yh, y1, r )
+               ( y1 LE  yh ) : RETURN, Arc( x, y0, yh, r ) + Chord( x, yh, y1 )
+               ELSE          : RETURN, Arc( x, y0, y1, r )
+            ENDCASE
+         END
+      ENDCASE
+   END
+
+   ELSE : BEGIN
+      ans = x
+      t0 = WHERE( x EQ 0, count )
+      IF count EQ n_elements( x ) THEN RETURN, ans
+
+      ans = x * 0
+      yh = ans
+      to = WHERE( ABS( x ) GE r, tocount )
+      ti = WHERE( ABS( x ) LT r, ticount )
+      IF tocount NE 0 THEN ans[ to ] = Arc( x[to], y0[to], y1[to], r )
+      IF ticount EQ 0 THEN RETURN, ans
+
+      yh[ ti ] = SQRT( r*r - x[ti]*x[ti] )
+
+      t1 = WHERE( y0[ti] LE -yh[ti], count )
+      IF count NE 0 THEN BEGIN
+         i = ti[ t1 ]
+
+         t2 = WHERE( y1[i] LE -yh[i], count )
+         IF count NE 0 THEN BEGIN
+            j = ti[ t1[ t2 ] ]
+            ans[j] =  Arc( x[j], y0[j], y1[j], r )
+         ENDIF
+
+         t2 = WHERE( ( y1[i] GT -yh[i] ) AND ( y1[i] LE  yh[i] ), count )
+         IF count NE 0 THEN BEGIN
+            j = ti[ t1[ t2 ] ]
+            ans[j] = Arc( x[j], y0[j], -yh[j], r ) $
+                   + Chord( x[j], -yh[j], y1[j] )
+         ENDIF
+
+         t2 = WHERE( y1[i] GT yh[i], count )
+         IF count NE 0 THEN BEGIN
+            j = ti[ t1[ t2 ] ]
+            ans[j] = Arc( x[j], y0[j], -yh[j], r ) $
+                   + Chord( x[j], -yh[j], yh[j] ) $
+                   + Arc( x[j], yh[j], y1[j], r )
+         ENDIF
+      ENDIF
+
+      t1 = WHERE( ( y0[ti] GT -yh[ti] ) AND ( y0[ti] LT yh[ti] ), count )
+      IF count NE 0 THEN BEGIN
+         i = ti[ t1 ]
+
+         t2 = WHERE( y1[i] LE -yh[i], count )
+         IF count NE 0 THEN BEGIN
+            j = ti[ t1[ t2 ] ]
+            ans[j] = Chord( x[j], y0[j], -yh[j] ) $
+                   + Arc( x[j], -yh[j], y1[j], r )
+         ENDIF
+
+         t2 = WHERE( ( y1[i] GT -yh[i] ) AND ( y1[i] LE  yh[i] ), count )
+         IF count NE 0 THEN BEGIN
+            j = ti[ t1[ t2 ] ]
+            ans[j] = Chord( x[j], y0[j], y1[j] )
+         ENDIF
+
+         t2 = WHERE( y1[i] GT yh[i], count )
+         IF count NE 0 THEN BEGIN
+            j = ti[ t1[ t2 ] ]
+            ans[j] = Chord( x[j], y0[j], yh[j] ) $
+                   + Arc( x[j], yh[j], y1[j], r )
+         ENDIF
+      ENDIF
+
+      t1 = WHERE( y0[ti] GE yh[ti], count )
+      IF count NE 0 THEN BEGIN
+         i = ti[ t1 ]
+
+         t2 = WHERE ( y1[i] LE -yh[i], count )
+         IF count NE 0 THEN BEGIN
+            j = ti[ t1[ t2 ] ]
+            ans[j] = Arc( x[j], y0[j], yh[j], r ) $
+                   + Chord( x[j], yh[j], -yh[j] ) $
+                   + Arc( x[j], -yh[j], y1[j], r )
+         ENDIF
+
+         t2 = WHERE( ( y1[i] GT -yh[i] ) AND ( y1[i] LE  yh[i] ), count )
+         IF count NE 0 THEN BEGIN
+            j = ti[ t1[ t2 ] ]
+            ans[j] = Arc( x[j], y0[j], yh[j], r ) $
+                   + Chord( x[j], yh[j], y1[j] )
+         ENDIF
+
+         t2 = WHERE( y1[i] GT yh[i], count )
+         IF count NE 0 THEN BEGIN
+            j = ti[ t1[ t2 ] ]
+            ans[j] = Arc( x[j], y0[j], y1[j], r )
+         ENDIF
+      ENDIF
+
+      RETURN, ans
+   END
+ENDCASE
+
+END
+
+
+; ---------------------------------------------------------------------------
+; Function Intarea( xc, yc, r, x0, x1, y0, y1 )
+;
+; Compute the area of overlap of a circle and a rectangle.
+;    xc, yc  :  Center of the circle.
+;    r       :  Radius of the circle.
+;    x0, y0  :  Corner of the rectangle.
+;    x1, y1  :  Opposite corner of the rectangle.
+; ---------------------------------------------------------------------------
+FUNCTION Intarea, xc, yc, r, x0, x1, y0, y1
+;
+; Shift the objects so that the circle is at the origin.
+;
+x0 = x0 - xc
+y0 = y0 - yc
+x1 = x1 - xc
+y1 = y1 - yc
+
+RETURN, Oneside( x1, y0, y1, r ) + Oneside( y1, -x1, -x0, r ) +$
+        Oneside( -x0, -y1, -y0, r ) + Oneside( -y0, x0, x1, r )
+
+END
+
+
+; ---------------------------------------------------------------------------
+; FUNCTION Pixwt( xc, yc, r, x, y )
+;
+; Compute the fraction of a unit pixel that is interior to a circle.
+; The circle has a radius r and is centered at (xc, yc).  The center of
+; the unit pixel (length of sides = 1) is at (x, y).
+; ---------------------------------------------------------------------------
+FUNCTION Pixwt, xc, yc, r, x, y
+RETURN, Intarea( xc, yc, r, x-0.5, x+0.5, y-0.5, y+0.5 )
+END
diff --git a/Code/script_idl_mv/astrolib/pkfit.pro b/Code/script_idl_mv/astrolib/pkfit.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5815e362c6550949613231d96a3d274e910367e0
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/pkfit.pro
@@ -0,0 +1,247 @@
+pro pkfit,f,scale,x,y,sky,radius,ronois,phpadu,gauss,psf, $
+                  errmag,chi,sharp,niter, DEBUG= debug
+;+
+; NAME:
+;	PKFIT
+; PURPOSE:
+;	Subroutine of  GETPSF to perform a one-star least-squares fit 
+; EXPLANATION:
+;	Part of the DAOPHOT PSF photometry sequence
+;
+; CALLING SEQUENCE:
+;	PKFIT, f, scale, x, y, sky, radius, ronois, phpadu, gauss, psf, 
+;				errmag, chi, sharp, Niter, /DEBUG 
+; INPUTS:
+;	F      - NX by NY array containing actual picture data.           
+;	X, Y   - the initial estimates of the centroid of the star relative
+;		to the corner (0,0) of the subarray.  Upon return, the
+;		final computed values of X and Y will be passed back to the
+;		calling routine.
+;	SKY  -   the local sky brightness value, as obtained from APER
+;	RADIUS-  the fitting radius-- only pixels within RADIUS of the
+;		instantaneous estimate of the star's centroid will be
+;		included in the fit, scalar
+;	RONOIS - readout noise per pixel, scalar
+;	PHPADU - photons per analog digital unit, scalar
+;	GAUSS -  vector containing the values of the five parameters defining
+;		the analytic Gaussian which approximates the core of the PSF.
+;	PSF   -  an NPSF by NPSF look-up table containing corrections from
+;		the Gaussian approximation of the PSF to the true PSF.
+;
+; INPUT-OUTPUT:
+;	SCALE  - the initial estimate of the brightness of the star,
+;		expressed as a fraction of the brightness of the PSF.
+;		Upon return, the final computed value of SCALE will be
+;		passed back to the calling routine.
+; OUTPUTS:
+;	ERRMAG - the estimated standard error of the value of SCALE
+;		returned by this routine.
+;	CHI    - the estimated goodness-of-fit statistic:  the ratio
+;		of the observed pixel-to-pixel mean absolute deviation from
+;		the profile fit, to the value expected on the basis of the
+;		noise as determined from Poisson statistics and the 
+;		readout noise.
+;	SHARP  - a goodness-of-fit statistic describing how much broader  
+;		the actual profile of the object appears than the
+;		profile of the PSF.
+;	NITER -  the number of iterations the solution required to achieve
+;		convergence.  If NITER = 25, the solution did not converge.
+;		If for some reason a singular matrix occurs during the least-
+;		squares solution, this will be flagged by setting NITER = -1.
+;
+; RESTRICTIONS:
+;	No parameter checking is performed
+; REVISON HISTORY:
+;	Adapted from the official DAO version of 1985 January 25
+;	Version 2.0 W. Landsman STX             November 1988
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ s = size(f)	;Get array dimensions
+ nx = s[1] & ny = s[2]
+;               ;Initialize a few things for the solution
+ redo = 0B		
+ pkerr = 0.027/(gauss[3]*gauss[4])^2
+ clamp = fltarr(3) + 1.
+ dtold = fltarr(3) 
+ niter = 0                                
+ chiold = 1.
+
+ if keyword_set(DEBUG) then $
+     print,'PKFIT: ITER  X      Y      SCALE    ERRMAG   CHI     SHARP'
+
+BIGLOOP:  		        ;Begin the big least-squares loop
+ niter = niter+1
+
+ ixlo = fix(x-radius) > 0	;Choose boundaries of subarray containing
+ iylo = fix(y-radius) > 0        ;points inside the fitting radius
+ ixhi = fix(x+radius) +1 < (nx-1)
+ iyhi = fix(y+radius) +1 < (ny-1)
+ ixx  = ixhi-ixlo+1
+ iyy  = iyhi-iylo+1
+ dy   =  findgen(iyy) + iylo - y    ;X distance vector from stellar centroid
+ dysq = dy^2
+ dx   = findgen(ixx) + ixlo - x
+ dxsq = dx^2
+ rsq  = fltarr(ixx,iyy)  ;RSQ - array of squared 
+
+ for J = 0,iyy-1 do rsq[0,j] = (dxsq+dysq[j])/radius^2
+
+ ; The fitting equation is of the form
+ ;
+ ; Observed brightness =
+ ;      SCALE + delta(SCALE)  *  PSF + delta(Xcen)*d(PSF)/d(Xcen) +
+ ;                                           delta(Ycen)*d(PSF)/d(Ycen)
+ ;
+ ; and is solved for the unknowns delta(SCALE) ( = the correction to
+ ; the brightness ratio between the program star and the PSF) and
+ ; delta(Xcen) and delta(Ycen) ( = corrections to the program star's
+ ; centroid).
+ ;
+ ; The point-spread function is equal to the sum of the integral under
+ ; a two-dimensional Gaussian profile plus a value interpolated from            
+ ; a look-up table.
+
+ good = where(rsq lt 1.,ngood)
+ ngood = ngood > 1
+
+ t = fltarr(ngood,3)
+ dx = dx[good mod ixx]
+ dy = dy[good/ixx]
+ model = dao_value(dx, dy, gauss, psf, dvdx, dvdy)
+
+ if keyword_set(DEBUG) then begin print,'model created ' & stop & end
+
+ t[0,0] = model
+ t[0,1] = -scale*dvdx  
+ t[0,2] = -scale*dvdy
+ fsub = f[ixlo:ixhi,iylo:iyhi]
+ fsub = fsub[good]
+ rsq = rsq[good]
+ df = fsub - scale*model - sky     ;Residual of the brightness from the PSF fit
+
+ ; The expected random error in the pixel is the quadratic sum of
+ ; the Poisson statistics, plus the readout noise, plus an estimated
+ ; error of 0.75% of the total brightness for the difficulty of flat-
+ ; fielding and bias-correcting the chip, plus an estimated error of 
+ ; of some fraction of the fourth derivative at the peak of the profile,
+ ; to account for the difficulty of accurately interpolating within the
+ ; point-spread function.  The fourth derivative of the PSF is
+ ; proportional to H/sigma**4 (sigma is the Gaussian width parameter for
+ ; the stellar core); using the geometric mean of sigma(x) and sigma(y),
+ ; this becomes H/ sigma(x)*sigma(y) **2.  The ratio of the fitting
+ ; error to this quantity is estimated from a good-seeing CTIO frame to
+ ; be approximately 0.027 (see definition of PKERR above.)
+
+ fpos = (fsub-df) > 0	;Raw data - residual = model predicted intensity
+ sigsq = fpos/phpadu + ronois + (0.0075*fpos)^2 + (pkerr*(fpos-sky))^2
+ sig = sqrt(sigsq)
+ relerr = df/sig
+
+ ; SIG is the anticipated standard error of the intensity
+ ; including readout noise, Poisson photon statistics, and an estimate
+ ; of the standard error of interpolating within the PSF.  
+
+ rhosq = fltarr(ixx,iyy)
+
+ for j = 0,iyy-1 do rhosq[0,j] = (dxsq/gauss[3]^2+dysq[j]/gauss[4]^2)
+
+ rhosq = rhosq[good]
+ if (niter GE 2) then begin    ;Reject any pixel with 10 sigma residual
+        badpix = where( ABS(relerr/chiold) GE 10.,nbad ) 
+        if nbad GT 0 then begin 
+            remove, badpix, fsub, df, sigsq, sig
+            remove, badpix, relerr, rsq, rhosq
+            ngood = ngood-badpix
+        endif
+ endif
+
+ wt = 5./(5.+rsq/(1.-rsq))
+ lilrho = where(rhosq LE 36.)	;Include only pixels within 6 sigma of centroid
+ rhosq[lilrho] = 0.5*rhosq[lilrho]
+ dfdsig = exp(-rhosq[lilrho])*(rhosq[lilrho]-1.)
+ fpos = ( fsub[lilrho]-sky) >0 + sky
+
+ ; FPOS-SKY = raw data minus sky = estimated value of the stellar
+ ; intensity (which presumably is non-negative).
+
+ sig  = fpos/phpadu + ronois + (0.0075*fpos)^2 + (pkerr*(fpos-sky))^2
+ numer = total(dfdsig*df/sig)
+ denom = total(dfdsig^2/sig)
+
+ ; Derive the weight of this pixel.  First of all, the weight depends
+ ; upon the distance of the pixel from the centroid of the star-- it
+ ; is determined from a function which is very nearly unity for radii
+ ; much smaller than the fitting radius, and which goes to zero for
+ ;  radii very near the fitting radius.  
+
+ chi = total(wt*abs(relerr))
+ sumwt = total(wt)
+
+ wt = wt/sigsq   ;Scale weight to inverse square of expected mean error
+ if niter GE 2 then $	;Reduce weight of a bad pixel
+         wt = wt/(1.+(0.4*relerr/chiold)^8)
+
+ v = fltarr(3)       ;Compute vector of residuals and the normal matrix. 
+ c = fltarr(3,3)
+
+ for kk = 0,2 do begin   
+    v[kk] = TOTAL(df*t[*,kk]*wt)
+    for ll = 0,2 do C[kk,ll] = TOTAL(t[*,kk]*t[*,ll]*wt)
+ end 
+
+ ; Compute the (robust) goodness-of-fit index CHI.
+ ; CHI is pulled toward its expected value of unity before being stored
+ ; in CHIOLD to keep the statistics of a small number of pixels from
+ ; completely dominating the error analysis.
+
+ if sumwt GT 3.0 then begin  
+   chi = 1.2533*chi*sqrt(1./(sumwt*(sumwt-3.)))
+   chiold = ((sumwt-3.)*chi+3.)/sumwt
+ endif
+
+ C = INVERT(C)	;Invert the normal matrix
+ dt = c#v 	;Compute parameter corrections
+
+; In the beginning, the brightness of the star will not be permitted
+; to change by more than two magnitudes per iteration (that is to say,
+; if the estimate is getting brighter, it may not get brighter by
+; more than 525% per iteration, and if it is getting fainter, it may
+; not get fainter by more than 84% per iteration).  The x and y
+; coordinates of the centroid will be allowed to change by no more
+; than one-half pixel per iteration.  Any time that a parameter
+; correction changes sign, the maximum permissible change in that
+; parameter will be reduced by a factor of 2.
+
+ div = where( dtold*dt LT -1.e-38, nbad )
+ if nbad GT 0 then clamp[div] = clamp[div]/2.
+ dtold = dt
+ adt = abs(dt)
+
+ scale = scale+dt[0]/    $
+  (1.+(( dt[0]/(5.25*scale)) > (-1*dt[0]/(0.84*scale)) )/clamp[0])
+ x = x + dt[1]/(1.+adt[1]/(0.5*clamp[1]))
+ y = y + dt[2]/(1.+adt[2]/(0.5*clamp[2]))
+ redo = 0B
+
+; Convergence criteria:  if the most recent computed correction to the
+; brightness is larger than 0.1% or than 0.05 * sigma(brightness),
+; whichever is larger, OR if the absolute change in X or Y is
+; greater than 0.01 pixels, convergence has not been achieved.
+
+ sharp = 2.*gauss[3]*gauss[4]*numer/(gauss[0]*scale*denom)
+ errmag = chiold*sqrt(c[0,0])
+ if ( adt[0] GT ( 0.05*errmag > 0.001*scale )) then redo = 1b
+ if ((adt[1] > adt[2] ) GT 0.01) then redo = 1b
+
+ if keyword_set(DEBUG) then print,format='(1H  ,I9,2F7.2,2F9.3,F8.2,F9.2)', $ 
+                       niter,x,y,scale,errmag,chiold,sharp
+ if niter LT 3 then goto, BIGLOOP 	;At least 3 iterations required
+
+; If the solution has gone 25 iterations, OR if the standard error of 
+; the brightness is greater than 200%, give up.
+
+ if (redo and (errmag LE 1.9995) and (niter LT 25) ) then goto, BIGLOOP
+ sharp = sharp>(-99.999)<99.999
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/planck.pro b/Code/script_idl_mv/astrolib/planck.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ffbf59089abb356c01f494dc9f99782d766572e9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/planck.pro
@@ -0,0 +1,71 @@
+function planck,wave,temp
+;+
+; NAME:
+;       PLANCK()   
+; PURPOSE: 
+;       To calculate the Planck function in units of ergs/cm2/s/A  
+;
+; CALLING SEQUENCE: 
+;       bbflux = PLANCK( wave, temp) 
+;
+; INPUT PARAMETERS: 
+;       WAVE   Scalar or vector giving the wavelength(s) in **Angstroms**
+;               at which the Planck function is to be evaluated.
+;       TEMP   Scalar giving the temperature of the planck function in degree K
+;
+; OUTPUT PARAMETERS:
+;       BBFLUX - Scalar or vector giving the blackbody flux (i.e. !pi*Intensity)
+;               in erg/cm^2/s/A in at the specified wavelength points.
+;
+; EXAMPLES:
+;       To calculate the blackbody flux at 30,000 K every 100 Angstroms between
+;       2000A and 2900 A
+;   
+;       IDL> wave = 2000 + findgen(10)*100
+;       IDL> bbflux = planck(wave,30000)
+;
+;       If a star with a blackbody spectrum has a radius R, and distance,d, then
+;       the flux at Earth in erg/cm^2/s/A will be bbflux*R^2/d^2
+; PROCEDURE:
+;       The wavelength data are converted to cm, and the Planck function
+;       is calculated for each wavelength point. See Allen (1973), Astrophysical
+;       Quantities, section 44 for more information.
+;
+; NOTES:
+;       See the procedure planck_radiance.pro in 
+;       ftp://origin.ssec.wisc.edu/pub/paulv/idl/Radiance/planck_radiance.pro
+;       for computation of Planck radiance given wavenumber in cm-1 or  
+;       wavelength in microns 
+; MODIFICATION HISTORY:
+;       Adapted from the IUE RDAF               August, 1989
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Improve precision of constants    W. Landsman  January 2002
+;-
+ On_error,2
+
+ if ( N_elements(wave) LT 1 ) then begin
+     print,'Syntax - bbflux = planck( wave, temp)'
+     return,0
+  endif    
+
+  if ( N_elements( temp ) NE 1 ) then $
+      read,'Enter a blackbody temperature', temp
+
+  bbflux = wave*0.
+
+; Gives the blackbody flux (i.e. PI*Intensity) ergs/cm2/s/a
+
+  w = wave / 1.E8                              ; Angstroms to cm    
+;constants appropriate to cgs units.
+  c1 =  3.7417749d-5                ; =2*!DPI*h*c*c       
+  C2 =  1.4387687d                  ; =h*c/k
+  val =  c2/w/temp  
+  mstr = machar(double = (size(val,/type) EQ 5) )  ;Get machine precision      
+  good = where( val LT alog(mstr.xmax), Ngood )    ;Avoid floating underflow
+
+  if ( Ngood GT 0 ) then  $
+      bbflux[ good ] =  C1 / ( w[good]^5 * ( exp( val[good])-1. ) )
+
+  return, bbflux*1.E-8              ; Convert to ergs/cm2/s/A
+
+  end 
diff --git a/Code/script_idl_mv/astrolib/planet_coords.pro b/Code/script_idl_mv/astrolib/planet_coords.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3f62cddb6b58796aeeec1dec0db117e82d76cfa5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/planet_coords.pro
@@ -0,0 +1,169 @@
+pro planet_coords, date, ra, dec, planet=planet, jd = jd, jpl = jpl
+;+
+; NAME:
+;    PLANET_COORDS
+; PURPOSE:  
+;    Find low or high precision RA and DEC for the planets given a date
+;
+; EXPLANATION:
+;    For low precision this routine uses HELIO to get the heliocentric ecliptic
+;    coordinates of the planets at the given date, then converts these to 
+;    geocentric ecliptic coordinates ala "Astronomical Algorithms" by Jean 
+;    Meeus (1991, p 209). These are then converted to RA and Dec using EULER.
+;    The accuracy between the years 1800 and 2050 is better than 1 arcminute 
+;    for  the terrestial planets, but reaches 10 arcminutes for Saturn.    
+;    Before 1850 or after 2050 the accuracy can get much worse.   
+;
+;    For high precision use the /JPL option ito use the full JPL ephemeris.
+; CALLING SEQUENCE:
+;    PLANET_COORDS, DATE, RA, DEC, [ PLANET = , /JD, /JPL]
+;
+; INPUTS:
+;       DATE - If /JD is not set, then date is a 3-6 element vector containing
+;              year,month (1-12), day, and optionally hour, minute, & second.
+;              If /JD is set then DATE is a Julian date.   An advantage of the
+;              /JD option is that it allows the use of vector dates.
+; OUTPUTS:
+;       RA - right ascension of planet(s), J2000 degrees, double precision
+;       DEC - declination of   planet(s), J2000 degrees, double precision
+;
+; OPTIONAL INPUT KEYWORD:
+;       PLANET - scalar string giving name of a planet, e.g. 'venus'. Default 
+;               is to compute coords for all of them (except Earth).
+;       /JD - If set, then the date parameter should be supplied as Julian date
+;       JPL - if /JPL set, then PLANET_COORDS will call the procedure 
+;             JPLEPHINTERP to compute positions using the full JPL ephemeris.   
+;             The JPL ephemeris FITS file JPLEPH.405 must exist in either the 
+;             current directory, or in the directory specified by the 
+;             environment variable ASTRO_DATA.   Alternatively, the JPL keyword
+;             can be set to the full path and name of the ephemeris file.
+;             A copy of the JPL ephemeris FITS file JPLEPH.405 is available in
+;                 http://idlastro.gsfc.nasa.gov/ftp/data/         
+; EXAMPLES:
+;    (1)  Find the RA, Dec of Venus on 1992 Dec 20
+;          IDL> planet_coords, [1992,12,20], ra,dec    ;Compute for all planets
+;          IDL> print,adstring(ra[1],dec[1],1)         ;Venus is second planet
+;     ====> RA = 21 05  2.66  Dec = -18 51 45.7
+;    This position is 37" from the full DE406 ephemeris position of
+;          RA = 21 05  5.24        -18 51 43.1
+;
+;    (2) Return the current RA and Dec of all 8 planets using JPL ephemeris
+;          IDL> get_juldate, jd                 ;Get current Julian Date
+;          IDL> planet_coords,jd,ra,dec,/jd,/jpl  ;Find positions of all planets
+;          IDL> forprint,adstring(ra,dec,0)     ;Display positions   
+;
+;    (3) Plot the declination of Mars for every day in the year 2001
+;          IDL> jdcnv,2001,1,1,0,jd      ;Get Julian date of midnight on Jan 1 
+;               Now get Mars RA,Dec for 365 consecutive days
+;          IDL> planet_coords,jd+indgen(365),ra,dec,/jd, planet = 'mars'     
+;          IDL> plot,indgen(365)+1,dec
+; NOTES:
+;          HELIO is based on the two-body problem and neglects interactions 
+;           between the planets.   This is why the worst results are for
+;           Saturn.   Use the /JPL option or the online ephemeris generator 
+;           http://ssd.jpl.nasa.gov/horizons.cgi for more accuracy. 
+;
+;           The procedure returns astrometric coordinates, i.e. no correction
+;           for aberration.   A correction for light travel time is applied
+;           when /JPL is set, but not for the default low-precision calculation.
+; PROCEDURES USED:
+;        JULDATE 
+;        EULER, HELIO  - if /JPL is not set
+;        JPLEPHREAD, JPLEPHINTERP - if /JPL is set
+; REVISION HISTORY:
+;        Written P.Plait & W. Landsman     August 2000
+;        Fixed Julian date conversion   W. Landsman August 2000
+;        Added /JPL keyword  W. Landsman   July 2001
+;        Allow vector Julian dates with JPL ephemeris W. Landsman December 2002
+;-   
+; On_error,2
+ if N_params() LT 1 then begin
+     print,'Syntax - PLANET_COORDS, date, ra,dec, [PLANET =, /JD , JPL= ]'
+     print,'      date - either 3-6 element date or Julian date (if /JD is set)'
+     print,'      ra,dec - output ra and dec in degrees'
+     print,'      PLANET - name of planet (optional)'
+     return
+ endif
+
+ radeg = 180.0d/!DPI
+ c = 2.99792458d5
+
+;convert input date to real JD
+  
+  if keyword_set(jd) then begin 
+       jj = date
+       if N_elements(jj) GT 0 then if N_elements(planet) GT 1 then $
+       message,'ERROR - A planet name must be supplied for vector dates'
+  endif else begin 
+           juldate,date,jj
+            jj = jj + 2400000.0d
+  endelse 
+
+;make output arrays to include each planet
+; note that we need Earth to convert from heliocentric
+; ecliptic coordinates to geocentric and then to RA and DEC
+
+   if keyword_set(planet) then begin
+       planetlist = ['MERCURY','VENUS','MARS', $
+        'JUPITER','SATURN','URANUS','NEPTUNE','PLUTO']
+        index = 1+ where(planetlist eq strupcase(strtrim(planet,2)), Nfound) 
+        if index[0] GE 3 then index = index + 1
+       if Nfound EQ 0 then message,'Unrecognized planet of ' + planet
+   endif else index = [1,2,4,5,6,7,8,9]
+
+   if keyword_set(JPL) then begin
+       if size(jpl,/TNAME) EQ 'STRING' then jplfile = jpl else $
+            jplfile = find_with_def('JPLEPH.405','ASTRO_DATA')
+
+        if jplfile EQ '' then message,'ERROR - Cannot find JPL ephemeris file' 
+;Read ephemeris FITS file
+        JPLEPHREAD,jplfile, pinfo, pdata, [long(min(jj)-1), long(max(jj)+1)]
+        np = N_elements(index)
+        njd = n_elements(jj)
+        ra = dblarr(njd,np)   & dec = dblarr(njd,np)
+
+        for i=0, Np-1 do begin
+        JPLEPHINTERP, pinfo, pdata, jj, x,y,z, $
+                       objectname=index[i],center='EARTH'
+;  Compute distance to planet(s) and adjust Julian date for light travel time
+; and recompute planet positions
+       dis = sqrt(x^2 + y^2 + z^2)
+       jj1 = jj - dis/c/86400.0d
+
+; Compute position of Earth at current time, but position of planet at time
+; light started traveling 
+       JPLEPHINTERP, pinfo, pdata, jj, xe,ye,ze, /EARTH
+       JPLEPHINTERP, pinfo, pdata, jj1, x,y,z, objectname=index[i]
+       x = x-xe & y = y-ye & z = z-ze
+         ra[0,i] = atan(y,x) * radeg
+       g = where(ra LT 0, Ng)
+       if Ng GT 0 then ra[g] = ra[g] + 360.0d
+       dec[0,i]   = atan(z,sqrt(x*x + y*y)) * radeg
+       endfor
+       ra = reform(ra) & dec = reform(dec)
+       return
+   endif
+
+   helio,jj,index,rad,lon,lat,/radian
+
+; extract Earth's info
+
+   helio,jj,3,rade,lone,late,/radian
+
+;get rectangular coords of planets
+
+   x = rad * cos(lat) * cos(lon) - rade * cos(late) * cos(lone)
+   y = rad * cos(lat) * sin(lon) - rade * cos(late) * sin(lone)
+   z = rad * sin(lat)            - rade * sin(late)
+
+;get geocentric longitude lambda and geo latitude, beta
+
+   lambda = atan(y,x) * radeg
+   beta   = atan(z,sqrt(x*x + y*y)) * radeg
+
+;convert to Ra and Dec
+
+   euler, lambda, beta, ra, dec, 4
+
+   return
+   end
diff --git a/Code/script_idl_mv/astrolib/ploterror.pro b/Code/script_idl_mv/astrolib/ploterror.pro
new file mode 100644
index 0000000000000000000000000000000000000000..dbb3515a2502f0874f6159d98e945ec34574f90a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ploterror.pro
@@ -0,0 +1,334 @@
+PRO ploterror, x, y, xerr, yerr, NOHAT=hat, HATLENGTH=hln, ERRTHICK=eth, $
+      ERRSTYLE=est, TYPE=itype, XRANGE = xrange, XLOG=xlog, YLOG=ylog, $
+      NSKIP = nskip, NOCLIP = noclip, ERRCOLOR= ecol, YRANGE = yrange, $
+      NSUM = nsum, WINDOW=window, _EXTRA = pkey
+
+;+
+; NAME:
+;     PLOTERROR
+; PURPOSE:
+;     Plot data points with accompanying X or Y error bars.
+; EXPLANATION:
+;     This is a greatly enhanced version of the standard IDL Library routine
+;     PLOTERR
+;
+;     Note that since December 2013 a similar error plotting capablity is 
+;     available in CGPLOT (http://www.idlcoyote.com/programs/cgplot.pro).
+;
+; CALLING SEQUENCE:
+;     ploterror, [ x,]  y, [xerr], yerr [, TYPE=, /NOHAT, HATLENGTH= , NSUM =
+;                  ERRTHICK=, ERRSTYLE=, ErrcolOR=, NSKIP=, .. PLOT keywords]
+;
+; INPUTS:
+;     X = array of abscissas.
+;     Y = array of Y values.
+;     XERR = array of error bar values (along X)
+;     YERR = array of error bar values (along Y)
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS:
+;     TYPE = type of plot produced.  The possible types are:
+;              TYPE = 0 :       X Linear - Y Linear  (default)
+;              TYPE = 1 :       X Linear - Y Log
+;              TYPE = 2 :       X Log    - Y Linear
+;              TYPE = 3 :       X Log    - Y Log
+;              Actually, if 0 is specified, the XLOG and YLOG keywords
+;              are used.  If these aren't specified, then a linear-linear
+;              plot is produced.  This keyword is available to maintain
+;              compatibility with the previous version of PLOTERROR.
+;     /NOHAT     = if specified and non-zero, the error bars are drawn
+;              without hats.
+;     HATLENGTH = the length of the hat lines in device units used to cap the 
+;              error bars.   Defaults to !D.X_VSIZE / 100).
+;     ERRTHICK  = the thickness of the error bar lines.  Defaults to the
+;              THICK plotting keyword.
+;     ERRSTYLE  = the line style to use when drawing the error bars.  Uses
+;              the same codes as LINESTYLE.
+;     ERRCOLOR =  String (e.g. 'red') or scalar integer (0 - !D.N_TABLE)
+;              specifying the color to use for the error bars.   See CGCOLOR()
+;              for a list of possible color names.  See 
+;              http://www.idlcoyote.com/cg_tips/legcolor.php
+;              for a warning about the use of indexed color
+;     NSKIP = Integer specifying the error bars to be plotted.   For example,
+;              if NSKIP = 2 then every other error bar is plotted; if NSKIP=3
+;              then every third error bar is plotted.   Default is to plot
+;              every error bar (NSKIP = 1)
+;     NSUM =  Number of points to average over before plotting, default=!P.NSUM
+;             The errors are also averaged, and then divided by sqrt(NSUM).   
+;             This  approximation is meaningful only when the neighboring error
+;             bars have similar sizes.    PLOTERROR does not pass the NSUM 
+;             keyword to the PLOT command, but rather computes the binning 
+;             itself using the  FREBIN function.
+;     TRADITIONAL - If set to 0 then a black plot is drawn on a white background
+;             in the graphics window.   The default value is 1, giving the
+;             traditional black background for a graphics window.
+;     WINDOW - Set this keyword to plot to a resizeable graphics window
+;            
+;
+;     Any valid keywords to the cgPLOT command (e.g. PSYM, YRANGE, AXISCOLOR  
+;     SYMCOLOR, ASPECT) are also accepted by PLOTERROR via the _EXTRA facility.
+;
+; RESTRICTIONS:
+;       Arrays must not be of type string, and there must be at least 1 point.
+;       If only three parameters are input, they will be taken as X, Y and
+;       YERR respectively.
+;
+;       PLOTERROR cannot be used for asymmetric error bars.   Instead use
+;       OPLOTERROR with the /LOBAR and /HIBAR keywords.
+;
+;       Any data points with NAN values in the X, Y, or error vectors are 
+;       ignored.
+; EXAMPLE:
+;       Suppose one has X and Y vectors with associated errors XERR and YERR
+;
+;       (1) Plot Y vs. X with both X and Y errors and no lines connecting
+;           the points
+;                  IDL> ploterror, x, y, xerr, yerr, psym=3
+;
+;       (2) Like (1) but plot only the Y errors bars and omits "hats"
+;                  IDL> ploterror, x, y, yerr, psym=3, /NOHAT
+;
+; WARNING:
+;       This an enhanced version of the procedure PLOTERR in the standard IDL
+;       distribution.    It was renamed from PLOTERR to PLOTERROR in June 1998
+;       in the IDL Astronomy Library to avoid conflict with the RSI procedure.
+;
+; PROCEDURE:
+;       A plot of X versus Y with error bars drawn from Y - YERR to Y + YERR
+;       and optionally from X - XERR to X + XERR is written to the output device
+;
+; PROCEDURE CALLS:
+;     cgPlot, cgPlots
+;     FREBIN - used to compute binning if NSUM keyword is present
+; MODIFICATION HISTORY:
+;     William Thompson        Applied Research Corporation  July, 1986
+;     DMS, April, 1989        Modified for Unix
+;     Michael R. Greason      ST Systems
+;     May, 1991               Added most of the plotting keywords, put hats
+;                               on the error bars.
+;     K. Venkatakrishna       Added option to plot xerr, May, 1992
+;     Michael R. Greason      Corrected handling of reversed axes.  Aug. 1992
+;     W. Landsman             Use _EXTRA keyword                    July 1995
+;     W. Landsman             Plot more than 32767 points           Feb 1996
+;     W. Landsman     Fix Y scaling when only XRANGE supplied       Nov 1996
+;     W. Landsman     Added NSKIP keyword                           Dec 1996
+;     W. Landsman     Use XLOG, YLOG instead of XTYPE, YTYPE        Jan 1998
+;     W. Landsman     Rename to PLOTERROR, OPLOTERROR               Jun 1998
+;     W. Landsman  Better default scaling when NSKIP supplied       Oct 1998 
+;     W. Landsman  Ignore !P.PSYM when drawing error bars           Jan 1999
+;     W. Landsman  Handle NSUM keyword correctly                    Aug 1999
+;     W. Landsman  Fix case of /XLOG but no X error bars            Oct 1999
+;     W. Landsman  Work in the presence of NAN values               Nov 2000
+;     W. Landsman  Improve logic when NSUM or !P.NSUM is set        Jan 2001
+;     W. Landsman  Only draw error bars with in XRANGE (for speed)  Jan 2002
+;     W. Landsman  Fix Jan 2002 update to work with log plots       Jun 2002
+;     W. Landsman  Added _STRICT_EXTRA                              Jul 2005
+;     W. Landsman/D.Nidever Fixed case of logarithmic axes reversed Mar 2009
+;     W. Landsman/S. Koch  Allow input to be a single point         Jan 2010
+;     W. Landsman  Add Coyote Graphics                              Feb 2011
+;     W. Landsman Make keyword name ERRCOLOR instead of ECOLOR 
+;                 Speedup when no ERRCOLOR defined                  Feb 2011
+;     D. Fanning Use PLOTS instead of CGPLOTS for speed             Jan 2012
+;-
+;                       Check the parameters.
+ On_error, 2
+ compile_opt idl2
+
+ np = N_params()
+ IF (np LT 2) THEN BEGIN
+        print, "PLOTERROR must be called with at least two parameters."
+        print, "Syntax: ploterror, [x,] y, [xerr], yerr"
+        RETURN
+ ENDIF
+ 
+IF Keyword_Set(window) THEN BEGIN
+
+   currentWindow = cgQuery(/CURRENT, COUNT=wincnt)
+   IF wincnt EQ 0 THEN replaceCmd = 0 ELSE replaceCmd=1
+   cgWindow, 'ploterror', x, y, xerr, yerr, NOHAT=hat, HATLENGTH=hln, ERRTHICK=eth, $
+      ERRSTYLE=est, TYPE=itype, XRANGE = xrange, XLOG=xlog, YLOG=ylog, $
+      NSKIP = nskip, NOCLIP = noclip, ERRCOLOR= ecol, YRANGE = yrange, $
+      NSUM = nsum, _EXTRA = pkey, REPLACECMD=replaceCmd
+   RETURN
+   
+ENDIF
+
+; Error bar keywords (except for HATLENGTH; this one will be taken care of 
+; later, when it is time to deal with the error bar hats).
+
+ hat =  ~keyword_set(hat)
+ setdefaultvalue, eth, !P.thick
+ setdefaultvalue, est, 0
+ setdefaultvalue, ecol, 'Opposite'
+ setdefaultvalue, noclip, 0
+ setdefaultvalue, nskip, 1
+ setdefaultvalue, nsum, !p.nsum
+ setdefaultvalue, traditional, 0
+ 
+;				Other keywords.
+
+ IF (keyword_set(itype)) THEN BEGIN
+	CASE (itype) OF
+		   1 :  ylog = 1	; X linear, Y log
+		   2 :  xlog = 1	; X log, Y linear
+		   3 :  BEGIN		; X log, Y log
+			xlog = 1
+			ylog = 1
+			END
+		ELSE : 
+	ENDCASE
+ ENDIF
+ setdefaultvalue,xlog, 0
+ setdefaultvalue,ylog, 0
+ ;			If no x array has been supplied, create one.  Make
+;			sure the rest of the procedure can know which parameter
+;			is which.
+
+ IF np EQ 2 THEN BEGIN			; Only Y and YERR passed.
+	yerr = y
+	yy = x
+	xx = lindgen(n_elements(yy))
+        xerr = make_array(size=size(xx))
+
+ ENDIF ELSE IF np EQ 3 THEN BEGIN 	; X, Y, and YERR passed.
+        yerr = xerr
+        yy = y
+        xx = x
+
+ ENDIF ELSE BEGIN                        ; X, Y, XERR and YERR passed.
+	yy = y
+        g = where(finite(xerr))
+        xerr[g] = abs(xerr[g])
+	xx = x
+ ENDELSE
+
+ g = where(finite(yerr))               ;Don't take absolute value of NAN values
+ yerr[g] = abs(yerr[g])
+
+;			Determine the number of points being plotted.  This
+;			is the size of the smallest of the three arrays
+;			passed to the procedure.  Truncate any overlong arrays.
+
+ n = N_elements(xx) < N_elements(yy)
+
+ IF np GT 2 then n = n < N_elements(yerr)   
+ IF np EQ 4 then n = n < N_elements(xerr)
+
+ IF n LT 1 THEN $
+	message,'ERROR - No data points to plot.'
+
+ xx = xx[0:n-1]
+ yy = yy[0:n-1]
+ yerr = yerr[0:n-1]
+ IF np EQ 4 then xerr = xerr[0:n-1]
+
+; If NSUM is greater than one, then we need to smooth ourselves (using FREBIN)
+
+ if nsum GT 1 then begin
+      n1 = float(n) / nsum
+      n  = long(n1)
+      xx = frebin(xx, n1)
+      yy = frebin(yy, n1)
+      yerror = frebin(yerr,n1)/sqrt(nsum)
+      if NP EQ 4 then xerror = frebin(xerr,n1)/sqrt(nsum)
+  endif else begin
+      yerror = yerr
+      if NP EQ 4 then xerror = xerr
+  endelse
+
+
+; If no y-range was passed via keyword or system variable, force one large 
+; enough to display all the data and the entire error bars.     
+; If a reversed y-range was passed, switch ylo and yhi.
+
+ ylo = yy - yerror
+ yhi = yy + yerror
+
+ setdefaultvalue, yrange, !Y.RANGE
+ IF yrange[0] EQ yrange[1] THEN BEGIN
+	if keyword_set( XRANGE ) then  begin
+		good = where( (xx GT min(xrange)) and (xx LT max(xrange)), Ng )
+		if Ng EQ 0 then message, $
+		   'ERROR - No X data within specified X range'
+		yrange = [min(ylo[good],/NAN), max(yhi[good], /NAN)]
+	endif else yrange = [min(ylo,/NAN), max(yhi, /NAN)]
+ ENDIF 
+;        Similarly for x-range
+ setdefaultvalue, xrange, !X.RANGE
+ if NP EQ 4 then begin
+   xlo = xx - xerror
+   xhi = xx + xerror
+   IF xrange[0] EQ xrange[1] THEN xrange = [min(xlo,/NAN), max(xhi,/NAN)]
+ endif
+
+; Plot the positions.    Always set NSUM = 1 since we already took care of 
+; smoothing with FREBIN
+
+ cgPlot, xx, yy, XRANGE = xrange, YRANGE = yrange, XLOG = xlog, YLOG = ylog, $
+         _EXTRA = pkey, NOCLIP = noclip, NSum= 1, TRADITIONAL=traditional
+
+;	Plot the error bars.   Compute the hat length in device coordinates
+;       so that it remains fixed even when doing logarithmic plots.
+
+    data_low = convert_coord(xx,ylo,/TO_DEVICE)
+    data_hi = convert_coord(xx,yhi,/TO_DEVICE)
+    if NP EQ 4 then begin
+       x_low = convert_coord(xlo,yy,/TO_DEVICE)
+       x_hi = convert_coord(xhi,yy,/TO_DEVICE)
+    endif
+    ycrange = !Y.crange
+    xcrange = !x.crange
+    sv_psym = !P.PSYM & !P.PSYM = 0
+    
+    if ylog EQ 1 then ylo = ylo > 10^min(ycrange)    
+    if (xlog EQ 1) && (np EQ 4) then  xlo = xlo > 10^min(xcrange)    
+	                   
+; Only draw error bars for X values within XCRANGE
+    if xlog EQ 1 then xcrange = 10^xcrange
+    g = where((xx GT xcrange[0]) and (xx LE xcrange[1]), Ng)
+
+    if (Ng GT 0) && (Ng NE n) then begin  
+          istart = min(g, max = iend)  
+    endif else begin
+          istart = 0L & iend = n-1
+    endelse
+    
+    ecol = cgDefaultColor(ecol, Default='opposite')
+    IF Size(ecol, /TNAME) EQ 'STRING' THEN ecol = cgColor(ecol)
+					 
+ FOR i = istart, iend, Nskip DO BEGIN     
+
+    Plots, [xx[i],xx[i]], [ylo[i],yhi[i]], LINESTYLE=est,THICK=eth,  $
+		NOCLIP = noclip, COLOR = ecol
+;                                                         Plot X-error bars 
+    if np EQ 4 then Plots, [xlo[i],xhi[i]],[yy[i],yy[i]],LINESTYLE=est, $
+		THICK=eth, COLOR = ecol, NOCLIP = noclip
+	IF (hat NE 0) THEN BEGIN
+		IF (N_elements(hln) EQ 0) THEN hln = !D.X_VSIZE/100. 
+		exx1 = data_low[0,i] - hln/2.
+		exx2 = exx1 + hln
+		
+		Plots, [exx1,exx2], [data_low[1,i],data_low[1,i]], $  
+		  COLOR=ecol, $
+      LINESTYLE=est,THICK=eth,/DEVICE, noclip = noclip
+		Plots, [exx1,exx2], [data_hi[1,i],data_hi[1,i]], $
+		 COLOR = ecol, $
+     LINESTYLE=est,THICK=eth,/DEVICE, noclip = noclip
+
+;                                                        Plot Y-error bars
+
+                IF np EQ 4 THEN BEGIN
+                   IF (N_elements(hln) EQ 0) THEN hln = !D.Y_VSIZE/100.
+                   eyy1 = x_low[1,i] - hln/2.
+                   eyy2 = eyy1 + hln
+                   Plots, [x_low[0,i],x_low[0,i]], [eyy1,eyy2],COLOR = ecol, $
+                         LINESTYLE=est,THICK=eth,/DEVICE, NOCLIP = noclip
+                   Plots, [x_hi[0,i],x_hi[0,i]], [eyy1,eyy2],COLOR = ecol, $
+                         LINESTYLE=est,THICK=eth,/DEVICE, NOCLIP = noclip
+                ENDIF
+	ENDIF
+    NOPLOT:
+ ENDFOR
+ !P.PSYM = sv_psym
+;
+ RETURN
+ END
diff --git a/Code/script_idl_mv/astrolib/plothist.pro b/Code/script_idl_mv/astrolib/plothist.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b311a1ef760429377636b3d71597560030999e72
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/plothist.pro
@@ -0,0 +1,369 @@
+PRO plothist, arr, xhist,yhist, BIN=bin,  NOPLOT=NoPlot, $
+                 OVERPLOT=Overplot, PSYM = psym, Peak=Peak, $
+                 Fill=Fill, FCOLOR=Fcolor, FLINE=FLINE, $
+                 FTHICK=FThick, FSPACING=Fspacing, FPATTERN=Fpattern, $
+                 FORIENTATION=Forientation, NAN = NAN, $
+                 _EXTRA = _extra, Halfbin = halfbin, AUTOBin = autobin, $
+                 Boxplot = boxplot, xlog = xlog, ylog = ylog, $
+                 yrange = yrange, Color = color,axiscolor=axiscolor, $
+                 rotate = rotate, WINDOW=window,XSTYLE=xstyle, YSTYLE = ystyle,$
+		 THICK= thick, LINESTYLE = linestyle
+;+
+; NAME:
+;      PLOTHIST
+; PURPOSE:
+;      Plot the histogram of an array with the corresponding abscissa.
+;
+; CALLING SEQUENCE:
+;      plothist, arr, xhist, yhist, [, BIN=, /FILL, /NOPLOT, /OVERPLOT, PEAK=,
+;                                     /AUTOBIN,  ...plotting keywords]
+; INPUTS:
+;      arr - The array to plot the histogram of.   It can include negative
+;            values, but non-integral values will be truncated.              
+;
+; OPTIONAL OUTPUTS:
+;      xhist - X vector used in making the plot  
+;              ( = lindgen( N_elements(h)) * bin + min(arr) )
+;      yhist - Y vector used in making the plot  (= histogram(arr/bin))
+;
+; OPTIONAL INPUT-OUTPUT KEYWORD:
+;      BIN -  The size of each bin of the histogram, scalar (not necessarily
+;             integral).  If not present (or zero), then the default is to 
+;             automatically determine the binning size as the square root of 
+;             the number of samples
+;             If undefined on input, then upon return BIN will contain the 
+;             automatically computing bin factor.
+; OPTIONAL INPUT KEYWORDS:
+;      /AUTOBIN - (OBSOLETE) Formerly would automatically determines bin size 
+;                 of the histogram as the square root of the number of samples. 
+;                 This is now the default so the keyword is no longer needed.
+;                 Use the  BIN keyword to manually set the bin size.
+;      AXISCOLOR - Color (string or number) of the plotting axes.  
+;      BOXPLOT - If set (default), then each histogram data value is plotted
+;             "box style" with vertical lines drawn from Y=0 at each end of 
+;              the bin width.   Set BOXPLOT=0 to suppress this.
+;      COLOR - Color (number or string) of the plotted data.    See CGCOLOR
+;              for a list of available color names. 
+;      /HALFBIN - Set this keyword to a nonzero value to shift the binning by
+;              half a bin size.     This is useful for integer data, where e.g.
+;              the bin for values of 6 will go from 5.5 to 6.5.   The default
+;              is to set the HALFBIN keyword for integer data, and not for
+;              non-integer data.     
+;      /NAN - If set, then check for the occurence of IEEE not-a-number values
+;             This is the default for floating point or Double data
+;      /NOPLOT - If set, will not plot the result.  Useful if intention is to
+;             only get the xhist and yhist outputs.
+;      /OVERPLOT - If set, will overplot the data on the current plot.  User
+;            must take care that only keywords valid for OPLOT are used.
+;      PEAK - if non-zero, then the entire histogram is normalized to have
+;             a maximum value equal to the value in PEAK.  If PEAK is
+;             negative, the histogram is inverted.
+;      /FILL - if set, will plot a filled (rather than line) histogram.
+;      /ROTATE - if set, the plot is rotated onto it's side, meaning the bars 
+;             extend from left to right.  Xaxis corresponds to the count within 
+;             in each bin.      Useful for placing a histogram plot
+;             at the side of a scatter plot.
+;       WINDOW - Set this keyword to plot to a resizeable graphics window
+;
+;
+; The following keywords will automatically set the  FILL keyword:
+;      FCOLOR - color (string or number) to use for filling the histogram
+;      /FLINE - if set, will use lines rather than solid color for fill (see
+;              the LINE_FILL keyword in the POLYFILL routine)
+;      FORIENTATION - angle of lines for fill (see the ORIENTATION keyword
+;              in the POLYFILL routine)
+;      FPATTERN - the pattern to use for the fill (see the PATTERN keyword
+;              in the POLYFILL routine)
+;      FSPACING - the spacing of the lines to use in the fill (see the SPACING
+;              keyword in the POLYFILL routine)
+;      FTHICK - the thickness of the lines to use in the fill (see the THICK
+;              keyword in the POLYFILL routine)
+;
+; Any input keyword that can be supplied to the cgPLOT procedure (e.g. XRANGE,
+;    AXISCOLOR, LINESTYLE, /XLOG, /YLOG) can also be supplied to PLOTHIST.
+;
+; EXAMPLE:
+;       (1) Create a vector of random 1000 values derived from a Gaussian of 
+;       mean 0, and sigma of 1.    Plot the histogram of these values with a 
+;       binsize of 0.1, and use a blue colored box fill.
+;
+;       IDL> a = randomn(seed,1000)
+;       IDL> plothist,a, bin = 0.1,fcolor='blue'
+;
+;       (2) As before, but use autobinning and fill the plot with diagonal lines at 
+;           a 45 degree angle
+;
+;       IDL> plothist,a, /fline, forient=45
+;
+; NOTES:
+;       David Fanning has written a similar program CGHISTOPLOT with more graphics
+;       options:   See http://www.idlcoyote.com/programs/cghistoplot.pro
+; MODIFICATION HISTORY:
+;        Written     W. Landsman            January, 1991
+;        Add inherited keywords W. Landsman        March, 1994
+;        Use ROUND instead of NINT  W. Landsman   August, 1995
+;        Add NoPlot and Overplot keywords.   J.Wm.Parker  July, 1997
+;        Add Peak keyword.   J.Wm.Parker  Jan, 1998
+;        Add FILL,FCOLOR,FLINE,FPATTERN,FSPACING keywords. J.Wm.Parker Jan, 1998
+;        Add /NAN keyword        W. Landsman October 2001
+;        Don't plot out of range with /FILL, added HALFBIN keyword, make
+;        half bin shift default for integer only W. Landsman/J. Kurk May 2002
+;        Add BOXPLOT keyword, use exact XRANGE as default W.L.  May 2006
+;        Allow use of /XLOG and /YLOG keywords  W.L. June 2006
+;        Adjust Ymin when /YLOG is used  W. L.  Sep 2007
+;        Added AXISCOLOR keyword, fix color problem with overplots WL Nov 2007
+;        Check when /NAN is used and all elements are NAN  S. Koposov Sep 2008
+;        Added /ROTATE keyword to turn plot on its side. J. Mullaney, 2009.
+;        Added FTHICK keyword for thickness of fill lines. L. Anderson Oct. 2010
+;        Use Coyote Graphics  W. Landsman Feb 2011
+;        Explicit XSTYLE, YSTYLE keywords to avoid _EXTRA confusion WL. Aug 2011
+;        Fix PLOT keyword problem with /ROTATE  WL  Dec 2011
+;        Fix problems when /XLOG is set A. Kimball/WL April 2013
+;        Fix FILL to work when axis is inverted (xcrange[0] >
+;          xcrange[1]) T.Ellsworth-Bowers July 2014
+;        Make /NaN,/AUTOBIN and BOXPLOT the default  W. Landsman   April 2016
+;-
+;			Check parameters.
+
+ compile_opt idl2
+
+ if N_params() LT 1 then begin   
+	print,'Syntax - plothist, arr, [xhist,yhist, ' 
+        print, '         [/AUTOBIN, BIN=, /BOXPLOT, HALFBIN=, PEAK=, /NOPLOT,'
+	print, '         /OVERPLOT, /FILL...plotting keywords]' 
+        print,'Fill keywords: FCOLOR=, /FLINE, FORIENTATION=, FPATTERN=,' + $
+              'FSPACING= '
+	return
+ endif
+
+ Catch, theError
+ if theError NE 0 then begin 
+     Catch,/Cancel
+ ;    void = cgErrorMsg(/quiet)
+     return
+ endif
+
+ if N_elements( arr ) LT 2 then message, $
+      'ERROR - Input array must contain at least 2 elements'
+ arrmin = min( arr, MAX = arrmax)
+ if ( arrmin EQ arrmax ) then message, $
+       'ERROR - Input array must contain distinct values'
+  if N_elements(boxplot) EQ 0 then boxplot=1     
+
+ dtype = size(arr,/type)
+ floatp = (dtype EQ 4) || (dtype EQ 5)
+
+ ;Determining how to calculate bin size:
+ if ~keyword_set(BIN) then begin
+       bin = (max(arr)-min(arr))/sqrt(N_elements(arr))
+       if ~floatp then bin = bin > 1
+ endif else begin
+    bin = float(abs(bin))
+ endelse
+
+ 
+
+; Compute the histogram and abscissa.    
+; Determine if a half bin shift is 
+; desired (default for integer data)     
+ if N_elements(halfbin) EQ 0 then halfbin = ~floatp         ;integer data?
+ 
+ 
+ if N_elements(NaN) EQ 0 then NaN = 1
+ if floatp && NaN then begin
+      good = where(finite(arr), ngoods )
+      if ngoods eq 0 then $
+              message, 'ERROR - Input array contains no finite values'
+
+      if halfbin then y = round( ( arr[good] / bin)) $
+                 else y = floor( ( arr[good] / bin))
+ endif else if halfbin then y = round( ( arr / bin)) $
+                       else y = floor( ( arr/ bin)) 
+ 
+ ;Determine number in each bin:
+ yhist = histogram( y )
+ N_hist = N_elements( yhist )
+ 
+ ;Positions of each bin:
+ xhist = lindgen( N_hist ) * bin + min(y*bin) 
+ 
+ if ~halfbin then xhist = xhist + 0.5*bin
+
+;;;
+;   If renormalizing the peak, do so.
+;
+if keyword_set(Peak) then yhist = yhist * (Peak / float(max(yhist)))
+
+;;;
+;   If not doing a plot, exit here.
+;
+ if keyword_set(NoPlot) then return
+ 
+ ;JRM;;;;;
+ xra_set = keyword_set(XRANGE)?1:0
+ xst_set = keyword_set(xstyle)?1:0
+ yst_set = keyword_set(ystyle)?1:0
+;JRM;;;;;
+ 
+ if N_elements(fill) EQ 0 then $
+    fill  = keyword_set(fcolor) || keyword_set(fline)
+    
+ if keyword_set(over) then begin ;if overplotting, was original plot a log?
+      if N_elements(ylog) EQ 0 then ylog = !Y.type
+      if N_elements(xlog) EQ 0 then xlog = !X.type
+ endif     
+ if N_elements(PSYM) EQ 0 then psym = 10         ;Default histogram plotting
+ if ~keyword_set(XRANGE) then xrange = [ xhist[0]-bin ,xhist[N_hist-1]+bin ]
+ if ~keyword_set(xstyle) then xstyle=1
+ 
+ if  keyword_set(ylog) then begin 
+     ymin = min(yhist) GT 1 ? 1 : 0.1
+     if N_elements(yrange) EQ 2 then ymin = ymin < yrange[0] 
+     ;ydata contains the y-positions where the lines should be linked.
+     ydata = [ymin, yhist>ymin, ymin] 
+  endif else ydata = [0, yhist, 0]
+ ;xdata contains the y-positions where the lines should be linked.
+ xdata = [xhist[0] - bin, xhist, xhist[n_hist-1]+ bin]
+ if keyword_set(xlog) then xrange[0] = xrange[0]>1
+ 
+ ;JRM;;;;;;;;;;;
+  IF n_elements(rotate) EQ 1 THEN BEGIN
+    old_xdata = xdata
+    old_ydata = ydata
+    xdata = old_ydata
+    ydata = old_xdata
+    
+    old_xhist=xhist
+    old_yhist=yhist
+    xhist=old_yhist
+    yhist=old_xhist
+    
+    ;If xrange is not set.
+    ;Then the auto x- range by setting xrange to [0,0].
+    if ~xra_set then xrange=[0,0]
+    if ~xst_set then xstyle=0
+    if ~yst_set then ystyle=1
+    
+ ENDIF
+ 
+ 
+  if ~keyword_set(Overplot) then begin
+
+     cgplot, xdata , ydata,  $ 
+           PSYM = psym, _EXTRA = _extra,xrange=xrange,axiscolor=axiscolor, $
+           xstyle=xstyle, xlog = xlog, ylog = ylog, yrange=yrange, $
+           ystyle=ystyle, /nodata,window=window
+	   if keyword_Set(window) then cgcontrol,execute=0
+  endif
+;JRM;;;;;;;;;;;;;
+
+;;;
+;   If doing a fill of the histogram, then go for it.
+;
+  if N_elements(color) EQ 0 then color = cgcolor('opposite')
+ 
+ if keyword_set(Fill) then begin
+    ;JRM;;;;;;;;;;;
+    xcrange = keyword_set(xlog)? 10^!X.CRANGE : !X.CRANGE
+    ycrange = keyword_set(ylog)? 10^!Y.CRANGE : !Y.CRANGE
+       
+    IF n_elements(rotate) EQ 0 THEN BEGIN
+       Xfill = transpose([[Xhist-bin/2.0],[Xhist+bin/2.0]])
+       Xfill = reform(Xfill, n_elements(Xfill))
+       Xfill = [Xfill[0], Xfill, Xfill[n_elements(Xfill)-1]]
+       Yfill = transpose([[Yhist],[Yhist]])
+       Yfill = reform(Yfill, n_elements(Yfill))
+    
+       if keyword_set(ylog) then Yfill = [ycrange[0]/10, Yfill, ycrange[0]/10] $
+       else yfill = [0, yfill, 0 ]
+       
+    ENDIF ELSE BEGIN
+       Xfill = transpose([[Xhist],[Xhist]])
+       Xfill = reform(Xfill, n_elements(Xfill))
+       Yfill = transpose([[Yhist-bin/2.0],[Yhist+bin/2.0]])
+       Yfill = reform(Yfill, n_elements(Yfill))
+       Yfill = [Yfill[0], Yfill, Yfill[n_elements(Yfill)-1]]
+    
+       if keyword_set(xlog) then Xfill = [xcrange[0]/10, xfill, xcrange[0]/10] $
+       else xfill = [0, xfill, 0 ]
+    ENDELSE
+    ;JRM;;;;;;;;;;;
+
+    ;; TPEB;;;;;;;;;;;
+    ;; Check if plot ranges are reversed (i.e. large to small)
+    Xfill = (XCRANGE[0] GT XCRANGE[1]) ? Xfill > XCRANGE[1] < XCRANGE[0] : $
+            Xfill > XCRANGE[0] < XCRANGE[1] ;Make sure within plot range
+    
+    Yfill = (YCRANGE[0] GT YCRANGE[1]) ? Yfill > YCRANGE[1] < YCRANGE[0] : $
+            Yfill > YCRANGE[0] < YCRANGE[1]
+    ;; TPEB;;;;;;;;;;;
+    
+    if keyword_set(Fcolor) then Fc = Fcolor else Fc = 'Opposite'
+    if keyword_set(Fline) then begin
+       Fs =  keyword_set(Fspacing) ? Fspacing : 0
+       Fo =  keyword_set(Forientation) ? Forientation: 0
+       cgcolorfill, Xfill,Yfill, color=Fc, /line_fill, spacing=Fs, orient=Fo, $
+         thick = fthick, WINDOW=window
+    
+    endif else begin
+   
+       if keyword_set(Fpattern) then begin
+          cgcolorfill, Xfill,Yfill, color=Fc, pattern=Fpattern, window=window
+       endif else begin
+          cgcolorfill, Xfill,Yfill, color=Fc,window=window
+       endelse
+    endelse
+ endif
+ 
+ ;JRM;;;;;;;;;;;
+ IF n_elements(rotate) GT 0 THEN BEGIN
+    ;Need to determine the positions and use plotS.
+    ycrange = keyword_set(ylog)? 10^!Y.CRANGE : !Y.CRANGE
+    xcrange = keyword_set(xlog)? 10^!X.CRANGE : !X.CRANGE
+    cgplots, xdata[0]<xcrange[1], ycrange[1]<(ydata[0]-bin/2)>ycrange[0], $
+           color=color,Thick = thick, LINESTYLE = linestyle, ADDCMD=window
+    cgplots, xdata[0]<xcrange[1], ycrange[1]<(ydata[1]-bin/2)>ycrange[0], $
+           color=color,THICK = thick, LINESTYLE= linestyle, ADDCMD=window
+    FOR i=1, n_elements(xdata)-2 DO BEGIN
+       cgplots, xdata[i]<xcrange[1], ycrange[1]<(ydata[i]-bin/2)>ycrange[0], $
+              color=color, THICK=thick, LINESTYLE= linestyle, $
+	      /CONTINUE,ADDCMD=window
+       cgplots, xdata[i]<xcrange[1], ycrange[1]<(ydata[i+1]-bin/2)>ycrange[0], $
+              color=color, /CONTINUE,THICK=thick, LINESTYLE=linestyle, $
+	       ADDCMD=window
+    ENDFOR
+    cgplots, xdata[i]<xcrange[1], ycrange[1]<(ydata[i]-bin/2)>ycrange[0], $
+           color=color, /CONTINUE, THICK=thick, LINESTYLE = linestyle, $
+	   ADDCMD=window
+ ENDIF ELSE BEGIN
+    cgplot, /over, xdata, ydata, XSTYLE= xstyle, YSTYLE = ystyle, $ 
+           PSYM = psym, THICK=thick, LINESTYLE = linestyle, $
+	    _EXTRA = _extra,color=color,ADDCMD=window
+    ENDELSE
+ ;JRM;;;;;;;;;;;
+ 
+ ; Make histogram boxes by drawing lines in data color.
+if keyword_set(boxplot) then begin
+   ;JRM;;;;;;;;;;;
+   IF n_elements(rotate) EQ 0 THEN BEGIN
+      ycrange = keyword_set(ylog)? 10^!Y.CRANGE : !Y.CRANGE
+      FOR j =0 ,N_Elements(xhist)-1 DO BEGIN
+         cgPlotS, [xhist[j], xhist[j]]-bin/2, [YCRange[0], yhist[j], Ycrange[1]], $
+                Color=Color,noclip=0, THICK=thick, LINESTYLE = linestyle, $
+		_Extra=extra,ADDCMD=window
+      ENDFOR 
+      
+   ENDIF ELSE BEGIN
+      xcrange = keyword_set(xlog)? 10^!X.CRANGE : !X.CRANGE
+      FOR j =0 ,N_Elements(xhist)-1 DO BEGIN
+         cgPlotS, [xcrange[0], xhist[j]<xcrange[1]], [yhist[j], $
+	            yhist[j]]-bin/2, ADDCMD=window, THICK=thick, $
+                    LINESTYLE = linestyle, Color=Color, noclip=0
+      ENDFOR 
+   ENDELSE
+   ;JRM;;;;;;;;;;;
+endif
+
+ if keyword_Set(window) then cgcontrol,execute=1
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/plotsym.pro b/Code/script_idl_mv/astrolib/plotsym.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0188320a36cf9504b581af3b6d9f358cfd7dc551
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/plotsym.pro
@@ -0,0 +1,135 @@
+pro plotsym, psym, psize, FILL=fill,thick=thick,Color = color
+;+
+; NAME:
+;     PLOTSYM
+; PURPOSE:
+;     Define useful plotting symbols not in the standard !PSYM definitions.
+; EXPLANATION:
+;     After a symbol has been defined with PLOTSYM, a plotting command should
+;     follow with either PSYM = 8 or !P.PSYM = 8 (see USERSYM)
+;
+;     For additional rotationally symmetric plotting symbols, see VSYM.PRO
+;     Also see CGSYMCAT in the/coyote directory.
+; CALLING SEQUENCE:
+;     PLOTSYM, PSYM,[ PSIZE, /FILL, THICK=, COLOR=]
+;
+; INPUTS:
+;     PSYM -  The following integer values of PSYM will create the
+;             corresponding plot symbols
+;     0 - circle
+;     1 - downward arrow (upper limit), base of arrow begins at plot value             value
+;     2 - upward arrow (lower limt)
+;     3 - 5 pointed star
+;     4 - triangle
+;     5 - upside down triangle
+;     6 - left pointing arrow
+;     7 - right pointing arrow
+;     8 - square
+;
+;     Arrows are defined such that their base begins at their origin.
+;
+; OPTIONAL INPUTS:
+;     PSIZE - Size of the plotting symbol in multiples of the default size
+;               (default PSIZE=1).  Does not need to be an integer
+;
+; OPTIONAL INPUT KEYWORD:
+;     FILL -  Parameter indicating whether to fill the symbol (see USERSYM)
+;             The default is 0, unfilled symbol.  Does not affect arrows
+;             or character symbols.
+;     THICK -  Thickness of unfilled symbols. Default is 1.
+;     COLOR - Color of the symbols, Default is !P.color
+; OUTPUTS:
+;     None
+;
+; EXAMPLES:
+;     Plot Y vs. X with filled stars as the symbol, twice the default size
+;     IDL> PLOTSYM, 3 ,2, /FILL       ;Plotting symbol is a filled star,
+;                                       ;twice default size
+;     IDL> PLOT,X,Y,PSYM=8            ;Set PSYM = 8 to get star symbol
+;
+;     Now plot Y vs. X with an open circle as the symbol
+;
+;      IDL> PLOTSYM, 0               ;Plotting symbol is a circle
+;      IDL> PLOT,X,Y,PSYM=8
+;
+; METHOD:
+;     Appropriate X,Y vectors are used to define the symbol and passed to the
+;     USERSYM command.
+;
+; REVISION HISTORY
+;      Written       W. Landsman         June 1992
+;      18-JAN-1996    Added a square symbol, HCW.
+;      98Aug20         Added keyword thick parameter - RCB.
+;      April 2001     Added COLOR keyword    WBL
+;-
+ On_error,2
+
+ if N_elements(psym) LT 1 then begin
+     print,'Syntax - PLOTSYM, psym, [ size, /FILL, THICK= ]'
+     print,'  PSYM values 0 - circle, 1 - down arrow, 2 - up arrow, 3 - star'
+     print,'       4 - triangle, 5 - upside down triangle, 6 - left arrow'
+     print,'       7 - right arrow, 8 - square'
+     return
+ endif
+
+ if ( N_elements(psize) LT 1 ) then psize = 1 else psize = psize > 0.1
+
+ if ~keyword_set(FILL) then fill = 0
+ if ~keyword_set(thick) then thick=1
+
+  case psym of
+  0:  begin          ;Circle
+    ang = 2*!PI*findgen(49)/48.     ;Get position every 5 deg
+    xarr = psize*cos(ang)  &  yarr = psize*sin(ang)
+    end
+1:  begin                                     ;Down arrow
+    xarr = [0,0,.5,0,-.5]*psize
+    yarr = [0,-2,-1.4,-2,-1.4]*psize
+    fill = 0
+    end
+2:  begin                                     ;Up arrow
+    xarr = [0,0,.5,0,-.5]*psize
+    yarr = [0,2,1.4,2,1.4]*psize
+    fill = 0
+    end
+3:  begin                                     ;Star
+   ang = (360. / 10 * findgen(11) + 90) / !RADEG  ;star angles every 36 deg
+   r = ang*0
+   r[2*indgen(6)] = 1.
+   cp5 = cos(!pi/5.)
+   r1 = 2. * cp5 - 1. / cp5
+   r[2*indgen(5)+1] = r1
+   r = r * psize / sqrt(!pi/4.) * 2. / (1.+r1)
+   xarr = r * cos(ang)   &   yarr = r * sin(ang)
+   end
+4:  begin                                     ;Triangle
+    xarr = [-1,0,1,-1]*psize
+    yarr = [-1,1,-1,-1]*psize
+    end
+5:  begin                                     ;Upside down triangle
+    xarr = [-1, 0, 1, -1]*psize
+    yarr = [ 1,-1, 1, 1]*psize
+    end
+6:  begin                                     ;Left pointing arrow
+    yarr = [0, 0, 0.5, 0, -.5]*psize
+    xarr = [0,-2,-1.4,-2,-1.4]*psize
+    fill = 0
+    end
+7:  begin                                     ;Left pointing arrow
+    yarr = [ 0, 0, 0.5, 0, -.5] * psize
+    xarr = [ 0, 2, 1.4, 2, 1.4] * psize
+    fill = 0
+    end
+8:  begin                                     ;Square
+    xarr = [-1,-1,1, 1,-1] * psize
+    yarr = [-1, 1,1,-1,-1] * psize
+    end
+ else: message,'Unknown plotting symbol value of '+strtrim(psym,2)
+ endcase
+
+ if N_elements(color) GT 0 then $
+ usersym, xarr, yarr, FILL = fill,thick=thick, color = color else $
+ usersym, xarr, yarr, FILL = fill,thick=thick
+ return
+ end
+
diff --git a/Code/script_idl_mv/astrolib/poidev.pro b/Code/script_idl_mv/astrolib/poidev.pro
new file mode 100644
index 0000000000000000000000000000000000000000..70bbcaf83e324ce3c17fb25fd24cb070dac4a43f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/poidev.pro
@@ -0,0 +1,134 @@
+function poidev, xm, SEED = seed
+;+
+; NAME:
+;     POIDEV
+; PURPOSE:
+;     Generate a Poisson random deviate
+; EXPLANATION:
+;     Return an integer random deviate drawn from a Poisson distribution with
+;     a specified mean.    Adapted from procedure of the same name in 
+;     "Numerical Recipes" by Press et al. (1992), Section 7.3
+;
+;     NOTE: This routine became partially obsolete in V5.0 with the 
+;     introduction of the POISSON keyword to the intrinsic functions 
+;     RANDOMU and RANDOMN.     However, POIDEV is still useful for adding 
+;     Poisson noise to an existing image array, for which the coding is much 
+;     simpler than it would be using RANDOMU (see example 1) 
+; CALLING SEQUENCE:
+;     result = POIDEV( xm, [ SEED = ] )
+;
+; INPUTS:
+;     xm - numeric scalar, vector or array, specifying the mean(s) of the 
+;          Poisson distribution
+;
+; OUTPUT:
+;     result - Long integer scalar or vector, same size as xm
+;
+; OPTIONAL KEYWORD INPUT-OUTPUT:
+;     SEED -  Scalar to be used as the seed for the random distribution.  
+;             For best results, SEED should be a large (>100) integer.
+;             If SEED is undefined, then its value is taken from the system 
+;             clock (see RANDOMU).    The value of SEED is always updated 
+;             upon output.   This keyword can be used to have POIDEV give 
+;             identical results on consecutive runs.     
+;
+; EXAMPLE:
+;     (1) Add Poisson noise to an integral image array, im
+;              IDL> imnoise = POIDEV( im)
+;
+;     (2) Verify the expected mean  and sigma for an input value of 81
+;              IDL> p = POIDEV( intarr(10000) + 81)   ;Test for 10,000 points
+;              IDL> print,mean(p),sigma(p)
+;     Mean and sigma of the 10000 points should be close to 81 and 9
+;
+; METHOD: 
+;     For small values (< 20) independent exponential deviates are generated 
+;     until their sum exceeds the specified mean, the number of events 
+;     required is returned as the Poisson deviate.   For large (> 20) values,
+;     uniform random variates are compared with a Lorentzian distribution 
+;     function.
+;
+; NOTES:
+;     Negative values in the input array will be returned as zeros.  
+;
+;       
+; REVISION HISTORY:
+;      Version 1               Wayne Landsman        July  1992
+;      Added SEED keyword                            September 1992
+;      Call intrinsic LNGAMMA function               November 1994
+;      Converted to IDL V5.0   W. Landsman   September 1997
+;      Use COMPLEMENT keyword to WHERE()        W. Landsman August 2008
+;-
+  On_error,2
+  compile_opt idl2
+
+ Npts = N_elements( xm)
+ 
+ case NPTS of 
+ 0: message,'ERROR - Poisson mean vector (first parameter) is undefined'
+ 1: output = lonarr(1) 
+ else: output = make_array( SIZE = size(xm), /NOZERO ) 
+ endcase 
+ 
+   index = where( xm LE 20, Nindex, complement=big, Ncomplement=Nbig)
+
+   if Nindex GT 0 then begin
+
+   g = exp( -xm[ index] )           ;To compare with exponential distribution
+   em1 = replicate( -1, Nindex )    ;Counts number of events
+   t = replicate( 1., Nindex )          ;Counts (log) of total time
+
+  Ngood = Nindex
+  good = lindgen( Nindex)                 ;GOOD indexes the original array
+  good1 = good                         ;GOOD1 indexes the GOOD vector
+
+ REJECT:  em1[good] = em1[good] + 1      ;Increment event counter
+   t = t[good1]*randomu( seed, Ngood )   ;Add exponential deviate, equivalent
+                                         ;to multiplying random deviate
+   good1 = where( t GT g[good], Ngood1)  ;Has sum of exponential deviates 
+                                         ;exceeded specified mean?
+   if ( Ngood1 GE 1 ) then begin
+           good = good[ good1]
+           Ngood = Ngood1
+           goto, REJECT
+   endif
+   output[index] = em1
+ endif
+     if Nindex EQ Npts then return, output
+; ***************************************
+
+    xbig = xm[big]
+
+    sq = sqrt( 2.*xbig )           ;Sq, Alxm, and g are precomputed
+    alxm = alog( xbig )
+    g = xbig * alxm - lngamma( xbig + 1.)
+
+    Ngood = Nbig  & Ngood1 = Nbig
+    good = lindgen( Ngood)
+    good1 = good
+    y = fltarr(Ngood, /NOZERO ) & em = y
+
+
+REJECT1:   y[good] = tan( !PI * randomu( seed, Ngood ) )  
+   em[good] = sq[good]*y[good] + xbig[good]
+   good2 = where( em[good] LT 0. , Ngood )
+   if (Ngood GT 0) then begin
+            good = good[good2]
+            goto, REJECT1
+   endif
+
+   fixem = long( em[good1] )
+   test = check_math( 0, 1)         ;Don't want overflow messages
+   t = 0.9*(1. + y[good1]^2)*exp( fixem*alxm[good1] - $ 
+               lngamma( fixem + 1.) - g[good1] )
+   good2 = where( randomu (seed, Ngood1) GT T , Ngood)
+   if ( Ngood GT 0 ) then begin
+            good1 = good1[good2]
+            good = good1
+            goto, REJECT1
+   endif
+   output[ big ] = long(em)
+
+ return, output
+
+ end
diff --git a/Code/script_idl_mv/astrolib/polint.pro b/Code/script_idl_mv/astrolib/polint.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9c36b4b894f05604b8fb53fadf0b4705f5ca1082
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/polint.pro
@@ -0,0 +1,85 @@
+pro polint, xa, ya, x, y, dy
+;+
+; NAME:
+;     POLINT
+; PURPOSE:
+;     Interpolate a set of N points by fitting a polynomial of degree N-1
+; EXPLANATION:
+;     Adapted from algorithm in Numerical Recipes, Press et al. (1992), 
+;     Section 3.1.
+;
+; CALLING SEQUENCE
+;     POLINT, xa, ya, x, y, [ dy ]
+; INPUTS:
+;     XA - X Numeric vector, all values must be distinct.   The number of
+;          values in XA should rarely exceed 10 (i.e. a 9th order polynomial)
+;     YA - Y Numeric vector, same number of elements
+;     X - Numeric scalar specifying value to be interpolated 
+;
+; OUTPUT:
+;     Y - Scalar, interpolated value in (XA,YA) corresponding to X
+;
+; OPTIONAL OUTPUT
+;     DY - Error estimate on Y, scalar
+;
+; EXAMPLE:
+;     Find sin(2.5) by polynomial interpolation on sin(indgen(10))
+;
+;               IDL> xa = indgen(10)
+;               IDL> ya = sin( xa )
+;               IDL> polint, xa, ya, 2.5, y ,dy
+;             
+;     The above method gives  y = .5988 & dy = 3.1e-4  a close
+;     approximation to the actual sin(2.5) = .5985
+;
+; METHOD:
+;     Uses Neville's algorithm to iteratively build up the correct
+;     polynomial, with each iteration containing one higher order.
+;
+; REVISION HISTORY:
+;     Written W. Landsman                 January, 1992
+;     Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+
+ if N_params() LT 4 then begin
+     print,'Syntax - polint, xa, ya, x, y, [ dy ]'
+     print,'     xa,ya - Input vectors to be interpolated'
+     print,'     x - Scalar specifying point at which to interpolate'
+     print,'     y - Output interpolated scalar value'
+     print,'     dy - Optional error estimate on y'
+     return
+ endif
+
+ N = N_elements( xa ) 
+ if N_elements( ya ) NE N then message, $
+      'ERROR - Input X and Y vectors must have same number of elements'
+
+; Find the index of XA which is closest to X
+
+ dif = min( abs(x-xa), ns )
+
+ c = ya   &   d = ya
+ y = ya[ns]
+ ns = ns - 1
+
+ for m = 1,n-1 do begin
+
+    ho = xa[0:n-m-1] - x
+    hp = xa[m:n-1] - x
+    w = c[1:n-m] - d[0:n-m-1]
+    den = ho - hp
+    if min( abs(den) ) EQ 0 then message, $
+           'ERROR - All input X vector values must be distinct'
+    den = w / den
+    d = hp * den
+    c = ho * den
+    if ( 2*ns LT n-m-1 ) then dy = c[ns+1] else begin
+                  dy = d[ns]
+                  ns = ns - 1
+    endelse
+    y = y + dy
+ endfor
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/polrec.pro b/Code/script_idl_mv/astrolib/polrec.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1b246b1156181abbff0ca70f62e7e8e9c903bb0e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/polrec.pro
@@ -0,0 +1,52 @@
+;-------------------------------------------------------------
+;+
+; NAME:
+;       POLREC
+; PURPOSE:
+;       Convert 2-d polar coordinates to rectangular coordinates.
+; CATEGORY:
+; CALLING SEQUENCE:
+;       polrec, r, a, x, y
+; INPUTS:
+;       r, a = vector in polar form: radius, angle (radians).  in
+; KEYWORD PARAMETERS:
+;       Keywords:
+;         /DEGREES means angle is in degrees, else radians.
+; OUTPUTS:
+;       x, y = vector in rectangular form, double precision     out
+; COMMON BLOCKS:
+; NOTES:
+; MODIFICATION HISTORY:
+;       R. Sterner. 18 Aug, 1986.
+;       Johns Hopkins University Applied Physics Laboratory.
+;       RES 13 Feb, 1991 --- added /degrees.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       1999 May 03 --- Made double precision.  R. Sterner.
+;
+; Copyright (C) 1986, Johns Hopkins University/Applied Physics Laboratory
+; This software may be used, copied, or redistributed as long as it is not
+; sold and this copyright notice is reproduced on each copy made.  This
+; routine is provided as is without any express or implied warranties
+; whatsoever.  Other limitations apply as described in the file disclaimer.txt.
+;-
+;-------------------------------------------------------------
+ 
+	PRO POLREC, R, A, X, Y, help=hlp, degrees=degrees
+ 
+	IF (N_PARAMS(0) LT 4) or keyword_set(hlp) THEN BEGIN
+	  PRINT,' Convert 2-d polar coordinates to rectangular coordinates.
+	  PRINT,' polrec, r, a, x, y
+	  PRINT,'   r, a = vector in polar form: radius, angle (radians).  in'
+	  PRINT,'   x, y = vector in rectangular form.                     out'
+          print,' Keywords:'
+          print,'   /DEGREES means angle is in degrees, else radians.'
+	  RETURN
+	ENDIF
+ 
+	cf = 1.D0
+	if keyword_set(degrees) then cf = 180.0d/!dpi
+ 
+	X = R*COS(A/cf)
+	Y = R*SIN(A/cf)	
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/poly_smooth.pro b/Code/script_idl_mv/astrolib/poly_smooth.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0289e4f473909f9d639757665b6fb9121fae6fc4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/poly_smooth.pro
@@ -0,0 +1,191 @@
+function poly_smooth, data, width, DEGREE=degree, NLEFT=nl, NRIGHT=nr,  $
+                                DERIV_ORDER=order, COEFFICIENTS=filter_coef
+;+
+; NAME:
+;       POLY_SMOOTH  
+;
+; PURPOSE:
+;       Apply a least-squares (Savitzky-Golay) polynomial smoothing filter
+; EXPLANATION:
+;       Reduce noise in 1-D data (e.g. time-series, spectrum) but retain 
+;       dynamic range of variations in the data by applying a least squares 
+;       smoothing polynomial filter,
+;
+;       Also called the Savitzky-Golay smoothing filter, cf. Numerical
+;       Recipes (Press et al. 1992, Sec.14.8)
+;
+;       The low-pass filter coefficients are computed by effectively
+;       least-squares fitting a polynomial in moving window,
+;       centered on each data point, so the new value will be the
+;       zero-th coefficient of the polynomial. Approximate first derivates
+;       of the data can be computed by using first degree coefficient of
+;       each polynomial, and so on. The filter coefficients for a specified
+;       polynomial degree and window width are computed independent of any
+;       data, and stored in a common block. The filter is then convolved
+;       with the data array to result in smoothed data with reduced noise,
+;       but retaining higher order variations (better than SMOOTH).
+;
+;       This procedure became partially obsolete in IDL V5.4 with the 
+;       introduction of the SAVGOL function, which computes the smoothing
+;       coefficients.
+; CALLING SEQUENCE:
+;
+;       spectrum = poly_smooth( data, [ width, DEGREE = , NLEFT = , NRIGHT = 
+;                                       DERIV_ORDER = ,COEFF = ]
+;
+; INPUTS:
+;       data = 1-D array, such as a spectrum or time-series.
+;
+;       width = total number of data points to use in filter convolution,
+;               (default = 5, using 2 past and 2 future data points),
+;               must be larger than DEGREE of polynomials, and a guideline is to
+;               make WIDTH between 1 and 2 times the FWHM of desired features.
+;
+; OPTIONAL INPUT KEYWORDS:
+;
+;       DEGREE = degree of polynomials to use in designing the filter
+;               via least squares fits, (default DEGREE = 2)
+;               The higher degrees will preserve sharper features.
+;
+;       NLEFT = # of past data points to use in filter convolution,
+;               excluding current point, overrides width parameter,
+;               so that width = NLEFT + NRIGHT + 1.  (default = NRIGHT)
+;
+;       NRIGHT = # of future data points to use (default = NLEFT).
+;
+;       DERIV_ORDER = order of derivative desired (default = 0, no derivative).
+;
+; OPTIONAL OUTPUT KEYWORD:
+;
+;       COEFFICIENTS = optional output of the filter coefficients applied,
+;               but they are all stored in common block for reuse, anyway.
+; RESULTS:
+;       Function returns the data convolved with polynomial filter coefs.
+;
+; EXAMPLE:
+;
+;       Given a wavelength - flux spectrum (w,f), apply a 31 point quadratic
+;       smoothing filter and plot
+;
+;       IDL> cgplot, w, poly_smooth(f,31) 
+; COMMON BLOCKS:
+;       common poly_smooth, degc, nlc, nrc, coefs, ordermax
+;
+; PROCEDURE:
+;       As described in Numerical Recipes, 2nd edition sec.14.8, 
+;       Savitsky-Golay filter.
+;       Matrix of normal eqs. is formed by starting with small terms
+;       and then adding progressively larger terms (powers).
+;       The filter coefficients of up to derivative ordermax are stored
+;       in common, until the specifications change, then recompute coefficients.
+;       Coefficients are stored in convolution order, zero lag in the middle.
+;
+; MODIFICATION HISTORY:
+;       Written, Frank Varosi NASA/GSFC 1993.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use /EDGE_TRUNCATE keyword to CONVOL  W. Landsman March 2006
+;-
+        compile_opt idl2
+        On_error,2
+
+        if N_params() LT 1 then begin
+                print,'Syntax - smoothdata = ' + $ 
+                        'poly_smooth( data , width, [ DEGREE = , NLEFT = '
+        print,f='(35x,A)', 'NRIGHT = , DERIV_ORDER =, COEFFICIENT = ]'
+        return, -1
+        endif
+
+  common poly_smooth, degc, nlc, nrc, coefs, ordermax
+
+        if N_elements( degree ) NE 1 then degree = 2
+        if N_elements( order ) NE 1 then order = 0
+        order = ( order < (degree-1) ) > 0
+
+        if N_elements( width ) EQ 1 then begin
+                width = fix( width ) > 3
+                if (N_elements(nr) NE 1) AND (N_elements(nl) NE 1) then begin
+                        nl = width/2
+                        nr = width - nl -1
+                   endif
+           endif
+
+        if N_elements( nr ) NE 1 then begin
+                if N_elements( nl ) EQ 1 then  nr = nl  else  nr = 2
+           endif
+
+        if N_elements( nl ) NE 1 then begin
+                if N_elements( nr ) EQ 1 then  nl = nr  else  nl = 2
+           endif
+
+        if N_elements( coefs ) LE 1 then begin
+                degc = 0
+                nlc = 0
+                nrc = 0
+                ordermax = 3
+           endif
+
+        if (degree NE degc) OR (nl NE nlc) OR (nr NE nrc) OR $
+                                                (order GT ordermax) then begin
+                degree = degree > 2
+                ordermax = ( ordermax < 3 ) > order
+                nj = degree+1
+                nl = nl > 0
+                nr = nr > 0
+                nrl = nr + nl + 1
+
+                if (nrl LE degree) then begin
+                        message,"# of points in filter must be > degree",/INFO
+                        return, data
+                   endif
+
+                ATA = fltarr( nj, nj )
+                ATA[0,0] = 1
+                iaj = indgen( nj ) # replicate( 1, nj )
+                iaj = iaj + transpose( iaj )
+                m1_iaj = (-1)^iaj
+
+                for k = 1, nr>nl do begin
+                    k_iaj = float( k )^iaj
+                    CASE 1 OF
+                        ( k LE nr<nl ): ATA = ATA + ( k_iaj + k_iaj*m1_iaj )
+                        ( k LE nr ):    ATA = ATA + k_iaj
+                        ( k LE nl ):    ATA = ATA + k_iaj * m1_iaj
+                     ENDCASE
+                  endfor
+
+                LUdc, ATA, LUindex, /COL
+
+                Bmat = fltarr( nj, degree<(ordermax+1) )
+                B = fltarr( nj )
+
+                for m = 0, (degree-1)<ordermax do begin
+                        B[*] = 0
+                        B[m] = 1
+                        Bmat[0,m] = LUsol(ATA, LUindex, B, /COL)
+                  endfor
+
+                kvec = [0]
+                if (nl GT 0) then kvec = [ rotate( -indgen( nl )-1, 2 ), kvec ]
+                if (nr GT 0) then kvec = [ kvec, indgen( nr )+1 ]
+                Kmat = fltarr( nrl, nj )
+                Kmat[*,0] = 1
+                for m = 1,degree do Kmat[0,m] = Kmat[*,m-1] * kvec
+
+                coefs = Kmat # Bmat
+                degc = degree
+                nlc = nl
+                nrc = nr
+
+                if (nr GT nl) then begin
+                        sc = size( coefs )
+                        coefs = [  fltarr( nr-nl, sc[2] ),  coefs ]
+                  endif else if (nl GT nr) then begin
+                        sc = size( coefs )
+                        coefs = [ coefs,  fltarr( nl-nr, sc[2] )  ]
+                   endif
+           endif
+
+        filter_coef = coefs[*,order]
+
+return, convol( data, filter_coef, /EDGE_TRUNCATE )
+end
diff --git a/Code/script_idl_mv/astrolib/polyleg.pro b/Code/script_idl_mv/astrolib/polyleg.pro
new file mode 100644
index 0000000000000000000000000000000000000000..bc41bab9b1d071224f100d03694172f5ffb78eb3
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/polyleg.pro
@@ -0,0 +1,76 @@
+function polyleg,x,coeff
+;+
+; NAME:
+;       POLYLEG
+;
+; PURPOSE:
+;       Evaluate a Legendre polynomial with specified coefficients.
+; EXPLANATION:
+;       Meant to be used analogously to the POLY function in the IDL User's
+;       Library distribution.
+;
+; CALLING SEQUENCE:
+;       Result = POLYLEG( X, C )        
+;
+; INPUTS:
+;       X - input variable, scalar or vector    
+;       C - vector of Legendre polynomial coefficients. 
+; OUTPUTS:
+;       POLYLEG returns a result equal to:
+;               C[0] + C[1]*P_1(x) + C[2]*P_2(x) + ...
+;
+;       where P_j(x) is the jth Legendre polynomial.   The output will have
+;       the same dimensions as the input X variable.
+;
+; EXAMPLE:
+;       If x = [0.5, 1.0] and C = [2.4, 1.3, 2.5] then
+;       print, polyleg(x, c)    ====> [2.7375, 6.20]
+;
+;       The result can be checked using the first 3 Legendre polynomial terms
+;       C[0] + C[1]*x + C[2]*(0.5*(3*x^2-1))
+; METHOD:
+;       Uses the recurrence relation of Legendre polynomials
+;               (n+1)*P_n+1(x) = (2n+1)*x*P_n(x) - n*P_n-1(x)
+;       evaluated with the Clenshaw recurrence formula, see Numerical Recipes
+;       by Press et al. (1992), Section 5.5
+;
+; REVISION HISTORY:
+;       Written W. Landsman   Hughes STX Co.        April, 1995    
+;       Fixed for double precision  W. Landsman     May, 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+ 
+ if N_params() LT 2 then begin
+        print,'Syntax - result = POLYLEG( X, Coeff)'
+        return, -1
+ endif
+
+ N= N_elements(coeff) -1
+ M = N_elements(x)
+
+ case N of 
+ 0: return, replicate( coeff, M)
+ 1: return, x* coeff[1] + coeff[0]
+ else:
+ endcase
+
+; If X is double then compute in double; otherwise compute in real
+
+ if size(x,/TNAME) EQ 'DOUBLE'  then begin      
+        y = dblarr( M, N+2)
+        jj = dindgen(N) + 2.0d
+ endif else begin
+        y = fltarr( M, N+2 )
+        jj = findgen(N) + 2.
+ endelse
+
+ beta1 =  -jj / (jj+1)
+ for j = N,1,-1 do begin
+
+        alpha = (2*j + 1.)*x/float(j + 1.) 
+        y[0,j-1] = alpha*y[*,j] + beta1[j-1]*y[*,j+1] + coeff[j]
+ endfor
+
+ return, -0.5*y[*,1] + x*y[*,0] + coeff[0]
+ end
diff --git a/Code/script_idl_mv/astrolib/posang.pro b/Code/script_idl_mv/astrolib/posang.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e32dd8585f1439b8a3ec878700d9c80488f34af9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/posang.pro
@@ -0,0 +1,121 @@
+PRO POSANG,u,ra1,dc1,ra2,dc2,angle
+;+
+; NAME:
+;       POSANG
+; PURPOSE:
+;       Computes rigorous position angle of source 2 relative to source 1
+;       
+; EXPLANATION:
+;       Computes the rigorous position angle of source 2 (with given RA, Dec) 
+;       using source 1 (with given RA, Dec) as the center.
+; 
+; CALLING SEQUENCE:
+;       POSANG, U, RA1, DC1, RA2, DC2, ANGLE
+;
+; INPUTS:
+;       U    -- Describes units of inputs and output:
+;               0:  everything radians
+;               1:  RAx in decimal hours, DCx in decimal
+;                       degrees, ANGLE in degrees
+;       RA1  -- Right ascension of point 1
+;       DC1  -- Declination of point 1
+;       RA2  -- Right ascension of point 2
+;       DC2  -- Declination of point 2
+;
+;   OUTPUTS:
+;       ANGLE-- Angle of the great circle containing [ra2, dc2] from
+;               the meridian containing [ra1, dc1], in the sense north
+;               through east rotating about [ra1, dc1].  See U above 
+;               for units.
+;
+;   PROCEDURE:
+;       The "four-parts formula" from spherical trig (p. 12 of Smart's
+;       Spherical Astronomy or p. 12 of Green' Spherical Astronomy).
+;
+;   EXAMPLE:
+;       For the star 56 Per, the Hipparcos catalog gives a position of 
+;       RA = 66.15593384, Dec = 33.94988843 for component A, and 
+;       RA = 66.15646079, Dec =  33.96100069 for component B.   What is the
+;       position angle of B relative to A?
+;
+;       IDL> RA1 = 66.15593384/15.d   & DC1 = 33.95988843
+;       IDL> RA2 = 66.15646079/15.d   & DC2 = 33.96100069
+;       IDL> posang,1,ra1,dc1,ra2,dc2, ang
+;            will give the answer of ang = 21.4 degrees
+;   NOTES:
+;       (1) If RA1,DC1 are scalars, and RA2,DC2 are vectors, then ANGLE is a
+;       vector giving the position angle between each element of RA2,DC2 and 
+;       RA1,DC1.   Similarly, if RA1,DC1 are vectors, and RA2, DC2 are scalars,
+;       then DIS is a vector giving the position angle of each element of RA1, 
+;       DC1 and RA2, DC2.    If both RA1,DC1 and RA2,DC2 are vectors then ANGLE 
+;       is a vector giving the position angle between each element of RA1,DC1 
+;       and the corresponding element of RA2,DC2.    If then vectors are not the
+;       same length, then excess elements of the longer one will be ignored.
+;
+;       (2) Note that POSANG is not commutative -- the position angle between
+;        A and B is theta, then the position angle between B and A is 180+theta 
+;   PROCEDURE CALLS:
+;        ISARRAY()
+;   HISTORY:
+;       Modified from GCIRC, R. S. Hill, RSTX, 1 Apr. 1998
+;       Use V6.0 notation W.L. Mar 2011
+;
+;-
+ On_error,2                            ;Return to caller
+ compile_opt idl2
+
+ npar = N_params()
+ IF (npar lt 5)  THEN BEGIN
+   print,'Calling sequence:  POSANG,U,RA1,DC1,RA2,DC2,ANGLE'
+   print,'   U = 0  ==> Everything in radians'
+   print, $
+   '   U = 1  ==> RAx decimal hours, DCx decimal degrees, ANGLE degrees'
+   RETURN
+ENDIF
+
+scalar = (~isarray(ra1) ) && (~isarray(ra2) )
+IF scalar THEN BEGIN
+    IF (ra1 eq ra2) && (dc1 eq dc2) THEN BEGIN
+       angle = 0.0d0
+       IF npar eq 5 THEN $
+           print,'Positions are equal:  ', ra1, dc1
+       RETURN
+    ENDIF
+ENDIF
+
+d2r    = !DPI/180.0d0
+h2r    = !DPI/12.0d0
+
+CASE u OF
+   0:  BEGIN                    
+          rarad1 = ra1
+          rarad2 = ra2
+          dcrad1 = dc1
+          dcrad2 = dc2
+       END
+   1:  BEGIN                    
+          rarad1 = ra1*h2r
+          rarad2 = ra2*h2r
+          dcrad1 = dc1*d2r
+          dcrad2 = dc2*d2r
+       END
+   ELSE:  MESSAGE, $
+                'U must be 0 for radians or 1 for hours, degrees, arcsec'
+ENDCASE
+
+radif  = rarad2-rarad1
+angle  = atan(sin(radif),cos(dcrad1)*tan(dcrad2)-sin(dcrad1)*cos(radif))
+
+IF (u ne 0) THEN angle = angle/d2r  
+
+IF (npar eq 5) && (scalar) THEN BEGIN
+    IF (u ne 0) && (abs(angle) ge 0.1) $
+       THEN fmt = '(F14.8)' $
+       ELSE fmt = '(E15.8)'
+    units = (u ne 0) ? ' degrees' : ' radians'   
+    print,'Position angle of target 2 about target 1 is ' $
+        + string(angle,format=fmt) + units
+ENDIF
+
+RETURN
+END                   
diff --git a/Code/script_idl_mv/astrolib/positivity.pro b/Code/script_idl_mv/astrolib/positivity.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6e0abc5756d5d27bf17bc3c7de648b47eaf3ea93
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/positivity.pro
@@ -0,0 +1,50 @@
+function positivity, x, DERIVATIVE=deriv, EPSILON=epsilon
+;+
+; NAME:
+;	POSITIVITY
+; PURPOSE:
+;	Map an image uniquely and smoothly into all positive values.
+; EXPLANATION:
+;	Take unconstrained x (usually an image), and map it uniquely and 
+;	smoothly into positive values.   Negative values of x get mapped to 
+;	interval ( 0, sqrt( epsilon )/2 ], positive values go to 
+;	( sqrt( epsilon )/2, oo ) with deriv approaching 1.  Derivative is 
+;	always 1/2 at x=0.   Derivative is used by the MRL deconvolution 
+;	algorithm.
+;
+; CALLING SEQUENCE:
+;	result = POSITIVITY( x, [ /DERIVATIVE, EPSILON = )
+;
+; INPUTS:
+;	x - input array, unconstrained
+;
+; OUTPUT:
+;	result =  output array = ((x + sqrt(x^2 + epsilon))/2
+;		if the /DERIV keyword is set then instead the derivative of
+;		the above expression with respect to X is returned
+;
+; OPTIONAL INPUT KEYWORDS:
+;	DERIV -  if this keyword set, then the derivative of the positivity
+;		mapping is returned, rather than the mapping itself
+;	EPSILON - real scalar specifying the interval into which to map
+;		negative values.    If EPSILON EQ 0 then the mapping reduces to 
+;		positive truncation.   If EPSILON LT then the mapping reduces to
+;		an identity (no change).  Default is EPSILON = 1e-9 
+;
+; REVISION HISTORY:
+;	 F.Varosi NASA/GSFC 1992, as suggested by R.Pina UCSD.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+	if N_elements( epsilon ) NE 1 then epsilon = 1.e-9
+
+	if keyword_set( deriv ) then begin
+		if (epsilon GT 0) then return,(1 + x/sqrt( x^2 + epsilon ))/2 $
+				  else if (epsilon LT 0) then return,(1)      $
+				  else return,( x GT 0 )
+	  endif else begin
+		if (epsilon GT 0) then return,( x + sqrt( x^2 + epsilon ) )/2 $
+				  else if (epsilon LT 0) then return, x       $
+				  else return,( x > 0 )
+	   endelse
+end
diff --git a/Code/script_idl_mv/astrolib/precess.pro b/Code/script_idl_mv/astrolib/precess.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e304799bb405cf5e1b8fbbe82bf6ca717ba50aa7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/precess.pro
@@ -0,0 +1,163 @@
+pro precess, ra, dec, equinox1, equinox2, PRINT = print, FK4 = FK4, $
+        RADIAN=radian
+;+
+; NAME:
+;      PRECESS
+; PURPOSE:
+;      Precess coordinates from EQUINOX1 to EQUINOX2.  
+; EXPLANATION:
+;      For interactive display, one can use the procedure ASTRO which calls 
+;      PRECESS or use the /PRINT keyword.   The default (RA,DEC) system is 
+;      FK5 based on epoch J2000.0 but FK4 based on B1950.0 is available via 
+;      the /FK4 keyword.
+;
+;      Use BPRECESS and JPRECESS to convert between FK4 and FK5 systems
+; CALLING SEQUENCE:
+;      PRECESS, ra, dec, [ equinox1, equinox2, /PRINT, /FK4, /RADIAN ]
+;
+; INPUT - OUTPUT:
+;      RA - Input right ascension (scalar or vector) in DEGREES, unless the 
+;              /RADIAN keyword is set
+;      DEC - Input declination in DEGREES (scalar or vector), unless the 
+;              /RADIAN keyword is set
+;              
+;      The input RA and DEC are modified by PRECESS to give the 
+;      values after precession.
+;
+; OPTIONAL INPUTS:
+;      EQUINOX1 - Original equinox of coordinates, numeric scalar.  If 
+;               omitted, then PRECESS will query for EQUINOX1 and EQUINOX2.
+;      EQUINOX2 - Equinox of precessed coordinates.
+;
+; OPTIONAL INPUT KEYWORDS:
+;      /PRINT - If this keyword is set and non-zero, then the precessed
+;               coordinates are displayed at the terminal.    Cannot be used
+;               with the /RADIAN keyword
+;      /FK4   - If this keyword is set and non-zero, the FK4 (B1950.0) system
+;               will be used otherwise FK5 (J2000.0) will be used instead.
+;      /RADIAN - If this keyword is set and non-zero, then the input and 
+;               output RA and DEC vectors are in radians rather than degrees
+;
+; RESTRICTIONS:
+;       Accuracy of precession decreases for declination values near 90 
+;       degrees.  PRECESS should not be used more than 2.5 centuries from
+;       2000 on the FK5 system (1950.0 on the FK4 system).
+;
+; EXAMPLES:
+;       (1) The Pole Star has J2000.0 coordinates (2h, 31m, 46.3s, 
+;               89d 15' 50.6"); compute its coordinates at J1985.0
+;
+;       IDL> precess, ten(2,31,46.3)*15, ten(89,15,50.6), 2000, 1985, /PRINT
+;
+;               ====> 2h 16m 22.73s, 89d 11' 47.3"
+;
+;       (2) Precess the B1950 coordinates of Eps Ind (RA = 21h 59m,33.053s,
+;       DEC = (-56d, 59', 33.053") to equinox B1975.
+;
+;       IDL> ra = ten(21, 59, 33.053)*15
+;       IDL> dec = ten(-56, 59, 33.053)
+;       IDL> precess, ra, dec ,1950, 1975, /fk4
+;
+; PROCEDURE:
+;       Algorithm from Computational Spherical Astronomy by Taff (1983), 
+;       p. 24. (FK4). FK5 constants from "Astronomical Almanac Explanatory
+;       Supplement 1992, page 104 Table 3.211.1.
+;
+; PROCEDURE CALLED:
+;       Function PREMAT - computes precession matrix 
+;
+; REVISION HISTORY
+;       Written, Wayne Landsman, STI Corporation  August 1986
+;       Correct negative output RA values   February 1989
+;       Added /PRINT keyword      W. Landsman   November, 1991
+;       Provided FK5 (J2000.0)  I. Freedman   January 1994
+;       Precession Matrix computation now in PREMAT   W. Landsman June 1994
+;       Added /RADIAN keyword                         W. Landsman June 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Correct negative output RA values when /RADIAN used    March 1999 
+;       Work for arrays, not just vectors  W. Landsman    September 2003 
+;-    
+  On_error,2                                           ;Return to caller
+
+  npar = N_params()
+  deg_to_rad = !DPI/180.0D0
+
+   if ( npar LT 2 ) then begin 
+
+     print,'Syntax - PRECESS, ra, dec, [ equinox1, equinox2,' + $ 
+                ' /PRINT, /FK4, /RADIAN ]'
+     print,'         NOTE: RA and DEC must be in DEGREES unless /RADIAN is set'
+     return 
+
+  endif else if (npar LT 4) then $
+      read,'Enter original and new equinox of coordinates: ',equinox1,equinox2 
+
+  npts = min( [N_elements(ra), N_elements(dec)] )
+  if npts EQ 0 then $  
+       message,'ERROR - Input RA and DEC must be vectors or scalars'
+  array  = size(ra,/N_dimen) GE 2
+  if array then dimen = size(ra,/dimen)
+
+  if ~keyword_set( RADIAN) then begin
+          ra_rad = ra*deg_to_rad     ;Convert to double precision if not already
+          dec_rad = dec*deg_to_rad 
+  endif else begin
+        ra_rad= double(ra) & dec_rad = double(dec)
+  endelse
+
+  a = cos( dec_rad )  
+
+ CASE npts of                    ;Is RA a vector or scalar?
+
+   1:    x = [a*cos(ra_rad), a*sin(ra_rad), sin(dec_rad)] ;input direction 
+
+   else: begin          
+
+         x = dblarr(npts,3)
+         x[0,0] = reform(a*cos(ra_rad),npts,/over)
+         x[0,1] = reform(a*sin(ra_rad),npts,/over)
+         x[0,2] = reform(sin(dec_rad),npts,/over)
+         x = transpose(x)
+         end
+
+   ENDCASE  
+
+   sec_to_rad = deg_to_rad/3600.d0
+
+; Use PREMAT function to get precession matrix from Equinox1 to Equinox2
+
+  r = premat(equinox1, equinox2, FK4 = fk4)
+
+  x2 = r#x      ;rotate to get output direction cosines
+
+ if npts EQ 1 then begin                 ;Scalar
+
+        ra_rad = atan(x2[1],x2[0])
+        dec_rad = asin(x2[2])
+
+ endif else begin                ;Vector     
+
+        ra_rad = dblarr(npts) + atan(x2[1,*],x2[0,*])
+        dec_rad = dblarr(npts) + asin(x2[2,*])
+
+ endelse
+
+  if ~keyword_set(RADIAN) then begin
+        ra = ra_rad/deg_to_rad
+        ra = ra + (ra LT 0.)*360.D            ;RA between 0 and 360 degrees
+        dec = dec_rad/deg_to_rad
+  endif else begin
+        ra = ra_rad & dec = dec_rad
+        ra = ra + (ra LT 0.)*2.0d*!DPI
+  endelse
+
+  if array then begin
+       ra = reform(ra, dimen , /over)
+       dec = reform(dec, dimen, /over)
+  endif
+
+  if keyword_set( PRINT ) then $
+      print, 'Equinox (' + strtrim(equinox2,2) + '): ',adstring(ra,dec,1)
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/precess_cd.pro b/Code/script_idl_mv/astrolib/precess_cd.pro
new file mode 100644
index 0000000000000000000000000000000000000000..fbf071c2fcbf7258590ee833d43ee8081617f992
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/precess_cd.pro
@@ -0,0 +1,105 @@
+pro PRECESS_CD, cd, epoch1, epoch2, crval_old, crval_new, FK4 = FK4     
+;+
+; NAME:
+;       PRECESS_CD
+;
+; PURPOSE:
+;       Precess the CD (coordinate description) matrix from a FITS header 
+; EXPLANATION:
+;       The CD matrix is precessed from EPOCH1 to EPOCH2.  Called by HPRECESS
+;
+; CALLING SEQUENCE:
+;       PRECESS_CD, cd, epoch1, epoch2, crval_old, crval_new, [/FK4]  
+;
+; INPUTS/OUTPUT:
+;       CD - 2 x 2 CD (coordinate description) matrix in any units
+;               (degrees or radians).  CD will altered on output to contain 
+;               precessed values in the same units.    On output CD will always
+;               be double precision no matter how input.
+;
+; INPUTS:
+;       EPOCH1 - Original equinox of coordinates, scalar (e.g. 1950.0).  
+;       EPOCH2 - Equinox of precessed coordinates, scalar (e.g. 2000.0)
+;       CRVAL_OLD - 2 element vector containing RA and DEC in DEGREES
+;               of the reference pixel in the original equinox
+;       CRVAL_NEW - 2 elements vector giving CRVAL in the new equinox 
+;
+; INPUT KEYWORD:
+;       /FK4 - If this keyword is set, then the precession constants are taken
+;             in the FK4 reference frame.   The default is the FK5 frame.
+;
+; RESTRICTIONS:
+;       PRECESS_CD should not be used more than 2.5 centuries from the
+;       year 1900.      
+;
+; PROCEDURE:
+;       Adapted from the STSDAS program FMATPREC.  Precession changes the
+;       location of the north pole, and thus changes the rotation of
+;       an image from north up.  This is reflected in the precession of the
+;       CD matrix.   This is usually a very small change. 
+;
+; PROCEDURE CALLS:
+;       PRECESS
+;
+; REVISION HISTORY:
+;       Written, Wayne Landsman, ST Systems  February 1988
+;       Fixed sign error in computation of SINRA     March 1992
+;       Added /FK4 keyword                           Feb 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use B/Jprecess for conversion between 1950 and 2000 W.L. Aug 2009
+;-    
+  On_error,2
+  compile_opt idl2
+
+  if N_params() LT 3 then begin    
+      print,'Syntax: precess_cd, cd, epoch1, epoch2, crval_old, crval_new
+      return
+   endif 
+
+   deg_to_rad = !DPI/180.0D
+   crvalold = crval_old * deg_to_rad
+   crvalnew = crval_new * deg_to_rad
+
+   sec_to_rad = deg_to_rad/3600.d0
+   t = 0.001d0 * (epoch2-epoch1)
+
+;   Compute C - inclination of the mean equator in the new equinox relative
+;   to that of the old equinox
+
+   if keyword_set(FK4) then begin
+
+        st = 0.001d0 * (epoch1-1900.d0)
+
+        C = sec_to_rad * T * ( 20046.85D0 - ST*(85.33D0 + 0.37D0*ST) $
+                + T*(-42.67D0 - 0.37D0*ST -41.8D0*T))
+
+   endif else begin
+
+        st = 0.001d0*( epoch1 - 2000.d0)
+
+        C = sec_to_rad * T * (20043.109D0 - ST*(85.33D0 + 0.217D0*ST) $
+                + T*(-42.665D0 - 0.217D0*ST -41.833D0*T))
+   endelse
+
+; Get RA of old pole in new coordinates
+
+  pole_ra = 0. & pole_dec = 90.d       ;Coordinates of old pole (RA is arbitrary)
+  if (epoch1 EQ 2000) && (epoch2 EQ 1950) then begin
+    bprecess, pole_ra, pole_dec,pra,pdec
+    pole_ra =  pra
+  endif else if (epoch1 EQ 1950) and (epoch2 EQ 2000) then begin
+     bprecess, pole_ra, pole_dec,pra,pdec
+     pole_ra =  pra    
+  endif else precess, pole_ra, pole_dec, epoch1, epoch2, FK4 = FK4    
+
+  sind1 = sin( crvalold[1] ) &  sind2 = sin( crvalnew[1] )
+  cosd1 = cos( crvalold[1] ) &  cosd2 = cos( crvalnew[1] )
+  sinra = sin( crvalnew[0] - pole_ra*deg_to_rad)    ;Fixed sign error Mar-92
+  cosfi = (cos(c) - sind1*sind2)/( cosd1*cosd2 )
+  sinfi = ( abs(sin(c) ) * sinra) / cosd1 
+  r = [ [cosfi, sinfi], [-sinfi, cosfi] ]
+
+  cd = r # cd            ;Rotate to new north pole
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/precess_xyz.pro b/Code/script_idl_mv/astrolib/precess_xyz.pro
new file mode 100644
index 0000000000000000000000000000000000000000..01801304a1c4d67d6612328f2943c20b06d7fdd2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/precess_xyz.pro
@@ -0,0 +1,63 @@
+pro precess_xyz,x,y,z,equinox1,equinox2
+;+
+; NAME:
+;	PRECESS_XYZ
+;
+; PURPOSE:
+;	Precess equatorial geocentric rectangular coordinates. 
+;
+; CALLING SEQUENCE:
+;	precess_xyz, x, y, z, equinox1, equinox2
+;
+; INPUT/OUTPUT:
+;	x,y,z: scalars or vectors giving heliocentric rectangular coordinates
+;              THESE ARE CHANGED UPON RETURNING.
+; INPUT:
+;	EQUINOX1: equinox of input coordinates, numeric scalar
+;       EQUINOX2: equinox of output coordinates, numeric scalar
+;
+; OUTPUT:
+;	x,y,z are changed upon return
+;
+; NOTES:
+;   The equatorial geocentric rectangular coords are converted
+;      to RA and Dec, precessed in the normal way, then changed
+;      back to x, y and z using unit vectors.
+;
+;EXAMPLE:
+;	Precess 1950 equinox coords x, y and z to 2000.
+;	IDL> precess_xyz,x,y,z, 1950, 2000
+;
+;HISTORY:
+;	Written by P. Plait/ACC March 24 1999 
+;	   (unit vectors provided by D. Lindler)
+;       Use /Radian call to PRECESS     W. Landsman     November 2000
+;       Use two parameter call to ATAN   W. Landsman    June 2001
+;-
+;check inputs
+   if N_params() NE 5 then begin
+      print,'Syntax - PRECESS_XYZ,x,y,z,equinox1,equinox2'
+      return
+   endif
+
+;take input coords and convert to ra and dec (in radians)
+
+   ra = atan(y,x)
+   del = sqrt(x*x + y*y + z*z)  ;magnitude of distance to Sun
+   dec = asin(z/del) 
+
+;   precess the ra and dec
+    precess, ra, dec, equinox1, equinox2, /Radian
+
+;convert back to x, y, z
+   xunit = cos(ra)*cos(dec)
+   yunit = sin(ra)*cos(dec)
+   zunit = sin(dec)
+
+   x = xunit * del
+   y = yunit * del
+   z = zunit * del
+
+   return
+   end
+
diff --git a/Code/script_idl_mv/astrolib/premat.pro b/Code/script_idl_mv/astrolib/premat.pro
new file mode 100644
index 0000000000000000000000000000000000000000..63b055b3793dd4df7b921fcad7513f1f2ae89155
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/premat.pro
@@ -0,0 +1,92 @@
+function premat, equinox1, equinox2, FK4 = FK4
+;+
+; NAME:
+;       PREMAT
+; PURPOSE:
+;       Return the precession matrix needed to go from EQUINOX1 to EQUINOX2.  
+; EXPLANTION:
+;       This matrix is used by the procedures PRECESS and BARYVEL to precess 
+;       astronomical coordinates
+;
+; CALLING SEQUENCE:
+;       matrix = PREMAT( equinox1, equinox2, [ /FK4 ] )
+;
+; INPUTS:
+;       EQUINOX1 - Original equinox of coordinates, numeric scalar.  
+;       EQUINOX2 - Equinox of precessed coordinates.
+;
+; OUTPUT:
+;      matrix - double precision 3 x 3 precession matrix, used to precess
+;               equatorial rectangular coordinates
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /FK4   - If this keyword is set, the FK4 (B1950.0) system precession
+;               angles are used to compute the precession matrix.   The 
+;               default is to use FK5 (J2000.0) precession angles
+;
+; EXAMPLES:
+;       Return the precession matrix from 1950.0 to 1975.0 in the FK4 system
+;
+;       IDL> matrix = PREMAT( 1950.0, 1975.0, /FK4)
+;
+; PROCEDURE:
+;       FK4 constants from "Computational Spherical Astronomy" by Taff (1983), 
+;       p. 24. (FK4). FK5 constants from "Astronomical Almanac Explanatory
+;       Supplement 1992, page 104 Table 3.211.1.
+;
+; REVISION HISTORY
+;       Written, Wayne Landsman, HSTX Corporation, June 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-    
+  On_error,2                                           ;Return to caller
+
+  npar = N_params()
+
+   if ( npar LT 2 ) then begin 
+
+     print,'Syntax - PREMAT, equinox1, equinox2, /FK4]'
+     return,-1 
+
+  endif 
+
+  deg_to_rad = !DPI/180.0d
+  sec_to_rad = deg_to_rad/3600.d0
+
+   t = 0.001d0*( equinox2 - equinox1)
+
+ if ~keyword_set( FK4 )  then begin  
+           st = 0.001d0*( equinox1 - 2000.d0)
+;  Compute 3 rotation angles
+           A = sec_to_rad * T * (23062.181D0 + ST*(139.656D0 +0.0139D0*ST) $
+            + T*(30.188D0 - 0.344D0*ST+17.998D0*T))
+
+           B = sec_to_rad * T * T * (79.280D0 + 0.410D0*ST + 0.205D0*T) + A
+
+        C = sec_to_rad * T * (20043.109D0 - ST*(85.33D0 + 0.217D0*ST) $
+              + T*(-42.665D0 - 0.217D0*ST -41.833D0*T))
+
+ endif else begin  
+
+           st = 0.001d0*( equinox1 - 1900.d0)
+;  Compute 3 rotation angles
+
+           A = sec_to_rad * T * (23042.53D0 + ST*(139.75D0 +0.06D0*ST) $
+            + T*(30.23D0 - 0.27D0*ST+18.0D0*T))
+
+           B = sec_to_rad * T * T * (79.27D0 + 0.66D0*ST + 0.32D0*T) + A
+
+           C = sec_to_rad * T * (20046.85D0 - ST*(85.33D0 + 0.37D0*ST) $
+              + T*(-42.67D0 - 0.37D0*ST -41.8D0*T))
+
+ endelse  
+
+  sina = sin(a) &  sinb = sin(b)  & sinc = sin(c)
+  cosa = cos(a) &  cosb = cos(b)  & cosc = cos(c)
+
+  r = dblarr(3,3)
+  r[0,0] = [ cosa*cosb*cosc-sina*sinb, sina*cosb+cosa*sinb*cosc,  cosa*sinc]
+  r[0,1] = [-cosa*sinb-sina*cosb*cosc, cosa*cosb-sina*sinb*cosc, -sina*sinc]
+  r[0,2] = [-cosb*sinc, -sinb*sinc, cosc]
+
+  return,r
+  end
diff --git a/Code/script_idl_mv/astrolib/prime.pro b/Code/script_idl_mv/astrolib/prime.pro
new file mode 100644
index 0000000000000000000000000000000000000000..490916eb970d4e7137ec7b17c7a35d119369e3c3
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/prime.pro
@@ -0,0 +1,81 @@
+;-------------------------------------------------------------
+;+
+; NAME:
+;     PRIME
+; PURPOSE:
+;     Return an array with the specified number of prime numbers.
+; EXPLANATATION:
+;     This procedure is similar to PRIMES in the standard IDL distribution,
+;     but stores results in a common block, and so is much faster 
+;
+; CALLING SEQUENCE:
+;       p = prime(n)
+; INPUTS:
+;       n = desired number of primes, scalar positive integer
+; OUTPUTS:
+;       p = resulting array of primes, vector of positive integers
+; COMMON BLOCKS:
+;       prime_com
+; NOTES:
+;       Note: Primes that have been found in previous calls are
+;         remembered and are not regenerated.
+; MODIFICATION HISTORY:
+;       R. Sterner  17 Oct, 1985.
+;       R. Sterner,  5 Feb, 1993 --- fixed a bug that missed a few primes.
+;       Converted to IDL V5          March 1999
+;
+; Copyright (C) 1985, Johns Hopkins University/Applied Physics Laboratory
+; This software may be used, copied, or redistributed as long as it is not
+; sold and this copyright notice is reproduced on each copy made.  This
+; routine is provided as is without any express or implied warranties
+; whatsoever.  Other limitations apply as described in the file disclaimer.txt.
+;-
+;-------------------------------------------------------------
+ 
+	function prime,n, help=hlp
+ 
+	common prime_com, max, pmax
+ 
+	if (n_params(0) lt 1) or keyword_set(hlp) then begin
+	  print,' Return an array with the specified number of prime numbers.'
+	  print,' p = prime(n)'
+	  print,'   n = desired number of primes.    in'
+	  print,'   p = resulting array of primes.   out'
+	  print,' Note: Primes that have been found in previous calls are'
+	  print,'   remembered and are not regenerated.'
+	  return, -1
+	endif
+ 
+	if n_elements(max) eq 0 then max = 0	; Make MAX defined.
+	if n le max then return, pmax[0:n-1]	; Enough primes in memory.
+	p = lonarr(n)				; Need to find primes.
+	if max eq 0 then begin			; Have none now. Start with 8.
+	  p[0] = [2,3,5,7,11,13,17,19]
+	  if n le 8 then return, p[0:n-1]	; Need 8 or less.
+	  i = 8					; Need more than 8.
+	  t = 19L				; Search start value.
+	endif else begin			; Start with old primes.
+	  p[0] = pmax				; Move old primes into big arr.
+	  i = max				; Current prime count.
+	  t = p[max-1]				; Biggest prime so far.
+	endelse
+ 
+loop:	if i eq n then begin			; Have enough primes.
+	  max = n				; Count.
+	  pmax = p				; Array of primes.
+	  return, p				; Return primes.
+	endif
+loop2:	t = t + 2				; Next test value, t.
+	it = 1					; Start testing with 1st prime.
+loop3:	pr = p[it]				; Pick next test prime.
+	pr2 = pr*pr				; Square it.
+	if pr2 gt t then begin			; Selected prime > sqrt(t)?
+	  i = i + 1				; Yes, count
+	  p[i-1] = t				; and store new prime.
+	  goto, loop				; Go check if done.
+	endif
+	if pr2 eq t then goto, loop2		; Test number, t, was a square.
+	if (t mod pr) eq 0 then goto, loop2	; Curr prime divides t.
+	it = it + 1				; Check next prime.
+	goto, loop3
+	end
diff --git a/Code/script_idl_mv/astrolib/print_struct.pro b/Code/script_idl_mv/astrolib/print_struct.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9271a6e1464e33b4ccb0d2a4d9ac3db061b4c73d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/print_struct.pro
@@ -0,0 +1,245 @@
+;+
+; NAME:
+;       PRINT_STRUCT
+;
+; PURPOSE:
+;       Print the tag values of an array of structures in nice column format.
+; EXPLANATION:
+;       The tag names are displayed in a header line.
+;
+; CALLING SEQUENCE:
+;       print_struct, structure, Tags_to_print [ , title, string_matrix 
+;                FILE=, LUN_OUT=, TNUMS= , TRANGE= , FRANGE=, WHICH=
+;                FORM_FLOAT =, MAX_ELEMENTS
+; INPUTS:
+;       structure = array of structured variables
+;
+;       Tags_to_print = string array specifying the names of tags to print.
+;                       Default is to print all tags which are not arrays.
+; OPTIONAL INPUT KEYWORDS:
+;       FILE = string, optional file name to which output will then be written.
+;       LUN_OUT = Logical unit number for output to an open file,
+;               default is to print to standard output.
+;       TNUMS = tag numbers to print (alternative to specifying tag names).
+;       TRANGE = [beg,end] tag number range to print.
+;       FRANGE = same as TRANGE.
+;       WHICH = optional array of subscripts to select
+;               which structure elements to print.
+;       FORM_FLOAT = string array of three elements specifying
+;               floating point format, ex: FORM=['f','9','2'] means "(F9.2)",
+;               (default float format is G12.4).
+;       MAX_ELEMENTS = positive integer, print only tags that have less than
+;                       this number of elements (default is no screening).
+;       /NO_TITLE - If set, then the header line of tag names is not printed
+;       /STRINGS : instead of printing, return the array of strings in
+;               fourth argument of procedure: string_matrix.
+; OUTPUTS:
+;       title = optional string, list of tags printed/processed.
+;       string_matrix = optional output of string matrix of tag values,
+;                       instead of printing to terminal or file, if /STRINGS.
+; PROCEDURE:
+;       Check the types and lengths of fields to decide formats,
+;       then loop and form text string from requested fields, then print.
+; HISTORY:
+;       Written: Frank Varosi NASA/GSFC 1991.
+;       F.V.1993, fixed up the print formats.
+;       F.V.1994, added more keyword options.
+;       F.V.1997, added WHICH and MAX_ELEM keyword options.
+;       WBL 1997, Use UNIQ() rather than UNIQUE function
+;       Remove call to N_STRUCT()   W. Landsman  March 2004
+;       Avoid overflow with more than 10000 elements  W. Landsman Nov 2005
+;       Really remove call to N_STRUCT() W. Landsman July 2009
+;-
+
+pro print_struct, structure, Tags_to_print, title, string_matrix, TNUMS=tagi, $
+                        FRANGE=fran, TRANGE=tran, FILE=filout, LUN_OUT=Lun, $
+                        STRINGS=strings, FORM_FLOAT=formf, NO_TITLE=no_tit, $
+                        WHICH_TO_PRINT=which, MAX_ELEMENTS=max_elements
+                        
+        compile_opt idl2
+        if N_params() LT 1 then begin
+        print, $
+       'Syntax - PRINT_STRUCT, structure, Tags_to_print [ ,title, string_matrix' 
+        print,'        FILE=, LUN_OUT=, TNUMS= , TRANGE= , FRANGE=, WHICH= '
+        print,'        FORM_FLOAT =, MAX_ELEMENTS, /NO_TITLE'
+        return
+        end
+   
+
+        if size(structure,/TNAME) NE 'STRUCT' then begin
+                message,"ERROR - expecting a structure",/INFO
+                return
+         endif
+ ;Use size(/N_Elements) instead of N_elements() so it can work with assoc 
+ ;variables	 
+         Nstruct = size(structure,/N_elements)
+	 Ntag = N_tags(structure)
+
+        if Nstruct EQ 1 then structure = [structure]
+
+        tags = [tag_names( structure )]
+        Npr = N_elements( Tags_to_print )
+        if N_elements( tran ) EQ 2 then fran = tran
+
+        if N_elements( tagi ) GT 0 then begin
+
+                tagi = ( tagi > 0 ) < (Ntag-1)
+                tagi = tagi[ uniq( sort(tagi) ) ]
+
+         endif else if N_elements( fran ) EQ 2 then begin
+
+                fran = ( fran > 0 ) < (Ntag-1)
+                nf = abs( fran[1] - fran[0] )+1
+                tagi = indgen( nf ) + min( fran )
+
+          endif else if (Npr LE 0) then begin
+
+                for i=0,Ntag-1 do begin
+
+                    if  (N_elements( structure[0].(i) ) LE 1) AND $
+                        (N_tags( structure[0].(i) ) LE 0) then begin
+
+                          if N_elements( tagi ) LE 0 then tagi = [i] $
+                                                     else tagi = [ tagi, i ]
+                      endif
+                  endfor
+
+           endif else begin
+
+                ptags = [strupcase( Tags_to_print )]
+
+                for i=0,Npr-1 do begin
+
+                    w = where( tags EQ ptags[i], nf )
+
+                    if (nf GT 0) then begin
+
+                          if N_elements( tagi ) LE 0 then tagi = [w[0]] $
+                                                else tagi = [ tagi, w[0] ]
+
+                      endif else message,"Tag <"+ptags[i]+"> not found",/INFO
+                  endfor
+            endelse
+
+        if N_elements( tagi ) LE 0 then begin
+                message,"requested Tags are not in structure",/INFO
+                return
+           endif
+
+        if keyword_set( max_elements ) then begin
+
+                Ntag = N_elements( tagi )
+                Ntel = Lonarr( Ntag )
+                Ntst = intarr( Ntag )
+
+                for i=0,Ntag-1 do begin
+                        Ntel[i] = N_elements( structure[0].(tagi[i]) )
+                        Ntst[i] = N_tags( structure[0].(tagi[i]) )
+                  endfor
+
+                w = where( (Ntel LE max_elements) and (Ntst LE 0), nw )
+
+                if (nw GT 0) then  tagi = tagi[w]  else begin
+                        message,"requested Tags have too many elements",/INFO
+                        return
+                   endelse
+           endif
+
+        ndigit = ceil(alog10(Nstruct))      ;Number of digits in index
+        iform = "(I" + strtrim(ndigit,2) + ")"
+        if ndigit GT 1 then $
+             title = string(replicate(32b,ndigit-1)) else title=''
+        title = title + '#'
+
+        Tags_to_print = tags[tagi]
+        Npr = N_elements( tagi )
+        vtypes = intarr( Npr )
+        sLens = intarr( Npr )
+        formats = strarr( Npr )
+        ncht = strlen( Tags_to_print ) + 2
+        minch = [ 0, 5, 8, 12, 12, 12, 12, 0 ]
+
+        for i=0,Npr-1 do begin
+                st = size( structure[0].(tagi[i]) )
+                vtypes[i] = st[st[0]+1]
+                CASE vtypes[i] OF
+                1:      formats[i] = "I" + strtrim( ncht[i]>5, 2 ) + ")"
+                2:      formats[i] = "I" + strtrim( ncht[i]>8, 2 ) + ")"
+                3:      formats[i] = "I" + strtrim( ncht[i]>12, 2 ) + ")"
+                7: BEGIN
+                        sLens[i] = $
+                         ( max( strlen( structure.(tagi[i]) ) ) + 2 ) > ncht[i]
+                        formats[i] = "A" + strtrim( sLens[i], 2 ) + ")"
+                     END
+                else: BEGIN
+                        if N_elements( formf ) EQ 3 then begin
+                                formf = strtrim( formf, 2 )
+                                ndig = fix( formf[1] )
+                                minch[4] = ndig
+                                formats[i] = formf[0] + $
+                                        strtrim( ncht[i] > ndig, 2 ) + $
+                                        "." + formf[2] + ")"
+                         endif else $
+                           formats[i] = "G" + strtrim( ncht[i]>12, 2 ) + ".4)"
+                        END
+                ENDCASE
+                nelem = st[st[0]+2]
+                formats[i] = "(" + strtrim( nelem, 2 ) + formats[i]
+                minch[7] = sLens[i]
+                nb = nelem * ( ncht[i] > minch[vtypes[i]] ) - ncht[i] + 2
+                title = title + string( replicate( 32b,nb ) ) + Tags_to_print[i]
+          endfor
+
+        if N_elements( which ) GT 0 then begin
+                w = where( (which GE 0) AND (which LT Nstruct), nw )
+                if (nw LE 0) then begin
+                        message,"keyword WHICH subscripts out of range",/INFO
+                        return
+                   endif
+                which = which[w]
+                Nprint = nw
+         endif else begin
+                which = lindgen( Nstruct )
+                Nprint = Nstruct
+          endelse
+
+        pr_tit = keyword_set( no_tit ) EQ 0
+
+        if keyword_set( strings ) then begin
+                string_matrix = strarr( Npr, Nprint )
+                title = strmid( title, 3, 999 )
+          endif else begin
+                if keyword_set( filout ) then openw, Lun, filout,/GET_LUN
+                if (pr_tit) then begin
+                        if (Nstruct LE 3) then title = strmid( title, 3, 999 )
+                        if N_elements( Lun ) EQ 1 then printf,Lun,title $
+                                                else print,title
+                   endif
+           endelse
+
+        for n=0,Nprint-1 do begin
+
+            wp = which[n]
+
+            if keyword_set( strings ) then begin
+
+                for i=0,Npr-1 do string_matrix[i,n] = $
+                        string( structure[wp].(tagi[i]), FORM=formats[i] )
+
+             endif else begin
+
+                if (pr_tit) AND (Nstruct GT 3) then $
+                        text = string( wp,FORM=iform )  else text=""
+
+                for i=0,Npr-1 do text = text + $
+                        string( structure[wp].(tagi[i]), FORM=formats[i] )
+
+                if N_elements( Lun ) EQ 1 then printf,Lun,text else print,text
+              endelse
+          endfor
+
+        if keyword_set( filout ) then begin
+                free_Lun, Lun
+                message,"structure printed into file: " + filout,/INFO
+           endif
+end
diff --git a/Code/script_idl_mv/astrolib/prob_ks.pro b/Code/script_idl_mv/astrolib/prob_ks.pro
new file mode 100644
index 0000000000000000000000000000000000000000..43df32d77962a5f35fd294619084c50d79c2c197
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/prob_ks.pro
@@ -0,0 +1,70 @@
+pro prob_ks, D, N_eff, probks
+;+
+; NAME:
+;       PROB_KS
+; PURPOSE:
+;       Return the significance of the Kolmogoroff-Smirnov statistic
+; EXPLANATION:
+;       Returns the significance level of an observed value of the 
+;       Kolmogorov-Smirnov statistic D for an effective number of data points
+;       N_eff.   Called by KSONE and KSTWO
+;
+; CALLING SEQUENCE:
+;       prob_ks, D, N_eff, probks
+;
+; INPUT PARAMETERS:
+;       D -  Kolmogorov statistic, floating scalar, always non-negative
+;       N_eff - Effective number of data points, scalar.   For a 2 sided test 
+;               this is given by (N1*N2)/(N1+N2) where N1 and N2 are the number 
+;               of points in each data set.
+;
+; OUTPUT PARAMETERS:
+;       probks - floating scalar between 0 and 1 giving the significance level of
+;               the K-S statistic.   Small values of PROB suggest that the 
+;               distribution being tested are not the same
+;
+; REVISION HISTORY:
+;       Written     W. Landsman                August, 1992
+;       Corrected typo (termbv for termbf)    H. Ebeling/W.Landsman  March 1996
+;       Probably did not affect numeric result, but iteration went longer
+;       than necessary
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+
+ if N_params() LT 3 then begin
+     print,'Syntax - prob_ks, D, N_eff, prob'
+     print,'  D - Komolgorov-Smirnov statistic, input'
+     print,'  N_eff - effective number of data points, input'
+     print,'  prob - Significance level of D, output'
+     return
+ endif
+
+ eps1 = 0.001    ;Stop if current term less than EPS1 times previous term
+ eps2 = 1.e-8    ;Stop if current term changes output by factor less than EPS2
+
+ en = sqrt( N_eff )
+ lambda = (en + 0.12 + 0.11/en)*D
+
+ a2 = -2.*lambda^2
+ probks = 0.
+ termbf = 0.
+ sign = 1.
+
+ for j = 1,100 do begin 
+
+     term = sign*2*exp(a2*j^2)
+     probks = probks + term
+
+     if ( abs(term) LE eps1*termbf ) or $ 
+        ( abs(term) LE eps2*probks ) then return
+
+     sign = -sign                  ;Series alternates in sign
+     termbf = abs(term)
+
+ endfor
+
+ probks = 1.          ;Sum did not converge after 100 iterations
+ return
+
+ end
diff --git a/Code/script_idl_mv/astrolib/prob_kuiper.pro b/Code/script_idl_mv/astrolib/prob_kuiper.pro
new file mode 100644
index 0000000000000000000000000000000000000000..25c13f9f8241fb6c60536b6cf2a86ed8ffe4a171
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/prob_kuiper.pro
@@ -0,0 +1,76 @@
+pro prob_kuiper, D, N_eff, probks
+;+
+; NAME:
+;       PROB_KUIPER
+; PURPOSE:
+;       Return the significance of the Kuiper statistic
+; EXPLANATION:
+;       Returns the significance level of an observed value of the
+;       Kuiper statistic D for an effective number of data points
+;       N_eff.   Called by KUIPERONE
+;
+; CALLING SEQUENCE:
+;       prob_kuiper, D, N_eff, probks
+;
+; INPUT PARAMETERS:
+;       D -  Kuiper statistic, floating scalar, always non-negative
+;       N_eff - Effective number of data points, scalar.   For a 2 sided test
+;               this is given by (N1*N2)/(N1+N2) where N1 and N2 are the number
+;               of points in each data set.
+;
+; OUTPUT PARAMETERS:
+;       probks - floating scalar between 0 and 1 giving the significance level of
+;               the Kuiper statistic.   Small values of PROB suggest that the
+;               distribution being tested are not the same
+;
+; REVISION HISTORY:
+;       Written     W. Landsman                August, 1992
+;       Corrected typo (termbv for termbf)    H. Ebeling/W.Landsman  March 1996
+;       Probably did not affect numeric result, but iteration went longer
+;       than necessary
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Adapted from PROB_KS    J. Ballet     July 2003
+;-
+ On_error,2
+
+ if N_params() LT 3 then begin
+     print,'Syntax - prob_kuiper, D, N_eff, prob'
+     print,'  D - Kuiper statistic, input'
+     print,'  N_eff - effective number of data points, input'
+     print,'  prob - Significance level of D, output'
+     return
+ endif
+
+ eps1 = 0.001    ;Stop if current term less than EPS1 times previous term
+ eps2 = 1.e-8    ;Stop if current term changes output by factor less than EPS2
+
+ en = sqrt( N_eff )
+ lambda = (en + 0.155 + 0.24/en)*D
+
+; No iteration if lambda is smaller than 0.4
+ if lambda le 0.4 then begin
+ 	probks = 1.0
+ 	return
+ endif
+
+ a2 = -2.*lambda^2
+ probks = 0.
+ termbf = 0.
+
+ for j = 1,100 do begin
+
+     a2j2 = a2 * j^2
+     term = 2 * (-2*a2j2-1) * exp(a2j2)
+     probks = probks + term
+
+     if ( abs(term) LE eps1*termbf ) or $
+        ( abs(term) LE eps2*probks ) then return
+
+     termbf = abs(term)
+
+ endfor
+
+ probks = 1.          ;Sum did not converge after 100 iterations
+ return
+
+ end
diff --git a/Code/script_idl_mv/astrolib/psf_gaussian.pro b/Code/script_idl_mv/astrolib/psf_gaussian.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1c0b94df95969776c2c5f95f3aeccb3d08569733
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/psf_gaussian.pro
@@ -0,0 +1,190 @@
+function psf_gaussian, parameters, NPIXEL=npixel, NDIMENSION=ndim, FWHM=fwhm,  $
+                        DOUBLE = double, CENTROID=cntrd, ST_DEV=st_dev,  $
+                        XY_CORREL=xy_corr, NORMALIZE=normalize
+;+
+; NAME:
+;       PSF_GAUSSIAN
+;
+; PURPOSE:
+;       Create a 1-d, 2-d, or 3-d Gaussian with specified FWHM, center 
+; EXPLANATION:
+;       Return a point spread function having Gaussian profiles,
+;       as either a 1D vector, a 2D image, or 3D volumetric-data.
+;
+; CALLING SEQUENCE:
+;       psf = psf_Gaussian( NPIXEL=, FWHM= , CENTROID = 
+;                     [ /DOUBLE, /NORMALIZE, ST_DEV=,  NDIMEN= ] ) 
+; or:
+;       psf = psf_Gaussian( parameters, NPIXEL = ,NDIMEN = )
+;
+; REQUIRED INPUT KEYWORD:
+;       NPIXEL = number pixels for each dimension, specify as an array,
+;               or just one number to make all sizes equal.
+;
+; OPTIONAL KEYWORDS:
+;       CENTROID = floating scalar or vector giving position of  PSF center.    
+;               default is exact center of requested vector/image/volume.
+;               The number of elements in CENTROID should equal the number of
+;               dimensions.    **The definition of Centroid was changed in
+;               March 2002, and now an integer defines the center of a pixel.**
+;
+;       /DOUBLE  = If set, then the output array is computed in double precision
+;               the default is to return a floating point array.
+;
+;       FWHM = the desired Full-Width Half-Max (pixels) in each dimension,
+;               specify as an array, or single number to make all the same.
+;
+;       NDIMEN = integer dimension of result: either 1 (vector), 2 (image), or 
+;                3 (volume), default = 2 (an image result).
+;
+;       /NORMALIZE causes resulting PSF to be normalized so Total( psf ) = 1.
+;
+;       ST_DEV = optional way to specify width by standard deviation param.
+;                Ignored if FWHM is specified.
+;
+;       XY_CORREL = scalar between 0 and 1 specifying correlation coefficient
+;               Use this keyword, for example, to specify an elliptical 
+;               Gaussian oriented at an angle to the X,Y axis.   Only valid
+;               for 2-dimensional case.
+;
+;
+; INPUTS (optional):
+;
+;       parameters = an NDIMEN by 3 array giving for each dimension:
+;                       [ maxval, center, st_dev ],  overrides other keywords.
+;
+; EXAMPLE:
+;       (1) Create a 31 x 31 array containing a normalized centered Gaussian 
+;       with an X FWHM = 4.3 and a Y FWHM = 3.6
+;
+;       IDL> array = PSF_GAUSSIAN( Npixel=31, FWHM=[4.3,3.6], /NORMAL )
+;
+;       (2) Create a 50 pixel 1-d Gaussian vector with a maximum of 12, 
+;          centered at  pixel 23 with a sigma of 19.2
+;
+;       IDL> psf = psf_gaussian([12,23,19.2],npixel=50)
+; EXTERNAL CALLS:
+;       function Gaussian()
+; NOTES:
+;       To improve speed, floating underflow exceptions are suppressed (using 
+;       the MASK=32  keyword of CHECK_MATH() rather than being flagged.
+;
+; HISTORY:
+;       Written, Frank Varosi NASA/GSFC 1991.
+;       Suppress underflow messages, add DOUBLE keyword. **Modified centroid
+;       definition so integer position is pixel center** W. Landsman March 2002
+;       Allow use of the ST_DEV (not STDEV) keyword W. Landsman Nov. 2002
+;       Do not modify NPIXEL input keyword   W. Landsman  
+;-
+        On_error,2
+	compile_opt idl2
+
+        if (N_params() LT 1 ) and $
+            ~(keyword_set( FWHM) || keyword_set(ST_DEV)) then begin
+                print,'Syntax - psf = PSF_GAUSSIAN( parameters, NPIXEL = )'
+                print, $
+       'or       psf = PSF_GAUSSIAN( FWHM = ,ST_DEV = ,NPIXEL = ,[CENTROID = ])'
+                return, -1
+        endif
+
+        sp = size( parameters )
+        if sp[0] EQ 1 then begin               ;Vector supplied?
+                ndim = 1
+                factor = parameters[0]
+                cntrd = parameters[1]
+                st_dev = parameters[2] 
+         endif  else  if (sp[0] GE 1) then begin    ;Ndimen x 3 array supplied?
+                 ndim = sp[1]
+                 factor = total( parameters[*,0] )/float( ndim )
+                cntrd = parameters[*,1]
+                st_dev = parameters[*,2]
+           endif
+
+        double = keyword_set(double)
+        if double then idltype = 5 else idltype = 4
+        if N_elements( ndim ) NE 1 then ndim=2
+        ndim = ndim>1
+
+        if N_elements( npixel ) LE 0 then begin
+                message,"must specify size of result with NPIX=",/INFO
+                return,(-1)
+          endif else begin 
+	      npix = npixel
+	      if N_elements( npix ) LT ndim then npix = replicate( npix[0], ndim )
+         endelse
+
+        if (N_elements( cntrd ) LT ndim) && (N_elements( cntrd ) GT 0) then $
+                        cntrd = replicate( cntrd[0], ndim )
+
+        if N_elements( cntrd ) LE 0 then cntrd=(npix-1)/2. 
+        if N_elements( fwhm ) GT 0 then begin 
+               st_dev = fwhm/( 2.0d* sqrt( 2.0d* aLog(2.0d) ) )
+               if ~double then st_dev  = float(st_dev)
+        endif 
+
+        if N_elements( st_dev ) LE 0 then begin
+                message,"must specify ST_DEV= or FWHM=",/INFO
+                return,(-1)
+          endif
+
+        if N_elements( st_dev ) LT ndim then $
+                        st_dev = replicate( st_dev[0], ndim )
+
+        CASE ndim OF
+
+        1: BEGIN
+                x = findgen( npix[0] ) - cntrd[0]
+                psf = gaussian( x, [1,0,st_dev] )
+             END
+
+        2: BEGIN
+                psf = make_array( DIM=npix[0:ndim-1], TYPE = idltype )
+                x = make_array( npix[0], /INDEX, TYPE=idltype ) - cntrd[0]
+                y = make_array( npix[1], /INDEX, TYPE=idltype ) - cntrd[1]
+
+                if N_elements( xy_corr ) EQ 1 then begin
+                        sigfac = 1 / (2. * st_dev^2 )
+                        y2 = sigfac[1] * y^2
+                        x1 = sigfac[0] * x
+                        yc = y * ( xy_corr/(st_dev[0]*st_dev[1]) )
+                        for j=0,npix[1]-1 do begin
+                                zz = x * (yc[j] + x1) + y2[j]
+                                w = where( zz LT 86, nw )
+                                if (nw GT 0) then psf[w,j] = exp( -zz[w] )
+                          endfor
+                  endif else begin
+                        psfx = gaussian( x, [ 1, 0, st_dev[0] ], DOUBLE=double )
+                        psfy = gaussian( y, [ 1, 0, st_dev[1] ], DOUBLE=double )
+                        error = check_math(/print, MASK=32)
+                        save_except = !EXCEPT & !EXCEPT = 0
+                        for j=0,npix[1]-1 do psf[0,j] = psfx * psfy[j]
+                        error = check_math(MASK=32)    ;Clear floating underflow
+                        !EXCEPT = save_except  
+                   endelse
+             END
+
+        3: BEGIN
+                psf = make_array( DIM=npix[0:ndim-1], TYPE = idltype )
+                x = make_array( npix[0], /INDEX, TYPE=idltype ) - cntrd[0]
+                y = make_array( npix[1], /INDEX, TYPE=idltype ) - cntrd[1]
+                z = make_array( npix[2], /INDEX, TYPE=idltype ) - cntrd[2]
+                psfx = gaussian( x, [ 1, 0, st_dev[0] ], DOUBLE = double )
+                psfy = gaussian( y, [ 1, 0, st_dev[1] ], DOUBLE = double)
+                psfz = gaussian( z, [ 1, 0, st_dev[2] ], DOUBLE = double )
+                error = check_math(MASK=32,/PRINT)
+                save_except = !EXCEPT & !EXCEPT = 0
+                for k=0,npix[2]-1 do begin
+                    for j=0,npix[1]-1 do psf[0,j,k] = psfx * psfy[j] * psfz[k]
+                 endfor
+                 error = check_math(MASK=32)
+                 !EXCEPT = save_except  
+             END
+
+        ENDCASE
+
+        if keyword_set( normalize ) then return, psf/total( psf )
+
+        if N_elements( factor ) EQ 1 then begin
+                if (factor NE 1) then return,factor*psf else return,psf
+           endif else return, psf
+end
diff --git a/Code/script_idl_mv/astrolib/putast.pro b/Code/script_idl_mv/astrolib/putast.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c7231c27be1b684b35defbde4afb0e7c35f38af5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/putast.pro
@@ -0,0 +1,484 @@
+pro putast, hdr, astr, crpix, crval, ctype, EQUINOX=equinox, $
+                  CD_TYPE = cd_type, ALT = alt, NAXIS = naxis
+;+
+; NAME:
+;    PUTAST
+; PURPOSE:
+;    Put WCS astrometry parameters into a given FITS header.
+;
+; CALLING SEQUENCE:
+;     putast, hdr              ;Prompt for all values
+;               or
+;     putast, hdr, astr, [EQUINOX =, CD_TYPE =, ALT= , NAXIS=]
+;               or
+;     putast, hdr, cd,[ crpix, crval, ctype], [ EQUINOX =, CD_TYPE =, ALT= ]    
+;
+; INPUTS:
+;     HDR -  FITS header, string array.   HDR will be updated to contain
+;             the supplied astrometry.
+;     ASTR - IDL structure containing values of the astrometry parameters
+;            CDELT, CRPIX, CRVAL, CTYPE, LONGPOLE, and PV2
+;            See EXTAST.PRO for more info about the structure definition
+;                            or
+;     CD   - 2 x 2 array containing the astrometry parameters CD1_1 CD1_2
+;                                                             CD2_1 CD2_2
+;              in units of DEGREES/PIXEL
+;     CRPIX - 2 element vector giving X and Y coord of reference pixel
+;              BE SURE THE COORDINATES IN CRPIX ARE GIVEN IN FITS STANDARD
+;              (e.g. first pixel in image is [1,1] ) AND NOT IDL STANDARD
+;              (first pixel in image is [0,0]
+;     CRVAL - 2 element vector giving R.A. and DEC of reference pixel 
+;               in degrees
+;     CTYPE - 2 element string vector giving projection types for the two axes.
+;             For example, to specify a tangent projection one should set
+;             ctype = ['RA---TAN','DEC--TAN'] 
+;
+;     Fields added for version 2:
+;      .PV1 - Vector of projection parameters associated with longitude axis
+;      .AXES  - 2 element integer vector giving the FITS-convention axis 
+;               numbers associated with astrometry, in ascending order. 
+;               Default [1,2].
+;      .REVERSE - byte, true if first astrometry axis is Dec/latitude
+;      .COORDSYS - 1 or 2 character code giving coordinate system, including
+;                 'C' = RA/Dec, 'G' = Galactic, 'E' = Ecliptic, 'X' = unknown.
+;      .RADECSYS - String giving RA/Dec system e.g. 'FK4', 'ICRS' etc.
+;      .EQUINOX  - Double giving the epoch of the mean equator and equinox
+;      .DATEOBS  - Text string giving (start) date/time of observations
+;      .MJDOBS   - Modified julian date of start of observations.
+;      .X0Y0     - Not written to header.
+;
+;
+; OUTPUTS:
+;      HDR - FITS header now contains the updated astrometry parameters
+;               A brief HISTORY record is also added.
+;
+; OPTIONAL KEYWORD INPUTS:
+;       ALT -  single character 'A' through 'Z' or ' ' specifying an alternate 
+;              astrometry system to write in the FITS header.    The default is
+;              to write primary astrometry or ALT = ' '.   If /ALT is set, 
+;              then this is equivalent to ALT = 'A'.   See Section 3.3 of 
+;              Greisen & Calabretta (2002, A&A, 395, 1061) for information about
+;              alternate astrometry keywords.
+;
+;
+;       CD_TYPE - Integer scalar, either 0, 1 or 2 specifying how the CD matrix
+;                is to be written into the header
+;               (0) write PCn_m values along with CDELT values
+;               (1) convert to rotation and write as a CROTA2 value (+ CDELT)
+;               (2) as CDn_m values (IRAF standard) 
+;
+;            All three forms are valid representations according to Greisen &
+;            Calabretta (2002, A&A, 395, 1061), also available at 
+;            http://fits.gsfc.nasa.gov/fits_wcs.html ) although form (0) is 
+;            preferred.   Form (1) is the former AIPS standard and is now  
+;            deprecated and cannot be used if any skew is present.
+;            If CD_TYPE is not supplied, PUTAST will try to determine the 
+;            type of astrometry already in the header.   If there is no 
+;            astrometry in the header then the default is CD_TYPE = 2.
+;
+;      EQUINOX - numeric scalar giving the year of equinox  of the reference 
+;                coordinates.  Keyword value takes precedence over value in
+;                astrometry structure which takes precedence over value in 
+;                header; if none of these present then default is 2000.
+;
+;      NAXIS - By default, PUTAST does not update the NAXIS keywords in the
+;            FITS header.    If NAXIS is set, and an astrometry structure is
+;            supplied then the NAXIS1 and NAXIS2 keywords in the FITS header 
+;            will be updated with the .NAXIS structure tags values.     If an 
+;            astrometry structure is not supplied, then one can set NAXIS to a 
+;            two element vector to update the NAXIS1, NAXIS2 keywords. 
+; NOTES:
+;       The recommended use of this procedure is to supply an astrometry
+;       structure. This can be produced with MAKE_ASTR.    
+;
+;       If parameters are supplied by keyword, the full range of
+;       astrometry header info is not supported by PUTAST.
+;
+;       PUTAST does not delete astrometry parameters already present in the 
+;       header, unless they are explicity overwritten.   
+;
+;       If present in the astrometry structure, PUTAST will add SIP 
+;      ( http://fits.gsfc.nasa.gov/registry/sip.html ) or TPV 
+;       ( http://fits.gsfc.nasa.gov/registry/tpvwcs.html ) distortion parameters
+;       to a FITS header.  
+; PROMPTS:
+;       If only a header is supplied, the user will be prompted for a plate 
+;       scale, the X and Y coordinates of a reference pixel, the RA and
+;       DEC of the reference pixel, the equinox of the RA and Dec and a 
+;       rotation angle.
+;
+; PROCEDURES USED:
+;       ADD_DISTORT, GETOPT(), GET_COORDS, GET_EQUINOX(), SXADDPAR, SXPAR(), 
+;       TAG_EXIST(), ZPARCHECK
+; REVISION HISTORY:
+;       Written by W. Landsman 9-3-87
+;       Major rewrite, use new astrometry structure   March, 1994
+;       Use both CD and CDELT to get plate scale for CD_TYPE=1   September 1995
+;       Use lower case for FITS keyword Comments  W.L.    March 1997
+;       Fixed for CD_TYPE=1 and CDELT = [1.0,1.0]   W.L   September 1997
+;       Default value of CD_TYPE is now 2, Use GET_COORDS to read coordinates
+;       to correct -0 problem           W.L.  September 1997
+;       Update CROTA1 if it already exists  W.L. October 1997
+;       Convert rotation to degrees for CD_TYPE = 1  W. L.   June 1998
+;       Accept CD_TYPE = 0 keyword input   W.L   October 1998
+;       Remove reference to obsolete !ERR  W.L.  February 2000
+;       No longer support CD001001 format, write default tangent CTYPE value
+;       consistent conversion between CROTA and CD matrix W.L. October 2000
+;       Use GET_EQUINOX to get equinox value  W.L.  January 2001
+;       Update CTYPE keyword if previous value is 'LINEAR'  W.L. July 2001
+;       Use SIZE(/TNAME) instead of DATATYPE()  W.L.   November 2001
+;       Allow direct specification of CTYPE W.L.        June 2002
+;       Don't assume celestial coordinates W. Landsman  April 2003
+;       Make default CD_TYPE = 2  W. Landsman   September 2003
+;       Add projection parameters, e.g. PV2_1, PV2_2 if present in the 
+;       input structure   W. Landsman    May 2004
+;       Correct interactive computation of image center W. Landsman Feb. 2005
+;       Don't use CROTA (CD_TYPE=1) if a skew exists W. Landsman  May 2005
+;       Added NAXIS keyword  W. Landsman   January 2007
+;       Update PC matrix, if CD_TYPE=0 and CD matrix supplied W.L. July 2007
+;       Don't write PV2 keywords for WCS types that don't use it W.L. Aug 2011
+;       Add SIP distortion parameters if present W.L. April 2012
+;       Work if empty distortion structure present  W.L. November 2012
+;       Spurious error message introduced April 2012 if CD matrix rather
+;         than structure supplied  W.L.  January 2013 
+;       Allow for version 2 astrometry structure J. P. Leahy July 2013
+;       Bug fix in interactive use JPL Aug 2013.
+;       Support IRAF TNX projection  M. Sullivan U. of Southamptom March 2014
+;       PV1_3, PV1_4 keywords take precedence over LONPOLE, LATPOLE keywords
+;                   WL, August 2014
+;-
+
+ compile_opt idl2
+ npar = N_params()
+
+ if ( npar EQ 0 ) then begin    ;Was header supplied?
+        print,'Syntax: PUTAST, Hdr, astr, [ EQUINOX= , CD_TYPE=, ALT= ,/NAXIS]'
+        print,'       or'
+        print,'Syntax: PUTAST, Hdr, [ cd, crpix, crval, EQUINOX = , CD_TYPE =]'   
+        return
+ endif
+ 
+ RADEG = 180.0d/!DPI
+ ax = ['1','2'] ; Default axis numbers
+ astr2 = 0B     ; Assume input astronomy structure (if any) is version 1.
+                ; will be updated if not.
+ 
+ zparcheck, 'PUTAST', hdr, 1, 7, 1, 'FITS image header'
+ if N_elements(alt) EQ 0 then alt = '' else if (alt EQ '1') then alt = 'a'
+
+ if ( npar EQ 1 ) then begin            ;Prompt for astrometry parameters?
+   ctype = strtrim(sxpar(hdr,'CTYPE*', Count = N_Ctype),2)
+   if (N_Ctype NE 2) || (ctype[0] EQ 'PIXEL') || (ctype[0] EQ 'LINEAR') then $
+                ctype = ['RA---TAN','DEC--TAN']
+   read,'Enter plate scale in arc seconds/pixel: ',cdelt
+   inp =''
+   print,'Reference pixel position should be in FORTRAN convention'
+   print,'(First pixel has coordinate (1,1) )'
+
+GETCRPIX: print, $
+  'Enter X and Y position of a reference pixel ([RETURN] for plate center)'
+   read, inp
+   if ( inp EQ '' ) then $ 
+          crpix = [ sxpar(hdr,'NAXIS1')+1, sxpar(hdr,'NAXIS2')+1] / 2. $
+     else crpix = getopt( inp, 'F')
+
+   if N_elements( crpix ) NE 2 then begin
+      print,'PUTAST: INVALID INPUT - Enter 2 scalar values'
+      goto, GETCRPIX     
+   endif
+
+RD_CEN:
+   inp = ''
+   read,'Enter RA (hrs) and Dec (degrees) of reference pixel:',inp
+   GET_COORDS, crval,in=inp
+   if crval[0] EQ -999 then goto, rd_cen
+
+   crval[0] = crval[0]*15.
+ 
+   inp = ''
+   read,'Enter rotation angle in degrees, East of north [0.]: ',inp
+   rotat = getopt(inp,'F')/RADEG
+   cd = (cdelt / 3600.)*[[-cos(rotat),-sin(rotat)], [-sin(rotat), cos(rotat)]]
+   npar = 4
+ endif else begin
+
+   if size(astr,/TNAME) EQ 'STRUCT' then begin      
+                                             ;User supplied astrometry structure
+        cd = astr.cd
+        cdelt = astr.cdelt
+        crval = astr.crval
+        crpix = astr.crpix
+        ctype = astr.ctype
+        if keyword_set(naxis)  then if tag_exist(astr,'NAXIS') then $
+            naxis = astr.naxis
+        longpole = astr.longpole
+        if tag_exist(astr,'latpole') then latpole = astr.latpole
+        if tag_exist(astr,'pv2') then pv2 = astr.pv2
+        astr2 = TAG_EXIST(astr,'AXES')
+        IF astr2 THEN BEGIN ; version 2 astrometry structure
+           ax = STRTRIM(STRING(astr.axes),2)
+           IF N_ELEMENTS(equinox) EQ 0 THEN equinox = astr.equinox
+        ENDIF
+   endif else  begin
+        cd = astr
+        zparcheck,'PUTAST', cd, 2, [4,5], 2, 'CD matrix'
+   endelse
+ endelse
+ 
+
+ ;Write NAXIS values
+   if N_elements(naxis) EQ 2 then begin 
+	      sxaddpar,hdr,'NAXIS'+ax[0],naxis[0],/SaveC
+	      sxaddpar,hdr,'NAXIS'+ax[1],naxis[1],/SaveC
+   endif      
+ 
+;   Add CTYPE to FITS header
+
+ if N_elements( ctype ) GE 2 then begin
+
+ sxaddpar,hdr,'CTYPE'+ax[0]+alt,ctype[0],' Coordinate Type','HISTORY',/SaveC
+ sxaddpar,hdr,'CTYPE'+ax[1]+alt,ctype[1],' Coordinate Type','HISTORY',/SaveC
+
+ endif
+
+;   Add EQUINOX keyword and value to FITS header
+
+ if N_elements( equinox ) EQ 0 then begin        ;Is EQUINOX already in header?
+    equinox = get_equinox( hdr, code)   
+    if  code LT 0 then $
+       sxaddpar, hdr, 'EQUINOX'+alt, 2000.0, ' Equinox of Ref. Coord.',  $
+                      'HISTORY',/SaveC
+ 
+ endif else $
+     sxaddpar,hdr, 'EQUINOX'+alt, equinox, 'Equinox of Ref. Coord.', 'HISTORY',/Sav
+
+; Add coordinate description (CD) matrix to FITS header
+; 0. PCn_m keywords  1. CROTA + CDELT     2: CD1_1 
+  
+ 
+ if (N_elements(cd_type) EQ 0) then begin
+ cd_type = 2
+ pc1_1 = sxpar( hdr, 'PC'+ax[0]+'_'+ax[0]+alt, Count = N_PC)
+      if N_pc EQ 0 then begin 
+      cd1_1 = sxpar( hdr, 'CD'+ax[0]+'_'+ax[0]+alt, Count = N_CD)
+      if N_CD EQ 0 then begin               ; 
+             CDELT1 = sxpar( hdr,'CDELT'+ax[0]+alt, COUNT = N_CDELT1)
+             if N_CDELT1 GE 1 then cd_type = 1
+      endif       
+     endif else cd_type = 0
+ endif
+
+; If there is a skew then we can't use a simple CROTA representation
+
+  if CD_TYPE EQ 1 then if abs(cd[1,0]) NE abs(cd[0,1]) then begin
+         cd_type = 0
+	 sxdelpar,hdr,['CROTA'+ax[0] + alt,'CROTA'+ax[1] + alt]
+        message,/INF,'Astrometry incompatible with a CROTA2 representation'
+        message,/INF,'Writing PC matrix instead'
+  endif	 
+
+
+  degpix  = ' Degrees / Pixel'
+  
+  if cd_type EQ 0 then begin
+
+
+    sxaddpar, hdr, 'PC'+ax[0]+'_'+ax[0]+alt, cd[0,0], degpix, 'HISTORY',/SaveC
+    sxaddpar, hdr, 'PC'+ax[1]+'_'+ax[0]+alt, cd[1,0], degpix, 'HISTORY',/SaveC
+    sxaddpar, hdr, 'PC'+ax[0]+'_'+ax[1]+alt, cd[0,1], degpix, 'HISTORY',/SaveC
+    sxaddpar, hdr, 'PC'+ax[1]+'_'+ax[1]+alt, cd[1,1], degpix, 'HISTORY',/SaveC
+
+    if N_elements(cdelt) EQ 2 then begin 
+    sxaddpar, hdr, 'CDELT'+ax[0]+alt, cdelt[0], degpix, 'HISTORY',/SaveC
+    sxaddpar, hdr, 'CDELT'+ax[1]+alt, cdelt[1], degpix, 'HISTORY',/SaveC
+    endif
+
+  endif else if cd_type EQ 2 then begin
+
+    if N_elements(CDELT) GE 2 then if (cdelt[0] NE 1.0) then begin
+            cd[0,0] = cd[0,0]*cdelt[0] & cd[0,1] =  cd[0,1]*cdelt[0]
+            cd[1,1] = cd[1,1]*cdelt[1] & cd[1,0] =  cd[1,0]*cdelt[1]
+    endif
+
+
+    sxaddpar, hdr, 'CD'+ax[0]+'_'+ax[0]+alt, cd[0,0], degpix, 'HISTORY',/SaveC
+    sxaddpar, hdr, 'CD'+ax[1]+'_'+ax[0]+alt, cd[1,0], degpix, 'HISTORY',/SaveC
+    sxaddpar, hdr, 'CD'+ax[0]+'_'+ax[1]+alt, cd[0,1], degpix, 'HISTORY',/SaveC
+    sxaddpar, hdr, 'CD'+ax[1]+'_'+ax[1]+alt, cd[1,1], degpix, 'HISTORY',/SaveC
+
+ endif else begin
+
+  ; Programs should only look for CROTA2, but we also update CROTA1 if it 
+  ; already exists.   Also keep existing comment field if it exists.
+
+         if N_elements(CDELT) GE 2 then begin
+                if cdelt[0] NE 1.0 then delt = cdelt
+        endif 
+
+        if N_elements(delt) EQ 0 then begin
+                        det = cd[0,0]*cd[1,1] - cd[0,1]*cd[1,0]
+                        if det LT 0 then sgn = -1 else sgn = 1
+                        delt = [sgn*sqrt(cd[0,0]^2 + cd[0,1]^2), $
+                                     sqrt(cd[1,0]^2 + cd[1,1]^2) ]
+        endif 
+        sxaddpar, hdr, 'CDELT'+ax[0]+alt, delt[0],degpix, 'HISTORY',/SaveC
+        sxaddpar, hdr, 'CDELT'+ax[1]+alt, delt[1],degpix, 'HISTORY',/SaveC
+
+        if (cd[1,0] eq 0) and (cd[0,1] eq 0) then rot = 0.0 else $ 
+        rot = float(atan( -cd[1,0],cd[1,1])*RADEG) 
+
+        crota2 = sxpar(hdr,'CROTA'+ax[1], Count = N_crota2)
+        if N_crota2 GT 0 then sxaddpar, hdr, 'CROTA2'+alt, rot else $
+             sxaddpar, hdr, 'CROTA'+ax[1]+alt, rot, ' Rotation Angle (Degrees)'
+        crota1 = sxpar(hdr,'CROTA'+ax[0], Count = N_crota1)
+        if N_crota1 GT 0 then $
+             sxaddpar, hdr, 'CROTA'+ax[0]+alt, rot       
+ 
+
+ endelse
+
+ hist = ' CD Matrix Written'
+
+; Add CRPIX keyword to FITS header
+
+ if N_elements( crpix ) GE 2  then begin                ;Add CRPIX vector?
+
+        zparcheck, 'PUTAST', crpix, 3, [1,2,4,3,5], 1, 'CRPIX vector'
+
+        sxaddpar, hdr, 'CRPIX'+ax[0]+alt, crpix[0], ' Reference Pixel in X', $
+                        'HISTORY', /SaveC
+        sxaddpar, hdr, 'CRPIX'+ax[1]+alt ,crpix[1], ' Reference Pixel in Y', $
+                        'HISTORY', /SaveC
+
+        hist = ' CD and CRPIX parameters written'
+ endif
+
+;  Add CRVAL keyword and values to FITS header.   Convert CRVAL to double
+;  precision to ensure enough significant figures
+
+ if N_elements( crval ) GE 2 then begin
+     comm = STRARR(2)
+     astrcode = astr2 ? astr.coord_sys : STRMID(ctype[0],0,1)
+     IF ~astr2 && STRMID(ctype[0],0,4) EQ 'RA--' THEN astrcode = 'C'
+     CASE astrcode OF
+        'C': BEGIN
+              coord = 'Celestial'
+              comm[0] = ' R.A. (degrees) of reference pixel'
+              comm[1] = ' Declination of reference pixel'
+        END
+        'G': coord = 'Galactic'               
+        'E': coord = 'Ecliptic'
+        'S': coord = 'Supergalactic'               
+        'H': coord = 'Helioecliptic'
+        'T': coord = 'Terrestrial'
+        'X': coord = ''  ; unknown system
+        ELSE: coord = astrcode
+     ENDCASE
+     IF astrcode NE 'C' THEN $
+         comm = ' '+coord+[' longitude',' latitude']+' of reference pixel'
+     IF astr2 && astr.reverse THEN comm = REVERSE(comm)
+     zparcheck, 'PUTAST', crval, 3, [2,4,3,5], 1, 'CRVAL vector'
+     sxaddpar, hdr, 'CRVAL'+ax[0]+alt, double(crval[0]), comm[0], 'HISTORY'
+     sxaddpar, hdr, 'CRVAL'+ax[1]+alt, double(crval[1]), comm[1], 'HISTORY'
+     hist = ' World Coordinate System parameters written'
+  endif
+
+; 
+    if N_elements(longpole) EQ 1 then begin
+        astr.pv1[3] = longpole
+        test = sxpar(hdr,'LONPOLE',count=N_lonpole)
+        if N_lonpole EQ 1 then $
+            sxaddpar, hdr, 'LONPOLE' +alt ,double(longpole), $
+	 ' Native longitude of ' +coord + ' pole', 'HISTORY', /SaveC
+    endif 
+ 
+    if N_elements(latpole) EQ 1 then begin
+       astr.pv1[4] = latpole
+       test = sxpar(hdr,'LATPOLE',count=N_latpole)
+        if N_latpole EQ 1 then $
+         sxaddpar, hdr, 'LATPOLE' +alt ,double(latpole), $
+          ' Native latitude of ' +coord + ' pole', 'HISTORY', /SaveC
+    endif	 
+
+    Npv2 = N_elements(pv2)
+    if Npv2 GT 0 then begin
+         ctyp = strmid(ctype[0],5,3)
+; List of WCS types for which no PV2 values should be written	 
+         no_pv2 = ['TPV','TNX','TAN','ARC','STG','CAR','MER','SFL','PAR','MOL','AIT', $
+	           'PC0','TSC','CSC','QSC' ]
+	 if total(no_pv2 EQ ctyp,/int) EQ 0 then begin
+	       pv2str = 'PV2_' 	   
+	       IF astr2 THEN $
+	          pv2str = 'PV'+(astr.reverse ? ax[0] : ax[1])+'_' ; Latitude axis PV 
+         case ctyp of 
+        'ZPN': for i=0,npv2-1 do sxaddpar,hdr, pv2str + strtrim(i,2) + alt, $
+             pv2[i],' Projection parameter ' + strtrim(i,2),'HISTORY',/SaveC 
+          else: for i=0,npv2-1 do sxaddpar,hdr, pv2str + strtrim(i+1,2) + alt,$
+             pv2[i],' Projection parameter ' + strtrim(i+1,2),'HISTORY',/SaveC 
+          endcase
+	 endif
+    endif
+     
+    IF astr2 THEN BEGIN
+       ctyp = strmid(ctype[0],5,3)
+; List of WCS types for which no PV1 values should be written	 
+       no_pv1 = ['TPV','TNX','TAN']
+       if total(no_pv1 EQ ctyp,/int) EQ 0 then begin
+          pv1str = 'PV'+(astr.reverse ? ax[1] : ax[0])+'_' ; Longitude axis PV 
+          FOR i=0,4 DO SXADDPAR, hdr, pv1str + STRTRIM(i,2)+alt, $
+            astr.pv1[i], ' Projection parameters', 'HISTORY', /SaveC
+       ENDIF
+        IF FINITE(astr.mjdobs) THEN SXADDPAR, hdr, 'MJD-OBS', astr.mjdobs, $
+          ' Modified Julian day of observations', 'HISTORY', /SaveC
+        IF astr.dateobs NE 'UNKNOWN' THEN SXADDPAR, hdr, 'DATE-OBS', $
+          astr.dateobs, ' Date of observations', 'HISTORY', /SaveC
+        IF astr.radecsys NE '' THEN SXADDPAR, hdr, 'RADESYS'+alt, $
+          astr.radecsys,' Reference frame', 'HISTORY', /SaveC
+    ENDIF
+    
+;Add SIP distortion parameters if present  
+
+    if size(astr,/tname) EQ 'STRUCT' && tag_exist(astr,'DISTORT') then begin
+       if astr.distort.name EQ 'SIP' then begin 
+; First remove any SIP parameters in the FITS header.
+          nord = sxpar(hdr, 'A_Order',Count = N)
+          if (N GT 0) && (nord GT 0) then begin 
+             key = ''
+             for i=0,nord do begin
+                for j=0,nord-i do begin
+                   if i+j NE 0 then $
+                     key = [key, strtrim(i,2) + '_' + strtrim(j,2)]
+                endfor
+             endfor
+             key = key[1:*]
+             oldkey = ['A_' + key, 'B_' + key, 'AP_' + key,'BP_'+key]
+             sxdelpar,oldkey, hdr
+          endif
+          add_distort, hdr, astr
+       ENDIF ELSE IF astr.distort.name EQ 'TNX' then BEGIN
+
+          ;; remove any existing WAT keywords
+          w=WHERE(STREGEX(hdr,'^WAT2_',/BOOLEAN,/FOLD),count,COMPLEMENT=w1)
+          IF(count GT 0)THEN hdr=hdr[w1]
+          w=WHERE(STREGEX(hdr,'^WAT1_',/BOOLEAN,/FOLD),count,COMPLEMENT=w1)
+          IF(count GT 0)THEN hdr=hdr[w1]
+          w=WHERE(STREGEX(hdr,'^WAT0_',/BOOLEAN,/FOLD),count,COMPLEMENT=w1)
+          IF(count GT 0)THEN hdr=hdr[w1]
+          ;; and add in the new ones
+          add_distort, hdr, astr
+       ENDIF ELSE IF astr.distort.name EQ 'TPV' then BEGIN
+
+          FOR i=0,N_ELEMENTS(astr.pv1)-1 DO BEGIN
+             SXADDPAR, hdr, 'PV1_'+STRTRIM(i,2)+alt, astr.pv1[i]
+          ENDFOR
+          FOR i=0,N_ELEMENTS(astr.pv2)-1 DO BEGIN
+             SXADDPAR, hdr, 'PV2_'+STRTRIM(i,2)+alt, astr.pv2[i]
+          ENDFOR
+          
+       ENDIF
+    endif
+    
+    sxaddhist,'PUTAST: ' + strmid(systime(),4,20) + hist,hdr
+    
+    return
+ end
diff --git a/Code/script_idl_mv/astrolib/qdcb_grid.pro b/Code/script_idl_mv/astrolib/qdcb_grid.pro
new file mode 100644
index 0000000000000000000000000000000000000000..432d76776d9b6b6b2f6aff033cb33e62d6887e86
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/qdcb_grid.pro
@@ -0,0 +1,162 @@
+;+
+; NAME:
+;	QDCB_GRID
+;
+; PURPOSE:
+;	Produce an overlay of latitude and longitude lines over a plot or image
+; EXPLANATION:
+;	Grid is plotted on the current graphics device assuming that the 
+;	current plot is a map  in the so called quad cube projection. The 
+;	output plot range is assumed to go from 7.0 to -1.0 on the X axis and 
+;	-3.0 to 3.0 on the Y axis. Within this plotting space, the quad cube 
+;	faces are laid out as follows (X=Empty, Astronomical Layout shown - 
+;	X axis can be swapped for geographic maps):
+;
+;	    3.0_
+;		XXX0
+;		4321
+;	   -3.0_XXX5
+;		|  |
+;	      7.0  -1.0
+;
+; CATEGORY:
+;	Mapping Support Routine
+;
+; CALLING SEQUENCE:
+;
+;	QDCB_GRID,[,DLONG,DLAT,[LINESTYLE=N,/LABELS]
+;
+; INPUT PARAMETERS:
+;
+;	DLONG	= Optional input longitude line spacing in degrees. If left
+;		  out, defaults to 30.
+;
+;	DLAT    = Optional input lattitude line spacing in degrees. If left
+;		  out, defaults to 30.
+;
+;
+; OPTIONAL KEYWORD PARAMETERS:
+;
+;	LINESTYLE	= Optional input integer specifying the linestyle to
+;			  use for drawing the grid lines.
+;
+;	LABELS		= Optional keyword specifying that the lattitude and
+;			  longitude lines on the prime meridian and the
+;			  equator should be labeled in degrees. If LABELS is
+;			  given a value of 2, i.e. LABELS=2, then the longitude
+;			  labels will be in hours and minutes instead of
+;			  degrees.
+;
+; OUTPUT PARAMETERS:
+;
+;	NONE
+;
+; PROCEDURE:
+;
+;	Uses WCSSPH2XY.PRO with projection 23 ("QSC" - COBE Quadrilatieralized
+;	Spherical Cube) to compute positions of grid lines and labels.
+;
+; COPYRIGHT NOTICE:
+;
+;	Copyright 1991, The Regents of the University of California. This
+;	software was produced under U.S. Government contract (W-7405-ENG-36)
+;	by Los Alamos National Laboratory, which is operated by the
+;	University of California for the U.S. Department of Energy.
+;	The U.S. Government is licensed to use, reproduce, and distribute
+;	this software. Neither the Government nor the University makes
+;	any warranty, express or implied, or assumes any liability or
+;	responsibility for the use of this software.
+;
+; AUTHOR:
+;
+;	Jeff Bloch
+;
+; MODIFICATIONS/REVISION LEVEL:
+;
+;	%I%	%G%
+;	Use WCSSPH2XY instead of QDCB   Wayne Landsman   December 1994
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+PRO QDCB_GRID,DLONG,DLAT,LINESTYLE=N,LABELS=LABELS
+
+	if not keyword_set(n) then n=0
+	if n_params() lt 2 then dlat = 30.0
+	if n_params() lt 1 then dlong = 30.0
+;
+;	Set up offsets to cube face panes
+;
+	xfaceoff = [0.0,0.0,2.0,4.0,6.0,0.0]
+	yfaceoff = [2.0,0.0,0.0,0.0,0.0,-2.0]
+	face = 0
+;
+;	Do lines of constant longitude
+;
+	lat=findgen(180)-90
+	lng=fltarr(180)
+	lngtot = long(360.0/dlong)
+	for i=0,lngtot do begin
+		lng[*]=-180.0+(i*dlong)
+                wcssph2xy, lng, lat, x, y, 23,face = face,north=0.,south=0.
+		x = x/45. & y = y/45.
+		for k=0,5 do begin
+		    j=where(face eq k,nf)
+		    if nf ne 0 then $
+		      oplot,x[j]+xfaceoff[k],$
+			y[j]+yfaceoff[k],linestyle=n
+		endfor
+	endfor
+;
+;	Do lines of constant latitude
+;
+	lng=findgen(360)-45.0
+	lat=fltarr(360)
+	lattot=long(180.0/dlat)
+	for i=1,lattot do begin
+		lat[*]=-90+(i*dlat)
+		wcssph2xy, lng, lat, x, y, 23,face = face,north=0.,south=0.
+		x = x/45. & y = y/45.
+                for k=0,5 do begin
+                    j=where(face eq k,nf) 
+                    if nf ne 0 then $
+                      oplot,x[j]+xfaceoff[k],$
+                        y[j]+yfaceoff[k],linestyle=n
+                endfor
+	endfor
+
+;
+;	Do labeling if requested
+;
+	if keyword_set(labels) then begin
+;
+;	Label equator
+;
+	    for i=0,lngtot-1 do begin
+		lng = (i*dlong)
+		if lng ne 0.0 then begin
+                    wcssph2xy, lng, 0.0, x, y, 23, face = face,north=0.,south=0.
+		    x = x/45. & y = y/45.
+		    if labels eq 1 then xyouts,x[0]+xfaceoff[face],$
+					y[0]+yfaceoff[face],noclip=0,$
+			strcompress(string(lng,format="(I4)"),/remove_all) $
+		    else begin
+		        tmp=sixty(lng*23.0/360.0)
+		        xyouts,x[0]+xfaceoff[face[0]],y[0]+yfaceoff[face[0]],$
+			    noclip=0,strcompress(string(tmp[0],tmp[1],$
+			    format='(I2,"h",I2,"m")'),/remove_all),alignment=0.5
+		    endelse
+		endif
+	    endfor
+;
+;	Label prime meridian
+;
+	    for i=1,lattot-1 do begin
+		lat=-90+(i*dlat)
+                wcssph2xy, 0.0, lat, x, y, 23, face = face
+		x = x/45. & y = y/45.
+		xyouts,x[0]+xfaceoff[face[0]],y[0]+yfaceoff[face[0]],noclip=0,$
+			strcompress(string(lat,format="(I4)"),/remove_all)
+	    endfor
+	endif
+	return
+END
diff --git a/Code/script_idl_mv/astrolib/qget_string.pro b/Code/script_idl_mv/astrolib/qget_string.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0b6159201e720c526fda2d9212d28fdc6d68c5fc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/qget_string.pro
@@ -0,0 +1,89 @@
+FUNCTION qget_string, dummy
+;+
+; NAME:
+;     QGET_STRING
+; PURPOSE:
+;     To get a string from the keyboard without echoing it to the screen.
+;
+; CALLING SEQUENCE:
+;     string = QGET_STRING() 
+;
+; INPUTS:
+;     None.
+;
+; OUTPUTS:
+;     string   The string read from the keyboard.
+;
+; SIDE EFFECTS:
+;     A string variable is created and filled.
+;
+; PROCEDURE:
+;     The IDL GET_KBRD functions is used to get each character in
+;     the string.  Each character is added to the string until a
+;     carriage return is struck.  The carriage return is not appended
+;     to the string.  Striking the delete key or the backspace key
+;     removes the previous character from the string.
+;
+; NOTES:
+;     For a widget password procedure see 
+;     http://idlcoyote.com/tip_examples/password.pro
+; MODIFICATION HISTORY:
+;     Written by Michael R. Greason, STX, 8 January 1991.
+;     Work for Mac and Windows IDL  W. Landsman    September 1995
+;-
+ compile_opt idl2
+
+;                       Variable definitions.
+;
+ st = bytarr(1)                                 ; String variable.
+ n = 0
+
+ IF !VERSION.OS_FAMILY EQ "unix" THEN dun = 10B $       ; Unix version of CR.
+                           ELSE dun = 13B       ; All other version of CR.
+wt = 1                                          ; Wait for key to be struck?
+del = 127B & bs = 8B                            ; Delete, backspace keys.
+;
+;                       Loop, gathering characters into the string until
+;                       a carriage return has been struck.
+;
+REPEAT BEGIN
+;
+;                               Get next character.
+;
+        ch = byte(get_kbrd(wt))
+        ch = ch[0]
+;
+;                               If it isn't a carriage return, process it.
+;
+        IF (ch NE dun) THEN BEGIN
+;
+;                                       If it isn't a delete or backspace,
+;                                       append it to the string.
+;
+                IF ((ch NE del) && (ch NE bs)) THEN BEGIN
+                        IF (n LE 0) THEN BEGIN
+                                st[0] = ch
+                                n = 1
+                        ENDIF ELSE BEGIN
+                                st = [st, ch]
+                                n++
+                        ENDELSE
+                ENDIF ELSE BEGIN
+;
+;                                       It's a delete/backspace.  Remove the
+;                                       previous character.
+;
+                        IF (n GT 0) THEN BEGIN
+                                n--
+                                IF (n GT 0) THEN st = st[0:(n-1)]
+                        ENDIF
+                ENDELSE
+        ENDIF
+;
+ENDREP UNTIL (ch EQ dun)
+;
+;                       Finished.
+;
+IF (n LE 0) THEN st = '' ELSE st = string(st)
+RETURN, st
+END
diff --git a/Code/script_idl_mv/astrolib/qsimp.pro b/Code/script_idl_mv/astrolib/qsimp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3e57145993dd94350d4e5d31706396a2ca10d362
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/qsimp.pro
@@ -0,0 +1,99 @@
+pro qsimp, func, A, B, S, EPS=eps, MAX_ITER = max_iter, _EXTRA = _EXTRA
+;+
+; NAME:
+;       QSIMP
+; PURPOSE:
+;       Integrate using Simpson's rule to specified accuracy.
+; EXPLANATION:
+;       Integrate a function to specified accuracy using the extended 
+;       trapezoidal rule.   Adapted from algorithm in Numerical Recipes, 
+;       by Press et al. (1992, 2nd edition), Section 4.2.     This procedure
+;       has been partly obsolete since IDL V3.5 with the introduction of the 
+;       intrinsic function QSIMP(), but see notes below.
+;
+; CALLING SEQUENCE:
+;       QSIMP, func, A, B, S, [ EPS = , MAX_ITER =, _EXTRA =  ]
+;
+; INPUTS:
+;       func - scalar string giving name of function of one variable to 
+;               be integrated
+;       A,B  - numeric scalars giving the lower and upper bound of the 
+;               integration
+;
+; OUTPUTS:
+;       S - Scalar giving the approximation to the integral of the specified
+;               function between A and B.
+;
+; OPTIONAL KEYWORD PARAMETERS:
+;       EPS - scalar specifying the fractional accuracy before ending the 
+;               iteration.  Default = 1E-6
+;       MAX_ITER - Integer specifying the total number iterations at which 
+;               QSIMP will terminate even if the specified accuracy has not yet
+;               been met.   The maximum number of function evaluations will be
+;               2^(MAX_ITER).    Default value is MAX_ITER = 20
+;
+;       Any other keywords are passed directly to the user-supplied function
+;       via the _EXTRA facility.
+; NOTES:
+;       (1) The function QTRAP is robust way of doing integrals that are not 
+;       very smooth.  However, if the function has a continuous 3rd derivative
+;       then QSIMP will likely be more efficient at performing the integral.
+;
+;       (2) QSIMP can be *much* faster than the intrinsic QSIMP() function (as
+;       of IDL V8.2.3).   This is because the intrinsic QSIMP() function only 
+;       requires that the user supplied function accept a *scalar* variable.
+;       Thus on the the 16th iteration, the intrinsic QSIMP() makes 32,767
+;       calls to the user function, whereas this procedure makes one call 
+;       with a  32,767 element vector.  Also, unlike the intrinsic QSIMP(), this
+;       procedure allows keywords in the user-supplied function.
+;
+;       (3) Since the intrinsic QSIMP() is a function, and this file contains a 
+;       procedure, there should be no name conflict.
+; EXAMPLE:
+;       Compute the integral of sin(x) from 0 to !PI/3.
+;    
+;       IDL> QSIMP, 'sin', 0, !PI/3, S   & print, S
+;   
+;       The value obtained should be cos(!PI/3) = 0.5
+;
+; PROCEDURES CALLED:
+;       SETDEFAULTVALUE, TRAPZD, ZPARCHECK
+;
+; REVISION HISTORY:
+;       W. Landsman         ST Systems Co.         August, 1991
+;       Continue after max iter warning message   W. Landsman   March, 1996
+;       Pass keyword to function via _EXTRA facility  W. Landsman July 1999
+;       Use SETDEFAULTVALUE    W. Landsman  Aug 2013
+;-
+
+ On_error,2                                  ;Return to caller
+ compile_opt idl2
+
+ if N_params() LT 4 then begin
+    print,'Syntax - QSIMP, func, A, B, S, [ MAX_ITER = , EPS = ]'
+    print,' func - scalar string giving function name'
+    print,' A,B - endpoints of integration, S - output sum'
+    return
+ endif
+
+ zparcheck, 'QSIMP', func, 1, 7, 0, 'Function name'       ;Valid inputs?
+ zparcheck, 'QSIMP', A, 2, [1,2,3,4,5], 0, 'Lower limit of Integral'
+ zparcheck, 'QSIMP', B, 3, [1,2,3,4,5], 0, 'Upper limit of Integral'
+
+ setdefaultvalue,eps,1.e-6               ;Typo fixed Oct 2013
+ setdefaultvalue,max_iter,20
+
+ ost = (oS = -1.e30)
+ for i = 0,max_iter - 1 do begin
+    trapzd, func, A,B, st, it, _EXTRA = _EXTRA
+    S = (4.*st - ost)/3.
+    if ( abs(S-oS) LT eps*abs(oS) ) then return
+    os = s
+    ost = st
+ endfor
+ 
+ message,/CON, $
+        'WARNING - Sum did not converge after '+ strtrim(max_iter,2) + ' steps'
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/qtrap.pro b/Code/script_idl_mv/astrolib/qtrap.pro
new file mode 100644
index 0000000000000000000000000000000000000000..22532d181326c716e73a16dae40c28c484ada0ca
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/qtrap.pro
@@ -0,0 +1,84 @@
+pro  qtrap, func, A, B, S, EPS=eps, MAX_ITER = max_iter, _EXTRA = _Extra
+;+
+; NAME:
+;       QTRAP
+; PURPOSE:
+;       Integrate using trapezoidal rule to specified accuracy.
+; EXPLANATION:
+;       Integrate a function to specified accuracy using the extended 
+;       trapezoidal rule.   Adapted from Numerical Recipes (1992, 2nd edition),
+;       Section 4.2. 
+;
+; CALLING SEQUENCE:
+;       QTRAP, func, A, B, S, [EPS = , MAX_ITER =, _EXTRA = ]
+;
+; INPUTS:
+;       func - scalar string giving name of function of one variable to 
+;               be integrated
+;       A,B  - numeric scalars giving the lower and upper bound of the 
+;               integration
+;
+; OUTPUTS:
+;       S - Scalar giving the approximation to the integral of the specified
+;               function between A and B.
+;
+; OPTIONAL KEYWORD PARAMETERS:
+;       EPS - scalar specify the fractional accuracy before ending the 
+;             iteration.    Default = 1E-6
+;       MAX_ITER - Integer specifying the total number iterations at which 
+;               QTRAP will terminate even if the specified accuracy has not yet
+;               been met.    The maximum number of function evaluations will 
+;               be 2^(MAX_ITER).   Default value is MAX_ITER = 20
+;
+;       Any other keywords are passed directly to the user-supplied function
+;       via the _EXTRA facility.
+; NOTES:
+;       QTRAP is robust way of doing integrals that are not very smooth.  If the
+;       function has a continuous 3rd derivative then the function QSIMP will 
+;          likely be more efficient at performing the integral.
+; EXAMPLE:
+;       Compute the integral of sin(x) from 0 to !PI/3.
+;    
+;       IDL> QTRAP, 'sin', 0, !PI/3, S   & print,S
+;   
+;       The value obtained should be cos(!PI/3) = 0.5
+;
+; PROCEDURES CALLED:
+;       TRAPZD, ZPARCHECK
+; REVISION HISTORY:
+;       W. Landsman         ST Systems Co.         August, 1991
+;       Continue after Max Iter warning message, W. Landsman  March 1996
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Pass keyword to function via _EXTRA facility  W. Landsman July 1999
+;-
+ On_error,2                                      ;Return to caller
+ compile_opt idl2
+
+ if N_params() LT 4 then begin
+    print,'Syntax - QTRAP, func, A, B, S, [ Eps = , MAX_ITER = ]
+    print,' func - scalar string giving function name
+    print,' A,B - endpoints of integration, S - output sum'
+    return
+ endif
+
+ zparcheck, 'QTRAP', func, 1, 7, 0, 'Function name'          ;Valid inputs?
+ zparcheck, 'QTRAP', A, 2, [1,2,3,4,5], 0, 'Lower limit of Integral'
+ zparcheck, 'QTRAP', B, 3, [1,2,3,4,5], 0, 'Upper limit of Integral'
+
+ if ~keyword_set( EPS ) then eps = 1.e-6
+ if ~keyword_set( MAX_ITER ) then max_iter = 20
+ olds = -1.e30
+
+ for i = 0, max_iter-1 do begin
+
+    trapzd, func, A, B, S, it, _EXTRA = _EXTRA
+    if ( abs(S-oldS) LT eps*abs(oldS) ) then return
+    olds = s
+
+ endfor
+
+ message,/CON, $
+        'WARNING - Sum did not converge after '+ strtrim(max_iter,2) + ' steps'
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/quadterp.pro b/Code/script_idl_mv/astrolib/quadterp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6f18f3bd44f5d85819d22c701a1c5ed70dd7275c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/quadterp.pro
@@ -0,0 +1,128 @@
+PRO quadterp, xtab, ytab, xint, yint, MISSING = MISSING
+;+
+; NAME:
+;       QUADTERP     
+; PURPOSE:
+;       Quadratic interpolation of X,Y vectors onto a new X grid
+; EXPLANATION:
+;       Interpolate a function Y = f(X) at specified grid points using an
+;       average of two neighboring 3 point quadratic (Lagrangian) interpolants.
+;       Use LINTERP for linear interpolation
+;
+; CALLING SEQUENCE:
+;       QUADTERP, Xtab, Ytab, Xint, Yint, [ MISSING = ]
+;
+; INPUT: 
+;       Xtab - Vector (X TABle) containing the current independent variable 
+;               Must be either monotonic increasing or decreasing
+;       Ytab - Vector (Y TABle) containing the dependent variable defined
+;               at each of the points of XTAB.
+;       Xint - Scalar or vector giving the values of X for which interpolated 
+;               Y values are sought
+;
+; OUTPUT: 
+;       Yint - Interpolated value(s) of Y, same number of points as Xint
+;
+; OPTIONAL INPUT KEYWORD:
+;       MISSING - Scalar specifying Yint value(s) to be assigned, when Xint
+;               value(s) are outside of the range of Xtab.     Default is to
+;               truncate the out of range Yint value(s) to the nearest value 
+;               of Ytab.   See the help for the INTERPOLATE function.
+; METHOD:
+;       3-point Lagrangian interpolation.  The average of the two quadratics 
+;       derived from the four nearest  points is returned in YTAB.   A single
+;       quadratic is used near the end points.   VALUE_LOCATE is used 
+;       to locate center point of the interpolation.
+;
+; NOTES:
+;       QUADTERP provides one method of high-order interpolation.   The 
+;           RSI interpol.pro function includes the following alternatives:
+;
+;       interpol(/LSQUADRATIC) - least squares quadratic fit to a 4 pt 
+;               neighborhood
+;       interpol(/QUADRATIC) - quadratic fit to a 3 pt neighborhood
+;       interpol(/SPLINE) - cubic spline fit to a 4 pt neighborhood
+;
+;       Also, the IDL Astro function HERMITE fits a cubic polynomial and its
+;             derivative to the two nearest points. 
+; RESTRICTIONS:
+;       Unless MISSING keyword is set, points outside the range of Xtab in 
+;       which valid quadratics can be computed are returned at the value 
+;       of the nearest end point of Ytab (i.e. Ytab[0] and Ytab[NPTS-1] ).
+;
+; EXAMPLE:
+;       A spectrum has been defined using a wavelength vector WAVE and a
+;       flux vector FLUX.  Interpolate onto a new wavelength grid, e.g. 
+;
+;       IDL> wgrid = [1540.,1541.,1542.,1543.,1544.,1545.]
+;       IDL> quadterp, wave, flux, wgrid, fgrid 
+;     
+;       FGRID will be a 5 element vector containing the quadratically
+;       interpolated values of FLUX at the wavelengths given in WGRID.
+;
+;  EXTERNAL ROUTINES:
+;       ZPARCHECK
+;  REVISION HISTORY:
+;       31 October 1986 by B. Boothman, adapted from the IUE RDAF
+;       12 December 1988 J. Murthy, corrected error in Xint
+;       September 1992, W. Landsman, fixed problem with double precision
+;       August 1993, W. Landsman, added MISSING keyword
+;       June, 1995, W. Landsman, use single quadratic near end points
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Fix occasional problem with integer X table,  
+;       YINT is a scalar if XINT is a scalar   W. Landsman Dec 1999
+;       Use VALUE_LOCATE instead of TABINV W. Landsman  Feb. 2000
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 4 then begin
+     print,'Syntax - QUADTERP, xtab, ytab, xint, yint, [ MISSING = ]'
+     return
+ endif
+
+ zparcheck,'QUADTERP',xtab,1,[1,2,3,4,5],1,'Independent (X) vector'
+ zparcheck,'QUADTERP',ytab,2,[1,2,3,4,5],1,'Dependent (Y) vector'
+
+ npts = min( [N_elements(xtab), N_elements(ytab) ] )
+ m = n_elements(xint)
+
+ if size(xtab,/TNAME) NE 'DOUBLE' then xt = float(xtab) else xt = xtab
+ 
+ Xmin = min( [ Xtab[0],Xtab[npts-1] ], max = Xmax)
+ u = xint > Xmin < Xmax 
+
+ if npts LT 3 then  $
+     message,' ERROR - At least 3 points required for quadratic interpolation'
+
+; Determine index of data-points from which interpolation is made 
+
+        index = value_locate(xtab,xint) > 0L < (npts-2)
+
+; First quadratic   
+ 
+        i0 = (index-1) > 0   & i1 = i0+1 & i2 = (i1 +1)
+        x0  = xt[i0]  & x1 = xt[i1] & x2 = xt[i2]
+        p1 = ytab[i0] * (u-x1) * (u-x2) / ((x0-x1) * (x0-x2)) + $
+             ytab[i1] * (u-x0) * (u-x2) / ((x1-x0) * (x1-x2)) + $
+             ytab[i2] * (u-x0) * (u-x1) / ((x2-x0) * (x2-x1))
+
+; Second Quadratic
+
+        i2 = (index+2) < (npts-1) & i1 = i2-1 & i0 = (i1-1)
+        x0  = xt[i0]  & x1 = xt[i1] & x2 = xt[i2]
+        p2 =  ytab[i0] * (u-x1) * (u-x2) / ((x0-x1) * (x0-x2)) + $
+              ytab[i1] * (u-x0) * (u-x2) / ((x1-x0) * (x1-x2)) + $
+              ytab[i2] * (u-x0) * (u-x1) / ((x2-x0) * (x2-x1))
+  
+  
+      yint = (p1 + p2) / 2.    ;Average of two quadratics
+
+  if N_elements(missing) EQ 1 then begin
+        bad = where( (Xint LT Xmin) or (Xint GT Xmax ), Nbad)
+        if Nbad GT 0 then Yint[bad] = missing
+  endif
+ 
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/query_irsa_cat.pro b/Code/script_idl_mv/astrolib/query_irsa_cat.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5886516a606a6ee27e4d906dbc539a301792a8a4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/query_irsa_cat.pro
@@ -0,0 +1,258 @@
+FUNCTION query_irsa_cat, targetname_OR_coords, catalog=catalog, radius=radius, radunits=radunits, outfile=outfile, change_null=change_null, DEBUG=debug
+
+;+
+; NAME: 
+;    QUERY_IRSA_CAT
+;
+; PURPOSE: 
+;    Query a catalog in the NASA/IPAC Infrared Science Archive (IRSA)
+;    database by position or resolvable target name.
+; 
+; EXPLANATION:
+;    Uses the IDL SOCKET command to provide a query of a catalog 
+;    in the IRSA (http://irsa.ipac.caltech.edu/) database over the Web and
+;    return results in an IDL structure.  If outfile is set, it saves
+;    the query as an IPAC table file.  This can be slow for large query
+;    results, so only write a file if needed.    
+;     
+; CALLING SEQUENCE: 
+;    info = query_irsa_cat(targetname_or_coords, [catalog=catalog,
+;    radius=radius, radunits=radunits, outfile=outfile,
+;    change_null=change_null, /DEBUG])
+;
+; INPUTS: 
+;
+;    TARGETNAME_OR_COORDS - Either a string giving a resolvable target
+;           name (with J2000 coordinates determined by NED or SIMBAD), or a 
+;           2-element numeric vector giving the J2000 right ascension 
+;           and declination, both in degrees.
+;
+; OPTIONAL INPUT:
+;
+;    CATALOG - string giving the identifier of the IRSA catalog to be
+;           searched.  The complete list of catalogs and identifier strings is available in
+;           XML format at:
+;             http://irsa.ipac.caltech.edu/cgi-bin/Gator/nph-scan?mode=xml
+;           or as an IPAC Table (ascii) at:
+;             http://irsa.ipac.caltech.edu/cgi-bin/Gator/nph-scan?mode=ascii
+;
+;           In the table, the identifier string is in the "catname" column.
+;
+;           If this keyword is omitted, the program will query the 2MASS point
+;           source catalog.
+;
+;           Examples of current IRSA catalogs include:  
+;              'wise_allsky_4band_p3as_psd' - WISE All-Sky Source Catalog
+;              'fp_psc' - 2MASS Point Source Catalog
+;              'iraspsc' - IRAS Point Source Catalog v2.1 (PSC)
+;              'irasfsc' - IRAS Faint Source Catalog v2.0
+;              'cosmos_ib_phot' - COSMOS Intermediate and Broad Band Photometry Catalog 2008
+;              'akari_irc' - Akari/IRC Point Source Catalogue
+;
+;    RADIUS - scalar input of the radius of the search.  By default it
+;             has a value of 60 arcsec. IRSA
+;           catalogs have maximum allowable search radii.  These are listed on the corresponding
+;           web interface page for the catalog search, or in the nph-scan return table in the
+;           "coneradius" column.
+;    
+;    RADUNITS - string giving the units of the radius.  By default it is 'arcsec'.
+;    
+;    OUTFILE - if present, the search results are written to a file with this name.
+;
+;     CHANGE_NULL - a numeric value (input as integer) to put in the structure if the table uses a string for nulls.  Default is -9999.
+;
+;    DEBUG - /DEBUG provides some additional output.
+;
+; OUTPUTS: 
+;    info - Anonymous IDL structure containing information on the catalog.  The structure
+;           tag names are taken from the catalog column names.  If no objects were found in 
+;           the catalog, the structure values will be empty or zero.  If any input parameter
+;           (e.g. catalog name) is invalid, the structure will have no
+;           content fields other than info.CREATED.
+;
+;           If the query fails or is invalid, the function returns a value of -1.  
+;
+; EXAMPLES: 
+;           (1) Plot a histogram of the J magnitudes of all 2MASS
+;               point sources within 10 arcminutes of the center of the
+;               globular cluster M13.  Save the IPAC table. 
+;
+;             IDL> info = query_irsa_cat('m13',radius=10,radunits='arcmin',outfile='save.tbl')
+;             IDL> help,/struct,info
+;             IDL> plothist,info.j_m,xran=[10,20]
+;
+;           (2) Find the position of the faintest IRAS 60 micron
+;               source within 1 degree of central position of the
+;               COSMOS survey (10h00m28.6s +02d12m21.0s in J2000)
+;  
+;             IDL> info = query_irsa_cat([150.11917,2.205833], catalog='irasfsc', radius=1, radunits='deg')
+;             IDL> help,/struct,info
+;             IDL> idx = where(info.fnu_60 eq min(info.fnu_60))
+;             IDL> print, (info.ra)[idx], (info.dec)[idx]
+;
+; PROCEDURES USED:
+;    READ_IPAC_VAR  comes with query_irsa_cat.pro
+;    WEBGET(), VALID_NUM  from IDLastro
+;
+; NOTES:
+;    The program writes an output IPAC table file only if the
+;    OUTFILE keyword is set.
+;
+; MODIFICATION HISTORY:
+;    Adapted from queryvizier.pro - H. Teplitz, IPAC  September 2010
+;    Removed requirement of writing/reading IPAC table file -
+;      T. Brooke, IPAC May 2011
+;    Longer timeout for webget, added change_null - TYB June 2013
+;-
+
+;Copyright � 2013, California Institute of Technology
+;All rights reserved. Based on Government Sponsored Research NAS7-03001 and NNN12AA01C.
+;
+;
+;Redistribution and use in source and binary forms, with or without
+;modification, are permitted provided that the following conditions
+;are met:
+;
+; *  Redistributions of source code must retain the above copyright
+;    notice, this list of conditions and the following disclaimer.
+;
+; *  Redistributions in binary form must reproduce the above copyright
+;    notice, this list of conditions and the following disclaimer in
+;    the documentation and/or other materials provided with the
+;    distribution.
+;
+; *  Neither the name of the California Institute of Technology
+;    (Caltech) nor the names of its contributors may be used to
+;    endorse or promote products derived from this software without
+;    specific prior written permission.
+;
+;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+;"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+;LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+;A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+;HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+;INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+;BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+;OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+;AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+;LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+;WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+;POSSIBILITY OF SUCH DAMAGE.
+;
+
+on_error,2
+compile_opt idl2
+
+if N_params() lt 1 then begin
+  print,'Syntax - info = query_irsa_cat(targetname_or_coords,'
+  print,'           [catalog=catalog,radius=radius,radunits=radunits,'
+  print,'            outfile=outfile,change_null=change_null,/DEBUG])'
+endif
+
+IF NOT(keyword_set(radius)) THEN radius = 60
+IF NOT(keyword_set(radunits)) THEN radunits = 'arcsec'
+
+IF (keyword_set(outfile)) THEN BEGIN
+  writefile=outfile
+  check = file_search(writefile)
+  IF check NE '' THEN BEGIN
+    print, 'OUTFILE exists.  Delete it [y/n]?  '
+    c2 = get_kbrd(1)
+    IF c2 EQ 'y' OR c2 EQ 'Y' THEN spawn, 'rm '+writefile $
+    ELSE return, -1
+  ENDIF
+ENDIF 
+
+IF ( keyword_set(change_null) ) THEN BEGIN 
+  IF ( NOT(valid_num(change_null,/integer)) ) THEN BEGIN
+    print, 'ERROR: change null value must be integer.'
+    return, -1
+  ENDIF
+  null_num = change_null
+ENDIF 
+
+;;;;;;;;;;;;;;;;;;;  CONSTRUCT THE PARTS OF THE QUERY STRING
+
+root = 'http://irsa.ipac.caltech.edu/cgi-bin/Gator/nph-query'
+
+;;;; CATALOG STRING
+
+IF keyword_set(catalog) THEN catalog_name=catalog ELSE catalog_name='fp_psc'
+
+catstr='&catalog='+catalog_name
+
+;;;; OBJECT STRING
+
+target = targetname_OR_coords
+
+IF N_elements(target) EQ 2 THEN BEGIN 
+   ra = double(target[0])
+   dec = double(target[1])
+   objstr = '&objstr='+strn(ra)+'+'+strn(dec)
+ENDIF $
+ELSE BEGIN 
+   object = repstr(target,'+','%2B')
+   object = repstr(strcompress(object),' ','+')
+   objstr = '&objstr='+object
+ENDELSE 
+
+; No empty string
+IF strlen(objstr) le 8 THEN BEGIN
+  print, 'Empty object string not allowed.'
+  return, -1
+ENDIF
+
+;;;;  SEARCH SHAPE AND SIZE
+
+spatial_str='Cone' 
+spatial_param_name=['radius','radunits']
+spatial_param_value_str = [strn(radius), radunits]
+
+nspat = n_elements(spatial_param_name)
+
+spatstr = '&spatial='+spatial_str
+spatparstr = ''
+
+FOR i = 0l, nspat-1 DO $
+   spatparstr=spatparstr+'&'+spatial_param_name[i]+'='+spatial_param_value_str[i]
+      
+;;;; USE IPAC FORMAT
+
+out_fmt = '?outfmt=1'
+
+;;;; combine into query string
+
+url_q = root+out_fmt+objstr+spatstr+spatparstr+catstr
+IF keyword_set(debug) THEN print, url_q
+
+;;;;;  use the IDL WEBGET to do the HTTP GET
+
+IF keyword_set(debug) THEN print, systime(0) 
+
+url_return = WEBGET(url_q, timeout=120)   
+
+IF keyword_set(debug) THEN print, systime(0) 
+
+;;;;;  If requested, write the output to the outputfile
+
+IF (keyword_set(outfile)) THEN BEGIN
+  n = N_ELEMENTS(url_return.text)
+  OPENW, wunit, writefile, /get_lun
+  FOR i = 0l, n-1 DO PRINTF, wunit, (url_return.text)[i]
+  FREE_LUN, wunit
+ENDIF
+
+;;;;; read the IPAC query into a structure
+
+textvar = url_return.text
+
+IF (keyword_set(change_null)) THEN $
+  irsa_struct = read_ipac_var(textvar, change_null = null_num) $
+ELSE $
+  irsa_struct = read_ipac_var(textvar)
+
+IF (n_tags(irsa_struct) eq 0) THEN print,'ERROR: unable to read results into structure.'
+
+return, irsa_struct
+
+END
diff --git a/Code/script_idl_mv/astrolib/querydss.pro b/Code/script_idl_mv/astrolib/querydss.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6b04de0157580240fa456405ec84c8b45cef9835
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/querydss.pro
@@ -0,0 +1,182 @@
+PRO QueryDSS, target, Image,  Header, IMSIZE=ImSIze, ESO=eso, STSCI=stsci, $
+              NED=ned, SURVEY = survey, OUTFILE = outfile, VERBOSE=verbose
+;+
+; NAME: 
+;   QueryDSS
+;
+; PURPOSE: 
+;    Query the digital sky survey (DSS) on-line at  the STSCI (or ESO) server
+;
+; EXPLANATION: 
+;     The script can query the DSS survey and retrieve an image and FITS 
+;     header either from the the Space Telescope Science Institute (STScI) or 
+;     European Space Observatory (ESO) servers.
+;     See http://archive.eso.org/dss/dss and/or 
+;     http://archive.stsci.edu/dss/index.html for details.
+;
+; CALLING SEQUENCE: 
+;      QueryDSS, targetname_or_coords, Im, Hdr, [IMSIZE= , /ESO, Outfile= ]
+;
+; INPUTS:
+;      TARGETNAME_OR_COORDS - Either a scalar string giving a target name, 
+;          (with J2000 coordinates determined by SIMBAD (default) or NED), or 
+;          a 2-element numeric vector giving the J2000 right ascension in 
+;          *degrees* and the target declination in degrees.
+;
+; OPTIONAL INPUTS: 
+;          None
+;
+; OPTIONAL KEYWORD PARAMETERS: 
+;     ImSize - Numeric scalar giving size of the image to be retrieved in 
+;                 arcminutes.    Default is 10 arcminute.
+;
+;     /ESO - Use the ESO server for image retrieval.    Default is to use
+;            the STScI server 
+;
+;     /NED - Query the Nasa Extragalactic Database (NED) for the
+;            target's coordinates.  The default is to use Simbad for
+;            the target search.
+;
+;     OUTPUT  - scalar string specifying name of output FITS file.    
+;            If set, then the output IDL variables are not used.
+; 
+;     /STSCI - obsolete keyword, now does nothing, since STSCI is the default
+;              Server.     
+;
+;     SURVEY - Scalar string specifying which survey to retrieve.  
+;          Possible values are 
+;          '1' - First generation (red), this is the default
+;          '2b' - Second generation blue
+;          '2r' - Second generation red
+;          '2i' - Second generation near-infrared
+; 
+;      Note that 2nd generation images may not be available for all regions
+;      of the sky.   Also note that the first two letters of the 'REGION'
+;      keyword in the FITS header gives the bandpass 'XP' - Red IIIaF, 
+;      'XJ' - Blue IIIaJ, 'XF' - Near-IR IVN
+;
+;      /VERBOSE - If set, then the query sent to the DSS server is displayed
+;
+; OUTPUTS: 
+;       Im - The image returned by the server. If there is an error, this 
+;             contains a single 0.
+;
+;       Hdr - The FITS header of the image. Empty string in case of errors.
+;
+;       If the OutFile keyword is set then no outputs are returned (only the
+;       file is written).
+; SIDE EFFECTS: 
+;     If Im and Hdr exist in advance,  they are overwritten.
+;
+; RESTRICTIONS: 
+;      Relies on a working network connection. 
+;
+; PROCEDURE: 
+;      Construct a query-url,  call WEBGET() and sort out the server's 
+;      answer.
+;
+; EXAMPLE:           
+;      Retrieve an 10'  image surrounding the ultracompact HII region
+;       G45.45+0.06.   Obtain the 2nd generation blue image.
+;
+;       IDL> QueryDSS, 'GAL045.45+00.06', image, header, survey = '2b'
+;       IDL> tvscl, image
+;       IDL> hprint, header
+;       IDL> writefits,'dss_image.fits', image, header
+; Note that the coordinates could have been specified directly, rather than
+; giving the target name.
+;       IDL> QueryDSS, [288.587, 11.1510], image, header,survey='2b'
+;
+; To write a file directly to disk, use the OutFile keyword
+;
+;       IDL> QueryDSS, [288.587, 11.1510], survey='2b', out='gal045_2b.fits'
+;   
+; PROCEDURES CALLED:
+;       QUERYSIMBAD, WEBGET()
+; MODIFICATION HISTORY: 
+;       Written by M. Feldt, Heidelberg, Oct 2001 <mfeldt@mpia.de>
+;       Option to supply target name instead of coords  W. Landsman Aug. 2002
+;       Added OUTFILE, /NED keywords W. Landsman   April 2003
+;       Don't abort on Simbad failure W. Landsman/J. Brauher  June 2003
+;       Added /VERBOSE keyword W. Landsman   Jan 2009
+;       Make /STScI server the default  W. Landsman June 2010
+;      Fix OUTPUT option  W. Landsman June 2010
+;
+;-
+  On_error,2
+  compile_opt idl2
+  if N_params() LT 1 then begin
+      print,'Syntax - QueryDSS, TargetName_or_coords, image, header'
+      print,"           [Imsize= ,/ESO, /STScI, Survey = ['1','2b','2r','2i'] "
+      print,'            /NED, OutFile = ]'
+      return
+   endif
+  ;;
+  if N_elements(target) EQ 2 then begin
+      ra = float(target[0])
+      dec = float(target[1])
+  endif else begin
+       QuerySimbad, target, ra,dec, NED= ned, Found = Found
+       if found EQ 0 then begin 
+             message,/inf,'Target name ' + target + $
+                 ' could not be translated by SIMBAD'
+             return
+       endif
+  endelse  
+  IF ~Keyword_Set(ImSize) THEN ImSize = 10
+  Equinox = 'J2000'
+  ;;
+  ;;
+ if N_elements(survey) EQ 0 then survey = '1'
+ dss = strlowcase(strtrim(strmid(survey,0,2),2))
+ if keyword_set(ESO) then begin
+  case dss of 
+  '1': dss = 'DSS1'
+  '2b': dss = 'DSS2-blue'
+  '2r': dss = 'DSS2-red'
+  '2i': dss = 'DSS2-infrared'
+  else: message,'Unrecognized Survey - should be 1, 2b, 2r or 2i'
+ endcase
+ endif
+  IF keyword_set(eso) THEN $ 
+    QueryURL=strcompress("http://archive.eso.org/dss/dss/image?ra="+$
+                       string(RA)+$
+                       "&dec="+$
+                       string(DEC)+$
+                       "&x="+$
+                       string(ImSize)+$
+                       "&y="+$
+                       string(ImSize)+$
+                       "&Sky-Survey="+dss +"&mime-type=download-fits", /remove) $
+  ELSE $
+    QueryURL=strcompress("http://archive.stsci.edu/cgi-bin/dss_search?ra="+$
+                         string(RA)+$
+                         "& dec="+$
+                         string(DEC)+$
+                         "& equinox="+$
+                         Equinox +$
+                         "& height="+$
+                         string(ImSize) +$
+                         "&generation=" + dss +$                       
+                         "& width="+$
+                         string(ImSize)+$
+                         "& format=FITS", /remove)
+  ;;
+
+  if keyword_set(verbose) then message,/INF, QueryURL
+  if keyword_set(OutFile) then begin
+      if ~keyword_set(ESO) then dss = 'DSS' + dss
+      message,'Writing ' + dss + ' FITS file ' + outfile,/inf
+      Result = webget(QueryURL, copyfile= outfile)
+      return
+  endif
+  Result = webget(QueryURL)
+  Image = Result.Image
+  Header = Result.ImageHeader
+  ;;
+  ;; error ?
+  ;;
+  IF N_Elements(Image) NE 1 THEN return
+  message, 'Problem retrieving your image! The server answered:', /info
+  print, Result.Text
+END 
diff --git a/Code/script_idl_mv/astrolib/querygsc.pro b/Code/script_idl_mv/astrolib/querygsc.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d59af6bcae9bffda3f09fcd4f879e3f3a31392a4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/querygsc.pro
@@ -0,0 +1,192 @@
+
+function Querygsc, target, dis,magrange = magrange, HOURS = hours, $
+   VERBOSE=verbose, BOX = box
+;+
+; NAME: 
+;   QUERYGSC
+;
+; PURPOSE: 
+;   Query the Guide Star Catalog (GSC V2.3.2) at STScI by position
+; 
+; EXPLANATION:
+;   Uses the IDL SOCKET command to query the GSC 2.3.2 database over the Web.
+;   The number and names of the structure tags was changed in September 2015
+;
+;   Alternatively, (and more reliably) one can query the GSC 2.3.2 catalog using
+;   queryvizier.pro and the VIZIER database, e.g.  
+;     IDL> st = queryvizier('GSC2.3',[23,35],10,/all)
+; 
+;   GSC2.3 is an all-sky export of calibrated photographic survey plate 
+;   source parameters from the COMPASS database.  The number of unique
+;   objects is approximately 945,592,683.   All sources are 
+;   from the second-generation plate-processing pipeline with the exception
+;   of Tycho-2 and Skymap sources in the case of very bright objects. The 
+;   Skymap sources are exported when there is no matching GSC or Tycho 
+;   sources.  Each GSC 2.3 entry contains only one position and one 
+;   magnitude per bandpass for each unique sky object
+;
+; CALLING SEQUENCE: 
+;     info = QueryGSC(targetname_or_coords, [ dis, /HOURS] )
+;
+; INPUTS: 
+;      TARGETNAME_OR_COORDS - Either a scalar string giving a target name, 
+;          (with J2000 coordinates determined by SIMBAD), or a 2-element
+;          numeric vector giving the J2000 right ascension in *degrees* (or 
+;          hours if /HOURS is set) and the target declination in degrees.
+;
+; OPTIONAL INPUT:
+;    dis - Numeric scalar giving search radius in arcminutes to search around 
+;          specified target    Default is 5 arcminutes
+;
+; OPTIONAL INPUT KEYWORDS:
+;
+;    /BOX - if set, then radius gives  a box width in arcminutes
+;    /HOURS - If set, then the right ascension is both input and output (in
+;             the info .ra tag) in hours instead of degrees
+;    /VERBOSE - If set, then the CGI command to the Webserver will be displayed
+;;
+; OUTPUTS: 
+;   info - IDL structure containing information on the GSC stars within the 
+;          specified distance of the specified center.   There are (currently)
+;          48 tags in this structure  -- for further information see
+;          http://gsss.stsci.edu/Catalogs/GSC/GSC2/gsc23/gsc23_release_notes.htm
+;
+
+;          .GSC2ID - GSC2 name
+;          .GSC1ID - GSC1 name
+;          .HSTID - GSC 2.3 name for HST operations
+;          .RA,.DEC - Position in degrees (double precision).   RA is given in
+;                   hours if the /HOURS keyword is set.
+;          .EPOCH - mean epoch of the observation
+;          .RAEPSILON, .DECEPSION - uncertainty (in arcseconds) in the RA and 
+;                   Dec
+;          .FPGMAG, .FPGERR, .FPGMAGCODE - mag, error, code in photographic F
+;          .JPGMAG, .JPGERR, .JPGMAGCODE - mag, error code, photographic J
+;          .VPGMAG, .VPGERR, .VPGMAGCODE - V mag, error, code
+;          .NPGMAG, .NPGERR, .NPGMAGCODE - mag, error, code
+;          .UMAG, .UERR, .UMAGCODE - magnitude, error, code
+;          .BMAG, .BERR, .BMAGCODE - magnitude, error, code
+;          .VMAG, .VERR, .VMAGCODE - magnitude, error, code
+;          .RMAG, .RERR, .RMAGCODE - magnitude, error, code
+;          .IMAG, .IERR, .IMAGCODE - magnitude, error, code
+;          .JMAG, .JERR, .JMAGCODE - magnitude, error, code
+;          .HMAG, .HERR, .HMAGCODE - magnitude, error, code
+;          .KMAG, .KERR, .KMAGCODE - magnitude, error, code
+;          .CLASS - classification (0-5): 0-star, 1-galaxy, 2-blend, 
+;          .SEMIMAJORAXIS - semi-major axis in pixels
+;          .POSITIONANGLE - Position angle of extended objects in degrees
+;                         3-nonstar, 4-unclassified, 5-defect
+;          .SOURCESTATUS -10 digit field  used to encode more detailed information 
+;              about the properties of the catalog object.   For more info, see
+;http://www-gsss.stsci.edu/Catalogs/GSC/GSC2/gsc23/gsc23_release_notes.htm#ClassificationCodes
+;           .VARIABLEFLAG, MULTIPLEFLAG - Variability andd multiplicity flags
+;            COMPASSGSC2ID - Unique ID in the Compass database 
+;            http://gsss.stsci.edu/zzzOldWebSite/compass/CompassHome.htm
+; EXAMPLE: 
+;          Plot a histogram of the photographic J magnitudes of all GSC 2.3.2 
+;          stars within 10 arcminutes of the center of the globular cluster M13 
+;
+;          IDL> info = querygsc('M13',10)
+;          IDL> plothist,info.jpgmag,xran=[10,20]
+;
+; PROCEDURES USED:
+;          QUERYSIMBAD, RADEC, WEBGET()
+;
+; MODIFICATION HISTORY: 
+;         Written by W. Landsman  SSAI  August 2002
+;         Fixed parsing of RA and Dec  W. Landsman September 2002
+;         Major rewrite to use new STScI Web server, remove magrange
+;           keyword   W. Landsman Dec 2007
+;         Update server name, added /BOX,/ VERBOSE keywords W.L 19 Dec 2007
+;         Web server now also returns infrared data  W.L. Feb 2010
+;         Fixed case where dec neg. and deg or min 0 Pat Fry Jul 2010
+;         Updated for new server format W. Landsman  April 2014
+;         Updated for new server format W. Landsman  September 2015
+;
+;-
+  compile_opt idl2
+  if N_params() LT 2 then begin
+       print,'Syntax - info = QueryGSC(targetname_or_coord, dis,'
+       print,'                        [/Hours, /Box, /VERBOSE} )'
+       print,'   RA (degrees), Dec (degrees) -- search coordinates of center)'
+       print,'  dis -- search radius in arcminutes'
+       if N_elements(info) GT 0 then return,info else return, -1
+  endif
+  if N_elements(dis) EQ 0 then dis = 5
+    if N_elements(target) EQ 2 then begin
+      ra = float(target[0])
+      dec = float(target[1])
+  endif else begin
+       QuerySimbad, target, ra,dec, Found = Found
+       if found EQ 0 then message,'Target name ' + target + $
+                 ' could not be translated by SIMBAD'
+  endelse  
+  radius = keyword_set(box)? 'Box' : 'Radius'
+  
+   radec,ra,dec,hr,mn,sc,deg,dmn,dsc,hours=keyword_set(hours)
+   deg = string(deg,'(i3.2)')
+   dsn = strmid(deg,0,1)
+   deg = strmid(deg,1,2)
+   if (dmn lt 0 || dsc lt 0) then begin
+       dmn = abs(dmn)
+       dsc = abs(dsc)
+       dsn = '-'
+   endif
+   sc = round(sc)
+   dsc = round(dsc)
+   if dsn EQ ' ' then dsn = '%2B'
+  ;;
+  QueryURL = "http://gsss.stsci.edu/webservices/vo/CatalogSearch.aspx?" + $
+   'RA=' + strtrim(ra,2)  + '&Dec=' + strtrim(dec,2) + $
+    '&SR=' + strtrim(dis/60.,2) + $
+    '&FORMAT=CSV&CAT=GSC23'
+
+    
+  if keyword_set(verbose) then print,queryurl
+  ;;  
+  Result = webget(QueryURL)
+ ;
+  t = result.text
+
+  nstar = N_elements(t) -2
+  if strmid(t[0],0,5) NE 'Usage' and nstar GT 0 THEN BEGIN
+  headers = strsplit(t[1],',',/extract)
+  
+  info = create_struct(Name='gsc',headers, 0LL,'','','', $
+   0.0d,0.0d, 0.0,0.0,0.0, $
+     0.0, 0.0, 0, $   ;Fpgmag,Err,code
+   0.0, 0.0, 0, $   ;Jpgmag,Err,code
+    0.0, 0.0, 0, $   ;Vmag,Err,code
+       0.0, 0.0, 0, $   ;Nmag,Err,code 
+  0.0, 0.0, 0, $   ;Umag,Err,code
+    0.0, 0.0, 0, $   ;Bmag,Err,code
+  0.0, 0.0, 0, $   ;Rmag,Err,code
+    0.0, 0.0, 0, $   ;Imag,Err,code
+  0.0, 0.0, 0, $   ;Jmag,Err,code
+   0.0, 0.0, 0, $   ;Hmag,Err,code
+   0.0, 0.0, 0, $   ;Kmag,Err,code
+   0,  $   ;Classification 
+   0.,  $   ;Size
+   0., 0., 0LL, $   eccentricity, positionangle, objectflags
+   0, 0 , $ ;variable, Multiple flag
+   0LL, '' )
+ 
+
+
+  info = replicate(info,nstar)
+
+  for i=0,nstar-1 do begin
+      temp = strtrim(strsplit(t[i+2],',',/extract),2)
+       for j=0,N_elements(temp)-1 do begin 
+       info[i].(j) = temp[j]      
+       endfor
+  endfor
+   ENDIF ELSE BEGIN 
+      message, 'No objects returned by server. The server answered:', /info
+      print, Result.Text
+      if N_elements(info) GT 0 then return, info else return, -1
+  ENDELSE 
+  if keyword_set(hours) then info.ra = info.ra/15.0d 
+ return,info
+END 
+  
diff --git a/Code/script_idl_mv/astrolib/querysimbad.pro b/Code/script_idl_mv/astrolib/querysimbad.pro
new file mode 100644
index 0000000000000000000000000000000000000000..70fd6c3946f432e2b9b7ed67c6c189f1f28eb3e9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/querysimbad.pro
@@ -0,0 +1,200 @@
+PRO QuerySimbad, name, ra, de, id, Found = found, NED = ned, ERRMSG = errmsg, $
+    Verbose = verbose, CADC = cadc, CFA=cfa, Server=server, SILENT=silent, $
+    Print = print,Vmag=Vmag,Jmag=Jmag,Hmag=Hmag,Kmag=Kmag,parallax=parallax
+;+
+; NAME: 
+;   QUERYSIMBAD
+;
+; PURPOSE: 
+;   Query the SIMBAD/NED/Vizier astronomical name resolver to obtain coordinates
+;
+; EXPLANATION: 
+;   Uses the IDL SOCKET command to query either the SIMBAD or NED nameserver 
+;   over the Web to return J2000 coordinates.  By default, QuerySimbad 
+;   first queries the Simbad database, then (if no match found) the NED 
+;   database, and then the Vizier database.
+;    
+;   For details on the SIMBAD service, see http://simbad.u-strasbg.fr/Simbad 
+;   and for the NED service, see http://ned.ipac.caltech.edu/
+;
+; CALLING SEQUENCE: 
+;    QuerySimbad, name, ra, dec, [ id, Found=, /NED, /CADC, ERRMSG=, /VERBOSE]
+;        /PRINT, Vmag=V, Jmag=J, Hmag=H, Kmag=Kmag, parallax=parallax
+;
+; INPUTS: 
+;    name - a scalar string containing the target name in SIMBAD (or NED)
+;           nomenclature. For SIMBAD details see
+;           http://vizier.u-strasbg.fr/cgi-bin/Dic-Simbad .
+;
+; OUTPUTS: 
+;     ra -  Right ascension of the target in J2000.0 in *degrees*, scalar 
+;     dec - declination of the target in degrees, scalar
+;
+; OPTIONAL INPUT KEYWORD:
+;     /CFA - if set, then use the Simbad server at the Center for Astrophysics
+;             rather than the default server in Strasbourg, France.
+;     ERRMSG   = If defined and passed, then any error messages will be
+;                  returned to the user in this parameter rather than
+;                  depending on the MESSAGE routine in IDL.  If no errors are
+;                  encountered, then a null string is returned. 
+;     /NED - if set, then only the nameserver of the NASA Extragalactic database
+;            is used to resolve the name and return coordinates.   Note that
+;           /NED cannot be used with Galactic objects
+;     /VERBOSE - If set, then the HTTP-GET command is displayed
+;     /PRINT - if set, then output coordinates are displayed at the terminal 
+;            By default, the coordinates are displayed if no output parameters
+;           are supplied to QUERYSIMBAD
+;     /SILENT - If set, then don't print warnings if multiple SIMBAD objects
+;             correspond to the supplied name.
+; OPTIONAL OUTPUT: 
+;     id - the primary SIMBAD (or NED) ID of the target, scalar string
+;          As of June 2009, a more reliable ID seems to be found when using 
+;          CFA (/CFA) server.
+;
+; OPTIONAL KEYWORD OUTPUTS:
+;     found - set to 1 if the translation was successful, or to 0 if the
+;           the object name could not be translated by SIMBAD or NED
+;     Errmsg - if supplied, then any error messages are returned in this
+;            keyword, rather than being printed at the terminal.   May be either
+;            a scalar or array.
+;     Server - Character indicating which server was actually used to resolve
+;           the object, 'S'imbad, 'N'ed or 'V'izier
+;     Vmag - supply to receive the SIMBAD V magnitude 
+;     Jmag - supply to receive the SIMBAD J magntiude
+;     Hmag - supply to receive the SIMBAD H magnitude 
+;     Kmag - supply to receive the SIMBAD K magnitude 
+;     Parallax - supply to receive the SIMBAD parallax in milliarcseconds
+;            
+; EXAMPLES:
+;     (1) Display the J2000 coordinates for the ultracompact HII region
+;         G45.45+0.06 
+;
+;      IDL> QuerySimbad,'GAL045.45+00.06'
+;           ===>19 14 20.77  +11 09  3.6
+; PROCEDURES USED:
+;       REPSTR(), WEBGET()
+; NOTES:
+;     The actual  query is made to the Sesame name resolver 
+;     ( see http://cdsweb.u-strasbg.fr/doc/sesame.htx ).     The Sesame
+;     resolver first searches the Simbad name resolver, then  NED and then
+;     Vizier.   
+; MODIFICATION HISTORY: 
+;     Written by M. Feldt, Heidelberg, Oct 2001   <mfeldt@mpia.de>
+;     Minor updates, W. Landsman   August 2002
+;     Added option to use NED server, better parsing of SIMBAD names such as 
+;          IRAS F10190+5349    W. Landsman  March 2003
+;     Turn off extended name search for NED server, fix negative declination
+;     with /NED    W. Landsman  April 2003
+;     Use Simbad Sesame sever, add /Verbose, /CADC keywords 
+;       B. Stecklum, TLS Tautenburg/ W. Landsman, Feb 2007
+;    Update NED query to account for new IPAC format, A. Barth  March 2007
+;    Update NED query to account for another new IPAC format, A. Barth  
+;                                                   July 2007
+;     Update message when NED does not find object  W.L.  October 2008
+;     Remove CADC keyword, add CFA keyword, warning if more than two
+;         matches  W.L. November 2008 
+;     Make NED queries through the Sesame server, add Server output 
+;          keyword  W.L.  June 2009
+;     Don't get primary name if user didn't ask for it  W.L. Aug 2009
+;     Added /SILENT keyword W.L. Oct 2009
+;     Added /PRINT keyword W.L.   Oct 2011
+;     Added ability to get V, J, H, and K magnitudes as well as
+;     a parallax - jswift, Jan 2014
+;-
+
+  compile_opt idl2
+  if N_params() LT 1 then begin
+       print,'Syntax - QuerySimbad, name, ra, dec, [ id, ]'
+       print,'                 Found=, /CFA, /NED, ERRMSG=, /VERBOSE]'
+       print,'   Input - object name, scalar string'
+       print,'   Output -  Ra, dec of object (degrees)'
+       return
+  endif
+  
+  Catch, theError
+  IF theError NE 0 THEN BEGIN
+      Catch,/CANCEL
+      void = cgErrorMsg(/Quiet)
+      RETURN
+      ENDIF   
+  ;;
+  printerr = ~arg_present(errmsg)
+  if ~printerr  then errmsg = ''
+  object = repstr(name,'+','%2B')
+ object = repstr(strcompress(object),' ','%20')
+ if keyword_set(Cadc) then message,'CADC keyword is no longer supported'
+ if keyword_set(cfa) then base = 'vizier.cfa.harvard.edu/viz-bin' else $
+ base = 'cdsweb.u-strasbg.fr/cgi-bin'
+    if keyword_set(NED) then begin
+ QueryURL = "http://" + base + "/nph-sesame/-o/N?" + $
+               strcompress(object,/remove)
+ endif else begin
+ QueryURL = "http://" + base + "/nph-sesame/-oI?" + $
+               strcompress(object,/remove)
+
+  endelse
+  ;;
+  if keyword_set(verbose) then print,queryURL
+  Result = webget(QueryURL)
+  found = 0
+  ;;
+   Result=Result.Text
+   if arg_present(server) then $ 
+        server = strmid(result[1],2,1)
+; look for J2000 coords
+  idx=where(strpos(Result, '%J ') ne -1,cnt)
+
+  if cnt GE 1 then begin
+      if cnt GT 1 then begin 
+          if ~keyword_set(SILENT) then $
+            message,/INF,'Warning - More than one match found for name '  + name
+          idx = idx[0]
+      endif 	
+      found=1   
+      ra = 0.0d & de = 0.0d
+      reads,strmid(Result[idx],2),ra,de
+
+      if N_params() GT 3 then begin 
+          
+          idx2= where(strpos(Result, '%I.0 ') ne -1,cnt)
+          if cnt GT 0 then id = strtrim(strmid(Result[idx2],4),2) else $
+            if ~keyword_set(SILENT) then $
+            message,'Warning - could not determine primary ID',/inf 
+      endif	    
+
+      ; Get V mag if present
+      vi = where(strpos(Result, '%M.V ') ne -1,vcnt)
+      if vcnt GE 1 then reads,strmid(Result[vi],4),vmag
+
+      ; Get J mag if present
+      ji = where(strpos(Result, '%M.J ') ne -1,jcnt)
+      if jcnt GE 1 then reads,strmid(Result[ji],4),jmag
+
+      ; Get H mag if present
+      hi = where(strpos(Result, '%M.H ') ne -1,hcnt)
+      if hcnt GE 1 then reads,strmid(Result[hi],4),hmag
+
+      ; Get K mag if present
+      ki = where(strpos(Result, '%M.K ') ne -1,kcnt)
+      if kcnt GE 1 then reads,strmid(Result[ki],4),kmag
+
+      ; Get parallax if present
+      plxi = where(strpos(Result, '%X ') ne -1,plxcnt)
+      if plxcnt GE 1 then reads,strmid(Result[plxi],2),parallax
+      
+
+  ENDIF ELSE BEGIN 
+      errmsg = ['No objects returned by SIMBAD.   The server answered:' , $
+                 strjoin(result)]
+      if printerr then begin
+         message, errmsg[0], /info	
+	 message,strjoin(result),/info
+      endif	 
+  ENDELSE
+  if found GT 0 && ((N_params() LT 2) || keyword_set(print)) then $
+       print,adstring(ra,de,1)
+        
+  
+  return 
+END 
+  
diff --git a/Code/script_idl_mv/astrolib/queryvizier.pro b/Code/script_idl_mv/astrolib/queryvizier.pro
new file mode 100644
index 0000000000000000000000000000000000000000..675e65b02ef8dc4844614a63bbf51da53680d329
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/queryvizier.pro
@@ -0,0 +1,348 @@
+function Queryvizier, catalog, target, dis, VERBOSE=verbose, CANADA = canada, $
+               CONSTRAINT = constraint, ALLCOLUMNS=allcolumns, SILENT=silent, $
+	       CFA = CFA
+;+
+; NAME: 
+;   QUERYVIZIER
+;
+; PURPOSE: 
+;   Query any catalog in the Vizier database by position
+; 
+; EXPLANATION:
+;   Uses the IDL SOCKET command to provide a positional query of any catalog 
+;   in the the Vizier (http://vizier.u-strasbg.fr/) database over the Web and
+;   return results in an IDL structure.    
+; 
+;    
+; CALLING SEQUENCE: 
+;     info = QueryVizier(catalog, targetname_or_coords, [ dis
+;                        /ALLCOLUMNS, /CFA, CONSTRAINT= ,/VERBOSE ])
+;
+; INPUTS: 
+;      CATALOG - Scalar string giving the name of the VIZIER catalog to be
+;            searched.    The complete list of catalog names is available at
+;            http://vizier.u-strasbg.fr/vizier/cats/U.htx . 
+;
+;            Popular VIZIER catalogs include 
+;            'II/328'- AllWISE Data Release (Cutri+ 2013)
+;            'V/139' - Sloan SDSS photometric catalog Release 9 (2012)
+;            '2MASS-PSC' - 2MASS point source catalog (2003)
+;            'GSC2.3' - Version 2.3.2 of the HST Guide Star Catalog (2006)
+;            'USNO-B1' - Verson B1 of the US Naval Observatory catalog (2003)
+;            'UCAC4'  - 4th U.S. Naval Observatory CCD Astrograph Catalog (2012)
+;            'B/DENIS/DENIS' - 2nd Deep Near Infrared Survey of southern Sky (2005)
+;            'I/259/TYC2' - Tycho-2 main catalog (2000)
+;            'I/311/HIP2' - Hipparcos main catalog, new reduction (2007)
+;
+;          Note that some names will prompt a search of multiple catalogs
+;          and QUERYVIZIER will only return the result of the first search.
+;          Thus, setting catalog to "HIP2" will search all catalogs 
+;          associated with the Hipparcos mission, and return results for the
+;          first catalog found.    To specifically search the Hipparcos or
+;          Tycho main catalogs use the VIZIER catalog names listed above
+;                             
+;      TARGETNAME_OR_COORDS - Either a scalar string giving a target name, 
+;          (with J2000 coordinates determined by SIMBAD), or a 2-element
+;          numeric vector giving the J2000 right ascension in *degrees* and 
+;          the target declination in degrees.
+;          If the targetname is set to 'NONE' then QUERYVIZIER will perform
+;          an all-sky search using the constraints given in the CONSTRAINT
+;          keyword.   
+; OPTIONAL INPUT:
+;    dis - scalar or 2-element vector.   If one value is supplied then this
+;          is the search radius in arcminutes.     If two values are supplied
+;          then this is the width (i.e., in longitude direction) and height
+;          of the search box.   Default is a radius search with radius of
+;          5 arcminutes
+;
+; OUTPUTS: 
+;   info - Anonymous IDL structure containing information on the catalog  
+;          sources within the specified distance of the specified center.  The 
+;          structure tag names are identical with the VIZIER  catalog column 
+;          names, with the exception of an occasional underscore
+;          addition, if necessary to convert the column name to a valid 
+;          structure tag.    The VIZIER Web  page should consulted for the 
+;          column names and their meaning for each particular catalog..
+;           
+;          If the tagname is numeric and the catalog field is blank then either
+;          NaN  (if floating) or -1 (if integer) is placed in the tag.
+;
+;          If no sources are found within the specified radius, or an
+;          error occurs in the query then -1 is returned. 
+; OPTIONAL KEYWORDS:
+;          /ALLCOLUMNS - if set, then all columns for the catalog are returned
+;                 The default is to return a smaller VIZIER default set. 
+;
+;          /CANADA - obsolete, the Canadian Vizier site no longer seems 
+;                  supported.
+;
+;          /CFA - By default, the query is sent to the main VIZIER site in
+;            Strasbourg, France.   If /CFA is set then the VIZIER site
+;            at the Harvard Center for Astrophysics (CFA) is used instead.
+;            Note that not all Vizier sites have the option to return
+;            tab-separated values (TSV) which is required by this program.
+;   
+;          CONSTRAINT - string giving additional nonpositional numeric 
+;            constraints on the entries to be selected.     For example, when 
+;            in the GSC2.3  catalog, to only select sources with Rmag < 16 set 
+;            Constraint = 'Rmag<16'.    Multiple constraints can be 
+;            separated by commas.    Use '!=' for "not equal", '<=' for smaller
+;            or equal, ">=" for greater than or equal.  See the complete list
+;            of operators at  
+;                 http://vizier.u-strasbg.fr/doc/asu.html#AnnexQual
+;            For this keyword only, **THE COLUMN NAME IS CASE SENSITIVE** and 
+;            must be written exactly as displayed on the VIZIER Web page.  
+;            Thus for the GSC2.3 catalog one must use 'Rmag' and not 'rmag' or
+;            'RMAG'.    In addition, *DO NOT INCLUDE ANY BLANK SPACE* unless it 
+;            is a necessary part of the query.
+;         
+;           /SILENT - If set, then no message will be displayed if no sources
+;                are found.    Error messages are still displayed.
+;           /VERBOSE - If set then the query sent to the VIZIER site is
+;               displayed, along with the returned title(s) of found catalog(s)
+; EXAMPLES: 
+;          (1) Plot a histogram of the J magnitudes of all 2MASS point sources 
+;          stars within 10 arcminutes of the center of the globular cluster M13 
+;
+;          IDL>  info = queryvizier('2MASS-PSC','m13',10)
+;          IDL> plothist,info.jmag,xran=[10,20]
+;
+;          (2)  Find the brightest J mag GSC2.3 source within 3' of the 
+;               J2000 position ra = 10:12:34, dec = -23:34:35
+;          
+;          IDL> str = queryvizier('GSC2.3',[ten(10,12,34)*15,ten(-23,34,35)],3)
+;          IDL> print,min(str.jmag,/NAN)
+;
+;          (3) Find sources with V < 19 in the Magellanic Clouds Photometric 
+;              Survey (Zaritsky+, 2002) within 5 arc minutes of  the position 
+;              00:47:34 -73:06:27
+;
+;              Checking the VIZIER Web page we find that this catalog is
+;          IDL>  catname =  'J/AJ/123/855/table1'
+;          IDL>  ra = ten(0,47,34)*15 & dec = ten(-73,6,27)
+;          IDL> str = queryvizier(catname, [ra,dec], 5, constra='Vmag<19')
+;
+;          (4) Perform an all-sky search of the Tycho-2 catalog for stars with
+;              BTmag = 13+/-0.1
+;
+;         IDL> str = queryvizier('I/259/TYC2','NONE',constrain='BTmag=13+/-0.1')
+;
+; PROCEDURES USED:
+;          GETTOK(), REMCHAR, REPSTR(), STRCOMPRESS2(), WEBGET()
+; TO DO:
+;       (1) Allow specification of output sorting
+; MODIFICATION HISTORY: 
+;         Written by W. Landsman  SSAI  October 2003
+;         Give structure name returned by VIZIER not that given by user
+;                    W. Landsman   February 2004 
+;         Don't assume same format for all found sources W. L. March 2004
+;         Added CONSTRAINT keyword for non-positional constraints WL July 2004
+;         Remove use of EXECUTE() statement WL June 2005
+;         Make dis optional as advertised WL August 2005
+;         Update for change in Vizier output format WL February 2006
+;         Fix problem in Feb 2006 update when only 1 object found
+;                     WL/D.Apai        March 2006
+;         Accept 'E' format for floating point. M. Perrin April 2006
+;         Added /ALLCOLUMNS option to return even more data.  M. Perrin, May 2006
+;         Return anonymous structure W. Landsman  May 2006
+;         Removed V6.0 notation to restore V5 compatibility W.Landsman July2006
+;         Accept target='NONE' for all-sky search, allow '+/-' constraints
+;                W. Landsman  October 2006
+;         Use HTTP 1.0 protocol in call to webget.pro
+;         Use vector form of IDL_VALIDNAME if V6.4 or later W.L. Dec 2007
+;         Update Strasbourg Web address for target name W.L. 3 March 2008
+;         Also update Web address for coordinate search W.L. 7 March 2008 
+;         Allow for 'D' specification format  R. Gutermuth/W.L.  June 2008
+;         Allow for possible lower-case returned formats  W.L. July 2008
+;         Use STRCOMPRESS2()to remove blanks around operators in constraint
+;              string  W.L.  August 2008
+;         Added /SILENT keyword  W.L.  Jan 2009
+;         Avoid error if output columns but not data returned W.L. Mar 2010
+;         Ignore vector tags (e.g. SED spectra) W.L.   April 2011
+;         Better checking when more than one catalog returned W.L. June 2012
+;         Assume since IDL V6.4 W.L. Aug 2013
+;         Update HTTP syntax for /CANADA    W. L.  Feb 2014
+;         Add CFA keyword, remove /CANADA keyword  W.L. Oct 2014
+;-
+  On_error,2
+  compile_opt idl2
+  if N_params() LT 2 then begin
+       print,'Syntax - info = QueryVizier(catalog, targetname_or_coord, dis,'
+       print,'         [/ALLCOLUMNS, /SILENT, /VERBOSE, /CFA, CONSTRAINT= ]'
+       print,'                       '
+       print,'  Coordinates (if supplied) should be J2000 RA (degrees) and Dec'
+       print,'  dis -- search radius or box in arcminutes'
+       if N_elements(info) GT 0 then return,info else return, -1
+  endif
+
+ if keyword_set(CFA) then root = "http://vizier.hia.nrc.ca/viz-bin/" $
+                       else  root = "http://webviz.u-strasbg.fr/viz-bin/" 
+ silent = keyword_set(silent)
+ 
+  if N_elements(catalog) EQ 0 then $
+            message,'ERROR - A catalog name must be supplied as a keyword'
+  targname = 0b
+ if N_elements(dis) EQ 0 then dis = 5
+ if min(dis) LE 0 then $
+     message,'ERROR - Search distances must be greater than zero'
+ 
+ nopoint = 0b
+ if N_elements(dis) EQ 2 then $
+    search = "&-c.bm=" + strtrim(dis[0],2) + '/' + strtrim(dis[1],2) else $
+    search = "&-c.rm=" + strtrim(dis,2) 
+    if N_elements(target) EQ 2 then begin
+      ra = float(target[0])
+      dec = float(target[1])
+   endif else begin
+       nopoint = strupcase( strtrim(target,2) ) EQ 'NONE' 
+       object = repstr(target,'+','%2B')
+        object = repstr(strcompress(object),' ','+')
+       targname = 1b 
+  endelse
+
+; Add any additional constraints to the search. Convert any URL special 
+; special characters in the constraint string.
+
+ if N_elements(constraint) EQ 0 then constraint = '' 
+ if strlen(constraint) GT 0 then begin
+     urlconstrain = strtrim(constraint,2)
+     urlconstrain = strcompress2(constraint,['<','>','='])
+      urlconstrain = repstr(urlconstrain, ',','&')
+     urlconstrain = repstr(urlconstrain, '<','=%3C')
+     urlconstrain = repstr(urlconstrain, '>','=%3E')
+     urlconstrain = repstr(urlconstrain, '+','%2B')
+     urlconstrain = repstr(urlconstrain, '/','%2F')
+     urlconstrain = repstr(urlconstrain, '!','=!')
+     if nopoint then search = urlconstrain else $
+                     search = search + '&' + urlconstrain
+ endif
+ ;
+ if nopoint then $
+  QueryURL = root + "asu-tsv/?-source=" + catalog + '&' + $
+              search + '&-out.max=unlimited' else $
+ if targname then $
+  QueryURL = $
+   root + "asu-tsv/?-source=" + catalog + $
+     "&-c=" + object + search + '&-out.max=unlimited' else $
+  queryURL = $
+   root + "asu-tsv/?-source=" + catalog + $
+       "&-c.ra=" + strtrim(ra,2) + '&-c.dec=' + strtrim(dec,2) + $
+       search + '&-out.max=unlimited'
+
+  if keyword_set(allcolumns) then queryURL = queryURL + '&-out.all=1'
+ if keyword_set(verbose) then message,queryurl,/inf
+
+  Result = webget(QueryURL,/http10, silent=silent)
+;
+  t = strtrim(result.text,2)
+  keyword = strtrim(strmid(t,0,7),2)
+
+  linecon = where(keyword EQ '#---Lis', Ncon)
+  if Ncon GT 0 then remove,linecon, t, keyword
+ 
+; Check to see if more than one catalog has been searched
+; Use only the first catalog found
+
+  rcol = where(keyword Eq '#RESOUR', Nfound) 
+  if N_elements(rcol) GT 1 then begin 
+       t = t[0:rcol[1]-1 ]
+       keyword = keyword[0:rcol[1]-1]
+  endif     
+  lcol = where(keyword EQ "#Column", Nfound)
+  if Nfound EQ 0 then begin
+       if max(strpos(strlowcase(t),'errors')) GE 0 then begin 
+            message,'ERROR - Unsuccessful VIZIER query',/CON 
+            print,t
+       endif else if ~silent then $
+            message,'No sources found within specified radius',/INF
+       return,-1
+  endif
+  
+
+  if keyword_set(verbose) then begin
+    titcol = where(keyword EQ '#Title:', Ntit)
+        if Ntit GT 0 then message,/inform, $
+        strtrim(strmid(t[titcol[0]],8),2)
+  endif
+;Check if any Warnings or fatal errors in the VIZIER output
+   badflag = strmid(keyword,0,5)
+   warn = where(badflag EQ '#++++', Nwarn)
+   if Nwarn GT 0 then for i=0,Nwarn-1 do $
+        message,'Warning: ' + strtrim(t[warn[i]],2),/info
+   
+   fatal = where(badflag EQ '#****', Nfatal)
+   if Nfatal GT 0 then for i=0,Nfatal-1 do $
+        message,'Error: ' + strtrim(t[fatal[i]],2),/info
+
+
+  trow = t[lcol]
+  dum = gettok(trow,' ')
+  colname = gettok(trow,' ')
+  fmt = gettok(trow,' ')
+
+  remchar,fmt,'('
+  remchar,fmt,')' 
+  remchar,colname,')'
+  colname = IDL_VALIDNAME(colname,/convert_all)
+ 
+; Find the vector tags (Format begins with a number) and remove them 
+
+ bad = where(stregex(fmt,'^[0-9]') GE 0, Nbad)
+ if Nbad GT 0 then remove,bad,fmt,colname 
+ 
+ ntag = N_elements(colname)
+ fmt = strupcase(fmt)
+ val = fix(strmid(fmt,1,4))
+ 
+ for i=0,Ntag-1 do begin
+
+ case strmid(fmt[i],0,1) of 
+ 
+  'A': cval = ' '
+  'I': cval = (val[i] LE 4) ? 0 : 0L         ;16 bit integer if 4 chars or less
+  'F': cval = (val[i] LE 7) ? 0. : 0.0d      ;floating point if 7 chars or less
+   'E': cval = (val[i] LE 7) ? 0. : 0.0d 
+  'D': cval = (val[i] LE 7) ? 0. : 0.0d 
+   else: message,'ERROR - unrecognized format ' + fmt[i]
+ 
+  endcase
+
+   if i EQ 0 then   info = create_struct(colname[0], cval) else begin
+	   ; If you set the /ALLCOLUMNS flag, in some cases (2MASS) you
+	   ; get a duplicate column name. Check for this and avoid it by appending
+	   ; an extra bit to the duplicate name
+	   if where(tag_names(info) eq strupcase(colname[i])) ge 0 then $
+	      colname[i] = colname[i] + '_2'
+   info =  create_struct(temporary(info), colname[i],cval)
+   endelse
+ endfor
+ 
+  i0 = max(lcol) + 4  
+  if i0 GT (N_elements(t)-1) then begin 
+       message,'No sources found within specified radius',/INF
+       return,-1
+  endif
+  
+  iend = where( t[i0:*] EQ '', Nend)
+  if Nend EQ  0  then iend = N_elements(t) else iend = iend[0] + i0
+  nstar = iend - i0 
+  info = replicate(info, nstar)
+
+; Find positions of tab characters 
+  t = t[i0:iend-1]
+
+  for j=0,Ntag-1 do begin
+      x = strtrim( gettok(t,string(9b),/exact ),2)
+       dtype = size(info[0].(j),/type)
+       if dtype NE 7 then begin
+       bad = where(strlen(x) EQ 0, Nbad)
+      if (Nbad GT 0) then $
+           if (dtype EQ 4) || (dtype EQ 5) then x[bad] = 'NaN' $
+                                           else x[bad] = -1
+      endif
+      info.(j) = x 
+   endfor
+ return,info
+END 
+  
+
diff --git a/Code/script_idl_mv/astrolib/radec.pro b/Code/script_idl_mv/astrolib/radec.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ebad23581d89257269646ddb246a7671e7031b68
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/radec.pro
@@ -0,0 +1,75 @@
+pro radec,ra,dec,ihr,imin,xsec,ideg,imn,xsc, hours = hours
+;+
+; NAME:
+;	RADEC
+; PURPOSE:
+;	To convert RA and Dec  from decimal to sexagesimal units.
+; EXPLANATION: 
+;	The conversion is to sexagesimal hours for RA,  and sexagesimal 
+;	degrees for declination.
+;
+; CALLING SEQUENCE:
+;	radec, ra, dec, ihr, imin, xsec, ideg, imn, xsc, [/HOURS}
+;
+; INPUTS:
+;	ra   - Right ascension, scalar or vector, in DEGREES unless the
+;              /HOURS keyword is set
+;	dec  - declination in decimal DEGREES, scalar or vector, same number
+;		of elements as RA
+;
+; OUTPUTS:
+;	ihr  - right ascension hours   (INTEGER*2)
+;	imin - right ascension minutes (INTEGER*2)
+;	xsec - right ascension seconds  (REAL*4 or REAL*8)
+;	ideg - declination degrees (INTEGER*2)
+;	imn  - declination minutes (INTEGER*2)
+;	xsc  - declination seconds (REAL*4 or REAL*8)
+;
+; OPTIONAL KEYWORD INPUT:
+;       /HOURS - if set, then the input righ ascension should be specified in
+;              hours instead of degrees.
+; RESTRICTIONS:
+;	RADEC does minimal parameter checking.
+;
+; REVISON HISTORY:
+;	Written by B. Pfarr, STX, 4/24/87
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Added /HOURS keyword W. Landsman  August 2002
+;-
+  On_error,2
+
+  if (N_params() LT 2 ) then begin
+    print,'Syntax - radec, ra, dec, ihr, imin, xsec, ideg, imn, xsc'
+    return
+  endif
+  
+;    Compute RA
+  if keyword_set(hours) then begin
+      ra = ra mod 24.
+      ra = ra + 24*(ra lt 0)
+      ihr = fix(ra)
+      xmin = abs(ra*60. - ihr*60.)
+  endif else begin
+      ra = ra mod 360.          ;Make sure between 0 and 24 hours
+      ra = ra + 360*(ra lt 0)
+      ihr = fix(ra/15.)
+      xmin =abs(ra*4.0-ihr*60.0)
+  endelse 
+      imin = fix(xmin)
+      xsec = (xmin-imin)*60.0
+
+;    Compute Dec
+
+  ideg = fix(dec)
+  xmn = abs(dec-ideg)*60.0
+  imn = fix(xmn)
+  xsc = (xmn-imn)*60.0
+
+; Now test for the special case of zero degrees
+
+  zero_deg = ( ideg EQ 0 ) and (dec LT 0)
+  imn = imn - 2*imn*fix( zero_deg*(imn NE 0) )
+  xsc = xsc - 2*xsc*zero_deg*(imn EQ 0)
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/randomchi.pro b/Code/script_idl_mv/astrolib/randomchi.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6c79d362e0599b8fa0438771a98d60a593ba1b52
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/randomchi.pro
@@ -0,0 +1,36 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;+
+;   NAME:
+;      RANDOMCHI
+; PURPOSE:
+;      GENERATE CHI-SQUARE DISTRIBUTED RANDOM VARIABLES.
+;
+; AUTHOR : BRANDON C. KELLY, STEWARD OBS., SEP 2005
+;
+; INPUTS :
+;
+;   SEED - THE SEED FOR THE RANDOM NUMBER GENERATOR, CAN BE UNDEFINED.
+;   DOF - THE DEGREES OF FREEDOM FOR THE CHI-SQUARED DISTRIBUTION.
+;
+; OPTIONAL INPUTS :
+;
+;   NRAND - THE NUMBER OF RANDOM NUMBERS TO DRAW
+;-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function randomchi, seed, dof, nrand
+
+if n_params() lt 2 then begin
+    print, 'Syntax- result = randomchi( seed, dof[, nrand] )'
+    return, -1
+endif
+
+if n_elements(nrand) eq 0 then nrand = 1
+
+alpha = dof / 2.0
+beta = 0.5
+
+chisqr = randomgam( seed, alpha, beta, nrand )
+
+return, chisqr
+end
diff --git a/Code/script_idl_mv/astrolib/randomdir.pro b/Code/script_idl_mv/astrolib/randomdir.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f1b6e054fe678d0b6dca3f0cf1ded9278e036ab2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/randomdir.pro
@@ -0,0 +1,56 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;+
+;  NAME:
+;     RANDOMDIR
+; PURPOSE:
+;     GENERATE DIRICHLET-DISTRIBUTED RANDOM VARIABLES.
+;
+; AUTHOR : BRANDON C. KELLY, STEWARD OBS., APRIL 2006
+;
+; INPUTS :
+;
+;   SEED - THE SEED FOR THE RANDOM NUMBER GENERATOR, CAN BE UNDEFINED.
+;   ALPHA - THE SHAPE PARAMETERS FOR THE DIRICHLET DISTRIBUTION. THIS
+;           SHOULD BE A K-ELEMENT VECTOR.
+;
+; OPTIONAL INPUTS :
+;
+;   NRAND - THE NUMBER OF RANDOM NUMBERS TO DRAW
+;
+; CALLED ROUTINES :
+;
+;   RANDOMGAM
+;-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function randomdir, seed, alpha, nrand
+
+if n_params() lt 2 then begin
+    print, 'Syntax- theta = randomdir( seed, alpha[, nrand] )'
+    return, 0
+endif
+
+if n_elements(alpha) lt 2 then begin
+    print, 'Alpha must have at least 2 elements.'
+    return, 0
+endif
+
+K = n_elements(alpha)
+
+bad = where(alpha le 0, nbad)
+if nbad ne 0 then begin
+    print, 'All elements of ALPHA must be greater than 0.'
+    return, 0
+endif
+
+if n_elements(nrand) eq 0 then nrand = 1
+
+gamma = dblarr(nrand, K)
+
+for j = 0, K - 1 do $
+  gamma[0,j] = randomgam(seed, alpha[j], 1.0, nrand)
+
+theta = gamma / transpose(total(gamma,2) ## replicate(1, K))
+
+return, theta
+end
diff --git a/Code/script_idl_mv/astrolib/randomgam.pro b/Code/script_idl_mv/astrolib/randomgam.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5a76873f4531c3c1f38bad5ff08aee9da775e7d0
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/randomgam.pro
@@ -0,0 +1,88 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;+
+; NAME:
+;     RANDOMGAM
+; PURPOSE:
+;     GENERATE GAMMA-DISTRIBUTED RANDOM VARIABLES.
+;
+; AUTHOR : BRANDON C. KELLY, STEWARD OBS., APRIL 2006
+;
+; INPUTS :
+;
+;   SEED - THE SEED FOR THE RANDOM NUMBER GENERATOR, CAN BE UNDEFINED.
+;   ALPHA, BETA - THE SHAPE PARAMETERS FOR THE GAMMA DISTRIBUTION.
+;
+; OPTIONAL INPUTS :
+;
+;   NRAND - THE NUMBER OF RANDOM NUMBERS TO DRAW
+;-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function randomgam, seed, alpha, beta, nrand
+
+if n_params() lt 3 then begin
+    print, 'Syntax- X = randomgam( seed, alpha, beta[, nrand] )'
+    return, 0
+endif
+
+if alpha le 0 or beta le 0 then begin
+    print, 'ALPHA and BETA must both be greater than zero.'
+    return, 0
+endif
+
+if n_elements(nrand) eq 0 then nrand = 1
+
+if alpha le 1 then begin
+
+    alpha = alpha + 1
+    alfshift = 1
+
+endif else alfshift = 0
+
+d = alpha - 1d / 3
+c = 1 / sqrt(9 * d)
+
+gamma = dblarr(nrand)
+
+nempty = nrand
+empty = lindgen(nrand)
+
+repeat begin
+    
+    x = randomn(seed, nempty)
+    v = 1 + c * x
+    
+    bad = where(v le 0, nbad)
+    while nbad gt 0 do begin
+        
+        x2 = randomn(seed, nbad)
+        x[bad] = x2
+        v[bad] = 1 + c * x2
+        bad2 = where(v[bad] le 0, nbad2)
+        if nbad2 gt 0 then bad = bad[bad2]
+        nbad = bad2
+        
+    endwhile
+    
+    v = v^3
+
+    unif = randomu(seed, nempty)
+    factor = 0.5 * x^2 + d - d * v + d * alog(v)
+    u = where( alog(unif) lt factor, nu, comp=empty1 )
+
+    if nu gt 0 then gamma[empty[u]] = d * v[u]
+    nempty = nempty - nu
+
+    if nempty ne 0 then empty = empty[empty1]
+
+endrep until nempty eq 0
+
+if alfshift then begin
+    alpha = alpha - 1
+    gamma = gamma * (randomu(seed, nrand))^(1d / alpha)
+endif
+
+gamma = gamma / beta
+
+return, gamma
+end
diff --git a/Code/script_idl_mv/astrolib/randomp.pro b/Code/script_idl_mv/astrolib/randomp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1587d090467b4e71985177f93e8634ee1f196fc5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/randomp.pro
@@ -0,0 +1,83 @@
+pro randomp,x,pow,n,range_x=range_x,seed=s
+;+
+; NAME:
+;       RANDOMP
+; PURPOSE:
+;       Generates an array of random numbers distributed as a power law.
+; CALLING SEQUENCE:
+;       RANDOMP, X, Pow, N, [ RANGE_X = [low,high], SEED= ]'
+; INPUTS:
+;       Pow:  Exponent of power law.
+;               The pdf of X is f_X(x) = A*x^pow, low <= x <= high
+;               ASTRONOMERS PLEASE NOTE:  
+;               pow is little gamma  = big gamma - 1 for stellar IMFs.
+;       N:    Number of elements in generated vector.
+;
+; OPTIONAL INPUT KEYWORD PARAMETER:
+;       RANGE_X:  2-element vector [low,high] specifying the range of 
+;               output X values; the default is [5, 100].
+;
+; OPTIONAL INPUT-OUTPUT KEYWORD PARAMETER:
+;       SEED:    Seed value for RANDOMU function.    As described in the 
+;               documentation for RANDOMU, the value of SEED is updated on 
+;               each call to RANDOMP, and taken from the system clock if not
+;               supplied.   This keyword can be used to have RANDOMP give 
+;               identical results on different runs.
+; OUTPUTS:
+;       X:    Vector of random numbers, distributed as a power law between
+;               specified range
+; PROCEDURE:  
+;       "Transformation Method" for random variables is described in Bevington 
+;       & Robinson, "Data Reduction & Error Analysis for Physical Sciences", 2nd
+;       Edition (McGraw-Hill, 1992). p. 83.
+;       Output of RANDOMU function is transformed to power-law
+;       random variable.
+;
+; EXAMPLE:
+;       Create a stellar initial mass function (IMF) with 10000 stars
+;       ranging from 0.5 to 100 solar masses and a Salpeter slope.  Enter:
+;
+;       RANDOMP,MASS,-2.35,10000,RANGE_X=[0.5,100]
+;
+; NOTES:
+;       Versions 5.1.1 and V5.2 of IDL have a bug in RANDOMU such that the SEED
+;       value is initialized to the same value at the start of each session,
+;       rather than being initialized by the system clock.    RANDOMP will be
+;       affected in a similar manner.
+; MODIFICATION HISTORY:
+;       Written by R. S. Hill, Hughes STX, July 13, 1995
+;       July 14, 1995   SEED keyword added at Landsman's suggestion.
+;                    Documentation converted to standard format.  RSH
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+
+ if N_params() LT 3 then begin
+   print,'Syntax - RANDOMP, x, pow, n, [ RANGE_X = [low,high], SEED= ]'
+   return
+ endif
+
+ if N_elements(range_x) lt 1 then range_x=[5,100]
+ if N_elements(range_x) ne 2 then begin
+   message,'Error - RANGE_X keyword must be a 2 element vector',/CON
+   return
+ endif
+
+ pow1 = pow + 1.0
+ lo = range_x[0] & hi = range_x[1]
+ if lo GT hi then begin
+  temp=lo & lo=hi & hi=tmp
+ endif
+
+ r = randomu(s, n )
+ if pow NE -1.0 then begin
+   norm = 1.0d0/(hi^pow1 - lo^pow1)
+   expo = alog10(r/norm + lo^pow1)/pow1
+   x = 10.0^expo
+ endif else begin
+   norm = 1.0d0/(alog(hi) - alog(lo))
+   x = exp(r/norm + alog(lo))
+ endelse
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/randomwish.pro b/Code/script_idl_mv/astrolib/randomwish.pro
new file mode 100644
index 0000000000000000000000000000000000000000..caf104b89e6be0e2423da9c89fde47c269d35c47
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/randomwish.pro
@@ -0,0 +1,56 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;+
+; NAME:
+;    RANDOMWISH
+; PURUPOSE:
+; ROUTINE TO DRAW RANDOM MATRICES FROM A WISHART DISTRIBUTION WITH DOF
+; DEGREES OF FREEDOM AND SCALE MATRIX S.
+;
+; AUTHOR : BRANDON C. KELLY, STEWARD OBS., JULY 2006
+;
+; INPUTS :
+;
+;   SEED - THE SEED FOR THE RANDOM NUMBER GENERATOR, CAN BE UNDEFINED.
+;   DOF - THE DEGREES OF FREEDOM FOR THE WISHART DISTRIBUTION.
+;   S - THE SCALE MATRIX. THE DIMENSION OF S CANNOT BE GREATER THAN
+;       DOF.
+;
+; OPTIONAL INPUTS :
+;
+;   NRAND - THE NUMBER OF RANDOM MATRICES TO DRAW
+;
+; CALLED ROUTINES :
+;
+;   MRANDOMN
+;-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+function randomwish, seed, dof, S, nrand
+
+if n_params() lt 3 then begin
+    print, 'Syntax- W = randomwish( seed, dof, S[, nrand] )'
+    return, 0
+endif
+
+dim = (size(S, /dim))[0]
+
+if dim gt dof then begin
+
+    print, 'Dimension of S cannot be larger than DOF.'
+    return, 0
+
+endif
+
+if n_elements(nrand) eq 0 then nrand = 1
+
+wish = dblarr(dim, dim, nrand)
+
+for i = 0, nrand - 1 do begin
+
+    x = mrandomn(seed, S, dof)
+    wish[*,*,i] = x ## transpose(x)
+
+endfor
+
+return, reform(wish)
+end
diff --git a/Code/script_idl_mv/astrolib/rdfits_struct.pro b/Code/script_idl_mv/astrolib/rdfits_struct.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7400769041a1cb10e7cdaa058eb9e94ad45f26a0
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/rdfits_struct.pro
@@ -0,0 +1,121 @@
+pro rdfits_struct, filename, struct,SILENT = silent, HEADER_ONLY = header_only,$
+    EXTEN = exten 
+;+
+; NAME:
+;      RDFITS_STRUCT
+; PURPOSE:
+;      Read an entire FITS file (all extensions) into a single IDL structure. 
+; EXPLANATION:
+;      Each header, image or table array is placed in a separate structure 
+;      tag.
+;
+; CALLING SEQUENCE:
+;      RDFITS_STRUCT, filename, struct, /SILENT, /HEADER_ONLY, EXTEN= ]
+;
+; INPUT:
+;      FILENAME = Scalar string giving the name of the FITS file.  
+;                 One can also specify a gzip (.gz) compressed file 
+;
+; OPTIONAL KEYWORD: 
+;      /HEADER_ONLY - If set, then only the FITS headers (and not the data)
+;                are read into the structure.
+;      /SILENT - Set this keyword to suppress informational displays at the
+;               terminal.
+; OUTPUT:
+;      struct = structure into which FITS data is read.   The primary header
+;             and image are placed into tag names HDR0 and IM0.   The ith
+;             extension is placed into the tag names HDRi, and either TABi
+;             (if it is a binary or ASCII table) or IMi (if it is an image
+;             extension)
+;
+;             If /HEADER_ONLY is set, then struct will contain tags HDR0, HDR1
+;             ....HDRn containing all the headers of a FITS file with n 
+;             extensions
+; OPTIONAL INPUT KEYWORD:
+;       EXTEN - positive integer array specifying which extensions to read.
+;             Default is to read all extensions. 
+; PROCEDURES USED:
+;       FITS_OPEN, FITS_READ, FITS_CLOSE
+;
+; METHOD:
+;       The file is opened with FITS_OPEN which return information on the 
+;       number and type of each extension.    The CREATE_STRUCT() function
+;       is used iteratively, with FITS_READ calls to build the final structure.
+;
+; EXAMPLE:
+;       Read the FITS file 'm33.fits' into an IDL structure, st
+;
+;       IDL> rdfits_struct, 'm33.fits', st
+;       IDL> help, /str, st                   ;Display info about the structure
+;
+;       To just read the second and fourth extensions 
+;       IDL> rdfits_struct, 'm33.fits', st, exten=[2,4]
+; RESTRICTIONS:
+;       Does not handle random groups or variable length binary tables
+; MODIFICATION HISTORY:
+;       Written K. Venkatakrishna, STX April 1992
+;       Code cleaned up a bit  W. Landsman  STX  October 92
+;       Modified for MacOS     I.  Freedman  HSTX April 1994
+;       Work under Windows 95  W. Landsman   HSTX  January 1996
+;       Use anonymous structures, skip extensions without data WBL April 1998
+;       Converted to IDL V5.0, W. Landsman, April 1998
+;       OS-independent deletion of temporary file  W. Landsman  Jan 1999
+;       Major rewrite to use FITS_OPEN and CREATE_STRUCT() W. Landsman Sep 2002
+;       Added /HEADER_ONLY keyword   W. Landsman  October 2003
+;       Do not copy primary header into extension headers W. Landsman Dec 2004
+;       Do not modify NAXIS when using /HEADER_ONLY W. Landsman Jan 2005
+;       Added EXTEN keyword  W. Landsman July 2009
+;-
+
+ compile_opt idl2
+ if N_Params() LT 2 then begin 
+        print,'Syntax - RDFITS_STRUCT, file, struct, [ /SILENT, /HEADER_ONLY ]'
+        return
+ endif
+
+ fits_open, filename, fcb                ; Get the description of the file
+ if ~keyword_set(silent) then $
+      message,/inf,'Now reading file ' + filename + ' with ' + $
+      strtrim(fcb.nextend,2) + ' extensions'
+
+ h_only = keyword_set(header_only)  
+ if h_only then begin
+     fits_read,fcb,0,h,/header_only,exten_no=0
+     struct = {hdr0:h}
+ endif else begin
+     fits_read,fcb,d,h,exten_no=0
+     struct = {hdr0:h,im0:temporary(d)}
+ endelse
+
+ if fcb.nextend EQ 0 then begin
+      fits_close,fcb 
+      return
+ endif
+
+ n = N_elements(exten)
+ if N_elements(exten) EQ 0 then begin 
+      n = fcb.nextend 
+      exten = indgen(n)+1
+ endif else begin 
+      if max(exten) GT fcb.nextend then message, $
+          'ERROR - extension ' + strtrim(max(exten),2) + ' does not exist'     
+ endelse
+ for i= 0, n-1 do begin
+     j = exten[i]
+     jj  = strtrim(j,2)
+     if h_only then begin
+     fits_read,fcb,0,h,/header_only,/no_pdu,exten=j
+     struct = create_struct(temporary(struct), 'hdr' + jj, $
+              temporary(h))
+     endif else begin
+     fits_read,fcb,d,h,/no_pdu,exten=j
+     if fcb.xtension[j] EQ 'IMAGE' then tag = 'im' + jj $
+                                else tag = 'tab' + jj
+     struct = create_struct(temporary(struct), 'hdr' + jj, $
+              temporary(h),tag, temporary(d))
+    endelse
+ endfor
+     
+ fits_close,fcb                             
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/rdfloat.pro b/Code/script_idl_mv/astrolib/rdfloat.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f4f22442c6a92f16d95bbaecb68c19f386d4ac28
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/rdfloat.pro
@@ -0,0 +1,152 @@
+pro rdfloat,name,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17, $
+            v18,v19,SKIPLINE = skipline, NUMLINE = numline,DOUBLE=double, $
+            SILENT = silent, COLUMNS = columns
+;+
+; NAME:
+;      RDFLOAT
+; PURPOSE:
+;      Quickly read a numeric ASCII data file into IDL floating/double vectors.
+; EXPLANATION:
+;      Columns of data may be separated by tabs or spaces.      This 
+;      program is fast but is restricted to data files where all columns can 
+;      be read as floating point (or all double precision).   
+;
+;      Use READCOL if  greater flexibility is desired.   Use READFMT to read a 
+;      fixed-format ASCII file.   Use FORPRINT to print columns of data.
+;
+; CALLING SEQUENCE:
+;      RDFLOAT, name, v1, [ v2, v3, v4, v5, ...  v19] 
+;                         COLUMNS, /DOUBLE, SKIPLINE = , NUMLINE = ]
+;
+; INPUTS:
+;      NAME - Name of ASCII data file, scalar string.  In VMS, an extension of 
+;              .DAT is assumed, if not supplied.
+;
+; OPTIONAL INPUT KEYWORDS:
+;      COLUMNS - Numeric scalar or vector specifying which columns in the file
+;               to read.    For example, if COLUMNS = [3,7,11] then the first
+;               output variable (v1) would contain column 3, the second would
+;               contain column 7 and the third would contain column 11.   If
+;               the number of elements in the COLUMNS vector is less than the
+;               number of output parameters, then consecutive columns are 
+;               implied.    For example, if 3 output parameters are supplied
+;               (v1,v2,v3) and COLUMNS = 3, then columns 3,4, and 5 will be
+;               read.   
+;      SKIPLINE - Integer scalar specifying number of lines to skip at the top
+;              of file before reading.   Default is to start at the first line.
+;      NUMLINE - Integer scalar specifying number of lines in the file to read.  
+;             Default is to read the entire file
+;      /DOUBLE - If this keyword is set, then all variables are read in as
+;              double precision.
+;      /SILENT - Set this keyword to suppress any informative messages
+;
+; OUTPUTS:
+;      V1,V2,V3,...V19 - IDL vectors to contain columns of data.
+;               Up to 19 columns may be read.  All output vectors are of type
+;               float, unless the /DOUBLE keyword is set, 
+;
+; EXAMPLES:
+;      Each row in a file 'position.dat' contains a star number and 6 columns
+;      of data giving an RA and Dec in sexagesimal format.   Read into IDL 
+;      variables.     
+;
+;       IDL> rdfloat,'position.dat',ID,hr,min,sec,deg,dmin,dsec  
+;
+;       All output vectors will be floating point.    To only read the 
+;       declination vectors (Deg,dmin,dsec)
+;
+;       IDL> rdfloat,'position.dat',deg,dmin,dsec,col=4
+;
+; RESTRICTIONS:
+;      (1) All rows in the file must be formatted identically (except for 
+;          those skipped by SKIPLINE).    RDFLOAT reads the first line of 
+;          the data (after SKIPLINE) to determine the number of columns of 
+;          data.
+;      (2) Cannot be used to read strings
+; PROCEDURES USED:
+;      None.
+; REVISION HISTORY:
+;      Written         W. Landsman                 September 1995
+;      Call NUMLINES() function                    February 1996
+;      Read up to 19 columns                       August 1997
+;      Converted to IDL V5.0   W. Landsman         September 1997
+;      Allow to skip more than 32767 lines  W. Landsman  June 2001
+;      Added /SILENT keyword   W. Landsman         March 2002
+;      Added COLUMNS keyword, use STRSPLIT    W. Landsman May 2002
+;      Use SKIP_LUN if V5.6 or later          W. Landsman Nov 2002
+;      V5.6 version, use FILE_LINES()         W. Landsman Dec 2002
+;-
+  On_error,2                           ;Return to caller
+
+  if N_params() lt 2 then begin
+     print,'Syntax - RDFLOAT, name, v1, [ v2, v3,...v19 '
+     print,'                    COLUMNS = ,/DOUBLE, SKIPLINE =, NUMLINE = ]'
+     return
+  endif
+
+; Get number of lines in file
+
+   nlines = FILE_LINES( name )
+   if nlines LE 0 then begin
+        message,'ERROR - File ' + name+' contains no data',/CON
+	return
+   endif     
+
+ 
+   if ~keyword_set( SKIPLINE ) then skipline = 0
+   nlines = nlines - skipline
+   if keyword_set( NUMLINE) then nlines = numline < nlines
+
+;Read first line, and determine number of columns of data
+
+   openr, lun, name, /GET_LUN
+   temp = ''
+   if skipline GT 0 then $
+        skip_lun, lun, skipline, /lines
+   readf,lun,temp
+   
+   
+   colval = strsplit(temp, count=ncol)         ;Determine number of columns
+ 
+;Create big output array and read entire file into the array
+
+   bigarr = keyword_set(DOUBLE) ? dblarr(ncol, nlines, /NOZERO):  $
+                                  fltarr(ncol, nlines, /NOZERO) 
+
+   close,lun
+   openr, lun, name
+   if skipline GT 0 then skip_lun, lun, skipline, /lines 
+
+   readf, lun, bigarr
+   free_lun, lun
+
+   if ~keyword_set(SILENT) then $
+       message, strtrim(nlines,2) + ' lines of data read',/INF
+
+   Nvector = (N_params()-1) < ncol
+   if N_elements(columns) EQ 0 then c = indgen(nvector) else c = columns - 1
+   Nc = N_elements(c)
+   if Nc LT nvector then c = [c,indgen(nvector-nc) + c[nc-1] +1 ] 
+   v1 = reform( bigarr[c[0],*])
+ 
+   if Nvector GT 1 then v2 = reform( bigarr[c[1],*]) else return
+   if Nvector GT 2 then v3 = reform( bigarr[c[2],*]) else return
+   if Nvector GT 3 then v4 = reform( bigarr[c[3],*]) else return
+   if Nvector GT 4 then v5 = reform( bigarr[c[4],*]) else return
+   if Nvector GT 5 then v6 = reform( bigarr[c[5],*]) else return
+   if Nvector GT 6 then v7 = reform( bigarr[c[6],*]) else return
+   if Nvector GT 7 then v8 = reform( bigarr[c[7],*]) else return
+   if Nvector GT 8 then v9 = reform( bigarr[c[8],*]) else return
+   if Nvector GT 9 then v10 = reform( bigarr[c[9],*]) else return
+   if Nvector GT 10 then v11 = reform( bigarr[c[10],*]) else return
+   if Nvector GT 11 then v12 = reform( bigarr[c[11],*]) else return
+   if Nvector GT 12 then v13 = reform( bigarr[c[12],*]) else return
+   if Nvector GT 13 then v14 = reform( bigarr[c[13],*]) else return
+   if Nvector GT 14 then v15 = reform( bigarr[c[14],*]) else return
+   if Nvector GT 15 then v16 = reform( bigarr[c[15],*]) else return
+   if Nvector GT 16 then v17 = reform( bigarr[c[16],*]) else return
+   if Nvector GT 17 then v18 = reform( bigarr[c[17],*]) else return
+   if Nvector GT 18 then v19 = reform( bigarr[c[18],*]) 
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/rdplot.pro b/Code/script_idl_mv/astrolib/rdplot.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d08bf05f780674ed2fa083f5b1c26d2d161f671a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/rdplot.pro
@@ -0,0 +1,671 @@
+pro RESET_RDPLOT
+;
+;   If the user crashes out of the RDPLOT program, they can call this procedure
+; to reset the graphics device functions to default values.
+;
+device, /CURSOR_CROSSHAIR, SET_GRAPHICS_FUNCTION=3, BYPASS_TRANSLATION=0
+end
+
+
+
+pro RDPLOT, x, y, WaitFlag, DATA=Data, DEVICE=Device, NORMAL=Normal, $
+   NOWAIT=NoWait, WAIT=Wait, DOWN=Down, CHANGE=Change, Err=Err, $
+   PRINT=Print, XTITLE=XTitle,YTITLE=YTitle, XVALUES=XValues,YVALUES=YValues, $
+   FULLCURSOR=FullCursor, NOCLIP=NoClip, LINESTYLE=Linestyle, THICK=Thick, $
+   COLOR=Color, BACKGROUND=BackGround, CROSS=Cross, ACCUMULATE=Accumulate, $
+   CURSOR_STANDARD=cursor_standard
+   
+;*******************************************************************************
+;+
+; NAME:
+;   RDPLOT
+;
+; PURPOSE:
+;   Like CURSOR but with a full-screen cursor and continuous readout option
+;
+; EXPLANATION:
+;   This program is designed to essentially mimic the IDL CURSOR command,
+;   but with the additional options of continuously printing out the data
+;   values of the cursor's position, and using a full-screen cursor rather 
+;   than a small cross cursor.  The full screen cursor uses OPLOT and 
+;   X-windows graphics masking to emulate the cursor.
+;      One difference is that IF the PRINT keyword is set but the DOWN,
+;   WAIT, CHANGE, or NOWAIT keywords are not set, then the leftmost mouse
+;   button will print a "newline" line-feed, but not exit.
+;
+;   Mac users may need to set their X windows preferences to (1) Emulate 3
+;    button mouse and (2) Click through inactive windows, to make cursor
+;    work properly.
+;
+; CALLING SEQUENCE:
+;   RDPLOT [, X, Y] [, WaitFlag] [, /DATA | /DEVICE | /NORMAL]
+;      [, /NOWAIT | /WAIT | /DOWN | /CHANGE] 
+;      [, /FULLCURSOR] [, /NOCLIP] [, /CROSS] [, /ACCUMULATE]
+;      [, ERR=, PRINT=, XTITLE=, YTITLE=, XVALUES=, YVALUES=
+;       , LINESTYLE=, THICK=, COLOR=, BACKGROUND=, CURSOR_STANDARD=]
+;
+; REQUIRED INPUTS:
+;   None.
+;
+; OPTIONAL INPUTS: 
+;   WAITFLAG = Uses the same table as the intrinsic CURSOR command, But note
+;	that unlike the CURSOR command, there is no UP keyword.
+;		WaitFlag=0 sets the NOWAIT keyword
+;		WaitFlag=1 sets the WAIT keyword {default}
+;		WaitFlag=2 sets the CHANGE keyword
+;		WaitFlag=3 sets the DOWN keyword
+;
+; OPTIONAL OUTPUTS:
+;    X - a named variable to receive the final cursor X position, scalar
+;        or vector (if /ACCUMULATE is set)
+;    Y - a named variable to receive the final cursor Y position, scalar
+;        or vector (if /ACCUMULATE is set)
+; OPTIONAL KEYWORD INPUT PARAMETERS:
+;   /DATA - data coordinates are displayed and returned.
+;   /DEVICE - device coordinates are displayed and returned.
+;   /NORMAL - normal coordinates are displayed and returned.
+;      Default is to use DATA coordinates if available (see notes).
+;   /NOWAIT = if non-zero the routine will immediately return the cursor's
+;      present position.
+;   /WAIT - if non-zero will wait for a mouse key click before returning.  If
+;      cursor key is already down, then procedure immediately exits.
+;   /DOWN - equivalent to WAIT *except* that if the mouse key is already down
+;      when the procedure is called, the procedure will wait until the mouse
+;      key is clicked down again.
+;   /CHANGE - returns when the mouse is moved OR a key is clicked up or down.
+;   PRINT = if non-zero will continuously print out (at the terminal) the data 
+;      values of the cursor's position.  If PRINT>1, program will printout a 
+;      brief header describing the mouse button functions.  However, note that 
+;      the button functions are overridden if any of the DOWN, WAIT, or
+;      CHANGE values are non-zero.
+;   XTITLE = label used to describe the values of the abscissa if PRINT>0.
+;   YTITLE = label used to describe the values of the ordinate if PRINT>0.
+;   XVALUES = a vector corresponding to the values to be printed when the
+;	PRINT keyword is set.  This allows the user the option of printing
+;	out other values rather than the default X coordinate position of
+;	the cursor.  E.g., if XVALUES is a string vector of dates such as
+;	['May 1', 'May 2', ...], then those dates will be printed rather than
+;	the X value of the cursor's position: if X=1 then 'May 2' would be
+;	printed, etc.  This requires that the values of the X coordinate read
+;	by the cursor must be positive (can't access negative elements).
+;       If XVALUES=-1, then NO values for X will be printed.
+;   YVALUES = analogous to the XVALUES keyword.
+;   /FULLCURSOR - if non-zero default cursor is blanked out and full-screen 
+;      (or full plot window, depending on the value of NOCLIP) lines are
+;      drawn; their intersecton is centered on the cursor position.
+;   /NOCLIP - if non-zero will make a full-screen cursor, otherwise it will
+;      default to the value in !P.NOCLIP.
+;   LINESTYLE = style of line that makes the full-screen cursor.
+;   THICK = thickness of the line that makes the full-screen cursor.
+;   COLOR = color of the full-screen cursor.
+;   BACKGROUND = color of the background of the plot device.  If this has
+;      been set to !P.BackGround, then this keyword is unnecessary.
+;   /CROSS = if non-zero will show the regular cross AND full screen cursors.
+;   /ACCUMULATE - all of the positions for which the left button was
+;      clicked are stored in the X and Y variables.  Has no effect if X and Y 
+;      are not present.
+;   CURSOR_STANDARD = this keyword can be used to select the cursor
+;      appearance if /CROSS is set and will set the cursor to this value
+;      when the full-screen cursor is turned off if /FULLCURSOR has been
+;      set. See IDL help for the DEVICE keyword CURSOR_STANDARD to see
+;      possible cursors for X Windows and MS Windows.  The default
+;      behavior, if this keyword is not set, is to set the cursor to the
+;      window system's default cursor, which might not be the user's
+;      preferred cursor.
+;
+; OPTIONAL KEYWORD OUTPUT PARAMETER:
+;   ERR = returns the most recent value of the !mouse.button value.
+;
+; NOTES:
+;   Note that this procedure does not allow the "UP" keyword/flag...which 
+;   doesn't seem to work too well in the origianl CURSOR version anyway.
+;   Note: this might have been the case back in the day, but Robishaw
+;   hasn't experienced any problems with CURSOR, /UP in the last 10
+;   years.  Even so, it would be somewhat tricky to implement the /UP
+;   behavior in this routine, which explains why it's still missing.
+;
+;   If a data coordinate system has not been established, then RDPLOT
+;   will create one identical to the device coordinate system.  Note that
+;   this kluge is required even if the user specified /NORMAL coordinates,
+;   since RDPLOT makes use of the OPLOT procedure.  This new data
+;   coordinate system is effectively "erased" (!X.CRange and !Y.CRange are
+;   both set to zero) upon exit of the routine so as to not change the plot
+;   status from the user's point of view.
+;
+;   Only tested on X-windows systems.  If this program is interrupted, the
+;   graphics function might be left in a non-standard state; in that case,
+;   run the program RESET_RDPLOT to return the standard graphics functions,
+;   or type the command:   DEVICE, /CURSOR_CROSS, SET_GRAPHICS=3, BYPASS=0
+;
+;   Robishaw added /ACCUMULATE keyword to pass back all the positions at
+;   which the mouse was left-clicked.  In addition, the value of the exit
+;   click is returned unless the cursor did not change position between the
+;   last left-click and the exit click.
+;
+;
+;
+; PROCEDURE:
+;   Basically is a bells-n-whistles version of the CURSOR procedure.  All
+;   the details are covered in the above discussion of the keywords.
+;
+; EXAMPLES:
+;   A silly, but informative one:
+;   Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', $
+;             'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+;   plot, indgen(12), xrange=[-5, 15]
+;   rdplot, /FULL, /PRINT, $
+;      XTITLE='Month: ', YTITLE='Y-value per month = ', $
+;      xvalues=Months
+;
+;   If your plot has a non-black background color, be sure to set either
+;   !p.background or the BACKGROUND keyword.  Here are examples of how to
+;   use a blue full-screen cursor on a plot with a red background and
+;   yellow axes and data. First, deal with color decomposition off:
+;   device, decomposed=0
+;   tvlct, [255,255,0], [0,255,0], [0,0,255], 1
+;   plot, randomn(seed,1024), XSTYLE=19, PSYM=3, COLOR=2, BACK=1
+;   rdplot, /PRINT, /FULL, THICK=5, /NOCLIP, BACK=1, COLOR=3
+;
+;   For decomposition on (TrueColor or DirectColor only):
+;   device, decomposed=1
+;   plot, randomn(seed,1024), XSTYLE=19, PSYM=3, COLOR=65535l, BACK=255l
+;   rdplot, /PRINT, /FULL, THICK=5, /NOCLIP, BACK=255l, COLOR=16711680l
+;
+; MODIFICATION HISTORY:
+;   Written (originally named CURFULL) by J.Wm.Parker  1993 Nov 22 
+;   Created data coordinates if not already present, W. Landsman Nov. 93
+;   Added continuous printout of data values, COLOR and FULLCURSOR keywords
+;      (so that default is that it acts just like the cursor command).
+;      Changed name from CURFULL to RDPLOT.   J.Wm.Parker  1994 Apr 20
+;   Modified (with some translation table assistance from the IDL support 
+;      group) to correctly plot the crosshair with the desired IDL 
+;      color using the device's translation table to determine the XOR 
+;      function and using the BYPASS function.  Added the RESET_RDPLOT
+;      procedure to cleanup crashes that might occur while running
+;      RDPLOT.  Other minor changes/bug fixes.  J.Wm.Parker  1994 May 21
+;   Modified DOWN, WAIT, CHANGE functions to behave more similar to the
+;      generic CURSOR procedure.   J.Wm.Parker  1995 April 24
+;   Added XVALUES, YVALUES keywords and cleanup.   J.Wm.Parker  1995 April 24
+;   Convert to IDL V5.0,  W. Landsman    July 1998
+;   Change !D.NCOLORS to !D.TABLE_SIZE for 24 bit displays W. Landsman May 2000
+;   Skip translation table for TrueColor visuals   W. Landsman  March 2001
+;   Fixed /FULLCURSOR ghosts. Fixed to properly deal with background colors
+;      in 24-bit visual classes (TrueColor and DirectColor).  Added
+;      BACKGROUND keyword. Tim Robishaw 2005 Jan 27       
+;   Added /ACCUMULATE keyword. T. Robishaw 2006 Nov 8
+;   Corrected following problems. When /CHANGE and /PRINT were set,
+;      returned X & Y were different than those printed.  When /PRINT and
+;      /NOWAIT were set, or /PRINT and /WAIT were set and the routine was
+;      entered with a mouse button clicked, nothing was printed. When
+;      /PRINT and /DOWN were set, if routine was started with button down,
+;      advertised behavior was that routine would exit on next down click;
+;      in practice if cursor was not moved, successive down clicks had no
+;      effect.  Now, if X is passed as an output variable, requires that Y
+;      is also passed, like CURSOR.  Bottom line is that RDPLOT now really
+;      does behave like CURSOR and when /PRINT is set, the values printed
+;      correspond to those returned in X & Y.  T. Robishaw 2006 Nov 12
+;   Fixed misbehavior when color decomposition was set to off for
+;      TrueColor and DirectColor.  Now thoroughly tested on PseudoColor
+;      displays as well as both decomposition states for TrueColor and
+;      DirectColor.  Also made the default cursor color white when
+;      decomposition is on (this has been its default value for
+;      decomposition off). T. Robishaw 2006 Nov 16
+;   Fixed misbehavior when /FULLCURSOR not set; was checking for
+;      non-existent variable VisualName. T. Robishaw 2007 Jul 01
+;   Added the CURSOR_STANDARD keyword because I hate how this routine
+;      changes my default cursor. Also, it was crashing when /FULL not set:
+;      small fix, now works. T. Robishaw  2007 Jul 03
+;   Fixed bug where moving mouse with button pressed or releasing button
+;      would return values even if DOWN was set. The checks for this were
+;      only being done if PRINT was set also. T.V. Wenger 2013 May 14
+;   Fix problem exiting when X,Y not supplied W. Landsman June 2013
+;-
+;*******************************************************************************
+On_error,2
+
+;;;
+;   If the device does not support windows, then this program can not be used.
+;
+if ((!D.Flags and 256) ne 256) then message, $
+  'ERROR - Current graphics device ' + !D.NAME + ' does not support windows'
+
+;;;
+;   Like cursor, require that if present, both X and Y be specified...
+;
+if (N_Params() eq 1) then message, $
+   'Incorrect number of arguments. Both X & Y must be present.'
+
+;;;
+;   Keywords, keywords.
+;
+if (N_Params() eq 3) then begin
+   case WaitFlag of
+      0 : NoWait = 1
+      1 : Wait = 1
+      2 : Change = 1
+      3 : Down = 1
+      else : Wait = 1
+   endcase
+endif
+
+NoWait = keyword_set(NoWait)
+Wait = keyword_set(Wait)
+Down = keyword_set(Down); or Wait
+Change = keyword_set(Change)
+FullCursor = keyword_set(FullCursor)
+
+;;;
+;   If plotting coordinates are not already established, and the NORMAL keyword
+; is not set, then use device coordinates.
+;   Note that even if this procedure was called with the DATA keyword set, that
+; the DEVICE keyword will always take precedence over the DATA keyword in the
+; cursor command.  However, if the NORMAL and DEVICE keywords are both set,
+; then very strange values are returned.
+;
+UndefinedPlot = ((!X.CRange[0] eq 0) and (!X.CRange[1] eq 0))
+if UndefinedPlot then plot, [0,!D.X_Size], [0,!D.Y_Size], /NODATA, $
+   XSTYLE=5, YSTYLE=5, XMARGIN=[0,0], YMARGIN=[0,0], /NOERASE
+
+;;;
+;   Initialize the !mouse.button variable.  The value of !mouse.button 
+; corresponds to the BYTE  value of the buttons on the mouse from left to right,
+; lowest bit first.  So, the left button gives !mouse.button = 1, next button 
+; gives !mouse.button = 2, then 4.
+;  Read in the cursor with no wait.  If the user does not want to wait, or if 
+; the DOWN or WAIT keywords are set AND the mouse key is depressed, then we're
+; done (I hate GOTO's, but it is appropriate here).
+; NOTE: Robishaw gets rid of GOTO statement... if user asks for value to be
+;       printed, it should be printed!
+;
+!mouse.button = 0
+cursor, X, Y, /NOWAIT, DATA=Data, DEVICE=Device, NORMAL=Normal
+;if (keyword_set(NoWait) or (Wait and (!mouse.button gt 0))) then $
+;            goto, LABEL_DONE
+;;;
+;   PRINTOUT SETUP SECTION ==================================================
+;;;
+
+;;;
+;   Is the PRINT keyword set?  Then we have a lot of things to set up.  First,
+; set up carriage return and line feed variables for the formatted printout,
+; and define the titles for the printed values.
+;
+if keyword_set(Print) then begin 
+   if not(keyword_set(XTitle)) then XTitle = "X = "
+   if not(keyword_set(YTitle)) then YTitle = "Y = "
+   Blanks  = "                    "
+
+;;;
+;   Now, if the XValues and/or YValues keywords are set, then deal with them.
+; Also, we may want to suppress the printing of the X or Y values (e.g.,
+; XValues=-1 or YValues=-1 sets the ShowX and ShowY variables).
+;
+   ShowX = 1
+   UseXV = keyword_set(XValues)
+   if UseXV then begin
+      XVSt = string(XValues)
+      XVtop = n_elements(XValues) - 1
+      XVfmt = "(A" + strtrim(max(strlen(XVst))+3,2) + ")"
+      if ((XVtop eq 0) and (strtrim(XVSt[0],2) eq '-1')) then ShowX = 0
+   endif else XVfmt = "(A13)"
+   if not(ShowX) then XTitle = ''
+
+   ShowY = 1
+   UseYV = keyword_set(YValues)
+   if UseYV then begin
+      YVSt = string(YValues)
+      YVtop = n_elements(YValues) - 1
+      YVfmt = "(A" + strtrim(max(strlen(YVst)),2) + ")"
+      if ((YVtop eq 0) and (strtrim(YVSt[0],2) eq '-1')) then ShowY = 0
+   endif else YVfmt = "(A13)"
+   if not(ShowY) then YTitle = ''
+
+;;;
+;   If Print>1, then printout the informative header, which will vary depending
+; on the values of the DOWN and CHANGE keywords.
+;
+   if (Print gt 1) and not(NoWait) then begin
+      print
+      if Change then begin
+         print, " Hit any mouse button or move the mouse to exit."
+      endif else begin
+         if Down or Wait then begin
+            print, " Hit any mouse button to exit."
+         endif else begin
+            print, '  Mouse Button:   LEFT         MIDDLE        RIGHT'
+            print, ' Result Action:   New Line     Exit          Exit'
+         endelse
+      endelse
+      print
+   endif
+
+endif else Print = 0
+
+
+;;;
+;   FULL-SCREEN CURSOR SETUP SECTION =======================================
+;;;
+
+;;;;
+; If using the full-screen cursor:
+;   Determine the data range for the full screen.
+;   Blank out the regular cross cursor if the CROSS keyword is not set.
+;   Set up the linestyle, thickness, clipping, and color parameters for the 
+; oplot commands.
+;   Set up the graphics to be XOR with the overplotted crosshair, and figure
+; out the color to use for plotting the crosshair {details below}.
+;
+if FullCursor then begin
+   Yfull = convert_coord([0.0,1.0], [0.0,1.0], /NORMAL, /TO_DATA)
+   Xfull = Yfull[0,*]
+   Yfull = Yfull[1,*]
+
+   device, GET_GRAPHICS=OldGraphics, SET_GRAPHICS=6
+   if not(keyword_set(Cross)) then device, CURSOR_IMAGE=intarr(16)
+
+   if not(keyword_set(Linestyle)) then Linestyle = 0
+   if not(keyword_set(Thick)) then Thick = 1
+   NoClip = keyword_set(NoClip)
+
+;;;
+;   I think the best way to make the fullscreen cursor work is to use the XOR
+; graphics function - overplotting a line will XOR with the data already on
+; the screen, then overplotting the same line again will XOR again, effectively
+; erasing the line and returning the device to its original state/appearance.
+;    But first, let me present a quick primer on plotting colors in IDL and the 
+; related color tables and translation table:
+;   Normally, when a color N (a number between 0 and 255 which refers to a
+; particular color in the currently loaded IDL color table) is used in one of
+; the plotting or tv commands, the value that is actually sent to the display is
+; the value in the N-th bin of the translation table.  E.g., if the background
+; color is 0, then the actual (device) color value of the background is the
+; value in the zeroth bin of the translation table.  Similarly, if the user
+; wants to plot the color defined by number 147 in the IDL color table, the
+; actual (device) color value of that color is the value in the 147th bin
+; of the translation table.
+;  So in the following example, let's pretend we have the following situation:
+;   IDL> PRINT, !D.N_Colors
+;            222
+;   IDL> PRINT, !P.Background
+;              0
+;   IDL> DEVICE, TRANSLATION=TTab
+;   IDL> PRINT, TTab[0]
+;             34
+;   IDL> PRINT, TTab[147]
+;            181
+;   When we set DEVICE,SET_GRAPHICS=6, and do an overplot, it performs an XOR
+; function between the overplot's translated color value and the background's
+; translated color value.
+;   If we want the resulting color to be the IDL color 147, then we have to 
+; overplot with the color whose translated color value XOR'ed with the 
+; background's translated color value (34) will equal 181, which is the 
+; translated color value of the desired IDL color 147.
+;
+; Symbolically:
+; *  TTab[Desired Color] = TTab[OPLOT color] XOR TTab[Background]
+; *  OPLOT Color = where( TTab eq (TTab[Desired Color] XOR TTab[Background]) )
+;
+; Numerically {using the above example}:
+; *  OPLOT Color = where( TTab eq (TTab[147] XOR TTab[0]) )
+; *  OPLOT Color = where( TTab eq (181 XOR 34) )
+; *  OPLOT Color = where( TTab eq 151 )
+;
+;   Fine.
+;   HOWEVER...since the translation table often does NOT contain the full range
+; of possible numbers (e.g., 0 to 255), the result of the XOR function between 
+; the background and the oplot color may be a value that does NOT appear in the 
+; translation table.  This is particularly a problem for colors near the bottom
+; of the translation table where the result of the XOR function may be less than
+; the lowest value in TTab.
+;   To fix this problem, I bypass the translation table, and directly send the
+; device color (e.g., the value 151 in the above example) to the OPLOT command.
+;   There is still some bug here - sometimes the color still isn't right.  I'll
+; have to talk to the IDL support people about this {as soon as our support
+; license is renewed!}
+; NOTE: Took a while to figure out how to make the full cursor work with
+;       both a specified cursor color and a non-black background.  We stick
+;       with the XOR graphics function.  However, we need to deal with the
+;       complex case of an indexed color model (Decompositon off) for the
+;       TrueColor and DirectColor visual classes.  For TrueColor, we get
+;       the RGB triplet stored in the color table at the indices specified
+;       by Color and BackGround and convert them to 24-bit decomposed color
+;       indices.  Then we turn on color decomposition.  Before we exit, we
+;       turn it back off.  For DirectColor, we just need to XOR the 8-bit
+;       color table indices. -Robishaw
+;
+
+   ; CHECK FOR THE VISUAL CLASS AND COLOR DECOMPOSITION STATE...
+   device, Get_Visual_Name=VisualName, Get_Decomposed=Decomposed
+
+   ; SET COLOR KEYWORDS IF NOT DEFINED...
+   if ((size(Color))[1] eq 0) then $   ;  if undefined
+      Color = Decomposed ? !D.N_Colors - 1 : !D.Table_Size - 1
+   if (N_elements(BACKGROUND) eq 0) then BackGround = !P.BackGround
+   
+   ; Are we using a TrueColor or DirectColor visual class...
+   if (VisualName eq 'TrueColor') OR (VisualName eq 'DirectColor') then begin
+      if (VisualName eq 'TrueColor') AND not(Decomposed) then begin
+         ; For TrueColor with color decomposition off, we need to...
+         ; Turn on Color Decomposition...
+         device, Decomposed=1
+         ; Get the RGB triplets stored in our color table...
+         tvlct, rct, gct, bct, /GET
+         ; Find the corresponding 24-bit decomposed color indices...
+         CTab = long(rct) + ishft(long(gct),8) + ishft(long(bct),16)
+         DevColor = CTab[Color]
+         DevBack = CTab[BackGround]
+      endif else begin
+         ; If TrueColor or Directcolor with Decomposition On, or
+         ; DirectColor with Decomposition Off...
+         DevColor = Color
+         DevBack  = BackGround
+      endelse
+   endif else begin
+      ; If we're not using TrueColor or DirectColor, then we'll
+      ; access the translation table...
+      device, TRANSLATION=TTab, BYPASS_TRANSLATION=1
+      if (Color ge !D.Table_size) then $
+         message, /INFO, $
+                  'Trying to draw cursor with color table index GT Table Size'
+      DevColor = TTab[Color < (!D.Table_size - 1)]
+      if (BackGround ge !D.Table_size) then $
+         message, /INFO, $
+                  'Specified background has color table index GT Table Size'
+      DevBack  = TTab[BackGround < (!D.Table_size - 1)]
+   endelse
+   OColor = DevColor xor DevBack
+endif
+
+
+;;;
+;   FINALLY...THE PLOT READING SECTION  ====================================
+;;;
+
+;;;
+;   If the cursor is beyond the boundaries of the window (device coordinates of
+; X=-1 and Y=-1), then wait until the cursor is moved into the window.
+;
+cursor, X, Y, /NOWAIT, /DEVICE
+if ((X lt 0) or (Y lt 0)) then cursor, X, Y, /CHANGE
+
+
+;;;
+;   Begin the loop that will repeat until a button is clicked (or a change if
+; that is what the user wanted).   Err0 is used to keep track if the procedure
+; was entered with a key already down, then it will be non-zero until that
+; key has been released, at which point it will be permanantly set to zero.
+; NOTE: Robishaw's edits make Err0 obsolete so these lines are commented.
+;   Wait for a change (movement or key click).  Delete the old lines, and
+; if we don't exit the loop, repeat and draw new lines.
+;
+cursor, X, Y, /NOWAIT, DATA=Data, DEVICE=Device, NORMAL=Normal
+;Err0 = !mouse.button
+
+NClicks = 0l
+repeat begin    ; here we go!
+
+;;;
+;   This wait is a kludge to prevent ghosts from being left when /FULLCURSOR
+;   is set.
+;
+    if FullCursor then wait, 0  ; black magic
+
+;;;
+;   If doing a full-screen cursor, overplot two full-screen lines intersecting 
+; at that position.
+;
+   if FullCursor then begin
+      XY = convert_coord(X,Y, DATA=Data,DEVICE=Device,NORMAL=Normal, /TO_DATA)
+      Xdata = XY[0] * [1.0,1.0]
+      Ydata = XY[1] * [1.0,1.0]
+      oplot,Xdata,Yfull,LINE=Linestyle,THICK=Thick,NOCLIP=NoClip,COLOR=OColor
+      oplot,Xfull,Ydata,LINE=Linestyle,THICK=Thick,NOCLIP=NoClip,COLOR=OColor
+   endif
+
+;;;
+;   If printing out data values, do so.
+;   !mouse.button=1 is the signal for a new line.
+;
+   if (Print gt 0) then begin
+
+      if ShowX then begin
+         if UseXV then Xst = XVSt[(X+0.5) > 0 < XVtop] else Xst = strtrim(X,2)
+         XSt = XTitle + string(Xst + Blanks, FORMAT=XVfmt)
+      endif else Xst = ''
+      if ShowY then begin
+         if UseYV then Yst = YVSt[(Y+0.5) > 0 < YVtop] else Yst = strtrim(Y,2)
+         YSt = YTitle + string(Yst + Blanks, FORMAT=YVfmt)
+      endif else Yst = ''
+
+      print, Xst, Yst, format='($,2A,%"\R")'
+
+      ; If left button pressed, then print out a new line; accumulate
+      ; position if /ACCUMULATE set...
+      if (!mouse.button eq 1) and $
+         not(Down or Wait or Change or NoWait) then begin ;  new line?
+         print, format='($,%"\n")'
+         NClicks++
+         if Arg_Present(y) then begin
+            if keyword_set(ACCUMULATE) && (NClicks gt 1) then begin
+               xout = [xout,x]
+               yout = [yout,y]
+            endif else begin
+               xout = x
+               yout = y
+            endelse
+         endif
+      endif
+   endif
+
+   ; If button is held down, don't continue until button is released...
+   if ( (!mouse.button eq 1) and not(Wait or Change or NoWait) ) $
+      ; if entered with a button down, wait for next down click before
+      ; returning...
+      or ( (!mouse.button gt 1) and Down) then begin
+      while (!mouse.button gt 0) do begin
+         wait, 0.1
+         cursor, XX, YY, /NOWAIT
+      endwhile
+   endif
+
+   ;Err0 = Err0 < !mouse.button
+
+;;;
+;  Check to see that the cursor's current position is really the last measured 
+; position (the mouse could have moved during a delay in the last section).  If
+; so, then go on.  If not, then wait for some change in the mouse's status 
+; before going on.
+;  In either case, once we are going on, then if doing a full-screen cursor, 
+; overplot the previous lines {the XOR graphics function will return the plot
+; to its original appearance}.  Repeat until exit signal.
+;
+
+   ; There are a few cases where we just want to exit immediately...
+   InstantOut = ( NoWait ) OR $  ; if /NoWait is set
+                ; if /WAIT is set and *any* button is pressed, even if
+                ; a button is being held down when the routine is called...
+                ( Wait AND (!mouse.button gt 0) ) OR $
+                ; if /CHANGE is set and *any* button is pressed...
+                ( Change AND (NClicks gt 0) )
+
+   if ~(InstantOut) then begin
+      cursor, XX, YY, /NOWAIT, DATA=Data, DEVICE=Device, NORMAL=Normal
+      if ((XX eq X) and (YY eq Y)) then $
+         cursor, XX, YY, /CHANGE, DATA=Data, DEVICE=Device, NORMAL=Normal
+      ; Load the new XX and YY values into the X and Y variables...
+      X = XX
+      Y = YY
+   endif
+
+   ; Erase the full cursor...
+   if FullCursor then begin
+      oplot,Xdata,Yfull,LINE=Linestyle,THICK=Thick,NOCLIP=NoClip,COLOR=OColor
+      oplot,Xfull,Ydata,LINE=Linestyle,THICK=Thick,NOCLIP=NoClip,COLOR=OColor
+   endif
+
+   ; Handle case of /CHANGE but cursor was moved rather than a button
+   ; clicked; we use kludge of incrementing NClicks counter...
+   ; this will force the new position to be printed...
+   if Change AND (NClicks eq 0) then begin
+      XOut = X
+      YOut = Y
+      NClicks++
+      ExitFlag = 0
+      continue
+   endif
+
+   Err = !mouse.button
+
+   ExitFlag = (Down AND (Err gt 0)) OR (Err gt 1) OR InstantOut
+   print,down,instantout,err,exitflag
+endrep until ExitFlag
+;;;
+; If exit click was at a position different from last left-click, then add
+; this to the list of positions...
+;
+if (NClicks gt 0) then begin
+   last_left_click = keyword_set(ACCUMULATE) ? NClicks-1 : 0
+   if N_elements(Xout) Gt 0 THEN $
+   if ~((X eq XOut[last_left_click]) and $
+          (Y eq YOut[last_left_click])) then begin
+      XOut = [XOut,X]
+      YOut = [YOut,Y]
+   endif ELSE BEGIN
+      Xout = x
+      YOut = y
+   endELSE   
+endif else begin
+   XOut = X
+   YOut = Y
+endelse
+
+if (Print gt 0) then print ; clear the last printed line
+
+;LABEL_DONE:
+
+;;;
+;  Done!  Go back to the default Graphics and cursor in case they were changed.
+;  Also erase the plot ranges if they originally were not defined.
+;
+if FullCursor then begin
+   if (N_elements(CURSOR_STANDARD) eq 0) $
+      then device,/CURSOR_CROSSHAIR,SET_GRAPHICS=OldGraphics,Bypass=0 $
+      else device,CURSOR_STANDARD=cursor_standard,SET_GRAPHICS=OldGraphics,$
+                  Bypass=0
+
+   ; If the color decomposition was off when we started, shut it off again...
+   if (VisualName eq 'TrueColor') && ~Decomposed then device, Decomposed=0
+endif
+
+if UndefinedPlot then begin
+   !X.CRange = 0
+   !Y.CRange = 0
+endif
+
+;;;
+;  Assign X & Y to the accumulated values if /ACCUMULATE is set...
+if keyword_set(ACCUMULATE) and Arg_Present(Y) then begin
+   X = temporary(XOut)
+   Y = temporary(YOut)
+endif
+end   ;   RDPLOT
diff --git a/Code/script_idl_mv/astrolib/rdpsf.pro b/Code/script_idl_mv/astrolib/rdpsf.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9e72781c9cd1fc26d5c70bb5997ce502eb4dd8c0
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/rdpsf.pro
@@ -0,0 +1,58 @@
+pro rdpsf,psf,hpsf,psfname
+;+
+; NAME:
+;       RDPSF
+; PURPOSE:
+;       Read the FITS file created by GETPSF in the DAOPHOT sequence 
+; EXPLANATION:
+;       Combines the Gaussian with the residuals to create an output PSF array.
+;
+; CALLING SEQUENCE:
+;       RDPSF, PSF, HPSF, [ PSFname]
+;
+; OPTIONAL INPUTS
+;       PSFname - string giving the name of the FITS file containing the PSF
+;               residuals
+;
+; OUTPUTS
+;       psf - array containing the actual PSF
+;       hpsf - header associated with psf
+;
+; PROCEDURES CALLED:
+;       DAO_VALUE(), MAKE_2D, SXADDPAR, READFITS(), SXPAR()
+; REVISION HISTORY:
+;       Written W. Landsman              December, 1988
+;       Checked for IDL Version 2, J. Isensee & J. Hill, December, 1990
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+
+ if N_params() LT 2 then begin
+    print,'Syntax - RDPSF, psf, Hpsf, [ PSFname ]'
+    print,'    PSF,HPSF - are the output PSF array and header'
+    print,'    PSFNAME - the name of the file containing the PSF, input'
+   return
+ endif
+
+ if N_params() EQ 2 then begin
+    psfname = ''
+    read,'Enter name of the FITS file containing the PSF residuals: ',psfname
+ endif
+
+ resid = readfits(psfname, hpsf)
+ gauss = sxpar(hpsf,'GAUSS*')  ;Get Gaussian parameters (5)
+ psfrad = sxpar(hpsf,'PSFRAD') ;Get PSF radius
+ npsf = 2*psfrad+1             ;Width of output array containing PSF
+ psf = fltarr(npsf,npsf)       ;Create output array
+ dx = indgen(npsf) - psfrad    ;Vector gives X distance from center of array
+ dy = dx                       ;Ditto for dy
+ make_2d,dx,dy                 ;Now have X and Y values for each pixel in
+;                              the output array   
+
+ psf = psf + dao_value(dx,dy,gauss,resid) ;Compute DAOPHOT value at each point
+
+ sxaddpar,hpsf,'NAXIS1',npsf   ;Update header to contain PSF size
+ sxaddpar,hpsf,'NAXIS2',npsf   ;rather than residual array size
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/read_fmr.pro b/Code/script_idl_mv/astrolib/read_fmr.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b4c7199f34b8d6a404ec239d0cf83f3911dda041
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/read_fmr.pro
@@ -0,0 +1,345 @@
+;+
+; NAME:
+;  READ_FMR
+;
+; PURPOSE:
+;   Read a journal (ApJ, AJ) machine-readable table into IDL
+;
+; EXPLANATION:
+;  Given a machine readable table name and optionally column
+;  numbers, this FUNCTION reads the format information in the
+;  meta-header and outputs a IDL function containing either the
+;  complete table or only the requested columns.
+;
+; CALLING SEQUENCE:
+;  data = read_fmr(filename)
+;
+; INPUTS:
+;  filename [STRING]: the name of the file containing the machine
+;  readable table. If filename is missing a dialog to select the
+;  filename will be presented
+;
+; INPUT KEYWORD PARAMETERS:
+;   /HELP  - if set show the help
+;
+;   COLUMNS -  [(array of) integers or strings] of column(s) to be returned.
+;     If columns is of type integer they represent indices for which
+;     column numbers to return, if they are strings the columns with the
+;     corresponding names will be returned in the order as given.
+;
+;   MISSINGVALUE [float]: value with which to replace the missing values in the 
+;        table, default is NaN.
+;
+;   /USE_COLNUM - If  specified and non-zero then column names will be generated
+;        as 'C1, C2,  .... Cn'  for the number of columns in the table, rather
+;        than using the table names.
+;
+; OUTPUTS:
+;  The ouput data structure will look like:
+;    TYPE            STRING    'mr_structure'
+;    NAME            STRING    Array[X]
+;    UNIT            STRING    Array[X]
+;    DESCRIPTION     STRING    Array[X]
+;    DATA            STRUCT    -> <Anonymous> Array[1]
+;  where name contains the names of each columns
+;  unit contains the given units
+;  description contains the short descriptions and
+;  data holds the values of the separate columns.   By default the tag names are
+;  taken from the column names, with modifications necessary to make them a 
+;  valid tag name.    For example, the column name 'B-V' will be converted to 
+;  'B_V' to become a valid tag name.    If the /USE_COLNUM keyword is set, then
+;  the column will be named  C0,  C1, ... , CX, where X stands for the total 
+;  number of columns read.
+;
+; RESTRICTIONS:
+;  (1) The file to be read should be formatted as a machine readable datafile.
+;  (2) Use of the COLUMN keyword currently requires use of the EXECUTE function,
+;      and so cannot be used with the IDL Virtual machine.
+; EXAMPLE:
+;  meas = read_fmr('smith.dat',col=[2,5,6], /Use_colnum)
+;   plot,meas.data.c1,ytitle=meas.name[1]+' ('+meas.unit[1]+')'
+;
+;  and
+;  data = read_fmr('smith.dat',col=['Name','Date'], /Use_colnum)
+;   print,meas.data.c0
+;   
+; MODIFICATION HISTORY:
+;  Version 1:
+;  Written by Sacha Hony (ESA) Nov 14 2003
+;   Based heavily on mrcolextract by Greg Schwarz (AAS Journals
+;   staff scientist) on 8/16/00.
+;
+;  Version 1.1:
+;    Fixed bug where column=[3,4] always returned the first few columns
+;
+;  VErsion 2.0 By default use column names as tag names W. Landsman Feb 2010
+;  Version 3.0 Use long integers W. Landsman/T. Ellsworth-Bowers May 2013
+;  Version 3.1 Assume since IDL V6.4  W.L. Aug 2013
+;-
+
+FUNCTION read_fmr,filename, $
+                  columns=columns, $
+                  missingvalue=missingvalue, $
+                  help=help, $
+		  use_colnum = use_colnum
+
+  compile_opt idl2
+  ;; Only print the usage info and return if asked for help
+  IF keyword_set(help) THEN BEGIN
+      doc_library,'read_fmr'
+      return,0
+  ENDIF
+  
+  ;; If no filename is given then pop-up the dialog_pickfile dialog
+  IF N_elements(filename) EQ 0 THEN BEGIN
+       filename =dialog_pickfile(filter=['*.dat;*.asc*;*.txt','*'], $
+                                 /must_exist)
+   ENDIF
+  
+  ;; Check that file exists and is readable otherwise bail-out
+  IF ~FILE_TEST(filename) THEN BEGIN
+      message,'The file: '+filename+' does cannot be found or read', $
+              /informational
+      return,0
+  ENDIF
+  
+  IF N_elements(missingvalue) EQ 0 THEN missingvalue=!VALUES.F_NAN
+  
+;; Variables needed to read single lines of the file
+  dumI=' '
+  tmp=''
+  irow=0L ;; Make sure it can hold a lot of lines
+  startpos=' '
+  endpos=' '
+
+;; Variable in which the total information of the files is collected  
+  names=''
+  units=''
+  descriptions=''
+  startposs=0
+  idltypes=0
+  
+  openr,lun,filename,/get_lun
+  
+;; Read the first few lines into a dummy variable
+;; because this info is not needed.  However, keep
+;; track of the number of lines.
+  WHILE (strpos(dumI,'Bytes Format') EQ -1) DO BEGIN
+      readf,lun,dumI
+      irow++
+  END 
+  
+  readf,lun,dumI
+  irow++
+  
+;; Read until you reach a '------' line terminator
+  WHILE (strpos(tmp,'-----------------') EQ -1) DO BEGIN
+      irow++
+      
+;; Extract out the 6-8th positions.  
+;; If there is a number you have a column
+      readf,lun,f='(1X,A3,1X,A3,1X,A80)',startpos,endpos,tmp
+      
+;; If startpos is --- then you are at the end 
+;; so set the 9999 flag so it isn't counted
+      IF (startpos EQ '---') THEN startpos = '9999'
+
+;; If starpos is blank then this is either a continuation
+;; line or a column that is only one digit wide.  You can
+;; tell by checking if endpos is also blank.  If it is a 
+;; column then set startpos and endpos to the same value
+      IF (startpos EQ '   ') THEN BEGIN
+          startpos = endpos
+          IF (endpos EQ '   ') THEN startpos = '9999'
+      ENDIF
+      IF (fix(startpos) GE 1 AND fix(startpos) LE 999) THEN BEGIN
+          
+;; Squeeze out the blanks.
+          less_blanks = strcompress(tmp)
+          
+;; Separate the non-location info by sorting into an array that is 
+;; delimited by blank spaces.  The first position is the format,
+;; the second is the units, the third is the name, and the last
+;; positions are the short description of the column
+          
+;;(SH Nov 18 2003) strsplit is not available in older versions of IDL
+         components=strsplit(less_blanks,' ',/extract)
+ 
+;; Determine the column type (A|I|F|E)
+          vtype = strmid(components[0],0,1)
+          CASE vtype OF
+              'A': idltype = 7
+              'I': idltype = 3
+              'F': idltype = 5
+              'E': idltype = 5
+          ENDCASE
+          
+          ;; Add the collected data to the lists
+          names=[names,components[2]]
+          units=[units,components[1]]
+          ;; Take the rest of the strings a description
+          description=''
+          FOR i=3,n_elements(components)-1 DO description=description+ $
+            components[i]+' '
+          descriptions=[descriptions,description]
+          startposs=[startposs,startpos-1]
+          idltypes=[idltypes,idltype]
+      ENDIF 
+  ENDWHILE
+
+;; iskip is the end (maybe see below) of the meta-header 
+  iskip=irow
+  
+;; Continue reading the file to get the number of lines
+  lastdash=0L
+  WHILE ~eof(lun) DO BEGIN
+      readf,lun,dumI
+      irow++
+;; If you encounter another '--------' (e.g. the end of a
+;; notes subsection) mark it because you don't want to 
+;; read the previous information as data!
+      IF (strmid(dumI,0,6) EQ '------') THEN BEGIN
+          lastdash=irow
+      ENDIF
+  ENDWHILE
+  
+  ;; Make sure we close the file and free the lun
+  free_lun,lun
+  
+;; If you found a '-------' line then set iskip to the last dash
+;; line so not to read any extra headers
+  IF (lastdash NE 0L) THEN BEGIN
+      iskip=lastdash
+  ENDIF
+
+;; Clean the arrays from the first dummy element
+  names=names[1:*]
+  units=units[1:*]
+  descriptions=descriptions[1:*]
+  startposs=startposs[1:*]
+  idltypes=idltypes[1:*]
+  ncolumns = n_elements(startposs)
+  if keyword_set(USE_COLNUM) then $
+      fieldnames = 'C' + strtrim(indgen(ncolumns),2) else $
+      fieldnames = IDL_VALIDNAME(names,/convert_all)
+  
+  ;; now fill the template stuff for read_ascii
+  template = {VERSION:1.00000, $
+              DATASTART:iskip, $
+              DELIMITER:0B, $
+              MISSINGVALUE:missingvalue, $
+              COMMENTSYMBOL:'', $
+              FIELDCOUNT:ncolumns, $
+              FIELDTYPES:idltypes, $
+              FIELDNAMES: fieldnames, $
+              FIELDLOCATIONS:startposs, $
+              FIELDGROUPS:indgen(ncolumns)}
+  
+  data = read_ascii(filename,template=template)
+  
+
+  ;; This is all if the columns keyword is given then
+  ;; only certain columns are requested. So do the selections here
+  IF keyword_set(columns) THEN BEGIN
+
+      ncolumns = n_elements(columns)
+   
+      ;; are they strings?
+      IF size(columns,/TNAME) EQ 'STRING' THEN BEGIN
+
+          ;; first convert the columns and the output names to uppercase
+          ;; to be able to compare them directly without strcmp
+          names_up   = strupcase(names)
+          columns_up = strupcase(columns)
+
+          ;; create an array to hold the requested column numbers set
+          ;; these to -1
+          idx_columns = make_array(ncolumns,value=-1)
+
+          ;; Now match each string with the names
+          FOR i=0,ncolumns-1 DO BEGIN
+              ;; take the first instance where the uppercase name and
+              ;; uppercase column match
+              idx_columns[i] = ( where(names_up EQ columns_up[i]) )[0]
+          ENDFOR
+
+          ;; Are there elements which did not find a match?
+          idx_missing_columns = where(idx_columns EQ -1,cnt)
+
+          ;; All the elements of idx_columns are -1
+          IF (cnt EQ ncolumns) THEN BEGIN
+              message,'None of the column names could be found in the table', $
+                      /informational
+              return,0
+          ENDIF
+
+          ;; Some elements are matched but some are missing
+          IF (cnt NE 0) THEN BEGIN
+              message,'The following columns are not present in the table:', $
+                      /informational
+              message,columns[idx_missing_columns], $
+                      /informational
+              ;; Only take the valid columns and still continue
+              idx_columns =idx_columns[where(idx_columns NE -1)]
+          ENDIF
+
+      ENDIF ELSE BEGIN
+          ;; Assume the columns are numbers which indicate the
+          ;; requested column numbers
+
+          max_column=n_tags(data)-1
+          columns = fix(columns)
+          ;; make sure they are not higher than the available number
+          ;; of columns and not negative
+          idx_columns = columns[where( (columns LE max_column) AND $
+                                       (columns GE 0) ,cnt)]
+
+          IF (cnt EQ 0) THEN BEGIN
+              message,'The requested columns are not present in the file', $
+                      /informational
+              return,0
+          ENDIF
+
+          ;; Some elements are matched but some are too high
+          IF cnt NE ncolumns THEN BEGIN
+              message,'Some column numbers are out of range.'+ $
+                      ' Valid range=[0,'+ $
+                      strcompress(string(max_column),/remove_all)+']', $
+                      /informational
+          ENDIF
+      ENDELSE
+
+;; now take the requested columns
+      names=names[idx_columns]
+      units=units[idx_columns]
+      if ~keyword_set(use_colnum) then fieldnames = fieldnames[idx_columns] $
+         else fieldnames = 'C' + strtrim(indgen(ncolumns),2)
+      descriptions=descriptions[idx_columns]
+      ncolumns = n_elements(names)
+      
+
+      ;; We need this to restructure the data structure to hold only
+      ;; the requested columns
+      exec_string = 'data={' + fieldnames[0] + $
+                    ':data.('+string(idx_columns[0])+')'
+      FOR i=1,ncolumns-1 DO BEGIN
+          exec_string = exec_string + ',' + fieldnames[i] + $
+                        ':data.('+string(idx_columns[i])+')'
+      ENDFOR
+      exec_string=exec_string+'}'
+      foo = execute(exec_string)
+  ENDIF
+  
+  
+  out = {type:'mr_structure', $
+         name:names, $
+         unit:units, $
+         description:descriptions, $
+         data:data}
+  
+  message,"Read "+strcompress(ncolumns)+" columns from "+ $
+          filename,/informational
+  
+  return,out
+  
+END
diff --git a/Code/script_idl_mv/astrolib/read_ipac_table.pro b/Code/script_idl_mv/astrolib/read_ipac_table.pro
new file mode 100644
index 0000000000000000000000000000000000000000..cf98664f5aa1a395653d33ee8271018b4290d574
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/read_ipac_table.pro
@@ -0,0 +1,521 @@
+FUNCTION read_ipac_table, filename, change_null=change_null, debug=debug
+
+;+
+; NAME: 
+;   READ_IPAC_TABLE
+;
+; PURPOSE: 
+;   Read an IPAC ascii table from a file into an IDL structure
+;
+; EXPLANATION:
+;   Reads an IPAC ascii table from a file into an IDL structure.  The
+;   definition of an IPAC-format table is currently here:
+;      http://irsa.ipac.caltech.edu/applications/DDGEN/Doc/ipac_tbl.html
+;
+; CALLING SEQUENCE: 
+;      info = read_ipac_table(filename, [change_null=change_null, /debug])
+;
+; INPUTS:  
+;      FILENAME -- string giving the file with the input IPAC ascii table
+;
+; OPTIONAL INPUT:
+;      CHANGE_NULL -- an integer value to be used when the IPAC table
+;                     has a non-numeric string for null values in an 
+;                     integer column.  The default is -9999.  For
+;                     floating-point columns, this is 'NaN'.
+;
+;      DEBUG -- enables some debugging statements
+;
+; OUTPUTS: 
+;      info - Anonymous IDL structure containing information on the catalog.  The structure
+;           tag names are taken from the column names.  The structure will put header
+;           information in tags starting with "HEADER", e.g.
+;           HEADER_TABLE_HEADER, HEADER_DATA_UNITS, and HEADER_NULL_VALUES.
+;           Since the table column names may be altered if they are
+;           not valid IDL variable names, the original column names
+;           are saved as HEADER_COL_NAMES_ORIG.  The original data
+;           type names are also saved as HEADER_COL_TYPES_ORIG.
+;
+;           If the table is not valid, or contains no data, the function returns a value of -1
+;
+; PROCEDURES USED:
+;   GET_DATE, VALID_NUM
+;
+; MODIFICATION HISTORY:
+;      Written by H. Teplitz, IPAC September 2010 
+;      Allow long integer, convert blanks in numeric fields to null
+;      value - T. Brooke, IPAC May 2011
+;      Allow 64bit long; use valid_num to check - TB June 2013
+;-
+
+;Copyright � 2013, California Institute of Technology
+;All rights reserved. Based on Government Sponsored Research NAS7-03001 and NNN12AA01C.
+;
+;
+;Redistribution and use in source and binary forms, with or without
+;modification, are permitted provided that the following conditions
+;are met:
+;
+; *  Redistributions of source code must retain the above copyright
+;    notice, this list of conditions and the following disclaimer.
+;
+; *  Redistributions in binary form must reproduce the above copyright
+;    notice, this list of conditions and the following disclaimer in
+;    the documentation and/or other materials provided with the
+;    distribution.
+;
+; *  Neither the name of the California Institute of Technology
+;    (Caltech) nor the names of its contributors may be used to
+;    endorse or promote products derived from this software without
+;    specific prior written permission.
+;
+;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+;"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+;LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+;A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+;HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+;INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+;BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+;OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+;AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+;LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+;WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+;POSSIBILITY OF SUCH DAMAGE.
+;
+
+on_error,2
+compile_opt idl2
+
+IF N_params() lt 1 THEN BEGIN
+   print,'Syntax - info = read_ipac_table(filename, [change_null=change_null, /debug])'
+   return, -1
+ENDIF
+
+file = filename
+n_lines = file_lines(file)
+
+IF keyword_set(change_null) THEN BEGIN
+  IF ( NOT(valid_num(change_null,/integer)) ) THEN BEGIN
+    print, 'ERROR: change null value must be integer.'
+    return,-1
+  ENDIF ELSE BEGIN
+    null_num = change_null
+  ENDELSE
+ENDIF ELSE null_num = -9999
+
+line=''
+inline=''
+inheader=''
+
+already_read = 0
+lines_read = 0
+
+openr, lun, file, /get_lun
+
+firstchar = '\'
+WHILE firstchar NE '|' DO BEGIN 
+   readf, lun, inline
+   lines_read = lines_read+1
+   IF EOF(lun) THEN BEGIN
+      print, 'ERROR:  Invalid IPAC table - no header lines'
+      return, -1
+   ENDIF
+   firstchar = strmid(inline,0,1)
+   IF firstchar EQ '\' THEN inheader = [inheader,inline]
+ENDWHILE
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; use first line with '|' to find indices between columns
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+line = inline
+len = strlen(line)
+
+;;;; check for trailing spaces after last |
+
+pos = strpos(line,'|',/reverse_search)
+IF (pos lt 2) THEN BEGIN
+  print,'ERROR: invalid table column header'
+  return, -1
+ENDIF ELSE BEGIN
+  len = pos + 1
+  line = strmid(line,0,len)
+ENDELSE
+
+name_line_length = len
+subline = line
+
+strput, subline, 'x', 0
+delim_idx = [0]
+eol=0
+WHILE NOT(eol) DO BEGIN 
+   char = strpos(subline,'|')
+   IF char NE -1 THEN begin
+      strput, subline, 'x', char
+      delim_idx = [delim_idx, char]
+   ENDIF 
+   IF char EQ len-1 THEN eol=1
+ENDWHILE
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; check for at least 1 column
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+IF n_elements(delim_idx) le 1 THEN BEGIN 
+   print, 'ERROR: invalid table header' 
+   return, -1
+ENDIF
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; get column names and put into a strarr
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+ncol = n_elements(delim_idx)-1
+col_names = strarr(ncol)
+col_names_orig = strarr(ncol)
+col_width = intarr(ncol)
+FOR i = 0, ncol-1 DO BEGIN 
+   col_width[i] = delim_idx[i+1]-delim_idx[i]-1
+   col_names[i] = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+   col_names_orig[i] = col_names[i]
+ENDFOR
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; check for duplicate column names, add "_idl_[i]"
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+cntr = intarr(ncol)*0 + 1
+FOR ik = 0, ncol-2 DO BEGIN
+   FOR ij = ik+1, ncol-1 DO BEGIN
+     IF (strcmp(col_names[ij],col_names[ik],/fold_case)) THEN BEGIN
+       col_names[ij] = col_names[ij] + '_idl_' + strn(cntr[ik])
+       cntr[ik] = cntr[ik] + 1
+       print,'WARNING: Duplicate column names, replacing occured'
+     ENDIF
+   ENDFOR
+ENDFOR
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; next line must be data types
+;;;; need error check if it isn't....
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+readf, lun, inline
+lines_read = lines_read+1
+
+;;;; check for no data after types line 
+IF EOF(lun) THEN BEGIN 
+   print, 'ERROR: invalid table; no data' 
+   return, -1
+ENDIF
+
+line=inline
+
+IF strmid(line, 0, 1) NE '|' THEN BEGIN 
+   print, 'ERROR: invalid or missing data types line' 
+   return, -1
+ENDIF
+
+col_type_string = strarr(ncol)
+col_types_orig = strarr(ncol)
+col_type_code = intarr(ncol)
+
+FOR i = 0, ncol-1 DO BEGIN
+   ;;; strip spaces from data type and convert to all upper case
+   col_type_string[i] = strupcase(strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2))
+   col_types_orig[i] = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+   check = strmid(line,delim_idx[i+1],1)
+   IF check NE '|' THEN BEGIN 
+      print, 'ERROR: missing pipe in data types line'
+      IF keyword_set(debug) then stop
+      return, -1
+   ENDIF   
+   
+;;; convert data types to    
+   
+   CASE col_type_string[i] OF 
+      'INTEGER':  BEGIN
+         col_type_code[i] = 3      
+         print, 'Data type INTEGER is used.  For full compatibility with all IPAC services, please use INT, IN or I'
+      END
+      'INT':  col_type_code[i] = 3
+      'IN':  col_type_code[i] = 3
+      'I':  col_type_code[i] = 3
+      'LONG':  col_type_code[i] = 14
+      'LON':  col_type_code[i] = 14
+      'LO':  col_type_code[i] = 14
+      'L':  col_type_code[i] = 14
+      'FLOAT':   col_type_code[i] = 4
+      'FLOA':   col_type_code[i] = 4
+      'FLO':   col_type_code[i] = 4
+      'FL':   col_type_code[i] = 4      
+      'F':   col_type_code[i] = 4      
+      'REAL':   col_type_code[i] = 4
+      'REA':   col_type_code[i] = 4
+      'RE':   col_type_code[i] = 4
+      'R':   col_type_code[i] = 4
+      'DOUBLE':   col_type_code[i] = 5
+      'DOUBL':   col_type_code[i] = 5
+      'DOUB':   col_type_code[i] = 5
+      'DOU':   col_type_code[i] = 5
+      'DO':   col_type_code[i] = 5
+      'D':   col_type_code[i] = 5
+      'CHAR':  col_type_code[i] = 7
+      'CHA':  col_type_code[i] = 7
+      'CH':  col_type_code[i] = 7
+      'C':  col_type_code[i] = 7
+      'DATE':  col_type_code[i] = 7
+      'DAT':  col_type_code[i] = 7
+      'DA':  col_type_code[i] = 7
+      ELSE: BEGIN 
+         print, 'ERROR:  invalid data type = '+col_type_string[i]
+         IF keyword_set(debug) then stop
+         return,-1
+      ENDELSE     
+   ENDCASE
+     
+ENDFOR
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; create the basic structure
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+get_date, dte, /time
+info = create_struct('HEADER_Date_Created', string(dte))
+n_header_lines = 1
+
+n_header = n_elements(inheader)
+IF n_header GT 1 THEN BEGIN 
+   current = info
+   info = create_struct(current, 'HEADER_TABLE_HEADER', inheader[1:n_header-1])
+   n_header_lines = n_header_lines+1
+ENDIF
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; Save the original column names and column types.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+current = info
+info = create_struct(current, 'HEADER_Col_Names_Orig', col_names_orig)
+n_header_lines = n_header_lines+1
+current = info
+info = create_struct(current, 'HEADER_Col_Types_Orig', col_types_orig)
+n_header_lines = n_header_lines+1
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;; Read next line.  If it starts with a pipe, it should be the units line.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+readf, lun, inline
+
+line=inline
+
+IF strmid(inline,0,1) EQ '|' THEN BEGIN 
+   lines_read = lines_read+1
+   data_units_string = strarr(ncol)
+   FOR i = 0, ncol-1 DO BEGIN
+   ;;; strip spaces from units
+      data_units_string[i] = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+      check = strmid(line,delim_idx[i+1],1)
+      IF check NE '|' THEN BEGIN 
+         print, 'ERROR: missing pipe in units line'
+         IF keyword_set(debug) then stop
+         return, -1
+      ENDIF   
+   endfor
+   current = info
+   info = create_struct(current, 'HEADER_Data_Units', data_units_string)
+   n_header_lines = n_header_lines+1
+;   remember to add lines to structure and to increment lines_read
+ENDIF $
+ELSE already_read = 1
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;; If the line was data units then read next line.  
+;;;;; If it starts with a pipe, it should be the nulls line
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+IF NOT(already_read) THEN BEGIN 
+   readf, lun, inline
+   line=inline
+
+   IF strmid(inline,0,1) EQ '|' THEN BEGIN 
+      lines_read = lines_read+1
+      null_value_string = strarr(ncol)
+      new_null_value_string = strarr(ncol)
+      FOR i = 0, ncol-1 DO BEGIN
+;;; strip spaces from nulls
+         null_value_string[i] = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+         check = strmid(line,delim_idx[i+1],1)
+         IF check NE '|' THEN BEGIN 
+            print, 'ERROR: missing pipe in nulls line'
+            IF keyword_set(debug) then stop
+            return, -1
+         ENDIF
+
+         IF (col_type_code[i] ne 7) THEN BEGIN
+            IF ( (col_type_code[i] eq 4) or (col_type_code[i] eq 5) ) THEN BEGIN
+               check_num = valid_num(null_value_string[i])
+               IF (check_num eq 0) THEN BEGIN
+                 new_null_value_string[i] = 'NaN'
+               ENDIF ELSE BEGIN
+                 new_null_value_string[i] = null_value_string[i]
+               ENDELSE
+            ENDIF ELSE BEGIN
+               check_num = valid_num(null_value_string[i], /integer)
+               IF (check_num eq 0) THEN BEGIN 
+                 new_null_value_string[i] = strn(null_num)
+               ENDIF ELSE BEGIN
+                 new_null_value_string[i] = null_value_string[i]
+               ENDELSE
+            ENDELSE
+         ENDIF ELSE new_null_value_string[i] = null_value_string[i]
+      ENDFOR 
+   ENDIF ELSE BEGIN 
+      null_value_string = strarr(ncol)+'no input null strings'
+      new_null_value_string = null_value_string
+      iwant = where ( ( (col_type_code eq 4) or (col_type_code eq 5) ),nwant)
+      if (nwant gt 0) then new_null_value_string[iwant] = 'NaN'
+      iwant = where ( ( (col_type_code eq 3) or (col_type_code eq 14) ),nwant)
+      if (nwant gt 0) then new_null_value_string[iwant] = strn(null_num)
+      already_read = 1
+   ENDELSE
+ENDIF ELSE BEGIN 
+   null_value_string = strarr(ncol)+'no input null strings'   
+   new_null_value_string = null_value_string
+   iwant = where ( ( (col_type_code eq 4) or (col_type_code eq 5) ),nwant)
+   if (nwant gt 0) then new_null_value_string[iwant] = 'NaN'
+   iwant = where ( ( (col_type_code eq 3) or (col_type_code eq 14) ),nwant)
+   if (nwant gt 0) then new_null_value_string[iwant] = strn(null_num)
+ENDELSE 
+
+current = info
+info = create_struct(current, 'HEADER_Null_Values', new_null_value_string)
+n_header_lines = n_header_lines+1
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;; set up data structure.  length of vectors is number of lines in 
+;;;;; file minus lines read so far
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+ndata = n_lines - lines_read
+
+IF ndata LE 0 THEN BEGIN
+   print, 'ERROR:  no data'
+   return, -1
+ENDIF
+
+FOR i = 0, ncol-1 DO BEGIN 
+   current = info
+   info = create_struct(current, $
+            IDL_VALIDNAME(col_names[i],/convert_all),make_array(ndata, type=col_type_code[i]))                     
+ENDFOR
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;; read data lines to put into structure
+;;;;; and pad the line if it isn't long enough for all columns
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;   
+
+lmax = 2.0d^63 - 1.0d
+lmin = -2.0d^63
+lmaxi = 2.0d^31 - 1.0d
+lmini = -2.0d^31
+   
+FOR j = 0, ndata-1 DO BEGIN 
+  
+   IF NOT(already_read) THEN readf, lun, inline
+
+;;;; check for non-printable characters
+   IF ( (stregex(inline,string(9b)) ne -1) or $
+        (stregex(inline,string(7b)) ne -1) or $
+        (stregex(inline,string(8b)) ne -1) or $
+        (stregex(inline,string(10b)) ne -1) or $
+        (stregex(inline,string(11b)) ne -1) or $
+        (stregex(inline,string(12b)) ne -1) or $
+        (stregex(inline,string(13b)) ne -1) or $
+        (stregex(inline,string(27b)) ne -1) ) THEN BEGIN
+     print,'Non-printable character in data row = ',j
+     return,-1
+   ENDIF
+  
+   cur_len = strlen(inline)
+   IF cur_len LT name_line_length THEN BEGIN 
+      padlen = name_line_length - cur_len
+      pad = strjoin(replicate(' ', padlen))
+      line = inline+pad
+   ENDIF ELSE line=inline
+   
+   FOR i = 0, ncol-1 DO BEGIN
+      data_string = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+      check = strmid(line,delim_idx[i],1)
+      IF check NE ' ' THEN BEGIN 
+         print, 'ERROR: misaligned columns (data under pipe)'
+         print, 'ERROR: data row, column = ',j,' , ',i
+         IF keyword_set(debug) THEN stop
+         return, -1
+      ENDIF   
+      IF (col_type_code[i] ne 7) THEN BEGIN
+         IF ( (col_type_code[i] eq 4) or (col_type_code[i] eq 5) ) THEN BEGIN
+            check_num = valid_num(data_string)
+            IF (check_num eq 0) THEN BEGIN
+               IF (data_string ne null_value_string[i]) THEN BEGIN
+                 data_string = new_null_value_string[i]
+                 print,'WARNING: Invalid data entry replaced by null value in row, column = ',j,', ',i
+               ENDIF ELSE data_string = new_null_value_string[i]
+            ENDIF
+;;;; Check floating point limits
+            IF (check_num ne 0) THEN BEGIN
+               check_lim = fix(data_string, type=5)
+               IF (finite(check_lim)) THEN BEGIN
+                  IF (col_type_code[i] eq 4) THEN BEGIN
+                     check_lim = fix(data_string, type=4)
+                     IF ( NOT(finite(check_lim)) ) THEN BEGIN
+                        data_string = new_null_value_string[i]
+                        print,'WARNING: Float overflow replaced by null value in row, column = ',j,', ',i
+                     ENDIF
+                  ENDIF
+               ENDIF ELSE BEGIN
+                  data_string = new_null_value_string[i]
+                  print,'WARNING: Double overflow replaced by null value in row, column = ',j,', ',i
+               ENDELSE
+            ENDIF 
+         ENDIF ELSE BEGIN
+            check_num = valid_num(data_string,/integer)
+            IF (check_num eq 0) THEN BEGIN
+               IF (data_string ne null_value_string[i]) THEN BEGIN
+                 data_string = new_null_value_string[i]
+                 print,'WARNING: Invalid data entry replaced by null value in row, column = ',j,', ',i
+               ENDIF ELSE data_string = new_null_value_string[i]
+           ENDIF
+;;;; Check integer limits
+           IF (check_num ne 0) THEN BEGIN
+              check_lim = fix(data_string, type=5)
+              IF ( (check_lim gt lmin) and (check_lim lt lmax) ) THEN BEGIN
+                 IF (col_type_code[i] eq 3) THEN BEGIN
+                    IF ( (check_lim le lmini) or (check_lim ge lmaxi) ) THEN BEGIN
+                       data_string = new_null_value_string[i]
+                       print,'WARNING: Integer overflow replaced by null value in row, column = ',j,', ',i
+                    ENDIF
+                 ENDIF
+              ENDIF ELSE BEGIN
+                 data_string = new_null_value_string[i]
+                 print,'WARNING: Long overflow replaced by null value in row, column = ',j,', ',i
+              ENDELSE
+           ENDIF 
+         ENDELSE
+      ENDIF
+      info.(i+n_header_lines)[j] = data_string
+   ENDFOR   
+   already_read=0
+ENDFOR
+
+close, lun
+free_lun, lun
+
+return, info
+
+END
+
+
+   
diff --git a/Code/script_idl_mv/astrolib/read_ipac_var.pro b/Code/script_idl_mv/astrolib/read_ipac_var.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0d0f49db883c005bbbe893e38cfcd3e066f4bec7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/read_ipac_var.pro
@@ -0,0 +1,528 @@
+FUNCTION read_ipac_var, textvar, change_null=change_null, debug=debug
+
+;+
+; NAME: 
+;   READ_IPAC_VAR
+;
+; PURPOSE: 
+;   Read an IPAC ascii table from a variable into an IDL structure.
+;   Used by query_irsa_cat.pro.
+;
+; EXPLANATION:
+;   Reads an IPAC ascii table from a variable into an IDL structure.  The
+;   definition of an IPAC-format table is currently here:
+;      http://irsa.ipac.caltech.edu/applications/DDGEN/Doc/ipac_tbl.html
+;
+; CALLING SEQUENCE: 
+;      info = read_ipac_var(textvar, [change_null=change_null, /debug])
+;
+; INPUTS:  
+;      TEXTVAR -- a text variable with the table returned from the query
+;
+; OPTIONAL INPUT:
+;      CHANGE_NULL -- an integer value to be used when the IPAC table
+;                     has a non-numeric string for null values in an 
+;                     integer column.  The default is -9999.  For
+;                     floating-point columns, this is 'NaN'.
+;
+;      DEBUG -- enables some debugging statements
+;
+; OUTPUTS: 
+;      info - Anonymous IDL structure containing information on the catalog.  The structure
+;           tag names are taken from the column names.  The structure will put header
+;           information in tags starting with "HEADER", e.g.
+;           HEADER_TABLE_HEADER, HEADER_DATA_UNITS, and HEADER_NULL_VALUES.
+;           Since the table column names may be altered if they are
+;           not valid IDL variable names, the original column names
+;           are saved as HEADER_COL_NAMES_ORIG.  The original data
+;           type names are also saved as HEADER_COL_TYPES_ORIG.
+;
+;           If the table is not valid, or contains no data, the function returns a value of -1
+;
+; PROCEDURES USED:
+;   GET_DATE, VALID_NUM
+;
+; NOTES:
+;   Uses some unnecessary looping, but it's kept this way to stay
+;   similar to read_ipac_table.pro. 
+;
+; MODIFICATION HISTORY:
+;      Adapted from read_ipac_table - C. Gonzalez, U. Alicante March 2011 
+;      Allow long integer, convert blanks in numeric fields to null
+;      value - T. Brooke, IPAC May 2011
+;      Allow 64bit long; use valid_num to check - TB June 2013
+;-
+
+;Copyright � 2013, California Institute of Technology
+;All rights reserved. Based on Government Sponsored Research NAS7-03001 and NNN12AA01C.
+;
+;
+;Redistribution and use in source and binary forms, with or without
+;modification, are permitted provided that the following conditions
+;are met:
+;
+; *  Redistributions of source code must retain the above copyright
+;    notice, this list of conditions and the following disclaimer.
+;
+; *  Redistributions in binary form must reproduce the above copyright
+;    notice, this list of conditions and the following disclaimer in
+;    the documentation and/or other materials provided with the
+;    distribution.
+;
+; *  Neither the name of the California Institute of Technology
+;    (Caltech) nor the names of its contributors may be used to
+;    endorse or promote products derived from this software without
+;    specific prior written permission.
+;
+;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+;"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+;LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+;A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+;HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+;INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+;BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+;OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+;AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+;LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+;WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+;POSSIBILITY OF SUCH DAMAGE.
+;
+
+on_error,2
+compile_opt idl2
+
+n_lines = n_elements(textvar)
+IF (n_lines eq 0) THEN BEGIN
+  print,'ERROR: Empty variable'
+  return,-1
+ENDIF
+
+IF keyword_set(change_null) THEN BEGIN
+  IF ( NOT(valid_num(change_null,/integer)) ) THEN BEGIN
+    print, 'ERROR: change null value must be integer.'
+    return,-1
+  ENDIF ELSE BEGIN
+    null_num = change_null
+  ENDELSE
+ENDIF ELSE null_num = -9999
+
+line=''
+inline=''
+inheader=''
+
+already_read = 0
+lines_read = 0
+
+firstchar = '\'
+WHILE ( (firstchar ne '|') and (lines_read lt n_lines) ) DO BEGIN 
+   inline = textvar[lines_read]
+   lines_read = lines_read+1
+   firstchar = strmid(inline,0,1)
+   IF firstchar EQ '\' THEN inheader = [inheader,inline]
+ENDWHILE
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; if at end then it means no column header or only 1
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+IF (lines_read eq n_lines) THEN BEGIN 
+   print, 'ERROR: invalid table column header' 
+   return, -1
+ENDIF
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; use first line with '|' to find indices between columns
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+line = inline
+len = strlen(line)
+
+;;;; check for trailing spaces after last |
+
+pos = strpos(line,'|',/reverse_search)
+IF (pos lt 2) THEN BEGIN
+  print,'ERROR: invalid table column header'
+  return, -1
+ENDIF ELSE BEGIN
+  len = pos + 1
+  line = strmid(line,0,len)
+ENDELSE
+
+name_line_length = len
+subline = line
+
+strput, subline, 'x', 0
+delim_idx = [0]
+eol=0
+WHILE NOT(eol) DO BEGIN 
+   char = strpos(subline,'|')
+   IF char NE -1 THEN begin
+      strput, subline, 'x', char
+      delim_idx = [delim_idx, char]
+   ENDIF 
+   IF char EQ len-1 THEN eol=1
+ENDWHILE
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; check for at least 1 column
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+IF n_elements(delim_idx) le 1 THEN BEGIN 
+   print, 'ERROR: invalid table header' 
+   return, -1
+ENDIF
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; get column names and put into a strarr
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+ncol = n_elements(delim_idx)-1
+col_names = strarr(ncol)
+col_names_orig = strarr(ncol)
+col_width = intarr(ncol)
+FOR i = 0, ncol-1 DO BEGIN 
+   col_width[i] = delim_idx[i+1]-delim_idx[i]-1
+   col_names[i] = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+   col_names_orig[i] = col_names[i]
+ENDFOR
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; check for duplicate column names, add "_idl_[i]"
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+cntr = intarr(ncol)*0 + 1
+FOR ik = 0, ncol-2 DO BEGIN
+   FOR ij = ik+1, ncol-1 DO BEGIN
+     IF (strcmp(col_names[ij],col_names[ik],/fold_case)) THEN BEGIN
+       col_names[ij] = col_names[ij] + '_idl_' + strn(cntr[ik])
+       cntr[ik] = cntr[ik] + 1
+       print,'WARNING: Duplicate column names, replacing occured'
+     ENDIF
+   ENDFOR
+ENDFOR
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; next line must be data types
+;;;; need error check if it isn't....
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+inline = textvar[lines_read]
+lines_read = lines_read+1
+
+;;;; check for no data after types line 
+IF (lines_read eq n_lines) THEN BEGIN 
+   print, 'ERROR: invalid table; no data' 
+   return, -1
+ENDIF
+
+line=inline
+
+IF strmid(line, 0, 1) NE '|' THEN BEGIN 
+   print, 'ERROR: invalid or missing data types line' 
+   return, -1
+ENDIF
+
+col_type_string = strarr(ncol)
+col_types_orig = strarr(ncol)
+col_type_code = intarr(ncol)
+
+FOR i = 0, ncol-1 DO BEGIN
+   ;;; strip spaces from data type and convert to all upper case
+   col_type_string[i] = strupcase(strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2))
+   col_types_orig[i] = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+   check = strmid(line,delim_idx[i+1],1)
+   IF check NE '|' THEN BEGIN 
+      print, 'ERROR: missing pipe in data types line'
+      IF keyword_set(debug) then stop
+      return, -1
+   ENDIF   
+   
+;;; convert data types to    
+   
+   CASE col_type_string[i] OF 
+      'INTEGER':  BEGIN
+         col_type_code[i] = 3      
+         print, 'Data type INTEGER is used.  For full compatibility with all IPAC services, please use INT, IN or I'
+      END
+      'INT':  col_type_code[i] = 3
+      'IN':  col_type_code[i] = 3
+      'I':  col_type_code[i] = 3
+      'LONG':  col_type_code[i] = 14
+      'LON':  col_type_code[i] = 14
+      'LO':  col_type_code[i] = 14
+      'L':  col_type_code[i] = 14
+      'FLOAT':   col_type_code[i] = 4
+      'FLOA':   col_type_code[i] = 4
+      'FLO':   col_type_code[i] = 4
+      'FL':   col_type_code[i] = 4      
+      'F':   col_type_code[i] = 4      
+      'REAL':   col_type_code[i] = 4
+      'REA':   col_type_code[i] = 4
+      'RE':   col_type_code[i] = 4
+      'R':   col_type_code[i] = 4
+      'DOUBLE':   col_type_code[i] = 5
+      'DOUBL':   col_type_code[i] = 5
+      'DOUB':   col_type_code[i] = 5
+      'DOU':   col_type_code[i] = 5
+      'DO':   col_type_code[i] = 5
+      'D':   col_type_code[i] = 5
+      'CHAR':  col_type_code[i] = 7
+      'CHA':  col_type_code[i] = 7
+      'CH':  col_type_code[i] = 7
+      'C':  col_type_code[i] = 7
+      'DATE':  col_type_code[i] = 7
+      'DAT':  col_type_code[i] = 7
+      'DA':  col_type_code[i] = 7
+      ELSE: BEGIN 
+         print, 'ERROR:  invalid data type = '+col_type_string[i]
+         IF keyword_set(debug) then stop
+         return,-1
+      ENDELSE     
+   ENDCASE
+     
+ENDFOR
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; create the basic structure
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+get_date, dte, /time
+info = create_struct('HEADER_Date_Created', string(dte))
+n_header_lines = 1
+
+n_header = n_elements(inheader)
+IF n_header GT 1 THEN BEGIN 
+   current = info
+   info = create_struct(current, 'HEADER_TABLE_HEADER', inheader[1:n_header-1])
+   n_header_lines = n_header_lines+1
+ENDIF
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;; Save the original column names and column types.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+current = info
+info = create_struct(current, 'HEADER_Col_Names_Orig', col_names_orig)
+n_header_lines = n_header_lines+1
+current = info
+info = create_struct(current, 'HEADER_Col_Types_Orig', col_types_orig)
+n_header_lines = n_header_lines+1
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;; Read next line.  If it starts with a pipe, it should be the units line.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+inline = textvar[lines_read]
+
+line=inline
+
+IF strmid(inline,0,1) EQ '|' THEN BEGIN 
+   lines_read = lines_read+1
+   data_units_string = strarr(ncol)
+   FOR i = 0, ncol-1 DO BEGIN
+   ;;; strip spaces from units
+      data_units_string[i] = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+      check = strmid(line,delim_idx[i+1],1)
+      IF check NE '|' THEN BEGIN 
+         print, 'ERROR: missing pipe in units line'
+         IF keyword_set(debug) then stop
+         return, -1
+      ENDIF   
+   endfor
+   current = info
+   info = create_struct(current, 'HEADER_Data_Units', data_units_string)
+   n_header_lines = n_header_lines+1
+;   remember to add lines to structure and to increment lines_read
+ENDIF $
+ELSE already_read = 1
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;; If the line was data units then read next line.  
+;;;;; If it starts with a pipe, it should be the nulls line
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+IF NOT(already_read) THEN BEGIN 
+   inline = textvar[lines_read]
+   line=inline
+
+   IF strmid(inline,0,1) EQ '|' THEN BEGIN 
+      lines_read = lines_read+1
+      null_value_string = strarr(ncol)
+      new_null_value_string = strarr(ncol)
+      FOR i = 0, ncol-1 DO BEGIN
+;;; strip spaces from nulls
+         null_value_string[i] = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+         check = strmid(line,delim_idx[i+1],1)
+         IF check NE '|' THEN BEGIN 
+            print, 'ERROR: missing pipe in nulls line'
+            IF keyword_set(debug) then stop
+            return, -1
+         ENDIF
+
+         IF (col_type_code[i] ne 7) THEN BEGIN
+            IF ( (col_type_code[i] eq 4) or (col_type_code[i] eq 5) ) THEN BEGIN
+               check_num = valid_num(null_value_string[i])
+               IF (check_num eq 0) THEN BEGIN
+                 new_null_value_string[i] = 'NaN'
+               ENDIF ELSE BEGIN
+                 new_null_value_string[i] = null_value_string[i]
+               ENDELSE
+            ENDIF ELSE BEGIN
+               check_num = valid_num(null_value_string[i], /integer)
+               IF (check_num eq 0) THEN BEGIN 
+                 new_null_value_string[i] = strn(null_num)
+               ENDIF ELSE BEGIN
+                 new_null_value_string[i] = null_value_string[i]
+               ENDELSE
+            ENDELSE
+         ENDIF ELSE new_null_value_string[i] = null_value_string[i]
+      ENDFOR 
+   ENDIF ELSE BEGIN 
+      null_value_string = strarr(ncol)+'no input null strings'
+      new_null_value_string = null_value_string
+      iwant = where ( ( (col_type_code eq 4) or (col_type_code eq 5) ),nwant)
+      if (nwant gt 0) then new_null_value_string[iwant] = 'NaN'
+      iwant = where ( ( (col_type_code eq 3) or (col_type_code eq 14) ),nwant)
+      if (nwant gt 0) then new_null_value_string[iwant] = strn(null_num)
+      already_read = 1
+   ENDELSE
+ENDIF ELSE BEGIN 
+   null_value_string = strarr(ncol)+'no input null strings'   
+   new_null_value_string = null_value_string
+   iwant = where ( ( (col_type_code eq 4) or (col_type_code eq 5) ),nwant)
+   if (nwant gt 0) then new_null_value_string[iwant] = 'NaN'
+   iwant = where ( ( (col_type_code eq 3) or (col_type_code eq 14) ),nwant)
+   if (nwant gt 0) then new_null_value_string[iwant] = strn(null_num)
+ENDELSE 
+
+current = info
+info = create_struct(current, 'HEADER_Null_Values', new_null_value_string)
+n_header_lines = n_header_lines+1
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;; set up data structure.  length of vectors is number of lines in 
+;;;;; file minus lines read so far
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+ndata = n_lines - lines_read
+
+IF ndata LE 0 THEN BEGIN
+   print, 'ERROR:  no data'
+   return, -1
+ENDIF
+
+FOR i = 0, ncol-1 DO BEGIN 
+   current = info
+   info = create_struct(current, $
+            IDL_VALIDNAME(col_names[i],/convert_all),make_array(ndata, type=col_type_code[i]))                     
+ENDFOR
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;; read data lines to put into structure
+;;;;; and pad the line if it isn't long enough for all columns
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;   
+
+lmax = 2.0d^63 - 1.0d
+lmin = -2.0d^63
+lmaxi = 2.0d^31 - 1.0d
+lmini = -2.0d^31
+   
+FOR j = 0, ndata-1 DO BEGIN 
+  
+   IF NOT(already_read) THEN BEGIN
+      inline = textvar[lines_read]
+      lines_read = lines_read + 1
+   ENDIF
+
+;;;; check for non-printable characters
+   IF ( (stregex(inline,string(9b)) ne -1) or $
+        (stregex(inline,string(7b)) ne -1) or $
+        (stregex(inline,string(8b)) ne -1) or $
+        (stregex(inline,string(10b)) ne -1) or $
+        (stregex(inline,string(11b)) ne -1) or $
+        (stregex(inline,string(12b)) ne -1) or $
+        (stregex(inline,string(13b)) ne -1) or $
+        (stregex(inline,string(27b)) ne -1) ) THEN BEGIN
+     print,'Non-printable character in data row = ',j
+     return,-1
+   ENDIF
+  
+   cur_len = strlen(inline)
+   IF cur_len LT name_line_length THEN BEGIN 
+      padlen = name_line_length - cur_len
+      pad = strjoin(replicate(' ', padlen))
+      line = inline+pad
+   ENDIF ELSE line=inline
+   
+   FOR i = 0, ncol-1 DO BEGIN
+      data_string = strtrim(strmid(line, delim_idx[i]+1, col_width[i]),2)
+      check = strmid(line,delim_idx[i],1)
+      IF check NE ' ' THEN BEGIN 
+         print, 'ERROR: misaligned columns (data under pipe)'
+         print, 'ERROR: data row, column = ',j,' , ',i
+         IF keyword_set(debug) THEN stop
+         return, -1
+      ENDIF   
+      IF (col_type_code[i] ne 7) THEN BEGIN
+         IF ( (col_type_code[i] eq 4) or (col_type_code[i] eq 5) ) THEN BEGIN
+            check_num = valid_num(data_string)
+            IF (check_num eq 0) THEN BEGIN
+               IF (data_string ne null_value_string[i]) THEN BEGIN
+                 data_string = new_null_value_string[i]
+                 print,'WARNING: Invalid data entry replaced by null value in row, column = ',j,', ',i
+               ENDIF ELSE data_string = new_null_value_string[i]
+            ENDIF
+;;;; Check floating point limits
+            IF (check_num ne 0) THEN BEGIN
+               check_lim = fix(data_string, type=5)
+               IF (finite(check_lim)) THEN BEGIN
+                  IF (col_type_code[i] eq 4) THEN BEGIN
+                     check_lim = fix(data_string, type=4)
+                     IF ( NOT(finite(check_lim)) ) THEN BEGIN
+                        data_string = new_null_value_string[i]
+                        print,'WARNING: Float overflow replaced by null value in row, column = ',j,', ',i
+                     ENDIF
+                  ENDIF
+               ENDIF ELSE BEGIN
+                  data_string = new_null_value_string[i]
+                  print,'WARNING: Double overflow replaced by null value in row, column = ',j,', ',i
+               ENDELSE
+            ENDIF 
+         ENDIF ELSE BEGIN
+            check_num = valid_num(data_string,/integer)
+            IF (check_num eq 0) THEN BEGIN
+               IF (data_string ne null_value_string[i]) THEN BEGIN
+                 data_string = new_null_value_string[i]
+                 print,'WARNING: Invalid data entry replaced by null value in row, column = ',j,', ',i
+               ENDIF ELSE data_string = new_null_value_string[i]
+           ENDIF
+;;;; Check integer limits
+           IF (check_num ne 0) THEN BEGIN
+              check_lim = fix(data_string, type=5)
+              IF ( (check_lim gt lmin) and (check_lim lt lmax) ) THEN BEGIN
+                 IF (col_type_code[i] eq 3) THEN BEGIN
+                    IF ( (check_lim le lmini) or (check_lim ge lmaxi) ) THEN BEGIN
+                       data_string = new_null_value_string[i]
+                       print,'WARNING: Integer overflow replaced by null value in row, column = ',j,', ',i
+                    ENDIF
+                 ENDIF
+              ENDIF ELSE BEGIN
+                 data_string = new_null_value_string[i]
+                 print,'WARNING: Long overflow replaced by null value in row, column = ',j,', ',i
+              ENDELSE
+           ENDIF 
+         ENDELSE
+      ENDIF
+      info.(i+n_header_lines)[j] = data_string
+   ENDFOR   
+   already_read=0
+ENDFOR
+
+return, info
+
+END
+
+
+   
+
diff --git a/Code/script_idl_mv/astrolib/read_key.pro b/Code/script_idl_mv/astrolib/read_key.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4e04bac1cc2e71dd05800740e30266fb434f7b47
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/read_key.pro
@@ -0,0 +1,129 @@
+FUNCTION read_key, wait
+;+
+; NAME:
+;	READ_KEY
+; PURPOSE:
+;	To read a keystroke and return its ASCII equivalent
+; EXPLANATION:
+;	If an ESCAPE sequence was produced and  the sequence is
+;	recognized (e.g. up arrow), then a code is returned.
+;
+;       This functionality is mostly made obsolete by the addition of the
+;       ESCAPE and KEY_NAME keywords to GET_KBRD in IDL V6.2
+;
+; CALLING SEQUENCE:
+;	key = READ_KEY(Wait)
+;
+; INPUTS:
+;	Wait  -  The wait flag.  If non-zero, execution is halted until a
+;	         key is struck.  If zero, execution returns immediately and
+;	         a zero is returned if there was no keystroke waiting in the
+;	         keyboard buffer.  If not specified, zero is assumed.
+;
+; OUTPUT:
+;	Returned - The key struck.  The ASCII code for non-escape sequences.
+;	           Escape sequence equivalents:
+;			Up Arrow     --  128
+;			Down Arrow   --  130
+;			Left Arrow   --  129
+;			Right Arrow  --  131
+;			Else         --    0
+;
+;	The return value is a byte value.
+;
+; MODIFICATION HISTORY:
+;	Written by Michael R. Greason, STX, 22 June 1990.
+;	Rewritten for a SUN workstation.  MRG, STX, 23 August 1990.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;			Check the input parameter.
+;
+IF (n_params(0) LT 1) THEN wait = 0
+;
+;			Get the keystroke.
+;
+key = byte(get_kbrd(wait))
+key = key[0]
+;
+;			If it is an ESCAPE, get the rest of it and 
+;			then decode it.
+;
+IF (key EQ 27B) THEN BEGIN
+	st = bytarr(10)
+;
+;				Get the rest of the escape sequence.
+;
+	i = 0
+	REPEAT BEGIN
+		key = byte(get_kbrd(0))
+		st[i] = key[0]
+		i = i + 1
+	ENDREP UNTIL (st[i-1] EQ 0B)
+;
+;				Decode the escape sequence.
+;
+	CASE string(st) OF
+		'[A' : key = 128B
+		'[B' : key = 130B
+		'[D' : key = 129B
+		'[C' : key = 131B
+		ELSE : BEGIN
+				 IF (i GT 1) THEN key = 0B ELSE key = 27B
+		       END
+	ENDCASE
+ENDIF
+;
+;			If it is a CSI, get the rest of it and 
+;			then decode it.
+;
+IF (key EQ '9B'XB) THEN BEGIN
+	st = bytarr(10)
+;
+;				Get the rest of the sequence.
+;
+	i = 0
+	REPEAT BEGIN
+		key = byte(get_kbrd(0))
+		st[i] = key[0]
+		i = i + 1
+	ENDREP UNTIL (st[i-1] EQ 0B)
+;
+;				Decode the sequence.
+;
+	CASE string(st) OF
+		'A' : key = 128B
+		'B' : key = 130B
+		'D' : key = 129B
+		'C' : key = 131B
+		ELSE : BEGIN
+				 IF (i GT 1) THEN key = 0B ELSE key = '9B'XB
+		       END
+	ENDCASE
+ENDIF
+;
+;			If it is a SS3, get the rest of it and 
+;			then decode it.
+;
+IF (key EQ '8F'XB) THEN BEGIN
+	st = bytarr(10)
+;
+;				Get the rest of the sequence.
+;
+	i = 0
+	REPEAT BEGIN
+		key = byte(get_kbrd(0))
+		st[i] = key[0]
+		i = i + 1
+	ENDREP UNTIL (st[i-1] EQ 0B)
+;
+;				Decode the sequence.
+;
+	CASE string(st) OF
+		ELSE : BEGIN
+				 IF (i GT 1) THEN key = 0B ELSE key = '8F'XB
+		       END
+	ENDCASE
+ENDIF
+;
+RETURN, key
+END
diff --git a/Code/script_idl_mv/astrolib/readcol.pro b/Code/script_idl_mv/astrolib/readcol.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e88900b74a0083bdf950bc50a688106115cd5180
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/readcol.pro
@@ -0,0 +1,369 @@
+pro readcol,name,v1,V2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15, $
+            v16,v17,v18,v19,v20,v21,v22,v23,v24,v25,v26,v27,v28,v29,v30,$
+            v31,v32,v33,v34,v35,v36,v37,v38,v39,v40,v41,v42,v43,v44,v45, $
+	    v46,v47,v48,v49,v50, COMMENT = comment, $
+            FORMAT = fmt, DEBUG=debug, SILENT=silent, SKIPLINE = skipline, $
+            NUMLINE = numline, DELIMITER = delimiter, NAN = NaN, $
+            PRESERVE_NULL = preserve_null, COUNT=ngood, NLINES=nlines, $
+            STRINGSKIP = skipstart, QUICK = quick, COMPRESS = compress
+;+
+; NAME:
+;       READCOL
+; PURPOSE:
+;       Read a free-format ASCII file with columns of data into IDL vectors 
+; EXPLANATION:
+;       Lines of data not meeting the specified format (e.g. comments) are 
+;       ignored.  By default, columns may be separated by commas or spaces.
+;
+;       Use READFMT to read a fixed-format ASCII file.   Use RDFLOAT for
+;       much faster I/O (but less flexibility).    Use FORPRINT to write 
+;       columns of data (inverse of READCOL).   
+;
+;       If you sure that all lines meet the specified format (excluding 
+;       commented and SKIPed lines) then the speed for reading large files
+;       can be significantly improved by setting the /QUICK keyword.
+;
+; CALLING SEQUENCE:
+;       READCOL, name, v1, [ v2, v3, v4, v5, ...  v50 , COMMENT=, /NAN
+;           DELIMITER= ,FORMAT = , /DEBUG ,  /SILENT , SKIPLINE = , NUMLINE = 
+;           COUNT =, STRINGSKIP= 
+;
+; INPUTS:
+;       NAME - Name of ASCII data file, scalar string.  
+;
+; OPTIONAL INPUT KEYWORDS:
+;       FORMAT - scalar string containing a letter specifying an IDL type
+;               for each column of data to be read.  Allowed letters are 
+;               A - string data, B - byte, D - double precision, F- floating 
+;               point, I - short integer, L - longword, LL - 64 bit integer, 
+;               U - unsigned short integer, UL - unsigned long integer 
+;               Z - longword hexadecimal, and X - skip a column.
+;
+;               Columns without a specified format are assumed to be floating 
+;               point.  Examples of valid values of FMT are
+;
+;       'A,B,I'        ;First column to read as a character string, then 
+;                       1 column of byte data, 1 column integer data
+;       'L,L,L,L'       ;Four columns will be read as longword arrays.
+;       ' '             ;All columns are floating point
+;
+;       If a FORMAT keyword string is not supplied, then all columns are 
+;       assumed to be floating point.
+;
+;       /SILENT - Normally, READCOL will display each line that it skips over.
+;               If SILENT is set and non-zero then these messages will be 
+;               suppressed.
+;       /DEBUG - If this keyword is non-zero, then additional information is
+;                printed as READCOL attempts to read and interpret the file.
+;       COMMENT - single character specifying comment character.   Any line 
+;                beginning with this character will be skipped.   Default is
+;                no comment lines.
+;       /COMPRESS - If set, then the file is assumed to be gzip compressed.
+;                The file is assumed to be compressed if it ends in '.gz'
+;       DELIMITER - Character(s) specifying delimiter used to separate 
+;                columns.   Usually a single character but, e.g. delimiter=':,'
+;                specifies that either a colon or comma as a delimiter. 
+;                Set DELIM = string(9b) to read tab separated data
+;                The default delimiter is either a comma or a blank.
+;       /NAN - if set, then an empty field will be read into a floating or 
+;                double numeric variable as NaN; by default an empty field is 
+;                converted to 0.0.
+;       /PRESERVE_NULL - If set, then spaces are considered to be valid fields,
+;                useful if the columns contain missing data.   Note that between
+;                April and December 2006, /PRESERVE_NULL was the default.
+;       /QUICK -  If set, then READCOL does not check that each individual line
+;                matches the supplied format.     This makes READCOL less 
+;                flexible but can provide a significant speed improvement when
+;                reading large files.                       
+;       SKIPLINE - Scalar specifying number of lines to skip at the top of file
+;               before reading.   Default is to start at the first line.
+;       NUMLINE - Scalar specifying number of lines in the file to read.  
+;               Default is to read the entire file
+;       STRINGSKIP - will skip all lines that begin with the specified string.
+;               (Unlike COMMENT this can be more than 1 character.) Useful to 
+;               skip over comment lines.
+;
+; OUTPUTS:
+;       V1,V2,V3,...V50 - IDL vectors to contain columns of data.
+;               Up to 50 columns may be read.  The type of the output vectors
+;               are as specified by FORMAT.
+;
+; OPTIONAL OUTPUT KEYWORDS:
+;       COUNT - integer giving the number of valid lines actually read
+;       NLINES - integer giving the total number of lines in the file 
+;                (as returned by FILE_LINES)
+;
+; EXAMPLES:
+;       Each row in a file position.dat contains a star name and 6 columns
+;       of data giving an RA and Dec in sexagesimal format.   Read into IDL 
+;       variables.   (NOTE: The star names must not include the delimiter 
+;       as a part of the name, no spaces or commas as default.)
+;
+;       IDL> FMT = 'A,I,I,F,I,I,F'
+;       IDL> READCOL,'position.dat',F=FMT,name,hr,min,sec,deg,dmin,dsec  
+;
+;       The HR,MIN,DEG, and DMIN variables will be integer vectors.
+;
+;       Alternatively, all except the first column could be specified as
+;       floating point.
+;
+;       IDL> READCOL,'position.dat',F='A',name,hr,min,sec,deg,dmin,dsec 
+;
+;       To read just the variables HR,MIN,SEC
+;       IDL> READCOL,'position.dat',F='X,I,I,F',HR,MIN,SEC
+;
+; RESTRICTIONS:
+;       This procedure is designed for generality and not for speed.
+;       If a large ASCII file is to be read repeatedly, it may be worth
+;       writing a specialized reader.
+;
+;       Columns to be read as strings must not contain the delimiter character
+;       (i.e. commas or spaces by default).   Either change the default 
+;       delimiter with the DELIMITER keyword, or use READFMT to read such files.
+;
+;       Numeric values are converted to specified format.  For example,
+;       the value 0.13 read with an 'I' format will be converted to 0.
+;
+; PROCEDURES CALLED
+;       GETTOK(), STRNUMBER()
+;       The version of STRNUMBER() must be after August 2006.
+; REVISION HISTORY:
+;       Written         W. Landsman                 November, 1988
+;       Modified             J. Bloch                   June, 1991
+;       (Fixed problem with over allocation of logical units.)
+;       Added SKIPLINE and NUMLINE keywords  W. Landsman    March 92
+;       Read a maximum of 25 cols.  Joan Isensee, Hughes STX Corp., 15-SEP-93.
+;       Call NUMLINES() function W. Landsman          Feb. 1996
+;       Added DELIMITER keyword  W. Landsman          Nov. 1999
+;       Fix indexing typos (i for k) that mysteriously appeared W. L. Mar. 2000
+;       Hexadecimal support added.  MRG, RITSS, 15 March 2000.
+;       Default is comma or space delimiters as advertised   W.L. July 2001
+;       Faster algorithm, use STRSPLIT if V5.3 or later  W.L.  May 2002
+;       Accept null strings separated by delimiter ,e.g. ',,,'
+;       Use SCOPE_VARFETCH instead of EXECUTE() for >V6.1  W.L. Jun 2005
+;       Added compile_opt idl2   W. L.  July 2005
+;       Added the NaN keyword   W. L      August 2006
+;       Added /PRESERVE_NULL keyword  W.L.  January 2007
+;       Assume since V5.6 (FILE_LINES available ) W.L. Nov 2007
+;       Added COUNT output keyword  W.L.  Aug 2008
+;       Added NLINES output keyword W.L.   Nov 2008
+;       Added SKIPSTART keyword  Stephane Beland January 2008
+;       Renamed SKIPSTART to STRINGSKIP to keep meaning of SKIP W.L. Feb 2008
+;       Assume since V6.1, SCOPE_VARFETCH available W.L. July 2009
+;       Read up to 40 columns W.L. Aug 2009
+;       Use pointers instead of SCOPE_VARFETCH. Fixes bug with
+;       IDL Workbench and runs 20% faster Douglas J. Marshall/W.L. Nov 2009
+;       Recognize  LL, UL, and ULL data types, don't use 'val' output from 
+;           STRNUMBER()   W.L.  Feb 2010
+;       Graceful return even if no valid lines are present D. Sahnow April 2010
+;       Ability to read tab separated data WL April 2010
+;       Free memory used by pointers  WL  July 2010
+;       Added /QUICK keyword  WL  Sep 2010
+;       Accept normal FORTRAN formats (e.g. F5.1) P. Noterdaeme/W.L Jan 2011
+;       Add COMPRESS keyword, IDL 6 notation W. Landsman/J. Bailin   Feb 2011
+;       Allow filename to be 1 element array W.Landsman/S.Antonille Apr 2011
+;       Feb 2010 change caused errors when reading blanks as numbers. 
+;                          W.L. July 2012
+;       Read up to 50 columns W.L.  March 2013
+;       Assume a compressed file if it ends in '.gz'  W.L.  Oct 2015
+;-
+  On_error,2                    ;Return to caller
+  compile_opt idl2
+
+  if N_params() lt 2 then begin
+    print,'Syntax - READCOL, name, v1, [ v2, v3,...v50, /NAN, DELIMITER=,/QUICK'
+    print,'        FORMAT= ,/SILENT  ,SKIPLINE =, NUMLINE = , /DEBUG, COUNT=]'
+     return
+  endif
+
+; Get number of lines in file
+
+  ngood = 0L                 ;Number of good lines
+  if N_elements(compress) EQ 0 then $
+        compress = strmid(name,2,3,/reverse) EQ '.gz'
+  nlines = FILE_LINES( name, COMPRESS=compress )
+  
+
+  if keyword_set(DEBUG) then $
+     message,'File ' + name+' contains ' + strtrim(nlines,2) + ' lines',/INF
+
+  if N_elements( SKIPLINE ) EQ 0 then skipline = 0
+  nlines = nlines - skipline
+  if nlines LE 0 then begin
+     message,'ERROR - File ' + name+' contains no data',/CON
+     return
+  endif     
+  if N_elements( NUMLINE) GT 0 then nlines = numline < nlines
+
+  if N_elements( SKIPSTART ) EQ 0 then begin
+     skipstart_flg=0 
+  endif else begin
+     skipstart_flg=1
+     nskipstart = strlen(skipstart)
+  endelse
+
+  ncol = N_params() - 1         ;Number of columns of data expected
+  vv = 'v' + strtrim( indgen(ncol)+1, 2)
+  nskip = 0
+
+  if N_elements(fmt) GT 0 then begin ;FORMAT string supplied?
+
+     if size(fmt,/tname) NE 'STRING' then $
+        message,'ERROR - Supplied FORMAT keyword must be a scalar string'
+;   Remove blanks from format string
+     frmt = strupcase(strcompress(fmt,/REMOVE))   
+     remchar, frmt, '('         ;Remove parenthesis from format
+     remchar, frmt, ')'           
+
+;   Determine number of columns to skip ('X' format)
+     pos = strpos(frmt, 'X', 0)
+
+     while pos NE -1 do begin
+        pos = strpos( frmt, 'X', pos+1)
+        nskip++
+     endwhile
+
+  endif else begin              ;Read everything as floating point
+
+     frmt = 'F'
+     if ncol GT 1 then for i = 1,ncol-1 do frmt += ',F'
+     if ~keyword_set( SILENT ) then message, $
+        'Format keyword not supplied - All columns assumed floating point',/INF
+
+  endelse
+
+  nfmt = ncol + nskip
+  idltype = intarr(nfmt)
+  bigarr = ptrarr(ncol)
+
+; Create output arrays according to specified formats
+
+  k = 0L                        ;Loop over output columns
+  hex = bytarr(nfmt)
+  for i = 0L, nfmt-1 do begin
+
+     fmt1 = gettok( frmt, ',' )
+     if fmt1 EQ '' then fmt1 = 'F' ;Default is F format
+     case strmid(fmt1,0,1) of 
+        'A':  idltype[i] = 7          
+        'D':  idltype[i] = 5
+        'F':  idltype[i] = 4
+        'I':  idltype[i] = 2
+        'B':  idltype[i] = 1
+        'L':  idltype[i] = strmid(fmt1,0,2) EQ 'LL' ? 14 : 3 
+	'U':  if strmid(fmt1,1,1) NE 'L' then idltype[i] = 12 else $
+	      idltype[i] = strmid(fmt1,2,1) EQ 'L' ? 15 : 13
+        'Z':  begin 
+           idltype[i] = 3       ;Hexadecimal
+           hex[i] = 1b
+        end
+        'X':  idltype[i] = 0    ;IDL type of 0 ==> to skip column
+        ELSE:  message,'Illegal format ' + fmt1 + ' in field ' + strtrim(i,2)
+     endcase
+
+; Define output arrays
+
+     if idltype[i] GT 0 then begin
+        bigarr[k] = ptr_new(make_array(nlines,type=idltype[i]))
+        k++
+     endif
+
+  endfor
+  goodcol = where(idltype)
+  idltype = idltype[goodcol]
+  check_numeric = (idltype NE 7)
+  check_comment = N_elements(comment) GT 0
+  openr, lun, name, /get_lun, compress=compress
+
+  temp = ' '
+  skip_lun,lun,skipline, /lines
+
+  if ~keyword_set(delimiter) then delimiter = ' ,'
+  
+  for j = 0L, nlines[0]-1 do begin
+     readf, lun, temp
+     if skipstart_flg then begin
+                                ; requested to skip lines starting with specifc string
+        if strmid(temp,0,nskipstart) eq skipstart then begin
+           ngood--
+           goto, BADLINE
+        endif
+     endif
+
+     if strlen(temp) LT ncol then begin ;Need at least 1 chr per output line
+        ngood--
+        if ~keyword_set(SILENT) then $
+           message,'Skipping Line ' + strtrim(skipline+j+1,2),/INF
+        goto, BADLINE 
+     endif
+
+     temp = strtrim(temp,1)     ;Remove leading spaces
+     if check_comment then if strmid(temp,0,1) EQ comment then begin
+        ngood--
+        if keyword_set(DEBUG) then $
+           message,'Skipping Comment Line ' + strtrim(skipline+j+1,2),/INF
+        goto, BADLINE 
+     endif
+
+       var = delimiter EQ string(9b) ?  $
+        strsplit(  temp,delimiter,/extract, preserve=preserve_null) $
+       :strsplit(strcompress(temp) ,delimiter,/extract, preserve=preserve_null) 
+     if N_elements(var) LT nfmt then begin 
+        if ~keyword_set(SILENT) then $ 
+           message,'Skipping Line ' + strtrim(skipline+j+1,2),/INF 
+        ngood--            
+        goto, BADLINE           ;Enough columns?
+     endif
+     var = var[goodcol]
+
+     k = 0
+     if keyword_set(quick) then $      ;Don't check for valid numeric values
+     
+         for i = 0L,ncol-1 do (*bigarr[i])[ngood] = var[i]   $
+    
+    else begin 
+     
+     
+     for i = 0L,ncol-1 do begin
+        
+        if check_numeric[i] then begin                      ;Check for valid numeric data
+           tst = strnumber(var[i],val,hex=hex[i],NAN=nan)   ;Valid number?
+           if ~tst  then begin                           ;If not, skip this line
+              if ~keyword_set(SILENT) then $ 
+                 message,'Skipping Line ' + strtrim(skipline+j+1,2),/INF 
+              ngood--
+              goto, BADLINE 
+           endif
+        endif 
+	if strlen(strtrim(var[i],2)) Eq 0 then begin
+	   if keyword_set(NAN) then (*bigarr[k])[ngood] = !VALUES.F_NAN else $
+	                            (*bigarr[k])[ngood] = 0 
+        endif else (*bigarr[k])[ngood] = var[i]
+        k++
+
+     endfor
+
+endelse
+     BADLINE:  ngood++
+
+  endfor
+
+  free_lun,lun
+  if ngood EQ 0 then begin 
+     message,'ERROR - No valid lines found for specified format',/INFORM
+     return
+  endif
+
+  if ~keyword_set(SILENT) then $
+     message,strtrim(ngood,2) + ' valid lines read', /INFORM  
+
+; Compress arrays to match actual number of valid lines
+  if ngood lt Nlines then for i=0,ncol-1 do $
+     (*bigarr[i]) = (*bigarr[i])[0:ngood-1]
+
+; Use SCOPE_VARFETCH to place into output variables..
+   for i=0,ncol-1 do $
+         (SCOPE_VARFETCH(vv[i],LEVEL=0)) = reform(*bigarr[i])
+    ptr_free, bigarr	 
+  return
+end
diff --git a/Code/script_idl_mv/astrolib/readfits.pro b/Code/script_idl_mv/astrolib/readfits.pro
new file mode 100644
index 0000000000000000000000000000000000000000..20c62c65ecca1aff501a0a56f7df3621895674f9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/readfits.pro
@@ -0,0 +1,598 @@
+;+
+; NAME:
+;       READFITS
+; PURPOSE:
+;       Read a FITS file into IDL data and header variables. 
+; EXPLANATION:
+;       READFITS() can read FITS files compressed with gzip or Unix (.Z) 
+;       compression.  FPACK ( http://heasarc.gsfc.nasa.gov/fitsio/fpack/ )
+;       compressed FITS files can also be read provided that the FPACK software
+;       is installed.
+;       See http://idlastro.gsfc.nasa.gov/fitsio.html for other ways of
+;       reading FITS files with IDL.   
+;
+; CALLING SEQUENCE:
+;       Result = READFITS( Filename/Fileunit,[ Header, heap, /NOSCALE, EXTEN_NO=,
+;                     NSLICE=, /SILENT , STARTROW =, NUMROW = , HBUFFER=,
+;                     /CHECKSUM, /COMPRESS, /FPACK, /No_Unsigned, NaNVALUE = ]
+;
+; INPUTS:
+;       Filename = Scalar string containing the name of the FITS file  
+;                 (including extension) to be read.   If the filename has
+;                  a *.gz extension, it will be treated as a gzip compressed
+;                  file.   If it has a .Z extension, it will be treated as a
+;                  Unix compressed file.     If Filename is an empty string then
+;                  the user will be queried for the file name.
+;                                   OR
+;       Fileunit - A scalar integer specifying the unit of an already opened
+;                  FITS file.  The unit will remain open after exiting 
+;                  READFITS().  There are two possible reasons for choosing 
+;                  to specify a unit number rather than a file name:
+;          (1) For a FITS file with many extensions, one can move to the 
+;              desired extensions with FXPOSIT() and then use READFITS().  This
+;              is more efficient than repeatedly starting at the beginning of 
+;              the file.
+;          (2) For reading a FITS file across a Web http: address after opening
+;              the unit with the SOCKET procedure 
+;
+; OUTPUTS:
+;       Result = FITS data array constructed from designated record.
+;                If the specified file was not found, then Result = -1
+;
+; OPTIONAL OUTPUT:
+;       Header = String array containing the header from the FITS file.
+;              If you don't need the header, then the speed may be improved by
+;              not supplying this parameter.    Note however, that omitting 
+;              the header can imply /NOSCALE, i.e. BSCALE and BZERO values
+;              may not be applied.
+;       heap = For extensions, the optional heap area following the main
+;              data array (e.g. for variable length binary extensions).
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /CHECKSUM - If set, then READFITS() will call FITS_TEST_CHECKSUM to 
+;                verify the data integrity if CHECKSUM keywords are present
+;                in the FITS header.   Cannot be used with the NSLICE, NUMROW
+;                or STARTROW keywords, since verifying the checksum requires 
+;               that all the data be read.  See FITS_TEST_CHECKSUM() for more
+;               information.
+;
+;       /COMPRESS - Signal that the file is gzip compressed.  By default, 
+;               READFITS will assume that if the file name extension ends in 
+;               '.gz' then the file is gzip compressed.   The /COMPRESS keyword
+;               is required only if the the gzip compressed file name does not 
+;               end in '.gz' or .ftz
+;              
+;       EXTEN_NO - non-negative scalar integer specifying the FITS extension to
+;               read.  For example, specify EXTEN = 1 or /EXTEN to read the 
+;               first FITS extension.   
+;
+;       /FPACK - Signal that the file is compressed with the FPACK software. 
+;               http://heasarc.gsfc.nasa.gov/fitsio/fpack/ ) By default, 
+;               (READFITS will assume that if the file name extension ends in 
+;               .fz that it is fpack compressed.     The FPACK software must
+;               be installed on the system 
+;   
+;        HBUFFER - Number of lines in the header, set this to slightly larger
+;                than the expected number of lines in the FITS header, to 
+;               improve performance when reading very large FITS headers. 
+;               Should be a multiple of 36 -- otherwise it will be modified
+;               to the next higher multiple of 36.   Default is 180
+;
+;       /NOSCALE - If present and non-zero, then the ouput data will not be
+;                scaled using the optional BSCALE and BZERO keywords in the 
+;                FITS header.   Default is to scale.
+;
+;       /NO_UNSIGNED - By default, if the header indicates an unsigned integer 
+;               (BITPIX = 16, BZERO=2^15, BSCALE=1) then READFITS() will output 
+;               an IDL unsigned integer data type (UINT).   But if /NO_UNSIGNED
+;               is set, then the data is converted to type LONG.  
+;
+;       NSLICE - An integer scalar specifying which N-1 dimensional slice of a 
+;                N-dimensional array to read.   For example, if the primary 
+;                image of a file 'wfpc.fits' contains a 800 x 800 x 4 array, 
+;                then 
+;
+;                 IDL> im = readfits('wfpc.fits',h, nslice=2)
+;                           is equivalent to 
+;                 IDL> im = readfits('wfpc.fits',h)
+;                 IDL> im = im[*,*,2]
+;                 but the use of the NSLICE keyword is much more efficient.
+;                 Note that any degenerate dimensions are ignored, so that the
+;                 above code would also work with a 800 x 800 x 4 x 1 array.
+;
+;       NUMROW -  Scalar non-negative integer specifying the number of rows 
+;                 of the image or table extension to read.   Useful when one 
+;                 does not want to read the entire image or table.  
+;
+;       POINT_LUN  -  Position (in bytes) in the FITS file at which to start
+;                 reading.   Useful if READFITS is called by another procedure
+;                 which needs to directly read a FITS extension.    Should 
+;                 always be a multiple of 2880, and not be used with EXTEN_NO
+;                 keyword.
+;
+;       /SILENT - Normally, READFITS will display the size the array at the
+;                 terminal.  The SILENT keyword will suppress this
+;
+;        STARTROW - Non-negative integer scalar specifying the row
+;               of the image or extension table at which to begin reading. 
+;               Useful when one does not want to read the entire table.  
+;
+;       NaNVALUE - This keyword is included only for backwards compatibility
+;                  with routines that require IEEE "not a number" values to be
+;                  converted to a regular value.
+;
+;       /UNIXPIPE - When a FileUnit is supplied to READFITS(), then /UNIXPIPE
+;                 indicates that the unit is to a Unix pipe, and that 
+;                 no automatic byte swapping is performed.
+;
+; EXAMPLE:
+;       Read a FITS file test.fits into an IDL image array, IM and FITS 
+;       header array, H.   Do not scale the data with BSCALE and BZERO.
+;
+;              IDL> im = READFITS( 'test.fits', h, /NOSCALE)
+;
+;       If the file contains a FITS extension, it could be read with
+;
+;              IDL> tab = READFITS( 'test.fits', htab, /EXTEN )
+;
+;       The function TBGET() can be used for further processing of a binary 
+;       table, and FTGET() for an ASCII table.
+;       To read only rows 100-149 of the FITS extension,
+;
+;              IDL> tab = READFITS( 'test.fits', htab, /EXTEN, 
+;                                   STARTR=100, NUMR = 50 )
+;
+;       To read in a file that has been compressed:
+;
+;              IDL> tab = READFITS('test.fits.gz',h)
+;
+; ERROR HANDLING:
+;       If an error is encountered reading the FITS file, then 
+;               (1) the system variable !ERROR_STATE.CODE is set negative 
+;                   (via the MESSAGE facility)
+;               (2) the error message is displayed (unless /SILENT is set),
+;                   and the message is also stored in !!ERROR_STATE.MSG
+;               (3) READFITS returns with a value of -1
+; RESTRICTIONS:
+;       (1) Cannot handle random group FITS
+;
+; NOTES:
+;       (1) If data is stored as integer (BITPIX = 16 or 32), and BSCALE
+;       and/or BZERO keywords are present, then the output array is scaled to 
+;       floating point (unless /NOSCALE is present) using the values of BSCALE
+;       and BZERO.   In the header, the values of BSCALE and BZERO are then 
+;       reset to 1. and 0., while the original values are written into the 
+;       new keywords O_BSCALE and O_BZERO.     If the BLANK keyword was
+;       present (giving the value of undefined elements *prior* to the 
+;       application of BZERO and BSCALE) then the *keyword* value will be
+;       updated with the values of BZERO and BSCALE.
+;       
+;       (2) The use of the NSLICE keyword is incompatible with the NUMROW
+;       or STARTROW keywords.
+;
+;       (3) On some Unix shells, one may get a "Broken pipe" message if reading
+;        a Unix compressed (.Z) file, and not reading to the end of the file 
+;       (i.e. the decompression has not gone to completion).     This is an 
+;        informative message only, and should not affect the output of READFITS.   
+; PROCEDURES USED:
+;       Functions:   SXPAR()
+;       Procedures:  MRD_SKIP, SXADDPAR, SXDELPAR
+;
+; MODIFICATION HISTORY:
+;       Original Version written in 1988, W.B. Landsman   Raytheon STX
+;       Revision History prior to October 1998 removed          
+;       Major rewrite to eliminate recursive calls when reading extensions
+;                  W.B. Landsman  Raytheon STX                    October 1998
+;       Add /binary modifier needed for Windows  W. Landsman    April 1999
+;       Read unsigned datatypes, added /no_unsigned   W. Landsman December 1999
+;       Output BZERO = 0 for unsigned data types   W. Landsman   January 2000
+;       Update to V5.3 (see notes)  W. Landsman                  February 2000
+;       Fixed logic error in use of NSLICE keyword  W. Landsman  March 2000
+;       Fixed byte swapping for Unix compress files on little endian machines
+;                                    W. Landsman    April 2000
+;       Added COMPRESS keyword, catch IO errors W. Landsman September 2000
+;       Option to read a unit number rather than file name W.L    October 2001
+;       Fix undefined variable problem if unit number supplied W.L. August 2002
+;       Don't read entire header unless needed   W. Landsman  Jan. 2003
+;       Added HBUFFER keyword    W. Landsman   Feb. 2003
+;       Added CHECKSUM keyword   W. Landsman   May 2003
+;       Restored NaNVALUE keyword for backwards compatibility,
+;               William Thompson, 16-Aug-2004, GSFC
+;       Recognize .ftz extension as compressed  W. Landsman   September 2004
+;       Fix unsigned integer problem introduced Sep 2004 W. Landsman Feb 2005
+;       Don't modify header for unsigned integers, preserve double precision
+;           BSCALE value  W. Landsman March 2006
+;       Use gzip instead of compress for Unix compress files W.Landsman Sep 2006
+;       Call MRD_SKIP to skip bytes on different file types W. Landsman Oct 2006
+;       Make ndata 64bit for very large files E. Hivon/W. Landsman May 2007
+;       Fixed bug introduced March 2006 in applying Bzero C. Magri/W.L. Aug 2007
+;       Check possible 32bit overflow when using NSKIP W. Landsman Mar 2008
+;       Always reset BSCALE, BZERO even for unsigned integers W. Landsman May 2008
+;       Make ndata 64bit for very large extensions J. Schou/W. Landsman Jan 2009
+;       Use PRODUCT() to compute # of data points  W. Landsman  May 2009
+;       Read FPACK compressed file via UNIX pipe. W. Landsman May 2009
+;       Fix error using NUMROW,STARTROW with non-byte data, allow these 
+;           keywords to be used with primary array  W. Landsman July 2009
+;       Ignore degenerate trailing dimensions with NSLICE keyword W.L. Oct 2009
+;       Add DIALOG_PICKFILE() if filename is an empty string  W.L. Apr 2010
+;       Set BLANK values *before* applying BSCALE,BZERO, use short-circuit
+;           operators  W.L. May 2010
+;      Skip extra SPAWN with FPACK decompress J. Eastman, W.L. July 2010
+;      Fix possible problem when startrow=0 supplied J. Eastman/W.L. Aug 2010
+;      First header is not necessarily primary if unit supplied WL Jan 2011
+;      Fix test for 'SIMPLE' at beginning of header WL November 2012
+;      Fix problem passing extensions with > 2GB WL, M. Carlson August 2013
+;-
+function READFITS, filename, header, heap, CHECKSUM=checksum, $ 
+                   COMPRESS = compress, HBUFFER=hbuf, EXTEN_NO = exten_no, $
+                   NOSCALE = noscale, NSLICE = nslice, $
+                   NO_UNSIGNED = no_unsigned,  NUMROW = numrow, $
+                   POINTLUN = pointlun, SILENT = silent, STARTROW = startrow, $
+                   NaNvalue = NaNvalue, FPACK = fpack, UNIXpipe=unixpipe
+
+  On_error,2                    ;Return to user
+  compile_opt idl2
+  On_IOerror, BAD
+
+; Check for filename input
+
+   if N_params() LT 1 then begin                
+      print,'Syntax - im = READFITS( filename, [ h, heap, /NOSCALE, /SILENT,'
+      print,'                 EXTEN_NO =, STARTROW = , NUMROW=, NSLICE = ,'
+      print,'                 HBUFFER = ,/NO_UNSIGNED, /CHECKSUM, /COMPRESS]'
+      return, -1
+   endif
+
+   unitsupplied = size(filename,/TNAME) NE 'STRING'
+
+; Set default keyword values
+
+   silent = keyword_set( SILENT )
+   do_checksum = keyword_set( CHECKSUM )
+   if N_elements(exten_no) EQ 0 then exten_no = 0
+
+;  Check if this is a Unix compressed file.   (gzip files are handled 
+;  separately using the /compress keyword to OPENR).
+
+    if N_elements(unixpipe) EQ 0 then unixpipe = 0                  
+    if unitsupplied then unit = filename else begin
+    len = strlen(filename)
+    if len EQ 0 then begin
+        filename =dialog_pickfile(filter=['*.fit*;*.fts*;*.img*'], $
+	title='Please select a FITS file',/must_exist)
+        len = strlen(filename)
+    endif 
+    ext = strlowcase(strmid(filename,len-3,3))
+    gzip = (ext EQ '.gz') || (ext EQ 'ftz')
+    compress = keyword_set(compress) || gzip[0]
+    unixZ =  (strmid(filename, len-2, 2) EQ '.Z') 
+    fcompress = keyword_set(fpack) || ( ext EQ '.fz') 
+    unixpipe = unixZ || fcompress	      
+
+ 
+;  Go to the start of the file.
+
+   openr, unit, filename, ERROR=error,/get_lun, $
+                COMPRESS = compress, /swap_if_little_endian
+   if error NE 0 then begin
+        message,/con,' ERROR - Unable to locate file ' + filename
+        return, -1
+   endif
+
+;  Handle Unix or Fpack compressed files which will be opened via a pipe using
+;  the SPAWN command.     
+
+        if unixZ then begin
+                free_lun, unit
+                spawn, 'gzip -cd '+filename, unit=unit                 
+                gzip = 1b
+
+        endif else if fcompress then begin 
+	        free_lun, unit
+		spawn,'funpack -S ' + filename, unit=unit,/sh
+                if eof(unit) then begin 
+		    message,'Error spawning FPACK decompression',/CON
+		    free_lun,unit
+		    return,-1
+		endif    
+	endif	
+  endelse
+  if N_elements(POINTLUN) GT 0 then mrd_skip, unit, pointlun
+
+  doheader = arg_present(header) || do_checksum
+  if doheader  then begin
+          if N_elements(hbuf) EQ 0 then hbuf = 180 else begin
+                  remain = hbuf mod 36
+                  if remain GT 0 then hbuf = hbuf + 36-remain
+           endelse
+  endif else hbuf = 36
+
+  for ext = 0L, exten_no do begin
+               
+;  Read the next header, and get the number of bytes taken up by the data.
+
+       block = string(replicate(32b,80,36))
+       w = [-1]
+       if ((ext EQ exten_no) && (doheader)) then header = strarr(hbuf) $
+                                             else header = strarr(36)
+       headerblock = 0L
+       i = 0L      
+
+       while w[0] EQ -1 do begin
+          
+       if EOF(unit) then begin 
+            message,/ CON, $
+               'EOF encountered attempting to read extension ' + strtrim(ext,2)
+            if ~unitsupplied then free_lun,unit
+            return,-1
+       endif
+
+      readu, unit, block
+      headerblock++
+      w = where(strlen(block) NE 80, Nbad)
+      if (Nbad GT 0) then begin
+           message,'Warning-Invalid characters in header',/INF,NoPrint=Silent
+           block[w] = string(replicate(32b, 80))
+      endif
+
+      w = where(strcmp(block,'END     ',8), Nend)
+      if (headerblock EQ 1) || ((ext EQ exten_no) && (doheader)) then begin
+              if Nend GT 0 then  begin
+             if headerblock EQ 1 then header = block[0:w[0]]   $
+                                 else header = [header[0:i-1],block[0:w[0]]]
+             endif else begin
+                header[i] = block
+                i += 36
+                if i mod hbuf EQ 0 then $
+                              header = [header,strarr(hbuf)]
+           endelse
+          endif
+
+      if (ext EQ 0 ) && ~((N_elements(pointlun) GT 0) || unitsupplied ) then $
+             if strmid( header[0], 0, 8)  NE 'SIMPLE  ' then begin
+              message,/CON, $
+                 'ERROR - Header does not contain required SIMPLE keyword'
+                if ~unitsupplied then free_lun, unit
+                return, -1
+      endif
+
+      endwhile          
+; Get parameters that determine size of data region.
+                
+       bitpix =  sxpar(header,'BITPIX')
+       byte_elem = abs(bitpix)/8               ;Bytes per element
+       naxis  = sxpar(header,'NAXIS')
+       gcount = sxpar(header,'GCOUNT') > 1
+       pcount = sxpar(header,'PCOUNT')
+                
+       if naxis GT 0 then begin 
+            dims = sxpar( header,'NAXIS*')           ;Read dimensions
+	    ndata = product(dims,/integer)
+       endif else ndata = 0
+                
+       nbytes = byte_elem * gcount * (pcount + ndata)
+
+;  Move to the next extension header in the file.   Use MRD_SKIP to skip with
+;  fastest available method (POINT_LUN or readu) for different file
+;  types (regular, compressed, Unix pipe, socket) 
+
+      if ext LT exten_no then begin
+                nrec = long64((nbytes + 2879) / 2880)
+                if nrec GT 0 then mrd_skip, unit, nrec*2880L    
+       endif
+       endfor
+
+ case BITPIX of 
+           8:   IDL_type = 1          ; Byte
+          16:   IDL_type = 2          ; Integer*2
+          32:   IDL_type = 3          ; Integer*4
+          64:   IDL_type = 14         ; Integer*8
+         -32:   IDL_type = 4          ; Real*4
+         -64:   IDL_type = 5          ; Real*8
+        else:   begin
+                message,/CON, 'ERROR - Illegal value of BITPIX (= ' +  $
+                strtrim(bitpix,2) + ') in FITS header'
+                if ~unitsupplied then free_lun,unit
+                return, -1
+                end
+  endcase     
+ 
+  if nbytes EQ 0 then begin
+        if ~SILENT then message, $
+                "FITS header has NAXIS or NAXISi = 0,  no data array read",/CON
+        if do_checksum then begin
+             result = FITS_TEST_CHECKSUM(header, data, ERRMSG = errmsg)
+             if ~SILENT then begin
+               case result of 
+                1: message,/INF,'CHECKSUM keyword in header is verified'
+               -1: message,/CON, errmsg
+                else: 
+                endcase
+              endif
+        endif
+        if ~unitsupplied then free_lun, unit
+        return,-1
+ endif
+
+; Check for FITS extensions, GROUPS
+
+ groups = sxpar( header, 'GROUPS' ) 
+ if groups then message,NoPrint=Silent, $
+           'WARNING - FITS file contains random GROUPS', /INF
+
+; If an extension, did user specify row to start reading, or number of rows
+; to read?
+
+   if N_elements(STARTROW) EQ 0 then startrow = 0       ;updated Aug 2010
+   if naxis GE 2 then nrow = dims[1] else nrow = ndata
+   if N_elements(NUMROW) EQ 0 then numrow = nrow
+   if do_checksum then if ((startrow GT 0) || $
+      (numrow LT nrow) || (N_elements(nslice) GT 0)) then begin 
+      message,/CON, $
+      'Warning - CHECKSUM not applied when STARTROW, NUMROW or NSLICE is set'
+      do_checksum = 0
+   endif 
+
+   if exten_no GT 0 then begin
+        xtension = strtrim( sxpar( header, 'XTENSION' , Count = N_ext),2)
+        if N_ext EQ 0 then message, /INF, NoPRINT = Silent, $
+                'WARNING - Header missing XTENSION keyword'
+   endif 
+
+   if ((startrow NE 0) || (numrow NE nrow)) then begin
+        if startrow GE dims[1] then begin
+           message,'ERROR - Specified starting row ' + strtrim(startrow,2) + $
+          ' but only ' + strtrim(dims[1],2) + ' rows in extension',/CON
+           if ~unitsupplied then free_lun,unit
+           return,-1
+        endif 
+        dims[1] = dims[1] - startrow    
+        dims[1] = dims[1] < numrow
+        sxaddpar, header, 'NAXIS2', dims[1]
+	if startrow GT 0 then mrd_skip, unit, byte_elem*startrow*dims[0]
+
+    endif else if (N_elements(NSLICE) EQ 1) then begin
+ 
+        ldim = naxis-1
+        lastdim = dims[ldim]
+	while lastdim EQ 1 do begin
+	      ldim = ldim-1
+	      lastdim = dims[ldim]
+	endwhile
+	       if nslice GE lastdim then begin 
+	      message,/CON, $
+        'ERROR - Value of NSLICE must be less than ' + strtrim(lastdim,2)
+               if ~unitsupplied then free_lun, unit
+ 	      return, -1
+	endif      
+        dims = dims[0:ldim-1]
+        for i = ldim,naxis-1 do sxdelpar,header,'NAXIS' + strtrim(i+1,2)
+        naxis = ldim
+        sxaddpar,header,'NAXIS' + strtrim(ldim,2),1
+        ndata = ndata/lastdim
+        nskip = long64(nslice)*ndata*byte_elem
+	if Ndata GT 0 then mrd_skip, unit, nskip  
+  endif
+
+
+  if ~SILENT then begin   ;Print size of array being read
+
+         if exten_no GT 0 then message, $
+                     'Reading FITS extension of type ' + xtension, /INF  
+	 if N_elements(dims) EQ 1 then $
+	 st = 'Now reading ' + strtrim(dims,2) + ' element vector' else $	          
+	 st = 'Now reading ' + strjoin(strtrim(dims,2),' by ') + ' array'
+         if (exten_no GT 0) && (pcount GT 0) then st = st + ' + heap area'
+         message,/INF,st   
+   endif
+
+; Read Data in a single I/O call.   Only need byteswapping for data read with
+; bidirectional pipe.
+
+    data = make_array( DIM = dims, TYPE = IDL_type, /NOZERO)
+    readu, unit, data
+    if unixpipe  then swap_endian_inplace,data,/swap_if_little
+    if (exten_no GT 0) && (pcount GT 0) then begin
+        theap = sxpar(header,'THEAP')
+        skip = theap - N_elements(data)
+        if skip GT 0 then begin 
+                temp = bytarr(skip,/nozero)
+                readu, unit, skip
+        endif
+        heap = bytarr(pcount*gcount*byte_elem)
+        readu, unit, heap
+        if do_checksum then $
+        result = fits_test_checksum(header,[data,heap],ERRMSG=errmsg)
+    endif else if do_checksum then $
+        result = fits_test_checksum(header, data, ERRMSG = errmsg)
+    if ~unitsupplied then free_lun, unit
+    if do_checksum then if ~SILENT then begin
+        case result of 
+        1: message,/INF,'CHECKSUM keyword in header is verified'
+       -1: message,/CON, 'CHECKSUM ERROR! ' + errmsg
+        else: 
+        endcase
+    endif
+
+; Scale data unless it is an extension, or /NOSCALE is set
+; Use "TEMPORARY" function to speed processing.  
+
+   do_scale = ~keyword_set( NOSCALE )
+   if (do_scale && (exten_no GT 0)) then do_scale = xtension EQ 'IMAGE' 
+   if do_scale then begin
+
+          if bitpix GT 0 then $
+                blank = sxpar( header, 'BLANK', Count = N_blank) $
+		else N_blank = 0
+ 
+          Bscale = sxpar( header, 'BSCALE' , Count = N_bscale)
+          Bzero = sxpar(header, 'BZERO', Count = N_Bzero )
+         if (N_blank GT 0) && ((N_bscale GT 0) || (N_Bzero GT 0)) then $
+                 sxaddpar,header,'O_BLANK',blank,' Original BLANK value'
+       
+ 
+ 
+; Check for unsigned integer (BZERO = 2^15) or unsigned long (BZERO = 2^31)
+
+          if ~keyword_set(No_Unsigned) then begin
+            no_bscale = (Bscale EQ 1) || (N_bscale EQ 0)
+            unsgn_int = (bitpix EQ 16) && (Bzero EQ 32768) && no_bscale
+            unsgn_lng = (bitpix EQ 32) && (Bzero EQ 2147483648) && no_bscale
+            unsgn = unsgn_int || unsgn_lng
+           endif else unsgn = 0
+
+          if unsgn then begin
+                    if unsgn_int then begin  
+                        data =  uint(data) - 32768US
+			if N_blank then blank = uint(blank) - 32768US 
+		   endif else  begin 
+                         data = ulong(data) - 2147483648UL
+			if N_blank then blank = ulong(blank) - 2147483648UL
+		   endelse 
+		   if N_blank then sxaddpar,header,'BLANK',blank
+                   sxaddpar, header, 'BZERO', 0
+                   sxaddpar, header, 'O_BZERO', Bzero,' Original BZERO Value'
+               
+          endif else begin
+ 
+          if N_Bscale GT 0  then $ 
+               if ( Bscale NE 1. ) then begin
+	           if size(Bscale,/TNAME) NE 'DOUBLE' then $
+                      data *= float(Bscale) else $ 
+		      data *= Bscale 
+		  if N_blank then blank *= bscale    
+                  sxaddpar, header, 'BSCALE', 1.
+                   sxaddpar, header, 'O_BSCALE', Bscale,' Original BSCALE Value'
+		   
+               endif
+
+         if N_Bzero GT 0  then $
+               if (Bzero NE 0) then begin
+	             if size(Bzero,/TNAME) NE 'DOUBLE' then $
+                      data += float(Bzero) else $    ;Fixed Aug 07
+                      data +=  Bzero
+		      if N_blank then blank += bzero
+                     sxaddpar, header, 'BZERO', 0.
+                     sxaddpar, header, 'O_BZERO', Bzero,' Original BZERO Value'
+               endif
+        
+        endelse
+	if  N_blank then sxaddpar,header,'BLANK',blank
+        endif
+
+
+; Return array.  If necessary, first convert NaN values.
+
+        if n_elements(nanvalue) eq 1 then begin
+            w = where(finite(data,/nan),count)
+            if count gt 0 then data[w] = nanvalue
+        endif
+        return, data    
+
+; Come here if there was an IO_ERROR
+    
+ BAD:   print,!ERROR_STATE.MSG
+        if (~unitsupplied) && (N_elements(unit) GT 0) then free_lun, unit
+        if N_elements(data) GT 0 then return,data else return, -1
+
+ end 
diff --git a/Code/script_idl_mv/astrolib/readfmt.pro b/Code/script_idl_mv/astrolib/readfmt.pro
new file mode 100644
index 0000000000000000000000000000000000000000..efdd2d5f5ce42e65bd95aa25663418e2e61574c9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/readfmt.pro
@@ -0,0 +1,297 @@
+pro readfmt,name,fmt,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15, $
+                 v16,v17,v18,v19,v20,v21,v22,v23,v24,v25, $
+                 SILENT = silent, DEBUG = debug, SKIPLINE = skipline, $
+                 NUMLINE = numline
+;+
+; NAME:
+;     READFMT
+; PURPOSE:
+;       Quickly read a fixed format ASCII data file into IDL variables. 
+; EXPLANATION:
+;       Lines of data not meeting the specified format (e.g. comments) are
+;       ignored.  
+;      
+;       To read a free format ASCII data file use the procedures 
+;       READCOL or RDFLOAT.     To print (formatted or free) columns of data
+;       use the procedure FORPRINT.   
+;
+; CALLING SEQUENCE:
+;       READFMT, name, fmt, v1,[ v2, v3, v4, ..., v25 , 
+;                          /SILENT, /DEBUG, SKIPLINE= , NUMLINE =]
+;
+; INPUTS:
+;       NAME - Name of ASCII data file.  An extension of .DAT is assumed,
+;               if not supplied.
+;       FMT - scalar string containing a valid FORTRAN read format.
+;               Must include a field length specification.   Cannot include
+;               internal parenthesis.  A format field must be included for 
+;               each output vector.   Multiple format fields are allowed, but
+;               the repetition factor must be less than 100, (.i.e. 19X is 
+;               allowed but 117X is illegal) 
+;
+;       Examples of valid FMT values are
+;               FMT = 'A7,3X,2I4'  or FMT = '1H ,5I7,2A7'
+;       Examples of INVALID FMT values are
+;               FMT = 'A7,B3'           ;'B' is not a valid FORTRAN format
+;               FMT = 'A7,2(I3,F5.1)'   ;Internal parenthesis not allowed
+;               FMT = 'A7,F,I'          ;Field length not included
+;
+; OUTPUTS:
+;       V1,V2,V3,V4... - IDL vectors to contain columns of data.
+;               Up to 25 output vectors may be read.  The type of the output 
+;               vectors are specified by FMT.
+;
+; OPTIONAL KEYWORD INPUTS:
+;       /SILENT - If this keyword is set and non-zero, then certain terminal
+;               output is suppressed while reading the file
+;       /DEBUG - Set this keyword to display additional information while
+;               reading the file.
+;       SKIPLINE - Scalar specifying number of lines to skip at the top of
+;               file before reading. Default is to start at first line
+;       NUMLINE - Scalar specifying number of lines in the file to read.
+;               Default is to read the entire file 
+;
+; EXAMPLES:
+;       Each row in a fixed-format file POSITION.DAT contains a 5 character 
+;       star name  and 6 columns of data giving an RA and Dec in sexagesimal 
+;       format.   A possible format for such data might be
+;
+;       IDL> FMT = 'A5,2I3,F5.1,2x,3I3'    
+;       and the file could be quickly read with
+;
+;       IDL> READFMT,'POSITION', fmt, name, hr, min, sec, deg, dmin, dsec 
+;    
+;       NAME will be a string vector,SEC will be a floating point vector, and
+;       the other vectors will be of integer type.
+;
+; RESTRICTIONS:
+;       This procedure is designed for generality and not for speed.
+;       If a large ASCII file is to be read repeatedly, it may be worth
+;       writing a specialized reader.
+;
+; NOTES:
+;       When reading a field with an integer format I<n>, the output vector is
+;               byte  - if n = 1
+;               integer*2 - if 1 < n < 5
+;               integer*4  - in all other cases
+;       Octal ('O') and hexadecimal ('Z') formats are read into longwords
+;
+; PROCEDURE CALLS:
+;       GETTOK(), REMCHAR, ZPARCHECK
+;
+; REVISION HISTORY:
+;       Written         W. Landsman                 November, 1988
+;       Added SKIPLINE and NUMLINE keywords         March 92
+;       Allow up to 25 columns to be read           June 92
+;       Call NUMLINES() function                    Feb 1996
+;       Recognize 'O' and 'Z' formats  W. Landsman   September 1997
+;       Recognize 'G' format, use SKIP_LUN   W. Landsman  May 2010
+;-
+  On_error,2
+  compile_opt idl2
+
+  if N_params() LT 3 then begin
+      print,'Syntax - readfmt, name, fmt, v1,[ v2, v3, v4...v25, '
+      print,'         /SILENT, /DEBUG, SKIPLINE =, NUMLINE = ]'
+      return
+  endif
+
+  zparcheck, 'READFMT', fmt, 2, 7, 0, 'FORMAT string'
+
+; Get number of lines in file 
+
+   nlines = FILE_LINES( name )
+ 
+  if ~keyword_set( SKIPLINE ) then skipline = 0
+  if keyword_set( NUMLINE) then nlines = numline < nlines else $
+               nlines = nlines - skipline
+ 
+  if nlines LE 0 then begin
+        message,'ERROR - File ' + name+' contains no valid data',/CON
+	return
+   endif   
+  ncol = N_params() - 2           ;Number of columns of data expected
+  ii = strtrim(indgen(ncol)+1,2)
+  frmt = strtrim( strupcase(fmt), 2 )      ;Working FORMAT string
+
+; If format string is of the form "$(...)"  then remove dollar sign and
+; parenthesis 
+
+  remchar, frmt, '$'                      ;Remove dollar sign
+  if strmid(frmt,0,1) EQ '(' then $
+          frmt = strmid( frmt,1,strlen(frmt)-1 )
+
+  if strmid(frmt,strlen(frmt)-1,1) EQ ')' then $
+          frmt = strmid(frmt,0,strlen(frmt)-1 )
+
+  fmt1 = '(' + frmt + ')'              ;Now make a valid read format
+
+
+; Create output arrays according to specified formats
+
+   k = 0L                             ;Loop over output columns
+  REPEAT BEGIN
+
+    fmt_1 = gettok(frmt,',')
+    vtype = strmid( fmt_1, 0, 1)
+    ndup = 1
+    if (strnumber(vtype,val) EQ 1) then begin   ;Test for multiple format
+
+        ndup = val
+        vtype = strmid(fmt_1,1,1)
+
+        if (strnumber(vtype,val) EQ 1) then begin
+
+               ndup = 10*ndup+ val
+               vtype = strmid(fmt_1,2,1)
+
+        endif
+
+        if vtype EQ '(' then $
+           message,'Parenthesis within format string not allowed'
+
+    endif   
+  
+    for j = 1L,ndup do begin
+     CASE vtype OF 
+
+     'A':  begin
+
+        tst = strnumber(strmid(fmt_1,1, strlen(fmt_1)-1), nfield)
+        if (tst EQ 0) or (strlen(fmt_1) LT 2) then $ 
+             message,'String format must include a field length'
+    
+         nfield = fix(nfield)
+         idltype = 7
+         end
+
+   'D':  idltype = 5
+
+   'E':  idltype = 4
+
+   'F':  idltype = 4
+   
+   'G':  idltype = 4
+
+   'I':  begin                       ;Decide whether BYTE, INTEGER or LONG
+
+         pos = strpos(fmt_1,vtype)
+         len = fix(strmid( fmt_1, pos+1, strlen(fmt_1)-pos-1))
+         if len EQ 1 then idltype = 1 $
+           else if len LT 5 then idltype = 2 $
+                            else idltype = 3
+
+         end
+
+   'H':  goto, NO_VAR 
+   
+   'O':  idltype = 3
+   
+   'Z':  idltype = 3
+
+   'X':  goto, NO_VAR               ;No variable declaration needed
+
+   ELSE: message,'ERROR - Illegal format '+fmt_1 +' in field ' + strtrim(k,2)
+
+ endcase
+
+; Define output arrays
+
+   st = 'v'+ ii[k] +'= make_array(nlines, type = idltype)'  
+   tst = execute(st)
+   st = 'x'+ ii[k] +'= make_array(1,type = idltype)'  
+   tst = execute(st)
+   k = k+1
+   if k EQ ncol then goto, DONE          ;Normal exit
+  endfor
+NO_VAR:  
+
+  ENDREP until frmt EQ ''
+
+  message,'ERROR - ' + strtrim(ncol,2)+ ' output vectors supplied but only ' + $
+         strtrim(k,2) + ' FORMAT fields specified'
+
+DONE: 
+  
+  openr, LUN, name, /get_lun
+  ngood = 0L
+  skip_lun,lun,skipline,/lines
+
+  On_IOerror, BAD_LINE  
+
+
+  for j = 0L,nlines-1 do begin
+
+   badline = 1
+
+   case ncol of                  ;Can't use ON_IOERROR with EXECUTE statement
+;                                 so have to list all the possibilities 
+   1:   readf,LUN,f = fmt1,x1        
+   2:   readf,LUN,f = fmt1,x1,x2
+   3:   readf,LUN,f = fmt1,x1,x2,x3
+   4:   readf,LUN,f = fmt1,x1,x2,x3,x4
+   5:   readf,LUN,f = fmt1,x1,x2,x3,x4,x5
+   6:   readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6
+   7:   readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7
+   8:   readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8
+   9:   readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9
+   10:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10
+   11:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11
+   12:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12
+   13:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13
+   14:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14
+   15:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15
+   16:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,$
+                      x16
+   17:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,$
+                      x16,x17
+   18:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15, $
+                   x16,x17,x18
+   19:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15, $
+                   x16,x17,x18,x19 
+   20:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15, $
+                   x16,x17,x18,x19,x20 
+   21:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15, $
+                   x16,x17,x18,x19,x20,x21 
+   22:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15, $
+                   x16,x17,x18,x19,x20,x21,x22 
+   23:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15, $
+                   x16,x17,x18,x19,x20,x21,x22,x23
+   24:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15, $
+                   x16,x17,x18,x19,x20,x21,x22,x23,x24 
+   25:  readf,LUN,f = fmt1,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15, $
+                   x16,x17,x18,x19,x20,x21,x22,x23,x24,x25 
+
+  ENDCASE 
+
+    for i = 0L, ncol-1 do begin
+
+        st ='v' + ii[i] + '[ngood] = x'+ii[i]
+        tst = execute(st)
+
+     endfor
+
+     ngood = ngood + 1
+     badline = 0
+BAD_LINE: 
+     if badline then if ~keyword_set(SILENT) then $
+                 message,'Error reading line ' + strtrim(skipline+ j+1,2),/CON
+  endfor
+  free_lun, LUN
+
+  if ngood EQ 0L then message, $
+                'ERROR - No valid lines found with specified format'
+  if ~keyword_set( SILENT)  then $
+          message, strtrim(ngood,2) + ' valid lines read',/INF
+
+; Compress arrays to match actual number of valid lines
+
+  for i = 0L, ncol-1 do begin 
+
+      var ='v'+ii[i]
+      tst = execute(var + '='+ var+ '[0:ngood-1]')
+
+  endfor
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/recpol.pro b/Code/script_idl_mv/astrolib/recpol.pro
new file mode 100644
index 0000000000000000000000000000000000000000..50701a19b580038b7670db9dd6be673162b9739d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/recpol.pro
@@ -0,0 +1,63 @@
+;-------------------------------------------------------------
+;+
+; NAME:
+;       RECPOL
+; PURPOSE:
+;       Convert 2-d rectangular coordinates to polar coordinates.
+; CATEGORY:
+; CALLING SEQUENCE:
+;       recpol, x, y, r, a
+; INPUTS:
+;       x, y = vector in rectangular form.           in
+; KEYWORD PARAMETERS:
+;       Keywords:
+;         /DEGREES means angle is in degrees, else radians.
+; OUTPUTS:
+;       r, a = vector in polar form: radius, angle.  out
+; COMMON BLOCKS:
+; NOTES:
+; MODIFICATION HISTORY:
+;       R. Sterner. 18 Aug, 1986.
+;       Johns Hopkins University Applied Physics Laboratory.
+;       RES 13 Feb, 1991 --- added /degrees.
+;       R. Sterner, 30 Dec, 1991 --- simplified.
+;       R. Sterner, 25 May, 1993 --- Fixed atan (0,0) problem.
+;
+; Copyright (C) 1986, Johns Hopkins University/Applied Physics Laboratory
+; This software may be used, copied, or redistributed as long as it is not
+; sold and this copyright notice is reproduced on each copy made.  This
+; routine is provided as is without any express or implied warranties
+; whatsoever.  Other limitations apply as described in the file disclaimer.txt.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;-------------------------------------------------------------
+ 
+ 
+	pro recpol, x, y, r, a, help=hlp, degrees=degrees
+ 
+	if (n_params(0) lt 4) or keyword_set(hlp) then begin
+	  print,' Convert 2-d rectangular coordinates to polar coordinates.
+	  print,' recpol, x, y, r, a
+	  print,'   x, y = vector in rectangular form.           in'
+	  print,'   r, a = vector in polar form: radius, angle.  out'
+	  print,' Keywords:'
+	  print,'   /DEGREES means angle is in degrees, else radians.'
+	  return
+	endif
+ 
+	;----------------------------------------------------------------
+	;  Angle complicated because atan won't take (0,0) and
+	;  also because want to keep angle in 0 to 360 (2 pi) range.
+	;----------------------------------------------------------------
+	w = where((x ne 0) or (y ne 0), count)	; Where not both X,Y eq 0.
+	a = x*0.				; Output angle array.
+	if count gt 0 then a[w]=atan(y[w],x[w])	; Find angles.
+	w = where(a lt 0, count)		; find A < 0 and fix.
+	if count gt 0 then a[w]= a[w]+2*!dpi	; add 2 pi to angles < 0.
+ 
+	r = sqrt(x^2 + y^2)			; Find radii.
+ 
+	if keyword_set(degrees) then a = a*!radeg
+ 
+	return
+	end
diff --git a/Code/script_idl_mv/astrolib/rem_dup.pro b/Code/script_idl_mv/astrolib/rem_dup.pro
new file mode 100644
index 0000000000000000000000000000000000000000..14ce10972b8b35e3fecbf5d58f062c21bdd11d0b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/rem_dup.pro
@@ -0,0 +1,104 @@
+function rem_dup, a, flag
+;+
+; NAME:	
+;	REM_DUP
+; PURPOSE:  
+;	Function to remove duplicate values from a vector.
+;
+; CALLING SEQUENCE:
+;	result = rem_dup( a, [ flag ] )
+;
+; INPUTS:
+;	a - vector of values from which duplicates are to be found
+;	flag - (optional) if supplied then when duplicates occur,
+;		the one with the largest value of flag is selected.
+;		If not supplied the the first occurence of the value
+;		in a is selected.     Should be a vector with the same
+;               number of elements as a.
+;
+; OUTPUT:
+;	A vector of subscripts in a is returned.  Each subscript
+;	points to a selected value such that a(rem_dup(a,flag))
+;	has no duplicates.
+;
+; SIDE EFFECTS:
+;	The returned subscripts will sort the values in a in ascending
+;	order with duplicates removed.
+;
+; EXAMPLES:
+;
+;	Remove duplicate values in vector a.
+;	 	a = a[ rem_dup(a)]
+;
+;	Remove duplicates in vector WAVE.  When duplicate values
+;	are found, select the one with the largest intensity, INTE.
+;
+;		sub = rem_dup( wave, inte)
+;		wave = wave[sub]      
+;		inte = inte[sub]
+;
+; NOTES:
+;	The UNIQ function in the User's Library uses a faster algorithm,
+;	but has no equivalent of the "flag" parameter.    Also, note that
+;       REM_DUP() gives the index of the *first* equal value found, while
+;       UNIQ() gives the index of the *last* equal value found.
+;
+; MODIFICATION HISTORY:
+;	D. Lindler  Mar. 87
+;	11/16/90 JKF ACC - converted to IDL Version 2.
+;	August 1997  -- Changed loop index to type LONG
+;	October 1997 -- Also changed NGOOD index to LONG
+;       April 2007 - Use faster algorithm when Flag vector not set, W. Landsman
+;       Feb 2011 - Remove spurious line W.L.
+;       Jan 2012 - Call BSORT() to ensure original order maintained for equal
+;            values
+;-
+;-------------------------------------------------------------------------------
+;
+ compile_opt idl2
+ On_error,2
+ npar = N_params()		;number of input parameters supplied
+ if npar EQ 0 then begin
+ 	print,'Syntax -  b = rem_dup( a, [ flag ] )'
+ 	return, -1
+ end
+
+ n = N_elements(a)			;number of values in a
+ if n lt 2 then return, lonarr(1)	;only one value in a
+ sub = Npar GE 2 ? sort(a) : bsort(a)                      ;sorted subscripts
+ aa = a[sub]			;sorted a
+;
+; loop on aa
+;
+ val = aa[0]			;first value processed
+ if npar GE 2 then begin 
+ 
+ good = lonarr(n)		;values to keep
+ ngood = 0L			;number kept.
+ff = flag[sub]			;sorted flags
+ f = ff[0]			;flag for first value
+ for i = 1L, n-1 do begin
+	if aa[i] ne val then begin
+		val = aa[i]
+		f = ff[i]
+		ngood++
+		good[ngood] = i
+	  end else begin
+		if ff[i] gt f then begin
+			f = ff[i]
+			good[ngood] = i
+		endif
+	endelse
+ endfor
+  good = good[0:ngood]
+
+ endif else begin
+ 
+   good    = where( shift( aa, 1) NE aa, count)
+   if count EQ 0 then good = 0
+
+ endelse
+ 
+ return, sub[good]		;return subscripts in original a
+ end
+
diff --git a/Code/script_idl_mv/astrolib/remchar.pro b/Code/script_idl_mv/astrolib/remchar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..15977356e7b1fa8b2faccf971f7d7767b63c61dc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/remchar.pro
@@ -0,0 +1,46 @@
+pro remchar,st,char	;Remove character
+;+
+; NAME:
+;	REMCHAR
+; PURPOSE:
+;	Remove all appearances of character (char) from string (st)
+;
+; CALLING SEQUENCE:
+;	REMCHAR, ST, CHAR
+;
+; INPUT-OUTPUT:
+;	ST  - String from which character will be removed, scalar or vector  
+; INPUT:
+;	CHAR- Single character to be removed from string or all elements of a
+;		string array 
+;
+; EXAMPLE:
+;	If a = 'a,b,c,d,e,f,g' then 
+;
+;	IDL> remchar,a, ','
+;
+;      will give a = 'abcdefg'
+;
+; REVISIONS HISTORY
+;	Written D. Lindler October 1986
+;	Test if empty string needs to be returned   W. Landsman  Feb 1991
+;	Work on string arrays    W. Landsman   August 1997
+;	Avoid 32 bit integer overflow K. Tolbert/W. Landsman Feb 2007
+;-
+ compile_opt idl2                             
+ if N_params() LT 2 then begin
+     print,'Syntax - REMCHAR, string, character'
+     return
+ endif
+
+ bchar = byte(char) & bchar = bchar[0]          ;Convert character to byte
+
+ for i = 0L,N_elements(st)-1 do  begin
+
+ bst = byte(st[i])
+ good = where( bst NE bchar, Ngood)
+ if Ngood GT 0 then st[i] = string(bst[good]) else st[i] = ''
+
+ endfor
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/remove.pro b/Code/script_idl_mv/astrolib/remove.pro
new file mode 100644
index 0000000000000000000000000000000000000000..97f2a758ee9fab414a776ecbf50c1542f5323459
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/remove.pro
@@ -0,0 +1,124 @@
+pro remove,index, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, $
+     v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25
+;+
+; NAME:
+;       REMOVE
+; PURPOSE:
+;       Contract a vector or up to 25 vectors by removing specified elements   
+; CALLING SEQUENCE:
+;       REMOVE, index, v1,[ v2, v3, v4, v5, v6, ... v25]     
+; INPUTS:
+;       INDEX - scalar or vector giving the index number of elements to
+;               be removed from vectors.  Duplicate entries in index are
+;               ignored.    An error will occur if one attempts to remove
+;               all the elements of a vector.     REMOVE will return quietly
+;               (no error message) if index is !NULL or undefined.
+;
+; INPUT-OUTPUT:
+;       v1 - Vector or array.  Elements specifed by INDEX will be 
+;               removed from v1.  Upon return v1 will contain
+;               N fewer elements, where N is the number of distinct values in
+;               INDEX.
+;
+; OPTIONAL INPUT-OUTPUTS:
+;       v2,v3,...v25 - additional vectors containing
+;               the same number of elements as v1.  These will be
+;               contracted in the same manner as v1.
+;
+; EXAMPLES:
+;       (1) If INDEX = [2,4,6,4] and V = [1,3,4,3,2,5,7,3] then after the call
+;
+;               IDL> remove,index,v      
+;
+;       V will contain the values [1,3,3,5,3]
+;
+;       (2) Suppose one has a wavelength vector W, and three associated flux
+;       vectors F1, F2, and F3.    Remove all points where a quality vector,
+;       EPS is negative
+;
+;               IDL> bad = where( EPS LT 0, Nbad)
+;               IDL> if Nbad GT 0 then remove, bad, w, f1, f2, f3
+;
+; METHOD:
+;       If more than one element is to be removed, then HISTOGRAM is used
+;       to generate a 'keep' subscripting vector.    To minimize the length of 
+;       the subscripting vector, it is only computed between the minimum and 
+;       maximum values of the index.   Therefore, the slowest case of REMOVE
+;       is when both the first and last element are removed.
+;
+; REVISION HISTORY:
+;       Written W. Landsman        ST Systems Co.       April 28, 1988
+;       Cleaned up code          W. Landsman            September, 1992
+;       Major rewrite for improved speed   W. Landsman    April 2000
+;       Accept up to 25 variables, use SCOPE_VARFETCH internally
+;              W. Landsman   Feb 2010
+;       Fix occasional integer overflow problem  V. Geers  Feb 2011
+;       Quietly return if index is !null or undefined W.L. Aug 2011
+;             
+;-
+ On_error,2
+ compile_opt idl2,strictarrsubs
+
+ npar = N_params()
+ nvar = npar-1
+ if npar LT 2 then begin
+      print,'Syntax - remove, index, v1, [v2, v3, v4,..., v25]'
+      return
+ endif
+
+ if N_elements(index) EQ 0 then return
+
+  vv = 'v' + strtrim( indgen(nvar)+1, 2) 
+  npts = N_elements(v1)
+   
+  max_index = max(index, MIN = min_index)
+
+ if ( min_index LT 0 ) || (max_index GT npts-1) then message, $
+             'ERROR - Index vector is out of range'
+
+ if ( max_index Eq min_index ) then begin   ;Remove only 1 element?
+     Ngood = 0  
+    if npts EQ 1 then message, $ 
+         'ERROR - Cannot delete all elements from a vector'
+  endif else begin 
+         
+
+;  Begin case where more than 1 element is to be removed.   Use HISTOGRAM
+;  to determine then indices to keep
+
+ nhist = max_index - min_index +1 
+
+ hist = histogram( index)      ;Find unique index values to remove
+ keep = where( hist EQ 0, Ngood ) + min_index
+
+ if ngood EQ 0 then begin 
+    if ( npts LE nhist ) then message, $
+          'ERROR - Cannot delete all elements from a vector'
+  endif 
+ endelse
+
+ imin = min_index - 1
+ imax = max_index + 1
+ i0 = (min_index EQ 0) + 2*(max_index EQ npts-1) 
+ case i0 of 
+ 3: begin
+    for i=0, nvar-1 do  $
+         (SCOPE_VARFETCH(vv[i],LEVEL=0)) = $
+	 (SCOPE_VARFETCH(vv[i],LEVEL=0))[keep]
+     return	 
+     end
+
+ 1:  ii = Ngood EQ 0 ? imax + lindgen(npts-imax) : $
+                      [keep, imax + lindgen(npts-imax) ]
+ 2:  ii = Ngood EQ 0 ? lindgen(imin+1)               :  $
+                       [lindgen(imin+1), keep ]
+ 0:   ii = Ngood EQ 0 ? [lindgen(imin+1), imax + lindgen(npts-imax) ]  : $
+                      [lindgen(imin+1), keep, imax + lindgen(npts-imax) ]
+ endcase 
+
+      for i=0,nvar-1 do  $
+         (SCOPE_VARFETCH(vv[i],LEVEL=0)) =    $
+	        (SCOPE_VARFETCH(vv[i],LEVEL=0))[ii]
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/repchr.pro b/Code/script_idl_mv/astrolib/repchr.pro
new file mode 100644
index 0000000000000000000000000000000000000000..94f1c84b2e4a41566cad6f9d69e287a31bc81fd8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/repchr.pro
@@ -0,0 +1,60 @@
+;+
+; NAME:
+;       REPCHR()
+; PURPOSE:
+;       Replace all occurrences of one character with another in a string.
+;
+; CALLING SEQUENCE:
+;       New_String = repchr( In_string, OldChar, [NewChar])
+; INPUTS:
+;       in_string = original text string, scalar or array
+;       OldChar = character to replace.     If the OldChar contains
+;            more than 1 character, only the first character is used.
+; OPTIONAL INPUT:
+;       newchar = single character to replace it with.
+;                The default is a single space
+; OUTPUTS:
+;       new_string = same as in_string, but with all occurrences of old
+;               replaced  by newchar
+; EXAMPLE:
+;       in_string = ['lettuce, tomato, grape']
+;       print, repchr( in_string, ',')   ;replace comma with space
+;            'lettuce tomato grape'
+; NOTES: 
+;       Use REPSTR() to replace words rather than a single character
+;
+;	    For a more sophisticated routine that allows regular expressions look
+;	    at MG_STRREPLACE() http://docs.idldev.com/idllib/strings/mg_streplace.html
+;
+;       Since IDL 8.4 one can use the .REPLACE() method for string variables
+;
+;       Note that REPCHR() is the fastest (though least versatile) of these routines, 
+;       because the length of the string never changes, allowing direct manipulation of 
+;       byte values.
+; MODIFICATION HISTORY:
+;       Written W. Landsman   April 2016
+;       Adapted from similar code by  R. Sterner JHUAPL Oct, 1986
+;-
+
+
+	function repchr, In_String, OldChar, NewChar
+ 
+	if N_params() LT 2 then begin
+	  print,' Replace all occurrences of one character with another '+$
+	    'in a text string.'
+	  print,' new_string = repchr(In_String, OldChar, [NewChar])'
+	  return, -1
+	endif
+ 
+	bString = byte(In_String)			   ; convert string to a byte array.
+	b_OldChar = byte(OldChar)			   ; convert OldChar to byte.
+
+	g = where(bString EQ b_OldChar[0],Ng)  ; find occurrences of char 1.
+	IF Ng EQ 0 then return,In_string	   ; if none, return input string.
+
+    if N_elements(NewChar) EQ 0 then NewChar = ' '   ;Default new char is a space
+    b_NewChar = byte(NewChar)              ;Convert NewChar to byte
+	bstring[g] = b_NewChar[0]			   ; replace oldchar by newchar.
+
+	return, STRING(bString)		   ; return new string.
+	END
diff --git a/Code/script_idl_mv/astrolib/repstr.pro b/Code/script_idl_mv/astrolib/repstr.pro
new file mode 100644
index 0000000000000000000000000000000000000000..326a671622320241c04b9718cdd6b646cc8f371d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/repstr.pro
@@ -0,0 +1,87 @@
+function repstr,obj,in,out
+;+
+; NAME:
+;   REPSTR
+; PURPOSE:
+;   Replace all occurences of one substring by another.
+; EXPLANATION:
+;   Meant to emulate the string substitution capabilities of text editors
+;
+;   Obsolete since introduction of the REPLACE method for string variables 
+;   introduced in IDL 8.4
+;
+;	For a more sophisticated routine that allows regular expressions look
+;	at MG_STRREPLACE() http://docs.idldev.com/idllib/strings/mg_streplace.html
+; CALLING SEQUENCE:
+;	result = repstr( obj, in, out )
+;
+; INPUT PARAMETERS:
+;   obj    = object string for editing, scalar or array
+;	in     = substring of 'obj' to be replaced, scalar 
+;
+; OPTIONAL INPUT PARMETER:
+;	out    = what 'in' is replaced with, scalar.   If not supplied
+;		then out = '', i.e. 'in' is not replaced by anything. 
+;
+; OUTPUT PARAMETERS:
+;	Result returned as function value.  Input object string
+;	not changed unless assignment done in calling program.
+;
+; PROCEDURE:
+;	Searches for 'in', splits 'obj' into 3 pieces, reassembles
+;	with 'out' in place of 'in'.  Repeats until all cases done.
+;
+; EXAMPLE:
+;	If a = 'I am what I am' then print,repstr(a,'am','was')
+;	will give 'I was what I was'.
+;
+; MODIFICATION HISTORY:
+;	Written by Robert S. Hill, ST Systems Corp., 12 April 1989.
+;	Accept vector object strings, W. Landsman   HSTX,   April, 1996
+;   Convert loop to LONG, vectorize STRLEN call W. Landsman June 2002
+;   Correct bug in optimization, case where STRLEN(OBJ) EQ
+;         STRLEN(IN), C. Markwardt, Jan 2003
+;   Fixed problem when multiple replacements extend the string length
+;                 D. Finkbeiner, W. Landsman  April 2003
+;   Allow third parameter to be optional again W. Landsman  August 2003
+;   Remove limitation of 9999 characters, C. Markwardt Dec 2003
+;   Test for empty "in" string (causing infinite loop) W. Landsman Jan 2010
+;   Streamline code W Landsman Dec 2011
+;   Use string .replace method in IDL 8.4 or later  W. Landsman Feb 2015
+;   Use CALL_METHOD so that it still compiles in IDL 7.1 W.Landsman Aug 2015
+;-
+ On_error,2
+ compile_opt idl2
+ 
+ if N_params() LT 2 then begin
+	print,'Syntax - result = REPSTR( obj, in, out )'
+	return, obj
+ endif
+
+ if !VERSION.RELEASE GE '8.4' then return,call_method('replace',obj,in,out)
+ if N_elements(out) EQ 0 then out = ''
+ l1 = strlen(in)
+ if l1 EQ 0 then message,'ERROR - empty input string not allowed'
+ l2 = strlen(out)
+ diflen = l2- l1
+ Nstring = N_elements(obj)
+ object = obj
+ lo = strlen(object) - l1             ;Last character needed to look at 
+ for i= 0L ,Nstring-1 do begin
+ last_pos = 0
+ pos = 0
+ while ( pos LE lo[i]) do begin
+   pos = strpos(object[i],in,last_pos)
+   if (pos GE 0) then begin
+	      first_part = strmid(object[i],0,pos)
+	      last_part  = strmid(object[i],pos+l1)
+	      object[i] = first_part + out + last_part
+              last_pos = pos + l2
+              lo[i] += diflen      ;Length of string may have changed
+   endif else break
+ endwhile
+ endfor
+
+ return,object
+
+ end
diff --git a/Code/script_idl_mv/astrolib/resistant_mean.pro b/Code/script_idl_mv/astrolib/resistant_mean.pro
new file mode 100644
index 0000000000000000000000000000000000000000..da623638d007963090c1688eee6a2a2df9a0056d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/resistant_mean.pro
@@ -0,0 +1,202 @@
+PRO RESISTANT_Mean,Y,CUT,Mean,Sigma,Num_Rej,goodvec = goodvec, $
+                  dimension=dimension, double=double,sumdim=sumdim, $
+		  wused=wused, Silent = silent
+;+
+; NAME:
+;    RESISTANT_Mean  
+;
+; PURPOSE:
+;    Outlier-resistant determination of the mean and standard deviation. 
+; 
+; EXPLANATION:
+;    RESISTANT_Mean trims away outliers using the median and the median 
+;    absolute deviation.    An approximation formula is used to correct for
+;    the truncation caused by trimming away outliers
+;
+; CALLING SEQUENCE:
+;    RESISTANT_Mean, ARRAY, Sigma_CUT, Mean, Sigma_Mean, Num_RejECTED
+;                         [/DOUBLE, DIMENSION= , GOODVEC = ]
+; INPUT ARGUMENT:
+;       ARRAY    = Vector or array to average, NaN values will be ignored
+;       Sigma_CUT = Data more than this number of standard deviations from the
+;               median is ignored. Suggested values: 2.0 and up.
+;
+; OUTPUT ARGUMENT:
+;       Mean  = the mean of the input array, numeric scalar,    If the 
+;            DIMENSION keyword is set, then MEAN will be an array with one
+;            less dimension than the input.
+; OPTIONAL OUTPUTS:
+;	Sigma_Mean = the approximate standard deviation of the mean, numeric 
+;            scalar.  This is the Sigma of the distribution divided by sqrt(N-1)
+;            where N is the number of unrejected points. The larger
+;            SIGMA_CUT, the more accurate. It will tend to underestimate the 
+;            true uncertainty of the mean, and this may become significant for 
+;            cuts of 2.0 or less. 
+;       Num_RejECTED = the number of points trimmed, integer scalar
+; OPTIONAL INPUT KEYWORDS:
+;      /DOUBLE - If set, then all calculations are performed internally 
+;            in double precision.  
+;      DIMENSION - for a multi-dimensional array, the dimension over which to
+;            take the mean, starting at 1. If not set, then the scalar mean
+;            over all elements is used. If this argument is present, the result
+;            is an array with one less dimension than Array. For example, if 
+;            the dimensions of Array are N1, N2, N3, and Dimension is 2, then 
+;            the dimensions of the result are (N1, N3)    
+;      /SILENT - Set to suppress error messages, e.g.if all values in the array
+;            are NaN
+;      SUMDIM - Obsolete synonym for DIMENSION
+; OPTIONAL OUTPUT KEYWORD:
+;       Goodvec -  Indices of non-trimmed elements of the input vector
+;       Wused - synonym for Goodvec (for solarsoft compatibility)
+; EXAMPLE:
+;       IDL> a = randomn(seed, 10000)    ;Normal distribution with 10000 pts
+;       IDL> RESISTANT_Mean,a, 3, mean, meansig, num    ;3 Sigma clipping    
+;       IDL> print, mean, meansig,num
+; 
+;       The mean should be near 0, and meansig should be near 0.01 ( =
+;        1/sqrt(10000) ).     
+; PROCEDURES USED:
+;       MEAN() - compute simple mean, in Exelis library
+; REVISION HISTORY:
+;       Written, H. Freudenreich, STX, 1989; Second iteration added 5/91.
+;       Use MEDIAN(/EVEN)    W. Landsman   April 2002
+;       Correct conditional test, higher order truncation correction formula
+;                R. Arendt/W. Landsman   June 2002
+;       New truncation formula for sigma H. Freudenriech  July 2002
+;       Divide Sigma_mean by Num_good rather than Npts W. Landsman/A. Conley
+;                          January 2006
+;       Use of double precision S. Bianchi February 2008
+;       More double precision B. Carcich December 2009
+;       Added DIMENSION keyword (from M. Desnoyer) B. Carcich December 2009
+;       Use IDL's MEAN() function instead of AVG() W. Landsman Jan 2012
+;       Use of Dimension keyword yielded transpose of correct value
+;                     W. Landsman  July 2012
+;       Added NaN keyword to MEAN() call N. Crouzet/WL  April 2013
+;       Allow a row/column to be all NaN values N. Crouzet/WL  April 2013
+;       Use of DIMENSION keyword yielded wrong answer for non-square arrays
+;                       D. Cottingham  December 2014
+;-
+
+ On_Error,2
+ compile_opt idl2
+ if N_params() LT 3 then begin
+     print,'Syntax - Resistant_Mean, Vector, Sigma_cut, Mean, [ Sigma_mean, ' 
+     print,'                                  Num_Rejected,  GOODVEC=,'
+     print,'                                  DIMEN=, /DOUBLE]'
+     return
+ endif
+
+ sz = size(Y)
+ indouble = size(Y,/tname) EQ 'DOUBLE'          ;Is input double precision?
+ 
+; Average over a single dimension?
+   if N_elements(DIMENSION)  then DIM = long(DIMENSION[0]) $
+   else if n_elements(SUMDIM) then DIM = long(SUMDIM[0]) 
+ if (sz[0] gt 1L) && (sz[0] lt 5L) && (N_elements(DIM) EQ 1) then begin
+   if (DIM lt 1L) || (dim gt sz[0]) then begin
+     message,/continue, 'Invalid dimension number'
+     print,'Syntax - Resistant_Mean, Vector, Sigma_cut, Mean'
+     print,'        , [ Sigma_mean, Num_Rejected, Dimension={1|2} ]'
+     return
+   endif
+   ;;;
+   od=[ sz[0:dim-1], sz[dim+1:sz[0]+1] ]  ;;; [buffer, i,j,k,m, buffer]
+   od=[ od[1:sz[0]-1], 1, 1, 1]         ;;; [i,j,k,m]
+   rowlen = sz[dim]
+   colhgt = sz[sz[0]+2]/rowlen
+   sd = size([0d0])
+   Num_Rej = make_array(od[0],od[1],od[2],od[3],val=0L)
+   if keyword_set(double) || indouble then v=0d0 else v=0.
+   Mean = make_array(od[0],od[1],od[2],od[3],val=v)
+   Sigma = Mean
+   ;;;
+   if n_elements(CUT) eq colhgt then iwCUT = lindgen(colhgt) $
+   else iwCUT = make_array(colhgt,val=0L)
+   ;;;
+   ijkL=0L
+  
+   for L=0L,od[3]-1L do begin
+   for k=0L,od[2]-1L do begin
+   for j=0L,od[1]-1L do begin
+   for i=0L,od[0]-1L do begin
+     thisCut = CUT[iwCUT[ijkL]]
+     case dim of
+     1: RESISTANT_Mean,Y[*,i,j,k,L],thisCUT,M,S,N,double=double,/Silent
+     2: RESISTANT_Mean,Y[i,*,j,k,L],thisCUT,M,S,N,double=double,/Silent
+     3: RESISTANT_Mean,Y[i,j,*,k,L],thisCUT,M,S,N,double=double,/Silent
+     4: RESISTANT_Mean,Y[i,j,k,*,L],thisCUT,M,S,N,double=double,/Silent
+     5: RESISTANT_Mean,Y[i,j,k,L,*],thisCUT,M,S,N,double=double,/Silent
+     endcase
+     
+     ;;;
+     Mean[ijkL] = M
+     Sigma[ijkL] = S
+     Num_Rej[ijkL] = N
+     ijkL++
+   endfor
+   endfor
+   endfor
+   endfor
+   return
+ endif
+
+ MADscale = 0.6745d0
+ MADscale2 = 0.8d0
+ MADlim = 1d-24
+ Sigcoeff = [ -0.15405d0, +0.90723d0, -0.23584d0, +0.020142d0 ]
+ One = 1d0
+ if ~keyword_set(double) && ~indouble then begin
+   MADscale = float(MADscale)
+   MADscale2 = float(MADscale2)
+   MADlim = float(MADlim)
+   SIGcoeff = float(SIGcoeff)
+   One = float(One)
+ endif
+
+ Npts    = N_Elements(Y)
+ YMed    = MEDIAN(Y,/EVEN, DOUBLE=double)
+ AbsDev  = ABS(Y-YMED)
+ MedAbsDev = MEDIAN(AbsDev,/EVEN, DOUBLE=double)/MADscale
+ IF MedAbsDev LT MADlim THEN $
+      MedAbsDev = MEAN(AbsDev, DOUBLE=double, /NaN)/MADscale2
+
+ Cutoff    = Cut*MedAbsDev
+
+ goodvec = where( AbsDev LE Cutoff, Num_Good) 
+ if Num_Good LE 0 then begin
+     if ~keyword_set(SILENT) then $
+           message,'Unexpected error -- Unable to compute mean',/Con
+     mean = !Values.F_NaN & sigma = !VALUES.F_NAN & Num_rej = 0
+     return
+ endif    
+ GoodPts = Y[ goodvec]
+ Mean    = mean( GoodPts, DOUBLE=double)
+ Sigma   = SQRT( TOTAL((GoodPts-Mean)^2, DOUBLE=double)/Num_Good )
+ Num_Rej = Npts - Num_Good
+
+; Compensate Sigma for truncation (formula by HF):
+ SC = Cut > 1.0
+ IF SC LE 4.50 THEN SIGMA=SIGMA/poly(SC, SIGcoeff)
+
+ Cutoff = Cut*Sigma 
+
+ goodvec = where( AbsDev LE Cutoff, Num_Good) 
+
+ Num_Rej = Npts - Num_Good
+ GoodPts = Y[ goodvec ]
+ if arg_present(wused) then wused = goodvec
+ Mean    = mean( GoodPts, DOUBLE= double)
+ if N_params() LT 4 then return     ;Skip sigma calculation?
+ 
+
+ Sigma   = SQRT( TOTAL((GoodPts-Mean)^2)/Num_Good )
+
+; Fixed bug (should check for SC not Sigma) & add higher order correction
+ SC = Cut > 1.0
+ IF SC LE 4.50 THEN SIGMA=SIGMA/poly(SC, SIGcoeff)
+
+; Now the standard deviation of the mean:
+ Sigma = Sigma/SQRT(Num_Good-One)
+
+ RETURN
+ END
diff --git a/Code/script_idl_mv/astrolib/rhotheta.pro b/Code/script_idl_mv/astrolib/rhotheta.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5ceec75e252c29d99c608e24cd4693477aaf5aed
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/rhotheta.pro
@@ -0,0 +1,103 @@
+FUNCTION RHOTHETA,P,T,e,a,i,Omega,omega2,t2
+
+;+ 
+; NAME:
+;    RHOTHETA
+;
+; PURPOSE:
+;     Calculate the separation and position angle of a binary star
+;
+; EXPLANATION:
+;    This function will return the separation rho and position angle
+;    theta of a visual binary star derived from its orbital elements.
+;    The algorithms described in the following book will be used:
+;    Meeus J., 1992, Astronomische Algorithmen, Barth.
+;    Compared to the examples given at p. 400 and no discrepancy found.
+;    Input parameters will never be changed.
+;
+; CALLING SEQUENCE:
+;
+;  Result = RHOTHETA ( P, T, e, a, i, Omega, omega2, t2)
+;
+; INPUT:
+;
+; P             - Period [year]
+; T             - Time of periastron passage [year]
+; e             - eccentricity of the orbit
+; a             - semi-major axis [arc second]
+; i             - inclination [degree]
+; Omega         - node [degree]
+; omega2        - longitude of periastron [degree] 
+; t2            - epoch of observation [year]
+;
+; OUTPUT:
+;
+;  structure containing
+;  rho          - separation [arc second]
+;  theta        - position angle [degree]
+;  In case of errors rho and theta are -1.
+;
+; RESTRICTIONS:
+;
+;  All input parameters have to be scalars and floating point numbers.
+;  
+; EXAMPLE:
+;       Find the position of Eta Coronae Borealis at the epoch 1980.0
+;
+;       IDL> test=rhotheta(41.623,1934.008,0.2763,0.907,59.025,23.717,219.907,1980.0)
+;       rho=     0.411014    theta=       318.42307
+; 
+; PROCEDURES CALLED:
+;       CIRRANGE  - from IDL Astronomy Library
+;       
+; MODIFICATION HISTORY:
+;
+;  Written by:  Sebastian Kohl Hamburg Observatory, November, 2012
+;-
+;
+result={rho:DOUBLE(-1),theta:DOUBLE(-1)}
+
+IF (N_PARAMS() EQ 8) THEN BEGIN 
+; see chapter 55
+n=360.0/P
+M=n*(t2-T)
+M=M/360.0*2.0*!PI; convert M in radians
+
+; solution of Kepler equation, see chapter 29, 3rd method
+F= M GT 0 ? 1 : -1
+M=ABS(M)/2.0/!PI
+M=(M-FLOOR(M))*2.0*!PI*F
+IF (M LT 0.0) THEN M=M+2.0*!PI
+F=1.0
+IF (M GT !PI) THEN F=-1.0
+IF (M GT !PI) THEN M=2.0*!PI-M
+E0=!PI/2.0
+D=!PI/4.0
+FOR j=1,33 DO BEGIN
+M1=E0-e*sin(E0)
+SGN_M = (M-M1) GT 0 ? 1 : -1
+E0=E0+D*SGN_M
+D=D/2.0
+ENDFOR
+E0=E0*F
+
+; return to chapter 55
+r=a*(1.0-e*cos(E0))
+nu=2.0*ATAN(SQRT((1.0+e)/(1.0-e))*TAN(E0/2.0))
+my_omega2=omega2/180.0*!PI; convert variables in radians and copy them to a new variable to prevent changes to the input parameter
+my_i=i/180.0*!PI
+my_Omega=Omega/180.0*!PI
+theta=my_Omega+ATAN(SIN(nu+my_omega2)*COS(my_i),COS(nu+my_omega2))
+rho=r*COS(nu+my_omega2)/COS(theta-my_Omega)
+theta=theta*180.0/!PI; convert theta in degree
+
+CIRRANGE,theta; force theta to be in 0..360 range
+print,'rho=   ',rho,'    theta= ',theta
+result.rho=rho
+result.theta=theta
+
+ENDIF ELSE print,'Syntax - RHOTHETA, P, T, e, a, i, Omega, omega2, t2'
+
+RETURN,result
+
+ end
diff --git a/Code/script_idl_mv/astrolib/rinter.pro b/Code/script_idl_mv/astrolib/rinter.pro
new file mode 100644
index 0000000000000000000000000000000000000000..702d9e9fe6d0dfd4bc0341bbee8292d25731bec6
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/rinter.pro
@@ -0,0 +1,170 @@
+FUNCTION RINTER, P, X, Y, DFDX, DFDY, INITIALIZE = initialize
+;+
+; NAME:
+;      RINTER
+; PURPOSE:
+;      Cubic interpolation of an image at a set of reference points.
+; EXPLANATION:
+;      This interpolation program is equivalent to using the intrinsic 
+;      INTERPOLATE() function with CUBIC = -0.5.   However,
+;      RINTER() has two advantages: (1) one can optionally obtain the 
+;      X and Y derivatives at the reference points, and (2) if repeated
+;      interpolation is to be applied to an array, then some values can
+;      be pre-computed and stored in Common.   RINTER() was originally  
+;      for use with the DAOPHOT procedures, but can also be used for 
+;      general cubic interpolation.
+;
+; CALLING SEQUENCE:
+;      Z = RINTER( P, X, Y, [ DFDX, DFDY ] )
+;               or
+;      Z = RINTER(P, /INIT)
+;
+; INPUTS:                 
+;      P  - Two dimensional data array, 
+;      X  - Either an N element vector or an N x M element array,
+;               containing X subscripts where cubic interpolation is desired.
+;      Y -  Either an N element vector or an N x M element array, 
+;               containing Y subscripts where cubic interpolation is desired.
+;
+; OUTPUT:
+;      Z -  Result = interpolated vector or array.  If X and Y are vectors,
+;              then so is Z, but if X and Y are arrays then Z will be also.
+;              If P is DOUBLE precision, then so is Z, otherwise Z is REAL.
+;
+; OPTIONAL OUTPUT:
+;      DFDX - Vector or Array, (same size and type as Z), containing the 
+;               derivatives with respect to X
+;      DFDY - Array containing derivatives with respect to Y
+;
+; OPTIONAL KEYWORD INPUT:
+;     /INIT - Perform computations associated only with the input array (i.e. 
+;             not with X and Y) and store in common.    This can save time if
+;             repeated calls to RINTER are made using the same array.  
+;        
+; EXAMPLE:
+;      suppose P is a 256 x 256 element array and X = FINDGEN(50)/2. + 100.
+;      and Y = X.  Then Z will be a 50 element array, containing the
+;      cubic interpolated points.
+;
+; SIDE EFFECTS:
+;      can be time consuming.
+;
+; RESTRICTION:
+;      Interpolation is not possible at positions outside the range of 
+;       the array (including all negative subscripts), or within 2 pixel
+;       units of the edge.  No error message is given but values of the 
+;       output array are meaningless at these positions.
+;
+; PROCEDURE:
+;       invokes CUBIC interpolation algorithm to evaluate each element
+;       in Z at virtual coordinates contained in X and Y with the data
+;       in P.                                                          
+;
+; COMMON BLOCKS:
+;       If repeated interpolation of the same array is to occur, then
+;       one can save time by initializing the common block RINTER.    
+;
+; REVISION HISTORY:
+;       March 1988 written W. Landsman STX Co.
+;       Checked for IDL Version 2, J. Isensee, September, 1990
+;       Corrected call to HISTOGRAM, W. Landsman November 1990
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Fix output derivatives for 2-d inputs, added /INIT W. Landsman May 2000
+;       
+;-
+ On_error, 2     
+ common rinter, c1, c2, c3, init         
+
+ if (N_params() LT 3) and (NOT keyword_set(INIT)) then begin     
+        print, 'Syntax:  Z = RINTER( P, X, Y, [ DFDX, DFDY] )  '
+        print, '   or    Z = RINTER( P, /INIT)    to initialize common block
+        print,'P -  Array to be interpolated' 
+        print,'X -  Vector or array of X positions' 
+        print,'Y -  Vector or array of Y Positions'
+        print,'DFDX, DFDY - Optional output derivatives '
+        return,0
+ endif 
+
+ c = size(p)
+ if c[0] NE 2 then $    
+        message,'Input array (first parameter) must be 2 dimensional'
+
+ if keyword_set(initialize) then begin
+
+; Don't use SHIFT function to avoid wraparound at the end points
+
+      nx = c[1]
+      p_1 = p & p1 = p & p2 = p
+      p_1[1,0] = p[0:nx-2,*]
+      p1[0,0] = p[1:*,*]
+      p2[0,0] = p[2:*,*]
+      c1 = 0.5*(p1 - p_1)
+      c2 = 2.*p1 + p_1 - 0.5*(5.*p + p2)
+      c3 = 0.5*(3.*(p-p1) + p2 - p_1)
+      init = 1
+      if N_params() LT 3 then return,0
+ endif
+ 
+ sx = size(x)
+ npts = sx[sx[0]+2]
+ c[3] = c[3] > 4                        ;Make sure output array at least REAL
+
+ i = long( x[*] ) 
+ j = long( y[*] )
+ xdist = x[*] - i  
+ ydist = y[*] - j
+ x_1 = c[1]*(j-1) + i
+ x0 = x_1 + c[1] 
+ x1 = x0 + c[1] 
+ x2 = x1 + c[1]
+
+ if N_elements(init) EQ 0 then init = 0    ;Has COMMON block been initialized?
+
+ if init EQ 0 then begin 
+
+   xgood = [ x_1,x0,x1,x2 ]
+   num = histogram( xgood, MIN=0)
+   xgood = where( num GE 1 ) 
+   p_1 = p[xgood-1] & p0 = p[xgood] & p1 = p[xgood+1] & p2 = p[xgood+2]
+   c1 = p*0.  & c2 = c1 & c3 = c1
+   c1[xgood] = 0.5*( p1 - p_1)
+   c2[xgood] = 2.*p1 + p_1 - 0.5*(5.*p0 + p2)
+   c3[xgood] = 0.5*(3.*(p0 - p1) + p2 - p_1)
+ endif
+
+ y_1 = xdist*( xdist*( xdist*c3[x_1] +c2[x_1]) + c1[x_1]) + p[x_1]
+ y0 =  xdist*( xdist*( xdist*c3[x0] +c2[x0]) + c1[x0]) + p[x0]
+ y1 =  xdist*( xdist*( xdist*c3[x1] +c2[x1]) + c1[x1]) + p[x1]
+ y2 =  xdist*( xdist*( xdist*c3[x2] +c2[x2]) + c1[x2]) + p[x2]
+
+ if N_params() GT 3 then begin
+ 
+    dy_1 = xdist*(xdist*c3[x_1]*3. + 2.*c2[x_1]) + c1[x_1]
+    dy0  = xdist*(xdist*c3[x0 ]*3. + 2.*c2[x0]) + c1[x0]
+    dy1  = xdist*(xdist*c3[x1 ]*3. + 2.*c2[x1]) + c1[x1]
+    dy2  = xdist*(xdist*c3[x2 ]*3. + 2.*c2[x2]) + c1[x2]
+    d1 = 0.5*(dy1 - dy_1)
+    d2 = 2.*dy1 + dy_1 - 0.5*(5.*dy0 +dy2)
+    d3 = 0.5*( 3.*( dy0-dy1 ) + dy2 - dy_1)
+    dfdx =  ydist*( ydist*( ydist*d3 + d2 ) + d1 ) + dy0
+
+ endif
+
+ d1 = 0.5*(y1 - y_1)
+ d2 = 2.*y1 + y_1 - 0.5*(5.*y0 +y2)
+ d3 = 0.5*(3.*(y0-y1) + y2 - y_1)
+ z =  ydist*(ydist*(ydist*d3 + d2) + d1) + y0  
+ if N_params() GT 3 then dfdy = ydist*(ydist*d3*3.+2*d2) + d1   
+ 
+ if ( sx[0] EQ 2 ) then begin        ;Convert results to 2-D if desired
+
+    z = reform(z,sx[1],sx[2] ) 
+    if N_params() GT 3 then  begin      ;Create output derivative arrays?
+          dfdx = reform(dfdx,sx[1],sx[2])
+          dfdy = reform(dfdy,sx[1],sx[2])          
+     endif
+
+ endif
+
+ return,z
+ end                    
diff --git a/Code/script_idl_mv/astrolib/rob_checkfit.pro b/Code/script_idl_mv/astrolib/rob_checkfit.pro
new file mode 100644
index 0000000000000000000000000000000000000000..17696066f9c5f32f470eb2eb052f626e3b18d85d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/rob_checkfit.pro
@@ -0,0 +1,66 @@
+FUNCTION ROB_CHECKFIT,Y, YFIT, EPS, DEL, SIG, FRACDEV, NGOOD,W,B,$
+                      BISQUARE_LIMIT=BLIM
+;+
+; NAME:
+;	ROB_CHECKFIT
+; PURPOSE:
+;	Used by ROBUST_... routines to determine the quality of a fit and to
+;	return biweights.
+; CALLING SEQUENCE:
+;	status = ROB_CHECKFIT( Y, YFIT, EPS, DEL, SIG, FRACDEV, NGOOD, W, B
+;				BISQUARE_LIMIT = )
+; INPUT:
+;	Y     = the data
+;	YFIT  = the fit to the data
+;	EPS   = the "too small" limit
+;	DEL   = the "close enough" for the fractional median abs. deviations
+; RETURNS:
+;	Integer status. if =1, the fit is considered to have converged
+;
+; OUTPUTS:
+;	SIG   = robust standard deviation analog
+;	FRACDEV = the fractional median absolute deviation of the residuals
+;	NGOOD   = the number of input point given non-zero weight in the 
+;		calculation
+;	W     = the bisquare weights of Y
+;	B     = residuals scaled by sigma
+;
+; OPTIONAL INPUT KEYWORD:
+;	BISQUARE_LIMIT = allows changing the bisquare weight limit from 
+;			default 6.0
+; PROCEDURES USED:
+;       ROBUST_SIGMA()
+; REVISION HISTORY:
+;	Written, H.T. Freudenreich, HSTX, 1/94
+;-
+
+  ISTAT = 0
+
+  IF KEYWORD_SET(BLIM) THEN BFAC=BLIM ELSE BFAC=6.
+
+  DEV = Y-YFIT
+
+  SIG=ROBUST_SIGMA(DEV,/ZERO)
+; If the standard deviation = 0 then we're done:
+  IF SIG LT EPS THEN GOTO,DONE 
+
+  IF DEL GT 0. THEN BEGIN
+   ; If the fraction std. deviation ~ machine precision, we're done:
+     Q=WHERE( ABS(YFIT) GT EPS, COUNT )
+     IF COUNT LT 3 THEN FRACDEV = 0. ELSE $
+                        FRACDEV = MEDIAN(ABS( DEV[Q]/YFIT[Q] ),/EVEN )
+     IF FRACDEV LT DEL THEN GOTO,DONE
+  ENDIF
+
+  ISTAT = 1
+
+; Calculate the (bi)weights:
+  B = ABS(DEV)/(BFAC*SIG)
+  S = WHERE( B GT 1.0,COUNT )  &  IF COUNT GT 0 THEN B[S] = 1.
+  NGOOD = N_ELEMENTS(Y)-COUNT
+  
+  W=(1.-B^2)
+  W=W/TOTAL(W)
+DONE:
+RETURN, ISTAT
+END
diff --git a/Code/script_idl_mv/astrolib/robust_linefit.pro b/Code/script_idl_mv/astrolib/robust_linefit.pro
new file mode 100644
index 0000000000000000000000000000000000000000..817a0f07a5be2813bdedd63a306ced18fa34ec07
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/robust_linefit.pro
@@ -0,0 +1,268 @@
+FUNCTION  ROBUST_LINEFIT,XIN,YIN,YFIT,SIG,SS, NUMIT=THIS_MANY, BISECT=TYPE, $
+                         Bisquare_Limit=Bisquare_Limit, $
+                         Close_Factor=Close_Factor
+;+
+; NAME:
+;       ROBUST_LINEFIT
+;
+; PURPOSE:
+;       An outlier-resistant two-variable linear regression. 
+; EXPLANATION:
+;       Either Y on X or, for the case in which there is no true independent 
+;       variable, the bisecting line of Y vs X and X vs Y is calculated. No 
+;       knowledge of the errors of the input points is assumed.
+;
+; CALLING SEQUENCE:
+;       COEFF = ROBUST_LINEFIT( X, Y, YFIT, SIG, COEF_SIG, [ /BISECT,
+;                       BiSquare_Limit = , Close_factor = , NumIT = ] )
+;
+; INPUTS:
+;       X = Independent variable vector, floating-point or double-precision
+;       Y = Dependent variable vector
+;
+; OUTPUTS:
+;       Function result = coefficient vector. 
+;       If = 0.0 (scalar), no fit was possible.
+;       If vector has more than 2 elements (the last=0) then the fit is dubious.
+;
+; OPTIONAL OUTPUT PARAMETERS:
+;       YFIT = Vector of calculated y's
+;       SIG  = The "standard deviation" of the fit's residuals. If BISECTOR 
+;               is set, this will be smaller by ~ sqrt(2).
+;       COEF_SIG  = The estimated standard deviations of the coefficients. If 
+;               BISECTOR is set, however, this becomes the vector of fit 
+;               residuals measured orthogonal to the line.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       NUMIT = the number of iterations allowed. Default = 25
+;       BISECT  if set, the bisector of the "Y vs X" and "X vs Y" fits is 
+;               determined.  The distance PERPENDICULAR to this line is used 
+;               in calculating weights. This is better when the uncertainties 
+;               in X and Y are comparable, so there is no true independent 
+;               variable.  Bisquare_Limit  Limit used for calculation of 
+;               bisquare weights. In units of outlier-resistant standard 
+;               deviations. Default: 6.
+;               Smaller limit ==>more resistant, less efficient
+; Close_Factor  - Factor used to determine when the calculation has converged.
+;               Convergence if the computed standard deviation changes by less 
+;               than Close_Factor * ( uncertainty of the std dev of a normal
+;               distribution ). Default: 0.03.
+; SUBROUTINE CALLS:
+;       ROB_CHECKFIT
+;       ROBUST_SIGMA, to calculate a robust analog to the std. deviation
+;
+; PROCEDURE:
+;       For the initial estimate, the data is sorted by X and broken into 2
+;       groups. A line is fitted to the x and y medians of each group.
+;       Bisquare ("Tukey's Biweight") weights are then calculated, using the 
+;       a limit of 6 outlier-resistant standard deviations.
+;       This is done iteratively until the standard deviation changes by less 
+;       than CLOSE_ENOUGH = CLOSE_FACTOR * {uncertainty of the standard 
+;               deviation of a normal distribution}
+;
+; REVISION HISTORY:
+;       Written, H. Freudenreich, STX, 4/91.
+;       4/13/93 to return more realistic SS's HF
+;       2/94 --more error-checking, changed convergence criterion HF
+;       5/94 --added BISECT option. HF.
+;       8/94 --added Close_Factor and Bisquare_Limit options  Jack Saba.
+;       4/02 --V5.0 version, use MEDIAN(/EVEN)  W. Landsman
+;-
+
+ON_ERROR,2
+
+IF N_ELEMENTS(THIS_MANY) GT 0 THEN ITMAX = THIS_MANY ELSE ITMAX=25
+
+IF  N_elements(Close_Factor) EQ 0  THEN Close_Factor = 0.03
+
+DEL = 5.0E-07
+EPS = 1.0E-20
+
+N = N_ELEMENTS(XIN)
+
+; First, shift X and Y to their centers of gravity:
+ X0 = TOTAL(XIN)/N  &  Y0=TOTAL(YIN)/N
+ X = XIN-X0       &  Y = YIN-Y0 
+
+ CC=FLTARR(2)
+ SS=FLTARR(2)
+ SIG=0.
+ YFIT=YIN
+ BADFIT=0
+ NGOOD=N
+
+; Make sure the independent variables are not all the same.
+ XRANGE=MAX(X)-MIN(X)
+ AVEX= (TOTAL(ABS(X))/N) > EPS
+ IF (XRANGE LT EPS) OR (XRANGE/AVEX LT DEL) THEN BEGIN
+   message,'Independent variables the same. No fit possible.',/CON
+   RETURN,0.
+ENDIF 
+
+; First guess: 
+LSQ=0
+YP=Y
+IF N GT 5 THEN BEGIN
+;  We divide the data into 2 groups and fit a line to their X and Y medians.
+   S=SORT(X) &  U=X[S]  &  V=Y[S]
+   NHALF=N/2-1
+   X1=MEDIAN(U[0:NHALF],/EVEN) & X2=MEDIAN(U[NHALF+1:N-1],/EVEN)
+   Y1=MEDIAN(V[0:NHALF],/EVEN) & Y2=MEDIAN(V[NHALF+1:N-1],/EVEN)
+   IF ABS(X2-X1) LT EPS THEN BEGIN
+;     The X medians are too close. Select the end-points instead.
+      X1=U[0]  &  X2=U[N-1]
+      Y1=V[0]  &  Y2=V[N-1]
+   ENDIF
+   CC[1]=(Y2-Y1)/(X2-X1)  & CC[0]=Y1-CC[1]*X1
+   YFIT = CC[0]+CC[1]*X
+   ISTAT = ROB_CHECKFIT(YP,YFIT,EPS,DEL,  SIG,FRACDEV,NGOOD,W,S)
+   IF NGOOD LT 2 THEN LSQ=1
+ENDIF 
+IF (LSQ EQ 1) OR (N LT 6) THEN BEGIN  ; Try a least-squares fit
+   SX=TOTAL(X) & SY=TOTAL(Y) & SXY=TOTAL(X*Y) & SXX=TOTAL(X*X) 
+   D=SXX-SX*SX
+   IF ABS(D) LT EPS THEN BEGIN
+      PRINT,'ROBUST_LINEFIT: No fit possible.'
+      RETURN,0.
+   ENDIF 
+   YSLOP=(SXY-SX*SY)/D      &   YYINT=(SXX*SY-SX*SXY)/D 
+
+   IF KEYWORD_SET(TYPE) THEN BEGIN    
+;     Get the X vs Y line.
+      SYY=TOTAL(Y*Y)
+      D=SYY-SY*SY
+      IF ABS(D) LT EPS THEN BEGIN
+         PRINT,'ROBUST_LINEFIT: No fit possible.'
+         RETURN,0.
+      ENDIF
+      TSLOP=(SXY-SY*SX)/D   &   TYINT=(SYY*SX-SY*SXY)/D 
+;     Now invert it to get the form Y=a+bX:
+      IF ABS(TSLOP) LT EPS THEN BEGIN
+         message,'No fit possible.',/CON
+         RETURN,0.
+      ENDIF
+      XSLOP = 1./TSLOP       &   XYINT=-TYINT/TSLOP
+;     Now calculate the equation of the bisector of the 2 lines:
+      IF YSLOP GT XSLOP THEN BEGIN
+         A1=YYINT  &  B1=YSLOP  &  R1=SQRT(1.+YSLOP^2)
+         A2=XYINT  &  B2=XSLOP  &  R2=SQRT(1.+XSLOP^2)
+      ENDIF ELSE BEGIN
+         A2=YYINT  &  B2=YSLOP  &  R2=SQRT(1.+YSLOP^2)
+         A1=XYINT  &  B1=XSLOP  &  R1=SQRT(1.+XSLOP^2)
+      ENDELSE
+      YINT = (R1*A2+R2*A1)/(R1+R2) 
+      SLOP = (R1*B2+R2*B1)/(R1+R2)
+;     Now find the orthogonal distance to the line. Convert to normal
+;     coordinates.
+      R = SQRT(1.+SLOP^2)  & IF YINT GT 0. THEN R=-R
+      U1 = SLOP/R  & U2=-1./R  &  U3=YINT/R 
+      YP = U1*X+U2*Y+U3  ; = orthog. distance to line
+      YFIT = FLTARR(N)   ; to fool ROB_CHECKFIT
+      SS=YP
+   ENDIF ELSE BEGIN
+      SLOP=YSLOP               &   YINT=YYINT
+      YFIT = YINT+SLOP*X
+   ENDELSE
+   CC = [YINT,SLOP]
+   ISTAT = ROB_CHECKFIT(YP,YFIT,EPS,DEL,  SIG,FRACDEV,NGOOD,W,S)
+ENDIF
+
+ IF ISTAT EQ 0 THEN GOTO,AFTERFIT
+
+ IF NGOOD LT 2 THEN BEGIN
+   message,'Data Dangerously Weird. Fit Questionable.',/CON
+   BADFIT=1
+   GOTO,AFTERFIT
+ENDIF
+
+; Now iterate until the solution converges:
+ SIG_1= (100.*SIG) < 1.0E20
+ CLOSE_ENOUGH = Close_Factor * SQRT(.5/(N-1)) > DEL
+ DIFF= 1.0E20
+ NIT = 0
+ WHILE( (DIFF GT CLOSE_ENOUGH) AND (NIT LT ITMAX) ) DO BEGIN
+  NIT=NIT+1
+  SIG_2=SIG_1
+  SIG_1=SIG
+  SX=TOTAL(W*X) & SY=TOTAL(W*Y) & SXY=TOTAL(W*X*Y) & SXX=TOTAL(W*X*X) 
+  D=SXX-SX*SX
+  IF ABS(D) LT EPS THEN BEGIN
+     message,'No fit possible.',/CON
+     RETURN,0.
+  ENDIF 
+  YSLOP = (SXY-SX*SY)/D      &   YYINT = (SXX*SY-SX*SXY)/D 
+  SLOP = YSLOP               &   YINT = YYINT
+  IF KEYWORD_SET(TYPE) THEN BEGIN    
+;    Get the X vs Y line.
+     SYY=TOTAL(W*Y*Y) 
+     D=SYY-SY*SY
+     IF ABS(D) LT EPS THEN BEGIN
+        PRINT,'ROBUST_LINEFIT: No fit possible.'
+        RETURN,0.
+     ENDIF
+     TSLOP=(SXY-SY*SX)/D   &   TYINT=(SYY*SX-SY*SXY)/D 
+;    Now invert it to get the form Y=a+bX:
+     IF ABS(TSLOP) LT EPS THEN BEGIN
+        PRINT,'ROBUST_LINEFIT: No fit possible.'
+        RETURN,0.
+     ENDIF
+     XSLOP=1./TSLOP       &   XYINT=-TYINT/TSLOP
+;    Now calculate the equation of the bisector of the 2 lines:
+     IF YSLOP GT XSLOP THEN BEGIN
+        A1=YYINT  &  B1=YSLOP  &  R1=SQRT(1.+YSLOP^2)
+        A2=XYINT  &  B2=XSLOP  &  R2=SQRT(1.+XSLOP^2)
+     ENDIF ELSE BEGIN
+        A2=YYINT  &  B2=YSLOP  &  R2=SQRT(1.+YSLOP^2)
+        A1=XYINT  &  B1=XSLOP  &  R1=SQRT(1.+XSLOP^2)
+     ENDELSE
+     YINT=(R1*A2+R2*A1)/(R1+R2)
+     SLOP=(R1*B2+R2*B1)/(R1+R2)
+     R=SQRT(1.+SLOP^2)  & IF YINT GT 0. THEN R=-R
+     U1=SLOP/R  & U2=-1./R  &  U3=YINT/R 
+     YP=U1*X+U2*Y+U3  ; = orthog distance to line
+     YFIT=FLTARR(N) & YFIT[*]=0.
+     SS=YP
+  ENDIF ELSE BEGIN
+     YFIT = YINT+SLOP*X
+  ENDELSE
+  CC=[YINT,SLOP] 
+  ISTAT=ROB_CHECKFIT(YP,YFIT,EPS,DEL,  SIG,FRACDEV,NGOOD,W,S, $
+                     Bisquare_Limit=Bisquare_Limit )
+
+  IF ISTAT EQ 0 THEN GOTO,AFTERFIT
+  IF NGOOD LT 2 THEN BEGIN
+     PRINT,'ROBUST_LINEFIT: Data Dangerously Weird. Fit Questionable.'
+     BADFIT=1
+     GOTO,AFTERFIT
+  ENDIF
+  DIFF = (ABS(SIG_1-SIG)/SIG) < (ABS(SIG_2-SIG)/SIG)
+ENDWHILE
+
+AFTERFIT:
+; Untranslate the coefficients
+ CC[0] = CC[0]+Y0-CC[1]*X0
+
+IF N_PARAMS(0) GT 2 THEN YFIT = CC[0] + CC[1]*XIN
+ IF KEYWORD_SET(BISECT) THEN RETURN,CC
+
+ IF (N_PARAMS(0) GT 3) AND (SIG GT EPS) AND (NGOOD GT 2) THEN BEGIN
+   ; Here we use an empirical formula to approximate the standard deviations
+   ; of the coefficients. They are usually accurate to ~ 25%.
+   SX2 = TOTAL(W*X*X) 
+   UU = S*S
+   DEV = YIN-YFIT
+   Y0 = TOTAL( W*DEV )
+   Q = WHERE(UU LE 1.0,COUNT)
+   DEN1 = ABS(TOTAL( (1.-UU[Q])*(1.-5.*UU[Q]) ))
+   SIG = ROBUST_SIGMA(DEV,/ZERO)
+   ; Now empirically derived estimates of the uncertainties:
+   SS[0] = SIG/SQRT(DEN1)/1.105 
+   SS[1] = SS[0]/SQRT(SX2)
+   ; Take the X shift into account:
+   SS[0] = SQRT(SS[0]^2+X0*SS[1]^2)
+ ENDIF
+
+ IF BADFIT EQ 1 THEN CC=[CC,0.]
+
+ RETURN,CC
+ END
diff --git a/Code/script_idl_mv/astrolib/robust_poly_fit.pro b/Code/script_idl_mv/astrolib/robust_poly_fit.pro
new file mode 100644
index 0000000000000000000000000000000000000000..149a3e7974719c41369abd1021dc7938af2c145b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/robust_poly_fit.pro
@@ -0,0 +1,194 @@
+FUNCTION ROBUST_POLY_FIT,X,Y,NDEG,YFIT,SIG, NUMIT=THIS_MANY, DOUBLE=DOUBLE
+;+
+; NAME:
+;	ROBUST_POLY_FIT
+;
+; PURPOSE:
+;	An outlier-resistant polynomial fit.
+;
+; CALLING SEQUENCE:
+;	COEFF = ROBUST_POLY_FIT(X,Y,NDEGREE, [ YFIT,SIG, /DOUBLE, NUMIT=] )
+;
+; INPUTS:
+;	X = Independent variable vector, floating-point or double-precision
+;	Y = Dependent variable vector
+;       NDEGREE - integer giving degree of polynomial to fit, maximum = 6
+; OUTPUTS:
+;	Function result = coefficient vector, length NDEGREE+1. 
+;	IF COEFF=0.0, NO FIT! If N_ELEMENTS(COEFF) > degree+1, the fit is poor
+;	(in this case the last element of COEFF=0.)
+;	Either floating point or double precision.
+;
+; OPTIONAL OUTPUT PARAMETERS:
+;	YFIT = Vector of calculated y's
+;	SIG  = the "standard deviation" of the residuals
+;
+; OPTIONAL INPUT KEYWORD:
+;       /DOUBLE - If set, then force all computations to double precision.
+;       NUMIT - Maximum number of iterations to perform, default = 25
+; RESTRICTIONS:
+;	Large values of NDEGREE should be avoided. This routine works best
+;	when the number of points >> NDEGREE.
+;
+; PROCEDURE:
+;	For the initial estimate, the data is sorted by X and broken into 
+;	NDEGREE+2 sets. The X,Y medians of each set are fitted to a polynomial
+;	 via POLY_FIT.   Bisquare ("Tukey's Biweight") weights are then 
+;	calculated, using a limit  of 6 outlier-resistant standard deviations.
+;	The fit is repeated iteratively until the robust standard deviation of 
+;	the residuals changes by less than .03xSQRT(.5/(N-1)).
+;
+; PROCEDURES CALLED:
+;        POLY(), POLY_FIT()
+;       ROB_CHECKFIT()
+; REVISION HISTORY
+;	Written, H. Freudenreich, STX, 8/90. Revised 4/91.
+;	2/94 -- changed convergence criterion
+;        Added /DOUBLE keyword, remove POLYFITW call  W. Landsman  Jan 2009
+;-
+
+ON_ERROR,2
+COMPILE_OPT IDL2
+
+EPS   = 1.0E-20
+DEL   = 5.0E-07
+DEGMAX= 6
+
+IF N_ELEMENTS(THIS_MANY) GT 0 THEN ITMAX=THIS_MANY ELSE ITMAX=25
+
+BADFIT=0
+
+NPTS = N_ELEMENTS(X)
+MINPTS=NDEG+1
+IF (NPTS/4*4) EQ NPTS THEN NEED2 = 1 ELSE NEED2 = 0
+N3 = 3*NPTS/4  &  N1 = NPTS/4
+
+; If convenient, move X and Y to their centers of gravity:
+IF NDEG LT DEGMAX THEN BEGIN
+   X0=TOTAL(X)/NPTS  &  Y0=TOTAL(Y)/NPTS
+   U=X-X0            &  V=Y-Y0
+ENDIF ELSE BEGIN
+   U=X               &  V=Y
+ENDELSE
+
+; The initial estimate.
+
+; Choose an odd number of segments:
+NUM_SEG = NDEG+2
+IF (NUM_SEG/2*2) EQ NUM_SEG THEN NUM_SEG =NUM_SEG+1
+MIN_PTS = NUM_SEG*3
+IF NPTS LT 10000 THEN BEGIN ;MIN_PTS THEN BEGIN
+;  Settle for least-squares:
+   LSQFIT = 1
+   CC = POLY_FIT( U, V, NDEG, YFIT , DOUBLE=DOUBLE)
+ENDIF ELSE BEGIN
+;  Break up the data into segments:
+   LSQFIT = 0
+   Q = SORT(U)
+   U = U[Q]  &  V = V[Q]
+   N_PER_SEG = REPLICATE( NPTS/NUM_SEG, NUM_SEG)
+
+;  Put the leftover points in the middle segment:
+   N_LEFT = NPTS - N_PER_SEG[0]*NUM_SEG
+   N_PER_SEG[NUM_SEG/2] = N_PER_SEG[NUM_SEG/2] + N_LEFT
+   R = DBLARR(NUM_SEG)  &  S = DBLARR(NUM_SEG)
+   R[0]=MEDIAN( U[0:N_PER_SEG[0]-1],/EVEN ) 
+   S[0]=MEDIAN( V[0:N_PER_SEG[0]-1],/EVEN )
+   I2 = N_PER_SEG[0]-1
+   FOR I=1,NUM_SEG-1 DO BEGIN
+     I1 = I2 + 1
+     I2 = I1 + N_PER_SEG[I] - 1
+     R[I] = MEDIAN( U[I1:I2], /EVEN)      &  S[I] = MEDIAN( V[I1:I2],/EVEN )
+   ENDFOR
+;  Now fit:
+   CC = POLY_FIT( R,S, NDEG, DOUBLE=DOUBLE )
+   YFIT = POLY(U,CC)  
+ENDELSE
+
+ISTAT = ROB_CHECKFIT(V,YFIT,EPS,DEL,  SIG,FRACDEV,NGOOD,W,S)
+
+IF ISTAT EQ 0 THEN GOTO,AFTERFIT
+
+IF NGOOD LT MINPTS THEN BEGIN
+   IF LSQFIT EQ 0 THEN BEGIN
+      ;  Try a least-squares:
+      CC = POLY_FIT( U, V, NDEG, YFIT, DOUBLE=DOUBLE )
+      ISTAT = ROB_CHECKFIT(V,YFIT,EPS,DEL,  SIG,FRACDEV,NGOOD,W,S)
+      IF ISTAT EQ 0 THEN GOTO,AFTERFIT
+      NGOOD = NPTS-COUNT
+   ENDIF
+   IF NGOOD LT MINPTS THEN BEGIN
+      PRINT,'ROBUST_POLY_FIT: No Fit Possible!'
+      RETURN,0.
+   ENDIF
+ENDIF
+
+; Now iterate until the solution converges:
+CLOSE_ENOUGH = .03*SQRT(.5/(NPTS-1)) > DEL 
+DIFF= 1.0E10
+SIG_1= (100.*SIG) < 1.0E20
+NIT = 0
+WHILE( (DIFF GT CLOSE_ENOUGH) AND (NIT LT ITMAX) ) DO BEGIN
+  NIT=NIT+1
+  SIG_2=SIG_1
+  SIG_1=SIG
+; We use the "obsolete" POLYFITW routine because it allows us to input weights
+; rather than measure errors
+  g = where(W gt 0, Ng)
+  if Ng LT N_elements(w) then begin    ;Throw out points with zero weight
+    u = u[g]
+    v = v[g]
+    w = w[g]
+  endif  
+  CC = POLY_FIT( U, V, NDEG, YFIT, MEASURE_ERRORS = 1/W^2, DOUBLE=DOUBLE )
+  ISTAT = ROB_CHECKFIT(V,YFIT,EPS,DEL,  SIG,FRACDEV,NGOOD,W,S)
+  IF ISTAT EQ 0 THEN GOTO,AFTERFIT
+  IF NGOOD LT MINPTS THEN BEGIN
+     PRINT,'ROBUST_POLY_FIT: Questionable Fit!'
+     BADFIT=1
+     GOTO,AFTERFIT
+  ENDIF
+  DIFF = (ABS(SIG_1-SIG)/SIG) < (ABS(SIG_2-SIG)/SIG)
+ENDWHILE
+
+;IF NIT GE ITMAX THEN PRINT,'ROBUST_POLY_FIT: Did not converge in',ITMAX,$
+;' iterations!'
+
+AFTERFIT:
+CC=REFORM(CC)
+
+IF NDEG LT DEGMAX THEN BEGIN
+CASE NDEG OF
+ 1: CC[0] = CC[0]-CC[1]*X0 + Y0
+ 2: BEGIN   
+   CC[0] = CC[0]-CC[1]*X0+CC[2]*X0^2 + Y0
+   CC[1] = CC[1]-2.*CC[2]*X0
+    END
+ 3: BEGIN
+   CC[0] = CC[0]-CC[1]*X0+CC[2]*X0^2-CC[3]*X0^3 + Y0
+   CC[1] = CC[1]-2.*CC[2]*X0+3.*CC[3]*X0^2
+   CC[2] = CC[2]-3.*CC[3]*X0
+    END
+ 4: BEGIN
+   CC[0] = CC[0]-   CC[1]*X0+CC[2]*X0^2-CC[3]*X0^3+CC[4]*X0^4+ Y0
+   CC[1] = CC[1]-2.*CC[2]*X0+3.*CC[3]*X0^2-4.*CC[4]*X0^3
+   CC[2] = CC[2]-3.*CC[3]*X0+6.*CC[4]*X0^2
+   CC[3] = CC[3]-4.*CC[4]*X0
+    END
+ 5: BEGIN
+   CC[0] = CC[0]-  CC[1]*X0+CC[2]*X0^2-CC[3]*X0^3+CC[4]*X0^4-CC[5]*X0^5+ Y0
+   CC[1] = CC[1]-2.*CC[2]*X0+ 3.*CC[3]*X0^2- 4.*CC[4]*X0^3+5.*CC[5]*X0^4
+   CC[2] = CC[2]-3.*CC[3]*X0+ 6.*CC[4]*X0^2-10.*CC[5]*X0^3
+   CC[3] = CC[3]-4.*CC[4]*X0+10.*CC[5]*X0^2
+   CC[4] = CC[4]-5.*CC[5]*X0
+    END
+ ENDCASE
+ENDIF
+
+; Calculate the fit at points X:
+IF( N_PARAMS(0) GT 3 )THEN YFIT=POLY(X,CC)
+
+IF BADFIT EQ 1 THEN CC=[CC,0.]
+
+RETURN,CC
+END
diff --git a/Code/script_idl_mv/astrolib/robust_sigma.pro b/Code/script_idl_mv/astrolib/robust_sigma.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e43ef4c83096b45c189df92ea8ced2c39bb8d379
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/robust_sigma.pro
@@ -0,0 +1,73 @@
+FUNCTION  ROBUST_SIGMA,Y, ZERO=REF, GOODVEC = Q
+;
+;+
+; NAME:
+;	ROBUST_SIGMA  
+;
+; PURPOSE:
+;	Calculate a resistant estimate of the dispersion of a distribution.
+; EXPLANATION:
+;	For an uncontaminated distribution, this is identical to the standard
+;	deviation.
+;
+; CALLING SEQUENCE:
+;	result = ROBUST_SIGMA( Y, [ /ZERO, GOODVEC = ] )
+;
+; INPUT: 
+;	Y = Vector of quantity for which the dispersion is to be calculated
+;
+; OPTIONAL INPUT KEYWORD:
+;	/ZERO - if set, the dispersion is calculated w.r.t. 0.0 rather than the
+;		central value of the vector. If Y is a vector of residuals, this
+;		should be set.
+;
+; OPTIONAL OUPTUT KEYWORD:
+;       GOODVEC = Vector of non-trimmed indices of the input vector
+; OUTPUT:
+;	ROBUST_SIGMA returns the dispersion. In case of failure, returns 
+;	value of -1.0
+;
+; PROCEDURE:
+;	Use the median absolute deviation as the initial estimate, then weight 
+;	points using Tukey's Biweight. See, for example, "Understanding Robust
+;	and Exploratory Data Analysis," by Hoaglin, Mosteller and Tukey, John
+;	Wiley & Sons, 1983, or equation 9 in Beers et al. (1990, AJ, 100, 32)
+;
+; REVSION HISTORY: 
+;	H. Freudenreich, STX, 8/90
+;       Replace MED() call with MEDIAN(/EVEN)  W. Landsman   December 2001
+;       Don't count NaN values  W.Landsman  June 2010
+;
+;-
+ On_error,2
+ compile_opt idl2
+ 
+ EPS = 1.0E-20
+ IF KEYWORD_SET(REF) THEN Y0=0. ELSE Y0  = MEDIAN(Y,/EVEN)
+
+; First, the median absolute deviation MAD about the median:
+
+ MAD = MEDIAN( ABS(Y-Y0), /EVEN )/0.6745
+
+; If the MAD=0, try the MEAN absolute deviation:
+ IF MAD LT EPS THEN MAD = MEAN( ABS(Y-Y0) )/.80
+ IF MAD LT EPS THEN RETURN, 0.0
+
+; Now the biweighted value:
+ U   = (Y-Y0)/(6.*MAD)
+ UU  = U*U
+ Q   = WHERE(UU LE 1.0, COUNT)
+ IF COUNT LT 3 THEN BEGIN
+   PRINT,'ROBUST_SIGMA: This distribution is TOO WEIRD! Returning -1'
+   SIGGMA = -1.
+   RETURN,SIGGMA
+ ENDIF
+
+ N = TOTAL(FINITE(Y),/INT)      ;In case Y has NaN values          ;
+ NUMERATOR = TOTAL( (Y[Q]-Y0)^2 * (1-UU[Q])^4 )
+ DEN1  = TOTAL( (1.-UU[Q])*(1.-5.*UU[Q]) )
+ SIGGMA = N*NUMERATOR/(DEN1*(DEN1-1.))
+ 
+ IF SIGGMA GT 0. THEN RETURN, SQRT(SIGGMA) ELSE RETURN, 0.
+
+ END
diff --git a/Code/script_idl_mv/astrolib/safe_correlate.pro b/Code/script_idl_mv/astrolib/safe_correlate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c44ed3ab4189c5a4d6a56e9141e251b9cd592cb4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/safe_correlate.pro
@@ -0,0 +1,230 @@
+;function to detect type of error array input
+function errtype, err, bad_err_msg
+sz = size(err)
+  case sz[0] of
+    0: errtype = 'sigma'
+    1: errtype = 'sigmas'
+    3: errtype = 'pdfs'
+    else: message,bad_err_msg
+  endcase
+return,errtype
+end
+
+;function to check for consistent error array input
+pro vet_err, err, errtype, n, bad_err_msg
+  sz = size(err)
+  
+  badinput = 0 ;turn this switch on if input is bad
+  ;check that dimensions are good
+  ;if errtype eq 'sigma' -- no action needed for scalar
+  if errtype eq 'sigmas' and sz[1] ne n then badinput = 1
+  if errtype eq 'pdfs' and (sz[1] ne n or sz[2] ne 2) then badinput = 1
+  
+  ;print error if bad dimensions
+  if badinput then message,bad_err_msg
+end
+
+;function to generate simulated data based on values and error array
+function generate_data, v, err, type, n, nsim, dbl, seed
+  r = type eq 'pdfs' ? randomU(seed, n, nsim, double=dbl) : randomN(seed, n, nsim, double=dbl)
+  case type of
+    ;v # replicate(1,n) uses matrix multiplication to create an array where the
+    ;nth column is filled with v[n]
+    'sigma': simdata = r*err + (v # replicate(1,nsim))
+    'sigmas': simdata = r*(err # replicate(1,nsim)) + (v # replicate(1,nsim))
+    'pdfs': begin
+      simdata = dbl ? dblarr(n, nsim) : fltarr(n, nsim)
+      for i = 0,n-1 do begin
+        pdfx = err[i,0,*]
+        pdfy = err[i,1,*]
+        
+        ;first compute the cdf from the pdf using trapezoidal integration
+        trapezoid_areas = 0.5*(pdfy[1:-1] + pdfy[0:-2])*(pdfx[1:-1] - pdfx[0:-2])
+        f = TOTAL(trapezoid_areas,/CUMULATIVE)
+        f = f/f[-1] ;ensure it is normalized
+        
+        ;modify x vector have one pt centered at each trapezoidal element
+        pdfx = (pdfx[1:-1] + pdfx[0:-2])/2.
+        
+        ;transform uniform to input distribution via interpolation from the cdf
+        simdata[i,*] = INTERPOL(pdfx, f, r[i,*])
+      endfor
+    end
+  endcase
+  return,simdata
+end
+
+;;;;; THE MAIN FUNCTION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+function safe_correlate, x, y, xerr, yerr, nsim=nsim, seed=seed
+;+
+;
+; NAME:
+;       SAFE_CORRELATE
+;
+; PURPOSE:
+;       This function computes the probability by which the null hypothesis of 
+;       uncorrelated data may be rejected while accounting for uncertainty in 
+;       the data values.
+;
+; EXPLANATION:
+;       This function generates NSIM simulated X,Y datasets based on the
+;       provided points and their erros. These are then used to compute
+;       the probability that uncorrelated data could explain the arrangement
+;       of the points, the probability-to-exceed or PTE, using Spearman's rank 
+;       correlation test. Each simulated dataset is assigned a probability of 
+;       1/NSIM of occuring. Thus, for a given dataset, the probability that the 
+;       true data (given the uncertainties) are arranged as simulated AND
+;       that this particular arrangment of data can be explained without an 
+;       underlying correlation is PTE/NSIM. These values are summed to compute 
+;       the overall probability that the data represent an uncorrelated 
+;       arrangement of points (in other words, the p-value or PTE for the null 
+;       hypothesis of uncorrelated data).
+;
+;       A tutorial on SAFE_CORRELATE is available at 
+;       http://parkeloyd.com/output/code/safe_correlate/
+;
+; CALLING SEQUENCE:
+;       Result = SAFE_CORRELATE(X, Y, XERR, YERR, [NSIM=1e4, SEED=SEED])
+;
+; INPUTS:
+;       X,Y:       N-element vectors of the data points. These are ignored if 
+;                  PDF input is supplied for X or Y (see below).
+;       
+;       XERR,YERR: The data point errors. These may be supplied as a scalar, 
+;                  N-element vector, 2xM array, or Nx2xM array.
+;                  scalar: The identical Gaussian 1-sigma error for all 
+;                          points.
+;                  N vector: The Gaussian 1-sigma error for each respective 
+;                            point.
+;                  Nx2xM array: M points sampling the probability distribution 
+;                               function (PDF) for each data point. The values 
+;                               are contained in [N,0,*] and probability 
+;                               densities in [N,1,*]. This is useful for 
+;                               non-Gaussian errors, especially upper limits.
+;
+; KEYWORD PARAMETERS:
+;       NSIM: The number of X,Y datasets to simulate. Default = 1e4.
+;       SEED: Random number seed for use with RANDOMN and RANDOMU. Useful for 
+;             ensuring reproducible results. Can either be an input value or 
+;             a variable into which the used value will be stored.
+;
+; EXAMPLES:
+;       Data with identical errors:
+;         xerr = 2.0
+;         yerr = 3.0
+;          
+;         ;generate linear data with errors
+;         N = 10
+;         x = findgen(N) + randomn(seed,N)*xerr
+;         y = findgen(N) + randomn(seed,N)*yerr
+;          
+;         ;plot
+;         ep = errorplot(x,y,replicate(xerr,N),replicate(yerr,N),'o')
+;          
+;         ;corrrelate
+;         print,safe_correlate(x,y,xerr,yerr)
+;          
+;       Data with differing errors, 5e3 simulations:
+;         ;generate nonuniform errors
+;         N = 10
+;         xerr = randomu(seed,N) + 1.0
+;         yerr = randomu(seed,N)*1.5 + 1.0
+;         
+;         ;generate linear data with errors
+;         x = findgen(N) + randomn(seed,N)*xerr
+;         y = findgen(N) + randomn(seed,N)*yerr
+;         
+;         ;plot
+;         ep = errorplot(x,y,xerr,yerr,'o')
+;         
+;         ;correlate
+;         print,safe_correlate(x,y,xerr,yerr,nsim=5e3)
+;         
+;       Data with non-gaussian errors
+;         ;generate linear data with some scatter
+;         N = 10
+;         x = findgen(N) + 5 + 2*randomn(seed,N)
+;         y = findgen(N) + 5 + 3*randomn(seed,N)
+;         
+;         ;assign uniform pdfs to the x data and gamma distributions to the
+;         ;y data (just for example, since the data were actaully generated
+;         ;from a Gaussian PDF)
+;         ;note that the PDFs do not have to be normalized
+;         M = 1000 ;number of points sampling pdfs
+;         xerr = fltarr(N,2,M)
+;         yerr = fltarr(N,2,M)
+;         t = 0.7 ;gamma distribution scale parameter
+;         for i = 0,N-1 do begin &$
+;           xvalues = findgen(M)/(M-1) + x[i] - 0.5 &$ ;width = 1.0
+;           xprobs = replicate(1.0, M) &$
+;           xerr[i,0,*] = xvalues &$
+;           xerr[i,1,*] = xprobs &$
+;           yvalues = findgen(M)/(M-1)*y[i]*2.0 &$
+;           k = y[i]/t + 1 &$
+;           yprobs = yvalues^(k-1)*exp(-yvalues/t)/t^k/gamma(k) &$
+;           yerr[i,0,*] = yvalues &$
+;           yerr[i,1,*] = yprobs &$
+;         endfor
+;         
+;         ;correlate
+;         print,safe_correlate(x,y,xerr,yerr)
+;
+; REFERENCE:
+;       See Numerical Recipes by Press et al. for information on the 
+;       Spearman Rank correlation test.
+;
+; MODIFICATION HISTORY:
+;       Written by:  R. O. Parke Loyd, 2014-07
+;-
+
+;;;;; GROOM AND VET THE INPUT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+bad_err_msg = 'Bad input shape for xerr or yerr, see code header.'
+
+;determine type of error array supplied (sigma, sigmas, pdfs)
+xerrtype = errtype(xerr, bad_err_msg)
+yerrtype = errtype(yerr, bad_err_msg)
+
+;check if x and y are going to be used and, if so, make sure they have the same
+;length
+if xerrtype eq 'pdfs' then begin
+  temp = size(xerr)
+  n = temp[1]
+endif else begin
+  if yerrtype eq 'pdfs' then begin
+    temp = size(yerr)
+    n = temp[1]
+  endif else begin
+    n = n_elements(x)
+    if n ne n_elements(y) then begin
+      message, 'The x and y vectors must have the same number of points.'
+    endif
+  endelse
+endelse
+
+;check that error input is good and determine its type
+vet_err,xerr,xerrtype,n,bad_err_msg
+vet_err,yerr,yerrtype,n,bad_err_msg
+
+;record whether double precision is used
+dbl = isa(x,'double') or isa(y,'double')
+
+;set default number of simulations
+if ~keyword_set(nsim) then nsim = 1e4
+
+;;;;; GENERATE SIMULATED DATA ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+xsim = generate_data(x, xerr, xerrtype, n, nsim, dbl, seed)
+ysim = generate_data(y, yerr, yerrtype, n, nsim, dbl, seed)
+
+;;;;; COMPUTE PROBABILITY TO EXCEED FOR NULL HYPOTHESIS ;;;;;;;;;;;;;;;;;;;;;;; 
+
+pte = 0.0d
+for i = 0,nsim-1 do begin
+  result = r_correlate(xsim[*,i], ysim[*,i])
+  pte += result[1]
+endfor
+pte = pte/nsim
+
+return,pte
+
+end
diff --git a/Code/script_idl_mv/astrolib/select_w.pro b/Code/script_idl_mv/astrolib/select_w.pro
new file mode 100644
index 0000000000000000000000000000000000000000..24971819cf4e0d122ee44525bc15f44bf4a274ea
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/select_w.pro
@@ -0,0 +1,138 @@
+PRO select_w_event, event
+;
+;This procedure is the event handler for the CW_BGROUP widget below
+COMMON select_w, val, exclusive
+
+WIDGET_CONTROL, event.id, GET_VALUE = value
+
+if exclusive then begin 
+   val = event.value
+   widget_control, event.top,/DESTROY
+   return	
+endif
+
+done = ((size(value,/tname) EQ 'STRING') && (value EQ 'DONE'))
+
+if done then begin 
+       good  = where( val GE 0, nsel )
+       if (nsel GT 0) THEN val = val[good] 
+       widget_control, event.top,/DESTROY
+       return
+endif
+
+; Get the selections
+if (event.select EQ 1) then val = [val,event.value] $
+                       else val = val[ where( val NE event.value) ]
+		       
+ 	       
+		       		       
+END
+
+PRO select_w, items, iselected, comments, command_line, only_one, $
+	Count = count, GROUP_LEADER=GROUP, selectin = selectin, columns = columns, $
+	y_scroll_size = y_scroll_size
+;+
+; NAME:
+;	SELECT_W    
+; PURPOSE:
+;	Create a non-exclusive widget menu of items
+; EXPLANATION:
+;	More than one item may be selected or 'de-selected'.   
+;
+; CALLING SEQUENCE:
+;	SELECT_W, items ,iselected, [ comments, command_line, only_one, 
+;                             SELECTIN = , COLUMNS=, Y_SCROLL_SIZE= ]
+;
+; INPUTS:
+;	items - string array giving list of items that can be selected.
+;
+; OPTIONAL INPUTS:
+;	comments - string array of comments (same number of elements as items)
+;               for each item in array selections.     Will be displayed as a
+;               tooltip when passing the cursor over the button for that item. 
+;               Should have the same number of elements as items; otherwise
+;               will be ignored (and no tooltips will be displayed).
+;                 
+;	command_line - optional command line to be placed at the bottom
+;		of the screen.  It is usually used to specify what the
+;		user is selecting.
+;	only_one - integer flag. If set to 1 then the user can only select
+;		one item.  The routine returns immediately after the first
+;		selection is made.
+;	columns - number of columns (default = 8)
+;       y_scroll_size - size of GUI in device coordinates for scrolling large lists.
+; OPTIONAL KEYWORD INPUT
+;       SELECTIN - vector of items to be pre-selected upon input (not used for
+;               only_one option)
+;
+; OUTPUT:
+;	iselected - list of indices in selections giving the selected
+;		items, in the order they were selected.
+;
+; OPTIONAL OUTPUT KEYWORD:
+;       COUNT  - Integer scalar giving the number of items selected 
+;
+; MODIFICATION HISTORY:
+;	Written, K. Venkatakrishna & W. Landsman, Hughes/STX    January, 1992
+;	Widgets made MODAL.  M. Greason, Hughes STX, 15 July 1992.
+;       Changed handling of MODAL keyword for V5.0   W.Thompson  September 1997
+;       Added selectin keyword  D. Lindler 01/12/99 
+;       Added Columns, y_scroll_size keyword inputs, D. Lindler 6/20/2013
+;       Use CW_BGROUP instead of obsolete XMENU, implement comments parameter
+;         as tooltips.   W. Landsman Aug 2013
+;       Restore SELECTIN capability  W. Landsman Aug 2013
+;       Kluge for Unix systems when Y_SCROLL_SIZE set Nov 2013
+;-
+;
+ common select_w, val, exclusive
+ 
+ if N_elements(only_one) EQ 0 then only_one = 0
+ if N_params() LT 5 then exclusive = 0 else exclusive = only_one
+ if N_elements(columns) eq 0 then columns = 8
+
+ if N_params() LT 4 then command_line = $ 
+' Select by pressing the left mouse button once; To de-select press twice; finally QUIT'
+    
+        scroll = N_elements(y_scroll_size) NE 0
+        MODAL = N_ELEMENTS(GROUP) GE 1
+        base = WIDGET_BASE( TITLE = command_line, /COLUMN, MODAL=MODAL, $
+                GROUP_LEADER=GROUP)
+; On windows, IDL knows what X_scroll_size to set to get the specified number 
+; of columns.   On Unix we need a kluge to estimate the required X_SCROLL_SIZE   
+ if (!VERSION.OS_FAMILY EQ 'unix') && keyword_set(y_scroll_size) then $
+     x_scroll_size = columns*90		
+		
+ if only_one then $
+       bgroup = cw_bgroup(base,items, COLUMN=columns, /EXCLUSIVE, $
+        y_scroll_size=y_scroll_size, ids = id, UNAME='BGROUP', $
+	x_scroll_size=x_scroll_size) $
+    else begin 
+       donebut = WIDGET_BUTTON( base, VALUE = 'DONE', UVALUE= -1)
+       if N_elements(selectin) GT 0 then begin
+           preselect = bytarr(N_elements(items))
+	   preselect[selectin] = 1b
+	   val = selectin
+       endif else val=-1 	   
+       bgroup = cw_bgroup(base,items, COLUMN=columns, $
+       /NONEXCLUSIVE,y_scroll_size=y_scroll_size, ids= id, $
+                  X_SCROLL_SIZE=x_scroll_size, UNAME='BGROUP', $
+		  set_value = preselect) 
+     endelse		  
+
+; Realize the widgets:
+ WIDGET_CONTROL, base, /REALIZE
+ 
+;In Unix one gets an error if trying to display a Tooltip of zero length 
+  lencomm = strlen(comments)
+  if N_elements(comments) EQ N_elements(items) then $
+     for i= 0, N_elements(comments)-1  do $
+         if lencomm[i] GT 0 then widget_control, id[i], ToolTip = comments[i]
+
+; Hand off to the XMANAGER, i.e.,event-handler,:
+  XMANAGER, 'select_w', base, GROUP_LEADER = GROUP
+  if val[0] NE -1 then iselected = val
+  count = N_elements( iselected)
+
+ return
+ end
+
diff --git a/Code/script_idl_mv/astrolib/sigma_filter.pro b/Code/script_idl_mv/astrolib/sigma_filter.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9cc2b6014c78dc58ede3634a0f83a50a2a47e00c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sigma_filter.pro
@@ -0,0 +1,88 @@
+function sigma_filter, image, box_width, N_SIGMA=Nsigma, ALL_PIXELS=all,   $
+					ITERATE=iterate, MONITOR=monitor,  $
+					KEEP_OUTLIERS=keep, RADIUS=radius, $
+		N_CHANGE=nchange, VARIANCE_IMAGE=imvar, DEVIATION_IMAGE=imdev
+;+
+; NAME:
+;	SIGMA_FILTER
+; PURPOSE:
+;	Replace pixels more than a specified pixels deviant from its neighbors
+; EXPLANATION:
+;	Computes the mean and standard deviation of pixels in a box centered at 
+;	each pixel of the image, but excluding the center pixel. If the center 
+;	pixel value exceeds some # of standard deviations from the mean, it is 
+;	replaced by the mean in box. Note option to process pixels on the edges.
+; CALLING SEQUENCE:
+;	Result = sigma_filter( image, box_width, N_sigma=(#), /ALL,/MON )
+; INPUTS:
+;	image = 2-D image (matrix)
+;	box_width = width of square filter box, in # pixels (default = 3)
+; KEYWORDS:
+;	N_sigma = # standard deviations to define outliers, floating point,
+;			recommend > 2, default = 3. For gaussian statistics:
+;			N_sigma = 1 smooths 35% of pixels, 2 = 5%, 3 = 1%.
+;	RADIUS = alternative to specify box radius, so box_width = 2*radius+1.
+;      /ALL_PIXELS causes computation to include edges of image,
+;      /KEEP causes opposite effect: pixels with values outside of specified
+;		deviation are not changed, pixels within deviation are smoothed.
+;      /ITERATE causes sigma_filter to be applied recursively (max = 20 times)
+;		until no more pixels change (only allowed when N_sigma >= 2).
+;      /MONITOR prints information about % pixels replaced.
+; Optional Outputs:
+;	N_CHANGE = # of pixels changed (replaced with neighborhood mean).
+;	VARIANCE = image of pixel neighborhood variances * (N_sigma)^2,
+;	DEVIATION = image of pixel deviations from neighborhood means, squared.
+; CALLS:
+;	function filter_image( )
+; PROCEDURE:
+;	Compute mean over moving box-cars using smooth, subtract center values,
+;	compute variance using smooth on deviations from mean,
+;	check where pixel deviation from mean is within variance of box,
+;	replace those pixels in smoothed image (mean) with orignal values,
+;	return the resulting partial mean image.
+; MODIFICATION HISTORY:
+;	Written, 1991, Frank Varosi and Dan Gezari NASA/GSFC
+;	F.V.1992, added optional keywords /ITER,/MON,VAR=,DEV=,N_CHANGE=.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+	if N_elements( radius ) EQ 1 then  box_width = 2*radius+1  else begin
+		if N_elements( box_width ) NE 1 then box_width=3
+		box_width = 2*(fix( box_width )/2) + 1	;make sure width is odd.
+	   endelse
+
+	if (box_width LT 3) then return,image
+	bw2 = box_width^2
+
+	mean=( filter_image( image,SMO=box_width,ALL=all )*bw2 - image )/(bw2-1)
+
+	if N_elements( Nsigma ) NE 1 then Nsigma=3
+	if (Nsigma LE 0) then return, mean
+
+	imdev = (image - mean)^2
+	fact = float( Nsigma^2 )/(bw2-2)
+	imvar = fact*( filter_image( imdev,SMO=box_width,ALL=all )*bw2 - imdev )
+
+	if keyword_set( keep )  then  wok = where( imdev GE imvar, nok ) $
+				else  wok = where( imdev LT imvar, nok )
+
+	npix = N_elements( image )
+	nchange = npix - nok
+	if keyword_set( monitor ) then $
+		print, nchange*100./npix, Nsigma, $
+			FORM="(F6.2,' % of pixels replaced, N_sigma=',F3.1)"
+
+	if (nok EQ npix) then return,image
+	if (nok GT 0) then mean[wok] = image[wok]
+
+	if keyword_set( iterate ) AND (Nsigma GE 2) then begin
+		iterate = iterate+1
+		if (iterate GT 20) then begin
+			iterate = 1
+			return,mean
+		   endif
+	    return, sigma_filter( mean, box_width, N_SIGMA=Nsigma, ALL=all,$
+					KEEP=keep, ITER=iterate, MONIT=monitor )
+	   endif
+
+return, mean
+end
diff --git a/Code/script_idl_mv/astrolib/sigrange.pro b/Code/script_idl_mv/astrolib/sigrange.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8d36123da8644d8fe20499aa4fdd395e302cb2eb
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sigrange.pro
@@ -0,0 +1,139 @@
+	FUNCTION SIGRANGE,ARRAY,FRACTION=FRACTION,MISSING=MISSING,RANGE=RANGE
+;+
+; NAME: 
+;	SIGRANGE()
+; PURPOSE: 
+;	Selects the most significant data range in an image.
+; EXPLANATION: 
+;	Selects out the most significant range in the data to be used in 
+;	displaying images.  The histogram of ARRAY is used to select the most
+;	significant range.      Useful for scaling an image display.
+; CALLING SEQUENCE: 
+;	OUTPUT = SIGRANGE( ARRAY )
+; INPUTS: 
+;	ARRAY	 = Array to take most significant range of.
+; OPTIONAL INPUTS: 
+;	None.
+; OUTPUTS: 
+;	The function returns an array where values above and below the
+;	selected range are set equal to the maximum and minimum of the
+;	range respectively.
+; OPTIONAL INPUT KEYWORDS: 
+;	FRACTION = Fraction of data to consider most significant.
+;		   Defaults to 0.99
+;	MISSING	 = Value used to flag missing points.  Data points with this
+;		   value are not considered or changed.
+; OPTIONAL OUTPUT KEYWORD
+;	RANGE    = 2 element vector, giving the range (minimum and maxmimum) 
+;		used
+;
+; NOTES:
+;       If the image array contains more than 10,000 points then SIGRANGE() 
+;       uses random indexing of a subset of the points to determine the range
+;       (for speed).    Thus identical calls to SIGRANGE() might not yield
+;       identical results (although they should be very close).     
+; RESTRICTIONS: 
+;	ARRAY must have more than two points.  Fraction must be greater than 0 
+;	and less than 1.
+;
+;	SIGRANGE was originally part of the SERTS image display package.   
+;	Other routines from this package are available at 
+;
+;	http://sohowww.nascom.nasa.gov/solarsoft/gen/idl/image/
+;
+;	Note that this version of SIGRANGE does not include the non-standard 
+;	system variables used in the SERTS package.
+; REVISION HISTORY: 
+;	Version 1, William Thompson, GSFC, 12 May 1993.
+;		Incorporated into CDS library.
+;	Version 2, William Thompson, GSFC, 25 May 1993.
+;		Changed call to HISTOGRAM to be compatible with OpenVMS/ALPHA
+;       Version 3, CDP, RAL, Add RANGE keyword.  16-Apr-96
+;	Version 4, William Thompson, GSFC, 17 April 1996
+;		Corrected some problems when range is too high.
+;	Version 5, 13-Jan-1998, William Thompson, GSFC
+;		Use random numbers to improve statistics when only using a
+;		fraction of the array.
+;	Version 6, 06-Mar-1998, William Thompson, GSFC
+;		Change default to 0.99
+;-
+;
+	IF N_ELEMENTS(FRACTION) NE 1 THEN FRACTION = 0.99
+	IF N_ELEMENTS(ARRAY) LE 2 THEN BEGIN
+	    MESSAGE, /CONTINUE, 'Not enough points to form histogram'
+	    RETURN, ARRAY
+	END ELSE IF (FRACTION LE 0) OR (FRACTION GE 1) THEN BEGIN
+	    MESSAGE, /CONTINUE, 'Fraction must be GT 0 and LT 1'
+	    RETURN, ARRAY
+	ENDIF
+;
+;  To speed up the process, work on a reduced version of ARRAY.
+;
+	IF N_ELEMENTS(ARRAY) LT 10000 THEN ATEMP0 = ARRAY ELSE BEGIN
+	    NN = 1000 > (N_ELEMENTS(ARRAY) / 25) < 100000
+	    ATEMP0 = ARRAY[N_ELEMENTS(ARRAY)*RANDOMU(SEED,NN)]
+	ENDELSE
+;
+;  Get the total range of the data, excluding any missing points.
+;
+        IF N_ELEMENTS(MISSING) EQ 1 THEN BEGIN
+                W = WHERE(ATEMP0 NE MISSING, COUNT)
+                IF COUNT GT 0 THEN ATEMP0 = ATEMP0(W)
+	ENDIF 
+	N_TOTAL = N_ELEMENTS(ATEMP0)
+	AMAX = 1.*MAX(ATEMP0)
+	AMIN = 1.*MIN(ATEMP0)
+	IF AMIN EQ AMAX THEN GOTO, EXIT_POINT
+;
+;  Set up some initial parameters for the reiteration.
+;
+	ATEMP = ATEMP0
+	DELTA = 0
+;
+;  Form the histogram, and calculate an array expressing the fraction of points
+;  that fall within or below the given bin.
+;
+FIND_RANGE:
+	LAST_DELTA = DELTA
+	X = AMIN  +  FINDGEN(1001) * (AMAX - AMIN) / 1000.
+	H = HISTOGRAM(LONG((ATEMP-AMIN)*1000./(AMAX - AMIN)))
+	FOR I = 1,N_ELEMENTS(H)-1 DO H[I] = H[I] + H[I-1]
+	H = H / FLOAT(N_TOTAL)
+;
+;  Estimate the endpoints corresponding to the specified range, and calculate
+;  the values at these endpoints.  Limit the array to be within these values.
+;
+	IMIN = (MIN( WHERE( H GT ((1. - FRACTION) / 2.) )) - 1) > 0
+	IMAX =  MIN( WHERE( H GT ((1. + FRACTION) / 2.) ))
+	IF IMAX LT 0 THEN IMAX = 1000
+	AMIN = X[IMIN]
+	AMAX = X[IMAX]
+;
+;  If the calculated range is zero, then use 2% of the full range of the data.
+;
+	IF AMAX EQ AMIN THEN BEGIN
+		BMAX = MAX(ATEMP0, MIN=BMIN)
+		AMAX = MAX(ATEMP0(WHERE(ATEMP0 LE (AMAX + 0.01*(BMAX-BMIN)))))
+		AMIN = MIN(ATEMP0(WHERE(ATEMP0 GE (AMIN - 0.01*(BMAX-BMIN)))))
+	ENDIF
+;
+;  If the range calculated has changed by more than 5% from the last iteration,
+;  the reiterate.
+;
+	ATEMP = AMIN > ATEMP0 < AMAX
+	DELTA = AMAX - AMIN
+	RATIO = (DELTA - LAST_DELTA) / (DELTA + LAST_DELTA)
+	IF ABS(RATIO) GT 0.05 THEN GOTO, FIND_RANGE
+;
+;  If a missing pixel flag value was passed, then reset those points to the
+;  flag value.  Return the adjusted array.
+;
+EXIT_POINT:
+	ATEMP = AMIN > ARRAY < AMAX
+	IF N_ELEMENTS(MISSING) EQ 1 THEN BEGIN
+		WW = WHERE(ARRAY EQ MISSING,N_MISSING)
+		IF N_MISSING GT 0 THEN ATEMP[WW] = MISSING
+	ENDIF
+        RANGE = [AMIN,AMAX]
+	RETURN, ATEMP
+	END
diff --git a/Code/script_idl_mv/astrolib/sip_eval.pro b/Code/script_idl_mv/astrolib/sip_eval.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e7ec74e047cf6eae351eef3a9963446216d27d24
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sip_eval.pro
@@ -0,0 +1,46 @@
+function sip_eval, xy
+;+
+; NAME:
+;     SIP_EVAL
+; PURPOSE:
+;     Compute distorted coordinates given SIP (simple imaging polynomial) 
+;     coefficients.
+; EXPLANATION:
+;     See http://fits.gsfc.nasa.gov/registry/sip.html for the SIP convention
+;      
+;     The coefficients are passed via common block.    This is because this
+;     routine is called by the intrinisc BROYDEN() function in AD2XY, and 
+;     common blocks are the only way to pass parameters to the user supplied 
+;     function in BROYDEN().  
+; CALLING SEQUENCE:
+;     res = SIP_EVAL(xy)   
+; INPUTS:
+;     xy - 2 elements vector giving the undistorted X,Y position  
+; OUTPUTS:
+;     res - 2 element vector giving the distorted position 
+; COMMON BLOCKS: 
+;      common broyden_coeff,xcoeff,ycoeff
+;
+;      XCOEFF, YCOEFF are both nxn arrays giving the SIP coefficient for an
+;      n x n polynomial.
+; REVISION HISTORY:
+;     Written   W. Landsman                  Dec 2013
+;-
+compile_opt idl2,hidden
+common broyden_coeff,xcoeff,ycoeff
+
+dim = size(xcoeff,/dimen)
+n = dim[0]
+xp = xy[0]
+yp = xy[1]
+
+for i= 0,n-1 do begin
+     for j=0,n-1 DO begin 
+         if xcoeff[i,j] NE 0.0 then  xp += xcoeff[i,j]*xy[0]^i*xy[1]^j
+	 if ycoeff[i,j] NE 0.0 then  yp += ycoeff[i,j]*xy[0]^i*xy[1]^j
+      endfor
+endfor 
+     	 
+return, [xp,yp]
+
+end
diff --git a/Code/script_idl_mv/astrolib/sixlin.pro b/Code/script_idl_mv/astrolib/sixlin.pro
new file mode 100644
index 0000000000000000000000000000000000000000..24fe6891e184bdd18cb5f29bb45036d868627bd2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sixlin.pro
@@ -0,0 +1,156 @@
+pro sixlin,xx,yy,a,siga,b,sigb,weight=weight
+;+
+; NAME:
+;       SIXLIN
+; PURPOSE:
+;       Compute linear regression coefficients by six different methods.
+; EXPLANATION:
+;       Adapted from the FORTRAN program (Rev. 1.1)  supplied by Isobe, 
+;       Feigelson, Akritas, and Babu Ap. J. Vol. 364, p. 104 (1990).   
+;       Suggested when there is no understanding about the nature of the 
+;       scatter about a linear relation, and NOT when the errors in the 
+;       variable are calculable.
+;
+; CALLING SEQUENCE:
+;       SIXLIN, xx, yy, a, siga, b, sigb, [WEIGHT = ]  
+;
+; INPUTS:
+;       XX - vector of X values
+;       YY - vector of Y values, same number of elements as XX
+;
+; OUTPUTS:
+;       A - Vector of 6 Y intercept coefficients
+;       SIGA - Vector of standard deviations of 6 Y intercepts
+;       B - Vector of 6 slope coefficients
+;       SIGB - Vector of standard deviations of slope coefficients
+;
+;       The output variables are computed using linear regression for each of 
+;       the following 6 cases:
+;               (0) Ordinary Least Squares (OLS) Y vs. X (c.f. linfit.pro)
+;               (1) Ordinary Least Squares  X vs. Y
+;               (2) Ordinary Least Squares Bisector
+;               (3) Orthogonal Reduced Major Axis
+;               (4) Reduced Major-Axis 
+;               (5) Mean ordinary Least Squares
+;
+; OPTIONAL INPUT KEYWORD:
+;      WEIGHT -  vector of weights, same number of elements as XX and YY
+;                For 1 sigma Gausssian errors, the weights are 1/sigma^2 but
+;                the weight vector can be more general.   Default is no 
+;                weighting. 
+; NOTES:
+;       Isobe et al. make the following recommendations
+;
+;       (1) If the different linear regression methods yield similar results
+;               then quoting OLS(Y|X) is probably the most familiar.
+;
+;       (2) If the linear relation is to be used to predict Y vs. X then
+;               OLS(Y|X) should be used.   
+;
+;       (3) If the goal is to determine the functional relationship between
+;               X and Y then the OLS bisector is recommended.
+;
+; REVISION HISTORY:
+;       Written   Wayne Landsman          February, 1991         
+;       Corrected sigma calculations      February, 1992
+;       Added WEIGHT keyword   J. Moustakas   February 2007
+;-
+ compile_opt idl2
+ On_error, 2                                   ;Return to Caller
+
+ if N_params() LT 5 then begin   
+    print,'Syntax - SIXLIN, xx, yy, a, siga, b, sigb, {WEIGHT =]'   
+    return
+  endif
+
+ b = dblarr(6) &  siga = b & sigb =b
+ x = double(xx)      ;Keep input X and Y vectors unmodified
+ y = double(yy)
+ rn = N_elements(x)
+
+ if rn LT 2 then $
+    message,'Input X and Y vectors must contain at least 2 data points'
+
+ if rn NE N_elements(y) then $
+    message,'Input X and Y vectors must contain equal number of data points'
+
+ if (n_elements(weight) eq 0L) then weight = replicate(1.0,rn) else begin
+    if (rn ne n_elements(weight)) then $
+      message,'Input X and WEIGHT vectors must contain equal number of data points'
+ endelse
+ 
+; Compute averages and sums
+
+ sumw = total(weight) 
+ 
+ xavg = total( weight * x)/sumw
+ yavg = total( weight * y)/sumw
+ x = x - xavg
+ y = y - yavg
+ sxx = total( weight * x^2)
+ syy = total( weight * y^2)
+ sxy = total( weight * x*y)
+ if sxy EQ 0. then $
+      message,'SXY is zero, SIXLIN is terminated'
+ if sxy LT 0. then sign = -1.0 else sign = 1.0
+
+; Compute the slope coefficients
+
+ b[0] = sxy / sxx
+ b[1] = syy / sxy
+ b[2] = (b[0]*b[1] - 1.D + sqrt((1.D + b[0]^2)*(1.D +b[1]^2)))/(b[0] + b[1] )
+ b[3] = 0.5 * ( b[1] - 1.D/b[0] + sign*sqrt(4.0D + (b[1]-1.0/b[0])^2))
+ b[4] = sign*sqrt( b[0]*b[1] )
+ b[5] = 0.5 * ( b[0] + b[1] )
+
+; Compute Intercept Coefficients
+
+ a = yavg - b*xavg
+
+;  Prepare for computation of variances
+
+ gam1 = b[2] / ( (b[0] + b[1]) *   $
+         sqrt( (1.D + b[0]^2)*(1.D + b[1]^2)) )
+ gam2 = b[3] / (sqrt( 4.D*b[0]^2 + ( b[0]*b[1] - 1.D)^2))
+ sum1 = total( weight * ( x*( y - b[0]*x ) )^2)
+ sum2 = total( weight * ( y*( y - b[1]*x ) )^2)
+ sum3 = total( weight * x * y * ( y - b[0]*x) * (y - b[1]*x ) )
+ cov = sum3 / ( b[0]*sxx^2 )
+
+; Compute variances of the slope coefficients
+
+ sigb[0] = sum1 / sxx^2
+ sigb[1] = sum2 / sxy^2
+ sigb[2] = (gam1^2) * ( ( (1.D + b[1]^2) ^2 )*sigb[0] +  $
+                  2.D*(1.D + b[0]^2) * (1.D + b[1]^2)*cov + $
+                  (  (1.D + b[0]^2)^2)*sigb[1] )
+ sigb[3] = (gam2^2)*( sigb[0]/b[0]^2 + 2.D*cov + b[0]^2*sigb[1] )
+ sigb[4] = 0.25*(b[1]*sigb[1]/b[1] + $
+                     2.D*cov + b[0]*sigb[1]/b[1] )
+ sigb[5] = 0.25*(sigb[0] + 2.D*cov + sigb[1] )
+
+; Compute variances of the intercept coefficients
+
+ siga[0] = total( weight * ( ( y - b[0]*x) * (1.D - sumw*xavg*x/sxx) )^2 )
+ siga[1] = total( weight * ( ( y - b[1]*x) * (1.D - sumw*xavg*y/sxy) )^2 ) 
+ siga[2] = total( weight * ( (x * (y - b[0]*x) * (1.D + b[1]^2) / sxx + $
+                  y * (y - b[1]*x) * (1.D + b[0]^2) / sxy)*  $
+                  gam1 * xavg * sumw - y + b[2] * x) ^ 2)
+ siga[3] = total( weight * ( ( x * ( y - b[0]*x) / sxx + $
+                   y * ( y - b[1]*x) * b[0]^2/ sxy) * gam2 * $
+                   xavg * sumw / sqrt( b[0]^2) - y + b[3]*x) ^ 2 )
+ siga[4] = total( weight * ( ( x * ( y - b[0] * x) * sqrt( b[1] / b[0] ) / sxx + $
+                   y * ( y - b[1] * x) * sqrt( b[0] / b[1] ) / sxy) * $
+                  0.5 * sumw * xavg - y + b[4] * x)^2 )
+
+ siga[5] = total( weight * ( (x * ( y - b[0] * x) / sxx +  $
+                  y * ( y - b[1] * x) / sxy)*    $
+                  0.5 * sumw * xavg - y + b[5]*x )^2 )
+
+; Convert variances to standard deviation
+
+ sigb = sqrt(sigb)
+ siga = sqrt(siga)/sumw
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/sixty.pro b/Code/script_idl_mv/astrolib/sixty.pro
new file mode 100644
index 0000000000000000000000000000000000000000..126136c2b0b94726ae1ffbe7ab5d422f59c3d3e8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sixty.pro
@@ -0,0 +1,66 @@
+      FUNCTION sixty,scalar, Trailsign = trailsign
+;+
+; NAME:
+;	SIXTY()
+; PURPOSE:
+;	Converts a decimal number to sexagesimal.
+; EXPLANATION:
+;	Reverse of the TEN() function.
+;
+; CALLING SEQUENCE:
+;	X = SIXTY( SCALAR, [ /TrailSign ] ) 
+;
+; INPUTS:
+;	SCALAR -- Decimal quantity.  
+; OUTPUTS:
+;	Function value returned = real vector of three elements, 
+;	sexagesimal equivalent of input decimal quantity.    Double
+;       precision if the input is double, otherwise floating point.
+;	By default, a negative number is signified by making the first non-zero
+;	element of the output vection negative, but this can be modified with
+;       the /TrailSign keyword.
+;
+; OPTIONAL INPUT KEYWORD:
+;      /TrailSign - By default, SIXTY() returns a negative sign in the first
+;         nonzero element.   If /TrailSign is set, then SIXTY() will return
+;         always return a negative sign in the first element, even if it is
+;         zero
+; PROCEDURE:
+;	Mostly involves checking arguments and setting the sign.
+;
+; EXAMPLE:
+;	If x = -0.345d then sixty(x) = [0.0, -20.0, 42.0]
+;                      and sixty(x,/trail) = [-0.0, 20.0, 42.0]
+; MODIFICATION HISTORY:
+;	Written by R. S. Hill, STX, 19-OCT-87         
+;	Output changed to single precision.  RSH, STX, 1/26/88
+;	Accept single element vector   W. Landsman   Sep. 1996
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Added /TrailSign keyword, preserve data type  
+;                 B. Stecklum/ W. Landsman   March 2006
+;-
+
+      if N_elements(scalar) NE 1 then begin
+	      message,'ERROR - First parameter must contain 1 element',/CON
+	      return,replicate(100.0e0,3)
+      endif	
+
+      ss=abs(3600.0d0*scalar)
+      mm=abs(60.0d0*scalar) 
+      dd=abs(scalar) 
+      if size(scalar,/tname) EQ 'DOUBLE' then result = dblarr(3) else $
+                                             result=fltarr(3)
+      result[0]= fix(dd) 
+      result[1]= fix(mm-60.0d0*result[0])
+      result[2]= ss - 3600.d0*result[0] - 60.0d0*result[1]
+     
+      if scalar[0] lt 0.0d0 then begin 
+         if keyword_set(trailsign) then result[0] = -result[0] else begin
+            if result[0] ne 0 then result[0] = -result[0] else $
+            if result[1] ne 0 then result[1] = -result[1] else $
+            result[2] = -result[2]
+	 endelse 
+      endif
+
+      return,result
+      end
diff --git a/Code/script_idl_mv/astrolib/sky.pro b/Code/script_idl_mv/astrolib/sky.pro
new file mode 100644
index 0000000000000000000000000000000000000000..317d758a20fe6838818a4fa27e53972b548e0dd7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sky.pro
@@ -0,0 +1,185 @@
+pro sky,image,skymode,skysig, SILENT=silent, CIRCLERAD = circlerad, $
+      _EXTRA = _EXTRA, NAN = nan, MEANBACK = meanback
+;+
+; NAME:
+;       SKY
+; PURPOSE:
+;       Determine the sky level in an image 
+; EXPLANATION:
+;       Approximately 10000 uniformly spaced pixels are selected for the
+;       computation.  Adapted from the DAOPHOT routine of the same name.
+;
+;       The sky is computed either by using the procedure mmm.pro (default) 
+;       or by sigma clipping (if /MEANBACK is set) 
+;
+; CALLING SEQUENCE:
+;       SKY, image, [ skymode, skysig ,/SILENT, /MEANBACK, /NAN, CIRCLERAD= ]
+;     
+;         Keywords available  when MEANBACK is not set (passed to mmm.pro): 
+;                   /DEBUG, HIGHBAD=, /INTEGER, MAXITER=. READNOISE=
+;         Keywords available when /MEANBACK is set: 
+;                   CLIPSIG=, /DOUBLE, CONVERGE_NUM=, MAXITER=, /VERBOSE 
+; INPUTS:
+;       IMAGE - One or two dimensional array
+;
+; OPTIONAL OUTPUT ARRAYS:
+;       SKYMODE - Scalar, giving the mode of the sky pixel values of the 
+;               array IMAGE, as determined by the procedures MMM or MEANCLIP
+;       SKYSIG -  Scalar, giving standard deviation of sky brightness.   If it
+;               was not possible to derive a mode then SKYSIG is set to -1
+;
+; INPUT KEYWORD PARAMETERS:
+;	CIRCLERAD - Use this keyword to have SKY only select pixels within
+;		specified pixel radius of the center of the image.  If 
+;		CIRCLERAD =1, then the radius is set equal to half the image
+;		width.   Can only be used with square images.
+;       /MEANBACK - if set, then the background is computed using the 3 sigma 
+;             clipped mean (using meanclip.pro) rather than using the mode 
+;             computed with mmm.pro.    This keyword is useful for the Poisson 
+;             count regime or where contamination is known  to be minimal.
+;       /NAN - This keyword must be set to  ignore NaN values when computing 
+;              the sky.
+;       /SILENT - If this keyword is supplied and non-zero, then SKY will not
+;               display the sky value and sigma at the terminal
+;
+;      The _EXTRA facility can is used to pass optional keywords to the programs
+;             that actually perform the sky computation: either mmm.pro 
+;             (default) or meanclip.pro (if /MEANBACK) is set.    The following
+;             keywords are available with the mmm.pro (default) setting 
+
+;       HIGHBAD - scalar value of the (lowest) "bad" pixel level (e.g. cosmic 
+;                rays or saturated pixels) If not supplied, then there is 
+;                assumed to be no high bad pixels.
+;       READNOISE - Scalar giving the read noise (or minimum noise for any 
+;                pixel).     Normally, MMM determines the (robust) median by 
+;                averaging the central 20% of the sky values.     In some cases
+;                where the noise is low, and pixel values are quantized a
+;                larger fraction may be needed.    By supplying the optional
+;                read noise parameter, MMM is better able to adjust the
+;                fraction of pixels used to determine the median. 
+;       /INTEGER - Set this keyword if the  input SKY image only contains
+;                discrete integer values.    This keyword is only needed if the
+;                SKY image is of type float or double precision, but contains 
+;                only discrete integer values.     
+;
+;     If the /MEANBACK keyword is set then the following keywords are available
+;
+;       CLIPSIG:  Number of sigma at which to clip.  Default=3
+;	MAXITER:  Ceiling on number of clipping iterations.  Default=5
+;       CONVERGE_NUM:  If the proportion of rejected pixels is less
+;           than this fraction, the iterations stop.  Default=0.02, i.e.,
+;           iteration stops if fewer than 2% of pixels excluded.
+;       /DOUBLE - if set then perform all computations in double precision.
+;                 Otherwise double precision is used only if the input
+;                 data is double
+;
+; PROCEDURE:
+;       A grid of points, not exceeding 10000 in number, is extracted
+;       from the srray.  The mode of these pixel values is determined
+;       by the procedure mmm.pro or meanclip.pro.   In a 2-d array the grid is 
+;       staggered in each row to avoid emphasizing possible bad columns
+;
+; PROCEDURE CALLS:
+;       MEANCLIP, MMM, DIST_CIRCLE
+; REVISION HISTORY:
+;       Written, W. Landsman   STX Co.            September, 1987     
+;       Changed INDGEN to LINDGEN                 January, 1994
+;       Fixed display of # of points used         March, 1994
+;       Stagger beginning pixel in each row, added NSKY, READNOISE, HIGHBAD
+;          W. Landsman        June 2004
+;      Adjustments for unbiased sampling  W. Landsman June 2004
+;      Added /NAN keyword, put back CIRCLERAD keyword W. Landsman July 2004
+;      Added MEANBACK keyword, _EXTRA kewyord ,preserve data type in 
+;             calculations       W. Landsman November 2005
+;      Fix problem for very large images by requiring at least 2 pixels to
+;       be sampled per row.    March 2007    W. Landsman
+;      Avoid possible out of bounds if /NAN set   W. Landsman   Jan 2008
+;      Use  TOTAL(/INTEGER)      June 2009
+;      Fix occasional out of bounds problem when /NAN set W. Landsman Jul 2013
+;-
+  On_error,2              ;Return to caller
+  compile_opt idl2
+
+ if N_params() eq 0 then begin
+        print,'Syntax - sky, image, [ skymode, skysig , HIGHBAD= '
+        print, '                    READNOISE = , /NAN, CIRCLERAD = , /SILENT ]'
+        return
+ endif
+
+ checkbad = (N_elements(highbad) GT 0) || keyword_set(circlerad) || $
+              keyword_set(nan)                          
+ s = size(image)      
+ nrow = s[1]
+ if s[0] EQ 1 then ncol = 1 else begin                      
+    if s[0] NE 2 then message, $
+          'ERROR - Input array (first parameter) must be 1 or 2 dimensional'
+    ncol = s[2]
+ endelse
+ if keyword_set(circlerad) then if ncol ne nrow then message, $
+       'ERROR - The CIRCLERAD keyword only applies to a 2-d square array'
+        
+ if checkbad then begin 
+          mask = replicate(1b, nrow, ncol)
+          if N_elements(highbad) GT 0 then mask = mask and (image LT highbad)
+          if keyword_set(nan) then mask = mask and finite(image)
+          if keyword_set(circlerad) then begin
+                  if circlerad EQ 1 then rad = nrow/2 else rad = long(circlerad)
+                  dist_circle,drad, nrow
+                  mask = mask and (temporary(drad) LT rad)
+           endif
+          npts = total(mask,/integer)  
+ endif else  npts = N_elements(image)
+ 
+;  Use ~10000 data points or  at least 2 points per row
+ maxsky = 2*npts/(nrow-1) > 10000          ;Maximum # of pixels to be used in sky calculation
+; Maintain the same data type as the input image Nov 2005
+    istep = npts/maxsky +1
+ skyvec = make_array(maxsky+200,type=size(image,/type))
+     nstep = (nrow/istep)
+ 
+    jj = 0
+    index0 = istep*lindgen(nstep) 
+    if nstep GT 1 then begin 
+          i0 = (nrow-1 - max(index0)  - istep)/2 > 0  ;Adjust margin for symmetry
+          index0  = index0 + i0
+    endif
+
+; The beginning index in each row is staggered to avoid emphasizing possible
+; bad columns
+
+    for i=0, Ncol-1 do begin
+        index  = index0 + (i mod istep)  
+        row = image[*,i]
+        if checkbad then begin         
+            g = where(mask[*,i],ng)
+            case ng of 
+            0: goto, Done
+            Nrow: 
+            else: row = row[g]
+            endcase
+          endif else ng = nrow
+   	      imax = value_locate( index, ng-1) > 0
+	       ix = index[0:imax] < (ng-1)
+	       skyvec[jj] = row[ix]
+          jj = jj + imax + 1
+ DONE:
+
+  endfor    
+  skyvec = skyvec[0:jj-1] 
+
+ 
+  if keyword_set(meanback) then begin 
+   meanclip, skyvec, skymode, skysig,sub=sub, _EXTRA = _extra
+   nsky = N_elements(sub)
+ endif else $ 
+ MMM, skyvec, skymode, skysig, _EXTRA = _extra, nsky = nsky
+
+ skymode = float(skymode)  &  skysig = float(skysig)
+ if ~keyword_set(SILENT) then begin
+        print,'Number of points used to find sky = ',nsky
+        print,'Approximate sky value for this frame = ',skymode
+        print,'Standard deviation of sky brightness = ',skysig
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/skyadj_cube.pro b/Code/script_idl_mv/astrolib/skyadj_cube.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2a15f3706c89a0484a5966dd6c16331c762dd05f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/skyadj_cube.pro
@@ -0,0 +1,343 @@
+;+
+; NAME:                    
+;        SKYADJ_CUBE
+;
+; PURPOSE:
+;       Sky adjust the planes of a datacube.
+;
+; EXPLANATION:
+;       When removing cosmic rays from a set of images, it is desirable that
+;       all images have the same sky level.    This procedure (called by
+;       CR_REJECT) removes the sky from each image in a data cube.    
+;
+; CALLING SEQUENCE:
+;       SKYADJ_CUBE,Datacube,Skyvals,Totsky
+;
+; MODIFIED ARGUMENT:
+;       Datacube:  3-D array with one image of same field in each plane.
+;                  Returned with sky in each plane adjusted to zero.
+;
+; OUTPUT ARGUMENTS:
+;       Skyvals:   Array of sky values used on each plane of datacube.
+;                  For a scalar sky, this parameter is a vector
+;                  containing the sky value for each image plane.  For a
+;                  vector sky, this parameter is a 2-D array where each
+;                  line corresponds to one image plane.
+;
+; INPUT KEYWORD PARAMETERS:
+;
+;       REGION   - [X0,X1,Y0,Y1] to restrict area used for computation
+;                  of sky.  Default is 0.1*Xdim, 0.9*Xdim, 0.1*Ydim,
+;                  0.9*Ydim.  If INPUT_MASK is specified, the two 
+;                  specs are combined, i.e., the intersection of the
+;                  areas is used.
+;       VERBOSE  - Flag.  If set, print information on skyvals.
+;       NOEDIT   - Flag.  If set, return sky values without changing
+;                  datacube.
+;       XMEDSKY  - Flag.  If set, return vector sky as a function of X.
+;       SELECT   - Array of subscripts of planes of the cube to process.
+;                  (Default=all)
+;       EXTRAPR  - Applies only in XMEDSKY mode.
+;                  Subregion to use for polynomial extrapolation of sky
+;                  vector into portions excluded by REGION parameter.
+;                  (Default=first and last 10% of pixels; set to zero
+;                  to defeat extrapolation)
+;       EDEGREE  - Applies only in XMEDSKY mode.  
+;                  Degree of polynomial for extrapolation (Default=1)
+;       INPUT_MASK - Cube of flags corresponding to data cube.  If used,
+;                  the sky computation is restricted to the smallest 
+;                  contiguous rectangle containing all the pixels flagged
+;                  valid (with 1 rather than 0).
+;
+; PROCEDURE:
+;       Uses astronomy library "sky" routine for scalar sky and
+;       column-by-column median for vector sky.
+;
+; MODIFICATION HISTORY:
+;   10 Jul. 1997   - Written.  R. S. Hill, Hughes STX
+;   20 Oct. 1997   - 1-D sky option.  RSH
+;    7 Aug. 1998   - SELECT keyword.  RSH
+;    6 Oct. 1998   - Extrapolation.  RSH
+;    7 Oct. 1998   - INPUT_MASK added.  RSH
+;   21 Oct. 1998   - Fallback to 3-sigma clipped mean if mode fails.  RSH
+;   22 Mar. 2000   - Combine mask with region rather having mask
+;                    override region.  Improve comments.  RSH
+;   16 June 2000   - On_error and message used.  Square brackets for array 
+;                    subscripts.  EXTRAP included in this file.  
+;                    WBL & RSH, 16 June 2000
+;-
+pro EXTRAP, Deg, X, Y, Y2, LIMS=lims
+;+
+; NAME:
+;       EXTRAP
+;
+; PURPOSE:
+;       This procedure fills in the ends of a one-dimensional array from
+;       interior portions using polynomial extrapolation.
+;
+; CATEGORY:
+;       Image processing
+;
+; CALLING SEQUENCE:
+;       EXTRAP, Deg, X, Y, Y2
+;
+; INPUT POSITIONAL PARAMETERS:
+;       Deg:   Degree of polynomial
+;       X:     Independent variable
+;       Y:     Dependent variable
+;
+; KEYWORD PARAMETERS:
+;       LIMS:  3-element array giving range of X to be used to fit
+;              polynomial and starting point where extrapolation is
+;              to be substituted; if not given, you click on a plot;
+;              order of elements is [xmin, xmax, xstart]; if LIMS is
+;              specified, then program is silent
+;
+; OUTPUT POSITIONAL PARAMETERS:
+;       Y2:    Dependent variable with extrapolated portion filled in
+;
+; SIDE EFFECTS:
+;     May pop a window for selecting range.
+;
+; MODIFICATION HISTORY:
+;     Written by RSH, RITSS, 14 Aug 98
+;     Spiffed up for library.  RSH, 6 Oct 98
+;-
+IF n_params(0) LT 1 THEN BEGIN
+    print, 'CALLING SEQUENCE:  extrap, deg, x, y, y2'
+    print, 'KEYWORD PARAMETER:  lims'
+    RETALL
+ENDIF
+IF ~keyword_set(lims) THEN BEGIN
+    verbose = 1b
+    savedev = strtrim(strupcase(!D.name),2)
+    set_plot, 'X'
+    window, /free
+    plot,x,y
+    print, 'Click on fit limit 1'
+    cursor, xx1, yy1, /down, /data
+    print, 'Click on fit limit 2'
+    cursor, xx2, yy2, /down, /data
+    print, 'Click starting point of extrapolation'
+    cursor, xx3, yy3, /down, /data
+    wdelete, !D.window
+    IF savedev NE 'X' THEN set_plot, savedev
+ENDIF ELSE BEGIN
+    verbose = 0b
+    xx1 = lims[0]
+    xx2 = lims[1]
+    xx3 = lims[2]
+ENDELSE
+IF verbose THEN print,'Extrapolating from region ',xx1, ' to ', xx2
+wmin = min(where(x ge min([xx1,xx2])))
+wmax = max(where(x le max([xx1,xx2])))
+coeff = poly_fit(x[wmin:wmax],y[wmin:wmax], deg, yfit, /double)
+xhalf = 0.5*(min(x)+max(x))
+up = 1b
+if xx3 lt xhalf then up = 0b
+ypoly = poly(x, coeff)
+y2 = y
+IF up THEN BEGIN
+    if verbose then print, 'Extrapolating above x = ',xx3
+    y2[wstart] = ypoly[wstart:*]
+ENDIF ELSE BEGIN
+    if verbose then print, 'Extrapolating below x = ',xx3
+    y2[0]   = ypoly[0:wstart]
+ENDELSE
+RETURN
+END
+
+PRO SKYADJ_CUBE,Datacube,Skyvals,Totsky, XMEDSKY=xmedsky, $
+                REGION=region,VERBOSE=verbose,NOEDIT=noedit, $
+                SELECT=select,EXTRAPR=extrapr,EDEGREE=edegree, $
+                INPUT_MASK=input_mask
+
+
+xmed = keyword_set(xmedsky)
+verbose=keyword_set(verbose)
+ipm = keyword_set(input_mask)
+szc = size(datacube)
+xdim = szc[1]
+ydim = szc[2]
+zdim = szc[3]
+
+;
+;  Default region is between 10% and 90% of range in each
+;  coordinate
+IF n_elements(region) LT 1 THEN BEGIN
+    xmarg = xdim/10
+    ymarg = ydim/10
+    region = [xmarg,xdim-xmarg,ymarg,ydim-ymarg]
+ENDIF
+
+;
+;  Arrays to hold min and max good pixels according to input
+;  mask
+xmin = intarr(zdim)
+xmax = xmin
+ymax = xmin
+ymin = xmin
+
+;
+;  Process input mask if any
+IF ipm THEN BEGIN
+    ;
+    ;  Check size
+    szm = size(input_mask)
+    w_dim_ne = where(szc[0:3] NE szm[0:3], cw_dim_ne)
+    IF cw_dim_ne GT 0 THEN BEGIN
+        print, 'SKYADJ_CUBE:  INPUT_MASK has different dims from ' $
+          + 'DATACUBE'
+        print, 'Executing RETALL.'
+        retall
+    ENDIF
+    ;
+    ;  Go through planes of mask one by one
+    FOR i=0,zdim-1 DO BEGIN
+        ;
+        ;  Integrate over Y
+        xtot = total(input_mask[*,*,i],2)
+        ;
+        ;  Integrate over X
+        ytot = total(input_mask[*,*,i],1)
+        ;
+        ;  Non-zero in each dimension
+        wxt = where(xtot GT 0,cwxt)
+        wyt = where(ytot GT 0,cwyt)
+        ;
+        ;  If whole image masked out something wrong
+        IF cwxt LE 0 OR cwyt LE 0 THEN BEGIN
+            print, 'SKYADJ_CUBE:  INPUT_MASK invalid'
+            print, 'Executing RETALL'
+            retall
+        ENDIF
+        ;
+        ;  Find smallest rectangle containing all the good pixels
+        xmin1 = min(wxt,max=xmax1)
+        ymin1 = min(wyt,max=ymax1)
+        xmin[i] = xmin1
+        ymin[i] = ymin1
+        xmax[i] = xmax1
+        ymax[i] = ymax1
+    ENDFOR
+ENDIF ELSE BEGIN
+    ;
+    ;  No input mask:  set limits to whole image
+    xmin[*] = 0
+    ymin[*] = 0
+    xmax[*] = xdim-1
+    ymax[*] = ydim-1
+ENDELSE
+
+IF n_elements(edegree) LT 1 THEN edegree=1
+IF n_elements(extrapr) LT 1 THEN extrapr=0.1
+do_extrap=keyword_set(extrapr)
+
+IF n_elements(select) LT 1 THEN select=indgen(zdim)
+nsel = n_elements(select)
+
+;
+;  Initialize sky arrays
+IF xmed THEN BEGIN
+    skyvals = fltarr(xdim,zdim) - 32768.
+ENDIF ELSE BEGIN
+    skyvals = fltarr(zdim) - 32768.
+ENDELSE 
+skyplane = fltarr(xdim,ydim)
+
+;
+;  Go through all the planes that are in the selected set
+;  (probably usually all of them)
+FOR i=0,nsel-1 DO BEGIN
+    sel = select[i]
+    plane = datacube[*,*,sel]
+    ;
+    ;  Final clip region
+    clip_par = [xmin[sel]>region[0],xmax[sel]<region[1], $
+                ymin[sel]>region[2],ymax[sel]<region[3]]
+    ;
+    ;  Is sky a function of X or a scalar?
+    IF xmed THEN BEGIN
+        ;
+        ;  Function of X -- do it
+        xmedsky, plane, bkg, clip=clip_par
+        ;
+        ;  Extrapolate beyond clip points if desired
+        IF do_extrap THEN BEGIN
+            xrange = clip_par[1]-clip_par[0]+1
+            extsize = round(extrapr*xrange)
+            indx = indgen(xdim)
+            extrap, edegree, indx, temporary(bkg), bkg2, $
+                lims=[clip_par[0],clip_par[0]+extsize, $
+                clip_par[0]+0.4*extsize]
+            extrap, edegree, temporary(indx), temporary(bkg2), bkg3, $
+                lims=[clip_par[1]-extsize,clip_par[1], $
+                clip_par[1]-0.4*extsize]
+        ENDIF ELSE BEGIN
+            bkg3 = temporary(bkg)
+        ENDELSE
+        ;
+        ;  Store sky vector
+        skyvals[0,sel] = bkg3
+        ;
+        ;  Make sky image
+        FOR j=0,ydim-1 DO BEGIN 
+            skyplane[0,j] = bkg3
+        ENDFOR 
+    ENDIF ELSE BEGIN 
+        ;
+        ;  Scalar sky -- use DAOPHOT algorithm (mode as linear comb
+        ;  of mean and median)
+        sky, plane[clip_par[0]:clip_par[1],clip_par[2]:clip_par[3]], $
+          skymode, skysig, /silent
+        IF skysig LT 0 THEN BEGIN
+            ;
+            ;  Doesn't always work, but this does
+            print, 'SKYADJ_CUBE:  Fallback to 3-sigma clipped sky ' $
+                + 'for plane '+strn(i)
+            meanclip, plane[clip_par[0]:clip_par[1],clip_par[2]:clip_par[3]], $
+                skymode, skysig, verbose=verbose
+        ENDIF
+        ;
+        ;  Save sky value
+        skyvals[sel] = skymode
+        ;
+        ;  Make sky image
+        skyplane[*] = skymode
+    ENDELSE 
+    ;
+    ;  Substract the sky unless for some reason the caller says not
+    IF ~keyword_set(noedit) THEN BEGIN
+        IF verbose THEN print,'SKYADJ_CUBE:  Adjusting plane ', $
+            strn(sel)
+        datacube[0,0,sel] = plane-skyplane
+    ENDIF
+ENDFOR
+
+;
+;  Report results
+IF verbose THEN BEGIN
+    IF xmed THEN BEGIN 
+        print,'SKYADJ_CUBE:  1-D sky as function of X'
+        print,'              Average values per image plane are'
+        FOR i=0,zdim-1 DO $
+          print,'             ',avg(skyvals[*,i])
+    ENDIF ELSE BEGIN
+        print,'SKYADJ_CUBE:  Scalar sky for each image plane'
+        print,'              Values are '
+        print,'              ',skyvals
+    ENDELSE
+    print,'              Region used = ', clip_par
+ENDIF 
+
+;
+;  Compute total sky for sum of image planes
+IF xmed THEN BEGIN
+    totsky = total(skyvals[*,select],2)
+ENDIF ELSE begin
+    totsky = total(skyvals[select])
+ENDELSE
+
+RETURN
+END
+
diff --git a/Code/script_idl_mv/astrolib/solve_astro.pro b/Code/script_idl_mv/astrolib/solve_astro.pro
new file mode 100644
index 0000000000000000000000000000000000000000..409a74462b35b93f230c872d8f61edd2391fad1f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/solve_astro.pro
@@ -0,0 +1,501 @@
+;+
+; NAME:
+;    SOLVE_ASTRO
+; PURPOSE:
+;    Solve for an TANgent-plane astrometric plate solution with optional 
+;    distortion terms
+; EXPLANATION:
+;    SOLVE_ASTRO takes an input matched xpixel/ypixel/ra/dec list, and returns
+;    an IDL astrometry structure containing the astrometric
+;    solution. Optional sigma clipping on the fit. Similar in
+;    principle to IRAF's ccmap.
+; CALLING SEQUENCE:
+;    SOLVE_ASTRO, ra_degrees, dec_degrees, xpixel, ypixel,
+;    [XIORDER=, ETAORDER=, XTERMS= DISTORT=, CRVAL= NITER=, REJECT=,
+;    XIRMS=, ETARMS= SUCCESS=,VERBOSE= NORTERMS= NREJ=, N_TPVTERMS=,
+;    XIRESID=, ETARESID=, WFIT= ]
+; INPUT ARGUMENT:
+;    ra_degrees  - the right ascensions in degrees of the matched objects
+;    dec_degrees - the declinations in degrees
+;    xpixel      - the x pixel values
+;    ypixel      - the y pixel values
+; RETURNS:
+;    astr        - an IDL astrometric structure containing the plate solution
+; OPTIONAL INPUT KEYWORDS:
+;    distort     - type of distortion to fit. Options: 'none': just
+;                  fit the linear solution, 'tpv' fit the TPV
+;                  convention, 'tnx': fit the iraf tnx
+;                  convention. default: 'none'. See:
+;                  http://iraf.noao.edu/projects/ccdmosaic/tnx.html
+;                  for TNX
+;                  http://iraf.noao.edu/projects/ccdmosaic/tpv.html
+;                  for TPV
+;    crval       - the ra/dec of the reference pixel in
+;                  degrees. Default is mean of the input coordinates
+;    nrej        - Number of rejection iterations
+;    reject      - the sigma of the rejection (input to RESISTANT_MEAN)
+;    etaorder    - The order of the TNX coefficients in eta
+;    xiorder     - The order of the TNX coefficients in xi
+;    xterms      - TNX cross-terms type (0: none, 1: full, 2: half)
+;    n_tpvterms  - Number of TPV coefficients to include. Always
+;                  includes rterms even if /NORTERMS is passed. Useful
+;                  values include: 7 (quadratic), 12 (cubic), 17
+;                  (quartic).
+;    norterms    - Do not fit the "r" terms if using a TPV distortion
+;    naxis1      - xpixel size of image being fit. If passed this is
+;                  inserted into the final astrometry structure, but
+;                  are not required.
+;    naxis2      - ypixel size of image being fit. If passed this is
+;                  inserted into the final astrometry structure, but
+;                  are not required.
+;    verbose     - Give verbose output
+; OPTIONAL OUTPUT KEYWORD:
+;    xirms       - the xi fit rms in arcseconds
+;    etarms      - the eta fit rms in arcseconds
+;    xiresid     - Residuals of each point in xi entering the final
+;                  (fit indices of input points given by wfit).
+;    etaresid    - Residuals of each point in eta entering the final
+;                  (fit indices of input points given by wfit).
+;    wfit        - index of input datapoints that were used in the
+;                  final fit
+;    success     - Did the code run successfully?
+; NOTES:
+;  The implmentation is slightly limited. It can only fit the
+;  polynomial terms in TNX and not the legendre or chebyshev. The x/y
+;  pixels must be previously matched with the ra/decs. The use of
+;  MPFIT2DFUN is probably not needed and could be re-written to run
+;  faster with LAPACK libraries. For tnx projections, orders in eta and xi cannot currently
+;  be specified separately.
+;  There is code duplication between tpv_eval, tnx_eval and this routine
+;  Input/Output is always assumed to be FK5/equinox 2000. Could be changed!
+; PROCEDURES USED:
+;       STRN() - in astrolib
+;       RESISTANT_MEAN - in astrolib
+;       MPFIT2DFUN(), MPFIT - in MPFIT library, can be downloaded from either
+;               from   http://cow.physics.wisc.edu/~craigm/idl/fitting.html
+;               or     http://idlastro.gsfc.nasa.gov/ftp/pro/markwardt/
+;       LA_LEAST_SQUARES() - IDL built-in
+; REVISION HISTORY:
+;       Written, M. Sullivan, March 2014
+;       Modified to better handle fields that straddle RA=0
+;       Modified to populate final astrometry structure with naxis1/2
+;         if these are passed
+;
+;-
+
+FUNCTION xi_solve_tpv,xpixel,ypixel,pv1,TPVINFO=tpvinfo
+COMPILE_OPT IDL2, STRICTARRSUBS
+
+xin=tpvinfo.CD11*(xpixel-tpvinfo.crpix1)+tpvinfo.cd12*(ypixel-tpvinfo.crpix2)
+yin=tpvinfo.CD21*(xpixel-tpvinfo.crpix1)+tpvinfo.cd22*(ypixel-tpvinfo.crpix2)
+
+
+npv1=N_ELEMENTS(pv1)
+
+xin2=xin*xin
+yin2=yin*yin
+r=SQRT(xin2+yin2)
+
+xp=pv1[0] + pv1[1]*xin + pv1[2]*yin
+IF(npv1 GT 3 && pv1[3] NE 0.0)THEN xp += pv1[3]*r
+IF(npv1 GT 4 && pv1[4] NE 0.0)THEN xp += pv1[4]*xin2
+IF(npv1 GT 5 && pv1[5] NE 0.0)THEN xp += pv1[5]*xin*yin
+IF(npv1 GT 6 && pv1[6] NE 0.0)THEN xp += pv1[6]*yin2
+IF(npv1 GT 7)THEN BEGIN
+   IF(pv1[7] NE 0.0)THEN xp += pv1[7]*xin^3
+   IF(Npv1 GT 8 && pv1[8] NE 0.0)THEN xp += pv1[8]*xin2*yin
+   IF(Npv1 GT 9 && pv1[9] NE 0.0)THEN xp += pv1[9]*xin*yin2
+   IF(Npv1 GT 10 && pv1[10] NE 0.0)THEN xp += pv1[10]*yin2*yin
+   IF(Npv1 GT 11 && pv1[11] NE 0.0)THEN xp += pv1[11]*r^3
+   IF(Npv1 GT 12) THEN BEGIN
+      IF(pv1[12] NE 0.0) THEN xp += pv1[12]*xin2*xin2
+      IF(npv1 GT 13 && pv1[13] NE 0.0) THEN xp += pv1[13]*xin2*xin*yin
+      IF(npv1 GT 14 && pv1[14] NE 0.0) THEN xp += pv1[14]*xin2*yin2
+      IF(npv1 GT 15 && pv1[15] NE 0.0) THEN xp += pv1[15]*xin*yin2*yin
+      IF(npv1 GT 16 && pv1[16] NE 0.0) THEN xp += pv1[16]*yin2*yin2
+   ENDIF
+ENDIF      
+
+RETURN,xp
+END
+
+
+FUNCTION eta_solve_tpv,xpixel,ypixel,pv2,TPVINFO=tpvinfo
+COMPILE_OPT IDL2, STRICTARRSUBS
+
+xin=tpvinfo.CD11*(xpixel-tpvinfo.crpix1)+tpvinfo.cd12*(ypixel-tpvinfo.crpix2)
+yin=tpvinfo.CD21*(xpixel-tpvinfo.crpix1)+tpvinfo.cd22*(ypixel-tpvinfo.crpix2)
+
+
+npv2=N_ELEMENTS(pv2)
+
+xin2=xin*xin
+yin2=yin*yin
+r=SQRT(xin2+yin2)
+
+yp=pv2[0] + pv2[1]*yin + pv2[2]*xin
+IF(npv2 GT 3 && pv2[3] NE 0.0)THEN yp += pv2[3]*r
+IF(npv2 GT 4 && pv2[4] NE 0.0)THEN yp += pv2[4]*yin2
+IF(npv2 GT 5 && pv2[5] NE 0.0)THEN yp += pv2[5]*yin*xin
+IF(npv2 GT 6 && pv2[6] NE 0.0)THEN yp += pv2[6]*xin2
+IF(npv2 GT 7)THEN BEGIN
+   IF(pv2[7] NE 0.0)THEN yp += pv2[7]*yin^3
+   IF(Npv2 GT 8 && pv2[8] NE 0.0)THEN yp += pv2[8]*yin2*xin
+   IF(Npv2 GT 9 && pv2[9] NE 0.0)THEN yp += pv2[9]*yin*xin2
+   IF(Npv2 GT 10 && pv2[10] NE 0.0)THEN yp += pv2[10]*xin2*xin
+   IF(Npv2 GT 11 && pv2[11] NE 0.0)THEN yp += pv2[11]*r^3
+   IF(Npv2 GT 12) THEN BEGIN
+      IF(pv2[12] NE 0.0) THEN yp += pv2[12]*yin2*yin2
+      IF(npv2 GT 13 && pv2[13] NE 0.0) THEN yp += pv2[13]*yin2*yin*xin
+      IF(npv2 GT 14 && pv2[14] NE 0.0) THEN yp += pv2[14]*yin2*xin2
+      IF(npv2 GT 15 && pv2[15] NE 0.0) THEN yp += pv2[15]*yin*xin2*xin
+      IF(npv2 GT 16 && pv2[16] NE 0.0) THEN yp += pv2[16]*xin2*xin2
+   ENDIF
+ENDIF      
+
+RETURN,yp
+END
+
+
+FUNCTION eta_solve_tnx,xpixel,ypixel,params,TNXINFO=tnxinfo
+COMPILE_OPT IDL2, STRICTARRSUBS
+
+xin=tnxinfo.CD11*(xpixel-tnxinfo.crpix1)+tnxinfo.cd12*(ypixel-tnxinfo.crpix2)
+yin=tnxinfo.CD21*(xpixel-tnxinfo.crpix1)+tnxinfo.cd22*(ypixel-tnxinfo.crpix2)
+
+yp=0.d0
+icount=0L
+IF(tnxinfo.latcor.xterms EQ 1)THEN BEGIN
+   ;; full cross-terms
+   FOR n=0,tnxinfo.latcor.etaorder-1 DO BEGIN
+      FOR m=0,tnxinfo.latcor.xiorder-1 DO BEGIN
+         yp=yp + xin^m * yin^n * params[icount]
+         icount++
+      ENDFOR
+   ENDFOR
+ENDIF ELSE IF(tnxinfo.latcor.xterms EQ 0)THEN BEGIN
+   ;; no cross-terms
+   FOR m=0,tnxinfo.latcor.xiorder-1 DO BEGIN
+      yp=yp + xin^m * params[icount]
+      icount++
+   ENDFOR
+   FOR n=0,tnxinfo.latcor.etaorder-1 DO BEGIN
+      yp=yp + yin^n * params[icount]
+      icount++
+   ENDFOR
+ENDIF ELSE IF(tnxinfo.latcor.xterms EQ 2)THEN BEGIN
+   ;; half cross terms
+   maxxt=MAX([tnxinfo.latcor.xiorder,tnxinfo.latcor.etaorder])-1
+   FOR n=0,tnxinfo.latcor.etaorder-1 DO BEGIN
+      FOR m=0,tnxinfo.latcor.xiorder-1 DO BEGIN
+         IF(m+n GT maxxt)THEN CONTINUE
+;         print,m,n,m+n,icount
+         yp=yp + xin^m * yin^n * params[icount]
+         icount++
+      ENDFOR
+   ENDFOR   
+ENDIF
+
+etamodel=yin+yp
+
+RETURN,etamodel
+END
+
+FUNCTION xi_solve_tnx,xpixel,ypixel,params,TNXINFO=tnxinfo
+COMPILE_OPT IDL2, STRICTARRSUBS
+
+xin=tnxinfo.CD11*(xpixel-tnxinfo.crpix1)+tnxinfo.cd12*(ypixel-tnxinfo.crpix2)
+yin=tnxinfo.CD21*(xpixel-tnxinfo.crpix1)+tnxinfo.cd22*(ypixel-tnxinfo.crpix2)
+
+xp=0.d0
+icount=0L
+IF(tnxinfo.lngcor.xterms EQ 1)THEN BEGIN
+   ;; full cross-terms
+   FOR n=0,tnxinfo.lngcor.etaorder-1 DO BEGIN
+      FOR m=0,tnxinfo.lngcor.xiorder-1 DO BEGIN
+         xp=xp + xin^m * yin^n * params[icount]
+         icount++
+      ENDFOR
+   ENDFOR
+ENDIF ELSE IF(tnxinfo.lngcor.xterms EQ 0)THEN BEGIN
+   ;; no cross-terms
+   FOR m=0,tnxinfo.lngcor.xiorder-1 DO BEGIN
+      xp=xp + xin^m * params[icount]
+      icount++
+   ENDFOR
+   FOR n=0,tnxinfo.lngcor.etaorder-1 DO BEGIN
+      xp=xp + yin^n * params[icount]
+      icount++
+   ENDFOR
+ENDIF ELSE IF(tnxinfo.lngcor.xterms EQ 2)THEN BEGIN
+   ;; half cross terms
+   maxxt=MAX([tnxinfo.lngcor.xiorder,tnxinfo.lngcor.etaorder])-1
+   FOR n=0,tnxinfo.lngcor.etaorder-1 DO BEGIN
+      FOR m=0,tnxinfo.lngcor.xiorder-1 DO BEGIN
+         IF(m+n GT maxxt)THEN CONTINUE
+;         print,m,n,m+n,icount
+         xp=xp + xin^m * yin^n * params[icount]
+         icount++
+      ENDFOR
+   ENDFOR   
+ENDIF
+
+ximodel=xin+xp
+
+RETURN,ximodel
+END
+
+FUNCTION solve_astro,radeg,decdeg,xpixel,ypixel,XIORDER=xiorder,ETAORDER=etaorder,XTERMS=xterms,$
+                     DISTORT=distort,CRVAL=crval,NITER=niter,REJECT=reject,XIRMS=xirms,ETARMS=etarms,$
+                     SUCCESS=success,VERBOSE=verbose,NORTERMS=norterms,NREJ=nrej,n_tpvterms=n_tpvterms,$
+                     XIRESID=xiresid,ETARESID=etaresid,WFIT=wfit,NAXIS1=naxis1,NAXIS2=naxis2
+COMPILE_OPT IDL2, STRICTARRSUBS
+
+success=0b
+IF(N_ELEMENTS(verbose) EQ 0)THEN verbose=0b
+IF(N_ELEMENTS(distort) EQ 0)THEN mydistort='none' ELSE mydistort=STRLOWCASE(distort)
+IF(N_ELEMENTS(reject) EQ 0)THEN reject=3.0
+IF(N_ELEMENTS(niter) EQ 0)THEN niter=1S
+
+IF(N_ELEMENTS(radeg) NE N_ELEMENTS(decdeg))THEN BEGIN
+   PRINT,'ERROR in solve_astro: xpixel/ypixel/radeg/decdeg must contain the same number of elements'
+   RETURN,0
+ENDIF
+
+IF(N_ELEMENTS(xpixel) NE N_ELEMENTS(ypixel))THEN BEGIN
+   PRINT,'ERROR in solve_astro: xpixel/ypixel/radeg/decdeg must contain the same number of elements'
+   RETURN,0
+ENDIF
+
+IF(N_ELEMENTS(xpixel) NE N_ELEMENTS(radeg))THEN BEGIN
+   PRINT,'ERROR in solve_astro: xpixel/ypixel/radeg/decdeg must contain the same number of elements'
+   RETURN,0
+ENDIF
+
+IF(N_ELEMENTS(xpixel) EQ 0)THEN BEGIN
+   PRINT,'ERROR in solve_astro: xpixel/ypixel/radeg/decdeg must contain at least 6 objects.'
+   RETURN,0
+ENDIF
+
+data=REPLICATE({index:0L,xpixel:0.d0,ypixel:0.d0,radeg:0.d0,decdeg:0.d0,eta:0.d0,xi:0.d0},N_ELEMENTS(xpixel))
+data.xpixel=xpixel
+data.ypixel=ypixel
+data.radeg=radeg
+data.decdeg=decdeg
+ndata=N_ELEMENTS(data)
+data.index=LINDGEN(ndata)
+
+IF (ndata LE 5)THEN BEGIN
+   PRINT,'ERROR in solve_astro: xpixel/ypixel/radeg/decdeg must contain at least 6 objects.'
+   RETURN,0
+ENDIF
+
+CASE mydistort OF 
+   'tnx' : BEGIN
+      IF(N_ELEMENTS(xterms) EQ 0)THEN xterms=2S
+      IF(N_ELEMENTS(etaorder) EQ 0)THEN etaorder=3S
+      IF(N_ELEMENTS(xiorder) EQ 0)THEN xiorder=3S
+      IF(verbose GE 1)THEN PRINT,'Solving astrometry using '+STRN(ndata)+' points and TNX distortion, xiorder='+STRN(xiorder)+', etaorder='+STRN(etaorder)
+   END
+   'tpv' : BEGIN
+      IF(N_ELEMENTS(norterms) EQ 0)THEN norterms=0b
+      IF(N_ELEMENTS(n_tpvterms) EQ 0)THEN n_tpvterms=7S
+      IF(verbose GE 1)THEN BEGIN
+         IF(norterms)THEN PRINT,'Solving astrometry using '+STRN(ndata)+' points and TPV distortion, nterms='+STRN(n_tpvterms)+', no radial terms'
+         IF(~norterms)THEN PRINT,'Solving astrometry using '+STRN(ndata)+' points and TPV distortion, nterms='+STRN(n_tpvterms)+', with radial terms.'
+      ENDIF
+   END
+   'none': BEGIN
+      IF(verbose GE 1)THEN PRINT,'Solving astrometry using '+STRN(ndata)+' points and no distortion.'
+   END
+   ELSE : BEGIN
+      PRINT,'ERROR in solve_astro: distortion term '+mydistort+' not known.'
+      RETURN,0
+   ENDELSE
+ENDCASE
+
+
+
+;; set reference ra/dec to mean of input coords if not specified
+IF(N_ELEMENTS(crval) EQ 0)THEN BEGIN
+   ;; we need an ugly hack here to protect against fields that straddle ra=0
+   wlow=WHERE(data.radeg GE 0.0 AND data.radeg LT 10.0,nlow)
+   whigh=WHERE(data.radeg GE 350 AND data.radeg LT 360.0,nhigh)
+   IF(nlow EQ 0 || nhigh EQ 0)THEN BEGIN
+      crval=[MEAN(data.radeg,/DOUBLE),MEAN(data.decdeg,/DOUBLE)]
+   ENDIF ELSE BEGIN
+      ;; wrap around region
+      ratmp=data.radeg
+      ratmp[whigh]=ratmp[whigh]-360d0
+      ratmpmean=MEAN(ratmp,/DOUBLE)
+      IF(ratmpmean LT 0.0)THEN ratmpmean=ratmpmean+360d0
+      crval=[ratmpmean,MEAN(data.decdeg,/DOUBLE)]
+   ENDELSE
+ENDIF
+crpix=DBLARR(2)
+
+
+;; apply tangent plane projection
+WCSSPH2XY,data.radeg,data.decdeg,xi,eta,CRVAL=crval,CTYPE=['RA---TAN','DEC--TAN']
+data.xi=xi
+data.eta=eta
+
+ndataorig=ndata
+dataorig=data
+FOR iter=0,niter-1 DO BEGIN
+   ndatastart=ndata
+;         xi = CD1_1 * (x - CRPIX1) + CD1_2 * (y - CRPIX2)
+;         eta = CD2_1 * (x - CRPIX1) + CD2_2 * (y - CRPIX2)
+
+;xi = a + b * x + c * y
+;eta = d + e * x + f * y
+
+   lhs1=[[REPLICATE(1d0,ndata)],[data.xpixel],[data.ypixel]]
+   lhs1=TRANSPOSE(lhs1)
+   rhs1=data.xi
+   soln1=LA_LEAST_SQUARES(lhs1,rhs1,/DOUBLE)
+   
+   CD11=soln1[1]
+   CD12=soln1[2]
+   
+   lhs2=[[REPLICATE(1d0,ndata)],[data.xpixel],[data.ypixel]]
+   lhs2=TRANSPOSE(lhs2)
+   rhs2=data.eta
+   soln2=LA_LEAST_SQUARES(lhs2,rhs2,/DOUBLE)
+   
+   CD21=soln2[1]
+   CD22=soln2[2]
+
+   crpix[1]=(soln2[0]-CD21*soln1[0]/CD11 ) / (CD21/CD11*CD12-CD22)
+   crpix[0]=(soln1[0]+CD12*crpix[1])/((-1.0)*CD11)
+
+   IF(mydistort EQ 'tnx')THEN BEGIN
+      
+      ximin=MIN(data.xi)
+      ximax=MAX(data.xi)
+      etamin=MIN(data.eta)
+      etamax=MAX(data.eta)
+
+      IF(xterms EQ 0)THEN BEGIN
+         nparams=etaorder+xiorder ;; none
+      ENDIF ELSE IF(xterms EQ 1) THEN BEGIN
+         nparams=etaorder*xiorder ;; full
+      ENDIF ELSE IF(xterms EQ 2) THEN BEGIN
+         nparams=0
+         maxxt=MAX([xiorder,etaorder])-1
+         FOR n=0,etaorder-1 DO BEGIN
+            FOR m=0,xiorder-1 DO BEGIN
+               IF(m+n GT maxxt)THEN CONTINUE
+               nparams++
+            ENDFOR
+         ENDFOR   
+      ENDIF
+      
+      lngcor={functype:3,xiorder:xiorder,etaorder:etaorder,xterms:xterms,ximin:ximin,ximax:ximax,etamin:etamin,etamax:etamax,coeff:DBLARR(nparams)}
+      latcor={functype:3,xiorder:xiorder,etaorder:etaorder,xterms:xterms,ximin:ximin,ximax:ximax,etamin:etamin,etamax:etamax,coeff:DBLARR(nparams)}
+      
+      tnxinfo={crpix1:crpix[0],crpix2:crpix[1],cd11:cd11,cd12:cd12,cd21:cd21,cd22:cd22,$
+               lngcor:{etaorder:etaorder,xiorder:xiorder,xterms:xterms},$
+               latcor:{etaorder:etaorder,xiorder:xiorder,xterms:xterms}}
+
+      functargs={tnxinfo:tnxinfo}
+      start_params=DBLARR(nparams)
+      res=MPFIT2DFUN('xi_solve_tnx',data.xpixel,data.ypixel,data.xi,REPLICATE(0.2d0/3600,ndata),start_params,FUNCTARGS=functargs,YFIT=ximodel,QUIET=verbose LT 2)
+      lngcor.coeff=res
+      start_params=DBLARR(nparams)
+      res=MPFIT2DFUN('eta_solve_tnx',data.xpixel,data.ypixel,data.eta,REPLICATE(0.2d0/3600,ndata),start_params,FUNCTARGS=functargs,YFIT=etamodel,QUIET=verbose LT 2)
+      latcor.coeff=res
+   ENDIF ELSE IF (mydistort EQ 'tpv')THEN BEGIN
+      
+      tpvinfo={crpix1:crpix[0],crpix2:crpix[1],cd11:cd11,cd12:cd12,cd21:cd21,cd22:cd22}
+      functargs={tpvinfo:tpvinfo}
+
+      IF(norterms)THEN BEGIN
+         pi=REPLICATE({value:0.d0, fixed:0},n_tpvterms)
+         IF(n_tpvterms GE 4)THEN pi[3].fixed=1
+         IF(n_tpvterms GE 10)THEN pi[11].fixed=1
+      ENDIF
+ 
+      start_params=DBLARR(n_tpvterms)
+      pv1=MPFIT2DFUN('xi_solve_tpv',data.xpixel,data.ypixel,data.xi,REPLICATE(0.2d0/3600,ndata),start_params,FUNCTARGS=functargs,YFIT=ximodel,QUIET=verbose LT 2,PARINFO=pi)
+      
+      start_params=DBLARR(n_tpvterms)
+      pv2=MPFIT2DFUN('eta_solve_tpv',data.xpixel,data.ypixel,data.eta,REPLICATE(0.2d0/3600,ndata),start_params,FUNCTARGS=functargs,YFIT=etamodel,QUIET=verbose LT 2,PARINFO=pi)
+     
+      
+   ENDIF ELSE IF(mydistort EQ 'none')THEN BEGIN
+      ximodel=CD11*(data.xpixel-crpix[0])+cd12*(data.ypixel-crpix[1])
+      etamodel=CD21*(data.xpixel-crpix[0])+cd22*(data.ypixel-crpix[1])
+   ENDIF ELSE BEGIN
+      PRINT,'ERROR in solve_astro: distortion type '+mydistort+' not known.'
+      RETURN,0
+   ENDELSE
+
+   xiresid=(data.xi-ximodel)*3600d0 ;; in "
+   etaresid=(data.eta-etamodel)*3600d0 ; in "
+   xirms=SQRT(TOTAL(xiresid^2,/DOUBLE)/ndata)
+   etarms=SQRT(TOTAL(etaresid^2,/DOUBLE)/ndata)
+
+   ;; sigma clipping on all but final loops
+   IF(iter NE niter-1)THEN BEGIN
+      resistant_mean,xiresid,reject,m,s,nrejxi,GOODVEC=wgoodxi,/DOUBLE
+      data=data[wgoodxi]
+      resistant_mean,etaresid[wgoodxi],reject,m,s,nrejeta,GOODVEC=wgoodeta,/DOUBLE
+      data=data[wgoodeta]
+      ndata=N_ELEMENTS(data)
+   ENDIF
+   IF(ndata EQ ndatastart)THEN BREAK
+   
+ENDFOR
+nrej=ndataorig-ndata
+wfit=data.index
+
+;; construct the astro structure
+cd=DBLARR(2,2)
+cd[0,0]=cd11
+cd[1,0]=cd21
+cd[0,1]=cd12
+cd[1,1]=cd22
+
+make_astr,astro, CRPIX=crpix, CRVAL=crval, CD=cd, EQUINOX=2000., RADECSYS='FK5', CTYPE=['RA---TAN','DEC--TAN']
+
+IF(N_ELEMENTS(naxis1) GT 0)THEN astro.naxis[0]=naxis1
+IF(N_ELEMENTS(naxis2) GT 0)THEN astro.naxis[1]=naxis2
+
+IF(mydistort EQ 'tnx')THEN BEGIN
+
+   distorttmp = {name:'TNX', lngcor:lngcor, latcor:latcor}
+   astro = create_struct(temporary(astro), 'distort', distorttmp)
+   
+   astro.ctype[0]='RA---TNX'
+   astro.ctype[1]='DEC--TNX'
+   
+ENDIF ELSE IF(mydistort EQ 'tpv')THEN BEGIN
+   
+   distorttmp = {name:'TPV', a:0.0d, b:0.0d, ap:0.0d, bp:0.0d}
+   astro = create_struct(temporary(astro), 'distort', distorttmp)
+   
+   astro.ctype[0]='RA---TPV'
+   astro.ctype[1]='DEC--TPV'
+
+   ;; remove PV1/PV2 tags if make_astr wrote them
+   tags=tag_names(astro)
+   ntags=N_ELEMENTS(tags)
+   newstruct=create_struct(tags[0], astro.(0))
+   FOR i=1L,ntags-1 DO BEGIN
+      IF(tags[i] EQ 'PV1' || tags[i] EQ 'PV2')THEN CONTINUE
+      newstruct=create_struct(newstruct, tags[i], astro.(i))
+   ENDFOR
+   astro=newstruct
+   
+   astro= create_struct(temporary(astro), 'pv1', pv1)
+   astro= create_struct(temporary(astro), 'pv2', pv2)
+   
+ENDIF
+IF(verbose GE 1)THEN BEGIN
+   PRINT,'Solution has xi rms '+STRN(xirms)+' and eta rms '+STRN(etarms)+' with '+STRN(ndata)+' points.'
+ENDIF
+
+success=1b
+RETURN,astro
+END
diff --git a/Code/script_idl_mv/astrolib/spec_dir.pro b/Code/script_idl_mv/astrolib/spec_dir.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ae3fdab1625dec9bdeeca4e0ce8e902906f0165e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/spec_dir.pro
@@ -0,0 +1,60 @@
+function spec_dir,filename,extension
+;+
+; NAME:
+;     SPEC_DIR()
+; PURPOSE:
+;     Complete a file specification by appending the default disk or directory
+;
+; CALLING SEQUENCE:                      
+;     File_spec = SPEC_DIR( filename, [ extension ] )
+; INPUT:
+;     filename - character string giving partial specification of a file
+;               name.  Examples for different operating systems include the
+;                       following:
+;               Unix: 'pro/test.dat', '$IDL_HOME/test','~/subpro'
+;               MacOS: ':Programs:test'
+;               Windows: '\pro\test.dat','d:\pro\test'
+;
+; OPTIONAL INPUT:
+;     exten - string giving a default file name extension to be used if
+;             filename does not contain one.  Do not include the period.
+;
+; OUTPUT:
+;     File_spec - Complete file specification using default disk or 
+;               directory when necessary.  
+;
+; EXAMPLE:
+;      IDL> a = spec_dir('test','dat')
+;
+;      is equivalent to the commands
+;      IDL> cd, current=cdir
+;      IDL> a = cdir + delim + 'test.dat'
+;
+;      where delim is the OS-dependent separator 
+; METHOD:
+;      SPEC_DIR() decomposes the file name using FDECOMP, and appends the 
+;      default directory (obtained from the FILE_EXPAND_PATH) if necessary.   
+;
+;      SPEC_DIR() does not check whether the constructed file name actually
+;      exists.
+; PROCEDURES CALLED:
+;      FDECOMP, EXPAND_TILDE()
+; REVISION HISTORY:
+;      Written W. Landsman         STX         July, 1987
+;      Expand Unix tilde if necessary              W. Landsman  September 1997
+;      Assume since V5.5, use FILE_EXPAND_PATH, remove VMS support        
+;              W. Landsman   September 2006
+;-
+ On_error,2                                     ;Return to user
+ compile_opt idl2
+ fdecomp,filename,disk,dir,name,ext
+ if N_elements(extension) GT 0 then $ 
+      if (ext EQ '') then ext =  extension
+     
+ dir = disk+ dir    
+ if !VERSION.OS_FAMILY EQ 'unix' then $ 
+     if strpos(dir,'~') GE 0 then dir = expand_tilde(dir) 
+     
+ dir = file_expand_path(disk+dir)
+ return, dir + path_sep() + name + '.' + ext
+  end
diff --git a/Code/script_idl_mv/astrolib/sphdist.pro b/Code/script_idl_mv/astrolib/sphdist.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2e7cdfab4879ee1373eff0acbe5573953783af5d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sphdist.pro
@@ -0,0 +1,88 @@
+;-------------------------------------------------------------
+;+
+; NAME:
+;       SPHDIST
+; PURPOSE:
+;       Angular distance between points on a sphere.
+; CALLING SEQUENCE:
+;       d = sphdist(long1, lat1, long2, lat2)
+; INPUTS:
+;       long1 = longitude of point 1, scalar or vector
+;       lat1 = latitude of point 1, scalar or vector
+;       long2 = longitude of point 2, scalar or vector
+;       lat2 = latitude of point 2, scalar or vector
+;
+; OPTIONAL KEYWORD INPUT PARAMETERS:
+;       /DEGREES - means angles are in degrees, else radians.
+; OUTPUTS:
+;       d = angular distance between points (in radians unless /DEGREES
+;           is set.)
+; PROCEDURES CALLED:
+;       RECPOL, POLREC
+; NOTES:
+;       (1) The procedure GCIRC is similar to SPHDIST(), but may be more 
+;           suitable for astronomical applications.
+;
+;       (2) If long1,lat1 are scalars, and long2,lat2 are vectors, then
+;           SPHDIST returns a vector giving the distance of each element of 
+;           long2,lat2 to long1,lat1.   Similarly, if long1,lat1 are vectors,
+;           and long2, lat2 are scalars, then SPHDIST returns a vector giving
+;           giving the distance of each element of long1,lat1 to to long2,lat2. 
+;           If both long1,lat1 and long2,lat2 are vectors then SPHDIST returns
+;           vector giving the distance of each element of long1,lat1 to the 
+;           corresponding element of long2, lat2.   If the input vectors are 
+;           not of equal length, then excess elements of the longer ones will 
+;           be ignored.
+; MODIFICATION HISTORY:
+;       R. Sterner, 5 Feb, 1991
+;       R. Sterner, 26 Feb, 1991 --- Renamed from sphere_dist.pro
+;
+; Copyright (C) 1991, Johns Hopkins University/Applied Physics Laboratory
+; This software may be used, copied, or redistributed as long as it is not
+; sold and this copyright notice is reproduced on each copy made.  This
+; routine is provided as is without any express or implied warranties
+; whatsoever.  Other limitations apply as described in the file disclaimer.txt.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;-------------------------------------------------------------
+ 
+	function sphdist, long1, lat1, long2, lat2, $
+	  help=hlp, degrees=degrees
+ 
+	if (n_params(0) lt 4) or keyword_set(hlp) then begin
+	  print,' Angular distance between points on a sphere.'
+	  print,' d = sphdist(long1, lat1, long2, lat2)'
+	  print,'   long1 = longitude of point 1.         in'
+	  print,'   lat1 = latitude of point 1.           in'
+	  print,'   long2 = longitude of point 2.         in'
+	  print,'   lat2 = latitude of point 2.           in'
+	  print,'   d = angular distance between points.  out'
+	  print,' Keywords:'
+	  print,'   /DEGREES means angles are in degrees, else radians.'
+	  print,' Notes: points 1 and 2 may be arrays.'
+	  return, -1
+	endif
+ 
+	cf = 1.0
+	if keyword_set(degrees) then cf = !radeg
+ 
+	;--- Convert both points to rectangular coordinates. ---
+	polrec, 1.0, lat1/cf, rxy, z1
+	polrec, rxy, long1/cf, x1, y1
+	polrec, 1.0, lat2/cf, rxy, z2
+	polrec, rxy, long2/cf, x2, y2
+ 
+	;--- Compute vector dot product for both points. ---
+	cs = x1*x2 + y1*y2 + z1*z2
+ 
+	;--- Compute the vector cross product for both points. ---
+	xc = y1*z2 - z1*y2
+	yc = z1*x2 - x1*z2
+	zc = x1*y2 - y1*x2
+	sn = sqrt(xc*xc + yc*yc + zc*zc)
+ 
+	;--- Convert to polar.  ------
+	recpol, cs, sn, r, a
+	return, cf*a
+ 
+	end
diff --git a/Code/script_idl_mv/astrolib/srcor.pro b/Code/script_idl_mv/astrolib/srcor.pro
new file mode 100644
index 0000000000000000000000000000000000000000..cb3d36223b9512427cdab5137011281aeed8fb88
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/srcor.pro
@@ -0,0 +1,257 @@
+PRO srcor,x1in,y1in,x2in,y2in,dcr,ind1,ind2,option=option,magnitude=magnitude,$
+   spherical=spherical,silent=silent,count = count
+;+
+; NAME:
+;       SRCOR
+; PURPOSE:
+;       Correlate the source positions found on two lists.
+;
+; EXPLANATION:
+;       Source matching is done by finding sources within a specified radius.
+;       If you have position errors available and wish to match by significance
+;       level, then try match_xy.pro in the TARA library 
+;      (http://www.astro.psu.edu/xray/docs/TARA/)
+;
+; CALLING SEQUENCE:
+;       srcor,x1in,ylin,x2in,y2in,dcr,ind1,ind2,
+;                         [MAGNITUDE=,SPHERICAL=,COUNT=,/SILENT]
+; INPUTS:
+;       x1in,y1in - First set of x and y coordinates.  The program
+;                   marches through this list element by element,
+;                   looking in list 2 for the closest match.  So, the program
+;                   will run faster if this is the shorter of the two lists.
+;                   Unless you use the option or magnitude keyword, there is
+;                   nothing to guarantee unique matches.  
+;       x2in,y2in - Second set of x and y coordinates.  This list is
+;                   searched in its entirety every time one element of list 1
+;                   is processed.
+;       dcr - Critical radius outside which correlations are rejected;
+;             but see 'option' below.
+; OPTIONAL KEYWORD INPUT:
+;       option - Changes behavior of program and description of output
+;                lists slightly, as follows: 
+;       OPTION=0 or left out
+;             Same as older versions of SRCOR.  The closest match from list2
+;             is found for each element of list 1, but if the distance is
+;             greater than DCR, the match is thrown out.  Thus the index
+;             of that element within list 1 will not appear in the IND1 output
+;             array.
+;       OPTION=1
+;             Forces the output mapping to be one-to-one.  OPTION=0 results,
+;             in general, in a many-to-one mapping from list 1 to list 2.
+;             Under OPTION=1, a further processing step is performed to
+;             keep only the minimum-distance match, whenever an entry from
+;             list 1 appears more than once in the initial mapping.
+;       OPTION=2
+;             Same as OPTION=1, except the critical distance parameter DCR
+;             is ignored.  I.e., the closest object is retrieved from list 2
+;             for each object in list 1 WITHOUT a critical-radius criterion,
+;             then the clean-up of duplicates is done as under OPTION=1.
+;       magnitude
+;             An array of stellar magnitudes corresponding to x1in and y1in.  
+;             If this is supplied, then the brightest star from list 1
+;             within the selected distance of the star in list 2 is taken.
+;             The option keyword is ignored in this case.
+;       spherical
+;             If SPHERICAL=1, it is assumed that the input arrays are in
+;             celestial coordinates (RA and Dec), with x1in and x2in in
+;             decimal hours and y1in and y2in in decimal degrees.  If
+;             SPHERICAL=2 then it is assumed that the input arrays are in
+;             longitude and latitude with x1in,x2in,y1in,y2in in decimal
+;             degrees.  In both cases, the critial radius dcr is in
+;             *arcseconds*.  Calculations of spherical distances are made
+;             with the gcirc program.
+; OUTPUTS:
+;       ind1 - index of matched stars in first list, set to -1 if no matches
+;              found
+;       ind2 - index of matched stars in second list
+; OPTIONAL OUTPUT KEYWORD:
+;       Count - integer giving number of matches returned
+; PROCEDURES USED:
+;       GCIRC, REMOVE
+; REVISON HISTORY:
+;       Adapted from UIT procedure  J.Wm.Parker, SwRI 29 July 1997
+;       Improve speed for spherical searches, added /SILENT keyword  
+;                               W. Landsman  Mar 2009
+;       Avoid error when no matches found with /SPHERICAL  O. Trottier June 2009
+;       Added output Count keyword     W.L   June 2009
+;       Adjust right ascension for cosine angle W.L. December 2009
+;       Return as soon as no matches found W.L.  December 2009
+;       Use some V6.0 notation  W.L.   February 2011
+;       Fix problem when /Spherical and Option =2 set, and sources separated
+;          by more han 180 degrees.   W.L.  March 2011
+;       
+;-
+;
+ ON_Error,2   ; Return if error (incl. non-info message)
+ compile_opt idl2
+;;;
+;   If not enough parameters, then print out the syntax.
+;
+IF N_params() lt 7 THEN BEGIN
+  print,'SRCOR calling sequence: '
+  print,'srcor,x1in,y1in,x2in,y2in,dcr,ind1,ind2 [,option={0, 1, or 2}] $'
+  print,'      [,magnitude=mag_list_1, COUNT=count, spherical={1 or 2}, /SILENT]'
+  RETURN
+ENDIF
+ count = 0
+
+;;;
+;   Keywords.
+;
+IF ~keyword_set(option) THEN option=0
+IF (option lt 0) or (option gt 2) THEN MESSAGE,'Invalid option code.'
+
+SphereFlag = keyword_set(Spherical)
+
+;;;
+;   Store the input variables into internal arrays that we can manipulate and
+; modify.
+;
+x1 = x1in
+y1 = y1in
+x2 = x2in
+y2 = y2in 
+
+;;;
+;   If the Spherical keyword is set, then convert the input values (degrees
+; and maybe hours) into radians, so GCIRC doesn't have to make this calculation
+; each time it is called in the FOR loop.  Also convert the critical radius
+; (which is in arcsec, so convert by 3600.) to radians
+;
+if SphereFlag then begin
+   dcr2 = dcr
+   XScale = Spherical EQ 1 ? 15.0 : 1.0
+   d2r  = !DPI/180.0d0
+   x1 = x1 * (XScale * d2r)
+   y1 = y1 * d2r
+   x2 = x2 * (XScale * d2r)
+   y2 = y2 * d2r
+   cosy2 = sin(y2)
+   dcr2 = dcr2 * (d2r / 3600.)
+   radcr2 = dcr2/cos(y2)        ;Adjust RA for declination
+endif else dcr2=dcr^2
+
+
+;;;
+;   Set up some other variables.
+;
+ n1 = N_elements(x1)  
+ n2 = N_elements(x2) 
+ if ~keyword_set(silent) then begin 
+      message,/info,'Option code = '+strtrim(option,2)
+      message,/info,strtrim(n1,2)+' sources in list 1'
+       message,/info,strtrim(n2,2)+' sources in list 2'
+  endif
+
+;;;
+;   The main loop.  Step through each index of list 1, look for matches in 2.
+;
+  nmch = 0L
+ ind1 = lonarr(n1)-1 & ind2 = ind1
+   
+   if SphereFlag then begin         
+      if option EQ 2 then begin      ;Closest source, no critical distance
+;For speed we find the maximum value of cos(d) where d is the arc distance
+;This avoids having to calculate the arc cosine.    Test modified Mar 2011       
+         cosy2 = cos(y2)
+          siny2 = sin(y2)
+     FOR i=0L,n1-1 DO BEGIN
+         d2  =  siny2*sin(y1[i]) + cosy2*cos(y1[i])*cos(x1[i]-x2)
+         dmch = max(d2,m)                 ;Uncommented 29-May-2009 	 
+	 ind1[nmch] = i
+         ind2[nmch] = m
+         nmch++
+      ENDFOR
+      
+      endif else begin               ;Closest source within critical distance
+        
+;For speed we first find sources within a square of the size of the critical
+;distance.    Exact distances are then computed for sources within the square.      
+     FOR i=0L,n1-1 DO BEGIN
+           xx = x1[i] & yy = y1[i]
+
+        g = where(( x2 GE (xx-radcr2)) and (x2 LE (xx+radcr2)) and $
+	(y2 GE (yy-dcr2)) and  (y2 LE (yy + dcr2)), Ng)
+
+        if Ng GT 0 then begin 
+          gcirc,0,x2[g],y2[g],xx,yy,d2
+          dmch = min(d2,mg)
+          if dmch LE dcr2 then begin 
+	      ind1[nmch] = i
+	      ind2[nmch] = g[mg]
+	      nmch++
+       endif
+       endif 
+       ENDFOR
+       endelse
+    endif else begin 
+    FOR i=0L,n1-1 DO BEGIN
+
+       d2=(x1[i]-x2)^2+(y1[i]-y2)^2
+       dmch=min(d2,m)
+         IF (option eq 2) || (dmch le dcr2) THEN BEGIN
+      ind1[nmch] = i
+      ind2[nmch] = m
+      nmch++
+   ENDIF 
+   ENDFOR  
+   endelse
+
+if ~keyword_set(silent) then message,/info,strtrim(nmch,2)+' matches found.'
+
+count = nmch
+if nmch GT 0 then begin 
+   ind1 = ind1[0:nmch-1]
+   ind2 = ind2[0:nmch-1]
+endif else begin 
+   ind1 = -1 & ind2 = -1
+   return
+endelse   
+;;;
+;   Modify the matches depending on input options.
+;
+use_mag = (n_elements(magnitude) ge 1)
+IF (option eq 0) && (~use_mag) THEN RETURN
+if ~keyword_set(silent) then begin
+IF use_mag THEN BEGIN
+   message,/info,'Cleaning up output list using magnitudes.'
+ENDIF ELSE BEGIN
+   
+   IF option eq 1 then message,/info,'Cleaning up output list (option = 1).'
+   IF option eq 2 then message,/info,'Cleaning up output list (option = 2).'
+ENDELSE
+endif
+
+FOR i=0L,max(ind2) DO BEGIN
+   csave = n_elements(ind2)
+   ww = where(ind2 eq i,count) ; All but one of the list in WW must
+                               ; eventually be removed.
+   IF count gt 1 THEN BEGIN
+      IF use_mag THEN BEGIN
+         dummy = min(magnitude[ind1[ww]],m)
+      ENDIF ELSE BEGIN
+         xx=x2[i] & yy=y2[i]
+         if SphereFlag then gcirc,0,xx,yy,x1[ind1[ww]],y1[ind1[ww]],d2 else $
+                            d2=(xx-x1[ind1[ww]])^2+(yy-y1[ind1[ww]])^2
+         IF n_elements(d2) ne count THEN MESSAGE,'Logic error 1'
+         dummy = min(d2,m)
+      ENDELSE
+      remove,m,ww              ; Delete the minimum element
+                               ; from the deletion list itself.
+
+      remove,ww,ind1,ind2      ; Now delete the deletion list from
+                               ; the original index arrays.
+      IF n_elements(ind2) ne (csave-count+1) THEN MESSAGE,'Logic error 2'
+      IF n_elements(ind1) ne (csave-count+1) THEN MESSAGE,'Logic error 3'
+      IF n_elements(ind2) ne n_elements(ind1) THEN MESSAGE,'Logic error 4'
+   ENDIF
+ENDFOR
+
+ count = N_elements(ind1)
+ if ~keyword_set(silent) then $
+  message,/info,strtrim(n_elements(ind1),2)+' final matches found'
+
+;
+RETURN
+end
diff --git a/Code/script_idl_mv/astrolib/st_diskread.pro b/Code/script_idl_mv/astrolib/st_diskread.pro
new file mode 100644
index 0000000000000000000000000000000000000000..61d11711b92895b447366e290e0d4f490629ccfa
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/st_diskread.pro
@@ -0,0 +1,781 @@
+pro st_diskread, infiles, DUMP = dump
+;+
+; NAME: 
+;       ST_DISKREAD
+;
+; PURPOSE:  
+;       Read HST FITS formatted disk files and reconstruct GEIS (STSDAS) files.
+;
+; CALLING SEQUENCE:  
+;       ST_DISKREAD, infiles
+;
+; INPUT PARAMETER:
+;       infiles - (scalar string) input disk files to be converted into GEIS
+;                       files. Wildcards are allowed.
+; FILES CREATED:
+;
+;   GEIS files:
+;         The GEIS file is reconstructed from each input Fits file. The 
+;       output filename is composed from the rootname of the observation
+;       and the appropriate GEIS file extension (i.e. d0h/d, c0h/d, etc.).
+;   Tables:
+;         If input file is a fits table, the output is an SDAS table.
+;
+; EXAMPLES:
+;       a) Reconstruct the GEIS file for disk FITS file z29i020ct*.fits.
+;               st_diskread,'z29i020ct*.fits'
+;
+; PROCEDURES CALLED:
+;       ST_DISK_DATA, ST_DISK_TABLE, ST_DISK_GEIS
+;       FTSIZE,SXPAR(),TAB_CREATE, TAB_WRITE
+; HISTORY: 
+;       10/17/94        JKF/ACC - taken from ST_TAPEREAD.
+;       11/02/94        JKF/ACC - added /block on open statement to
+;                                 handle files with 512 bytes/record.
+;       12/6/95         JKF/ACC - include new jitter files...replaces
+;                                               st_read_jitter.pro.
+;       03/5/96         W. Landsman, change FORRD to READU, remove Version 1
+;                               type codes, add message facility
+;       05/20/00        W. Landsman, remove obsolete !ERR calls, new calling
+;                               sequence to FTINFO
+;       09/2006        W. Landsman, remove obsolete keywords to OPEN
+;
+;****************************************************************************
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+ On_error,2
+
+ if n_params() lt 1 then begin
+        print,'Syntax - ST_DISKREAD, infiles'
+        return
+ endif
+ !ERROR = 0
+ if not keyword_set(DUMP) then dump = 0
+;
+; Search for names of input disk FITS files.
+;
+   file_list = file_search(infiles,count=count)
+   if count le 0 then $                                              
+        message,' No files found: '+ infiles $
+   else message,/INF, $
+        'Number of files to process: ' + strtrim(count,2)
+;
+; Loop on files
+;
+   for file = 0,count-1 do begin
+        openr,unit,file_list[file],/get_lun
+;
+; read data header and data
+;
+        st_disk_data,unit,h,data,fname,gcount,dimen,opsize,nbytes,itype
+        if !ERROR NE 0 then return
+;
+; read optional table extension
+;
+        st_disk_table,unit,htab,tab,table_available
+        if !ERROR NE 0 then return
+;
+; Finished reading the input dataset at this point. Now process the information
+; and create the output datasets.
+;
+;       GEIS file or trailer text file
+;
+
+        if sxpar(h,'naxis') gt 0 then begin
+                st_disk_geis,h,data,htab,tab,table_available, $
+                        fname,gcount,dimen,opsize,nbytes,itype  ;GEIS file
+                if !ERROR NE 0 then return
+                if dump gt 0 then $
+                        print,format='(t5,i4,t15,a)',file+1,strlowcase(fname)
+        end else begin                  ;either a text trailer or jitter table
+
+           outname = strtrim(sxpar(htab,'extname'),2)
+           if outname eq strtrim(0,2) then $
+                outname= strtrim(sxpar(h,'filename')) 
+
+           if  table_available then begin               
+
+                outname = strtrim(sxpar(htab,'extname'))
+                s=size(tab) & nl=s[2]                           
+                name=strtrim(sxpar(htab,'extname'))             ;file name
+                ;
+                ;  What type of table?
+                ;     - trailer file - ascii table
+                ;     - jitter data  - sdas table
+                ;
+                if strpos(strlowcase(name),'jit') eq -1 then begin; text trailer
+                  ;
+                  ;     Special case NAME: PODPS/IRAF uses j7 as special 
+                  ;     character, so that a file with z0j7<...> will be 
+                  ;     created as z0.<...> ( . is substituted for j7 ).
+                  ;     To avoid: Check file name for ., if found replace
+                  ;     with j7.
+                  ;
+                  invalid_char = strpos(name,'.')
+                  if invalid_char lt 5 then begin
+                        message,' Warning: Invalid filename found: '+name ,/cont
+                        name = strmid(name,0,invalid_char) + 'j7' + $
+                                 strmid(name,invalid_char+1,strlen(name)) 
+                        message,'   Filename will be changed to: '+ name,/cont
+                  end       
+                 
+                  openw,ounit,name,/get_lun
+                  for i = 0,nl-1 do printf,ounit,strtrim(string(tab[*,i]))
+                  free_lun,ounit
+                  if dump gt 0 then $
+                        print,format='(t5,i4,t15,a)',file+1,strlowcase(name)
+                end else begin                                  ; jitter table
+                  ;
+                  ; Convert from FITS to SDAS table
+                  ;
+                  ftsize,htab,tab,ncols,nrows,tfields
+                  tab_create,tcb,otab,tfields,nrows,ncols/2
+                  ftinfo,htab,ft_str
+                  fname = ft_str.ttype
+                  for j= 0, tfields-1 do begin
+                        val=ftget(ft_str,tab,j+1)     ; extract column
+                        tab_put,strtrim(fname[i]),val,tcb,otab
+                  end
+                  tab_write,outname,tcb,otab,htab
+                  if dump gt 0 then $
+                        print,format='(t5,i4,t15,a,a)',file+1, $
+                                strlowcase(outname)," jitter table "
+                end
+           end else $
+                if dump gt 0 then $
+                        print,format='(t5,i4,t15,a,a)',file+1, $
+                                strlowcase(outname)," (No data found)
+        end     
+        free_lun,unit
+   endfor
+return
+end
+;
+pro st_disk_data,unit,h,data,name,gcount,dimen,opsize,nbytes,itype
+;**************************************************************************
+;+
+; NAME:
+;       ST_DISK_DATA 
+;
+; PURPOSE:
+;       Routine to read next header and data array from an HST FITS disk file.
+;       This is a subroutine of ST_DISKREAD and not intended for stand alone 
+;       use.
+;
+;CALLING SEQUENCE:
+;       st_disk_data,unit,h,data,name,gcount,dimen,opsize,nbytes,itype
+;
+;INPUTS:
+;       unit - logical unit number.
+;
+;OUTPUTS:
+;       h - FITS header
+;       data - data array
+;       name - file name
+;       gcount - number of groups
+;       dimen - data dimensions
+;       opsize - parameter blocks size
+;       nbytes - bytes per data group
+;       itype - idl data type
+;
+; Notes:
+;       This is not a standalone program. Use ST_DISKREAD.
+;
+; PROCEDURES CALLED:
+;       GETTOK(), SXPAR()
+; HISTORY:
+;       10/17/94        JKF/ACC         - taken from ST_TAPE_DATA.
+;
+;***************************************************************************
+;-
+        On_error,2
+;
+; read fits header
+;
+        h = strarr(500)
+        nhead = 0
+        while 1 do begin
+            buf=bytarr(2880)
+            readu,unit,buf
+        
+            for i=0,35 do begin
+                st = string(buf[i*80:i*80+79])
+                h[nhead]=st
+                if strtrim(strmid(st,0,8)) eq 'END' then goto,fini
+                nhead=nhead+1
+            endfor
+        endwhile
+fini:
+;
+; get keywords from header needed to read data
+;
+        bitpix = sxpar(h,'bitpix', Count = N_bitpix)
+
+        if N_bitpix EQ 0 then begin
+            message,/CON,'ERROR - BITPIX missing from FITS header'
+            return
+        endif
+
+        naxis = sxpar(h,'naxis', Count = N_naxis)
+        if N_naxis EQ 0 then begin
+            message,/CON,'ERROR- NAXIS missing from FITS header'
+            return
+        endif
+        if naxis eq 0 then return               ;NO data to read
+;
+; get scale factors
+;
+        bscale = sxpar(h,'bscale', Count = N_bscale)
+        if N_bscale EQ 0 then bscale=1.
+        bzero = sxpar(h,'bzero', Count = N_bzero)
+        if N_bzero EQ 0 then bzero=0.
+        iraf_bp = sxpar(h,'IRAF-B/P')           ;Geis file bitpix
+        if iraf_bp ne 64 then begin
+                bscale = float(bscale)
+                bzero = float(bzero)
+            end else begin
+                bscale = double(bscale)
+                bzero = double(bzero)
+        end
+;
+; determine output bitpix
+;
+        obitpix = abs(bitpix)
+        if (bscale ne 1.0) or (bzero ne 0.0) then obitpix = 32
+        if iraf_bp eq 64 then obitpix = 64 
+;
+; get dimensions
+;
+        dimen = lonarr(naxis)
+        npoints = 1L
+        for i=0,naxis-1 do begin
+            dimen[i]=sxpar(h,'naxis'+strtrim(i+1,2))
+            if dimen[i] le 0 then begin
+                message,/CON,'ERROR- Invalid data dimension'
+                return
+            endif
+            npoints = npoints*dimen[i]
+        endfor
+;
+; determine group count
+;
+        gcount = sxpar(h,'sdasmgnu')>1
+        if gcount gt 1 then begin
+                naxis = naxis-1
+                dimen = dimen[0:naxis-1]     
+                if n_elements(dimen) eq 1 then dimen = lonarr(1)+dimen
+                npoints = npoints/gcount
+        endif
+;
+; determine orignal psize in bytes
+;
+        opsize = sxpar(h,'opsize', Count = N_opsize)
+        if N_opsize EQ 0 then opsize = 0
+        opsize = opsize/8
+;
+; set up data array
+;
+        case bitpix of
+           8: data = make_array(dimen=dimen,/byte)
+          16: data = make_array(dimen=dimen,/int)
+          32: data = make_array(dimen=dimen,/long)
+          64: data = make_array(dimen=dimen,/double)
+         -32: data = make_array(dimen=dimen,/float)
+         -64: data = make_array(dimen=dimen,/double)
+
+          else: begin
+                message,/CON,'ERROR - Invalid BITPIX value'
+                return
+                end
+        endcase
+;
+; determine file name
+;
+        ;
+        ; Keyword IRAFNAME has been changed to FILENAME in new style 
+        ;       PODPS keywords (JHB 11-2-91)
+        ;
+        name = sxpar(h,'FILENAME', Count = N_filename)
+        if N_filename EQ 0 then begin
+                name = sxpar(h,'IRAFNAME', Count = N_irafname)
+                if N_irafname EQ 0 then $
+                        message,' Keyword(IRAFNAME) missing from data header'+ $
+                        '...ABORTING '
+        endif
+
+        ;
+        ; Special case NAME: PODPS/IRAF uses j7 as special
+        ; character, so that a file with z0j7<...> will be
+        ; created as z0.<...> ( . is substituted for j7 ).
+        ; To avoid: Check file name for ., if found replace
+        ; with j7.
+        ; Special case code added by JKF/ACC 12/30/91
+        ;
+        invalid_char = strpos(name,'.')
+        if invalid_char lt 5 then begin
+            message,' Warning: Invalid filename found: '+name ,/cont
+            name = strmid(name,0,invalid_char) + 'j7' + $
+                    strmid(name,invalid_char+1,strlen(name))
+            message,'   Filename will be changed to: '+ name,/cont
+            end
+
+        name = strtrim(gettok(name,'.') +'.'+ gettok(name,'.'),2)
+        pos = strpos(name,'_cvt')               ;take out _cvt
+        if pos gt 4 then name = strmid(name,0,pos) + $
+                                strmid(name,pos+4,strlen(name)-pos-4)
+        dname = name
+        strput,dname,'d',strlen(name)-1 ;change last character to a d
+;
+; determine number of blocks in the file
+;
+        bytes_per_point = obitpix/8
+        in_bytes_per_point = abs(bitpix)/8
+        nbytes = bytes_per_point * npoints
+        nblocks = ((nbytes + opsize)*gcount + 511)/512
+;
+; open output data file
+;
+        close,1
+        openw,1,dname
+;
+; create output assoc variable
+;
+        if (bzero eq 0) and (bscale eq 1) and (bitpix gt 0) then begin
+                s = size(data) & itype = s[s[0]+1] ; idl data type
+                tmp_data = make_array( dimen=dimen, type= itype )
+
+           end else begin   
+
+                if obitpix eq 32 then begin
+                        tmp_data =  make_array(dimen=dimen,/float)
+                        itype = 4
+                   end else begin
+                        tmp_data =  make_array(dimen=dimen,/double)
+                        itype = 5
+                end
+        end 
+;
+; read data
+;
+
+        pointer = 2880          ;byte pointer in current 2880 byte disk record
+              
+        for group=0,gcount-1 do begin           ;loop on groups
+            pos = 0                             ;current pointer in data array
+            while pos lt npoints do begin
+                if pointer ge 2880 then begin
+                   readu,unit,buf
+                   case bitpix of
+                        16: byteorder,buf,/NtoHS
+                        32: byteorder,buf,/NtoHL
+                        -32: byteorder,buf,/XDRTOF
+                        -64: byteorder,buf,/XDRTOD
+                        ELSE:
+                   endcase
+                   pointer = 0
+                endif
+                words_needed = (npoints-pos)
+                bytes_needed = words_needed*in_bytes_per_point
+                bytes_to_take = (2880-pointer) < bytes_needed
+                words_to_take = bytes_to_take/in_bytes_per_point
+
+                case bitpix of
+                        8: data[pos]=buf[pointer:bytes_to_take-1]
+                        16: data[pos]=fix(buf,pointer,words_to_take)
+                        32: data[pos]=long(buf,pointer,words_to_take)
+                        64: data[pos]=double(buf,pointer,words_to_take)
+                       -32: data[pos]=float(buf,pointer,words_to_take)   ;IEEE
+                       -64: data[pos]=double(buf,pointer,words_to_take)  ;IEEE
+                endcase
+                pos = pos + words_to_take
+                pointer = pointer + bytes_to_take
+            endwhile
+;
+; write data
+;
+            if (bscale ne 1.0) or (bzero ne 0.0) then begin
+        
+                    out_rec = assoc(1,tmp_data,(nbytes+opsize)*group)
+                    out_rec[0] = data * bscale + bzero  
+                end else begin
+                    out_rec = assoc(1,tmp_data,(nbytes+opsize)*group)
+                    out_rec[0] = data
+            end
+        endfor
+return               
+end
+;
+pro st_disk_table,unit,h,data,table_available
+;+
+;NAME:
+;       ST_DISK_TABLE 
+;
+; PURPOSE:
+;       Routine to read FITS table from an ST fits on disk.
+;       This is a subroutine of st_diskread and not intended for stand alone 
+;       use.
+;
+; CALLING SEQUENCE:
+;       st_disk_table,unit,h,data
+;
+; INPUTS PARAMETER:
+;       unit - disk unit number
+;
+;
+; OUTPUTS:
+;       h - FITS header
+;       data - table array
+;
+; NOTES:
+;       This is not a standalone program. Use ST_DISKREAD.
+;          
+; HISTORY:
+;       10/17/94        JKF/ACC - taken from ST_TAPE_TABLE.
+;       12/7/95         JKF/ACC - handle tables for jitter data.
+;                                            
+;****************************************************************************
+;-
+;
+; read fits header
+;
+   h = strarr(500)
+   nhead = 0
+   while 1 do begin
+
+        buf  = bytarr(2880)
+           
+on_ioerror, no_table_found
+        readu,unit,buf
+        
+        for i=0,35 do begin
+                st = string(buf[i*80:i*80+79])
+                h[nhead]=st
+                if strtrim(strmid(st,0,8)) eq 'END' then goto,fini
+                nhead=nhead+1
+        endfor
+   endwhile
+fini:
+
+;
+; get keywords from header needed to read data
+;
+   bitpix = sxpar(h,'bitpix', Count = N_bitpix)
+   if N_bitpix EQ 0 then begin
+        message,/CON,'ERROR- BITPIX missing from FITS header'
+        return
+   endif
+   if bitpix ne 8 then begin
+        message,/CON,'Invalid BITPIX for FITS table'
+        return
+    endif
+    naxis = sxpar(h,'naxis', Count = N_naxis)
+    if N_naxis EQ 0 then begin
+            message,/CON,'ERROR- NAXIS missing from FITS table header'
+            return
+    endif
+    if naxis ne 2 then begin
+        message,/CON,'Invalid NAXIS for FITS table '
+        return
+    endif
+
+    dimen = lonarr(2)
+    npoints = 1L
+    for i=0,1 do begin
+            dimen[i]=sxpar(h,'naxis'+strtrim(i+1,2))
+            if dimen[i] le 0 then begin
+                if dump gt 1 then message,/cont,"No data found in table"
+                goto, no_table_found
+            endif
+            npoints = npoints*dimen[i]
+    endfor
+    data = make_array(dimen=dimen,/byte)
+;
+; read data array
+;
+    nrecs = (npoints + 2879)/2880
+    nleft = npoints      
+
+    for i=0L,nrecs-1 do begin
+                readu,unit,buf
+                case bitpix of
+                        16: byteorder,buf,/NtoHS
+                        32: byteorder,buf,/NtoHL
+                        -32: byteorder,buf,/XDRTOF
+                        -64: byteorder,buf,/XDRTOD
+                        ELSE:
+                endcase
+
+                if nleft lt 2880 then max_nleft = nleft-1 $
+                         else max_nleft= 2880L-1
+                data[i*2880L] = buf[0 : max_nleft ]
+                nleft   = (npoints-1) - ((i+1)*2880L)
+    endfor
+
+table_available=1
+return
+
+no_table_found:
+table_available=0
+
+return
+end
+
+pro st_disk_geis,h,data,htab,tab,table_available,name,gcount,dimen,opsize, $
+                nbytes_g,itype
+;+
+; NAME:
+;       ST_DISK_GEIS 
+;
+; PURPOSE:
+;        Routine to construct GEIS files from ST FITS disk files.
+;
+; CALLING SEQUENCE:
+;       ST_DISK_GEIS, h, data, htab, tab, table_available, name, gcount, 
+;               dimen,opsize, nbytes_g,itype
+;
+; INPUT PARAMETERS:
+;       h - header for data
+;       data - data array
+;       htab - header for the table
+;       tab - fits table
+;       table_available - logical variable (1 if table was found)
+;       name - data set name
+;       gcount - number of groups
+;       dimen - data dimensions
+;       opsize - original parameter block size
+;       nbytes_g - number of bytes per group
+;       itype - idl integer data type value for the output data groups
+;
+; SIDE EFFECTS:
+;
+;       GEIS file updated with group parameters in unit 1 (already open)
+;       and header file created
+;
+; NOTES:
+;       This is not a standalone program. Use st_diskread.
+;
+;       During the creation of the header, this routine performs the 
+;       following steps:
+;       1) create a basic fits header (7 keywords)
+;       2) adjust basic fits header for the number of axis present (i.e. >1)
+;       3) adjust basic fits header for parameter keywords (i.e. ptype,etc)
+;       4) from this point, sequentially copies keywords until it hits one of
+;               the following keywords 'INSTRUME','INSTRUID', or 'CONFG'.
+;       5) append 'END' statement
+;
+; PROCEDURES CALLED:
+;       FTSIZE, SXADDPAR, SXHWRITE
+; HISTORY:
+;       10/17/94        JKF/ACC         - taken from ST_DISK_GEIS
+;
+;****************************************************************************
+;-
+;
+; convert table to parameter block 
+;
+        hpar = strarr(200)              ;parameter header
+        hpar[0]='END'
+        sxaddpar,hpar,'PCOUNT',0
+        sxaddpar,hpar,'PSIZE',opsize*8
+        npar = 0
+        if table_available then begin
+                ftsize,htab,tab,ncols,ngroups,npar
+                if ngroups ne gcount then begin
+                    print,'ST_DISK_GEIS - number of rows in table does '+ $
+                        'not match GCOUNT'
+                    retall
+                endif
+                sxaddpar,hpar,'PCOUNT',npar
+;
+; get parameter descriptions
+;
+
+                ptype = sxpar(htab,'ttype*')    ;parameter name
+                tform = sxpar(htab,'tform*')    ;formats in table
+                tbcol = sxpar(htab,'tbcol*')-1  ;starting byte in table
+                twidth = intarr(npar)           ;width of table columns
+                pdtype = strarr(16,npar)        ;data type
+                nbytes = intarr(npar)           ;size in bytes of the par.
+                sbyte = intarr(npar)            ;starting byte in par. block
+                idltypes = intarr(npar)         ;idl data type
+                for i=0,npar-1 do begin
+                    type=strmid(tform[i],0,1)
+                    case strupcase(type) of
+                                'A' : idltype = 1
+                                'I' : idltype = 16
+                                'E' : idltype = 8
+                                'F' : idltype = 8
+                                'D' : idltype = 32
+                    endcase
+                    idltypes[i]=idltype
+;
+; get field width in characters
+;
+                    twidth[i]=fix(strtrim(gettok( $
+                                strmid(tform[i],1,strlen(tform[i])-1),'.'),2))
+
+                    case idltype of
+                        1: begin                        ;string
+                                if ((twidth[i] mod 4) gt 0) then $
+                                        twidth[i]= (fix(twidth[i]/4)*4 + 4) 
+                                nbytes[i] = twidth[i]
+                                pdtype[i] = 'CHARACTER*'+strtrim(twidth[i],2)
+                           end
+                        8: begin
+                                nbytes[i] = 4
+                                pdtype[i] = 'REAL*4'
+                           end
+                        16: begin
+                                nbytes[i] = 4          
+                                pdtype[i] = 'INTEGER*4'
+                            end
+                        32: begin
+                                nbytes[i] = 8
+                                pdtype[i] = 'REAL*8'
+                            end
+                    endcase
+
+                    if i gt 0 then sbyte[i] = nbytes[i-1]+sbyte[i-1]
+
+                endfor
+;
+; complete parameter block portion of the header
+;
+                if total(nbytes) ne opsize then begin
+                    print,'ST_DISK_GEIS - mismatch of computed and ' + $
+                          'original group par. block sizes'
+                    retall
+                endif
+                blank = string(replicate(32b,80))
+                strput,blank,'=',8
+                nhpar = 2
+                for i=0,npar-1 do begin
+                        st=strtrim(i+1,2)
+
+                        line=blank                      ;PTYPEn
+                        strput,line,'PTYPE'+st
+                        strput,line,"'"+ptype[i]+"'",10
+;
+;       Add comments to group parameters (PTYPEn field)...JKF/ACC 1/22/92
+;               
+                        strput,line,'/',31
+                        strput,line, strtrim(sxpar(htab,ptype[i]),2), 33
+                        hpar[nhpar]=line
+
+                        line=blank                      ;PDTYPEn
+                        strput,line,'PDTYPE'+st
+                        strput,line,"'"+pdtype[i]+"'",10
+                        strput,line,'/',31
+                        hpar[nhpar+1]=line
+
+                        line=blank                      ;PSIZEn
+                        strput,line,'PSIZE'+st
+                        strput,line,string(nbytes[i]*8,'(I5)'),25
+                        strput,line,'/',31
+                        hpar[nhpar+2]=line
+                        nhpar=nhpar+3
+                endfor
+                hpar[nhpar]='END'
+;
+; read table columns and insert into 2-d parameter block
+;
+                pblock=bytarr(total(nbytes),ngroups)
+                for i=0,npar-1 do begin
+                        width = twidth[i]
+                        width1 = width-1
+                        column = tab[tbcol[i]:tbcol[i]+width1,*]
+                        if idltypes[i] ne 1 then begin
+                                case idltypes[i] of
+                                        8: val = fltarr(ngroups)
+                                        16: val = lonarr(ngroups)
+                                        32: val = dblarr(ngroups)
+                                endcase
+                                for j=0L,ngroups-1 do begin
+                                    start = width*j
+                                    ;
+                                    ; If the field is blank, force atleast
+                                    ;  a character 0. (DJL 10/92)
+                                    ;
+                                    tmp = string(column[start:start+width1])
+                                    if strtrim(tmp) eq '' then tmp ='0'
+                                    val[j]=tmp
+                                endfor
+                                column = byte(val,0,nbytes[i],ngroups)
+                        endif
+                        pblock[sbyte[i],0]=column
+                endfor
+        endif
+;
+; Create output header        ---------------------------------------------
+;
+; determine type and size of data
+;
+        case itype of
+                1:  begin & datatype='BYTE'      & bitpix=8  & end
+                2:  begin & datatype='INTEGER*2' & bitpix=16 & end
+                3: begin & datatype='INTEGER*4' & bitpix=32 & end
+                4:  begin & datatype='REAL*4'    & bitpix=32 & end
+                5: begin & datatype='REAL*8'    & bitpix=64 & end
+        endcase
+;
+; create output header for GEIS file
+;
+
+        hout = strarr(500) & hout[0]='END'      ;standard keywords
+        sxaddpar,hout,'SIMPLE','F'              ;not standard fits
+        sxaddpar,hout,'BITPIX',bitpix
+        sxaddpar,hout,'DATATYPE',datatype
+        sxaddpar,hout,'NAXIS',n_elements(dimen)
+        ndim = n_elements(dimen)
+        for i=1,ndim do sxaddpar,hout,'NAXIS'+strtrim(i,2),dimen[i-1]
+        sxaddpar,hout,'GROUPS','T'              ;group format data
+        sxaddpar,hout,'GCOUNT',gcount
+;
+; combine information from hpar, hs and h headers to form output header
+;
+        nout = 7
+        while strtrim(strmid(hout[nout],0,8)) ne 'END' do nout=nout+1
+;
+; add parameter block information
+;
+        pos = 0
+        while strtrim(strmid(hpar[pos],0,8)) ne 'END' do begin
+                hout[nout]=hpar[pos]
+                nout=nout+1
+                pos=pos+1
+        endwhile
+;
+; skip junk at first part of h header
+;
+        pos = 0
+        while (strmid(h[pos],0,8) ne 'INSTRUME') and $
+              (strmid(h[pos],0,8) ne 'INSTRUID') and $
+              (strtrim(strmid(h[pos],0,8),2) ne 'CONFIG') do begin
+            pos = pos + 1
+            if strtrim(strmid(h[pos],0,8)) eq 'END' then begin
+                print,'ST_DISK_GEIS- INSTRUME keyword missing from header'
+                retall
+            endif
+        endwhile
+;
+; copy rest of header to hout
+;
+        while strtrim(strmid(h[pos],0,8)) ne 'END' do begin
+                hout[nout] = h[pos]
+                nout=nout+1
+                pos=pos+1
+        endwhile
+        hout[nout]='END'
+;
+; Create output GEIS file --------------------------------------------------
+;
+        sxhwrite,name,hout                      ;output header file
+        if npar gt 0 then begin
+                out_rec = assoc(1,bytarr(1))    ;put in group parameters
+                for i=0,gcount-1 do $
+                        out_rec[i*(nbytes_g+opsize)+nbytes_g] = pblock[*,i]
+        end
+close,1
+return
+end
diff --git a/Code/script_idl_mv/astrolib/starast.pro b/Code/script_idl_mv/astrolib/starast.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a120d24b2825dbc41e0c63afd1a3d237fd2a5386
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/starast.pro
@@ -0,0 +1,140 @@
+pro starast,ra,dec,x,y,cd, righthanded=right,hdr=hdr, projection=projection    
+;+
+; NAME:
+;       STARAST 
+; PURPOSE:
+;       Compute astrometric solution using positions of 2 or 3 reference stars
+; EXPLANATION:
+;       Computes an exact astrometric solution using the positions and 
+;       coordinates from 2 or 3 reference stars and assuming a tangent 
+;       (gnomonic) projection.   If 2 stars are used, then
+;       the X and Y plate scales are assumed to be identical, and the
+;       axis are assumed to be orthogonal.   Use of three stars will
+;       allow a unique determination of each element of the CD matrix.
+;
+; CALLING SEQUENCE:
+;       starast, ra, dec, x, y, cd, [/Righthanded, HDR = h, PROJECTION=]
+;
+; INPUTS:
+;       RA - 2 or 3 element vector containing the Right Ascension in DEGREES
+;       DEC- 2 or 3 element vector containing the Declination in DEGREES
+;       X -  2 or 3 element vector giving the X position of reference stars
+;       Y -  2 or 3 element vector giving the Y position of reference stars
+; OUTPUTS:
+;       CD - CD (Coordinate Description) matrix (DEGREES/PIXEL) determined 
+;               from stellar positions and coordinates.
+; OPTIONAL INPUT KEYWORD:
+;       /RightHanded - If only 2 stars are supplied, then there is an ambiguity
+;               in the orientation of the coordinate system.   By default,
+;               STARAST assumes the astronomical standard left-handed system
+;               (R.A. increase to the left).   If /Right is set then a 
+;               righthanded coordinate is assumed.  This keyword has no effect
+;               if 3 star positions are supplied.
+;        PROJECTION - Either a 3 letter scalar string giving the projection
+;               type (e.g. 'TAN' or 'SIN') or an integer 1 - 25 specifying the
+;               projection as given in the WCSSPH2XY procedure.   If not 
+;               specified then a tangent projection is computed.
+; OPTIONAL INPUT-OUTPUT KEYWORD:
+;        HDR - If a FITS header string array is supplied, then an astrometry 
+;              solution is added to the header using the CD matrix and star 0
+;              as the reference pixel (see example).   Equinox 2000 is assumed.
+; EXAMPLE:
+;        To use STARAST to add astrometry to a FITS header H;
+;
+;        IDL> starast,ra,dec,x,y,cd       ;Determine CD matrix
+;        IDL> crval = [ra[0],dec[0]]      ;Use Star 0 as reference star
+;        IDL> crpix = [x[0],y[0]] +1      ;FITS is offset 1 pixel from IDL
+;        IDL> putast,H,cd,crpix,crval     ;Add parameters to header
+;
+;        This is equivalent to the following command:
+;        IDL> STARAST,ra,dec,x,y,hdr=h      
+;  
+; METHOD:
+;       The CD parameters are determined by solving the linear set of equations
+;       relating position to local coordinates (l,m)
+;
+;       For highest accuracy the first star position should be the one closest
+;       to the reference pixel.
+; REVISION HISTORY:
+;       Written, W. Landsman             January 1988
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added /RightHanded and HDR keywords   W. Landsman   September 2000
+;       Write CTYPE values into header   W. Landsman/A. Surkov  December 2002
+;       CD matrix was mistakenly transpose in 3 star solution
+;       Added projection keyword    W. Landsman   September 2003
+;       Test for singular matrix W. Landsman  August 2011 
+;-
+ On_ERROR,2
+ compile_opt idl2
+
+ if N_params() LT 4 then begin
+        print,'Syntax - STARAST, ra, dec, x, y, cd, [/Right, HDR =h,Projection=]'
+        return                         
+ endif
+
+ cdr = !DPI/180.0D 
+ map_types=['DEF','AZP','TAN','SIN','STG','ARC','ZPN','ZEA','AIR','CYP',$
+            'CAR','MER','CEA','COP','COD','COE','COO','BON','PCO','SFL',$
+            'PAR','AIT','MOL','CSC','QSC','TSC']
+
+ iterate = (N_elements(crpix) EQ 2) && (N_elements(crval) EQ 0)
+ if N_elements(projection) EQ 0 then projection = 2    ;Default is tangent proj.
+ if size(projection,/TNAME) EQ 'STRING' then begin
+      map_type  =where(map_types EQ strupcase(strtrim(projection,2)), Ng)
+      if Ng EQ 0 then message, $
+         'ERROR - supplied projection of ' + projection[0] + ' not recognized'
+      map_type = map_type[0]
+ endif else map_type = projection
+
+ nstar = min( [N_elements(ra), N_elements(dec), N_elements(x), N_elements(y)])
+ if (nstar NE 2) && (nstar NE 3) then $
+        message,'ERROR -  Either 2 or 3 star positions required'
+ crval1  = [ ra[0], dec[0] ]
+ crpix1  = [ x[0], y[0] ]
+
+; Convert RA, Dec to Eta, Xi
+
+ wcssph2xy, crval = crval1, ra[1:*], dec[1:*], eta, xi, map_type, $
+         latpole = 0.0
+ delx1 = x[1] - crpix1[0] 
+ dely1 = y[1] - crpix1[1]     
+
+if nstar EQ 3 then begin
+
+        delx2 = x[2] - crpix1[0] & dely2 = y[2] - crpix1[1]
+        b = double([eta[0],xi[0],eta[1],xi[1]])
+        a = double( [ [delx1, 0, delx2,    0    ], $
+                      [dely1, 0,  dely2,    0  ], $
+                      [0. , delx1, 0,    delx2    ], $
+                      [0    , dely1   , 0. ,dely2] ] )
+endif else begin
+
+        b = double( [eta[0],xi[0]] )
+        if keyword_set(right) then  $
+              a = double( [ [delx1,dely1], [-dely1,delx1] ] ) else $
+              a = double( [ [delx1,-dely1], [dely1,delx1] ] )
+
+endelse
+
+ cd = invert(a,status)#b        ;Solve linear equations
+ if status EQ 1 then $
+    message,'ERROR - Singular matrix (collinear points)' 
+ if nstar EQ 2 then begin
+           if keyword_set(right) then $ 
+               cd = [ [cd[0],cd[1]],[-cd[1],cd[0]] ] else $
+               cd = [ [cd[0],cd[1]],[cd[1],-cd[0]] ] 
+ endif else $ 
+       cd = transpose(reform(cd,2,2))
+
+
+;Add parameters to header
+ if N_elements(hdr) GT 0 then begin
+        proj = map_types[map_type]
+        make_astr, astr,CD = cd, crval = crval1, crpix = crpix1+1, $
+                   ctype = ['RA---','DEC--'] + proj
+        putast, hdr, astr, equi=2000.0,cd_type=2
+       
+ endif
+     
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/store_array.pro b/Code/script_idl_mv/astrolib/store_array.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8e4f9888d75ca039d541046b0fec1005ad68b64e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/store_array.pro
@@ -0,0 +1,149 @@
+	PRO STORE_ARRAY, DESTINATION, INSERT, INDEX
+;+
+; NAME:
+;	STORE_ARRAY
+; PURPOSE:
+;	Insert array INSERT into the array DESTINATION
+; EXPLANATION:
+;	The dimensions of the DESTINATION array are adjusted to accommodate
+;	the inserted array.
+; CATEGOBY:
+;	Utility
+; CALLING SEQUENCE:
+;	STORE_ARRAY, DESTINATION, INSERT, INDEX
+; INPUT:
+;	DESTINATION	= Array to be expanded.
+;	INSERT		= Array to insert into DESTINATION.
+;	INDEX		= Index of the final dimension of DESTINATION to insert
+;			  INSERT into.
+; OUTPUTS:
+;	DESTINATION	= Expanded output array.  If both input arrays have the
+;			  same number of dimensions, then the DESTINATION will
+;			  be replaced with INSERT.
+; RESTRICTIONS:
+;	DESTINATION and INSERT have to be either both of type string or both of
+;	numerical types.
+;
+;	INSERT must not have more dimensions than DESTINATION.
+;
+; MODIFICATION HISTOBY:
+;	William Thompson, Feb. 1992, from BOOST_ARRAY by D. Zarro and P. Hick.
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+	ON_ERROR, 2			;On error, return to caller
+;
+;  Check the number of parameters.
+;
+	IF N_PARAMS() NE 3 THEN MESSAGE,	$
+		'Syntax:  STORE_ARRAY, DESTINATION, INSERT, INDEX'
+;
+;  Make sure everything is defined.
+;
+	IF N_ELEMENTS(INSERT) EQ 0 THEN MESSAGE,'INSERT not defined'
+	IF N_ELEMENTS(INDEX) EQ 0 THEN MESSAGE,'INDEX not defined'
+;
+;  If DESTINATION is not defined, then set it equal to INSERT.
+;
+	IF N_ELEMENTS(DESTINATION) EQ 0 THEN BEGIN
+		DESTINATION = INSERT
+		RETURN
+	ENDIF
+;
+;  Get the array types and dimensions of DESTINATION and INSERT.
+;
+	SD = SIZE(DESTINATION)
+	SA = SIZE(INSERT)
+	D_NDIM = SD[0]
+	A_NDIM = SA[0]
+	IF D_NDIM EQ 0 THEN D_DIM = 1 ELSE D_DIM = SD[1:D_NDIM]
+	IF A_NDIM EQ 0 THEN A_DIM = 1 ELSE A_DIM = SA[1:A_NDIM]
+	D_TYPE = SD[N_ELEMENTS(SD)-2]
+	A_TYPE = SA[N_ELEMENTS(SA)-2]
+;
+;  Treat scalars as one-dimensional arrays.
+;
+	D_NDIM = D_NDIM > 1
+	A_NDIM = A_NDIM > 1
+; 
+;  Check to see if both arrays are of type string or numeric.
+;
+	IF D_TYPE EQ 7 THEN D_STRING = 1  ELSE D_STRING = 0
+	IF A_TYPE EQ 7 THEN A_STRING = 1  ELSE A_STRING = 0
+	IF D_STRING NE A_STRING THEN MESSAGE,	$
+		'Data arrays should be either both string or both non-string'
+;
+;  If both arrays have the same number of elements, then replace DESTINATION
+;  with INSERT.
+;
+	IF D_NDIM EQ A_NDIM THEN BEGIN
+		DESTINATION = INSERT
+		RETURN
+;
+;  Otherwise, make sure that INSERT has fewer dimensions than DESTINATION.
+;
+	END ELSE IF D_NDIM LT A_NDIM THEN MESSAGE,	$
+		'INSERT has more dimensions than DESTINATION'
+;
+;  Check INDEX
+;
+	LAST = D_DIM[D_NDIM-1] - 1
+	IF (INDEX LT 0) OR (INDEX GT LAST) THEN MESSAGE,	$
+		'INDEX must be between 0 and ' + STRTRIM(LAST,2)
+;
+;  Merge the dimensions of DESTINATION and INSERT.
+;
+	R_DIM = D_DIM
+	FOR I = 0,A_NDIM-1 DO R_DIM[I] = D_DIM[I] > A_DIM[I]
+;
+;  Create the output array with the correct number of elements, and the greater
+;  of the types of DESTINATION and INSERT.
+;
+	OUTPUT = MAKE_ARRAY(DIMENSION=R_DIM, TYPE=(D_TYPE > A_TYPE))
+	R_NDIM = N_ELEMENTS(R_DIM)
+;
+;  If INDEX is not zero, then store the first part of DESTINATION in the output
+;  array.
+;
+	IF INDEX NE 0 THEN BEGIN
+	    K = INDEX - 1
+	    CASE R_NDIM OF
+	    	2:  OUTPUT[0,0] = DESTINATION[*,0:K]
+	    	3:  OUTPUT[0,0,0] = DESTINATION[*,*,0:K]
+	    	4:  OUTPUT[0,0,0,0] = DESTINATION[*,*,*,0:K]
+	    	5:  OUTPUT[0,0,0,0,0] = DESTINATION[*,*,*,*,0:K]
+	    	6:  OUTPUT[0,0,0,0,0,0] = DESTINATION[*,*,*,*,*,0:K]
+	    	7:  OUTPUT[0,0,0,0,0,0,0] = DESTINATION[*,*,*,*,*,*,0:K]
+	    ENDCASE
+	ENDIF
+;
+;  Add INSERT.
+;
+	CASE R_NDIM OF
+		2:  OUTPUT[0,INDEX] = INSERT
+		3:  OUTPUT[0,0,INDEX] = INSERT
+		4:  OUTPUT[0,0,0,INDEX] = INSERT
+		5:  OUTPUT[0,0,0,0,INDEX] = INSERT
+		6:  OUTPUT[0,0,0,0,0,INDEX] = INSERT
+		7:  OUTPUT[0,0,0,0,0,0,INDEX] = INSERT
+	ENDCASE
+;
+;  Store the remainder of DESTINATION, if any, in the output array.
+;
+	IF INDEX NE LAST THEN BEGIN
+	    K = INDEX + 1
+	    CASE R_NDIM OF
+	    	2:  OUTPUT[0,K] = DESTINATION[*,K:*]
+	    	3:  OUTPUT[0,0,K] = DESTINATION[*,*,K:*]
+	    	4:  OUTPUT[0,0,0,K] = DESTINATION[*,*,*,K:*]
+	    	5:  OUTPUT[0,0,0,0,K] = DESTINATION[*,*,*,*,K:*]
+	    	6:  OUTPUT[0,0,0,0,0,K] = DESTINATION[*,*,*,*,*,K:*]
+	    	7:  OUTPUT[0,0,0,0,0,0,K] = DESTINATION[*,*,*,*,*,*,K:*]
+	    ENDCASE
+	ENDIF
+;
+;  Replace DESTINATION with OUTPUT, and return.
+;
+	DESTINATION = OUTPUT
+	RETURN
+	END
diff --git a/Code/script_idl_mv/astrolib/str_index.pro b/Code/script_idl_mv/astrolib/str_index.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d3c9b132f28c903d99631e9b59eef533f25d5066
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/str_index.pro
@@ -0,0 +1,68 @@
+FUNCTION STR_INDEX, str, substr, offset
+;+
+; NAME:
+;       STR_INDEX()
+;
+; PURPOSE:
+;       Get indices of a substring (SUBSTR) in string.
+;
+; EXPLANATION:
+;       The IDL intrinsic function STRPOS returns only the index of the first
+;       occurrence of a substring. This routine calls itself recursively to get
+;       indices of the remaining occurrences.
+;
+; CALLING SEQUENCE:
+;       result= STR_INDEX(str, substr [, offset])
+;
+; INPUTS:
+;       STR    -- The string in which the substring is searched for
+;       SUBSTR -- The substring to be searched for within STR
+;
+; OPTIONAL INPUTS:
+;       OFFSET -- The character position at which the search is begun. If
+;                 omitted or being negative, the search begins at the first
+;                 character (character position 0).
+;
+; OUTPUTS:
+;       RESULT -- Integer scalar or vector containing the indices of SUBSTR
+;                 within STR. If no substring is found, it is -1.
+;
+; CALLS:
+;       DELVARX
+;
+; COMMON BLOCKS:
+;       STR_INDEX -- internal common block. The variable save in the block is
+;                    deleted upon final exit of this routine.
+;
+; CATEGORY:
+;       Utility, string
+;
+; MODIFICATION HISTORY:
+;       Written January 3, 1995, Liyun Wang, GSFC/ARC
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use size(/TNAME) instead of DATATYPE()   W. Landsman   October 2001
+;       
+;-
+;
+   ON_ERROR, 2
+   COMMON str_index, idx
+
+   IF N_PARAMS() LT 2 THEN MESSAGE,'Syntax: str_index, str, substr [,offset]'
+
+   IF size(str,/TNAME) NE 'STRING' OR size(substr,/TNAME) NE 'STRING' THEN $
+      MESSAGE, 'The first two input parameters must be of string type.'
+
+   IF N_ELEMENTS(offset) EQ 0 THEN pos = 0 ELSE pos = offset
+   aa = STRPOS(str,substr,pos)
+   IF aa NE -1 THEN BEGIN
+      IF N_ELEMENTS(idx) EQ 0 THEN idx = aa ELSE idx = [idx,aa]
+      bb = str_index(str,substr,aa+1)
+      RETURN, bb
+   ENDIF ELSE BEGIN
+      IF N_ELEMENTS(idx) NE 0 THEN BEGIN
+         result = idx
+         delvarx, idx
+      ENDIF ELSE result = -1
+      RETURN, result
+   ENDELSE
+END
diff --git a/Code/script_idl_mv/astrolib/strcompress2.pro b/Code/script_idl_mv/astrolib/strcompress2.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3c34055d8d432b48a32ce6f391873fc444bcced6
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/strcompress2.pro
@@ -0,0 +1,51 @@
+function strcompress2, str, chars
+;+
+; NAME:
+;	STRCOMPRESS2
+; PURPOSE:
+;	Remove blanks around specified characters in a string
+; CALLING SEQUENCE
+;	newstring = strcompress2( st, chars)
+; INPUTS:
+;       st - any scalar string
+;      chars - scalar  or vector string specifing which characters around which 
+;             blanks should be removed.    For example, if chars=['=','-','+'] 
+;              then spaces around the three characters "=', '-', and '+' will 
+;             be removed.
+; OUTPUTS:
+;       newstring - input string with spaces removed around the specified 
+;        characters.   
+; EXAMPLE:
+;       The Vizier constraint string (see queryvizier.pro) does not allow 
+;       blanks around the operators '=','<', or '>'.     But we do not want
+;       to remove blanks around names (e.g. 'NGC 5342'):
+; 
+;       IDL> st = 'name = NGC 5342, v< 23'
+;       IDL> print,strcompress2(st, ['=','<','>'])
+;            name=NGC 5342, v<23
+; MODIFICATION HISTORY:
+;	Written by W.Landsman                   July 2008
+;-
+
+ On_error,2
+ compile_opt idl2
+ st = strcompress(str)    ;Ok to compress to a single space
+ if N_elements(chars) GT 1 then op = '(' + strjoin(chars,'|') + ')'   $
+                           else op = chars
+ 
+ op1 = ' ' + op      ;first look for Leading space
+ n = stregex(st, op1)     
+ while n GT 0 do begin
+    st = strmid(st,0,n) + strmid(st,n+1)   ;piece string together
+    n = stregex(st,op1)      ; Look for another occurrence since stregex just 
+ endwhile                    ; gives the first
+
+ op2 = op + ' '    ;Now look for Following space
+ n = stregex(st, op2)
+ while n GT 0 do begin
+    st = strmid(st,0,n+1) + strmid(st,n+2)
+    n = stregex(st,op2)
+ endwhile 
+
+   return,st
+ end
diff --git a/Code/script_idl_mv/astrolib/strn.pro b/Code/script_idl_mv/astrolib/strn.pro
new file mode 100644
index 0000000000000000000000000000000000000000..45b92bcf62a9a6bb1accb3de8971bafc6348f6c4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/strn.pro
@@ -0,0 +1,100 @@
+function strn, number, LENGTH = length, PADTYPE = padtype, PADCHAR = padchar, $
+                       FORMAT = Format
+;+
+; NAME:
+;	STRN
+; PURPOSE:
+;	Convert a number to a string and remove padded blanks.
+; EXPLANATION:
+;	The main and original purpose of this procedure is to convert a number
+;	to an unpadded string (i.e. with no blanks around it.)  However, it 
+;	has been expanded to be a multi-purpose formatting tool.  You may 
+;	specify a length for the output string; the returned string is either 
+;	set to that length or padded to be that length.  You may specify 
+;	characters to be used in padding and which side to be padded.  Finally,
+;	you may also specify a format for the number.  NOTE that the input 
+;	"number" need not be a number; it may be a string, or anything.  It is
+;	converted to string.
+;
+; CALLING SEQEUNCE:
+;	tmp = STRN( number, [ LENGTH=, PADTYPE=, PADCHAR=, FORMAT = ] )
+;
+; INPUT:
+;	NUMBER    This is the input variable to be operated on.  Traditionally,
+;		 it was a number, but it may be any scalar type.
+;
+; OPTIONAL INPUT:
+;	LENGTH    This KEYWORD specifies the length of the returned string.  
+;		If the output would have been longer, it is truncated.  If 
+;		the output would have been shorter, it is padded to the right 
+;		length.
+;	PADTYPE   This KEYWORD specifies the type of padding to be used, if any.
+;		0=Padded at End, 1=Padded at front, 2=Centered (pad front/end)
+;		IF not specified, PADTYPE=1
+;	PADCHAR   This KEYWORD specifies the character to be used when padding.
+;		The default is a space (' ').
+;	FORMAT    This keyword allows the FORTRAN type formatting of the input
+;		number (e.g. '(f6.2)')
+;
+; OUTPUT:
+;	tmp       The formatted string
+;
+; USEFUL EXAMPLES:
+;	print,'Used ',strn(stars),' stars.'  ==> 'Used 22 stars.'
+;	print,'Attempted ',strn(ret,leng=6,padt=1,padch='0'),' retries.'
+;		==> 'Attempted 000043 retries.'
+;	print,strn('M81 Star List',length=80,padtype=2)
+;		==> an 80 character line with 'M81 Star List' centered.
+;	print,'Error: ',strn(err,format='(f15.2)')
+;		==> 'Error: 3.24'     or ==> 'Error: 323535.22'
+;
+; HISTORY:
+;	03-JUL-90 Version 1 written by Eric W. Deutsch
+;	10-JUL-90 Trimming and padding options added         (E. Deutsch)
+;	29-JUL-91 Changed to keywords and header spiffed up     (E. Deutsch)
+;	Ma7 92 Work correctly for byte values (W. Landsman)
+;	19-NOV-92 Added Patch to work around IDL 2.4.0 bug which caused an
+;	error when STRN('(123)') was encountered.            (E. Deutsch)
+;;       Handles array input, M. Sullivan March 2014
+;       Use V6.0 notation W. Landsman April 2014
+;       Fix problem with vector strings of different length WL Aug 2014
+;-
+ On_error,2
+  if ( N_params() LT 1 ) then begin
+    print,'Call: IDL> tmp=STRN(number,[length=,padtype=,padchar=,format=])'
+    print,"e.g.: IDL> print,'Executed ',strn(ret,leng=6,padt=1,padch='0'),' retries.'"
+    return,''
+    endif
+  if (N_elements(padtype) eq 0) then padtype=1
+  if (N_elements(padchar) eq 0) then padchar=' '
+  if (N_elements(Format) eq 0) then Format=''
+
+  padc = byte(padchar)
+  pad = string(replicate(padc[0],200))
+
+  tmp=STRARR(N_ELEMENTS(number))
+  FOR i=0L,N_ELEMENTS(number)-1 DO BEGIN
+     ss=size(number[i]) & PRN=1 & if (ss[1] eq 7) then PRN=0
+     if ( Format EQ '') then tmp[i] = strtrim( string(number[i], PRINT=PRN),2) $
+     else tmp[i] = strtrim( string( number[i], FORMAT=Format, PRINT=PRN),2)
+     
+     if (N_elements(length) eq 0) then len=strlen(tmp[i]) else len = length
+     
+     if (strlen(tmp[i]) gt len) then tmp[i]=strmid(tmp[i],0,len)
+  
+     if (strlen(tmp[i]) lt len) && (padtype eq 0) then begin
+        tmp[i] += strmid(pad,0,len-strlen(tmp[i]))
+     endif
+     
+     if (strlen(tmp[i]) lt len) && (padtype eq 1) then begin
+        tmp[i] = strmid(pad,0,len-strlen(tmp[i]))+tmp[i]
+     endif
+     
+     if (strlen(tmp[i]) lt len) && (padtype eq 2) then begin
+        padln=len-strlen(tmp[i]) & padfr=padln/2 & padend=padln-padfr
+        tmp[i]=strmid(pad,0,padfr)+tmp[i]+strmid(pad,0,padend)
+     endif
+  endfor
+;;Return an array if passed an array, or not if not
+  IF ( SIZE(number,/DIMENSION) EQ 0 ) THEN RETURN,tmp[0] ELSE RETURN,tmp
+end
diff --git a/Code/script_idl_mv/astrolib/strnumber.pro b/Code/script_idl_mv/astrolib/strnumber.pro
new file mode 100644
index 0000000000000000000000000000000000000000..458630186a846a6ae5f39cab543c3f47eb4ec756
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/strnumber.pro
@@ -0,0 +1,84 @@
+function strnumber, st, val, hex = hexflg, NaN = nan, L64 = l64
+;+
+; NAME:
+;      STRNUMBER()
+; PURPOSE:
+;      Function to determine if a string is a valid numeric value.
+;
+; EXPLANATION:
+;      A string is considered a valid numeric value if IDL can convert it
+;      to a numeric variable without error.    
+; CALLING SEQUENCE:
+;      result = strnumber( st, [val, /HEX] )
+;
+; INPUTS:
+;      st - any IDL scalar string
+;
+; OUTPUTS:
+;      1 is returned as the function value if the string st has a
+;      valid numeric value, otherwise, 0 is returned.
+;
+; OPTIONAL OUTPUT:
+;      val - (optional) value of the string. double precision unless /L64 is set
+;
+; OPTIONAL INPUT KEYWORD:
+;       /HEX - If present and nonzero, the string is treated as a hexadecimal
+;             longword integer.
+;       /L64 - If present and nonzero, the val output variable is returned
+;              as a 64 bit integer.    This to ensure that precision is not       
+;              lost when returning a large 64 bit integer as double precision.
+;              This keyword has no effect on the function result.
+;       /NAN - if set, then the value of an empty string is returned as NaN,
+;              by default the returned value is 0.0d.     In either case,
+;              an empty string is considered a valid numeric value.
+;
+; EXAMPLES:
+;      IDL> res = strnumber('0.2d', val)
+;           returns res=1 (a valid number), and val = 0.2000d
+;              
+; NOTES:
+;      (1) STRNUMBER was modified in August 2006 so that an empty string is 
+;      considered a valid number.   Earlier versions of strnumber.pro did not 
+;      do this because in very early (pre-V4.0) versions of IDL
+;      this could corrupt the IDL session.
+;
+;       (2) STRNUMBER will return a string such as '23.45uyrg' as a valid 
+;      number (=23.45) since this is how IDL performs the type conversion.  If
+;      you want a stricter definition of valid number then use the VALID_NUM()
+;      function.
+; HISTORY:
+;      version 1  By D. Lindler Aug. 1987
+;      test for empty string, W. Landsman          February, 1993
+;      Hex keyword added.  MRG, RITSS, 15 March 2000.
+;      An empty string is a valid number   W. Landsman    August 2006
+;      Added /NAN keyword  W. Landsman August 2006
+;      Added /L64 keyword W. Landsman  Feb 2010
+;-
+ compile_opt idl2
+ if N_params() EQ 0 then begin
+      print,'Syntax - result = strnumber( st, [val, /HEX, /NAN] )'
+      return, 0
+ endif
+
+ newstr = strtrim( st )
+ if keyword_set(NAN) then if newstr EQ '' then begin
+        val = !VALUES.D_NAN
+	return, 1
+  endif 	
+
+ On_IOerror, L1                 ;Go to L1 if conversion error occurs
+
+  If ~keyword_set(hexflg) Then Begin
+   val = double( newstr )
+ EndIf Else Begin
+   val = 0L
+   reads, newstr, val, Format="(Z)"
+ EndElse
+
+ if keyword_set(L64) then val = long64( newstr) 
+ return, 1                      ;No conversion error
+
+ L1: return, 0                  ;Conversion error occured
+
+ end
+ 
diff --git a/Code/script_idl_mv/astrolib/substar.pro b/Code/script_idl_mv/astrolib/substar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9ece34e4ae217cb7e26f5452be17953117e02411
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/substar.pro
@@ -0,0 +1,124 @@
+pro substar,image,x,y,mag,id,psfname,VERBOSE = verbose	;Subtract scaled PSF stars
+;+
+; NAME:
+;	SUBSTAR
+; PURPOSE:
+;	Subtract a scaled point spread function at specified star position(s).
+; EXPLANATION:
+;	Part of the IDL-DAOPHOT photometry sequence
+;
+; CALLING SEQUENCE:
+;	SUBSTAR, image, x, y, mag, [ id, psfname, /VERBOSE] 
+;
+; INPUT-OUTPUT:
+;	IMAGE -  On input, IMAGE is the original image array.  A scaled
+;		PSF will be subtracted from IMAGE at specified star positions.
+;		Make a copy of IMAGE before calling SUBSTAR, if you want to
+;		keep a copy of the unsubtracted image array
+;
+; INPUTS:
+;	X -   REAL Vector of X positions found by NSTAR (or FIND)
+;	Y -   REAL Vector of Y positions found by NSTAR (or FIND)        
+;	MAG - REAL Vector of stellar magnitudes found by NSTAR (or APER)
+;		Used to scale the PSF to match intensity at star position.
+;		Stars with magnitude values of 0.0 are assumed missing and 
+;		ignored in the subtraction.
+;
+; OPTIONAL INPUTS:
+;	ID -  Index vector indicating which stars are to be subtracted.  If
+;		omitted, (or set equal to -1), then stars will be subtracted 
+;		at all positions specified by the X and Y vectors.
+;
+;	PSFNAME - Name of the FITS file containing the PSF residuals, as
+;		generated by GETPSF.  SUBSTAR will prompt for this parameter
+;		if not supplied.      
+;
+; OPTIONAL INPUT KEYWORD:
+;	VERBOSE - If this keyword is set and nonzero, then SUBSTAR will 
+;		display the star that it is currently processing      
+;
+; COMMON BLOCKS:
+;	The RINTER common block is used (see RINTER.PRO) to save time in the
+;	PSF calculations
+;
+; PROCEDURES CALLED:
+;	DAO_VALUE(), READFITS(), REMOVE, SXOPEN, SXPAR(), SXREAD()
+; REVISION HISTORY:
+;	Written, W. Landsman                      August, 1988
+;	Added VERBOSE keyword                     January, 1992
+;	Fix star subtraction near edges, W. Landsman    May, 1996
+;	Assume the PSF file is in FITS format  W. Landsman   July, 1997
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ common rinter,c1,c2,c3,init                  ;Save time in RINTER
+ if N_params() LT 4 then begin
+    print,'Syntax - SUBSTAR, image, x, y, mag,[ id, psfname, /VERBOSE]'
+    return
+ endif 
+
+ s = size(image)
+ if s[0] NE 2 then $
+    message, 'ERROR - Input array (first parameter) must be 2 dimensions'
+ npts = N_elements(image)
+
+ if N_elements(psfname) NE 1 then begin
+     psfname = ''
+     read, 'Enter name of the FITS file containing PSF residuals: ', psfname
+ endif
+
+ if N_params() LT 5 then id = indgen( N_elements(x) ) else begin
+    if min(id) LT 0 then id = indgen( N_elements(x) )    ;Subtract all stars?
+ endelse
+
+ psf = readfits(psfname, hpsf)
+ nstar = N_elements(id)            ;Number of stars to subtract
+ gauss = sxpar( hpsf, 'GAUSS*' )
+ psfmag = sxpar( hpsf, 'PSFMAG' )
+ psfrad = sxpar( hpsf, 'PSFRAD' )
+ fitrad = sxpar( hpsf, 'FITRAD' ) 
+ npsf = sxpar( hpsf, 'NAXIS1' )
+
+ nbox = ( 2*fix( psfrad + 0.5 ) + 1) > ((npsf-7)/2)
+ nhalf = (nbox-1)/2
+ psfrsq = psfrad^2
+ lx = fix( x[id] + 0.5 ) - nhalf
+ ly = fix( y[id] + 0.5 ) - nhalf
+ smag = mag[id]
+ scale = 10^(-0.4*(smag- psfmag))
+ xx = x[id] - lx
+ yy = y[id] - ly 
+ bad = where( (smag EQ 0.0), Nbad)        ;Any stars with missing magnitudes?
+ if Nbad GT 0 then begin
+	nstar = nstar - Nbad
+	remove,bad,lx,ly,xx,yy,scale
+ endif
+ rsq = fltarr( nbox, nbox)
+ boxgen = indgen(nbox)
+
+;     Compute RINTER common block arrays
+
+ p_1 = shift(psf,1,0) & p1 = shift(psf,-1,0) & p2 = shift(psf,-2,0)
+ c1 = 0.5*(p1-p_1)
+ c2 = 2.*p1 + p_1 - 0.5*(5.*psf + p2)
+ c3 = 0.5 *(3.*(psf-p1) + p2 - p_1)
+ init = 1
+
+ verbose = keyword_set(VERBOSE)
+ cr = string("15b)
+ for i = 0L,nstar-1 do begin                                 
+   dx = boxgen - xx[i]
+   dy = boxgen - yy[i]
+   dx2 = dx^2 & dy2 = dy^2
+   for j = 0,nbox-1 do rsq[0,j] = dx2 + dy2[j]
+   good = where( rsq LT psfrsq)
+   xgood = good mod nbox      &  ygood = good/nbox
+   dx = dx[xgood]             &  dy = dy[ygood]
+   goodbig = ( xgood + lx[i] ) + ( ygood + ly[i] )*s[1]
+   bad = where( (goodbig LT 0) or (goodbig GE npts), Nbad)
+   if nbad GT 0 then remove,bad,goodbig,dx,dy
+   image[goodbig] = image[goodbig] - scale[i] * dao_value( dx,dy,gauss,psf )
+   if VERBOSE then  $
+             print,f="($,'SUBSTAR: Processing Star',I5,A)",id[i],cr
+endfor                                                 
+return
+end
diff --git a/Code/script_idl_mv/astrolib/sunpos.pro b/Code/script_idl_mv/astrolib/sunpos.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8b25c82e8735bbdb1feb25aa3e246feebe19c95e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sunpos.pro
@@ -0,0 +1,167 @@
+PRO sunpos, jd, ra, dec, longmed, oblt, RADIAN = radian
+;+
+; NAME:
+;       SUNPOS
+; PURPOSE:
+;       To compute the RA and Dec of the Sun at a given date.
+;
+; CALLING SEQUENCE:
+;       SUNPOS, jd, ra, dec, [elong, obliquity, /RADIAN ]
+; INPUTS:
+;       jd    - The Julian date of the day (and time), scalar or vector
+;               usually double precision
+; OUTPUTS:
+;       ra    - The right ascension of the sun at that date in DEGREES
+;               double precision, same number of elements as jd
+;       dec   - The declination of the sun at that date in DEGREES
+;
+; OPTIONAL OUTPUTS:
+;       elong - Ecliptic longitude of the sun at that date in DEGREES.
+;       obliquity - the obliquity of the ecliptic, in DEGREES
+;
+; OPTIONAL INPUT KEYWORD:
+;       /RADIAN - If this keyword is set and non-zero, then all output variables 
+;               are given in Radians rather than Degrees
+;
+; NOTES:
+;       Patrick Wallace (Rutherford Appleton Laboratory, UK) has tested the
+;       accuracy of a C adaptation of the sunpos.pro code and found the 
+;       following results.   From 1900-2100 SUNPOS  gave 7.3 arcsec maximum 
+;       error, 2.6 arcsec RMS.  Over the shorter interval 1950-2050 the figures
+;       were 6.4 arcsec max, 2.2 arcsec RMS.  
+;
+;       The returned RA and Dec are in the given date's equinox.
+;
+;       Procedure was extensively revised in May 1996, and the new calling
+;       sequence is incompatible with the old one.
+; METHOD:
+;       Uses a truncated version of Newcomb's Sun.    Adapted from the IDL
+;       routine SUN_POS by CD Pike, which was adapted from a FORTRAN routine
+;       by B. Emerson (RGO).
+; EXAMPLE:
+;       (1) Find the apparent RA and Dec of the Sun on May 1, 1982
+;       
+;       IDL> jdcnv, 1982, 5, 1,0 ,jd      ;Find Julian date jd = 2445090.5   
+;       IDL> sunpos, jd, ra, dec
+;       IDL> print,adstring(ra,dec,2)
+;                02 31 32.61  +14 54 34.9
+;
+;       The Astronomical Almanac gives 02 31 32.58 +14 54 34.9 so the error
+;               in SUNPOS for this case is < 0.5".      
+;
+;       (2) Find the apparent RA and Dec of the Sun for every day in 1997
+;
+;       IDL> jdcnv, 1997,1,1,0, jd                ;Julian date on Jan 1, 1997
+;       IDL> sunpos, jd+ dindgen(365), ra, dec    ;RA and Dec for each day 
+;
+; MODIFICATION HISTORY:
+;       Written by Michael R. Greason, STX, 28 October 1988.
+;       Accept vector arguments, W. Landsman     April,1989
+;       Eliminated negative right ascensions.  MRG, Hughes STX, 6 May 1992.
+;       Rewritten using the 1993 Almanac.  Keywords added.  MRG, HSTX, 
+;               10 February 1994.
+;       Major rewrite, improved accuracy, always return values in degrees
+;       W. Landsman  May, 1996 
+;       Added /RADIAN keyword,    W. Landsman       August, 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+ compile_opt idl2
+;                       Check arguments.
+ if N_params() LT 3 then begin 
+     print, 'Syntax - SUNPOS, jd, ra, dec, [elong, obliquity, /RADIAN] '
+     print, 'Inputs  -  jd (Julian date)'
+     print, 'Outputs - Apparent RA and Dec, longitude, & obliquity'
+     print, 'All angles in DEGREES unless /RADIAN is set'
+     return
+ endif
+
+ dtor = !DPI/180.0d       ;(degrees to radian, double precision)
+
+;  form time in Julian centuries from 1900.0
+
+ t = (jd - 2415020.0d)/36525.0d0
+
+;  form sun's mean longitude
+
+ l = (279.696678d0+((36000.768925d0*t) mod 360.0d0))*3600.0d0
+
+;  allow for ellipticity of the orbit (equation of centre)
+;  using the Earth's mean anomaly ME
+
+ me = 358.475844d0 + ((35999.049750D0*t) mod 360.0d0)
+ ellcor  = (6910.1d0 - 17.2D0*t)*sin(me*dtor) + 72.3D0*sin(2.0D0*me*dtor)
+ l = l + ellcor
+
+; allow for the Venus perturbations using the mean anomaly of Venus MV
+
+ mv = 212.603219d0 + ((58517.803875d0*t) mod 360.0d0) 
+ vencorr = 4.8D0 * cos((299.1017d0 + mv - me)*dtor) + $
+          5.5D0 * cos((148.3133d0 +  2.0D0 * mv  -  2.0D0 * me )*dtor) + $
+          2.5D0 * cos((315.9433d0 +  2.0D0 * mv  -  3.0D0 * me )*dtor) + $
+          1.6D0 * cos((345.2533d0 +  3.0D0 * mv  -  4.0D0 * me )*dtor) + $
+          1.0D0 * cos((318.15d0   +  3.0D0 * mv  -  5.0D0 * me )*dtor)
+l = l + vencorr
+
+;  Allow for the Mars perturbations using the mean anomaly of Mars MM
+
+ mm = 319.529425d0  +  (( 19139.858500d0 * t)  mod  360.0d0 )
+ marscorr = 2.0d0 * cos((343.8883d0 -  2.0d0 * mm  +  2.0d0 * me)*dtor ) + $
+            1.8D0 * cos((200.4017d0 -  2.0d0 * mm  + me) * dtor)
+ l = l + marscorr
+
+; Allow for the Jupiter perturbations using the mean anomaly of
+; Jupiter MJ
+
+ mj = 225.328328d0  +  (( 3034.6920239d0 * t)  mod  360.0d0 )
+ jupcorr = 7.2d0 * cos(( 179.5317d0 - mj + me )*dtor) + $
+          2.6d0 * cos((263.2167d0  -  MJ ) *dtor) + $
+          2.7d0 * cos(( 87.1450d0  -  2.0d0 * mj  +  2.0D0 * me ) *dtor) + $
+          1.6d0 * cos((109.4933d0  -  2.0d0 * mj  +  me ) *dtor)
+ l = l + jupcorr
+
+; Allow for the Moons perturbations using the mean elongation of
+; the Moon from the Sun D
+
+ d = 350.7376814d0  + (( 445267.11422d0 * t)  mod  360.0d0 )
+ mooncorr  = 6.5d0 * sin(d*dtor)
+ l = l + mooncorr
+
+; Allow for long period terms
+
+ longterm  = + 6.4d0 * sin(( 231.19d0  +  20.20d0 * t )*dtor)
+ l  =    l + longterm
+ l  =  ( l + 2592000.0d0)  mod  1296000.0d0 
+ longmed = l/3600.0d0
+
+; Allow for Aberration
+
+ l  =  l - 20.5d0
+
+; Allow for Nutation using the longitude of the Moons mean node OMEGA
+
+ omega = 259.183275d0 - (( 1934.142008d0 * t ) mod 360.0d0 )
+ l  =  l - 17.2d0 * sin(omega*dtor)
+
+; Form the True Obliquity
+
+ oblt  = 23.452294d0 - 0.0130125d0*t + (9.2d0*cos(omega*dtor))/3600.0d0
+
+; Form Right Ascension and Declination
+
+ l = l/3600.0d0
+ ra  = atan( sin(l*dtor) * cos(oblt*dtor) , cos(l*dtor) )
+
+ neg = where(ra LT 0.0d0, Nneg) 
+ if Nneg GT 0 then ra[neg] = ra[neg] + 2.0d*!DPI
+
+ dec = asin(sin(l*dtor) * sin(oblt*dtor))
+ 
+ if keyword_set(RADIAN) then begin
+        oblt = oblt*dtor 
+        longmed = longmed*dtor
+ endif else begin
+        ra = ra/dtor
+        dec = dec/dtor
+ endelse
+ end
diff --git a/Code/script_idl_mv/astrolib/sunsymbol.pro b/Code/script_idl_mv/astrolib/sunsymbol.pro
new file mode 100644
index 0000000000000000000000000000000000000000..5bd2558708caaac9d7568e5560f5236e2e8b5faa
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sunsymbol.pro
@@ -0,0 +1,77 @@
+function sunsymbol, FONT=font
+;+
+; NAME:
+;	SUNSYMBOL
+; PURPOSE:
+;	Return the Sun symbol as a subscripted postscript character string
+; EXPLANATION:
+;	Returns the Sun symbol (circle with a dot in the middle) as a 
+;	(subscripted) postscript character string.    Needed because although 
+;	the Sun symbol	is available using the vector fonts as the string 
+;	'!9n', it is not in the standard postscript set.   
+;
+; CALLING SEQUENCE:
+;	result = SUNSYMBOL([FONT= ])
+;
+; INPUTS:
+;	None
+;
+; OPTIONAL INPUT KEYWORDS:
+;       font = scalar font graphics keyword (-1,0 or 1) for text.   Note that
+;              this keyword is useful for printing text with XYOUTS but *not*
+;              e.g. the XTIT keyword to PLOT where the font call to PLOT takes
+;              precedence.
+;
+; OUTPUTS:
+;	result - a scalar string representing the Sun symbol.   A different
+;		string is output depending (1) the device is postscript and
+;		hardware fonts are used (!P.FONT=0), (2) vector fonts are used,
+;		or (3) hardware fonts are used on a non-postscript device.
+;		For case (3), SUNSYMBOL simply outputs the 3 character string
+;		'Sun'
+;
+; EXAMPLE:
+;	To make the X-axis of a plot read  M/M_Sun
+;	IDL>  cgplot,indgen(10),xtit = 'M / M' + sunsymbol()
+;
+; RESTRICTIONS:
+;	(1) The postscript output does not have the dot perfectly centered in 
+;		the circle.   For a better symbol, consider postprocessing with
+;               psfrag (see http://www.astrobetter.com/idl-psfrag/ ).
+;	(2) SUNSYMBOL() includes subscript output positioning commands in the 
+;		output string.
+;       (3) For true-type fonts(Font=1) and IDL Versions prior to V8.2,
+;           you must first use the SET_FONT keyword to Device to use a font
+;           that includes the Sun Symbol, e.g. "arial Unicode MS" or
+;           the Apple Symbols font.
+;           http://www.idlcoyote.com/misc_tips/sun_symbol.html
+;           In V8.2 and later, SUNSYMBOL() will automatically convert to the
+;           DejaVuSans font to create a Sun symbol (and then return to the 
+;           input font).
+;       (4) Also look at CGSYMBOL http://www.idlcoyote.com/programs/cgsymbol.pro
+;           which includes 'sun' as one if the symbols.
+; REVISION HISTORY:
+;	Written,  W. Landsman,    HSTX          April, 1997
+;       Allow font keyword to be passed.  T. Robishaw Apr. 2006
+;       Since IDL8.2 a Sun symbol is available for true-type fonts Feb 2013
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_elements(font) eq 0 then font = !p.font
+ if (font EQ -1) then return,'!D!9n!N!X' else $
+ if (!D.NAME NE 'PS')  then return,'!DSun!N' else begin
+
+;Since 8.2 we can use !10 to select DejaVuSans font and then use the 
+;unicode Sun symbol
+ if FONT EQ 1 then $ 
+     if (!VERSION.RELEASE GE '8.2') then return,'!10!D!Z(2609)!X!N' else $
+     return,'!D!Z(2609)!X!N'
+;Want to use /AVANTGARDE,/BOOK which is the default font 17, but to make sure
+;that ISOLATIN encoding is turned off, we'll define our own font.
+
+   device,/AVANTGARDE,/BOOK,ISOLATIN=0,FONT_INDEX = 20
+
+   return, '!20!S!DO!R!I ' + string(183b) + '!X!N'
+ endelse
+ end
diff --git a/Code/script_idl_mv/astrolib/sxaddhist.pro b/Code/script_idl_mv/astrolib/sxaddhist.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7597d1583962f2521bff9a57d432c3828dd84dc6
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxaddhist.pro
@@ -0,0 +1,137 @@
+pro sxaddhist,history,header,blank = blank,comment= comment, location=key, $
+                            pdu=pdu
+;+
+; NAME:
+;       SXADDHIST                           
+; PURPOSE:
+;       Procedure to add HISTORY (or COMMENT) line(s) to a FITS header
+;
+; EXPLANATION:
+;       The advantage of using SXADDHIST instead of SXADDPAR is that with 
+;       SXADDHIST many HISTORY or COMMENT records can be added in a single call.
+;
+; CALLING SEQUENCE
+;       sxaddhist, history, header, [ /PDU, /COMMENT ]
+;
+; INPUTS:
+;        history - string or string array containing history or comment line(s)
+;               to add to the FITS header
+; INPUT/OUTPUT
+;       header - FITS header (string array).   Upon output, it will contain the
+;               specified HISTORY records added to the end
+;
+; OPTIONAL KEYWORD INPUTS:
+;       /BLANK - If specified then blank ('       ') keywords will be written
+;              rather than 'HISTORY ' keywords.
+;       /COMMENT - If specified, then 'COMMENT ' keyword will be written rather
+;              than 'HISTORY ' keywords.    
+;              Note that according to the FITS definition, any number of 
+;              'COMMENT' and 'HISTORY' or blank keywords may appear in a header,
+;              whereas all other keywords may appear only once.   
+;       LOCATION=key - If present, the history will be added before this
+;              keyword.  Otherwise put it at the end.
+;       /PDU - if specified, the history will be added to the primary
+;              data unit header, (before the line beginning BEGIN EXTENSION...)               
+;              Otherwise, it will be added to the end of the header.
+;              This has meaning only for extension headers using the STScI
+;              inheritance convention. 
+; OUTPUTS:
+;       header - updated FITS header
+;
+; EXAMPLES:
+;       sxaddhist, 'I DID THIS', header      ;Add one history record
+;
+;       hist = strarr(3)
+;       hist[0] = 'history line number 1'
+;       hist[1[ = 'the next history line'
+;       hist[2] = 'the last history line'
+;       sxaddhist, hist, header              ;Add three history records
+;
+; SIDE EFFECTS:
+;       Header array is truncated to the final END statement
+;       LOCATION overrides PDU.
+; HISTORY:
+;       D. Lindler  Feb. 87
+;       April 90  Converted to new idl  D. Lindler
+;       Put only a single space after HISTORY   W. Landsman  November 1992
+;       Aug. 95   Added PDU keyword parameters
+;       LOCATION added.  M. Greason, 28 September 2004.
+;       Missing minus sign (1 -> -1) in testing for WHERE output when 
+;        looking for location to insert a comment  M. Haffner Oct 2012
+;-
+;--------------------------------------------------------------------
+        On_error,2
+
+        if N_params() LT 2 then begin
+           print, ' Syntax - SXADDHIST, hist, header, '
+           print,  '       /PDU, /BLANK, /COMMENT, LOCATION= ] '
+           return
+        endif
+
+; Check input parameters
+
+        if (n_elements(key) LE 0) then keynam = ''                      $
+                                  else keynam = strupcase(strtrim(key, 2))
+
+        s = size(history) & ndim = s[0] & type = s[ndim+1]
+        if type NE 7 then message, $
+            'Invalid history lines specified; must be a string or string array'
+ 
+        if keyword_set(COMMENT) then keyword = 'COMMENT ' else $
+        if keyword_set(BLANK) then keyword = ' ' else $
+                                   keyword = 'HISTORY '
+        nadd = N_elements(history)            ;Number of lines to add
+
+        s = size(header) & ndim2 = s[0] & type = s[ndim2+1]
+        if (ndim2 NE 1) || (type NE 7) then message, $
+                'Invalid FITS header supplied; header must be a string array'
+
+        nlines = N_elements(header)           ;Number of lines in header
+
+; Find END statement of FITS header
+        
+        endline = where( strtrim(strmid(header,0,8),2) EQ 'END' )
+        n = endline[0]
+        if n LT 0 then message, $
+                    'Invalid FITS header array, END keyword not found'
+
+        blank = string( replicate(32b,80) )
+        n1 = n          ;position to insert
+;
+; if LOCATION was specified and found, make room before it.
+;
+        locfnd = 0
+        if (strlen(keynam) gt 0) then begin
+            extline = where( strupcase(strtrim(strmid(header,0,8),2)) EQ keynam )
+            n_ext = extline[0]
+            if (n_ext gt -1) then begin
+                n1 = n_ext
+                locfnd = 1
+            endif
+        endif
+;
+; if /PDU find beginning of the extension header and make room for the
+; history
+;
+        if (keyword_set(PDU) && (locfnd EQ 0)) then begin
+            extline = where( strupcase(strtrim(strmid(header,0,8),2)) EQ 'BEGIN EX' )
+            n_ext = extline[0]
+            if n_ext gt 1 then n1 = n_ext
+        end
+;
+; make room in the header
+;
+        if n1 eq 0 then header = [replicate(blank,nadd),header[n1:n]] else $
+                header = [header[0:n1-1],replicate(blank,nadd),header[n1:n]]
+
+; Add history records to header starting at position N1
+
+        for i = 0, nadd-1 do begin
+        
+                newline = blank
+                strput, newline, keyword + history[i]
+                header[n1+i] = newline
+
+        endfor
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/sxaddpar.pro b/Code/script_idl_mv/astrolib/sxaddpar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..fa95b949bde988547d272534ff612a819574c2c8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxaddpar.pro
@@ -0,0 +1,390 @@
+Pro sxaddpar, Header, Name, Value, Comment, Location, before=before, $
+                 savecomment = savecom, after=after , format=format, pdu = pdu, $
+                 missing = missing, null = null
+;+
+; NAME:
+;       SXADDPAR
+; PURPOSE:
+;       Add or modify a parameter in a FITS header array.
+;
+; CALLING SEQUENCE:
+;       SXADDPAR, Header, Name, Value, [ Comment,  Location, /SaveComment, 
+;                               BEFORE =, AFTER = , FORMAT= , /PDU
+;                               /SAVECOMMENT, Missing=, /Null
+; INPUTS:
+;       Header = String array containing FITS or STSDAS header.    The
+;               length of each element must be 80 characters.    If not 
+;               defined, then SXADDPAR will create an empty FITS header array.
+;
+;       Name = Name of parameter. If Name is already in the header the value 
+;               and possibly comment fields are modified.  Otherwise a new 
+;               record is added to the header.  If name is equal to 'COMMENT'
+;               or 'HISTORY' or a blank string then the value will be added to 
+;               the record without replacement.  For these cases, the comment 
+;               parameter is ignored.
+;
+;       Value = Value for parameter.  The value expression must be of the 
+;               correct type, e.g. integer, floating or string.  String values
+;                of 'T' or 'F' are considered logical values.
+;
+; OPTIONAL INPUT PARAMETERS:
+;       Comment = String field.  The '/' is added by this routine.  Added 
+;               starting in position 31.    If not supplied, or set equal to 
+;               '', or /SAVECOMMENT is set, then the previous comment field is 
+;               retained (when found) 
+;
+;       Location = Keyword string name.  The parameter will be placed before the
+;               location of this keyword.    This parameter is identical to
+;               the BEFORE keyword and is kept only for consistency with
+;               earlier versions of SXADDPAR.
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS:
+;       BEFORE  = Keyword string name.  The parameter will be placed before the
+;               location of this keyword.  For example, if BEFORE='HISTORY'
+;               then the parameter will be placed before the first history
+;               location.  This applies only when adding a new keyword;
+;               keywords already in the header are kept in the same position.
+;
+;       AFTER   = Same as BEFORE, but the parameter will be placed after the
+;               location of this keyword.  This keyword takes precedence over
+;               BEFORE.
+;
+;       FORMAT  = Specifies FORTRAN-like format for parameter, e.g. "F7.3".  A
+;               scalar string should be used.  For complex numbers the format
+;               should be defined so that it can be applied separately to the
+;               real and imaginary parts.  If not supplied then the default is
+;               'G19.12' for double precision, and 'G14.7' for floating point.
+;       /NULL   = If set, then keywords with values which are undefined, or
+;                 which have non-finite values (such as NaN, Not-a-Number) are
+;                 stored in the header without a value, such as
+;
+;                       MYKEYWD =                      /My comment
+;
+;       MISSING = A value which signals that data with this value should be
+;                 considered missing.  For example, the statement
+;
+;                       FXADDPAR, HEADER, 'MYKEYWD', -999, MISSING=-999
+;
+;                 would result in the valueless line described above for the
+;                 /NULL keyword.  Setting MISSING to a value implies /NULL.
+;                 Cannot be used with string or complex values.
+;       /PDU    = specifies keyword is to be added to the primary data unit
+;               header. If it already exists, it's current value is updated in
+;               the current position and it is not moved.
+;       /SAVECOMMENT = if set, then any existing comment is retained, i.e. the
+;               COMMENT parameter only has effect if the keyword did not 
+;               previously exist in the header.
+; OUTPUTS:
+;       Header = updated FITS header array.
+;
+; EXAMPLE:
+;       Add a keyword 'TELESCOP' with the value 'KPNO-4m' and comment 'Name
+;       of Telescope' to an existing FITS header h.
+;
+;       IDL> sxaddpar, h, 'TELESCOPE','KPNO-4m','Name of Telescope'
+; NOTES:
+;       The functions SXADDPAR() and FXADDPAR() are nearly identical, with the
+;       major difference being that FXADDPAR forces required FITS keywords
+;       BITPIX, NAXISi, EXTEND, PCOUNT, GCOUNT to appear in the required order
+;       in the header, and FXADDPAR supports the OGIP LongString convention.   
+;       There is no particular reason for having two nearly identical 
+;       procedures, but both are too widely used to drop either one.
+;
+;       All HISTORY records are inserted in order at the end of the header.
+;
+;       All COMMENT records are also inserted in order at the end of the header
+;       header, but before the HISTORY records.  The BEFORE and AFTER keywords
+;       can override this.
+;
+;       All records with no keyword (blank) are inserted in order at the end of
+;       the header, but before the COMMENT and HISTORY records.  The BEFORE and
+;       AFTER keywords can override this.
+
+; RESTRICTIONS:
+;       Warning -- Parameters and names are not checked
+;               against valid FITS parameter names, values and types.
+;
+; MODIFICATION HISTORY:
+;       DMS, RSI, July, 1983.
+;       D. Lindler Oct. 86  Added longer string value capability
+;       Converted to NEWIDL  D. Lindler April 90
+;       Added Format keyword, J. Isensee, July, 1990
+;       Added keywords BEFORE and AFTER. K. Venkatakrishna, May '92
+;       Pad string values to at least 8 characters   W. Landsman  April 94
+;       Aug 95: added /PDU option and changed routine to update last occurrence
+;               of an existing keyword (the one SXPAR reads) instead of the
+;               first occurrence.
+;       Comment for string data can start after column 32 W. Landsman June 97
+;       Make sure closing quote supplied with string value  W. Landsman  June 98
+;       Increase precision of default formatting of double precision floating
+;               point values.   C. Gehman, JPL  September 1998
+;       Mar 2000, D. Lindler, Modified to use capital E instead of lower case
+;               e for exponential formats.
+;       Apr 2000, Make user-supplied format upper-case  W. Landsman 
+;       Oct 2001, Treat COMMENT or blank string like HISTORY keyword W. Landsman
+;       Jan 2002, Allow BEFORE, AFTER to apply to COMMENT keywords W. Landsman
+;       June 2003, Added SAVECOMMENT keyword    W. Landsman
+;       Jan 2004, If END is missing, then add it at the end W. Landsman
+;       May 2005 Fix SAVECOMMENT error with non-string values W. Landsman
+;       Oct 2005 Jan 2004 change made SXADDPAR fail for empty strings W.L.
+;       May 2011 Fix problem with slashes in string values W.L. 
+;       Aug 2013 Only use keyword_set for binary keywords W. L. 
+;       Sep 2015 Added NULL and MISSING keywords W.L>
+;       
+;-
+ compile_opt idl2
+ if N_params() LT 3 then begin             ;Need at least 3 parameters
+      print,'Syntax - Sxaddpar, Header, Name,  Value, [Comment, Postion'
+      print,'                      BEFORE = ,AFTER = , FORMAT =, /SAVECOMMENT'
+      print,'                      MISSING =, /NULL'
+      return
+ endif
+
+; Define a blank line and the END line
+
+ ENDLINE = 'END' +string(replicate(32b,77))     ;END line
+ BLANK = string(replicate(32b,80))             ;BLANK line
+;
+;  If Location parameter not defined, set it equal to 'END     '
+;
+ if ( N_params() GT 4 ) then loc = strupcase(location) else $
+ if N_elements( BEFORE) GT 0 then loc = strupcase(before) else $
+ if N_elements( AFTER) GT 0  then loc = strupcase(after) else $
+ if N_elements( PDU) GT 0  then loc = 'BEGIN EX' else $
+                             loc = 'END'
+
+ while strlen(loc) lt 8 do loc += ' '
+
+ if N_params() lt 4 then comment = ''      ;Is comment field specified?
+
+ n = N_elements(header)                  ;# of lines in FITS header
+ if (n EQ 0) then begin                  ;header defined?
+          header=strarr(10)              ;no, make it.
+          header[0]=ENDLINE
+          n=10
+ endif else begin
+          s = size(header)               ;check for string type
+              if (s[0] ne 1) || (s[2] ne 7) then $
+                  message,'FITS Header (first parameter) must be a string array'
+ endelse
+
+;  Make sure Name is 8 characters long
+
+        nn = string(replicate(32b,8))   ;8 char name
+        strput,nn,strupcase(name) ;insert name
+;
+;  Check to see if the parameter should be saved as a null value.
+;
+        stype = size(value,/type)
+        save_as_null = 0
+        if stype EQ 0 then $
+            if (n_elements(missing) eq 1) || keyword_set(null) then $
+              save_as_null = 1 else $
+                message = 'keyword value (third parameter) is not defined'
+        if (stype NE 6) && (stype NE 7) && (stype NE 9) then begin
+            if N_elements(missing) eq 1 then $
+              if value eq missing then save_as_null = 1
+              if ~save_as_null then if ~finite(value) then begin
+                if ((n_elements(missing) eq 1) || keyword_set(null)) then $
+                  save_as_null = 1 else $
+                    message = 'keyword value (third parameter) is not finite'
+            endif
+        endif
+;
+;  Extract first 8 characters of each line of header, and locate END line
+
+ keywrd = strmid(header,0,8)                 ;Header keywords
+ iend = where(keywrd eq 'END     ',nfound)
+;
+;  If no END, then add it.  Either put it after the last non-null string, or
+;  append it to the end.
+;
+        if nfound EQ 0 then begin
+                ii = where(strtrim(header) ne '',nfound)
+                ii = max(ii) + 1
+                if ii eq n_elements(header) then begin
+                        header = [header,endline]
+                        n++ 
+                endif else header[ii] = endline
+                keywrd = strmid(header,0,8)
+                iend = where(keywrd eq 'END     ',nfound)
+        endif
+;
+        iend = iend[0] > 0                      ;make scalar
+
+;  History, comment and "blank" records are treated differently from the
+;  others.  They are simply added to the header array whether there are any
+;  already there or not.
+
+ if (nn EQ 'HISTORY ') || (nn EQ 'COMMENT ') || $
+    (nn EQ '        ')  then begin             ;add history record?
+;
+;  If the header array needs to grow, then expand it in increments of 5 lines.
+;
+
+     if iend GE (n-1) then begin
+                 header = [header,replicate(blank,5)] ;yes, add 5.
+                 n = N_elements(header)
+      endif
+
+; Format the record
+
+      newline = blank
+      strput,newline,nn+string(value),0
+
+;
+;  If a history record, then append to the record just before the end.
+;
+      if nn EQ 'HISTORY ' then begin
+             header[iend] = newline             ;add history rec.
+             header[iend+1] = endline
+;
+;  The comment record is placed immediately after the last previous comment
+;  record, or immediately before the first history record, unless overridden by
+;  either the BEFORE or AFTER keywords.
+;
+      endif else if nn EQ 'COMMENT ' then begin
+            if loc EQ 'END     ' then loc = 'COMMENT '
+            iloc = where(keywrd EQ loc, nloc)
+            if nloc EQ 0 then iloc = where(keywrd EQ 'HISTORY ', nloc)
+            if nloc gt 0 then begin
+               i = iloc[nloc-1]
+               if keyword_set(after) or (loc EQ 'COMMENT ') then i = i+1 < iend 
+               if i gt 0 then header=[header[0:i-1],newline,header[i:n-1]] $
+                        else header=[newline,header[0:n-1]]
+            endif else begin
+                header[iend] = newline
+                header[iend+1] = endline
+            endelse
+
+;
+;  The "blank" record is placed immediately after the last previous "blank"
+;  record, or immediately before the first comment or history record, unless
+;  overridden by either the BEFORE or AFTER keywords.
+;
+          ENDIF ELSE BEGIN
+            if loc EQ 'END     ' then loc = '       '
+            iloc = where(keywrd[0:iend] EQ loc, nloc)
+            if nloc gt 0 then begin
+               i = iloc[0]
+               if keyword_set(after) and loc ne 'HISTORY ' then i = i+1 < iend 
+               if i gt 0 then header=[header[0:i-1],newline,header[i:n-1]] $
+                        else header=[newline,header[0:n-1]]
+            endif else begin
+                iloc = where(keywrd EQ 'COMMENT ', nloc)
+                if nloc Eq 0 then iloc = where(keywrd EQ 'HISTORY ', nloc)
+                if nloc GT 0 then begin
+                   i = iloc[0]
+                   if i gt 0 then header=[header[0:i-1],newline,header[i:n-1]] $
+                        else header=[newline,header[0:n-1]]
+                endif else begin
+                  header[iend] = newline
+                  header[iend+1] = endline
+            endelse
+            endelse
+           endelse
+            RETURN
+ endif
+
+; Find location to insert keyword.   Save the existing comment if user did
+; not supply a new one.   Comment starts after column 32 for numeric data,
+; after the slash (but at least after final quote) for string data. 
+
+ ncomment = comment
+ ipos  = where(keywrd eq nn,nfound)
+ if nfound gt 0 then begin
+         i = ipos[nfound-1]
+         if comment eq '' or keyword_set(savecom) then begin  ;save comment?
+         if strmid(header[i],10,1) NE "'" then $
+                 ncomment=strmid(header[i],32,48) else begin
+		 quote = strpos(header[i],"'",11)
+		
+                 if quote EQ -1 then slash = -1 else $
+		       slash = strpos(header[i],'/',quote)  		
+                 if slash NE -1 then $
+                        ncomment =  strmid(header[i], slash+1, 80) else $
+                        ncomment = string(replicate(32B,80))
+                endelse
+        endif 
+         goto, REPLACE    
+ endif
+
+ if loc ne '' then begin
+          iloc =  where(keywrd eq loc,nloc)
+          if nloc gt 0 then begin
+             i = iloc[0]
+             if keyword_set(after) && (loc ne 'HISTORY ') then i = i+1 < iend 
+             if i gt 0 then header=[header[0:i-1],blank,header[i:n-1]] $
+                        else header=[blank,header[0:n-1]]
+             goto, REPLACE  
+          endif
+ endif
+
+; At this point keyword and location parameters were not found, so a new
+; line is added at the end of the FITS header
+
+        if iend lt (n-1) then begin     ;Not found, add more?
+                header[iend+1] = ENDLINE        ;no, already long enough.
+                i = iend                ;position to add.
+           endif else begin             ;must lengthen.
+                header = [header,replicate(blank,5)] ;add an element on the end
+                header[n]=ENDLINE               ;save "END"
+                i =n-1                  ;add to end
+        end
+
+; Now put value into keyword at line i
+
+REPLACE:    
+        h=blank                 ;80 blanks
+        strput,h,nn+'= '        ;insert name and =.
+        apost = "'"             ;quote a quote
+        type = size(value)      ;get type of value parameter
+        if type[0] ne 0 then $
+                message,'Keyword Value (third parameter) must be scalar'
+
+        case type[1] of         ;which type?
+
+7:      begin
+          upval = strupcase(value)      ;force upper case.
+          if (upval eq 'T') || (upval eq 'F') then begin
+                strput,h,upval,29  ;insert logical value.
+            end else begin              ;other string?
+                if strlen(value) gt 18 then begin       ;long string
+                    strput, h, apost + strmid(value,0,68) + apost + $
+                        ' /' + ncomment,10
+                    header[i] = h
+                    return
+                endif
+                strput, h, apost + value,10       ;insert string val
+                strput, h, apost, 11 + (strlen(value)>8)   ;pad string vals
+          endelse                                          ;to at least 8 chars
+          endcase
+
+5:      BEGIN
+        IF (N_ELEMENTS(format) EQ 1) THEN $             ; use format keyword
+            v = string(value, FORMAT='('+strupcase(format)+')') $
+        ELSE v = STRING(value, FORMAT='(G19.12)')
+        s = strlen(v)                                   ; right justify
+        strput, h, v, (30-s)>10
+        END
+
+ else:  begin
+        if ~save_as_null then begin
+        if (N_elements(format) eq 1) then $            ;use format keyword
+            v = string(value, FORMAT='('+strupcase(format)+')' ) else $
+            v = strtrim(strupcase(value),2)      
+                                      ;convert to string, default format
+        s = strlen(v)                 ;right justify
+        strput,h,v,(30-s)>10          ;insert
+        endif
+        end
+ endcase
+
+ if (~save_as_null) || (strlen(strtrim(comment)) GT 0) then begin
+   strput,h,' /',30       ;add ' /'
+   strput, h, ncomment, 32 ;add comment
+ endif  
+   header[i] = h          ;save line
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/sxdelpar.pro b/Code/script_idl_mv/astrolib/sxdelpar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..2cd73a5ec1f0a7201b5b8bb6ff6b1f9afadb28f2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxdelpar.pro
@@ -0,0 +1,69 @@
+pro sxdelpar, h, parname
+;+
+; NAME:
+;	SXDELPAR
+; PURPOSE:
+;	Procedure to delete a keyword parameter(s) from a FITS header
+;
+; CALLING SEQUENCE:
+;	sxdelpar, h, parname
+;
+; INPUTS:
+;	h - FITS or STSDAS header, string array
+;	parname - string or string array of keyword name(s) to delete
+;
+; OUTPUTS:
+;	h - updated FITS header, If all lines are deleted from 
+;		the header, then h is returned with a value of 0
+;
+; EXAMPLE:
+;	Delete the astrometry keywords CDn_n from a FITS header, h
+;
+;	IDL> sxdelpar, h, ['CD1_1','CD1_2','CD2_1','CD2_2']
+;
+; NOTES:
+;	(1)  No message is returned if the keyword to be deleted is not found
+;	(2)  All appearances of a keyword in the header will be deleted
+; HISTORY:
+;	version 1  D. Lindler Feb. 1987
+;	Test for case where all keywords are deleted    W. Landsman Aug 1995 
+;       Allow for headers with more than 32767 lines W. Landsman Jan. 2003
+;       Use ARRAY_EQUAL, cleaner syntax  W. L.  July 2009
+;------------------------------------------------------------------
+ On_error,2
+ compile_opt idl2
+
+ if N_Params() LT 2 then begin
+      print,'Syntax - SXDELPAR, h, parname'
+      return
+ endif
+
+; convert parameters to string array of upper case names of length 8 char
+
+
+ if size(parname,/type) NE 7 then $
+         message,'Keyword name(s) must be a string or string array'
+ par = strtrim( strupcase(parname),2 ) 
+
+ sz = size(h,/structure)
+ if (sz.N_dimensions NE 1) || (sz.type NE 7) then $
+	message,'FITS header (1st parameter) must be a string array'
+
+ nlines = sz.N_elements		;number of lines in header array
+ pos = 0L		;position in compressed header with keywords removed
+
+; loop on header lines
+
+ keyword = strtrim( strmid(h,0,8), 2 )
+ for i = 0L, nlines-1 do begin
+        if array_equal(keyword[i] NE par, 1b) then begin   
+ 	   h[pos] = h[i]		;keep it
+	   pos++		;increment number of lines kept
+	   if keyword[i] eq 'END' then break  	;end of header
+        endif 
+ endfor
+
+ if pos GT 0 then h = h[0:pos-1] else h = 0	      ;truncate
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/sxginfo.pro b/Code/script_idl_mv/astrolib/sxginfo.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e12ae35983a19bc19470dc280254cc4b73cd937d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxginfo.pro
@@ -0,0 +1,126 @@
+pro sxginfo,h,par,type,sbyte,nbytes
+;+
+; NAME:
+;	SXGINFO
+;
+; PURPOSE:
+;	Return information on all group parameters in an STSDAS header.
+; EXPLANATION:
+;	Return datatype, starting byte, and number bytes for all group
+;	parameters in an STSDAS file.     Obtaining these values 
+;	greatly speed up execution time in subsequent calls to SXGPAR.
+;
+; CALLING SEQUENCE:
+;	sxginfo, h, par, type, sbyte, nbytes
+;
+; INPUTS:
+;	h - header returned by SXOPEN
+;	par - parameter block returned by SXREAD or multiple
+;		parameter blocks stored in array of dimension
+;		greater than one.
+;
+; OUTPUT:
+;	type - data type (if not supplied or null string, the
+;		header is searched for type,sbyte, and nbytes)
+;	sbyte - starting byte in parameter block for data
+;	nbytes - number of bytes in parameter block for data
+;
+;	The number of elements in type,sbyte and nbytes equals the total
+;	number of group parameters.
+;
+; METHOD:
+;	The parameter type for each parameter is obtained
+;	from PDTYPEn keyword.  If not found then DATATYPE keyword
+;	value is used.  If that is not found then BITPIX is
+;	used.  BITPIX=8, byte; BITPIX=16 integer*2; BITPIX=32
+;	integer*4.
+;
+; NOTES:
+;	For an example of the use of SXGINFO, see CONV_STSDAS
+;
+; HISTORY:
+;	version 1  W. Landsman   Apr. 93
+;
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;------------------------------------------------------------
+ On_error,2
+
+ if N_params() LT 3 then begin
+	print,'Syntax - sxginfo,h,par,type,sbyte,nbytes'
+	return
+ endif
+
+; determine size of output result
+
+	s = size(par)
+	ndim = s[0]
+	dtype = s[ndim+1]
+	case 1 of
+	    (ndim eq 0) or (dtype ne 1) : message, $
+			'Invalid parameter block specified'	
+
+	    ndim eq 1 : begin
+			scalar = 1	; output will be scalar
+			dimen = intarr(1)+1
+			end
+	    else: begin
+			scalar = 0	; output will be vector
+			dimen = s[2:ndim]
+		 	end
+	endcase
+	plen = s[1]		;length of parameter blocks
+;
+; check remaining input parameters
+;
+	s=size(h)
+	!err=-1
+	if (s[0] ne 1) or (s[2] ne 7) then message, $
+		'Header array must be string array'
+
+	if strlen(h[0]) ne 80 then message, $
+		'Header must contain 80 character strings'
+;
+; get number of group parameters and size
+;
+;
+	pcount = sxpar(h,'PCOUNT')	;get number of group parameters
+	if pcount eq 0 then begin
+		message,'No group parameters present',/INFO
+		return
+	endif
+
+        sbyte = intarr(pcount)
+        nbytes = intarr(pcount)
+        type = strarr(pcount)
+
+; Determine BITPIX and DATATYPE in case PSIZE or PDTYPE is undefined
+
+	nbits=0		;number of bits to skip
+	dtype = strtrim(sxpar(h, 'DATATYPE') )
+	bitpix = sxpar(h,'BITPIX')
+	if !err lt 0 then begin
+		case bitpix of
+			8: dtype = 'BYTE'
+			16: dtype = 'INTEGER*2'
+			32: dtype = 'INTEGER*4'
+                       -32: dtype = 'REAL*4'
+		       -64: dtype = 'REAL*8'	
+		endcase
+	endif
+
+	for i = 1,pcount do begin
+		nbit = sxpar(h,'PSIZE'+strtrim(i,2))
+		if !err lt 0 then nbit = bitpix
+		nbits=nbits+nbit
+		if i NE pcount then sbyte[i]=nbits/8   ;number of bytes to skip
+		pdtype = strtrim(sxpar(h,'PDTYPE' + strtrim(i,2)))
+		if !ERR LT 0 then pdtype = dtype
+		type[i-1] = pdtype
+		aster = strpos(pdtype,'*')
+		if aster gt 0 then $
+		nbytes[i-1]=fix(strmid(pdtype,aster+1,strlen(pdtype)-aster-1)) $
+		else nbytes[i-1]=4
+	endfor
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/sxgpar.pro b/Code/script_idl_mv/astrolib/sxgpar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0c2f10d1aa21bcee97112ade22c045fc5719d8c5
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxgpar.pro
@@ -0,0 +1,228 @@
+function sxgpar,h,par,name,type,sbyte,nbytes
+;
+;+
+; NAME:
+;	SXGPAR                           
+;
+; PURPOSE:
+;	Obtain group parameter value in SDAS/FITS file
+;
+; CALLING SEQUENCE:
+;	result = sxgpar( h, par, name, [ type, sbyte, nbytes] )
+;
+; INPUTS:
+;	h - header returned by SXOPEN
+;	par - parameter block returned by SXREAD or multiple
+;		parameter blocks stored in array of dimension
+;		greater than one.
+;	name - parameter name (keyword PTYPEn) or integer
+;		parameter number.
+;
+; OPTIONAL INPUT/OUTPUT
+;	type - data type (if not supplied or null string, the
+;		header is searched for type,sbyte, and nbytes)
+;	sbyte - starting byte in parameter block for data
+;	nbytes - number of bytes in parameter block for data
+;
+; OUTPUT:
+;	parameter value or value(s) returned as function value
+;
+; SIDE EFFECTS:
+;	If an error occured then !err is set to -1
+;
+; OPERATIONAL NOTES:
+;	Supplying type, sbyte and nbytes greatly decreases execution
+;	time.  The best way to get the types is on the first call
+;	pass undefined variables for the three parameters or set
+;	type = ''.  The routine will then return their values for
+;	use in subsequent calls.
+;	
+; METHOD:
+;	The parameter type for parameter n is obtained
+;	from PDTYPEn keyword.  If not found then DATATYPE keyword
+;	value is used.  If that is not found then BITPIX is
+;	used.  BITPIX=8, byte; BITPIX=16 integer*2; BITPIX=32
+;	integer*4.
+;
+; HISTORY:
+;	version 1  D. Lindler  Oct. 86
+;	version 2  D. Lindler Jan. 90  added ability to process
+;		multiple parameter blocks in single call
+;	version 3  D. Lindler  (converted to New vaxidl)
+;       Apr 14 1991      JKF/ACC - fixed make_array datatypes(float/double)
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;------------------------------------------------------------
+ On_error,2
+
+ if N_params() lt 3 then $
+    message,'Syntax - result = sxgpar( h, par, name, [ type, sbyte, nbytes ])'
+;
+; determine size of output result
+;
+	s = size(par)
+	ndim = s[0]
+	dtype = s[ndim+1]
+	case 1 of
+	    (ndim eq 0) or (dtype ne 1) : begin
+			print,'SXGPAR - invalid parameter block specified'	
+			return,0
+			end
+	    ndim eq 1 : begin
+			scalar = 1	; output will be scalar
+			dimen = intarr(1)+1
+			end
+	    else: begin
+			scalar = 0	; output will be vector
+			dimen = s[2:ndim]
+		 	end
+	endcase
+	plen = s[1]		;length of parameter blocks
+;
+; check if type, sbyte and nbytes supplied
+;
+	if n_elements(type) ne 0 then if strtrim(type) ne '' then goto,bypass
+;
+; check remaining input parameters
+;
+	s=size(h)
+	!err=-1
+	if (s[0] ne 1) or (s[2] ne 7) then begin
+		print,'SXGPAR -- Header array must be string array'
+		return,0
+	end
+	if strlen(h[0]) ne 80 then begin
+		print,'SXGPAR -- header must contain 80 character strings'
+		return,0
+	end
+;
+	if n_elements(name) eq 0 then begin
+		print,'SXGPAR -- parameter name must be a scalar'
+ 		return,0
+	endif
+;
+; get number of group parameters and size
+;
+;
+	pcount=sxpar(h,'PCOUNT')	;get number of group parameters
+	if pcount eq 0 then begin
+		print,'sxgpar -- No group parameters present'
+		return,0
+	endif
+	psize=sxpar(h,'PSIZE')	;number of bits in parameter block
+	if psize eq 0 then psize=sxpar(h,'BITPIX')*pcount
+;
+; determine if name supplied or parameter number
+;
+	s=size(name)
+	if s[1] eq 7 then begin	;is it a string?
+		nam=strtrim(strupcase(name)) ;convert to upper case and trim
+;
+; search for parameter name
+;
+		for i=1,pcount do begin
+			if strtrim(sxpar(h,'PTYPE'+strtrim(i,2))) eq nam then $
+								goto,found
+		endfor
+		!err=-1
+		print,'SXGPAR -- group parameter ',name,' not found'
+		return,0
+found:
+		ipar=i
+	    end else begin		;integer
+		ipar=fix(name)
+		if ipar gt pcount then begin
+			!err=-1
+			print,'SXGPAR -- parameter number',name,' is too large'
+			print,'       -- only ',pcount,' group parameters'
+			return,0
+		endif
+	endelse
+;
+; find starting position of parameter in parameter block
+;
+	nbits=0		;number of bits to skip
+	if ipar gt 1 then begin
+		for i=1,ipar-1 do begin
+			nbit=sxpar(h,'PSIZE'+strtrim(i,2))
+			if !err lt 0 then nbit=sxpar(h,'bitpix')
+			nbits=nbits+nbit
+		endfor
+	endif
+	sbyte=nbits/8		;number of bytes to skip
+;
+; determine type of output data
+;
+	charn=strtrim(ipar,2)	;convert ipar to string
+	type=strtrim(sxpar(h,'pdtype'+charn))
+	if !err lt 0 then type=strtrim(sxpar(h,'datatype'))
+	if !err lt 0 then begin
+		case sxpar(h,'bitpix') of
+			8: type = 'BYTE'
+			16: type = 'INTEGER*2'
+			32: type = 'INTEGER*4'
+                       -32: type = 'REAL*4'
+		endcase
+	endif
+;
+; get number of bytes from type
+;
+	aster=strpos(type,'*')
+	if aster gt 0 then $
+		nbytes=fix(strmid(type,aster+1,strlen(type)-aster-1)) $
+		else nbytes=4
+
+BYPASS:		
+;-------------------------------------------------------------
+;
+; get first character of type
+;
+	c=strupcase(strmid(type,0,1))
+;
+; create output vector
+;
+	if c eq 'L' then c = 'I'	;change LOGICAL to INTEGER
+	case c of
+		'R' : if nbytes eq 8 then $
+			val = make_array(dimension=dimen,/double) $
+			else val = make_array(dimension=dimen,/float)
+		'I' : case nbytes of
+			1: val=make_array(dimension=dimen,/byte)
+			2: val=make_array(dimension=dimen,/int)
+			4: val=make_array(dimension=dimen,/long)
+		      endcase
+		'B' : val = make_array(dimension=dimen,/byte)
+		'C' : val = make_array(dimension=dimen,/string)
+		else: begin
+			print,'sxgpar -- unsupported group parameter data type'
+			!err=-1
+			return,0
+		      end
+	endcase
+	nval = n_elements(val)
+;
+; extract data
+;
+	for i=0,nval-1 do begin
+	    ssbyte = sbyte + plen*i
+   	    case c of
+		'R' : begin
+			if nbytes eq 4 then val[i]=float(par,ssbyte)
+			if nbytes eq 8 then val[i]=double(par,ssbyte)
+		      end
+		'I' : begin
+			if nbytes eq 1 then val[i]=byte(par,ssbyte)
+			if nbytes eq 2 then val[i]=fix(par,ssbyte)
+			if nbytes eq 4 then val[i]=long(par,ssbyte)
+		      end
+		'B' :val=byte(par,ssbyte,1)
+		'C' : begin
+			val[i]=string(byte(par,ssbyte,nbytes))
+		      end
+	    endcase
+	endfor
+;
+	if scalar then val=val[0]
+	!err=0
+	return,val
+end
diff --git a/Code/script_idl_mv/astrolib/sxgread.pro b/Code/script_idl_mv/astrolib/sxgread.pro
new file mode 100644
index 0000000000000000000000000000000000000000..48de9364fff300c292c1e3c99cf67690f2b1bb4a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxgread.pro
@@ -0,0 +1,55 @@
+function sxgread,unit,group
+;+
+; NAME:
+;	SXGREAD
+; PURPOSE:
+;	Read group parameters from a Space Telescope STSDAS image file     
+;
+; CALLING SEQUENCE:
+;	grouppar = sxgread( unit, group )
+;
+; INPUTS:
+;	UNIT   = Supply same unit as used in SXOPEN.
+;	GROUP  =  group number to read.  if omitted, read first group.
+;		The first group is number 0.
+;
+; OUTPUTS:
+;	GROUPPAR  =  parameter values from fits group parameter block.
+;		It is a byte array which may contain multiple data types.
+;		The function SXGPAR can be used to retrieve values from it.
+;
+; COMMON BLOCKS:
+;	Uses IDL Common STCOMMN to access parameters.
+; SIDE EFFECTS:
+;	IO is performed. 
+; MODIFICATION HISTORY:
+;	WRITTEN, Don Lindler, July, 1 1987
+;	MODIFIED, Don Neill, Jan 11, 1991 - derived from sxread.pro
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+;
+; common block containing description of file (see SXOPEN)
+;
+	common stcommn,result,filename
+;
+; check if unit open
+;
+ if (unit lt 1) or (unit gt 9) then $
+     message,'Invalid unit number, must be between 1 and 9'
+ if N_elements(result) eq 0 then result = 0
+ if (N_elements(result) ne 200) or (result[0,unit] ne 121147) then $
+        message,'Specified unit is not open'
+ desc = result[*,unit]				;description for unit
+;
+; default group number is 0 (first group)
+;
+ if N_params() eq 1 then group = 0
+;
+; read group parameters
+;
+ parrec = assoc(UNIT,bytarr(desc[7]),(group+1)*desc[9]-desc[7])
+ par = parrec[0]
+;
+ return,par
+ end
diff --git a/Code/script_idl_mv/astrolib/sxhcopy.pro b/Code/script_idl_mv/astrolib/sxhcopy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e0736c3add4ef03070b917efd8fce7b142f64fc7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxhcopy.pro
@@ -0,0 +1,85 @@
+pro sxhcopy, h, keyword1, keyword2, hout
+;+
+; NAME:
+;	SXHCOPY                            
+; PURPOSE:
+;	Copies selected portions of one header to another
+;
+; CALLING SEQUENCE:
+;	sxhcopy, h, keyword1, keyword2, hout
+;
+; INPUTS:
+;	h - input header
+;	keyword1 - first keyword to copy
+;	keyword2 - last keyword to copy
+;
+; INPUT/OUTPUT:
+;	hout - header to copy the information to.
+;
+; METHOD:
+;	the headers lines from keyword1 to keyword2 are copied to
+;	the end of the output header.  No check is made to verify
+;	that a keyword value already exists in the output header.
+;
+; HISTORY:
+;	version 1  D. Lindler    Sept. 1989
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;--------------------------------------------------------------------------
+;
+; make keywords 8 characters long (upper case)
+;
+	key1 = strmid(strupcase(keyword1+'        '),0,8)
+	key2 = strmid(strupcase(keyword2+'        '),0,8)
+;
+; get header lengths
+;
+	n = n_elements(h)
+	nout = n_elements(hout)
+;
+; find position of first keyword in h
+;
+	i1 = 0
+
+	while i1 lt n do begin
+		key = strmid(h[i1],0,8)
+		if key1 eq key then goto,found1
+		if key eq 'END     ' then begin
+			print,'SXHCOPY -- keyword '+key1+' not found in header.'
+			print,'           Nothing copied to output header.'
+			return
+		endif
+		i1 = i1+1
+	endwhile
+found1:
+;
+; find position of second keyword
+;
+	i2 = i1
+	while i2 lt n do begin
+		key = strmid(h[i2],0,8)
+		if key eq 'END     ' then begin
+			i2 = i2-1		;do not copy 'END     '
+			goto,found2
+		endif
+		if key2 eq key then goto,found2
+		i2 = i2+1
+	endwhile
+found2:
+;
+; find end of output header
+;
+	i = 0
+	while i lt nout do begin
+		if strmid(hout[i],0,8) eq 'END     ' then goto,found
+		i = i+1
+	endwhile
+	message,'No END keyword found in output header'
+found:
+;
+; create new output header
+;
+	if i gt 0 then hout=[hout[0:i-1],h[i1:i2],hout[i]] $
+		  else hout=[h[i1:i2],hout[i]]
+return
+end
diff --git a/Code/script_idl_mv/astrolib/sxhmake.pro b/Code/script_idl_mv/astrolib/sxhmake.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e45675fe326ab425c3f5da689ebc0a0ad4736ff8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxhmake.pro
@@ -0,0 +1,76 @@
+Pro sxhmake,data,groups,header
+;+
+; NAME:
+;       SXHMAKE
+; PURPOSE:
+;       Create a basic STSDAS header file from an IDL data array
+;
+; CALLING SEQUENCE:
+;       sxhmake, Data, Groups, Header
+;
+; INPUTS:
+;       Data = IDL data array of the same type, dimensions and
+;               size as are to be written to file.
+;       Groups = # of groups to be written.
+;
+; OUTPUTS:
+;       Header = String array containing ST header file.
+;
+; PROCEDURE:
+;       Call sxhmake to create a header file.  Then call sxopen to
+;       open output image, followed by sxwrite to write the data.
+;       If you do not plan to change the header created by sxhmake
+;       before calling sxopen, you might consider using sxmake which
+;       does both steps.
+;
+; MODIFICATION HISTORY:
+;       Don Lindler  Feb 1990 modified from SXMAKE by DMS, July, 1983.
+;       D. Lindler April 90  Converted to new VMS IDL
+;       M. Greason May 1990  Header creation bugs eliminated.
+;       W. Landsman Aug 1997 Use SYSTIME() instead of !STIME for V5.0 
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Recognize unsigned datatype    January 2000   W. Landsman 
+;-
+;-----------------------------------------------------------------------------
+ On_error,2
+ if N_Params() LT 3 then begin
+     print,'Syntax - sxhmake, Data, Groups, Header'
+     return
+ endif
+
+        s = size(data)                  ;obtain size of array.
+        stype = s[s[0]+1]               ;type of data.
+        if (groups eq 0) and (stype LT 6) then $
+                sxaddpar,header,'simple','T','Written by IDL:  '+ systime() $
+            else $
+                sxaddpar,header,'simple','F','Written by IDL:  '+ systime()
+
+        case stype of
+0:      message,'Data parameter is not defined'
+7:      message,"Can't write strings to ST files'
+1:      begin& bitpix= 8 & d='INTEGER*1' & endcase
+2:      begin& bitpix= 16 & d = 'INTEGER*2' & endcase
+4:      begin& bitpix= 32 & d='REAL*4' & endcase
+3:      begin& bitpix= 32 & d='INTEGER*4' & endcase
+5:      begin& bitpix= 64 & d='REAL*8' & endcase
+6:      begin& bitpix= 64 & d='COMPLEX*8' & endcase
+12:     begin & bitpix=16 & d='UNSIGNED*2' & endcase
+13:     begin & bitpix=32 & d='UNSIGNED*4' & endcase
+else:   message,'ERROR -- Unrecoginized input data type'
+        endcase
+        sxaddpar,header,'BITPIX',bitpix
+        sxaddpar,header,'NAXIS',S[0]    ;# of dimensions
+        for i=1,s[0] do sxaddpar,header,'NAXIS'+strtrim(i,2),s[i]
+        sxaddpar,header,'DATATYPE',d,'Type of data'
+        Get_date,dte                    ;Get current date as CCYY-MM-DD
+        sxaddpar,header,'DATE',dte
+        if groups eq 0 then $           ;true if not group fmt.
+                sxaddpar,header,'GROUPS','F','No groups' $
+           else begin                   ;make group params.
+                sxaddpar,header,'GROUPS','T'
+                sxaddpar,header,'PCOUNT',0
+                sxaddpar,header,'GCOUNT',groups
+                sxaddpar,header,'PSIZE',0,'# of bits in parm blk'
+           endelse
+        return
+end
diff --git a/Code/script_idl_mv/astrolib/sxhread.pro b/Code/script_idl_mv/astrolib/sxhread.pro
new file mode 100644
index 0000000000000000000000000000000000000000..43e186952d57e7c308fb6d4e967fe6c759b5d605
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxhread.pro
@@ -0,0 +1,120 @@
+pro sxhread, name, header
+;+
+; NAME:
+;       SXHREAD
+; PURPOSE:
+;       Procedure to read a STSDAS header from disk.
+; EXPLANATION:
+;       This version of SXHREAD can read two types of disk files
+;       (1)  Unix stream files with a CR after every 80 bytes
+;       (2)  Variable length record files 
+;       (3)  Fixed length (80 byte) record files
+;
+; CALLING SEQUENCE:
+;       sxhread, name, header
+;
+; INPUT:
+;       name - file name, scalar string.  An extension of .hhh is appended
+;               if not already supplied.   (Note STSDAS headers are required
+;               to have a 3 letter extension ending in 'h'.)   gzip extensions
+;               .gz will be recognized as compressed.
+; OUTPUT:
+;       header - STSDAS header, string array
+; NOTES:
+;       SXHREAD  does not do any checking to see if the file is a valid
+;       STSDAS header.    It simply reads the file into a string array with
+;       80 byte elements
+;
+; HISTORY:
+;       Version 1  D. Lindler  July, 1987
+;       Version 2  M. Greason, August 1990
+;       Use READU for certain ST VAX GEIS files   W. Landsman January, 1992
+;       Read variable length Unix files  E. Deutsch/W. Landsman November, 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Updated by E. Artigau to handle gzipped fits  August 2004
+;       Remove VMS support, W. Lnadsman September 2006
+;-
+;--------------------------------------------------------------------
+ compile_opt idl2
+ On_error,2                              ;Return to caller
+
+ if N_params() LT 2 then begin
+     print,'Syntax - SXHREAD, name, header'
+     return
+ endif
+
+; Add extension name if needed
+
+ hname = strtrim(name,2)
+ if strpos(hname,'.',strpos(hname,']') ) EQ -1 then hname = hname + '.hhh'
+ compress =  (strmid(name,strlen(name)-2,2) eq 'gz') 
+ openr, unit, hname, /GET_LUN, ERROR = err,COMPRESS = compress
+
+ if err LT 0 then goto, BADFILE
+
+ len = 80  & ai = 99                    ;Usual header length is 80 bytes
+    ;but Unix files may have an
+                                        ;embedded carriage returns to make
+   atmp = assoc(unit,bytarr(85))           ;header length 81 bytes
+   a=atmp[0] & ai=0
+   while (a[ai] ne 10) and (a[ai] ne 13) and (ai lt 84) do ai=ai+1
+   if (ai EQ 80) then len=81
+   Point_lun, unit, 0            ;Back to the beginning of the file
+
+
+
+; Get the number of lines in the header
+
+ status = fstat(unit)
+ nlines = status.size/len                      ;Number of lines in file
+ if (ai lt 80) then goto,VAR_LENGTH
+
+; Read header
+
+ header =  bytarr(len,nlines ,/NOZERO)
+ On_ioerror, VAR_LENGTH        ;READU cannot be used on variable length records
+ readu, unit, header
+ header = string(header)
+ On_ioerror,NULL
+
+ free_lun,unit             ;Close and free file unit
+
+; Trim to the END line, and delete carriage returns if necessary
+
+ endline = where( strmid(header,0,8) EQ 'END     ',nfound)
+ if nfound gt 0 then header = header[0:endline[0]] else $
+     message,'WARNING: No END statement found in header',/inform
+ if len EQ 81 then header = strmid(header,0,80)
+ return
+
+VAR_LENGTH:                 ;Now try to read as variable length records
+
+ Point_lun, unit, 0          ;Back to the beginning of file
+ h = ''  & header = strarr( nlines)
+ i = 0
+
+ On_ioerror,NOEND            ;Can't use EOF function on certain GEIS files
+ while ( strtrim( strmid(h,0,8), 2) NE 'END') do begin
+    readf, unit, h
+    if (strlen(h) LT 80) then h=h+string(replicate(32b,80-strlen(h)))
+    header[i] = h                  ;Swapped with line above 95-Aug
+    i = i + 1
+    if i EQ nlines then begin
+            header = [header,strarr(100)]
+            nlines = nlines + 100
+     endif
+ endwhile
+ header = header[0:i-1]
+ free_lun,unit
+ return
+
+NOEND:
+   message,'WARNING - No END statement found in header', /INFORM
+   free_lun,unit
+   return
+
+BADFILE:
+   message,'Error opening file ' + ' ' + hname
+   return
+
+end
diff --git a/Code/script_idl_mv/astrolib/sxhwrite.pro b/Code/script_idl_mv/astrolib/sxhwrite.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0ad4848274866bec3672d17653a1baa5c5382ce9
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxhwrite.pro
@@ -0,0 +1,95 @@
+pro sxhwrite,name,h
+;+
+; NAME:
+;       SXHWRITE
+; PURPOSE:
+;       Procedure to write an STSDAS or FITS header to disk as a *.hhh file.
+;
+; CALLING SEQUENCE:
+;       SXHWRITE,name,h
+;
+; INPUTS:
+;       name - file name. If an extension is supplied it must be 3 characters
+;               ending in "h".
+;       h - FITS header, string array
+;
+; SIDE EFFECTS:
+;       File with specified name is written.  If qualifier not specified
+;       then .hhh is used
+;   
+;       SXHWRITE will modify the header in the following ways, if necessary
+;       (1)  If not already present, an END statement is added as the 
+;               last line.   Lines after an existing END statment are
+;               deleted.
+;       (2)  Spaces are appended to force each line to be 80 characters.
+;       (3)  On Unix machines, a carriage return is appended at the end
+;               of each line.   This is consistent with STSDAS and allows
+;               the file to be directly displayed on a stream device
+;
+; PROCEDURES USED:
+;       zparcheck, fdecomp
+; HISTORY:
+;       version 1  D. Lindler  June 1987
+;       conversion cleaned up.  M. Greason, June 1990
+;       Add carriage return at the end of Unix files   W. Landsman Oct 1991
+;       Use SYSTIME() instead of !STIME for V5.0 compatibility Aug 1997
+;       Assume since V55, remove VMS support
+;-
+;----------------------------------------------------------------
+ compile_opt idl2
+ On_error,2 
+ if N_params() LT 2 then begin
+    print,'Syntax - SXHWRITE, name, hdr'
+    return
+ endif
+
+; Create output file name
+
+ ZPARCHECK, 'SXHWRITE', name, 1, 7, 0, 'Disk file name'  ;Check for valid param
+ FDECOMP,name, disk, dir, file, qual
+ if ( qual EQ '' ) then qual = 'hhh'                    ;default qualifier
+
+; Check for valid qualifier
+
+ if ( strlen(qual) NE 3 ) || ( strupcase(strmid(qual,2,1)) NE 'H' ) then $
+        message,'Qualifier on file name must be 3 characters, ending in h'
+
+ hname = disk + dir + file + '.' + qual           ;header file name
+
+; Check that valid FITS header was supplied
+
+ ZPARCHECK, 'SXHWRITE', h, 2, 7, 1, 'FITS header'
+
+ sxdelpar,'XTENSION',h       ;For SDAS header SIMPLE must be the first line
+ SXADDPAR, h, 'SIMPLE', 'F', ' Written by IDL:  ' + systime()
+
+; Determine if an END line occurs, and add one if necessary
+
+ endline = where( strtrim( strmid(h,0,8), 2) EQ 'END', Nend)
+ if Nend EQ 0 then begin
+
+    message, /INF, $
+        'WARNING - An END statement has been appended to the FITS header'
+    h = [ h, 'END' + string( replicate(32b,77) ) ]
+    endline = N_elements(h) - 1 
+
+ endif
+ nmax = endline[0] + 1
+
+; Convert to byte and force into 80 character lines
+
+ temp = replicate( 32b, 80, nmax)
+ for n = 0, endline[0] do temp[0,n] = byte( h[n] )
+
+; Under Unix append a carriage return ( = string(10b) )
+
+ temp = [ temp, rotate( replicate(10b,nmax), 1 ) ]
+
+; Open the output file and write as byte array.
+
+ openw, unit, hname, 80, /GET_LUN
+ writeu, unit, temp
+ free_lun,unit
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/sxmake.pro b/Code/script_idl_mv/astrolib/sxmake.pro
new file mode 100644
index 0000000000000000000000000000000000000000..54fc315fb3d16960a46c146258000ba3378a5f61
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxmake.pro
@@ -0,0 +1,128 @@
+Pro sxmake, unit, File, Data, Par, Groups, Header, PSIZE = psize
+;+
+; NAME:
+;       SXMAKE
+; PURPOSE:
+;       Create a basic ST header file from an IDL array prior to writing data.
+;
+; CALLING SEQUENCE:
+;       sxmake, Unit, File, Data, Par, Groups, Header, [ PSIZE = ]
+;
+; INPUTS:
+;       Unit = Logical unit number from 1 to 9.
+;       File = file name of data and header files to create.   If no file name
+;              extension is supplied then the default is to use .hhh for the
+;              header file extension and .hhd for the data file extension    
+;              If an extension is supplied, it should be of the form .xxh
+;              where xx are any alphanumeric characters.
+;       Data = IDL data array of the same type, dimensions and
+;               size as are to be written to file.
+;       Par = # of elements in each parameter block for each data record.  If 
+;             set equal to 0, then parameter blocks will not be written.  The 
+;             data type of the parameter blocks must be the same as the data 
+;             array.   To get around this restriction, use the PSIZE keyword.
+;       Groups = # of groups to write.  If 0 then write in basic
+;               format without groups.  
+;
+; OPTIONAL INPUT PARAMETERS:
+;       Header = String array containing ST header file.  If this
+;               parameter is omitted, a basic header is constructed.
+;               If included, the basic parameters are added to the
+;               header using sxaddpar.  The END keyword must terminate
+;               the parameters in Header.
+;
+; OPTIONAL KEYWORD INPUT PARAMETER:
+;        PSIZE - Integer scalar giving the number of bits in the parameter 
+;               block.    If the PSIZE keyword is given, then the Par input
+;               parameter is ignored.
+;                
+; OPTIONAL OUTPUT PARAMETERS:
+;       Header = ST header array, an 80 by N character array.
+;
+; COMMON BLOCKS:
+;       Stcommn - as used in sxwrite, sxopen, etc.
+;
+; SIDE EFFECTS:
+;       The header file is created and written and then the
+;       data file is opened on the designated unit.
+;
+; RESTRICTIONS:
+;       Header files must be named .xxh and data files must be
+;       named .xxd, where xx are any alphanumeric characters.
+;
+; PROCEDURE:
+;       Call sxmake to create a header file.  Then call sxwrite
+;       to output each group.
+; 
+; PROCEDURES USED:
+;       GET_DATE, SXADDPAR, SXOPEN
+; MODIFICATION HISTORY:
+;       DMS, July, 1983.
+;       converted to new VMS IDL  April 90
+;       Use SYSTIME() instead of !STIME   W. Landsman   Aug 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added optional PSIZE keyword   August 1999 W. Landsman 
+;       Recognize unsigned datatype    January 2000   W. Landsman 
+;-
+        common stcommn, result, filename
+;
+        if N_params() LT 2 then begin
+           print,'Syntax - SXMAKE,unit,file,data,par,groups,header, [PSIZE = ]'
+           return
+        endif
+;
+        if N_elements(result) ne 200 then begin
+                result = lonarr(20,10)  ;define common blks
+                filename = strarr(10)
+                endif
+;
+        if (unit lt 1) or (unit gt 9) then $  ;unit ok?
+                message,'Unit number must be from 1 to 9.'
+;
+        close,unit
+        result[unit,*]=0
+;
+        if N_elements(par) EQ 0 then par = 0
+        if N_elements(groups) EQ 0 then groups = 0
+;
+        s = size(data)                  ;obtain size of array.
+        stype = s[s[0]+1]               ;type of data.
+        if (par eq 0) and (groups eq 0) and (stype LT 6) then $
+                sxaddpar,header,'simple','T','Written by IDL:  '+ systime() $
+            else $
+                sxaddpar,header,'simple','F','Written by IDL:  '+ systime()
+        case stype of
+0:      message,'Data parameter is not defined'
+7:      message,"Can't write strings to ST files"
+1:      begin& bitpix=  8 & d = 'INTEGER*1' & endcase
+2:      begin& bitpix= 16 & d = 'INTEGER*2' & endcase
+4:      begin& bitpix= 32 & d = 'REAL*4' & endcase
+3:      begin& bitpix= 32 & d = 'INTEGER*4' & endcase
+5:      begin& bitpix= 64 & d = 'REAL*8' & endcase
+6:      begin& bitpix= 64 & d = 'COMPLEX*8' & endcase
+12:     begin & bitpix=16 & d='UNSIGNED*2' & endcase
+13:     begin & bitpix=32 & d='UNSIGNED*4' & endcase
+else:   message,'ERROR -- Unrecognized input data type'
+
+        endcase
+;
+        sxaddpar,header,'BITPIX',bitpix
+        sxaddpar,header,'NAXIS',S[0]    ;# of dimensions
+        for i=1,s[0] do sxaddpar,header,'NAXIS'+strtrim(i,2),s[i]
+        sxaddpar,header,'DATATYPE',d,'Type of data'
+        Get_date,dte
+        sxaddpar,header,'DATE',dte
+;
+        if groups eq 0 then $           ;true if not group fmt.
+                sxaddpar,header,'GROUPS','F','No groups' $
+           else begin                   ;make group params.
+                sxaddpar,header,'GROUPS','T'
+                sxaddpar,header,'PCOUNT',par
+                sxaddpar,header,'GCOUNT',groups
+                if N_elements(psize) EQ 0 then psize = bitpix*par
+                sxaddpar,header,'PSIZE',psize,'# of bits in parm blk'
+           endelse
+;
+        sxopen,unit,file,header,hist,'W' ;make header file, etc.
+        return
+end
diff --git a/Code/script_idl_mv/astrolib/sxopen.pro b/Code/script_idl_mv/astrolib/sxopen.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f47908ff230374a393726d2e665a27ee7074bd55
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxopen.pro
@@ -0,0 +1,213 @@
+pro SXOPEN,unit,fname,header,history,access
+;+
+; NAME:
+;       SXOPEN
+; PURPOSE:
+;       Open a Space Telescope formatted (STSDAS) header file.
+; EXPLANATION:
+;       Saves the parameters required subsequent SX routines in
+;       the common block Stcommn.  Optionally save the header in 
+;       the string array Header, and the history in the string array
+;       History.  Open the data file associated with this
+;       header on the same unit.
+;
+; CALLING SEQUENCE:
+;       SXOPEN, Unit, Fname [, Header [,History] [,Access]]
+;
+; INPUTS:
+;       Unit = IDL unit used for IO.  Must be from 1 to 9.
+;       Fname = File name of header file.  Default extension
+;               is .hhh for header files and .hhd for data
+;               files.    If an extension is supplied it must have the 
+;               form .xxh where xx are any alphanumeric characters. The
+;               data file must have extension .xxd
+;               No version number is allowed.  Most recent versions
+;               of the files are used.
+;
+; OPTIONAL INPUT PARAMETER:
+;       Access = 'R' to open for read, 'W' to open for write.
+;
+; OUTPUTS:
+;       Stcommn = Common block containing ST parameter blocks.
+;               (Long arrays.)
+;
+; OPTIONAL OUTPUT PARAMETERS:
+;       Header = 80 char by N string array containing the
+;               names, values and comments from the FITS header.
+;               Use the function SXPAR to obtain individual
+;               parameter values.
+;       History = String array containing the value of the
+;               history parameter.
+;
+; COMMON BLOCKS:
+;       STCOMMN - Contains RESULT(20,10) where RESULT(i,LUN) =
+;       0 - 121147 for consistency check, 1 - Unit for consistency,
+;       2 - bitpix, 3 - naxis, 4 - groups (0 or 1), 5 - pcount,
+;       6 - gcount, 7 - psize, 8 - data type as idl type code,
+;       9 - bytes / record, 10 to 10+N-1 - dimension N,
+;       17 = record length of file in bytes.
+;       18 - # of groups written, 19 = gcount.
+;
+; SIDE EFFECTS:
+;       The data and header files are accessed.
+;
+; RESTRICTIONS:
+;       Works only for disc files.  The data file must have
+;       must have the extension ".xxd" and the header file must
+;       have the extension ".xxh" where x is any alphanumeric character
+;
+; PROCEDURE:
+;       The header file is opened and each line is read.
+;       Important parameters are stored in the output
+;       parameter.  If the last two parameters are specified
+;       the parameter names and values are stored.  The common
+;       block STCOMMN is filled with the type of data, dimensions,
+;       etc. for use by SXREAD.
+;
+;       If access is for write, each element of the header
+;       array, which must be supplied, is written to the
+;       header file.  The common block is filled with
+;       relevant parameters for SXWRITE.  A keyword of "END"
+;       ends the header.
+;
+; MODIFICATION HISTORY:
+;       Written, DMS, May, 1983.
+;       D. Lindler Feb. 1990
+;               Modified to allow var. record length header files.
+;       D. Lindler April 1990   Conversion to new VMS IDL
+;       Added /BLOCK when opening new .hhd file
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Recognize unsigned datatype for V5.1 or greater   W. Landsman Jan 2000
+;       Assume since V5.5  W. Landsman Sep 2006
+;-
+;------------------------------------------------------------------------------
+        On_error,2
+        common stcommn,result,filename
+;
+     if N_params() LT 2 then begin
+         print, 'Syntax: SXOPEN, unit, fname, [ header, history, access]'
+         return
+     endif
+;
+        if N_elements(result) NE 200 then begin ;defined?
+                result = lonarr(20,10)
+                filename = strarr(10)
+                endif
+;
+        if (unit lt 1) OR (unit gt 9) then $
+                message,'Unit number must be from 1 to 9.'
+;
+        close,unit              ;close unit first
+;
+        n = N_params(0)              ;# of parameters we have
+        if n LT 5 then access = 'R'   ;read access if unspecified
+;
+; Add default extension (.hhh) if not specified       
+;
+        xname=strtrim(fname,2)
+        if strmid(xname,strlen(xname)-4,1) NE '.' then xname = xname + '.hhh'
+        t=xname                         ;Open keywords.
+        CASE strupcase(access) OF
+'R':    sxhread,fname,header               ;Read FITS header
+'W':    sxhwrite,fname,header              ;Write FITS header
+ELSE:   message,'Illegal access value, must be R or W'
+        ENDCASE
+;
+        result[*,unit]=0        ;Zero our block     
+        filename[unit]=fname    ;Save file name   
+        result[0,unit]=121147L  ;Code for descr block   
+        result[1,unit] = unit   ;Save unit number    
+        result[6,unit]=1        ;Default value of GCOUNT is 1
+;
+; Get keyword names and values from header array
+;
+ name =  strtrim(strmid(header,0,8),2)   ;param name
+ value = strtrim(strmid(header,10,20),2) ;param value
+;
+ L_bitpix = where(name EQ 'BITPIX',nfound)
+      if nfound GT 0 then result[2,unit] = value[L_bitpix[0]] else $
+       message,'Required Keyword BITPIX not found',/CON
+;
+ l_naxis = where(strmid(name,0,5) EQ 'NAXIS',nfound)         
+      IF nfound GT 0 then BEGIN
+           axis = fix(strtrim(strmid(name[l_naxis],5,3),2))
+           for i=0,nfound-1 do begin
+                if axis[i] EQ 0 then  $
+                       result[3,unit]=value[l_naxis[i]] else  $  ;# of dimensions
+                       result[9+axis[i],unit]=value[l_naxis[i]] ;each dimension
+            endfor
+       endif else message,'Required Keyword NAXIS not found'
+;           
+ if n GE 4 then BEGIN                ;Create history parameter?
+   L_hist = where(name EQ 'HISTORY',nfound)  
+   IF nfound then history = strtrim(strmid(header[l_hist],8,72),2) else $
+                  history = ''  
+ENDIF
+;
+ L_groups = where(name EQ 'GROUPS',nfound)
+   if nfound GT 0 then result[4,unit] = value[L_groups[0]] eq 'T'
+;
+ L_pcount = where(name EQ 'PCOUNT',nfound)
+   if nfound GT 0 then result[5,unit] = value[L_pcount[0]]
+;
+ L_gcount = where(name EQ 'GCOUNT',nfound)
+if nfound GT 0 then result[6,unit] = value[L_gcount[0]]
+;
+ L_psize = where(name EQ 'PSIZE',nfound)
+ if nfound GT 0 then result[7,unit] = value[L_psize[0]]/8 $
+               else result[7,unit] = result[5,unit]*result[2,unit]
+;
+ L_datatype = where(name EQ 'DATATYPE',nfound)
+ if nfound GT 0 then begin 
+                v = value[L_datatype[0]]      ;Process data type.
+                v = strmid(v,1,strlen(v)-2)   ;Remove apostrophes
+                v = strtrim(v,2)                    ;trim blanks
+                CASE v OF       ;Cvt datatype to IDL type code    
+                'BYTE':                 result[8,unit]=1
+                'LOGICAL*1':            result[8,unit]=1        ;Byte
+                'INTEGER*1':            result[8,unit]=1
+                'REAL*4':               result[8,unit]=4
+                'INTEGER*2':            result[8,unit]=2
+                'UNSIGNED*2':           result[8,unit]=12
+                'INTEGER*4':            result[8,unit]=3
+                'UNSIGNED*4':           result[8,unit]=13 
+                'REAL*8':               result[8,unit]=5
+                'COMPLEX*8':            result[8,unit]=6
+                ELSE:                   message,'Undefined Datatype value'
+                ENDCASE         ;V OF
+ endif                       ;DATATYPE
+;
+;
+; If DATATYPE not specified assume integer of size specified by BITPIX
+;
+        if result[8,unit] EQ 0 then begin
+                CASE result[2,unit] OF
+                        8: result[8,unit]=1             ;byte
+                       16: result[8,unit]=2             ;integer*2
+                       32: result[8,unit]=3             ;integer*4
+                      -32: result[8,unit]=4
+                      -64: result[8,unit]=5
+                     else: message,'Unable to determine data type'
+                ENDCASE
+        endif
+;      
+        bytes = abs(result[2,unit])/8l  ;bytes/datum
+        for j=1,result[3,unit] do $     ;accum bytes/record
+                        bytes=bytes*result[9+j,unit]
+        bytes = bytes + result[7,unit]     ;+ header.
+        result[9,unit]=bytes               ;Save bytes/record. 
+;
+        xname=strmid(xname,0,strlen(xname)-1)+'d'   ;Change to data filename  
+;
+        If result[3,unit] GT 0 then begin      ;NAXIS non-zero?
+          close,unit
+          if strupcase(access) eq 'R' then $
+                openr,unit,xname  $
+          else begin
+                nrecs = (result[6,unit]*result[9,unit]+511)/512
+                openw, unit, xname
+          endelse
+        result[17,unit] = 512           ;Save record length    
+        endif else result[17,unit]=0    ;NAXIS = 0
+        return
+end  
diff --git a/Code/script_idl_mv/astrolib/sxpar.pro b/Code/script_idl_mv/astrolib/sxpar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d137cf4d49c584647624ab9fb85db0024998a13d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxpar.pro
@@ -0,0 +1,404 @@
+function SXPAR, hdr, name, abort, COUNT=matches, COMMENT = comments, $
+                IFound = number, NoContinue = NoContinue, SILENT = silent, $
+                NULL = K_Null, NAN = NaN, MISSING = Missing
+;+
+; NAME:
+;      SXPAR
+; PURPOSE:
+;      Obtain the value of a parameter in a FITS header
+;
+; CALLING SEQUENCE:
+;      result = SXPAR( Hdr, Name, [ Abort, COUNT=, COMMENT =, /NoCONTINUE, 
+;                                           /SILENT  ])   
+;
+; INPUTS:
+;      Hdr =  FITS header array, (e.g. as returned by READFITS) 
+;             string array, each element should have a length of 80 characters      
+;
+;      Name = String name of the parameter to return.   If Name is of the
+;             form 'keyword*' then an array is returned containing values of
+;             keywordN where N is a positive (non-zero) integer.  The value of 
+;             keywordN will be placed in RESULT[N-1].  The data type of RESULT 
+;             will be the type of the first valid match of keywordN found.
+;
+; OPTIONAL INPUTS:
+;       ABORT - string specifying that SXPAR should do a RETALL
+;               if a parameter is not found.  ABORT should contain
+;               a string to be printed if the keyword parameter is not found.
+;               If not supplied, SXPAR will return quietly with COUNT = 0
+;               (and !ERR = -1) if a keyword is not found.
+;
+; OPTIONAL INPUT KEYWORDS: 
+;       /NOCONTINUE = If set, then continuation lines will not be read, even
+;                 if present in the header
+;       /SILENT - Set this keyword to suppress warning messages about duplicate
+;                 keywords in the FITS header.
+;       MISSING = By default, this routine returns 0 when keyword values are
+;                 not found.  This can be overridden by using the MISSING
+;                 keyword, e.g. MISSING=-1.
+;       /NAN    = If set, then return Not-a-Number (!values.f_nan) for missing
+;                 values.  Ignored if keyword MISSING is present.
+;       /NULL   = If set, then return !NULL (undefined) for missing values.
+;                 Ignored if MISSING of /NAN is present, or if earlier than IDL
+;                 version 8.0.  If multiple values would be returned, then
+;                 MISSING= or /NAN should be used instead of /NULL, making sure
+;                 that the datatype is consistent with the non-missing values,
+;                 e.g. MISSING='' for strings, MISSING=-1 for integers, or
+;                 MISSING=-1.0 or /NAN for floating point.  /NAN should not be
+;                 used if the datatype would otherwise be integer.
+;
+; OPTIONAL OUTPUT KEYWORDS:
+;       COUNT - Optional keyword to return a value equal to the number of 
+;               parameters found by SXPAR, integer scalar
+;
+;       COMMENT - Array of comments associated with the returned values
+;       IFOUND - Array of found keyword indicies when Name is of the form keyword*
+;              For example, one searches for 'TUNIT*' and the FITS header contains
+;              TUNIT1, TUNIT2, TUNIT4, and TUNIT6 then IFOUND woud be returned as
+;              [1,2,4,6].    Set to zero if Name is not of the form keyword*.
+
+;
+; OUTPUTS:
+;       Function value = value of parameter in header.
+;               If parameter is double precision, floating, long or string,
+;               the result is of that type.  Apostrophes are stripped
+;               from strings.  If the parameter is logical, 1b is
+;               returned for T, and 0b is returned for F.
+;               If Name was of form 'keyword*' then a vector of values
+;               are returned.
+;
+; SIDE EFFECTS:
+;       !ERR is set to -1 if parameter not found, 0 for a scalar
+;       value returned.  If a vector is returned it is set to the
+;       number of keyword matches found.    The use of !ERR is deprecated, and
+;       instead the COUNT keyword is preferred
+;
+;       If a keyword (except HISTORY or COMMENT) occurs more than once in a 
+;       header, a warning is given, and the *last* occurrence is used.
+;
+; EXAMPLES:
+;       Given a FITS header, h, return the values of all the NAXISi values
+;       into a vector.    Then place the history records into a string vector.
+;
+;       IDL> naxisi = sxpar( h ,'NAXIS*')         ; Extract NAXISi value
+;       IDL> history = sxpar( h, 'HISTORY' )      ; Extract HISTORY records
+;
+; PROCEDURE:
+;       The first 8 chacters of each element of Hdr are searched for a 
+;       match to Name.  The value from the last 20 characters is returned.  
+;       An error occurs if there is no parameter with the given name.
+;
+;       If a numeric value has no decimal point it is returned as type
+;       LONG.   If it contains more than 8 numerals, or contains the 
+;       characters 'D' or 'E', then it is returned as type DOUBLE.  Otherwise
+;       it is returned as type FLOAT.    Very large integer values, outside
+;       the range of valid LONG, are returned as DOUBLE.
+;
+;       If the value is too long for one line, it may be continued on to the
+;       the next input card, using the OGIP CONTINUE convention.  For more info,
+;       see http://fits.gsfc.nasa.gov/registry/continue_keyword.html
+;
+;       Complex numbers are recognized as two numbers separated by one or more
+;       space characters.
+;
+;       If a numeric value has no decimal point (or E or D) it is returned as
+;       type LONG.  If it contains more than 8 numerals, or contains the
+;       character 'D', then it is returned as type DOUBLE.  Otherwise it is
+;       returned as type FLOAT.    If an integer is too large to be stored as
+;       type LONG, then it is returned as DOUBLE.
+;
+; NOTES:
+;       The functions SXPAR() and FXPAR() are nearly identical, although
+;       FXPAR() has slightly more sophisticated parsing, and additional keywords
+;       to specify positions in the header to search (for speed), and to force
+;       the output to a specified data type..   There is no
+;       particular reason for having two nearly identical procedures, but
+;       both are too widely used to drop either one.
+;
+; PROCEDURES CALLED:
+;       cgErrorMsg(), GETTOK(), VALID_NUM()
+; MODIFICATION HISTORY:
+;       DMS, May, 1983, STPAR Written.
+;       D. Lindler Jan 90 added ABORT input parameter
+;       J. Isensee Jul,90 added COUNT keyword
+;       W. Thompson, Feb. 1992, added support for FITS complex values.
+;       W. Thompson, May 1992, corrected problem with HISTORY/COMMENT/blank
+;               keywords, and complex value error correction.
+;       W. Landsman, November 1994, fix case where NAME is an empty string 
+;       W. Landsman, March 1995,  Added COMMENT keyword, ability to read
+;               values longer than 20 character
+;       W. Landsman, July 1995, Removed /NOZERO from MAKE_ARRAY call
+;       T. Beck May 1998, Return logical as type BYTE
+;       W. Landsman May 1998, Make sure integer values are within range of LONG
+;       W. Landsman Feb 1998, Recognize CONTINUE convention 
+;       W. Landsman Oct 1999, Recognize numbers such as 1E-10 as floating point
+;       W. Landsman Jan 2000, Only accept integer N values when name = keywordN
+;       W. Landsman Dec 2001, Optional /SILENT keyword to suppress warnings
+;       W. Landsman/D. Finkbeiner  Mar 2002  Make sure extracted vectors 
+;             of mixed data type are returned with the highest type.
+;       W.Landsman Aug 2008  Use vector form of VALID_NUM()
+;       W. Landsman Jul 2009  Eliminate internal recursive call
+;       W. Landsman Apr 2012  Require vector numbers be greater than 0
+;       W. Landsman Apr 2014  Don't convert Long64 numbers to double
+;       W. Landsman Nov 2014  Use cgErrorMsg rather than On_error,2
+;       W. Landsman Dec 2014  Return Logical as IDL Boolean in IDL 8.4 or later
+;       W. Landsman May 2015  Added IFound output keyword
+;       J. Slavin Aug 2015 Allow for 72 character par values (fixed from 71)
+;       W. Landsman Sep 2015  Added Missing, /NULL and /NaN keywords 
+;-
+;----------------------------------------------------------------------
+ compile_opt idl2
+
+ if N_params() LT 2 then begin
+     print,'Syntax -  result =  sxpar( hdr, name, [abort])'
+     print,'   Input Keywords:    /NOCONTINUE, /SILENT, MISSING=, /NAN, /NULL'
+     print,'   Output Keywords:   COUNT=,  COMMENT= '
+     return, -1
+ endif 
+ 
+ ;
+;  Determine the default value for missing data.
+;
+        CASE 1 OF 
+            N_ELEMENTS(MISSING) EQ 1: MISSING_VALUE = MISSING
+            KEYWORD_SET(NAN): MISSING_VALUE = !VALUES.F_NAN
+            KEYWORD_SET(K_NULL) AND !VERSION.RELEASE GE '8.': $
+              DUMMY = EXECUTE('MISSING_VALUE = !NULL')
+            ELSE: MISSING_VALUE = 0
+        ENDCASE
+        VALUE = MISSING_VALUE
+;
+ 
+ VALUE = 0
+ if N_params() LE 2 then begin
+      abort_return = 0
+      abort = 'FITS Header'
+ end else abort_return = 1
+ if abort_return then On_error,1 else begin
+      Catch, theError
+      if theError NE 0 then begin
+           Catch,/Cancel
+	   void = cgErrorMsg(/quiet)
+	   return,-1
+	   endif
+   endelse
+;       Check for valid header
+
+;Check header for proper attributes.
+  if ( size(hdr,/N_dimen) NE 1 ) || ( size(hdr,/type) NE 7 ) then $
+           message,'FITS Header (first parameter) must be a string array'
+
+  nam = strtrim( strupcase(name) )      ;Copy name, make upper case     
+
+
+;  Determine if NAME is of form 'keyword*'.  If so, then strip off the '*', and
+;  set the VECTOR flag.  One must consider the possibility that NAM is an empty
+;  string.
+
+   namelength1 = (strlen(nam) - 1 ) > 1         
+   if strpos( nam, '*' ) EQ namelength1 then begin    
+            nam = strmid( nam, 0, namelength1)  
+            vector = 1                  ;Flag for vector output  
+            name_length = strlen(nam)   ;Length of name 
+            num_length = 8 - name_length        ;Max length of number portion  
+            if num_length LE 0 then  $ 
+                  message, 'Keyword length must be 8 characters or less'
+
+;  Otherwise, extend NAME with blanks to eight characters.
+
+    endif else begin  
+                while strlen(nam) LT 8 do nam += ' ' ;Make 8 chars long
+                vector = 0      
+    endelse
+
+
+;  If of the form 'keyword*', then find all instances of 'keyword' followed by
+;  a number.  Store the positions of the located keywords in NFOUND, and the
+;  value of the number field in NUMBER.
+
+        histnam = (nam eq 'HISTORY ') || (nam eq 'COMMENT ') || (nam eq '') 
+        keyword = strmid( hdr, 0, 8)
+	number = 0
+ 
+        if vector then begin
+            nfound = where(strpos(keyword,nam) GE 0, matches)
+            if  matches GT 0  then begin
+                numst= strmid( hdr[nfound], name_length, num_length)
+		igood = where(VALID_NUM(numst,/INTEGER), matches)
+		if matches GT 0 then begin 
+		     nfound = nfound[igood]
+                     number = long(numst[igood])
+		     g = where(number GT 0, matches)
+ 		     if matches GT 0 then number = number[g]
+
+		endif 
+           endif
+
+;  Otherwise, find all the instances of the requested keyword.  If more than
+;  one is found, and NAME is not one of the special cases, then print an error
+;  message.
+
+        endif else begin
+            nfound = where(keyword EQ nam, matches)
+             if (matches GT 1) && ~histnam then        $
+                if ~keyword_set(silent) then $
+                message,/informational, 'Warning - keyword ' +   $
+                nam + ' located more than once in ' + abort
+        endelse
+
+
+; Process string parameter 
+
+ if matches GT 0 then begin
+  line = hdr[nfound]
+  svalue = strtrim( strmid(line,9,71),2)
+  if histnam then $
+       value = strtrim(strmid(line,8,72),2) else for i = 0,matches-1 do begin
+      if ( strmid(svalue[i],0,1) EQ "'" ) then begin   ;Is it a string?
+                  test = strmid( svalue[i],1,strlen( svalue[i] )-1)
+                  next_char = 0
+                  off = 0
+                  value = '' 
+          NEXT_APOST:
+                  endap = strpos(test, "'", next_char)      ;Ending apostrophe  
+                  if endap LT 0 then $ 
+                            MESSAGE,'Value of '+name+' invalid in '+abort
+                  value += strmid( test, next_char, endap-next_char )  
+
+;  Test to see if the next character is also an apostrophe.  If so, then the
+;  string isn't completed yet.  Apostrophes in the text string are signalled as
+;  two apostrophes in a row.
+
+                 if strmid( test, endap+1, 1) EQ "'" then begin    
+                    value += "'"
+                    next_char = endap+2         
+                    goto, NEXT_APOST
+                 endif      
+
+; Extract the comment, if any
+                
+                slash = strpos( test, "/", endap )
+                if slash LT 0 then comment = '' else    $
+                        comment = strmid( test, slash+1, strlen(test)-slash-1 )
+
+; This is a string that could be continued on the next line.  Check this
+; possibility with the following four criteria: *1) Ends with '&'
+; (2) Next line is CONTINUE  (3) LONGSTRN keyword is present (recursive call to
+; SXPAR) 4. /NOCONTINE is not set
+
+    if ~keyword_set(nocontinue) then begin
+                off++
+                val = strtrim(value,2)
+
+                if (strlen(val) gt 0) && $
+                  (strmid(val, strlen(val)-1, 1) EQ '&') && $
+                  (strmid(hdr[nfound[i]+off],0,8) EQ 'CONTINUE') then $
+		      if ~array_equal(keyword EQ 'LONGSTRN',0b) then begin 
+                  value = strmid(val, 0, strlen(val)-1)
+                  test = hdr[nfound[i]+off]
+                  test = strmid(test, 8, strlen(test)-8)
+                  test = strtrim(test, 2)
+                  if strmid(test, 0, 1) NE "'" then message, $
+                    'ERROR: Invalidly CONTINUEd string in '+ abort
+                  next_char = 1
+                  GOTO, NEXT_APOST
+                ENDIF
+    ENDIF
+
+
+; Process non-string value  
+
+          endif else begin
+               value = missing_value
+               test = svalue[i]
+               if test EQ '' then begin
+                        comment = ''
+                        GOTO, got_value
+                endif
+                slash = strpos( test, "/" )
+                if slash GE 0 then begin
+                        comment = strmid( test, slash+1, strlen(test)-slash-1 )
+                        if slash GT 0 then test = strmid(test, 0, slash) else $
+                            GOTO, got_value
+                endif else comment = ''
+
+; Find the first word in TEST.  Is it a logical value ('T' or 'F') ?
+
+                test2 = test
+                value = gettok(test2,' ')
+                true = 1b
+                false = 0b
+                if !VERSION.RELEASE GE 8.4 then begin
+                	true =  boolean(true) 
+                	false = boolean(false)
+               endif 
+
+               if ( value EQ 'T' ) then value = true else $
+               if ( value EQ 'F' ) then value = false else begin
+
+;  Test to see if a complex number.  It's  a complex number if the value and
+;  the next word, if any, are both valid values.
+
+                if strlen(test2) EQ 0 then goto, NOT_COMPLEX
+                value2 = gettok( test2, ' ') 
+                if value2 EQ '' then goto, NOT_COMPLEX
+                On_ioerror, NOT_COMPLEX
+                value2 = float(value2)
+                value = complex(value,value2)
+                goto, GOT_VALUE
+
+;  Not a complex number.  Decide if it is a floating point, double precision,
+;  or integer number.
+
+NOT_COMPLEX:
+                On_IOerror, GOT_VALUE
+                  if (strpos(value,'.') GE 0) || (strpos(value,'E') GT 0) $
+                  || (strpos(value,'D') GE 0) then begin  ;Floating or double?
+                      if ( strpos(value,'D') GT 0 ) || $  ;Double?
+                         ( strlen(value) GE 8 ) then value = double(value) $
+                                                else value = float(value)
+                       endif else begin                   ;Long integer
+                            lmax = 2.0d^31 - 1.0d
+                            lmin = -2.0d^31      ;Typo fixed Feb 2010
+                            value = long64(value)
+                            if (value GE lmin) && (value LE lmax) then $
+                                value = long(value) 
+                       endelse
+
+GOT_VALUE:
+                On_IOerror, NULL
+                endelse
+             endelse; if c eq apost
+
+;  Add to vector if required
+
+         if vector then begin
+               if ( i EQ 0 ) then begin
+                     maxnum = max(number)
+                     dtype = size(value,/type)
+                     result = make_array( maxnum, TYPE = dtype )
+                     comments = strarr( maxnum )
+               endif 
+               if size(value,/type) GT dtype then begin   ;Do we need to recast?
+                    result = result + 0*value
+                    dtype = size(value,/type)
+               endif
+               result[ number[i]-1 ] =  value
+               comments[ number[i]-1 ] = comment
+          endif else $
+                comments = comment
+  endfor
+
+  if vector then begin
+         !ERR = matches     
+         return, result
+  endif else !ERR = 0
+
+endif  else  begin    
+     if abort_return then message,'Keyword '+nam+' not found in '+abort
+     !ERR = -1
+endelse     
+
+return, value       
+
+END                 
diff --git a/Code/script_idl_mv/astrolib/sxread.pro b/Code/script_idl_mv/astrolib/sxread.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4a255ef1c2c1c66f0dcd5535b30981c2f4a47f35
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxread.pro
@@ -0,0 +1,81 @@
+function sxread,unit,group,par
+;+
+; NAME:
+;	SXREAD
+; PURPOSE:
+;	Read a Space Telescope STSDAS image file     
+;
+; CALLING SEQUENCE:
+;	result = sxread( Unit, group , [par] )
+;
+; INPUTS:
+;	UNIT  =  Unit number of file, must be from 1 to 9.
+;		Unit must have been opened with SXOPEN.
+;	GROUP  =  group number to read.  if omitted, read first record.
+;		The first record is number 0.
+; OUTPUTS:
+;	Result of function  =  array constructed from designated record.
+;
+; OPTIONAL OUTPUT:
+;	PAR  =  Variable name into which parameter values from STSDAS
+;		group parameter block are read.  It is a byte array
+;		which may contain multiple data types.  The function
+;		SXGPAR can be used to retrieve values from it.
+;
+; COMMON BLOCKS:
+;	Uses IDL Common STCOMMN to access parameters.
+;
+; NOTES:
+;	Use the function SXGREAD to read the group parameter blocks without
+;	having to read the group array.
+;
+;	If the STSDAS file does not contain groups, then the optional output
+;	parameter PAR is returned undefined, but no error message is given.
+;
+; SIDE EFFECTS:
+;	IO is performed. 
+; MODIFICATION HISTORY:
+;	WRITTEN, Don Lindler, July, 1 1987
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+ On_error,2
+
+; common block containing description of file (see SXOPEN)
+
+	common stcommn,result,filename
+
+; check if unit open
+
+ if ( unit LT 1 ) or ( unit GT 9 ) then $
+     message,'Invalid unit number, must be between 1 and 9'
+
+ if N_elements(result) EQ 0 then result = 0
+
+ if ( N_elements(result) NE 200 ) or ( result[0,unit] NE 121147 ) then $
+        message,'Specified unit is not open'
+
+ desc = result[*,unit]				;description for unit
+
+; default group number is 0 (first group)
+
+ if N_params() eq 1 then group = 0
+
+; read group parameters if requested
+
+ if (N_params() GT 2) and ( desc[7] GT 0 ) then begin
+	parrec = assoc(UNIT, bytarr(desc[7]),(group+1)*desc[9]-desc[7])
+	par = parrec[0]
+ end
+
+; read data with dimensions specified in desc.
+
+ ndimen = desc[3]
+ dtype  =  desc[8]
+ dimen = desc[10:9+ndimen]
+ sbyte = long(group)*desc[9]
+
+ rec  =  assoc(unit,make_array(size=[ndimen,dimen>1,dtype,0],/nozero),sbyte)
+
+ return,rec[0]
+
+ end
diff --git a/Code/script_idl_mv/astrolib/sxwrite.pro b/Code/script_idl_mv/astrolib/sxwrite.pro
new file mode 100644
index 0000000000000000000000000000000000000000..a106095077d3fa66314be80e297c4902348c7476
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/sxwrite.pro
@@ -0,0 +1,92 @@
+pro SXWRITE, Unit, Data, Par
+;+
+; NAME:
+;	SXWRITE
+; PURPOSE:
+;	Write a group of data and parameters in ST format
+;	to a STSDAS data file.
+;
+; CALLING SEQUENCE:
+;	SXWRITE, Unit, Data,[ Par]
+;
+; INPUTS:
+;	Unit = unit number of file.  The file must have been
+;		previously opened by SXOPEN.
+;	Data = Array of data to be written.  The dimensions
+;		must agree with those supplied to SXOPEN and written
+;		into the FITS header.  The type is converted if
+;		necessary.
+;
+; OPTIONAL INPUT PARAMETERS:
+;	Par = parameter block.  The size of this array must
+;		agree with the Psize parameter in the FITS header.
+;
+; OUTPUTS:
+;	None.
+; COMMON BLOCKS:
+;	STCOMMN - Contains RESULT(20,10) where RESULT(i,LUN) =
+;	0 - 121147 for consistency check, 1 - Unit for consistency,
+;	2 - bitpix, 3 - naxis, 4 - groups (0 or 1), 5 - pcount,
+;	6 - gcount, 7 - psize, 8 - data type as idl type code,
+;	9 - bytes / record, 10 to 10+N-1 - dimension N,
+;	18 - # of groups written, 19 = gcount.
+;
+; SIDE EFFECTS:
+;	The data are written into the next group.
+;
+; RESTRICTIONS:
+;	SXOPEN must have been called to initialize the
+;	header and the common block.
+;
+; MODIFICATION HISTORY:
+;	DMS, July, 1983.
+;	D.Lindler July, 1986 - changed block size of file to 512
+;			moved group parameters after the groups data.
+;	D.Lindler July, 1987 - modified to allow any size parameter block
+;			(in bytes).
+;	D. Lindler  April, 1990 - converted to new VMS IDL
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;----------------------------------------------------------------------------
+;
+	common stcommn, result, filename
+	if N_params() LT 2 then begin
+		print,'Syntax - SXWRITE, Unit, Data,[ Par]
+		return
+        endif	
+;
+	if N_elements(result) ne 200 then begin
+		print,'SXWRITE - Sxopen not called'
+		return
+		endif
+	if result[1,unit] ne unit then begin
+		print,'SXWRITE - unit not opened with SXOPEN'
+		return
+		endif
+;
+	on_error,2			;return to caller on error
+	s = size(data)			;get data dims
+;
+; determine position in file to write
+;
+	start=result[18,unit]*result[9,unit]
+;
+; create assoc variable for data
+;
+	rec = assoc(unit,data,start)
+;
+; write data
+;
+	rec[0]=data
+;
+; write pblk
+;
+	if result[7,unit] gt 0 then begin
+		if n_params(0) lt 3 then par=bytarr(result[7,unit])
+		p=byte(par,0,result[7,unit])
+		rec=assoc(unit,p,start+result[9,unit]-result[7,unit])
+		rec[0]=p
+	end
+	result[18,unit] = result[18,unit]+1 ;did one more group
+	return
+end
diff --git a/Code/script_idl_mv/astrolib/t_aper.pro b/Code/script_idl_mv/astrolib/t_aper.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8a9b24c056cbe820c189e1cfa83486e3b2aad71b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/t_aper.pro
@@ -0,0 +1,160 @@
+pro t_aper,image,fitsfile,apr,skyrad,badpix,PRINT=print,SILENT=silent, $
+        NEWTABLE = newtable, SETSKYVAL = setskyval,EXACT = Exact
+;+
+; NAME:
+;       T_APER
+; PURPOSE:
+;       Driver procedure (for APER) to compute concentric aperture photometry.
+; EXPLANATION:
+;       Data is read from and written to disk FITS ASCII tables.   
+;       Part of the IDL-DAOPHOT photometry sequence
+;
+; CALLING SEQUENCE:
+;       T_APER, image, fitsfile, [ apr, skyrad, badpix, PRINT=, NEWTABLE=, 
+;                       /EXACT, /SILENT, SETSKYVAL = ]
+;
+; INPUTS:
+;       IMAGE   - input data array
+;       FITSFILE  - disk FITS ASCII table name (from T_FIND).  Must contain
+;               the keywords 'X' and 'Y' giving the centroid of the source
+;               positions in FORTRAN (first pixel is 1) convention.   An
+;               extension of .fit is assumed if not supplied.
+;
+; OPTIONAL INPUTS:
+;       User will be prompted for the following parameters if not supplied.
+;
+;       APR    -  Vector of up to 12 REAL photometry aperture radii.
+;       SKYRAD  - Two element vector giving the inner and outer radii
+;               to be used for the sky annulus
+;       BADPIX  - Two element vector giving the minimum and maximum
+;               value of a good pixel (Default [-32765,32767])
+;
+; OPTIONAL KEYWORDS INPUTS:
+;       /EXACT - If this keyword is set, then intersection of the circular
+;               aperture is computed exactly (and slowly) rather than using
+;               an approximation.   See APER for more info.
+;       /PRINT - if set and non-zero then NSTAR will also write its results to
+;               a file aper.prt.   One can specify a different output file 
+;               name by setting PRINT = 'filename'.
+;       /SILENT - If this keyword is set and non-zero, then APER will not
+;               display photometry results at the screen, and the results 
+;               will be automatically incorporated in the FITS table without
+;               prompting the user
+;       NEWTABLE  - Name of output disk FITS ASCII table, scalar string.   
+;               If not supplied, then the input FITSFILE will be updated with 
+;               the aperture photometry results.
+;       SETSKYVAL - Use this keyword to force the sky to a specified value 
+;               rather than have APER compute a sky value.    SETSKYVAL 
+;               can either be a scalar specifying the sky value to use for 
+;               all sources, or a 3 element vector specifying the sky value, 
+;               the sigma of the sky value, and the number of elements used 
+;               to compute a sky value.   The 3 element form of SETSKYVAL
+;               is needed for accurate error budgeting.
+;
+; PROMPTS:
+;       T_APER requires the number of photons per analog digital unit
+;       (PHPADU), so that it can compute Poisson noise statistics to assign
+;       photometry errors.    It first tries to find the PHPADU keyword in the
+;       original image header, and if not found will look for the GAIN, 
+;       CCDGAIN and finally ATODGAIN keywords.   If still not found, T_APER 
+;       will prompt the user for this value.
+;
+; PROCEDURES:
+;       APER, FTADDCOL, FTGET(), FTINFO, FTPUT, READFITS(), SXADDPAR, 
+;       SXPAR(), WRITEFITS 
+; REVISON HISTORY:
+;       Written   W. Landsman   ST Systems Co.            May 1988
+;       Store results as flux or magnitude                August 1988
+;       Added SILENT keyword  W. Landsman                 Sep. 1991
+;       Changed ERR SKY to ERR_SKY W. Landsman   March 1996
+;       Replace TEXTOUT keyword with PRINT keyword  W. Landsman  May 1996
+;       Check CCDGAIN or ATODGAIN keywords to find phpadu W. Landsman May 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Updated for new FTINFO calling sequence   W. Landsman  May 2000
+;       Added /EXACT keyword                      W. Landsman  June 2000
+;       
+;-
+ On_error,2                         ;Return to caller
+
+ if N_params() LT 2 then begin
+    print,'Syntax - T_APER, image, fitsfile, [ apr, skyrad, badpix'
+    print,'              /EXACT, SETSKY = ,PRINT = , NEWTABLE = ,/SILENT ]'
+  return
+ endif
+
+ newfile = keyword_set(NEWTABLE)
+ if not keyword_set(NEWTABLE) then newtable = fitsfile
+
+ dummy = readfits( fitsfile, hprimary, /SILENT )
+ tab = readfits( fitsfile, h, /exten)
+
+ ftinfo,h,ft_str
+ ttype = strtrim(ft_str.ttype,2)
+ xc = ftget( ft_str, tab, 'X' ) - 1.      ;Subtract to conv from FORTRAN to IDL
+ yc = ftget( ft_str, tab, 'Y' ) - 1.
+
+ phpadu = sxpar( hprimary, 'PHPADU', Count = n )  ;Try to get photons per ADU
+ if n EQ 0 then begin
+        phpadu = sxpar( hprimary, 'GAIN', Count  = n)
+        if n EQ 0 then phpadu = sxpar( hprimary, 'CCDGAIN', Count = n)
+        if n EQ 0 then phpadu = sxpar( hprimary, 'ATODGAIN', Count = n)
+        if n EQ 0 then begin
+        read,'Enter photons per ADU (CCD Gain):  ',phpadu
+        message,'Storing photon/ADU value of ' + strtrim(phpadu,2) + $
+               ' in header',/INF
+       sxaddpar,hprimary,'PHPADU',phpadu,'Photons Per ADU',before = 'HISTORY'
+       endif
+ endif
+
+ message,'Using photon/ADU value of ' + strtrim(phpadu,2),/INF
+
+ aper, image, xc, yc, mags, errap, sky, skyerr, phpadu, apr, skyrad,$
+     badpix, PRINT = print, SILENT=silent, SETSKYVAL = setskyval, EXACT = exact
+
+ ans=''
+ if NOT keyword_set(SILENT) and (NOT newfile) then read, $
+    'T_APER: Update table with current results [Y]? ',ans
+
+ if strupcase(ans) NE 'N' then begin   
+    sxaddpar,h,'EXTNAME','IDL DAOPHOT: APER',' Last DAOPHOT step'
+    sxaddpar,h,'SKYIN',skyrad[0],' Inner Sky Radius','TTYPE1' 
+    sxaddpar,h,'SKYOUT',skyrad[1],' Outer Sky Radius','TTYPE1'
+    sxaddpar,h,'BADPIX1',badpix[0],' Bad Pixel Value: LOW','TTYPE1'
+    sxaddpar,h,'BADPIX2',badpix[1],' Bad Pixel Value: HIGH','TTYPE1'
+
+    gsky = where(ttype EQ 'SKY', N_sky)
+    if N_sky EQ 0 then ftaddcol,h,tab,'SKY',8,'F8.3'
+    ftput,h,tab,'SKY',0,sky
+    
+    gskyerr = where(ttype EQ 'ERR_SKY', N_skyerr)
+    if N_skyerr EQ 0 then ftaddcol,h,tab,'ERR_SKY',8,'F8.3'
+    ftput,h,tab,'ERR_SKY',0,skyerr
+    nstars = N_elements(xc)
+    name = 'MAG'       &  e_name = 'ERR_AP'
+    units = ' MAG'
+    f_format = 'F7.3'  &  e_format ='F6.3'
+
+    for i = 1,N_elements(apr) do begin    
+       ii = strtrim(i,2)
+      apsize = 'APR' + ii
+      sxaddpar,h,apsize,apr[i-1],' Aperture ' + ii + ' Size','TTYPE1'
+      field = 'AP' + ii + '_' + name                
+      efield = e_name + ii 
+      gap = where(ttype EQ field, Nap)
+     
+      if Nap EQ 0 then begin            ;Create new columns?
+           ftaddcol,h,tab,field,8,f_format,units
+           ftaddcol,h,tab,efield,8,e_format,units
+      endif
+      ftput,h,tab,field,0,fltarr(nstars) + mags[i-1,*]
+      ftput,h,tab,efield,0,fltarr(nstars) + errap[i-1,*]
+    endfor     
+
+    sxaddhist,'T_APER: '+ systime(),h
+ endif
+
+ writefits, newtable, 0, hprimary
+ writefits, newtable, tab,h,/append
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/t_find.pro b/Code/script_idl_mv/astrolib/t_find.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e94ef07e86b6452e3cfecc2b46ecde44900f78a3
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/t_find.pro
@@ -0,0 +1,127 @@
+pro t_find,image, im_hdr, fitsfile, hmin, fwhm, sharplim, roundlim,$
+           PRINT = print, SILENT = silent
+;+
+; NAME:
+;        T_FIND
+; PURPOSE:
+;       Driver procedure (for FIND) to locate stars in an image.
+; EXPLANATION:
+;       Finds positive brightness perturbations (i.e stars) in a 
+;       2 dimensional image.  Output is to a FITS ASCII table.
+;
+; CALLING SEQUENCE:
+;       T_FIND, image, im_hdr, [ fitsfile, hmin, fwhm, sharplim, roundlim, 
+;                                       PRINT = , /SILENT ]
+; INPUTS:
+;       image - 2 dimensional image array (integer or real) for which one
+;               wishes to identify the stars present
+;       im_hdr - FITS header associated with image array
+;
+; OPTIONAL INPUTS: 
+;       T_FIND will prompt for these parameters if not supplied
+;
+;       fitsfile - scalar string specifying the name of the output FITS ASCII
+;               table file
+;       fwhm - FWHM to be used in the convolving filter
+;       hmin - Threshold intensity for a point source - should generally
+;               be 3 or 4 sigma above background level
+;       sharplim - 2 element vector giving low and high Limit for 
+;               sharpness statistic (Default: [0.2,1.0] )
+;       roundlim - 2 element vector giving low and high Limit for
+;               roundness statistic (Default: [-1.0,1.0] )
+;
+; OPTIONAL INPUT KEYWORDS:
+;       /PRINT - if set and non-zero then NSTAR will also write its results to
+;               a file find.prt.   One can specify the output file name by
+;               setting PRINT = 'filename'.
+;       /SILENT -   If this keyword is set and non-zero, then FIND will work
+;               silently, and not display each star found
+;
+; OUTPUTS:
+;       None
+;
+; PROCEDURES CALLED:
+;       CHECK_FITS, FDECOMP, FIND, FTADDCOL, FTCREATE, SXADDHIST, SXADDPAR, 
+;       SXDELPAR, SXPAR(), WRITEFITS
+;
+; REVISION HISTORY:
+;       Written W. Landsman, STX  May, 1988
+;       Added phpadu, J. Hill, STX, October, 1990
+;       New calling syntax output to disk FITS table, W. Landsman    May 1996
+;       Work with more than 32767 stars  W. Landsman August 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Remove obsolete !ERR system variable   W. Landsman   May 2000
+;-
+ On_error,2               ;Return to caller
+
+ if N_params() LT 2 then begin                                  
+    print,'Syntax -  ' + $
+       'T_FIND, image, hdr, [fitsfile, hmin, fwhm, sharplim, roundlim '
+    print,'                       PRINT = ,/SILENT ]'
+    return
+ endif
+
+ if not keyword_set( SILENT ) then silent = 0
+
+ check_FITS, image, im_hdr, /NOTYPE, ERRMSG = errmsg
+ if ERRMSG NE ''  then begin
+       message,'ERROR - ' + errmsg, /CON 
+       return
+ endif
+ 
+ if N_elements(fitsfile) EQ 0 then begin
+        fitsfile = ''
+        read,'Enter name of output FITS ASCII table file: ', fitsfile
+ endif
+
+ find, image, x, y, flux, sharp, round, hmin, fwhm, roundlim, sharplim, $ 
+                PRINT = print, SILENT = silent
+
+ nstar = N_elements(x)
+ if nstar EQ 0 then message,'No FITS table created'
+
+ ftcreate, 80, nstar, h, tab
+
+ name = sxpar( im_hdr, 'IMAGE', Count = N_name )
+ if N_name GT 0 then sxaddpar, h, 'IMAGE',name
+
+ sxaddpar, h, 'EXTNAME', 'IDL DAOPHOT: FIND',' Last DAOPHOT stage'
+ sxaddpar, h, 'HMIN', hmin, 'Threshold Above Background'
+ sxaddpar, h, 'FWHM', fwhm, 'FIND FWHM'
+ sxaddpar, h, 'ROUNDLO', roundlim[0], ' Roundness Limit: Low '
+ sxaddpar, h, 'ROUNDHI', roundlim[1], ' Roundness Limit: High'
+ sxaddpar, h, 'SHARPLO', sharplim[0], ' Sharpness Limit: Low '
+ sxaddpar, h, 'SHARPHI', sharplim[1], ' Sharpness Limit: High'
+
+ bscale = sxpar( im_hdr, 'BSCALE', Count = N_bscale )
+ if N_bscale EQ 0 then sxaddpar, h, 'BSCALE', bscale, 'Calibration Const'
+ phpadu = sxpar( im_hdr, 'PHPADU', Count = N_phpadu )
+ if N_phpadu EQ 0 then sxaddpar, h, 'PHPADU', phpadu, 'Photons Per ADU'
+
+ ftaddcol, h, tab, 'STAR_ID', 4, 'I5'
+ ftput, h, tab, 1, 0, lindgen(nstar)+1
+ ftaddcol, h, tab, 'X', 8, 'F7.2', 'PIX'
+ ftput, h, tab, 2, 0, x+1.              ;Position written in FORTRAN convention
+ ftaddcol, h, tab, 'Y', 8, 'F7.2', 'PIX'
+ ftput, h, tab, 3, 0, y+1.              
+ ftaddcol, h, tab, 'FLUX', 8, 'F8.1', 'ADU'
+ ftput, h, tab, 4, 0, flux
+ ftaddcol, h, tab, 'SHARP', 8, 'F6.3'
+ ftput, h, tab, 5, 0, sharp
+ ftaddcol, h, tab, 'ROUND', 8, 'F6.3'
+ ftput, h, tab, 6, 0, round
+ sxaddhist, 'T_FIND: ' + systime(),h
+
+ hprimary = im_hdr                ;Primary FITS header
+ sxdelpar,hprimary,['NAXIS1','NAXIS2']
+ sxaddpar,hprimary,'NAXIS',0
+ sxaddpar,hprimary,'SIMPLE','T'
+ sxaddpar,hprimary,'EXTEND','T',after='NAXIS'
+
+ sxaddpar, h, 'NAXIS1', 80
+ message,'Creating FITS ASCII table ' + fitsfile, /INF
+ writefits, fitsfile, 0, hprimary
+ writefits, fitsfile, tab,h,/append
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/t_getpsf.pro b/Code/script_idl_mv/astrolib/t_getpsf.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f08cb8a7a96d2c141742dfc3f294f1e5d258d2a2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/t_getpsf.pro
@@ -0,0 +1,120 @@
+pro t_getpsf,image,fitsfile,idpsf,psfrad,fitrad,psfname, $ 
+             NEWTABLE = newtable, DEBUG = debug
+;+
+; NAME:
+;       T_GETPSF
+; PURPOSE:
+;       Driver procedure (for GETPSF) to generate a PSF from isolate stars.
+; EXPLANATION:
+;       Generates a point-spread function from one or more isolated stars.
+;       List of stars is read from the FITS ASCII table output of T_APER.
+;       PSF is represented as a sum of a Gaussian plus residuals.
+;       Ouput residuals are written to a FITS image file.
+;
+; CALLING SEQUENCE:
+;       T_GETPSF, image, fitsfile, [ idpsf, psfrad, fitrad, psfname, 
+;                                       /DEBUG, NEWTABLE =]
+;
+; INPUTS:
+;       IMAGE - image array
+;       FITSFILE  - scalar string giving name of disk FITS ASCII table.  Must 
+;               contain the keywords 'X','Y' (from T_FIND) and 'AP1_MAG','SKY'
+;               (from T_APER).
+;
+; OPTIONAL INPUTS:
+;       IDPSF - vector of stellar ID indices indicating which stars are to be 
+;               used to create the PSF.    Not that the PSF star should be 
+;               specified *not* by its STAR_ID value, but rather by the its 
+;               row number (starting with 0) in the FITS table
+;       PSFRAD - the radius for which the PSF will be defined
+;       FITRAD - fitting radius, always smaller than PSFRAD
+;       PSFNAME - name of FITS image file to contain PSF residuals,
+;               scalar string
+;       GETPSF will prompt for all the above values if not supplied.
+;
+; OPTIONAL KEYWORD INPUT
+;       NEWTABLE - scalar string specifying the name of the output FITS ASCII
+;               table.   If not supplied, then the input table is updated with
+;               the keyword PSF_CODE, specifying which stars were used for the
+;               PSF.
+;       DEBUG - if this keyword is set and non-zero, then the result of each
+;               fitting iteration will be displayed.
+;
+; PROMPTS:
+;       T_GETPSF will prompt for the readout noise (in data numbers), and
+;       the gain (in photons or electrons per data number) so that pixels can
+;       be weighted during the PSF fit.   To avoid the prompt, add the 
+;       keywords RONOIS and PHPADU to the FITS ASCII table header.     
+;
+; PROCEDURES USED:
+;       FTADDCOL, FTGET(), FTPUT, GETPSF, READFITS(), SXADDHIST, SXADDPAR, 
+;       SXPAR(), WRITEFITS, ZPARCHECK
+; REVISION HISTORY:
+;       Written  W. Landsman     STX           May, 1988
+;       Update PSF_CODE to indicate PSF stars in order used, W. Landsman Mar 96
+;       I/O to FITS ASCII disk files  W. Landsman    May 96
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Update for new FTINFO call   W. Landsman   May 2000
+;-
+ On_error,2
+
+ if N_params() LT 2 then begin
+        print,'Syntax - T_GETPSF, image, fitsfile, [ idpsf, psfrad, fitrad,'+ $ 
+              '/DEBUG, NEWTABLE = ]'
+        return
+ endif 
+
+ zparcheck,'T_GETPSF',image,1,[1,2,3,4,5],2,'image array'
+ zparcheck,'T_GETPSF',fitsfile,2,7,0,'name of disk FITS ASCII table'
+ if not keyword_set(newtable) then newtable = fitsfile
+
+ dummy = readfits(fitsfile, hprimary,/SILENT)
+ tab = readfits(fitsfile,h,/ext)
+
+ ftinfo,h,ft_str
+ ttype = strtrim(ft_str.ttype,2)
+ x = ftget(ft_str,tab,'X') - 1.
+ y = ftget(ft_str,tab,'Y') - 1.
+ apmag = ftget(ft_str,tab,'AP1_MAG')
+ sky = ftget(ft_str,tab,'SKY')
+
+;Try to get read-out noise from header; otherwise prompt for it
+
+ ronois = sxpar(hprimary,'RONOIS', Count = N_Ronois)    
+ if N_Ronois EQ 0 then begin
+    read,'Enter the read-out noise in ADU per pixel: ',ronois
+    print,'Storing readout noise  of ',strtrim(ronois,2),' in header'
+    sxaddpar,hprimary,'RONOIS',ronois,'Read out noise (ADU/pixel)', $
+        before = 'HISTORY'
+ endif
+
+;Try to get photons per ADU; otherwise prompt for it
+
+ phpadu = sxpar(hprimary,'PHPADU', Count = N_phpadu)       
+ if N_phpadu GT 0 then begin
+       message,'Using photon/ADU value of ' + strtrim(phpadu,2),/INF
+ endif else begin
+       read,'Enter photons per ADU:  ',phpadu
+       print,'Storing photon/ADU of ',strtrim(phpadu,2),' in header'
+       sxaddpar,hprimary,'PHPADU',phpadu,'Photons Per ADU',before='HISTORY'
+ endelse
+
+ getpsf,image,x,y,apmag,sky,ronois,phpadu,gauss,psf,idpsf,psfrad,fitrad,psfname
+
+ if psfname NE '' then begin
+   code = bytarr(N_elements(apmag))
+   code[idpsf] = indgen(N_elements(idpsf)) + 1
+
+   g = where(ttype EQ 'PSF_CODE', Ng) 
+   if Ng EQ 0 then ftaddcol,h,tab,'PSF_CODE',2,'I1'
+   ftput,h,tab,'PSF_CODE',0,code
+
+   sxaddpar,h,'EXTNAME','IDL DAOPHOT: GETPSF','DAOPHOT stage'
+   sxaddpar,h,'PSF_NAME',psfname,'Name of PSF Image','TTYPE1'
+   sxaddhist,'T_GETPSF: ' + systime(),h
+   writefits, newtable, 0, hprimary
+   writefits, newtable, tab,h,/append
+ endif else print,'No PSF file created; Table not updated'
+
+  return
+ end
diff --git a/Code/script_idl_mv/astrolib/t_group.pro b/Code/script_idl_mv/astrolib/t_group.pro
new file mode 100644
index 0000000000000000000000000000000000000000..011516f400e99477e02d081b06c36bf3cab8adf2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/t_group.pro
@@ -0,0 +1,73 @@
+pro t_group,fitsfile,rmax,xpar=xpar,ypar=ypar, NEWTABLE = newtable
+;+
+; NAME:
+;	T_GROUP
+; PURPOSE:
+;	Driver procedure (for GROUP) to place stars in non-overlapping groups.
+; EXPLANATION:
+;	This procedure is part of the DAOPHOT sequence that places star
+;	positions with non-overlapping PSFs into distinct groups   
+;	Input and output are to FITS ASCII tables
+;
+; CALLING SEQUENCE:
+;	T_GROUP, fitsfile, [ rmax, XPAR = , YPAR = , NEWTABLE = ]
+;
+; INPUTS:
+;	FITSFILE -  Name of disk FITS ASCII table containing the X,Y positions
+;		in FITS (FORTRAN) convention (first pixel is 1,1)
+;
+; OPTIONAL INPUTS:
+;	rmax - maximum allowable distance between stars in a single group
+;
+; OPTIONAL INPUT KEYWORDS:
+;	XPAR, YPAR - scalar strings giving the field name in the output table
+;		containing the X and Y coordinates.   If not supplied,
+;		then the fields 'X' and 'Y' are read.
+;	NEWTABLE - scalar giving name of output disk FITS ASCII table.   If not
+;		supplied, 
+;
+; PROCEDURES:
+;	FTADDCOL, FTGET(), FTINFO, FTPUT, GROUP, READFITS(), SXADDHIST, 
+;	SXADDHIST, WRITEFITS
+; REVISION HISTORY:
+;	Written, W. Landsman        STX Co.      May, 1996
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Updated for new FTINFO call    W. Landsman    May 2000
+;-
+ On_error,2    
+
+ if N_params() LT 1 then begin
+	print,'Syntax - T_GROUP, fitsfile, [rmax, XPAR = , YPAR =, NEWTABLE = ]'
+        return
+ endif
+
+ if not keyword_set(XPAR) then xpar = 'X'
+ if not keyword_set(YPAR) then ypar = 'Y'
+ if not keyword_set(NEWTABLE) then newtable = fitsfile
+
+ dummy = readfits( fitsfile, hprimary, /SILENT )
+ tab = readfits(fitsfile, h, /ext)
+
+ ftinfo,h,ft_str
+ ttype = strtrim(ft_str.ttype,2)
+ x = ftget( ft_str, tab, xpar) - 1.
+ y = ftget( ft_str, tab, ypar) - 1.
+
+ if N_elements(rmax) EQ 0 then $
+	read,'Enter maximum distance between stars in a group: ',rmax
+
+ group, x, y, rmax, ngroup
+
+ sxaddpar, h, 'RMAX', rmax, 'Maximum Distance in Group', 'TTYPE1'
+ sxaddpar, h, 'EXTNAME', 'IDL DAOPHOT: Group', 'DAOPHOT Stage'
+
+ gid = where(ttype EQ 'GROUP_ID', Nid)
+ if Nid EQ 0 then ftaddcol, h, tab, 'GROUP_ID', 4, 'I4'
+ ftput, h, tab, 'GROUP_ID', 0, ngroup
+ sxaddhist, 'T_GROUP: ' + systime(),h
+
+ writefits, newtable, 0, hprimary
+ writefits, newtable, tab,h,/append
+ return
+
+ end
diff --git a/Code/script_idl_mv/astrolib/t_nstar.pro b/Code/script_idl_mv/astrolib/t_nstar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..453c9806e15ae0581d03702278a327f10d06506e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/t_nstar.pro
@@ -0,0 +1,159 @@
+pro t_nstar,image,fitsfile,psfname,groupsel,SILENT=silent,PRINT=print, $
+                NEWTABLE = newtable, VARSKY = varsky, DEBUG = debug
+;+
+; NAME:
+;       T_NSTAR
+; PURPOSE:
+;       Driver procedure (for NSTAR) for simultaneous PSF fitting.  
+; EXPLANATION:
+;       Input and output are to disk FITS ASCII tables.
+;
+; CALLING SEQUENCE:
+;       T_NSTAR, image, fitsfile, [psfname, groupsel, /SILENT, /PRINT
+;                               NEWTABLE = , /VARSKY ]
+; INPUTS:
+;       IMAGE - 2-d image array
+;       FITSFILE  - scalar string giving name of disk FITS ASCII table.  Must 
+;               contain the keywords 'X','Y' (from T_FIND) 'AP1_MAG','SKY'
+;               (from T_APER) and 'GROUP_ID' (from T_GROUP).   This table
+;               will be updated with the results of T_NSTAR, unless the 
+;               keyword NEWTABLE is supplied.   
+;
+; OPTIONAL INPUTS:
+;       PSFNAME - Name of the FITS file created by T_GETPSF containing
+;               PSF residuals, scalar string
+;       GROUPSEL - Scalar or vector listing the groups to process.  For
+;               example, to process stars in groups 2 and 5 set
+;               GROUPSEL = [2,5].  If omitted, or set equal to -1,
+;               then NSTAR will process all groups.
+;
+; OPTIONAL KEYWORD INPUTS:
+;       VARSKY - If this keyword is set and non-zero, then the mean sky level
+;               in each group of stars, will be fit along with the brightness
+;               and positions.
+;       /SILENT - if set and non-zero, then NSTAR will not display its results
+;               at the terminal
+;       /PRINT - if set and non-zero then NSTAR will also write its results to
+;               a file NSTAR.PRT.   One can specify the output file name by
+;               setting PRINT = 'filename'.
+;       NEWTABLE  - Name of output disk FITS ASCII table to contain the results
+;               of NSTAR.   If not supplied, then the input FITSFILE will be 
+;               updated.  
+;       DEBUG - if this keyword is set and non-zero, then the result of each
+;               fitting iteration will be displayed.
+;
+; PROCEDURES CALLED:
+;       FTADDCAL, FTINFO, FTGET(), FTPUT, NSTAR, SXADDHIST, 
+;       SXADDPAR, SXPAR(), READFITS(), WRITEFITS
+; REVISION HISTORY:
+;       Written        W. Landsman         STX Co.    May, 1988
+;       Check for CCDGAIN, ATODGAIN keywords to get PHPADU  W. Landsman May 1997
+;       Fixed typo preventing compilation, groupsel parameter W.L. July 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Update for new FTINFO call    W. Landsman   May 2000
+;-
+ On_error,2
+
+ if N_params() LT 2 then begin
+        print, 'Syntax - T_NSTAR, image, fitsfile, [ psfname, groupsel, '
+        print,'                          /VARSKY, NEWTABLE = ,/SILENT, PRINT=]'
+        return
+ endif
+
+ if not keyword_set(NEWTABLE) then newtable = fitsfile
+
+ dummy = readfits(fitsfile, hprimary, /SILENT)
+ tab = readfits(fitsfile, h, /ext)
+
+ ftinfo, h, ft_str
+ ttype = strtrim(ft_str.ttype,2)
+
+ idg = where(ttype EQ 'GROUP_ID', Nid)
+ if Nid EQ 0 then begin
+        message,'T_NSTAR: ERROR - Field GROUP_ID not found in header',/CON
+        message,'Procedure T_GROUP must be run before T_NSTAR',/CON
+        return
+ endif else group = ftget(ft_str,tab,idg[0] + 1)
+
+ if N_params() EQ 4 then begin
+        nsel = N_elements(groupsel)
+           if groupsel[0] LT 0 then select = indgen(N_elements(group)) $ 
+        else begin
+           select = where(group EQ groupsel[0])
+           if nsel GT 1 then $ 
+              for i=1,nsel-1 do select = [select,where(group eq groupsel[i])]
+        endelse
+ endif else select = indgen(N_elements(group)) 
+ group = group[select]
+
+ id = ftget( ft_str, tab, 'STAR_ID', select )
+ x = ftget( ft_str, tab, 'X', select )-1.
+ y = ftget( ft_str, tab, 'Y', select )-1.
+ mags = ftget( ft_str, tab, 'AP1_MAG', select )          
+ sky = ftget( ft_str, tab, 'SKY', select )
+
+;Try to get read-out noise from header
+ ronois = sxpar(hprimary,'RONOIS', Count = Nronois) 
+ if Nronois EQ 0 then begin
+    read,'Enter the read-out noise in ADU per pixel: ',ronois
+    print,'Storing readout noise  of ',ronois,' in header'
+    sxaddpar,hprimary,'RONOIS',ronois,' Read out noise (ADU/pixel)', $
+                before='HISTORY'
+ endif
+
+ phpadu = sxpar( hprimary, 'PHPADU', COUNT = n )  ;Try to get photons per ADU
+ if n EQ 0 then begin
+       phpadu = sxpar( hprimary, 'GAIN', Count  = n)
+       if n EQ 0 then phpadu = sxpar( hprimary, 'CCDGAIN', Count = n)
+       if n EQ 0 then phpadu = sxpar( hprimary, 'ATODGAIN', Count = n)
+       if n EQ 0 then begin
+            read,'Enter photons per ADU (CCD Gain):  ',phpadu
+      sxaddpar,hprimary,'PHPADU',phpadu,' Photons Per ADU',before = 'HISTORY'
+ endif
+ endif
+
+ message,'Using photon/ADU (CCD Gain) value of ' + strtrim(phpadu,2),/INF
+
+ nstar, image, id, x, y, mags, sky, group, phpadu, ronois, psfname, errmag,$
+         iter, chisq,peak,PRINT = print, SILENT = silent, VARSKY = varsky, $
+         DEBUG = debug
+
+ id = id-1
+
+ sxaddpar,h,'EXTNAME','IDL DAOPHOT: NSTAR','DAOPHOT stage'
+
+ g = where(ttype EQ 'X_PSF', Ng)
+ if Ng EQ 0 then ftaddcol,h,tab,'X_PSF',8,'F7.2','PIX'
+ ftput,h,tab,'X_PSF',id,x+1.
+
+ g = where(ttype EQ 'Y_PSF', Ng)
+ if Ng EQ 0 then ftaddcol,h,tab,'Y_PSF',8,'F7.2','PIX'
+ ftput,h,tab,'Y_PSF',id,y+1.
+
+ g = where(ttype EQ 'PSF_MAG', Ng)
+ if Ng EQ 0 then ftaddcol,h,tab,'PSF_MAG',8,'F7.3','MAG'
+ ftput,h,tab,'PSF_MAG',id,mags
+
+ g = where(ttype EQ 'ERR_PSF', Ng)
+ if Ng EQ 0 then ftaddcol,h,tab,'ERR_PSF',8,'F5.3','MAG'
+ ftput,h,tab,'ERR_PSF',id,errmag
+
+ g = where(ttype EQ 'ITER', Ng)
+ if Ng EQ 0 then ftaddcol,h,tab,'ITER',4,'I2'
+ ftput,h,tab,'ITER',id,iter
+
+ g = where(ttype EQ 'CHI', Ng)
+ if Ng EQ 0 then ftaddcol,h,tab,'CHI',8,'F5.2'
+ ftput,h,tab,'CHI',id,chisq
+
+ g = where(ttype EQ 'PEAK', Ng)
+ if Ng EQ 0 then ftaddcol,h,tab,'PEAK',8,'F7.3'
+ ftput,h,tab,'PEAK',id,peak
+
+ sxaddhist,'T_NSTAR: ' + systime(), h
+
+ writefits, newtable, 0, hprimary
+ writefits, newtable, tab,h,/append
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/t_substar.pro b/Code/script_idl_mv/astrolib/t_substar.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b09bce2b0487ba0e36dd9074d9ff9b8b3bf6689a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/t_substar.pro
@@ -0,0 +1,78 @@
+pro t_substar,image,fitsfile,id,psfname, VERBOSE = verbose, NOPSF = nopsf
+;+
+; NAME:
+;       T_SUBSTAR
+; PURPOSE:
+;       Driver procedure (for SUBSTAR) to subtract scaled PSF values 
+; EXPLANATION:
+;       Computes residuals of the PSF fitting program
+;
+; CALLING SEQUENCE:
+;       T_SUBSTAR, image, fitsfile, id,[ psfname, /VERBOSE, /NOPSF ]
+;
+; INPUT-OUTPUT:
+;       IMAGE -  On input, IMAGE is the original image array.  A scaled
+;               PSF will be subtracted from IMAGE at specified star positions.
+;               Make a copy of IMAGE before calling SUBSTAR, if you want to
+;               keep a copy of the unsubtracted image array
+; INPUTS:
+;       FITSFILE  - scalar string giving the name of the disk FITS ASCII 
+;               produced as an output from T_NSTAR.   
+;
+; OPTIONAL INPUTS:
+;       ID -  Index vector indicating which stars are to be subtracted.  If
+;               omitted, (or set equal to -1), then stars will be subtracted 
+;               at all positions specified by the X and Y vectors.
+;               (IDL convention - zero-based subscripts)
+;       PSFNAME - Name of the FITS file containing the PSF residuals, as
+;               generated by GETPSF.  SUBSTAR will prompt for this parameter
+;               if not supplied.      
+; OPTIONAL INPUT KEYWORD:
+;       /VERBOSE - If this keyword is set and non-zero, then the value of each
+;               star number will be displayed as it is processed.
+;       /NOPSF - if this keyword is set and non-zero, then all stars will be 
+;               be subtracted *except* those used to determine the PSF.
+;               An improved PSF can then be derived from the subtracted image.
+;               If NOPSF is supplied, then the ID parameter is ignored
+; NOTES:
+;       T_SUBSTAR does not modify the input FITS table.
+;
+; PROCEDURES USED:
+;       FTGET(), FTINFO, READFITS(), REMOVE, SUBSTAR
+; REVISION HISTORY:
+;       Written, R. Hill, ST Sys. Corp., 22 August 1991
+;       Added NOPSF keyword   W. Landsman        March, 1996
+;       Use FITS format for PSF resduals         July, 1997
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Call FTINFO first to improve efficiency   W. Landsman  May 2000
+;-
+ On_Error,2
+
+ if N_params() LT 2 then begin
+    print,'Syntax -  T_SUBSTAR, im, fitsfile,[id, psfname, /VERBOSE, /NOPSF ]'
+    print,'      im - Image Array'
+    print,'      fitsfile - name of disk FITS ASCII table (from T_NSTAR)'
+    print,"      id - vector of Star ID's to subtract (optional)"
+    print,'      psfname - Name of FITS file containing the PSF'
+    return
+ endif 
+
+ tab = readfits(fitsfile, htab,/exten)
+ ftinfo, htab, ft_str
+ x = ftget(ft_str,tab,'X_PSF') - 1.0
+ y = ftget(ft_str,tab,'Y_PSF') - 1.0
+ mag = ftget(ft_str,tab,'PSF_MAG')
+ IF (N_elements(id) EQ 0) THEN id = -1
+ if keyword_set(NOPSF) then begin 
+        g = where(ft_str.ttype EQ 'PSF_CODE', Ng)
+        if Ng EQ 0 then message,'ERROR -- FITS table missing PSF_CODE column'
+        idpsf = ftget(ft_str,tab,'PSF_CODE')
+        ipsf = where(idpsf)
+        id = indgen(N_elements(x) )
+        remove, ipsf, id
+ endif
+ if not keyword_set( VERBOSE )  then verbose = 0
+ substar,image,x,y,mag,id,psfname, VERBOSE = verbose  ;Subtract scaled PSF stars
+
+ RETURN
+ END
diff --git a/Code/script_idl_mv/astrolib/tabinv.pro b/Code/script_idl_mv/astrolib/tabinv.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1feafd8abb39b3fa7c7559c24560699bfc040df3
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tabinv.pro
@@ -0,0 +1,95 @@
+PRO TABINV, XARR, X, IEFF, FAST = fast
+;+ 
+; NAME:
+;       TABINV     
+; PURPOSE:  
+;       To find the effective index of a function value in an ordered vector.
+;       
+; CALLING SEQUENCE:
+;       TABINV, XARR, X, IEFF, [/FAST]
+; INPUTS:
+;       XARR - the vector array to be searched, must be monotonic
+;               increasing or decreasing
+;       X    - the function value(s) whose effective
+;               index is sought (scalar or vector)
+;
+; OUTPUT:
+;       IEFF - the effective index or indices of X in XARR
+;              always floating point, same # of elements as X
+;
+; OPTIONAL KEYWORD INPUT:
+;       /FAST - If this keyword is set, then the input vector is not checked
+;               for monotonicity, in order to improve the program speed.
+; RESTRICTIONS:
+;       TABINV will abort if XARR is not monotonic.  (Equality of 
+;       neighboring values in XARR is allowed but results may not be
+;       unique.)  This requirement may mean that input vectors with padded
+;       zeroes could cause routine to abort.
+;
+; PROCEDURE:
+;       VALUE_LOCATE() is used to find the values XARR[I]
+;       and XARR[I+1] where XARR[I] < X < XARR[I+1].
+;       IEFF is then computed using linear interpolation 
+;       between I and I+1.
+;               IEFF = I + (X-XARR[I]) / (XARR[I+1]-XARR[I])
+;       Let N = number of elements in XARR
+;               if x < XARR[0] then IEFF is set to 0
+;               if x > XARR[N-1] then IEFF is set to N-1
+;
+; EXAMPLE:
+;       Set all flux values of a spectrum (WAVE vs FLUX) to zero
+;       for wavelengths less than 1150 Angstroms.
+;         
+;       IDL> tabinv, wave, 1150.0, I
+;       IDL> flux[ 0:fix(I) ] = 0.                         
+;
+; FUNCTIONS CALLED:
+;       None
+; REVISION HISTORY:
+;       Adapted from the IUE RDAF                     January, 1988         
+;       More elegant code  W. Landsman                August, 1989
+;       Mod to work on 2 element decreasing vector    August, 1992
+;       Updated for V5.3 to use VALUE_LOCATE()     W. Landsman January 2000
+;       Work when both X and Xarr are integers     W. Landsman August 2001
+;       Use ARRAY_EQUAL, always internal double precision W.L.  July 2009
+;       Allow Double precision output, faster test for monotonicity.
+;                    WL, January 2012
+;-               
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 3 then begin
+     print,'Syntax- TABINV, XARR, X, I, [/FAST]'
+     return
+ endif
+
+ Npoints = N_elements(xarr) & npt= npoints - 1
+ if ( Npoints LE 1 ) then message, /TRACE, $
+   'Search vector (first parameter) must contain at least 2 elements'
+
+ do_double= (size(xarr,/tname) EQ 'DOUBLE') || (size(x,/TNAME) EQ 'DOUBLE')
+
+ if ~keyword_set(fast) then begin
+
+ ; Test for monotonicity (everywhere increasing or decreasing vector)
+
+  i = xarr[1:*] GE xarr
+   test = array_equal( i, 1b) || array_equal(i, 0b) 
+     if ~test then  message, $
+       'ERROR - First parameter must be a monotonic vector' 
+ endif
+
+ if do_double then ieff = double( VALUE_LOCATE(xarr,x)) else $
+                   ieff = float(  VALUE_LOCATE(xarr,x))
+ g = where( (ieff LT npt) and (ieff GE 0), Ngood)
+ if Ngood GT 0 then begin
+      neff = ieff[g]
+      x0 = double(xarr[neff])
+      diff =  x[g] - x0
+      ieff[g] = neff +  diff / (xarr[neff+1] - x0 ) 
+ endif
+     
+ ieff = ieff > 0.0
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/tag_exist.pro b/Code/script_idl_mv/astrolib/tag_exist.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8006edcc6b6e6e9239f97037ff8b83c46ae3b86c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tag_exist.pro
@@ -0,0 +1,99 @@
+;+
+; NAME:        
+;       TAG_EXIST()
+; PURPOSE:              
+;       To test whether a tag name exists in a structure.
+; EXPLANATION:               
+;       Routine obtains a list of tagnames and tests whether the requested one
+;       exists or not. The search is recursive so if any tag names in the 
+;       structure are themselves structures the search drops down to that level.
+;       (However, see the keyword TOP_LEVEL).
+;               
+; CALLING SEQUENCE: 
+;       status = TAG_EXIST(str, tag, [ INDEX =, /TOP_LEVEL, /QUIET ] )
+;    
+; INPUT PARAMETERS:     
+;       str  -  structure variable to search
+;       tag  -  tag name to search for, scalar string
+;
+; OUTPUTS:
+;       Function returns 1b if tag name exists or 0b if it does not.
+;                              
+; OPTIONAL INPUT KEYWORD:
+;       /TOP_LEVEL = If set, then only the top level of the structure is
+;                           searched.
+;       /QUIET - if set, then do not print messages if invalid parameters given
+;       /RECURSE - does nothing but kept for compatibility with the
+;                  Solarsoft version for which recursion is not the default 
+;        http://sohowww.nascom.nasa.gov/solarsoft/gen/idl/struct/tag_exist.pro
+; OPTIONAL OUTPUT KEYWORD:
+;       INDEX = index of matching tag, scalar longward, -1 if tag name does
+;               not exist
+;
+; EXAMPLE:
+;       Determine if the tag 'THICK' is in the !P system variable
+;       
+;       IDL> print,tag_exist(!P,'THICK')
+;
+; PROCEDURE CALLS:
+;       None.
+;
+; MODIFICATION HISTORY:     : 
+;       Written,       C D Pike, RAL, 18-May-94               
+;       Passed out index of matching tag,  D Zarro, ARC/GSFC, 27-Jan-95     
+;       William Thompson, GSFC, 6 March 1996    Added keyword TOP_LEVEL
+;       Zarro, GSFC, 1 August 1996    Added call to help 
+;       Use SIZE(/TNAME) rather than DATATYPE()  W. Landsman  October 2001
+;       Added /RECURSE and /QUIET for compatibility with Solarsoft version
+;                W. Landsman  March 2009
+;       Slightly faster algorithm   W. Landsman    July 2009
+;       July 2009 update was not setting Index keyword  W. L   Sep 2009.
+;       Use V6.0 notation W.L. Jan 2012 
+;        Not setting index again, sigh  W.L./ K. Allers  Jan 2012
+;-            
+
+function tag_exist, str, tag,index=index, top_level=top_level,recurse=recurse, $
+         quiet=quiet
+
+;
+;  check quantity of input
+;
+compile_opt idl2
+if N_params() lt 2 then begin
+   print,'Use:  status = tag_exist(structure, tag_name)'
+   return,0b
+endif
+
+;
+;  check quality of input
+;
+
+if size(str,/TNAME) ne 'STRUCT' or size(tag,/TNAME) ne 'STRING' then begin
+ if ~keyword_set(quiet) then begin 
+   if size(str,/TNAME) ne 'STRUCT' then help,str
+   if size(tag,/TNAME) ne 'STRING' then help,tag
+   print,'Use: status = tag_exist(str, tag)'
+   print,'str = structure variable'
+   print,'tag = string variable'
+  endif 
+   return,0b
+endif
+
+  tn = tag_names(str)
+
+  index = where(tn eq strupcase(tag), nmatch)
+
+ if ~nmatch && ~keyword_set(top_level) then begin
+       status= 0b
+       for i=0,n_elements(tn)-1 do begin
+        if size(str.(i),/TNAME) eq 'STRUCT' then $
+                status=tag_exist(str.(i),tag,index=index)
+        if status then return,1b
+      endfor
+    return,0b
+
+endif else begin
+    index = index[0] 
+    return,logical_true(nmatch)
+ endelse
+end
diff --git a/Code/script_idl_mv/astrolib/tbdelcol.pro b/Code/script_idl_mv/astrolib/tbdelcol.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f9f7479918505147a21fc208ac2eebfdee944196
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tbdelcol.pro
@@ -0,0 +1,111 @@
+pro tbdelcol,h,tab,name                                               
+;+
+; NAME:
+;       TBDELCOL
+; PURPOSE:
+;       Delete a column of data from a FITS binary table
+;
+; CALLING SEQUENCE:
+;       TBDELCOL, h, tab, name
+;
+; INPUTS-OUPUTS
+;       h,tab - FITS binary table header and data array.  H and TAB will
+;               be updated with the specified column deleted
+;
+; INPUTS:
+;       name - Either (1) a string giving the name of the column to delete
+;                       or (2) a scalar giving the column number to delete
+;
+; EXAMPLE:
+;       Delete the column "FLUX" from FITS binary table test.fits
+;
+;       IDL> tab = readfits('test.fits',h,/ext)    ;Read table
+;       IDL> tbdelcol, h, tab, 'FLUX'              ;Delete Flux column
+;       IDL> modfits,'test.fits',tab,h,/ext        ;Write back table
+;
+; PROCEDURES USED:
+;       SXADDPAR, TBINFO, TBSIZE
+; REVISION HISTORY:                                           
+;       Written   W. Landsman        STX Co.     August, 1988
+;       Use new structure returned by TBINFO,  August, 1997
+;       Use SIZE(/TNAME) instead of DATATYPE()   October 2001
+;       Use /NOSCALE in call to TBINFO, update TDISP   W. Landsman   March 2007
+;- 
+ compile_opt idl2
+ On_error, 2
+
+ if N_params() LT 3 then begin
+     print,'Syntax - tbdelcol, h, tab, name'
+     return
+ endif
+
+ s = size(name)
+
+ tbsize, h, tab, ncol, nrows, tfields, allcols, allrows
+
+; Make sure column exists
+
+ tbinfo,h,tb_str,/NOSCALE
+
+ case size(name,/TNAME) of
+ 'STRING': begin
+      field = where(tb_str.ttype eq strupcase(name),nfound)
+      if nfound eq 0 then $ 
+         message,'Field '+strupcase(name) + ' not found in header'
+      end
+ 'UNDEFINED':message,'Third parameter must be field name or number'
+ ELSE: begin
+      field = name-1
+      if (field LT 0 ) or (field GT tfields) then $
+            message,'Field number must be between 1 and ' +strtrim(tfields,2)
+      end
+ endcase
+
+ fname = strtrim(strupcase(name),2)
+ field = field[0]
+
+; Eliminate relevant columns from TAB
+
+ tcol = tb_str.tbcol[field] & w = tb_str.width[field]*tb_str.numval[field]
+
+ case 1 of 
+        tcol eq 0: tab = tab[w:*,*]                     ;First column
+        tcol eq ncol-w: tab = tab[0:tcol-1,*]          ;Last column
+        else: tab = [tab[0:tcol-1,*],tab[tcol+w:*,*]]  ;All other columns
+ endcase
+
+; Parse the header.  Remove specified keyword from header.  Lower
+; the index of subsequent keywords.  Update the TBCOL*** index of
+; subsequent keywords
+
+ nlines = N_elements(h)
+ field = field + 1
+ hnew = strarr(nlines)
+ j = 0
+ for i = 0,nlines-1 DO BEGIN    ;Loop over each element in header
+
+ key = strupcase(strmid(h[i],0,5))
+ if (key eq 'TTYPE') OR (key eq 'TFORM') or (key eq 'TUNIT') or $
+   (key eq 'TNULL') or (key EQ 'TDISP') then begin
+        row = h[i]                    
+        ifield = fix(strtrim(strmid(row,5,3)))    
+        if ifield gt field then begin    ;Subsequent field?
+                if ifield le 10 then fmt = "(I1,' ')" else fmt ='(I2)'
+                strput,row,string(ifield-1,format=fmt),5
+        endif 
+        if ifield ne field then hnew[j] = row else j=j-1
+  endif else hnew[j] = h[i]      
+
+ j = j+1
+
+ endfor  
+
+ sxaddpar,hnew,'TFIELDS',tfields-1 ;Reduce number of fields by 1
+ sxaddpar,hnew,'NAXIS1',ncol-w ;Reduce num. of columns by WIDTH
+
+ h = hnew[0:j-1]
+
+ message,'Field '+fname+' has been deleted from the FITS table',/INF
+
+ return  
+ end
diff --git a/Code/script_idl_mv/astrolib/tbdelrow.pro b/Code/script_idl_mv/astrolib/tbdelrow.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7926cd222eb30e9e69702bd1bec4dc5d019aa07b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tbdelrow.pro
@@ -0,0 +1,76 @@
+pro tbdelrow,h,tab,rows                                               
+;+
+; NAME:
+;	TBDELROW
+; PURPOSE:
+;	Delete specified row or rows of data from a FITS binary table
+;
+; CALLING SEQUENCE:
+;	TBDELROW, h, tab, rows
+;
+; INPUTS-OUPUTS
+;	h,tab - FITS binary table header and data array.  H and TAB will
+;		be updated on output with the specified row(s) deleted.
+;
+;	rows  -  scalar or vector, specifying the row numbers to delete
+;		First row has index 0.   If a vector it will be sorted and
+;		duplicates removed by TBDELROW
+;
+; EXAMPLE:
+;	Compress a table to include only non-negative flux values
+;
+;	flux = TBGET(h,tab,'FLUX')       ;Obtain original flux vector
+;	bad = where(flux lt 0)           ;Find negative fluxes
+;	TBDELROW,h,tab,bad               ;Delete rows with negative fluxes
+;
+; PROCEDURE:
+;	Specified rows are deleted from the data array, TAB.  The NAXIS2
+;	keyword in the header is updated.
+;
+; REVISION HISTORY:                                           
+;	Written   W. Landsman        STX Co.     August, 1988
+;	Checked for IDL Version 2, J. Isensee, July, 1990
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;- 
+ On_error,2
+
+ if N_params() LT 3 then begin
+     print,'Syntax - tbdelrow, h, tab, rows '
+     return                                                  
+ endif
+
+ nrows = sxpar(h,'NAXIS2')            ;Original number of rows
+ if (max(rows) GE nrows) or (min(rows) LT 0) then $
+     message,'Specified rows must be between 0 and ' + strtrim(nrows-1,2)
+
+ ndel = N_elements(rows)
+ if ndel GT 1 then begin
+    rows = rows[rem_dup(rows)]
+    ndel = N_elements(rows)
+ endif
+
+ j = 0L
+ i = rows[0]
+
+ for k = long(rows[0]),nrows-1 do begin
+
+ if k eq rows[j] then begin
+     j = j+1 
+     if j EQ ndel then goto,done
+ endif else begin
+     tab[0,i] = tab[*,k]
+     i = i+1
+ endelse
+
+ endfor
+
+ k = k-1
+
+DONE: 
+
+ if k NE nrows-1 then tab[0,i] = tab[*,i+j:nrows-1]
+ tab = tab[*,0:nrows-ndel-1]
+ sxaddpar,h,'NAXIS2',nrows-ndel      ;Reduce number of rows
+
+ return  
+ end
diff --git a/Code/script_idl_mv/astrolib/tbget.pro b/Code/script_idl_mv/astrolib/tbget.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f6a6720314be6e023e72566a62d07533753b0ea2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tbget.pro
@@ -0,0 +1,255 @@
+function tbget, hdr_or_tbstr, tab, field, rows, nulls, NOSCALE = noscale, $
+        CONTINUE = continue
+;+
+; NAME:
+;       TBGET
+; PURPOSE:
+;       Return value(s) from specified column in a FITS binary table
+;
+; CALLING SEQUENCE
+;       values = TBGET( h, tab, field, [ rows, nulls, /NOSCALE] )
+;               or
+;       values = TBGET( tb_str, tab, field, [ rows, nulls, /NOSCALE] )
+;
+; INPUTS:
+;       h - FITS binary table header, e.g. as returned by FITS_READ
+;                       or
+;       tb_str - IDL structure extracted from FITS header by TBINFO.
+;               Use of the IDL structure will improve processing speed
+;       tab - FITS binary table array, e.g. as returned by FITS_READ
+;       field - field name or number, scalar
+;
+; OPTIONAL INPUTS:
+;       rows -  scalar or vector giving row number(s)
+;               Row numbers start at 0.  If not supplied or set to
+;               -1 then values for all rows are returned
+;
+; OPTIONAL KEYWORD INPUT:
+;       /NOSCALE - If this keyword is set and nonzero, then the TSCALn and
+;               TZEROn keywords will *not* be used to scale to physical values
+;               Default is to perform scaling
+;       CONTINUE - This keyword does nothing, it is kept for consistency with
+;               with earlier versions of TBGET().
+; OUTPUTS:
+;       the values for the row are returned as the function value.
+;       Null values are set to 0 or blanks for strings.
+;
+; OPTIONAL OUTPUT:
+;       nulls - null value flag of same length as the returned data.
+;               Only used for integer data types, B, I, and J
+;               It is set to 1 at null value positions and 0 elsewhere.
+;               If supplied then the optional input, rows, must also
+;               be supplied.
+;
+; EXAMPLE:
+;       Read the columns labeled 'WAVELENGTH' and 'FLUX' from the second
+;       extension of a FITS file 'spectra.fits' into IDL vectors w and f
+;
+;       IDL> fits_read,'spectra.fits',tab,htab,exten=2   ;Read 2nd extension
+;       IDL> w = tbget(htab,tab,'wavelength')
+;       IDL> f = tbget(htab,tab,'flux')
+;
+; NOTES:
+;       (1) If the column is variable length ('P') format, then TBGET() will 
+;       return the longword array of pointers into the heap area.   TBGET() 
+;       currently lacks the ability to actually extract the data from the 
+;       heap area.
+;       (2) Use the higher-level procedure FTAB_EXT (which calls TBGET()) to
+;       extract vectors directly from the FITS file.   
+;       (3) Use the procedure FITS_HELP to determine which extensions are 
+;       binary tables, and FTAB_HELP or TBHELP to determine the columns of the
+;       table
+; PROCEDURE CALLS:
+;       TBINFO, TBSIZE 
+; HISTORY:
+;       Written  W. Landsman        February, 1991
+;       Work for string and complex   W. Landsman         April, 1993
+;       Default scaling by TSCALn, TZEROn, Added /NOSCALE keyword,
+;       Fixed nulls output, return longword pointers for variable length
+;               binary tables,     W. Landsman  December 1996
+;       Added a check for zero width column  W. Landsman   April, 1997
+;       Add TEMPORARY() and REFORM() for speed  W. Landsman  May, 1997
+;       Use new structure returned by TBINFO    W. Landsman  August 1997
+;       Add IS_IEEE_BIG(), No subscripting when all rows requested
+;                               W. Landsman    March 2000
+;       Use SIZE(/TNAME) instead of DATATYPE()  W. Landsman October 2001
+;       Bypass IEEE_TO_HOST call for improved speed W. Landsman November 2002
+;       Cosmetic changes to SIZE() calls W. Landsman December 2002
+;       Added unofficial support for 64bit integers W. Landsman February 2003
+;       Support unsigned integers, new pointer types of TSCAL and TZERO
+;       returned by TBINFO   W. Landsman        April 2003
+;       Add an i = i[0] for V6.0 compatibility  W. Landsman  August 2003
+;       Use faster BYTEORDER byteswapping  W. Landsman April 2006
+;       Free pointers if FITS header supplied W. Landsman March 2007
+;       Use V6.0 notation W. Landsman  April 2014
+;-
+;------------------------------------------------------------------
+ On_error,2
+ compile_opt idl2
+        
+ if N_params() LT 3 then begin
+    print, $
+ 'Syntax - values = TBGET(h, tab, field, [ rows, nulls, /NOSCALE ])'
+    return, -1
+ endif
+
+; get size of table
+
+ ndimen = size(tab,/n_dimen)
+ if Ndimen EQ 1 then nrows =1 else $
+ nrows = (size(tab,/dimen))[1]
+
+; get characteristics of specified field
+
+ case size(hdr_or_tbstr,/type) of 
+ 7: tbinfo,hdr_or_tbstr,tb_str,NOSCALE=noscale
+ 8: tb_str = hdr_or_tbstr
+ else: message,'ERROR - Invalid FITS header or structure supplied' 
+ endcase 
+
+ tfields = N_elements(tb_str.ttype)
+
+ case size(field,/TNAME) of
+
+ 'STRING': begin
+      i = where( strupcase(tb_str.ttype) EQ strupcase(field), Nfound)
+      if Nfound EQ 0 then $ 
+         message,'Field ' + field + ' not found in header'
+      i=i[0]
+      end
+
+ 'UNDEFINED':message,'First parameter must be field name or number'
+ 
+ ELSE: begin
+      i = field[0]-1
+      if (i LT 0 ) || (i GT tfields) then $
+            message,'Field number must be between 1 and ' +strtrim(tfields,2)
+      end
+
+ endcase
+
+; Now that the right column has been found, extract necessary info about this
+; column 
+
+ ttype = tb_str.ttype[i]
+ numval = tb_str.numval[i]
+ tform = tb_str.tform[i]
+ tbcol = tb_str.tbcol[i]
+ width = tb_str.width[i]
+ idltype = tb_str.idltype[i]
+ tnull = tb_str.tnull[i]
+
+ if numval EQ 0 then begin 
+        message,/INF, 'Column ' + ttype + ' has zero width'
+        return, -1
+ endif
+
+ if tform EQ 'P' then message, /INF, $ 
+           'Variable Length column - returning array of pointers'
+
+; if rows not supplied then return all rows
+
+ if N_params() LT 4 then rows = -1
+
+; determine if scalar supplied
+
+ row = rows
+ ndim = size(row,/N_dimen)  
+ if row[0] LT 0 then nrow = nrows else  begin
+     nrow = N_elements(row)
+                                              ; check for valid row numbers
+     if (min(row) LT 0) || (max(row) GT (nrows-1)) then $
+        message,'ERROR - Invalid row number: FITS table contains '+ $
+        strtrim(nrows,2) + ' rows'
+ endelse 
+; get column
+
+ if row[0] LT 0 then $                                 ;All rows?
+        d = tab[tbcol:tbcol + numval*width-1,*]  $
+  else if ndim EQ 0 then  $                              ;scalar?                                               
+        d = tab[tbcol:tbcol + numval*width-1,row[0]] $
+  else $                                        ;vector of rows
+        d = tab[tbcol:tbcol + numval*width-1,row]
+ Nnull = 0
+; convert data to the correct type
+
+ case idltype of
+
+ 1:  begin
+     temp = byte( d, 0, numval, nrow)
+     if tform EQ 'L' then begin
+       d = strarr( numval, nrow )
+       for j = 0, numval*nrow-1 do d[j] = string( temp[j] )
+     endif else if tnull NE 0 then nullval = where(d EQ tnull, Nnull)
+     end
+
+ 2:  begin
+     byteorder,d,/NTOHS, /SWAP_IF_LITTLE
+     d = fix(d,0, numval, nrow)
+     if tnull NE 0 then nullval = where(d EQ tnull, Nnull)
+     end
+ 
+ 3:  begin
+     byteorder,d,/NTOHL, /SWAP_IF_LITTLE
+     d = long( d, 0, numval, nrow)
+     if tnull NE 0 then nullval = where(d EQ tnull, Nnull)
+     end
+
+ 4:  begin
+     d = float( d, 0, numval, nrow)
+     byteorder,d,/LSWAP, /SWAP_IF_LITTLE
+     end
+
+ 5:  begin
+     d = double( d, 0, numval, nrow)
+     byteorder,d,/L64SWAP, /SWAP_IF_LITTLE
+      end
+
+ 6:  begin
+     d = complex( d, 0, numval, nrow)
+     byteorder,d,/LSWAP, /SWAP_IF_LITTLE
+     end
+
+ 7:  d = string(d)
+
+
+ 14: begin
+     d = long64(d, 0, numval, nrow)
+     byteorder, d, /L64swap, /SWAP_IF_LITTLE
+     end
+
+ endcase
+
+
+ if ~keyword_set(NOSCALE) then begin
+    if tag_exist(tb_str,'TSCAL') then begin
+        tscale = *tb_str.tscal[i]
+        tzero = *tb_str.tzero[i]
+        unsgn_int = (tzero EQ 32768) && (tscale EQ 1)
+        unsgn_lng = (tzero EQ 2147483648) && (tscale EQ 1)
+        if unsgn_int then d = uint(d) - uint(32768) $
+        else if unsgn_lng then d = ulong(d) - ulong(2147483648) else $
+        if ( (tscale NE 1.0) or (tzero NE 0.0) ) then $
+                d = temporary(d)*tscale + tzero
+	endif	
+ endif
+
+ if N_params() EQ 5 then begin
+         nulls = bytarr(N_elements(d))
+         if Nnull GT 0 then begin
+                nulls[nullval] = 1b
+                d[nullval] = 0
+        endif
+ endif  
+
+; Extract correct rows if vector supplied
+
+ if size(hdr_or_tbstr,/TYPE) NE 8 && (~keyword_set(NOSCALE)) then begin
+       ptr_free, tb_str.tscal
+       ptr_free, tb_str.tzero
+ endif       
+
+ if N_elements(d) EQ 1 then return, d[0] else return, reform(d,/overwrite)
+ 
+
+ end
diff --git a/Code/script_idl_mv/astrolib/tbhelp.pro b/Code/script_idl_mv/astrolib/tbhelp.pro
new file mode 100644
index 0000000000000000000000000000000000000000..64db8c8c6dc22dc9c451cb319dbc99d8ec7d64cc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tbhelp.pro
@@ -0,0 +1,132 @@
+pro tbhelp,h, TEXTOUT = textout
+;+
+; NAME:
+;       TBHELP
+; PURPOSE:
+;       Routine to print a description of a FITS binary table header
+;
+; CALLING SEQUENCE:
+;       TBHELP, h, [TEXTOUT = ]
+;
+; INPUTS:
+;       h - FITS header for a binary table, string array
+;
+; OPTIONAL INPUT KEYWORD:
+;       TEXTOUT - scalar number (0-7) or string (file name) controling 
+;               output device (see TEXTOPEN).  Default is TEXTOUT=1, output 
+;               to the user's terminal    
+;
+; METHOD:
+;       FITS Binary Table keywords NAXIS*,EXTNAME,TFIELDS,TTYPE*,TFORM*,TUNIT*,
+;       are read from the header and displayed at the terminal
+;
+;       A FITS header is recognized as bein for a binary table if the keyword 
+;       XTENSION has the value 'BINTABLE' or 'A3DTABLE'
+;
+; NOTES:
+;       Certain fields may be truncated in the display
+; SYSTEM VARIABLES:
+;       Uses the non-standard system variables !TEXTOUT and !TEXTUNIT.   These
+;       are automatically defined by TBHELP if they have not been defined
+;       previously. 
+; PROCEDURES USED:
+;       REMCHAR, SXPAR(), TEXTCLOSE, TEXTOPEN, ZPARCHECK 
+; HISTORY:
+;       W. Landsman       February, 1991
+;       Parsing of a FITS binary header made more robust    May, 1992
+;       Added TEXTOUT keyword      August 1997
+;       Define !TEXTOUT if not already present   W. Landsman  November 2002
+;       Slightly more compact display   W. Landsman August 2005
+;       Fix Aug 2005 error omitting TFORM display W. Landsman Sep 2005
+;-
+ compile_opt idl2
+ On_error,2
+
+ if N_params() LT 1 then begin
+     print,'Syntax - tbhelp, hdr, [TEXTOUT= ]'     
+     return
+ endif
+; Define !TEXTOUT and !TEXTUNIT if not already present
+ defsysv,'!TEXTOUT',exists=ex                  ; Check if !TEXTOUT exists.
+ if ex eq 0 then defsysv,'!TEXTOUT',1          ; If not define it.
+ defsysv,'!TEXTUNIT',exists=ex                 ; Check if !TEXTUNIT exists.
+ if ex eq 0 then defsysv,'!TEXTUNIT',0         ; If not define it.
+
+ zparcheck, 'TBHELP', h, 1, 7, 1, 'Table Header'
+
+ naxis = sxpar( h, 'NAXIS*')
+ if N_elements(naxis) LT 2 then $
+         message,'ERROR - FITS Binary table must have NAXIS = 2'
+
+ ext_type = strmid( strtrim( sxpar( h, 'XTENSION'), 2 ), 0, 8)
+ if (ext_type NE 'A3DTABLE') && (ext_type NE 'BINTABLE') then message, $
+ 'WARNING - Header type of ' + ext_type + ' is not for a FITS Binary Table',/CON
+
+ n = sxpar( h, 'TFIELDS', Count = N_tfields)  
+ if N_tfields EQ 0 then message, $
+        'ERROR - Required TFIELDS keyword is missing from binary table header'
+
+ tform = sxpar(h,'TFORM*', Count = N_tform)      ;Get required TFORM* values
+ n = n > N_tform
+ 
+ if ~keyword_set(TEXTOUT) then textout = !TEXTOUT
+ textopen,'tbhelp',TEXTOUT=textout
+
+ printf,!TEXTUNIT,'FITS Binary Table: ' + $
+        'Size ',strtrim(naxis[0],2),' by ',strtrim(naxis[1],2)
+ extname = sxpar(h,'EXTNAME', Count=N_ext)	
+ if N_ext GT 0 then printf,!TEXTUNIT, 'Extension Name:   ',sxpar(h,'EXTNAME')
+
+ tnull =  strarr(n)
+ tunit = tnull & ttype =tnull & tcomm = tnull
+ key = strmid( h, 0, 5)
+ for i = 1, N_elements(h)-1 do begin
+
+ case key[i] of
+ 'TTYPE':   begin
+           j = fix(strtrim(strmid(h[i],5,3),2))
+          apos = strpos( h[i], "'") 
+          ttype[j-1] = strmid( h[i], apos+1, 20)
+          slash = strpos(h[i],'/')
+          if slash GT 0 then $
+              tcomm[j-1] = strcompress( strmid(h[i], slash+1, 55))
+          end
+
+ 'TUNIT':  begin 
+          apos = strpos( h[i], "'") 
+          tunit[fix(strtrim(strmid(h[i],5,3),2))-1] = strmid(h[i],apos+1,20)
+          end
+ 'TNULL':  begin
+          tnull[fix(strtrim(strmid(h[i],5,3),2))-1] = $
+                                 strtrim( strmid( h[i], 10, 20 ),2)
+          end
+ 'END  ':  goto, DONE 
+ ELSE :
+ endcase
+ endfor
+
+DONE:
+ remchar,ttype,"'" & ttype = strtrim(ttype,2)
+ remchar,tunit,"'" & tunit = strtrim(tunit,2)
+ tform = strtrim(tform,2)
+ remchar,tnull,"'" & tnull = strtrim(tnull,2)
+ len_ttype = strtrim( max(strlen(ttype)) > 4,2)
+ len_tunit = strtrim( max(strlen(tunit)) > 4,2)
+ len_tform = strtrim( max(strlen(tform)) > 4,2)
+ len_tnull = strtrim( max(strlen(tnull)) > 4,2)
+
+
+ fmt = '(A5,1x,A' + len_ttype +',1x,A' + len_tunit + ',1x,A' + len_tform + $
+        ',1x,A' + len_tnull +',1x,A)'
+
+ printf,!TEXTUNIT,'Field','Name','Unit','Frmt','Null','Comment',f=fmt
+ 
+ field = strtrim(sindgen(n)+1,2)
+ for i=0,n-1 do begin 
+        printf,!TEXTUNIT,field[i],ttype[i],tunit[i],tform[i],tnull[i],tcomm[i], $
+                format=fmt
+ endfor
+
+ textclose, TEXTOUT = textout
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/tbinfo.pro b/Code/script_idl_mv/astrolib/tbinfo.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0d2c8c205703abbafc71dcd1abd4cdd132c3c5e0
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tbinfo.pro
@@ -0,0 +1,192 @@
+pro tbinfo,h,tb_str, errmsg = errmsg, NOSCALE= noscale
+;+
+; NAME:
+;       TBINFO
+; PURPOSE:
+;       Return an informational IDL structure from a FITS binary table header.
+;
+; CALLING SEQUENCE:
+;       tbinfo, h, tb_str, [ERRMSG = ]
+; INPUTS:
+;       h - FITS binary table header, e.g. as returned by READFITS()
+;
+; OUTPUTS:
+;       tb_str - IDL structure with extracted info from the FITS binary table
+;               header.   Tags include
+;       .tbcol - starting column position in bytes, integer vector
+;       .width - width of the field in bytes, integer vector
+;       .idltype - idltype of field, byte vector
+;               7 - string, 4- real*4, 3-integer*4, 5-real*8
+;       .numval - repeat count, 64 bit longword vector
+;       .tunit - string unit numbers, string vector
+;       .tnull - integer null value for the field, stored as a string vector
+;                 so that an empty string indicates that TNULL is not present
+;       .tform - format for the field, string vector
+;       .ttype - field name, string vector
+;       .maxval- maximum number of elements in a variable length array, long
+;               vector
+;       .tscal - pointer array giving the scale factor for converting to 
+;                physical values, default 1.0
+;       .tzero - pointer array giving the additive offset for converting to 
+;                physical values, default 0.0
+;       .tdisp - recommended output display format
+;
+;       All of the output vectors will have same number of elements, equal
+;       to the number of columns in the binary table.
+;
+;       The .tscal and .tzero values are stored as pointers so as to preserve
+;       the individual data types (e.g. float or double) which may differ 
+;       in different columns.   For example, to obtain the value of TSCAL for
+;       the third column use *tab_str.tscal[2]  
+; OPTIONAL INPUT KEYWORD:
+;       /NOSCALE - if set, then the TSCAL* and TZERO* keywords are not extracted
+;            from the FITS header, and the .tscal and .tzero pointers do not
+;            appear in the output structure.
+; OPTIONAL OUTPUT KEYWORD:
+;        ERRMSG = if present, then error messages are returned in this keyword
+;            rather than displayed using the MESSAGE facility 
+; PROCEDURES USED:
+;       SXPAR()
+; NOTES:
+;       For variable length ('P' format) column, TBINFO returns values for
+;       reading the 2 element longward array of pointers (numval=2, 
+;       idltype = 3, width=4)
+; HISTORY:
+;       Major rewrite to return a structure      W. Landsman   August 1997
+;       Added "unofficial" 64 bit integer "K" format W. Landsamn Feb. 2003
+;       Store .tscal and .tzero tags as pointers, so as to preserve 
+;       type information   W. Landsman          April 2003
+;       Treat repeat count for string as specifying string length, not number
+;          of elements, added ERRMSG    W. Landsman        July 2006
+;       Treat logical as character string 'T' or 'F' W. Landsman  October 2006
+;       Added NOSCALE keyword  W. Landsman   March 2007
+;       Make .numval 64 bit for very large tables  W. Landsman   April 2014
+;-
+;----------------------------------------------------------------------------
+ On_error,2
+ compile_opt idl2
+ if N_params() LT 2 then begin
+        print,'Syntax - TBINFO, h, tb_str, [ERRMSG=, /NOSCALE]'
+        return
+ endif
+ save_err = arg_present(errmsg)
+
+; get number of fields
+
+ tfields = sxpar( h, 'TFIELDS', COUNT = N_TFields)
+ if N_TFields EQ 0 then begin    ;Legal Binary Table Header?
+        errmsg = 'Invalid FITS binary table header. keyword TFIELDS is missing'
+	if ~save_err then message,errmsg else return
+   endif	    
+
+ if tfields EQ 0 then begin     ;Any fields in table?
+        errmsg = 'No Columns in FITS binary table, keyword TFIELDS = 0'
+	if ~save_err then message,errmsg else return
+  endif	    
+ 
+; Create output arrays with default values
+
+ idltype = intarr(tfields) & tnull = idltype
+ numval = lon64arr(tfields) & tbcol = numval & width = numval & maxval = numval
+ tunit = replicate('',tfields) & ttype = tunit & tdisp = tunit & tnull = tunit
+
+ type = sxpar(h,'TTYPE*', COUNT = N_ttype)
+ if N_ttype GT 0 then ttype[0] = strtrim(type,2) 
+
+ tform = strtrim( sxpar(h,'tform*', COUNT = N_tform), 2)     ; column format
+ if N_tform EQ 0 then $
+        message,'Invalid FITS table header -- keyword TFORM not present
+ tform =  strupcase(strtrim(tform,2))
+                                                
+ unit = strtrim(sxpar(h, 'TUNIT*', COUNT = N_tunit),2)     ;physical units
+ if N_tunit GT 0 then tunit[0] = unit
+
+ null = sxpar(h, 'TNULL*', COUNT = N_tnull)      ;null data value
+ if N_tnull GT 0 then tnull[0] = null
+
+ if ~keyword_set(noscale) then begin
+  tscal = ptrarr(tfields,/all)
+  tzero = ptrarr(tfields,/all)
+  index = strtrim(indgen(tfields)+1,2)
+  for i=0,tfields-1 do begin
+    scale = sxpar(h,'TSCAL' + index[i], COUNT = N_tscal)     ;Scale factor
+    if N_tscal GT 0 then *tscal[i] = scale else *tscal[i] = 1.0
+    zero = sxpar(h,'TZERO' + index[i], Count = N_tzero)
+    if N_tzero GT 0 then *tzero[i] = zero else *tzero[i] = 0
+  endfor
+ endif  
+
+ disp = sxpar(h,'TDISP*', COUNT = N_tdisp)       ;Display format string
+ if N_tdisp GT 0 then tdisp[0] = disp
+
+; determine idl data type from format
+
+ len = strlen(tform)
+
+ for i = 0, N_elements(tform)-1 do begin
+
+; Step through each character in the format, until a non-numerical character
+; is encountered
+
+        ichar = 0
+NEXT_CHAR:
+        if ichar GE len[i] then message, $
+           'Invalid format specification for keyword TFORM ' + strtrim(i+1)
+        char = strupcase( strmid(tform[i],ichar,1) )
+        if ( (char GE '0') && ( char LE '9')) then begin
+                ichar++
+                goto, NEXT_CHAR
+        endif
+
+        if ichar EQ 0 then numval[i] = 1 else $
+        numval[i] = strmid( tform[i], 0, ichar )
+
+        if char EQ "P" then begin            ;Variable length array?
+                char = strupcase( strmid(tform[i],ichar+1,1) )
+                maxval[i] = long( strmid(tform[i],ichar+3, len[i]-ichar-4) )
+                width[i] = 4  & numval[i] = 2  & idltype[i] = 3
+        endif else begin
+
+        tform[i] =  char
+
+        case strupcase( tform[i] ) of
+
+        'A' : begin 
+	      idltype[i] = 7 &  width[i] = numval[i] & numval[i]=1 
+	      end
+        'I' : begin & idltype[i] = 2 &  width[i] = 2 &  end
+        'J' : begin & idltype[i] = 3 &  width[i] = 4 &  end
+        'E' : begin & idltype[i] = 4 &  width[i] = 4 &  end
+        'D' : begin & idltype[i] = 5 &  width[i] = 8 &  end
+        'L' : begin & idltype[i] = 7 &  width[i] = 1 &  end
+        'B' : begin & idltype[i] = 1 &  width[i] = 1 &  end
+        'C' : begin & idltype[i] = 6 &  width[i] = 8 &  end
+        'M' : begin & idltype[i] = 9 &  width[i] =16 &  end
+        'K' : begin & idltype[i] = 14 & width[i] = 8 &  end
+;  Treat bit arrays as byte arrays with 1/8 the number of elements.
+
+        'X' : begin
+              idltype[i] = 1
+              numval[i] = long((numval[i]+7)/8)
+              width[i] = 1
+              end
+
+        else : message,'Invalid format specification for keyword ' + $
+                        'TFORM'+ strtrim(i+1,2)
+ endcase
+ endelse
+
+ if i ge 1 then tbcol[i] = tbcol[i-1] + width[i-1]*numval[i-1]
+
+ endfor
+ if keyword_set(noscale) then $ 
+
+  tb_str = {TBCOL:tbcol,WIDTH:width,IDLTYPE:idltype,NUMVAL:numval,TUNIT:tunit,$
+           TNULL:tnull,TFORM:tform,TTYPE:ttype,MAXVAL:maxval, TDISP:tdisp} $
+ else $
+ 
+ tb_str = {TBCOL:tbcol,WIDTH:width,IDLTYPE:idltype,NUMVAL:numval,TUNIT:tunit,$
+           TNULL:tnull,TFORM:tform,TTYPE:ttype,MAXVAL:maxval, TSCAL:tscal, $
+           TZERO:tzero, TDISP:tdisp}
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/tbprint.pro b/Code/script_idl_mv/astrolib/tbprint.pro
new file mode 100644
index 0000000000000000000000000000000000000000..dcebebf74f9afec10654d5a4a97870d640629c40
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tbprint.pro
@@ -0,0 +1,307 @@
+pro tbprint,hdr_or_tbstr,tab,columns,rows,textout=textout,fmt=fmt, $
+            num_header_lines=num_header_lines,nval_per_line=nval_per_line
+;+
+; NAME:
+;       TBPRINT
+;  PURPOSE:
+;       Procedure to print specified columns & rows of a FITS binary table
+;
+; CALLING SEQUENCE:
+;       TBPRINT, h, tab, columns, [ rows, TEXTOUT =, FMT=, NUM_HEADER= ]
+;               or
+;       TBPRINT,tb_str, tab, columns, [ rows, TEXTOUT =, FMT=, NUM_HEADER =  ]
+;
+; INPUTS:
+;       h - FITS header for table, string array
+;                       or
+;       tb_str - IDL structure extracted from FITS header by TBINFO, useful 
+;           when TBPRINT is called many times with the same header
+;       tab - table array 
+;       columns - string giving column names, or vector giving
+;               column numbers (beginning with 1).  If string 
+;               supplied then column names should be separated by comma's.
+;               If set to '*' then all columns are printed in table format 
+;               (1 row per line, binary tables only).
+;       rows - (optional) vector of row numbers to print.  If
+;               not supplied or set to scalar, -1, then all rows
+;               are printed.
+;
+; OUTPUTS:
+;       None
+; OPTIONAL INPUT KEYWORDS:
+;       FMT = Format string for print display.   If not supplied, then any 
+;               formats in the TDISP keyword fields of the table will be
+;               used, otherwise IDL default formats.   
+;       NUM_HEADER_LINES - Number of lines to display the column headers 
+;               default = 1).  By setting NUM_HEADER_LINES to an integer larger
+;               than 1, one can avoid truncation of the column header labels.  
+;               In addition, setting NUM_HEADER_LINES will display commented 
+;               lines indicating a FORMAT for reading the data, and a 
+;               suggested call to  readfmt.pro.
+;       NVAL_PER_LINE - The maximum number of values displayed from a multivalued
+;               column when printing in table format.   Default = 6
+;       TEXTOUT - scalar number (0-7) or string (file name) determining
+;               output device (see TEXTOPEN).  Default is TEXTOUT=1, output 
+;               to the user's terminal    
+; SYSTEM VARIABLES:
+;       Uses nonstandard system variables !TEXTOUT and !TEXTOPEN
+;       Set !TEXTOUT = 3 to direct output to a disk file.   The system
+;       variable is overriden by the value of the keyword TEXTOUT
+;
+; EXAMPLES:
+;       tab = readfits('test.fits',htab,/ext) ;Read first extension into vars
+;       tbprint,h,tab,'STAR ID,RA,DEC'    ;print id,ra,dec for all stars
+;       tbprint,h,tab,[2,3,4],indgen(100) ;print columns 2-4 for 
+;                                          first 100 stars
+;       tbprint,h,tab,text="stars.dat"    ;Convert entire FITS table to
+;                                         ;an ASCII file named 'stars.dat'
+;
+; PROCEDURES USED:
+;       GETTOK(), STRNUMBER(), TEXTOPEN, TEXTCLOSE, TBINFO
+;
+; RESTRICTIONS: 
+;       (1) Program does not check whether output length exceeds output
+;               device capacity (e.g. 80 or 132).
+;       (2) Column heading may be truncated to fit in space defined by
+;               the FORMAT specified for the column.    Use NUM_HEADER_LINES
+;               to avoid truncation.
+;       (3) Program does not check for null values
+;       (4) Does not work with variable length columns
+;       (5) Will only the display the first value of fields with multiple values
+;        (unless there is one row each with the same number of mulitple values)
+;        If printing in table format (column='*') then up to 6 values
+;        can be printed per line.
+;
+; HISTORY:
+;       version 1  D. Lindler Feb. 1987
+;       Accept undefined values of rows,columns W. Landsman  August 1997
+;       Use new structure returned by TBINFO    W. Landsman  August 1997
+;       Made formatting more robust    W. Landsman   March 2000
+;       Use STRSPLIT to parse string column listing W. Landsman July 2002
+;       Wasn't always printing last row   W. Landsman  Feb. 2003
+;       Better formatting (space between columns) W. Landsman Oct. 2005
+;       Use case-insensitive match with TTYPE, use STRJOIN W.L. June 2006
+;       Fixed check for multiple values W.L. August 2006
+;       Fixed bad index value in August 2006 fix  W.L Aug 15 2006
+;       Free-up pointers after calling TBINFO  W.L. Mar 2007
+;       Add table format capability  W.L. Mar 2010
+;       Add NUM_HEADER_LINE keyword  P. Broos Apr 2010
+;-
+ On_error,2
+ compile_opt idl2
+
+ if N_params() LT 2 then begin
+   print,'Syntax -  TBPRINT, h, tab, [ columns, rows, device, '
+   print,'              TEXTOUT= ,FMT=, NUM_HEADER_LINES= '
+   return
+ endif
+
+; set default parameters
+
+ if N_elements(columns) EQ 0 then columns = -1
+ if N_elements(rows) EQ 0 then rows= -1
+ if ~keyword_set(textout) then textout = 1
+ if N_elements(nval_per_line) EQ 0 then $
+     nval_per_line = 6     ;Number of  values that can be displayed in 'table' format
+ 
+ nbytes = [1,2,4,4,8,8,1,0,16]
+ fmt_def = ['','I4','I8','I12','G13.6','G16.8','','A','','','','']
+
+; make sure rows is a vector
+
+ sz = size(tab)
+ nrows = sz[2]
+ r = long(rows)
+ if r[0] eq -1 then r = lindgen(nrows)          ;default
+ n = N_elements(r)
+ dotable = n EQ 1         ;Print in table format?
+
+; Did user supply a FITS header, or a structure (output of tbinfo)?
+
+ case  size(hdr_or_tbstr,/type) of 
+ 7: tbinfo,hdr_or_tbstr,tb_str
+ 8: tb_str = hdr_or_tbstr
+ else: message,'ERROR - Invalid FITS header or structure supplied' 
+ endcase 
+ 
+ tfields = N_elements(tb_str.ttype)
+
+; if columns is a string, change it to string array
+
+ if size(columns,/tname) eq 'STRING' then begin
+        if columns[0] EQ '*' then begin       
+	colnum = indgen(tfields) + 1 
+	numcol = tfields
+	dotable = 1 
+	endif else begin 
+        colnames = strsplit(columns,',',/extract) 
+        numcol = N_elements(colnames) 
+        colnum = intarr(numcol)
+        field = strupcase(colnames)
+        for i = 0,numcol-1 do begin 
+        colnum[i] = where(strupcase(tb_str.ttype) EQ field[i],nfound) + 1
+        if nfound EQ 0 then $ 
+           message,'Field '+ field[i] + ' not found in header'
+       endfor
+       endelse
+   endif else begin                       ;user supplied vector
+        colnum = fix(columns)           ;make sure it is integer
+        if colnum[0] eq -1 then colnum = indgen(tfields) + 1 
+        numcol = N_elements(colnum)     ;number of elements
+ endelse
+
+ if ~keyword_set(fmt) then form = tb_str.tdisp[colnum-1] else begin
+        if N_elements(fmt) EQ 1 && (numcol GT 1) then begin
+                temp = strupcase(strtrim(fmt,2))
+                if strmid(temp,0,1) EQ '(' then $
+                        temp = strmid(temp,1,strlen(temp)-2)
+                        form = strarr(numcol)
+                        ifmt = 0
+                         while strtrim(temp,2) NE ''  do begin
+                                tstform = gettok(temp,',')
+                                ndup = 1
+                                vtype = strmid(tstform,0,1)
+                                if strnumber(vtype,val) then begin
+                                        ndup = val
+                                        tstform = strmid(tstform,1,100)
+                                endif
+                                if strpos(tstform,'X') LT 0 then begin
+                                     form[ifmt:ifmt+ndup-1]=tstform
+                                     ifmt += ndup
+                                endif
+                        endwhile
+        endif else form = fmt
+ endelse
+
+ default = where(form EQ '',Ndef)
+ if Ndef GT 0 then form[default] = fmt_def[ tb_str.idltype[colnum[default]-1] ]
+  form = strtrim(form,2)
+ row_format = strjoin(form,',1x,')
+
+ num = where(tb_str.idltype[colnum-1] NE 7, Nnumeric)
+ if Nnumeric GT 0 then minnumval = min(tb_str.numval[colnum[num]-1]) $
+ else minnumval = 1
+
+ if (minnumval GT 1) then begin 
+        if rows[0] NE -1 then nrow1 = N_elements(rows)-1 else begin
+                rows = lindgen(minnumval)
+                nrow1 = minnumval-1
+        endelse
+        
+ endif
+
+ textopen,'TBPRINT', TEXTOUT = textout
+
+ field = tb_str.ttype[colnum-1]
+  fieldlen = strlen(field)
+
+;Print in table format?
+  dotable = dotable || (n EQ 1)  && (minnumval LE nval_per_line)   
+  if dotable then begin 
+  maxlen = max(fieldlen)
+  
+  for j = 0, n-1 do begin 
+  printf,!TEXTUNIT,'ROW: ',r[j]
+  for i = 0, numcol-1 do begin
+      val =  tbget(tb_str,tab,colnum[i],r[j])
+      nval = N_elements(val)
+      if nval GT 1 then begin            ;Print up to 5 values
+           val = strcompress(strjoin(val[0:(nval-1)< (nval_per_line-1)],' '))
+	   if nval GT nval_per_line then val = val + '...'
+      endif	   
+      printf,!TEXTUNIT, colnum[i],') ', field[i],strtrim(string(val,/pr),2),$
+          f='(i3,A,A-' + strtrim(maxlen+2,2) + ',A)'
+  endfor
+     printf,!TEXTUNIT, ' '
+  endfor
+
+  endif else begin     
+ 
+
+ varname = 'v' + strtrim(sindgen(numcol)+1,2)
+ len = lonarr(numcol)
+ varstr = varname + '[0]'
+ xform = '(' + form + ')'
+ for i = 0,numcol-1 do begin
+        result = execute(varname[i] + '= tbget(tb_str,tab,colnum[i],r)' )
+        result = execute('len[i] = strlen(string(' + varstr[i] + ',f=xform[i]))')
+ endfor
+ 
+ 
+ if keyword_set(num_header_lines) then begin
+   ;; Build a multi-line header showing the column names left-justified.
+   header = strarr(num_header_lines+1)
+   
+; The printed data columns are separated by a space, so the column widths are actually (len+1).
+   column_width = len + 1
+   for ii=0,numcol-1 do begin
+     header_ind = ii MOD num_header_lines
+     
+     ; Pad the start of the header lines as needed.
+     if ((ii GT 0) && (ii LT num_header_lines)) then header[header_ind] += string(replicate(32B, total(column_width[0:ii-1], /INT)))
+     
+     if ((ii+num_header_lines) LT numcol) then begin
+       ; The space we have to print this label is the width of the next num_header_lines columns, minus one space for the '|' separator..
+       ; Put the label at the LEFT end of this space.
+       label_length = total(column_width[ii : ii+num_header_lines-1], /INT) - 1
+       label_format_code  = string(label_length, F='(%"|%%-%ds")')
+     endif else begin
+       ; We're at the end of the header line, so print this last label without truncation.
+       label_format_code  = '|%s'
+     endelse
+     header[header_ind] += string(field[ii], F='(%"'+label_format_code+'")')
+   endfor ; ii
+   
+   printf,!TEXTUNIT, "# FORMAT='" + row_format + "'"
+   printf,!TEXTUNIT, 3+num_header_lines+1, strjoin(field,','), F='(%"# readfmt, ''table.txt'', SKIPLINE=%d, FORMAT, %s")' 
+   printf,!TEXTUNIT, "#"
+
+   header[num_header_lines] = string(replicate(byte('-'), max(strlen(header))))
+   strput, header, '#', 0
+   forprint, TEXTOUT=5, header, /NoComment
+   
+ endif else begin
+   ;; Build a single-line header showing the column names centered on the columns.
+   field = strtrim(tb_str.ttype[colnum-1],2)
+   fieldlen = strlen(field)
+   for i=0,numcol-1 do begin
+          if fieldlen[i] LT len[i] then begin
+            space = len[i] - fieldlen[i]
+      if space EQ 1 then field[i] = field[i]+ ' ' else begin
+                     pad = string(replicate(32b,space/2))
+                     field[i] = pad + field[i] + pad
+         if space mod 2 EQ 1 then field[i] = field[i] + ' '
+      endelse   
+          endif else field[i] = strmid(field[i],0,len[i])
+   endfor
+   printf,!TEXTUNIT,field
+ endelse
+ 
+ 
+ if size(hdr_or_tbstr,/TYPE) NE 8  then begin
+       ptr_free, tb_str.tscal
+       ptr_free, tb_str.tzero
+ endif
+
+
+
+; If there are multiple values then only print the first value....
+
+  if minnumval EQ 1 then begin        
+       index = replicate('[i]',numcol)
+       g = where( tb_str.numval[colnum-1] GT 1,Ng) 
+       if Ng GT 0 then index[g] = '[0,i]'  
+       vstring  = strjoin(varname + index,',')
+  endif else  vstring = strjoin(varname + '[i]',',') 
+
+ row_format = '(' + row_format + ')'
+
+ if minnumval EQ 1 then $
+ result = execute('for i=0,n-1 do printf,!TEXTUNIT,' +  $
+                   vstring + ',f=row_format') else $
+ result = execute('for i=rows[0],rows[nrow1] do printf,!TEXTUNIT,' +  $
+                   vstring + ',f=fmt') 
+ endelse		   
+ textclose, TEXTOUT = textout
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/tbsize.pro b/Code/script_idl_mv/astrolib/tbsize.pro
new file mode 100644
index 0000000000000000000000000000000000000000..36dc68d47a4c3de718edce3cef3d28de26b30e56
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tbsize.pro
@@ -0,0 +1,63 @@
+pro tbsize, h, tab, ncols, nrows, tfields, ncols_all, nrows_all
+;+
+; NAME:
+;       TBSIZE
+;
+; PURPOSE:
+;       Procedure to return the size of a FITS binary table.
+;
+; CALLING SEQUENCE:
+;       tbsize, h, tab, ncols, nrows, tfields, ncols_all, nrows_all
+;
+; INPUTS:
+;       h - FITS table header
+;       tab - FITS table array
+;
+; OUTPUTS:
+;       ncols - number of characters per row in table
+;       nrows - number of rows in table
+;       tfields - number of fields per row
+;       ncols_all - number of characters/row allocated (size of tab)
+;       nrows_all - number of rows allocated
+; PROCEDURES USED:
+;       SXPAR()
+; HISTORY
+;       D. Lindler  July, 1987
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Remove obsolete !ERR call   W. Landsman   May 2000
+;-
+;------------------------------------------------------------------------
+ On_error,2
+
+; check for valid header type
+
+ s=size(h) & ndim=s[0] & type=s[ndim+1]
+ if (ndim NE 1) or (type ne 7) then $
+        message,'Invalid FITS header, it must be a string array'
+
+; check for valid table array
+
+ s = size(tab) & ndim = s[0] & type = s[ndim+1]
+ if (ndim gt 2) or (type ne 1) or (ndim lt 1) then $
+        message,'Invalid table array, it must be a 2-D byte array'
+
+ ncols_all = s[1]                       ;allocated characters per row
+ nrows_all = s[2]                       ;allocated rows
+
+;
+; get number of fields
+;
+ tfields = sxpar( h, 'TFIELDS', Count = N_tfields )
+ if N_tfields EQ 0 then $
+        message,'Invalid FITS table header, TFIELDS keyword missing'
+
+;
+; get number of columns and rows
+;
+ ncols = sxpar(h, 'NAXIS1' )
+ nrows = sxpar(h, 'NAXIS2' )
+ if ( ncols GT ncols_all ) or ( nrows GT nrows_all ) then message, $
+  'WARNING - Size information in header does not match that in array',/CON
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/tdb2tdt.pro b/Code/script_idl_mv/astrolib/tdb2tdt.pro
new file mode 100644
index 0000000000000000000000000000000000000000..86e1e1c5b06613855786110a04969b9e56e67b7e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tdb2tdt.pro
@@ -0,0 +1,1071 @@
+;+
+; NAME:
+;   TDB2TDT
+;
+; AUTHOR:
+;   Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
+;   craigm@lheamail.gsfc.nasa.gov
+;   UPDATED VERSIONs can be found on my WEB PAGE: 
+;      http://cow.physics.wisc.edu/~craigm/idl/idl.html
+;
+; PURPOSE:
+;   Relativistic clock corrections due to Earth motion in solar system 
+;
+; MAJOR TOPICS:
+;   Planetary Orbits
+;
+; CALLING SEQUENCE:
+;   corr = TDB2TDT(JD, TBASE=, DERIV=deriv)
+;
+; DESCRIPTION:
+;
+;   The function TDB2TDT computes relativistic corrections that must
+;   be applied when performing high precision absolute timing in the
+;   solar system.
+;
+;   According to general relativity, moving clocks, and clocks at
+;   different gravitational potentials, will run at different rates
+;   with respect to each other.  A clock placed on the earth will run
+;   at a time-variable rate because of the non-constant influence of
+;   the sun and other planets.  Thus, for the most demanding
+;   astrophysical timing applications -- high precision pulsar timing
+;   -- times in the accelerating earth observer's frame must be
+;   corrected to an inertial frame, such as the solar system
+;   barycenter (SSB).  This correction is also convenient because the
+;   coordinate time at the SSB is the ephemeris time of the JPL
+;   Planetary Ephemeris.
+;
+;   In general, the difference in the rate of Ti, the time kept by an
+;   arbitrary clock, and the rate of T, the ephemeris time, is given
+;   by the expression (Standish 1998):
+;
+;      dTi/dT = 1 - (Ui + vi^2/2) / c^2
+;
+;   where Ui is the potential of clock i, and vi is the velocity of
+;   clock i.  However, when integrated, this expression depends on the
+;   position of an individual clock.  A more convenient approximate
+;   expression is:
+;
+;     T = Ti + (robs(Ti) . vearth(T))/c^2 + dtgeo(Ti) + TDB2TDT(Ti)
+;
+;   where robs is the vector from the geocenter to the observer;
+;   vearth is the vector velocity of the earth; and dtgeo is a
+;   correction to convert from the observer's clock to geocentric TT
+;   time.  TDB2TDT is the value computed by this function, the
+;   correction to convert from the geocenter to the solar system
+;   barycenter.
+;
+;   As the above equation shows, while this function provides an
+;   important component of the correction, the user must also be
+;   responsible for (a) correcting their times to the geocenter (ie,
+;   by maintaining atomic clock corrections); (b) estimating the
+;   observatory position vector; and and (c) estimating earth's
+;   velocity vector (using JPLEPHINTERP).
+;
+;   Users may note a circularity to the above equation, since
+;   vearth(T) is expressed in terms of the SSB coordinate time.  This
+;   appears to be a chicken and egg problem since in order to get the
+;   earth's velocity, the ephemeris time is needed to begin with.
+;   However, to the precision of the above equation, < 25 ns, it is
+;   acceptable to replace vearth(T) with vearth(TT).
+;
+;   The method of computation of TDB2TDT in this function is based on
+;   the analytical formulation by Fairhead, Bretagnon & Lestrade, 1988
+;   (so-called FBL model) and Fairhead & Bretagnon 1990, in terms of
+;   sinusoids of various amplitudes.  TDB2TDT has a dominant periodic
+;   component of period 1 year and amplitude 1.7 ms.  The set of 791
+;   coefficients used here were drawn from the Princeton pulsar timing
+;   program TEMPO version 11.005 (Taylor & Weisberg 1989).
+;
+;   Because the TDB2TDT quantity is rather expensive to compute but
+;   slowly varying, users may wish to also retrieve the time
+;   derivative using the DERIV keyword, if they have many times to
+;   convert over a short baseline.
+;
+; Verification
+;
+;   This implementation has been compared against a set of FBL test
+;   data found in the 1996 IERS Conventions, Chapter 11, provided by
+;   T. Fukushima.  It has been verified that this routine reproduces
+;   the Fukushima numbers to the accuracy of the table, within
+;   10^{-14} seconds.
+;
+;   Fukushima (1995) has found that the 791-term Fairhead & Bretagnon
+;   analytical approximation use here has a maximum error of 23
+;   nanoseconds in the time range 1980-2000, compared to a numerical
+;   integration.  In comparison the truncated 127-term approximation
+;   has an error of ~130 nanoseconds.
+;
+;
+; PARAMETERS: 
+;
+;   JD - Geocentric time TT, scalar or vector, expressed in Julian
+;        days.  The actual time used is (JD + TBASE).  For maximum
+;        precision, TBASE should be used to express a fixed epoch in
+;        whole day numbers, and JD should express fractional offset
+;        days from that epoch.
+;
+;
+; KEYWORD PARAMETERS:
+;
+;   TBASE - scalar Julian day of a fixed epoch, which provides the
+;           origin for times passed in JD.
+;          Default: 0
+;
+;   DERIV - upon return, contains the derivative of TDB2TDT in units
+;           of seconds per day.  As many derivatives are returned as
+;           values passed in JD.
+;
+;
+; RETURNS:
+;   The correction offset(s) in units of seconds, to be applied as
+;   noted above.
+;
+;
+; EXAMPLE:
+;
+;   Find the correction at ephemeris time 2451544.5 (JD):
+;     IDL> print, tdb2tdt(2451544.5d)
+;       -0.00011376314
+;   or 0.11 ms.
+;
+;
+; REFERENCES:
+;
+;   Princeton TEMPO Program
+;      http://tempo.sourceforge.net/tempo_idx.html
+;
+;   FBL Test Data Set
+;      ftp://maia.usno.navy.mil/conventions/chapter11/fbl.results
+;
+;   Fairhead, L. & Bretagnon, P. 1990, A&A, 229, 240
+;     (basis of this routine)
+;
+;   Fairhead, L. Bretagnon, P. & Lestrade, J.-F. 1988, in *The Earth's
+;     Rotation and Reference Frames for Geodesy and Geodynamics*,
+;     ed. A. K. Babcock and G. A. Wilkins, (Dordrecht: Kluwer), p. 419
+;     (original "FBL" paper)
+;
+;   Fukushima, T. 1995, A&A, 294, 895  (error analysis)
+;
+;   Irwin, A. W. & Fukushima, T. 1999, A&A, 348, 642  (error analysis)
+;
+;   Standish, E. M. 1998, A&A, 336, 381 (description of time scales)
+;
+;   Taylor, J. H. & Weisberg, J. M. 1989, ApJ, 345, 434 (pulsar timing)
+;
+;
+; SEE ALSO
+;   JPLEPHREAD, JPLEPHINTERP, JPLEPHTEST
+;   
+; MODIFICATION HISTORY:
+;   Original logic from Fairhead & Bretagnon, 1990
+;   Drawn from TEMPO v. 11.005, copied 20 Jun 2001
+;   Documented and vectorized, 30 Jun 2001
+;   
+;
+;  $Id: tdb2tdt.pro,v 1.4 2001/07/01 07:37:40 craigm Exp $
+;
+;-
+; Copyright (C) 2001, Craig Markwardt
+; This software is provided as is without any warranty whatsoever.
+; Permission to use, copy and distribute unmodified copies for
+; non-commercial purposes, and to modify and use for personal or
+; internal use, is granted.  All other rights are reserved.
+;-
+
+
+function tdb2tdt_calc, jd, deriv=deriv, tbase=tbase
+
+  common tdb2tdt_common, const0, freq0, phase0, texp
+  if n_elements(const0) EQ 0 then begin
+fbldata = [ $
+1656.674564d,   6283.075849991d, 6.240054195d, $
+  22.417471d,   5753.384884897d, 4.296977442d, $
+  13.839792d,  12566.151699983d, 6.196904410d, $
+   4.770086d,    529.690965095d, 0.444401603d, $
+   4.676740d,   6069.776754553d, 4.021195093d, $
+   2.256707d,    213.299095438d, 5.543113262d, $
+   1.694205d,     -3.523118349d, 5.025132748d, $
+   1.554905d,  77713.771467920d, 5.198467090d, $
+   1.276839d,   7860.419392439d, 5.988822341d, $
+   1.193379d,   5223.693919802d, 3.649823730d, $
+   1.115322d,   3930.209696220d, 1.422745069d, $
+   0.794185d,  11506.769769794d, 2.322313077d, $
+   0.447061d,     26.298319800d, 3.615796498d, $
+   0.435206d,   -398.149003408d, 4.349338347d, $
+   0.600309d,   1577.343542448d, 2.678271909d, $
+   0.496817d,   6208.294251424d, 5.696701824d, $
+   0.486306d,   5884.926846583d, 0.520007179d, $
+   0.432392d,     74.781598567d, 2.435898309d, $
+   0.468597d,   6244.942814354d, 5.866398759d, $
+   0.375510d,   5507.553238667d, 4.103476804d, $
+   0.243085d,   -775.522611324d, 3.651837925d, $
+   0.173435d,  18849.227549974d, 6.153743485d, $
+   0.230685d,   5856.477659115d, 4.773852582d, $
+   0.203747d,  12036.460734888d, 4.333987818d, $
+   0.143935d,   -796.298006816d, 5.957517795d  ]
+fbldata = [ fbldata, $
+   0.159080d,  10977.078804699d, 1.890075226d, $
+   0.119979d,     38.133035638d, 4.551585768d, $
+   0.118971d,   5486.777843175d, 1.914547226d, $
+   0.116120d,   1059.381930189d, 0.873504123d, $
+   0.137927d,  11790.629088659d, 1.135934669d, $
+   0.098358d,   2544.314419883d, 0.092793886d, $
+   0.101868d,  -5573.142801634d, 5.984503847d, $
+   0.080164d,    206.185548437d, 2.095377709d, $
+   0.079645d,   4694.002954708d, 2.949233637d, $
+   0.062617d,     20.775395492d, 2.654394814d, $
+   0.075019d,   2942.463423292d, 4.980931759d, $
+   0.064397d,   5746.271337896d, 1.280308748d, $
+   0.063814d,   5760.498431898d, 4.167901731d, $
+   0.048042d,   2146.165416475d, 1.495846011d, $
+   0.048373d,    155.420399434d, 2.251573730d, $
+   0.058844d,    426.598190876d, 4.839650148d, $
+   0.046551d,     -0.980321068d, 0.921573539d, $
+   0.054139d,  17260.154654690d, 3.411091093d, $
+   0.042411d,   6275.962302991d, 2.869567043d, $
+   0.040184d,     -7.113547001d, 3.565975565d, $
+   0.036564d,   5088.628839767d, 3.324679049d, $
+   0.040759d,  12352.852604545d, 3.981496998d, $
+   0.036507d,    801.820931124d, 6.248866009d, $
+   0.036955d,   3154.687084896d, 5.071801441d, $
+   0.042732d,    632.783739313d, 5.720622217d  ]
+fbldata = [ fbldata, $
+   0.042560d, 161000.685737473d, 1.270837679d, $
+   0.040480d,  15720.838784878d, 2.546610123d, $
+   0.028244d,  -6286.598968340d, 5.069663519d, $
+   0.033477d,   6062.663207553d, 4.144987272d, $
+   0.034867d,    522.577418094d, 5.210064075d, $
+   0.032438d,   6076.890301554d, 0.749317412d, $
+   0.030215d,   7084.896781115d, 3.389610345d, $
+   0.029247d, -71430.695617928d, 4.183178762d, $
+   0.033529d,   9437.762934887d, 2.404714239d, $
+   0.032423d,   8827.390269875d, 5.541473556d, $
+   0.027567d,   6279.552731642d, 5.040846034d, $
+   0.029862d,  12139.553509107d, 1.770181024d, $
+   0.022509d,  10447.387839604d, 1.460726241d, $
+   0.020937d,   8429.241266467d, 0.652303414d, $
+   0.020322d,    419.484643875d, 3.735430632d, $
+   0.024816d,  -1194.447010225d, 1.087136918d, $
+   0.025196d,   1748.016413067d, 2.901883301d, $
+   0.021691d,  14143.495242431d, 5.952658009d, $
+   0.017673d,   6812.766815086d, 3.186129845d, $
+   0.022567d,   6133.512652857d, 3.307984806d, $
+   0.016155d,  10213.285546211d, 1.331103168d, $
+   0.014751d,   1349.867409659d, 4.308933301d, $
+   0.015949d,   -220.412642439d, 4.005298270d, $
+   0.015974d,  -2352.866153772d, 6.145309371d, $
+   0.014223d,  17789.845619785d, 2.104551349d  ]
+fbldata = [ fbldata, $
+   0.017806d,     73.297125859d, 3.475975097d, $
+   0.013671d,   -536.804512095d, 5.971672571d, $
+   0.011942d,   8031.092263058d, 2.053414715d, $
+   0.014318d,  16730.463689596d, 3.016058075d, $
+   0.012462d,    103.092774219d, 1.737438797d, $
+   0.010962d,      3.590428652d, 2.196567739d, $
+   0.015078d,  19651.048481098d, 3.969480770d, $
+   0.010396d,    951.718406251d, 5.717799605d, $
+   0.011707d,  -4705.732307544d, 2.654125618d, $
+   0.010453d,   5863.591206116d, 1.913704550d, $
+   0.012420d,   4690.479836359d, 4.734090399d, $
+   0.011847d,   5643.178563677d, 5.489005403d, $
+   0.008610d,   3340.612426700d, 3.661698944d, $
+   0.011622d,   5120.601145584d, 4.863931876d, $
+   0.010825d,    553.569402842d, 0.842715011d, $
+   0.008666d,   -135.065080035d, 3.293406547d, $
+   0.009963d,    149.563197135d, 4.870690598d, $
+   0.009858d,   6309.374169791d, 1.061816410d, $
+   0.007959d,    316.391869657d, 2.465042647d, $
+   0.010099d,    283.859318865d, 1.942176992d, $
+   0.007147d,   -242.728603974d, 3.661486981d, $
+   0.007505d,   5230.807466803d, 4.920937029d, $
+   0.008323d,  11769.853693166d, 1.229392026d, $
+   0.007490d,  -6256.777530192d, 3.658444681d, $
+   0.009370d, 149854.400134205d, 0.673880395d  ]
+fbldata = [ fbldata, $
+   0.007117d,     38.027672636d, 5.294249518d, $
+   0.007857d,  12168.002696575d, 0.525733528d, $
+   0.007019d,   6206.809778716d, 0.837688810d, $
+   0.006056d,    955.599741609d, 4.194535082d, $
+   0.008107d,  13367.972631107d, 3.793235253d, $
+   0.006731d,   5650.292110678d, 5.639906583d, $
+   0.007332d,     36.648562930d, 0.114858677d, $
+   0.006366d,   4164.311989613d, 2.262081818d, $
+   0.006858d,   5216.580372801d, 0.642063318d, $
+   0.006919d,   6681.224853400d, 6.018501522d, $
+   0.006826d,   7632.943259650d, 3.458654112d, $
+   0.005308d,  -1592.596013633d, 2.500382359d, $
+   0.005096d,  11371.704689758d, 2.547107806d, $
+   0.004841d,   5333.900241022d, 0.437078094d, $
+   0.005582d,   5966.683980335d, 2.246174308d, $
+   0.006304d,  11926.254413669d, 2.512929171d, $
+   0.006603d,  23581.258177318d, 5.393136889d, $
+   0.005123d,     -1.484472708d, 2.999641028d, $
+   0.004648d,   1589.072895284d, 1.275847090d, $
+   0.005119d,   6438.496249426d, 1.486539246d, $
+   0.004521d,   4292.330832950d, 6.140635794d, $
+   0.005680d,  23013.539539587d, 4.557814849d, $
+   0.005488d,     -3.455808046d, 0.090675389d, $
+   0.004193d,   7234.794256242d, 4.869091389d, $
+   0.003742d,   7238.675591600d, 4.691976180d  ]
+fbldata = [ fbldata, $
+   0.004148d,   -110.206321219d, 3.016173439d, $
+   0.004553d,  11499.656222793d, 5.554998314d, $
+   0.004892d,   5436.993015240d, 1.475415597d, $
+   0.004044d,   4732.030627343d, 1.398784824d, $
+   0.004164d,  12491.370101415d, 5.650931916d, $
+   0.004349d,  11513.883316794d, 2.181745369d, $
+   0.003919d,  12528.018664345d, 5.823319737d, $
+   0.003129d,   6836.645252834d, 0.003844094d, $
+   0.004080d,  -7058.598461315d, 3.690360123d, $
+   0.003270d,     76.266071276d, 1.517189902d, $
+   0.002954d,   6283.143160294d, 4.447203799d, $
+   0.002872d,     28.449187468d, 1.158692983d, $
+   0.002881d,    735.876513532d, 0.349250250d, $
+   0.003279d,   5849.364112115d, 4.893384368d, $
+   0.003625d,   6209.778724132d, 1.473760578d, $
+   0.003074d,    949.175608970d, 5.185878737d, $
+   0.002775d,   9917.696874510d, 1.030026325d, $
+   0.002646d,  10973.555686350d, 3.918259169d, $
+   0.002575d,  25132.303399966d, 6.109659023d, $
+   0.003500d,    263.083923373d, 1.892100742d, $
+   0.002740d,  18319.536584880d, 4.320519510d, $
+   0.002464d,    202.253395174d, 4.698203059d, $
+   0.002409d,      2.542797281d, 5.325009315d, $
+   0.003354d, -90955.551694697d, 1.942656623d, $
+   0.002296d,   6496.374945429d, 5.061810696d  ]
+fbldata = [ fbldata, $
+   0.003002d,   6172.869528772d, 2.797822767d, $
+   0.003202d,  27511.467873537d, 0.531673101d, $
+   0.002954d,  -6283.008539689d, 4.533471191d, $
+   0.002353d,    639.897286314d, 3.734548088d, $
+   0.002401d,  16200.772724501d, 2.605547070d, $
+   0.003053d, 233141.314403759d, 3.029030662d, $
+   0.003024d,  83286.914269554d, 2.355556099d, $
+   0.002863d,  17298.182327326d, 5.240963796d, $
+   0.002103d,  -7079.373856808d, 5.756641637d, $
+   0.002303d,  83996.847317911d, 2.013686814d, $
+   0.002303d,  18073.704938650d, 1.089100410d, $
+   0.002381d,     63.735898303d, 0.759188178d, $
+   0.002493d,   6386.168624210d, 0.645026535d, $
+   0.002366d,      3.932153263d, 6.215885448d, $
+   0.002169d,  11015.106477335d, 4.845297676d, $
+   0.002397d,   6243.458341645d, 3.809290043d, $
+   0.002183d,   1162.474704408d, 6.179611691d, $
+   0.002353d,   6246.427287062d, 4.781719760d, $
+   0.002199d,   -245.831646229d, 5.956152284d, $
+   0.001729d,   3894.181829542d, 1.264976635d, $
+   0.001896d,  -3128.388765096d, 4.914231596d, $
+   0.002085d,     35.164090221d, 1.405158503d, $
+   0.002024d,  14712.317116458d, 2.752035928d, $
+   0.001737d,   6290.189396992d, 5.280820144d, $
+   0.002229d,    491.557929457d, 1.571007057d  ]
+fbldata = [ fbldata, $
+   0.001602d,  14314.168113050d, 4.203664806d, $
+   0.002186d,    454.909366527d, 1.402101526d, $
+   0.001897d,  22483.848574493d, 4.167932508d, $
+   0.001825d,  -3738.761430108d, 0.545828785d, $
+   0.001894d,   1052.268383188d, 5.817167450d, $
+   0.001421d,     20.355319399d, 2.419886601d, $
+   0.001408d,  10984.192351700d, 2.732084787d, $
+   0.001847d,  10873.986030480d, 2.903477885d, $
+   0.001391d,  -8635.942003763d, 0.593891500d, $
+   0.001388d,     -7.046236698d, 1.166145902d, $
+   0.001810d, -88860.057071188d, 0.487355242d, $
+   0.001288d,  -1990.745017041d, 3.913022880d, $
+   0.001297d,  23543.230504682d, 3.063805171d, $
+   0.001335d,   -266.607041722d, 3.995764039d, $
+   0.001376d,  10969.965257698d, 5.152914309d, $
+   0.001745d, 244287.600007027d, 3.626395673d, $
+   0.001649d,  31441.677569757d, 1.952049260d, $
+   0.001416d,   9225.539273283d, 4.996408389d, $
+   0.001238d,   4804.209275927d, 5.503379738d, $
+   0.001472d,   4590.910180489d, 4.164913291d, $
+   0.001169d,   6040.347246017d, 5.841719038d, $
+   0.001039d,   5540.085789459d, 2.769753519d, $
+   0.001004d,   -170.672870619d, 0.755008103d, $
+   0.001284d,  10575.406682942d, 5.306538209d, $
+   0.001278d,     71.812653151d, 4.713486491d  ]
+fbldata = [ fbldata, $
+   0.001321d,  18209.330263660d, 2.624866359d, $
+   0.001297d,  21228.392023546d, 0.382603541d, $
+   0.000954d,   6282.095528923d, 0.882213514d, $
+   0.001145d,   6058.731054289d, 1.169483931d, $
+   0.000979d,   5547.199336460d, 5.448375984d, $
+   0.000987d,  -6262.300454499d, 2.656486959d, $
+   0.001070d,-154717.609887482d, 1.827624012d, $
+   0.000991d,   4701.116501708d, 4.387001801d, $
+   0.001155d,    -14.227094002d, 3.042700750d, $
+   0.001176d,    277.034993741d, 3.335519004d, $
+   0.000890d,  13916.019109642d, 5.601498297d, $
+   0.000884d,  -1551.045222648d, 1.088831705d, $
+   0.000876d,   5017.508371365d, 3.969902609d, $
+   0.000806d,  15110.466119866d, 5.142876744d, $
+   0.000773d,  -4136.910433516d, 0.022067765d, $
+   0.001077d,    175.166059800d, 1.844913056d, $
+   0.000954d,  -6284.056171060d, 0.968480906d, $
+   0.000737d,   5326.786694021d, 4.923831588d, $
+   0.000845d,   -433.711737877d, 4.749245231d, $
+   0.000819d,   8662.240323563d, 5.991247817d, $
+   0.000852d,    199.072001436d, 2.189604979d, $
+   0.000723d,  17256.631536341d, 6.068719637d, $
+   0.000940d,   6037.244203762d, 6.197428148d, $
+   0.000885d,  11712.955318231d, 3.280414875d, $
+   0.000706d,  12559.038152982d, 2.824848947d  ]
+fbldata = [ fbldata, $
+   0.000732d,   2379.164473572d, 2.501813417d, $
+   0.000764d,  -6127.655450557d, 2.236346329d, $
+   0.000908d,    131.541961686d, 2.521257490d, $
+   0.000907d,  35371.887265976d, 3.370195967d, $
+   0.000673d,   1066.495477190d, 3.876512374d, $
+   0.000814d,  17654.780539750d, 4.627122566d, $
+   0.000630d,     36.027866677d, 0.156368499d, $
+   0.000798d,    515.463871093d, 5.151962502d, $
+   0.000798d,    148.078724426d, 5.909225055d, $
+   0.000806d,    309.278322656d, 6.054064447d, $
+   0.000607d,    -39.617508346d, 2.839021623d, $
+   0.000601d,    412.371096874d, 3.984225404d, $
+   0.000646d,  11403.676995575d, 3.852959484d, $
+   0.000704d,  13521.751441591d, 2.300991267d, $
+   0.000603d, -65147.619767937d, 4.140083146d, $
+   0.000609d,  10177.257679534d, 0.437122327d, $
+   0.000631d,   5767.611978898d, 4.026532329d, $
+   0.000576d,  11087.285125918d, 4.760293101d, $
+   0.000674d,  14945.316173554d, 6.270510511d, $
+   0.000726d,   5429.879468239d, 6.039606892d, $
+   0.000710d,  28766.924424484d, 5.672617711d, $
+   0.000647d,  11856.218651625d, 3.397132627d, $
+   0.000678d,  -5481.254918868d, 6.249666675d, $
+   0.000618d,  22003.914634870d, 2.466427018d, $
+   0.000738d,   6134.997125565d, 2.242668890d  ]
+fbldata = [ fbldata, $
+   0.000660d,    625.670192312d, 5.864091907d, $
+   0.000694d,   3496.032826134d, 2.668309141d, $
+   0.000531d,   6489.261398429d, 1.681888780d, $
+   0.000611d,-143571.324284214d, 2.424978312d, $
+   0.000575d,  12043.574281889d, 4.216492400d, $
+   0.000553d,  12416.588502848d, 4.772158039d, $
+   0.000689d,   4686.889407707d, 6.224271088d, $
+   0.000495d,   7342.457780181d, 3.817285811d, $
+   0.000567d,   3634.621024518d, 1.649264690d, $
+   0.000515d,  18635.928454536d, 3.945345892d, $
+   0.000486d,   -323.505416657d, 4.061673868d, $
+   0.000662d,  25158.601719765d, 1.794058369d, $
+   0.000509d,    846.082834751d, 3.053874588d, $
+   0.000472d, -12569.674818332d, 5.112133338d, $
+   0.000461d,   6179.983075773d, 0.513669325d, $
+   0.000641d,  83467.156352816d, 3.210727723d, $
+   0.000520d,  10344.295065386d, 2.445597761d, $
+   0.000493d,  18422.629359098d, 1.676939306d, $
+   0.000478d,   1265.567478626d, 5.487314569d, $
+   0.000472d,    -18.159247265d, 1.999707589d, $
+   0.000559d,  11190.377900137d, 5.783236356d, $
+   0.000494d,   9623.688276691d, 3.022645053d, $
+   0.000463d,   5739.157790895d, 1.411223013d, $
+   0.000432d,  16858.482532933d, 1.179256434d, $
+   0.000574d,  72140.628666286d, 1.758191830d  ]
+fbldata = [ fbldata, $
+   0.000484d,  17267.268201691d, 3.290589143d, $
+   0.000550d,   4907.302050146d, 0.864024298d, $
+   0.000399d,     14.977853527d, 2.094441910d, $
+   0.000491d,    224.344795702d, 0.878372791d, $
+   0.000432d,  20426.571092422d, 6.003829241d, $
+   0.000481d,   5749.452731634d, 4.309591964d, $
+   0.000480d,   5757.317038160d, 1.142348571d, $
+   0.000485d,   6702.560493867d, 0.210580917d, $
+   0.000426d,   6055.549660552d, 4.274476529d, $
+   0.000480d,   5959.570433334d, 5.031351030d, $
+   0.000466d,  12562.628581634d, 4.959581597d, $
+   0.000520d,  39302.096962196d, 4.788002889d, $
+   0.000458d,  12132.439962106d, 1.880103788d, $
+   0.000470d,  12029.347187887d, 1.405611197d, $
+   0.000416d,  -7477.522860216d, 1.082356330d, $
+   0.000449d,  11609.862544012d, 4.179989585d, $
+   0.000465d,  17253.041107690d, 0.353496295d, $
+   0.000362d,  -4535.059436924d, 1.583849576d, $
+   0.000383d,  21954.157609398d, 3.747376371d, $
+   0.000389d,     17.252277143d, 1.395753179d, $
+   0.000331d,  18052.929543158d, 0.566790582d, $
+   0.000430d,  13517.870106233d, 0.685827538d, $
+   0.000368d,  -5756.908003246d, 0.731374317d, $
+   0.000330d,  10557.594160824d, 3.710043680d, $
+   0.000332d,  20199.094959633d, 1.652901407d  ]
+fbldata = [ fbldata, $
+   0.000384d,  11933.367960670d, 5.827781531d, $
+   0.000387d,  10454.501386605d, 2.541182564d, $
+   0.000325d,  15671.081759407d, 2.178850542d, $
+   0.000318d,    138.517496871d, 2.253253037d, $
+   0.000305d,   9388.005909415d, 0.578340206d, $
+   0.000352d,   5749.861766548d, 3.000297967d, $
+   0.000311d,   6915.859589305d, 1.693574249d, $
+   0.000297d,  24072.921469776d, 1.997249392d, $
+   0.000363d,   -640.877607382d, 5.071820966d, $
+   0.000323d,  12592.450019783d, 1.072262823d, $
+   0.000341d,  12146.667056108d, 4.700657997d, $
+   0.000290d,   9779.108676125d, 1.812320441d, $
+   0.000342d,   6132.028180148d, 4.322238614d, $
+   0.000329d,   6268.848755990d, 3.033827743d, $
+   0.000374d,  17996.031168222d, 3.388716544d, $
+   0.000285d,   -533.214083444d, 4.687313233d, $
+   0.000338d,   6065.844601290d, 0.877776108d, $
+   0.000276d,     24.298513841d, 0.770299429d, $
+   0.000336d,  -2388.894020449d, 5.353796034d, $
+   0.000290d,   3097.883822726d, 4.075291557d, $
+   0.000318d,    709.933048357d, 5.941207518d, $
+   0.000271d,  13095.842665077d, 3.208912203d, $
+   0.000331d,   6073.708907816d, 4.007881169d, $
+   0.000292d,    742.990060533d, 2.714333592d, $
+   0.000362d,  29088.811415985d, 3.215977013d  ]
+fbldata = [ fbldata, $
+   0.000280d,  12359.966151546d, 0.710872502d, $
+   0.000267d,  10440.274292604d, 4.730108488d, $
+   0.000262d,    838.969287750d, 1.327720272d, $
+   0.000250d,  16496.361396202d, 0.898769761d, $
+   0.000325d,  20597.243963041d, 0.180044365d, $
+   0.000268d,   6148.010769956d, 5.152666276d, $
+   0.000284d,   5636.065016677d, 5.655385808d, $
+   0.000301d,   6080.822454817d, 2.135396205d, $
+   0.000294d,   -377.373607916d, 3.708784168d, $
+   0.000236d,   2118.763860378d, 1.733578756d, $
+   0.000234d,   5867.523359379d, 5.575209112d, $
+   0.000268d,-226858.238553767d, 0.069432392d, $
+   0.000265d, 167283.761587465d, 4.369302826d, $
+   0.000280d,  28237.233459389d, 5.304829118d, $
+   0.000292d,  12345.739057544d, 4.096094132d, $
+   0.000223d,  19800.945956225d, 3.069327406d, $
+   0.000301d,  43232.306658416d, 6.205311188d, $
+   0.000264d,  18875.525869774d, 1.417263408d, $
+   0.000304d,  -1823.175188677d, 3.409035232d, $
+   0.000301d,    109.945688789d, 0.510922054d, $
+   0.000260d,    813.550283960d, 2.389438934d, $
+   0.000299d, 316428.228673312d, 5.384595078d, $
+   0.000211d,   5756.566278634d, 3.789392838d, $
+   0.000209d,   5750.203491159d, 1.661943545d, $
+   0.000240d,  12489.885628707d, 5.684549045d  ]
+fbldata = [ fbldata, $
+   0.000216d,   6303.851245484d, 3.862942261d, $
+   0.000203d,   1581.959348283d, 5.549853589d, $
+   0.000200d,   5642.198242609d, 1.016115785d, $
+   0.000197d,    -70.849445304d, 4.690702525d, $
+   0.000227d,   6287.008003254d, 2.911891613d, $
+   0.000197d,    533.623118358d, 1.048982898d, $
+   0.000205d,  -6279.485421340d, 1.829362730d, $
+   0.000209d, -10988.808157535d, 2.636140084d, $
+   0.000208d,   -227.526189440d, 4.127883842d, $
+   0.000191d,    415.552490612d, 4.401165650d, $
+   0.000190d,  29296.615389579d, 4.175658539d, $
+   0.000264d,  66567.485864652d, 4.601102551d, $
+   0.000256d,  -3646.350377354d, 0.506364778d, $
+   0.000188d,  13119.721102825d, 2.032195842d, $
+   0.000185d,   -209.366942175d, 4.694756586d, $
+   0.000198d,  25934.124331089d, 3.832703118d, $
+   0.000195d,   4061.219215394d, 3.308463427d, $
+   0.000234d,   5113.487598583d, 1.716090661d, $
+   0.000188d,   1478.866574064d, 5.686865780d, $
+   0.000222d,  11823.161639450d, 1.942386641d, $
+   0.000181d,  10770.893256262d, 1.999482059d, $
+   0.000171d,   6546.159773364d, 1.182807992d, $
+   0.000206d,     70.328180442d, 5.934076062d, $
+   0.000169d,  20995.392966449d, 2.169080622d, $
+   0.000191d,  10660.686935042d, 5.405515999d  ]
+fbldata = [ fbldata, $
+   0.000228d,  33019.021112205d, 4.656985514d, $
+   0.000184d,  -4933.208440333d, 3.327476868d, $
+   0.000220d,   -135.625325010d, 1.765430262d, $
+   0.000166d,  23141.558382925d, 3.454132746d, $
+   0.000191d,   6144.558353121d, 5.020393445d, $
+   0.000180d,   6084.003848555d, 0.602182191d, $
+   0.000163d,  17782.732072784d, 4.960593133d, $
+   0.000225d,  16460.333529525d, 2.596451817d, $
+   0.000222d,   5905.702242076d, 3.731990323d, $
+   0.000204d,    227.476132789d, 5.636192701d, $
+   0.000159d,  16737.577236597d, 3.600691544d, $
+   0.000200d,   6805.653268085d, 0.868220961d, $
+   0.000187d,  11919.140866668d, 2.629456641d, $
+   0.000161d,    127.471796607d, 2.862574720d, $
+   0.000205d,   6286.666278643d, 1.742882331d, $
+   0.000189d,    153.778810485d, 4.812372643d, $
+   0.000168d,  16723.350142595d, 0.027860588d, $
+   0.000149d,  11720.068865232d, 0.659721876d, $
+   0.000189d,   5237.921013804d, 5.245313000d, $
+   0.000143d,   6709.674040867d, 4.317625647d, $
+   0.000146d,   4487.817406270d, 4.815297007d, $
+   0.000144d,   -664.756045130d, 5.381366880d, $
+   0.000175d,   5127.714692584d, 4.728443327d, $
+   0.000162d,   6254.626662524d, 1.435132069d, $
+   0.000187d,  47162.516354635d, 1.354371923d  ]
+fbldata = [ fbldata, $
+   0.000146d,  11080.171578918d, 3.369695406d, $
+   0.000180d,   -348.924420448d, 2.490902145d, $
+   0.000148d,    151.047669843d, 3.799109588d, $
+   0.000157d,   6197.248551160d, 1.284375887d, $
+   0.000167d,    146.594251718d, 0.759969109d, $
+   0.000133d,  -5331.357443741d, 5.409701889d, $
+   0.000154d,     95.979227218d, 3.366890614d, $
+   0.000148d,  -6418.140930027d, 3.384104996d, $
+   0.000128d,  -6525.804453965d, 3.803419985d, $
+   0.000130d,  11293.470674356d, 0.939039445d, $
+   0.000152d,  -5729.506447149d, 0.734117523d, $
+   0.000138d,    210.117701700d, 2.564216078d, $
+   0.000123d,   6066.595360816d, 4.517099537d, $
+   0.000140d,  18451.078546566d, 0.642049130d, $
+   0.000126d,  11300.584221356d, 3.485280663d, $
+   0.000119d,  10027.903195729d, 3.217431161d, $
+   0.000151d,   4274.518310832d, 4.404359108d, $
+   0.000117d,   6072.958148291d, 0.366324650d, $
+   0.000165d,  -7668.637425143d, 4.298212528d, $
+   0.000117d,  -6245.048177356d, 5.379518958d, $
+   0.000130d,  -5888.449964932d, 4.527681115d, $
+   0.000121d,   -543.918059096d, 6.109429504d, $
+   0.000162d,   9683.594581116d, 5.720092446d, $
+   0.000141d,   6219.339951688d, 0.679068671d, $
+   0.000118d,  22743.409379516d, 4.881123092d  ]
+fbldata = [ fbldata, $
+   0.000129d,   1692.165669502d, 0.351407289d, $
+   0.000126d,   5657.405657679d, 5.146592349d, $
+   0.000114d,    728.762966531d, 0.520791814d, $
+   0.000120d,     52.596639600d, 0.948516300d, $
+   0.000115d,     65.220371012d, 3.504914846d, $
+   0.000126d,   5881.403728234d, 5.577502482d, $
+   0.000158d, 163096.180360983d, 2.957128968d, $
+   0.000134d,  12341.806904281d, 2.598576764d, $
+   0.000151d,  16627.370915377d, 3.985702050d, $
+   0.000109d,   1368.660252845d, 0.014730471d, $
+   0.000131d,   6211.263196841d, 0.085077024d, $
+   0.000146d,   5792.741760812d, 0.708426604d, $
+   0.000146d,    -77.750543984d, 3.121576600d, $
+   0.000107d,   5341.013788022d, 0.288231904d, $
+   0.000138d,   6281.591377283d, 2.797450317d, $
+   0.000113d,  -6277.552925684d, 2.788904128d, $
+   0.000115d,   -525.758811831d, 5.895222200d, $
+   0.000138d,   6016.468808270d, 6.096188999d, $
+   0.000139d,  23539.707386333d, 2.028195445d, $
+   0.000146d,  -4176.041342449d, 4.660008502d, $
+   0.000107d,  16062.184526117d, 4.066520001d, $
+   0.000142d,  83783.548222473d, 2.936315115d, $
+   0.000128d,   9380.959672717d, 3.223844306d, $
+   0.000135d,   6205.325306007d, 1.638054048d, $
+   0.000101d,   2699.734819318d, 5.481603249d  ]
+fbldata = [ fbldata, $
+   0.000104d,   -568.821874027d, 2.205734493d, $
+   0.000103d,   6321.103522627d, 2.440421099d, $
+   0.000119d,   6321.208885629d, 2.547496264d, $
+   0.000138d,   1975.492545856d, 2.314608466d, $
+   0.000121d,    137.033024162d, 4.539108237d, $
+   0.000123d,  19402.796952817d, 4.538074405d, $
+   0.000119d,  22805.735565994d, 2.869040566d, $
+   0.000133d,  64471.991241142d, 6.056405489d, $
+   0.000129d,    -85.827298831d, 2.540635083d, $
+   0.000131d,  13613.804277336d, 4.005732868d, $
+   0.000104d,   9814.604100291d, 1.959967212d, $
+   0.000112d,  16097.679950283d, 3.589026260d, $
+   0.000123d,   2107.034507542d, 1.728627253d, $
+   0.000121d,  36949.230808424d, 6.072332087d, $
+   0.000108d, -12539.853380183d, 3.716133846d, $
+   0.000113d,  -7875.671863624d, 2.725771122d, $
+   0.000109d,   4171.425536614d, 4.033338079d, $
+   0.000101d,   6247.911759770d, 3.441347021d, $
+   0.000113d,   7330.728427345d, 0.656372122d, $
+   0.000113d,  51092.726050855d, 2.791483066d, $
+   0.000106d,   5621.842923210d, 1.815323326d, $
+   0.000101d,    111.430161497d, 5.711033677d, $
+   0.000103d,    909.818733055d, 2.812745443d, $
+   0.000101d,   1790.642637886d, 1.965746028d  ]
+fbldata = [ fbldata, $  ;; From end of TDB1NS.F
+   0.00065d,    6069.776754d,    4.021194d, $
+   0.00033d,     213.299095d,    5.543132d, $
+  -0.00196d,    6208.294251d,    5.696701d, $
+  -0.00173d,      74.781599d,    2.435900d  ]
+
+i1terms = n_elements(fbldata)/3
+; T**1                          
+fbldata = [ fbldata, $
+ 102.156724d,   6283.075849991d, 4.249032005d, $
+   1.706807d,  12566.151699983d, 4.205904248d, $
+   0.269668d,    213.299095438d, 3.400290479d, $
+   0.265919d,    529.690965095d, 5.836047367d, $
+   0.210568d,     -3.523118349d, 6.262738348d, $
+   0.077996d,   5223.693919802d, 4.670344204d, $
+   0.054764d,   1577.343542448d, 4.534800170d, $
+   0.059146d,     26.298319800d, 1.083044735d, $
+   0.034420d,   -398.149003408d, 5.980077351d, $
+   0.032088d,  18849.227549974d, 4.162913471d, $
+   0.033595d,   5507.553238667d, 5.980162321d, $
+   0.029198d,   5856.477659115d, 0.623811863d, $
+   0.027764d,    155.420399434d, 3.745318113d, $
+   0.025190d,   5746.271337896d, 2.980330535d, $
+   0.022997d,   -796.298006816d, 1.174411803d, $
+   0.024976d,   5760.498431898d, 2.467913690d, $
+   0.021774d,    206.185548437d, 3.854787540d, $
+   0.017925d,   -775.522611324d, 1.092065955d, $
+   0.013794d,    426.598190876d, 2.699831988d, $
+   0.013276d,   6062.663207553d, 5.845801920d, $
+   0.011774d,  12036.460734888d, 2.292832062d, $
+   0.012869d,   6076.890301554d, 5.333425680d, $
+   0.012152d,   1059.381930189d, 6.222874454d, $
+   0.011081d,     -7.113547001d, 5.154724984d, $
+   0.010143d,   4694.002954708d, 4.044013795d  ]
+fbldata = [ fbldata, $
+   0.009357d,   5486.777843175d, 3.416081409d, $
+   0.010084d,    522.577418094d, 0.749320262d, $
+   0.008587d,  10977.078804699d, 2.777152598d, $
+   0.008628d,   6275.962302991d, 4.562060226d, $
+   0.008158d,   -220.412642439d, 5.806891533d, $
+   0.007746d,   2544.314419883d, 1.603197066d, $
+   0.007670d,   2146.165416475d, 3.000200440d, $
+   0.007098d,     74.781598567d, 0.443725817d, $
+   0.006180d,   -536.804512095d, 1.302642751d, $
+   0.005818d,   5088.628839767d, 4.827723531d, $
+   0.004945d,  -6286.598968340d, 0.268305170d, $
+   0.004774d,   1349.867409659d, 5.808636673d, $
+   0.004687d,   -242.728603974d, 5.154890570d, $
+   0.006089d,   1748.016413067d, 4.403765209d, $
+   0.005975d,  -1194.447010225d, 2.583472591d, $
+   0.004229d,    951.718406251d, 0.931172179d, $
+   0.005264d,    553.569402842d, 2.336107252d, $
+   0.003049d,   5643.178563677d, 1.362634430d, $
+   0.002974d,   6812.766815086d, 1.583012668d, $
+   0.003403d,  -2352.866153772d, 2.552189886d, $
+   0.003030d,    419.484643875d, 5.286473844d, $
+   0.003210d,     -7.046236698d, 1.863796539d, $
+   0.003058d,   9437.762934887d, 4.226420633d, $
+   0.002589d,  12352.852604545d, 1.991935820d, $
+   0.002927d,   5216.580372801d, 2.319951253d  ]
+fbldata = [ fbldata, $
+   0.002425d,   5230.807466803d, 3.084752833d, $
+   0.002656d,   3154.687084896d, 2.487447866d, $
+   0.002445d,  10447.387839604d, 2.347139160d, $
+   0.002990d,   4690.479836359d, 6.235872050d, $
+   0.002890d,   5863.591206116d, 0.095197563d, $
+   0.002498d,   6438.496249426d, 2.994779800d, $
+   0.001889d,   8031.092263058d, 3.569003717d, $
+   0.002567d,    801.820931124d, 3.425611498d, $
+   0.001803d, -71430.695617928d, 2.192295512d, $
+   0.001782d,      3.932153263d, 5.180433689d, $
+   0.001694d,  -4705.732307544d, 4.641779174d, $
+   0.001704d,  -1592.596013633d, 3.997097652d, $
+   0.001735d,   5849.364112115d, 0.417558428d, $
+   0.001643d,   8429.241266467d, 2.180619584d, $
+   0.001680d,     38.133035638d, 4.164529426d, $
+   0.002045d,   7084.896781115d, 0.526323854d, $
+   0.001458d,   4292.330832950d, 1.356098141d, $
+   0.001437d,     20.355319399d, 3.895439360d, $
+   0.001738d,   6279.552731642d, 0.087484036d, $
+   0.001367d,  14143.495242431d, 3.987576591d, $
+   0.001344d,   7234.794256242d, 0.090454338d, $
+   0.001438d,  11499.656222793d, 0.974387904d, $
+   0.001257d,   6836.645252834d, 1.509069366d, $
+   0.001358d,  11513.883316794d, 0.495572260d, $
+   0.001628d,   7632.943259650d, 4.968445721d  ]
+fbldata = [ fbldata, $
+   0.001169d,    103.092774219d, 2.838496795d, $
+   0.001162d,   4164.311989613d, 3.408387778d, $
+   0.001092d,   6069.776754553d, 3.617942651d, $
+   0.001008d,  17789.845619785d, 0.286350174d, $
+   0.001008d,    639.897286314d, 1.610762073d, $
+   0.000918d,  10213.285546211d, 5.532798067d, $
+   0.001011d,  -6256.777530192d, 0.661826484d, $
+   0.000753d,  16730.463689596d, 3.905030235d, $
+   0.000737d,  11926.254413669d, 4.641956361d, $
+   0.000694d,   3340.612426700d, 2.111120332d, $
+   0.000701d,   3894.181829542d, 2.760823491d, $
+   0.000689d,   -135.065080035d, 4.768800780d, $
+   0.000700d,  13367.972631107d, 5.760439898d, $
+   0.000664d,   6040.347246017d, 1.051215840d, $
+   0.000654d,   5650.292110678d, 4.911332503d, $
+   0.000788d,   6681.224853400d, 4.699648011d, $
+   0.000628d,   5333.900241022d, 5.024608847d, $
+   0.000755d,   -110.206321219d, 4.370971253d, $
+   0.000628d,   6290.189396992d, 3.660478857d, $
+   0.000635d,  25132.303399966d, 4.121051532d, $
+   0.000534d,   5966.683980335d, 1.173284524d, $
+   0.000543d,   -433.711737877d, 0.345585464d, $
+   0.000517d,  -1990.745017041d, 5.414571768d, $
+   0.000504d,   5767.611978898d, 2.328281115d, $
+   0.000485d,   5753.384884897d, 1.685874771d  ]
+fbldata = [ fbldata, $
+   0.000463d,   7860.419392439d, 5.297703006d, $
+   0.000604d,    515.463871093d, 0.591998446d, $
+   0.000443d,  12168.002696575d, 4.830881244d, $
+   0.000570d,    199.072001436d, 3.899190272d, $
+   0.000465d,  10969.965257698d, 0.476681802d, $
+   0.000424d,  -7079.373856808d, 1.112242763d, $
+   0.000427d,    735.876513532d, 1.994214480d, $
+   0.000478d,  -6127.655450557d, 3.778025483d, $
+   0.000414d,  10973.555686350d, 5.441088327d, $
+   0.000512d,   1589.072895284d, 0.107123853d, $
+   0.000378d,  10984.192351700d, 0.915087231d, $
+   0.000402d,  11371.704689758d, 4.107281715d, $
+   0.000453d,   9917.696874510d, 1.917490952d, $
+   0.000395d,    149.563197135d, 2.763124165d, $
+   0.000371d,   5739.157790895d, 3.112111866d, $
+   0.000350d,  11790.629088659d, 0.440639857d, $
+   0.000356d,   6133.512652857d, 5.444568842d, $
+   0.000344d,    412.371096874d, 5.676832684d, $
+   0.000383d,    955.599741609d, 5.559734846d, $
+   0.000333d,   6496.374945429d, 0.261537984d, $
+   0.000340d,   6055.549660552d, 5.975534987d, $
+   0.000334d,   1066.495477190d, 2.335063907d, $
+   0.000399d,  11506.769769794d, 5.321230910d, $
+   0.000314d,  18319.536584880d, 2.313312404d, $
+   0.000424d,   1052.268383188d, 1.211961766d  ]
+fbldata = [ fbldata, $
+   0.000307d,     63.735898303d, 3.169551388d, $
+   0.000329d,     29.821438149d, 6.106912080d, $
+   0.000357d,   6309.374169791d, 4.223760346d, $
+   0.000312d,  -3738.761430108d, 2.180556645d, $
+   0.000301d,    309.278322656d, 1.499984572d, $
+   0.000268d,  12043.574281889d, 2.447520648d, $
+   0.000257d,  12491.370101415d, 3.662331761d, $
+   0.000290d,    625.670192312d, 1.272834584d, $
+   0.000256d,   5429.879468239d, 1.913426912d, $
+   0.000339d,   3496.032826134d, 4.165930011d, $
+   0.000283d,   3930.209696220d, 4.325565754d, $
+   0.000241d,  12528.018664345d, 3.832324536d, $
+   0.000304d,   4686.889407707d, 1.612348468d, $
+   0.000259d,  16200.772724501d, 3.470173146d, $
+   0.000238d,  12139.553509107d, 1.147977842d, $
+   0.000236d,   6172.869528772d, 3.776271728d, $
+   0.000296d,  -7058.598461315d, 0.460368852d, $
+   0.000306d,  10575.406682942d, 0.554749016d, $
+   0.000251d,  17298.182327326d, 0.834332510d, $
+   0.000290d,   4732.030627343d, 4.759564091d, $
+   0.000261d,   5884.926846583d, 0.298259862d, $
+   0.000249d,   5547.199336460d, 3.749366406d, $
+   0.000213d,  11712.955318231d, 5.415666119d, $
+   0.000223d,   4701.116501708d, 2.703203558d, $
+   0.000268d,   -640.877607382d, 0.283670793d  ]
+fbldata = [ fbldata, $
+   0.000209d,   5636.065016677d, 1.238477199d, $
+   0.000193d,  10177.257679534d, 1.943251340d, $
+   0.000182d,   6283.143160294d, 2.456157599d, $
+   0.000184d,   -227.526189440d, 5.888038582d, $
+   0.000182d,  -6283.008539689d, 0.241332086d, $
+   0.000228d,  -6284.056171060d, 2.657323816d, $
+   0.000166d,   7238.675591600d, 5.930629110d, $
+   0.000167d,   3097.883822726d, 5.570955333d, $
+   0.000159d,   -323.505416657d, 5.786670700d, $
+   0.000154d,  -4136.910433516d, 1.517805532d, $
+   0.000176d,  12029.347187887d, 3.139266834d, $
+   0.000167d,  12132.439962106d, 3.556352289d, $
+   0.000153d,    202.253395174d, 1.463313961d, $
+   0.000157d,  17267.268201691d, 1.586837396d, $
+   0.000142d,  83996.847317911d, 0.022670115d, $
+   0.000152d,  17260.154654690d, 0.708528947d, $
+   0.000144d,   6084.003848555d, 5.187075177d, $
+   0.000135d,   5756.566278634d, 1.993229262d, $
+   0.000134d,   5750.203491159d, 3.457197134d, $
+   0.000144d,   5326.786694021d, 6.066193291d, $
+   0.000160d,  11015.106477335d, 1.710431974d, $
+   0.000133d,   3634.621024518d, 2.836451652d, $
+   0.000134d,  18073.704938650d, 5.453106665d, $
+   0.000134d,   1162.474704408d, 5.326898811d, $
+   0.000128d,   5642.198242609d, 2.511652591d  ]
+fbldata = [ fbldata, $
+   0.000160d,    632.783739313d, 5.628785365d, $
+   0.000132d,  13916.019109642d, 0.819294053d, $
+   0.000122d,  14314.168113050d, 5.677408071d, $
+   0.000125d,  12359.966151546d, 5.251984735d, $
+   0.000121d,   5749.452731634d, 2.210924603d, $
+   0.000136d,   -245.831646229d, 1.646502367d, $
+   0.000120d,   5757.317038160d, 3.240883049d, $
+   0.000134d,  12146.667056108d, 3.059480037d, $
+   0.000137d,   6206.809778716d, 1.867105418d, $
+   0.000141d,  17253.041107690d, 2.069217456d, $
+   0.000129d,  -7477.522860216d, 2.781469314d, $
+   0.000116d,   5540.085789459d, 4.281176991d, $
+   0.000116d,   9779.108676125d, 3.320925381d, $
+   0.000129d,   5237.921013804d, 3.497704076d, $
+   0.000113d,   5959.570433334d, 0.983210840d, $
+   0.000122d,   6282.095528923d, 2.674938860d, $
+   0.000140d,    -11.045700264d, 4.957936982d, $
+   0.000108d,  23543.230504682d, 1.390113589d, $
+   0.000106d, -12569.674818332d, 0.429631317d, $
+   0.000110d,   -266.607041722d, 5.501340197d, $
+   0.000115d,  12559.038152982d, 4.691456618d, $
+   0.000134d,  -2388.894020449d, 0.577313584d, $
+   0.000109d,  10440.274292604d, 6.218148717d, $
+   0.000102d,   -543.918059096d, 1.477842615d, $
+   0.000108d,  21228.392023546d, 2.237753948d  ]
+fbldata = [ fbldata, $
+   0.000101d,  -4535.059436924d, 3.100492232d, $
+   0.000103d,     76.266071276d, 5.594294322d, $
+   0.000104d,    949.175608970d, 5.674287810d, $
+   0.000101d,  13517.870106233d, 2.196632348d, $
+   0.000100d,  11933.367960670d, 4.056084160d  ]
+
+i2terms = n_elements(fbldata)/3
+; T**2                          
+fbldata = [ fbldata, $
+   4.322990d,   6283.075849991d, 2.642893748d, $
+   0.406495d,      0.000000000d, 4.712388980d, $
+   0.122605d,  12566.151699983d, 2.438140634d, $
+   0.019476d,    213.299095438d, 1.642186981d, $
+   0.016916d,    529.690965095d, 4.510959344d, $
+   0.013374d,     -3.523118349d, 1.502210314d, $
+   0.008042d,     26.298319800d, 0.478549024d, $
+   0.007824d,    155.420399434d, 5.254710405d, $
+   0.004894d,   5746.271337896d, 4.683210850d, $
+   0.004875d,   5760.498431898d, 0.759507698d, $
+   0.004416d,   5223.693919802d, 6.028853166d, $
+   0.004088d,     -7.113547001d, 0.060926389d, $
+   0.004433d,  77713.771467920d, 3.627734103d, $
+   0.003277d,  18849.227549974d, 2.327912542d, $
+   0.002703d,   6062.663207553d, 1.271941729d, $
+   0.003435d,   -775.522611324d, 0.747446224d, $
+   0.002618d,   6076.890301554d, 3.633715689d, $
+   0.003146d,    206.185548437d, 5.647874613d, $
+   0.002544d,   1577.343542448d, 6.232904270d, $
+   0.002218d,   -220.412642439d, 1.309509946d, $
+   0.002197d,   5856.477659115d, 2.407212349d, $
+   0.002897d,   5753.384884897d, 5.863842246d, $
+   0.001766d,    426.598190876d, 0.754113147d, $
+   0.001738d,   -796.298006816d, 2.714942671d, $
+   0.001695d,    522.577418094d, 2.629369842d  ]
+fbldata = [ fbldata, $
+   0.001584d,   5507.553238667d, 1.341138229d, $
+   0.001503d,   -242.728603974d, 0.377699736d, $
+   0.001552d,   -536.804512095d, 2.904684667d, $
+   0.001370d,   -398.149003408d, 1.265599125d, $
+   0.001889d,  -5573.142801634d, 4.413514859d, $
+   0.001722d,   6069.776754553d, 2.445966339d, $
+   0.001124d,   1059.381930189d, 5.041799657d, $
+   0.001258d,    553.569402842d, 3.849557278d, $
+   0.000831d,    951.718406251d, 2.471094709d, $
+   0.000767d,   4694.002954708d, 5.363125422d, $
+   0.000756d,   1349.867409659d, 1.046195744d, $
+   0.000775d,    -11.045700264d, 0.245548001d, $
+   0.000597d,   2146.165416475d, 4.543268798d, $
+   0.000568d,   5216.580372801d, 4.178853144d, $
+   0.000711d,   1748.016413067d, 5.934271972d, $
+   0.000499d,  12036.460734888d, 0.624434410d, $
+   0.000671d,  -1194.447010225d, 4.136047594d, $
+   0.000488d,   5849.364112115d, 2.209679987d, $
+   0.000621d,   6438.496249426d, 4.518860804d, $
+   0.000495d,  -6286.598968340d, 1.868201275d, $
+   0.000456d,   5230.807466803d, 1.271231591d, $
+   0.000451d,   5088.628839767d, 0.084060889d, $
+   0.000435d,   5643.178563677d, 3.324456609d, $
+   0.000387d,  10977.078804699d, 4.052488477d, $
+   0.000547d, 161000.685737473d, 2.841633844d  ]
+fbldata = [ fbldata, $
+   0.000522d,   3154.687084896d, 2.171979966d, $
+   0.000375d,   5486.777843175d, 4.983027306d, $
+   0.000421d,   5863.591206116d, 4.546432249d, $
+   0.000439d,   7084.896781115d, 0.522967921d, $
+   0.000309d,   2544.314419883d, 3.172606705d, $
+   0.000347d,   4690.479836359d, 1.479586566d, $
+   0.000317d,    801.820931124d, 3.553088096d, $
+   0.000262d,    419.484643875d, 0.606635550d, $
+   0.000248d,   6836.645252834d, 3.014082064d, $
+   0.000245d,  -1592.596013633d, 5.519526220d, $
+   0.000225d,   4292.330832950d, 2.877956536d, $
+   0.000214d,   7234.794256242d, 1.605227587d, $
+   0.000205d,   5767.611978898d, 0.625804796d, $
+   0.000180d,  10447.387839604d, 3.499954526d, $
+   0.000229d,    199.072001436d, 5.632304604d, $
+   0.000214d,    639.897286314d, 5.960227667d, $
+   0.000175d,   -433.711737877d, 2.162417992d, $
+   0.000209d,    515.463871093d, 2.322150893d, $
+   0.000173d,   6040.347246017d, 2.556183691d, $
+   0.000184d,   6309.374169791d, 4.732296790d, $
+   0.000227d, 149854.400134205d, 5.385812217d, $
+   0.000154d,   8031.092263058d, 5.120720920d, $
+   0.000151d,   5739.157790895d, 4.815000443d, $
+   0.000197d,   7632.943259650d, 0.222827271d, $
+   0.000197d,     74.781598567d, 3.910456770d  ]
+fbldata = [ fbldata, $
+   0.000138d,   6055.549660552d, 1.397484253d, $
+   0.000149d,  -6127.655450557d, 5.333727496d, $
+   0.000137d,   3894.181829542d, 4.281749907d, $
+   0.000135d,   9437.762934887d, 5.979971885d, $
+   0.000139d,  -2352.866153772d, 4.715630782d, $
+   0.000142d,   6812.766815086d, 0.513330157d, $
+   0.000120d,  -4705.732307544d, 0.194160689d, $
+   0.000131d, -71430.695617928d, 0.000379226d, $
+   0.000124d,   6279.552731642d, 2.122264908d, $
+   0.000108d,  -6256.777530192d, 0.883445696d  ]
+
+i3terms = n_elements(fbldata)/3
+; T**3                          
+fbldata = [ fbldata, $
+   0.143388d,   6283.075849991d, 1.131453581d, $
+   0.006671d,  12566.151699983d, 0.775148887d, $
+   0.001480d,    155.420399434d, 0.480016880d, $
+   0.000934d,    213.299095438d, 6.144453084d, $
+   0.000795d,    529.690965095d, 2.941595619d, $
+   0.000673d,   5746.271337896d, 0.120415406d, $
+   0.000672d,   5760.498431898d, 5.317009738d, $
+   0.000389d,   -220.412642439d, 3.090323467d, $
+   0.000373d,   6062.663207553d, 3.003551964d, $
+   0.000360d,   6076.890301554d, 1.918913041d, $
+   0.000316d,    -21.340641002d, 5.545798121d, $
+   0.000315d,   -242.728603974d, 1.884932563d, $
+   0.000278d,    206.185548437d, 1.266254859d, $
+   0.000238d,   -536.804512095d, 4.532664830d, $
+   0.000185d,    522.577418094d, 4.578313856d, $
+   0.000245d,  18849.227549974d, 0.587467082d, $
+   0.000180d,    426.598190876d, 5.151178553d, $
+   0.000200d,    553.569402842d, 5.355983739d, $
+   0.000141d,   5223.693919802d, 1.336556009d, $
+   0.000104d,   5856.477659115d, 4.239842759d  ]
+
+i4terms = n_elements(fbldata)/3
+; T**4                          
+fbldata = [ fbldata, $
+   0.003826d,   6283.075849991d, 5.705257275d, $
+   0.000303d,  12566.151699983d, 5.407132842d, $
+   0.000209d,    155.420399434d, 1.989815753d  ]
+
+    nterms = n_elements(fbldata)/3
+    fbldata = reform(fbldata, 3, nterms, /overwrite)
+    const0 = reform(fbldata[0,*], nterms)
+    freq0  = reform(fbldata[1,*], nterms)
+    phase0 = reform(fbldata[2,*], nterms)
+
+    texp = dblarr(nterms) +   0
+    texp[i1terms:i2terms-1] = 1
+    texp[i2terms:i3terms-1] = 2
+    texp[i3terms:i4terms-1] = 3
+    texp[i4terms:*        ] = 4
+
+  endif
+
+  if n_elements(tbase) EQ 0 then tbase = 0D
+  t = ((tbase[0]-2451545D) + jd[0])/365250.0D
+  if t EQ 0 then t = 1d-100
+
+  ph = freq0 * t + phase0 
+  sint = sin( ph )
+  sinf = const0 * t^texp
+
+  dt = total(sinf*sint)*1d-6
+  if arg_present(deriv) then $
+    deriv = total(sinf*(texp*sint/t + freq0*cos(ph)))*(1d-6/365250.0D)
+
+  return, dt
+end
+
+function tdb2tdt, jd, deriv=deriv, tbase=tbase
+
+  sz = size(jd)
+  if sz[0] EQ 0 then $
+    return, tdb2tdt_calc(jd, deriv=deriv, tbase=tbase)
+
+  result = reform(double(jd), sz[1:sz[0]])
+  if arg_present(deriv) then begin
+      deriv = reform(double(jd), sz[1:sz[0]])
+      for i = 0L, sz[sz[0]+2]-1 do begin
+          result[i] = tdb2tdt_calc(jd[i], deriv=dd, tbase=tbase)
+          deriv[i] = dd
+      endfor
+  endif else begin
+      for i = 0L, sz[sz[0]+2]-1 do begin
+          result[i] = tdb2tdt_calc(jd[i], tbase=tbase)
+      endfor
+  endelse
+
+  return, result
+end
+
diff --git a/Code/script_idl_mv/astrolib/ten.pro b/Code/script_idl_mv/astrolib/ten.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e3b894c8276ad3f066505513d49b1d2e8bc67a0a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ten.pro
@@ -0,0 +1,93 @@
+      FUNCTION ten,dd,mm,ss
+;+
+; NAME:
+;	TEN()
+; PURPOSE:
+;	Converts a sexagesimal number or string to decimal.
+; EXPLANATION:
+;	Inverse of the SIXTY() function.
+;
+; CALLING SEQUENCES:
+;	X = TEN( [ HOUR_OR_DEG, MIN, SEC ] )
+;	X = TEN( HOUR_OR_DEG, MIN, SEC )
+;	X = TEN( [ HOUR_OR_DEG, MIN ] )
+;	X = TEN( HOUR_OR_DEG, MIN )
+;	X = TEN( [ HOUR_OR_DEG ] )      <--  Trivial cases
+;	X = TEN( HOUR_OR_DEG )          <--
+;
+;        or
+;       X = TEN(HRMNSC_STRING)
+;
+; INPUTS:
+;	HOUR_OR_DEG,MIN,SEC -- Scalars giving sexagesimal quantity in 
+;		in order from largest to smallest.    
+;                         or
+;   HRMNSC_STRING - String giving sexagesmal quantity separated by
+;               spaces or colons e.g. "10 23 34" or "-3:23:45.2"
+;               Any negative values should begin with a minus sign.
+; OUTPUTS:
+;	Function value returned = double real scalar, decimal equivalent of
+;	input sexigesimal quantity.  For numeric input, a minus sign on any 
+;   nonzero element of the input vector causes all the elements to be taken 
+;   as < 0.
+;
+; EXAMPLES:
+;       IDL> print,ten(0,-23,34)
+;                 --> -0.39277778
+;       IDL> print,ten("-0:23:34")
+;                 --> -0.39277778
+; PROCEDURE:
+;	Mostly involves checking arguments and setting the sign.
+;
+;	The procedure TENV can be used when dealing with a vector of 
+;	sexigesimal quantities.
+;
+; MODIFICATION HISTORY:
+;	Written by R. S. Hill, STX, 21 April 87       
+;	Modified to allow non-vector arguments.  RSH, STX, 19-OCT-87
+;       Recognize -0.0   W. Landsman/B. Stecklum   Dec 2005
+;       Work with string input  W. Landsman Dec 2008
+;-
+      compile_opt idl2
+      np = N_params()
+
+      if (np eq 1) then begin
+         if size(dd,/TNAME) EQ 'STRING' then begin  
+	      temp = strtrim(dd,2)
+	      neg = strmid(dd,0,1) EQ '-'
+	      temp = repchr(temp,':',' ')
+	      value = abs(double(gettok(temp,' ')))
+	       mm = double(gettok(temp,' '))
+	       decimal =  value + mm/60. + double(temp)/3600.0d 
+              if neg then decimal = -decimal
+	      return,decimal
+         endif else vector=dd
+      endif else begin
+         if (np lt 1) or (np gt 3) then goto,bad_args
+         vector=dblarr(3)
+         vector[0]=dd
+         vector[1]=mm
+         if np gt 2 then vector[2]=ss
+      endelse
+      sz = size(vector)
+      ndim = sz[0]
+      if (ndim eq 0) then return,double(vector)
+      facs=[1.0d0,60.0d0,3600.0d0]
+      nel = sz[1]
+      sign = +1.0d0
+       dummy=where(strpos(string(vector),'-') ge 0,cnt)
+       if cnt gt 0 then sign = -1.0d0
+      vector = abs(vector)
+      decim = double(vector[0])
+      i = 1
+      while (i le nel-1) do begin
+         decim = decim + double(vector[i])/facs[i]
+         i = i + 1
+      endwhile
+      return,decim*sign
+bad_args:    
+      print,'Argument(s) should be hours/degrees, minutes (optional),'
+      print,'seconds (optional)   in vector or as separate arguments.'
+      print,'If any one number negative, all taken as negative.'
+      return,0.0d0     
+      end
diff --git a/Code/script_idl_mv/astrolib/tenv.pro b/Code/script_idl_mv/astrolib/tenv.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c0292356d1ffd520c24926a0730dbc7f51a7b655
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tenv.pro
@@ -0,0 +1,106 @@
+      FUNCTION tenv,dd,mm,ss
+;+
+; NAME:
+;	TENV()
+; PURPOSE:
+;	Converts sexagesimal number or string vector to decimal.  
+; EXPLANATION:
+;	Like TEN() but allows vector input.
+;
+; CALLING SEQUENCES:
+;	Result = TENV( dd, mm )           ; result = dd + mm/60.
+;	Result = TENV( dd, mm, ss)        ; result = dd + mm/60. + ss/3600.
+;                       or
+;       Result = TENV(ddmmss_string)
+; INPUTS:
+;	dd - sexagesimal element(s) corresponding to hours or degrees
+;	mm - sexagesimal element(s) corresponding to minutes
+;	ss - sexagesimal element(s) corresponding to seconds (optional)
+;		The input parameters can be scalars or vectors.   However, the
+;		number of elements in each parameter must be the same.
+;
+;       HRMNSC_STRING - String scalar or vector giving sexagesmal quantity 
+;               separated by spaces or colons e.g. "10 23 34" or "-3:23:45.2"
+;               Any negative values should begin with a minus sign.
+; OUTPUTS:
+;	Result -  double, decimal equivalent of input sexagesimal 
+;		quantities.  Same number of elements as the input parameters.
+;		If the nth element in any of the input parameters is negative 
+;		then the nth element in Result will also be negative.
+;
+; EXAMPLE:
+;	If dd = [60,60,0], and mm = [30,-30,-30], then
+;
+;	IDL> Result = TENV(dd,mm)  ====>   Result =  [60.5,-60.5,-0.5]
+;       
+;       Alternatively, the input could be written as the string vector
+;       IDL> str = ['60:30','-60:30','-0:30'] 
+;       IDL> print,tenv(str)   ====>   Result =  [60.5,-60.5,-0.5]
+;
+; WARNING: 
+;       TENV() will recognize floating point values of -0.0 as negative numbers.
+;       However,  there is no distinction in the binary representation of -0 
+;       and 0  (integer values), and so TENV will treat both values as positive.
+; PROCEDURES USED:
+;       GETTOK(), REPCHR()  for string processing.
+; PROCEDURE:
+;	Mostly involves checking arguments and setting the sign.
+;
+;   MODIFICATION HISTORY:
+;	Written by W.B. Landsman           April, 1991
+;       Recognize -0.0   W. Landsman/B. Stecklum   Dec 2005
+;       Work with string input   W. Landsman Feb 2009
+;
+;-
+ compile_opt idl2
+ On_error,2                                 ;Return to caller
+
+ npar = N_params()
+ npts = N_elements(dd)
+ if npts EQ 0 then begin
+     print,'Syntax -  RESULT = TENV( dd, mm, ss)'
+     return, 0.0d
+ endif
+
+ if ( npar EQ 1 ) then begin 
+ if size(dd,/TNAME) EQ 'STRING' then begin 
+       temp = strtrim(dd,2)
+       temp = repchr(temp,':',' ')
+       neg = where( strmid(temp,0,1) EQ '-', Nneg)
+       value = abs(double(gettok(temp,' ')))
+       mm = double(gettok(temp,' '))
+       decimal =  value + mm/60. + double(temp)/3600.0d
+       if Nneg GT 0 then decimal[neg] = -decimal[neg]
+       return,decimal
+         
+ endif else return,double( dd )   ;No need to check for neg values.
+ endif
+
+ value = double( abs(dd) ) 
+
+ if ( npar GT 1 ) then begin               ;Add minutes/60., check for <0
+
+      if N_elements(mm) NE npts then $
+           message,'ERROR - Number of elements in each parameter must be equal'
+      nd=(strpos(string(dd),'-') ge 0)
+      nm=(strpos(string(mm),'-') ge 0)
+      neg =  nd OR nm
+      value = value + abs(mm)/60.0d
+
+ endif
+
+ if ( npar GT 2 ) then begin               ;Add sec/3600., check for <0
+
+      if N_elements(ss) NE npts then $
+           message,'ERROR - Number of elements in each parameter must be equal'
+      ns=(strpos(string(ss),'-') ge 0)
+      neg = neg OR ns
+      value = value + abs(ss)/3600.0d
+
+ endif
+
+ neg = where( neg, Nfound )                  ;Account for negative values
+ if ( Nfound GT 0 ) then value[neg] = -value[neg]
+
+ return,value      
+ end
diff --git a/Code/script_idl_mv/astrolib/textclose.pro b/Code/script_idl_mv/astrolib/textclose.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e05be1092e08476ae0289bb41eafa1c10851dc98
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/textclose.pro
@@ -0,0 +1,46 @@
+pro textclose,textout=textout
+;+
+; NAME:
+;	TEXTCLOSE                   
+;
+; PURPOSE:
+;	Close a text outpu file previously opened with TEXTOPEN 
+; EXPLANATION:
+;	procedure to close file for text output as specifed
+;	by the (non-standard) system variable !TEXTOUT. 
+;
+; CALLING SEQUENCE:
+;	textclose, [ TEXTOUT = ]
+;
+; KEYWORDS:
+;	textout - Indicates output device that was used by
+;		TEXTOPEN
+;
+; SIDE EFFECTS:
+;	if !textout is not equal to 5 and the textunit is
+;	opened.   Then unit !textunit is closed and released
+;
+; HISTORY:
+;	D. Lindler  Dec. 1986  (Replaces PRTOPEN)
+;	Test if TEXTOUT is a scalar string   W. Landsman   August 1993
+; Can't close unit -1 (Standard Output) I. Freedman  April  1994
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;-----------------------------------------------------------
+; CLOSE PROPER UNIT
+;
+
+ if N_elements( textout ) EQ 0 then textout = !textout ;use default
+
+ ptype = size( textout )           ;Test if TEXTOUT is a scalar string
+ if ptype[1] EQ 7  then text_out = 6 else text_out = textout
+
+ if ( text_out NE 5 ) then begin
+	if !textunit ne 0 AND !textunit ne -1 then begin
+		free_lun, !TEXTUNIT  
+                !textunit = 0
+	end
+ end
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/textopen.pro b/Code/script_idl_mv/astrolib/textopen.pro
new file mode 100644
index 0000000000000000000000000000000000000000..64325393562e1d76464be329154b5ccbaae76649
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/textopen.pro
@@ -0,0 +1,217 @@
+PRO TEXTOPEN,PROGRAM,TEXTOUT=TEXTOUT, STDOUT = STDOUT, MORE_SET = more_set, $
+             SILENT = silent, WIDTH = width
+;+
+; NAME:
+;       TEXTOPEN
+; PURPOSE:
+;       Open a device specified by TEXTOUT with unit !TEXTUNIT 
+; EXPLANATION:
+;       Procedure to open file for text output.   The type of output 
+;       device (disk file or terminal screen) is specified by the 
+;       TEXTOUT keyword or the (nonstandard) system variable !TEXTOUT.
+;
+; CALLING SEQUENCE:
+;       textopen, program, [ TEXTOUT =, /STDOUT, /SILENT, MORE_SET=, WIDTH= ]
+;
+; INPUTS:
+;       program - scalar string giving name of program calling textopen
+;
+; OPTIONAL INPUT KEYWORDS:
+;       TEXTOUT - Integer scalar (0-7) specifying output file/device to be 
+;               opened (see below) or scalar string giving name of output file.
+;               If TEXTOUT is not supplied, then the (non-standard) system 
+;               variable !TEXTOUT is used.
+;       /SILENT - By default, TEXTOPEN prints an informational message when
+;                opening a file for hardcopy output.   Set /SILENT (or !QUIET)
+;                to suppress this message.
+;       /STDOUT - if this keyword is set and non-zero, then the standard output
+;               (unit = -1) is used for TEXTOUT=1 or TEXTOUT=2.   The use
+;               of STDOUT has  2 possible advantages:
+;               (1) the output will appear in a journal file
+;               (2) Many Unix machines print spurious control characters when
+;               printing to /dev/tty.   These characters are eliminated by 
+;               setting /STDOUT
+;
+;               The disadvantage of /STDOUT is that the /MORE option is not
+;               available.
+;
+;         WIDTH - Specify line width for hardcopy output line wrapping (passed onto OPENW).
+;
+; OPTIONAL OUTPUT KEYWORD:
+;       MORE_SET - Returns 1 if the output unit was opened with /MORE.   This
+;               occurs if (1) TEXTOUT = 1 and (2) the device is a tty, and 
+;               (3) /STDOUT is not set.      User can use the returned value
+;                of MORE_SET to determine whether to end output when user
+;                presses 'Q'.
+; SIDE EFFECTS:
+;       The following dev/file is opened for output.    Different effects
+;       occur depending whether the standard output is a GUI (Macintosh,
+;       Windows, Unix/IDLTool) or a TTY
+;
+;               textout=0       Nowhere
+;               textout=1       if a TTY then TERMINAL using /more option
+;                                   otherwise standard (Unit=-1) output
+;               textout=2       if a TTY then TERMINAL without /more option
+;                                   otherwise standard (Unit=-1) output
+;               textout=3       <program>.prt
+;               textout=4       laser.tmp
+;               textout=5      user must open file
+;               textout=7      same as 3 but text is appended to <program>.prt
+;                               file if it already exists.
+;               textout = filename (default extension of .prt)
+;
+;       The unit to be opened is obtained with the procedure GET_LUN
+;       unless !TEXTOUT=5.  The unit number is placed in system variable 
+;       !TEXTUNIT.  For !TEXTOUT=5 the user must set !TEXTUNIT to the 
+;       appropriate unit number.
+;
+; NOTES:
+;       When printing to a TTY terminal, the output will *not* appear in an 
+;       IDL JOURNAL session, unlike text printed with the PRINT command.
+;
+; NON-STANDARD SYSTEM VARIABLES:
+;       TEXTOPEN will automatically define the following system variables if
+;       they are not previously defined:
+;
+;       DEFSYSV,'!TEXTOUT',1
+;       DEFSYSV,'!TEXTUNIT',0
+; HISTORY:
+;       D. Lindler  Dec. 1986  
+;       Keyword textout added, J. Isensee, July, 1990
+;       Made transportable, D. Neill, April, 1991
+;       Trim input PROGRAM string W. Landsman  Feb 1993
+;       Don't modify TEXTOUT value   W. Landsman   Aug 1993
+;       Modified for MacOS  I. Freedman April 1994
+;       Modified for output terminals without a TTY  W. Landsman  August 1995
+;       Added /STDOUT keyword   W. Landsman    April 1996
+;       added textout=7 option, D. Lindler, July, 1996
+;       Exit with RETURN instead of RETALL  W. Landsman  June 1999
+;       In IDL V5.4 filepath(/TERMINAL) not allowed in the IDLDE WL August 2001
+;       Added MORE_SET output keyword   W.Landsman   January 2002
+;       Added /SILENT keyword  W. Landsman  June 2002  
+;	Define !TEXTOUT and !TEXTUNIT if needed.  R. Sterner, 2002 Aug 27
+;       Return Calling Sequence if no parameters supplied W.Landsman Nov 2002
+;       Remove VMS specific code  W. Landsman Sep 2006
+;       Make sure MORE_SET is always defined   W. Landsman Jan 2007
+;       Added WIDTH keyword   J. Bailin Nov 2010
+;       Use V6.0 notation  W. Landsman April 2011
+;-
+;-----------------------------------------------------------
+  On_Error,2
+  compile_opt idl2
+
+  if N_params() LT 1 then begin
+      print,'Syntax - TEXTOPEN, program, [ TEXTOUT =, /STDOUT, /SILENT,' 
+      print,'                              MORE_SET=, WIDTH= ]' 
+      return
+  endif
+
+  defsysv,'!TEXTOUT',exists=ex			; Check if !TEXTOUT exists.
+  if ex eq 0 then defsysv,'!TEXTOUT',1		; If not define it.
+  defsysv,'!TEXTUNIT',exists=ex			; Check if !TEXTUNIT exists.
+  if ex eq 0 then defsysv,'!TEXTUNIT',0		; If not define it.
+  more_set = 0                                  
+  ;
+  ; Open proper unit.
+  ;
+  if N_elements( textout ) NE 1 then textout = !textout ;use default output dev.
+
+  ; keywords for openw
+  if n_elements(width) gt 0 then openw_keywords = {width: width}
+
+  if size(textout,/tname) EQ 'STRING' then begin  ;test if filename entered
+        filename = textout
+        j = strpos(filename,'.')        ;test if file extension given
+        if j lt 0 then filename = filename + ".prt"
+        text_out = 6
+  endif else text_out = textout     
+
+  if TEXT_OUT eq 5 then begin
+     if !TEXTUNIT eq 0 then begin
+         print,' '
+         print,' You must set !TEXTUNIT to the desired unit number...'
+         print,'                    ...see following example'
+         print,' '
+         print,'                    OPENW, LUN, filename, /GET_LUN
+         print,'                    !TEXTUNIT = LUN
+         print,'                    DBPRINT...
+         print,'
+         print,' Action: returning'
+         print,' '
+         return
+     end
+     return
+  end
+   stndout = fstat(-1)
+   isatty = (stndout.isatty) && (~stndout.isagui) && $
+             (~keyword_set(STDOUT))
+
+   if isatty || (text_out GT 2) then begin 
+
+        if !TEXTUNIT GT 0 then free_lun,!TEXTUNIT 
+        get_lun,unit
+        !TEXTUNIT = unit
+
+    endif else !TEXTUNIT = -1                     ;standard output
+
+  more_set = (text_out EQ 1) && isatty
+  
+  case text_out of
+     1: if isatty then openw, !TEXTUNIT, filepath(/TERMINAL), /MORE, _extra=openw_keywords
+
+     2: if isatty then openw, !TEXTUNIT, filepath(/TERMINAL) , _extra=openw_keywords
+
+     3: begin
+        oname = strlowcase( strtrim( PROGRAM,2) +'.prt')
+         openw, !TEXTUNIT, oname, _extra=openw_keywords
+        if ~keyword_set(SILENT) then $
+        message,'Output is being directed to a file ' + oname,/INFORM
+        end
+
+     4: openw, !TEXTUNIT, 'laser.tmp', _extra=openw_keywords
+
+     6: begin
+        openw,!TEXTUNIT,filename, _extra=openw_keywords
+        if ~keyword_set(SILENT) then $
+        message,'Output is being directed to a file ' + filename,/INFORM
+        end
+
+     7: begin
+        oname = strlowcase(strtrim( PROGRAM,2) +'.prt')
+        openw, !TEXTUNIT, oname, /append, _extra=openw_keywords
+        if ~keyword_set(SILENT) then $
+        message,'Output is being appended to file ' + oname,/INFORM
+        for i=0,3 do printf,!textunit,' '       ;added a couple of blank lines
+        end
+
+     0: openw,!TEXTUNIT, strtrim(PROGRAM,2) + '.tmp',/DELETE, _extra=openw_keywords
+
+     else: begin
+        !textunit = 0
+        print,' '
+        print,' Invalid value for TEXTOUT =',TEXTOUT
+        print,' '
+        print,'                 ...the possibilities are:
+        print,' '
+        print,'                 textout=0      nowhere
+        if isatty then begin
+                print,'                 textout=1      terminal with /more 
+                print,'                 textout=2      terminal without /more 
+        endif else begin
+                print,'                 textout=1      terminal
+                print,'                 textout=2      terminal
+        endelse
+        print,'                 textout=3      file   <program>.prt
+        print,'                 textout=4      file   laser.tmp
+        print,'                 textout=5      User supplied file
+        print,'                 textout = filename (default extension of .prt)
+        print,'                 textout=7      Same as 3 but append the file
+        print,' '
+        print,' Action: returning
+        print,' '
+        return
+    end
+ endcase
+
+ return
+ end   ; textout
diff --git a/Code/script_idl_mv/astrolib/tic_one.pro b/Code/script_idl_mv/astrolib/tic_one.pro
new file mode 100644
index 0000000000000000000000000000000000000000..35214717d1defc26aa3af4dd0b044dff504c2f09
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tic_one.pro
@@ -0,0 +1,63 @@
+pro tic_one, min, pixx, incr, min2, tic1, RA=ra
+;+
+; NAME:
+;	TIC_ONE
+; PURPOSE:
+;	Determine the position of the first tic mark for astronomical images.
+; EXPLANATION:
+;	For use in labelling images with right ascension
+;	and declination axes. This routine determines the 
+;	position in pixels of the first tic.
+;
+; CALLING SEQUENCE:
+;	tic_one, zmin, pixx, incr, min2, tic1, [RA = ]
+;
+; INPUTS:
+;	zmin  - astronomical coordinate value at axis zero point (degrees 
+;		or hours)
+;	pixx - distance in pixels between tic marks (usually obtained from TICS)
+;	incr - increment in minutes for labels (usually an even number obtained 
+;		from the procedure TICS)
+;
+; OUTPUTS:
+;	min2 - astronomical coordinate value at first tic mark 
+;	tic1 - position in pixels of first tic mark
+;
+; EXAMPLE:
+;	Suppose a declination axis has a value of 30.2345 degrees at its
+;	zero point.  A tic mark is desired every 10 arc minutes, which 
+;	corresponds to 12.74 pixels.  Then
+;
+;	IDL> TIC_ONE, 30.2345, 1, 12.74, min2, tic1
+;
+;	yields values of min2 = 30.333 and tic1 = 5.74, i.e. the first tic
+;	mark should be labeled 30 deg 20 minutes and be placed at pixel value
+;	5.74
+;
+; REVISION HISTORY:
+;	by B. Pfarr, 4/15/87
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+  On_error,2
+;                             convert min to minutes
+  if keyword_set(RA) then mul = 4.0000 else mul = 60.00000
+  min1 = min*mul		;Convert from degrees to minutes
+;
+  incra =  abs(incr)
+  rem = min1 mod incra                     ;get remainder
+  sign = min1*incr
+
+  if ( sign GT 0 ) then begin 
+
+	tic1 = pixx - abs(rem)*(pixx/incra)  
+	min2 = (min1+incr-rem)/mul 
+
+  endif else begin 
+
+	tic1 = abs(rem)*(pixx/incra)       
+	min2 = (min1 - rem)/mul      
+
+  endelse
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/ticlabels.pro b/Code/script_idl_mv/astrolib/ticlabels.pro
new file mode 100644
index 0000000000000000000000000000000000000000..91d07e952a3340ab4a7315a36c801177b6337325
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ticlabels.pro
@@ -0,0 +1,233 @@
+pro ticlabels, minval, numtics, incr, ticlabs, RA=ra, DELTA=delta, FONT=font
+;+
+; NAME:
+;	TICLABELS
+; PURPOSE:
+;	Create tic labels for labeling astronomical images.
+; EXPLANATION: 
+;	Used to display images with right ascension or declination
+;	axes.  This routine creates labels for already determined tic
+;	marks (every other tic mark by default)
+;
+; CALLING SEQUENCE:
+;	TICLABELS, minval, numtics, incr, ticlabs, [ RA = ,DELTA = ]
+;
+; INPUTS:
+;	minval  - minimum value for labels (degrees)
+;	numtics - number of tic marks
+;	incr    - increment in minutes for labels
+;
+; OUTPUTS:
+;	ticlabs - array of charater string labels
+;
+; OPTIONAL INPUT KEYWORDS:
+;	/RA - if this keyword is set then the grid axis is assumed to be
+;		a Right Ascension.   Otherwise a declination axis is assumed
+;	DELTA - Scalar specifying spacing of labels.   The default is 
+;		DELTA = 2 which means that a label is made for every other tic
+;		mark.  Set DELTA=1 to create a label for every tic mark.
+;       FONT - scalar font graphics keyword (-1,0 or 1) for text
+;
+; PROCEDURES USED:
+;	RADEC
+;
+; RESTRICTIONS:
+;	Invalid for wide field (> 2 degree) images since it assumes that a 
+;	fixed interval in Y (or X) corresponds to a fixed interval in Dec 
+;	(or RA)
+;
+; REVISON HISTORY:
+;	written by B. Pfarr, 4/15/87
+;	Added DELTA keywrd for compatibility with IMCONTOUR W. Landsman Nov 1991
+;	Added nicer hms and dms symbols when using native PS fonts Deutsch 11/92
+;	Added Patch for bug in IDL <2.4.0 as explained in NOTES E. Deutsch 11/92
+;	Fix when crossing 0 dec or 24h RA
+;	Fix DELTA keyword so that it behaves according to the documentation
+;			W. Landsman  Hughes STX,  Nov 95  
+;       Allow sub arcsecond formatting  W. Landsman   May 2000
+;       Better formatting for non-unity DELTA values  W. Landsman July 2004
+;       Allow FONT keyword to be passed.  T. Robishaw Apr. 2006
+;       Write 0h rather than 24h  W. L. August 2008
+;       Fix problem when tic values is exactly 0 degrees   Mar 2012
+;       Only modulo 24 when /RA is set  WL.  October 2012
+;-
+ On_error,2
+ compile_opt idl2
+;                               convert min to hours, minutes, secs
+  if N_params() LT 4 then begin
+
+     print,'Syntax - ticlabels, minval, numtics, incr, ticlabs, ' + $
+                     '[ /RA  ,DELTA = ]'
+     return
+
+  endif
+
+  if N_elements(FONT) eq 0 then font = !p.font
+
+  ticlabs = replicate(' ',numtics )
+
+  if minval LT 0 then begin 
+	neg = -1 & sgn = '-'
+  endif else begin
+	neg = 1  & sgn = '' 
+  endelse
+   firstval = minval
+  if ~keyword_set( DELTA ) then delta = 2
+
+   
+  if keyword_set( RA ) then begin                  ;Define RA tic symbols
+
+       radec, firstval, 0, minh, minm, mins, dum1, dum2, dum3 
+       sd = '!Ah!N' & sm = '!Am!N'  & ss = '!As!N' 
+
+       if (!d.name eq 'PS') and (font eq 0) then begin    ;Postscript fonts?
+         sd ='!Uh!N' & sm='!Um!N' & ss='!Us!N' 
+         endif
+
+ endif else begin 
+
+       radec, 0, firstval, dum1, dum2, dum3, minh, minm, mins
+       minm = abs(minm)
+       mins = abs(mins)
+       sd = "!Ao!N" & sm = "'" &  ss = "''"
+
+       if (!d.name eq 'PS') and (font eq 0) then begin
+
+         RtEF = '!X'
+         sd = '!9' + string(176b) + RtEF 
+         sm = '!9' + string(162b) + RtEF
+         ss = '!9' + string(178b) + RtEF
+       endif
+ 
+ endelse
+
+     inc1 = incr*60.0d
+      inc = incr*60.0d*delta           ;increment in arc seconds
+      if abs(inc1) GE 1.0 then begin 
+             mins = round(mins)
+            sfmt = '(i2.2)' 
+      endif else $  
+         if abs(inc1) GT 0.1 then  sfmt = '(f4.1)' else sfmt = '(f5.2)'
+      if abs(inc) GE 1.0 then  inc = round(inc)
+      
+
+     while (mins GE 60) do begin
+            mins = mins - 60
+            minm++
+     endwhile
+     
+     if (minm ge 60) then begin
+             minm = minm - 60
+             minh = minh + neg
+     endif
+ 
+
+ if (abs(mins) GT 1) || (abs(incr) LT 1.0/DELTA)  then begin      ;Seconds
+
+    ticlabs[0] = sgn + string( abs(minh), '(i2.2)') + sd + ' ' + $
+            string(minm,'(i2.2)') + sm + ' ' + string( mins, sfmt) + ss  
+  
+      for i = delta,numtics-1, delta do begin
+     
+         mins = mins + neg*inc
+         if ( ( mins GE 60) || (mins LE 0) ) then begin
+
+            while ( mins GE 60 ) do begin
+                mins = mins - 60
+                minm++
+            endwhile
+
+            while ( mins LT 0 ) do begin
+                mins = mins + 60
+                minm--
+             endwhile
+    
+         if (minm ge 60) then begin
+             minm = minm - 60
+             minh = minh + neg
+             ticlabs[i]= sgn + string(abs(minh),'(i2.2)') + sd + ' ' + $ 
+                         string(minm,'(i2.2)') + sm
+ 
+        endif else if (minm LE 0) then begin
+	     
+	     if minh EQ 0 then begin             ;Cross zero Dec or RA?
+		if keyword_set(RA) then begin
+			minh = 23
+			minm = minm + 60
+		endif else begin
+			 minm = -minm
+	                 neg = -neg
+		         if neg EQ 1 then sgn = '' else sgn = '-'
+		endelse
+	     endif else begin
+	             minm = minm + 60
+	             minh = minh - neg
+             endelse
+
+	     ticlabs[i]= sgn + string(abs(minh),'(i2.2)') + sd + ' ' + $ 
+              string((minm),'(i2)') + sm + ' ' +string(mins,sfmt) + ss
+
+
+        endif else ticlabs[i] = string( minm, '(i2.2)' ) + sm + ' '+ $
+                         string( mins, sfmt) + ss
+
+         endif else ticlabs[i] = string( mins, sfmt ) + ss
+
+      endfor
+
+ endif else $
+    if (abs(minm) gt 1) || (abs(incr) LT 60.0/DELTA) then begin ;MINUTES
+
+      inc = fix(incr*DELTA)
+      ticlabs[0] = sgn + string(abs(minh),'(i2.2)')+ sd+ ' ' + $
+		string(minm,'(i2.2)') + sm
+      for i = delta,numtics-1, delta do begin
+         minm = minm + neg*inc
+
+         if (minm ge 60) then begin
+             minm = minm - 60
+             minh = minh + neg
+	     if keyword_set(RA) then begin
+	        while minh LT 0 do minh = minh + 24
+	        while minh GE 24 do minh = minh - 24
+             endif  
+             ticlabs[i]= sgn + string(abs(minh),'(i2.2)') + sd + ' ' + $
+			string(minm,'(i2.2)') +sm
+
+         endif else if (minm LT 0) then begin
+
+	     if minh EQ 0 then begin             ;Cross zero Dec or RA?
+		if keyword_set(RA) then begin
+			minh = 23
+			minm = minm + 60
+		endif else begin
+			 minm = -minm
+	                 neg = -neg
+		         if neg EQ 1 then sgn = '' else sgn = '-'
+		endelse
+	     endif else begin
+	             minm = minm + 60
+	             minh = minh - neg
+             endelse
+	     ticlabs[i]= sgn + string(abs(minh),'(i2.2)') + sd + ' ' + $
+			string((minm),'(i2.2)') + sm
+      endif else ticlabs[i] = string(minm,'(i2.2)')
+      endfor
+ endif else begin                        ;Hours/Degrees
+
+      inc = fix(DELTA*incr/60.0)
+      ticlabs[0] = strtrim(minh,2) + sd 
+      for i = delta,numtics-1, delta do begin
+          minh = minh + inc
+	  if keyword_set(RA) then begin
+	  
+		while minh LT 0 do minh = minh + 24
+		while minh GE 24 do minh = minh - 24
+          endif
+          ticlabs[i] = strtrim( minh,2) + sd
+      endfor      
+
+ endelse
+
+ return    
+ end
diff --git a/Code/script_idl_mv/astrolib/ticpos.pro b/Code/script_idl_mv/astrolib/ticpos.pro
new file mode 100644
index 0000000000000000000000000000000000000000..92e621ac670eac47caa3e07bbe85b8743262b14a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ticpos.pro
@@ -0,0 +1,88 @@
+pro ticpos,deglen,pixlen,ticsize,incr,units      ;Compute tic positions
+;+
+; NAME:
+;       TICPOS
+; PURPOSE:
+;       Specify distance between tic marks for astronomical coordinate overlays
+; EXPLANATION:
+;       User inputs number an approximate distance
+;       between tic marks, and the axis length in degrees.  TICPOS will return 
+;       a distance between tic marks such that the separation is a round
+;       multiple in arc seconds, arc minutes, or degrees
+;
+; CALLING SEQUENCE:
+;       TICPOS, deglen, pixlen, ticsize, incr, units
+;
+; INPUTS:
+;       deglen - length of axis in DEGREES
+;       pixlen - length of axis in plotting units (pixels)
+;       ticsize - distance between tic marks (pixels).  This value will be
+;               adjusted by TICPOS such that the distance corresponds to
+;               a round multiple in the astronomical coordinate.
+;
+; OUTPUTS:
+;       ticsize - distance between tic marks (pixels), positive scalar 
+;       incr    - incremental value for tic marks in round units given 
+;               by the UNITS parameter
+;       units - string giving units of ticsize, either 'ARC SECONDS',
+;               'ARC MINUTES', or 'DEGREES'
+;
+; EXAMPLE:
+;       Suppose a 512 x 512 image array corresponds to 0.2 x 0.2 degrees on
+;       the sky.   A tic mark is desired in round angular units, approximately 
+;       every 75 pixels.
+;
+;       IDL> ticsize = 75
+;       IDL> TICPOS,0.2,512,ticsize,incr,units   
+;
+;       ==> ticsize = 85.333, incr = 2. units = 'Arc Minutes'
+;
+;       i.e. a good tic mark spacing is every 2 arc minutes, corresponding
+;       to 85.333 pixels.
+;
+; REVISON HISTORY:
+;       written by W. Landsman            November, 1988
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Don't use all capital letters  W. Landsman May 2003
+;       Fix case where incr crosses degree/minute or minute/degree boundary
+;               A. Mortier/W.Landsman April 2005
+;-
+  On_error,2
+
+  minpix = deglen*60./pixlen            ;Arc minute per pixel
+  incr = minpix*ticsize                 ;Arc minutes between tics
+
+  if (incr LT 0 ) then sgn = -1 else sgn = 1
+  incr = abs(incr)
+  if ( incr GE 30 )  then units = 'Degrees' else $
+  if ( incr LE 0.5 ) then units = 'Arc Seconds'  $
+                     else units = 'Arc Minutes'
+;                                        determine increment
+  case 1 of 
+
+    incr GE 120.0  : incr =  4.         ;degrees
+    incr GE  60.0  : incr =  2.         ;degrees
+    incr GE  30.0  : incr =  1.         ;degrees
+    incr GT  15.0  : incr = 30.         ;minutes 
+    incr GE  10.0  : incr = 15.         ;minutes  
+    incr GE   5.0  : incr = 10.         ;minutes
+    incr GE   2.0  : incr =  5.         ;minutes
+    incr GE   1.0  : incr =  2.         ;minutes
+    incr GT   0.5  : incr =  1.         ;minutes
+    incr GE   0.25 : incr = 30.         ;seconds
+    incr GE   0.16 : incr = 15.         ;seconds
+    incr GE   0.08 : incr = 10.         ;seconds
+    incr GE   0.04 : incr =  5.         ;seconds
+    incr GE   0.02 : incr = 2.           ;seconds
+    incr LT   0.02 : incr = 1.           ;seconds
+  
+  endcase                                         
+
+  if ( units EQ 'Arc Seconds' ) then minpix = minpix*60. else $
+  if ( units EQ 'Degrees' )  then minpix = minpix/60.
+
+  ticsize= incr/abs(minpix)                ;determine ticsize
+  incr = incr*sgn
+ 
+  return 
+  end
diff --git a/Code/script_idl_mv/astrolib/tics.pro b/Code/script_idl_mv/astrolib/tics.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ab28918d7678bdef704689f6af778e790925c630
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tics.pro
@@ -0,0 +1,76 @@
+pro tics,radec_min,radec_max,numx,ticsize,incr,RA=ra
+;+
+; NAME:
+;       TICS
+; PURPOSE:
+;       Compute a nice increment between tic marks for astronomical images.
+; EXPLANATION:       
+;       For use in labelling a displayed image with right ascension
+;       or declination axes.  An approximate distance between tic 
+;       marks is input, and a new value is computed such that the 
+;       distance between tic marks is in simple increments of the 
+;       tic label values.
+;
+; CALLING SEQUENCE:
+;       tics, radec_min, radec_max, numx, ticsize, incr, [ /RA ]
+;
+; INPUTS:
+;       radec_min - minimum axis value (degrees)
+;       radec_max - maximum axis value (degrees)
+;       numx  - number of pixels in x direction
+;
+; INPUT/OUTPUT  
+;       ticsize - distance between tic marks (pixels)
+;
+; OUTPUTS:
+;       incr    - incremental value for tic labels (in minutes of 
+;               time for R.A., minutes of arc for dec.)
+;
+; REVISON HISTORY:
+;       written by B. Pfarr, 4/14/87
+;       Added some more tick precision (i.e. 1 & 2 seconds in case:) EWD May92
+;       Added sub arcsecond tick precision   W. Landsman   May 2000
+;       Plate scale off by 1 pixel  W. Landsman July 2004
+;-
+  On_error,2
+
+  numtics = numx/ticsize                   ;initial number of tics
+
+;     Convert total distance to arc minutes for dec. or to
+;     minutes of time for r.a.
+
+  if keyword_set(RA) then mul = 4.0 else mul = 60.
+  mins = abs(radec_min-radec_max)*mul       ;total distance in minutes
+  rapix = (numx-1)/mins                        ;pixels per minute
+  incr = mins/numtics                      ;minutes per tic
+
+;                                        determine increment
+  case 1 of 
+    incr GE 120.0  : incr = 480.0       ; 4 hours
+    incr GE  60.0  : incr = 120.0       ; 2 hours
+    incr GE  30.0  : incr =  60.0       ; 1 hour
+    incr GE  15.0  : incr =  30.0       ; 30 minutes 
+    incr GE  10.0  : incr =  15.0       ; 15 minutes
+    incr GE   5.0  : incr =  10.0       ; 10 minutes
+    incr GE   2.0  : incr =   5.0       ;  5 minutes
+    incr GE   1.0  : incr =   2.0       ;  2 minutes
+    incr GE   0.5  : incr =   1.0       ;  1 minute
+    incr GE   0.25 : incr =   0.5       ; 30 seconds
+    incr GE   10/60.0d  : incr =   0.25      ; 15 seconds
+    incr GE   5/60.0d   : incr =   10/60.0d  ; 10 seconds
+    incr GE   2/60.0d   : incr =   5/60.0d   ;  5 seconds
+    incr GE   1/60.0d   : incr =   2/60.0d   ;  2 seconds
+    incr GE   0.5/60.0d : incr =   1./60.0d  ;  1 seconds
+    incr GE   0.2/60.0d : incr = 0.5/60.0d   ;  0.5 seconds
+    incr GE   0.1/60.0d  : incr = 0.2/60.0d    ;  0.2 seconds
+    incr GE   0.05/60.0d : incr = 0.1/60.0d    ;  0.1 seconds
+    incr GE   0.02/60.0d : incr = 0.05/60.0d   ;  0.05 seconds
+    incr GE   0.01/60.0d : incr = 0.02/60.0d   ;  0.02 seconds
+    incr GE   0          : incr = 0.01/60.0d   ;  0.01 seconds
+  endcase
+
+   ticsize = rapix*incr                 ;determine ticsize
+   if ( radec_min GT radec_max ) then incr = -incr 
+
+   return 
+  end
diff --git a/Code/script_idl_mv/astrolib/tnx_eval.pro b/Code/script_idl_mv/astrolib/tnx_eval.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d5dcbba8a86588c9627d90c8222452dacd5e21a7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tnx_eval.pro
@@ -0,0 +1,134 @@
+function TNX_eval, xy
+
+;+
+; NAME:
+;     TNX_EVAL
+; PURPOSE:
+;     Compute distorted coordinates given TNX (Tangent + Iraf tnx
+;     distortion  polynomial) coefficients.
+; EXPLANATION:
+;     See http://fits.gsfc.nasa.gov/registry/tnx.html for the TNX convention
+;   
+;     This distortion convention is used by IRAF.    The current procedures only
+;     supports simple polynomials and not Legendre or Chebyshev polynomials
+;   
+;     The coefficients and information are passed via common block.    This is because this
+;     routine is called by the intrinisc BROYDEN() function in AD2XY, and 
+;     common blocks are the only way to pass parameters to the user supplied 
+;     function in BROYDEN().  
+; CALLING SEQUENCE:
+;     res = TNX_EVAL(xy)   
+; INPUTS:
+;     xy - 2 elements vector giving the undistorted X,Y position  
+; OUTPUTS:
+;     res - 2 element vector giving the distorted position 
+; COMMON BLOCKS: 
+;      common broyden_coeff,pv1,pv2
+;
+;      pv1, pv2 are both structures giving the TNX coefficients. The
+;      pv1/pv2 naming convention is a hangover from tpv_eval.pro on
+;      which this approach is heavily based.
+;      pv1.functype gives the TNX function type. Only type 3
+;         (polynomial) is supported.
+;      pv1.xterms gives the type of cross-terms (1: full, 2: half, 0: none)
+;      pv1.etaorder gives the order in eta
+;      pv1.xiorder gives the order in xi
+;      pv1.coeff gives the actual coefficients.
+; REVISION HISTORY:
+;     Written   M. Sullivan                  Mar 2014
+;     Use post-V6.0 notation  W. Landsman    Feb 2015
+;-
+
+compile_opt idl2,hidden
+common broyden_coeff,pv1,pv2
+
+lngcor=pv1
+latcor=pv2
+
+if N_elements(xy) EQ 2 then begin
+   x = xy[0]
+   y = xy[1]
+endif else begin    
+   x = reform(xy[*,0])
+   y = reform(xy[*,1])
+endelse   
+
+IF(lngcor.functype NE 3 || latcor.functype NE 3)THEN BEGIN
+   PRINT,'ERROR in tnx_eval: only functype=3 (polynominal) is supported)'
+   RETURN,0
+ENDIF
+
+
+IF(lngcor.functype EQ 1 || lngcor.functype EQ 2)THEN xin = (2. * x - (lngcor.ximax + lngcor.ximin)) / (lngcor.ximax - lngcor.ximin) ELSE xin=x
+IF(latcor.functype EQ 1 || latcor.functype EQ 2)THEN etain = (2. * y - (latcor.etamax + latcor.etamin)) / (latcor.etamax - latcor.etamin) ELSE yin=y
+
+xp=0.d0
+icount=0L
+IF(lngcor.xterms EQ 1)THEN BEGIN
+   ;; full cross-terms
+   FOR n=0,lngcor.etaorder-1 DO BEGIN
+      FOR m=0,lngcor.xiorder-1 DO BEGIN
+         xp += xin^m * yin^n * lngcor.coeff[icount]
+         icount++
+      ENDFOR
+   ENDFOR
+ENDIF ELSE IF(lngcor.xterms EQ 0)THEN BEGIN
+   ;; no cross-terms
+   FOR m=0,lngcor.xiorder-1 DO BEGIN
+      xp += xin^m * lngcor.coeff[icount]
+      icount++
+   ENDFOR
+   FOR n=0,lngcor.etaorder-1 DO BEGIN
+      xp += yin^n * lngcor.coeff[icount]
+      icount++
+   ENDFOR
+ENDIF ELSE IF(lngcor.xterms EQ 2)THEN BEGIN
+   ;; half cross terms
+   maxxt=MAX([lngcor.xiorder,lngcor.etaorder])-1
+   FOR n=0,lngcor.etaorder-1 DO BEGIN
+      FOR m=0,lngcor.xiorder-1 DO BEGIN
+         IF(m+n GT maxxt)THEN CONTINUE
+         xp += xin^m * yin^n * lngcor.coeff[icount]
+         icount++
+      ENDFOR
+   ENDFOR   
+ENDIF
+
+yp = 0.d0
+icount = 0L
+IF(latcor.xterms EQ 1)THEN BEGIN
+   ;; full cross-terms
+   FOR n=0,latcor.etaorder-1 DO BEGIN
+      FOR m=0,latcor.xiorder-1 DO BEGIN
+         yp += xin^m * yin^n * latcor.coeff[icount]
+         icount++
+      ENDFOR
+   ENDFOR
+ENDIF ELSE IF(latcor.xterms EQ 0)THEN BEGIN
+   ;; no cross-terms
+   FOR m=0,latcor.xiorder-1 DO BEGIN
+      yp += xin^m * latcor.coeff[icount]
+      icount++
+   ENDFOR
+   FOR n=0,latcor.etaorder-1 DO BEGIN
+      yp += yin^n * latcor.coeff[icount]
+      icount++
+   ENDFOR
+ENDIF ELSE IF(latcor.xterms EQ 2)THEN BEGIN
+   ;; half cross terms
+   maxxt=MAX([latcor.xiorder,latcor.etaorder])-1
+   FOR n=0,latcor.etaorder-1 DO BEGIN
+      FOR m=0,latcor.xiorder-1 DO BEGIN
+         IF(m+n GT maxxt)THEN CONTINUE
+         yp += xin^m * yin^n * latcor.coeff[icount]
+         icount++
+      ENDFOR
+   ENDFOR   
+ENDIF
+
+xp = x+xp
+yp = y+yp
+
+return, [[xp],[yp]]
+
+end
diff --git a/Code/script_idl_mv/astrolib/to_hex.pro b/Code/script_idl_mv/astrolib/to_hex.pro
new file mode 100644
index 0000000000000000000000000000000000000000..42033975adef58e7815f8ccbc1f7fcf7d79d7d1b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/to_hex.pro
@@ -0,0 +1,44 @@
+FUNCTION TO_HEX, D, NCHAR
+;+
+; NAME:
+;       TO_HEX
+; PURPOSE:
+;       Translate a non-negative decimal integer to a hexadecimal string
+; CALLING SEQUENCE:
+;       HEX = TO_HEX( D, [ NCHAR ] )
+; INPUTS:
+;       D - non-negative decimal integer, scalar or vector.  If input as a
+;           string, (e.g. '32') then all leading blanks are removed.
+;
+; OPTIONAL INPUT:
+;       NCHAR - number of characters in the output hexadecimal string.
+;               If not supplied, then the hex string will contain no 
+;               leading zeros.
+;
+; OUTPUT:
+;       HEX - hexadecimal translation of input integer, string
+;
+; EXAMPLES:
+;       IDL> A = TO_HEX([11,16])    ==>   A = ['B','10']
+;       IDL> A = TO_HEX(100,3) ==>   A = '064'
+;
+; METHOD:
+;       The hexadecimal format code '(Z)' is used to convert.  No parameter
+;       checking is done.
+; PROCEDURES CALLED:
+;       None.
+; REVISION HISTORY:
+;       Written   W. Landsman         November, 1990
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Use FSTRING() for more than 1024 values      March 2000 
+;       Assume since  V5.4, omit FSTRING() call      April 2006
+;-
+
+  if N_elements(nchar) EQ 0 then format = '(Z)' else begin
+      ch = strtrim( nchar, 2 ) 
+      format = '(Z' + ch + '.' + ch + ')'
+  endelse
+
+  return, strtrim( string(d, FORM = format), 2)
+
+  end
diff --git a/Code/script_idl_mv/astrolib/tpv_eval.pro b/Code/script_idl_mv/astrolib/tpv_eval.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3f7c8d40e5b36f180da2b308be62643027cb208a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tpv_eval.pro
@@ -0,0 +1,92 @@
+function TPV_eval, xy
+;+
+; NAME:
+;     TPV_EVAL
+; PURPOSE:
+;     Compute distorted coordinates given TPV (Tangent + PV_ polynomial) 
+;     coefficients.
+; EXPLANATION:
+;     See http://fits.gsfc.nasa.gov/registry/tpvwcs.html for the TPV convention
+;   
+;     This distortion convention is used by the SCAMP software 
+;     ( http://www.astromatic.net/software/scamp ) though SCAMP does not 
+;     include the '-TPV' in the CTYPE keyword.
+;   
+;     The coefficients are passed via common block.    This is because this
+;     routine is called by the intrinisc BROYDEN() function in AD2XY, and 
+;     common blocks are the only way to pass parameters to the user supplied 
+;     function in BROYDEN().  
+; CALLING SEQUENCE:
+;     res = TPV_EVAL(xy)   
+; INPUTS:
+;     xy - 2 elements vector giving the undistorted X,Y position  
+; OUTPUTS:
+;     res - 2 element vector giving the distorted position 
+; COMMON BLOCKS: 
+;      common broyden_coeff,pv1,ycoeff
+;
+;      pv1, YCOEFF are both vectors giving the TPV coefficients
+; REVISION HISTORY:
+;     Written   W. Landsman                  Dec 2013
+;     Correct several typos for 4th power terms    M. Sullivan  Mar 2014
+;-
+compile_opt idl2,hidden
+common broyden_coeff,pv1,pv2
+
+Npv1 = N_elements(pv1)
+NPv2 = N_elements(pv2)
+
+if N_elements(xy) EQ 2 then begin
+   x = xy[0]
+   y = xy[1]
+endif else begin    
+   x = reform(xy[*,0])
+   y = reform(xy[*,1])
+endelse   
+x2 = x*x
+y2 = y*y
+
+xp = pv1[0] + pv1[1]*x + pv1[2]*y
+if Npv1 GT 3 && (pv1[3] NE 0.0) then xp += pv1[3]*sqrt(x2 + y2)
+if Npv1 GT 4 && (pv1[4] NE 0.0) then xp += pv1[4]*x2
+if Npv1 GT 5 && (pv1[5] NE 0.0) then xp += pv1[5]*x*y
+if Npv1 GT 6 && (pv1[6] NE 0.0) then xp += pv1[6]*y2
+if Npv1 GT 7 then begin
+  if pv1[7] NE 0.0 then xp += pv1[7]*x^3
+  if Npv1 GT 8 && (pv1[8] NE 0.0) then xp += pv1[8]*x2*y
+  if Npv1 GT 9 && (pv1[9] NE 0.0) then xp += pv1[9]*x*y2
+  if Npv1 GT 10 && (pv1[10] NE 0.0) then xp += pv1[10]*y2*y
+  if Npv1 GT 11 && (pv1[11] NE 0.0) then xp += pv1[11]*sqrt(x2+y2)^3
+  if Npv1 GT 12 then begin
+      if (pv1[12] NE 0.0) then xp += pv1[12]*y2*y2
+      if Npv1 GT 13 && (pv1[13] NE 0.0) then xp += pv1[13]*x2*x*y
+      if Npv1 GT 14 && (pv1[14] NE 0.0) then xp += pv1[14]*x2*y2
+      if Npv1 GT 15 && (pv1[15] NE 0.0) then xp += pv1[15]*x*y2*y
+      if Npv1 GT 16 && (pv1[16] NE 0.0) then xp += pv1[16]*y2*y2
+      endif
+ endif      
+
+yp = pv2[0] + pv2[1]*y + pv2[2]*x
+if Npv2 GT 3 && (pv2[3] NE 0.0) then yp += pv2[3]*sqrt(x2 + y2)
+if NPv2 GT 4 && (pv2[4] NE 0.0) then yp += pv2[4]*y2
+if NPv2 GT 5 && (pv2[5] NE 0.0) then yp += pv2[5]*x*y
+if NPv2 GT 6 && (pv2[6] NE 0.0) then yp += pv2[6]*x2
+if NPv2 GT 7 then begin
+  if pv2[7] NE 0.0 then yp += pv2[7]*y^3
+  if NPv2 GT 8 && (pv2[8] NE 0.0) then yp += pv2[8]*y2*x
+  if NPv2 GT 9 && (pv2[9] NE 0.0) then yp += pv2[9]*y*x2
+  if NPv2 GT 10 && (pv2[10] NE 0.0) then yp += pv2[10]*x2*x
+  if NPv2 GT 11 && (pv2[11] NE 0.0) then yp += pv2[11]*sqrt(x2+y2)^3
+  if NPv2 GT 12 then begin
+      if (pv2[12] NE 0.0) then yp += pv2[12]*y2*y2
+      if NPv2 GT 13 && (pv2[13] NE 0.0) then yp += pv2[13]*y2*y*x
+      if NPv2 GT 14 && (pv2[14] NE 0.0) then yp += pv2[14]*y2*x2
+      if NPv2 GT 15 && (pv2[15] NE 0.0) then yp += pv2[15]*y*x2*x
+      if NPv2 GT 16 && (pv2[16] NE 0.0) then yp += pv2[16]*x2*x2
+      endif
+      
+ endif      
+   	 
+return, [[xp],[yp]]
+
+end
diff --git a/Code/script_idl_mv/astrolib/transform_coeff.pro b/Code/script_idl_mv/astrolib/transform_coeff.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b8094f1b9db30b85f0020c66628bdc971d0fb5ee
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/transform_coeff.pro
@@ -0,0 +1,62 @@
+
+function transform_coeff, coeff, alpha, beta
+;+
+; NAME:
+;    TRANSFORM_COEFF()
+; PURPOSE:
+;    Compute new polynomial coefficients under a linear transformation 
+; EXPLANATION:
+;     Suppose one has a (nonlinear) polynomial (similar to the POLY() function)
+;          y = C[0] + C[1]*x  + C[2]*x^2 + C[3]*x^3 + ...
+;
+;      and one has a linear transformation in X
+;
+;          x = alpha*x' + beta
+;     This function computes the new polynomial coefficients under the linear
+;     transformation. 
+;
+; CALLING SEQUENCE:
+;     newcoeff = TRANSFORM_COEFF( coeff, alpha, beta)
+; INPUTS:
+;     Coeff  -  vector of polynomial coefficients (as with POLY()).    The 
+;         degree of the polynomial is N_elements(coeff) - 1
+;     Alpha, Beta - numeric scalars defining the linear transformation in X 
+; OUTPUTS:
+;    NewCoeff - Vector (same size as Coeff) giving the new polynomial 
+;               coefficients
+; EXAMPLE:
+;     Suppose one has polynomial mapping a nonlinear distortion in the X 
+;     direction of a spectrum
+;
+;     y = 0.2 + 1.1*x + 0.1*x^2
+;
+;     if one rebins the spectrum to half the size then the linear transformation
+;     is  x = 2.*x'
+;     so alpha = 2 and beta = 0
+;     The new coefficients are
+;     IDL> print, transform_coeff([0.2,1.1,0.1],2.,0) 
+;     ==> [0.2, 2.2, 0.4] 
+; METHOD:
+;    Performs a binomial expansion of the polynomial and collect like terms
+;    groups.google.com/group/comp.lang.idl-pvwave/msg/11132d96d9c0f93d?hl=en&
+; REVISION HISTORY:
+;   Written   W. Landsman          December 2007
+;-
+compile_opt idl2
+if N_Params() LT 3 then begin
+    print,'Syntax - newcoeff = TRANSFORM_COEFF( coeff, alpha, beta) '
+    if N_elements(coeff) GT 0 then return,coeff else return,-1
+endif    
+degree=n_elements(coeff)-1
+
+newarray=coeff*0
+
+FOR i=0,degree DO BEGIN
+    FOR j=0,i DO BEGIN
+       newarray[j] = newarray[j] +  $
+          coeff[i]*factorial(i)*alpha^j*beta^(i-j)/factorial(j)/factorial(i-j)
+    ENDFOR
+ENDFOR 
+
+return, newarray
+end
diff --git a/Code/script_idl_mv/astrolib/trapzd.pro b/Code/script_idl_mv/astrolib/trapzd.pro
new file mode 100644
index 0000000000000000000000000000000000000000..11e0cda795109365868489ec20265ae443c1831f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/trapzd.pro
@@ -0,0 +1,82 @@
+pro trapzd, func, a, b, s, step, _EXTRA = _EXTRA
+;+
+; NAME:
+;       TRAPZD
+; PURPOSE:
+;       Compute the nth stage of refinement of an extended trapezoidal rule.
+; EXPLANATION:
+;       This procedure is called by QSIMP and QTRAP.   Algorithm from Numerical
+;       Recipes, Section 4.2.   TRAPZD is meant to be called iteratively from
+;       a higher level procedure.
+;
+; CALLING SEQUENCE:
+;       TRAPZD, func, A, B, S, step, [ _EXTRA = ]
+;
+; INPUTS:
+;       func - scalar string giving name of function to be integrated.   This
+;               must be a function of one variable.
+;       A,B -  scalars giving the limits of the integration
+;
+; INPUT-OUTPUT:
+;       S -    scalar giving the total sum from the previous iterations on 
+;               input and the refined sum after the current iteration on output.
+;
+;       step - LONG scalar giving the number of points at which to compute the
+;               function for the current iteration.   If step is not defined on
+;               input, then S is intialized using the average of the endpoints
+;               of limits of integration.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       Any supplied keywords will be passed to the user function via the 
+;       _EXTRA facility. 
+;
+; NOTES:
+;       (1) TRAPZD will check for math errors (except for underflow) when 
+;       computing the function at the endpoints, but not on subsequent 
+;       iterations.
+;
+;       (2) TRAPZD always uses double precision to sum the function values
+;       but the call to the user-supplied function is double precision only if 
+;       one of the limits A or B is double precision.
+; REVISION HISTORY:
+;       Written         W. Landsman                 August, 1991
+;       Always use double precision for TOTAL       March, 1996
+;       Pass keyword to function via _EXTRA facility  W. Landsman July 1999
+;       Don't check for floating underflow  W.Landsman  April 2008
+;-
+ On_error,2
+ compile_opt idl2
+
+ kpresent = keyword_set(_EXTRA)
+ if N_elements(step) EQ 0 then begin          ;Initialize?
+
+;If a math error occurs, it is likely to occur at the endpoints
+     junk = check_math()                    ;
+     if kpresent then s1 = CALL_FUNCTION(func,A, _EXTRA= _EXTRA) $
+                 else s1 = CALL_FUNCTION(func,A)
+     if check_math(mask=211) NE 0 then $
+        message,'ERROR - Illegal lower bound of '+strtrim(A,2)+ $
+                ' to function ' + strupcase(func)
+     if kpresent then s2 = CALL_FUNCTION(func,B, _EXTRA = _EXTRA) $
+                 else s2 = CALL_FUNCTION(func,B)
+     if check_math(mask=211) NE 0 then $
+        message,'ERROR - Illegal upper bound of '+strtrim(B,2) + $
+                ' to function ' + strupcase(func)
+     junk= check_math()		
+     s = 0.5d * ( double(B)-A ) * ( s1+s2 )    ;First approx is average of endpoints
+     step = 1l
+
+ endif else begin
+
+     tnm = float( step )               
+     del = ( B - A ) / tnm                    ;Spacing of the points to add
+     x = A + 0.5*del + findgen( step ) * del  ;Grid of points @ compute function
+     if kpresent then sum = CALL_FUNCTION( func, x, _EXTRA = _EXTRA) $
+                 else sum = CALL_FUNCTION( func, x)
+     S = 0.5d * ( S + (double(B)-A) * total( sum, /DOUBLE )/tnm )     
+     step = 2*step
+
+ endelse
+
+ return
+ end 
diff --git a/Code/script_idl_mv/astrolib/tsc.pro b/Code/script_idl_mv/astrolib/tsc.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0ddecd7dddc86597f291463dea355e982a84ff4c
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tsc.pro
@@ -0,0 +1,595 @@
+FUNCTION tsc,value,posx,nx,posy,ny,posz,nz, $
+             AVERAGE=average,WRAPAROUND=wraparound,NO_MESSAGE=no_message, $
+             ISOLATED=isolated
+;+
+; NAME:
+;       TSC
+;
+; PURPOSE:
+;       Interpolate an irregularly sampled field using a Triangular Shaped Cloud
+;
+; EXPLANATION:
+;       This function interpolates an irregularly sampled field to a
+;       regular grid using Triangular Shaped Cloud (nearest grid point
+;       gets weight 0.75-dx^2, points before and after nearest grid
+;       points get weight 0.5*(1.5-dx)^2, where dx is the distance
+;       from the sample to the grid point in units of the cell size).
+;
+; CATEGORY:
+;       Mathematical functions, Interpolation
+;
+; CALLING SEQUENCE:
+;       Result = TSC, VALUE, POSX, NX[, POSY, NY, POSZ, NZ, 
+;                     AVERAGE = average, WRAPAROUND =  wraparound,
+;                     ISOLATED = isolated, NO_MESSAGE = no_message]
+;
+; INPUTS:
+;       VALUE: Array of sample weights (field values). For e.g. a
+;              temperature field this would be the temperature and the
+;              keyword AVERAGE should be set. For e.g. a density field
+;              this could be either the particle mass (AVERAGE should
+;              not be set) or the density (AVERAGE should be set).
+;       POSX:  Array of X coordinates of field samples, unit indices: [0,NX>.
+;       NX:    Desired number of grid points in X-direction.
+;       
+; OPTIONAL INPUTS:
+;      POSY: Array of Y coordinates of field samples, unit indices: [0,NY>.
+;      NY:   Desired number of grid points in Y-direction.
+;      POSZ: Array of Z coordinates of field samples, unit indices: [0,NZ>.
+;      NZ:   Desired number of grid points in Z-direction.
+;
+; KEYWORD PARAMETERS:
+;       AVERAGE:    Set this keyword if the nodes contain field samples
+;                   (e.g. a temperature field). The value at each grid
+;                   point will then be the weighted average of all the
+;                   samples allocated to it. If this keyword is not
+;                   set, the value at each grid point will be the
+;                   weighted sum of all the nodes allocated to it
+;                   (e.g. for a density field from a distribution of
+;                   particles). (D=0). 
+;       WRAPAROUND: Set this keyword if you want the first grid point
+;                   to contain samples of both sides of the volume
+;                   (see below).
+;       ISOLATED:   Set this keyword if the data is isolated, i.e. not
+;                   periodic. In that case total `mass' is not conserved.
+;                   This keyword cannot be used in combination with the
+;                   keyword WRAPAROUND.
+;       NO_MESSAGE: Suppress informational messages.
+;
+; Example of default allocation of nearest grid points: n0=4, *=gridpoint.
+;
+;     0   1   2   3     Index of gridpoints
+;     *   *   *   *     Grid points
+;   |---|---|---|---|   Range allocated to gridpoints ([0.0,1.0> --> 0, etc.)
+;   0   1   2   3   4   posx
+;
+; Example of ngp allocation for WRAPAROUND: n0=4, *=gridpoint.
+;
+;   0   1   2   3         Index of gridpoints
+;   *   *   *   *         Grid points
+; |---|---|---|---|--     Range allocated to gridpoints ([0.5,1.5> --> 1, etc.)
+;   0   1   2   3   4=0   posx
+;
+;
+; OUTPUTS:
+;       Prints that a TSC interpolation is being performed of x
+;       samples to y grid points, unless NO_MESSAGE is set.
+;
+; RESTRICTIONS:
+;       Field data is assumed to be periodic with the sampled volume
+;       the basic cell, unless ISOLATED is set.
+;       All input arrays must have the same dimensions.
+;       Position coordinates should be in `index units' of the
+;       desired grid: POSX=[0,NX>, etc.
+;       Keywords ISOLATED and WRAPAROUND cannot both be set.
+;
+; PROCEDURE:
+;       Nearest grid point is determined for each sample.
+;       TSC weights are computed for each sample.
+;       Samples are interpolated to the grid.
+;       Grid point values are computed (sum or average of samples).
+;
+; EXAMPLE:
+;       nx=20
+;       ny=10
+;       posx=randomu(s,1000)
+;       posy=randomu(s,1000)
+;       value=posx^2+posy^2
+;       field=tsc(value,posx*nx,nx,posy*ny,ny,/average)
+;       surface,field,/lego
+;
+; NOTES:
+;       Use csc.pro or ngp.pro for lower order interpolation schemes.    A 
+;       standard reference for these interpolation methods is:   R.W. Hockney 
+;       and J.W. Eastwood, Computer Simulations Using Particles (New York: 
+;       McGraw-Hill, 1981).
+;
+; MODIFICATION HISTORY:
+;       Written by Joop Schaye, Feb 1999.
+;       Check for overflow for large dimensions  P. Riley/W. Landsman Dec. 1999
+;-
+
+nrsamples=n_elements(value)
+nparams=n_params()
+dim=(nparams-1)/2
+
+IF dim LE 2 THEN BEGIN
+    nz=1
+    IF dim EQ 1 THEN ny=1
+ENDIF
+nxny=long(nx)*long(ny)
+
+
+;---------------------
+; Some error handling.
+;---------------------
+
+on_error,2  ; Return to caller if an error occurs.
+
+IF NOT (nparams EQ 3 OR nparams EQ 5 OR nparams EQ 7) THEN BEGIN
+    message,'Incorrect number of arguments!',/continue
+    message,'Syntax: TSC, VALUE, POSX, NX[, POSY, NY, POSZ, NZ,' + $
+      ' AVERAGE = average, WRAPAROUND =  wraparound]'
+ENDIF 
+
+IF (nrsamples NE n_elements(posx)) OR $
+  (dim GE 2 AND nrsamples NE n_elements(posy)) OR $
+  (dim EQ 3 AND nrsamples NE n_elements(posz)) THEN $
+  message,'Input arrays must have the same dimensions!'
+
+IF keyword_set(isolated) AND keyword_set(wraparound) THEN $
+  message,'Keywords ISOLATED and WRAPAROUND cannot both be set!'
+
+IF NOT keyword_set(no_message) THEN $
+  print,'Interpolating ' + strtrim(string(nrsamples,format='(i10)'),1) $
+  + ' samples to ' + strtrim(string(nxny*nz,format='(i10)'),1) + $
+  ' grid points using TSC...'
+
+
+;-----------------------
+; Calculate TSC weights.
+;-----------------------
+
+; Compute weights per axis, in order to reduce memory (everything
+; needs to be in memory if we compute all nearest grid points first).
+
+;*************
+; X-direction.
+;*************
+
+; Coordinates of nearest grid point (ngp).
+IF keyword_set(wraparound) THEN ngx=fix(posx+0.5) $
+ELSE ngx=fix(posx)+0.5
+
+; Distance from sample to ngp.
+dngx=ngx-posx
+
+; Index of ngp.
+IF keyword_set(wraparound) THEN kx2=temporary(ngx) $
+ELSE kx2=temporary(ngx)-0.5
+; Weight of ngp.
+wx2=0.75-dngx*dngx
+
+; Point before ngp.
+kx1=kx2-1  ; Index.
+dx=1.0-dngx  ; Distance to sample.
+wx1=0.5*(1.5-temporary(dx))^2  ; TSC-weight.
+
+; Point after ngp.
+kx3=kx2+1  ; Index.
+dx=1.0+temporary(dngx)  ; Distance to sample.
+wx3=0.5*(1.5-temporary(dx))^2  ; TSC-weight.
+
+; Periodic boundary conditions.
+bad=where(kx2 EQ 0,count)
+IF count NE 0 THEN BEGIN       ; Otherwise kx1=-1.
+    kx1[bad]=nx-1
+    IF keyword_set(isolated) THEN wx1[bad]=0.
+ENDIF
+bad=where(kx2 EQ nx-1,count)
+IF count NE 0 THEN BEGIN       ; Otherwise kx3=nx.
+    kx3[bad]=0
+    IF keyword_set(isolated) THEN wx3[bad]=0.
+ENDIF
+IF keyword_set(wraparound) THEN BEGIN
+    bad=where(kx2 EQ nx,count)
+    IF count NE 0 THEN BEGIN
+        kx2[bad]=0
+        kx3[bad]=1
+    ENDIF
+ENDIF
+bad=0  ; Free memory.
+
+
+;*************
+; Y-direction.
+;*************
+
+IF dim GE 2 THEN BEGIN 
+    ; Coordinates of nearest grid point (ngp).
+    IF keyword_set(wraparound) THEN ngy=fix(posy+0.5) $
+    ELSE ngy=fix(posy)+0.5
+
+    ; Distance from sample to ngp.
+    dngy=ngy-posy
+
+    ; Index of ngp.
+    IF keyword_set(wraparound) THEN ky2=temporary(ngy) $
+    ELSE ky2=temporary(ngy)-0.5
+    ; Weight of ngp.
+    wy2=0.75-dngy*dngy
+
+    ; Point before ngp.
+    ky1=ky2-1  ; Index.
+    dy=1.0-dngy  ; Distance to sample.
+    wy1=0.5*(1.5-temporary(dy))^2  ; TSC-weight.
+
+    ; Point after ngp.
+    ky3=ky2+1  ; Index.
+    dy=1.0+temporary(dngy)  ; Distance to sample.
+    wy3=0.5*(1.5-temporary(dy))^2  ; TSC-weight.
+
+    ; Periodic boundary conditions.
+    bad=where(ky2 EQ 0,count)
+    IF count NE 0 THEN BEGIN       ; Otherwise ky1=-1.
+        ky1[bad]=ny-1
+        IF keyword_set(isolated) THEN wy1[bad]=0.
+    ENDIF
+    bad=where(ky2 EQ ny-1,count)
+    IF count NE 0 THEN BEGIN       ; Otherwise ky3=ny.
+        ky3[bad]=0
+        IF keyword_set(isolated) THEN wy3[bad]=0.
+    ENDIF
+    IF keyword_set(wraparound) THEN BEGIN
+        bad=where(ky2 EQ ny,count)
+        IF count NE 0 THEN BEGIN
+            ky2[bad]=0
+            ky3[bad]=1
+        ENDIF
+    ENDIF
+    bad=0  ; Free memory.
+ENDIF ELSE BEGIN
+    ky1=0
+    ky2=0
+    wy1=1
+    wy2=1
+ENDELSE
+
+
+;*************
+; Z-direction.
+;*************
+
+IF dim EQ 3 THEN BEGIN
+    ; Coordinates of nearest grid point (ngp).
+    IF keyword_set(wraparound) THEN ngz=fix(posz+0.5) $
+    ELSE ngz=fix(posz)+0.5
+
+    ; Distance from sample to ngp.
+    dngz=ngz-posz
+
+    ; Index of ngp.
+    IF keyword_set(wraparound) THEN kz2=temporary(ngz) $
+    ELSE kz2=temporary(ngz)-0.5
+    ; Weight of ngp.
+    wz2=0.75-dngz*dngz
+
+    ; Point before ngp.
+    kz1=kz2-1  ; Index.
+    dz=1.0-dngz  ; Distance to sample.
+    wz1=0.5*(1.5-temporary(dz))^2  ; TSC-weight.
+
+    ; Point after ngp.
+    kz3=kz2+1  ; Index.
+    dz=1.0+temporary(dngz)  ; Distance to sample.
+    wz3=0.5*(1.5-temporary(dz))^2  ; TSC-weight.
+
+    ; Periodic boundary conditions.
+    bad=where(kz2 EQ 0,count)
+    IF count NE 0 THEN BEGIN       ; Otherwise kz1=-1.
+        kz1[bad]=nz-1
+        IF keyword_set(isolated) THEN wz1[bad]=0.
+    ENDIF
+    bad=where(kz2 EQ nz-1,count)
+    IF count NE 0 THEN BEGIN       ; Otherwise kz3=nz.
+        kz3[bad]=0
+        IF keyword_set(isolated) THEN wz3[bad]=0.
+    ENDIF
+    IF keyword_set(wraparound) THEN BEGIN
+        bad=where(kz2 EQ nz,count)
+        IF count NE 0 THEN BEGIN
+            kz2[bad]=0
+            kz3[bad]=1
+        ENDIF
+    ENDIF
+    bad=0  ; Free memory.
+ENDIF ELSE BEGIN
+    kz1=0
+    kz2=0
+    wz1=1
+    wz2=1
+ENDELSE
+    
+
+;-----------------------------
+; Interpolate samples to grid.
+;-----------------------------
+
+field=fltarr(nx,ny,nz)
+IF keyword_set(average) THEN tottscweight=fltarr(nx,ny,nz)
+
+; tscweight adds up all tsc weights allocated to a grid point, we need
+; to keep track of this in order to compute the temperature.
+; Note that total(tscweight) is equal to nrsamples and that
+; total(ifield)=n0^3 if sph.plot NE 'sph,temp' (not 1 because we use
+; xpos=posx*n0 --> cube length different from EDFW paper).
+
+index=kx1+ky1*nx+kz1*nxny
+tscweight=wx1*wy1*wz1
+IF keyword_set(average) THEN BEGIN
+    FOR j=0l,nrsamples-1l DO BEGIN
+        field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+    ENDFOR
+ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+  field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+index=kx2+ky1*nx+kz1*nxny
+tscweight=wx2*wy1*wz1
+IF keyword_set(average) THEN BEGIN
+    FOR j=0l,nrsamples-1l DO BEGIN
+        field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+    ENDFOR
+ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+  field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+index=kx3+ky1*nx+kz1*nxny
+tscweight=wx3*wy1*wz1
+IF keyword_set(average) THEN BEGIN
+    FOR j=0l,nrsamples-1l DO BEGIN
+        field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+    ENDFOR
+ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+  field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+
+IF dim GE 2 THEN BEGIN
+    index=kx1+ky2*nx+kz1*nxny
+    tscweight=wx1*wy2*wz1
+    IF keyword_set(average) THEN BEGIN
+        FOR j=0l,nrsamples-1l DO BEGIN
+            field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+            tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+        ENDFOR
+    ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+      field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+    index=kx2+ky2*nx+kz1*nxny
+    tscweight=wx2*wy2*wz1
+    IF keyword_set(average) THEN BEGIN
+        FOR j=0l,nrsamples-1l DO BEGIN
+            field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+            tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+        ENDFOR
+    ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+      field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+    index=kx3+ky2*nx+kz1*nxny
+    tscweight=wx3*wy2*wz1
+    IF keyword_set(average) THEN BEGIN
+        FOR j=0l,nrsamples-1l DO BEGIN
+            field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+            tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+        ENDFOR
+    ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+      field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+    index=kx1+ky3*nx+kz1*nxny
+    tscweight=wx1*wy3*wz1
+    IF keyword_set(average) THEN BEGIN
+        FOR j=0l,nrsamples-1l DO BEGIN
+            field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+            tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+        ENDFOR
+    ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+      field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+    index=kx2+ky3*nx+kz1*nxny
+    tscweight=wx2*wy3*wz1
+    IF keyword_set(average) THEN BEGIN
+        FOR j=0l,nrsamples-1l DO BEGIN
+            field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+            tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+        ENDFOR
+    ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+      field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+    index=kx3+ky3*nx+kz1*nxny
+    tscweight=wx3*wy3*wz1
+    IF keyword_set(average) THEN BEGIN
+        FOR j=0l,nrsamples-1l DO BEGIN
+            field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+            tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+        ENDFOR
+    ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+      field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+
+    IF dim EQ 3 THEN BEGIN
+        index=kx1+ky1*nx+kz2*nxny
+        tscweight=wx1*wy1*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx2+ky1*nx+kz2*nxny
+        tscweight=wx2*wy1*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx3+ky1*nx+kz2*nxny
+        tscweight=wx3*wy1*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx1+ky2*nx+kz2*nxny
+        tscweight=wx1*wy2*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx2+ky2*nx+kz2*nxny
+        tscweight=wx2*wy2*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx3+ky2*nx+kz2*nxny
+        tscweight=wx3*wy2*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx1+ky3*nx+kz2*nxny
+        tscweight=wx1*wy3*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx2+ky3*nx+kz2*nxny
+        tscweight=wx2*wy3*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx3+ky3*nx+kz2*nxny
+        tscweight=wx3*wy3*wz2
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx1+ky1*nx+kz3*nxny
+        tscweight=wx1*wy1*wz3
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx2+ky1*nx+kz3*nxny
+        tscweight=wx2*wy1*wz3
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx3+ky1*nx+kz3*nxny
+        tscweight=wx3*wy1*wz3
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx1+ky2*nx+kz3*nxny
+        tscweight=wx1*wy2*wz3
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx2+ky2*nx+kz3*nxny
+        tscweight=wx2*wy2*wz3
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx3+ky2*nx+kz3*nxny
+        tscweight=wx3*wy2*wz3
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx1+ky3*nx+kz3*nxny
+        tscweight=wx1*wy3*wz3
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx2+ky3*nx+kz3*nxny
+        tscweight=wx2*wy3*wz3
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+        index=kx3+ky3*nx+kz3*nxny
+        tscweight=wx3*wy3*wz3
+        IF keyword_set(average) THEN BEGIN
+            FOR j=0l,nrsamples-1l DO BEGIN
+                field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+                tottscweight[index[j]]=tottscweight[index[j]]+tscweight[j]
+            ENDFOR
+        ENDIF ELSE FOR j=0l,nrsamples-1l DO $
+          field[index[j]]=field[index[j]]+tscweight[j]*value[j]
+    ENDIF
+
+ENDIF
+
+; Free memory (no need to free any more local arrays, will not lower
+; maximum memory usage).
+index=0
+weight=0
+
+
+;--------------------------
+; Compute weighted average.
+;--------------------------
+
+IF keyword_set(average) THEN BEGIN
+    good=where(tottscweight NE 0,nrgood)
+    field[good]=temporary(field[good])/temporary(tottscweight[good])
+ENDIF
+
+return,field
+
+END  ; End of procedure tsc.
diff --git a/Code/script_idl_mv/astrolib/tsum.pro b/Code/script_idl_mv/astrolib/tsum.pro
new file mode 100644
index 0000000000000000000000000000000000000000..00a87450dd479d8518d94965a38bb12c0204edfc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tsum.pro
@@ -0,0 +1,100 @@
+FUNCTION TSUM,X,Y,IMIN,IMAX, NAN=NAN              ;Trapezoidal summation
+;+
+; NAME:
+;       TSUM
+; PURPOSE:
+;       Trapezoidal summation of the area under a curve. 
+; EXPLANATION:
+;       Adapted from the procedure INTEG in the IUE procedure library.  
+;
+; CALLING SEQUENCE:
+;       Result = TSUM(y)
+;              or
+;       Result = TSUM( x, y, [ imin, imax, /nan ] )  
+; INPUTS:
+;       x = array containing monotonic independent variable.  If omitted, then
+;               x is assumed to contain the index of the y variable.
+;               x = lindgen( N_elements(y) ).
+;       y = array containing dependent variable y = f(x)
+;
+; OPTIONAL INPUTS:
+;       imin = scalar index of x array at which to begin the integration
+;               If omitted, then summation starts at x[0].
+;       imax = scalar index of x value at which to end the integration 
+;               If omitted then the integration ends at x[npts-1].
+;       nan: If set cause the routine to check for occurrences of the IEEE 
+;                 floating-point values NaN or Infinity in the input data. 
+;                 Elements with the value NaN or Infinity are treated as missing data
+;
+; OUTPUTS:
+;       result = area under the curve y=f(x) between x[imin] and x[imax].
+;
+; EXAMPLE:
+;       IDL> x = [0.0,0.1,0.14,0.3] 
+;       IDL> y = sin(x)
+;       IDL> print,tsum(x,y)    ===>  0.0445843
+;       
+;       In this example, the exact curve can be computed analytically as 
+;       1.0 - cos(0.3) = 0.0446635     
+; PROCEDURE:
+;       The area is determined of individual trapezoids defined by x[i],
+;       x[i+1], y[i] and y[i+1].
+;
+;       If the data is known to be at all smooth, then a more accurate
+;       integration can be found by interpolation prior to the trapezoidal
+;       sums, for example, by the standard IDL User Library int_tabulated.pro.
+; MODIFICATION HISTORY:
+;       Written, W.B. Landsman, STI Corp. May 1986
+;       Modified so X is not altered in a one parameter call Jan 1990
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Allow non-integer values of imin and imax  W. Landsman April 2001
+;       Fix problem if only 1 parameter supplied W. Landsman June 2002
+;       Added /nan keyword. Julio Castro/WL May 2014
+;-
+; Set default parameters
+ On_error,2
+ npar = N_params()
+   
+ if npar EQ 1 then begin
+    npts = N_elements(x)
+    yy = x
+    xx = lindgen(npts)
+    ilo = 0   & imin = ilo
+    ihi = npts-1 & imax = ihi
+ endif else begin
+
+   if ( npar LT 3 ) then imin = 0
+   npts = min( [N_elements(x), N_elements(y)] )
+   if ( npar LT 4 ) then imax = npts-1
+   ilo = long(imin)
+   ihi = long(imax)
+   xx = x[ilo:ihi]
+   yy = y[ilo:ihi]
+   npts = ihi - ilo + 1
+ endelse   
+; 
+;  Remove NaN values
+;
+   if keyword_set(NaN) then begin 
+   g = where(finite(yy),npts)
+   yy = yy[g]
+   xx = xx[g]
+  endif          
+;   
+; Compute areas of trapezoids and sum result
+;
+  xdif = xx[1:*] - xx
+  yavg =  ( yy[0:npts-2] + yy[1:npts-1] ) / 2.  
+  sum = total( xdif*yavg ) 
+
+; Now account for edge effects if IMIN or IMAX parameter are not integers
+
+  hi = imax - ihi
+  lo = imin - ilo
+  if (ihi LT imax) then sum +=  (x[ihi+1]-x[ihi])*hi* $
+              (y[ihi] + (hi/2.) *(y[ihi+1] - y[ihi]) )
+  if (ilo LT imin) then sum -=  (x[ilo+1]-x[ilo])*lo* $
+              (y[ilo] + (lo/2.) *(y[ilo+1] - y[ilo]) )
+  return, sum
+
+  end     
diff --git a/Code/script_idl_mv/astrolib/tvbox.pro b/Code/script_idl_mv/astrolib/tvbox.pro
new file mode 100644
index 0000000000000000000000000000000000000000..58f13a46a5fe46e7e1193ddc455b6167561b3db8
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tvbox.pro
@@ -0,0 +1,191 @@
+pro tvbox,width,x,y,color,DATA = data,Color=TheColor, ANGLE = angle, $
+                 DEVICE=device, SQUARE=SQUARE,  _EXTRA = _EXTRA
+;+
+; NAME:
+;      TVBOX
+; PURPOSE:
+;      Draw a box(es) or rectangle(s) of specified width
+; EXPLANATION: 
+;      Positions can be specified either by the cursor position or by 
+;      supplying a vector of X,Y positions.  By default, TVBOX now
+;     (since Jan 2012) assumes data coordinates if !X.crange is set. 
+;
+; CALLING SEQUENCE:
+;      TVBOX, width, [ x, y, color, /DATA, ANGLE= ,COLOR =, _EXTRA =  ]
+;
+; INPUTS:
+;      WIDTH -  either a scalar giving the width of a box, or a 2 element
+;               vector giving the length and width of a rectangle.
+;
+; OPTIONAL INPUTS:           
+;      X  -  x position for box center, scalar or vector
+;      Y  -  y position for box center, scalar or vector.   If vector, then Y
+;            must have the same number of elements as X
+;            Positions are specified in device coordinates unless /DATA is set
+;            If X and Y are not specified, and device has a cursor, then 
+;            TVBOX will draw a box at current cursor position
+;      COLOR - String or integer specifying the color  to draw the box(es)
+;            If COLORS is a scalar then all boxes are drawn with the same
+;            color value.   Otherwise, the Nth box is drawn with the
+;            Nth value of color.    Color can also be specified as 
+;            string (e.g.'red').   See cgCOLOR for a list of available
+;            color names.     Default = "opposite".    
+; OUTPUTS:
+;      None
+;
+; OPTIONAL KEYWORD INPUTS:
+;      ANGLE - numeric scalar specifying the clockwise rotation of
+;              the boxes or rectangles.
+;      COLOR - Scalar or vector, overrides the COLOR input parameter
+;              Color can be specified as a string (e.g. 'red') or intensity 
+;              value. See cgCOLOR() for a list of color names.       
+;               Default = 'opposite' (i.e. color opposite the background).   
+;      /DATA - if this keyword is set and non-zero, then the box width and
+;              X,Y position center are interpreted as being in DATA 
+;              coordinates.   Note that data coordinates must be previously
+;              defined (with a PLOT or CONTOUR call).   The default
+;              is to assume data coordinates if !X.CRANGE is set.    Force
+;              device coordinates by setting DATA = 0 or /DEVICE
+;      /DEVICE Set this keyword to force use of device coordinates
+;      /FILL  - If set, fill the box using cgCOLORFILL
+;      /SQUARE - If set, then a square is drawn, even if in data coordinates
+;               with unequal X and Y axes.   The X width is used for the
+;               square width, and the Y width is ignored.
+;
+;      Any keyword recognized by cgPLOTS (or cgCOLORFILL if /FILL is set) 
+;      is also recognized by TVBOX.   
+;      In particular, the linestyle, thickness and clipping of the boxes
+;      is controlled by the  LINESTYLE, THICK and NOCLIP keywords.
+;      (Clipping is turned off by default, set NOCLIP=0 to activate it.)
+;      If /FILL is set then available keywords include LINE_FILL and 
+;      FILL_PATTERN. 
+;
+; SIDE EFFECTS:
+;       A square or rectangle will be drawn on the device
+;       For best results WIDTH should be odd when using the default DEVICE
+;       coordinates.  (If WIDTH is even, the actual size of the box will be 
+;       WIDTH + 1, so that box remains centered.)
+;
+; EXAMPLES:
+;       (1) Draw a double thick box of width 13, centered at 221,256 in the
+;       currently active window
+;
+;           IDL> tvbox, 13, 221, 256, thick=2
+;
+;       (2) Overlay a "slit" with dimension 52" x 2" on a previously displayed
+;           image at a position angle (East of North) of 32 degrees.    The 
+;           slit is to be centered at XC, YC and the plate scale 
+;           arcsec_per_pixel is known.
+;
+;           IDL> w = [2.,52.]/arcsec_per_pixel ;Convert slit size to pixel units
+;           IDL> tvbox,w,XC,YC,ang=-32          ;Draw slit
+; RESTRICTIONS:
+;         Allows use of only device (default) or data (if /DATA is set) 
+;           coordinates.   Normalized coordinates are not allowed
+; PROCEDURES USED:
+;       cgpolygon, zparcheck
+; REVISON HISTORY:
+;       Written, W. Landsman   STX Co.           10-6-87
+;       Modified to take vector arguments. Greg Hennessy Mar 1991
+;       Fixed centering of odd width    W. Landsman    Sep. 1991
+;       Let the user specify COLOR=0, accept vector color, W. Landsman Nov. 1995
+;       Fixed typo in _EXTRA keyword  W. Landsman   August 1997
+;       Added ANGLE keyword    W.Landsman     February 2000 
+;       Make sure ANGLE is a scalar   W. Landsman  September 2001
+;       Don't round coordinates if /DATA is set.   M. Perrin  August 2005
+;       Use STRICT_EXTRA to flag valid keywords W. Landsman Sep 2005
+;       Check that width has only 1 or 2 elements W. Landsman August 2010
+;       Use Coyote Graphcis  W. Landsman February 2011
+;       Added /FILL keyword  W. Landsman  July 2011
+;       Default to data coordinates if !X.crange present  WL Jan 2012
+;       Added Square keyword  WL.  April 2012
+;       
+;-
+ compile_opt idl2
+ On_error,2
+
+ npar = N_params()                         ;Get number of parameters
+
+ if ( npar LT 1 ) then begin
+     print,'Syntax - TVBOX, width,[ x, y, color, THICK= ,/DATA, ANGLE=, COLOR=]'
+     return
+ endif
+
+ zparcheck, 'TVBOX', width, 1, [1,2,3,4,5], [0,1], 'Box Width'
+
+ if N_elements(width) GT 2 then message, $
+     'ERROR - First parameter (box width) must have 1 or 2 values' 
+ if ( N_elements(width) EQ 2 ) then w = width/2. else w = [width,width]/2.
+
+; Use data coordinates if !X.crange is set (previous plot) and /DEVICE not set 
+
+; Default to data coordinates if !X.crange is set (previous plot) 
+   if keyword_set(device) then datacoord = 0 else begin
+      if N_elements(data) eq 0 then datacoord = !x.crange[0] NE !x.crange[1]  $
+                          else datacoord = logical_true(data)
+   endelse   			  
+ 
+
+; Can't figure out in IDL how to figure out if the device has a cursor so
+; we'll just check for a postscript device
+
+ if ( npar LT 3 ) then if  (!D.NAME NE 'PS') then begin 
+    cursor,x,y,/DEVICE,/NOWAIT          ;Read X,Y from the window
+    if (x LT 0) or (y LT 0) then begin
+       message,'Position cursor in window ' + strtrim(!D.WINDOW,2) + $
+              ' -- then hit mouse button',/INF
+       cursor,x,y,/DEVICE,/WAIT
+       message, 'Box is centered at (' + strtrim(x,2) + ',' + $
+                 strtrim(y,2) + ')',/INF
+    endif
+ endif else message, $
+     'ERROR - X,Y position must be specified for Postscript device'
+
+ if N_elements(TheColor) EQ 0 then begin 
+     if N_elements(color) EQ 0 then color = cgcolor('opposite')
+ endif else color = TheColor    
+ nbox = N_elements(x)                      ;Number of boxes to draw
+ if ( nbox NE N_elements(Y) ) then $
+       message,'ERROR - X and Y positions must have same number of elements'
+
+ xs = x & ys = y
+ 
+ Ncol = N_elements(color)
+ xbox = [1,1,-1,-1,1]*w[0]
+ ybox = [-1,1,1,-1,-1]*w[1]
+ if keyword_set(angle) then begin           ;Non-zero rotation angle?
+       ang = angle[0]/!RADEG
+       xprime =  xbox*cos(ang) + ybox*sin(ang)
+       yprime = -xbox*sin(ang) + ybox*cos(ang)
+       xbox = xprime
+       ybox = yprime
+ endif
+ 
+ if keyword_set(square) && datacoord then begin
+ ; Get ratio of unit vectors in X and Y direction
+   t = convert_coord([0,w[0],0],[0,0,w[0]],/data,/to_device)
+   ratio = (t[0,1]-t[0,0])/(t[1,2]-t[1,0])
+    ybox = ybox*ratio
+ endif
+      
+ for i = 0l, nbox-1 do begin
+
+  j = i < (Ncol-1)
+  xt = xs[i] + xbox      ;X edges of rectangle
+  yt = ys[i] + ybox     ;Y edges of rectangle
+   
+; Plot the box in data or device coordinates.   Default for Coyote graphcis
+; is data coordinates. 
+
+  if datacoord then $
+     cgpolygon, xt, yt, color= color[j], _STRICT_EXTRA = _EXTRA $
+   else begin    
+ ; only round coordinates to integers if using device coords;
+ ; data coords can potentially be fractional.
+     xt = round(xt) & yt = round(yt)
+     cgpolygon,xt,yt,/DEVICE,color=color[j],_STRICT_EXTRA=_EXTRA  
+   endelse
+ endfor
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/tvcircle.pro b/Code/script_idl_mv/astrolib/tvcircle.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4693e0742f5e8470d24017710e4e2d2a71a331ae
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tvcircle.pro
@@ -0,0 +1,228 @@
+Pro Tvcircle, radius, xc, yc, color, COLOR = TheColor, Device=device, $
+               DATA= data, FILL=fill,_Extra = _extra
+;+
+; NAME:
+;     TVCIRCLE
+; PURPOSE:
+;     Draw circle(s) of specified radius at specified position(s) 
+; EXPLANATION: 
+;     If a position is not specified, and device has a cursor, then a circle
+;     is drawn at the current cursor position.    By default, TVCIRCLE now
+;     (since Jan 2012) assumes data coordinates if !X.crange is set.
+;
+; CALLING SEQUENCE:
+;     TVCIRCLE, rad, x, y, color, [ /DATA, /FILL, _EXTRA  =  ]         
+;
+; INPUTS:
+;     RAD - radius of circle(s) to be drawn, positive numeric scalar
+;
+; OPTIONAL INPUT:
+;      X - x position for circle center, vector or scalar
+;      Y - y position for circle center, vector or scalar
+;               If X and Y are not specified, and the device has a cursor, 
+;               then program will draw a circle at the current cursor position
+;      COLOR -  color name or intensity value(s) (0 - !D.N_COLORS) used to draw
+;               the circle(s).   If COLOR is a scalar then all circles are drawn
+;               with the same color value.   Otherwise, the Nth circle is drawn
+;               with the  Nth value of color.  See cgCOLOR() for a list of color
+;               names.  Default = 'opposite' (i.e. color opposite the 
+;               background).   
+;
+; OPTIONAL KEYWORD INPUTS:
+;       /DATA - if this keyword is set and non-zero, then the circle width and
+;              X,Y position center are interpreted as being in DATA 
+;              coordinates.   Note that data coordinates must be previously
+;              defined (with a PLOT or CONTOUR call).    TVCIRCLE will
+;              internally convert to device coordinates before drawing the
+;              circle, in order to maintain optimal smoothness.    The default
+;              is to assume data coordinates if !X.CRANGE is set.    Force
+;              device coordinates by setting DATA = 0 or /DEVICE
+;       /DEVICE - If set, then force use of device coordinates..
+;       /FILL  - If set, fill the circle using cgCOLORFILL
+;
+;               Any keyword recognized by cgPLOTS (or cgCOLORFILL if /FILL is 
+;               set) is also recognized by TVCIRCLE.   In particular, the color,
+;               linestyle, thickness and clipping of the circles are controlled
+;               by the  COLOR, LINESTYLE, THICK and NOCLIP keywords.  (Clipping
+;               is turned off by default, set NOCLIP=0 to activate it.)
+;               If /FILL is set then available keywords are LINE_FILL and 
+;               FILL_PATTERN. 
+; OUTPUTS:
+;       None
+;
+; RESTRICTIONS:
+;       (1) Some round-off error may occur when non-integral values are 
+;           supplied for both the radius and the center coordinates
+;       (2) TVCIRCLE does not accept /NORMAL coordinates.
+;       (3) TVCIRCLE always draws a circle --- even when in data coordinates 
+;           and the X and Y data scales are unequal.    (The X data scale is 
+;           used to define the circle radius.)     If this is not the behaviour
+;           you want, then use TVELLIPSE instead.
+; EXAMPLE:
+;       (1) Draw circles of radius 9 pixels at the positions specified by 
+;           X,Y vectors, using double thickness lines
+;
+;           IDL> tvcircle, 9, x, y, THICK = 2
+;
+;           Now fill in the circles using the LINE_FILL method
+;
+;           IDL> tvcircle, 9, x, y, /FILL, /LINE_FILL
+; METHOD:
+;           The method used is that of Michener's, modified to take into account
+;           the fact that IDL plots arrays faster than single points.   See
+;           "Fundamental of Interactive Computer Graphics" by Foley and Van Dam"
+;           p. 445 for the algorithm.
+;
+; REVISON HISTORY:
+;           Original version   written by B. Pfarr  STX   10-88 
+;           Major rewrite adapted from CIRCLE by Allyn Saroyan   LNLL
+;           Wayne Landsman   STX     Sep. 91
+;           Added DATA keyword   Wayne Landsman  HSTX    June 1993
+;           Added FILL keyword.  R. S. Hill, HSTX, 4-Nov-1993
+;           Always convert to device coords, add _EXTRA keyword, allow vector
+;           colors.   Wayne Landsman, HSTX,  May 1995
+;           Allow one to set COLOR = 0,   W. Landsman, HSTX, November 1995
+;           Check if data axes reversed.  P. Mangifico, W. Landsman  May 1996
+;           Use strict_extra to check input keywords W. Landsman  July 2005
+;           Update documentation to note NOCLIP=0 option W.L.  Oct. 2006
+;           Make all integers default to LONG  W. Landsman  Dec 2006
+;           Use Coyote Graphics procedures W. Landsman Feb 2011
+;           Default to data coordinates if !X.crange present  WL Jan 2012
+;           Add /DEVICE coords, fix Jan 2012 update.   Mar 2012
+;-
+
+   On_Error, 2   ; Return to caller
+   compile_opt idl2
+
+   if ( N_params() LT 1) then begin
+       print, 'Syntax - TVCIRCLE, rad, [ xc, yc, color, /DATA, /FILL, _EXTRA= ]'
+       return
+   endif
+
+; Default to data coordinates if !X.crange is set (previous plot) 
+   if keyword_set(device) then datacoord = 0 else begin
+      if N_elements(data) eq 0 then datacoord = !x.crange[0] NE !x.crange[1]  $
+                          else datacoord = logical_true(data)
+   endelse   			  
+		  
+   if N_elements(radius) NE 1 then message, $
+          'ERROR - Circle radius (first parameter) must be a scalar'
+
+   if N_elements(TheColor) EQ 0 then begin
+      IF N_Elements( Color ) EQ 0 THEN Color = cgcolor('opposite')
+   endif else color = TheColor
+
+
+  if N_params() LT 3 then begin
+        if (!D.WINDOW EQ -1) then message, $
+                'ERROR - Cursor not available for device ' + !D.NAME
+        cursor, xc, yc, /DEVICE, /NOWAIT
+        if (xc LT 0) || (yc LT 0) then begin
+        message,'Position cursor in window ' + strtrim(!D.WINDOW,2) + $
+                ' -- then hit mouse button',/INF
+        cursor, xc, yc, /DEVICE, /WAIT
+        message,'Circle is centered at (' + strtrim(xc,2) + ',' + $
+                strtrim(yc,2) + ')',/INF
+  endif
+
+  endif 
+
+    N_circle = min( [ N_elements(xc), N_elements(yc) ] )
+
+
+    if datacoord then begin 
+                coord = abs(convert_coord(radius,0,/data,/to_dev) - $
+                        convert_coord(0,0,/data,/to_dev)) 
+                irad =  round( coord[0] )
+    endif else $
+               irad = round(radius)	             
+
+   x = 0
+   y = irad 
+   d = 3 - 2 * irad
+
+
+   ; Find the x and y coordinates for one eighth of a circle.
+   ; The maximum number of these coordinates is the radius of the circle.
+
+   xHalfQuad = Make_Array( irad + 1, /Long, /NoZero )
+   yHalfQuad = xHalfQuad
+
+   path = 0
+
+   WHILE x lt y $
+   DO BEGIN
+
+      xHalfQuad[path] = x
+      yHalfQuad[path] = y
+
+      path++
+
+      IF d lt 0 $
+      THEN d += 4*x + 6 $
+      ELSE BEGIN
+
+           d +=  4*(x-y) + 10
+           y--
+
+           END
+
+      x++
+
+      END
+
+   IF x eq y $
+   THEN BEGIN ; Fill in last point
+
+        xHalfQuad[path] = x
+        yHalfQuad[path] = y
+
+        path++
+
+        END ; Filling in last point
+
+   ; Shrink the arrays to their correct size
+
+   xHalfQuad = xHalfQuad[ 0:path-1 ]
+   yHalfQuad = yHalfQuad[ 0:path-1 ]
+
+   ; Convert the eighth circle into a quadrant
+
+   xQuad = [ xHalfQuad, Rotate(yHalfQuad, 5) ]
+   yQuad = [ yHalfQuad, Rotate(xHalfQuad, 5) ]
+
+   ; Prepare for converting the quadrants into a full circle
+
+   xQuadRev = Rotate( xQuad[0:2*path-2], 5 )
+   yQuadRev = Rotate( yQuad[0:2*path-2], 5 )
+
+   ; Create full-circle coordinates
+
+   x = [ xQuad, xQuadRev, -xQuad[1:*], -xQuadRev ]
+   y = [ yQuad, -yQuadRev, -yQuad[1:*], yQuadRev ]
+
+   ; Plot the coordinates about the given center
+   
+   if datacoord then begin        ;Convert to device coordinates
+        coord = convert_coord( xc, yc, /DATA, /TO_DEVICE)
+        xcen = round(coord[0,*]) & ycen = round(coord[1,*])
+   endif else begin
+        xcen = round(xc) & ycen = round(yc)
+   endelse
+
+
+   Ncolor1 = N_elements(color) -1
+   for i = 0l, N_circle-1 do begin
+      j = i < Ncolor1
+      if keyword_set(fill) then begin
+            cgcolorfill, x+xcen[i],  y + ycen[i], COLOR=color[j], /DEV, $
+            _STRICT_Extra = _extra
+      endif else begin
+            cgPlotS, x + xcen[i], y+ ycen[i], COLOR = Color[j], /DEV, $
+            _STRICT_Extra = _extra
+      endelse
+
+   endfor
+
+   Return
+   End; TVcircle
diff --git a/Code/script_idl_mv/astrolib/tvellipse.pro b/Code/script_idl_mv/astrolib/tvellipse.pro
new file mode 100644
index 0000000000000000000000000000000000000000..6f98274f3979f9e47d43ad12eafde2951a87b7e2
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tvellipse.pro
@@ -0,0 +1,184 @@
+pro tvellipse, rmax, rmin, xc, yc, pos_ang, color, DATA = data, $
+	NPOINTS = npoints, COLOR=thecolor, MAJOR=major, MINOR=minor, $
+        DEVICE= device, FILL = fill, _Extra = _extra
+;+
+; NAME:
+;      TVELLIPSE
+;
+; PURPOSE:
+;      Draw an ellipse on the current graphics device.
+;
+; CALLING SEQUENCE:
+;      TVELLIPSE, rmax, rmin, xc, yc, [ pos_ang, color, COLOR= ,/DATA, NPOINTS=
+;                                        LINESTYLE=, THICK=, /MAJOR, /MINOR ]
+; INPUTS:
+;       RMAX,RMIN - Scalars giving the semi-major and semi-minor axes of
+;                   the ellipse
+; OPTIONAL INPUTS:
+;       XC,YC - Scalars giving the position on the TV of the ellipse center
+;               If not supplied (or if XC, YC are negative and /DATA is not 
+;               set), and an interactive graphics device (e.g. not postscript)
+;               is set,  then the user will be prompted for X,Y
+;       POS_ANG - Position angle of the major axis, measured counter-clockwise
+;                 from the X axis.  Default is 0.
+;       COLOR - Scalar  integer or string specifying color to draw ellipse.   
+;               See cgcolor.pro for a list of possible color names
+
+; OPTIONAL KEYWORD INPUT:
+;        COLOR - Intensity value or color name used to draw the circle, 
+;                overrides parameter value.  Default = 'opposite'
+;                See cgCOLOR() for a list of color names.;        
+;       /DATA - if this keyword is set and non-zero, then the ellipse radii and
+;               X,Y position center are interpreted as being in DATA
+;               coordinates.   Note that the data coordinates must have been
+;               previously defined (with a PLOT or CONTOUR call).  The default
+;              is to assume data coordinates if !X.CRANGE has been set by a 
+;              previous plot.    Force device coordinates by setting DATA = 0.
+;        /DEVICE - Set to force use of device coordinates.
+;        /FILL - If set, then fill the ellipse using cgCOLORFILL
+;        NPOINTS - Number of points to connect to draw ellipse, default = 120
+;                  Increase this value to improve smoothness
+;        /MAJOR - Plot a line along the ellipse's major axis
+;        /MINOR - Plot a line along the ellipse's minor axis
+;
+;               Any keyword recognized by cgPLOTS is also recognized by TVELLIPSE.
+;               In particular, the color, linestyle, thickness and clipping of
+;               the ellipses are controlled by the  COLOR, LINESTYLE, THICK and
+;               NOCLIP keywords.  (Clipping is turned off by default, set
+;               NOCLIP=0 to activate it.)  If /FILL is set then available 
+;               keywords include LINE_FILL and FILL_PATTERN. 
+;
+; RESTRICTIONS:
+;        TVELLIPSE does not check whether the ellipse is within the boundaries
+;        of the window.
+;
+;        The ellipse is evaluated at NPOINTS (default = 120) points and
+;        connected by straight lines, rather than using the more sophisticated
+;        algorithm used by TVCIRCLE
+;
+;        TVELLIPSE does not accept normalized coordinates.
+;
+;        TVELLIPSE is not vectorized; it only draws one ellipse at a time
+;
+; EXAMPLE:
+;        Draw an ellipse of semi-major axis 50 pixels, minor axis 30
+;        pixels, centered on (250,100), with the major axis inclined 25
+;        degrees counter-clockwise from the X axis.  Use a double thickness
+;        line and device coordinates
+;
+;	IDL> tvellipse,50,30,250,100,25,thick=2,/device
+;
+; NOTES:
+;        Note that the position angle for TVELLIPSE (counter-clockwise from
+;        the X axis) differs from the astronomical position angle
+;        (counter-clockwise from the Y axis).
+;
+; REVISION HISTORY:
+;        Written  W. Landsman STX          July, 1989
+;        Converted to use with a workstation.  M. Greason, STX, June 1990
+;        LINESTYLE keyword, evaluate at 120 points,  W. Landsman HSTX Nov 1995
+;        Added NPOINTS keyword, fixed /DATA keyword W. Landsman HSTX Jan 1996
+;        Check for reversed /DATA coordinates  P. Mangiafico, W.Landsman May 1996
+;        Work correctly when X & Y data scales are unequal  December 1998
+;        Removed cursor input when -ve coords are entered with /data
+;        keyword set  P. Maxted, Keele, 2002
+;        Use _EXTRA keywords including NOCLIP  W. Landsman October 2006
+;        Add plotting of major and minor axes and /MAJOR, /MINOR keywords;
+;        fixed description of RMAX,RMIN (semi-axes).  J. Guerber Feb. 2007
+;        Update to use Coyote graphics W. Landsman Feb 2011
+;        Default to data coordinates if a previous plot has been made 
+;        (X.crange is non-zero)  W. Landsman Jan 2012
+;        Added /DEVICE keyword W. Landsman   Mar 2012
+;        Added /FILL keyword  W. Landsman Mar 2012
+;-
+ On_error,2                              ;Return to caller
+
+ if N_params() lt 2 then begin
+   print,'Syntax - TVELLIPSE, rmax, rmin, xc, yc, [pos_ang, color, COLOR=,'
+   print,'          /FILL, NPOINTS=, LINESTYLE=, THICK=, /DATA, /MAJOR, /MINOR]'
+   print,'          /DEVICE...any other keyword accepted by cgPLOTS'
+   return
+ endif
+ 
+ ; Default to data coordinates if !X.crange is set (previous plot) 
+
+  if keyword_set(device) then datacoord = 0 else begin
+  if N_elements(data) Eq 0  then datacoord = !x.crange[0] NE !x.crange[1]  $
+                         else datacoord = logical_true(data)
+  endelse			 
+
+ if N_params() lt 4 then $
+       cursor, xc, yc, /DEVICE, /NOWAIT      ;Get unroamed,unzoomed coordinates
+
+ if ( (xc LT 0) || (yc LT 0)) && ~keyword_set(data) then begin
+       message,'Position cursor in window ' + strtrim(!D.WINDOW,2) + $
+              ' -- then hit mouse button',/INF
+       cursor, xc, yc, /DEVICE, /WAIT
+         message,'Ellipse is centered at (' + strtrim(xc,2) + ',' + $
+		strtrim(yc,2) + ')',/INF
+ endif
+
+ if N_params() LT 5 then pos_ang = 0.    ;Default position angle
+ if N_Elements(TheColor) EQ 0 then begin
+     IF N_Elements( Color ) eq 0 THEN Color = cgcolor('opposite')
+ endif else color = TheColor
+ 
+ if ~keyword_set(NPOINTS) then npoints = 120   ;Number of points to connect
+
+ phi = 2*!pi*(findgen(npoints)/(npoints-1))       ;Divide circle into Npoints
+ ang = pos_ang/!RADEG               	          ;Position angle in radians
+ cosang = cos(ang)
+ sinang = sin(ang)
+
+ x =  rmax*cos(phi)              ;Parameterized equation of ellipse
+ y =  rmin*sin(phi)
+
+ xprime = xc + x*cosang - y*sinang   	;Rotate to desired position angle
+ yprime = yc + x*sinang + y*cosang
+
+ if keyword_set(fill) then begin 
+ if datacoord then $
+   cgcolorfill, xprime, yprime, /DATA, COLOR=color, _STRICT_Extra = _extra else $
+   cgcolorfill, round(xprime), round(yprime),  COLOR=color, /DEVICE,  $
+                _STRICT_Extra = _extra
+ endif else begin		
+ if datacoord then $
+   cgplots, xprime, yprime, /DATA, COLOR=color, _STRICT_Extra = _extra else $
+   cgplots, round(xprime), round(yprime),  COLOR=color, /DEVICE,  $
+                _STRICT_Extra = _extra
+ endelse
+
+ if keyword_set(major) then begin
+     xmaj = xc + [rmax,-rmax]*cosang  ; rot & transl points (rmax,0),(-rmax,0)
+     ymaj = yc + [rmax,-rmax]*sinang
+     if keyword_set(fill) then begin
+     if datacoord then $
+       cgcolorfill, xmaj, ymaj, /DATA, COLOR=color, _STRICT_Extra=_extra  $
+     else   cgcolorfill, round(xmaj), round(ymaj), $
+       /DEVICE, COLOR=color, _STRICT_Extra=_extra
+      endif else begin
+     if datacoord then $
+       cgplots, xmaj, ymaj, /DATA, COLOR=color, _STRICT_Extra=_extra  $
+     else   cgplots, round(xmaj), round(ymaj), $
+       /DEVICE, COLOR=color, _STRICT_Extra=_extra
+       endelse
+ endif
+
+ if keyword_set(minor) then begin
+     xmin = xc - [rmin,-rmin]*sinang  ; rot & transl points (0,rmin),(0,-rmin)
+     ymin = yc + [rmin,-rmin]*cosang
+     if keyword_set(fill) then begin
+     if datacoord then $
+       cgcolorfill, xmin, ymin, /DATA, COLOR=color, _STRICT_Extra=_extra  $
+     else   cgplots, round(xmin), round(ymin), $
+         /DEVICE, COLOR=color, _STRICT_Extra=_extra
+     endif else begin
+     if datacoord then $
+       cgplots, xmin, ymin, /DATA, COLOR=color, _STRICT_Extra=_extra  $
+     else   cgplots, round(xmin), round(ymin), $
+         /DEVICE, COLOR=color, _STRICT_Extra=_extra
+     endelse
+ endif
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/tvlaser.pro b/Code/script_idl_mv/astrolib/tvlaser.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c4c3b2f4aa7055f181fcf746707747405ef08f21
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tvlaser.pro
@@ -0,0 +1,707 @@
+PRO TVLASER, hdr, Image, BARPOS=BarPos, CARROWS=CArrows, CLABELS=CLabels, $
+	COLORPS=ColorPS, COMMENTS=Comments, CSIZE=CSize, CTITLE=CTitle, $
+ 	DX=dX, DY=dY, ENCAP=encap, FILENAME=filename, HEADER=Header, HELP=Help,$
+	IMAGEOut=ImageOut, INTERP=Interp, MAGNIFY=Magnify, NoClose=noclose, $
+        NODELETE=NoDelete, NO_PERS_INFO=No_Pers_Info, NOEIGHT=NoEight, $ 
+        NOPRINT=NoPrint, NORETAIN = NoRetain, PORTRAIT=Portrait, $
+        PRINTER = Printer, REVERSE=Reverse, SCALE=Scale, TITLE=Title, $
+        XSTART=XStart, YSTART=YStart, XDIM=XDim, YDIM=YDim, $
+        TrueColor=TrueColor, BOTTOMDW=bottomdw, NCOLORSDW=ncolorsdw
+;+
+; NAME:
+;      TVLASER
+; PURPOSE:
+;      Prints screen or image array onto a Postscript file or printer.
+;      Information from FITS header is optionally used for labeling.  
+;
+; CALLING SEQUENCE:     
+;      TVLASER, [header, Image, BARPOS = ,CARROWS =, CLABELS = ,/COLORPS, 
+;             COMMENTS = ,CSIZE = ,CTITLE = , DX = , DY =, /ENCAP, FILENAME =
+;             HEADER = ,/HELP, IMAGEOUT = ,/INTERP, /MAGNIFY, /NoCLOSE, 
+;             /NoDELETE, /NO_PERS_INFO, /NoEIGHT, /NoPRINT, /NoRETAIN, 
+;             /PORTRAIT, PRINTER = , /REVERSE, /SCALE, TITLE = , /TrueColor, 
+;             XDIM=, XSTART=, YDIM=, YSTART=, BOTTOMDW=, NCOLORSDW= ]	
+;
+;       Note that the calling sequence was changed in May 1997
+; OPTIONAL INPUTS: 
+;       HEADER - FITS header string array.   Object and astrometric info from
+;               the FITS header will be used for labeling, if available
+;       IMAGE - if an array is passed through this parameter, then this image
+;               will be used rather than reading off the current window.  This
+;		allows easy use of large images.     It is usually preferable
+;               to optimally byte scale IMAGE before supplying it to TVLASER   
+;
+; OPTIONAL KEYWORD INPUT PARAMETERS: 
+;       BARPOS - A four- or five-element vector giving the position and
+;            orientation of the color bar.  The first four elements
+;            [X0,Y0,XSize,YSize] indicate the position and size of the color
+;            bar in INCHES, relative to origin of the displayed image.
+;            (X0,Y0) are the position of the lower left corner and 
+;            (XSize,YSize) are the width and height.  The fifth element is
+;            optional, and if present, the color bar will be printed
+;            horizontally rather than vertically.  If BARPOS is set to
+;            anything but a four- or five-element vector, the bar is NOT
+;            printed.  The default value is BARPOS = [-0.25, 0.0, 0.2, 2.0] 
+;       BOTTOMDW - The lowest value to use in building the density
+;            wedge.  Used with NCOLORSDW.  Compatible with BOTTOM and
+;            NCOLORS keywords of XLOADCT.
+;       CARROWS - The color to print the North-East arrows.  Default is dark.
+;            Three types of values can be passed:
+;                 SCALAR: that value's color in the current color table
+;                 3-ELEMENT VECTOR: the color will be [R,G,B]
+;                 STRING: A letter indicating the color.  Valid names are:  
+;                 'W' (white), 'D' (dark/black), 'R' (red),    'G' (green), 
+;                 'B' (blue),  'T' (turquoise),  'V' (violet), 'Y' (yellow), 
+;             If the keyword is set to a value of -1, the arrows are
+;             NOT printed.
+;       COLORPS - If present and non-zero, the idl.ps file is written using
+;             color postscript.
+;       COMMENTS - A string that will be included in the comment line below the
+;                image.  For multi-line comments you can either use "!C" in the
+;                string as a carriage return {although the vertical spacing
+;                might be a little off} or, preferably, make the COMMENTS a
+;                string array with each line as a separate element. 
+;       CLABELS - Color to print the labels, same format as for CARROWS.
+;       CSIZE - Color to print the size-scale bar and label, same format as for
+;                CARROWS.
+;       CTITLE - Color to print the title, same format as for CARROWS.
+;       DX,DY - offsets in INCHES added to the position of the figure on the
+;               paper.  As is the case for the device keywords XOFFSET and
+;               YOFFSET, when in landscape mode DX and DY are the same
+;               *relative to the paper*, not relative to the plot (e.g., DX is
+;               the horizontal offset in portrait mode, but the *vertical*
+;               offset in landscape mode).
+;       ENCAP - If present and non-zero, the IDL.PS file is written in
+;               encapsulated postscript for import into LaTeX documents
+;       FILENAME - scalar string giving name of output postscript file.
+;               Default is idl.ps.   Automatically sets /NODELETE
+;       HEADER = FITS header.   This is an alternative to supplying the FITS
+;                header in the first parameter.
+;       HELP - print out the sytax for this procedure.
+;       INTERP - If present and non-zero, current color table will be
+;                interpolated to fill the full range of the PostScript color
+;                table (256 colors).  Otherwise, the current color table will be
+;                directly copied.   You probably will want to use this if you
+;                are using IMAGE keyword and a shared color table.
+;       MAGNIFY - The net magnification of the entire figure.  At this point,
+;                the figure is not automatically centered on the paper if the
+;                value of MAGNIFY is not equal to 1, but the DX and DY keywords
+;                can be used to shift location.  For example, to fit a full plot
+;                on the printable area (8.5x8.5 inches) of the Tek PhaserIISD
+;                color printer use:  MAGNIFY=0.8, DX=0.5, DY=0.5.;       
+;       NCOLORSDW - The number of values to include in the density
+;                wedge.  Used with BOTTOMDW.  Compatible with
+;                BOTTOM/NCOLORS keywords of XLOADCT.
+;       NoCLOSE - If present and non-zero, then the postscript file is not
+;             closed (or printed), the device is set to 'PS', and the data 
+;             coordinate system is set to match the image size.  This allows the
+;             user to add additional plotting commands before printing.  For 
+;             example, to include a 15 pixel circle around a source at 
+;             coordinates (150,160), around an image, im, with FITS header 
+;             array, h
+;
+;                IDL> tvlaser,h,im,/NoClose      ;Write image & annotation
+;                IDL> tvcircle,15,150,160,/data  ;Draw circle
+;                IDL> device,/close              ;Close postscript file & print
+;
+;       NoDELETE - If present and non-zero, the postscript file is kept AND is 
+;                 also sent to the printer
+;       NoEIGHT - if set then only four bits sent to printer (saves space)
+;       NO_PERS_INFO - if present and non-zero, output notation will NOT
+;                 include date/user block of information.
+;       NoPRINT - If present and non-zero, the output is sent to a file (default
+;                name 'idl.ps'), which is NOT deleted and is NOT sent to the 
+;                printer.
+;       NoRETAIN - In order to avoid possible problems when using TVRD with
+;                 an obscured window, TVLASER will first copy the current window
+;                 to a temporary RETAIN=2 window.    Set /NORETAIN to skip this
+;                 step and improve performance
+;       PORTRAIT - if present and non-zero, the printer results will be in
+;                 portrait format; otherwise, they will be in landscape format.
+;                 If labels are requested, image will be in portrait mode,
+;                 regardless
+;       PRINTER - scalar string giving the OS command to send a the postscript
+;               file to the printer.   Under Unix, the default value of PRINTER
+;               is 'lpr ' while for other OS it is 'print ' 
+;       REVERSE - if present and non-zero, color table will be fliped, so black
+;               and white are reversed.
+;       SCALE - if present and non-zero, image will be bytscaled before being
+;               sent to postscript file.      
+;       TITLE - if present and non-zero, the string entered here will be the
+;               title of the picture.  Default is the OBJECT field in the
+;               header (if present).
+;       TRUECOLOR - if present and non-zero, the postscript file is created
+;               using the truecolor switch (i.e. true=3). The colorbar is
+;               not displayed in this mode.  
+;       XDIM,YDIM - Number of pixels.  Default is from !d.x_size and !d.y_size,
+;               or size of image if passed with IMAGE keyword.
+;       XSTART,YSTART - lower left corner (default of (0,0))
+;
+; OPTIONAL KEYWORD OUTPUT PARAMETER
+;        IMAGEOUT = the image byte array actually sent to the postscript file.
+;
+; SIDE EFFECTS: 
+;        A postscript file is created in the current directory.  User must have 
+;        write privileges in the current directory.  The file is named idl.ps
+;        unless the FILENAME keyword is given.   The file is directed to the
+;        printer unless the /ENCAP, /NoCLOSE, or /NOPRINT keywords are given.
+;        After printing, the file is deleted unless the /NODELETE or FILENAME 
+;        keywords are given. 
+; PROCEDURE:  
+;       Read display or take IMAGE and then redisplay into a postscript file.
+;       If a header exists, printout header information.  If header has
+;       astrometry, then print out orientation and scale information.
+; PROCEDURES USED:
+;        ARROWS, EXTAST, FDECOMP, GETROT, PIXCOLOR, SXPAR(), XYAD, ZPARCHECK
+;
+;*EXAMPLE:
+;       1) Send a true color image (xsize,ysize,3) to a printer (i.e. print23l),
+;                tvlaser,huv,cpic,/colorps,/truecolor,printer="print23l"
+;                % TVLASER: Now printing image: $print23l idl.ps
+;
+; MODIFICATION HISTORY:     
+;       Major rewrite from UIT version   W. Landsman   Dec 94
+;       Massive rewrite.  Added North-East arrows, pixel scale bar, color bar,
+;       and keywords DX, DY, MAGNIFY, INTERP, HELP, and COMMENTS.
+;       Created ablility to define colors for annotation and
+;       text.  Repositioned text labels.     J.Wm.Parker, HITC, 5/95
+;       Make Header and Image parameters instead of keywords.   Add PRINTER
+;       keyword.   Include alternate FITS keywords.   W. Landsman May 97      
+;       Copy to a RETAIN=2 window, work without FITS header W. Landsman June 97
+;       Cleaner output when no astrometry in header  W. Landsman  June 97
+;       Added /INFO to final MESSAGE  W. Landsman   July 1997
+;       12/4/97	jkf/acc	- added TrueColor optional keyword.
+;       Added /NoClose keyword, trim Equinox format  W. Landsman 9-Jul-1998
+;       Don't display coordinate labels if no astrometry, more flexible
+;       formatting of exposure time W. Landsman 30-Aug-1998
+;       BottomDW and NColorsDW added.  R. S. Hill, 1-Mar-1999
+;       Apply func tab to color bar if not colorps.  RSH, 21 Mar 2000
+;       Fix problem with /NOCLOSE and unequal X,Y sizes  W. Landsman Feb 2001
+;       Use TVRD(True=3) if /TRUECOLOR set    W. Landsman   November 2001
+;       More synonyms, check for header supplied W. Landsman November 2007
+;-
+ compile_opt idl2
+ on_error,2
+
+ if keyword_set(Help) then begin
+   print, 'Syntax:  TVLASER, [ Header, Image ]'
+   print, 'Keywords:  BARPOS= ,CARROWS= , CLABELS= ,/COLOPS, COMMENTS= ,'
+   print, '           CSIZE= , CTITLE= , DX= , DY= , /ENCAP, FILENAME= ,'
+   print, '           HEADER= ,/HELP, IMAGEOUT= , /INTERP, /MAGNIFY,/NoCLOSE ,'
+   print, '           /NoDELETE, NO_PERS_INFO, /NoEIGHT, /NoPRINT, /NORETAIN,'
+   print, '           /PORTRAIT,PRINTER=,/REVERSE, /SCALE, TITLE= , /TRUECOLOR,' 
+   print, '           XDIM= ,XSTART=, YDIM= , YSTART= ] '
+   print, '   '        
+   return
+ endif
+
+;----------------------------;
+;  SECTION:  INITIALIZATION  ;
+;----------------------------;
+
+;;;
+;   Save some info and set some variables.  LogoDir may need to be changed
+; depending on where the GIF logos are.
+;
+ sv_device = !D.NAME
+ sv_color = !P.Color
+ if !D.NAME EQ 'PS' then set_plot,'X'     ;Return to X terminal
+ tvlct,sv_rr,sv_gg,sv_bb,/get
+
+ if keyword_set(NoEight)  THEN NBits = 4 ELSE NBits = 8
+ if keyword_set(Portrait) THEN Lands = 0 ELSE Lands = 1
+ ColorPS  = keyword_set(ColorPS)
+ Encap    = keyword_set(Encap)
+ NoPrint  = keyword_set(NoPrint)
+ NoDelete = keyword_set(NoDelete)
+ TrueColor= keyword_set(TrueColor)
+ if TrueColor then TrueValue =3 else TrueValue =0
+ 
+ if N_elements(hdr) EQ 0 then $
+	if N_elements(header) NE 0 then hdr = header
+ if (N_params() GE 1) and (N_elements(hdr) EQ 0) then message,/INF, $
+        'Warning - No valid FITS header supplied'	
+ if N_elements(hdr) NE 0 then zparcheck,'TVLASER',hdr,1,7,1,'FITS image header'
+;;;
+;   If no image was passed in the IMAGE keyword, then we will be reading the
+; image from the screen.  Default values are to start at 0,0 and read the
+; entire window.
+;
+ FromTV = N_elements(Image) eq 0
+ if FromTV then begin
+   if !D.WINDOW EQ -1 then begin
+	tvlaser,/help
+	return
+   endif
+   message,'Reading image from window ' + strtrim(!D.WINDOW,2) + $
+        ' ... Please be patient', /INF
+   if not keyword_set(XStart) then XStart = 0
+   if not keyword_set(YStart) then YStart = 0
+   if not keyword_set(XDim) then XDim = !d.x_size
+   if not keyword_set(YDim) then YDim = !d.y_size
+   if not keyword_set(noretain) then begin
+	chan = !D.WINDOW
+	xsize = !D.X_SIZE & ysize = !D.Y_SIZE
+	window,/free,xsize=xsize,ysize=ysize
+	wset,!D.WINDOW
+	device,copy=[0,0,xsize,ysize,0,0,chan]
+   endif
+   ImageOut = tvrd(XStart,YStart,XDim,YDim,true = truevalue)
+   if not keyword_set(noretain) then begin
+	wdelete,!D.WINDOW
+	wset,chan
+   endif
+ endif else begin
+   XStart = 0
+   YStart = 0
+   XDim   = (size(Image))[1]
+   YDim   = (size(Image))[2]
+   ImageOut = Image
+ endelse
+;;;
+;   YSpace is used to scale the vertical spacing of text and the title.
+;
+ YSpace  = (float(Xdim) / Ydim) > 1.              ;Modified December 1994 WBL
+ XSpace  = (float(Ydim) / Xdim) > 1.
+
+;;;
+;   If using B/W PostScript, use NTSC color -> B/W formula, J Brinkmann
+;   Scale and/or reverse if desired.
+;
+ if not(ColorPS) then ImageOut = $
+   0.299 * sv_rr[ImageOut] + 0.587 * sv_gg[ImageOut] + 0.114 * sv_bb[ImageOut]
+ if keyword_set(Scale)   then ImageOut = bytscl(ImageOut)
+ if keyword_set(Reverse) then ImageOut = 255b - temporary(ImageOut)
+
+;;;
+;   If a header is given, put in portrait mode regardless. 
+;
+ if N_elements(hdr) NE 0 then Lands = 0
+
+;;;
+;   Set up colors for density wedge.
+;
+ if N_elements(BottomDW) LE 0 then BottomDW = 0
+ nc = !D.table_size - BottomDW
+ if n_elements(NColors) GT 0 then nc = nc < ncolors
+ if nc LE 0 then begin
+   message, /INFO, 'Bad color spec; using default'
+   BottomDW = 0
+   nc = !D.table_size
+ endif
+
+
+;------------------------------;
+;  SECTION:  POSTSCRIPT SETUP  ;
+;------------------------------;
+
+;;;
+;   Redirect output to Postscript printer file, which may be printed.
+;   Size of image is restricted to 7.5 inches in the paper's narrow direction
+; for MAGNIFY=1.  If we will be printing out header info, then restrict the
+; Y size to be no more than 7.5 also.
+;
+if (Lands eq 1) then begin
+   inx = 10.0
+   iny = float(YDim)/float(XDim)*float(inx)
+   if (iny gt 7.5) then begin
+     iny = 7.5
+     inx = (float(XDim)/float(YDim))*float(iny)
+   endif
+ endif
+
+ if (Lands eq 0) then begin
+   if N_elements(hdr) NE 0 then iny = 7.5 else iny = 10.0
+   inx = float(XDim)/float(YDim)*float(iny)
+   if (inx gt 7.5) then begin
+     inx = 7.5
+     iny = (float(YDim)/float(XDim))*float(inx)
+   endif
+ endif
+
+;;;
+;   Some info for the user, and setting the filename.
+;
+ pstype = ' '
+ if Encap then pstype = pstype + 'encapsulated '
+ if ColorPS then pstype = pstype + 'color '
+ if not keyword_set(filename) then fname = 'idl.ps' else begin
+   fdecomp,filename,disk,dir,name,ext
+   if ext EQ '' then ext = 'ps'
+   fname = disk + dir + name + '.' + ext
+   NoDelete = 1
+ endelse 
+ if keyword_set(NoDelete) or keyword_set(EnCap) or keyword_set(NoPrint) then $ 
+ message,'Writing image to' + pstype + 'postscript file ' + fname, /INF
+
+;;;
+;   Set plot to the PostScript printer.  Set all the device keywords.
+;
+set_plot, 'ps', INTERPOLATE=keyword_set(Interp)
+sv_font = !P.FONT
+!p.font = 0
+
+ if not keyword_set(dX) then dX = 0
+ if not keyword_set(dY) then dY = 0
+
+ XOff =  0.75 + dX
+ YOff = 10.25 + dY
+ if Lands then begin
+   device, /landscape
+   YOff = inx + ((11 - inx) / 2.0) + dY   ; centered
+ endif else begin
+   device, /portrait
+   YOff = Yoff - iny
+ endelse
+
+ device, xsize=inx, ysize=iny, xoffset=XOff, yoffset=YOff, /inches, $
+   bits=NBits, filename=fname, /helvetica, encapsulated=Encap, color=ColorPS
+
+ if keyword_set(Magnify) then device, scale=Magnify else device, scale=1
+
+
+;-----------------------;
+;  SECTION:  TV OUTPUT  ;
+;-----------------------;
+
+ tv, ImageOut,true=TrueValue
+
+;   If the BarPos keyword has four or five elements, then show the color bar.
+
+ if (not(TrueValue)) then begin 
+   if (N_elements(BarPos) eq 0) then BarPos = [-0.25, 0.0, 0.2, 2.0]
+   NumEls = N_elements(BarPos)
+   if ( (NumEls eq 4) or (NumEls eq 5) ) then begin
+    ColorBar = byte(round(congrid(findgen(nc)+BottomDW, 256))) $
+       # make_array(20,val=1b)
+    if not(ColorPS) then $
+       ColorBar = 0.299 * sv_rr[ColorBar] + 0.587 * sv_gg[ColorBar] $
+                  + 0.114 * sv_bb[ColorBar]
+    ColorBar[0:*,[0,19]]  = 0
+    ColorBar[[0,255],0:*] = 0
+    if (NumEls eq 4) then ColorBar = transpose(ColorBar)
+    tv, ColorBar, BarPos[0],BarPos[1], xsize=BarPos[2],ysize=BarPos[3], /INCHES
+   endif
+ endif
+
+;;;
+;   Now that the image has been displayed with the desired color table, we will 
+; play with the color table a bit to get the appropriate colors for the text,
+; arrows, and scale bar.  The three RGB values for each one will be loaded into
+; vectors called things like 'CArrowsRGBN', 'CSizeRGBN', etc.  The last value
+; in this vector will be the location of that color in the color table.
+;   "Colors" is a string array of the keyword names, then via the EXECUTE
+; function, we determine what the content of each variable is: a string to be
+; used inthe pixcolor procedure, a single number indicating the location in the
+; current color table, or a 3-element vector with RGB values.  One reason for
+; doing it this way, is that if more objects to be colored are added to the
+; keywords, only the variable COLORS need be changed here by adding those
+; keyword names.
+;   "Val" is where we will be temporarily putting the new colors (usually in
+; the bottom bin).
+;
+ Colors = ['CArrows','CSize','CTitle','CLabels']
+ r_new = bytarr(n_elements(Colors))
+ g_new = r_new
+ b_new = r_new
+
+ for N=0,(n_elements(Colors) -1) do begin
+  tvlct, sv_rr, sv_gg, sv_bb
+  Val = 0
+
+  dummy = execute( 'NumEls = n_elements(' + Colors[N] + ')' )
+  if (NumEls eq 0) then begin
+    dummy = execute( Colors[N] + ' = "D"' )
+    NumEls = 1
+  endif
+  dummy = execute( 'C = ' + Colors[N] )
+  if (NumEls eq 1) then begin  ; string or color value
+    if ((size(C))[1] eq 7) then pixcolor, Val, C else Val = C
+  endif else begin
+    if (NumEls eq 3) then tvlct,transpose(C) else pixcolor, Val, 'D'
+  endelse
+
+  tvlct, r, g, b, /get
+  if (Val[0] ne -1) then begin
+     r_new[N] = r[Val]
+     g_new[N] = g[Val]
+     b_new[N] = b[Val]
+     dummy = execute(Colors[N]+'RGBN = [r[Val],g[Val],b[Val],N]')
+  endif
+endfor
+
+ tvlct, r_new, g_new, b_new
+
+
+;-------------------------------;
+;  SECTION:  HEADER and LABELS  ;
+;-------------------------------;
+
+;;;
+;   If a FITS header was given then include whatever of the following FITS
+; keywords that are present as annotation:  OBJECT (becomes the title if none
+; given), TELESCOP, IMAGE, EXPTIME, EQUINOX, CRVAL1 (Right Ascension), CRVAL2
+; (Declination), NAXIS1, NAXIS2, CD (Rotation angle and pixel size), PDSDATIM
+; (Date of Microdensitometry).  Also will include the name of the user and the
+; current date.  Some blocks can be suppressed...see description of keywords
+; above.  Also prints directional arrows and scale.
+;  
+if (N_elements(Hdr) NE 0) then begin
+ 
+
+;;;
+;   Does the header have astrometry?
+;
+  extast, hdr, astr, NoAstrom
+  if NoAstrom GT 0 then begin
+    ast_type = strmid( strupcase( strtrim(astr.ctype[0],2) ), 0 ,4)
+    if  ((ast_type NE 'RA--') and (ast_type NE 'GLON') and $ ;Valid projection?
+         (ast_type NE 'ELAT') ) then NoAstrom = -1
+  endif
+	
+  if (NoAstrom LT 0) then begin
+    rga      = 'N/A'
+    decl     = 'N/A'
+    equi     = ''
+    ROTATE   = 'N/A'
+    CDELT    = [0.0,0.0]
+    CDELTAS  = 'N/A'
+  endif else begin
+    xcen = (XDim-XStart-1)/2.
+    ycen = (YDim-YStart-1)/2.
+    if FromTV then zoom_xy,xcen,ycen ;In case TV image has non-zero zoom or roam
+    xyad,hdr, xcen, ycen, ra_cen, dec_cen
+    str = adstring(ra_cen,dec_cen,1)
+    rga = strmid( str, 1, 11)
+    decl = strmid( str, 14, 11)
+    equi = sxpar( hdr, 'EQUINOX', Count = N_equi)
+    if N_equi EQ 0 then equi = '' else $ 
+              equi = '(' + strmid(strtrim(equi,2),0,7) + ')'
+    getrot, hdr ,ROTATE, CDELT
+    ROTATE  = strtrim(string(ROTATE,  format='(f7.2)'),2) + ' degrees'
+    CDELT   = abs(CDELT*60.*60.)
+    if CDELT[0] LT 0.1 then fmt = '(f7.3)' else fmt = '(f7.2)'
+    CDELTAS = strtrim(string(CDELT[0],format=fmt ),2)
+    if (abs(CDELT[0] - CDELT[1]) GT 0.05*CDELT[0]) THEN $
+       CDELTAS = CDELTAS + ' by ' + strtrim(string(CDELT[1],format=fmt),2)
+    CDELTAS = CDELTAS + ' arcsec/pixel'
+  endelse
+
+;;;
+;   Printout the image information?  YSpace is used to scale the spacing of the
+; linformation lines in NORMAL units.  dY is one line height.  LabXs and LabYs
+; are arrays that define the placement of Label/Value pairs in the NORMAL
+; coordinates.  So to increment to the next line, simply use:
+;   LabYs = LabYs + dY
+;
+if (strtrim(CLabels[0],2) ne '-1') then begin
+    dY     = -0.025 * YSpace
+    LabYs  = [-0.05, -0.05] * YSpace
+    LabX1s = [ 0.01,  0.21] * XSpace
+    LabX2s = [ 0.64,  0.74] * XSpace
+
+;;;
+;  Set the label color and print out each label/value.
+;
+  !P.Color = CLabelsRGBN[3]
+
+;OBJECT
+    OBJ = strtrim( sxpar(hdr,'OBJECT', Count = N_Obj),2 )
+    if N_Obj EQ 0 then begin 
+    OBJ = strtrim( sxpar( hdr,'TARGNAME', Count = N_Obj),2)
+    if N_Obj EQ 0 then OBJ = 'N/A'
+    endif
+    XYOUTS, LabX1s, LabYs, ['OBJECT:',OBJ],/ NORMAL
+    LabYs = LabYs + dY
+
+;TITLE (set here, but print out later in case no header was given)
+    if NOT keyword_set(TITLE) then begin
+      if (N_Obj NE 0) then TITLE=OBJ else TITLE = ''
+    endif 
+
+;IMAGE ID
+    imname = 'N/A'
+    imname = sxpar(hdr,'IMAGE', Count = N_image)
+    if N_image EQ 0 then imname = sxpar(hdr,'EXPNAME', Count = N_image)
+    if N_image EQ 0 then imname = sxpar(hdr,'OBS_ID', Count = N_image)
+    if N_image EQ 0 then imname = sxpar(hdr,'ROOTNAME', Count = N_image)
+    imname = strtrim(imname,2)
+ 
+  
+    XYOUTS,LabX1s,LabYs,['IMAGE:',IMNAME],/NORMAL
+    LabYs = LabYs + dY
+
+  LabYs = LabYs + dY
+
+;TELESCOPE
+    scop = sxpar( hdr,'INSTRUME', Count = N_Scop)
+    if N_Scop EQ 0 then scop = sxpar( hdr,'TELESCOP', Count = N_Scop)
+    if N_Scop EQ 0 then scop = sxpar( hdr,'OBSERVAT', Count = N_Scop)
+    if N_Scop EQ 0 then scop = '' else scop = strtrim(scop,2)
+    detector = sxpar( hdr,'DETECTOR', Count = N_det)
+    if N_det EQ 0 then detector = '' else detector = strtrim(detector,2)
+    if scop EQ '' then scop = detector else $
+    if detector NE '' then scop = scop + '/' + detector
+    XYOUTS,LabX1s,LabYs,['INSTRUMENT:',scop],/NORMAL
+
+;SIZE
+    SIZ = strtrim(XDim,2) +' by ' + strtrim(YDim,2) + ' pixels'
+    XYOUTS,LabX2s,LabYs,['SIZE:',SIZ],/NORMAL
+    LabYs = LabYs + dY
+
+;FILTER
+    filter = sxpar(hdr, 'FILTER', Count= N_filter)
+    if N_filter EQ 0 then filter = sxpar(hdr, 'FILTNAM1', Count= N_filter)
+    if N_filter EQ 0 then filter = sxpar(hdr, 'FILTER1', Count= N_filter)
+    if N_filter EQ 0 then FILTER = 'N/A' else filter = strtrim(filter,2)
+    XYOUTS,LabX1s,LabYs,['CAMERA/FILTER:',FILTER],/NORMAL
+
+;SCALE
+    if NoAstrom GE 0 then XYOUTS,LabX2s,LabYs,['SCALE:',CDELTAS],/NORMAL
+    LabYs = LabYs + dY
+
+;EXPOSURE TIME   First try 'EXPTIME' then 'EXPOSURE' then 'INTEG'
+    exptime = sxpar(hdr, 'EXPTIME', Count = N_time)
+    if N_time EQ 0 then exptime = sxpar(hdr, 'EXPOSURE', Count = N_time)
+    if N_time EQ 0 then exptime = sxpar(hdr, 'INTEG', Count = N_time)
+    if N_time EQ 0 then exptime = 'N/A' else $
+	exptime = strmid( strtrim(exptime,2),0,6) + ' seconds'
+    XYOUTS,LabX1s,LabYs,['EXPOSURE TIME:',EXPTIME],/NORMAL	
+    LabYs = LabYs + dY
+
+    LabYs = LabYs + dY
+
+    if noastrom GE 0 then begin
+;CENTER COORDINATES
+    XYOUTS, LabX1s, LabYs,['CENTER '+ equi + ':', $
+     'RA = ' + RGA + '    DEC = ' + DECL], /NORMAL
+    LabYs = LabYs + dY
+
+;ROTATION
+    XYOUTS,LabX1s,LabYs,['ROTATION:',strtrim(ROTATE,2)],/NORMAL
+    LabYs = LabYs + dY
+    endif
+
+
+
+;COMMENTS
+    if keyword_set(Comments) then begin
+      XYOUTS,LabX1s[0],LabYs[0],'COMMENTS:',/NORMAL
+      for N=0,(n_elements(Comments)-1) do $
+        XYOUTS,LabX1s[1],(LabYs[1] + (dY * N)),Comments[N],/NORMAL
+    endif
+    LabYs = LabYs + dY
+
+;USER and DATE/TIME
+    if not keyword_set(No_pers_info) then begin
+      XYOUTS, LabX2s[0],LabYs[0], GetEnv('USER') + '  (' + $
+      STRMID(systime(),4,20) + ')' ,SIZE=0.9, /NORMAL
+    endif
+
+  endif
+
+
+;ARROWS
+;   The calculations AX and XY allow the smallest use of space for the arrows
+; for all possible rotation angles.  To test the extent of the circle, add
+; code like the following in before the "R = float(..." line:
+;   hextract,ImageOut,h,i1,h1,0,5,0,5 & for N=0,18 do begin
+;   hrot,i1,h1,i2,h2,N*20,-1,-1,0  & getrot, h2 ,Rotate
+;
+  if ((strtrim(CArrows[0],2) ne '-1') and (NoAstrom ne -1)) then begin
+    R = float(rotate) * !pi / 180
+    AX = ( 0.50 + (0.05 * (cos(R) + sin(R)))) * XSpace
+    AY = (-0.10 - (0.05 * (cos(R) - sin(R)))) * YSpace
+
+    !P.Font  = -1
+    !P.Color = CArrowsRGBN[3]
+    arrows, hdr, AX, AY, /NORMAL, FONT=13, COLOR=!P.Color, arrowlen=3, charsize=2
+    !P.Font  = 0
+  endif 
+
+
+;SIZE SCALE BAR
+;   This is probably more complicated than necessary, but the idea is to find
+; the best size scale bar for any image, where the scale may be a few arcsec
+; or a few degrees.
+;   "BarLength" is the length of a 1 arcsecond bar in normal coordinates
+;   "BarScale" is the list of standard sizes for the bar in arcsec or arcmin.
+;   "BarLength" is the length in normal coordiates of the "best" scale bar.
+;
+  if ((strtrim(CSize[0],2) ne '-1') and (NoAstrom ne -1)) then begin
+    BarLength = 1.0 / (CDelt[0] * XDim)
+    BarScale = [1,2,3,5,10,15,20,25,30,40]
+    MinBar   = 0.1 * XSpace
+
+    BS = where((BarLength * BarScale) gt MinBar)        ; bar scale in arcsec?
+    if (BS[0] ne -1) then begin
+      BarLength = BarLength * BarScale[BS[0]]
+      BarLabel  = strtrim(BarScale[BS[0]], 2) + '"'
+    endif else begin
+      BS = where((BarLength * BarScale * 60) gt MinBar) ; bar scale in arcmin?
+      if (BS[0] ne -1) then begin
+        BarLength = BarLength * BarScale[BS[0]] * 60
+        BarLabel  = strtrim(BarScale[BS[0]], 2) + "'"
+      endif else begin
+        BarLength = BarLength * 3600
+        BarLabel  = '1 degree'
+      endelse
+    endelse
+
+;    Barlength = BarLength * XSpace
+    BarX      =   0.7 * XSpace             ; left end of bar
+    BarY      = -0.03 * YSpace             ; Y position of bar
+    BarDY     = 0.01 * [-1,1] * YSpace     ; height of bar's endpoints
+    LabY      = BarY - (0.025 * YSpace)    ; position of label
+
+    !P.Color = CSizeRGBN[3]
+    plots, BarX+[0,BarLength], [BarY,BarY], /NORMAL
+    plots, [BarX,BarX], BarY+BarDY, /NORMAL
+    plots, BarLength+[BarX,BarX], BarY+BarDY,/NORMAL
+    xyouts, ((BarX + (BarX + BarLength)) / 2.0), LabY, /NORMAL, ALIGN=0.5, $
+      '!6'+BarLabel+'!X', FONT=-1
+
+  endif
+
+endif 
+
+;;;
+; TITLE  (handle here in case no header was given but TITLE keyword was used.)
+;
+ if (keyword_set(TITLE) and (strtrim(CTitle[0],2) ne '-1')) then begin
+   !P.Color = CTitleRGBN[3]
+   XYOUTS, 0.50*XSpace, 1+(0.01*YSpace), TITLE,SIZE=2.0, /NORMAL, ALIGN=0.5
+ endif
+
+ if keyword_set(NoClose) then begin
+       plot,[0,xdim-1],[0,ydim-1],/noerase,xsty=5,ysty=5,/nodata, $
+       pos = [0,0,1,1]
+       return
+ endif 
+
+ Device,/close
+
+;-------------------------------;
+;  SECTION:  PRINTING THE FILE  ;
+;-------------------------------;
+
+ if not(NoPrint or Encap) then begin        ;Should the file be printed out?
+ if not keyword_set(PRINTER) then begin
+	 case !VERSION.OS_FAMILY of
+	 'unix': printer = 'lpr'
+	 else: printer = 'print'
+	 endcase
+ endif
+ spawn,printer + ' ' + fname
+ message,/INFO,'Now printing image: $' + printer + ' ' + fname
+ endif
+
+;  Reset output direction to X-windows, and restore some variables.
+
+ tvlct,sv_rr,sv_gg,sv_bb
+ set_plot, sv_device
+ !P.font = sv_font
+ !P.Color = sv_color
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/tvlist.pro b/Code/script_idl_mv/astrolib/tvlist.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3acc4da5068b7a9a3f6c5dcccd6894a5bee51afd
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/tvlist.pro
@@ -0,0 +1,164 @@
+pro tvlist, image, dx, dy, TEXTOUT = textout, OFFSET = offset, ZOOM = ZOOM
+;+
+; NAME:
+;	TVLIST
+; PURPOSE:
+;	Cursor controlled listing of image pixel values in a window. 
+;
+; CALLING SEQUENCE:
+;	TVLIST, [image, dx, dy, TEXTOUT=, OFFSET= , ZOOM= ]
+;
+; OPTIONAL INPUTS:
+;	IMAGE - Array containing the image currently displayed on the screen.
+;		If omitted, the byte pixel intensities are read from the TV
+;		If the array does not start at position (0,0) on the window then
+;		the OFFSET keyword should be supplied.
+;
+;	DX     -Integer scalar giving the number of pixels in the X direction 
+;		to be displayed.  If omitted then DX = 18 for byte images, and 
+;		DX = 14 for integer images.  TVLIST will display REAL data 
+;		with more significant figures if more room is availble to 
+;		print.  
+;
+;	DY    - Same as DX, but in Y direction.  If omitted, then DY = DX 
+;
+; OPTIONAL INPUT KEYWORDS:
+;      OFFSET - 2 element vector giving the location of the image pixel (0,0) 
+;		on the window display.   OFFSET can be positive (e.g if the 
+;		image is centered in a larger window) or negative (e.g. if the
+;		only the central region of an image much larger than the window
+;		is being displayed. 
+;		Default value is [0,0], or no offset.
+;	ZOOM - Scalar specifying the magnification of the window with respect
+;		to the image variable.    Use, for example, if image has been
+;		REBINed before display.
+;	TEXTOUT - Optional keyword that determines output device.
+;		The following dev/file is opened for output.
+;
+;		textout=1	TERMINAL using /more option (default)
+;		textout=2	TERMINAL without /more option
+;		textout=3	<program>.prt  
+;		textout=4	laser.tmp
+;		textout=5       user must open file
+;		textout=7	Append to an existing <program>.prt file if it
+;				exists
+;		textout = filename (default extension of .prt)
+;
+;	If TEXTOUT > 3 or set to a filename, then TVLIST will prompt for a 
+;	brief description to be included in the output file
+; OUTPUTS:
+;	None.
+; PROCEDURE:
+;	Program prompts user to place cursor on region of interest in 
+;	image display.  Corresponding region of image is then displayed at
+;	the terminal.   A compression factor between the image array and the
+;	displayed image is determined using the ratio of image sizes.  If 
+;	necessary, TVLIST will divide all pixel values in a REAL*4 image by a 
+;	(displayed) factor of 10^n (n=1,2,3...) to make a pretty format.
+;
+; SYSTEM VARIABLE:
+;	The nonstandard system variable !TEXTOUT is used as an alternative to
+;	the keyword TEXTOUT.   The procedure ASTROLIB can be used to define
+;	!TEXTOUT (and !TEXTUNIT) if necessary.
+;
+; RESTRICTIONS:
+;	TVLIST may not be able to correctly format all pixel values if the
+;	dynamic range near the cursor position is very large.
+;
+;       For the cursor to work under Mac OSX  the "Click-through Inactive 
+;       Windows" setting the in X11:Preferences:Window needs to be enabled.
+; PROCEDURES CALLED:
+;	IMLIST, UNZOOM_XY
+; REVISION HISTORY:
+;	Written by rhc, SASC Tech, 3/14/86.
+;	Added textout keyword option, J. Isensee, July, 1990
+;	Check for readable pixels     W. Landsman   May 1992
+;	Use integer format statement from F_FORMAT    W. Landsman   Feb 1994
+;	Added OFFSET, ZOOM keywords  W. Landsman   Mar 1996
+;	More intelligent formatting of longword, call TEXTOPEN with /STDOUT
+;		W. Landsman  April, 1996
+;	Added check for valid dx value  W. Landsman   Mar 1997
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Major rewrite to call IMLIST, recognize new integer data types
+;                                           W. Landsman Jan 2000
+;       Remove all calls to !TEXTUNIT   W. Landsman   Sep 2000
+;       Always call UNZOOM_XY for MOUSSE compatibility  W. Landsman Sep. 2004
+;-
+ On_error,2
+ Compile_opt idl2
+
+ npar = N_params()
+
+ if npar GE 2 then $
+    if N_elements( dx) NE 1 then $
+          message, 'ERROR - Second parameter (format width) must be a scalar'
+
+ if npar EQ 0 then begin 	;Read pixel values from TV
+
+        if (!D.FLAGS and 128) NE 128 then message, $
+             'ERROR -- Unable to read pixels from current device ' + !D.NAME
+	message,'No image array supplied, pixel values read from TV',/INF 
+	type = 1		;Byte format
+
+ endif else begin
+
+	sz = size(image)
+	if (sz[0] LT 2) or (sz[sz[0]+2] NE sz[1]*sz[2]) then $
+		message,'Image array (first parameter) not 2-dimensional'
+    	type = sz[sz[0]+1]	     ;Byte or Integer image?
+
+ endelse 
+
+ if (!D.FLAGS AND 256) EQ 256 THEN wshow,!D.WINDOW
+
+ if ( npar GT 0 ) then begin 	;get X and Y dimensions of the image
+	xdim = sz[1] - 1 
+	ydim = sz[2] - 1 
+ endif else begin		;dimensions of TV display
+	xdim = !d.x_vsize
+	ydim = !d.y_vsize
+ endelse
+
+ if N_elements(dx) EQ 0 then  $  ;Use default print size? 
+    if type EQ 1 then dx = 18 else dx = 15 else $
+    if (dx GT 38) then begin 
+	message, 'ERROR - X Pixel Width (second parameter) value of ' + $
+		strtrim(dx,2) + ' is too large',/CON
+    return
+ endif
+
+ tvcrs, 1                                    ;Make sure cursor is on
+ print, 'Put the cursor on the area you want to list; press any mousse button'
+ if Npar GT 0 then begin
+   cursor, xtv, ytv, /WAIT, /DEVICE
+   unzoom_xy, xtv, ytv, xim, yim, OFFSET=offset, ZOOM=zoom 
+   xim = fix(xim+0.5)
+   yim = fix(yim+0.5)
+ endif else cursor, xim, yim, /WAIT, /DEVICE
+
+ if npar LT 3 then dy = dx
+; Don't try to print outside the image
+  xmax = (xim + dx/2) < xdim
+  xmin = (xim - dx/2) > 0 
+  ymax = (yim + dy/2) < ydim
+  ymin = (yim - dy/2) > 0 
+
+ dx = xmax - xmin + 1 & dy = ymax - ymin + 1
+
+ if xmin GE xmax then $
+    message,'ERROR - The cursor is off the image in the x-direction'
+ if ymin GE ymax then $
+    message,'ERROR - The cursor is off the image in the y-direction'
+
+
+ if npar EQ 0 then begin 
+    image = tvrd( xmin,ymin,dx,dy)
+    xim = dx/2
+    yim = dy/2
+    zoffset = [xmin,ymin]
+ endif
+
+ imlist,image,xim,yim,dx=dx,dy=dy,textout=textout,offset=zoffset
+ 
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/unzoom_xy.pro b/Code/script_idl_mv/astrolib/unzoom_xy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ed49b9e67587bc8b0ceb54410e0de379a600331f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/unzoom_xy.pro
@@ -0,0 +1,82 @@
+pro unzoom_xy,xtv,ytv,xim,yim,OFFSET=offset, ZOOM = zoom
+;+
+; NAME:
+;      UNZOOM_XY
+; PURPOSE:
+;      Converts X, Y position on the image display to the the X,Y position 
+;      on the corresponding data array.  (These  positions are identical 
+;      only for an unroamed, unzoomed image with with pixel [0,0] of the 
+;      image placed at position [0,0] on the image display.)
+;
+; CALLING SEQUENCE:
+;      UNZoom_XY, Xtv,Ytv,Xim,Yim, [ OFFSET =, ZOOM = ]   
+;
+; INPUTS:
+;      XTV - Scalar or vector giving X position(s) as read on the image
+;            display (e.g. with Cursor, Xtv, Ytv,/DEVICE)
+;      YTV - Scalar or vector giving Y position(s) on the image display.
+;
+;      If only 2 parameters are supplied then XTV and YTV will be modified
+;      on output to contain the image array coordinates.
+;
+; OPTIONAL KEYWORD INPUT:
+;      OFFSET - 2 element vector giving the location of the image pixel [0,0] 
+;               on the window display.   OFFSET can be positive (e.g if the 
+;               image is centered in a larger window) or negative (e.g. if the
+;               only the central region of an image much larger than the window
+;               is being displayed. 
+;               Default value is [0,0], or no offset.
+;      ZOOM - scalar giving the ratio of the size on the image display to the
+;             original data size.     There is no capability for separate X 
+;             and Y zoom.   Default = 1.
+; OUTPUTS:
+;      XIM,YIM - X and Y coordinates of the image corresponding to the
+;            cursor position on the image display.
+; COMMON BLOCKS:
+;       If present, ZOOM_XY will use the TV and IMAGE common blocks which are
+;       defined in the MOUSSE software system (see 
+;        http://archive.stsci.edu/uit/analysis.html)   If the user is not using
+;       the MOUSSE software (which keeps track of the offset and zoom in each
+;       window) then the common blocks are ignored.
+; NOTES:
+;       The integer value of a pixel is assumed to refer to the *center*
+;       of a pixel.
+; REVISON HISTORY:
+;       Adapted from MOUSSE procedure  W. Landsman       March 1996
+;       Proper handling of offset option          S. Ott/W. Landsman May 2000
+;       Put back common blocks for MOUSSE compatibility    September 2004
+;       Fix algorithm for non-unity ZOOM values  Aug. 2013
+;-
+
+ On_error,2
+ Compile_opt idl2
+ common tv,chan,czoom,xroam,yroam
+ common images,x00,y00,xsize,ysize 
+
+ if N_params() LT 2 then begin
+        print,'Syntax - UNZOOM_XY, xtv, ytv, xim, yim, [OFFSET= ,ZOOM = ]'
+        return
+ endif
+
+    
+ if N_elements(offset) NE 2 then begin
+;Determine if Images common block defined
+      if N_elements(x00) eq 0 then offset = [0,0] $ 
+                              else offset = [x00[chan],y00[chan]]
+ endif
+ if N_elements(zoom) NE 1 then begin 
+          if N_elements(czoom) GT 0 then zoom = czoom[chan] else $
+             zoom = 1
+ endif
+
+
+ cen =  (zoom-1)/2.
+ xim =  float((xtv-cen)/zoom) - offset[0]
+ yim =  float((ytv-cen)/zoom) - offset[1]
+ if N_Params() LT 3 then begin
+   xtv = xim & ytv = yim
+ endif
+
+return
+end                                    
+
diff --git a/Code/script_idl_mv/astrolib/update_distort.pro b/Code/script_idl_mv/astrolib/update_distort.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1c84b1a0f2c56d7af3496daeca96ffaafcade888
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/update_distort.pro
@@ -0,0 +1,78 @@
+pro update_distort, distort, xcoeff, ycoeff
+;+
+; NAME:
+;    UPDATE_DISTORT
+; PURPOSE:
+;    Update SIP nonlinear distortion coefficients for a linear transformation
+; EXPLANATION:
+;    The SIP coefficients can account for nonlinearities in the astrometry
+;    of an astronomical image.    When the image is compressed or expanded
+;    these coefficients must be adjusted in a nonlinear way.
+; CALLING SEQUENCE:
+;    UPDATE_DISTORT, distort, xcoeff, ycoeff
+; INPUT/OUTPUT:
+;    distort - structure giving SIP coefficients.    See extast.pro for 
+;             description of the SIP distortion structure
+;    xcoeff - 2 element numeric vector describing the linear transformation
+;              xp = xcoeff[0]*x + xcoeff[1]
+;    xcoeff - 2 element numeric vector describing the linear transformation
+;              yp = ycoeff[0]*x + ycoeff[1]
+;
+; METHOD:
+;     The procedure TRANSFORM_COEFF is  used to determine how the
+;     coefficients change under the linear transformation.
+;
+;     See example of usage in hrebin.pro
+; REVISION HISTORY:
+;     Written, December 2007            W. Landsman
+;-
+ compile_opt idl2
+ On_error,2
+ if N_params() LT 3 then begin 
+    print,'Syntax - UPDATE_DISTORT, distort, xcoeff, ycoeff'
+    return
+    endif
+    
+ a = distort.a
+ b = distort.b
+ a_sz = size(a,/dimen)
+
+ for i=0,a_sz[0] - 1 do begin
+     a[0,i] = transform_coeff(a[*,i], xcoeff[0], xcoeff[1] )
+     b[0,i] = transform_coeff(b[*,i], xcoeff[0], xcoeff[1] )
+ endfor     
+
+ a = transpose(a)
+ b = transpose(b)
+ for i=0,a_sz[1] - 1 do begin
+     a[0,i] = transform_coeff(a[*,i], ycoeff[0], ycoeff[1] )
+     b[0,i] = transform_coeff(b[*,i], ycoeff[0], ycoeff[1] )
+ endfor
+ distort.a = transpose(a)/xcoeff[0]
+ distort.b = transpose(b)/ycoeff[0]
+
+ if N_elements(distort.ap) GT 1 then begin 
+
+ ap = distort.ap
+ bp = distort.bp
+ ap_sz = size(ap,/dimen)
+
+ for i=0,ap_sz[0] - 1 do begin
+     ap[0,i] = transform_coeff(ap[*,i], xcoeff[0], xcoeff[1] )
+     bp[0,i] = transform_coeff(bp[*,i], xcoeff[0], xcoeff[1] )
+ endfor     
+
+ ap = transpose(ap)
+ bp = transpose(bp)
+ for i=0,ap_sz[1] - 1 do begin
+     ap[0,i] = transform_coeff(ap[*,i], ycoeff[0], ycoeff[1] )
+     bp[0,i] = transform_coeff(bp[*,i], ycoeff[0], ycoeff[1] )
+ endfor
+ distort.ap = transpose(ap)/xcoeff[0]
+ distort.bp = transpose(bp)/ycoeff[0]
+
+ endif
+
+ return
+ end
+
diff --git a/Code/script_idl_mv/astrolib/uvbybeta.pro b/Code/script_idl_mv/astrolib/uvbybeta.pro
new file mode 100644
index 0000000000000000000000000000000000000000..45c93797af5dcc7b9db70338c316dc419aef022d
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/uvbybeta.pro
@@ -0,0 +1,488 @@
+pro uvbybeta,xby,xm1,xc1,xHbeta,xn,Te,MV,eby,delm0,radius,TEXTOUT=textout, $
+    eby_in = eby_in, name = name, prompt=prompt,print=print
+;+
+; NAME:
+;       UVBYBETA
+; PURPOSE:
+;       Derive dereddened colors, metallicity, and Teff from Stromgren colors.
+; EXPLANATION:
+;       Adapted from FORTRAN routine of same name published by T.T. Moon, 
+;       Communications of University of London Observatory, No. 78. Parameters 
+;       can either be input interactively (with /PROMPT keyword) or supplied 
+;       directly.   
+;
+; CALLING SEQUENCE:
+;       uvbybeta, /PROMPT               ;Prompt for all parameters
+;       uvbybeta,by,m1,c1,Hbeta,n        ;Supply inputs, print outputs
+;       uvbybeta, by, m1, c1, Hbeta, n, Te, Mv, Eby, delm0, radius, 
+;                       [ TEXTOUT=, Eby_in =, Name =  ]
+;
+; INPUTS:
+;       by - Stromgren b-y color, scalar or vector
+;       m1 - Stromgren line-blanketing parameter, scalar or vector
+;       c1 - Stromgren Balmer discontinuity parameter, scalar or vector
+;       Hbeta - H-beta line strength index.  Set  Hbeta to 0 if it is not 
+;            known, and UVBYBETA will estimate a value based on by, m1,and c1.
+;            Hbeta is not used for stars in group 8.
+;       n -  Integer (1-8), scalar or vector,  giving approximate stellar 
+;            classification
+;
+;       (1) B0 - A0, classes III - V, 2.59 < Hbeta < 2.88,-0.20 <   c0  < 1.00
+;       (2) B0 - A0, class   Ia     , 2.52 < Hbeta < 2.59,-0.15 <   c0  < 0.40
+;       (3) B0 - A0, class   Ib     , 2.56 < Hbeta < 2.61,-0.10 <   c0  < 0.50
+;       (4) B0 - A0, class   II     , 2.58 < Hbeta < 2.63,-0.10 <   c0  < 0.10
+;       (5) A0 - A3, classes III - V, 2.87 < Hbeta < 2.93,-0.01 < (b-y)o< 0.06
+;       (6) A3 - F0, classes III - V, 2.72 < Hbeta < 2.88, 0.05 < (b-y)o< 0.22
+;       (7) F1 - G2, classes III - V, 2.60 < Hbeta < 2.72, 0.22 < (b-y)o< 0.39
+;       (8) G2 - M2, classes  IV _ V, 0.20 < m0   < 0.76, 0.39 < (b-y)o< 1.00
+;
+;
+; OPTIONAL INPUT KEYWORD:
+;       Eby_in - numeric scalar specifying E(b-y) color to use.   If not
+;             supplied, then E(b-y) will be estimated from the Stromgren colors
+;       NAME - scalar or vector string giving name(s) of star(s).  Used only 
+;               when writing to  disk for identification purposes.
+;       /PROMPT - if set, then uvbybeta.pro will prompt for Stromgren indicies
+;                interactively
+;       TEXTOUT  -  Used to determine output device.  If not present, the
+;               value of the !TEXTOUT system variable is used (see TEXTOPEN)
+;               textout=1       Terminal with /MORE (if a tty)
+;               textout=2       Terminal without /MORE
+;               textout=3       uvbybeta.prt   (output file)
+;               textout=4       Laser Printer 
+;               textout=5       User must open file         
+;               textout=7       Append to existing uvbybeta.prt file
+;               textout = filename (default extension of .prt)
+;      /PRINT - if set, then force display output information to the device 
+;               specified by !TEXTOUT.    By default, UVBYBETA does not display
+;               information if output variables are supplied (and TEXTOUT is
+;               not set). 
+;
+; OPTIONAL OUTPUTS:
+;       Te - approximate effective temperature
+;       MV - absolute visible magnitude
+;       Eby - Color excess E(b-y)
+;       delm0 - metallicity index, delta m0, (may not be calculable for early
+;               B stars).
+;       radius - Stellar radius (R/R(solar))
+; EXAMPLE:
+;       Suppose 5 stars have the following Stromgren parameters
+;
+;       by = [-0.001 ,0.403, 0.244, 0.216, 0.394 ]
+;       m1 = [0.105, -0.074, -0.053, 0.167, 0.186 ]
+;       c1 = [0.647, 0.215, 0.051, 0.785, 0.362] 
+;       hbeta = [2.75, 2.552, 2.568, 2.743, 0 ]
+;       nn = [1,2,3,7,8]              ;Processing group number
+;
+;       Determine stellar parameters and write to a file uvbybeta.prt
+;       IDL> uvbybeta, by,m1,c1,hbeta, nn, t=3
+;            ==> E(b-y) = 0.050    0.414   0.283  0.023  -0.025
+;                Teff =   13060    14030   18420  7250    5760
+;                M_V =    -0.27    -6.91   -5.94  2.23    3.94
+;                radius=  2.71     73.51    39.84 2.02    1.53
+; SYSTEM VARIABLES:
+;       The non-standard system variables !TEXTOUT and !TEXTUNIT will be  
+;       automatically defined if they are not already present.   
+;
+;       DEFSYSV,'!TEXTOUT',1
+;       DEFSYSV,'!TEXTUNIT',0
+;
+; NOTES:
+;       (1) **This procedure underwent a major revision in January 2002
+;       and the new calling sequence may not be compatible with the old** (NAME
+;       is now a keyword rather than a parameter.)
+;
+;       (2) Napiwotzki et al. (1993, A&A, 268, 653) have written a FORTRAN
+;           program that updates some of the Moon (1985) calibrations.  These
+;           updates are *not* included in this IDL procedure.
+; PROCEDURES USED:
+;       DEREDD, TEXTOPEN, TEXTCLOSE
+; REVISION HISTORY:                                           
+;       W. Landsman          IDL coding              February, 1988
+;       Keyword textout added, J. Isensee, July, 1990
+;       Made some constants floating point.   W. Landsman    April, 1994
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Added Eby_in, /PROMPT keywords, make NAME a keyword and not a parameter
+;                 W. Landsman      January 2002
+;-
+ npar = N_params()
+ if (npar EQ 0) and ( not keyword_set(PROMPT)) then begin
+     print,'Syntax - UVBYBETA, by, m1, c1, beta, n,     ;Input parameters'
+     print,'                   Te,MV,eby,delm0,radius   ;Output parameters'
+     print,'Input Keywords: Eby_in=, /PROMPT, NAME=, TEXTOUT ='
+     return
+ endif
+
+ defsysv,'!textout',exists = i
+ if i EQ 0 then astrolib
+
+ if N_elements( TEXTOUT ) EQ 0 then textout = !TEXTOUT  ;default output dev.
+ do_print =  (npar LT 6) || (TEXTOUT GT 2) || keyword_set(PRINT)
+
+ Rm1 = -0.33 & Rc1 = 0.19 & Rub = 1.53          ;Parameter values
+ init = 0
+
+ READ_PAR:  if keyword_set(PROMPT) then begin 
+  ans = ''
+  print,'Enter (b-y), m1, c1, and Hbeta in that order ([RETURN] to exit)'
+  read,ans
+  if ans eq '' then begin               ;Normal Exit
+    if ( init EQ 1 ) then textclose, TEXTOUT = textout
+    return 
+  endif else ans = getopt(ans)
+  if ( N_elements(ans) NE 4 ) then begin
+    message, 'INPUT ERROR - Expecting 4 scalar values', /CON
+    print, 'Enter 0.0 for Hbeta if it is not known: '
+    goto, READ_PAR 
+  endif else begin
+    xby = ans[0] & xm1 = ans[1] & xc1 = ans[2]  & xhbeta = ans[3]
+    endelse
+ endif 
+    
+ nstar  = N_elements(xby)
+ xub = xc1 + 2*(xm1+xby)
+ xflag1 = (xHbeta EQ 0.)
+
+ 
+ READ_GROUP:  if ( npar LT 5 )then begin
+
+   print,' The following group of stars are available'
+   print, $ 
+     '(1) B0 - A0, classes III - V, 2.59 < Hbeta < 2.88,-0.20 <   c0  < 1.00'
+   print, $
+     '(2) B0 - A0, class   Ia     , 2.52 < Hbeta < 2.59,-0.15 <   c0  < 0.40'
+   print, $
+     '(3) B0 - A0, class   Ib     , 2.56 < Hbeta < 2.61,-0.10 <   c0  < 0.50'
+   print, $ 
+     '(4) B0 - A0, class   II     , 2.58 < Hbeta < 2.63,-0.10 <   c0  < 0.10'
+   print, $ 
+     '(5) A0 - A3, classes III - V, 2.87 < Hbeta < 2.93,-0.01 < (b-y)o< 0.06'
+   print, $
+     '(6) A3 - F0, classes III - V, 2.72 < Hbeta < 2.88, 0.05 < (b-y)o< 0.22'
+   print,$ 
+     '(7) F1 - G2, classes III - V, 2.60 < Hbeta < 2.72, 0.22 < (b-y)o< 0.39'
+   print, $ 
+     '(8) G2 - M2, classes  IV _ V, 0.20 < m0   < 0.76, 0.39 < (b-y)o< 1.00'
+   xn = 0                   
+   read,'Enter group number to which star belongs: ',xn
+
+   if N_elements(name) Eq 0 then begin
+   if (TEXTOUT ne 1) and (npar lt 6) then begin ;Prompt for star name?
+     name = ''
+     read,'Enter name of star: ',name
+   endif
+   endif
+ endif
+
+ do_eby = N_elements(eby_in) EQ 0
+ te = fltarr(nstar) & MV = te & delm0 = te & radius = te
+ if N_elements(name) EQ 0 then name = strtrim( indgen(nstar)+1,2)
+ if not do_eby then eby = replicate(eby_in,nstar) else eby = te
+
+ for i=0,Nstar -1 do begin
+   by = xby[i] & m1 = xm1[i] & c1 = xc1[i] & hbeta = xhbeta[i] & n = fix(xn[i])
+   ub = xub[i] & flag1 = xflag1[i] 
+   flag2 = 0
+   warn = ''
+
+ case n of
+
+ 1: BEGIN
+
+;   For group 1, beta is a luminosity indicator and c0 is a temperature
+;   indicator. (u-b) is also a suitable temperature indicator.
+
+;   For dereddening a linear relation between the intrinsic (b-y)
+;   and (u-b) colors is used (Crawford 1978, AJ 83, 48)
+
+    if do_eby then Eby[i] = ( 13.608*by-ub+1.467 ) / (13.608-Rub)
+    DEREDD, Eby[i], by, m1, c1, ub, by0, m0, c0, ub0
+
+; If beta is not given it is estimated using a cubic fit to the
+; c0-beta relation for luminosity class V given in Crawford (1978).
+    IF flag1 EQ 1 then Hbeta = $
+                   poly(c0, [2.61033, 0.132557, 0.161463, -0.027352] )
+; Calculation of the absolute magnitude by applying the calibration
+; of Balona & Shobbrock (1974, MNRAS 211, 375)   
+   g = ALOG10(Hbeta - 2.515) - 1.6*ALOG10(c0 +0.322)
+   MV[i] = 3.4994 + 7.2026*ALOG10(Hbeta - 2.515) -2.3192*g + 2.9375*g^3
+   Te[i] = 5040/(0.2917*c0 + 0.2)  
+
+; The ZAMS value of m0 is calculated from a fit to the data of 
+; Crawford (1978), modified by Hilditch, Hill & Barnes (1983, 
+; MNRAS 204, 241)
+   m0zams = poly(c0, [0.07473, 0.109804, -0.139003, 0.0957758] )
+   delm0[i] = m0zams - m0
+   flag2 = 1
+   END
+
+ 2: BEGIN
+    if do_eby then begin
+; For dereddening the linear relations between c0 and (u-b)
+; determined from Zhang (1983, AJ 88, 825) is used.
+       Eub = ( 1.5*c1 - ub + 0.035) / (1.5/(Rub/Rc1)-1)
+       Eby[i] = Eub/Rub
+    endif
+    DEREDD, Eby[i], by, m1, c1, ub, by0, m0, c0, ub0
+    if ( flag1 EQ 1 ) then Hbeta = 0.037*c0 + 2.542
+    END
+
+ 3: BEGIN
+; For dereddening the linear relations between c0 and (u-b)
+; determined from Zhang (1983, AJ 88, 825) is used.
+    if do_Eby then begin
+       Eub = (1.36*c1-ub+0.004) / (1.36/(Rub/Rc1)-1)
+       Eby[i] = Eub/Rub
+    endif
+    DEREDD, Eby[i], by, m1, c1, ub, by0, m0, c0, ub0
+; If beta is not given it is derived from a fit of the c0-beta
+; relation of Zhang (1983).
+    if flag1 then Hbeta = 0.047*c0 +2.578
+    END
+
+ 4: BEGIN
+; For dereddening the linear relations between c0 and (u-b)
+; determined from Zhang (1983, AJ 88, 825) is used.
+    if do_Eby then begin
+       Eub = ( 1.32*c1 - ub - 0.056) / ( 1.32 / (Rub/Rc1)-1 )
+       Eby[i] = Eub/Rub
+    endif
+    DEREDD, Eby[i], by, m1, c1, ub, by0, m0, c0, ub0
+; If beta is not given it is derived from a fit of the c0-beta
+; relation of Zhang (1983).
+    if ( flag1 EQ 1 ) then Hbeta = 0.066*c0+2.59
+    END
+
+ 5: BEGIN
+; For group 5, the hydrogen Balmer lines are at maximum; hence two
+; new parameters, a0 = f{(b-y),(u-b)} and r = f{beta,[c1]} are defined
+; in order to calculate absolute magnitude and metallicity.
+
+    if do_eby then begin
+      m = m1 - Rm1*by
+      by0 = 4.2608*m^2 - 0.53921*m - 0.0235
+      REPEAT BEGIN
+         bycorr = by0
+         m0 = m1 - Rm1*(by-bycorr)
+         by0 = 14.0881*m0^2 - 3.36225*m0 + 0.175709
+      ENDREP UNTIL ( abs(bycorr - by0) LT 0.001)
+       Eby[i] = by - by0
+    endif
+    DEREDD, Eby[i], by, m1, c1, ub, by0, m0, c0, ub0
+    if flag1 eq 1 then Hbeta = 2.7905 - 0.6105*by + 0.5*m0 + 0.0355*c0
+    r = 0.35*(c1-Rc1*by) - (Hbeta-2.565)
+    a0 = by0+ 0.18*(ub0-1.36)
+; MV is calculated according to Stroemgren (1966, ARA&A 4, 433)
+; with corrections by Moon & Dworetsky (1984, Observatory 104, 273)
+    MV[i] = 1.5 + 6.0*a0 - 17.0*r
+    Te[i] =  5040. /(0.7536 *a0 +0.5282)
+    m0zams = -3.95105*by0^2 + 0.86888*by0 + 0.1598
+    delm0[i] = m0zams - m0
+   end
+
+ 6: begin
+    if flag1 then begin
+        warn = ' Estimate of Hbeta only valid if star is unreddened'
+        Hbeta = 3.06 - 1.221*by - 0.104*c1
+    endif
+    m1zams = -2.158*Hbeta^2 +12.26*Hbeta-17.209
+    if ( Hbeta LE 2.74 ) then begin
+
+        c1zams = 3.0*Hbeta - 7.56
+        MVzams = 22.14 - 7*Hbeta
+
+   endif else if ( ( Hbeta GT 2.74 ) and ( Hbeta LE 2.82 ) ) then begin
+
+        c1zams = 2.0*Hbeta - 4.82
+        MVzams = 11.16-3*Hbeta
+
+   endif else begin
+        c1zams = 2.0*Hbeta-4.83
+        MVzams =-88.4*Hbeta^2+497.2*Hbeta-696.41
+
+   endelse        
+   if do_eby then begin
+     delm1 = m1zams - m1
+     delc1 = c1-c1zams
+     if delm1 lt 0. then $
+          by0 = 2.946 - Hbeta - 0.1*delc1 - 0.25*delm1 else $
+          by0 = 2.946 - Hbeta - 0.1*delc1
+     Eby[i] = by - by0
+   endif 
+   Deredd, eby[i], by, m1, c1, ub, by0, m0, c0, ub0
+   delm0[i] = m1zams - m0
+   delc0 = c0 - c1zams
+   MV[i] = MVzams -9.0*delc0
+   Te[i] = 5040 / (0.771453*by0 + 0.546544)
+ end
+
+ 7: begin
+
+;  For group 7 c1 is the luminosity indicator for a particular beta,
+;  while beta {or (b-y)0} indicates temperature.
+;  Where beta is not available iteration is necessary to evaluate
+;  a corrected (b-y) from which beta is then estimated.
+
+   if flag1 then begin 
+        byinit = by
+        m1init = m1
+        for ii = 1,1000 do begin
+          m1by = 2.5*byinit^2 - 1.32*byinit + 0.345
+          bycorr = byinit + (m1by-m1init) / 2.0
+          if ( abs(bycorr-byinit) LE 0.0001 ) then goto,T71
+          byinit = bycorr
+          m1init = m1by
+        endfor
+        T71: Hbeta = 1.01425*bycorr^2 - 1.32861*bycorr + 2.96618 
+    endif
+
+; m1(ZAMS) and MV(ZAMS) are calculated according to Crawford (1975)
+;	with corrections suggested by Hilditch, Hill & Barnes (1983,
+;	MNRAS 204, 241) and Olson (1984, A&AS 57, 443).
+
+    m1zams = poly(Hbeta, [ 46.4167, -34.4538, 6.41701] )
+    MVzams = poly(Hbeta, [324.482, -188.748, 11.0494, 5.48012])
+
+;c1(ZAMS) calculated according to Crawford (1975)
+    if Hbeta le 2.65 then $
+        c1zams = 2*Hbeta - 4.91 else $
+        c1zams = 11.1555*Hbeta^2-56.9164*Hbeta+72.879
+
+     if do_eby then begin
+       delm1 = m1zams - m1
+       delc1 = c1 - c1zams
+       dbeta = 2.72 - Hbeta
+       by0 = 0.222+1.11*dbeta +2.7*dbeta^2-0.05*delc1-(0.1+3.6*dbeta)*delm1
+       Eby[i] = by - by0
+     endif
+     Deredd,Eby[i],by,m1,c1,ub,by0,m0,c0,ub0
+     delm0[i] = m1zams - m0
+     delc0 = c0 - c1zams
+     f = 9.0 + 20.0*dbeta
+     MV[i] = MVzams - f*delc0
+     Te[i] = 5040/(0.771453*by0 + 0.546544)
+ end
+
+ 8:   begin
+     if ( flag1 EQ 1 ) then flag1 = 2
+;  Dereddening is done using color-color relations derived from 
+;  Olson 1984, A&AS 57, 443)
+     if ( by LE 0.65 ) then $
+           Eby[i] = (5.8651*by - ub -0.8975) / (5.8651 - Rub) $
+
+     else if ( ( by GT 0.65 ) and ( by LT 0.79 ) ) then begin 
+
+           Eby[i] = (-0.7875*by - c1 +0.6585)/(-0.7875 - Rc1)
+           by0 = by - Eby[i]
+        if ( by0 LT 0.65 ) then $
+           Eby[i] = (5.8651*by - ub -0.8975) / (5.8651-Rub)
+
+     endif else begin 
+
+        Eby[i] = ( 0.5126*by - c1 - 0.3645 ) / (0.5126-Rc1)
+        by0 = by - Eby[i]
+        if ( by0 LT 0.79 ) then $ 
+                  Eby[i] = (-0.7875*by - c1 + 0.6585) / (-0.7875-Rc1)
+        by0  = by - Eby[i]
+        if ( by0 LT 0.65 ) then $ 
+                  Eby[i] = ( 5.8651*by - ub - 0.8975) / (5.8651-Rub)
+
+     endelse 
+    
+
+        DEREDD,Eby[i],by,m1,c1,ub,by0,m0,c0,ub0
+; m1(ZAMS), c1(ZAMS), and MV(ZAMS) are calculated according to Olson (1984)
+        m1zams = poly( by0, [7.18436, -49.43695, 122.1875, -122.466, 42.93678]) 
+         IF by0 lt 0.65 THEN BEGIN
+                c1zams = poly(by0, [3.78514, -21.278, 42.7486, -28.7056 ] )
+                MVzams =  $
+                  poly(by0, [-59.2095, 432.156, -1101.257, 1272.503, -552.48])
+        ENDIF ELSE IF (by0 GE 0.65) and (by0 lt 0.79) THEN BEGIN
+                c1zams = -0.631821*by0^2+0.116031*by0+0.33657
+                MVzams = 1.37632*by0^2 + 4.97911*by0+3.4305
+        ENDIF ELSE BEGIN
+                c1zams = -0.010028*by0^2 + 0.530426*by0 - 0.37237
+                MVzams =  1.18298*by0^2  + 3.92776*by0 + 4.37507
+        ENDELSE
+        delm0[i] = m1zams - m0
+        delc0 =c0 - c1zams
+; Teff and MV calibration of Olson (1984)
+        IF (by0 LE 0.505) THEN BEGIN
+                f = 10. - 80.*(by0-0.38)
+                Te[i] = 10^(-0.416*by0+3.924)
+        ENDIF ELSE BEGIN
+                f = 0.0
+                Te[i] = 10^(-0.341*by0+3.869)
+        ENDELSE
+        MV[i] = MVzams - f*delc0 + 3.2*delm0[i] - 0.07
+      END 
+ ELSE: BEGIN
+      print,'A stellar group of',n,' is not available'
+      npar = npar<4
+      goto, READ_GROUP 
+      end
+
+ endcase
+ if (n GE 2) and ( n LE 4 ) then begin
+; c0-beta relation for ZAMS stars according to Crawford (1978,
+; AJ 83, 48), modified by Hilditch, Hill & Barnes (1983, MNRAS 204, 241).
+     betaza = poly(c0, [2.62745, 0.228638, -0.099623, 0.277363, -0.160402 ] )
+     B = betaza - 2.5
+; MV(ZAMS) calculated according to Balona & Shobbrock (1984, MNRAS 211, 375)
+     MVzams =203.704*B^3 - 206.98*B^2 + 77.18*b - 9.563
+; MV is calculated from the d(beta)-d(MV) relation of Zhang (1983)
+     dbeta = betaza - Hbeta
+     dMV = -121.6*dbeta^2 +61.0*dbeta + 0.08
+     MV[i] = MVzams - dMV
+; Estimate of Teff by coupling the relations of Boehm-Vitense 
+; (1981, ARA&A 19, 295) and Zhang (1983)     
+     Te[i] = 5040 / (0.35866*ub0 + 0.27346)
+     flag2 = 2
+endif
+
+; Transformation according to the FV-(b-y)0 relation of Moon 
+; (1984, MNRAS 211, 21P)
+ if ( by0 LE 0.335 ) then $
+            FV = -6.759*by0^3 + 3.731*by0^2 - 1.092*by0 + 3.981 $
+       else FV = -0.534*by0 + 3.959
+ radius[i] = 10^(2.*(4.236-0.1*MV[i] - FV))
+  if do_print then begin
+ if ( flag2 EQ 2 )then metal = 'no delta(m0)' else metal = 'delta(m0) = '
+ Hbeta = round(Hbeta*1000)/1000.
+ Teff = long(round(Te[i]/10.)*10.)
+ if !TEXTUNIT eq 0 then textopen,'uvbybeta',textout=textout
+ init = 1                          ;First star has been done
+ printf,!TEXTUNIT,'        Star is: ',strtrim(name[i],2), $
+        '                Processed in group ' + strtrim(n,2) 
+ fmt = '(2x,A, f6.3,7x, A, f6.3, 10x,A, F6.3,A,F5.3)'
+ if strlen(warn) GT 0 then printf, !TEXTUNIT, warn
+ nohbeta = '      Hbeta is not used'
+
+ case flag1 of 
+    2: printf, !TEXTUNIT, 'b-y   = ',by, 'm1 = ', m1,'c1 = ',c1, f=fmt, $
+               nohbeta
+    1: printf, !TEXTUNIT, f = fmt, $
+       'b-y   = ',by, 'm1 = ', m1,'c1 = ',c1,' estimated Hbeta  = ', Hbeta 
+    0: printf,!TEXTUNIT, f = fmt, $
+       'b-y   = ',by, 'm1 = ', m1,'c1 = ',c1,'         Hbeta  = ', Hbeta 
+ endcase
+
+ fmt = '(1x,A, F6.3,7X, A,F6.3,10X,A,F6.3, 8x, A, F6.3,/)' 
+ printf,!TEXTUNIT,f=fmt, '(b-y)0 = ', by0, 'm0 = ',m0,'c0 = ', c0, $
+         'E(b-y) = ',Eby[i]
+
+ printf,!TEXTUNIT,form="(1X,'Absolute Magnitude (Mv) = ',F6.2,5x," + $
+       "'Radius  (R/R[solar]) = ',F7.2)",MV[i],radius[i]
+
+ fmt1 = "(1X,A12,25X,'Effective Temperature (Teff) = ',I5,1X,'K'//)"
+ fmt2 = "(1X,A12,F6.3,20X,'Effective Temperature (Teff) = ',I5,1X,'K'//)"
+
+ if ( flag2 EQ 2 ) then printf,!TEXTUNIT,form=fmt1,metal,Teff else  $
+                       printf,!TEXTUNIT,form=fmt2,metal,delm0[i],Teff
+
+ endif
+ endfor
+ if keyword_set(PROMPT) then goto, READ_PAR 
+ if do_print then textclose, textout = textout
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/vactoair.pro b/Code/script_idl_mv/astrolib/vactoair.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d0dc2a997f5d354f9023243d57e49bbfac1977dd
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/vactoair.pro
@@ -0,0 +1,68 @@
+pro vactoair,wave_vac, wave_air
+;+
+; NAME:
+;	VACTOAIR
+; PURPOSE:
+;	Convert vacuum wavelengths to air wavelengths
+; EXPLANATION:
+;	Corrects for the index of refraction of air under standard conditions.  
+;	Wavelength values below 2000 A will not be altered.  Accurate to 
+;	about 10 m/s.
+;
+; CALLING SEQUENCE:
+;	VACTOAIR, WAVE_VAC, [WAVE_AIR]
+;
+; INPUT/OUTPUT:
+;	WAVE_VAC - Vacuum Wavelength in Angstroms, scalar or vector
+;		If the second parameter is not supplied, then this will be
+;               updated on output to contain double precision air wavelengths.
+;
+; OPTIONAL OUTPUT:
+;        WAVE_AIR - Air wavelength in Angstroms, same number of elements as
+;                 WAVE_VAC, double precision
+;
+; EXAMPLE:
+;	If the vacuum wavelength is  W = 2000, then 
+;
+;	IDL> VACTOAIR, W 
+;
+;	yields an air wavelength of W = 1999.353 Angstroms
+;
+; METHOD:
+;	Formula from Ciddor 1996  Applied Optics , 35, 1566
+;
+; REVISION HISTORY
+;	Written, D. Lindler 1982 
+;	Documentation W. Landsman  Feb. 1989
+;       Use Ciddor (1996) formula for better accuracy in the infrared 
+;           Added optional output vector, W Landsman Mar 2011
+;-
+  On_error,2
+  compile_opt idl2
+  
+  if N_params() EQ 0 then begin
+     print,'Syntax - VACTOAIR, Wave_Vac, [Wave_Air]'
+     return
+  endif
+  
+    wave_air = double(wave_vac)
+    g = where(wave_vac GE 2000, Ng)     ;Only modify above 2000 A
+    
+    if Ng GT 0 then begin 
+ 
+    sigma2 = (1d4/double(wave_vac[g]) )^2.   ;Convert to wavenumber squared
+
+; Compute conversion factor
+
+  fact = 1.D +  5.792105D-2/(238.0185D0 - sigma2) + $
+                            1.67917D-3/( 57.362D0 - sigma2)
+    
+
+; Convert wavelengths
+
+  wave_air[g] = wave_vac[g]/fact
+  if N_Params() eq 1 then wave_vac = wave_air
+  endif 
+
+  return
+  end                        
diff --git a/Code/script_idl_mv/astrolib/valid_num.pro b/Code/script_idl_mv/astrolib/valid_num.pro
new file mode 100644
index 0000000000000000000000000000000000000000..05b2a205bc0bfd60f96c83525a50a68682e1c1be
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/valid_num.pro
@@ -0,0 +1,80 @@
+;+
+; NAME: 
+;     VALID_NUM()
+; PURPOSE:               
+;     Check if a string is a valid number representation.
+; EXPLANATION:              
+;     The input string is parsed for characters that may possibly
+;     form a valid number.  It is more robust than simply checking
+;     for an IDL conversion error because that allows strings such
+;     as '22.3qwert' to be returned as the valid number 22.3
+;
+;     This function had a major rewrite in August 2008 to use STREGEX
+;     and allow vector input.    It should be backwards compatible.
+; CALLING SEQUENCE: 
+;     IDL> status = valid_num(string  [,value]  [,/integer])
+;    
+; INPUTS:
+;     string  -  the string to be tested, scalar or array
+;               
+; RETURNS
+;     status - byte scalar or array, same size as the input string
+;              set to 1 where the string is a  valid number, 0 for invalid
+; OPTIONAL OUTPUT:               
+;     value     - The value the string decodes to, same size as input string.
+;           This will be returned as a double precision number unless 
+;           /INTEGER is present, in which case a long integer is returned.
+;           
+; OPTIONAL INPUT KEYWORD:          
+;    /INTEGER   -  if present code checks specifically for an integer.
+; EXAMPLES:
+;     (1) IDL> print,valid_num(3.2,/integer) 
+;        --> 0     ;Since 3.2 is not an integer 
+;     (2) IDL> str =['-0.03','2.3g', '3.2e12']
+;         IDL> test = valid_num(str,val)
+;              test = [1,0,1]    &  val =  [-0.030000000 ,NaN ,3.2000000e+12]
+; REVISION HISTORY:
+;          Version 1, C D Pike, RAL, 24-May-93
+;          Version 2, William Thompson, GSFC, 14 October 1994
+;                       Added optional output parameter VALUE to allow
+;                       VALID_NUM to replace STRNUMBER in FITS routines.
+;          Version 3 Wayne Landsman rewrite to use STREGEX, vectorize
+;          Version 4 W.L. (fix from C. Markwardt) Better Stregex expression, 
+;                    was missing numbers like '134.' before Jan 1 2010
+;-            
+
+FUNCTION valid_num, string, value, INTEGER=integer
+ On_error,2
+ compile_opt idl2 
+ 
+; A derivation of the regular expressions below can be found on 
+; http://wiki.tcl.tk/989
+
+   if keyword_set(INTEGER) then $ 
+    st = '^[-+]?[0-9][0-9]*$'  else $                    ;Integer
+     st = '^[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)([eEdD][-+]?[0-9]+)?$' ;F.P.
+   
+;Simple return if we just need a boolean test.
+    if N_params() EQ 1 then return, stregex(strtrim(string,2),st,/boolean)
+
+   
+      vv = stregex(strtrim(string,2),st,/boolean)      
+      if size(string,/N_dimen) EQ 0 then begin     ;Scalar
+         if vv then $
+            value= keyword_set(integer) ? long(string) : double(string) 
+      endif else begin                             ;Array 
+         
+      g = where(vv,Ng)
+      if Ng GT 0 then begin      ;Need to create output vector
+        if keyword_set(integer) then begin 
+              value = vv*0L 
+              value[g] = long(string[g])
+        endif else begin 
+                value = replicate(!VALUES.D_NAN,N_elements(vv))
+                value[g] = double(string[g])
+        endelse 
+        endif   
+        endelse 
+     
+       return,vv
+      end
diff --git a/Code/script_idl_mv/astrolib/vect.pro b/Code/script_idl_mv/astrolib/vect.pro
new file mode 100644
index 0000000000000000000000000000000000000000..1990abc291ee78d96f55ca4f22d7129ce84797c6
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/vect.pro
@@ -0,0 +1,61 @@
+function VECT,vctr,form,Format=Format,delim=delim
+;+
+; NAME:
+;	VECT
+; PURPOSE:
+;	Print a set of numbers as a string with delimiters included
+; EXPLANATION:
+;	This function returns the given vector in parenthesized coordinates
+;	as in the form (X,Y).  No limit on the number of dimensions.  Also
+;	note that the vector does not need to be numbers.  It may also be a
+;	string vector.  e.g. ['X','Y']
+;
+; CALLING SEQEUNCE:
+;	tmp = VECT( vctr, [ form, FORMAT = , DELIM =  ] )
+; INPUT:
+;	VCTR      The vector to be displayed  e.g. [56,44]
+;
+; OPTIONAL KEYWORD INPUT:
+;	FORMAT    This KEYWORD allows the specification of a format for the
+;		elements.  e.g.: VECT([2,3],format='(f7.1)') gives '(2.0,3.0)'
+;	DELIM     This KEYWORD specifies the delimeter.  The default is ',' but
+;		other useful examples might be ', ' or ':'
+;
+; OPTIONAL INPUT
+;	FORM      This parameter may be used instead of the keyword FORMAT
+;
+; OUTPUT:
+;	tmp       A returned string of the parenthesized vector
+;
+; Other Procedures/Functions Called:
+;	STRN
+;
+; HISTORY:
+;	03-JUL-90 Version 1 written by Eric W. Deutsch
+;	24-AUG-91 Format='' keyword added (E. Deutsch)
+;	29-AUG-91 FORM parameter added (E. Deutsch)
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;-
+
+  if (n_params(0) lt 1) then begin
+    print,'Call: IDL> stringvar=VECT(vector,[FORMAT],[FORMAT=])'
+    print,"e.g.: IDL> tmp=VECT([512,512]) & print,'Center: ',tmp"
+    return,''
+    endif
+  if (n_params(0) lt 2) then FORM=''
+  if (n_elements(vctr) lt 1) then return,''
+  if (n_elements(Format) eq 0) then Format=''
+  if (n_elements(delim) eq 0) then delim=','
+  if (FORM ne '') then Format=FORM
+
+  tmp='('
+  for i=0,n_elements(vctr)-1 do begin
+    sep=delim
+    if (i eq 0) then sep=''
+    if (Format eq '') then tmp=tmp+sep+strn(vctr[i]) $
+    else tmp=tmp+sep+strn(vctr[i],Format=Format)
+    endfor
+  tmp=tmp+')'
+
+  return,tmp
+end
diff --git a/Code/script_idl_mv/astrolib/vsym.pro b/Code/script_idl_mv/astrolib/vsym.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9f78c032106fad27dd5050e7633c04d5294bfa85
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/vsym.pro
@@ -0,0 +1,98 @@
+PRO VSYM, Nvert, STAR=star, SKELETON=skeleton, POLYGON=polygon, $
+    FILL=fill, ROT=rot, THICK=thick
+
+;+
+; NAME:
+;       VSYM
+;
+; PURPOSE:
+;       Create "Mongo"-like polygonal plot symbols
+; EXPLANATION:
+;       This procedure generates a subset of Mongo-like plot symbols.
+;       The symbols are the rotationally symmetric ones that have
+;       a specified number of vertices and are either open or filled.
+;       (The half-filled symbols are not included.)     After defining the
+;       plot symbol with VSYM, make the call to PLOT (or PLOTS or OPLOT) with 
+;       PSYM=8.
+;
+; CATEGORY:
+;       Graphics
+;
+; CALLING SEQUENCE:
+;       VSYM, Nvert
+;
+; INPUT POSITIONAL PARAMETERS:
+;       Nvert:     Number of vertices in plot symbol.  Maximum value
+;                  used is 24.
+;
+; INPUT KEYWORD PARAMETERS:
+;       STAR:      Set this flag to get a star.  E.g., 
+;                  vsym, 5,/star gets you a pentagram.
+;       SKELETON:  Set this flag to get an asterisk-like symbol, where
+;                  the center is connected to each vertex.  E.g.,
+;                  vsym, 4, /skel gets you an X.
+;       POLYGON:   Set this flag to get a regular polygon.  This is
+;                  the default symbol type.
+;       FILL:      Set this flag to get filled symbol.  Default=open
+;       ROT:       Rotation of symbol about center, in degrees.
+;                  E.g., vsym, 4, rot=45 gets you a diamond, whereas
+;                  vsym, 4 gets you a square.
+;       THICK:     Line thickness of symbol.  Default=!P.thick
+;
+; MODIFICATION HISTORY:
+;       Written by:     R. S. Hill, RITSS, 2 Oct 98
+;-
+
+On_error, 0
+
+IF n_elements(nvert) LT 1 THEN nvert=4
+
+IF nvert GT 24 THEN $
+    message,/info,'More than 24 vertices requested; 24 used'
+
+nv = nvert < 24
+vangle = (nv-2.)/nv*180.
+
+st = keyword_set(star)
+sk = keyword_set(skeleton)
+po = keyword_set(polygon)
+fi = keyword_set(fill)
+rt = keyword_set(rot)
+
+IF n_elements(thick) LT 1 THEN thick=!P.thick
+
+rot_zero = -0.5*vangle
+if rt then rot_zero = rot_zero + 180./nvert
+
+IF st + sk + po GT 1 THEN message, 'More than one symbol type specified'
+IF st + sk + po EQ 0 THEN po=1
+
+angles = indgen(nv+1)/float(nv) * 2 * !pi + rot_zero/180.0*!pi
+x = cos(angles) & y = sin(angles)
+
+inv2 = indgen(nv+1)*2
+inv2_1 = indgen(nv)*2 + 1
+
+IF po THEN BEGIN
+    usersym, x, y, fill=fi, thick=thick
+ENDIF ELSE IF sk THEN BEGIN
+    xx = fltarr(2*nv+1) & yy = xx
+    xx[inv2] = x
+    yy[inv2] = y 
+    usersym, xx, yy, thick=thick
+ENDIF ELSE IF st THEN BEGIN
+    rot2 = rot_zero + 180./nv
+    inner_angles = $
+        indgen(nv)/float(nv) * 2 * !pi + rot2/180.0*!pi
+    inner_x = cos(inner_angles)*0.32
+    inner_y = sin(inner_angles)*0.32
+    xx = fltarr(2*nv+1) & yy = xx
+    xx[inv2] = x
+    xx[inv2_1] = inner_x
+    yy[inv2] = y
+    yy[inv2_1] = inner_y    
+    usersym, xx, yy, fill=fi, thick=thick
+ENDIF
+
+RETURN
+END
diff --git a/Code/script_idl_mv/astrolib/wcs_check_ctype.pro b/Code/script_idl_mv/astrolib/wcs_check_ctype.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b613c9d91437a543e72354a0833dfb0448614c47
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/wcs_check_ctype.pro
@@ -0,0 +1,153 @@
+PRO wcs_check_ctype, ctype, projection_type, coord_type
+;+
+; NAME:
+;     WCS_CHECK_CTYPE
+; PURPOSE:
+;     Checks that a pair of CTYPE parameters conform to WCS format and return
+;     the projection type and coordinate type extracted from them.
+;
+; EXPLANATION:
+;
+;     Stops with an error message if CTYPE does not conform to standard,
+;     unless one or both CTYPE strings is missing.
+;
+;     If only CTYPE[0] is present, and is valid, this counts as a
+;     "pass".
+;
+;     If ctype is unset, returns silently, with coord_type = 'X' and
+;     projection_type = 'DEF'.
+;
+;     Low-level procedure extracted from WCSXY2SPH & WCSSPH2XY to reduce code 
+;     duplication. 
+;
+; CATEGORY:
+;     Mapping and Auxiliary FITS Routine
+;
+; CALLING SEQUENCE:
+;      wcs_check_ctype, ctype, projection_type, [coord_type]
+;
+; INPUT PARAMETERS:
+;     ctype  - astrometry-related CTYPE strings extracted from the header.
+;
+; OUTPUT PARAMETERS:
+;     projection_type - three-character code specifying map projection.
+;                       If ctype is not specified returns 'DEF' for default.
+;     coord_type -      one- or two-character code specifying the coordinate
+;                       type, 'X' (unknown) if not specified. 'C' for RA & Dec. 
+;
+; NOTES:
+;       The conventions followed here check consistency with
+;       "Representations of Celestial Coordinates in FITS" by Calabretta
+;       and  Greisen (2002, A&A, 395, 1077; also see
+;       http://fits.gsfc.nasa.gov/fits_wcs.html).
+;
+; PROCEDURE:
+;       Astrometry CTYPEs should come in longitude and latitude pairs in one
+;       of three formats: 'RA---xxx' & 'DEC--xxx', 'yLON-xxx' & 'yLAT-xxx', or
+;       'zzLN-xxx' & 'zzLT-xxx' where xxx is the projection code and y or zz
+;       specify the type of the latitude & longitude axes, e.g. Galactic,
+;       Ecliptic etc. If the CTYPE pair is in this format, xxx is returned as
+;       the projection type.
+;
+; COMMON BLOCKS:
+;       none
+;
+; PROCEDURES CALLED:
+;       none
+;
+; AUTHOR:
+;
+;       J. P. Leahy
+;
+; MODIFICATIONS/REVISION LEVEL:
+;
+;       1.0     Jul 2013 Extracted from WCSXY2SPH & WCSSPH2XY
+;       1.1     Aug 2013 Now does actually stop if error detected.
+;       1.2     Jan 2014 Recognize when RA, DEC reversed, W. Landsman
+;-
+COMPILE_OPT IDL2, hidden
+ON_ERROR, 1
+
+projection_type = 'DEF'
+coord_type = 'X'
+coord_form1 = 0
+IF N_elements( ctype ) GE 1 THEN BEGIN
+    ctype1 = strtrim(ctype[0],2)
+    if strlen(ctype1) LT 8 then $
+      message,'ERROR - ' + strupcase(ctype1) + $
+      ' is not a valid spherical projection type.'
+    projection_type = STRUPCASE(STRMID(ctype1,5,3))
+    coord = STRUPCASE(STRMID(ctype1,0,4))
+    coord_tail = STRMID(coord,2,2)
+    bad_coord = 0B
+    CASE coord_tail OF
+        '--': BEGIN
+            coord_form1 = 1
+            bad_coord = coord NE 'RA--'
+            coord_type = 'C'
+        END
+        'ON': BEGIN
+            coord_form1 = 2
+            bad_coord = STRMID(coord,1,3) NE 'LON'
+            coord_type = STRMID(coord,0,1)
+        END
+        'LN': BEGIN
+            coord_form1 = 3
+            coord_type = STRMID(coord,0,2)
+        END
+	'C-': BEGIN
+	     coord_form1 = 1
+	     bad_coord = coord NE 'DEC-'
+	     coord_type = 'C'
+	     END
+        ELSE: bad_coord = 1B
+    ENDCASE
+   
+    IF bad_coord THEN BEGIN
+        MESSAGE, 'Unrecognised first coordinate type:' +  coord, /continue
+        MESSAGE, 'Should be ''RA--'' or ''xLON'' or ''xxLN'''
+    ENDIF
+
+    IF N_elements( ctype ) GE 2 THEN BEGIN
+        ctype2 = ctype[1]
+        if (projection_type ne STRUPCASE(STRMID(ctype2,5,3))) then begin
+          message,'The same map projection type must be in characters',/continue
+          message,'    5-8 of both CTYPE1 and CTYPE2.'
+        endif
+        coord = STRUPCASE(STRMID(ctype2,0,4))
+        coord_tail = STRMID(coord,2,2)
+        CASE coord_tail OF
+            'C-': BEGIN
+                bad_coord = coord NE 'DEC-'
+                coord_form2 = 1
+                coord_head2='C'
+            END
+	    '--': BEGIN
+            coord_form2 = 1
+            bad_coord = coord NE 'RA--'
+            coord_head2 = 'C'
+             END
+
+            'AT': BEGIN
+                bad_coord = STRMID(coord,1,3) NE 'LAT'
+                coord_head2 = STRMID(coord,0,1)
+                coord_form2 = 2
+            END
+            'LT': BEGIN
+                coord_head2 = STRMID(coord,0,2)
+                coord_form2 = 3
+            END  
+            ELSE: bad_coord = 1B
+        ENDCASE
+        IF bad_coord THEN BEGIN
+            MESSAGE, 'Unrecognised second coordinate type:' + coord, /CONTINUE
+            MESSAGE, 'Should be ''DEC-'' or ''xLAT'' or ''xxLT'''
+        ENDIF
+        if (coord_form1 NE coord_form2 || coord_type NE coord_head2) then begin
+           message,'The same standard system must be in the first 4', /continue
+           message,'characters of both CTYPE1 and CTYPE2.'
+        endif
+    ENDIF
+ENDIF
+END
+
diff --git a/Code/script_idl_mv/astrolib/wcs_demo.pro b/Code/script_idl_mv/astrolib/wcs_demo.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d74517627e26b31e825992af4f03c5c898d3631e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/wcs_demo.pro
@@ -0,0 +1,1198 @@
+;+
+; NAME:
+;       WCS_DEMO
+;
+; PURPOSE:
+;       Demonstrate the basic capabilities of procedures WCSSPH2XY & WCSXY2SPH
+;
+; CATEGORY:
+;       Mapping and Auxilary FITS Demo Routine
+;
+; CALLING SEQUENCE:
+;
+;       .run wcs_demo: compiles wcs_demo and the supporting demo routines
+;       wcs_demo: run the demo
+;
+; INPUT PARAMETERS:
+;
+;       none
+;
+; OUTPUT PARAMETERS:
+;       none
+;
+; PROCEDURE:
+;
+;       This is a demo program which is meant to call the routines 
+;       wcssph2xy.pro and wcsxy2sph.pro.  Since the purpose of this
+;       routine is both to show what the routines can do and what the
+;       user has to do, a file is created with all of the commands 
+;       needed to complete the desired operation.  Wcs_demo actually 
+;       executes this command file, so the user can exactly duplicate
+;       the results by simply re-executing this file.  Also, this 
+;       allows a user to edit an already existing file which calls 
+;       wcssph2xy.pro and wcsxy2sph.pro properly and extend the file's
+;       usefulness.  This demo program allows several possible tests.
+;       The first option is to simply draw a grid of evenly spaced
+;       latitude and longitude lines in a particular map transformation.
+;       Another possibility is to do a full loop, creating a Cartesian
+;       grid of latitude and longitude lines and calling wcssph2xy.pro
+;       to convert them to a particular map.  Then, wcsxy2sph.pro is
+;       called to invert the process and the difference between the
+;       original and final latitudes and longitudes can be plotted.
+;       This allows one to assess the level of the numerical errors
+;       introduced by the mapping routines.  A third possible option is to
+;       look at some of the map transformations and include rotations of
+;       the reference points so that a different perspective is given.
+;
+; COMMON BLOCKS:
+;       none
+;
+; PROCEDURES CALLED:
+;       SPHDIST(), WCSXY2SPH, WCSSPH2XY
+; COPYRIGHT NOTICE:
+;
+;       Copyright 1991, The Regents of the University of California. This
+;       software was produced under U.S. Government contract (W-7405-ENG-36)
+;       by Los Alamos National Laboratory, which is operated by the
+;       University of California for the U.S. Department of Energy.
+;       The U.S. Government is licensed to use, reproduce, and distribute
+;       this software. Neither the Government nor the University makes
+;       any warranty, express or implied, or assumes any liability or
+;       responsibility for the use of this software.
+;
+; AUTHOR:
+;
+;       Rick Balsano
+;
+; MODIFICATIONS/REVISION LEVEL:
+;
+;       1.1     8/31/93
+;       1.2     3/19/96 - J. Bloch - LANL
+;                        - Made compatible with wcslib-2.2 by Calabretta.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Updated for conical projections W. Landsman  July 2003
+;-
+
+; PROCEDURE FOR OPTION 1
+pro wcssph2xy_plot,file_unit,map,param1,param2
+printf,file_unit,";PLOTTING"
+printf,file_unit,"; Plot the resulting map."
+if ((map ge 0) and (map le 22)) then begin
+  ; For all but the spherical cube projections, simply plot the results from
+  ; wcssph2xy.pro as is.
+  printf,file_unit,"xdelta = (max(xx) - min(xx))/20"
+  printf,file_unit,"ydelta = (max(y) - min(y))/20"
+  printf,file_unit,$
+         "plot,xx,y,psym = 3,xrange = [min(xx) - xdelta,max(xx) + xdelta],$"
+  printf,file_unit,$
+         "yrange = [min(y) - ydelta,max(y) + ydelta],xstyle = 4,ystyle = 4"
+
+  ; ZENITHAL PROJECTIONS.
+  if ((map ge 1) and (map le 8)) then begin
+
+    printf,file_unit,""
+    printf,file_unit,$
+    "; Only connect latitude lines in a full circle if the longitude"
+    printf,file_unit,"; values cover the full circle."
+    printf,file_unit,$
+      "if (360 - abs(longitude(0,0) - longitude(n_elements(xx[*,0])-1)) $"
+    printf,file_unit,"                             le lon_spacing) $"
+    printf,file_unit,$
+      "then for i = 0,num_lat - 1 do oplot,[xx[*,i],xx(0,i)],[y[*,i],y(0,i)] $"
+    printf,file_unit,"else for i = 0,num_lat - 1 do oplot,xx[*,i],y[*,i]"
+
+    printf,file_unit,""
+    printf,file_unit,$
+    "; Connect the longitude lines from the poles outward."
+    printf,file_unit,"for i = 0,num_lon - 1 do oplot,xx[i,*],y[i,*]"
+
+    printf,file_unit,""
+    printf,file_unit,";LABELS"
+    printf,file_unit,$
+    "; Label the latitude and longitude lines and correctly orient the labels."
+    printf,file_unit,"j = 0"
+    printf,file_unit,"repeat begin"
+    printf,file_unit,"  i = lon_index(j)"
+    printf,file_unit,"  xyouts,xx(i,0)-xdelta*sin(longitude(i,0)/!radeg),$"
+    printf,file_unit,"         y(i,0)-ydelta*cos(longitude(i,0)/!radeg),$"
+    printf,file_unit,$
+          "         strcompress(string(long(longitude(i,0)))),alignment=0.5,$"
+    printf,file_unit,"         orientation=360-longitude(i,0)"
+    printf,file_unit,"  j = j + 1"
+    printf,file_unit,"endrep until (j eq n_elements(lon_index))"
+    printf,file_unit,"if (lat_index[0] ne -1) then $"
+    printf,file_unit,"  xyouts,xx(0,lat_index),y(0,lat_index),$"
+    printf,file_unit,"       strcompress(string(long(latitude(0,lat_index))))"
+
+  ; CYLINDRICAL PROJECTIONS
+  endif else if (((map ge 9) and (map le 12)) or (map eq 0)) then begin
+    printf,file_unit,""
+    printf,file_unit,"; Draw lines connecting equal longitudes"
+    printf,file_unit,"for i = 0,num_lon - 1 do oplot,xx[i,*],y[i,*]"
+    printf,file_unit,"; Draw lines connecting equal latitudes"
+    printf,file_unit,$
+    "if ((min(longitude[*,0]) ge 180) or (max(longitude[*,0]) lt 180)) then $"
+    printf,file_unit,"  for i = 0,num_lat - 1 do oplot,xx[*,i],y[*,i] $"
+    printf,file_unit,"else begin"
+    printf,file_unit,"  index = where(longitude[*,0] ge 180)"
+    printf,file_unit,$
+    "  if ((360 - max(longitude[*,0]) + min(longitude[*,0])) le lon_spacing) $"
+    printf,file_unit,"    then begin"
+    printf,file_unit,$
+    "    for i = 0, num_lat - 1 do oplot,[xx(index,i),xx(0:index[0]-1,i)],$"
+    printf,file_unit,$
+    "                                    [y(index,i),y(0:index[0]-1,i)]"
+    printf,file_unit,"  endif else begin"
+    printf,file_unit,"    for i = 0,num_lat - 1 do begin"
+    printf,file_unit,"      oplot,xx(0:index[0] - 1,i),y(0:index[0] - 1,i)"
+    printf,file_unit,"      oplot,xx(index,i),y(index,i)"
+    printf,file_unit,"    endfor"
+    printf,file_unit,"  endelse"
+    printf,file_unit,"endelse"
+
+    printf,file_unit,""
+    printf,file_unit,";LABELS"
+    printf,file_unit,$
+    "; Label the latitude and longitude lines and correctly orient the labels."
+    printf,file_unit,$
+    "xyouts,xx(lon_index,0),y(lon_index,0) - ydelta,orientation=90,$"
+    printf,file_unit,$
+    "       strcompress(string(long(longitude(lon_index,0)))),alignment=0.5"
+    printf,file_unit,"y_index = where(longitude[0,*] eq max(longitude[0,*]))"
+    printf,file_unit,"if (lat_index[0] ne -1) then $"
+    printf,file_unit,$
+    "xyouts,max(xx) + xdelta,y(y_index[0],lat_index),alignment=0.5,$"
+    printf,file_unit,"       strcompress(string(long(latitude(0,lat_index))))"
+
+  ; CONICAL PROJECTIONS
+  endif else if ((map ge 13) and (map le 16)) then begin
+    printf,file_unit,""
+    printf,file_unit,"; Draw lines of longitude out from the poles."
+    printf,file_unit,"for i = 0,num_lon - 1 do oplot,xx[i,*],y[i,*]"
+
+    printf,file_unit,$
+    "; Draw lines of latitude, making sure to break the line at 180 degrees."
+    printf,file_unit,"index = where(longitude[*,0] ge 180)"
+    printf,file_unit,"if (index[0] ne -1) then $"
+    printf,file_unit,$
+          "  for i = 0,num_lat - 1 do oplot,[xx(index,i),xx(0:index[0]-1,i)],$"
+    printf,file_unit,"                        [y(index,i),y(0:index[0]-1,i)] $"
+    printf,file_unit,"else begin"
+    printf,file_unit,"  index = where(longitude[*,0] eq max(longitude[*,0]))"
+    printf,file_unit,$
+    "  for i = 0,num_lat - 1 do oplot,xx(0:index[0],i),y(0:index[0],i)"
+    printf,file_unit,"endelse"
+
+    printf,file_unit,""
+    printf,file_unit,";LABELS"
+    printf,file_unit,$
+    "; Label latitude and longitude and correctly orient the labels."
+    printf,file_unit,"j = 0"
+    printf,file_unit,"if (min(longitude) lt 180) then begin"
+    printf,file_unit,$
+    "  lon_ind_1 = lon_index(where(longitude(lon_index,0) lt 180))"
+    printf,file_unit,$
+    "  lon_ind_1 = lon_ind_1(reverse(sort(longitude(lon_ind_1,0))))"
+    printf,file_unit,"endif"
+    printf,file_unit,"if (max(longitude) ge 180) then begin"
+    printf,file_unit,$
+    "  lon_ind_2 = lon_index(where(longitude(lon_index,0) ge 180))"
+    printf,file_unit,$
+    "  lon_ind_2 = lon_ind_2(reverse(sort(longitude(lon_ind_2,0))))"
+    printf,file_unit,"endif"
+    printf,file_unit,$
+    "if ((n_elements(lon_ind_1) ne 0) and (n_elements(lon_ind_2) ne 0)) then $"
+    printf,file_unit,"  lon_index = [lon_ind_1,lon_ind_2] $"
+    printf,file_unit,"else if (n_elements(lon_ind_1) ne 0) then $"
+    printf,file_unit,"  lon_index = lon_ind_1 $"
+    printf,file_unit,"else if (n_elements(lon_ind_2) ne 0) then $"
+    printf,file_unit,"  lon_index = lon_ind_2"
+    if (param2 gt -param1) then begin
+    printf,file_unit,"repeat begin"
+    printf,file_unit,"  i = lon_index(j)"
+    printf,file_unit,"  i1 = lon_index(j + 1)"
+    printf,file_unit,"  angle = atan(y(i1,0) - y(i,0),xx(i1,0) - xx(i,0))"
+    printf,file_unit,$
+    "  xyouts,xx(i,0) + xdelta*sin(angle),y(i,0) - ydelta*cos(angle),$"
+    printf,file_unit,$
+    "         strcompress(string(long(longitude(i,0)))),alignment = 0.5,$"
+    printf,file_unit,"         orientation = !radeg*angle"
+    printf,file_unit,"  j = j + 1"
+    printf,file_unit,"endrep until (j eq (n_elements(lon_index) - 1))"
+    endif else begin
+    printf,file_unit,"end_index = n_elements(xx[i,*]) - 1"
+    printf,file_unit,"repeat begin"
+    printf,file_unit,"  i = lon_index(j)"
+    printf,file_unit,"  i1 = lon_index(j + 1)"
+    printf,file_unit,"  angle = atan(y(i1,end_index) - y(i,end_index),$"
+    printf,file_unit,"               xx(i1,end_index) - xx(i,end_index))"
+    printf,file_unit,$
+    "  xyouts,xx(i,end_index) - xdelta*sin(angle),y(i,end_index) + $"
+    printf,file_unit,$
+    "         ydelta*cos(angle),strcompress(string(long(longitude($"
+    printf,file_unit,"i,end_index)))),alignment=0.5,orientation=!radeg*angle"
+    printf,file_unit,"  j = j + 1"
+    printf,file_unit,"endrep until (j eq n_elements(lon_index) - 1)"
+    endelse
+    printf,file_unit,$
+    "if (lat_index[0] ne -1) then xyouts,xx(0,lat_index),y(0,lat_index),$"
+    printf,file_unit,$
+    "                        strcompress(string(long(latitude(0,lat_index))))"
+
+  ; CONVENTIONAL PROJECTIONS
+  endif else if ((map ge 17) and (map le 22)) then begin
+    printf,file_unit,""
+    printf,file_unit,"; Draw lines of longitude"
+    printf,file_unit,"for i = 0,num_lon - 1 do oplot,xx[i,*],y[i,*]"
+
+    printf,file_unit,$
+    "; Draw lines of latitude, breaking the line at 180 degrees."
+    printf,file_unit,$
+    "if ((min(longitude[*,0]) ge 180) or (max(longitude[*,0]) lt 180)) then $"
+    printf,file_unit,"  for i = 0,num_lat - 1 do oplot,xx[*,i],y[*,i] $"
+    printf,file_unit,"else begin"
+    printf,file_unit,"  index = where(longitude[*,0] ge 180)"
+    printf,file_unit,$
+    "  if ((360 - max(longitude[*,0]) + min(longitude[*,0])) le lon_spacing) $"
+    printf,file_unit,"    then begin"
+    printf,file_unit,$
+    "    for i = 0, num_lat - 1 do oplot,[xx(index,i),xx(0:index[0]-1,i)],$"
+    printf,file_unit,$
+    "                                    [y(index,i),y(0:index[0]-1,i)]"
+    printf,file_unit,"  endif else begin"
+    printf,file_unit,"    for i = 0,num_lat - 1 do begin"
+    printf,file_unit,"      oplot,xx(0:index[0] - 1,i),y(0:index[0] - 1,i)"
+    printf,file_unit,"      oplot,xx(index,i),y(index,i)"
+    printf,file_unit,"    endfor"
+    printf,file_unit,"  endelse"
+    printf,file_unit,"endelse"
+
+    printf,file_unit,""
+    printf,file_unit,";LABELS"
+    printf,file_unit,$
+    "; Label latitude and longitude lines and orient the labels correctly."
+    printf,file_unit,"if (lat_index[0] ne -1) then $"
+    printf,file_unit,"xyouts,xx(0,lat_index),y(0,lat_index),$"
+    printf,file_unit,"       strcompress(string(long(latitude(0,lat_index))))"
+    printf,file_unit,$
+    "index = where(abs(latitude[0,*]) eq min(abs(latitude[0,*])))"
+    printf,file_unit,$
+    "xyouts,xx(lon_index,index[0]),y(lon_index,index[0]),orientation=90,$"
+    printf,file_unit,$
+"       strcompress(string(long(longitude(lon_index,index[0])))),alignment=0.5"
+  endif
+
+; SPHERICAL CUBE PROJECTIONS
+endif else begin
+  printf,file_unit,"xx = -x"
+  printf,file_unit,"yy = y"
+
+  printf,file_unit,""
+  printf,file_unit,"; Make arrays with the locations of all points."
+  printf,file_unit,"face_0 = where(face eq 0)"
+  printf,file_unit,"face_1 = where(face eq 1)"
+  printf,file_unit,"face_2 = where(face eq 2)"
+  printf,file_unit,"face_3 = where(face eq 3)"
+  printf,file_unit,"face_4 = where(face eq 4)"
+  printf,file_unit,"face_5 = where(face eq 5)"
+
+  printf,file_unit,""
+  printf,file_unit,"; Define the size of the box around each face."
+  printf,file_unit,"x_len = 2*45.0"
+  printf,file_unit,"y_len = 2*45.0"
+
+  printf,file_unit,""
+  printf,file_unit,$
+  "; Correctly adjust the x and y values for display purposes (they all start "
+  printf,file_unit,$
+  "; out on the same face)."
+  printf,file_unit,"if (face_0[0] ne -1) then begin"
+  printf,file_unit,"  x0 = -x(face_0) + 2.d0*x_len"
+  printf,file_unit,"  y0 = y(face_0) + y_len"
+  printf,file_unit,"  xx(face_0) = x0"
+  printf,file_unit,"  yy(face_0) = y0"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_1[0] ne -1) then begin"
+  printf,file_unit,"  x1 = -x(face_1) + 2.d0*x_len"
+  printf,file_unit,"  y1 = y(face_1)"
+  printf,file_unit,"  xx(face_1) = x1"
+  printf,file_unit,"  yy(face_1) = y1"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_2[0] ne -1) then begin"
+  printf,file_unit,"  x2 = -x(face_2) + x_len"
+  printf,file_unit,"  y2 = y(face_2)"
+  printf,file_unit,"  xx(face_2) = x2"
+  printf,file_unit,"  yy(face_2) = y2"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_3[0] ne -1) then begin"
+  printf,file_unit,"  x3 = -x(face_3)"
+  printf,file_unit,"  y3 = y(face_3)"
+  printf,file_unit,"  xx(face_3) = x3"
+  printf,file_unit,"  yy(face_3) = y3"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_4[0] ne -1) then begin"
+  printf,file_unit,"  x4 = -x(face_4) - x_len"
+  printf,file_unit,"  y4 = y(face_4)"
+  printf,file_unit,"  xx(face_4) = x4"
+  printf,file_unit,"  yy(face_4) = y4"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_5[0] ne -1) then begin"
+  printf,file_unit,"  x5 = -x(face_5) + 2.d0*x_len"
+  printf,file_unit,"  y5 = y(face_5) - y_len"
+  printf,file_unit,"  xx(face_5) = x5"
+  printf,file_unit,"  yy(face_5) = y5"
+  printf,file_unit,"endif"
+
+  printf,file_unit,""
+  printf,file_unit,$
+  "; Define plot ranges by finding which faces are actually used."
+  printf,file_unit,"if (face_4[0] ne -1) then x_low = -3*x_len/2 $"
+  printf,file_unit,"else if (face_3[0] ne -1) then x_low = -x_len/2 $"
+  printf,file_unit,"else if (face_2[0] ne -1) then x_low = x_len/2 $"
+  printf,file_unit,$
+  "else if ((face_1[0] ne -1) or (face_0[0] ne -1) or (face_5[0] ne -1)) $"
+  printf,file_unit,"then x_low = 3*x_len/2"
+  printf,file_unit,$
+  "if ((face_1[0] ne -1) or (face_5[0] ne -1) or (face_0[0] ne -1)) $"
+  printf,file_unit,"  then x_high = 5*x_len/2 $"
+  printf,file_unit,"else if (face_2[0] ne -1) then x_high = 3*x_len/2 $"
+  printf,file_unit,"else if (face_3[0] ne -1) then x_high = x_len/2 $"
+  printf,file_unit,"else if (face_4[0] ne -1) then x_high = -x_len/2"
+  printf,file_unit,"if (face_5[0] ne -1) then y_low = -3*y_len/2 $"
+  printf,file_unit,$
+  "else if ((face_1[0] ne -1) or (face_3[0] ne -1) or (face_2[0] ne -1) or $"
+  printf,file_unit,"   (face_4[0] ne -1)) then y_low = -y_len/2 $"
+  printf,file_unit,"else if (face_0[0] ne -1) then y_low = y_len/2"
+  printf,file_unit,"if (face_0[0] ne -1) then y_high = 3*y_len/2 $"
+  printf,file_unit,$
+  "else if ((face_1[0] ne -1) or (face_3[0] ne -1) or (face_2[0] ne -1) or $"
+  printf,file_unit,"   (face_4[0] ne -1)) then y_high = y_len/2 $"
+  printf,file_unit,"else if (face_5[0] ne -1) then y_high = -y_len/2"
+
+  printf,file_unit,""
+  printf,file_unit,"; Plot the points calculated by wcssph2xy."
+  printf,file_unit,$
+  "plot,xx,yy,psym=3,xrange=[x_low,x_high],yrange=[y_low,y_high],xstyle=4,$"
+  printf,file_unit,"     ystyle=4"
+
+  printf,file_unit,""
+  printf,file_unit,$
+  "; Set-up an array with the correct ordering of indices to connect the"
+  printf,file_unit,"; latitude lines correctly on faces 1-4."
+  printf,file_unit,"face_ind = intarr(1)"
+  printf,file_unit,"if (face_4[0] ne -1) then face_ind = [face_ind,face_4]"
+  printf,file_unit,"if (face_3[0] ne -1) then face_ind = [face_ind,face_3]"
+  printf,file_unit,"if (face_2[0] ne -1) then face_ind = [face_ind,face_2]"
+  printf,file_unit,"if (face_1[0] ne -1) then face_ind = [face_ind,face_1]"
+  printf,file_unit,"; Draw the latitude lines on faces 1-4"
+  printf,file_unit,"if (n_elements(face_ind) gt 1) then begin"
+  printf,file_unit,"  face_ind = face_ind(1:*)"
+  printf,file_unit,"  xxx = xx(face_ind)"
+  printf,file_unit,"  yyy = yy(face_ind)"
+  printf,file_unit,"  for i = 0,num_lat - 1 do begin"
+  printf,file_unit,"    index = where(latitude(face_ind) eq latitude(0,i))"
+  printf,file_unit,"    if (index[0] ne -1) then begin"
+  printf,file_unit,"      tempx = xxx(index)"
+  printf,file_unit,"      tempy = yyy(index)"
+  printf,file_unit,"      index = sort(tempx)"
+  printf,file_unit,$
+  "         if (((360 - abs(longitude(0,0) - longitude(num_lon - 1,0))) le $"
+  printf,file_unit,$
+  "          lon_spacing) or (max(longitude(index)) le 135) or $"
+  printf,file_unit,$
+"       (min(longitude(index)) gt 135)) then oplot,tempx(index),tempy(index) $"
+  printf,file_unit,"        else begin"
+  printf,file_unit,"           lon_ind = 0"
+  printf,file_unit,$
+ "           repeat lon_ind=lon_ind+1 until (longitude(index(lon_ind)) gt 135)"
+  printf,file_unit,"          index_1 = index(0:lon_ind - 1)"
+  printf,file_unit,"          index_2 = index(lon_ind:*)
+  printf,file_unit,"          oplot,tempx(index_1),tempy(index_1)"
+  printf,file_unit,"          oplot,tempx(index_2),tempy(index_2)"
+  printf,file_unit,"        endelse"
+  printf,file_unit,"      endif"
+  printf,file_unit,"    endfor"
+  printf,file_unit,"  endif"
+  printf,file_unit,""
+  printf,file_unit,"; Draw latitude lines on faces 0 and 5"
+  printf,file_unit,"  for i = 0,num_lat - 1 do begin"
+  printf,file_unit,"    if (face_0[0] ne -1) then begin"
+  printf,file_unit,"      index = where(latitude(face_0) eq latitude(0,i))"
+  printf,file_unit,"      if (index[0] ne -1) then begin"
+  printf,file_unit,$
+  "        if ((360 - abs(longitude(0,0) - longitude(n_elements(x) - 1))) $"
+  printf,file_unit,"                                    le lon_spacing) then $"
+  printf,file_unit,$
+  "          oplot,[x0(index),x0(index[0])],[y0(index),y0(index[0])] $"
+  printf,file_unit,"        else oplot,x0(index),y0(index)"
+  printf,file_unit,"      endif"
+  printf,file_unit,"    endif"
+  printf,file_unit,"    if (face_5[0] ne -1) then begin"
+  printf,file_unit,"      index = where(latitude(face_5) eq latitude(0,i))"
+  printf,file_unit,"      if (index[0] ne -1) then begin"
+  printf,file_unit,$
+  "        if ((360 - abs(longitude(0,0) - longitude(n_elements(x) - 1))) $"
+  printf,file_unit,"                                    le lon_spacing) then $"
+  printf,file_unit,$
+  "          oplot,[x5(index),x5(index[0])],[y5(index),y5(index[0])] $"
+  printf,file_unit,"        else oplot,x5(index),y5(index)" 
+  printf,file_unit,"      endif"
+  printf,file_unit,"    endif"
+  printf,file_unit,"  endfor"
+  printf,file_unit,""
+  printf,file_unit,"; Draw boxes around each face and draw longitude lines"
+  printf,file_unit,"  for i = 0,num_lon - 1 do begin"
+  printf,file_unit,"    if (face_4[0] ne -1) then begin"
+  printf,file_unit,"      index = where(longitude(face_4) eq longitude(i,0))"
+  printf,file_unit,"      if (index[0] ne -1) then oplot,x4(index),y4(index)"
+  printf,file_unit,"      plots,[-3*x_len/2,-x_len/2],[-y_len/2,-y_len/2]"
+  printf,file_unit,"      plots,[-3*x_len/2,-x_len/2],[y_len/2,y_len/2]"
+  printf,file_unit,"      plots,[-x_len/2,-x_len/2],[-y_len/2,y_len/2]"
+  printf,file_unit,"      plots,[-3*x_len/2,-3*x_len/2],[-y_len/2,y_len/2]"
+  printf,file_unit,"    endif"
+  printf,file_unit,"    if (face_2[0] ne -1) then begin"
+  printf,file_unit,"      index = where(longitude(face_2) eq longitude(i,0))"
+  printf,file_unit,"      if (index[0] ne -1) then oplot,x2(index),y2(index)"
+  printf,file_unit,"      plots,[x_len/2,3*x_len/2],[-y_len/2,-y_len/2]"
+  printf,file_unit,"      plots,[x_len/2,3*x_len/2],[y_len/2,y_len/2]"
+  printf,file_unit,"      plots,[x_len/2,x_len/2],[-y_len/2,y_len/2]"
+  printf,file_unit,"      plots,[3*x_len/2,3*x_len/2],[-y_len/2,y_len/2]"
+  printf,file_unit,"    endif"
+  printf,file_unit,"    if (face_3[0] ne -1) then begin"
+  printf,file_unit,"      index = where(longitude(face_3) eq longitude(i,0))"
+  printf,file_unit,"      if (index[0] ne -1) then oplot,x3(index),y3(index)"
+  printf,file_unit,"      plots,[-x_len/2,x_len/2],[-y_len/2,-y_len/2]"
+  printf,file_unit,"      plots,[-x_len/2,x_len/2],[y_len/2,y_len/2]"
+  printf,file_unit,"      plots,[-x_len/2,-x_len/2],[-y_len/2,y_len/2]"
+  printf,file_unit,"      plots,[x_len/2,x_len/2],[-y_len/2,y_len/2]"
+  printf,file_unit,"    endif"
+  printf,file_unit,"    if (face_1[0] ne -1) then begin"
+  printf,file_unit,"      index = where(longitude(face_1) eq longitude(i,0))"
+  printf,file_unit,"      if (index[0] ne -1) then oplot,x1(index),y1(index)"
+  printf,file_unit,"      plots,[3*x_len/2,5*x_len/2],[-y_len/2,-y_len/2]"
+  printf,file_unit,"      plots,[3*x_len/2,5*x_len/2],[y_len/2,y_len/2]"
+  printf,file_unit,"      plots,[3*x_len/2,3*x_len/2],[-y_len/2,y_len/2]"
+  printf,file_unit,"      plots,[5*x_len/2,5*x_len/2],[-y_len/2,y_len/2]"
+  printf,file_unit,"    endif"
+  printf,file_unit,"    if (face_0[0] ne -1) then begin"
+  printf,file_unit,"      index = where(longitude(face_0) eq longitude(i,0))"
+  printf,file_unit,"      if (index[0] ne -1) then oplot,x0(index),y0(index)"
+  printf,file_unit,"      plots,[3*x_len/2,5*x_len/2],[y_len/2,y_len/2]"
+  printf,file_unit,"      plots,[3*x_len/2,5*x_len/2],[3*y_len/2,3*y_len/2]"
+  printf,file_unit,"      plots,[3*x_len/2,3*x_len/2],[y_len/2,3*y_len/2]"
+  printf,file_unit,"      plots,[5*x_len/2,5*x_len/2],[y_len/2,3*y_len/2]"
+  printf,file_unit,"    endif"
+  printf,file_unit,"    if (face_5[0] ne -1) then begin"
+  printf,file_unit,"      index = where(longitude(face_5) eq longitude(i,0))"
+  printf,file_unit,"      if (index[0] ne -1) then oplot,x5(index),y5(index)"
+  printf,file_unit,"      plots,[3*x_len/2,5*x_len/2],[-3*y_len/2,-3*y_len/2]"
+  printf,file_unit,"      plots,[3*x_len/2,5*x_len/2],[-y_len/2,-y_len/2]"
+  printf,file_unit,"      plots,[3*x_len/2,3*x_len/2],[-3*y_len/2,-y_len/2]"
+  printf,file_unit,"      plots,[5*x_len/2,5*x_len/2],[-3*y_len/2,-y_len/2]"
+  printf,file_unit,"    endif"
+  printf,file_unit,"  endfor"
+  printf,file_unit,""
+  printf,file_unit,";LABELS"
+  printf,file_unit,"  if (lat_index[0] ne -1) then $"
+  printf,file_unit,"  xyouts,xx(0,lat_index),yy(0,lat_index),$"
+  printf,file_unit,"         strcompress(string(long(latitude(0,lat_index))))"
+  printf,file_unit,$
+  "  index = where(abs(latitude[0,*]) eq min(abs(latitude[0,*])))"
+  printf,file_unit,$
+  "  xyouts,xx(lon_index,index[0]),yy(lon_index,index[0]),orientation=90,$"
+  printf,file_unit,$
+"       strcompress(string(long(longitude(lon_index,index[0])))),alignment=0.5"
+endelse
+end
+
+; PROCEDURE FOR OPTION 2
+pro inversion_error,file_unit,map,param1,param2
+printf,file_unit,";CONVERSION"
+printf,file_unit,$
+"; Convert the x-y coordinates into spherical coordinates by using wcsxy2sph."
+if (map lt 23) then begin
+  if (n_elements(param1) eq 0) then begin
+    printf,file_unit,"wcsxy2sph,x,y,longitude_inv,latitude_inv,map"
+  endif else if (n_elements(param2) eq 0) then begin
+  printf,file_unit,"wcsxy2sph,x,y,longitude_inv,latitude_inv,map,pv2=param1"
+  endif else begin
+    printf,file_unit,$
+     "wcsxy2sph,x,y,longitude_inv,latitude_inv,map,pv2= [param1, param2] "
+  endelse
+endif else begin
+  printf,file_unit,$
+ "; The variable face must be declared with the same structure as latitude and"
+  printf,file_unit,"; longitude before calling wcsxy2sph." 
+  printf,file_unit,"wcsxy2sph,x,y,longitude_inv,latitude_inv,map,face=face"
+endelse
+
+printf,file_unit,""
+printf,file_unit,";PLOTTING"
+printf,file_unit,"; Plot the resulting map."
+printf,file_unit,"lon_delta = (max(longitude_inv) - min(longitude_inv))/20"
+printf,file_unit,"lat_delta = (max(latitude_inv) - min(latitude_inv))/20"
+printf,file_unit,$
+    "plot,longitude_inv,latitude_inv,psym = 3,xrange = [min(longitude_inv) - $"
+printf,file_unit,$
+"    lon_delta,max(longitude_inv) + lon_delta],yrange = [min(latitude_inv) - $"
+printf,file_unit,$
+"    lat_delta,max(latitude_inv) + lat_delta],xstyle = 4,ystyle = 4"
+printf,file_unit,"; Draw lines connecting equal longitudes"
+printf,file_unit,$
+       "for i = 0,num_lon - 1 do oplot,longitude_inv[i,*],latitude_inv[i,*]"
+printf,file_unit,"; Draw lines connecting equal latitudes"
+printf,file_unit,$
+"if ((min(longitude[*,0]) ge 180) or (max(longitude[*,0]) lt 180)) then $"
+printf,file_unit,$
+      "  for i = 0,num_lat - 1 do oplot,longitude_inv[*,i],latitude_inv[*,i] $"
+printf,file_unit,"else begin"
+printf,file_unit,"  index = where(longitude[*,0] ge 180)"
+printf,file_unit,$
+"  if ((360 - max(longitude[*,0]) + min(longitude[*,0])) le lon_spacing) $"
+printf,file_unit,"    then begin"
+printf,file_unit,$
+       "    for i = 0, num_lat - 1 do oplot,[longitude_inv(index,i),$"
+printf,file_unit,$
+       "      longitude_inv(0:index[0]-1,i)],[latitude_inv(index,i),$"
+printf,file_unit,"      latitude_inv(0:index[0]-1,i)]"
+printf,file_unit,"  endif else begin"
+printf,file_unit,"    for i = 0,num_lat - 1 do begin"
+printf,file_unit,$
+   "      oplot,longitude_inv(0:index[0] - 1,i),latitude_inv(0:index[0] - 1,i)"
+printf,file_unit,"      oplot,longitude_inv(index,i),latitude_inv(index,i)"
+printf,file_unit,"    endfor"
+printf,file_unit,"  endelse"
+printf,file_unit,"endelse"
+
+printf,file_unit,""
+printf,file_unit,";LABELS"
+printf,file_unit,$
+"; Label the latitude and longitude lines and correctly orient the labels."
+printf,file_unit,$
+    "xyouts,longitude_inv(lon_index,0),latitude_inv(lon_index,0) - lat_delta,$"
+printf,file_unit,$
+    "       orientation=90,strcompress(string(long(longitude(lon_index,0)))),$"
+printf,file_unit,"       alignment=0.5"
+printf,file_unit,"lat1_index = where(longitude[0,*] eq max(longitude[0,*]))"
+printf,file_unit,"if (lat_index[0] ne -1) then $"
+printf,file_unit,$
+"xyouts,max(longitude_inv) + lon_delta,latitude_inv(lat1_index[0],lat_index),$"
+printf,file_unit,$
+"       alignment=0.5,strcompress(string(long(latitude(0,lat_index))))"
+
+printf,file_unit,"read,'Press return to continue',key"
+print,"  In order to make the scripts wcssph2xy.pro and wcsxy2sph.pro"
+print,"invertible and minimize the error in the process, it was necessary to"
+print,"offset the latitude of all points at the poles by a small amount."
+print,"When viewing the difference between the original longitude and"
+print,"latitude and the longitude and latitude after points are run through"
+print,"wcssph2xy.pro and wcsxy2sph.pro, the offset at the poles will show up"
+print,"as vertical lines.  This overshadows any numerical error elsewhere"
+print,"by orders of magnitude.  The default is to ignore these errors, but"
+print,"to include them, enter n at the prompt"
+print,""
+key = ""
+repeat $
+  read,"Ignore offset at poles when plotting vector field (y or n):",key $
+until ((key eq "y") or (key eq "n")) 
+
+if (key eq "y") then begin
+  printf,file_unit,"poles = where(abs(abs(latitude_inv) - 9.d1) le 573.d-4)"
+  printf,file_unit,"if (poles[0] ne -1) then $"
+  printf,file_unit,$
+    "  latitude_inv(poles) = latitude_inv(poles)/abs(latitude_inv(poles))*9.d1"
+endif
+
+printf,file_unit, $
+    "dist = sphdist(longitude,latitude,longitude_inv,latitude_inv,/degrees)"
+printf,file_unit,"erase"
+printf,file_unit,$
+"print,'The largest arrow on the plot will represent a difference of '"
+printf,file_unit,"print,max(dist),' degrees.'"
+printf,file_unit,"read,'Press return to continue',key"
+printf,file_unit,$
+       "norm = sqrt((longitude-longitude_inv)^2 + (latitude-latitude_inv)^2)"
+printf,file_unit,"lon_diff=dist*(longitude-longitude_inv)"
+printf,file_unit,"good = where(norm ne 0.d0)"
+printf,file_unit,"lon_diff(good) = lon_diff(good)/norm(good)"
+printf,file_unit,"lat_diff = dist*(latitude-latitude_inv)"
+printf,file_unit,"lat_diff(good) = lat_diff(good)/norm(good)"
+printf,file_unit,"velovect,lon_diff,lat_diff,longitude[*,0],latitude[0,*]"
+end
+
+; PROCEDURE FOR OPTION 3
+pro wcs_rot,file_unit,map,param1,param2
+printf,file_unit,";PLOTTING"
+printf,file_unit,"; Plot the resulting map."
+if ((map ge 0) and (map le 22)) then begin
+  ; For all but the spherical cube projections, simply plot the results from
+  ; wcssph2xy.pro as is.
+  printf,file_unit,"xdelta = (max(xx) - min(xx))/20"
+  printf,file_unit,"ydelta = (max(y) - min(y))/20"
+  printf,file_unit,$
+         "plot,xx,y,psym = 3,xrange = [min(xx) - xdelta,max(xx) + xdelta],$"
+  printf,file_unit,$
+         "yrange = [min(y) - ydelta,max(y) + ydelta],xstyle = 4,ystyle = 4"
+  printf,file_unit,"zero_ind = where(latitude[0,*] eq min(abs(latitude[0,*])))"
+  printf,file_unit,$
+  "xyouts,xx(lon_index,zero_ind[0]),y(lon_index,zero_ind[0]),$"
+  printf,file_unit,$
+  "       strcompress(string(long(longitude(lon_index,zero_ind[0])))),$"
+  printf,file_unit,"       alignment = 0.5"
+  printf,file_unit,$
+  "zero_ind2 = where(longitude[*,0] eq min(abs(longitude[*,0])))"
+  printf,file_unit,$
+  "xyouts,xx(zero_ind2[0],lat_index),y(zero_ind2[0],lat_index),$"
+  printf,file_unit,$
+  "       strcompress(string(long(latitude(zero_ind2[0],lat_index)))),$"
+  printf,file_unit,"       alignment = 0.5"
+  printf,file_unit,$
+  "non_zero_ind = where(longitude[*,0] ne min(abs(longitude[*,0])))
+  printf,file_unit,$
+  "for i = 0,zero_ind[0] - 1 do $"
+  printf,file_unit,$
+  "    oplot,xx(non_zero_ind,i),y(non_zero_ind,i),psym=4"
+  printf,file_unit,$
+  "for i = zero_ind[0] + 1,n_elements(longitude[0,*]) - 1 do $"
+  printf,file_unit,"   oplot,xx(non_zero_ind,i),y(non_zero_ind,i),psym=4"
+endif else begin
+  printf,file_unit,"xx = -x"
+  printf,file_unit,"yy = y"
+
+  printf,file_unit,""
+  printf,file_unit,"; Make arrays with the locations of all points."
+  printf,file_unit,"face_0 = where(face eq 0)"
+  printf,file_unit,"face_1 = where(face eq 1)"
+  printf,file_unit,"face_2 = where(face eq 2)"
+  printf,file_unit,"face_3 = where(face eq 3)"
+  printf,file_unit,"face_4 = where(face eq 4)"
+  printf,file_unit,"face_5 = where(face eq 5)"
+
+  printf,file_unit,""
+  printf,file_unit,"; Define the size of the box around each face."
+  if (map eq 23) then begin
+    printf,file_unit,"x_len = 90"
+    printf,file_unit,"y_len = 90"
+  endif else begin
+    printf,file_unit,"x_len = 2*!radeg"
+    printf,file_unit,"y_len = 2*!radeg"
+  endelse
+
+  printf,file_unit,""
+  printf,file_unit,$
+  "; Correctly adjust the x and y values for display purposes (they all start "
+  printf,file_unit,$
+  "; out on the same face)."
+  printf,file_unit,"if (face_0[0] ne -1) then begin"
+  printf,file_unit,"  x0 = -x(face_0)"
+  printf,file_unit,"  y0 = y(face_0) - y_len"
+  printf,file_unit,"  xx(face_0) = x0"
+  printf,file_unit,"  yy(face_0) = y0"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_1[0] ne -1) then begin"
+  printf,file_unit,"  x1 = -x(face_1) + 2.d0*x_len"
+  printf,file_unit,"  y1 = y(face_1)"
+  printf,file_unit,"  xx(face_1) = x1"
+  printf,file_unit,"  yy(face_1) = y1"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_2[0] ne -1) then begin"
+  printf,file_unit,"  x2 = -x(face_2) + x_len"
+  printf,file_unit,"  y2 = y(face_2)"
+  printf,file_unit,"  xx(face_2) = x2"
+  printf,file_unit,"  yy(face_2) = y2"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_3[0] ne -1) then begin"
+  printf,file_unit,"  x3 = -x(face_3)"
+  printf,file_unit,"  y3 = y(face_3)"
+  printf,file_unit,"  xx(face_3) = x3"
+  printf,file_unit,"  yy(face_3) = y3"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_4[0] ne -1) then begin"
+  printf,file_unit,"  x4 = -x(face_4) - x_len"
+  printf,file_unit,"  y4 = y(face_4)"
+  printf,file_unit,"  xx(face_4) = x4"
+  printf,file_unit,"  yy(face_4) = y4"
+  printf,file_unit,"endif"
+  printf,file_unit,"if (face_5[0] ne -1) then begin"
+  printf,file_unit,"  x5 = -x(face_5)"
+  printf,file_unit,"  y5 = y(face_5) - y_len"
+  printf,file_unit,"  xx(face_5) = x5"
+  printf,file_unit,"  yy(face_5) = y5"
+  printf,file_unit,"endif"
+
+  printf,file_unit,""
+  printf,file_unit,$
+  "; Define plot ranges by finding which faces are actually used."
+  printf,file_unit,"if (face_4[0] ne -1) then x_low = -3*x_len/2 $"
+  printf,file_unit,"else if (face_3[0] ne -1) then x_low = -x_len/2 $"
+  printf,file_unit,"else if (face_2[0] ne -1) then x_low = x_len/2 $"
+  printf,file_unit,$
+  "else if ((face_1[0] ne -1) or (face_5[0] ne -1) or (face_0[0] ne -1)) $"
+  printf,file_unit,"  then x_low = 3*x_len/2"
+  printf,file_unit,"if (face_4[0] ne -1) then x_high = -x_len/2 $"
+  printf,file_unit,"else if (face_2[0] ne -1) then x_high = 3*x_len/2 $"
+  printf,file_unit,"else if (face_3[0] ne -1) then x_high = x_len/2 $"
+  printf,file_unit,$
+  "else if ((face_1[0] ne -1) or (face_5[0] ne -1) or (face_0[0] ne -1)) $"
+  printf,file_unit,"  then x_high = 5*x_len/2"
+  printf,file_unit,"if (face_5[0] ne -1) then y_low = -3*y_len/2 $"
+  printf,file_unit,$
+  "else if ((face_4[0] ne -1) or (face_3[0] ne -1) or (face_2[0] ne -1) or $"
+  printf,file_unit,"   (face_1[0] ne -1)) then y_low = -y_len/2 $"
+  printf,file_unit,"else if (face_0[0] ne -1) then y_low = y_len/2"
+  printf,file_unit,"if (face_0[0] ne -1) then y_high = 3*y_len/2 $"
+  printf,file_unit,$
+  "else if ((face_1[0] ne -1) or (face_3[0] ne -1) or (face_2[0] ne -1) or $"
+  printf,file_unit,"   (face_4[0] ne -1)) then y_high = y_len/2 $"
+  printf,file_unit,"else if (face_5[0] ne -1) then y_high = -y_len/2"
+
+  printf,file_unit,""
+  printf,file_unit,"; Plot the points calculated by wcssph2xy."
+  printf,file_unit,$
+  "plot,xx,yy,psym=3,xrange=[x_low,x_high],yrange=[y_low,y_high],xstyle=4,$"
+  printf,file_unit,"     ystyle=4"
+  printf,file_unit,"zero_ind = where(latitude[0,*] eq min(abs(latitude[0,*])))"
+  printf,file_unit,$
+  "xyouts,xx(lon_index,zero_ind[0]),yy(lon_index,zero_ind[0]),$"
+  printf,file_unit,$
+  "       strcompress(string(long(longitude(lon_index,zero_ind[0])))),$"
+  printf,file_unit,"       alignment = 0.5"
+  printf,file_unit,$
+  "zero_ind2 = where(longitude[*,0] eq min(abs(longitude[*,0])))"
+  printf,file_unit,$
+  "xyouts,xx(zero_ind2[0],lat_index),yy(zero_ind2[0],lat_index),$"
+  printf,file_unit,$
+  "       strcompress(string(long(latitude(zero_ind2[0],lat_index)))),$"
+  printf,file_unit,"       alignment = 0.5"
+  printf,file_unit,$
+  "non_zero_ind = where(longitude[*,0] ne min(abs(longitude[*,0])))
+  printf,file_unit,$
+  "for i = 0,zero_ind[0] - 1 do $"
+  printf,file_unit,$
+  "    oplot,xx(non_zero_ind,i),yy(non_zero_ind,i),psym=4"
+  printf,file_unit,$
+  "for i = zero_ind[0] + 1,n_elements(longitude[0,*]) - 1 do $"
+  printf,file_unit,"   oplot,xx(non_zero_ind,i),yy(non_zero_ind,i),psym=4"
+endelse
+end
+
+; MAIN DEMO PROGRAM
+pro wcs_demo
+print,""
+print,"This demo program demonstrates the basic usage of the IDL procedures"
+print,"wcssph2xy.pro and wcsxy2sph.pro.  You will be prompted for information"
+print,"about the type of map projection you would like to try out and what"
+print,"portion of the sky you would like to view.  All of the commands"
+print,"actually issued to carry out these operations will be recorded in a"
+print,"journal file so that the user may later reproduce the results from this"
+print,"demo by issuing the commands him/herself.  Enjoy!"
+key=''
+print,""
+repeat read,"Enter 'c' to continue or 'x' to exit:",key $
+until ((key eq 'c') or (key eq 'x'))
+if (key eq 'x') then stop
+print,""
+
+; Major loop of whole program.
+repeat begin
+
+print,""
+print,"Your options are:"
+print,"(1) Convert spherical (sky) coordinates to x and y coordinates"
+print,"    (in other words, perform a map projection) and plot the results."
+print,"(2) Do (1) without plotting, then perform the inverse operation."
+print,"    Plot the results, then plot the difference between the original"
+print,"    sky coordinates and the coordinates that have been produced by"
+print,"    wcssph2xy and wcsxy2sph."
+print,"(3) Do (1) with an added twist, rotating the coordinate system."
+print,"(4) Exit"
+print,""
+repeat read,"Enter a number between 1 and 4:",option $
+until ((option ge 1) and (option le 4))
+print,""
+
+if (option eq 4) then stop
+
+file_name = ""
+repeat begin
+  read,"Please enter a name for the journal file:",file_name
+  print,""
+  suffix = strmid(file_name,strlen(file_name)-4,4)
+  if (suffix ne ".pro") then file_name = string(file_name,".pro")
+  file_test = file_search(file_name)
+  if (file_test[0] ne "") then begin
+    print,"The file ",file_name," already exists."
+    print,"You can overwrite this file, but if you used this journal name"
+    print,"previously in this IDL session, you will not get the desired"
+    print,"results.  To avoid any conflicts, either quit and start a new"
+    print,"session of IDL using this name (and ignore this message) or give a"
+    print,"new name to the journal file.  NOTE:  This is due to IDL's"
+    print,"inability to re-compile a procedure except from the interactive"
+    print,"mode." 
+    print,""
+    read,"Type 'y' to overwrite the file:",key
+    if (key ne 'y') then file_name = ""
+  endif
+endrep until (file_name ne "")
+openw,file_unit,file_name,/get_lun
+
+printf,file_unit,$
+"; This is an IDL procedure created by running the IDL program wcs_demo.pro"
+printf,file_unit,$
+"; and can be executed from the IDL prompt by typing .run ",file_name,"."
+printf,file_unit,$
+"; This procedure may be far more complicated than what you need.  In order"
+printf,file_unit,$
+"; to make it more user-friendly, I have broken up the tasks performed into"
+printf,file_unit,"; the following categories:"
+printf,file_unit,";   (1) SET-UP -- sections declaring constants"
+printf,file_unit,$
+";   (2) CONVERSION -- section in which spherical to xy conversion is done"
+printf,file_unit,$
+";   (3) LABELS -- sections setting up and printing labels on the maps"
+printf,file_unit,$
+";   (4) PLOTTING -- sections in which data or lines are plotted"
+printf,file_unit,$
+";To find the appropriate section, simply search for one of these four"
+printf,file_unit,";capitalized words."
+
+printf,file_unit,""
+printf,file_unit,string("pro ",strmid(file_name,0,strlen(file_name) - 4))
+
+map = 0
+print,""
+print,"Which map projection would you like to try?  Your options are:"
+print,"Number  Description                Number  Description"
+print,"------  -------------------------  ------  -------------------------"
+print,"   0    Default = Cartesian           1    Zenithal perspective"
+print,"   2    Gnomic                        3    Orthographic"
+print,"   4    Stereographic                 5    Zenithal Equidistant"
+print,"   6    Zenithal polynomial (not implemented)"
+print,"   7    Zenithal equal area           8    Airy"
+print,"   9    Cylindrical perspective      10    Cartesian"
+print,"  11    Mercator                     12    Cylindrical equal area"
+print,"  13    Conical perspective          14    Conical equidistant"
+print,"  15    Conical equal area           16    Conical orthomorphic"
+print,"  17    Bonne's equal area           18    Polyconic"
+print,"  19    Sanson-Flmsteed              20    Parabolic"
+print,"  21    Hammer-Aitoff                22    Mollweide"
+print,"  23    Cobe Quadrilateralized Spherical Cube"
+print,"  24    Quadrilateralized Spherical Cube"
+print,"  25    Tangential Spherical Cube"
+print,""
+print,$
+"NOTE: This demo program does not support the map types: 1-4,8-9,11,13, or 16 "
+print,$
+"with coordinate system rotation (option 3 above).  These are allowed by"
+print,$
+"wcssph2xy.pro and wcsxy2sph.pro, but due to problems with the general case of"
+print,$
+"latitude and longitude restrictions, these map types were skipped here." 
+print,""
+repeat read,"Please enter a number from 0 to 25:",map $
+until ((map ge 0) and (map le 25))
+
+if (option eq 3) then begin
+  if ((map le 4) or (map eq 8) or (map eq 9) or (map eq 11) or (map eq 13) $
+      or (map eq 16)) then begin
+    close,file_unit
+    file_delete, file_name
+    message,"The map type selected is not supported with coordinate rotations."
+  endif else begin
+    print,$
+    "The idea behind the rotation of the coordinate systems is to relocate the"
+    print,$
+  "'special' point of the projection.  For instance, the azimuthal projections"
+    print,$
+    "project from the north pole.  So, the lines of longitude appear as rays"
+    print,$
+    "coming from the center of the projection and lines of latitude appear as"
+    print,$
+    "concentric rings around the center.  By rotating the coordinate system,"
+    print,$
+    "a different point can play the role of the north pole in this example."
+    print,$
+    "To perform the rotation, the latitude and longitude of the new 'special'"
+    print,$
+    "point must be given.  In addition, to specify a full rotation, a third"
+    print,$
+    "angle must be given.  This angle specifies the longitude of the north"
+    print,$
+    "pole in the transformed system and has a default of 180 degrees."
+    print,""
+    read,"Please enter the longitude of the 'special' point:",alpha 
+    read,"Please enter the latitude of the 'special' point:",delta
+    read,"Please enter the third angle (enter 180 for the default):",longpole
+  endelse
+endif
+  
+printf,file_unit,";SET-UP"
+printf,file_unit,"; Set-up constants used later in this procedure"
+printf,file_unit,"map = ",map
+print,""
+
+; Get parameters for map types that require them.
+case map of
+  1:begin
+    read,$
+    "AZP: Enter distance of source to projection (range = [0,10^14]):",param1
+  end
+  6:begin
+    close,file_unit
+    file_delete,file_name,/allow
+    message,"ZPN: This map projection has not been implemented."
+  end
+  8:begin
+    print,"AIR: Enter the angular distance from the tangent point in which the"
+    read,"error is to be minimized (range = [0,90]):",param1
+  end
+  9:begin
+    read,"CYP: Enter the radius of the cylinder (range = [0,10^14]):",param2
+    print,"CYP: Enter the distance from the projection point to the center of"
+    read,"the sphere (range = [-10^14,10^14], but not -radius):",param1
+  end
+  12:begin
+    print,"CEA: Enter the square of the cosine of the latitude at which the"
+    read,"map is conformal (range = [0,1]):",param1
+  end
+  13:begin
+    read,$
+    "COP: Lower angle at which cone intersects sphere (range = [-90,upper]):",$
+    theta1
+    read,$
+    "COP: Upper angle at which cone intersects sphere (range = [lower,90]):",$
+    theta2
+    param1 = (theta2+theta1)/2.
+    param2 = abs(theta2 - theta1)/2
+  end
+  14:begin
+    read,$
+    "COD: Lower angle at which cone intersects sphere (range = [-90,upper]):",$
+    param1
+    read,$
+    "COD: Upper angle at which cone intersects sphere (range = [lower,90]):",$
+    param2
+  end
+  15:begin
+    read,$
+    "COE: Lower angle at which cone intersects sphere (range = [-90,upper]):",$
+    param1
+    read,$
+    "COE: Upper angle at which cone intersects sphere (range = [lower,90]):",$
+    param2
+  end
+  16:begin
+    read,$
+    "COO: Lower angle at which cone intersects sphere (range = [-90,upper]):",$
+    param1
+    read,$
+    "COO: Upper angle at which cone intersects sphere (range = [lower,90]):",$
+    param2
+  end
+  17:begin
+    read,"BON: Characteristic angle (range = [-90,90]):",param1
+  end
+  else:
+endcase
+
+if (n_elements(param1) ne 0) then printf,file_unit,"param1 = ",param1
+if (n_elements(param2) ne 0) then printf,file_unit,"param2 = ",param2
+if (n_elements(alpha) ne 0) then printf,file_unit,"alpha = ",alpha
+if (n_elements(delta) ne 0) then printf,file_unit,"delta = ",delta
+if (n_elements(longpole) ne 0) then printf,file_unit,"longpole = ",longpole
+
+print,"Would you like to:"
+print,"(1) Do a whole-sky map."
+print,"(2) Select a (rectangular) region on the sky to map."
+print,""
+repeat read,"Enter '1' or '2':",choice until ((choice eq 1) or (choice eq 2))
+print,""
+
+; Set-up to do a full-sky map.
+if (choice eq 1) then begin
+  ; set-up the longitude range
+  printf,file_unit,"min_lon = 0"
+  printf,file_unit,"max_lon = 345"
+  printf,file_unit,"lon_spacing = 15"
+
+  ; set-up the latitude range (this differs from map to map because some maps
+  ; diverge at particular latitudes)
+  if ((map eq  1) or (map eq 3)) then begin
+    printf,file_unit,"min_lat = 0"
+    printf,file_unit,"max_lat = 90"
+  endif else if (map eq 2) then begin
+    printf,file_unit,"min_lat = 15"
+    printf,file_unit,"max_lat = 90"
+  endif else if (map eq 4) then begin
+    printf,file_unit,"min_lat = -75"
+    printf,file_unit,"max_lat = 90"
+  endif else if (map eq 8) then begin
+  ; For the Airy map projection, the minimum usable latitude depends on the
+  ; input parameters, so it must be calculated now.
+    xi = (findgen(90) + 1)/!radeg
+    xi_b = (!pi/2.0 - param1/!radeg)/2.0
+    radius=-!radeg*(alog(cos(xi))/tan(xi)+alog(cos(xi_b))/tan(xi_b)*tan(xi))
+    i = 0
+    repeat i = i + 1 $
+    until ((radius[i + 1] lt radius[i]) or (i eq (n_elements(radius) - 2)))
+    i = i - 1
+    min_lat = 90 - 2*!radeg*xi[i]
+    printf,file_unit,"min_lat = ",min_lat[0]
+    printf,file_unit,"max_lat = 90"
+  endif else if (map eq 9) then begin
+ ; The CYP map projection diverges at the poles when param1 (mu) is equal to 0.
+    if (param1 eq 0) then begin
+      printf,file_unit,"min_lat = -75"
+      printf,file_unit,"max_lat = 75"
+    endif else begin
+      printf,file_unit,"min_lat = -90"
+      printf,file_unit,"max_lat = 90"
+    endelse
+  endif else if (map eq 11) then begin
+    printf,file_unit,"min_lat = -75"
+    printf,file_unit,"max_lat = 75"
+  endif else if (map eq 13) then begin
+    printf,file_unit,"min_lat = -90 > (param1 - 90 + 15)"
+    printf,file_unit,"max_lat =  90 < (param1 + 90 - 15)"
+  endif else if (map eq 16) then begin
+    printf,file_unit,"min_lat = -75"
+    printf,file_unit,"max_lat = 90"
+  endif else begin
+    printf,file_unit,"min_lat = -90"
+    printf,file_unit,"max_lat = 90"
+  endelse
+  printf,file_unit,"lat_spacing = 15"
+endif else if (choice eq 2) then begin
+  print,"Please enter the following quantities in degrees.'
+  read,"  minimum longitude:",min_lon
+  printf,file_unit,"min_lon = ",min_lon
+  read,"  maximum longitude:",max_lon
+  printf,file_unit,"max_lon = ",max_lon
+  read,"  longitude spacing:",lon_spacing
+  printf,file_unit,"lon_spacing = ",lon_spacing
+  read,"  minimum latitude:",min_lat
+  printf,file_unit,"min_lat = ",min_lat
+  read,"  maximum latitude:",max_lat
+  printf,file_unit,"max_lat = ",max_lat
+  read,"  latitude spacing:",lat_spacing
+  printf,file_unit,"lat_spacing = ",lat_spacing
+endif
+
+printf,file_unit,""
+printf,file_unit,$
+"; Based on the ranges for latitude and longitude, as well as their spacing,"
+printf,file_unit,$
+"; generate the latitude and longitude arrays."
+printf,file_unit,"num_lon = long((max_lon - min_lon)/lon_spacing) + 1"
+printf,file_unit,"lon = dindgen(num_lon)*lon_spacing + min_lon"
+printf,file_unit,"num_lat = long((max_lat - min_lat)/lat_spacing) + 1"
+printf,file_unit,"lat = dindgen(num_lat)*lat_spacing + min_lat"
+printf,file_unit,"longitude = dblarr(num_lon,num_lat)"
+printf,file_unit,"for i = 0,num_lat - 1 do longitude[*,i] = lon"
+printf,file_unit,"latitude = dblarr(num_lon,num_lat)"
+printf,file_unit,"for i = 0,num_lon - 1 do latitude[i,*] = lat"
+
+printf,file_unit,""
+printf,file_unit,";CONVERSION"
+
+printf,file_unit,$
+"; Convert the spherical coordinates into x-y coordinates by using wcssph2xy."
+if (map lt 23) then begin
+  if (n_elements(param1) eq 0) then begin
+    if (n_elements(alpha) ne 0) then begin
+      printf,file_unit,$
+        "wcssph2xy,longitude,latitude,x,y,map,crval=[alpha,delta],$"
+      printf,file_unit,"          longpole=longpole"
+    endif else begin
+      printf,file_unit,"wcssph2xy,longitude,latitude,x,y,map"
+    endelse
+  endif else if (n_elements(param2) eq 0) then begin
+    if (n_elements(alpha) ne 0) then begin
+      printf,file_unit,$
+        "wcssph2xy,longitude,latitude,x,y,map,pv2=param1, $"
+      printf,file_unit,"          crval=[alpha,delta],longpole=longpole"
+    endif else begin
+       printf,file_unit,"wcssph2xy,longitude,latitude,x,y,map,pv2=param1"
+    endelse
+  endif else begin
+    if (n_elements(alpha) ne 0) then begin
+      printf,file_unit,$
+        "wcssph2xy,longitude,latitude,x,y,map,pv2=[param1,param2],$
+      printf,file_unit,"          crval=[alpha,delta],longpole=longpole"
+    endif else begin
+      printf,file_unit,$
+        "wcssph2xy,longitude,latitude,x,y,map,pv2=[param1,param2]"
+    endelse
+  endelse
+endif else begin
+  printf,file_unit,$
+ "; The variable face must be declared with the same structure as latitude and"
+  printf,file_unit,"; longitude before calling wcssph2xy." 
+  printf,file_unit,"face = longitude - longitude"
+  if (n_elements(alpha) ne 0) then begin
+    printf,file_unit,$
+      "wcssph2xy,longitude,latitude,x,y,map,face=face,crval=[alpha,delta], $
+    printf,file_unit,"          longpole=longpole"
+  endif else begin
+    printf,file_unit,"wcssph2xy,longitude,latitude,x,y,map,face=face"
+  endelse
+endelse
+printf,file_unit,""
+
+printf,file_unit,";PLOTTING"
+printf,file_unit,$
+"; all maps have x increasing to the left, so switch this"
+printf,file_unit,"xx = -x"
+printf,file_unit,""
+
+printf,file_unit,";LABELS"
+printf,file_unit,$
+"; The arrays lon_index and lat_index contain the indices for the latitude"
+printf,file_unit,$
+"; and longitude labels.  Labels occur every 30 degrees unless 30 doesn't"
+printf,file_unit,$
+"; divide into any of the latitude and longitude values evenly.  In this case,"
+printf,file_unit,$
+"; all latitude and longitude lines are labeled."
+printf,file_unit,$
+  "lon_index = where(long(longitude[*,0])/30 eq longitude[*,0]/30.)"
+printf,file_unit,$
+  "lat_index = where(long(latitude[0,*])/30 eq latitude[0,*]/30.)"
+printf,file_unit,$
+  "if (lat_index[0] eq -1) then lat_index = indgen(n_elements(latitude[0,*]))"
+printf,file_unit,$
+  "if (lon_index[0] eq -1) then lon_index = indgen(n_elements(longitude[*,0]))"
+
+printf,file_unit,""
+
+if (option lt 3) then begin
+ if (n_elements(param2) eq 1) then wcssph2xy_plot,file_unit,map,param1,param2 $
+  else if (n_elements(param1) eq 1) then wcssph2xy_plot,file_unit,map,param1 $
+  else wcssph2xy_plot,file_unit,map
+
+  if (option eq 2) then begin
+    printf,file_unit,"key = ''"
+    printf,file_unit,"read,'Press return to continue',key"
+
+    if (n_elements(param2) eq 1) then $
+       inversion_error,file_unit,map,param1,param2 $
+    else if (n_elements(param1) eq 1) then $
+       inversion_error,file_unit,map,param1 $
+    else inversion_error,file_unit,map
+  endif
+endif else begin
+  if (n_elements(param2) eq 1) then wcs_rot,file_unit,map,param1,param2 $
+  else if (n_elements(param1) eq 1) then wcs_rot,file_unit,map,param1 $
+  else wcs_rot,file_unit,map
+endelse
+
+printf,file_unit,"end"
+close,file_unit
+print,$
+"The commands needed to execute what you are about to see can be executed"
+print,"interactively, by typing ",strmid(file_name,0,strlen(file_name)-3)
+print,""
+command = strmid(file_name,0,strlen(file_name) - 4)
+r = execute(command)
+endrep until (option eq 4)
+end
diff --git a/Code/script_idl_mv/astrolib/wcs_getpole.pro b/Code/script_idl_mv/astrolib/wcs_getpole.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e317cee60ba6ffafddf32dba7e32cc28e8ff8208
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/wcs_getpole.pro
@@ -0,0 +1,141 @@
+;+
+; NAME:
+;       WCS_GETPOLE 
+;
+; PURPOSE:
+;       Compute the coordinates of the native pole
+;
+; EXPLANATION:
+;       WCS_GETPOLE is used to determine the celestial position of the 
+;       native pole.    See section 2.4 of the paper 
+;       "Representation of Celestial Coordinates in FITS" by Calabretta 
+;       Greisen (2002, A&A, 395, 1077, also available at  
+;       http://fits.gsfc.nasa.gov/fits_wcs.html    Called by WCS_ROTATE
+;
+; CALLING SEQUENCE:
+;       WCS_GETPOLE, crval, lonpole, theta0, alpha_p, delta_p, [LATPOLE= AT_POLE=]
+;
+; INPUT PARAMETERS:
+;       crval - 2 element vector containing standard system coordinates (the 
+;               longitude and latitude) of the reference point in degrees
+;       lonpole - native longitude of the celestial North Pole (degrees)
+;                 *unless* the fiducial point is at non-zero native longitude
+;                 (phi_0 =/ 0), in which case phi_0 should have been subtracted,
+;                 i.e. lonpole = phi_p - phi_0.
+;       theta0  - native latitude of the fiducial point (degrees)
+;
+; OUTPUT PARAMETERS:
+;       alpha_p, delta_p - celestial longitude and latitude of the native pole
+;               (Radians)
+; OPTIONAL KEYWORD INPUT PARAMETERS:
+;       LATPOLE - native latitude of the celestial North Pole (degrees)
+;                 NB only used to resolve ambiguity. Final value is the one 
+;                 nearest to input value of LATPOLE. Can be set outside range
+;                 [-90,90]
+;
+; OPTIONAL KEYWORD OUTPUT PARAMETERS
+;       AT_POLE (byte) true if delta_p = pi/2 (avoiding some round-off errors)
+;
+; REVISION HISTORY:
+;       Written    W. Landsman               June, 2003
+;       Fix calculation when theta0 is not 0 or 90     February 2004
+;       E. Hivon: alpha_p, delta_p consistenly in Radians May 2010
+;       J. P. Leahy introduced AT_POLE, more traps for special cases to
+;       avoid rounding errors                July 2013
+;
+;-
+
+pro WCS_GETPOLE, crval, lonpole, theta0, alpha_p, delta_p, $
+  LATPOLE = latpole, AT_POLE = at_pole
+    
+ compile_opt idl2, hidden
+          
+; check to see that enough parameters (at least 4) were sent
+ if (N_params() lt 5) then begin
+    print,'Syntax - WCS_GETPOLE,  crval, lonpole, theta0 = ,alpha_p, delta_p, '
+    print,'                [LATPOLE= ]' 
+    return
+ endif 
+
+ ; DEFINE ANGLE CONSTANTS 
+ pi = !DPI
+ pi2 = acos(0d0) ; do it this way to mitigate risks of round-off errors when
+                 ; checking equality to pi/2
+                 
+ radeg = 1.8d2/pi
+ alpha_0 = double(crval[0])/radeg
+ delta_0 = double(crval[1])/radeg
+
+ if theta0 EQ 90 then begin
+     alpha_p = alpha_0
+     delta_p = delta_0
+     at_pole = crval[1] EQ 90d0
+     return
+ endif
+
+; Longpole is the longitude in the native system of the North Pole in the
+; standard system (default = 180 degrees).
+
+ phi_p   = double(lonpole)/radeg
+ theta_p = double(latpole)/radeg
+ sp = sin(phi_p)
+ cp = cos(phi_p)
+ sd = sin(delta_0)
+ cd = cos(delta_0)
+ tand = tan(delta_0)
+
+ 
+ if (theta0 EQ 0d0) then begin
+        if (delta_0 EQ 0d0) && (abs(lonpole) EQ 90.0d) then begin
+            delta_p = theta_p
+            at_pole = latpole EQ 90d0 
+        endif else begin
+            delta_p = acos( sd/cp)               ;Updated May 98
+            IF latpole LE -90 then delta_p *= -1d0 else if $
+              (latpole LT 90 && abs(theta_p + delta_p) LT abs(theta_p - delta_p)) $
+               then delta_p = -delta_p
+            at_pole = theta_p ge 0d0 && crval[1] EQ 0d0
+        endelse
+        alpha_p = alpha_0
+        if (lonpole NE 1.8d2) && (cd NE 0d0) THEN CASE delta_p OF
+            pi2:  alpha_p += phi_p - !dpi
+           -pi2:  alpha_p -= phi_p 
+            ELSE: alpha_p -= atan(sp/cd, -tan(delta_p)*tand )
+        ENDCASE
+ endif else IF theta0 EQ crval[1] && lonpole EQ 0 THEN BEGIN
+     delta_p = pi2
+     alpha_p = alpha_0 + phi_p - !dpi
+     at_pole = 1B
+ ENDIF ELSE begin                ;General case for arbitary theta0
+        ctheta = cos(theta0/RADEG)
+        stheta = sin(theta0/RADEG)
+        term1 = atan(stheta, ctheta*cp ) 
+        term2 = acos( sd/( sqrt(1.0d - ctheta^2*sp^2)  ))
+        if term2 EQ 0d0 then delta_p = term1 else begin
+           delta_p1 = abs( (term1 + term2)*radeg)
+           delta_p2 = abs( (term1 - term2)*radeg)
+           case 1 of 
+           (delta_p1 GT 90) and (delta_p2 GT 90):message,'No valid solution'
+           (delta_p1 LE 90) and (delta_p2 GT 90): delta_p = term1 + term2
+           (delta_p1 GT 90) and (delta_p2 LE 90): delta_p = term1 - term2
+           else: begin             ;Two valid solutions
+                 delta_p1 = (term1 + term2)*radeg
+                 delta_p2 = (term1 - term2)*radeg
+                 print, delta_p1, delta_p2, latpole
+                 if abs(latpole-delta_p1) LT abs(latpole - delta_p2) then $
+                       delta_p = term1+term2 else delta_p = term1 - term2
+                 end
+           endcase
+           if (cd EQ 0d0) then alpha_p = alpha_0 else begin
+              sdelt = sin(delta_p)
+              if (sdelt EQ 1) then alpha_p = alpha_0 - phi_p - !DPI else $
+              if (sdelt EQ -1) then alpha_p = alpha_0 -phi_p else $
+              alpha_p = alpha_0 - $
+               atan( (stheta-sin(delta_p)*sd)/(cos(delta_p)*cd), sp*ctheta/cd )
+           endelse
+         endelse
+         at_pole = delta_p EQ pi2
+ endelse 
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/wcs_rotate.pro b/Code/script_idl_mv/astrolib/wcs_rotate.pro
new file mode 100644
index 0000000000000000000000000000000000000000..e9b64b47c6891aca571a850564457681cc7afae4
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/wcs_rotate.pro
@@ -0,0 +1,205 @@
+;+
+; NAME:
+;       WCS_ROTATE 
+;
+; PURPOSE:
+;       Rotate between standard (e.g. celestial) and native coordinates
+; EXPLANATION:
+;       Computes a spherical coordinate rotation between native coordinates 
+;       and  standard celestial coordinate system (celestial, Galactic, or
+;       ecliptic).   Applies the equations in Appendix B of the paper 
+;       "Representation of Celestial Coordinates in FITS" by Calabretta 
+;       Greisen (2002, A&A, 395, 1077).    Also see 
+;       http://fits.gsfc.nasa.gov/fits_wcs.html
+;
+; CATEGORY:
+;       Mapping and Auxiliary FITS Routine
+;
+; CALLING SEQUENCE:
+;       WCS_ROTATE, longitude, latitude, phi, theta, crval, theta0 = 
+;               [LONGPOLE = , LATPOLE = , PV1 = , /REVERSE, /ORIGIN ]
+;
+; INPUT PARAMETERS:
+;       crval - 2 element vector containing standard system coordinates (the 
+;               longitude and latitude) of the reference point
+;
+; INPUT OR OUTPUT PARAMETERS
+;       longitude - longitude of data, scalar or vector, in degrees, in the
+;               standard celestial coordinate system
+;       latitude - latitude of data, same number of elements as longitude, 
+;               in degrees
+;       theta - latitude of data in the native system, in degrees, scalar or
+;               vector
+;
+;       If the keyword(REVERSE) is set then phi and theta are input parameters
+;       and longitude and latitude are computed.    Otherwise, longitude and
+;       latitude are input parameters and phi and theta are computed.
+;
+; OPTIONAL KEYWORD INPUT PARAMETERS:
+;
+;       THETA0   - Native latitude of the reference point (required unless PV1 set)
+;       PV1      - Vector giving parameters of user-defined fiducial point
+;       LONGPOLE - native longitude of standard system's North Pole
+;       LATPOLE -  native latitude of the standard system's North Pole
+;       /REVERSE - if set then phi and theta are input parameters and longitude
+;                  and latitude are computed.    By default, longitude and
+;                  latitude are input parameters and phi and theta are computed.
+;
+;      /ORIGIN     This keyword is obsolete and is no longer used. Replaced by
+;                  explicitly specifying theta0 and/or PV1
+;
+; REVISION HISTORY:
+;       Written    W. Landsman               December, 1994
+;       Fixed error in finding North Pole if /ORIGIN and LONGPOLE NE 180
+;       Xiaoyi Wu and W. Landsman,   March, 1996
+;       Fixed implementation of March 96 error, J. Thieler,  April 1996
+;       Updated to IDL V5.0   W. Landsman    December 1997
+;       Fixed determination of alpha_p if /ORIGIN and LONGPOLE EQ 180
+;               W. Landsman    May 1998
+;       Ensure argument of ASIN() is -1<x<-1 after roundoff 
+;               W. Landsman/R. Arendt  June 2002
+;       Call WCS_GETPOLE, accept LATPOLE keyword, update cylindrical coords
+;               W. Landsman  June 2003 
+;       Don't attempt to rotate NaN values   W. Landsman  May 2004
+;       at some unknown time theta0 introduced
+;       Traps put in to detect no rotation and avoid rounding errors for
+;       common special cases. PV1 introduced. Comments updated & corrected.
+;                                            J. P. Leahy July 2013.
+;       Avoid roundoff error when longitude = +/- 180 W. Landsman Dec 2013 
+;       
+;-
+
+pro wcs_rotate, longitude, latitude, phi, theta, crval, LONGPOLE = longpole, $
+          LATPOLE = latpole, REVERSE=reverse, ORIGIN = origin, $
+          PV1 = PV1, THETA0 = theta0
+
+ COMPILE_OPT idl2, hidden
+ 
+; check to see that enough parameters (at least 4) were sent
+ if (N_params() lt 5 || N_Elements(theta0) ne 1) then begin
+    print,'Syntax - WCS_ROTATE, longitude, latitude, phi, theta, crval, PV1 = '
+    print,'               THETA0 = <scalar>, LATPOLE =,  LONGPOLE = , /REVERSE' 
+    return
+ endif 
+
+ ; DEFINE ANGLE CONSTANTS 
+ pi = !DPI
+ pi2 = pi/2.d0
+ radeg = 1.8d2/pi
+ twopi = !dpi+!dpi
+
+ if keyword_set( REVERSE) then begin
+        if min([ N_elements(phi), N_elements(theta) ]) EQ 0 then          $
+        message, 'ERROR - Native Coordinates (phi,theta) not defined'    
+ endif else begin
+        if min([ N_elements(longitude), N_elements(latitude) ]) EQ 0 then $ 
+        message, 'ERROR - Celestial Coordinates (long,lat) not defined' 
+ endelse
+
+ IF N_ELEMENTS(pv1) GT 0 THEN BEGIN ; User-specified fiducial point
+    IF N_ELEMENTS(pv1) NE 5 THEN $
+        MESSAGE, 'ERROR:- PV1 array should contain five values if specified'
+    phi0 = pv1[1]
+    theta0 = pv1[2]
+    longpole = pv1[3]
+    latpole = pv1[4]
+ ENDIF ELSE BEGIN
+    IF N_elements(theta0) NE 1 THEN $
+        MESSAGE, 'ERROR: Either PV1 or THETA0 must be set'
+    IF N_elements(longpole) eq 0 THEN longpole = crval[1] ge theta0 ? 0d0 : 1.8d2
+    IF N_elements(latpole)  eq 0 THEN latpole  = 90d0
+    phi0 = 0
+ ENDELSE
+; Longpole is the longitude in the native system of the North Pole in the
+; standard system.
+
+ phi_p = double(longpole)/radeg
+ IF longpole EQ 180d0 THEN BEGIN ; Check for special case to avoid roundoff
+     sp =  0d0
+     cp = -1d0
+ ENDIF ELSE BEGIN
+     sp = sin(phi_p)
+     cp = cos(phi_p)
+ ENDELSE
+
+; CRVAL give the celestial coordinates of the fiducial point (phi0, theta0)
+; in the native system.   This must be converted (using Eqs 8-10 in Greisen & 
+; Calabretta with theta0 = 0) to give the coordinates of the North pole 
+; (alpha_p, delta_p)
+ if theta0 EQ 90 then begin
+    ; Easy case: fiducial point is native pole:
+     alpha_p = double(crval[0]) / radeg
+     delta_p = double(crval[1]) / radeg
+     at_pole = crval[1] EQ 90d0
+ endif else WCS_GETPOLE, crval, longpole-phi0, theta0, alpha_p, delta_p, $
+                 LATPOLE = latpole, AT_POLE = at_pole
+ 
+ IF at_pole && ABS((alpha_p - phi_p) mod twopi) EQ !dpi THEN BEGIN
+     ; Native and celestial frames coincide
+     ; No rotation needed
+     IF KEYWORD_SET(REVERSE) THEN BEGIN
+         latitude  = theta
+         longitude = phi
+     ENDIF ELSE BEGIN
+         phi   = longitude
+         theta = latitude
+     ENDELSE
+     RETURN
+ ENDIF
+ 
+; compute useful quantities relating to reference angles
+ sa = sin(alpha_p)
+ ca = cos(alpha_p)
+ sd = sin(delta_p)
+ cd = cos(delta_p)
+ IF at_pole THEN cd = 0d0 ; suppress rounding errors.
+ 
+; calculate rotation matrix 
+
+  r = [ [-sa*sp - ca*cp*sd,  ca*sp - sa*cp*sd, cp*cd ] , $
+        [ sa*cp - ca*sp*sd, -ca*cp - sa*sp*sd, sp*cd ] , $
+        [ ca*cd           ,  sa*cd           , sd    ] ]
+
+; solve the set of equations for each datum point
+
+ if keyword_set(REVERSE) then begin
+        latitude = phi
+        longitude = theta
+        g = where( finite(phi) and finite(theta), Ng )
+        if Ng EQ 0 then return
+        phi1 = double(phi[g])/radeg
+        theta1 = double(theta[g])/radeg
+        r = transpose(r)
+ endif else begin
+        phi = longitude
+	phi1 = dblarr(N_elements(longitude) ) 
+	g = where(abs(longitude) NE 180.0d, Ng)    ;Avoid roundoff error
+	if Ng GT 0 then phi1[g] = double(longitude[g])/radeg
+        theta1 = double(latitude)/radeg
+ endelse
+
+; define the right-hand side of the equations
+
+ l = cos(theta1)*cos(phi1)
+ m = cos(theta1)*sin(phi1)
+ n = sin(theta1)
+
+; find solution to the system of equations and put it in b
+; Can't use matrix notation in case l,m,n are vectors
+
+ b0 = r[0,0]*l + r[1,0]*m + r[2,0]*n
+ b1 = r[0,1]*l + r[1,1]*m + r[2,1]*n
+ b2 = (r[0,2]*l + r[1,2]*m + r[2,2]*n) > (-1) < 1 ;Account for possible roundoff
+
+; use b0,b1,b2 to compute "native" latitude and longitude
+
+ if keyword_set(REVERSE) then begin
+        latitude[g] = asin(b2)*radeg
+        longitude[g] = atan( b1, b0)*radeg
+ endif else begin
+        theta = asin(b2)*radeg
+        phi = atan( b1, b0)*radeg
+ endelse
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/wcssph2xy.pro b/Code/script_idl_mv/astrolib/wcssph2xy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f4b0bf1f8b1e22f0e80c4a7a71f2ad17e52f74ba
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/wcssph2xy.pro
@@ -0,0 +1,1138 @@
+;+
+; NAME:
+;     WCSSPH2XY
+; PURPOSE:
+;     Convert spherical coordinates to x and y (map) angular coordinates
+; EXPLANATION:
+;     Convert spherical (longitude and latitude -- sky) coordinates to x
+;     and y intermediate world coordinates (still nominally in degrees) in
+;     the projection plane of the map.  This procedure is the inverse of
+;     WCSXY2SPH.    See WCS_DEMO for example of use.
+;
+;     This is a lower level procedure -- given a FITS header, the user will
+;     usually use ADXY which will then call WCSSPH2XY with the appropriate
+;     parameters.
+; CATEGORY:
+;     Mapping and Auxiliary FITS Routine
+;
+; CALLING SEQUENCE:
+;      wcssph2xy, longitude, latitude, x, y, [ map_type , CTYPE = ,
+;               FACE =, PV1= PV2= , CRVAL = , CRXY = , LONGPOLE = ,
+;               LATPOLE = , PHI0 = , NORTH_OFFSET =, SOUTH_OFFSET =, BADINDEX =]
+;
+; INPUT PARAMETERS:
+;     longitude - longitude of data, scalar or vector, in degrees
+;     latitude  - latitude of data, same number of elements as longitude,
+;                 in degrees
+;     map_type  - optional positional parameter, numeric scalar (0-26)
+;               corresponding to a particular map projection.  This is not a
+;               FITS standard, it is simply put in to allow function similar
+;               to that of less general map projection procedures (eg AITOFF).
+;               The following list gives the map projection types and their
+;               respective numbers.
+;
+;  FITS  Number  Name                       Comments
+;  code   code
+;  ----  ------  -----------------------    -----------------------------------
+;   DEF     0    Default = Plate Carree
+;   AZP     1    Zenithal perspective       PV2_1 required
+;   TAN     2    Gnomic                     AZP w/ mu = 0
+;   SIN     3    Orthographic               PV2_1,PV2_2 optional
+;   STG     4    Stereographic              AZP w/ mu = 1
+;   ARC     5    Zenithal Equidistant
+;   ZPN     6    Zenithal polynomial        PV2_0, PV2_1....PV2_20 possible
+;   ZEA     7    Zenithal equal area
+;   AIR     8    Airy                       PV2_1 required
+;   CYP     9    Cylindrical perspective    PV2_1 and PV2_2 required
+;   CAR    10    Plate Carree
+;   MER    11    Mercator
+;   CEA    12    Cylindrical equal area     PV2_1 required
+;   COP    13    Conical perspective        PV2_1 and PV2_2 required
+;   COD    14    Conical equidistant        PV2_1 and PV2_2 required
+;   COE    15    Conical equal area         PV2_1 and PV2_2 required
+;   COO    16    Conical orthomorphic       PV2_1 and PV2_2 required
+;   BON    17    Bonne's equal area         PV2_1 required
+;   PCO    18    Polyconic
+;   SFL    19    Sanson-Flamsteed  (GLS is allowed as a synonym for SFL)
+;   PAR    20    Parabolic
+;   AIT    21    Hammer-Aitoff
+;   MOL    22    Mollweide
+;   CSC    23    Cobe Quadrilateralized     convergence of inverse is poor
+;                Spherical Cube
+;   QSC    24    Quadrilateralized
+;                Spherical Cube
+;   TSC    25    Tangential Spherical Cube
+;   SZP    26    Slant Zenithal Projection   PV2_1,PV2_2, PV2_3 optional
+;   HPX    27    HealPix
+;   HCT    28    HealCart (Cartesian approximation of Healpix)
+;   XPH    29    HEALPix butterfly projection
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS:
+;
+;     CTYPE - One, two, or three element vector containing 8 character
+;              strings corresponding to the CTYPE1, CTYPE2, and CTYPE3
+;              FITS keywords:
+;
+;               CTYPE[0] - first four characters specify standard system
+;               ('RA--','GLON' or 'ELON' for right ascension, Galactic
+;               longitude or ecliptic longitude respectively), second four
+;               letters specify the type of map projection (eg '-AIT' for
+;               Aitoff projection)
+;               CTYPE[1] - first four characters specify standard system
+;               ('DEC-','GLAT' or 'ELAT' for declination, galactic latitude
+;               or ecliptic latitude respectively; these must match
+;               the appropriate system of ctype1), second four letters of
+;               ctype2 must match second four letters of ctype1.
+;               CTYPE[2] - if present must be the 8 character string,'CUBEFACE',
+;                only used for spherical cube projections to identify an axis
+;               as containing the face on which each x and y pair of
+;               coordinates lie.
+;     PV2  -  Vector of projection parameter associated with latitude axis
+;             PV2 will have up to 21 elements for the ZPN projection, up to 3
+;             for the SIN projection and no more than 2 for any other
+;             projection.   The first element corresponds to PV2_1, the
+;             second to PV2_2, etc.
+;     CRXY -    2 element vector giving the x and y coordinates of the
+;               reference point. if this is not set the offset is [0,0].
+;               Used to implement (x0,y0) in Sect 2.5 of Griesen & Calabretta 2002
+;               Do not confuse with CRPIX.
+;
+;    Parameters simply passed to WCS_ROTATE:
+;  
+;     CRVAL - 2 element vector containing standard system coordinates (the
+;               longitude and latitude) of the reference point
+;     PV1   - Vector of projection parameters associated with longitude
+;     LONGPOLE -  native longitude of standard system's North Pole
+;     LATPOLE  -  "target" native latitude of the standard system's North Pole
+;
+;    Parameters intended to enhance invertability:
+;
+;     NORTH_OFFSET - offset (radians) added to input points near north pole.
+;     SOUTH_OFFSET - offset (radians) added to input points near south pole.
+;
+; OUTPUT PARAMETERS:
+;
+;       x - x coordinate of data, same number of elements as longitude, in
+;               degrees; if CRXY is set, then x will be returned offset by
+;               crxy(0).  NOTE: x in all map projections increases to the
+;               left, not the right.
+;       y - y coordinate of data, same number of elements as longitude, in
+;               degrees; if CRXY is set, y will be returned offset by crxy[1]
+;
+; OPTIONAL OUTPUT KEYWORD PARAMETERS:
+;       FACE - a output variable used for spherical cube projections to
+;               designate the face of the cube on which the x and y
+;               coordinates lie.   Will contain the same number of elements as
+;               X and Y.    Must contain at least 1 arbitrary element on input
+;               If FACE is NOT defined on input, it is assumed that the
+;               spherical cube projection is laid out over the whole sky
+;               in the "sideways T" configuration.
+;     BADINDEX - vector, list of transformed points too close to poles.
+;
+; NOTES:
+;       The conventions followed here are described in more detail in
+;       "Representations of Celestial Coordinates in FITS" by Calabretta
+;       and  Greisen (2002, A&A, 395, 1077; also see
+;       http://fits.gsfc.nasa.gov/fits_wcs.html).  The general
+;       scheme outlined in that article is to first use WCS_ROTATE to convert
+;       coordinates in one of three standard systems (celestial, galactic,
+;       or ecliptic) into a "native system" of latitude and longitude.  The
+;       latitude and longitude are then converted into x and y coordinates
+;       which depend on the map projection which is performed.   The rotation
+;       from standard to native coordinates can be skipped if one so desires.
+;       This procedure necessitates two basic sections.  The first converts
+;       "standard" coordinates to "native" coordinates while the second converts
+;       "native" coordinates to x and y coordinates.  The first section is
+;       simply a call to WCS_ROTATE, while the second contains the guts of
+;       the code in which all of the map projection is done.  This procedure
+;       can be called in a form similar to AITOFF, EQPOLE, or QDCB by calling
+;       wcssph2xy with a fifth parameter specifying the map projection by
+;       number and by not using any of the keywords related to the map
+;       projection type (e.g. CTYPE).
+;
+; PROCEDURE:
+;
+;       The first task of the procedure is to do general error-checking to
+;       make sure the procedure was called correctly and none of the
+;       parameters or keywords conflict.  This is particularly important
+;       because the procedure can be called in two ways (either using
+;       FITS-type keywords or using a number corresponding to a map projection
+;       type).  All variables are converted into double precision values and
+;       angular measurements are converted from degrees into radians.
+;       If necessary, longitude values are converted into the range -pi to pi.
+;       Any latitude points close to the  of the poles are mapped to a specific
+;       latitude of  from the pole so that the map transformations become
+;       completely invertible.  The magnitude of this correction is given by
+;       the keywords NORTH_OFFSET and SOUTH_OFFSET and a list of affected
+;       points is optionally returned in the "badindex" output parameter.
+;       The next task of the procedure is to convert the "standard"
+;       coordinates to "native" coordinates by rotating the coordinate system.
+;       This rotation is performed by the procedure WCS_ROTATE and is governed
+;       by the keywords CRVAL and LONGPOLE.   The final task of the WCSSPH2XY
+;       is to take "native" latitude and longitude coordinates and convert
+;       them into x and y coordinates.  Any map specific error-checking is
+;       done at this time.  All of the equations were obtained from
+;       "Representations of Celestial Coordinates in FITS" and cases needing
+;       special attention are handled appropriately (see the comments with
+;       individual map projections for more information on special cases).
+;
+;       Note that a further transformation (using the CD matrix) is required
+;       to convert the (x,y) coordinates to pixel coordinates.
+; COMMON BLOCKS:
+;
+;       none
+;
+; PROCEDURES CALLED:
+;       WCS_ROTATE
+;
+; AUTHOR:
+;
+;       Rick Balsano
+;
+; MODIFICATIONS/REVISION LEVEL:
+;
+;       1.1     8/31/93
+;       2.3     9/15/93  W. Landsman (HSTX) Update quad cube coords, vectorize
+;                        keywords
+;       2.4     12/29/93 I. Freedman (HSTX) Eliminated LU decomposition
+;       2.5     1/5/93   I. Freedman (HSTX) Offset keywords / bad point index
+;       2.6     Dec 94   Compute pole for transformations where the reference
+;                       pixel is at the native origin    W. Landsman (HSTX)
+;       2.7     May 95  Change internal variable BETA for V4.0 compatibility
+;       2.8     June 95 Change loop indices from integer to long
+;       2.9     3/18/96 Change FACE usage for cube projections to match WCSLIB
+;                       C/FORTRAN software library.
+;       2.10    02/18/99 Fixed implementation of ARC algorithm
+;       2.11    June 2003 Update conic projections, add LATPOLE keyword
+;     	2.12  	Aug 2003, N.Rich - Fix pre-V5.5 bug from previous update
+;       2.13    Sep 2003, W. Landsman CTYPE keywords need not be 8 characters
+;       2.14    Jan 2004, W. Landsman don't modify scalars, fix PARabolic code
+;       2.15    Feb 2004, W. Landsman Fix AZP and AIR algorithms
+;       3.0    May 2004  W. Landsman Support extended SIN (=NCP), slant zenithal
+;                  (SZP), and zenithal polynomial (ZPN) projections, use
+;                   PV2 keyword vector instead of PROJP1, PROJP2
+;       3.1     Jul 2005 W.Landsman/C. Markwardt Set unprojectable points in
+;                   tangent projection to NaN
+;       3.1.1   Jul 2005 Fixed 3.1 mod to work for scalars
+;       3.2     Dec 2005 Fixed Airy projection for latitude centered at 90 deg
+;       3.3     Aug 2007 R. Munoz, W.Landsman Correct treatment of PV1_2 and
+;                        PV2_2 parameters
+;       3.4    Oct 2007  Sergey Koposov Support HEALPIX projection
+;       3.4.1  June 2009 Check for range of validity of ZPN polynomial W.L.
+;       3.5    May 2012  Benjamin Alan Weaver, Add nonstandard HEALCART 
+;                        projection, Allow map_index to be > 25
+;       3.5.1  May 2013  W. Landsman Allow GLS as a synonym for SFL
+;       3.6    Jul 2013  J. P. Leahy added XPH projection, apply polar offsets
+;                        only for cylindrical & conic projections. 
+;       3.6.1  Dec 2013  W. Landsman Polar offsets done in radians
+;       3.6.2  Jan 2016  W. Landsman Lat and Long can have different size so long 
+;                        as they have the same number of elements
+;-
+
+PRO wcssph2xy,longitude,latitude,x,y,map_type, ctype=ctype,$
+              face = face, pv1 = pv1, pv2 = pv2, crval = crval, $
+              crxy = crxy, longpole = longpole, latpole = latpole, $
+              north_offset = north_offset, south_offset = south_offset, $
+              badindex = badindex
+
+compile_opt idl2, hidden
+
+
+; DEFINE ANGLE CONSTANTS
+ pi = !DPI
+ pi2 = pi/2.d0
+ radeg = 57.295779513082323d0
+ map_types=['DEF','AZP','TAN','SIN','STG','ARC','ZPN','ZEA','AIR','CYP',$
+            'CAR','MER','CEA','COP','COD','COE','COO','BON','PCO','SFL',$
+            'PAR','AIT','MOL','CSC','QSC','TSC','SZP','HPX','HCT','XPH']
+
+; check to see that enough parameters (at least 4) were sent
+ if (N_params() lt 4) then begin
+    print,'Syntax - WCSSPH2XY, longitude, latitude, x, y, [ map_type,'
+    print,'           CTYPE= ,FACE=, PV1=, PV2=, CRVAL=, CRXY=, LATPOLE='
+    print,'           LONGPOLE= ,NORTH_OFFSET=, SOUTH_OFFSET=, BADINDEX=]'
+    return
+ endif
+
+
+; GENERAL ERROR CHECKING
+; find the number of elements in each of the data arrays
+
+ n_long = N_elements( longitude )
+ n_lat  = N_elements( latitude )
+ ; check to see that the data arrays have the same size
+ if n_long NE n_lat then begin
+     message,$
+       'LONGITUDE and LATITUDE must have the same number of elements.'
+ endif
+
+ if (N_params() eq 5) then begin
+
+  if keyword_set(ctype) then message,$
+  'Use either the MAP_TYPE positional parameter or set the projection type' + $
+  ' with CTYPE, but not both.'
+
+; set projection_type string using map_type parameter (a number)
+  ntypes = n_elements(map_types)
+  if (N_ELEMENTS(map_type) eq 1 && map_type ge 0 && $
+      map_type lt ntypes) then begin
+         projection_type = map_types[map_type]
+  endif else message,'MAP_TYPE must be a scalar >= 0 and < '+$
+            strtrim(string(ntypes),2)+'; it was set to '+$
+            strtrim(string(map_type),2)
+
+endif else if (n_params() eq 4) then  wcs_check_ctype, ctype, projection_type 
+    ; checks CTYPE format and extract projection type
+
+; this sets the default map projection type for the cases when map_type or
+; projection_type is set to 'DEF' or if projection_type is not set at this
+; point.  As suggested in 'Representations of Celestial Coordinates in FITS'
+; the default type is set to CAR (Plate Caree) the simplest of all projections.
+ if ((n_elements(projection_type) eq 0) || $
+     (projection_type eq 'DEF') ) then begin
+           projection_type='CAR'
+        message, /INFORMATIONAL, $
+          'Projection type not supplied, set to default (Plate Caree)'
+ endif
+
+; Check to make sure all the correct parameters and keywords are set for
+; spherical projections.
+if (N_ELEMENTS(ctype) EQ 3 || keyword_set(face) || (projection_type eq 'CSC') || $
+    (projection_type eq 'QSC') || (projection_type eq 'TSC')) then begin
+
+  noface = n_elements(face) eq 0
+
+endif
+
+; check to see if the x and y offsets are set properly.  If not, break out
+; of program.  If the x and y offsets are not set then assume they are zero.
+if ((n_elements(crxy) ne 0) && (n_elements(crxy) ne 2)) then $
+    message,'Offset keyword CRXY must contain 2 elements'
+
+if ((n_elements(crval) ne 0) && (n_elements(crval) ne 2)) then $
+    message,'CRVAL keyword must contain 2 elements'
+
+
+; Convert all longitude values into the range -180 to 180 so that equations
+; work properly.
+  lng = double( longitude )   & lat = double( latitude )
+  temp = where(lng ge 180d0, Ntemp)
+  if Ntemp GT 0 then lng[temp] = lng[temp] - 360.0d0
+
+; Convert from standard coordinate system to "native" coordinate system
+; if the CRVAL keyword is set.  Otherwise, assume the latitude and longitude
+; given are in "native" coordinates already (this is  essentially what is done
+; in the procedure AITOFF).
+
+ PV2_1 = N_elements(pv2) GT 0 ? pv2[0] : 0
+ PV2_2 = N_elements(pv2) GT 1 ? pv2[1] : 0
+
+ if N_elements(map_type) EQ 0 then begin
+     wmt      = where(projection_type EQ map_types)
+     map_type = wmt[0]
+ endif
+
+ conic = (map_type GE 13) && (map_type LE 16)
+ zenithal = ((map_type GE 1) && (map_type LE 8)) || $
+             (map_type EQ 26) || (map_type EQ 29)
+ cylindrical = (map_type GE 9 && map_type LE 12) || $
+             map_type EQ 27 || map_type EQ 28 
+; Rotate from standard celestial coordinates into the native system.
+        if conic then theta0 = PV2_1 else if zenithal then theta0 = 90 $
+                 else theta0 = 0
+ if N_elements(crval) GE 2 then begin
+        wcs_rotate, lng, lat, phi, theta, crval, pv1 = pv1, $
+                latpole = latpole, longpole=longpole, theta0 = theta0
+	
+        phi   /= radeg
+        theta /= radeg
+ endif else begin
+     phi = lng/radeg
+     theta = lat/radeg
+ endelse
+
+  IF cylindrical || conic  THEN BEGIN
+; Make small offsets at poles to allow the transformations to be
+; completely invertible. They are necessary in cylindrical & conic 
+; projections since the pole is mapped to a line in the projection plane. 
+; These introduce a small fractional error but only at the poles. 
+;
+     IF N_elements(north_offset) EQ 0 then north_offset = 1.d-7
+     IF N_elements(south_offset) EQ 0 then south_offset = 1.d-7
+
+     bad = where(abs(theta - pi2) lt north_offset, Nbad)
+     IF (Nbad GT 0) THEN BEGIN
+         MESSAGE,/INFORM,'Some input points are too close to the NORTH pole.'
+         theta[bad] = pi2 - north_offset
+         IF KEYWORD_SET(badindex) THEN badindex = bad
+     ENDIF
+     bad = where(abs(theta + pi2) lt south_offset, Nbad)
+     IF (Nbad GT 0) THEN BEGIN
+         MESSAGE,/INFORM,'Some input points are too close to the SOUTH pole.'
+         lat[bad] = south_offset - pi2
+         IF KEYWORD_SET(badindex) THEN BEGIN
+             badindex = [badindex, bad]
+             badindex = badindex[sort(badindex)]
+         ENDIF
+     ENDIF
+ ENDIF
+ 
+; BRANCH BY MAP PROJECTION TYPE
+case strupcase(projection_type) of
+  'AZP':begin
+     if (PV2_1 lt 0) then message,$
+      'AZP map projection requires the keyword PV2_1 >= 0'
+    gamma = PV2_2/radeg
+    mu = PV2_1
+
+    r_theta = radeg*cos(theta)*(mu + 1.d0)/ $
+             ( (mu + sin(theta)) + cos(theta)*cos(phi)*tan(gamma))
+    x = r_theta*sin(phi)
+    y = -r_theta*cos(phi)/cos(gamma)
+  end
+  'SZP': begin
+     mu = N_elements(PV2) GT 0 ? PV2[0] : 0
+     phi_c = N_elements(PV2) GT 1 ? PV2[1] : 0
+     theta_c = N_elements(PV2) GT 1 ? PV2[2] : 90
+     phi_c = phi_c/radeg & theta_c = theta_c/radeg
+     xp = -mu*cos(theta_c)*sin(phi_c)
+     yp =  mu*cos(theta_c)*cos(phi_c)
+     zp =  mu*sin(theta_c) + 1.
+     denom = zp - (1-sin(theta))
+     x = radeg*( zp*cos(theta)*sin(phi) - xp*(1-sin(theta)) )/ denom
+     y = -radeg*( zp*cos(theta)*cos(phi) + yp*(1-sin(theta)) )/ denom
+
+     end
+  'TAN':begin
+    sz_theta = size(theta,/dimen)
+    if sz_theta[0] EQ 0 then x = !Values.D_NAN else $
+          x = make_array(value = !values.D_NAN, dimen=sz_theta)
+    y = x
+    g = where(theta GT 0, Ng)
+    if Ng GT 0 then begin
+        r_theta = radeg/tan(theta[g])
+        x[g] = r_theta*sin(phi[g])
+        y[g] = -r_theta*cos(phi[g])
+    endif
+  end
+
+  'SIN':begin
+    if N_elements(PV2_1) EQ 0 then PV2_1 = 0
+    if N_elements(PV2_2) EQ 0 then PV2_2 = 0
+    if (PV2_1 EQ 0) && (PV2_2 EQ 0) then begin
+        r_theta = radeg*cos(theta)
+        x = r_theta*sin(phi)
+        y = -r_theta*cos(phi)
+    endif else begin                   ;NCP projection
+        x =  radeg*(cos(theta)*sin(phi) + PV2_1*(1-sin(theta)) )
+        y = -radeg*(cos(theta)*cos(phi) - PV2_2*(1-sin(theta)) )
+    endelse
+  end
+
+  'STG':begin
+    r_theta = 2.d0*radeg*tan((pi2-theta)/2.d0)
+    x = r_theta*sin(phi)
+    y = -r_theta*cos(phi)
+  end
+
+  'ARC':begin
+    r_theta = radeg*( pi2 - theta )
+    x = r_theta*sin(phi)
+    y = -r_theta*cos(phi)
+  end
+
+  'ZPN':begin
+    z = pi2 - theta
+    g = where(pv2 NE 0, Ng)
+    np = Ng GT 0 ? max(g) : 0
+    par = pv2[0:np]
+    Nbad  = 0
+;Check for range of validity for a nonlinear polynomial.    Set the derivative
+; to zero and check for any real, positive roots.
+    if np GT 2 then begin
+          dpar = (indgen(np)+1) * par[1:*]     ;Polynomial derivative
+	  zroots = fz_roots(dpar)               ;Find zeros
+	  g = where(imaginary(zroots) EQ 0, Ng)      ;Any real roots?
+          if Ng GT 0 then zroots = float(zroots[g])
+	  g = where(zroots gt 0,Ng)
+	  if Ng GT 0 then rlim = min(zroots[g])
+	  bad = where(z GT rlim, Nbad)
+    endif
+    r_theta = radeg*poly(z, par)
+    x = r_theta*sin(phi)
+    y = -r_theta*cos(phi)
+    if Nbad GT 0 then begin
+        x[bad] = !VALUES.D_NAN
+	y[bad] = !VALUES.D_NAN
+	endif
+    end
+
+
+  'ZEA':begin
+    r_theta = 2.d0*radeg*sin((pi2 - theta)/2.d0)
+    x = r_theta*sin(phi)
+    y = -r_theta*cos(phi)
+  end
+
+  'AIR':begin
+    if ~keyword_set(PV2_1) then begin
+      message,/informational,$
+          'PV2_1 not set, using default of PV2_1 = 90 for AIR map projection'
+      PV2_1 = 9.d1
+    endif
+    theta_b = PV2_1/radeg
+
+    xi = (pi2 - theta)/2.d0
+
+; When theta_b (aka PV2_1 in radians) is equal to pi/2 the normal equations
+; for the AIR projection produce infinities.  To avoid the problem, values
+; of theta_b equal to pi/2 cause a different set of equations to be used.
+    if (theta_b eq pi2) then begin
+
+; AIR produces the same radii for different latitudes, causing some overlap.  To
+; avoid this problem, if latitudes which are far enough south to be a problem
+; are included in the data, the routine will stop.
+
+      if (min(theta) lt -36/radeg) then begin
+        message,'AIR produces overlap of native latitudes south of ',/continue
+        print,'-36 with the PV2_1 = 90'
+        return
+      endif
+
+; points with xi too small are labelled as bad to prevent poor behavior of the
+; equation for r_theta
+      good = where(abs(xi) ge 1.d-10, Ngood)
+      r_theta = lng*0
+      if (Ngood GT 0) then $
+        r_theta[good] = -2*radeg*(alog(cos(xi[good]))/tan(xi[good]) - $
+	                 0.5*tan(xi[good]))
+
+    endif else begin
+      xi_b = (pi2 - theta_b)/2.d0
+      a = alog(cos(xi_b))/tan(xi_b)/tan(xi_b)
+
+; AIR produces the same radii for different latitudes, causing some overlap.  To
+; avoid this problem, if latitudes which are far enough south to be a problem
+; are included in the data, the routine will stop.
+
+      xi_temp = (findgen(90) + 1)/radeg
+      radius=-radeg*(alog(cos(xi_temp))/tan(xi_temp)+alog(cos(xi_b))/$
+                                                      tan(xi_b)*tan(xi_temp))
+      i = 0
+      repeat i = i + 1 $
+      until ((radius[i + 1] le radius[i]) || (i eq n_elements(radius) - 2))
+      if (i lt (n_elements(radius)- 2)) then min_lat = 90 - 2*radeg*xi_temp[i] $
+      else min_lat = -90
+      if (min(theta) lt min_lat[0]/radeg) then begin
+        message,'AIR produces overlap of native latitudes south of ',/continue
+        print,format='(i3,a21,i3)',min_lat[0],' with the PV2_1 = ',PV2_1
+        return
+      endif
+
+; points with xi too small are labelled as bad to prevent poor behavior of the
+; equation for r_theta
+
+      good = where(abs(xi) ge 1.d-10, Ngood)
+      r_theta = lng*0
+      if (Ngood GT 0) then r_theta[good] = -2*radeg*(alog(cos(xi[good]))/$
+        tan(xi[good]) + a*tan(xi[good]))
+    endelse
+    x = r_theta*sin(phi)
+    y = -r_theta*cos(phi)
+  end
+
+  'CYP':begin
+    if (n_elements(PV2_1) eq 0) then begin
+      message,/informational,$
+           'PV2_1 not set, using default of PV2_1 = 0 for CYP map projection'
+      PV2_1 = 0.d0
+    endif
+    if (n_elements(PV2_2) eq 0) then begin
+      message,/informational,$
+           'PV2_2 not set, using default of PV2_2 = 1 for CYP map projection'
+      PV2_2 = 1.d0
+    endif
+    if (PV2_1 eq -PV2_2) then message,$
+      'PV2_1 = -PV2_2 is not allowed for CYP map projection.'
+
+    x = PV2_2*radeg*phi
+    y = radeg*(PV2_1 + PV2_2)*sin(theta)/(PV2_1 + cos(theta))
+  end
+
+  'CAR':begin
+    x = radeg*phi
+    y = radeg*theta
+  end
+
+  'MER':begin
+    x = radeg*phi
+    y = radeg*alog(tan((pi2 + theta)/2.d0))
+  end
+
+  'CEA':begin
+    if N_elements(PV2_1) EQ 0  then message,$
+      'CEA map projection requires that PV2_1 keyword be set.'
+    if ((PV2_1 le 0) || (PV2_1 gt 1)) then message,$
+      'CEA map projection requires 0 < PV2_1 <= 1'
+    x = radeg*phi
+    y = radeg*sin(theta)/PV2_1
+  end
+
+  'COP':begin
+    if ~keyword_set(PV2_1) then message,$
+      'COP map projection requires that PV2_1 keyword be set.'
+    if ~keyword_set(PV2_2) then begin
+      message,/informational,$
+      'PV2_2 not set, using default of PV2_2 = 0 for COP map projection'
+      PV2_2= 0
+    endif
+    if ((PV2_1 lt -90) || (PV2_2 gt 90) || (PV2_1 gt 90)) then message,$
+ 'PV2_1 and PV2_2 must satisfy -90<=PV2_1<=90,0<=PV2_2<=90 for COP projection'
+    if (PV2_1 eq -PV2_2) then message,$
+ 'COP projection with PV2_1=-PV2_2 is better done as a cylindrical projection'
+    theta_a = PV2_1/radeg
+    alpha = PV2_2/radeg
+    bad = where((theta ge theta_a + pi2) or (theta le theta_a - pi2))
+    if (bad[0] ne -1) then begin
+      message,/continue,$
+  'COP map projection diverges for native latitude = PV2_1 +- 90.'
+      message,'Remove these points and try again.'
+    endif
+
+    r_theta = radeg*cos(alpha)*(1.d0/tan(theta_a)-tan(theta-theta_a))
+    a_phi = phi*sin(theta_a)
+    y_0 = radeg*cos(alpha)/tan(theta_a)
+    x = r_theta*sin(a_phi)
+    y = y_0 - r_theta*cos(a_phi)
+
+  end
+
+  'COD':begin
+    if ~keyword_set(PV2_1) then message,$
+      'COD map projection requires that PV2_1 keyword be set.'
+    if ~keyword_set(PV2_2) then begin
+      message,/informational,$
+     'PV2_2 not set, using default of PV2_2 = 0 for COD map projection'
+      PV2_2 = 0
+    end
+    if ((PV2_1 lt -90) || (PV2_2 gt 90) || (PV2_1 gt 90)) then message,$
+ 'PV2_1 and PV2_2 must satisfy -90<=PV2_1<=90,PV2_2<=90 for COD projection'
+    if (PV2_1 eq -PV2_2) then message,$
+    'COD gives divergent equations for PV2_1 = -PV2_2'
+    theta_a = PV2_1/radeg
+
+; when PV2_1 not = PV2_2 use regular equations
+  if (PV2_2 NE 0) then begin
+      alpha = PV2_2/radeg
+      r_theta = theta_a - theta + alpha/(tan(alpha)*tan(theta_a))
+      a_phi = sin(theta_a)*sin(alpha)*phi/alpha
+      y_0 = radeg*alpha/(tan(alpha)*tan(theta_a))
+; if the two parameters PV2_1 and PV2_2 are equal use the simpler set of
+; equations
+    endif else begin
+      r_theta = theta_a - theta + 1.d0/tan(theta_a)
+      a_phi = phi*sin(theta_a)
+      y_0 = radeg/tan(theta_a)
+
+    endelse
+    x = radeg*r_theta*sin(a_phi)
+    y = y_0 - radeg*r_theta*cos(a_phi)
+
+  end
+
+  'COE':begin
+    if N_elements(PV2_1) EQ 0 then message,$
+      'COE map projection requires that PV2_1 keyword be set.'
+    if N_elements(PV2_2) EQ 0 then begin
+      message,/informational,$
+      'PV2_2 not set, using default of PV2_2 = 0 for COE map projection'
+      PV2_2 = 0
+    end
+    if ((PV2_1 lt -90) || (PV2_2 gt 90) || (PV2_1 gt PV2_2)) then message,$
+ 'PV2_1 and PV2_2 must satisfy -90<=PV2_1<=PV2_2<=90 for COE map projection'
+    if (PV2_1 eq -PV2_2) then message,$
+    'COE gives divergent equations for PV2_1 = -PV2_2'
+
+    theta_1 = (PV2_1 - PV2_2)/radeg
+    theta_2 = (PV2_1 + PV2_2)/radeg
+    s_1 = sin(theta_1)
+    s_2 = sin(theta_2)
+    stheta_a = sin(PV2_1/radeg)
+    gamma = s_1 + s_2
+    r_theta=radeg*2.d0*sqrt(1.d0+ s_1*s_2-gamma*sin(theta))/gamma
+
+     a_phi = phi*gamma/2.d0
+    y_0 = radeg*2.d0*sqrt(1.d0+ s_1*s_2-gamma*stheta_a)/gamma
+    x = r_theta*sin(a_phi)
+    y = y_0 - r_theta*cos(a_phi)
+  end
+
+  'COO':begin
+    if ~keyword_set(PV2_1) then message,$
+      'COO map projection requires that PV2_1 keyword be set.'
+    if ~keyword_set(PV2_2) then begin
+      message,/informational,$
+      'PV2_2 not set, using default of PV2_2 = 0 for COO map projection'
+      PV2_2 = 0
+    end
+    if ((PV2_1 lt -90) || (PV2_2 gt 90) || (PV2_1 gt 90)) then message,$
+ 'PV2_1 and PV2_2 must satisfy -90<=PV2_1<=90,PV2_2<=90 for COO projection'
+    if (PV2_1 eq -PV2_2) then message,$
+    'COO gives divergent equations for PV2_1 = -PV2_2'
+    theta_1 = (PV2_1 - PV2_2)/radeg
+    theta_2 = (PV2_1 + PV2_2)/radeg
+    theta_a = PV2_1/radeg
+
+
+; for cases where PV2_1 = 0, use a simpler formula to calculate c,
+; otherwise use the regular formula
+    if (PV2_2 eq 0) then c = sin(theta_1) else $
+    c = alog(cos(theta_2)/cos(theta_1))/alog(tan((pi2-theta_2)/2.d0)/$
+    tan((pi2-theta_1)/2.d0))
+
+    alpha = radeg*cos(theta_1)/(c*(tan((pi2-theta_1)/2.d0))^c)
+    r_theta = alpha*(tan((pi2-theta)/2.d0))^c
+    y_0 = alpha*tan((pi2-theta_a)/2.)^c
+    a_phi = c*phi
+    x = r_theta*sin(a_phi)
+    y = y_0 - r_theta*cos(a_phi)
+
+  end
+
+  'BON':begin
+    if (N_elements(PV2) LT 1) then message,$
+      'BON map projection requires that PV2_1 keyword be set.'
+    pv2_1 = pv2[0]
+    if ((PV2_1 lt -90) || (PV2_1 gt 90)) then message,$
+      'PV2_1 must satisfy -90 <= PV2_1 <= 90 for BON map projection'
+    if (PV2_1 eq 0) then message,$
+      'PV2_1 = 0 for BON map projection is better done with SFL map projection'
+
+    theta_1 = PV2_1/radeg
+    s = theta_1/abs(theta_1)
+    y_0 = 1.d0/tan(theta_1) + theta_1
+    a = phi*cos(theta)/(y_0 - theta)
+    x = radeg*(y_0 - theta)*sin(a)
+    y = radeg*(y_0 - (y_0 - theta)*cos(a))
+  end
+
+  'PCO':begin
+; The equations for x and y are poorly behaved for theta = 0.  Avoid this by
+; explicitly assigning values for x and y when theta = 0.
+    zero_ind = where(theta eq 0, Nzero)
+
+; create x and y with same structure as longitude
+    x = lng*0  & y = x
+    if (Nzero GT 0) then begin
+      x[zero_ind] = radeg*phi[zero_ind]
+      y[zero_ind] = 0.d0
+    endif
+    good_ind = where(theta ne 0, Ngood)
+    if Ngood GT 0 then begin
+    x[good_ind] = radeg*sin(phi[good_ind]*sin(theta[good_ind]))/$
+                  tan(theta[good_ind])
+    y[good_ind] = radeg*(theta[good_ind]+$
+        (1.d0 - cos(phi[good_ind]*sin(theta[good_ind])))/tan(theta[good_ind]))
+    endif
+  end
+
+  'SFL':begin
+    x = radeg*phi*cos(theta)
+    y = radeg*theta
+  end
+
+  'GLS':begin        ;Alternative name for SFL projection
+    x = radeg*phi*cos(theta)
+    y = radeg*theta
+  end
+
+  'PAR':begin
+    x = radeg*phi*(2.d0*cos(2.d0*theta/3.d0) - 1.d0)
+    y = 180.0*sin(theta/3.d0)
+  end
+
+  'AIT':begin
+    alpha = radeg*sqrt(2.d0/(1.d0 + cos(theta)*cos(0.5d0*phi)))
+    x = 2.d0*alpha*cos(theta)*sin(0.5d0*phi)
+    y = alpha*sin(theta)
+  end
+
+  'MOL':begin
+; Use Newton's method to find a numerical solution to the equation:
+;  alpha + 1/2*sin(2*alpha) - 1/2*pi*sin(theta) = 0
+    tolerance = 1.0d-14
+    alpha = lng*0
+    repeat begin
+    alpha_old = alpha
+    alpha = alpha_old - (alpha_old + 0.5*sin(2.d0*alpha_old) - $
+            0.5*pi*sin(theta))/(1.d0 + cos(2.d0*alpha_old))
+    endrep until (max(abs(alpha - alpha_old)) lt tolerance)
+
+    x = 2.d0^1.5*phi*radeg*cos(alpha)/pi
+    y = sqrt(2.d0)*radeg*sin(alpha)
+  end
+
+  'CSC':begin
+; calculate direction cosines
+    l = cos(theta)*sin(phi)
+    m = cos(theta)*cos(phi)
+    n = sin(theta)
+
+; determine the face on which the x and y coordinates will reside by setting
+; rho equal to the maximum of n,m,l,-m,-l,-n which corresponds to faces 0
+; through 5 respectively
+    rho =  lng*0
+    if size(lng,/N_dimen) EQ 0 then  face = 0 else face = lonarr(n_long)
+
+; use an array to store a remapping of the direction cosines.  This way, faces
+; 0 and 5 take points on their borders with faces 1-4.  The reason for this is
+; that if the max function sees identical values in an array, it takes the
+; index of the first occurrence of that value.
+    remap = [0,5,2,1,4,3]
+
+    for i = 0l, n_long-1 do begin
+      dir_cos = float([n[i],-n[i],l[i],m[i],-l[i],-m[i]])
+      rho[i] = max(dir_cos,temp)
+      face[i] = remap[temp]
+    endfor
+
+; based on the face determined for each point, find the parameters alpha and
+; beta1
+    alpha = lng*0
+    beta1 = alpha
+    for i = 0l, n_long-1 do begin
+      case face[i] of
+        0:begin
+          alpha[i] = l[i]/n[i]
+          beta1[i] = -m[i]/n[i]
+        end
+        1:begin
+          alpha[i] = l[i]/m[i]
+          beta1[i] = n[i]/m[i]
+        end
+        2:begin
+          alpha[i] = -m[i]/l[i]
+          beta1[i] = n[i]/l[i]
+        end
+        3:begin
+          alpha[i] = l[i]/m[i]
+          beta1[i] = -n[i]/m[i]
+        end
+        4:begin
+          alpha[i] = -m[i]/l[i]
+          beta1[i] = -n[i]/l[i]
+        end
+        5:begin
+          alpha[i] = -l[i]/n[i]
+          beta1[i] = -m[i]/n[i]
+        end
+      endcase
+    end
+
+; define all of the numerical constants to use in determining x and y
+    r_0 = 0.577350269
+    gam_s = 1.37484847732
+    em = 0.004869491981
+    gam = -0.13161671474
+    ome = -0.159596235474
+    d_0 = 0.0759196200467
+    d_1 = -0.0217762490699
+    c_00 = 0.141189631152
+    c_10 = 0.0809701286525
+    c_01 = -0.281528535557
+    c_20 = -0.178251207466
+    c_11 = 0.15384112876
+    c_02 = 0.106959469314
+    fconst = 45.0d0
+
+    x = fconst*(alpha*gam_s+alpha^3*(1-gam_s)+alpha*beta1^2*(1-alpha^2)*$
+        (gam+(em-gam)*alpha^2+(1-beta1^2)*(c_00+c_10*alpha^2+c_01*beta1^2+$
+        c_20*alpha^4+c_11*alpha^2*beta1^2+c_02*beta1^4))+alpha^3*(1-alpha^2)*$
+        (ome-(1-alpha^2)*(d_0+d_1*alpha^2)))
+    y = fconst*(beta1*gam_s+beta1^3*(1-gam_s)+beta1*alpha^2*(1-beta1^2)*$
+        (gam+(em-gam)*beta1^2+(1-alpha^2)*(c_00+c_10*beta1^2+c_01*alpha^2+$
+        c_20*beta1^4+c_11*beta1^2*alpha^2+c_02*alpha^4))+beta1^3*(1-beta1^2)*$
+        (ome-(1-beta1^2)*(d_0+d_1*beta1^2)))
+
+
+    if noface eq 1 then begin
+        xf=fconst*[0.0d0,0.0d0,2.0d0,4.0d0,6.0d0,0.0d0]
+        yf=fconst*[2.0d0,0.0d0,0.0d0,0.0d0,0.0d0,-2.0d0]
+        x=x+xf[face]
+        y=y+yf[face]
+    endif
+  end
+
+  'QSC':begin
+; calculate direction cosines
+    l = cos(theta)*sin(phi)
+    m = cos(theta)*cos(phi)
+    n = sin(theta)
+
+; determine the face on which the x and y coordinates will reside by setting
+; rho equal to the maximum of n,m,l,-m,-l,-n which corresponds to faces 0
+; through 5 respectively
+    rho = lng*0
+    if size(lng,/N_dimen) EQ 0 then face = 0 else face = lonarr(n_long)
+
+; use an array to store a remapping of the direction cosines.  This way, faces
+; 0 and 5 take points on their borders with faces 1-4.  The reason for this is
+; that if the max function sees identical values in an array, it takes the
+; index of the first occurrence of that value.
+    remap = [0,5,2,1,4,3]
+
+    for i = 0l, n_long-1 do begin
+      dir_cos = float([n[i],-n[i],l[i],m[i],-l[i],-m[i]])
+      rho[i] = max(dir_cos,temp)
+      face[i] = remap[temp]
+    endfor
+
+; based on the face determined for each point, find the parameters alpha and
+; beta1
+    alpha = lng*0
+    beta1 = alpha
+    for i = 0l, n_long-1 do begin
+      case face[i] of
+        0:begin
+          alpha[i] = l[i]/n[i]
+          beta1[i] = -m[i]/n[i]
+        end
+        1:begin
+          alpha[i] = l[i]/m[i]
+          beta1[i] = n[i]/m[i]
+        end
+        2:begin
+          alpha[i] = -m[i]/l[i]
+          beta1[i] = n[i]/l[i]
+        end
+        3:begin
+          alpha[i] = l[i]/m[i]
+          beta1[i] = -n[i]/m[i]
+        end
+        4:begin
+          alpha[i] = -m[i]/l[i]
+          beta1[i] = -n[i]/l[i]
+        end
+        5:begin
+          alpha[i] = -l[i]/n[i]
+          beta1[i] = -m[i]/n[i]
+        end
+      endcase
+    end
+
+    x = lng*0
+    y = x &  xi = y
+
+    s = 2.d0*(((alpha gt abs(beta1)) or (beta1 ge abs(alpha))) - 0.5d0)
+
+    case_1 = where(abs(alpha) gt abs(beta1))
+    case_2 = where((abs(alpha) le abs(beta1)) and (beta1 ne 0.d0))
+    case_3 = where((alpha eq 0.d0) and (beta1 eq 0.d0))
+    if (case_1[0] ne -1) then xi[case_1] = beta1[case_1]/alpha[case_1]
+    if (case_2[0] ne -1) then xi[case_2] = alpha[case_2]/beta1[case_2]
+    if (case_3[0] ne -1) then xi[case_3] = 0.d0
+
+    fconst=45.0d0
+    u = fconst*s*sqrt((1.d0 - rho)/(1.d0 - 1.d0/sqrt(2.d0 + xi^2)))
+    v = (u/1.5d1)*radeg*(atan(xi) - asin(xi/sqrt(2.d0*(1.d0 + xi^2))))
+    if (case_1[0] ne -1) then begin
+      x[case_1] = u[case_1]
+      y[case_1] = v[case_1]
+    endif
+    if (case_2[0] ne -1) then begin
+      x[case_2] = v[case_2]
+      y[case_2] = u[case_2]
+    endif
+    if (case_3[0] ne -1) then begin
+      x[case_3] = 0.d0
+      y[case_3] = 0.d0
+    endif
+
+    if noface eq 1 then begin
+        xf=fconst*[0.0d0,0.0d0,2.0d0,4.0d0,6.0d0,0.0d0]
+        yf=fconst*[2.0d0,0.0d0,0.0d0,0.0d0,0.0d0,-2.0d0]
+        x=(x+xf[face])
+        y=(y+yf[face])
+    endif
+  end
+
+  'TSC':begin
+; calculate direction cosines
+    l = cos(theta)*sin(phi)
+    m = cos(theta)*cos(phi)
+    n = sin(theta)
+
+; determine the face on which the x and y coordinates will reside by setting
+; rho equal to the maximum of n,m,l,-m,-l,-n which corresponds to faces 0
+; through 5 respectively
+    rho = lng*0
+    if size(lng,/N_dimen) EQ 0 then face = 0 else face = lonarr(n_long)
+
+; use an array to store a remapping of the direction cosines.  This way, faces
+; 0 and 5 take points on their borders with faces 1-4.  The reason for this is
+; that if the max function sees identical values in an array, it takes the
+; index of the first occurrence of that value.
+    remap = [0,5,2,1,4,3]
+
+    for i = 0l, n_long-1 do begin
+      dir_cos = float([n[i],-n[i],l[i],m[i],-l[i],-m[i]])
+      rho[i] = max(dir_cos,temp)
+      face[i] = remap[temp]
+    endfor
+
+; based on the face determined for each point, find the parameters eta and xi
+    eta = lng*0
+    xi = eta
+    for i = 0l, n_long-1 do begin
+      case face[i] of
+        0:begin
+          eta[i] = -m[i]
+          xi[i] = l[i]
+        end
+        1:begin
+          eta[i] = n[i]
+          xi[i] = l[i]
+        end
+        2:begin
+          eta[i] = n[i]
+          xi[i] = -m[i]
+        end
+        3:begin
+          eta[i] = n[i]
+          xi[i] = -l[i]
+        end
+        4:begin
+          eta[i] = n[i]
+          xi[i] = m[i]
+        end
+        5:begin
+          eta[i] = m[i]
+          xi[i] = l[i]
+        end
+      endcase
+    endfor
+    fconst = 45.0d0
+    r_theta = fconst/tan(asin(rho))
+    a_phi = atan(xi,-eta)
+    x = r_theta*sin(a_phi)
+    y = -r_theta*cos(a_phi)
+    if noface eq 1 then begin
+        xf=fconst*[0.0d0,0.0d0,2.0d0,4.0d0,6.0d0,0.0d0]
+        yf=fconst*[2.0d0,0.0d0,0.0d0,0.0d0,0.0d0,-2.0d0]
+        x=(x+xf[face])
+        y=(y+yf[face])
+    endif
+  end
+
+  'HPX': begin
+;
+; See Calabretta & Roukema 2007, MNRAS, 381, 865
+;
+      pv2_1 = N_ELEMENTS(pv2) GE 1 ? pv2[0] : 4.d
+      pv2_2 = N_ELEMENTS(pv2) GE 2 ? pv2[1] : 3.d
+      hpx_k = pv2_2                  ; The main generalised HEALPIX parameters
+      hpx_h = pv2_1                  ;
+      ik = ROUND(hpx_k)
+      ih = ROUND(hpx_h)
+
+      thetalim = asin((hpx_k-1d0)/hpx_k)
+
+      eqfaces = where( abs(theta) le thetalim, complement=polfaces)
+      x = phi  ; make x & y arrays in same shape as phi/theta.  
+      y = phi
+
+; equatorial region  
+      if eqfaces[0] ne -1 then begin
+          x[eqfaces] = phi[eqfaces]*radeg
+          y[eqfaces] = (90d * hpx_k / hpx_h) * sin( theta[eqfaces])
+      endif
+
+;polar regions
+      if polfaces[0] ne -1 then begin
+          hpx_sig = sqrt ( hpx_k * (1d0 - abs(sin(theta[polfaces]))))
+          hpx_omega = ((hpx_k mod 2 eq 1) or theta[polfaces] gt 0)*1.D
+          hpx_phic = -180d0 + (2*floor((phi[polfaces]*radeg+180d0)*hpx_h/360d0 + $
+                                  (1-hpx_omega)/2.) + hpx_omega)*180d0/hpx_h
+          x[polfaces] = hpx_phic + (phi[polfaces]*radeg-hpx_phic) * hpx_sig
+          y[polfaces] = 180./hpx_h * ((theta[polfaces] gt 0)*2-1) * $
+                                ((hpx_k+1)/2 - hpx_sig)
+      endif
+  end
+  'HCT':begin
+    x = phi*radeg
+    y = DBLARR(N_ELEMENTS(theta))
+    thetalim = ASIN(2.D/3.D)
+    w_np = WHERE(theta GE thetalim, n_np)
+    w_eq = WHERE((theta LT thetalim) AND (theta GT -thetalim), n_eq)
+    w_sp = WHERE(theta LE -thetalim, n_sp)
+    IF n_np GT 0 THEN y[w_np] = 45.D*(2.D - SQRT(3.D*(1.D - SIN(theta[w_np]))))
+    IF n_eq GT 0 THEN y[w_eq] = 45.D*(3.D/2.D)*SIN(theta[w_eq])
+    IF n_sp GT 0 THEN y[w_sp] = 45.D*(SQRT(3.D*(1.D + SIN(theta[w_sp])))-2.D)
+  end
+
+  'XPH':begin
+;
+; HEALPix butterfly projection: see Calabretta & Lowe (2013)
+; 
+    scale = 1d0/sqrt(2d0)
+    thetalim = asin(2d0/3d0)
+    out_of_range = WHERE(phi EQ !dpi, nout)
+    IF nout GT 0 THEN phi[out_of_range] = -!dpi
+    xi  = phi           ; get array of same shape
+    eta = SIN(theta)
+    test = 0*FIX(xi)
+    psi = (phi*radeg + 180d0) mod 90d0
+    eqfaces = where(abs(theta) le thetalim, complement=polfaces)
+    IF eqfaces[0] NE -1 THEN BEGIN
+        xi[eqfaces]  = psi[eqfaces]
+        eta[eqfaces] *= 67.5d0
+    ENDIF
+    IF polfaces[0] NE -1 THEN BEGIN
+        hpx_sigma = SQRT(3d0*(1d0 - ABS(eta[polfaces])))
+        xi[polfaces] = 45d0 + (psi[polfaces] - 45d0)*hpx_sigma
+        sgn = 2*(theta[polfaces] GT 0) - 1
+        eta[polfaces] = TEMPORARY(sgn)*(90d0 - 45d0*hpx_sigma)
+    ENDIF
+    psi = 0
+    xi  -= 45d0
+    eta -= 90d0
+    x = xi + eta
+    y = TEMPORARY(xi) - TEMPORARY(eta)
+    quad = WHERE((-!dpi LE phi) AND (phi LT -pi2))
+    IF quad[0] NE -1 THEN BEGIN
+        temp    = x[quad]
+        x[quad] = -y[quad]
+        y[quad] = -temp
+        test[quad] = 1
+    ENDIF
+    quad = WHERE((-pi2 LE phi) AND (phi LT 0))
+    IF quad[0] NE -1 THEN BEGIN
+        y[quad] *= -1d0
+        test[quad] = 1
+    ENDIF
+    quad = WHERE((0d0 LE phi) AND (phi LT pi2))
+    IF quad[0] NE -1 THEN BEGIN
+        temp    = x[quad] 
+        x[quad] = y[quad]
+        y[quad] = temp
+        test[quad] = 1
+    ENDIF
+    quad = WHERE((pi2 LE phi) AND (phi LT !dpi))
+    IF quad[0] NE -1 THEN BEGIN
+        x[quad]  *= -1d0
+        test[quad] = 1
+    ENDIF
+    quad = 0
+    x *= scale
+    y *= scale
+  end
+  else:message,strupcase(projection_type)+$
+               ' is not a valid projection type.  Reset CTYPE'
+endcase
+
+if keyword_set(crxy) && ~array_equal(crxy, [0d0,0d0]) then begin
+    x = x - crxy[0]
+    y = y - crxy[1]
+endif
+
+END
diff --git a/Code/script_idl_mv/astrolib/wcsxy2sph.pro b/Code/script_idl_mv/astrolib/wcsxy2sph.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8ad32099a03b6f41d8d1b9d1e4e48fd81eea0067
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/wcsxy2sph.pro
@@ -0,0 +1,1447 @@
+;+
+; NAME:
+;      WCSXY2SPH
+;
+; PURPOSE:
+;      Convert x and y (map) coordinates to spherical coordinates
+; EXPLANATION:
+;      To convert x and y (map) coordinates to spherical (longitude and
+;      latitude or sky) coordinates.    This procedure is the inverse of
+;      WCSSPH2XY.
+;
+;     This is a lower level procedure -- given a FITS header, the user will
+;     usually use XYAD which will then call WCSXY2SPH with the appropriate
+;     parameters.
+; CATEGORY:
+;      Mapping and Auxilliary FITS Routine
+;
+; CALLING SEQUENCE:
+;
+;      wcsxy2sph, x, y, longitude, latitude, [map_type], [ CTYPE = ,$
+;             FACE = , PV1 =, PV2 = ,CRVAL =, CRXY =, LONGPOLE=, LATPOLE=]
+;
+; INPUT PARAMETERS:
+;
+;       x - x coordinate of data, scalar or vector, in degrees, NOTE: x
+;               increases to the left, not the right
+;       y - y coordinate of data, same number of elements as x, in degrees
+;       map_type - optional positional parameter, scalar corresponding to a
+;               particular map projection.  This is not a FITS standard, it is
+;               simply put in to allow function similar to that of less general
+;               map projection procedures (eg AITOFF).  The following list gives
+;               the map projection types and their respective numbers.
+;
+;  FITS  Number  Name                       Comments
+;  code   code
+;  ----  ------  -----------------------    -----------------------------------
+;   DEF     0    Default = Plate Carree
+;   AZP     1    Zenithal perspective       pv2_1 required
+;   TAN     2    Gnomic                     AZP w/ pv2_1 = 0
+;   SIN     3    Orthographic               pv2_1, pv2_2 optional
+;   STG     4    Stereographic              AZP w/ pv2_1 = 1
+;   ARC     5    Zenithal Equidistant
+;   ZPN     6    Zenithal polynomial        PV2_0, PV2_1....PV2_20 possible
+;   ZEA     7    Zenithal equal area
+;   AIR     8    Airy                       pv2_1 required
+;   CYP     9    Cylindrical perspective    pv2_1 and pv2_2 required
+;   CAR    10    Plate Carree
+;   MER    11    Mercator
+;   CEA    12    Cylindrical equal area     pv2_1 required
+;   COP    13    Conical perspective        pv2_1 and pv2_2 required
+;   COD    14    Conical equidistant        pv2_1 and pv2_2 required
+;   COE    15    Conical equal area         pv2_1 and pv2_2 required
+;   COO    16    Conical orthomorphic       pv2_1 and pv2_2 required
+;   BON    17    Bonne's equal area         pv2_1 required
+;   PCO    18    Polyconic
+;   SFL    19    Sanson-Flamsteed (GLS is allowed as a synonym for SFL)
+;   PAR    20    Parabolic
+;   AIT    21    Hammer-Aitoff
+;   MOL    22    Mollweide
+;   CSC    23    Cobe Quadrilateralized     inverse converges poorly
+;                Spherical Cube
+;   QCS    24    Quadrilateralized
+;                Spherical Cube
+;   TSC    25    Tangential Spherical Cube
+;   SZP    26    Slant Zenithal perspective  PV2_1,PV2_2, PV2_3 optional
+;   HPX    27    HEALPix projection          pv2_1 and pv2_2 optional
+;   HCT    28    HealCart (Cartesian approximation of Healpix)
+;   XPH    29    HEALPix butterfly projection (centred on a pole)
+;
+; OPTIONAL KEYWORD PARAMETERS:
+;
+;       CTYPE - One, two, or three element vector containing 8 character
+;               strings corresponding to the CTYPE1, CTYPE2, and CTYPE3
+;               FITS keywords:
+;
+;               CTYPE[0] - first four characters specify standard system
+;               ('RA--','GLON' or 'ELON' for right ascension, galactic
+;               longitude or ecliptic longitude respectively), second four
+;               letters specify the type of map projection (eg '-AIT' for
+;               Aitoff projection)
+;               CTYPE[1] - first four characters specify standard system
+;               ('DEC-','GLAT' or 'ELAT' for declination, galactic latitude
+;               or ecliptic latitude respectively; these must match
+;               the appropriate system of ctype1), second four letters of
+;               ctype2 must match second four letters of ctype1.
+;               CTYPE[2] - if present must be the 8 character string,'CUBEFACE',
+;               only used for spherical cube projections to identify an axis
+;               as containing the face on which each x and y pair of
+;               coordinates lie.
+;       FACE - a input variable used for spherical cube projections to
+;               designate the face of the cube on which the x and y
+;               coordinates lie.   Must contain the same number of elements
+;               as X and Y.
+;       CRVAL - 2 element vector containing standard system coordinates (the
+;               longitude and latitude) of the reference point
+;       CRXY - 2 element vector giving the x and y coordinates of the
+;               reference point, if this is not set the offset of the x
+;               coordinate is assumed to be 0. 
+;               See Calabretta & Griesen Sec 2.5.
+;       PV2  - Vector of projection parameter associated with latitude axis
+;             PV2 will have up to 21 elements for the ZPN projection, up to 3
+;             for the SIN projection and no more than 2 for any other
+;             projection.   The first element corresponds to PV2_1, the
+;             second to PV2_2, etc.
+
+;    Parameters simply passed to WCS_ROTATE:
+;  
+;     CRVAL - 2 element vector containing standard system coordinates (the
+;               longitude and latitude) of the reference point
+;     PV1   - Vector of projection parameters associated with longitude
+;     LONGPOLE -  native longitude of standard system's North Pole
+;     LATPOLE  -  "target" native latitude of the standard system's North Pole
+;
+; OUTPUT PARAMETERS:
+;
+;       longitude - longitude of data, same number of elements as x, in degrees
+;       latitude - latitude of data, same number of elements as x, in degrees
+;
+;       Longitude and latitude will be set to NaN, wherever elements of X,Y
+;       have no corresponding longitude, latitude values.
+; NOTES:
+;       The conventions followed here are described in more detail in the paper
+;      "Representations of Celestial Coordinates in FITS" by Calabretta &
+;       Greisen (2002, A&A, 395, 1077, also see
+;       http://fits.gsfc.nasa.gov/fits_wcs.html).   The general scheme
+;       outlined in that article is to convert x and y coordinates into a
+;       "native" longitude and latitude and then rotate the system into one of
+;       three generally recognized systems (celestial, galactic or ecliptic).
+;
+;       This procedure necessitates two basic sections.  The first converts
+;       x and y coordinates to "native" coordinates while the second converts
+;       "native" to "standard" coordinates.  The first section contains the
+;       guts of the code in which all of the map projection is done.  The
+;       second step is performed by WCS_ROTATE and only involves rotation of
+;       coordinate systems.  WCSXY2SPH can be called in a form similar to
+;       AITOFF, EQPOLE, or QDCB by calling wcsxy2sph with a fifth parameter
+;       specifying the map projection by number and by not using any of the
+;       keywords related to the map projection type (eg ctype1 and ctyp2).
+;
+; PROCEDURE:
+;       The first task of the procedure is to do general error-checking to
+;       make sure the procedure was called correctly and none of the
+;       parameters or keywords conflict.  This is particularly important
+;       because the procedure can be called in two ways (either using
+;       FITS-type keywords or using a number corresponding a map projection
+;       type).  All variables are converted into double precision values.
+;
+;       The second task of the procedure is to take x and y coordinates and
+;       convert them into "native" latitude and longitude coordinates.
+;       Map-specific error-checking is done at this time.  All of the
+;       equations were obtained from "Representations of Celestial
+;       Coordinates in FITS" and cases needing special attention are handled
+;       appropriately (see the comments with individual map projections for
+;       more information on special cases).     WCS_ROTATE is then called to
+;       convert the "native" coordinates to "standard" coordinates by rotating
+;       the coordinate system.  This rotation is governed by the keywords
+;       CRVAL, and LONGPOLE.  The transformation is a straightforward
+;       application of euler angles.  Finally, longitude values are converted
+;       into the range from 0 to 360 degrees.
+;
+; COMMON BLOCKS:
+;       none
+; PROCEDURES CALLED:
+;       WCS_ROTATE
+;
+; AUTHOR:
+;
+;       Rick Balsano
+;
+; MODIFICATIONS/REVISION LEVEL:
+;
+; 1.1    8/31/93
+; 1.2    9/12/93   W. Landsman Vectorized CRXY, CRVAL, CTYPE
+; 1.3    29/12/93  I. Freedman Eliminated LU decomposition
+; 1.4    22/09/94  W. Landsman If scalar input, then scalar output
+; 1.5    02/03/05  W. Landsman Change variable name BETA for V4.0 compatibility
+; 1.6    06/07/05  W. Landsman Change loop index from integer to long
+; 1.7    02/18/99  W. Landsman Fixed implementation of ARC algorithm
+; 1.8    June 2003 W. Landsman Update conic projections, add LATPOLE keyword
+; 1.81   Sep 2003 W. Landsman Avoid divide by zero
+; 1.82   Sep 2003 W. Landsman CTYPE keywords need not be 8 characters
+; 1.83   Sep 2003 W. Landsman Preserve input array sizes
+; 1.9    Jan 2004 W. Landsman don't modify scalars, fix PARabolic code
+; 2.0    Feb 2004 W. Landsman Fix AIR and AZP projections
+; 2.1    Feb 2004 W. Landsman Fix tangent projection for matrix input
+; 3.0    May 2004  W. Landsman Support extended SIN (=NCP), slant zenithal
+;                  (SZP), and zenithal polynomial (ZPN) projections, use
+;                   PV2 keyword vector instead of PROJP1, PROJP2
+; 3.1    May 2004 W. Landsman/J. Ballet Handle NaN values, flag invalid output
+;                   for AITOFF projection
+; 3.1.1  Dec 2005 W. Landsman/W. Thompson Fixed problem with Airy projection
+;                   centered on 90 degree latitude
+; 3.1.2  May 2006 W. Landsman/Y.Sato Fix problem selecting the correct root
+;                    for the ZPN projection
+; 3.2    Aug 2007  W. Landsman Correct treatment of PVi_j parameters
+; 3.3    Oct 2007  Sergey Koposov Support HEALPIX projection
+; 3.4    May 2012  Benjamin Alan Weaver, Add nonstandard HEALCART 
+;                        projection, Allow map_index to be > 25
+; 3.4.1  May 2013  W. Landsman Allow GLS as a synonym for SFL 
+; 3.5    Jul 2013  J. P. Leahy: Add nonstandard XPH projection and 
+;                        improved HEALPix support; changed sign of CRXY
+;                        for consistency with WCSSPH2XY; introduced PV1
+; 3.5.1  Dec 2013  W. Landsman Return scalar for scalar input for ZPN proj.
+;-
+PRO wcsxy2sph, x, y, longitude, latitude, map_type, ctype = ctype, $
+              face=face, pv2 = pv2, pv1 = pv1, $
+              crval=crval, crxy = crxy, longpole = longpole, Latpole = latpole
+
+ compile_opt idl2, hidden
+ 
+; Define angle constants
+ pi = !DPI
+ radeg = 180d0/!dpi
+ pi2 = pi/2.0d
+ ;
+ ; Please keep the following list up to date in extast as well as here
+ ; and in wcssph2xy:
+ ;
+ map_types=['DEF','AZP','TAN','SIN','STG','ARC','ZPN','ZEA','AIR','CYP',$
+            'CAR','MER','CEA','COP','COD','COE','COO','BON','PCO','SFL',$
+            'PAR','AIT','MOL','CSC','QSC','TSC','SZP','HPX','HCT','XPH']
+
+; check to see that enough parameters (at least 4) were sent
+ if ( N_params() lt 4 ) then begin
+    print,'Syntax - WCSXY2SPH, x, y, longitude, latitude,[ map_type,  '
+    print,'             CTYPE= , FACE=, PV2= , CRVAL= , CRXY= , '
+    print,'             LATPOLE = , LONGPOLE = ]'
+    return
+ endif else if (n_params() eq 5) then begin
+    if keyword_set(ctype) then message,$
+  'Use either the MAP_TYPE positional parameter or set the projection type' + $
+    'with CTYPE, but not both.'
+
+; set projection_type string using map_type parameter (a number)
+  ntypes = n_elements(map_types)
+  if (N_ELEMENTS(map_type) eq 1 && map_type ge 0 && $
+      map_type lt ntypes) then begin
+         projection_type = map_types[map_type]
+  endif else message,'MAP_TYPE must be a scalar >= 0 and < '+$
+            strtrim(string(ntypes),2)+'; it was set to '+$
+            strtrim(string(map_type),2)
+
+endif else if (n_params() eq 4) then wcs_check_ctype, ctype, projection_type 
+    ; checks CTYPE format and extract projection type
+
+; GENERAL ERROR CHECKING
+
+; find the number of elements in each of the data arrays
+ n_x = n_elements(x)
+ n_y = n_elements(y)
+ sz_x = size(x)
+ sz_y = size(y)
+; check to see that the data arrays have the same size
+ if (n_x ne n_y) then $
+        message,'The arrays X and Y must have the same number of elements.'
+
+; this sets the default map projection type for the cases when map_type or
+; projection_type is set to 'DEF' or if projection_type is not set at this
+; point.  As suggested in 'Representations of Celestial Coordinates in FITS'
+; the default type is set to CAR (Plate Carree) the simplest of all projections.
+if (n_elements(projection_type) eq 0) || (projection_type eq 'DEF') then $
+   projection_type='CAR'
+
+; Check to make sure all the correct parameters and keywords are set for
+; spherical projections.
+ if ((N_elements(ctype) EQ 3) || keyword_set(face) ||  $
+    (projection_type eq 'CSC') || $
+    (projection_type eq 'QSC') || (projection_type eq 'TSC')) then begin
+
+  noface =  ~keyword_set(face)
+
+endif
+
+; check to see if the x and y offsets are set properly.  If not, break out
+; of program.  If so, apply offsets.  If the x and y offsets are not set,
+; then assume they are zero.
+
+ if ( (keyword_set(crxy)) && N_elements(crxy) NE 2) then $
+     message,'Offset keyword CRXY must contain 2 elements'
+
+ if keyword_set(crxy) && ~array_equal(crxy,[0d0,0d0]) then begin
+        xx = double(x + crxy[0] )
+        yy = double(y + crxy[1] )
+ endif else begin
+        xx = double(x)
+        yy = double(y)
+ endelse
+
+ if  ( N_elements(crval) eq 1 ) then $
+        message,'CRVAL keyword must be a 2 element vector'
+
+; BRANCH BY MAP PROJECTION TYPE
+case strupcase(projection_type) of
+  'AZP':begin
+  PV2_1 = N_elements(PV2) GE 1 ? PV2[0] : 0.0  ; PV2_1 =mu (spherical radii)
+  PV2_2 = N_elements(PV2) GE 2 ? PV2[1] : 0.0  ; PV2_2 = gamma (degrees)
+
+    if (pv2_1 lt 0) then message,$
+      'AZP map projection requires the keyword pv2_1 >= 0'
+    gamma = pv2_2/radeg
+    mu = pv2_1
+    r = sqrt(xx^2 + yy^2*cos(gamma)^2)
+    rho = r/(radeg*(mu+1) + yy*sin(gamma) )
+    omega = asin( rho*mu/ sqrt( rho^2 + 1.d0) )
+    xsi = atan(1.d0, rho)
+    phi = atan(xx, -yy*cos(gamma) )
+    theta1 = xsi - omega
+    theta2 = xsi + omega + !dpi
+    theta = theta1*0.0
+    if abs(mu) LT 1 then begin
+          g = where(abs(theta1) LT pi2, Ng)
+          if Ng GT 0 then theta[g] = theta1[g]
+          g = where(abs(theta2) LT pi2, Ng)
+          if Ng GT 0 then theta[g] = theta2[g]
+    endif else begin
+          diff1 = abs(pi2 - theta1)
+          diff2 = abs(pi2 - theta2)
+          g = where((diff1 le diff2), Ng)
+          if Ng GT 0 then theta[g] = theta1[g]
+          g = where( (diff2 LT diff1) , Ng)
+          if Ng GT 0 then theta[g] = theta2[g]
+    endelse
+
+  end
+  'SZP': begin
+
+       mu = N_elements(PV2) GT 0 ? PV2[0] : 0
+       phi_c = N_elements(PV2) GT 1 ? PV2[1] : 0
+       theta_c = N_elements(PV2) GT 2 ? PV2[2] : 90.0
+
+       phi_c = phi_c/radeg & theta_c = theta_c/radeg
+       xp = -mu*cos(theta_c)*sin(phi_c)
+       yp =  mu*cos(theta_c)*cos(phi_c)
+       zp =  mu*sin(theta_c) + 1.
+
+       xx = xx/radeg  &  yy = yy/radeg
+       xb = (xx - xp)/zp & yb = (yy - yp)/zp
+       a = xb^2 + yb^2 + 1
+       b = xb*(xx - xb) + yb*(yy - yb)
+       c = (xx - xb)^2 + (yy - yb)^2 - 1.
+       rad = sqrt(b^2 - a*c)
+       rad1 = (-b + rad)/a
+       rad2 = (-b - rad)/a
+       arad1 = abs(rad1)
+       arad2 = abs(rad2)
+       rad = rad*0.
+       g = where((arad1 LE pi2) and (arad2 GT pi2), Ng )
+       if Ng GT 0 then rad[g] = rad1[g]
+       g = where((arad2 LE pi2) and (arad1 GT pi2), Ng )
+       if Ng GT 0 then rad[g] = rad2[g]
+       g = where((arad2 LE pi2) and (arad1 LE pi2), Ng )
+       if Ng GT 0 then rad[g] = rad2[g] > rad1[g]
+       theta = asin(rad)
+       phi = atan( xx - xb*(1-sin(theta)), -(yy - yb*(1-sin(theta))) )
+        end
+
+  'TAN':begin
+    sz_x = size(xx,/dimen)
+    if sz_x[0] EQ 0 then theta = pi2 else $
+        theta = make_array(value=pi2,dimen = sz_x)     ;Default is 90 degrees
+    r = sqrt(xx^2 + yy^2)
+    g = where(r GT 0, Ng)
+    if Ng GT 0 then theta[g] = atan(radeg/r[g])
+    phi = atan(xx,-yy)
+  end
+
+  'SIN':begin
+
+    PV2_1 = N_elements(PV2) GE 1 ? PV2[0] : 0.0
+    PV2_2 = N_elements(PV2) GE 2 ? PV2[1] : 0.0
+
+    if (pv2_1 EQ 0) && (pv2_2 EQ 0) then begin
+       theta = acos(sqrt(xx^2 + yy^2)/radeg)
+       phi = atan(xx,-yy)
+    endif else begin
+       x = xx/radeg & y = yy/radeg
+       a = pv2_1^2 + pv2_2^2 + 1
+       b = pv2_1*(x - pv2_1) + pv2_2*(y - pv2_2)
+       c = (x - pv2_1)^2 + (y - pv2_2)^2 - 1.
+       rad = sqrt(b^2 - a*c)
+       rad1 = (-b + rad)/a
+       rad2 = (-b - rad)/a
+       arad1 = abs(rad1)
+       arad2 = abs(rad2)
+       rad = rad*0.
+       g = where((arad1 LE pi2) and (arad2 GT pi2), Ng )
+       if Ng GT 0 then rad[g] = rad1[g]
+       g = where((arad2 LE pi2) and (arad1 GT pi2), Ng )
+       if Ng GT 0 then rad[g] = rad2[g]
+       g = where((arad2 LE pi2) and (arad1 LE pi2), Ng )
+       if Ng GT 0 then rad[g] = rad2[g] > rad1[g]
+       theta = asin(rad)
+       phi = atan( x - pv2_1*(1-sin(theta)), -(y - pv2_2*(1-sin(theta))) )
+   endelse
+  end
+
+  'STG':begin
+    theta = pi2 - 2*atan(sqrt(xx^2 + yy^2)/(2.d0*radeg))
+    phi = atan(xx, -yy)
+  end
+
+  'ARC':begin
+    theta = pi2 - sqrt(xx^2 + yy^2)/radeg
+    phi = atan(xx, -yy)
+  end
+
+  'ZPN':  begin
+   rtheta = sqrt(xx^2 + yy^2)/radeg
+   phi = atan(xx, -yy)
+   g = where(pv2 NE 0, Ng)
+   if Ng GT 0 then np = max(g) else np =0
+   pv2 = pv2[0:np]
+   n = N_elements(xx)
+   theta = dblarr(n)
+   for i=0, n-1 do begin
+      pv = pv2
+      pv[0] = pv[0] - rtheta[i]
+      gamma = fz_roots(pv)
+; Want only the real roots
+   good = where( imaginary(gamma) EQ 0, Ng)
+   if Ng EQ 0 then message,'ERROR in ZPN computation: no real roots found'
+   gamma = double( gamma[good])
+
+; If multiple real roots are found, then we seek the value closest to the
+; approximate linear solution
+
+   if Ng GT 1 then begin
+        gamma1 = -pv[0]/pv[1]
+        dgmin = min(abs(gamma - gamma1), dgmin_index)
+        gamma = gamma[dgmin_index]
+      good = where( (gamma GE -pi2) and (gamma LE pi2), Ng)
+      if Ng EQ 0 then gamma = gamma[0] else gamma = gamma[good[0]]
+   endif
+   theta[i] = pi2 - gamma
+   if size(yy,/N_dimen) EQ 0 then theta = theta[0]    ;Make scalar again
+   endfor
+  end
+
+
+  'ZEA':begin
+    theta = pi2 - 2.d0*asin(sqrt(xx^2 + yy^2)/(2.d0*radeg))
+    phi = atan(xx,-yy)
+  end
+
+  'AIR':begin
+
+    if N_elements(PV2) LT 1 then begin
+      message,/informational,$
+          'pv2_1 not set, using default of pv2_1 = 90 for AIR map projection'
+      pv2_1 = 9.d1
+    endif else pv2_1 = pv2[0]
+
+; Numerically solve the equation for xi, by iterating the equation for xi.
+; The default initial value for xi is 30 degrees, but for some values of
+; x and y, this causes an imaginary angle to result for the next iteration of
+; xi.  Unfortunately, this causes the value of xi to converge to an incorrect
+; value, so the initial xi is adjusted to avoid this problem.
+    theta_b = pv2_1/radeg
+    xi = theta_b
+    zeta_b = (pi2-theta_b)/2.d0
+    if (cos(zeta_b) NE 1) then $
+      a = alog(cos(zeta_b))/(tan(zeta_b))^2 $
+    else a = -0.5d0
+    rtheta = sqrt(xx^2 + yy^2)/(2.0d*radeg)
+
+    repeat begin
+      bad=where( abs(exp((-rtheta - a*tan(xi))*tan(xi))) gt 1)
+      if (bad[0] ne -1) then xi[bad] = xi[bad]/2.d0
+    endrep until (bad[0] eq -1)
+
+    tolerance = 1.d-12
+    repeat begin
+
+      xi_old = xi
+      xi = acos(exp( (-rtheta - a*tan(xi) )*tan(xi)))
+
+    endrep until (max(abs(xi_old - xi)) lt tolerance)
+
+;    print,rtheta,alog(cos(xi))/tan(xi) + a*tan(xi)
+    theta = pi2 - 2.d0*xi
+    phi = atan(xx,-yy)
+  end
+
+  'CYP':begin
+    if n_elements(pv2 eq 0) then begin
+      message,/informational,$
+            'PV2_1 not set, using default of pv2_1 = 0 for CYP map projection'
+      pv2_1 = 0.d0
+    endif else pv2_1 = pv2[0]
+    if N_elements(pv2) LT 2 then begin
+      message,/informational,$
+            'PV2_2 not set, using default of pv2_2 = 1 for CYP map projection'
+      pv2_2 = 1.d0
+    endif else pv2_2 = pv2[1]
+    if (pv2_1 eq -pv2_2) then message,$
+      'PV2_1 = -PV2_2 is not allowed for CYP map projection.'
+
+    eta = yy/((pv2_1 + pv2_2)*radeg)
+    theta = atan(eta,1) + asin(eta*pv2_1/sqrt(eta^2 + 1.d0))
+    phi = xx/(pv2_2*radeg)
+  end
+
+  'CAR':begin
+    phi = xx/radeg
+    theta = yy/radeg
+  end
+
+  'MER':begin
+    phi = xx/radeg
+    theta = 2*atan(exp(yy/radeg)) - pi2
+  end
+
+  'CEA':begin
+    if N_elements(PV2) LT 1 then message,$
+      'CEA map projection requires that PV2_1 keyword be set.'
+    pv2_1 = pv2[0]
+    if ((pv2_1 le 0) || (pv2_1 gt 1)) then message,$
+      'CEA map projection requires 0 < PV2_1 <= 1'
+    phi = xx/radeg
+    theta = asin(yy*pv2_1/radeg)
+  end
+
+  'COP':begin
+    if N_elements(PV2) LT 1 then message,$
+      'COP map projection requires that PV2_1 keyword be set.'
+    pv2_1 =  pv2[0]
+    if N_elements(PV2) LT 2 then begin
+      message,/informational,$
+      'PV2_2 not set, using default of PV2_2 = 0 for COP map projection'
+      pv2_2=0
+    endif else pv2_2 = pv2[1]
+    if ((pv2_1 lt -90) || (pv2_2 gt 90) || (pv2_1 gt 90)) then message,$
+ 'pv2_1 and pv2_2 must satisfy -90<=PV2_1<=90, PV2_2<=90 for COP projection'
+    if (pv2_1 eq -pv2_2) then message,$
+ 'COP projection with PV2_1=-PV2_2 is better done as a cylindrical projection'
+
+    theta_a = pv2_1/radeg
+    alpha = pv2_2/radeg
+    y_0 = radeg*cos(alpha)/tan(theta_a)
+    R_theta = sqrt(xx^2+(y_0-yy)^2)
+    if pv2_1 LT 0 then R_theta = -R_theta
+    theta = theta_a + atan(1.d0/tan(theta_a) - R_theta/$
+          (radeg*cos(alpha)))
+     phi = atan( xx/R_theta,(y_0-yy)/R_theta )/sin(theta_a)
+  end
+
+  'COD':begin
+    if N_elements(pv2) LT 1 then message,$
+      'COD map projection requires that PV2_1 keyword be set.'
+    pv2_1 = pv2[0]
+    if N_elements(pv2) LT 2 then begin
+      message,/informational,$
+     'PV2_2 not set, using default of PV2_2 = 0 for COD map projection'
+      pv2_2 = 0
+    endif else pv2_2 = pv2[1]
+    if ((pv2_1 lt -90) || (pv2_2 gt 90) || (pv2_1 gt 90)) then message,$
+ 'pv2_1 and pv2_2 must satisfy -90<=pv2_1<=90,pv2_2<=90 for COD projection'
+
+; use general set of equations for pv2_1 not = pv2_2
+    theta_a = pv2_1/radeg
+
+    if (pv2_2 NE 0) then begin
+      alpha = pv2_2/radeg
+      C = sin(theta_a)*sin(alpha)/alpha
+      Y_0 = radeg*alpha/tan(alpha)/tan(theta_a)
+      R_theta = sqrt(xx^2+(y_0-yy)^2)
+      if pv2_1 LT 0 then R_theta = -R_theta
+       theta = theta_a + alpha/(tan(alpha)*tan(theta_a))-  R_theta/radeg
+; use special set of equations for pv2_1 = pv2_2
+    endif else begin
+      C = sin(theta_a)
+      y_0 = radeg/tan(theta_a)
+     R_theta = sqrt(xx^2+(y_0-yy)^2)
+     if pv2_1 LT 0 then R_theta = -R_theta
+      theta = theta_a + 1.0d/tan(theta_a) - R_theta/radeg
+   endelse
+    phi = atan( xx/R_theta,(y_0-yy)/R_theta )/C
+   end
+
+  'COE':begin
+    if N_elements(pv2) LT 1 then message,$
+      'COE map projection requires that pv2_1 keyword be set.'
+    pv2_1 = pv2[0]
+    if N_elements(pv2) LT 2 then begin
+      message,/informational,$
+      'pv2_2 not set, using default of pv2_2 = 0 for COE map projection'
+      pv2_2 = 0
+    endif else pv2_2 = pv2[1]
+    if ((pv2_1 lt -90) || (pv2_2 gt 90) || (pv2_1 gt 90)) then message,$
+ 'pv2_1 and pv2_2 must satisfy -90<=pv2_1<=90,pv2_2<=90 for COE projection'
+    theta_a = pv2_1/radeg
+    eta = pv2_2/radeg
+    theta1 = (theta_a - eta)
+     theta2 = (theta_a + eta)
+    s_1 = sin( theta1)
+    s_2 = sin( theta2)
+    stheta_a = sin(theta_a)
+    gamma = s_1 + s_2
+    C = gamma/2
+    y_0 = radeg*2.d0*sqrt(1.d0 + s_1*s_2 - gamma*stheta_a)/gamma
+    R_theta = (xx^2+(y_0-yy)^2)
+    if pv2_1 LT 0 then R_theta = -R_theta
+    phi = 2*atan(xx/R_theta,(y_0 - yy)/R_theta)/gamma
+    theta = asin((1.d0 + s_1*s_2-(xx^2+(y_0-yy)^2)*(gamma/(2.d0*radeg))^2)/gamma)
+
+  end
+
+  'COO':begin
+    if N_elements(pv2) LT 1 then message,$
+      'COO map projection requires that pv2_1 keyword be set.'
+    pv2_1 = pv2[0]
+    if N_elements(pv2) LT 2 then begin
+      message,/informational,$
+      'pv2_2 not set, using default of pv2_2 = 0 for COO map projection'
+      pv2_2 = 0
+    endif else  pv2_2 = pv2[1]
+    if ((pv2_1 lt -90) || (pv2_2 gt 90) || (pv2_1 gt 90)) then message,$
+ 'pv2_1 and pv2_2 must satisfy -90<=pv2_1<=90,pv2_2<=90 for COO projection'
+    theta_1 = (pv2_1 - pv2_2)/radeg
+    theta_2 = (pv2_1 + pv2_2)/radeg
+    theta_a = pv2_1/radeg
+
+
+; calculate value of c in simpler fashion if pv2_1 = pv2_2
+    if (theta_1 eq theta_2) then c = sin(theta_1) else $
+    c = alog(cos(theta_2)/cos(theta_1))/alog(tan((pi2-theta_2)/2.d0)/$
+    tan((pi2-theta_1)/2.d0))
+
+    alpha = radeg*cos(theta_1)/(c*(tan((pi2-theta_1)/2.d0))^c)
+    Y_0 = alpha*(tan((pi2-theta_a)/2.d0)^c)
+    R_theta = sqrt(xx^2+(y_0-yy)^2)
+    if pv2_1 LT 0 then R_theta = -R_theta
+     phi = atan( xx/R_theta,(y_0-yy)/R_theta )/C
+    theta = pi2 - 2*atan((R_theta/alpha)^(1.d0/c))
+  end
+
+  'BON':begin
+    if (N_elements(pv2) LT 1) then message,$
+      'BON map projection requires that PV2_1 keyword be set.'
+    pv2_1 = pv2[0]
+    if ((pv2_1 lt -90) || (pv2_1 gt 90)) then message,$
+      'pv2_1 must satisfy -90 <= pv2_1 <= 90 for BON map projection'
+    if (pv2_1 eq 0) then message,$
+      'pv2_1 = 0 for BON map projection is better done with SFL map projection'
+    theta_1 = pv2_1/radeg
+    y_0 = 1.d0/tan(theta_1) + theta_1
+    s = theta_1/abs(theta_1)
+    theta = y_0 - s*sqrt(xx^2 + (y_0*radeg - yy)^2)/radeg
+    phi = s*(y_0 - theta)*atan(s*xx/(y_0*radeg - theta),$
+                               (y_0*radeg - yy)/(y_0*radeg - theta))/cos(theta)
+  end
+
+  'PCO':begin
+; Determine where y = 0 and assign theta to 0 for these points.  The reason
+; for doing this separately is that the initial condition for theta in the
+; numerical solution is sign(y)*45 which only works for y not = 0.
+    bad = where(yy eq 0)
+    good = where(yy ne 0)
+    theta = double(xx - xx)
+    if (bad[0] ne -1) then theta[bad] = 0.d0
+
+; Find theta numerically.
+    tolerance = 1.d-11
+    tolerance_2 = 1.d-11
+    if (good[0] ne -1) then begin
+      theta_p = double(xx - xx)
+      theta_p[good] = pi2*yy[good]/abs(yy[good])
+      theta_n = double(xx - xx)
+      f_p = double(xx - xx)
+      f_p[good] = xx[good]^2 - 2.d0*radeg*(yy[good] - radeg*theta_p[good])/$
+                  tan(theta_p[good]) + (yy[good] - radeg*theta_p[good])^2
+      f_n = double(xx - xx) - 999.d0
+      lambda = double(xx - xx)
+      f = double(xx - xx)
+      repeat begin
+        case_1 = where((yy ne 0.d0) and (f_n lt (-1.d2)))
+        case_2 = where((yy ne 0.d0) and (f_n ge (-1.d2)))
+        if (case_1[0] ne -1) then lambda[case_1] = 0.5d0
+        if (case_2[0] ne -1) then $
+          lambda[case_2] = f_p[case_2]/(f_p[case_2] - f_n[case_2])
+        lambda[good] = 1.d-1 > (9.d-1 < lambda[good])
+        theta[good] = (1.d0 - lambda[good])*theta_p[good] + $
+                      lambda[good]*theta_n[good]
+        f[good] = xx[good]^2 - 2.d0*radeg*(yy[good] - radeg*theta[good])/$
+                  tan(theta[good]) + (yy[good] - radeg*theta[good])^2
+        neg = where((yy ne 0.d0) and (f lt 0.d0))
+        pos = where((yy ne 0.d0) and (f gt 0.d0))
+        if (neg[0] ne -1) then begin
+          f_n[neg] = f[neg]
+          theta_n[neg] = theta[neg]
+        end
+        if (pos[0] ne -1) then begin
+          f_p[pos] = f[pos]
+          theta_p[pos] = theta[pos]
+        end
+      endrep until ((max(abs(theta_p - theta_n)) lt tolerance) || $
+                    (max(abs(f)) lt tolerance_2))
+    endif
+
+; Determine phi differently depending on whether theta = 0 or not.
+    bad = where(theta eq 0.d0)
+    good = where(theta ne 0.d0)
+    phi = double(x - x)
+    if (bad[0] ne -1) then phi[bad] = xx[bad]/radeg
+    phi[good] = atan(xx[good]/radeg*tan(theta[good]),$
+       1.d0 - (yy[good]/radeg - theta[good])*tan(theta[good]))/sin(theta[good])
+  end
+
+  'SFL':begin
+    phi = xx/(radeg*cos(yy/radeg))
+    theta = yy/radeg
+  end
+
+  'GLS':begin
+    phi = xx/(radeg*cos(yy/radeg))
+    theta = yy/radeg
+  end
+
+
+  'PAR':begin
+
+    theta = 3.d0*asin(yy/pi/radeg)
+    phi = xx/(1.d0 - 4.d0*(yy/pi/radeg)^2)/radeg
+  end
+
+  'AIT':begin
+  z2 = 1.d0 - (xx/(4.d0*radeg))^2 - (yy/(2.d0*radeg))^2
+  bad = where(z2 lt 0.5d0,nbad)
+  z = sqrt(z2)
+  phi = 2.d0*atan(z*xx/(2.d0*radeg),2.d0*z^2 - 1.d0)
+  theta = asin(yy*z/radeg)
+  if nbad gt 0 then begin
+       phi[bad] = !values.d_nan
+      theta[bad] = !values.d_nan
+   endif
+
+  end
+
+  'MOL':begin
+    phi = pi*xx/(radeg*2.d0*sqrt(2.d0 - (yy/radeg)^2))
+    arg = 2.d0*asin(yy/(sqrt(2.d0)*radeg))/pi + $
+                 yy*sqrt(2.d0 - (yy/radeg)^2)/1.8d2
+
+   theta = asin(2.d0*asin(yy/(sqrt(2.d0)*radeg))/pi + $
+                 yy*sqrt(2.d0 - (yy/radeg)^2)/1.8d2)
+
+  end
+
+  'CSC':begin
+    xx = xx/4.5d1
+    yy = yy/4.5d1
+
+;
+;   If the faces are not defined, assume that the faces need to be defined
+;   and the whole sky is displayed as a "sideways T".
+;
+        if noface eq 1 then begin
+
+                face=intarr(n_elements(xx))
+
+                face1 = where((xx le 1.0) and (yy le 1.0) and (yy ge -1.0),nf1)
+                if nf1 gt 0 then begin
+                        face[face1]=1
+                endif
+
+                face4 = where((xx gt 5.0),nf4)
+                if nf4 gt 0 then begin
+                        face[face4]=4
+                        xx[face4]=xx[face4]-6.0d0
+                endif
+
+                face3 = where((xx le 5.0) and (xx gt 3.0),nf3)
+                if nf3 gt 0 then begin
+                        face[face3]=3
+                        xx[face3]=xx[face3]-4.0d0
+                endif
+
+                face2 = where((xx le 3.0) and (xx gt 1.0),nf2)
+                if nf2 gt 0 then begin
+                        face[face2]=2
+                        xx[face2]=xx[face2]-2.0d0
+                endif
+
+                face0 = where((xx le 1.0) and (yy gt 1.0),nf0)
+                if nf0 gt 0 then begin
+                        face[face0]=0
+                        yy[face0]=yy[face0] - 2.0
+                endif
+
+                face5 = where((xx le 1.0) and (yy lt -1.0),nf5)
+                if nf5 gt 0 then begin
+                        face[face5]=5
+                        yy[face5]=yy[face5] + 2.0
+                endif
+
+        endif
+
+; Define array of numerical constants used in determining alpha and beta1.
+    p = dblarr(7,7)
+    p[0,0] = -0.27292696d0
+    p[1,0] = -0.07629969d0
+    p[0,1] = -0.02819452d0
+    p[2,0] = -0.22797056d0
+    p[1,1] = -0.01471565d0
+    p[0,2] = 0.27058160d0
+    p[3,0] = 0.54852384d0
+    p[2,1] = 0.48051509d0
+    p[1,2] = -0.56800938d0
+    p[0,3] = -0.60441560d0
+    p[4,0] = -0.62930065d0
+    p[3,1] = -1.74114454d0
+    p[2,2] = 0.30803317d0
+    p[1,3] = 1.50880086d0
+    p[0,4] = 0.93412077d0
+    p[5,0] = 0.25795794d0
+    p[4,1] = 1.71547508d0
+    p[3,2] = 0.98938102d0
+    p[2,3] = -0.93678576d0
+    p[1,4] = -1.41601920d0
+    p[0,5] = -0.63915306d0
+    p[6,0] = 0.02584375d0
+    p[5,1] = -0.53022337d0
+    p[4,2] = -0.83180469d0
+    p[3,3] = 0.08693841d0
+    p[2,4] = 0.33887446d0
+    p[1,5] = 0.52032238d0
+    p[0,6] = 0.14381585d0
+
+; Calculate alpha and beta1 using numerical constants
+    sum = double(x - x)
+    for j = 0,6 do for i = 0,6 - j do sum = sum + p[i,j]*xx^(2*i)*yy^(2*j)
+    alpha = xx + xx*(1 - xx^2)*sum
+
+    sum = double(x - x)
+    for j = 0,6 do for i = 0,6 - j do sum = sum + p[i,j]*yy^(2*i)*xx^(2*j)
+    beta1 = yy + yy*(1 - yy^2)*sum
+
+; Calculate theta and phi from alpha and beta1; the method depends on which
+; face the point lies on
+    phi = double(x - x)
+    theta = double(x - x)
+    for i = 0l, n_x - 1 do begin
+      case face[i] of
+        0:begin
+          if (beta1[i] eq 0.d0) then begin
+            if (alpha[i] eq 0.d0) then begin
+              theta[i] = pi2
+; uh-oh lost information if this happens
+              phi[i] = 0.d0
+            endif else begin
+              phi[i] = alpha[i]/abs(alpha[i])*pi2
+              theta[i] = atan(abs(1.d0/alpha[i]))
+            endelse
+          endif else begin
+            phi[i] = atan(alpha[i],-beta1[i])
+            theta[i] = atan(-cos(phi[i])/beta1[i])
+          endelse
+; ensure that the latitudes are positive
+          theta[i] = abs(theta[i])
+        end
+        1:begin
+          phi[i] = atan(alpha[i])
+          theta[i] = atan(beta1[i]*cos(phi[i]))
+        end
+        2:begin
+          if (alpha[i] eq 0.d0) then phi[i] = pi2 else $
+            phi[i] = atan(-1.d0/alpha[i])
+          if (phi[i] lt 0.d0) then phi[i] = phi[i] + pi
+          theta[i] = atan(beta1[i]*sin(phi[i]))
+        end
+        3:begin
+          phi[i] = atan(alpha[i])
+          if (phi[i] gt 0.d0) then phi[i] = phi[i] - pi else $
+          if (phi[i] lt 0.d0) then phi[i] = phi[i] + pi
+          theta[i] = atan(-beta1[i]*cos(phi[i]))
+        end
+        4:begin
+          if (alpha[i] eq 0.d0) then phi[i] = -pi2 else $
+            phi[i] = atan(-1.d0/alpha[i])
+          if (phi[i] gt 0.d0) then phi[i] = phi[i] - pi
+          theta[i] = atan(-beta1[i]*sin(phi[i]))
+        end
+        5:begin
+          if (beta1[i] eq 0.d0) then begin
+            if (alpha[i] eq 0.d0) then begin
+              theta[i] = -pi2
+; uh-oh lost information if this happens
+              phi[i] = 0.d0
+            endif else begin
+              phi[i] = -alpha[i]/abs(alpha[i])*pi2
+              theta[i] = -atan(abs(1.d0/alpha[i]))
+            endelse
+          endif else begin
+            phi[i] = atan(alpha[i],beta1[i])
+            theta[i] = atan(-cos(phi[i])/beta1[i])
+          endelse
+; ensure that the latitudes are negative
+          theta[i] = -abs(theta[i])
+        end
+
+      endcase
+    endfor
+  end
+
+  'QSC':begin
+
+    xx=xx/45.0d0
+    yy=yy/45.0d0
+;
+;   If the faces are not defined, assume that the faces need to be defined
+;   and the whole sky is displayed as a "sideways T".
+;
+        if noface eq 1 then begin
+
+                face=intarr(n_elements(xx))
+
+                face1 = where((xx le 1.0) and (yy le 1.0) and (yy ge -1.0),nf1)
+                if nf1 gt 0 then begin
+                        face[face1]=1
+                endif
+
+                face4 = where((xx gt 5.0),nf4)
+                if nf4 gt 0 then begin
+                        face[face4]=4
+                        xx[face4]=xx[face4]-6.0d0
+                endif
+
+                face3 = where((xx le 5.0) and (xx gt 3.0),nf3)
+                if nf3 gt 0 then begin
+                        face[face3]=3
+                        xx[face3]=xx[face3]-4.0d0
+                endif
+
+                face2 = where((xx le 3.0) and (xx gt 1.0),nf2)
+                if nf2 gt 0 then begin
+                        face[face2]=2
+                        xx[face2]=xx[face2]-2.0d0
+                endif
+
+                face0 = where((xx le 1.0) and (yy gt 1.0),nf0)
+                if nf0 gt 0 then begin
+                        face[face0]=0
+                        yy[face0]=yy[face0] - 2.0
+                endif
+
+                face5 = where((xx le 1.0) and (yy lt -1.0),nf5)
+                if nf5 gt 0 then begin
+                        face[face5]=5
+                        yy[face5]=yy[face5] + 2.0
+                endif
+
+        endif
+
+
+; First determine the quadrant in which each points lies.  Calculate the
+; ratio (alpha/beta1) for each point depending on the quadrant.  Finally,
+; use this information and the face on which the point lies to calculate
+; phi and theta.
+    theta = double(x - x)
+    phi = double(x - x)
+    rho = double(x - x)
+    ratio = double(x - x)
+    larger = double(x - x)
+    smaller = double(x - x)
+
+    temp = where(abs(yy) ge abs(xx), Ntemp)
+    if Ntemp GT 0 then larger[temp] = yy[temp]
+    temp = where(abs(xx) gt abs(yy), Ntemp )
+    if Ntemp GT 0 then larger[temp] = xx[temp]
+    temp = where(abs(yy) lt abs(xx), Ntemp )
+    if Ntemp GT 0 then smaller[temp] = yy[temp]
+    temp = where(abs(xx) le abs(yy), Ntemp)
+    if Ntemp GT 0 then smaller[temp] = xx[temp]
+
+    temp = where(larger ne 0.d0, Ntemp)
+    if Ntemp GT 0 then ratio[temp] = sin(pi/1.2d1*smaller[temp]/larger[temp])/$
+                      (cos(pi/1.2d1*smaller[temp]/larger[temp]) - sqrt(0.5d0))
+
+    temp = where(larger eq 0.d0, Ntemp)
+    if Ntemp GT 0 then ratio[temp] = 1.d0
+    rho = 1.d0 - (larger)^2*(1.d0 - 1.d0/sqrt(2.d0 + ratio^2))
+
+    temp = where((abs(xx) gt abs(yy)) and (ratio ne 0.d0), Ntemp)
+    if Ntemp GT 0 then ratio[temp] = 1.d0/ratio[temp]
+
+    temp = where((abs(xx) gt abs(yy)) and (ratio eq 0.d0), Ntemp)
+; use a kludge to produce the correct value for 1/0 without generating an error
+    if Ntemp GT 0 then ratio[temp] = tan(pi2)
+
+    for i = 0l, n_x-1 do begin
+      case face[i] of
+        0:begin
+          if (xx[i] ne 0.d0) then phi[i] = atan(-ratio[i]) else $
+          if (yy[i] le 0.d0) then phi[i] = 0.d0 else $
+          if (yy[i] gt 0.d0) then phi[i] = pi
+
+          if (yy[i] ne 0.d0) then theta[i] = asin(rho[i]) else $
+          if (xx[i] le 0.d0) then theta[i] = -pi2 else $
+          if (xx[i] gt 0.d0) then theta[i] = pi2
+
+          if (yy[i] gt 0.d0) then begin
+            if (xx[i] lt 0.d0) then phi[i] = phi[i] - pi $
+            else if (xx[i] gt 0.d0) then phi[i] = phi[i] + pi
+          endif
+        end
+        1:begin
+          if (xx[i] ne 0.d0) then begin
+            if (yy[i] ne 0.d0) then $
+             phi[i] = xx[i]/abs(xx[i])*acos(sqrt(rho[i]^2*(1.d0 + ratio[i]^2)/$
+                             (ratio[i]^2 + rho[i]^2))) $
+            else phi[i] = xx[i]/abs(xx[i])*acos(rho[i])
+          endif else phi[i] = 0.d0
+          if (yy[i] ne 0.d0) then theta[i] = yy[i]/abs(yy[i])*acos(rho[i]/$
+                                            cos(phi[i])) else theta[i] = 0.d0
+        end
+        2:begin
+          if (yy[i] ne 0.d0) then begin
+            if (xx[i] gt 0.d0) then $
+               phi[i] = pi - asin(sqrt(rho[i]^2*(1.d0 + ratio[i]^2)/$
+                             (ratio[i]^2 + rho[i]^2))) $
+            else if (xx[i] lt 0.d0) then $
+               phi[i] = asin(sqrt(rho[i]^2*(1.d0 + ratio[i]^2)/$
+                             (ratio[i]^2 + rho[i]^2))) $
+            else phi[i] = pi2
+            theta[i] = yy[i]/abs(yy[i])*acos(rho[i]/abs(sin(phi[i])))
+          endif else begin
+            theta[i] = 0.d0
+            if (xx[i] gt 0.d0) then phi[i] = pi - asin(rho[i]) $
+            else if (xx[i] lt 0.d0) then phi[i] = asin(rho[i]) $
+            else phi[i] = pi2
+          endelse
+        end
+        3:begin
+          if (yy[i] ne 0.d0) then begin
+            if (xx[i] gt 0.d0) then $
+              phi[i] = acos(sqrt(rho[i]^2*(1.d0 + ratio[i]^2)/$
+                            (ratio[i]^2 + rho[i]^2))) - pi $
+            else if (xx[i] lt 0.d0) then $
+              phi[i] = -acos(sqrt(rho[i]^2*(1.d0 + ratio[i]^2)/$
+                       (ratio[i]^2 + rho[i]^2))) + pi $
+            else phi[i] = pi
+            theta[i] = yy[i]/abs(yy[i])*acos(-rho[i]/cos(phi[i]))
+          endif else begin
+            theta[i] = 0.d0
+            if (xx[i] gt 0.d0) then phi[i] = acos(rho[i]) - pi $
+            else if (xx[i] lt 0.d0) then phi[i] = -acos(rho[i]) + pi $
+            else phi[i] = pi
+          endelse
+        end
+        4:begin
+          if (yy[i] ne 0.d0) then begin
+            if (xx[i] gt 0.d0) then $
+               phi[i] = -asin(sqrt(rho[i]^2*(1.d0 + ratio[i]^2)/$
+                             (ratio[i]^2 + rho[i]^2))) $
+            else if (xx[i] lt 0.d0) then $
+               phi[i] = asin(sqrt(rho[i]^2*(1.d0 + ratio[i]^2)/$
+                             (ratio[i]^2 + rho[i]^2))) - pi $
+            else phi[i] = -pi2
+            theta[i] = yy[i]/abs(yy[i])*acos(-rho[i]/sin(phi[i]))
+          endif else begin
+            theta[i] = 0.d0
+            if (xx[i] gt 0.d0) then phi[i] = -asin(rho[i]) $
+            else if (xx[i] lt 0.d0) then phi[i] = asin(rho[i]) - pi $
+            else phi[i] = -pi2
+          endelse
+        end
+        5:begin
+          if (xx[i] ne 0.d0) then phi[i] = atan(ratio[i]) $
+          else if (yy[i] le 0.d0) then phi[i] = pi $
+          else if (yy[i] gt 0.d0) then phi[i] = 0.d0
+
+          if (yy[i] ne 0.d0) then theta[i] = asin(-rho[i]) $
+          else if (xx[i] le 0.d0) then theta[i] = -pi2 $
+          else if (xx[i] gt 0.d0) then theta[i] = pi2
+
+          if (yy[i] lt 0.d0) then begin
+            if (xx[i] lt 0.d0) then phi[i] = phi[i] - pi $
+            else if (xx[i] gt 0.d0) then phi[i] = phi[i] + pi
+          endif
+        end
+      endcase
+    endfor
+  end
+
+  'TSC':begin
+
+    xx=xx/45.0d0
+    yy=yy/45.0d0
+;
+;   If the faces are not defined, assume that the faces need to be defined
+;   and the whole sky is displayed as a "sideways T".
+;
+        if noface eq 1 then begin
+
+                face=intarr(n_elements(xx))
+
+                face1 = where((xx le 1.0) and (yy le 1.0) and (yy ge -1.0),nf1)
+                if nf1 gt 0 then begin
+                        face[face1]=1
+                endif
+
+                face4 = where((xx gt 5.0),nf4)
+                if nf4 gt 0 then begin
+                        face[face4]=4
+                        xx[face4]=xx[face4]-6.0d0
+                endif
+
+                face3 = where((xx le 5.0) and (xx gt 3.0),nf3)
+                if nf3 gt 0 then begin
+                        face[face3]=3
+                        xx[face3]=xx[face3]-4.0d0
+                endif
+
+                face2 = where((xx le 3.0) and (xx gt 1.0),nf2)
+                if nf2 gt 0 then begin
+                        face[face2]=2
+                        xx[face2]=xx[face2]-2.0d0
+                endif
+
+                face0 = where((xx le 1.0) and (yy gt 1.0),nf0)
+                if nf0 gt 0 then begin
+                        face[face0]=0
+                        yy[face0]=yy[face0] - 2.0
+                endif
+
+                face5 = where((xx le 1.0) and (yy lt -1.0),nf5)
+                if nf5 gt 0 then begin
+                        face[face5]=5
+                        yy[face5]=yy[face5] + 2.0
+                endif
+
+        endif
+
+    rho = sin(atan(1.0d0/sqrt(xx^2 + yy^2)))
+    phi = double(x - x)
+    theta = double(x - x)
+    for i = 0l, n_x - 1 do begin
+      case face[i] of
+        0:begin
+          phi[i] = atan(xx[i],-yy[i])
+          theta[i] = asin(rho[i])
+        end
+        1:begin
+          if (xx[i] ne 0.d0) then begin
+            if (xx[i] ge 0.d0) then $
+             phi[i] = atan(sqrt((1.d0/rho[i]^2- 1.d0)/(1 + (yy[i]/xx[i])^2))) $
+            else phi[i] =atan(-sqrt((1.d0/rho[i]^2 - 1.d0)/$
+                               (1 + (yy[i]/xx[i])^2)))
+            theta[i] = atan(yy[i]/xx[i]*sin(phi[i]))
+          endif else begin
+            phi[i] = 0.d0
+            if (yy[i] ge 0.d0) then theta[i] = acos(rho[i]) $
+            else theta[i] = -acos(rho[i])
+          endelse
+        end
+        2:begin
+; The point theta = 0, phi = Pi/2 lies in this region, allowing
+; rho = Cos[theta]*Sin[phi] to be 1, causing an infinite quantity in the
+; equation for phi
+          if (rho[i] eq 1.d0) then begin
+            phi[i] = pi2
+            theta[i] = 0.d0
+          endif else if (xx[i] gt 1.d-14) then begin
+           phi[i] = atan(-sqrt((1.d0 + (yy[i]/xx[i])^2)/$
+                                (1.d0/rho[i]^2 - 1.d0)))+pi
+            theta[i] = atan(-yy[i]/xx[i]*cos(phi[i]))
+          endif else if (xx[i] lt -1.d-14) then begin
+            phi[i]=atan(sqrt((1.d0+(yy[i]/xx[i])^2)/(1.d0/rho[i]^2 - 1.d0)))
+            theta[i] = atan(-yy[i]/xx[i]*cos(phi[i]))
+          endif else begin
+             phi[i] = pi2
+            if (yy[i] ge 0) then theta[i] = acos(rho[i]/sin(phi[i])) $
+            else theta[i] = -acos(rho[i]/sin(phi[i]))
+          endelse
+        end
+        3:begin
+          if (abs(xx[i]) ge 1.d-5) then begin
+            if (xx[i] gt 0.d0) then $
+           phi[i] = atan(sqrt((1.d0/rho[i]^2 - 1.d0)/$
+                          (1 + (yy[i]/xx[i])^2)))-pi $
+        else phi[i] = atan(-sqrt((1.d0/rho[i]^2 - 1.d0)/$
+                            (1 + (yy[i]/xx[i])^2)))+pi
+            theta[i] = atan(-yy[i]/xx[i]*sin(phi[i]))
+          endif else begin
+            if (xx[i] ge 0.d0) then phi[i] = -pi $
+            else phi[i] = pi
+            if (yy[i] ge 0) then theta[i] = acos(rho[i]) $
+            else theta[i] = -acos(rho[i])
+          endelse
+        end
+        4:begin
+          if (rho[i] eq 1.d0) then begin
+            phi[i] = -pi2
+            theta[i] = atan(yy[i]/xx[i])
+          endif else if (xx[i] gt 1.d-14) then begin
+           phi[i]=atan(-sqrt((1.d0 + (yy[i]/xx[i])^2)/(1.d0/rho[i]^2 - 1.d0)))
+            theta[i] = atan(yy[i]/xx[i]*cos(phi[i]))
+          endif else if (xx[i] lt -1.d-14) then begin
+            phi[i]=atan(sqrt((1.d0+(yy[i]/xx[i])^2)/(1.d0/rho[i]^2 - 1.d0)))-pi
+            theta[i] = atan(yy[i]/xx[i]*cos(phi[i]))
+          endif else begin
+             phi[i] = 1.5d0*!pi
+            if (yy[i] ge 0) then theta[i] = acos(rho[i]) $
+            else theta[i] = -acos(rho[i])
+          endelse
+        end
+        5:begin
+          phi[i] = atan(xx[i],yy[i])
+          theta[i] = asin(-rho[i])
+        end
+
+      endcase
+    endfor
+  end
+
+  'HPX':begin ; HEALPix projection
+              ; See Calabretta & Roukema 2007, MNRAS, 381, 865
+
+    pv2_1 = N_ELEMENTS(pv2) GE 1 ? pv2[0] : 4.d
+    pv2_2 = N_ELEMENTS(pv2) GE 2 ? pv2[1] : 3.d
+    hpx_k = pv2_2                  ; The main generalised HEALPIX parameters
+    hpx_h = pv2_1                  ;
+    ik = ROUND(hpx_k)
+    ih = ROUND(hpx_h)
+    IF (ik le 0 || ih le 0) THEN MESSAGE, $
+      'Illegal PV2 array:' + STRCOMPRESS(STRJOIN(pv2,/single))+ $
+      '; should be positive integers'
+
+    phi   = xx ; Create theta & phi arrays in same shape as xx & yy.
+    theta = yy ;
+
+                            ; Is pixel is in an unoccupied facet ?
+    invfd = ih / 360d0      ; inverse facet diagonal
+    wdiag = ik / 2          ; Semi-width of occupied diagonal in facets
+
+    IF ik THEN BEGIN               ; Odd K (including standard HEALPix):
+      xoff = ih + wdiag + 1 ? 0.5d0 : 0d0
+      ix   = ROUND( (xx + yy)*invfd + xoff ) ; Facet indices
+      diag = ROUND( (yy - xx)*invfd - xoff ) + wdiag + TEMPORARY(ix)
+    ENDIF ELSE BEGIN               ; Even K
+                                   ; Row offset for (0,0) depends on
+                                   ; whether h is odd or even:
+      yoff = ih + (ik-1)/2 ? -0.25d0 : 0.25d0
+      ioff = ih ? 2*((ik-2)/4) + 1 : 2*(ik/4)
+      ix   = ROUND( (xx + yy)*invfd + yoff )
+      diag = ROUND( (yy - xx)*invfd + yoff ) + ioff  + TEMPORARY(ix)
+    ENDELSE
+    hpx_good = WHERE(diag LT ik AND diag GE 0, COMPLEMENT = hpx_bad)
+    if hpx_bad[0] ne -1 then begin ; Set coords of off-sky pixels to NaN:
+      phi[hpx_bad]   = !VALUES.D_NAN
+      theta[hpx_bad] = !VALUES.D_NAN
+    endif
+ 
+    ylim = 90*(ik-1)/hpx_h
+
+    eqfaces = -1
+    polfaces = -1
+    IF hpx_good[0] NE -1 THEN BEGIN
+        equas = where(abs(yy[hpx_good]) le ylim, complement=poles)
+        IF equas[0] NE -1 THEN eqfaces  = hpx_good[TEMPORARY(equas)]
+        IF poles[0] NE -1 THEN polfaces = hpx_good[TEMPORARY(poles)]
+    ENDIF
+    hpx_good = 0
+ 
+                                ; equatorial region
+    if eqfaces[0] ne -1 then begin
+        phi[eqfaces]=xx[eqfaces]/radeg
+        theta[eqfaces]=asin(yy[eqfaces]*(hpx_h/(ik*90)))
+; Allow wrapped values of x, so following commented out:
+;       hpx_bad = where(xx[eqfaces] lt -180.D or xx[eqfaces] gt 180.D)
+;       if hpx_bad[0] ne -1 then begin
+;           phi[eqfaces[hpx_bad]]=!VALUES.D_NAN
+;           theta[eqfaces[hpx_bad]]=!VALUES.D_NAN
+;       endif
+    endif
+
+                                ; polar regions
+    if polfaces[0] ne -1 then begin
+      hpx_sig = (ik+1)/2.D - abs(yy[polfaces])*(hpx_h/180d)
+      hpx_omega = FIX((hpx_k mod 2 eq 1) or yy[polfaces] gt 0)
+      hpx_xc = -180 + (2 * floor( (xx[polfaces]+180d0)*hpx_h/360d0 + $
+                                (1-hpx_omega)/2d0 ) $
+                       + hpx_omega) * 180d0/hpx_h
+
+      poles = where(hpx_sig EQ 0.D) ; Avoid divide by zero at poles
+      IF poles[0] NE -1 THEN hpx_sig[poles] = 1.D
+      phi[polfaces] = ( hpx_xc + (xx[polfaces]-hpx_xc)/hpx_sig ) / radeg
+      theta[polfaces] = ((yy[polfaces] gt 0)*2-1)*asin(1-hpx_sig^2/hpx_k)
+      IF poles[0] NE -1 THEN BEGIN
+          phi[polfaces[poles]] = hpx_xc[poles] / radeg
+          theta[polfaces[poles]] = $
+               ((yy[polfaces[poles]] gt 0)*2-1) * !dpi/2d0
+      ENDIF
+    endif
+  end
+
+  'HCT':begin
+    phi = xx/radeg
+    theta = dblarr(n_elements(yy))
+    ylim = 90*(3-1)/4
+    w_np = where(yy ge ylim, n_np)
+    w_eq = where((yy lt ylim) and (yy gt -ylim), n_eq)
+    w_sp = where(yy le -ylim, n_sp)
+
+    if n_np gt 0 then theta[w_np] =  asin(1-(2-yy[w_np]/ylim)^2/3.d)
+    if n_eq gt 0 then theta[w_eq] =  asin((yy[w_eq]/ylim)*2./3.d)
+    if n_sp gt 0 then theta[w_sp] = -asin(1-(2+yy[w_sp]/ylim)^2/3.d)
+  end
+
+  'XPH':begin ; Butterfly re-arrangement of HPX, see Calabretta & Lowe (2013)
+;                                
+; xx, yy reference point is the pole which is in the centre of the
+; grid. Diagonal length in IWC is 360deg/sqrt(2)
+;
+; identify on-sky & off-sky pixels:
+     scale = 1d0/sqrt(2d0)
+     halfwidth = 180d0*scale
+     quarter   =  90d0*scale    
+     dg1 = (xx + yy)
+     dg2 = (xx - yy)
+     good = WHERE( xx ge -halfwidth AND xx le halfwidth AND $
+                   yy ge -halfwidth AND yy le halfwidth AND $
+                   ((dg1 ge -quarter  AND dg1 le quarter)  OR $
+                    (dg2 ge -quarter  AND dg2 le quarter)), ngood, $
+                   COMPLEMENT = bad, NCOMPLEMENT = nbad)     
+
+     scalar = sz_x[0] EQ 0
+     dims = scalar ? 1 : sz_x[1:sz_x[0]]
+     phi = DBLARR(dims,/NOZERO) & theta = DBLARR(dims,/NOZERO)
+     
+     IF nbad GT 0 THEN BEGIN
+        phi[bad] = !values.D_NAN
+        theta[bad] = !values.D_NAN
+        bad = 0
+     ENDIF
+     IF ngood GT 0 THEN BEGIN
+        dg1 = dg1[good]*scale
+        dg2 = dg2[good]*scale
+        
+        xi  = DBLARR(ngood,/NOZERO)
+        eta = DBLARR(ngood,/NOZERO)
+        phig = DBLARR(ngood)
+                              ; transform xy coords from XPH to HPX
+        xq = xx[good] ge 0d0  ; true on LHS of projection
+        yq = yy[good] ge 0d0  ; true in top half of projection
+               
+        quad = WHERE(~xq AND yq)  ; upper right
+        IF quad[0] ne -1 THEN BEGIN
+           xi[quad]   = -dg1[quad]
+           eta[quad]  =  dg2[quad]
+           phig[quad] = -180d0 
+        ENDIF
+        quad = WHERE(~xq AND ~yq) ; lower right
+        IF quad[0] ne -1 THEN BEGIN
+           xi[quad]   =  dg2[quad]
+           eta[quad]  =  dg1[quad]
+           phig[quad] =  -90d0
+        ENDIF
+        quad = WHERE(xq AND ~yq)  ; lower left
+        IF quad[0] ne -1 THEN BEGIN
+           xi[quad]   =  dg1[quad]
+           eta[quad]  = -dg2[quad]
+           phig[quad] =  0d0
+        ENDIF
+        quad = WHERE(xq AND yq)  ; upper left
+        IF quad[0] ne -1 THEN BEGIN
+           xi[quad]   = -dg2[quad]
+           eta[quad]  = -dg1[quad]
+           phig[quad] =  90d0
+        ENDIF
+        xq = 0 & yq = 0 & dg1 = 0 & dg2 = 0 & quad = 0
+        eta += 90d0
+        thetag = DBLARR(ngood,/NOZERO)
+        
+        poles = WHERE(ABS(eta) gt 45d0, COMPLEMENT=equas)
+        IF equas[0] NE -1 THEN BEGIN
+          phig[equas]  += xi[equas]
+          thetag[equas] = ASIN(eta[equas]*(2d0/135d0))
+        ENDIF
+        equas = 0
+        IF poles[0] NE -1 THEN BEGIN
+          hpx_sigma     = (90d0 - ABS(eta[poles]))/45d0
+          test = WHERE(hpx_sigma lt 1d-4,ntest)
+          phig[poles]  += xi[poles]/hpx_sigma
+          sgn           = 2*(eta[poles] gt 0d0) - 1
+          thetag[poles] = TEMPORARY(sgn)*ASIN(1d0-hpx_sigma^2/3d0)
+          IF ntest GT 0 THEN $
+              thetag[poles[test]] = pi2 - SQRT(2d0/3d0)*hpx_sigma[test]
+        ENDIF
+        phig += 45d0
+        poles = 0 & hpx_sigma = 0
+        phi[good] = TEMPORARY(phig)/radeg
+        theta[good] = TEMPORARY(thetag)
+        good = 0
+     ENDIF
+     IF scalar THEN BEGIN
+        phi = phi[0]
+        theta = theta[0]
+     ENDIF  
+  end
+
+  else:message,strupcase(projection_type) + $
+               ' is not a valid projection type.  Reset CTYPE'
+
+endcase
+
+; Convert from "native" coordinate system to "standard" coordinate system
+; if the CRVAL keyword is set.  Otherwise, assume the map projection is
+; complete
+
+ phi = phi*radeg
+ theta = theta*radeg
+
+ if ( N_elements(crval) GE 2 ) then begin
+
+  if N_elements(map_type) EQ 0 then $
+           map_type = where(projection_type EQ map_types)
+   map_type = map_type[0]
+   conic = (map_type GE 13) && (map_type LE 16)
+   zenithal =  ((map_type GE 1) && (map_type LE 8)) || $
+                (map_type EQ 26) || (map_type EQ 29)
+   if conic then theta0 = pv2_1 else if zenithal then theta0 = 90 $
+            else theta0 = 0
+
+   wcs_rotate, longitude, latitude, phi, theta, crval, longpole=longpole, $
+           theta0 = theta0, latpole = latpole, pv1=pv1, /REVERSE
+ endif else begin    ;no rotation from standard to native coordinates
+
+  latitude = theta
+  longitude = phi
+
+endelse
+
+; CONVERT LONGITUDE FROM -180 TO 180 TO 0 TO 360
+
+good = WHERE(FINITE(longitude), ngood)
+IF ngood GT 0 THEN BEGIN
+   lgood = longitude[good]
+   temp = where(lgood lt 0.d0, Nneg)
+   if (Nneg GT 0) then lgood[temp] = lgood[temp] + 3.6d2
+   temp = where(lgood ge 3.6d2, Nneg)
+   if (Nneg GT 0) then lgood[temp] = lgood[temp] - 3.6d2
+   longitude[good] = lgood
+ENDIF
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/webget.pro b/Code/script_idl_mv/astrolib/webget.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8cb66b513407379ddb0a8defd3a0c355b2b09efc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/webget.pro
@@ -0,0 +1,280 @@
+;+
+; NAME: 
+;    WEBGET()
+;
+; PURPOSE: 
+;    Use the IDL SOCKET procedure to get data from http servers
+;
+; EXPLANATION: 
+;     WEBGET() can access http servers - even from behind a firewall - 
+;     and perform simple downloads. Currently, text and FITS files can be 
+;     accessed.    
+;
+; CALLING SEQUENCE: 
+;      a=webget(URL)
+;
+; INPUTS: 
+;      URL - scalar string giving a fully qualified url of the form
+;          'http://server.eso.org/path/file.html'.    WEBGET() can
+;          also use other valid URLs that contain 'GET' or 'POST' codes.
+;
+; OPTIONAL INPUT KEYWORD PARAMETERS: 
+;       COPYFILE - if set to a valid filename (file must have write permission),
+;            the data contents of the web server's answer is copied to that 
+;            file.
+;       HTTP10 - If set, then use the HTTP 1.0 
+;       POST - if set to a structure, the structure tags and values
+;              will be used as post variables and POST'ed to the URL.
+;              If POST is not set, the normal HTTP GET is used to
+;              retrieve the URL.
+;       /SILENT - If set, the information error messages are suppressed
+;       TIMEOUT - Integer scalar giving number of seconds to wait to connect 
+;                or for data to arrive before giving up and issuing an error.
+;                Default=15 seconds 
+; OUTPUTS: A structure with the following fields:
+;
+;            .Header - the HTTP header sent by the server
+;
+;            .Text   - The text part of the downloaded file. If the
+;                     content type of the file was not of class
+;                     'text',  this will be an empty string.
+;
+;            .ImageHeader - Header file of a FITS-image. FITS images
+;                          are read when the content type is
+;                          'image/fits' or 'application/octet-stream'
+;                          (for dss-access). If the file is not a FITS
+;                          image,  this will be an empty string.
+;
+;            .Image - The FITS image read from the server. If the file
+;                    did not contain a FITS image,  this will be zero.
+;
+;
+; RESTRICTIONS: 
+;     The mime-type recognition is extremely limited. Only the content-type is 
+;     determined. Any text-file  will be stored in out.Text. The only other 
+;     category which can be fetched is FITS files,  which will be stored in 
+;     out.Image and out.ImageHeader.
+;
+;     PROXY: If you are behind a firewall and have to access the net through a 
+;         Web proxy,  set the environment variable 'http_proxy' to point to 
+;         your proxy server and port, e.g. 
+;         'setenv http_proxy=http://web-proxy.mpia-hd.mpg.de:3128'
+;
+;               The URL *MUST* begin with "http://".
+;
+; PROCEDURE: 
+;     Open a socket to the webserver and download the header. After deciding 
+;     whether it is text or binary, either store the text or try to read a 
+;     FITS file.
+;
+; EXAMPLE: 
+;      IDL> a=webget('http://www.mpia.de/index.html')
+;      IDL> print,a.Text
+;      or
+;
+;          > PointingRA=0.0
+;          > PointingDE=30.0
+;          > QueryURL = strcompress("http://archive.eso.org/dss/dss/image?ra="+$
+;          >                          string(PointingRA)+$
+;          >                          "&dec="+$
+;          >                          string(PointingDE)+$
+;          >                          "&x=10&y=10&Sky-Survey=DSS1&mime-type=download-fits", $
+;          >                          /remove)
+;          > a=webget(QueryURL)
+;          > tvscl,a.Image
+;          > print,a.ImageHead
+;
+;
+; MODIFICATION HISTORY: 
+;     Written by M. Feldt, Heidelberg, Oct 2001 <mfeldt@mpia.de>
+;     Use /swap_if_little_endian keyword to SOCKET  W. Landsman August 2002
+;     Less restrictive search on Content-Type   W. Landsman   April 2003
+;     Modified to work with FIRST image server-  A. Barth, Nov 2006
+;     Better recovery from errors  W. Landsman  April 2007
+;     Add support for POST access               J.D. Smith    June 2007
+;     Recognize "fits" image type used by SKYVIEW   W. Landsman  June 2007
+;     Upgraded, partially, to HTTP 1.1				M. Perrin, July 2007
+;       The HTTP 1.1 support is presently INCOMPLETE: virtual servers are
+;       supported, but chunked transfer encoding is not yet supported, so
+;       technically this is not fully HTTP 1.1 compliant.
+;     Added http10 keyword  W. Landsman   August 2007
+;     Assume since V5.6, sockets always available  W. Landsman Nov 2007
+;     Fix problem when using proxy server   W. Landsman July 2008
+;     Fix problem with /SILENT keyword  W. Landsman  Jan 2009
+;     Added check for missing Mime TYPE in CLASSANDTYPE, Zarro, December 2011
+;     Timeout applies to connecting as well as reading, default is now 15
+;               seconds  W Landsman January 2012
+;     Allow http_proxy to be upper or lower case W.L./D. Palmer Feb 2013
+;-
+
+PRO MimeType,  Header, Class, Type, Length
+;;
+;; MIME type recognition
+;
+  Class = 'text'
+  Type = 'simple'               ; in case no information found...    
+  def = strupcase(strmid(header,0,13))
+  g = where(def EQ 'CONTENT-TYPE:', Ng)
+  if Ng GT 0 then begin
+       ClassAndType = strmid(Header[g[0]], 14, strlen(Header[g[0]])-1)
+       temp=strsplit(ClassAndType, '/', /extract)
+       Class=temp[0]
+       if n_elements(temp) gt 1 then Type=temp[1]
+  ENDIF 
+  def = strupcase(strmid(header,0,15))
+  g = where(def EQ 'CONTENT-LENGTH:', Ng)
+  if Ng GT 0 then $
+         Length = long(strmid(Header[g[0]], 15, strlen(Header[g[0]])-1))
+  return
+END 
+
+FUNCTION webget,  url,  SILENT=silent, COPYFILE=copyfile, POST=post, $
+   HTTP10=http10, timeout=timeout
+   compile_opt idl2
+  ;;
+   ;;
+  ;; define the result fields
+  ;;
+  Header = strarr(256)
+  Data = strarr(256)
+  Image = 0
+  ImageHeader = ''
+  
+  ;; Setup post variables
+  if n_elements(post) ne 0 then begin 
+     method='POST'
+     t=tag_names(post)
+     post_vars=strarr(n_elements(t))
+     for i=0,n_elements(t)-1 do $
+        post_vars[i]=strlowcase(t[i])+'='+strtrim(post.(i),2)
+     post_vars=strjoin(post_vars,'&')
+     post_data=['Content-Type: application/x-www-form-urlencoded',$
+                'Content-Length: '+strtrim(strlen(post_vars),2), $
+                '', $
+                post_vars]
+  endif else method='GET'
+  
+  
+  ;;
+  ;; open the connection and request the file
+  ;;
+  ProtocolString = keyword_set(http10) ? "HTTP/1.0" : " HTTP/1.1"
+  UserAgentString= "IDL "+!version.release+' on '+!VERSION.OS+'/'+!VERSION.ARCH
+  Proxy = getenv('http_proxy')
+  if Proxy EQ '' then Proxy = getenv('HTTP_PROXY')
+  slash1 = StrPos(strmid(url, 7), '/')    ;Position of first slash
+  Server = StrMid(url, 7, slash1 )
+  if N_elements(timeout) EQ 0 then timeout=15
+
+  IF Proxy NE '' THEN BEGIN 
+     ;;
+     ;; sort out proxy name
+     ;;
+     LastColon = StrPos(Proxy, ':', /Reverse_Search)
+     ProxyPort = fix(StrMid(Proxy, LastColon+1))
+     ProxyServer = StrMid(Proxy, 7, LastColon-7)
+     ;; open the connection and send the 'GET' command
+     socket, unit, ProxyServer,  ProxyPort, /get_lun, /swap_if_little_endian, $
+             read_timeout=timeout,connect_timeout=timeout
+     printf, unit, method+' '+url+ProtocolString
+  ENDIF ELSE BEGIN 
+     ;;
+     ;; same thing easier without proxy
+     ;;
+     purl = strmid(url,slash1+7)
+     Port = 80
+     socket, unit, Server,  Port, /get_lun,/swap_if_little_endian, $
+          connect_timeout=timeout,read_timeout=timeout
+     printf, unit, method+' '+purl + ProtocolString
+  ENDELSE 
+  ;; These lines are the same for either with or without proxy.
+  ;; in HTTP 1.1 we MUST include the Host: line to allow requests
+  ;; from co-hosted virtual servers to operate properly.
+  printf, unit, "Host: "+Server
+  printf, unit, 'User-Agent: '+ UserAgentString
+  ;; HTTP 1.1 clients must either support persistent connections, or indicate
+  ;; they do not by stating Connection: close
+  printf, unit, "Connection: close"
+
+  ;; Add the POST data, if requested
+  if n_elements(post) ne 0 then printf,unit,transpose(post_data)
+  ;; Blank line required to terminate HTTP request.
+  printf, unit, ''
+
+  LinesRead = 0
+  text = 'xxx'
+  ;;
+  ;; now read the header
+  ;;
+On_IOERROR, done
+  WHILE  text NE '' do begin
+      readf, unit, text
+      Header[LinesRead] = text
+      LinesRead = LinesRead+1
+      IF LinesRead MOD 256 EQ 0 THEN $
+        Header=[Header, StrArr(256)]
+  ENDWHILE 
+DONE: On_IOERROR, NULL
+  ;;
+  if LinesRead EQ 0 then begin
+      message,'Unable to read HTTP server',/CON
+      free_lun,unit
+      return,{Header:'', Text:'', ImageHeader:ImageHeader,  Image: Image}
+  endif    
+  Header = Header[0:LinesRead-1]
+  MimeType, Header, Class,  Type, Length; analyze the header
+  ;;
+  IF Keyword_Set(CopyFile) THEN BEGIN
+      openw, wunit, CopyFile, /get_lun
+      aaa = bytarr(Length,/nozero)
+      readu, unit, aaa
+      writeu, wunit, aaa
+      free_lun, wunit
+      free_lun, unit
+      return, 1
+  ENDIF 
+  ;;
+  text = '' ;initialize text fields
+  LinesRead = 0l
+  ;;
+
+  CASE Class OF 
+      'text': BEGIN 
+          ;;
+          ;; read anything of class 'text'
+          WHILE  eof(unit) EQ 0 do begin
+              readf, unit, text
+              Data[LinesRead] = text
+              LinesRead = LinesRead+1
+              IF LinesRead MOD 256 EQ 0 THEN $
+                Data=[Data, StrArr(256)]
+          ENDWHILE 
+          if LinesRead EQ 0 then if ~keyword_set(SILENT) then $
+	       message,'ERROR - no lines of text read',/CON
+          Data = Data[0:(LinesRead-1) > 0 ]
+      END 
+      'image':BEGIN
+          CASE Type OF
+              'x-fits': Image = readfits(unit, ImageHeader)
+	      'fits': Image = readfits(unit, ImageHeader)
+              else: message,'Unrecognized  image type of ' + type
+          ENDCASE 
+      END 
+      'application':BEGIN 
+          CASE Type OF
+              'octet-stream':BEGIN ; try reading a FITS file because ESO 
+                                   ; answers this way
+                  Image = readfits(unit, ImageHeader)
+               END 
+               'force-download': BEGIN     ; need this for FIRST survey
+                   image = readfits(unit, imageheader)
+               END
+
+          ENDCASE 
+      END 
+  ENDCASE 
+
+  IF LinesRead EQ 0 THEN Data = ''
+  free_lun, unit
+  return, {Header:Header, Text:Data, ImageHeader:ImageHeader,  Image: Image}
+END
diff --git a/Code/script_idl_mv/astrolib/wfpc2_metric.pro b/Code/script_idl_mv/astrolib/wfpc2_metric.pro
new file mode 100644
index 0000000000000000000000000000000000000000..67f7722bb847390d1a09b0caf4b8b9018a8c03bc
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/wfpc2_metric.pro
@@ -0,0 +1,199 @@
+ pro wfpc2_metric,xin,yin,xout,yout,  chip, Header = h, GLOBAL = global, $
+                                  YEAR = year, FILTER = filter, RADec = radec
+;+
+; NAME:
+;   WFPC2_METRIC
+; PURPOSE:
+;   Compute the distortion in a WFPC2 image and optionally return coordinates
+; EPLANATION:
+;   Uses the distortion solution of Anderson & King (2003, PASP, 115, 113)
+;   Pixel 424, 424 on each chip remains fixed, and other pixel positions are
+;   mapped to remove nonlinearities.   If /GLOBAL is set, then all chips are
+;   put on the same reference frame where pixel 424, 424 in the WF3 chip 
+;   remains fixed. 
+; CALLING SEQUENCE:
+;      WFPC2_METRIC, xin, yin, xout, yout, [ChipNum, HEADER=, /GLOBAL
+;                                           YEAR =, FILTER=
+;                            or
+;      WFPC2_METRIC, xin, yin, a, d, HEADER=, /RAdec, /GLOBAL ]
+; INPUTS:
+;     XIN, YIN - X,Y positions (0-799) on a WFPC2 chip in 
+;               IDL convention (first pixel is 0,0), scalar or vectors
+; OUTPUTS:
+;     XOUT, YOUT - X,Y positions in the undistorted frame, same number of 
+;                  elements as XIN, YIN
+;                          or if /RADEC is set
+;     AA, DD  - Right ascension and declination (in degrees) corresponding 
+;               to the input coordinates after distortion correction.
+; OPTIONAL INPUT:
+;     ChipNum - Integer  1, 2, 3, or 4  specifying the WFPC2 chip number
+;             1-PC, 2-WF2, 3-WF3, 4-WF4.   If not supplied, then WFPC2_METRIC
+;             will try to read the value from the DETECTOR in the FITS header.
+; OPTIONAL INPUTS:
+;     /GLOBAL - If set, then positions are returned in a master reference 
+;              frame with pixel 424,424 of WF3 remaining fixed.   Thus, 
+;              information  concerning the  interchip separation and 
+;              orientation (with a weak dependence on time and filter) is 
+;              incorporated. 
+;     Header - FITS header with astrometry for a particular chip.
+;             If both /RADec and /Global are set, then the header must be
+;             from the WF3 chip. 
+;     /RADec - If set, then astrometry information in the FITS header (which
+;             must be supplied as a keyword) is used to convert the output
+;             to Right Ascension and declination (both in degrees).
+;     FILTER - Filter name needed if /GLOBAL is set, must be either 'F300W'
+;             'F336W', 'F439W', 'F555W' or 'F814W'; otherwise the plate scale
+;             for F555W is assumed.   WFPC2_METRIC will try to read this 
+;             value from the FITS header if not supplied as a keyword.
+;     YEAR -  Observation year including fraction (e.g. 1998.56) needed if
+;             /GLOBAL is set.  WFPC2_METRIC will try to read this value from 
+;             the FITS header if not supplied as a keyword.  The time 
+;             correction is currently applied through the year 2002; later 
+;             dates will use the year 2002 correction.              
+; EXAMPLES:
+;     (1) Find the undistorted X,Y coordinates of position 682.3,234.2 on chip 1 
+;         (the PC chip).
+;          IDL> WFPC2_METRIC, 682.3, 234.2, xout, yout, 1 
+;             ==> xout = 681.13   yout = 235.05
+;
+;     (2) Determine the RA and Dec of position 682.3, 234.2 on chip 1 on the 
+;         WFPC2 image U2Z30201T
+;         IDL> WFPC2_READ, 'u2z30201t.c0h', im,h   ;Get header for chip 1
+;         IDL> WFPC2_METRIC, 682.3, 234.2, aa, dd, header= h,/RADec
+;         IDL> print, adstring(aa,dd,2)
+;         05 20 53.572  -69 35 18.17
+;
+;         Note that a chip number did not need to be specified since its value
+;         is in the FITS header
+;
+;     (3) As above, but now compute coordinates in the global frame, needed
+;         for example, to compute the distance between stars on two different
+;         chips. 
+;
+;        First get headers for chips 1 and 3
+;        IDL> WFPC2_READ, 'u2z30201t.c0h', im1,h1, im3,h3,num=[1,3]   
+;        IDL> WFPC2_METRIC, 682.3, 234.2, aa, dd, 1, header=h3,/RADec,/GLOBAL
+;        IDL> print, adstring(aa,dd,2)
+;         05 20 53.513  -69 35 17.98
+;
+;        Note that with /GLOBAL set, that the header must be for WF3, even
+;        though coordinates are being computed for chip 1.   Also note that
+;        the time and filter will be read from the FITS header.   Finally,
+;        note that the coordinates given in examples (2) and (3) differ
+;        slightly, because the chip separations incorporated in the FITS 
+;        headers differ slightly from those in the Anderson & King solution.   
+; PROCEDURES USED:
+;     LINTERP, SXPAR(), XYAD, YMD2DN()
+; REVISION HISTORY:
+;     Written     W. Landsman         March 2003
+;-
+ On_error,2
+ compile_opt idl2
+ if N_params() LT 4 then begin
+     print,'Syntax - WFPC2_METRIC, xin, yin, xout, yout, chip, /GLOBAL, '
+     print,'                  /RADec, HEADER =, YEAR=, FILTER = '
+     return
+ endif
+
+ have_header = N_elements(h) GT 0 
+ if N_elements(chip) EQ 0 then if have_header then $
+               chip = sxpar(h,'DETECTOR')
+ if (chip LT 1) or (chip GT 4) then message, $
+     'ERROR - Supplied chip number must be between 1 and 4'
+ k = chip-1
+       pwfpc2gc  =   $                                             ;Order
+   [    0.000, 0.418, 0.000, 0.051, 0.000,-0.028, 0.000, 0.070 , $ ;x
+        0.000,-0.016, 0.000,-0.015, 0.000,-0.036, 0.000, 0.059  , $ ;y
+       -0.525,-0.280,-0.624,-0.038,-0.349,-0.027,-0.489,-0.050 , $ ;xx
+       -0.268,-0.292,-0.411,-0.568,-0.353,-0.423,-0.391,-0.485 , $ ;xy
+       -0.249,-0.470,-0.092,-0.444, 0.009,-0.373,-0.066,-0.406 , $ ;yy
+       -1.902,-0.011,-1.762, 0.003,-1.791, 0.004,-1.821,-0.015 , $ ;xxx
+        0.024,-1.907, 0.016,-1.832, 0.006,-1.848, 0.022,-1.890 , $ ;xxy
+       -1.890, 0.022,-1.825, 0.011,-1.841, 0.006,-1.875, 0.022 , $ ;xyy
+       -0.004,-1.923, 0.010,-1.730, 0.021,-1.788,-0.006,-1.821  ]  ;yyy
+
+;        APC1  BPC1    AWF2   BWF2   AWF3   BWF3   AWF4   BWF4  
+      
+  
+      x = (xin-424.0)/375.0
+      y = (yin-424.0)/375.0
+
+      pwfpc2gc  = reform(pwfpc2gc,2,4,9,/over)
+      
+      t = pwfpc2gc[0,k,*]
+      x2 = x^2
+      x3 = x^2*x
+      y2 = y^2
+      y3 = y2*y
+      xout = xin + t[0]*x + $
+                   t[1]*y + $
+                   t[2]*x2 + $ 
+                   t[3]*x*y  + $
+                   t[4]*y2 + $
+                   t[5]*x3 + $
+                   t[6]*x2*y + $
+                   t[7]*y2*x + $
+                   t[8]*y3 
+;     for x use the A coeffs...
+     t = pwfpc2gc[1,k,*]
+     yout = yin +  t[0]*x + $
+                   t[1]*y + $
+                   t[2]*x2 + $ 
+                   t[3]*x*y  + $
+                   t[4]*y2 + $
+                   t[5]*x3 + $
+                   t[6]*x2*y + $
+                   t[7]*y2*x + $
+                   t[8]*y3
+      if keyword_set(GLOBAL) then begin
+
+             radeg = 180.0d/!Dpi
+            if not keyword_set(year) then begin
+               if have_header then begin
+                   mjd = sxpar(h,'EXPSTART')
+                   CALDAT, mjd+2400000.5d, Month, Day, Year
+                   dy = ymd2dn(year,month,day)
+                   year = year + dy/365.25
+               endif else year = 1998.0
+            endif
+            alphak = [0.45729, 1.00020, 1.0000, 1.00048]
+            theta = [180.178, 269.682, 0.0   , 90.551 ]
+            x0_94 = [-140.2, +430.1, +425.0, -347.2 ]
+            x0_98 = [-139.4, +430.0, +425.0, -346.1]
+            x0_02 = [-138.8, +430.3, +425.0, -345.5]         
+            y0_94 = [-123.3, -328.4, +425.0, +423.9 ]
+            y0_98 = [-121.5, -327.9, +425.0, 424.2 ]
+            y0_02 = [-120.8, -327.2, +425.0, 424.4 ]
+            linterp,[1994.,1998.,2002.],[x0_94[k],x0_98[k],x0_02[k]],year,x0
+            linterp,[1994.,1998.,2002.],[y0_94[k],y0_98[k],y0_02[k]],year,y0
+            if N_elements(filter) EQ 0 then begin 
+                if have_header then filter = strtrim(sxpar(h,'FILTNAM1'),2) $
+                               else filter = 'F555W'
+            endif
+            alphaf = 1.0
+            case strupcase(FILTER) of 
+            'F300W': alphaf = 0.99953
+            'F336W': alphaf = 0.99953
+            'F439W': alphaf = 0.99978
+            'F555W': alphaf = 1.0
+            'F814W': alphaf = 1.00036
+            else:  message,/CON, $
+                   'WARNING - No scale factor determined for filter '+filter +$
+                   ' using F555W factor'
+            endcase
+            xnew = xout - 425.0
+            ynew = yout - 425.0
+            ctheta  = cos(theta[k]/radeg)
+            stheta = sin(theta[k]/radeg)
+            alpha = alphaf*alphak[k]
+            xout = alpha*(xnew*ctheta-ynew*stheta) + x0
+            yout = alpha*(xnew*stheta + ynew*ctheta) + y0
+      endif
+
+      if  keyword_set(RADec) then begin 
+                xyad,h,xout,yout,a,d
+                xout = a
+                yout = d
+      endif
+      return
+      end 
diff --git a/Code/script_idl_mv/astrolib/wfpc2_read.pro b/Code/script_idl_mv/astrolib/wfpc2_read.pro
new file mode 100644
index 0000000000000000000000000000000000000000..eef87a62873eade5d97d82a7306e93c7ebc9c501
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/wfpc2_read.pro
@@ -0,0 +1,292 @@
+pro wfpc2_read,filename,chip1,header1,chip2,header2, $
+               chip3,header3,chip4,header4,num_chip=num_chip, $
+               trim=trim, path = path,batwing=batwing
+;+
+; NAME:
+;    WFPC2_READ
+;
+; PURPOSE:
+;   Read WFPC2 images in either FITS or STSDAS format into IDL variables.
+;
+; EXPLANATION:
+;   This a versatile procedure for reading Wide Field Planetary Camera 2 
+;   (WFPC2) images.   One can read either multi-extension FITS or  STSDAS or
+;   STSDAS converted to FITS format, and specific 
+;   chip or chips.    One can also read all four chips into a "batwing" mosaic--
+;   so-called because the PC chip (chip 1) has a plate scale of 0.045", while
+;   the other three WF chips have a plate scale of 0.1"
+; 
+; CALLING SEQUENCE:
+;    WFPC2_READ,filename,chip1,hdr1,chip2,hdr2,chip3,hdr3,chip4,hdr4
+;                   or
+;    WFPC2_READ,filename,chip,hdr, NUM_CHIP = [1,2,3,4], [/TRIM, PATH = ]
+;                   or
+;    WFPC2_READ,filename,image,hdr,/BATWING
+;
+; INPUTS:
+;    filename - Name of FITS or STSDAS file with a stack of images from
+;            the four WF/PC-2 chips, followed by a FITS ASCII
+;            table with header parameters for each chip.    If the file
+;            name extension ends in 'h' then it is assumed to be an
+;            STSDAS file.   If no extension is supplied, and the file is
+;            is not found, then WFPC2_READ first tries appending a '.fits'
+;            extension, and then tries appending a '.c0h' extension.  
+;
+;            The file may als be gzip compressed (with a .gz extension) 
+; INPUT KEYWORD PARAMETERS:
+;    NUM_CHIP - Integer scalar or vector, subset of 1, 2, 3, 4, specifying 
+;               particular chip numbers to read.    Outputs will be in same 
+;               order as specification of subset.   (See Example 2.)
+;    /TRIM   - If set, trim off areas with no image and re-orient so that
+;              all  the chips have a common orientation suitable for insertion 
+;               into "bat-wing" mosaic (no image distortion removal, however).
+;    PATH   -   scalar string specifying a !PATH-like list of directories
+;               in which to search for the file.   Default is to look only
+;               in the current directory.
+;    /BATWING -  Return a 1600 x 1600 array containing all four chips in a
+;               "bat wing" mosaic formation.     This image is mainly for 
+;               display  purposes, since the PC chip is compressed to match the plate 
+;               scale of the WF chips.    In addition, a small astrometry error
+;               is introduced since chips do not have the same rotation, nor    
+;               are they aligned at the integer pixel level.
+; OUTPUTS:
+;    chipN    - 800 X 800 image from chip N.   If /TRIM is set then the output
+;               size is somewhat smaller (e.g. 756 x 757)
+;    headerN  - Individual FITS header for chip N with correct astrometry.
+;
+; PROCEDURES USED:
+;     For FITS I/O: FITS_CLOSE, FITS_OPEN, FITS_READ
+;     For STSDAS I/O: EXTGRP, FTGET(), SXOPEN, SXREAD()
+;     Other procedures:  CHECK_FITS, FDECOMP, FIND_WITH_DEF(), FREBIN, HEXTRACT, 
+;           HROTATE, SXADDHIST, SXADDPAR, SXPAR()
+; EXAMPLE: 
+;    (1) Read all four chips of the FITS file u2ou0201t_c0f.fits
+; 
+;    IDL> wfpc2_read,'u2ou0201t_c0f',c1,h1,c2,h2,c3,h3,c4,h4
+;
+;     (2) Note that supplying the .fits extension is optional.   Now read only
+;     chips 1 (the PC chip) and 3.   Trim off portions of the arrays where
+;     there is no image.   
+;
+;    IDL> wfpc2_read,'u2ou0201t_c0f',c1,h1,c3,h3,num=[1,3],/trim
+;
+;      (3) Note that with the /TRIM option the output chip sizes are no longer
+;          800 x 800 but odd sizes such as 770 by 753.    Now read all 4 chips
+;          into a 1600 x 1600 "batwing" mosaic
+;
+;    IDL> wfpc2_read,'u2ou0201t_c0f',im,h,/batwing
+;
+; MODIFICATION HISTORY:
+;     Written by W. Landsman, Raytheon STX, for IDL V5.0     June 1998
+;     Based on code by Robert Hill, Raytheon STX
+;     Better astrometry of PC image in "batwing" configuration, W. Landsman
+;                August 1999
+;     Use vector call to SXADDHIST  W. Landsman   March 2003
+;     Don't use EXECUTE() for V6.1 or later W. Landsman Dec 2006
+;     Assume since V6.1  W. Landsman  June 2009
+;     Ability to read multi-extension format FITS  W. Landsman May 2010
+;     Correct header in MEF form when only reading PC chip.  W.L. July 2010
+;-
+ compile_opt idl2
+ if N_params() LT 2 then begin
+    print,'Syntax:'
+    print,' WFPC2_READ,filename,chip1,hdr1,chip2,hdr2,chip3,hdr3,chip4,hdr4'
+    print,'           or'
+    print,' WFPC2_READ, filename,chip,hdr, NUM_CHIP =[1,2,3,4], [/TRIM, PATH=]'
+    print,'           or'
+    print,' WFPC2_READ,filename,image,hdr,/BATWING, PATH=]'  
+    return
+ endif
+
+; EXTPARS gives the region of each chip containing valid data.    ROTPARS 
+; gives the IDL ROTATE parameter to apply to each chip to give them a common 
+; orientation.   X1 and Y1 give the starting pixel of each chip in the 
+; "batwing" mosaic.
+
+ extpars = [[44, 799, 52, 799], [ 0, 773, 46, 799], $
+           [ 0, 769,  0, 752], [44, 799,  0, 756] ]
+ rotpars = [0, 1, 2, 3]
+ x1 = [800,26,30,800]      
+ y1 = [800,800,47,43]
+
+ if N_elements(num_chip) EQ 0 then $
+	if (N_params() LE 3) and ~keyword_set(BATWING) then num_chip = 1 
+ if N_elements(num_chip) GT 0 then num_c = num_chip $
+    else num_c = [1,2,3,4]
+ if keyword_set(batwing) then begin 
+      chip1 = fltarr(1600,1600)
+      num_c = [1,2,3,4]
+      trim = 1
+ endif
+ Nout = N_elements(num_c)
+
+; If the specified file is not found, try adding a '.fits' and then a '.c0h'
+; extension
+ 
+ if N_elements(PATH) EQ 0 then path = ''
+ a = FIND_WITH_DEF(filename, path, '.fits,.c0h')
+ FDECOMP, a[0], disk, dir, fname, ext
+
+ if strlowcase(strmid(ext,2,1) EQ 'h') then begin      ;SDAS format
+      SXOPEN, 1, a[0], htab
+      for i = 0, Nout-1 do begin
+        j = num_c[i] - 1
+        thischp = SXREAD(1, j, par)
+        thishdr = htab 
+        EXTGRP, thishdr, par         ;Insert group parameters into FITS header
+        if keyword_set(TRIM) then begin 
+           HROTATE, thischp, thishdr, rotpars[j]
+           HEXTRACT, thischp, thishdr, extpars[0,j], extpars[1,j], $
+               extpars[2,j], extpars[3,j], /SILENT
+        endif
+        ii = strtrim(i+1,2)
+        jj = strtrim(j+1,2)
+    sxaddhist, ['----------------------------------------------------------', $
+                '      WFPC2_READ:  ' + systime(),  $
+                '      Header parameters for chip ' + jj + $
+                 ' replaced from group parameters'],  thishdr
+        if keyword_set(batwing) then begin
+           if i EQ 0 then $
+                    chip1[x1[0],y1[0]] = FREBIN(thischp,345.7,342,/total) else $
+                    chip1[x1[i],y1[i]] =  thischp
+           if i EQ 3 then begin
+                    crpix = sxpar(thishdr,'CRPIX*')
+                    sxaddpar, thishdr, 'CRPIX1', crpix[0] + x1[3]
+                    sxaddpar, thishdr, 'CRPIX2', crpix[1] + y1[3]
+                    header1 = thishdr
+                    CHECK_FITS,chip1,header1,/update,/silent,/FITS
+            endif 
+        endif else begin  
+	   (scope_varfetch('chip' + ii)) = temporary(thischp)
+	   (scope_varfetch('header' + ii)) = thishdr
+	endelse
+        endfor
+ 
+ endif else begin
+ 
+ FITS_OPEN, a[0], fcb
+
+; Is a converted GEIS file or the newer multi-extension format (MEF)?
+ if (fcb.nextend EQ 4) && (fcb.naxis[0,0] EQ 0) then begin 
+       if Nout EQ 1 then begin
+             FITS_READ, fcb, chip1, header1, exten_no=num_chip
+	endif else begin 
+	      d = fltarr(800,800,4,/nozero)
+	      if keyword_set(batwing) then $
+	           fits_read, fcb, chip_pc, header1, exten=1 else $
+		   fits_read, fcb, chip1, header1, exten=1
+              fits_read,fcb, chip2,header2
+              fits_read,fcb, chip3,header3
+              fits_read,fcb, chip4,header4
+              if keyword_set(batwing) then begin 
+	      chip1[x1[0],y1[0]] = FREBIN(chip_pc,345.7,342.0, /total)
+	      chip1[x1[1],y1[1]] = chip2
+	      chip1[x1[2],y1[2]] = chip3
+	      chip1[x1[3],y1[3]] = chip4
+	      crpix = sxpar(header3,'CRPIX*')
+              sxaddpar, header1, 'CRPIX1', crpix[0] + x1[3]
+              sxaddpar, header1, 'CRPIX2', crpix[1] + y1[3]
+              CHECK_FITS, chip1, header1, /update, /silent, /FITS
+              endif else if keyword_set(trim) then begin 
+              HROTATE, chip1, header1, rotpars[0]
+              HEXTRACT, chip1, header1, extpars[0,0], extpars[1,0], $
+          extpars[2,0], extpars[3,0],/SILENT
+              HROTATE, chip2, header2, rotpars[1]
+              HEXTRACT, chip2, header2, extpars[0,1], extpars[1,1], $
+          extpars[2,1], extpars[3,1],/SILENT
+              HROTATE, chip3, header3, rotpars[2]
+              HEXTRACT, chip3, header3, extpars[0,2], extpars[1,2], $
+          extpars[2,2], extpars[3,2],/SILENT
+              HROTATE, chip4, header4, rotpars[3]
+              HEXTRACT, chip4, header4, extpars[0,3], extpars[1,3], $
+          extpars[2,3], extpars[3,3],/SILENT
+
+	  endif
+	  endelse    	     
+          return
+  endif 
+ if Nout EQ 1 then begin
+	ns = fcb.axis[0,0]
+	nl = fcb.axis[1,0]
+	i1 = (ns*nl)*(num_chip-1)
+	i2 = i1 + ns*nl-1
+	FITS_READ, fcb, chip1, h, first=i1, last=i2
+	chip1 = reform(chip1,ns,nl)
+ endif else FITS_READ,fcb,d,h
+
+; Now read the first FITS extension which contains the ASCII table, giving
+; separate astrometry parameters for each of the four chips.
+
+ FITS_READ, fcb, dtab, htab, /no_pdu
+ tf = sxpar(htab,'TFIELDS')
+ name = sxpar(htab,'TTYPE*')
+ fmt = sxpar(htab,'TFORM*')
+ comment = strarr(tf)
+ for j=0,tf-1 do comment[j] = sxpar(htab,name[j])
+ 
+ if fcb.axis[1,0] LT max(num_c) then begin
+	message, /inf,'Image ' + filename + ' contains only PC image'
+	num_c = fcb.axis[1,0]
+ endif
+ FITS_CLOSE, fcb
+
+ ftinfo,htab,ft_str
+ for i = 0, Nout-1 do begin
+    cn = num_c[i]
+    cn_0 = cn - 1
+    cn_str = strtrim(num_c[i],2)
+    cn_arg = strtrim(i+1,2)
+    hdr = 'header' + cn_arg
+    chp = 'chip' + cn_arg
+    thishdr = h
+    sxaddhist, ['----------------------------------------------------------', $
+                '      WFPC2_READ:  ' + systime(), $
+                '      Header parameters for chip ' + cn_str $
+              + ' replaced from table.'], thishdr   
+ for j=0,tf-1 do begin
+         value = ftget(ft_str,dtab,j+1,cn_0)
+        sxaddpar, thishdr,name[j],value[0],comment[j],format=fmt[j]
+    endfor
+     if nout GT 1 then begin
+
+	thischp = d[*,*,cn_0] 
+        CHECK_FITS, thischp,  thishdr, /fits, /update, /silent
+          
+    if keyword_set(trim) then begin
+        HROTATE, thischp, thishdr, rotpars[cn_0]
+        HEXTRACT, thischp, thishdr, extpars[0,cn_0], extpars[1,cn_0], $
+          extpars[2,cn_0], extpars[3,cn_0],/SILENT
+    endif
+       
+      if keyword_set(batwing) then begin
+
+           if i EQ 0 then $
+                    chip1[x1[0],y1[0]] = FREBIN(thischp,345.7,342.0, /total) else $
+                    chip1[x1[i],y1[i]] =  thischp
+           if i EQ 3 then begin 
+                    crpix = sxpar(thishdr,'CRPIX*')
+                    sxaddpar, thishdr, 'CRPIX1', crpix[0] + x1[3]
+                    sxaddpar, thishdr, 'CRPIX2', crpix[1] + y1[3]
+                    header1 = thishdr
+                    CHECK_FITS, chip1, header1, /update, /silent, /FITS
+           endif
+    endif else begin
+    
+        (SCOPE_VARFETCH(chp)) = temporary(thischp) 
+        (SCOPE_VARFETCH(hdr)) = temporary(thishdr)
+    endelse
+   endif else begin
+       header1 = thishdr
+       CHECK_FITS, chip1, header1, /fits,/update,/silent  
+       if keyword_set(TRIM) then begin
+         HROTATE, chip1, header1, rotpars[cn_0]
+         HEXTRACT, chip1, header1, extpars[0,cn_0], extpars[1,cn_0], $
+              extpars[2,cn_0], extpars[3,cn_0],/silent
+       endif
+   endelse
+
+endfor  
+
+ endelse 
+return
+end
diff --git a/Code/script_idl_mv/astrolib/where_tag.pro b/Code/script_idl_mv/astrolib/where_tag.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7ef16755c370c7edff1d35cd866a5c9e67fe7d33
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/where_tag.pro
@@ -0,0 +1,170 @@
+function where_Tag, Struct, Nfound,	TAG_NAME=Tag_Name,	$
+					TAG_NUMBER=Tag_Num,	$
+					ISELECT=ipart, NOPRINT=noprint,	$
+					RANGE=range, VALUES=values
+;+
+; NAME:
+;	WHERE_TAG
+; PURPOSE:
+;	Like WHERE but works on structure tag names
+; EXPLANATION:
+;	Obtain subscripts of elements in structure array for which
+;	a particular Tag has values in a range or matching specified values.
+;	Like the WHERE function but for use with structures
+; CATEGORY:
+;			Structures
+; CALLING SEQUENCE:
+;	 w = where_tag( struct, [ Nfound,  TAG_NAME=, TAG_NUMBER = , RANGE =, 
+;				VALUES =, RANGE =, ISELECT =, /NOPRINT ]
+;
+; INPUTS:
+;	Struct = structure array to search.
+;
+; INPUT KEYWORDS:
+;	User *must* specify (1) TAG_NAME or TAG_NUMBER to search, and (2)
+;		the VALUES or RANGE to search on
+;
+;	TAG_NAME = Scalar string specifying Tag Name
+;	TAG_NUMBER = otherwise give the Tag Number,
+;	RANGE = [min,max] range to search for in Struct,
+;	VALUES = one or array of numbers to match for in Struct,
+;	ISELECT= specifies indices to select only part of structure array,
+;		(use it to recycle subscripts from previous searches).
+;	/NOPRINT = suppress informational messages about nothing found.
+;
+; OUTPUTS:
+;	Nfound = # of occurrences found.
+;
+; RESULT:
+;	Function returns subscripts (indices) to desired elements.
+;
+; EXAMPLES:
+;	Suppose STR is a structure with tags CAT_NO:indgen(10), and 
+;		NAME:strarr(10).   Find the indices where STR.CAT_NO is
+;		between 3 and 5.
+;
+;	IDL> print, WHERE_TAG( str, TAG_NAME = 'CAT_NO', VALUE = [3,4,5] )  ;or
+;	IDL> print, WHERE_TAG( str, TAG_NUM = 0, RANGE = [3,5]) 
+;
+; PROCEDURE:
+;	Get tag number and apply the WHERE function appropriately.
+;
+; MODIFICATION HISTORY:
+;	written 1990 Frank Varosi STX @ NASA/GSFC
+;	Stop printing "Tag <xxx> not found" with /NOPRINT, CD Pike 8-Jun-93
+;       Use STRJOIN for display  W.L. July 2009
+;-
+;First check required parameters...
+
+        On_Error,2
+	compile_opt idl2
+	Ntag = N_tags( Struct )
+
+	if (Ntag LE 1) then begin
+		message,"expecting a Structure Array, try again...",/CONTIN
+		return,[-1]
+	   endif
+
+	if (N_elements( Tag_Num ) NE 1) AND $
+	   (N_elements( Tag_Name ) NE 1) then begin
+		message,"specify TAG_NAME= or TAG_NUMBER= to search",/CONTIN
+		return,[-1]
+	   endif
+
+	Tags = Tag_names( Struct )
+
+	if N_elements( Tag_Name ) EQ 1 then begin
+		Tag_Name = strupcase( Tag_Name )
+		Tag_Num = where( Tags EQ Tag_Name )
+		Tag_Num = Tag_Num[0]
+		if (Tag_Num LT 0) then begin
+		 if ~keyword_set( noprint ) then $
+			message,"Tag <"+Tag_Name+"> not found",/CONTIN
+			return,[-2]
+		   endif
+	   endif
+
+	if (Tag_Num LT 0) OR (Tag_Num GE Ntag) then begin
+		message,"Tag# " + strtrim(Tag_Num,2) + " exceeds Max Tag# " $
+			+ strtrim(Ntag-1,2) + " in structure",/CONTIN
+		return,[-1]
+	   endif
+
+	if N_elements( ipart ) GT 0 then begin		;check if any searching	
+							;on a subset of input.
+		w = where( ipart GE 0, nf )
+		if (nf LE 0) then return,[-1]
+		if (nf LT N_elements( ipart )) then ipart = ipart[w]
+	   endif
+
+;Now find out where for RANGE :
+
+	if N_elements( range ) EQ 2 then begin
+
+		if N_elements( ipart ) GT 0 then begin
+
+		     w = where( (Struct[ipart].(Tag_Num) GE range[0]) AND $
+				(Struct[ipart].(Tag_Num) LE range[1]), Nfound )
+
+		     if (Nfound GT 0) then windex = ipart[w] else windex = w
+
+		 endif $
+		  else 	windex = where( (Struct.(Tag_Num) GE range[0]) AND $
+					(Struct.(Tag_Num) LE range[1]), Nfound )
+
+		if (Nfound LE 0) && (~keyword_set( noprint ) ) then begin
+			strnums = strtrim( range, 2 )
+			string = strnums[0] + "," + strnums[1]
+			message," NO values of <" + Tags[Tag_num] + $
+				"> found in the Range [" + string + "]",/CONTIN
+		   endif
+;where Values:
+
+	 endif else if N_elements( values ) GE 1 then begin
+
+		Nval = N_elements( values )
+		vals = [values]
+		Nfound = 0
+
+		if N_elements( ipart ) GT 0 then begin
+
+		    for v=0,Nval-1 do begin
+			w = where( Struct[ipart].(Tag_Num) EQ vals[v], Nf )
+			if (Nf GT 0) then begin
+				if (Nfound GT 0) then ww = [ww,w] else ww = w
+			   endif
+			Nfound = Nfound + Nf
+		      endfor
+
+		    if (Nfound GT 0) then windex = ipart[ww[sort( ww )]] $
+				     else windex = w
+
+		 endif else begin
+
+		    for v=0,Nval-1 do begin
+			w = where( Struct.(Tag_Num) EQ vals[v], Nf )
+			if (Nf GT 0) then begin
+				if (Nfound GT 0) then ww = [ww,w] else ww = w
+			   endif
+			Nfound = Nfound + Nf
+		      endfor
+
+		    if (Nfound GT 0) then windex = ww[sort( ww )] $
+				     else windex = w
+
+		  endelse
+
+		if (Nfound LE 0) && (~keyword_set( noprint ) ) then begin
+			string = strjoin( strtrim(vals,2) ,',') 
+			message," NO values of <" + Tags[Tag_num] + $
+				"> found Equaling [" + string + "]",/CONTIN
+		   endif
+
+	   endif else begin
+
+		message,"must specify a RANGE=[#,#]  or VALUES=#('s)",/CONTIN
+		windex=[-1]
+	    endelse
+
+return, windex
+end
diff --git a/Code/script_idl_mv/astrolib/wherenan.pro b/Code/script_idl_mv/astrolib/wherenan.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b53fb1da6a1fccf3e991969a5b57dfd163732ec3
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/wherenan.pro
@@ -0,0 +1,113 @@
+        FUNCTION WHERENAN, ARRAY, COUNT
+;+
+; NAME:
+;      WHERENAN()
+; PURPOSE:
+;      Find the indices of all big-endian NaN values in an array.  OBSOLETE
+; EXPLANATION: 
+;      Find the positions of all values within an array that correspond to the
+;      big-endian NaN (not-a-number) special values.    
+;
+;      THIS PROCEDURE ONLY IDENTIFIES BIG_ENDIAN NaN VALUES.  DO NOT USE IT 
+;      TO IDENTIFY NaN VALUES IN GENERAL.   Instead, to identify NaN values on 
+;      the host machine use the FINITE() function 
+;
+;      IDL>     result = where( finite(array,/NAN) )
+;       
+;      The main purpose of this routine is to catch  NaN special values 
+;      written in big_endian format (e.g. FITS data) on a little endian 
+;      machine prior to conversion with e.g. IEEE_TO_HOST.    It was needed
+;      many years ago because VMS machines could not handle big-endian 
+;      special values, but this routine is now kept only for backwards 
+;      compatibility.
+;
+; CALLING SEQUENCE:
+;      Result = WHERENAN( ARRAY [, COUNT ] )
+;
+; INPUT PARAMETERS:
+;      ARRAY   = Array to test against the IEEE NaN special values.  Must be
+;                of either floating point, double-precision, or complex type.
+;
+; OUTPUTS:
+;      The result of the function is the indices of all values of ARRAY
+;      corresponding to the IEEE NaN specification, similar to the IDL WHERE
+;      function.
+;
+; OPTIONAL OUTPUT PARAMETERS:
+;      COUNT   = Number of values found corresponding to IEEE NaN.
+;
+; SIDE EFFECTS:
+;      If no NaN values are found, or if ARRAY is not of type float, double
+;      precision, or complex, then -1 is returned, and COUNT is set to 0.
+;
+; RESTRICTIONS:
+;      ARRAY must be of type float, double-precision, or complex.
+;
+; PROCEDURE:
+;      The bit patterns of the numbers being tested are compared against the
+;      IEEE NaN standard.
+;
+; MODIFICATION HISTORY:
+;      William Thompson, Feb. 1992.
+;      William Thompson, Oct. 1992, fixed bug regarding order of bytes on VAX
+;              machines.
+;      Converted to IDL V5.0   W. Landsman   September 1997
+;-
+;
+        ON_ERROR,2
+;
+;  Check the number of parameters.
+;
+        IF N_PARAMS() LT 1 THEN MESSAGE,        $
+                'Syntax:  Result = WHERENAN(ARRAY [,COUNT])'
+;
+;  Parse the input array based on the datatype.
+;
+        SZ = SIZE(ARRAY)
+        CASE SZ[SZ[0]+1] OF
+;
+;  Single precision floating point.
+;
+                4:  BEGIN
+                        LARRAY = LONG(ARRAY,0,N_ELEMENTS(ARRAY))
+                        BYTEORDER,LARRAY,/NTOHL
+                        E0 = '7F800000'X
+                        E = LARRAY AND E0
+                        F = LARRAY AND '7FFFFF'X
+                        RESULT = WHERE((E EQ E0) AND (F NE 0), COUNT)
+                        END
+;
+;  Double precision floating point.
+;
+                5:  BEGIN
+                        LARRAY = LONG(ARRAY,0,2,N_ELEMENTS(ARRAY))
+                        BYTEORDER,LARRAY,/NTOHL
+                        E0 = '7FF00000'X
+                        E = LARRAY[0,*] AND E0
+                        F1 = LARRAY[0,*] AND 'FFFFF'X
+                        RESULT = WHERE((E EQ E0) AND ((F1 NE 0) OR      $
+                                (LARRAY[1,*] NE 0)), COUNT)
+                        END
+;
+;  Single precision complex floating point.
+;
+                6:  BEGIN
+                        LARRAY = LONG(ARRAY,0,2,N_ELEMENTS(ARRAY))
+                        BYTEORDER,LARRAY,/NTOHL
+                        E0 = '7F800000'X
+                        E1 = LARRAY[0,*] AND E0
+                        E2 = LARRAY[1,*] AND E0
+                        F1 = LARRAY[0,*] AND '7FFFFF'X
+                        F2 = LARRAY[1,*] AND '7FFFFF'X
+                        RESULT = WHERE(((E1 EQ E0) AND (F1 NE 0)) OR    $
+                                ((E2 EQ E0) AND (F2 NE 0)), COUNT)
+                        END
+                ELSE:  BEGIN
+                        MESSAGE,'Data type must be floating point',/INFORMATIONAL
+                        RESULT = -1
+                        COUNT = 0
+                        END
+        ENDCASE
+;
+        RETURN, RESULT
+        END
diff --git a/Code/script_idl_mv/astrolib/write_ipac_table.pro b/Code/script_idl_mv/astrolib/write_ipac_table.pro
new file mode 100644
index 0000000000000000000000000000000000000000..04f34d7960c6155e83ee8d7a22a4ee6f03c1cb03
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/write_ipac_table.pro
@@ -0,0 +1,290 @@
+PRO write_ipac_table, in_struct, outfile, short_format=short_format, exact_format=exact_format, format=format
+
+;+
+; NAME: 
+;   WRITE_IPAC_TABLE
+;
+; PURPOSE:  
+;   Write an IPAC table from an IDL structure.
+;
+; EXPLANATION:  
+;   Writes an IPAC table to a file from an IDL structure.  If the
+;   structure has certain pre-defined tag names (see below), the 
+;   header information will be written to the table.
+;
+; CALLING SEQUENCE: 
+;   write_ipac_table, in_struct, outfile, [/short_format, /exact_format, format=format]
+;
+; INPUTS: 
+;   IN_STRUCT -- an IDL structure containing the table to be written to the output
+;           ascii file.  Header information must be in the keywords, HEADER_TABLE_HEADER,
+;           HEADER_DATA_UNITS, and HEADER_NULL_VALUES.  
+;
+;   OUTFILE -- string containing the name of the output file
+;
+; OPTIONAL INPUT:
+;   /SHORT_FORMAT -- if set, uses IDL "print" formats.
+;
+;   /EXACT_FORMAT -- if set, floating point and double precision
+;                     data are written with formats of (e16.9) and
+;                     (e24.17), respectively.  This option takes
+;                     precedence over the /SHORT_FORMAT option.
+;
+;   FORMAT -- A user supplied format statement that will
+;                     override the other formating options.  This
+;                     is given in the usual IDL form, e.g. '(f13.6)'.
+;     
+; OUTPUTS:
+;    On completion, an ascii table will be written to the outfile.
+;
+; PROCEDURES USED:  
+;    GET_DATE
+;
+; NOTES:  
+;    The default format is IDL's '(f)', ('d'), etc.
+;    The procedure will write out header lines (lines starting with "\"), data unit and null
+;    value lines if the structure has tag names "HEADER_TABLE_HEADER",
+;    "HEADER_DATA_UNITS" and "HEADER_NULL_VALUES"
+;    respectively.  If "HEADER_COL_NAMES_ORIG" and
+;    "HEADER_COL_TYPES_ORIG" are present, these will
+;    be the column names and types.  Currently forces "RA" and "DEC" to lower case.
+;
+; MODIFICATION HISTORY:
+;       Initial version - H. Teplitz, IPAC September 2010 
+;       Output original column names/types if present - T. Brooke, IPAC June 2013
+;-
+
+;Copyright � 2013, California Institute of Technology
+;All rights reserved. Based on Government Sponsored Research NAS7-03001 and NNN12AA01C.
+;
+;
+;Redistribution and use in source and binary forms, with or without
+;modification, are permitted provided that the following conditions
+;are met:
+;
+; *  Redistributions of source code must retain the above copyright
+;    notice, this list of conditions and the following disclaimer.
+;
+; *  Redistributions in binary form must reproduce the above copyright
+;    notice, this list of conditions and the following disclaimer in
+;    the documentation and/or other materials provided with the
+;    distribution.
+;
+; *  Neither the name of the California Institute of Technology
+;    (Caltech) nor the names of its contributors may be used to
+;    endorse or promote products derived from this software without
+;    specific prior written permission.
+;
+;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+;"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+;LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+;A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+;HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+;INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+;BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+;OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+;AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+;LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+;WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+;POSSIBILITY OF SUCH DAMAGE.
+;
+
+on_error,2
+compile_opt idl2
+
+IF NOT(keyword_set(in_struct)) OR NOT(keyword_set(outfile)) THEN BEGIN
+   print, 'Syntax - write_ipac_table, in_struct, outfile,'
+   print, '         [/short, /exact, format=]'
+   return
+ENDIF
+
+;;;; find the header tags
+
+tag_names_string = tag_names(in_struct)
+
+header_tag_idx = where(strmatch(tag_names_string, 'HEADER*') EQ 1, n_header)
+
+units_idx = where(strmatch(strupcase(tag_names_string), '*DATA_UNITS*'))
+null_idx = where(strmatch(strupcase(tag_names_string), '*NULL_VALUES*'))
+input_header_idx = where(strmatch(strupcase(tag_names_string), '*TABLE_HEADER*'))
+
+data_tag_idx = where(strmatch(tag_names_string, 'HEADER*') EQ 0, n_data)
+
+orig_names_idx = where(strmatch(strupcase(tag_names_string), '*COL_NAMES_ORIG') eq 1, n_orig)
+
+orig_types_idx = where(strmatch(strupcase(tag_names_string), '*COL_TYPES_ORIG') eq 1, n_t_orig)
+
+IF (n_orig gt 0) THEN BEGIN
+   tag_names_string[data_tag_idx] = in_struct.HEADER_COL_NAMES_ORIG
+ENDIF
+
+n_data_rows = n_elements(in_struct.(data_tag_idx[0]))
+
+;;;; ra and dec will be written as lowercase, so find them and change 
+
+ra_idx = where(strcmp(tag_names_string,'RA',/FOLD_CASE) EQ 1, nra)
+dec_idx = where(strcmp(tag_names_string,'DEC',/FOLD_CASE) EQ 1, ndec)
+
+radec_idx = intarr(n_elements(tag_names_string))
+
+IF nra GT 0 THEN BEGIN 
+   radec_idx[ra_idx] = 1
+   tag_names_string[ra_idx] = 'ra'
+ENDIF
+
+IF ndec GT 0 THEN BEGIN 
+   radec_idx[dec_idx] = 1
+   tag_names_string[dec_idx] = 'dec'
+ENDIF
+
+;;;;;; parse the format string to find delimeters
+
+IF keyword_set(short_format) THEN short_fmt = 1 ELSE short_fmt = 0 
+
+IF keyword_set(exact_format) THEN exact_fmt =1 ELSE exact_fmt = 0
+
+IF keyword_set(format) THEN BEGIN 
+   
+   len = strlen(format)
+   user_fmt = 1 
+   IF strmid(format,0, 1) EQ '(' THEN strput, format, ' ', 0 ELSE format=' '+format
+   len = strlen(format)
+   IF strmid(format,len-1, 1) EQ ')' THEN strput, format, ' ', len-1 ELSE format=format+' '
+   
+   len = strlen(format)
+   fmt_line_length = len
+   subline= format
+   
+   delim_idx = [0]
+   eol=0
+   WHILE NOT(eol) DO BEGIN 
+      char = strpos(subline,',')
+      IF char NE -1 THEN begin
+         strput, subline, 'x', char
+         delim_idx = [delim_idx, char]
+      ENDIF $
+      ELSE eol=1
+   ENDWHILE
+   IF n_elements(delim_idx) NE n_data THEN BEGIN 
+      print, 'ERROR:  Format statement has the wrong number of elements'
+      return
+   ENDIF
+   
+   delim_idx = [delim_idx, len-1]
+   
+ENDIF ELSE BEGIN 
+   user_fmt = 0 
+ENDELSE
+
+;;;;;  create format array
+
+fmt_arr = strarr(n_data)
+data_type_string = strarr(n_data)
+
+FOR i = 0, n_data-1 DO BEGIN 
+   IF NOT(user_fmt) THEN BEGIN
+      type = size(in_struct.(data_tag_idx[i]),/type)
+      CASE type OF 
+         3: fmt_arr[i]='i'
+         4: BEGIN 
+            IF exact_fmt THEN fmt_arr[i]='e16.9' $
+            ELSE IF short_fmt then fmt_arr[i]='' ELSE fmt_arr[i]='f' 
+            IF short_fmt AND radec_idx[data_tag_idx[i]] THEN fmt_arr[i]='f13.6'
+         END
+         
+         5: BEGIN 
+            IF exact_fmt THEN fmt_arr[i]='e24.17' $
+            ELSE IF short_fmt THEN fmt_arr[i]='' ELSE fmt_arr[i]='d' 
+            IF short_fmt AND radec_idx[data_tag_idx[i]] THEN fmt_arr[i]='f13.6'
+         END
+         
+         7: fmt_arr[i]='a'  
+         14: fmt_arr[i]='i'  
+         ELSE: stop
+      ENDCASE
+     
+   ENDIF $
+   ELSE BEGIN
+      between_delim = delim_idx[i+1]-delim_idx[i]-1
+      fmt_arr[i] = strmid(format,delim_idx[i]+1,between_delim)
+   ENDELSE   
+ENDFOR
+
+;;;; find width of each column
+
+max_len_arr = intarr(n_data)
+
+FOR i = 0, n_data-1 DO BEGIN
+   IF fmt_arr[i] NE '' THEN curr_fmt = '('+fmt_arr[i]+')' ELSE curr_fmt = ''
+   tmp_string = string(in_struct.(data_tag_idx[i]),format=curr_fmt)
+   data_len = max(strlen(tmp_string))
+   IF units_idx GE 0 THEN units_len = strlen(in_struct.(header_tag_idx[units_idx])[i]) ELSE units_len = 0
+   IF null_idx GE 0 THEN null_len = strlen(in_struct.(header_tag_idx[null_idx])[i]) ELSE null_len = 0
+   IF (n_t_orig gt 0) THEN BEGIN
+     type_len = strlen(in_struct.header_col_types_orig[i])
+   ENDIF ELSE BEGIN
+     sz = size(in_struct.(data_tag_idx[i]), /tname)
+     IF sz EQ 'STRING' THEN sz = 'CHAR'
+     IF sz EQ 'LONG' THEN sz = 'INT'
+     IF sz EQ 'LONG64' THEN sz = 'LONG'
+     type_len = strlen(sz)
+   ENDELSE
+   tag_len = strlen(tag_names_string[data_tag_idx[i]])
+   len_arr = [tag_len, type_len, units_len, null_len, data_len]
+   max_len_arr[i] = max(len_arr)      
+endfor   
+
+;;;;  construct the header rows
+
+name_row = '|'
+type_row = '|'
+units_row = '|'
+null_row = '|'
+
+FOR i = 0, n_data-1 DO BEGIN 
+   name_row = name_row + ' '+strn(tag_names_string[data_tag_idx[i]],len=max_len_arr[i])+' |'
+   IF (n_t_orig gt 0) THEN BEGIN
+     type_row = type_row + ' '+strn(in_struct.header_col_types_orig[i],len=max_len_arr[i])+' |'
+   ENDIF ELSE BEGIN
+     sz = size(in_struct.(data_tag_idx[i]), /tname)
+     IF sz EQ 'STRING' THEN sz = 'CHAR'
+     IF sz EQ 'LONG' THEN sz = 'INT'
+     IF sz EQ 'LONG64' THEN sz = 'LONG'
+     type_row = type_row + ' '+strn(sz,len=max_len_arr[i])+' |'
+   ENDELSE
+   IF units_idx GE 0 THEN $
+      units_row = units_row + ' '+strn(in_struct.(header_tag_idx[units_idx])[i],len=max_len_arr[i])+' |'
+   IF null_idx GE 0 THEN $
+      null_row = null_row + ' '+strn(in_struct.(header_tag_idx[null_idx])[i],len=max_len_arr[i])+' |'
+ENDFOR
+
+openw, lun, outfile, /get_lun
+
+;;;;  write out the data rows
+get_date, dte, /time
+printf, lun, '\created '+string(dte)
+
+IF input_header_idx GE 0 THEN BEGIN 
+   n_input_header = n_elements(in_struct.(header_tag_idx[input_header_idx]))
+   FOR i = 0, n_input_header - 1 DO printf, lun, in_struct.(header_tag_idx[input_header_idx])[i]
+endif
+
+printf, lun, name_row
+printf, lun, type_row
+IF units_idx GE 0 THEN printf, lun, units_row
+IF null_idx GE 0 THEN printf, lun, null_row
+
+FOR j = 0, n_data_rows-1 DO BEGIN 
+   out_string = ' '
+   FOR i = 0, n_data-1 DO BEGIN 
+   IF fmt_arr[i] NE '' THEN curr_fmt = '('+fmt_arr[i]+')' ELSE curr_fmt = ''      
+      data_string = strn(in_struct.(data_tag_idx[i])[j],format=curr_fmt,len=max_len_arr[i])
+      out_string = out_string + '  ' + data_string+' '
+   ENDFOR
+   printf, lun, out_string
+ENDFOR
+
+close, lun
+free_lun, lun
+
+end
diff --git a/Code/script_idl_mv/astrolib/writefits.pro b/Code/script_idl_mv/astrolib/writefits.pro
new file mode 100644
index 0000000000000000000000000000000000000000..145d56f5d4c0dea3037c7143c8273e7a99812f6a
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/writefits.pro
@@ -0,0 +1,291 @@
+pro writefits, filename, data, header, heap, Append = Append,  $
+       compress = compress, CheckSum = checksum, NaNValue = NaNvalue
+       
+;+
+; NAME:
+;       WRITEFITS
+; PURPOSE:
+;       Write IDL array and header variables to a disk FITS file.    
+;
+; EXPLANATION:
+;       A minimal FITS header is created if not supplied.
+;       WRITEFITS works for all types of FITS files except random groups
+;
+; CALLING SEQUENCE:
+;       WRITEFITS, filename, data [, header, /APPEND, /COMPRESS, /CHECKSUM] 
+;
+; INPUTS:
+;       FILENAME = String containing the name of the file to be written.
+;
+;       DATA = Image array to be written to FITS file.    If DATA is 
+;              undefined or a scalar, then only the FITS header (which
+;              must have NAXIS = 0) will be written to disk
+;
+; OPTIONAL INPUT:
+;       HEADER = String array containing the header for the FITS file.
+;                If variable HEADER is not given, the program will generate
+;                a minimal FITS header.
+;       HEAP -   A byte array giving the heap area following, e.g. a variable
+;                length binary table
+;
+; OPTIONAL INPUT KEYWORD:
+;       /APPEND - If this keyword is set then the supplied header and data
+;                array are assumed to be an extension and are appended onto
+;                the end of an existing FITS file.    If the file does not 
+;                exist, then WRITEFITS will create one with a minimal primary
+;                header (and /EXTEND keyword) and then append the supplied
+;                extension header and array.     Note that the primary
+;                header in an existing file must already have an EXTEND
+;                keyword to indicate the presence of an FITS extension.
+;       /COMPRESS - If this keyword is set, then the FITS file is written as
+;                a gzip compressed file.   An extension '.gz' is appended to
+;                to the file name if it does not already exist.   The /COMPRESS
+;                option is incompatible with the /APPEND option.
+;      /Checksum - If set, then the CHECKSUM keywords to monitor data integrity
+;                 will be included in the FITS header.    For more info, see
+;                 http://fits.gsfc.nasa.gov/registry/checksum.html
+;                 By default, checksum keywords will updated if they are already
+;                 in the FITS header.
+;       NaNvalue - Value in the data array which represents missing pixels.
+;		 This keyword should only used when missing pixels are not
+;		 represented by NaN values in the input array.
+; OUTPUTS:
+;       None
+;
+; RESTRICTIONS:
+;       (1) It recommended that BSCALE and BZERO not be used (or set equal
+;           to 1. and 0) except with integer data
+;       (2) WRITEFITS will remove any group parameters from the FITS header
+;       (3) As of Feb 2008, WRITEFITS no longer requires the primary header of a
+;           FITS file with extension contain the EXTEND keyword, consistent with 
+;           Section 4.4.2.1 of the FITS 3.0 standard.    A warning is still 
+;           given.  See http://fits.gsfc.nasa.gov/fits_standard.html
+;
+; EXAMPLE:
+;       Write a randomn 50 x 50 array as a FITS file creating a minimal header.
+;
+;       IDL> im = randomn(seed, 50, 50)        ;Create array
+;       IDL> writefits, 'test', im             ;Write to a FITS file "test"
+;
+; PROCEDURES USED:
+;       CHECK_FITS, FITS_ADD_CHECKSUM, MKHDR, MRD_HREAD, SXDELPAR, SXADDPAR, 
+;       SXPAR()
+;
+; MODIFICATION HISTORY:
+;       WRITTEN, Jim Wofford, January, 29 1989
+;       Added call to IS_IEEE_BIG()  W. Landsman  Apr 96
+;       Make sure SIMPLE is written in first line of header  W. Landsman Jun 97
+;       Use SYSTIME() instead of !STIME    W. Landsman  July 97
+;       Create a default image extension header if needed W. Landsman June 98
+;       Write unsigned data types W. Landsman       December 1999
+;       Update for IDL V5.3, add /COMPRESS keyword W. Landsman  February 2000
+;       Correct BZERO value for unsigned data  W. Landsman   July 2000
+;       Eliminate duplication of input array if possible W. Landsman April 2001
+;       Use FILE_SEARCH for V5.5 or later     W. Landsman    April 2002
+;       Create the file if not already present and /APPEND is set
+;                                             W. Landsman    September 2002
+;       Proper call to MRD_HREAD if /APPEND is set  W. Landsman December 2002 
+;       Added /CHECKSUM keyword              W. Landsman     December 2002
+;	Restored NANvalue keyword, William Thompson,	     October 2003
+;       Write BZERO in beginning of header for unsigned integers WL April 2004
+;       Added ability to write heap array       WL             October 2004
+;       Correct checksum if writing heap array   WL           November 2004
+;       Assume since V5.5, no VMS support, use file_search() WL   September 2006
+;       Set nbytes variable to LONG64 for very large files WL  May 2007
+;       Update CHECKSUM keywords if already present  WL   Oct 2007
+;       EXTEND keyword no longer required in FITS files with extensions WL Feb 2008
+;       Bug fix when filename ends with '.gz' and COMPRESS is used,
+;            the output file must be compressed          S. Koposov June 2008
+;       Introduce V6.0 notation                W.L. Nov. 2010 
+;       Set /APPEND if XTENSION specifies a table   W.L.  July 2012
+;       Bug fix when /CHECKSUM used with unsigned data  W.L. June 2013
+;       June 2013 bug fix introduced problem when NAXIS=0  W.L. July 2013
+;-
+  On_error, 2
+  compile_opt idl2  
+
+  if N_params() LT 2 then begin 
+       print,'Syntax - WRITEFITS, filename, data,[ header, /APPEND, /CHECKSUM]'
+       return
+  endif
+
+; Get information about data
+
+  siz = size( data )      
+  naxis = siz[0]                    ;Number of dimensions
+  if naxis GT 0 then nax = siz[ 1:naxis ]              ;Vector of dimensions
+  lim = siz[ naxis+2 ]              ;Total number of data points
+  type = siz[naxis + 1]             ;Data type
+
+;Create a primary or image extension header if not supplied by the user
+
+        if N_elements(header) LT 2 then begin 
+                if keyword_set(append) then mkhdr, header, data, /IMAGE  $
+                                       else mkhdr, header, data, /EXTEND
+        endif else if naxis GT 0 then $         
+              check_FITS, data, header, /UPDATE, /FITS
+
+; Remove any STSDAS/random group keywords from the primary header
+
+  hdr = header
+  
+;If header indicates a table extension then set the append keyword  
+  if ~keyword_set( APPEND) && ( strmid(hdr[0],0,8) EQ 'XTENSION' ) then begin
+	 xten = strtrim(sxpar(hdr,'XTENSION'),2)
+	 if (xten EQ 'TABLE') || (xten Eq 'BINTABLE') || (xten Eq 'A3DTABLE') $
+	     then begin 
+	     append = 1
+	     message,'Writing FITS table extension',/INF
+	 endif    
+   endif	     
+
+  if ~keyword_set( APPEND) then begin 
+         simple = 'SIMPLE  =                    T / Written by IDL:  ' $
+                        + systime()  
+         hdr[0] =  simple + string( replicate(32b,80-strlen(simple) ) )
+         sxdelpar, hdr, [ 'GCOUNT', 'GROUPS', 'PCOUNT', 'PSIZE' ]
+  endif
+  
+; If necessary,convert unsigned to signed.    Do not destroy the original data
+
+  unsigned = 0
+  if naxis NE 0 then begin
+              
+        unsigned = (type EQ 12) || (type EQ 13)
+        if  unsigned then begin
+             if type EQ 12 then begin
+                     sxaddpar,hdr,'BZERO',32768,'Data is Unsigned Integer', $
+                              before = 'DATE'
+                     newdata = fix(data - 32768)
+             endif else if type EQ 13 then begin 
+                    sxaddpar,hdr,'BZERO',2147483648,'Data is Unsigned Long', $
+                              before = 'DATE'
+                    newdata = long(data - 2147483648)
+             endif
+         endif 
+
+; For floating or double precision test for NaN values to write
+
+  NaNtest = keyword_set(NaNvalue) && ( (type EQ 4) || (type EQ 5) )
+  if NaNtest then begin
+     NaNpts = where( data EQ NaNvalue, N_NaN)
+     if (N_NaN GT 0) then begin
+         if type EQ 4 then data[NaNpts]  = !Values.F_NaN	$
+     else if type EQ 8 then data[NaNpts] = !Values.D_NaN
+     endif
+  endif 
+  endif
+
+; Open file and write header information
+
+        if keyword_set( APPEND) then begin
+            if (strmid( hdr[0],0,8 ) NE 'XTENSION') then begin
+                   message, $
+            'ERROR - "XTENSION" must be first keyword in header extension',/CON
+                  return
+            endif
+            if ~file_test(filename)  then  begin       ;Create default primary header
+                 mkhdr,h0,0b,/exten
+                 writefits,filename,0b,h0, checksum = checksum
+                 openu, unit, filename, /GET_LUN, /swap_if_little_endian
+             endif else begin
+            openu, unit, filename, /GET_LUN, /swap_if_little_endian
+            mrd_hread, unit, hprimary
+            extend = where( strcmp(hprimary,'EXTEND  ',8), Nextend)
+            if Nextend EQ 0 then $
+               message,'WARNING - EXTEND keyword not found in primary FITS header',/CON
+            endelse
+                   
+            file = fstat(unit)
+            nbytes  = file.size
+            point_lun, unit, nbytes
+            npad = nbytes mod 2880
+            if npad NE 0 then writeu, unit, replicate(32b, 2880 - npad)
+
+    endif else begin
+
+        ext = ''
+        if keyword_set(COMPRESS) then begin 
+            if strlowcase(strmid(filename,2,3,/reverse)) NE '.gz' $
+               then ext = '.gz' 
+        endif else compress = 0
+
+
+       openw, unit, filename + ext, /GET_LUN, /swap_if_little_endian, $
+                             compress = compress
+
+    endelse
+
+; Determine if an END line occurs, and add one if necessary
+
+       endline = where( strcmp(hdr, 'END     ', 8), Nend)
+     if Nend EQ 0 then begin
+
+ message,'WARNING - An END statement has been appended to the FITS header',/INF
+     hdr = [ hdr, 'END' + string( replicate(32b,77) ) ]
+     endline = N_elements(hdr) - 1 
+
+   endif
+
+; Add any CHECKSUM keywords if desired or already present
+   
+    do_Checksum = keyword_set(checksum)
+    if ~do_checksum then test = sxpar(hdr,'CHECKSUM',count=do_checksum)
+  
+     if do_checksum then begin 
+               if unsigned then begin 
+	       if N_elements(heap) GT 0 then $
+	         FITS_ADD_CheckSum, hdr, [newdata,heap] else $
+		 FITS_Add_CheckSum, hdr, newdata
+	       endif else begin 	 
+               if N_elements(heap) GT 0 then $
+	         FITS_ADD_CHECKSUM, hdr, [data,heap] else $
+                 FITS_ADD_CHECKSUM, hdr, data
+	       endelse	 
+               endline = where( strcmp(hdr,'END     ',8), Nend)
+       endif
+       nmax = endline[0] + 1
+
+; Convert to byte and force into 80 character lines
+
+       bhdr = replicate(32b, 80l*nmax)
+       for n = 0l, endline[0] do bhdr[80*n] = byte( hdr[n] )
+       npad = 80l*nmax mod 2880
+       writeu, unit, bhdr
+       if npad GT 0 then writeu, unit,  replicate(32b, 2880 - npad)
+
+; Write data
+       if naxis EQ 0 then goto, DONE
+        bitpix = sxpar( hdr, 'BITPIX' )
+        nbytes = long64( N_elements( data)) * (abs(bitpix) / 8 )
+        npad = nbytes mod 2880
+
+        if unsigned then writeu, unit, newdata $
+                    else writeu, unit, data 
+
+; Write optional heap area
+        if N_elements(heap) GT 0 then begin
+              theap = sxpar(hdr,'THEAP', Count=N_Theap)
+              if N_Theap GT 0 then begin
+                  offset = theap - nbytes
+                  if offset GT 0 then begin
+                      writeu, unit, bytarr(offset)
+                      npad = (npad + offset) mod 2880
+                  endif
+                  writeu, unit, heap
+                  npad = (npad + N_elements(heap)) mod 2880
+              endif
+         endif
+
+; ASCII Tables padded with blanks (32b) otherwise pad with zeros
+        if keyword_set( APPEND) then begin
+             exten = sxpar( header, 'XTENSION')
+             padnum =  exten EQ 'TABLE   ' ? 32b : 0b
+        endif else padnum = 0b
+         
+        if npad GT 0 then writeu, unit, replicate( padnum, 2880 - npad)
+DONE:
+        free_lun, unit  
+
+  return
+  end
diff --git a/Code/script_idl_mv/astrolib/xdispstr.pro b/Code/script_idl_mv/astrolib/xdispstr.pro
new file mode 100644
index 0000000000000000000000000000000000000000..32618e0e502e22abb6841427406a01d6f0507965
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/xdispstr.pro
@@ -0,0 +1,170 @@
+;+
+;  NAME:      
+;     XDISPSTR
+;
+;  PURPOSE:   
+;     Display a string array in a text widget with a simple search capability.
+;
+; EXPLANATION:
+;     Similar to the IDL XDISPLAYFILE procedure but includes a search capbility.
+; CALLING SEQUENCE:    
+;                 
+;     xdispstr, array, [/BLOCK, WIDTH= , HEIGHT=, TITLE=, GROUP_LEADER=, FONT=
+;                       TOP_LINE=, POS= ]
+;
+; INPUT PARAMETER:
+;
+;     array  - String array (.e.g. FITS header) to be displayed
+;
+;  OPTIONAL INPUT KEYWORD PARAMETERS:
+;
+;    block -  Set to 1 to make widget blocking.  Default = block=0
+;
+;    font  -     Display font for text.
+;          
+;    width, height  - Scalars giving number of characters per line, number
+;                           of lines.  Default = 80x48
+;
+;    group_leader  -    Group leader for top level base.
+;
+;    title  - Scalar Title for outermost base widget.
+;
+;    pos - 2 element array containing the normalized X and Y position to
+;            display the widget on the screen.    [0,0] is the upper left
+;            hand corner.
+;
+;    top_line - first line in the string array to display (default is 0)
+;
+; PROCEDURES USED:
+;     CGCENTERTLB
+;
+;  MODIFICATION HISTORY:
+;     Written by R. S. Hill, RITSS, 17 Nov 2000
+;     Use cumulative keyword to TOTAL   W. Landsman   May 2006
+;     Made resizeable, default size now 48 lines  W. Landsman   July 2013
+;     Added POS keyword W. Landsman  Sep 2013
+;-
+
+
+PRO XDISPSTR_EVENT, Event
+
+widget_control, event.top, get_uvalue=info
+
+search = 0b
+destroy = 0b
+if tag_names(event,/STRUCTURE_NAME) EQ 'WIDGET_BASE' then begin
+    widget_control,(*info).array_text, $
+          scr_ysize = event.Y,scr_xsize=event.X
+endif else begin	  
+CASE event.id OF
+(*info).done_button:  destroy=1b
+(*info).search_button:  search=1b
+(*info).search_text:  search=1b
+ELSE:
+ENDCASE
+endelse
+
+IF search THEN BEGIN
+    widget_control, (*info).search_text, get_value=seastr
+    seastr = seastr[0]
+    sp = strpos(strupcase(*(*info).arrayptr), strupcase(seastr))
+    w = where(sp GE 0, c)
+    IF c GT 0 THEN BEGIN
+        tptr = sp[w] + (*(*info).clenptr)[w]
+        tlen = strlen(seastr)
+        ts = widget_info((*info).array_text, /text_select)
+        this_line = max(where(ts[0] GE *(*info).clenptr, c3))
+        line_frag = $
+            strmid(strupcase((*(*info).arrayptr)[this_line]), $
+                   ts[0] - (*(*info).clenptr)[this_line] + tlen)
+        again = strpos(line_frag, strupcase(seastr))
+        IF again GE 0 THEN BEGIN
+            newtptr = again + tlen + ts[0]
+        ENDIF ELSE BEGIN
+            next = min(where(tptr GT ts[0], c2))
+            IF c2 GT 0 THEN newtptr = tptr[next] ELSE newtptr = tptr[0]
+        ENDELSE
+        widget_control, (*info).array_text, set_text_select=[newtptr,tlen]
+        new_line = max(where(newtptr GE *(*info).clenptr))
+        middle = (*info).height/2
+        nl = n_elements(*(*info).arrayptr)
+        tl = ((new_line-middle)>0)<(nl-(*info).height)
+        widget_control, (*info).array_text, set_text_top_line=tl
+        widget_control, (*info).msg_text, set_value='Line '+strn(new_line)
+    ENDIF ELSE BEGIN
+        widget_control, (*info).msg_text, set_value='String not found'
+    ENDELSE
+ENDIF
+
+IF destroy THEN widget_control, event.top, /destroy
+
+RETURN
+END
+
+PRO XDISPSTR_CLEANUP, Id
+widget_control, id, get_uvalue=info
+IF ptr_valid(info) THEN BEGIN
+    IF ptr_valid((*info).clenptr) THEN ptr_free, (*info).clenptr
+    IF ptr_valid((*info).arrayptr) THEN ptr_free, (*info).arrayptr
+    ptr_free, info
+ENDIF
+RETURN
+END
+
+
+PRO XDISPSTR, Array, BLOCK=block, WIDTH=width, HEIGHT=height, TITLE=title, $
+                     GROUP_LEADER=group_leader, FONT=font,top_line=top_line, $
+		     POS = pos
+
+on_error, 2
+
+IF N_params() LT 1 THEN BEGIN
+    print, 'CALLING SEQUENCE:  XDISPSTR, Array'
+    print, 'KEYWORD PARAMETERS:  BLOCK, WIDTH, HEIGHT, TITLE, ' $
+            + 'GROUP_LEADER, FONT'
+    RETURN
+ENDIF
+
+IF n_elements(block) LT 1 THEN block=0
+IF n_elements(width) LT 1 THEN width=80
+IF n_elements(height) LT 1 THEN height=48 < N_elements(array)
+IF n_elements(title) LT 1 THEN title='XDISPSTR'
+
+tlb = widget_base(title=title,col=1,group_leader=group_leader,/TLB_Size_Events)
+
+controls = widget_base(tlb, frame=1, row=1)
+done_button = widget_button(controls, value='Done', /no_release)
+search_button = widget_button(controls, value='Search:', /no_release)
+search_text = widget_text(controls, xsize=30, ysize=1, /editable, font=font)
+msg_label = widget_label(controls, value='Message: ')
+msg_text = widget_text(controls, xsize=20, ysize=1, font=font)
+
+array_text = widget_text(tlb, value=array, $
+                         xsize=width, ysize=height, /scroll, edit=0, font=font)
+
+if N_elements(top_line) EQ 0 then top_line = 0
+widget_control, array_text, set_text_top_line=top_line
+widget_control, array_text, set_text_select=[0,0]
+
+widget_control, tlb, /realize
+    
+linelen1 = strlen(array) + 1
+cumul_len = [0, total(linelen1,/cumulative,/integer)]
+geom = widget_info(tlb,/geometry)
+info = ptr_new({done_button:done_button, $
+                search_button:search_button, search_text:search_text, $
+                array_text:array_text, arrayptr:ptr_new(array), $
+                clenptr:ptr_new(cumul_len,/no_copy), $
+                msg_text:msg_text, width:width, height:height})
+
+
+widget_control, tlb, set_uvalue=info
+widget_control, tlb,tlb_get_size = basesize
+xmanager, 'xdispstr', tlb, cleanup='xdispstr_cleanup', $
+          event_handler='xdispstr_event', no_block=1b-block, $
+          group_leader=group_leader
+if N_elements(pos) EQ 2 then cgcentertlb,tlb,pos[0],pos[1]	  
+
+RETURN
+END
+
diff --git a/Code/script_idl_mv/astrolib/xmedsky.pro b/Code/script_idl_mv/astrolib/xmedsky.pro
new file mode 100644
index 0000000000000000000000000000000000000000..217697260d85b68e712887a133c89583127fcd54
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/xmedsky.pro
@@ -0,0 +1,77 @@
+PRO XMEDSKY, Image, Bkg, CLIP=clip, Nsig = nsig
+;+
+; NAME:
+;       XMEDSKY
+;
+; PURPOSE:
+;       Subtract sky from an image as a 1-D function of X
+; EXPLANATION:
+;       This procedure is designed to remove the sky from slitless spectra.
+;       The sky is assumed to vary with wavelength (along a row) but not with
+;       position (along a column).    The sky is computed as the 
+;       column-by-column median of pixels within 3 sigma of the image global 
+;       median.   This procedure is called by the cosmic ray rejection routine
+;       CR_REJECT
+;
+; CALLING SEQUENCE:
+;       XMEDSKY, Image, Bkg, [ CLIP=[x0, x1, y0, y1], NSIG= ]
+;
+; INPUTS:
+;       Image:  Input image for which sky vector is to be computed.
+;       
+; INPUT KEYWORD PARAMETERS:
+;       CLIP:   [x0, x1, y0, y1]: region of image to be used for all
+;               statistical computations.    Default is to use the entire
+;               image.   For STIS 1024 x 512 slitless spectra, the suggested
+;               value is CLIP = [32,1023,12,499]
+;       NSIG:   Positive scalar giving the number of sigma a pixel must be above
+;               the global median to be reject.   Default is 3 sigma.
+; OUTPUT PARAMETER:
+;       Bkg:    Vector of sky values.
+;;
+; MODIFICATION HISTORY:
+;       Written by:     R. S. Hill, Hughes STX, 20 Oct. 1997
+;       Converted to V5.0, use STDDEV()   W. Landsman   June 1998
+;       Check for valid WHERE, added NSIG keyword  W. Landsman   December 2000 
+;       Assume since V5.1 so always use STDDEV  W. Landsman Feb 2004 
+;       Assume since V5.6 use DIMEN keyword to MEDIAN W. Landsman Jan 2008  
+;-
+ compile_opt idl2
+ if N_params() LT 2 then begin
+        print,'Syntax - Xmedsky, Image, Bkg, [CLIP = ]'
+        return
+ endif
+ if N_elements(nsig) EQ 0 then nsig=3
+ sz = size(image)
+ nbkg = sz[1]
+ if N_elements(clip) LT 1 then clip = [0,sz[1]-1,0,sz[2]-1 ]
+
+  bkg = median( image, dimen=2)
+
+ tmpimg=image
+ FOR i=0,sz[2]-1 DO tmpimg[0,i] = image[*,i] - bkg
+
+; Now get the global median and standard deviation
+
+ totmed = median(tmpimg[clip[0]:clip[1],clip[2]:clip[3]])
+ totsdv = stddev(tmpimg[clip[0]:clip[1],clip[2]:clip[3]]) 
+
+; Create a mask array showing where pixels are more than 3 (or Nsig) sigma
+; from the global median.
+
+ mask = byte(0*image+1)
+ watt = where(abs(temporary(tmpimg)-totmed) GT (nsig*totsdv), cwatt)
+ if cwatt GT 0 then mask[watt] = 0
+
+; Now recompute column by column median using only unmasked pixels within the
+; clipped region.
+
+ FOR i=0,nbkg-1 DO BEGIN
+   wmi = where(mask[i,clip[2]:clip[3]], cwmi)
+   if cwmi GT 0 THEN $
+       bkg[i]=median( image[i,clip[2] + wmi ] )
+ ENDFOR
+
+ return
+ END
+
diff --git a/Code/script_idl_mv/astrolib/xy2ad.pro b/Code/script_idl_mv/astrolib/xy2ad.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8873edefe657c4aa941859467cc615f09b86255f
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/xy2ad.pro
@@ -0,0 +1,199 @@
+pro xy2ad, x, y, astr, a, d
+;+
+; NAME:
+;     XY2AD
+;
+; PURPOSE:
+;     Compute R.A. and Dec from X and Y and a FITS astrometry structure
+; EXPLANATION:
+;     The astrometry structure must first be extracted by EXTAST from a FITS
+;     header.   The offset from the reference pixel is computed and the CD 
+;     matrix is applied.     If distortion is present then this is corrected.
+;     If a WCS projection (Calabretta & Greisen 2002, A&A, 395, 1077) is 
+;     present, then the procedure WCSXY2SPH is used to compute astronomical
+;     coordinates.    Angles are returned in  degrees.
+;   
+;     XY2AD is meant to be used internal to other procedures.  
+;     For interactive purposes use XYAD.
+;
+; CALLING SEQUENCE:
+;     XY2AD, x, y, astr, a, d   
+;
+; INPUTS:
+;     X     - row position in pixels, scalar or vector
+;     Y     - column position in pixels, scalar or vector
+;           X and Y should be in the standard IDL convention (first pixel is
+;           0), and not the FITS convention (first pixel is 1). 
+;     ASTR - astrometry structure, output from EXTAST procedure containing:
+;        .CD   -  2 x 2 array containing the astrometry parameters CD1_1 CD1_2
+;               in DEGREES/PIXEL                                   CD2_1 CD2_2
+;        .CDELT - 2 element vector giving physical increment at reference pixel
+;        .CRPIX - 2 element vector giving X and Y coordinates of reference pixel
+;               (def = NAXIS/2)
+;        .CRVAL - 2 element vector giving R.A. and DEC of reference pixel 
+;               in DEGREES
+;        .CTYPE - 2 element vector giving projection types 
+;        .LONGPOLE - scalar longitude of north pole
+;        .LATPOLE - scalar giving native latitude of the celestial pole
+;        .PV2 - Vector of projection parameter associated with latitude axis
+;             PV2 will have up to 21 elements for the ZPN projection, up to 3
+;             for the SIN projection and no more than 2 for any other
+;             projection
+;
+;     Fields added for version 2:
+;      .PV1 - Vector of projection parameters associated with longitude axis
+;      .AXES  - 2 element integer vector giving the FITS-convention axis 
+;               numbers associated with astrometry, in ascending order. 
+;               Default [1,2].
+;      .REVERSE - byte, true if first astrometry axis is Dec/latitude
+;      .COORDSYS - 1 or 2 character code giving coordinate system, including
+;                 'C' = RA/Dec, 'G' = Galactic, 'E' = Ecliptic, 'X' = unknown.
+;      .RADECSYS - String giving RA/Dec system e.g. 'FK4', 'ICRS' etc.
+;      .EQUINOX  - Double giving the epoch of the mean equator and equinox
+;      .DATEOBS  - Text string giving (start) date/time of observations
+;      .MJDOBS   - Modified julian date of start of observations.
+;      .X0Y0     - Implied offset in intermediate world coordinates if user has
+;                  specified a non-standard fiducial point via PV1 and also
+;                  has set PV1_0a =/ 0 to indicate that the offset should be
+;                  applied in order to place CRVAL at the IWC origin.
+;                  Should be *added* to the IWC derived from application of
+;                  CRPIX, CDELT, CD to the pixel coordinates.
+;
+;      .DISTORT - Optional substructure specifying distortion parameters
+;                  
+;
+; OUTPUT:
+;     A - R.A. in DEGREES, same number of elements as X and Y
+;     D - Dec. in DEGREES, same number of elements as X and Y
+;
+; RESTRICTIONS:
+;       Note that all angles are in degrees, including CD and CRVAL
+;       Also note that the CRPIX keyword assumes an FORTRAN type
+;       array beginning at (1,1), while X and Y give the IDL position
+;       beginning at (0,0).   No parameter checking is performed.
+;
+; NOTES:
+;      XY2AD tests for presence of WCS coordinates by the presence of a dash 
+;      in the 5th character position in the value of CTYPE (e.g 'DEC--SIN').       
+; PROCEDURES USED:
+;       TAG_EXIST(), WCSXY2SPH, SIP_EVAL(), TPV_EVAL()
+; REVISION HISTORY:
+;       Written by R. Cornett, SASC Tech., 4/7/86
+;       Converted to IDL by B. Boothman, SASC Tech., 4/21/86
+;       Perform CD  multiplication in degrees  W. Landsman   Dec 1994
+;       Understand reversed X,Y (X-Dec, Y-RA) axes,   W. Landsman  October 1998
+;       Consistent conversion between CROTA and CD matrix W. Landsman Oct. 2000
+;       No special case for tangent projection W. Landsman June 2003
+;       Work for non-WCS coordinate transformations W. Landsman Oct 2004
+;       Use CRVAL reference point for non-WCS transformation  W.L. March 2007
+;       Use post V6.0 notation   W.L. July 2009
+;       Some optimisation for large input arrays & use of version 2 astr
+;       structure, J. P. Leahy July 2013
+;       Evalue TPV distortion (SCAMP) if present W. Landsman   Jan 2014
+;       Support IRAF TNX porjection  M. Sullivan U. of Southamptom  Mar 2014
+;       No longer check that CDELT[0] NE 1  W. Landsman Apr 2015
+;- 
+ common Broyden_coeff, pv1, pv2       ;Needed for TPV transformation
+ compile_opt idl2
+
+ if N_params() LT 4 then begin
+        print,'Syntax -- XY2AD, x, y, astr, a, d'
+        return
+ endif
+ 
+ Catch, theError
+ IF theError NE 0 then begin
+     Catch,/Cancel
+     void = cgErrorMsg(/quiet)
+     RETURN
+     ENDIF
+ 
+ cd = astr.cd
+ crpix = astr.crpix
+ cdelt = astr.cdelt 
+         
+  cd[0,0] *= cdelt[0] & cd[0,1] *= cdelt[0]
+  cd[1,1] *= cdelt[1] & cd[1,0] *= cdelt[1]
+
+ xdif = x - (crpix[0]-1)            
+ ydif = y - (crpix[1]-1)
+ no_PV1 =0     ;Set if PV1 used by TGV distortion
+ 
+ if tag_exist(astr,'DISTORT') && astr.distort.name EQ 'SIP' then begin
+           distort  = astr.distort
+           a = distort.a
+           b = distort.b
+           na = ((size(a,/dimen))[0])
+           xdif1 = xdif
+           ydif1 = ydif
+           
+           for i=0,na-1 do begin
+               for j=0,na-1 do begin
+                  if a[i,j] NE 0.0 then xdif1 +=  xdif^i*ydif^j*a[i,j]            
+                  if b[i,j] NE 0.0 then ydif1 +=  xdif^i*ydif^j*b[i,j]
+           endfor
+           endfor
+
+           xdif = TEMPORARY(xdif1)
+           ydif = TEMPORARY(ydif1)
+           
+  ENDIF 
+
+ astr2 = TAG_EXIST(astr,'AXES') ; version 2 astrometry structure
+ 
+ xsi = cd[0,0]*xdif + cd[0,1]*ydif   ;Can't use matrix notation, in
+                                     ;case X and Y are vectors
+ eta = cd[1,0]*TEMPORARY(xdif) + cd[1,1]*TEMPORARY(ydif)   
+
+ if tag_exist(astr,'DISTORT') && astr.distort.name EQ 'TPV' then begin
+           pv1 = astr.pv1
+	   pv2 = astr.pv2
+	   result = tpv_eval( [[xsi],[eta]])
+	   xsi = reform( result[*,0] )
+           eta = reform( result[*,1] )
+           no_PV1 = 1
+           ctype = strmid(astr.ctype,0,4) + '-TAN'
+        endif	   
+
+        if tag_exist(astr,'DISTORT') && astr.distort.name EQ 'TNX' then begin
+        pv1=astr.distort.lngcor
+        pv2=astr.distort.latcor
+        result = tnx_eval( [[xsi],[eta]])
+        xsi = reform( result[*,0] )
+        eta = reform( result[*,1] )
+        no_PV1 = 1
+        ctype = strmid(astr.ctype,0,4) + '-TAN'
+     endif
+
+ if N_elements(ctype) Eq 0 then ctype = astr.ctype
+ crval = astr.crval
+ IF astr2 THEN reverse = astr.reverse ELSE BEGIN
+     coord = strmid(ctype,0,4)
+     reverse = ((coord[0] EQ 'DEC-') && (coord[1] EQ 'RA--')) || $
+               ((coord[0] EQ 'GLAT') && (coord[1] EQ 'GLON')) || $
+               ((coord[0] EQ 'ELAT') && (coord[1] EQ 'ELON'))
+ ENDELSE
+ if reverse then begin
+     crval = rotate(crval,2)
+     temp = TEMPORARY(xsi) & xsi = TEMPORARY(eta) & eta = TEMPORARY(temp)
+ endif
+
+ if strmid(ctype[0],4,1) EQ '-' then begin
+     if no_PV1 then begin       ;Set default values for tangent projection
+           pv1 = [0.0d,0,90.0d,180.0d,90.0d]  & pv2 = [0.0d,0.0d]	   
+     endif else begin 
+            pv1 = astr.pv1
+	    pv2 = astr.pv2
+     endelse 	    
+     if astr2 THEN $
+         WCSXY2SPH, xsi, eta, a, d, CTYPE = ctype[0:1], PV1 = pv1, $
+              PV2 = astr.PV2, CRVAL = crval, CRXY = astr.x0y0 $
+     ELSE $ 
+         WCSXY2SPH, xsi, eta, a, d, CTYPE = ctype[0:1], PV2 = pv2, $
+              LONGPOLE = astr.longpole, CRVAL = crval, LATPOLE = astr.latpole
+ endif else begin
+         a = crval[0] + TEMPORARY(xsi) & d = crval[1] + TEMPORARY(eta)	
+ endelse
+
+ return
+ end
diff --git a/Code/script_idl_mv/astrolib/xyad.pro b/Code/script_idl_mv/astrolib/xyad.pro
new file mode 100644
index 0000000000000000000000000000000000000000..07ac623c8d27603870a7a34eb926929f3ddad919
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/xyad.pro
@@ -0,0 +1,205 @@
+pro xyad, hdr, x, y, a, d, PRINT = print, GALACTIC = galactic, ALT = alt, $
+         CELESTIAL = celestial, ECLIPTIC = ecliptic, PRECISION = precision
+;+
+; NAME:
+;       XYAD
+; PURPOSE:
+;       Use a FITS header to convert pixel (X,Y) to world coordinates
+; EXPLANATION: 
+;       Use astrometry in a FITS image header to compute world
+;       coordinates in decimal degrees from X and Y.    
+;
+;       If spherical coordinates (Calabretta & Greisen 2002, A&A, 395, 1077) are 
+;       not present, then XYAD will still perform the transformation specified
+;       by the CD, CRVAL, and CRPIX keywords.
+; CALLING SEQUENCE:
+;       XYAD, HDR               ;Prompt for X and Y positions
+;       XYAD, HDR, X, Y, A, D, [ /PRINT, /Galactic, /Celestial, /Ecliptic, 
+;                                ALT =, PRECISION=]
+; INPUTS:
+;       HDR - FITS Image header containing astrometry info
+;
+; OPTIONAL INPUTS:
+;       X     - row position in pixels, scalar or vector
+;       Y     - column position in pixels, scalar or vector
+;
+;       X and Y should be in IDL convention, (first pixel is (0,0) where
+;       the integral value corresponds to the center of the pixel.)
+;
+; OPTIONAL OUTPUT:
+;       A - Output longitude in decimal DEGREES (for spherical coordinates), 
+;               same number of elements as X and Y.    For celestial 
+;               coordinates, this is the Right ascension.
+;       D - Output latitude in decimal DEGREES.   For celestial coordinates,
+;               this is the declination.
+; OPTIONAL KEYWORD INPUT:
+;       ALT -  single character 'A' through 'Z' or ' ' specifying an alternate 
+;             astrometry system present in the FITS header.    The default is
+;             to use the primary astrometry or ALT = ' '.   If /ALT is set, 
+;             then this is equivalent to ALT = 'A'.   See Section 3.3 of 
+;             Greisen & Calabretta (2002, A&A, 395, 1061) for information about
+;             alternate astrometry keywords.
+;       PRECISION - Integer scalar (0-4) specifying the number of digits 
+;             displayed after the decimal of declination.   The RA is
+;             automatically one digit more.   See ADSTRING() for more info.
+;             Default value is 1, and the keyword is ignored if results are not 
+;             displayed at the terminal 
+;       /PRINT - If this keyword is set and non-zero, then results are displayed
+;               at the terminal.in both decimal and sexagesimal notation.
+;
+;       The default for XYAD is to return the coordinate system present in
+;       in the FITS header.    However, the following mutually exclusive 
+;       keywords can be used to convert to a particular coordinate system:
+;
+;       /CELESTIAL - Output is Right Ascension and declination
+;       /ECLIPTIC - Output is Ecliptic longitude and latitude
+;       /GALACTIC - Output is Galactic longitude and latitude
+;                   Celestial & Ecliptic coords depend on the reference
+;                   equinox, set to either B1950 (=FK4) or J2000 (=FK5,ICRS)
+;                   according to the header or standard FITS WCS defaults.
+;                   Note that astrometry at the sub-arcsec level requires
+;                   fine distinctions that are not handled here.
+; 
+; OPERATIONAL NOTES:
+;       If less than 5 parameters are supplied, or if the /PRINT keyword is
+;       set, then the computed astronomical coordinates are displayed at the 
+;       terminal.
+;
+;       If this procedure is to be used repeatedly with the same header,
+;       then it would be faster to use XY2AD.
+;
+; EXAMPLE:
+;       A FITS header, hdr, contains astrometric information in celestial
+;       coordinates.   Find the RA and Dec corresponding to position X=23.3
+;        Y = 100.2 on an image
+;        IDL> xyad, hdr, 23.3, 100.2      ;Displays results at the terminal
+;       To display the results in Galactic coordinates
+;        IDL> xyad, hdr, 23.3, 100.2, /GALACTIC
+;
+; PROCEDURES CALLED
+;       ADSTRING(), EULER, EXTAST, GET_EQUINOX(), GSSSXYAD, REPCHR(), XY2AD
+;
+; REVISION HISTORY:
+;       W. Landsman                 STX          Jan, 1988
+;       Use astrometry structure  W. Landsman    Jan, 1994
+;       Recognize GSSS header  W. Landsman       June, 1994
+;       Changed ADSTRING output format   W. Landsman    September 1995
+;       Use vector call to ADSTRING() W. Landsman February 2000
+;       Added ALT input keyword  W. Landsman June 2003
+;       Add precision keyword  W. Landsman February 2004
+;       Fix display if 'RA','DEC' reversed in CTYPE  W. Landsman Feb. 2004
+;       Handle display of NaN values W. Landsman May 2004
+;       Work for non-spherical coordinate transformations W. Landsman Oct 2004
+;       Fix output display units if ALT keyword used W. Landsman March 2005
+;       More informative error message if no astrometry present W.L Nov 2007
+;       Fix display when no equinox in header W.L. Dec 2007
+;       Fix header display for noncelestial coords W.L. Jan 2008
+;       Check for non-standard projections, set FK4 flag. J. P. Leahy Jul 2013
+;-
+ compile_opt idl2
+ On_error,2
+
+ npar = N_params()
+ if ( npar EQ 0 ) then begin
+        print,'Syntax -  XYAD, hdr, [x, y, a, d, /PRINT, Alt=, Precision=, '
+        print,'                      /Galactic, /Celestial, /Ecliptic ]'
+        print,'HDR - FITS header (string array) containing astrometry'
+        print,'X,Y - Input X and Y positions (scalar or vector)'
+        print,'A,D - Output RA and Dec in decimal degrees'
+        return
+ endif                                                         
+
+  extast, hdr, astr, noparams, ALT = alt       ;Extract astrometry structure
+
+  if ( noparams LT 0 ) then begin
+        if alt EQ '' then $
+        message,'ERROR - No astrometry info in supplied FITS header' $
+	else  message, $
+	'ERROR  - No alt=' + alt + ' astrometry info in supplied FITS header'
+  endif	
+ 
+  astr2 = TAG_EXIST(astr,'AXES')
+  
+  if ( npar lt 3 ) then read,'XYAD: Enter X and Y positions: ',x,y
+
+  case strmid(astr.ctype[0],5,3)  of 
+        'GSS': gsssxyad, astr, x, y, a, d
+         else: xy2ad, x, y, astr, a, d
+  endcase
+  titname = strmid(astr.ctype,0,4)
+  if (titname[0] EQ 'DEC-') || (titname[0] EQ 'ELAT') or $
+          (titname[0] EQ 'GLAT') then titname = rotate(titname,2)
+
+  eqnx = Get_Equinox(hdr,code)  
+  IF astr2 THEN FK4 = STRMID(astr.RADECSYS,0,3) EQ 'FK4' ELSE $
+     FK4 = eqnx EQ 1950
+
+  if keyword_set(GALACTIC) then begin
+      case titname[0] of 
+      'RA--': euler, a,d, select=1, FK4=fk4
+      'ELON': euler, a,d, select=5, FK4=fk4
+      'GLON':
+      else: MESSAGE, "doesn't know how to convert from "+titname
+      endcase
+      titname = ['GLON','GLAT']
+  endif else if keyword_set(ECLIPTIC) then begin 
+      case titname[0] of 
+      'RA--': euler, a, d, select=3, FK4=fk4
+      'ELON':
+      'GLON': euler, a,d, select=6, FK4=fk4
+      else: MESSAGE, "doesn't know how to convert from "+titname
+      endcase
+      titname = ['ELON','ELAT']
+  endif else if keyword_set(CELESTIAL) then begin
+      case titname[0] of 
+      'RA--':
+      'ELON': euler, a, d, select=4, FK4 = FK4
+      'GLON': euler, a,d, select=2, FK4 = FK4
+      else: MESSAGE, "doesn't know how to convert from "+titname
+      endcase
+      titname = ['RA--','DEC-']
+  endif 
+
+  if (npar lt 5) or keyword_set(PRINT) then begin
+        g = where( finite(d) and finite(a), Ng)
+	 tit1= titname[0]
+	 t1 = strpos(tit1,'-')
+	 if t1 gt 0 then tit1 = strmid(tit1,0,t1)
+	 tit2= titname[1]
+	 t1 = strpos(tit2,'-')
+	 if t1 gt 0 then tit2 = strmid(tit2,0,t1)
+        npts = N_elements(X)
+        spherical = strmid(astr.ctype[0],4,1) EQ '-'
+        fmt = '(2F8.2,2x,2F9.4,2x,A)'
+        if spherical then begin
+
+        tit = '    X       Y         ' + tit1 + '      ' + tit2 
+	sexig = strmid(titname[0],0,4) EQ 'RA--'
+	if sexig then begin 
+  
+ 	eqnx = code NE -1 ? '_' + string(eqnx,f='(I4)') :  '    '
+	tit +=  $
+	   '        ' + tit1  + eqnx +  '      ' + tit2 + eqnx
+        if N_elements(precision) EQ 0 then precision = 1
+        str = replicate('    ---          ---    ', Npts)
+        if Ng GT 0 then str[g] = adstring(a[g],d[g],precision)
+	endif else str = replicate('', npts)
+	print,tit
+        for i=0l, npts-1 do $
+        print,FORMAT=fmt, float(x[i]), float(y[i]), a[i], d[i], str[i]	
+	
+        endif else begin
+            unit1 = strtrim( sxpar( hdr, 'CUNIT1'+alt,count = N_unit1),2)
+            if N_unit1 EQ 0 then unit1 = ''
+	    unit2 = strtrim( sxpar( hdr, 'CUNIT2'+alt,count = N_unit2),2)
+            if N_unit2 EQ 0 then unit2 = ''
+       print,'    X       Y         ' + titname[0] + '     ' + titname[1] 
+       if (N_unit1 GT 0) || (N_unit2 GT 0) then $
+           print,unit1 ,unit2,f='(t23,a,t33,a)' 	    
+       for i=0l, npts-1 do $
+            print,FORMAT=fmt, float(x[i]), float(y[i]), a[i], d[i]
+        endelse 
+   endif
+   
+   return
+   end
diff --git a/Code/script_idl_mv/astrolib/xyxy.pro b/Code/script_idl_mv/astrolib/xyxy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d0008e9f4bfd55c689573d022b1c38a8b40bffda
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/xyxy.pro
@@ -0,0 +1,110 @@
+PRO xyxy, hdra, hdrb, xa, ya, xb, yb
+;+
+; NAME:
+;	XYXY
+; PURPOSE:
+;	To use a pair of headers to convert X/Y positions from one frame
+;	to another.
+; CALLING SEQUENCE:
+;	XYXY, hdra, hdrb, xa, ya, [ xb, yb ]
+; INPUTS:
+;	hdra - The header containing the plate solution describing the
+;	       frame of reference being converted FROM.
+;	hdra - The header containing the plate solution describing the
+;	       frame of reference being converted TO.
+;	xa   - A scalar or vector containing the x coordinate(s) to convert.
+;	ya   - A scalar or vector containing the y coordinate(s) to convert.
+;	       Must have the same number of elements as 'xa'.
+; OUTPUTS:
+;	xb   - The converted x coordinate(s).  If this parameter is not
+;	       specified, it is returned through 'xa'.
+;	yb   - The converted y coordinate(s).  If this parameter is not
+;	       specified, it is returned through 'ya'.
+; PROCEDURE:
+;	The procedures 'xyad' and 'adxy' are used to perform the 
+;       conversion.     The equinoxes of each header are checked with
+;       "get_equinox" to make sure that they are identical, and "precess"
+;       is used if they are not.   HEULER used if the headers have a different
+;       coordinate system (e.g. Celestial, Galactic, Ecliptic)
+;
+;       Note that all X,Y coordinates are in the IDL convention (starting with
+;       0,0) and not the FITS convention (first pixel is 1,1)
+; PROCEDURES USED:
+;	GET_EQUINOX(), EXTAST, XYAD, ADXY, PRECESS, HEULER
+; MODIFICATION HISTORY:
+;	Written by Michael R. Greason, Hughes-STX, 13 April 1992.
+;	Updated to use ASTROMETRY structures.  J.D.Offenberg, HSTX, Jan 1993
+;	Converted to IDL V5.0   W. Landsman   September 1997
+;       Check coordinate system   J. Ballet/ W. Landsman  April 2004
+;-
+On_error,2
+;			Check parameters.
+np = N_params()
+if (np LT 4) then begin  
+	print, "Syntax:  xyxy, hdra, hdrb, xa, ya [, xb, yb]"
+	return
+endif
+if ( N_elements(xa) NE N_elements(ya) ) then begin  
+	message,/CON, $
+     'ERROR - The first two parameters must have the same number of elements.'
+	return
+endif
+epa = get_equinox( hdra, codea)
+epb = get_equinox( hdrb, codeb)
+
+;			Extract the plate solutions from the headers.  If
+;			either header hasn't a plate solution, set the 
+;			output coordinates to the inputs and return.
+;
+extast, hdra, astra, noparamsa
+extast, hdrb, astrb, noparamsb
+IF ( (noparamsa LT 0) OR (noparamsb LT 0)) THEN BEGIN
+	xb = xa
+	yb = ya
+	return
+endif
+
+;Convert between Celestial, Galactic, and Ecliptic if necessary
+
+typea = strmid(astra.ctype[0],0,4)
+typeb = strmid(astrb.ctype[0],0,4)
+
+IF typea NE typeb THEN $
+     case typeb OF
+     'GLON': HEULER, astra, /GALACTIC
+     'RA--': HEULER, astra, /CELESTIAL
+     'ELON': HEULER, astra, /ECLIPTIC
+      ELSE : BEGIN
+             PRINT, 'Cannot convert between coordinate systems ', typea, $
+                      ' and ', typeb
+             RETURN
+      ENDELSE
+   ENDCASE
+
+;			Perform the conversion.
+
+case strmid(astra.ctype[0],5,3)  of
+   'GSS': gsssxyad, astra, xa, ya, a, d
+   else: xy2ad, xa, ya, astra, a, d
+endcase
+
+if ( codea GE 0 ) and (codeb GE 0) then $
+    if ( epa NE epb ) then $
+           precess, a, d, epa, epb
+
+case strmid( astrb.ctype[0], 5,3) of
+ 'GSS': gsssadxy, astrb, a, d, xb, yb
+  else:  ad2xy, a, d, astrb, xb, yb 
+endcase
+
+
+;		If 'xb' and 'yb' weren't specified in the procedure
+;		call, overwrite xa and ya.
+
+if ( np LT 6 ) then begin  
+	xa = xb
+	ya = yb
+endif
+;
+return
+end
diff --git a/Code/script_idl_mv/astrolib/xyz.pro b/Code/script_idl_mv/astrolib/xyz.pro
new file mode 100644
index 0000000000000000000000000000000000000000..338fb1e3eccc0fc2ae92a8b9e7eb77141a0376de
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/xyz.pro
@@ -0,0 +1,189 @@
+pro xyz,date,x,y,z,xvel,yvel,zvel,equinox=equinox
+
+;+
+; NAME:
+;       XYZ
+; PURPOSE:
+;       Calculate geocentric X,Y, and Z  and velocity coordinates of the Sun
+; EXPLANATION:
+;       Calculates geocentric X,Y, and Z vectors and velocity coordinates 
+;       (dx, dy and dz) of the Sun.   (The positive X axis is directed towards 
+;       the equinox, the y-axis, towards the point on the equator at right 
+;       ascension 6h, and the z axis toward the north pole of the equator).
+;       Typical position accuracy is <1e-4 AU (15000 km).
+;
+; CALLING SEQUENCE:
+;       XYZ, date, x, y, z, [ xvel, yvel, zvel, EQUINOX = ]
+;
+; INPUT:
+;       date: reduced julian date (=JD - 2400000), scalar or vector
+;
+; OUTPUT:
+;       x,y,z: scalars or vectors giving heliocentric rectangular coordinates
+;                 (in A.U) for each date supplied.    Note that sqrt(x^2 + y^2
+;                 + z^2) gives the Earth-Sun distance for the given date.
+;       xvel, yvel, zvel: velocity vectors corresponding to X, Y and Z.
+;
+; OPTIONAL KEYWORD INPUT:
+;       EQUINOX: equinox of output. Default is 1950.
+;
+; EXAMPLE:
+;       What were the rectangular coordinates and velocities of the Sun on 
+;       Jan 22, 1999 0h UT (= JD 2451200.5) in J2000 coords? NOTE:
+;       Astronomical Almanac (AA) is in TDT, so add 64 seconds to 
+;       UT to convert.
+;
+;       IDL> xyz,51200.5+64.d/86400.d,x,y,z,xv,yv,zv,equinox = 2000
+;
+;       Compare to Astronomical Almanac (1999 page C20)
+;                   X  (AU)        Y  (AU)     Z (AU)
+;       XYZ:      0.51456871   -0.76963263  -0.33376880
+;       AA:       0.51453130   -0.7697110   -0.3337152
+;       abs(err): 0.00003739    0.00007839   0.00005360
+;       abs(err)
+;           (km):   5609          11759         8040 
+;
+;       NOTE: Velocities in AA are for Earth/Moon barycenter
+;             (a very minor offset) see AA 1999 page E3
+;                  X VEL (AU/DAY) YVEL (AU/DAY)   Z VEL (AU/DAY)
+;       XYZ:      -0.014947268   -0.0083148382    -0.0036068577
+;       AA:       -0.01494574    -0.00831185      -0.00360365
+;       abs(err):  0.000001583    0.0000029886     0.0000032077
+;       abs(err)
+;        (km/sec): 0.00265        0.00519          0.00557
+;
+; PROCEDURE CALLS:
+;       PRECESS_XYZ
+; REVISION HISTORY
+;       Original algorithm from Almanac for Computers, Doggett et al. USNO 1978
+;       Adapted from the book Astronomical Photometry by A. Henden
+;       Written  W. Landsman   STX       June 1989
+;       Correct error in X coefficient   W. Landsman HSTX  January 1995
+;       Added velocities, more terms to positions and EQUINOX keyword,
+;          some minor adjustments to calculations 
+;          P. Plait/ACC March 24, 1999
+;-
+
+   On_error,2
+  
+   if (n_params() eq 0) then begin
+      print,'Syntax - XYZ, date, x, y, z, [ xvel, yvel, zvel, EQUINOX= ]'
+      print,'     (date is REDUCED Julian date (JD - 2400000.0) )'
+      return
+   endif
+
+   picon = !DPI/180.0d
+   t = (date - 15020.0d0)/36525.0d0         ;Relative Julian century from 1900
+
+; NOTE: longitude arguments below are given in *equinox* of date.
+;   Precess these to equinox 1950 to give everything an even footing.
+;   Compute argument of precession from equinox of date back to 1950
+   pp = (1.396041d + 0.000308d*(t + 0.5d))*(t-0.499998d)
+
+; Compute mean solar longitude, precessed back to 1950
+   el = 279.696678D + 36000.76892D*t + 0.000303d*t*t - pp
+
+; Compute Mean longitude of the Moon
+   c = 270.434164d + 480960.d*t + 307.883142d*t - 0.001133d*t*t - pp
+
+; Compute longitude of Moon's ascending node
+   n = 259.183275d - 1800.d*t - 134.142008d*t + 0.002078d*t*t - pp
+
+; Compute mean solar anomaly
+   g = 358.475833d + 35999.04975d*t - 0.00015d*t*t
+
+; Compute the mean jupiter anomaly
+   j = 225.444651d + 2880.0d*t + 154.906654d*t*t
+
+; Compute mean anomaly of Venus
+   v = 212.603219d + 58320.d*t + 197.803875d*t + 0.001286d*t*t
+
+; Compute mean anomaly of Mars
+   m = 319.529425d + 19080.d*t + 59.8585d*t + 0.000181d*t*t
+
+; Convert degrees to radians for trig functions
+   el = el*picon
+   g  = g*picon
+   j =  j*picon
+   c  = c*picon
+   v  = v*picon
+   n  = n*picon
+   m  = m*picon
+
+; Calculate X,Y,Z using trigonometric series
+   X =   0.999860d*cos(el)                          $
+       - 0.025127d*cos(g - el)                      $
+       + 0.008374d*cos(g + el)                      $
+       + 0.000105d*cos(g + g + el)                  $
+       + 0.000063d*t*cos(g - el)                    $
+       + 0.000035d*cos(g + g - el)                  $
+       - 0.000026d*sin(g - el - j)                  $
+       - 0.000021d*t*cos(g + el)                    $
+       + 0.000018d*sin(2.d*g + el - 2.d*v)          $
+       + 0.000017d*cos(c)                           $
+       - 0.000014d*cos(c - 2.d*el)                  $
+       + 0.000012d*cos(4.d*g + el - 8.d*m + 3.d*j)  $
+       - 0.000012d*cos(4.d*g - el - 8.d*m + 3.d*j)  $
+       - 0.000012d*cos(g + el - v)                  $
+       + 0.000011d*cos(2.d*g + el - 2.d*v)          $
+       + 0.000011d*cos(2.d*g - el - 2.d*j)         
+  
+
+   Y =   0.917308d*sin(el)                             $
+       + 0.023053d*sin(g - el)                         $
+       + 0.007683d*sin(g + el)                         $
+       + 0.000097d*sin(g + g + el)                     $
+       - 0.000057d*t*sin(g - el)                       $
+       - 0.000032d*sin(g + g - el)                     $
+       - 0.000024d*cos(g - el - j)                     $
+       - 0.000019d*t*sin(g + el)                       $
+       - 0.000017d*cos(2.d0*g + el - 2.d0*v)           $
+       + 0.000016d*sin(c)                              $
+       + 0.000013d*sin(c - 2.d0*el )                   $
+       + 0.000011d*sin(4.d0*g + el - 8.d0*m + 3.d0*j)  $
+       + 0.000011d*sin(4.d0*g - el - 8.d0*m + 3.d0*j)  $
+       - 0.000011d*sin(g + el - v)                     $
+       + 0.000010d*sin(2.d0*g + el - 2.d0*v )          $
+       - 0.000010d*sin(2.d0*g - el - 2.d0*j )         
+
+
+   Z =   0.397825d*sin(el)        $
+       + 0.009998d*sin(g-el)      $
+       + 0.003332d*sin(g+el)      $
+       + 0.000042d*sin(g+g+el)    $
+       - 0.000025d*t*sin(g-el)    $
+       - 0.000014d*sin(g+g-el)    $
+       - 0.000010d*cos(g-el-j)    
+
+;Precess_to new equator?
+   if keyword_set(equinox) then precess_xyz, x, y, z, 1950, equinox
+
+   if N_params() LE 3 then return
+   
+   XVEL = -0.017200d * sin(el)           $
+          -0.000288d * sin(g + el)       $
+          -0.000005d * sin(2.d0*g + el)  $
+          -0.000004d * sin(c)            $
+          +0.000003d * sin(c - 2.d0*el)  $
+          +0.000001d *t * sin(g+el)      $
+          -0.000001d * sin(2.d0*g-el)           
+ 
+   YVEL =  0.015780 * cos(el)            $
+          +0.000264 * cos(g + el)        $
+          +0.000005 * cos(2.d0*g + el)   $
+          +0.000004 * cos(c)             $
+          +0.000003 * cos(c - 2.d0*el)   $
+          -0.000001 * t * cos(g + el)    
+
+   ZVEL = 0.006843 * cos(el)             $
+         +0.000115 * cos(g  + el)        $
+         +0.000002 * cos(2.d0*g + el)    $
+         +0.000002 * cos(c)              $
+         +0.000001 * cos(c - 2.d0*el)    
+
+;Precess to new equator?
+
+   if keyword_set(equinox) then precess_xyz, xvel, yvel, zvel, 1950, equinox
+
+   return
+   end
diff --git a/Code/script_idl_mv/astrolib/ydn2md.pro b/Code/script_idl_mv/astrolib/ydn2md.pro
new file mode 100644
index 0000000000000000000000000000000000000000..17dbb65a43ac4683ef89e6cf247ea77a5444e571
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ydn2md.pro
@@ -0,0 +1,62 @@
+;-------------------------------------------------------------
+;+
+; NAME:
+;       YDN2MD
+; PURPOSE:
+;       Convert from year and day number of year to month and day of month.       
+; CALLING SEQUENCE:
+;       YDN2MD,yr,dy,m,d
+; INPUTS:
+;       yr = 4 digit year (like 1988), integer scalar
+;       dy = day number in year (like 310), integer scalar or vector
+;
+; OUTPUTS:
+;       m = month number (1-12, e.g. 11 = Nov)   
+;       d = day of month (like 5).          
+;       Note: On error returns m = d = -1.
+;
+; EXAMPLE:
+;       Find the month/day of days 155 and 255 in the year 2001
+;
+;       IDL> ydn2md, 2001, [155,255], m, d
+;         ==> m = [6,9]   & d = [4,12]        ; = June 4 and September 12 
+;       
+; MODIFICATION HISTORY:
+;       Adapted from Johns Hopkins University/Applied Physics Laboratory
+;       Update to use VALUE_LOCATE,   W. Landsman    January 2001  
+;-
+;-------------------------------------------------------------
+ 
+	PRO YDN2MD,YR,DY,M,D, help=hlp
+ 
+	IF (N_PARAMS() LT 4) or keyword_set(hlp) THEN BEGIN
+	  PRINT,' Convert from year and day number of year to month '+$
+	    'and day of month.'
+	  PRINT,' ydn2md,yr,dy,m,d'
+	  PRINT,'   yr = year (like 1988), scalar input'
+	  PRINT,'   dy = day number in year (like 310), scalar or vector input'
+	  PRINT,'   m = month number (like 11 = Nov).    out'
+	  PRINT,'   d = day of month (like 5).           out'
+	  PRINT,' Note: On error returns m = d = -1.'
+	  RETURN
+	ENDIF
+ 
+	; Days before start of each month.
+	YDAYS = [0,31,59,90,120,151,181,212,243,273,304,334,366] + 1
+ 
+	LEAP =  (((YR MOD 4) EQ 0) AND ((YR MOD 100) NE 0)) OR $
+                ((YR MOD 400) EQ 0) 
+                    
+        IF LEAP THEN YDAYS[2] = YDAYS[2:*] + 1
+        M = VALUE_LOCATE(YDAYS, DY) + 1
+	D = DY - YDAYS[M-1] + 1
+        BAD = WHERE(M GT 12, NBAD)
+
+        IF NBAD GT 0 THEN BEGIN
+            M[BAD] = -1
+            D[BAD] = -1
+            MESSAGE,'Error in Day Number',/CON
+        ENDIF
+	RETURN
+ 
+	END
diff --git a/Code/script_idl_mv/astrolib/ymd2dn.pro b/Code/script_idl_mv/astrolib/ymd2dn.pro
new file mode 100644
index 0000000000000000000000000000000000000000..96010541e9475b1333e9a8f81e08070e58ac8f3e
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/ymd2dn.pro
@@ -0,0 +1,56 @@
+;-------------------------------------------------------------
+;+
+; NAME:
+;       YMD2DN
+; PURPOSE:
+;       Convert from year, month, day to day number of year.
+; CATEGORY:
+; CALLING SEQUENCE:
+;       dy = ymd2dn(yr,m,d)
+; INPUTS:
+;       yr = year (like 1988).      scalar or vector
+;       m = month number (like 11 = Nov).   scalar or vector
+;       d = day of month (like 5).        scalar or vector
+; KEYWORD PARAMETERS:
+; OUTPUTS:
+;       dy = day number in year (like 310).  out
+; COMMON BLOCKS:
+; NOTES:
+; MODIFICATION HISTORY:
+;       Written by R. Sterner, 20 June, 1985.
+;       Johns Hopkins University Applied Physics Laboratory.
+;       RES 18 Sep, 1989 --- converted to SUN
+;       R. Sterner, 1997 Feb 3 --- Made work for arrays.
+;
+; Copyright (C) 1985, Johns Hopkins University/Applied Physics Laboratory
+; This software may be used, copied, or redistributed as long as it is not
+; sold and this copyright notice is reproduced on each copy made.  This
+; routine is provided as is without any express or implied warranties
+; whatsoever.  Other limitations apply as described in the file disclaimer.txt.
+;	Converted to IDL V5.0   W. Landsman  2-Jan-1998
+;-
+;-------------------------------------------------------------
+ 
+	function ymd2dn,yr,m,d, help=hlp
+ 
+	if (n_params(0) lt 3) or keyword_set(hlp) then begin
+	  print,' Convert from year, month, day to day number of year.'
+	  print,' dy = ymd2dn(yr,m,d)'
+	  print,'   yr = year (like 1988).               in'
+	  print,'   m = month number (like 11 = Nov).    in'
+	  print,'   d = day of month (like 5).           in'
+	  print,'   dy = day number in year (like 310).  out'
+	  return, -1
+	endif
+ 
+	;----  Days before start of each month (non-leap year)  -----
+	idays = [0,31,59,90,120,151,181,212,243,273,304,334,366]
+ 
+	;----  Correct for leap year if month ge 3  -------------
+	lpyr = (((yr mod 4) eq 0) and ((yr mod 100) ne 0)) $
+            or ((yr mod 400) eq 0) and (m ge 3)
+ 
+	dy = d + idays[m-1] + lpyr
+	return, dy
+ 
+	end
diff --git a/Code/script_idl_mv/astrolib/zang.pro b/Code/script_idl_mv/astrolib/zang.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4c2934c450dd52a53b5444af2b37d0f731f29dc7
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/zang.pro
@@ -0,0 +1,78 @@
+function zang,dl,z, h0=h0, k = k, Lambda0 = lambda0, Omega_m = Omega_m, $
+                    q0 = q0, SILENT = silent
+;+
+; NAME:
+;       ZANG
+; PURPOSE:
+;       Determine the angular size of an object as a function of redshift
+; EXPLANATION:
+;       Requires an input size in kpc and returns an angular size in arc seconds
+;       Default cosmology has a Hubble constant of 70 km/s/Mpc, Omega (matter)
+;       =0.3 and a normalized cosmological constant Lambda = 0.7; however these
+;       values can be changed with appropriate keywords.
+;
+; CALLING SEQUENCE:
+;       angsiz = zang( dl, [ z, H0 =, Omega_m =, Lambda0 = , q0 = , k =, 
+;                               /SILENT] )
+;
+; INPUTS:
+;       dl - linear size of the object *in kpc*, non-negative scalar or vector
+;       z - redshift of object, postive  scalar or vector
+;           Either dl and z must have the same number of elements, or at least
+;           one of them must be a vector.
+; OPTIONAL INPUT KEYWORDS
+;    H0 -  Hubble constant in km/s/Mpc, default is 70
+;
+;        No more than two of the following four parameters should be
+;        specified.    None of them need be specified, default values are given
+;    k - curvature constant, normalized to the closure density.   Default is
+;        0, indicating a flat universe
+;    Omega_m -  Matter density, normalized to the closure density, default
+;        is 0.3.   Must be non-negative
+;    Lambda0 - Cosmological constant, normalized to the closure density,
+;        default is 0.7
+;    q0 - Deceleration parameter, numeric scalar = -R*(R'')/(R')^2, default
+;        is -0.55
+;
+;    Note that Omega_m + lambda0 + k = 1 and q0 = 0.5*omega_m - lambda0
+; OUTPUT:
+;       angsiz - Angular size of the object at the given redshift in 
+;               arc seconds 
+; EXAMPLE:
+;  (1) What would be the angular size of galaxy of diameter 50 kpc at a redshift
+;      of 1.5 in an open universe with Lambda = 0 and Omega (matter) = 0.3.
+;      Assume the default Hubble constant value of 70 km/s/Mpc.
+;      
+;      IDL> print,zang(50,1.5, Lambda = 0,omega_m = 0.3)
+;             ====> 6.58 arc seconds
+;
+;  (2) Now plot the angular size of a 50 kpc diameter galaxy as a function of 
+;      redshift for the default cosmology (Lambda = 0.7, Omega_m=0.3) up to 
+;      z = 0.5
+;      IDL> z = findgen(50)/10. + 0.1    ;Angular size undefined at z = 0
+;      IDL> plot,z,zang(50,z),xtit='z',ytit='Angular Size (")'
+; NOTES:
+;      This procedure underwent a major revision in April 2000 to allow for a 
+;      cosmological constant, ***including a change of the calling sequence***
+;
+;      Be sure to supply the input linear size dl in units of kpc.
+; PROCEDURES CALLED:
+;      LUMDIST() -- Calculates the luminosity distance
+; REVISION HISTORY:
+;      Written    J. Hill   STX           July, 1988
+;      Converted to IDL V5.0   W. Landsman   September 1997
+;      Major rewrite to call LUMDIST function  W. Landsman   April 2000     
+;-
+
+ if N_params() LT 2 then begin 
+      print,'Sytnax - ' + $
+ 'angsiz = zang( dl, z, [H0 =, Omega_m =, Lambda0 = , q0 = , k =, /SILENT])'
+      return,-1
+ endif
+
+ d = lumdist(z,H0 = h0,k = k, Lambda0 = lambda0, Omega_m = Omega_m,  q0 = q0, $
+               SILENT = silent)
+
+; Angular distance is equal to the luminosity distance times (1+z)^2
+ return,!RADEG*3600.*dl*(1.+z)^2/(1000.*d)
+ end
diff --git a/Code/script_idl_mv/astrolib/zbrent.pro b/Code/script_idl_mv/astrolib/zbrent.pro
new file mode 100644
index 0000000000000000000000000000000000000000..86942f58f30299d35a50b2ca6cf3f2e3260e7931
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/zbrent.pro
@@ -0,0 +1,157 @@
+function ZBRENT, x1, x2, FUNC_NAME=func_name, _EXTRA = _extra,   $
+                         MAX_ITERATIONS=maxit, TOLERANCE=TOL
+;+
+; NAME:
+;     ZBRENT
+; PURPOSE:
+;     Find the zero of a 1-D function up to specified tolerance.
+; EXPLANTION:
+;     This routine assumes that the function is known to have a zero.
+;     Adapted from procedure of the same name in "Numerical Recipes" by
+;     Press et al. (1992), Section 9.3
+;
+; CALLING:
+;       x_zero = ZBRENT( x1, x2, FUNC_NAME="name", MaX_Iter=, Tolerance=, 
+;                                 _EXTRA =  )
+;
+; INPUTS:
+;       x1, x2 = scalars, 2 points which bracket location of function zero,
+;                                               that is, F(x1) < 0 < F(x2).
+;       Note: computations are performed with
+;       same precision (single/double) as the inputs and user supplied function.
+;
+; REQUIRED INPUT KEYWORD:
+;       FUNC_NAME = function name (string)
+;               Calling mechanism should be:  F = func_name( px )
+;               where:  px = scalar independent variable, input.
+;                       F = scalar value of function at px,
+;                           should be same precision (single/double) as input.
+;
+; OPTIONAL INPUT KEYWORDS:
+;       MAX_ITER = maximum allowed number iterations, default=100.
+;       TOLERANCE = desired accuracy of minimum location, default = 1.e-3.
+;
+;       Any other keywords are passed directly to the user-supplied function
+;       via the _EXTRA facility.
+; OUTPUTS:
+;       Returns the location of zero, with accuracy of specified tolerance.
+;
+; PROCEDURE:
+;       Brent's method to find zero of a function by using bracketing,
+;       bisection, and inverse quadratic interpolation,
+;
+; EXAMPLE:
+;       Find the root of the COSINE function between 1. and 2.  radians
+;
+;        IDL> print, zbrent( 1, 2, FUNC = 'COS')
+;
+;       and the result will be !PI/2 within the specified tolerance
+; MODIFICATION HISTORY:
+;       Written, Frank Varosi NASA/GSFC 1992.
+;       FV.1994, mod to check for single/double prec. and set zeps accordingly.
+;       Use MACHAR() to define machine precision   W. Landsman September 2002
+;       Added _EXTRA keyword  W. Landsman  December 2011
+;       Need to check whether user function accepts keywords W.L. Jan 2012
+;-
+        compile_opt idl2
+        if N_params() LT 2 then begin
+             print,'Syntax - result = ZBRENT( x1, x2, FUNC_NAME = ,'
+             print,'                  [ MAX_ITER = , TOLERANCE = , _EXTRA=])'
+             return, -1
+        endif
+
+        kpresent = keyword_set(_EXTRA)
+        if N_elements( TOL ) NE 1 then TOL = 1.e-3
+        if N_elements( maxit ) NE 1 then maxit = 100
+
+        if size(x1,/TNAME) EQ 'DOUBLE' OR size(x2,/TNAME) EQ 'DOUBLE' then begin
+                xa = double( x1 )
+                xb = double( x2 )
+                zeps = (machar(/DOUBLE)).eps   ;machine epsilon in double.
+          endif else begin
+                xa = x1
+                xb = x2
+                zeps = (machar(/DOUBLE)).eps   ;machine epsilon, in single 
+           endelse
+        
+	if kpresent then begin 
+          fa = call_function( func_name, xa, _EXTRA = _extra )
+          fb = call_function( func_name, xb, _EXTRA = _extra )
+        endif else begin 
+          fa = call_function( func_name, xa )
+          fb = call_function( func_name, xb )
+	endelse
+        fc = fb
+
+        if (fb*fa GT 0) then begin
+                message,"root must be bracketed by the 2 inputs",/INFO
+                return,xa
+           endif
+
+        for iter = 1,maxit do begin
+
+                if (fb*fc GT 0) then begin
+                        xc = xa
+                        fc = fa
+                        Din = xb - xa
+                        Dold = Din
+                   endif
+
+                if (abs( fc ) LT abs( fb )) then begin
+                        xa = xb   &   xb = xc   &   xc = xa
+                        fa = fb   &   fb = fc   &   fc = fa
+                   endif
+
+                TOL1 = 0.5*TOL + 2*abs( xb ) * zeps     ;Convergence check
+                xm = (xc - xb)/2.
+
+                if (abs( xm ) LE TOL1) || (fb EQ 0) then return,xb
+
+                if (abs( Dold ) GE TOL1) && (abs( fa ) GT abs( fb )) then begin
+
+                        S = fb/fa       ;attempt inverse quadratic interpolation
+
+                        if (xa EQ xc) then begin
+                                p = 2 * xm * S
+                                q = 1-S
+                          endif else begin
+                                T = fa/fc
+                                R = fb/fc
+                                p = S * (2*xm*T*(T-R) - (xb-xa)*(R-1) )
+                                q = (T-1)*(R-1)*(S-1)
+                           endelse
+
+                        if (p GT 0) then q = -q
+                        p = abs( p )
+                        test = ( 3*xm*q - abs( q*TOL1 ) ) < abs( Dold*q )
+
+                        if (2*p LT test)  then begin
+                                Dold = Din              ;accept interpolation
+                                Din = p/q
+                          endif else begin
+                                Din = xm                ;use bisection instead
+                                Dold = xm
+                           endelse
+
+                  endif else begin
+
+                        Din = xm    ;Bounds decreasing to slowly, use bisection
+                        Dold = xm
+                   endelse
+
+                xa = xb
+                fa = fb         ;evaluate new trial root.
+
+                if (abs( Din ) GT TOL1) then xb = xb + Din $
+                                        else xb = xb + TOL1 * (1-2*(xm LT 0))
+
+                if kpresent then $
+                   fb = call_function( func_name, xb, _EXTRA = _extra ) else $
+                   fb = call_function( func_name, xb )
+		   
+          endfor
+
+        message,"exceeded maximum number of iterations: "+strtrim(iter,2),/INFO
+
+return, xb
+end
diff --git a/Code/script_idl_mv/astrolib/zenpos.pro b/Code/script_idl_mv/astrolib/zenpos.pro
new file mode 100644
index 0000000000000000000000000000000000000000..9c5a861aa1a10597fba20bf6027f4f5c9c235f0b
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/zenpos.pro
@@ -0,0 +1,73 @@
+PRO ZENPOS, date, ra, dec
+;+
+; NAME:
+;       ZENPOS
+; PURPOSE:
+;       Return the zenith RA and Dec in radians for a given Julian date.
+;
+; CALLING SEQUENCE:
+;       ZENPOS, Date, Ra, Dec
+;
+; INPUT:
+;       Date  The Julian date, in double precision, of the date and time
+;               for which the zenith position is desired, scalar or vector.
+;
+; OUTPUTS:
+;       Ra    The right ascension in RADIANS of the zenith.
+;       Dec   The declination in RADIANS of the zenith.
+;
+; PROCEDURE:
+;       The local sidereal time is computed; this is the RA of the zenith.
+;       It and the observatories latitude (corresponding to the Dec.) are
+;       converted to radians and returned as the zenith direction.
+;
+; PROMPTS:
+;       ZENPOS will prompt for the following 3 parameters if they are not
+;       defined in the common block SITE (see below)
+;
+;       LAT,LNG - north latitude and east longitude of the desired location 
+;               in DEGREES
+;       TZONE - Time Zone (in hours) of the desired location (e.g. 4 = EDT,
+;               5 = EST)
+;
+; COMMON BLOCKS:
+;       SITE - This common block should contain the three scalars LAT, LNG
+;               and TZONE
+;
+; PROCEDURE CALLS:
+;       CT2LST - Convert to Local Mean Sidereal Time
+; MODIFICATION HISTORY:
+;       Written by Michael R. Greason, STX, 14 October 1988.
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Update documentation, longitude now *east* of Greenwich W.L. July 2000
+;-
+ COMMON SITE, lat, lng, tzone
+
+ if N_params() EQ 0 then begin
+     print,'Syntax - zenpos, dte, ra, dec'
+     print,'         dte = Julian Date, Ouput Ra and Dec in radians'
+     return
+ endif
+
+ if N_elements(lat) eq 0 then read, $
+       'Enter latitude and longitude (in degrees): ',lat,lng
+ if N_elements(tzone) eq 0 then read, $
+       'Enter time zone (in hours): ',tzone
+;
+;                            Define the needed conversion factors.
+;
+ d2rad = !DPI / 180.D0
+ h2rad = !DPI / 12.D0
+;
+;                            Get the sidereal time corresponding to the 
+;                            supplied date.
+;
+ ct2lst, lst, lng, tzone, date
+;
+;                            Compute the RA and Dec.
+;
+ ra = lst * h2rad
+ dec = ra*0. + lat * d2rad
+;
+ RETURN
+ END
diff --git a/Code/script_idl_mv/astrolib/zoom_xy.pro b/Code/script_idl_mv/astrolib/zoom_xy.pro
new file mode 100644
index 0000000000000000000000000000000000000000..75d9cb7e95c766f6a13cd55e5720e4169153c074
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/zoom_xy.pro
@@ -0,0 +1,79 @@
+pro zoom_xy, xim, yim, xtv, ytv, OFFSET=offset, ZOOM = zoom
+;+
+; NAME:
+;      ZOOM_XY
+; PURPOSE:
+;       Converts X, Y position on the image array to the the X,Y position 
+;       in the current window.   (These  positions are identical 
+;       only for an unroamed, zoomed image with with pixel [0,0] of the 
+;       image placed at position [0,0] on the image display.)
+;
+; CALLING SEQUENCE:
+;      ZOOM_XY, Xim,Yim,Xtv,Ytv, [ OFFSET =, ZOOM = ]
+;
+; INPUTS:
+;      XIM - Scalar or vector giving X position(s) in an image array.
+;      YIM - Like XTV but giving Y position(s) in an image array.
+;
+;      If only 2 parameters are supplied then XIM and YIM will be modfied
+;      on output to contain the converted coordinates.
+;
+; OPTIONAL KEYWORD INPUT:
+;      OFFSET - 2 element vector giving the location of the image pixel (0,0) 
+;               on the window display.   OFFSET can be positive (e.g if the 
+;               image is centered in a larger window) or negative (e.g. if the
+;               only the central region of an image much larger than the window
+;               is being displayed. 
+;               Default value is [0,0], or no offset.
+;
+;       ZOOM - Scalar specifying the magnification of the window with respect
+;               to the image variable.
+; OUTPUTS:
+;      XTV,YTV - REAL*4 X and Y coordinates on the image display corresponding
+;              to the input data coordinates. 
+; COMMON BLOCKS:
+;       If present, ZOOM_XY will use the TV and IMAGE common blocks which are
+;       defined in the MOUSSE software system (see 
+;        http://archive.stsci.edu/uit/analysis.html)   If the user is not using
+;       the MOUSSE software (which keeps track of the offset and zoom in each
+;       window) then the common blocks are ignored.
+; NOTES:
+;       The integer value of a pixel is assumed to refer to the *center*
+;       of a pixel.
+; REVISON HISTORY:
+;       Adapted from MOUSSE procedure of the same name W. Landsman HSTX Mar 1996
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Properly include ZOOM keyword  W. Landsman   May 2000
+;       Put back common blocks for MOUSSE compatibility    September 2004
+;-
+ On_error,2
+ Compile_opt idl2
+ common tv,chan,czoom,xroam,yroam
+ common images,x00,y00,xsize,ysize 
+
+ if N_params() LT 2 then begin
+        print,'Syntax - Zoom_XY, Xtv, Ytv, Xim, Yim, [ Offset=, Zoom = ]'
+        return
+ endif
+    
+ if N_elements(offset) NE 2 then begin
+;Determine if Images common block defined
+      if n_elements(x00) eq 0 then offset = [0,0] $ 
+                              else offset = [x00[chan],y00[chan]]
+ endif
+ if N_elements(zoom) NE 1 then begin 
+          if N_elements(czoom) GT 0 then zoom = czoom[chan] else $
+             zoom = 1
+ endif
+
+ cen =  (zoom-1)/2.
+
+ xtv =  cen + zoom*(xim + offset[0] )
+ ytv =  cen + zoom*(yim + offset[1] )
+
+ if N_Params() LT 3 then begin
+    xim = xtv  & yim = ytv
+ endif                  
+
+ return
+ end                                    
diff --git a/Code/script_idl_mv/astrolib/zparcheck.pro b/Code/script_idl_mv/astrolib/zparcheck.pro
new file mode 100644
index 0000000000000000000000000000000000000000..585cbbf221eeb42c19a2b4c00a0f4a008a2f4b82
--- /dev/null
+++ b/Code/script_idl_mv/astrolib/zparcheck.pro
@@ -0,0 +1,132 @@
+pro zparcheck,progname,parameter,parnum,types,dimens,message
+;+
+; NAME:
+;       ZPARCHECK
+; PURPOSE:
+;       Routine to check user parameters to a procedure
+;
+; CALLING SEQUENCE:
+;       zparcheck, progname, parameter, parnum, types, dimens, [ message ]
+;
+; INPUTS:
+;       progname  - scalar string name of calling procedure
+;       parameter - parameter passed to the routine
+;       parnum    - integer parameter number
+;       types     - integer scalar or vector of valid types
+;                1 - byte        2 - integer   3 - int*4
+;                4 - real*4      5 - real*8    6 - complex
+;                7 - string      8 - structure 9 - double complex
+;               10 - pointer    11 - object ref 12 - Unsigned integer
+;               13 - unsigned int*4 
+;               14 - int*8  
+;               15 - Unsigned int*8
+;       dimens   - integer scalar or vector giving number
+;                     of allowed dimensions.
+; OPTIONAL INPUT:
+;       message - string message describing the parameter to be printed if an 
+;               error is found
+;
+; OUTPUTS:
+;       none
+;
+; EXAMPLE:
+;       IDL> zparcheck, 'HREBIN', hdr, 2, 7, 1, 'FITS Image Header'
+;
+;       This example checks whether the parameter 'hdr' is of type string (=7)
+;       and is a vector (1 dimension).   If either of these tests fail, a 
+;       message will be printed
+;               "Parameter 2 (FITS Image Header) is undefined"
+;               "Valid dimensions are 1"
+;               "Valid types are string"        
+;
+; SIDE EFFECTS:
+;       If an error in the parameter is a message is printed
+;       a RETALL issued
+;
+; HISTORY
+;       version 1  D. Lindler  Dec. 86
+;       documentation updated.  M. Greason, May 1990.
+;       Recognize double complex datatype    W. Landsman   September 1995
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Check for new data types (e.g. unsigned) W. Landsman February 2000
+;       Print a traceback if an error occurs  W. Landsman  Aug 2011
+;-
+;----------------------------------------------------------
+  compile_opt idl2
+  if N_params() LT 4 then begin
+        print, $
+   'Syntax -  ZPARCHECK, progname, parameter, parnum, types, dimens, [message ]
+        return
+  endif
+
+; get type and size of parameter
+
+  s = size(parameter)
+  ndim = s[0]
+  type = s[ndim+1]
+
+; check if parameter defined.
+
+  if type EQ 0 then begin
+        err = ' is undefined.'
+        goto, ABORT 
+  endif
+
+; check for valid dimensions
+
+  valid = where( ndim EQ dimens, Nvalid)
+  if Nvalid LT 1 then begin
+        err = 'has wrong number of dimensions'
+        goto, ABORT   
+  endif
+
+; check for valid type
+
+  valid = where(type EQ types, Ngood)
+  if ngood lt 1 then begin
+        err = 'is an invalid data type'
+        goto, ABORT   
+  endif
+
+  return
+
+; bad parameter
+
+ABORT:
+  mess = ' '
+  if N_params() lt 6 then message = ''
+  if message NE '' then mess = ' ('+message+') '
+  print,string(7b) + 'Parameter '+strtrim(parnum,2) + mess,$
+        ' of routine ', strupcase(progname) + ' ', err
+  sdim = ' '
+  for i = 0,N_elements(dimens)-1 do begin
+        if dimens[i] eq 0 then sdim = sdim + 'scalar' $
+                          else sdim = sdim + string(dimens[i],'(i3)')
+  end
+  print,'Valid dimensions are:'+sdim
+
+  stype = ' '
+  for i = 0, N_elements( types )-1 do begin
+        case types[i] of
+                1: stype = stype + ' byte'
+                2: stype = stype + ' int*2'
+                3: stype = stype + ' int*4'
+                4: stype = stype + ' real*4'
+                5: stype = stype + ' real*8'
+                6: stype = stype + ' complex'
+                7: stype = stype + ' string'
+                8: stype = stype + ' structure'
+                9: stype = stype + ' dcomplex'
+               10: stype = stype + ' pointer'
+               11: stype = stype + ' Object'
+               12: stype = stype + ' Unsigned(i*2)'
+               13: stype = stype + ' Unsigned(i*4)'
+               14: stype = stype + ' int*8'
+               15: stype = stype + ' Unsigned(i*8)'
+        endcase
+  endfor
+  print,'Valid types are:' + stype
+  if scope_level() GT 3 then help,/trace
+  ;if !debug then stop
+  retall  ; zparcheck
+  end
diff --git a/Code/script_idl_mv/higalfit/lbol.pro b/Code/script_idl_mv/higalfit/lbol.pro
new file mode 100755
index 0000000000000000000000000000000000000000..26312c7b2f6bba5374a890b744df013d03d48d4e
--- /dev/null
+++ b/Code/script_idl_mv/higalfit/lbol.pro
@@ -0,0 +1,41 @@
+function lbol,w,f,d
+
+; interpolation between data points is done in logarithmic space to allow 
+; straight lines (the SED is like that) in the log-log plot. interpolation 
+; on a finer grid is done and then data are transformed back in linear space
+; where the trapezoidal interpolation is then done
+
+; w is lambda in um
+; f is flux jy
+; d is dist in parsec
+
+;convert flux to W/cm2/um
+fw=1.e-15*f*.2997/(w^2.)
+lw=alog10(w)
+lfw=alog10(fw)
+;1000 points resampling
+lw1=(findgen(1000)*((max(lw)-min(lw))/1000.))+min(lw)
+lfw1=interpol(lfw,lw,lw1)
+
+w1=10.^lw1
+fw1=10.^lfw1
+jy=fw1/1.e-15/.3*(w1^2.)
+;integrate over whole range
+fint=0.
+for i=0,n_elements(w1)-2 do fint=fint+((fw1(i)+fw1(i+1))*(w1(i+1)-w1(i))/2.)
+
+;fint=int_tabulated(w,fw,/double,/sort)
+; integrate longword of 350um
+;qc0=where(w ge 350.)
+;fc0=int_tabulated(w(qc0),fw(qc0))
+; compute lbol
+l=fint*4.*!pi*d*3.d18/3.8d26*d*3.d18   ;lsol
+
+;c0ratio=fc0/fint
+;print,'Lsubmm/Lbol = ',c0ratio
+
+return,l
+end
+
+
+
diff --git a/Code/script_idl_mv/higalfit/newtest.pro b/Code/script_idl_mv/higalfit/newtest.pro
new file mode 100644
index 0000000000000000000000000000000000000000..94beea62e8dbf56b5ca4ccab9ee483f61398f5b9
--- /dev/null
+++ b/Code/script_idl_mv/higalfit/newtest.pro
@@ -0,0 +1,26 @@
+pro newtest
+lambda=[250,350,500]                           ; in micron
+flux= [7.52693,4.67851,1.28158]                ; in Jy
+eflux=[0.169653,0.246719,0.337756,0.266316]    ; in Jy
+;lambda=[70.,160,250,350,500]                  ; in micron
+;flux=[244.963,238.151,492.615,236.352,37.521] ; in Jy
+;eflux=[27.166,54.749,106.240,37.521,8.985]    ; in Jy
+sizz=20.                                      ; observed FWHM in arcsec
+dist=1000.                                    ; in pc
+mrange=[10.,5000.,100]                        ; in M_Sun ; only for opt. thin
+trange=[7.,40.,50]                            ; in K; for both opt. thin and thick
+brange=[2.000,2.000,1]                        ; for both opt. thin and thick
+l0range=[10.,300,100]                         ; in micron; only for opt. thick
+sfactrange=[1.000,1.000,1]                    ; range of variability of the source size with respect to the observed
+                                              ; one (sizz); only for opt. thick
+sref=[0.1,300]                                ; kref in cm^2 g-1, lref (micron) 
+ul=[1,0,0,0,0]                                ; upper limit switch on/off; 0: regular flux, 1: upper limit 
+
+thinfile='./thinfit.csv'    ; output file containing the best-fitting greybody in csv format
+thickfile='./thickfit.csv'
+
+sedfitgrid_engine_thin_vialactea,lambda,flux,mrange,trange,brange,dist,sref,lambdatn,flussotn,mtn,ttn,btn,ltn,dmtn,dttn,errorbars=eflux,ulimit=ul,printfile=thinfile ; /silent
+;sedfitgrid_engine_thick_vialactea,lambda,flux,sizz,trange,brange,l0range,sfactrange,dist,sref,lambdagb,flussogb,mtk,ttk,btk,l0,sizesec,ltk,dmtk,dtk,dl0,errorbars=eflux,ulimit=ul,printfile=thickfile ; /silent
+
+
+end
diff --git a/Code/script_idl_mv/higalfit/planck.pro b/Code/script_idl_mv/higalfit/planck.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ffbf59089abb356c01f494dc9f99782d766572e9
--- /dev/null
+++ b/Code/script_idl_mv/higalfit/planck.pro
@@ -0,0 +1,71 @@
+function planck,wave,temp
+;+
+; NAME:
+;       PLANCK()   
+; PURPOSE: 
+;       To calculate the Planck function in units of ergs/cm2/s/A  
+;
+; CALLING SEQUENCE: 
+;       bbflux = PLANCK( wave, temp) 
+;
+; INPUT PARAMETERS: 
+;       WAVE   Scalar or vector giving the wavelength(s) in **Angstroms**
+;               at which the Planck function is to be evaluated.
+;       TEMP   Scalar giving the temperature of the planck function in degree K
+;
+; OUTPUT PARAMETERS:
+;       BBFLUX - Scalar or vector giving the blackbody flux (i.e. !pi*Intensity)
+;               in erg/cm^2/s/A in at the specified wavelength points.
+;
+; EXAMPLES:
+;       To calculate the blackbody flux at 30,000 K every 100 Angstroms between
+;       2000A and 2900 A
+;   
+;       IDL> wave = 2000 + findgen(10)*100
+;       IDL> bbflux = planck(wave,30000)
+;
+;       If a star with a blackbody spectrum has a radius R, and distance,d, then
+;       the flux at Earth in erg/cm^2/s/A will be bbflux*R^2/d^2
+; PROCEDURE:
+;       The wavelength data are converted to cm, and the Planck function
+;       is calculated for each wavelength point. See Allen (1973), Astrophysical
+;       Quantities, section 44 for more information.
+;
+; NOTES:
+;       See the procedure planck_radiance.pro in 
+;       ftp://origin.ssec.wisc.edu/pub/paulv/idl/Radiance/planck_radiance.pro
+;       for computation of Planck radiance given wavenumber in cm-1 or  
+;       wavelength in microns 
+; MODIFICATION HISTORY:
+;       Adapted from the IUE RDAF               August, 1989
+;       Converted to IDL V5.0   W. Landsman   September 1997
+;       Improve precision of constants    W. Landsman  January 2002
+;-
+ On_error,2
+
+ if ( N_elements(wave) LT 1 ) then begin
+     print,'Syntax - bbflux = planck( wave, temp)'
+     return,0
+  endif    
+
+  if ( N_elements( temp ) NE 1 ) then $
+      read,'Enter a blackbody temperature', temp
+
+  bbflux = wave*0.
+
+; Gives the blackbody flux (i.e. PI*Intensity) ergs/cm2/s/a
+
+  w = wave / 1.E8                              ; Angstroms to cm    
+;constants appropriate to cgs units.
+  c1 =  3.7417749d-5                ; =2*!DPI*h*c*c       
+  C2 =  1.4387687d                  ; =h*c/k
+  val =  c2/w/temp  
+  mstr = machar(double = (size(val,/type) EQ 5) )  ;Get machine precision      
+  good = where( val LT alog(mstr.xmax), Ngood )    ;Avoid floating underflow
+
+  if ( Ngood GT 0 ) then  $
+      bbflux[ good ] =  C1 / ( w[good]^5 * ( exp( val[good])-1. ) )
+
+  return, bbflux*1.E-8              ; Convert to ergs/cm2/s/A
+
+  end 
diff --git a/Code/script_idl_mv/higalfit/sedbank_thick_large.pro b/Code/script_idl_mv/higalfit/sedbank_thick_large.pro
new file mode 100644
index 0000000000000000000000000000000000000000..d0688a56cc62529c559eab3ccf507d5a74a2905b
--- /dev/null
+++ b/Code/script_idl_mv/higalfit/sedbank_thick_large.pro
@@ -0,0 +1,58 @@
+pro sedbank_thick_large,lam,lmin,lmax,nl,tmin,tmax,nt,bmin,bmax,nb,smin,smax,ns,lam0,temp,beta,siz,bank
+;Written by Davide Elia, 2012-2015
+; lam=lambda in um
+
+;print,systime()
+; k0 = 0.16 ; cm^2 g^-1  ; Olmi
+ ;nu0 = 3e14/250 ; Hz  ; Olmi
+
+ lmin=float(lmin)
+ tmin=float(tmin)
+ bmin=float(bmin)
+ smin=float(smin)
+
+
+if nl gt 1 then begin
+  lstep=(lmax-lmin)/(nl-1)
+  lam0=lmin+lstep*findgen(nl)
+endif else lam0=lmin
+
+if nt gt 1 then begin
+  tstep=(tmax-tmin)/(nt-1)
+  temp=tmin+tstep*findgen(nt)
+endif else temp=tmin
+
+if nb gt 1 then begin
+  bstep=(bmax-bmin)/(nb-1)
+  beta=bmin+bstep*findgen(nb)
+endif else beta=bmax
+
+if ns gt 1 then begin
+  sstep=(smax-smin)/(ns-1)
+  psiz=smin+sstep*findgen(ns)
+endif else psiz=smin
+siz=(psiz/206265.)^2.*!pi/4.
+
+alam=lam*1.d4    ; lambda in Angstrom
+
+nlam=n_elements(lam)
+bank=dblarr(nl,nt,nb,ns,nlam)
+;param=dblarr(nl,nt,nb,ns,4)
+
+conv=1/2.99792e-13*lam^2.
+
+
+for il=0,nl-1 do begin
+for it=0,nt-1 do begin
+for ibb=0,nb-1 do begin
+for is=0,ns-1 do begin
+tau=(lam0[il]/lam)^beta[ibb]
+bank[il,it,ibb,is,*]=siz[is]*planck(alam,temp[it])/!pi*conv*(1- exp(-tau))
+;param[il,it,ibb,is,*]=[lam0[il],temp[it],beta[ibb],siz[is]]
+endfor
+endfor
+endfor
+endfor
+
+
+end
diff --git a/Code/script_idl_mv/higalfit/sedbank_thin_large.pro b/Code/script_idl_mv/higalfit/sedbank_thin_large.pro
new file mode 100644
index 0000000000000000000000000000000000000000..39e9f765f4f7906baab2ff03cd3d376dfbd47c98
--- /dev/null
+++ b/Code/script_idl_mv/higalfit/sedbank_thin_large.pro
@@ -0,0 +1,56 @@
+pro sedbank_thin_large,lambda,mmin,mmax,nm,tmin,tmax,nt,bmin,bmax,nb,kref,lambdaref,dist,mass,temp,beta,bank,mlin=mlin,m2=m2
+;Written by Davide Elia, 2012-2015
+
+Rgd = 1. ; il fattore 100 � gi� contenuto in k0
+mmin=float(mmin)
+tmin=float(tmin)
+bmin=float(bmin)
+
+if nm gt 1 then begin
+  if keyword_set(m2) then nm=nm/2
+    mstep=(mmax^0.2-mmin^0.2)/(nm-1)
+    amass=mmin^0.2+mstep*findgen(nm)
+    mass=amass^5.
+  if keyword_set(mlin) then begin
+    mstep=(mmax-mmin)/(nm-1)
+    mass=mmin+mstep*findgen(nm)
+  endif
+endif else mass=mmin
+
+massg=mass*1.98892e33 ;g
+distcm=dist*3.09d18
+
+if nt gt 1 then begin
+  tstep=(tmax-tmin)/(nt-1)
+  temp=tmin+tstep*findgen(nt)
+endif else temp=tmin
+
+if nb gt 1 then begin
+  bstep=(bmax-bmin)/(nb-1)
+  beta=bmin+bstep*findgen(nb)
+endif else beta=bmax
+
+
+lam=lambda  ; um
+alam=lambda*1.d4    ; lambda in Angstrom
+
+nlam=n_elements(lam)
+bank=dblarr(nm,nt,nb,nlam)
+;param=dblarr(nl,nt,nb,ns,4)
+
+conv=1/2.99792e-13*lam^2.
+
+
+for im=0,nm-1 do begin
+for it=0,nt-1 do begin
+for ibb=0,nb-1 do begin
+
+bank[im,it,ibb,*]=massg[im]*kref/distcm^2*planck(alam,temp[it])/!pi*conv*(lambdaref/lam)^beta[ibb]
+
+
+endfor
+endfor
+endfor
+
+
+end
diff --git a/Code/script_idl_mv/higalfit/sedfitgrid_engine_thick_vialactea.pro b/Code/script_idl_mv/higalfit/sedfitgrid_engine_thick_vialactea.pro
new file mode 100644
index 0000000000000000000000000000000000000000..4a2f5ffdb85d341859028efb70f25fa2a0235ff9
--- /dev/null
+++ b/Code/script_idl_mv/higalfit/sedfitgrid_engine_thick_vialactea.pro
@@ -0,0 +1,234 @@
+pro sedfitgrid_engine_thick_vialactea,lambda,flux,sizz,temprange,betarange,l0range,sfactrange,dist,sref,lambdagb,flussogb,mass,ftemp,fbeta,fl0,sizesec,lum,dmass,dtemp,dlam0,errorbars=errorbars,ulimit=ulimit,silent=silent,printfile=printfile
+; version for VIALACTEA, it doesn't use a settings file, but accepts the initial ranges as input variables
+;Written by Davide Elia, 2012-2015
+
+; lambda in um, size in arcsec, flux in Jy, size in arcsec, dist in pc
+; temprange,betarange,l0range,sfactrange: ; 3-element vectors, cotaining min, max and number of steps
+; sref: 2-element vector, containing opacity at lambda-ref (cm^2_g^-1), and lambda_ref itself (micron).
+
+stemp=temprange
+sbeta=betarange
+sl0=l0range
+sfact=sfactrange
+
+if temprange[2]  lt 5 then deg1=0 else deg1=1
+if betarange[2]  lt 5 then deg2=0 else deg2=1
+if sfactrange[2] lt 5 then deg3=0 else deg3=1
+if l0range[2]    lt 5 then deg4=0 else deg4=1
+deg=deg1+deg2+deg3+deg4
+
+pccm=3.09d18
+kref=sref[0]
+lambdaref=sref[1]
+
+lambdagb=5.+findgen(1995)
+
+conv=1/2.99792e-13*lambdagb^2.
+
+ssize=sfact*sizz
+
+m1=0
+m2=1
+t1=0
+t2=1
+b1=0
+b2=1
+s1=0
+s2=1
+npos=3
+
+chi0=1e8
+rchi=1e8
+count=0
+
+lmask=fltarr(n_elements(lambda))
+wf=where(flux,c)
+lmask[wf]=1.
+
+if keyword_set(ulimit) gt 0 then begin
+  wu=where(ulimit ne 0,cu)
+  if cu ne 0 then lmask[wu]=0.
+endif
+
+if keyword_set(silent) eq 0 then begin
+print,''
+print,'------------------------------------------------------'
+print,'flux= ',flux
+endif
+
+
+sedbank_thick_large,lambda,sl0[m1],sl0[m2],l0range[2],stemp[t1],stemp[t2],temprange[2],sbeta[b1],sbeta[b2],betarange[2],ssize[s1],ssize[s2],sfact[2],ssl0,sstemp,ssbeta,sssize,bank
+sb=size(bank)
+cflux=fltarr(sb[1]*sb[2]*sb[3]*sb[4],sb[5])
+mask=fltarr(sb[1]*sb[2]*sb[3]*sb[4],sb[5])
+if keyword_set(errorbars) ne 0 then eflux=fltarr(sb[1]*sb[2]*sb[3]*sb[4],sb[5])
+
+for tt=0,sb[5]-1 do begin
+  cflux[*,tt]=flux[tt]
+  mask[*,tt]=lmask[tt]
+  if keyword_set(errorbars) ne 0 then eflux[*,tt]=errorbars[tt]
+endfor
+
+
+while (rchi gt 1.01 and count le 10) do begin
+
+sedbank_thick_large,lambda,sl0[m1],sl0[m2],l0range[2],stemp[t1],stemp[t2],temprange[2],sbeta[b1],sbeta[b2],betarange[2],ssize[s1],ssize[s2],sfact[2],ssl0,sstemp,ssbeta,sssize,bank
+sssize=206265.*sqrt(sssize*4./!pi)
+
+sl0=ssl0
+sbeta=ssbeta
+stemp=sstemp
+ssize=sssize
+sb=size(bank)
+
+ctemp=[250. , 100 ,  50 ,  40 , 30  , 20  , 19  , 18  , 17  , 16  , 15  , 14  , 13  , 12  , 11  , 10  ,  9  ,  8  ,  7   ,  6   ,   5   ]
+c70 = [1.005,0.989,0.982,0.992,1.034,1.224,1.269,1.325,1.396,1.488,1.607,1.768,1.992,2.317,2.816,3.645,5.175,8.497,17.815,58.391,456.837] 
+c100= [1.023,1.007,0.985,0.980,0.982,1.036,1.051,1.069,1.093,1.123,1.162,1.213,1.282,1.377,1.512,1.711,2.024,2.554, 3.552, 5.774, 12.259] 
+c160= [1.062,1.042,1.010,0.995,0.976,0.963,0.964,0.967,0.972,0.979,0.990,1.005,1.028,1.061,1.110,1.184,1.300,1.491, 1.833, 2.528,  4.278]
+
+w70=where(lambda eq 70.,cw70)
+if cw70 ne 0 then begin
+ic70=interpol(c70,ctemp,stemp,/quadratic)
+corr70=fltarr(sb[1],sb[2],sb[3],sb[4])
+for z=0,sb[2]-1 do begin
+corr70[*,z,*,*]=ic70[z]
+endfor
+bank[*,*,*,*,w70]=bank[*,*,*,*,w70]*corr70
+endif
+
+w160=where(lambda eq 160.,cw160)
+if cw160 ne 0 then begin
+ic160=interpol(c160,ctemp,stemp,/quadratic)
+corr160=fltarr(sb[1],sb[2],sb[3],sb[4])
+for z=0,sb[2]-1 do begin
+corr160[*,z,*,*]=ic160[z]
+endfor
+bank[*,*,*,*,w160]=bank[*,*,*,*,w160]*corr160
+endif
+
+
+
+rbank=reform(bank,sb[1]*sb[2]*sb[3]*sb[4],sb[5])
+
+if keyword_set(errorbars) ne 0 then begin
+  chi=total(((rbank*mask-cflux)/(eflux+1e-7))^2,2)
+endif else begin
+  chi=total((rbank*mask-cflux)^2/(rbank*mask+1e-7),2)
+endelse
+redchi=chi/max([1,(n_elements(lambda)-deg-1 )])
+
+if keyword_set(ulimit) gt 0 then begin
+   if cu gt 1 then mdiff=sum((rbank[*,wu]-cflux[*,wu])/abs(rbank[*,wu]-cflux[*,wu]),1) else mdiff=(rbank[*,wu[0]]-cflux[*,wu[0]])/abs(rbank[*,wu[0]]-cflux[*,wu[0]])
+   wf=where(mdiff lt cu,cf)
+   mmm=min(chi[wf],wmm)
+   wm=wf[wmm]
+endif else mmm=min(chi,wm)
+remchi=mmm/max([1,(n_elements(lambda)-deg-1 )])
+
+wm3 = ARRAY_INDICES(bank,wm)
+wm3 = ARRAY_INDICES(bank,wm)
+m1=max([0,wm3[0]-npos])
+m2=min([sb[1]-1,wm3[0]+npos])
+t1=max([0,wm3[1]-npos])
+t2=min([sb[2]-1,wm3[1]+npos])
+b1=max([0,wm3[2]-npos])
+b2=min([sb[3]-1,wm3[2]+npos])
+s1=max([0,wm3[3]-npos])
+s2=min([sb[4]-1,wm3[3]+npos])
+
+fl0=sl0[wm3[0]]
+ftemp=stemp[wm3[1]]
+fbeta=sbeta[wm3[2]]
+fsize=(ssize[wm3[3]]/206265.)^2*!pi/4.
+if KEYWORD_SET(silent) eq 0 then begin
+print,fl0,ftemp,fbeta,206265.*sqrt(fsize*4./!pi),mmm
+print,sl0[m1],sl0[m2],stemp[t1],stemp[t2],sbeta[b1],sbeta[b2],ssize[s1],ssize[s2]
+endif
+
+cchi=total(((rbank[wm,wf]*mask[wm,wf]-cflux[wm,wf])/(rbank[wm,wf]*mask[wm,wf]))^2,2)/c ;stima "personalizzata del chi2 per confrontarlo con l'altro metodo
+rchi=chi0/mmm
+count=count+1
+if count eq 1 then rchi=1e8
+chi0=mmm
+endwhile
+
+inc=1
+ccc=0
+while ccc eq 0  do begin
+wwc=where(redchi lt remchi+inc,ccc)
+inc=inc+1
+endwhile
+
+wm4 = ARRAY_INDICES(bank,wwc)
+wm4 = ARRAY_INDICES(bank,wwc)
+
+dtemp1=abs(ftemp-max(stemp[wm4[1,*]]))
+dtemp2=abs(ftemp-min(stemp[wm4[1,*]]))
+dlam01=abs(fl0-max(sl0[wm4[0,*]]))
+dlam02=abs(fl0-min(sl0[wm4[0,*]]))
+dbeta1=abs(fbeta-max(sbeta[wm4[2,*]]))
+dbeta2=abs(fbeta-min(sbeta[wm4[2,*]]))
+dsize1=abs(fsize-max((ssize[wm4[3,*]])/206265.)^2*!pi/4.)
+dsize2=abs(fsize-min((ssize[wm4[3,*]])/206265.)^2*!pi/4.)
+mll=[min(sl0[wm4[0,*]]),max(sl0[wm4[0,*]])]
+mss=([min(ssize[wm4[0,*]]),max(ssize[wm4[0,*]])]/206265.)^2*!pi/4.
+mbb=[min(sbeta[wm4[0,*]]),max(sbeta[wm4[0,*]])]
+
+dmm=dblarr(8)
+for k0=0,1 do begin
+for k1=0,1 do begin
+for k2=0,1 do begin
+dmm[4*k0+2*k1+k2]=(dist*pccm)^2.*mss[k1]/kref*(mll[k0]/lambdaref)^mbb[k2]
+;print,dmm[4*k0+2*k1+k2]
+endfor
+endfor
+endfor
+dmass1=abs(ftemp-max(sbeta[wm4[2,*]]))
+dmass2=abs(ftemp-min(sbeta[wm4[2,*]]))
+
+
+dlam0=max([dlam01,dlam02])
+dmass=max([dmass1,dmass2])
+dtemp=max([dtemp1,dtemp2])
+
+if KEYWORD_SET(silent) eq 0 then begin
+print,'.....'
+;print,param[wm3[0],wm3[1],wm3[2],wm3[3],*]
+endif
+
+;window,1
+;plot_oo,lambda[wf],flux[wf],psym=4,yrange=[1e-2,1.3*max(flux[wf])]
+
+;xyouts,0.2,0.9,strtrim(id[i],1),/normal
+
+mass=(dist*pccm)^2.*fsize/kref*(fl0/lambdaref)^fbeta
+mass=mass/1.98892e33
+dmass1=abs(mass-max(dmm)/1.98892e33)
+dmass2=abs(mass-min(dmm)/1.98892e33)
+dmass=max([dmass1,dmass2])
+sizesec=206265.*sqrt(fsize*4./!pi)
+if KEYWORD_SET(silent) eq 0 then begin
+  print,'FIT results:  Mass: ',mass,'     Temp: ',ftemp,'     Beta: ',fbeta,'     lambda_0:',fl0,'     size:',sizesec,'     iterations:',count
+endif
+tau=(fl0/lambdagb)^fbeta
+flussogb=fsize*planck(lambdagb*1e4,ftemp)/!pi*conv*(1- exp(-tau))
+
+if keyword_set(printfile) then begin
+  openw,lun,printfile,/get_lun
+  for uu=0,n_elements(lambdagb)-1 do begin
+    str=string([lambdagb[uu],flussogb[uu]])
+    printf,lun,strjoin(strtrim(str,2),','),format='(A)'
+  endfor
+  openw,lun2,'par_'+printfile,/get_lun
+  cmm=‘,’
+  printf,lun2,cmm,mass,cmm,dmass,cmm,ftemp,cmm,dtemp,cmm,fbeta,cmm,fl0,cmm,dlam0,cmm,sizesec,cmm,lum,format='(f13.2,A1,f13.2,A1,f7.2,A1,f7.2,A1,F5.2,A1,F7.2,A1,F7.2,A1,f6.1,A1,f13.2)'
+  free_lun,lun
+  free_lun,lun2
+endif
+
+qd=where(flussogb gt 1e-8)
+;oplot,lambdagb[qd],flussogb[qd]
+lum=lbol(lambdagb[qd],flussogb[qd],dist)
+
+
+end
diff --git a/Code/script_idl_mv/higalfit/sedfitgrid_engine_thin_vialactea.pro b/Code/script_idl_mv/higalfit/sedfitgrid_engine_thin_vialactea.pro
new file mode 100644
index 0000000000000000000000000000000000000000..b1bc831a73ddc8de3b838a79f749a04f970f71d6
--- /dev/null
+++ b/Code/script_idl_mv/higalfit/sedfitgrid_engine_thin_vialactea.pro
@@ -0,0 +1,212 @@
+pro sedfitgrid_engine_thin_vialactea,lambda,flux,massrange,temprange,betarange,dist,sref,lambdagb,flussogb,fmass,ftemp,fbeta,lum,dmass,dtemp,errorbars=errorbars,ulimit=ulimit,silent=silent,printfile=printfile
+; version for VIALACTEA, it doesn't use a settings file, but accepts the initial ranges as input variables
+;Written by Davide Elia, 2012-2015
+
+; lambda in um, size in arcsec, flux in Jy, size in arcsec, dist in pc
+; massrange,temprange,betarange: ; 3-element vectors, cotaining min, max and number of steps
+; sref: 2-element vector, containing opacity at lambda-ref (cm^2_g^-1), and lambda_ref itself (micron).
+
+
+dvirt=1000.
+
+
+smass=massrange  
+stemp=temprange
+sbeta=betarange
+
+if massrange[2] lt 5 then deg1=0 else deg1=1
+if temprange[2] lt 5 then deg2=0 else deg2=1
+if betarange[2] lt 5 then deg3=0 else deg3=1
+deg=deg1+deg2+deg3
+
+pccm=3.09d18
+kref=sref[0]
+lambdaref=sref[1]
+
+
+lambdagb=5.+findgen(1995)
+
+conv=1/2.99792e-13*lambdagb^2.
+
+
+distcm=dist*pccm
+
+m1=0
+m2=1
+t1=0
+t2=1
+b1=0
+b2=1
+
+npos=3
+
+chi0=1e8
+rchi=1e8
+count=0
+
+lmask=fltarr(n_elements(lambda))
+wf=where(flux,c)
+lmask[wf]=1.
+
+
+if keyword_set(ulimit) gt 0 then begin
+  wu=where(ulimit ne 0,cu)
+  if cu ne 0 then lmask[wu]=0.
+endif
+
+if keyword_set(silent) eq 0 then begin
+print,''
+print,'------------------------------------------------------'
+print,'flux= ',flux
+endif
+
+maxmass=smass[m2]
+sedbank_thin_large,lambda,smass[m1],smass[m2],massrange[2],stemp[t1],stemp[t2],temprange[2],sbeta[b1],sbeta[b2],betarange[2],kref,lambdaref,dvirt,ssmass,sstemp,ssbeta,bank
+sb=size(bank)
+cflux=fltarr(sb[1]*sb[2]*sb[3],sb[4])
+mask=fltarr(sb[1]*sb[2]*sb[3],sb[4])
+if keyword_set(errorbars) ne 0 then eflux=fltarr(sb[1]*sb[2]*sb[3],sb[4])
+
+for tt=0,sb[4]-1 do begin
+  cflux[*,tt]=flux[tt]
+  mask[*,tt]=lmask[tt]
+  if keyword_set(errorbars) ne 0 then eflux[*,tt]=errorbars[tt]
+endfor
+
+while (rchi gt 1.01 and count le 10) do begin
+REFIT:
+sedbank_thin_large,lambda,smass[m1],smass[m2],massrange[2],stemp[t1],stemp[t2],temprange[2],sbeta[b1],sbeta[b2],betarange[2],kref,lambdaref,dvirt,ssmass,sstemp,ssbeta,bank
+smass=ssmass
+sbeta=ssbeta
+stemp=sstemp
+sb=size(bank)
+
+ctemp=[250. , 100 ,  50 ,  40 , 30  , 20  , 19  , 18  , 17  , 16  , 15  , 14  , 13  , 12  , 11  , 10  ,  9  ,  8  ,  7   ,  6   ,   5   ]
+c70 = [1.005,0.989,0.982,0.992,1.034,1.224,1.269,1.325,1.396,1.488,1.607,1.768,1.992,2.317,2.816,3.645,5.175,8.497,17.815,58.391,456.837] 
+c100= [1.023,1.007,0.985,0.980,0.982,1.036,1.051,1.069,1.093,1.123,1.162,1.213,1.282,1.377,1.512,1.711,2.024,2.554, 3.552, 5.774, 12.259] 
+c160= [1.062,1.042,1.010,0.995,0.976,0.963,0.964,0.967,0.972,0.979,0.990,1.005,1.028,1.061,1.110,1.184,1.300,1.491, 1.833, 2.528,  4.278]
+
+w70=where(lambda eq 70.,cw70)
+if cw70 ne 0 then begin
+ic70=interpol(c70,ctemp,stemp,/quadratic)
+corr70=fltarr(sb[1],sb[2],sb[3])
+for z=0,sb[2]-1 do begin
+corr70[*,z,*]=ic70[z]
+endfor
+bank[*,*,*,w70]=bank[*,*,*,w70]*corr70
+endif
+
+w160=where(lambda eq 160.,cw160)
+if cw160 ne 0 then begin
+ic160=interpol(c160,ctemp,stemp,/quadratic)
+corr160=fltarr(sb[1],sb[2],sb[3])
+for z=0,sb[2]-1 do begin
+corr160[*,z,*]=ic160[z]
+endfor
+bank[*,*,*,w160]=bank[*,*,*,w160]*corr160
+endif
+
+
+rbank=reform(bank,sb[1]*sb[2]*sb[3],sb[4])
+
+if keyword_set(errorbars) ne 0 then begin
+  chi=total(((rbank*mask-cflux)/(eflux+1e-7))^2,2)
+endif else begin
+  chi=total((rbank*mask-cflux)^2/(rbank*mask+1e-7),2)
+endelse
+ redchi=chi/max([1,(n_elements(lambda)-deg-1 )])
+
+if keyword_set(ulimit) gt 0 then begin
+   if cu gt 1 then mdiff=sum((rbank[*,wu]-cflux[*,wu])/abs(rbank[*,wu]-cflux[*,wu]),1) else mdiff=(rbank[*,wu[0]]-cflux[*,wu[0]])/abs(rbank[*,wu[0]]-cflux[*,wu[0]])
+   wf=where(mdiff lt cu,cf)
+   mmm=min(chi[wf],wmm)
+   wm=wf[wmm]
+endif else mmm=min(chi,wm)
+remchi=mmm/max([1,(n_elements(lambda)-deg-1 )])   
+   
+wm3 = ARRAY_INDICES(bank,wm)
+m1=max([0,wm3[0]-npos])
+m2=min([sb[1]-1,wm3[0]+npos])
+t1=max([0,wm3[1]-npos])
+t2=min([sb[2]-1,wm3[1]+npos])
+b1=max([0,wm3[2]-npos])
+b2=min([sb[3]-1,wm3[2]+npos])
+
+fmass=smass[wm3[0]]
+ftemp=stemp[wm3[1]]
+fbeta=sbeta[wm3[2]]
+
+if abs(fmass-maxmass)/fmass lt 1e-6 then begin
+  print,'azz',maxmass,fmass
+  maxmass=2*maxmass
+  smass[m1]=4*smass[m1]
+  smass[m2]=maxmass
+  goto,REFIT
+endif
+
+if KEYWORD_SET(silent) eq 0 then begin
+print,fmass,ftemp,fbeta,mmm
+print,smass[m1],smass[m2],stemp[t1],stemp[t2],sbeta[b1],sbeta[b2]
+endif
+
+cchi=total(((rbank[wm,wf]*mask[wm,wf]-cflux[wm,wf])/(rbank[wm,wf]*mask[wm,wf]))^2,2)/c ;stima "personalizzata del chi2 per confrontarlo con l'altro metodo
+rchi=chi0/mmm
+count=count+1
+if count eq 1 then rchi=1e8
+chi0=mmm
+endwhile
+fmass=fmass*(dist/dvirt)^2
+
+inc=1
+ccc=0
+while ccc eq 0 do begin
+wwc=where(redchi lt remchi+inc,ccc)
+inc=inc+1
+endwhile
+
+wm4 = ARRAY_INDICES(bank,wwc)
+wm4 = ARRAY_INDICES(bank,wwc)
+
+dmass1=abs(fmass-max(smass[wm4[0,*]]))
+dmass2=abs(fmass-min(smass[wm4[0,*]]))
+dtemp1=abs(ftemp-max(stemp[wm4[1,*]]))
+dtemp2=abs(ftemp-min(stemp[wm4[1,*]]))
+
+dmass=max([dmass1,dmass2])
+dtemp=max([dtemp1,dtemp2])
+
+if KEYWORD_SET(silent) eq 0 then begin
+print,'.....'
+;print,param[wm3[0],wm3[1],wm3[2],wm3[3],*]
+
+;window,1
+;plot_oo,lambda[wf],flux[wf],psym=4,yrange=[1e-2,1.3*max(flux[wf])]
+
+;xyouts,0.2,0.9,strtrim(id[i],1),/normal
+
+print,'FIT results:  Mass: ',fmass,'     Temp: ',ftemp,'     Beta: ',fbeta,'     iterations:',count
+;print,dmass1,dmass2,dtemp1,dtemp2
+endif
+
+fmassg=fmass*1.98892e33
+flussogb=fmassg*kref/distcm^2*planck(1e4*lambdagb,ftemp)/!pi*conv*(lambdaref/lambdagb)^fbeta
+
+if keyword_set(printfile) then begin
+  openw,lun,printfile,/get_lun
+  for uu=0,n_elements(lambdagb)-1 do begin
+    str=string([lambdagb[uu],flussogb[uu]])
+    printf,lun,strjoin(strtrim(str,2),','),format='(A)'
+  endfor
+  openw,lun2,'par_'+printfile,/get_lun
+  cmm=‘,’
+  printf,lun2,fmass,cmm,dmass,cmm,ftemp,cmm,dtemp,cmm,fbeta,cmm,lum,format='(f13.2,A1,f13.2,A1,f7.2,A1,f7.2,A1,F5.2,A1,f13.2)'
+  free_lun,lun
+  free_lun,lun2
+endif
+
+qd=where(flussogb gt 1e-8)
+;oplot,lambdagb[qd],flussogb[qd]
+lum=lbol(lambdagb[qd],flussogb[qd],dist)
+
+
+end
diff --git a/Code/script_idl_mv/modelsed_fit.pro b/Code/script_idl_mv/modelsed_fit.pro
new file mode 100644
index 0000000000000000000000000000000000000000..7fd03b87b2512247dfd4962ade858064fe70b36a
--- /dev/null
+++ b/Code/script_idl_mv/modelsed_fit.pro
@@ -0,0 +1,101 @@
+function modelsed_fit,wavelength,flux,fluxflag,w_mod,fmod,dmin,dmax,nstep,sed_weights=sed_weights
+
+;print,sed_weights
+par=replicate({d:0.d0, chi2:0.d0},1)
+
+; convert model flux in Jy and interpolate at observations wavelength
+d_ref=1000.   ; reference distance for SED models is 1 kpc
+; exclude NaNs
+
+qnotnan=where(finite(fmod) eq 1 or fmod gt 0)
+wavelength1=wavelength(qnotnan)
+flux1=flux(qnotnan)
+fluxflag1=fluxflag(qnotnan)
+fmod=fmod(qnotnan)
+
+
+;establishes weights by assigning equal weights to mid-IR, far-IR and sub-mm
+flag=fix(fluxflag1)
+ul_flag=intarr(n_elements(flag))
+ul_flag(*)=0
+ll_flag=intarr(n_elements(flag))
+ll_flag(*)=0
+dyd=fltarr(n_elements(flag))
+;w1=sqrt(3./7.)
+;w2=sqrt(3./7.)
+;w3=sqrt(3./7.)
+
+if (keyword_set(sed_weights) eq 0) then sed_weights=[3./7.,1./7.,3./7.] 
+renorm=total(sed_weights)
+w1=sqrt(sed_weights(0)/renorm)
+w2=sqrt(sed_weights(1)/renorm)
+w3=sqrt(sed_weights(2)/renorm)
+qmir=where(wavelength1 lt 25,nqmir)
+qfir=where(wavelength1 ge 25 and wavelength1 le 250,nqfir)
+qmm=where(wavelength1 gt 250,nqmm)
+if (nqmir gt 0) then begin
+  q1=where(flag(qmir) eq 1,nq1,complement=q1neg,ncomplement=nq1neg)
+  if (nq1 gt 0) then dyd(qmir(q1))=sqrt(nq1)/w1
+  if (nq1neg gt 0) then begin
+    dyd(qmir(q1neg))=9999.  ;i.e. it's an upper/lower limit
+    ul_flag(qmir(q1neg))=1
+  endif
+endif
+if (nqfir gt 0) then begin
+  q2=where(flag(qfir) eq 1,nq2,complement=q2neg,ncomplement=nq2neg)
+  if (nq2 gt 0) then dyd(qfir(q2))=sqrt(nq2)/w2
+  if (nq2neg gt 0) then begin
+    dyd(qfir(q2neg))=9999.   ;i.e. it's an upper limit
+    ul_flag(qfir(q2neg))=1
+  endif
+endif
+if (nqmm gt 0) then begin
+  q3=where(flag(qmm) eq 1,nq3,complement=q3neg,ncomplement=nq3neg)
+  if (nq3 gt 0) then dyd(qmm(q3))=sqrt(nq3)/w3
+  if (nq3neg gt 0) then begin
+    dyd(qmm(q3neg))=9999.   ;i.e. it's an upper limit
+    ul_flag(qmm(q3neg))=1
+  endif
+endif
+
+; now dyd has to be interpreted as a relative uncertainty otherwise the same assigned weight will have more or less importance depending on the level of flux and flux difference between model and observation. I then multiply the "weight" by the flux value
+dyd=dyd*flux
+
+;nstep=10
+;dist_arr=10.^(alog10(dmin)+findgen(nstep)*(alog10(dmax/dmin)/nstep))  
+dist_arr=dmin+findgen(nstep)*(dmax-dmin)/nstep
+
+upplim_good=intarr(nstep)
+chi2=fltarr(nstep)
+invalid_chi2=-999
+chi2(*)=invalid_chi2
+q_ul=where(ul_flag eq 1,nq_ul)
+
+if (nq_ul ge 1) then begin
+  for i=0,nstep-1 do begin
+    if ( total(flux1(q_ul)-fmod(q_ul)*((d_ref/dist_arr(i))^2.)) eq total(abs(flux1(q_ul)-fmod(q_ul)*((d_ref/dist_arr(i))^2.))) ) then upplim_good(i)=1 ; if the sum of the source-model fluxes is equal to its absolute value then it means that no upper limit is violated 
+  endfor
+  qgoodul=where(upplim_good eq 1,nqgoodul)
+endif else begin
+  qgoodul=indgen(n_elements(upplim_good))
+  nqgoodul=n_elements(upplim_good)
+endelse
+
+if (nqgoodul gt 0) then begin
+  for j=0,nqgoodul-1 do begin
+;    chi2tmp=total((((fmod*((d_ref/dist_arr(qgoodul(j)))^2.))-flux1)^2)/(dyd^2))
+    chi2tmp=total((((fmod*((d_ref/dist_arr(qgoodul(j)))^2.))-flux1)^2))
+    chi2(qgoodul(j))=chi2tmp
+  endfor
+  qvalid=where(chi2 ne invalid_chi2)
+  qmin=where(chi2(qvalid) eq min(chi2(qvalid)))
+  par.d=dist_arr(qvalid(qmin(0)))  ; put qmin(0) because I found there may be two occasions with the minimum chi2
+  par.chi2=chi2(qvalid(qmin(0)))
+endif else begin
+  par.d=-999
+  par.chi2=invalid_chi2
+endelse
+
+return,par
+
+end
diff --git a/Code/script_idl_mv/modelsed_fit_v2.pro b/Code/script_idl_mv/modelsed_fit_v2.pro
new file mode 100644
index 0000000000000000000000000000000000000000..739790006f4d1c481f2a482e28abe97f734289cd
--- /dev/null
+++ b/Code/script_idl_mv/modelsed_fit_v2.pro
@@ -0,0 +1,111 @@
+function modelsed_fit_v2,wavelength,flux,dflux,fluxflag,w_mod,fmod,dmin,dmax,nstep,sed_weights=sed_weights
+
+;print,sed_weights
+par=replicate({d:0.d0, chi2:0.d0},1)
+
+; convert model flux in Jy and interpolate at observations wavelength
+d_ref=1000.   ; reference distance for SED models is 1 kpc
+; exclude NaNs
+
+qnotnan=where(finite(fmod) eq 1 or fmod gt 0)
+wavelength1=wavelength(qnotnan)
+flux1=flux(qnotnan)
+dflux1=dflux(qnotnan)
+fluxflag1=fluxflag(qnotnan)
+fmod=fmod(qnotnan)
+
+
+;establishes weights by assigning equal weights to mid-IR, far-IR and sub-mm
+flag=fix(fluxflag1)
+ul_flag=intarr(n_elements(flag))
+ul_flag(*)=0
+ll_flag=intarr(n_elements(flag))
+ll_flag(*)=0
+dyd=fltarr(n_elements(flag))
+;w1=sqrt(3./7.)
+;w2=sqrt(3./7.)
+;w3=sqrt(3./7.)
+
+if (keyword_set(sed_weights) eq 0) then sed_weights=[3./7.,1./7.,3./7.] 
+renorm=total(sed_weights)
+w1=sqrt(sed_weights(0)/renorm)
+w2=sqrt(sed_weights(1)/renorm)
+w3=sqrt(sed_weights(2)/renorm)
+qmir=where(wavelength1 lt 25,nqmir)
+qfir=where(wavelength1 ge 25 and wavelength1 le 250,nqfir)
+qmm=where(wavelength1 gt 250,nqmm)
+if (nqmir gt 0) then begin
+  q1=where(flag(qmir) eq 1,nq1,complement=q1neg,ncomplement=nq1neg)
+  if (nq1 gt 0) then dyd(qmir(q1))=sqrt(nq1)/w1
+  if (nq1neg gt 0) then begin
+    dyd(qmir(q1neg))=9999.  ;i.e. it's an upper/lower limit
+    ul_flag(qmir(q1neg))=1
+  endif
+endif
+if (nqfir gt 0) then begin
+  q2=where(flag(qfir) eq 1,nq2,complement=q2neg,ncomplement=nq2neg)
+  if (nq2 gt 0) then dyd(qfir(q2))=sqrt(nq2)/w2
+  if (nq2neg gt 0) then begin
+    dyd(qfir(q2neg))=9999.   ;i.e. it's an upper limit
+    ul_flag(qfir(q2neg))=1
+  endif
+endif
+if (nqmm gt 0) then begin
+  q3=where(flag(qmm) eq 1,nq3,complement=q3neg,ncomplement=nq3neg)
+  if (nq3 gt 0) then dyd(qmm(q3))=sqrt(nq3)/w3
+  if (nq3neg gt 0) then begin
+    dyd(qmm(q3neg))=9999.   ;i.e. it's an upper limit
+    ul_flag(qmm(q3neg))=1
+  endif
+endif
+
+; now normalize dyd to the minimum value
+; so that we artificially increase the uncertainties of the fluxes where the SED-based weight is lower
+dyd=dyd/min(dyd)
+;print,'DF/F= ',dflux1/flux1
+dyd=dyd*dflux1/flux1
+
+; now dyd has to be interpreted as a relative uncertainty otherwise the same assigned weight will have more or less importance depending on the level of flux and flux difference between model and observation. I then multiply the "weight" by the flux value
+dyd=dyd*flux1
+
+;nstep=10
+;dist_arr=10.^(alog10(dmin)+findgen(nstep)*(alog10(dmax/dmin)/nstep))  
+dist_arr=dmin+findgen(nstep)*(dmax-dmin)/nstep
+
+upplim_good=intarr(nstep)
+chi2=fltarr(nstep)
+invalid_chi2=-999
+chi2(*)=invalid_chi2
+q_ul=where(ul_flag eq 1,nq_ul)
+
+if (nq_ul ge 1) then begin
+  for i=0,nstep-1 do begin
+    if ( total(flux1(q_ul)-fmod(q_ul)*((d_ref/dist_arr(i))^2.)) eq total(abs(flux1(q_ul)-fmod(q_ul)*((d_ref/dist_arr(i))^2.))) ) then upplim_good(i)=1 ; if the sum of the source-model fluxes is equal to its absolute value then it means that no upper limit is violated 
+  endfor
+  qgoodul=where(upplim_good eq 1,nqgoodul)
+endif else begin
+  qgoodul=indgen(n_elements(upplim_good))
+  nqgoodul=n_elements(upplim_good)
+endelse
+
+if (nqgoodul gt 0) then begin
+  for j=0,nqgoodul-1 do begin
+    chi2tmp=total((((fmod*((d_ref/dist_arr(qgoodul(j)))^2.))-flux1)^2)/(dyd^2))
+
+;stop
+
+;    chi2tmp=total((((fmod*((d_ref/dist_arr(qgoodul(j)))^2.))-flux1)^2))
+    chi2(qgoodul(j))=chi2tmp
+  endfor
+  qvalid=where(chi2 ne invalid_chi2)
+  qmin=where(chi2(qvalid) eq min(chi2(qvalid)))
+  par.d=dist_arr(qvalid(qmin(0)))  ; put qmin(0) because I found there may be two occasions with the minimum chi2
+  par.chi2=chi2(qvalid(qmin(0)))
+endif else begin
+  par.d=-999
+  par.chi2=invalid_chi2
+endelse
+
+return,par
+
+end
diff --git a/Code/script_idl_mv/pad0_num.pro b/Code/script_idl_mv/pad0_num.pro
new file mode 100755
index 0000000000000000000000000000000000000000..8b36f91430ef9cef87b3b9943a7c0b8cd9f2f585
--- /dev/null
+++ b/Code/script_idl_mv/pad0_num.pro
@@ -0,0 +1,8 @@
+function pad0_num,num
+; num must be a string that contains a number. If the number ends with a floating point without a trailing 0 then this routine will add the 0. This is needed for a bug of the VIALACTEA TAP query service that is screwed up by numbers ending with floating points
+
+q=strpos(num,'.')
+if (q eq strlen(num)-1) then num0=num+'0' else num0=num
+
+return,num0
+end
diff --git a/Code/script_idl_mv/remove_char.pro b/Code/script_idl_mv/remove_char.pro
new file mode 100755
index 0000000000000000000000000000000000000000..293b88e81f3b228e4f2672e3be2420e940ca7553
--- /dev/null
+++ b/Code/script_idl_mv/remove_char.pro
@@ -0,0 +1,12 @@
+function remove_char,in_str,ch
+
+q=strpos(in_str,ch)
+if (q eq -1) then begin
+	print,'character ',ch,' not found in string ',in_str
+	out_str=in_str
+endif else begin
+	out_str=strmid(in_str,0,q)+strmid(in_str,q+1,strlen(in_str)-1)
+endelse
+
+return,out_str
+end
diff --git a/Code/script_idl_mv/rename_tags.pro b/Code/script_idl_mv/rename_tags.pro
new file mode 100644
index 0000000000000000000000000000000000000000..0bce5172a921f81e4117f454d005b14399a202a8
--- /dev/null
+++ b/Code/script_idl_mv/rename_tags.pro
@@ -0,0 +1,100 @@
+;+
+; NAME:
+;  RENAME_TAGS()
+;
+; PURPOSE:
+;  Rename tags in a structure
+;
+; CALLING SEQUENCE:
+;  newstruct = rename_tags(struct, oldtagnames, newtagnames)
+;
+; INPUTS:
+;  struct: The original structure. May be an array.
+;  oldtagnames: names of tags to change
+;  newtagnames: new names for tags
+;
+; OUTPUTS:
+;  A new structure with tags renamed.
+;
+; OPTIONAL INPUTS:
+;  verbose: set to greater than 0 for verbose mode.  Each changed tag will
+;    be printed.
+;
+; MODIFICATION HISTORY:
+;  Early 2006: Erin Sheldon, NYU
+;-
+;
+;
+;
+;  Copyright (C) 2005  Erin Sheldon.  erin dot sheldon at gmail dot com
+;
+;    This program is free software; you can redistribute it and/or modify
+;    it under the terms of the GNU General Public License as published by
+;    the Free Software Foundation; either version 2 of the License, or
+;    (at your option) any later version.
+;
+;    This program is distributed in the hope that it will be useful,
+;    but WITHOUT ANY WARRANTY; without even the implied warranty of
+;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;    GNU General Public License for more details.
+;
+;    You should have received a copy of the GNU General Public License
+;    along with this program; if not, write to the Free Software
+;    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+;
+;
+
+FUNCTION rename_tags, struct, oldtags_in, newtags_in, verbose=verbose
+
+  IF n_params() LT 3 THEN BEGIN 
+      print,'-Syntax: newstruct = rename_tags(struct, oldtags, newtags, verbose=)'
+      return, -1
+  ENDIF 
+
+  if n_elements(verbose) eq 0 then verbose=0
+
+  oldtags = strupcase(oldtags_in)
+  newtags = strupcase(newtags_in)
+
+  nst = n_elements(struct)
+  nold = n_elements(oldtags)
+  nnew = n_elements(newtags)
+
+  IF nold NE nnew THEN BEGIN 
+      message,'# of old tags ne # of new tags',/inf
+      return, -1
+  ENDIF 
+
+  tags = tag_names(struct)
+  ntags = n_elements(tags)
+
+  ;; create new structure
+  FOR i=0L, ntags-1 DO BEGIN 
+
+      w=where(oldtags EQ tags[i], nw)
+      
+      IF nw NE 0 THEN BEGIN 
+          taguse = newtags[w[0]]
+          if verbose gt 0 then begin
+              print,'Changing tag "'+tags[i]+'" to "'+taguse+'"'
+          endif
+      ENDIF ELSE BEGIN 
+          taguse = tags[i]
+      ENDELSE 
+      
+      IF i EQ 0 THEN BEGIN 
+          newst = create_struct(taguse, struct[0].(i))
+      ENDIF ELSE BEGIN 
+          newst = create_struct(newst, taguse, struct[0].(i))
+      ENDELSE 
+
+  ENDFOR 
+  
+  ;; copy in the values
+  newstruct = replicate(newst, nst)
+  FOR i=0L, ntags-1 DO BEGIN 
+      newstruct.(i) = struct.(i)
+  ENDFOR 
+
+  return, newstruct
+END 
diff --git a/Code/script_idl_mv/sedfitgrid_engine_thick_vialactea.pro b/Code/script_idl_mv/sedfitgrid_engine_thick_vialactea.pro
new file mode 100644
index 0000000000000000000000000000000000000000..8d1d6881ae928547a7a9ba7c8466dd36451ed991
--- /dev/null
+++ b/Code/script_idl_mv/sedfitgrid_engine_thick_vialactea.pro
@@ -0,0 +1,233 @@
+pro sedfitgrid_engine_thick_vialactea,lambda,flux,sizz,temprange,betarange,l0range,sfactrange,dist,sref,lambdagb,flussogb,mass,ftemp,fbeta,fl0,sizesec,lum,dmass,dtemp,dlam0,errorbars=errorbars,ulimit=ulimit,silent=silent,printfile=printfile
+; version for VIALACTEA, it doesn't use a settings file, but accepts the initial ranges as input variables
+;Written by Davide Elia, 2012-2015
+
+; lambda in um, size in arcsec, flux in Jy, size in arcsec, dist in pc
+; temprange,betarange,l0range,sfactrange: ; 3-element vectors, cotaining min, max and number of steps
+; sref: 2-element vector, containing opacity at lambda-ref (cm^2_g^-1), and lambda_ref itself (micron).
+
+stemp=temprange
+sbeta=betarange
+sl0=l0range
+sfact=sfactrange
+
+if temprange[2]  lt 5 then deg1=0 else deg1=1
+if betarange[2]  lt 5 then deg2=0 else deg2=1
+if sfactrange[2] lt 5 then deg3=0 else deg3=1
+if l0range[2]    lt 5 then deg4=0 else deg4=1
+deg=deg1+deg2+deg3+deg4
+
+pccm=3.09d18
+kref=sref[0]
+lambdaref=sref[1]
+
+lambdagb=5.+findgen(1995)
+
+conv=1/2.99792e-13*lambdagb^2.
+
+ssize=sfact*sizz
+
+m1=0
+m2=1
+t1=0
+t2=1
+b1=0
+b2=1
+s1=0
+s2=1
+npos=3
+
+chi0=1e8
+rchi=1e8
+count=0
+
+lmask=fltarr(n_elements(lambda))
+wf=where(flux,c)
+lmask[wf]=1.
+
+if keyword_set(ulimit) gt 0 then begin
+  wu=where(ulimit ne 0,cu)
+  if cu ne 0 then lmask[wu]=0.
+endif
+
+if keyword_set(silent) eq 0 then begin
+print,''
+print,'------------------------------------------------------'
+print,'flux= ',flux
+endif
+
+
+sedbank_thick_large,lambda,sl0[m1],sl0[m2],l0range[2],stemp[t1],stemp[t2],temprange[2],sbeta[b1],sbeta[b2],betarange[2],ssize[s1],ssize[s2],sfact[2],ssl0,sstemp,ssbeta,sssize,bank
+sb=size(bank)
+cflux=fltarr(sb[1]*sb[2]*sb[3]*sb[4],sb[5])
+mask=fltarr(sb[1]*sb[2]*sb[3]*sb[4],sb[5])
+if keyword_set(errorbars) ne 0 then eflux=fltarr(sb[1]*sb[2]*sb[3]*sb[4],sb[5])
+
+for tt=0,sb[5]-1 do begin
+  cflux[*,tt]=flux[tt]
+  mask[*,tt]=lmask[tt]
+  if keyword_set(errorbars) ne 0 then eflux[*,tt]=errorbars[tt]
+endfor
+
+
+while (rchi gt 1.01 and count le 10) do begin
+
+sedbank_thick_large,lambda,sl0[m1],sl0[m2],l0range[2],stemp[t1],stemp[t2],temprange[2],sbeta[b1],sbeta[b2],betarange[2],ssize[s1],ssize[s2],sfact[2],ssl0,sstemp,ssbeta,sssize,bank
+sssize=206265.*sqrt(sssize*4./!pi)
+
+sl0=ssl0
+sbeta=ssbeta
+stemp=sstemp
+ssize=sssize
+sb=size(bank)
+
+ctemp=[250. , 100 ,  50 ,  40 , 30  , 20  , 19  , 18  , 17  , 16  , 15  , 14  , 13  , 12  , 11  , 10  ,  9  ,  8  ,  7   ,  6   ,   5   ]
+c70 = [1.005,0.989,0.982,0.992,1.034,1.224,1.269,1.325,1.396,1.488,1.607,1.768,1.992,2.317,2.816,3.645,5.175,8.497,17.815,58.391,456.837] 
+c100= [1.023,1.007,0.985,0.980,0.982,1.036,1.051,1.069,1.093,1.123,1.162,1.213,1.282,1.377,1.512,1.711,2.024,2.554, 3.552, 5.774, 12.259] 
+c160= [1.062,1.042,1.010,0.995,0.976,0.963,0.964,0.967,0.972,0.979,0.990,1.005,1.028,1.061,1.110,1.184,1.300,1.491, 1.833, 2.528,  4.278]
+
+w70=where(lambda eq 70.,cw70)
+if cw70 ne 0 then begin
+ic70=interpol(c70,ctemp,stemp,/quadratic)
+corr70=fltarr(sb[1],sb[2],sb[3],sb[4])
+for z=0,sb[2]-1 do begin
+corr70[*,z,*,*]=ic70[z]
+endfor
+bank[*,*,*,*,w70]=bank[*,*,*,*,w70]*corr70
+endif
+
+w160=where(lambda eq 160.,cw160)
+if cw160 ne 0 then begin
+ic160=interpol(c160,ctemp,stemp,/quadratic)
+corr160=fltarr(sb[1],sb[2],sb[3],sb[4])
+for z=0,sb[2]-1 do begin
+corr160[*,z,*,*]=ic160[z]
+endfor
+bank[*,*,*,*,w160]=bank[*,*,*,*,w160]*corr160
+endif
+
+
+
+rbank=reform(bank,sb[1]*sb[2]*sb[3]*sb[4],sb[5])
+
+if keyword_set(errorbars) ne 0 then begin
+  chi=total(((rbank*mask-cflux)/(eflux+1e-7))^2,2)
+endif else begin
+  chi=total((rbank*mask-cflux)^2/(rbank*mask+1e-7),2)
+endelse
+redchi=chi/max([1,(n_elements(lambda)-deg-1 )])
+
+if keyword_set(ulimit) gt 0 then begin
+   if cu gt 1 then mdiff=sum((rbank[*,wu]-cflux[*,wu])/abs(rbank[*,wu]-cflux[*,wu]),1) else mdiff=(rbank[*,wu[0]]-cflux[*,wu[0]])/abs(rbank[*,wu[0]]-cflux[*,wu[0]])
+   wf=where(mdiff lt cu,cf)
+   mmm=min(chi[wf],wmm)
+   wm=wf[wmm]
+endif else mmm=min(chi,wm)
+remchi=mmm/max([1,(n_elements(lambda)-deg-1 )])
+
+wm3 = ARRAY_INDICES(bank,wm)
+wm3 = ARRAY_INDICES(bank,wm)
+m1=max([0,wm3[0]-npos])
+m2=min([sb[1]-1,wm3[0]+npos])
+t1=max([0,wm3[1]-npos])
+t2=min([sb[2]-1,wm3[1]+npos])
+b1=max([0,wm3[2]-npos])
+b2=min([sb[3]-1,wm3[2]+npos])
+s1=max([0,wm3[3]-npos])
+s2=min([sb[4]-1,wm3[3]+npos])
+
+fl0=sl0[wm3[0]]
+ftemp=stemp[wm3[1]]
+fbeta=sbeta[wm3[2]]
+fsize=(ssize[wm3[3]]/206265.)^2*!pi/4.
+if KEYWORD_SET(silent) eq 0 then begin
+print,fl0,ftemp,fbeta,206265.*sqrt(fsize*4./!pi),mmm
+print,sl0[m1],sl0[m2],stemp[t1],stemp[t2],sbeta[b1],sbeta[b2],ssize[s1],ssize[s2]
+endif
+
+cchi=total(((rbank[wm,wf]*mask[wm,wf]-cflux[wm,wf])/(rbank[wm,wf]*mask[wm,wf]))^2,2)/c ;stima "personalizzata del chi2 per confrontarlo con l'altro metodo
+rchi=chi0/mmm
+count=count+1
+if count eq 1 then rchi=1e8
+chi0=mmm
+endwhile
+
+inc=1
+ccc=0
+while ccc eq 0  do begin
+wwc=where(redchi lt remchi+inc,ccc)
+inc=inc+1
+endwhile
+
+wm4 = ARRAY_INDICES(bank,wwc)
+wm4 = ARRAY_INDICES(bank,wwc)
+
+dtemp1=abs(ftemp-max(stemp[wm4[1,*]]))
+dtemp2=abs(ftemp-min(stemp[wm4[1,*]]))
+dlam01=abs(fl0-max(sl0[wm4[0,*]]))
+dlam02=abs(fl0-min(sl0[wm4[0,*]]))
+dbeta1=abs(fbeta-max(sbeta[wm4[2,*]]))
+dbeta2=abs(fbeta-min(sbeta[wm4[2,*]]))
+dsize1=abs(fsize-max((ssize[wm4[3,*]])/206265.)^2*!pi/4.)
+dsize2=abs(fsize-min((ssize[wm4[3,*]])/206265.)^2*!pi/4.)
+mll=[min(sl0[wm4[0,*]]),max(sl0[wm4[0,*]])]
+mss=([min(ssize[wm4[0,*]]),max(ssize[wm4[0,*]])]/206265.)^2*!pi/4.
+mbb=[min(sbeta[wm4[0,*]]),max(sbeta[wm4[0,*]])]
+
+dmm=dblarr(8)
+for k0=0,1 do begin
+for k1=0,1 do begin
+for k2=0,1 do begin
+dmm[4*k0+2*k1+k2]=(dist*pccm)^2.*mss[k1]/kref*(mll[k0]/lambdaref)^mbb[k2]
+;print,dmm[4*k0+2*k1+k2]
+endfor
+endfor
+endfor
+dmass1=abs(ftemp-max(sbeta[wm4[2,*]]))
+dmass2=abs(ftemp-min(sbeta[wm4[2,*]]))
+
+
+dlam0=max([dlam01,dlam02])
+dmass=max([dmass1,dmass2])
+dtemp=max([dtemp1,dtemp2])
+
+if KEYWORD_SET(silent) eq 0 then begin
+print,'.....'
+;print,param[wm3[0],wm3[1],wm3[2],wm3[3],*]
+endif
+
+;window,1
+;plot_oo,lambda[wf],flux[wf],psym=4,yrange=[1e-2,1.3*max(flux[wf])]
+
+;xyouts,0.2,0.9,strtrim(id[i],1),/normal
+
+mass=(dist*pccm)^2.*fsize/kref*(fl0/lambdaref)^fbeta
+mass=mass/1.98892e33
+dmass1=abs(mass-max(dmm)/1.98892e33)
+dmass2=abs(mass-min(dmm)/1.98892e33)
+dmass=max([dmass1,dmass2])
+sizesec=206265.*sqrt(fsize*4./!pi)
+if KEYWORD_SET(silent) eq 0 then begin
+  print,'FIT results:  Mass: ',mass,'     Temp: ',ftemp,'     Beta: ',fbeta,'     lambda_0:',fl0,'     size:',sizesec,'     iterations:',count
+endif
+tau=(fl0/lambdagb)^fbeta
+flussogb=fsize*planck(lambdagb*1e4,ftemp)/!pi*conv*(1- exp(-tau))
+
+qd=where(flussogb gt 1e-8)
+;oplot,lambdagb[qd],flussogb[qd]
+lum=lbol(lambdagb[qd],flussogb[qd],dist)
+
+if keyword_set(printfile) then begin
+  openw,lun,printfile,/get_lun
+  for uu=0,n_elements(lambdagb)-1 do begin
+    str=string([lambdagb[uu],flussogb[uu]])
+    printf,lun,strjoin(strtrim(str,2),','),format='(A)'
+  endfor
+  openw,lun2,printfile+'.par',/get_lun
+  cmm=','
+  printf,lun2,mass,cmm,dmass,cmm,ftemp,cmm,dtemp,cmm,fbeta,cmm,fl0,cmm,dlam0,cmm,sizesec,cmm,lum,format='(f13.2,A1,f13.2,A1,f7.2,A1,f7.2,A1,F5.2,A1,F7.2,A1,F7.2,A1,f6.1,A1,f13.2)'
+  free_lun,lun
+  free_lun,lun2
+endif
+
+end
diff --git a/Code/script_idl_mv/sedfitgrid_engine_thin_vialactea.pro b/Code/script_idl_mv/sedfitgrid_engine_thin_vialactea.pro
new file mode 100644
index 0000000000000000000000000000000000000000..3a00012a31af34129e11281d0e482e63d5947d59
--- /dev/null
+++ b/Code/script_idl_mv/sedfitgrid_engine_thin_vialactea.pro
@@ -0,0 +1,212 @@
+pro sedfitgrid_engine_thin_vialactea,lambda,flux,massrange,temprange,betarange,dist,sref,lambdagb,flussogb,fmass,ftemp,fbeta,lum,dmass,dtemp,errorbars=errorbars,ulimit=ulimit,silent=silent,printfile=printfile
+; version for VIALACTEA, it doesn't use a settings file, but accepts the initial ranges as input variables
+;Written by Davide Elia, 2012-2015
+
+; lambda in um, size in arcsec, flux in Jy, size in arcsec, dist in pc
+; massrange,temprange,betarange: ; 3-element vectors, cotaining min, max and number of steps
+; sref: 2-element vector, containing opacity at lambda-ref (cm^2_g^-1), and lambda_ref itself (micron).
+
+
+dvirt=1000.
+
+
+smass=massrange  
+stemp=temprange
+sbeta=betarange
+
+if massrange[2] lt 5 then deg1=0 else deg1=1
+if temprange[2] lt 5 then deg2=0 else deg2=1
+if betarange[2] lt 5 then deg3=0 else deg3=1
+deg=deg1+deg2+deg3
+
+pccm=3.09d18
+kref=sref[0]
+lambdaref=sref[1]
+
+
+lambdagb=5.+findgen(1995)
+
+conv=1/2.99792e-13*lambdagb^2.
+
+
+distcm=dist*pccm
+
+m1=0
+m2=1
+t1=0
+t2=1
+b1=0
+b2=1
+
+npos=3
+
+chi0=1e8
+rchi=1e8
+count=0
+
+lmask=fltarr(n_elements(lambda))
+wf=where(flux,c)
+lmask[wf]=1.
+
+
+if keyword_set(ulimit) gt 0 then begin
+  wu=where(ulimit ne 0,cu)
+  if cu ne 0 then lmask[wu]=0.
+endif
+
+if keyword_set(silent) eq 0 then begin
+print,''
+print,'------------------------------------------------------'
+print,'flux= ',flux
+endif
+
+maxmass=smass[m2]
+sedbank_thin_large,lambda,smass[m1],smass[m2],massrange[2],stemp[t1],stemp[t2],temprange[2],sbeta[b1],sbeta[b2],betarange[2],kref,lambdaref,dvirt,ssmass,sstemp,ssbeta,bank
+sb=size(bank)
+cflux=fltarr(sb[1]*sb[2]*sb[3],sb[4])
+mask=fltarr(sb[1]*sb[2]*sb[3],sb[4])
+if keyword_set(errorbars) ne 0 then eflux=fltarr(sb[1]*sb[2]*sb[3],sb[4])
+
+for tt=0,sb[4]-1 do begin
+  cflux[*,tt]=flux[tt]
+  mask[*,tt]=lmask[tt]
+  if keyword_set(errorbars) ne 0 then eflux[*,tt]=errorbars[tt]
+endfor
+
+while (rchi gt 1.01 and count le 10) do begin
+REFIT:
+sedbank_thin_large,lambda,smass[m1],smass[m2],massrange[2],stemp[t1],stemp[t2],temprange[2],sbeta[b1],sbeta[b2],betarange[2],kref,lambdaref,dvirt,ssmass,sstemp,ssbeta,bank
+smass=ssmass
+sbeta=ssbeta
+stemp=sstemp
+sb=size(bank)
+
+ctemp=[250. , 100 ,  50 ,  40 , 30  , 20  , 19  , 18  , 17  , 16  , 15  , 14  , 13  , 12  , 11  , 10  ,  9  ,  8  ,  7   ,  6   ,   5   ]
+c70 = [1.005,0.989,0.982,0.992,1.034,1.224,1.269,1.325,1.396,1.488,1.607,1.768,1.992,2.317,2.816,3.645,5.175,8.497,17.815,58.391,456.837] 
+c100= [1.023,1.007,0.985,0.980,0.982,1.036,1.051,1.069,1.093,1.123,1.162,1.213,1.282,1.377,1.512,1.711,2.024,2.554, 3.552, 5.774, 12.259] 
+c160= [1.062,1.042,1.010,0.995,0.976,0.963,0.964,0.967,0.972,0.979,0.990,1.005,1.028,1.061,1.110,1.184,1.300,1.491, 1.833, 2.528,  4.278]
+
+w70=where(lambda eq 70.,cw70)
+if cw70 ne 0 then begin
+ic70=interpol(c70,ctemp,stemp,/quadratic)
+corr70=fltarr(sb[1],sb[2],sb[3])
+for z=0,sb[2]-1 do begin
+corr70[*,z,*]=ic70[z]
+endfor
+bank[*,*,*,w70]=bank[*,*,*,w70]*corr70
+endif
+
+w160=where(lambda eq 160.,cw160)
+if cw160 ne 0 then begin
+ic160=interpol(c160,ctemp,stemp,/quadratic)
+corr160=fltarr(sb[1],sb[2],sb[3])
+for z=0,sb[2]-1 do begin
+corr160[*,z,*]=ic160[z]
+endfor
+bank[*,*,*,w160]=bank[*,*,*,w160]*corr160
+endif
+
+
+rbank=reform(bank,sb[1]*sb[2]*sb[3],sb[4])
+
+if keyword_set(errorbars) ne 0 then begin
+  chi=total(((rbank*mask-cflux)/(eflux+1e-7))^2,2)
+endif else begin
+  chi=total((rbank*mask-cflux)^2/(rbank*mask+1e-7),2)
+endelse
+ redchi=chi/max([1,(n_elements(lambda)-deg-1 )])
+
+if keyword_set(ulimit) gt 0 then begin
+   if cu gt 1 then mdiff=sum((rbank[*,wu]-cflux[*,wu])/abs(rbank[*,wu]-cflux[*,wu]),1) else mdiff=(rbank[*,wu[0]]-cflux[*,wu[0]])/abs(rbank[*,wu[0]]-cflux[*,wu[0]])
+   wf=where(mdiff lt cu,cf)
+   mmm=min(chi[wf],wmm)
+   wm=wf[wmm]
+endif else mmm=min(chi,wm)
+remchi=mmm/max([1,(n_elements(lambda)-deg-1 )])   
+   
+wm3 = ARRAY_INDICES(bank,wm)
+m1=max([0,wm3[0]-npos])
+m2=min([sb[1]-1,wm3[0]+npos])
+t1=max([0,wm3[1]-npos])
+t2=min([sb[2]-1,wm3[1]+npos])
+b1=max([0,wm3[2]-npos])
+b2=min([sb[3]-1,wm3[2]+npos])
+
+fmass=smass[wm3[0]]
+ftemp=stemp[wm3[1]]
+fbeta=sbeta[wm3[2]]
+
+if abs(fmass-maxmass)/fmass lt 1e-6 then begin
+  print,'azz',maxmass,fmass
+  maxmass=2*maxmass
+  smass[m1]=4*smass[m1]
+  smass[m2]=maxmass
+  goto,REFIT
+endif
+
+if KEYWORD_SET(silent) eq 0 then begin
+print,fmass,ftemp,fbeta,mmm
+print,smass[m1],smass[m2],stemp[t1],stemp[t2],sbeta[b1],sbeta[b2]
+endif
+
+cchi=total(((rbank[wm,wf]*mask[wm,wf]-cflux[wm,wf])/(rbank[wm,wf]*mask[wm,wf]))^2,2)/c ;stima "personalizzata del chi2 per confrontarlo con l'altro metodo
+rchi=chi0/mmm
+count=count+1
+if count eq 1 then rchi=1e8
+chi0=mmm
+endwhile
+fmass=fmass*(dist/dvirt)^2
+
+inc=1
+ccc=0
+while ccc eq 0 do begin
+wwc=where(redchi lt remchi+inc,ccc)
+inc=inc+1
+endwhile
+
+wm4 = ARRAY_INDICES(bank,wwc)
+wm4 = ARRAY_INDICES(bank,wwc)
+
+dmass1=abs(fmass-max(smass[wm4[0,*]]))
+dmass2=abs(fmass-min(smass[wm4[0,*]]))
+dtemp1=abs(ftemp-max(stemp[wm4[1,*]]))
+dtemp2=abs(ftemp-min(stemp[wm4[1,*]]))
+
+dmass=max([dmass1,dmass2])
+dtemp=max([dtemp1,dtemp2])
+
+if KEYWORD_SET(silent) eq 0 then begin
+print,'.....'
+;print,param[wm3[0],wm3[1],wm3[2],wm3[3],*]
+
+;window,1
+;plot_oo,lambda[wf],flux[wf],psym=4,yrange=[1e-2,1.3*max(flux[wf])]
+
+;xyouts,0.2,0.9,strtrim(id[i],1),/normal
+
+print,'FIT results:  Mass: ',fmass,'     Temp: ',ftemp,'     Beta: ',fbeta,'     iterations:',count
+;print,dmass1,dmass2,dtemp1,dtemp2
+endif
+
+fmassg=fmass*1.98892e33
+flussogb=fmassg*kref/distcm^2*planck(1e4*lambdagb,ftemp)/!pi*conv*(lambdaref/lambdagb)^fbeta
+
+qd=where(flussogb gt 1e-8)
+;oplot,lambdagb[qd],flussogb[qd]
+lum=lbol(lambdagb[qd],flussogb[qd],dist)
+
+if keyword_set(printfile) then begin
+  openw,lun,printfile,/get_lun
+  for uu=0,n_elements(lambdagb)-1 do begin
+    str=string([lambdagb[uu],flussogb[uu]])
+    printf,lun,strjoin(strtrim(str,2),','),format='(A)'
+  endfor
+  openw,lun2,printfile+'.par',/get_lun
+  cmm=','
+  printf,lun2,fmass,cmm,dmass,cmm,ftemp,cmm,dtemp,cmm,fbeta,cmm,lum,format='(f13.2,A1,f13.2,A1,f7.2,A1,f7.2,A1,F5.2,A1,f13.2)'
+  free_lun,lun
+  free_lun,lun2
+endif
+
+
+end
diff --git a/Code/script_idl_mv/tostring.pro b/Code/script_idl_mv/tostring.pro
new file mode 100644
index 0000000000000000000000000000000000000000..c8d9c39ade79e9f993839843172a766f633fafd0
--- /dev/null
+++ b/Code/script_idl_mv/tostring.pro
@@ -0,0 +1,3 @@
+function tostring,something
+	return,strtrim(string(something),2)
+end
diff --git a/Code/script_idl_mv/vialactea_tap_sedfit.pro b/Code/script_idl_mv/vialactea_tap_sedfit.pro
new file mode 100644
index 0000000000000000000000000000000000000000..ca4559cf6a4fc0bcdc762ccdfc4334209bac4a1c
--- /dev/null
+++ b/Code/script_idl_mv/vialactea_tap_sedfit.pro
@@ -0,0 +1,120 @@
+function vialactea_tap_sedfit,w,f,fflag,distance,prefilter_thresh,sed_weights=sed_weights,local=local,use_wave=use_wave
+
+DB_schema='vlkb_compactsources'
+DB_table='sed_models'
+user='root'
+db_server=''
+phys_par=strupcase(['clump_mass','clump_upper_age'])
+
+jy2mjy=1000.
+
+; w is the array of SED wavelengths in microns
+; f is the array of the fluxes in Jy (df contains the uncertainties, if defined)
+; distance is in parsec
+; prefilter_thresh is the threshold for prefiltering the model grids at the given USE_WAVE wavelength
+
+; this is an updated version with respect to FIT_SED because it only uses fixed photometric bands and extract them already at the stage of initial model selection 
+; instead of re-extracting each and every pre-selected model a second time. Indeed, most of the computing time was being wasted for the MYSQL calls
+d_ref=1000.
+ref_wave=[3.6,4.5,5.8,8.0,24.0,70.,100.,160.,250.,350.,500.]
+col_names=['I1','I2','I3','I4','M1','PACS1','PACS2','PACS3','SPIR1','SPIR2','SPIR3']
+
+fac_resc=(distance/d_ref)^2.
+; now builds the string for mysql query to dump either the full model or the discrete sed points corresponding to fixed wavelengths
+  
+par_str=''
+par_str_arr=strarr(n_elements(w))
+ret_par_str=''
+phys_str=''
+phys_par_arr=strarr(n_elements(phys_par))
+ret_phys_par=''  
+for k=0,n_elements(w)-1 do begin
+  qw=where(ref_wave-0.5 lt w(k) and ref_wave+0.5 gt w(k)) 
+  par_str=par_str+col_names(qw(0))+','
+  ret_par_str=ret_par_str+col_names(qw(0))+','
+  par_str_arr(k)=strlowcase(col_names(qw(0)))
+endfor
+par_str=strupcase(strmid(par_str,0,strlen(par_str)-1))
+ret_par_str=strlowcase(strmid(ret_par_str,0,strlen(ret_par_str)-1))
+
+for k=0,n_elements(phys_par)-1 do begin
+  phys_str=phys_str+phys_par(k)+','
+  ret_phys_par=ret_phys_par+phys_par(k)+','
+  phys_par_arr(k)=strlowcase(phys_par(k))
+endfor
+phys_str=strupcase(strmid(phys_str,0,strlen(phys_str)-1))
+ret_phys_par=strlowcase(strmid(ret_phys_par,0,strlen(ret_phys_par)-1))
+
+if (keyword_set(use_wave)) then begin
+  qband=where(w eq use_wave)
+  if (qband eq -1) then begin
+    print,'Reference wavelength required not found in data file.....EXIT'
+    return,-1
+  endif
+  qrefband=where(ref_wave eq use_wave)
+  nreq_par=1+n_elements(phys_par_arr)+n_elements(par_str_arr)
+    
+  dqlcmd="select id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where ("+col_names(qrefband(0))+">'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1-prefilter_thresh))+"') and ("+col_names(qrefband(0))+" <'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1+prefilter_thresh))+"')" ;fluxes are mutliplied by 1000 because model fluxes are in milliJy  
+    
+  curlcmd='curl --user vialactea:ia2vlkb -L --data "REQUEST=doQuery&VERSION=1.0&LANG=ADQL&FORMAT=csv&QUERY='
+  
+  curl_endpoint='" "http://palantir19.oats.inaf.it:8080/vlkb/sync?"'
+  
+  spawn,curlcmd+dqlcmd+curl_endpoint, data
+
+  openw,unit1,'sed_returned.dat', /get_lun
+  for i=0,n_elements(data)-1 do printf,unit1,data(i)
+  free_lun, unit1
+    
+;  models=read_votable8('sed_returned.dat')
+  dmodels=read_csv('sed_returned.dat', header=new_tags, count=nlines)
+  if (nlines le 1) then return,-1
+  old_tags=tag_names(dmodels)
+  models=rename_tags(dmodels,old_tags,new_tags)
+  
+endif else begin
+; compute object luminosity from observed SED
+  lum=lbol(w,f,distance)
+; rescaling factor has to stay at 1 if luminosity is used
+  fac_resc=1.
+  z=execute('mysqlquery,lun," select MODEL_NAME, INCL, THETA,'+par_str+' from YSO_OBJECTS_2 where (LTOT_OBS>'+string(lum*fac_resc*(1-prefilter_thresh))+') and (LTOT_OBS <'+string(lum*fac_resc*(1+prefilter_thresh))+');",id,incl,theta,'+ret_par_str)
+endelse
+
+
+n_sel=n_elements(models.id)
+par=replicate({id:0L, clump_mass:0.d0, clump_age:0.d0, d:0.d0, chi2:0.d0, wmod:ptr_new(), fmod:ptr_new()},n_sel)
+
+
+;MODIFICARE PER METTERE IL CHI2 NEL FILE DI OUTPUT
+openr,unit1,'sed_returned.dat', /get_lun
+openw,unit2,'sedfit_output.dat', /get_lun
+
+s=' '
+readf,unit1,s
+printf,unit2,s+',CHI2,DIST'
+
+; now starts fitting to the subset of models
+for i=0L,n_sel-1 do begin
+    print,'Doing model ',models.id(i),' clump mass= ',models.clump_mass(i),'...',n_sel-i,' to go'
+      w_mod=w
+      fjy_mod=dblarr(n_elements(w))
+      for k=0,n_elements(w)-1 do z=execute('fjy_mod(k)=double(models.'+par_str_arr(k)+'(i))/1000.') ;divide by 1000 because model fluxes are in milliJy
+      fmod=fjy_mod
+    p=modelsed_fit(w,f,fflag,w_mod,fmod,distance*(1-sqrt(prefilter_thresh)),distance*(1+sqrt(prefilter_thresh)),5,sed_weights=sed_weights)
+    par(i).id=models.id(i)
+    par(i).clump_mass=models.clump_mass(i)
+    par(i).clump_age=models.clump_upper_age(i)
+    par(i).d=p.d
+    par(i).chi2=p.chi2
+    par(i).wmod=ptr_new(w_mod)
+    par(i).fmod=ptr_new(fmod)
+
+	readf,unit1,s
+	printf,unit2,s+','+tostring(p.chi2)+','+tostring(p.d)
+endfor
+
+free_lun,unit1,unit2
+
+return,par
+
+end
diff --git a/Code/script_idl_mv/vialactea_tap_sedfit_v2.pro b/Code/script_idl_mv/vialactea_tap_sedfit_v2.pro
new file mode 100644
index 0000000000000000000000000000000000000000..12a432bf31144d717f8733bc1877b31dbd0d2efa
--- /dev/null
+++ b/Code/script_idl_mv/vialactea_tap_sedfit_v2.pro
@@ -0,0 +1,132 @@
+function vialactea_tap_sedfit_v2,w,f,df,fflag,distance,prefilter_thresh,sed_weights=sed_weights,local=local,use_wave=use_wave
+
+DB_schema='vlkb_compactsources'
+DB_table='sed_models'
+user='root'
+db_server=''
+phys_par=strupcase(['clump_mass','compact_mass_fraction','compact_mass_desired','compact_mass_actual','clump_upper_age','dust_temp'])
+
+jy2mjy=1000.
+
+; w is the array of SED wavelengths in microns
+; f is the array of the fluxes in Jy (df contains the uncertainties, if defined)
+; distance is in parsec
+; prefilter_thresh is the threshold for prefiltering the model grids at the given USE_WAVE wavelength
+
+; this is an updated version with respect to FIT_SED because it only uses fixed photometric bands and extract them already at the stage of initial model selection 
+; instead of re-extracting each and every pre-selected model a second time. Indeed, most of the computing time was being wasted for the MYSQL calls
+d_ref=1000.
+ref_wave=[3.37,3.6,4.5,4.62,5.8,8.0,12.08,22.194,24.0,70.,100.,160.,250.,350.,500.,870.,1100.]
+col_names=['WISE1','I1','I2','WISE2','I3','I4','WISE3','WISE4','M1','PACS1','PACS2','PACS3','SPIR1','SPIR2','SPIR3','LABOC','BOL11']
+
+fac_resc=(distance/d_ref)^2.
+delta=1-(prefilter_thresh)
+
+; now builds the string for mysql query to dump either the full model or the discrete sed points corresponding to fixed wavelengths
+  
+par_str=''
+par_str_arr=strarr(n_elements(w))
+ret_par_str=''
+phys_str=''
+phys_par_arr=strarr(n_elements(phys_par))
+ret_phys_par=''  
+for k=0,n_elements(w)-1 do begin
+  qw=where(ref_wave-0.5 lt w(k) and ref_wave+0.5 gt w(k)) 
+  par_str=par_str+col_names(qw(0))+','
+  ret_par_str=ret_par_str+col_names(qw(0))+','
+  par_str_arr(k)=strlowcase(col_names(qw(0)))
+endfor
+par_str=strupcase(strmid(par_str,0,strlen(par_str)-1))
+ret_par_str=strlowcase(strmid(ret_par_str,0,strlen(ret_par_str)-1))
+
+for k=0,n_elements(phys_par)-1 do begin
+  phys_str=phys_str+phys_par(k)+','
+  ret_phys_par=ret_phys_par+phys_par(k)+','
+  phys_par_arr(k)=strlowcase(phys_par(k))
+endfor
+phys_str=strupcase(strmid(phys_str,0,strlen(phys_str)-1))
+ret_phys_par=strlowcase(strmid(ret_phys_par,0,strlen(ret_phys_par)-1))
+
+if (keyword_set(use_wave)) then begin
+  qband=where(w eq use_wave)
+  if (qband eq -1) then begin
+    print,'Reference wavelength required not found in data file.....EXIT'
+    return,-1
+  endif
+  qrefband=where(ref_wave eq use_wave)
+  nreq_par=1+n_elements(phys_par_arr)+n_elements(par_str_arr)
+    
+  dqlcmd="select id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where ("+col_names(qrefband(0))+">'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1-(delta^2.)))+"') and ("+col_names(qrefband(0))+" <'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1+(delta^2.)))+"')" ;fluxes are mutliplied by 1000 because model fluxes are in milliJy  
+      
+endif else begin
+; compute object luminosity from observed SED
+  lum=lbol(w,f,distance)
+; rescaling factor has to stay at 1 if luminosity is used
+  fac_resc=1.
+  dqlcmd="select id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where (bolometric_luminosity >'"+tostring(lum*fac_resc*(1-(delta^2.)))+"') and (bolometric_luminosity <'"+tostring(lum*fac_resc*(1+(delta^2.)))+"')" 
+endelse
+
+curlcmd='curl --user vialactea:ia2vlkb -L --data "REQUEST=doQuery&VERSION=1.0&LANG=ADQL&FORMAT=csv&QUERY='
+  
+curl_endpoint='" "http://palantir19.oats.inaf.it:8080/vlkb/sync?"'
+spawn,curlcmd+dqlcmd+curl_endpoint, data
+print,curlcmd+dqlcmd+curl_endpoint
+openw,unit1,'sed_returned.dat', /get_lun
+for i=0,n_elements(data)-1 do printf,unit1,data(i)
+free_lun, unit1
+    
+;  models=read_votable8('sed_returned.dat')
+dmodels=read_csv('sed_returned.dat', header=new_tags, count=nlines)
+if (nlines le 1) then return,-1
+old_tags=tag_names(dmodels)
+models=rename_tags(dmodels,old_tags,new_tags)
+
+n_sel=n_elements(models.id)
+par=replicate({id:0L, clump_mass:0.d0, compact_mass_fraction: 0.d0, compact_mass_desired: 0.d0, compact_mass_actual: 0.d0, clump_age:0.d0, dust_temp:0.d0, d:0.d0, chi2:0.d0, wmod:ptr_new(), fmod:ptr_new()},n_sel)
+
+;MODIFICARE PER METTERE IL CHI2 NEL FILE DI OUTPUT
+openr,unit1,'sed_returned.dat', /get_lun
+openw,unit2,'sedfit_output.dat', /get_lun
+
+s=' '
+readf,unit1,s
+printf,unit2,s+',CHI2,DIST'
+
+; now starts fitting to the subset of models
+for i=0L,n_sel-1 do begin
+  print,'Doing model ',models.id(i),' clump mass= ',models.clump_mass(i),'...',n_sel-i,' to go'
+  w_mod=w
+  fjy_mod=dblarr(n_elements(w))
+  for k=0,n_elements(w)-1 do z=execute('fjy_mod(k)=double(models.'+par_str_arr(k)+'(i))/1000.') ;divide by 1000 because model fluxes are in milliJy
+    fmod=fjy_mod
+ 	  p=modelsed_fit_v2(w,f,df,fflag,w_mod,fmod,distance*(1-(delta^2.)),distance*(1+(delta^2.)),50,sed_weights=sed_weights)
+
+; rescale the models for the fitted distance....I'll have to see what this means for rescaling the model parameters
+;	fmod=fmod/fac_resc*((d_ref/p.d)^2.)
+	fmod=fmod*((d_ref/p.d)^2.)
+	
+    par(i).id=models.id(i)
+    par(i).clump_mass=models.clump_mass(i)
+    par(i).compact_mass_fraction=models.compact_mass_fraction(i)
+    par(i).compact_mass_desired=models.compact_mass_desired(i)
+    par(i).compact_mass_actual=models.compact_mass_actual(i)
+    par(i).clump_age=models.clump_upper_age(i)
+    par(i).dust_temp=models.dust_temp(i)
+    par(i).d=p.d
+    par(i).chi2=p.chi2
+    par(i).wmod=ptr_new(w_mod)
+    par(i).fmod=ptr_new(fmod)
+
+;	readf,unit1,s
+;	printf,unit2,s+','+tostring(p.chi2)+','+tostring(p.d)
+	str_mod=' '
+	for nmod=0,n_elements(fmod)-1 do str_mod=str_mod+tostring(fmod(nmod))+','
+		printf,unit2,tostring(par(i).id)+','+tostring(par(i).clump_mass)+','+tostring(par(i).compact_mass_fraction)+','+tostring(par(i).compact_mass_desired)+','+tostring(par(i).compact_mass_actual)+','+tostring(par(i).clump_age)+','+tostring(par(i).dust_temp)+','+str_mod+tostring(par(i).chi2)+','+tostring(par(i).d)
+		
+endfor
+
+free_lun,unit1,unit2
+
+return,par
+
+end
diff --git a/Code/script_idl_mv/vialactea_tap_sedfit_v3.pro b/Code/script_idl_mv/vialactea_tap_sedfit_v3.pro
new file mode 100644
index 0000000000000000000000000000000000000000..55d7a6a43b55542de1848e5faacb06404c5a5d65
--- /dev/null
+++ b/Code/script_idl_mv/vialactea_tap_sedfit_v3.pro
@@ -0,0 +1,146 @@
+; version v3 implements the ability to use a number of pivotal wavelength for model pre-filtering, instead of only 1
+
+function vialactea_tap_sedfit_v3,w,f,df,fflag,distance,prefilter_thresh,sed_weights=sed_weights,local=local,use_wave=use_wave,outdir=outdir
+
+if (keyword_set(outdir) eq 0) then outdir=''
+
+DB_schema='vlkb_compactsources'
+DB_table='sed_models'
+user='root'
+db_server=''
+phys_par=strupcase(['clump_mass','compact_mass_fraction','compact_mass_desired','compact_mass_actual','clump_upper_age','dust_temp'])
+
+jy2mjy=1000.
+
+; w is the array of SED wavelengths in microns
+; f is the array of the fluxes in Jy (df contains the uncertainties, if defined)
+; distance is in parsec
+; prefilter_thresh is the threshold for prefiltering the model grids at the given USE_WAVE wavelength
+
+; this is an updated version with respect to FIT_SED because it only uses fixed photometric bands and extract them already at the stage of initial model selection 
+; instead of re-extracting each and every pre-selected model a second time. Indeed, most of the computing time was being wasted for the MYSQL calls
+d_ref=1000.
+ref_wave=[3.37,3.6,4.5,4.62,5.8,8.0,12.08,22.194,24.0,70.,100.,160.,250.,350.,500.,870.,1100.]
+col_names=['WISE1','I1','I2','WISE2','I3','I4','WISE3','WISE4','M1','PACS1','PACS2','PACS3','SPIR1','SPIR2','SPIR3','LABOC','BOL11']
+
+fac_resc=(distance/d_ref)^2.
+delta=1-(prefilter_thresh)
+
+; now builds the string for mysql query to dump either the full model or the discrete sed points corresponding to fixed wavelengths
+  
+par_str=''
+par_str_arr=strarr(n_elements(w))
+ret_par_str=''
+phys_str=''
+phys_par_arr=strarr(n_elements(phys_par))
+ret_phys_par=''  
+for k=0,n_elements(w)-1 do begin
+  qw=where(ref_wave-0.5 lt w(k) and ref_wave+0.5 gt w(k)) 
+  par_str=par_str+col_names(qw(0))+','
+  ret_par_str=ret_par_str+col_names(qw(0))+','
+  par_str_arr(k)=strlowcase(col_names(qw(0)))
+endfor
+par_str=strupcase(strmid(par_str,0,strlen(par_str)-1))
+ret_par_str=strlowcase(strmid(ret_par_str,0,strlen(ret_par_str)-1))
+
+for k=0,n_elements(phys_par)-1 do begin
+  phys_str=phys_str+phys_par(k)+','
+  ret_phys_par=ret_phys_par+phys_par(k)+','
+  phys_par_arr(k)=strlowcase(phys_par(k))
+endfor
+phys_str=strupcase(strmid(phys_str,0,strlen(phys_str)-1))
+ret_phys_par=strlowcase(strmid(ret_phys_par,0,strlen(ret_phys_par)-1))
+
+openw,unit1,outdir+'sed_returned.dat', /get_lun
+
+if (keyword_set(use_wave)) then begin
+ for bb=0,n_elements(use_wave)-1 do begin
+  qband=where(w eq use_wave(bb))
+  if (qband eq -1) then begin
+    print,'Reference wavelength required not found in data file.....EXIT'
+;    return,-1
+    break
+  endif
+  qrefband=where(ref_wave eq use_wave(bb))
+  nreq_par=1+n_elements(phys_par_arr)+n_elements(par_str_arr)
+    
+  dqlcmd="select id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where ("+col_names(qrefband(0))+">'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1-(delta^2.)))+"') and ("+col_names(qrefband(0))+" <'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1+(delta^2.)))+"')" ;fluxes are mutliplied by 1000 because model fluxes are in milliJy  
+  curlcmd='curl --user vialactea:ia2vlkb -L --data "REQUEST=doQuery&VERSION=1.0&LANG=ADQL&FORMAT=csv&QUERY='
+  curl_endpoint='" "http://palantir19.oats.inaf.it:8080/vlkb/sync?"'
+  spawn,curlcmd+dqlcmd+curl_endpoint, data
+  print,curlcmd+dqlcmd+curl_endpoint
+  if (bb eq 0) then begin
+    for i=0,n_elements(data)-1 do printf,unit1,data(i)
+  endif else begin
+    for i=1,n_elements(data)-1 do printf,unit1,data(i)
+  endelse
+ endfor      
+endif else begin
+; compute object luminosity from observed SED
+  lum=lbol(w,f,distance)
+; rescaling factor has to stay at 1 if luminosity is used
+  fac_resc=1.
+  dqlcmd="select id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where (bolometric_luminosity >'"+tostring(lum*fac_resc*(1-(delta^2.)))+"') and (bolometric_luminosity <'"+tostring(lum*fac_resc*(1+(delta^2.)))+"')" 
+  curlcmd='curl --user vialactea:ia2vlkb -L --data "REQUEST=doQuery&VERSION=1.0&LANG=ADQL&FORMAT=csv&QUERY='
+  curl_endpoint='" "http://palantir19.oats.inaf.it:8080/vlkb/sync?"'
+  spawn,curlcmd+dqlcmd+curl_endpoint, data
+  print,curlcmd+dqlcmd+curl_endpoint
+  for i=0,n_elements(data)-1 do printf,unit1,data(i)
+endelse
+free_lun, unit1
+    
+;  models=read_votable8('sed_returned.dat')
+dmodels=read_csv(outdir+'sed_returned.dat', header=new_tags, count=nlines)
+if (nlines le 1) then return,-1
+old_tags=tag_names(dmodels)
+models=rename_tags(dmodels,old_tags,new_tags)
+
+n_sel=n_elements(models.id)
+par=replicate({id:0L, clump_mass:0.d0, compact_mass_fraction: 0.d0, compact_mass_desired: 0.d0, compact_mass_actual: 0.d0, clump_age:0.d0, dust_temp:0.d0, d:0.d0, chi2:0.d0, wmod:ptr_new(), fmod:ptr_new()},n_sel)
+
+;MODIFICARE PER METTERE IL CHI2 NEL FILE DI OUTPUT
+openr,unit1,outdir+'sed_returned.dat', /get_lun
+openw,unit2,outdir+'sedfit_output.dat', /get_lun
+
+s=' '
+readf,unit1,s
+printf,unit2,s+',CHI2,DIST'
+
+; now starts fitting to the subset of models
+for i=0L,n_sel-1 do begin
+  print,'Doing model ',models.id(i),' clump mass= ',models.clump_mass(i),'...',n_sel-i,' to go'
+  w_mod=w
+  fjy_mod=dblarr(n_elements(w))
+  for k=0,n_elements(w)-1 do z=execute('fjy_mod(k)=double(models.'+par_str_arr(k)+'(i))/1000.') ;divide by 1000 because model fluxes are in milliJy
+    fmod=fjy_mod
+ 	  p=modelsed_fit_v2(w,f,df,fflag,w_mod,fmod,distance*(1-(delta^2.)),distance*(1+(delta^2.)),50,sed_weights=sed_weights)
+
+; rescale the models for the fitted distance....I'll have to see what this means for rescaling the model parameters
+;	fmod=fmod/fac_resc*((d_ref/p.d)^2.)
+	fmod=fmod*((d_ref/p.d)^2.)
+	
+    par(i).id=models.id(i)
+    par(i).clump_mass=models.clump_mass(i)
+    par(i).compact_mass_fraction=models.compact_mass_fraction(i)
+    par(i).compact_mass_desired=models.compact_mass_desired(i)
+    par(i).compact_mass_actual=models.compact_mass_actual(i)
+    par(i).clump_age=models.clump_upper_age(i)
+    par(i).dust_temp=models.dust_temp(i)
+    par(i).d=p.d
+    par(i).chi2=p.chi2
+    par(i).wmod=ptr_new(w_mod)
+    par(i).fmod=ptr_new(fmod)
+
+;	readf,unit1,s
+;	printf,unit2,s+','+tostring(p.chi2)+','+tostring(p.d)
+	str_mod=' '
+	for nmod=0,n_elements(fmod)-1 do str_mod=str_mod+tostring(fmod(nmod))+','
+		printf,unit2,tostring(par(i).id)+','+tostring(par(i).clump_mass)+','+tostring(par(i).compact_mass_fraction)+','+tostring(par(i).compact_mass_desired)+','+tostring(par(i).compact_mass_actual)+','+tostring(par(i).clump_age)+','+tostring(par(i).dust_temp)+','+str_mod+tostring(par(i).chi2)+','+tostring(par(i).d)
+		
+endfor
+
+free_lun,unit1,unit2
+
+return,par
+
+end
diff --git a/Code/script_idl_mv/vialactea_tap_sedfit_v6.pro b/Code/script_idl_mv/vialactea_tap_sedfit_v6.pro
new file mode 100644
index 0000000000000000000000000000000000000000..bdc676f6b6d922777d73d701e702d68d05b709e9
--- /dev/null
+++ b/Code/script_idl_mv/vialactea_tap_sedfit_v6.pro
@@ -0,0 +1,262 @@
+; version v3 implements the ability to use a number of pivotal wavelength for model pre-filtering, instead of only 1
+; version v5 works with new models grid version v5 delivered in early 2016
+; v6 tries to implement a vector way of doing the fit rather than doing the FOR loops
+
+function vialactea_tap_sedfit_v6,w,f,df,fflag,distance,prefilter_thresh,sed_weights=sed_weights,local=local,use_wave=use_wave,outdir=outdir,delta_chi2=delta_chi2
+
+if (keyword_set(outdir) eq 0) then outdir=''
+
+DB_schema='vlkb_compactsources'
+DB_table='sed_models'
+user='root'
+db_server=''
+phys_par=strupcase(['clump_mass','compact_mass_fraction','compact_mass_desired','compact_mass_actual','clump_upper_age','dust_temp','bolometric_luminosity','random_sample'])
+
+jy2mjy=1000.
+
+; w is the array of SED wavelengths in microns
+; f is the array of the fluxes in Jy (df contains the uncertainties, if defined)
+; distance is in parsec
+; prefilter_thresh is the threshold for prefiltering the model grids at the given USE_WAVE wavelength
+
+; this is an updated version with respect to FIT_SED because it only uses fixed photometric bands and extract them already at the stage of initial model selection 
+; instead of re-extracting each and every pre-selected model a second time. Indeed, most of the computing time was being wasted for the MYSQL calls
+d_ref=1000.
+ref_wave=[3.37,3.6,4.5,4.62,5.8,8.0,12.08,22.194,24.0,70.,100.,160.,250.,350.,500.,870.,1100.]
+col_names=['WISE1','I1','I2','WISE2','I3','I4','WISE3','WISE4','M1','PACS1','PACS2','PACS3','SPIR1','SPIR2','SPIR3','LABOC','BOL11']
+
+fac_resc=(distance/d_ref)^2.
+delta=1-(prefilter_thresh)
+
+; now builds the string for mysql query to dump either the full model or the discrete sed points corresponding to fixed wavelengths
+  
+par_str=''
+par_str_arr=strarr(n_elements(w))
+ret_par_str=''
+phys_str=''
+phys_par_arr=strarr(n_elements(phys_par))
+ret_phys_par=''  
+for k=0,n_elements(w)-1 do begin
+  qw=where(ref_wave-0.5 lt w(k) and ref_wave+0.5 gt w(k)) 
+  par_str=par_str+col_names(qw(0))+','
+  ret_par_str=ret_par_str+col_names(qw(0))+','
+  par_str_arr(k)=strlowcase(col_names(qw(0)))
+endfor
+par_str=strupcase(strmid(par_str,0,strlen(par_str)-1))
+ret_par_str=strlowcase(strmid(ret_par_str,0,strlen(ret_par_str)-1))
+
+for k=0,n_elements(phys_par)-1 do begin
+  phys_str=phys_str+phys_par(k)+','
+  ret_phys_par=ret_phys_par+phys_par(k)+','
+  phys_par_arr(k)=strlowcase(phys_par(k))
+endfor
+phys_str=strupcase(strmid(phys_str,0,strlen(phys_str)-1))
+ret_phys_par=strlowcase(strmid(ret_phys_par,0,strlen(ret_phys_par)-1))
+
+openw,unit1,outdir+'sed_returned.dat', /get_lun
+
+if (keyword_set(use_wave)) then begin
+; make sure that use_wave doe not contain bands for which e have upper limits
+ for bb=0,n_elements(use_wave)-1 do begin
+  qband=where(w eq use_wave(bb))
+  if (qband eq -1) then begin
+    print,'Reference wavelength required not found in data file.....EXIT'
+;    return,-1
+    break
+  endif
+  qrefband=where(ref_wave eq use_wave(bb))
+  match,ref_wave,w,qqueryband,qdummy
+  qulband=where(fflag eq 0, nqulband)
+  ul_str=''
+  if (nqulband gt 0) then begin
+    ul_str=' and '
+    for t=0,nqulband-1 do ul_str=ul_str+'('+col_names(qqueryband(qulband(t)))+"<'"+tostring(f(qulband(t))*jy2mjy*fac_resc)+"') and "
+    if (fflag(qband) eq 1) then ul_str=strmid(ul_str,0,strlen(ul_str)-4)
+    if (fflag(qband) eq 0) then ul_str=strmid(ul_str,4,strlen(ul_str)-4)
+  endif
+  
+  nreq_par=1+n_elements(phys_par_arr)+n_elements(par_str_arr)
+  
+  
+  if (fflag(qband) eq 1) then dqlcmd="select cluster_id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where ("+col_names(qrefband(0))+">'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1-(delta^2.)))+"') and ("+col_names(qrefband(0))+" <'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1+(delta^2.)))+"')"+ul_str ;fluxes are mutliplied by 1000 because model fluxes are in milliJy  
+  if (fflag(qband) eq 0) then dqlcmd="select cluster_id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where ("+col_names(qrefband(0))+" <'"+tostring(f(qband(0))*jy2mjy*fac_resc)+"')"+ul_str ;fluxes are mutliplied by 1000 because model fluxes are in milliJy  
+
+  curlcmd='curl --user vialactea:ia2vlkb -L --data "REQUEST=doQuery&VERSION=1.0&LANG=ADQL&FORMAT=csv&QUERY='
+  curl_endpoint='" "http://palantir19.oats.inaf.it:8080/vlkb/sync?"'
+  print,curlcmd+dqlcmd+curl_endpoint
+  spawn,curlcmd+dqlcmd+curl_endpoint, data
+  if (bb eq 0) then begin
+    for i=0,n_elements(data)-1 do printf,unit1,data(i)
+  endif else begin
+    for i=1,n_elements(data)-1 do printf,unit1,data(i)
+  endelse
+ endfor      
+endif else begin
+; compute object luminosity from observed SED
+  lum=lbol(w,f,distance)
+; rescaling factor has to stay at 1 if luminosity is used-----WHYYYY ??????
+;  fac_resc=1.
+  dqlcmd="select cluster_id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where (bolometric_luminosity >'"+tostring(lum*fac_resc*(1-(delta^2.)))+"') and (bolometric_luminosity <'"+tostring(lum*fac_resc*(1+(delta^2.)))+"')" 
+  curlcmd='curl --user vialactea:ia2vlkb -L --data "REQUEST=doQuery&VERSION=1.0&LANG=ADQL&FORMAT=csv&QUERY='
+  curl_endpoint='" "http://palantir19.oats.inaf.it:8080/vlkb/sync?"'
+  spawn,curlcmd+dqlcmd+curl_endpoint, data
+  print,curlcmd+dqlcmd+curl_endpoint
+  for i=0,n_elements(data)-1 do printf,unit1,data(i)
+endelse
+free_lun, unit1
+    
+;  models=read_votable8('sed_returned.dat')
+dmodels=read_csv(outdir+'sed_returned.dat', header=new_tags, count=nlines)
+if (nlines le 1) then return,-1
+old_tags=tag_names(dmodels)
+models=rename_tags(dmodels,old_tags,new_tags)
+
+n_sel=n_elements(models.cluster_id)
+
+;MODIFICARE PER METTERE IL CHI2 NEL FILE DI OUTPUT
+openr,unit1,outdir+'sed_returned.dat', /get_lun
+openw,unit2,outdir+'sedfit_output.dat', /get_lun
+
+s=' '
+readf,unit1,s
+printf,unit2,s+',CHI2,DIST'
+
+;establishes weights by assigning equal weights to mid-IR, far-IR and sub-mm
+flag=fix(fflag)
+ul_flag=intarr(n_elements(flag))
+ul_flag(*)=0
+ll_flag=intarr(n_elements(flag))
+ll_flag(*)=0
+dyd=fltarr(n_elements(flag))
+;w1=sqrt(3./7.)
+;w2=sqrt(3./7.)
+;w3=sqrt(3./7.)
+
+if (keyword_set(sed_weights) eq 0) then sed_weights=[3./7.,1./7.,3./7.] 
+renorm=total(sed_weights)
+w1=sqrt(sed_weights(0)/renorm)
+w2=sqrt(sed_weights(1)/renorm)
+w3=sqrt(sed_weights(2)/renorm)
+qmir=where(w lt 25,nqmir)
+qfir=where(w ge 25 and w le 250,nqfir)
+qmm=where(w gt 250,nqmm)
+if (nqmir gt 0) then begin
+  q1=where(flag(qmir) eq 1,nq1,complement=q1neg,ncomplement=nq1neg)
+  if (nq1 gt 0) then dyd(qmir(q1))=sqrt(nq1)/w1
+  if (nq1neg gt 0) then begin
+    dyd(qmir(q1neg))=9999.  ;i.e. it's an upper/lower limit
+    ul_flag(qmir(q1neg))=1
+  endif
+endif
+if (nqfir gt 0) then begin
+  q2=where(flag(qfir) eq 1,nq2,complement=q2neg,ncomplement=nq2neg)
+  if (nq2 gt 0) then dyd(qfir(q2))=sqrt(nq2)/w2
+  if (nq2neg gt 0) then begin
+    dyd(qfir(q2neg))=9999.   ;i.e. it's an upper limit
+    ul_flag(qfir(q2neg))=1
+  endif
+endif
+if (nqmm gt 0) then begin
+  q3=where(flag(qmm) eq 1,nq3,complement=q3neg,ncomplement=nq3neg)
+  if (nq3 gt 0) then dyd(qmm(q3))=sqrt(nq3)/w3
+  if (nq3neg gt 0) then begin
+    dyd(qmm(q3neg))=9999.   ;i.e. it's an upper limit
+    ul_flag(qmm(q3neg))=1
+  endif
+endif
+
+good_flag=1-ul_flag
+
+; now normalize dyd to the minimum value
+; so that we artificially increase the uncertainties of the fluxes where the SED-based weight is lower
+dyd=dyd/min(dyd)
+;print,'DF/F= ',dflux1/flux1
+dyd=dyd*df/f
+
+; now dyd has to be interpreted as a relative uncertainty otherwise the same assigned weight will have more or less importance depending on the level of flux and flux difference between model and observation. I then multiply the "weight" by the flux value
+dyd=dyd*f
+
+; for each model compute the adjusting distance steps
+nstep=50
+dist_arr=distance*(1-(delta^2.))+findgen(nstep)*(distance*(1+(delta^2.))-distance*(1-(delta^2.)))/nstep
+
+nw=n_elements(w)
+invalid_chi2=-999
+
+matrix_models=fltarr(n_sel,nstep,nw)
+matrix_chi2=fltarr(n_sel,nstep) & matrix_chi2(*,*)=invalid_chi2
+matrix_fluxes=fltarr(n_sel,nstep,nw)
+matrix_dfluxes=fltarr(n_sel,nstep,nw)
+
+; create matrix of models
+for i=0,nstep-1 do begin
+;  print,i
+  for k=0,nw-1 do z=execute('matrix_models(*,i,k)=models.'+par_str_arr(k)+'(*)/1000.*((d_ref/dist_arr(i))^2.)')
+endfor
+
+for k=0,nw-1 do matrix_fluxes(*,*,k)=f(k)
+for k=0,nw-1 do matrix_dfluxes(*,*,k)=dyd(k)
+
+print,'fatte matrici flussi'
+
+dmat=((matrix_models-matrix_fluxes)^2)/(matrix_dfluxes^2.)
+
+matrix_chi2=total(dmat,3)
+
+print,'fatta matrice chi2'
+
+;select by CHI2
+if (keyword_set(delta_chi2)) then dchi2=delta_chi2 else dchi2=1
+
+qchi2=where(matrix_chi2 le min(matrix_chi2)+dchi2, nqchi2)
+
+par=replicate({cluster_id:' ', clump_mass:0.d0, compact_mass_fraction: 0.d0, compact_mass_desired: 0.d0, compact_mass_actual: 0.d0, clump_age:0.d0, dust_temp:0.d0, lum_bol:0.d0, sample:0, d:0.d0, chi2:0.d0, wmod:ptr_new(), fmod:ptr_new()},nqchi2)
+
+wheretomulti,matrix_chi2,qchi2,mod_chi2,dist_chi2
+
+print,'wheretomulti'
+		
+print,'selezionati ',nqchi2,' modelli'
+
+; wmod and fmod can be addressed as, e.g., print,*(res(i).fmod)
+;	readf,unit1,s
+;	printf,unit2,s+','+tostring(p.chi2)+','+tostring(p.d)
+
+    par.cluster_id=models.cluster_id(mod_chi2)
+    par.clump_mass=models.clump_mass(mod_chi2)
+    par.compact_mass_fraction=models.compact_mass_fraction(mod_chi2)
+    par.compact_mass_desired=models.compact_mass_desired(mod_chi2)
+    par.compact_mass_actual=models.compact_mass_actual(mod_chi2)
+    par.clump_age=models.clump_upper_age(mod_chi2)
+    par.lum_bol=models.bolometric_luminosity(mod_chi2)
+    par.sample=models.random_sample(mod_chi2)
+    par.dust_temp=models.dust_temp(mod_chi2)
+    par.d=dist_arr(dist_chi2)
+    par.chi2=matrix_chi2(qchi2)
+    par.wmod=ptr_new(w)
+	z=ptrarr(nqchi2)
+	for i=0,nqchi2-1 do z(i)=ptr_new(matrix_models(mod_chi2(i),dist_chi2(i),*))
+	par.fmod=z
+	
+;str_mod=strarr(nqchi2) & str_mod(*)=' '
+for i=0,nqchi2-1 do begin
+	str_mod=' '
+	str_mod=str_mod+tostring(matrix_models(mod_chi2(i),dist_chi2(i),*))+','
+	printf,unit2,tostring(par(i).cluster_id)+','+tostring(par(i).clump_mass)+','+tostring(par(i).compact_mass_fraction)+','+tostring(par(i).compact_mass_desired)+','+tostring(par(i).compact_mass_actual)+','+tostring(par(i).clump_age)+','+tostring(par(i).dust_temp)+','+str_mod+tostring(par(i).chi2)+','+tostring(par(i).d)
+endfor
+
+
+free_lun,unit1,unit2
+
+return,par
+
+end
+
+
+
+
+
+
+
+
+
diff --git a/Code/script_idl_mv/vialactea_tap_sedfit_v7.pro b/Code/script_idl_mv/vialactea_tap_sedfit_v7.pro
new file mode 100644
index 0000000000000000000000000000000000000000..77804e920833a1ad83b89b8811fcb190e46bee1c
--- /dev/null
+++ b/Code/script_idl_mv/vialactea_tap_sedfit_v7.pro
@@ -0,0 +1,330 @@
+; version v3 implements the ability to use a number of pivotal wavelength for model pre-filtering, instead of only 1
+; version v5 works with new models grid version v5 delivered in early 2016
+; v6 tries to implement a vector way of doing the fit rather than doing the FOR loops
+; v7 corrects a bug of v6, because v6 was keeping all models with NSTEP adjusted distances. Instead, for each model I should keep only the distance step with minimum CHI2, and not all other distances. MAJOR BUG: passando i valori di limiti per le ricerche SQL sui flussi lo facevo con valori stringa ed il sistema interpretava a cazzo completamente
+
+
+function vialactea_tap_sedfit_v7,w_in,f_in,df_in,fflag_in,distance,prefilter_thresh,sed_weights=sed_weights,local=local,use_wave=use_wave,outdir=outdir,delta_chi2=delta_chi2
+
+if (keyword_set(outdir) eq 0) then outdir=''
+
+DB_schema='vlkb_compactsources'
+DB_table='sed_models'
+user='root'
+db_server=''
+
+phys_par=strupcase(['clump_mass','compact_mass_fraction','clump_upper_age','dust_temp','bolometric_luminosity','random_sample','n_star_tot','m_star_tot','n_star_zams','m_star_zams','l_star_tot','l_star_zams','zams_luminosity_1','zams_mass_1','zams_temperature_1','zams_luminosity_2','zams_mass_2','zams_temperature_2','zams_luminosity_3','zams_mass_3','zams_temperature_3'])
+
+curlcmd='curl --user vialactea:ia2vlkb -L --data "REQUEST=doQuery&VERSION=1.0&LANG=ADQL&FORMAT=csv&QUERY='
+;curl_endpoint='" "http://palantir19.oats.inaf.it:8080/vlkb/sync?"'
+curl_endpoint='" "http://ia2-vialactea.oats.inaf.it:8080/vlkb/sync?"'
+
+
+
+jy2mjy=1000.
+
+; w is the array of SED wavelengths in microns
+; f is the array of the fluxes in Jy (df contains the uncertainties, if defined)
+; distance is in parsec
+; prefilter_thresh is the threshold for prefiltering the model grids at the given USE_WAVE wavelength
+
+; this is an updated version with respect to FIT_SED because it only uses fixed photometric bands and extract them already at the stage of initial model selection 
+; instead of re-extracting each and every pre-selected model a second time. Indeed, most of the computing time was being wasted for the MYSQL calls
+d_ref=1000.
+ref_wave=[3.4,3.6,4.5,4.6,5.8,8.0,12.,22.,24.0,70.,100.,160.,250.,350.,500.,870.,1100.]
+col_names=['WISE1','I1','I2','WISE2','I3','I4','WISE3','WISE4','M1','PACS1','PACS2','PACS3','SPIR1','SPIR2','SPIR3','LABOC','BOL11']
+
+fac_resc=(distance/d_ref)^2.
+delta=1-(prefilter_thresh)
+
+; make sure that only bands for which there are models in the DB are fitted
+match,w_in,ref_wave,q12,q21
+w=w_in(q12) & f=f_in(q12) & df=df_in(q12) & fflag=fflag_in(q12)
+
+; now builds the string for mysql query to dump either the full model or the discrete sed points corresponding to fixed wavelengths
+  
+par_str=''
+par_str_arr=strarr(n_elements(w))
+ret_par_str=''
+phys_str=''
+phys_par_arr=strarr(n_elements(phys_par))
+ret_phys_par=''  
+for k=0,n_elements(w)-1 do begin
+  qw=where(ref_wave-0.05 lt w(k) and ref_wave+0.05 gt w(k), nqw) 
+  par_str=par_str+col_names(qw(0))+','
+  ret_par_str=ret_par_str+col_names(qw(0))+','
+  par_str_arr(k)=strlowcase(col_names(qw(0)))
+endfor
+par_str=strupcase(strmid(par_str,0,strlen(par_str)-1))
+ret_par_str=strlowcase(strmid(ret_par_str,0,strlen(ret_par_str)-1))
+
+for k=0,n_elements(phys_par)-1 do begin
+  phys_str=phys_str+phys_par(k)+','
+  ret_phys_par=ret_phys_par+phys_par(k)+','
+  phys_par_arr(k)=strlowcase(phys_par(k))
+endfor
+phys_str=strupcase(strmid(phys_str,0,strlen(phys_str)-1))
+ret_phys_par=strlowcase(strmid(ret_phys_par,0,strlen(ret_phys_par)-1))
+
+openw,unit1,outdir+'sed_returned.dat', /get_lun
+
+if (keyword_set(use_wave)) then begin
+; make sure that use_wave doe not contain bands for which e have upper limits
+ for bb=0,n_elements(use_wave)-1 do begin
+  qband=where(w eq use_wave(bb))
+  if (qband eq -1) then begin
+    print,'Reference wavelength required ',use_wave(bb),' not found in data file'
+;    return,-1
+;    break
+  endif else begin
+  
+  qrefband=where(ref_wave eq use_wave(bb))
+  match,ref_wave,w,qqueryband,qdummy
+  qulband=where(fflag eq 0, nqulband)
+  ul_str=''
+  if (nqulband gt 0) then begin
+    ul_str=' and '
+    for t=0,nqulband-1 do ul_str=ul_str+'('+col_names(qqueryband(qulband(t)))+"<'"+tostring(f(qulband(t))*jy2mjy*fac_resc)+"') and "
+    if (fflag(qband) eq 1) then ul_str=strmid(ul_str,0,strlen(ul_str)-4)
+    if (fflag(qband) eq 0) then ul_str=strmid(ul_str,4,strlen(ul_str)-4)
+  endif
+  
+  nreq_par=1+n_elements(phys_par_arr)+n_elements(par_str_arr)
+  
+;  if (fflag(qband) eq 1) then dqlcmd="select id,cluster_id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where ("+col_names(qrefband(0))+">'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1-(delta^2.)))+"') and ("+col_names(qrefband(0))+" <'"+tostring(f(qband(0))*jy2mjy*fac_resc*(1+(delta^2.)))+"')"+ul_str ;fluxes are mutliplied by 1000 because model fluxes are in milliJy  
+;  if (fflag(qband) eq 0) then dqlcmd="select id,cluster_id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where ("+col_names(qrefband(0))+" <'"+tostring(f(qband(0))*jy2mjy*fac_resc)+"')"+ul_str ;fluxes are mutliplied by 1000 because model fluxes are in milliJy  
+  if (fflag(qband) eq 1) then dqlcmd="select id,cluster_id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where ("+col_names(qrefband(0))+">"+remove_char(pad0_num(tostring(double(f(qband(0))*jy2mjy*fac_resc*(1-(delta^2.))))),'+')+") and ("+col_names(qrefband(0))+" <"+remove_char(pad0_num(tostring(double(f(qband(0))*jy2mjy*fac_resc*(1+(delta^2.))))),'+')+")"+ul_str ;fluxes are mutliplied by 1000 because model fluxes are in milliJy  
+  if (fflag(qband) eq 0) then dqlcmd="select id,cluster_id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where ("+col_names(qrefband(0))+" <"+remove_char(pad0_num(tostring(double(f(qband(0))*jy2mjy*fac_resc))),'+')+")"+ul_str ;fluxes are mutliplied by 1000 because model fluxes are in milliJy  
+
+  print,curlcmd+dqlcmd+curl_endpoint
+  spawn,curlcmd+dqlcmd+curl_endpoint, data
+  if (bb eq 0) then begin
+    for i=0,n_elements(data)-1 do printf,unit1,data(i)
+  endif else begin
+    for i=1,n_elements(data)-1 do printf,unit1,data(i)
+  endelse
+  
+  endelse
+  
+ endfor      
+endif else begin
+; compute object luminosity from observed SED
+  lum=lbol(w,f,distance)
+; rescaling factor has to stay at 1 if luminosity is used-----WHYYYY ??????
+  fac_resc=1.
+;  dqlcmd="select id,cluster_id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where (bolometric_luminosity >'"+tostring(lum*fac_resc*(1-(delta^2.)))+"') and (bolometric_luminosity <'"+tostring(lum*fac_resc*(1+(delta^2.)))+"')" 
+  dqlcmd="select id,cluster_id,"+phys_str+","+par_str+" from "+db_schema+"."+db_table+" where (bolometric_luminosity >"+remove_char(pad0_num(tostring(double(lum*fac_resc*(1-(delta^2.))))),'+')+") and (bolometric_luminosity <"+remove_char(pad0_num(tostring(double(lum*fac_resc*(1+(delta^2.))))),'+')+")" 
+;  curlcmd='curl --user vialactea:ia2vlkb -L --data "REQUEST=doQuery&VERSION=1.0&LANG=ADQL&FORMAT=csv&QUERY='
+;  curl_endpoint='" "http://palantir19.oats.inaf.it:8080/vlkb/sync?"'
+  spawn,curlcmd+dqlcmd+curl_endpoint, data
+  print,curlcmd+dqlcmd+curl_endpoint
+  for i=0,n_elements(data)-1 do printf,unit1,data(i)
+endelse
+free_lun, unit1
+    
+;  models=read_votable8('sed_returned.dat')
+dmodels=read_csv(outdir+'sed_returned.dat', header=new_tags, count=nlines)
+if (nlines le 1) then return,-1
+old_tags=tag_names(dmodels)
+models=rename_tags(dmodels,old_tags,new_tags)
+
+n_sel=n_elements(models.cluster_id)
+idx=lindgen(n_sel)
+
+;MODIFICARE PER METTERE IL CHI2 NEL FILE DI OUTPUT
+openr,unit1,outdir+'sed_returned.dat', /get_lun
+openw,unit2,outdir+'sedfit_output.dat', /get_lun
+
+s=' '
+readf,unit1,s
+;printf,unit2,'IDX,'+s+',CHI2,DIST'
+printf,unit2,s+',CHI2,DIST'
+
+;establishes weights by assigning equal weights to mid-IR, far-IR and sub-mm
+flag=fix(fflag)
+ul_flag=intarr(n_elements(flag))
+ul_flag(*)=0
+ll_flag=intarr(n_elements(flag))
+ll_flag(*)=0
+dyd=fltarr(n_elements(flag))
+;w1=sqrt(3./7.)
+;w2=sqrt(3./7.)
+;w3=sqrt(3./7.)
+
+if (keyword_set(sed_weights) eq 0) then sed_weights=[3./7.,1./7.,3./7.] 
+renorm=total(sed_weights)
+w1=sqrt(sed_weights(0)/renorm)
+w2=sqrt(sed_weights(1)/renorm)
+w3=sqrt(sed_weights(2)/renorm)
+qmir=where(w lt 25,nqmir)
+qfir=where(w ge 25 and w le 250,nqfir)
+qmm=where(w gt 250,nqmm)
+if (nqmir gt 0) then begin
+  q1=where(flag(qmir) eq 1,nq1,complement=q1neg,ncomplement=nq1neg)
+  if (nq1 gt 0) then dyd(qmir(q1))=sqrt(nq1)/w1
+  if (nq1neg gt 0) then begin
+    dyd(qmir(q1neg))=9999.  ;i.e. it's an upper/lower limit
+    ul_flag(qmir(q1neg))=1
+  endif
+endif
+if (nqfir gt 0) then begin
+  q2=where(flag(qfir) eq 1,nq2,complement=q2neg,ncomplement=nq2neg)
+  if (nq2 gt 0) then dyd(qfir(q2))=sqrt(nq2)/w2
+  if (nq2neg gt 0) then begin
+    dyd(qfir(q2neg))=9999.   ;i.e. it's an upper limit
+    ul_flag(qfir(q2neg))=1
+  endif
+endif
+if (nqmm gt 0) then begin
+  q3=where(flag(qmm) eq 1,nq3,complement=q3neg,ncomplement=nq3neg)
+  if (nq3 gt 0) then dyd(qmm(q3))=sqrt(nq3)/w3
+  if (nq3neg gt 0) then begin
+    dyd(qmm(q3neg))=9999.   ;i.e. it's an upper limit
+    ul_flag(qmm(q3neg))=1
+  endif
+endif
+
+good_flag=1-ul_flag
+
+; now normalize dyd to the minimum value
+; so that we artificially increase the uncertainties of the fluxes where the SED-based weight is lower
+dyd=dyd/min(dyd)
+;print,'DF/F= ',dflux1/flux1
+dyd=dyd*df/f
+
+; now dyd has to be interpreted as a relative uncertainty otherwise the same assigned weight will have more or less importance depending on the level of flux and flux difference between model and observation. I then multiply the "weight" by the flux value
+dyd=dyd*f
+
+; for each model compute the adjusting distance steps
+nstep=10
+dist_arr=distance*(1-(delta^2.))+findgen(nstep)*(distance*(1+(delta^2.))-distance*(1-(delta^2.)))/nstep
+
+nw=n_elements(w)
+invalid_chi2=-999
+
+matrix_models=fltarr(n_sel,nstep,nw)
+matrix_chi2=fltarr(n_sel,nstep) & matrix_chi2(*,*)=invalid_chi2
+matrix_fluxes=fltarr(n_sel,nstep,nw)
+matrix_dfluxes=fltarr(n_sel,nstep,nw)
+
+; create matrix of models
+for i=0,nstep-1 do begin
+;  print,i
+  for k=0,nw-1 do z=execute('matrix_models(*,i,k)=models.'+par_str_arr(k)+'(*)/1000.*((d_ref/dist_arr(i))^2.)')
+endfor
+
+for k=0,nw-1 do matrix_fluxes(*,*,k)=f(k)
+for k=0,nw-1 do matrix_dfluxes(*,*,k)=dyd(k)
+
+print,'fatte matrici flussi'
+
+dmat=((matrix_models-matrix_fluxes)^2.)/(matrix_dfluxes^2.)
+
+matrix_chi2=total(dmat,3)
+
+print,'fatta matrice chi2'
+
+;select by CHI2
+if (keyword_set(delta_chi2)) then dchi2=delta_chi2 else dchi2=1.
+
+qchi2=where(matrix_chi2 le min(matrix_chi2)+dchi2, nqchi2)
+
+;par=replicate({idx:0L, cluster_id:' ', clump_mass:0.d0, compact_mass_fraction: 0.d0, compact_mass_desired: 0.d0, compact_mass_actual: 0.d0, clump_age:0.d0, dust_temp:0.d0, lum_bol:0.d0, sample:0, n_star_tot:0., m_star_tot:0., n_star_zams:0., m_star_zams:0., l_star_tot:0., l_star_zams:0., zams_luminosity_1:0., zams_mass_1:0., zams_temperature_1:0., zams_luminosity_2:0., zams_mass_2:0., zams_temperature_2:0., zams_luminosity_3:0., zams_mass_3:0., zams_temperature_3:0., d:0.d0, chi2:0.d0, wmod:ptr_new(), fmod:ptr_new()},nqchi2)
+par=replicate({id:0L, cluster_id:' ', clump_mass:0.d0, compact_mass_fraction: 0.d0, clump_upper_age:0.d0, dust_temp:0.d0, bolometric_luminosity:0.d0, random_sample:0, n_star_tot:0., m_star_tot:0., n_star_zams:0., m_star_zams:0., l_star_tot:0., l_star_zams:0., zams_luminosity_1:0., zams_mass_1:0., zams_temperature_1:0., zams_luminosity_2:0., zams_mass_2:0., zams_temperature_2:0., zams_luminosity_3:0., zams_mass_3:0., zams_temperature_3:0., d:0.d0, chi2:0.d0, wmod:ptr_new(), fmod:ptr_new()},nqchi2)
+
+wheretomulti,matrix_chi2,qchi2,mod_chi2,dist_chi2
+
+print,'wheretomulti'
+		
+print,'selezionati inizialmente ',nqchi2,' modelli'
+
+; wmod and fmod can be addressed as, e.g., print,*(res(i).fmod)
+;	readf,unit1,s
+;	printf,unit2,s+','+tostring(p.chi2)+','+tostring(p.d)
+
+par.id=models.id(mod_chi2)
+par.cluster_id=models.cluster_id(mod_chi2)
+par.clump_mass=models.clump_mass(mod_chi2)
+par.compact_mass_fraction=models.compact_mass_fraction(mod_chi2)
+;par.compact_mass_desired=models.compact_mass_desired(mod_chi2)
+;par.compact_mass_actual=models.compact_mass_actual(mod_chi2)
+par.clump_upper_age=models.clump_upper_age(mod_chi2)
+par.bolometric_luminosity=models.bolometric_luminosity(mod_chi2)
+par.random_sample=models.random_sample(mod_chi2)
+par.dust_temp=models.dust_temp(mod_chi2)
+par.n_star_tot=models.n_star_tot(mod_chi2)
+par.m_star_tot=models.m_star_tot(mod_chi2)
+par.n_star_zams=models.n_star_zams(mod_chi2)
+par.m_star_zams=models.m_star_zams(mod_chi2)
+par.l_star_tot=models.l_star_tot(mod_chi2)
+par.l_star_zams=models.l_star_zams(mod_chi2)
+par.zams_luminosity_1=models.zams_luminosity_1(mod_chi2)
+par.zams_mass_1=models.zams_mass_1(mod_chi2)
+par.zams_temperature_1=models.zams_temperature_1(mod_chi2)
+par.zams_luminosity_2=models.zams_luminosity_2(mod_chi2)
+par.zams_mass_2=models.zams_mass_2(mod_chi2)
+par.zams_temperature_2=models.zams_temperature_2(mod_chi2)
+par.zams_luminosity_3=models.zams_luminosity_3(mod_chi2)
+par.zams_mass_3=models.zams_mass_3(mod_chi2)
+par.zams_temperature_3=models.zams_temperature_3(mod_chi2)
+par.d=dist_arr(dist_chi2)
+par.chi2=matrix_chi2(qchi2)
+par.wmod=ptr_new(w)
+z=ptrarr(nqchi2)
+for i=0,nqchi2-1 do z(i)=ptr_new(matrix_models(mod_chi2(i),dist_chi2(i),*))
+par.fmod=z
+	
+; it is much easier if I first write down the output products (the PAR structure and the contents of the SEDFIT_OUTPUT file, and THEN eliminate the duplicate records rather than doing it on the fly 
+
+output_string=strarr(nqchi2)
+for i=0,nqchi2-1 do begin
+  str_fit=' ' & for k=0,n_elements(w)-1 do str_fit=str_fit+tostring(reform(matrix_models(mod_chi2(i),dist_chi2(i),k)))+','
+  str_fit=strmid(str_fit,1,strlen(str_fit)-1)
+;  str_idx=tostring(idx(mod_chi2(i)))+','+models.cluster_id(mod_chi2(i))+','
+  str_idx=tostring(models.id(mod_chi2(i)))+','+models.cluster_id(mod_chi2(i))+','
+  str_phys=' ' & for k=0,n_elements(phys_par_arr)-1 do z=execute('str_phys=str_phys+tostring(par(i).'+phys_par_arr(k)+')+","')
+  str_phys=strmid(str_phys,1,strlen(str_phys)-1)
+;  output_string(i)=str_idx+tostring(par(i).cluster_id)+','+tostring(par(i).clump_mass)+','+tostring(par(i).compact_mass_fraction)+','+tostring(par(i).compact_mass_desired)+','+tostring(par(i).compact_mass_actual)+','+tostring(par(i).clump_age)+','+tostring(par(i).dust_temp)+','+str_fit+tostring(par(i).chi2)+','+tostring(par(i).d)
+  output_string(i)=str_idx+str_phys+str_fit+tostring(par(i).chi2)+','+tostring(par(i).d)
+endfor
+
+print,' done writing full output file.....now eliminating duplications'
+
+;now get rid of duplicated models where the only thing changing is the adjusted distance
+keep_yn=intarr(nqchi2) & keep_yn(*)=0   ;initialize at discard flag value
+umod=uniq(idx(mod_chi2))
+;generate array of flags for records to be kept or discarded
+; do the cycle because for each set of duplicated models I have to keep the one with least CHI2
+for i=0,n_elements(umod)-1 do begin
+  qu=where(par.id eq models.id(mod_chi2(umod(i))), nqu)
+  if (nqu gt 1) then begin
+	qu1=where(par(qu).chi2 eq min(par(qu).chi2),complement=qu1_dupl)
+    keep_yn(qu(qu1(0)))=1
+  endif else begin
+	keep_yn(qu(0))=1
+  endelse
+endfor
+
+; now remove duplicate elements
+qkeep=where(keep_yn eq 1, nqkeep)
+par2=par(qkeep)
+output_string2=output_string(qkeep)
+
+for i=0,nqkeep-1 do printf,unit2,output_string2(i)
+
+print,'selezionati ',nqkeep,' modelli dopo rimozione duplicazioni'
+
+free_lun,unit1,unit2
+
+return,par2
+
+end
+
+
+
+
+
+
+
+
+
diff --git a/Code/script_idl_mv/wheretomulti.pro b/Code/script_idl_mv/wheretomulti.pro
new file mode 100644
index 0000000000000000000000000000000000000000..81ee3ccdec381cd70b7a37ee1a7985c2ee8830d0
--- /dev/null
+++ b/Code/script_idl_mv/wheretomulti.pro
@@ -0,0 +1,62 @@
+;************************ Beginning of wheretomulti.pro ************************
+
+PRO wheretomulti, Array, Indices, Col, Row, Frame
+;+
+; NAME:		wheretomulti.pro
+;
+; FUNCTION:	Convert WHERE output to 2d or 3d indices
+;
+; USAGE:	WhereToMulti, Array, Indices, Col, Row, Frame
+;
+; INPUT ARGUMENTS: 
+;   Array: the array that was WHERE'd
+;   Indices: the indices returned by WHERE
+;
+; OUTPUT ARGUMENTS: 
+;   Col:     Indices to first dimension.
+;   Row:     Indices to second dimension.
+;   Frame:   Indices to third dimension. Returned only for 3-d array.
+;
+; OPTIONAL ARGUMENTS: 
+;
+; KEYWORDS: 
+;
+; REQUIRED MODULES: 
+;
+; SIDE EFFECTS: 
+;
+; ERROR HANDLING:
+;   If Array is not a vector or matrix, all return values are set to zero
+;   and a message is written to the screen.
+;
+; NOTES:
+;
+; HISTORY:
+; 1998 Sept 15	J.L.Saba	Developed based on code from David Fanning's
+;                               web site.
+;
+;- End of prologue -------------------------------------------------------------
+
+   s    = SIZE ( Array )
+
+   NCol = s[1]
+   Col  = Indices MOD NCol
+
+   IF s[0] EQ 2 THEN BEGIN              ; 2-d array
+      Row = Indices / NCol
+   ENDIF ELSE IF s[0] EQ 3 THEN BEGIN   ; 3-d array
+      NRow  = s(2)
+      Row   = ( Indices / NCol ) MOD NRow
+      Frame = Indices / ( NRow * NCol )
+   ENDIF ELSE BEGIN                     ; neither 2d or 3d
+      Col   = 0
+      Row   = 0
+      Frame = 0
+      PRINT, 'WhereToMulti called with bad input. Array not a vector or matrix.'
+      HELP, Array
+   ENDELSE
+
+   RETURN
+END
+
+;*************************** End of wheretomulti.pro ***************************
diff --git a/Code/sed_fit/certs.zip b/Code/sed_fit/certs.zip
new file mode 100644
index 0000000000000000000000000000000000000000..f4d811fa7c75da14cef58070aa6624f46d6623ed
Binary files /dev/null and b/Code/sed_fit/certs.zip differ
diff --git a/Code/sed_fit/execute.bin b/Code/sed_fit/execute.bin
new file mode 100644
index 0000000000000000000000000000000000000000..eff154404606c8fcbb16b8222f863479773e9919
--- /dev/null
+++ b/Code/sed_fit/execute.bin
@@ -0,0 +1,4 @@
+#!/bin/bash
+unzip scripts.zip
+/usr/local/bin/idl -e "sedpar=vialactea_tap_sedfit_v3([160,250,350,500],[4.82534,4.65167,4.04317,3.5252],[0.571259,0.392511,0.337027,0.526754],[1,1,1,1],2000.,0.8,sed_weights=[1.,1.,1.],use_wave=[160,160,500])" &> log.dat
+zip output.zip *dat
diff --git a/Code/sed_fit/inputs.zip b/Code/sed_fit/inputs.zip
new file mode 100644
index 0000000000000000000000000000000000000000..9c2ac9fe655dad390b41675641bb7488aa466f5b
Binary files /dev/null and b/Code/sed_fit/inputs.zip differ
diff --git a/Code/sed_fit/portmapping.txt b/Code/sed_fit/portmapping.txt
new file mode 100644
index 0000000000000000000000000000000000000000..05043794278c7e1273fb6e26d7b9d49bfd005a45
--- /dev/null
+++ b/Code/sed_fit/portmapping.txt
@@ -0,0 +1,2 @@
+scripts.zip=sed_fit_2015-12-21-172628/executable/0
+execute.bin=sed_fit_2015-12-21-172628/executable
diff --git a/Code/sed_fit/scripts.zip b/Code/sed_fit/scripts.zip
new file mode 100755
index 0000000000000000000000000000000000000000..70fb109dd460dafbc2734f70791806ad64a2cc88
Binary files /dev/null and b/Code/sed_fit/scripts.zip differ
diff --git a/Code/sed_fit/workflow.xml b/Code/sed_fit/workflow.xml
new file mode 100755
index 0000000000000000000000000000000000000000..fe6775a4a7c78989b80cfa7d16e4e72149957efa
--- /dev/null
+++ b/Code/sed_fit/workflow.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<workflow download="all" export="proj" mainabst="" maingraf="sed_fit_2015-12-21-172628" mainreal="sed_fit_2015-12-21-172628" name="sed_fit_2015-12-21-172628">
+   <graf name="sed_fit_2015-12-21-172628" text="Description of Graph">
+      <job name="executable" text="Description of Job" x="66" y="113">
+         <input name="scripts.zip" prejob="" preoutput="" seq="0" text="Description of Port" x="65" y="98"/>
+         <output name="output.zip" seq="1" text="Description of Port" x="81" y="98"/>
+      </job>
+   </graf>
+   <real abst="" graf="sed_fit_2015-12-21-172628" name="sed_fit_2015-12-21-172628" text="2015-4-2">
+      <job name="executable" text="Description of Job" x="66" y="113">
+         <input name="scripts.zip" prejob="" preoutput="" seq="0" text="Description of Port" x="65" y="98">
+            <port_prop desc="null" inh="null" key="eparam" label="null" value="0"/>
+            <port_prop desc="null" inh="null" key="file" label="null" value="C:/fakepath/scripts.zip"/>
+            <port_prop desc="null" inh="null" key="intname" label="null" value="scripts.zip"/>
+            <port_prop desc="null" inh="null" key="dpid" label="null" value="0"/>
+            <port_prop desc="null" inh="null" key="pequaltype" label="null" value="0"/>
+         </input>
+         <output name="output.zip" seq="1" text="Description of Port" x="81" y="98">
+            <port_prop desc="null" inh="null" key="maincount0" label="null" value="1"/>
+            <port_prop desc="null" inh="null" key="type0" label="null" value="permament"/>
+            <port_prop desc="null" inh="null" key="intname" label="null" value="output.zip"/>
+            <port_prop desc="null" inh="null" key="maincount" label="null" value="1"/>
+         </output>
+         <execute desc="" inh="" key="module" label="" value="true"/>
+         <execute desc="null" inh="null" key="binary" label="null" value="C:/fakepath/execute.bin"/>
+         <execute desc="null" inh="null" key="gridtype" label="null" value="pbs"/>
+         <execute desc="null" inh="null" key="jobmanager" label="null" value="-"/>
+         <execute desc="null" inh="null" key="jobistype" label="null" value="binary"/>
+         <execute desc="null" inh="null" key="resource" label="null" value="high"/>
+         <execute desc="null" inh="null" key="grid" label="null" value="muoni-server-02.oact.inaf.it"/>
+         <execute desc="null" inh="null" key="type" label="null" value="Sequence"/>
+         <history mdyid="modify.input.file" nvalue="C:/fakepath/scripts.zip" ovalue="C:/fakepath/input.zip" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="modify.job.binary" nvalue="C:/fakepath/execute.bin" ovalue="C:/fakepath/script.sh" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="modify.input.file" nvalue="C:/fakepath/input.zip" ovalue="C:/fakepath/script.sh" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="modify.input.file" nvalue="C:/fakepath/script.sh" ovalue="C:/fakepath/input.zip" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="modify.job.breaktimeout" nvalue="0" ovalue="60" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.output.maincount" nvalue="1" ovalue="" port="1" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.output.type0" nvalue="permament" ovalue="" port="1" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.output.intname" nvalue="output.zip" ovalue="" port="1" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.output.maincount0" nvalue="1" ovalue="" port="1" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.pequaltype" nvalue="0" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.dpid" nvalue="0" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.intname" nvalue="scripts.zip" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.file" nvalue="C:/fakepath/input.zip" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.eparam" nvalue="0" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.breaktimeout" nvalue="60" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.type" nvalue="Sequence" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.grid" nvalue="muoni-server-02.oact.inaf.it" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.resource" nvalue="high" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.breakpoint" nvalue="none" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.jobistype" nvalue="binary" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.jobmanager" nvalue="-" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.binary" nvalue="C:/fakepath/script.sh" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.gridtype" nvalue="pbs" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+      </job>
+      <instances>
+         <instance name="text" rtid="857454547494zentest" value="2015-12-21 17:27"/>
+         <instance name="wftype" rtid="*" value="null"/>
+         <instance name="appmain" rtid="*" value="true"/>
+         <instance name="storage" rtid="857454547494zentest" value="http://muoni-server-01.oact.inaf.it:8081/storage"/>
+         <instance name="status" rtid="857454547494zentest" value="6"/>
+         <jobstatus job="executable" pid="0" resource="muoni-server-02.oact.inaf.it/high" rtid="857454547494zentest" status="6"/>
+      </instances>
+   </real>
+</workflow>
diff --git a/Code/sed_fit/workflow.xml.orig b/Code/sed_fit/workflow.xml.orig
new file mode 100755
index 0000000000000000000000000000000000000000..e19727790634a5a334a1205009079017fff42212
--- /dev/null
+++ b/Code/sed_fit/workflow.xml.orig
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<workflow download="all" export="proj" mainabst="" maingraf="sed_fit_2015-12-21-172628" mainreal="sed_fit_2015-12-21-172628" name="sed_fit_2015-12-21-172628">
+   <graf name="sed_fit_2015-12-21-172628" text="Description of Graph">
+      <job name="executable" text="Description of Job" x="66" y="113">
+         <input name="scripts.zip" prejob="" preoutput="" seq="0" text="Description of Port" x="65" y="98"/>
+         <output name="output.zip" seq="1" text="Description of Port" x="81" y="98"/>
+      </job>
+   </graf>
+   <real abst="" graf="sed_fit_2015-12-21-172628" name="sed_fit_2015-12-21-172628" text="2015-4-2">
+      <job name="executable" text="Description of Job" x="66" y="113">
+         <input name="scripts.zip" prejob="" preoutput="" seq="0" text="Description of Port" x="65" y="98">
+            <port_prop desc="null" inh="null" key="eparam" label="null" value="0"/>
+            <port_prop desc="null" inh="null" key="file" label="null" value="C:/fakepath/scripts.zip"/>
+            <port_prop desc="null" inh="null" key="intname" label="null" value="scripts.zip"/>
+            <port_prop desc="null" inh="null" key="dpid" label="null" value="0"/>
+            <port_prop desc="null" inh="null" key="pequaltype" label="null" value="0"/>
+         </input>
+         <output name="output.zip" seq="1" text="Description of Port" x="81" y="98">
+            <port_prop desc="null" inh="null" key="maincount0" label="null" value="1"/>
+            <port_prop desc="null" inh="null" key="type0" label="null" value="permament"/>
+            <port_prop desc="null" inh="null" key="intname" label="null" value="output.zip"/>
+            <port_prop desc="null" inh="null" key="maincount" label="null" value="1"/>
+         </output>
+         <execute desc="" inh="" key="module" label="" value="true"/>
+         <execute desc="null" inh="null" key="binary" label="null" value="C:/fakepath/execute.bin"/>
+         <execute desc="null" inh="null" key="gridtype" label="null" value="pbs"/>
+         <execute desc="null" inh="null" key="jobmanager" label="null" value="-"/>
+         <execute desc="null" inh="null" key="jobistype" label="null" value="binary"/>
+         <execute desc="null" inh="null" key="resource" label="null" value="vialactea"/>
+         <execute desc="null" inh="null" key="grid" label="null" value="muoni-server-02.oact.inaf.it"/>
+         <execute desc="null" inh="null" key="type" label="null" value="Sequence"/>
+         <history mdyid="modify.input.file" nvalue="C:/fakepath/scripts.zip" ovalue="C:/fakepath/input.zip" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="modify.job.binary" nvalue="C:/fakepath/execute.bin" ovalue="C:/fakepath/script.sh" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="modify.input.file" nvalue="C:/fakepath/input.zip" ovalue="C:/fakepath/script.sh" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="modify.input.file" nvalue="C:/fakepath/script.sh" ovalue="C:/fakepath/input.zip" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="modify.job.breaktimeout" nvalue="0" ovalue="60" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.output.maincount" nvalue="1" ovalue="" port="1" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.output.type0" nvalue="permament" ovalue="" port="1" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.output.intname" nvalue="output.zip" ovalue="" port="1" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.output.maincount0" nvalue="1" ovalue="" port="1" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.pequaltype" nvalue="0" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.dpid" nvalue="0" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.intname" nvalue="scripts.zip" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.file" nvalue="C:/fakepath/input.zip" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.input.eparam" nvalue="0" ovalue="" port="0" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.breaktimeout" nvalue="60" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.type" nvalue="Sequence" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.grid" nvalue="muoni-server-02.oact.inaf.it" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.resource" nvalue="vialactea" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.breakpoint" nvalue="none" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.jobistype" nvalue="binary" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.jobmanager" nvalue="-" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.binary" nvalue="C:/fakepath/script.sh" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+         <history mdyid="new.job.gridtype" nvalue="pbs" ovalue="" port="-" tim="2015-12-21-17:26:28" user="10812"/>
+      </job>
+      <instances>
+         <instance name="text" rtid="857454547494zentest" value="2015-12-21 17:27"/>
+         <instance name="wftype" rtid="*" value="null"/>
+         <instance name="appmain" rtid="*" value="true"/>
+         <instance name="storage" rtid="857454547494zentest" value="http://muoni-server-01.oact.inaf.it:8081/storage"/>
+         <instance name="status" rtid="857454547494zentest" value="6"/>
+         <jobstatus job="executable" pid="0" resource="muoni-server-02.oact.inaf.it/vialactea" rtid="857454547494zentest" status="6"/>
+      </instances>
+   </real>
+</workflow>
diff --git a/Code/src/.DS_Store b/Code/src/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..44b0b869fcee97e0c30cf88b68f5744fdc6ee64f
Binary files /dev/null and b/Code/src/.DS_Store differ
diff --git a/Code/src/LoadingWidget.cpp b/Code/src/LoadingWidget.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f867d5080afcad0cefcfb73078765302968191dd
--- /dev/null
+++ b/Code/src/LoadingWidget.cpp
@@ -0,0 +1,41 @@
+#include "loadingwidget.h"
+#include "ui_loadingwidget.h"
+#include <QDebug>
+
+LoadingWidget::LoadingWidget(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::LoadingWidget)
+{
+    ui->setupUi(this);
+    //this->setWindowFlags(this->windowFlags() |  Qt::WindowStaysOnTopHint);
+
+}
+
+LoadingWidget::~LoadingWidget()
+{
+    delete ui;
+}
+
+void LoadingWidget::init()
+{
+    ui->progressBar->setMaximum(0);
+    ui->progressBar->setValue(0);
+}
+
+void LoadingWidget::setFileName(QString name)
+{
+    ui->titleLabel->setText(name);
+}
+
+void LoadingWidget::loadingEnded()
+{
+    ui->progressBar->setMaximum(100);
+    ui->progressBar->setValue(100);
+
+    ui->dismissPushButton->setEnabled(true);
+}
+
+void LoadingWidget::on_dismissPushButton_clicked()
+{
+    close();
+}
diff --git a/Code/src/aboutform.cpp b/Code/src/aboutform.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..27f29f608ac484603ecf39e85b1a25f3169a5639
--- /dev/null
+++ b/Code/src/aboutform.cpp
@@ -0,0 +1,14 @@
+#include "aboutform.h"
+#include "ui_aboutform.h"
+
+AboutForm::AboutForm(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::AboutForm)
+{
+    ui->setupUi(this);
+}
+
+AboutForm::~AboutForm()
+{
+    delete ui;
+}
diff --git a/Code/src/aboutform.h b/Code/src/aboutform.h
new file mode 100644
index 0000000000000000000000000000000000000000..6aaebe200d413c747ee88979568bfa51c4af991d
--- /dev/null
+++ b/Code/src/aboutform.h
@@ -0,0 +1,22 @@
+#ifndef ABOUTFORM_H
+#define ABOUTFORM_H
+
+#include <QWidget>
+
+namespace Ui {
+class AboutForm;
+}
+
+class AboutForm : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit AboutForm(QWidget *parent = 0);
+    ~AboutForm();
+
+private:
+    Ui::AboutForm *ui;
+};
+
+#endif // ABOUTFORM_H
diff --git a/Code/src/astroutils.cpp b/Code/src/astroutils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..49edc85bf5b7a8e6927843eef477787e1d254282
--- /dev/null
+++ b/Code/src/astroutils.cpp
@@ -0,0 +1,957 @@
+#include "astroutils.h"
+#include <QDebug>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <math.h>
+#include "libwcs/wcs.h"
+#include "libwcs/fitsfile.h"
+#include "libwcs/wcscat.h"
+#include "libwcs/lwcs.h"
+
+#include <iostream>
+#include <vector>
+#include <string>
+
+#include <boost/algorithm/string/split.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string.hpp>
+using namespace boost::algorithm;
+
+
+extern void setsys();
+
+AstroUtils::AstroUtils()
+{
+}
+
+
+double AstroUtils::arcsecPixel(std::string file)
+{
+
+    char *fn= new char[file.length() + 1];
+    strcpy(fn, file.c_str());
+
+    struct WorldCoor *wcs;
+    char *header;
+    double cra, cdec, dra, ddec, secpix;
+    int wp, hp;
+    int sysout = 0;
+    double eqout = 0.0;
+
+    header = GetFITShead (fn, 0);
+
+    wcs= GetFITSWCS (fn,header,0,&cra,&cdec,&dra,&ddec,&secpix, &wp, &hp, &sysout, &eqout);
+
+    return secpix;
+}
+
+
+//void AstroUtils::xy2sky(QString map,float x, float y,double* coord,int wcs_type)
+void AstroUtils::xy2sky(std::string map,float x, float y,double* coord,int wcs_type)
+{
+    struct WorldCoor *wcs;
+    char *fn=new char[map.length() + 1];;
+    static char coorsys[16];
+    char wcstring[64];
+    char lstr = 64;
+    *coorsys = 0;
+
+
+
+    strcpy(fn, map.c_str());
+    //    fn=map.c_str();
+    wcs = GetWCSFITS (fn, 0);
+
+    wcs->sysout = wcs_type;
+    // force the set of wcs in degree
+
+
+    setwcsdeg(wcs,1);
+    if(wcs_type==WCS_GALACTIC)
+    {
+        wcs->eqout = 2000.0;
+    }
+    else if ( wcs_type = WCS_J2000)
+    {
+    }
+
+    if (pix2wcst (wcs, x, y, wcstring, lstr))
+    {
+
+        std::string str(wcstring);
+        using namespace boost::algorithm;
+        std::vector<std::string> tokens;
+
+        trim(str);
+
+        split(tokens, str, is_any_of(" "),boost::token_compress_on);
+
+        coord[0]=atof(tokens[0].c_str());
+        coord[1]=atof(tokens[1].c_str());
+
+
+
+
+    }
+
+    delete [] fn;
+
+
+}
+
+int AstroUtils::getSysOut( std::string file )
+{
+
+
+    double x1,x2,y1,y2;
+    double d1, d2, r, diffi;
+    double pos1[3], pos2[3], w, diff;
+    int i;
+
+    x1=40.25058;
+
+    y1=-0.98334;
+
+    x2= 40.30152;
+
+    y2=-0.30171;
+
+    /* Convert two vectors to direction cosines */
+    r = 1.0;
+    d2v3 (x1, y1, r, pos1);
+    d2v3 (x2, y2, r, pos2);
+
+    /* Modulus squared of half the difference vector */
+    w = 0.0;
+    for (i = 0; i < 3; i++) {
+        diffi = pos1[i] - pos2[i];
+        w = w + (diffi * diffi);
+        }
+    w = w / 4.0;
+    if (w > 1.0) w = 1.0;
+
+    /* Angle beween the vectors */
+    diff = 2.0 * atan2 (sqrt (w), sqrt (1.0 - w));
+    diff = raddeg (diff);
+
+    w = 0.0;
+    d1 = 0.0;
+    d2 = 0.0;
+    for (i = 0; i < 3; i++) {
+        w = w + (pos1[i] * pos2[i]);
+        d1 = d1 + (pos1[i] * pos1[i]);
+        d2 = d2 + (pos2[i] * pos2[i]);
+        }
+    diff = acosdeg (w / (sqrt (d1) * sqrt (d2)));
+
+
+
+
+
+
+    char *fn= new char[file.length() + 1];
+    strcpy(fn, file.c_str());
+
+    struct WorldCoor *wcs;
+    char *header;
+    double cra, cdec, dra, ddec, secpix;
+    int wp, hp;
+    int sysout = 0;
+    double eqout = 0.0;
+
+
+    //sysout=WCS_GALACTIC;
+    header = GetFITShead (fn, 0);
+
+    wcs = GetFITSWCS (fn,header,1,&cra,&cdec,&dra,&ddec,&secpix, &wp, &hp, &sysout, &eqout);
+
+    return wcs->sysout;
+}
+
+void AstroUtils::getRotationAngle( std::string file )
+{
+
+
+    //char *fn;
+    // do stuff
+
+    char *fn= new char[file.length() + 1];
+    strcpy(fn, file.c_str());
+
+    struct WorldCoor *wcs;
+    char *header;
+    double cra, cdec, dra, ddec, secpix;
+    int wp, hp;
+    int sysout = 0;
+    double eqout = 0.0;
+
+
+    header = GetFITShead (fn, 0);
+
+    wcs = GetFITSWCS (fn,header,1,&cra,&cdec,&dra,&ddec,&secpix, &wp, &hp, &sysout, &eqout);
+
+
+
+    wcsoutinit(wcs,"GALACTIC");
+
+
+    int off;
+    double xc, xn, xe, yc, yn, ye;
+
+    /* If image is one-dimensional, leave rotation angle alone */
+    if (wcs->nxpix < 1.5 || wcs->nypix < 1.5) {
+        wcs->imrot = wcs->rot;
+        wcs->pa_north = wcs->rot + 90.0;
+        wcs->pa_east = wcs->rot + 180.0;
+        return;
+    }
+
+
+    /* Do not try anything if image is LINEAR (not Cartesian projection) */
+    if (wcs->syswcs == WCS_LINEAR)
+        return;
+
+    wcs->xinc = fabs (wcs->xinc);
+    wcs->yinc = fabs (wcs->yinc);
+
+    /* Compute position angles of North and East in image */
+    xc = wcs->xrefpix;
+    yc = wcs->yrefpix;
+    pix2wcs (wcs, xc, yc, &cra, &cdec);
+    if (wcs->coorflip) {
+        wcs2pix (wcs, cra+wcs->yinc, cdec, &xe, &ye, &off);
+        wcs2pix (wcs, cra, cdec+wcs->xinc, &xn, &yn, &off);
+    }
+    else {
+        wcs2pix (wcs, cra+wcs->xinc, cdec, &xe, &ye, &off);
+        wcs2pix (wcs, cra, cdec+wcs->yinc, &xn, &yn, &off);
+    }
+    wcs->pa_north = raddeg (atan2 (yn-yc, xn-xc));
+    if (wcs->pa_north < -90.0)
+        wcs->pa_north = wcs->pa_north + 360.0;
+    wcs->pa_east = raddeg (atan2 (ye-yc, xe-xc));
+    if (wcs->pa_east < -90.0)
+        wcs->pa_east = wcs->pa_east + 360.0;
+
+    /* Compute image rotation angle from North */
+    if (wcs->pa_north < -90.0)
+        wcs->imrot = 270.0 + wcs->pa_north;
+    else
+        wcs->imrot = wcs->pa_north - 90.0;
+
+    /* Compute CROTA */
+    if (wcs->coorflip) {
+        wcs->rot = wcs->imrot + 90.0;
+        if (wcs->rot < 0.0)
+            wcs->rot = wcs->rot + 360.0;
+    }
+    else
+        wcs->rot = wcs->imrot;
+    if (wcs->rot < 0.0)
+        wcs->rot = wcs->rot + 360.0;
+    if (wcs->rot >= 360.0)
+        wcs->rot = wcs->rot - 360.0;
+
+    /* Set image mirror flag based on axis orientation */
+    wcs->imflip = 0;
+    if (wcs->pa_east - wcs->pa_north < -80.0 &&
+            wcs->pa_east - wcs->pa_north > -100.0)
+        wcs->imflip = 1;
+    if (wcs->pa_east - wcs->pa_north < 280.0 &&
+            wcs->pa_east - wcs->pa_north > 260.0)
+        wcs->imflip = 1;
+    if (wcs->pa_north - wcs->pa_east > 80.0 &&
+            wcs->pa_north - wcs->pa_east < 100.0)
+        wcs->imflip = 1;
+    if (wcs->coorflip) {
+        if (wcs->imflip)
+            wcs->yinc = -wcs->yinc;
+    }
+    else {
+        if (!wcs->imflip)
+            wcs->xinc = -wcs->xinc;
+    }
+
+
+    delete [] fn;
+
+}
+
+//bool AstroUtils::sky2xy(QString map,double ra, double dec,double* coord)
+bool AstroUtils::sky2xy(std::string map, double ra, double dec, double* coord)
+{
+
+    //char *fn;
+    char *fn=new char[map.length() + 1];;
+
+    struct WorldCoor *wcs;
+    char *header;
+    double cra, cdec, dra, ddec, secpix;
+    int wp, hp;
+    int sysout = 0;
+    double eqout = 0.0;
+    double x, y, ra0, dec0;
+    int sysin;
+    char csys[16];
+    double eqin = 0.0;
+    int offscale;
+
+    strcpy(fn, map.c_str());
+    //    fn=map.c_str();
+
+    header = GetFITShead (fn, 0);
+
+    wcs = GetFITSWCS (fn,header,0,&cra,&cdec,&dra,&ddec,&secpix, &wp, &hp, &sysout, &eqout);
+
+
+    ra0=ra;
+    dec0=dec;
+
+    if (wcs->prjcode < 0)
+        strcpy (csys, "PIXEL");
+    else if (wcs->prjcode < 2)
+        strcpy (csys, "LINEAR");
+    else
+        strcpy (csys, wcs->radecsys);
+
+    sysin = wcscsys (csys);
+    eqin = wcsceq (csys);
+
+    if (wcs->syswcs > 0 && wcs->syswcs != 6 && wcs->syswcs != 10)
+        wcscon (sysin, wcs->syswcs, eqin, eqout, &ra, &dec, wcs->epoch);
+
+
+
+    wcsc2pix (wcs, ra0, dec0, csys, &x, &y, &offscale);
+
+
+    //    delete[] fn;
+    delete[] header;
+    delete[] wcs;
+
+
+    /*
+ // COMMENTATO FV PER AGGIUNGERE LAYER, ANCHE SE IMMAGINE E' FUORI RESTITUISCE OFFSET
+
+    if (offscale == 2){
+        return false;
+    }
+    else if (offscale){
+        return false;
+    }
+*/
+    coord[0]= x;
+    coord[1]= y;
+    coord[2]= secpix;
+
+
+
+    delete []fn;
+    return true;
+}
+
+
+//QUI
+static double secpix0 = PSCALE;		/* Set image scale--override header */
+static int usecdelt = 0;		/* Use CDELT if 1, else CD matrix */
+static int hp0 = 0;			/* Initial height of image */
+static int wp0 = 0;			/* Initial width of image */
+static double ra0 = -99.0;		/* Initial center RA in degrees */
+static double dec0 = -99.0;		/* Initial center Dec in degrees */
+static int comsys = WCS_J2000;		/* Command line center coordinte system */
+static int ptype0 = -1;			/* Projection type to fit */
+static int  nctype = 28;		/* Number of possible projections */
+static char ctypes[32][4];		/* 3-letter codes for projections */
+static double xref0 = -99999.0;		/* Reference pixel X coordinate */
+static double yref0 = -99999.0;		/* Reference pixel Y coordinate */
+static double secpix2 = PSCALE;		/* Set image scale 2--override header */
+static double *cd0 = NULL;		/* Set CD matrix--override header */
+static double rot0 = 361.0;		/* Initial image rotation */
+static char *dateobs0 = NULL;		/* Initial DATE-OBS value in FITS date format */
+
+//END
+
+
+
+
+WorldCoor* AstroUtils::GetFITSWCS (char *filename, char	*header, int verbose, double *cra, double *cdec,double	*dra, double *ddec, double *secpix, int *wp,int	*hp,int	*sysout ,double	*eqout)
+{
+
+    int naxes;
+    double eq1, x, y;
+    double ra1, dec1, dx, dy;
+    double xmin, xmax, ymin, ymax, ra2, dec2, ra3, dec3, ra4, dec4;
+    double dra0, dra1, dra2, dra3, dra4;
+    struct WorldCoor *wcs;
+    char rstr[64], dstr[64], cstr[16];
+
+
+    /* Initialize WCS structure from possibly revised FITS header */
+
+    wcs = ChangeFITSWCS (filename, header, verbose);
+    if (wcs == NULL) {
+        return (NULL);
+    }
+    *hp = (int) wcs->nypix;
+    *wp = (int) wcs->nxpix;
+
+    /* If incomplete WCS in header, drop out */
+    if (nowcs (wcs)) {
+        setwcsfile (filename);
+        /* wcserr(); */
+        if (verbose)
+            fprintf (stderr,"Insufficient information for initial WCS\n");
+        return (NULL);
+    }
+
+    /* If in linear coordinates, do not print as sexigesimal */
+    if (wcs->sysout < 1 || wcs->sysout == 6 || wcs->sysout == 10)
+        wcs->degout = 1;
+
+    /* Set flag to get appropriate equinox for catalog search */
+    if (!*sysout)
+        *sysout = wcs->syswcs;
+    if (*eqout == 0.0)
+        *eqout = wcs->equinox;
+    eq1 = wcs->equinox;
+    if (wcs->coorflip) {
+        ra1 = wcs->crval[1];
+        dec1 = wcs->crval[0];
+    }
+    else {
+        ra1 = wcs->crval[0];
+        dec1 = wcs->crval[1];
+    }
+
+    /* Print reference pixel position and value */
+    if (verbose && (eq1 != *eqout || wcs->syswcs != *sysout)) {
+        if (wcs->degout) {
+            deg2str (rstr, 32, ra1, 6);
+            deg2str (dstr, 32, dec1, 6);
+        }
+        else {
+            ra2str (rstr, 32, ra1, 3);
+            dec2str (dstr, 32, dec1, 2);
+        }
+        wcscstr (cstr, wcs->syswcs, wcs->equinox, wcs->epoch);
+        fprintf (stderr,"Reference pixel (%.2f,%.2f) %s %s %s\n",
+                 wcs->xrefpix, wcs->yrefpix, rstr, dstr, cstr);
+    }
+
+    /* Get coordinates of corners for size for catalog searching */
+    dx = wcs->nxpix;
+    dy = wcs->nypix;
+    xmin = 0.5;
+    ymin = 0.5;
+    xmax = 0.5 + dx;
+    ymax = 0.5 + dy;
+    pix2wcs (wcs, xmin, ymin, &ra1, &dec1);
+    pix2wcs (wcs, xmin, ymax, &ra2, &dec2);
+    pix2wcs (wcs, xmax, ymin, &ra3, &dec3);
+    pix2wcs (wcs, xmax, ymax, &ra4, &dec4);
+
+    /* Convert search corners to output coordinate system and equinox */
+    if (wcs->syswcs > 0 && wcs->syswcs != 6 && wcs->syswcs != 10) {
+        wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,&ra1,&dec1,wcs->epoch);
+        wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,&ra2,&dec2,wcs->epoch);
+        wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,&ra3,&dec3,wcs->epoch);
+        wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,&ra4,&dec4,wcs->epoch);
+    }
+
+    /* Find center and convert to output coordinate system and equinox */
+    x = 0.5 + (dx * 0.5);
+    y = 0.5 + (dy * 0.5);
+    pix2wcs (wcs, x, y, cra, cdec);
+    if (wcs->syswcs > 0 && wcs->syswcs != 6 && wcs->syswcs != 10)
+        wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,cra,cdec,wcs->epoch);
+
+    /* Find maximum half-width in declination */
+    *ddec = fabs (dec1 - *cdec);
+    if (fabs (dec2 - *cdec) > *ddec)
+        *ddec = fabs (dec2 - *cdec);
+    if (fabs (dec3 - *cdec) > *ddec)
+        *ddec = fabs (dec3 - *cdec);
+    if (fabs (dec4 - *cdec) > *ddec)
+        *ddec = fabs (dec4 - *cdec);
+
+    /* Find maximum half-width in right ascension */
+    dra0 = (dx / dy) * (*ddec / cos (*cdec));
+    dra1 = ra1 - *cra;
+    dra2 = ra2 - *cra;
+    if (*cra < 0 && *cra + dra0 > 0.0) {
+        dra1 = -(dra1 - 360.0);
+        dra2 = -(dra2 - 360.0);
+    }
+    if (dra1 > 180.0)
+        dra1 = dra1 - 360.0;
+    else if (dra1 < -180.0)
+        dra1 = dra1 + 360.0;
+    else if (dra1 < 0.0)
+        dra1 = -dra1;
+    if (dra2 > 180.0)
+        dra2 = dra2 - 360.0;
+    else if (dra2 < -180.0)
+        dra2 = dra2 + 360.0;
+    else if (dra2 < 0.0)
+        dra2 = -dra2;
+    dra3 = *cra - ra3;
+    dra4 = *cra - ra4;
+    if (*cra > 0 && *cra - dra0 < 0.0) {
+        dra3 = dra3 + 360.0;
+        dra4 = dra4 + 360.0;
+    }
+    if (dra3 > 180.0)
+        dra3 = dra3 - 360.0;
+    else if (dra3 < -180.0)
+        dra3 = dra3 + 360.0;
+    else if (dra3 < 0.0)
+        dra3 = -dra3;
+    if (dra4 > 180.0)
+        dra4 = dra4 - 360.0;
+    else if (dra4 < -180.0)
+        dra4 = dra4 + 360.0;
+    else if (dra4 < 0.0)
+        dra4 = -dra4;
+    *dra = dra1;
+    if (dra2 > *dra)
+        *dra = dra2;
+    if (dra3 > *dra)
+        *dra = dra3;
+    if (dra4 > *dra)
+        *dra = dra4;
+
+    /* wcssize (wcs, cra, cdec, dra, ddec); */
+
+    /* Set reference pixel to center of image if it has not been set */
+    if (wcs->xref == -999.0 && wcs->yref == -999.0) {
+        wcs->xref = *cra;
+        wcs->cel.ref[0] = *cra;
+        wcs->crval[0] = *cra;
+        wcs->yref = *cdec;
+        wcs->cel.ref[1] = *cdec;
+        wcs->crval[1] = *cdec;
+        ra1 = *cra;
+        dec1 = *cdec;
+        if (wcs->xrefpix == 0.0 && wcs->yrefpix == 0.0) {
+            wcs->xrefpix = 0.5 + (double) wcs->nxpix * 0.5;
+            wcs->yrefpix = 0.5 + (double) wcs->nypix * 0.5;
+        }
+        wcs->xinc = *dra * 2.0 / (double) wcs->nxpix;
+        wcs->yinc = *ddec * 2.0 / (double) wcs->nypix;
+        /* hchange (header,"PLTRAH","PLT0RAH");
+    wcs->plate_fit = 0; */
+    }
+
+    /* Convert center to desired coordinate system */
+    else if (wcs->syswcs != *sysout && wcs->equinox != *eqout) {
+        wcscon (wcs->syswcs, *sysout, wcs->equinox, *eqout, &ra1, &dec1, wcs->epoch);
+        if (wcs->coorflip) {
+            wcs->yref = ra1;
+            wcs->xref = dec1;
+        }
+        else {
+            wcs->xref = ra1;
+            wcs->yref = dec1;
+        }
+    }
+
+    /* Compute plate scale to return if it was not set on the command line */
+    if (secpix0 <= 0.0) {
+        pix2wcs (wcs, wcs->xrefpix-0.5, wcs->yrefpix, &ra1, &dec1);
+        pix2wcs (wcs, wcs->xrefpix+0.5, wcs->yrefpix, &ra2, &dec2);
+        *secpix = 3600.0 * wcsdist (ra1, dec1, ra2, dec2);
+    }
+
+    wcs->crval[0] = wcs->xref;
+    wcs->crval[1] = wcs->yref;
+    if (wcs->coorflip) {
+        wcs->cel.ref[0] = wcs->crval[1];
+        wcs->cel.ref[1] = wcs->crval[0];
+    }
+    else {
+        wcs->cel.ref[0] = wcs->crval[0];
+        wcs->cel.ref[1] = wcs->crval[1];
+    }
+
+    if (wcs->syswcs > 0 && wcs->syswcs != 6 && wcs->syswcs != 10) {
+        wcs->cel.flag = 0;
+        wcs->wcsl.flag = 0;
+    }
+    else {
+        wcs->lin.flag = LINSET;
+        wcs->wcsl.flag = WCSSET;
+    }
+
+    wcs->equinox = *eqout;
+    wcs->syswcs = *sysout;
+    wcs->sysout = *sysout;
+    wcs->eqout = *eqout;
+    wcs->sysin = *sysout;
+    wcs->eqin = *eqout;
+    wcscstr (cstr,*sysout,*eqout,wcs->epoch);
+    strcpy (wcs->radecsys, cstr);
+    strcpy (wcs->radecout, cstr);
+    strcpy (wcs->radecin, cstr);
+    wcsininit (wcs, wcs->radecsys);
+    wcsoutinit (wcs, wcs->radecsys);
+
+    naxes = wcs->naxis;
+    if (naxes < 1 || naxes > 9) {
+        naxes = wcs->naxes;
+        wcs->naxis = naxes;
+    }
+
+    if (usecdelt) {
+        hputnr8 (header, "CDELT1", 9, wcs->xinc);
+        if (naxes > 1) {
+            hputnr8 (header, "CDELT2", 9, wcs->yinc);
+            hputnr8 (header, "CROTA2", 9, wcs->rot);
+        }
+        hdel (header, "CD1_1");
+        hdel (header, "CD1_2");
+        hdel (header, "CD2_1");
+        hdel (header, "CD2_2");
+    }
+    else {
+        hputnr8 (header, "CD1_1", 9, wcs->cd[0]);
+        if (naxes > 1) {
+            hputnr8 (header, "CD1_2", 9, wcs->cd[1]);
+            hputnr8 (header, "CD2_1", 9, wcs->cd[2]);
+            hputnr8 (header, "CD2_2", 9, wcs->cd[3]);
+        }
+    }
+
+    /* Print reference pixel position and value */
+    if (verbose) {
+        if (wcs->degout) {
+            deg2str (rstr, 32, ra1, 6);
+            deg2str (dstr, 32, dec1, 6);
+        }
+        else {
+            ra2str (rstr, 32, ra1, 3);
+            dec2str (dstr, 32, dec1, 2);
+        }
+        wcscstr (cstr,*sysout,*eqout,wcs->epoch);
+        fprintf (stderr,"Reference pixel (%.2f,%.2f) %s %s %s\n",
+                 wcs->xrefpix, wcs->yrefpix, rstr, dstr, cstr);
+    }
+
+    /* Image size for catalog search */
+    if (verbose) {
+        if (wcs->degout) {
+            deg2str (rstr, 32, *cra, 6);
+            deg2str (dstr, 32, *cdec, 6);
+        }
+        else {
+            ra2str (rstr, 32, *cra, 3);
+            dec2str (dstr, 32, *cdec, 2);
+        }
+        wcscstr (cstr, *sysout, *eqout, wcs->epoch);
+        fprintf (stderr,"Search at %s %s %s", rstr, dstr, cstr);
+        if (wcs->degout) {
+            deg2str (rstr, 32, *dra, 6);
+            deg2str (dstr, 32, *ddec, 6);
+        }
+        else {
+            ra2str (rstr, 32, *dra, 3);
+            dec2str (dstr, 32, *ddec, 2);
+        }
+        fprintf (stderr," +- %s %s\n", rstr, dstr);
+        fprintf (stderr,"Image width=%d height=%d, %g arcsec/pixel\n",
+                 *wp, *hp, *secpix);
+    }
+
+    return (wcs);
+}
+
+
+WorldCoor* AstroUtils::ChangeFITSWCS (char *filename, char* header, int verbose)
+{
+    int nax, i, hp, wp;
+    double xref, yref, degpix, secpix;
+    struct WorldCoor *wcs;
+    char temp[16];
+    char *cwcs;
+
+    /* Set the world coordinate system from the image header */
+
+
+
+    if (strlen (filename) > 0)
+    {
+        cwcs = strchr (filename, '%');
+
+
+        if (cwcs != NULL)
+            cwcs++;
+    }
+
+    if (!strncmp (header, "END", 3)) {
+        //qDebug()<<"strncmp - pre ";
+
+
+        cwcs = NULL;
+        for (i = 0; i < 2880; i++)
+            header[i] = (char) 32;
+        hputl (header, "SIMPLE", 1);
+        hputi4 (header, "BITPIX", 0);
+        hputi4 (header, "NAXIS", 2);
+        hputi4 (header, "NAXIS1", 1);
+        hputi4 (header, "NAXIS2", 1);
+
+        // qDebug()<<"strncmp - post ";
+
+    }
+
+    //qDebug()<<"dopo id strncmp --  ";
+
+    /* Set image dimensions */
+    nax = 0;
+    if (hp0 > 0 || wp0 > 0) {
+        hp = hp0;
+        wp = wp0;
+        if (hp > 0 && wp > 0)
+            nax = 2;
+        else
+            nax = 1;
+        hputi4 (header, "NAXIS", nax);
+        hputi4 (header, "NAXIS1", wp);
+        hputi4 (header, "NAXIS2", hp);
+    }
+    else if (hgeti4 (header,"NAXIS",&nax) < 1 || nax < 1) {
+        if (hgeti4 (header, "WCSAXES", &nax) < 1)
+            return (NULL);
+        else {
+            if (hgeti4 (header, "IMAGEW", &wp) < 1)
+                return (NULL);
+            if (hgeti4 (header, "IMAGEH", &wp) < 1)
+                return (NULL);
+        }
+    }
+    else {
+        if (hgeti4 (header,"NAXIS1",&wp) < 1)
+            return (NULL);
+        if (hgeti4 (header,"NAXIS2",&hp) < 1)
+            return (NULL);
+    }
+
+    /* Set plate center from command line, if it is there */
+    if (ra0 > -99.0 && dec0 > -99.0) {
+        hputnr8 (header, "CRVAL1" ,8,ra0);
+        hputnr8 (header, "CRVAL2" ,8,dec0);
+        hputra (header, "RA", ra0);
+        hputdec (header, "DEC", dec0);
+        if (comsys == WCS_B1950) {
+            hputi4 (header, "EPOCH", 1950);
+            hputi4 (header, "EQUINOX", 1950);
+            hputs (header, "RADECSYS", "FK4");
+        }
+        else {
+            hputi4 (header, "EPOCH", 2000);
+            hputi4 (header, "EQUINOX", 2000);
+            if (comsys == WCS_GALACTIC)
+                hputs (header, "RADECSYS", "GALACTIC");
+            else if (comsys == WCS_ECLIPTIC)
+                hputs (header, "RADECSYS", "ECLIPTIC");
+            else if (comsys == WCS_ICRS)
+                hputs (header, "RADECSYS", "ICRS");
+            else
+                hputs (header, "RADECSYS", "FK5");
+        }
+        if (hgetr8 (header, "SECPIX", &secpix)) {
+            degpix = secpix / 3600.0;
+            hputnr8 (header, "CDELT1", 8, -degpix);
+            hputnr8 (header, "CDELT2", 8, degpix);
+            hdel (header, "CD1_1");
+            hdel (header, "CD1_2");
+            hdel (header, "CD2_1");
+            hdel (header, "CD2_2");
+        }
+    }
+    if (ptype0 > -1 && ptype0 < nctype) {
+        strcpy (temp,"RA---");
+        strcat (temp, ctypes[ptype0]);
+        hputs (header, "CTYPE1", temp);
+        strcpy (temp,"DEC--");
+        strcat (temp, ctypes[ptype0]);
+        hputs (header, "CTYPE2", temp);
+    }
+
+    /* Set reference pixel from command line, if it is there */
+    if (xref0 > -99999.0 && yref0 > -99999.0) {
+        hputr8 (header, "CRPIX1", xref0);
+        hputr8 (header, "CRPIX2", yref0);
+    }
+    else if (hgetr8 (header, "CRPIX1", &xref) < 1) {
+        xref = 0.5 + (double) wp / 2.0;
+        yref = 0.5 + (double) hp / 2.0;
+        hputnr8 (header, "CRPIX1", 3, xref);
+        hputnr8 (header, "CRPIX2", 3, yref);
+    }
+
+    /* Set plate scale from command line, if it is there */
+    if (secpix0 != 0.0 || cd0 != NULL) {
+        if (secpix2 != 0.0) {
+            secpix = 0.5 * (secpix0 + secpix2);
+            hputnr8 (header, "SECPIX1", 5, secpix0);
+            hputnr8 (header, "SECPIX2", 5, secpix2);
+            degpix = -secpix0 / 3600.0;
+            hputnr8 (header, "CDELT1", 8, degpix);
+            degpix = secpix2 / 3600.0;
+            hputnr8 (header, "CDELT2", 8, degpix);
+            hdel (header, "CD1_1");
+            hdel (header, "CD1_2");
+            hdel (header, "CD2_1");
+            hdel (header, "CD2_2");
+        }
+        else if (secpix0 != 0.0) {
+            secpix = secpix0;
+            hputnr8 (header, "SECPIX", 5, secpix);
+            degpix = secpix / 3600.0;
+            hputnr8 (header, "CDELT1", 8, -degpix);
+            hputnr8 (header, "CDELT2", 8, degpix);
+            hdel (header, "CD1_1");
+            hdel (header, "CD1_2");
+            hdel (header, "CD2_1");
+            hdel (header, "CD2_2");
+        }
+        else {
+            hputr8 (header, "CD1_1", cd0[0]);
+            hputr8 (header, "CD1_2", cd0[1]);
+            hputr8 (header, "CD2_1", cd0[2]);
+            hputr8 (header, "CD2_2", cd0[3]);
+            hdel (header, "CDELT1");
+            hdel (header, "CDELT2");
+            hdel (header, "CROTA1");
+            hdel (header, "CROTA2");
+        }
+        if (!ksearch (header,"CRVAL1")) {
+            hgetra (header, "RA", &ra0);
+            hgetdec (header, "DEC", &dec0);
+            hputnr8 (header, "CRVAL1", 8, ra0);
+            hputnr8 (header, "CRVAL2", 8, dec0);
+        }
+        if (!ksearch (header,"CRPIX1")) {
+            xref = (double) wp / 2.0;
+            yref = (double) hp / 2.0;
+            hputnr8 (header, "CRPIX1", 3, xref);
+            hputnr8 (header, "CRPIX2", 3, yref);
+        }
+        if (!ksearch (header,"CTYPE1")) {
+            if (comsys == WCS_GALACTIC) {
+                hputs (header, "CTYPE1", "GLON-TAN");
+                hputs (header, "CTYPE2", "GLAT-TAN");
+            }
+            else {
+                hputs (header, "CTYPE1", "RA---TAN");
+                hputs (header, "CTYPE2", "DEC--TAN");
+            }
+        }
+    }
+
+    /* Set rotation angle from command line, if it is there */
+    if (rot0 < 361.0) {
+        hputnr8 (header, "CROTA1", 5, rot0);
+        hputnr8 (header, "CROTA2", 5, rot0);
+    }
+
+    /* Set observation date for epoch, if it is there */
+    if (dateobs0 != NULL)
+        hputs (header, "DATE-OBS", dateobs0);
+
+    /* Initialize WCS structure from FITS header */
+    wcs = wcsinitn (header, cwcs);
+
+    /* If incomplete WCS in header, drop out */
+    if (nowcs (wcs)) {
+        setwcsfile (filename);
+        /* wcserr(); */
+        if (verbose)
+            fprintf (stderr,"Insufficient information for initial WCS\n");
+        return (NULL);
+    }
+    return (wcs);
+}
+
+
+WorldCoor * AstroUtils::GetWCSFITS (char *filename, int verbose)
+{
+    char *header;		/* FITS header */
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+    //char *GetFITShead();
+    char *cwcs;			/* Multiple wcs string (name or character) */
+
+    /* Read the FITS or IRAF image file header */
+    header = GetFITShead (filename, verbose);
+    if (header == NULL)
+        return (NULL);
+
+    verbose=true;
+
+    /* Set the world coordinate system from the image header */
+    cwcs = strchr (filename, '%');
+    if (cwcs != NULL)
+        cwcs++;
+    wcs = wcsinitn (header, cwcs);
+    if (wcs == NULL) {
+        setwcsfile (filename);
+        if (verbose)
+            wcserr ();
+    }
+    free (header);
+
+    return (wcs);
+}
+
+char * AstroUtils::GetFITShead (char * filename, int verbose)
+{
+    char *header;		/* FITS header */
+    int lhead;			/* Maximum number of bytes in FITS header */
+    char *irafheader;		/* IRAF image header */
+    int nbiraf, nbfits;
+
+    /* Open IRAF image if .imh extension is present */
+    if (isiraf (filename)) {
+        if ((irafheader = irafrhead (filename, &nbiraf)) != NULL) {
+            if ((header = iraf2fits (filename, irafheader, nbiraf, &lhead)) == NULL) {
+                if (verbose)
+                    fprintf (stderr, "Cannot translate IRAF header %s\n",filename);
+                free (irafheader);
+                irafheader = NULL;
+                return (NULL);
+            }
+            free (irafheader);
+            irafheader = NULL;
+        }
+        else {
+            if (verbose)
+                fprintf (stderr, "Cannot read IRAF header file %s\n", filename);
+            return (NULL);
+        }
+    }
+    else if (istiff (filename) || isgif (filename) || isjpeg (filename)) {
+        if ((header = fitsrtail (filename, &lhead, &nbfits)) == NULL) {
+            if (verbose)
+                fprintf (stderr, "TIFF file %s has no appended header\n", filename);
+            return (NULL);
+        }
+    }
+
+
+    /* Open FITS file if .imh extension is not present */
+    else {
+        if ((header = fitsrhead (filename, &lhead, &nbfits)) == NULL) {
+            if (verbose)
+                /* fprintf (stderr, "Cannot read FITS file %s\n", filename); */
+                fitserr ();
+            return (NULL);
+        }
+    }
+
+    return (header);
+}
diff --git a/Code/src/astroutils.h b/Code/src/astroutils.h
new file mode 100644
index 0000000000000000000000000000000000000000..42233b244603097174df5bfec408417a7890e75c
--- /dev/null
+++ b/Code/src/astroutils.h
@@ -0,0 +1,30 @@
+#ifndef ASTROUTILS_H
+#define ASTROUTILS_H
+
+#include <QString>
+#include "libwcs/wcs.h"
+
+class AstroUtils
+{
+public:
+    AstroUtils();
+    static bool sky2xy(std::string map,double ra, double dec,double* coord);
+
+    static void xy2sky(std::string map, float x, float y, double *coord,  int wcs_type=WCS_ECLIPTIC);
+//    static void xy2sky(QString map, float x, float y, double *coord,  int wcs_type=WCS_ECLIPTIC);
+//    static bool sky2xy(QString map, double l, double b, double* coord);
+    static double arcsecPixel(std::string file);
+    static void getRotationAngle(std::string file);
+    static int getSysOut( std::string file );
+
+
+private:
+    static WorldCoor* GetWCSFITS (char *filename, int verbose);
+    static char * GetFITShead (char * filename, int verbose);
+    static WorldCoor* GetFITSWCS (char *filename, char	*header, int verbose, double *cra, double *cdec,double *dra, double *ddec, double *secpix, int *wp,int	*hp,int	*sysout ,double	*eqout);
+    static WorldCoor* ChangeFITSWCS (char *filename, char* header, int verbose);
+
+
+};
+
+#endif // ASTROUTILS_H
diff --git a/Code/src/base64.cpp b/Code/src/base64.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a41bfaab07baf32f2af87060ee246ac9a1276124
--- /dev/null
+++ b/Code/src/base64.cpp
@@ -0,0 +1,96 @@
+#include "base64.h"
+#include <iostream>
+
+static const std::string base64_chars =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "abcdefghijklmnopqrstuvwxyz"
+        "0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+    return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+    std::string ret;
+    int i = 0;
+    int j = 0;
+    unsigned char char_array_3[3];
+    unsigned char char_array_4[4];
+
+    while (in_len--) {
+        char_array_3[i++] = *(bytes_to_encode++);
+        if (i == 3) {
+            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+            char_array_4[3] = char_array_3[2] & 0x3f;
+
+            for(i = 0; (i <4) ; i++)
+                ret += base64_chars[char_array_4[i]];
+            i = 0;
+        }
+    }
+
+    if (i)
+    {
+        for(j = i; j < 3; j++)
+            char_array_3[j] = '\0';
+
+        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+        char_array_4[3] = char_array_3[2] & 0x3f;
+
+        for (j = 0; (j < i + 1); j++)
+            ret += base64_chars[char_array_4[j]];
+
+        while((i++ < 3))
+            ret += '=';
+
+    }
+
+    return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+    int in_len = encoded_string.size();
+    int i = 0;
+    int j = 0;
+    int in_ = 0;
+    unsigned char char_array_4[4], char_array_3[3];
+    std::string ret;
+
+    while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+        char_array_4[i++] = encoded_string[in_]; in_++;
+        if (i ==4) {
+            for (i = 0; i <4; i++)
+                char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+            for (i = 0; (i < 3); i++)
+                ret += char_array_3[i];
+            i = 0;
+        }
+    }
+
+    if (i) {
+        for (j = i; j <4; j++)
+            char_array_4[j] = 0;
+
+        for (j = 0; j <4; j++)
+            char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+        char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+        char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+        for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+    }
+
+    return ret;
+}
diff --git a/Code/src/base64.h b/Code/src/base64.h
new file mode 100644
index 0000000000000000000000000000000000000000..9bab939b787988cb5c075f709622da2ea2502ce5
--- /dev/null
+++ b/Code/src/base64.h
@@ -0,0 +1,5 @@
+
+#include <string>
+
+std::string base64_encode(unsigned char const* , unsigned int len);
+std::string base64_decode(std::string const& s);
diff --git a/Code/src/color.cpp b/Code/src/color.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b0b2f72747a35de449426a25bbdb8fed6cbd642b
--- /dev/null
+++ b/Code/src/color.cpp
@@ -0,0 +1,154 @@
+/***************************************************************************
+ *   Copyright (C) 2008 by Gabriella Caniglia *
+ *  gabriella.caniglia@oact.inaf.it *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include <cstdlib>
+#include <cstring>
+
+#include "color.h"
+
+//----------------------------------------------------------------------------
+Color::Color()
+//----------------------------------------------------------------------------
+{
+    m_red = m_green = m_blue = m_hue = m_saturation = m_value =0;
+    m_alpha = 0;
+}
+//----------------------------------------------------------------------------
+Color::Color( int r, int g, int b, int a )
+//----------------------------------------------------------------------------
+{
+    setRGB(r,g,b,a);
+}
+
+
+//----------------------------------------------------------------------------
+void Color::setRGB( int r, int g, int b, int a )
+//----------------------------------------------------------------------------
+{
+    if( r<0 ) r=0; if( r>255 ) r=255;
+    if( g<0 ) g=0; if( g>255 ) g=255;
+    if( b<0 ) b=0; if( b>255 ) b=255;
+
+    m_red = r; m_green = g; m_blue =b;
+    if(a != -1)
+    {
+        if( a<0 ) a=0; if( a>255 ) a=255;
+        m_alpha = a;
+    }
+    RGBToHSV();
+}
+
+//----------------------------------------------------------------------------
+void Color::setHSV(int h, int s, int v)
+//----------------------------------------------------------------------------
+{
+    if( s<0 ) s=0; if( s>255 ) s=255;
+    if( v<0 ) v=0; if( v>255 ) v=255;
+    while(h<0)   h+= 360;
+    while(h>360) h-= 360;
+
+    m_hue = h; m_saturation = s; m_value =v;
+    HSVToRGB();
+}
+//----------------------------------------------------------------------------
+void Color::getHSV(int *h, int *s, int *v)
+//----------------------------------------------------------------------------
+{
+    *h = m_hue; *s = m_saturation;  *v =m_value;
+}
+
+
+//----------------------------------------------------------------------------
+// rgb,sv in range [0..255], h in range [0..360]
+void Color::RGBToHSV(int r, int g, int b, int *h, int *s, int *v)
+//----------------------------------------------------------------------------
+{
+    float max = r;
+    if (max < g ) max = g;
+    if (max < b ) max = b;
+
+    float min = r;
+    if (min > g ) min = g;
+    if (min > b ) min = b;
+
+    *h = 0;
+    *s = max-min;
+    *v = max;
+
+    if(s==0) return;
+
+    float delta = max - min;
+    float H;
+
+    if (max == r) H = 0 + (g-b)/delta;
+    else if (max == g) H = 2 + (b-r)/delta;
+    else if (max == b) H = 4 + (r-g)/delta;
+
+    *h = H * 60;
+    if (*h<0) *h+=360;
+}
+//----------------------------------------------------------------------------
+// rgb,sv in range [0..255], h in range [0..360]
+void Color::HSVToRGB(int h, int s, int v, int *r, int *g, int *b)
+//----------------------------------------------------------------------------
+{
+    *r = *g = *b = v;
+    if (s == 0) return;
+
+    if (h == 360) h=0;
+
+    float H = h / 60.0;  // H is in [0..6)
+    float S = s / 255.0; // S is in [0..1]
+
+    int i=0;  // i is the largest integer <= H
+    if( H>=1 ) i=1;
+    if( H>=2 ) i=2;
+    if( H>=3 ) i=3;
+    if( H>=4 ) i=4;
+    if( H>=5 ) i=5;
+
+    float F = H-i; // f is the fractional part of fh;
+
+    float p,q,t;
+    p = v * (1.0 - S         );
+    q = v * (1.0 - S * F     );
+    t = v * (1.0 - S * (1-F) );
+
+    switch(i)
+    {
+    case 0: *r=v; *g=t; *b=p ; break;
+    case 1: *r=q; *g=v; *b=p ; break;
+    case 2: *r=p; *g=v; *b=t ; break;
+    case 3: *r=p; *g=q; *b=v ; break;
+    case 4: *r=t; *g=p; *b=v ; break;
+    case 5: *r=v; *g=p; *b=q ; break;
+    }
+}
+//----------------------------------------------------------------------------
+void Color::HSVToRGB()
+//----------------------------------------------------------------------------
+{
+    HSVToRGB( m_hue, m_saturation, m_value, &m_red, &m_green, &m_blue );
+}
+//----------------------------------------------------------------------------
+void Color::RGBToHSV()
+//----------------------------------------------------------------------------
+{
+    RGBToHSV( m_red, m_green, m_blue, &m_hue, &m_saturation, &m_value );
+}
diff --git a/Code/src/color.h b/Code/src/color.h
new file mode 100644
index 0000000000000000000000000000000000000000..6183fcb35f016aa507b8ed101539cb278f6182ba
--- /dev/null
+++ b/Code/src/color.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ *   Copyright (C) 2008 by Gabriella Caniglia *
+ *  gabriella.caniglia@oact.inaf.it *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef COLOR_H
+#define COLOR_H
+
+//----------------------------------------------------------------------------
+/**
+Color is a class representing an RGBA color:
+
+- Provides conversion among RGB and HSV as static member functions.
+
+- Contain both RGBA and HSV representation of a colour.
+  RGB,SV in range [0..255], H in range [0..360].
+  If updated through the various Set methods, then the two representation
+  are kept in sync. This provide a kind of implicit conversion.
+
+- Provide conversion among the usual color format ( rgb on [0..255] ),
+  the vtk format ( rgba on [0..1] ) and wxWindows ( wxColour ). These are accepted
+  from all the Set,Get and Constructors.
+
+- Provide weighted interpolation among two colors. Both in RGB and HSV space.
+
+- Provide a visual representation of a transparent color.
+  Filling the pixels (x,y) of a bitmap with Color::CheckeredColor(color, x,y)
+  will draw an image with a checkered pattern.
+  The pattern visibility is proportional with the alpha value.
+ */
+class Color
+//----------------------------------------------------------------------------
+{
+  public:
+    /** default ctor*/
+    Color();
+    /** ctor accepting an integer RGBA */
+    Color( int r, int g, int b, int a=0 );
+    /** ctor accepting an vtk color */
+
+
+    /** Set using integer RGBA - components in [0..255] */
+    void setRGB( int r, int g, int b, int a=-1 );
+
+
+    /** Set using integer HSV - H in [0..360] ,S,V in [0..255] */
+    void setHSV(int h, int s, int v);
+    /** Set returning integer HSV - H in [0..360] ,S,V in [0..255] */
+    void getHSV(int *h, int *s, int *v);
+
+
+
+  /** Force updating the RGB representation.
+    - useful if the HSV member variable are set directly */
+    void HSVToRGB();
+  /** Force updating the HSV representation.
+    - useful if the RGB member variable are set directly */
+    void RGBToHSV();
+
+    /** static function - conversion from RGB to HSV, all represented on Integer */
+    static void RGBToHSV(int r, int g, int b, int *h, int *s, int *v);
+    /** static function - conversion from HSV to RGB, all represented on Integer */
+    static void HSVToRGB(int h, int s, int v, int *r, int *g, int *b);
+
+
+  // member variables
+    int m_red,m_green,m_blue; ///<  rgb color representation
+    int m_hue,m_saturation,m_value; ///<  hsv color representation
+    int m_alpha;         ///<  alpha value
+};
+#endif
diff --git a/Code/src/contour.cpp b/Code/src/contour.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f63a67feb3f7c89a0f05baec088425b255f4f734
--- /dev/null
+++ b/Code/src/contour.cpp
@@ -0,0 +1,1060 @@
+#include "contour.h"
+#include "ui_contour.h"
+#include <vtkPolyDataMapper.h>
+#include "vtkMath.h"
+#include "vtkDoubleArray.h"
+#include "vtkContourFilter.h"
+#include "vtkPlaneSource.h"
+#include "vtkMarchingCubes.h"
+#include "vtkImageShiftScale.h"
+#include "vtkImageData.h"
+#include "vtkImageReader2.h"
+#include "vtkPlane.h"
+#include "vtkImageReader.h"
+#include "vtkImageToStructuredPoints.h"
+#include "vtkMarchingSquares.h"
+#include "vtkProperty.h"
+#include "vtkStructuredPoints.h"
+#include "vtkLabeledDataMapper.h"
+#include "vtkStripper.h"
+#include "vtkPointData.h"
+#include "vtkCellArray.h"
+#include "vtkRenderWindow.h"
+#include "vtkRendererCollection.h"
+#include "vtkCamera.h"
+#include "vtkMapper2D.h"
+#include "vtkSmartPointer.h"
+#include "vtkIntArray.h"
+#include "vtkCutter.h"
+#include "luteditor.h"
+#include <vtkScalarBarActor.h>
+#include <vtkScalarBarWidget.h>
+#include <vtkTextProperty.h>
+#include <vtkPlanes.h>
+
+
+#include <QTableView>
+#include <QSplitter>
+#include <QStandardItemModel>
+#include <QMenu>
+#include <QDebug>
+#include <QTreeWidget>
+
+static const int ItemTypeRoot = QTreeWidgetItem::UserType;
+static const int ItemTypeChild = QTreeWidgetItem::UserType + 1;
+
+/*
+contour::contour(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::contour)
+{
+    ui->setupUi(this);
+}*/
+
+
+contour::contour(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::contour)
+{
+    ui->setupUi(this);
+    isolines =
+            vtkSmartPointer<vtkActor>::New();
+
+
+    // ui->contourTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+
+    //connect(this,SIGNAL(customContextMenuRequested(const QPoint&)),SLOT(onCustomContextMenuRequested(const QPoint&)));
+
+
+    //   ui->->setContextMenuPolicy(Qt::ActionsContextMenu);
+
+    QStringList ColumnNames;
+    ColumnNames << "Id" << "Value";
+
+
+    connect(ui->labelCheckBox, SIGNAL(clicked(bool)), this, SLOT(toggledLabel(bool)));
+    connect(ui->idCheckBox, SIGNAL(clicked(bool)), this, SLOT(toggledId(bool)));
+
+
+}
+
+
+
+contour::~contour()
+{
+    delete ui;
+}
+
+
+void contour::toggledLabel(bool t)
+{
+    if(t)
+    {
+
+        ui->idCheckBox->setChecked(false);
+        if(ui->activateCheckBox->isChecked())
+        {
+            vtkWin->removeActor(idlabel);
+            vtkWin->addActor(isolabels);
+
+        }
+    }
+    else
+    {
+        vtkWin->removeActor(isolabels);
+    }
+
+}
+
+void contour::toggledId(bool t)
+{
+    ui->labelCheckBox->setChecked(false);
+
+    if(t)
+    {
+        if(ui->activateCheckBox->isChecked())
+        {
+            vtkWin->removeActor(isolabels);
+            vtkWin->addActor(idlabel);
+
+        }
+    }
+    else
+    {
+        vtkWin->removeActor(idlabel);
+    }
+
+}
+
+void contour::setFitsReader(vtkFitsReader *fits,vtkwindow_new *win)
+{
+
+    fitsReader=fits;
+    vtkWin = win;
+
+    //SET DEFAULT PARAMS
+    ui->numOfContourText->setText("5");
+
+    ui->minValueText->setText(QString::number( fitsReader->GetRMS()*3));
+    ui->maxValueText->setText(QString::number(fitsReader->GetMax()));
+
+    ui->minFitsText->setText(QString::number( fitsReader->GetMin()));
+    ui->maxFitsText->setText(QString::number(fitsReader->GetMax()));
+
+
+    ui->slicesText->setText(QString::number(fitsReader->GetNaxes(2)));
+    ui->RMSText->setText(QString::number(fitsReader->GetRMS()));
+    //ui->mediaText->setText(QString::number(fitsReader->GetMedia()));
+
+
+    ui->minFitsText->setEnabled(false);
+    ui->maxFitsText->setEnabled(false);
+    ui->slicesText->setEnabled(false);
+    ui->RMSText->setEnabled(false);
+    ui->LogLinearCheckBox->setChecked(true);
+
+
+    ui->redText->hide();
+    ui->greenText->hide();
+    ui->blueText->hide();
+    ui->label_2->hide();
+    ui->label_3->hide();
+    ui->label->hide();
+
+
+    if(vtkWin->getContourVisualized())
+    {
+        //qDebug()<<"true";
+        ui->activateCheckBox->setChecked(true);
+        ui->LogLinearCheckBox->setChecked(true);
+    }
+    else
+    {
+        ui->activateCheckBox->setChecked(false);
+    }
+
+
+}
+
+void contour::createContour()
+{
+    std::cout<<"Inside create Contour"<<std::endl;
+
+    //TEST da qui
+
+    int pointThreshold = 10;
+    vtkSmartPointer<vtkContourFilter> mycontours =
+            vtkSmartPointer<vtkContourFilter>::New();
+
+    double range[2];
+    fitsReader->GetOutput()->GetScalarRange(range);
+    mycontours->SetValue(0, (range[1] + range[0]) / 2.0);
+    mycontours->SetInputConnection(fitsReader->GetOutputPort());
+
+    vtkSmartPointer<vtkPlaneSource> plane =
+            vtkSmartPointer<vtkPlaneSource>::New();
+    int *size=vtkWin->renwin->GetSize();
+    plane->SetXResolution(size[0]);
+    plane->SetYResolution(size[1]);
+    plane->Update();
+
+    vtkSmartPointer<vtkDoubleArray> randomScalars =
+            vtkSmartPointer<vtkDoubleArray>::New();
+    randomScalars->SetNumberOfComponents(1);
+    randomScalars->SetName("Isovalues");
+    for (int i = 0; i < plane->GetOutput()->GetNumberOfPoints(); i++)
+    {
+        randomScalars->InsertNextTuple1(vtkMath::Random(-100.0, 100.0));
+    }
+
+    //TEST2
+    /*
+    vtkSmartPointer<vtkResliceImageViewer>viewer  =vtkSmartPointer<vtkResliceImageViewer>::New();
+    viewer->SetInput(fitsReader->GetOutput());
+    vtkSmartPointer<vtkDoubleArray> scalars =
+          vtkSmartPointer<vtkDoubleArray>::New();
+    viewer->SetSlice(101);
+    scalars= viewer->GetSlice()-;
+
+*/
+
+
+
+    plane->GetOutput()->GetPointData()->SetScalars(randomScalars);
+    vtkSmartPointer<vtkPolyData> polyData =
+            vtkSmartPointer<vtkPolyData>::New();
+
+    polyData = plane->GetOutput();
+    mycontours->SetInputConnection(plane->GetOutputPort());
+    mycontours->GenerateValues(5, -100, 100);
+    pointThreshold = 0;
+
+    // Connect the segments of the contours into polylines
+    vtkSmartPointer<vtkStripper> contourStripper =
+            vtkSmartPointer<vtkStripper>::New();
+    contourStripper->SetInputConnection( mycontours->GetOutputPort());
+    contourStripper->Update();
+
+
+    // Da qui commentato
+    int numberOfContourLines = contourStripper->GetOutput()->GetNumberOfLines();
+
+
+    std::cout << "There are "
+              << numberOfContourLines << " contours lines."
+              << std::endl;
+
+
+    vtkPoints *points     =
+            contourStripper->GetOutput()->GetPoints();
+    vtkCellArray *cells   =
+            contourStripper->GetOutput()->GetLines();
+    vtkDataArray *scalars =
+            contourStripper->GetOutput()->GetPointData()->GetScalars();
+
+    // Create a polydata that contains point locations for the contour
+    // line labels
+    vtkSmartPointer<vtkPolyData> labelPolyData =
+            vtkSmartPointer<vtkPolyData>::New();
+    vtkSmartPointer<vtkPoints> labelPoints =
+            vtkSmartPointer<vtkPoints>::New();
+    vtkSmartPointer<vtkDoubleArray> labelScalars =
+            vtkSmartPointer<vtkDoubleArray>::New();
+    labelScalars->SetNumberOfComponents(1);
+    labelScalars->SetName("Isovalues");
+
+    vtkIdType *indices;
+    vtkIdType numberOfPoints;
+    unsigned int lineCount = 0;
+    for (cells->InitTraversal();
+         cells->GetNextCell(numberOfPoints, indices);
+         lineCount++)
+    {
+        if (numberOfPoints < pointThreshold)
+        {
+            continue;
+        }
+        std::cout << "Line " << lineCount << ": " << std::endl;
+
+        // Compute the point id to hold the label
+        // Mid point or a random point
+        vtkIdType midPointId = indices[numberOfPoints / 2];
+        midPointId =
+                indices[static_cast<vtkIdType>(vtkMath::Random(0, numberOfPoints))];
+
+        double midPoint[3];
+        points->GetPoint(midPointId, midPoint);
+        /*
+        std::cout << "\tmidPoint is " << midPointId << " with coordinate "
+                  << "("
+                  << midPoint[0] << ", "
+                  << midPoint[1] << ", "
+                  << midPoint[2] << ")"
+                  << " and value " << scalars->GetTuple1(midPointId)
+                  << std::endl;*/
+        labelPoints->InsertNextPoint(midPoint);
+        labelScalars->InsertNextTuple1(scalars->GetTuple1(midPointId));
+    }
+    labelPolyData->SetPoints(labelPoints);
+    labelPolyData->GetPointData()->SetScalars(labelScalars);
+
+    vtkSmartPointer<vtkPolyDataMapper> contourMapper =
+            vtkSmartPointer<vtkPolyDataMapper>::New();
+    contourMapper->SetInputConnection(contourStripper->GetOutputPort());
+    //contourMapper->ScalarVisibilityOff();
+
+
+
+    vtkSmartPointer<vtkActor> isolines =
+            vtkSmartPointer<vtkActor>::New();
+    isolines->SetMapper(contourMapper);
+
+    vtkSmartPointer<vtkLookupTable> surfaceLUT =
+            vtkSmartPointer<vtkLookupTable>::New();
+    surfaceLUT->SetRange(
+                polyData->GetPointData()->GetScalars()->GetRange());
+    surfaceLUT->Build();
+
+    vtkSmartPointer<vtkPolyDataMapper> surfaceMapper =
+            vtkSmartPointer<vtkPolyDataMapper>::New();
+#if VTK_MAJOR_VERSION <= 5
+    surfaceMapper->SetInput(polyData);
+#else
+    surfaceMapper->SetInputData(polyData);
+#endif
+    surfaceMapper->ScalarVisibilityOn();
+    surfaceMapper->SetScalarRange(
+                polyData->GetPointData()->GetScalars()->GetRange());
+    surfaceMapper->SetLookupTable(surfaceLUT);
+
+    vtkSmartPointer<vtkActor> surface =
+            vtkSmartPointer<vtkActor>::New();
+    surface->SetMapper(surfaceMapper);
+
+    // The labeled data mapper will place labels at the points
+    vtkSmartPointer<vtkLabeledDataMapper> labelMapper =
+            vtkSmartPointer<vtkLabeledDataMapper>::New();
+    labelMapper->SetFieldDataName("Isovalues");
+#if VTK_MAJOR_VERSION <= 5
+    labelMapper->SetInput(labelPolyData);
+#else
+    labelMapper->SetInputData(labelPolyData);
+#endif
+    labelMapper->SetLabelModeToLabelScalars();
+    labelMapper->SetLabelFormat("%6.2f");
+
+    vtkSmartPointer<vtkActor2D> isolabels =
+            vtkSmartPointer<vtkActor2D>::New();
+    isolabels->SetMapper(labelMapper);
+
+    // Create a renderer and render window
+    /*vtkSmartPointer<vtkRenderer> renderer =
+           vtkSmartPointer<vtkRenderer>::New();
+
+       vtkSmartPointer<vtkRenderWindow> renderWindow =
+           vtkSmartPointer<vtkRenderWindow>::New();
+       renderWindow->AddRenderer(renderer);
+
+       // Create an interactor
+       vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
+           vtkSmartPointer<vtkRenderWindowInteractor>::New();
+       renderWindowInteractor->SetRenderWindow(renderWindow);*/
+
+    // Add the actors to the scene
+
+    vtkWin->ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->AddActor(isolines);
+    vtkWin->ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->AddActor(isolabels);
+
+    //renderer->AddActor(isolabels);
+    //renderer->AddActor(surface);
+
+    // Render the scene (lights and cameras are created automatically)
+    vtkWin->ui->qVTK1->GetRenderWindow()->Render();
+    vtkWin->ui->qVTK1->update();
+
+    //renderWindow->Render();
+    //renderWindowInteractor->Start();
+
+
+    //fine TEST
+
+
+    /* commentato per via del TEST
+    contours = vtkMarchingSquares::New();
+    contours->SetInputConnection(fitsReader->GetOutputPort());
+            //();
+
+
+    contours->GenerateValues(ui->numOfContourText->text().toInt(),ui->minValueText->text().toDouble(),ui->maxValueText->text().toDouble() );
+
+    qDebug()<<"valori per contours: "<<ui->numOfContourText->text().toInt()<<ui->minValueText->text().toDouble()<<ui->maxValueText->text().toDouble();
+
+
+    // Connect the segments of the contours into polylines
+    contourStripper =  vtkSmartPointer<vtkStripper>::New();
+    contourStripper->SetInputConnection(contours->GetOutputPort());
+    contourStripper->Update();
+    contourStripper->GetOutput()->BuildLinks();*/
+
+    //esempio qui: http://www.vtk.org/Wiki/VTK/Examples/Cxx/Visualization/LabelContours
+
+    /*
+     //OK QUESTO FUNZIONA PER RIMUOVERE I VARI CONTOUR
+    std::cout<<"PRE- SIZE CELLS: "<<contourStripper->GetOutput()->GetNumberOfCells()<<std::endl;
+     contourStripper->GetOutput()->BuildLinks();
+
+     for(int i=0;i<20;i++)
+         contourStripper->GetOutput()->DeleteCell(i);
+    contourStripper->GetOutput()->RemoveDeletedCells();
+    std::cout<<"POST- SIZE CELLS: "<<contourStripper->GetOutput()->GetNumberOfCells()<<std::endl;
+*/
+
+    //commentato per via di TEST: codice originario
+    /*
+    vtkPolyDataMapper *contour_mapper = vtkPolyDataMapper::New();
+    //contour_mapper->SetInput(contour->GetOutput());
+    contour_mapper->SetInput(contourStripper->GetOutput());
+    contour_mapper->ScalarVisibilityOff();
+
+    contour_actor = vtkActor::New();
+    contour_actor->SetMapper(contour_mapper);
+    contour_actor->GetProperty()->SetColor(ui->redText->text().toDouble(), ui->greenText->text().toDouble(), ui->blueText->text().toDouble());
+*/
+    //fa parte di TEST
+    vtkWin->ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->AddActor(contour_actor);
+    vtkWin->ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->AddActor(isolabels);
+
+    vtkWin->ui->qVTK1->GetRenderWindow()->Render();
+    vtkWin->ui->qVTK1->update();
+}
+
+void contour::deleteContour()
+{
+
+
+    contourStripper->GetOutput()->RemoveDeletedCells();
+
+
+
+    //vtkWin->removeActor(contour_actor);
+    // vtkWin->addActor(contour_actor);
+
+    //contour_actor->GetMapper()->Update();
+
+    vtkWin->ui->qVTK1->GetRenderWindow()->Render();
+    std::cout<<"POST- SIZE CELLS: "<<contourStripper->GetOutput()->GetNumberOfCells()<<std::endl;
+
+
+}
+
+void contour::on_okButton_clicked()
+{
+    int pointThreshold = 1;
+
+    //std::cout<<"ENTRO DENTRO LA FUNZIONE"<<std::endl;
+    //std::cout<<ui->activateCheckBox->isChecked()<<std::endl;
+
+    if(ui->activateCheckBox->isChecked())
+    {
+        vtkWin->removeActor(isolabels);
+        vtkWin->removeActor(isolines);
+        vtkWin->removeActor(idlabel);
+        vtkWin->removeActor(scalarBar);
+        ui->idCheckBox->setChecked(false);
+
+        addContours();
+
+        ui->labelCheckBox->setEnabled(true);
+        ui->activateCheckBox->setChecked(true);
+        vtkWin->setContourVisualized(true);
+        std::cout<<"Labels activated"<<std::endl;
+
+        /*int numberOfContourLines = contourStripper->GetOutput()->GetNumberOfLines();
+
+        std::cout << "****There are " << numberOfContourLines << " contours lines." << std::endl;
+
+
+        vtkPoints *points     = contourStripper->GetOutput()->GetPoints();
+        vtkCellArray *cells   = contourStripper->GetOutput()->GetLines();
+        vtkDataArray *scalars = contourStripper->GetOutput()->GetPointData()->GetScalars();
+
+        // Create a polydata that contains point locations for the contour
+        // line labels
+        vtkSmartPointer<vtkPolyData> labelPolyData = vtkSmartPointer<vtkPolyData>::New();
+        vtkSmartPointer<vtkPoints> labelPoints =  vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkDoubleArray> labelScalars = vtkSmartPointer<vtkDoubleArray>::New();
+
+
+
+        vtkSmartPointer<vtkPolyData> idPolyData = vtkSmartPointer<vtkPolyData>::New();
+        vtkSmartPointer<vtkIntArray> idScalars = vtkSmartPointer<vtkIntArray>::New();
+
+       // vtkSmartPointer<vtkDoubleArray> labelId = vtkSmartPointer<vtkDoubleArray>::New();
+
+        labelScalars->SetNumberOfComponents(1);
+        labelScalars->SetName("Isovalues");
+
+     //   labelId->SetNumberOfComponents(1);
+      //  labelId->SetName("Id");
+
+        vtkIdType *indices;
+        vtkIdType numberOfPoints;
+        unsigned int lineCount = 0;
+        int i=0;
+        for (cells->InitTraversal(); cells->GetNextCell(numberOfPoints, indices);lineCount++)
+          {
+          if (numberOfPoints < pointThreshold)
+            {
+            continue;
+            }
+
+       //   std::cout << "Line " << lineCount << ":("<<numberOfPoints<<") " << std::endl;
+       //   std::cout << "indices " << indices << std::endl;
+
+          // Compute the point id to hold the label
+          // Mid point or a random point
+          vtkIdType midPointId = indices[numberOfPoints / 2];
+          midPointId = indices[static_cast<vtkIdType>(vtkMath::Random(0, numberOfPoints))];
+
+          double midPoint[3];
+          points->GetPoint(midPointId, midPoint);*/
+
+        /*già commentato nel codice di fabio
+          std::cout << "\tmidPoint is " << midPointId << " with coordinate "
+                    << "("
+                    << midPoint[0] << ", "
+                    << midPoint[1] << ", "
+                    << midPoint[2] << ")"
+                    << " and value " << scalars->GetTuple1(midPointId)
+                    << std::endl;
+        */
+        /*
+          labelPoints->InsertNextPoint(midPoint);
+          labelScalars->InsertNextTuple1(scalars->GetTuple1(midPointId));
+
+          idScalars->InsertNextTuple1(i);
+     //     labelId->->InsertNextTuple1(scalars->GetTuple1(midPointId));
+          addTreeRoot( QString::number(i), QString::number(labelScalars->GetTuple1(i)),midPoint[0],midPoint[1],midPoint[2]);
+
+          i++;
+        }
+        labelPolyData->SetPoints(labelPoints);
+        labelPolyData->GetPointData()->SetScalars(labelScalars);
+
+        idPolyData->SetPoints(labelPoints);
+        idPolyData->GetPointData()->SetScalars(idScalars);
+
+
+        // The labeled data mapper will place labels at the points
+        vtkSmartPointer<vtkLabeledDataMapper> labelMapper = vtkSmartPointer<vtkLabeledDataMapper>::New();
+        labelMapper->SetFieldDataName("Isovalues");
+        labelMapper->SetInput(labelPolyData);
+        labelMapper->SetLabelModeToLabelScalars();
+        labelMapper->SetLabelFormat("%6.2f");
+
+
+        // The labeled data mapper will place labels at the points
+        vtkSmartPointer<vtkLabeledDataMapper> idMapper = vtkSmartPointer<vtkLabeledDataMapper>::New();
+        idMapper->SetFieldDataName("Id");
+        idMapper->SetInput(idPolyData);
+        idMapper->SetLabelModeToLabelScalars();
+
+        isolabels = vtkSmartPointer<vtkActor2D>::New();
+        isolabels->SetMapper(labelMapper);
+
+        idlabel = vtkSmartPointer<vtkActor2D>::New();
+        idlabel->SetMapper(idMapper);*/
+
+    }
+    else{
+        vtkWin->setContourVisualized(false);
+        ui->labelCheckBox->setEnabled(false);
+        ui->idCheckBox->setEnabled(false);
+    }
+
+
+}
+
+
+void contour::addContours()
+{
+
+    /*
+    qDebug()<<"Window name:"<<vtkWin->getWindowName();
+
+    vtkSmartPointer<vtkImageToStructuredPoints> convertFilter =
+    vtkSmartPointer<vtkImageToStructuredPoints>::New();
+
+    qDebug()<<"Get slice *"<<vtkWin->imageViewer->GetSlice();
+
+    convertFilter->SetInput(vtkWin->imageViewer->GetInput());
+    convertFilter->Update();
+
+    vtkStructuredPoints *myPoint= vtkStructuredPoints::New();
+
+    myPoint=convertFilter->GetStructuredPointsOutput();
+    myPoint->GetNumberOfPoints();
+    qDebug()<<"GetNumberOfPoints:"<<myPoint->GetNumberOfPoints();
+    qDebug()<<"GetNumberOfScalarComponents:"<<myPoint->GetNumberOfScalarComponents();
+    qDebug()<<"GetNumberOfCells:"<<myPoint->GetNumberOfCells();
+
+
+    qDebug()<<"fits file"<<fitsReader->GetNaxes(0)<<fitsReader->GetNaxes(1)<<fitsReader->GetNaxes(2);
+
+*/
+    //start cutter:
+    vtkSmartPointer<vtkCutter> cutter =
+            vtkSmartPointer<vtkCutter>::New();
+    // Create a plane to cut,here it cuts in the XZ direction (xz normal=(1,0,0);XY =(0,0,1),YZ =(0,1,0)
+
+    if(vtkWin->ui->spinBox_channels->text().toInt()==0)
+    {
+        vtkSmartPointer<vtkPlane> plane =
+                vtkSmartPointer<vtkPlane>::New();
+
+         plane->SetOrigin(0,0,vtkWin->viewer->GetSlice());
+        //  plane->SetOrigin(0,0,vtkWin->imageViewer->GetSlice());
+        plane->SetNormal(0,0,1);
+        // Set cutter function for a single planes
+        cutter->SetCutFunction(plane);
+    }
+    else
+    {
+
+        vtkSmartPointer<vtkPlanes> planes =
+                vtkSmartPointer<vtkPlanes>::New();
+        planes->SetBounds(0, fitsReader->GetNaxes(0), 0, fitsReader->GetNaxes(1), vtkWin->imageViewer->GetSlice()- vtkWin->ui->spinBox_channels->text().toInt(), vtkWin->imageViewer->GetSlice()+vtkWin->ui->spinBox_channels->text().toInt() );
+
+        vtkSmartPointer<vtkPoints> points =vtkSmartPointer<vtkPoints>::New();
+        points=planes->GetPoints();
+
+
+        int i;
+        double *point;
+        for (i=0;i<points->GetNumberOfPoints();i++)
+        {
+            point=points->GetPoint(i);
+        }
+
+
+        // Set cutter function for planes
+        cutter->SetCutFunction(planes);
+
+    }
+
+
+    cutter->SetInputData(fitsReader->GetOutput());
+    cutter->Update();
+
+    vtkSmartPointer<vtkPolyDataMapper> cutterMapper =
+            vtkSmartPointer<vtkPolyDataMapper>::New();
+    cutterMapper->SetInputConnection( cutter->GetOutputPort());
+
+
+
+    // Create plane actor
+    vtkSmartPointer<vtkActor> planeActor =
+            vtkSmartPointer<vtkActor>::New();
+    planeActor->GetProperty()->SetColor(1.0,1,0);
+    planeActor->GetProperty()->SetLineWidth(2);
+    planeActor->SetMapper(cutterMapper);
+
+
+
+    // Create renderers and add actors of plane and cube
+    vtkSmartPointer<vtkRenderer> renderer =
+            vtkSmartPointer<vtkRenderer>::New();
+    renderer->AddActor(planeActor); //display the rectangle resulting from the cut
+    //renderer->AddActor(cubeActor); //display the cube
+
+    // Add renderer to renderwindow and render
+    vtkSmartPointer<vtkRenderWindow> renderWindow =
+            vtkSmartPointer<vtkRenderWindow>::New();
+    renderWindow->AddRenderer(renderer);
+    renderWindow->SetSize(600, 600);
+
+    vtkSmartPointer<vtkRenderWindowInteractor> interactor =
+            vtkSmartPointer<vtkRenderWindowInteractor>::New();
+    interactor->SetRenderWindow(renderWindow);
+    renderer->SetBackground(0,0,0);
+
+
+    //end cutter
+
+
+    int pointThreshold = 10;
+
+    vtkSmartPointer<vtkPolyData> polyData =
+            vtkSmartPointer<vtkPolyData>::New();
+    vtkSmartPointer<vtkContourFilter> contoursFilter =
+            vtkSmartPointer<vtkContourFilter>::New();
+
+    polyData = cutter->GetOutput();
+   contoursFilter->GenerateValues(ui->numOfContourText->text().toInt(), ui->minValueText->text().toDouble(), ui->maxValueText->text().toDouble());
+
+    //contoursFilter->SetValue(0, abs((abs(ui->maxValueText->text().toDouble())-abs(ui->minValueText->text().toDouble()))) / 2.0);
+
+    contoursFilter->SetInputConnection(cutter->GetOutputPort());
+
+    // Connect the segments of the conours into polylines
+    vtkSmartPointer<vtkStripper> contourStripper =
+            vtkSmartPointer<vtkStripper>::New();
+    contourStripper->SetInputConnection(contoursFilter->GetOutputPort());
+    contourStripper->Update();
+
+    int numberOfContourLines = contourStripper->GetOutput()->GetNumberOfLines();
+
+    std::cout << "There are "
+              << numberOfContourLines << " contours lines."
+              << std::endl;
+
+    vtkPoints *points     =
+            contourStripper->GetOutput()->GetPoints();
+    vtkCellArray *cells   =
+            contourStripper->GetOutput()->GetLines();
+    vtkDataArray *scalars =
+            contourStripper->GetOutput()->GetPointData()->GetScalars();
+
+    // Create a polydata that contains point locations for the contour
+    // line labels
+    vtkSmartPointer<vtkPolyData> labelPolyData =
+            vtkSmartPointer<vtkPolyData>::New();
+    vtkSmartPointer<vtkPoints> labelPoints =
+            vtkSmartPointer<vtkPoints>::New();
+    vtkSmartPointer<vtkDoubleArray> labelScalars =
+            vtkSmartPointer<vtkDoubleArray>::New();
+    labelScalars->SetNumberOfComponents(1);
+    labelScalars->SetName("Isovalues");
+
+    vtkIdType *indices;
+    vtkIdType numberOfPoints;
+    unsigned int lineCount = 0;
+    for (cells->InitTraversal();
+         cells->GetNextCell(numberOfPoints, indices);
+         lineCount++)
+    {
+        if (numberOfPoints < pointThreshold)
+        {
+            continue;
+        }
+        //std::cout << "Line " << lineCount << ": " << std::endl;
+
+        // Compute the point id to hold the label
+        // Mid point or a random point
+        vtkIdType midPointId = indices[numberOfPoints / 2];
+        midPointId =
+                indices[static_cast<vtkIdType>(vtkMath::Random(0, numberOfPoints))];
+
+        double midPoint[3];
+        points->GetPoint(midPointId, midPoint);
+        /*std::cout << "\tmidPoint is " << midPointId << " with coordinate "
+                         << "("
+                         << midPoint[0] << ", "
+                         << midPoint[1] << ", "
+                         << midPoint[2] << ")"
+                         << " and value " << scalars->GetTuple1(midPointId)
+                         << std::endl;*/
+        labelPoints->InsertNextPoint(midPoint);
+        labelScalars->InsertNextTuple1(scalars->GetTuple1(midPointId));
+    }
+    labelPolyData->SetPoints(labelPoints);
+    labelPolyData->GetPointData()->SetScalars(labelScalars);
+
+    vtkSmartPointer<vtkPolyDataMapper> contourMapper =
+            vtkSmartPointer<vtkPolyDataMapper>::New();
+    contourMapper->SetInputConnection(contourStripper->GetOutputPort());
+    //contourMapper->ScalarVisibilityOff();
+
+
+    /*START LUT*/
+    contourMapper->ScalarVisibilityOn();
+    contourMapper->SetScalarModeToUsePointData();
+    contourMapper->SetColorModeToMapScalars();
+
+    contourMapper->SetScalarRange(fitsReader->GetMin(), fitsReader->GetMax());
+
+    //vtkSmartPointer<vtkScalarBarActor>
+    scalarBar =
+            vtkSmartPointer<vtkScalarBarActor>::New();
+
+    //scalarBar->SetLookupTable(contourMapper->GetLookupTable());
+    //scalarBar->SetTitle("Contours Scalar Bar");
+    scalarBar->DragableOn();
+
+    scalarBar->SetNumberOfLabels(5);
+    //qDebug()<<"GetTextPosition:"<<scalarBar->GetTextPosition();
+
+    //scalarBar->SetOrientationToHorizontal();
+
+    // Create one text property for all
+    vtkSmartPointer<vtkTextProperty> textProperty =
+            vtkSmartPointer<vtkTextProperty>::New();
+    textProperty->SetFontSize(0.1);
+    //textProperty->SetJustificationToCentered();
+    textProperty->SetColor(0.4,0.4,1);
+    textProperty->SetFontFamilyToArial();
+
+    scalarBar->SetLabelTextProperty(textProperty);
+
+    //scalarBar->SetTextureGridWidth(0.1);
+    scalarBar->SetWidth(0.05);
+    scalarBar->SetHeight(0.9);
+
+    vtkSmartPointer<vtkTextProperty> titleTextProperty =
+            vtkSmartPointer<vtkTextProperty>::New();
+    titleTextProperty->SetFontSize(24);
+    titleTextProperty->SetFontFamilyToArial();
+    titleTextProperty->SetBold(true);
+    titleTextProperty->SetColor(1.,0.,0.);
+
+    scalarBar->SetTitleTextProperty(titleTextProperty);
+    //vtkWin->showColorbar(true);
+
+
+    // Create a lookup table to share between the mapper and the scalarbar
+    vtkSmartPointer<vtkLookupTable> hueLut =
+            vtkSmartPointer<vtkLookupTable>::New();
+    //hueLut->SetTableRange (0, 1);
+    hueLut->SetHueRange (0, 1);
+    hueLut->SetSaturationRange (1, 1);
+    hueLut->SetValueRange (1, 1);
+    hueLut->Build();
+
+    contourMapper->SetLookupTable( hueLut );
+    scalarBar->SetLookupTable( hueLut );
+
+    //# create the scalar_bar_widget
+    //vtkSmartPointer<vtkScalarBarWidget>  scalar_bar_widget = vtkSmartPointer<vtkScalarBarWidget>::New();
+    //scalar_bar_widget->SetInteractor(vtkWin->ui->qVTK1->GetRenderWindow()->GetInteractor());
+    //scalar_bar_widget->SetScalarBarActor(scalarBar);
+    //scalar_bar_widget->On();
+    //scalar_bar_widget->SetEnabled(1);
+
+    /*
+
+        vtkAxesWidget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
+        vtkAxesWidget->SetInteractor(ui->qVTK1->GetRenderWindow()->GetInteractor());
+
+        vtkAxesWidget->SetOrientationMarker(vtkAxes);
+
+        vtkAxesWidget->SetOutlineColor( 0.9300, 0.5700, 0.1300 );
+        vtkAxesWidget->SetViewport( 0.0, 0.0, 0.2, 0.2 );
+        vtkAxesWidget->SetEnabled(1);
+        vtkAxesWidget->InteractiveOff();
+*/
+
+
+    //vtkSmartPointer<vtkLookupTable> lut = vtkSmartPointer<vtkLookupTable>::New();
+    //lut->SetTableRange( 0, 255 );
+    //qDebug()<<"my LUT scale: "<<lut->GetScale();
+    //SelectLookTable("glow",lut);
+    //contourMapper->SetLookupTable(lut);
+    //qDebug()<<"cutterMapper LUT scale: "<<contourMapper->GetLookupTable();
+
+    //ui->qVTK1->update();
+
+    /*FINE LUT*/
+
+
+    // isolines =
+    //   vtkSmartPointer<vtkActor>::New();
+    isolines->SetMapper(contourMapper);
+
+
+    //contour_actor=vtkSmartPointer<vtkActor>::New();
+    //contour_actor->SetMapper(contourMapper);
+
+    vtkSmartPointer<vtkLookupTable> surfaceLUT =
+            vtkSmartPointer<vtkLookupTable>::New();
+    surfaceLUT->SetRange(
+                polyData->GetPointData()->GetScalars()->GetRange());
+    surfaceLUT->Build();
+
+    vtkSmartPointer<vtkPolyDataMapper> surfaceMapper =
+            vtkSmartPointer<vtkPolyDataMapper>::New();
+#if VTK_MAJOR_VERSION <= 5
+    surfaceMapper->SetInput(polyData);
+#else
+    surfaceMapper->SetInputData(polyData);
+#endif
+    surfaceMapper->ScalarVisibilityOn();
+    surfaceMapper->SetScalarRange(
+                polyData->GetPointData()->GetScalars()->GetRange());
+    surfaceMapper->SetLookupTable(surfaceLUT);
+
+    vtkSmartPointer<vtkActor> surface =
+            vtkSmartPointer<vtkActor>::New();
+    surface->SetMapper(surfaceMapper);
+
+    // The labeled data mapper will place labels at the points
+    vtkSmartPointer<vtkLabeledDataMapper> labelMapper =
+            vtkSmartPointer<vtkLabeledDataMapper>::New();
+    labelMapper->SetFieldDataName("Isovalues");
+#if VTK_MAJOR_VERSION <= 5
+    labelMapper->SetInput(labelPolyData);
+#else
+    labelMapper->SetInputData(labelPolyData);
+#endif
+    labelMapper->SetLabelModeToLabelScalars();
+    labelMapper->SetLabelFormat("%6.2f");
+
+
+
+
+    //vtkSmartPointer<vtkActor2D> isolabels =
+    isolabels= vtkSmartPointer<vtkActor2D>::New();
+    isolabels->SetMapper(labelMapper);
+
+
+    // Create an interactor
+    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
+            vtkSmartPointer<vtkRenderWindowInteractor>::New();
+    renderWindowInteractor->SetRenderWindow(renderWindow);
+
+
+    //colore
+    //isolines->GetProperty()->SetColor(ui->redText->text().toDouble(), ui->greenText->text().toDouble(), ui->blueText->text().toDouble());
+
+
+
+    vtkWin->m_Ren2->AddActor(isolines);
+   // vtkWin->m_Ren2->AddActor(scalarBar);
+   // vtkWin->m_Ren2->AddActor(idlabel);
+    if (ui->labelCheckBox->isChecked()==true)
+        vtkWin->m_Ren2->AddActor(isolabels);
+    vtkWin->m_Ren2->Render();
+    vtkWin->ui->isocontourVtkWin->update();
+    // Add the actors to the scene
+    //renderer->AddActor(isolines);
+    //renderer->AddActor(isolabels);8
+    //  renderer->AddActor(surface);
+
+    // Render the scene (lights and cameras are created automatically)
+    //renderWindow->Render();
+    //renderWindowInteractor->Start();
+
+
+
+
+    /*
+    vtkMarchingSquares *contour = vtkMarchingSquares::New();
+
+    contour->SetInput(cutter->GetOutput());
+
+    contour->GenerateValues(5,fitsReader->GetMin(),fitsReader->GetMax());
+
+    // Connect the segments of the conours into polylines
+    vtkSmartPointer<vtkStripper>contourStripper =  vtkSmartPointer<vtkStripper>::New();
+    contourStripper->SetInputConnection(contour->GetOutputPort());
+    contourStripper->Update();
+    contourStripper->GetOutput()->BuildLinks();
+
+
+    vtkPolyDataMapper *contour_mapper = vtkPolyDataMapper::New();
+    //contour_mapper->SetInput(contour->GetOutput());
+    contour_mapper->SetInputConnection(contourStripper->GetOutputPort());
+    contour_mapper->ScalarVisibilityOff();
+
+    qDebug()<<"add actor contour";
+    vtkActor* contour_actor = vtkActor::New();
+    contour_actor->SetMapper(contour_mapper);
+    //vtkWin->m_Ren1->AddActor(contour_actor);
+
+
+    //UFFa
+    // Add the actors
+    renderer->AddActor(contour_actor);
+    // Begin interaction
+    //renderWindow->Render();
+
+
+    // Create renderers and add actors of plane and cube
+    vtkSmartPointer<vtkRenderer> renderer2 =
+            vtkSmartPointer<vtkRenderer>::New();
+    renderer2->AddActor(contour_actor); //display the rectangle resulting from the cut
+    //renderer->AddActor(cubeActor); //display the cube
+
+    // Add renderer to renderwindow and render
+    vtkSmartPointer<vtkRenderWindow> renderWindow2 =
+            vtkSmartPointer<vtkRenderWindow>::New();
+    renderWindow2->AddRenderer(renderer);
+    renderWindow2->SetSize(600, 600);
+
+    vtkSmartPointer<vtkRenderWindowInteractor> interactor2 =
+            vtkSmartPointer<vtkRenderWindowInteractor>::New();
+    interactor2->SetRenderWindow(renderWindow);
+    renderer2->SetBackground(0,0,0);
+
+
+    renderWindow->Render();
+    interactor->Start();
+
+
+    renderWindow2->Render();
+    interactor2->Start();
+
+    qDebug()<<"fine me";*/
+
+
+
+}
+
+void contour::addContours2()
+{
+
+
+    // Create a plane to cut,here it cuts in the XZ direction (xz normal=(1,0,0);XY =(0,0,1),YZ =(0,1,0)
+    vtkSmartPointer<vtkPlane> plane =
+            vtkSmartPointer<vtkPlane>::New();
+    plane->SetOrigin(0,0,vtkWin->imageViewer->GetSlice());
+    plane->SetNormal(0,0,1);
+
+
+    // Create cutter
+    vtkSmartPointer<vtkCutter> cutter =
+            vtkSmartPointer<vtkCutter>::New();
+    cutter->SetCutFunction(plane);
+    cutter->SetInputConnection(fitsReader->GetOutputPort());
+    cutter->GenerateValues(5, fitsReader->GetMin(), fitsReader->GetMax());
+    cutter->Update();
+
+    vtkSmartPointer<vtkPolyDataMapper> cutterMapper =
+            vtkSmartPointer<vtkPolyDataMapper>::New();
+    cutterMapper->SetInputConnection ( cutter->GetOutputPort());
+    //cutterMapper->ScalarVisibilityOff();
+
+
+    // Create plane actor
+    vtkSmartPointer<vtkActor> planeActor =
+            vtkSmartPointer<vtkActor>::New();
+    planeActor->GetProperty()->SetColor(1.0,1,0);
+    planeActor->GetProperty()->SetLineWidth(2);
+    planeActor->SetMapper(cutterMapper);
+
+    // Create renderers and add actors of plane and cube
+    vtkSmartPointer<vtkRenderer> renderer =
+            vtkSmartPointer<vtkRenderer>::New();
+    renderer->AddActor(planeActor); //display the rectangle resulting from the cut
+    //renderer->AddActor(cubeActor); //display the cube
+
+    // Add renderer to renderwindow and render
+    vtkSmartPointer<vtkRenderWindow> renderWindow =
+            vtkSmartPointer<vtkRenderWindow>::New();
+    renderWindow->AddRenderer(renderer);
+    renderWindow->SetSize(600, 600);
+
+    vtkSmartPointer<vtkRenderWindowInteractor> interactor =
+            vtkSmartPointer<vtkRenderWindowInteractor>::New();
+    interactor->SetRenderWindow(renderWindow);
+    renderer->SetBackground(0,0,0);
+
+    renderWindow->Render();
+    interactor->Start();
+
+}
+
+
+void contour::createwin()
+{
+
+}
diff --git a/Code/src/contour.h b/Code/src/contour.h
new file mode 100644
index 0000000000000000000000000000000000000000..65c9dfc76ca77e75baccd24e478a90d4562c37c9
--- /dev/null
+++ b/Code/src/contour.h
@@ -0,0 +1,75 @@
+#ifndef CONTOUR_H
+#define CONTOUR_H
+
+#include <QWidget>
+#include <vtkActor.h>
+#include <vtkMarchingSquares.h>
+#include <vtkSmartPointer.h>
+#include <QTreeWidgetItem>
+#include <QAction>
+#include "vtkfitsreader.h"
+#include "vtkActor2D.h"
+#include "vtkStripper.h"
+#include "vtkwindow_new.h"
+#include "vtkRenderer.h"
+#include "vtkRenderWindow.h"
+#include "vtkRenderWindowInteractor.h"
+#include "vtkScalarBarActor.h"
+
+
+
+namespace Ui {
+class contour;
+}
+
+class contour : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit contour(QWidget *parent = 0);
+    ~contour();
+    void setFitsReader(vtkFitsReader *fits,vtkwindow_new *win);
+
+
+
+    vtkActor *contour_actor;
+    vtkSmartPointer<vtkRenderer> renderer;
+    vtkSmartPointer<vtkRenderWindow> renderWindow;
+    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor;
+    bool createwin_flag=false;
+    void createwin();
+    Ui::contour *ui;
+    vtkSmartPointer<vtkActor> isolines;
+    vtkSmartPointer<vtkActor2D> getIsolabels(){return isolabels;};
+    vtkSmartPointer<vtkActor2D> getIdlabel(){return idlabel;};
+    vtkSmartPointer<vtkScalarBarActor> getScalarBar(){return  scalarBar;};
+
+public slots:
+ void on_okButton_clicked();
+
+private:
+
+    vtkFitsReader *fitsReader;
+    vtkwindow_new *vtkWin;
+    vtkMarchingSquares *contours;
+    vtkSmartPointer<vtkActor2D> isolabels;
+    vtkSmartPointer<vtkActor2D> idlabel;
+    vtkSmartPointer<vtkStripper> contourStripper;
+    vtkSmartPointer<vtkScalarBarActor> scalarBar;
+
+
+
+private slots:
+
+    void createContour();
+    void addContours2();
+    void deleteContour();
+    void addContours();
+    void toggledLabel(bool t);
+    void toggledId(bool t);
+
+    //void on_pushButton_clicked();
+};
+
+#endif // CONTOUR_H
diff --git a/Code/src/dbquery.cpp b/Code/src/dbquery.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b133c7ee9bf96efe3b44f50859ffb5224fcffc16
--- /dev/null
+++ b/Code/src/dbquery.cpp
@@ -0,0 +1,898 @@
+#include "dbquery.h"
+#include "ui_dbquery.h"
+#include "qdebug.h"
+#include <QXmlStreamReader>
+#include <QMessageBox>
+#include <QDomElement>
+#include "xmlparser.h"
+#include <QStringList>
+#include "QDir"
+#include <QCoreApplication>
+#include <QTimer>
+#include "downloadmanager.h"
+#include <QSignalMapper>
+#include "vtkwindow_new.h"
+#include "QObject"
+#include <QSettings>
+
+dbquery::dbquery(QWidget *parent) :
+    QDialog(parent),
+    ui(new Ui::dbquery)
+{
+    ui->setupUi(this);
+    n_surveys=10; //+2
+    n_species=11; //+1
+    n_transitions=7; //+1
+    loading = new LoadingWidget();
+
+    QStringList list_surveys=(QStringList()<<"CHAMP"<<"HOPS"<<"FCRAO_GRS"<<"MALT90"<<"THRUMMS"<<"NANTEN"<<"OGS"<<"JCMT-HARP"<<"VGPS"<<"CGPS");
+    QStringList list_species=(QStringList()<<"12CO"<<"13CO"<<"C18O"<<"CN"<<"H2O"<<"HCN"<<"HNC"<<"HCO+"<<"N2H+"<<"NH3"<<"HI");
+    QStringList list_transitions=(QStringList()<<"1-0"<<"1(1)0a-1(1)0s"<<"1(23)-0(12)"<<"2(2)0a-2(2)0s"<<"3-2"<<"6(1,6)-5(2,3)"<<"21CM");
+
+    ui->comboBox_surveys->addItems(list_surveys);
+    ui->comboBox_species->addItems(list_species);
+    ui->comboBox_transitions->addItems(list_transitions);
+
+    //nam = new QNetworkAccessManager(this);
+
+
+
+    QSettings settings(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini"), QSettings::NativeFormat);
+    vlkbUrl= settings.value("vlkburl", "").toString();
+
+    parser=new xmlparser();
+
+
+
+}
+
+dbquery::~dbquery()
+{
+    delete ui;
+    delete parser;
+}
+
+void dbquery::finishedSlot(QNetworkReply* reply)
+{
+    QString string;
+    QString file;
+
+    // Reading attributes of the reply
+    // e.g. the HTTP status code
+    QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+    // Or the target URL if it was a redirect:
+    QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+
+    if (reply->error() == QNetworkReply::NoError)
+    {
+        QXmlStreamReader xml(reply);
+
+        QString url=reply->request().url().toString();
+
+
+        QString subString1 = url.mid(0,62); //
+
+        //first query (l,b,r,v_l, v_u) xml result -> looking for a PUBLISHER ID
+        if( url.contains("vlkb_search") )
+        {
+            parser->parseXML(xml, string);
+
+            if(string.compare("NULL")!=0)
+            {
+                loading->setFileName("Datacube found");
+
+                QUrl url2 (vlkbUrl+"/vlkb_cutout?pubdid="+string+"&l="+ui->lineEdit_l->text()+
+                           "&b="+ui->lineEdit_b->text()+"&r="+ui->lineEdit_b->text()+"&vl="+ui->lineEdit_vl->text()+"&vu="+ui->lineEdit_vu->text()+"&nullvals");
+
+                nam->get(QNetworkRequest(url2));
+            }
+            else
+            {
+                loading->setFileName("Datacube inexistent");
+                loading->loadingEnded();
+                loading->hide();
+                QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr("Datacube inexistent - Try again"));
+            }
+        }
+        //second query (PUBDID) xml result -> looking for a datacube (URL)
+        if (url.contains("vlkb_cutout") )
+        {
+            parser->parseXML(xml, string);
+            if(string.compare("NULL")!=0)
+            {
+                loading->setFileName("Datacube found");
+                DownloadManager *manager= new DownloadManager();
+                QString urlString=string.trimmed();
+                QUrl url3(urlString);
+
+                //segnale tra due oggetti:
+                connect(manager, SIGNAL(downloadCompleted()),this, SLOT(on_download_completed()));
+
+                file=manager->doDownload(url3);
+                loading->loadingEnded();
+                loading->hide();
+
+                downloadedFile=file;
+
+
+            }
+            else
+            {
+                QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr("Inconsistent data (PubDID vs Region only partially overlap)"));
+                loading->loadingEnded();
+                loading->hide();
+            }
+
+        }
+    }
+
+
+    else
+    {
+        //handle errors here
+        qDebug()<<"Server is not replying";
+    }
+    //delete reply;
+    reply->deleteLater();
+
+}
+
+//this method is used for the datacube extraction
+void dbquery::finishedSlot2(QNetworkReply* reply)
+{
+
+    QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+
+
+
+    if (reply->error() == QNetworkReply::NoError)
+    {
+
+        QList< QMap<QString,QString> > datacubes;
+        QXmlStreamReader xml(reply);
+        parser->datacubeExtraction(xml, datacubes);
+
+        this->addDatacubesToUI(datacubes);
+
+    } else
+    {
+        //handle errors here
+        qDebug()<<"Server not replying";
+    }
+    //delete reply;
+    this->activateWindow();
+    this->setFocus();
+    loading->close();
+    reply->deleteLater();
+
+    
+}
+
+
+
+void dbquery::setCoordinate(QString l,QString b)
+{
+    ui->lineEdit_l->setText( l );
+    ui->lineEdit_b->setText( b);
+    double ray=0.1;
+    if(l.toDouble()>b.toDouble())
+        ray=b.toDouble()/2;
+    else
+        ray=l.toDouble()/2;
+    QString myray=QString::number(qAbs(ray));
+    ui->lineEdit_r->setText( myray);
+}
+
+void dbquery::setCoordinate(char* l,char* b)
+{
+    ui->lineEdit_l->setText( l );
+    ui->lineEdit_b->setText( b);
+}
+
+
+
+void dbquery::enableAllItems(QComboBox *comboBox, int nItems){
+
+    QVariant v(1 | 32);
+    //qDebug()<<"enabling All: "<<nItems;
+    QModelIndex index;
+    for(int i=0; i<nItems; i++)
+    {
+        index = comboBox->model()->index(i, 0);
+        comboBox->model()->setData(index, v, Qt::UserRole - 1);
+    }
+    // comboBox->setCurrentIndex(0);
+}
+
+void dbquery::disableItems(QComboBox *comboBox, int nItem, int* indexes, int size){
+    QVariant v(0);
+    //qDebug()<<"Disabling: "<<nItem<<" items";
+    QModelIndex index;
+    for(int i=0; i<nItem; i++)
+    {
+        index = comboBox->model()->index(indexes[i], 0);
+        comboBox->model()->setData(index, v, Qt::UserRole - 1);
+    }
+    //comboBox->setCurrentIndex(5);
+}
+
+void dbquery::on_comboBox_surveys_activated(const QString &arg1)
+{
+
+    if(arg1=="CHAMP")
+    {
+        //meno il 7
+        int indexes_species[10]={0,1,2,3,4,5,6,8,9,10};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species,n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //         ui->comboBox_species->setCurrentIndex(7);
+
+
+        //tranne 0
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition,n_transitions);
+        //        ui->comboBox_transitions->setCurrentIndex(0);
+    }
+
+    else if(arg1=="HOPS")
+    {
+
+        //meno 4 e 9
+        int indexes_species[9]={0,1,2,3,5,6,7,8,10};
+        int nItems_species=9;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //  ui->comboBox_species->setCurrentIndex(4);
+
+        //tranne 1,3,5
+        int indexes_transition[4]={0,2,4,6};
+        int nItems_transition=4;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //      ui->comboBox_transitions->setCurrentIndex(1);
+
+    }
+    else if(arg1=="FCRAO_GRS")
+    {
+
+
+        //meno 1
+        int indexes_species[10]={0,2,3,4,5,6,7,8,9,10};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        // ui->comboBox_species->setCurrentIndex(1);
+        //tranne 0
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition,n_transitions);
+        // ui->comboBox_transitions->setCurrentIndex(0);
+    }
+    else if(arg1=="MALT90")
+    {
+
+        //meno 5 6 7 8
+        int indexes_species[7]={0,1,2,3,4,9,10};
+        int nItems_species=7;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        // this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        ui->comboBox_species->setCurrentIndex(5);
+        //tranne 0
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(0);
+    }
+    else if(arg1=="THRUMMS")
+    {
+        //meno 0 1 2 3
+        int indexes_species[7]={4,5,6,7,8,9,10};
+        int nItems_species=7;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(0);
+        //tranne 0 e 2
+        int indexes_transition[5]={1,3,4,5,6};
+        int nItems_transition=5;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition,n_transitions);
+        ui->comboBox_transitions->setCurrentIndex(0);
+    }
+    else if(arg1=="NANTEN")
+    {
+        //meno 0
+        int indexes_species[10]={1,2,3,4,5,6,7,8,9,10};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        ui->comboBox_species->setCurrentIndex(0);
+        //tranne 0
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        // ui->comboBox_transitions->setCurrentIndex(0);
+    }
+    else if(arg1=="OGS")
+    {
+        //meno 0 1
+        int indexes_species[9]={2,3,4,5,6,7,8,9,10};
+        int nItems_species=9;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(0);
+        //tranne 0
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(0);
+
+    }
+    else if(arg1=="JCMT-HARP")
+    {
+        //meno 0
+        int indexes_species[10]={1,2,3,4,5,6,7,8,9,10};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(0);
+        //tranne 4
+        int indexes_transition[6]={0,1,2,3,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(4);
+    }
+
+    else if(arg1=="VGPS")
+    {
+        //meno 10
+        int indexes_species[10]={0,1,2,3,4,5,6,7,8,9};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(10);
+        //tranne 6
+        int indexes_transition[6]={0,1,2,3,4,5};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(6);
+    }
+    else if(arg1=="CGPS")
+    {
+        //meno 10
+        int indexes_species[10]={0,1,2,3,4,5,6,7,8,9};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(10);
+        //tranne 6 di 7
+        int indexes_transition[6]={0,1,2,3,4,5};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(6);
+    }
+
+    this->enableAllItems(ui->comboBox_surveys, n_surveys);
+}
+
+void dbquery::on_comboBox_species_activated(const QString &arg1)
+{
+    if(arg1=="12CO")
+    {
+
+
+        //tranne 4,5,6,7 di 8
+        int indexes_surveys[6]={0,1,2,3,8,9};
+        int nItems_surveys=6;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(4);
+
+        //meno il 0,4 di 6
+        int indexes_transition[5]={1,2,3,5,6};
+        int nItems_transition=5;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(0);
+    }
+    else if(arg1=="13CO")
+    {
+        //tranne 2,4,6 di 8
+        int indexes_surveys[7]={0,1,3,5,7,8,9};
+        int nItems_surveys=7;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys,n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(2);
+
+        //meno il 0 di 6
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition ,n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(0);
+
+    }
+    else if(arg1=="C18O")
+    {
+
+        //tranne 4 di 8
+        int indexes_surveys[9]={0,1,2,3,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(4);
+
+        //meno il 0 di 6
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(0);
+
+    }
+    else if(arg1=="CN")
+    {
+        //tranne 4 di 8
+        int indexes_surveys[9]={0,1,2,3,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(4);
+
+        //meno il 2 di 6
+        int indexes_transition[6]={0,1,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(2);
+
+    }
+    else if(arg1=="H2O")
+    {
+        //tranne 1 di 8
+        int indexes_surveys[9]={0,2,3,4,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(1);
+
+        //meno il 5 di 6
+        int indexes_transition[6]={0,1,2,3,4,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(5);
+
+    }
+    else if(arg1=="HCN")
+    {
+
+        //tranne 3 di 8
+        int indexes_surveys[9]={0,1,2,4,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(3);
+
+        //meno il 0 di 6
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(0);
+
+    }
+    else if(arg1=="HNC")
+    {
+        //tranne 3 di 8
+        int indexes_surveys[9]={0,1,2,4,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(3);
+
+        //meno il 0 di 6
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(0);
+
+    }
+    else if(arg1=="HCO+")
+    {
+
+        //tranne 0,3 di 8
+        int indexes_surveys[8]={1,2,4,5,6,7,8,9};
+        int nItems_surveys=8;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(0);
+
+        //meno il 0 di 6
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(0);
+
+    }
+    else if(arg1=="N2H+")
+    {
+        //tranne 3 di 8
+        int indexes_surveys[9]={0,1,2,4,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(3);
+
+        //meno il 0 di 6
+        int indexes_transition[6]={1,2,3,4,5,6};
+        int nItems_transition=6;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(0);
+
+    }
+    else if(arg1=="NH3")
+    {
+        //tranne 1 di 8
+        int indexes_surveys[9]={0,2,3,4,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(1);
+
+        //meno il 1,3 di 6
+        int indexes_transition[5]={0,2,4,5,6};
+        int nItems_transition=5;
+        this->enableAllItems(ui->comboBox_transitions, n_transitions);
+        this->disableItems(ui->comboBox_transitions, nItems_transition, indexes_transition, n_transitions);
+        //ui->comboBox_transitions->setCurrentIndex(1);
+
+    }
+    this->enableAllItems(ui->comboBox_species, n_species);
+}
+
+void dbquery::on_comboBox_transitions_activated(const QString &arg1)
+{
+    if(arg1=="1-0")
+        /*   n_surveys=10; //+2
+    n_species=11; //+1
+    n_transitions=7; //+1*/
+    {
+        //tranne 0,2,3,4,5,6,7 di 8
+        int indexes_surveys[3]={1,8,9};
+        int nItems_surveys=3;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(0);
+
+        //meno il 0,1,2,5,6,7,8 di 10
+        int indexes_species[4]={3,4,9,10};
+        int nItems_species=4;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+
+    }
+    else if(arg1=="1(1)0a-1(1)0s")
+    {
+        //tranne 1 di 8
+        int indexes_surveys[9]={0,2,3,4,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(1);
+
+        //meno il 9 di 10
+        int indexes_species[10]={0,1,2,3,4,5,6,7,8,10};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(9);
+    }
+    else if(arg1=="1(23)-0(12)")
+    {
+        //tranne 4 di 8
+        int indexes_surveys[9]={0,1,2,3,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(4);
+
+        //meno il 3 di 10
+        int indexes_species[10]={0,1,2,4,5,6,7,8,9,10};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(3);
+
+    }
+    else if(arg1=="2(2)0a-2(2)0s")
+    {
+        //tranne 1 di 8
+        int indexes_surveys[9]={0,2,3,4,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        ui->comboBox_surveys->setCurrentIndex(1);
+        //meno il 9 di 10
+        int indexes_species[10]={0,1,2,3,4,5,6,7,8,10};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(9);
+
+    }
+    else if(arg1=="3-2")
+    {
+        //tranne 7 di 8
+        int indexes_surveys[9]={0,1,2,3,4,5,6,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(7);
+        //meno il 0 di 10
+        int indexes_species[10]={1,2,3,4,5,6,7,8,9,10};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species, n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(0);
+
+    }
+    else if(arg1=="6(1,6)-5(2,3)")
+    {
+        //tranne 1 di 8
+        int indexes_surveys[9]={0,2,3,4,5,6,7,8,9};
+        int nItems_surveys=9;
+        this->enableAllItems(ui->comboBox_surveys, n_surveys);
+        this->disableItems(ui->comboBox_surveys, nItems_surveys, indexes_surveys, n_surveys);
+        //ui->comboBox_surveys->setCurrentIndex(1);
+        //meno il 4 di 10
+        int indexes_species[10]={0,1,2,3,5,6,7,8,9,10};
+        int nItems_species=10;
+        this->enableAllItems(ui->comboBox_species,n_species);
+        this->disableItems(ui->comboBox_species, nItems_species, indexes_species, n_species);
+        //ui->comboBox_species->setCurrentIndex(4);
+
+    }
+
+    this->enableAllItems(ui->comboBox_transitions, n_transitions);
+}
+
+void dbquery::on_queryPushButton_clicked()
+{
+
+
+    
+    ui->lineEdit_l->setReadOnly(true);
+    ui->lineEdit_b->setReadOnly(true);
+    ui->lineEdit_r->setReadOnly(true);
+    ui->lineEdit_vl->setReadOnly(true);
+    ui->lineEdit_vu->setReadOnly(true);
+    
+    loading->init();
+    loading->setFileName("Connecting to the cutout service");
+    loading ->show();
+    loading->activateWindow();
+    
+    survey=ui->comboBox_surveys->currentText();
+    species=ui->comboBox_species->currentText();
+    transition=ui->comboBox_transitions->currentText();
+    
+    parser->datacube_element.insert("Survey",survey);
+    parser->datacube_element.insert("Species",species);
+    parser->datacube_element.insert("Transition",transition);
+    nam = new QNetworkAccessManager(this);
+    QObject::connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(finishedSlot(QNetworkReply*)));
+
+    //QUrl url ("http://vialactea:ia2vlkb@palantir19.oats.inaf.it:8080/libjnifitsdb-0.15.0/vlkb_search?l="+ui->lineEdit_l->text()+"&b="+ui->lineEdit_b->text()+"&r="+ui->lineEdit_r->text()+"&vl="+ui->lineEdit_vl->text()+"&vu="+ui->lineEdit_vu->text());
+    QUrl url(vlkbUrl+"/vlkb_search?l="+ui->lineEdit_l->text()+"&b="+ui->lineEdit_b->text()+"&r="+ui->lineEdit_r->text()+"&vl="+ui->lineEdit_vl->text()+"&vu="+ui->lineEdit_vu->text());
+
+
+    
+
+    //QNetworkReply* reply = nam->get(QNetworkRequest(url));
+    nam->get(QNetworkRequest(url));
+    
+    ui->lineEdit_l->setReadOnly(false);
+    ui->lineEdit_b->setReadOnly(false);
+    ui->lineEdit_r->setReadOnly(false);
+    ui->lineEdit_vl->setReadOnly(false);
+    ui->lineEdit_vu->setReadOnly(false);
+    
+    
+
+
+    
+}
+
+//datacube list button
+void dbquery::on_pushButton_map_clicked()
+{
+    loading = new LoadingWidget();
+    loading->init();
+    loading->setFileName("Getting datacube list...");
+    loading ->show();
+    loading->activateWindow();
+    loading->setFocus();
+
+    nam = new QNetworkAccessManager(this);
+    QObject::connect(nam, SIGNAL(finished(QNetworkReply*)),
+                     this, SLOT(finishedSlot2(QNetworkReply*)));
+    /*
+    QUrl url ("http://vialactea:ia2vlkb@palantir19.oats.inaf.it:8080/libjnifitsdb-0.15.0/vlkb_search?l="+ui->lineEdit_l->text()+
+              "&b="+ui->lineEdit_b->text()+"&r="+ui->lineEdit_r->text()+"&vl="+ui->lineEdit_vl->text()+"&vu="+ui->lineEdit_vu->text());
+  */
+    QUrl url (vlkbUrl+"/vlkb_search?l="+ui->lineEdit_l->text()+
+              "&b="+ui->lineEdit_b->text()+"&r="+ui->lineEdit_r->text()+"&vl="+ui->lineEdit_vl->text()+"&vu="+ui->lineEdit_vu->text());
+
+
+    nam->get(QNetworkRequest(url));
+}
+
+void dbquery::handleButton(int i)
+{
+    loading = new LoadingWidget();
+    loading->init();
+    loading->setFileName("Downloading selected datacube...");
+    loading ->show();
+    loading->activateWindow();
+    loading->setFocus();
+
+
+    QMap<QString,QString> datacube=datacubes_list[i];
+
+    nam = new QNetworkAccessManager(this);
+    QObject::connect(nam, SIGNAL(finished(QNetworkReply*)),
+                     this, SLOT(finishedSlot(QNetworkReply*)));
+    
+    QUrl url (vlkbUrl+"/vlkb_cutout?pubdid="+datacube["PublisherDID"]+"&l="
+            +ui->lineEdit_l->text()+"&b="+ui->lineEdit_b->text()+"&r="+ui->lineEdit_r->text()+
+            "&vl="+ui->lineEdit_vl->text()+"&vu="+ui->lineEdit_vu->text()+"&nullvals");
+    /*
+ QUrl url ("http://vialactea:ia2vlkb@palantir19.oats.inaf.it:8080/libjnifitsdb-0.14.2/vlkb_cutout?pubdid="+datacube["PublisherDID"]+"&l="
+ +ui->lineEdit_l->text()+"&b="+ui->lineEdit_b->text()+"&r="+ui->lineEdit_r->text()+
+ "&vl="+ui->lineEdit_vl->text()+"&vu="+ui->lineEdit_vu->text());
+*/
+    nam->get(QNetworkRequest(url));
+}
+
+void dbquery::addDatacubesToUI(QList< QMap<QString,QString> >& datacubes) {
+    
+    
+    /* Create the data model */
+    // 1. give it some headers
+    
+    datacubes_list=datacubes;
+    
+    int i=0;
+    int z=0;
+    ui->datacube_tableWidget->clear();
+    int rows=ui->datacube_tableWidget->rowCount();
+    int columns=ui->datacube_tableWidget->columnCount();
+    for(int j=0; j<rows; j++)
+        ui->datacube_tableWidget->removeRow(j);
+    for(int j=0; j<columns; j++)
+        ui->datacube_tableWidget->removeColumn(j);
+    
+    
+    ui->datacube_tableWidget->insertColumn(0);
+    ui->datacube_tableWidget->setHorizontalHeaderItem(0, new QTableWidgetItem("Survey"));
+    ui->datacube_tableWidget->insertColumn(1);
+    ui->datacube_tableWidget->setHorizontalHeaderItem(1, new QTableWidgetItem("Species"));
+    ui->datacube_tableWidget->insertColumn(2);
+    ui->datacube_tableWidget->setHorizontalHeaderItem(2, new QTableWidgetItem("Transition"));
+    ui->datacube_tableWidget->insertColumn(3);
+    ui->datacube_tableWidget->setHorizontalHeaderItem(3, new QTableWidgetItem("Visualize"));
+    
+    while(!datacubes.isEmpty()) {
+        QMap<QString,QString> datacube = datacubes.takeFirst();
+        //datacubes_list->append(datacube);
+
+        if(! datacube["Survey"].contains("HI-Gal"))
+        {
+            ui->datacube_tableWidget->insertRow(i);
+            QTableWidgetItem *item_0 = new QTableWidgetItem();
+            item_0->setText(datacube["Survey"]);
+            ui->datacube_tableWidget->setItem(i,0,item_0);
+
+            QTableWidgetItem *item_1 = new QTableWidgetItem();
+            item_1->setText(datacube["Species"]);
+            ui->datacube_tableWidget->setItem(i,1,item_1);
+
+            QTableWidgetItem *item_2 = new QTableWidgetItem();
+            item_2->setText(datacube["Transition"]);
+            ui->datacube_tableWidget->setItem(i,2,item_2);
+
+            QPushButton *p_button=new QPushButton("Visualize", this);
+            p_button->setGeometry(QRect(QPoint(100, 100),
+                                        QSize(200, 50)));
+
+            QSignalMapper* mapper = new QSignalMapper(this);
+            int row=i;
+            connect(p_button, SIGNAL(released()),mapper, SLOT(map()));
+            mapper->setMapping(p_button, row);
+            connect(mapper, SIGNAL(mapped(int)), this, SLOT(handleButton(int)));
+            ui->datacube_tableWidget->setCellWidget(i,3,p_button);
+        }
+        i++;
+
+        z++;
+    }
+}
+
+
+
+void dbquery::on_horizontalSlider_sliderMoved(int position)
+{
+
+    double mov=vtkwin->min+(double)position/20.*vtkwin->max-vtkwin->min;
+    //int mov= static_cast<int> (vtkwin->min) + static_cast<int>(position/50) % static_cast<int>(vtkwin->max-vtkwin->min);
+    
+    vtkwin->shellE->SetValue(0, mov);
+    vtkwin->update();
+    vtkwin->showNormal();
+    //shellE->SetValue(1, position);
+    //renWin->Render();
+    
+}
+
+
+
+void dbquery::setVtkWindow(vtkwindow_new *v)
+{
+    vtkwin=v;
+}
+
+
+void dbquery::on_vtkwindow_button_clicked()
+{
+    //vtkwin=new vtkwindow(this);
+}
+
+void dbquery::on_spinBox_valueChanged(int arg1)
+{
+    vtkwin->shellE->SetValue(0, arg1);
+    vtkwin->update();
+    vtkwin->showNormal();
+}
+
+void dbquery::on_download_completed()
+{
+
+
+    vtkFitsReader *fitsReader = vtkFitsReader::New();
+    fitsReader->is3D=true;
+    QString currentPath=QDir::currentPath()+"/"+downloadedFile;
+    
+    fitsReader->SetFileName(currentPath.toStdString());
+    fitsReader->CalculateRMS();
+    fitsReader->Update();
+    
+    vtkwin=new vtkwindow_new(this, fitsReader, 1);
+    
+    /*
+    vtkwin->setSurvey(survey);
+    vtkwin->setSpecies(species);
+    vtkwin->setTransition(transition);
+    vtkwin->vtkcontourwindow->survey=survey;
+    vtkwin->vtkcontourwindow->species=species;
+    vtkwin->vtkcontourwindow->transition=transition;
+  */
+    
+    //vtkcontourwindow=new vtkwindow(this, fitsReader, 2);
+    
+    //ui->horizontalSlider->setMaximum(fitsReader->GetSigma()*10);
+    //ui->horizontalSlider->setMinimum(3*fitsReader->GetSigma());
+    
+}
diff --git a/Code/src/dbquery.h b/Code/src/dbquery.h
new file mode 100644
index 0000000000000000000000000000000000000000..e3ff988206b2eea736b70c66da13680c5c79a66f
--- /dev/null
+++ b/Code/src/dbquery.h
@@ -0,0 +1,79 @@
+#ifndef DBQUERY_H
+#define DBQUERY_H
+
+#include <QDialog>
+#include <QNetworkAccessManager>
+#include <QUrl>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QWidget>
+#include "xmlparser.h"
+#include <QComboBox>
+#include "loadingwidget.h"
+#include "dcvisualizer.h"
+#include "vtkwindow_new.h"
+
+namespace Ui {
+class dbquery;
+}
+
+class dbquery : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit dbquery(QWidget *parent = 0);
+    QNetworkAccessManager* nam;
+    xmlparser *parser;
+    LoadingWidget *loading;
+    dcvisualizer *visualizer;
+    QString downloadedFile;
+    ~dbquery();
+    void setCoordinate(QString l, QString b);
+    void setCoordinate(char* l, char* b);
+    void on_mapInteraction(QString &l, QString &b);
+    void addDatacubesToUI(QList< QMap<QString,QString> >& datacubes);
+    void setVtkWindow(vtkwindow_new *v);
+
+
+private slots:
+
+    void finishedSlot(QNetworkReply* reply);
+    void finishedSlot2(QNetworkReply* reply);
+
+    void on_comboBox_surveys_activated(const QString &arg1);
+
+    void enableAllItems(QComboBox *, int iItems);
+    void disableItems(QComboBox *, int nItems, int* indexes, int size);
+    void on_comboBox_species_activated(const QString &arg1);
+    void on_comboBox_transitions_activated(const QString &arg1);
+    void on_queryPushButton_clicked();
+    void on_pushButton_map_clicked();
+    void handleButton(int i);
+    
+    void on_horizontalSlider_sliderMoved(int position);
+    void on_vtkwindow_button_clicked();
+
+    void on_spinBox_valueChanged(int arg1);
+
+
+public slots:
+    void on_download_completed();
+    
+
+private:
+    Ui::dbquery *ui;
+    vtkwindow_new *vtkwin, *vtkcontourwindow;
+    int n_surveys;
+    int n_species;
+    int n_transitions;
+    QString species;
+    QString survey;
+    QString transition;
+    QList< QMap<QString,QString> > datacubes_list;
+    QString vlkbUrl;
+    QString vlkbsearchUrl;
+    QString vlkbcutoutUrl;
+};
+
+#endif // DBQUERY_H
diff --git a/Code/src/dcvisualizer.cpp b/Code/src/dcvisualizer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cb19081b716b2450b9298a5b61e56b6507bf141b
--- /dev/null
+++ b/Code/src/dcvisualizer.cpp
@@ -0,0 +1,128 @@
+#include "dcvisualizer.h"
+#include <vtkCallbackCommand.h>
+#include <vtkCommand.h>
+#include <qdebug.h>
+#include "vtkwindow.h"
+#include <vtkSmartPointer.h>
+
+dcvisualizer::dcvisualizer()
+{
+
+}
+
+dcvisualizer::~dcvisualizer()
+{
+
+}
+
+void dcvisualizer::visualize(const QString &filename){
+
+
+    // create a window to render into
+    vtkRenderWindow *renWin = vtkRenderWindow::New();
+    //renWin = vtkRenderWindow::New();
+    vtkRenderer *ren1 = vtkRenderer::New();
+    //ren1->SetBackground(1.0, 1.0, 1.0);
+    renWin->AddRenderer(ren1);
+    vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
+    iren->SetRenderWindow(renWin);
+
+    // vtk pipeline
+    vtkFitsReader *fitsReader = vtkFitsReader::New();
+    fitsReader->is3D=true;
+    qDebug()<<"******************   is3D: "<<fitsReader->is3D;
+    qDebug()<<"******************   reading file: "<<filename;
+
+    //sistemare qui
+    //fitsReader->SetFileName("/Users/mariba/Dropbox/University/ViaLactea/Code/VisIVODesktop/trunk/VisIVODesktop.app/Contents/MacOS/vlkb-cutout_2015-04-17_10-19-36_478143_HOPS_G310.3-320.0-H2O-cube.fits");
+    //fitsReader->SetFileName("/Users/mariba/Dropbox/University/ViaLactea/Code/VisIVODesktop/trunk/VisIVODesktop.app/Contents/MacOS/"+filename.toLocal8Bit().data());
+    //QString cazzucazzu="/Users/mariba/Dropbox/University/ViaLactea/Code/VisIVODesktop/trunk/VisIVODesktop.app/Contents/MacOS/"+QString::fromAscii(filename);
+    fitsReader->SetFileName(filename.toLocal8Bit().data());
+
+
+    qDebug()<<"******************   1 ";
+    fitsReader->Update();
+    qDebug()<<"******************   2 ";
+
+
+    // outline
+    vtkOutlineFilter *outlineF = vtkOutlineFilter::New();
+    outlineF->SetInputConnection(fitsReader->GetOutputPort());
+
+    // outlineF->SetInput(fitsReader->GetOutput());
+    vtkPolyDataMapper *outlineM = vtkPolyDataMapper::New();
+    outlineM->SetInput(outlineF->GetOutput());
+    outlineM->ScalarVisibilityOff();
+
+    vtkActor *outlineA = vtkActor::New();
+    outlineA->SetMapper(outlineM);
+    //outlineA->GetProperty()->SetColor(0.0, 0.0, 0.0);
+
+    // isosurface
+    vtkMarchingCubes *shellE = vtkMarchingCubes::New();
+    shellE->SetInput(fitsReader->GetOutput());
+    shellE->ComputeNormalsOn();
+    shellE->SetValue(0, 2.0f);
+
+    // shellE->SetInputConnection(fitsReader->GetOutputPort());
+    //shellE->SetValue(0, 10.0f);
+
+
+    // decimate
+    //vtkDecimate *shellD = vtkDecimate::New();
+    //shellD->SetInput(shellE->GetOutput());
+    //shellD->SetTargetReduction(0.7);
+
+    vtkPolyDataMapper *shellM = vtkPolyDataMapper::New();
+    shellM->SetInput(shellE->GetOutput());
+    //shellM->SetInput(shellD->GetOutput());
+    shellM->ScalarVisibilityOff();
+
+    vtkActor *shellA = vtkActor::New();
+    shellA->SetMapper(shellM);
+    shellA->GetProperty()->SetColor(1.0, 0.5, 1.0);
+
+
+    // slice
+    vtkImageDataGeometryFilter *sliceE =
+                    vtkImageDataGeometryFilter::New();
+    // values are clamped
+    sliceE->SetExtent(0, 5000, 0, 5000, 13, 13);
+    sliceE->SetInputConnection(fitsReader->GetOutputPort());
+
+    vtkPolyDataMapper *sliceM = vtkPolyDataMapper::New();
+    sliceM->SetInput(sliceE->GetOutput());
+    sliceM->ScalarVisibilityOn();
+    double *range;
+    range = fitsReader->GetOutput()->GetScalarRange();
+    sliceM->SetScalarRange(range);
+
+    vtkActor *sliceA = vtkActor::New();
+    sliceA->SetMapper(sliceM);
+
+    // add actors to renderer
+    ren1->AddActor(outlineA);
+    ren1->AddActor(shellA);
+    //Commentato per ora
+    ren1->AddActor(sliceA);
+
+
+
+    /*vtkSmartPointer<vtkCallbackCommand> keypressCallback = vtkSmartPointer<vtkCallbackCommand>::New();
+      keypressCallback->SetCallback (KeypressCallbackFunction2);
+      iren->AddObserver (
+        vtkCommand::KeyPressEvent,
+        keypressCallback );*/
+
+    // Render an image; since no lights/cameras specified, created automatically
+    renWin->Render();
+
+       // uncomment to write VRML
+       //vtkVRMLExporter *vrml = vtkVRMLExporter::New();
+       //vrml->SetRenderWindow(renWin);
+       //vrml->SetFileName("out.wrl");
+       //vrml->Write();
+
+       // Begin mouse interaction
+       iren->Start();
+}
diff --git a/Code/src/dcvisualizer.h b/Code/src/dcvisualizer.h
new file mode 100644
index 0000000000000000000000000000000000000000..3fe434719af89f77f331462ec92fc06ff8374be6
--- /dev/null
+++ b/Code/src/dcvisualizer.h
@@ -0,0 +1,32 @@
+#ifndef DCVISUALIZER
+#define DCVISUALIZER
+#include <QString>
+#include "vtkfitsreader.h"
+#include "vtkRenderWindow.h"
+#include "vtkRenderer.h"
+#include "vtkRenderWindowInteractor.h"
+#include "vtkActor.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkMarchingCubes.h"
+#include "vtkStructuredPoints.h"
+#include "vtkStructuredPointsGeometryFilter.h"
+#include "vtkProperty.h"
+#include "vtkOutlineFilter.h"
+#include "vtkAlgorithmOutput.h"
+#include <vtkSmartPointer.h>
+#include <curl/curl.h>
+#include <string.h>
+#include <stdio.h>
+
+class dcvisualizer{
+public:
+    dcvisualizer();
+    ~dcvisualizer();
+    void visualize(const QString &filename);
+    vtkMarchingCubes *shellE;
+    vtkRenderWindow *renWin;
+};
+
+
+
+#endif // DCVISUALIZER
diff --git a/Code/src/downloadmanager.cpp b/Code/src/downloadmanager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..00c991a185949051298a355415d70998c2979028
--- /dev/null
+++ b/Code/src/downloadmanager.cpp
@@ -0,0 +1,147 @@
+#include <QCoreApplication>
+#include <QDebug>
+
+#include "downloadmanager.h"
+#include "dcvisualizer.h"
+#include "loadingwidget.h"
+#include <QDir>
+#include <QMessageBox>
+
+// constructor
+DownloadManager::DownloadManager()
+{
+    qDebug()<<"Download manager Constructor";
+
+    man = new QNetworkAccessManager(this);
+    connect(man, SIGNAL(finished(QNetworkReply*)),
+            this, SLOT(downloadFinished(QNetworkReply*)));
+
+    loading = new LoadingWidget();
+
+}
+
+void DownloadManager::execute()
+{
+    /*
+    QString urlString="http://ia2-vo.oats.inaf.it/vialactea/cutouts/vlkb-cutout_2015-03-12_18-29-49_584568_NANTEN_L291_L279_hup.fits";
+    QUrl url = QUrl::fromEncoded(urlString.toLocal8Bit());
+    // makes a request
+    doDownload(url);
+*/
+}
+
+// Constructs a QList of QNetworkReply
+QString DownloadManager::doDownload(const QUrl &url, QString fn)
+{
+
+    qDebug()<<" ********************************** FN: "<<fn;
+    loading ->show();
+    loading->activateWindow();
+    loading->setFileName("Downloading...");
+
+    savedFilename=fn;
+    QNetworkRequest request(url);
+    QNetworkReply *reply = man->get(request);
+
+    qDebug()<<"doDownload, request:"<<request.url()<<" and saving to: "<<savedFilename;
+#ifndef QT_NO_SSL
+    connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(sslErrors(QList<QSslError>)));
+#endif
+
+    // List of reply
+    currentDownloads.append(reply);
+    QUrl myurl = reply->url();
+    
+    return saveFileName(myurl,savedFilename);
+}
+
+QString DownloadManager::saveFileName(const QUrl &url,QString outputFile )
+{
+    if (outputFile=="")
+    {
+        //    m_sSettingsFile = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini");
+
+        //  QString path = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append
+        QString path = url.path();
+        QString basename =QFileInfo(path).fileName() ;
+        basename=  QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append(QDir::separator()).append("tmp_download").append(QDir::separator()).append( basename);
+
+        qDebug()<<"QFileInfo(path):"<<basename;
+
+        if (basename.isEmpty())
+            basename = "download";
+
+        if (QFile::exists(basename)) {
+            // already exists, don't overwrite
+            int i = 0;
+            basename += '.';
+            while (QFile::exists(basename + QString::number(i)))
+                ++i;
+
+            basename += QString::number(i);
+        }
+
+        return basename;
+    }
+    else
+        return outputFile ;
+}
+
+void DownloadManager::downloadFinished(QNetworkReply *reply)
+{
+    qDebug()<<"Download Finished: "<<reply->url().toString();
+    
+    QUrl url = reply->url();
+    if (reply->error()) {
+        qDebug()<<"Download of"<< url.toEncoded().constData()<<"failed: "<<qPrintable(reply->errorString());
+
+        QMessageBox::critical(loading ,"Error", qPrintable(reply->errorString()));
+
+        //fprintf(stderr, "Download of %s failed: %s\n",url.toEncoded().constData(),qPrintable(reply->errorString()));
+    } else {
+
+        filenamePath = saveFileName(url,savedFilename);
+        if (saveToDisk(filenamePath, reply))
+            qDebug()<<"Download of"<<url.toEncoded().constData()<<" succeeded and saved to"<<qPrintable(filenamePath);
+
+        emit downloadCompleted();
+
+    }
+
+    currentDownloads.removeAll(reply);
+    reply->deleteLater();
+    loading->hide();
+
+}
+
+bool DownloadManager::saveToDisk(const QString &filename, QIODevice *reply)
+{
+    QFile file(filename);
+    if (!file.open(QIODevice::WriteOnly)) {
+        qDebug()<<"Could not open";
+        fprintf(stderr, "Could not open %s for writing: %s\n",
+                qPrintable(filename),
+                qPrintable(file.errorString()));
+        return false;
+    }
+
+    file.write(reply->readAll());
+    file.close();
+
+
+    // dcvisualizer *datacubeVisualizer=new dcvisualizer();
+    //datacubeVisualizer->visualize(filename);
+    return true;
+}
+
+
+void DownloadManager::sslErrors(const QList<QSslError> &sslErrors)
+{
+#ifndef QT_NO_SSL
+    foreach (const QSslError &error, sslErrors)
+        fprintf(stderr, "SSL error: %s\n", qPrintable(error.errorString()));
+#else
+    Q_UNUSED(sslErrors);
+#endif
+}
+
diff --git a/Code/src/downloadmanager.h b/Code/src/downloadmanager.h
new file mode 100644
index 0000000000000000000000000000000000000000..8386746a674a4bfe91b763d8fcb83d71722c6f84
--- /dev/null
+++ b/Code/src/downloadmanager.h
@@ -0,0 +1,48 @@
+#ifndef DOWNLOADMANAGER_H
+#define DOWNLOADMANAGER_H
+
+#include <QFile>
+#include <QFileInfo>
+#include <QList>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QSslError>
+#include <QStringList>
+#include <QTimer>
+#include <QUrl>
+#include "loadingwidget.h"
+
+#include <stdio.h>
+
+
+class QSslError;
+
+class DownloadManager: public QObject
+{
+    Q_OBJECT
+    QNetworkAccessManager *man;
+    QList<QNetworkReply *> currentDownloads;
+
+public:
+    DownloadManager();
+    QString doDownload(const QUrl &url, QString fn="");
+    QString saveFileName(const QUrl &url, QString outputFile="");
+    bool saveToDisk(const QString &filename, QIODevice *data);
+    QString filenamePath;
+
+private:
+    LoadingWidget *loading;
+    QString savedFilename;
+
+public slots:
+    void execute();
+    void downloadFinished(QNetworkReply *reply);
+    void sslErrors(const QList<QSslError> &errors);
+
+signals:
+    void downloadCompleted();
+};
+
+
+#endif // DOWNLOADMANAGER_H
diff --git a/Code/src/extendedglyph3d.cpp b/Code/src/extendedglyph3d.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..13f712269e8b8018dd4beb9f436703f0b46cdace
--- /dev/null
+++ b/Code/src/extendedglyph3d.cpp
@@ -0,0 +1,955 @@
+
+#include "extendedglyph3D.h"
+
+#include "vtkCellData.h"
+#include "vtkCell.h"
+#include "vtkFloatArray.h"
+#include "vtkIdList.h"
+#include "vtkIdTypeArray.h"
+#include "vtkInformation.h"
+#include "vtkInformationVector.h"
+#include "vtkMath.h"
+#include "vtkNew.h"
+#include "vtkObjectFactory.h"
+#include "vtkPointData.h"
+#include "vtkPolyData.h"
+#include "vtkSmartPointer.h"
+#include "vtkStreamingDemandDrivenPipeline.h"
+#include "vtkTransform.h"
+#include "vtkTrivialProducer.h"
+#include "vtkUniformGrid.h"
+#include "vtkUnsignedCharArray.h"
+
+vtkStandardNewMacro(ExtendedGlyph3D);
+vtkCxxSetObjectMacro(ExtendedGlyph3D, SourceTransform, vtkTransform);
+
+//----------------------------------------------------------------------------
+// Construct object with scaling on, scaling mode is by scalar value,
+// scale factor = 1.0, the range is (0,1), orient geometry is on, and
+// orientation is by vector. Clamping and indexing are turned off. No
+// initial sources are defined.
+ExtendedGlyph3D::ExtendedGlyph3D()
+{
+    this->Scaling = 1;
+    this->ColorMode = VTK_COLOR_BY_SCALE;
+    this->ScaleMode = VTK_SCALE_BY_SCALAR;
+    this->ScaleFactor = 1.0;
+    this->Range[0] = 0.0;
+    this->Range[1] = 1.0;
+    this->Orient = 1;
+    this->VectorMode = VTK_USE_VECTOR;
+    this->Clamping = 0;
+    this->IndexMode = VTK_INDEXING_OFF;
+    this->GeneratePointIds = 0;
+    this->PointIdsName = NULL;
+    this->SetPointIdsName("InputPointIds");
+    this->SetNumberOfInputPorts(2);
+    this->FillCellData = 0;
+    this->SourceTransform = 0;
+
+    // by default process active point scalars
+    this->SetInputArrayToProcess(0,0,0,vtkDataObject::FIELD_ASSOCIATION_POINTS,
+                                 vtkDataSetAttributes::SCALARS);
+    // by default process active point vectors
+    this->SetInputArrayToProcess(1,0,0,vtkDataObject::FIELD_ASSOCIATION_POINTS,
+                                 vtkDataSetAttributes::VECTORS);
+    // by default process active point normals
+    this->SetInputArrayToProcess(2,0,0,vtkDataObject::FIELD_ASSOCIATION_POINTS,
+                                 vtkDataSetAttributes::NORMALS);
+    // by default process active point scalars
+    this->SetInputArrayToProcess(3,0,0,vtkDataObject::FIELD_ASSOCIATION_POINTS,
+                                 vtkDataSetAttributes::SCALARS);
+
+}
+
+//----------------------------------------------------------------------------
+ExtendedGlyph3D::~ExtendedGlyph3D()
+{
+    delete [] PointIdsName;
+    this->SetSourceTransform(NULL);
+}
+
+//----------------------------------------------------------------------------
+unsigned long ExtendedGlyph3D::GetMTime()
+{
+    unsigned long mTime=this->Superclass::GetMTime();
+    unsigned long time;
+    if ( this->SourceTransform != NULL )
+    {
+        time = this->SourceTransform ->GetMTime();
+        mTime = ( time > mTime ? time : mTime );
+    }
+    return mTime;
+}
+
+//----------------------------------------------------------------------------
+int ExtendedGlyph3D::RequestData(
+        vtkInformation *vtkNotUsed(request),
+        vtkInformationVector **inputVector,
+        vtkInformationVector *outputVector)
+{
+    // get the info objects
+    vtkDataSet* input = vtkDataSet::GetData(inputVector[0], 0);
+    vtkPolyData* output = vtkPolyData::GetData(outputVector, 0);
+
+    int requestedGhostLevel = outputVector->GetInformationObject(0)->Get(
+                vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS());
+    return this->Execute(input, inputVector[1], output, requestedGhostLevel)? 1 : 0;
+}
+
+
+//----------------------------------------------------------------------------
+bool ExtendedGlyph3D::Execute(
+        vtkDataSet* input,
+        vtkInformationVector* sourceVector,
+        vtkPolyData* output,
+        int requestedGhostLevel)
+{
+    assert(input && output);
+    if (input == NULL || output == NULL)
+    {
+        // nothing to do.
+        return true;
+    }
+
+    // this is used to respect blanking specified on uniform grids.
+    vtkUniformGrid* inputUG = vtkUniformGrid::SafeDownCast(input);
+
+    vtkPointData *pd;
+    vtkDataArray *inSScalars; // Scalars for Scaling
+    vtkDataArray *inCScalars; // Scalars for Coloring
+    vtkDataArray *inVectors;
+    unsigned char* inGhostLevels=0;
+    vtkDataArray *inNormals, *sourceNormals = NULL;
+    vtkDataArray *sourceTCoords = NULL;
+    vtkIdType numPts, numSourcePts, numSourceCells, inPtId, i;
+    vtkPoints *sourcePts = NULL;
+    vtkSmartPointer<vtkPoints> transformedSourcePts = vtkSmartPointer<vtkPoints>::New();
+    vtkPoints *newPts;
+    vtkDataArray *newScalars=NULL;
+    vtkDataArray *newVectors=NULL;
+    vtkDataArray *newNormals=NULL;
+    vtkDataArray *newTCoords = NULL;
+    double x[3], v[3], vNew[3], s = 0.0, vMag = 0.0, value, tc[3];
+    vtkTransform *trans = vtkTransform::New();
+    vtkNew<vtkIdList> pointIdList;
+    vtkIdList *cellPts;
+    int npts;
+    vtkIdList *pts;
+    vtkIdType ptIncr, cellIncr, cellId;
+    int haveVectors, haveNormals, haveTCoords = 0;
+    double scalex,scaley,scalez, den;
+    vtkPointData* outputPD = output->GetPointData();
+    vtkCellData* outputCD = output->GetCellData();
+    int numberOfSources = this->GetNumberOfInputConnections(1);
+    vtkPolyData *defaultSource = NULL;
+    vtkIdTypeArray *pointIds=0;
+    vtkPolyData *source = this->GetSource(0, sourceVector);
+    vtkNew<vtkIdList> srcPointIdList;
+    vtkNew<vtkIdList> dstPointIdList;
+    vtkNew<vtkIdList> srcCellIdList;
+    vtkNew<vtkIdList> dstCellIdList;
+
+    vtkDebugMacro(<<"Generating glyphs");
+
+    pts = vtkIdList::New();
+    pts->Allocate(VTK_CELL_SIZE);
+
+    pd = input->GetPointData();
+    inSScalars = this->GetInputArrayToProcess(0, input);
+    inVectors = this->GetInputArrayToProcess(1, input);
+    inNormals = this->GetInputArrayToProcess(2, input);
+    inCScalars = this->GetInputArrayToProcess(3, input);
+    if (inCScalars == NULL)
+    {
+        inCScalars = inSScalars;
+    }
+
+    vtkDataArray* temp = 0;
+    if (pd)
+    {
+        temp = pd->GetArray("vtkGhostLevels");
+    }
+    if ( (!temp) || (temp->GetDataType() != VTK_UNSIGNED_CHAR)
+         || (temp->GetNumberOfComponents() != 1))
+    {
+        vtkDebugMacro("No appropriate ghost levels field available.");
+    }
+    else
+    {
+        inGhostLevels =static_cast<vtkUnsignedCharArray *>(temp)->GetPointer(0);
+    }
+
+
+    numPts = input->GetNumberOfPoints();
+    if (numPts < 1)
+    {
+        vtkDebugMacro(<<"No points to glyph!");
+        pts->Delete();
+        trans->Delete();
+        return true;
+    }
+
+    // Check input for consistency
+    //
+    if ( (den = this->Range[1] - this->Range[0]) == 0.0 )
+    {
+        den = 1.0;
+    }
+    if ( this->VectorMode != VTK_VECTOR_ROTATION_OFF &&
+         ((this->VectorMode == VTK_USE_VECTOR && inVectors != NULL) ||
+          (this->VectorMode == VTK_USE_NORMAL && inNormals != NULL)) )
+    {
+        haveVectors = 1;
+    }
+    else
+    {
+        haveVectors = 0;
+    }
+
+    if ( (this->IndexMode == VTK_INDEXING_BY_SCALAR && !inSScalars) ||
+         (this->IndexMode == VTK_INDEXING_BY_VECTOR &&
+          ((!inVectors && this->VectorMode == VTK_USE_VECTOR) ||
+           (!inNormals && this->VectorMode == VTK_USE_NORMAL))) )
+    {
+        if ( !source )
+        {
+            vtkErrorMacro(<<"Indexing on but don't have data to index with");
+            pts->Delete();
+            trans->Delete();
+            return true;
+        }
+        else
+        {
+            vtkWarningMacro(<<"Turning indexing off: no data to index with");
+            this->IndexMode = VTK_INDEXING_OFF;
+        }
+    }
+
+    // Allocate storage for output PolyData
+    //
+    outputPD->CopyVectorsOff();
+    outputPD->CopyNormalsOff();
+    outputPD->CopyTCoordsOff();
+
+    if (!source)
+    {
+        defaultSource = vtkPolyData::New();
+        defaultSource->Allocate();
+        vtkPoints *defaultPoints = vtkPoints::New();
+        defaultPoints->Allocate(6);
+        defaultPoints->InsertNextPoint(0, 0, 0);
+        defaultPoints->InsertNextPoint(1, 0, 0);
+        vtkIdType defaultPointIds[2];
+        defaultPointIds[0] = 0;
+        defaultPointIds[1] = 1;
+        defaultSource->SetPoints(defaultPoints);
+        defaultSource->InsertNextCell(VTK_LINE, 2, defaultPointIds);
+        defaultSource->Delete();
+        defaultSource = NULL;
+        defaultPoints->Delete();
+        defaultPoints = NULL;
+        source = defaultSource;
+    }
+
+    if ( this->IndexMode != VTK_INDEXING_OFF )
+    {
+        pd = NULL;
+        haveNormals = 1;
+        for (numSourcePts=numSourceCells=i=0; i < numberOfSources; i++)
+        {
+            source = this->GetSource(i, sourceVector);
+            if ( source != NULL )
+            {
+                if (source->GetNumberOfPoints() > numSourcePts)
+                {
+                    numSourcePts = source->GetNumberOfPoints();
+                }
+                if (source->GetNumberOfCells() > numSourceCells)
+                {
+                    numSourceCells = source->GetNumberOfCells();
+                }
+                if ( !(sourceNormals = source->GetPointData()->GetNormals()) )
+                {
+                    haveNormals = 0;
+                }
+            }
+        }
+    }
+    else
+    {
+        sourcePts = source->GetPoints();
+        numSourcePts = sourcePts->GetNumberOfPoints();
+        numSourceCells = source->GetNumberOfCells();
+
+        sourceNormals = source->GetPointData()->GetNormals();
+        if ( sourceNormals )
+        {
+            haveNormals = 1;
+        }
+        else
+        {
+            haveNormals = 0;
+        }
+
+        sourceTCoords = source->GetPointData()->GetTCoords();
+        if (sourceTCoords)
+        {
+            haveTCoords = 1;
+        }
+        else
+        {
+            haveTCoords = 0;
+        }
+
+        // Prepare to copy output.
+        pd = input->GetPointData();
+        outputPD->CopyAllocate(pd,numPts*numSourcePts);
+        if (this->FillCellData)
+        {
+            outputCD->CopyAllocate(pd,numPts*numSourceCells);
+        }
+    }
+
+    srcPointIdList->SetNumberOfIds(numSourcePts);
+    dstPointIdList->SetNumberOfIds(numSourcePts);
+    srcCellIdList->SetNumberOfIds(numSourceCells);
+    dstCellIdList->SetNumberOfIds(numSourceCells);
+
+    newPts = vtkPoints::New();
+    newPts->Allocate(numPts*numSourcePts);
+    if ( this->GeneratePointIds )
+    {
+        pointIds = vtkIdTypeArray::New();
+        pointIds->SetName(this->PointIdsName);
+        pointIds->Allocate(numPts*numSourcePts);
+        outputPD->AddArray(pointIds);
+        pointIds->Delete();
+    }
+    if ( this->ColorMode == VTK_COLOR_BY_SCALAR && inCScalars )
+    {
+        newScalars = inCScalars->NewInstance();
+        newScalars->SetNumberOfComponents(inCScalars->GetNumberOfComponents());
+        newScalars->Allocate(inCScalars->GetNumberOfComponents()*numPts*numSourcePts);
+        newScalars->SetName(inCScalars->GetName());
+    }
+    else if ( (this->ColorMode == VTK_COLOR_BY_SCALE) && inSScalars)
+    {
+        newScalars = vtkFloatArray::New();
+        newScalars->Allocate(numPts*numSourcePts);
+        newScalars->SetName("GlyphScale");
+        if (this->ScaleMode == VTK_SCALE_BY_SCALAR)
+        {
+            newScalars->SetName(inSScalars->GetName());
+        }
+    }
+    else if ( (this->ColorMode == VTK_COLOR_BY_VECTOR) && haveVectors)
+    {
+        newScalars = vtkFloatArray::New();
+        newScalars->Allocate(numPts*numSourcePts);
+        newScalars->SetName("VectorMagnitude");
+    }
+    if ( haveVectors )
+    {
+        newVectors = vtkFloatArray::New();
+        newVectors->SetNumberOfComponents(3);
+        newVectors->Allocate(3*numPts*numSourcePts);
+        newVectors->SetName("GlyphVector");
+    }
+    if ( haveNormals )
+    {
+        newNormals = vtkFloatArray::New();
+        newNormals->SetNumberOfComponents(3);
+        newNormals->Allocate(3*numPts*numSourcePts);
+        newNormals->SetName("Normals");
+    }
+    if (haveTCoords)
+    {
+        newTCoords = vtkFloatArray::New();
+        int numComps = sourceTCoords->GetNumberOfComponents();
+        newTCoords->SetNumberOfComponents(numComps);
+        newTCoords->Allocate(numComps*numPts*numSourcePts);
+        newTCoords->SetName("TCoords");
+    }
+
+    // Setting up for calls to PolyData::InsertNextCell()
+    if (this->IndexMode != VTK_INDEXING_OFF )
+    {
+        output->Allocate(3*numPts*numSourceCells,numPts*numSourceCells);
+    }
+    else
+    {
+        output->Allocate(source,
+                         3*numPts*numSourceCells, numPts*numSourceCells);
+    }
+
+    transformedSourcePts->SetDataTypeToDouble();
+    transformedSourcePts->Allocate(numSourcePts);
+
+    // Traverse all Input points, transforming Source points and copying
+    // point attributes.
+    //
+    ptIncr=0;
+    cellIncr=0;
+    for (inPtId=0; inPtId < numPts; inPtId++)
+    {
+        scalex = scaley = scalez = 1.0;
+        if ( ! (inPtId % 10000) )
+        {
+            this->UpdateProgress(static_cast<double>(inPtId)/numPts);
+            if (this->GetAbortExecute())
+            {
+                break;
+            }
+        }
+
+        // Get the scalar and vector data
+        if ( inSScalars )
+        {
+            s = inSScalars->GetComponent(inPtId, 0);
+            if ( this->ScaleMode == VTK_SCALE_BY_SCALAR ||
+                 this->ScaleMode == VTK_DATA_SCALING_OFF )
+            {
+                scalex = scaley = scalez = s;
+            }
+        }
+
+        if ( haveVectors )
+        {
+            vtkDataArray *array3D = this->VectorMode == VTK_USE_NORMAL? inNormals : inVectors;
+            if(array3D->GetNumberOfComponents()>3)
+            {
+                vtkErrorMacro(<<"vtkDataArray "<<array3D->GetName()<<" has more than 3 components.\n");
+                pts->Delete();
+                trans->Delete();
+                if(newPts)
+                {
+                    newPts->Delete();
+                }
+                if(newVectors)
+                {
+                    newVectors->Delete();
+                }
+                return false;
+            }
+
+            v[0] = 0;
+            v[1] = 0;
+            v[2] = 0;
+            array3D->GetTuple(inPtId, v);
+            vMag = vtkMath::Norm(v);
+            if ( this->ScaleMode == VTK_SCALE_BY_VECTORCOMPONENTS )
+            {
+                scalex = v[0];
+                scaley = v[1];
+                scalez = v[2];
+            }
+            else if ( this->ScaleMode == VTK_SCALE_BY_VECTOR )
+            {
+                scalex = scaley = scalez = vMag;
+            }
+        }
+
+        // Clamp data scale if enabled
+        if ( this->Clamping )
+        {
+            scalex = (scalex < this->Range[0] ? this->Range[0] :
+                    (scalex > this->Range[1] ? this->Range[1] : scalex));
+            scalex = (scalex - this->Range[0]) / den;
+            scaley = (scaley < this->Range[0] ? this->Range[0] :
+                    (scaley > this->Range[1] ? this->Range[1] : scaley));
+            scaley = (scaley - this->Range[0]) / den;
+            scalez = (scalez < this->Range[0] ? this->Range[0] :
+                    (scalez > this->Range[1] ? this->Range[1] : scalez));
+            scalez = (scalez - this->Range[0]) / den;
+        }
+
+        // Compute index into table of glyphs
+        if ( this->IndexMode != VTK_INDEXING_OFF )
+        {
+            if ( this->IndexMode == VTK_INDEXING_BY_SCALAR )
+            {
+                value = s;
+            }
+            else
+            {
+                value = vMag;
+            }
+
+            int index = static_cast<int>((value - this->Range[0])*numberOfSources / den);
+            index = (index < 0 ? 0 :
+                                 (index >= numberOfSources ? (numberOfSources-1) : index));
+
+            source = this->GetSource(index, sourceVector);
+            if ( source != NULL )
+            {
+                sourcePts = source->GetPoints();
+                sourceNormals = source->GetPointData()->GetNormals();
+                numSourcePts = sourcePts->GetNumberOfPoints();
+                numSourceCells = source->GetNumberOfCells();
+            }
+        }
+
+        // Make sure we're not indexing into empty glyph
+        if ( !source )
+        {
+            continue;
+        }
+
+        // Check ghost points.
+        // If we are processing a piece, we do not want to duplicate
+        // glyphs on the borders.  The corrct check here is:
+        // ghostLevel > 0.  I am leaving this over glyphing here because
+        // it make a nice example (sphereGhost.tcl) to show the
+        // point ghost levels with the glyph filter.  I am not certain
+        // of the usefulness of point ghost levels over 1, but I will have
+        // to think about it.
+        if (inGhostLevels && inGhostLevels[inPtId] > requestedGhostLevel)
+        {
+            continue;
+        }
+
+        if (inputUG && !inputUG->IsPointVisible(inPtId))
+        {
+            // input is a vtkUniformGrid and the current point is blanked. Don't glyph
+            // it.
+            continue;
+        }
+
+        if (!this->IsPointVisible(input, inPtId))
+        {
+            continue;
+        }
+
+        // Now begin copying/transforming glyph
+        trans->Identity();
+
+        // Copy all topology (transformation independent)
+        for (cellId=0; cellId < numSourceCells; cellId++)
+        {
+            source->GetCellPoints(cellId, pointIdList.GetPointer());
+            cellPts = pointIdList.GetPointer();
+            npts = cellPts->GetNumberOfIds();
+            for (pts->Reset(), i=0; i < npts; i++)
+            {
+                pts->InsertId(i, cellPts->GetId(i) + ptIncr);
+            }
+            output->InsertNextCell(source->GetCellType(cellId), pts);
+        }
+
+        // translate Source to Input point
+        input->GetPoint(inPtId, x);
+        trans->Translate(x[0], x[1], x[2]);
+
+        if ( haveVectors )
+        {
+            // Copy Input vector
+            for (i=0; i < numSourcePts; i++)
+            {
+                newVectors->InsertTuple(i+ptIncr, v);
+            }
+            if (this->Orient && (vMag > 0.0))
+            {
+                // if there is no y or z component
+                if ( v[1] == 0.0 && v[2] == 0.0 )
+                {
+                    if (v[0] < 0) //just flip x if we need to
+                    {
+                        trans->RotateWXYZ(180.0,0,1,0);
+                    }
+                }
+                else
+                {
+                    vNew[0] = (v[0]+vMag) / 2.0;
+                    vNew[1] = v[1] / 2.0;
+                    vNew[2] = v[2] / 2.0;
+                    trans->RotateWXYZ(180.0,vNew[0],vNew[1],vNew[2]);
+                }
+            }
+        }
+
+        if (haveTCoords)
+        {
+            for (i = 0; i < numSourcePts; i++)
+            {
+                sourceTCoords->GetTuple(i, tc);
+                newTCoords->InsertTuple(i+ptIncr, tc);
+            }
+        }
+
+        // determine scale factor from scalars if appropriate
+        // Copy scalar value
+        if (inSScalars && (this->ColorMode == VTK_COLOR_BY_SCALE))
+        {
+            for (i=0; i < numSourcePts; i++)
+            {
+                newScalars->InsertTuple(i+ptIncr, &scalex); // = scaley = scalez
+            }
+        }
+        else if (inCScalars && (this->ColorMode == VTK_COLOR_BY_SCALAR))
+        {
+            for (i=0; i < numSourcePts; i++)
+            {
+                outputPD->CopyTuple(inCScalars, newScalars, inPtId, ptIncr+i);
+            }
+        }
+        if (haveVectors && this->ColorMode == VTK_COLOR_BY_VECTOR)
+        {
+            for (i=0; i < numSourcePts; i++)
+            {
+                newScalars->InsertTuple(i+ptIncr, &vMag);
+            }
+        }
+
+        // scale data if appropriate
+        if ( this->Scaling )
+        {
+            if ( this->ScaleMode == VTK_DATA_SCALING_OFF )
+            {
+                scalex = scaley = scalez = this->ScaleFactor;
+            }
+            else
+            {
+                scalex *= this->ScaleFactor;
+                scaley *= this->ScaleFactor;
+                scalez *= this->ScaleFactor;
+            }
+
+            if ( scalex == 0.0 )
+            {
+                scalex = 1.0e-10;
+            }
+            if ( scaley == 0.0 )
+            {
+                scaley = 1.0e-10;
+            }
+            if ( scalez == 0.0 )
+            {
+                scalez = 1.0e-10;
+            }
+            trans->Scale(scalex,scaley,scalez);
+        }
+
+        // multiply points and normals by resulting matrix
+        if (this->SourceTransform)
+        {
+            transformedSourcePts->Reset();
+            this->SourceTransform->TransformPoints(sourcePts, transformedSourcePts);
+            trans->TransformPoints(transformedSourcePts, newPts);
+        }
+        else
+        {
+            trans->TransformPoints(sourcePts,newPts);
+        }
+
+        if ( haveNormals )
+        {
+            trans->TransformNormals(sourceNormals,newNormals);
+        }
+
+        // Copy point data from source (if possible)
+        if ( pd )
+        {
+            for (i = 0; i < numSourcePts; ++i)
+            {
+                srcPointIdList->SetId(i, inPtId);
+                dstPointIdList->SetId(i, ptIncr + i);
+            }
+            outputPD->CopyData(pd, srcPointIdList.GetPointer(),
+                               dstPointIdList.GetPointer());
+            if (this->FillCellData)
+            {
+                for (i = 0; i < numSourceCells; ++i)
+                {
+                    srcCellIdList->SetId(i, inPtId);
+                    dstCellIdList->SetId(i, cellIncr + i);
+                }
+                outputCD->CopyData(pd, srcCellIdList.GetPointer(),
+                                   dstCellIdList.GetPointer());
+            }
+        }
+
+        // If point ids are to be generated, do it here
+        if ( this->GeneratePointIds )
+        {
+            for (i=0; i < numSourcePts; i++)
+            {
+                pointIds->InsertNextValue(inPtId);
+            }
+        }
+
+        ptIncr += numSourcePts;
+        cellIncr += numSourceCells;
+    }
+
+    // Update ourselves and release memory
+    //
+    output->SetPoints(newPts);
+    newPts->Delete();
+
+    if (newScalars)
+    {
+        int idx = outputPD->AddArray(newScalars);
+        outputPD->SetActiveAttribute(idx, vtkDataSetAttributes::SCALARS);
+        newScalars->Delete();
+    }
+
+    if (newVectors)
+    {
+        outputPD->SetVectors(newVectors);
+        newVectors->Delete();
+    }
+
+    if (newNormals)
+    {
+        outputPD->SetNormals(newNormals);
+        newNormals->Delete();
+    }
+
+    if (newTCoords)
+    {
+        outputPD->SetTCoords(newTCoords);
+        newTCoords->Delete();
+    }
+
+    output->Squeeze();
+    trans->Delete();
+    pts->Delete();
+
+    return true;
+}
+
+//----------------------------------------------------------------------------
+// Specify a source object at a specified table location.
+void ExtendedGlyph3D::SetSourceConnection(int id, vtkAlgorithmOutput* algOutput)
+{
+    if (id < 0)
+    {
+        vtkErrorMacro("Bad index " << id << " for source.");
+        return;
+    }
+
+    int numConnections = this->GetNumberOfInputConnections(1);
+    if (id < numConnections)
+    {
+        this->SetNthInputConnection(1, id, algOutput);
+    }
+    else if (id == numConnections && algOutput)
+    {
+        this->AddInputConnection(1, algOutput);
+    }
+    else if (algOutput)
+    {
+        vtkWarningMacro("The source id provided is larger than the maximum "
+                        "source id, using " << numConnections << " instead.");
+        this->AddInputConnection(1, algOutput);
+    }
+}
+
+//----------------------------------------------------------------------------
+// Specify a source object at a specified table location.
+void ExtendedGlyph3D::SetSourceData(int id, vtkPolyData *pd)
+{
+    int numConnections = this->GetNumberOfInputConnections(1);
+
+    if (id < 0 || id > numConnections)
+    {
+        vtkErrorMacro("Bad index " << id << " for source.");
+        return;
+    }
+
+    vtkTrivialProducer* tp = 0;
+    if (pd)
+    {
+        tp = vtkTrivialProducer::New();
+        tp->SetOutput(pd);
+    }
+
+    if (id < numConnections)
+    {
+        if (tp)
+        {
+            this->SetNthInputConnection(1, id, tp->GetOutputPort());
+        }
+        else
+        {
+            this->SetNthInputConnection(1, id, 0);
+        }
+    }
+    else if (id == numConnections && tp)
+    {
+        this->AddInputConnection(1, tp->GetOutputPort());
+    }
+
+    if (tp)
+    {
+        tp->Delete();
+    }
+}
+
+//----------------------------------------------------------------------------
+// Get a pointer to a source object at a specified table location.
+vtkPolyData *ExtendedGlyph3D::GetSource(int id)
+{
+    if ( id < 0 || id >= this->GetNumberOfInputConnections(1) )
+    {
+        return NULL;
+    }
+
+    return vtkPolyData::SafeDownCast(
+                this->GetExecutive()->GetInputData(1, id));
+}
+/*
+//----------------------------------------------------------------------------
+void ExtendedGlyph3D::PrintSelf(ostream& os, vtkIndent indent)
+{
+    //this->Superclass::PrintSelf(os,indent);
+
+    os << indent << "Generate Point Ids "
+       << (this->GeneratePointIds ? "On\n" : "Off\n");
+
+    os << indent << "PointIdsName: " << (this->PointIdsName ? this->PointIdsName
+                                                            : "(none)") << "\n";
+
+    os << indent << "Color Mode: " << this->GetColorModeAsString() << endl;
+
+    if ( this->GetNumberOfInputConnections(1) < 2 )
+    {
+        if ( this->GetSource(0) != NULL )
+        {
+            os << indent << "Source: (" << this->GetSource(0) << ")\n";
+        }
+        else
+        {
+            os << indent << "Source: (none)\n";
+        }
+    }
+    else
+    {
+        os << indent << "A table of " << this->GetNumberOfInputConnections(1) << " glyphs has been defined\n";
+    }
+
+    os << indent << "Scaling: " << (this->Scaling ? "On\n" : "Off\n");
+
+    os << indent << "Scale Mode: ";
+    if ( this->ScaleMode == VTK_SCALE_BY_SCALAR )
+    {
+        os << "Scale by scalar\n";
+    }
+    else if ( this->ScaleMode == VTK_SCALE_BY_VECTOR )
+    {
+        os << "Scale by vector\n";
+    }
+    else
+    {
+        os << "Data scaling is turned off\n";
+    }
+
+    os << indent << "Scale Factor: " << this->ScaleFactor << "\n";
+    os << indent << "Clamping: " << (this->Clamping ? "On\n" : "Off\n");
+    os << indent << "Range: (" << this->Range[0] << ", " << this->Range[1] << ")\n";
+    os << indent << "Orient: " << (this->Orient ? "On\n" : "Off\n");
+    os << indent << "Orient Mode: " << (this->VectorMode == VTK_USE_VECTOR ?
+                                            "Orient by vector\n" : "Orient by normal\n");
+    os << indent << "Index Mode: ";
+    if ( this->IndexMode == VTK_INDEXING_BY_SCALAR )
+    {
+        os << "Index by scalar value\n";
+    }
+    else if ( this->IndexMode == VTK_INDEXING_BY_VECTOR )
+    {
+        os << "Index by vector value\n";
+    }
+    else
+    {
+        os << "Indexing off\n";
+    }
+
+    os << indent << "Fill Cell Data: " << (this->FillCellData ? "On\n" : "Off\n");
+
+    os << indent << "SourceTransform: ";
+    if (this->SourceTransform)
+    {
+        os << endl;
+        this->SourceTransform->PrintSelf(os, indent.GetNextIndent());
+    }
+    else
+    {
+        os << "(none)" << endl;
+    }
+}
+*/
+int ExtendedGlyph3D::RequestUpdateExtent(
+        vtkInformation *vtkNotUsed(request),
+        vtkInformationVector **inputVector,
+        vtkInformationVector *outputVector)
+{
+    // get the info objects
+    vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
+    vtkInformation *sourceInfo = inputVector[1]->GetInformationObject(0);
+    vtkInformation *outInfo = outputVector->GetInformationObject(0);
+
+    if (sourceInfo)
+    {
+        sourceInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(),
+                        0);
+        sourceInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(),
+                        1);
+        sourceInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(),
+                        0);
+    }
+    inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(),
+                outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()));
+    inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(),
+                outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES()));
+    inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(),
+                outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS()));
+    inInfo->Set(vtkStreamingDemandDrivenPipeline::EXACT_EXTENT(), 1);
+
+    return 1;
+}
+
+//----------------------------------------------------------------------------
+vtkPolyData* ExtendedGlyph3D::GetSource(int idx, vtkInformationVector *sourceInfo)
+{
+    vtkInformation *info = sourceInfo->GetInformationObject(idx);
+    if (!info)
+    {
+        return NULL;
+    }
+    return vtkPolyData::SafeDownCast(info->Get(vtkDataObject::DATA_OBJECT()));
+}
+
+//----------------------------------------------------------------------------
+int ExtendedGlyph3D::FillInputPortInformation(int port, vtkInformation *info)
+{
+    if (port == 0)
+    {
+        info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
+        return 1;
+    }
+    else if (port == 1)
+    {
+        info->Set(vtkAlgorithm::INPUT_IS_REPEATABLE(), 1);
+        info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
+        info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
+        return 1;
+    }
+    return 0;
+}
+
+void ExtendedGlyph3D::PrintSelf(ostream& os, vtkIndent indent)
+{
+    // this->Superclass::PrintSelf(os, indent);
+}
+
+void ExtendedGlyph3D::PrintHeader(ostream& os, vtkIndent indent)
+{
+    // this->Superclass::PrintHeader(os, indent);
+
+}
+
+void ExtendedGlyph3D::PrintTrailer(std::ostream& os , vtkIndent indent)
+{
+    // this->Superclass::PrintTrailer(os, indent);
+}
diff --git a/Code/src/extendedglyph3d.h b/Code/src/extendedglyph3d.h
new file mode 100644
index 0000000000000000000000000000000000000000..8aa7b75e14021e02460de3c3b534c6dcdf34ce37
--- /dev/null
+++ b/Code/src/extendedglyph3d.h
@@ -0,0 +1,340 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Alessandro Costa                                *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef __ExtendedGlyph3D_h
+#define __ExtendedGlyph3D_h
+
+/**
+        @author Fabio Vitello <fabio.vitello@oact.inaf.it>
+        @author Alessandro Costa <alessandro.costa@oact.inaf.it>
+        @author Ugo Becciani <ugo.becciani@oact.inaf.it>
+*/
+
+#include "vtkFiltersCoreModule.h" // For export macro
+#include "vtkPolyDataAlgorithm.h"
+
+#define VTK_SCALE_BY_SCALAR 0
+#define VTK_SCALE_BY_VECTOR 1
+#define VTK_SCALE_BY_VECTORCOMPONENTS 2
+#define VTK_DATA_SCALING_OFF 3
+
+#define VTK_COLOR_BY_SCALE  0
+#define VTK_COLOR_BY_SCALAR 1
+#define VTK_COLOR_BY_VECTOR 2
+
+#define VTK_USE_VECTOR 0
+#define VTK_USE_NORMAL 1
+#define VTK_VECTOR_ROTATION_OFF 2
+
+#define VTK_INDEXING_OFF 0
+#define VTK_INDEXING_BY_SCALAR 1
+#define VTK_INDEXING_BY_VECTOR 2
+
+class vtkTransform;
+
+class  ExtendedGlyph3D : public vtkPolyDataAlgorithm
+{
+public:
+    vtkTypeMacro(ExtendedGlyph3D,vtkPolyDataAlgorithm);
+    void PrintSelf(ostream& os, vtkIndent indent);
+    void PrintHeader(ostream& os, vtkIndent indent);
+    void PrintTrailer(std::ostream& os , vtkIndent indent);
+
+    // Description
+    // Construct object with scaling on, scaling mode is by scalar value,
+    // scale factor = 1.0, the range is (0,1), orient geometry is on, and
+    // orientation is by vector. Clamping and indexing are turned off. No
+    // initial sources are defined.
+    static ExtendedGlyph3D *New();
+
+    // Description:
+    // Set the source to use for the glyph.
+    // Note that this method does not connect the pipeline. The algorithm will
+    // work on the input data as it is without updating the producer of the data.
+    // See SetSourceConnection for connecting the pipeline.
+    void SetSourceData(vtkPolyData *pd) {this->SetSourceData(0,pd);};
+
+    // Description:
+    // Specify a source object at a specified table location.
+    // Note that this method does not connect the pipeline. The algorithm will
+    // work on the input data as it is without updating the producer of the data.
+    // See SetSourceConnection for connecting the pipeline.
+    void SetSourceData(int id, vtkPolyData *pd);
+
+    // Description:
+    // Specify a source object at a specified table location. New style.
+    // Source connection is stored in port 1. This method is equivalent
+    // to SetInputConnection(1, id, outputPort).
+    void SetSourceConnection(int id, vtkAlgorithmOutput* algOutput);
+    void SetSourceConnection(vtkAlgorithmOutput* algOutput)
+    {
+        this->SetSourceConnection(0, algOutput);
+    }
+
+    // Description:
+    // Get a pointer to a source object at a specified table location.
+    vtkPolyData *GetSource(int id=0);
+
+    // Description:
+    // Turn on/off scaling of source geometry.
+    vtkSetMacro(Scaling,int);
+    vtkBooleanMacro(Scaling,int);
+    vtkGetMacro(Scaling,int);
+
+    // Description:
+    // Either scale by scalar or by vector/normal magnitude.
+    vtkSetMacro(ScaleMode,int);
+    vtkGetMacro(ScaleMode,int);
+    void SetScaleModeToScaleByScalar()
+    {this->SetScaleMode(VTK_SCALE_BY_SCALAR);};
+    void SetScaleModeToScaleByVector()
+    {this->SetScaleMode(VTK_SCALE_BY_VECTOR);};
+    void SetScaleModeToScaleByVectorComponents()
+    {this->SetScaleMode(VTK_SCALE_BY_VECTORCOMPONENTS);};
+    void SetScaleModeToDataScalingOff()
+    {this->SetScaleMode(VTK_DATA_SCALING_OFF);};
+    const char *GetScaleModeAsString();
+
+    // Description:
+    // Either color by scale, scalar or by vector/normal magnitude.
+    vtkSetMacro(ColorMode,int);
+    vtkGetMacro(ColorMode,int);
+    void SetColorModeToColorByScale()
+    {this->SetColorMode(VTK_COLOR_BY_SCALE);};
+    void SetColorModeToColorByScalar()
+    {this->SetColorMode(VTK_COLOR_BY_SCALAR);};
+    void SetColorModeToColorByVector()
+    {this->SetColorMode(VTK_COLOR_BY_VECTOR);};
+    const char *GetColorModeAsString();
+
+    // Description:
+    // Specify scale factor to scale object by.
+    vtkSetMacro(ScaleFactor,double);
+    vtkGetMacro(ScaleFactor,double);
+
+    // Description:
+    // Specify range to map scalar values into.
+    vtkSetVector2Macro(Range,double);
+    vtkGetVectorMacro(Range,double,2);
+
+    // Description:
+    // Turn on/off orienting of input geometry along vector/normal.
+    vtkSetMacro(Orient,int);
+    vtkBooleanMacro(Orient,int);
+    vtkGetMacro(Orient,int);
+
+    // Description:
+    // Turn on/off clamping of "scalar" values to range. (Scalar value may be
+    //  vector magnitude if ScaleByVector() is enabled.)
+    vtkSetMacro(Clamping,int);
+    vtkBooleanMacro(Clamping,int);
+    vtkGetMacro(Clamping,int);
+
+    // Description:
+    // Specify whether to use vector or normal to perform vector operations.
+    vtkSetMacro(VectorMode,int);
+    vtkGetMacro(VectorMode,int);
+    void SetVectorModeToUseVector() {this->SetVectorMode(VTK_USE_VECTOR);};
+    void SetVectorModeToUseNormal() {this->SetVectorMode(VTK_USE_NORMAL);};
+    void SetVectorModeToVectorRotationOff()
+    {this->SetVectorMode(VTK_VECTOR_ROTATION_OFF);};
+    const char *GetVectorModeAsString();
+
+    // Description:
+    // Index into table of sources by scalar, by vector/normal magnitude, or
+    // no indexing. If indexing is turned off, then the first source glyph in
+    // the table of glyphs is used. Note that indexing mode will only use the
+    // InputScalarsSelection array and not the InputColorScalarsSelection
+    // as the scalar source if an array is specified.
+    vtkSetMacro(IndexMode,int);
+    vtkGetMacro(IndexMode,int);
+    void SetIndexModeToScalar() {this->SetIndexMode(VTK_INDEXING_BY_SCALAR);};
+    void SetIndexModeToVector() {this->SetIndexMode(VTK_INDEXING_BY_VECTOR);};
+    void SetIndexModeToOff() {this->SetIndexMode(VTK_INDEXING_OFF);};
+    const char *GetIndexModeAsString();
+
+    // Description:
+    // Enable/disable the generation of point ids as part of the output. The
+    // point ids are the id of the input generating point. The point ids are
+    // stored in the output point field data and named "InputPointIds". Point
+    // generation is useful for debugging and pick operations.
+    vtkSetMacro(GeneratePointIds,int);
+    vtkGetMacro(GeneratePointIds,int);
+    vtkBooleanMacro(GeneratePointIds,int);
+
+    // Description:
+    // Set/Get the name of the PointIds array if generated. By default the Ids
+    // are named "InputPointIds", but this can be changed with this function.
+    vtkSetStringMacro(PointIdsName);
+    vtkGetStringMacro(PointIdsName);
+
+    // Description:
+    // Enable/disable the generation of cell data as part of the output.
+    // The cell data at each cell will match the point data of the input
+    // at the glyphed point.
+    vtkSetMacro(FillCellData,int);
+    vtkGetMacro(FillCellData,int);
+    vtkBooleanMacro(FillCellData,int);
+
+    // Description:
+    // This can be overwritten by subclass to return 0 when a point is
+    // blanked. Default implementation is to always return 1;
+    virtual int IsPointVisible(vtkDataSet*, vtkIdType) {return 1;};
+
+    // Description:
+    // When set, this is use to transform the source polydata before using it to
+    // generate the glyph. This is useful if one wanted to reorient the source,
+    // for example.
+    void SetSourceTransform(vtkTransform*);
+    vtkGetObjectMacro(SourceTransform, vtkTransform);
+
+    // Description:
+    // Overridden to include SourceTransform's MTime.
+    virtual unsigned long GetMTime();
+    vtkSetMacro(ScalarVisibility,int);
+    vtkBooleanMacro(ScalarVisibility,int);
+    vtkGetMacro(ScalarVisibility,int);
+
+
+
+    vtkSetMacro(UseSecondScalar,bool);
+    vtkSetMacro(UseThirdScalar,bool);
+
+    vtkSetStringMacro(InputScalarsSelectionXZ);
+    vtkSetStringMacro(InputScalarsSelectionY);
+
+
+protected:
+    ExtendedGlyph3D();
+    ~ExtendedGlyph3D();
+
+    int ScalarVisibility;
+
+    bool UseSecondScalar;
+    bool UseThirdScalar;
+    char *InputScalarsSelectionXZ;
+    char *InputScalarsSelectionY;
+    virtual int RequestData(vtkInformation *, vtkInformationVector **, vtkInformationVector *);
+    virtual int RequestUpdateExtent(vtkInformation *, vtkInformationVector **, vtkInformationVector *);
+    virtual int FillInputPortInformation(int, vtkInformation *);
+
+    vtkPolyData* GetSource(int idx, vtkInformationVector *sourceInfo);
+
+    // Description:
+    // Method called in RequestData() to do the actual data processing. This will
+    // glyph the \c input, filling up the \c output based on the filter
+    // parameters.
+    virtual bool Execute(vtkDataSet* input,
+                         vtkInformationVector* sourceVector,
+                         vtkPolyData* output, int requestedGhostLevel);
+
+    vtkPolyData **Source; // Geometry to copy to each point
+    int Scaling; // Determine whether scaling of geometry is performed
+    int ScaleMode; // Scale by scalar value or vector magnitude
+    int ColorMode; // new scalars based on scale, scalar or vector
+    double ScaleFactor; // Scale factor to use to scale geometry
+    double Range[2]; // Range to use to perform scalar scaling
+    int Orient; // boolean controls whether to "orient" data
+    int VectorMode; // Orient/scale via normal or via vector data
+    int Clamping; // whether to clamp scale factor
+    int IndexMode; // what to use to index into glyph table
+    int GeneratePointIds; // produce input points ids for each output point
+    int FillCellData; // whether to fill output cell data
+    char *PointIdsName;
+    vtkTransform* SourceTransform;
+
+private:
+    ExtendedGlyph3D(const ExtendedGlyph3D&);  // Not implemented.
+    void operator=(const ExtendedGlyph3D&);  // Not implemented.
+};
+
+// Description:
+// Return the method of scaling as a descriptive character string.
+inline const char *ExtendedGlyph3D::GetScaleModeAsString(void)
+{
+    if ( this->ScaleMode == VTK_SCALE_BY_SCALAR )
+    {
+        return "ScaleByScalar";
+    }
+    else if ( this->ScaleMode == VTK_SCALE_BY_VECTOR )
+    {
+        return "ScaleByVector";
+    }
+    else
+    {
+        return "DataScalingOff";
+    }
+}
+
+// Description:
+// Return the method of coloring as a descriptive character string.
+inline const char *ExtendedGlyph3D::GetColorModeAsString(void)
+{
+    if ( this->ColorMode == VTK_COLOR_BY_SCALAR )
+    {
+        return "ColorByScalar";
+    }
+    else if ( this->ColorMode == VTK_COLOR_BY_VECTOR )
+    {
+        return "ColorByVector";
+    }
+    else
+    {
+        return "ColorByScale";
+    }
+}
+
+// Description:
+// Return the vector mode as a character string.
+inline const char *ExtendedGlyph3D::GetVectorModeAsString(void)
+{
+    if ( this->VectorMode == VTK_USE_VECTOR)
+    {
+        return "UseVector";
+    }
+    else if ( this->VectorMode == VTK_USE_NORMAL)
+    {
+        return "UseNormal";
+    }
+    else
+    {
+        return "VectorRotationOff";
+    }
+}
+
+// Description:
+// Return the index mode as a character string.
+inline const char *ExtendedGlyph3D::GetIndexModeAsString(void)
+{
+    if ( this->IndexMode == VTK_INDEXING_OFF)
+    {
+        return "IndexingOff";
+    }
+    else if ( this->IndexMode == VTK_INDEXING_BY_SCALAR)
+    {
+        return "IndexingByScalar";
+    }
+    else
+    {
+        return "IndexingByVector";
+    }
+}
+
+#endif
diff --git a/Code/src/filtercustomize.cpp b/Code/src/filtercustomize.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..72206cf1aa9eb9848c1b0d11c32bf00afeef1c1c
--- /dev/null
+++ b/Code/src/filtercustomize.cpp
@@ -0,0 +1,43 @@
+#include "filtercustomize.h"
+#include "ui_filtercustomize.h"
+#include "vispoint.h"
+
+FilterCustomize::FilterCustomize(vtkwindow_new *v, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::FilterCustomize)
+{
+    ui->setupUi(this);
+
+    vtkwin=v;
+    //add lut on main window FV
+    for (unsigned int i =0; i < vtkwin->vispoint->getOrigin()->getNumberOfColumns();i++)
+    {
+        QString field =  QString(vtkwin->vispoint->getOrigin()->getColName(i).c_str());
+        ui->fieldComboBox->addItem(field);
+    }
+
+
+    setRangeId(0);
+}
+
+FilterCustomize::~FilterCustomize()
+{
+    delete ui;
+}
+
+void FilterCustomize::on_fieldComboBox_activated(const QString &arg1)
+{
+    int id=vtkwin->vispoint->getOrigin()->getColId(arg1.toStdString());
+    setRangeId(id);
+
+}
+
+void FilterCustomize::setRangeId(int id)
+{
+
+    float range[3] ={};// = new float[3];
+    vtkwin->vispoint->getOrigin()->getRange(id,range);
+    ui->minValueLineEdit->setText(QString::number(range[0]));
+    ui->maxValueLineEdit->setText(QString::number(range[1]));
+}
+
diff --git a/Code/src/filtercustomize.h b/Code/src/filtercustomize.h
new file mode 100644
index 0000000000000000000000000000000000000000..fa72f6b3fb9b3cdce204b5ab7dd4907eba0f6c72
--- /dev/null
+++ b/Code/src/filtercustomize.h
@@ -0,0 +1,29 @@
+#ifndef FILTERCUSTOMIZE_H
+#define FILTERCUSTOMIZE_H
+#include <QWidget>
+#include "vtkwindow_new.h"
+
+namespace Ui {
+class FilterCustomize;
+}
+
+class FilterCustomize : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit FilterCustomize(vtkwindow_new *v, QWidget *parent = 0);
+
+    ~FilterCustomize();
+
+private slots:
+    void on_fieldComboBox_activated(const QString &arg1);
+    void setRangeId(int id);
+
+private:
+    Ui::FilterCustomize *ui;
+    vtkwindow_new *vtkwin;
+
+};
+
+#endif // FILTERCUSTOMIZE_H
diff --git a/Code/src/fitsimagestatisiticinfo.cpp b/Code/src/fitsimagestatisiticinfo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..702e8b93e5928e52e12025a467a949edc8a926d2
--- /dev/null
+++ b/Code/src/fitsimagestatisiticinfo.cpp
@@ -0,0 +1,50 @@
+#include "fitsimagestatisiticinfo.h"
+#include "ui_fitsimagestatisiticinfo.h"
+#include <QDebug>
+
+FitsImageStatisiticInfo::FitsImageStatisiticInfo(vtkwindow_new *v, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::FitsImageStatisiticInfo)
+{
+    ui->setupUi(this);
+
+    vtkwin=v;
+
+    qDebug()<<vtkwin->getWindowName();
+
+}
+
+FitsImageStatisiticInfo::~FitsImageStatisiticInfo()
+{
+    delete ui;
+}
+
+void FitsImageStatisiticInfo::setFilename()
+{
+    ui->filenameLabel->setText(vtkwin->getWindowName());
+}
+
+void FitsImageStatisiticInfo::setGalaptic(double l, double b)
+{
+    ui->lGalapticLabel->setText( QString::number(l) );
+    ui->bGalapticLabel->setText( QString::number(b) );
+}
+
+void FitsImageStatisiticInfo::setEcliptic(double lat, double lon)
+{
+    ui->latEclipticLabel->setText( QString::number(lat) );
+    ui->longEclipticLabel->setText( QString::number(lon) );
+}
+
+void FitsImageStatisiticInfo::setFk5(double ra, double dec)
+{
+    ui->raFk5Label->setText( QString::number(ra) );
+    ui->decFk5Label->setText( QString::number(dec) );
+}
+
+
+void FitsImageStatisiticInfo::setImage(double x, double y)
+{
+    ui->xLabel->setText( QString::number(x) );
+    ui->yLabel->setText( QString::number(y) );
+}
diff --git a/Code/src/fitsimagestatisiticinfo.h b/Code/src/fitsimagestatisiticinfo.h
new file mode 100644
index 0000000000000000000000000000000000000000..45d0a6679204fcbe0504a6899b19ccd9e121e18e
--- /dev/null
+++ b/Code/src/fitsimagestatisiticinfo.h
@@ -0,0 +1,32 @@
+#ifndef FITSIMAGESTATISITICINFO_H
+#define FITSIMAGESTATISITICINFO_H
+
+#include <QWidget>
+#include "vtkwindow_new.h"
+
+namespace Ui {
+class FitsImageStatisiticInfo;
+}
+
+class FitsImageStatisiticInfo : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit FitsImageStatisiticInfo(vtkwindow_new *v, QWidget *parent = 0);
+    ~FitsImageStatisiticInfo();
+    void setGalaptic(double l, double b);
+    void setEcliptic(double lat, double lon);
+    void setFk5(double ra, double dec);
+    void setImage(double x, double y);
+    void setFilename();
+
+
+
+
+private:
+    Ui::FitsImageStatisiticInfo *ui;
+    vtkwindow_new *vtkwin;
+};
+
+#endif // FITSIMAGESTATISITICINFO_H
diff --git a/Code/src/fitsimageviewer.cpp b/Code/src/fitsimageviewer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..367a7dccbef44fc980f66a7db2f74add85fdb656
--- /dev/null
+++ b/Code/src/fitsimageviewer.cpp
@@ -0,0 +1,5 @@
+#include "fitsimageviewer.h"
+
+FitsImageViewer::FitsImageViewer()
+{
+}
diff --git a/Code/src/fitsimageviewer.h b/Code/src/fitsimageviewer.h
new file mode 100644
index 0000000000000000000000000000000000000000..8135eb8c6716af2f81e40bf983fe5e5789241b20
--- /dev/null
+++ b/Code/src/fitsimageviewer.h
@@ -0,0 +1,10 @@
+#ifndef FITSIMAGEVIEWER_H
+#define FITSIMAGEVIEWER_H
+
+class FitsImageViewer
+{
+public:
+    FitsImageViewer();
+};
+
+#endif // FITSIMAGEVIEWER_H
diff --git a/Code/src/ftoa.h b/Code/src/ftoa.h
new file mode 100644
index 0000000000000000000000000000000000000000..5763f2e33206f7e66c4272e44f4594f323af0ae3
--- /dev/null
+++ b/Code/src/ftoa.h
@@ -0,0 +1,75 @@
+#ifndef FTOA
+#define FTOA
+// C program for implementation of ftoa()
+#include<stdio.h>
+#include<math.h>
+#include <qstring.h>
+#include <qbytearray.h>
+
+
+// reverses a string 'str' of length 'len'
+void reverse(char *str, int len)
+//void reverse(QString str, int len)
+{
+    int i=0, j=len-1, temp;
+    while (i<j)
+    {
+        temp = str[i];
+        str[i] = str[j];
+        str[j] = temp;
+        i++; j--;
+    }
+}
+
+ // Converts a given integer x to string str[].  d is the number
+ // of digits required in output. If d is more than the number
+ // of digits in x, then 0s are added at the beginning.
+int intToStr(int x, char str[], int d)
+//int intToStr(int x, QString str, int d)
+{
+    int i = 0;
+    while (x)
+    {
+        str[i++] = (x%10) + '0';
+        x = x/10;
+    }
+
+    // If number of digits required is more, then
+    // add 0s at the beginning
+    while (i < d)
+        str[i++] = '0';
+
+    reverse(str, i);
+    str[i] = '\0';
+    return i;
+}
+
+// Converts a floating point number to string.
+void ftoa(float n, char *res, int afterpoint)
+//void ftoa(float n, QString res, int afterpoint)
+{
+    // Extract integer part
+    int ipart = (int)n;
+
+    // Extract floating part
+    float fpart = n - (float)ipart;
+
+    // convert integer part to string
+    int i = intToStr(ipart, res, 0);
+
+    // check for display option after point
+    if (afterpoint != 0)
+    {
+        res[i] = '.';  // add dot
+
+        // Get the value of fraction part upto given no.
+        // of points after dot. The third parameter is needed
+        // to handle cases like 233.007
+        fpart = fpart * pow(10, afterpoint);
+
+        intToStr((int)fpart, res + i + 1, afterpoint);
+    }
+}
+
+#endif // FTOA
+
diff --git a/Code/src/higalselectedsources.cpp b/Code/src/higalselectedsources.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7ea02b58993c4877b6dcc87f5e4cb9ec3d781549
--- /dev/null
+++ b/Code/src/higalselectedsources.cpp
@@ -0,0 +1,312 @@
+#include "higalselectedsources.h"
+#include "ui_higalselectedsources.h"
+#include "qdebug.h"
+#include <QListWidget>
+//#include "ViaLactea.h"
+#include "singleton.h"
+#include "plotwindow.h"
+//#include "viewselectedsourcedataset.h"
+#include "selectedsourcefieldsselect.h"
+#include <QWidgetAction>
+#include "vlkbquery.h"
+#include "astroutils.h"
+#include "vtkCleanPolyData.h"
+#include "vtkPolyData.h"
+HigalSelectedSources::HigalSelectedSources(vtkwindow_new *v, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::HigalSelectedSources)
+{
+    ui->setupUi(this);
+
+    vtkwin=v;
+
+    plotMenu = new QMenu("Plot", this);
+
+
+    QAction*   newWindowAction = new QAction("New plot window", this);
+    plotMenu->addAction(newWindowAction);
+    connect(newWindowAction, SIGNAL(triggered()), this, SLOT(plotNewWindow()));
+
+    existingWindowMenu = new QMenu("Existing plot", this);
+    //existingWindowMenu->setEnabled(false);
+    plotMenu->addMenu(existingWindowMenu);
+
+    ui->plotButton->setMenu(plotMenu);
+
+    itemSelected=false;
+    itemChanged=true;
+
+    qDebug()<<"HigalSelectedSources costruttore";
+
+}
+
+void HigalSelectedSources::setConnect(QListWidget * list)
+{
+
+    connect( list, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*) ), this, SLOT( sourceChangedEvent(QListWidgetItem*,QListWidgetItem*) ) );
+    connect( list, SIGNAL( itemSelectionChanged()) , this, SLOT( itemSelectionChanged() ) );
+    connect( list, SIGNAL( itemPressed(QListWidgetItem*)) , this, SLOT( itemPressed(QListWidgetItem*) ) );
+
+}
+
+void HigalSelectedSources::itemPressed(QListWidgetItem *cur)
+{
+
+    if( itemChanged)
+    {
+
+        qDebug()<<ui->tabWidget->tabText(ui->tabWidget->currentIndex());
+
+        VSTableDesktop *table=vtkwin->getEllipseList().value(cur->text())->getTable();
+        std::cout<<table->getName()<<std::endl;
+        qDebug()<<"cur: "<<cur->text();
+        int row = vtkwin->getEllipseList().value(cur->text())->getRowInTable();
+        qDebug()<<"row: "<<row;
+
+
+        double semiMajorAxisLength,semiMinorAxisLength,angle,ra,dec;
+
+        int id=table->getColId("fwhma");
+        if(id == -1)
+            id=table->getColId("fwhma"+ui->tabWidget->tabText(ui->tabWidget->currentIndex()).toStdString());
+        if(id == -1)
+            qDebug()<<"non esiste";
+        else
+            semiMajorAxisLength= ::atof(table->getTableData()[id][row].c_str());
+
+        //double semiMajorAxisLength= ::atof(table->getTableData()[table->getColId("fwhma")][row].c_str());
+
+        id=table->getColId("fwhmb");
+        if(id == -1)
+            id=table->getColId("fwhmb"+ui->tabWidget->tabText(ui->tabWidget->currentIndex()).toStdString());
+        if(id == -1)
+            qDebug()<<"non esiste";
+        else
+            semiMinorAxisLength= ::atof(table->getTableData()[id][row].c_str());
+
+        id=table->getColId("pa");
+        if(id == -1)
+            id=table->getColId("pa"+ui->tabWidget->tabText(ui->tabWidget->currentIndex()).toStdString());
+        if(id == -1)
+            qDebug()<<"non esiste";
+        else
+            angle= ::atof(table->getTableData()[id][row].c_str());
+
+        //double angle= ::atof(table->getTableData()[table->getColId("pa")][row].c_str());
+
+        id=table->getColId("glon");
+        if(id == -1)
+            id=table->getColId("glon"+ui->tabWidget->tabText(ui->tabWidget->currentIndex()).toStdString());
+        if(id == -1)
+            qDebug()<<"non esiste";
+        else
+            ra= ::atof(table->getTableData()[id][row].c_str());
+
+        // double ra= ::atof(table->getTableData()[table->getColId("glon")][row].c_str());
+
+        id=table->getColId("glat");
+        if(id == -1)
+            id=table->getColId("glat"+ui->tabWidget->tabText(ui->tabWidget->currentIndex()).toStdString());
+        if(id == -1)
+            qDebug()<<"non esiste";
+        else
+            dec= ::atof(table->getTableData()[id][row].c_str());
+
+
+        //  double dec= ::atof(table->getTableData()[table->getColId("glat")][row].c_str());
+
+        double *coord= new double[3];
+
+        AstroUtils().sky2xy(vtkwin->filenameWithPath,ra,dec,coord);
+
+
+        vtkEllipse *el= new vtkEllipse(semiMajorAxisLength/2,semiMinorAxisLength/2,angle, coord[0], coord[1], coord[2], 0,0, cur->text(), NULL);
+        drawSingleEllipse(el);
+        itemChanged=false;
+    }
+
+
+}
+
+void HigalSelectedSources::itemSelectionChanged()
+{
+
+    itemSelected=true;
+    if(ellipseActor)
+    {
+        vtkwin->removeSingleEllipse(ellipseActor);
+    }
+
+
+}
+
+void HigalSelectedSources::sourceChangedEvent(QListWidgetItem* cur,QListWidgetItem* pre)
+{
+    if (pre)
+        itemChanged= true;
+
+    /*
+    if(itemSelected)
+    {
+        qDebug()<<"\t*";
+
+        if(ellipseActor)
+        {
+            vtkwin->removeSingleEllipse(ellipseActor);
+        }
+
+        VSTable *table=vtkwin->getEllipseList().value(cur->text())->getTable();
+        int row = vtkwin->getEllipseList().value(cur->text())->getRowInTable();
+        double semiMajorAxisLength= ::atof(table->getTableData()[table->getColId("fwhma")][row].c_str());
+        double semiMinorAxisLength= ::atof( table->getTableData()[table->getColId("fwhmb")][row].c_str());
+        double angle= ::atof(table->getTableData()[table->getColId("pa")][row].c_str());
+        double ra= ::atof(table->getTableData()[table->getColId("glon")][row].c_str());
+        double dec= ::atof(table->getTableData()[table->getColId("glat")][row].c_str());
+
+        double *coord= new double[3];
+
+        AstroUtils().sky2xy(vtkwin->filenameWithPath,ra,dec,coord);
+
+
+        vtkEllipse *el= new vtkEllipse(semiMajorAxisLength/2,semiMinorAxisLength/2,angle, coord[0], coord[1], coord[2], 0,0, cur->text(), NULL);
+        drawSingleEllipse(el);
+        */
+}
+
+
+
+
+void HigalSelectedSources::drawSingleEllipse(vtkEllipse * ellipse )
+{
+
+    vtkSmartPointer<vtkCleanPolyData> cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
+#if VTK_MAJOR_VERSION <= 5
+    cleanFilter->SetInput(ellipse->getPolyData());
+#else
+    cleanFilter->SetInputData(ellipse->getPolyData());
+#endif
+
+    cleanFilter->Update();
+
+    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputConnection(cleanFilter->GetOutputPort());
+
+    ellipseActor = vtkSmartPointer<vtkLODActor>::New();
+    ellipseActor->SetMapper(mapper);
+
+    ellipseActor->GetProperty()->SetColor(0, 0, 0);
+    vtkwin->drawSingleEllipse(ellipseActor);
+
+}
+
+HigalSelectedSources::~HigalSelectedSources()
+{
+    delete ui;
+}
+
+
+void HigalSelectedSources::plotNewWindow()
+{
+
+    QList<QListWidgetItem*> selectedItems = qobject_cast<QListWidget *>(ui->tabWidget->currentWidget())->selectedItems();
+
+    PlotWindow *plotwin= new PlotWindow(vtkwin,selectedItems,plotWindowList.size());
+    plotWindowList.append(plotwin);
+
+    plotwin ->show();
+    updateExistingWindowMenu();
+
+}
+
+void HigalSelectedSources::on_datasetButton_clicked()
+{
+
+    QList<QListWidgetItem*> selectedItems = qobject_cast<QListWidget *>(ui->tabWidget->currentWidget())->selectedItems();
+
+
+    selectedSourceFieldsSelect *selectFields = new selectedSourceFieldsSelect(vtkwin,selectedItems);
+    selectFields->show();
+
+}
+
+void HigalSelectedSources::updateExistingWindowMenu()
+{
+
+    existingWindowMenu->clear();
+
+    for(int i=0;i< plotWindowList.count();i++)
+    {
+
+        if(plotWindowList[i]->isVisible())
+        {
+            QAction*   newWindowAction = new QAction(plotWindowList[i]->windowTitle(), this);
+            existingWindowMenu->addAction(newWindowAction);
+        }
+
+
+    }
+}
+
+void HigalSelectedSources::on_selectAllButton_clicked()
+{
+    QListWidget *list=qobject_cast<QListWidget *>(ui->tabWidget->currentWidget());
+
+    for(int i=0;i< list->count();i++)
+    {
+        list->item(i)->setSelected(true);
+    }
+}
+
+void HigalSelectedSources::on_deselectAllButton_clicked()
+{
+    QListWidget *list=qobject_cast<QListWidget *>(ui->tabWidget->currentWidget());
+    for(int i=0;i< list->count();i++)
+    {
+        list->item(i)->setSelected(false);
+    }
+}
+
+void HigalSelectedSources::on_sedButton_clicked()
+{
+
+    QString wave=QString::number(vtkwin->file_wavelength.value(ui->tabWidget->tabText(ui->tabWidget->currentIndex())));
+    if( wave.compare("0") ==0 )
+        wave=ui->tabWidget->tabText(ui->tabWidget->currentIndex());
+
+    qDebug()<<"SELECTED: "<<ui->tabWidget->tabText(ui->tabWidget->currentIndex())<<" -> "<<wave;
+
+    QList<QListWidgetItem*> selectedItems =qobject_cast<QListWidget *>(ui->tabWidget->currentWidget())->selectedItems();
+    for(int i=0;i<selectedItems.size();i++)
+    {
+
+        //QString query="SELECT * FROM vlkb_compactsources.bandmerged_sed_view where designation"+wave;
+        //   QString query="SELECT * FROM vlkb_compactsources.sed_view_final where designation"+wave;
+
+        // QString query="SELECT * FROM vlkb_compactsources.sed_view_final where numidtree";
+        // query+="='"+QString::number(vtkwin->getFtEllipseList().value(selectedItems.at(i)->text())->getNumidtree())+"'";
+
+       // QString query="SELECT 1 as flux22, 0.2 as err_flux22, * FROM vlkb_compactsources.sed_view_final where designationft=";
+        QString query="SELECT  * FROM vlkb_compactsources.sed_view_final where designationft=";
+        query+="'"+vtkwin->getFtEllipseList().value(selectedItems.at(i)->text())->getSourceName()+"'";
+
+
+        new VLKBQuery(query,vtkwin);
+        qDebug()<< query;
+    }
+    if(ellipseActor)
+        vtkwin->removeSingleEllipse(ellipseActor);
+
+    //this->close();
+}
+
+void HigalSelectedSources::closeEvent(QCloseEvent *event)
+{
+    itemSelectionChanged();
+    event->accept();
+    vtkwin->activateWindow();
+}
+
+void HigalSelectedSources::on_tabWidget_currentChanged(int index)
+{
+    qobject_cast<QListWidget *>(ui->tabWidget->currentWidget())->clearSelection();
+}
diff --git a/Code/src/higalselectedsources.h b/Code/src/higalselectedsources.h
new file mode 100644
index 0000000000000000000000000000000000000000..459cc625ba69c84604a69703ef51515d20113597
--- /dev/null
+++ b/Code/src/higalselectedsources.h
@@ -0,0 +1,50 @@
+#ifndef HIGALSELECTEDSOURCES_H
+#define HIGALSELECTEDSOURCES_H
+
+#include <QWidget>
+#include "vtkwindow_new.h"
+#include <QList>
+#include "plotwindow.h"
+
+namespace Ui {
+class HigalSelectedSources;
+}
+
+class HigalSelectedSources : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit HigalSelectedSources(vtkwindow_new *v,QWidget *parent = 0);
+    ~HigalSelectedSources();
+    Ui::HigalSelectedSources *ui;
+
+private slots:
+    void plotNewWindow();
+    void on_datasetButton_clicked();
+    void on_selectAllButton_clicked();
+    void on_deselectAllButton_clicked();
+    void updateExistingWindowMenu();
+    void on_sedButton_clicked();
+    void sourceChangedEvent(QListWidgetItem* cur,QListWidgetItem* pre);
+    void itemSelectionChanged();
+    void itemPressed(QListWidgetItem* cur);
+    void drawSingleEllipse(vtkEllipse * ellipse );
+    void closeEvent(QCloseEvent *event);
+    void on_tabWidget_currentChanged(int index);
+
+public slots:
+    void setConnect(QListWidget *list);
+
+private:
+        vtkwindow_new *vtkwin;
+        QList<PlotWindow*> plotWindowList;
+        QMenu* plotMenu;
+        QMenu* existingWindowMenu;
+        vtkSmartPointer<vtkLODActor>  ellipseActor;
+        bool itemSelected;
+        bool itemChanged;
+
+};
+
+#endif // HIGALSELECTEDSOURCES_H
diff --git a/Code/src/histogram.cpp b/Code/src/histogram.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc21f69a40aa5ef82812f45555d0a08fc5ce7ee1
--- /dev/null
+++ b/Code/src/histogram.cpp
@@ -0,0 +1,174 @@
+#include "histogram.h"
+
+#include <QPainter>
+#include <cmath>
+
+Histogram::Histogram(QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f),
+_bins(NULL),
+_heightMax(1)
+{
+};
+
+void Histogram::setBins(QVector<int>& bins)
+{
+    if( !_toggled ) return;
+    _bins = bins;
+    update();
+}
+
+void Histogram::setBins(int* bins, int nbBins)
+{
+    if( !_toggled ) return;
+    _bins.resize(nbBins);
+    memcpy(_bins.data(), bins, nbBins*sizeof(int));
+    update();
+}
+
+
+void Histogram::clear()
+{
+    _bins.clear();
+    update();
+}
+
+void Histogram::mousePressEvent(QMouseEvent*)
+{
+    _toggled = ! _toggled;
+    if( !_toggled ) clear();
+}
+
+void Histogram::paintEvent(QPaintEvent*)
+{
+    QRect viewPort = rect();
+    int xLeft = viewPort.left();
+    int xRight = viewPort.right();
+    int yTop = viewPort.top();
+    int yBottom = viewPort.bottom();
+    int width = viewPort.width();
+    int height = viewPort.height();
+
+    QPainter painter(this);
+
+    QPen pen;
+    pen.setColor(Qt::black);
+    pen.setWidth(1);
+    painter.setPen(pen);
+
+    QBrush brush(Qt::SolidPattern);
+    brush.setColor("#DDDDDD");
+    painter.setBrush(brush);
+
+    // ---- Draw gray background ------------------------------------------------
+    painter.drawRect(xLeft, yTop, xRight, yBottom);
+
+    // ---- Draw vertical lines -------------------------------------------------
+    pen.setColor("#AAAAAA");
+    pen.setStyle(Qt::DashDotLine);
+    painter.setPen(pen);
+
+    int stepsV = 1<< int(log(width/40.0f)/log(2.0f));
+    for(int i=1; i<stepsV; ++i)
+    {
+        painter.drawLine(width*float(i)/stepsV, yTop+1,
+                         width*float(i)/stepsV, yBottom-1);
+    }
+
+    // ---- Draw horizontal lines -----------------------------------------------
+    int stepsH = 1<< int(log(height/40.0f)/log(2.0f));
+    for(int i=1; i<stepsH; ++i)
+    {
+        painter.drawLine(xLeft+1, height*float(i)/stepsH,
+                         xRight-1,height*float(i)/stepsH);
+    }
+
+    // ---- Histogram itself ----------------------------------------------------
+    int nbBins = _bins.size();
+
+    if( !nbBins )
+    {
+        pen.setColor("#016790");
+        painter.setPen(pen);
+        painter.drawText(xLeft+2, yBottom-2, tr("Histogram off"));
+        return;
+    }
+
+    // Find maximum height in bins unit
+    int heightMax=1;
+    for( int i=0; i<nbBins; ++i )
+        if( _bins[i]>heightMax ) heightMax = _bins[i];
+
+    // Avoid giggling graph: do not update heightmax if variation <5%
+    if( abs(_heightMax-heightMax)/float(_heightMax) > 0.05f )
+        _heightMax = heightMax;
+
+    // Scale histogram from bins unit to pixels unit
+    // handle upscaling and downscaling in a different way
+    QPolygon myPolygon;
+    QLinearGradient linearGradient(0, 0, 0, height);
+    pen.setStyle(Qt::SolidLine);
+
+    if( nbBins < width )
+    {
+        float wScale = width/float(nbBins);
+        float hScale = height/float(_heightMax);
+        float hScaleLog = height/log(float(_heightMax));
+
+        // log(bins)
+        pen.setColor("#00aaee");   painter.setPen(pen);
+        linearGradient.setColorAt(0.2, Qt::white);
+        linearGradient.setColorAt(1.0, "#00aaee");
+        painter.setBrush(linearGradient);
+
+        //brush.setColor("#00aaee"); painter.setBrush(brush);
+
+        myPolygon.clear();
+        myPolygon << QPoint(xRight, yBottom) << QPoint(xLeft, yBottom);
+        for( int i=0; i<nbBins; ++i )
+            myPolygon << QPoint(xLeft+wScale*i, yTop+hScaleLog*( _bins[i] ? log(_heightMax/float(_bins[i])) : _heightMax));
+        painter.drawPolygon(myPolygon);
+
+        // bins
+        pen.setColor("#016790");
+        painter.setPen(pen);
+        linearGradient.setColorAt(0.2, Qt::white);
+        linearGradient.setColorAt(1.0, "#016790");
+        painter.setBrush(linearGradient);
+
+        myPolygon.clear();
+        myPolygon << QPoint(xRight, yBottom) << QPoint(xLeft, yBottom);
+        for( int i=0; i<nbBins; ++i )
+            myPolygon << QPoint(xLeft+wScale*i, yTop+hScale*(_heightMax-_bins[i]));
+        painter.drawPolygon(myPolygon);
+    }
+    else
+    {
+        float wScale = float(nbBins-1)/(width-1);
+        float hScale = height/float(_heightMax);
+        float hScaleLog = height/log(float(_heightMax));
+
+        // log(bins)
+        pen.setColor("#00aaee");   painter.setPen(pen);
+        linearGradient.setColorAt(0.2, Qt::white);
+        linearGradient.setColorAt(1.0, "#00aaee");
+        painter.setBrush(linearGradient);
+
+        myPolygon.clear();
+        myPolygon << QPoint(xRight, yBottom) << QPoint(xLeft, yBottom);
+        for( int i=0; i<width; ++i )
+            myPolygon << QPoint(xLeft+i, yTop+hScaleLog*( _bins[wScale*i] ? log(_heightMax/float(_bins[wScale*i])) : _heightMax));
+        painter.drawPolygon(myPolygon);
+
+        // bins
+        pen.setColor("#016790");
+        painter.setPen(pen);
+        linearGradient.setColorAt(0.2, Qt::white);
+        linearGradient.setColorAt(1.0, "#016790");
+        painter.setBrush(linearGradient);
+
+        myPolygon.clear();
+        myPolygon << QPoint(xRight, yBottom) << QPoint(xLeft, yBottom);
+        for( int i=0; i<width; ++i )
+            myPolygon << QPoint(xLeft+i, yTop+hScale*(_heightMax-_bins[wScale*i]));
+        painter.drawPolygon(myPolygon);
+    }
+}
diff --git a/Code/src/histogram.h b/Code/src/histogram.h
new file mode 100644
index 0000000000000000000000000000000000000000..5f3aa38eeab604dac431cadc92f0a9f7f4572d15
--- /dev/null
+++ b/Code/src/histogram.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <QObject>
+#include <QWidget>
+
+class Histogram : public QWidget
+{
+    Q_OBJECT
+private:
+    QVector<int> _bins;
+    bool _toggled;
+    int _heightMax;
+
+public:
+    Histogram(QWidget* parent = 0, Qt::WindowFlags f = 0);
+    void setBins(QVector<int>& bins);
+    void setBins(int* bins, int nbBins);
+    void clear();
+    bool toggled() {return _toggled;}
+
+protected:
+    void paintEvent(QPaintEvent* event);
+    void mousePressEvent(QMouseEvent* event);
+};
diff --git a/Code/src/libsamp/.BASE b/Code/src/libsamp/.BASE
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Code/src/libsamp/.DS_Store b/Code/src/libsamp/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..b48f53d5c908e0be85b790c67cbbbc66d628ab8f
Binary files /dev/null and b/Code/src/libsamp/.DS_Store differ
diff --git a/Code/src/libsamp/.alias b/Code/src/libsamp/.alias
new file mode 100644
index 0000000000000000000000000000000000000000..ea6bd61e6d5c29b68d36ab3814496f6c79c90759
--- /dev/null
+++ b/Code/src/libsamp/.alias
@@ -0,0 +1,21 @@
+
+
+alias zt0   "unsetenv XMLRPC_TRACE_XML"
+alias zt1   "setenv XMLRPC_TRACE_XML 1"
+alias zt2   "setenv XMLRPC_TRACE_XML 2"
+
+alias hub   "java -jar jsamp/jsamp-1.3.jar hub"
+alias hmon  "java -jar jsamp/jsamp-1.3.jar hubmonitor"
+
+alias hubm  "java -jar jsamp/jsamp-1.3.jar hub -v -v -mode msg-gui"
+
+alias hubv  "java -jar jsamp/jsamp-1.3.jar hub -v -v"
+alias hmonv "java -jar jsamp/jsamp-1.3.jar hubmonitor -v -v"
+
+alias xhub  "java -jar jsamp/jsamp-1.3-nocheck.jar hub -verbose"
+alias xhmon "java -jar jsamp/jsamp-1.3-nocheck.jar hubmonitor -verbose"
+
+alias ssend "java -jar jsamp/jsamp-1.3.jar MessageSender \!*" 
+alias snoop "java -jar jsamp/jsamp-1.3.jar Snooper -verbose" 
+alias hubtester "java -jar jsamp/jsamp-1.3.jar HubTester -verbose" 
+alias sping "java -jar jsamp/jsamp-1.3.jar MessageSender -mtype samp.app.ping" 
diff --git a/Code/src/libsamp/.old/samp.c b/Code/src/libsamp/.old/samp.c
new file mode 100644
index 0000000000000000000000000000000000000000..6ddb955df0863d0eff12604f7c73db92afb47c29
--- /dev/null
+++ b/Code/src/libsamp/.old/samp.c
@@ -0,0 +1,979 @@
+/**
+ *  SAMP.C --  Top-level interface to the SAMP library.
+ *
+ *                  samp = sampInit  (appName, descr)
+ *                        sampClose  (samp)
+
+ *               stat = sampStartup  (samp)
+ *                     sampShutdown  (samp)
+ *
+ *                    samp_Metadata  (samp, field, value)
+ *                   samp_Subscribe  (samp, mtype, handler)
+ *                 samp_Unsubscribe  (samp, mtype)
+ *
+ *            samp_setReplyCallback  (samp, func)
+ *         samp_setResponseCallback  (samp, func)
+ *	    stat = samp_replyStatus  (samp)
+ *
+ *  Utility Methods:
+ *                 samp_setSyncMode  (samp)			// Default
+ *                samp_setASyncMode  (samp)
+ *               samp_setNotifyMode  (samp)
+ *                samp_setCallByRef  (samp)
+ *
+ *                  samp_setTimeout  (samp, timeout)
+ *                  samp_setAppNmae  (samp, version)
+ *               samp_setAppVersion  (samp, name)
+ *
+ *                  samp_mapClients  (handle_t handle)
+ *                   samp_addClient  (handle_t handle, String name, String id)
+ *                samp_removeClient  (handle_t handle, String id)
+ *
+ *  @brief      Top-level interface to the SAMP library.
+ *
+ *  @file       samp.c
+ *  @author     Mike Fitzpatrick
+ *  @date       7/10/09
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include "samp.h"
+
+
+Samp        *sampP;			/** SAMP struct pointer		*/
+handle_t     sampH;			/** SAMP handle			*/
+
+Map	     nullMap;			/** empty Map struct	        */
+Map	     OK_Map;			/** SAMP_OK Map struct	        */
+List	     nullList;			/** empty List struct	        */
+
+extern Hub  *hub;			/** HUB connection		*/
+extern int   numHubs;			/** No. of available hubs	*/
+extern long  hubHandles[MAX_HUBS];	/** Handles for hubs		*/
+
+
+static int  samp_initServer (Samp *sampP);
+static void samp_printSubs (Samp *sampP);
+
+
+
+
+/**
+ *  SAMP_INIT -- Initialize the SAMP interface.
+ *
+ *  @brief  	Initialize the SAMP interface.
+ *  @fn  	handle = sampInit (String name, String descr)
+ *
+ *  @param appName	application name
+ *  @param description	description of application
+ *  @return		samp handle
+ */
+handle_t
+sampInit (String appName, String description)
+{
+    handle_t handle = -1;
+
+
+    /*  Allocate the SAMP structure.
+     */
+    sampP = calloc (1, sizeof(Samp));
+    if ((sampH = handle = samp_newHandle ((void *) sampP))  < 0) {
+	fprintf (stderr, "Error allocating Samp struct.\n");
+	return (-1);
+    }
+
+
+    /* Initialize our connection to the Hub and register as a client.
+    */
+    strcpy (sampP->appName, appName);
+    strcpy (sampP->appVer, "1.0");
+    strcpy (sampP->description, description);
+
+    /*  Open a connection to the Hub.
+    */
+    if ((sampP->hubHandle = samp_hubOpen (sampP)) < 0)
+        sampP->hub = (Hub *) NULL;
+    else
+    	sampP->hub = (Hub *) samp_H2P (sampP->hubHandle);
+
+    sampP->msgMode         = DEF_CALLMODE;	/* set some defaults	    */
+    sampP->handlerMode     = SAMP_CBV;
+    sampP->trace           = SAMP_TRACE;
+    sampP->verbose         = TRUE;
+    sampP->serverPort      = samp_serverPort ();
+    sampP->svrThread       = (pthread_t) 0;
+    sampP->defaultUserFunc = NULL;
+
+    sampTrace (handle, "%d = sampInit(%s,'%s')\n", handle,appName,description);
+    
+    /*  Create a NULL map for messages.
+     */
+    nullMap  = samp_newMap ();
+    nullList = samp_newList ();
+
+    /*  Create a OK map for messages when there is no real value to be 
+     * returned.
+     */
+    OK_Map = samp_newMap ();
+        samp_setStringInMap (OK_Map, "samp.status", "samp.ok");
+        samp_setMapInMap (OK_Map, "samp.result", nullMap);
+
+    /* Return the handle to the structure.
+     */
+    return ( handle );
+}
+
+
+/**
+ *  SAMP_CLOSE -- Close the SAMP interface.
+ *
+ *  @brief  	Close the SAMP interface and free resources.
+ *  @fn		sampClose (handle_t handle)
+ * 
+ *  @param handle	user handle to samp struct
+ *  @return		nothing
+ */
+void 
+sampClose (handle_t handle)
+{
+    Samp *sampP = samp_H2P (handle);	/* get struct pointer	*/
+
+
+    sampTrace (handle, "sampClose (%d)\n", handle);
+
+    /* Close existing Hub handle.
+     */
+    if (sampP->hubHandle > 0 && samp_hubClose (sampP->hubHandle) != SAMP_OK)
+	fprintf (stderr, "Error closing Hub connection.\n");
+
+    if (sampP->hubHandle > 0)
+        samp_freeHandle (sampP->hubHandle);	/* release handles	*/
+    samp_freeHandle (handle);
+
+    sampP->hub = (Hub *) NULL;
+    sampP->hubHandle = -1;
+
+    free ((void *) sampP);			/* free SAMP structure	*/
+}
+
+
+/**
+ *  SAMPDEBUG -- Set interface debug level.
+ *
+ *  @brief  	Set interface debug level.
+ *  @fn		sampDebug (handle_t handle, int value)
+ * 
+ *  @param handle	user handle to samp struct
+ *  @param value	debug value
+ *  @return		nothing
+ */
+int 
+sampDebug (handle_t handle, int value)
+{
+    Samp *sampP = samp_H2P (handle);	/* get struct pointer	*/
+    if (value >= 0)
+        sampP->debug = value;
+
+    return (sampP->verbose);
+}
+
+
+/**
+ *  SAMPVERBOSE -- Set interface verbose level.
+ *
+ *  @brief  	Set interface debug level.
+ *  @fn		sampVerbose (handle_t handle, int value)
+ * 
+ *  @param handle	user handle to samp struct
+ *  @param value	verbose value
+ *  @return		nothing
+ */
+int 
+sampVerbose (handle_t handle, int value)
+{
+    Samp *sampP = samp_H2P (handle);	/* get struct pointer	*/
+    if (value >= 0)
+        sampP->verbose = value;
+
+    return (sampP->verbose);
+}
+
+
+/**
+ *  SAMP_STARTUP -- Startup the SAMP interface to send/receive messages.
+ *
+ *  @brief  	Startup the SAMP interface to send/receive messages.
+ *  @fn		sampStartup (handle_t handle)
+ * 
+ *  @param handle	user handle to samp struct
+ *  @return		nothing
+ */
+int 
+sampStartup (handle_t handle)
+{
+    Samp  *sampP = samp_H2P (handle);	/* get struct pointer	*/
+    int    stat;
+
+
+    stat = SAMP_OK;
+    if (sampP->active == 0) {
+	/*  Startup the application server so we can handle the responses.
+	 */
+        samp_initServer (sampP);
+
+	if (sampP->trace)
+	    samp_printSubs (sampP);
+	    
+	/*  Open the Hub connection.
+	 */
+	if (sampP->hubHandle < 0)
+            sampP->hubHandle = samp_hubOpen (sampP);
+
+	/*  We *still* can't open the Hub, a first attempt failed in sampInit()
+  	 */
+        if (sampP->hubHandle < 0) {
+            sampP->hub = (Hub *) NULL;
+	    if (sampP->verbose)
+	        fprintf (stderr, "Error: No Hub available on startup\n");
+    	    sampP->active = 0;
+	} else {
+            sampP->hub = (Hub *) samp_H2P (sampP->hubHandle);
+    	    sampP->active = 1;
+	}
+    }
+
+
+    /*  Do the client startup declarations to the hub.
+     */
+    if (sampP->hub) {
+
+	int   i, len, clients = 0, appnum = 0, done = 0;
+	char *app = NULL, *pubId = NULL;
+
+	/* First get the list of currently registered clients.  If we find
+ 	 * one already registered using our name, add a number to the name
+	 * and try agin.
+	 */
+        pubId =  samp_id2app (handle, sampP->appName);
+        clients = samp_GetRegisteredClients (handle);
+	while (!done) {
+	    done = 1;
+            for (i=0; i < samp_listLen (clients); i++) {
+                app = samp_getStringFromList (clients, i);
+                len = min (strlen (pubId), strlen (app));
+fprintf (stderr, "app = '%s'  pubId = '%s'\n", app, pubId);
+                if (strncasecmp (pubId, app, len) == 0) {
+		    sprintf (sampP->appName, "%s%d", sampP->appName, appnum++);
+    		    strcpy (hub->appName, sampP->appName);
+fprintf (stderr, "changed name to '%s'\n", sampP->appName);
+		    done = 0;
+                    break;
+                }
+            }
+	}
+        samp_freeList (clients);
+fprintf (stderr, "Registering with appName  '%s'\n", sampP->appName);
+
+
+
+        if (samp_hubDeclareMetadata (hub) != SAMP_OK) {
+            fprintf (stderr, "Error: Metadata declaration fails: '%s'\n", 
+                xr_getErrMsg (hub->id));
+            return ((int) 0);
+        }
+
+        if (samp_hubDeclareSubscriptions (hub) != SAMP_OK) {
+            fprintf (stderr, "Error: Subscription declaration fails: '%s'\n", 
+                xr_getErrMsg (hub->id));
+            return ((int) 0);
+        }
+
+        /*  Final step is to gather the metadata from the other attached
+         *  clients.  This allows us to send messages to the appName and not
+         *  just the public ID.
+         */
+        samp_mapClients (handle);
+    }
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_SHUTDOWN -- Shut down the active SAMP interface.
+ *
+ *  @brief  	Shut down the active SAMP interface.
+ *  @fn		sampShutdown (handle_t handle)
+ * 
+ *  @param handle	user handle to samp struct
+ *  @return		nothing
+ */
+int 
+sampShutdown (handle_t handle)
+{
+    Samp *sampP = samp_H2P (handle);
+
+
+    /* Close existing Hub handle.
+     */
+    if (sampP->hubHandle > 0 && samp_hubClose (sampP->hubHandle) != SAMP_OK)
+        fprintf (stderr, "Error closing Hub connection.\n");
+
+    /*  Stop XML-RPC server thread
+     */
+#ifdef KILL_SAMP_SERVER
+    if (pthread_kill (sampP->svrThread, SIGUSR1) != 0)
+	fprintf (stderr, "Error killing server thread %ld\n", 
+	    (long) sampP->svrThread);
+#endif
+
+    sampP->hubHandle = -1;
+    sampP->svrThread =  0;
+    sampP->active    =  0;
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_HUBACTIVE -- Determine if the Hub is active (i.e. connected).
+ *
+ *  @brief  	Determine if the Hub is active (i.e. connected).
+ *  @fn		samp_hubActive (handle_t handle)
+ * 
+ *  @param handle	user handle to samp struct
+ *  @return		nothing
+ */
+int 
+samp_hubActive (handle_t handle)
+{
+    Samp *sampP = samp_H2P (handle);
+    return (sampP->active);
+}
+
+
+
+/*****************************************************************************/
+
+/**
+ *  SAMP_METADATA -- Set a metadata field for the application.  This will
+ *  	be sent to the Hub later in a declareMetadata() call.
+ *
+ *  @brief 	Set a metadata field value for the application.
+ *  @fn		samp_Metadata (handle_t handle, String field, String value)
+ * 
+ *  @param  handle	samp handle
+ *  @param  field	metadata field to set (name, author, etc)
+ *  @param  samp	value of field
+ *  @return		nothing
+ */
+void
+samp_Metadata (handle_t handle, String field, String value)
+{
+    Samp *sampP = samp_H2P (handle);
+    Hub  *hub = sampP->hub;
+
+
+    sampTrace (handle, "sampSetMetadata (%d) %s = %s\n", handle, field, value);
+
+    if (strcmp (field, "samp.name") == 0) { 	/* Known metadata values      */
+        strcpy (sampP->meta.name, value);
+	strcpy (sampP->appName, value);
+	if (hub)
+	    strcpy (hub->appName, value);
+    } else if (strcmp (field, "samp.description.text") == 0) {
+        strcpy (sampP->meta.desc, value);
+	strcpy (sampP->description, value);
+	if (hub)
+	    strcpy (hub->description, value);
+    } else if (strcmp (field, "samp.icon.url") == 0) {
+        strcpy (sampP->meta.iconURL, value);
+	if (hub)
+            strcpy (hub->meta.iconURL, value);
+    } else if (strcmp (field, "samp.documentation.url") == 0) {
+        strcpy (sampP->meta.docURL, value);
+	if (hub)
+            strcpy (hub->meta.docURL, value);
+    } else {
+	sampP->meta.aKey[sampP->meta.nkeys] = calloc (1, strlen (field) + 1);
+	sampP->meta.aVal[sampP->meta.nkeys] = calloc (1, strlen (value) + 1);
+
+        strcpy (sampP->meta.aKey[sampP->meta.nkeys], field);
+        strcpy (sampP->meta.aVal[sampP->meta.nkeys], value);
+        sampP->meta.nkeys++;
+
+	if (hub) {
+	    int n = hub->meta.nkeys;
+	    hub->meta.aKey[n] = calloc (1, strlen (field) + 1);
+	    hub->meta.aVal[n] = calloc (1, strlen (value) + 1);
+
+            strcpy (hub->meta.aKey[n], field); 
+            strcpy (hub->meta.aVal[n], value);
+            hub->meta.nkeys++;
+	}
+    }
+
+    if (hub) {
+    	strcpy (hub->meta.name, sampP->appName);
+    	strcpy (hub->appName, sampP->appName);
+    	strcpy (hub->appVer, sampP->appVer);
+    	strcpy (hub->meta.desc, sampP->description);
+    	strcpy (hub->description, sampP->description);
+    }
+}
+
+
+/**
+ *  SAMP_SUBSCRIBE -- Subscribe to a given mtype.
+ *
+ *  @brief  	Subscribe to a given mtype.
+ *  @fn		samp_Subscribe (handle_t handle, String mtype, void *func)
+ * 
+ *  @param handle	user handle to samp struct
+ *  @param mtype	mtype name
+ *  @param func		callback function
+ *  @return		nothing
+ */
+void 
+samp_Subscribe (handle_t handle, String mtype, void *userFunc)
+{
+    Samp *sampP = samp_H2P (handle);
+
+
+    if (sampP->nsubs == MAX_SUBS) {
+	fprintf (stderr, "Error: Too many subscriptions\n");
+	exit (1);
+    }
+
+    if (samp_getSampHandler (mtype) == (void *) NULL) {
+	/* If there is no Samp Handler installed, assumed it's a generic
+	 * message so we can be sure a reply is sent in our handler.
+	 */
+        samp_setSampHandler (handle, mtype, samp_genericMsgHandler);
+    }
+
+    samp_setUserHandler (handle, mtype, userFunc);
+}
+
+
+/**
+ *  SAMP_UNSUBSCRIBE -- Unsubscribe to a given mtype.
+ *
+ *  @brief  	Unsubscribe to a given mtype.
+ *  @fn		samp_Unsubscribe (handle_t handle, String mtype)
+ * 
+ *  @param handle	user handle to samp struct
+ *  @param mtype	mtype name
+ *  @return		nothing
+ */
+void 
+samp_Unsubscribe (handle_t handle, String mtype)
+{
+    Samp *sampP = samp_H2P (handle);
+    register int i, j, found = 0;
+
+
+    /* Find the mtype in question.
+     */
+    for (i=0; i < sampP->nsubs; i++) {
+	if (strcasecmp (sampP->subs[i].mtype, mtype) == 0) {
+	    found = 1;
+	    break;
+	}
+    }
+
+    if (found) {
+	/*  Shift the remainder of the list. 		
+	 */
+	for (j=i; j < (sampP->nsubs - 1); j++) {
+	    memset (sampP->subs[j].mtype, 0, SZ_LINE);
+            strcpy (sampP->subs[j].mtype, sampP->subs[j+1].mtype);
+            sampP->subs[j].userFunc = sampP->subs[j+1].userFunc;
+            sampP->subs[j].sampFunc = sampP->subs[j+1].sampFunc;
+	}
+	memset (sampP->subs[j].mtype, 0, SZ_LINE);
+        sampP->nsubs--;
+
+	/*  Send unsubscribe msg to Hub.
+	 */
+        samp_DeclareSubscriptions (handle);
+    }
+}
+
+
+
+/*****************************************************************************/
+
+/**
+ *  SAMP_SETCALLMODE -- Set the default calling mode (synch or asynch)
+ *
+ *  @brief	Set the default calling mode (synch or asynch)
+ *  @fn 	samp_setCallMode (handle_t handle, int mode);
+ * 
+ *  @param  handle	samp handle
+ *  @param  mode	call mode (synch or asynch)
+ *  @return		nothing
+ */
+void
+samp_setCallMode (handle_t handle, int mode)
+{
+    Samp *sampP = samp_H2P (handle);
+    sampP->msgMode = mode;
+}
+
+
+/**
+ *  SAMP_SETSYNCMODE -- Set the calling mode to use synchronous messaging.
+ *
+ *  @brief	Set the calling mode to use synchronous messaging.
+ *  @fn 	samp_setSyncMode (handle_t handle)
+ * 
+ *  @param  handle	samp handle
+ *  @return		nothing
+ */
+void
+samp_setSyncMode (handle_t handle)
+{
+    Samp *sampP = samp_H2P (handle);
+    sampP->msgMode = MSG_SYNC;
+}
+
+
+/**
+ *  SAMP_SETASYNCMODE -- Set the calling mode to use asynchronous messaging.
+ *
+ *  @brief	Set the calling mode to use asynchronous messaging.
+ *  @fn 	samp_setASyncMode (handle_t handle)
+ * 
+ *  @param  handle	samp handle
+ *  @return		nothing
+ */
+void
+samp_setASyncMode (handle_t handle)
+{
+    Samp *sampP = samp_H2P (handle);
+    sampP->msgMode = MSG_ASYNC;
+}
+
+
+/**
+ *  SAMP_SETNOTIFYMODE -- Set the calling mode to use notification messaging.
+ *
+ *  @brief	Set the calling mode to use notification messaging.
+ *  @fn 	samp_setNotifyMode (handle_t handle)
+ * 
+ *  @param  handle	samp handle
+ *  @return		nothing
+ */
+void
+samp_setNotifyMode (handle_t handle)
+{
+    Samp *sampP = samp_H2P (handle);
+    sampP->msgMode = MSG_NOTIFY;
+}
+
+
+/**
+ *  SAMP_SETCALLBYREF -- Have interface call user handlers by reference.
+ *
+ *  @brief	Have interface call user handlers by reference.
+ *  @fn 	samp_setCallByRef (handle_t handle)
+ * 
+ *  @param  handle	samp handle
+ *  @return		nothing
+ */
+void
+samp_setCallByRef (handle_t handle)
+{
+    Samp *sampP = samp_H2P (handle);
+    sampP->handlerMode = SAMP_CBR;
+}
+
+
+/**
+ *  SAMP_SETREPLYCALLBACK -- Set the Reply callback.
+ *
+ *  @brief	Set the Reply callback.
+ *  @dn		samp_setReplyCallback (handle_t handle, int *func)
+ * 
+ *  @param  handle	samp handle
+ *  @param  func	callback for Reply message
+ *  @return		nothing
+ */
+void
+samp_setReplyCallback (handle_t handle, int *func)
+{
+}
+
+
+/**
+ *  SAMP_SETRESPONSECALLBACK -- Set the Response callback.
+ *
+ *  @brief	Set the Response callback.
+ *  @dn		samp_setResponseCallback (handle_t handle, int *func)
+ * 
+ *  @param  handle	samp handle
+ *  @param  func	callback for message response
+ *  @return		nothing
+ */
+void
+samp_setResponseCallback (handle_t handle, int *func)
+{
+}
+
+
+/**
+ *  SAMP_DEFAULTREPLYHANDLER -- The interface's default Reply handler.
+ *
+ *  @brief	The interface's default Reply handler.
+ *  @fn		samp_defaultReplyHandler (handle_t handle)
+ * 
+ *  @param  handle	samp handle
+ *  @return		nothing
+ */
+void
+samp_defaultReplyHandler (handle_t handle)
+{
+}
+
+
+/**
+ *  SAMP_DEFAULTRESPONSEHANDLER -- The interface's default Response handler.
+ *
+ *  @brief	The interface's default Response handler.
+ *  @fn		samp_defaultResponseHandler (handle_t handle)
+ * 
+ *  @param  handle	samp handle
+ *  @return		nothing
+ */
+void
+samp_deaultfResponseHandler (handle_t handle)
+{
+}
+
+
+/**
+ *  SAMP_REPLYSTATUS -- Reply with the status of the last message sent.
+ *
+ *  May be used to 'poll' for a reponse from the caller in cases where
+ *  use of a callback is a problem.  Codes are: <0==ERR, 0==PENDING, 1==OK    
+ * 
+ *  @brief	Reply with the status of the last message sent.
+ *  @fn		status = samp_replyStatus (handle_t handle)
+ *
+ *  @param	samp	samp struct ptr 
+ *  @return		message status
+ */	    
+int
+samp_replyStatus  (handle_t handle)
+{
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_SETTIMEOUT -- Set the message timeout value (in seconds).
+ *
+ *  @brief	Set the message timeout value (in seconds).
+ *  @fn		samp_setTimeout (handle_t handle, int timeout)
+ * 
+ *  @param  handle	samp handle
+ *  @param  name	application name
+ *  @return		nothing
+ */
+void
+samp_setTimeout (handle_t handle, int timeout)
+{
+    Samp *sampP = samp_H2P (handle);
+    char  buf[32];
+
+    memset (buf, 0, 32);
+    sprintf (buf, "%d", timeout);
+
+    strcpy (sampP->hub->timeout, buf);
+}
+
+
+/**
+ *  SAMP_SETAPPNAME -- Set the application name string.
+ *
+ *  @brief	Set the application name string.
+ *  @fn		samp_setAppVersion (handle_t handle, String name)
+ * 
+ *  @param  handle	samp handle
+ *  @param  name	application name
+ *  @return		nothing
+ */
+void
+samp_setAppName (handle_t handle, String name)
+{
+    Samp *sampP = samp_H2P (handle);
+
+    strcpy (sampP->appName, name);
+    strcpy (sampP->hub->appName, name);
+}
+
+
+/**
+ *  SAMP_SETAPPVERSION -- Set the application version string.
+ *
+ *  @brief	Set the application version string.
+ *  @fn		samp_setAppVersion (handle_t handle, String version)
+ * 
+ *  @param  handle	samp handle
+ *  @param  name	application version
+ *  @return		nothing
+ */
+void
+samp_setAppVersion (handle_t handle, String version)
+{
+    Samp *sampP = samp_H2P (handle);
+
+    strcpy (sampP->appVer, version);
+    strcpy (sampP->hub->appVer, version);
+}
+
+
+/**
+ *  SAMP_MAPCLIENTS -- Map the public-ids of registered clients to the 
+ *  appName.
+ */
+int
+samp_mapClients (handle_t handle)
+{
+    Samp  *sampP = samp_H2P (handle);	/* get struct pointer	*/
+    register int  i = 0;
+    char  *pub, rstr[SZ_RESSTR], *sres = rstr;
+    List clients;
+    Map  resp;
+
+
+    /*  Get the list of registered clients and their metadata.
+     */        
+    sampP->nclients = 0;
+    clients = samp_GetRegisteredClients (handle);
+    for (i=0; i < samp_listLen (clients); i++) {
+	pub = samp_getStringFromList (clients, i);
+        resp = samp_GetMetadata (handle, pub);
+        xr_getStringFromStruct (resp, "samp.name", &sres);
+
+	memset (sampP->clients[i].name, 0, SZ_NAME);
+	memset (sampP->clients[i].pubId, 0, SZ_NAME);
+
+	sampP->nclients++;
+	strcpy (sampP->clients[i].name, sres);
+	strcpy (sampP->clients[i].pubId, pub);
+	xr_freeStruct (resp);
+    }
+    samp_freeList (clients);
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_ADDCLIENT -- Add a newly registered client to the list of known
+ *  apps so we can do the public-private name translation.
+ */
+int
+samp_addClient (handle_t handle, String name, String id)
+{
+    Samp  *sampP = samp_H2P (handle);	/* get struct pointer	*/
+    register int  i = 0;
+
+
+    /*  Scan the list of registered clients in case this is a name change.
+     */        
+    for (i=0; i < sampP->nclients; i++) {
+	if (strcasecmp (sampP->clients[i].pubId, id) == 0) 
+	    break;
+    }
+
+    /*  Otherwise, add new client.
+     */
+    memset (sampP->clients[i].name, 0, SZ_NAME);
+    memset (sampP->clients[i].pubId, 0, SZ_NAME);
+
+    strcpy (sampP->clients[i].name, name);
+    strcpy (sampP->clients[i].pubId, id);
+    sampP->nclients++;
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_ADDCLIENT -- Add a newly registered client to the list of known
+ *  apps so we can do the public-private name translation.
+ */
+int
+samp_listClients (handle_t handle)
+{
+    Samp  *sampP = samp_H2P (handle);	/* get struct pointer	*/
+    register int  i = 0;
+
+
+    for (i=0; i < sampP->nclients; i++)
+	printf ("%2d:  PubID = '%s'  Name = '%s'\n",
+	    i, sampP->clients[i].pubId, sampP->clients[i].name);
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_REMOVECLIENT -- Remove a registered client from the list of known
+ *  apps that do the public-private name translation.
+ */
+int
+samp_removeClient (handle_t handle, String id)
+{
+    Samp  *sampP = samp_H2P (handle);	/* get struct pointer	*/
+    register int  i = 0;
+
+
+    /*  Find the client.
+     */        
+    for (i=0; i < sampP->nclients; i++) {
+	if (strcasecmp (sampP->clients[i].pubId, id) == 0) {
+	    if (i == (sampP->nclients - 1)) {	/* last entry, ignore */
+	        ;
+	    } else {
+		/* Shift the list. */
+    		for (i=0; i < (sampP->nclients-1); i++) {
+	            strcpy (sampP->clients[i].name,sampP->clients[i+1].name);
+	            strcpy (sampP->clients[i].pubId,sampP->clients[i+1].pubId);
+    		}
+	    }
+	    memset (sampP->clients[i].name, 0, SZ_NAME);
+	    memset (sampP->clients[i].pubId, 0, SZ_NAME);
+	    sampP->nclients--;
+	    break;
+	}
+    }
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_GETOKMAP -- Generate an 'OK' map we can return to the Hub.
+ */
+Map
+samp_getOKMap ()
+{
+    Map map;
+    map = samp_newMap ();
+        samp_setStringInMap (map, "samp.status", "samp.ok");
+        samp_setMapInMap (map, "samp.result", nullMap);
+
+    return (map);
+}
+
+
+/**
+ *  SAMP_GETNULLMAP -- Generate a 'Null' map we can return to the Hub.
+ */
+Map
+samp_getNullMap ()
+{
+    return ( samp_newMap() );
+}
+
+
+
+
+
+/******************************************************************************
+ *  Internal Interface procedures.
+ *****************************************************************************/
+
+/**
+ *  SAMP_INITSERVER -- Initialize the server-side of the SAMP.  Create
+ *  an XML-RPC server instance, define the commands and start the thread.
+ */
+static int
+samp_initServer (Samp *sampP)
+{
+    int  handle = samp_P2H(sampP);
+    static int initialized = 0;
+
+
+    if (initialized++)
+	return (SAMP_OK);
+
+    if (sampP->trace)
+	fprintf (stderr, "Initializing Server Methods:\n");
+
+    /*  Create the server and launch.  Note we never return from this
+    **  and make no calls to other servers.
+    */
+    xr_createServer ("/RPC2", sampP->serverPort, NULL);
+
+    /*  Define the required SAMP client methods.
+     *
+     *	receiveNotification (string sender-id, map message)
+     *	receiveCall (string sender-id, string msg-id, map message)
+     *	receiveResponse (string responder-id, string msg-tag, map response)
+     */
+    xr_addServerMethod ("test.echo", samp_testEcho,   		NULL);
+
+    xr_addServerMethod ("samp.client.receiveCall",
+ 				samp_receiveCall,   		NULL);
+    xr_addServerMethod ("samp.client.receiveNotification",
+				samp_receiveNotification,   	NULL);
+    xr_addServerMethod ("samp.client.receiveResponse",
+ 				samp_receiveResponse,   	NULL);
+
+    /*  Define the SAMP message handlers.
+    */
+    samp_setSampHandler(handle, "samp.app.ping",       samp_PingHandler);
+    samp_setSampHandler(handle, "samp.app.status",     samp_StatusHandler);
+    samp_setSampHandler(handle, "image.load.fits",     samp_imLoadHandler);
+    samp_setSampHandler(handle, "table.load.fits",     samp_tbLoadFITSHandler);
+    samp_setSampHandler(handle, "table.load.votable",  samp_tbLoadVOTHandler);
+    samp_setSampHandler(handle, "table.highlight.row", samp_tbHighlightHandler);
+    samp_setSampHandler(handle, "table.select.rowList",samp_tbSelectHandler);
+    samp_setSampHandler(handle, "coord.pointAt.sky",   samp_pointAtHandler);
+    samp_setSampHandler(handle, "client.cmd.exec",     samp_cmdExecHandler);
+    samp_setSampHandler(handle, "client.env.get",      samp_envGetHandler);
+    samp_setSampHandler(handle, "client.env.set",      samp_envSetHandler);
+    samp_setSampHandler(handle, "client.param.get",    samp_paramGetHandler);
+    samp_setSampHandler(handle, "client.param.set",    samp_paramSetHandler);
+
+    samp_setSampHandler(handle, "spectrum.load",       samp_specLoadHandler);
+    samp_setSampHandler(handle, "bibcode.load",        samp_bibcodeHandler);
+    samp_setSampHandler(handle, "voresource.loadlist", samp_resLoadHandler);
+
+
+    /*  Start the server thread.
+     */
+    sampP->svrThread = (pthread_t) xr_startServerThread ();
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_PRINTSUBS -- Print the application subscriptions.
+ */
+static void 
+samp_printSubs (Samp *sampP)
+{
+    if (!sampP->trace) {
+        int i = 0;
+
+        for (i=0; i < sampP->nsubs; i++) {
+          fprintf (stderr, "sampStartup: sub[%d] =  %12ld %12ld '%s'\n", i,
+	        (long)sampP->subs[i].userFunc, (long)sampP->subs[i].sampFunc, 
+	        sampP->subs[i].mtype);
+        }
+    }
+}
diff --git a/Code/src/libsamp/.old/sampCommands.c b/Code/src/libsamp/.old/sampCommands.c
new file mode 100644
index 0000000000000000000000000000000000000000..4660c7f40cf7c3419be127d4735b9fa224af6537
--- /dev/null
+++ b/Code/src/libsamp/.old/sampCommands.c
@@ -0,0 +1,357 @@
+/**
+ *  SAMPCOMMANDS.C -- SAMP commands used by app to send administrative messages.
+ *
+ *
+ *               stat = samp_Register  (samp)
+ *             stat = samp_UnRegister  (samp)
+ *        stat = samp_DeclareMetadata  (samp)
+ *                   stat = samp_Ping  (samp, appName)
+ *             map = samp_GetMetadata  (samp, pubId)
+ *          samp_DeclareSubscriptions  (samp, subscripMap)
+ *        map = samp_GetSubscriptions  (samp)
+ *   list = samp_GetRegisteredClients  (samp, mtype)
+ *
+ *
+ *  @brief      SAMP commands used by app to send administrative messages.
+ *
+ *  @file       sampCommands.c
+ *  @author     Mike Fitzpatrick
+ *  @date       7/10/11
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+
+#include "samp.h"
+
+
+
+/*****************************************************************************
+*/
+
+/**
+ *  SAMP_REGISTER -- Register with the Hub.  We use the currently stored
+ *  metadata.
+ *
+ *  @brief      ...
+ *  @fn         handle = samp_hubOpen (Samp *samp)
+ *
+ *  @param  samp        ...
+ *  @return             ...
+ */
+int
+samp_Register (handle_t samp)
+{
+    char *privateKey, *hubId, *selfId;
+    Samp *sampP = samp_H2P(samp);
+    Hub  *hub 	= sampP->hub;
+    int   reg;
+
+
+    if (!hub)
+	return (SAMP_ERR);
+
+    privateKey = hub->privateKey;
+    hubId      = hub->hubId;
+    selfId     = hub->selfId;
+
+    xr_initParam (hub->id);
+    xr_setStringInParam (hub->id, hub->secret);
+
+    xr_callSync (hub->id, "samp.hub.register");
+
+    /* Check for an error response.
+    */
+    if (samp_replyStatus (hub->id) != SAMP_OK) {
+	fprintf (stderr, "Hub registration failed.\n");
+	return (SAMP_ERR);
+    }
+	
+
+    xr_getStructFromResult (hub->id, &reg);
+        xr_getStringFromStruct (reg, "samp.private-key", &privateKey);
+        xr_getStringFromStruct (reg, "samp.hub-id", &hubId);
+        xr_getStringFromStruct (reg, "samp.self-id", &selfId);
+
+    /* Save the private key we get back.  Note there is a difference in
+    ** behavior between the hubs as to whether we strip any prefix on the
+    ** key returned in the response.
+    */
+    strcpy (hub->privateKey, &hub->privateKey[7]);
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_UNREGISTER -- Un-Register with the hub.
+ *
+ *  @brief      ...
+ *  @fn         handle = samp_hubOpen (Samp *samp)
+ *
+ *  @param  samp        ...
+ *  @return             ...
+ */
+int
+samp_UnRegister (handle_t samp)
+{
+    Samp *sampP = samp_H2P(samp);
+    Hub *hub = sampP->hub;
+
+
+    if (!hub)
+	return (SAMP_ERR);
+
+    xr_initParam (hub->id);
+
+    xr_setStringInParam (hub->id, hub->privateKey);
+    xr_callSync (hub->id, "samp.hub.unregister");
+
+    /* Check for error result.
+    */
+	;
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_DECLAREMETATA -- (Re)Declare all of our metadata.
+ *
+ *  @brief      ...
+ *  @fn         handle = samp_hubOpen (Samp *samp)
+ *
+ *  @param  samp        ...
+ *  @return             ...
+ */
+int
+samp_DeclareMetadata (handle_t samp)
+{
+    Samp *sampP = samp_H2P(samp);
+    Hub *hub = sampP->hub;
+    int   map;
+
+
+    if (!hub)
+	return (SAMP_ERR);
+
+    xr_initParam (hub->id);
+
+    xr_setStringInParam (hub->id, hub->appId);
+    map = xr_newStruct ();
+        xr_setStringInStruct (map, "samp.name", hub->appName);
+        xr_setStringInStruct (map, "samp.description.text", hub->description);
+    xr_setStructInParam (hub->id, map);
+
+    xr_callSync (hub->id, "samp.hub.declareMetadata");
+
+    /* ignore result */
+
+    return (SAMP_OK);
+}
+
+
+/**
+ *  SAMP_PING --  Ping the hub/app to see if it is alive (returns >0).
+ *
+ *  @brief      Ping the hub/app to see if it is alive (returns >0).
+ *  @fn         handle = samp_Ping (handle_t samp, String appName)
+ *
+ *  @param  samp        samp struct handle
+ *  @param  appName     application name
+ *  @return             OK or ERR if no response
+ */
+int
+samp_Ping (handle_t samp, String appName)
+{
+    Samp *sampP = samp_H2P(samp);
+    char  *tag = (char *) NULL;
+    int   res = -1;
+    Map   resp = (Map) 0;
+
+
+    if (strncasecmp (appName, "hub", 3) == 0) {
+        res = samp_hubPing (sampP->hub);
+        return ( samp_hubPing (sampP->hub) );
+    } else {
+        Msg   msg   = samp_newMsg ();
+        Param param = samp_newParam ();
+
+	samp_msgMType (msg, "samp.app.ping");
+	samp_msgParam (msg, param);
+
+	tag = samp_msgTag();
+        if (strncasecmp (appName, "all", 3) == 0) {
+	    /* callAll */
+	    resp = samp_callAll (samp, tag, msg);
+
+        } else {
+	    /* call    */
+	    resp = samp_callAndWait (samp, appName, tag, msg);
+	    samp_freeMap (resp);
+	}
+
+	samp_freeMsg (msg);
+    }
+
+    return (SAMP_ERR);
+}
+
+
+/**
+ *  SAMP_GETMETADATA -- Get the metadata for a specified app.
+ *
+ *  @brief      Get the metadata for a specified app.
+ *  @fn         map = samp_GetMetadata (Samp *samp, String pubId)
+ *
+ *  @param  samp        samp struct handle
+ *  @param  pubId       App public-id
+ *  @return             Map to message response
+ */
+Map
+samp_GetMetadata (handle_t samp, String pubId)
+{
+    Samp *sampP = samp_H2P(samp);
+    Hub *hub = sampP->hub;
+    Map   resp  = (Map) 0;
+/*
+    Map   err = (Map) 0, snum = (Map) 0, res = (Map) 0;
+*/
+
+
+    if (!hub)
+	return ((Map) 0);
+
+    xr_initParam (hub->id);             /* set calling parameters       */
+    xr_setStringInParam (hub->id, hub->privateKey);
+    xr_setStringInParam (hub->id, pubId);
+
+    if (xr_callSync (hub->id, "samp.hub.getMetadata") == OK) {
+        xr_getStructFromResult (hub->id, &resp);
+/*
+        char  rstr[SZ_RESSTR], *sres = rstr;
+        resp = samp_newMap ();
+            xr_getStringFromStruct (snum, "samp.status", &sres);
+	    if (strcasecmp (sres, "samp.ok") != 0) {
+        	xr_freeStruct (snum);
+    		return ( (Map) 0 );
+	    } else {
+                xr_getStructFromStruct (snum, "samp.error", &err);
+                xr_getStringFromStruct (snum, "samp.errortxt", &sres);
+		memset (sampP->errortxt, 0, SZ_LINE);
+		strcpy (sampP->errortxt, sres);
+
+                xr_getStructFromStruct (snum, "samp.result", &res);
+	    }
+        xr_freeStruct (snum);
+*/
+        return (resp);
+    } else {
+	memset (sampP->errortxt, 0, SZ_LINE);
+	strcpy (sampP->errortxt, xr_getErrMsg (hub->id));
+ 
+	fprintf (stderr, "Error: '%s'\n", sampP->errortxt);
+    }
+
+    return ( (Map) 0 );
+}
+
+
+/**
+ *  SAMP_DECLARESUBSCRIPIONS -- Declare the messages we're interested in.
+ *
+ *  @brief      ...
+ *  @fn         handle = samp_hubOpen (Samp *samp)
+ *
+ *  @param  samp        ...
+ *  @return             ...
+ */
+void
+samp_DeclareSubscriptions (handle_t samp, Map subscriptions)
+{
+    Samp *sampP = samp_H2P(samp);
+    Hub *hub = sampP->hub;
+
+    if (!hub)
+	return;
+
+    xr_initParam (hub->id);
+
+    /*  NYI  */
+}
+
+
+/**
+ *  SAMP_GETSUBSCRIPTIONS -- Get the message subscriptions for a specific app.
+ *
+ *  @brief      ...
+ *  @fn         handle = samp_hubOpen (Samp *samp, String pubId)
+ *
+ *  @param  samp        ...
+ *  @return             ...
+ */
+Map
+samp_GetSubscriptions (handle_t samp, String pubId)
+{
+    Samp *sampP = samp_H2P(samp);
+    Hub *hub = sampP->hub;
+    Map   resp  = (Map) 0;
+
+    if (!hub)
+	return ((Map) 0);
+
+    xr_initParam (hub->id);             /* set calling parameters       */
+    xr_setStringInParam (hub->id, hub->privateKey);
+    xr_setStringInParam (hub->id, pubId);
+    if (xr_callSync (hub->id, "samp.hub.getSubscriptions") == OK) {
+        xr_getStructFromResult (hub->id, &resp);
+        return (resp);
+    }
+
+    return ( (Map) 0 );
+}
+
+
+/**
+ *  SAMP_GETREGISTEREDCLIENTS -- Get the list of public-ids of the registered
+ *  clients.
+ *
+ *  @brief      ...
+ *  @fn         handle = samp_hubOpen (Samp *samp)
+ *
+ *  @param  samp        ...
+ *  @return             ...
+ */
+List
+samp_GetRegisteredClients (handle_t samp)
+{
+    Samp *sampP = samp_H2P(samp);
+    Hub *hub = sampP->hub;
+    int res;
+    List lres = (List) 0;
+
+
+    if (!hub)
+	return ((List) 0);
+
+    xr_initParam (hub->id);
+    xr_setStringInParam (hub->id, hub->privateKey);
+
+    xr_callSync (hub->id, "samp.hub.getRegisteredClients");
+
+    xr_getArrayFromResult (hub->id, &res);
+
+    /*
+    lres = samp_newList ();
+    for (i=0; i < samp_listLen (res); i++) {
+	samp_setStringInList (lres, xr_getStringFromList (list, i);
+    }
+    */
+    lres = res;
+
+    return (lres);
+}
diff --git a/Code/src/libsamp/samp.h b/Code/src/libsamp/samp.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2498be2cb0199533067d9cb68abafe24a808eab
--- /dev/null
+++ b/Code/src/libsamp/samp.h
@@ -0,0 +1,526 @@
+/**
+ *  SAMP.H -- SAMP interface include file.
+ *
+ *  @brief      SAMP interface include file.
+ *
+ *  @file       samp.h
+ *  @author     Mike FItzpatrick
+ *  @date       7/10/09
+ */
+
+#include "xmlrpc-c/base.h"             /* XMLRPC-C interface               */
+#include "xmlrpc-c/client.h"
+#include "xmlrpc-c/server.h"
+#include "xmlrpc-c/server_abyss.h"
+#include <sys/types.h>                  /* for struct stat                  */
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "xrpc.h"                       /* XRPC Wrapper Definitions         */
+
+#ifndef SZ_NAME
+#define	SZ_NAME	    	    256		/** size of a file path       	    */
+#endif
+#ifndef SZ_LINE
+#define	SZ_LINE		    256 	/** size of a text line       	    */
+#endif
+#ifndef SZ_URL
+#define	SZ_URL		    1024	/** len of a URL	      	    */
+#endif
+
+#define	SZ_SECRET	    64		/** size of secret string     	    */
+#define	SZ_DESC		    8192	/** len of a description      	    */
+#define	SZ_CMD		    1024	/** len of a command	      	    */
+#define SZ_SBUF             65536	/** big string buffer		    */
+#define	SZ_RESSTR	    1024	/** len of result string      	    */
+
+#define	DEF_PORT	    9876	/** server port			    */
+#define	DEF_TIMEOUT	    "15"	/** sync message timeout	    */
+
+#define MAX_SAMPS	    16		/** max clients allowed	      	    */
+#define	MAX_HUBS	    16		/** max hubs allowed	      	    */
+#define	MAX_MDATTRS	    32		/** max metadata attrs	      	    */
+#define	MAX_SUBS	    256		/** max subscriptions allowed  	    */
+#define	MAX_CLIENTS	    32		/** max number of clients      	    */
+#define	MAX_ROWS	    256		/** max rows to highlight  	    */
+
+#define SAMP_ERR	    -1		/** error return		    */
+#define SAMP_PENDING	    0		/** pending operation		    */
+#define SAMP_OK		    1		/** ok return			    */
+
+#define SAMP_CBR	    0		/** use call-by-reference	    */
+#define SAMP_CBV	    1		/** use call-by-value		    */
+
+#define	SAMP_SYNCH	    0		/** synchronous pattern		    */
+#define	SAMP_ASYNCH	    1		/** asynchronous pattern	    */
+#define	SAMP_NOTIFY	    2		/** notification pattern	    */
+
+#define SAMP_INT            TY_INT	/* values from xrpcP.h 	      	    */
+#define SAMP_DOUBLE         TY_DOUBLE
+#define SAMP_BOOL           TY_BOOL
+#define SAMP_STRING         TY_STRING
+#define SAMP_DATETIME       TY_DATETIME
+#define SAMP_SAMPRUCT       TY_STRUCT
+#define SAMP_ARRAY          TY_ARRAY
+
+                    /** debug trace               	    */
+#define	SAMP_TRACE   (getenv("SAMP_TRACE")||access("/tmp/SAMP_TRACE",F_OK)==0)
+
+
+/**
+ * Special Hub events
+ */
+#define HUB_SHUTDOWN	    0		/** Hub is shutting down	    */
+#define HUB_REGISTER	    1		/** An app has registered	    */
+#define HUB_UNREGISTER	    2		/** An app has unregistered	    */
+#define HUB_SUBSCRIPTIONS   3		/** An app declared subscritions    */
+#define HUB_METADATA	    4		/** An app declared metadata	    */
+#define HUB_DISCONNECT	    5		/** An app is forcibly disconnected */
+
+
+#define	LEN_DESC	    32768	/** max len of description    	    */
+
+#ifdef min
+#undef min
+#endif
+#ifdef max
+#undef max
+#endif
+
+#define  min(a,b)		(a<b?a:b)
+#define  max(a,b)		(a>b?a:b)
+
+
+typedef  long   handle_t; 		/** generic object handle	    */
+typedef  int    Map; 			/** SAMP Map datatype  		    */
+typedef  int    List; 			/** SAMP List datatype  	    */
+typedef  int    Msg; 			/** SAMP Msg datatype  		    */
+typedef  int    Param; 			/** SAMP Param datatype		    */
+typedef  char  *String;			/** SAMP String datatype  	    */
+
+
+/**
+ *  Application (and Hub) metadata.
+ */
+typedef struct {
+    char      name[SZ_LINE];		/** name			    */
+    char      desc[SZ_DESC];		/** descriptive text		    */
+    char      iconURL[SZ_URL];		/** icon URL			    */
+    char      docURL[SZ_URL];		/** documentation URL		    */
+
+    char     *descHTML;			/** descriptive text (HTML)	    */
+
+    int	      nkeys;			/** number of meta keys		    */
+    char     *aKey[MAX_MDATTRS];	/** attr keyword		    */
+    char     *aVal[MAX_MDATTRS];	/** attr value			    */
+} appMD, *appMDP;
+
+
+
+/**
+ *  Message subscription.
+ */
+typedef struct {
+    char  mtype[SZ_LINE];		/** mtype string		    */
+    int   (*userFunc)(void *p); 	/** user handler function           */
+                    /** samp handler function           */
+    int   (*sampFunc)(char *sid, char *sender, char *msgid, Map map);
+} Subs, *SubsP;
+
+
+
+/**
+ *  Registered Client name mappings.
+ */
+typedef struct {
+    char  pubId[SZ_NAME];		/** public name			    */
+    char  name[SZ_NAME];		/** app name			    */
+} Client, *ClientP;
+
+
+/**
+ *  Hub description.  Our application connects to this hub by default, but
+ *  the structure will be valid for any Hub.
+ */
+typedef struct {
+    char      appName[SZ_LINE];		/** application name		    */
+    char      appVer[SZ_LINE];		/** application version		    */
+    char      description[SZ_LINE];	/** descriptive text		    */
+
+    appMD     meta;			/** metadata			    */
+
+    char      secret[SZ_SECRET];	/** registration string		    */
+    char      url[SZ_URL];		/** Hub service endpoint	    */
+    char      version[SZ_NAME];		/** Hub version string		    */
+
+    int	      id;			/** Hub XML-RPC connection	    */
+
+    char      appId[SZ_NAME];		/** client key			    */
+    char      privateKey[SZ_LINE];	/** client key value		    */
+    char      hubId[SZ_LINE];		/** Hub id value		    */
+    char      selfId[SZ_LINE];		/** Client id value		    */
+    char      timeout[SZ_NAME];		/** Sync msg timeout (str)	    */
+
+    void     *samp;			/** back pointer		    */
+} Hub, *HubP;
+
+
+/**
+ *  SAMP application description.  By default this describes our app by
+ *  may be used to store information about other apps in the network as
+ *  well.
+ */
+typedef struct {
+    char      appName[SZ_NAME];		/** application name		    */
+    char      appVer[SZ_LINE];		/** application version		    */
+    char      description[SZ_NAME];	/** application description	    */
+
+    char      errortxt[SZ_LINE];	/** last msh error string	    */
+
+    appMD     meta;			/** metadata			    */
+
+    pthread_t svrThread;		/** server thread number	    */
+
+                    /** default user handler	    */
+    int   (*defaultUserFunc)(char *sender, char *msgid, Map map);
+
+    Subs      subs[MAX_SUBS];		/** message subscriptions	    */
+    int	      nsubs;			/** number of subscriptions	    */
+
+    Client    clients[MAX_CLIENTS];	/** samp clients		    */
+    int	      nclients;			/** number of samp clients	    */
+
+    int	      serverTid;		/** samp server threadId	    */
+    int	      serverPort;		/** samp server port		    */
+
+    Hub      *hub;			/** Hub connection 		    */
+    handle_t  hubHandle;		/** Hub handle alias 		    */
+    int       hubThreadID;		/** Hub thread id 		    */
+
+    int	      active;			/** is interface active		    */
+    int	      mapClients;		/** map other clients		    */
+    int	      msgMode;			/** (a)synch message mode	    */
+    int	      handlerMode;		/** CBR / CBV for user handlers     */
+
+    FILE     *logfd;			/** log file descriptor		    */
+    int	      debug;			/** debug flag			    */
+    int	      verbose;			/** verbose flag		    */
+    int	      trace;			/** trace flag			    */
+} Samp, *SampP;
+
+
+#define MSG_SYNC	0
+#define MSG_ASYNC	1
+#define MSG_NOTIFY	2
+#define DEF_CALLMODE	MSG_ASYNC
+
+
+
+/**
+ *   Prototype declarations.
+ */
+
+/******************************************************************************
+ **  Public Interface Methods
+ *****************************************************************************/
+
+/*  samp.c -- Methods called by user apps to initialize the interface.
+ */
+extern "C"
+{
+handle_t  sampInit (char* appName, char* description);
+
+void 	  samp_Metadata (handle_t handle, String field, String value);
+void 	  samp_Subscribe (handle_t handle, String mtype, void *func);
+void 	  samp_Unsubscribe (handle_t handle, String mtype);
+int  	  sampStartup (handle_t handle);
+int  	  sampShutdown (handle_t handle);
+int  	  sampDebug (handle_t handle, int value);
+int  	  sampVerbose (handle_t handle, int value);
+void 	  sampClose (handle_t handle);
+int 	  samp_hubActive (handle_t handle);
+int 	  samp_setOpt (handle_t handle, char *opt, int value);
+
+void 	  samp_setSyncMode (handle_t handle);
+void 	  samp_setASyncMode (handle_t handle);
+void 	  samp_setNotifyMode (handle_t handle);
+void 	  samp_setMsgMode (handle_t handle, int mode);
+void 	  samp_setCallByRef (handle_t handle);
+void 	  samp_setCallMode (handle_t handle, int mode);
+
+void 	  samp_setReplyCallback (handle_t handle, int *func);
+void 	  samp_setResponseCallback (handle_t handle, int *func);
+void      samp_setTimeout (handle_t handle, int timeout);
+void      samp_setAppName (handle_t handle, String name);
+void      samp_setAppVersion (handle_t handle, String version);
+
+void 	  samp_defaultReplyHandler (handle_t handle);
+void 	  samp_deaultfResponseHandler (handle_t handle);
+int 	  samp_replyStatus (handle_t handle);
+
+int  	  samp_mapClients (handle_t handle);
+int  	  samp_listClients (handle_t handle);
+char  	 *samp_getClients (handle_t handle);
+int  	  samp_addClient (handle_t handle, String name, String id);
+int  	  samp_removeClient (handle_t handle, String id);
+
+Map	  samp_getOKMap (void);
+Map	  samp_getNullMap (void);
+
+
+/*  sampCommands.c -- Methods called to send messages to the Hub.
+ */
+int 	samp_Register (handle_t handle);
+int 	samp_UnRegister (handle_t handle);
+int 	samp_DeclareMetadata (handle_t handle);
+int 	samp_Ping (handle_t handle, String appName);
+Map 	samp_GetMetadata (handle_t handle, String pubId);
+int 	samp_DeclareSubscriptions (handle_t handle);
+Map     samp_GetSubscriptions (handle_t handle, String pubId);
+List 	samp_GetRegisteredClients (handle_t handle);
+List 	samp_GetSubscribedClients (handle_t handle, String mtype);
+
+
+/*  sampMType.c -- Methods called to send messages to other apps.
+ */
+int 	samp_tableLoadVOTable (handle_t handle, String recip, String url,
+        String tableId, String name);
+int 	samp_tableLoadFITS (handle_t handle, String recip, String url,
+        String tableId, String name);
+int 	samp_imageLoadFITS (handle_t handle, String recip, String url,
+        String imageId, String name);
+
+int 	samp_tableHighlightRow (handle_t handle, String recip, String tableId,
+        String url, int row);
+int 	samp_tableSelectRowList (handle_t handle, String recip, String tableId,
+        String url, int rows[], int nrows);
+int 	samp_coordPointAtSky (handle_t handle, String recip,
+        float ra, float dec);
+int 	samp_specLoadSSAGeneric (handle_t handle, String recip, String url,
+        Map meta, String spectrumId, String name);
+
+int     samp_cmdExec (handle_t handle, String recip, String cmd);
+char   *samp_envGet (handle_t handle, String recip, String name);
+int 	samp_envSet (handle_t handle, String recip, String name, String value);
+char   *samp_paramGet (handle_t handle, String recip, String name);
+int 	samp_paramSet(handle_t handle, String recip, String name, String value);
+int 	samp_bibLoad (handle_t handle, String recip, String bibcode);
+int 	samp_resourceLoad (handle_t handle, String recip, String type,
+        String name, Map resMap);
+
+int 	samp_sendGeneric (handle_t handle, String recip, String mtype,
+        String args[]);
+int 	samp_sendMsg (handle_t handle, String recip, Map msg);
+
+
+
+/*  sampClient.c -- Low-level methods to send messages.
+ */
+void    samp_notify (handle_t handle, String recipId, Map msg);
+List    samp_notifyAll (handle_t handle, Map msg);
+String  samp_call (handle_t handle, String recipId, String tag, Map msg);
+Map     samp_callAll (handle_t handle, String msg_tag, Map msg);
+Map     samp_callAndWait (handle_t handle, String recipId, String msg_tag,
+                Map msg);
+int  	samp_Reply (handle_t handle, String msg_id, Map resp);
+
+String 	samp_clientName (handle_t handle, String pubId);
+int	samp_setErr (handle_t handle, Map resp);
+String  samp_getErr (handle_t handle);
+
+
+/*  sampMethods.c -- SAMP methods implemented by a callable client.
+ */
+int 	samp_receiveCall (void *data);
+int 	samp_receiveNotification (void *data);
+int 	samp_receiveResponse (void *data);
+
+void	samp_setHandlerReply (Map resp);
+Map	samp_getHandlerReply (void);
+
+
+/*  sampHandlers.c -- Handlers to responses from the message.
+ */
+void 	samp_setUserHandler (handle_t handle, String mtype, void *func);
+void 	samp_setSampHandler (handle_t handle, String mtype, void *func);
+void   *samp_getUserHandler (String mtype);
+void   *samp_getSampHandler (String mtype);
+void    samp_execUserHandler (String mtype, String sender,
+        String msg_id, Map params);
+
+int     samp_genericMsgHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+
+int 	samp_PingHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_StatusHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+
+int 	samp_imLoadHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_tbLoadHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_tbLoadFITSHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_tbLoadVOTHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+
+int 	samp_tbHighlightHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_tbSelectHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_pointAtHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+
+int 	samp_specLoadHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_specSSAHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+
+int 	samp_cmdExecHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_envGetHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_envSetHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_paramGetHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_paramSetHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+
+int 	samp_bibcodeHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+
+int 	samp_resLoadHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_resConeHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_resSiapHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_resSsapHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_resTapHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+int 	samp_resVOSpaceHandler (String sender, String mtype, String msg_id,
+        Map msg_map);
+
+void    samp_printMessage (String mtype, String sender, String msg_id,
+        Map params);
+
+void    samp_printMap (String name, Map map);
+int 	samp_nullResponse (void *data);
+int 	samp_testEcho (void *data);
+
+
+
+/******************************************************************************
+ **  Internal Interface Methods
+ *****************************************************************************/
+
+/*  sampHub.c
+ */
+handle_t  samp_hubOpen (Samp *samp);
+int 	  samp_hubClose (handle_t handle);
+List 	  samp_getAvailableHubs (handle_t handle);
+char     *samp_getActiveHubName (handle_t handle);
+int 	  samp_getActiveHub (handle_t handle);
+int 	  samp_hubRunning (void);
+int 	  samp_hubInit (handle_t samp, char *appName, char *descr);
+
+int	  samp_processHubEvent (String mtype, Map params);
+int	  samp_hubEvent (String mtype);
+
+int 	  samp_hubRegister (Hub *hub);
+int 	  samp_hubUnRegister (Hub *hub);
+int 	  samp_hubSendShutdown (Hub *hub);
+int	  samp_hubSetXmlrpcCallback (Hub *hub);
+int 	  samp_hubPing (Hub *hub);
+int 	  samp_hubDeclareMetadata (Hub *hub);
+int 	  samp_hubDeclareSubscriptions (Hub *hub);
+
+
+/* sampList.c
+*/
+handle_t  samp_newList ();
+void 	  samp_freeList (List list);
+int 	  samp_listLen (List list);
+
+void 	  samp_setStringInList (List list, char *value);
+void 	  samp_setMapInList (List list, Map map);
+void 	  samp_setListInList (List list1, List list2);
+void 	  samp_setIntInList (List list, int value);
+void 	  samp_setFloatInList (List list, float value);
+
+char     *samp_getStringFromList (List list, int index);
+Map 	  samp_getMapFromList (List list, int index);
+List 	  samp_getListFromList (List list, int index);
+int 	  samp_getIntFromList (List list, int index);
+float 	  samp_getFloatFromList (List list, int index);
+
+
+/* sampMap.c
+*/
+handle_t  samp_newMap (void);
+void 	  samp_freeMap (Map map);
+
+int	  samp_getMapSize (Map map);
+char 	 *samp_getMapKey (Map map, int index);
+char 	 *samp_getMapVal (Map map, int index);
+
+void 	  samp_setStringInMap (Map map, char *key, char *value);
+void 	  samp_setMapInMap (Map map1, char *key, Map map2);
+void 	  samp_setListInMap (Map map, char *key, List list);
+void 	  samp_setIntInMap (Map map, char *key, int value);
+void 	  samp_setFloatInMap (Map map, char *key, float value);
+
+char     *samp_getStringFromMap (Map map, char *key);
+Map 	  samp_getMapFromMap (Map map, char *key);
+List 	  samp_getListFromMap (Map map, char *key);
+int 	  samp_getIntFromMap (Map map, char *key);
+float 	  samp_getFloatFromMap (Map map, char *key);
+
+
+/* sampMsg.c
+ */
+Msg 	  samp_newMsg (void);
+void 	  samp_freeMsg (Msg msg);
+void 	  samp_msgMType (Msg msg, String mtype);
+void 	  samp_msgParam (Msg msg, Param param);
+char     *samp_msgTag (void);
+
+
+/* sampParam.c
+ */
+Param 	  samp_newParam (void);
+void 	  samp_freeParam (Param param);
+Param 	  samp_paramInit (Msg msg);
+void 	  samp_addStringParam (Msg msg, char *keyw, String val);
+void 	  samp_addMapParam (Msg msg, char *keyw, Map val);
+void 	  samp_addListParam (Msg msg, char *keyw, List val);
+void 	  samp_addIntParam (Msg msg, char *keyw, int val);
+void 	  samp_addFloatParam (Msg msg, char *keyw, float val);
+int 	  samp_paramLen (Msg msg);
+
+
+/* sampLog.c
+*/
+void 	  sampLog (handle_t handle, char *format, ...);
+void 	  sampTrace (handle_t handle, char *format, ...);
+
+
+/*  sampUtil.c
+ */
+handle_t  samp_newHandle (void *ptr);
+void 	  samp_freeHandle (handle_t handle);
+
+handle_t  samp_P2H (void *ptr);
+void     *samp_H2P (handle_t handle);
+
+char     *samp_app2id (handle_t handle, char *appName);
+char     *samp_id2app (handle_t handle, char *pubId);
+
+int	  samp_serverPort (void);
+void 	  samp_printMetadata (handle_t handle, String name);
+char 	 *samp_getMetadata (handle_t handle, String name);
+
+}
diff --git a/Code/src/libsamp/xmlrpc-c/abyss.h b/Code/src/libsamp/xmlrpc-c/abyss.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ee4c282d6ebda230ab90825f841905b23aa23ac
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/abyss.h
@@ -0,0 +1,560 @@
+/*****************************************************************************
+                                 abyss.h
+******************************************************************************
+
+  This file is the interface header for the Abyss HTTP server component of
+  XML-RPC For C/C++ (Xmlrpc-c).
+
+  The Abyss component of Xmlrpc-c is based on the independently developed
+  and distributed Abyss web server package from 2001.
+
+  Copyright information is at the end of the file.
+****************************************************************************/
+
+#ifndef XMLRPC_ABYSS_H_INCLUDED
+#define XMLRPC_ABYSS_H_INCLUDED
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+#include "inttypes.h"
+
+/****************************************************************************
+  STUFF FOR THE OUTER CONTROL PROGRAM TO USE
+****************************************************************************/
+
+typedef int abyss_bool;
+
+/****************************************************************************
+  GLOBAL (STATIC) PROGRAM STUFF
+****************************************************************************/
+
+void
+AbyssInit(const char ** const errorP);
+
+void
+AbyssTerm(void);
+
+/*********************************************************************
+** MIMEType
+*********************************************************************/
+
+typedef struct MIMEType MIMEType;
+
+MIMEType *
+MIMETypeCreate(void);
+
+void
+MIMETypeDestroy(MIMEType * const MIMETypeP);
+
+void
+MIMETypeInit(void);
+
+void
+MIMETypeTerm(void);
+
+abyss_bool
+MIMETypeAdd2(MIMEType *   const MIMETypeP,
+             const char * const type,
+             const char * const ext);
+
+abyss_bool
+MIMETypeAdd(const char * const type,
+            const char * const ext);
+
+
+enum abyss_foreback {ABYSS_FOREGROUND, ABYSS_BACKGROUND};
+
+#define HAVE_CHANSWITCH
+
+typedef struct _TChanSwitch TChanSwitch;
+typedef struct _TChannel TChannel;
+typedef struct _TSocket TSocket;
+
+#ifdef WIN32
+  #include "abyss_winsock.h"
+#else
+  #include "abyss_unixsock.h"
+#endif
+
+void
+ChanSwitchInit(const char ** const errorP);
+
+void
+ChanSwitchTerm(void);
+
+/* If you're wondering where the constructors for TChanSwitch,
+   TChannel, and TSocket are: They're implementation-specific, so look
+   in abyss_unixsock.h, etc.
+*/
+
+void
+ChanSwitchDestroy(TChanSwitch * const chanSwitchP);
+
+void
+ChannelInit(const char ** const errorP);
+
+void
+ChannelTerm(void);
+
+void
+ChannelDestroy(TChannel * const channelP);
+
+void
+SocketDestroy(TSocket * const socketP);
+
+
+typedef struct {
+    /* Before Xmlrpc-c 1.04, the internal server representation,
+       struct _TServer, was exposed to users and was the only way to
+       set certain parameters of the server.  Now, use the (new)
+       ServerSet...() functions.  Use the HAVE_ macros to determine
+       which method you have to use.
+    */
+    struct _TServer * srvP;
+} TServer;
+
+typedef struct _TSession TSession;
+
+abyss_bool
+ServerCreate(TServer *       const serverP,
+             const char *    const name,
+             xmlrpc_uint16_t const port,
+             const char *    const filespath,
+             const char *    const logfilename);
+
+void
+ServerCreateSwitch(TServer *     const serverP,
+                   TChanSwitch * const chanSwitchP,
+                   const char ** const errorP);
+
+abyss_bool
+ServerCreateSocket(TServer *    const serverP,
+                   const char * const name,
+                   TOsSocket    const socketFd,
+                   const char * const filespath,
+                   const char * const logfilename);
+
+#define HAVE_SERVER_CREATE_SOCKET_2
+void
+ServerCreateSocket2(TServer *     const serverP,
+                    TSocket *     const socketP,
+                    const char ** const errorP);
+
+abyss_bool
+ServerCreateNoAccept(TServer *    const serverP,
+                     const char * const name,
+                     const char * const filespath,
+                     const char * const logfilename);
+
+void
+ServerFree(TServer * const serverP);
+
+void
+ServerSetName(TServer *    const serverP,
+              const char * const name);
+
+void
+ServerSetFilesPath(TServer *    const serverP,
+                   const char * const filesPath);
+
+void
+ServerSetLogFileName(TServer *    const serverP,
+                     const char * const logFileName);
+
+#define HAVE_SERVER_SET_KEEPALIVE_TIMEOUT 1
+void
+ServerSetKeepaliveTimeout(TServer *       const serverP,
+                          xmlrpc_uint32_t const keepaliveTimeout);
+
+#define HAVE_SERVER_SET_KEEPALIVE_MAX_CONN 1
+void
+ServerSetKeepaliveMaxConn(TServer *       const serverP,
+                          xmlrpc_uint32_t const keepaliveMaxConn);
+
+#define HAVE_SERVER_SET_TIMEOUT 1
+void
+ServerSetTimeout(TServer *       const serverP,
+                 xmlrpc_uint32_t const timeout);
+
+#define HAVE_SERVER_SET_ADVERTISE 1
+void
+ServerSetAdvertise(TServer *  const serverP,
+                   abyss_bool const advertise);
+
+#define HAVE_SERVER_SET_MIME_TYPE 1
+void
+ServerSetMimeType(TServer *  const serverP,
+                  MIMEType * const MIMETypeP);
+
+void
+ServerInit(TServer * const serverP);
+
+void
+ServerRun(TServer * const serverP);
+
+void
+ServerRunOnce(TServer * const serverP);
+
+/* ServerRunOnce2() is obsolete.  See user's guide. */
+void
+ServerRunOnce2(TServer *           const serverP,
+               enum abyss_foreback const foregroundBackground);
+
+void
+ServerRunChannel(TServer *     const serverP,
+                 TChannel *    const channelP,
+                 void *        const channelInfoP,
+                 const char ** const errorP);
+
+#define HAVE_SERVER_RUN_CONN_2
+void
+ServerRunConn2(TServer *     const serverP,
+               TSocket *     const connectedSocketP,
+               const char ** const errorP);
+
+void
+ServerRunConn(TServer * const serverP,
+              TOsSocket const connectedSocket);
+
+void
+ServerDaemonize(TServer * const serverP);
+
+void
+ServerTerminate(TServer * const serverP);
+
+void
+ServerResetTerminate(TServer * const serverP);
+
+void
+ServerUseSigchld(TServer * const serverP);
+
+#ifndef WIN32
+void
+ServerHandleSigchld(pid_t const pid);
+#endif
+
+typedef abyss_bool (*URIHandler) (TSession *); /* deprecated */
+
+struct URIHandler2;
+
+typedef void (*initHandlerFn)(struct URIHandler2 *, abyss_bool *);
+
+typedef void (*termHandlerFn)(void *);
+
+typedef void (*handleReq3Fn)(void *,
+                             TSession *,
+                             abyss_bool *);
+
+typedef void (*handleReq2Fn)(struct URIHandler2 *,
+                             TSession *,
+                             abyss_bool *);
+
+struct ServerReqHandler3 {
+    termHandlerFn term;
+    handleReq3Fn  handleReq;
+    void *        userdata;
+    size_t        handleReqStackSize; /* zero = default */
+};
+
+void
+ServerAddHandler3(TServer *                        const serverP,
+                  const struct ServerReqHandler3 * const handlerP,
+                  abyss_bool *                     const successP);
+
+typedef struct URIHandler2 {
+    initHandlerFn init;
+    termHandlerFn term;
+    handleReq2Fn  handleReq2;
+    URIHandler    handleReq1;  /* deprecated */
+    void *        userdata;
+} URIHandler2;
+
+void
+ServerAddHandler2(TServer *     const srvP,
+                  URIHandler2 * const handlerP,
+                  abyss_bool *  const successP);
+
+abyss_bool
+ServerAddHandler(TServer * const srvP,
+                 URIHandler const handler);
+
+typedef abyss_bool (*THandlerDflt) (TSession *);
+
+/* Note: 'handler' used to be URIHandler;  THandlerDflt is a newer name
+   for the same type
+*/
+
+void
+ServerDefaultHandler(TServer *    const srvP,
+                     THandlerDflt const handler);
+
+/* ConfReadServerFile() is inappropriately named; it was a mistake.
+   But then, so is having this function at all.  The config file is
+   inappropriate for an API.
+*/
+
+abyss_bool
+ConfReadServerFile(const char * const filename,
+                   TServer *    const srvP);
+
+void
+LogWrite(TServer *    const srvP,
+         const char * const c);
+
+/****************************************************************************
+  STUFF FOR HTTP REQUEST HANDLERS TO USE
+****************************************************************************/
+
+typedef enum {
+    m_unknown, m_get, m_put, m_head, m_post, m_delete, m_trace, m_options
+} TMethod;
+
+typedef struct {
+    TMethod method;
+    const char * uri;
+        /* This is NOT the URI.  It is the pathname part of the URI.
+           We really should fix that and put the pathname in another
+           member.  If the URI does not contain a pathname, this is "*".
+        */
+    const char * query;
+        /* The query part of the URI (stuff after '?').  NULL if none. */
+    const char * host;
+        /* NOT the value of the host: header.  Rather, the name of the
+           target host (could be part of the host: value; could be from the
+           URI).  No port number.  NULL if request does not specify a host
+           name.
+        */
+    const char * from;
+    const char * useragent;
+    const char * referer;
+    const char * requestline;
+    const char * user;
+        /* Requesting user (from authorization: header).  NULL if
+           request doesn't specify or handler has not authenticated it.
+        */
+    xmlrpc_uint16_t port;
+        /* The port number from the URI, or default 80 if the URI doesn't
+           specify a port.
+        */
+    abyss_bool keepalive;
+} TRequestInfo;
+
+abyss_bool
+SessionRefillBuffer(TSession * const sessionP);
+
+size_t
+SessionReadDataAvail(TSession * const sessionP);
+
+void
+SessionGetReadData(TSession *    const sessionP, 
+                   size_t        const max, 
+                   const char ** const outStartP, 
+                   size_t *      const outLenP);
+
+void
+SessionGetRequestInfo(TSession *            const sessionP,
+                      const TRequestInfo ** const requestInfoPP);
+
+void
+SessionGetChannelInfo(TSession * const sessionP,
+                      void **    const channelInfoPP);
+
+void *
+SessionGetDefaultHandlerCtx(TSession * const sessionP);
+
+char *
+RequestHeaderValue(TSession *   const sessionP,
+                   const char * const name);
+
+abyss_bool
+ResponseAddField(TSession *   const sessionP,
+                 const char * const name,
+                 const char * const value);
+
+void
+ResponseWriteStart(TSession * const sessionP);
+
+/* For backward compatibility: */
+#define ResponseWrite ResponseWriteStart
+
+abyss_bool
+ResponseWriteBody(TSession *      const sessionP,
+                  const char *    const data,
+                  xmlrpc_uint32_t const len);
+
+abyss_bool
+ResponseWriteEnd(TSession * const sessionP);
+
+abyss_bool
+ResponseChunked(TSession * const sessionP);
+
+xmlrpc_uint16_t
+ResponseStatusFromErrno(int const errnoArg);
+
+void
+ResponseStatus(TSession *      const sessionP,
+               xmlrpc_uint16_t const code);
+
+void
+ResponseStatusErrno(TSession * const sessionP);
+
+abyss_bool
+ResponseContentType(TSession *   const serverP,
+                    const char * const type);
+
+abyss_bool
+ResponseContentLength(TSession *      const sessionP,
+                      xmlrpc_uint64_t const len);
+
+void
+ResponseError2(TSession *   const sessionP,
+               const char * const explanation);
+
+void
+ResponseError(TSession * const sessionP);
+
+const char *
+MIMETypeFromExt(const char * const ext);
+
+const char *
+MIMETypeFromExt2(MIMEType *   const MIMETypeP,
+                 const char * const ext);
+
+const char *
+MIMETypeFromFileName2(MIMEType *   const MIMETypeP,
+                      const char * const fileName);
+
+const char *
+MIMETypeFromFileName(const char * const fileName);
+
+const char *
+MIMETypeGuessFromFile2(MIMEType *   const MIMETypeP,
+                       const char * const fileName);
+
+const char *
+MIMETypeGuessFromFile(const char * const filename);
+
+
+/****************************************************************************
+  STUFF THAT PROBABLY DOESN'T BELONG IN THIS FILE BECAUSE IT IS INTERNAL
+
+  Some day, we sort this out.
+****************************************************************************/
+
+
+#define CR      '\r'
+#define LF      '\n'
+#define CRLF    "\r\n"
+
+/*********************************************************************
+** Paths and so on...
+*********************************************************************/
+
+#ifdef WIN32
+#define DEFAULT_ROOT        "c:\\abyss"
+#define DEFAULT_DOCS        DEFAULT_ROOT"\\htdocs"
+#define DEFAULT_CONF_FILE   DEFAULT_ROOT"\\conf\\abyss.conf"
+#define DEFAULT_LOG_FILE    DEFAULT_ROOT"\\log\\abyss.log"
+#else
+#ifdef __rtems__
+#define DEFAULT_ROOT        "/abyss"
+#else
+#define DEFAULT_ROOT        "/usr/local/abyss"
+#endif
+#define DEFAULT_DOCS        DEFAULT_ROOT"/htdocs"
+#define DEFAULT_CONF_FILE   DEFAULT_ROOT"/conf/abyss.conf"
+#define DEFAULT_LOG_FILE    DEFAULT_ROOT"/log/abyss.log"
+#endif
+
+/*********************************************************************
+** Maximum number of simultaneous connections
+*********************************************************************/
+
+#define MAX_CONN    16
+
+/*********************************************************************
+** General purpose definitions
+*********************************************************************/
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif  /* NULL */
+
+#ifndef TRUE
+#define TRUE    1
+#endif  /* TRUE */
+
+#ifndef FALSE
+#define FALSE    0
+#endif  /* FALSE */
+
+/*********************************************************************
+** Range
+*********************************************************************/
+
+abyss_bool
+RangeDecode(char *            const str,
+            xmlrpc_uint64_t   const filesize,
+            xmlrpc_uint64_t * const start,
+            xmlrpc_uint64_t * const end);
+
+abyss_bool DateInit(void);
+
+/*********************************************************************
+** Base64
+*********************************************************************/
+
+void
+Base64Encode(const char * const chars,
+             char *       const base64);
+
+/*********************************************************************
+** Session
+*********************************************************************/
+
+abyss_bool SessionLog(TSession * const s);
+
+
+#ifdef __cplusplus
+}
+
+
+#endif
+
+/*****************************************************************************
+** Here is the copyright notice from the Abyss web server project file from
+** which this file is derived.
+**
+** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission.
+** 
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE.
+**
+******************************************************************************/
+#endif  /* _ABYSS_H_ */
diff --git a/Code/src/libsamp/xmlrpc-c/abyss_unixsock.h b/Code/src/libsamp/xmlrpc-c/abyss_unixsock.h
new file mode 100644
index 0000000000000000000000000000000000000000..023f9580ac330fe4a011bacffa6320224b274592
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/abyss_unixsock.h
@@ -0,0 +1,42 @@
+/* This is just a sub-file for abyss.h */
+
+#include <sys/socket.h>
+
+struct abyss_unix_chaninfo {
+    size_t peerAddrLen;
+    struct sockaddr peerAddr;
+};
+
+void
+ChanSwitchUnixCreate(unsigned short const portNumber,
+                     TChanSwitch ** const chanSwitchPP,
+                     const char **  const errorP);
+
+void
+ChanSwitchUnixCreateFd(int            const fd,
+                       TChanSwitch ** const chanSwitchPP,
+                       const char **  const errorP);
+
+void
+ChannelUnixCreateFd(int                           const fd,
+                    TChannel **                   const channelPP,
+                    struct abyss_unix_chaninfo ** const channelInfoPP,
+                    const char **                 const errorP);
+
+void
+ChannelUnixGetPeerName(TChannel *         const channelP,
+                       struct sockaddr ** const sockaddrPP,
+                       size_t  *          const sockaddrLenP,
+                       const char **      const errorP);
+
+void
+SocketUnixCreateFd(int        const fd,
+                   TSocket ** const socketPP);
+
+typedef int TOsSocket;
+    /* TOsSocket is the type of a conventional socket offered by our OS.
+       This is for backward compatibility; everyone should use TChanSwitch
+       and TChannel instead today.
+    */
+
+
diff --git a/Code/src/libsamp/xmlrpc-c/abyss_winsock.h b/Code/src/libsamp/xmlrpc-c/abyss_winsock.h
new file mode 100644
index 0000000000000000000000000000000000000000..f376446b8fcc775773b6e0c1d77014fcafb877b7
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/abyss_winsock.h
@@ -0,0 +1,27 @@
+/* This is just a sub-file for abyss.h */
+
+#include <winsock.h>
+
+struct abyss_win_chaninfo {
+    size_t peerAddrLen;
+    struct sockaddr peerAddr;
+};
+
+
+void
+ChanSwitchWinCreate(unsigned short const portNumber,
+                    TChanSwitch ** const chanSwitchPP,
+                    const char **  const errorP);
+
+void
+ChanSwitchWinCreateWinsock(SOCKET         const winsock,
+                           TChanSwitch ** const chanSwitchPP,
+                           const char **  const errorP);
+
+void
+ChannelWinCreateWinsock(SOCKET                       const fd,
+                        TChannel **                  const channelPP,
+                        struct abyss_win_chaninfo ** const channelInfoPP,
+                        const char **                const errorP);
+
+typedef SOCKET TOsSocket;
diff --git a/Code/src/libsamp/xmlrpc-c/base.h b/Code/src/libsamp/xmlrpc-c/base.h
new file mode 100644
index 0000000000000000000000000000000000000000..7812ca82d07d54193f091e2f557ff6d41846246a
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/base.h
@@ -0,0 +1,757 @@
+/* Copyright and license information is at the end of the file */
+
+#ifndef XMLRPC_H_INCLUDED
+#define XMLRPC_H_INCLUDED
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <time.h>
+#include "util.h"
+#include "config.h"
+  /* Defines XMLRPC_HAVE_WCHAR, XMLRPC_INT64, XMLRPC_HAVE_TIMEVAL */
+
+#if XMLRPC_HAVE_WCHAR
+#include <wchar.h>
+#endif
+
+#if XMLRPC_HAVE_TIMEVAL
+#include <sys/time.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*=========================================================================
+**  Version of libxmlrpc
+**=======================================================================*/
+extern unsigned int const xmlrpc_version_major;
+extern unsigned int const xmlrpc_version_minor;
+extern unsigned int const xmlrpc_version_point;
+
+/*=========================================================================
+**  C types equivalent to XML-RPC types
+**=======================================================================*/
+
+/*  We define names for these types, because they may change from platform
+    to platform.
+*/
+
+typedef signed int xmlrpc_int;  
+    /* An integer of the type defined by XML-RPC <int>; i.e. 32 bit */
+typedef XMLRPC_INT32 xmlrpc_int32;
+    /* An integer of the type defined by XML-RPC <i4>; i.e. 32 bit */
+typedef XMLRPC_INT64 xmlrpc_int64;
+    /* An integer of the type defined by "XML-RPC" <i8>; i.e. 64 bit */
+typedef int xmlrpc_bool;
+    /* A boolean (of the type defined by XML-RPC <boolean>, but there's
+       really only one kind)
+    */
+typedef double xmlrpc_double;
+    /* A double precision floating point number as defined by
+       XML-RPC <float>.  But the C "double" type is universally the same,
+       so it's probably clearer just to use that.  This typedef is here 
+       for mathematical completeness.
+    */
+
+/* xmlrpc_socket is just for backward compatibility, in case someone decided
+   to use this in user code.  New code should use the native type for a
+   socket (e.g. int or SOCKET).  (We stopped using this because for winsock
+   users, we would have to #include <winsock.h> in every file that
+   #includes <xmlrpc-c/base.h> and we don't want that).
+*/
+typedef int xmlrpc_socket;
+
+#define XMLRPC_INT32_MAX 0x7fffffff
+#define XMLRPC_INT32_MIN (-XMLRPC_INT32_MAX - 1)
+
+#define XMLRPC_INT64_MAX 0x7fffffffffffffffll
+#define XMLRPC_INT64_MIN (-XMLRPC_INT64_MAX - 1)
+
+
+/*=========================================================================
+**  xmlrpc_value
+**=========================================================================
+**  An XML-RPC value (of any type).
+*/
+
+typedef enum {
+    XMLRPC_TYPE_INT      =  0,
+    XMLRPC_TYPE_BOOL     =  1,
+    XMLRPC_TYPE_DOUBLE   =  2,
+    XMLRPC_TYPE_DATETIME =  3,
+    XMLRPC_TYPE_STRING   =  4,
+    XMLRPC_TYPE_BASE64   =  5,
+    XMLRPC_TYPE_ARRAY    =  6,
+    XMLRPC_TYPE_STRUCT   =  7,
+    XMLRPC_TYPE_C_PTR    =  8,
+    XMLRPC_TYPE_NIL      =  9,
+    XMLRPC_TYPE_I8       = 10,
+    XMLRPC_TYPE_DEAD     = 0xDEAD
+} xmlrpc_type;
+
+#define XMLRPC_HAVE_I8 1
+
+/* These are *always* allocated on the heap. No exceptions. */
+typedef struct _xmlrpc_value xmlrpc_value;
+
+const char *
+xmlrpc_type_name(xmlrpc_type const type);
+
+void
+xmlrpc_abort_if_array_bad(xmlrpc_value * const arrayP);
+
+#define XMLRPC_ASSERT_ARRAY_OK(val) \
+    xmlrpc_abort_if_array_bad(val)
+
+/* Increment the reference count of an xmlrpc_value. */
+extern void xmlrpc_INCREF (xmlrpc_value* const value);
+
+/* Decrement the reference count of an xmlrpc_value. If there
+** are no more references, free it. */
+extern void xmlrpc_DECREF (xmlrpc_value* const value);
+
+/* Get the type of an XML-RPC value. */
+extern xmlrpc_type xmlrpc_value_type (xmlrpc_value* const value);
+
+xmlrpc_value *
+xmlrpc_int_new(xmlrpc_env * const envP,
+               int          const intValue);
+
+xmlrpc_value *
+xmlrpc_i8_new(xmlrpc_env * const envP, 
+              xmlrpc_int64 const value);
+
+void 
+xmlrpc_read_int(xmlrpc_env *         const envP,
+                const xmlrpc_value * const valueP,
+                int *                const intValueP);
+
+xmlrpc_value *
+xmlrpc_bool_new(xmlrpc_env * const envP,
+                xmlrpc_bool  const boolValue);
+
+void
+xmlrpc_read_bool(xmlrpc_env *         const envP,
+                 const xmlrpc_value * const valueP,
+                 xmlrpc_bool *        const boolValueP);
+
+xmlrpc_value *
+xmlrpc_double_new(xmlrpc_env * const envP,
+                  double       const doubleValue);
+
+void
+xmlrpc_read_double(xmlrpc_env *         const envP,
+                   const xmlrpc_value * const valueP,
+                   xmlrpc_double *      const doubleValueP);
+
+xmlrpc_value *
+xmlrpc_datetime_new_str(xmlrpc_env * const envP,
+                        const char * const value);
+
+xmlrpc_value *
+xmlrpc_datetime_new_sec(xmlrpc_env * const envP, 
+                        time_t       const value);
+
+xmlrpc_value*
+xmlrpc_datetime_new_usec(xmlrpc_env * const envP,
+                         time_t       const secs,
+                         unsigned int const usecs);
+
+#if XMLRPC_HAVE_TIMEVAL
+xmlrpc_value *
+xmlrpc_datetime_new_timeval(xmlrpc_env *   const envP, 
+                            struct timeval const value);
+#endif
+
+#if XMLRPC_HAVE_TIMESPEC
+xmlrpc_value *
+xmlrpc_datetime_new_timespec(xmlrpc_env *    const envP, 
+                             struct timespec const value);
+#endif
+
+void
+xmlrpc_read_datetime_str(xmlrpc_env *         const envP,
+                         const xmlrpc_value * const valueP,
+                         const char **        const stringValueP);
+
+void
+xmlrpc_read_datetime_sec(xmlrpc_env *         const envP,
+                         const xmlrpc_value * const valueP,
+                         time_t *             const timeValueP);
+
+void
+xmlrpc_read_datetime_usec(xmlrpc_env *         const envP,
+                          const xmlrpc_value * const valueP,
+                          time_t *             const secsP,
+                          unsigned int *       const usecsP);
+
+#if XMLRPC_HAVE_TIMEVAL
+void
+xmlrpc_read_datetime_timeval(xmlrpc_env *         const envP,
+                             const xmlrpc_value * const valueP,
+                             struct timeval *     const timeValueP);
+#endif
+
+#if XMLRPC_HAVE_TIMESPEC
+void
+xmlrpc_read_datetime_timespec(xmlrpc_env *         const envP,
+                              const xmlrpc_value * const valueP,
+                              struct timespec *    const timeValueP);
+#endif
+
+xmlrpc_value *
+xmlrpc_string_new(xmlrpc_env * const envP,
+                  const char * const stringValue);
+
+xmlrpc_value *
+xmlrpc_string_new_lp(xmlrpc_env * const envP, 
+                     size_t       const length,
+                     const char * const stringValue);
+
+xmlrpc_value *
+xmlrpc_string_new_va(xmlrpc_env * const envP,
+                     const char * const format,
+                     va_list            args);
+
+xmlrpc_value *
+xmlrpc_string_new_f(xmlrpc_env * const envP,
+                    const char * const format,
+                    ...);
+
+xmlrpc_value *
+xmlrpc_string_new_lp_cr(xmlrpc_env * const envP, 
+                        size_t       const length,
+                        const char * const value);
+
+xmlrpc_value *
+xmlrpc_string_new_cr(xmlrpc_env * const envP,
+                     const char * const value);
+
+void
+xmlrpc_read_string(xmlrpc_env *         const envP,
+                   const xmlrpc_value * const valueP,
+                   const char **        const stringValueP);
+
+
+void
+xmlrpc_read_string_crlf(xmlrpc_env *         const envP,
+                        const xmlrpc_value * const valueP,
+                        const char **        const stringValueP);
+
+void
+xmlrpc_read_string_lp_crlf(xmlrpc_env *         const envP,
+                           const xmlrpc_value * const valueP,
+                           size_t *             const lengthP,
+                           const char **        const stringValueP);
+
+void
+xmlrpc_read_string_lp(xmlrpc_env *         const envP,
+                      const xmlrpc_value * const valueP,
+                      size_t *             const lengthP,
+                      const char **        const stringValueP);
+
+#if XMLRPC_HAVE_WCHAR
+xmlrpc_value *
+xmlrpc_string_w_new(xmlrpc_env *    const envP,
+                    const wchar_t * const stringValue);
+
+xmlrpc_value *
+xmlrpc_string_w_new_lp(xmlrpc_env *    const envP, 
+                       size_t          const length,
+                       const wchar_t * const stringValue);
+
+void
+xmlrpc_read_string_w(xmlrpc_env *     const envP,
+                     xmlrpc_value *   const valueP,
+                     const wchar_t ** const stringValueP);
+
+void
+xmlrpc_read_string_w_crlf(xmlrpc_env *     const envP,
+                          xmlrpc_value *   const valueP,
+                          const wchar_t ** const stringValueP);
+
+void
+xmlrpc_read_string_w_lp(xmlrpc_env *     const envP,
+                        xmlrpc_value *   const valueP,
+                        size_t *         const lengthP,
+                        const wchar_t ** const stringValueP);
+
+void
+xmlrpc_read_string_w_lp_crlf(xmlrpc_env *     const envP,
+                             xmlrpc_value *   const valueP,
+                             size_t *         const lengthP,
+                             const wchar_t ** const stringValueP);
+
+xmlrpc_value *
+xmlrpc_string_w_new_lp_cr(xmlrpc_env *    const envP, 
+                          size_t          const length,
+                          const wchar_t * const value);
+
+xmlrpc_value *
+xmlrpc_string_w_new_cr(xmlrpc_env *    const envP,
+                       const wchar_t * const value);
+
+#endif /* XMLRPC_HAVE_WCHAR */
+
+xmlrpc_value *
+xmlrpc_base64_new(xmlrpc_env *          const envP, 
+                  size_t                const length,
+                  const unsigned char * const value);
+
+void
+xmlrpc_read_base64(xmlrpc_env *           const envP,
+                   const xmlrpc_value *   const valueP,
+                   size_t *               const lengthP,
+                   const unsigned char ** const bytestringValueP);
+
+void
+xmlrpc_read_base64_size(xmlrpc_env *           const envP,
+                        const xmlrpc_value *   const valueP,
+                        size_t *               const lengthP);
+
+xmlrpc_value *
+xmlrpc_array_new(xmlrpc_env * const envP);
+
+/* Return the number of elements in an XML-RPC array.
+** Sets XMLRPC_TYPE_ERROR if 'array' is not an array. */
+int 
+xmlrpc_array_size(xmlrpc_env *         const env, 
+                  const xmlrpc_value * const array);
+
+/* Append an item to an XML-RPC array.
+** Sets XMLRPC_TYPE_ERROR if 'array' is not an array. */
+extern void
+xmlrpc_array_append_item (xmlrpc_env   * const envP,
+                          xmlrpc_value * const arrayP,
+                          xmlrpc_value * const valueP);
+
+void
+xmlrpc_array_read_item(xmlrpc_env *         const envP,
+                       const xmlrpc_value * const arrayP,
+                       unsigned int         const index,
+                       xmlrpc_value **      const valuePP);
+
+/* Deprecated.  Use xmlrpc_array_read_item() instead.
+
+   Get an item from an XML-RPC array.
+   Does not increment the reference count of the returned value.
+   Sets XMLRPC_TYPE_ERROR if 'array' is not an array.
+   Sets XMLRPC_INDEX_ERROR if 'index' is out of bounds.
+*/
+xmlrpc_value * 
+xmlrpc_array_get_item(xmlrpc_env *         const envP,
+                      const xmlrpc_value * const arrayP,
+                      int                  const index);
+
+/* Not implemented--we don't need it yet.
+extern 
+int xmlrpc_array_set_item (xmlrpc_env* env,
+xmlrpc_value* array,
+int index,
+                                  xmlrpc_value* value);
+*/
+
+xmlrpc_value *
+xmlrpc_struct_new(xmlrpc_env * const env);
+
+/* Return the number of key/value pairs in a struct.
+** Sets XMLRPC_TYPE_ERROR if 'strct' is not a struct. */
+int
+xmlrpc_struct_size (xmlrpc_env   * env, 
+                    xmlrpc_value * strct);
+
+/* Returns true iff 'strct' contains 'key'.
+** Sets XMLRPC_TYPE_ERROR if 'strct' is not a struct. */
+int 
+xmlrpc_struct_has_key(xmlrpc_env *   const envP,
+                      xmlrpc_value * const strctP,
+                      const char *   const key);
+
+/* The same as the above, but the key may contain zero bytes.
+   Deprecated.  xmlrpc_struct_get_value_v() is more general, and this
+   case is not common enough to warrant a shortcut.
+*/
+int 
+xmlrpc_struct_has_key_n(xmlrpc_env   * const envP,
+                        xmlrpc_value * const strctP,
+                        const char *   const key, 
+                        size_t         const key_len);
+
+#if 0
+/* Not implemented yet, but needed for completeness. */
+int
+xmlrpc_struct_has_key_v(xmlrpc_env *   env, 
+                        xmlrpc_value * strct,
+                        xmlrpc_value * const keyval);
+#endif
+
+
+void
+xmlrpc_struct_find_value(xmlrpc_env *    const envP,
+                         xmlrpc_value *  const structP,
+                         const char *    const key,
+                         xmlrpc_value ** const valuePP);
+
+
+void
+xmlrpc_struct_find_value_v(xmlrpc_env *    const envP,
+                           xmlrpc_value *  const structP,
+                           xmlrpc_value *  const keyP,
+                           xmlrpc_value ** const valuePP);
+
+void
+xmlrpc_struct_read_value(xmlrpc_env *    const envP,
+                         xmlrpc_value *  const structP,
+                         const char *    const key,
+                         xmlrpc_value ** const valuePP);
+
+void
+xmlrpc_struct_read_value_v(xmlrpc_env *    const envP,
+                           xmlrpc_value *  const structP,
+                           xmlrpc_value *  const keyP,
+                           xmlrpc_value ** const valuePP);
+
+/* The "get_value" functions are deprecated.  Use the "find_value"
+   and "read_value" functions instead.
+*/
+xmlrpc_value * 
+xmlrpc_struct_get_value(xmlrpc_env *   const envP,
+                        xmlrpc_value * const strctP,
+                        const char *   const key);
+
+/* The same as above, but the key may contain zero bytes. 
+   Deprecated.  xmlrpc_struct_get_value_v() is more general, and this
+   case is not common enough to warrant a shortcut.
+*/
+xmlrpc_value * 
+xmlrpc_struct_get_value_n(xmlrpc_env *   const envP,
+                          xmlrpc_value * const strctP,
+                          const char *   const key, 
+                          size_t         const key_len);
+
+/* Set the value associated with 'key' in 'strct' to 'value'.
+   Sets XMLRPC_TYPE_ERROR if 'strct' is not a struct. 
+*/
+void 
+xmlrpc_struct_set_value(xmlrpc_env *   const env,
+                        xmlrpc_value * const strct,
+                        const char *   const key,
+                        xmlrpc_value * const value);
+
+/* The same as above, but the key may contain zero bytes.  Deprecated.
+   The general way to set a structure value is xmlrpc_struct_set_value_v(),
+   and this case is not common enough to deserve a shortcut.
+*/
+void 
+xmlrpc_struct_set_value_n(xmlrpc_env *    const env,
+                          xmlrpc_value *  const strct,
+                          const char *    const key, 
+                          size_t          const key_len,
+                          xmlrpc_value *  const value);
+
+/* The same as above, but the key must be an XML-RPC string.
+** Fails with XMLRPC_TYPE_ERROR if 'keyval' is not a string. */
+void 
+xmlrpc_struct_set_value_v(xmlrpc_env *   const env,
+                          xmlrpc_value * const strct,
+                          xmlrpc_value * const keyval,
+                          xmlrpc_value * const value);
+
+/* Given a zero-based index, return the matching key and value. This
+** is normally used in conjunction with xmlrpc_struct_size.
+** Fails with XMLRPC_TYPE_ERROR if 'struct' is not a struct.
+** Fails with XMLRPC_INDEX_ERROR if 'index' is out of bounds. */
+
+void 
+xmlrpc_struct_read_member(xmlrpc_env *    const envP,
+                          xmlrpc_value *  const structP,
+                          unsigned int    const index,
+                          xmlrpc_value ** const keyvalP,
+                          xmlrpc_value ** const valueP);
+
+/* The same as above, but does not increment the reference count of the
+   two values it returns, and return NULL for both if it fails, and
+   takes a signed integer for the index (but fails if it is negative).
+
+   Deprecated.  Use xmlrpc_struct_read_member() instead.
+*/
+void
+xmlrpc_struct_get_key_and_value(xmlrpc_env *    const env,
+                                xmlrpc_value *  const strct,
+                                int             const index,
+                                xmlrpc_value ** const out_keyval,
+                                xmlrpc_value ** const out_value);
+
+void
+xmlrpc_read_cptr(xmlrpc_env *         const envP,
+                 const xmlrpc_value * const valueP,
+                 void **              const ptrValueP);
+
+void
+xmlrpc_read_nil(xmlrpc_env *   const envP,
+                xmlrpc_value * const valueP);
+                
+
+void 
+xmlrpc_read_i8(xmlrpc_env *         const envP,
+               const xmlrpc_value * const valueP,
+               xmlrpc_int64 *       const intValueP);
+
+
+xmlrpc_value *
+xmlrpc_cptr_new(xmlrpc_env * const envP,
+                void *       const value);
+
+xmlrpc_value *
+xmlrpc_nil_new(xmlrpc_env * const envP);
+
+
+/* Build an xmlrpc_value from a format string. */
+
+xmlrpc_value * 
+xmlrpc_build_value(xmlrpc_env * const env,
+                   const char * const format, 
+                   ...);
+
+/* The same as the above, but using a va_list and more general */
+void
+xmlrpc_build_value_va(xmlrpc_env *    const env,
+                      const char *    const format,
+                      va_list         const args,
+                      xmlrpc_value ** const valPP,
+                      const char **   const tailP);
+
+void 
+xmlrpc_decompose_value(xmlrpc_env *   const envP,
+                       xmlrpc_value * const value,
+                       const char *   const format, 
+                       ...);
+
+void 
+xmlrpc_decompose_value_va(xmlrpc_env *   const envP,
+                          xmlrpc_value * const value,
+                          const char *   const format,
+                          va_list        const args);
+
+/* xmlrpc_parse_value... is the same as xmlrpc_decompose_value... except
+   that it doesn't do proper memory management -- it returns xmlrpc_value's
+   without incrementing the reference count and returns pointers to data
+   inside an xmlrpc_value structure.
+
+   These are deprecated.  Use xmlrpc_decompose_value... instead.
+*/
+void 
+xmlrpc_parse_value(xmlrpc_env *   const envP,
+                   xmlrpc_value * const value,
+                   const char *   const format, 
+                   ...);
+
+/* The same as the above, but using a va_list. */
+void 
+xmlrpc_parse_value_va(xmlrpc_env *   const envP,
+                      xmlrpc_value * const value,
+                      const char *   const format,
+                      va_list        const args);
+
+/*=========================================================================
+**  Encoding XML
+**=======================================================================*/
+
+typedef enum xmlrpc_dialect {
+    xmlrpc_dialect_i8,
+    xmlrpc_dialect_apache
+} xmlrpc_dialect;
+
+void 
+xmlrpc_serialize_value2(xmlrpc_env *       const envP,
+                        xmlrpc_mem_block * const outputP,
+                        xmlrpc_value *     const valueP,
+                        xmlrpc_dialect     const dialect);
+
+void
+xmlrpc_serialize_value(xmlrpc_env *       const envP,
+                       xmlrpc_mem_block * const outputP,
+                       xmlrpc_value *     const valueP);
+
+void 
+xmlrpc_serialize_params2(xmlrpc_env *       const envP,
+                         xmlrpc_mem_block * const outputP,
+                         xmlrpc_value *     const paramArrayP,
+                         xmlrpc_dialect     const dialect);
+
+void
+xmlrpc_serialize_params(xmlrpc_env *       const envP,
+                        xmlrpc_mem_block * const outputP,
+                        xmlrpc_value *     const paramArrayP);
+
+void 
+xmlrpc_serialize_call2(xmlrpc_env *       const envP,
+                       xmlrpc_mem_block * const outputP,
+                       const char *       const methodName,
+                       xmlrpc_value *     const paramArrayP,
+                       xmlrpc_dialect     const dialect);
+
+void 
+xmlrpc_serialize_call(xmlrpc_env *       const envP,
+                      xmlrpc_mem_block * const outputP,
+                      const char *       const methodName,
+                      xmlrpc_value *     const paramArrayP);
+
+void 
+xmlrpc_serialize_response2(xmlrpc_env *       const envP,
+                           xmlrpc_mem_block * const outputP,
+                           xmlrpc_value *     const valueP,
+                           xmlrpc_dialect     const dialect);
+
+void
+xmlrpc_serialize_response(xmlrpc_env *       const envP,
+                          xmlrpc_mem_block * const outputP,
+                          xmlrpc_value *     const valueP);
+
+void
+xmlrpc_serialize_fault(xmlrpc_env *       const envP,
+                       xmlrpc_mem_block * const outputP,
+                       const xmlrpc_env * const faultP);
+
+
+/*=========================================================================
+**  Decoding XML
+**=======================================================================*/
+
+/* Parse an XML-RPC call. If an error occurs, set a fault and set
+** the output variables to NULL.
+** The caller is responsible for calling free(*out_method_name) and
+** xmlrpc_DECREF(*out_param_array). */
+void 
+xmlrpc_parse_call(xmlrpc_env *    const envP,
+                  const char *    const xml_data,
+                  size_t          const xml_len,
+                  const char **   const out_method_name,
+                  xmlrpc_value ** const out_param_array);
+
+void
+xmlrpc_parse_response2(xmlrpc_env *    const envP,
+                       const char *    const xmlData,
+                       size_t          const xmlDataLen,
+                       xmlrpc_value ** const resultPP,
+                       int *           const faultCodeP,
+                       const char **   const faultStringP);
+
+
+/* xmlrpc_parse_response() is for backward compatibility */
+
+xmlrpc_value *
+xmlrpc_parse_response(xmlrpc_env * const envP, 
+                      const char * const xmlData, 
+                      size_t       const xmlDataLen);
+
+
+/*=========================================================================
+**  XML-RPC Base64 Utilities
+**=========================================================================
+**  Here are some lightweight utilities which can be used to encode and
+**  decode Base64 data. These are exported mainly for testing purposes.
+*/
+
+/* This routine inserts newlines every 76 characters, as required by the
+** Base64 specification. */
+xmlrpc_mem_block *
+xmlrpc_base64_encode(xmlrpc_env *    env,
+                     unsigned char * bin_data,
+                     size_t          bin_len);
+
+/* This routine encodes everything in one line. This is needed for HTTP
+** authentication and similar tasks. */
+xmlrpc_mem_block *
+xmlrpc_base64_encode_without_newlines(xmlrpc_env *    env,
+                                      unsigned char * bin_data,
+                                      size_t          bin_len);
+
+/* This decodes Base64 data with or without newlines. */
+extern xmlrpc_mem_block *
+xmlrpc_base64_decode(xmlrpc_env * const envP,
+                     const char * const ascii_data,
+                     size_t       const ascii_len);
+
+
+/*=========================================================================
+**  Authorization Cookie Handling
+**=========================================================================
+**  Routines to get and set values for authorizing via authorization
+**  cookies. Both the client and server use HTTP_COOKIE_AUTH to store
+**  the representation of the authorization value, which is actually
+**  just a base64 hash of username:password. (This entire method is
+**  a cookie replacement of basic authentication.)
+**/
+
+extern void xmlrpc_authcookie_set(xmlrpc_env * const env,
+                                  const char * const username,
+                                  const char * const password);
+
+char *xmlrpc_authcookie(void);
+
+/*=========================================================================
+   Resource Limits
+
+   Ideally, there would be enough resource limits to ensure that
+   XML-RPC partners cannot cause libxmlrpc objects and routines to use
+   more resource than is available for them (either by accident or
+   malice).  We have a long way to go to get there.
+   
+=========================================================================*/
+/* These functions are _not_ re-entrant and the limits are per-process
+   (i.e. their values live in static global variables).
+*/
+
+/* Limit IDs. There will be more of these as time goes on. */
+#define XMLRPC_NESTING_LIMIT_ID   (0)
+#define XMLRPC_XML_SIZE_LIMIT_ID  (1)
+#define XMLRPC_LAST_LIMIT_ID      (XMLRPC_XML_SIZE_LIMIT_ID)
+
+/* By default, deserialized data may be no more than 64 levels deep. */
+#define XMLRPC_NESTING_LIMIT_DEFAULT  (64)
+
+/* By default, XML data from the network may be no larger than 512K.
+** Some client and server modules may fail to enforce this properly. */
+#define XMLRPC_XML_SIZE_LIMIT_DEFAULT (512*1024)
+
+/* Set a specific limit to the specified value. */
+extern void xmlrpc_limit_set (int const limit_id, size_t const value);
+
+/* Get the value of a specified limit. */
+extern size_t xmlrpc_limit_get (int const limit_id);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission. 
+**  
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE. */
+
+#endif
+
diff --git a/Code/src/libsamp/xmlrpc-c/c_util.h b/Code/src/libsamp/xmlrpc-c/c_util.h
new file mode 100644
index 0000000000000000000000000000000000000000..a1ede08a066a7437c11148fe581472f0cdcca895
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/c_util.h
@@ -0,0 +1,20 @@
+#ifndef XMLRPC_C_C_UTIL_H_INCLUDED
+#define XMLRPC_C_C_UTIL_H_INCLUDED
+
+/* C language stuff.  Doesn't involve any libraries that aren't part of
+   the compiler.
+*/
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+/* GNU_PRINTF_ATTR lets the GNU compiler check printf-type
+   calls to be sure the arguments match the format string, thus preventing
+   runtime segmentation faults and incorrect messages.
+*/
+#ifdef __GNUC__
+#define GNU_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
+#else
+#define GNU_PRINTF_ATTR(a,b)
+#endif
+
+#endif
diff --git a/Code/src/libsamp/xmlrpc-c/client.h b/Code/src/libsamp/xmlrpc-c/client.h
new file mode 100644
index 0000000000000000000000000000000000000000..1608063101554ba520af0bf9bfcfdf5b66e57958
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/client.h
@@ -0,0 +1,316 @@
+/*============================================================================
+                         xmlrpc_client.h
+==============================================================================
+  This header file defines the interface between xmlrpc.c and its users,
+  related to clients.
+
+  Copyright information is at the end of the file.
+============================================================================*/
+
+#ifndef  XMLRPC_CLIENT_H_INCLUDED
+#define  XMLRPC_CLIENT_H_INCLUDED
+
+#include "base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct xmlrpc_client;
+struct xmlrpc_client_transport;
+struct xmlrpc_client_transport_ops;
+#ifndef __cplusplus
+typedef struct xmlrpc_client xmlrpc_client;
+typedef struct xmlrpc_client_transport xmlrpc_client_transport;
+typedef struct xmlrpc_client_transport_ops xmlrpc_client_transport_ops;
+#endif
+
+/* libxmlrpc_client typically does _not_ actually include all of the
+   XML transports declared here by xmlrpc_*_transport_ops.
+
+   Use 'xmlrpc-c-config --features' to determine which features are
+   installed.
+*/
+
+/* Before Xmlrpc-c 1.13 (December 2007), we declared struct
+   xmlrpc_xportparms, as a sort of "base class."  The struct was never
+   complete -- you just cast pointer to it it to pointers to other
+   types.  It turned out not to be really helpful and casts are ugly,
+   so now we just use void * as a base class pointer.
+*/
+
+extern struct xmlrpc_client_transport_ops xmlrpc_libwww_transport_ops;
+extern struct xmlrpc_client_transport_ops xmlrpc_wininet_transport_ops;
+extern struct xmlrpc_client_transport_ops xmlrpc_curl_transport_ops;
+
+enum xmlrpc_sslversion {
+    XMLRPC_SSLVERSION_DEFAULT,
+    XMLRPC_SSLVERSION_TLSv1,
+    XMLRPC_SSLVERSION_SSLv2,
+    XMLRPC_SSLVERSION_SSLv3
+};
+
+struct xmlrpc_curl_xportparms {
+    /* This is designed so that zero values are always the defaults. */
+    const char * network_interface;
+    xmlrpc_bool  no_ssl_verifypeer;
+    xmlrpc_bool  no_ssl_verifyhost;
+    const char * user_agent;
+    const char * ssl_cert;
+    const char * sslcerttype;
+    const char * sslcertpasswd;
+    const char * sslkey;
+    const char * sslkeytype;
+    const char * sslkeypasswd;
+    const char * sslengine;
+    xmlrpc_bool  sslengine_default;
+    enum xmlrpc_sslversion sslversion;
+    const char * cainfo;
+    const char * capath;
+    const char * randomfile;
+    const char * egdsocket;
+    const char * ssl_cipher_list;
+    unsigned int timeout;
+};
+
+
+#define XMLRPC_CXPSIZE(mbrname) \
+    XMLRPC_STRUCTSIZE(struct xmlrpc_curl_xportparms, mbrname)
+
+/* XMLRPC_CXPSIZE(xyz) is analogous to XMLRPC_CPSIZE, below */
+
+struct xmlrpc_wininet_xportparms {
+    int allowInvalidSSLCerts;
+};
+
+#define XMLRPC_WXPSIZE(mbrname) \
+    XMLRPC_STRUCTSIZE(struct xmlrpc_wininet_xportparms, mbrname)
+
+/* XMLRPC_WXPSIZE(xyz) is analogous to XMLRPC_CPSIZE, below */
+
+struct xmlrpc_clientparms {
+    /* (transport, transportparmsP, transportparm_size) and
+       (transportOpsP, transportP) are mutually exclusive.
+    */
+    const char *               transport;
+    const void *               transportparmsP;
+        /* This should be type "const struct ..._xportparms *" */
+    size_t                     transportparm_size;
+
+    const struct xmlrpc_client_transport_ops * transportOpsP;
+    xmlrpc_client_transport *  transportP;
+    xmlrpc_dialect             dialect;
+};
+
+#define XMLRPC_CPSIZE(mbrname) \
+  XMLRPC_STRUCTSIZE(struct xmlrpc_clientparms, mbrname)
+
+/* XMLRPC_CPSIZE(xyz) is the minimum size a struct xmlrpc_clientparms
+   must be to include the 'xyz' member.  This is essential to forward and
+   backward compatbility, as new members will be added to the end of the
+   struct in future releases.  This is how the callee knows whether or
+   not the caller is new enough to have supplied a certain parameter.
+*/
+
+const char * 
+xmlrpc_client_get_default_transport(xmlrpc_env * const env);
+
+/* A callback function to handle the response to an asynchronous call.
+** If 'fault->fault_occurred' is true, then response will be NULL. All
+** arguments except 'user_data' will be deallocated internally; please do
+** not free any of them yourself.
+** WARNING: param_array may (or may not) be NULL if fault->fault_occurred
+** is true, and you set up the call using xmlrpc_client_call_asynch.
+** WARNING: If asynchronous calls are still pending when the library is
+** shut down, your handler may (or may not) be called with a fault. */
+typedef void (*xmlrpc_response_handler) (const char *server_url,
+                                         const char *method_name,
+                                         xmlrpc_value *param_array,
+                                         void *user_data,
+                                         xmlrpc_env *fault,
+                                         xmlrpc_value *result);
+
+
+/*=========================================================================
+   xmlrpc_server_info
+===========================================================================
+  We normally refer to servers by URL. But sometimes we need to do extra
+  setup for particular servers. In that case, we can create an
+  xmlrpc_server_info object, configure it in various ways, and call the
+  remote server.
+
+  (This interface is also designed to discourage further multiplication
+  of xmlrpc_client_call APIs. We have enough of those already. Please
+  add future options and flags using xmlrpc_server_info.)
+=========================================================================*/
+
+typedef struct _xmlrpc_server_info xmlrpc_server_info;
+
+/* Create a new server info record, pointing to the specified server. */
+xmlrpc_server_info *
+xmlrpc_server_info_new(xmlrpc_env * const envP,
+                       const char * const serverUrl);
+
+/* Create a new server info record, with a copy of the old server. */
+extern xmlrpc_server_info * 
+xmlrpc_server_info_copy(xmlrpc_env *         const envP,
+                        xmlrpc_server_info * const srcP);
+
+void
+xmlrpc_server_info_free(xmlrpc_server_info * const serverP);
+
+
+void 
+xmlrpc_server_info_set_user(xmlrpc_env *         const envP,
+                            xmlrpc_server_info * const serverInfoP,
+                            const char *         const username,
+                            const char *         const password);
+
+void 
+xmlrpc_server_info_set_basic_auth(xmlrpc_env *         const envP,
+                                  xmlrpc_server_info * const serverP,
+                                  const char *         const username,
+                                  const char *         const password);
+
+void
+xmlrpc_server_info_allow_auth_basic(xmlrpc_env *         const envP,
+                                    xmlrpc_server_info * const sP);
+
+void
+xmlrpc_server_info_disallow_auth_basic(xmlrpc_env *         const envP,
+                                       xmlrpc_server_info * const sP);
+
+void
+xmlrpc_server_info_allow_auth_digest(xmlrpc_env *         const envP,
+                                     xmlrpc_server_info * const sP);
+
+void
+xmlrpc_server_info_disallow_auth_digest(xmlrpc_env *         const envP,
+                                        xmlrpc_server_info * const sP);
+
+void
+xmlrpc_server_info_allow_auth_negotiate(xmlrpc_env *         const envP,
+                                        xmlrpc_server_info * const sP);
+
+void
+xmlrpc_server_info_disallow_auth_negotiate(xmlrpc_env *         const envP,
+                                           xmlrpc_server_info * const sP);
+
+void
+xmlrpc_server_info_allow_auth_ntlm(xmlrpc_env *         const envP,
+                                   xmlrpc_server_info * const sP);
+
+void
+xmlrpc_server_info_disallow_auth_ntlm(xmlrpc_env *         const envP,
+                                      xmlrpc_server_info * const sP);
+
+extern unsigned int const xmlrpc_client_version_major;
+extern unsigned int const xmlrpc_client_version_minor;
+extern unsigned int const xmlrpc_client_version_point;
+
+void
+xmlrpc_client_setup_global_const(xmlrpc_env * const envP);
+
+void
+xmlrpc_client_teardown_global_const(void);
+
+void 
+xmlrpc_client_create(xmlrpc_env *                      const envP,
+                     int                               const flags,
+                     const char *                      const appname,
+                     const char *                      const appversion,
+                     const struct xmlrpc_clientparms * const clientparmsP,
+                     unsigned int                      const parmSize,
+                     xmlrpc_client **                  const clientPP);
+
+void 
+xmlrpc_client_destroy(xmlrpc_client * const clientP);
+
+void
+xmlrpc_client_transport_call2(
+    xmlrpc_env *               const envP,
+    xmlrpc_client *            const clientP,
+    const xmlrpc_server_info * const serverP,
+    xmlrpc_mem_block *         const callXmlP,
+    xmlrpc_mem_block **        const respXmlPP);
+
+void
+xmlrpc_client_call2(xmlrpc_env *               const envP,
+                    struct xmlrpc_client *     const clientP,
+                    const xmlrpc_server_info * const serverInfoP,
+                    const char *               const methodName,
+                    xmlrpc_value *             const paramArrayP,
+                    xmlrpc_value **            const resultPP);
+
+void
+xmlrpc_client_call2f(xmlrpc_env *    const envP,
+                     xmlrpc_client * const clientP,
+                     const char *    const serverUrl,
+                     const char *    const methodName,
+                     xmlrpc_value ** const resultPP,
+                     const char *    const format,
+                     ...);
+
+void 
+xmlrpc_client_event_loop_finish(xmlrpc_client * const clientP);
+
+void 
+xmlrpc_client_event_loop_finish_timeout(xmlrpc_client * const clientP,
+                                        unsigned long   const milliseconds);
+
+void
+xmlrpc_client_start_rpc(xmlrpc_env *             const envP,
+                        struct xmlrpc_client *   const clientP,
+                        xmlrpc_server_info *     const serverInfoP,
+                        const char *             const methodName,
+                        xmlrpc_value *           const argP,
+                        xmlrpc_response_handler        responseHandler,
+                        void *                   const userData);
+
+void 
+xmlrpc_client_start_rpcf(xmlrpc_env *    const envP,
+                         xmlrpc_client * const clientP,
+                         const char *    const serverUrl,
+                         const char *    const methodName,
+                         xmlrpc_response_handler callback,
+                         void *          const userData,
+                         const char *    const format,
+                         ...);
+
+void
+xmlrpc_client_set_interrupt(xmlrpc_client * const clientP,
+                            int *           const interruptP);
+
+#include "client_global.h"
+
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission. 
+**  
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE. */
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _XMLRPC_CLIENT_H_ */
diff --git a/Code/src/libsamp/xmlrpc-c/client_global.h b/Code/src/libsamp/xmlrpc-c/client_global.h
new file mode 100644
index 0000000000000000000000000000000000000000..4f388fcc95ca37ee8907c455a22e456ab4a3386d
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/client_global.h
@@ -0,0 +1,141 @@
+#ifndef CLIENT_GLOBAL_H_INCLUDED
+#define CLIENT_GLOBAL_H_INCLUDED
+
+/*=========================================================================
+**  Initialization and Shutdown
+**=========================================================================
+**  These routines initialize and terminate the XML-RPC client. If you're
+**  already using libwww on your own, you can pass
+**  XMLRPC_CLIENT_SKIP_LIBWWW_INIT to avoid initializing it twice.
+*/
+
+#define XMLRPC_CLIENT_NO_FLAGS         (0)
+#define XMLRPC_CLIENT_SKIP_LIBWWW_INIT (1)
+
+extern void
+xmlrpc_client_init(int          const flags,
+                   const char * const appname,
+                   const char * const appversion);
+
+void 
+xmlrpc_client_init2(xmlrpc_env *                      const env,
+                    int                               const flags,
+                    const char *                      const appname,
+                    const char *                      const appversion,
+                    const struct xmlrpc_clientparms * const clientparms,
+                    unsigned int                      const parm_size);
+
+extern void
+xmlrpc_client_cleanup(void);
+
+/*=========================================================================
+**  xmlrpc_client_call
+**=========================================================================
+**  A synchronous XML-RPC client. Do not attempt to call any of these
+**  functions from inside an asynchronous callback!
+*/
+
+xmlrpc_value * 
+xmlrpc_client_call(xmlrpc_env * const envP,
+                   const char * const server_url,
+                   const char * const method_name,
+                   const char * const format,
+                   ...);
+
+xmlrpc_value * 
+xmlrpc_client_call_params(xmlrpc_env *   const envP,
+                          const char *   const serverUrl,
+                          const char *   const methodName,
+                          xmlrpc_value * const paramArrayP);
+
+xmlrpc_value * 
+xmlrpc_client_call_server(xmlrpc_env *               const envP,
+                          const xmlrpc_server_info * const server,
+                          const char *               const method_name,
+                          const char *               const format, 
+                          ...);
+
+xmlrpc_value *
+xmlrpc_client_call_server_params(
+    xmlrpc_env *               const envP,
+    const xmlrpc_server_info * const serverP,
+    const char *               const method_name,
+    xmlrpc_value *             const paramArrayP);
+
+void
+xmlrpc_client_transport_call(
+    xmlrpc_env *               const envP,
+    void *                     const reserved,  /* for client handle */
+    const xmlrpc_server_info * const serverP,
+    xmlrpc_mem_block *         const callXmlP,
+    xmlrpc_mem_block **        const respXmlPP);
+
+
+/*=========================================================================
+**  xmlrpc_client_call_asynch
+**=========================================================================
+**  An asynchronous XML-RPC client.
+*/
+
+/* Make an asynchronous XML-RPC call. We make internal copies of all
+** arguments except user_data, so you can deallocate them safely as soon
+** as you return. Errors will be passed to the callback. You will need
+** to run the event loop somehow; see below.
+** WARNING: If an error occurs while building the argument, the
+** response handler will be called with a NULL param_array. */
+void 
+xmlrpc_client_call_asynch(const char * const server_url,
+                          const char * const method_name,
+                          xmlrpc_response_handler callback,
+                          void *       const user_data,
+                          const char * const format,
+                          ...);
+
+/* As above, but use an xmlrpc_server_info object. The server object can be
+** safely destroyed as soon as this function returns. */
+void 
+xmlrpc_client_call_server_asynch(xmlrpc_server_info * const server,
+                                 const char *         const method_name,
+                                 xmlrpc_response_handler callback,
+                                 void *               const user_data,
+                                 const char *         const format,
+                                 ...);
+
+/* As above, but the parameter list is supplied as an xmlrpc_value
+** containing an array.
+*/
+void
+xmlrpc_client_call_asynch_params(const char *   const server_url,
+                                 const char *   const method_name,
+                                 xmlrpc_response_handler callback,
+                                 void *         const user_data,
+                                 xmlrpc_value * const paramArrayP);
+    
+/* As above, but use an xmlrpc_server_info object. The server object can be
+** safely destroyed as soon as this function returns. */
+void 
+xmlrpc_client_call_server_asynch_params(
+    xmlrpc_server_info * const server,
+    const char *         const method_name,
+    xmlrpc_response_handler callback,
+    void *               const user_data,
+    xmlrpc_value *       const paramArrayP);
+    
+/*=========================================================================
+**  Event Loop Interface
+**=========================================================================
+**  These functions can be used to run the XML-RPC event loop. If you
+**  don't like these, you can also run the libwww event loop directly.
+*/
+
+/* Finish all outstanding asynchronous calls. Alternatively, the loop
+** will exit if someone calls xmlrpc_client_event_loop_end. */
+extern void
+xmlrpc_client_event_loop_finish_asynch(void);
+
+
+/* Finish all outstanding asynchronous calls. */
+extern void
+xmlrpc_client_event_loop_finish_asynch_timeout(unsigned long const milliseconds);
+
+#endif
diff --git a/Code/src/libsamp/xmlrpc-c/config.h b/Code/src/libsamp/xmlrpc-c/config.h
new file mode 100644
index 0000000000000000000000000000000000000000..1c1b6b654a6baf94de123e351efb456c27d62b64
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/config.h
@@ -0,0 +1,37 @@
+#ifndef XMLRPC_C_CONFIG_H_INCLUDED
+#define XMLRPC_C_CONFIG_H_INCLUDED
+
+/* This file, part of XML-RPC For C/C++, is meant to 
+   define characteristics of this particular installation 
+   that the other <xmlrpc-c/...> header files need in 
+   order to compile correctly when #included in Xmlrpc-c
+   user code.
+
+   Those header files #include this one.
+
+   This file was created by a make rule.
+*/
+#define XMLRPC_HAVE_WCHAR 1
+#ifdef WIN32
+  /* SOCKET is a type defined by <winsock.h>.  Anyone who
+     uses XMLRPC_SOCKET on a WIN32 system must #include
+     <winsock.h>
+  */
+  #define XMLRPC_SOCKET SOCKET
+  #define XMLRPC_HAVE_TIMEVAL 0
+  #define XMLRPC_HAVE_TIMESPEC 0
+#else
+  #define XMLRPC_SOCKET int
+  #define XMLRPC_HAVE_TIMEVAL 1
+  #define XMLRPC_HAVE_TIMESPEC 1
+#endif
+
+#if defined(_MSC_VER)
+  /* Newer MSVC has long long, but MSVC 6 does not */
+  #define XMLRPC_INT64 __int64
+  #define XMLRPC_INT32 __int32
+#else
+  #define XMLRPC_INT64 long long
+  #define XMLRPC_INT32 int
+#endif
+#endif
diff --git a/Code/src/libsamp/xmlrpc-c/inttypes.h b/Code/src/libsamp/xmlrpc-c/inttypes.h
new file mode 100644
index 0000000000000000000000000000000000000000..fe1feb53e279be2f07e71ea09564bf237594cb84
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/inttypes.h
@@ -0,0 +1,19 @@
+#ifndef XMLRPC_INTTYPES_H_INCLUDED
+#define XMLRPC_INTTYPES_H_INCLUDED
+
+#ifdef _MSC_VER
+
+typedef unsigned short    xmlrpc_uint16_t;
+typedef unsigned int      xmlrpc_uint32_t;
+typedef unsigned __int64  xmlrpc_uint64_t;
+
+#else
+#include <inttypes.h>
+
+typedef uint16_t xmlrpc_uint16_t;
+typedef uint32_t xmlrpc_uint32_t;
+typedef uint64_t xmlrpc_uint64_t;
+
+#endif
+
+#endif
diff --git a/Code/src/libsamp/xmlrpc-c/server.h b/Code/src/libsamp/xmlrpc-c/server.h
new file mode 100644
index 0000000000000000000000000000000000000000..e86bdb38a0779df589c165504721b63176a734a4
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/server.h
@@ -0,0 +1,173 @@
+/* Copyright and license information is at the end of the file */
+
+#ifndef  XMLRPC_SERVER_H_INCLUDED
+#define  XMLRPC_SERVER_H_INCLUDED
+
+#include "base.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct xmlrpc_registry xmlrpc_registry;
+
+typedef void
+(*xmlrpc_preinvoke_method)(xmlrpc_env *   const envP,
+                           const char *   const methodName,
+                           xmlrpc_value * const paramArrayP,
+                           void *         const userData);
+
+typedef xmlrpc_value *
+(*xmlrpc_method1)(xmlrpc_env *   const envP,
+                  xmlrpc_value * const paramArrayP,
+                  void *         const serverInfo);
+
+typedef xmlrpc_value *
+(*xmlrpc_method2)(xmlrpc_env *   const envP,
+                  xmlrpc_value * const paramArrayP,
+                  void *         const serverInfo,
+                  void *         const callInfo);
+
+typedef xmlrpc_method1 xmlrpc_method;  /* backward compatibility */
+
+typedef xmlrpc_value *
+(*xmlrpc_default_method)(xmlrpc_env *   const envP,
+                         const char *   const callInfoP,
+                         const char *   const methodName,
+                         xmlrpc_value * const paramArrayP,
+                         void *         const serverInfo);
+
+extern unsigned int const xmlrpc_server_version_major;
+extern unsigned int const xmlrpc_server_version_minor;
+extern unsigned int const xmlrpc_server_version_point;
+
+xmlrpc_registry *
+xmlrpc_registry_new(xmlrpc_env * const envP);
+
+void
+xmlrpc_registry_free(xmlrpc_registry * const registryP);
+
+void
+xmlrpc_registry_disable_introspection(xmlrpc_registry * const registryP);
+
+void
+xmlrpc_registry_add_method(xmlrpc_env *      const envP,
+                           xmlrpc_registry * const registryP,
+                           const char *      const host,
+                           const char *      const methodName,
+                           xmlrpc_method     const method,
+                           void *            const serverInfo);
+
+void
+xmlrpc_registry_add_method_w_doc(xmlrpc_env *      const envP,
+                                 xmlrpc_registry * const registryP,
+                                 const char *      const host,
+                                 const char *      const methodName,
+                                 xmlrpc_method     const method,
+                                 void *            const serverInfo,
+                                 const char *      const signatureString,
+                                 const char *      const help);
+
+void
+xmlrpc_registry_add_method2(xmlrpc_env *      const envP,
+                            xmlrpc_registry * const registryP,
+                            const char *      const methodName,
+                            xmlrpc_method2          method,
+                            const char *      const signatureString,
+                            const char *      const help,
+                            void *            const serverInfo);
+
+struct xmlrpc_method_info3 {
+    const char *      methodName;
+    xmlrpc_method2    methodFunction;
+    void *            serverInfo;
+    size_t            stackSize;
+    const char *      signatureString;
+    const char *      help;
+};
+
+void
+xmlrpc_registry_add_method3(
+    xmlrpc_env *                       const envP,
+    xmlrpc_registry *                  const registryP,
+    const struct xmlrpc_method_info3 * const infoP);
+
+void
+xmlrpc_registry_set_default_method(xmlrpc_env *          const envP,
+                                   xmlrpc_registry *     const registryP,
+                                   xmlrpc_default_method const handler,
+                                   void *                const userData);
+
+void
+xmlrpc_registry_set_preinvoke_method(xmlrpc_env *            const envP,
+                                     xmlrpc_registry *       const registryP,
+                                     xmlrpc_preinvoke_method const method,
+                                     void *                  const userData);
+
+
+typedef void xmlrpc_server_shutdown_fn(xmlrpc_env * const envP,
+                                       void *       const context,
+                                       const char * const comment,
+                                       void *       const callInfo);
+
+void
+xmlrpc_registry_set_shutdown(xmlrpc_registry *           const registryP,
+                             xmlrpc_server_shutdown_fn * const shutdownFn,
+                             void *                      const context);
+
+void
+xmlrpc_registry_set_dialect(xmlrpc_env *      const envP,
+                            xmlrpc_registry * const registryP,
+                            xmlrpc_dialect    const dialect);
+
+/*----------------------------------------------------------------------------
+   Lower interface -- services to be used by an HTTP request handler
+-----------------------------------------------------------------------------*/
+                    
+void
+xmlrpc_registry_process_call2(xmlrpc_env *        const envP,
+                              xmlrpc_registry *   const registryP,
+                              const char *        const xmlData,
+                              size_t              const xmlLen,
+                              void *              const callInfo,
+                              xmlrpc_mem_block ** const outputPP);
+
+xmlrpc_mem_block *
+xmlrpc_registry_process_call(xmlrpc_env *      const envP,
+                             xmlrpc_registry * const registryP,
+                             const char *      const host,
+                             const char *      const xmlData,
+                             size_t            const xmlLen);
+
+size_t
+xmlrpc_registry_max_stackSize(xmlrpc_registry * const registryP);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission. 
+**  
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE. */
+#endif
diff --git a/Code/src/libsamp/xmlrpc-c/server_abyss.h b/Code/src/libsamp/xmlrpc-c/server_abyss.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e3a7f827aec43d3c043771e2060cc6f006df822
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/server_abyss.h
@@ -0,0 +1,276 @@
+/*============================================================================
+                              server_abyss.h
+==============================================================================
+  This declares the user interface to libxmlrpc_server_abyss, which
+  provides facilities for running an XML-RPC server based on the Xmlrpc-c
+  Abyss HTTP server.
+============================================================================*/
+
+/* Copyright and license information is at the end of the file */
+
+#ifndef  XMLRPC_SERVER_ABYSS_H_INCLUDED
+#define  XMLRPC_SERVER_ABYSS_H_INCLUDED
+
+#ifdef WIN32
+#include <winsock.h>  /* For XMLRPC_SOCKET (= SOCKET) */
+#endif
+
+#include "config.h"  /* For XMLRPC_SOCKET */
+#include "abyss.h"
+#include "server.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define XMLRPC_SERVER_ABYSS_NO_FLAGS (0)
+
+
+/*=========================================================================
+**  Global Initialization/Termination.
+**  
+**  These are not thread-safe.  You call them at the beginning and end
+**  of your program, when it is only one thread.
+**=======================================================================*/
+
+void
+xmlrpc_server_abyss_global_init(xmlrpc_env * const envP);
+
+void
+xmlrpc_server_abyss_global_term(void);
+
+/*=========================================================================
+**  Basic Abyss Server Functions
+**=======================================================================*/
+
+typedef void ((*runfirstFn)(void *));
+
+typedef struct {
+    const char *      config_file_name;
+        /* NULL to use preferred proper API-level interface */
+
+    xmlrpc_registry * registryP;
+
+    /* runfirstFn and runfirst_arg are meaningless when 
+       config_file_name is NULL
+    */
+    runfirstFn        runfirst;
+    void *            runfirst_arg;
+
+    unsigned int      port_number;
+    const char *      log_file_name;
+    unsigned int      keepalive_timeout;
+    unsigned int      keepalive_max_conn;
+    unsigned int      timeout;
+    xmlrpc_bool       dont_advertise;
+    xmlrpc_bool       socket_bound;
+    XMLRPC_SOCKET     socket_handle;
+    const char *      uri_path;
+    xmlrpc_bool       chunk_response;
+    xmlrpc_bool       enable_shutdown;
+} xmlrpc_server_abyss_parms;
+
+
+#define XMLRPC_APSIZE(MBRNAME) \
+    XMLRPC_STRUCTSIZE(xmlrpc_server_abyss_parms, MBRNAME)
+
+/* XMLRPC_APSIZE(xyz) is the minimum size a struct xmlrpc_server_abyss_parms
+   must be to include the 'xyz' member.  This is essential to forward and
+   backward compatibility, as new members will be added to the end of the
+   struct in future releases.  This is how the callee knows whether or
+   not the caller is new enough to have supplied a certain parameter.
+*/
+
+/*=========================================================================
+**  Simple server with Abyss under the covers
+**=======================================================================*/
+
+void
+xmlrpc_server_abyss(xmlrpc_env *                      const envP,
+                    const xmlrpc_server_abyss_parms * const parms,
+                    unsigned int                      const parm_size);
+
+/*=========================================================================
+**  Object-oriented XML-RPC server with Abyss under the covers
+**=======================================================================*/
+
+typedef struct xmlrpc_server_abyss xmlrpc_server_abyss_t;
+
+void
+xmlrpc_server_abyss_create(xmlrpc_env *                      const envP,
+                           const xmlrpc_server_abyss_parms * const parmsP,
+                           unsigned int                      const parmSize,
+                           xmlrpc_server_abyss_t **          const serverPP);
+
+void
+xmlrpc_server_abyss_destroy(xmlrpc_server_abyss_t * const serverP);
+
+void
+xmlrpc_server_abyss_run_server(xmlrpc_env *            const envP,
+                               xmlrpc_server_abyss_t * const serverP);
+
+void
+xmlrpc_server_abyss_terminate(xmlrpc_env *            const envP,
+                              xmlrpc_server_abyss_t * const serverP);
+
+void
+xmlrpc_server_abyss_reset_terminate(xmlrpc_env *            const envP,
+                                    xmlrpc_server_abyss_t * const serverP);
+
+void
+xmlrpc_server_abyss_use_sigchld(xmlrpc_server_abyss_t * const serverP);
+
+
+typedef struct xmlrpc_server_abyss_sig xmlrpc_server_abyss_sig;
+
+void
+xmlrpc_server_abyss_setup_sig(
+    xmlrpc_env *               const envP,
+    xmlrpc_server_abyss_t *    const serverP,
+    xmlrpc_server_abyss_sig ** const oldHandlersPP);
+
+void
+xmlrpc_server_abyss_restore_sig(
+    const xmlrpc_server_abyss_sig * const oldHandlersP);
+
+
+
+/*=========================================================================
+**  Functions to make an XML-RPC server out of your own Abyss server
+**=======================================================================*/
+
+void
+xmlrpc_server_abyss_set_handlers2(TServer *         const srvP,
+                                  const char *      const filename,
+                                  xmlrpc_registry * const registryP);
+
+void
+xmlrpc_server_abyss_set_handlers(TServer *         const serverP,
+                                 xmlrpc_registry * const registryP);
+
+void
+xmlrpc_server_abyss_set_handler(xmlrpc_env *      const envP,
+                                TServer *         const serverP,
+                                const char *      const filename,
+                                xmlrpc_registry * const registryP);
+
+/*=========================================================================
+**  Handy Abyss Extensions
+**=======================================================================*/
+
+/* These are functions that have nothing to do with Xmlrpc-c, but provide
+   convenient Abyss services beyond those provided by the Abyss library.
+*/
+
+/* Start an Abyss webserver running (previously created and
+** initialized).  Under Unix, this routine will attempt to do a
+** detaching fork, drop root privileges (if any) and create a pid
+** file.  Under Windows, this routine merely starts the server.  This
+** routine never returns.
+**
+** Once you call this routine, it is illegal to modify the server any
+** more, including changing any method registry.
+*/
+void
+xmlrpc_server_abyss_run(void);
+
+/* Same as xmlrpc_server_abyss_run(), except you get to specify a "runfirst"
+** function.  The server runs this just before executing the actual server
+** function, after any daemonizing.  NULL for 'runfirst' means no runfirst
+** function.  'runfirstArg' is the argument the server passes to the runfirst
+** function.
+**/
+void 
+xmlrpc_server_abyss_run_first(runfirstFn const runfirst,
+                              void *     const runfirstArg);
+
+/*=========================================================================
+**  Method Registry
+**=========================================================================
+   These functions are for the built-in xmlrpc_server_abyss registry.
+   It's usually simpler to skip all this and use the regular method
+   registry services (from xmlrpc_server.h) to build a registry and
+   pass it to xmlrpc_server_abyss.
+*/
+
+/* Call this function to create a new Abyss webserver with the default
+** options and the built-in method registry.  If you've already
+** initialized Abyss using Abyss functions, you can instead call
+** xmlrpc_server_abyss_init_registry() to make it an Xmlrpc-c server.
+** Or use a regular method registry and call
+** xmlrpc_server_abyss_set_handlers().
+**/
+void 
+xmlrpc_server_abyss_init(int          const flags, 
+                         const char * const config_file);
+
+/* This is called automatically by xmlrpc_server_abyss_init. */
+void xmlrpc_server_abyss_init_registry (void);
+
+/* Fetch the internal registry, if you happen to need it. 
+   If you're using this, you really shouldn't be using the built-in
+   registry at all.  It exists today only for backward compatibilty.
+*/
+extern xmlrpc_registry *
+xmlrpc_server_abyss_registry (void);
+
+/* A quick & easy shorthand for adding a method. Depending on
+** how you've configured your copy of Abyss, it's probably not safe to
+** call this method after calling xmlrpc_server_abyss_run. */
+void xmlrpc_server_abyss_add_method (char *        const method_name,
+                                     xmlrpc_method const method,
+                                     void *        const user_data);
+    
+/* As above, but provide documentation (see xmlrpc_registry_add_method_w_doc
+** for more information). You should really use this one. */
+extern void
+xmlrpc_server_abyss_add_method_w_doc (char *        const method_name,
+                                      xmlrpc_method const method,
+                                      void *        const user_data,
+                                      char *        const signature,
+                                      char *        const help);
+
+/*=========================================================================
+**  Content Handlers
+**=======================================================================*/
+/* Abyss contents handlers xmlrpc_server_abyss_rpc2_handler()
+   and xmlrpc_server_abyss_default_handler() were available in older
+   Xmlrpc-c, but starting with Release 1.01, they are not.  Instead,
+   call xmlrpc_server_abyss_set_handlers() to install them.
+
+   Alternatively, you can write your own handlers that do the same thing.
+   It's not hard, and if you're writing low enough level Abyss code that
+   you can't use xmlrpc_server_abyss_set_handlers(), you probably want to
+   anyway.
+*/
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+/* Copyright (C) 2001 by First Peer, Inc. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+**    notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+**    notice, this list of conditions and the following disclaimer in the
+**    documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+**    derived from this software without specific prior written permission. 
+**  
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+** SUCH DAMAGE. */
+
+#endif
diff --git a/Code/src/libsamp/xmlrpc-c/util.h b/Code/src/libsamp/xmlrpc-c/util.h
new file mode 100644
index 0000000000000000000000000000000000000000..6c6c9a2638818b7df075d360ae928d09641d602d
--- /dev/null
+++ b/Code/src/libsamp/xmlrpc-c/util.h
@@ -0,0 +1,328 @@
+/*=============================================================================
+                                 xmlrpc-c/util.h
+===============================================================================
+
+  This is the interface to the libxmlrpc_util library, which contains
+  utility routines that have nothing to do with XML-RPC.  The library
+  exists because other Xmlrpc-c libraries use the utilities.
+
+  By Bryan Henderson, San Jose, CA 05.09.21.
+
+  Contributed to the public domain by its author.
+=============================================================================*/
+
+#ifndef XMLRPC_C_UTIL_H_INCLUDED
+#define XMLRPC_C_UTIL_H_INCLUDED
+
+#include <sys/types.h>
+#include <stdarg.h>
+
+#include "config.h"  /* Defines XMLRPC_HAVE_WCHAR */
+#include "c_util.h"  /* for GNU_PRINTF_ATTR */
+
+#if XMLRPC_HAVE_WCHAR
+#include <wchar.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*=========================================================================
+**  C struct size computations
+**=======================================================================*/
+
+/* Use XMLRPC_STRUCT_MEMBER_SIZE() to determine how big a structure is
+   up to and including a specified member.  E.g. if you have
+   struct mystruct {int red; int green; int blue};, then
+   XMLRPC_STRUCT_MEMBER_SIZE(mystruct, green) is (8).
+*/
+
+#define _XMLRPC_STRUCT_MEMBER_OFFSET(TYPE, MBRNAME) \
+  ((size_t)(char*)&((TYPE *)0)->MBRNAME)
+#define _XMLRPC_STRUCT_MEMBER_SIZE(TYPE, MBRNAME) \
+  sizeof(((TYPE *)0)->MBRNAME)
+#define XMLRPC_STRUCTSIZE(TYPE, MBRNAME) \
+  (_XMLRPC_STRUCT_MEMBER_OFFSET(TYPE, MBRNAME) + \
+  _XMLRPC_STRUCT_MEMBER_SIZE(TYPE, MBRNAME))
+
+/*=========================================================================
+**  Assertions and Debugging
+**=========================================================================
+**  Note that an assertion is _not_ a directive to check a condition and
+**  crash if it isn't true.  It is an assertion that the condition _is_
+**  true.  This assertion helps people to read the code.  The program
+**  may also check the assertion as it runs, and if it conflicts with reality,
+**  recognize that the program is incorrect and abort it.  In practice,
+**  it does this checking when the program was compiled without the NDEBUG
+**  macro defined.
+*/
+
+#ifndef NDEBUG
+
+#define XMLRPC_ASSERT(cond) \
+    do \
+        if (!(cond)) \
+            xmlrpc_assertion_failed(__FILE__, __LINE__); \
+    while (0)
+
+#else
+#define XMLRPC_ASSERT(cond) while (0) {}
+#endif
+
+void
+xmlrpc_assertion_failed(const char * const fileName,
+                        int          const lineNumber);
+
+/* Validate a pointer. */
+#define XMLRPC_ASSERT_PTR_OK(ptr) \
+    XMLRPC_ASSERT((ptr) != NULL)
+
+
+/*=========================================================================
+**  xmlrpc_env
+**=========================================================================
+**  XML-RPC represents runtime errors as <fault> elements. These contain
+**  <faultCode> and <faultString> elements.
+**
+**  Since we need as much thread-safety as possible, we borrow an idea from
+**  CORBA--we store exception information in an "environment" object.
+**  You'll pass this to many different functions, and it will get filled
+**  out appropriately.
+**
+**  For example:
+**
+**    xmlrpc_env env;
+**
+**    xmlrpc_env_init(&env);
+**
+**    xmlrpc_do_something(&env);
+**    if (env.fault_occurred)
+**        report_error_appropriately();
+**
+**    xmlrpc_env_clean(&env);
+*/
+
+#define XMLRPC_INTERNAL_ERROR               (-500)
+#define XMLRPC_TYPE_ERROR                   (-501)
+#define XMLRPC_INDEX_ERROR                  (-502)
+#define XMLRPC_PARSE_ERROR                  (-503)
+#define XMLRPC_NETWORK_ERROR                (-504)
+#define XMLRPC_TIMEOUT_ERROR                (-505)
+#define XMLRPC_NO_SUCH_METHOD_ERROR         (-506)
+#define XMLRPC_REQUEST_REFUSED_ERROR        (-507)
+#define XMLRPC_INTROSPECTION_DISABLED_ERROR (-508)
+#define XMLRPC_LIMIT_EXCEEDED_ERROR         (-509)
+#define XMLRPC_INVALID_UTF8_ERROR           (-510)
+
+typedef struct _xmlrpc_env {
+    int    fault_occurred;
+    int    fault_code;
+    char * fault_string;
+} xmlrpc_env;
+
+/* Initialize and destroy the contents of the provided xmlrpc_env object.
+** These functions will never fail. */
+void xmlrpc_env_init (xmlrpc_env* env);
+void xmlrpc_env_clean (xmlrpc_env* const env);
+
+/* Fill out an xmlrpc_fault with the specified values, and set the
+** fault_occurred flag. This function will make a private copy of 'string',
+** so you retain responsibility for your copy. */
+void 
+xmlrpc_env_set_fault(xmlrpc_env * const env, 
+                     int          const faultCode, 
+                     const char * const faultDescription);
+
+/* The same as the above, but using varargs */
+void
+xmlrpc_set_fault_formatted_v(xmlrpc_env * const envP,
+                             int          const code,
+                             const char * const format,
+                             va_list      const args);
+
+/* The same as the above, but using a printf-style format string. */
+void 
+xmlrpc_env_set_fault_formatted(xmlrpc_env * const envP, 
+                               int          const code,
+                               const char * const format, 
+                               ...) GNU_PRINTF_ATTR(3,4);
+
+/* This one infers XMLRPC_INTERNAL_ERROR and has a shorter name.
+   So a call takes up less source code space.
+*/
+void
+xmlrpc_faultf(xmlrpc_env * const envP,
+              const char * const format,
+              ...) GNU_PRINTF_ATTR(2,3);
+
+/* A simple debugging assertion. */
+#define XMLRPC_ASSERT_ENV_OK(envP) \
+    XMLRPC_ASSERT((envP) != NULL && \
+    (envP->fault_string == NULL) && \
+    !(envP)->fault_occurred)
+
+/* This version must *not* interpret 'str' as a format string, to avoid
+** several evil attacks. */
+#define XMLRPC_FAIL(env,code,str) \
+    do { xmlrpc_env_set_fault((env),(code),(str)); goto cleanup; } while (0)
+
+#define XMLRPC_FAIL1(env,code,str,arg1) \
+    do { \
+        xmlrpc_env_set_fault_formatted((env),(code),(str),(arg1)); \
+        goto cleanup; \
+    } while (0)
+
+#define XMLRPC_FAIL2(env,code,str,arg1,arg2) \
+    do { \
+        xmlrpc_env_set_fault_formatted((env),(code),(str),(arg1),(arg2)); \
+        goto cleanup; \
+    } while (0)
+
+#define XMLRPC_FAIL3(env,code,str,arg1,arg2,arg3) \
+    do { \
+        xmlrpc_env_set_fault_formatted((env),(code), \
+                                       (str),(arg1),(arg2),(arg3)); \
+        goto cleanup; \
+    } while (0)
+
+#if !defined(__cplusplus)
+#if defined(__GNUC__)
+#define XMLRPC_FAILF( env, code, fmt, ... )  \
+    do {  \
+        xmlrpc_env_set_fault_formatted((env), (code), (fmt),  \
+                                       ##__VA_ARGS__ );  \
+        goto cleanup;  \
+    } while (0)
+#endif
+#endif
+
+#define XMLRPC_FAIL_IF_NULL(ptr,env,code,str) \
+    do { \
+        if ((ptr) == NULL) \
+            XMLRPC_FAIL((env),(code),(str)); \
+    } while (0)
+
+#define XMLRPC_FAIL_IF_FAULT(env) \
+    do { if ((env)->fault_occurred) goto cleanup; } while (0)
+
+
+/*=========================================================================
+**  xmlrpc_mem_block
+**=========================================================================
+**  A resizable chunk of memory. This is mostly used internally, but it is
+**  also used by the public API in a few places.
+**  The struct fields are private!
+*/
+
+typedef struct _xmlrpc_mem_block {
+    size_t _size;
+    size_t _allocated;
+    void*  _block;
+} xmlrpc_mem_block;
+
+/* Allocate a new xmlrpc_mem_block. */
+xmlrpc_mem_block* xmlrpc_mem_block_new (xmlrpc_env* const env, size_t const size);
+
+/* Destroy an existing xmlrpc_mem_block, and everything it contains. */
+void xmlrpc_mem_block_free (xmlrpc_mem_block* const block);
+
+/* Initialize the contents of the provided xmlrpc_mem_block. */
+void xmlrpc_mem_block_init
+    (xmlrpc_env* const env, xmlrpc_mem_block* const block, size_t const size);
+
+/* Deallocate the contents of the provided xmlrpc_mem_block, but not the
+** block itself. */
+void xmlrpc_mem_block_clean (xmlrpc_mem_block* const block);
+
+/* Get the size and contents of the xmlrpc_mem_block. */
+size_t 
+xmlrpc_mem_block_size(const xmlrpc_mem_block * const block);
+
+void * 
+xmlrpc_mem_block_contents(const xmlrpc_mem_block * const block);
+
+/* Resize an xmlrpc_mem_block, preserving as much of the contents as
+** possible. */
+void xmlrpc_mem_block_resize
+    (xmlrpc_env* const env, xmlrpc_mem_block* const block, size_t const size);
+
+/* Append data to an existing xmlrpc_mem_block. */
+void xmlrpc_mem_block_append
+    (xmlrpc_env* const env, xmlrpc_mem_block* const block, const void * const data, size_t const len);
+
+#define XMLRPC_MEMBLOCK_NEW(type,env,size) \
+    xmlrpc_mem_block_new((env), sizeof(type) * (size))
+#define XMLRPC_MEMBLOCK_FREE(type,block) \
+    xmlrpc_mem_block_free(block)
+#define XMLRPC_MEMBLOCK_INIT(type,env,block,size) \
+    xmlrpc_mem_block_init((env), (block), sizeof(type) * (size))
+#define XMLRPC_MEMBLOCK_CLEAN(type,block) \
+    xmlrpc_mem_block_clean(block)
+#define XMLRPC_MEMBLOCK_SIZE(type,block) \
+    (xmlrpc_mem_block_size(block) / sizeof(type))
+#define XMLRPC_MEMBLOCK_CONTENTS(type,block) \
+    ((type*) xmlrpc_mem_block_contents(block))
+#define XMLRPC_MEMBLOCK_RESIZE(type,env,block,size) \
+    xmlrpc_mem_block_resize(env, block, sizeof(type) * (size))
+#define XMLRPC_MEMBLOCK_APPEND(type,env,block,data,size) \
+    xmlrpc_mem_block_append(env, block, data, sizeof(type) * (size))
+
+/* Here are some backward compatibility definitions.  These longer names
+   used to be the only ones and typed memory blocks were considered
+   special.
+*/
+#define XMLRPC_TYPED_MEM_BLOCK_NEW(type,env,size) \
+    XMLRPC_MEMBLOCK_NEW(type,env,size)
+#define XMLRPC_TYPED_MEM_BLOCK_FREE(type,block) \
+    XMLRPC_MEMBLOCK_FREE(type,block)
+#define XMLRPC_TYPED_MEM_BLOCK_INIT(type,env,block,size) \
+    XMLRPC_MEMBLOCK_INIT(type,env,block,size)
+#define XMLRPC_TYPED_MEM_BLOCK_CLEAN(type,block) \
+    XMLRPC_MEMBLOCK_CLEAN(type,block)
+#define XMLRPC_TYPED_MEM_BLOCK_SIZE(type,block) \
+    XMLRPC_MEMBLOCK_SIZE(type,block)
+#define XMLRPC_TYPED_MEM_BLOCK_CONTENTS(type,block) \
+    XMLRPC_MEMBLOCK_CONTENTS(type,block)
+#define XMLRPC_TYPED_MEM_BLOCK_RESIZE(type,env,block,size) \
+    XMLRPC_MEMBLOCK_RESIZE(type,env,block,size)
+#define XMLRPC_TYPED_MEM_BLOCK_APPEND(type,env,block,data,size) \
+    XMLRPC_MEMBLOCK_APPEND(type,env,block,data,size)
+
+
+/*=========================================================================
+**  UTF-8 Encoding and Decoding
+**=======================================================================*/
+
+void 
+xmlrpc_validate_utf8(xmlrpc_env * const envP,
+                     const char * const utf8Data,
+                     size_t       const utf8Len);
+
+/* Decode a UTF-8 string. */
+xmlrpc_mem_block *
+xmlrpc_utf8_to_wcs(xmlrpc_env * const envP,
+                   const char * const utf8_data,
+                   size_t       const utf8_len);
+
+/* Encode a UTF-8 string. */
+
+#if XMLRPC_HAVE_WCHAR
+xmlrpc_mem_block *
+xmlrpc_wcs_to_utf8(xmlrpc_env *    const envP,
+                   const wchar_t * const wcsData,
+                   size_t          const wcsLen);
+#endif
+
+void
+xmlrpc_force_to_utf8(char * const buffer);
+
+void
+xmlrpc_force_to_xml_chars(char * const buffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Code/src/libsamp/xrpc.h b/Code/src/libsamp/xrpc.h
new file mode 100644
index 0000000000000000000000000000000000000000..9b560ba10b63e2221a57c3e71716f203b9d8ce35
--- /dev/null
+++ b/Code/src/libsamp/xrpc.h
@@ -0,0 +1,194 @@
+/*  
+**  XRPC.h -- Public include file for the XRPC interface.
+*/
+
+
+#ifdef  OK
+#undef  OK
+#endif
+#define	OK		0
+
+#ifdef  ERR
+#undef  ERR
+#endif
+#define	ERR		1
+
+
+#ifdef  TRUE
+#undef  TRUE
+#endif
+#define TRUE            1
+
+#ifdef  FALSE
+#undef  FALSE
+#endif
+#define FALSE           0
+
+
+
+/*****************************************************************************/
+/****                       Function Prototypes                           ****/
+/*****************************************************************************/
+
+/*  xrArray.c
+*/
+int    xr_newArray (void);
+void   xr_freeArray (int anum);
+int    xr_arrayLen (int anum);
+
+void   xr_setIntInArray (int anum, int value);
+void   xr_setDoubleInArray (int anum, double value);
+void   xr_setBoolInArray (int anum, int value);
+void   xr_setStringInArray (int anum, char *value);
+void   xr_setDatetimeInArray (int anum, char *value);
+void   xr_setStructInArray (int anum, int value);
+void   xr_setArrayInArray (int anum, int value);
+
+void   xr_getIntFromArray (int anum, int index, int *ival);
+void   xr_getDoubleFromArray (int anum, int index, double *dval);
+void   xr_getBoolFromArray (int anum, int index, int *bval);
+void   xr_getStringFromArray (int anum, int index, char **value);
+void   xr_getDatetimeFromArray (int anum, int index, char **value);
+void   xr_getStructFromArray (int anum, int index, int *value);
+void   xr_getArrayFromArray (int anum, int index, int *value);
+
+xmlrpc_value *xr_getAElement (int anum);
+void   xr_setAElement (int anum, xmlrpc_value *v);
+
+
+/*  xrClient.c
+*/
+int    xr_newASync (int cnum);
+int    xr_initClient (char *url, char *name, char *version);
+int    xr_closeClient (int cnum);
+int    xr_setClient (int cnum, char *url);
+int    xr_callSync (int cnum, char *name);
+
+int    xr_callASync (int cnum, char *name, void *ret_handler);
+int    xr_asyncWait (void);
+
+void   xr_initParam (int cnum);
+void   xr_setVerbose (int verbose);
+void   xr_setDebug   (int debug);
+
+void   xr_setIntInParam (int cnum, int value);
+void   xr_setDoubleInParam (int cnum, double value);
+void   xr_setBoolInParam (int cnum, int value);
+void   xr_setStringInParam (int cnum, char *str);
+void   xr_setDatetimeInParam (int cnum, char *str);
+void   xr_setStructInParam (int cnum, int snum);
+void   xr_setArrayInParam (int cnum, int anum);
+
+int    xr_getIntFromResult (int cnum, int *value);
+int    xr_getDoubleFromResult (int cnum, double *value);
+int    xr_getBoolFromResult (int cnum, int *value);
+int    xr_getStringFromResult (int cnum, char **value);
+int    xr_getDatetimeFromResult (int cnum, char **date);
+int    xr_getStructFromResult (int cnum, int *snum);
+int    xr_getArrayFromResult (int cnum, int *anum);
+
+char  *xr_getErrMsg (int cnum);
+int    xr_getErrCode (int cnum);
+
+void   xr_envClean (int cnum);
+void   xr_freeParam (int cnum);
+void   xr_freeResult (int cnum);
+void   xr_clientCleanup (int cnum);
+void   xr_printClient (int cnum);
+
+
+/*  xrMethod.c
+*/
+int    xr_getIntFromParam (void *data, int index);
+double xr_getDoubleFromParam (void *data, int index);
+char  *xr_getStringFromParam (void *data, int index);
+int    xr_getBoolFromParam (void *data, int index);
+char  *xr_getDatetimeFromParam (void *data, int index);
+int    xr_getStructFromParam (void *data, int index);
+int    xr_getArrayFromParam (void *data, int index);
+
+void   xr_setIntInResult (void *data, int val);
+void   xr_setDoubleInResult (void *data, double val);
+void   xr_setBoolInResult (void *data, int val);
+void   xr_setStringInResult (void *data, char *val);
+void   xr_setDatetimeInResult (void *data, char *val);
+void   xr_setStructInResult (void *data, int snum);
+void   xr_setArrayInResult (void *data, int anum);
+
+void   xr_setShutdown (void *data, int val);
+
+
+/*  xrServer.c
+*/
+typedef void (*sighandler_t)(int);
+
+int    xr_createServer (char *path, int port, char *logfile);
+int    xr_addServerMethod (char *name, void *method, void *userData);
+int    xr_removeServerMethod (char *name);
+void   xr_setServerParam (char *param, void *value);
+pthread_t xr_startServerThread (void);
+void   xr_startServer (void);
+int    xr_shutdownServer (void);
+int    xr_requestAbort (void *data);
+void   xr_setShutdownLevel (int level);
+void   xr_setSigHandler (int sig, sighandler_t handler);
+
+
+/*  xrStruct.c
+*/
+int    xr_newStruct (void);
+void   xr_freeStruct (int snum);
+
+void   xr_printJSONStruct (int snum);
+int    xr_structSize (int snum);
+char  *xr_getStructKey (int snum, int index);
+char  *xr_getStructVal (int snum, int index);
+
+void   xr_setIntInStruct (int snum, char *key, int value);
+void   xr_setDoubleInStruct (int snum, char *key, double value);
+void   xr_setBoolInStruct (int snum, char *key, int value);
+void   xr_setStringInStruct (int snum, char *key, char *value);
+void   xr_setDatetimeInStruct (int snum, char *key, char *value);
+void   xr_setStructInStruct (int snum, char *key, int value);
+void   xr_setArrayInStruct (int snum, char *key, int value);
+
+void   xr_getIntFromStruct (int snum, char *key, int *value);
+void   xr_getDoubleFromStruct (int snum, char *key, double *value);
+void   xr_getBoolFromStruct (int snum, char *key, int *value);
+void   xr_getStringFromStruct (int snum, char *key, char **value);
+void   xr_getDatetimeFromStruct (int snum, char *key, char **value);
+void   xr_getStructFromStruct (int snum, char *key, int *value);
+void   xr_getArrayFromStruct (int snum, char *key, int *value);
+
+xmlrpc_value *xr_getSParam (int snum);
+void   xr_setSParam (int snum, xmlrpc_value *v);
+
+
+/*  xrUtil.c
+*/
+void   xr_setupSigtermHandler (xmlrpc_server_abyss_t *serverP);
+void   xr_svrSigtermHandler (int signalClass);
+void   xr_restoreSigtermHandler (void);
+
+void   xr_setupSigpipeHandlers (void);
+
+char  *xr_getPeerIpAddr (TSession * const abyssSessionP);
+void   xr_dieIfFailed (char *description, xmlrpc_env env);
+void   xr_dbgPrintParams (xmlrpc_server_abyss_parms s);
+void   die_on_error (xmlrpc_env *env);
+void   warn_on_error (xmlrpc_env *env);
+
+
+
+/*  xrValues.c  
+*/
+void   xr_initValues (void);
+int    xr_newValue (int type, void *v);
+int    xr_tmpValue (int type, void *v);
+void   xr_freeValue (int index);
+
+int    xr_appendStruct (int snum, char *key, int value);
+int    xr_appendArray (int anum, char *key, int value);
+
+void   xr_getStructValue (int snum, char *key, void *value);
+void   xr_getArrayValue (int anum, int index, void *value);
diff --git a/Code/src/libwcs/.DS_Store b/Code/src/libwcs/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..ab4c63d55dfbc4252fea52ed4458010320c68157
Binary files /dev/null and b/Code/src/libwcs/.DS_Store differ
diff --git a/Code/src/libwcs/actread.c b/Code/src/libwcs/actread.c
new file mode 100644
index 0000000000000000000000000000000000000000..c3f60b8bc4eb13edb75201f63f37f815071573a5
--- /dev/null
+++ b/Code/src/libwcs/actread.c
@@ -0,0 +1,1199 @@
+/*** File libwcs/actread.c
+ *** January 10, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Copyright (C) 1999-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "fitsfile.h"
+#include "wcs.h"
+#include "wcscat.h"
+
+/* pathname of ACT CDROM or catalog search engine URL */
+char actcd[64]="/data/act";
+
+#define MAXREG 100
+
+static double *gdist;	/* Array of distances to stars */
+static int ndist = 0;
+
+static int actreg();
+struct StarCat *actopen();
+void actclose();
+static int actstar();
+static int actsize();
+static int actsra();
+
+/* ACTREAD -- Read USNO ACT Star Catalog stars from CDROM */
+
+int
+actread (cra,cdec,dra,ddec,drad,dradi,distsort,sysout,eqout,epout,mag1,mag2,
+	 sortmag,nstarmax,gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog)
+
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	distsort;	/* 1 to sort stars by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*gnum;		/* Array of Guide Star numbers (returned) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double  *gpra;          /* Array of right ascension proper motions (returned) */
+double  *gpdec;         /* Array of declination proper motions (returned) */
+double	**gmag;		/* Array of visual magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    double dist = 0.0;  /* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    int	faintstar=0;	/* Faintest star */
+    int	farstar=0;	/* Most distant star */
+    int nreg = 0;	/* Number of ACT regions in search */
+    int rlist[MAXREG];	/* List of input region files */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    double secmarg = 60.0;	/* Arcsec/century margin for proper motion */
+    struct StarCat *starcat;
+    struct Star *star;
+    int verbose;
+    int wrap;
+    int pass;
+    int magsort;
+    int rnum, ireg;
+    int jstar, iw;
+    int nrmax,nstar,i, ntot;
+    int istar, istar1, istar2;
+    double num, ra, dec, rapm, decpm, mag, magb, magv;
+    double rra1, rra2, rra2a, rdec1, rdec2;
+    double rdist, ddist;
+    char *str;
+    char cstr[32], decstr[32], rastr[32];
+    int nbytes;
+
+    ntot = 0;
+    if (nlog == 1)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set path to ACT Catalog */
+    if ((str = getenv("ACT_PATH")) == NULL )
+	str = actcd;
+
+    /* If pathname is a URL, search and return */
+    if (!strncmp (str, "http:",5)) {
+	return (webread (str,"act",distsort,cra,cdec,dra,ddec,drad,dradi,
+			 sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,
+			 gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog));
+	}
+
+    if (sortmag > 0 && sortmag < 3)
+	magsort = sortmag - 1;
+    else 
+	magsort = 1;
+
+    wcscstr (cstr, sysout, eqout, epout);
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+/* make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Allocate table for distances of stars from search center */
+    if (nstarmax > 10)
+	nbytes = nstarmax * sizeof (double);
+    else
+	nbytes = 10 * sizeof (double);
+    
+    if (nstarmax > ndist) {
+	if (ndist > 0)
+	    free ((void *) gdist);
+	gdist = (double *) malloc (nbytes);
+	if (gdist == NULL) {
+	    fprintf (stderr,"ACTREAD:  cannot allocate separation array\n");
+	    return (0);
+	    }
+	ndist = nstarmax;
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    nstar = 0;
+    jstar = 0;
+
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+    if (wrap) {
+	rra2a = rra2;
+	rra2 = 360.0;
+	}
+    else
+	rra2a = 0.0;
+    nrmax = MAXREG;
+
+    /* Write header if printing star entries as found */
+    if (nstarmax < 1) {
+	char *revmessage;
+	revmessage = getrevmsg();
+	printf ("catalog	ACT\n");
+	ra2str (rastr, 31, cra, 3);
+	printf ("ra	%s\n", rastr);
+	dec2str (decstr, 31, cdec, 2);
+	printf ("dec	%s\n", decstr);
+	printf ("rpmunit	mas/year\n");
+	printf ("dpmunit	mas/year\n");
+	if (drad != 0.0) {
+	    printf ("radmin	%.1f\n", drad*60.0);
+	    if (dradi > 0)
+		printf ("radimin	%.1f\n", dradi*60.0);
+	    }
+	else {
+	    printf ("dramin	%.1f\n", dra*60.0 * cosdeg (cdec));
+	    printf ("ddecmin	%.1f\n", ddec*60.0);
+	    }
+	printf ("radecsys	%s\n", cstr);
+	printf ("equinox	%.3f\n", eqout);
+	printf ("epoch	%.3f\n", epout);
+	printf ("program	scat %s\n", revmessage);
+	printf ("act_id    	ra          	dec         	");
+	printf ("magb 	magv 	ura   	udec  	arcmin\n");
+	printf ("----------	------------	------------	");
+	printf ("-----	-----	------	------	------\n");
+	}
+
+    /* If searching through RA = 0:00, split search in two */
+    for (iw = 0; iw <= wrap; iw++) {
+
+	/* Find ACT Star Catalog regions in which to search */
+	nreg = actreg (rra1,rra2,rdec1,rdec2,nrmax,rlist,verbose);
+	if (nreg <= 0) {
+	    fprintf (stderr,"ACTREAD:  no ACT regions found\n");
+	    free ((void *)star);
+	    return (0);
+	    }
+
+	/* Loop through region list */
+	for (ireg = 0; ireg < nreg; ireg++) {
+
+	    /* Open catalog file for this region */
+	    starcat = actopen (rlist[ireg]);
+	    rnum = rlist[ireg];
+
+	    /* Set first and last stars to check */
+	    istar1 = actsra (starcat, star, rra1);
+	    istar2 = actsra (starcat, star, rra2);
+	    if (verbose)
+		fprintf (stderr,"ACTREAD: Searching stars %d.%d through %d.%d\n",
+			rnum,istar1,rnum,istar2);
+
+	    /* Loop through catalog for this region */
+	    for (istar = istar1; istar <= istar2; istar++) {
+		if (actstar (starcat, star, istar)) {
+		    fprintf (stderr,"ACTREAD: Cannot read star %d\n", istar);
+		    break;
+		    }
+
+		/* Magnitude */
+		magv = star->xmag[0];
+		magb = star->xmag[1];
+		mag = star->xmag[magsort];
+
+		/* Check magnitude limits */
+		pass = 1;
+		if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+		    pass = 0;
+
+		/* Get position in output coordinate system */
+		if (pass) {
+		    rapm = star->rapm;
+		    decpm = star->decpm;
+		    ra = star->ra;
+		    dec = star->dec;
+		    wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+			     &ra, &dec, &rapm, &decpm);
+
+		    /* Compute distance from search center */
+		    if (drad > 0 || distsort)
+			dist = wcsdist (cra,cdec,ra,dec);
+		    else
+			dist = 0.0;
+
+		    /* Check radial distance to search center */
+		    if (drad > 0) {
+			if (dist > drad)
+			    pass = 0;
+			if (dradi > 0.0 && dist < dradi)
+			    pass = 0;
+			}
+
+		    /* Check distance along RA and Dec axes */
+		    else {
+			ddist = wcsdist (cra,cdec,cra,dec);
+			if (ddist > ddec)
+			    pass = 0;
+			rdist = wcsdist (cra,dec,ra,dec);
+		        if (rdist > dra)
+			   pass = 0;
+			}
+		    }
+
+		if (pass) {
+
+		    /* ID number */
+		    num = (double) rlist[ireg] + (star->num / 100000.0);
+
+		    /* Write star position and magnitudes to stdout */
+		    if (nstarmax < 1) {
+			ra2str (rastr, 31, ra, 3);
+			dec2str (decstr, 31, dec, 2);
+			dist = wcsdist (cra,cdec,ra,dec) * 60.0;
+			printf ("%010.5f	%s	%s", num,rastr,decstr);
+			printf ("	%.2f	%.2f	%6.1f	%6.1f	%.2f\n",
+				magb, magv,
+				gpra[nstar] * 3600000.0 * cosdeg(dec),
+				gpdec[nstar] * 3600000.0, dist / 60.0);
+			}
+
+		    /* Save star position and magnitude in table */
+		    if (nstar < nstarmax) {
+			gnum[nstar] = num;
+			gra[nstar] = ra;
+			gdec[nstar] = dec;
+			gpra[nstar] = rapm;
+			gpdec[nstar] = decpm;
+			gmag[0][nstar] = magb;
+			gmag[1][nstar] = magv;
+			gdist[nstar] = dist;
+			if (dist > maxdist) {
+			    maxdist = dist;
+			    farstar = nstar;
+			    }
+			if (mag > faintmag) {
+			    faintmag = mag;
+			    faintstar = nstar;
+			    }
+			}
+
+		    /* If too many stars and distance sorting,
+		       replace farthest star */
+		    else if (distsort) {
+			if (dist < maxdist) {
+			    gnum[farstar] = num;
+			    gra[farstar] = ra;
+			    gdec[farstar] = dec;
+			    gpra[farstar] = rapm;
+			    gpdec[farstar] = decpm;
+			    gmag[0][farstar] = magb;
+			    gmag[1][farstar] = magv;
+			    gdist[farstar] = dist;
+
+			    /* Find new farthest star */
+			    maxdist = 0.0;
+			    for (i = 0; i < nstarmax; i++) {
+				if (gdist[i] > maxdist) {
+				    maxdist = gdist[i];
+				    farstar = i;
+				    }
+				}
+			    }
+			}
+
+		    /* Else if too many stars, replace faintest star */
+		    else if (mag < faintmag) {
+			gnum[faintstar] = num;
+			gra[faintstar] = ra;
+			gdec[faintstar] = dec;
+			gpra[farstar] = rapm;
+			gpdec[farstar] = decpm;
+			gmag[0][faintstar] = magb;
+			gmag[1][faintstar] = magv;
+			gdist[faintstar] = dist;
+			faintmag = 0.0;
+
+			/* Find new faintest star */
+			for (i = 0; i < nstarmax; i++) {
+			    if (gmag[magsort][i] > faintmag) {
+				faintmag = gmag[magsort][i];
+				faintstar = i;
+				}
+			    }
+			}
+
+		    nstar++;
+		    if (nlog == 1)
+			fprintf (stderr,"ACTREAD: %11.6f: %9.5f %9.5f %5.2f %5.2f\n",
+				 num,ra,dec,magb,mag);
+
+		    /* End of accepted star processing */
+		    }
+
+		/* Log operation */
+		jstar++;
+		if (nlog > 0 && istar%nlog == 0)
+		    fprintf (stderr,"ACTREAD: %5d / %5d / %5d sources\r",
+			     nstar,jstar,starcat->nstars);
+
+		/* End of star loop */
+		}
+
+	    ntot = ntot + starcat->nstars;
+	    if (nlog > 0)
+		fprintf (stderr,"ACTREAD: %4d / %4d: %5d / %5d  / %5d sources from region %4d    \n",
+		 	 ireg+1,nreg,nstar,jstar,starcat->nstars,rlist[ireg]);
+
+	    /* Close region input file */
+	    actclose (starcat);
+	    }
+	rra1 = 0.0;
+	rra2 = rra2a;
+	}
+
+/* close output file and summarize transfer */
+    if (nlog > 0) {
+	if (nreg > 1)
+	    fprintf (stderr,"ACTREAD: %d regions: %d / %d found\n",nreg,nstar,ntot);
+	else
+	    fprintf (stderr,"ACTREAD: 1 region: %d / %d found\n",nstar,ntot);
+	if (nstar > nstarmax)
+	    fprintf (stderr,"ACTREAD: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+    free ((void *)star);
+    return (nstar);
+}
+
+/* ACTRNUM -- Read HST Guide Star Catalog stars from CDROM */
+
+int
+actrnum (nstars,sysout,eqout,epout,
+	 gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog)
+
+int	nstars;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*gnum;		/* Array of Guide Star numbers (returned) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double  *gpra;          /* Array of right ascension proper motions (returned) */
+double  *gpdec;         /* Array of declination proper motions (returned) */
+double	**gmag;		/* Array of V, B magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog equinox */
+    double epref;	/* Catalog epoch */
+    struct StarCat *starcat = NULL;
+    struct Star *star;
+
+    int rnum;
+    int jstar;
+    int istar, nstar, snum;
+    double num, ra, dec, rapm, decpm, mag, magb;
+    char *str;
+
+    /* Set path to ACT Catalog */
+    if ((str = getenv("ACT_PATH")) == NULL )
+	str = actcd;
+
+    /* If pathname is a URL, search and return */
+    if (!strncmp (str, "http:",5)) {
+	return (webrnum (str,"act",nstars, sysout,eqout,epout, 1,
+			 gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog));
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    nstar = 0;
+
+/* Loop through star list */
+    for (jstar = 0; jstar < nstars; jstar++) {
+	rnum = (int) gnum[jstar];
+	snum = (int) (((gnum[jstar] - (double)rnum) * 100000.0) + 0.01);
+
+	/* Open file for this region of ACT catalog */
+	starcat = actopen (rnum);
+	if (starcat == NULL) {
+	    free ((void*) star);
+	    return (0);
+	    }
+
+	sysref = starcat->coorsys;
+	eqref = starcat->equinox;
+	epref = starcat->epoch;
+
+	/* Find star in catalog */
+	istar = snum;
+	if (actstar (starcat, star, istar)) {
+	    fprintf (stderr,"ACTRNUM: Cannot read star %d\n", istar);
+	    gra[nstar] = 0.0;
+	    gdec[nstar] = 0.0;
+	    gmag[0][nstar] = 0.0;
+	    gmag[1][nstar] = 0.0;
+	    gtype[nstar] = 0;
+	    continue;
+	    }
+
+	/* If star has been found in catalog */
+
+	/* ID number */
+	num = star->num;
+
+	/* Position in degrees at designated epoch */
+	ra = star->ra;
+	dec = star->dec;
+	rapm = star->rapm;
+	decpm = star->decpm;
+	wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		     &ra, &dec, &rapm, &decpm);
+
+	/* Magnitude */
+	mag = star->xmag[0];
+	magb = star->xmag[1];
+
+	/* Save star position and magnitude in table */
+	gra[nstar] = ra;
+	gdec[nstar] = dec;
+	gpra[nstar] = rapm;
+	gpdec[nstar] = decpm;
+	gmag[0][nstar] = magb;
+	gmag[1][nstar] = mag;
+	nstar++;
+	if (nlog == 1)
+	    fprintf (stderr,"ACTRNUM: %11.6f: %9.5f %9.5f %5.2f %5.2f \n",
+		     num, ra, dec, magb, mag);
+
+	/* End of star loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"ACTRNUM: %d / %d found\n",nstar,nstars);
+
+    if (starcat != NULL)
+	actclose (starcat);
+    free ((void*) star);
+    return (nstar);
+}
+
+
+/* ACTBIN -- Fill FITS WCS image with USNO ACT Star Catalog stars */
+
+int
+actbin (wcs, header, image, mag1, mag2, sortmag, magscale, nlog)
+
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    int nreg = 0;	/* Number of ACT regions in search */
+    int rlist[MAXREG];	/* List of input region files */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    double secmarg = 60.0;	/* Arcsec/century margin for proper motion */
+    struct StarCat *starcat;
+    struct Star *star;
+    int verbose;
+    int wrap;
+    int pass;
+    int magsort;
+    int rnum, ireg;
+    int ix, iy;
+    int jstar, iw;
+    int nrmax,nstar, ntot;
+    int istar, istar1, istar2;
+    double num, ra, dec, rapm, decpm, mag, magb, magv;
+    double rra1, rra2, rra2a, rdec1, rdec2;
+    double rdist, ddist;
+    char *str;
+    char cstr[32];
+    double xpix, ypix, flux;
+    int offscl;
+    int bitpix, w, h;   /* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+
+    ntot = 0;
+    if (nlog == 1)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set path to ACT Catalog */
+    str = getenv("ACT_PATH");
+
+    if (sortmag > 0 && sortmag < 3)
+	magsort = sortmag - 1;
+    else 
+	magsort = 1;
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* If RA range includes zero, split it in two */
+    wrap = 0;
+    if (ra1 > ra2)
+	wrap = 1;
+    else
+	wrap = 0;
+
+    /* make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    nstar = 0;
+    jstar = 0;
+
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+    if (wrap) {
+	rra2a = rra2;
+	rra2 = 360.0;
+	}
+    else
+	rra2a = 0.0;
+
+    nrmax = MAXREG;
+
+    /* If searching through RA = 0:00, split search in two */
+    for (iw = 0; iw <= wrap; iw++) {
+
+	/* Find ACT Star Catalog regions in which to search */
+	nreg = actreg (rra1,rra2,rdec1,rdec2,nrmax,rlist,verbose);
+	if (nreg <= 0) {
+	    fprintf (stderr,"ACTBIN:  no ACT regions found\n");
+	    free ((void *)star);
+	    return (0);
+	    }
+
+	/* Loop through region list */
+	for (ireg = 0; ireg < nreg; ireg++) {
+
+	    /* Open catalog file for this region */
+	    starcat = actopen (rlist[ireg]);
+	    rnum = rlist[ireg];
+
+	    /* Set first and last stars to check */
+	    istar1 = actsra (starcat, star, rra1);
+	    istar2 = actsra (starcat, star, rra2);
+	    if (verbose)
+		fprintf (stderr,"ACTBIN: Searching stars %d.%d through %d.%d\n",
+			rnum,istar1,rnum,istar2);
+
+	    /* Loop through catalog for this region */
+	    for (istar = istar1; istar <= istar2; istar++) {
+		if (actstar (starcat, star, istar)) {
+		    fprintf (stderr,"ACTBIN: Cannot read star %d\n", istar);
+		    break;
+		    }
+
+		/* ID number */
+		num = star->num;
+
+		/* Magnitude */
+		magv = star->xmag[0];
+		magb = star->xmag[1];
+		mag = star->xmag[magsort];
+
+		/* Check magnitude limits */
+		pass = 1;
+		if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+		    pass = 0;
+
+		/* Get position in output coordinate system */
+		if (pass) {
+		    rapm = star->rapm;
+		    decpm = star->decpm;
+		    ra = star->ra;
+		    dec = star->dec;
+		    wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+			     &ra, &dec, &rapm, &decpm);
+
+		    /* Check distance along RA and Dec axes */
+		    ddist = wcsdist (cra,cdec,cra,dec);
+		    if (ddist > ddec)
+			pass = 0;
+		    rdist = wcsdist (cra,dec,ra,dec);
+		    if (rdist > dra)
+			pass = 0;
+		    }
+
+		/* Save star in FITS image */
+		if (pass) {
+		    wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+		    if (!offscl) {
+			if (magscale > 0.0)
+			    flux = magscale * exp (logt * (-mag / 2.5));
+			else
+			    flux = 1.0;
+			ix = (int) (xpix + 0.5);
+			iy = (int) (ypix + 0.5);
+			addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+			nstar++;
+			jstar++;
+			}
+		    else {
+			ix = 0;
+			iy = 0;
+			}
+		    if (nlog == 1) {
+			fprintf (stderr,"TABBIN: %11.6f: %9.5f %9.5f %s",
+				 num,ra,dec,cstr);
+			if (magscale > 0.0)
+			    fprintf (stderr, " %5.2f", mag);
+			if (!offscl)
+			    flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+			else
+			    flux = 0.0;
+			fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+			}
+
+		    /* End of accepted star processing */
+		    }
+
+		/* Log operation */
+		jstar++;
+		if (nlog > 0 && istar%nlog == 0)
+		    fprintf (stderr,"ACTBIN: %5d / %5d / %5d sources\r",
+			     nstar,jstar,starcat->nstars);
+
+		/* End of star loop */
+		}
+
+	    ntot = ntot + starcat->nstars;
+	    if (nlog > 0)
+		fprintf (stderr,"ACTBIN: %4d / %4d: %5d / %5d  / %5d sources from region %4d    \n",
+		 	 ireg+1,nreg,nstar,jstar,starcat->nstars,rlist[ireg]);
+
+	    /* Close region input file */
+	    actclose (starcat);
+	    }
+	rra1 = 0.0;
+	rra2 = rra2a;
+	}
+
+/* close output file and summarize transfer */
+    if (nlog > 0) {
+	if (nreg > 1)
+	    fprintf (stderr,"ACTBIN: %d regions: %d / %d found\n",nreg,nstar,ntot);
+	else
+	    fprintf (stderr,"ACTBIN: 1 region: %d / %d found\n",nstar,ntot);
+	}
+    free ((void *)star);
+    return (nstar);
+}
+
+
+/* ACTREG -- from RA and Dec ranges, figure out which ACT files to search
+ * Build a list containing the numeric part of the CDROM file names.
+ */
+
+static int regions[48]={   0,  30, 100, 130, 200, 230, 300, 330, 400, 430,
+			 500, 530, 600, 630, 700, 730, 800, 830, 900, 930,
+			1000,1030,1100,1130,1200,1230,1300,1330,1400,1430,
+			1500,1530,1600,1630,1700,1730,1800,1830,1900,1930,
+			2000,2030,2100,2130,2200,2230,2300,2330};
+static double reghour[49]={ 0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5,
+			    5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5,
+			   10.0,10.5,11.0,11.5,12.0,12.5,13.0,13.5,14.0,14.5,
+			   15.0,15.5,16.0,16.5,17.0,17.5,18.0,18.5,19.0,19.5,
+			   20.0,20.5,21.0,21.5,22.0,22.5,23.0,23.5,24.0};
+
+static int
+actreg (ra1, ra2, dec1, dec2, nrmax, rgns, verbose)
+
+double	ra1, ra2;	/* Right ascension limits in degrees */
+double	dec1, dec2; 	/* Declination limits in degrees */
+int	nrmax;		/* Maximum number of regions to find */
+int	*rgns;		/* Region numbers (returned)*/
+int	verbose;	/* 1 for diagnostics */
+
+{
+    int nsrch;		/* Number of regions found (returned) */
+
+    int i, ir, irx;
+    int ir1 = 0;
+    int ir2 = 0;
+
+    /* Zero out regions to be searched */
+    for (i = 0; i < nrmax; i++)
+	rgns[i] = 0;
+
+    /* Find region range to search based on declination */
+
+    /* Find RA regions to search */
+    ra1 = ra1 / 15.0;
+    ra2 = ra2 / 15.0;
+    irx = 0;
+
+    /* Find first region to search */
+    for (ir = 1; ir < 49; ir++) {
+	if (ra1 >= reghour[ir-1] && ra1 <= reghour[ir]) {
+	    ir1 = ir - 1;
+	    break;
+	    }
+	}
+
+    /* Find last region to search */
+    for (ir = 1; ir < 49; ir++) {
+	if (ra2 >= reghour[ir-1] && ra2 <= reghour[ir]) {
+	    ir2 = ir - 1;
+	    break;
+	    }
+	}
+
+    if (ir2 >= ir1) {
+	for (ir = ir1; ir <= ir2; ir++) {
+	    if (irx < nrmax)
+		rgns[irx++] = regions[ir];
+	    }
+	}
+    else if (ir2 < ir1) {
+	for (ir = ir1; ir < 48; ir++) {
+	    if (irx < nrmax)
+		rgns[irx++] = regions[ir];
+	    }
+	for (ir = 0; ir <= ir2; ir++) {
+	    if (irx < nrmax)
+		rgns[irx++] = regions[ir];
+	    }
+	}
+    nsrch = irx;
+
+    if (verbose) {
+	fprintf (stderr,"ACTREG: RA: %.5f - %.5f, Dec: %.5f - %.5f\n",
+		 ra1,ra2,dec1,dec2);
+	fprintf (stderr,"ACTREG: searching %d regions:",nsrch);
+	for (ir = 0; ir < nsrch; ir++)
+	    fprintf (stderr," %04d",rgns[ir]);
+	fprintf (stderr,"\n");
+	}
+    return (nsrch);
+}
+
+
+/* ACTOPEN -- Open ACT catalog region file, returning number of entries */
+
+struct StarCat *
+actopen (regnum)
+
+int regnum;	/* ACT Catalog region number */
+
+{
+    FILE *fcat;
+    struct StarCat *sc;
+    int lfile, lpath;
+    char *actfile;
+    char *path;		/* Full pathname for catalog file */
+    char *cdpath;
+
+    /* Set the pathname using the appropriate ACT CDROM directory */
+    if ((cdpath = getenv("ACT_PATH")) == NULL )
+	cdpath = actcd;
+    lpath = strlen (cdpath) + 32;
+    path = (char *) calloc (lpath, 1);
+
+    /* Declination zoned regions */
+    if (regnum > 0 && regnum < 5)
+	sprintf (path,"%s/data2/act%1d.dat", cdpath, regnum);
+
+    /* Right ascension zoned regions */
+    else
+	sprintf (path,"%s/data1/act%04d.dat", cdpath, regnum);
+
+    /* Find length of ACT catalog region file */
+    lfile = actsize (path);
+
+    /* Check for existence of catalog */
+    if (lfile < 2) {
+	fprintf (stderr,"ACTOPEN: Binary catalog %s has no entries\n", path);
+	free (path);
+	return (0);
+	}
+
+    /* Open ACT region file */
+    if (!(fcat = fopen (path, "r"))) {
+	fprintf (stderr,"ACTOPEN: ACT region file %s cannot be read\n",path);
+	free (path);
+	return (0);
+	}
+
+    /* Set ACT catalog header information */
+    sc = (struct StarCat *) calloc (1, sizeof (struct StarCat));
+    sc->byteswapped = 0;
+
+    sc->nbent = 161;
+    sc->nstars = lfile / sc->nbent;
+
+    /* Separate filename from pathname and save in structure */
+    actfile = strrchr (path,'/');
+    if (actfile)
+	actfile = actfile + 1;
+    else
+	actfile = path;
+    if (strlen (actfile) < 24)
+	strcpy (sc->isfil, actfile);
+    else
+	strncpy (sc->isfil, actfile, 23);
+
+    /* Set other catalog information in structure */
+    sc->inform = 'J';
+    sc->coorsys = WCS_J2000;
+    sc->epoch = 2000.0;
+    sc->equinox = 2000.0;
+    sc->ifcat = fcat;
+    sc->sptype = 2;
+
+    /* ACT region files are all RA-sorted */
+    sc->rasorted = 1;
+
+    return (sc);
+}
+
+
+void
+actclose (sc)
+struct StarCat *sc;	/* Star catalog descriptor */
+{
+    fclose (sc->ifcat);
+    free ((void *)sc);
+    return;
+}
+
+
+/* ACTSRA -- Find star closest to given RA in ACT catalog file */
+
+static int
+actsra (sc, st, dra)
+
+struct StarCat *sc;	/* Star catalog descriptor */
+struct Star *st;	/* Current star entry */
+double	dra;		/* Right ascension in degrees */
+
+{
+    char rastr[32], raxstr[32], ramins[32], ramaxs[32];
+    int istar0, istarx, nrep, ismin, ismax;
+    double rax, ramin, ramax;
+    int verbose = 0;
+
+    /* Keep RA between 0 and 360 degrees */
+    if (dra > 360.0)
+	rax = dra - 360.0;
+    else
+	rax = dra;
+
+    ismin = 1;
+    if (actstar (sc, st, ismin)) {
+	fprintf (stderr,"ACTSRA: Cannot read star %d\n", ismin);
+	return (0);
+	}
+    else
+	ramin = st->ra;
+
+    ismax = sc->nstars;
+    if (actstar (sc, st, ismax)) {
+	fprintf (stderr,"ACTSRA: Cannot read star %d\n", ismax);
+	return (0);
+	}
+    else
+	ramax = st->ra;
+
+    istarx = sc->nstars / 2;
+
+    for (nrep = 0; nrep < 32; nrep++) {
+	if (actstar (sc, st, istarx)) {
+	    fprintf (stderr,"ACTSRA: Cannot read star %d\n", istarx);
+            return (0);
+	    }
+
+	/* Find next catalog number to read */
+	if (st->ra < rax) {
+	    ismin = istarx;
+	    ramin = st->ra;
+	    istar0 = istarx;
+	    if (ismax - istarx > 1)
+		istarx = istarx + (ismax - istarx) / 2;
+	    else if (ismax - istarx > 0)
+		istarx = istarx + 1;
+	    }
+	else if (st->ra > rax) {
+	    ismax = istarx;
+	    ramax = st->ra;
+	    istar0 = istarx;
+	    if (istarx - ismin > 1)
+		istarx = istarx - ((istarx - ismin) / 2);
+	    else if (istarx - ismin > 0)
+		istarx = istarx - 1;
+	    }
+	else
+	    break;
+
+	if (verbose) {
+	    ra2str (rastr, 16, st->ra, 3);
+	    ra2str (raxstr, 16, rax, 3);
+	    ra2str (ramins, 16, ramin, 3);
+	    ra2str (ramaxs, 16, ramax, 3);
+	    fprintf (stderr,"%9d: %s -> %s  %9d: %s  %9d: %s\n",
+		    istarx, rastr, raxstr, ismin,ramins,ismax,ramaxs);
+	    }
+	if (istarx == istar0)
+	    break;
+	}
+
+    /* Make sure final star is real */
+    if (actstar (sc, st, istarx)) {
+	fprintf (stderr,"ACTSRA: Cannot read star %d\n", istarx);
+        return (0);
+	}
+    else
+	return (istarx);
+}
+
+
+/* ACTSTAR -- Get ACT catalog entry for one star;
+              return 0 if successful */
+
+static int
+actstar (sc, st, istar)
+
+struct StarCat *sc;	/* Star catalog descriptor */
+struct Star *st;	/* Current star entry */
+int istar;	/* Star sequence number in ACT catalog region file */
+{
+    int nbr;
+    long offset;
+    char dsgn;
+    char line[256];
+    int irh,irm,idd,idm;
+    double rs, ds;
+/*    double bvmag; */
+
+    /* Drop out if catalog pointer is not set */
+    if (sc == NULL)
+	return (1);
+
+    /* Drop out if catalog is not open */
+    if (sc->ifcat == NULL)
+	return (2);
+
+    /* Drop out if star number is too large */
+    if (istar > sc->nstars) {
+	fprintf (stderr, "ACTSTAR:  %d  > %d is not in catalog\n",
+		 istar, sc->nstars);
+	return (3);
+	}
+
+    /* Move file pointer to start of correct star entry */
+    if (istar > 0) {
+	offset = (istar - 1) * sc->nbent;
+	if (fseek (sc->ifcat, offset, SEEK_SET))
+	    return (4);
+	}
+
+    /* Read catalog entry */
+    if ((nbr = fread (line, sc->nbent, 1, sc->ifcat)) > sc->nbent) {
+	fprintf (stderr, "ACTSTAR:  %d / %d bytes read from %s\n",
+		 nbr, sc->nbent, sc->isfil);
+	return (5);
+	}
+
+    st->num = (double) istar;
+
+    /* Read position for this star */
+    irh = atoi (line);
+    irm = atoi (line+3);
+    rs = atof (line+6);
+    dsgn = line[14];
+    idd = atoi (line+15);
+    idm = atoi (line+18);
+    ds = atof (line+21);
+
+    /* Convert position to degrees */
+    st->ra = hrdeg ((double)irh + ((double)irm)/60.0 + rs / 3600.0);
+    st->dec = (double) idd + ((double)idm) / 60.0 + ds / 3600.0;
+    if (dsgn == '-') st->dec = -st->dec;
+
+    /* Read proper motion and convert it to to degrees/year */
+    st->rapm = hrdeg (atof (line+28)) / 3600.0;
+    st->decpm = atof (line+36) / 3600.0;
+
+    /* Set V, B, B-V magnitudes */
+    st->xmag[0] = atof (line+75);
+    st->xmag[1] = atof (line+68);
+    st->xmag[2] = atof (line+82);
+    st->isp[0] = (char) 0;
+    st->isp[1] = (char) 0;
+
+    return (0);
+}
+
+/* ACTSIZE -- return size of one ACT catalog file in bytes */
+
+static int
+actsize (filename)
+
+char	*filename;	/* Name of file for which to find size */
+{
+    FILE *diskfile;
+    long filesize;
+
+    /* Open file */
+    if ((diskfile = fopen (filename, "r")) == NULL)
+	return (-1);
+
+    /* Move to end of the file */
+    if (fseek (diskfile, 0, 2) == 0)
+
+	/* Position is the size of the file */
+	filesize = ftell (diskfile);
+
+    else
+	filesize = -1;
+
+    fclose (diskfile);
+
+    return (filesize);
+}
+
+
+/* Feb 11 1999	New program
+ * Apr 13 1999	Fix bugs which caused failure on crossing 0:00 h
+ * May 12 1999	Fix bug for all searches
+ * May 21 1999	Fix bug with proper motion so it is in deg/yr, not sec/yr
+ * Jun 16 1999	Use SearchLim()
+ * Aug 16 1999	Add RefLim() to get converted search coordinates right
+ * Aug 25 1999	Return real number of stars from actread()
+ * Sep 16 1999	Fix bug which didn't always return closest stars
+ * Sep 16 1999	Add distsort argument so brightest stars in circle works, too
+ * Oct 21 1999	Delete unused varaiables after lint
+ *
+ * Jan  5 2000	Add 2 to spectral type string so there can be proper termination
+ * Mar 15 2000	Add proper motions to returns from actread() and actrnum()
+ * May 31 2000	Get spectral type from bv2sp()
+ * Jun  2 2000	Free all allocated data structures
+ * Jun  9 2000	Fix bug which caused memory overflow if limiting number
+ * Jun 26 2000	Add coordinate system to SearchLim() arguments
+ * Sep 25 2000	Set sc->sptype to 2 to indicate presence of spectral type
+ * Nov 29 2000	Add option to read catalog using HTTP
+ * Dec 11 2000	Allow catalog search engine URL in actcd[]
+ *
+ * Jan 11 2001	All printing is to stderr
+ * Jun 14 2001	Drop spectral type approximation
+ * Sep 11 2001	Change to single magnitude argeument
+ * Sep 11 2001	Add sort magnitude argument to actread()
+ *
+ * Apr  8 2002	Fix extraneous declaration of actsize()
+ * Oct  2 2002	Print stars as found in actread() if nstarmax < 1
+ *
+ * Mar 11 2003	Fix position limit testing
+ * Apr  3 2003	Drop unused type variables in actstar() and actread()
+ * Apr 14 2003	Explicitly get revision date if nstarmax < 1
+ * Jun  2 2003	Print proper motion as mas/year
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Aug 22 2003	Fix bug in actrnum: mags were not in one array
+ * Sep 25 2003	Add actbin() to fill an image with sources
+ * Oct  6 2003	Update actread() and actbin() for improved RefLim()
+ * Nov 18 2003	Initialize image size and bits/pixel from header in actbin()
+ * Dec  1 2003	Add missing tab to n=-1 header
+ * Dec 12 2003	Fix bug in wcs2pix() call in actbin()
+ *
+ * Aug 30 2004	Include math.h
+ *
+ * Jun 20 2006	Initialize uninitialized variables
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ * Nov 16 2006	Fix binning
+ *
+ * Jan 10 2007	Add match=1 argument to webrnum()
+ * Jan 10 2007	Rewrite web access in actread() and actrnum() to reduce code
+ */
diff --git a/Code/src/libwcs/ang2str.c b/Code/src/libwcs/ang2str.c
new file mode 100644
index 0000000000000000000000000000000000000000..68c860f92c5cf001a7b078ae37979095839f72b9
--- /dev/null
+++ b/Code/src/libwcs/ang2str.c
@@ -0,0 +1,373 @@
+
+
+/* Write the right ascension ra in sexagesimal format into string*/
+
+void
+ra2str (string, lstr, ra, ndec)
+
+char	*string;	/* Character string (returned) */
+int	lstr;		/* Maximum number of characters in string */
+double	ra;		/* Right ascension in degrees */
+int	ndec;		/* Number of decimal places in seconds */
+
+{
+    double a,b;
+    double seconds;
+    char tstring[64];
+    int hours;
+    int minutes;
+    int isec, ltstr;
+    double dsgn;
+
+    /* Keep RA between 0 and 360 */
+    if (ra < 0.0 ) {
+	ra = -ra;
+	dsgn = -1.0;
+	}
+    else
+	dsgn = 1.0;
+    ra = fmod(ra, 360.0);
+    ra *= dsgn;
+    if (ra < 0.0)
+	ra = ra + 360.0;
+
+    a = ra / 15.0;
+
+    /* Convert to hours */
+    hours = (int) a;
+
+    /* Compute minutes */
+    b =  (a - (double)hours) * 60.0;
+    minutes = (int) b;
+
+    /* Compute seconds */
+    seconds = (b - (double)minutes) * 60.0;
+
+    if (ndec > 5) {
+	if (seconds > 59.999999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%09.6f",hours,minutes,seconds);
+	}
+    else if (ndec > 4) {
+	if (seconds > 59.99999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%08.5f",hours,minutes,seconds);
+	}
+    else if (ndec > 3) {
+	if (seconds > 59.9999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%07.4f",hours,minutes,seconds);
+	}
+    else if (ndec > 2) {
+	if (seconds > 59.999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%06.3f",hours,minutes,seconds);
+	}
+    else if (ndec > 1) {
+	if (seconds > 59.99) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%05.2f",hours,minutes,seconds);
+	}
+    else if (ndec > 0) {
+	if (seconds > 59.9) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%04.1f",hours,minutes,seconds);
+	}
+    else {
+	isec = (int)(seconds + 0.5);
+	if (isec > 59) {
+	    isec = 0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%02d",hours,minutes,isec);
+	}
+
+    /* Move formatted string to returned string */
+    ltstr = (int) strlen (tstring);
+    if (ltstr < lstr-1)
+	strcpy (string, tstring);
+    else {
+	strncpy (string, tstring, lstr-1);
+	string[lstr-1] = 0;
+	}
+    return;
+}
+
+
+/* Write the variable a in sexagesimal format into string */
+
+void
+dec2str (string, lstr, dec, ndec)
+
+char	*string;	/* Character string (returned) */
+int	lstr;		/* Maximum number of characters in string */
+double	dec;		/* Declination in degrees */
+int	ndec;		/* Number of decimal places in arcseconds */
+
+{
+    double a, b, dsgn, deg1;
+    double seconds;
+    char sign;
+    int degrees;
+    int minutes;
+    int isec, ltstr;
+    char tstring[64];
+
+    /* Keep angle between -180 and 360 degrees */
+    deg1 = dec;
+    if (deg1 < 0.0 ) {
+	deg1 = -deg1;
+	dsgn = -1.0;
+	}
+    else
+	dsgn = 1.0;
+    deg1 = fmod(deg1, 360.0);
+    deg1 *= dsgn;
+    if (deg1 <= -180.0)
+	deg1 = deg1 + 360.0;
+
+    a = deg1;
+
+    /* Set sign and do all the rest with a positive */
+    if (a < 0) {
+	sign = '-';
+	a = -a;
+	}
+    else
+	sign = '+';
+
+    /* Convert to degrees */
+    degrees = (int) a;
+
+    /* Compute minutes */
+    b =  (a - (double)degrees) * 60.0;
+    minutes = (int) b;
+
+    /* Compute seconds */
+    seconds = (b - (double)minutes) * 60.0;
+
+    if (ndec > 5) {
+	if (seconds > 59.999999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%09.6f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 4) {
+	if (seconds > 59.99999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%08.5f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 3) {
+	if (seconds > 59.9999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%07.4f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 2) {
+	if (seconds > 59.999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%06.3f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 1) {
+	if (seconds > 59.99) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%05.2f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 0) {
+	if (seconds > 59.9) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%04.1f",sign,degrees,minutes,seconds);
+	}
+    else {
+	isec = (int)(seconds + 0.5);
+	if (isec > 59) {
+	    isec = 0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%02d",sign,degrees,minutes,isec);
+	}
+
+    /* Move formatted string to returned string */
+    ltstr = (int) strlen (tstring);
+    if (ltstr < lstr-1)
+	strcpy (string, tstring);
+    else {
+	strncpy (string, tstring, lstr-1);
+	string[lstr-1] = 0;
+	}
+   return;
+}
+
+
+/* Write the angle a in decimal format into string */
+
+void
+deg2str (string, lstr, deg, ndec)
+
+char	*string;	/* Character string (returned) */
+int	lstr;		/* Maximum number of characters in string */
+double	deg;		/* Angle in degrees */
+int	ndec;		/* Number of decimal places in degree string */
+
+{
+    char degform[8];
+    int field, ltstr;
+    char tstring[64];
+    double deg1;
+    double dsgn;
+
+    /* Keep angle between -180 and 360 degrees */
+    deg1 = deg;
+    if (deg1 < 0.0 ) {
+	deg1 = -deg1;
+	dsgn = -1.0;
+	}
+    else
+	dsgn = 1.0;
+    deg1 = fmod(deg1, 360.0);
+    deg1 *= dsgn;
+    if (deg1 <= -180.0)
+	deg1 = deg1 + 360.0;
+
+    /* Write angle to string, adding 4 digits to number of decimal places */
+    field = ndec + 4;
+    if (ndec > 0) {
+	sprintf (degform, "%%%d.%df", field, ndec);
+	sprintf (tstring, degform, deg1);
+	}
+    else {
+	sprintf (degform, "%%%4d", field);
+	sprintf (tstring, degform, (int)deg1);
+	}
+
+    /* Move formatted string to returned string */
+    ltstr = (int) strlen (tstring);
+    if (ltstr < lstr-1)
+	strcpy (string, tstring);
+    else {
+	strncpy (string, tstring, lstr-1);
+	string[lstr-1] = 0;
+	}
+    return;
+}
+
+
+/* Write the variable a in decimal format into field-character string  */
+
+void
+num2str (string, num, field, ndec)
+
+char	*string;	/* Character string (returned) */
+double	num;		/* Number */
+int	field;		/* Number of characters in output field (0=any) */
+int	ndec;		/* Number of decimal places in degree string */
+
+{
+    char numform[8];
+
+    if (field > 0) {
+	if (ndec > 0) {
+	    sprintf (numform, "%%%d.%df", field, ndec);
+	    sprintf (string, numform, num);
+	    }
+	else {
+	    sprintf (numform, "%%%dd", field);
+	    sprintf (string, numform, (int)num);
+	    }
+	}
+    else {
+	if (ndec > 0) {
+	    sprintf (numform, "%%.%df", ndec);
+	    sprintf (string, numform, num);
+	    }
+	else {
+	    sprintf (string, "%d", (int)num);
+	    }
+	}
+    return;
+}
diff --git a/Code/src/libwcs/binread.c b/Code/src/libwcs/binread.c
new file mode 100644
index 0000000000000000000000000000000000000000..59bfea677f63b811a2dd9dc5ddb18636371f6a13
--- /dev/null
+++ b/Code/src/libwcs/binread.c
@@ -0,0 +1,1598 @@
+/*** File libwcs/binread.c
+ *** September 25, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1998-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* int binread()	Read binary catalog sources + names in specified region
+ * int binrnum()	Read binary catalog sources + names with specified numbers
+ * int binopen()	Open binary catalog, returning number of entries
+ * int binstar()	Get binary catalog entry for one source
+ * void binclose()	Close binary catalog
+ */
+
+/* default pathname for catalog,  used if catalog file not found in current
+   working directory, but overridden by the WCS_BINDIR, SAO_PATH (if bincat
+   is SAO), PPM_PATH (if bincat is PPM, HIP_PATH, if bincat is HIP, or
+   IRAS_PATH is bincat is IRAS) environment variable */
+
+char bindir[64]="/data/stars";
+
+static double *tdist;	/* Array of distances to sources from search center */
+static int ndist = 0;
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+static int binsra();
+static int binsize();
+void binclose();
+static void binswap8();
+static void binswap4();
+static void binswap2();
+
+
+/* BINREAD -- Read binary catalog sources + names in specified region */
+
+int
+binread (bincat,distsort,cra,cdec,dra,ddec,drad,dradi,sysout,eqout,epout,
+	 mag1,mag2,sortmag,nstarmax,starcat,
+	 tnum,tra,tdec,tpra,tpdec,tmag,tpeak,tobj,nlog)
+
+char	*bincat;	/* Name of reference star catalog file */
+int	distsort;	/* 1 to sort stars by distance from center */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of sources to be returned */
+struct StarCat **starcat; /* Star catalog data structure */
+double	*tnum;		/* Array of catalog numbers (returned) */
+double	*tra;		/* Array of right ascensions (returned) */
+double	*tdec;		/* Array of declinations (returned) */
+double  *tpra;		/* Array of right ascension proper motions (returned) */
+double  *tpdec;		/* Array of declination proper motions (returned) */
+double	**tmag;		/* 2-D Array of magnitudes (returned) */
+int	*tpeak;		/* Array of encoded spectral types (returned) */
+char	**tobj;		/* Array of object names (returned) */
+int	nlog;
+{
+    double rra1,rra2;	/* Limiting catalog right ascensions of region */
+    double rdec1,rdec2;	/* Limiting catalog declinations of region */
+    double ra1,ra2;	/* Limiting output right ascensions of region */
+    double dec1,dec2;	/* Limiting output declinations of region */
+    double dist = 0.0;  /* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    int faintstar=0;    /* Faintest star */
+    int farstar=0;      /* Most distant star */
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog coordinate equinox */
+    double epref;	/* Catalog position epoch */
+    double secmarg = 60.0; /* Arcsec/century margin for proper motion */
+    double ra, dec, rapm, decpm;
+    double rra1a, rra2a;
+    double rdist, ddist;
+    struct StarCat *sc;	/* Star catalog data structure */
+    struct Star *star;
+    int wrap, iwrap, istar1,istar2;
+    int pass;
+    int imag;
+    char *objname;
+    int lname;
+    int nmag;		/* Real number of magnitudes per entry (- rv) */
+    int jstar;
+    int nstar;
+    double mag = 0.0;
+    double num;
+    int i;
+    int magsort;
+    int istar = 0;
+    int isp;
+    int verbose;
+    int mrv;
+    char cstr[16];
+    char str[128];
+
+    sc = *starcat;
+
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Open catalog */
+    if (sc == NULL)
+	sc = binopen (bincat);
+    *starcat = sc;
+    if (sc == NULL)
+	return (0);
+
+    /* If pathname is a URL, search and return */
+    if (sc->caturl != NULL) {
+	*starcat = NULL;
+	strcpy (str, sc->caturl);
+	free (sc);
+	return (webread (str,bincat,distsort,cra,cdec,dra,ddec,drad,dradi,
+		sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,
+		tnum,tra,tdec,tpra,tpdec,tmag,tpeak,nlog));
+	}
+
+    if (sc->nstars <= 0) {
+	binclose (sc);
+	sc = NULL;
+	return (0);
+	}
+
+    /* Keep mag1 the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    if (sortmag > 0 && sortmag <= sc->nmag)
+	magsort = sortmag - 1;
+    else 
+	magsort = 0;
+
+    /* Logging interval */
+    nstar = 0;
+
+    /* Allocate space for distances from search center, if necessary */
+    if (nstarmax > ndist) {
+	if (ndist > 0)
+	    free ((void *)tdist);
+	if (nstarmax > 10)
+	    tdist = (double *) calloc (nstarmax, sizeof(double));
+	else
+	    tdist = (double *) calloc (10, sizeof(double));
+	if (tdist == NULL) {
+	    fprintf (stderr,"BINREAD:  cannot allocate separation array\n");
+	    ndist = 0;
+	    return (0);
+	    }
+	if (nstarmax > 10)
+	    ndist = nstarmax;
+	else
+	    ndist = 10;
+	}
+
+    SearchLim (cra, cdec, dra, ddec, sysout, &ra1, &ra2, &dec1, &dec2, verbose);
+
+    /* Make sure first declination is always the smallest one */
+    if (dec1 > dec2) {
+	dec = dec1;
+	dec1 = dec2;
+	dec2 = dec;
+	}
+  
+    sysref = sc->coorsys;
+    eqref = sc->equinox;
+    epref = sc->epoch;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    if (verbose) {
+	char rstr1[16],rstr2[16],dstr1[16],dstr2[16];
+	ra2str (rstr1, 16, rra1, 3);
+        dec2str (dstr1, 16, rdec1, 2);
+	ra2str (rstr2, 16, rra2, 3);
+        dec2str (dstr2, 16, rdec2, 2);
+
+	wcscstr (cstr, sysref,eqref,epref);
+	fprintf (stderr,"BINREAD: RA: %s - %s  Dec: %s - %s %s\n",
+		 rstr1, rstr2, dstr1, dstr2, cstr);
+	}
+
+    /* If catalog RA range includes zero, split search in two */
+    if (wrap) {
+	rra1a = 0.0;
+	rra2a = rra2;
+	rra2 = 360.0;
+	}
+    else {
+	rra2a = 0;
+	}
+
+    if (sc->entrv > 0) {
+	nmag = sc->nmag - 1;
+	mrv = nmag;
+	}
+    else {
+	nmag = sc->nmag;
+	mrv = 0;
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    jstar = 0;
+
+    /* Loop through wraps (do not cross 360 degrees in search */
+    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+	/* Set first and last stars to check */
+	if (sc->rasorted) {
+	    istar1 = binsra (sc, star, rra1);
+	    istar2 = binsra (sc, star, rra2);
+	    }
+	else {
+	    istar1 = sc->star1;
+	    istar2 = sc->star0 + sc->nstars;
+	    }
+	if (verbose)
+	    fprintf (stderr,"BINREAD: Searching stars %d through %d\n",istar1,istar2);
+
+	/* Loop through catalog */
+	for (istar = istar1; istar <= istar2; istar++) {
+	    if (binstar (sc, star, istar)) {
+		fprintf (stderr,"BINREAD: Cannot read star %d\n", istar);
+		break;
+		}
+
+	    /* ID number */
+	    num = star->num;
+
+	    /* Magnitude */
+	    if (sc->entmag[0] > 0)
+		mag = star->xmag[magsort];
+
+	    /* Check magnitude limits */
+	    pass = 1;
+	    if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+		pass = 0;
+
+	    /* Get position in output coordinate system, equinox, and epoch */
+	    if (pass) {
+		rapm = star->rapm;
+		decpm = star->decpm;
+		ra = star->ra;
+		dec = star->dec;
+		wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+			 &ra, &dec, &rapm, &decpm);
+
+		/* Compute distance from search center */
+		if (drad > 0 || distsort)
+		    dist = wcsdist (cra,cdec,ra,dec);
+		else
+		    dist = 0.0;
+
+		/* Check radial distance to search center */
+		if (drad > 0) {
+		    if (dist > drad)
+			pass = 0;
+		    if (dradi > 0.0 && dist < dradi)
+			pass = 0;
+		    }
+
+		/* Check distance along RA and Dec axes */
+		else {
+		    ddist = wcsdist (cra,cdec,cra,dec);
+		    if (ddist > ddec)
+			pass = 0;
+		    rdist = wcsdist (cra,dec,ra,dec);
+		    if (rdist > dra)
+			pass = 0;
+		    }
+		}
+
+	    if (pass) {
+
+		/* Radial velocity */
+		if (sc->entrv > 0)
+		    star->xmag[mrv] = star->radvel;
+
+		/* Spectral Type */
+		isp = (1000 * (int) star->isp[0]) + (int)star->isp[1];
+
+		/* Save star position and magnitude in table */
+		if (nstar < nstarmax) {
+		    tnum[nstar] = num;
+		    tra[nstar] = ra;
+		    tdec[nstar] = dec;
+		    if (sc->mprop == 1) {
+			tpra[nstar] = rapm;
+			tpdec[nstar] = decpm;
+			}
+		    for (imag = 0; imag < sc->nmag; imag++) {
+			if (tmag[imag] != NULL)
+			    tmag[imag][nstar] = star->xmag[imag];
+			}
+		    tpeak[nstar] = isp;
+		    tdist[nstar] = dist;
+		    if (sc->ncobj > 0 && tobj != NULL) {
+			lname = strlen (star->objname) + 1;
+			objname = (char *)calloc (lname, 1);
+			strcpy (objname, star->objname);
+			tobj[nstar] = objname;
+			}
+		    if (dist > maxdist) {
+			maxdist = dist;
+			farstar = nstar;
+			}
+		    if (mag > faintmag) {
+			faintmag = mag;
+			faintstar = nstar;
+			}
+		    }
+
+		/* If too many stars and distance sorting,
+		   replace furthest star */
+		else if (distsort) {
+		    if (dist < maxdist) {
+			tnum[farstar] = num;
+			tra[farstar] = ra;
+			tdec[farstar] = dec;
+			if (sc->mprop == 1) {
+			    tpra[farstar] = rapm;
+			    tpdec[farstar] = decpm;
+			    }
+			for (imag = 0; imag < sc->nmag; imag++) {
+			    if (tmag[imag] != NULL)
+				tmag[imag][farstar] = star->xmag[imag];
+			    }
+			tpeak[farstar] = isp;
+			tdist[farstar] = dist;
+			if (sc->ncobj > 0 && tobj != NULL) {
+			    free ((void *)tobj[farstar]);
+			    lname = strlen (star->objname) + 1;
+			    objname = (char *)calloc (lname, 1);
+			    strcpy (objname, star->objname);
+			    tobj[farstar] = objname;
+			    }
+			maxdist = 0.0;
+
+		    /* Find new farthest star */
+			for (i = 0; i < nstarmax; i++) {
+			    if (tdist[i] > maxdist) {
+				maxdist = tdist[i];
+				farstar = i;
+				}
+			    }
+			}
+		    }
+
+		/* Else if too many stars, replace faintest star */
+		else if (mag < faintmag) {
+		    tnum[faintstar] = num;
+		    tra[faintstar] = ra;
+		    tdec[faintstar] = dec;
+		    if (sc->mprop == 1) {
+			tpra[faintstar] = rapm;
+			tpdec[faintstar] = decpm;
+			}
+		    for (imag = 0; imag < sc->nmag; imag++) {
+			if (tmag[imag] != NULL)
+			    tmag[imag][faintstar] = star->xmag[imag];
+			}
+		    tpeak[faintstar] = isp;
+		    tdist[faintstar] = dist;
+		    if (sc->ncobj > 0 && tobj != NULL) {
+			free ((void *)tobj[faintstar]);
+			lname = strlen (star->objname) + 1;
+			objname = (char *)calloc (lname, 1);
+			strcpy (objname, star->objname);
+			tobj[faintstar] = objname;
+			}
+		    faintmag = 0.0;
+
+		    /* Find new faintest star */
+		    for (i = 0; i < nstarmax; i++) {
+			if (tmag[magsort][i] > faintmag) {
+			    faintmag = tmag[magsort][i];
+			    faintstar = i;
+			    }
+			}
+		    }
+		
+		nstar++;
+		jstar++;
+		if (nlog == 1)
+		    fprintf (stderr,"BINREAD: %11.6f: %9.5f %9.5f %5.2f\n",
+			   num,ra,dec,mag);
+
+	    /* End of accepted star processing */
+		}
+
+	/* Log operation */
+	    if (nlog > 0 && istar%nlog == 0)
+		fprintf (stderr,"BINREAD: %5d / %5d / %5d sources catalog %s\r",
+			jstar,istar,sc->nstars,bincat);
+
+	/* End of star loop */
+	    }
+
+	/* Set second set of RA limits if passing through 0h */
+	rra1 = rra1a;
+	rra2 = rra2a;
+	}
+
+    /* Summarize search */
+    if (nlog > 0) {
+	fprintf (stderr,"BINREAD: Catalog %s : %d / %d / %d found\n",
+		 bincat,jstar,istar,sc->nstars);
+	if (nstar > nstarmax)
+	    fprintf (stderr,"BINREAD: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+
+    free ((void *)star);
+    free ((void *)tdist);
+    return (nstar);
+}
+
+
+/* BINRNUM -- Read binary catalog stars with specified numbers */
+
+int
+binrnum (bincat, nnum, sysout, eqout, epout, match,
+	 tnum,tra,tdec,tpra,tpdec,tmag,tpeak,tobj,nlog)
+
+char	*bincat;	/* Name of reference star catalog file */
+int	nnum;		/* Number of stars to look for */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+int	match;		/* If 1, match number exactly, else number is sequence*/
+double	*tnum;		/* Array of star numbers to look for */
+double	*tra;		/* Array of right ascensions (returned) */
+double	*tdec;		/* Array of declinations (returned) */
+double  *tpra;		/* Array of right ascension proper motions (returned) */
+double  *tpdec;		/* Array of declination proper motions (returned) */
+double	**tmag;		/* 2-D Array of magnitudes (returned) */
+int	*tpeak;		/* Array of peak counts (returned) */
+char	**tobj;		/* Array of object names (returned) */
+int	nlog;
+{
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog coordinate equinox */
+    double epref;	/* Catalog position epoch */
+    int jnum;
+    int nstar;
+    double ra, dec, rapm, decpm;
+    double num;
+    int istar;
+    int isp;
+    int imag;
+    int lname;
+    char *objname;
+    struct StarCat *starcat;
+    struct Star *star;
+    char str[128];
+    int nmag;
+    int mrv;
+
+    nstar = 0;
+    starcat = binopen (bincat);
+    if (starcat == NULL)
+	return (0);
+
+    /* If pathname is a URL, search and return */
+    if (starcat->caturl != NULL) {
+	strcpy (str, starcat->caturl);
+	free (starcat);
+	return (webrnum (str,bincat,nnum,sysout,eqout,epout,1,
+		tnum,tra,tdec,tpra,tpdec,tmag,tpeak,nlog));
+	}
+
+    /* If no stars in catalog, print error message and return */
+    if (starcat->nstars <= 0) {
+	free ((void *)starcat);
+	fprintf (stderr,"BINRNUM: Cannot read catalog %s\n", bincat);
+	return (0);
+	}
+
+    sysref = starcat->coorsys;
+    eqref = starcat->equinox;
+    epref = starcat->epoch;
+    if (!sysout)
+	sysout = sysref;
+    if (!eqout)
+	eqout = eqref;
+    if (!epout)
+	epout = epref;
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+    if (starcat->entrv > 0) {
+	nmag = starcat->nmag - 1;
+	mrv = nmag;
+	}
+    else
+	nmag = starcat->nmag;
+    
+
+    /* Loop through star list */
+    for (jnum = 0; jnum < nnum; jnum++) {
+
+	/* Find star in catalog */
+	istar = (int) tnum[jnum];
+	if (match) {
+	    istar = 1;
+	    while (istar <= starcat->nstars) {
+		if (binstar (starcat, star, istar)) {
+		    fprintf (stderr,"BINRNUM: Cannot read star %d\n", istar);
+		    tra[jnum] = 0.0;
+		    tdec[jnum] = 0.0;
+		    tpra[jnum] = 0.0;
+		    tpdec[jnum] = 0.0;
+		    for (imag = 0; imag < starcat->nmag; imag++)
+			tmag[imag][jnum] = 0.0;
+		    tpeak[jnum] = 0;
+		    continue;
+		    }
+		if (star->num == tnum[jnum])
+		    break;
+		istar++;
+		}
+	    if (star->num != tnum[jnum])
+		continue;
+	    }
+
+	else if (binstar (starcat, star, istar)) {
+	    fprintf (stderr,"BINRNUM: Cannot read star %d\n", istar);
+	    tra[jnum] = 0.0;
+	    tdec[jnum] = 0.0;
+	    tpra[jnum] = 0.0;
+	    tpdec[jnum] = 0.0;
+	    for (imag = 0; imag < starcat->nmag; imag++)
+		tmag[imag][jnum] = 0.0;
+	    tpeak[jnum] = 0;
+	    continue;
+	    }
+
+	/* If star has been found in catalog */
+
+	/* ID number */
+	num = star->num;
+
+	/* Position in degrees at designated epoch */
+	ra = star->ra;
+	dec = star->dec;
+	rapm = star->rapm;
+	decpm = star->decpm;
+	wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		     &ra, &dec, &rapm, &decpm);
+
+	/* Spectral Type */
+	isp = (1000 * (int) star->isp[0]) + (int)star->isp[1];
+
+	/* Save star position and magnitude in table */
+	tnum[jnum] = num;
+	tra[jnum] = ra;
+	tdec[jnum] = dec;
+	if (starcat->mprop == 1) {
+	    tpra[jnum] = rapm;
+	    tpdec[jnum] = decpm;
+	    }
+
+	/* Radial velocity, if present */
+	if (starcat->entrv > 0)
+	    tmag[mrv][nstar] = star->radvel;
+
+	/* Magnitudes */
+	if (nmag > 0) {
+	    for (imag = 0; imag < nmag; imag++) {
+		if (tmag[imag] != NULL)
+		    tmag[imag][nstar] = star->xmag[imag];
+		}
+	    }
+
+	tpeak[jnum] = isp;
+	if (starcat->ncobj > 0 && tobj != NULL) {
+	    lname = strlen (star->objname) + 1;
+	    objname = (char *)calloc (lname, 1);
+	    strcpy (objname, star->objname);
+	    tobj[nstar] = objname;
+	    }
+	nstar++;
+	if (nlog == 1) {
+	    fprintf (stderr,"BINRNUM: %11.6f: %9.5f %9.5f", num, ra, dec);
+	    for (imag = 0; imag < starcat->nmag; imag++)
+		fprintf (stderr," %5.2f",tmag[imag][nstar]);
+	    fprintf (stderr," %s  \n", star->isp);
+	    }
+
+	/* End of star loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"BINRNUM: Catalog %s : %d / %d found\n",
+		 bincat,nstar,starcat->nstars);
+
+    binclose (starcat);
+    free ((void *) star);
+    return (nstar);
+}
+
+
+/* BINBIN -- Fill FITS WCS image with stars from binary catalog */
+
+int
+binbin (bincat, wcs, header, image, mag1, mag2, sortmag, magscale, nlog)
+
+char	*bincat;	/* Name of reference star catalog file */
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int	sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double rra1,rra2;	/* Limiting catalog right ascensions of region */
+    double rdec1,rdec2;	/* Limiting catalog declinations of region */
+    double ra1,ra2;	/* Limiting output right ascensions of region */
+    double dec1,dec2;	/* Limiting output declinations of region */
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog coordinate equinox */
+    double epref;	/* Catalog position epoch */
+    double secmarg = 60.0; /* Arcsec/century margin for proper motion */
+    double ra, dec, rapm, decpm;
+    double rra1a, rra2a;
+    double rdist, ddist;
+    struct StarCat *sc;	/* Star catalog data structure */
+    struct Star *star;
+    int wrap, iwrap, istar1,istar2;
+    int ix, iy;
+    int pass;
+    int nmag;		/* Real number of magnitudes per entry (- rv) */
+    int jstar;
+    int nstar;
+    double mag;
+    double num;
+    int magsort;
+    int istar = 0;
+    int verbose;
+    int mrv;
+    char cstr[16];
+    double xpix, ypix, flux;
+    int offscl;
+    int bitpix, w, h;   /* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    /* Open catalog */
+    sc = binopen (bincat);
+    if (sc == NULL)
+	return (0);
+
+    if (sc->nstars <= 0) {
+	binclose (sc);
+	sc = NULL;
+	return (0);
+	}
+
+    /* Keep mag1 the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    if (sortmag > 0 && sortmag <= sc->nmag)
+	magsort = sortmag - 1;
+    else 
+	magsort = 0;
+
+    /* Logging interval */
+    nstar = 0;
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra, cdec, dra, ddec, sysout, &ra1, &ra2, &dec1, &dec2, verbose);
+
+    /* Make sure first declination is always the smallest one */
+    if (dec1 > dec2) {
+	dec = dec1;
+	dec1 = dec2;
+	dec2 = dec;
+	}
+  
+    sysref = sc->coorsys;
+    eqref = sc->equinox;
+    epref = sc->epoch;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    if (verbose) {
+	char rstr1[16],rstr2[16],dstr1[16],dstr2[16];
+	ra2str (rstr1, 16, rra1, 3);
+        dec2str (dstr1, 16, rdec1, 2);
+	ra2str (rstr2, 16, rra2, 3);
+        dec2str (dstr2, 16, rdec2, 2);
+
+	wcscstr (cstr, sysref,eqref,epref);
+	fprintf (stderr,"BINREAD: RA: %s - %s  Dec: %s - %s %s\n",
+		 rstr1, rstr2, dstr1, dstr2, cstr);
+	}
+
+    /* If catalog RA range includes zero, split search in two */
+    if (wrap) {
+	rra1a = 0.0;
+	rra2a = rra2;
+	rra2 = 360.0;
+	}
+    else {
+	rra2a = 0.0;
+	}
+
+    if (sc->entrv > 0) {
+	nmag = sc->nmag - 1;
+	mrv = nmag;
+	}
+    else
+	nmag = sc->nmag;
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    jstar = 0;
+
+    /* Loop through wraps (do not cross 360 degrees in search */
+    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+	/* Set first and last stars to check */
+	if (sc->rasorted) {
+	    istar1 = binsra (sc, star, rra1);
+	    istar2 = binsra (sc, star, rra2);
+	    }
+	else {
+	    istar1 = sc->star1;
+	    istar2 = sc->star0 + sc->nstars;
+	    }
+	if (verbose)
+	    fprintf (stderr,"BINREAD: Searching stars %d through %d\n",istar1,istar2);
+
+	/* Loop through catalog */
+	for (istar = istar1; istar <= istar2; istar++) {
+	    if (binstar (sc, star, istar)) {
+		fprintf (stderr,"BINREAD: Cannot read star %d\n", istar);
+		break;
+		}
+
+	    /* ID number */
+	    num = star->num;
+
+	    /* Magnitude */
+	    if (sc->entmag[0] > 0)
+		mag = star->xmag[magsort];
+
+	    /* Check magnitude limits */
+	    pass = 1;
+	    if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+		pass = 0;
+
+	    /* Get position in output coordinate system, equinox, and epoch */
+	    if (pass) {
+		rapm = star->rapm;
+		decpm = star->decpm;
+		ra = star->ra;
+		dec = star->dec;
+		wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+			 &ra, &dec, &rapm, &decpm);
+
+		/* Check distance along RA and Dec axes */
+		ddist = wcsdist (cra,cdec,cra,dec);
+		if (ddist > ddec)
+		    pass = 0;
+		rdist = wcsdist (cra,dec,ra,dec);
+		if (rdist > dra)
+		    pass = 0;
+		}
+
+	    /* Save star in FITS image */
+	    if (pass) {
+		wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+		if (!offscl) {
+		    if (magscale > 0.0)
+			flux = magscale * exp (logt * (-mag / 2.5));
+		    else
+			flux = 1.0;
+		    ix = (int) (xpix + 0.5);
+		    iy = (int) (ypix + 0.5);
+		    addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+		    nstar++;
+		    jstar++;
+		    }
+		else {
+		    ix = 0;
+		    iy = 0;
+		    }
+		if (nlog == 1) {
+		    fprintf (stderr,"BINBIN: %11.6f: %9.5f %9.5f %s",
+			     num,ra,dec,cstr);
+		    if (magscale > 0.0)
+			fprintf (stderr, " %5.2f", mag);
+		    if (!offscl)
+			flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+		    else
+			flux = 0.0;
+		    fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+		    }
+		if (nlog == 1)
+		    fprintf (stderr,"BINREAD: %11.6f: %9.5f %9.5f %5.2f\n",
+			   num,ra,dec,mag);
+
+	    /* End of accepted star processing */
+		}
+
+	/* Log operation */
+	    if (nlog > 0 && istar%nlog == 0)
+		fprintf (stderr,"BINREAD: %5d / %5d / %5d sources catalog %s\r",
+			jstar,istar,sc->nstars,bincat);
+
+	/* End of star loop */
+	    }
+
+	/* Set second set of RA limits if passing through 0h */
+	rra1 = rra1a;
+	rra2 = rra2a;
+	}
+
+    /* Summarize search */
+    if (nlog > 0) {
+	fprintf (stderr,"BINREAD: Catalog %s : %d / %d / %d found\n",
+		 bincat,jstar,istar,sc->nstars);
+	}
+
+    free ((void *)star);
+    free ((void *)tdist);
+    return (nstar);
+}
+
+
+/* BINOPEN -- Open binary catalog, returning number of entries */
+
+struct StarCat *
+binopen (bincat)
+
+char *bincat;	/* Binary catalog file name */
+{
+    int fcat;
+    struct StarCat *sc;
+    int nr, lfile;
+    char *binfile;
+    char binpath[128];	/* Full pathname for catalog file */
+    int lf, nb, binset;
+    char *str;
+
+    sc = (struct StarCat *) calloc (1, sizeof (struct StarCat));
+
+    /* Find length of binary catalog, if in working directory */
+    lfile = binsize (bincat);
+
+    /* Set catalog directory; if pathname is a URL, return */
+    binset = 0;
+    if (!strncasecmp (bincat,"PPM",3)) {
+	if ((str = getenv("PPM_PATH")) != NULL ) {
+	    if (!strncmp (str, "http:",5)) {
+		sc->caturl = str;
+		}
+	    else if (strlen (str) < 64) {
+		strcpy (bindir, str);
+		binset = 1;
+		}
+            }
+	}
+    else if (!strncasecmp (bincat,"SAO",3)) {
+	if ((str = getenv("SAO_PATH")) != NULL ) {
+	    if (!strncmp (str, "http:",5)) {
+		sc->caturl = str;
+		}
+	    else if (strlen (str) < 64) {
+		strcpy (bindir, str);
+		binset = 1;
+		}
+            }
+	}
+    else if (!strncasecmp (bincat,"SKY2K",3)) {
+	if ((str = getenv("SKY2K_PATH")) != NULL ) {
+	    if (!strncmp (str, "http:",5)) {
+		sc->caturl = str;
+		}
+	    else if (strlen (str) < 64) {
+		strcpy (bindir, str);
+		binset = 1;
+		}
+            }
+	}
+    else if (!strncasecmp (bincat,"HIP",3)) {
+	if ((str = getenv("HIP_PATH")) != NULL ) {
+	    if (!strncmp (str, "http:",5)) {
+		sc->caturl = str;
+		}
+	    else if (strlen (str) < 64) {
+		strcpy (bindir, str);
+		binset = 1;
+		}
+            }
+	}
+    else if (!strncasecmp (bincat,"IRAS",4)) {
+	if ((str = getenv("IRAS_PATH")) != NULL ) {
+	    if (!strncmp (str, "http:",5)) {
+		sc->caturl = str;
+		}
+	    else if (strlen (str) < 64) {
+		strcpy (bindir, str);
+		binset = 1;
+		}
+            }
+	}
+    if (!binset && (str = getenv("WCS_BINDIR")) != NULL ) {
+	if (!strncmp (str, "http:",5)) {
+	    sc->caturl = str;
+            }
+	else if (strlen (str) < 64)
+	    strcpy (bindir, str);
+	}
+
+    /* Set up catalog information and return if over web */
+    if (sc->caturl != NULL) {
+	sc->coorsys = 0;
+	sc->epoch = 0.0;
+	sc->equinox = 0.0;
+	if (!strncasecmp (bincat, "sao", 3) ||
+	    !strncasecmp (bincat, "ppm", 3) ||
+	    !strncasecmp (bincat, "sky2k", 3) ||
+	    !strncasecmp (bincat, "hip", 3))
+	   sc->mprop = 1;
+	else
+	   sc->mprop = 0;
+	return (sc);
+	}
+
+    /* Prepend directory name file not in working directory */
+    if (lfile < 2) {
+
+	strcpy (binpath, bindir);
+	strcat (binpath, "/");
+	strcat (binpath, bincat);
+	lfile = binsize (binpath);
+	if (lfile < 2) {
+	    fprintf (stderr,"BINOPEN: Binary catalog %s has no entries\n",bincat);
+	    return (NULL);
+	    }
+	}
+    else
+	strcpy (binpath, bincat);
+
+    /* Open binary catalog */
+    if ((fcat = open (binpath, O_RDONLY+O_BINARY)) < 3) {
+	fprintf (stderr,"BINOPEN: Binary catalog %s cannot be read\n",binpath);
+	free (sc);
+	return (NULL);
+	}
+
+    /* Read binary catalog header information */
+    nr = (int) read (fcat, sc, 28);
+    if (nr < 28) {
+	fprintf (stderr,"BINOPEN: read only %d / %d bytes of file %s\n",
+		 nr, lfile, binpath);
+	(void) close (fcat);
+	return (NULL);
+	}
+
+    /* Check for byte reversal */
+    if (sc->nbent > 80) {
+	sc->byteswapped = 1;
+	binswap4 (&sc->star0);
+	binswap4 (&sc->star1);
+	binswap4 (&sc->nstars);
+	binswap4 (&sc->stnum);
+	binswap4 (&sc->mprop);
+	binswap4 (&sc->nmag);
+	binswap4 (&sc->nbent);
+	}
+    else
+	sc->byteswapped = 0;
+
+    /* Allocate buffer to read one line of catalog */
+    sc->catline = (char *) calloc (sc->nbent, sizeof (char));
+
+    nb = 0;
+    sc->ncobj = 0;
+    if (sc->stnum < 0)
+	sc->ncobj = -sc->stnum;
+    else if (sc->stnum > 0)	
+	nb = 4;
+
+    sc->entra = nb;
+    sc->entdec = nb + 8;
+    sc->entpeak = nb + 16;
+
+    /* Set other catalog information in structure */
+    if (sc->nmag < 0) {
+	sc->inform = 'J';
+	sc->coorsys = WCS_J2000;
+	sc->epoch = 2000.0;
+	sc->equinox = 2000.0;
+	sc->nmag = -sc->nmag;
+	}
+    else if (sc->nstars < 0) {
+	sc->inform = 'J';
+	sc->coorsys = WCS_J2000;
+	sc->epoch = 2000.0;
+	sc->equinox = 2000.0;
+	sc->nstars = -sc->nstars;
+	}
+    else {
+	sc->inform = 'B';
+	sc->coorsys = WCS_B1950;
+	sc->epoch = 1950.0;
+	sc->equinox = 1950.0;
+	}
+
+    if (sc->nmag > 0)
+	sc->entmag[0] = nb + 18;
+    else
+	sc->entmag[0] = 0;
+    nb = nb + 18 + (sc->nmag * 2);
+    if (sc->mprop == -1)
+	sc->mprop = 1;
+    if (sc->mprop == 1) {
+	sc->entrpm = nb;
+	sc->entdpm = nb + 4;
+	nb = nb + 8;
+	}
+    else if (sc->mprop == 2) {
+	sc->entrv = nb;
+	nb = nb + 8;
+	}
+    if (sc->ncobj)
+	sc->entname = nb;
+
+    /* Set number of decimal places in star numbers */
+    if (sc->stnum == 2)
+	sc->nndec = 4;
+    else if (sc->stnum == 3)
+	sc->nndec = 5;
+    else
+	sc->nndec = 0;
+
+    strcpy (sc->incdir, bindir);
+    strcpy (sc->incfile, bincat);
+
+    /* Separate filename from pathname and save in structure */
+    binfile = strrchr (binpath,'/');
+    if (binfile)
+	binfile = binfile + 1;
+    else
+	binfile = binpath;
+    if (strlen (binfile) < 24)
+	strcpy (sc->isfil, binfile);
+    else
+	strncpy (sc->isfil, binfile, 23);
+
+    sc->entadd = fcat;
+    sc->sptype = 1;
+    if (sc->mprop == 2)
+	sc->nmag = sc->nmag + 1;
+
+    /* Check name to see if file is RA-sorted */
+    lf = strlen (binfile);
+    sc->rasorted = 0;
+    if (binfile[lf-2] == 'r' && binfile[lf-1] == 'a')
+	sc->rasorted = 1;
+    if (!strncmp (binfile, "IRAS", 4))
+	sc->rasorted = 1;
+
+    sc->refcat = BINCAT;
+    return (sc);
+}
+
+
+void
+binclose (sc)
+struct StarCat *sc;	/* Star catalog descriptor */
+{
+    close (sc->entadd);
+    free ((void *)sc->catline);
+    free ((void *)sc);
+    return;
+}
+
+
+/* BINSRA -- Find star closest to given RA in RA-sorted catalog */
+
+static int
+binsra (sc, st, dra)
+
+struct StarCat *sc;	/* Star catalog descriptor */
+struct Star *st;	/* Current star entry */
+double	dra;		/* Right ascension in degrees */
+
+{
+    char rastr[32], raxstr[32], ramins[32], ramaxs[32];
+    int istar0, istarx, nrep, ismin, ismax;
+    double rax, ramin, ramax;
+    int verbose = 0;
+
+    /* Keep RA between 0 and 360 degrees */
+    if (dra > 360.0)
+	rax = dra - 360.0;
+    else
+	rax = dra;
+
+    ismin = 1;
+    if (binstar (sc, st, ismin)) {
+	fprintf (stderr,"BINSRA: Cannot read star %d\n", ismin);
+	return (0);
+	}
+    else
+	ramin = st->ra;
+
+    ismax = sc->nstars;
+    if (binstar (sc, st, ismax)) {
+	fprintf (stderr,"BINSRA: Cannot read star %d\n", ismax);
+	return (0);
+	}
+    else
+	ramax = st->ra;
+
+    istarx = sc->nstars / 2;
+
+    for (nrep = 0; nrep < 32; nrep++) {
+	if (binstar (sc, st, istarx)) {
+	    fprintf (stderr,"BINSRA: Cannot read star %d\n", istarx);
+            return (0);
+	    }
+
+	/* Find next catalog number to read */
+	if (st->ra < rax) {
+	    ismin = istarx;
+	    ramin = st->ra;
+	    istar0 = istarx;
+	    if (ismax - istarx > 1)
+		istarx = istarx + (ismax - istarx) / 2;
+	    else if (ismax - istarx > 0)
+		istarx = istarx + 1;
+	    }
+	else if (st->ra > rax) {
+	    ismax = istarx;
+	    ramax = st->ra;
+	    istar0 = istarx;
+	    if (istarx - ismin > 1)
+		istarx = istarx - ((istarx - ismin) / 2);
+	    else if (istarx - ismin > 0)
+		istarx = istarx - 1;
+	    }
+	else
+	    break;
+
+	if (verbose) {
+	    ra2str (rastr, 16, st->ra, 3);
+	    ra2str (raxstr, 16, rax, 3);
+	    ra2str (ramins, 16, ramin, 3);
+	    ra2str (ramaxs, 16, ramax, 3);
+	    fprintf (stderr,"%9d: %s -> %s  %9d: %s  %9d: %s\n",
+		    istarx, rastr, raxstr, ismin,ramins,ismax,ramaxs);
+	    }
+	if (istarx == istar0)
+	    break;
+	}
+
+    /* Make sure final star is real */
+    if (binstar (sc, st, istarx)) {
+	fprintf (stderr,"BINSRA: Cannot read star %d\n", istarx);
+        return (0);
+	}
+    else
+	return (istarx);
+}
+
+
+/* BINSTAR -- Get binary catalog entry for one star;
+              return 0 if successful */
+
+int
+binstar (sc, st, istar)
+
+struct StarCat *sc;	/* Star catalog descriptor */
+struct Star *st;	/* Current star entry */
+int istar;	/* Star sequence number in binary catalog */
+{
+    int ino, i, nmag;
+    long offset;
+    double radvel;
+    float pm[2];
+
+    /* Drop out if catalog pointer is not set */
+    if (sc == NULL)
+	return (1);
+
+    /* Drop out if catalog is not open */
+    if (sc->entadd < 3)
+	return (2);
+
+    /* Drop out if star number is too large */
+    if (istar > sc->nstars) {
+	fprintf (stderr, "BINSTAR:  %d  > %d is not in catalog\n",
+		 istar, sc->nstars);
+	return (3);
+	}
+
+    /* Move file pointer to start of correct star entry */
+    if (istar > 0) {
+	offset = 28 + (istar - sc->star1) * sc->nbent;
+	if (lseek (sc->entadd, offset, SEEK_SET) < offset)
+	    return (0);
+	}
+
+    /* Read catalog entry */
+    if ((int)read (sc->entadd, sc->catline, sc->nbent) < 1)
+	return (4);
+
+    /* Read catalog number or object name */
+    sc->ncobj = 0;
+    if (sc->stnum <= 0) {
+	sc->ncobj = -sc->stnum;
+	movebuff (sc->catline, st->objname, sc->ncobj, sc->entname, 0);
+	}
+    else {
+	movebuff (sc->catline, (char *) &st->xno, 4, 0, 0);
+	if (sc->byteswapped)
+	    binswap4 (&st->xno);
+	}
+
+    /* Interpret catalog number */
+    switch (sc->stnum) {
+	case 1:
+	    st->num = (double) st->xno;
+	    break;
+	case 2:
+	    bcopy ((char *)&st->xno, (char *) &ino, 4);
+	    st->num = 0.0001 * (double) ino;
+	    break;
+	case 3:
+	    bcopy ((char *)&st->xno, (char *) &ino, 4);
+	    st->num = 0.00001 * (double) ino;
+	    break;
+	case 4:
+	    bcopy ((char *)&st->xno, (char *) &ino, 4);
+	    st->num = (double) ino;
+	    break;
+	default:
+	    if (istar > 0)
+		st->num = (double) istar;
+	    else
+		st->num = st->num + 1.0;
+	    break;
+	}
+
+    /* Right ascension and declination and convert to degrees */
+    movebuff (sc->catline, (char *) &st->ra, 8, sc->entra, 0);
+    movebuff (sc->catline, (char *) &st->dec, 8, sc->entdec, 0);
+    if (sc->byteswapped) {
+	binswap8 (&st->ra);
+	binswap8 (&st->dec);
+	}
+    st->ra = raddeg (st->ra);
+    st->dec = raddeg (st->dec);
+
+    /* Proper motion, if present, and convert to degrees/year */
+    nmag = sc->nmag;
+    if (sc->mprop == 1) {
+	movebuff (sc->catline, (char *) &pm[0], 4, sc->entrpm, 0);
+	movebuff (sc->catline, (char *) &pm[1], 4, sc->entdpm, 0);
+	if (sc->byteswapped) {
+	    binswap4 (&pm[0]);
+	    binswap4 (&pm[1]);
+	    }
+	st->rapm = raddeg ((double) pm[0]);
+	st->decpm = raddeg ((double) pm[1]);
+	}
+
+    /* Radial velocity, if it is present */
+    else if (sc->mprop == 2) {
+	movebuff (sc->catline, (char *) &radvel, 8, sc->entrv, 0);
+	if (sc->byteswapped)
+	    binswap8 (&radvel);
+	st->radvel = radvel;
+	nmag = nmag - 1;
+	}
+
+    /* Spectral type */
+    movebuff (sc->catline, (char *) st->isp, 2, sc->entpeak, 0);
+
+    /* Magnitudes */
+    if (sc->entmag[0] > 0) {
+	for (i = 0; i < nmag; i++) {
+	    movebuff (sc->catline, (char *) st->mag, 2, sc->entmag[0]+(i*2), i*2);
+	    if (sc->byteswapped)
+		binswap2 (&st->mag[i], 2);
+	    st->xmag[i] = 0.01 * (double) st->mag[i];
+	    }
+	}
+    return (0);
+}
+
+
+/* BINSWAP2 -- Swap bytes in string in place */
+
+static void
+binswap2 (string,nbytes)
+
+
+char *string;	/* Address of starting point of bytes to swap */
+int nbytes;	/* Number of bytes to swap */
+
+{
+    char *sbyte, temp, *slast;
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp = sbyte[0];
+	sbyte[0] = sbyte[1];
+	sbyte[1] = temp;
+	sbyte= sbyte + 2;
+	}
+    return;
+}
+
+
+/* BINSWAP4 -- Reverse bytes of Integer*4 or Real*4 number in place */
+
+static void
+binswap4 (string)
+
+char *string;	/* Address of Integer*4 or Real*4 vector */
+
+{
+    char temp0, temp1, temp2, temp3;
+
+    temp3 = string[0];
+    temp2 = string[1];
+    temp1 = string[2];
+    temp0 = string[3];
+    string[0] = temp0;
+    string[1] = temp1;
+    string[2] = temp2;
+    string[3] = temp3;
+
+    return;
+}
+
+
+/* BINSWAP8 -- Reverse bytes of Real*8 vector in place */
+
+static void
+binswap8 (string)
+
+char *string;	/* Address of Real*8 vector */
+
+{
+    char temp[8];
+
+    temp[7] = string[0];
+    temp[6] = string[1];
+    temp[5] = string[2];
+    temp[4] = string[3];
+    temp[3] = string[4];
+    temp[2] = string[5];
+    temp[1] = string[6];
+    temp[0] = string[7];
+    string[0] = temp[0];
+    string[1] = temp[1];
+    string[2] = temp[2];
+    string[3] = temp[3];
+    string[4] = temp[4];
+    string[5] = temp[5];
+    string[6] = temp[6];
+    string[7] = temp[7];
+    return;
+}
+
+
+/* BINSIZE -- return size of binary catalog file in bytes */
+
+static int
+binsize (filename)
+
+char	*filename;	/* Name of file for which to find size */
+{
+    FILE *diskfile;
+    long filesize;
+
+    /* Open file */
+    if ((diskfile = fopen (filename, "rb")) == NULL)
+	return (-1);
+
+    /* Move to end of the file */
+    if (fseek (diskfile, 0, 2) == 0)
+
+ 	/* Position is the size of the file */
+	filesize = ftell (diskfile);
+
+    else
+	filesize = -1;
+
+    fclose (diskfile);
+
+    return (filesize);
+}
+
+
+/* ISBIN -- Return 1 if TDC binary catalog file, else 0 */
+
+int
+isbin (filename)
+
+char    *filename;      /* Name of file to check */
+{
+    FILE *diskfile;
+    char line[8];
+    int nbr;
+
+    if ((diskfile = fopen (filename, "rb")) == NULL)
+	return (0);
+    else {
+	nbr = fread (line, 1, 4, diskfile);
+	fclose (diskfile);
+	if (nbr < 4)
+	    return (0);
+	else if (line[0] == 0 || line[1] == 0 || line[2] == 0 || line[3] == 0)
+	    return (1);
+	else
+	    return (0);
+	}
+}
+
+/* Sep 10 1998	New subroutines
+ * Sep 15 1998	Add byte swapping
+ * Sep 16 1998	Use limiting radius correctly; use arbitrary search system
+ * Sep 21 1998	Return converted coordinates
+ * Sep 23 1998	Set search limits in catalog system, but test in output system
+ * Sep 24 1998	Return second magnitude if more than one in catalog
+ * Oct 15 1998	Add binsize() to compute file length in bytes
+ * Oct 20 1998	Add binrobj() to read object names for specified stars
+ * Oct 21 1998	Fix binsra() to get out of 3-in-a-row identical RAs
+ * Oct 21 1998	Add isbin() to check whether file could be TDC binary catalog
+ * Oct 26 1998	Add object retrieval to binread() and binrnum()
+ * Oct 29 1998	Correctly assign numbers when too many stars are found
+ * Oct 30 1998	Be more careful with coordinate system of catalog
+ * Oct 30 1998	Fix convergence at edges of catalog
+ * Nov  9 1998	Add flag to catalog structure for RA-sorted catalogs
+ * Dec  8 1998	Have binstar() return 0 instead of null
+ *
+ * Feb  1 1999	Add match argument to binrnum() 
+ * Feb  2 1999	Set number of decimal places in star number
+ * Feb 11 1999	Change starcat.insys to starcat.coorsys
+ * Jun 16 1999	Use SearchLim()
+ * Aug 16 1999	Add RefLim() to get converted search coordinates right
+ * Aug 24 1999	Fix declination limit bug which broke search 
+ * Aug 25 1999	Return real number of stars from binread()
+ * Sep 16 1999	Fix bug which didn't always return closest stars
+ * Sep 16 1999	Add distsort argument so brightest stars in circle works, too
+ * Oct 21 1999	Fix declarations after lint
+ *
+ * Mar 15 2000	Add proper motion return to binread() and binrnum()
+ * Mar 28 2000	Use moveb() to extract information from entries
+ * Mar 30 2000	Use standard i/O instead of stream I/O
+ * Jun  2 2000	Minor changes after lint
+ * Jun 26 2000	Add coordinate system to SearchLim() arguments
+ * Jul 12 2000	Add star catalog structure to binread() argument list
+ * Jul 25 2000	Pass star catalog address of data structure address
+ * Sep 25 2000	Set sc->sptype to 1 to indicate presence of spectral type
+ * Nov 29 2000	Add option to read catalogs using HTTP
+ * Dec  1 2000	Add separate paths for SAO and PPM catalogs
+ * Dec 18 2000	Drop unused variable str in binopen()
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Feb 14 2001	Correctly full-circle limits at poles
+ * Mar 19 2001	Check WCS_BINDIR if PPM_PATH or SAO_PATH not set in binread()
+ * Mar 22 2001	Move path environment variable reading to binopen()
+ * Mar 23 2001	Set system, equinox, and epoch to 0 in binopen if using Web
+ * Jun 20 2001	Save all four magnitudes if present, dropping spectral type
+ * Jun 22 2001	Do not swap spectral type in binstar()
+ * Jun 25 2001	Add HIP_PATH and IRAS_PATH; allow 5 digits for magnitudes
+ * Jun 27 2001	Allocate separation array only when larger one is needed
+ * Jul  6 2001	Fix bug in dealing with proper motions of excess stars
+ * Jul 17 2001	Fix bug which prevented use of full catalog pathname on CL
+ * Aug  8 2001	Add radial velocity if mprop is 2; return as magnitude
+ * Sep 11 2001	Pass array of magnitude vectors to avoid kludges
+ * Sep 11 2001	Add sort magnitude argument
+ * Sep 18 2001	Fix magnitude number in binrnum()
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ *
+ * Mar 25 2002	Fix bugs dealing with radial velocity
+ * Mar 26 2002	Don't set object name unless header says it is there
+ * Aug  6 2002	Make sc->entmag into vector, but only use first position
+ *
+ * Feb  4 2003	Open catalog file rb instead of r (Martin Ploner, Bern)
+ * Feb 19 2003	Increase maximum nbent from 50 to 80
+ * Feb 20 2003	Eliminate unused variables after lint
+ * Mar 11 2003	Upgrade position testing
+ * Mar 11 2003	If proper motion flag is -1, reset it to 1
+ * Mar 26 2003	Open binpath with binary flag set
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Sep 25 2003	Add binbin() to fill an image with sources
+ * Oct  6 2003	Update binread() and binbin() for improved RefLim()
+ * Nov 18 2003	Initialize image size and bits/pixel from header in binbin()
+ * Dec 12 2003	Fix bug in wcs2pix() call in binbin()
+ *
+ * Aug 27 2004	Include math.h
+ *
+ * Jan 19 2006	Fix bug when J2000 system set by negative number of magnitudes
+ * Jun 20 2006	Initialize uninitialized variables
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ * Nov 15 2006	Fix binning
+ *
+ * Jan  8 2007	Drop unused variables in binbin()
+ * Jan 10 2007	Add match=1 argument to webrnum()
+ * Nov 28 2007	Move moveb() to catutil.c
+ *
+ * Sep 25 2009	Call movebuff() instead of moveb() and move mvebuff() to catutil.c
+ */
diff --git a/Code/src/libwcs/caphot.c b/Code/src/libwcs/caphot.c
new file mode 100644
index 0000000000000000000000000000000000000000..fc9b6df5d8f7572d59277c7d8afa249c46463aef
--- /dev/null
+++ b/Code/src/libwcs/caphot.c
@@ -0,0 +1,409 @@
+/*** File caphot.c
+ *** January 30, 2002
+ *** By Doug Mink from Fortran code by Sam Conner (MIT, 1984)
+ *** Copyright (C) 2002
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <math.h>
+#include "fitshead.h"
+#include "imio.h"
+
+static double apint();
+static double imapfr();
+
+/* CAPHOT -- Perform rigorous circular aperture photometry using the imapfr()
+ *	     subroutine to determine the fraction of a square pixel enclosed
+ *	     by a circular aperture.
+ */
+
+void
+caphot (imbuff,header,cx,cy,rad,sumw,wsum)
+
+char	*imbuff;		/* address of start of image buffer */
+char	*header;	/* image FITS header */
+double	cx,cy;		/* center x,y of aperture */
+double	rad;		/* radius of aperture */
+double	sumw;		/* sum of values of pixel weights (returned) */
+double	wsum;		/* sum of weighted pixel fluxes (returned) */
+{
+    double x, y, factor, flux, bs, bz;
+    int ix1, ix2, iy1, iy2, ix, iy, bitpix, nx, ny;
+
+    sumw = 0.0;
+    wsum = 0.0;
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS1", &nx);
+    hgeti4 (header, "NAXIS2", &ny);
+    hgetr8 (header, "BSCALE", &bs);
+    hgeti4 (header, "BZERO", &bz);
+
+    /* Find range of ys to check */
+    iy1 = (int) (cy - rad);
+    iy2 = (int) (cy + rad + 0.99999);
+    if (iy1 < 1)
+	iy1 = 1;
+    if (iy2 > ny)
+	iy2 = ny;
+
+    /* Find range of xumns to check */
+    ix1 = (int) (cx - rad);
+    ix2 = (int) (cx + rad + 0.99999);
+    if (ix1 < 1)
+	ix1 = 1;
+    if (ix2 > nx)
+	ix2 = nx;
+
+    for (iy = iy1; iy <= iy2; iy++) {
+	y = (double) iy;
+	for (ix = ix1; ix <= ix2; ix++) {
+	    x = (double) ix;
+	    factor = imapfr (x,y,cx,cy,rad);
+	    sumw = sumw + factor;
+	    flux = getpix1 (imbuff, bitpix, nx, ny, bz, bs, ix, iy);
+	    wsum = wsum + (factor * flux);
+
+	    /* fprintf (stderr, "IMAPSB: (%d,%d)= %f weight= %f\n",
+		    ix,iy,flux,factor); */
+	    }
+	}
+
+ /* fprintf (stderr, "IMAPSB: sum of weights = %f\n", sumw);
+    fprintf (stderr, "IMAPSB: sum of weighted intensity = %f\n", wsum);
+ */
+
+    return;
+}
+
+/* IMAPFR -- Determine the fraction of a square pixel that is included
+ *	     within the boundary of a circle of arbitrary center and radius
+ */
+
+static double
+imapfr (pcol,prow,ccol,crow,rad)
+
+double	pcol,prow;	/* column, row of pixel */
+double	ccol,crow;	/* column, row of center of circle */
+double	rad;		/* radius of circle in pixels */
+
+{
+    int icorn[4], ncorn, j, i;
+    double dist, xdiff, y0, y1, ydiff, x0, x1, x, y, dc5, dr5;
+    double cterm, ulim, llim, dc, dr, dx, dy;
+    double frac;		/* fraction of pixel within circle */
+
+    /* Compute distance to center of circle from most distant corner of pixel */
+    dc = fabs (pcol - ccol);
+    dr = fabs (prow - crow);
+    dc5 = dc + 0.5;
+    dr5 = dr + 0.5;
+    dist = sqrt ((dc5 * dc5) + (dr5 * dr5));
+    dc5 = dc - 0.5;
+    dr5 = dr - 0.5;
+
+    /* if the pixel is completely enclosed by the aperture, return 1 */
+    if (dist <= rad)
+	return (1.0);
+
+    /* if the pixel is not completely included, compute distance from the
+       center of the circle to the nearest point on the periphery of the pixel*/
+    if ((pcol-0.5) < ccol && ccol < (pcol+.5)) {
+	dist = dr5;
+
+	/* the pixel is completely excluded from the aperture */
+	if (dist >= rad)
+	      return (0.0);
+
+	else if ((prow - 0.5) < crow && crow < (prow + 0.5)) {
+	    dist = dc5;
+
+	    /* the pixel is completely excluded from the aperture */
+	    if (dist >= rad)
+	      return (0.0);
+	    }
+
+	else
+	    dist = sqrt (dc5*dc5 + dr5*dr5);
+
+	/* the pixel is completely excluded from the aperture */
+	if (dist >= rad)
+	    return (0.0);
+
+	}
+
+    /* If the pixel is partially included in the aperture, determine how
+       many corners are enclosed */
+    ncorn = 1;
+    y = prow - 1.5;
+    for (i = 0; i < 2; i++) {
+	y = y + 1.0;
+	x = pcol - 1.50;
+	for (j = 0; j < 2; j++) {
+	    icorn[j+(i*2)] = 0;
+	    x = x + 1.0;
+	    dx = x - ccol;
+	    dy = y - crow;
+	    dist = sqrt (dx*dx + dy*dy);
+	    if (dist < rad) {
+	        ncorn = ncorn + 1;
+	        icorn[j+(i*2)] = 1;
+	        }
+	    }
+	}
+
+    /* Depending on number of corners enclosed,
+       branch to appropriate computation */
+
+    /* If no corners are enclosed, determine whether the slice is vertical or
+       horizontal */
+    if (ncorn < 1) {
+
+	/* If the slice is vertical (at the sides of the aperture,
+	   at extreme x), determine the limits of integration */
+	if ((pcol - 0.5) >= ccol || ccol >= (pcol + 0.5)) {
+	    xdiff = fabs (pcol - ccol) - 0.5;
+	    dx = sqrt (rad*rad - xdiff*xdiff);
+	    y0 = crow - dx;
+	    y1 = crow + dx;
+	    if (ccol >= pcol)
+		x = pcol + 0.5;
+	    else
+		x = pcol - 0.5;
+
+	    /* Evaluate integral */
+	    frac = -xdiff * (y1 - y0) + apint ((y1 - crow), rad) -
+		   apint ((y0 - crow), rad);
+	    return (frac);
+	    }
+
+	/* If the slice is horizontal (at top or bottom of the aperture, at
+	   extreme y), determine the limits of integration. */
+	else {
+	    ydiff = fabs (prow - crow) - 0.5;
+	    dy = sqrt (rad*rad - ydiff*ydiff);
+	    x0 = ccol - dy;
+	    x1 = ccol + dy;
+	    if (crow >= prow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+
+	    /* Evaluate integral */
+	    frac = -ydiff*(x1 - x0) + apint ((x1 - ccol),rad) -
+		   apint ((x0 - ccol),rad);
+	    return (frac);
+	    }
+	}
+
+    /* if one corner is enclosed, find direction (ne,nw,se,sw) from the center*/
+    else if (ncorn < 2) {
+
+	if (pcol < ccol) {
+
+	    /* Determine the limits of integration */
+	    ydiff = fabs (prow - crow) - 0.5;;
+	    x0 = ccol - sqrt (rad*rad - ydiff*ydiff);
+	    if (crow >= prow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+
+	    /* Evaluate integral */
+	    frac = -ydiff*(pcol + 0.5 - x0) + apint ((pcol + 0.5 - ccol),rad) -
+		   apint ((x0 - ccol),rad);
+	    return (frac);
+	    }
+
+	else {
+
+	    /* Determine the limits of integration */
+	    ydiff = fabs (crow - prow) - 0.5;
+	    x1 = ccol + sqrt (rad*rad - ydiff*ydiff);
+	    if (crow >= prow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+
+	    /* Evaluate integral */
+	    frac = -ydiff*(x1 - pcol + 0.5) + apint ((x1 - ccol),rad) -
+		   apint ((pcol - .5 - ccol),rad);
+	    return (frac);
+	    }
+	}
+
+    /* If two corners are enclosed, this problem has two cases: one in
+       which the aperture boundary intersects the pixel boundary twice,
+       and another in which it intersects the pixel boundaries 4 times */
+
+    /* determine whether this may be a 4-intersection configuration */
+    else if (ncorn < 3) {
+
+	if (((pcol - 0.5) < ccol && ccol < (pcol + .5) &&
+	    (fabs(crow - prow) + 0.5) < rad) ||
+	    ((prow - 0.5) < crow && crow < (prow + 0.5) &&
+	    (fabs(ccol - pcol) + 0.5) < rad)) {
+
+/* if the pixel boundary intersects the aperture boundary in four places,
+   determine whether the pixel is east-west or north-south. */
+
+	    /* pixel is east-west */
+	    if ((pcol - 0.5) >= ccol || (pcol + 0.5) <= ccol) {
+		xdiff = dc + 0.5;
+
+		/* Determine the limits of integration */
+		dx = sqrt (rad*rad - xdiff*xdiff);
+		y0 = crow - dx;
+		y1 = crow + dx;
+		if (pcol >= ccol)
+		    x = pcol + .5;
+		else
+		    x = pcol - .5;
+		frac = 1. - xdiff*(y0 - y1 + 1.0) +
+			apint ((y0 - crow), rad) -
+			apint ((prow - 0.5 - crow), rad) +
+			apint ((prow + 0.5 - crow), rad) -
+			apint ((y1 - crow), rad);
+		return (frac);
+		}
+
+	    /* Pixel is north-south */
+
+	    else {
+		ydiff = dr + 0.5;
+
+		/* determine the limits of integration */
+		dy = sqrt (rad*rad - ydiff*ydiff);
+
+		/* x0 is the x-coordinate of the small x intercept */
+		x0 = ccol - dy;
+
+		/* x1 is the x-coordinate of the large x intercept */
+		x1 = ccol + dy;
+		if (prow >= crow)
+		    y = prow + 0.5;
+		else
+		    y = prow - 0.5;
+		frac = 1. - ydiff * (x0 - x1 + 1.0) +
+			apint ((x0 - ccol), rad) -
+			apint ((pcol - 0.5 - ccol), rad) +
+			apint ((pcol + 0.5 - ccol), rad) -
+			apint ((x1 - ccol), rad);
+		return (frac);
+		}
+	    }
+
+	/* in the two-intersection case, determine which corners are included */
+	else {
+
+	    /* the pixel is north or south of the center of the aperture */
+	    if (icorn[0]*icorn[1] == 1 || icorn[2]*icorn[3] == 1) {
+		cterm = 0.5 - dr;
+		ulim = apint ((pcol + 0.5 - ccol) ,rad);
+		llim = apint ((pcol - 0.5 - ccol) ,rad);
+		frac = cterm + ulim - llim;
+		return (frac);
+		}
+
+	    /* the pixel is east or west of the center */
+	    else {
+		cterm = 0.5 - dc;
+		ulim = apint ((prow + 0.5 - crow), rad);
+		llim = apint ((prow - 0.5 - crow), rad);
+		frac = cterm + ulim - llim;
+		return (frac);
+		}
+	    }
+	}
+
+    /* if three corners are enclosed, determine whether the corner in question
+       is east or west of the center of the aperture */
+
+    else {
+	if (pcol > ccol) {
+	    ydiff = dr + 0.5;
+	    if (prow >= crow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+
+	    x0 = ccol + sqrt (rad*rad - ydiff*ydiff);
+	    frac = 1.0 - (ydiff * (pcol + 0.5 - x0)) +
+		   apint ((pcol + 0.5 - ccol), rad) -
+		   apint ((x0 - ccol), rad);
+	    return (frac);
+	    }
+	else {
+	    ydiff = dr + 0.5;
+	    if (prow >= crow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+	    x0 = ccol - sqrt (rad*rad - ydiff*ydiff);
+	    frac = 1. - ydiff*(x0 - pcol + 0.5) +
+		   apint ((x0 - ccol),rad) -
+		   apint ((pcol - 0.5 - ccol),rad);
+	    return (frac);
+	    }
+	}
+}
+
+
+/* APINT -- Evaluate the integral of sqrt (rad**2 - x**2) dx at one limit */
+
+static double
+apint (x, rad)
+
+double	x;
+double	rad;
+{
+    double x2,rad2,arg,arg2,arcsin,pi;
+
+    pi = 3.141592654;
+
+    arg = x / rad;
+    x2 = x * x;
+    rad2 = rad * rad;
+    arg2 = x2 / rad2;
+
+    arcsin = atan2 (arg, sqrt (1.0 - arg2));
+
+    if ((1. - fabs (arg)) < 0.000001) {
+	if (arg >= 0)
+	    arcsin = pi / 2.0;
+	else
+	    arcsin = -pi / 2.0;
+	}
+
+    return (0.5 * (x * sqrt (rad2 - x2) + rad2 * arcsin));
+}
+
+/* Jul  4 1984	Original Fortran program by Sam Conner at MIT
+ * Mar  2 1987	Original VAX Unix version
+ * Mar  2 1987	declare undecldared variables x, y, and j
+ *
+ * Jan  8 1993	Modify to handle arbitrary byte-per-pixel images
+ * Apr 16 1993	Declare undeclared variables iy, ic0, and ir0
+ *
+ * Jan 30 2002	Translate from Fortran to C
+ */
diff --git a/Code/src/libwcs/catutil.c b/Code/src/libwcs/catutil.c
new file mode 100644
index 0000000000000000000000000000000000000000..07dc683cc338800944b5b37aff9d42ce2113456a
--- /dev/null
+++ b/Code/src/libwcs/catutil.c
@@ -0,0 +1,3345 @@
+/*** File libwcs/catutil.c
+ *** October 26, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1998-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* int RefCat (refcatname,title,syscat,eqcat,epcat, catprop, nmag)
+ *	Return catalog type code, title, coord. system, proper motion, num mags
+ * char *CatName (refcat, refcatname)
+ *	Return catalog name given catalog type code
+ * char *CatSource (refcat, refcatname)
+ *	Return name for catalog sources given catalog type code
+ * void CatID (catid, refcat)
+ *	Return ID column heading for the given catalog
+ * double CatRad (refcat)
+ *	Return default search radius for the given catalog
+ * char *ProgCat (progname)
+ *	Return catalog name from program name, NULL if none there
+ * char *ProgName (progpath0)
+ *	Return program name from pathname by which program is invoked
+ * void CatNum (refcat, nndec, dnum, numstr)
+ *	Return formatted source number
+ * void SearchLim (cra, cdec, dra, ddec, sys, ra1, ra2, dec1, dec2, verbose)
+ *	Compute limiting RA and Dec from center and half-widths
+ * int CatNumLen (refcat, nndec)
+ *	Return length of source number
+ * int CatNdec (refcat)
+ *	Return number of decimal places in source number, if known
+ * void CatMagName (imag, refcat, magname)
+ *	Returns name of specified magnitude
+ * int CatMagNum (imag, refcat)
+ *	Returns number of magnitude specified by letter as int
+ * int StrNdec (string)
+ *	Returns number of decimal places in a numeric string (-1=not number)
+ * int NumNdec (number)
+ *	Returns number of decimal places in a number
+ * char *DateString (dateform,epoch)
+ *	Return string with epoch of position in desired format
+ * void RefLim (cra,cdec,dra,ddec,sysc,sysr,eqc,eqr,epc,ramin,ramax,decmin,decmax,verbose)
+ *	Compute limiting RA and Dec in new system from center and half-widths
+ * struct Range *RangeInit (string, ndef)
+ *	Return structure containing ranges of numbers
+ * int isrange (string)
+ *	Return 1 if string is a range, else 0
+ * int rstart (range)
+ *	Restart at beginning of range
+ * int rgetn (range)
+ *	Return number of values from range structure
+ * int rgeti4 (range)
+ *	Return next number from range structure as 4-byte integer
+ * int rgetr8 (range)
+ *	Return next number from range structure as 8-byte floating point number
+ * int ageti4 (string, keyword, ival)
+ *	Read int value from a file where keyword=value, anywhere on a line
+ * int agetr8 (string, keyword, dval)
+ *	Read double value from a file where keyword=value, anywhere on a line
+ * int agets (string, keyword, lval, value)
+ *	Read value from a file where keyword=value, anywhere on a line
+ * void bv2sp (bv, b, v, isp)
+ *	approximate spectral type given B - V or B and V magnitudes
+ * void br2sp (br, b, r, isp)
+ *	approximate spectral type given B - R or B and R magnitudes
+ * void vothead (refcat, refcatname, mprop, typecol)
+ *	Print heading for VOTable format catalog search return
+ * void vottail ()
+ *	Print end of VOTable format catalog search return
+ * void	setrevmsg (revmessage)
+ *	Set version/date message for nstarmax=-1 returns from *read subroutines
+ * char	*getrevmsg ()
+ *	Return version/date message for nstarmax=-1 returns from *read subroutines
+ * int	*is2massid (string)
+ *	Return 1 if string is 2MASS ID, else 0
+ * void polfit (x, y, x0, npts, nterms, a, stdev)
+ *	Polynomial least squares fitting program
+ *double polcomp (xi, x0, norder, a)
+ *	Polynomial evaluation Y = A(1) + A(2)*X + A(3)*X^2 + A(3)*X^3 + ...
+ * void moveb (source, dest, nbytes, offs, offd)
+ *	Copy nbytes bytes from source+offs to dest+offd (any data type)
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+static char *revmessage = NULL;	/* Version and date for calling program */
+static char *revmsg0 = "";
+void
+setrevmsg (revmsg)		/* Set version and date string*/
+char *revmsg;
+{ revmessage = revmsg; return; }
+char *
+getrevmsg ()			/* Return version and date string */
+{ if (revmessage == NULL) return (revmsg0);
+  else return (revmessage); }
+
+static int degout = 0;	/* Set to 1 to print coordinates in degrees */
+void
+setlimdeg (degoutx)		/* Set degree output flag */
+int degoutx;
+{ degout = degoutx; return; }
+
+
+/* Return code for reference catalog or its type */
+
+int
+RefCat (refcatname, title, syscat, eqcat, epcat, catprop, nmag)
+
+char	*refcatname;	/* Name of reference catalog */
+char	*title;		/* Description of catalog (returned) */
+int	*syscat;	/* Catalog coordinate system (returned) */
+double	*eqcat;		/* Equinox of catalog (returned) */
+double	*epcat;		/* Epoch of catalog (returned) */
+int	*catprop;	/* 1 if proper motion in catalog (returned) */
+int	*nmag;		/* Number of magnitudes in catalog (returned) */
+{
+    struct StarCat *starcat;
+    int refcat, nbuff;
+
+    *catprop = 0;
+
+    refcat = CatCode (refcatname);
+    if (refcat == GSCACT) {
+	strcpy (title, "HST Guide Stars/ACT");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*nmag = 1;
+	*catprop = 0;
+	}
+    else if (refcat == GSC2) {
+	if (strsrch (refcatname, "22")) {
+	    strcpy (title, "GSC 2.2 Sources");
+	    *catprop = 0;
+	    }
+	else if (strsrch (refcatname, "23")) {
+	    strcpy (title, "GSC 2.3 Sources");
+	    *catprop = 1;
+	    }
+	else {
+	    strcpy (title, "GSC 2.3 Sources");
+	    *catprop = 0;
+	    }
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*nmag = 5;
+	refcat = GSC2;
+	}
+    else if (refcat == GSC) {
+	strcpy (title, "HST Guide Stars");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 0;
+	*nmag = 1;
+	}
+    else if (refcat == SDSS) {
+	strcpy (title, "SDSS Sources");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 0;
+	*nmag = 5;
+	}
+    else if (refcat == SKYBOT) {
+	strcpy (title, "SkyBot Sources");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 1;
+	*nmag = 3;
+	}
+    else if (refcat == UB1) {
+	strcpy (title, "USNO-B1.0 Sources");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 1;
+	*nmag = 5;
+	refcat = UB1;
+	}
+    else if (refcat == YB6) {
+	strcpy (title, "USNO-YB6 Sources");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 1;
+	*nmag = 5;
+	refcat = YB6;
+	}
+    else if (refcat == USA1 || refcat == USA2 || refcat == USAC) {
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*nmag = 2;
+	*catprop = 0;
+	if (strchr (refcatname, '1') != NULL)
+	    strcpy (title, "USNO SA-1.0 Catalog Stars");
+	else if (strchr (refcatname, '2') != NULL)
+	    strcpy (title, "USNO SA-2.0 Catalog Stars");
+	else
+	    strcpy (title, "USNO SA Catalog Stars");
+	}
+    else if (refcat == USNO) {
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 0;
+	*nmag = 1;
+	sprintf (title, "USNO %s Stars", refcatname);
+	}
+    else if (refcat == UA1 || refcat == UA2 || refcat == UAC) {
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 0;
+	*nmag = 2;
+	if (strchr (refcatname, '1') != NULL)
+	    strcpy (title, "USNO A-1.0 Sources");
+	else if (strchr (refcatname, '2') != NULL)
+	    strcpy (title, "USNO A-2.0 Sources");
+	else
+	    strcpy (title, "USNO A Sources");
+	}
+    else if (refcat == UCAC1) {
+	strcpy (title, "USNO UCAC1 Catalog Stars");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 1;
+	*nmag = 1;
+	}
+    else if (refcat == UCAC2) {
+	strcpy (title, "USNO UCAC2 Catalog Stars");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 1;
+	*nmag = 4;
+	}
+    else if (refcat == UCAC3) {
+	strcpy (title, "USNO UCAC3 Catalog Stars");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 1;
+	*nmag = 8;
+	}
+    else if (refcat == UJC) {
+	strcpy (title, "USNO J Catalog Stars");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 0;
+	*nmag = 1;
+	}
+    else if (refcat == SAO) {
+	strcpy (title, "SAO Catalog Stars");
+	starcat = binopen ("SAO");
+	if (starcat == NULL)
+	    starcat = binopen ("SAOra");
+	if (starcat) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *catprop = starcat->mprop;
+	    *nmag = 1;
+	    binclose (starcat);
+	    }
+	}
+    else if (refcat == PPM) {
+	strcpy (title, "PPM Catalog Stars");
+	starcat = binopen ("PPM");
+	if (starcat == NULL)
+	    starcat = binopen ("PPMra");
+	if (starcat) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *catprop = starcat->mprop;
+	    *nmag = 1;
+	    binclose (starcat);
+	    }
+	}
+    else if (refcat == IRAS) {
+	strcpy (title, "IRAS Point Sources");
+	if ((starcat = binopen ("IRAS"))) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *nmag = 1;
+	    *catprop = starcat->mprop;
+	    binclose (starcat);
+	    }
+	}
+    else if (refcat == SKY2K) {
+	strcpy (title, "SKY2000 Master Catalog Stars");
+	starcat = binopen ("sky2k");
+	if (starcat == NULL)
+	    starcat = binopen ("sky2kra");
+	if (starcat) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *catprop = starcat->mprop;
+	    *nmag = 4;
+	    binclose (starcat);
+	    }
+	}
+    else if (refcat == TYCHO2) {
+	strcpy (title, "Tycho 2 Catalog Stars");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 1;
+	*nmag = 2;
+	}
+    else if (refcat == TYCHO2E) {
+	strcpy (title, "Tycho 2 Catalog Stars");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 1;
+	*nmag = 4;
+	}
+    else if (refcat == TYCHO) {
+	strcpy (title, "Tycho Catalog Stars");
+	if ((starcat = binopen ("tycho"))) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *catprop = 1;
+	    *nmag = 2;
+	    binclose (starcat);
+	    }
+	}
+    else if (refcat == HIP) {
+	strcpy (title, "Hipparcos Catalog Stars");
+	if ((starcat = binopen ("hipparcos"))) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *catprop = starcat->mprop;
+	    *nmag = 1;
+	    binclose (starcat);
+	    }
+	}
+    else if (refcat == ACT) {
+	strcpy (title, "ACT Catalog Stars");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 1;
+	*nmag = 2;
+	}
+    else if (refcat == BSC) {
+	strcpy (title, "Bright Star Catalog Stars");
+	if ((starcat = binopen ("BSC5"))) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *catprop = starcat->mprop;
+	    *nmag = 1;
+	    binclose (starcat);
+	    }
+	}
+    else if (refcat == TMPSC || refcat == TMIDR2) {
+	strcpy (title, "2MASS Point Sources");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 0;
+	*nmag = 3;
+	}
+    else if (refcat == TMPSCE) {
+	strcpy (title, "2MASS Point Sources");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 0;
+	*nmag = 6;
+	}
+    else if (refcat == TMXSC) {
+	strcpy (title, "2MASS Extended Sources");
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 0;
+	*nmag = 3;
+	}
+    else if (refcat == USNO) {
+	*syscat = WCS_J2000;
+	*eqcat = 2000.0;
+	*epcat = 2000.0;
+	*catprop = 0;
+	*nmag = 1;
+	sprintf (title, "USNO %s Stars", refcatname);
+	}
+    else if (refcat == BINCAT) {
+	strcpy (title, refcatname);
+	strcat (title, " Catalog Sources");
+	if ((starcat = binopen (refcatname))) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *catprop = starcat->mprop;
+	    *nmag = starcat->nmag;
+	    binclose (starcat);
+	    }
+	}
+    else if (refcat == TABCAT) {
+	strcpy (title, refcatname);
+	strcat (title, " Catalog Sources");
+	if (strchr (refcatname, ','))
+	    nbuff = 0;
+	else
+	    nbuff = 1000;
+	if ((starcat = tabcatopen (refcatname, NULL, nbuff))) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *catprop = starcat->mprop;
+	    *nmag = starcat->nmag;
+	    ctgclose (starcat);
+	    }
+	}
+    else if (refcat != 0) {
+	strcpy (title, refcatname);
+	strcat (title, " Catalog Sources");
+	if ((starcat = ctgopen (refcatname, TXTCAT))) {
+	    *syscat = starcat->coorsys;
+	    *eqcat = starcat->equinox;
+	    *epcat = starcat->epoch;
+	    *catprop = starcat->mprop;
+	    *nmag = starcat->nmag;
+	    ctgclose (starcat);
+	    }
+	}
+    return refcat;
+}
+
+
+/* Return code for reference catalog or its type */
+
+int
+CatCode (refcatname)
+
+char	*refcatname;	/* Name of reference catalog */
+{
+    struct StarCat *starcat;
+    int refcat, nbuff;
+
+    refcat = 0;
+    if (refcatname == NULL)
+	refcat = 0;
+    else if (strlen (refcatname) < 1)
+	refcat = 0;
+    else if (strncasecmp(refcatname,"gsca",4)==0 &&
+	strcsrch(refcatname, ".tab") == NULL)
+	refcat = GSCACT;
+    else if (strncasecmp(refcatname,"gsc2",4)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = GSC2;
+    else if (strncasecmp(refcatname,"sdss",4)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = SDSS;
+    else if (strncasecmp(refcatname,"skyb",4)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = SKYBOT;
+    else if (strncasecmp(refcatname,"gs",2)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = GSC;
+    else if (strncasecmp(refcatname,"ub",2)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = UB1;
+    else if (strncasecmp(refcatname,"ucac1",5)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = UCAC1;
+    else if (strncasecmp(refcatname,"ucac2",5)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = UCAC2;
+    else if (strncasecmp(refcatname,"ucac3",5)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = UCAC3;
+    else if (strncasecmp(refcatname,"usa",3)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL) {
+	if (strchr (refcatname, '1') != NULL)
+	    refcat = USA1;
+	else if (strchr (refcatname, '2') != NULL)
+	    refcat = USA2;
+	else
+	    refcat = USAC;
+	}
+    else if (strncmp (refcatname, ".usnop", 6) == 0)
+	refcat = USNO;
+    else if (strncasecmp(refcatname,"ua",2)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL) {
+	if (strchr (refcatname, '1') != NULL)
+	    refcat = UA1;
+	else if (strchr (refcatname, '2') != NULL)
+	    refcat = UA2;
+	else
+	    refcat = UAC;
+	}
+    else if (strncasecmp(refcatname,"uj",2)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = UJC;
+    else if (strncasecmp(refcatname,"yb6",3)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = YB6;
+    else if (strncasecmp(refcatname,"sky2k",5)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = SKY2K;
+    else if (strncasecmp(refcatname,"sao",3)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL) {
+	starcat = binopen ("SAO");
+	if (starcat == NULL)
+	    starcat = binopen ("SAOra");
+	if (starcat) {
+	    binclose (starcat);
+	    refcat = SAO;
+	    }
+	}
+    else if (strncasecmp(refcatname,"ppm",3)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL) {
+	starcat = binopen ("PPM");
+	if (starcat == NULL)
+	    starcat = binopen ("PPMra");
+	if (starcat) {
+	    binclose (starcat);
+	    refcat = PPM;
+	    }
+	}
+    else if (strncasecmp(refcatname,"iras",4)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL) {
+	if ((starcat = binopen ("IRAS"))) {
+	    binclose (starcat);
+	    refcat = IRAS;
+	    }
+	}
+    else if (strncasecmp(refcatname,"ty",2)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL) {
+	if (strcsrch (refcatname, "2e") != NULL) {
+	    refcat = TYCHO2E;
+	    }
+	else if (strsrch (refcatname, "2") != NULL) {
+	    refcat = TYCHO2;
+	    }
+	else {
+	    if ((starcat = binopen ("tycho"))) {
+		binclose (starcat);
+		refcat = TYCHO;
+		}
+	    }
+	}
+    else if (strncasecmp(refcatname,"hip",3)==0 &&
+	      strcsrch(refcatname, ".tab") == NULL) {
+	if ((starcat = binopen ("hipparcos"))) {
+	    binclose (starcat);
+	    refcat = HIP;
+	    }
+	}
+    else if (strncasecmp(refcatname,"act",3)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL)
+	refcat = ACT;
+    else if (strncasecmp(refcatname,"bsc",3)==0 &&
+	     strcsrch(refcatname, ".tab") == NULL) {
+	if ((starcat = binopen ("BSC5"))) {
+	    binclose (starcat);
+	    refcat = BSC;
+	    }
+	}
+    else if (strcsrch (refcatname,"idr2") &&
+	     strcsrch (refcatname, ".tab") == NULL) {
+	refcat = TMIDR2;
+	}
+    else if ((strncasecmp(refcatname,"2mp",3)==0 ||
+	     strncasecmp(refcatname,"2mc",3)==0 ||
+	     strncasecmp(refcatname,"tmp",3)==0 ||
+	     strncasecmp(refcatname,"tmc",3)==0) &&
+	     strcsrch(refcatname, ".tab") == NULL) {
+	if (strcsrch (refcatname, "ce"))
+	    refcat = TMPSCE;
+	else
+	    refcat = TMPSC;
+	}
+    else if ((strncasecmp(refcatname,"2mx",3)==0 ||
+	     strncasecmp(refcatname,"tmx",3)==0) &&
+	     strcsrch(refcatname, ".tab") == NULL) {
+	refcat = TMXSC;
+	}
+    else if (strcsrch (refcatname, ".usno")) {
+	refcat = USNO;
+	}
+    else if (isbin (refcatname)) {
+	if ((starcat = binopen (refcatname))) {
+	    binclose (starcat);
+	    refcat = BINCAT;
+	    }
+	else
+	    refcat = 0;
+	}
+    else if (istab (refcatname)) {
+	if (strchr (refcatname, ','))
+	    nbuff = 0;
+	else
+	    nbuff = 1000;
+	if ((starcat = tabcatopen (refcatname, NULL, nbuff))) {
+	    ctgclose (starcat);
+	    refcat = TABCAT;
+	    }
+	else
+	    refcat = 0;
+	}
+    else if (refcatname != NULL) {
+	if ((starcat = ctgopen (refcatname, TXTCAT))) {
+	    ctgclose (starcat);
+	    refcat = TXTCAT;
+	    }
+	else
+	    refcat = 0;
+	}
+    else
+	refcat = 0;
+    return refcat;
+}
+
+
+char *
+CatName (refcat, refcatname)
+
+int	refcat;		/* Catalog code */
+char	*refcatname;	/* Catalog file name */
+{
+    char *catname;
+
+    if (refcat < 1 || refcat > NUMCAT)
+	return (refcatname);
+
+    /* Allocate string in which to return a catalog name */
+    catname = (char *)calloc (16, 1);
+
+    if (refcat ==  GSC)		/* HST Guide Star Catalog */
+	strcpy (catname, "GSC");
+    else if (refcat ==  GSCACT)	/* HST GSC revised with ACT */
+	strcpy (catname, "GSC-ACT");
+    else if (refcat ==  GSC2) {	/* GSC II */
+	if (strsrch (refcatname, "22")) {
+	    strcpy (catname, "GSC 2.2");
+	    }
+	else {
+	    strcpy (catname, "GSC 2.3");
+	    }
+	}
+    else if (refcat == YB6)	/* USNO YB6 Star Catalog */
+	strcpy (catname, "USNO-YB6");
+    else if (refcat ==  UJC)	/* USNO UJ Star Catalog */
+	strcpy (catname, "UJC");
+    else if (refcat ==  UAC)	/* USNO A Star Catalog */
+	strcpy (catname, "USNO-A2.0");
+    else if (refcat ==  USAC)	/* USNO SA Star Catalog */
+	strcpy (catname, "USNO-SA2.0");
+    else if (refcat ==  SAO)	/* SAO Star Catalog */
+	strcpy (catname, "SAO");
+    else if (refcat ==  IRAS)	/* IRAS Point Source Catalog */
+	strcpy (catname, "IRAS PSC");
+    else if (refcat ==  SDSS)	/* Sloan Digital Sky Survey */
+	strcpy (catname, "SDSS");
+    else if (refcat ==  PPM)	/* PPM Star Catalog */
+	strcpy (catname, "PPM");
+    else if (refcat ==  TYCHO)	/* Tycho Star Catalog */
+	strcpy (catname, "TYCHO");
+    else if (refcat ==  UA1)	/* USNO A-1.0 Star Catalog */
+	strcpy (catname, "USNO-A1.0");
+    else if (refcat ==  UB1)	/* USNO B-1.0 Star Catalog */
+	strcpy (catname, "USNO-B1.0");
+    else if (refcat ==  UCAC1)	/* USNO UCAC1 Star Catalog */
+	strcpy (catname, "USNO-UCAC1");
+    else if (refcat ==  UCAC2)	/* USNO UCAC2 Star Catalog */
+	strcpy (catname, "USNO-UCAC2");
+    else if (refcat ==  UCAC3)	/* USNO UCAC3 Star Catalog */
+	strcpy (catname, "USNO-UCAC3");
+    else if (refcat ==  UA2)	/* USNO A-2.0 Star Catalog */
+	strcpy (catname, "USNO-A2.0");
+    else if (refcat ==  USA1)	/* USNO SA-1.0 Star Catalog */
+	strcpy (catname, "USNO-SA1.0");
+    else if (refcat ==  USA2)	/* USNO SA-2.0 Star Catalog */
+	strcpy (catname, "USNO-SA2.0");
+    else if (refcat ==  HIP)	/* Hipparcos Star Catalog */
+	strcpy (catname, "Hipparcos");
+    else if (refcat ==  ACT)	/* USNO ACT Star Catalog */
+	strcpy (catname, "ACT");
+    else if (refcat ==  BSC)	/* Yale Bright Star Catalog */
+	strcpy (catname, "BSC");
+    else if (refcat ==  TYCHO2 ||
+	     refcat == TYCHO2E)	/* Tycho-2 Star Catalog */
+	strcpy (catname, "TYCHO-2");
+    else if (refcat ==  TMPSC ||
+	     refcat == TMPSCE)	/* 2MASS Point Source Catalog */
+	strcpy (catname, "2MASS PSC");
+    else if (refcat ==  TMXSC)	/* 2MASS Extended Source Catalog */
+	strcpy (catname, "2MASS XSC");
+    else if (refcat ==  TMIDR2)	/* 2MASS Point Source Catalog */
+	strcpy (catname, "2MASS PSC IDR2");
+    else if (refcat ==  SKY2K)	/* SKY2000 Master Catalog */
+	strcpy (catname, "SKY2000");
+    else if (refcat ==  SKYBOT)	/* SkyBot Solar System Objects */
+	strcpy (catname, "SkyBot");
+    return (catname);
+}
+
+
+char *
+CatSource (refcat, refcatname)
+
+int	refcat;		/* Catalog code */
+char	*refcatname;	/* Catalog file name */
+{
+    char *catname;
+    int lname;
+
+    if (refcat < 1 || refcat > NUMCAT) {
+	if (refcatname == NULL)
+	    lname = 0;
+	else
+	    lname = strlen (refcatname);
+	catname = (char *)calloc (lname + 16, 1);
+	if (lname > 0)
+	    sprintf (catname, "%s sources", refcatname);
+	else
+	    sprintf (catname, "catalog sources");
+	return (catname);
+	}
+
+    /* Allocate string in which to return a catalog name */
+    catname = (char *)calloc (64, 1);
+
+    if (refcat ==  GSC)		/* HST Guide Star Catalog */
+	strcpy (catname, "HST Guide Stars");
+    else if (refcat ==  GSCACT)	/* HST GSC revised with ACT */
+	strcpy (catname, "GSC-ACT Stars");
+    else if (refcat ==  GSC2) {	/* GSC II */
+	if (strsrch (refcatname, "22")) {
+	    strcpy (catname, "GSC 2.2 Stars");
+	    }
+	else {
+	    strcpy (catname, "GSC 2.3 Stars");
+	    }
+	}
+    else if (refcat == YB6)	/* USNO YB6 Star Catalog */
+	strcpy (catname, "USNO-YB6 Stars");
+    else if (refcat ==  UJC)	/* USNO UJ Star Catalog */
+	strcpy (catname, "USNO J Catalog Stars");
+    else if (refcat ==  UAC)	/* USNO A Star Catalog */
+	strcpy (catname, "USNO-A2.0 Stars");
+    else if (refcat ==  USAC)	/* USNO SA Star Catalog */
+	strcpy (catname, "USNO-SA2.0 Stars");
+    else if (refcat ==  SAO)	/* SAO Star Catalog */
+	strcpy (catname, "SAO Catalog Stars");
+    else if (refcat ==  IRAS)	/* IRAS Point Source Catalog */
+	strcpy (catname, "IRAS Point Sources");
+    else if (refcat ==  SDSS)	/* Sloan Digital Sky Survey */
+	strcpy (catname, "SDSS Photmetric Catalog Sources");
+    else if (refcat ==  PPM)	/* PPM Star Catalog */
+	strcpy (catname, "PPM Catalog Stars");
+    else if (refcat ==  TYCHO)	/* Tycho Star Catalog */
+	strcpy (catname, "Tycho Catalog Stars");
+    else if (refcat ==  TYCHO2)	/* Tycho-2 Star Catalog */
+	strcpy (catname, "Tycho-2 Catalog Stars");
+    else if (refcat == TYCHO2E)	/* Tycho-2 Star Catalog */
+	strcpy (catname, "Tycho-2 Catalog Stars with mag error");
+    else if (refcat ==  UA1)	/* USNO A-1.0 Star Catalog */
+	strcpy (catname, "USNO-A1.0 Stars");
+    else if (refcat ==  UB1)	/* USNO B-1.0 Star Catalog */
+	strcpy (catname, "USNO-B1.0 Stars");
+    else if (refcat ==  UCAC1)	/* USNO UCAC1 Star Catalog */
+	strcpy (catname, "USNO-UCAC1 Stars");
+    else if (refcat ==  UCAC2)	/* USNO UCAC2 Star Catalog */
+	strcpy (catname, "USNO-UCAC2 Stars");
+    else if (refcat ==  UCAC3)	/* USNO UCAC3 Star Catalog */
+	strcpy (catname, "USNO-UCAC3 Stars");
+    else if (refcat ==  UA2)	/* USNO A-2.0 Star Catalog */
+	strcpy (catname, "USNO-A2.0 Stars");
+    else if (refcat ==  USA1)	/* USNO SA-1.0 Star Catalog */
+	strcpy (catname, "USNO-SA1.0 Stars");
+    else if (refcat ==  USA2)	/* USNO SA-2.0 Star Catalog */
+	strcpy (catname, "USNO-SA2.0 Stars");
+    else if (refcat ==  HIP)	/* Hipparcos Star Catalog */
+	strcpy (catname, "Hipparcos Catalog Stars");
+    else if (refcat ==  ACT)	/* USNO ACT Star Catalog */
+	strcpy (catname, "ACT Catalog Stars");
+    else if (refcat ==  BSC)	/* Yale Bright Star Catalog */
+	strcpy (catname, "Bright Star Catalog Stars");
+    else if (refcat ==  TMPSC)	/* 2MASS Point Source Catalog */
+	strcpy (catname, "2MASS Point Sources");
+    else if (refcat == TMPSCE)	/* 2MASS Point Source Catalog */
+	strcpy (catname, "2MASS Point Sources with mag error");
+    else if (refcat ==  TMXSC)	/* 2MASS Extended Source Catalog */
+	strcpy (catname, "2MASS Extended Sources");
+    else if (refcat ==  TMIDR2)	/* 2MASS Point Source Catalog */
+	strcpy (catname, "2MASS-IDR2 Point Sources");
+    else if (refcat ==  SKY2K)	/* SKY2000 Master Catalog */
+	strcpy (catname, "SKY2000 Catalog Stars");
+    else if (refcat ==  SKYBOT)	/* SkyBot Solar System Objects */
+	strcpy (catname, "SkyBot Objects");
+    return (catname);
+}
+
+
+void
+CatID (catid, refcat)
+
+char	*catid;		/* Catalog ID (returned) */
+int	refcat;		/* Catalog code */
+{
+    if (refcat == ACT)
+	strcpy (catid, "act_id     ");
+    else if (refcat == BSC)
+	strcpy (catid, "bsc_id    ");
+    else if (refcat == GSC || refcat == GSCACT)
+	strcpy (catid, "gsc_id    ");
+    else if (refcat == GSC2)
+	strcpy (catid, "gsc2_id        ");
+    else if (refcat == SDSS)
+	strcpy (catid, "sdss_id            ");
+    else if (refcat == USAC)
+	strcpy (catid,"usac_id       ");
+    else if (refcat == USA1)
+	strcpy (catid,"usa1_id       ");
+    else if (refcat == USA2)
+	strcpy (catid,"usa2_id       ");
+    else if (refcat == UAC)
+	strcpy (catid,"usnoa_id      ");
+    else if (refcat == UA1)
+	strcpy (catid,"usnoa1_id     ");
+    else if (refcat == UB1)
+	strcpy (catid,"usnob1_id    ");
+    else if (refcat == YB6)
+	strcpy (catid,"usnoyb6_id   ");
+    else if (refcat == UA2)
+	strcpy (catid,"usnoa2_id     ");
+    else if (refcat == UCAC1)
+	strcpy (catid,"ucac1_id  ");
+    else if (refcat == UCAC2)
+	strcpy (catid,"ucac2_id  ");
+    else if (refcat == UCAC3)
+	strcpy (catid,"ucac3_id  ");
+    else if (refcat == UJC)
+	strcpy (catid,"usnoj_id     ");
+    else if (refcat == TMPSC || refcat == TMPSCE)
+	strcpy (catid,"2mass_id      ");
+    else if (refcat == TMXSC)
+	strcpy (catid,"2mx_id        ");
+    else if (refcat == SAO)
+	strcpy (catid,"sao_id ");
+    else if (refcat == PPM)
+	strcpy (catid,"ppm_id ");
+    else if (refcat == IRAS)
+	strcpy (catid,"iras_id");
+    else if (refcat == TYCHO)
+	strcpy (catid,"tycho_id  ");
+    else if (refcat == TYCHO2 || refcat == TYCHO2E)
+	strcpy (catid,"tycho2_id ");
+    else if (refcat == HIP)
+	strcpy (catid,"hip_id ");
+    else if (refcat == SKY2K)
+	strcpy (catid,"sky_id ");
+    else if (refcat == SKYBOT)
+	strcpy (catid,"skybot_id ");
+    else
+	strcpy (catid,"id              ");
+
+    return;
+}
+
+double
+CatRad (refcat)
+
+int	refcat;		/* Catalog code */
+{
+    if (refcat==GSC || refcat==GSCACT || refcat==UJC || refcat==USAC ||
+	refcat==USA1 || refcat==USA2 ||
+	refcat == UCAC1 || refcat == UCAC2 || refcat == UCAC3)
+	return (900.0);
+    else if (refcat==UAC  || refcat==UA1  || refcat==UA2)
+	return (120.0);
+    else if (refcat == UB1 || refcat==SDSS)
+	return (120.0);
+    else if (refcat==GSC2)
+	return (120.0);
+    else if (refcat==TMPSC || refcat==TMPSCE || refcat==TMIDR2)
+	return (120.0);
+    else if (refcat==TMXSC)
+	return (900.0);
+    else if (refcat==GSC2)
+	return (120.0);
+    else if (refcat==SAO || refcat==PPM || refcat==IRAS || refcat == SKY2K ||
+	    refcat==SKYBOT)
+	return (5000.0);
+    else
+	return (1800.0);
+}
+
+
+char *
+ProgName (progpath0)
+
+char *progpath0;	/* Pathname by which program is invoked */
+{
+    char *progpath, *progname;
+    int i, lpath;
+
+    lpath = (strlen (progpath0) + 2) / 8;
+    lpath = (lpath + 1) * 8;;
+    progpath = (char *) calloc (lpath, 1);
+    strcpy (progpath, progpath0);
+    progname = progpath;
+    for (i = strlen (progpath); i > -1; i--) {
+        if (progpath[i] > 63 && progpath[i] < 90)
+            progpath[i] = progpath[i] + 32;
+        if (progpath[i] == '/') {
+            progname = progpath + i + 1;
+            break;
+            }
+	}
+    return (progname);
+}
+
+
+char *
+ProgCat (progname)
+
+char *progname;	/* Program name which might contain catalog code */
+{
+    char *refcatname;
+    refcatname = NULL;
+
+    if (strcsrch (progname,"gsca") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "gscact");
+	}
+    else if (strcsrch (progname,"gsc2") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "gsc2");
+	}
+    else if (strcsrch (progname,"gsc") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "gsc");
+	}
+    else if (strcsrch (progname,"sdss") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "sdss");
+	}
+    else if (strcsrch (progname,"uac") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "uac");
+	}
+    else if (strcsrch (progname,"ua1") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "ua1");
+	}
+    else if (strcsrch (progname,"ub") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "ub1");
+	}
+    else if (strcsrch (progname,"yb6") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "yb6");
+	}
+    else if (strcsrch (progname,"ua2") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "ua2");
+	}
+    else if (strcsrch (progname,"usac") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "usac");
+	}
+    else if (strcsrch (progname,"usa1") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "usa1");
+	}
+    else if (strcsrch (progname,"usa2") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "usa2");
+	}
+    else if (strcsrch (progname,"ucac1") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "ucac1");
+	}
+    else if (strcsrch (progname,"ucac2") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "ucac2");
+	}
+    else if (strcsrch (progname,"ucac3") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "ucac3");
+	}
+    else if (strcsrch (progname,"ujc") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "ujc");
+	}
+    else if (strcsrch (progname,"sao") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "sao");
+	}
+    else if (strcsrch (progname,"ppm") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "ppm");
+	}
+    else if (strcsrch (progname,"ira") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "iras");
+	}
+    else if (strcsrch (progname,"ty") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	if (strcsrch (progname, "2e") != NULL)
+	    strcpy (refcatname, "tycho2e");
+	else if (strcsrch (progname, "2") != NULL)
+	    strcpy (refcatname, "tycho2");
+	else
+	    strcpy (refcatname, "tycho");
+	}
+    else if (strcsrch (progname,"hip") != NULL) {
+	refcatname = (char *) calloc (1,16);
+	strcpy (refcatname, "hipparcos");
+	}
+    else if (strcsrch (progname,"act") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "act");
+	}
+    else if (strcsrch (progname,"bsc") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "bsc");
+	}
+    else if (strcsrch (progname,"sky2k") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "sky2k");
+	}
+    else if (strcsrch (progname,"skybot") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "skybot");
+	}
+    else if (strcsrch (progname,"2mp") != NULL ||
+	strcsrch (progname,"tmc") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	if (strcsrch (progname,"ce"))
+	    strcpy (refcatname, "tmce");
+	else
+	    strcpy (refcatname, "tmc");
+	}
+    else if (strcsrch (progname,"2mx") != NULL ||
+	strcsrch (progname,"tmx") != NULL) {
+	refcatname = (char *) calloc (1,8);
+	strcpy (refcatname, "tmx");
+	}
+    else
+	refcatname = NULL;
+
+    return (refcatname);
+}
+
+
+void
+CatNum (refcat, nnfld, nndec, dnum, numstr)
+
+int	refcat;		/* Catalog code */
+int	nnfld;		/* Number of characters in number (from CatNumLen) */
+			/* Print leading zeroes if negative */
+int	nndec;		/* Number of decimal places ( >= 0) */
+			/* Omit leading spaces if negative */
+double	dnum;		/* Catalog number of source */
+char	*numstr;	/* Formatted number (returned) */
+
+{
+    char nform[16];	/* Format for star number */
+    int lnum, i;
+
+    /* USNO A1.0, A2.0, SA1.0, or SA2.0 Catalogs */
+    if (refcat == USAC || refcat == USA1 || refcat == USA2 ||
+	refcat == UAC  || refcat == UA1  || refcat == UA2) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%013.8f", dnum);
+	else
+	    sprintf (numstr, "%13.8f", dnum);
+	}
+
+    /* USNO-B1.0  and USNO-YB6 */
+    else if (refcat == UB1 || refcat == YB6) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%012.7f", dnum);
+	else
+	    sprintf (numstr, "%12.7f", dnum);
+	}
+
+    /* USNO-UCAC1 */
+    else if (refcat == UCAC1) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%010.6f", dnum);
+	else
+	    sprintf (numstr, "%10.6f", dnum);
+	}
+
+    /* USNO-UCAC2 */
+    else if (refcat == UCAC2) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%010.6f", dnum);
+	else
+	    sprintf (numstr, "%10.6f", dnum);
+	}
+
+    /* USNO-UCAC3 */
+    else if (refcat == UCAC3) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%010.6f", dnum);
+	else
+	    sprintf (numstr, "%10.6f", dnum);
+	}
+
+    /* SDSS */
+    else if (refcat == SDSS) {
+	sprintf (numstr, "582%015.0f", dnum);
+	}
+
+    /* GSC II */
+    else if (refcat == GSC2) {
+	if (nnfld < 0) {
+	    if (dnum > 0)
+		sprintf (numstr, "N%.0f", (dnum+0.01));
+	    else
+		sprintf (numstr, "S%.0f", (-dnum + 0.01));
+	    lnum = strlen (numstr);
+	    if (lnum < -nnfld) {
+		for ( i = lnum; i < -nnfld; i++)
+		    strcat (numstr, " ");
+		}
+	    }
+	else {
+	    if (dnum > 0)
+		sprintf (numstr, "N%.0f", (dnum+0.5));
+	    else
+		sprintf (numstr, "S%.0f", (-dnum + 0.5));
+	    }
+	}
+
+    /* 2MASS Point Source Catalogs */
+    else if (refcat == TMPSC || refcat == TMPSCE) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%011.6f", dnum);
+	else
+	    sprintf (numstr, "%11.6f", dnum);
+	}
+
+    /* 2MASS Extended Source Catalog */
+    else if (refcat == TMXSC) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%011.6f", dnum);
+	else
+	    sprintf (numstr, "%11.6f", dnum);
+	}
+
+    /* 2MASS Point Source Catalogs */
+    else if (refcat == TMIDR2) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%010.7f", dnum);
+	else
+	    sprintf (numstr, "%10.7f", dnum);
+	}
+
+    /* USNO Plate Catalog */
+    else if (refcat == USNO) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%07d", (int)(dnum+0.5));
+	else
+	    sprintf (numstr, "%7d", (int)(dnum+0.5));
+	}
+
+    /* USNO UJ 1.0 Catalog */
+    else if (refcat == UJC) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%012.7f", dnum);
+	else
+	    sprintf (numstr, "%12.7f", dnum);
+	}
+
+    /* HST Guide Star Catalog */
+    else if (refcat == GSC || refcat == GSCACT) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%09.4f", dnum);
+	else
+	    sprintf (numstr, "%9.4f", dnum);
+	}
+
+    /* SAO, PPM, or IRAS Point Source Catalogs (TDC binary format) */
+    else if (refcat==SAO || refcat==PPM || refcat==IRAS || refcat==BSC ||
+	     refcat==HIP) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%06d", (int)(dnum+0.5));
+	else
+	    sprintf (numstr, "%6d", (int)(dnum+0.5));
+	}
+
+    /* SKY2000 Catalog (TDC binary format) */
+    else if (refcat==SKY2K) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%07d", (int)(dnum+0.5));
+	else
+	    sprintf (numstr, "%7d", (int)(dnum+0.5));
+	}
+
+
+    /* Tycho or ACT catalogs */
+    else if (refcat==TYCHO || refcat==TYCHO2 ||
+	     refcat == TYCHO2E || refcat==ACT) {
+	if (nnfld < 0)
+	    sprintf (numstr, "%010.5f", dnum);
+	else
+	    sprintf (numstr, "%10.5f", dnum);
+	}
+
+    /* Starbase tab-separated, TDC binary, or TDC ASCII catalogs */
+    else if (nndec > 0) {
+	if (nnfld > 0)
+	    sprintf (nform,"%%%d.%df", nnfld, nndec);
+	else if (nnfld < 0)
+	    sprintf (nform,"%%0%d.%df", -nnfld, nndec);
+	else
+	    sprintf (nform,"%%%d.%df", nndec+5, nndec);
+	sprintf (numstr, nform, dnum);
+	}
+    else if (nnfld > 10) {
+	sprintf (nform,"%%%d.0f", nnfld);
+	sprintf (numstr, nform, dnum+0.49);
+	}
+    else if (nnfld > 0) {
+	sprintf (nform,"%%%dd", nnfld);
+	sprintf (numstr, nform, (int)(dnum+0.49));
+	}
+    else if (nnfld < 0) {
+	sprintf (nform,"%%0%dd", -nnfld);
+	sprintf (numstr, nform, (int)(dnum+0.49));
+	}
+    else if (nndec < 0)
+	sprintf (numstr, "%d", (int)(dnum+0.49));
+    else
+	sprintf (numstr, "%6d", (int)(dnum+0.49));
+
+    return;
+}
+
+
+int
+CatNumLen (refcat, maxnum, nndec)
+
+int	refcat;		/* Catalog code */
+double	maxnum;		/* Maximum ID number */
+			/* (Ignored for standard catalogs) */
+int	nndec;		/* Number of decimal places ( >= 0) */
+
+{
+    int ndp;		/* Number of characters for decimal point */
+
+    /* USNO A1.0, A2.0, SA1.0, or SA2.0 Catalogs */
+    if (refcat == USAC || refcat == USA1 || refcat == USA2 ||
+	refcat == UAC  || refcat == UA1  || refcat == UA2)
+	return (13);
+
+    /* USNO-B1.0 and YB6 */
+    else if (refcat == UB1 || refcat == YB6)
+	return (12);
+
+    /* GSC II */
+    else if (refcat == GSC2)
+	return (13);
+
+    /* 2MASS Point Source Catalog */
+    else if (refcat == TMPSC || refcat == TMPSCE)
+	return (11);
+    else if (refcat == TMIDR2)
+	return (10);
+
+    /* 2MASS Extended Source Catalog */
+    else if (refcat == TMXSC)
+	return (11);
+
+    /* UCAC1 Catalog */
+    else if (refcat == UCAC1)
+	return (10);
+
+    /* UCAC2 Catalog */
+    else if (refcat == UCAC2)
+	return (10);
+
+    /* UCAC3 Catalog */
+    else if (refcat == UCAC3)
+	return (10);
+
+    /* USNO Plate Catalogs */
+    else if (refcat == USNO)
+	return (7);
+
+    /* USNO UJ 1.0 Catalog */
+    else if (refcat == UJC)
+	return (12);
+
+    /* SDSS Catalog */
+    else if (refcat == SDSS)
+	return (18);
+
+    /* SkyBot Objects */
+    else if (refcat == SKYBOT)
+	return (6);
+
+    /* HST Guide Star Catalog */
+    else if (refcat == GSC || refcat == GSCACT)
+	return (9);
+
+    /* SAO, PPM, Hipparcos, or IRAS Point Source Catalogs (TDC binary format) */
+    else if (refcat==SAO || refcat==PPM || refcat==IRAS || refcat==BSC ||
+	     refcat==HIP)
+	return (6);
+
+    /* SKY2000 Catalog (TDC binary format) */
+    else if (refcat==SKY2K)
+	return (7);
+
+    /* Tycho, Tycho2, or ACT catalogs */
+    else if (refcat == TYCHO || refcat == TYCHO2 ||
+	     refcat == TYCHO2E || refcat == ACT)
+	return (10);
+
+    /* Starbase tab-separated, TDC binary, or TDC ASCII catalogs */
+    else {
+	if (nndec > 0)
+	    ndp = 1;
+	else {
+	    if ((nndec = NumNdec (maxnum)) > 0)
+		ndp = 1;
+	    else
+		ndp = 0;
+	    }
+	if (maxnum < 10.0)
+	    return (1 + nndec + ndp);
+	else if (maxnum < 100.0)
+	    return (2 + nndec + ndp);
+	else if (maxnum < 1000.0)
+	    return (3 + nndec + ndp);
+	else if (maxnum < 10000.0)
+	    return (4 + nndec + ndp);
+	else if (maxnum < 100000.0)
+	    return (5 + nndec + ndp);
+	else if (maxnum < 1000000.0)
+	    return (6 + nndec + ndp);
+	else if (maxnum < 10000000.0)
+	    return (7 + nndec + ndp);
+	else if (maxnum < 100000000.0)
+	    return (8 + nndec + ndp);
+	else if (maxnum < 1000000000.0)
+	    return (9 + nndec + ndp);
+	else if (maxnum < 10000000000.0)
+	    return (10 + nndec + ndp);
+	else if (maxnum < 100000000000.0)
+	    return (11 + nndec + ndp);
+	else if (maxnum < 1000000000000.0)
+	    return (12 + nndec + ndp);
+	else if (maxnum < 10000000000000.0)
+	    return (13 + nndec + ndp);
+	else
+	    return (14 + nndec + ndp);
+	}
+}
+
+
+/* Return number of decimal places in catalogued numbers, if known */
+int
+CatNdec (refcat)
+
+int	refcat;		/* Catalog code */
+
+{
+    /* USNO A1.0, A2.0, SA1.0, or SA2.0 Catalogs */
+    if (refcat == USAC || refcat == USA1 || refcat == USA2 ||
+	refcat == UAC  || refcat == UA1  || refcat == UA2)
+	return (8);
+
+    /* USNO B1.0 and USNO YB6 */
+    else if (refcat == UB1 || refcat == YB6)
+	return (7);
+
+    /* GSC II */
+    else if (refcat == GSC2)
+	return (0);
+
+    /* SDSS */
+    else if (refcat == SDSS)
+	return (0);
+
+    /* SkyBot */
+    else if (refcat == SKYBOT)
+	return (0);
+
+    /* 2MASS Point Source Catalog */
+    else if (refcat == TMPSC || refcat == TMPSCE)
+	return (6);
+
+    /* 2MASS Extended Source Catalog */
+    else if (refcat == TMXSC)
+	return (6);
+
+    /* 2MASS Point Source Catalog */
+    else if (refcat == TMIDR2)
+	return (7);
+
+    /* USNO Plate Catalogs */
+    else if (refcat == USNO)
+	return (0);
+
+    /* UCAC1 Catalog */
+    else if (refcat == UCAC1)
+	return (6);
+
+    /* UCAC2 Catalog */
+    else if (refcat == UCAC2)
+	return (6);
+
+    /* UCAC3 Catalog */
+    else if (refcat == UCAC3)
+	return (6);
+
+    /* USNO UJ 1.0 Catalog */
+    else if (refcat == UJC)
+	return (7);
+
+    /* HST Guide Star Catalog */
+    else if (refcat == GSC || refcat == GSCACT)
+	return (4);
+
+    /* SAO, PPM, Hipparcos, or IRAS Point Source Catalogs (TDC binary format) */
+    else if (refcat==SAO || refcat==PPM || refcat==IRAS || refcat==BSC ||
+	     refcat==HIP || refcat == SKY2K)
+	return (0);
+
+    /* Tycho, Tycho2, or ACT catalogs */
+    else if (refcat == TYCHO || refcat == TYCHO2 ||
+	     refcat == TYCHO2E || refcat == ACT)
+	return (5);
+
+    /* Starbase tab-separated, TDC binary, or TDC ASCII catalogs */
+    else
+	return (-1);
+}
+
+/* Return name of specified magnitude */
+
+void
+CatMagName (imag, refcat, magname)
+
+int	imag;		/* Sequence number of magnitude */
+int	refcat;		/* Catalog code */
+char	*magname;	/* Name of magnitude, returned */
+{
+    if (refcat == UAC  || refcat == UA1  || refcat == UA2 ||
+	refcat == USAC || refcat == USA1 || refcat == USA2) {
+	if (imag == 2)
+	    strcpy (magname, "MagR");
+	else
+	    strcpy (magname, "MagB");
+	}
+    else if (refcat == UB1) {
+	if (imag == 5)
+	    strcpy (magname, "MagN");
+	else if (imag == 4)
+	    strcpy (magname, "MagR2");
+	else if (imag == 3)
+	    strcpy (magname, "MagB2");
+	else if (imag == 2)
+	    strcpy (magname, "MagR1");
+	else
+	    strcpy (magname, "MagB1");
+	}
+    else if (refcat == YB6) {
+	if (imag == 5)
+	    strcpy (magname, "MagK");
+	else if (imag == 4)
+	    strcpy (magname, "MagH");
+	else if (imag == 3)
+	    strcpy (magname, "MagJ");
+	else if (imag == 2)
+	    strcpy (magname, "MagR");
+	else
+	    strcpy (magname, "MagB");
+	}
+    else if (refcat == SDSS) {
+	if (imag == 5)
+	    strcpy (magname, "Magz");
+	else if (imag == 4)
+	    strcpy (magname, "Magi");
+	else if (imag == 3)
+	    strcpy (magname, "Magr");
+	else if (imag == 2)
+	    strcpy (magname, "Magg");
+	else
+	    strcpy (magname, "Magu");
+	}
+    else if (refcat==TYCHO || refcat==TYCHO2 || refcat==HIP || refcat==ACT) {
+	if (imag == 2)
+	    strcpy (magname, "MagV");
+	else
+	    strcpy (magname, "MagB");
+	}
+    else if (refcat==TYCHO2E) {
+	if (imag == 1)
+	    strcpy (magname, "MagB");
+	else if (imag == 3)
+	    strcpy (magname, "MagBe");
+	else if (imag == 4)
+	    strcpy (magname, "MagVe");
+	else
+	    strcpy (magname, "MagV");
+	}
+    else if (refcat==GSC2) {
+	if (imag == 2)
+	    strcpy (magname, "MagJ");
+	else if (imag == 3)
+	    strcpy (magname, "MagN");
+	else if (imag == 4)
+	    strcpy (magname, "MagU");
+	else if (imag == 5)
+	    strcpy (magname, "MagB");
+	else if (imag == 6)
+	    strcpy (magname, "MagV");
+	else if (imag == 7)
+	    strcpy (magname, "MagR");
+	else if (imag == 8)
+	    strcpy (magname, "MagI");
+	else
+	    strcpy (magname, "MagF");
+	}
+    else if (refcat==SKY2K) {
+	if (imag == 1)
+	    strcpy (magname, "MagB");
+	else if (imag == 2)
+	    strcpy (magname, "MagV");
+	else if (imag == 3)
+	    strcpy (magname, "MagP");
+	else
+	    strcpy (magname, "MagPv");
+	}
+    else if (refcat==TMPSC || refcat == TMXSC) {
+	if (imag == 1)
+	    strcpy (magname, "MagJ");
+	else if (imag == 2)
+	    strcpy (magname, "MagH");
+	else
+	    strcpy (magname, "MagK");
+	}
+    else if (refcat==TMPSCE) {
+	if (imag == 1)
+	    strcpy (magname, "MagJ");
+	else if (imag == 2)
+	    strcpy (magname, "MagH");
+	else if (imag == 3)
+	    strcpy (magname, "MagK");
+	else if (imag == 4)
+	    strcpy (magname, "MagJe");
+	else if (imag == 5)
+	    strcpy (magname, "MagHe");
+	else if (imag == 6)
+	    strcpy (magname, "MagKe");
+	}
+    else if (refcat==SKYBOT)
+	strcpy (magname, "MagV");
+    else
+	strcpy (magname, "Mag");
+    return;
+}
+
+/* Return number of magnitude specified by int of letter */
+
+int
+CatMagNum (imag, refcat)
+
+int	imag;		/* int of magnitude letter */
+int	refcat;		/* Catalog code */
+{
+    char cmag = (char) imag;	/* Letter name of magnitude */
+
+    /* Make letter upper case */
+    if (cmag > 96)
+	cmag = cmag - 32;
+ 
+    if (refcat == UAC  || refcat == UA1  || refcat == UA2 ||
+	refcat == USAC || refcat == USA1 || refcat == USA2) {
+	if (cmag == 'R')
+	    return (2);
+	else
+	    return (1);	/* B */
+	}
+    if (refcat == UB1) {
+	if (cmag == 'N')
+	    return (5);
+	else if (cmag == 'R')
+	    return (4);
+	else
+	    return (3);	/* B */
+	}
+    else if (refcat == YB6) {
+	if (cmag == 'K')
+	    return (5);
+	else if (cmag == 'H')
+	    return (4);
+	else if (cmag == 'J')
+	    return (3);
+	else if (cmag == 'R')
+	    return (2);
+	else if (cmag == 'B')
+	    return (1);
+	else
+	    return (3);	/* J */
+	}
+    else if (refcat == SKYBOT) {
+	return (1);
+	}
+    else if (refcat == SDSS) {
+	if (cmag == 'Z')
+	    return (5);
+	else if (cmag == 'I')
+	    return (4);
+	else if (cmag == 'R')
+	    return (3);
+	else if (cmag == 'G')
+	    return (2);
+	else if (cmag == 'B')
+	    return (1);
+	else
+	    return (2);	/* G */
+	}
+    else if (refcat==TYCHO || refcat==TYCHO2 || refcat==HIP || refcat==ACT) {
+	if (cmag == 'B')
+	    return (1);
+	else
+	    return (2);	/* V */
+	}
+    else if (refcat==GSC2) {
+	if (cmag == 'J')
+	    return (2);
+	else if (cmag == 'N')
+	    return (3);
+	else if (cmag == 'U')
+	    return (4);
+	else if (cmag == 'B')
+	    return (5);
+	else if (cmag == 'V')
+	    return (6);
+	else if (cmag == 'R')
+	    return (7);
+	else if (cmag == 'I')
+	    return (8);
+	else
+	    return (1);	/* F */
+	}
+    else if (refcat==TMPSC || refcat == TMXSC) {
+	if (cmag == 'J')
+	    return (1);
+	else if (cmag == 'H')
+	    return (2);
+	else
+	    return (3);	/* K */
+	}
+    else
+	return (1);
+}
+
+
+/* Return number of decimal places in numeric string (-1 if not number) */
+
+int
+StrNdec (string)
+
+char *string;	/* Numeric string */
+{
+    char *cdot;
+    int lstr;
+
+    if (notnum (string))
+	return (-1);
+    else {
+	lstr = strlen (string);
+	if ((cdot = strchr (string, '.')) == NULL)
+	    return (0);
+	else
+	    return (lstr - (cdot - string));
+	}
+}
+
+
+/* Return number of decimal places in a number */
+
+int
+NumNdec (number)
+
+double number;	/* Floating point number */
+{
+    char nstring[16];
+    char format[16];
+    int fracpart;
+    int ndec, ndmax;
+    double shift;
+
+    if (number < 10.0) {
+	ndmax = 12;
+	shift = 1000000000000.0;
+	}
+    else if (number < 100.0) {
+	ndmax = 11;
+	shift = 100000000000.0;
+	}
+    else if (number < 1000.0) {
+	ndmax = 10;
+	shift = 10000000000.0;
+	}
+    else if (number < 10000.0) {
+	ndmax = 9;
+	shift = 1000000000.0;
+	}
+    else if (number < 100000.0) {
+	ndmax = 8;
+	shift = 100000000.0;
+	}
+    else if (number < 1000000.0) {
+	ndmax = 7;
+	shift = 10000000.0;
+	}
+    else if (number < 10000000.0) {
+	ndmax = 6;
+	shift = 1000000.0;
+	}
+    else if (number < 100000000.0) {
+	ndmax = 5;
+	shift = 100000.0;
+	}
+    else if (number < 1000000000.0) {
+	ndmax = 4;
+	shift = 10000.0;
+	}
+    else if (number < 10000000000.0) {
+	ndmax = 3;
+	shift = 1000.0;
+	}
+    else if (number < 100000000000.0) {
+	ndmax = 2;
+	shift = 100.0;
+	}
+    else if (number < 1000000000000.0) {
+	ndmax = 1;
+	shift = 10.0;
+	}
+    else
+	return (0);
+    fracpart = (int) (((number - floor (number)) * shift) + 0.5);
+    sprintf (format, "%%0%dd", ndmax);
+    sprintf (nstring, format, fracpart);
+    for (ndec = ndmax; ndec > 0; ndec--) {
+	if (nstring[ndec-1] != '0')
+	    break;
+	}
+    return (ndec);
+}
+
+
+static int dateform = 0 ;
+
+void
+setdateform (dateform0)
+int dateform0;
+{ dateform = dateform0; return; }
+
+
+/* DateString-- Return string with epoch of position in desired format */
+char *
+DateString (epoch, tabout)
+
+double	epoch;
+int	tabout;
+{
+    double year;
+    char *temp, *temp1;
+    temp = calloc (16, 1);
+
+    if (dateform < 1)
+	dateform = EP_MJD;
+
+    if (dateform == EP_EP) {
+	if (tabout)
+	    sprintf (temp, "	%9.4f", epoch);
+	else
+	    sprintf (temp, " %9.4f", epoch);
+	}
+    else if (dateform == EP_JD) {
+	if (epoch == 0.0)
+	    year = 0.0;
+	else
+	    year = ep2jd (epoch);
+	if (tabout)
+	    sprintf (temp, "	%13.5f", year);
+	else
+	    sprintf (temp, " %13.5f", year);
+	}
+    else if (dateform == EP_MJD) {
+	if (epoch == 0.0)
+	    year = 0.0;
+	else
+	    year = ep2mjd (epoch);
+	if (tabout)
+	    sprintf (temp, "	%11.5f", year);
+	else
+	    sprintf (temp, " %11.5f", year);
+	}
+    else {
+	if (epoch == 0.0) {
+	    if (tabout)
+		sprintf (temp,"	0000-00-00");
+	    else
+		sprintf (temp," 0000-00-00");
+	    if (dateform == EP_ISO)
+		sprintf (temp,"T00:00");
+	    }
+	else {
+	    temp1 = ep2fd (epoch);
+	    if (dateform == EP_FD && strlen (temp1) > 10)
+		temp1[10] = (char) 0;
+	    if (dateform == EP_ISO && strlen (temp1) > 16)
+		temp1[16] = (char) 0;
+	    if (tabout)
+		sprintf (temp, "	%s", temp1);
+	    else
+		sprintf (temp, " %s", temp1);
+	    free (temp1);
+	    }
+	}
+    return (temp);
+}
+
+
+/* SEARCHLIM-- Set RA and Dec limits for search given center and dimensions */
+void
+SearchLim (cra, cdec, dra, ddec, syscoor, ra1, ra2, dec1, dec2, verbose)
+
+double	cra, cdec;	/* Center of search area  in degrees */
+double	dra, ddec;	/* Horizontal and vertical half-widths in degrees */
+int	syscoor;	/* Coordinate system */
+double	*ra1, *ra2;	/* Right ascension limits in degrees */
+double	*dec1, *dec2;	/* Declination limits in degrees */
+int	verbose;	/* 1 to print limits, else 0 */
+
+{
+    double dec;
+
+    /* Set right ascension limits for search */
+    *ra1 = cra - dra;
+    *ra2 = cra + dra;
+
+    /* Keep right ascension between 0 and 360 degrees */
+    if (syscoor != WCS_XY) {
+	if (*ra1 < 0.0)
+	    *ra1 = *ra1 + 360.0;
+	if (*ra2 > 360.0)
+	    *ra2 = *ra2 - 360.0;
+	}
+
+    /* Set declination limits for search */
+    *dec1 = cdec - ddec;
+    *dec2 = cdec + ddec;
+
+    /* dec1 is always the smallest declination */
+    if (*dec1 > *dec2) {
+	dec = *dec1;
+	*dec1 = *dec2;
+	*dec2 = dec;
+	}
+
+    /* Search zones which include the poles cover 360 degrees in RA */
+    if (syscoor != WCS_XY) {
+	if (*dec1 < -90.0) {
+	    *dec1 = -90.0;
+	    *ra1 = 0.0;
+	    *ra2 = 359.99999;
+	    }
+	if (*dec2 > 90.0) {
+	    *dec2 = 90.0;
+	    *ra1 = 0.0;
+	    *ra2 = 359.99999;
+	    }
+	}
+
+    if (verbose) {
+	char rstr1[16],rstr2[16],dstr1[16],dstr2[16];
+	if (syscoor == WCS_XY) {
+	    num2str (rstr1, *ra1, 10, 5);
+            num2str (dstr1, *dec1, 10, 5);
+	    num2str (rstr2, *ra2, 10, 5);
+            num2str (dstr2, *dec2, 10, 5);
+	    }
+	else if (degout) {
+	    deg2str (rstr1, 16, *ra1, 6);
+            deg2str (dstr1, 16, *dec1, 6);
+	    deg2str (rstr2, 16, *ra2, 6);
+            deg2str (dstr2, 16, *dec2, 6);
+	    }
+	else {
+	    ra2str (rstr1, 16, *ra1, 3);
+            dec2str (dstr1, 16, *dec1, 2);
+	    ra2str (rstr2, 16, *ra2, 3);
+            dec2str (dstr2, 16, *dec2, 2);
+	    }
+	fprintf (stderr,"SearchLim: RA: %s - %s  Dec: %s - %s\n",
+		 rstr1,rstr2,dstr1,dstr2);
+	}
+    return;
+}
+
+
+/* REFLIM-- Set limits in reference catalog coordinates given search coords */
+void
+RefLim (cra, cdec, dra, ddec, sysc, sysr, eqc, eqr, epc, epr, secmarg,
+	ramin, ramax, decmin, decmax, wrap, verbose)
+
+double	cra, cdec;	/* Center of search area  in degrees */
+double	dra, ddec;	/* Horizontal and vertical half-widths of area */
+int	sysc, sysr;	/* System of search, catalog coordinates */
+double	eqc, eqr;	/* Equinox of search, catalog coordinates in years */
+double	epc, epr;	/* Epoch of search, catalog coordinates in years */
+double	secmarg;	/* Margin in arcsec/century to catch moving stars */
+double	*ramin,*ramax;	/* Right ascension search limits in degrees (returned)*/
+double	*decmin,*decmax; /* Declination search limits in degrees (returned) */
+int	*wrap;		/* 1 if search passes through 0:00:00 RA */
+int	verbose;	/* 1 to print limits, else 0 */
+
+{
+    double ra, ra1, ra2, ra3, ra4, dec1, dec2, dec3, dec4;
+    double dec, acdec, adec, adec1, adec2, dmarg, dist, dra1;
+    int nrot;
+
+    /* Deal with all or nearly all of the sky */
+    if (ddec > 80.0 && dra > 150.0) {
+	*ramin = 0.0;
+	*ramax = 360.0;
+	*decmin = -90.0;
+	*decmax = 90.0;
+	*wrap = 0;
+	if (verbose)
+	    fprintf (stderr,"RefLim: RA: 0.0 - 360.0  Dec: -90.0 - 90.0\n");
+	return;
+	}
+
+    /* Set declination limits for search */
+    dec1 = cdec - ddec;
+    dec2 = cdec + ddec;
+
+    /* dec1 is always the smallest declination */
+    if (dec1 > dec2) {
+	dec = dec1;
+	dec1 = dec2;
+	dec2 = dec;
+	}
+    dec3 = dec2;
+    dec4 = dec1;
+
+    /* Deal with south pole */
+    if (dec1 < -90.0) {
+	dec1 = 90.0 - (dec1 + 90.0);
+	if (dec1 > dec2)
+	    dec2 = dec1;
+	dec1 = -90.0;
+	dra1 = 180.0;
+	}
+
+    /* Deal with north pole */
+    if (dec2 > 90.0) {
+	dec2 = 90.0 - (dec2 - 90.0);
+	if (dec2 < dec1)
+	    dec1 = dec2;
+	dec2 = 90.0;
+	dra1 = 180.0;
+	}
+
+    /* Adjust width in right ascension to that at max absolute declination */
+    adec1 = fabs (dec1);
+    adec2 = fabs (dec2);
+    if (adec1 > adec2)
+	adec = adec1;
+    else
+	adec = adec2;
+    acdec = fabs (cdec);
+    if (adec < 90.0 && adec > acdec)
+	dra1 = dra * (cos (degrad(acdec)) / cos (degrad(adec)));
+    else if (adec == 90.0)
+	dra1 = 180.0;
+
+    /* Set right ascension limits for search */
+    ra1 = cra - dra1;
+    ra2 = cra + dra1;
+
+    /* Keep right ascension limits between 0 and 360 degrees */
+    if (ra1 < 0.0) {
+	nrot = 1 - (int) (ra1 / 360.0);
+	ra1 = ra1 + (360.0 * (double) nrot);
+	}
+    if (ra1 > 360.0) {
+	nrot = (int) (ra1 / 360.0);
+	ra1 = ra1 - (360.0 * (double) nrot);
+	}
+    if (ra2 < 0.0) {
+	nrot = 1 - (int) (ra2 / 360.0);
+	ra2 = ra2 + (360.0 * (double) nrot);
+	}
+    if (ra2 > 360.0) {
+	nrot = (int) (ra2 / 360.0);
+	ra2 = ra2 - (360.0 * (double) nrot);
+	}
+
+    if (ra1 > ra2)
+	*wrap = 1;
+    else
+	*wrap = 0;
+
+    ra3 = ra1;
+    ra4 = ra2;
+
+    /* Convert search corners to catalog coordinate system and equinox */
+    ra = cra;
+    dec = cdec;
+    wcscon (sysc, sysr, eqc, eqr, &ra, &dec, epc);
+    wcscon (sysc, sysr, eqc, eqr, &ra1, &dec1, epc);
+    wcscon (sysc, sysr, eqc, eqr, &ra2, &dec2, epc);
+    wcscon (sysc, sysr, eqc, eqr, &ra3, &dec3, epc);
+    wcscon (sysc, sysr, eqc, eqr, &ra4, &dec4, epc);
+
+    /* Find minimum and maximum right ascensions to search */
+    *ramin = ra1;
+    if (ra3 < *ramin)
+	*ramin = ra3;
+    *ramax = ra2;
+    if (ra4 > *ramax)
+	*ramax = ra4;
+
+    /* Add margins to RA limits to get most stars which move */
+    if (secmarg > 0.0 && epc != 0.0) {
+	dmarg = (secmarg / 3600.0) * fabs (epc - epr);
+	*ramin = *ramin - (dmarg * cos (degrad (cdec)));
+	*ramax = *ramax + (dmarg * cos (degrad (cdec)));
+	}
+   else
+	dmarg = 0.0;
+
+    if (*wrap) {
+	ra = *ramax;
+	*ramax = *ramin;
+	*ramin = ra;
+	}
+
+    /* Find minimum and maximum declinatons to search */
+    *decmin = dec1;
+    if (dec2 < *decmin)
+	*decmin = dec2;
+    if (dec3 < *decmin)
+	*decmin = dec3;
+    if (dec4 < *decmin)
+	*decmin = dec4;
+    *decmax = dec1;
+    if (dec2 > *decmax)
+	*decmax = dec2;
+    if (dec3 > *decmax)
+	*decmax = dec3;
+    if (dec4 > *decmax)
+	*decmax = dec4;
+
+    /* Add margins to Dec limits to get most stars which move */
+    if (dmarg > 0.0) {
+	*decmin = *decmin - dmarg;
+	*decmax = *decmax + dmarg;
+	}
+
+    /* Check for pole */
+    dist = wcsdist (ra, dec, *ramax, *decmax);
+    if (dec + dist > 90.0) {
+	*ramin = 0.0;
+	*ramax = 359.99999;
+	*decmax = 90.0;
+	*wrap = 0;
+	}
+    else if (dec - dist < -90.0) {
+	*ramin = 0.0;
+	*ramax = 359.99999;
+	*decmin = -90.0;
+	*wrap = 0;
+	}
+	
+
+    /* Search zones which include the poles cover 360 degrees in RA */
+    else if (*decmin < -90.0) {
+	*decmin = -90.0;
+	*ramin = 0.0;
+	*ramax = 359.99999;
+	*wrap = 0;
+	}
+    else if (*decmax > 90.0) {
+	*decmax = 90.0;
+	*ramin = 0.0;
+	*ramax = 359.99999;
+	*wrap = 0;
+	}
+    if (verbose) {
+	char rstr1[16],rstr2[16],dstr1[16],dstr2[16];
+	if (degout) {
+	    deg2str (rstr1, 16, *ramin, 6);
+            deg2str (dstr1, 16, *decmin, 6);
+	    deg2str (rstr2, 16, *ramax, 6);
+            deg2str (dstr2, 16, *decmax, 6);
+	    }
+	else {
+	    ra2str (rstr1, 16, *ramin, 3);
+            dec2str (dstr1, 16, *decmin, 2);
+	    ra2str (rstr2, 16, *ramax, 3);
+            dec2str (dstr2, 16, *decmax, 2);
+	    }
+	fprintf (stderr,"RefLim: RA: %s - %s  Dec: %s - %s",
+		 rstr1,rstr2,dstr1,dstr2);
+	if (*wrap)
+	    fprintf (stderr," wrap\n");
+	else
+	    fprintf (stderr,"\n");
+	}
+    return;
+}
+
+
+/* RANGEINIT -- Initialize range structure from string */
+
+struct Range *
+RangeInit (string, ndef)
+
+char	*string;	/* String containing numbers separated by , and - */
+int	ndef;		/* Maximum allowable range value */
+
+{
+    struct Range *range;
+    int ip, irange;
+    char *slast;
+    double first, last, step;
+
+    if (!isrange (string) && !isnum (string))
+	return (NULL);
+    ip = 0;
+    range = (struct Range *)calloc (1, sizeof (struct Range));
+    range->irange = -1;
+    range->nvalues = 0;
+    range->nranges = 0;
+
+    for (irange = 0; irange < MAXRANGE; irange++) {
+
+	/* Default to entire list */
+	first = 1.0;
+	last = ndef;
+	step = 1.0;
+
+	/* Skip delimiters to start of range */
+	while (string[ip] == ' ' || string[ip] == '	' ||
+	       string[ip] == ',')
+	    ip++;
+
+	/* Get first limit
+	 * Must be a number, '-', 'x', or EOS.  If not return ERR */
+	if (string[ip] == (char)0) {	/* end of list */
+	    if (irange == 0) {
+
+		/* Null string defaults */
+		range->ranges[0] = first;
+		if (first < 1)
+		    range->ranges[1] = first;
+		else
+		    range->ranges[1] = last;
+		range->ranges[2] = step;
+		range->nvalues = range->nvalues + 1 +
+			  ((range->ranges[1]-range->ranges[0])/step);
+		range->nranges++;
+		return (range);
+		}
+	    else
+		return (range);
+	    }
+	else if (string[ip] > (char)47 && string[ip] < 58) {
+	    first = strtod (string+ip, &slast);
+	    ip = slast - string;
+	    }
+	else if (strchr ("-:x", string[ip]) == NULL) {
+	    free (range);
+	    return (NULL);
+	    }
+
+	/* Skip delimiters */
+	while (string[ip] == ' ' || string[ip] == '	' ||
+	       string[ip] == ',')
+	    ip++;
+
+	/* Get last limit
+	* Must be '-', or 'x' otherwise last = first */
+	if (string[ip] == '-' || string[ip] == ':') {
+	    ip++;
+	    while (string[ip] == ' ' || string[ip] == '	' ||
+	   	   string[ip] == ',')
+		ip++;
+	    if (string[ip] == (char)0)
+		last = first + ndef;
+	    else if (string[ip] > (char)47 && string[ip] < 58) {
+		last = strtod (string+ip, &slast);
+		ip = slast - string;
+		}
+	    else if (string[ip] != 'x')
+		last = first + ndef;
+	    }
+	else if (string[ip] != 'x')
+	    last = first;
+
+	/* Skip delimiters */
+	while (string[ip] == ' ' || string[ip] == '	' ||
+	       string[ip] == ',')
+	    ip++;
+
+	/* Get step
+	 * Must be 'x' or assume default step. */
+	if (string[ip] == 'x') {
+	    ip++;
+	    while (string[ip] == ' ' || string[ip] == '	' ||
+	   	   string[ip] == ',')
+		ip++;
+	    if (string[ip] == (char)0)
+		step = 1.0;
+	    else if (string[ip] > (char)47 && string[ip] < 58) {
+		step = strtod (string+ip, &slast);
+		ip = slast - string;
+		}
+	    else if (string[ip] != '-' && string[ip] != ':')
+		step = 1.0;
+            }
+
+	/* Output the range triple */
+	range->ranges[irange*3] = first;
+	range->ranges[irange*3 + 1] = last;
+	range->ranges[irange*3 + 2] = step;
+	range->nvalues = range->nvalues + ((last-first+(0.1*step)) / step + 1);
+	range->nranges++;
+	}
+
+    return (range);
+}
+
+
+/* ISRANGE -- Return 1 if string is a range, else 0 */
+
+int
+isrange (string)
+
+char *string;		/* String which might be a range of numbers */
+
+{
+    int i, lstr;
+
+    /* If string is NULL or empty, return 0 */
+    if (string == NULL || strlen (string) == 0)
+	return (0);
+
+    /* If range separators present, check to make sure string is range */
+    else if (strchr (string+1, '-') || strchr (string+1, ',')) {
+	lstr = strlen (string);
+	for (i = 0; i < lstr; i++) {
+	    if (strchr ("0123456789-,.x", (int)string[i]) == NULL)
+		return (0);
+	    }
+	return (1);
+	}
+    else
+	return (0);
+}
+
+
+/* RSTART -- Restart at beginning of range */
+
+void
+rstart (range)
+
+struct Range *range;	/* Range structure */
+
+{
+    range->irange = -1;
+    return;
+}
+
+
+/* RGETN -- Return number of values from range structure */
+
+int
+rgetn (range)
+
+struct Range *range;	/* Range structure */
+
+{
+    return (range->nvalues);
+}
+
+
+/*  RGETR8 -- Return next number from range structure as 8-byte f.p. number */
+
+double
+rgetr8 (range)
+
+struct Range *range;	/* Range structure */
+
+{
+    int i;
+
+    if (range == NULL)
+	return (0.0);
+    else if (range->irange < 0) {
+	range->irange = 0;
+	range->first = range->ranges[0];
+	range->last = range->ranges[1];
+	range->step = range->ranges[2];
+	range->value = range->first;
+	}
+    else {
+	range->value = range->value + range->step;
+	if (range->value > (range->last + (range->step * 0.5))) {
+	    range->irange++;
+	    if (range->irange < range->nranges) {
+		i = range->irange * 3;
+		range->first = range->ranges[i];
+		range->last = range->ranges[i+1];
+		range->step = range->ranges[i+2];
+		range->value = range->first;
+		}
+	    else
+		range->value = 0.0;
+	    }
+	}
+    return (range->value);
+}
+
+
+/*  RGETI4 -- Return next number from range structure as 4-byte integer */
+
+int
+rgeti4 (range)
+
+struct Range *range;	/* Range structure */
+
+{
+    double value;
+
+    value = rgetr8 (range);
+    return ((int) (value + 0.000000001));
+}
+
+
+/* AGETI4 -- Read int value from a file where keyword=value, anywhere */
+
+int
+ageti4 (string, keyword, ival)
+
+char	*string;	/* character string containing <keyword>= <value> */
+char	*keyword;	/* character string containing the name of the keyword
+			   the value of which is returned.  hget searches for a
+                 	   line beginning with this string.  if "[n]" or ",n" is
+			   present, the n'th token in the value is returned. */
+int	*ival;		/* Integer value, returned */
+{
+    char value[32];
+
+    if (agets (string, keyword, 31, value)) {
+	*ival = atoi (value);
+	return (1);
+	}
+    else
+	return (0);
+}
+	
+
+/* AGETR8 -- Read double value from a file where keyword=value, anywhere */
+int
+agetr8 (string, keyword, dval)
+
+char	*string;	/* character string containing <keyword>= <value> */
+char	*keyword;	/* character string containing the name of the keyword
+			   the value of which is returned.  hget searches for a
+                 	   line beginning with this string.  if "[n]" or ",n" is
+			   present, the n'th token in the value is returned. */
+double	*dval;		/* Double value, returned */
+{
+    char value[32];
+
+    if (agets (string, keyword, 31, value)) {
+	*dval = atof (value);
+	return (1);
+	}
+    else
+	return (0);
+}
+
+
+/* AGETS -- Get keyword value from ASCII string with keyword=value anywhere */
+
+int
+agets (string, keyword0, lval, value)
+
+char *string;  /* character string containing <keyword>= <value> info */
+char *keyword0;  /* character string containing the name of the keyword
+                   the value of which is returned.  hget searches for a
+                   line beginning with this string.  if "[n]" or ",n" is
+		   present, the n'th token in the value is returned. */
+int lval;       /* Size of value in characters
+		   If negative, value ends at end of line */
+char *value;      /* String (returned) */
+{
+    char keyword[81];
+    char *pval, *str, *pkey, *pv;
+    char squot[2], dquot[2], lbracket[2], rbracket[2], comma[2];
+    char *lastval, *rval, *brack1, *brack2, *lastring, *iquot, *ival;
+    int ipar, i, lkey;
+
+    squot[0] = (char) 39;
+    squot[1] = (char) 0;
+    dquot[0] = (char) 34;
+    dquot[1] = (char) 0;
+    lbracket[0] = (char) 91;
+    lbracket[1] = (char) 0;
+    comma[0] = (char) 44;
+    comma[1] = (char) 0;
+    rbracket[0] = (char) 93;
+    rbracket[1] = (char) 0;
+    lastring = string + strlen (string);
+
+    /* Find length of variable name */
+    strncpy (keyword,keyword0, sizeof(keyword)-1);
+    brack1 = strsrch (keyword,lbracket);
+    if (brack1 == NULL)
+	brack1 = strsrch (keyword,comma);
+    if (brack1 != NULL) {
+	*brack1 = '\0';
+	brack1++;
+	}
+    lkey = strlen (keyword);
+
+    /* First check for the existence of the keyword in the string */
+    pkey = strcsrch (string, keyword);
+
+    /* If keyword has not been found, return 0 */
+    if (pkey == NULL)
+	return (0);
+
+    /* If it has been found, check for = or : and preceding characters */
+    pval = NULL;
+    while (pval == NULL) {
+
+	/* Must be at start of file or after control character or space */
+	if (pkey != string && *(pkey-1) > 32) {
+	    str = pkey;
+	    pval = NULL;
+	    }
+
+	/* Must have "=" or ":" as next nonspace character */
+	else {
+	    pv = pkey + lkey;
+	    while (*pv == ' ')
+		pv++;
+	    if (*pv != '=' && *pv != ':') {
+		str = pkey;
+		pval = NULL;
+		}
+
+	    /* If found, bump pointer past keyword, operator, and spaces */
+	    else {
+		pval = pv + 1;
+		while (*pval == '=' || *pval == ' ')
+		    pval++;
+		break;
+		}
+	    }
+	str = str + lkey;
+	if (str > lastring)
+	    break;
+	pkey = strcsrch (str, keyword);
+	if (pkey == NULL)
+	    break;
+	}
+    if (pval == NULL)
+	return (0);
+
+    /* Drop leading spaces */
+    while (*pval == ' ') pval++;
+
+    /* Pad quoted material with _; drop leading and trailing quotes */
+    iquot = NULL;
+    if (*pval == squot[0]) {
+	pval++;
+	iquot = strsrch (pval, squot);
+	}
+    if (*pval == dquot[0]) {
+	pval++;
+	iquot = strsrch (pval, dquot);
+	}
+    if (iquot != NULL) {
+	*iquot = (char) 0;
+	for (ival = pval; ival < iquot; ival++) {
+	    if (*ival == ' ')
+		*ival = '_';
+	    }
+	}
+
+    /* If keyword has brackets, figure out which token to extract */
+    if (brack1 != NULL) {
+        brack2 = strsrch (brack1,rbracket);
+        if (brack2 != NULL)
+            *brack2 = '\0';
+        ipar = atoi (brack1);
+	}
+    else
+	ipar = 1;
+
+    /* Move to appropriate token */
+    for (i = 1; i < ipar; i++) {
+	while (*pval != ' ' && *pval != '/' && pval < lastring)
+	    pval++;
+
+	/* Drop leading spaces  or / */
+	while (*pval == ' ' || *pval == '/')
+	    pval++;
+	}
+
+    /* Transfer token value to returned string */
+    rval = value;
+    if (lval < 0) {
+	lastval = value - lval - 1;
+	while (*pval != '\n' && pval < lastring && rval < lastval) {
+	    if (lval > 0 && *pval == ' ')
+		break;
+	    *rval++ = *pval++;
+	    }
+	}
+    else {
+	lastval = value + lval - 1;
+	while (*pval != '\n' && *pval != '/' &&
+	    pval < lastring && rval < lastval) {
+	    if (lval > 0 && *pval == ' ')
+		break;
+	    *rval++ = *pval++;
+	    }
+	}
+    if (rval < lastval)
+	*rval = (char) 0;
+    else
+	*lastval = 0;
+
+    return (1);
+}
+
+char sptbv[468]={"O5O8B0B0B0B1B1B1B2B2B2B3B3B3B4B5B5B6B6B6B7B7B8B8B8B9B9B9B9A0A0A0A0A0A0A0A0A0A2A2A2A2A2A2A2A2A5A5A5A5A6A7A7A7A7A7A7A7A7A7A7F0F0F0F0F0F0F0F2F2F2F2F2F2F2F5F5F5F5F5F5F5F5F5F8F8F8F8F8F8G0G5G5G2G2G2G3G3G4G4G5G5G5G6G6G6G6G6K6K6K6K6K7K7K7K7K7K7K7K7K7K7K7K7K7K7K8K8K8K8K8K8K8K8K8K8K8K8K8K8K8K8K8K8K8K5K5K5K5K5K6K6K6K6K6K6K6K7K7K7K7K7K7K7K8K8K8K8K9K9K9M0M0M0M0M0M0M1M1M1M1M1M2M2M2M2M3M3M4M4M5M5M5M2M2M2M3M3M4M4M5M5M5M6M6M6M6M6M6M6M6M6M7M7M7M7M7M7M7M7M7M7M7M7M7M7M8M8M8M8M8M8M8"};
+
+void
+bv2sp (bv, b, v, isp)
+
+double	*bv;	/* B-V Magnitude */
+double	b;	/* B Magnitude used if bv is NULL */
+double	v;	/* V Magnitude used if bv is NULL */
+char	*isp;	/* Spectral type */
+{
+    double bmv;	/* B - V magnitude */
+    int im;
+
+    if (bv == NULL)
+	bmv = b - v;
+    else
+	bmv = *bv;
+
+    if (bmv < -0.32) {
+	isp[0] = '_';
+	isp[1] = '_';
+	}
+    else if (bmv > 2.00) {
+	isp[0] = '_';
+	isp[1] = '_';
+	}
+    else if (bmv < 0) {
+	im = 2 * (32 + (int)(bmv * 100.0 - 0.5));
+	isp[0] = sptbv[im];
+	isp[1] = sptbv[im+1];
+	}
+    else {
+	im = 2 * (32 + (int)(bmv * 100.0 + 0.5));
+	isp[0] = sptbv[im];
+	isp[1] = sptbv[im+1];
+	}
+    return;
+}
+
+char sptbr1[96]={"O5O8O9O9B0B0B0B0B0B1B1B1B2B2B2B2B2B3B3B3B3B3B3B5B5B5B5B6B6B6B7B7B7B7B8B8B8B8B8B9B9B9B9B9A0A0A0"};
+
+char sptbr2[904]={"A0A0A0A0A0A0A0A0A2A2A2A2A2A2A2A2A2A2A2A2A2A2A2A5A5A5A5A5A5A5A5A5A5A5A7A7A7A7A7A7A7A7A7A7A7A7A7A7A7A7F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F2F2F2F2F2F2F2F2F2F2F2F5F5F5F5F5F5F5F5F5F5F5F5F5F5F8F8F8F8F8F8F8F8F8F8F8F8F8F8G0G0G0G0G0G0G0G0G2G2G2G2G2G5G5G5G5G5G5G5G5G8G8G8G8G8G8G8G8G8G8G8G8G8G8K0K0K0K0K0K0K0K0K0K0K0K0K0K0K0K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K2K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K5K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7K7M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M0M1M1M1M1M1M1M1M1M1M1M1M1M1M1M1M2M2M2M2M2M2M2M2M2M2M2M2M2M2M2M3M3M3M3M3M3M3M3M3M3M3M4M4M4M4M4M4M4M4M4M4M4M4M4M4M5M5M5M5M5M5M5M5M5M5M5M5M5M5M5M5M5M5M5M5M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M6M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M7M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8M8"};
+
+void
+br2sp (br, b, r, isp)
+
+double	*br;	/* B-R Magnitude */
+double	b;	/* B Magnitude used if br is NULL */
+double	r;	/* R Magnitude used if br is NULL */
+char	*isp;	/* Spectral type */
+{
+    double bmr;	/* B - R magnitude */
+    int im;
+
+    if (br == NULL)
+	bmr = b - r;
+    else
+	bmr = *br;
+
+    if (b == 0.0 && r > 2.0) {
+	isp[0] = '_';
+	isp[1] = '_';
+	}
+    else if (bmr < -0.47) {
+	isp[0] = '_';
+	isp[1] = '_';
+	}
+    else if (bmr > 4.50) {
+	isp[0] = '_';
+	isp[1] = '_';
+	}
+    else if (bmr < 0) {
+	im = 2 * (47 + (int)(bmr * 100.0 - 0.5));
+	isp[0] = sptbr1[im];
+	isp[1] = sptbr1[im+1];
+	}
+    else {
+	im = 2 * ((int)(bmr * 100.0 + 0.49));
+	isp[0] = sptbr2[im];
+	isp[1] = sptbr2[im+1];
+	}
+    return;
+}
+
+
+void
+CatTabHead (refcat,sysout,nnfld,mprop,nmag,ranges,keyword,gcset,tabout,
+	    classd,printxy,gobj1,fd)
+
+int	refcat;		/* Catalog being searched */
+int	sysout;		/* Output coordinate system */
+int	nnfld;		/* Number of characters in ID column */
+int	mprop;		/* 1 if proper motion in catalog */
+int	nmag;		/* Number of magnitudes */
+char	*ranges;	/* Catalog numbers to print */
+char	*keyword;	/* Column to add to tab table output */
+int	gcset;		/* 1 if there are any values in gc[] */
+int	tabout;		/* 1 if output is tab-delimited */
+int	classd; 	/* GSC object class to accept (-1=all) */
+int	printxy;	/* 1 if X and Y included in output */
+char	**gobj1;	/* Pointer to array of object names; NULL if none */
+FILE	*fd;		/* Output file descriptor; none if NULL */
+
+{
+    int typecol;
+    char headline[160];
+
+    /* Set flag for plate, class, type, or 3rd magnitude column */
+    if (refcat == BINCAT || refcat == SAO  || refcat == PPM ||
+	refcat == ACT  || refcat == TYCHO2 || refcat == BSC)
+	typecol = 1;
+    else if ((refcat == GSC || refcat == GSCACT) && classd < -1)
+	typecol = 3;
+    else if (refcat == TMPSC)
+	typecol = 4;
+    else if (refcat == GSC || refcat == GSCACT ||
+	refcat == UJC || refcat == IRAS ||
+	refcat == USAC || refcat == USA1   || refcat == USA2 ||
+	refcat == UAC  || refcat == UA1    || refcat == UA2 ||
+	refcat == BSC  || (refcat == TABCAT&&gcset))
+	typecol = 2;
+    else
+	typecol = 0;
+
+
+    /* Print column headings */
+    if (refcat == ACT)
+	strcpy (headline, "act_id       ");
+    else if (refcat == BSC)
+	strcpy (headline, "bsc_id       ");
+    else if (refcat == GSC || refcat == GSCACT)
+	strcpy (headline, "gsc_id       ");
+    else if (refcat == USAC)
+	strcpy (headline,"usac_id       ");
+    else if (refcat == USA1)
+	strcpy (headline,"usa1_id       ");
+    else if (refcat == USA2)
+	strcpy (headline,"usa2_id       ");
+    else if (refcat == UAC)
+	strcpy (headline,"usnoa_id      ");
+    else if (refcat == UA1)
+	strcpy (headline,"usnoa1_id     ");
+    else if (refcat == UA2)
+	strcpy (headline,"usnoa2_id     ");
+    else if (refcat == UJC)
+	strcpy (headline,"usnoj_id      ");
+    else if (refcat == TMPSC)
+	strcpy (headline,"2mass_id      ");
+    else if (refcat == TMXSC)
+	strcpy (headline,"2mx_id        ");
+    else if (refcat == SAO)
+	strcpy (headline,"sao_id        ");
+    else if (refcat == PPM)
+	strcpy (headline,"ppm_id        ");
+    else if (refcat == IRAS)
+	strcpy (headline,"iras_id       ");
+    else if (refcat == TYCHO)
+	strcpy (headline,"tycho_id      ");
+    else if (refcat == TYCHO2)
+	strcpy (headline,"tycho2_id     ");
+    else if (refcat == HIP)
+	strcpy (headline,"hip_id        ");
+    else
+	strcpy (headline,"id            ");
+    headline[nnfld] = (char) 0;
+
+    if (sysout == WCS_GALACTIC)
+	strcat (headline,"	long_gal   	lat_gal  ");
+    else if (sysout == WCS_ECLIPTIC)
+	strcat (headline,"	long_ecl   	lat_ecl  ");
+    else if (sysout == WCS_B1950)
+	strcat (headline,"	ra1950      	dec1950  ");
+    else
+	strcat (headline,"	ra      	dec      ");
+    if (refcat == USAC || refcat == USA1 || refcat == USA2 ||
+	refcat == UAC  || refcat == UA1  || refcat == UA2)
+	strcat (headline,"	magb	magr	plate");
+    if (refcat == TMPSC)
+	strcat (headline,"	magj	magh	magk");
+    else if (refcat==TYCHO || refcat==TYCHO2 || refcat==HIP || refcat==ACT)
+	strcat (headline,"	magb	magv");
+    else if (refcat == GSC || refcat == GSCACT)
+	strcat (headline,"	mag	class	band	N");
+    else if (refcat == UJC)
+	strcat (headline,"	mag	plate");
+    else
+	strcat (headline,"	mag");
+    if (typecol == 1)
+	strcat (headline,"	type");
+    if (mprop)
+	strcat (headline,"	Ura    	Udec  ");
+    if (ranges == NULL)
+	strcat (headline,"	arcsec");
+    if (refcat == TABCAT && keyword != NULL) {
+	strcat (headline,"	");
+	strcat (headline, keyword);
+	}
+    if (gobj1 != NULL)
+	strcat (headline,"	object");
+    if (printxy)
+	strcat (headline, "	x      	y      ");
+    if (tabout) {
+	printf ("%s\n", headline);
+	if (fd != NULL)
+	    fprintf (fd, "%s\n", headline);
+	}
+
+    strcpy (headline, "---------------------");
+    headline[nnfld] = (char) 0;
+    strcat (headline,"	------------	------------");
+    if (nmag == 2)
+	strcat (headline,"	-----	-----");
+    else
+	strcat (headline,"	-----");
+    if (refcat == GSC || refcat == GSCACT)
+	strcat (headline,"	-----	----	-");
+    else if (typecol == 1)
+	strcat (headline,"	----");
+    else if (typecol == 2)
+	strcat (headline,"	-----");
+    else if (typecol == 4)
+	strcat (headline,"	-----");
+    if (mprop)
+	strcat (headline,"	-------	------");
+    if (ranges == NULL)
+	strcat (headline, "	------");
+    if (refcat == TABCAT && keyword != NULL)
+	strcat (headline,"	------");
+    if (printxy)
+	strcat (headline, "	-------	-------");
+    if (tabout) {
+	printf ("%s\n", headline);
+	if (fd != NULL)
+	    fprintf (fd, "%s\n", headline);
+	}
+}
+
+
+/* TMCID -- Return 1 if string is 2MASS ID, else 0 */
+
+int
+tmcid (string, ra, dec)
+
+char	*string;	/* Character string to check */
+double	*ra;		/* Right ascension (returned) */
+double	*dec;		/* Declination (returned) */
+{
+    char *sdec;
+    char csign;
+    int idec, idm, ids, ira, irm, irs;
+
+    /* Check first character */
+    if (string[0] != 'J' && string[0] != 'j')
+	return (0);
+
+    /* Find declination sign */
+    sdec = strsrch (string, "-");
+    if (sdec == NULL)
+	sdec = strsrch (string,"+");
+    if (sdec == NULL)
+	return (0);
+
+    /* Parse right ascension */
+    csign = *sdec;
+    *sdec = (char) 0;
+    ira = atoi (string+1);
+    irs = ira % 10000;
+    ira = ira / 10000;
+    irm = ira % 100;
+    ira = ira / 100;
+    *ra = (double) ira + ((double) irm) / 60.0 + ((double) irs) / 360000.0;
+    *ra = *ra * 15.0;
+
+    /* Parse declination */
+    idec = atoi (sdec+1);
+    ids = idec % 1000;
+    idec = idec / 1000;
+    idm = idec % 100;
+    idec = idec / 100;
+    *dec = (double) idec + ((double) idm) / 60.0 + ((double) ids) / 36000.0;
+    return (1);
+}
+
+int
+vothead (refcat, refcatname, mprop, typecol, ns, cra, cdec, drad)
+
+int	refcat;		/* Catalog code */
+char	*refcatname;	/* Name of catalog */
+int	mprop;		/* Proper motion flag */
+int	typecol;	/* Flag for spectral type */
+int	ns;		/* Number of sources found in catalog */
+double	cra;		/* Search center right ascension */
+double	cdec;		/* Search center declination */
+double	drad;		/* Radius to search in degrees */
+
+{
+    char *catalog = CatName (refcat, refcatname);
+    int nf = 0;
+
+    printf ("<!DOCTYPE VOTABLE SYSTEM \"http://us-vo.org/xml/VOTable.dtd\">\n");
+    printf ("<VOTABLE version=\"v1.1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
+    printf ("xsi:noNamespaceSchemaLocation=\"http://www.ivoa.net/xml/VOTable/VOTable/v1.1\">\n");
+    printf (" <DESCRIPTION>SAO/TDC %s Cone Search Response</DESCRIPTION>\n", catalog);
+    printf ("  <DEFINITIONS>\n");
+    printf ("   <COOSYS  ID=\"J2000\" equinox=\"2000.0\" epoch=\"2000.0\" system=\"ICRS\" >\n");
+    printf ("  </COOSYS>\n");
+    printf ("  </DEFINITIONS>\n");
+    printf ("  <RESOURCE>\n");
+    printf ("   <TABLE name=\"results\">\n");
+    printf ("    <DESCRIPTION>\n");
+    printf ("     %d objects within %.6f degrees of ra=%010.6f dec=%09.6f\n",
+	    ns, drad, cra, cdec);
+    printf ("    </DESCRIPTION>\n");
+    printf ("<FIELD ucd=\"ID_MAIN\" datatype=\"char\" name=\"Catalog Name\">\n");
+    if (refcat == USAC || refcat == USA1 || refcat == USA2 ||
+	refcat == UAC  || refcat == UA1  || refcat == UA2 || refcat == UB1)
+	printf ("  <DESCRIPTION>USNO Object Identifier</DESCRIPTION>\n");
+    else if (refcat == TYCHO2)
+	printf ("  <DESCRIPTION>Tycho-2 Object Identifier</DESCRIPTION>\n");
+    else if (refcat == GSC2)
+	printf ("  <DESCRIPTION>GSC II Object Identifier</DESCRIPTION>\n");
+    else if (refcat == TMPSC)
+	printf ("  <DESCRIPTION>2MASS Point Source Identifier</DESCRIPTION>\n");
+    else if (refcat == GSC || refcat == GSCACT)
+	printf ("  <DESCRIPTION>GSC Object Identifier</DESCRIPTION>\n");
+    else if (refcat == SAO)
+	printf ("  <DESCRIPTION>SAO Catalog Number</DESCRIPTION>\n");
+    else if (refcat == PPM)
+	printf ("  <DESCRIPTION>PPM Catalog Number</DESCRIPTION>\n");
+    else
+	printf ("  <DESCRIPTION>Object Identifier</DESCRIPTION>\n");
+    printf ("</FIELD>\n");
+
+    printf ("<FIELD ucd=\"POS_EQ_RA_MAIN\" datatype=\"float\" name=\"RA\" unit=\"degrees\" ref=\"J2000\">\n");
+    printf ("  <DESCRIPTION>Right Ascension of Object (J2000)</DESCRIPTION>\n");
+    printf ("</FIELD>\n");
+
+    printf ("<FIELD ucd=\"POS_EQ_DEC_MAIN\" datatype=\"float\" name=\"DEC\" unit=\"degrees\" ref=\"J2000\">\n");
+    printf ("   <DESCRIPTION>Declination of Object (J2000)</DESCRIPTION>\n");
+    printf ("</FIELD>\n");
+
+    if (refcat == USAC || refcat == USA1 || refcat == USA2 ||
+	refcat == UAC  || refcat == UA1  || refcat == UA2) {
+	printf ("<FIELD ucd=\"PHOT_PHG_B\" datatype=\"float\" name=\"B Magnitude\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION>Photographic B Magnitude of Object</DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+	printf ("<FIELD ucd=\"PHOT_PHG_R\" datatype=\"float\" name=\"R Magnitude\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION>Photographic R Magnitude of Object</DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+	printf ("<FIELD ucd=\"INST_PLATE_NUMBER\" datatype=\"int\" name=\"PlateID\">\n");
+	printf ("  <DESCRIPTION>USNO Plate ID of star</DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+ nf = 7;
+ }
+    else if (refcat == TYCHO2) {
+	printf ("<FIELD name=\"BTmag\" ucd=\"PHOT_TYCHO_B\" datatype=\"float\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION> Tycho-2 BT magnitude </DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+	printf ("<FIELD name=\"VTmag\" ucd=\"PHOT_TYCHO_V\" datatype=\"float\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION> Tycho-2 VT magnitude </DESCRIPTION>\n");
+ nf = 8;
+	}
+    else if (refcat == GSC || refcat == GSCACT) {
+	printf ("<FIELD name=\"Vmag\" ucd=\"PHOT_GSC_V\" datatype=\"float\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION> GSC V magnitude </DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+ nf = 8;
+	}
+    else if (refcat == GSC2) {
+	}
+    else if (refcat == TMPSC) {
+	printf ("<FIELD name=\"Jmag\" ucd=\"PHOT_MAG_J\" datatype=\"float\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION> Johnson J magnitude </DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+	printf ("<FIELD name=\"Hmag\" ucd=\"PHOT_MAG_H\" datatype=\"float\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION> Johnson H magnitude </DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+	printf ("<FIELD name=\"Kmag\" ucd=\"PHOT_MAG_K\" datatype=\"float\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION> Johnson K magnitude </DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+ nf = 7;
+	}
+    else if (refcat == SAO) {
+	printf ("<FIELD name=\"Vmag\" ucd=\"PHOT_MAG_V\" datatype=\"float\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION> SAO Catalog V magnitude (7)</DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+ nf = 8;
+	} 
+    else if (refcat == PPM) {
+	printf ("<FIELD name=\"Vmag\" ucd=\"PHOT_MAG_V\" datatype=\"float\" unit=\"mag\">\n");
+	printf ("  <DESCRIPTION> PPM Catalog V magnitude (7)</DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+ nf = 8;
+	} 
+    if (typecol == 1) {
+	printf ("<FIELD ucd=\"SPECT_TYPE_GENERAL\" name=\"Spectral Type\">\n");
+	printf ("  <DESCRIPTION>Spectral Type from catalog</DESCRIPTION>\n");
+	printf ("</FIELD>\n");
+	}
+    printf ("<FIELD ucd=\"POS_ANG_DIST_GENERAL\" datatype=\"float\" name=\"Offset\" unit=\"degrees\">\n");
+    printf ("  <DESCRIPTION>Radial distance from requested position</DESCRIPTION>\n");
+    printf ("</FIELD>\n");
+    printf ("<DATA> <TABLEDATA>\n");
+
+    return (nf);
+}
+
+
+void
+vottail ()
+{
+    printf ("        </TABLEDATA> </DATA>\n");
+    printf ("      </TABLE>\n");
+    printf ("    </RESOURCE>\n");
+    printf ("</VOTABLE>\n");
+    return;
+}
+
+
+/*    Polynomial least squares fitting program, almost identical to the
+ *    one in Bevington, "Data Reduction and Error Analysis for the
+ *    Physical Sciences," page 141.  The argument list was changed and
+ *    the weighting removed.
+ *      y = a(1) + a(2)*(x-x0) + a(3)*(x-x0)**2 + a(3)*(x-x0)**3 + . . .
+ */
+
+static double determ();
+
+void
+polfit (x, y, x0, npts, nterms, a, stdev)
+
+double	*x;		/* Array of independent variable points */
+double	*y;		/* Array of dependent variable points */
+double	x0;		/* Offset to independent variable */
+int	npts;		/* Number of data points to fit */
+int	nterms;		/* Number of parameters to fit */
+double	*a;		/* Vector containing current fit values */
+double	*stdev; 	/* Standard deviation of fit (returned) */
+{
+    double sigma2sum;
+    double xterm,yterm,xi,yi;
+    double *sumx, *sumy;
+    double *array;
+    int i,j,k,l,n,nmax;
+    double delta;
+
+    /* accumulate weighted sums */
+    nmax = 2 * nterms - 1;
+    sumx = (double *) calloc (nmax, sizeof(double));
+    sumy = (double *) calloc (nterms, sizeof(double));
+    for (n = 0; n < nmax; n++)
+	sumx[n] = 0.0;
+    for (j = 0; j < nterms; j++)
+	sumy[j] = 0.0;
+    for (i = 0; i < npts; i++) {
+	xi = x[i] - x0;
+	yi = y[i];
+	xterm = 1.0;
+	for (n = 0; n < nmax; n++) {
+	    sumx[n] = sumx[n] + xterm;
+	    xterm = xterm * xi;
+	    }
+	yterm = yi;
+	for (n = 0; n < nterms; n++) {
+	    sumy[n] = sumy[n] + yterm;
+	    yterm = yterm * xi;
+	    }
+	}
+
+    /* Construct matrices and calculate coeffients */
+    array = (double *) calloc (nterms*nterms, sizeof(double));
+    for (j = 0; j < nterms; j++) {
+	for (k = 0; k < nterms; k++) {
+	    n = j + k;
+	    array[j+k*nterms] = sumx[n];
+	    }
+	}
+    delta = determ (array, nterms);
+    if (delta == 0.0) {
+	*stdev = 0.;
+	for (j = 0; j < nterms; j++)
+	    a[j] = 0. ;
+	free (array);
+	free (sumx);
+	free (sumy);
+	return;
+	}
+
+    for (l = 0; l < nterms; l++) {
+	for (j = 0; j < nterms; j++) {
+	    for (k = 0; k < nterms; k++) {
+		n = j + k;
+		array[j+k*nterms] = sumx[n];
+		}
+	    array[j+l*nterms] = sumy[j];
+	    }
+	a[l] = determ (array, nterms) / delta;
+	}
+
+    /* Calculate sigma */
+    sigma2sum = 0.0;
+    for (i = 0; i < npts; i++) {
+	yi = polcomp (x[i], x0, nterms, a);
+	sigma2sum = sigma2sum + ((y[i] - yi) * (y[i] - yi));
+	}
+    *stdev = sqrt (sigma2sum / (double) (npts - 1));
+
+    free (array);
+    free (sumx);
+    free (sumy);
+    return;
+}
+
+
+/*--- Calculate the determinant of a square matrix
+ *    This subprogram destroys the input matrix array
+ *    From Bevington, page 294.
+ */
+
+static double
+determ (array, norder)
+
+double	*array;		/* Input matrix array */
+int	norder;		/* Order of determinant (degree of matrix) */
+
+{
+    double save, det;
+    int i,j,k,k1, zero;
+
+    det = 1.0;
+    for (k = 0; k < norder; k++) {
+
+	/* Interchange columns if diagonal element is zero */
+	if (array[k+k*norder] == 0) {
+	    zero = 1;
+	    for (j = k; j < norder; j++) {
+		if (array[k+j*norder] != 0.0)
+		    zero = 0;
+		}
+	    if (zero)
+		return (0.0);
+
+	    for (i = k; i < norder; i++) {
+		save = array[i+j*norder]; 
+		array[i+j*norder] = array[i+k*norder];
+		array[i+k*norder] = save ;
+		}
+	    det = -det;
+	    }
+
+	/* Subtract row k from lower rows to get diagonal matrix */
+	det = det * array[k+k*norder];
+	if (k < norder - 1) {
+	    k1 = k + 1;
+	    for (i = k1; i < norder; i++) {
+		for (j = k1; j < norder; j++) {
+		    array[i+j*norder] = array[i+j*norder] -
+				      (array[i+k*norder] * array[k+j*norder] /
+				      array[k+k*norder]);
+		    }
+		}
+	    }
+	}
+	return (det);
+}
+
+/* POLCOMP -- Polynomial evaluation
+ *	Y = A(1) + A(2)*X + A(3)*X**2 + A(3)*X**3 + . . . */
+
+double
+polcomp (xi, x0, norder, a)
+
+double	xi;	/* Independent variable */
+double	x0;	/* Offset to independent variable */
+int	norder;	/* Number of coefficients */
+double	*a;	/* Vector containing coeffiecients */
+{
+    double xterm, x, y;
+    int iterm;
+
+    /* Accumulate polynomial value */
+    x = xi - x0;
+    y = 0.0;
+    xterm = 1.0;
+    for (iterm = 0; iterm < norder; iterm++) {
+	y = y + a[iterm] * xterm;
+	xterm = xterm + x;
+	}
+    return (y);
+}
+
+/* MOVEB -- Copy nbytes bytes from source+offs to dest+offd (any data type) */
+
+void
+movebuff (source, dest, nbytes, offs, offd)
+
+char *source;	/* Pointer to source */
+char *dest;	/* Pointer to destination */
+int nbytes;	/* Number of bytes to move */
+int offs;	/* Offset in bytes in source from which to start copying */
+int offd;	/* Offset in bytes in destination to which to start copying */
+{
+char *from, *last, *to;
+        from = source + offs;
+        to = dest + offd;
+        last = from + nbytes;
+        while (from < last) *(to++) = *(from++);
+        return;
+}
+
+/* Mar  2 1998	Make number and second magnitude optional
+ * Oct 21 1998	Add RefCat() to set reference catalog code
+ * Oct 26 1998	Include object names in star catalog entry structure
+ * Oct 29 1998	Return coordinate system and title from RefCat
+ * Nov 20 1998	Add USNO A-2.0 catalog and return different code
+ * Dec  9 1998	Add Hipparcos and Tycho catalogs
+ *
+ * Jan 26 1999	Add subroutines to deal with ranges of numbers
+ * Feb  8 1999	Fix bug initializing ACT catalog
+ * Feb 11 1999	Change starcat.insys to starcat.coorsys
+ * May 19 1999	Separate catalog subroutines into separate file
+ * May 19 1999	Add CatNum() to return properly formatted catalog number
+ * May 20 1999	Add date/time conversion subroutines translated from Fortran
+ * May 28 1999	Fix bug in CatNum() which omitted GSC
+ * Jun  3 1999	Add return to CatNum()
+ * Jun  3 1999	Add CatNumLen()
+ * Jun 16 1999	Add SearchLim(), used by all catalog search subroutines
+ * Jun 30 1999	Add isrange() to check to see whether a string is a range
+ * Jul  1 1999	Move date and time utilities to dateutil.c
+ * Jul 15 1999	Add getfilebuff()
+ * Jul 23 1999	Add Bright Star Catalog
+ * Aug 16 1999	Add RefLim() to set catalog search limits
+ * Sep 21 1999	In isrange(), check for x
+ * Oct  5 1999	Add setoken(), nextoken(), and getoken()
+ * Oct 15 1999	Fix format eror in error message
+ * Oct 20 1999	Use strchr() in range decoding
+ * Oct 21 1999	Fix declarations after lint
+ * Oct 21 1999	Fix arguments to catopen() and catclose() after lint
+ * Nov  3 1999	Fix bug which lost last character on a line in getoken
+ * Dec  9 1999	Add next_token(); set pointer to next token in first_token
+ *
+ * Jan 11 2000	Use nndec for Starbase files, too
+ * Feb 10 2000	Read coordinate system, epoch, and equinox from Starbase files
+ * Mar  1 2000	Add isfile() to tell whether string is name of readable file
+ * Mar  1 2000	Add agets() to return value from keyword = value in string
+ * Mar  1 2000	Add isfile() to tell if a string is the name of a readable file
+ * Mar  1 2000	Add agets() to read a parameter from a comment line of a file
+ * Mar  8 2000	Add ProgCat() to return catalog flag from program name
+ * Mar 13 2000	Add PropCat() to return whether catalog has proper motions
+ * Mar 27 2000	Clean up code after lint
+ * May 22 2000	Add bv2sp() to approximate main sequence spectral type from B-V
+ * May 25 2000	Add Tycho 2 catalog
+ * May 26 2000	Add field size argument to CatNum() and CatNumLen()
+ * Jun  2 2000	Set proper motion for all catalog types in RefCat()
+ * Jun 26 2000	Add XY image coordinate system
+ * Jul 26 2000	Include math.h to get strtod() on SunOS machines
+ * Aug  2 2000	Allow up to 14 digits in catalog IDs
+ * Sep  1 2000	Add option in CatNum to print leading zeroes if nnfld > 0
+ * Sep 22 2000	Add br2sp() to approximate main sequence spectral type from B-R
+ * Oct 24 2000	Add USNO option to RefCat()
+ * Nov 21 2000	Clean up logic in RefCat()
+ * Nov 28 2000	Try PPMra and SAOra in RefCat() as well as PPM and SAO
+ * Dec 13 2000	Add StrNdec() to get number of decimal places in star numbers
+ *
+ * Jan 17 2001	Add vertical bar (|) as column separator
+ * Feb 28 2001	Separate .usno stars from usa stars
+ * Mar  1 2001	Add CatName()
+ * Mar 19 2001	Fix setting of ra-sorted PPM catalog in RefCat()
+ * Mar 27 2001	Add option to omit leading spaces in CatNum()
+ * May  8 2001	Fix bug in setokens() which failed to deal with quoted tokens
+ * May 18 2001	Fix bug in setokens() which returned on ntok < maxtok
+ * May 22 2001	Add GSC-ACT catalog
+ * May 24 2001	Add 2MASS Point Source Catalog
+ * Jun  7 2001	Return proper motion flag and number of magnitudes from RefCat()
+ * Jun 13 2001	Fix rounding problem in rgetr8()
+ * Jun 13 2001	Use strncasecmp() instead of two calls to strncmp() in RefCat()
+ * Jun 15 2001	Add CatName() and CatID()
+ * Jun 18 2001	Add maximum length of returned string to getoken(), nextoken()
+ * Jun 18 2001	Pad returned string in getoken(), nextoken()
+ * Jun 19 2001	Treat "bar" like "tab" as special single character terminator
+ * Jun 19 2001	Allow tab table options for named catalogs in RefCat()
+ * Jun 19 2001	Change number format to integer for Hipparcos catalog
+ * Jun 19 2001	Add refcatname as argument to CatName()
+ * Jun 20 2001	Add GSC II
+ * Jun 25 2001	Fix GSC II number padding
+ * Aug 20 2001	Add NumNdec() and guess number of decimal places if needed
+ * Sep 20 2001	Add CatMagName()
+ * Sep 25 2001	Move isfile() to fileutil.c
+ *
+ * Feb 26 2002	Fix agets() to work with keywords at start of line
+ * Feb 26 2002	Add option in agets() to return value to end of line or /
+ * Mar 25 2002	Fix bug in agets() to find second occurence of string 
+ * Apr 10 2002	Add CatMagNum() to translate single letters to mag sequence number
+ * May 13 2002	In agets(), allow arbitrary number of spaces around : or =
+ * Jun 10 2002	In isrange(), return 0 if string is null or empty
+ * Aug  1 2002	In agets(), read through / if reading to end of line
+ * Sep 18 2002	Add vothead() and vottail() for VOTable output from scat
+ * Oct 26 2002	Fix bugs in vothead()
+ *
+ * Jan 23 2003	Add USNO-B1.0 Catalog
+ * Jan 27 2003	Adjust dra in RefLimit to max width in RA seconds in region
+ * Mar 10 2003	Clean up RefLim() to better represent region to be searched
+ * Mar 24 2003	Add CatCode() to separate catalog type from catalog parameters
+ * Apr 14 2003	Add setrevmsg() and getrevmsg()
+ * Apr 24 2003	Add UCAC1 Catalog
+ * Apr 24 2003	Return 5 magnitudes for GSC II, including epoch
+ * Apr 24 2003	Fix bug dealing with HST GSC
+ * May 21 2003	Add TMIDR2=2MASS IDR2, and new 2MASS=TMPSC
+ * May 28 2003	Fix bug checking for TMIDR2=2MASS IDR2; 11 digits for TMPSC
+ * May 30 2003	Add UCAC2 catalog
+ * Sep 19 2003	Fix bug which shrank search width in RefLim()
+ * Sep 26 2003	In RefLim() do not use cos(90)
+ * Sep 29 2003	Add proper motion margins and wrap arguments to RefLim()
+ * Oct  1 2003	Add code in RefLim() for all-sky images
+ * Oct  6 2003	Add code in RefLim() to cover near-polar searches
+ * Dec  4 2003	Implement GSC 2.3 and USNO-YB6
+ * Dec 15 2003	Set refcat to 0 if no catalog name and refcatname to NULL
+ *
+ * Jan  5 2004	Add SDSS catalog
+ * Jan 12 2004	Add 2MASS Extended Source Catalog
+ * Jan 14 2004	Add CatSource()
+ * Jan 22 2004	Add global flag degout to print limits in degrees
+ *
+ * May 12 2005	Add tmcid() to decode 2MASS ID strings
+ * May 18 2005	Change Tycho-2 magnitudes to include B and V errors
+ * Jul 27 2005	Add DateString() to convert epoch to desired format
+ * Aug  2 2005	Fix setoken() to deal with whitespace before end of line
+ * Aug  2 2005	Use static maxtokens set to header MAXTOKENS
+ * Aug  5 2005	Add code to support magnitude errors in Tycho2 and 2MASS PSC
+ * Aug 11 2005	Add setdateform() so date can be formatted anywhere
+ * Aug 11 2005	Add full FITS ISO date as EP_ISO
+ * Aug 16 2005	Make all string matches case-independent
+ *
+ * Mar 15 2006	Clean up VOTable code
+ * Mar 17 2006	Return number of fields from vothead()
+ * Apr  7 2006	Keep quoted character strings together as a single token
+ * Jun  6 2006	Add SKY2000 catalog for wide fields
+ * Jun 20 2006	In CatSource() increase catalog descriptor from 32 to 64 chars
+ *
+ * Jan 10 2007	Add polynomial fitting subroutines from polfit.c
+ * Jan 11 2007	Move token access subroutines to fileutil.c
+ * Mar 13 2007	Set title accordingly for gsc22 and gsc23 and gsc2 options
+ * Jul  8 2007	Set up 8 magnitudes for GSC 2.3 from GALEX
+ * Jul 13 2007	Add SkyBot solar system object search
+ * Nov 28 2006	Add moveb() from binread.c
+ *
+ * Aug 19 2009	If pole is included, set RA range to 360 degrees in RefLim()
+ * Sep 25 2009	Change name of moveb() to movebuff()
+ * Sep 28 2009	For 2MASS Extended Source catalog, use 2mx_id, not 2mass_id
+ * Sep 30 2009	Add UCAC3 catalog
+ * Oct 26 2009	Do not wrap in RefLim() if dra=360
+ * Nov  6 2009	Add UCAC3 catalog to ProgCat()
+ */
diff --git a/Code/src/libwcs/cel.c b/Code/src/libwcs/cel.c
new file mode 100644
index 0000000000000000000000000000000000000000..91092843f2960c4f5641f16b7a40bfba55de1309
--- /dev/null
+++ b/Code/src/libwcs/cel.c
@@ -0,0 +1,474 @@
+/*=============================================================================
+*
+*   WCSLIB - an implementation of the FITS WCS proposal.
+*   Copyright (C) 1995-2002, Mark Calabretta
+*
+*   This library is free software; you can redistribute it and/or
+*   modify it under the terms of the GNU Lesser General Public
+*   License as published by the Free Software Foundation; either
+*   version 2 of the License, or (at your option) any later version.
+*
+*   This library is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+*   Lesser General Public License for more details.
+*   
+*   You should have received a copy of the GNU Lesser General Public
+*   License along with this library; if not, write to the Free Software
+*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*   Correspondence concerning WCSLIB may be directed to:
+*      Internet email: mcalabre@atnf.csiro.au
+*      Postal address: Dr. Mark Calabretta,
+*                      Australia Telescope National Facility,
+*                      P.O. Box 76,
+*                      Epping, NSW, 2121,
+*                      AUSTRALIA
+*
+*=============================================================================
+*
+*   C routines which implement the FITS World Coordinate System (WCS)
+*   convention.
+*
+*   Summary of routines
+*   -------------------
+*   These routines are provided as drivers for the lower level spherical
+*   coordinate transformation and projection routines.  There are separate
+*   driver routines for the forward, celfwd(), and reverse, celrev(),
+*   transformations.
+*
+*   An initialization routine, celset(), computes intermediate values from
+*   the transformation parameters but need not be called explicitly - see the
+*   explanation of cel.flag below.
+*
+*
+*   Initialization routine; celset()
+*   --------------------------------
+*   Initializes members of a celprm data structure which hold intermediate
+*   values.  Note that this routine need not be called directly; it will be
+*   invoked by celfwd() and celrev() if the "flag" structure member is
+*   anything other than a predefined magic value.
+*
+*   Given:
+*      pcode[4] const char
+*                        WCS projection code (see below).
+*
+*   Given and returned:
+*      cel      celprm*  Spherical coordinate transformation parameters
+*                        (see below).
+*      prj      prjprm*  Projection parameters (usage is described in the
+*                        prologue to "proj.c").
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Invalid coordinate transformation parameters.
+*                           2: Ill-conditioned coordinate transformation
+*                              parameters.
+*
+*   Forward transformation; celfwd()
+*   --------------------------------
+*   Compute (x,y) coordinates in the plane of projection from celestial
+*   coordinates (lng,lat).
+*
+*   Given:
+*      pcode[4] const char
+*                        WCS projection code (see below).
+*      lng,lat  const double
+*                        Celestial longitude and latitude of the projected
+*                        point, in degrees.
+*
+*   Given and returned:
+*      cel      celprm*  Spherical coordinate transformation parameters
+*                        (see below).
+*
+*   Returned:
+*      phi,     double*  Longitude and latitude in the native coordinate
+*      theta             system of the projection, in degrees.
+*
+*   Given and returned:
+*      prj      prjprm*  Projection parameters (usage is described in the
+*                        prologue to "proj.c").
+*
+*   Returned:
+*      x,y      double*  Projected coordinates, "degrees".
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Invalid coordinate transformation parameters.
+*                           2: Invalid projection parameters.
+*                           3: Invalid value of (lng,lat).
+*
+*   Reverse transformation; celrev()
+*   --------------------------------
+*   Compute the celestial coordinates (lng,lat) of the point with projected
+*   coordinates (x,y).
+*
+*   Given:
+*      pcode[4] const char
+*                        WCS projection code (see below).
+*      x,y      const double
+*                        Projected coordinates, "degrees".
+*
+*   Given and returned:
+*      prj      prjprm*  Projection parameters (usage is described in the
+*                        prologue to "proj.c").
+*
+*   Returned:
+*      phi,     double*  Longitude and latitude in the native coordinate
+*      theta             system of the projection, in degrees.
+*
+*   Given and returned:
+*      cel      celprm*  Spherical coordinate transformation parameters
+*                        (see below).
+*
+*   Returned:
+*      lng,lat  double*  Celestial longitude and latitude of the projected
+*                        point, in degrees.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Invalid coordinate transformation parameters.
+*                           2: Invalid projection parameters.
+*                           3: Invalid value of (x,y).
+*
+*   Coordinate transformation parameters
+*   ------------------------------------
+*   The celprm struct consists of the following:
+*
+*      int flag
+*         The celprm struct contains pointers to the forward and reverse
+*         projection routines as well as intermediaries computed from the
+*         reference coordinates (see below).  Whenever the projection code
+*         (pcode) or any of ref[4] are set or changed then this flag must be
+*         set to zero to signal the initialization routine, celset(), to
+*         redetermine the function pointers and recompute intermediaries.
+*         Once this has been done pcode itself is ignored.
+*
+*      double ref[4]
+*         The first pair of values should be set to the celestial longitude
+*         and latitude (usually right ascension and declination) of the
+*         reference point of the projection.  These are given by the CRVALn
+*         keywords in FITS.
+*
+*         The second pair of values are the native longitude of the celestial
+*         pole and the celestial latitude of the native pole and correspond to
+*         FITS keywords LONPOLE and LATPOLE.
+*
+*         LONPOLE defaults to 0 degrees if the celestial latitude of the
+*         reference point of the projection is greater than the native
+*         latitude, otherwise 180 degrees.  (This is the condition for the
+*         celestial latitude to increase in the same direction as the native
+*         latitude at the reference point.)  ref[2] may be set to 999.0 to
+*         indicate that the correct default should be substituted.
+*
+*         In some circumstances the celestial latitude of the native pole may
+*         be determined by the first three values only to within a sign and
+*         LATPOLE is used to choose between the two solutions.  LATPOLE is
+*         set in ref[3] and the solution closest to this value is used to
+*         reset ref[3].  It is therefore legitimate, for example, to set
+*         ref[3] to 999.0 to choose the more northerly solution - the default
+*         if the LATPOLE card is omitted from the FITS header.  For the
+*         special case where the reference point of the projection is at
+*         native latitude zero, its celestial latitude is zero, and
+*         LONPOLE = +/- 90 then the celestial latitude of the pole is not
+*         determined by the first three reference values and LATPOLE
+*         specifies it completely.
+*
+*   The remaining members of the celprm struct are maintained by the
+*   initialization routines and should not be modified.  This is done for the
+*   sake of efficiency and to allow an arbitrary number of contexts to be
+*   maintained simultaneously.
+*
+*      double euler[5]
+*         Euler angles and associated intermediaries derived from the
+*         coordinate reference values.
+*
+*
+*   WCS projection codes
+*   --------------------
+*   Zenithals/azimuthals:
+*      AZP: zenithal/azimuthal perspective
+*      TAN: gnomonic
+*      STG: stereographic
+*      SIN: synthesis (generalized orthographic)
+*      ARC: zenithal/azimuthal equidistant
+*      ZPN: zenithal/azimuthal polynomial
+*      ZEA: zenithal/azimuthal equal area
+*      AIR: Airy
+*
+*   Cylindricals:
+*      CYP: cylindrical perspective
+*      CEA: cylindrical equal area
+*      CAR: Cartesian
+*      MER: Mercator
+*
+*   Pseudo-cylindricals:
+*      SFL: Sanson-Flamsteed
+*      PAR: parabolic
+*      MOL: Mollweide
+*
+*   Conventional:
+*      AIT: Hammer-Aitoff
+*
+*   Conics:
+*      COP: conic perspective
+*      COD: conic equidistant
+*      COE: conic equal area
+*      COO: conic orthomorphic
+*
+*   Polyconics:
+*      BON: Bonne
+*      PCO: polyconic
+*
+*   Quad-cubes:
+*      TSC: tangential spherical cube
+*      CSC: COBE quadrilateralized spherical cube
+*      QSC: quadrilateralized spherical cube
+*
+*   Author: Mark Calabretta, Australia Telescope National Facility
+*   $Id: cel.c,v 2.14 2002/04/03 01:25:29 mcalabre Exp $
+*===========================================================================*/
+
+#include <math.h>
+#include <string.h>
+#include "wcslib.h"
+
+/* Map error number to error message for each function. */
+const char *celset_errmsg[] = {
+   0,
+   "Invalid coordinate transformation parameters",
+   "Ill-conditioned coordinate transformation parameters"};
+
+const char *celfwd_errmsg[] = {
+   0,
+   "Invalid coordinate transformation parameters",
+   "Invalid projection parameters",
+   "Invalid value of (lng,lat)"};
+
+const char *celrev_errmsg[] = {
+   0,
+   "Invalid coordinate transformation parameters",
+   "Invalid projection parameters",
+   "Invalid value of (x,y)"};
+ 
+
+int
+celset(pcode, cel, prj)
+
+const char pcode[4];
+struct celprm *cel;
+struct prjprm *prj;
+
+{
+   int dophip;
+   const double tol = 1.0e-10;
+   double clat0, cphip, cthe0, slat0, sphip, sthe0;
+   double latp, latp1, latp2;
+   double u, v, x, y, z;
+
+   /* Initialize the projection driver routines. */
+   if (prjset(pcode, prj)) {
+      return 1;
+   }
+
+   /* Set default for native longitude of the celestial pole? */
+   dophip = (cel->ref[2] == 999.0);
+
+   /* Compute celestial coordinates of the native pole. */
+   if (prj->theta0 == 90.0) {
+      /* Reference point is at the native pole. */
+
+      if (dophip) {
+         /* Set default for longitude of the celestial pole. */
+         cel->ref[2] = 180.0;
+      }
+
+      latp = cel->ref[1];
+      cel->ref[3] = latp;
+
+      cel->euler[0] = cel->ref[0];
+      cel->euler[1] = 90.0 - latp;
+   } else {
+      /* Reference point away from the native pole. */
+
+      /* Set default for longitude of the celestial pole. */
+      if (dophip) {
+         cel->ref[2] = (cel->ref[1] < prj->theta0) ? 180.0 : 0.0;
+      }
+
+      clat0 = cosdeg (cel->ref[1]);
+      slat0 = sindeg (cel->ref[1]);
+      cphip = cosdeg (cel->ref[2]);
+      sphip = sindeg (cel->ref[2]);
+      cthe0 = cosdeg (prj->theta0);
+      sthe0 = sindeg (prj->theta0);
+
+      x = cthe0*cphip;
+      y = sthe0;
+      z = sqrt(x*x + y*y);
+      if (z == 0.0) {
+         if (slat0 != 0.0) {
+            return 1;
+         }
+
+         /* latp determined by LATPOLE in this case. */
+         latp = cel->ref[3];
+      } else {
+         if (fabs(slat0/z) > 1.0) {
+            return 1;
+         }
+
+         u = atan2deg (y,x);
+         v = acosdeg (slat0/z);
+
+         latp1 = u + v;
+         if (latp1 > 180.0) {
+            latp1 -= 360.0;
+         } else if (latp1 < -180.0) {
+            latp1 += 360.0;
+         }
+
+         latp2 = u - v;
+         if (latp2 > 180.0) {
+            latp2 -= 360.0;
+         } else if (latp2 < -180.0) {
+            latp2 += 360.0;
+         }
+
+         if (fabs(cel->ref[3]-latp1) < fabs(cel->ref[3]-latp2)) {
+            if (fabs(latp1) < 90.0+tol) {
+               latp = latp1;
+            } else {
+               latp = latp2;
+            }
+         } else {
+            if (fabs(latp2) < 90.0+tol) {
+               latp = latp2;
+            } else {
+               latp = latp1;
+            }
+         }
+
+         cel->ref[3] = latp;
+      }
+
+      cel->euler[1] = 90.0 - latp;
+
+      z = cosdeg (latp)*clat0;
+      if (fabs(z) < tol) {
+         if (fabs(clat0) < tol) {
+            /* Celestial pole at the reference point. */
+            cel->euler[0] = cel->ref[0];
+            cel->euler[1] = 90.0 - prj->theta0;
+         } else if (latp > 0.0) {
+            /* Celestial pole at the native north pole.*/
+            cel->euler[0] = cel->ref[0] + cel->ref[2] - 180.0;
+            cel->euler[1] = 0.0;
+         } else if (latp < 0.0) {
+            /* Celestial pole at the native south pole. */
+            cel->euler[0] = cel->ref[0] - cel->ref[2];
+            cel->euler[1] = 180.0;
+         }
+      } else {
+         x = (sthe0 - sindeg (latp)*slat0)/z;
+         y =  sphip*cthe0/clat0;
+         if (x == 0.0 && y == 0.0) {
+            return 1;
+         }
+         cel->euler[0] = cel->ref[0] - atan2deg (y,x);
+      }
+
+      /* Make euler[0] the same sign as ref[0]. */
+      if (cel->ref[0] >= 0.0) {
+         if (cel->euler[0] < 0.0) cel->euler[0] += 360.0;
+      } else {
+         if (cel->euler[0] > 0.0) cel->euler[0] -= 360.0;
+      }
+   }
+
+   cel->euler[2] = cel->ref[2];
+   cel->euler[3] = cosdeg (cel->euler[1]);
+   cel->euler[4] = sindeg (cel->euler[1]);
+   cel->flag = CELSET;
+
+   /* Check for ill-conditioned parameters. */
+   if (fabs(latp) > 90.0+tol) {
+      return 2;
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int
+celfwd(pcode, lng, lat, cel, phi, theta, prj, x, y)
+
+const char pcode[4];
+const double lng, lat;
+struct celprm *cel;
+double *phi, *theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   int    err;
+
+   if (cel->flag != CELSET) {
+      if (celset(pcode, cel, prj)) return 1;
+   }
+
+   /* Compute native coordinates. */
+   sphfwd(lng, lat, cel->euler, phi, theta);
+
+   /* Apply forward projection. */
+   if ((err = prj->prjfwd(*phi, *theta, prj, x, y))) {
+      return err == 1 ? 2 : 3;
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int
+celrev(pcode, x, y, prj, phi, theta, cel, lng, lat)
+
+const char pcode[4];
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+struct celprm *cel;
+double *lng, *lat;
+
+{
+   int    err;
+
+   if (cel->flag != CELSET) {
+      if(celset(pcode, cel, prj)) return 1;
+   }
+
+   /* Apply reverse projection. */
+   if ((err = prj->prjrev(x, y, prj, phi, theta))) {
+      return err == 1 ? 2 : 3;
+   }
+
+   /* Compute native coordinates. */
+   sphrev(*phi, *theta, cel->euler, lng, lat);
+
+   return 0;
+}
+
+/* Dec 20 1999	Doug Mink - Change cosd() and sind() to cosdeg() and sindeg()
+ * Dec 20 1999	Doug Mink - Include wcslib.h, which includes wcsmath.h and cel.h
+ *
+ * Dec 18 2000	Doug Mink - Include string.h for strcmp()
+ *
+ * Mar 20 2001	Doug Mink - Add () around err assignments in if statements
+ * Sep 19 2001	Doug Mink - Add above changes to WCSLIB-2.7 cel.c
+ *
+ * Mar 12 2002	Doug Mink - Add changes to WCSLIB-2.8.2 cel.c
+ */
diff --git a/Code/src/libwcs/ctgread.c b/Code/src/libwcs/ctgread.c
new file mode 100644
index 0000000000000000000000000000000000000000..fcb13cc811455f8dbae7c24b0b2fcb755ff674dc
--- /dev/null
+++ b/Code/src/libwcs/ctgread.c
@@ -0,0 +1,2120 @@
+/*** File libwcs/ctgread.c
+ *** September 30, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1998-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* int ctgread()	Read catalog stars in specified region of the sky
+ * int ctgrnum()	Read catalog stars with specified numbers
+ * int ctgopen()	Open catalog, returning number of entries
+ * int ctgstar()	Get ASCII catalog entry for one star
+ * int ctgsize()	Return length of file in bytes
+ * int isacat()		Return 1 if file is ASCII catalog, else 0
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <sys/types.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+/* default pathname for catalog,  used if catalog file not found in current
+   working directory, but overridden by WCS_CATDIR environment variable */
+char catdir[64]="/data/catalogs";
+
+#define MAX_LTOK	80
+
+static double ctg2ra();
+static double ctg2dec();
+static int ctgsize();
+double dt2ep();		/* Julian Date to epoch (fractional year) */
+
+static char newline = 10;
+
+
+/* CTGREAD -- Read ASCII stars in specified region */
+
+int
+ctgread (catfile, refcat, distsort, cra, cdec, dra, ddec, drad, dradi,
+	 sysout, eqout, epout, mag1, mag2, sortmag, nsmax, starcat,
+	 tnum, tra, tdec, tpra, tpdec, tmag, tc, tobj, nlog)
+
+char	*catfile;	/* Name of reference star catalog file */
+int	refcat;		/* Catalog code from wcscat.h */
+int	distsort;	/* 1 to sort stars by distance from center */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Number of magnitude by which to limit and sort */
+int	nsmax;		/* Maximum number of stars to be returned */
+struct StarCat **starcat; /* Catalog data structure */
+double	*tnum;		/* Array of ID numbers (returned) */
+double	*tra;		/* Array of right ascensions (returned) */
+double	*tdec;		/* Array of declinations (returned) */
+double	*tpra;		/* Array of right ascension proper motions (returned) */
+double	*tpdec;		/* Array of declination proper motions (returned) */
+double	**tmag;		/* 2-D array of magnitudes (returned) */
+int	*tc;		/* Array of fluxes (returned) */
+char	**tobj;		/* Array of object names (returned) */
+int	nlog;
+{
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    double dist = 0.0;  /* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    int faintstar=0;    /* Faintest star */
+    int farstar=0;      /* Most distant star */
+    double *tdist;      /* Array of distances to stars */
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog equinox */
+    double epref;	/* Catalog epoch */
+    char cstr[32];
+    struct Star *star;
+    struct StarCat *sc; /* Catalog data structure */
+    char *objname;
+    int nameobj;	/* Save object name if 1, else do not */
+    int lname;
+    int wrap;
+    int imag;
+    int jstar;
+    int nstar;
+    int magsort;
+    double ra,dec,rapm,decpm;
+    double mag;
+    double num;
+    double rdist, ddist;
+    int i;
+    int istar;
+    int verbose;
+    int isp = 0;
+    int pass;
+
+    nstar = 0;
+
+    /* Call the appropriate search program if not TDC ASCII catalog */
+    if (refcat != TXTCAT) {
+        if (refcat == GSC || refcat == GSCACT)
+            nstar = gscread (refcat,cra,cdec,dra,ddec,drad,dradi,distsort,
+			     sysout,eqout,epout,mag1,mag2,nsmax,
+			     tnum,tra,tdec,tmag,tc,nlog);
+        else if (refcat == GSC2)
+            nstar = gsc2read (catfile,cra,cdec,dra,ddec,drad,dradi,distsort,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,
+			     tnum,tobj,tra,tdec,tpra,tpdec,tmag,tc,nlog);
+        else if (refcat == SDSS)
+            nstar = sdssread (cra,cdec,dra,ddec,drad,dradi,distsort,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,
+			     tnum,tobj,tra,tdec,tmag,tc,nlog);
+        else if (refcat == USAC || refcat == USA1 || refcat == USA2 ||
+                 refcat == UAC  || refcat == UA1  || refcat == UA2)
+            nstar = uacread (catfile,distsort,cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,tnum,
+			     tra,tdec,tmag,tc,nlog);
+        else if (refcat == UJC || refcat == USNO)
+            nstar = ujcread (catfile,cra,cdec,dra,ddec,drad,dradi,distsort,
+			     sysout,eqout,epout,mag1,mag2,nsmax,
+			     tnum,tra,tdec,tmag,tc,nlog);
+        else if (refcat == UB1 || refcat == YB6)
+            nstar = ubcread (catfile,distsort,cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,tnum,
+			     tra,tdec,tpra,tpdec,tmag,tc,nlog);
+        else if (refcat == UCAC1 || refcat == UCAC2 || refcat == UCAC3)
+            nstar = ucacread (catfile,cra,cdec,dra,ddec,drad,dradi,distsort,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,nlog);
+        else if (refcat == TMPSC || refcat == TMIDR2 || refcat == TMXSC ||
+		 refcat == TMPSCE)
+            nstar = tmcread (refcat,cra,cdec,dra,ddec,drad,dradi,distsort,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,
+			     tnum,tra,tdec,tmag,tc,nlog);
+        else if (refcat == ACT)
+            nstar = actread (cra,cdec,dra,ddec,drad,dradi,distsort,
+			     sysout,eqout,epout,mag1, mag2,sortmag,nsmax,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,nlog);
+        else if (refcat == TYCHO2 || refcat == TYCHO2E)
+            nstar = ty2read (refcat, cra,cdec,dra,ddec,drad,dradi,distsort,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,nlog);
+        else if (refcat == SAO)
+            nstar = binread ("SAOra", distsort,cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,starcat,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+        else if (refcat == PPM)
+            nstar = binread ("PPMra",distsort,cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,starcat,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+        else if (refcat == SKY2K)
+            nstar = binread ("sky2kra",distsort,cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,starcat,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+        else if (refcat == IRAS)
+            nstar = binread ("IRAS", distsort, cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,starcat,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+        else if (refcat == TYCHO)
+            nstar = binread ("tychora", distsort,cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,starcat,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+        else if (refcat == HIP)
+            nstar = binread("hipparcosra",distsort,cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,starcat,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+        else if (refcat == BSC)
+            nstar = binread ("BSC5ra", distsort, cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,starcat,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+        else if (refcat == BINCAT)
+            nstar = binread (catfile, distsort, cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,starcat,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,tobj,nlog);
+        else if (refcat == TABCAT || refcat == WEBCAT)
+            nstar = tabread (catfile, distsort,cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,starcat,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,tobj,nlog);
+        else if (refcat == SKYBOT)
+            nstar = skybotread (cra,cdec,dra,ddec,drad,distsort,
+			     sysout,eqout,epout,mag1,mag2,sortmag,nsmax,
+			     tnum,tobj,tra,tdec,tpra,tpdec,tmag,tc,nlog);
+	return (nstar);
+	}
+
+    star = NULL;
+    sc = *starcat;
+
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    wcscstr (cstr, sysout, eqout, epout);
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* If RA range includes zero, split it in two */
+    wrap = 0;
+    if (ra1 > ra2)
+	wrap = 1;
+    else
+	wrap = 0;
+
+    /* Search zones which include the poles cover 360 degrees in RA */
+    if (cdec - ddec < -90.0) {
+	if (dec1 > dec2)
+	    dec2 = dec1;
+	dec1 = -90.0;
+	ra1 = 0.0;
+	ra2 = 359.99999;
+	wrap = 0;
+	}
+    if (cdec + ddec > 90.0) {
+	if (dec2 < dec1)
+	    dec1 = dec2;
+	dec2 = 90.0;
+	ra1 = 0.0;
+	ra2 = 359.99999;
+	wrap = 0;
+	}
+
+    /* mag1 is always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    /* Logging interval */
+    tdist = (double *) malloc (nsmax * sizeof (double));
+
+    /* Open catalog file */
+    if (sc == NULL) {
+	if ((sc = ctgopen (catfile, refcat)) == NULL) {
+	    fprintf (stderr,"CTGRNUM: Cannot read catalog %s\n", catfile);
+	    *starcat = sc;
+	    return (0);
+	    }
+	}
+    *starcat = sc;
+    if (sc->nstars <= 0) {
+	free (sc);
+	if (star != NULL)
+	    free (star);
+	sc = NULL;
+	return (0);
+	}
+
+    if (sortmag > 0 && sortmag <= sc->nmag)
+	magsort = sortmag - 1;
+    else 
+	magsort = 1;
+
+    jstar = 0;
+    if (tobj == NULL || sc->ignore)
+	nameobj = 0;
+    else
+	nameobj = 1;
+
+    /* Loop through catalog */
+    for (istar = 1; istar <= sc->nstars; istar++) {
+	if (ctgstar (istar, sc, star)) {
+	    fprintf (stderr,"\nCTGREAD: Cannot read %s star %d\n",
+		     sc->isfil, istar);
+	    break;
+	    }
+
+	/* Magnitude */
+	mag = star->xmag[magsort];
+
+	/* Check magnitude limits */
+	pass = 1;
+	if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+	    pass = 0;
+
+	/* Set coordinate system for this star */
+	if (pass) {
+	    sysref = star->coorsys;
+	    eqref = star->equinox;
+	    epref = star->epoch;
+
+	    /* Extract selected fields  */
+	    num = star->num;
+	    ra = star->ra;
+	    dec = star->dec;
+	    rapm = star->rapm;
+	    decpm = star->decpm;
+
+	    /* If catalog is RA-sorted, stop reading if past highest RA */
+	    if (sc->rasorted && !wrap && ra > ra2)
+		break;
+
+	    /* Get position in output coordinate system, equinox, and epoch */
+	    if (sc->inform != 'X') {
+		if (sc->mprop == 1)
+		    wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		         &ra, &dec, &rapm, &decpm);
+		else
+		    wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+		}
+
+	    /* Compute distance from search center */
+	    if (drad > 0 || distsort) {
+		if (sc->inform == 'X')
+		    dist = sqrt ((ra-cra)*(ra-cra) + (dec-cdec)*(dec-cdec));
+		else
+		    dist = wcsdist (cra,cdec,ra,dec);
+		}
+	    else
+		dist = 0.0;
+
+	    /* Check radial distance to search center */
+	    if (drad > 0) {
+		if (dist > drad)
+		    pass = 0;
+		if (dradi > 0.0 && dist < dradi)
+		    pass = 0;
+		}
+
+	    /* Check distance along RA and Dec axes */
+	    else {
+		ddist = wcsdist (cra,cdec,cra,dec);
+		if (ddist > ddec)
+		    pass = 0;
+		rdist = wcsdist (cra,dec,ra,dec);
+		if (rdist > dra)
+		    pass = 0;
+		}
+	    }
+
+	/* Check magnitude and position limits */
+	if (pass) {
+
+	    /* Spectral type */
+	    if (sc->sptype)
+		isp = (1000 * (int) star->isp[0]) + (int)star->isp[1];
+
+	    /* Save star position and magnitude in table */
+	    if (nstar < nsmax) {
+		tnum[nstar] = num;
+		tra[nstar] = ra;
+		tdec[nstar] = dec;
+		for (imag = 0; imag < sc->nmag; imag++) {
+		    if (tmag[imag] != NULL)
+			tmag[imag][nstar] = star->xmag[imag];
+		    }
+		if (sc->mprop == 1) {
+		    tpra[nstar] = rapm;
+		    tpdec[nstar] = decpm;
+		    }
+		if (sc->sptype)
+		    tc[nstar] = isp;
+		tdist[nstar] = dist;
+		if (nameobj) {
+		    lname = strlen (star->objname) + 1;
+		    if (lname > 1) {
+			objname = (char *)calloc (lname, 1);
+			strcpy (objname, star->objname);
+			tobj[nstar] = objname;
+			}
+		    else
+			tobj[nstar] = NULL;
+		    }
+		if (dist > maxdist) {
+			maxdist = dist;
+			farstar = nstar;
+			}
+		if (mag > faintmag) {
+			faintmag = mag;
+			faintstar = nstar;
+			}
+		}
+
+	    /* If too many stars and distance sorting, replace furthest star */
+	    else if (distsort) {
+		if (dist < maxdist) {
+		    tnum[farstar] = num;
+		    tra[farstar] = ra;
+		    tdec[farstar] = dec;
+		    if (sc->mprop == 1) {
+			tpra[farstar] = rapm;
+			tpdec[farstar] = decpm;
+			}
+		    for (imag = 0; imag < sc->nmag; imag++) {
+			if (tmag[imag] != NULL)
+			    tmag[imag][farstar] = star->xmag[imag];
+			}
+		    if (sc->sptype)
+			tc[farstar] = isp;
+		    tdist[farstar] = dist;
+		    if (nameobj) {
+			free (tobj[farstar]);
+			lname = strlen (star->objname) + 1;
+			if (lname > 1) {
+			    objname = (char *)calloc (lname, 1);
+			    strcpy (objname, star->objname);
+			    tobj[farstar] = objname;
+			    }
+			else
+			    tobj[farstar] = NULL;
+			}
+
+		    /* Find new farthest star */
+		    maxdist = 0.0;
+		    for (i = 0; i < nsmax; i++) {
+			if (tdist[i] > maxdist) {
+			    maxdist = tdist[i];
+			    farstar = i;
+			    }
+			}
+		    }
+		}
+
+	    /* If too many stars, replace faintest star */
+	    else if (mag < faintmag) {
+		tnum[faintstar] = num;
+		tra[faintstar] = ra;
+		tdec[faintstar] = dec;
+		if (sc->mprop == 1) {
+		    tpra[faintstar] = rapm;
+		    tpdec[faintstar] = decpm;
+		    }
+		for (imag = 0; imag < sc->nmag; imag++) {
+		    if (tmag[imag] != NULL)
+			tmag[imag][faintstar] = star->xmag[imag];
+		    }
+		if (sc->sptype)
+		    tc[faintstar] = isp;
+		tdist[faintstar] = dist;
+		if (nameobj) {
+		    free (tobj[faintstar]);
+		    lname = strlen (star->objname) + 1;
+		    if (lname > 1) {
+			objname = (char *)calloc (lname, 1);
+			strcpy (objname, star->objname);
+			tobj[faintstar] = objname;
+			}
+		    else
+			tobj[faintstar] = NULL;
+		    }
+		faintmag = 0.0;
+
+		/* Find new faintest star */
+		for (i = 0; i < nsmax; i++) {
+		    if (tmag[magsort][i] > faintmag) {
+			faintmag = tmag[magsort][i];
+			faintstar = i;
+			}
+		    }
+		}
+
+	    nstar++;
+	    jstar++;
+	    if (nlog == 1)
+		fprintf (stderr,"CTGREAD: %11.6f: %9.5f %9.5f %s %5.2f    \n",
+			 num,ra,dec,cstr,mag);
+
+	    /* End of accepted star processing */
+	    }
+
+	/* Log operation */
+	if (nlog > 0 && istar%nlog == 0)
+	    fprintf (stderr,"CTGREAD: %5d / %5d / %5d sources catalog %s\r",
+		     jstar,istar,sc->nstars,catfile);
+
+	/* End of star loop */
+	}
+
+    /* Summarize search */
+    if (nlog > 0) {
+	fprintf (stderr,"CTGREAD: Catalog %s : %d / %d / %d found\n",
+		 catfile,jstar,istar,sc->nstars);
+	if (nstar > nsmax)
+	    fprintf (stderr,"CTGREAD: %d stars found; only %d returned\n",
+		     nstar,nsmax);
+	}
+
+    free ((char *)tdist);
+    free (star);
+    return (nstar);
+}
+
+
+/* CTGRNUM -- Read ASCII stars with specified numbers */
+
+int
+ctgrnum (catfile,refcat, nnum,sysout,eqout,epout,match,starcat,
+	 tnum,tra,tdec,tpra,tpdec,tmag,tc,tobj,nlog)
+
+char	*catfile;	/* Name of reference star catalog file */
+int	refcat;		/* Catalog code from wcscat.h */
+int	nnum;		/* Number of stars to look for */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+int	match;		/* 1 to match star number exactly, else sequence num.*/
+struct StarCat **starcat; /* Catalog data structure */
+double	*tnum;		/* Array of star numbers to look for */
+double	*tra;		/* Array of right ascensions (returned) */
+double	*tdec;		/* Array of declinations (returned) */
+double	*tpra;		/* Array of right ascension proper motions (returned) */
+double	*tpdec;		/* Array of declination proper motions (returned) */
+double	**tmag;		/* 2-D Array of magnitudes (returned) */
+int	*tc;		/* Array of fluxes (returned) */
+char	**tobj;		/* Array of object names (returned) */
+int	nlog;
+{
+    int jnum;
+    int nstar;
+    double ra,dec;
+    double rapm, decpm;
+    int istar;
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog equinox */
+    double epref;	/* Catalog epoch */
+    char cstr[32];
+    struct StarCat *sc;
+    struct Star *star;
+    char *objname;
+    int imag;
+    int lname;
+    int starfound;
+    int nameobj;
+
+    nstar = 0;
+
+    /* Call the appropriate search program if not TDC ASCII catalog */
+    if (refcat != TXTCAT) {
+        if (refcat == GSC || refcat == GSCACT)
+	    nstar = gscrnum (refcat,nnum,sysout,eqout,epout,
+			     tnum,tra,tdec,tmag,tc,nlog);
+	else if (refcat == GSC2)
+	    nstar = 0;
+	else if (refcat == USAC || refcat == USA1 || refcat == USA2 ||
+	         refcat == UAC  || refcat == UA1  || refcat == UA2)
+	    nstar = uacrnum (catfile,nnum,sysout,eqout,epout,
+			     tnum,tra,tdec,tmag,tc,nlog);
+	else if (refcat == UB1 || refcat == YB6)
+	    nstar = ubcrnum (catfile,nnum,sysout,eqout,epout,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,nlog);
+        else if (refcat == UJC || refcat == USNO)
+	    nstar = ujcrnum (catfile,nnum,sysout,eqout,epout,
+			     tnum,tra,tdec,tmag,tc,nlog);
+        else if (refcat == UCAC1 || refcat == UCAC2 || refcat == UCAC3)
+	    nstar = ucacrnum (catfile,nnum,sysout,eqout,epout,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,nlog);
+        else if (refcat == TMPSC || refcat == TMPSCE ||
+		 refcat == TMIDR2 || refcat == TMXSC)
+	    nstar = tmcrnum (refcat,nnum,sysout,eqout,epout,
+			     tnum,tra,tdec,tmag,tc,nlog);
+	else if (refcat == SAO)
+	    nstar = binrnum ("SAO",nnum,sysout,eqout,epout,match,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+	else if (refcat == PPM)
+	    nstar = binrnum ("PPM",nnum,sysout,eqout,epout,match,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+	else if (refcat == SKY2K)
+	    nstar = binrnum ("sky2k",nnum,sysout,eqout,epout,match,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+	else if (refcat == IRAS)
+	    nstar = binrnum ("IRAS",nnum,sysout,eqout,epout,match,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+	else if (refcat == TYCHO)
+	    nstar = binrnum ("tycho",nnum,sysout,eqout,epout,match,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+	else if (refcat == HIP)
+	    nstar = binrnum ("hipparcos",nnum,sysout,eqout,epout,match,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+	else if (refcat == BSC)
+	    nstar = binrnum ("BSC5",nnum,sysout,eqout,epout,match,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,NULL,nlog);
+	else if (refcat == ACT)
+	    nstar = actrnum (nnum,sysout,eqout,epout,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,nlog);
+	else if (refcat == TYCHO2 || refcat == TYCHO2E)
+	    nstar = ty2rnum (refcat,nnum,sysout,eqout,epout,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,nlog);
+	else if (refcat == TABCAT || refcat == WEBCAT)
+	    nstar = tabrnum (catfile,nnum,sysout,eqout,epout,starcat,match,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,tobj,nlog);
+	else if (refcat == BINCAT)
+	    nstar = binrnum (catfile,nnum,sysout,eqout,epout,match,
+			     tnum,tra,tdec,tpra,tpdec,tmag,tc,tobj,nlog);
+	return (nstar);
+	}
+
+    sc = *starcat;
+    if (sc == NULL) {
+	if ((sc = ctgopen (catfile, refcat)) == NULL) {
+	    fprintf (stderr,"CTGRNUM: Cannot read catalog %s\n", catfile);
+	    *starcat = sc;
+	    return (0);
+	    }
+	}
+    *starcat = sc;
+    sysref = sc->coorsys;
+    eqref = sc->equinox;
+    epref = sc->epoch;
+    if (!sysout)
+	sysout = sysref;
+    if (!eqout)
+	eqout = eqref;
+    if (!epout)
+	epout = epref;
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+    if (tobj == NULL || sc->ignore)
+	nameobj = 0;
+    else
+	nameobj = 1;
+
+    /* Loop through star list */
+    for (jnum = 0; jnum < nnum; jnum++) {
+
+	/* Loop through catalog to star */
+	starfound = 0;
+	if (match && sc->stnum > 0) {
+	    for (istar = 1; istar <= sc->nstars; istar++) {
+		if (ctgstar (istar, sc, star)) {
+		    fprintf (stderr,"CTGRNUM: Cannot read star %d\n", istar);
+		    break;
+		    }
+		if (star->num == tnum[jnum]) {
+		    starfound = 1;
+		    break;
+		    }
+		}
+	    }
+	else {
+	    istar = (int) (tnum[jnum] + 0.5);
+	    if (ctgstar (istar, sc, star)) {
+		fprintf (stderr,"CTGRNUM: Cannot read star %d\n", istar);
+		continue;
+		}
+	    starfound = 1;
+	    }
+
+	/* If star has been found in catalog */
+	if (starfound) {
+
+	    /* Extract selected fields  */
+	    ra = star->ra;
+	    dec = star->dec;
+	    rapm = star->rapm;
+	    decpm = star->decpm;
+
+	    /* Set coordinate system for this star */
+	    sysref = star->coorsys;
+	    eqref = star->equinox;
+	    epref = star->epoch;
+    
+	    if (sc->inform != 'X') {
+		if (sc->mprop == 1)
+		    wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+			     &ra, &dec, &rapm, &decpm);
+		else
+		    wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+		}
+
+	    /* Save star position and magnitude in table */
+	    tnum[nstar] = star->num;
+	    tra[nstar] = ra;
+	    tdec[nstar] = dec;
+	    if (sc->mprop == 1) {
+		tpra[nstar] = rapm;
+		tpdec[nstar] = decpm;
+		}
+	    for (imag = 0; imag < sc->nmag; imag++) {
+		if (tmag[imag] != NULL)
+		    tmag[imag][nstar] = star->xmag[imag];
+		}
+
+	    /* Spectral type */
+	    if (sc->sptype)
+		tc[nstar] = (1000 * (int) star->isp[0]) + (int)star->isp[1];
+
+	    if (nameobj) {
+		lname = strlen (star->objname) + 1;
+		if (lname > 1) {
+		    objname = (char *)calloc (lname, 1);
+		    strcpy (objname, star->objname);
+		    tobj[nstar] = objname;
+		    }
+		else
+		    tobj[nstar] = NULL;
+		}
+	    nstar++;
+	    if (nlog == 1)
+		fprintf (stderr,"CTGRNUM: %11.6f: %9.5f %9.5f %s %5.2f    \n",
+			 star->num,ra,dec,cstr,star->xmag[0]);
+
+	    /* End of accepted star processing */
+	    }
+
+	/* Log operation */
+	if (nlog > 0 && jnum%nlog == 0)
+	    fprintf (stderr,"CTGRNUM: %5d / %5d / %5d sources catalog %s\r",
+		     nstar,jnum,sc->nstars,catfile);
+
+	/* End of star loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"CTGRNUM: Catalog %s : %d / %d found\n",
+		 catfile,nstar,sc->nstars);
+
+    free (star);
+    return (nstar);
+}
+
+
+/* CTGRDATE -- Read ASCII stars with specified date range */
+
+int
+ctgrdate (catfile,refcat,sysout,eqout,epout,starcat,date1,date2,
+	 nmax,tnum,tra,tdec,tpra,tpdec,tmag,tc,tobj,nlog)
+
+char	*catfile;	/* Name of reference star catalog file */
+int	refcat;		/* Catalog code from wcscat.h */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+struct StarCat **starcat; /* Catalog data structure */
+double	date1;		/* Start time as Modified Julian Date or Julian Date */
+double	date2;		/* End time as Modified Julian Date or Julian Date */
+int	nmax;		/* Maximum number of stars to look for */
+double	*tnum;		/* Array of star numbers to look for */
+double	*tra;		/* Array of right ascensions (returned) */
+double	*tdec;		/* Array of declinations (returned) */
+double	*tpra;		/* Array of right ascension proper motions (returned) */
+double	*tpdec;		/* Array of declination proper motions (returned) */
+double	**tmag;		/* 2-D Array of magnitudes (returned) */
+int	*tc;		/* Array of fluxes (returned) */
+char	**tobj;		/* Array of object names (returned) */
+int	nlog;
+{
+    int nstar;
+    double ra,dec;
+    double rapm, decpm;
+    int istar;
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog equinox */
+    double epref;	/* Catalog epoch */
+    double epoch1, epoch2;
+    char cstr[32];
+    struct StarCat *sc;
+    struct Star *star;
+    char *objname;
+    int imag;
+    int lname;
+    int starfound;
+    int nameobj;
+
+    nstar = 0;
+
+    /* Call the appropriate search program if not TDC ASCII catalog
+    if (refcat != TXTCAT) {
+	if (refcat == TABCAT || refcat == WEBCAT)
+	    nstar = tabrdate (catfile,sysout,eqout,epout,starcat,date1,date2,
+			     nmax,tnum,tra,tdec,tpra,tpdec,tmag,tc,tobj,nlog);
+	else if (refcat == BINCAT)
+	    nstar = binrdate (catfile,nnum,sysout,eqout,epout,date1,date2,
+			     nmax,tnum,tra,tdec,tpra,tpdec,tmag,tc,tobj,nlog);
+	return (nstar);
+	} */
+
+    sc = *starcat;
+    if (sc == NULL) {
+	if ((sc = ctgopen (catfile, refcat)) == NULL) {
+	    fprintf (stderr,"CTGRDATE: Cannot read catalog %s\n", catfile);
+	    *starcat = sc;
+	    return (0);
+	    }
+	}
+    *starcat = sc;
+    sysref = sc->coorsys;
+    eqref = sc->equinox;
+    epref = sc->epoch;
+    if (!sysout)
+	sysout = sysref;
+    if (!eqout)
+	eqout = eqref;
+    if (!epout)
+	epout = epref;
+
+    /* Convert date limits to fractional year for easy testing */
+    if (date1 < 3000.0 && date1 > 0.0)
+	epoch1 = date1;
+    else if (date1 < 100000.0)
+	epoch1 = mjd2ep (date1);
+    else
+        epoch1 = jd2ep (date1);
+    if (date2 < 3000.0 && date2 > 0.0)
+	epoch2 = date2;
+    else if (date2 < 100000.0)
+	epoch2 = mjd2ep (date2);
+    else
+        epoch2 = jd2ep (date2);
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+    if (tobj == NULL || sc->ignore)
+	nameobj = 0;
+    else
+	nameobj = 1;
+
+    /* Loop through catalog to star from first date */
+    starfound = 0;
+    nstar = 0;
+    for (istar = 1; istar <= sc->nstars; istar++) {
+	if (ctgstar (istar, sc, star)) {
+	    fprintf (stderr,"CTGRDATE: Cannot read star %d\n", istar);
+	    break;
+	    }
+
+	/* If before start date, skip to next star */
+	if (star->epoch < epoch1) {
+	    continue;
+	    }
+
+	/* If past starting date, drop out of reading loop */
+	else if (star->epoch > epoch2) {
+	    break;
+	    }
+
+	/* Extract selected fields  */
+	ra = star->ra;
+	dec = star->dec;
+	rapm = star->rapm;
+	decpm = star->decpm;
+
+	/* Set coordinate system for this star */
+	sysref = star->coorsys;
+	eqref = star->equinox;
+	epref = star->epoch;
+    
+	if (sc->inform != 'X') {
+	    if (sc->mprop == 1)
+		wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+			 &ra, &dec, &rapm, &decpm);
+	    else
+		wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+	    }
+
+	/* Save star position and magnitude in table */
+	tnum[nstar] = star->num;
+	tra[nstar] = ra;
+	tdec[nstar] = dec;
+	if (sc->mprop == 1) {
+	    tpra[nstar] = rapm;
+	    tpdec[nstar] = decpm;
+	    }
+	for (imag = 0; imag < sc->nmag; imag++) {
+	    if (tmag[imag] != NULL)
+		tmag[imag][nstar] = star->xmag[imag];
+	    }
+
+	/* Spectral type */
+	if (sc->sptype)
+	    tc[nstar] = (1000 * (int) star->isp[0]) + (int)star->isp[1];
+
+	if (nameobj) {
+	    lname = strlen (star->objname) + 1;
+	    if (lname > 1) {
+		objname = (char *)calloc (lname, 1);
+		strcpy (objname, star->objname);
+		tobj[nstar] = objname;
+		}
+	    else
+		tobj[nstar] = NULL;
+	    }
+	nstar++;
+	if (nlog == 1)
+	    fprintf (stderr,"CTGRDATE: %11.6f: %9.5f %9.5f %s %5.2f    \n",
+		     star->num,ra,dec,cstr,star->xmag[0]);
+
+	/* Log operation */
+	else if (nlog > 0 && istar%nlog == 0)
+	    fprintf (stderr,"CTGRDATE: %5d / %5d / %5d sources catalog %s\r",
+		     nstar,istar,sc->nstars,catfile);
+
+	/* End of star loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"CTGRDATE: Catalog %s : %d / %d found\n",
+		 catfile,nstar,sc->nstars);
+
+    free (star);
+    return (nstar);
+}
+
+
+/* CTGBIN -- Fill a FITS WCS image with stars from catalog */
+
+int
+ctgbin (catfile,refcat,wcs,header,image,mag1,mag2,sortmag,magscale,nlog)
+
+char	*catfile;	/* Name of reference star catalog file */
+int	refcat;		/* Catalog code from wcscat.h */
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Number of magnitude by which to limit and sort */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog equinox */
+    double epref;	/* Catalog epoch */
+    char cstr[32];
+    struct Star *star;
+    struct StarCat *sc; /* Catalog data structure */
+    int wrap;
+    int jstar;
+    int nstar;
+    int magsort;
+    double ra,dec,rapm,decpm;
+    double mag;
+    double num;
+    double rdist, ddist;
+    int istar;
+    int verbose;
+    int pass;
+    int ix, iy;
+    double xpix, ypix, flux;
+    int offscl;
+    int bitpix, w, h;   /* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+
+    nstar = 0;
+
+    /* Call the appropriate search program if not TDC ASCII catalog */
+    if (refcat != TXTCAT) {
+        if (refcat == GSC || refcat == GSCACT)
+            nstar = gscbin (refcat,wcs,header,image,mag1,mag2,magscale,nlog);
+        else if (refcat == USAC || refcat == USA1 || refcat == USA2 ||
+                 refcat == UAC  || refcat == UA1  || refcat == UA2)
+            nstar = uacbin (catfile,wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == UJC || refcat == USNO)
+            nstar = ujcbin (catfile,wcs,header,image,mag1,mag2,magscale,nlog);
+        else if (refcat == UB1 || refcat == YB6)
+            nstar = ubcbin (catfile,wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == UCAC1 || refcat == UCAC2 || refcat == UCAC3)
+            nstar = ucacbin (catfile,wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == TMPSC || refcat == TMIDR2 || refcat == TMXSC)
+            nstar = tmcbin (refcat,wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == ACT)
+            nstar = actbin (wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == TYCHO2 || refcat == TYCHO2E)
+            nstar = ty2bin (wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == SDSS || refcat == GSC2)
+            nstar = -1;
+        else if (refcat == SAO)
+            nstar = binbin ("SAOra",wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == PPM)
+            nstar = binbin ("PPMra",wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == SKY2K)
+            nstar = binbin ("sky2kra",wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == IRAS)
+            nstar = binbin ("IRAS",wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == TYCHO)
+            nstar = binbin ("tychora",wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == HIP)
+            nstar = binbin("hipparcosra",wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == BSC)
+            nstar = binbin ("BSC5ra",wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == BINCAT)
+            nstar = binbin (catfile,wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+        else if (refcat == TABCAT || refcat == WEBCAT)
+            nstar = tabbin (catfile,wcs,header,image,mag1,mag2,sortmag,magscale,nlog);
+	return (nstar);
+	}
+
+    star = NULL;
+
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* If RA range includes zero, split it in two */
+    wrap = 0;
+    if (ra1 > ra2)
+	wrap = 1;
+    else
+	wrap = 0;
+
+    /* Search zones which include the poles cover 360 degrees in RA */
+    if (cdec - ddec < -90.0) {
+	if (dec1 > dec2)
+	    dec2 = dec1;
+	dec1 = -90.0;
+	ra1 = 0.0;
+	ra2 = 359.99999;
+	wrap = 0;
+	}
+    if (cdec + ddec > 90.0) {
+	if (dec2 < dec1)
+	    dec1 = dec2;
+	dec2 = 90.0;
+	ra1 = 0.0;
+	ra2 = 359.99999;
+	wrap = 0;
+	}
+
+    /* mag1 is always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    /* Open catalog file */
+    if ((sc = ctgopen (catfile, refcat)) == NULL) {
+	fprintf (stderr,"CTGRNUM: Cannot read catalog %s\n", catfile);
+	return (0);
+	}
+    if (sc->nstars <= 0) {
+	free (sc);
+	if (star != NULL)
+	    free (star);
+	sc = NULL;
+	return (0);
+	}
+
+    if (sortmag > 0 && sortmag <= sc->nmag)
+	magsort = sortmag - 1;
+    else 
+	magsort = 1;
+
+    jstar = 0;
+
+    /* Loop through catalog */
+    for (istar = 1; istar <= sc->nstars; istar++) {
+	if (ctgstar (istar, sc, star)) {
+	    fprintf (stderr,"\nCTGBIN: Cannot read %s star %d\n",
+		     sc->isfil, istar);
+	    break;
+	    }
+
+	/* Magnitude */
+	mag = star->xmag[magsort];
+
+	/* Check magnitude limits */
+	pass = 1;
+	if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+	    pass = 0;
+
+	/* Set coordinate system for this star */
+	if (pass) {
+	    sysref = star->coorsys;
+	    eqref = star->equinox;
+	    epref = star->epoch;
+
+	    /* Extract selected fields  */
+	    num = star->num;
+	    ra = star->ra;
+	    dec = star->dec;
+	    rapm = star->rapm;
+	    decpm = star->decpm;
+
+	    /* If catalog is RA-sorted, stop reading if past highest RA */
+	    if (sc->rasorted && !wrap && ra > ra2)
+		break;
+
+	    /* Get position in output coordinate system, equinox, and epoch */
+	    if (sc->inform != 'X') {
+		if (sc->mprop == 1)
+		    wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		         &ra, &dec, &rapm, &decpm);
+		else
+		    wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+		}
+
+	    /* Check distance along RA and Dec axes */
+	    ddist = wcsdist (cra,cdec,cra,dec);
+	    if (ddist > ddec)
+		pass = 0;
+	    rdist = wcsdist (cra,dec,ra,dec);
+	    if (rdist > dra)
+		pass = 0;
+	    }
+
+	/* Save star in FITS image */
+	if (pass) {
+	    wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+	    if (!offscl) {
+		if (magscale > 0.0)
+		    flux = magscale * exp (logt * (-mag / 2.5));
+		else
+		    flux = 1.0;
+		ix = (int) (xpix + 0.5);
+		iy = (int) (ypix + 0.5);
+		addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+		nstar++;
+		jstar++;
+		}
+	    else {
+		ix = 0;
+		iy = 0;
+		}
+	    if (nlog == 1) {
+		fprintf (stderr,"CTGBIN: %11.6f: %9.5f %9.5f %s",
+			 num,ra,dec,cstr);
+		if (magscale > 0.0)
+		    fprintf (stderr, " %5.2f", mag);
+		if (!offscl)
+		    flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+		else
+		    flux = 0.0;
+		fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+		}
+
+	    /* End of accepted star processing */
+	    }
+
+	/* Log operation */
+	if (nlog > 0 && istar%nlog == 0)
+	    fprintf (stderr,"CTGBIN: %5d / %5d / %5d sources catalog %s\r",
+		     jstar,istar,sc->nstars,catfile);
+
+	/* End of star loop */
+	}
+
+    /* Summarize search */
+    if (nlog > 0) {
+	fprintf (stderr,"CTGBIN: Catalog %s : %d / %d / %d found\n",
+		 catfile,jstar,istar,sc->nstars);
+	}
+
+    free (star);
+    return (nstar);
+}
+
+
+/* CTGOPEN -- Open ASCII catalog, returning number of entries */
+
+struct StarCat *
+ctgopen (catfile, refcat)
+
+char	*catfile;	/* ASCII catalog file name */
+int	refcat;		/* Catalog code from wcscat.h (TXTCAT,BINCAT,TABCAT) */
+{
+    struct StarCat *sc;
+    struct Tokens tokens;
+    FILE *fcat;
+    char header[80];
+    char catpath[128];	/* Full pathname for catalog file */
+    char *catname;
+    char *str;
+    int nr, lfile, lhead, ldesc;
+    char *catnew, *catdesc;
+    char ctemp, *line, *linend, *cdot;
+    char token[MAX_LTOK];
+    int ntok;
+    int ltok;
+
+    if (refcat != TXTCAT) {
+	if (refcat == BINCAT)
+	    sc = binopen (catfile);
+	else if (refcat == TABCAT)
+	    sc = tabcatopen (catfile, NULL, 0);
+	else
+	    sc = NULL;
+	return (sc);
+	}
+
+/* Find length of ASCII catalog */
+    lfile = ctgsize (catfile);
+
+    /* If catalog is not in current directory, look elsewhere */
+    if (lfile < 2) {
+
+	/* Prepend directory name file not in working directory */
+	if ((str = getenv("WCS_CATDIR")) != NULL )
+	    strcpy (catpath, str);
+	else
+	    strcpy (catpath, catdir);
+	strcat (catpath, "/");
+	strcat (catpath, catfile);
+	lfile = ctgsize (catpath);
+	if (lfile < 2) {
+	    fprintf (stderr,"CTGOPEN: ASCII catalog %s has no entries\n",catfile);
+	    return (NULL);
+	    }
+	}
+    else
+	strcpy (catpath, catfile);
+
+    /* Allocate catalog data structure */
+    sc = (struct StarCat *) calloc (1, sizeof (struct StarCat));
+
+    /* Open ASCII catalog */
+    if (!(fcat = fopen (catpath, "r"))) {
+	fprintf (stderr,"CTGOPEN: ASCII catalog %s cannot be read\n",catfile);
+	free (sc);
+	return (NULL);
+	}
+
+    /* Allocate buffer to hold entire catalog and read it */
+    if ((sc->catbuff = malloc (lfile+2)) == NULL) {
+	fprintf (stderr,"CTGOPEN: Cannot allocate memory for ASCII catalog %s\n",
+		 catfile);
+	fclose (fcat);
+	free (sc);
+	return (NULL);
+	}
+    sc->catbuff[lfile] = (char) 0;
+    sc->catbuff[lfile+1] = (char) 0;
+
+    /* Separate filename from pathname and save in structure */
+    catname = strrchr (catfile,'/');
+    if (catname)
+	catname = catname + 1;
+    else
+	catname = catfile;
+    if (strlen (catname) < 24)
+	strcpy (sc->isfil, catname);
+    else
+	strncpy (sc->isfil, catname, 23);
+
+    /* Read entire catalog into memory at once */
+    nr = fread (sc->catbuff, 1, lfile, fcat);
+    if (nr < lfile) {
+	fprintf (stderr,"CTGOPEN: read only %d / %d bytes of file %s\n",
+		 nr, lfile, catfile);
+	(void) fclose (fcat);
+	free (sc);
+	return (NULL);
+	}
+
+    /* Extract catalog information from first line */
+    sc->inform = 'H';
+    sc->coorsys = WCS_B1950;
+    sc->epoch = 1950.0;
+    sc->equinox = 1950.0;
+    sc->nmag = 1;
+    sc->mprop = 0;
+    sc->rasorted = 0;
+    sc->sptype = 0;
+    sc->stnum = 1;
+    sc->entepoch = 0;
+    sc->entrv = 0;
+
+    catdesc = strchr (sc->catbuff, newline) + 1;
+    lhead = catdesc - sc->catbuff;
+    if (lhead > 79)
+	lhead = 79;
+    if (sc->catbuff[0] == '#' && sc->catbuff[1] == ' ') {
+	strncpy (header, sc->catbuff+2, lhead-2);
+	header[lhead-2] = (char) 0;
+	}
+    else if (sc->catbuff[0] == '#') {
+	strncpy (header, sc->catbuff+1, lhead-1);
+	header[lhead-1] = (char) 0;
+	}
+    else {
+	strncpy (header, sc->catbuff, lhead);
+	header[lhead] = (char) 0;
+	}
+
+    /* Catalog positions are in radians */
+    if (strsrch (header, "/a") || strsrch (header, "/A"))
+	sc->inform = 'R';
+
+    /* Catalog positions are in B1950 (FK4) coordinates */
+    if (strsrch (header, "/b") || strsrch (header, "/B")) {
+	sc->coorsys = WCS_B1950;
+	sc->epoch = 1950.0;
+	sc->equinox = 1950.0;
+	}
+
+    /* Catalog positions are in degrees */
+    if (strsrch (header, "/d") || strsrch (header, "/D"))
+	sc->inform = 'D';
+
+    /* Catalog positions are in ecliptic coordinates */
+    if (strsrch (header, "/e") || strsrch (header, "/E")) {
+	sc->coorsys = WCS_ECLIPTIC;
+	sc->inform = 'D';
+	sc->epoch = 2000.0;
+	sc->equinox = 2000.0;
+	}
+
+    /* Catalog positions are in hh.mm and fractional degrees */
+    if (strsrch (header, "/f") || strsrch (header, "/F"))
+	sc->inform = 'F';
+
+    /* Catalog positions are galactic coordinates */
+    if (strsrch (header, "/g") || strsrch (header, "/G")) {
+	sc->coorsys = WCS_GALACTIC;
+	sc->inform = 'D';
+	sc->epoch = 2000.0;
+	sc->equinox = 2000.0;
+	}
+
+    /* Catalog positions are in hh.mmsssss dd.mmssss format */
+    if (strsrch (header, "/h") || strsrch (header, "/H"))
+	sc->inform = 'H';
+
+    /* Ignore information after position and magnitude */
+    if (strsrch (header, "/i") || strsrch (header, "/I"))
+	sc->ignore = 1;
+    else
+	sc->ignore = 0;
+
+    /* Catalog positions are J2000 (FK5) coordinates */
+    if (strsrch (header, "/j") || strsrch (header, "/J")) {
+	sc->coorsys = WCS_J2000;
+	sc->epoch = 2000.0;
+	sc->equinox = 2000.0;
+	}
+
+    /* Catalog positions are in fractional hours and fractional degrees */
+    if (strsrch (header, "/k") || strsrch (header, "/K"))
+	sc->inform = 'K';
+
+    /* No magnitude */
+    if (strsrch (header, "/m") || strsrch (header, "/M"))
+	sc->nmag = 0;
+    else if (strsrch (header, "/2"))
+	sc->nmag = 2;
+    else if (strsrch (header, "/3"))
+	sc->nmag = 3;
+    else if (strsrch (header, "/4"))
+	sc->nmag = 4;
+
+    /* No number in first column, RA or object name first */
+    if (strsrch (header, "/n") || strsrch (header, "/N"))
+	sc->stnum = 0;
+
+    /* Object name instead of number in first column */
+    if (strsrch (header, "/o") || strsrch (header, "/O"))
+	sc->stnum = -16;
+
+    /* Proper motion */
+    if (strsrch (header, "/p") || strsrch (header, "/P"))
+	sc->mprop = 1;
+
+    if (strsrch (header, "/q") || strsrch (header, "/Q")) {
+	sc->coorsys = 0;
+	sc->epoch = 0.0;
+	sc->equinox = 0.0;
+	}
+
+    /* RA-sorted catalog */
+    if (strsrch (header, "/r") || strsrch (header, "/R"))
+	sc->rasorted = 1;
+
+    /* Spectral type present */
+    if (strsrch (header, "/s") || strsrch (header, "/S"))
+	sc->sptype = 1;
+
+    /* Table format (hh mm ss dd mm ss) */
+    if (strsrch (header, "/t") || strsrch (header, "/T"))
+	sc->inform = 'T';
+
+    /* Catalog positions are in hhmmss.s ddmmss.s (UZC) */
+    if (strsrch (header, "/u") || strsrch (header, "/U"))
+	sc->inform = 'U';
+
+    /* Radial velocity is included after magnitude(s) */
+    if (strsrch (header, "/v") || strsrch (header, "/V")) {
+	sc->mprop = 2;
+	sc->entrv = 1;
+	}
+
+    /* X Y format (x.xxxxxx y.yyyyy) */
+    if (strsrch (header, "/x") || strsrch (header, "/X")) {
+	sc->inform = 'X';
+	sc->coorsys = WCS_XY;
+	}
+
+    /* Epoch per catalog object as yyyy.mmdd */
+    if (strsrch (header, "/y") || strsrch (header, "/Y"))
+	sc->nepoch = 1;
+    else
+	sc->nepoch = 0;
+
+    /* Second line is description */
+    sc->catdata = strchr (catdesc, newline) + 1;
+    ldesc = sc->catdata - catdesc;
+    if (ldesc > 63)
+	ldesc = 63;
+    if (catdesc[0] == '#' && catdesc[1] == ' ') {
+	strncpy (sc->isname, catdesc+2, ldesc-2);
+	sc->isname[ldesc-2] = (char) 0;
+	}
+    else if (catdesc[0] == '#') {
+	strncpy (sc->isname, catdesc+1, ldesc-1);
+	sc->isname[ldesc-1] = (char) 0;
+	}
+    else {
+	strncpy (sc->isname, catdesc, ldesc);
+	sc->isname[ldesc] = (char) 0;
+	}
+
+    if (sc->entrv > 0 && sc->nmag < 10) {
+	sc->nmag = sc->nmag + 1;
+	strcpy (sc->keymag[sc->nmag-1], "velocity");
+	}
+
+    if (sc->nepoch > 0 && sc->nmag < 10) {
+	sc->nmag = sc->nmag + 1;
+	strcpy (sc->keymag[sc->nmag-1], "epoch");
+	}
+
+    /* Enumerate entries in ASCII catalog by counting newlines */
+    catnew = sc->catdata;
+    sc->nstars = 0;
+    while ((catnew = strchr (catnew, newline)) != NULL) {
+	catnew = catnew + 1;
+	if (*catnew != '#')
+	    sc->nstars = sc->nstars + 1;
+	}
+    sc->catline = sc->catdata;
+    sc->catlast = sc->catdata + lfile;
+    sc->istar = 1;
+
+    /* Check number of decimal places in star number, if present */
+    if (sc->stnum == 1) {
+
+	/* Temporarily terminate line with 0 */
+	line = sc->catline;
+	linend = strchr (sc->catline, newline);
+	if (linend == NULL)
+	    linend = sc->catlast;
+	ctemp = *linend;
+	*linend = (char) 0;
+
+	/* Extract information from line of catalog */
+	ntok = setoken (&tokens, line, NULL);
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	sc->nnfld = ltok;
+	if (ltok > 0) {
+	    sc->nndec = 0;
+	    if ((cdot = strchr (token,'.')) != NULL) {
+		while (*(++cdot) != (char)0)
+		    sc->nndec++;
+		}
+	    }
+	else
+	    sc->nndec = 0;
+	*linend = ctemp;
+	}
+
+    sc->refcat = TXTCAT;
+
+    (void) fclose (fcat);
+    return (sc);
+}
+
+
+/* CTGCLOSE -- Close ASCII catalog and free associated data structures */
+
+void
+ctgclose (sc)
+
+struct	StarCat *sc;
+{
+    if (sc == NULL)
+	return;
+
+    else if (sc->refcat == BINCAT)
+	binclose (sc);
+    else if (sc->refcat == TABCAT)
+	tabcatclose (sc);
+    else if (sc->refcat == TXTCAT) {
+	free (sc->catbuff);
+	free (sc);
+	}
+    else
+	free (sc);
+
+    sc = NULL;
+    return;
+}
+
+
+
+/* CTGSTAR -- Get ASCII catalog entry for one star; return 0 if successful */
+
+int
+ctgstar (istar, sc, st)
+
+int istar;	/* Star sequence number in ASCII catalog */
+struct StarCat *sc; /* Star catalog data structure */
+struct Star *st; /* Star data structure, updated on return */
+{
+    struct Tokens tokens;
+    double ydate, dtemp;
+    char *line;
+    char *nextline;
+    char token[80];
+    char ctemp, *linend;
+    int ntok, itok;
+    int ltok;
+    int imag, nmag;
+
+    /* Return error if requested number beyond catalog */
+    if (istar > sc->nstars) {
+	fprintf (stderr, "CTGSTAR:  %d is not in catalog\n",istar);
+	return (-1);
+	}
+
+    /* If star is 0, read next star in catalog */
+    else if (istar < 1) {
+	line = strchr (sc->catline, newline) + 1;
+	while (*line == '#')
+	    line = strchr (line, newline) + 1;
+	if (line == NULL)
+	    return (-1);
+	else
+	    sc->catline = line;
+	}
+
+    /* If star is before current star, read from start of catalog */
+    else if (istar < sc->istar) {
+	sc->istar = 1;
+	sc->catline = sc->catdata;
+	nextline = sc->catline;
+	while (sc->istar < istar) {
+	    nextline = strchr (nextline, newline) + 1;
+	    while (*nextline == '#')
+		nextline = strchr (nextline, newline) + 1;
+	    if (nextline == NULL)
+		return (-1);
+	    sc->catline = nextline;
+	    sc->istar++;
+	    }
+	}
+
+    /* If star is after current star, read forward to it */
+    else if (istar > sc->istar) {
+	nextline = sc->catline;
+	while (sc->istar < istar) {
+	    nextline = strchr (nextline, newline) + 1;
+	    while (*nextline == '#')
+		nextline = strchr (nextline, newline) + 1;
+	    if (nextline == NULL)
+		return (-1);
+	    sc->catline = nextline;
+	    sc->istar++;
+	    }
+	}
+
+    /* Temporarily terminate line with 0 */
+    line = sc->catline;
+    linend = strchr (sc->catline, newline);
+    if (linend == NULL)
+	linend = sc->catlast;
+    ctemp = *linend;
+    *linend = (char) 0;
+    st->objname[0] = (char) 0;
+
+    /* Extract information from line of catalog */
+    ntok = setoken (&tokens, line, NULL);
+
+    /* Source number */
+    if (sc->stnum > 0) {
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok > 0) {
+	    st->num = atof (token);
+	    if (st->num - ((double) ((int) st->num)) < 0.000001)
+		sc->stnum = 1;
+	    else
+		sc->stnum = 2;
+	    }
+	}
+    else
+	st->num = (double) sc->istar;
+
+    /* Object name, if at start of line */
+    if (sc->stnum < 0) {
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok > 31) {
+	    strncpy (st->objname, token, 31);
+	    st->objname[31] = 0;
+	    }
+	else if (ltok > 0)
+	    strcpy (st->objname, token);
+	}
+
+    /* Right ascension or longitude */
+    ltok = nextoken (&tokens, token, MAX_LTOK);
+    if (ltok < 1)
+	return (-1);
+
+    /* Translate 3-token right ascension (hh mm ss.ss) */
+    if (sc->inform == 'T') {
+	int hr, mn;
+	double sec;
+
+	hr = (int) (atof (token) + 0.5);
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok < 1)
+	    return (-1);
+	mn = (int) (atof (token) + 0.5);
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok < 1)
+	    return (-1);
+	sec = atof (token);
+	st->ra = 15.0 * ((double) hr + ((double) mn / 60.0) + (sec / 3600.0));
+	}
+
+    /* Translate single-token right ascension from hhmmss.ss */
+    else if (sc->inform == 'U') {
+	dtemp = 0.0001 * atof (token);
+	sprintf (token, "%.6f", dtemp);
+	st->ra = ctg2ra (token);
+	}
+
+    /* Translate single-token right ascension as degrees */
+    else if (sc->inform == 'D')
+	st->ra = atof (token);
+
+    /* Translate single-token right ascension as hh.mm */
+    else if (sc->inform == 'F')
+	st->ra = ctg2ra (token);
+
+    /* Translate single-token right ascension as fractional hours */
+    else if (sc->inform == 'K')
+	st->ra = 15.0 * atof (token);
+
+    /* Translate single-token right ascension as radians */
+    else if (sc->inform == 'R')
+	st->ra = raddeg (atof (token));
+
+    /* Translate single-token right ascension as X image coordinate */
+    else if (sc->inform == 'X')
+	st->ra = atof (token);
+
+    /* Translate single-token right ascension (hh:mm:ss.ss or hh.mmssss) */
+    else
+	st->ra = ctg2ra (token);
+
+    /* Declination or latitude */
+    ltok = nextoken (&tokens, token, MAX_LTOK);
+    if (ltok < 1)
+	return (-1);
+
+    /* Translate 3-token declination (sdd mm ss.ss) */
+    if (sc->inform == 'T') {
+	int deg, min;
+	int decsgn = 0;
+	double sec;
+
+	if (strchr (token, '-') != NULL) {
+	    decsgn = 1;
+	    deg = (int) (atof (token) - 0.5);
+	    }
+	else
+	    deg = (int) (atof (token) + 0.5);
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok < 1)
+	    return (-1);
+	min = (int) (atof (token) + 0.5);
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok < 1)
+	    return (-1);
+	sec = atof (token);
+	st->dec = (double) deg + ((double) min / 60.0) + (sec / 3600.0);
+	if (deg > -1 && decsgn)
+	    st->dec = -st->dec;
+	}
+
+    /* Translate single-token declination as Y image coordinate */
+    else if (sc->inform == 'X')
+	st->dec = atof (token);
+
+    /* Translate single-token declination as degrees */
+    else if (sc->inform == 'D' || sc->inform == 'F' || sc->inform == 'K')
+	st->dec = atof (token);
+
+    /* Translate single-token declination as radians */
+    else if (sc->inform == 'R')
+	st->dec = raddeg (atof (token));
+
+    /* Translate single-token declination from ddmmss.ss */
+    else if (sc->inform == 'U') {
+	dtemp = 0.0001 * atof (token);
+	sprintf (token, "%.6f", dtemp);
+	st->dec = ctg2dec (token);
+	}
+
+    /* Translate single-token declination (dd:mm:ss.ss or dd.mmssss) */
+    else
+	st->dec = ctg2dec (token);
+
+    /* Equinox, if not set by header flag */
+    if (sc->coorsys == 0) {
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok < 1)
+	    return (-1);
+	st->coorsys = wcscsys (token);
+	st->equinox = wcsceq (token);
+	st->epoch = sc->epoch;
+	if (st->epoch == 0.0)
+	    st->epoch = st->equinox;
+	}
+
+    else {
+	st->coorsys = sc->coorsys;
+	st->equinox = sc->equinox;
+	st->epoch = sc->epoch;
+	}
+
+    /* Magnitude, if present */
+    nmag = sc->nmag;
+    if (sc->nmag > 0 && sc->mprop == 2)
+	nmag = nmag - 1;
+    if (sc->nmag > 0 && sc->nepoch)
+	nmag = nmag - 1;
+    if (nmag > 0) {
+	for (imag = 0; imag < nmag; imag++) {
+	    ltok = nextoken (&tokens, token, MAX_LTOK);
+	    if (ltok > 0)
+		st->xmag[imag] = atof (token);
+	    else
+		st->xmag[imag] = 0.0;
+	    }
+	}
+
+    /* Spectral type, if present */
+    if (sc->sptype > 0) {
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok > 0) {
+	    if (token[0] == ' ' && token[1] == ' ') {
+		st->isp[0] = '_';
+		st->isp[1] = '_';
+		}
+	    else {
+		st->isp[0] = token[0];
+		st->isp[1] = token[1];
+		}
+	    }
+	}
+
+    /* Radial velocity, if present */
+    if (sc->entrv > 0) {
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok > 0)
+	    st->radvel = atof (token);
+	else
+	    st->radvel = 0.0;
+	st->xmag[nmag] = st->radvel;
+	}
+
+    /* Proper motion, if present */
+    if (sc->mprop == 1) {
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok > 1)
+	    st->rapm = atof (token) / 3600.0;
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (ltok > 1)
+	    st->decpm = atof (token) / 3600.0;
+	}
+
+    /* Epoch, if present */
+    if (sc->nepoch) {
+	ltok = nextoken (&tokens, token, MAX_LTOK);
+	if (strchr (token, '-') != NULL)
+	    st->epoch = fd2ep (token);
+	else {
+	    ydate = atof (token);
+	    if (ydate < 3000.0 && ydate > 0.0)
+		st->epoch = dt2ep (ydate, 12.0);
+	    else if (ydate < 100000.0)
+		st->epoch = mjd2ep (ydate);
+	    else
+		st->epoch = jd2ep (ydate);
+	    }
+	if (sc->entrv > 0)
+	    st->xmag[nmag+1] = st->epoch;
+	else
+	    st->xmag[nmag] = st->epoch;
+	}
+
+    /* Object name */
+    itok = tokens.itok;
+    if (sc->stnum > 0 && itok < ntok && !sc->ignore) {
+	itok = -(itok+1);
+	ltok = getoken (&tokens, itok, token, MAX_LTOK);
+	if (ltok > 79) {
+	    strncpy (st->objname, token, 79);
+	    st->objname[79] = 0;
+	    }
+	else if (ltok > 0)
+	    strcpy (st->objname, token);
+	}
+
+    *linend = ctemp;
+    return (0);
+}
+
+
+/* CTGSIZE -- return size of ASCII catalog file in bytes */
+
+static int
+ctgsize (filename)
+
+char	*filename;	/* Name of file for which to find size */
+{
+    FILE *diskfile;
+    long filesize;
+
+    /* Open file */
+    if ((diskfile = fopen (filename, "r")) == NULL)
+	return (-1);
+
+    /* Move to end of the file */
+    if (fseek (diskfile, 0, 2) == 0)
+
+ 	/* Position is the size of the file */
+	filesize = ftell (diskfile);
+
+    else
+	filesize = -1;
+
+    fclose (diskfile);
+
+    return (filesize);
+}
+
+
+/* ISACAT -- Return 1 if file is likely to be an ASCII catalog */
+
+int
+isacat (catpath)
+
+char *catpath;
+
+{
+    char buf[100];
+    char *errc;
+    FILE *fcat;
+
+    /* Open ASCII catalog */
+    if (!(fcat = fopen (catpath, "r"))) {
+	return (0);
+	}
+    errc = fgets (buf, 100, fcat);
+    fclose (fcat);
+    if (isnum (buf))
+	return (0);
+    else
+	return (1);
+}
+
+
+/* CTG2RA -- Read the right ascension, ra, in sexagesimal hours from in[] */
+
+static double
+ctg2ra (in)
+
+char	*in;	/* Character string */
+
+{
+    double ra;	/* Right ascension in degrees (returned) */
+
+    ra = ctg2dec (in);
+    ra = ra * 15.0;
+
+    return (ra);
+}
+
+
+
+/* CTG2DEC -- Read the declination, dec, in sexagesimal degrees from in[] */
+
+static double
+ctg2dec (in)
+
+char	*in;	/* Character string */
+
+{
+    double dec;		/* Declination in degrees (returned) */
+    double deg, min, sec, sign;
+    char *value, *c1;
+
+    dec = 0.0;
+
+    /* Translate value from ASCII colon-delimited string to binary */
+    if (!in[0])
+	return (dec);
+    else
+	value = in;
+
+    /* Set sign */
+    if (!strchr (value,'-'))
+	sign = 1.0;
+    else {
+	sign = -1.0;
+	value = strchr (value,'-') + 1;
+	}
+
+    /* Translate value from ASCII colon-delimited string to binary */
+    if ((c1 = strchr (value,':')) != NULL) {
+	*c1 = 0;
+	deg = (double) atoi (value);
+	*c1 = ':';
+	value = c1 + 1;
+	if ((c1 = strchr (value,':')) != NULL) {
+	    *c1 = 0;
+	    min = (double) atoi (value);
+	    *c1 = ':';
+	    value = c1 + 1;
+	    sec = atof (value);
+	    }
+	else {
+	    sec = 0.0;
+	    if ((c1 = strchr (value,'.')) != NULL)
+		min = atof (value);
+	    if (strlen (value) > 0)
+		min = (double) atoi (value);
+	    }
+	dec = sign * (deg + (min / 60.0) + (sec / 3600.0));
+	}
+    
+    /* Translate value from dd.mmssss */
+    else if ((c1 = strchr (value,'.')) != NULL) {
+	double xnum, deg, min, sec;
+	xnum = atof (value);
+	deg = (double)((int) (xnum + 0.000000001));
+	xnum = (xnum - deg) * 100.0;
+	min = (double)((int) (xnum + 0.000000001));
+	sec = (xnum - min) * 100.0;
+	dec = sign * (deg + (min / 60.0) + (sec / 3600.0));
+	}
+
+    /* Translate integer */
+    else 
+	dec = sign * (double) atoi (value);
+
+    return (dec);
+}
+
+/* Oct 16 1998	New subroutines
+ * Oct 20 1998	Clean up error messages
+ * Oct 26 1998	Return object names in catread() and catrnum()
+ * Oct 29 1998	Correctly assign numbers when too many stars are found
+ * Oct 30 1998	Fix epoch and equinox for J2000
+ * Nov  9 1998	Drop out of star loop if rasorted catalog and past max RA
+ * Dec  8 1998	Do not declare catsize() static
+ * Dec 21 1998	Fix parsing so first character of line is not dropped
+
+ * Jan 20 1999	Use strchr() instead of strsrch() for single char searches
+ * Jan 29 1999	Default to star id number present
+ * Feb  1 1999	Rewrite tokenizing subroutines for clarity
+ * Feb  1 1999	Add match argument to catrnum()
+ * Feb  2 1999	Add code to count decimal places in numbers
+ * Feb  2 1999	Set sysout, eqout, and epout in catrnum() if not set
+ * Feb 10 1999	Implement per star coordinate system
+ * Feb 11 1999	Change starcat.insys to starcat.coorsys for consistency
+ * Feb 17 1999	Fix per star coordinate system bugs
+ * May 20 1999	Add option to read epoch of coordinates
+ * Jun 16 1999	Use SearchLim()
+ * Aug 16 1999	Fix bug to fix failure to search across 0:00 RA
+ * Aug 25 1999	Return real number of stars from catread()
+ * Sep 13 1999	Use these subroutines for general catalog access
+ * Sep 16 1999	Fix bug which didn't always return closest stars
+ * Sep 16 1999	Add distsort argument so brightest stars in circle works, too
+ * Oct  5 1999	Move token subroutines to catutil.c
+ * Oct 15 1999	Fix calls to catclose(); eliminate dno in catstar()
+ * Oct 22 1999	Fix declarations after lint
+ * Oct 22 1999	Rename subroutines from cat* to ctg* to avoid system conflict
+ * Nov 16 1999	Transfer dec degree sign if table format
+ *
+ * Jan 10 2000	Add second magnitude to tabread() and tabrnum()
+ * Mar 10 2000	Add proper motions to ctgread(), ctgrnum(), tabread(), tabrnum()
+ * Mar 15 2000	Add proper motions to binread(), binrnum(), actread(), actrnum()
+ * Apr  3 2000	Implement fractional degrees (/d) for positions
+ * Apr  3 2000	Add /i option to ignore stuff at end of line
+ * Apr  3 2000	Ignore leading # on first two lines of ASCII catalog file
+ * May 20 2000	Add Tycho 2 catalog support
+ * Jun 23 2000	Ignore any line with # as first character, not just heading
+ * Jun 26 2000	Implement /X catalog flag to avoid conversions
+ * Jun 26 2000	Add coordinate system to SearchLim() arguments
+ * Jun 26 2000	Ignore lines starting with # when counting stars in catalog
+ * Jul 12 2000	Add star catalog data structure to ctgread() argument list
+ * Jul 25 2000	Pass star catalog address of data structure address
+ * Sep 20 2000	Implement multiple catalog magnitudes; return only first 2
+ * Sep 20 2000	Add isacat() subroutine to detect ASCII catalogs
+ * Sep 25 2000	Add spectral type with flag /s
+ * Oct 17 2000	Add missing arguments in generic binrnum() call
+ * Oct 24 2000	Use ujcread() and ujcrnum() for USNO plate catalogs
+ * Nov 21 2000	Add WEBCAT as tab catalog results returned from the Web
+ * Nov 28 2000	Add starcat structure to *rnum() calls
+ * Dec 18 2000	Include math.h for sqrt()
+ *
+ * Feb 14 2001	Search all around RA if either pole is included
+ * Feb 16 2001	Update comments
+ * May 22 2001	Add GSC-ACT catalog; pass refcat to gscread() and gscrnum()
+ * May 23 2001	Add 2MASS Point Source Catalog
+ * May 29 2001	Save length of star i.d. number in ctgopen()
+ * Jun 18 2001	Add maximum length of returned string to getoken(), nextoken()
+ * Jun 20 2001	Add GSC II to ctgread() and ctgrnum()
+ * Aug  8 2001	Add /v option to return radial velocity
+ * Aug  8 2001	Add /f option for hhmmss.s ddmmss.s coordinates
+ * Aug 24 2001	Use STNUM < 0 instead of STNUM ==5 for object name not number
+ * Sep 11 2001	Allow an arbitrary number of magnitudes
+ * Sep 11 2001	Add sort magnitude as argument to *read() subroutines
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ * Sep 27 2001	Fix bug which reset number of magnitudes to 1 if /m not last
+ * Oct 17 2001	Fix argument sequence bug in ty2read() call
+ * Dec 11 2001	Set magsort which was unitialized in ctgread()
+ *
+ * Jan 31 2002	Always return NULL for object if no object name in catalog
+ * Mar 12 2002	Add /a flag for positions in radians
+ * May  6 2002	Allow object names to be up to 79 characters
+ * Aug  6 2002	Change keymag to avector of strings
+ * Oct 29 2002	Change UZC format to /u; add /f as fractional hours and degrees
+ * Oct 30 2002	Read epoch as MJD or ISO data+time as well as yyyy.ddmm for /y
+ *
+ * Jan 16 2003	Add USNO-B1.0 Catalog
+ * Mar 11 2003	Improve position filtering
+ * Apr  3 2003	Drop call to gsc2rnum(); it didn't do anything anyway
+ * Apr 23 2003	Add ucacread() and ucacrnum()
+ * May 21 2003	Pass catalog names for UCAC and 2MASS PSC
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Sep 25 2003	Add ctgbin() to fill an image with sources
+ * Nov 18 2003	Initialize image size and bits/pixel from header in ctgbin()
+ * Dec  3 2003	Add filename to gsc2read() call; add USNO YB6 Catalog
+ * Dec 12 2003	Fix bug in wcs2pix() call in ctgbin(); fix ujcread() call
+ *
+ * Jan  5 2004	Add SDSS catalog to ctgread()
+ * Jan 12 2004	Add 2MASS Extended Source Catalog to ctgread() and ctgrnum()
+ * Apr 23 2004	Add ctgrdate() to read by date range
+ * Apr 23 2004	Fix bug in ctgrnum() to index all returns on nstar, not jnum
+ * Nov  5 2004	Finish implementing proper motion in ASCII catalogs
+ *
+ * Jan 18 2005	Fix bug dealing with negative declinations in ASCII table format
+ * Aug  5 2005	Add additional catalog codes for Tycho-2 and 2MASS w/mag errs
+ * Jun  6 2006	Add SKY2000 catalog
+ * Jun 20 2006	Drop unused variables
+ * Jun 30 2006	Add match argument to tabrnum() to enable sequential reads
+ * Nov  6 2006	Add object name to sdssread() call to deal with long IDs
+ * Nov 15 2006	Fix binning
+ * Nov 16 2006	Return -1 for SDSS and GSC2; binning subroutines do not exist
+ *
+ * Jan  9 2007	Fix reference to refcat code  in wcscat.h
+ * Jan  9 2007	Drop catfile from call to sdssread()
+ * Mar 13 2007	Add object name array tobj to gsc2read() call
+ * Jul 13 2007	Add skybotread() for SkyBot solar system object search
+ *
+ * Aug 27 2009	Add /k option for fractional hours of RA and degrees of Dec
+ * Sep 30 2009	Add UCAC3
+ */
diff --git a/Code/src/libwcs/daoread.c b/Code/src/libwcs/daoread.c
new file mode 100644
index 0000000000000000000000000000000000000000..703b0ef91c6cea8b7861e54f2105c4ac5cfdd792
--- /dev/null
+++ b/Code/src/libwcs/daoread.c
@@ -0,0 +1,211 @@
+/*** File libwcs/daoread.c
+ *** January 11, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+static int nlines;	/* Number of lines in catalog */
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+
+static char newline = 10;
+static char *daobuff;
+
+/* DAOREAD -- Read DAOFIND file of star positions in an image */
+
+int
+daoread (daocat, xa, ya, ba, pa, nlog)
+
+char	*daocat;	/* Name of DAOFIND catalog file */
+double	**xa, **ya;	/* X and Y coordinates of stars, array returned */
+double	**ba;		/* Instrumental magnitudes of stars, array returned */
+int	**pa;		/* Peak counts of stars in counts, array returned */
+int	nlog;		/* 1 to print each star's position */
+{
+    int nstars;
+    double xi, yi, magi;
+    double flux;
+    int iline;
+    char *line;
+
+    line = 0;
+    iline = 0;
+    nstars = 0;
+
+    if (daoopen (daocat) > 0) {
+	line = daobuff;
+
+    /* Loop through catalog */
+	for (iline = 1; iline <= nlines; iline++) {
+	    line = daoline (iline, line);
+	    if (line == NULL) {
+		fprintf (stderr,"DAOREAD: Cannot read line %d\n", iline);
+		break;
+		}
+	    else if (line[0] != '#') {
+
+		/* Extract X, Y, magnitude  */
+		sscanf (line,"%lg %lg %lg", &xi, &yi, &magi);
+
+		/* Save star position, scaled flux, and magnitude in table */
+		nstars++;
+		*xa= (double *) realloc(*xa, nstars*sizeof(double));
+		*ya= (double *) realloc(*ya, nstars*sizeof(double));
+		*ba= (double *) realloc(*ba, nstars*sizeof(double));
+		*pa= (int *) realloc(*pa, nstars*sizeof(int));
+		(*xa)[nstars-1] = xi;
+		(*ya)[nstars-1] = yi;
+		(*ba)[nstars-1] = magi;
+		flux = pow (10.0, (-magi / 2.5));
+		(*pa)[nstars-1] = (int) flux;
+
+		if (nlog == 1)
+		    fprintf (stderr,"DAOREAD: %6d: %9.5f %9.5f %15.4f %6.2f\n",
+			   nstars,xi,yi,flux,magi);
+		}
+
+	    /* Log operation */
+	    if (nlog > 0 && iline%nlog == 0)
+		fprintf (stderr,"DAOREAD: %5d / %5d / %5d stars from catalog %s\r",
+			nstars, iline, nlines, daocat);
+
+	    /* End of star loop */
+	    }
+
+	/* End of open catalog file */
+	}
+
+/* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"DAOREAD: Catalog %s : %d / %d / %d found\n",
+		 daocat, nstars, iline, nlines);
+
+    free (daobuff);
+
+    return (nstars);
+}
+
+
+/* DAOOPEN -- Open DAOFIND catalog, returning number of entries */
+
+int
+daoopen (daofile)
+
+char *daofile;	/* DAOFIND catalog file name */
+{
+    FILE *fcat;
+    int nr, lfile;
+    char *daonew;
+    
+/* Find length of DAOFIND catalog */
+    lfile = getfilesize (daofile);
+    if (lfile < 2) {
+	fprintf (stderr,"DAOOPEN: DAOFIND catalog %s has no entries\n",daofile);
+	return (0);
+	}
+
+/* Open DAOFIND catalog */
+    if (!(fcat = fopen (daofile, "r"))) {
+	fprintf (stderr,"DAOOPEN: DAOFIND catalog %s cannot be read\n",daofile);
+	return (0);
+	}
+
+/* Allocate buffer to hold entire catalog and read it */
+    if ((daobuff = malloc (lfile)) != NULL) {
+	nr = fread (daobuff, 1, lfile, fcat);
+	if (nr < lfile) {
+	    fprintf (stderr,"DAOOPEN: read only %d / %d bytes of file %s\n",
+		     nr, lfile, daofile);
+	    (void) fclose (fcat);
+	    return (0);
+	    }
+
+    /* Enumerate entries in DAOFIND catalog by counting newlines */
+	daonew = daobuff;
+	nlines = 0;
+	while ((daonew = strchr (daonew, newline)) != NULL) {
+	    daonew = daonew + 1;
+	    nlines = nlines + 1;
+	    }
+	}
+
+    (void) fclose (fcat);
+    return (nlines);
+}
+
+
+/* DAOLINE -- Get DAOFIND catalog entry for one star; return 0 if successful */
+
+char *
+daoline (iline, line)
+
+int iline;	/* Star sequence number in DAOFIND catalog */
+char *line;	/* Pointer to iline'th entry (returned updated) */
+{
+    char *nextline;
+    int i;
+
+    if (iline > nlines) {
+	fprintf (stderr, "DAOSTAR:  %d is not in catalog\n",iline);
+	return (NULL);
+	}
+    else if (iline < 1 && line) {
+	nextline = strchr (line, newline) + 1;
+	}
+    else {
+	nextline = daobuff;
+	for (i = 1; i < iline; i++) {
+	    nextline = strchr (nextline, newline) + 1;
+	    }
+	}
+
+    return (nextline);
+}
+
+/* Dec 11 1996	New subroutines
+ *
+ * Mar 20 1997	Removed unused variables, fixed logging after lint
+ *
+ * Jul 20 2001	Return magnitude as well as flux
+ *
+ * May 27 2003	Use getfilesize() to get length of file
+ *
+ * Aug  3 2004	Move daoopen() and daoline() declarations to wcscat.h
+ * Aug 30 2004	Include fitsfile.h
+ *
+ * Jun 19 2006	Initialized uninitialized  variable iline
+ *
+ * Jan 10 2007	Include wcs.h
+ * Jan 11 2007	Include fitsfile.h
+ */
diff --git a/Code/src/libwcs/dateutil.c b/Code/src/libwcs/dateutil.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a6772de68283eb4f5c4100b01025a8c0f250d50
--- /dev/null
+++ b/Code/src/libwcs/dateutil.c
@@ -0,0 +1,4457 @@
+/*** File libwcs/dateutil.c
+ *** September 24, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1999-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* Date and time conversion routines using the following conventions:
+  ang = Angle in fractional degrees
+  deg = Angle in degrees as dd:mm:ss.ss
+  doy = 2 floating point numbers: year and day, including fraction, of year
+	*** First day of year is 1, not zero.
+   dt = 2 floating point numbers: yyyy.mmdd, hh.mmssssss
+   ep = fractional year, often epoch of a position including proper motion
+  epb = Besselian epoch = 365.242198781-day years based on 1900.0
+  epj = Julian epoch = 365.25-day years based on 2000.0
+   fd = FITS date string which may be any of the following:
+	yyyy.ffff (fractional year)
+	dd/mm/yy (FITS standard before 2000)
+	dd-mm-yy (nonstandard FITS use before 2000)
+	yyyy-mm-dd (FITS standard after 1999)
+	yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999)
+   hr = Sexigesimal hours as hh:mm:dd.ss
+   jd = Julian Date
+   lt = Local time
+  mjd = modified Julian Date = JD - 2400000.5
+  ofd = FITS date string (dd/mm/yy before 2000, else no return)
+ time = use fd2* with no date to convert time as hh:mm:ss.ss to sec, day, year
+   ts = UT seconds since 1950-01-01T00:00 (used for ephemeris computations)
+  tsi = local seconds since 1980-01-01T00:00 (used by IRAF as a time tag)
+  tsu = UT seconds since 1970-01-01T00:00 (used as Unix system time)
+  tsd = UT seconds of current day
+   ut = Universal Time (UTC)
+   et = Ephemeris Time (or TDB or TT)
+  mst = Mean Greenwich Sidereal Time
+  gst = Greenwich Sidereal Time (includes nutation)
+  lst = Local Sidereal Time (includes nutation) (longitude must be set)
+  hjd = Heliocentric Julian Date
+ mhjd = modified Heliocentric Julian Date = HJD - 2400000.5
+
+ * ang2hr (angle)
+ *	Convert angle in decimal floating point degrees to hours as hh:mm:ss.ss
+ * ang2deg (angle)
+ *	Convert angle in decimal floating point degrees to degrees as dd:mm:ss.ss
+ * deg2ang (angle as dd:mm:ss.ss)
+ *	Convert angle in degrees as dd:mm:ss.ss to decimal floating point degrees
+ * ang2hr (angle)
+ *	Convert angle in hours as hh:mm:ss.ss to decimal floating point degrees
+ *
+ * doy2dt (year, doy, date, time)
+ *	Convert year and day of year to date as yyyy.ddmm and time as hh.mmsss
+ * doy2ep, doy2epb, doy2epj (date, time)
+ *	Convert year and day of year to fractional year
+ * doy2fd (year, doy)
+ *	Convert year and day of year to FITS date string
+ * doy2mjd (year, doy)
+ *	Convert year and day of year to modified Julian date
+ *
+ * dt2doy (date, time, year, doy)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to year and day of year
+ * dt2ep, dt2epb, dt2epj (date, time)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to fractional year
+ * dt2fd (date, time)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to FITS date string
+ * dt2i (date,time,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert yyyy.mmdd hh.mmssss to year month day hours minutes seconds
+ * dt2jd (date,time)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to Julian date
+ * dt2mjd (date,time)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to modified Julian date
+ * dt2ts (date,time)
+ *	Convert date (yyyy.ddmm) and time (hh.mmsss) to seconds since 1950-01-01
+ * dt2tsi (date,time)
+ *	Convert date (yyyy.ddmm) and time (hh.mmsss) to seconds since 1980-01-01
+ * dt2tsu (date,time)
+ *	Convert date (yyyy.ddmm) and time (hh.mmsss) to seconds since 1970-01-01
+ *
+ * ep2dt, epb2dt, epj2dt (epoch,date, time)
+ *	Convert fractional year to date as yyyy.ddmm and time as hh.mmsss
+ * ep2fd, epb2fd, epj2fd (epoch)
+ *	Convert epoch to FITS ISO date string
+ * ep2i, epb2i, epj2i (epoch,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert fractional year to year month day hours minutes seconds
+ * ep2jd, epb2jd, epj2jd (epoch)
+ *	Convert fractional year as used in epoch to Julian date
+ * ep2mjd, epb2mjd, epj2mjd (epoch)
+ *	Convert fractional year as used in epoch to modified Julian date
+ * ep2ts, epb2ts, epj2ts (epoch)
+ *	Convert fractional year to seconds since 1950.0
+ *
+ * et2fd (string)
+ *	Convert from ET (or TDT or TT) in FITS format to UT in FITS format
+ * fd2et (string)
+ *	Convert from UT in FITS format to ET (or TDT or TT) in FITS format
+ * jd2jed (dj)
+ *	Convert from Julian Date to Julian Ephemeris Date
+ * jed2jd (dj)
+ *	Convert from Julian Ephemeris Date to Julian Date
+ * dt2et (date, time)
+ *	Convert date (yyyy.ddmm) and time (hh.mmsss) to ephemeris time
+ * edt2dt (date, time)
+ *	Convert ephemeris date (yyyy.ddmm) and time (hh.mmsss) to UT
+ * ts2ets (tsec)
+ *	Convert from UT in seconds since 1950-01-01 to ET in same format
+ * ets2ts (tsec)
+ *	Convert from ET in seconds since 1950-01-01 to UT in same format
+ *
+ * fd2ep, fd2epb, fd2epj (string)
+ *	Convert FITS date string to fractional year
+ *	Convert time alone to fraction of Besselian year
+ * fd2doy (string, year, doy)
+ *	Convert FITS standard date string to year and day of year
+ * fd2dt (string, date, time)
+ *	Convert FITS date string to date as yyyy.ddmm and time as hh.mmsss
+ *	Convert time alone to hh.mmssss with date set to 0.0
+ * fd2i (string,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert FITS standard date string to year month day hours min sec
+ *	Convert time alone to hours min sec, year month day are zero
+ * fd2jd (string)
+ *	Convert FITS standard date string to Julian date
+ *	Convert time alone to fraction of day
+ * fd2mjd (string)
+ *	Convert FITS standard date string to modified Julian date
+ * fd2ts (string)
+ *	Convert FITS standard date string to seconds since 1950.0
+ *	Convert time alone to seconds of day
+ * fd2fd (string)
+ *	Convert FITS standard date string to ISO FITS date string
+ * fd2of (string)
+ *	Convert FITS standard date string to old-format FITS date and time
+ * fd2ofd (string)
+ *	Convert FITS standard date string to old-format FITS date string
+ * fd2oft (string)
+ *	Convert time part of FITS standard date string to FITS date string
+ *
+ * jd2doy (dj, year, doy)
+ *	Convert Julian date to year and day of year
+ * jd2dt (dj,date,time)
+ *	Convert Julian date to date as yyyy.mmdd and time as hh.mmssss
+ * jd2ep, jd2epb, jd2epj (dj)
+ *	Convert Julian date to fractional year as used in epoch
+ * jd2fd (dj)
+ *	Convert Julian date to FITS ISO date string
+ * jd2i (dj,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert Julian date to year month day hours min sec
+ * jd2mjd (dj)
+ *	Convert Julian date to modified Julian date
+ * jd2ts (dj)
+ *	Convert Julian day to seconds since 1950.0
+ *
+ * lt2dt()
+ *	Return local time as yyyy.mmdd and time as hh.mmssss
+ * lt2fd()
+ *	Return local time as FITS ISO date string
+ * lt2tsi()
+ *	Return local time as IRAF seconds since 1980-01-01 00:00
+ * lt2tsu()
+ *	Return local time as Unix seconds since 1970-01-01 00:00
+ * lt2ts()
+ *	Return local time as Unix seconds since 1950-01-01 00:00
+ *
+ * mjd2doy (dj,year,doy)
+ *	Convert modified Julian date to date as year and day of year
+ * mjd2dt (dj,date,time)
+ *	Convert modified Julian date to date as yyyy.mmdd and time as hh.mmssss
+ * mjd2ep, mjd2epb, mjd2epj (dj)
+ *	Convert modified Julian date to fractional year as used in epoch
+ * mjd2fd (dj)
+ *	Convert modified Julian date to FITS ISO date string
+ * mjd2i (dj,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert modified Julian date to year month day hours min sec
+ * mjd2jd (dj)
+ *	Convert modified Julian date to Julian date
+ * mjd2ts (dj)
+ *	Convert modified Julian day to seconds since 1950.0
+ *
+ * ts2dt (tsec,date,time)
+ *	Convert seconds since 1950.0 to date as yyyy.ddmm and time as hh.mmsss
+ * ts2ep, ts2epb, ts2epj (tsec)
+ *	Convert seconds since 1950.0 to fractional year
+ * ts2fd (tsec)
+ *	Convert seconds since 1950.0 to FITS standard date string
+ * ts2i (tsec,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert sec since 1950.0 to year month day hours minutes seconds
+ * ts2jd (tsec)
+ *	Convert seconds since 1950.0 to Julian date
+ * ts2mjd (tsec)
+ *	Convert seconds since 1950.0 to modified Julian date
+ * tsi2fd (tsec)
+ *	Convert seconds since 1980-01-01 to FITS standard date string
+ * tsi2dt (tsec,date,time)
+ *	Convert seconds since 1980-01-01 to date as yyyy.ddmm, time as hh.mmsss
+ * tsu2fd (tsec)
+ *	Convert seconds since 1970-01-01 to FITS standard date string
+ * tsu2tsi (tsec)
+ *	Convert UT seconds since 1970-01-01 to local seconds since 1980-01-01
+ * tsu2dt (tsec,date,time)
+ *	Convert seconds since 1970-01-01 to date as yyyy.ddmm, time as hh.mmsss
+ *
+ * tsd2fd (tsec)
+ *	Convert seconds since start of day to FITS time, hh:mm:ss.ss
+ * tsd2dt (tsec)
+ *	Convert seconds since start of day to hh.mmssss
+ *
+ * fd2gst (string)
+ *      convert from FITS date Greenwich Sidereal Time
+ * dt2gst (date, time)
+ *      convert from UT as yyyy.mmdd hh.mmssss to Greenwich Sidereal Time
+ * ts2gst (tsec)
+ *      Calculate Greenwich Sidereal Time given Universal Time
+ *          in seconds since 1951-01-01T0:00:00
+ * fd2mst (string)
+ *      convert from FITS UT date to Mean Sidereal Time
+ * dt2gmt (date, time)
+ *      convert from UT as yyyy.mmdd hh.mmssss to Mean Sidereal Time
+ * ts2mst (tsec)
+ *      Calculate Mean Sidereal Time given Universal Time
+ *          in seconds since 1951-01-01T0:00:00
+ * jd2mst (string)
+ *      convert from Julian Date to Mean Sidereal Time
+ * mst2fd (string)
+ *	convert to current UT in FITS format given Greenwich Mean Sidereal Time
+ * mst2jd (dj)
+ *	convert to current UT as Julian Date given Greenwich Mean Sidereal Time
+ * jd2lst (dj)
+ *	Calculate Local Sidereal Time from Julian Date
+ * ts2lst (tsec)
+ *	Calculate Local Sidereal Time given UT in seconds since 1951-01-01T0:00
+ * fd2lst (string)
+ *	Calculate Local Sidereal Time given Universal Time as FITS ISO date
+ * lst2jd (dj, lst)
+ *	Calculate Julian Date given current Julian date and Local Sidereal Time
+ * lst2fd (string, lst)
+ *	Calculate Julian Date given current UT date and Local Sidereal Time
+ * gst2fd (string)
+ * 	Calculate current UT given UT date and Greenwich Sidereal Time
+ * gst2jd (dj)
+ * 	Calculate current UT given UT date and Greenwich Sidereal Time as JD
+ *
+ * compnut (dj, dpsi, deps, eps0)
+ *      Compute the longitude and obliquity components of nutation and
+ *      mean obliquity from the IAU 1980 theory
+ *
+ * utdt (dj)
+ *	Compute difference between UT and dynamical time (ET-UT)
+ * ut2dt (year, doy)
+ *	Current Universal Time to year and day of year
+ * ut2dt (date, time)
+ *	Current Universal Time to date (yyyy.mmdd) and time (hh.mmsss)
+ * ut2ep(), ut2epb(), ut2epj()
+ *	Current Universal Time to fractional year, Besselian, Julian epoch
+ * ut2fd()
+ *	Current Universal Time to FITS ISO date string
+ * ut2jd()
+ *	Current Universal Time to Julian Date
+ * ut2mjd()
+ *	Current Universal Time to Modified Julian Date
+ * ut2tsi()
+ *	Current Universal Time to IRAF seconds since 1980-01-01T00:00
+ * ut2tsu()
+ *	Current Universal Time to Unix seconds since 1970-01-01T00:00
+ * ut2ts()
+ *	Current Universal Time to seconds since 1950-01-01T00:00
+ * isdate (string)
+ *	Return 1 if string is a FITS date (old or ISO)
+ *
+ * Internally-used subroutines
+ *
+ * fixdate (iyr, imon, iday, ihr, imn, sec, ndsec)
+ *	Round seconds and make sure date and time numbers are within limits
+ * caldays (year, month)
+ *	Calculate days in month 1-12 given year (Gregorian calendar only
+ * dint (dnum)
+ *	Return integer part of floating point number
+ * dmod (dnum)
+ *	Return Mod of floating point number
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <sys/time.h>
+#include "wcs.h"
+#include "fitsfile.h"
+
+static double suntl();
+static void fixdate();
+static int caldays();
+static double dint();
+static double dmod();
+
+static double longitude = 0.0;	/* longitude of observatory in degrees (+=west) */
+void
+setlongitude (longitude0)
+double longitude0;
+{ longitude = longitude0; return; }
+
+static int ndec = 3;
+void
+setdatedec (nd)
+int nd;
+{ ndec = nd; return; }
+
+/* ANG2HR -- Convert angle in fraction degrees to hours as hh:mm:ss.ss */
+
+void
+ang2hr (angle, lstr, string)
+
+double	angle;	/* Angle in fractional degrees */
+int	lstr;	/* Maximum number of characters in string */
+char	*string; /* Character string (hh:mm:ss.ss returned) */
+
+{
+    angle = angle / 15.0;
+    dec2str (string, lstr, angle, ndec);
+    return;
+}
+
+
+/* ANG2DEG -- Convert angle in fraction degrees to degrees as dd:mm:ss.ss */
+
+void
+ang2deg (angle, lstr, string)
+
+double	angle;	/* Angle in fractional degrees */
+int	lstr;	/* Maximum number of characters in string */
+char	*string; /* Character string (dd:mm:ss.ss returned) */
+{
+    dec2str (string, lstr, angle, ndec);
+    return;
+}
+
+
+/* DEG2ANG -- Convert angle in degrees as dd:mm:ss.ss to fractional degrees */
+
+double
+deg2ang (angle)
+
+char *angle;	/* Angle as dd:mm:ss.ss */
+{
+    double deg;
+
+    deg = str2dec (angle);
+    return (deg);
+}
+
+/* HR2ANG -- Convert angle in hours as hh:mm:ss.ss to fractional degrees */
+
+double
+hr2ang (angle)
+
+char *angle;	/* Angle in sexigesimal hours (hh:mm:ss.sss) */
+
+{
+    double deg;
+
+    deg = str2dec (angle);
+    deg = deg * 15.0;
+    return (deg);
+}
+
+
+/* DT2FD-- convert vigesimal date and time to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+dt2fd (date, time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+    int nf;
+    char *string;
+    char tstring[32], dstring[32];
+    char outform[64];
+
+    dt2i (date, time, &iyr,&imon,&iday,&ihr,&imn,&sec, ndec);
+
+    /* Convert to ISO date format */
+    string = (char *) calloc (32, sizeof (char));
+
+    /* Make time string */
+    if (time != 0.0 || ndec > 0) {
+	if (ndec == 0)
+	    nf = 2;
+	else
+	    nf = 3 + ndec;
+	if (ndec > 0) {
+	    sprintf (outform, "%%02d:%%02d:%%0%d.%df", nf, ndec);
+	    sprintf (tstring, outform, ihr, imn, sec);
+	    }
+	else {
+	    sprintf (outform, "%%02d:%%02d:%%0%dd", nf);
+	    sprintf (tstring, outform, ihr, imn, (int)(sec+0.5));
+	    }
+	}
+
+    /* Make date string */
+    if (date != 0.0)
+	sprintf (dstring, "%4d-%02d-%02d", iyr, imon, iday);
+
+    /* Make FITS (ISO) date string */
+    if (date == 0.0)
+	strcpy (string, tstring);
+    else if (time == 0.0 && ndec < 1)
+	strcpy (string, dstring);
+    else
+	sprintf (string, "%sT%s", dstring, tstring);
+
+    return (string);
+}
+
+
+/* DT2JD-- convert from date as yyyy.mmdd and time as hh.mmsss to Julian Date
+ *	   Return fractional days if date is zero */
+
+double
+dt2jd (date,time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date (returned) */
+    double tsec;	/* seconds since 1950.0 */
+
+    tsec = dt2ts (date, time);
+    if (date == 0.0)
+	dj = tsec / 86400.0;
+    else
+	dj = ts2jd (tsec);
+
+    return (dj);
+}
+
+
+/* DT2MJD-- convert from date yyyy.mmdd time hh.mmsss to modified Julian Date
+ *	   Return fractional days if date is zero */
+
+double
+dt2mjd (date,time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Modified Julian date (returned) */
+    double tsec;	/* seconds since 1950.0 */
+
+    tsec = dt2ts (date, time);
+    if (date == 0.0)
+	dj = tsec / 86400.0;
+    else
+	dj = ts2jd (tsec);
+
+    return (dj - 2400000.5);
+}
+
+
+/* HJD2JD-- convert  Heliocentric Julian Date to (geocentric) Julian date */
+
+double
+hjd2jd (dj, ra, dec, sys)
+
+double	dj;	/* Heliocentric Julian date */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double lt;		/* Light travel difference to the Sun (days) */
+
+    lt = suntl (dj, ra, dec, sys);
+
+    /* Return Heliocentric Julian Date */
+    return (dj - lt);
+}
+
+
+/* JD2HJD-- convert (geocentric) Julian date to Heliocentric Julian Date */
+
+double
+jd2hjd (dj, ra, dec, sys)
+
+double	dj;	/* Julian date (geocentric) */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double lt;		/* Light travel difference to the Sun (days) */
+
+    lt = suntl (dj, ra, dec, sys);
+
+    /* Return Heliocentric Julian Date */
+    return (dj + lt);
+}
+
+
+/* MHJD2MJD-- convert modified Heliocentric Julian Date to
+	      modified geocentric Julian date */
+
+double
+mhjd2mjd (mhjd, ra, dec, sys)
+
+double	mhjd;	/* Modified Heliocentric Julian date */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double lt;		/* Light travel difference to the Sun (days) */
+    double hjd;		/* Heliocentric Julian date */
+
+    hjd = mjd2jd (mhjd);
+
+    lt = suntl (hjd, ra, dec, sys);
+
+    /* Return Heliocentric Julian Date */
+    return (jd2mjd (hjd - lt));
+}
+
+
+/* MJD2MHJD-- convert modified geocentric Julian date tp
+	      modified Heliocentric Julian Date */
+
+double
+mjd2mhjd (mjd, ra, dec, sys)
+
+double	mjd;	/* Julian date (geocentric) */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double lt;		/* Light travel difference to the Sun (days) */
+    double	dj;	/* Julian date (geocentric) */
+
+    dj = mjd2jd (mjd);
+
+    lt = suntl (dj, ra, dec, sys);
+
+    /* Return Heliocentric Julian Date */
+    return (jd2mjd (dj + lt));
+}
+
+
+/* SUNTL-- compute light travel time to heliocentric correction in days */
+/* Translated into C from IRAF SPP noao.astutils.asttools.asthjd.x */
+
+static double
+suntl (dj, ra, dec, sys)
+
+double	dj;	/* Julian date (geocentric) */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double t;		/* Number of Julian centuries since J1900 */
+    double manom;	/* Mean anomaly of the Earth's orbit (degrees) */
+    double lperi;	/* Mean longitude of perihelion (degrees) */
+    double oblq;	/* Mean obliquity of the ecliptic (degrees) */
+    double eccen;	/* Eccentricity of the Earth's orbit (dimensionless) */
+    double eccen2, eccen3;
+    double tanom;	/* True anomaly (approximate formula) (radians) */
+    double slong;	/* True longitude of the Sun from the Earth (radians) */
+    double rs;		/* Distance to the sun (AU) */
+    double lt;		/* Light travel difference to the Sun (days) */
+    double l;		/* Longitude of star in orbital plane of Earth (radians) */
+    double b;		/* Latitude of star in orbital plane of Earth (radians) */
+    double epoch;	/* Epoch of obervation */
+    double rs1,rs2;
+
+    t = (dj - 2415020.0) / 36525.0;
+
+    /* Compute earth orbital parameters */
+    manom = 358.47583 + (t * (35999.04975 - t * (0.000150 + t * 0.000003)));
+    lperi = 101.22083 + (t * (1.7191733 + t * (0.000453 + t * 0.000003)));
+    oblq = 23.452294 - (t * (0.0130125 + t * (0.00000164 - t * 0.000000503)));
+    eccen = 0.01675104 - (t * (0.00004180 + t * 0.000000126));
+    eccen2 = eccen * eccen;
+    eccen3 = eccen * eccen2;
+
+    /* Convert to principle angles */
+    manom = manom - (360.0 * (dint) (manom / 360.0));
+    lperi = lperi - (360.0 * (dint) (lperi / 360.0));
+
+    /* Convert to radians */
+    manom = degrad (manom);
+    lperi = degrad (lperi);
+    oblq = degrad (oblq);
+
+    /* True anomaly */
+    tanom = manom + (2 * eccen - 0.25 * eccen3) * sin (manom) +
+	    1.25 * eccen2 * sin (2 * manom) +
+	    13./12. * eccen3 * sin (3 * manom);
+
+    /* Distance to the Sun */
+    rs1 = 1.0 - eccen2;
+    rs2 = 1.0 + (eccen * cos (tanom));
+    rs = rs1 / rs2;
+
+    /* True longitude of the Sun seen from the Earth */
+    slong = lperi + tanom + PI;
+
+    /* Longitude and latitude of star in orbital plane of the Earth */
+    epoch = jd2ep (dj);
+    wcscon (sys, WCS_ECLIPTIC, 0.0, 0.0, &ra, &dec, epoch);
+    l = degrad (ra);
+    b = degrad (dec);
+
+    /* Light travel difference to the Sun */
+    lt = -0.005770 * rs * cos (b) * cos (l - slong);
+
+    /* Return light travel difference */
+    return (lt);
+}
+
+
+/* JD2DT-- convert Julian date to date as yyyy.mmdd and time as hh.mmssss */
+
+void
+jd2dt (dj,date,time)
+
+double	dj;	/* Julian date */
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    /* Convert Julian Date to date and time */
+    jd2i (dj, &iyr, &imon, &iday, &ihr, &imn, &sec, 4);
+
+    /* Convert date to yyyy.mmdd */
+    if (iyr < 0) {
+	*date = (double) (-iyr) + 0.01 * (double) imon + 0.0001 * (double) iday;
+	*date = -(*date);
+	}
+    else
+	*date = (double) iyr + 0.01 * (double) imon + 0.0001 * (double) iday;
+
+    /* Convert time to hh.mmssssss */
+    *time = (double) ihr + 0.01 * (double) imn + 0.0001 * sec;
+
+    return;
+}
+
+
+/* JD2I-- convert Julian date to date as year, month, and day, and time hours,
+          minutes, and seconds */
+/*        after Fliegel and Van Flander, CACM 11, 657 (1968) */
+
+
+void
+jd2i (dj, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	dj;	/* Julian date */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double tsec;
+    double frac, dts, ts, sday;
+    int jd, l, n, i, j;
+
+    tsec = jd2ts (dj);
+    /* ts2i (tsec, iyr, imon, iday, ihr, imn, sec, ndsec); */
+
+    /* Round seconds to 0 - 4 decimal places */
+    if (tsec < 0.0)
+	dts = -0.5;
+    else
+	dts = 0.5;
+    if (ndsec < 1)
+	ts = dint (tsec + dts);
+    else if (ndsec < 2)
+	ts = dint (tsec * 10.0 + dts) / 10.0;
+    else if (ndsec < 3)
+	ts = dint (tsec * 100.0 + dts) / 100.0;
+    else if (ndsec < 4)
+	ts = dint (tsec * 1000.0 + dts) / 1000.0;
+    else
+	ts = dint (tsec * 10000.0 + dts) / 10000.0;
+
+    /* Convert back to Julian Date */
+    dj = ts2jd (ts);
+
+    /* Compute time from fraction of a day */
+    frac = dmod (dj, 1.0);
+    if (frac < 0.5) {
+	jd = (int) (dj - frac);
+	sday = (frac + 0.5) * 86400.0;
+	}
+    else {
+	jd = (int) (dj - frac) + 1;
+	sday = (frac - 0.5) * 86400.0;
+	}
+    
+    *ihr = (int) (sday / 3600.0);
+    sday = sday - (double) (*ihr * 3600);
+    *imn = (int) (sday / 60.0);
+    *sec = sday - (double) (*imn * 60);
+
+    /* Compute day, month, year */
+    l = jd + 68569;
+    n = (4 * l) / 146097;
+    l = l - (146097 * n + 3) / 4;
+    i = (4000 * (l + 1)) / 1461001;
+    l = l - (1461 * i) / 4 + 31;
+    j = (80 * l) / 2447;
+    *iday = l - (2447 * j) / 80;
+    l = j / 11;
+    *imon = j + 2 - (12 * l);
+    *iyr = 100 * (n - 49) + i + l;
+
+    return;
+}
+
+
+/* JD2MJD-- convert Julian Date to Modified Julian Date */
+
+double
+jd2mjd (dj)
+
+double	dj;	/* Julian Date */
+
+{
+    return (dj - 2400000.5);
+}
+
+
+/* JD2EP-- convert Julian date to fractional year as used in epoch */
+
+double
+jd2ep (dj)
+
+double	dj;	/* Julian date */
+
+{
+    double date, time;
+    jd2dt (dj, &date, &time);
+    return (dt2ep (date, time));
+}
+
+
+/* JD2EPB-- convert Julian date to Besselian epoch */
+
+double
+jd2epb (dj)
+
+double	dj;	/* Julian date */
+
+{
+    return (1900.0 + (dj - 2415020.31352) / 365.242198781);
+}
+
+
+/* JD2EPJ-- convert Julian date to Julian epoch */
+
+double
+jd2epj (dj)
+
+double	dj;	/* Julian date */
+
+{
+    return (2000.0 + (dj - 2451545.0) / 365.25);
+}
+
+
+/* LT2DT-- Return local time as yyyy.mmdd and time as hh.mmssss */
+
+void
+lt2dt(date, time)
+
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+
+{
+    time_t tsec;
+    struct timeval tp;
+    struct timezone tzp;
+    struct tm *ts;
+
+    gettimeofday (&tp,&tzp);
+
+    tsec = tp.tv_sec;
+    ts = localtime (&tsec);
+
+    if (ts->tm_year < 1000)
+	*date = (double) (ts->tm_year + 1900);
+    else
+	*date = (double) ts->tm_year;
+    *date = *date + (0.01 * (double) (ts->tm_mon + 1));
+    *date = *date + (0.0001 * (double) ts->tm_mday);
+    *time = (double) ts->tm_hour;
+    *time = *time + (0.01 * (double) ts->tm_min);
+    *time = *time + (0.0001 * (double) ts->tm_sec);
+
+    return;
+}
+
+
+/* LT2FD-- Return current local time as FITS ISO date string */
+
+char *
+lt2fd()
+{
+    time_t tsec;
+    struct tm *ts;
+    struct timeval tp;
+    struct timezone tzp;
+    int month, day, year, hour, minute, second;
+    char *isotime;
+
+    gettimeofday (&tp,&tzp);
+    tsec = tp.tv_sec;
+
+    ts = localtime (&tsec);
+
+    year = ts->tm_year;
+    if (year < 1000)
+	year = year + 1900;
+    month = ts->tm_mon + 1;
+    day = ts->tm_mday;
+    hour = ts->tm_hour;
+    minute = ts->tm_min;
+    second = ts->tm_sec;
+
+    isotime = (char *) calloc (32, sizeof (char));
+    sprintf (isotime, "%04d-%02d-%02dT%02d:%02d:%02d",
+                      year, month, day, hour, minute, second);
+    return (isotime);
+}
+
+
+/* LT2TSI-- Return local time as IRAF seconds since 1980-01-01 00:00 */
+
+int
+lt2tsi()
+{
+    return ((int)(lt2ts() - 946684800.0));
+}
+
+
+/* LT2TSU-- Return local time as Unix seconds since 1970-01-01 00:00 */
+
+time_t
+lt2tsu()
+{
+    return ((time_t)(lt2ts() - 631152000.0));
+}
+
+/* LT2TS-- Return local time as Unix seconds since 1950-01-01 00:00 */
+
+double
+lt2ts()
+{
+    double tsec;
+    char *datestring;
+    datestring = lt2fd();
+    tsec = fd2ts (datestring);
+    free (datestring);
+    return (tsec);
+}
+
+
+/* MJD2DT-- convert Modified Julian Date to date (yyyy.mmdd) time (hh.mmssss) */
+
+void
+mjd2dt (dj,date,time)
+
+double	dj;	/* Modified Julian Date */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double tsec;
+
+    tsec = jd2ts (dj + 2400000.5);
+    ts2dt (tsec, date, time);
+
+    return;
+}
+
+
+/* MJD2I-- convert Modified Julian Date to date as year, month, day and
+           time as hours, minutes, seconds */
+
+void
+mjd2i (dj, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	dj;	/* Modified Julian Date */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double tsec;
+
+    tsec = jd2ts (dj + 2400000.5);
+    ts2i (tsec, iyr, imon, iday, ihr, imn, sec, ndsec);
+    return;
+}
+
+
+/* MJD2DOY-- convert Modified Julian Date to Year,Day-of-Year */
+
+void
+mjd2doy (dj, year, doy)
+
+double	dj;	/* Modified Julian Date */
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year with fraction (returned) */
+
+{
+    jd2doy (dj + 2400000.5, year, doy);
+    return;
+}
+
+
+/* MJD2JD-- convert Modified Julian Date to Julian Date */
+
+double
+mjd2jd (dj)
+
+double	dj;	/* Modified Julian Date */
+
+{
+    return (dj + 2400000.5);
+}
+
+
+/* MJD2EP-- convert Modified Julian Date to fractional year */
+
+double
+mjd2ep (dj)
+
+double	dj;	/* Modified Julian Date */
+
+{
+    double date, time;
+    jd2dt (dj + 2400000.5, &date, &time);
+    return (dt2ep (date, time));
+}
+
+
+/* MJD2EPB-- convert Modified Julian Date to Besselian epoch */
+
+double
+mjd2epb (dj)
+
+double	dj;	/* Modified Julian Date */
+
+{
+    return (1900.0 + (dj - 15019.81352) / 365.242198781);
+}
+
+
+/* MJD2EPJ-- convert Modified Julian Date to Julian epoch */
+
+double
+mjd2epj (dj)
+
+double	dj;	/* Modified Julian Date */
+
+{
+    return (2000.0 + (dj - 51544.5) / 365.25);
+}
+
+
+/* MJD2FD-- convert modified Julian date to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+mjd2fd (dj)
+
+double	dj;	/* Modified Julian date */
+{
+    return (jd2fd (dj + 2400000.5));
+}
+
+
+/* MJD2TS-- convert modified Julian date to seconds since 1950.0 */
+
+double
+mjd2ts (dj)
+
+double	dj;	/* Modified Julian date */
+{
+    return ((dj - 33282.0) * 86400.0);
+}
+
+
+/* EP2FD-- convert fractional year to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+ep2fd (epoch)
+
+double	epoch;	/* Date as fractional year */
+{
+    double tsec; /* seconds since 1950.0 (returned) */
+    tsec = ep2ts (epoch);
+    return (ts2fd (tsec));
+}
+
+
+/* EPB2FD-- convert Besselian epoch to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+epb2fd (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+{
+    double dj;		/* Julian Date */
+    dj = epb2jd (epoch);
+    return (jd2fd (dj));
+}
+
+
+/* EPJ2FD-- convert Julian epoch to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+epj2fd (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+{
+    double dj;		/* Julian Date */
+    dj = epj2jd (epoch);
+    return (jd2fd (dj));
+}
+
+
+/* EP2TS-- convert fractional year to seconds since 1950.0 */
+
+double
+ep2ts (epoch)
+
+double	epoch;	/* Date as fractional year */
+{
+    double dj;
+    dj = ep2jd (epoch);
+    return ((dj - 2433282.5) * 86400.0);
+}
+
+
+/* EPB2TS-- convert Besselian epoch to seconds since 1950.0 */
+
+double
+epb2ts (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+{
+    double dj;
+    dj = epb2jd (epoch);
+    return ((dj - 2433282.5) * 86400.0);
+}
+
+
+/* EPJ2TS-- convert Julian epoch to seconds since 1950.0 */
+
+double
+epj2ts (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+{
+    double dj;
+    dj = epj2jd (epoch);
+    return ((dj - 2433282.5) * 86400.0);
+}
+
+
+/* EPB2EP-- convert Besselian epoch to fractional years */
+
+double
+epb2ep (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+{
+    double dj;
+    dj = epb2jd (epoch);
+    return (jd2ep (dj));
+}
+
+
+/* EP2EPB-- convert fractional year to Besselian epoch */
+
+double
+ep2epb (epoch)
+
+double	epoch;	/* Fractional year */
+{
+    double dj;
+    dj = ep2jd (epoch);
+    return (jd2epb (dj));
+}
+
+
+/* EPJ2EP-- convert Julian epoch to fractional year */
+
+double
+epj2ep (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+{
+    double dj;
+    dj = epj2jd (epoch);
+    return (jd2ep (dj));
+}
+
+
+/* EP2EPJ-- convert fractional year to Julian epoch */
+
+double
+ep2epj (epoch)
+
+double	epoch;	/* Fractional year */
+{
+    double dj;
+    dj = ep2jd (epoch);
+    return (jd2epj (dj));
+}
+
+
+/* EP2I-- convert fractional year to year month day hours min sec */
+
+void
+ep2i (epoch, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	epoch;	/* Date as fractional year */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+{
+    double date, time;
+
+    ep2dt (epoch, &date, &time);
+    dt2i (date, time, iyr,imon,iday,ihr,imn,sec, ndsec);
+    return;
+}
+
+
+/* EPB2I-- convert Besselian epoch to year month day hours min sec */
+
+void
+epb2i (epoch, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+{
+    double date, time;
+
+    epb2dt (epoch, &date, &time);
+    dt2i (date, time, iyr,imon,iday,ihr,imn,sec, ndsec);
+    return;
+}
+
+
+/* EPJ2I-- convert Julian epoch to year month day hours min sec */
+
+void
+epj2i (epoch, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+{
+    double date, time;
+
+    epj2dt (epoch, &date, &time);
+    dt2i (date, time, iyr,imon,iday,ihr,imn,sec, ndsec);
+    return;
+}
+
+
+/* EP2JD-- convert fractional year as used in epoch to Julian date */
+
+double
+ep2jd (epoch)
+
+double	epoch;	/* Date as fractional year */
+
+{
+    double dj;	/* Julian date (returned)*/
+    double date, time;
+
+    ep2dt (epoch, &date, &time);
+    dj = dt2jd (date, time);
+    return (dj);
+}
+
+
+/* EPB2JD-- convert Besselian epoch to Julian Date */
+
+double
+epb2jd (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+
+{
+    return (2415020.31352 + ((epoch - 1900.0) * 365.242198781));
+}
+
+
+/* EPJ2JD-- convert Julian epoch to Julian Date */
+
+double
+epj2jd (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+
+{
+    return (2451545.0 + ((epoch - 2000.0) * 365.25));
+}
+
+
+/* EP2MJD-- convert fractional year as used in epoch to modified Julian date */
+
+double
+ep2mjd (epoch)
+
+double	epoch;	/* Date as fractional year */
+
+{
+    double dj;	/* Julian date (returned)*/
+    double date, time;
+
+    ep2dt (epoch, &date, &time);
+    dj = dt2jd (date, time);
+    return (dj - 2400000.5);
+}
+
+
+/* EPB2MJD-- convert Besselian epoch to modified Julian Date */
+
+double
+epb2mjd (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+
+{
+    return (15019.81352 + ((epoch - 1900.0) * 365.242198781));
+}
+
+
+/* EPJ2MJD-- convert Julian epoch to modified Julian Date */
+
+double
+epj2mjd (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+
+{
+    return (51544.5 + ((epoch - 2000.0) * 365.25));
+}
+
+
+
+/* EPB2EPJ-- convert Besselian epoch to Julian epoch */
+
+double
+epb2epj (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+{
+    double dj;		/* Julian date */
+    dj = epb2jd (epoch);
+    return (jd2epj (dj));
+}
+
+
+/* EPJ2EPB-- convert Julian epoch to Besselian epoch */
+
+double
+epj2epb (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+{
+    double dj;		/* Julian date */
+    dj = epj2jd (epoch);
+    return (jd2epb (dj));
+}
+
+
+/* JD2FD-- convert Julian date to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+jd2fd (dj)
+
+double	dj;	/* Julian date */
+{
+    double tsec;		/* seconds since 1950.0 (returned) */
+    tsec = (dj - 2433282.5) * 86400.0;
+    return (ts2fd (tsec));
+}
+
+
+/* JD2TS-- convert Julian date to seconds since 1950.0 */
+
+double
+jd2ts (dj)
+
+double	dj;	/* Julian date */
+{
+    return ((dj - 2433282.5) * 86400.0);
+}
+
+
+/* JD2TSI-- convert Julian date to IRAF seconds since 1980-01-01T0:00 */
+
+int
+jd2tsi (dj)
+
+double	dj;	/* Julian date */
+{
+    double ts;
+    ts = (dj - 2444239.5) * 86400.0;
+    return ((int) ts);
+}
+
+
+/* JD2TSU-- convert Julian date to Unix seconds since 1970-01-01T0:00 */
+
+time_t
+jd2tsu (dj)
+
+double	dj;	/* Julian date */
+{
+    return ((time_t)((dj - 2440587.5) * 86400.0));
+}
+
+
+/* DT2DOY-- convert yyyy.mmdd hh.mmss to year and day of year */
+
+void
+dt2doy (date, time, year, doy)
+
+double	date;	/* Date as yyyy.mmdd */
+double	time;	/* Time as hh.mmssxxxx */
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year with fraction (returned) */
+{
+    double	dj;	/* Julian date */
+    double	dj0;	/* Julian date on January 1 0:00 */
+    double	date0;	/* January first of date's year */
+    double	dyear;
+
+    dyear = floor (date);
+    date0 = dyear + 0.0101;
+    dj0 = dt2jd (date0, 0.0);
+    dj = dt2jd (date, time);
+    *year = (int) (dyear + 0.00000001);
+    *doy = dj - dj0 + 1.0;
+    return;
+}
+
+
+/* DOY2DT-- convert year and day of year to yyyy.mmdd hh.mmss */
+
+void
+doy2dt (year, doy, date, time)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    double	dj;	/* Julian date */
+    double	dj0;	/* Julian date on January 1 0:00 */
+    double	date0;	/* January first of date's year */
+
+    date0 = year + 0.0101;
+    dj0 = dt2jd (date0, 0.0);
+    dj = dj0 + doy - 1.0;
+    jd2dt (dj, date, time);
+    return;
+}
+
+
+/* DOY2EP-- convert year and day of year to fractional year as used in epoch */
+
+double
+doy2ep (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double date, time;
+    doy2dt (year, doy, &date, &time);
+    return (dt2ep (date, time));
+}
+
+
+
+/* DOY2EPB-- convert year and day of year to Besellian epoch */
+
+double
+doy2epb (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return (jd2epb (dj));
+}
+
+
+/* DOY2EPJ-- convert year and day of year to Julian epoch */
+
+double
+doy2epj (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return (jd2epj (dj));
+}
+
+
+/* DOY2FD-- convert year and day of year to FITS date */
+
+char *
+doy2fd (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;	/* Julian date  */
+
+    dj = doy2jd (year, doy);
+    return (jd2fd (dj));
+}
+
+
+/* DOY2JD-- convert year and day of year to Julian date */
+
+double
+doy2jd (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double	dj0;	/* Julian date */
+    double	date;	/* Date as yyyy.mmdd (returned) */
+    double	time;	/* Time as hh.mmssxxxx (returned) */
+
+    date = (double) year + 0.0101;
+    time = 0.0;
+    dj0 = dt2jd (date, time);
+    return (dj0 + doy - 1.0);
+}
+
+
+/* DOY2MJD-- convert year and day of year to Julian date */
+
+double
+doy2mjd (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double	dj0;	/* Julian date */
+    double	date;	/* Date as yyyy.mmdd (returned) */
+    double	time;	/* Time as hh.mmssxxxx (returned) */
+
+    date = (double) year + 0.0101;
+    time = 0.0;
+    dj0 = dt2jd (date, time);
+    return (dj0 + doy - 1.0 - 2400000.5);
+}
+
+
+/* DOY2TSU-- convert from FITS date to Unix seconds since 1970-01-01T0:00 */
+
+time_t
+doy2tsu (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return ((time_t)jd2ts (dj));
+}
+
+
+/* DOY2TSI-- convert from FITS date to IRAF seconds since 1980-01-01T0:00 */
+
+int
+doy2tsi (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return ((int)jd2tsi (dj));
+}
+
+
+/* DOY2TS-- convert year, day of year to seconds since 1950 */
+
+double
+doy2ts (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return (jd2ts (dj));
+}
+
+
+/* FD2DOY-- convert FITS date to year and day of year */
+
+void
+fd2doy (string, year, doy)
+
+char	*string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year with fraction (returned) */
+{
+    double dj;	/* Julian date */
+
+    dj = fd2jd (string);
+    jd2doy (dj, year, doy);
+    return;
+}
+
+
+/* JD2DOY-- convert Julian date to year and day of year */
+
+void
+jd2doy (dj, year, doy)
+
+double	dj;	/* Julian date */
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year with fraction (returned) */
+{
+    double date;	/* Date as yyyy.mmdd (returned) */
+    double time;	/* Time as hh.mmssxxxx (returned) */
+    double dj0;		/* Julian date at 0:00 on 1/1 */
+    double dyear;
+
+    jd2dt (dj, &date, &time);
+    *year = (int) date;
+    dyear = (double) *year;
+    dj0 = dt2jd (dyear+0.0101, 0.0);
+    *doy = dj - dj0 + 1.0;
+    return;
+}
+
+
+/* TS2JD-- convert seconds since 1950.0 to Julian date */
+
+double
+ts2jd (tsec)
+
+double	tsec;	/* seconds since 1950.0 */
+{
+    return (2433282.5 + (tsec / 86400.0));
+}
+
+
+/* TS2MJD-- convert seconds since 1950.0 to modified Julian date */
+
+double
+ts2mjd (tsec)
+
+double	tsec;	/* seconds since 1950.0 */
+{
+    return (33282.0 + (tsec / 86400.0));
+}
+
+
+/* TS2EP-- convert seconds since 1950.0 to fractional year as used in epoch */
+
+double
+ts2ep (tsec)
+
+double	tsec;	/* Seconds since 1950.0 */
+
+{
+    double date, time;
+    ts2dt (tsec, &date, &time);
+    return (dt2ep (date, time));
+}
+
+
+/* TS2EPB-- convert seconds since 1950.0 to Besselian epoch */
+
+double
+ts2epb (tsec)
+
+double	tsec;	/* Seconds since 1950.0 */
+
+{
+    double dj;		/* Julian Date */
+    dj = ts2jd (tsec);
+    return (jd2epb (dj));
+}
+
+
+/* TS2EPB-- convert seconds since 1950.0 to Julian epoch */
+
+double
+ts2epj (tsec)
+
+double	tsec;	/* Seconds since 1950.0 */
+
+{
+    double dj;		/* Julian Date */
+    dj = ts2jd (tsec);
+    return (jd2epj (dj));
+}
+
+
+/* DT2EP-- convert from date, time as yyyy.mmdd hh.mmsss to fractional year */
+
+double
+dt2ep (date, time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double epoch; /* Date as fractional year (returned) */
+    double dj, dj0, dj1, date0, time0, date1;
+
+    dj = dt2jd (date, time);
+    if (date == 0.0)
+	epoch = dj / 365.2422;
+    else {
+	time0 = 0.0;
+	date0 = dint (date) + 0.0101;
+	date1 = dint (date) + 1.0101;
+	dj0 = dt2jd (date0, time0);
+	dj1 = dt2jd (date1, time0);
+	epoch = dint (date) + ((dj - dj0) / (dj1 - dj0));
+	}
+    return (epoch);
+}
+
+
+/* DT2EPB-- convert from date, time as yyyy.mmdd hh.mmsss to Besselian epoch */
+
+double
+dt2epb (date, time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date */
+    double epoch;	/* Date as fractional year (returned) */
+    dj = dt2jd (date, time);
+    if (date == 0.0)
+	epoch = dj / 365.242198781;
+    else
+	epoch = jd2epb (dj);
+    return (epoch);
+}
+
+
+/* DT2EPJ-- convert from date, time as yyyy.mmdd hh.mmsss to Julian epoch */
+
+double
+dt2epj (date, time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date */
+    double epoch;	/* Date as fractional year (returned) */
+    dj = dt2jd (date, time);
+    if (date == 0.0)
+	epoch = dj / 365.25;
+    else
+	epoch = jd2epj (dj);
+    return (epoch);
+}
+
+
+/* EP2DT-- convert from fractional year to date, time as yyyy.mmdd hh.mmsss */
+
+void
+ep2dt (epoch, date, time)
+
+double epoch;	/* Date as fractional year */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj, dj0, dj1, date0, time0, date1, epochi, epochf;
+
+    time0 = 0.0;
+    epochi = dint (epoch);
+    epochf = epoch - epochi;
+    date0 = epochi + 0.0101;
+    date1 = epochi + 1.0101;
+    dj0 = dt2jd (date0, time0);
+    dj1 = dt2jd (date1, time0);
+    dj = dj0 + epochf * (dj1 - dj0);
+    jd2dt (dj, date, time);
+    return;
+}
+
+
+/* EPB2DT-- convert from Besselian epoch to date, time as yyyy.mmdd hh.mmsss */
+
+void
+epb2dt (epoch, date, time)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date */
+    dj = epb2jd (epoch);
+    jd2dt (dj, date, time);
+}
+
+
+/* EPJ2DT-- convert from Julian epoch to date, time as yyyy.mmdd hh.mmsss */
+
+void
+epj2dt (epoch, date, time)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date */
+    dj = epj2jd (epoch);
+    jd2dt (dj, date, time);
+}
+
+
+/* FD2JD-- convert FITS standard date to Julian date */
+
+double
+fd2jd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+
+    fd2dt (string, &date, &time);
+    return (dt2jd (date, time));
+}
+
+
+/* FD2MJD-- convert FITS standard date to modified Julian date */
+
+double
+fd2mjd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    return (fd2jd (string) - 2400000.5);
+}
+
+
+/* FD2TSU-- convert from FITS date to Unix seconds since 1970-01-01T0:00 */
+
+time_t
+fd2tsu (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+    fd2dt (string, &date, &time);
+    return (dt2tsu (date, time));
+}
+
+
+/* FD2TSI-- convert from FITS date to IRAF seconds since 1980-01-01T0:00 */
+
+int
+fd2tsi (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+    fd2dt (string, &date, &time);
+    return (dt2tsi (date, time));
+}
+
+
+/* FD2TS-- convert FITS standard date to seconds since 1950 */
+
+double
+fd2ts (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+    fd2dt (string, &date, &time);
+    return (dt2ts (date, time));
+}
+
+
+/* FD2FD-- convert any FITS standard date to ISO FITS standard date */
+
+char *
+fd2fd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+    fd2dt (string, &date, &time);
+    return (dt2fd (date, time));
+}
+
+
+/* FD2OF-- convert any FITS standard date to old FITS standard date time */
+
+char *
+fd2of (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    fd2i (string,&iyr,&imon,&iday,&ihr,&imn,&sec, 3);
+
+    /* Convert to old FITS date format */
+    string = (char *) calloc (32, sizeof (char));
+    if (iyr < 1900)
+	sprintf (string, "*** date out of range ***");
+    else if (iyr < 2000)
+	sprintf (string, "%02d/%02d/%02d %02d:%02d:%06.3f",
+		 iday, imon, iyr-1900, ihr, imn, sec);
+    else if (iyr < 2900.0)
+	sprintf (string, "%02d/%02d/%3d %02d:%02d:%6.3f",
+		 iday, imon, iyr-1900, ihr, imn, sec);
+    else
+	sprintf (string, "*** date out of range ***");
+    return (string);
+}
+
+
+/* TAI-UTC from the U.S. Naval Observatory */
+/* ftp://maia.usno.navy.mil/ser7/tai-utc.dat */
+static double taijd[23]={2441317.5, 2441499.5, 2441683.5, 2442048.5, 2442413.5,
+	      2442778.5, 2443144.5, 2443509.5, 2443874.5, 2444239.5, 2444786.5,
+	      2445151.5, 2445516.5, 2446247.5, 2447161.5, 2447892.5, 2448257.5,
+	      2448804.5, 2449169.5, 2449534.5, 2450083.5, 2450630.5, 2451179.5};
+static double taidt[23]={10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,
+	   20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0,32.0};
+static double dttab[173]={13.7,13.4,13.1,12.9,12.7,12.6,12.5,12.5,12.5,12.5,
+	   12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.4,12.3,12.2,12.0,11.7,11.4,
+	   11.1,10.6,10.2, 9.6, 9.1, 8.6, 8.0, 7.5, 7.0, 6.6, 6.3, 6.0, 5.8,
+	    5.7, 5.6, 5.6, 5.6, 5.7, 5.8, 5.9, 6.1, 6.2, 6.3, 6.5, 6.6, 6.8,
+            6.9, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.7, 7.8, 7.8,7.88,7.82,
+	   7.54, 6.97, 6.40, 6.02, 5.41, 4.10, 2.92, 1.82, 1.61, 0.10,-1.02,
+	  -1.28,-2.69,-3.24,-3.64,-4.54,-4.71,-5.11,-5.40,-5.42,-5.20,-5.46,
+	  -5.46,-5.79,-5.63,-5.64,-5.80,-5.66,-5.87,-6.01,-6.19,-6.64,-6.44,
+	  -6.47,-6.09,-5.76,-4.66,-3.74,-2.72,-1.54,-0.02, 1.24, 2.64, 3.86,
+	   5.37, 6.14, 7.75, 9.13,10.46,11.53,13.36,14.65,16.01,17.20,18.24,
+	  19.06,20.25,20.95,21.16,22.25,22.41,23.03,23.49,23.62,23.86,24.49,
+	  24.34,24.08,24.02,24.00,23.87,23.95,23.86,23.93,23.73,23.92,23.96,
+	  24.02,24.33,24.83,25.30,25.70,26.24,26.77,27.28,27.78,28.25,28.71,
+	  29.15,29.57,29.97,30.36,30.72,31.07,31.35,31.68,32.18,32.68,33.15,
+	  33.59,34.00,34.47,35.03,35.73,36.54,37.43,38.29,39.20,40.18,41.17,
+	  42.23};
+
+
+/* ET2FD-- convert from ET (or TDT or TT) in FITS format to UT in FITS format */
+
+char *
+et2fd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double dj0, dj, tsec, dt;
+
+    dj0 = fd2jd (string);
+    dt = utdt (dj0);
+    dj = dj0 - (dt / 86400.0);
+    dt = utdt (dj);
+    tsec = fd2ts (string);
+    tsec = tsec - dt;
+    return (ts2fd (tsec));
+}
+
+
+/* FD2ET-- convert from UT in FITS format to ET (or TDT or TT) in FITS format */
+
+char *
+fd2et (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double dj, tsec, dt;
+
+    dj = fd2jd (string);
+    dt = utdt (dj);
+    tsec = fd2ts (string);
+    tsec = tsec + dt;
+    return (ts2fd (tsec));
+}
+
+
+/* DT2ET-- convert from UT as yyyy.mmdd hh.mmssss to ET in same format */
+
+void
+dt2et (date, time)
+double	*date;	/* Date as yyyy.mmdd */
+double	*time;	/* Time as hh.mmssxxxx
+		 *if time<0, it is time as -(fraction of a day) */
+{
+    double dj, dt, tsec;
+
+    dj = dt2jd (*date, *time);
+    dt = utdt (dj);
+    tsec = dt2ts (*date, *time);
+    tsec = tsec + dt;
+    ts2dt (tsec, date, time);
+    return;
+}
+
+
+/* EDT2DT-- convert from ET as yyyy.mmdd hh.mmssss to UT in same format */
+
+void
+edt2dt (date, time)
+double	*date;	/* Date as yyyy.mmdd */
+double	*time;	/* Time as hh.mmssxxxx
+		 *if time<0, it is time as -(fraction of a day) */
+{
+    double dj, dt, tsec, tsec0;
+
+    dj = dt2jd (*date, *time);
+    dt = utdt (dj);
+    tsec0 = dt2ts (*date, *time);
+    tsec = tsec0 + dt;
+    dj = ts2jd (tsec);
+    dt = utdt (dj);
+    tsec = tsec0 + dt;
+    ts2dt (tsec, date, time);
+    return;
+}
+
+
+/* JD2JED-- convert from Julian Date to Julian Ephemeris Date */
+
+double
+jd2jed (dj)
+
+double dj;	/* Julian Date */
+{
+    double dt;
+
+    dt = utdt (dj);
+    return (dj + (dt / 86400.0));
+}
+
+
+/* JED2JD-- convert from Julian Ephemeris Date to Julian Date */
+
+double
+jed2jd (dj)
+
+double dj;	/* Julian Ephemeris Date */
+{
+    double dj0, dt;
+
+    dj0 = dj;
+    dt = utdt (dj);
+    dj = dj0 - (dt / 86400.0);
+    dt = utdt (dj);
+    return (dj - (dt / 86400.0));
+}
+
+
+/* TS2ETS-- convert from UT in seconds since 1950-01-01 to ET in same format */
+
+double
+ts2ets (tsec)
+
+double tsec;
+{
+    double dj, dt;
+
+    dj = ts2jd (tsec);
+    dt = utdt (dj);
+    return (tsec + dt);
+}
+
+
+/* ETS2TS-- convert from ET in seconds since 1950-01-01 to UT in same format */
+
+double
+ets2ts (tsec)
+
+double tsec;
+{
+    double dj, dj0, dt;
+
+    dj0 = ts2jd (tsec);
+    dt = utdt (dj0);
+    dj = dj0 - (dt / 86400.0);
+    dt = utdt (dj);
+    return (tsec - dt);
+}
+
+
+/* UTDT-- Compute difference between UT and dynamical time (ET-UT) */
+
+double
+utdt (dj)
+
+double dj;	/* Julian Date (UT) */
+{
+    double dt, date, time, ts, ts1, ts0, date0, yfrac, diff, cj;
+    int i, iyr, iyear;
+
+    /* If after 1972-01-01, use tabulated TAI-UT */
+    if (dj >= 2441317.5) {
+	dt = 0.0;
+	for (i = 22;  i > 0; i--) {
+	    if (dj >= taijd[i])
+		dt = taidt[i];
+	    }
+	dt = dt + 32.84;
+	}
+
+    /* For 1800-01-01 to 1972-01-01, use table of ET-UT from AE */
+    else if (dj >= 2378496.5) {
+	jd2dt (dj, &date, &time);
+	ts = jd2ts (dj);
+	iyear = (int) date;
+	iyr = iyear - 1800;
+	date0 = (double) iyear + 0.0101;
+	ts0 = dt2ts (date0, 0.0);
+	date0 = (double) (iyear + 1) + 0.0101;
+	ts1 = dt2ts (date0, 0.0);
+	yfrac = (ts - ts0) / (ts1 - ts0);
+	diff = dttab[iyr+1] - dttab[iyr];
+	dt = dttab[iyr] + (diff * yfrac);
+	}
+
+    /* Compute back to 1600 using formula from McCarthy and Babcock (1986) */
+    else if (dj >= 2305447.5) {
+	cj = (dj - 2378496.5) / 36525.0;
+	dt = 5.156 + 13.3066 * (cj - 0.19) * (cj - 0.19);
+	}
+
+    /* Compute back to 948 using formula from Stephenson and Morrison (1984) */
+    else if (dj >= 2067309.5) {
+	cj = (dj - 2378496.5) / 36525.0;
+	dt = 25.5 * cj * cj;
+	}
+
+    /*Compute back to 390 BC using formula from Stephenson and Morrison (1984)*/
+    else if (dj >= 0.0) {
+	cj = (dj = 2378496.5) / 36525.0;
+	dt = 1360.0 + (320.0 * cj) + (44.3 * cj * cj);
+	}
+
+    else
+	dt = 0.0;
+    return (dt);
+}
+
+
+/* FD2OFD-- convert any FITS standard date to old FITS standard date */
+
+char *
+fd2ofd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    fd2i (string,&iyr,&imon,&iday,&ihr,&imn,&sec, 3);
+
+    /* Convert to old FITS date format */
+    string = (char *) calloc (32, sizeof (char));
+    if (iyr < 1900)
+	sprintf (string, "*** date out of range ***");
+    else if (iyr < 2000)
+	sprintf (string, "%02d/%02d/%02d", iday, imon, iyr-1900);
+    else if (iyr < 2900.0)
+	sprintf (string, "%02d/%02d/%3d", iday, imon, iyr-1900);
+    else
+	sprintf (string, "*** date out of range ***");
+    return (string);
+}
+
+
+/* FD2OFT-- convert any FITS standard date to old FITS standard time */
+
+char *
+fd2oft (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    fd2i (string,&iyr,&imon,&iday,&ihr,&imn,&sec, 3);
+
+    /* Convert to old FITS date format */
+    string = (char *) calloc (32, sizeof (char));
+    sprintf (string, "%02d:%02d:%06.3f", ihr, imn, sec);
+    return (string);
+}
+
+
+/* FD2DT-- convert FITS standard date to date, time as yyyy.mmdd hh.mmsss */
+
+void
+fd2dt (string, date, time)
+
+char *string;	/* FITS date string, which may be:
+		    fractional year
+		    dd/mm/yy (FITS standard before 2000)
+		    dd-mm-yy (nonstandard use before 2000)
+		    yyyy-mm-dd (FITS standard after 1999)
+		    yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    fd2i (string,&iyr,&imon,&iday,&ihr,&imn,&sec, 4);
+
+    /* Convert date to yyyy.mmdd */
+    if (iyr < 0) {
+	*date = (double) (-iyr) + 0.01 * (double) imon + 0.0001 * (double) iday;
+	*date = -(*date);
+	}
+    else
+	*date = (double) iyr + 0.01 * (double) imon + 0.0001 * (double) iday;
+
+    /* Convert time to hh.mmssssss */
+    *time = (double) ihr + 0.01 * (double) imn + 0.0001 * sec;
+
+    return;
+}
+
+
+/* FD2EP-- convert from FITS standard date to fractional year */
+
+double
+fd2ep (string)
+
+char *string;	/* FITS date string, which may be:
+			yyyy.ffff (fractional year)
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+
+{
+    double dj;		/* Julian date */
+    dj = fd2jd (string);
+    if (dj < 1.0)
+	return (dj / 365.2422);
+    else
+	return (jd2ep (dj));
+}
+
+
+/* FD2EPB-- convert from FITS standard date to Besselian epoch */
+
+double
+fd2epb (string)
+
+char *string;	/* FITS date string, which may be:
+			yyyy.ffff (fractional year)
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+
+{
+    double dj;		/* Julian date */
+    dj = fd2jd (string);
+    if (dj < 1.0)
+	return (dj / 365.242198781);
+    else
+	return (jd2epb (dj));
+}
+
+
+/* FD2EPJ-- convert from FITS standard date to Julian epoch */
+
+double
+fd2epj (string)
+
+char *string;	/* FITS date string, which may be:
+			yyyy.ffff (fractional year)
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+
+{
+    double dj;		/* Julian date */
+    dj = fd2jd (string);
+    if (dj < 1.0)
+	return (dj / 365.25);
+    else
+	return (jd2epj (dj));
+}
+
+
+/* DT2TSU-- convert from date and time to Unix seconds since 1970-01-01T0:00 */
+
+time_t
+dt2tsu (date,time)
+
+double	date;	/* Date as yyyy.mmdd */
+double	time;	/* Time as hh.mmssxxxx
+		 *if time<0, it is time as -(fraction of a day) */
+{
+    return ((time_t)(dt2ts (date, time) - 631152000.0));
+}
+
+
+/* DT2TSI-- convert from date and time to IRAF seconds since 1980-01-01T0:00 */
+
+int
+dt2tsi (date,time)
+
+double	date;	/* Date as yyyy.mmdd */
+double	time;	/* Time as hh.mmssxxxx
+		 *if time<0, it is time as -(fraction of a day) */
+{
+    return ((int)(dt2ts (date, time) - 946684800.0));
+}
+
+
+
+/* DT2TS-- convert from date, time as yyyy.mmdd hh.mmsss to sec since 1950.0 */
+
+double
+dt2ts (date,time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double tsec; /* Seconds past 1950.0 (returned) */
+
+    double dh,dm,dd;
+    int iy,im,id;
+
+/* Calculate the number of full years, months, and days already
+ * elapsed since 0h, March 1, -1 (up to most recent midnight). */
+
+    /* convert time of day to elapsed seconds */
+
+    /* If time is < 0, it is assumed to be a fractional day */
+    if (time < 0.0)
+	tsec = time * -86400.0;
+    else {
+	dh = (int) (time + 0.0000000001);
+	dm = (int) (((time - dh) * 100.0) + 0.0000000001);
+	tsec = (time * 10000.0) - (dh * 10000.0) - (dm * 100.0);
+	tsec = (int) (tsec * 100000.0 + 0.0001) / 100000.0;
+	tsec = tsec + (dm * 60.0) + (dh * 3600.0);
+	}
+
+
+    /* Calculate the number of full months elapsed since
+     * the current or most recent March */
+    if (date >= 0.0301) {
+	iy = (int) (date + 0.0000000001);
+	im = (int) (((date - (double) (iy)) * 10000.0) + 0.00000001);
+	id = im % 100;
+	im = (im / 100) + 9;
+	if (im < 12) iy = iy - 1;
+	im = im % 12;
+	id = id - 1;
+
+	/* starting with March as month 0 and ending with the following
+	 * February as month 11, the calculation of the number of days
+	 * per month reduces to a simple formula. the following statement
+	 * determines the number of whole days elapsed since 3/1/-1 and then
+	 * subtracts the 712163 days between then and 1/1/1950.  it converts
+	 * the result to seconds and adds the accumulated seconds above. */
+	id = id + ((im+1+im/6+im/11)/2 * 31) + ((im-im/6-im/11)/2 * 30) +
+	     (iy / 4) - (iy / 100) + (iy / 400);
+	dd = (double) id + (365.0 * (double) iy) - 712163.0;
+	tsec = tsec + (dd * 86400.0);
+	}
+
+    return (tsec);
+}
+
+
+/* TS2DT-- convert seconds since 1950.0 to date, time as yyyy.mmdd hh.mmssss */
+
+void
+ts2dt (tsec,date,time)
+
+double	tsec;	/* Seconds past 1950.0 */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    ts2i (tsec,&iyr,&imon,&iday,&ihr,&imn,&sec, 4);
+
+    /* Convert date to yyyy.mmdd */
+    if (iyr < 0) {
+	*date = (double) (-iyr) + 0.01 * (double) imon + 0.0001 * (double) iday;
+	*date = -(*date);
+	}
+    else
+	*date = (double) iyr + 0.01 * (double) imon + 0.0001 * (double) iday;
+
+    /* Convert time to hh.mmssssss */
+    *time = (double) ihr + 0.01 * (double) imn + 0.0001 * sec;
+
+    return;
+}
+
+
+/* TSI2DT-- Convert seconds since 1980-01-01 to date yyyy.ddmm, time hh.mmsss */
+
+void
+tsi2dt (isec,date,time)
+
+int	isec;	/* Seconds past 1980-01-01 */
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    ts2dt (tsi2ts (isec), date, time);
+}
+
+
+/* TSI2FD-- Convert seconds since 1980-01-01 to FITS standard date string */
+
+char *
+tsi2fd (isec)
+
+int	isec;	/* Seconds past 1980-01-01 */
+{
+    return (ts2fd (tsi2ts (isec)));
+}
+
+
+/* TSI2TS-- Convert seconds since 1980-01-01 to seconds since 1950-01-01 */
+
+double
+tsi2ts (isec)
+int	isec;	/* Seconds past 1980-01-01 */
+{
+    return ((double) isec + 946684800.0);
+}
+
+
+/* TSU2FD-- Convert seconds since 1970-01-01 to FITS standard date string */
+
+char *
+tsu2fd (isec)
+time_t	isec;	/* Seconds past 1970-01-01 */
+{
+    return (ts2fd (tsu2ts (isec)));
+}
+
+
+/* TSU2DT-- Convert seconds since 1970-01-01 to date yyyy.ddmm, time hh.mmsss */
+
+void
+tsu2dt (isec,date,time)
+time_t	isec;	/* Seconds past 1970-01-01 */
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    ts2dt (tsu2ts (isec), date, time);
+}
+
+
+/* TSU2TS-- Convert seconds since 1970-01-01 to seconds since 1950-01-01 */
+
+double
+tsu2ts (isec)
+time_t	isec;	/* Seconds past 1970-01-01 */
+{
+    return ((double) isec + 631152000.0);
+}
+
+/* TSU2TSI-- UT seconds since 1970-01-01 to local seconds since 1980-01-01 */
+
+int
+tsu2tsi (isec)
+time_t	isec;	/* Seconds past 1970-01-01 */
+{
+    double date, time;
+    struct tm *ts;
+
+    /* Get local time  from UT seconds */
+    ts = localtime (&isec);
+    if (ts->tm_year < 1000)
+	date = (double) (ts->tm_year + 1900);
+    else
+	date = (double) ts->tm_year;
+    date = date + (0.01 * (double) (ts->tm_mon + 1));
+    date = date + (0.0001 * (double) ts->tm_mday);
+    time = (double) ts->tm_hour;
+    time = time + (0.01 * (double) ts->tm_min);
+    time = time + (0.0001 * (double) ts->tm_sec);
+    return ((int)(dt2ts (date, time) - 631152000.0));
+}
+
+
+/* TS2FD-- convert seconds since 1950.0 to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+ts2fd (tsec)
+
+double	tsec;	/* Seconds past 1950.0 */
+{
+    double date, time;
+
+    ts2dt (tsec, &date, &time);
+    return (dt2fd (date, time));
+}
+
+
+/* TSD2FD-- convert seconds since start of day to FITS time, hh:mm:ss.ss */
+
+char *
+tsd2fd (tsec)
+
+double	tsec;	/* Seconds since start of day */
+{
+    double date, time;
+    char *thms, *fdate;
+    int lfd, nbc;
+
+    ts2dt (tsec, &date, &time);
+    fdate = dt2fd (date, time);
+    thms = (char *) calloc (16, 1);
+    lfd = strlen (fdate);
+    nbc = lfd - 11;
+    strncpy (thms, fdate+11, nbc);
+    return (thms);
+}
+
+
+/* TSD2DT-- convert seconds since start of day to hh.mmssss */
+
+double
+tsd2dt (tsec)
+
+double	tsec;	/* Seconds since start of day */
+{
+    double date, time;
+
+    ts2dt (tsec, &date, &time);
+    return (time);
+}
+
+
+
+/* DT2I-- convert vigesimal date and time to year month day hours min sec */
+
+void
+dt2i (date, time, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double t,d;
+
+    t = time;
+    if (date < 0.0)
+	d = -date;
+    else
+	d = date;
+
+    /* Extract components of time */
+    *ihr = dint (t + 0.000000001);
+    t = 100.0 * (t - (double) *ihr);
+    *imn = dint (t + 0.0000001);
+    *sec = 100.0 * (t - (double) *imn);
+
+    /* Extract components of date */
+    *iyr = dint (d + 0.00001);
+    d = 100.0 * (d - (double) *iyr);
+    if (date < 0.0)
+	*iyr = - *iyr;
+    *imon = dint (d + 0.001);
+    d = 100.0 * (d - (double) *imon);
+    *iday = dint (d + 0.1);
+
+   /* Make sure date and time are legal */
+    fixdate (iyr, imon, iday, ihr, imn, sec, ndsec);
+
+    return;
+}
+
+
+/* FD2I-- convert from FITS standard date to year, mon, day, hours, min, sec */
+
+void
+fd2i (string, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+char	*string; /* FITS date string, which may be:
+			yyyy.ffff (fractional year)
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double tsec, fday, hr, mn;
+    int i;
+    char *sstr, *dstr, *tstr, *cstr, *nval, *fstr;
+
+    /* Initialize all returned data to zero */
+    *iyr = 0;
+    *imon = 0;
+    *iday = 0;
+    *ihr = 0;
+    *imn = 0;
+    *sec = 0.0;
+
+    /* Return if no input string */
+    if (string == NULL)
+	return;
+
+    /* Check for various non-numeric characters */
+    sstr = strchr (string,'/');
+    dstr = strchr (string,'-');
+    if (dstr == string)
+	dstr = strchr (string+1, '-');
+    fstr = strchr (string, '.');
+    tstr = strchr (string,'T');
+    if (tstr == NULL)
+	tstr = strchr (string, 'Z');
+    if (tstr == NULL)
+	tstr = strchr (string, 'S');
+    if (fstr != NULL && tstr != NULL && fstr > tstr)
+	fstr = NULL;
+    cstr = strchr (string,':');
+
+    /* Original FITS date format: dd/mm/yy */
+    if (sstr > string) {
+	*sstr = '\0';
+	*iday = (int) atof (string);
+	if (*iday > 31) {
+	    *iyr = *iday;
+	    if (*iyr >= 0 && *iyr <= 49)
+		*iyr = *iyr + 2000;
+	    else if (*iyr < 1000)
+		*iyr = *iyr + 1900;
+	    *sstr = '/';
+	    nval = sstr + 1;
+	    sstr = strchr (nval,'/');
+	    if (sstr > string) {
+		*sstr = '\0';
+		*imon = (int) atof (nval);
+		*sstr = '/';
+		nval = sstr + 1;
+		*iday = (int) atof (nval);
+		}
+	    }
+	else {
+	    *sstr = '/';
+	    nval = sstr + 1;
+	    sstr = strchr (nval,'/');
+	    if (sstr == NULL)
+		sstr = strchr (nval,'-');
+	    if (sstr > string) {
+		*sstr = '\0';
+		*imon = (int) atof (nval);
+		*sstr = '/';
+		nval = sstr + 1;
+		*iyr = (int) atof (nval);
+		if (*iyr >= 0 && *iyr <= 49)
+		    *iyr = *iyr + 2000;
+		else if (*iyr < 1000)
+		    *iyr = *iyr + 1900;
+		}
+	    }
+	tstr = strchr (string,'_');
+	if (tstr == NULL)
+	    return;
+	}
+
+    /* New FITS date format: yyyy-mm-ddThh:mm:ss[.sss] */
+    else if (dstr > string) {
+	*dstr = '\0';
+	*iyr = (int) atof (string);
+	*dstr = '-';
+	nval = dstr + 1;
+	dstr = strchr (nval,'-');
+	*imon = 1;
+	*iday = 1;
+
+	/* Decode year, month, and day */
+	if (dstr > string) {
+	    *dstr = '\0';
+	    *imon = (int) atof (nval);
+	    *dstr = '-';
+	    nval = dstr + 1;
+	    if (tstr > string)
+		*tstr = '\0';
+	    *iday = (int) atof (nval);
+
+	    /* If fraction of a day is present, turn it into a time */
+	    if (fstr != NULL) {
+		fday = atof (fstr);
+		hr = fday * 24.0;
+		*ihr = (int) hr;
+		mn = 60.0 * (hr - (double) *ihr);
+		*imn = (int) mn;
+		*sec = 60.0 * (mn - (double) *imn);
+		}
+
+	    if (tstr > string)
+		*tstr = 'T';
+	    }
+
+	/* If date is > 31, it is really year in old format */
+	if (*iday > 31) {
+	    i = *iyr;
+	    if (*iday < 100)
+		*iyr = *iday + 1900;
+	    else
+		*iyr = *iday;
+	    *iday = i;
+	    }
+	}
+
+    /* In rare cases, a FITS time is entered as an epoch */
+    else if (tstr == NULL && cstr == NULL && isnum (string)) {
+	tsec = ep2ts (atof (string));
+	ts2i (tsec,iyr,imon,iday,ihr,imn,sec, ndsec);
+	return;
+	}
+
+    /* Extract time, if it is present */
+    if (tstr > string || cstr > string) {
+	if (tstr > string)
+	    nval = tstr + 1;
+	else
+	    nval = string;
+	cstr = strchr (nval,':');
+	if (cstr > string) {
+	    *cstr = '\0';
+	    *ihr = (int) atof (nval);
+	    *cstr = ':';
+	    nval = cstr + 1;
+	    cstr = strchr (nval,':');
+	    if (cstr > string) {
+		*cstr = '\0';
+		*imn = (int) atof (nval);
+		*cstr = ':';
+		nval = cstr + 1;
+		*sec = atof (nval);
+		}
+	    else
+		*imn = (int) atof (nval);
+	    }
+	else
+	    *ihr = (int) atof (nval);
+	}
+    else
+	ndsec = -1;
+
+   /* Make sure date and time are legal */
+    fixdate (iyr, imon, iday, ihr, imn, sec, ndsec);
+
+    return;
+}
+
+
+/* TS2I-- convert sec since 1950.0 to year month day hours minutes seconds */
+
+void
+ts2i (tsec,iyr,imon,iday,ihr,imn,sec, ndsec)
+
+double	tsec;	/* seconds since 1/1/1950 0:00 */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double t,days, ts, dts;
+    int nc,nc4,nly,ny,m,im;
+
+    /* Round seconds to 0 - 4 decimal places */
+    ts = tsec + 61530883200.0;
+    if (ts < 0.0)
+	dts = -0.5;
+    else
+	dts = 0.5;
+    if (ndsec < 1)
+	t = dint (ts + dts) * 10000.0;
+    else if (ndsec < 2)
+	t = dint (ts * 10.0 + dts) * 1000.0;
+    else if (ndsec < 3)
+	t = dint (ts * 100.0 + dts) * 100.0;
+    else if (ndsec < 4)
+	t = dint (ts * 1000.0 + dts) * 10.0;
+    else
+	t = dint (ts * 10000.0 + dts);
+    ts = t / 10000.0;
+
+    /* Time of day (hours, minutes, seconds */
+    *ihr = (int) (dmod (ts/3600.0, 24.0));
+    *imn = (int) (dmod (ts/60.0, 60.0));
+    *sec = dmod (ts, 60.0);
+
+    /* Number of days since 0 hr 0/0/0000 */
+    days = dint ((t / 864000000.0) + 0.000001);
+
+    /* Number of leap centuries (400 years) */
+    nc4 = (int) ((days / 146097.0) + 0.00001);
+
+    /* Number of centuries since last /400 */
+    days = days - (146097.0 * (double) (nc4));
+    nc = (int) ((days / 36524.0) + 0.000001);
+    if (nc > 3) nc = 3;
+
+    /* Number of leap years since last century */
+    days = days - (36524.0 * nc);
+    nly = (int) ((days / 1461.0) + 0.0000000001);
+
+    /* Number of years since last leap year */
+    days = days - (1461.0 * (double) nly);
+    ny = (int) ((days / 365.0) + 0.00000001);
+    if (ny > 3) ny = 3;
+
+    /* Day of month */
+    days = days - (365.0 * (double) ny);
+    if (days < 0) {
+	m = 0;
+	*iday = 29;
+	}
+    else {
+	*iday = (int) (days + 0.00000001) + 1;
+	for (m = 1; m <= 12; m++) {
+	    im = (m + ((m - 1) / 5)) % 2;
+	    /* fprintf (stderr,"%d %d %d %d\n", m, im, *iday, nc); */
+	    if (*iday-1 < im+30) break;
+	    *iday = *iday - im - 30;
+	    }
+	}
+
+    /* Month */
+    *imon = ((m+1) % 12) + 1;
+
+    /* Year */
+    *iyr = nc4*400 + nc*100 + nly*4 + ny + m/11;
+
+   /* Make sure date and time are legal */
+    fixdate (iyr, imon, iday, ihr, imn, sec, ndsec);
+
+    return;
+}
+
+
+/* UT2DOY-- Current Universal Time as year, day of year */
+
+void
+ut2doy (year, doy)
+
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year (returned) */
+{
+    double date, time;
+    ut2dt (&date, &time);
+    dt2doy (date, time, year, doy);
+    return;
+}
+
+
+/* UT2DT-- Current Universal Time as date (yyyy.mmdd) and time (hh.mmsss) */
+
+void
+ut2dt(date, time)
+
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    time_t tsec;
+    struct timeval tp;
+    struct timezone tzp;
+    struct tm *ts;
+
+    gettimeofday (&tp,&tzp);
+
+    tsec = tp.tv_sec;
+    ts = gmtime (&tsec);
+
+    if (ts->tm_year < 1000)
+	*date = (double) (ts->tm_year + 1900);
+    else
+	*date = (double) ts->tm_year;
+    *date = *date + (0.01 * (double) (ts->tm_mon + 1));
+    *date = *date + (0.0001 * (double) ts->tm_mday);
+    *time = (double) ts->tm_hour;
+    *time = *time + (0.01 * (double) ts->tm_min);
+    *time = *time + (0.0001 * (double) ts->tm_sec);
+
+    return;
+}
+
+
+/* UT2EP-- Return current Universal Time as fractional year */
+
+double
+ut2ep()
+{
+    return (jd2ep (ut2jd()));
+}
+
+
+/* UT2EPB-- Return current Universal Time as Besselian epoch */
+
+double
+ut2epb()
+{
+    return (jd2epb (ut2jd()));
+}
+
+
+/* UT2EPJ-- Return current Universal Time as Julian epoch */
+
+double
+ut2epj()
+{
+    return (jd2epj (ut2jd()));
+}
+
+
+/* UT2FD-- Return current Universal Time as FITS ISO date string */
+
+char *
+ut2fd()
+{
+    int year, month, day, hour, minute, second;
+    time_t tsec;
+    struct timeval tp;
+    struct timezone tzp;
+    struct tm *ts;
+    char *isotime;
+
+    gettimeofday (&tp,&tzp);
+    tsec = tp.tv_sec;
+    ts = gmtime (&tsec);
+
+    year = ts->tm_year;
+    if (year < 1000)
+	year = year + 1900;
+    month = ts->tm_mon + 1;
+    day = ts->tm_mday;
+    hour = ts->tm_hour;
+    minute = ts->tm_min;
+    second = ts->tm_sec; 
+
+    isotime = (char *) calloc (32, sizeof (char));
+    sprintf (isotime, "%04d-%02d-%02dT%02d:%02d:%02d",
+		      year, month, day, hour, minute, second);
+    return (isotime);
+}
+
+
+/* UT2JD-- Return current Universal Time as Julian Date */
+
+double
+ut2jd()
+{
+    return (fd2jd (ut2fd()));
+}
+
+
+/* UT2MJD-- convert current UT to Modified Julian Date */
+
+double
+ut2mjd ()
+
+{
+    return (ut2jd() - 2400000.5);
+}
+
+/* UT2TS-- current Universal Time as IRAF seconds since 1950-01-01T00:00 */
+
+double
+ut2ts()
+{
+    double tsec;
+    char *datestring;
+    datestring = ut2fd();
+    tsec = fd2ts (datestring);
+    free (datestring);
+    return (tsec);
+}
+
+
+/* UT2TSI-- current Universal Time as IRAF seconds since 1980-01-01T00:00 */
+
+int
+ut2tsi()
+{
+    return ((int)(ut2ts() - 946684800.0));
+}
+
+
+/* UT2TSU-- current Universal Time as IRAF seconds since 1970-01-01T00:00 */
+
+time_t
+ut2tsu()
+{
+    return ((time_t)(ut2ts () - 631152000.0));
+}
+
+
+/* FD2GST-- convert from FITS date to Greenwich Sidereal Time */
+
+char *
+fd2gst (string)
+
+char	*string;	/* FITS date string, which may be:
+			  fractional year
+			  dd/mm/yy (FITS standard before 2000)
+			  dd-mm-yy (nonstandard use before 2000)
+			  yyyy-mm-dd (FITS standard after 1999)
+			  yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double dj, gsec, date, time;
+
+    dj = fd2jd (string);
+    gsec = jd2gst (dj);
+    ts2dt (gsec, &date, &time);
+    date = 0.0;
+    return (dt2fd (date, time));
+}
+
+
+/* DT2GST-- convert from UT as yyyy.mmdd hh.mmssss to Greenwich Sidereal Time*/
+
+void
+dt2gst (date, time)
+double  *date;  /* Date as yyyy.mmdd */
+double  *time;  /* Time as hh.mmssxxxx
+                 *if time<0, it is time as -(fraction of a day) */
+{
+    double dj, gsec;
+
+    dj = dt2ts (*date, *time);
+    gsec = jd2gst (dj);
+    ts2dt (gsec, date, time);
+    *date = 0.0;
+    return;
+}
+
+
+/* JD2LST - Local Sidereal Time in seconds from Julian Date */
+
+double
+jd2lst (dj)
+
+double dj;	/* Julian Date */
+{
+    double gst, lst, l0;
+
+    /* Compute Greenwich Sidereal Time at this epoch */
+    gst = jd2gst (dj);
+
+    /* Subtract longitude (degrees to seconds of time) */
+    lst = gst - (240.0 * longitude);
+    if (lst < 0.0)
+	lst = lst + 86400.0;
+    else if (lst > 86400.0)
+	lst = lst - 86400.0;
+    return (lst);
+}
+
+
+/* FD2LST - Local Sidereal Time  as hh:mm:ss.ss
+            from Universal Time as FITS ISO date */
+
+char *
+fd2lst (string)
+
+char	*string;	/* FITS date string, which may be:
+			  fractional year
+			  dd/mm/yy (FITS standard before 2000)
+			  dd-mm-yy (nonstandard use before 2000)
+			  yyyy-mm-dd (FITS standard after 1999) */
+{
+    double dj, date, time, lst;
+
+    dj = fd2jd (string);
+    lst = jd2lst (dj);
+    ts2dt (lst, &date, &time);
+    date = 0.0;
+    return (dt2fd (date, time));
+}
+
+
+/* DT2LST - Local Sidereal Time  as hh.mmssss
+            from Universal Time as yyyy.mmdd hh.mmssss */
+
+void
+dt2lst (date, time)
+
+double  *date;  /* Date as yyyy.mmdd */
+double  *time;  /* Time as hh.mmssxxxx
+                 *if time<0, it is time as -(fraction of a day) */
+{
+    double dj, lst, date0;
+
+    dj = dt2jd (*date, *time);
+    lst = jd2lst (dj);
+    date0 = 0.0;
+    ts2dt (lst, &date0, time);
+    return;
+}
+
+
+/* TS2LST - Local Sidereal Time in seconds of day
+ *          from Universal Time in seconds since 1951-01-01T0:00:00
+ */
+
+double
+ts2lst (tsec)
+
+double tsec;		/* time since 1950.0 in UT seconds */
+{
+    double gst;	/* Greenwich Sidereal Time in seconds since 0:00 */
+    double lst;	/* Local Sidereal Time in seconds since 0:00 */
+    double gsec, date;
+
+    /* Greenwich Sidereal Time */
+    gsec = ts2gst (tsec);
+    date = 0.0;
+    ts2dt (gsec, &date, &gst);
+
+    lst = gst - (longitude / 15.0);
+    if (lst < 0.0)
+	lst = lst + 86400.0;
+    else if (lst > 86400.0)
+	lst = lst - 86400.0;
+    return (lst);
+}
+
+
+/* LST2FD - calculate current UT given Local Sidereal Time
+ *	    plus date in FITS ISO format (yyyy-mm-dd)
+ *	    Return UT date and time in FITS ISO format
+ */
+
+char *
+lst2fd (string)
+
+char *string;		/* UT Date, LST as yyyy-mm-ddShh:mm:ss.ss */
+{
+    double sdj, dj;
+
+    sdj = fd2jd (string);
+
+    dj = lst2jd (sdj);
+
+    return (jd2fd (dj));
+}
+
+
+/* LST2JD - calculate current Julian Date given Local Sidereal Time
+ *	    plus current Julian Date (0.5 at 0:00 UT)
+ *	    Return UT date and time as Julian Date
+ */
+
+double
+lst2jd (sdj)
+
+double sdj;	/* Julian Date of desired day at 0:00 UT + sidereal time */
+{
+    double gst;	/* Greenwich Sidereal Time in seconds since 0:00 */
+    double lsd;	/* Local Sidereal Time in seconds since 0:00 */
+    double gst0, tsd, dj1, dj0, eqnx;
+    int idj;
+
+    /* Julian date at 0:00 UT */
+    idj = (int) sdj;
+    dj0 = (double) idj + 0.5;
+    if (dj0 > sdj) dj0 = dj0 - 1.0;
+
+    /* Greenwich Sidereal Time at 0:00 UT in seconds */
+    gst0 = jd2gst (dj0);
+
+    /* Sidereal seconds since 0:00 */
+    lsd = (sdj - dj0) * 86400.0;
+
+    /* Remove longitude for current Greenwich Sidereal Time in seconds */
+    /* (convert longitude from degrees to seconds of time) */
+    gst = lsd + (longitude * 240.0);
+
+    /* Time since 0:00 UT */
+    tsd = (gst - gst0) / 1.0027379093;
+
+    /* Julian Date (UT) */
+    dj1 = dj0 + (tsd / 86400.0);
+
+    /* Equation of the equinoxes converted to UT seconds */
+    eqnx = eqeqnx (dj1) / 1.002739093;
+
+    /* Remove equation of equinoxes */
+    dj1 = dj1 - (eqnx / 86400.0);
+    if (dj1 < dj0)
+	dj1 = dj1 + 1.0;
+
+    return (dj1);
+}
+
+
+/* MST2FD - calculate current UT given Greenwich Mean Sidereal Time
+ *	    plus date in FITS ISO format (yyyy-mm-ddShh:mm:ss.ss)
+ *	    Return UT date and time in FITS ISO format
+ */
+
+char *
+mst2fd (string)
+
+char *string;		/* UT Date, MST as yyyy-mm-ddShh:mm:ss.ss */
+{
+    double sdj, dj;
+
+    sdj = fd2jd (string);
+
+    dj = mst2jd (sdj);
+
+    return (jd2fd (dj));
+}
+
+
+/* MST2JD - calculate current UT given Greenwich Mean Sidereal Time
+ *	    plus date in Julian Date (0:00 UT + Mean Sidereal Time)
+ *	    Return UT date and time as Julian Date
+ */
+
+double
+mst2jd (sdj)
+
+double sdj;		/* UT Date, MST as Julian Date */
+{
+    double tsd, djd, st0, dj0, dj;
+
+    dj0 = (double) ((int) sdj) + 0.5;
+
+    /* Greenwich Mean Sidereal Time at 0:00 UT in seconds */
+    st0 = jd2mst (dj0);
+
+    /* Mean Sidereal Time in seconds */
+    tsd = (sdj - dj0) * 86400.0;
+    if (tsd < 0.0)
+	tsd = tsd + 86400.0;
+
+    /* Convert to fraction of a day since 0:00 UT */
+    djd = ((tsd - st0) / 1.0027379093) / 86400.0;
+
+    /* Julian Date */
+    dj = dj0 + djd;
+    if (dj < dj0)
+	dj = dj + (1.0 / 1.0027379093);
+
+    return (dj);
+}
+
+
+
+/* GST2FD - calculate current UT given Greenwich Sidereal Time
+ *	    plus date in FITS ISO format (yyyy-mm-ddShh:mm:ss.ss)
+ *	    Return UT date and time in FITS ISO format
+ */
+
+char *
+gst2fd (string)
+
+char *string;		/* UT Date, GST as yyyy-mm-ddShh:mm:ss.ss */
+{
+    double sdj, dj;
+
+    sdj = fd2jd (string);
+
+    dj = gst2jd (sdj);
+
+    return (jd2fd (dj));
+}
+
+
+/* GST2JD - calculate current UT given Greenwich Sidereal Time
+ *	    plus date as Julian Date (JD at 0:00 UT + sidereal time)
+ *	    Return UT date and time as Julian Date
+ */
+
+double
+gst2jd (sdj)
+
+double sdj;		/* UT Date, GST as Julian Date */
+{
+    double dj, tsd, djd, st0, dj0, eqnx;
+
+    dj0 = (double) ((int) sdj) + 0.5;
+
+    /* Greenwich Mean Sidereal Time at 0:00 UT in seconds */
+    st0 = jd2mst (dj0);
+
+    /* Mean Sidereal Time in seconds */
+    tsd = (sdj - dj0) * 86400.0;
+    if (tsd < 0.0)
+	tsd = tsd + 86400.0;
+
+    /* Convert to fraction of a day since 0:00 UT */
+    djd = ((tsd - st0) / 1.0027379093) / 86400.0;
+
+    /* Julian Date */
+    dj = dj0 + djd;
+
+    /* Equation of the equinoxes (converted to UT seconds) */
+    eqnx = eqeqnx (dj) / 1.002737909;
+
+    dj = dj - eqnx / 86400.0;
+    if (dj < dj0)
+	dj = dj + 1.0;
+
+    return (dj);
+}
+
+
+/* LST2DT - calculate current UT given Local Sidereal Time as hh.mmsss
+ *	    plus date as yyyy.mmdd
+ *	    Return UT time as hh.mmssss
+ */
+
+double
+lst2dt (date0, time0)
+
+double date0;	/* UT date as yyyy.mmdd */
+double time0;	/* LST as hh.mmssss */
+{
+    double gst;	/* Greenwich Sidereal Time in seconds since 0:00 */
+    double lst;	/* Local Sidereal Time in seconds since 0:00 */
+    double date1; /* UT date as yyyy.mmdd */
+    double time1; /* UT as hh.mmssss */
+    double tsec0, gst0, tsd, tsec;
+
+    /* Greenwich Sidereal Time at 0:00 UT */
+    tsec0 = dt2ts (date0, 0.0);
+    gst0 = ts2gst (tsec0);
+
+    /* Current Greenwich Sidereal Time in seconds */
+    /* (convert longitude from degrees to seconds of time) */
+    lst = dt2ts (0.0, time0);
+    gst = lst + (longitude * 240.0);
+
+    /* Time since 0:00 UT */
+    tsd = (gst - gst0) / 1.0027379093;
+
+    /* UT date and time */
+    tsec = tsec0 + tsd;
+    ts2dt (tsec, &date1, &time1);
+
+    return (time1);
+}
+
+
+/* TS2GST - calculate Greenwich Sidereal Time given Universal Time
+ *	    in seconds since 1951-01-01T0:00:00
+ *	    Return sidereal time of day in seconds
+ */
+
+double
+ts2gst (tsec)
+
+double tsec;	/* time since 1950.0 in UT seconds */
+{
+    double gst;	/* Greenwich Sidereal Time in seconds since 0:00 */
+    double tsd, eqnx, dj;
+    int its;
+
+    /* Elapsed time as of 0:00 UT */
+    if (tsec >= 0.0) {
+	its = (int) (tsec + 0.5);
+	tsd = (double) (its % 86400);
+	}
+    else {
+	its = (int) (-tsec + 0.5);
+	tsd = (double) (86400 - (its % 86400));
+	}
+
+    /* Mean sidereal time */
+    gst = ts2mst (tsec);
+
+    /* Equation of the equinoxes */
+    dj = ts2jd (tsec);
+    eqnx = eqeqnx (dj);
+
+    /* Apparent sidereal time at 0:00 ut */
+    gst = gst + eqnx;
+
+    /* Current sidereal time */
+    gst = gst + (tsd * 1.0027379093);
+    gst = dmod (gst,86400.0);
+
+    return (gst);
+}
+
+
+/* FD2MST-- convert from FITS date Mean Sidereal Time */
+
+char *
+fd2mst (string)
+
+char	*string;	/* FITS date string, which may be:
+			  fractional year
+			  dd/mm/yy (FITS standard before 2000)
+			  dd-mm-yy (nonstandard use before 2000)
+			  yyyy-mm-dd (FITS standard after 1999)
+			  yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double gsec, date, time, dj;
+
+    dj = fd2jd (string);
+    gsec = jd2mst (dj);
+    ts2dt (gsec, &date, &time);
+    date = 0.0;
+    return (dt2fd (date, time));
+}
+
+
+/* DT2MST-- convert from UT as yyyy.mmdd hh.mmssss to Mean Sidereal Time
+	    in the same format */
+
+void
+dt2mst (date, time)
+double  *date;  /* Date as yyyy.mmdd */
+double  *time;  /* Time as hh.mmssxxxx
+                 *if time<0, it is time as -(fraction of a day) */
+{
+    double date0, gsec, dj;
+    date0 = *date;
+    dj = dt2jd (*date, *time);
+    gsec = jd2mst (dj);
+    ts2dt (gsec, date, time);
+    *date = date0;
+    return;
+}
+
+
+/* TS2MST - calculate Greenwich Mean Sidereal Time given Universal Time
+ *	    in seconds since 1951-01-01T0:00:00
+ */
+
+double
+ts2mst (tsec)
+
+double tsec;	/* time since 1950.0 in UT seconds */
+{
+    double dj;
+
+    dj = ts2jd (tsec);
+    return (jd2mst (dj));
+}
+
+
+/* JD2MST - Julian Date to Greenwich Mean Sidereal Time using IAU 2000
+ *	    Return sideral time in seconds of time
+ *	    (from USNO NOVAS package
+ *	     http://aa.usno.navy.mil/software/novas/novas_info.html
+ */
+
+double
+jd2mst2 (dj)
+
+double	dj;	/* Julian Date */
+{
+    double dt, t, t2, t3, mst, st;
+
+    dt = dj - 2451545.0;
+    t = dt / 36525.0;
+    t2 = t * t;
+    t3 = t2 * t;
+
+    /* Compute Greenwich Mean Sidereal Time in seconds */
+    st = (8640184.812866 * t) +  (3155760000.0 * t) - (0.0000062 * t3)
+	 + (0.093104 * t2) + 67310.54841;
+
+    mst = dmod (st, 86400.0);
+    if (mst < 0.0)
+	mst = mst + 86400.0;
+    return (mst);
+}
+
+
+/* MJD2MST - Modified Julian Date to Greenwich Mean Sidereal Time using IAU 2000
+ *	    Return sideral time in seconds of time
+ *	    (from USNO NOVAS package
+ *	     http://aa.usno.navy.mil/software/novas/novas_info.html
+ */
+
+double
+mjd2mst (dj)
+
+double	dj;	/* Modified Julian Date */
+{
+    double dt, t, t2, t3, mst, st;
+
+    dt = dj - 51544.5;
+    t = dt / 36525.0;
+    t2 = t * t;
+    t3 = t2 * t;
+
+    /* Compute Greenwich Mean Sidereal Time in seconds */
+    st = (8640184.812866 * t) +  (3155760000.0 * t) - (0.0000062 * t3)
+	 + (0.093104 * t2) + 67310.54841;
+
+    mst = dmod (st, 86400.0);
+    if (mst < 0.0)
+	mst = mst + 86400.0;
+    return (mst);
+}
+
+
+/* JD2GST - Julian Date to Greenwich Sideral Time
+ *          Return sideral time in seconds of time
+ *	    (Jean Meeus, Astronomical Algorithms, Willmann-Bell, 1991, pp 83-84)
+ */
+
+double
+jd2gst (dj)
+
+double	dj;	/* Julian Date */
+{
+    double dj0, gmt, gst, tsd, eqnx, ssd, l0;
+    double ts2ss = 1.00273790935;
+    int ijd;
+
+    /* Julian date at 0:00 UT */
+    ijd = (int) dj;
+    dj0 = (double) ijd + 0.5;
+    if (dj0 > dj) dj0 = dj0 - 1.0;
+
+    /* Greenwich mean sidereal time at 0:00 UT in seconds */
+    l0 = longitude;
+    longitude = 0.0;
+    gmt = jd2mst (dj0);
+    longitude = l0;
+
+    /* Equation of the equinoxes */
+    eqnx = eqeqnx (dj);
+
+    /* Apparent sidereal time at 0:00 ut */
+    gst = gmt + eqnx;
+
+    /* UT seconds since 0:00 */
+    tsd = (dj - dj0) * 86400.0;
+    ssd = tsd * ts2ss;
+
+    /* Current sidereal time */
+    gst = gst + ssd;
+    gst = dmod (gst, 86400.0);
+
+    return (gst);
+}
+
+
+/* EQEQNX - Compute equation of the equinoxes for apparent sidereal time */
+
+double
+eqeqnx (dj)
+
+double	dj;	/* Julian Date */
+
+{
+    double dt, edj, dpsi, deps, obl, eqnx;
+    double rad2tsec = 13750.98708;
+
+    /* Convert UT to Ephemeris Time (TDB or TT)*/
+    dt = utdt (dj);
+    edj = dj + dt / 86400.0;
+
+    /* Nutation and obliquity */
+    compnut (edj, &dpsi, &deps, &obl);
+
+    /* Correct obliquity for nutation */
+    obl = obl + deps;
+
+    /* Equation of the equinoxes in seconds */
+    eqnx = (dpsi * cos (obl)) * rad2tsec;
+
+    return (eqnx);
+}
+
+
+
+/* JD2MST - Julian Date to Mean Sideral Time
+ *          Return sideral time in seconds of time
+ *	    (Jean Meeus, Astronomical Algorithms, Willmann-Bell, 1991, pp 83-84)
+ */
+
+double
+jd2mst (dj)
+
+double	dj;	/* Julian Date */
+{
+    double dt, t, mst;
+    double ts2ss = 1.00273790935;
+
+    dt = dj - 2451545.0;
+    t = dt / 36525.0;
+
+    /* Compute Greenwich mean sidereal time in degrees (Meeus, page 84) */
+    mst = 280.46061837 + (360.98564736629 * dt) + (0.000387933 * t * t) -
+	  (t * t * t / 38710000.0);
+
+    /* Keep degrees between 0 and 360 */
+    while (mst > 360.0)
+	mst = mst - 360.0;
+    while (mst < 0.0)
+	mst = mst + 360.0;
+
+    /* Convert to time in seconds  (3600 / 15) */
+    mst = mst * 240.0;
+
+    /* Subtract longitude (degrees to seconds of time) */
+    mst = mst - (240.0 * longitude);
+    if (mst < 0.0)
+	mst = mst + 86400.0;
+    else if (mst > 86400.0)
+	mst = mst - 86400.0;
+
+    return (mst);
+}
+
+
+/*  COMPNUT - Compute nutation using the IAU 2000b model */
+/*  Translated from Pat Wallace's Fortran subroutine iau_nut00b (June 26 2007)
+    into C by Doug Mink on September 5, 2008 */
+
+#define NLS	77 /* number of terms in the luni-solar nutation model */
+
+void
+compnut (dj, dpsi, deps, eps0)
+
+double	dj;	/* Julian Date */
+double *dpsi;   /* Nutation in longitude in radians (returned) */
+double *deps;   /* Nutation in obliquity in radians (returned) */
+double *eps0;   /* Mean obliquity in radians (returned) */
+
+/*  This routine is translated from the International Astronomical Union's
+ *  Fortran SOFA (Standards Of Fundamental Astronomy) software collection.
+ *
+ *  notes:
+ *
+ *  1) the nutation components in longitude and obliquity are in radians
+ *     and with respect to the equinox and ecliptic of date.  the
+ *     obliquity at j2000 is assumed to be the lieske et al. (1977) value
+ *     of 84381.448 arcsec.  (the errors that result from using this
+ *     routine with the iau 2006 value of 84381.406 arcsec can be
+ *     neglected.)
+ *
+ *     the nutation model consists only of luni-solar terms, but includes
+ *     also a fixed offset which compensates for certain long-period
+ *     planetary terms (note 7).
+ *
+ *  2) this routine is an implementation of the iau 2000b abridged
+ *     nutation model formally adopted by the iau general assembly in
+ *     2000.  the routine computes the mhb_2000_short luni-solar nutation
+ *     series (luzum 2001), but without the associated corrections for
+ *     the precession rate adjustments and the offset between the gcrs
+ *     and j2000 mean poles.
+ *
+ *  3) the full IAU 2000a (mhb2000) nutation model contains nearly 1400
+ *     terms.  the IAU 2000b model (mccarthy & luzum 2003) contains only
+ *     77 terms, plus additional simplifications, yet still delivers
+ *     results of 1 mas accuracy at present epochs.  this combination of
+ *     accuracy and size makes the IAU 2000b abridged nutation model
+ *     suitable for most practical applications.
+ *
+ *     the routine delivers a pole accurate to 1 mas from 1900 to 2100
+ *     (usually better than 1 mas, very occasionally just outside 1 mas).
+ *     the full IAU 2000a model, which is implemented in the routine
+ *     iau_nut00a (q.v.), delivers considerably greater accuracy at
+ *     current epochs;  however, to realize this improved accuracy,
+ *     corrections for the essentially unpredictable free-core-nutation
+ *     (fcn) must also be included.
+ *
+ *  4) the present routine provides classical nutation.  the
+ *     mhb_2000_short algorithm, from which it is adapted, deals also
+ *     with (i) the offsets between the gcrs and mean poles and (ii) the
+ *     adjustments in longitude and obliquity due to the changed
+ *     precession rates.  these additional functions, namely frame bias
+ *     and precession adjustments, are supported by the sofa routines
+ *     iau_bi00 and iau_pr00.
+ *
+ *  6) the mhb_2000_short algorithm also provides "total" nutations,
+ *     comprising the arithmetic sum of the frame bias, precession
+ *     adjustments, and nutation (luni-solar + planetary).  these total
+ *     nutations can be used in combination with an existing IAU 1976
+ *     precession implementation, such as iau_pmat76, to deliver gcrs-to-
+ *     true predictions of mas accuracy at current epochs.  however, for
+ *     symmetry with the iau_nut00a routine (q.v. for the reasons), the
+ *     sofa routines do not generate the "total nutations" directly.
+ *     should they be required, they could of course easily be generated
+ *     by calling iau_bi00, iau_pr00 and the present routine and adding
+ *     the results.
+ *
+ *  7) the IAU 2000b model includes "planetary bias" terms that are fixed
+ *     in size but compensate for long-period nutations.  the amplitudes
+ *     quoted in mccarthy & luzum (2003), namely dpsi = -1.5835 mas and
+ *     depsilon = +1.6339 mas, are optimized for the "total nutations"
+ *     method described in note 6.  the luzum (2001) values used in this
+ *     sofa implementation, namely -0.135 mas and +0.388 mas, are
+ *     optimized for the "rigorous" method, where frame bias, precession
+ *     and nutation are applied separately and in that order.  during the
+ *     interval 1995-2050, the sofa implementation delivers a maximum
+ *     error of 1.001 mas (not including fcn).
+ *
+ *  References from original Fortran subroutines:
+ *
+ *     Hilton, J. et al., 2006, Celest.Mech.Dyn.Astron. 94, 351
+ *
+ *     Lieske, J.H., Lederle, T., Fricke, W., Morando, B., "Expressions
+ *     for the precession quantities based upon the IAU 1976 system of
+ *     astronomical constants", Astron.Astrophys. 58, 1-2, 1-16. (1977)
+ *
+ *     Luzum, B., private communication, 2001 (Fortran code
+ *     mhb_2000_short)
+ *
+ *     McCarthy, D.D. & Luzum, B.J., "An abridged model of the
+ *     precession-nutation of the celestial pole", Cel.Mech.Dyn.Astron.
+ *     85, 37-49 (2003)
+ *
+ *     Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M.,
+ *     Francou, G., Laskar, J., Astron.Astrophys. 282, 663-683 (1994)
+ *
+ */
+
+{ 
+    double as2r = 0.000004848136811095359935899141; /* arcseconds to radians */
+
+    double dmas2r = as2r / 1000.0;	/* milliarcseconds to radians */
+
+    double as2pi = 1296000.0;	/* arc seconds in a full circle */
+
+    double d2pi = 6.283185307179586476925287; /* 2pi */
+
+    double u2r = as2r / 10000000.0;  /* units of 0.1 microarcsecond to radians */
+
+    double dj0 = 2451545.0;	/* reference epoch (j2000), jd */
+
+    double djc = 36525.0;	/* Days per julian century */
+
+    /*  Miscellaneous */
+    double t, el, elp, f, d, om, arg, dp, de, sarg, carg;
+    double dpsils, depsls, dpsipl, depspl;
+    int i, j;
+
+    int nls = NLS; /* number of terms in the luni-solar nutation model */
+
+    /* Fixed offset in lieu of planetary terms (radians) */
+    double dpplan = - 0.135 * dmas2r;
+    double deplan = + 0.388 * dmas2r;
+
+/* Tables of argument and term coefficients */
+
+    /* Coefficients for fundamental arguments */
+    /* Luni-solar argument multipliers: */
+    /*       l     l'    f     d     om */
+static int nals[5*NLS]=
+	    {0,    0,    0,    0,    1,
+             0,    0,    2,   -2,    2,
+             0,    0,    2,    0,    2,
+             0,    0,    0,    0,    2,
+             0,    1,    0,    0,    0,
+             0,    1,    2,   -2,    2,
+             1,    0,    0,    0,    0,
+             0,    0,    2,    0,    1,
+             1,    0,    2,    0,    2,
+             0,   -1,    2,   -2,    2,
+             0,    0,    2,   -2,    1,
+            -1,    0,    2,    0,    2,
+            -1,    0,    0,    2,    0,
+             1,    0,    0,    0,    1,
+            -1,    0,    0,    0,    1,
+            -1,    0,    2,    2,    2,
+             1,    0,    2,    0,    1,
+            -2,    0,    2,    0,    1,
+             0,    0,    0,    2,    0,
+             0,    0,    2,    2,    2,
+             0,   -2,    2,   -2,    2,
+            -2,    0,    0,    2,    0,
+             2,    0,    2,    0,    2,
+             1,    0,    2,   -2,    2,
+            -1,    0,    2,    0,    1,
+             2,    0,    0,    0,    0,
+             0,    0,    2,    0,    0,
+             0,    1,    0,    0,    1,
+            -1,    0,    0,    2,    1,
+             0,    2,    2,   -2,    2,
+             0,    0,   -2,    2,    0,
+             1,    0,    0,   -2,    1,
+             0,   -1,    0,    0,    1,
+            -1,    0,    2,    2,    1,
+             0,    2,    0,    0,    0,
+             1,    0,    2,    2,    2,
+            -2,    0,    2,    0,    0,
+             0,    1,    2,    0,    2,
+             0,    0,    2,    2,    1,
+             0,   -1,    2,    0,    2,
+             0,    0,    0,    2,    1,
+             1,    0,    2,   -2,    1,
+             2,    0,    2,   -2,    2,
+            -2,    0,    0,    2,    1,
+             2,    0,    2,    0,    1,
+             0,   -1,    2,   -2,    1,
+             0,    0,    0,   -2,    1,
+            -1,   -1,    0,    2,    0,
+             2,    0,    0,   -2,    1,
+             1,    0,    0,    2,    0,
+             0,    1,    2,   -2,    1,
+             1,   -1,    0,    0,    0,
+            -2,    0,    2,    0,    2,
+             3,    0,    2,    0,    2,
+             0,   -1,    0,    2,    0,
+             1,   -1,    2,    0,    2,
+             0,    0,    0,    1,    0,
+            -1,   -1,    2,    2,    2,
+            -1,    0,    2,    0,    0,
+             0,   -1,    2,    2,    2,
+            -2,    0,    0,    0,    1,
+             1,    1,    2,    0,    2,
+             2,    0,    0,    0,    1,
+            -1,    1,    0,    1,    0,
+             1,    1,    0,    0,    0,
+             1,    0,    2,    0,    0,
+            -1,    0,    2,   -2,    1,
+             1,    0,    0,    0,    2,
+            -1,    0,    0,    1,    0,
+             0,    0,    2,    1,    2,
+            -1,    0,    2,    4,    2,
+            -1,    1,    0,    1,    1,
+             0,   -2,    2,   -2,    1,
+             1,    0,    2,    2,    1,
+            -2,    0,    2,    2,    2,
+            -1,    0,    0,    0,    2,
+             1,    1,    2,   -2,    2};
+
+    /* Luni-solar nutation coefficients, in 1e-7 arcsec */
+    /* longitude (sin, t*sin, cos), obliquity (cos, t*cos, sin) */
+static double cls[6*NLS]=
+   {-172064161.0, -174666.0,  33386.0, 92052331.0,  9086.0, 15377.0,
+     -13170906.0,   -1675.0, -13696.0,  5730336.0, -3015.0, -4587.0,
+      -2276413.0,    -234.0,   2796.0,   978459.0,  -485.0,  1374.0,
+       2074554.0,     207.0,   -698.0,  -897492.0,   470.0,  -291.0,
+       1475877.0,   -3633.0,  11817.0,    73871.0,  -184.0, -1924.0,
+       -516821.0,    1226.0,   -524.0,   224386.0,  -677.0,  -174.0,
+        711159.0,      73.0,   -872.0,    -6750.0,     0.0,   358.0,
+       -387298.0,    -367.0,    380.0,   200728.0,    18.0,   318.0,
+       -301461.0,     -36.0,    816.0,   129025.0,   -63.0,   367.0,
+        215829.0,    -494.0,    111.0,   -95929.0,   299.0,   132.0,
+        128227.0,     137.0,    181.0,   -68982.0,    -9.0,    39.0,
+        123457.0,      11.0,     19.0,   -53311.0,    32.0,    -4.0,
+        156994.0,      10.0,   -168.0,    -1235.0,     0.0,    82.0,
+         63110.0,      63.0,     27.0,   -33228.0,     0.0,    -9.0,
+        -57976.0,     -63.0,   -189.0,    31429.0,     0.0,   -75.0,
+        -59641.0,     -11.0,    149.0,    25543.0,   -11.0,    66.0,
+        -51613.0,     -42.0,    129.0,    26366.0,     0.0,    78.0,
+         45893.0,      50.0,     31.0,   -24236.0,   -10.0,    20.0,
+         63384.0,      11.0,   -150.0,    -1220.0,     0.0,    29.0,
+        -38571.0,      -1.0,    158.0,    16452.0,   -11.0,    68.0,
+         32481.0,       0.0,      0.0,   -13870.0,     0.0,     0.0,
+        -47722.0,       0.0,    -18.0,      477.0,     0.0,   -25.0,
+        -31046.0,      -1.0,    131.0,    13238.0,   -11.0,    59.0,
+         28593.0,       0.0,     -1.0,   -12338.0,    10.0,    -3.0,
+         20441.0,      21.0,     10.0,   -10758.0,     0.0,    -3.0,
+         29243.0,       0.0,    -74.0,     -609.0,     0.0,    13.0,
+         25887.0,       0.0,    -66.0,     -550.0,     0.0,    11.0,
+        -14053.0,     -25.0,     79.0,     8551.0,    -2.0,   -45.0,
+         15164.0,      10.0,     11.0,    -8001.0,     0.0,    -1.0,
+        -15794.0,      72.0,    -16.0,     6850.0,   -42.0,    -5.0,
+         21783.0,       0.0,     13.0,     -167.0,     0.0,    13.0,
+        -12873.0,     -10.0,    -37.0,     6953.0,     0.0,   -14.0,
+        -12654.0,      11.0,     63.0,     6415.0,     0.0,    26.0,
+        -10204.0,       0.0,     25.0,     5222.0,     0.0,    15.0,
+         16707.0,     -85.0,    -10.0,      168.0,    -1.0,    10.0,
+         -7691.0,       0.0,     44.0,     3268.0,     0.0,    19.0,
+        -11024.0,       0.0,    -14.0,      104.0,     0.0,     2.0,
+          7566.0,     -21.0,    -11.0,    -3250.0,     0.0,    -5.0,
+         -6637.0,     -11.0,     25.0,     3353.0,     0.0,    14.0,
+         -7141.0,      21.0,      8.0,     3070.0,     0.0,     4.0,
+         -6302.0,     -11.0,      2.0,     3272.0,     0.0,     4.0,
+          5800.0,      10.0,      2.0,    -3045.0,     0.0,    -1.0,
+          6443.0,       0.0,     -7.0,    -2768.0,     0.0,    -4.0,
+         -5774.0,     -11.0,    -15.0,     3041.0,     0.0,    -5.0,
+         -5350.0,       0.0,     21.0,     2695.0,     0.0,    12.0,
+         -4752.0,     -11.0,     -3.0,     2719.0,     0.0,    -3.0,
+         -4940.0,     -11.0,    -21.0,     2720.0,     0.0,    -9.0,
+          7350.0,       0.0,     -8.0,      -51.0,     0.0,     4.0,
+          4065.0,       0.0,      6.0,    -2206.0,     0.0,     1.0,
+          6579.0,       0.0,    -24.0,     -199.0,     0.0,     2.0,
+          3579.0,       0.0,      5.0,    -1900.0,     0.0,     1.0,
+          4725.0,       0.0,     -6.0,      -41.0,     0.0,     3.0,
+         -3075.0,       0.0,     -2.0,     1313.0,     0.0,    -1.0,
+         -2904.0,       0.0,     15.0,     1233.0,     0.0,     7.0,
+          4348.0,       0.0,    -10.0,      -81.0,     0.0,     2.0,
+         -2878.0,       0.0,      8.0,     1232.0,     0.0,     4.0,
+         -4230.0,       0.0,      5.0,      -20.0,     0.0,    -2.0,
+         -2819.0,       0.0,      7.0,     1207.0,     0.0,     3.0,
+         -4056.0,       0.0,      5.0,       40.0,     0.0,    -2.0,
+         -2647.0,       0.0,     11.0,     1129.0,     0.0,     5.0,
+         -2294.0,       0.0,    -10.0,     1266.0,     0.0,    -4.0,
+          2481.0,       0.0,     -7.0,    -1062.0,     0.0,    -3.0,
+          2179.0,       0.0,     -2.0,    -1129.0,     0.0,    -2.0,
+          3276.0,       0.0,      1.0,       -9.0,     0.0,     0.0,
+         -3389.0,       0.0,      5.0,       35.0,     0.0,    -2.0,
+          3339.0,       0.0,    -13.0,     -107.0,     0.0,     1.0,
+         -1987.0,       0.0,     -6.0,     1073.0,     0.0,    -2.0,
+         -1981.0,       0.0,      0.0,      854.0,     0.0,     0.0,
+          4026.0,       0.0,   -353.0,     -553.0,     0.0,  -139.0,
+          1660.0,       0.0,     -5.0,     -710.0,     0.0,    -2.0,
+         -1521.0,       0.0,      9.0,      647.0,     0.0,     4.0,
+          1314.0,       0.0,      0.0,     -700.0,     0.0,     0.0,
+         -1283.0,       0.0,      0.0,      672.0,     0.0,     0.0,
+         -1331.0,       0.0,      8.0,      663.0,     0.0,     4.0,
+          1383.0,       0.0,     -2.0,     -594.0,     0.0,    -2.0,
+          1405.0,       0.0,      4.0,     -610.0,     0.0,     2.0,
+          1290.0,       0.0,      0.0,     -556.0,     0.0,     0.0};
+
+    /* Interval between fundamental epoch J2000.0 and given date (JC) */
+    t = (dj - dj0) / djc;
+
+/* Luni-solar nutation */
+
+/* Fundamental (delaunay) arguments from Simon et al. (1994) */
+
+    /* Mean anomaly of the moon */
+    el  = fmod (485868.249036 + (1717915923.2178 * t), as2pi) * as2r;
+
+    /* Mean anomaly of the sun */
+    elp = fmod (1287104.79305 + (129596581.0481 * t), as2pi) * as2r;
+
+    /* Mean argument of the latitude of the moon */
+    f   = fmod (335779.526232 + (1739527262.8478 * t), as2pi) * as2r;
+
+    /* Mean elongation of the moon from the sun */
+    d   = fmod (1072260.70369 + (1602961601.2090 * t), as2pi ) * as2r;
+
+    /* Mean longitude of the ascending node of the moon */
+    om  = fmod (450160.398036 - (6962890.5431 * t), as2pi ) * as2r;
+
+    /* Initialize the nutation values */
+    dp = 0.0;
+    de = 0.0;
+
+    /* Summation of luni-solar nutation series (in reverse order) */
+    for (i = nls; i > 0; i=i-1) {
+	j = i - 1;
+
+	/* Argument and functions */
+	arg = fmod ( (double) (nals[5*j]) * el +
+		     (double) (nals[1+5*j]) * elp +
+		     (double) (nals[2+5*j]) * f +
+		     (double) (nals[3+5*j]) * d +
+		     (double) (nals[4+5*j]) * om, d2pi);
+	sarg = sin (arg);
+	carg = cos (arg);
+
+	/* Terms */
+	dp = dp + (cls[6*j] + cls[1+6*j] * t) * sarg + cls[2+6*j] * carg;
+	de = de + (cls[3+6*j] + cls[4+6*j] * t) * carg + cls[5+6*j] * sarg;
+	}
+
+    /* Convert from 0.1 microarcsec units to radians */
+    dpsils = dp * u2r;
+    depsls = de * u2r;
+
+/* In lieu of planetary nutation */
+
+    /* Fixed offset to correct for missing terms in truncated series */
+    dpsipl = dpplan;
+    depspl = deplan;
+
+/* Results */
+
+    /* Add luni-solar and planetary components */
+    *dpsi = dpsils + dpsipl;
+    *deps = depsls + depspl;
+
+    /* Mean Obliquity in radians (IAU 2006, Hilton, et al.) */
+    *eps0 = ( 84381.406     +
+	    ( -46.836769    +
+	    (  -0.0001831   +
+	    (   0.00200340  +
+	    (  -0.000000576 +
+	    (  -0.0000000434 ) * t ) * t ) * t ) * t ) * t ) * as2r;
+}
+
+
+/* ISDATE - Return 1 if string is an old or ISO FITS standard date */
+
+int
+isdate (string)
+
+char	*string; /* Possible FITS date string, which may be:
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+
+{
+    int iyr = 0;	/* year (returned) */
+    int imon = 0;	/* month (returned) */
+    int iday = 0;	/* day (returned) */
+    int i;
+    char *sstr, *dstr, *tstr, *nval;
+
+    /* Translate string from ASCII to binary */
+    if (string == NULL) 
+	return (0);
+
+    sstr = strchr (string,'/');
+    dstr = strchr (string,'-');
+    if (dstr == string)
+	dstr = strchr (string+1,'-');
+    tstr = strchr (string,'T');
+
+    /* Original FITS date format: dd/mm/yy */
+    if (sstr > string) {
+	*sstr = '\0';
+	iday = (int) atof (string);
+	*sstr = '/';
+	nval = sstr + 1;
+	sstr = strchr (nval,'/');
+	if (sstr == NULL)
+	    sstr = strchr (nval,'-');
+	if (sstr > string) {
+	    *sstr = '\0';
+	    imon = (int) atof (nval);
+	    *sstr = '/';
+	    nval = sstr + 1;
+	    iyr = (int) atof (nval);
+	    if (iyr < 1000)
+		iyr = iyr + 1900;
+	    }
+	if (imon > 0 && iday > 0)
+	    return (1);
+	else
+	    return (0);
+	}
+
+    /* New FITS date format: yyyy-mm-ddThh:mm:ss[.sss] */
+    else if (dstr > string) {
+	*dstr = '\0';
+	iyr = (int) atof (string);
+	nval = dstr + 1;
+	*dstr = '-';
+	dstr = strchr (nval,'-');
+	imon = 0;
+	iday = 0;
+
+	/* Decode year, month, and day */
+	if (dstr > string) {
+	    *dstr = '\0';
+	    imon = (int) atof (nval);
+	    *dstr = '-';
+	    nval = dstr + 1;
+	    if (tstr > string)
+		*tstr = '\0';
+	    iday = (int) atof (nval);
+	    if (tstr > string)
+		*tstr = 'T';
+	    }
+
+	/* If day is > 31, it is really year in old format */
+	if (iday > 31) {
+	    i = iyr;
+	    if (iday < 100)
+		iyr = iday + 1900;
+	    else
+		iyr = iday;
+	    iday = i;
+	    }
+	if (imon > 0 && iday > 0)
+	    return (1);
+	else
+	    return (0);
+	}
+
+    /* If FITS date is entered as an epoch, return 0 anyway */
+    else
+	return (0);
+}
+
+
+/* Round seconds and make sure date and time numbers are within limits */
+
+static void
+fixdate (iyr, imon, iday, ihr, imn, sec, ndsec)
+
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+{
+    double days;
+
+    /* Round seconds to 0 - 4 decimal places (no rounding if <0, >4) */
+    if (ndsec == 0)
+	*sec = dint (*sec + 0.5);
+    else if (ndsec < 2)
+	*sec = dint (*sec * 10.0 + 0.5) / 10.0;
+    else if (ndsec < 3)
+	*sec = dint (*sec * 100.0 + 0.5) / 100.0;
+    else if (ndsec < 4)
+	*sec = dint (*sec * 1000.0 + 0.5) / 1000.0;
+    else if (ndsec < 5)
+	*sec = dint (*sec * 10000.0 + 0.5) / 10000.0;
+
+    /* Adjust minutes and hours */
+    if (*sec > 60.0) {
+	*sec = *sec - 60.0;
+	*imn = *imn + 1;
+	}
+    if (*imn > 60) {
+	*imn = *imn - 60;
+	*ihr = *ihr + 1;
+	}
+
+    /* Return if no date */
+    if (*iyr == 0 && *imon == 0 && *iday == 0)
+	return;
+
+   /* Adjust date */
+    if (*ihr > 23) {
+	*ihr = *ihr - 24;
+	*iday = *iday + 1;
+	}
+    days = caldays (*iyr, *imon);
+    if (*iday > days) {
+	*iday = *iday - days;
+	*imon = *imon + 1;
+	}
+    if (*iday < 1) {
+	*imon = *imon - 1;
+	if (*imon < 1) {
+	    *imon = *imon + 12;
+	    *iyr = *iyr - 1;
+	    }
+	days = caldays (*iyr, *imon);
+	*iday = *iday + days;
+	}
+    if (*imon < 1) {
+	*imon = *imon + 12;
+	*iyr = *iyr - 1;
+	days = caldays (*iyr, *imon);
+	if (*iday > days) {
+	    *iday = *iday - days;
+	    *imon = *imon + 1;
+	    }
+	}
+    if (*imon > 12) {
+	*imon = *imon - 12;
+	*iyr = *iyr + 1;
+	}
+    return;
+}
+
+
+/* Calculate days in month 1-12 given year (Gregorian calendar only) */
+
+static int
+caldays (year, month)
+
+int	year;	/* 4-digit year */
+int	month;	/* Month (1=January, 2=February, etc.) */
+{
+    if (month < 1) {
+	month = month + 12;
+	year = year + 1;
+	}
+    if (month > 12) {
+	month = month - 12;
+	year = year + 1;
+	}
+    switch (month) {
+	case 1:
+	    return (31);
+	case 2:
+	    if (year%400 == 0)
+		return (29);
+	    else if (year%100 == 0)
+		return (28);
+	    else if (year%4 == 0)
+		return (29);
+	    else
+		return (28);
+	case 3:
+	    return (31);
+	case 4:
+	    return (30);
+	case 5:
+	    return (31);
+	case 6:
+	    return (30);
+	case 7:
+	    return (31);
+	case 8:
+	    return (31);
+	case 9:
+	    return (30);
+	case 10:
+	    return (31);
+	case 11:
+	    return (30);
+	case 12:
+	    return (31);
+	default:
+	    return (0);
+	}
+}
+
+
+static double
+dint (dnum)
+
+double	dnum;
+{
+    double dn;
+
+    if (dnum < 0.0)
+	dn = -floor (-dnum);
+    else
+	dn = floor (dnum);
+    return (dn);
+}
+
+
+static double
+dmod (dnum, dm)
+
+double	dnum, dm;
+{
+    double dnumx, dnumi, dnumf;
+    if (dnum < 0.0)
+	dnumx = -dnum;
+    else
+	dnumx = dnum;
+    dnumi = dint (dnumx / dm);
+    if (dnum < 0.0)
+	dnumf = dnum + (dnumi * dm);
+    else if (dnum > 0.0)
+	dnumf = dnum - (dnumi * dm);
+    else
+	dnumf = 0.0;
+    return (dnumf);
+}
+
+/* Jul  1 1999	New file, based on iolib/jcon.f and iolib/vcon.f and hgetdate()
+ * Oct 21 1999	Fix declarations after lint
+ * Oct 27 1999	Fix bug to return epoch if fractional year input
+ * Dec  9 1999	Fix bug in ts2jd() found by Pete Ratzlaff (SAO)
+ * Dec 17 1999	Add all unimplemented conversions
+ * Dec 20 1999	Add isdate(); leave date, time strings unchanged in fd2i()
+ * Dec 20 1999	Make all fd2*() subroutines deal with time alone
+ *
+ * Jan  3 2000	In old FITS format, year 100 is assumed to be 2000
+ * Jan 11 2000	Fix epoch to date conversion so .0 is 0:00, not 12:00
+ * Jan 21 2000	Add separate Besselian and Julian epoch computations
+ * Jan 28 2000	Add Modified Julian Date conversions
+ * Mar  2 2000	Implement decimal places for FITS date string
+ * Mar 14 2000	Fix bug in dealing with 2000-02-29 in ts2i()
+ * Mar 22 2000	Add lt2* and ut2* to get current time as local and UT
+ * Mar 24 2000	Fix calloc() calls
+ * Mar 24 2000	Add tsi2* and tsu2* to convert IRAF and Unix seconds
+ * May  1 2000	In old FITS format, all years < 1000 get 1900 added to them
+ * Aug  1 2000	Make ep2jd and jd2ep consistently starting at 1/1 0:00
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * May 21 2001	Add day of year conversions
+ * May 25 2001	Allow fraction of day in FITS date instead of time
+ *
+ * Apr  8 2002	Change all long declaration to time_t
+ * May 13 2002	Fix bugs found by lint
+ * Jul  5 2002	Fix bug in fixdate() so fractional seconds come out
+ * Jul  8 2002	Fix rounding bug in t2i()
+ * Jul  8 2002	Try Fliegel and Van Flandern's algorithm for JD to UT date
+ * Jul  8 2002	If first character of string is -, check for other -'s in isdate
+ * Sep 10 2002	Add ET/TDT/TT conversion from UT subroutines
+ * Sep 10 2002	Add sidereal time conversions
+ *
+ * Jan 30 2003	Fix typo in ts2gst()
+ * Mar  7 2003	Add conversions for heliocentric julian dates
+ * May 20 2003	Declare nd in setdatedec()
+ * Jul 18 2003	Add code to parse Las Campanas dates
+ *
+ * Mar 24 2004	If ndec > 0, add UT to FITS date even if it is 0:00:00
+ *
+ * Oct 14 2005	Add tsd2fd() and tsd2dt()
+ *
+ * May  3 2006	Drop declaration of unused variables
+ * Jun 20 2006	Initialized uninitialized variables
+ * Aug  2 2006	Add local sidereal time
+ * Sep 13 2006	Add more local sidereal time subroutines
+ * Oct  2 2006	Add UT to old FITS date conversions
+ * Oct  6 2006	Add eqeqnx() to compute equation of the equinoxes
+ *
+ * Jan  8 2007	Remove unused variables
+ *
+ * Sep  5 2008	Replace nutation with IAU 2006 model translated from SOFA
+ * Sep  9 2008	Add ang2hr(), ang2deg(), hr2ang(), deg2ang()
+ * Sep 10 2008	Add longitude to mean standard time (default = Greenwich)
+ * Oct  8 2008	Clean up sidereal time computations
+ *
+ * Sep 24 2009	Add end to comment "Coefficients for fundamental arguments"
+ */
diff --git a/Code/src/libwcs/dateutil0.c b/Code/src/libwcs/dateutil0.c
new file mode 100644
index 0000000000000000000000000000000000000000..5fa60d4f235a0375eeeff87618bda5694d5e6959
--- /dev/null
+++ b/Code/src/libwcs/dateutil0.c
@@ -0,0 +1,4450 @@
+/*** File libwcs/dateutil.c
+ *** September 9, 2008
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1999-2008
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* Date and time conversion routines using the following conventions:
+  ang = Angle in fractional degrees
+  deg = Angle in degrees as dd:mm:ss.ss
+  doy = 2 floating point numbers: year and day, including fraction, of year
+	*** First day of year is 1, not zero.
+   dt = 2 floating point numbers: yyyy.mmdd, hh.mmssssss
+   ep = fractional year, often epoch of a position including proper motion
+  epb = Besselian epoch = 365.242198781-day years based on 1900.0
+  epj = Julian epoch = 365.25-day years based on 2000.0
+   fd = FITS date string which may be any of the following:
+	yyyy.ffff (fractional year)
+	dd/mm/yy (FITS standard before 2000)
+	dd-mm-yy (nonstandard FITS use before 2000)
+	yyyy-mm-dd (FITS standard after 1999)
+	yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999)
+   hr = Sexigesimal hours as hh:mm:dd.ss
+   jd = Julian Date
+   lt = Local time
+  mjd = modified Julian Date = JD - 2400000.5
+  ofd = FITS date string (dd/mm/yy before 2000, else no return)
+ time = use fd2* with no date to convert time as hh:mm:ss.ss to sec, day, year
+   ts = UT seconds since 1950-01-01T00:00 (used for ephemeris computations)
+  tsi = local seconds since 1980-01-01T00:00 (used by IRAF as a time tag)
+  tsu = UT seconds since 1970-01-01T00:00 (used as Unix system time)
+  tsd = UT seconds of current day
+   ut = Universal Time (UTC)
+   et = Ephemeris Time (or TDB or TT)
+  mst = Mean Greenwich Sidereal Time
+  gst = Greenwich Sidereal Time (includes nutation)
+  lst = Local Sidereal Time (includes nutation) (longitude must be set)
+  hjd = Heliocentric Julian Date
+ mhjd = modified Heliocentric Julian Date = HJD - 2400000.5
+
+ * ang2hr (angle)
+ *	Convert angle in decimal floating point degrees to hours as hh:mm:ss.ss
+ * ang2deg (angle)
+ *	Convert angle in decimal floating point degrees to degrees as dd:mm:ss.ss
+ * deg2ang (angle as dd:mm:ss.ss)
+ *	Convert angle in degrees as dd:mm:ss.ss to decimal floating point degrees
+ * ang2hr (angle)
+ *	Convert angle in hours as hh:mm:ss.ss to decimal floating point degrees
+ *
+ * doy2dt (year, doy, date, time)
+ *	Convert year and day of year to date as yyyy.ddmm and time as hh.mmsss
+ * doy2ep, doy2epb, doy2epj (date, time)
+ *	Convert year and day of year to fractional year
+ * doy2fd (year, doy)
+ *	Convert year and day of year to FITS date string
+ * doy2mjd (year, doy)
+ *	Convert year and day of year to modified Julian date
+ *
+ * dt2doy (date, time, year, doy)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to year and day of year
+ * dt2ep, dt2epb, dt2epj (date, time)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to fractional year
+ * dt2fd (date, time)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to FITS date string
+ * dt2i (date,time,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert yyyy.mmdd hh.mmssss to year month day hours minutes seconds
+ * dt2jd (date,time)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to Julian date
+ * dt2mjd (date,time)
+ *	Convert date as yyyy.ddmm and time as hh.mmsss to modified Julian date
+ * dt2ts (date,time)
+ *	Convert date (yyyy.ddmm) and time (hh.mmsss) to seconds since 1950-01-01
+ * dt2tsi (date,time)
+ *	Convert date (yyyy.ddmm) and time (hh.mmsss) to seconds since 1980-01-01
+ * dt2tsu (date,time)
+ *	Convert date (yyyy.ddmm) and time (hh.mmsss) to seconds since 1970-01-01
+ *
+ * ep2dt, epb2dt, epj2dt (epoch,date, time)
+ *	Convert fractional year to date as yyyy.ddmm and time as hh.mmsss
+ * ep2fd, epb2fd, epj2fd (epoch)
+ *	Convert epoch to FITS ISO date string
+ * ep2i, epb2i, epj2i (epoch,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert fractional year to year month day hours minutes seconds
+ * ep2jd, epb2jd, epj2jd (epoch)
+ *	Convert fractional year as used in epoch to Julian date
+ * ep2mjd, epb2mjd, epj2mjd (epoch)
+ *	Convert fractional year as used in epoch to modified Julian date
+ * ep2ts, epb2ts, epj2ts (epoch)
+ *	Convert fractional year to seconds since 1950.0
+ *
+ * et2fd (string)
+ *	Convert from ET (or TDT or TT) in FITS format to UT in FITS format
+ * fd2et (string)
+ *	Convert from UT in FITS format to ET (or TDT or TT) in FITS format
+ * jd2jed (dj)
+ *	Convert from Julian Date to Julian Ephemeris Date
+ * jed2jd (dj)
+ *	Convert from Julian Ephemeris Date to Julian Date
+ * dt2et (date, time)
+ *	Convert date (yyyy.ddmm) and time (hh.mmsss) to ephemeris time
+ * edt2dt (date, time)
+ *	Convert ephemeris date (yyyy.ddmm) and time (hh.mmsss) to UT
+ * ts2ets (tsec)
+ *	Convert from UT in seconds since 1950-01-01 to ET in same format
+ * ets2ts (tsec)
+ *	Convert from ET in seconds since 1950-01-01 to UT in same format
+ *
+ * fd2ep, fd2epb, fd2epj (string)
+ *	Convert FITS date string to fractional year
+ *	Convert time alone to fraction of Besselian year
+ * fd2doy (string, year, doy)
+ *	Convert FITS standard date string to year and day of year
+ * fd2dt (string, date, time)
+ *	Convert FITS date string to date as yyyy.ddmm and time as hh.mmsss
+ *	Convert time alone to hh.mmssss with date set to 0.0
+ * fd2i (string,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert FITS standard date string to year month day hours min sec
+ *	Convert time alone to hours min sec, year month day are zero
+ * fd2jd (string)
+ *	Convert FITS standard date string to Julian date
+ *	Convert time alone to fraction of day
+ * fd2mjd (string)
+ *	Convert FITS standard date string to modified Julian date
+ * fd2ts (string)
+ *	Convert FITS standard date string to seconds since 1950.0
+ *	Convert time alone to seconds of day
+ * fd2fd (string)
+ *	Convert FITS standard date string to ISO FITS date string
+ * fd2of (string)
+ *	Convert FITS standard date string to old-format FITS date and time
+ * fd2ofd (string)
+ *	Convert FITS standard date string to old-format FITS date string
+ * fd2oft (string)
+ *	Convert time part of FITS standard date string to FITS date string
+ *
+ * jd2doy (dj, year, doy)
+ *	Convert Julian date to year and day of year
+ * jd2dt (dj,date,time)
+ *	Convert Julian date to date as yyyy.mmdd and time as hh.mmssss
+ * jd2ep, jd2epb, jd2epj (dj)
+ *	Convert Julian date to fractional year as used in epoch
+ * jd2fd (dj)
+ *	Convert Julian date to FITS ISO date string
+ * jd2i (dj,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert Julian date to year month day hours min sec
+ * jd2mjd (dj)
+ *	Convert Julian date to modified Julian date
+ * jd2ts (dj)
+ *	Convert Julian day to seconds since 1950.0
+ *
+ * lt2dt()
+ *	Return local time as yyyy.mmdd and time as hh.mmssss
+ * lt2fd()
+ *	Return local time as FITS ISO date string
+ * lt2tsi()
+ *	Return local time as IRAF seconds since 1980-01-01 00:00
+ * lt2tsu()
+ *	Return local time as Unix seconds since 1970-01-01 00:00
+ * lt2ts()
+ *	Return local time as Unix seconds since 1950-01-01 00:00
+ *
+ * mjd2doy (dj,year,doy)
+ *	Convert modified Julian date to date as year and day of year
+ * mjd2dt (dj,date,time)
+ *	Convert modified Julian date to date as yyyy.mmdd and time as hh.mmssss
+ * mjd2ep, mjd2epb, mjd2epj (dj)
+ *	Convert modified Julian date to fractional year as used in epoch
+ * mjd2fd (dj)
+ *	Convert modified Julian date to FITS ISO date string
+ * mjd2i (dj,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert modified Julian date to year month day hours min sec
+ * mjd2jd (dj)
+ *	Convert modified Julian date to Julian date
+ * mjd2ts (dj)
+ *	Convert modified Julian day to seconds since 1950.0
+ *
+ * ts2dt (tsec,date,time)
+ *	Convert seconds since 1950.0 to date as yyyy.ddmm and time as hh.mmsss
+ * ts2ep, ts2epb, ts2epj (tsec)
+ *	Convert seconds since 1950.0 to fractional year
+ * ts2fd (tsec)
+ *	Convert seconds since 1950.0 to FITS standard date string
+ * ts2i (tsec,iyr,imon,iday,ihr,imn,sec, ndsec)
+ *	Convert sec since 1950.0 to year month day hours minutes seconds
+ * ts2jd (tsec)
+ *	Convert seconds since 1950.0 to Julian date
+ * ts2mjd (tsec)
+ *	Convert seconds since 1950.0 to modified Julian date
+ * tsi2fd (tsec)
+ *	Convert seconds since 1980-01-01 to FITS standard date string
+ * tsi2dt (tsec,date,time)
+ *	Convert seconds since 1980-01-01 to date as yyyy.ddmm, time as hh.mmsss
+ * tsu2fd (tsec)
+ *	Convert seconds since 1970-01-01 to FITS standard date string
+ * tsu2tsi (tsec)
+ *	Convert UT seconds since 1970-01-01 to local seconds since 1980-01-01
+ * tsu2dt (tsec,date,time)
+ *	Convert seconds since 1970-01-01 to date as yyyy.ddmm, time as hh.mmsss
+ *
+ * tsd2fd (tsec)
+ *	Convert seconds since start of day to FITS time, hh:mm:ss.ss
+ * tsd2dt (tsec)
+ *	Convert seconds since start of day to hh.mmssss
+ *
+ * fd2gst (string)
+ *      convert from FITS date Greenwich Sidereal Time
+ * dt2gst (date, time)
+ *      convert from UT as yyyy.mmdd hh.mmssss to Greenwich Sidereal Time
+ * ts2gst (tsec)
+ *      Calculate Greenwich Sidereal Time given Universal Time
+ *          in seconds since 1951-01-01T0:00:00
+ * fd2mst (string)
+ *      convert from FITS UT date to Mean Sidereal Time
+ * dt2gmt (date, time)
+ *      convert from UT as yyyy.mmdd hh.mmssss to Mean Sidereal Time
+ * ts2mst (tsec)
+ *      Calculate Mean Sidereal Time given Universal Time
+ *          in seconds since 1951-01-01T0:00:00
+ * jd2mst (string)
+ *      convert from Julian Date to Mean Sidereal Time
+ * mst2fd (string)
+ *	convert to current UT in FITS format given Greenwich Mean Sidereal Time
+ * mst2jd (dj)
+ *	convert to current UT as Julian Date given Greenwich Mean Sidereal Time
+ * jd2lst (dj)
+ *	Calculate Local Sidereal Time from Julian Date
+ * ts2lst (tsec)
+ *	Calculate Local Sidereal Time given UT in seconds since 1951-01-01T0:00
+ * fd2lst (string)
+ *	Calculate Local Sidereal Time given Universal Time as FITS ISO date
+ * lst2jd (dj, lst)
+ *	Calculate Julian Date given current Julian date and Local Sidereal Time
+ * lst2fd (string, lst)
+ *	Calculate Julian Date given current UT date and Local Sidereal Time
+ * gst2fd (string)
+ * 	Calculate current UT given UT date and Greenwich Sidereal Time
+ * gst2jd (dj)
+ * 	Calculate current UT given UT date and Greenwich Sidereal Time as JD
+ *
+ * compnut (dj, dpsi, deps, eps0)
+ *      Compute the longitude and obliquity components of nutation and
+ *      mean obliquity from the IAU 1980 theory
+ *
+ * utdt (dj)
+ *	Compute difference between UT and dynamical time (ET-UT)
+ * ut2dt (year, doy)
+ *	Current Universal Time to year and day of year
+ * ut2dt (date, time)
+ *	Current Universal Time to date (yyyy.mmdd) and time (hh.mmsss)
+ * ut2ep(), ut2epb(), ut2epj()
+ *	Current Universal Time to fractional year, Besselian, Julian epoch
+ * ut2fd()
+ *	Current Universal Time to FITS ISO date string
+ * ut2jd()
+ *	Current Universal Time to Julian Date
+ * ut2mjd()
+ *	Current Universal Time to Modified Julian Date
+ * ut2tsi()
+ *	Current Universal Time to IRAF seconds since 1980-01-01T00:00
+ * ut2tsu()
+ *	Current Universal Time to Unix seconds since 1970-01-01T00:00
+ * ut2ts()
+ *	Current Universal Time to seconds since 1950-01-01T00:00
+ * isdate (string)
+ *	Return 1 if string is a FITS date (old or ISO)
+ *
+ * Internally-used subroutines
+ *
+ * fixdate (iyr, imon, iday, ihr, imn, sec, ndsec)
+ *	Round seconds and make sure date and time numbers are within limits
+ * caldays (year, month)
+ *	Calculate days in month 1-12 given year (Gregorian calendar only
+ * dint (dnum)
+ *	Return integer part of floating point number
+ * dmod (dnum)
+ *	Return Mod of floating point number
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <sys/time.h>
+#include "wcs.h"
+#include "fitsfile.h"
+
+static double suntl();
+static void fixdate();
+static int caldays();
+static double dint();
+static double dmod();
+
+static double longitude = 0.0;	/* longitude of observatory in degrees (+=west) */
+void
+setlongitude (longitude0)
+double longitude0;
+{ longitude = longitude0; return; }
+
+static int ndec = 3;
+void
+setdatedec (nd)
+int nd;
+{ ndec = nd; return; }
+
+/* ANG2HR -- Convert angle in fraction degrees to hours as hh:mm:ss.ss */
+
+void
+ang2hr (angle, lstr, string)
+
+double	angle;	/* Angle in fractional degrees */
+int	lstr;	/* Maximum number of characters in string */
+char	*string; /* Character string (hh:mm:ss.ss returned) */
+
+{
+    angle = angle / 15.0;
+    dec2str (string, lstr, angle, ndec);
+    return;
+}
+
+
+/* ANG2DEG -- Convert angle in fraction degrees to degrees as dd:mm:ss.ss */
+
+void
+ang2deg (angle, lstr, string)
+
+double	angle;	/* Angle in fractional degrees */
+int	lstr;	/* Maximum number of characters in string */
+char	*string; /* Character string (dd:mm:ss.ss returned) */
+{
+    dec2str (string, lstr, angle, ndec);
+    return;
+}
+
+
+/* DEG2ANG -- Convert angle in degrees as dd:mm:ss.ss to fractional degrees */
+
+double
+deg2ang (angle)
+
+char *angle;	/* Angle as dd:mm:ss.ss */
+{
+    double deg;
+
+    deg = str2dec (angle);
+    return (deg);
+}
+
+/* HR2ANG -- Convert angle in hours as hh:mm:ss.ss to fractional degrees */
+
+double
+hr2ang (angle)
+
+char *angle;	/* Angle in sexigesimal hours (hh:mm:ss.sss) */
+
+{
+    double deg;
+
+    deg = str2dec (angle);
+    deg = deg * 15.0;
+    return (deg);
+}
+
+
+/* DT2FD-- convert vigesimal date and time to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+dt2fd (date, time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+    int nf;
+    char *string;
+    char tstring[32], dstring[32];
+    char outform[64];
+
+    dt2i (date, time, &iyr,&imon,&iday,&ihr,&imn,&sec, ndec);
+
+    /* Convert to ISO date format */
+    string = (char *) calloc (32, sizeof (char));
+
+    /* Make time string */
+    if (time != 0.0 || ndec > 0) {
+	if (ndec == 0)
+	    nf = 2;
+	else
+	    nf = 3 + ndec;
+	if (ndec > 0) {
+	    sprintf (outform, "%%02d:%%02d:%%0%d.%df", nf, ndec);
+	    sprintf (tstring, outform, ihr, imn, sec);
+	    }
+	else {
+	    sprintf (outform, "%%02d:%%02d:%%0%dd", nf);
+	    sprintf (tstring, outform, ihr, imn, (int)(sec+0.5));
+	    }
+	}
+
+    /* Make date string */
+    if (date != 0.0)
+	sprintf (dstring, "%4d-%02d-%02d", iyr, imon, iday);
+
+    /* Make FITS (ISO) date string */
+    if (date == 0.0)
+	strcpy (string, tstring);
+    else if (time == 0.0 && ndec < 1)
+	strcpy (string, dstring);
+    else
+	sprintf (string, "%sT%s", dstring, tstring);
+
+    return (string);
+}
+
+
+/* DT2JD-- convert from date as yyyy.mmdd and time as hh.mmsss to Julian Date
+ *	   Return fractional days if date is zero */
+
+double
+dt2jd (date,time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date (returned) */
+    double tsec;	/* seconds since 1950.0 */
+
+    tsec = dt2ts (date, time);
+    if (date == 0.0)
+	dj = tsec / 86400.0;
+    else
+	dj = ts2jd (tsec);
+
+    return (dj);
+}
+
+
+/* DT2MJD-- convert from date yyyy.mmdd time hh.mmsss to modified Julian Date
+ *	   Return fractional days if date is zero */
+
+double
+dt2mjd (date,time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Modified Julian date (returned) */
+    double tsec;	/* seconds since 1950.0 */
+
+    tsec = dt2ts (date, time);
+    if (date == 0.0)
+	dj = tsec / 86400.0;
+    else
+	dj = ts2jd (tsec);
+
+    return (dj - 2400000.5);
+}
+
+
+/* HJD2JD-- convert  Heliocentric Julian Date to (geocentric) Julian date */
+
+double
+hjd2jd (dj, ra, dec, sys)
+
+double	dj;	/* Heliocentric Julian date */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double lt;		/* Light travel difference to the Sun (days) */
+
+    lt = suntl (dj, ra, dec, sys);
+
+    /* Return Heliocentric Julian Date */
+    return (dj - lt);
+}
+
+
+/* JD2HJD-- convert (geocentric) Julian date to Heliocentric Julian Date */
+
+double
+jd2hjd (dj, ra, dec, sys)
+
+double	dj;	/* Julian date (geocentric) */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double lt;		/* Light travel difference to the Sun (days) */
+
+    lt = suntl (dj, ra, dec, sys);
+
+    /* Return Heliocentric Julian Date */
+    return (dj + lt);
+}
+
+
+/* MHJD2MJD-- convert modified Heliocentric Julian Date to
+	      modified geocentric Julian date */
+
+double
+mhjd2mjd (mhjd, ra, dec, sys)
+
+double	mhjd;	/* Modified Heliocentric Julian date */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double lt;		/* Light travel difference to the Sun (days) */
+    double hjd;		/* Heliocentric Julian date */
+
+    hjd = mjd2jd (mhjd);
+
+    lt = suntl (hjd, ra, dec, sys);
+
+    /* Return Heliocentric Julian Date */
+    return (jd2mjd (hjd - lt));
+}
+
+
+/* MJD2MHJD-- convert modified geocentric Julian date tp
+	      modified Heliocentric Julian Date */
+
+double
+mjd2mhjd (mjd, ra, dec, sys)
+
+double	mjd;	/* Julian date (geocentric) */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double lt;		/* Light travel difference to the Sun (days) */
+    double	dj;	/* Julian date (geocentric) */
+
+    dj = mjd2jd (mjd);
+
+    lt = suntl (dj, ra, dec, sys);
+
+    /* Return Heliocentric Julian Date */
+    return (jd2mjd (dj + lt));
+}
+
+
+/* SUNTL-- compute light travel time to heliocentric correction in days */
+/* Translated into C from IRAF SPP noao.astutils.asttools.asthjd.x */
+
+static double
+suntl (dj, ra, dec, sys)
+
+double	dj;	/* Julian date (geocentric) */
+double	ra;	/* Right ascension (degrees) */
+double	dec;	/* Declination (degrees) */
+int	sys;	/* J2000, B1950, GALACTIC, ECLIPTIC */
+{
+    double t;		/* Number of Julian centuries since J1900 */
+    double manom;	/* Mean anomaly of the Earth's orbit (degrees) */
+    double lperi;	/* Mean longitude of perihelion (degrees) */
+    double oblq;	/* Mean obliquity of the ecliptic (degrees) */
+    double eccen;	/* Eccentricity of the Earth's orbit (dimensionless) */
+    double eccen2, eccen3;
+    double tanom;	/* True anomaly (approximate formula) (radians) */
+    double slong;	/* True longitude of the Sun from the Earth (radians) */
+    double rs;		/* Distance to the sun (AU) */
+    double lt;		/* Light travel difference to the Sun (days) */
+    double l;		/* Longitude of star in orbital plane of Earth (radians) */
+    double b;		/* Latitude of star in orbital plane of Earth (radians) */
+    double epoch;	/* Epoch of obervation */
+    double rs1,rs2;
+
+    t = (dj - 2415020.0) / 36525.0;
+
+    /* Compute earth orbital parameters */
+    manom = 358.47583 + (t * (35999.04975 - t * (0.000150 + t * 0.000003)));
+    lperi = 101.22083 + (t * (1.7191733 + t * (0.000453 + t * 0.000003)));
+    oblq = 23.452294 - (t * (0.0130125 + t * (0.00000164 - t * 0.000000503)));
+    eccen = 0.01675104 - (t * (0.00004180 + t * 0.000000126));
+    eccen2 = eccen * eccen;
+    eccen3 = eccen * eccen2;
+
+    /* Convert to principle angles */
+    manom = manom - (360.0 * (dint) (manom / 360.0));
+    lperi = lperi - (360.0 * (dint) (lperi / 360.0));
+
+    /* Convert to radians */
+    manom = degrad (manom);
+    lperi = degrad (lperi);
+    oblq = degrad (oblq);
+
+    /* True anomaly */
+    tanom = manom + (2 * eccen - 0.25 * eccen3) * sin (manom) +
+	    1.25 * eccen2 * sin (2 * manom) +
+	    13./12. * eccen3 * sin (3 * manom);
+
+    /* Distance to the Sun */
+    rs1 = 1.0 - eccen2;
+    rs2 = 1.0 + (eccen * cos (tanom));
+    rs = rs1 / rs2;
+
+    /* True longitude of the Sun seen from the Earth */
+    slong = lperi + tanom + PI;
+
+    /* Longitude and latitude of star in orbital plane of the Earth */
+    epoch = jd2ep (dj);
+    wcscon (sys, WCS_ECLIPTIC, 0.0, 0.0, &ra, &dec, epoch);
+    l = degrad (ra);
+    b = degrad (dec);
+
+    /* Light travel difference to the Sun */
+    lt = -0.005770 * rs * cos (b) * cos (l - slong);
+
+    /* Return light travel difference */
+    return (lt);
+}
+
+
+/* JD2DT-- convert Julian date to date as yyyy.mmdd and time as hh.mmssss */
+
+void
+jd2dt (dj,date,time)
+
+double	dj;	/* Julian date */
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    /* Convert Julian Date to date and time */
+    jd2i (dj, &iyr, &imon, &iday, &ihr, &imn, &sec, 4);
+
+    /* Convert date to yyyy.mmdd */
+    if (iyr < 0) {
+	*date = (double) (-iyr) + 0.01 * (double) imon + 0.0001 * (double) iday;
+	*date = -(*date);
+	}
+    else
+	*date = (double) iyr + 0.01 * (double) imon + 0.0001 * (double) iday;
+
+    /* Convert time to hh.mmssssss */
+    *time = (double) ihr + 0.01 * (double) imn + 0.0001 * sec;
+
+    return;
+}
+
+
+/* JD2I-- convert Julian date to date as year, month, and day, and time hours,
+          minutes, and seconds */
+/*        after Fliegel and Van Flander, CACM 11, 657 (1968) */
+
+
+void
+jd2i (dj, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	dj;	/* Julian date */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double tsec;
+    double frac, dts, ts, sday;
+    int jd, l, n, i, j;
+
+    tsec = jd2ts (dj);
+    /* ts2i (tsec, iyr, imon, iday, ihr, imn, sec, ndsec); */
+
+    /* Round seconds to 0 - 4 decimal places */
+    if (tsec < 0.0)
+	dts = -0.5;
+    else
+	dts = 0.5;
+    if (ndsec < 1)
+	ts = dint (tsec + dts);
+    else if (ndsec < 2)
+	ts = dint (tsec * 10.0 + dts) / 10.0;
+    else if (ndsec < 3)
+	ts = dint (tsec * 100.0 + dts) / 100.0;
+    else if (ndsec < 4)
+	ts = dint (tsec * 1000.0 + dts) / 1000.0;
+    else
+	ts = dint (tsec * 10000.0 + dts) / 10000.0;
+
+    /* Convert back to Julian Date */
+    dj = ts2jd (ts);
+
+    /* Compute time from fraction of a day */
+    frac = dmod (dj, 1.0);
+    if (frac < 0.5) {
+	jd = (int) (dj - frac);
+	sday = (frac + 0.5) * 86400.0;
+	}
+    else {
+	jd = (int) (dj - frac) + 1;
+	sday = (frac - 0.5) * 86400.0;
+	}
+    
+    *ihr = (int) (sday / 3600.0);
+    sday = sday - (double) (*ihr * 3600);
+    *imn = (int) (sday / 60.0);
+    *sec = sday - (double) (*imn * 60);
+
+    /* Compute day, month, year */
+    l = jd + 68569;
+    n = (4 * l) / 146097;
+    l = l - (146097 * n + 3) / 4;
+    i = (4000 * (l + 1)) / 1461001;
+    l = l - (1461 * i) / 4 + 31;
+    j = (80 * l) / 2447;
+    *iday = l - (2447 * j) / 80;
+    l = j / 11;
+    *imon = j + 2 - (12 * l);
+    *iyr = 100 * (n - 49) + i + l;
+
+    return;
+}
+
+
+/* JD2MJD-- convert Julian Date to Modified Julian Date */
+
+double
+jd2mjd (dj)
+
+double	dj;	/* Julian Date */
+
+{
+    return (dj - 2400000.5);
+}
+
+
+/* JD2EP-- convert Julian date to fractional year as used in epoch */
+
+double
+jd2ep (dj)
+
+double	dj;	/* Julian date */
+
+{
+    double date, time;
+    jd2dt (dj, &date, &time);
+    return (dt2ep (date, time));
+}
+
+
+/* JD2EPB-- convert Julian date to Besselian epoch */
+
+double
+jd2epb (dj)
+
+double	dj;	/* Julian date */
+
+{
+    return (1900.0 + (dj - 2415020.31352) / 365.242198781);
+}
+
+
+/* JD2EPJ-- convert Julian date to Julian epoch */
+
+double
+jd2epj (dj)
+
+double	dj;	/* Julian date */
+
+{
+    return (2000.0 + (dj - 2451545.0) / 365.25);
+}
+
+
+/* LT2DT-- Return local time as yyyy.mmdd and time as hh.mmssss */
+
+void
+lt2dt(date, time)
+
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+
+{
+    time_t tsec;
+    struct timeval tp;
+    struct timezone tzp;
+    struct tm *ts;
+
+    gettimeofday (&tp,&tzp);
+
+    tsec = tp.tv_sec;
+    ts = localtime (&tsec);
+
+    if (ts->tm_year < 1000)
+	*date = (double) (ts->tm_year + 1900);
+    else
+	*date = (double) ts->tm_year;
+    *date = *date + (0.01 * (double) (ts->tm_mon + 1));
+    *date = *date + (0.0001 * (double) ts->tm_mday);
+    *time = (double) ts->tm_hour;
+    *time = *time + (0.01 * (double) ts->tm_min);
+    *time = *time + (0.0001 * (double) ts->tm_sec);
+
+    return;
+}
+
+
+/* LT2FD-- Return current local time as FITS ISO date string */
+
+char *
+lt2fd()
+{
+    time_t tsec;
+    struct tm *ts;
+    struct timeval tp;
+    struct timezone tzp;
+    int month, day, year, hour, minute, second;
+    char *isotime;
+
+    gettimeofday (&tp,&tzp);
+    tsec = tp.tv_sec;
+
+    ts = localtime (&tsec);
+
+    year = ts->tm_year;
+    if (year < 1000)
+	year = year + 1900;
+    month = ts->tm_mon + 1;
+    day = ts->tm_mday;
+    hour = ts->tm_hour;
+    minute = ts->tm_min;
+    second = ts->tm_sec;
+
+    isotime = (char *) calloc (32, sizeof (char));
+    sprintf (isotime, "%04d-%02d-%02dT%02d:%02d:%02d",
+                      year, month, day, hour, minute, second);
+    return (isotime);
+}
+
+
+/* LT2TSI-- Return local time as IRAF seconds since 1980-01-01 00:00 */
+
+int
+lt2tsi()
+{
+    return ((int)(lt2ts() - 946684800.0));
+}
+
+
+/* LT2TSU-- Return local time as Unix seconds since 1970-01-01 00:00 */
+
+time_t
+lt2tsu()
+{
+    return ((time_t)(lt2ts() - 631152000.0));
+}
+
+/* LT2TS-- Return local time as Unix seconds since 1950-01-01 00:00 */
+
+double
+lt2ts()
+{
+    double tsec;
+    char *datestring;
+    datestring = lt2fd();
+    tsec = fd2ts (datestring);
+    free (datestring);
+    return (tsec);
+}
+
+
+/* MJD2DT-- convert Modified Julian Date to date (yyyy.mmdd) time (hh.mmssss) */
+
+void
+mjd2dt (dj,date,time)
+
+double	dj;	/* Modified Julian Date */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double tsec;
+
+    tsec = jd2ts (dj + 2400000.5);
+    ts2dt (tsec, date, time);
+
+    return;
+}
+
+
+/* MJD2I-- convert Modified Julian Date to date as year, month, day and
+           time as hours, minutes, seconds */
+
+void
+mjd2i (dj, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	dj;	/* Modified Julian Date */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double tsec;
+
+    tsec = jd2ts (dj + 2400000.5);
+    ts2i (tsec, iyr, imon, iday, ihr, imn, sec, ndsec);
+    return;
+}
+
+
+/* MJD2DOY-- convert Modified Julian Date to Year,Day-of-Year */
+
+void
+mjd2doy (dj, year, doy)
+
+double	dj;	/* Modified Julian Date */
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year with fraction (returned) */
+
+{
+    jd2doy (dj + 2400000.5, year, doy);
+    return;
+}
+
+
+/* MJD2JD-- convert Modified Julian Date to Julian Date */
+
+double
+mjd2jd (dj)
+
+double	dj;	/* Modified Julian Date */
+
+{
+    return (dj + 2400000.5);
+}
+
+
+/* MJD2EP-- convert Modified Julian Date to fractional year */
+
+double
+mjd2ep (dj)
+
+double	dj;	/* Modified Julian Date */
+
+{
+    double date, time;
+    jd2dt (dj + 2400000.5, &date, &time);
+    return (dt2ep (date, time));
+}
+
+
+/* MJD2EPB-- convert Modified Julian Date to Besselian epoch */
+
+double
+mjd2epb (dj)
+
+double	dj;	/* Modified Julian Date */
+
+{
+    return (1900.0 + (dj - 15019.81352) / 365.242198781);
+}
+
+
+/* MJD2EPJ-- convert Modified Julian Date to Julian epoch */
+
+double
+mjd2epj (dj)
+
+double	dj;	/* Modified Julian Date */
+
+{
+    return (2000.0 + (dj - 51544.5) / 365.25);
+}
+
+
+/* MJD2FD-- convert modified Julian date to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+mjd2fd (dj)
+
+double	dj;	/* Modified Julian date */
+{
+    return (jd2fd (dj + 2400000.5));
+}
+
+
+/* MJD2TS-- convert modified Julian date to seconds since 1950.0 */
+
+double
+mjd2ts (dj)
+
+double	dj;	/* Modified Julian date */
+{
+    return ((dj - 33282.0) * 86400.0);
+}
+
+
+/* EP2FD-- convert fractional year to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+ep2fd (epoch)
+
+double	epoch;	/* Date as fractional year */
+{
+    double tsec; /* seconds since 1950.0 (returned) */
+    tsec = ep2ts (epoch);
+    return (ts2fd (tsec));
+}
+
+
+/* EPB2FD-- convert Besselian epoch to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+epb2fd (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+{
+    double dj;		/* Julian Date */
+    dj = epb2jd (epoch);
+    return (jd2fd (dj));
+}
+
+
+/* EPJ2FD-- convert Julian epoch to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+epj2fd (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+{
+    double dj;		/* Julian Date */
+    dj = epj2jd (epoch);
+    return (jd2fd (dj));
+}
+
+
+/* EP2TS-- convert fractional year to seconds since 1950.0 */
+
+double
+ep2ts (epoch)
+
+double	epoch;	/* Date as fractional year */
+{
+    double dj;
+    dj = ep2jd (epoch);
+    return ((dj - 2433282.5) * 86400.0);
+}
+
+
+/* EPB2TS-- convert Besselian epoch to seconds since 1950.0 */
+
+double
+epb2ts (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+{
+    double dj;
+    dj = epb2jd (epoch);
+    return ((dj - 2433282.5) * 86400.0);
+}
+
+
+/* EPJ2TS-- convert Julian epoch to seconds since 1950.0 */
+
+double
+epj2ts (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+{
+    double dj;
+    dj = epj2jd (epoch);
+    return ((dj - 2433282.5) * 86400.0);
+}
+
+
+/* EPB2EP-- convert Besselian epoch to fractional years */
+
+double
+epb2ep (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+{
+    double dj;
+    dj = epb2jd (epoch);
+    return (jd2ep (dj));
+}
+
+
+/* EP2EPB-- convert fractional year to Besselian epoch */
+
+double
+ep2epb (epoch)
+
+double	epoch;	/* Fractional year */
+{
+    double dj;
+    dj = ep2jd (epoch);
+    return (jd2epb (dj));
+}
+
+
+/* EPJ2EP-- convert Julian epoch to fractional year */
+
+double
+epj2ep (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+{
+    double dj;
+    dj = epj2jd (epoch);
+    return (jd2ep (dj));
+}
+
+
+/* EP2EPJ-- convert fractional year to Julian epoch */
+
+double
+ep2epj (epoch)
+
+double	epoch;	/* Fractional year */
+{
+    double dj;
+    dj = ep2jd (epoch);
+    return (jd2epj (dj));
+}
+
+
+/* EP2I-- convert fractional year to year month day hours min sec */
+
+void
+ep2i (epoch, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	epoch;	/* Date as fractional year */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+{
+    double date, time;
+
+    ep2dt (epoch, &date, &time);
+    dt2i (date, time, iyr,imon,iday,ihr,imn,sec, ndsec);
+    return;
+}
+
+
+/* EPB2I-- convert Besselian epoch to year month day hours min sec */
+
+void
+epb2i (epoch, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+{
+    double date, time;
+
+    epb2dt (epoch, &date, &time);
+    dt2i (date, time, iyr,imon,iday,ihr,imn,sec, ndsec);
+    return;
+}
+
+
+/* EPJ2I-- convert Julian epoch to year month day hours min sec */
+
+void
+epj2i (epoch, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+{
+    double date, time;
+
+    epj2dt (epoch, &date, &time);
+    dt2i (date, time, iyr,imon,iday,ihr,imn,sec, ndsec);
+    return;
+}
+
+
+/* EP2JD-- convert fractional year as used in epoch to Julian date */
+
+double
+ep2jd (epoch)
+
+double	epoch;	/* Date as fractional year */
+
+{
+    double dj;	/* Julian date (returned)*/
+    double date, time;
+
+    ep2dt (epoch, &date, &time);
+    dj = dt2jd (date, time);
+    return (dj);
+}
+
+
+/* EPB2JD-- convert Besselian epoch to Julian Date */
+
+double
+epb2jd (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+
+{
+    return (2415020.31352 + ((epoch - 1900.0) * 365.242198781));
+}
+
+
+/* EPJ2JD-- convert Julian epoch to Julian Date */
+
+double
+epj2jd (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+
+{
+    return (2451545.0 + ((epoch - 2000.0) * 365.25));
+}
+
+
+/* EP2MJD-- convert fractional year as used in epoch to modified Julian date */
+
+double
+ep2mjd (epoch)
+
+double	epoch;	/* Date as fractional year */
+
+{
+    double dj;	/* Julian date (returned)*/
+    double date, time;
+
+    ep2dt (epoch, &date, &time);
+    dj = dt2jd (date, time);
+    return (dj - 2400000.5);
+}
+
+
+/* EPB2MJD-- convert Besselian epoch to modified Julian Date */
+
+double
+epb2mjd (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+
+{
+    return (15019.81352 + ((epoch - 1900.0) * 365.242198781));
+}
+
+
+/* EPJ2MJD-- convert Julian epoch to modified Julian Date */
+
+double
+epj2mjd (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+
+{
+    return (51544.5 + ((epoch - 2000.0) * 365.25));
+}
+
+
+
+/* EPB2EPJ-- convert Besselian epoch to Julian epoch */
+
+double
+epb2epj (epoch)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+{
+    double dj;		/* Julian date */
+    dj = epb2jd (epoch);
+    return (jd2epj (dj));
+}
+
+
+/* EPJ2EPB-- convert Julian epoch to Besselian epoch */
+
+double
+epj2epb (epoch)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+{
+    double dj;		/* Julian date */
+    dj = epj2jd (epoch);
+    return (jd2epb (dj));
+}
+
+
+/* JD2FD-- convert Julian date to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+jd2fd (dj)
+
+double	dj;	/* Julian date */
+{
+    double tsec;		/* seconds since 1950.0 (returned) */
+    tsec = (dj - 2433282.5) * 86400.0;
+    return (ts2fd (tsec));
+}
+
+
+/* JD2TS-- convert Julian date to seconds since 1950.0 */
+
+double
+jd2ts (dj)
+
+double	dj;	/* Julian date */
+{
+    return ((dj - 2433282.5) * 86400.0);
+}
+
+
+/* JD2TSI-- convert Julian date to IRAF seconds since 1980-01-01T0:00 */
+
+int
+jd2tsi (dj)
+
+double	dj;	/* Julian date */
+{
+    double ts;
+    ts = (dj - 2444239.5) * 86400.0;
+    return ((int) ts);
+}
+
+
+/* JD2TSU-- convert Julian date to Unix seconds since 1970-01-01T0:00 */
+
+time_t
+jd2tsu (dj)
+
+double	dj;	/* Julian date */
+{
+    return ((time_t)((dj - 2440587.5) * 86400.0));
+}
+
+
+/* DT2DOY-- convert yyyy.mmdd hh.mmss to year and day of year */
+
+void
+dt2doy (date, time, year, doy)
+
+double	date;	/* Date as yyyy.mmdd */
+double	time;	/* Time as hh.mmssxxxx */
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year with fraction (returned) */
+{
+    double	dj;	/* Julian date */
+    double	dj0;	/* Julian date on January 1 0:00 */
+    double	date0;	/* January first of date's year */
+    double	dyear;
+
+    dyear = floor (date);
+    date0 = dyear + 0.0101;
+    dj0 = dt2jd (date0, 0.0);
+    dj = dt2jd (date, time);
+    *year = (int) (dyear + 0.00000001);
+    *doy = dj - dj0 + 1.0;
+    return;
+}
+
+
+/* DOY2DT-- convert year and day of year to yyyy.mmdd hh.mmss */
+
+void
+doy2dt (year, doy, date, time)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    double	dj;	/* Julian date */
+    double	dj0;	/* Julian date on January 1 0:00 */
+    double	date0;	/* January first of date's year */
+
+    date0 = year + 0.0101;
+    dj0 = dt2jd (date0, 0.0);
+    dj = dj0 + doy - 1.0;
+    jd2dt (dj, date, time);
+    return;
+}
+
+
+/* DOY2EP-- convert year and day of year to fractional year as used in epoch */
+
+double
+doy2ep (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double date, time;
+    doy2dt (year, doy, &date, &time);
+    return (dt2ep (date, time));
+}
+
+
+
+/* DOY2EPB-- convert year and day of year to Besellian epoch */
+
+double
+doy2epb (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return (jd2epb (dj));
+}
+
+
+/* DOY2EPJ-- convert year and day of year to Julian epoch */
+
+double
+doy2epj (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return (jd2epj (dj));
+}
+
+
+/* DOY2FD-- convert year and day of year to FITS date */
+
+char *
+doy2fd (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;	/* Julian date  */
+
+    dj = doy2jd (year, doy);
+    return (jd2fd (dj));
+}
+
+
+/* DOY2JD-- convert year and day of year to Julian date */
+
+double
+doy2jd (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double	dj0;	/* Julian date */
+    double	date;	/* Date as yyyy.mmdd (returned) */
+    double	time;	/* Time as hh.mmssxxxx (returned) */
+
+    date = (double) year + 0.0101;
+    time = 0.0;
+    dj0 = dt2jd (date, time);
+    return (dj0 + doy - 1.0);
+}
+
+
+/* DOY2MJD-- convert year and day of year to Julian date */
+
+double
+doy2mjd (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double	dj0;	/* Julian date */
+    double	date;	/* Date as yyyy.mmdd (returned) */
+    double	time;	/* Time as hh.mmssxxxx (returned) */
+
+    date = (double) year + 0.0101;
+    time = 0.0;
+    dj0 = dt2jd (date, time);
+    return (dj0 + doy - 1.0 - 2400000.5);
+}
+
+
+/* DOY2TSU-- convert from FITS date to Unix seconds since 1970-01-01T0:00 */
+
+time_t
+doy2tsu (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return ((time_t)jd2ts (dj));
+}
+
+
+/* DOY2TSI-- convert from FITS date to IRAF seconds since 1980-01-01T0:00 */
+
+int
+doy2tsi (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return ((int)jd2tsi (dj));
+}
+
+
+/* DOY2TS-- convert year, day of year to seconds since 1950 */
+
+double
+doy2ts (year, doy)
+
+int	year;	/* Year */
+double	doy;	/* Day of year with fraction */
+{
+    double dj;
+    dj = doy2jd (year, doy);
+    return (jd2ts (dj));
+}
+
+
+/* FD2DOY-- convert FITS date to year and day of year */
+
+void
+fd2doy (string, year, doy)
+
+char	*string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year with fraction (returned) */
+{
+    double dj;	/* Julian date */
+
+    dj = fd2jd (string);
+    jd2doy (dj, year, doy);
+    return;
+}
+
+
+/* JD2DOY-- convert Julian date to year and day of year */
+
+void
+jd2doy (dj, year, doy)
+
+double	dj;	/* Julian date */
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year with fraction (returned) */
+{
+    double date;	/* Date as yyyy.mmdd (returned) */
+    double time;	/* Time as hh.mmssxxxx (returned) */
+    double dj0;		/* Julian date at 0:00 on 1/1 */
+    double dyear;
+
+    jd2dt (dj, &date, &time);
+    *year = (int) date;
+    dyear = (double) *year;
+    dj0 = dt2jd (dyear+0.0101, 0.0);
+    *doy = dj - dj0 + 1.0;
+    return;
+}
+
+
+/* TS2JD-- convert seconds since 1950.0 to Julian date */
+
+double
+ts2jd (tsec)
+
+double	tsec;	/* seconds since 1950.0 */
+{
+    return (2433282.5 + (tsec / 86400.0));
+}
+
+
+/* TS2MJD-- convert seconds since 1950.0 to modified Julian date */
+
+double
+ts2mjd (tsec)
+
+double	tsec;	/* seconds since 1950.0 */
+{
+    return (33282.0 + (tsec / 86400.0));
+}
+
+
+/* TS2EP-- convert seconds since 1950.0 to fractional year as used in epoch */
+
+double
+ts2ep (tsec)
+
+double	tsec;	/* Seconds since 1950.0 */
+
+{
+    double date, time;
+    ts2dt (tsec, &date, &time);
+    return (dt2ep (date, time));
+}
+
+
+/* TS2EPB-- convert seconds since 1950.0 to Besselian epoch */
+
+double
+ts2epb (tsec)
+
+double	tsec;	/* Seconds since 1950.0 */
+
+{
+    double dj;		/* Julian Date */
+    dj = ts2jd (tsec);
+    return (jd2epb (dj));
+}
+
+
+/* TS2EPB-- convert seconds since 1950.0 to Julian epoch */
+
+double
+ts2epj (tsec)
+
+double	tsec;	/* Seconds since 1950.0 */
+
+{
+    double dj;		/* Julian Date */
+    dj = ts2jd (tsec);
+    return (jd2epj (dj));
+}
+
+
+/* DT2EP-- convert from date, time as yyyy.mmdd hh.mmsss to fractional year */
+
+double
+dt2ep (date, time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double epoch; /* Date as fractional year (returned) */
+    double dj, dj0, dj1, date0, time0, date1;
+
+    dj = dt2jd (date, time);
+    if (date == 0.0)
+	epoch = dj / 365.2422;
+    else {
+	time0 = 0.0;
+	date0 = dint (date) + 0.0101;
+	date1 = dint (date) + 1.0101;
+	dj0 = dt2jd (date0, time0);
+	dj1 = dt2jd (date1, time0);
+	epoch = dint (date) + ((dj - dj0) / (dj1 - dj0));
+	}
+    return (epoch);
+}
+
+
+/* DT2EPB-- convert from date, time as yyyy.mmdd hh.mmsss to Besselian epoch */
+
+double
+dt2epb (date, time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date */
+    double epoch;	/* Date as fractional year (returned) */
+    dj = dt2jd (date, time);
+    if (date == 0.0)
+	epoch = dj / 365.242198781;
+    else
+	epoch = jd2epb (dj);
+    return (epoch);
+}
+
+
+/* DT2EPJ-- convert from date, time as yyyy.mmdd hh.mmsss to Julian epoch */
+
+double
+dt2epj (date, time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date */
+    double epoch;	/* Date as fractional year (returned) */
+    dj = dt2jd (date, time);
+    if (date == 0.0)
+	epoch = dj / 365.25;
+    else
+	epoch = jd2epj (dj);
+    return (epoch);
+}
+
+
+/* EP2DT-- convert from fractional year to date, time as yyyy.mmdd hh.mmsss */
+
+void
+ep2dt (epoch, date, time)
+
+double epoch;	/* Date as fractional year */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj, dj0, dj1, date0, time0, date1, epochi, epochf;
+
+    time0 = 0.0;
+    epochi = dint (epoch);
+    epochf = epoch - epochi;
+    date0 = epochi + 0.0101;
+    date1 = epochi + 1.0101;
+    dj0 = dt2jd (date0, time0);
+    dj1 = dt2jd (date1, time0);
+    dj = dj0 + epochf * (dj1 - dj0);
+    jd2dt (dj, date, time);
+    return;
+}
+
+
+/* EPB2DT-- convert from Besselian epoch to date, time as yyyy.mmdd hh.mmsss */
+
+void
+epb2dt (epoch, date, time)
+
+double	epoch;	/* Besselian epoch (fractional 365.242198781-day years) */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date */
+    dj = epb2jd (epoch);
+    jd2dt (dj, date, time);
+}
+
+
+/* EPJ2DT-- convert from Julian epoch to date, time as yyyy.mmdd hh.mmsss */
+
+void
+epj2dt (epoch, date, time)
+
+double	epoch;	/* Julian epoch (fractional 365.25-day years) */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double dj;		/* Julian date */
+    dj = epj2jd (epoch);
+    jd2dt (dj, date, time);
+}
+
+
+/* FD2JD-- convert FITS standard date to Julian date */
+
+double
+fd2jd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+
+    fd2dt (string, &date, &time);
+    return (dt2jd (date, time));
+}
+
+
+/* FD2MJD-- convert FITS standard date to modified Julian date */
+
+double
+fd2mjd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    return (fd2jd (string) - 2400000.5);
+}
+
+
+/* FD2TSU-- convert from FITS date to Unix seconds since 1970-01-01T0:00 */
+
+time_t
+fd2tsu (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+    fd2dt (string, &date, &time);
+    return (dt2tsu (date, time));
+}
+
+
+/* FD2TSI-- convert from FITS date to IRAF seconds since 1980-01-01T0:00 */
+
+int
+fd2tsi (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+    fd2dt (string, &date, &time);
+    return (dt2tsi (date, time));
+}
+
+
+/* FD2TS-- convert FITS standard date to seconds since 1950 */
+
+double
+fd2ts (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+    fd2dt (string, &date, &time);
+    return (dt2ts (date, time));
+}
+
+
+/* FD2FD-- convert any FITS standard date to ISO FITS standard date */
+
+char *
+fd2fd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double date, time;
+    fd2dt (string, &date, &time);
+    return (dt2fd (date, time));
+}
+
+
+/* FD2OF-- convert any FITS standard date to old FITS standard date time */
+
+char *
+fd2of (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    fd2i (string,&iyr,&imon,&iday,&ihr,&imn,&sec, 3);
+
+    /* Convert to old FITS date format */
+    string = (char *) calloc (32, sizeof (char));
+    if (iyr < 1900)
+	sprintf (string, "*** date out of range ***");
+    else if (iyr < 2000)
+	sprintf (string, "%02d/%02d/%02d %02d:%02d:%06.3f",
+		 iday, imon, iyr-1900, ihr, imn, sec);
+    else if (iyr < 2900.0)
+	sprintf (string, "%02d/%02d/%3d %02d:%02d:%6.3f",
+		 iday, imon, iyr-1900, ihr, imn, sec);
+    else
+	sprintf (string, "*** date out of range ***");
+    return (string);
+}
+
+
+/* TAI-UTC from the U.S. Naval Observatory */
+/* ftp://maia.usno.navy.mil/ser7/tai-utc.dat */
+static double taijd[23]={2441317.5, 2441499.5, 2441683.5, 2442048.5, 2442413.5,
+	      2442778.5, 2443144.5, 2443509.5, 2443874.5, 2444239.5, 2444786.5,
+	      2445151.5, 2445516.5, 2446247.5, 2447161.5, 2447892.5, 2448257.5,
+	      2448804.5, 2449169.5, 2449534.5, 2450083.5, 2450630.5, 2451179.5};
+static double taidt[23]={10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,
+	   20.0,21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,31.0,32.0};
+static double dttab[173]={13.7,13.4,13.1,12.9,12.7,12.6,12.5,12.5,12.5,12.5,
+	   12.5,12.5,12.5,12.5,12.5,12.5,12.5,12.4,12.3,12.2,12.0,11.7,11.4,
+	   11.1,10.6,10.2, 9.6, 9.1, 8.6, 8.0, 7.5, 7.0, 6.6, 6.3, 6.0, 5.8,
+	    5.7, 5.6, 5.6, 5.6, 5.7, 5.8, 5.9, 6.1, 6.2, 6.3, 6.5, 6.6, 6.8,
+            6.9, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.7, 7.8, 7.8,7.88,7.82,
+	   7.54, 6.97, 6.40, 6.02, 5.41, 4.10, 2.92, 1.82, 1.61, 0.10,-1.02,
+	  -1.28,-2.69,-3.24,-3.64,-4.54,-4.71,-5.11,-5.40,-5.42,-5.20,-5.46,
+	  -5.46,-5.79,-5.63,-5.64,-5.80,-5.66,-5.87,-6.01,-6.19,-6.64,-6.44,
+	  -6.47,-6.09,-5.76,-4.66,-3.74,-2.72,-1.54,-0.02, 1.24, 2.64, 3.86,
+	   5.37, 6.14, 7.75, 9.13,10.46,11.53,13.36,14.65,16.01,17.20,18.24,
+	  19.06,20.25,20.95,21.16,22.25,22.41,23.03,23.49,23.62,23.86,24.49,
+	  24.34,24.08,24.02,24.00,23.87,23.95,23.86,23.93,23.73,23.92,23.96,
+	  24.02,24.33,24.83,25.30,25.70,26.24,26.77,27.28,27.78,28.25,28.71,
+	  29.15,29.57,29.97,30.36,30.72,31.07,31.35,31.68,32.18,32.68,33.15,
+	  33.59,34.00,34.47,35.03,35.73,36.54,37.43,38.29,39.20,40.18,41.17,
+	  42.23};
+
+
+/* ET2FD-- convert from ET (or TDT or TT) in FITS format to UT in FITS format */
+
+char *
+et2fd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double dj0, dj, tsec, dt;
+
+    dj0 = fd2jd (string);
+    dt = utdt (dj0);
+    dj = dj0 - (dt / 86400.0);
+    dt = utdt (dj);
+    tsec = fd2ts (string);
+    tsec = tsec - dt;
+    return (ts2fd (tsec));
+}
+
+
+/* FD2ET-- convert from UT in FITS format to ET (or TDT or TT) in FITS format */
+
+char *
+fd2et (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double dj, tsec, dt;
+
+    dj = fd2jd (string);
+    dt = utdt (dj);
+    tsec = fd2ts (string);
+    tsec = tsec + dt;
+    return (ts2fd (tsec));
+}
+
+
+/* DT2ET-- convert from UT as yyyy.mmdd hh.mmssss to ET in same format */
+
+void
+dt2et (date, time)
+double	*date;	/* Date as yyyy.mmdd */
+double	*time;	/* Time as hh.mmssxxxx
+		 *if time<0, it is time as -(fraction of a day) */
+{
+    double dj, dt, tsec;
+
+    dj = dt2jd (*date, *time);
+    dt = utdt (dj);
+    tsec = dt2ts (*date, *time);
+    tsec = tsec + dt;
+    ts2dt (tsec, date, time);
+    return;
+}
+
+
+/* EDT2DT-- convert from ET as yyyy.mmdd hh.mmssss to UT in same format */
+
+void
+edt2dt (date, time)
+double	*date;	/* Date as yyyy.mmdd */
+double	*time;	/* Time as hh.mmssxxxx
+		 *if time<0, it is time as -(fraction of a day) */
+{
+    double dj, dt, tsec, tsec0;
+
+    dj = dt2jd (*date, *time);
+    dt = utdt (dj);
+    tsec0 = dt2ts (*date, *time);
+    tsec = tsec0 + dt;
+    dj = ts2jd (tsec);
+    dt = utdt (dj);
+    tsec = tsec0 + dt;
+    ts2dt (tsec, date, time);
+    return;
+}
+
+
+/* JD2JED-- convert from Julian Date to Julian Ephemeris Date */
+
+double
+jd2jed (dj)
+
+double dj;	/* Julian Date */
+{
+    double dt;
+
+    dt = utdt (dj);
+    return (dj + (dt / 86400.0));
+}
+
+
+/* JED2JD-- convert from Julian Ephemeris Date to Julian Date */
+
+double
+jed2jd (dj)
+
+double dj;	/* Julian Ephemeris Date */
+{
+    double dj0, dt;
+
+    dj0 = dj;
+    dt = utdt (dj);
+    dj = dj0 - (dt / 86400.0);
+    dt = utdt (dj);
+    return (dj - (dt / 86400.0));
+}
+
+
+/* TS2ETS-- convert from UT in seconds since 1950-01-01 to ET in same format */
+
+double
+ts2ets (tsec)
+
+double tsec;
+{
+    double dj, dt;
+
+    dj = ts2jd (tsec);
+    dt = utdt (dj);
+    return (tsec + dt);
+}
+
+
+/* ETS2TS-- convert from ET in seconds since 1950-01-01 to UT in same format */
+
+double
+ets2ts (tsec)
+
+double tsec;
+{
+    double dj, dj0, dt;
+
+    dj0 = ts2jd (tsec);
+    dt = utdt (dj0);
+    dj = dj0 - (dt / 86400.0);
+    dt = utdt (dj);
+    return (tsec - dt);
+}
+
+
+/* UTDT-- Compute difference between UT and dynamical time (ET-UT) */
+
+double
+utdt (dj)
+
+double dj;	/* Julian Date (UT) */
+{
+    double dt, date, time, ts, ts1, ts0, date0, yfrac, diff, cj;
+    int i, iyr, iyear;
+
+    /* If after 1972-01-01, use tabulated TAI-UT */
+    if (dj >= 2441317.5) {
+	dt = 0.0;
+	for (i = 22;  i > 0; i--) {
+	    if (dj >= taijd[i])
+		dt = taidt[i];
+	    }
+	dt = dt + 32.84;
+	}
+
+    /* For 1800-01-01 to 1972-01-01, use table of ET-UT from AE */
+    else if (dj >= 2378496.5) {
+	jd2dt (dj, &date, &time);
+	ts = jd2ts (dj);
+	iyear = (int) date;
+	iyr = iyear - 1800;
+	date0 = (double) iyear + 0.0101;
+	ts0 = dt2ts (date0, 0.0);
+	date0 = (double) (iyear + 1) + 0.0101;
+	ts1 = dt2ts (date0, 0.0);
+	yfrac = (ts - ts0) / (ts1 - ts0);
+	diff = dttab[iyr+1] - dttab[iyr];
+	dt = dttab[iyr] + (diff * yfrac);
+	}
+
+    /* Compute back to 1600 using formula from McCarthy and Babcock (1986) */
+    else if (dj >= 2305447.5) {
+	cj = (dj - 2378496.5) / 36525.0;
+	dt = 5.156 + 13.3066 * (cj - 0.19) * (cj - 0.19);
+	}
+
+    /* Compute back to 948 using formula from Stephenson and Morrison (1984) */
+    else if (dj >= 2067309.5) {
+	cj = (dj - 2378496.5) / 36525.0;
+	dt = 25.5 * cj * cj;
+	}
+
+    /*Compute back to 390 BC using formula from Stephenson and Morrison (1984)*/
+    else if (dj >= 0.0) {
+	cj = (dj = 2378496.5) / 36525.0;
+	dt = 1360.0 + (320.0 * cj) + (44.3 * cj * cj);
+	}
+
+    else
+	dt = 0.0;
+    return (dt);
+}
+
+
+/* FD2OFD-- convert any FITS standard date to old FITS standard date */
+
+char *
+fd2ofd (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    fd2i (string,&iyr,&imon,&iday,&ihr,&imn,&sec, 3);
+
+    /* Convert to old FITS date format */
+    string = (char *) calloc (32, sizeof (char));
+    if (iyr < 1900)
+	sprintf (string, "*** date out of range ***");
+    else if (iyr < 2000)
+	sprintf (string, "%02d/%02d/%02d", iday, imon, iyr-1900);
+    else if (iyr < 2900.0)
+	sprintf (string, "%02d/%02d/%3d", iday, imon, iyr-1900);
+    else
+	sprintf (string, "*** date out of range ***");
+    return (string);
+}
+
+
+/* FD2OFT-- convert any FITS standard date to old FITS standard time */
+
+char *
+fd2oft (string)
+
+char *string;	/* FITS date string, which may be:
+			fractional year
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    fd2i (string,&iyr,&imon,&iday,&ihr,&imn,&sec, 3);
+
+    /* Convert to old FITS date format */
+    string = (char *) calloc (32, sizeof (char));
+    sprintf (string, "%02d:%02d:%06.3f", ihr, imn, sec);
+    return (string);
+}
+
+
+/* FD2DT-- convert FITS standard date to date, time as yyyy.mmdd hh.mmsss */
+
+void
+fd2dt (string, date, time)
+
+char *string;	/* FITS date string, which may be:
+		    fractional year
+		    dd/mm/yy (FITS standard before 2000)
+		    dd-mm-yy (nonstandard use before 2000)
+		    yyyy-mm-dd (FITS standard after 1999)
+		    yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    fd2i (string,&iyr,&imon,&iday,&ihr,&imn,&sec, 4);
+
+    /* Convert date to yyyy.mmdd */
+    if (iyr < 0) {
+	*date = (double) (-iyr) + 0.01 * (double) imon + 0.0001 * (double) iday;
+	*date = -(*date);
+	}
+    else
+	*date = (double) iyr + 0.01 * (double) imon + 0.0001 * (double) iday;
+
+    /* Convert time to hh.mmssssss */
+    *time = (double) ihr + 0.01 * (double) imn + 0.0001 * sec;
+
+    return;
+}
+
+
+/* FD2EP-- convert from FITS standard date to fractional year */
+
+double
+fd2ep (string)
+
+char *string;	/* FITS date string, which may be:
+			yyyy.ffff (fractional year)
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+
+{
+    double dj;		/* Julian date */
+    dj = fd2jd (string);
+    if (dj < 1.0)
+	return (dj / 365.2422);
+    else
+	return (jd2ep (dj));
+}
+
+
+/* FD2EPB-- convert from FITS standard date to Besselian epoch */
+
+double
+fd2epb (string)
+
+char *string;	/* FITS date string, which may be:
+			yyyy.ffff (fractional year)
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+
+{
+    double dj;		/* Julian date */
+    dj = fd2jd (string);
+    if (dj < 1.0)
+	return (dj / 365.242198781);
+    else
+	return (jd2epb (dj));
+}
+
+
+/* FD2EPJ-- convert from FITS standard date to Julian epoch */
+
+double
+fd2epj (string)
+
+char *string;	/* FITS date string, which may be:
+			yyyy.ffff (fractional year)
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+
+{
+    double dj;		/* Julian date */
+    dj = fd2jd (string);
+    if (dj < 1.0)
+	return (dj / 365.25);
+    else
+	return (jd2epj (dj));
+}
+
+
+/* DT2TSU-- convert from date and time to Unix seconds since 1970-01-01T0:00 */
+
+time_t
+dt2tsu (date,time)
+
+double	date;	/* Date as yyyy.mmdd */
+double	time;	/* Time as hh.mmssxxxx
+		 *if time<0, it is time as -(fraction of a day) */
+{
+    return ((time_t)(dt2ts (date, time) - 631152000.0));
+}
+
+
+/* DT2TSI-- convert from date and time to IRAF seconds since 1980-01-01T0:00 */
+
+int
+dt2tsi (date,time)
+
+double	date;	/* Date as yyyy.mmdd */
+double	time;	/* Time as hh.mmssxxxx
+		 *if time<0, it is time as -(fraction of a day) */
+{
+    return ((int)(dt2ts (date, time) - 946684800.0));
+}
+
+
+
+/* DT2TS-- convert from date, time as yyyy.mmdd hh.mmsss to sec since 1950.0 */
+
+double
+dt2ts (date,time)
+
+double	date;	/* Date as yyyy.mmdd
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    double tsec; /* Seconds past 1950.0 (returned) */
+
+    double dh,dm,dd;
+    int iy,im,id;
+
+/* Calculate the number of full years, months, and days already
+ * elapsed since 0h, March 1, -1 (up to most recent midnight). */
+
+    /* convert time of day to elapsed seconds */
+
+    /* If time is < 0, it is assumed to be a fractional day */
+    if (time < 0.0)
+	tsec = time * -86400.0;
+    else {
+	dh = (int) (time + 0.0000000001);
+	dm = (int) (((time - dh) * 100.0) + 0.0000000001);
+	tsec = (time * 10000.0) - (dh * 10000.0) - (dm * 100.0);
+	tsec = (int) (tsec * 100000.0 + 0.0001) / 100000.0;
+	tsec = tsec + (dm * 60.0) + (dh * 3600.0);
+	}
+
+
+    /* Calculate the number of full months elapsed since
+     * the current or most recent March */
+    if (date >= 0.0301) {
+	iy = (int) (date + 0.0000000001);
+	im = (int) (((date - (double) (iy)) * 10000.0) + 0.00000001);
+	id = im % 100;
+	im = (im / 100) + 9;
+	if (im < 12) iy = iy - 1;
+	im = im % 12;
+	id = id - 1;
+
+	/* starting with March as month 0 and ending with the following
+	 * February as month 11, the calculation of the number of days
+	 * per month reduces to a simple formula. the following statement
+	 * determines the number of whole days elapsed since 3/1/-1 and then
+	 * subtracts the 712163 days between then and 1/1/1950.  it converts
+	 * the result to seconds and adds the accumulated seconds above. */
+	id = id + ((im+1+im/6+im/11)/2 * 31) + ((im-im/6-im/11)/2 * 30) +
+	     (iy / 4) - (iy / 100) + (iy / 400);
+	dd = (double) id + (365.0 * (double) iy) - 712163.0;
+	tsec = tsec + (dd * 86400.0);
+	}
+
+    return (tsec);
+}
+
+
+/* TS2DT-- convert seconds since 1950.0 to date, time as yyyy.mmdd hh.mmssss */
+
+void
+ts2dt (tsec,date,time)
+
+double	tsec;	/* Seconds past 1950.0 */
+double	*date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	*time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+{
+    int iyr,imon,iday,ihr,imn;
+    double sec;
+
+    ts2i (tsec,&iyr,&imon,&iday,&ihr,&imn,&sec, 4);
+
+    /* Convert date to yyyy.mmdd */
+    if (iyr < 0) {
+	*date = (double) (-iyr) + 0.01 * (double) imon + 0.0001 * (double) iday;
+	*date = -(*date);
+	}
+    else
+	*date = (double) iyr + 0.01 * (double) imon + 0.0001 * (double) iday;
+
+    /* Convert time to hh.mmssssss */
+    *time = (double) ihr + 0.01 * (double) imn + 0.0001 * sec;
+
+    return;
+}
+
+
+/* TSI2DT-- Convert seconds since 1980-01-01 to date yyyy.ddmm, time hh.mmsss */
+
+void
+tsi2dt (isec,date,time)
+
+int	isec;	/* Seconds past 1980-01-01 */
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    ts2dt (tsi2ts (isec), date, time);
+}
+
+
+/* TSI2FD-- Convert seconds since 1980-01-01 to FITS standard date string */
+
+char *
+tsi2fd (isec)
+
+int	isec;	/* Seconds past 1980-01-01 */
+{
+    return (ts2fd (tsi2ts (isec)));
+}
+
+
+/* TSI2TS-- Convert seconds since 1980-01-01 to seconds since 1950-01-01 */
+
+double
+tsi2ts (isec)
+int	isec;	/* Seconds past 1980-01-01 */
+{
+    return ((double) isec + 946684800.0);
+}
+
+
+/* TSU2FD-- Convert seconds since 1970-01-01 to FITS standard date string */
+
+char *
+tsu2fd (isec)
+time_t	isec;	/* Seconds past 1970-01-01 */
+{
+    return (ts2fd (tsu2ts (isec)));
+}
+
+
+/* TSU2DT-- Convert seconds since 1970-01-01 to date yyyy.ddmm, time hh.mmsss */
+
+void
+tsu2dt (isec,date,time)
+time_t	isec;	/* Seconds past 1970-01-01 */
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    ts2dt (tsu2ts (isec), date, time);
+}
+
+
+/* TSU2TS-- Convert seconds since 1970-01-01 to seconds since 1950-01-01 */
+
+double
+tsu2ts (isec)
+time_t	isec;	/* Seconds past 1970-01-01 */
+{
+    return ((double) isec + 631152000.0);
+}
+
+/* TSU2TSI-- UT seconds since 1970-01-01 to local seconds since 1980-01-01 */
+
+int
+tsu2tsi (isec)
+time_t	isec;	/* Seconds past 1970-01-01 */
+{
+    double date, time;
+    struct tm *ts;
+
+    /* Get local time  from UT seconds */
+    ts = localtime (&isec);
+    if (ts->tm_year < 1000)
+	date = (double) (ts->tm_year + 1900);
+    else
+	date = (double) ts->tm_year;
+    date = date + (0.01 * (double) (ts->tm_mon + 1));
+    date = date + (0.0001 * (double) ts->tm_mday);
+    time = (double) ts->tm_hour;
+    time = time + (0.01 * (double) ts->tm_min);
+    time = time + (0.0001 * (double) ts->tm_sec);
+    return ((int)(dt2ts (date, time) - 631152000.0));
+}
+
+
+/* TS2FD-- convert seconds since 1950.0 to FITS date, yyyy-mm-ddThh:mm:ss.ss */
+
+char *
+ts2fd (tsec)
+
+double	tsec;	/* Seconds past 1950.0 */
+{
+    double date, time;
+
+    ts2dt (tsec, &date, &time);
+    return (dt2fd (date, time));
+}
+
+
+/* TSD2FD-- convert seconds since start of day to FITS time, hh:mm:ss.ss */
+
+char *
+tsd2fd (tsec)
+
+double	tsec;	/* Seconds since start of day */
+{
+    double date, time;
+    char *thms, *fdate;
+    int lfd, nbc;
+
+    ts2dt (tsec, &date, &time);
+    fdate = dt2fd (date, time);
+    thms = (char *) calloc (16, 1);
+    lfd = strlen (fdate);
+    nbc = lfd - 11;
+    strncpy (thms, fdate+11, nbc);
+    return (thms);
+}
+
+
+/* TSD2DT-- convert seconds since start of day to hh.mmssss */
+
+double
+tsd2dt (tsec)
+
+double	tsec;	/* Seconds since start of day */
+{
+    double date, time;
+
+    ts2dt (tsec, &date, &time);
+    return (time);
+}
+
+
+
+/* DT2I-- convert vigesimal date and time to year month day hours min sec */
+
+void
+dt2i (date, time, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+double	date;	/* Date as yyyy.mmdd (returned)
+		    yyyy = calendar year (e.g. 1973)
+		    mm = calendar month (e.g. 04 = april)
+		    dd = calendar day (e.g. 15) */
+double	time;	/* Time as hh.mmssxxxx (returned)
+		    *if time<0, it is time as -(fraction of a day)
+		    hh = hour of day (0 .le. hh .le. 23)
+		    nn = minutes (0 .le. nn .le. 59)
+		    ss = seconds (0 .le. ss .le. 59)
+		  xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double t,d;
+
+    t = time;
+    if (date < 0.0)
+	d = -date;
+    else
+	d = date;
+
+    /* Extract components of time */
+    *ihr = dint (t + 0.000000001);
+    t = 100.0 * (t - (double) *ihr);
+    *imn = dint (t + 0.0000001);
+    *sec = 100.0 * (t - (double) *imn);
+
+    /* Extract components of date */
+    *iyr = dint (d + 0.00001);
+    d = 100.0 * (d - (double) *iyr);
+    if (date < 0.0)
+	*iyr = - *iyr;
+    *imon = dint (d + 0.001);
+    d = 100.0 * (d - (double) *imon);
+    *iday = dint (d + 0.1);
+
+   /* Make sure date and time are legal */
+    fixdate (iyr, imon, iday, ihr, imn, sec, ndsec);
+
+    return;
+}
+
+
+/* FD2I-- convert from FITS standard date to year, mon, day, hours, min, sec */
+
+void
+fd2i (string, iyr, imon, iday, ihr, imn, sec, ndsec)
+
+char	*string; /* FITS date string, which may be:
+			yyyy.ffff (fractional year)
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double tsec, fday, hr, mn;
+    int i;
+    char *sstr, *dstr, *tstr, *cstr, *nval, *fstr;
+
+    /* Initialize all returned data to zero */
+    *iyr = 0;
+    *imon = 0;
+    *iday = 0;
+    *ihr = 0;
+    *imn = 0;
+    *sec = 0.0;
+
+    /* Return if no input string */
+    if (string == NULL)
+	return;
+
+    /* Check for various non-numeric characters */
+    sstr = strchr (string,'/');
+    dstr = strchr (string,'-');
+    if (dstr == string)
+	dstr = strchr (string+1, '-');
+    fstr = strchr (string, '.');
+    tstr = strchr (string,'T');
+    if (tstr == NULL)
+	tstr = strchr (string, 'Z');
+    if (tstr == NULL)
+	tstr = strchr (string, 'S');
+    if (fstr != NULL && tstr != NULL && fstr > tstr)
+	fstr = NULL;
+    cstr = strchr (string,':');
+
+    /* Original FITS date format: dd/mm/yy */
+    if (sstr > string) {
+	*sstr = '\0';
+	*iday = (int) atof (string);
+	if (*iday > 31) {
+	    *iyr = *iday;
+	    if (*iyr >= 0 && *iyr <= 49)
+		*iyr = *iyr + 2000;
+	    else if (*iyr < 1000)
+		*iyr = *iyr + 1900;
+	    *sstr = '/';
+	    nval = sstr + 1;
+	    sstr = strchr (nval,'/');
+	    if (sstr > string) {
+		*sstr = '\0';
+		*imon = (int) atof (nval);
+		*sstr = '/';
+		nval = sstr + 1;
+		*iday = (int) atof (nval);
+		}
+	    }
+	else {
+	    *sstr = '/';
+	    nval = sstr + 1;
+	    sstr = strchr (nval,'/');
+	    if (sstr == NULL)
+		sstr = strchr (nval,'-');
+	    if (sstr > string) {
+		*sstr = '\0';
+		*imon = (int) atof (nval);
+		*sstr = '/';
+		nval = sstr + 1;
+		*iyr = (int) atof (nval);
+		if (*iyr >= 0 && *iyr <= 49)
+		    *iyr = *iyr + 2000;
+		else if (*iyr < 1000)
+		    *iyr = *iyr + 1900;
+		}
+	    }
+	tstr = strchr (string,'_');
+	if (tstr == NULL)
+	    return;
+	}
+
+    /* New FITS date format: yyyy-mm-ddThh:mm:ss[.sss] */
+    else if (dstr > string) {
+	*dstr = '\0';
+	*iyr = (int) atof (string);
+	*dstr = '-';
+	nval = dstr + 1;
+	dstr = strchr (nval,'-');
+	*imon = 1;
+	*iday = 1;
+
+	/* Decode year, month, and day */
+	if (dstr > string) {
+	    *dstr = '\0';
+	    *imon = (int) atof (nval);
+	    *dstr = '-';
+	    nval = dstr + 1;
+	    if (tstr > string)
+		*tstr = '\0';
+	    *iday = (int) atof (nval);
+
+	    /* If fraction of a day is present, turn it into a time */
+	    if (fstr != NULL) {
+		fday = atof (fstr);
+		hr = fday * 24.0;
+		*ihr = (int) hr;
+		mn = 60.0 * (hr - (double) *ihr);
+		*imn = (int) mn;
+		*sec = 60.0 * (mn - (double) *imn);
+		}
+
+	    if (tstr > string)
+		*tstr = 'T';
+	    }
+
+	/* If date is > 31, it is really year in old format */
+	if (*iday > 31) {
+	    i = *iyr;
+	    if (*iday < 100)
+		*iyr = *iday + 1900;
+	    else
+		*iyr = *iday;
+	    *iday = i;
+	    }
+	}
+
+    /* In rare cases, a FITS time is entered as an epoch */
+    else if (tstr == NULL && cstr == NULL && isnum (string)) {
+	tsec = ep2ts (atof (string));
+	ts2i (tsec,iyr,imon,iday,ihr,imn,sec, ndsec);
+	return;
+	}
+
+    /* Extract time, if it is present */
+    if (tstr > string || cstr > string) {
+	if (tstr > string)
+	    nval = tstr + 1;
+	else
+	    nval = string;
+	cstr = strchr (nval,':');
+	if (cstr > string) {
+	    *cstr = '\0';
+	    *ihr = (int) atof (nval);
+	    *cstr = ':';
+	    nval = cstr + 1;
+	    cstr = strchr (nval,':');
+	    if (cstr > string) {
+		*cstr = '\0';
+		*imn = (int) atof (nval);
+		*cstr = ':';
+		nval = cstr + 1;
+		*sec = atof (nval);
+		}
+	    else
+		*imn = (int) atof (nval);
+	    }
+	else
+	    *ihr = (int) atof (nval);
+	}
+    else
+	ndsec = -1;
+
+   /* Make sure date and time are legal */
+    fixdate (iyr, imon, iday, ihr, imn, sec, ndsec);
+
+    return;
+}
+
+
+/* TS2I-- convert sec since 1950.0 to year month day hours minutes seconds */
+
+void
+ts2i (tsec,iyr,imon,iday,ihr,imn,sec, ndsec)
+
+double	tsec;	/* seconds since 1/1/1950 0:00 */
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+
+{
+    double t,days, ts, dts;
+    int nc,nc4,nly,ny,m,im;
+
+    /* Round seconds to 0 - 4 decimal places */
+    ts = tsec + 61530883200.0;
+    if (ts < 0.0)
+	dts = -0.5;
+    else
+	dts = 0.5;
+    if (ndsec < 1)
+	t = dint (ts + dts) * 10000.0;
+    else if (ndsec < 2)
+	t = dint (ts * 10.0 + dts) * 1000.0;
+    else if (ndsec < 3)
+	t = dint (ts * 100.0 + dts) * 100.0;
+    else if (ndsec < 4)
+	t = dint (ts * 1000.0 + dts) * 10.0;
+    else
+	t = dint (ts * 10000.0 + dts);
+    ts = t / 10000.0;
+
+    /* Time of day (hours, minutes, seconds */
+    *ihr = (int) (dmod (ts/3600.0, 24.0));
+    *imn = (int) (dmod (ts/60.0, 60.0));
+    *sec = dmod (ts, 60.0);
+
+    /* Number of days since 0 hr 0/0/0000 */
+    days = dint ((t / 864000000.0) + 0.000001);
+
+    /* Number of leap centuries (400 years) */
+    nc4 = (int) ((days / 146097.0) + 0.00001);
+
+    /* Number of centuries since last /400 */
+    days = days - (146097.0 * (double) (nc4));
+    nc = (int) ((days / 36524.0) + 0.000001);
+    if (nc > 3) nc = 3;
+
+    /* Number of leap years since last century */
+    days = days - (36524.0 * nc);
+    nly = (int) ((days / 1461.0) + 0.0000000001);
+
+    /* Number of years since last leap year */
+    days = days - (1461.0 * (double) nly);
+    ny = (int) ((days / 365.0) + 0.00000001);
+    if (ny > 3) ny = 3;
+
+    /* Day of month */
+    days = days - (365.0 * (double) ny);
+    if (days < 0) {
+	m = 0;
+	*iday = 29;
+	}
+    else {
+	*iday = (int) (days + 0.00000001) + 1;
+	for (m = 1; m <= 12; m++) {
+	    im = (m + ((m - 1) / 5)) % 2;
+	    /* fprintf (stderr,"%d %d %d %d\n", m, im, *iday, nc); */
+	    if (*iday-1 < im+30) break;
+	    *iday = *iday - im - 30;
+	    }
+	}
+
+    /* Month */
+    *imon = ((m+1) % 12) + 1;
+
+    /* Year */
+    *iyr = nc4*400 + nc*100 + nly*4 + ny + m/11;
+
+   /* Make sure date and time are legal */
+    fixdate (iyr, imon, iday, ihr, imn, sec, ndsec);
+
+    return;
+}
+
+
+/* UT2DOY-- Current Universal Time as year, day of year */
+
+void
+ut2doy (year, doy)
+
+int	*year;	/* Year (returned) */
+double	*doy;	/* Day of year (returned) */
+{
+    double date, time;
+    ut2dt (&date, &time);
+    dt2doy (date, time, year, doy);
+    return;
+}
+
+
+/* UT2DT-- Current Universal Time as date (yyyy.mmdd) and time (hh.mmsss) */
+
+void
+ut2dt(date, time)
+
+double	*date;	/* Date as yyyy.mmdd (returned) */
+double	*time;	/* Time as hh.mmssxxxx (returned) */
+{
+    time_t tsec;
+    struct timeval tp;
+    struct timezone tzp;
+    struct tm *ts;
+
+    gettimeofday (&tp,&tzp);
+
+    tsec = tp.tv_sec;
+    ts = gmtime (&tsec);
+
+    if (ts->tm_year < 1000)
+	*date = (double) (ts->tm_year + 1900);
+    else
+	*date = (double) ts->tm_year;
+    *date = *date + (0.01 * (double) (ts->tm_mon + 1));
+    *date = *date + (0.0001 * (double) ts->tm_mday);
+    *time = (double) ts->tm_hour;
+    *time = *time + (0.01 * (double) ts->tm_min);
+    *time = *time + (0.0001 * (double) ts->tm_sec);
+
+    return;
+}
+
+
+/* UT2EP-- Return current Universal Time as fractional year */
+
+double
+ut2ep()
+{
+    return (jd2ep (ut2jd()));
+}
+
+
+/* UT2EPB-- Return current Universal Time as Besselian epoch */
+
+double
+ut2epb()
+{
+    return (jd2epb (ut2jd()));
+}
+
+
+/* UT2EPJ-- Return current Universal Time as Julian epoch */
+
+double
+ut2epj()
+{
+    return (jd2epj (ut2jd()));
+}
+
+
+/* UT2FD-- Return current Universal Time as FITS ISO date string */
+
+char *
+ut2fd()
+{
+    int year, month, day, hour, minute, second;
+    time_t tsec;
+    struct timeval tp;
+    struct timezone tzp;
+    struct tm *ts;
+    char *isotime;
+
+    gettimeofday (&tp,&tzp);
+    tsec = tp.tv_sec;
+    ts = gmtime (&tsec);
+
+    year = ts->tm_year;
+    if (year < 1000)
+	year = year + 1900;
+    month = ts->tm_mon + 1;
+    day = ts->tm_mday;
+    hour = ts->tm_hour;
+    minute = ts->tm_min;
+    second = ts->tm_sec; 
+
+    isotime = (char *) calloc (32, sizeof (char));
+    sprintf (isotime, "%04d-%02d-%02dT%02d:%02d:%02d",
+		      year, month, day, hour, minute, second);
+    return (isotime);
+}
+
+
+/* UT2JD-- Return current Universal Time as Julian Date */
+
+double
+ut2jd()
+{
+    return (fd2jd (ut2fd()));
+}
+
+
+/* UT2MJD-- convert current UT to Modified Julian Date */
+
+double
+ut2mjd ()
+
+{
+    return (ut2jd() - 2400000.5);
+}
+
+/* UT2TS-- current Universal Time as IRAF seconds since 1950-01-01T00:00 */
+
+double
+ut2ts()
+{
+    double tsec;
+    char *datestring;
+    datestring = ut2fd();
+    tsec = fd2ts (datestring);
+    free (datestring);
+    return (tsec);
+}
+
+
+/* UT2TSI-- current Universal Time as IRAF seconds since 1980-01-01T00:00 */
+
+int
+ut2tsi()
+{
+    return ((int)(ut2ts() - 946684800.0));
+}
+
+
+/* UT2TSU-- current Universal Time as IRAF seconds since 1970-01-01T00:00 */
+
+time_t
+ut2tsu()
+{
+    return ((time_t)(ut2ts () - 631152000.0));
+}
+
+
+/* FD2GST-- convert from FITS date to Greenwich Sidereal Time */
+
+char *
+fd2gst (string)
+
+char	*string;	/* FITS date string, which may be:
+			  fractional year
+			  dd/mm/yy (FITS standard before 2000)
+			  dd-mm-yy (nonstandard use before 2000)
+			  yyyy-mm-dd (FITS standard after 1999)
+			  yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double dj, gsec, date, time;
+
+    dj = fd2jd (string);
+    gsec = jd2gst (dj);
+    ts2dt (gsec, &date, &time);
+    date = 0.0;
+    return (dt2fd (date, time));
+}
+
+
+/* DT2GST-- convert from UT as yyyy.mmdd hh.mmssss to Greenwich Sidereal Time*/
+
+void
+dt2gst (date, time)
+double  *date;  /* Date as yyyy.mmdd */
+double  *time;  /* Time as hh.mmssxxxx
+                 *if time<0, it is time as -(fraction of a day) */
+{
+    double dj, gsec;
+
+    dj = dt2ts (*date, *time);
+    gsec = jd2gst (dj);
+    ts2dt (gsec, date, time);
+    *date = 0.0;
+    return;
+}
+
+
+/* JD2LST - Local Sidereal Time in seconds from Julian Date */
+
+double
+jd2lst (dj)
+
+double dj;	/* Julian Date */
+{
+    double gst, lst;
+
+    /* Compute Greenwich Sidereal Time at this epoch */
+    gst = jd2gst (dj);
+
+    /* Subtract longitude (in seconds of time) */
+    lst = gst - 3600.0 * (longitude / 15.0);
+    if (lst < 0.0)
+	lst = lst + 86400.0;
+    else if (lst > 86400.0)
+	lst = lst - 86400.0;
+    return (lst);
+}
+
+
+/* FD2LST - Local Sidereal Time  as hh:mm:ss.ss
+            from Universal Time as FITS ISO date */
+
+char *
+fd2lst (string)
+
+char	*string;	/* FITS date string, which may be:
+			  fractional year
+			  dd/mm/yy (FITS standard before 2000)
+			  dd-mm-yy (nonstandard use before 2000)
+			  yyyy-mm-dd (FITS standard after 1999) */
+{
+    double dj, date, time, lst;
+
+    dj = fd2jd (string);
+    lst = jd2lst (dj);
+    ts2dt (lst, &date, &time);
+    date = 0.0;
+    return (dt2fd (date, time));
+}
+
+
+/* DT2LST - Local Sidereal Time  as hh.mmssss
+            from Universal Time as yyyy.mmdd hh.mmssss */
+
+void
+dt2lst (date, time)
+
+double  *date;  /* Date as yyyy.mmdd */
+double  *time;  /* Time as hh.mmssxxxx
+                 *if time<0, it is time as -(fraction of a day) */
+{
+    double dj, lst, date0;
+
+    dj = dt2jd (*date, *time);
+    lst = jd2lst (dj);
+    date0 = 0.0;
+    ts2dt (lst, &date0, time);
+    return;
+}
+
+
+/* TS2LST - Local Sidereal Time in seconds of day
+ *          from Universal Time in seconds since 1951-01-01T0:00:00
+ */
+
+double
+ts2lst (tsec)
+
+double tsec;		/* time since 1950.0 in UT seconds */
+{
+    double gst;	/* Greenwich Sidereal Time in seconds since 0:00 */
+    double lst;	/* Local Sidereal Time in seconds since 0:00 */
+    double gsec, date;
+
+    /* Greenwich Sidereal Time */
+    gsec = ts2gst (tsec);
+    date = 0.0;
+    ts2dt (gsec, &date, &gst);
+
+    lst = gst - (longitude / 15.0);
+    if (lst < 0.0)
+	lst = lst + 86400.0;
+    else if (lst > 86400.0)
+	lst = lst - 86400.0;
+    return (lst);
+}
+
+
+/* LST2FD - calculate current UT given Local Sidereal Time
+ *	    plus date in FITS ISO format (yyyy-mm-dd)
+ *	    Return UT date and time in FITS ISO format
+ */
+
+char *
+lst2fd (string)
+
+char *string;		/* UT Date, LST as yyyy-mm-ddShh:mm:ss.ss */
+{
+    double sdj, dj;
+
+    sdj = fd2jd (string);
+
+    dj = lst2jd (sdj);
+
+    return (jd2fd (dj));
+}
+
+
+/* LST2JD - calculate current Julian Date given Local Sidereal Time
+ *	    plus current Julian Date (0.5 at 0:00 UT)
+ *	    Return UT date and time as Julian Date
+ */
+
+double
+lst2jd (sdj)
+
+double sdj;	/* Julian Date of desired day at 0:00 UT + sidereal time */
+{
+    double gst;	/* Greenwich Sidereal Time in seconds since 0:00 */
+    double lsd;	/* Local Sidereal Time in seconds since 0:00 */
+    double gst0, tsd, dj1, dj0, eqnx;
+    int idj;
+
+    /* Julian date at 0:00 UT */
+    idj = (int) sdj;
+    dj0 = (double) idj + 0.5;
+    if (dj0 > sdj) dj0 = dj0 - 1.0;
+
+    /* Greenwich Sidereal Time at 0:00 UT in seconds */
+    gst0 = jd2gst (dj0);
+
+    /* Sidereal seconds since 0:00 */
+    lsd = (sdj - dj0) * 86400.0;
+
+    /* Remove longitude for current Greenwich Sidereal Time in seconds */
+    /* (convert longitude from degrees to seconds of time) */
+    gst = lsd + (longitude * 240.0);
+
+    /* Time since 0:00 UT */
+    tsd = (gst - gst0) / 1.0027379093;
+
+    /* Julian Date (UT) */
+    dj1 = dj0 + (tsd / 86400.0);
+
+    /* Equation of the equinoxes converted to UT seconds */
+    eqnx = eqeqnx (dj1) / 1.002739093;
+
+    /* Remove equation of equinoxes */
+    dj1 = dj1 - (eqnx / 86400.0);
+    if (dj1 < dj0)
+	dj1 = dj1 + 1.0;
+
+    return (dj1);
+}
+
+
+/* MST2FD - calculate current UT given Greenwich Mean Sidereal Time
+ *	    plus date in FITS ISO format (yyyy-mm-ddShh:mm:ss.ss)
+ *	    Return UT date and time in FITS ISO format
+ */
+
+char *
+mst2fd (string)
+
+char *string;		/* UT Date, MST as yyyy-mm-ddShh:mm:ss.ss */
+{
+    double sdj, dj;
+
+    sdj = fd2jd (string);
+
+    dj = mst2jd (sdj);
+
+    return (jd2fd (dj));
+}
+
+
+/* MST2JD - calculate current UT given Greenwich Mean Sidereal Time
+ *	    plus date in Julian Date (0:00 UT + Mean Sidereal Time)
+ *	    Return UT date and time as Julian Date
+ */
+
+double
+mst2jd (sdj)
+
+double sdj;		/* UT Date, MST as Julian Date */
+{
+    double tsd, djd, st0, dj0, dj;
+
+    dj0 = (double) ((int) sdj) + 0.5;
+
+    /* Greenwich Mean Sidereal Time at 0:00 UT in seconds */
+    st0 = jd2mst (dj0);
+
+    /* Mean Sidereal Time in seconds */
+    tsd = (sdj - dj0) * 86400.0;
+    if (tsd < 0.0)
+	tsd = tsd + 86400.0;
+
+    /* Convert to fraction of a day since 0:00 UT */
+    djd = ((tsd - st0) / 1.0027379093) / 86400.0;
+
+    /* Julian Date */
+    dj = dj0 + djd;
+    if (dj < dj0)
+	dj = dj + (1.0 / 1.0027379093);
+
+    return (dj);
+}
+
+
+
+/* GST2FD - calculate current UT given Greenwich Sidereal Time
+ *	    plus date in FITS ISO format (yyyy-mm-ddShh:mm:ss.ss)
+ *	    Return UT date and time in FITS ISO format
+ */
+
+char *
+gst2fd (string)
+
+char *string;		/* UT Date, GST as yyyy-mm-ddShh:mm:ss.ss */
+{
+    double sdj, dj;
+
+    sdj = fd2jd (string);
+
+    dj = gst2jd (sdj);
+
+    return (jd2fd (dj));
+}
+
+
+/* GST2JD - calculate current UT given Greenwich Sidereal Time
+ *	    plus date as Julian Date (JD at 0:00 UT + sidereal time)
+ *	    Return UT date and time as Julian Date
+ */
+
+double
+gst2jd (sdj)
+
+double sdj;		/* UT Date, GST as Julian Date */
+{
+    double dj, tsd, djd, st0, dj0, eqnx;
+
+    dj0 = (double) ((int) sdj) + 0.5;
+
+    /* Greenwich Mean Sidereal Time at 0:00 UT in seconds */
+    st0 = jd2mst (dj0);
+
+    /* Mean Sidereal Time in seconds */
+    tsd = (sdj - dj0) * 86400.0;
+    if (tsd < 0.0)
+	tsd = tsd + 86400.0;
+
+    /* Convert to fraction of a day since 0:00 UT */
+    djd = ((tsd - st0) / 1.0027379093) / 86400.0;
+
+    /* Julian Date */
+    dj = dj0 + djd;
+
+    /* Equation of the equinoxes (converted to UT seconds) */
+    eqnx = eqeqnx (dj) / 1.002737909;
+
+    dj = dj - eqnx / 86400.0;
+    if (dj < dj0)
+	dj = dj + 1.0;
+
+    return (dj);
+}
+
+
+/* LST2DT - calculate current UT given Local Sidereal Time as hh.mmsss
+ *	    plus date as yyyy.mmdd
+ *	    Return UT time as hh.mmssss
+ */
+
+double
+lst2dt (date0, time0)
+
+double date0;	/* UT date as yyyy.mmdd */
+double time0;	/* LST as hh.mmssss */
+{
+    double gst;	/* Greenwich Sidereal Time in seconds since 0:00 */
+    double lst;	/* Local Sidereal Time in seconds since 0:00 */
+    double date1; /* UT date as yyyy.mmdd */
+    double time1; /* UT as hh.mmssss */
+    double tsec0, gst0, tsd, tsec;
+
+    /* Greenwich Sidereal Time at 0:00 UT */
+    tsec0 = dt2ts (date0, 0.0);
+    gst0 = ts2gst (tsec0);
+
+    /* Current Greenwich Sidereal Time in seconds */
+    /* (convert longitude from degrees to seconds of time) */
+    lst = dt2ts (0.0, time0);
+    gst = lst + (longitude * 240.0);
+
+    /* Time since 0:00 UT */
+    tsd = (gst - gst0) / 1.0027379093;
+
+    /* UT date and time */
+    tsec = tsec0 + tsd;
+    ts2dt (tsec, &date1, &time1);
+
+    return (time1);
+}
+
+
+/* TS2GST - calculate Greenwich Sidereal Time given Universal Time
+ *	    in seconds since 1951-01-01T0:00:00
+ *	    Return sidereal time of day in seconds
+ */
+
+double
+ts2gst (tsec)
+
+double tsec;	/* time since 1950.0 in UT seconds */
+{
+    double gst;	/* Greenwich Sidereal Time in seconds since 0:00 */
+    double tsd, eqnx, dj;
+    int its;
+
+    /* Elapsed time as of 0:00 UT */
+    if (tsec >= 0.0) {
+	its = (int) (tsec + 0.5);
+	tsd = (double) (its % 86400);
+	}
+    else {
+	its = (int) (-tsec + 0.5);
+	tsd = (double) (86400 - (its % 86400));
+	}
+
+    /* Mean sidereal time */
+    gst = ts2mst (tsec);
+
+    /* Equation of the equinoxes */
+    dj = ts2jd (tsec);
+    eqnx = eqeqnx (dj);
+
+    /* Apparent sidereal time at 0:00 ut */
+    gst = gst + eqnx;
+
+    /* Current sidereal time */
+    gst = gst + (tsd * 1.0027379093);
+    gst = dmod (gst,86400.0);
+
+    return (gst);
+}
+
+
+/* FD2MST-- convert from FITS date Mean Sidereal Time */
+
+char *
+fd2mst (string)
+
+char	*string;	/* FITS date string, which may be:
+			  fractional year
+			  dd/mm/yy (FITS standard before 2000)
+			  dd-mm-yy (nonstandard use before 2000)
+			  yyyy-mm-dd (FITS standard after 1999)
+			  yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+{
+    double gsec, date, time, dj;
+
+    dj = fd2jd (string);
+    gsec = jd2mst (dj);
+    ts2dt (gsec, &date, &time);
+    date = 0.0;
+    return (dt2fd (date, time));
+}
+
+
+/* DT2MST-- convert from UT as yyyy.mmdd hh.mmssss to Mean Sidereal Time
+	    in the same format */
+
+void
+dt2mst (date, time)
+double  *date;  /* Date as yyyy.mmdd */
+double  *time;  /* Time as hh.mmssxxxx
+                 *if time<0, it is time as -(fraction of a day) */
+{
+    double date0, gsec, dj;
+    date0 = *date;
+    dj = dt2jd (*date, *time);
+    gsec = jd2mst (dj);
+    ts2dt (gsec, date, time);
+    *date = date0;
+    return;
+}
+
+
+/* TS2MST - calculate Greenwich Mean Sidereal Time given Universal Time
+ *	    in seconds since 1951-01-01T0:00:00
+ */
+
+double
+ts2mst (tsec)
+
+double tsec;	/* time since 1950.0 in UT seconds */
+{
+    double dj;
+
+    dj = ts2jd (tsec);
+    return (jd2mst (dj));
+}
+
+
+/* JD2MST - Julian Date to Greenwich Mean Sidereal Time using IAU 2000
+ *	    Return sideral time in seconds of time
+ *	    (from USNO NOVAS package
+ *	     http://aa.usno.navy.mil/software/novas/novas_info.html
+ */
+
+double
+jd2mst2 (dj)
+
+double	dj;	/* Julian Date */
+{
+    double dt, t, t2, t3, mst, st;
+
+    dt = dj - 2451545.0;
+    t = dt / 36525.0;
+    t2 = t * t;
+    t3 = t2 * t;
+
+    /* Compute Greenwich Mean Sidereal Time in seconds */
+    st = (8640184.812866 * t) +  (3155760000.0 * t) - (0.0000062 * t3)
+	 + (0.093104 * t2) + 67310.54841;
+
+    mst = dmod (st, 86400.0);
+    if (mst < 0.0)
+	mst = mst + 86400.0;
+    return (mst);
+}
+
+
+/* MJD2MST - Modified Julian Date to Greenwich Mean Sidereal Time using IAU 2000
+ *	    Return sideral time in seconds of time
+ *	    (from USNO NOVAS package
+ *	     http://aa.usno.navy.mil/software/novas/novas_info.html
+ */
+
+double
+mjd2mst (dj)
+
+double	dj;	/* Modified Julian Date */
+{
+    double dt, t, t2, t3, mst, st;
+
+    dt = dj - 51544.5;
+    t = dt / 36525.0;
+    t2 = t * t;
+    t3 = t2 * t;
+
+    /* Compute Greenwich Mean Sidereal Time in seconds */
+    st = (8640184.812866 * t) +  (3155760000.0 * t) - (0.0000062 * t3)
+	 + (0.093104 * t2) + 67310.54841;
+
+    mst = dmod (st, 86400.0);
+    if (mst < 0.0)
+	mst = mst + 86400.0;
+    return (mst);
+}
+
+
+/* JD2GST - Julian Date to Greenwich Sideral Time
+ *          Return sideral time in seconds of time
+ *	    (Jean Meeus, Astronomical Algorithms, Willmann-Bell, 1991, pp 83-84)
+ */
+
+double
+jd2gst (dj)
+
+double	dj;	/* Julian Date */
+{
+    double dj0, gmt, gst, tsd, eqnx, ssd;
+    double ts2ss = 1.00273790935;
+    int ijd;
+
+    /* Julian date at 0:00 UT */
+    ijd = (int) dj;
+    dj0 = (double) ijd + 0.5;
+    if (dj0 > dj) dj0 = dj0 - 1.0;
+
+    /* Greenwich mean sidereal time at 0:00 UT in seconds */
+    gmt = jd2mst (dj0);
+
+    /* Equation of the equinoxes */
+    eqnx = eqeqnx (dj);
+
+    /* Apparent sidereal time at 0:00 ut */
+    gst = gmt + eqnx;
+
+    /* UT seconds since 0:00 */
+    tsd = (dj - dj0) * 86400.0;
+    ssd = tsd * ts2ss;
+
+    /* Current sidereal time */
+    gst = gst + ssd;
+    gst = dmod (gst, 86400.0);
+
+    return (gst);
+}
+
+
+/* EQEQNX - Compute equation of the equinoxes for apparent sidereal time */
+
+double
+eqeqnx (dj)
+
+double	dj;	/* Julian Date */
+
+{
+    double dt, edj, dpsi, deps, obl, eqnx;
+    double rad2tsec = 13750.98708;
+
+    /* Convert UT to Ephemeris Time (TDB or TT)*/
+    dt = utdt (dj);
+    edj = dj + dt / 86400.0;
+
+    /* Nutation and obliquity */
+    compnut (edj, &dpsi, &deps, &obl);
+
+    /* Correct obliquity for nutation */
+    obl = obl + deps;
+
+    /* Equation of the equinoxes in seconds */
+    eqnx = (dpsi * cos (obl)) * rad2tsec;
+
+    return (eqnx);
+}
+
+
+
+/* JD2MST - Julian Date to Mean Sideral Time
+ *          Return sideral time in seconds of time
+ *	    (Jean Meeus, Astronomical Algorithms, Willmann-Bell, 1991, pp 83-84)
+ */
+
+double
+jd2mst (dj)
+
+double	dj;	/* Julian Date */
+{
+    double dt, t, mst;
+
+    dt = dj - 2451545.0;
+    t = dt / 36525.0;
+
+    /* Compute Greenwich mean sidereal time in degrees (Meeus, page 84) */
+    mst = 280.46061837 + (360.98564736629 * dt) + (0.000387933 * t * t) -
+	  (t * t * t / 38710000.0);
+
+    /* Keep degrees between 0 and 360 */
+    while (mst > 360.0)
+	mst = mst - 360.0;
+    while (mst < 0.0)
+	mst = mst + 360.0;
+
+    /* Convert to time in seconds  (3600 / 15) */
+    mst = mst * 240.0;
+
+    /* Subtract longitude (in seconds of time) */
+    mst = mst - 3600.0 * (longitude / 15.0);
+    if (mst < 0.0)
+	mst = mst + 86400.0;
+    else if (mst > 86400.0)
+	mst = mst - 86400.0;
+
+    return (mst);
+}
+
+
+/*  COMPNUT - Compute nutation using the IAU 2000b model */
+/*  Translated from Pat Wallace's Fortran subroutine iau_nut00b (June 26 2007)
+    into C by Doug Mink on September 5, 2008 */
+
+#define NLS	77 /* number of terms in the luni-solar nutation model */
+
+void
+compnut (dj, dpsi, deps, eps0)
+
+double	dj;	/* Julian Date */
+double *dpsi;   /* Nutation in longitude in radians (returned) */
+double *deps;   /* Nutation in obliquity in radians (returned) */
+double *eps0;   /* Mean obliquity in radians (returned) */
+
+/*  This routine is translated from the International Astronomical Union's
+ *  Fortran SOFA (Standards Of Fundamental Astronomy) software collection.
+ *
+ *  notes:
+ *
+ *  1) the nutation components in longitude and obliquity are in radians
+ *     and with respect to the equinox and ecliptic of date.  the
+ *     obliquity at j2000 is assumed to be the lieske et al. (1977) value
+ *     of 84381.448 arcsec.  (the errors that result from using this
+ *     routine with the iau 2006 value of 84381.406 arcsec can be
+ *     neglected.)
+ *
+ *     the nutation model consists only of luni-solar terms, but includes
+ *     also a fixed offset which compensates for certain long-period
+ *     planetary terms (note 7).
+ *
+ *  2) this routine is an implementation of the iau 2000b abridged
+ *     nutation model formally adopted by the iau general assembly in
+ *     2000.  the routine computes the mhb_2000_short luni-solar nutation
+ *     series (luzum 2001), but without the associated corrections for
+ *     the precession rate adjustments and the offset between the gcrs
+ *     and j2000 mean poles.
+ *
+ *  3) the full IAU 2000a (mhb2000) nutation model contains nearly 1400
+ *     terms.  the IAU 2000b model (mccarthy & luzum 2003) contains only
+ *     77 terms, plus additional simplifications, yet still delivers
+ *     results of 1 mas accuracy at present epochs.  this combination of
+ *     accuracy and size makes the IAU 2000b abridged nutation model
+ *     suitable for most practical applications.
+ *
+ *     the routine delivers a pole accurate to 1 mas from 1900 to 2100
+ *     (usually better than 1 mas, very occasionally just outside 1 mas).
+ *     the full IAU 2000a model, which is implemented in the routine
+ *     iau_nut00a (q.v.), delivers considerably greater accuracy at
+ *     current epochs;  however, to realize this improved accuracy,
+ *     corrections for the essentially unpredictable free-core-nutation
+ *     (fcn) must also be included.
+ *
+ *  4) the present routine provides classical nutation.  the
+ *     mhb_2000_short algorithm, from which it is adapted, deals also
+ *     with (i) the offsets between the gcrs and mean poles and (ii) the
+ *     adjustments in longitude and obliquity due to the changed
+ *     precession rates.  these additional functions, namely frame bias
+ *     and precession adjustments, are supported by the sofa routines
+ *     iau_bi00 and iau_pr00.
+ *
+ *  6) the mhb_2000_short algorithm also provides "total" nutations,
+ *     comprising the arithmetic sum of the frame bias, precession
+ *     adjustments, and nutation (luni-solar + planetary).  these total
+ *     nutations can be used in combination with an existing IAU 1976
+ *     precession implementation, such as iau_pmat76, to deliver gcrs-to-
+ *     true predictions of mas accuracy at current epochs.  however, for
+ *     symmetry with the iau_nut00a routine (q.v. for the reasons), the
+ *     sofa routines do not generate the "total nutations" directly.
+ *     should they be required, they could of course easily be generated
+ *     by calling iau_bi00, iau_pr00 and the present routine and adding
+ *     the results.
+ *
+ *  7) the IAU 2000b model includes "planetary bias" terms that are fixed
+ *     in size but compensate for long-period nutations.  the amplitudes
+ *     quoted in mccarthy & luzum (2003), namely dpsi = -1.5835 mas and
+ *     depsilon = +1.6339 mas, are optimized for the "total nutations"
+ *     method described in note 6.  the luzum (2001) values used in this
+ *     sofa implementation, namely -0.135 mas and +0.388 mas, are
+ *     optimized for the "rigorous" method, where frame bias, precession
+ *     and nutation are applied separately and in that order.  during the
+ *     interval 1995-2050, the sofa implementation delivers a maximum
+ *     error of 1.001 mas (not including fcn).
+ *
+ *  References from original Fortran subroutines:
+ *
+ *     Hilton, J. et al., 2006, Celest.Mech.Dyn.Astron. 94, 351
+ *
+ *     Lieske, J.H., Lederle, T., Fricke, W., Morando, B., "Expressions
+ *     for the precession quantities based upon the IAU 1976 system of
+ *     astronomical constants", Astron.Astrophys. 58, 1-2, 1-16. (1977)
+ *
+ *     Luzum, B., private communication, 2001 (Fortran code
+ *     mhb_2000_short)
+ *
+ *     McCarthy, D.D. & Luzum, B.J., "An abridged model of the
+ *     precession-nutation of the celestial pole", Cel.Mech.Dyn.Astron.
+ *     85, 37-49 (2003)
+ *
+ *     Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M.,
+ *     Francou, G., Laskar, J., Astron.Astrophys. 282, 663-683 (1994)
+ *
+ */
+
+{ 
+    double as2r = 0.000004848136811095359935899141; /* arcseconds to radians */
+
+    double dmas2r = as2r / 1000.0;	/* milliarcseconds to radians */
+
+    double as2pi = 1296000.0;	/* arc seconds in a full circle */
+
+    double d2pi = 6.283185307179586476925287; /* 2pi */
+
+    double u2r = as2r / 10000000.0;  /* units of 0.1 microarcsecond to radians */
+
+    double dj0 = 2451545.0;	/* reference epoch (j2000), jd */
+
+    double djc = 36525.0;	/* Days per julian century */
+
+    /*  Miscellaneous */
+    double t, el, elp, f, d, om, arg, dp, de, sarg, carg;
+    double dpsils, depsls, dpsipl, depspl;
+    int i, j;
+
+    int nls = NLS; /* number of terms in the luni-solar nutation model */
+
+    /* Fixed offset in lieu of planetary terms (radians) */
+    double dpplan = - 0.135 * dmas2r;
+    double deplan = + 0.388 * dmas2r;
+
+/* Tables of argument and term coefficients */
+
+    /* Coefficients for fundamental arguments
+    /* Luni-solar argument multipliers: */
+    /*       l     l'    f     d     om */
+static int nals[5*NLS]=
+	    {0,    0,    0,    0,    1,
+             0,    0,    2,   -2,    2,
+             0,    0,    2,    0,    2,
+             0,    0,    0,    0,    2,
+             0,    1,    0,    0,    0,
+             0,    1,    2,   -2,    2,
+             1,    0,    0,    0,    0,
+             0,    0,    2,    0,    1,
+             1,    0,    2,    0,    2,
+             0,   -1,    2,   -2,    2,
+             0,    0,    2,   -2,    1,
+            -1,    0,    2,    0,    2,
+            -1,    0,    0,    2,    0,
+             1,    0,    0,    0,    1,
+            -1,    0,    0,    0,    1,
+            -1,    0,    2,    2,    2,
+             1,    0,    2,    0,    1,
+            -2,    0,    2,    0,    1,
+             0,    0,    0,    2,    0,
+             0,    0,    2,    2,    2,
+             0,   -2,    2,   -2,    2,
+            -2,    0,    0,    2,    0,
+             2,    0,    2,    0,    2,
+             1,    0,    2,   -2,    2,
+            -1,    0,    2,    0,    1,
+             2,    0,    0,    0,    0,
+             0,    0,    2,    0,    0,
+             0,    1,    0,    0,    1,
+            -1,    0,    0,    2,    1,
+             0,    2,    2,   -2,    2,
+             0,    0,   -2,    2,    0,
+             1,    0,    0,   -2,    1,
+             0,   -1,    0,    0,    1,
+            -1,    0,    2,    2,    1,
+             0,    2,    0,    0,    0,
+             1,    0,    2,    2,    2,
+            -2,    0,    2,    0,    0,
+             0,    1,    2,    0,    2,
+             0,    0,    2,    2,    1,
+             0,   -1,    2,    0,    2,
+             0,    0,    0,    2,    1,
+             1,    0,    2,   -2,    1,
+             2,    0,    2,   -2,    2,
+            -2,    0,    0,    2,    1,
+             2,    0,    2,    0,    1,
+             0,   -1,    2,   -2,    1,
+             0,    0,    0,   -2,    1,
+            -1,   -1,    0,    2,    0,
+             2,    0,    0,   -2,    1,
+             1,    0,    0,    2,    0,
+             0,    1,    2,   -2,    1,
+             1,   -1,    0,    0,    0,
+            -2,    0,    2,    0,    2,
+             3,    0,    2,    0,    2,
+             0,   -1,    0,    2,    0,
+             1,   -1,    2,    0,    2,
+             0,    0,    0,    1,    0,
+            -1,   -1,    2,    2,    2,
+            -1,    0,    2,    0,    0,
+             0,   -1,    2,    2,    2,
+            -2,    0,    0,    0,    1,
+             1,    1,    2,    0,    2,
+             2,    0,    0,    0,    1,
+            -1,    1,    0,    1,    0,
+             1,    1,    0,    0,    0,
+             1,    0,    2,    0,    0,
+            -1,    0,    2,   -2,    1,
+             1,    0,    0,    0,    2,
+            -1,    0,    0,    1,    0,
+             0,    0,    2,    1,    2,
+            -1,    0,    2,    4,    2,
+            -1,    1,    0,    1,    1,
+             0,   -2,    2,   -2,    1,
+             1,    0,    2,    2,    1,
+            -2,    0,    2,    2,    2,
+            -1,    0,    0,    0,    2,
+             1,    1,    2,   -2,    2};
+
+    /* Luni-solar nutation coefficients, in 1e-7 arcsec */
+    /* longitude (sin, t*sin, cos), obliquity (cos, t*cos, sin) */
+static double cls[6*NLS]=
+   {-172064161.0, -174666.0,  33386.0, 92052331.0,  9086.0, 15377.0,
+     -13170906.0,   -1675.0, -13696.0,  5730336.0, -3015.0, -4587.0,
+      -2276413.0,    -234.0,   2796.0,   978459.0,  -485.0,  1374.0,
+       2074554.0,     207.0,   -698.0,  -897492.0,   470.0,  -291.0,
+       1475877.0,   -3633.0,  11817.0,    73871.0,  -184.0, -1924.0,
+       -516821.0,    1226.0,   -524.0,   224386.0,  -677.0,  -174.0,
+        711159.0,      73.0,   -872.0,    -6750.0,     0.0,   358.0,
+       -387298.0,    -367.0,    380.0,   200728.0,    18.0,   318.0,
+       -301461.0,     -36.0,    816.0,   129025.0,   -63.0,   367.0,
+        215829.0,    -494.0,    111.0,   -95929.0,   299.0,   132.0,
+        128227.0,     137.0,    181.0,   -68982.0,    -9.0,    39.0,
+        123457.0,      11.0,     19.0,   -53311.0,    32.0,    -4.0,
+        156994.0,      10.0,   -168.0,    -1235.0,     0.0,    82.0,
+         63110.0,      63.0,     27.0,   -33228.0,     0.0,    -9.0,
+        -57976.0,     -63.0,   -189.0,    31429.0,     0.0,   -75.0,
+        -59641.0,     -11.0,    149.0,    25543.0,   -11.0,    66.0,
+        -51613.0,     -42.0,    129.0,    26366.0,     0.0,    78.0,
+         45893.0,      50.0,     31.0,   -24236.0,   -10.0,    20.0,
+         63384.0,      11.0,   -150.0,    -1220.0,     0.0,    29.0,
+        -38571.0,      -1.0,    158.0,    16452.0,   -11.0,    68.0,
+         32481.0,       0.0,      0.0,   -13870.0,     0.0,     0.0,
+        -47722.0,       0.0,    -18.0,      477.0,     0.0,   -25.0,
+        -31046.0,      -1.0,    131.0,    13238.0,   -11.0,    59.0,
+         28593.0,       0.0,     -1.0,   -12338.0,    10.0,    -3.0,
+         20441.0,      21.0,     10.0,   -10758.0,     0.0,    -3.0,
+         29243.0,       0.0,    -74.0,     -609.0,     0.0,    13.0,
+         25887.0,       0.0,    -66.0,     -550.0,     0.0,    11.0,
+        -14053.0,     -25.0,     79.0,     8551.0,    -2.0,   -45.0,
+         15164.0,      10.0,     11.0,    -8001.0,     0.0,    -1.0,
+        -15794.0,      72.0,    -16.0,     6850.0,   -42.0,    -5.0,
+         21783.0,       0.0,     13.0,     -167.0,     0.0,    13.0,
+        -12873.0,     -10.0,    -37.0,     6953.0,     0.0,   -14.0,
+        -12654.0,      11.0,     63.0,     6415.0,     0.0,    26.0,
+        -10204.0,       0.0,     25.0,     5222.0,     0.0,    15.0,
+         16707.0,     -85.0,    -10.0,      168.0,    -1.0,    10.0,
+         -7691.0,       0.0,     44.0,     3268.0,     0.0,    19.0,
+        -11024.0,       0.0,    -14.0,      104.0,     0.0,     2.0,
+          7566.0,     -21.0,    -11.0,    -3250.0,     0.0,    -5.0,
+         -6637.0,     -11.0,     25.0,     3353.0,     0.0,    14.0,
+         -7141.0,      21.0,      8.0,     3070.0,     0.0,     4.0,
+         -6302.0,     -11.0,      2.0,     3272.0,     0.0,     4.0,
+          5800.0,      10.0,      2.0,    -3045.0,     0.0,    -1.0,
+          6443.0,       0.0,     -7.0,    -2768.0,     0.0,    -4.0,
+         -5774.0,     -11.0,    -15.0,     3041.0,     0.0,    -5.0,
+         -5350.0,       0.0,     21.0,     2695.0,     0.0,    12.0,
+         -4752.0,     -11.0,     -3.0,     2719.0,     0.0,    -3.0,
+         -4940.0,     -11.0,    -21.0,     2720.0,     0.0,    -9.0,
+          7350.0,       0.0,     -8.0,      -51.0,     0.0,     4.0,
+          4065.0,       0.0,      6.0,    -2206.0,     0.0,     1.0,
+          6579.0,       0.0,    -24.0,     -199.0,     0.0,     2.0,
+          3579.0,       0.0,      5.0,    -1900.0,     0.0,     1.0,
+          4725.0,       0.0,     -6.0,      -41.0,     0.0,     3.0,
+         -3075.0,       0.0,     -2.0,     1313.0,     0.0,    -1.0,
+         -2904.0,       0.0,     15.0,     1233.0,     0.0,     7.0,
+          4348.0,       0.0,    -10.0,      -81.0,     0.0,     2.0,
+         -2878.0,       0.0,      8.0,     1232.0,     0.0,     4.0,
+         -4230.0,       0.0,      5.0,      -20.0,     0.0,    -2.0,
+         -2819.0,       0.0,      7.0,     1207.0,     0.0,     3.0,
+         -4056.0,       0.0,      5.0,       40.0,     0.0,    -2.0,
+         -2647.0,       0.0,     11.0,     1129.0,     0.0,     5.0,
+         -2294.0,       0.0,    -10.0,     1266.0,     0.0,    -4.0,
+          2481.0,       0.0,     -7.0,    -1062.0,     0.0,    -3.0,
+          2179.0,       0.0,     -2.0,    -1129.0,     0.0,    -2.0,
+          3276.0,       0.0,      1.0,       -9.0,     0.0,     0.0,
+         -3389.0,       0.0,      5.0,       35.0,     0.0,    -2.0,
+          3339.0,       0.0,    -13.0,     -107.0,     0.0,     1.0,
+         -1987.0,       0.0,     -6.0,     1073.0,     0.0,    -2.0,
+         -1981.0,       0.0,      0.0,      854.0,     0.0,     0.0,
+          4026.0,       0.0,   -353.0,     -553.0,     0.0,  -139.0,
+          1660.0,       0.0,     -5.0,     -710.0,     0.0,    -2.0,
+         -1521.0,       0.0,      9.0,      647.0,     0.0,     4.0,
+          1314.0,       0.0,      0.0,     -700.0,     0.0,     0.0,
+         -1283.0,       0.0,      0.0,      672.0,     0.0,     0.0,
+         -1331.0,       0.0,      8.0,      663.0,     0.0,     4.0,
+          1383.0,       0.0,     -2.0,     -594.0,     0.0,    -2.0,
+          1405.0,       0.0,      4.0,     -610.0,     0.0,     2.0,
+          1290.0,       0.0,      0.0,     -556.0,     0.0,     0.0};
+
+    /* Interval between fundamental epoch J2000.0 and given date (JC) */
+    t = (dj - dj0) / djc;
+
+/* Luni-solar nutation */
+
+/* Fundamental (delaunay) arguments from Simon et al. (1994) */
+
+    /* Mean anomaly of the moon */
+    el  = fmod (485868.249036 + (1717915923.2178 * t), as2pi) * as2r;
+
+    /* Mean anomaly of the sun */
+    elp = fmod (1287104.79305 + (129596581.0481 * t), as2pi) * as2r;
+
+    /* Mean argument of the latitude of the moon */
+    f   = fmod (335779.526232 + (1739527262.8478 * t), as2pi) * as2r;
+
+    /* Mean elongation of the moon from the sun */
+    d   = fmod (1072260.70369 + (1602961601.2090 * t), as2pi ) * as2r;
+
+    /* Mean longitude of the ascending node of the moon */
+    om  = fmod (450160.398036 - (6962890.5431 * t), as2pi ) * as2r;
+
+    /* Initialize the nutation values */
+    dp = 0.0;
+    de = 0.0;
+
+    /* Summation of luni-solar nutation series (in reverse order) */
+    for (i = nls; i > 0; i=i-1) {
+	j = i - 1;
+
+	/* Argument and functions */
+	arg = fmod ( (double) (nals[5*j]) * el +
+		     (double) (nals[1+5*j]) * elp +
+		     (double) (nals[2+5*j]) * f +
+		     (double) (nals[3+5*j]) * d +
+		     (double) (nals[4+5*j]) * om, d2pi);
+	sarg = sin (arg);
+	carg = cos (arg);
+
+	/* Terms */
+	dp = dp + (cls[6*j] + cls[1+6*j] * t) * sarg + cls[2+6*j] * carg;
+	de = de + (cls[3+6*j] + cls[4+6*j] * t) * carg + cls[5+6*j] * sarg;
+	}
+
+    /* Convert from 0.1 microarcsec units to radians */
+    dpsils = dp * u2r;
+    depsls = de * u2r;
+
+/* In lieu of planetary nutation */
+
+    /* Fixed offset to correct for missing terms in truncated series */
+    dpsipl = dpplan;
+    depspl = deplan;
+
+/* Results */
+
+    /* Add luni-solar and planetary components */
+    *dpsi = dpsils + dpsipl;
+    *deps = depsls + depspl;
+
+    /* Mean Obliquity in radians (IAU 2006, Hilton, et al.) */
+    *eps0 = ( 84381.406     +
+	    ( -46.836769    +
+	    (  -0.0001831   +
+	    (   0.00200340  +
+	    (  -0.000000576 +
+	    (  -0.0000000434 ) * t ) * t ) * t ) * t ) * t ) * as2r;
+}
+
+
+/* ISDATE - Return 1 if string is an old or ISO FITS standard date */
+
+int
+isdate (string)
+
+char	*string; /* Possible FITS date string, which may be:
+			dd/mm/yy (FITS standard before 2000)
+			dd-mm-yy (nonstandard FITS use before 2000)
+			yyyy-mm-dd (FITS standard after 1999)
+			yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+
+{
+    int iyr = 0;	/* year (returned) */
+    int imon = 0;	/* month (returned) */
+    int iday = 0;	/* day (returned) */
+    int i;
+    char *sstr, *dstr, *tstr, *nval;
+
+    /* Translate string from ASCII to binary */
+    if (string == NULL) 
+	return (0);
+
+    sstr = strchr (string,'/');
+    dstr = strchr (string,'-');
+    if (dstr == string)
+	dstr = strchr (string+1,'-');
+    tstr = strchr (string,'T');
+
+    /* Original FITS date format: dd/mm/yy */
+    if (sstr > string) {
+	*sstr = '\0';
+	iday = (int) atof (string);
+	*sstr = '/';
+	nval = sstr + 1;
+	sstr = strchr (nval,'/');
+	if (sstr == NULL)
+	    sstr = strchr (nval,'-');
+	if (sstr > string) {
+	    *sstr = '\0';
+	    imon = (int) atof (nval);
+	    *sstr = '/';
+	    nval = sstr + 1;
+	    iyr = (int) atof (nval);
+	    if (iyr < 1000)
+		iyr = iyr + 1900;
+	    }
+	if (imon > 0 && iday > 0)
+	    return (1);
+	else
+	    return (0);
+	}
+
+    /* New FITS date format: yyyy-mm-ddThh:mm:ss[.sss] */
+    else if (dstr > string) {
+	*dstr = '\0';
+	iyr = (int) atof (string);
+	nval = dstr + 1;
+	*dstr = '-';
+	dstr = strchr (nval,'-');
+	imon = 0;
+	iday = 0;
+
+	/* Decode year, month, and day */
+	if (dstr > string) {
+	    *dstr = '\0';
+	    imon = (int) atof (nval);
+	    *dstr = '-';
+	    nval = dstr + 1;
+	    if (tstr > string)
+		*tstr = '\0';
+	    iday = (int) atof (nval);
+	    if (tstr > string)
+		*tstr = 'T';
+	    }
+
+	/* If day is > 31, it is really year in old format */
+	if (iday > 31) {
+	    i = iyr;
+	    if (iday < 100)
+		iyr = iday + 1900;
+	    else
+		iyr = iday;
+	    iday = i;
+	    }
+	if (imon > 0 && iday > 0)
+	    return (1);
+	else
+	    return (0);
+	}
+
+    /* If FITS date is entered as an epoch, return 0 anyway */
+    else
+	return (0);
+}
+
+
+/* Round seconds and make sure date and time numbers are within limits */
+
+static void
+fixdate (iyr, imon, iday, ihr, imn, sec, ndsec)
+
+int	*iyr;	/* year (returned) */
+int	*imon;	/* month (returned) */
+int	*iday;	/* day (returned) */
+int	*ihr;	/* hours (returned) */
+int	*imn;	/* minutes (returned) */
+double	*sec;	/* seconds (returned) */
+int	ndsec;	/* Number of decimal places in seconds (0=int) */
+{
+    double days;
+
+    /* Round seconds to 0 - 4 decimal places (no rounding if <0, >4) */
+    if (ndsec == 0)
+	*sec = dint (*sec + 0.5);
+    else if (ndsec < 2)
+	*sec = dint (*sec * 10.0 + 0.5) / 10.0;
+    else if (ndsec < 3)
+	*sec = dint (*sec * 100.0 + 0.5) / 100.0;
+    else if (ndsec < 4)
+	*sec = dint (*sec * 1000.0 + 0.5) / 1000.0;
+    else if (ndsec < 5)
+	*sec = dint (*sec * 10000.0 + 0.5) / 10000.0;
+
+    /* Adjust minutes and hours */
+    if (*sec > 60.0) {
+	*sec = *sec - 60.0;
+	*imn = *imn + 1;
+	}
+    if (*imn > 60) {
+	*imn = *imn - 60;
+	*ihr = *ihr + 1;
+	}
+
+    /* Return if no date */
+    if (*iyr == 0 && *imon == 0 && *iday == 0)
+	return;
+
+   /* Adjust date */
+    if (*ihr > 23) {
+	*ihr = *ihr - 24;
+	*iday = *iday + 1;
+	}
+    days = caldays (*iyr, *imon);
+    if (*iday > days) {
+	*iday = *iday - days;
+	*imon = *imon + 1;
+	}
+    if (*iday < 1) {
+	*imon = *imon - 1;
+	if (*imon < 1) {
+	    *imon = *imon + 12;
+	    *iyr = *iyr - 1;
+	    }
+	days = caldays (*iyr, *imon);
+	*iday = *iday + days;
+	}
+    if (*imon < 1) {
+	*imon = *imon + 12;
+	*iyr = *iyr - 1;
+	days = caldays (*iyr, *imon);
+	if (*iday > days) {
+	    *iday = *iday - days;
+	    *imon = *imon + 1;
+	    }
+	}
+    if (*imon > 12) {
+	*imon = *imon - 12;
+	*iyr = *iyr + 1;
+	}
+    return;
+}
+
+
+/* Calculate days in month 1-12 given year (Gregorian calendar only) */
+
+static int
+caldays (year, month)
+
+int	year;	/* 4-digit year */
+int	month;	/* Month (1=January, 2=February, etc.) */
+{
+    if (month < 1) {
+	month = month + 12;
+	year = year + 1;
+	}
+    if (month > 12) {
+	month = month - 12;
+	year = year + 1;
+	}
+    switch (month) {
+	case 1:
+	    return (31);
+	case 2:
+	    if (year%400 == 0)
+		return (29);
+	    else if (year%100 == 0)
+		return (28);
+	    else if (year%4 == 0)
+		return (29);
+	    else
+		return (28);
+	case 3:
+	    return (31);
+	case 4:
+	    return (30);
+	case 5:
+	    return (31);
+	case 6:
+	    return (30);
+	case 7:
+	    return (31);
+	case 8:
+	    return (31);
+	case 9:
+	    return (30);
+	case 10:
+	    return (31);
+	case 11:
+	    return (30);
+	case 12:
+	    return (31);
+	default:
+	    return (0);
+	}
+}
+
+
+static double
+dint (dnum)
+
+double	dnum;
+{
+    double dn;
+
+    if (dnum < 0.0)
+	dn = -floor (-dnum);
+    else
+	dn = floor (dnum);
+    return (dn);
+}
+
+
+static double
+dmod (dnum, dm)
+
+double	dnum, dm;
+{
+    double dnumx, dnumi, dnumf;
+    if (dnum < 0.0)
+	dnumx = -dnum;
+    else
+	dnumx = dnum;
+    dnumi = dint (dnumx / dm);
+    if (dnum < 0.0)
+	dnumf = dnum + (dnumi * dm);
+    else if (dnum > 0.0)
+	dnumf = dnum - (dnumi * dm);
+    else
+	dnumf = 0.0;
+    return (dnumf);
+}
+
+/* Jul  1 1999	New file, based on iolib/jcon.f and iolib/vcon.f and hgetdate()
+ * Oct 21 1999	Fix declarations after lint
+ * Oct 27 1999	Fix bug to return epoch if fractional year input
+ * Dec  9 1999	Fix bug in ts2jd() found by Pete Ratzlaff (SAO)
+ * Dec 17 1999	Add all unimplemented conversions
+ * Dec 20 1999	Add isdate(); leave date, time strings unchanged in fd2i()
+ * Dec 20 1999	Make all fd2*() subroutines deal with time alone
+ *
+ * Jan  3 2000	In old FITS format, year 100 is assumed to be 2000
+ * Jan 11 2000	Fix epoch to date conversion so .0 is 0:00, not 12:00
+ * Jan 21 2000	Add separate Besselian and Julian epoch computations
+ * Jan 28 2000	Add Modified Julian Date conversions
+ * Mar  2 2000	Implement decimal places for FITS date string
+ * Mar 14 2000	Fix bug in dealing with 2000-02-29 in ts2i()
+ * Mar 22 2000	Add lt2* and ut2* to get current time as local and UT
+ * Mar 24 2000	Fix calloc() calls
+ * Mar 24 2000	Add tsi2* and tsu2* to convert IRAF and Unix seconds
+ * May  1 2000	In old FITS format, all years < 1000 get 1900 added to them
+ * Aug  1 2000	Make ep2jd and jd2ep consistently starting at 1/1 0:00
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * May 21 2001	Add day of year conversions
+ * May 25 2001	Allow fraction of day in FITS date instead of time
+ *
+ * Apr  8 2002	Change all long declaration to time_t
+ * May 13 2002	Fix bugs found by lint
+ * Jul  5 2002	Fix bug in fixdate() so fractional seconds come out
+ * Jul  8 2002	Fix rounding bug in t2i()
+ * Jul  8 2002	Try Fliegel and Van Flandern's algorithm for JD to UT date
+ * Jul  8 2002	If first character of string is -, check for other -'s in isdate
+ * Sep 10 2002	Add ET/TDT/TT conversion from UT subroutines
+ * Sep 10 2002	Add sidereal time conversions
+ *
+ * Jan 30 2003	Fix typo in ts2gst()
+ * Mar  7 2003	Add conversions for heliocentric julian dates
+ * May 20 2003	Declare nd in setdatedec()
+ * Jul 18 2003	Add code to parse Las Campanas dates
+ *
+ * Mar 24 2004	If ndec > 0, add UT to FITS date even if it is 0:00:00
+ *
+ * Oct 14 2005	Add tsd2fd() and tsd2dt()
+ *
+ * May  3 2006	Drop declaration of unused variables
+ * Jun 20 2006	Initialized uninitialized variables
+ * Aug  2 2006	Add local sidereal time
+ * Sep 13 2006	Add more local sidereal time subroutines
+ * Oct  2 2006	Add UT to old FITS date conversions
+ * Oct  6 2006	Add eqeqnx() to compute equation of the equinoxes
+ *
+ * Jan  8 2007	Remove unused variables
+ *
+ * Sep  5 2008	Replace nutation with IAU 2006 model translated from SOFA
+ * Sep  9 2008	Add ang2hr(), ang2deg(), hr2ang(), deg2ang()
+ * Sep 10 2008	Add longitude to mean standard time (default = Greenwich)
+ */
diff --git a/Code/src/libwcs/distort.c b/Code/src/libwcs/distort.c
new file mode 100644
index 0000000000000000000000000000000000000000..9306c13a5a26f57b2d22a65f767a7710f9ffc1a4
--- /dev/null
+++ b/Code/src/libwcs/distort.c
@@ -0,0 +1,405 @@
+/*** File libwcs/distort.c
+ *** January 4, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu, 
+ *** Based on code written by Jing Li, IPAC
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2004-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	distort.c (World Coordinate Systems)
+ * Purpose:	Convert focal plane coordinates to pixels and vice versa:
+ * Subroutine:  distortinit (wcs, hstring) set distortion coefficients from FITS header
+ * Subroutine:  DelDistort (header, verbose) delete distortion coefficients in FITS header
+ * Subroutine:	pix2foc (wcs, x, y, u, v) pixel coordinates -> focal plane coordinates
+ * Subroutine:	foc2pix (wcs, u, v, x, y) focal plane coordinates -> pixel coordinates
+ * Subroutine:  setdistcode (wcs,ctype) sets distortion code from CTYPEi
+ * Subroutine:  getdistcode (wcs) returns distortion code string for CTYPEi
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "wcs.h"
+
+void
+distortinit (wcs, hstring)
+struct WorldCoor *wcs;  /* World coordinate system structure */
+const char *hstring;	/* character string containing FITS header information
+			   in the format <keyword>= <value> [/ <comment>] */
+{
+    int i, j, m;
+    char keyword[12];
+
+    /* Read distortion coefficients, if present */
+    if (wcs->distcode == DISTORT_SIRTF) {
+	if (wcs->wcsproj == WCS_OLD) {
+	    wcs->wcsproj = WCS_NEW;
+	    wcs->distort.a_order = 0;
+	    wcs->distort.b_order = 0;
+	    wcs->distort.ap_order = 0;
+	    wcs->distort.bp_order = 0;
+	    }
+	else {
+	    if (!hgeti4 (hstring, "A_ORDER", &wcs->distort.a_order)) {
+		setwcserr ("DISTINIT: Missing A_ORDER keyword for SIRTF distortion");
+		}
+	    else {
+		m = wcs->distort.a_order;
+		for (i = 0; i <= m; i++) {
+		    for (j = 0; j <= m; j++) {
+			wcs->distort.a[i][j] = 0.0;
+			}
+		    }
+		for (i = 0; i <= m; i++) {
+		    for (j = 0; j <= m-i; j++) {
+			sprintf (keyword, "A_%d_%d", i, j);
+			hgetr8 (hstring, keyword, &wcs->distort.a[i][j]);
+			}
+		    }
+		}
+	    if (!hgeti4 (hstring, "B_ORDER", &wcs->distort.b_order)) {
+		setwcserr ("DISTINIT: Missing B_ORDER keyword for SIRTF distortion");
+		}
+	    else {
+		m = wcs->distort.b_order;
+		for (i = 0; i <= m; i++) {
+		    for (j = 0; j <= m; j++) {
+			wcs->distort.b[i][j] = 0.0;
+			}
+		    }
+		for (i = 0; i <= m; i++) {
+		    for (j = 0; j <= m-i; j++) {
+			sprintf (keyword, "B_%d_%d", i, j);
+			hgetr8 (hstring, keyword, &wcs->distort.b[i][j]);
+			}
+		    }
+		}
+	    if (!hgeti4 (hstring, "AP_ORDER", &wcs->distort.ap_order)) {
+		setwcserr ("DISTINIT: Missing AP_ORDER keyword for SIRTF distortion");
+		}
+	    else {
+		m = wcs->distort.ap_order;
+		for (i = 0; i <= m; i++) {
+		    for (j = 0; j <= m; j++) {
+			wcs->distort.ap[i][j] = 0.0;
+			}
+		    }
+		for (i = 0; i <= m; i++) {
+		    for (j = 0; j <= m-i; j++) {
+			sprintf (keyword, "AP_%d_%d", i, j);
+			hgetr8 (hstring, keyword, &wcs->distort.ap[i][j]);
+			}
+		    }
+		}
+	    if (!hgeti4 (hstring, "BP_ORDER", &wcs->distort.bp_order)) {
+		setwcserr ("DISTINIT: Missing BP_ORDER keyword for SIRTF distortion");
+		}
+	    else {
+		m = wcs->distort.bp_order;
+		for (i = 0; i <= m; i++) {
+		    for (j = 0; j <= m; j++) {
+			wcs->distort.bp[i][j] = 0.0;
+			}
+		    }
+		for (i = 0; i <= m; i++) {
+		    for (j = 0; j <= m-i; j++) {
+			sprintf (keyword, "BP_%d_%d", i, j);
+			hgetr8 (hstring, keyword, &wcs->distort.bp[i][j]);
+			}
+		    }
+		}
+	    }
+	}
+    return;
+}
+
+
+/* Delete all distortion-related fields.
+ * return 0 if at least one such field is found, else -1.  */
+
+int
+DelDistort (header, verbose)
+
+char *header;
+int verbose;
+
+{
+    char keyword[16];
+    char str[32];
+    int i, j, m;
+    int lctype;
+    int n;
+
+    n = 0;
+
+    if (hgeti4 (header, "A_ORDER", &m)) {
+	for (i = 0; i <= m; i++) {
+	    for (j = 0; j <= m-i; j++) {
+		sprintf (keyword, "A_%d_%d", i, j);
+		hdel (header, keyword);
+		n++;
+		}
+	    }
+	hdel (header, "A_ORDER");
+	n++;
+	}
+
+    if (hgeti4 (header, "AP_ORDER", &m)) {
+	for (i = 0; i <= m; i++) {
+	    for (j = 0; j <= m-i; j++) {
+		sprintf (keyword, "AP_%d_%d", i, j);
+		hdel (header, keyword);
+		n++;
+		}
+	    }
+	hdel (header, "AP_ORDER");
+	n++;
+	}
+
+    if (hgeti4 (header, "B_ORDER", &m)) {
+	for (i = 0; i <= m; i++) {
+	    for (j = 0; j <= m-i; j++) {
+		sprintf (keyword, "B_%d_%d", i, j);
+		hdel (header, keyword);
+		n++;
+		}
+	    }
+	hdel (header, "B_ORDER");
+	n++;
+	}
+
+    if (hgeti4 (header, "BP_ORDER", &m)) {
+	for (i = 0; i <= m; i++) {
+	    for (j = 0; j <= m-i; j++) {
+		sprintf (keyword, "BP_%d_%d", i, j);
+		hdel (header, keyword);
+		n++;
+		}
+	    }
+	hdel (header, "BP_ORDER");
+	n++;
+	}
+
+    if (n > 0 && verbose)
+	fprintf (stderr,"%d keywords deleted\n", n);
+
+    /* Remove WCS distortion code from CTYPEi in FITS header */
+    if (hgets (header, "CTYPE1", 31, str)) {
+	lctype = strlen (str);
+	if (lctype > 8) {
+	    str[8] = (char) 0;
+	    hputs (header, "CTYPE1", str);
+	    }
+	}
+    if (hgets (header, "CTYPE2", 31, str)) {
+	lctype = strlen (str);
+	if (lctype > 8) {
+	    str[8] = (char) 0;
+	    hputs (header, "CTYPE2", str);
+	    }
+	}
+
+    return (n);
+}
+
+void
+foc2pix (wcs, x, y, u, v)
+
+struct WorldCoor *wcs;  /* World coordinate system structure */
+double	x, y;		/* Focal plane coordinates */
+double	*u, *v;		/* Image pixel coordinates (returned) */
+{
+    int m, n, i, j, k;
+    double s[DISTMAX], sum;
+    double temp_x, temp_y;
+
+    /* SIRTF distortion */
+    if (wcs->distcode == DISTORT_SIRTF) {
+	m = wcs->distort.ap_order;
+	n = wcs->distort.bp_order;
+
+	temp_x = x - wcs->xrefpix;
+	temp_y = y - wcs->yrefpix;
+
+	/* compute u */
+	for (j = 0; j <= m; j++) {
+	    s[j] = wcs->distort.ap[m-j][j];
+	    for (k = j-1; k >= 0; k--) {
+	   	s[j] = (temp_y * s[j]) + wcs->distort.ap[m-j][k];
+		}
+	    }
+  
+	sum = s[0];
+	for (i=m; i>=1; i--){
+	    sum = (temp_x * sum) + s[m-i+1];
+	    }
+	*u = sum;
+
+	/* compute v*/
+	for (j = 0; j <= n; j++) {
+	    s[j] = wcs->distort.bp[n-j][j];
+	    for (k = j-1; k >= 0; k--) {
+		s[j] = temp_y*s[j] + wcs->distort.bp[n-j][k];
+		}
+	    }
+   
+	sum = s[0];
+	for (i = n; i >= 1; i--)
+	    sum = temp_x * sum + s[n-i+1];
+
+	*v = sum;
+
+	*u = x + *u;
+	*v = y + *v;
+	}
+
+    /* If no distortion, return pixel positions unchanged */
+    else {
+	*u = x;
+	*v = y;
+	}
+
+    return;
+}
+
+
+void
+pix2foc (wcs, u, v, x, y)
+
+struct WorldCoor *wcs;  /* World coordinate system structure */
+double u, v;		/* Image pixel coordinates */
+double *x, *y;		/* Focal plane coordinates (returned) */
+{
+    int m, n, i, j, k;
+    double s[DISTMAX], sum;
+    double temp_u, temp_v;
+
+    /* SIRTF distortion */
+    if (wcs->distcode == DISTORT_SIRTF) {
+	m = wcs->distort.a_order;
+	n = wcs->distort.b_order;
+
+	temp_u = u - wcs->xrefpix;
+	temp_v = v - wcs->yrefpix;
+
+	/* compute u */
+	for (j = 0; j <= m; j++) {
+	    s[j] = wcs->distort.a[m-j][j];
+	    for (k = j-1; k >= 0; k--) {
+		s[j] = (temp_v * s[j]) + wcs->distort.a[m-j][k];
+		}
+	    }
+  
+	sum = s[0];
+	for (i=m; i>=1; i--){
+	    sum = temp_u*sum + s[m-i+1];
+	    }
+	*x = sum;
+
+	/* compute v*/
+	for (j=0; j<=n; j++) {
+	    s[j] = wcs->distort.b[n-j][j];
+	    for (k=j-1; k>=0; k--) {
+		s[j] =temp_v*s[j] + wcs->distort.b[n-j][k];
+		}
+	    }
+   
+	sum = s[0];
+	for (i=n; i>=1; i--)
+	    sum = temp_u*sum + s[n-i+1];
+
+	*y = sum;
+  
+	*x = u + *x;
+	*y = v + *y;
+
+/*	*x = u + *x + coeff.crpix1; */
+/*	*y = v + *y + coeff.crpix2; */
+	}
+
+    /* If no distortion, return pixel positions unchanged */
+    else {
+	*x = u;
+	*y = v;
+	}
+
+    return;
+}
+
+
+/* SETDISTCODE -- Set WCS distortion code from CTYPEi in FITS header */
+
+void
+setdistcode (wcs, ctype)
+
+struct WorldCoor *wcs;  /* World coordinate system structure */
+char *ctype;		/* Value of CTYPEi from FITS header */
+
+{
+    char *extension;
+    int lctype;
+
+    lctype = strlen (ctype);
+    if (lctype < 9)
+	wcs->distcode = DISTORT_NONE;
+    else {
+	extension = ctype + 8;
+	if (!strncmp (extension, "-SIP", 4))
+	    wcs->distcode = DISTORT_SIRTF;
+	else
+	    wcs->distcode = DISTORT_NONE;
+	}
+    return;
+}
+
+
+/* GETDISTCODE -- Return NULL if no distortion or code from wcs.h */
+
+char *
+getdistcode (wcs)
+
+struct WorldCoor *wcs;  /* World coordinate system structure */
+
+{
+    char *dcode;	/* Distortion string for CTYPEi */
+
+    if (wcs->distcode == DISTORT_SIRTF) {
+	dcode = (char *) calloc (8, sizeof (char));
+	strcpy (dcode, "-SIP");
+	}
+    else
+	dcode = NULL;
+    return (dcode);
+}
+
+/* Apr  2 2003	New subroutines
+ * Nov  3 2003	Add getdistcode to return distortion code string
+ * Nov 10 2003	Include unistd.h to get definition of NULL
+ * Nov 18 2003	Include string.h to get strlen()
+ *
+ * Jan  9 2004	Add DelDistort() to delete distortion keywords
+ *
+ * Jan  4 2007	Declare header const char*
+ */
diff --git a/Code/src/libwcs/dsspos.c b/Code/src/libwcs/dsspos.c
new file mode 100644
index 0000000000000000000000000000000000000000..a6d8942025d9333c620b73cd99fb59690e3ee225
--- /dev/null
+++ b/Code/src/libwcs/dsspos.c
@@ -0,0 +1,318 @@
+/*** File saoimage/wcslib/dsspos.c
+ *** October 21, 1999
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1995-2002
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	dsspos.c (Plate solution WCS conversion)
+ * Purpose:	Compute WCS from Digital Sky Survey plate fit
+ * Subroutine:	dsspos() converts from pixel location to RA,Dec 
+ * Subroutine:	dsspix() converts from RA,Dec to pixel location   
+
+    These functions are based on the astrmcal.c portion of GETIMAGE by
+    J. Doggett and the documentation distributed with the Digital Sky Survey.
+
+*/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "wcs.h"
+
+int
+dsspos (xpix, ypix, wcs, xpos, ypos)
+
+/* Routine to determine accurate position for pixel coordinates */
+/* returns 0 if successful otherwise 1 = angle too large for projection; */
+/* based on amdpos() from getimage */
+
+/* Input: */
+double	xpix;		/* x pixel number  (RA or long without rotation) */
+double	ypix;		/* y pixel number  (dec or lat without rotation) */
+struct WorldCoor *wcs;	/* WCS parameter structure */
+
+/* Output: */
+double	*xpos;		/* Right ascension or longitude in degrees */
+double	*ypos;		/* Declination or latitude in degrees */
+
+{
+  double x, y, xmm, ymm, xmm2, ymm2, xmm3, ymm3, x2y2;
+  double xi, xir, eta, etar, raoff, ra, dec;
+  double cond2r = 1.745329252e-2;
+  double cons2r = 206264.8062470964;
+  double twopi = 6.28318530717959;
+  double ctan, ccos;
+
+/*  Ignore magnitude and color terms 
+  double mag = 0.0;
+  double color = 0.0; */
+
+/* Convert from image pixels to plate pixels */
+  x = xpix + wcs->x_pixel_offset - 1.0 + 0.5;
+  y = ypix + wcs->y_pixel_offset - 1.0 + 0.5;
+
+/* Convert from pixels to millimeters */
+  xmm = (wcs->ppo_coeff[2] - x * wcs->x_pixel_size) / 1000.0;
+  ymm = (y * wcs->y_pixel_size - wcs->ppo_coeff[5]) / 1000.0;
+  xmm2 = xmm * xmm;
+  ymm2 = ymm * ymm;
+  xmm3 = xmm * xmm2;
+  ymm3 = ymm * ymm2;
+  x2y2 = xmm2 + ymm2;
+
+/*  Compute coordinates from x,y and plate model */
+
+  xi =  wcs->x_coeff[ 0]*xmm	+ wcs->x_coeff[ 1]*ymm +
+	wcs->x_coeff[ 2]		+ wcs->x_coeff[ 3]*xmm2 +
+	wcs->x_coeff[ 4]*xmm*ymm	+ wcs->x_coeff[ 5]*ymm2 +
+	wcs->x_coeff[ 6]*(x2y2)	+ wcs->x_coeff[ 7]*xmm3 +
+	wcs->x_coeff[ 8]*xmm2*ymm	+ wcs->x_coeff[ 9]*xmm*ymm2 +
+	wcs->x_coeff[10]*ymm3	+ wcs->x_coeff[11]*xmm*(x2y2) +
+	wcs->x_coeff[12]*xmm*x2y2*x2y2;
+
+/*  Ignore magnitude and color terms 
+	+ wcs->x_coeff[13]*mag	+ wcs->x_coeff[14]*mag*mag +
+	wcs->x_coeff[15]*mag*mag*mag + wcs->x_coeff[16]*mag*xmm +
+	wcs->x_coeff[17]*mag*x2y2	+ wcs->x_coeff[18]*mag*xmm*x2y2 +
+	wcs->x_coeff[19]*color; */
+
+  eta =	wcs->y_coeff[ 0]*ymm	+ wcs->y_coeff[ 1]*xmm +
+	wcs->y_coeff[ 2]		+ wcs->y_coeff[ 3]*ymm2 +
+	wcs->y_coeff[ 4]*xmm*ymm	+ wcs->y_coeff[ 5]*xmm2 +
+	wcs->y_coeff[ 6]*(x2y2)	+ wcs->y_coeff[ 7]*ymm3 +
+	wcs->y_coeff[ 8]*ymm2*xmm	+ wcs->y_coeff[ 9]*ymm*xmm2 +
+	wcs->y_coeff[10]*xmm3	+ wcs->y_coeff[11]*ymm*(x2y2) +
+	wcs->y_coeff[12]*ymm*x2y2*x2y2;
+
+/*  Ignore magnitude and color terms 
+	+ wcs->y_coeff[13]*mag	+ wcs->y_coeff[14]*mag*mag +
+	wcs->y_coeff[15]*mag*mag*mag + wcs->y_coeff[16]*mag*ymm +
+	wcs->y_coeff[17]*mag*x2y2)	+ wcs->y_coeff[18]*mag*ymm*x2y2 +
+	wcs->y_coeff[19]*color; */
+
+/* Convert to radians */
+
+  xir = xi / cons2r;
+  etar = eta / cons2r;
+
+/* Convert to RA and Dec */
+
+  ctan = tan (wcs->plate_dec);
+  ccos = cos (wcs->plate_dec);
+  raoff = atan2 (xir / ccos, 1.0 - etar * ctan);
+  ra = raoff + wcs->plate_ra;
+  if (ra < 0.0) ra = ra + twopi;
+  *xpos = ra / cond2r;
+
+  dec = atan (cos (raoff) * ((etar + ctan) / (1.0 - (etar * ctan))));
+  *ypos = dec / cond2r;
+  return 0;
+}
+
+
+int
+dsspix (xpos, ypos, wcs, xpix, ypix)
+
+/* Routine to determine pixel coordinates for sky position */
+/* returns 0 if successful otherwise 1 = angle too large for projection; */
+/* based on amdinv() from getimage */
+
+/* Input: */
+double	xpos;		/* Right ascension or longitude in degrees */
+double	ypos;		/* Declination or latitude in degrees */
+struct WorldCoor *wcs;	/* WCS parameter structure */
+
+/* Output: */
+double	*xpix;		/* x pixel number  (RA or long without rotation) */
+double	*ypix;		/* y pixel number  (dec or lat without rotation) */
+
+{
+  double div,xi,eta,x,y,xy,x2,y2,x2y,y2x,x3,y3,x4,y4,x2y2,cjunk,dx,dy;
+  double sypos,cypos,syplate,cyplate,sxdiff,cxdiff;
+  double f,fx,fy,g,gx,gy, xmm, ymm;
+  double conr2s = 206264.8062470964;
+  double tolerance = 0.0000005;
+  int    max_iterations = 50;
+  int    i;
+  double xr, yr; 	/* position in radians */
+
+  *xpix = 0.0;
+  *ypix = 0.0;
+
+/* Convert RA and Dec in radians to standard coordinates on a plate */
+  xr = degrad (xpos);
+  yr = degrad (ypos);
+  sypos = sin (yr);
+  cypos = cos (yr);
+  if (wcs->plate_dec == 0.0)
+    wcs->plate_dec = degrad (wcs->yref);
+  syplate = sin (wcs->plate_dec);
+  cyplate = cos (wcs->plate_dec);
+  if (wcs->plate_ra == 0.0)
+    wcs->plate_ra = degrad (wcs->yref);
+  sxdiff = sin (xr - wcs->plate_ra);
+  cxdiff = cos (xr - wcs->plate_ra);
+  div = (sypos * syplate) + (cypos * cyplate * cxdiff);
+  if (div == 0.0)
+    return (1);
+  xi = cypos * sxdiff * conr2s / div;
+  eta = ((sypos * cyplate) - (cypos * syplate * cxdiff)) * conr2s / div;
+
+/* Set initial value for x,y */
+  if (wcs->plate_scale == 0.0)
+    return (1);
+  xmm = xi / wcs->plate_scale;
+  ymm = eta / wcs->plate_scale;
+
+/* Iterate by Newton's method */
+  for (i = 0; i < max_iterations; i++) {
+
+    /* X plate model */
+    xy = xmm * ymm;
+    x2 = xmm * xmm;
+    y2 = ymm * ymm;
+    x2y = x2 * ymm;
+    y2x = y2 * xmm;
+    x2y2 = x2 + y2;
+    cjunk = x2y2 * x2y2;
+    x3 = x2 * xmm;
+    y3 = y2 * ymm;
+    x4 = x2 * x2;
+    y4 = y2 * y2;
+    f = wcs->x_coeff[0]*xmm      + wcs->x_coeff[1]*ymm +
+        wcs->x_coeff[2]          + wcs->x_coeff[3]*x2 +
+        wcs->x_coeff[4]*xy       + wcs->x_coeff[5]*y2 +
+        wcs->x_coeff[6]*x2y2     + wcs->x_coeff[7]*x3 +
+        wcs->x_coeff[8]*x2y      + wcs->x_coeff[9]*y2x +
+        wcs->x_coeff[10]*y3      + wcs->x_coeff[11]*xmm*x2y2 +
+        wcs->x_coeff[12]*xmm*cjunk;
+    /* magnitude and color terms ignored
+      + wcs->x_coeff[13]*mag +
+        wcs->x_coeff[14]*mag*mag   + wcs->x_coeff[15]*mag*mag*mag +
+        wcs->x_coeff[16]*mag*xmm   + wcs->x_coeff[17]*mag*(x2+y2) +
+        wcs->x_coeff[18]*mag*xmm*(x2+y2)  + wcs->x_coeff[19]*color;
+    */
+
+    /*  Derivative of X model wrt x */
+    fx = wcs->x_coeff[0]           + wcs->x_coeff[3]*2.0*xmm +
+         wcs->x_coeff[4]*ymm       + wcs->x_coeff[6]*2.0*xmm +
+         wcs->x_coeff[7]*3.0*x2    + wcs->x_coeff[8]*2.0*xy +
+         wcs->x_coeff[9]*y2        + wcs->x_coeff[11]*(3.0*x2+y2) +
+         wcs->x_coeff[12]*(5.0*x4 +6.0*x2*y2+y4);
+    /* magnitude and color terms ignored
+         wcs->x_coeff[16]*mag      + wcs->x_coeff[17]*mag*2.0*xmm +
+         wcs->x_coeff[18]*mag*(3.0*x2+y2);
+    */
+
+    /* Derivative of X model wrt y */
+    fy = wcs->x_coeff[1]           + wcs->x_coeff[4]*xmm +
+         wcs->x_coeff[5]*2.0*ymm   + wcs->x_coeff[6]*2.0*ymm +
+         wcs->x_coeff[8]*x2        + wcs->x_coeff[9]*2.0*xy +
+         wcs->x_coeff[10]*3.0*y2   + wcs->x_coeff[11]*2.0*xy +
+         wcs->x_coeff[12]*4.0*xy*x2y2;
+    /* magnitude and color terms ignored
+         wcs->x_coeff[17]*mag*2.0*ymm +
+         wcs->x_coeff[18]*mag*2.0*xy;
+    */
+
+    /* Y plate model */
+    g = wcs->y_coeff[0]*ymm       + wcs->y_coeff[1]*xmm +
+       wcs->y_coeff[2]            + wcs->y_coeff[3]*y2 +
+       wcs->y_coeff[4]*xy         + wcs->y_coeff[5]*x2 +
+       wcs->y_coeff[6]*x2y2       + wcs->y_coeff[7]*y3 +
+       wcs->y_coeff[8]*y2x        + wcs->y_coeff[9]*x2y +
+       wcs->y_coeff[10]*x3        + wcs->y_coeff[11]*ymm*x2y2 +
+       wcs->y_coeff[12]*ymm*cjunk;
+    /* magnitude and color terms ignored
+       wcs->y_coeff[13]*mag        + wcs->y_coeff[14]*mag*mag +
+       wcs->y_coeff[15]*mag*mag*mag + wcs->y_coeff[16]*mag*ymm +
+       wcs->y_coeff[17]*mag*x2y2 +
+       wcs->y_coeff[18]*mag*ymm*x2y2 + wcs->y_coeff[19]*color;
+    */
+
+    /* Derivative of Y model wrt x */
+    gx = wcs->y_coeff[1]           + wcs->y_coeff[4]*ymm +
+         wcs->y_coeff[5]*2.0*xmm   + wcs->y_coeff[6]*2.0*xmm +
+         wcs->y_coeff[8]*y2       + wcs->y_coeff[9]*2.0*xy +
+         wcs->y_coeff[10]*3.0*x2  + wcs->y_coeff[11]*2.0*xy +
+         wcs->y_coeff[12]*4.0*xy*x2y2;
+    /* magnitude and color terms ignored
+         wcs->y_coeff[17]*mag*2.0*xmm +
+         wcs->y_coeff[18]*mag*ymm*2.0*xmm;
+    */
+
+    /* Derivative of Y model wrt y */
+    gy = wcs->y_coeff[0]            + wcs->y_coeff[3]*2.0*ymm +
+         wcs->y_coeff[4]*xmm        + wcs->y_coeff[6]*2.0*ymm +
+         wcs->y_coeff[7]*3.0*y2     + wcs->y_coeff[8]*2.0*xy +
+         wcs->y_coeff[9]*x2         + wcs->y_coeff[11]*(x2+3.0*y2) +
+         wcs->y_coeff[12]*(5.0*y4 + 6.0*x2*y2 + x4);
+    /* magnitude and color terms ignored
+         wcs->y_coeff[16]*mag       + wcs->y_coeff[17]*mag*2.0*ymm +
+         wcs->y_coeff[18]*mag*(x2+3.0*y2);
+    */
+
+    f = f - xi;
+    g = g - eta;
+    dx = ((-f * gy) + (g * fy)) / ((fx * gy) - (fy * gx));
+    dy = ((-g * fx) + (f * gx)) / ((fx * gy) - (fy * gx));
+    xmm = xmm + dx;
+    ymm = ymm + dy;
+    if ((fabs(dx) < tolerance) && (fabs(dy) < tolerance)) break;
+    }
+
+/* Convert mm from plate center to plate pixels */
+  if (wcs->x_pixel_size == 0.0 || wcs->y_pixel_size == 0.0)
+    return (1);
+  x = (wcs->ppo_coeff[2] - xmm*1000.0) / wcs->x_pixel_size;
+  y = (wcs->ppo_coeff[5] + ymm*1000.0) / wcs->y_pixel_size;
+
+/* Convert from plate pixels to image pixels */
+  *xpix = x - wcs->x_pixel_offset + 1.0 - 0.5;
+  *ypix = y - wcs->y_pixel_offset + 1.0 - 0.5;
+
+/* If position is off of the image, return offscale code */
+  if (*xpix < 0.5 || *xpix > wcs->nxpix+0.5)
+    return -1;
+  if (*ypix < 0.5 || *ypix > wcs->nypix+0.5)
+    return -1;
+
+  return 0;
+}
+/* Mar  6 1995	Original version of this code
+ * May  4 1995	Fix eta cross terms which were all in y
+ * Jun 21 1995	Add inverse routine
+ * Oct 17 1995	Fix inverse routine (degrees -> radians)
+ * Nov  7 1995	Add half pixel to image coordinates to get astrometric
+ *                plate coordinates
+ * Feb 26 1996	Fix plate to image pixel conversion error
+ *
+ * Mar 23 1998	Change names from plate*() to dss*()
+ * Apr  7 1998	Change amd_i_coeff to i_coeff
+ * Sep  4 1998	Fix possible divide by zero in dsspos() from Allen Harris, SAO
+ * Sep 10 1998	Fix possible divide by zero in dsspix() from Allen Harris, SAO
+ *
+ * Oct 21 1999	Drop declaration of cond2r in dsspix()
+ */
diff --git a/Code/src/libwcs/fileutil.c b/Code/src/libwcs/fileutil.c
new file mode 100644
index 0000000000000000000000000000000000000000..c04cda7d80694227e03511b9d74d2dafcbf82bf5
--- /dev/null
+++ b/Code/src/libwcs/fileutil.c
@@ -0,0 +1,867 @@
+/*** File libwcs/fileutil.c
+ *** August 28, 2014
+ *** By Jessica Mink, jmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1999-2014
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: jmink@cfa.harvard.edu
+           Postal address: Jessica Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:      fileutil.c (ASCII file utilities)
+ * Purpose:     Find out things about ASCII files
+ * Subroutine:	getfilelines (filename)
+ *		Return number of lines in an ASCII file
+ * Subroutine:	getfilebuff (filename)
+ *		Return entire file contents in a character string
+ * Subroutine:	getfilesize (filename)
+ *		Return size of a binary or ASCII file
+ * Subroutine:	isimlist (filename)
+ *		Return 1 if file is list of FITS or IRAF image files, else 0
+ * Subroutine:	isimlistd (filename, rootdir)
+ *		Return 1 if file is list of FITS or IRAF image files, else 0
+ * Subroutine:	isfilelist (filename, rootdir)
+ *		Return 1 if file is list of readable files, else 0
+ * Subroutine:	isfile (filename)
+ *		Return 1 if file is a readable file, else 0
+ * Subroutine:	first_token (diskfile, ncmax, token)
+ *		Return the first token from the next line of an ASCII file
+ * Subroutine:	next_line (diskfile, ncmax, line)
+ *		Read the next line of an ASCII file and return its length
+ * Subroutine:  stc2s (spchar, string)
+ *		Replace character in string with space
+ * Subroutine:  sts2c (spchar, string)
+ *		Replace spaces in string with character
+ * Subroutine:	istiff (filename)
+ *		Return 1 if file is a readable TIFF graphics file, else 0
+ * Subroutine:	isjpeg (filename)
+ *		Return 1 if file is a readable JPEG graphics file, else 0
+ * int setoken (tokens, string, cwhite)
+ *	Tokenize a string for easy decoding
+ * int nextoken (tokens, token, maxchars)
+ *	Get next token from tokenized string
+ * int getoken (tokens, itok, token, maxchars)
+ *	Get specified token from tokenized string
+ */
+
+#include <stdlib.h>
+#ifndef VMS
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <string.h>
+#include "fitsfile.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+/* GETFILELINES -- return number of lines in one file */
+
+int
+getfilelines (filename)
+
+char    *filename;      /* Name of file for which to find number of lines */
+{
+
+    char *buffer, *bufline;
+    int nlines = 0;
+    char newline = 10;
+
+    /* Read file */
+    buffer = getfilebuff (filename);
+
+    /* Count lines in file */
+    if (buffer != NULL) {
+    bufline = buffer;
+    nlines = 0;
+    while ((bufline = strchr (bufline, newline)) != NULL) {
+            bufline = bufline + 1;
+            nlines++;
+        }
+    free (buffer);
+    return (nlines);
+    }
+    else {
+    return (0);
+    }
+}
+
+
+/* GETFILEBUFF -- return entire file contents in one character string */
+
+char *
+getfilebuff (filename)
+
+char    *filename;      /* Name of file for which to find number of lines */
+{
+
+    FILE *diskfile;
+    int lfile, nr, lbuff, ipt, ibuff;
+    char *buffer, *newbuff, *nextbuff;
+
+    /* Treat stdin differently */
+    if (!strcmp (filename, "stdin")) {
+    lbuff = 5000;
+    lfile = lbuff;
+    buffer = NULL;
+    ipt = 0;
+    for (ibuff = 0; ibuff < 10; ibuff++) {
+        if ((newbuff = realloc (buffer, lfile+1)) != NULL) {
+        buffer = newbuff;
+        nextbuff = buffer + ipt;
+            nr = fread (nextbuff, 1, lbuff, stdin);
+        if (nr == lbuff)
+            break;
+        else {
+            ipt = ipt + lbuff;
+            lfile = lfile + lbuff;
+            }
+        }
+        else {
+        fprintf (stderr,"GETFILEBUFF: No room for %d-byte buffer\n",
+             lfile);
+        break;
+        }
+        }
+    return (buffer);
+    }
+
+    /* Open file */
+    if ((diskfile = fopen (filename, "rb")) == NULL)
+        return (NULL);
+
+   /* Find length of file */
+    if (fseek (diskfile, 0, 2) == 0)
+        lfile = ftell (diskfile);
+    else
+        lfile = 0;
+    if (lfile < 1) {
+    fprintf (stderr,"GETFILEBUFF: File %s is empty\n", filename);
+    fclose (diskfile);
+    return (NULL);
+    }
+
+    /* Allocate buffer to hold entire file and read it */
+    if ((buffer = calloc (1, lfile+1)) != NULL) {
+    fseek (diskfile, 0, 0);
+        nr = fread (buffer, 1, lfile, diskfile);
+    if (nr < lfile) {
+        fprintf (stderr,"GETFILEBUFF: File %s: read %d / %d bytes\n",
+             filename, nr, lfile);
+        free (buffer);
+        fclose (diskfile);
+        return (NULL);
+        }
+    buffer[lfile] = (char) 0;
+    fclose (diskfile);
+    return (buffer);
+    }
+    else {
+    fprintf (stderr,"GETFILEBUFF: File %s: no room for %d-byte buffer\n",
+         filename, lfile);
+    fclose (diskfile);
+    return (NULL);
+    }
+}
+
+
+/* GETFILESIZE -- return size of one file in bytes */
+
+int
+getfilesize (filename)
+
+char    *filename;      /* Name of file for which to find size */
+{
+    struct stat statbuff;
+
+    if (stat (filename, &statbuff))
+    return (0);
+    else
+    return ((int) statbuff.st_size);
+}
+
+int
+getfilesize0 (filename)
+
+char    *filename;      /* Name of file for which to find size */
+{
+    FILE *diskfile;
+    long filesize;
+
+    /* Open file */
+    if ((diskfile = fopen (filename, "rb")) == NULL)
+        return (-1);
+
+    /* Move to end of the file */
+    if (fseek (diskfile, 0, 2) == 0)
+
+        /* Position is the size of the file */
+        filesize = ftell (diskfile);
+
+    else
+        filesize = -1;
+
+    fclose (diskfile);
+
+    return ((int) filesize);
+}
+
+
+/* ISIMLIST -- Return 1 if list of FITS or IRAF files, else 0 */
+int
+isimlist (filename)
+
+char    *filename;      /* Name of possible list file */
+{
+    FILE *diskfile;
+    char token[256];
+    int ncmax = 254;
+
+    if ((diskfile = fopen (filename, "r")) == NULL)
+    return (0);
+    else {
+    first_token (diskfile, ncmax, token);
+    fclose (diskfile);
+    if (isfits (token) | isiraf (token))
+        return (1);
+    else
+        return (0);
+    }
+}
+
+
+/* ISIMLISTD -- Return 1 if list of FITS or IRAF files, else 0 */
+int
+isimlistd (filename, rootdir)
+
+char    *filename;	/* Name of possible list file */
+char    *rootdir;	/* Name of root directory for files in list */
+{
+    FILE *diskfile;
+    char token[256];
+    char filepath[256];
+    int ncmax = 254;
+
+    if ((diskfile = fopen (filename, "r")) == NULL)
+    return (0);
+    else {
+    first_token (diskfile, ncmax, token);
+    fclose (diskfile);
+    if (rootdir != NULL) {
+        strcpy (filepath, rootdir);
+        strcat (filepath, "/");
+        strcat (filepath, token);
+        }
+    else
+        strcpy (filepath, token);
+    if (isfits (filepath) | isiraf (filepath))
+        return (1);
+    else
+        return (0);
+    }
+}
+
+
+/* ISFILELIST -- Return 1 if list of readable files, else 0 */
+int
+isfilelist (filename, rootdir)
+
+char    *filename;      /* Name of possible list file */
+char    *rootdir;	/* Name of root directory for files in list */
+{
+    FILE *diskfile;
+    char token[256];
+    char filepath[256];
+    int ncmax = 254;
+
+    if ((diskfile = fopen (filename, "r")) == NULL)
+    return (0);
+    else {
+    first_token (diskfile, ncmax, token);
+    fclose (diskfile);
+    if (rootdir != NULL) {
+        strcpy (filepath, rootdir);
+        strcat (filepath, "/");
+        strcat (filepath, token);
+        }
+    else
+        strcpy (filepath, token);
+    if (isfile (filepath))
+        return (1);
+    else
+        return (0);
+    }
+}
+
+
+/* ISFILE -- Return 1 if file is a readable file, else 0 */
+
+int
+isfile (filename)
+
+char    *filename;      /* Name of file to check */
+{
+    struct stat statbuff;
+
+    if (!strcasecmp (filename, "stdin"))
+    return (1);
+    else if (access (filename, R_OK))
+    return (0);
+    else if (stat (filename, &statbuff))
+        return (0);
+    else {
+        if (S_ISDIR(statbuff.st_mode) && S_IFDIR)
+        return (2);
+    else
+        return (1);
+    }
+}
+
+
+/* NEXT_LINE -- Read the next line of an ASCII file, returning length */
+/*              Lines beginning with # are ignored*/
+
+int
+next_line (diskfile, ncmax, line)
+
+FILE	*diskfile;		/* File descriptor for ASCII file */
+int	ncmax;			/* Maximum number of characters returned */
+char	*line;			/* Next line (returned) */
+{
+    char *lastchar;
+
+    /* If line can be read, add null at the end of the first token */
+    if (fgets (line, ncmax, diskfile) != NULL) {
+    while (line[0] == '#') {
+        (void) fgets (line, ncmax, diskfile);
+        }
+
+    /* If only character is a control character, return a NULL string */
+    if ((strlen(line)==1) && (line[0]<32)){
+        line[0] = (char)0;
+        return (1);
+        }
+    lastchar = line + strlen (line) - 1;
+
+    /* Remove trailing spaces or control characters */
+    while (*lastchar <= 32)
+        *lastchar-- = 0;
+
+    return (strlen (line));
+    }
+    else
+    return (0);
+}
+
+
+/* FIRST_TOKEN -- Return first token from the next line of an ASCII file */
+/*                Lines beginning with # are ignored */
+
+int
+first_token (diskfile, ncmax, token)
+
+FILE	*diskfile;		/* File descriptor for ASCII file */
+int	ncmax;			/* Maximum number of characters returned */
+char	*token;			/* First token on next line (returned) */
+{
+    char *lastchar, *lspace;
+
+    /* If line can be read, add null at the end of the first token */
+    if (fgets (token, ncmax, diskfile) != NULL) {
+    while (token[0] == '#') {
+        (void) fgets (token, ncmax, diskfile);
+        }
+
+    /* If only character is a control character, return a NULL */
+    if ((strlen(token)==1) && (token[0]<32)){
+        token[0]=0;
+        return (1);
+        }
+    lastchar = token + strlen (token) - 1;
+
+    /* Remove trailing spaces or control characters */
+    while (*lastchar <= 32)
+        *lastchar-- = 0;
+
+    if ((lspace = strchr (token, ' ')) != NULL) {
+        *lspace = (char) 0;
+        }
+    return (1);
+    }
+    else
+    return (0);
+}
+
+
+/* Replace character in string with space */
+
+int
+stc2s (spchar, string)
+
+char	*spchar;	/* Character to replace with spaces */
+char	*string;
+{
+    int i, lstr, n;
+    lstr = strlen (string);
+    n = 0;
+    for (i = 0; i < lstr; i++) {
+    if (string[i] == spchar[0]) {
+        n++;
+        string[i] = ' ';
+        }
+    }
+    return (n);
+}
+
+
+/* Replace spaces in string with character */
+
+int
+sts2c (spchar, string)
+
+char	*spchar;	/* Character with which to replace spaces */
+char	*string;
+{
+    int i, lstr, n;
+    lstr = strlen (string);
+    n = 0;
+    for (i = 0; i < lstr; i++) {
+    if (string[i] == ' ') {
+        n++;
+        string[i] = spchar[0];
+        }
+    }
+    return (n);
+}
+
+
+/* ISTIFF -- Return 1 if TIFF file, else 0 */
+int
+istiff (filename)
+
+char    *filename;      /* Name of file to check */
+{
+    int diskfile;
+    char keyword[16];
+    int nbr;
+
+    /* First check to see if this is an assignment */
+    if (strchr (filename, '='))
+        return (0);
+
+    /* Check file extension */
+    if (strsrch (filename, ".tif") ||
+        strsrch (filename, ".tiff") ||
+        strsrch (filename, ".TIFF") ||
+        strsrch (filename, ".TIF"))
+        return (1);
+
+ /* If no TIFF file suffix, try opening the file */
+    else {
+        if ((diskfile = open (filename, O_RDONLY)) < 0)
+            return (0);
+        else {
+            nbr = read (diskfile, keyword, 4);
+            close (diskfile);
+            if (nbr < 4)
+                return (0);
+            else if (!strncmp (keyword, "II", 2))
+                return (1);
+            else if (!strncmp (keyword, "MM", 2))
+                return (1);
+            else
+                return (0);
+            }
+        }
+}
+
+
+/* ISJPEG -- Return 1 if JPEG file, else 0 */
+int
+isjpeg (filename)
+
+char    *filename;      /* Name of file to check */
+{
+    int diskfile;
+    char keyword[16];
+    int nbr;
+
+    /* First check to see if this is an assignment */
+    if (strchr (filename, '='))
+        return (0);
+
+    /* Check file extension */
+    if (strsrch (filename, ".jpg") ||
+        strsrch (filename, ".jpeg") ||
+        strsrch (filename, ".JPEG") ||
+        strsrch (filename, ".jfif") ||
+        strsrch (filename, ".jfi") ||
+        strsrch (filename, ".JFIF") ||
+        strsrch (filename, ".JFI") ||
+        strsrch (filename, ".JPG"))
+        return (1);
+
+ /* If no JPEG file suffix, try opening the file */
+    else {
+        if ((diskfile = open (filename, O_RDONLY)) < 0)
+            return (0);
+        else {
+            nbr = read (diskfile, keyword, 2);
+            close (diskfile);
+            if (nbr < 4)
+                return (0);
+            else if (keyword[0] == (char) 0xFF &&
+             keyword[1] == (char) 0xD8)
+                return (1);
+            else
+                return (0);
+            }
+        }
+}
+
+
+/* ISGIF -- Return 1 if GIF file, else 0 */
+int
+isgif (filename)
+
+char    *filename;      /* Name of file to check */
+{
+    int diskfile;
+    char keyword[16];
+    int nbr;
+
+    /* First check to see if this is an assignment */
+    if (strchr (filename, '='))
+        return (0);
+
+    /* Check file extension */
+    if (strsrch (filename, ".gif") ||
+        strsrch (filename, ".GIF"))
+        return (1);
+
+ /* If no GIF file suffix, try opening the file */
+    else {
+        if ((diskfile = open (filename, O_RDONLY)) < 0)
+            return (0);
+        else {
+            nbr = read (diskfile, keyword, 6);
+            close (diskfile);
+            if (nbr < 4)
+                return (0);
+            else if (!strncmp (keyword, "GIF", 3))
+                return (1);
+            else
+                return (0);
+            }
+        }
+}
+
+
+static int maxtokens = MAXTOKENS; /* Set maximum number of tokens from wcscat.h*/
+
+/* -- SETOKEN -- tokenize a string for easy decoding */
+
+int
+setoken (tokens, string, cwhite)
+
+struct Tokens *tokens;	/* Token structure returned */
+char	*string;	/* character string to tokenize */
+char	*cwhite;	/* additional whitespace characters
+             * if = tab, disallow spaces and commas */
+{
+    char squote, dquote, jch, newline;
+    char *iq, *stri, *wtype, *str0, *inew;
+    int i,j,naddw, ltok;
+
+    newline = (char) 10;
+    squote = (char) 39;
+    dquote = (char) 34;
+    if (string == NULL)
+    return (0);
+
+    /* Line is terminated by newline or NULL */
+    inew = strchr (string, newline);
+    if (inew != NULL)
+    tokens->lline = inew - string - 1;
+    else
+    tokens->lline = strlen (string);
+
+    /* Save current line in structure */
+    tokens->line = string;
+
+    /* Add extra whitespace characters */
+    if (cwhite == NULL)
+    naddw = 0;
+    else
+    naddw = strlen (cwhite);
+
+    /* if character is tab, allow only tabs and nulls as separators */
+    if (naddw > 0 && !strncmp (cwhite, "tab", 3)) {
+    tokens->white[0] = (char) 9;	/* Tab */
+    tokens->white[1] = (char) 0;	/* NULL (end of string) */
+    tokens->nwhite = 2;
+    }
+
+    /* if character is bar, allow only bars and nulls as separators */
+    else if (naddw > 0 && !strncmp (cwhite, "bar", 3)) {
+    tokens->white[0] = '|';		/* Bar */
+    tokens->white[1] = (char) 0;	/* NULL (end of string) */
+    tokens->nwhite = 2;
+    }
+
+    /* otherwise, allow spaces, tabs, commas, nulls, and cwhite */
+    else {
+    tokens->nwhite = 4 + naddw;;
+    tokens->white[0] = ' ';		/* Space */
+    tokens->white[1] = (char) 9;	/* Tab */
+    tokens->white[2] = ',';		/* Comma */
+    tokens->white[3] = (char) 124;	/* Vertical bar */
+    tokens->white[4] = (char) 0;	/* Null (end of string) */
+    if (tokens->nwhite > 20)
+        tokens->nwhite = 20;
+    if (naddw > 0) {
+        i = 0;
+        for (j = 4; j < tokens->nwhite; j++) {
+        tokens->white[j] = cwhite[i];
+        i++;
+        }
+        }
+    }
+    tokens->white[tokens->nwhite] = (char) 0;
+
+    tokens->ntok = 0;
+    tokens->itok = 0;
+    iq = string - 1;
+    for (i = 0; i < maxtokens; i++) {
+    tokens->tok1[i] = NULL;
+    tokens->ltok[i] = 0;
+    }
+
+    /* Process string one character at a time */
+    stri = string;
+    str0 = string;
+    while (stri < string+tokens->lline) {
+
+    /* Keep stuff between quotes in one token */
+    if (stri <= iq)
+        continue;
+    jch = *stri;
+
+    /* Handle quoted strings */
+    if (jch == squote)
+        iq = strchr (stri+1, squote);
+    else if (jch == dquote)
+        iq = strchr (stri+1, dquote);
+    else
+        iq = stri;
+    if (iq > stri) {
+        tokens->ntok = tokens->ntok + 1;
+        if (tokens->ntok > maxtokens) return (maxtokens);
+        tokens->tok1[tokens->ntok] = stri + 1;
+        tokens->ltok[tokens->ntok] = (iq - stri) - 1;
+        stri = iq + 1;
+        str0 = iq + 1;
+        continue;
+        }
+
+    /* Search for unquoted tokens */
+    wtype = strchr (tokens->white, jch);
+
+    /* If this is one of the additional whitespace characters,
+     * pass as a separate token */
+    if (wtype > tokens->white + 3) {
+
+        /* Terminate token before whitespace */
+        if (stri > str0) {
+        tokens->ntok = tokens->ntok + 1;
+        if (tokens->ntok > maxtokens) return (maxtokens);
+        tokens->tok1[tokens->ntok] = str0;
+        tokens->ltok[tokens->ntok] = stri - str0;
+        }
+
+        /* Make whitespace character next token; start new one */
+        tokens->ntok = tokens->ntok + 1;
+        if (tokens->ntok > maxtokens) return (maxtokens);
+        tokens->tok1[tokens->ntok] = stri;
+        tokens->ltok[tokens->ntok] = 1;
+        stri++;
+        str0 = stri;
+        }
+
+    /* Pass previous token if regular whitespace or NULL */
+    else if (wtype != NULL || jch == (char) 0) {
+
+        /* Ignore leading whitespace */
+        if (stri == str0) {
+        stri++;
+        str0 = stri;
+        }
+
+        /* terminate token before whitespace; start new one */
+        else {
+        tokens->ntok = tokens->ntok + 1;
+        if (tokens->ntok > maxtokens) return (maxtokens);
+        tokens->tok1[tokens->ntok] = str0;
+        tokens->ltok[tokens->ntok] = stri - str0;
+        stri++;
+        str0 = stri;
+        }
+        }
+
+    /* Keep going if not whitespace */
+    else
+        stri++;
+    }
+
+    /* Add token terminated by end of line */
+    if (str0 < stri) {
+    tokens->ntok = tokens->ntok + 1;
+    if (tokens->ntok > maxtokens)
+        return (maxtokens);
+    tokens->tok1[tokens->ntok] = str0;
+    ltok = stri - str0 + 1;
+    tokens->ltok[tokens->ntok] = ltok;
+
+    /* Deal with white space just before end of line */
+    jch = str0[ltok-1];
+    if (strchr (tokens->white, jch)) {
+        ltok = ltok - 1;
+        tokens->ltok[tokens->ntok] = ltok;
+        tokens->ntok = tokens->ntok + 1;
+        tokens->tok1[tokens->ntok] = str0 + ltok;
+        tokens->ltok[tokens->ntok] = 0;
+        }
+    }
+
+    tokens->itok = 0;
+
+    return (tokens->ntok);
+}
+
+
+/* NEXTOKEN -- get next token from tokenized string */
+
+int
+nextoken (tokens, token, maxchars)
+
+struct Tokens *tokens;	/* Token structure returned */
+char	*token;		/* token (returned) */
+int	maxchars;	/* Maximum length of token */
+{
+    int ltok;		/* length of token string (returned) */
+    int it, i;
+    int maxc = maxchars - 1;
+
+    tokens->itok = tokens->itok + 1;
+    it = tokens->itok;
+    if (it > tokens->ntok)
+    it = tokens->ntok;
+    else if (it < 1)
+    it = 1;
+    ltok = tokens->ltok[it];
+    if (ltok > maxc)
+    ltok = maxc;
+    strncpy (token, tokens->tok1[it], ltok);
+    for (i = ltok; i < maxc; i++)
+    token[i] = (char) 0;
+    return (ltok);
+}
+
+
+/* GETOKEN -- get specified token from tokenized string */
+
+int
+getoken (tokens, itok, token, maxchars)
+
+struct Tokens *tokens;	/* Token structure returned */
+int	itok;		/* token sequence number of token
+             * if <0, get whole string after token -itok
+             * if =0, get whole string */
+char	*token;		/* token (returned) */
+int	maxchars;	/* Maximum length of token */
+{
+    int ltok;		/* length of token string (returned) */
+    int it, i;
+    int maxc = maxchars - 1;
+
+    it = itok;
+    if (it > 0 ) {
+    if (it > tokens->ntok)
+        it = tokens->ntok;
+    ltok = tokens->ltok[it];
+    if (ltok > maxc)
+        ltok = maxc;
+    strncpy (token, tokens->tok1[it], ltok);
+    }
+    else if (it < 0) {
+    if (it < -tokens->ntok)
+        it  = -tokens->ntok;
+    ltok = tokens->line + tokens->lline - tokens->tok1[-it];
+    if (ltok > maxc)
+        ltok = maxc;
+    strncpy (token, tokens->tok1[-it], ltok);
+    }
+    else {
+    ltok = tokens->lline;
+    if (ltok > maxc)
+        ltok = maxc;
+    strncpy (token, tokens->tok1[1], ltok);
+    }
+    for (i = ltok; i < maxc; i++)
+    token[i] = (char) 0;
+
+    return (ltok);
+}
+
+/*
+ * Jul 14 1999	New subroutines
+ * Jul 15 1999	Add getfilebuff()
+ * Oct 15 1999	Fix format eror in error message
+ * Oct 21 1999	Fix declarations after lint
+ * Dec  9 1999	Add next_token(); set pointer to next token in first_token
+ *
+ * Sep 25 2001	Add isfilelist(); move isfile() from catutil.c
+ *
+ * Jan  4 2002	Allow getfilebuffer() to read from stdin
+ * Jan  8 2002	Add sts2c() and stc2s() for space-replaced strings
+ * Mar 22 2002	Clean up isfilelist()
+ * Aug  1 2002	Return 1 if file is stdin in isfile()
+ *
+ * Feb  4 2003	Open catalog file rb instead of r (Martin Ploner, Bern)
+ * Mar  5 2003	Add isimlistd() to check image lists with root directory
+ * May 27 2003	Use file stat call in getfilesize() instead of opening file
+ * Jul 17 2003	Add root directory argument to isfilelist()
+ *
+ * Sep 29 2004	Drop next_token() to avoid conflict with subroutine in catutil.c
+ *
+ * Sep 26 2005	In first_token, return NULL if token is only control character
+ *
+ * Feb 23 2006	Add istiff(), isjpeg(), isgif() to check TIFF, JPEG, GIF files
+ * Jun 20 2006	Cast call to fgets() void
+ *
+ * Jan  5 2007	Change stc2s() and sts2c() to pass single character as pointer
+ * Jan 11 2007	Move token access subroutines from catutil.c
+ *
+ * Aug 28 2014	Return length from  next_line(): 0=unsuccessful
+ */
diff --git a/Code/src/libwcs/findstar.c b/Code/src/libwcs/findstar.c
new file mode 100644
index 0000000000000000000000000000000000000000..09a7142867463a545b92d02287e3345bd357f7bf
--- /dev/null
+++ b/Code/src/libwcs/findstar.c
@@ -0,0 +1,1095 @@
+/*** File libwcs/findstar.c
+ *** October 19, 2007
+ *** By Doug Mink, after Elwood Downey
+ *** Copyright (C) 1996-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "fitsfile.h"
+#include "wcs.h"
+#include "wcscat.h"
+#include "lwcs.h"
+
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+
+static int HotPixel();
+static int starRadius();
+static void starCentroid();
+static int BrightWalk ();
+static double FindFlux ();
+static void mean2d();
+static void mean1d();
+static void rotstars();
+extern void setminmatch();
+extern void setnitmax();
+extern void setminstars();
+extern void setminpmqual();
+extern void setminid();
+extern void setnxydec();
+
+/* Set input catalog for image stars */
+static char imcatname[256] = "";
+void setimcat (cat)
+char *cat;
+{strcpy (imcatname, cat); return; }
+
+/* Get input catalog for image stars */
+char *getimcat ()
+{return (imcatname); }
+
+static int nspix = NSTATPIX;	/* Stats are computed for +- this many pixels */
+void setnspix (nsp)
+int nsp;
+{ nspix = nsp; return; }
+
+static int ispix = ISTATPIX;	/* Stats are computed every this many pixels */
+void setispix (isp)
+int isp;
+{ ispix = isp; return; }
+
+static int maxw = MAXWALK;	/* Farthest distance to walk from seed */
+void setmaxwalk (wmax)
+int wmax;
+{ maxw = wmax; return; }
+
+static double burnedout = BURNEDOUT; /* Clamp pixels brighter than this */
+void setburnedout (bmax)
+double bmax;
+{ burnedout = bmax; return; }
+
+static int niterate = NITERATE;	/* Number of iterations for sigma clipping */
+void setniterate (nit)
+int nit;
+{ niterate = nit; return;}
+
+/* Stars must be at least this many standard deviations above the mean */
+static double starsig = STARSIGMA;
+void setstarsig (sig)
+double sig;
+{ starsig = sig; return; }
+
+static int fsborder = BORDER;	/* Ignore this much of the edge */
+void setborder (brd)
+int brd;
+{ fsborder = brd; return; }
+
+static int rnoise = RNOISE;	/* Mean noise is from center +- this many pixels */
+void setrnoise (rn)
+int rn;
+{ rnoise = rn; return; }
+
+static int maxrad = MAXRAD;	/* Maximum radius for a star */
+void setmaxrad (rmax)
+int rmax;
+{ maxrad = rmax; return; }
+
+static int minrad = MINRAD;	/* Minimum radius for a star */
+void setminrad (rmin)
+int rmin;
+{ minrad = rmin; return; }
+
+static double bmin = MINPEAK;	/* Minimum peak for a star */
+void setbmin (min)
+double min;
+{ bmin = min; return; }
+
+static int minsep = MINSEP;	/* Minimum separation for stars */
+void setminsep (smin)
+int smin;
+{ minsep = smin; return; }
+
+static int mirror = 0;
+void setmirror (mirror1)
+int mirror1;
+{ mirror = mirror1; return;}
+
+static int rotate = 0;
+void setrotate (rotate1)
+int rotate1;
+{ rotate = rotate1; return;}
+
+
+/* Find the location and brightest pixel of stars in the given image.
+ * Return malloced arrays of x and y and b.
+ * N.B. Caller must free *xa and *ya and *ba even if 0 stars are returned.
+ * N.B. Pixels outside fsborder are ignored.
+ * N.B. Isolated hot pixels are ignored.
+ * return number of stars (might well be 0 :-), or -1 if trouble.
+ */
+
+int
+FindStars (header, image, xa, ya, ba, pa, verbose, zap)
+
+char	*header;	/* FITS header */
+char	*image;		/* image pixels */
+double	**xa, **ya;	/* X and Y coordinates of stars, array returned */
+double	**ba;		/* Fluxes of stars in counts, array returned */
+int	**pa;		/* Peak counts of stars in counts, array returned */
+int	verbose;	/* 1 to print each star's position */
+int	zap;		/* If 1, set star to background after reading */
+
+{
+    double noise, nsigma;
+    int nstars;
+    double minll;
+    int bitpix;
+    int w, h, ilp, irp, i, idx, idy;
+    int x, y, x1, x2, y1, y2;
+    double xai, yai, bai;
+    double minsig, sigma;
+    double *svec, *svb, *sv, *sv1, *sv2, *svlim;
+    double rmax;
+    double bz, bs;		/* Pixel value scaling */
+    int *ixa, *iya;
+    int lwidth;
+    int nextline;
+    int xborder1, xborder2, yborder1, yborder2;
+    char trimsec[32];
+    int nstarmax = 100;
+    extern void setscale();
+
+    hgeti4 (header,"NAXIS1", &w);
+    hgeti4 (header,"NAXIS2", &h);
+    hgeti4 (header,"BITPIX", &bitpix);
+    bz = 0.0;
+    hgetr8 (header,"BZERO", &bz);
+    bs = 1.0;
+    hgetr8 (header,"BSCALE", &bs);
+    if (bz == 0.0 && bs == 1.0)
+	setscale (0);
+
+    /* Allocate the position, flux, and peak intensity arrays
+     * it's ok to do now because we claim caller should always free these.
+     */
+    *xa = (double *) calloc (nstarmax, sizeof(double));
+    *ya = (double *) calloc (nstarmax, sizeof(double));
+    *ba = (double *) calloc (nstarmax, sizeof(double));
+    *pa = (int *) calloc (nstarmax, sizeof(int));
+    ixa = (int *) calloc (nstarmax, sizeof (int));
+    iya = (int *) calloc (nstarmax, sizeof (int));
+
+    /* Read star list from file */
+    if (imcatname[0] != 0) {
+	int nlog = 0;
+	if (verbose) nlog = 10;
+	if (istab (imcatname))
+	    nstars = tabxyread (imcatname, xa, ya, ba, pa, nlog);
+	else
+	    nstars = daoread (imcatname, xa, ya, ba, pa, nlog);
+	if (rotate != 0 || mirror)
+	    rotstars (nstars, *xa, *ya, w, h);
+	return (nstars);
+	}
+
+    /* Set trim section for star searching */
+    if (hgets (header, "TRIMSEC", 32, trimsec)) {
+	char *tx1, *tx2, *tx3, *tx4, *tx5;
+	tx1 = trimsec + 1;
+	tx2 = strchr (trimsec, ':');
+	*tx2 = (char) 0;
+	xborder1 = atoi (tx1+1);
+	tx2 = tx2 + 1;
+	tx3 = strchr (tx2, ',');
+	*tx3 = (char) 0;
+	xborder2 = w - atoi (tx2);
+	tx3 = tx3 + 1;
+	tx4 = strchr (tx3, ':');
+	*tx4 = (char) 0;
+	yborder1 = atoi (tx3);
+	tx4= tx4 + 1;
+	tx5 = strchr (tx4, ']');
+	*tx5 = (char) 0;
+	yborder2 = atoi (tx4) - h;
+	}
+    else {
+	xborder1 = fsborder;
+	xborder2 = fsborder;
+	yborder1 = fsborder;
+	yborder2 = fsborder;
+	}
+
+    /* Allocate a buffer to hold one image line */
+    svec = (double *) malloc (w * sizeof (double));
+
+    /* Compute image noise from a central swath */
+    x1 = (w / 2) - rnoise;
+    if (x1 < 1)
+	x1 = 1;
+    x2 = (w / 2) + rnoise;
+    if (x2 > w)
+	x2 = w;
+    y1 = (h / 2) - rnoise;
+    if (y1 < 1)
+	y1 = 1;
+    y2 = (h / 2) + rnoise;
+    if (y2 > h)
+	y2 = h;
+    mean2d (image,bitpix,w,h,bz,bs, x1, x2, y1, y2, &noise, &nsigma);
+    if (verbose)
+	fprintf (stderr, "FindStar mean is %.2f, sigma is %.2f\n",
+		 noise, nsigma);
+
+    /* Fill in borders of the image line buffer with noise */
+    svlim = svec + w;
+    svb = svec + xborder1;
+    for (sv = svec; sv < svb; sv++)
+	*sv = noise;
+    for (sv = svlim - xborder2; sv < svlim; sv++)
+	*sv = noise;
+    if (verbose) {
+	fprintf (stderr, "FindStar x=1-%d, %d-%d set to noise\n",
+		 xborder1, w-xborder2+1, w);
+	fprintf (stderr, "FindStar y=1-%d, %d-%d set to noise\n",
+		 yborder1, h-yborder2+1, h);
+	}
+    if (bmin > 0)
+	minll = noise + bmin;
+    else
+	minll = noise + (starsig * nsigma);
+    sigma = sqrt (minll);
+    if (nsigma < sigma)
+	minsig = sigma;
+    else
+	minsig = nsigma;
+
+    /* Scan for stars based on surrounding local noise figure */
+    nstars = 0;
+    lwidth = w - xborder2 - xborder1 + 1;
+    for (y = yborder1; y < h-yborder1; y++) {
+        int ipix = 0;
+
+	/* Get one line of the image minus the noise-filled borders */
+	nextline = (w * (y-1)) + xborder1 - 1;
+	getvec (image, bitpix, bz, bs, nextline, lwidth, svb);
+	if (verbose)
+	    fprintf (stderr, "Row %5d Col     0:\r", y+1);
+
+	/* Search row for bright pixels */
+	for (x = xborder1; x < w-xborder2; x++) {
+
+	    if (verbose && x%100 == 0)
+		fprintf (stderr, "Row %5d Col %5d:\r", y+1, x+1);
+
+	    /* Redo stats once for every several pixels */
+	    if (ispix > 0 && nspix > 0 && ipix++ % ispix == 0) {
+
+		/* Find stats to the left */
+		ilp = x - (nspix / 2);
+		if (ilp < 0)
+		    ilp = 0;
+		sv1 = svec + ilp;
+		irp = ilp + nspix;
+		if (irp < w)
+		    sv2 = svec + irp;
+		else
+		    sv2 = svlim;
+		minsig = 0.0;
+		if (sv2 > sv1+1)
+		    mean1d (sv1, sv2, &noise, &minsig);
+		sigma = sqrt (noise);
+		if (minsig < sigma)
+		    minsig = sigma;
+		minll = noise + (starsig * minsig);
+		}
+
+	    /* Pixel is a candidate if above the noise */
+	    if (svec[x] > minll) {
+		int sx, sy, r, rf;
+		double b;
+		int i;
+
+		/* Ignore faint stars */
+		if (svec[x] < bmin)
+		    continue;
+
+		/* Ignore hot pixels */
+		if (!HotPixel (image,bitpix,w,h,bz,bs, x, y, minll))
+		    continue;
+
+		/* Walkabout to find brightest pixel in neighborhood */
+		if (BrightWalk (image,bitpix,w,h,bz,bs,x,y,maxw,&sx,&sy,&b) < 0)
+		    continue;
+
+		/* Ignore really bright stars */
+		if (burnedout > 0 && b >= burnedout)
+		    continue;
+
+		/* Skip star if already in list */
+		for (i = 0; i < nstars; i++) {
+		    idy = iya[i] - sy;
+		    if (idy < 0)
+			idy = -idy;
+		    if (idy <= minsep) {
+			idx = ixa[i] - sx;
+			if (idx < 0)
+			    idx = -idx;
+			if (idx <= minsep)
+			    break;
+			}
+		    }
+		if (i < nstars)
+		    continue;
+
+		/* Keep it if it is within the size range for stars */
+		rmax = maxrad;
+		r = starRadius (image,bitpix,w,h,bz,bs, sx, sy, rmax,
+				minsig, noise);
+		if (r > minrad && r <= maxrad) {
+
+		/* Centroid star */
+		    nstars++;
+		    if (nstars > nstarmax) {
+			nstarmax = nstarmax * 2;
+			*xa= (double *) realloc(*xa, nstarmax*sizeof(double));
+			*ya= (double *) realloc(*ya, nstarmax*sizeof(double));
+			ixa= (int *) realloc(ixa, nstarmax*sizeof(int));
+			iya= (int *) realloc(iya, nstarmax*sizeof(int));
+			*ba= (double *) realloc(*ba, nstarmax*sizeof(double));
+			*pa= (int *) realloc(*pa, nstarmax*sizeof(int));
+			}
+		    starCentroid (image,bitpix,w,h,bz,bs, sx, sy, &xai, &yai); 
+		    (*xa)[nstars-1] = xai;
+		    (*ya)[nstars-1] = yai;
+		    ixa[nstars-1] = (int) (xai + 0.5);
+		    iya[nstars-1] = (int) (yai + 0.5);
+		    (*pa)[nstars-1] = (int) b;
+
+		/* Find radius of star for photometry */
+		/* Outermost 1-pixel radial band is one sigma above background */
+		    sx = (int) (xai + 0.5);
+		    sy = (int) (yai + 0.5);
+		    rmax = 2.0 * (double) maxrad;
+		    rf = starRadius (image,bitpix,w,h,bz,bs, sx, sy, rmax,
+				    minsig, noise);
+
+		/* Find flux from star */
+		    bai = FindFlux (image,bitpix,w,h,bz,bs,sx,sy,rf,noise,zap);
+		    (*ba)[nstars-1] = bai;
+		    if (verbose) {
+			fprintf (stderr, "Row %5d Col %5d: ", y+1, x+1);
+			fprintf (stderr," %d: (%d %d) -> (%7.3f %7.3f)",
+				 nstars, sx, sy, xai, yai);
+			fprintf (stderr," %8.1f -> %10.1f  %d -> %d    ",
+				 b, bai, r, rf);
+			(void)putc (13,stderr);
+			}
+		    }
+		/* else {
+		    fprintf (stderr," %d: (%d %d) %d > %d\n",
+			     nstars, sx, sy, r, maxrad);
+		    } */
+		}
+	    }
+	}
+
+    /* Turn fluxes into instrument magnitudes */
+    (void) FluxSortStars (*xa, *ya, *ba, *pa, nstars);
+    if (nstars > 0) {
+	double *flux;
+	for (i = 0; i < nstars; i++) {
+	    flux = (*ba)+i;
+	    *flux = -2.5 * log10 (*flux);
+	    }
+	}
+
+    free ((char *)svec);
+    return (nstars);
+}
+
+
+/* Check pixel at x/y for being "hot", ie, a pixel surrounded by noise.
+ * If any are greater than pixel at x/y then return -1.
+ * Else set the pixel at x/y to llimit and return 0.
+ */
+
+static int
+HotPixel (image, bitpix, w, h, bz, bs, x, y, llimit)
+
+char	*image;		/* Image array origin pointer */
+int	bitpix;		/* Bits per pixel, negative for floating point or unsigned int */
+int	w;		/* Image width in pixels */
+int	h;		/* Image height in pixels */
+double	bz;		/* Zero point for pixel scaling */
+double	bs;		/* Scale factor for pixel scaling */
+int	x, y;
+double	llimit;
+
+{
+    double pix1, pix2, pix3;
+
+    /* Check for hot row */
+    pix1 = getpix (image,bitpix,w,h,bz,bs,x-1,y-1);
+    pix2 = getpix (image,bitpix,w,h,bz,bs,x,y-1);
+    pix3 = getpix (image,bitpix,w,h,bz,bs,x+1,y-1);
+    if (pix1 > llimit || pix2 > llimit || pix3 > llimit)
+	return (-1);
+    pix1 = getpix (image,bitpix,w,h,bz,bs,x-1,y+1);
+    pix2 = getpix (image,bitpix,w,h,bz,bs,x,y+1);
+    pix3 = getpix (image,bitpix,w,h,bz,bs,x+1,y+1);
+    if (pix1 > llimit || pix2 > llimit || pix3 > llimit)
+	return (-1);
+
+    /* Check for hot column */
+    pix1 = getpix (image,bitpix,w,h,bz,bs,x-1,y-1);
+    pix2 = getpix (image,bitpix,w,h,bz,bs,x-1,y);
+    pix3 = getpix (image,bitpix,w,h,bz,bs,x-1,y+1);
+    if (pix1 > llimit || pix2 > llimit || pix3 > llimit)
+	return (-1);
+    pix1 = getpix (image,bitpix,w,h,bz,bs,x+1,y-1);
+    pix2 = getpix (image,bitpix,w,h,bz,bs,x+1,y);
+    pix3 = getpix (image,bitpix,w,h,bz,bs,x+1,y+1);
+    if (pix1 > llimit || pix2 > llimit || pix3 > llimit)
+	return (-1);
+
+    /* Check for hot pixel */
+    pix1 = getpix (image,bitpix,w,h,bz,bs,x-1,y);
+    pix3 = getpix (image,bitpix,w,h,bz,bs,x+1,y);
+    if (pix1 > llimit || pix3 > llimit)
+	return (-1);
+    pix1 = getpix (image,bitpix,w,h,bz,bs,x,y-1);
+    pix3 = getpix (image,bitpix,w,h,bz,bs,x,y+1);
+    if (pix1 > llimit || pix3 > llimit)
+	return (-1);
+
+    putpix (image, bitpix, w, h, bz, bs, x, y, llimit);
+    return (0);
+}
+
+
+/* Compute and return the radius of the star centered at x0, y0.
+ * A guard band is assumed to exist on the image.
+ * Calling program is assumed to reject object if r > rmax.
+ */
+
+static int
+starRadius (imp, bitpix, w, h, bz, bs, x0, y0, rmax, minsig, background)
+
+char	*imp;		/* Image array origin pointer */
+int	bitpix;		/* Bits per pixel, negative for floating point or unsigned int */
+int	w;		/* Image width in pixels */
+int	h;		/* Image height in pixels */
+double	bz;		/* Zero point for pixel scaling */
+double	bs;		/* Scale factor for pixel scaling */
+int	x0, y0;		/* Coordinates of center pixel of star */
+double	rmax;		/* Maximum allowable radius of star */
+double	minsig;		/* Minimum level for signal */
+double	background;	/* Mean background level */
+
+{
+    int r, irmax;
+    double dp, sum, mean;
+    int xyrr, yrr, np;
+    int inrr, outrr;
+    int x, y;
+    irmax = (int) rmax;
+
+    /* Compute star's radius.
+     * Scan in ever-greater circles until find one such that the mean at
+     * that radius is less than one sigma above the background level.
+     */
+    for (r = 2; r <= irmax; r++) {
+	inrr = r*r;
+	outrr = (r+1)*(r+1);
+	np = 0;
+	sum = 0.0;
+
+	for (y = -r; y <= r; y++) { 
+	    yrr = y*y;
+	    for (x = -r; x <= r; x++) {
+		xyrr = x*x + yrr;
+		if (xyrr >= inrr && xyrr < outrr) {
+		    dp = getpix (imp,bitpix,w,h,bz,bs,x0+x,y0+y);
+		    sum += dp;
+		    np++;
+		    }
+		}
+	    }
+
+	mean = (sum / np) - background;
+	if (mean < minsig)
+	    break;
+	}
+
+    return (r);
+}
+
+/* Compute the fine location of the star peaking at [x0,y0] */
+
+static void
+starCentroid (imp, bitpix, w, h, bz, bs, x0, y0, xp, yp)
+
+char	*imp;
+int	bitpix;
+int	w;
+int	h;
+double	bz;		/* Zero point for pixel scaling */
+double	bs;		/* Scale factor for pixel scaling */
+int	x0, y0;
+double	*xp, *yp;
+
+{
+    double p1, p2, p22, p3, d;
+
+    /* Find maximum of best-fit parabola in each direction.
+     * see Bevington, page 210
+     */
+
+    p1 = getpix (imp,bitpix,w,h,bz,bs,x0-1,y0);
+    p2 = getpix (imp,bitpix,w,h,bz,bs,x0,y0);
+    p22 = 2*p2;
+    p3 = getpix (imp,bitpix,w,h,bz,bs,x0+1,y0);
+    d = p3 - p22 + p1;
+    *xp = (d == 0) ? x0 : x0 + 0.5 - (p3 - p2)/d;
+    *xp = *xp + 1.0;
+
+    p1 = getpix (imp,bitpix,w,h,bz,bs,x0,y0-1);
+    p3 = getpix (imp,bitpix,w,h,bz,bs,x0,y0+1);
+    d = p3 - p22 + p1;
+    *yp = (d == 0) ? y0 : y0 + 0.5 - (p3 - p2)/d;
+    *yp = *yp + 1.0;
+}
+
+
+/* Given an image and a starting point, walk the gradient to the brightest
+ * pixel and return its location, never going more than maxrad away.
+ * Return 0 if brightest pixel found within maxsteps, else -1 */
+
+static int dx[8]={1,0,-1,1,-1,1,0,-1};
+static int dy[8]={1,1,1,0,0,-1,-1,-1};
+
+static int
+BrightWalk (image, bitpix, w, h, bz, bs, x0, y0, maxr, xp, yp, bp)
+
+char	*image;
+int	bitpix;
+int	w;
+int	h;
+double	bz;		/* Zero point for pixel scaling */
+double	bs;		/* Scale factor for pixel scaling */
+int	x0;
+int	y0;
+int	maxr;
+int	*xp;
+int	*yp;
+double	*bp;
+
+{
+
+    double b, tmpb, newb;
+    int x, y, x1, y1, i, xa, ya;
+
+    /* start by assuming seed point is brightest */
+    b = getpix (image,bitpix,w,h,bz,bs, x0,y0);
+    x = x0;
+    y = y0;
+    xa = x0;
+    ya = y0;
+
+    /* walk towards any brighter pixel */
+    for (;;) {
+	int newx = 0;
+	int newy = 0;
+
+	/* Find brightest pixel in 3x3 region */
+	newb = b;
+	for (i = 0; i < 8; i++) {
+	    x1 = x + dx[i];
+	    y1 = y + dy[i];
+	    tmpb = getpix (image,bitpix,w,h,bz,bs, x1, y1);
+	    if (tmpb >= newb) {
+		if (x1 == xa && y1 == ya)
+		    break;
+		xa = x;
+		ya = y;
+		newx = x1;
+		newy = y1;
+		newb = tmpb;
+		}
+	    }
+ 
+	/* If brightest pixel is one in center of region, quit */
+	if (newb == b)
+	    break;
+
+	/* Otherwise, set brightest pixel to new center */
+	x = newx;
+	y = newy;
+	b = newb;
+	if (abs(x-x0) > maxr || abs(y-y0) > maxr)
+	    return (-1);
+	}
+
+    *xp = x;
+    *yp = y;
+    *bp = b;
+    return (0);
+}
+
+/* Compute stats in the give region of the image of width w pixels.
+ * Bounds are not checked.
+ */
+
+static void
+mean2d (image, bitpix, w, h, bz, bs, x1, x2, y1, y2, mean, sigma)
+
+char	*image;
+int	bitpix;
+int	w;
+int	h;
+double	bz;		/* Zero point for pixel scaling */
+double	bs;		/* Scale factor for pixel scaling */
+int	x1,x2;
+int	y1, y2;
+double	*mean;
+double	*sigma;
+
+{
+    double p, pmin, pmax;
+    double pmean = 0.0;
+    double sd = 0.0;
+    int x, y;
+    int i;
+    double sum;
+    double dnpix;
+    int npix;
+
+    pmin = -1.0e20;
+    pmax = 1.0e20;
+
+    for (i = 0; i < niterate; i++ ) {
+	sum = 0.0;
+	npix = 0;
+
+    /* Compute mean */
+	if (i == 0) {
+	    for (y = y1; y < y2; y++) {
+		for (x = x1; x < x2; x++) {
+		    p = getpix (image,bitpix,w,h,bz,bs, x, y);
+		    sum += p;
+		    npix++;
+		    }
+		}
+	    }
+	else {
+	    for (y = y1; y < y2; y++) {
+		for (x = x1; x < x2; x++) {
+		    p = getpix (image,bitpix,w,h,bz,bs, x, y);
+		    if (p > pmin && p < pmax) {
+			sum += p;
+			npix++;
+			}
+		    }
+		}
+	    }
+	dnpix = (double) npix;
+	pmean = sum / dnpix;
+
+    /* Compute average deviation */
+	npix = 0;
+	sum = 0.0;
+	for (y = y1; y < y2; y++) {
+	    for (x = x1; x < x2; x++) {
+		p = getpix (image,bitpix,w,h,bz,bs, x, y);
+		if (p > pmin && p < pmax) {
+		    sum += fabs (p - pmean);
+		    npix++;
+		    }
+		}
+	    }
+
+	dnpix = (double) npix;
+	if (npix > 0)
+	    sd = sum / dnpix;
+	else
+	    sd = 0.0;
+	pmin = pmean - sd * starsig;
+	pmax = pmean + sd * starsig;
+	}
+
+    *mean = pmean;
+    *sigma = sd;
+    return;
+}
+
+
+static void
+mean1d (sv1, sv2, mean, sigma)
+
+double *sv1, *sv2;	/* starting and ending pixels for statistics */
+double *mean;		/* Mean value of pixels (returned) */
+double *sigma;		/* Average deviation of pixels (returned) */
+{
+    double *sv;
+    double p, pmin, pmax;
+    double pmean = 0.0;
+    double sd = 0.0;
+    int i;
+    int npix;
+    double dnpix;
+    double sum;
+
+    pmin = -1.0e20;
+    pmax = 1.0e20;
+
+    /* Iterate with sigma-clipping */
+    for (i = 0; i < niterate; i++ ) {
+	npix = 0;
+	sum = 0.0;
+
+	/* Compute mean */
+	for (sv = sv1; sv < sv2; sv++) {
+	    p = *sv;
+	    if (p > pmin && p < pmax) {
+		sum += p;
+		npix++;
+		}
+	    }
+	if (npix > 0) {
+	    dnpix = (double) npix;
+	    pmean = sum / dnpix;
+	    }
+	else
+	    pmean = 0.0;
+
+	/* Compute average deviation */
+	npix = 0;
+	sum = 0.0;
+	for (sv = sv1; sv < sv2; sv++) {
+	    p = *sv;
+	    if (p > pmin && p < pmax) {
+		sum += fabs (p - pmean);
+		npix++;
+		}
+	    }
+	if (npix > 0)
+	    sd = sum / dnpix;
+	else
+	    sd = 0.0;
+	pmin = pmean - (sd * starsig);
+	pmax = pmean + (sd * starsig);
+	}
+    *mean = pmean;
+    *sigma = sd;
+    return;
+}
+
+
+/* Find total flux within a circular region minus a mean background level */
+
+static double
+FindFlux (image, bitpix, w, h, bz, bs, x0, y0, r, background, zap)
+
+char	*image;
+int	bitpix;
+int	w;
+int	h;
+double	bz;		/* Zero point for pixel scaling */
+double	bs;		/* Scale factor for pixel scaling */
+int	x0;
+int	y0;
+int	r;
+double	background;	/* Background level (subtracted for flux) */
+int	zap;		/* If 1, set star to background after reading */
+{
+    double sum = 0.0;
+    int x, y, x1, x2, y1, y2, yy, xxyy, xi, yi;
+    int rr = r * r;
+    double dp;
+
+/* Keep X within image */
+    x1 = -r;
+    if (x0-r < 0)
+	x1 = 0;
+    x2 = r;
+    if (x0+r > 0)
+	x2 = w;
+
+/* Keep Y within image */
+    y1 = -r;
+    if (y0-r < 0)
+	y1 = 0;
+    y2 = r;
+    if (y0+r > 0)
+	y2 = h;
+
+/* Integrate circular region around a star */
+    for (y = y1; y <= y2; y++) { 
+	yy = y*y;
+	for (x = x1; x <= x2; x++) {
+	    xxyy = x*x + yy;
+	    if (xxyy <= rr) {
+		xi = x0 + x;
+		yi = y0 + x;
+		dp = getpix (image, bitpix, w,h,bz,bs, xi, yi);
+		if (dp > background) {
+		    sum += dp - background;
+		    if (zap)
+		        putpix (image, bitpix, w,h,bz,bs, xi, yi,background);
+		    }
+		}
+	    }
+	}
+
+    return (sum);
+}
+
+
+/* Reset parameter values from the command line */
+void
+setparm (parstring)
+char *parstring;
+{
+    char *parname;
+    char *parvalue;
+
+    parname = parstring;
+    if ((parvalue = strchr (parname,'=')) == NULL)
+	return;
+    *parvalue = (char) 0;
+    parvalue++;
+    if (!strcmp (parname, "nstatpix") ||
+	!strcmp (parname, "nspix"))
+	setnspix (atoi (parvalue));
+    else if (!strcmp (parname, "istatpix") ||
+	!strcmp (parname, "ispix"))
+	setispix (atoi (parvalue));
+    else if (!strcmp (parname, "niterate") ||
+	!strcmp (parname, "niter"))
+	setniterate (atoi (parvalue));
+    else if (!strcmp (parname, "border"))
+	setborder (atoi (parvalue));
+    else if (!strcmp (parname, "maxrad"))
+	setmaxrad (atoi (parvalue));
+    else if (!strcmp (parname, "minrad"))
+	setminrad (atoi (parvalue));
+    else if (!strcmp (parname, "starsig"))
+	setstarsig (atof (parvalue));
+    else if (!strcmp (parname, "maxwalk"))
+	setmaxwalk (atoi (parvalue));
+    else if (!strcmp (parname, "minsep"))
+	setminsep (atoi (parvalue));
+    else if (!strcmp (parname, "minpeak"))
+	setbmin (atof (parvalue));
+    else if (!strcmp (parname, "minmatch"))
+	setminmatch ((int) atof (parvalue));
+    else if (!strcmp (parname, "nmax"))
+	setnitmax ((int) atof (parvalue));
+    else if (!strcmp (parname, "nitmax"))
+	setnitmax ((int) atof (parvalue));
+    else if (!strcmp (parname, "minstars"))
+	setminstars ((int) atof (parvalue));
+    else if (!strcmp (parname, "minpmqual"))
+	setminpmqual ((int) atof (parvalue));
+    else if (!strcmp (parname, "minid"))
+	setminid ((int) atof (parvalue));
+    else if (!strcmp (parname, "nxydec"))
+	setnxydec ((int) atof (parvalue));
+    else if (!strcmp (parname, "rnoise"))
+	setrnoise ((int) atof (parvalue));
+    return;
+}
+
+static void
+rotstars (nstars, xa, ya, w, h)
+
+int	nstars;	/* Number of stars found in image */
+double	*xa;	/* X coordinates of stars */
+double	*ya;	/* Y coordinates of stars */
+int	w;	/* Original width of image */
+int	h;	/* Original height of image */
+
+{
+    int istar;
+    void rotstar();
+
+    if (rotate == 1)
+	rotate = 90;
+    else if (rotate == 2)
+	rotate = 180;
+    else if (rotate == 3)
+	rotate = 270;
+    else if (rotate < 0)
+	rotate = rotate + 360;
+    else if (rotate > 360)
+	rotate = rotate - 360;
+
+    /* Rotate star postions one at a time */
+    for (istar = 0; istar < nstars; istar++)
+	rotstar (&xa[istar], &ya[istar], w, h);
+
+    return;
+}
+
+void
+rotstar (x, y, w, h)
+
+double	*x;	/* X coordinates of stars */
+double	*y;	/* Y coordinates of stars */
+int	w;	/* Original width of image */
+int	h;	/* Original height of image */
+
+{
+    double x1, y1, x2, y2;
+    double xn = (double) w;
+    double yn = (double) h;
+    int reflect=mirror;		/* 1 if image is reflected, else 0 */
+
+    x1 = *x;
+    y1 = *y;
+    x2 = x1;
+    y2 = y1;
+
+    /* Rotate star postions one at a time */
+
+    /* Mirror coordinates without rotation */
+    if (rotate < 45.0 && rotate > -45.0) {
+	if (reflect == 1)
+	    x2 = xn - x1 - 1.0;
+	else if (reflect == 2)
+	    y2 = yn - y1 - 1.0;
+	}
+
+    /* Rotate by 90 degrees */
+    else if (rotate >= 45 && rotate < 135) {
+	if (reflect == 1) {
+	    x2 = yn - y1 - 1.0;
+	    y2 = xn - x1 - 1.0;
+	    }
+	else if (reflect == 2) {
+	    x2 = y1;
+	    y2 = x1;
+	    }
+	else {
+	    x2 = yn - y1 - 1.0;
+	    y2 = x1;
+	    }
+	}
+
+    /* Rotate by 180 degrees */
+    else if (rotate >= 135 && rotate < 225) {
+	if (reflect == 1)
+	    y2 = yn - y1 - 1.0;
+	else if (reflect == 2)
+	    x2 = xn - x1 - 1.0;
+	else {
+	    x2 = xn - x1 - 1.0;
+	    y2 = yn - y1 - 1.0;
+	    }
+	}
+
+    /* Rotate by 270 degrees */
+    else if (rotate >= 225 && rotate < 315) {
+	if (reflect == 1) {
+	    x2 = y1;
+	    y2 = x1;
+	    }
+	else if (reflect == 2) {
+	    x2 = yn - y1 - 1.0;
+	    y2 = xn - x1 - 1.0;
+	    }
+	else {
+	    x2 = y1;
+	    y2 = xn - x1 - 1.0;
+	    }
+	}
+
+    /* If rotating by more than 315 degrees, flip across both axes */
+    else if (rotate >= 315 && mirror) {
+	x2 = y1;
+	y2 = x1;
+	}
+
+    *x = x2;
+    *y = y2;
+
+    return;
+}
+
+/* May 21 1996	Return peak flux in counts
+ * May 22 1996	Add arguments so GETPIX and PUTPIX can check coordinates
+ * Jun  6 1996	Change name from findStars to FindStars
+ * Jun 12 1996	Remove unused variables after using lint
+ * Jun 13 1996	Removed leftover free of image
+ * Aug  6 1996	Fixed small defects after lint
+ * Aug 26 1996	Drop unused variables NH and NW
+ * Aug 30 1996	Modify sigma computation; allow border to be set
+ * Sep  1 1996	Set constants in lwcs.h
+ * Oct 15 1996	Drop unused variables
+ * Dec 10 1996	Check for hot columns as well as hot rows
+ * Dec 10 1996	Add option to read image stars from DAOFIND file
+ *
+ * Mar 20 1997	Declare external subroutine DAOREAD
+ * Nov  6 1997	Add subroutine to return image catalog filename
+ * Dec 15 1997	Change calls to ABS to FABS when doubles are involved
+ *
+ * May 27 1998	Include imio.h
+ * Jul 30 1998	Deal with too-small sigmas
+ *
+ * Feb  4 1999	Keep overexposed (pixel value > BURNEDOUT) stars
+ * Apr 26 1999	Fix Bright Walk() to slide along bleeding rows or columns
+ * Apr 28 1999	Add scaling to getpix and getvec
+ * Jun 11 1999	Add parameter setting subroutine
+ * Oct 21 1999	Drop unused variables and fix sigma usage after lint
+ * Oct 25 1999	Fix mean loop to avoid bad pointer creation
+ * Oct 29 1999	Read image star positions from tab table or DAOPHOT table
+ * Nov 23 1999	Lengthen imcatname from 32 to 256 for long pathnames
+ *
+ * Mar 27 2000	Drop unused variable imtab
+ *
+ * Jan 18 2001	Use trim section from image header, if not trimmed
+ * Jul 25 2001	Return plate magnitudes instead of fluxes
+ * Oct 31 2001	Add minmatch from matchstar.c to setparm() options
+ * Nov  6 2001	Add setnitmax() to setparm()
+ * Nov  7 2001	Add setminstars() to setparm()
+ * Dec 19 2001	Add setmirror() and setrotate() to rotate input catalog
+ *
+ * Jan 23 2002	If zap, set pixels in star to background after adding up flux
+ * Jan 23 2002	Skip recomputation of noise if istat is zero
+ * Jan 23 2002	Set scale flag if BSCALE and BZERO not used
+ * May 13 2002	Fix bugs found by lint
+ *
+ * Jan 23 2003	Add setminpmqual() to setparm() for USNO-B1.0
+ * Jan 29 2003	Add setminid() to setparm() for USNO-B1.0
+ * Apr  3 2003	Fix bug setting minll if bmin is less than 0
+ * Jun  2 2003	Fix bug to setcale(0) if bscale == 1, not 0 (J-B Marquette)
+ *
+ * Aug  3 2004	Move single star image position rotation into rotstar()
+ * Aug  3 2004	Move daoread() declaration to wcscat.h
+ * Sep 24 2004	Fix rotstar() to separate output values from input values
+ *
+ * Mar 30 2006	Add nxydec=num. decimal places in image coordinates to setparm()
+ * Apr 25 2006	Change MINPEAK to mean counts above noise background
+ *              Suggested by Hill & Biddick for high background situations
+ * Jun 19 2006	Initialized uninitialized variables
+ * Oct 24 2006	Add reflection across horizontal as well as vertical axis
+ *
+ * Jan  8 2007	Include fitsfile.h instead of fitshead.h and imio.h
+ * Jan  8 2007	Drop unused variables
+ * Jan 10 2007	Include wcs.h
+ * Oct 19 2007	Fix pointers in trim section processing
+ */
diff --git a/Code/src/libwcs/fitsfile.c b/Code/src/libwcs/fitsfile.c
new file mode 100644
index 0000000000000000000000000000000000000000..658e2d2d3ab1b0ffe339b68bcd5fd8b03a627013
--- /dev/null
+++ b/Code/src/libwcs/fitsfile.c
@@ -0,0 +1,2325 @@
+/*** File libwcs/fitsfile.c
+ *** July 25, 2014
+ *** By Jessica Mink, jmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2014
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: jmink@cfa.harvard.edu
+           Postal address: Jessica Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:      fitsfile.c (FITS file reading and writing)
+ * Purpose:     Read and write FITS image and table files
+ * fitsropen (inpath)
+ *		Open a FITS file for reading, returning a FILE pointer
+ * fitsrhead (filename, lhead, nbhead)
+ *		Read FITS header and return it
+ * fitsrtail (filename, lhead, nbhead)
+ *		Read appended FITS header and return it
+ * fitsrsect (filename, nbhead, header, fd, x0, y0, nx, ny)
+ *		Read section of a FITS image, having already read the header
+ * fitsrimage (filename, nbhead, header)
+ *		Read FITS image, having already ready the header
+ * fitsrfull (filename, nbhead, header)
+ *		Read a FITS image of any dimension
+ * fitsrtopen (inpath, nk, kw, nrows, nchar, nbhead)
+ *		Open a FITS table file for reading; return header information
+ * fitsrthead (header, nk, kw, nrows, nchar, nbhead)
+ *		Extract FITS table information from a FITS header
+ * fitsrtline (fd, nbhead, lbuff, tbuff, irow, nbline, line)
+ *		Read next line of FITS table file
+ * ftgetr8 (entry, kw)
+ *		Extract column from FITS table line as double
+ * ftgetr4 (entry, kw)
+ *		Extract column from FITS table line as float
+ * ftgeti4 (entry, kw)
+ *		Extract column from FITS table line as int
+ * ftgeti2 (entry, kw)
+ *		Extract column from FITS table line as short
+ * ftgetc (entry, kw, string, maxchar)
+ *		Extract column from FITS table line as a character string
+ * fitswimage (filename, header, image)
+ *		Write FITS header and image
+ * fitswext (filename, header, image)
+ *		Write FITS header and image as extension to existing FITS file
+ * fitswhdu (fd, filename, header, image)
+ *		Write FITS header and image as extension to file descriptor
+ * fitscimage (filename, header, filename0)
+ *		Write FITS header and copy FITS image
+ * fitswhead (filename, header)
+ *		Write FITS header and keep file open for further writing
+ * fitswexhead (filename, header)
+ *		Write FITS header only to FITS extension without writing data
+ * isfits (filename)
+ *		Return 1 if file is a FITS file, else 0
+ * fitsheadsize (header)
+ *  		Return size of FITS header in bytes
+ */
+
+#include <stdlib.h>
+#ifndef VMS
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <string.h>
+#include "fitsfile.h"
+
+static int verbose=0;		/* Print diagnostics */
+static char fitserrmsg[80];
+static int fitsinherit = 1;	/* Append primary header to extension header */
+void
+setfitsinherit (inh)
+int inh;
+{fitsinherit = inh; return;}
+
+static off_t ibhead = 0;	/* Number of bytes read before header starts */
+
+off_t
+getfitsskip()
+{return (ibhead);}
+
+/* FITSRHEAD -- Read a FITS header */
+
+char *
+fitsrhead (filename, lhead, nbhead)
+
+char	*filename;	/* Name of FITS image file */
+int	*lhead;		/* Allocated length of FITS header in bytes (returned) */
+int	*nbhead;	/* Number of bytes before start of data (returned) */
+            /* This includes all skipped image extensions */
+
+{
+    int fd;
+    char *header;	/* FITS image header (filled) */
+    int extend;
+    int nbytes,naxis, i;
+    int ntry,nbr,irec,nrec, nbh, ipos, npos, nbprim, lprim, lext;
+    int nax1, nax2, nax3, nax4, nbpix, ibpix, nblock, nbskip;
+    char fitsbuf[2884];
+    char *headend;	/* Pointer to last line of header */
+    char *headnext;	/* Pointer to next line of header to be added */
+    int hdu;		/* header/data unit counter */
+    int extnum;		/* desired header data number
+               (0=primary -1=first with data -2=use EXTNAME) */
+    char extname[32];	/* FITS extension name */
+    char extnam[32];	/* Desired FITS extension name */
+    char *ext;		/* FITS extension name or number in header, if any */
+    char *pheader;	/* Primary header (naxis is 0) */
+    char cext = 0;
+    char *rbrac;	/* Pointer to right bracket if present in file name */
+    char *mwcs;		/* Pointer to WCS name separated by % */
+    char *newhead;	/* New larger header */
+    int nbh0;		/* Length of old too small header */
+    char *pheadend;
+    int inherit = 1;	/* Value of INHERIT keyword in FITS extension header */
+    int extfound = 0;	/* Set to one if desired FITS extension is found */
+    int npcount;
+
+    pheader = NULL;
+    lprim = 0;
+    header = NULL;
+
+    /* Check for FITS WCS specification and ignore for file opening */
+    mwcs = strchr (filename, '%');
+    if (mwcs != NULL)
+    *mwcs = (char) 0;
+
+    /* Check for FITS extension and ignore for file opening */
+    rbrac = NULL;
+    ext = strchr (filename, ',');
+    if (ext == NULL) {
+    ext = strchr (filename, '[');
+    if (ext != NULL) {
+        rbrac = strchr (filename, ']');
+        if (rbrac != NULL)
+        *rbrac = (char) 0;
+        }
+    }
+    if (ext != NULL) {
+    cext = *ext;
+    *ext = (char) 0;
+    }
+
+    /* Open the image file and read the header */
+    if (strncasecmp (filename,"stdin",5)) {
+    fd = -1;
+    fd = fitsropen (filename);
+    }
+#ifndef VMS
+    else {
+    fd = STDIN_FILENO;
+    extnum = -1;
+    }
+#endif
+
+    if (ext != NULL) {
+    if (isnum (ext+1))
+        extnum = atoi (ext+1);
+    else {
+        extnum = -2;
+        strcpy (extnam, ext+1);
+        }
+    }
+    else
+    extnum = -1;
+
+    /* Repair the damage done to the file-name string during parsing */
+    if (ext != NULL)
+    *ext = cext;
+    if (rbrac != NULL)
+    *rbrac = ']';
+    if (mwcs != NULL)
+    *mwcs = '%';
+
+    if (fd < 0) {
+    fprintf (stderr,"FITSRHEAD:  cannot read file %s\n", filename);
+    return (NULL);
+    }
+
+    nbytes = FITSBLOCK;
+    *nbhead = 0;
+    headend = NULL;
+    nbh = FITSBLOCK * 20 + 4;
+    header = (char *) calloc ((unsigned int) nbh, 1);
+    (void) hlength (header, nbh);
+    headnext = header;
+    nrec = 1;
+    hdu = 0;
+    ibhead = 0;
+
+    /* Read FITS header from input file one FITS block at a time */
+    irec = 0;
+    ibhead = 0;
+    while (irec < 500) {
+    nbytes = FITSBLOCK;
+    for (ntry = 0; ntry < 10; ntry++) {
+        for (i = 0; i < 2884; i++) fitsbuf[i] = 0;
+        nbr = read (fd, fitsbuf, nbytes);
+        if (verbose)
+        fprintf (stderr,"FITSRHEAD: %d header bytes read\n",nbr);
+
+        /* Short records allowed only if they have the last header line */
+        if (nbr < nbytes) {
+        headend = ksearch (fitsbuf,"END");
+        if (headend == NULL) {
+            if (ntry < 9) {
+            if (verbose)
+                fprintf (stderr,"FITSRHEAD: %d / %d bytes read %d\n",
+                     nbr,nbytes,ntry);
+            }
+            else {
+            snprintf(fitserrmsg,79,"FITSRHEAD: '%d / %d bytes of header read from %s\n"
+                ,nbr,nbytes,filename);
+#ifndef VMS
+            if (fd != STDIN_FILENO)
+#endif
+                (void)close (fd);
+            free (header);
+            /* if (pheader != NULL)
+                return (pheader); */
+                if (extnum != -1 && !extfound) {
+                *ext = (char) 0;
+                if (extnum < 0) {
+                    snprintf (fitserrmsg,79,
+                    "FITSRHEAD: Extension %s not found in file %s",
+                    extnam, filename);
+                }
+                else {
+                    snprintf (fitserrmsg,79,
+                    "FITSRHEAD: Extension %d not found in file %s",
+                    extnum, filename);
+                }
+                *ext = cext;
+                }
+            else if (hdu > 0) {
+                    snprintf (fitserrmsg,79,
+                "FITSRHEAD: No extensions found in file %s", filename);
+                hdu = 0;
+                if (pheader != NULL) {
+                *lhead = nbprim;
+                *nbhead = nbprim;
+                return (pheader);
+                }
+                break;
+                }
+            else {
+                    snprintf (fitserrmsg,79,
+                "FITSRHEAD: No header found in file %s", filename);
+                }
+            return (NULL);
+            }
+            }
+        else
+            break;
+        }
+        else
+        break;
+        }
+
+    /* Replace control characters and nulls with spaces */
+    for (i = 0; i < 2880; i++)
+        if (fitsbuf[i] < 32 || i > nbr) fitsbuf[i] = 32;
+    if (nbr < 2880)
+        nbr = 2880;
+
+    /* Move current FITS record into header string */
+    strncpy (headnext, fitsbuf, nbr);
+    *nbhead = *nbhead + nbr;
+    nrec = nrec + 1;
+    *(headnext+nbr+1) = 0;
+    ibhead = ibhead + 2880;
+    if (verbose)
+        fprintf (stderr,"FITSRHEAD: %d bytes in header\n",ibhead);
+
+    /* Check to see if this is the final record in this header */
+    headend = ksearch (fitsbuf,"END");
+    if (headend == NULL) {
+
+        /* Double size of header buffer if too small */
+        if (nrec * FITSBLOCK > nbh) {
+        nbh0 = nbh - 4;
+        nbh = (nrec * 2 * FITSBLOCK) + 4;
+        newhead = (char *) calloc (1,(unsigned int) nbh);
+        if (newhead) {
+            for (i = 0; i < nbh0; i++)
+            newhead[i] = header[i];
+            free (header);
+            newhead[nbh-3] = (char) 0;
+            header = newhead;
+            (void) hlength (header, nbh);
+            headnext = header + ((nrec-1) * FITSBLOCK);
+            }
+        else {
+            fprintf (stderr,"FITSRHEAD: %d bytes cannot be allocated for header\n",nbh);
+            exit (1);
+            }
+        }
+        else
+        headnext = headnext + FITSBLOCK;
+        }
+
+    else {
+        naxis = 0;
+        hgeti4 (header,"NAXIS",&naxis);
+
+        /* If header has no data, save it for appending to desired header */
+        if (naxis < 1) {
+        nbprim = nrec * FITSBLOCK;
+        headend = ksearch (header,"END");
+        lprim = headend + 80 - header;
+        pheader = (char *) calloc ((unsigned int) nbprim, 1);
+        for (i = 0; i < lprim; i++)
+            pheader[i] = header[i];
+        for (i = lprim; i < nbprim; i++)
+            pheader[i] = ' ';
+        }
+
+        /* If header has no data, start with the next record */
+        if (naxis < 1 && extnum == -1) {
+        extend = 0;
+        hgetl (header,"EXTEND",&extend);
+        if (naxis == 0 && extend) {
+            headnext = header;
+            *headend = ' ';
+            headend = NULL;
+            nrec = 1;
+            hdu = hdu + 1;
+            }
+        else {
+            break;
+            }
+        }
+
+        /* If this is the desired header data unit, keep it */
+        else if (extnum != -1) {
+        if (extnum > -1 && hdu == extnum) {
+            extfound = 1;
+            break;
+            }
+        else if (extnum < 0) {
+            extname[0] = 0;
+            hgets (header, "EXTNAME", 32, extname);
+            if (!strcmp (extnam,extname)) {
+            extfound = 1;
+            break;
+            }
+            }
+
+        /* If this is not desired header data unit, skip over data */
+        hdu = hdu + 1;
+        nblock = 0;
+        ibhead = 0;
+        if (naxis > 0) {
+            ibpix = 0;
+            hgeti4 (header,"BITPIX",&ibpix);
+            if (ibpix < 0) {
+            nbpix = -ibpix / 8;
+            }
+            else {
+            nbpix = ibpix / 8;
+            }
+            nax1 = 1;
+            hgeti4 (header,"NAXIS1",&nax1);
+            nax2 = 1;
+            if (naxis > 1) {
+            hgeti4 (header,"NAXIS2",&nax2);
+            }
+            nax3 = 1;
+            if (naxis > 2) {
+            hgeti4 (header,"NAXIS3",&nax3);
+            }
+            nax4 = 1;
+            if (naxis > 3) {
+            hgeti4 (header,"NAXIS4",&nax4);
+            }
+            nbskip = nax1 * nax2 * nax3 * nax4 * nbpix;
+            nblock = nbskip / 2880;
+            if (nblock*2880 < nbskip) {
+            nblock = nblock + 1;
+            }
+            npcount = 0;
+            hgeti4 (header,"PCOUNT", &npcount);
+            if (npcount > 0) {
+            nbskip = nbskip + npcount;
+            nblock = nbskip / 2880;
+            if (nblock*2880 < nbskip)
+                nblock = nblock + 1;
+            }
+            }
+        else {
+            nblock = 0;
+            }
+        *nbhead = *nbhead + (nblock * 2880);
+
+        /* Set file pointer to beginning of next header/data unit */
+        if (nblock > 0) {
+#ifndef VMS
+            if (fd != STDIN_FILENO) {
+            ipos = lseek (fd, *nbhead, SEEK_SET);
+            npos = *nbhead;
+            }
+            else {
+#else
+            {
+#endif
+            ipos = 0;
+            for (i = 0; i < nblock; i++) {
+                nbytes = FITSBLOCK;
+                nbr = read (fd, fitsbuf, nbytes);
+                if (nbr < nbytes) {
+                ipos = ipos + nbr;
+                break;
+                }
+                else {
+                ipos = ipos + nbytes;
+                }
+                }
+            npos = nblock * 2880;
+            }
+            if (ipos < npos) {
+            snprintf (fitserrmsg,79,"FITSRHEAD: %d / %d bytes skipped\n",
+                 ipos,npos);
+            extfound = 0;
+            break;
+            }
+            }
+        headnext = header;
+        headend = NULL;
+        nrec = 1;
+        }
+        else {
+        break;
+        }
+        }
+    }
+
+#ifndef VMS
+    if (fd != STDIN_FILENO)
+    (void)close (fd);
+#endif
+
+/* Print error message and return null if extension not found */
+    if (extnum != -1 && !extfound) {
+    if (extnum < 0)
+        fprintf (stderr, "FITSRHEAD: Extension %s not found in file %s\n",extnam, filename);
+    else
+        fprintf (stderr, "FITSRHEAD: Extension %d not found in file %s\n",extnum, filename);
+    if (pheader != NULL) {
+        free (pheader);
+        pheader = NULL;
+        }
+    return (NULL);
+    }
+
+    /* Allocate an extra block for good measure */
+    *lhead = (nrec + 1) * FITSBLOCK;
+    if (*lhead > nbh) {
+    newhead = (char *) calloc (1,(unsigned int) *lhead);
+    for (i = 0; i < nbh; i++)
+        newhead[i] = header[i];
+    free (header);
+    header = newhead;
+    (void) hlength (header, *lhead);
+    }
+    else
+    *lhead = nbh;
+
+    /* If INHERIT keyword is FALSE, never append primary header */
+    if (hgetl (header, "INHERIT", &inherit)) {
+    if (!inherit && fitsinherit)
+        fitsinherit = 0;
+    }
+
+    /* Append primary data header to extension header */
+    if (pheader != NULL && extnum != 0 && fitsinherit && hdu > 0) {
+    extname[0] = 0;
+    hgets (header, "XTENSION", 32, extname);
+    if (!strcmp (extname,"IMAGE")) {
+        strncpy (header, "SIMPLE  ", 8);
+        hputl (header, "SIMPLE", 1);
+        }
+    headend = blsearch (header,"END");
+    if (headend == NULL)
+        headend = ksearch (header, "END");
+    lext = headend - header;
+
+    /* Update primary header for inclusion at end of extension header */
+    hchange (pheader, "SIMPLE", "ROOTHEAD");
+    hchange (pheader, "NEXTEND", "NUMEXT");
+    hdel (pheader, "BITPIX");
+    hdel (pheader, "NAXIS");
+    hdel (pheader, "EXTEND");
+    hputl (pheader, "ROOTEND",1);
+    pheadend = ksearch (pheader,"END");
+    lprim = pheadend + 320 - pheader;
+    if (lext + lprim > nbh) {
+        nrec = (lext + lprim) / FITSBLOCK;
+        if (FITSBLOCK*nrec < lext+lprim)
+        nrec = nrec + 1;
+        *lhead = (nrec+1) * FITSBLOCK;
+        newhead = (char *) calloc (1,(unsigned int) *lhead);
+        for (i = 0; i < nbh; i++)
+        newhead[i] = header[i];
+        free (header);
+        header = newhead;
+        headend = header + lext;
+        (void) hlength (header, *lhead);
+        }
+    hputs (header,"COMMENT","-------------------------------------------");
+    hputs (header,"COMMENT","Information from Primary Header");
+    hputs (header,"COMMENT","-------------------------------------------");
+    headend = blsearch (header,"END");
+    if (headend == NULL)
+        headend = ksearch (header, "END");
+    pheader[lprim] = 0;
+    strncpy (headend, pheader, lprim);
+    if (pheader != NULL) {
+        free (pheader);
+        pheader = NULL;
+        }
+    }
+
+    ibhead = *nbhead - ibhead;
+
+    return (header);
+}
+
+
+/* FITSRTAIL -- Read FITS header appended to graphics file */
+
+char *
+fitsrtail (filename, lhead, nbhead)
+
+char	*filename;	/* Name of image file */
+int	*lhead;		/* Allocated length of FITS header in bytes (returned) */
+int	*nbhead;	/* Number of bytes before start of data (returned) */
+            /* This includes all skipped image extensions */
+
+{
+    int fd;
+    char *header;	/* FITS image header (filled) */
+    int nbytes, i, ndiff;
+    int nbr, irec;
+    off_t offset;
+    char *mwcs;		/* Pointer to WCS name separated by % */
+    char *headstart;
+    char *newhead;
+
+    header = NULL;
+
+    /* Check for FITS WCS specification and ignore for file opening */
+    mwcs = strchr (filename, '%');
+    if (mwcs != NULL)
+    *mwcs = (char) 0;
+
+    /* Open the image file and read the header */
+    if (strncasecmp (filename,"stdin",5)) {
+    fd = -1;
+    fd = fitsropen (filename);
+    }
+#ifndef VMS
+    else {
+    fd = STDIN_FILENO;
+    }
+#endif
+
+    /* Repair the damage done to the file-name string during parsing */
+    if (mwcs != NULL)
+    *mwcs = '%';
+
+    if (fd < 0) {
+    fprintf (stderr,"FITSRTAIL:  cannot read file %s\n", filename);
+    return (NULL);
+    }
+
+    nbytes = FITSBLOCK;
+    *nbhead = 0;
+    *lhead = 0;
+
+    /* Read FITS header from end of input file one FITS block at a time */
+    irec = 0;
+    while (irec < 100) {
+    nbytes = FITSBLOCK * (irec + 2);
+    header = (char *) calloc ((unsigned int) nbytes, 1);
+    offset = lseek (fd, -nbytes, SEEK_END);
+    if (offset < 0) {
+        free (header);
+        header = NULL;
+        nbytes = 0;
+        break;
+        }
+    for (i = 0; i < nbytes; i++) header[i] = 0;
+    nbr = read (fd, header, nbytes);
+
+    /* Check for SIMPLE at start of header */
+    for (i = 0; i < nbr; i++)
+        if (header[i] < 32) header[i] = 32;
+    if ((headstart = ksearch (header,"SIMPLE"))) {
+        if (headstart != header) {
+        ndiff = headstart - header;
+        newhead = (char *) calloc ((unsigned int) nbytes, 1);
+        for (i = 0; i < nbytes-ndiff; i++)
+            newhead[i] = headstart[i];
+        free (header);
+        header = newhead;
+        }
+        *lhead = nbytes;
+        *nbhead = nbytes;
+        break;
+        }
+    free (header);
+    }
+    (void) hlength (header, nbytes);
+
+#ifndef VMS
+    if (fd != STDIN_FILENO)
+    (void)close (fd);
+#endif
+
+    return (header);
+}
+
+
+/* FITSRSECT -- Read a piece of a FITS image, having already read the header */
+
+char *
+fitsrsect (filename, header, nbhead, x0, y0, nx, ny, nlog)
+
+char	*filename;	/* Name of FITS image file */
+char	*header;	/* FITS header for image (previously read) */
+int	nbhead;		/* Actual length of image header(s) in bytes */
+int	x0, y0;		/* FITS image coordinate of first pixel */
+int	nx;		/* Number of columns to read (less than NAXIS1) */
+int	ny;		/* Number of rows to read (less than NAXIS2) */
+int	nlog;		/* Note progress mod this rows */
+{
+    int fd;		/* File descriptor */
+    int nbimage, naxis1, naxis2, bytepix, nbread;
+    int bitpix, naxis, nblocks, nbytes, nbr;
+    int x1, y1, nbline, nyleft;
+    off_t impos, nblin;
+    char *image, *imline, *imlast;
+    int ilog = 0;
+    int row;
+
+    /* Open the image file and read the header */
+    if (strncasecmp (filename,"stdin", 5)) {
+    fd = -1;
+
+    fd = fitsropen (filename);
+    if (fd < 0) {
+        snprintf (fitserrmsg,79, "FITSRSECT:  cannot read file %s\n", filename);
+        return (NULL);
+        }
+
+    /* Skip over FITS header and whatever else needs to be skipped */
+    if (lseek (fd, nbhead, SEEK_SET) < 0) {
+        (void)close (fd);
+        snprintf (fitserrmsg,79, "FITSRSECT:  cannot skip header of file %s\n",
+             filename);
+        return (NULL);
+        }
+    }
+#ifndef VMS
+    else
+    fd = STDIN_FILENO;
+#endif
+
+    /* Compute size of image in bytes using relevant header parameters */
+    naxis = 1;
+    hgeti4 (header,"NAXIS",&naxis);
+    naxis1 = 1;
+    hgeti4 (header,"NAXIS1",&naxis1);
+    naxis2 = 1;
+    hgeti4 (header,"NAXIS2",&naxis2);
+    bitpix = 0;
+    hgeti4 (header,"BITPIX",&bitpix);
+    if (bitpix == 0) {
+    /* snprintf (fitserrmsg,79, "FITSRSECT:  BITPIX is 0; image not read\n"); */
+    (void)close (fd);
+    return (NULL);
+    }
+    bytepix = bitpix / 8;
+    if (bytepix < 0) bytepix = -bytepix;
+
+    /* Keep X coordinates within image limits */
+    if (x0 < 1)
+    x0 = 1;
+    else if (x0 > naxis1)
+    x0 = naxis1;
+    x1 = x0 + nx - 1;
+    if (x1 < 1)
+    x1 = 1;
+    else if (x1 > naxis1)
+    x1 = naxis1;
+    nx = x1 - x0 + 1;
+
+    /* Keep Y coordinates within image limits */
+    if (y0 < 1)
+    y0 = 1;
+    else if (y0 > naxis2)
+    y0 = naxis2;
+    y1 = y0 + ny - 1;
+    if (y1 < 1)
+    y1 = 1;
+    else if (y1 > naxis2)
+    y1 = naxis2;
+    ny = y1 - y0 + 1;
+
+    /* Number of bytes in output image */
+    nbline = nx * bytepix;
+    nbimage = nbline * ny;
+
+    /* Set number of bytes to integral number of 2880-byte blocks */
+    nblocks = nbimage / FITSBLOCK;
+    if (nblocks * FITSBLOCK < nbimage)
+    nblocks = nblocks + 1;
+    nbytes = nblocks * FITSBLOCK;
+
+    /* Allocate image section to be read */
+    image = (char *) malloc (nbytes);
+    nyleft = ny;
+    imline = image;
+    nbr = 0;
+
+    /* Computer pointer to first byte of input image to read */
+    nblin = naxis1 * bytepix;
+    impos = ((y0 - 1) * nblin) + ((x0 - 1) * bytepix);
+    row = y0 - 1;
+
+    /* Read image section one line at a time */
+    while (nyleft-- > 0) {
+    if (lseek (fd, impos, SEEK_CUR) >= 0) {
+        nbread = read (fd, imline, nbline);
+        nbr = nbr + nbread;
+        impos = nblin - nbread;
+        imline = imline + nbline;
+        row++;
+        if (++ilog == nlog) {
+        ilog = 0;
+        fprintf (stderr, "Row %5d extracted   ", row);
+                (void) putc (13,stderr);
+        }
+        }
+    }
+    if (nlog)
+    fprintf (stderr, "\n");
+
+    /* Fill rest of image with zeroes */
+    imline = image + nbimage;
+    imlast = image + nbytes;
+    while (imline++ < imlast)
+    *imline = (char) 0;
+
+    /* Byte-reverse image, if necessary */
+    if (imswapped ())
+    imswap (bitpix, image, nbytes);
+
+    return (image);
+}
+
+
+/* FITSRIMAGE -- Read a FITS image */
+
+char *
+fitsrimage (filename, nbhead, header)
+
+char	*filename;	/* Name of FITS image file */
+int	nbhead;		/* Actual length of image header(s) in bytes */
+char	*header;	/* FITS header for image (previously read) */
+{
+    int fd;
+    int nbimage, naxis1, naxis2, bytepix, nbread;
+    int bitpix, naxis, nblocks, nbytes, nbleft, nbr;
+    int simple;
+    char *image, *imleft;
+
+    /* Open the image file and read the header */
+    if (strncasecmp (filename,"stdin", 5)) {
+    fd = -1;
+
+    fd = fitsropen (filename);
+    if (fd < 0) {
+        snprintf (fitserrmsg,79, "FITSRIMAGE:  cannot read file %s\n", filename);
+        return (NULL);
+        }
+
+    /* Skip over FITS header and whatever else needs to be skipped */
+    if (lseek (fd, nbhead, SEEK_SET) < 0) {
+        (void)close (fd);
+        snprintf (fitserrmsg,79, "FITSRIMAGE:  cannot skip header of file %s\n",
+             filename);
+        return (NULL);
+        }
+    }
+#ifndef VMS
+    else
+    fd = STDIN_FILENO;
+#endif
+
+    /* If SIMPLE=F in header, simply put post-header part of file in buffer */
+    hgetl (header, "SIMPLE", &simple);
+    if (!simple) {
+    nbytes = getfilesize (filename) - nbhead;
+    if ((image = (char *) malloc (nbytes + 1)) == NULL) {
+        /* snprintf (fitserrmsg,79, "FITSRIMAGE:  %d-byte image buffer cannot be allocated\n"); */
+        (void)close (fd);
+        return (NULL);
+        }
+    hputi4 (header, "NBDATA", nbytes);
+    nbread = read (fd, image, nbytes);
+    return (image);
+    }
+
+    /* Compute size of image in bytes using relevant header parameters */
+    naxis = 1;
+    hgeti4 (header,"NAXIS",&naxis);
+    naxis1 = 1;
+    hgeti4 (header,"NAXIS1",&naxis1);
+    naxis2 = 1;
+    hgeti4 (header,"NAXIS2",&naxis2);
+    bitpix = 0;
+    hgeti4 (header,"BITPIX",&bitpix);
+    if (bitpix == 0) {
+    /* snprintf (fitserrmsg,79, "FITSRIMAGE:  BITPIX is 0; image not read\n"); */
+    (void)close (fd);
+    return (NULL);
+    }
+    bytepix = bitpix / 8;
+    if (bytepix < 0) bytepix = -bytepix;
+
+    /* If either dimension is one and image is 3-D, read all three dimensions */
+    if (naxis == 3 && (naxis1 ==1 || naxis2 == 1)) {
+    int naxis3;
+    hgeti4 (header,"NAXIS3",&naxis3);
+    nbimage = naxis1 * naxis2 * naxis3 * bytepix;
+    }
+    else
+    nbimage = naxis1 * naxis2 * bytepix;
+
+    /* Set number of bytes to integral number of 2880-byte blocks */
+    nblocks = nbimage / FITSBLOCK;
+    if (nblocks * FITSBLOCK < nbimage)
+    nblocks = nblocks + 1;
+    nbytes = nblocks * FITSBLOCK;
+
+    /* Allocate and read image */
+    image = (char *) malloc (nbytes);
+    nbleft = nbytes;
+    imleft = image;
+    nbr = 0;
+    while (nbleft > 0) {
+    nbread = read (fd, imleft, nbleft);
+    nbr = nbr + nbread;
+#ifndef VMS
+    if (fd == STDIN_FILENO && nbread < nbleft && nbread > 0) {
+        nbleft = nbleft - nbread;
+        imleft = imleft + nbread;
+        }
+    else
+#endif
+        nbleft = 0;
+    }
+#ifndef VMS
+    if (fd != STDIN_FILENO)
+    (void)close (fd);
+#endif
+    if (nbr < nbimage) {
+    snprintf (fitserrmsg,79, "FITSRIMAGE:  %d of %d bytes read from file %s\n",
+         nbr, nbimage, filename);
+    return (NULL);
+    }
+
+    /* Byte-reverse image, if necessary */
+    if (imswapped ())
+    imswap (bitpix, image, nbytes);
+
+    return (image);
+}
+
+
+/* FITSRFULL -- Read a FITS image of any dimension */
+
+char *
+fitsrfull (filename, nbhead, header)
+
+char	*filename;	/* Name of FITS image file */
+int	nbhead;		/* Actual length of image header(s) in bytes */
+char	*header;	/* FITS header for image (previously read) */
+{
+    int fd;
+    int nbimage, naxisi, iaxis, bytepix, nbread;
+    int bitpix, naxis, nblocks, nbytes, nbleft, nbr, simple;
+    char keyword[16];
+    char *image, *imleft;
+
+    /* Open the image file and read the header */
+    if (strncasecmp (filename,"stdin", 5)) {
+    fd = -1;
+
+    fd = fitsropen (filename);
+    if (fd < 0) {
+        snprintf (fitserrmsg,79, "FITSRFULL:  cannot read file %s\n", filename);
+        return (NULL);
+        }
+
+    /* Skip over FITS header and whatever else needs to be skipped */
+    if (lseek (fd, nbhead, SEEK_SET) < 0) {
+        (void)close (fd);
+        snprintf (fitserrmsg,79, "FITSRFULL:  cannot skip header of file %s\n",
+             filename);
+        return (NULL);
+        }
+    }
+#ifndef VMS
+    else
+    fd = STDIN_FILENO;
+#endif
+
+    /* If SIMPLE=F in header, simply put post-header part of file in buffer */
+    hgetl (header, "SIMPLE", &simple);
+    if (!simple) {
+    nbytes = getfilesize (filename) - nbhead;
+    if ((image = (char *) malloc (nbytes + 1)) == NULL) {
+        snprintf (fitserrmsg,79, "FITSRFULL:  %d-byte image buffer cannot be allocated\n",nbytes+1);
+        (void)close (fd);
+        return (NULL);
+        }
+    hputi4 (header, "NBDATA", nbytes);
+    nbread = read (fd, image, nbytes);
+    return (image);
+    }
+
+    /* Find number of bytes per pixel */
+    bitpix = 0;
+    hgeti4 (header,"BITPIX",&bitpix);
+    if (bitpix == 0) {
+    snprintf (fitserrmsg,79, "FITSRFULL:  BITPIX is 0; image not read\n");
+    (void)close (fd);
+    return (NULL);
+    }
+    bytepix = bitpix / 8;
+    if (bytepix < 0) bytepix = -bytepix;
+    nbimage = bytepix;
+
+    /* Compute size of image in bytes using relevant header parameters */
+    naxis = 1;
+    hgeti4 (header,"NAXIS",&naxis);
+    for (iaxis = 1; iaxis <= naxis; iaxis++) {
+    sprintf (keyword, "NAXIS%d", iaxis);
+    naxisi = 1;
+    hgeti4 (header,keyword,&naxisi);
+    nbimage = nbimage * naxisi;
+    }
+
+    /* Set number of bytes to integral number of 2880-byte blocks */
+    nblocks = nbimage / FITSBLOCK;
+    if (nblocks * FITSBLOCK < nbimage)
+    nblocks = nblocks + 1;
+    nbytes = nblocks * FITSBLOCK;
+
+    /* Allocate and read image */
+    image = (char *) malloc (nbytes);
+    nbleft = nbytes;
+    imleft = image;
+    nbr = 0;
+    while (nbleft > 0) {
+    nbread = read (fd, imleft, nbleft);
+    nbr = nbr + nbread;
+#ifndef VMS
+    if (fd == STDIN_FILENO && nbread < nbleft && nbread > 0) {
+        nbleft = nbleft - nbread;
+        imleft = imleft + nbread;
+        }
+    else
+#endif
+        nbleft = 0;
+    }
+#ifndef VMS
+    if (fd != STDIN_FILENO)
+    (void)close (fd);
+#endif
+    if (nbr < nbimage) {
+    snprintf (fitserrmsg,79, "FITSRFULL:  %d of %d image bytes read from file %s\n",
+         nbr, nbimage, filename);
+    return (NULL);
+    }
+
+    /* Byte-reverse image, if necessary */
+    if (imswapped ())
+    imswap (bitpix, image, nbytes);
+
+    return (image);
+}
+
+
+/* FITSROPEN -- Open a FITS file, returning the file descriptor */
+
+int
+fitsropen (inpath)
+
+char	*inpath;	/* Pathname for FITS tables file to read */
+
+{
+    int ntry;
+    int fd;		/* file descriptor for FITS tables file (returned) */
+    char *ext;		/* extension name or number */
+    char cext = 0;
+    char *rbrac;
+    char *mwcs;		/* Pointer to WCS name separated by % */
+
+/* Check for FITS WCS specification and ignore for file opening */
+    mwcs = strchr (inpath, '%');
+
+/* Check for FITS extension and ignore for file opening */
+    ext = strchr (inpath, ',');
+    rbrac = NULL;
+    if (ext == NULL) {
+    ext = strchr (inpath, '[');
+    if (ext != NULL) {
+        rbrac = strchr (inpath, ']');
+        }
+    }
+
+/* Open input file */
+    for (ntry = 0; ntry < 3; ntry++) {
+    if (ext != NULL) {
+        cext = *ext;
+        *ext = 0;
+        }
+    if (rbrac != NULL)
+        *rbrac = (char) 0;
+    if (mwcs != NULL)
+        *mwcs = (char) 0;
+    fd = open (inpath, O_RDONLY);
+    if (ext != NULL)
+        *ext = cext;
+    if (rbrac != NULL)
+        *rbrac = ']';
+    if (mwcs != NULL)
+        *mwcs = '%';
+    if (fd >= 0)
+        break;
+    else if (ntry == 2) {
+        snprintf (fitserrmsg,79, "FITSROPEN:  cannot read file %s\n", inpath);
+        return (-1);
+        }
+    }
+
+    if (verbose)
+    fprintf (stderr,"FITSROPEN:  input file %s opened\n",inpath);
+
+    return (fd);
+}
+
+
+static int offset1=0;
+static int offset2=0;
+
+/* FITSRTOPEN -- Open FITS table file and fill structure with
+ *		 pointers to selected keywords
+ *		 Return file descriptor (-1 if unsuccessful)
+ */
+
+int
+fitsrtopen (inpath, nk, kw, nrows, nchar, nbhead)
+
+char	*inpath;	/* Pathname for FITS tables file to read */
+int	*nk;		/* Number of keywords to use */
+struct Keyword	**kw;	/* Structure for desired entries */
+int	*nrows;		/* Number of rows in table (returned) */
+int	*nchar;		/* Number of characters in one table row (returned) */
+int	*nbhead;	/* Number of characters before table starts */
+
+{
+    char temp[16];
+    int fd;
+    int	lhead;		/* Maximum length in bytes of FITS header */
+    char *header;	/* Header for FITS tables file to read */
+
+/* Read FITS header from input file */
+    header = fitsrhead (inpath, &lhead, nbhead);
+    if (!header) {
+    snprintf (fitserrmsg,79,"FITSRTOPEN:  %s is not a FITS file\n",inpath);
+    return (0);
+    }
+
+/* Make sure this file is really a FITS table file */
+    temp[0] = 0;
+    (void) hgets (header,"XTENSION",16,temp);
+    if (strlen (temp) == 0) {
+    snprintf (fitserrmsg,79,
+          "FITSRTOPEN:  %s is not a FITS table file\n",inpath);
+    free ((void *) header);
+    return (0);
+    }
+
+/* If it is a FITS file, get table information from the header */
+    else if (!strcmp (temp, "TABLE") || !strcmp (temp, "BINTABLE")) {
+    if (fitsrthead (header, nk, kw, nrows, nchar)) {
+        snprintf (fitserrmsg,79,
+              "FITSRTOPEN: Cannot read FITS table from %s\n",inpath);
+        free ((void *) header);
+        return (-1);
+        }
+    else {
+        fd = fitsropen (inpath);
+        offset1 = 0;
+        offset2 = 0;
+        free ((void *) header);
+        return (fd);
+        }
+    }
+
+/* If it is another FITS extension note it and return */
+    else {
+    snprintf (fitserrmsg,79,
+          "FITSRTOPEN:  %s is a %s extension, not table\n",
+          inpath, temp);
+    free ((void *) header);
+    return (0);
+    }
+}
+
+static struct Keyword *pw;	/* Structure for all entries */
+static int *lpnam;		/* length of name for each field */
+static int bfields = 0;
+
+/* FITSRTHEAD -- From FITS table header, read pointers to selected keywords */
+
+int
+fitsrthead (header, nk, kw, nrows, nchar)
+
+char	*header;	/* Header for FITS tables file to read */
+int	*nk;		/* Number of keywords to use */
+struct Keyword	**kw;	/* Structure for desired entries */
+int	*nrows;		/* Number of rows in table (returned) */
+int	*nchar;		/* Number of characters in one table row (returned) */
+
+{
+    struct Keyword *rw;	/* Structure for desired entries */
+    int nfields;
+    int ifield, ik, i, ikf, ltform, kl;
+    char *h0, *h1, *tf1, *tf2;
+    char tname[12];
+    char temp[16];
+    char tform[16];
+    int tverb;
+    int bintable = 0;
+
+    h0 = header;
+
+/* Make sure this is really a FITS table file header */
+    temp[0] = 0;
+    hgets (header,"XTENSION",16,temp);
+    if (strlen (temp) == 0) {
+    snprintf (fitserrmsg,79, "FITSRTHEAD:  Not a FITS table header\n");
+    return (-1);
+    }
+    else if (!strcmp (temp, "BINTABLE")) {
+    bintable = 1;
+    }
+    else if (strcmp (temp, "TABLE")) {
+    snprintf (fitserrmsg,79, "FITSRTHEAD:  %s extension, not TABLE\n",temp);
+    return (-1);
+    }
+
+/* Get table size from FITS header */
+    *nchar = 0;
+    hgeti4 (header,"NAXIS1",nchar);
+    *nrows = 0;
+    hgeti4 (header,"NAXIS2", nrows);
+    if (*nrows <= 0 || *nchar <= 0) {
+    snprintf (fitserrmsg,79, "FITSRTHEAD: cannot read %d x %d table\n",
+         *nrows,*nchar);
+    return (-1);
+    }
+
+/* Set up table for access to individual fields */
+    nfields = 0;
+    hgeti4 (header,"TFIELDS",&nfields);
+    if (verbose)
+    fprintf (stderr, "FITSRTHEAD: %d fields per table entry\n", nfields);
+    if (nfields > bfields) {
+    if (bfields > 0)
+        free ((void *)pw);
+    pw = (struct Keyword *) calloc (nfields, sizeof(struct Keyword));
+    if (pw == NULL) {
+        snprintf (fitserrmsg,79,"FITSRTHEAD: cannot allocate table structure\n");
+        return (-1);
+        }
+    if (bfields > 0)
+        free ((void *)lpnam);
+    lpnam = (int *) calloc (nfields, sizeof(int));
+    if (lpnam == NULL) {
+        snprintf (fitserrmsg,79,"FITSRTHEAD: cannot allocate length structure\n");
+        return (-1);
+        }
+    bfields = nfields;
+    }
+
+    tverb = verbose;
+    verbose = 0;
+    ikf = 0;
+
+    for (ifield = 0; ifield < nfields; ifield++) {
+
+    /* Name of field */
+    for (i = 0; i < 12; i++) tname[i] = 0;
+    sprintf (tname, "TTYPE%d", ifield+1);;
+    temp[0] = 0;
+    h1 = ksearch (h0,tname);
+    h0 = h1;
+    hgets (h0,tname,16,temp);
+    strcpy (pw[ifield].kname,temp);
+    pw[ifield].lname = strlen (pw[ifield].kname);
+
+    /* Sequence of field on line */
+    pw[ifield].kn = ifield + 1;
+
+    /* First column of field */
+    if (bintable)
+        pw[ifield].kf = ikf;
+    else {
+        for (i = 0; i < 12; i++) tname[i] = 0;
+        sprintf (tname, "TBCOL%d", ifield+1);
+        pw[ifield].kf = 0;
+        hgeti4 (h0,tname, &pw[ifield].kf);
+        }
+
+    /* Length of field */
+    for (i = 0; i < 12; i++) tname[i] = 0;
+    sprintf (tname, "TFORM%d", ifield+1);;
+    tform[0] = 0;
+    hgets (h0,tname,16,tform);
+    strcpy (pw[ifield].kform, tform);
+    ltform = strlen (tform);
+    if (tform[ltform-1] == 'A') {
+        pw[ifield].kform[0] = 'A';
+        for (i = 0; i < ltform-1; i++)
+        pw[ifield].kform[i+1] = tform[i];
+        pw[ifield].kform[ltform] = (char) 0;
+        tf1 = pw[ifield].kform + 1;
+        kl = atof (tf1);
+        }
+    else if (!strcmp (tform,"I"))
+        kl = 2;
+    else if (!strcmp (tform, "J"))
+        kl = 4;
+    else if (!strcmp (tform, "E"))
+        kl = 4;
+    else if (!strcmp (tform, "D"))
+        kl = 8;
+    else {
+        tf1 = tform + 1;
+        tf2 = strchr (tform,'.');
+        if (tf2 != NULL)
+        *tf2 = ' ';
+        kl = atoi (tf1);
+        }
+    pw[ifield].kl = kl;
+    ikf = ikf + kl;
+    }
+
+/* Set up table for access to desired fields */
+    verbose = tverb;
+    if (verbose)
+    fprintf (stderr, "FITSRTHEAD: %d keywords read\n", *nk);
+
+/* If nk = 0, allocate and return structures for all table fields */
+    if (*nk <= 0) {
+    *kw = pw;
+    *nk = nfields;
+    return (0);
+    }
+    else
+    rw = *kw;
+
+/* Find each desired keyword in the header */
+    for (ik = 0; ik < *nk; ik++) {
+    if (rw[ik].kn <= 0) {
+        for (ifield = 0; ifield < nfields; ifield++) {
+        if (rw[ik].lname != pw[ifield].lname)
+            continue;
+        if (strcmp (pw[ifield].kname, rw[ik].kname) == 0) {
+            break;
+            }
+        }
+        }
+    else
+        ifield = rw[ik].kn - 1;
+
+/* Set pointer, lentth, and name in returned array of structures */
+    rw[ik].kn = ifield + 1;
+    rw[ik].kf = pw[ifield].kf - 1;
+    rw[ik].kl = pw[ifield].kl;
+    strcpy (rw[ik].kform, pw[ifield].kform);
+    strcpy (rw[ik].kname, pw[ifield].kname);
+    }
+
+    return (0);
+}
+
+
+int
+fitsrtline (fd, nbhead, lbuff, tbuff, irow, nbline, line)
+
+int	fd;		/* File descriptor for FITS file */
+int	nbhead;		/* Number of bytes in FITS header */
+int	lbuff;		/* Number of bytes in table buffer */
+char	*tbuff;		/* FITS table buffer */
+int	irow;		/* Number of table row to read */
+int	nbline;		/* Number of bytes to read for this line */
+char	*line;		/* One line of FITS table (returned) */
+
+{
+    int nbuff, nlbuff;
+    int nbr = 0;
+    int offset, offend, ntry, ioff;
+    char *tbuff1;
+
+    offset = nbhead + (nbline * irow);
+    offend = offset + nbline - 1;
+
+/* Read a new buffer of the FITS table into memory if needed */
+    if (offset < offset1 || offend > offset2) {
+    nlbuff = lbuff / nbline;
+    nbuff = nlbuff * nbline;
+    for (ntry = 0; ntry < 3; ntry++) {
+        ioff = lseek (fd, offset, SEEK_SET);
+        if (ioff < offset) {
+        if (ntry == 2)
+            return (0);
+        else
+            continue;
+        }
+        nbr = read (fd, tbuff, nbuff);
+        if (nbr < nbline) {
+        if (verbose)
+            fprintf (stderr, "FITSRTLINE: %d / %d bytes read %d\n",
+                nbr,nbuff,ntry);
+        if (ntry == 2)
+            return (nbr);
+        }
+        else
+        break;
+        }
+    offset1 = offset;
+    offset2 = offset + nbr - 1;
+    strncpy (line, tbuff, nbline);
+    return (nbline);
+    }
+    else {
+    tbuff1 = tbuff + (offset - offset1);
+    strncpy (line, tbuff1, nbline);
+    return (nbline);
+    }
+}
+
+
+void
+fitsrtlset ()
+{
+    offset1 = 0;
+    offset2 = 0;
+    return;
+}
+
+
+/* FTGETI2 -- Extract n'th column from FITS table line as short */
+
+short
+ftgeti2 (entry, kw)
+
+char	*entry;		/* Row or entry from table */
+struct Keyword *kw;	/* Table column information from FITS header */
+{
+    char temp[30];
+    short i;
+    int j;
+    float r;
+    double d;
+
+    if (ftgetc (entry, kw, temp, 30)) {
+    if (!strcmp (kw->kform, "I"))
+        moveb (temp, (char *) &i, 2, 0, 0);
+    else if (!strcmp (kw->kform, "J")) {
+        moveb (temp, (char *) &j, 4, 0, 0);
+        i = (short) j;
+        }
+    else if (!strcmp (kw->kform, "E")) {
+        moveb (temp, (char *) &r, 4, 0, 0);
+        i = (short) r;
+        }
+    else if (!strcmp (kw->kform, "D")) {
+        moveb (temp, (char *) &d, 8, 0, 0);
+        i = (short) d;
+        }
+    else
+        i = (short) atof (temp);
+    return (i);
+    }
+    else
+    return ((short) 0);
+}
+
+
+/* FTGETI4 -- Extract n'th column from FITS table line as int */
+
+int
+ftgeti4 (entry, kw)
+
+char	*entry;		/* Row or entry from table */
+struct Keyword *kw;	/* Table column information from FITS header */
+{
+    char temp[30];
+    short i;
+    int j;
+    float r;
+    double d;
+
+    if (ftgetc (entry, kw, temp, 30)) {
+    if (!strcmp (kw->kform, "I")) {
+        moveb (temp, (char *) &i, 2, 0, 0);
+        j = (int) i;
+        }
+    else if (!strcmp (kw->kform, "J"))
+        moveb (temp, (char *) &j, 4, 0, 0);
+    else if (!strcmp (kw->kform, "E")) {
+        moveb (temp, (char *) &r, 4, 0, 0);
+        j = (int) r;
+        }
+    else if (!strcmp (kw->kform, "D")) {
+        moveb (temp, (char *) &d, 8, 0, 0);
+        j = (int) d;
+        }
+    else
+        j = (int) atof (temp);
+    return (j);
+    }
+    else
+    return (0);
+}
+
+
+/* FTGETR4 -- Extract n'th column from FITS table line as float */
+
+float
+ftgetr4 (entry, kw)
+
+char	*entry;		/* Row or entry from table */
+struct Keyword *kw;	/* Table column information from FITS header */
+{
+    char temp[30];
+    short i;
+    int j;
+    float r;
+    double d;
+
+    if (ftgetc (entry, kw, temp, 30)) {
+    if (!strcmp (kw->kform, "I")) {
+        moveb (temp, (char *) &i, 2, 0, 0);
+        r = (float) i;
+        }
+    else if (!strcmp (kw->kform, "J")) {
+        moveb (temp, (char *) &j, 4, 0, 0);
+        r = (float) j;
+        }
+    else if (!strcmp (kw->kform, "E"))
+        moveb (temp, (char *) &r, 4, 0, 0);
+    else if (!strcmp (kw->kform, "D")) {
+        moveb (temp, (char *) &d, 8, 0, 0);
+        r = (float) d;
+        }
+    else
+        r = (float) atof (temp);
+    return (r);
+    }
+    else
+    return ((float) 0.0);
+}
+
+
+/* FTGETR8 -- Extract n'th column from FITS table line as double */
+
+double
+ftgetr8 (entry, kw)
+
+char	*entry;		/* Row or entry from table */
+struct Keyword *kw;	/* Table column information from FITS header */
+{
+    char temp[30];
+    short i;
+    int j;
+    float r;
+    double d;
+
+    if (ftgetc (entry, kw, temp, 30)) {
+    if (!strcmp (kw->kform, "I")) {
+        moveb (temp, (char *) &i, 2, 0, 0);
+        d = (double) i;
+        }
+    else if (!strcmp (kw->kform, "J")) {
+        moveb (temp, (char *) &j, 4, 0, 0);
+        d = (double) j;
+        }
+    else if (!strcmp (kw->kform, "E")) {
+        moveb (temp, (char *) &r, 4, 0, 0);
+        d = (double) r;
+        }
+    else if (!strcmp (kw->kform, "D"))
+        moveb (temp, (char *) &d, 8, 0, 0);
+    else
+        d = atof (temp);
+    return (d);
+    }
+    else
+    return ((double) 0.0);
+}
+
+
+/* FTGETC -- Extract n'th column from FITS table line as character string */
+
+int
+ftgetc (entry, kw, string, maxchar)
+
+char	*entry;		/* Row or entry from table */
+struct Keyword *kw;	/* Table column information from FITS header */
+char	*string;	/* Returned string */
+int	maxchar;	/* Maximum number of characters in returned string */
+{
+    int length = maxchar;
+
+    if (kw->kl < length)
+    length = kw->kl;
+    if (length > 0) {
+    strncpy (string, entry+kw->kf, length);
+    string[length] = 0;
+    return ( 1 );
+    }
+    else
+    return ( 0 );
+}
+
+extern int errno;
+
+
+/*FITSWIMAGE -- Write FITS header and image */
+
+int
+fitswimage (filename, header, image)
+
+char	*filename;	/* Name of FITS image file */
+char	*header;	/* FITS image header */
+char	*image;		/* FITS image pixels */
+
+{
+    int fd;
+
+    /* Open the output file */
+    if (strcasecmp (filename,"stdout") ) {
+
+    if (!access (filename, 0)) {
+        fd = open (filename, O_WRONLY);
+        if (fd < 3) {
+        snprintf (fitserrmsg,79, "FITSWIMAGE:  file %s not writeable\n", filename);
+        return (0);
+        }
+        }
+    else {
+        fd = open (filename, O_RDWR+O_CREAT, 0666);
+        if (fd < 3) {
+        snprintf (fitserrmsg,79, "FITSWIMAGE:  cannot create file %s\n", filename);
+        return (0);
+        }
+        }
+    }
+#ifndef VMS
+    else
+    fd = STDOUT_FILENO;
+#endif
+
+    return (fitswhdu (fd, filename, header, image));
+}
+
+
+/*FITSWEXT -- Write FITS header and image as extension to a file */
+
+int
+fitswext (filename, header, image)
+
+char	*filename;	/* Name of IFTS image file */
+char	*header;	/* FITS image header */
+char	*image;		/* FITS image pixels */
+
+{
+    int fd;
+
+    /* Open the output file */
+    if (strcasecmp (filename,"stdout") ) {
+
+    if (!access (filename, 0)) {
+        fd = open (filename, O_WRONLY);
+        if (fd < 3) {
+        snprintf (fitserrmsg,79, "FITSWEXT:  file %s not writeable\n",
+             filename);
+        return (0);
+        }
+        }
+    else {
+        fd = open (filename, O_APPEND, 0666);
+        if (fd < 3) {
+        snprintf (fitserrmsg,79, "FITSWEXT:  cannot append to file %s\n",
+             filename);
+        return (0);
+        }
+        }
+    }
+#ifndef VMS
+    else
+    fd = STDOUT_FILENO;
+#endif
+
+    return (fitswhdu (fd, filename, header, image));
+}
+
+
+/* FITSWHDU -- Write FITS head and image as extension */
+
+int
+fitswhdu (fd, filename, header, image)
+
+int	fd;		/* File descriptor */
+char	*filename;	/* Name of IFTS image file */
+char	*header;	/* FITS image header */
+char	*image;		/* FITS image pixels */
+{
+    int nbhead, nbimage, nblocks, bytepix, i, nbhw;
+    int bitpix, naxis, iaxis, naxisi, nbytes, nbw, nbpad, nbwp, simple;
+    char *endhead, *padding;
+    double bzero, bscale;
+    char keyword[32];
+
+    /* Change BITPIX=-16 files to BITPIX=16 with BZERO and BSCALE */
+    bitpix = 0;
+    hgeti4 (header,"BITPIX",&bitpix);
+    if (bitpix == -16) {
+    if (!hgetr8 (header, "BZERO", &bzero) &&
+        !hgetr8 (header, "BSCALE", &bscale)) {
+        bitpix = 16;
+        hputi4 (header, "BITPIX", bitpix);
+        hputr8 (header, "BZERO", 32768.0);
+        hputr8 (header, "BSCALE", 1.0);
+        }
+    }
+
+    /* Write header to file */
+    endhead = ksearch (header,"END") + 80;
+    nbhead = endhead - header;
+    nbhw = write (fd, header, nbhead);
+    if (nbhw < nbhead) {
+    snprintf (fitserrmsg,79, "FITSWHDU:  wrote %d / %d bytes of header to file %s\n",
+         nbhw, nbhead, filename);
+    (void)close (fd);
+    return (0);
+    }
+
+    /* Write extra spaces to make an integral number of 2880-byte blocks */
+    nblocks = nbhead / FITSBLOCK;
+    if (nblocks * FITSBLOCK < nbhead)
+    nblocks = nblocks + 1;
+    nbytes = nblocks * FITSBLOCK;
+    nbpad = nbytes - nbhead;
+    padding = (char *)calloc (1, nbpad);
+    for (i = 0; i < nbpad; i++)
+    padding[i] = ' ';
+    nbwp = write (fd, padding, nbpad);
+    if (nbwp < nbpad) {
+    snprintf (fitserrmsg,79, "FITSWHDU:  wrote %d / %d bytes of header padding to file %s\n",
+         nbwp, nbpad, filename);
+    (void)close (fd);
+    return (0);
+    }
+    nbhw = nbhw + nbwp;
+    free (padding);
+
+    /* Return if file has no data */
+    if (bitpix == 0 || image == NULL) {
+    /* snprintf (fitserrmsg,79, "FITSWHDU:  BITPIX is 0; image not written\n"); */
+    (void)close (fd);
+    return (0);
+    }
+
+    /* If SIMPLE=F in header, just write whatever is in the buffer */
+    hgetl (header, "SIMPLE", &simple);
+    if (!simple) {
+    hgeti4 (header, "NBDATA", &nbytes);
+    nbimage = nbytes;
+    }
+
+    else {
+
+    /* Compute size of pixel in bytes */
+    bytepix = bitpix / 8;
+    if (bytepix < 0) bytepix = -bytepix;
+    nbimage = bytepix;
+
+    /* Compute size of image in bytes using relevant header parameters */
+    naxis = 1;
+    hgeti4 (header,"NAXIS",&naxis);
+    for (iaxis = 1; iaxis <= naxis; iaxis++) {
+        sprintf (keyword, "NAXIS%d", iaxis);
+        naxisi = 1;
+        hgeti4 (header,keyword,&naxisi);
+        nbimage = nbimage * naxisi;
+        }
+
+    /* Number of bytes to write is an integral number of FITS blocks */
+    nblocks = nbimage / FITSBLOCK;
+    if (nblocks * FITSBLOCK < nbimage)
+        nblocks = nblocks + 1;
+    nbytes = nblocks * FITSBLOCK;
+
+    /* Byte-reverse image before writing, if necessary */
+    if (imswapped ())
+        imswap (bitpix, image, nbimage);
+    }
+
+    /* Write image to file */
+    nbw = write (fd, image, nbimage);
+    if (nbw < nbimage) {
+    snprintf (fitserrmsg,79, "FITSWHDU:  wrote %d / %d bytes of image to file %s\n",
+         nbw, nbimage, filename);
+    return (0);
+    }
+
+    /* Write extra zeroes to make an integral number of 2880-byte blocks */
+    nbpad = nbytes - nbimage;
+    if (nbpad > 0) {
+    padding = (char *)calloc (1, nbpad);
+    nbwp = write (fd, padding, nbpad);
+    if (nbwp < nbpad) {
+        snprintf (fitserrmsg,79, "FITSWHDU:  wrote %d / %d bytes of image padding to file %s\n",
+         nbwp, nbpad, filename);
+        (void)close (fd);
+        return (0);
+        }
+    free (padding);
+    }
+    else
+    nbwp = 0;
+
+    (void)close (fd);
+
+    /* Byte-reverse image after writing, if necessary */
+    if (imswapped ())
+    imswap (bitpix, image, nbimage);
+
+    nbw = nbw + nbwp + nbhw;
+    return (nbw);
+}
+
+
+/*FITSCIMAGE -- Write FITS header and copy FITS image
+        Return number of bytes in output image, 0 if failure */
+
+int
+fitscimage (filename, header, filename0)
+
+char	*filename;	/* Name of output FITS image file */
+char	*header;	/* FITS image header */
+char	*filename0;	/* Name of input FITS image file */
+
+{
+    int fdout, fdin;
+    int nbhead, nbimage, nblocks, bytepix;
+    int bitpix, naxis, naxis1, naxis2, nbytes, nbw, nbpad, nbwp;
+    char *endhead, *lasthead, *padding;
+    char *image;	/* FITS image pixels */
+    char *oldhead;	/* Input file image header */
+    int nbhead0;	/* Length of input file image header */
+    int lhead0;
+    int nbbuff, nbuff, ibuff, nbr, nbdata;
+
+    /* Compute size of image in bytes using relevant header parameters */
+    naxis = 1;
+    hgeti4 (header, "NAXIS", &naxis);
+    naxis1 = 1;
+    hgeti4 (header, "NAXIS1", &naxis1);
+    naxis2 = 1;
+    hgeti4 (header, "NAXIS2", &naxis2);
+    hgeti4 (header, "BITPIX", &bitpix);
+    bytepix = bitpix / 8;
+    if (bytepix < 0) bytepix = -bytepix;
+
+    /* If either dimension is one and image is 3-D, read all three dimensions */
+    if (naxis == 3 && (naxis1 ==1 || naxis2 == 1)) {
+    int naxis3;
+    hgeti4 (header,"NAXIS3",&naxis3);
+    nbimage = naxis1 * naxis2 * naxis3 * bytepix;
+    }
+    else
+    nbimage = naxis1 * naxis2 * bytepix;
+
+    nblocks = nbimage / FITSBLOCK;
+    if (nblocks * FITSBLOCK < nbimage)
+    nblocks = nblocks + 1;
+    nbytes = nblocks * FITSBLOCK;
+
+    /* Allocate image buffer */
+    nbbuff = FITSBLOCK * 100;
+    if (nbytes < nbbuff)
+    nbbuff = nbytes;
+    image = (char *) calloc (1, nbbuff);
+    nbuff = nbytes / nbbuff;
+    if (nbytes > nbuff * nbbuff)
+    nbuff = nbuff + 1;
+
+    /* Read input file header */
+    if ((oldhead = fitsrhead (filename0, &lhead0, &nbhead0)) == NULL) {
+    snprintf (fitserrmsg, 79,"FITSCIMAGE: header of input file %s cannot be read\n",
+         filename0);
+    return (0);
+    }
+
+    /* Find size of output header */
+    nbhead = fitsheadsize (header);
+
+    /* If overwriting, be more careful if new header is longer than old */
+    if (!strcmp (filename, filename0) && nbhead > nbhead0) {
+    if ((image = fitsrimage (filename0, nbhead0, oldhead)) == NULL) {
+        snprintf (fitserrmsg,79, "FITSCIMAGE:  cannot read image from file %s\n",
+             filename0);
+        free (oldhead);
+        return (0);
+        }
+    return (fitswimage (filename, header, image));
+    }
+    free (oldhead);
+
+    /* Open the input file and skip over the header */
+    if (strcasecmp (filename0,"stdin")) {
+    fdin = -1;
+    fdin = fitsropen (filename0);
+    if (fdin < 0) {
+        snprintf (fitserrmsg, 79,"FITSCIMAGE:  cannot read file %s\n", filename0);
+        return (0);
+        }
+
+    /* Skip over FITS header */
+    if (lseek (fdin, nbhead0, SEEK_SET) < 0) {
+        (void)close (fdin);
+        snprintf (fitserrmsg,79, "FITSCIMAGE:  cannot skip header of file %s\n",
+             filename0);
+        return (0);
+        }
+    }
+#ifndef VMS
+    else
+    fdin = STDIN_FILENO;
+#endif
+
+    /* Open the output file */
+    if (!access (filename, 0)) {
+    fdout = open (filename, O_WRONLY);
+    if (fdout < 3) {
+        snprintf (fitserrmsg,79, "FITSCIMAGE:  file %s not writeable\n", filename);
+        return (0);
+        }
+    }
+    else {
+    fdout = open (filename, O_RDWR+O_CREAT, 0666);
+    if (fdout < 3) {
+        snprintf (fitserrmsg,79, "FITSCHEAD:  cannot create file %s\n", filename);
+        return (0);
+        }
+    }
+
+    /* Pad header with spaces */
+    endhead = ksearch (header,"END") + 80;
+    lasthead = header + nbhead;
+    while (endhead < lasthead)
+    *(endhead++) = ' ';
+
+    /* Write header to file */
+    nbw = write (fdout, header, nbhead);
+    if (nbw < nbhead) {
+    snprintf (fitserrmsg, 79,"FITSCIMAGE:  wrote %d / %d bytes of header to file %s\n",
+         nbw, nbytes, filename);
+    (void)close (fdout);
+    (void)close (fdin);
+    return (0);
+    }
+
+    /* Return if no data */
+    if (bitpix == 0) {
+    (void)close (fdout);
+    (void)close (fdin);
+    return (nbhead);
+    }
+
+    nbdata = 0;
+    for (ibuff = 0; ibuff < nbuff; ibuff++) {
+    nbr = read (fdin, image, nbbuff);
+    if (nbr > 0) {
+        nbw = write (fdout, image, nbr);
+        nbdata = nbdata + nbw;
+        }
+    }
+
+    /* Write extra to make integral number of 2880-byte blocks */
+    nblocks = nbdata / FITSBLOCK;
+    if (nblocks * FITSBLOCK < nbdata)
+    nblocks = nblocks + 1;
+    nbytes = nblocks * FITSBLOCK;
+    nbpad = nbytes - nbdata;
+    padding = (char *)calloc (1,nbpad);
+    nbwp = write (fdout, padding, nbpad);
+    nbw = nbdata + nbwp;
+    free (padding);
+
+    (void)close (fdout);
+    (void)close (fdin);
+
+    if (nbw < nbimage) {
+    snprintf (fitserrmsg, 79, "FITSWIMAGE:  wrote %d / %d bytes of image to file %s\n",
+         nbw, nbimage, filename);
+    return (0);
+    }
+    else
+    return (nbw);
+}
+
+
+/* FITSWHEAD -- Write FITS header and keep file open for further writing */
+
+int
+fitswhead (filename, header)
+
+char	*filename;	/* Name of IFTS image file */
+char	*header;	/* FITS image header */
+
+{
+    int fd;
+    int nbhead, nblocks;
+    int nbytes, nbw;
+    char *endhead, *lasthead;
+
+    /* Open the output file */
+    if (!access (filename, 0)) {
+    fd = open (filename, O_WRONLY);
+    if (fd < 3) {
+        snprintf (fitserrmsg, 79, "FITSWHEAD:  file %s not writeable\n", filename);
+        return (0);
+        }
+    }
+    else {
+    fd = open (filename, O_RDWR+O_CREAT, 0666);
+    if (fd < 3) {
+        snprintf (fitserrmsg, 79, "FITSWHEAD:  cannot create file %s\n", filename);
+        return (0);
+        }
+    }
+
+    /* Write header to file */
+    endhead = ksearch (header,"END") + 80;
+    nbhead = endhead - header;
+    nblocks = nbhead / FITSBLOCK;
+    if (nblocks * FITSBLOCK < nbhead)
+    nblocks = nblocks + 1;
+    nbytes = nblocks * FITSBLOCK;
+
+    /* Pad header with spaces */
+    lasthead = header + nbytes;
+    while (endhead < lasthead)
+    *(endhead++) = ' ';
+
+    nbw = write (fd, header, nbytes);
+    if (nbw < nbytes) {
+    fprintf (stderr, "FITSWHEAD:  wrote %d / %d bytes of header to file %s\n",
+         nbw, nbytes, filename);
+    (void)close (fd);
+    return (0);
+    }
+    return (fd);
+}
+
+
+/* FITSWEXHEAD -- Write FITS header in place */
+
+int
+fitswexhead (filename, header)
+
+char	*filename;	/* Name of FITS image file with ,extension */
+char	*header;	/* FITS image header */
+
+{
+    int fd;
+    int nbhead, lhead;
+    int nbw, nbnew, nbold;
+    char *endhead, *lasthead, *oldheader;
+    char *ext, cext;
+
+    /* Compare size of existing header to size of new header */
+    fitsinherit = 0;
+    oldheader = fitsrhead (filename, &lhead, &nbhead);
+    if (oldheader == NULL) {
+    snprintf (fitserrmsg, 79, "FITSWEXHEAD:  file %s cannot be read\n", filename);
+    return (-1);
+    }
+    nbold = fitsheadsize (oldheader);
+    nbnew = fitsheadsize (header);
+
+    /* Return if the new header is bigger than the old header */
+    if (nbnew > nbold) {
+    snprintf (fitserrmsg, 79, "FITSWEXHEAD:  old header %d bytes, new header %d bytes\n", nbold,nbnew);
+    free (oldheader);
+    oldheader = NULL;
+    return (-1);
+    }
+
+    /* Add blank lines if new header is smaller than the old header */
+    else if (nbnew < nbold) {
+    strcpy (oldheader, header);
+    endhead = ksearch (oldheader,"END");
+    lasthead = oldheader + nbold;
+    while (endhead < lasthead)
+        *(endhead++) = ' ';
+    strncpy (lasthead-80, "END", 3);
+    }
+
+    /* Pad header with spaces */
+    else {
+    endhead = ksearch (header,"END") + 80;
+    lasthead = header + nbnew;
+    while (endhead < lasthead)
+        *(endhead++) = ' ';
+    strncpy (oldheader, header, nbnew);
+    }
+
+    /* Check for FITS extension and ignore for file opening */
+    ext = strchr (filename, ',');
+    if (ext == NULL)
+    ext = strchr (filename, '[');
+    if (ext != NULL) {
+    cext = *ext;
+    *ext = (char) 0;
+    }
+
+    /* Open the output file */
+    fd = open (filename, O_WRONLY);
+    if (ext != NULL)
+    *ext = cext;
+    if (fd < 3) {
+    snprintf (fitserrmsg, 79, "FITSWEXHEAD:  file %s not writeable\n", filename);
+    return (-1);
+    }
+
+    /* Skip to appropriate place in file */
+    (void) lseek (fd, ibhead, SEEK_SET);
+
+    /* Write header to file */
+    nbw = write (fd, oldheader, nbold);
+    (void)close (fd);
+    free (oldheader);
+    oldheader = NULL;
+    if (nbw < nbold) {
+    fprintf (stderr, "FITSWHEAD:  wrote %d / %d bytes of header to file %s\n",
+         nbw, nbold, filename);
+    return (-1);
+    }
+    return (0);
+}
+
+
+/* ISFITS -- Return 1 if FITS file, else 0 */
+int
+isfits (filename)
+
+char    *filename;      /* Name of file for which to find size */
+{
+    int diskfile;
+    char keyword[16];
+    char *comma;
+    int nbr;
+
+    /* First check to see if this is an assignment */
+    if (strchr (filename, '='))
+    return (0);
+
+    /* Check for stdin (input from pipe) */
+    else if (!strcasecmp (filename,"stdin"))
+    return (1);
+
+    /* Then check file extension
+    else if (strsrch (filename, ".fit") ||
+    strsrch (filename, ".fits") ||
+    strsrch (filename, ".fts"))
+    return (1); */
+
+    /* If no FITS file extension, try opening the file */
+    else {
+    if ((comma = strchr (filename,',')))
+        *comma = (char) 0;
+    if ((diskfile = open (filename, O_RDONLY)) < 0) {
+        if (comma)
+        *comma = ',';
+        return (0);
+        }
+    else {
+        nbr = read (diskfile, keyword, 8);
+        if (comma)
+        *comma = ',';
+        close (diskfile);
+        if (nbr < 8)
+        return (0);
+        else if (!strncmp (keyword, "SIMPLE", 6))
+        return (1);
+        else
+        return (0);
+        }
+    }
+}
+
+
+/* FITSHEADSIZE -- Find size of FITS header */
+
+int
+fitsheadsize (header)
+
+char	*header;	/* FITS header */
+{
+    char *endhead;
+    int nbhead, nblocks;
+
+    endhead = ksearch (header,"END") + 80;
+    nbhead = endhead - header;
+    nblocks = nbhead / FITSBLOCK;
+    if (nblocks * FITSBLOCK < nbhead)
+        nblocks = nblocks + 1;
+    return (nblocks * FITSBLOCK);
+}
+
+
+/* Print error message */
+void
+fitserr ()
+{   fprintf (stderr, "%s\n",fitserrmsg);
+    return; }
+
+
+/* MOVEB -- Copy nbytes bytes from source+offs to dest+offd (any data type) */
+
+void
+moveb (source, dest, nbytes, offs, offd)
+
+char *source;	/* Pointer to source */
+char *dest;	/* Pointer to destination */
+int nbytes;	/* Number of bytes to move */
+int offs;	/* Offset in bytes in source from which to start copying */
+int offd;	/* Offset in bytes in destination to which to start copying */
+{
+char *from, *last, *to;
+        from = source + offs;
+        to = dest + offd;
+        last = from + nbytes;
+        while (from < last) *(to++) = *(from++);
+        return;
+}
+
+/*
+ * Feb  8 1996	New subroutines
+ * Apr 10 1996	Add subroutine list at start of file
+ * Apr 17 1996	Print error message to stderr
+ * May  2 1996	Write using stream IO
+ * May 14 1996	If FITSRTOPEN NK is zero, return all keywords in header
+ * May 17 1996	Make header internal to FITSRTOPEN
+ * Jun  3 1996	Use stream I/O for input as well as output
+ * Jun 10 1996	Remove unused variables after running lint
+ * Jun 12 1996	Deal with byte-swapped images
+ * Jul 11 1996	Rewrite code to separate header and data reading
+ * Aug  6 1996  Fixed small defects after lint
+ * Aug  6 1996  Drop unused NBHEAD argument from FITSRTHEAD
+ * Aug 13 1996	If filename is stdin, read from standard input instead of file
+ * Aug 30 1996	Use write for output, not fwrite
+ * Sep  4 1996	Fix mode when file is created
+ * Oct 15 1996	Drop column argument from FGET* subroutines
+ * Oct 15 1996	Drop unused variable
+ * Dec 17 1996	Add option to skip bytes in file before reading the header
+ * Dec 27 1996	Turn nonprinting header characters into spaces
+ *
+ * Oct  9 1997	Add FITS extension support as filename,extension
+ * Dec 15 1997	Fix minor bugs after lint
+ *
+ * Feb 23 1998	Do not append primary header if getting header for ext. 0
+ * Feb 23 1998	Accept either bracketed or comma extension
+ * Feb 24 1998	Add SIMPLE keyword to start of extracted extension
+ * Apr 30 1998	Fix error return if not table file after Allan Brighton
+ * May  4 1998	Fix error in argument sequence in HGETS call
+ * May 27 1998	Include fitsio.h and imio.h
+ * Jun  1 1998	Add VMS fixes from Harry Payne at STScI
+ * Jun  3 1998	Fix bug reading EXTNAME
+ * Jun 11 1998	Initialize all header parameters before reading them
+ * Jul 13 1998	Clarify argument definitions
+ * Aug  6 1998	Rename fitsio.c to fitsfile.c to avoid conflict with CFITSIO
+ * Aug 13 1998	Add FITSWHEAD to write only header
+ * Sep 25 1998	Allow STDIN or stdin for standard input reading
+ * Oct  5 1998	Add isfits() to decide whether a file is FITS
+ * Oct  9 1998	Assume stdin and STDIN to be FITS files in isfits()
+ * Nov 30 1998	Fix bug found by Andreas Wicenec when reading large headers
+ * Dec  8 1998	Fix bug introduced by previous bug fix
+ *
+ * Jan  4 1999	Do not print error message if BITPIX is 0
+ * Jan 27 1999	Read and write all of 3D images if one dimension is 1
+ * Jan 27 1999	Pad out data to integral number of 2880-byte blocks
+ * Apr 29 1999	Write BITPIX=-16 files as BITPIX=16 with BSCALE and BZERO
+ * Apr 30 1999	Add % as alternative to , to denote sub-images
+ * May 25 1999	Set buffer offsets to 0 when FITS table file is opened
+ * Jul 14 1999	Do not try to write image data if BITPIX is 0
+ * Sep 27 1999	Add STDOUT as output filename option in fitswimage()
+ * Oct  6 1999	Set header length global variable hget.lhead0 in fitsrhead()
+ * Oct 14 1999	Update header length as it is changed in fitsrhead()
+ * Oct 20 1999	Change | in if statements to ||
+ * Oct 25 1999	Change most malloc() calls to calloc()
+ * Nov 24 1999	Add fitscimage()
+ *
+ * Feb 23 2000	Fix problem with some error returns in fitscimage()
+ * Mar 17 2000	Drop unused variables after lint
+ * Jul 20 2000	Drop BITPIX and NAXIS from primary header if extension printerd
+ * Jul 20 2000	Start primary part of header with ROOTHEAD keyword
+ * Jul 28 2000	Add loop to deal with buffered stdin
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Jan 12 2001	Add extension back onto filename after fitsropen() (Guy Rixon)
+ * Jan 18 2001	Drop EXTEND keyword when extracting an extension
+ * Jan 18 2001	Add fitswext() to append HDU and fitswhdu() to do actual writing
+ * Jan 22 2001	Ignore WCS name or letter following a : in file name in fitsrhead()
+ * Jan 30 2001	Fix FITSCIMAGE so it doesn't overwrite data when overwriting a file
+ * Feb 20 2001	Ignore WCS name or letter following a : in file name in fitsropen()
+ * Feb 23 2001	Initialize rbrac in fitsropen()
+ * Mar  8 2001	Use % instead of : for WCS specification in file name
+ * Mar  9 2001	Fix bug so primary header is always appended to secondary header
+ * Mar  9 2001	Change NEXTEND to NUMEXT in appended primary header
+ * Mar 20 2001	Declare fitsheadsize() in fitschead()
+ * Apr 24 2001	When matching column names, use longest length
+ * Jun 27 2001	In fitsrthead(), allocate pw and lpnam only if more space needed
+ * Aug 24 2001	In isfits(), return 0 if argument contains an equal sign
+ *
+ * Jan 28 2002	In fitsrhead(), allow stdin to include extension and/or WCS selection
+ * Jun 18 2002	Save error messages as fitserrmsg and use fitserr() to print them
+ * Oct 21 2002	Add fitsrsect() to read a section of an image
+ *
+ * Feb  4 2003	Open catalog file rb instead of r (Martin Ploner, Bern)
+ * Apr  2 2003	Drop unused variable in fitsrsect()
+ * Jul 11 2003	Use strcasecmp() to check for stdout and stdin
+ * Aug  1 2003	If no other header, return root header from fitsrhead()
+ * Aug 20 2003	Add fitsrfull() to read n-dimensional FITS images
+ * Aug 21 2003	Modify fitswimage() to always write n-dimensional FITS images
+ * Nov 18 2003	Fix minor bug in fitswhdu()
+ * Dec  3 2003	Remove unused variable lasthead in fitswhdu()
+ *
+ * May  3 2004	Do not always append primary header to extension header
+ * May  3 2004	Add ibhead as position of header read in file
+ * May 19 2004	Do not reset ext if NULL in fitswexhead()
+ * Jul  1 2004	Initialize INHERIT to 1
+ * Aug 30 2004	Move fitsheadsize() declaration to fitsfile.h
+ * Aug 31 2004	If SIMPLE=F, put whatever is in file after header in image
+ *
+ * Mar 17 2005	Use unbuffered I/O in isfits() for robustness
+ * Jun 27 2005	Drop unused variable nblocks in fitswexhead()
+ * Aug  8 2005	Fix space-padding bug in fitswexhead() found by Armin Rest
+ * Sep 30 2005	Fix fitsrsect() to position relatively, not absolutely
+ * Oct 28 2005	Add error message if desired FITS extension is not found
+ * Oct 28 2005	Fix initialization problem found by Sergey Koposov
+ *
+ * Feb 23 2006	Add fitsrtail() to read appended FITS headers
+ * Feb 27 2006	Add file name to header-reading error messages
+ * May  3 2006	Remove declarations of unused variables
+ * Jun 20 2006	Initialize uninitialized variables
+ * Nov  2 2006	Change all realloc() calls to calloc()
+ *
+ * Jan  5 2007	In fitsrtail(), change control characters in header to spaces
+ * Apr 30 2007	Improve error reporting in FITSRFULL
+ * Nov 28 2007	Add support to BINTABLE in ftget*() and fitsrthead()
+ * Dec 20 2007	Add data heap numerated by PCOUNT when skipping HDU in fitsrhead()
+ * Dec 20 2007	Return NULL pointer if fitsrhead() cannot find requested HDU
+ *
+ * Apr  7 2008	Drop comma from name when reading file in isfits()
+ * Jun 27 2008	Do not append primary data header if it is the only header
+ * Nov 21 2008	In fitswhead(), print message if too few bytes written
+ *
+ * Sep 18 2009	In fitswexhead() write to error string instead of stderr
+ * Sep 22 2009	In fitsrthead(), fix lengths for ASCII numeric table entries
+ * Sep 25 2009	Add subroutine moveb() and fix calls to it
+ * Sep 25 2009	Fix several small errors found by Jessicalas Burke
+ *
+ * Mar 29 2010	In fitswhead(), always pad blocks to 2880 bytes with spaces
+ * Mar 31 2010	In fitsrhead(), fix bug reading long primary headers
+ *
+ * Sep 15 2011	In fitsrsect() declare impos and nblin off_t
+ * Sep 15 2011	In fitsrtail() declare offset off_t
+ * Sep 15 2011	Declare global variable ibhead off_t
+ *
+ * Jul 25 2014	Fix bug when reallocating buffer for long headers
+ */
diff --git a/Code/src/libwcs/fitsfile.h b/Code/src/libwcs/fitsfile.h
new file mode 100644
index 0000000000000000000000000000000000000000..c47271c2fae6ecae555131bb697b38106353bb56
--- /dev/null
+++ b/Code/src/libwcs/fitsfile.h
@@ -0,0 +1,1293 @@
+/*** File fitsfile.h  FITS and IRAF file access subroutines
+ *** June 20, 2014
+ *** By Jessica Mink, jmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2014
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: jmink@cfa.harvard.edu
+           Postal address: Jessica Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#ifndef fitsfile_h_
+#define fitsfile_h_
+#include "fitshead.h"
+
+/* Declarations for subroutines in fitsfile.c, imhfile.c, imio.c,
+ * fileutil.c, and dateutil.c */
+
+#define FITSBLOCK 2880
+
+/* FITS table keyword structure */
+struct Keyword {
+    char kname[10];	/* Keyword for table entry */
+    int lname;		/* Length of keyword name */
+    int kn;		/* Index of entry on line */
+    int kf;		/* Index in line of first character of entry */
+    int kl;		/* Length of entry value */
+    char kform[8];	/* Format for this value */
+};
+
+/* Structure for access to tokens within a string */
+#define MAXTOKENS 1000    /* Maximum number of tokens to parse */
+#define MAXWHITE 20     /* Maximum number of different whitespace characters */
+struct Tokens {
+    char *line;		/* Line which has been parsed */
+    int lline;		/* Number of characters in line */
+    int ntok;		/* Number of tokens on line */
+    int nwhite;		/* Number of whitespace characters */
+    char white[MAXWHITE]; /* Whitespace (separator) characters */
+    char *tok1[MAXTOKENS]; /* Pointers to start of tokens */
+    int ltok[MAXTOKENS]; /* Lengths of tokens */
+    int itok;		/* Current token number */
+};
+
+#ifdef __cplusplus /* C++ prototypes */
+extern "C" {
+#endif
+
+
+#ifdef __STDC__   /* Full ANSI prototypes */
+
+/* Declarations for subroutines in fitsfile.c, imhfile.c, imio.c,
+ * fileutil.c, and dateutil.c */
+
+/* FITS file access subroutines in fitsfile.c */
+    int fitsropen(	/* Open a FITS file for reading, returning a FILE pointer */
+    char *inpath);	/* Pathname for FITS tables file to read */
+    char *fitsrhead(	/* Read a FITS header */
+    char *filename,	/* Name of FITS image file */
+    int *lhead,	/* Allocated length of FITS header in bytes (returned) */
+    int *nbhead);	/* Number of bytes before start of data (returned) */
+    char *fitsrtail(	/* Read FITS header appended to graphics file */
+    char *filename,	/* Name of FITS image file */
+    int *lhead,	/* Allocated length of FITS header in bytes (returned) */
+    int *nbhead);	/* Number of bytes before start of data (returned) */
+    char *fitsrimage(	/* Read a FITS image */
+    char *filename,	/* Name of FITS image file */
+    int nbhead,	/* Actual length of image header(s) in bytes */
+    char *header);	/* FITS header for image (previously read) */
+    char *fitsrfull(	/* Read a FITS image of any dimension */
+    char *filename,	/* Name of FITS image file */
+    int nbhead,	/* Actual length of image header(s) in bytes */
+    char *header);	/* FITS header for image (previously read) */
+    char *fitsrsect(	/* Read a piece of a FITS image, header */
+    char *filename,	/* Name of FITS image file */
+    char *header,	/* FITS header for image (previously read) */
+    int nbhead,	/* Actual length of image header(s) in bytes */
+    int x0, 	/* FITS image X coordinate of first pixel */
+    int y0, 	/* FITS image Y coordinate of first pixel */
+    int nx,		/* Number of columns to read (less than NAXIS1) */
+    int ny,		/* Number of rows to read (less than NAXIS2) */
+    int nlog);	/* Note progress mod this rows */
+    int fitswhead(	/* Write FITS header; keep file open for further writing */
+    char *filename,	/* Name of FITS image file */
+    char *header);	/* FITS header for image (previously read) */
+    int fitswexhead(	/* Write FITS header in place */
+    char *filename,	/* Name of FITS image file */
+    char *header);	/* FITS header for image */
+    int fitswext(	/* Write FITS header and image as extension to a file */
+    char *filename,	/* Name of FITS image file */
+    char *header,	/* FITS image header */
+    char *image);	/* FITS image pixels */
+    int fitswhdu(	/* Write FITS head and image as extension */
+    int fd,		/* File descriptor */
+    char *filename,	/* Name of FITS image file */
+    char *header,	/* FITS image header */
+    char *image);	/* FITS image pixels */
+    int fitswimage(	/* Write FITS header and image */
+    char *filename,	/* Name of FITS image file */
+    char *header,	/* FITS image header */
+    char *image);	/* FITS image pixels */
+    int fitscimage(	/* Write FITS header and copy FITS image */
+    char *filename,	/* Name of output FITS image file */
+    char *header,	/* FITS image header */
+    char *filename0); /* Name of input FITS image file */
+    int isfits(		/* Return 1 if file is a FITS file */
+    char *filename); /* Name of file to check */
+    void fitserr();	/* Print FITS error message to stderr */
+    void setfitsinherit( /* Set flag to append primary data header */
+    int inh);	/* 1 to inherit primary data header, else 0 */
+    int fitsheadsize(	/* Return size of fitsheader in bytes */
+    char *header);	/* FITS image header */
+
+/* FITS table file access subroutines in fitsfile.c */
+
+    int fitsrtopen(	/* Open FITS table file and fill structure with
+             * pointers to selected keywords
+             * Return file descriptor (-1 if unsuccessful) */
+    char *inpath,	/* Pathname for FITS tables file to read */
+    int *nk,	/* Number of keywords to use */
+    struct Keyword **kw, /* Structure for desired entries */
+    int *nrows,	/* Number of rows in table (returned) */
+    int *nchar,	/* Number of characters in one table row (returned) */
+    int *nbhead);	/* Number of characters before table starts */
+    int fitsrthead(	/* Read pointers to selected keywords
+             * from FITS table header */
+    char *header,	/* Header for FITS tables file */
+    int *nk,	/* Number of keywords to use */
+    struct Keyword **kw, /* Structure for desired entries */
+    int *nrows,	/* Number of rows in table (returned) */
+    int *nchar);	/* Number of characters in one table row (returned) */
+    void fitsrtlset(void); /* Reset FITS Table buffer limits from start of data */
+    int fitsrtline(	/* Return specified line of FITS table */
+    int fd,		/* File descriptor for FITS file */
+    int nbhead,	/* Number of bytes in FITS header */
+    int lbuff,	/* Number of bytes in table buffer */
+    char *tbuff,	/* FITS table buffer */
+    int irow,	/* Number of table row to read */
+    int nbline,	/* Number of bytes to read for this line */
+    char *line);	/* One line of FITS table (returned) */
+short ftgeti2(		/* Extract column for keyword from FITS table line
+             * as short */
+    char *entry,	/* Row or entry from table */
+    struct Keyword *kw); /* Table column information from FITS header */
+    int ftgeti4(	/* Extract column for keyword from FITS table line
+             * as int */
+    char *entry,	/* Row or entry from table */
+    struct Keyword *kw); /* Table column information from FITS header */
+float ftgetr4(		/* Extract column for keyword from FITS table line
+             * as float */
+    char *entry,	/* Row or entry from table */
+    struct Keyword *kw); /* Table column information from FITS header */
+    double ftgetr8(	/* Extract column for keyword from FITS table line
+             * as double */
+    char *entry,	/* Row or entry from table */
+    struct Keyword *kw); /* Table column information from FITS header */
+    int ftgetc(		/* Extract column for keyword from FITS table line
+             * as char string */
+    char *entry,	/* Row or entry from table */
+    struct Keyword *kw, /* Table column information from FITS header */
+    char *string,	/* Returned string */
+    int maxchar);	/* Maximum number of characters in returned string */
+
+    void moveb (     /* Copy nbytes bytes from source+offs to dest+offd */
+    char *source,   /* Pointer to source */
+    char *dest,     /* Pointer to destination */
+    int nbytes,     /* Number of bytes to move */
+    int offs,       /* Offset in bytes in source from which to start copying */
+    int offd);      /* Offset in bytes in destination to which to start copying */
+
+
+/* IRAF file access subroutines in imhfile.c */
+
+    char *irafrhead(	/* Read IRAF .imh header file and translate to FITS header */
+    char *filename,	/* Name of IRAF header file */
+    int *lihead);	/* Length of IRAF image header in bytes (returned) */
+    char *irafrimage(	/* Read IRAF image pixels (call after irafrhead) */
+    char *fitsheader); /* FITS image header (filled) */
+    int irafwhead(	/* Write IRAF .imh header file */
+    char *hdrname,	/* Name of IRAF header file */
+    int lhead,	/* Length of IRAF header */
+    char *irafheader, /* IRAF header */
+    char *fitsheader); /* FITS image header */
+    int irafwimage(	/* Write IRAF .imh header file and .pix image file */
+    char *hdrname,	/* Name of IRAF header file */
+    int lhead,	/* Length of IRAF header */
+    char *irafheader, /* IRAF header */
+    char *fitsheader, /* FITS image header */
+    char *image);	/* IRAF image */
+    int isiraf(		/* return 1 if IRAF imh file, else 0 */
+    char *filename); /* Name of file to check */
+    char *iraf2fits(	/* Convert IRAF image header to FITS image header,
+             * returning FITS header */
+    char *hdrname,	/* IRAF header file name (may be path) */
+    char *irafheader, /* IRAF image header */
+    int nbiraf,	/* Number of bytes in IRAF header */
+    int *nbfits);	/* Number of bytes in FITS header (returned) */
+
+    char *fits2iraf(	/* Convert FITS image header to IRAF image header,
+             * returning IRAF header */
+    char *fitsheader, /* FITS image header */
+    char *irafheader, /* IRAF image header (returned updated) */
+    int nbhead,	/* Length of IRAF header */
+    int *nbiraf);	/* Length of returned IRAF header */
+
+/* Image pixel access subroutines in imio.c */
+
+    double getpix(	/* Read one pixel from any data type 2-D array (0,0)*/
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel
+             *  16 = short, -16 = unsigned short, 32 = int
+             * -32 = float, -64 = double */
+    int w,		/* Image width in pixels */
+    int h,		/* Image height in pixels */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int x,		/* Zero-based horizontal pixel number */
+    int y);		/* Zero-based vertical pixel number */
+    double getpix1(	/* Read one pixel from any data type 2-D array (1,1)*/
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    int w,		/* Image width in pixels */
+    int h,		/* Image height in pixels */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int x,		/* One-based horizontal pixel number */
+    int y);		/* One-based vertical pixel number */
+    double maxvec(	/* Get maximum value in vector from a image */
+    char *image,	/* Image array from which to extract vector */
+    int bitpix,	/* Number of bits per pixel in image */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int pix1,	/* Offset of first pixel to extract */
+    int npix);	/* Number of pixels to extract */
+    double minvec(	/* Get minimum value in vector from a image */
+    char *image,	/* Image array from which to extract vector */
+    int bitpix,	/* Number of bits per pixel in image */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int pix1,	/* Offset of first pixel to extract */
+    int npix);	/* Number of pixels to extract */
+    void putpix(	/* Write one pixel to any data type 2-D array (0,0)*/
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    int w,		/* Image width in pixels */
+    int h,		/* Image height in pixels */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int x,		/* Zero-based horizontal pixel number */
+    int y,		/* Zero-based vertical pixel number */
+    double dpix);	/* Value to put into image pixel */
+    void putpix1(	/* Write one pixel to any data type 2-D array (1,1) */
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    int w,		/* Image width in pixels */
+    int h,		/* Image height in pixels */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int x,		/* One-based horizontal pixel number */
+    int y,		/* One-based vertical pixel number */
+    double dpix);	/* Value to put into image pixel */
+    void addpix(	/* Add to one pixel in any data type 2-D array (0,0)*/
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    int w,		/* Image width in pixels */
+    int h,		/* Image height in pixels */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int x,		/* Zero-based horizontal pixel number */
+    int y,		/* Zero-based vertical pixel number */
+    double dpix);	/* Value to add to image pixel */
+    void addpix1(	/* Add to one pixel in any data type 2-D array (1,1)*/
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    int w,		/* Image width in pixels */
+    int h,		/* Image height in pixels */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int x,		/* One-based horizontal pixel number */
+    int y,		/* One-based vertical pixel number */
+    double dpix);	/* Value to add to image pixel */
+    void movepix(	/* Move one pixel value between two 2-D arrays (0,0) */
+    char *image1,	/* Pointer to first pixel in input image */
+    int bitpix1,	/* Bits per input pixel (FITS codes) */
+    int w1,		/* Number of horizontal pixels in input image */
+    int x1,		/* Zero-based row for input pixel */
+    int y1,		/* Zero-based column for input pixel */
+    char *image2,	/* Pointer to first pixel in output image */
+    int bitpix2,	/* Bits per output pixel (FITS codes) */
+    int w2,		/* Number of horizontal pixels in output image */
+    int x2,		/* Zero-based row for output pixel */
+    int y2);	/* Zero-based column for output pixel */
+    void movepix1(	/* Move one pixel value between two 2-D arrays (1,1) */
+    char *image1,	/* Pointer to first pixel in input image */
+    int bitpix1,	/* Bits per input pixel (FITS codes) */
+    int w1,		/* Number of horizontal pixels in input image */
+    int x1,		/* One-based row for input pixel */
+    int y1,		/* One-based column for input pixel */
+    char *image2,	/* Pointer to first pixel in output image */
+    int bitpix2,	/* Bits per output pixel (FITS codes) */
+    int w2,		/* Number of horizontal pixels in output image */
+    int x2,		/* One-based row for output pixel */
+    int y2);	/* One-based column for output pixel */
+
+/* Image vector processing subroutines in imio.c */
+
+    void addvec(	/* Add constant to vector from 2-D array */
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int pix1,	/* Offset of first pixel to which to add */
+    int npix,	/* Number of pixels to which to add */
+    double dpix);	/* Value to add to pixels */
+    void multvec(	/* Multiply vector from 2-D array by a constant */
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int pix1,	/* Offset of first pixel to multiply */
+    int npix,	/* Number of pixels to multiply */
+    double dpix);	/* Value to add to pixels */
+    void getvec(	/* Read vector from 2-D array */
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int pix1,	/* Offset of first pixel to extract */
+    int npix,	/* Number of pixels to extract */
+    double *dvec0);	/* Vector of pixels (returned) */
+    void putvec(	/* Write vector into 2-D array */
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int pix1,	/* Offset of first pixel to insert */
+    int npix,	/* Number of pixels to insert */
+    double *dvec0);	/* Vector of pixels to insert */
+    void fillvec(	/* Write constant into a vector */
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int pix1,	/* Zero-based offset of first pixel to multiply */
+    int npix,	/* Number of pixels to multiply */
+    double dpix);	/* Value to which to set pixels */
+    void fillvec1(	/* Write constant into a vector */
+    char *image,	/* Image array as 1-D vector */
+    int bitpix,	/* FITS bits per pixel */
+    double bzero,	/* Zero point for pixel scaling */
+    double bscale,	/* Scale factor for pixel scaling */
+    int pix1,	/* One-based offset of first pixel to multiply */
+    int npix,	/* Number of pixels to multiply */
+    double dpix);	/* Value to which to set pixels */
+
+/* Image pixel byte-swapping subroutines in imio.c */
+
+    void imswap(	/* Swap alternating bytes in a vector */
+    int bitpix,	/* Number of bits per pixel */
+    char *string,	/* Address of starting point of bytes to swap */
+    int nbytes);	/* Number of bytes to swap */
+    void imswap2(	/* Swap bytes in a vector of 2-byte (short) integers */
+    char *string,	/* Address of starting point of bytes to swap */
+    int nbytes);	/* Number of bytes to swap */
+    void imswap4(	/* Reverse bytes in a vector of 4-byte numbers */
+    char *string,	/* Address of starting point of bytes to swap */
+    int nbytes);	/* Number of bytes to swap */
+    void imswap8(	/* Reverse bytes in a vector of 8-byte numbers */
+    char *string,	/* Address of starting point of bytes to swap */
+    int nbytes);	/* Number of bytes to swap */
+    int imswapped(void); /* Return 1 if machine byte order is not FITS order */
+
+/* File utilities from fileutil.c */
+
+    int getfilelines(	/*  Return number of lines in an ASCII file */
+    char *filename); /* Name of file to check */
+    char *getfilebuff(	/* Return entire file contents in a character string */
+    char *filename); /* Name of file to read */
+    int getfilesize(	/* Return size of a binary or ASCII file */
+    char *filename); /* Name of file to check */
+    int isimlist(	/* Return 1 if file is list of FITS or IRAF image files, else 0 */
+    char *filename); /* Name of file to check */
+    int isimlistd(	/* Return 1 if file is list of FITS or IRAF image files, else 0 */
+    char *filename,	/* Name of file to check */
+    char *rootdir);	/* Name of root directory for files in list */
+    int isfilelist(	/* Return 1 if list of readable files, else 0 */
+    char *filename,	/* Name of file to check */
+    char *rootdir);	/* Name of root directory for files in list */
+    int isfile(		/* Return 1 if file is a readable file, else 0 */
+    char *filename); /* Name of file to check */
+    int istiff(		/* Return 1 if TIFF image file, else 0 */
+    char *filename); /* Name of file to check */
+    int isjpeg(		/* Return 1 if JPEG image file, else 0 */
+    char *filename); /* Name of file to check */
+    int isgif(		/* Return 1 if GIF image file, else 0 */
+    char *filename); /* Name of file to check */
+    int next_line (	/* Return the next line of an ASCII file */
+    FILE *diskfile,	/* File descriptor for ASCII file */
+    int ncmax,	/* Maximum number of characters returned */
+    char *line);	/* Next line (returned) */
+    int first_token(	/* Return first token from the next line of an ASCII file */
+    FILE *diskfile,	/* File descriptor for ASCII file */
+    int ncmax,	/* Maximum number of characters returned */
+    char *token);	/* First token on next line (returned) */
+    int stc2s (		/* Replace character in string with space */
+    char *spchar,	/* Character to replace with spaces */
+    char *string);	/* Character string to process */
+    int sts2c (		/* Replace spaces in string with character */
+    char *spchar,	/* Character with which to replace spaces */
+    char *string);	/* Character string to process */
+
+/* Subroutines for access to tokens within a string from fileutil.c */
+    int setoken(	/* Tokenize a string for easy decoding */
+    struct Tokens *tokens, /* Token structure returned */
+    char    *string, /* character string to tokenize */
+    char *cwhite);	/* additional whitespace characters
+             * if = tab, disallow spaces and commas */
+    int nextoken(	/* Get next token from tokenized string */
+    struct Tokens *tokens, /* Token structure returned */
+    char *token,	/* token (returned) */
+    int maxchars);	/* Maximum length of token */
+    int getoken(	/* Get specified token from tokenized string */
+    struct Tokens *tokens, /* Token structure returned */
+    int itok,	/* token sequence number of token
+             * if <0, get whole string after token -itok
+             * if =0, get whole string */
+    char *token,	/* token (returned) */
+    int maxchars);	/* Maximum length of token */
+
+/* Subroutines for translating dates and times in dateutil.c */
+
+    /* Subroutines to convert between floating point and vigesimal angles */
+
+    void ang2hr ( 	/* Fractional degrees to hours as hh:mm:ss.ss */
+    double angle,	/* Angle in fractional degrees */
+    int lstr,	/* Maximum number of characters in string */
+    char *string);	/* Character string (hh:mm:ss.ss returned) */
+    void ang2deg (	/* Fractional degrees to degrees as dd:mm:ss.ss */
+    double  angle,	/* Angle in fractional degrees */
+    int lstr,	/* Maximum number of characters in string */
+    char *string);	/* Character string (dd:mm:ss.ss returned) */
+    double deg2ang (	/* Degrees as dd:mm:ss.ss to fractional degrees */
+    char *angle);	/* Angle as dd:mm:ss.ss */
+    double hr2ang (	/* Hours as hh:mm:ss.ss to fractional degrees */
+    char *angle);	/* Angle in sexigesimal hours (hh:mm:ss.sss) */
+
+    /* Subroutines to convert from year and day of year */
+
+    void doy2dt(	/* Year and day of year to yyyy.mmdd hh.mmss */
+    int year,	/* Year */
+    double doy,	/* Day of year with fraction */
+    double *date,	/* Date as yyyy.mmdd (returned) */
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    double doy2ep(	/* Year and day of year to fractional year (epoch) */
+    int year,	/* Year */
+    double doy);	/* Day of year with fraction */
+    double doy2epb( /* year and day of year to Besselian epoch */
+    int year,	/* Year */
+    double doy);	/* Day of year with fraction */
+    double doy2epj( /* year and day of year to Julian epoch */
+    int year,	/* Year */
+    double doy);	/* Day of year with fraction */
+    char *doy2fd(	/* year and day of year to FITS date */
+    int year,	/* Year */
+    double doy);	/* Day of year with fraction */
+    double doy2jd( /* year and day of year to Julian Day */
+    int year,	/* Year */
+    double doy);	/* Day of year with fraction */
+    double doy2mjd( /* year and day of year to Modified Julian Day */
+    int year,	/* Year */
+    double doy);	/* Day of year with fraction */
+    double doy2ts( /* year and day of year to seconds since 1950.0 */
+    int year,	/* Year */
+    double doy);	/* Day of year with fraction */
+    int doy2tsi(	/* year and day of year to IRAF seconds since 1980-01-01 */
+    int year,	/* Year */
+    double doy);	/* Day of year with fraction */
+    time_t doy2tsu(	/* year and day of year to Unix seconds since 1970-01-01 */
+    int year,	/* Year */
+    double doy);	/* Day of year with fraction */
+
+    /* Subroutines to convert from date and time */
+
+    void dt2doy(	/* yyyy.mmdd hh.mmss to year and day of year */
+    double date,	/* Date as yyyy.mmdd
+             * yyyy = calendar year (e.g. 1973)
+             * mm = calendar month (e.g. 04 = april)
+             * dd = calendar day (e.g. 15) */
+    double time,	/* Time as hh.mmssxxxx
+             * if time<0, it is time as -(fraction of a day)
+             * hh = hour of day (0 .le. hh .le. 23)
+             * nn = minutes (0 .le. nn .le. 59)
+             * ss = seconds (0 .le. ss .le. 59)
+             * xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+    int *year,	/* Year (returned) */
+    double *doy);	/* Day of year with fraction (returned) */
+    double dt2ep(	/* yyyy.ddmm and hh.mmsss to fractional year (epoch) */
+    double date,	/* Date as yyyy.mmdd */
+    double time);	/* Time as hh.mmssxxxx */
+    double dt2epb(	/* yyyy.ddmm and hh.mmsss to Besselian epoch */
+    double date,	/* Date as yyyy.mmdd */
+    double time);	/* Time as hh.mmssxxxx */
+    double dt2epj(	/* yyyy.ddmm and hh.mmsss to Julian epoch */
+    double date,	/* Date as yyyy.mmdd */
+    double time);	/* Time as hh.mmssxxxx */
+    char *dt2fd(	/* yyyy.ddmm and hh.mmsss to FITS date string */
+    double date,	/* Date as yyyy.mmdd */
+    double time);	/* Time as hh.mmssxxxx */
+    void dt2i(		/* yyyy.ddmm and hh.mmsss to year, month, day, hrs, min, sec */
+    double date,	/* Date as yyyy.mmdd */
+    double time,	/* Time as hh.mmssxxxx */
+    int *iyr,	/* year (returned) */
+    int *imon,	/* month (returned) */
+    int *iday,	/* day (returned) */
+    int *ihr,	/* hours (returned) */
+    int *imn,	/* minutes (returned) */
+    double *sec,	/* seconds (returned) */
+    int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double dt2jd(	/* yyyy.ddmm and hh.mmsss to Julian Day */
+    double date,	/* Date as yyyy.mmdd */
+    double time);	/* Time as hh.mmssxxxx */
+    double dt2mjd(	/* yyyy.ddmm and hh.mmsss to Modified Julian Day */
+    double date,	/* Date as yyyy.mmdd */
+    double time);	/* Time as hh.mmssxxxx */
+    double dt2ts(	/* yyyy.ddmm and hh.mmsss to seconds since 1950.0 */
+    double date,	/* Date as yyyy.mmdd */
+    double time);	/* Time as hh.mmssxxxx */
+    int dt2tsi(		/* yyyy.ddmm and hh.mmsss to IRAF seconds since 1980-01-01 */
+    double date,	/* Date as yyyy.mmdd */
+    double time);	/* Time as hh.mmssxxxx */
+    time_t dt2tsu(	/* yyyy.ddmm and hh.mmsss to Unix seconds since 1970-01-01 */
+    double date,	/* Date as yyyy.mmdd */
+    double time);	/* Time as hh.mmssxxxx */
+
+    /* Subroutines to convert from epoch (various types of fractional year) */
+
+    void ep2dt(		/* Fractional year to yyyy.mmdd hh.mmssss */
+    double epoch,	/* Date as fractional year */
+    double *date,	/* Date as yyyy.mmdd (returned) */
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    void epb2dt(	/* Besselian epoch to yyyy.mmdd hh.mmssss */
+    double  epoch,  /* Besselian epoch (fractional 365.242198781-day years) */
+    double *date,	/* Date as yyyy.mmdd (returned) */
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    void epj2dt(	/* Julian epoch to yyyy.mmdd hh.mmssss */
+    double  epoch,  /* Julian epoch (fractional 365.25-day years) */
+    double *date,	/* Date as yyyy.mmdd (returned)*/
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    char *ep2fd(	/* Fractional year to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+    double epoch);	/* Date as fractional year */
+    char *epb2fd(	/* Besselian epoch to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+    double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    char *epj2fd(	/* Julian epoch to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+    double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    void ep2i(		/* Fractional year to year, month, day, hours, min., sec. */
+    double epoch,	/* Date as fractional year */
+    int *iyr,	/* year (returned) */
+    int *imon,	/* month (returned) */
+    int *iday,	/* day (returned) */
+    int *ihr,	/* hours (returned) */
+    int *imn,	/* minutes (returned) */
+    double *sec,	/* seconds (returned) */
+    int ndsec);	/* Number of decimal places in seconds (0=int) */
+    void epb2i(		/* Besselian epoch to year, month, day, hours, min., sec. */
+    double  epoch,	/* Besselian epoch (fractional 365.242198781-day years) */
+    int *iyr,	/* year (returned) */
+    int *imon,	/* month (returned) */
+    int *iday,	/* day (returned) */
+    int *ihr,	/* hours (returned) */
+    int *imn,	/* minutes (returned) */
+    double *sec,	/* seconds (returned) */
+    int ndsec);	/* Number of decimal places in seconds (0=int) */
+    void epj2i(		/* Julian epoch to year, month, day, hours, min., sec. */
+    double  epoch,  /* Julian epoch (fractional 365.25-day years) */
+    int *iyr,	/* year (returned) */
+    int *imon,	/* month (returned) */
+    int *iday,	/* day (returned) */
+    int *ihr,	/* hours (returned) */
+    int *imn,	/* minutes (returned) */
+    double *sec,	/* seconds (returned) */
+    int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double ep2jd(	/* Fractional year to Julian Date */
+    double epoch);	/* Date as fractional year */
+    double epb2jd(	/* Besselian epoch to Julian Date */
+    double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2jd(	/* Julian epoch to Julian Date */
+    double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    double ep2mjd(	/* Fractional year to Modified Julian Date */
+    double epoch);	/* Date as fractional year */
+    double epb2mjd(	/* Besselian epoch to Modified Julian Date */
+    double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2mjd(	/* Julian epoch to Modified Julian Date */
+    double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    double ep2epb(	/* Fractional year to Besselian epoch */
+    double epoch);	/* Date as fractional year */
+    double ep2epj(	/* Fractional year to Julian epoch */
+    double epoch);	/* Date as fractional year */
+    double epb2epj(	/* Besselian epoch to Julian epoch */
+    double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2epb(	/* Julian epoch to Besselian epoch */
+    double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    double epb2ep(	/* Besselian epoch to fractional year */
+    double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2ep(	/* Julian epoch to fractional year */
+    double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    double ep2ts(	/* Fractional year to seconds since 1950.0 */
+    double epoch);	/* Date as fractional year */
+    double epb2ts(	/* Besselian epoch to seconds since 1950.0 */
+    double  epoch);  /* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2ts( /* Julian epoch to seconds since 1950.0 */
+    double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+
+    /* Convert from FITS standard date string */
+
+    void fd2dt(		/* FITS standard date string to date and time */
+    char *string,	/* FITS date string, which may be:
+             * fractional year
+             * dd/mm/yy (FITS standard before 2000)
+             * dd-mm-yy (nonstandard use before 2000)
+             * yyyy-mm-dd (FITS standard after 1999)
+             * yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+    double *date,	/* Date as yyyy.mmdd (returned)*/
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    void fd2doy(	/* FITS standard date string to year, day of year */
+    char *string,	/* FITS date string */
+    int *year,	/* Year (returned) */
+    double *doy);	/* Day of year with fraction (returned) */
+    double fd2ep(	/* FITS standard date string to fractional year (epoch) */
+    char *string);	/* FITS date string */
+    double fd2epb(	/* FITS standard date string to Besselian epoch */
+    char *string);	/* FITS date string */
+    double fd2epj(	/* FITS standard date string to Julian epoch */
+    char *string);	/* FITS date string */
+    char *fd2fd(	/* Any FITS standard date string to ISO FITS date string */
+    char *string);	/* FITS date string */
+    char *fd2of(	/* Any FITS standard date string to old FITS date and time */
+    char *string);	/* FITS date string */
+    char *fd2ofd(	/* Any FITS standard date string to old FITS date string */
+    char *string);	/* FITS date string */
+    char *fd2oft(	/* Any FITS standard date string to old FITS time string */
+    char *string);	/* FITS date string */
+    void fd2i(		/* FITS standard date string to year, mon, day, hrs, min, sec */
+    char *string,	/* FITS date string */
+    int *iyr,	/* year (returned) */
+    int *imon,	/* month (returned) */
+    int *iday,	/* day (returned) */
+    int *ihr,	/* hours (returned) */
+    int *imn,	/* minutes (returned) */
+    double *sec,	/* seconds (returned) */
+    int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double fd2jd(	/* FITS standard date string to Julian Day */
+    char *string);	/* FITS date string */
+    double fd2mjd(	/* FITS standard date string to Modified Julian Day */
+    char *string);	/* FITS date string */
+    double fd2ts(	/* FITS standard date to seconds since 1950-01-01 */
+    char *string);	/* FITS date string */
+    int fd2tsi(		/* FITS standard date to IRAF seconds since 1980-01-01 */
+    char *string);	/* FITS date string */
+    time_t fd2tsu(	/* FITS standard date to Unix seconds since 1970-01-01 */
+    char *string);	/* FITS date string */
+
+    /* Convert from Julian Day */
+
+    void jd2doy(	/* Julian Day to year and day of year */
+    double dj,	/* Julian Day */
+    int *year,	/* Year (returned) */
+    double *doy);	/* Day of year with fraction (returned) */
+    void jd2dt(		/* Julian Day to yyyy.mmdd hh.mmssss */
+    double dj,	/* Julian Day */
+    double *date,	/* Date as yyyy.mmdd (returned)*/
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    double jd2ep(	/* Julian Day to fractional year */
+    double dj);	/* Julian Day */
+    double jd2epb(	/* Julian Day to Besselian epoch */
+    double dj);	/* Julian Day */
+    double jd2epj(	/* Julian Day to Julian epoch */
+    double dj);	/* Julian Day */
+    char *jd2fd(	/* Julian Day to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+    double dj);	/* Julian Day */
+    void jd2i(		/* Julian Day to year, month, day, hours, min., sec. */
+    double dj,	/* Julian Day */
+    int *iyr,	/* year (returned) */
+    int *imon,	/* month (returned) */
+    int *iday,	/* day (returned) */
+    int *ihr,	/* hours (returned) */
+    int *imn,	/* minutes (returned) */
+    double *sec,	/* seconds (returned) */
+    int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double jd2mjd( /* Julian Day to Modified Julian day */
+    double dj);	/* Julian Day */
+    double jd2ts(	/* Julian Day to seconds since 1950.0 */
+    double dj);	/* Julian Day */
+    time_t jd2tsu( /* Julian Day to Unix seconds since 1970-01-01T00:00 */
+    double dj);	/* Julian Day */
+    int jd2tsi( /* Julian Day to IRAF seconds since 1980-01-01T00:00 */
+    double dj);	/* Julian Day */
+
+    /* Convert current local time to various formats */
+
+    void lt2dt(		/* Current local time to date (yyyy.mmdd), time (hh.mmsss) */
+    double *date,	/* Date as yyyy.mmdd (returned) */
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    char *lt2fd(void);	/* Current local time to FITS ISO date string */
+    int lt2tsi(void);	/* Current local time to IRAF seconds since 1980-01-01T00:00 */
+    time_t lt2tsu(void); /* Current local time to Unix seconds since 1970-01-01T00:00 */
+    double lt2ts(void);	/* Current local time to IRAF seconds since 1950-01-01T00:00 */
+
+    /* Convert from Modified Julian Day (JD - 2400000.5) */
+
+    void mjd2doy(	/* Modified Julian Day to year and day of year */
+    double dj,	/* Modified Julian Day */
+    int *year,	/* Year (returned) */
+    double *doy);	/* Day of year with fraction (returned) */
+    void mjd2dt(	/* Modified Julian Day to yyyy.mmdd hh.mmssss */
+    double dj,	/* Modified Julian Date */
+    double *date,	/* Date as yyyy.mmdd (returned)*/
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    double mjd2ep( /* Modified Julian Day to fractional year */
+    double dj);	/* Modified Julian Date */
+    double mjd2epb( /* Modified Julian Day to Besselian epoch */
+    double dj);	/* Modified Julian Date */
+    double mjd2epj( /* Modified Julian Day to Julian epoch */
+    double dj);	/* Modified Julian Date */
+    char *mjd2fd(	/* Modified Julian Day to FITS date yyyy-mm-ddThh:mm:ss.ss */
+    double dj);	/* Modified Julian Date */
+    void mjd2i(	/* Modified Julian Day to year, month, day, hours, min, sec */
+    double dj,	/* Modified Julian Date */
+    int *iyr,	/* year (returned) */
+    int *imon,	/* month (returned) */
+    int *iday,	/* day (returned) */
+    int *ihr,	/* hours (returned) */
+    int *imn,	/* minutes (returned) */
+    double *sec,	/* seconds (returned) */
+    int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double mjd2jd( /* Modified Julian Day to Julian day */
+    double dj);	/* Modified Julian Date */
+    double mjd2ts( /* Modified Julian Day to seconds since 1950.0 */
+    double dj);	/* Modified Julian Date */
+
+    /* Convert from seconds since 1950-01-01 0:00 (JPL Ephemeris time) */
+
+    void ts2dt(		/* Seconds since 1950.0 to yyyy.mmdd hh.mmssss */
+    double tsec,	/* seconds since 1950.0 */
+    double *date,	/* Date as yyyy.mmdd (returned)*/
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    double ts2ep(	/* Seconds since 1950.0 to fractional year */
+    double tsec);	/* seconds since 1950.0 */
+    double ts2epb( /* Seconds since 1950.0 to Besselian epoch */
+    double tsec);	/* seconds since 1950.0 */
+    double ts2epj( /* Seconds since 1950.0 to Julian epoch */
+    double tsec);	/* seconds since 1950.0 */
+    char *ts2fd(	/* Seconds since 1950.0 to FITS date, yyyy-mm-ddT00:00:00.000 */
+    double tsec);	/* seconds since 1950.0 */
+    void ts2i(	/* Seconds since 1950.0 to year, month, day, hours, min, sec */
+    double tsec,	/* seconds since 1950.0 */
+    int *iyr,	/* year (returned) */
+    int *imon,	/* month (returned) */
+    int *iday,	/* day (returned) */
+    int *ihr,	/* hours (returned) */
+    int *imn,	/* minutes (returned) */
+    double *sec,	/* seconds (returned) */
+    int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double ts2jd(	/* Seconds since 1950.0 to Julian Day */
+    double tsec);	/* seconds since 1950.0 */
+    double ts2mjd( /* Seconds since 1950.0 to Modified Julian Day */
+    double tsec);	/* seconds since 1950.0 */
+
+    /* Convert from IRAF time (seconds since 1980-01-01 0:00 UT) */
+
+    char *tsi2fd(	/* Seconds since 1980-01-01 to FITS standard date string */
+    int isec);	/* Seconds past 1980-01-01 */
+    double tsi2ts( /* Seconds since 1980-01-01 to seconds since 1950-01-01 */
+    int isec);	/* Seconds past 1980-01-01 */
+    void tsi2dt(	/* Seconds since 1980-01-01 to date yyyy.mmdd, time hh.mmssss */
+    int isec,	/* Seconds past 1980-01-01 */
+    double *date,	/* Date as yyyy.mmdd (returned) */
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+
+    /* Convert from Unix time (seconds since 1970-01-01 0:00 UT) */
+
+    void tsu2dt(	/* Seconds since 1970-01-01 to date yyyy.ddmm, time hh.mmsss */
+    time_t isec,	/* Seconds past 1970-01-01 */
+    double *date,	/* Date as yyyy.mmdd (returned) */
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    char *tsu2fd(	/* Seconds since 1970-01-01 to FITS standard date string */
+    time_t isec);	/* Seconds past 1970-01-01 */
+    double tsu2ts( /* Seconds since 1970-01-01 to seconds since 1950-01-01 */
+    time_t isec);	/* Seconds past 1970-01-01 */
+    int tsu2tsi(	/* Seconds since 1970-01-01 to local seconds since 1980-01-01 */
+    time_t isec);	/* Seconds past 1970-01-01 */
+
+    /* Convert times within a day */
+
+    char *tsd2fd(	/* Seconds since start of day to FITS standard time string */
+    double tsec);	/* Seconds since start of day */
+    double tsd2dt(	/* Seconds since start of day to hh.mmsssss */
+    double tsec);	/* Seconds since start of day */
+
+    /* Convert from current Universal Time */
+
+    void ut2dt(		/* Current Universal Time to date (yyyy.mmdd), time (hh.mmsss) */
+    double *date,	/* Date as yyyy.mmdd (returned) */
+    double *time);	/* Time as hh.mmssxxxx (returned) */
+    void ut2doy(	/* Current Universal Time to year, day of year */
+    int *year,	/* Year (returned) */
+    double *doy);	/* Day of year (returned) */
+    double ut2ep(void);	/* Current Universal Time to fractional year */
+    double ut2epb(void); /* Current Universal Time to Besselian Epoch */
+    double ut2epj(void); /* Current Universal Time to Julian Epoch */
+    char *ut2fd(void);	/* Current Universal Time to FITS ISO date string */
+    double ut2jd(void);	/* Current Universal Time to Julian Date */
+    double ut2mjd(void); /* Current Universal Time to Modified Julian Date */
+    int ut2tsi(void);	/* Current UT to IRAF seconds since 1980-01-01T00:00 */
+    time_t ut2tsu(void); /* Current UT to Unix seconds since 1970-01-01T00:00 */
+    double ut2ts(void);	/* Current UT to seconds since 1950-01-01T00:00 */
+
+    int isdate(		/* Return 1 if string is FITS old or ISO date */
+    char *string);	/* Possible FITS date string, which may be:
+             *  dd/mm/yy (FITS standard before 2000)
+             *  dd-mm-yy (nonstandard FITS use before 2000)
+             *  yyyy-mm-dd (FITS standard after 1999)
+             *  yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+
+    /* Ephemeris time conversions (ET, TT, and TDT) */
+
+    char *et2fd(	/* ET (or TDT or TT) in FITS format to UT in FITS format */
+    char *string);	/* Ephemeris Time as FITS date string (E not T) */
+    char *fd2et(	/* UT in FITS format to ET (or TDT or TT) in FITS format */
+    char *string);	/* FITS date string */
+    void dt2et(		/* yyyy.ddmm and hh.mmsss to Ephemeris Time */
+    double *date,	/* Date as yyyy.mmdd */
+    double *time);	/* Time as hh.mmssxxxx
+             *if time<0, it is time as -(fraction of a day) */
+    double jd2jed(	/* Convert from Julian Date to Julian Ephemeris Date */
+    double dj);	/* Julian Date */
+    double jed2jd(	/* Convert from Julian Ephemeris Date to Julian Date */
+    double dj);	/* Julian Ephemeris Date */
+    double ets2ts(	/* ET in seconds since 1950-01-01 to UT in same format */
+    double tsec);	/* ET in seconds since 1950-01-01 */
+    double ts2ets(	/* UT in seconds since 1950-01-01 to ET in same format */
+    double tsec);	/* UT in seconds since 1950-01-01 */
+    void edt2dt(	/* yyyy.ddmm and hh.mmsss Ephemeris Time to UT */
+    double *date,	/* Date as yyyy.mmdd */
+    double *time);	/* Time as hh.mmssxxxx
+             * If time<0, it is time as -(fraction of a day) */
+    double utdt(	/* Compute difference between UT and dynamical time (ET-UT) */
+    double dj);	/* Julian Date (UT) */
+
+    /* Sidereal Time conversions */
+
+    char *fd2gst(	/* Convert from FITS UT date to Greenwich Sidereal Time */
+    char *string);	/* FITS date string */
+    void dt2gst(	/* Convert from UT as yyyy.mmdd hh.mmssss to Greenwich Sidereal Time */
+    double *date,	/* Date as yyyy.mmdd */
+    double *time);	/* Time as hh.mmssxxxx
+             * If time<0, it is time as -(fraction of a day) */
+    double jd2gst(	/* Calculate Greenwich Sidereal Time given Julian Date */
+    double dj);	/* Julian Date (UT) */
+    double ts2gst(	/* Calculate Greenwich Sidereal Time given Universal Time */
+    double tsec);	/* Time since 1950.0 in UT seconds */
+    char *fd2lst(	/* Convert from FITS UT date to Local Sidereal Time */
+    char *string);	/* FITS date string */
+    void dt2lst(	/* Convert from UT as yyyy.mmdd hh.mmssss to Local Sidereal Time */
+    double *date,	/* Date as yyyy.mmdd */
+    double *time);	/* Time as hh.mmssxxxx
+             * If time<0, it is time as -(fraction of a day) */
+    double ts2lst(	/* Calculate Local Sidereal Time given Universal Time */
+    double tsec);	/* Time since 1950.0 in UT seconds */
+    double jd2lst(	/* Calculate Local Sidereal Time given Julian Date */
+    double dj);	/* Julian Date (UT) */
+    double eqeqnx(	/* Compute equation of eqinoxes from Julian Date */
+    double dj);	/* Julian Date (UT) */
+    char *fd2mst(	/* Convert from FITS UT date to Mean Sidereal Time */
+    char *string);	/* FITS date string */
+    double jd2mst(	/* Convert from Julian Date to Mean Sidereal Time */
+    double dj);	/* Julian Date (UT) */
+    double jd2mst2(	/* Convert from Julian Date to Mean Sidereal Time */
+    double dj);	/* Julian Date (UT) */
+    void dt2mst(	/* Convert from UT as yyyy.mmdd hh.mmssss to Mean Sidereal Time */
+    double *date,	/* Date as yyyy.mmdd */
+    double *time);	/* Time as hh.mmssxxxx
+             * If time<0, it is time as -(fraction of a day) */
+    double lst2dt(	/* Calculate UT as hh.mmsss given UT date and
+             * Local Sidereal Time */
+    double date0,   /* UT date as yyyy.mmdd */
+    double time0);   /* LST as hh.mmssss */
+    double lst2jd(	/* Calculate UT as Julian Date given UT date and
+             * Local Sidereal Time */
+    double sdj);     /* Julian Date of desired day at 0:00 UT + sidereal time */
+    char *lst2fd(	/* Calculate FITS UT date and time given UT date and
+             * Local Sidereal Time */
+    char *string);	/* UT Date, LST as yyyy-mm-ddShh:mm:ss.ss */
+    char *gst2fd(	/* Calculate FITS UT date and time given Greenwich Sidereal Time */
+    char *string);	/* UT Date, GST as yyyy-mm-ddShh:mm:ss.ss */
+    double gst2jd(	/* Calculate FITS UT Julian Date given Greenwich Sidereal Time */
+    double sdj);	/* UT Date, GST as Julian Date */
+    char *mst2fd(	/* Calculate FITS UT date and time given Mean Sidereal Time */
+    char *string);	/* UT Date, MST as yyyy-mm-ddShh:mm:ss.ss */
+    double mst2jd(	/* Calculate FITS UT Julian Date given Mean Sidereal Time */
+    double sdj);	/* UT Date, MST as Julian Date */
+    double ts2mst(	/* Calculate Mean Sidereal Time given Universal Time */
+    double tsec);	/* time since 1950.0 in UT seconds */
+    void setlongitude( /* Longitude for sidereal time in or out */
+    double longitude); /* longitude of observatory in degrees (+=west) */
+    void compnut(	/* Compute nutation in longitude and obliquity and mean obliquity*/
+    double dj,	/* TDB (loosely ET or TT) as Julian Date */
+    double *dpsi,	/* Nutation in longitude in radians (returned) */
+    double *deps,	/* Nutation in obliquity in radians (returned) */
+    double *eps0);	/* Mean obliquity in radians (returned) */
+
+    /* Heliocentric Julian Date conversions */
+
+    double mjd2mhjd(	/* Convert from Modified Julian Date to Heliocentric MJD */
+    double mjd,	/* Julian date (geocentric) */
+    double ra,	/* Right ascension (degrees) */
+    double dec,	/* Declination (degrees) */
+    int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+    double mjd2hjd( /* Convert from Modified Julian Date to Heliocentric JD */
+    double mjd,	/* Julian date (geocentric) */
+    double ra,	/* Right ascension (degrees) */
+    double dec,	/* Declination (degrees) */
+    int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+    double mhjd2mjd(	/* Convert from Heliocentric Modified Julian Date to MJD */
+    double mhjd,	/* Modified Heliocentric Julian date */
+    double ra,	/* Right ascension (degrees) */
+    double dec,	/* Declination (degrees) */
+    int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+    double jd2hjd( /* Convert from Julian Date to Heliocentric Julian Date */
+    double  dj,     /* Julian date (geocentric) */
+    double ra,	/* Right ascension (degrees) */
+    double dec,	/* Declination (degrees) */
+    int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+    double hjd2jd( /* Convert from Heliocentric Julian Date to Julian Date */
+    double  dj,	/* Heliocentric Julian date */
+    double ra,	/* Right ascension (degrees) */
+    double dec,	/* Declination (degrees) */
+    int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+
+    void setdatedec(	/* Set number of decimal places in FITS dates */
+    int nd);	/* Number of decimal places in FITS dates */
+
+#else /* K&R prototypes */
+
+/* FITS file access subroutines in fitsfile.c */
+extern int fitsropen();
+extern char *fitsrhead();
+extern char *fitsrtail();
+extern char *fitsrimage();
+extern char *fitsrfull();
+extern char *fitsrsect();
+extern int fitswhead();
+extern int fitswexhead();
+extern int fitswext();
+extern int fitswhdu();
+extern int fitswimage();
+extern int fitscimage();
+extern int isfits();		/* Return 1 if file is a FITS file */
+extern void fitserr();          /* Print FITS error message to stderr */
+extern void setfitsinherit();	/* Set flag to append primary data header */
+extern int fitsheadsize();	/* Return size of fitsheader in bytes */
+
+/* FITS table file access subroutines in fitsfile.c */
+extern int fitsrtopen();
+extern int fitsrthead();
+extern void fitsrtlset();
+extern int fitsrtline();
+extern short ftgeti2();
+extern int ftgeti4();
+extern float ftgetr4();
+extern double ftgetr8();
+extern int ftgetc();
+extern void moveb();	/* Copy nbytes bytes from source+offs to dest+offd */
+
+/* IRAF file access subroutines in imhfile.c */
+extern char *irafrhead();
+extern char *irafrimage();
+extern int irafwhead();
+extern int irafwimage();
+extern int isiraf();
+extern char *iraf2fits();
+extern char *fits2iraf();
+
+/* Image pixel access subroutines in imio.c */
+extern double getpix();	/* Read one pixel from any data type 2-D array (0,0)*/
+extern double getpix1(); /* Read one pixel from any data type 2-D array (1,1)*/
+extern double maxvec(); /* Get maximum value in vector from a image */
+extern double minvec(); /* Get minimum value in vector from a image */
+extern void putpix();	/* Write one pixel to any data type 2-D array (0,0)*/
+extern void putpix1();	/* Write one pixel to any data type 2-D array (1,1) */
+extern void addpix();	/* Add to one pixel in any data type 2-D array (0,0)*/
+extern void addpix1();	/* Add to one pixel in any data type 2-D array (1,1)*/
+extern void movepix();	/* Move one pixel value between two 2-D arrays (0,0) */
+extern void movepix1();	/* Move one pixel value between two 2-D arrays (1,1) */
+extern void addvec();	/* Add constant to vector from 2-D array */
+extern void multvec();	/* Multiply vector from 2-D array by a constant */
+extern void getvec();	/* Read vector from 2-D array */
+extern void putvec();	/* Write vector into 2-D array */
+extern void fillvec();   /* Write constant into a vector */
+extern void fillvec1();   /* Write constant into a vector */
+extern void imswap();	/* Swap alternating bytes in a vector */
+extern void imswap2();	/* Swap bytes in a vector of 2-byte (short) integers */
+extern void imswap4();	/* Reverse bytes in a vector of 4-byte numbers */
+extern void imswap8();	/* Reverse bytes in a vector of 8-byte numbers */
+extern int imswapped();	/* Return 1 if machine byte order is not FITS order */
+
+/* File utilities from fileutil.c */
+extern int getfilelines();
+extern char *getfilebuff();
+extern int getfilesize();
+extern int isimlist();
+extern int isimlistd();
+extern int isfilelist();
+extern int isfile();
+extern int istiff();
+extern int isjpeg();
+extern int isgif();
+extern int next_line();
+extern int first_token();
+
+/* Subroutines for access to tokens within a string from fileutil.c */
+int setoken();		/* Tokenize a string for easy decoding */
+int nextoken();		/* Get next token from tokenized string */
+int getoken();		/* Get specified token from tokenized string */
+
+/* Subroutines for translating dates and times in dateutil.c */
+
+void ang2hr();		/* Fractional degrees to hours as hh:mm:ss.ss */
+void ang2deg();		/* Fractional degrees to degrees as dd:mm:ss.ss */
+double deg2ang();	/* Degrees as dd:mm:ss.ss to fractional degrees */
+double hr2ang();	/* Hours as hh:mm:ss.ss to fractional degrees */
+
+void doy2dt();	/* year and day of year to yyyy.mmdd hh.mmss */
+double doy2ep(); /* year and day of year to fractional year (epoch) */
+double doy2epb(); /* year and day of year to Besselian epoch */
+double doy2epj(); /* year and day of year to Julian epoch */
+char *doy2fd();	/* year and day of year to FITS date */
+double doy2jd(); /* year and day of year to Julian date */
+double doy2mjd(); /* year and day of year to modified Julian date */
+double doy2ts(); /* year and day of year to seconds since 1950.0 */
+int doy2tsi();	/* year and day of year to IRAF seconds since 1980-01-01 */
+
+time_t doy2tsu();	/* year and day of year to Unix seconds since 1970-01-01 */
+void dt2doy();	/* yyyy.mmdd hh.mmss to year and day of year */
+double dt2ep();	/* yyyy.ddmm and hh.mmsss to fractional year (epoch) */
+double dt2epb(); /* yyyy.ddmm and hh.mmsss to Besselian epoch */
+double dt2epj(); /* yyyy.ddmm and hh.mmsss to Julian epoch */
+char *dt2fd();	/* yyyy.ddmm and hh.mmsss to FITS date string */
+void dt2i();	/* yyyy.ddmm and hh.mmsss to year, month, day, hrs, min, sec */
+double dt2jd();	/* yyyy.ddmm and hh.mmsss to Julian date */
+double dt2mjd(); /* yyyy.ddmm and hh.mmsss to modified Julian date */
+double dt2ts();	/* yyyy.ddmm and hh.mmsss to seconds since 1950.0 */
+int dt2tsi();	/* yyyy.ddmm and hh.mmsss to IRAF seconds since 1980-01-01 */
+time_t dt2tsu();	/* yyyy.ddmm and hh.mmsss to Unix seconds since 1970-01-01 */
+
+void ep2dt();	/* Fractional year to yyyy.mmdd hh.mmssss */
+void epb2dt();	/* Besselian epoch to yyyy.mmdd hh.mmssss */
+void epj2dt();	/* Julian epoch to yyyy.mmdd hh.mmssss */
+char *ep2fd();	/* Fractional year to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+char *epb2fd();	/* Besselian epoch to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+char *epj2fd();	/* Julian epoch to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+void ep2i();	/* Fractional year to year, month, day, hours, min., sec. */
+void epb2i();	/* Besselian epoch to year, month, day, hours, min., sec. */
+void epj2i();	/* Julian epoch to year, month, day, hours, min., sec. */
+double ep2jd();	/* Fractional year to Julian Date */
+double epb2jd(); /* Besselian epoch to Julian Date */
+double epj2jd(); /* Julian epoch to Julian Date */
+double ep2mjd(); /* Fractional year to modified Julian Date */
+double epb2mjd(); /* Besselian epoch to modified Julian Date */
+double epj2mjd(); /* Julian epoch to modified Julian Date */
+double ep2epb(); /* Fractional year to Besselian epoch */
+double ep2epj(); /* Fractional year to Julian epoch */
+double epb2epj(); /* Besselian epoch to Julian epoch */
+double epj2epb(); /* Julian epoch to Besselian epoch */
+double epb2ep(); /* Besselian epoch to fractional year */
+double epj2ep(); /* Julian epoch to fractional year */
+double ep2ts();	/* Fractional year to seconds since 1950.0 */
+double epb2ts(); /* Besselian epoch to seconds since 1950.0 */
+double epj2ts(); /* Julian epoch to seconds since 1950.0 */
+
+void fd2dt();	/* FITS standard date string to Julian date */
+void fd2doy();	/* FITS standard date string to year, day of year */
+double fd2ep();	/* FITS standard date string to fractional year (epoch) */
+double fd2epb(); /* FITS standard date string to Besselian epoch */
+double fd2epj(); /* FITS standard date string to Julian epoch */
+char *fd2fd();	/* Any FITS standard date string to ISO FITS date string */
+char *fd2of();	/* Any FITS standard date string to old FITS date and time */
+char *fd2ofd();	/* Any FITS standard date string to old FITS date string */
+char *fd2oft(); /* Any FITS standard date string to old FITS time string */
+void fd2i();	/* FITS standard date string to year, mon, day, hrs, min, sec */
+double fd2jd();	/* FITS standard date string to Julian date */
+double fd2mjd(); /* FITS standard date string to modified Julian date */
+double fd2ts();	/* FITS standard date to seconds since 1950-01-01 */
+int fd2tsi();	/* FITS standard date to IRAF seconds since 1980-01-01 */
+time_t fd2tsu();	/* FITS standard date to Unix seconds since 1970-01-01 */
+void jd2doy();	/* Julian date to year and day of year */
+void jd2dt();	/* Julian date to yyyy.mmdd hh.mmssss */
+double jd2ep();	/* Julian date to fractional year */
+double jd2epb(); /* Julian date to Besselian epoch */
+double jd2epj(); /* Julian date to Julian epoch */
+char *jd2fd();	/* Julian date to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+void jd2i();	/* Julian date to year, month, day, hours, min., sec. */
+double jd2mjd(); /* Julian date to modified Julian date */
+double jd2ts();	/* Julian date to seconds since 1950.0 */
+time_t jd2tsu(); /* Julian date to Unix seconds since 1970-01-01T00:00 */
+int jd2tsi(); /* Julian date to IRAF seconds since 1980-01-01T00:00 */
+
+void lt2dt();	/* Current local time to date (yyyy.mmdd), time (hh.mmsss) */
+char *lt2fd();	/* Current local time to FITS ISO date string */
+int lt2tsi();	/* Current local time to IRAF seconds since 1980-01-01T00:00 */
+time_t lt2tsu(); /* Current local time to Unix seconds since 1970-01-01T00:00 */
+double lt2ts(); /* Current local time to IRAF seconds since 1950-01-01T00:00 */
+
+void mjd2doy(); /* Convert from Modified Julian Date to Day of Year */
+void mjd2dt();	/* Modified Julian date to yyyy.mmdd hh.mmssss */
+double mjd2ep(); /* Modified Julian date to fractional year */
+double mjd2epb(); /* Modified Julian date to Besselian epoch */
+double mjd2epj(); /* Modified Julian date to Julian epoch */
+char *mjd2fd();	/* Modified Julian date to FITS date yyyy-mm-ddThh:mm:ss.ss */
+void mjd2i();	/* Modified Julian date to year, month, day, hours, min, sec */
+double mjd2jd(); /* Modified Julian date to Julian date */
+double mjd2ts(); /* Modified Julian date to seconds since 1950.0 */
+
+void ts2dt();	/* Seconds since 1950.0 to yyyy.mmdd hh.mmssss */
+double ts2ep();	/* Seconds since 1950.0 to fractional year */
+double ts2epb(); /* Seconds since 1950.0 to Besselian epoch */
+double ts2epj(); /* Seconds since 1950.0 to Julian epoch */
+char *ts2fd();	/* Seconds since 1950.0 to FITS date, yyyy-mm-ddT00:00:00.000 */
+void ts2i();	/* Seconds since 1950.0 to year, month, day, hours, min, sec */
+double ts2jd();	/* Seconds since 1950.0 to Julian date */
+double ts2mjd(); /* Seconds since 1950.0 to modified Julian date */
+char *tsi2fd();	/* Seconds since 1980-01-01 to FITS standard date string */
+double tsi2ts(); /* Seconds since 1980-01-01 to seconds since 1950-01-01 */
+double tsi2ts(); /* Seconds since 1980-01-01 to seconds since 1950-01-01 */
+void tsi2dt(); /* Seconds since 1980-01-01 to date yyyy.mmdd, time hh.mmssss */
+void tsu2dt();	/* Seconds since 1970-01-01 to date yyyy.ddmm, time hh.mmsss */
+char *tsu2fd();	/* Seconds since 1970-01-01 to FITS standard date string */
+char *tsd2fd();	/* Seconds since start of day to FITS standard time string */
+double tsd2dt(); /* Seconds since start of day to hh.mmsssss */
+double tsu2ts(); /* Seconds since 1970-01-01 to seconds since 1950-01-01 */
+int tsu2tsi();	/* Seconds since 1970-01-01 to local seconds since 1980-01-01 */
+int isdate();	/* Return 1 if string is FITS old or ISO date */
+void ut2dt();	/* Current Universal Time to date (yyyy.mmdd), time (hh.mmsss) */
+void ut2doy(); /* Current Universal Time to year, day of year */
+double ut2ep(); /* Current Universal Time to fractional year */
+double ut2epb(); /* Current Universal Time to Besselian Epoch */
+double ut2epj(); /* Current Universal Time to Julian Epoch */
+char *ut2fd();	/* Current Universal Time to FITS ISO date string */
+double ut2jd();	/* Current Universal Time to Julian Date */
+double ut2mjd(); /* Current Universal Time to Modified Julian Date */
+int ut2tsi();	/* Current UT to IRAF seconds since 1980-01-01T00:00 */
+time_t ut2tsu();	/* Current UT to Unix seconds since 1970-01-01T00:00 */
+double ut2ts(); /* Current UT to IRAF seconds since 1950-01-01T00:00 */
+int sts2c();	/* Replaces spaces in a string with a specified character */
+int stc2s();	/* Replaces a specified character in a string with spaces */
+char *et2fd();	/* ET (or TDT or TT) in FITS format to UT in FITS format */
+char *fd2et();	/* UT in FITS format to ET (or TDT or TT) in FITS format */
+double jd2jed(); /* Convert from Julian Date to Julian Ephemeris Date */
+double jed2jd(); /* Convert from Julian Ephemeris Date to Julian Date */
+double ets2ts(); /* ET in seconds since 1950-01-01 to UT in same format */
+double ts2ets(); /* UT in seconds since 1950-01-01 to ET in same format */
+void dt2et();	/* yyyy.ddmm and hh.mmsss to Ephemeris Time */
+void edt2dt(); /* yyyy.ddmm and hh.mmsss Ephemeris Time to UT */
+double utdt();	/* Compute difference between UT and dynamical time (ET-UT) */
+char *fd2gst();	/* Convert from FITS UT date to Greenwich Sidereal Time */
+void dt2gst();	/* Convert from UT as yyyy.mmdd hh.mmssss to Greenwich Sidereal Time */
+double jd2gst(); /* Calculate Greenwich Sidereal Time given Julian Date */
+double ts2gst(); /* Calculate Greenwich Sidereal Time given Universal Time */
+char *fd2lst();	/* Convert from FITS UT date to Local Sidereal Time */
+void dt2lst();	/* Convert from UT as yyyy.mmdd hh.mmssss to Local Sidereal Time */
+double ts2lst(); /* Calculate Local Sidereal Time given Universal Time */
+double jd2lst(); /* Calculate Local Sidereal Time given Julian Date */
+double eqeqnx(); /* Compute equation of eqinoxes from Julian Date */
+char *fd2mst();	/* Convert from FITS UT date to Mean Sidereal Time */
+double jd2mst(); /* Convert from Julian Date to Mean Sidereal Time */
+double jd2mst2(); /* Convert from Julian Date to Mean Sidereal Time */
+void dt2mst();	/* Convert from UT as yyyy.mmdd hh.mmssss to Mean Sidereal Time */
+double lst2ts(); /* Calculate Universal Time given Local Sidereal Time */
+double lst2dt(); /* Calculate UT as yyyy.mmdd hh.mmsss given UT date and Local Sidereal Time */
+double lst2jd(); /* Calculate UT as Julian Date given UT date and Local Sidereal Time */
+char *lst2fd(); /* Calculate FITS UT date and time given UT date and Local Sidereal Time */
+char *gst2fd(); /* Calculate FITS UT date and time given Greenwich Sidereal Time */
+double gst2jd(); /* Calculate FITS UT Julian Date given Greenwich Sidereal Time */
+char *mst2fd(); /* Calculate FITS UT date and time given Mean Sidereal Time */
+double mst2jd(); /* Calculate FITS UT Julian Date given Mean Sidereal Time */
+char *fd2mst();	/* Convert from FITS UT date to Mean Sidereal Time */
+void dt2mst();	/* Convert from UT as yyyy.mmdd hh.mmssss to Mean Sidereal Time */
+double ts2mst(); /* Calculate Mean Sidereal Time given Universal Time */
+double mjd2mhjd(); /* Convert from Modified Julian Date to Heliocentric MJD */
+double mjd2hjd(); /* Convert from Modified Julian Date to Heliocentric JD */
+double mhjd2mjd(); /* Convert from Heliocentric Modified Julian Date to MJD */
+double jd2hjd(); /* Convert from Julian Date to Heliocentric Julian Date */
+double jd2mhjd(); /* Convert from Julian Date to Modified Heliocentric JD */
+double hjd2jd(); /* Convert from Heliocentric Julian Date to Julian Date */
+double hjd2mjd(); /* Convert from Heliocentric Julian Date to Modified JD */
+double hjd2mhjd(); /* Convert from Heliocentric Julian Date to Modified HJD */
+void setdatedec(); /* Set number of decimal places in FITS dates */
+void setlongitude(); /* Longitude for sidereal time in or out */
+
+void compnut();	/* Compute nutation in longitude and obliquity and mean obliquity*/
+
+#endif  /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif  /* __cplusplus */
+
+#endif /* fitsfile_h_ */
+
+/* May 31 1996	Use stream I/O for reading as well as writing
+ * Jun 12 1996	Add byte-swapping subroutines
+ * Jul 10 1996	FITS header now allocated in subroutines
+ * Jul 17 1996	Add FITS table column extraction subroutines
+ * Aug  6 1996	Add MOVEPIX, HDEL and HCHANGE declarations
+ *
+ * Oct 10 1997	FITS file opening subroutines now return int instead of FILE *
+ *
+ * May 27 1998	Split off fitsio and imhio subroutines to fitsio.h
+ * Jun  4 1998	Change fits2iraf from int to int *
+ * Jul 24 1998	Make IRAF header char instead of int
+ * Aug 18 1998	Change name to fitsfile.h from fitsio.h
+ * Oct  5 1998	Add isiraf() and isfits()
+ * Oct  7 1998	Note separation of imhfile.c into two files
+ *
+ * Jul 15 1999	Add fileutil.c subroutines
+ * Sep 28 1999	Add (1,1)-based image access subroutines
+ * Oct 21 1999	Add fitswhead()
+ * Nov  2 1999	Add date utilities from wcscat.h
+ * Nov 23 1999	Add fitscimage()
+ * Dec 15 1999	Fix misdeclaration of *2fd() subroutines, add fd2i(), dt2i()
+ * Dec 20 1999	Add isdate()
+ *
+ * Jan 20 2000	Add conversions to and from Besselian and Julian epochs
+ * Jan 21 2000	Add conversions to old FITS date and time
+ * Jan 26 2000	Add conversion to modified Julian date (JD - 2400000.5
+ * Mar 22 2000  Add lt2* and ut2* to get current time as local and UT
+ * Mar 24 2000	Add tsi2* and tsu2* to convert IRAF and Unix seconds
+ * Sep  8 2000	Improve comments
+ *
+ * Apr 24 2001	Add length of column name to column data structure
+ * May 22 2001	Add day of year date conversion subroutines
+ * Sep 25 2001	Add isfilelist() and isfile()
+ *
+ * Jan  8 2002	Add sts2c() and stc2s()
+ * Apr  8 2002	Change all long declarations to time_t for compatibility
+ * Jun 18 2002	Add fitserr() to print error messages
+ * Aug 30 2002	Add Ephemeris Time date conversions
+ * Sep 10 2002	Add Sidereal Time conversions
+ * Oct 21 2002	Add fitsrsect() to read sections of FITS images
+ *
+ * Mar  5 2003	Add isimlistd() to check image lists with root directory
+ * Aug 20 2003	Add fitsrfull() to read n-dimensional simple FITS images
+ *
+ * Feb 27 2004  Add fillvec() and fillvec1()
+ * May  3 2004	Add setfitsinherit()
+ * May  6 2004	Add fitswexhead()
+ * Aug 27 2004	Add fitsheadsize()
+ *
+ * Oct 14 2005	Add tsd2fd(), tsd2dt(), epj2ep(), epb2ep(), tsi2dt()
+ *
+ * Feb 23 2006	Add fitsrtail() to read appended FITS header
+ * Feb 23 2006	Add istiff(), isjpeg(), isgif() to check TIFF, JPEG, GIF files
+ * Sep  6 2006	Add heliocentric time conversions
+ * Oct  5 2006	Add local sidereal time conversions
+ *
+ * Jan  9 2007	Add ANSI prototypes
+ * Jan 11 2007	Add token subroutines from catutil.c/wcscat.h to fileutil.c
+ * Jun 11 2007	Add minvec() subroutine in imio.c
+ * Nov 28 2007	Add kform format to FITS table keyword data structure
+ *
+ * Sep  8 2008	Add ag2hr(), ang2deg(), deg2ang(), and hr2ang()
+ *
+ * Sep 25 2009	Add moveb()
+ *
+ * Jun 20 2014	Add next_line()
+ */
diff --git a/Code/src/libwcs/fitsfile1.h b/Code/src/libwcs/fitsfile1.h
new file mode 100644
index 0000000000000000000000000000000000000000..c23605ecfb39932927566ce6e944b0490f6dbbc5
--- /dev/null
+++ b/Code/src/libwcs/fitsfile1.h
@@ -0,0 +1,854 @@
+/*** File fitsfile.h  FITS and IRAF file access subroutines
+ *** January 5, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#ifndef fitsfile_h_
+#define fitsfile_h_
+#include "fitshead.h"
+
+/* FITS table keyword structure */
+struct Keyword {
+    char kname[10];	/* Keyword for table entry */
+    int lname;		/* Length of keyword name */
+    int kn;		/* Index of entry on line */
+    int kf;		/* Index in line of first character of entry */
+    int kl;		/* Length of entry value */
+};
+
+#define FITSBLOCK 2880
+
+/* Declarations for subroutines in fitsfile.c, imhfile.c, imio.c,
+ * fileutil.c, and dateutil.c */
+
+/* FITS file access subroutines in fitsfile.c */
+    int fitsropen(	/* Open a FITS file for reading, returning a FILE pointer */
+	char *inpath);	/* Pathname for FITS tables file to read */
+    char *fitsrhead(	/* Read a FITS header */
+	char *filename,	/* Name of FITS image file */
+	int *lhead,	/* Allocated length of FITS header in bytes (returned) */
+	int *nbhead);	/* Number of bytes before start of data (returned) */
+    char *fitsrtail(	/* Read FITS header appended to graphics file */
+	char *filename,	/* Name of FITS image file */
+	int *lhead,	/* Allocated length of FITS header in bytes (returned) */
+	int *nbhead);	/* Number of bytes before start of data (returned) */
+    char *fitsrimage(	/* Read a FITS image */
+	char *filename,	/* Name of FITS image file */
+	int *nbhead),	/* Actual length of image header(s) in bytes */
+	char *header,	/* FITS header for image (previously read) */
+    char *fitsrfull(	/* Read a FITS image of any dimension */
+	char *filename,	/* Name of FITS image file */
+	int *nbhead),	/* Actual length of image header(s) in bytes */
+	char *header,	/* FITS header for image (previously read) */
+    char *fitsrsect(	/* Read a piece of a FITS image, header */
+	char *filename,	/* Name of FITS image file */
+	char *header,	/* FITS header for image (previously read) */
+	int *nbhead,	/* Actual length of image header(s) in bytes */
+	int x0, y0, 	/* FITS image coordinate of first pixel */
+	int nx,		/* Number of columns to read (less than NAXIS1) */
+	int ny,		/* Number of rows to read (less than NAXIS2) */
+	int nlog);	/* Note progress mod this rows */
+    int fitswhead(	/* Write FITS header; keep file open for further writing */
+	char *filename,	/* Name of FITS image file */
+	char *header);	/* FITS header for image (previously read) */
+    int fitswexhead(	/* Write FITS header in place */
+	char *filename,	/* Name of FITS image file */
+	char *header);	/* FITS header for image */
+    int fitswext(	/* Write FITS header and image as extension to a file */
+	char *filename,	/* Name of FITS image file */
+	char *header,	/* FITS image header */
+	char *image);	/* FITS image pixels */
+    int fitswhdu(	/* Write FITS head and image as extension */
+	int fd,		/* File descriptor */
+	char *filename,	/* Name of FITS image file */
+	char *header,	/* FITS image header */
+	char *image);	/* FITS image pixels */
+    int fitswimage(	/* Write FITS header and image */
+	char *filename,	/* Name of FITS image file */
+	char *header,	/* FITS image header */
+	char *image);	/* FITS image pixels */
+    int fitscimage(	/* Write FITS header and copy FITS image */
+	char *filename,	/* Name of output FITS image file */
+	char *header,	/* FITS image header */
+	char *filename0); /* Name of input FITS image file */
+    int isfits(		/* Return 1 if file is a FITS file */
+	char *filename); /* Name of file to check */
+    void fitserr();	/* Print FITS error message to stderr */
+    void setfitsinherit( /* Set flag to append primary data header */
+	int inh);	/* 1 to inherit primary data header, else 0 */
+    int fitsheadsize(	/* Return size of fitsheader in bytes */
+	char *header);	/* FITS image header */
+	
+/* FITS table file access subroutines in fitsfile.c */
+
+    int fitsrtopen(	/* Open FITS table file and fill structure with
+			 * pointers to selected keywords
+			 * Return file descriptor (-1 if unsuccessful) */
+	char *inpath,	/* Pathname for FITS tables file to read */
+	int *nk,	/* Number of keywords to use */
+	struct Keyword **kw, /* Structure for desired entries */
+	int *nrows,	/* Number of rows in table (returned) */
+	int *nchar,	/* Number of characters in one table row (returned) */
+	int *nbhead);	/* Number of characters before table starts */
+    int fitsrthead(	/* Read pointers to selected keywords
+			 * from FITS table header */
+	char *header,	/* Header for FITS tables file */
+	int *nk,	/* Number of keywords to use */
+	struct Keyword **kw, /* Structure for desired entries */
+	int *nrows,	/* Number of rows in table (returned) */
+	int *nchar);	/* Number of characters in one table row (returned) */
+    void fitsrtlset();	/* Reset FITS Table buffer limits from start of data */
+    int fitsrtline(	/* Return specified line of FITS table */
+	int fd,		/* File descriptor for FITS file */
+	int nbhead,	/* Number of bytes in FITS header */
+	int lbuff,	/* Number of bytes in table buffer */
+	char *tbuff,	/* FITS table buffer */
+	int irow,	/* Number of table row to read */
+	int nbline,	/* Number of bytes to read for this line */
+	char *line);	/* One line of FITS table (returned) */
+short ftgeti2(		/* Extract column for keyword from FITS table line
+			 * as short */
+	char *entry,	/* Row or entry from table */
+	struct Keyword *kw); /* Table column information from FITS header */
+    int ftgeti4(	/* Extract column for keyword from FITS table line
+			 * as int */
+	char *entry,	/* Row or entry from table */
+	struct Keyword *kw); /* Table column information from FITS header */
+float ftgetr4(		/* Extract column for keyword from FITS table line
+			 * as float */
+	char *entry,	/* Row or entry from table */
+	struct Keyword *kw); /* Table column information from FITS header */
+    double ftgetr8(	/* Extract column for keyword from FITS table line
+			 * as double */
+	char *entry,	/* Row or entry from table */
+	struct Keyword *kw); /* Table column information from FITS header */
+    int ftgetc(		/* Extract column for keyword from FITS table line
+			 * as char string */
+	char *entry,	/* Row or entry from table */
+	struct Keyword *kw, /* Table column information from FITS header */
+	char *string,	/* Returned string */
+	int maxchar);	/* Maximum number of characters in returned string */
+
+/* IRAF file access subroutines in imhfile.c */
+
+    char *irafrhead(	/* Read IRAF .imh header file and translate to FITS header */
+	char *filename,	/* Name of IRAF header file */
+	int *lihead);	/* Length of IRAF image header in bytes (returned) */
+    char *irafrimage(	/* Read IRAF image pixels (call after irafrhead) */
+	char *fitsheader); /* FITS image header (filled) */
+    int irafwhead(	/* Write IRAF .imh header file */
+	char *hdrname,	/* Name of IRAF header file */
+	int lhead,	/* Length of IRAF header */
+	char *irafheader, /* IRAF header */
+	char *fitsheader); /* FITS image header */
+    int irafwimage(	/* Write IRAF .imh header file and .pix image file */
+	char *hdrname,	/* Name of IRAF header file */
+	int lhead,	/* Length of IRAF header */
+	char *irafheader, /* IRAF header */
+	char *fitsheader, /* FITS image header */
+	char *image);	/* IRAF image */
+    int isiraf(		/* return 1 if IRAF imh file, else 0 */
+	char *filename); /* Name of file to check */
+    char *iraf2fits(	/* Convert IRAF image header to FITS image header,
+			 * returning FITS header */
+	char *hdrname,	/* IRAF header file name (may be path) */
+	char *irafheader, /* IRAF image header */
+	int nbiraf,	/* Number of bytes in IRAF header */
+	int *nbfits);	/* Number of bytes in FITS header (returned) */
+
+    char *fits2iraf(	/* Convert FITS image header to IRAF image header,
+			 * returning IRAF header */
+	char *fitsheader, /* FITS image header */
+	char *irafheader, /* IRAF image header (returned updated) */
+	int nbhead,	/* Length of IRAF header */
+	int *nbiraf);	/* Length of returned IRAF header */
+
+/* Image pixel access subroutines in imio.c */
+
+    double getpix(	/* Read one pixel from any data type 2-D array (0,0)*/
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel
+			 *  16 = short, -16 = unsigned short, 32 = int
+			 * -32 = float, -64 = double */
+	int w;		/* Image width in pixels */
+	int h;		/* Image height in pixels */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int x,		/* Zero-based horizontal pixel number */
+	int y);		/* Zero-based vertical pixel number */
+    double getpix1(	/* Read one pixel from any data type 2-D array (1,1)*/
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	int w;		/* Image width in pixels */
+	int h;		/* Image height in pixels */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int x,		/* One-based horizontal pixel number */
+	int y);		/* One-based vertical pixel number */
+    double maxvec(	/* Get maximum value in vector from a image */
+	char *image,	/* Image array from which to extract vector */
+	int bitpix,	/* Number of bits per pixel in image */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int pix1,	/* Offset of first pixel to extract */
+	int npix);	/* Number of pixels to extract */
+    void putpix(	/* Write one pixel to any data type 2-D array (0,0)*/
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	int w;		/* Image width in pixels */
+	int h;		/* Image height in pixels */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int x,		/* Zero-based horizontal pixel number */
+	int y,		/* Zero-based vertical pixel number */
+	double dpix);	/* Value to put into image pixel */
+    void putpix1(	/* Write one pixel to any data type 2-D array (1,1) */
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	int w;		/* Image width in pixels */
+	int h;		/* Image height in pixels */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int x,		/* One-based horizontal pixel number */
+	int y,		/* One-based vertical pixel number */
+	double dpix);	/* Value to put into image pixel */
+    void addpix(	/* Add to one pixel in any data type 2-D array (0,0)*/
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	int w;		/* Image width in pixels */
+	int h;		/* Image height in pixels */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int x,		/* Zero-based horizontal pixel number */
+	int y,		/* Zero-based vertical pixel number */
+	double dpix);	/* Value to add to image pixel */
+    void addpix1(	/* Add to one pixel in any data type 2-D array (1,1)*/
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	int w;		/* Image width in pixels */
+	int h;		/* Image height in pixels */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int x,		/* One-based horizontal pixel number */
+	int y,		/* One-based vertical pixel number */
+	double dpix);	/* Value to add to image pixel */
+    void movepix(	/* Move one pixel value between two 2-D arrays (0,0) */
+	char *image1,	/* Pointer to first pixel in input image */
+	int bitpix1,	/* Bits per input pixel (FITS codes) */
+	int w1,		/* Number of horizontal pixels in input image */
+	int x1, y1);	/* Zero-based row and column for input pixel */
+	char *image2,	/* Pointer to first pixel in output image */
+	int bitpix2,	/* Bits per output pixel (FITS codes) */
+	int w2,		/* Number of horizontal pixels in output image */
+	int x2, y2);	/* Zero-based row and column for output pixel */
+    void movepix1(	/* Move one pixel value between two 2-D arrays (1,1) */
+	char *image1,	/* Pointer to first pixel in input image */
+	int bitpix1,	/* Bits per input pixel (FITS codes) */
+	int w1,		/* Number of horizontal pixels in input image */
+	int x1, y1);	/* One-based row and column for input pixel */
+	char *image2,	/* Pointer to first pixel in output image */
+	int bitpix2,	/* Bits per output pixel (FITS codes) */
+	int w2,		/* Number of horizontal pixels in output image */
+	int x2, y2);	/* One-based row and column for output pixel */
+
+/* Image vector processing subroutines in imio.c */
+
+    void addvec(	/* Add constant to vector from 2-D array */
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int pix1,	/* Offset of first pixel to which to add */
+	int npix,	/* Number of pixels to which to add */
+	double dpix);	/* Value to add to pixels */
+    void multvec(	/* Multiply vector from 2-D array by a constant */
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int pix1,	/* Offset of first pixel to multiply */
+	int npix,	/* Number of pixels to multiply */
+	double dpix);	/* Value to add to pixels */
+    void getvec(	/* Read vector from 2-D array */
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int pix1,	/* Offset of first pixel to extract */
+	int npix,	/* Number of pixels to extract */
+	double *dvec0);	/* Vector of pixels (returned) */
+    void putvec(	/* Write vector into 2-D array */
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int pix1,	/* Offset of first pixel to insert */
+	int npix,	/* Number of pixels to insert */
+	double *dvec0);	/* Vector of pixels to insert */
+    void fillvec(	/* Write constant into a vector */
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int pix1,	/* Zero-based offset of first pixel to multiply */
+	int npix,	/* Number of pixels to multiply */
+	double dpix);	/* Value to which to set pixels */
+    void fillvec1(	/* Write constant into a vector */
+	char *image,	/* Image array as 1-D vector */
+	int bitpix,	/* FITS bits per pixel */
+	double bzero,	/* Zero point for pixel scaling */
+	double bscale,	/* Scale factor for pixel scaling */
+	int pix1,	/* One-based offset of first pixel to multiply */
+	int npix,	/* Number of pixels to multiply */
+	double dpix);	/* Value to which to set pixels */
+
+/* Image pixel byte-swapping subroutines in imio.c */
+
+    void imswap(	/* Swap alternating bytes in a vector */
+	int bitpix,	/* Number of bits per pixel */
+	char *string,	/* Address of starting point of bytes to swap */
+	int nbytes);	/* Number of bytes to swap */
+    void imswap2(	/* Swap bytes in a vector of 2-byte (short) integers */
+	char *string,	/* Address of starting point of bytes to swap */
+	int nbytes);	/* Number of bytes to swap */
+    void imswap4(	/* Reverse bytes in a vector of 4-byte numbers */
+	char *string,	/* Address of starting point of bytes to swap */
+	int nbytes);	/* Number of bytes to swap */
+    void imswap8(	/* Reverse bytes in a vector of 8-byte numbers */
+	char *string,	/* Address of starting point of bytes to swap */
+	int nbytes);	/* Number of bytes to swap */
+    int imswapped();	/* Return 1 if machine byte order is not FITS order */
+
+/* File utilities from fileutil.c */
+
+    int getfilelines(	/*  Return number of lines in an ASCII file */
+	char *filename); /* Name of file to check */
+    char *getfilebuff(	/* Return entire file contents in a character string */
+	char *filename); /* Name of file to read */
+    int getfilesize(	/* Return size of a binary or ASCII file */
+	char *filename); /* Name of file to check */
+    int isimlist(	/* Return 1 if file is list of FITS or IRAF image files, else 0 */
+	char *filename); /* Name of file to check */
+    int isimlistd(	/* Return 1 if file is list of FITS or IRAF image files, else 0 */
+	char *filename,	/* Name of file to check */
+	char *rootdir);	/* Name of root directory for files in list */
+    int isfilelist(	/* Return 1 if list of readable files, else 0 */
+	char *filename); /* Name of file to check */
+	char *rootdir);	/* Name of root directory for files in list */
+    int isfile(		/* Return 1 if file is a readable file, else 0 */
+	char *filename); /* Name of file to check */
+    int istiff(		/* Return 1 if TIFF image file, else 0 */
+	char *filename); /* Name of file to check */
+    int isjpeg(		/* Return 1 if JPEG image file, else 0 */
+	char *filename); /* Name of file to check */
+    int isgif(		/* Return 1 if GIF image file, else 0 */
+	char *filename); /* Name of file to check */
+    int first_token(	/* Return first token from the next line of an ASCII file */
+	FILE *diskfile,	/* File descriptor for ASCII file */
+	int ncmax,	/* Maximum number of characters returned */
+	char *token);	/* First token on next line (returned) */
+    int stc2s (		/* Replace character in string with space */
+	char spchar,	/* Character to replace with spaces */
+	char *string);	/* Character string to process */
+    int sts2c (		/* Replace spaces in string with character */
+	char spchar,	/* Character with which to replace spaces */
+	char *string);	/* Character string to process */
+
+/* Subroutines for translating dates and times in dateutil.c */
+
+    /* Subroutines to convert from year and day of year */
+
+    void doy2dt(	/* Year and day of year to yyyy.mmdd hh.mmss */
+	int year,	/* Year */
+	double doy,	/* Day of year with fraction */
+	double *date,	/* Date as yyyy.mmdd (returned) */
+	double *time);	/* Time as hh.mmssxxxx (returned) */
+    double doy2ep(	/* Year and day of year to fractional year (epoch) */
+	int year,	/* Year */
+	double doy);	/* Day of year with fraction */
+    double doy2epb( /* year and day of year to Besselian epoch */
+	int year,	/* Year */
+	double doy);	/* Day of year with fraction */
+    double doy2epj( /* year and day of year to Julian epoch */
+	int year,	/* Year */
+	double doy);	/* Day of year with fraction */
+    char *doy2fd(	/* year and day of year to FITS date */
+	int year,	/* Year */
+	double doy);	/* Day of year with fraction */
+    double doy2jd( /* year and day of year to Julian Day */
+	int year,	/* Year */
+	double doy);	/* Day of year with fraction */
+    double doy2mjd( /* year and day of year to Modified Julian Day */
+	int year,	/* Year */
+	double doy);	/* Day of year with fraction */
+    double doy2ts( /* year and day of year to seconds since 1950.0 */ 
+	int year,	/* Year */
+	double doy);	/* Day of year with fraction */
+    int doy2tsi(	/* year and day of year to IRAF seconds since 1980-01-01 */
+	int year,	/* Year */
+	double doy);	/* Day of year with fraction */
+    time_t doy2tsu(	/* year and day of year to Unix seconds since 1970-01-01 */
+	int year,	/* Year */
+	double doy);	/* Day of year with fraction */
+
+    /* Subroutines to convert from date and time */
+
+    void dt2doy(	/* yyyy.mmdd hh.mmss to year and day of year */
+	double date,	/* Date as yyyy.mmdd
+			 * yyyy = calendar year (e.g. 1973)
+			 * mm = calendar month (e.g. 04 = april)
+			 * dd = calendar day (e.g. 15) */
+	double time;	/* Time as hh.mmssxxxx
+			 * if time<0, it is time as -(fraction of a day)
+			 * hh = hour of day (0 .le. hh .le. 23)
+			 * nn = minutes (0 .le. nn .le. 59)
+			 * ss = seconds (0 .le. ss .le. 59)
+			 * xxxx = tenths of milliseconds (0 .le. xxxx .le. 9999) */
+	int *year,	/* Year (returned) */
+	double *doy);	/* Day of year with fraction (returned) */
+    double dt2ep(	/* yyyy.ddmm and hh.mmsss to fractional year (epoch) */
+	double date,	/* Date as yyyy.mmdd */
+	double time);	/* Time as hh.mmssxxxx */
+    double dt2epb(	/* yyyy.ddmm and hh.mmsss to Besselian epoch */
+	double date,	/* Date as yyyy.mmdd */
+	double time);	/* Time as hh.mmssxxxx */
+    double dt2epj(	/* yyyy.ddmm and hh.mmsss to Julian epoch */
+	double date,	/* Date as yyyy.mmdd */
+	double time);	/* Time as hh.mmssxxxx */
+    char *dt2fd(	/* yyyy.ddmm and hh.mmsss to FITS date string */
+	double date,	/* Date as yyyy.mmdd */
+	double time);	/* Time as hh.mmssxxxx */
+    void dt2i(		/* yyyy.ddmm and hh.mmsss to year, month, day, hrs, min, sec */
+	double date,	/* Date as yyyy.mmdd */
+	double time,	/* Time as hh.mmssxxxx */
+	int *iyr,	/* year (returned) */
+	int *imon,	/* month (returned) */
+	int *iday,	/* day (returned) */
+	int *ihr,	/* hours (returned) */
+	int *imn,	/* minutes (returned) */
+	double *sec,	/* seconds (returned) */
+	int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double dt2jd(	/* yyyy.ddmm and hh.mmsss to Julian Day */
+	double date,	/* Date as yyyy.mmdd */
+	double time);	/* Time as hh.mmssxxxx */
+    double dt2mjd(	/* yyyy.ddmm and hh.mmsss to Modified Julian Day */
+	double date,	/* Date as yyyy.mmdd */
+	double time);	/* Time as hh.mmssxxxx */
+    double dt2ts(	/* yyyy.ddmm and hh.mmsss to seconds since 1950.0 */ 
+	double date,	/* Date as yyyy.mmdd */
+	double time);	/* Time as hh.mmssxxxx */
+    int dt2tsi(		/* yyyy.ddmm and hh.mmsss to IRAF seconds since 1980-01-01 */
+	double date,	/* Date as yyyy.mmdd */
+	double time);	/* Time as hh.mmssxxxx */
+    time_t dt2tsu(	/* yyyy.ddmm and hh.mmsss to Unix seconds since 1970-01-01 */
+	double date,	/* Date as yyyy.mmdd */
+	double time);	/* Time as hh.mmssxxxx */
+
+    /* Subroutines to convert from epoch (various types of fractional year) */
+
+    void ep2dt(		/* Fractional year to yyyy.mmdd hh.mmssss */
+	double epoch,	/* Date as fractional year */
+	double *date,	/* Date as yyyy.mmdd (returned) */
+	double *time);	/* Time as hh.mmssxxxx (returned) */
+    void epb2dt(	/* Besselian epoch to yyyy.mmdd hh.mmssss */
+	double  epoch,  /* Besselian epoch (fractional 365.242198781-day years) */
+	double *date,	/* Date as yyyy.mmdd (returned) */
+	double *time);	/* Time as hh.mmssxxxx (returned) */
+    void epj2dt(	/* Julian epoch to yyyy.mmdd hh.mmssss */
+	double  epoch,  /* Julian epoch (fractional 365.25-day years) */
+	double *date,	/* Date as yyyy.mmdd (returned)*/
+	double *time);	/* Time as hh.mmssxxxx (returned) */
+    char *ep2fd(	/* Fractional year to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+	double epoch);	/* Date as fractional year */
+    char *epb2fd(	/* Besselian epoch to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+	double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    char *epj2fd(	/* Julian epoch to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+	double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    void ep2i(		/* Fractional year to year, month, day, hours, min., sec. */
+	double epoch,	/* Date as fractional year */
+	int *iyr,	/* year (returned) */
+	int *imon,	/* month (returned) */
+	int *iday,	/* day (returned) */
+	int *ihr,	/* hours (returned) */
+	int *imn,	/* minutes (returned) */
+	double *sec,	/* seconds (returned) */
+	int ndsec);	/* Number of decimal places in seconds (0=int) */
+    void epb2i(		/* Besselian epoch to year, month, day, hours, min., sec. */
+	double  epoch,	/* Besselian epoch (fractional 365.242198781-day years) */
+	int *iyr,	/* year (returned) */
+	int *imon,	/* month (returned) */
+	int *iday,	/* day (returned) */
+	int *ihr,	/* hours (returned) */
+	int *imn,	/* minutes (returned) */
+	double *sec,	/* seconds (returned) */
+	int ndsec);	/* Number of decimal places in seconds (0=int) */
+    void epj2i(		/* Julian epoch to year, month, day, hours, min., sec. */
+	double  epoch,  /* Julian epoch (fractional 365.25-day years) */
+	int *iyr,	/* year (returned) */
+	int *imon,	/* month (returned) */
+	int *iday,	/* day (returned) */
+	int *ihr,	/* hours (returned) */
+	int *imn,	/* minutes (returned) */
+	double *sec,	/* seconds (returned) */
+	int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double ep2jd(	/* Fractional year to Julian Date */
+	double epoch);	/* Date as fractional year */
+    double epb2jd(	/* Besselian epoch to Julian Date */
+	double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2jd(	/* Julian epoch to Julian Date */
+	double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    double ep2mjd(	/* Fractional year to Modified Julian Date */
+	double epoch);	/* Date as fractional year */
+    double epb2mjd(	/* Besselian epoch to Modified Julian Date */
+	double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2mjd(	/* Julian epoch to Modified Julian Date */
+	double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    double ep2epb(	/* Fractional year to Besselian epoch */
+	double epoch);	/* Date as fractional year */
+    double ep2epj(	/* Fractional year to Julian epoch */
+	double epoch);	/* Date as fractional year */
+    double epb2epj(	/* Besselian epoch to Julian epoch */
+	double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2epb(	/* Julian epoch to Besselian epoch */
+	double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    double epb2ep(	/* Besselian epoch to fractional year */
+	double  epoch);	/* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2ep(	/* Julian epoch to fractional year */
+	double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+    double ep2ts(	/* Fractional year to seconds since 1950.0 */
+	double epoch);	/* Date as fractional year */
+    double epb2ts(	/* Besselian epoch to seconds since 1950.0 */
+	double  epoch);  /* Besselian epoch (fractional 365.242198781-day years) */
+    double epj2ts( /* Julian epoch to seconds since 1950.0 */
+	double  epoch);  /* Julian epoch (fractional 365.25-day years) */
+
+    /* Convert from FITS standard date string */
+
+    void fd2dt(		/* FITS standard date string to date and time */
+	char *string;	/* FITS date string, which may be:
+			 * fractional year
+			 * dd/mm/yy (FITS standard before 2000)
+			 * dd-mm-yy (nonstandard use before 2000)
+			 * yyyy-mm-dd (FITS standard after 1999)
+			 * yyyy-mm-ddThh:mm:ss.ss (FITS standard after 1999) */
+	double *date,	/* Date as yyyy.mmdd (returned)*/
+	double *time);	/* Time as hh.mmssxxxx (returned) */
+    void fd2doy(	/* FITS standard date string to year, day of year */
+	char *string,	/* FITS date string */
+	int *year,	/* Year (returned) */
+	double *doy);	/* Day of year with fraction (returned) */
+    double fd2ep(	/* FITS standard date string to fractional year (epoch) */
+	char *string);	/* FITS date string */
+    double fd2epb(	/* FITS standard date string to Besselian epoch */
+	char *string);	/* FITS date string */
+    double fd2epj(	/* FITS standard date string to Julian epoch */
+	char *string);	/* FITS date string */
+    char *fd2fd(	/* Any FITS standard date string to ISO FITS date string */
+	char *string);	/* FITS date string */
+    char *fd2of(	/* Any FITS standard date string to old FITS date and time */
+	char *string);	/* FITS date string */
+    char *fd2ofd(	/* Any FITS standard date string to old FITS date string */
+	char *string);	/* FITS date string */
+    char *fd2oft(	/* Any FITS standard date string to old FITS time string */
+	char *string);	/* FITS date string */
+    void fd2i(		/* FITS standard date string to year, mon, day, hrs, min, sec */
+	char *string,	/* FITS date string */
+	int *iyr,	/* year (returned) */
+	int *imon,	/* month (returned) */
+	int *iday,	/* day (returned) */
+	int *ihr,	/* hours (returned) */
+	int *imn,	/* minutes (returned) */
+	double *sec,	/* seconds (returned) */
+	int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double fd2jd(	/* FITS standard date string to Julian Day */
+	char *string);	/* FITS date string */
+    double fd2mjd(	/* FITS standard date string to Modified Julian Day */
+	char *string);	/* FITS date string */
+    double fd2ts(	/* FITS standard date to seconds since 1950-01-01 */
+	char *string);	/* FITS date string */
+    int fd2tsi(		/* FITS standard date to IRAF seconds since 1980-01-01 */
+	char *string);	/* FITS date string */
+    time_t fd2tsu(	/* FITS standard date to Unix seconds since 1970-01-01 */
+	char *string);	/* FITS date string */
+
+    /* Convert from Julian Day */
+
+    void jd2doy(	/* Julian Day to year and day of year */
+	double dj,	/* Julian Day */
+	int *year,	/* Year (returned) */
+	double *doy);	/* Day of year with fraction (returned) */
+    void jd2dt(		/* Julian Day to yyyy.mmdd hh.mmssss */
+	double dj,	/* Julian Day */
+	double *date,	/* Date as yyyy.mmdd (returned)*/
+	double *time);	/* Time as hh.mmssxxxx (returned) */
+    double jd2ep(	/* Julian Day to fractional year */
+	double dj);	/* Julian Day */
+    double jd2epb(	/* Julian Day to Besselian epoch */
+	double dj);	/* Julian Day */
+    double jd2epj(	/* Julian Day to Julian epoch */
+	double dj);	/* Julian Day */
+    char *jd2fd(	/* Julian Day to FITS date string yyyy-mm-ddThh:mm:ss.ss */
+	double dj);	/* Julian Day */
+    void jd2i(		/* Julian Day to year, month, day, hours, min., sec. */
+	double dj,	/* Julian Day */
+	int *iyr,	/* year (returned) */
+	int *imon,	/* month (returned) */
+	int *iday,	/* day (returned) */
+	int *ihr,	/* hours (returned) */
+	int *imn,	/* minutes (returned) */
+	double *sec,	/* seconds (returned) */
+	int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double jd2mjd( /* Julian Day to Modified Julian day */
+	double dj);	/* Julian Day */
+    double jd2ts(	/* Julian Day to seconds since 1950.0 */
+	double dj);	/* Julian Day */
+    time_t jd2tsu( /* Julian Day to Unix seconds since 1970-01-01T00:00 */
+	double dj);	/* Julian Day */
+    int jd2tsi( /* Julian Day to IRAF seconds since 1980-01-01T00:00 */
+	double dj);	/* Julian Day */
+
+    /* Convert current local time to various formats */
+
+    void lt2dt(		/* Current local time to date (yyyy.mmdd), time (hh.mmsss) */
+	double *date,	/* Date as yyyy.mmdd (returned) */
+	double *time);	/* Time as hh.mmssxxxx (returned) */
+    char *lt2fd();	/* Current local time to FITS ISO date string */
+    int lt2tsi();	/* Current local time to IRAF seconds since 1980-01-01T00:00 */
+    time_t lt2tsu();	/* Current local time to Unix seconds since 1970-01-01T00:00 */
+    double lt2ts();	/* Current local time to IRAF seconds since 1950-01-01T00:00 */
+
+    /* Convert from Modified Julian Day (JD - 2400000.5) */
+
+    void mjd2doy(	/* Modified Julian Day to year and day of year */
+	double dj,	/* Modified Julian Day */
+	int *year,	/* Year (returned) */
+	double *doy);	/* Day of year with fraction (returned) */
+    void mjd2dt(	/* Modified Julian Day to yyyy.mmdd hh.mmssss */
+	double dj,	/* Modified Julian Date */
+	double *date,	/* Date as yyyy.mmdd (returned)*/
+	double *time);	/* Time as hh.mmssxxxx (returned) */
+    double mjd2ep( /* Modified Julian Day to fractional year */
+	double dj);	/* Modified Julian Date */
+    double mjd2epb( /* Modified Julian Day to Besselian epoch */
+	double dj);	/* Modified Julian Date */
+    double mjd2epj( /* Modified Julian Day to Julian epoch */
+	double dj);	/* Modified Julian Date */
+    char *mjd2fd(	/* Modified Julian Day to FITS date yyyy-mm-ddThh:mm:ss.ss */
+	double dj);	/* Modified Julian Date */
+    void mjd2i(	/* Modified Julian Day to year, month, day, hours, min, sec */
+	double dj,	/* Modified Julian Date */
+	int *iyr,	/* year (returned) */
+	int *imon,	/* month (returned) */
+	int *iday,	/* day (returned) */
+	int *ihr,	/* hours (returned) */
+	int *imn,	/* minutes (returned) */
+	double *sec,	/* seconds (returned) */
+	int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double mjd2jd( /* Modified Julian Day to Julian day */
+	double dj);	/* Modified Julian Date */
+    double mjd2ts( /* Modified Julian Day to seconds since 1950.0 */
+	double dj);	/* Modified Julian Date */
+
+    /* Convert from seconds since 1950-01-01 0:00 (JPL Ephemeris time) */
+
+    void ts2dt(		/* Seconds since 1950.0 to yyyy.mmdd hh.mmssss */
+	double tsec,	/* seconds since 1950.0 */
+	double *date,	/* Date as yyyy.mmdd (returned)*/
+	double *time);	/* Time as hh.mmssxxxx (returned) */
+    double ts2ep(	/* Seconds since 1950.0 to fractional year */
+	double tsec);	/* seconds since 1950.0 */
+    double ts2epb( /* Seconds since 1950.0 to Besselian epoch */
+	double tsec);	/* seconds since 1950.0 */
+    double ts2epj( /* Seconds since 1950.0 to Julian epoch */
+	double tsec);	/* seconds since 1950.0 */
+    char *ts2fd(	/* Seconds since 1950.0 to FITS date, yyyy-mm-ddT00:00:00.000 */
+	double tsec);	/* seconds since 1950.0 */
+    void ts2i(	/* Seconds since 1950.0 to year, month, day, hours, min, sec */
+	double tsec,	/* seconds since 1950.0 */
+	int *iyr,	/* year (returned) */
+	int *imon,	/* month (returned) */
+	int *iday,	/* day (returned) */
+	int *ihr,	/* hours (returned) */
+	int *imn,	/* minutes (returned) */
+	double *sec,	/* seconds (returned) */
+	int ndsec);	/* Number of decimal places in seconds (0=int) */
+    double ts2jd(	/* Seconds since 1950.0 to Julian Day */
+	double tsec);	/* seconds since 1950.0 */
+    double ts2mjd( /* Seconds since 1950.0 to Modified Julian Day */
+	double tsec);	/* seconds since 1950.0 */
+
+    /* Convert from IRAF time (seconds since 1980-01-01 0:00 UT) */
+
+    char *tsi2fd(	/* Seconds since 1980-01-01 to FITS standard date string */
+    double tsi2ts( /* Seconds since 1980-01-01 to seconds since 1950-01-01 */
+    double tsi2ts( /* Seconds since 1980-01-01 to seconds since 1950-01-01 */
+    void tsi2dt( /* Seconds since 1980-01-01 to date yyyy.mmdd, time hh.mmssss */
+
+    /* Convert from Unix time (seconds since 1970-01-01 0:00 UT) */
+
+    void tsu2dt(	/* Seconds since 1970-01-01 to date yyyy.ddmm, time hh.mmsss */
+    char *tsu2fd(	/* Seconds since 1970-01-01 to FITS standard date string */
+    char *tsd2fd(	/* Seconds since start of day to FITS standard time string */
+    double tsu2ts( /* Seconds since 1970-01-01 to seconds since 1950-01-01 */
+    int tsu2tsi(	/* Seconds since 1970-01-01 to local seconds since 1980-01-01 */
+
+    /* Convert from current Universal Time */
+
+    void ut2dt(	/* Current Universal Time to date (yyyy.mmdd), time (hh.mmsss) */
+    void ut2doy( /* Current Universal Time to year, day of year */
+    double ut2ep( /* Current Universal Time to fractional year */
+    double ut2epb( /* Current Universal Time to Besselian Epoch */
+    double ut2epj( /* Current Universal Time to Julian Epoch */
+    char *ut2fd(	/* Current Universal Time to FITS ISO date string */
+    double ut2jd(	/* Current Universal Time to Julian Date */
+    double ut2mjd( /* Current Universal Time to Modified Julian Date */
+    int ut2tsi(	/* Current UT to IRAF seconds since 1980-01-01T00:00 */
+    time_t ut2tsu(	/* Current UT to Unix seconds since 1970-01-01T00:00 */
+    double ut2ts( /* Current UT to IRAF seconds since 1950-01-01T00:00 */
+
+    double tsd2dt( /* Seconds since start of day to hh.mmsssss */
+    int isdate(	/* Return 1 if string is FITS old or ISO date */
+    int sts2c(	/* Replaces spaces in a string with a specified character */
+    int stc2s(	/* Replaces a specified character in a string with spaces */
+
+    /* Ephemeris time conversions (ET, TT, and TDT) */
+
+    char *et2fd(	/* ET (or TDT or TT) in FITS format to UT in FITS format */
+	char *string);	/* Ephemeris Time as FITS date string (E not T) */
+    char *fd2et(	/* UT in FITS format to ET (or TDT or TT) in FITS format */
+	char *string);	/* FITS date string */
+    void dt2et(		/* yyyy.ddmm and hh.mmsss to Ephemeris Time */ 
+	double *date,	/* Date as yyyy.mmdd */
+	double *time);	/* Time as hh.mmssxxxx
+			 *if time<0, it is time as -(fraction of a day) */
+    double jd2jed(	/* Convert from Julian Date to Julian Ephemeris Date */
+	double dj);	/* Julian Date */
+    double jed2jd(	/* Convert from Julian Ephemeris Date to Julian Date */
+	double dj);	/* Julian Ephemeris Date */
+    double ets2ts(	/* ET in seconds since 1950-01-01 to UT in same format */
+	double tsec);	/* ET in seconds since 1950-01-01 */
+    double ts2ets(	/* UT in seconds since 1950-01-01 to ET in same format */
+	double tsec);	/* UT in seconds since 1950-01-01 */
+    void edt2dt(	/* yyyy.ddmm and hh.mmsss Ephemeris Time to UT */ 
+	double *date;	/* Date as yyyy.mmdd */
+	double *time;	/* Time as hh.mmssxxxx
+			 * If time<0, it is time as -(fraction of a day) */
+    double utdt(	/* Compute difference between UT and dynamical time (ET-UT) */
+	double dj);	/* Julian Date (UT) */
+
+    /* Sidereal Time conversions */
+
+    char *fd2gst(	/* Convert from FITS UT date to Greenwich Sidereal Time */
+	char *string);	/* FITS date string */
+    void dt2gst(	/* Convert from UT as yyyy.mmdd hh.mmssss to Greenwich Sidereal Time */
+	double *date,	/* Date as yyyy.mmdd */
+	double *time);	/* Time as hh.mmssxxxx
+			 * If time<0, it is time as -(fraction of a day) */
+    double jd2gst(	/* Calculate Greenwich Sidereal Time given Julian Date */
+	double dj);	/* Julian Date (UT) */
+    double ts2gst(	/* Calculate Greenwich Sidereal Time given Universal Time */
+	double tsec);	/* Time since 1950.0 in UT seconds */
+    char *fd2lst(	/* Convert from FITS UT date to Local Sidereal Time */
+	char *string);	/* FITS date string */
+    void dt2lst(	/* Convert from UT as yyyy.mmdd hh.mmssss to Local Sidereal Time */
+	double *date,	/* Date as yyyy.mmdd */
+	double *time);	/* Time as hh.mmssxxxx
+			 * If time<0, it is time as -(fraction of a day) */
+    double ts2lst(	/* Calculate Local Sidereal Time given Universal Time */
+	double tsec);	/* Time since 1950.0 in UT seconds */
+    double jd2lst(	/* Calculate Local Sidereal Time given Julian Date */
+	double dj);	/* Julian Date (UT) */
+    double eqeqnx(	/* Compute equation of eqinoxes from Julian Date */
+	double dj);	/* Julian Date (UT) */
+    char *fd2mst(	/* Convert from FITS UT date to Mean Sidereal Time */
+	char *string);	/* FITS date string */
+    double jd2mst(	/* Convert from Julian Date to Mean Sidereal Time */
+	double dj);	/* Julian Date (UT) */
+    double jd2mst2(	/* Convert from Julian Date to Mean Sidereal Time */
+	double dj);	/* Julian Date (UT) */
+    void dt2mst(	/* Convert from UT as yyyy.mmdd hh.mmssss to Mean Sidereal Time */
+	double *date,	/* Date as yyyy.mmdd */
+	double *time);	/* Time as hh.mmssxxxx
+			 * If time<0, it is time as -(fraction of a day) */
+    double lst2dt(	/* Calculate UT as hh.mmsss given UT date and
+			 * Local Sidereal Time */
+	double date0;   /* UT date as yyyy.mmdd */
+	double time0;   /* LST as hh.mmssss */
+    double lst2jd(	/* Calculate UT as Julian Date given UT date and
+			 * Local Sidereal Time */
+	double sdj);     /* Julian Date of desired day at 0:00 UT + sidereal time */
+    char *lst2fd(	/* Calculate FITS UT date and time given UT date and
+			 * Local Sidereal Time */
+	char *string);	/* UT Date, LST as yyyy-mm-ddShh:mm:ss.ss */
+    char *gst2fd(	/* Calculate FITS UT date and time given Greenwich Sidereal Time */
+	char *string);	/* UT Date, GST as yyyy-mm-ddShh:mm:ss.ss */
+    double gst2jd(	/* Calculate FITS UT Julian Date given Greenwich Sidereal Time */
+	double sdj);	/* UT Date, GST as Julian Date */
+    char *mst2fd(	/* Calculate FITS UT date and time given Mean Sidereal Time */
+	char *string);	/* UT Date, MST as yyyy-mm-ddShh:mm:ss.ss */
+    double mst2jd(	/* Calculate FITS UT Julian Date given Mean Sidereal Time */
+	double sdj);	/* UT Date, MST as Julian Date */
+    double ts2mst(	/* Calculate Mean Sidereal Time given Universal Time */
+	double tsec);	/* time since 1950.0 in UT seconds */
+    void setlongitude( /* Longitude for sidereal time in or out */
+	double longitude); /* longitude of observatory in degrees (+=west) */
+    void compnut(	/* Compute nutation in longitude and obliquity and mean obliquity*/
+	double dj,	/* TDB (loosely ET or TT) as Julian Date */
+	double *dpsi,	/* Nutation in longitude in radians (returned) */
+	double *deps,	/* Nutation in obliquity in radians (returned) */
+	double *eps0);	/* Mean obliquity in radians (returned) */
+
+    /* Heliocentric Julian Date conversions */
+
+    double mjd2mhjd(	/* Convert from Modified Julian Date to Heliocentric MJD */
+	double mjd,	/* Julian date (geocentric) */
+	double ra,	/* Right ascension (degrees) */
+	double dec,	/* Declination (degrees) */
+	int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+    double mjd2hjd( /* Convert from Modified Julian Date to Heliocentric JD */
+	double mjd,	/* Julian date (geocentric) */
+	double ra,	/* Right ascension (degrees) */
+	double dec,	/* Declination (degrees) */
+	int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+    double mhjd2mjd(	/* Convert from Heliocentric Modified Julian Date to MJD */
+	double mhjd,	/* Modified Heliocentric Julian date */
+	double ra,	/* Right ascension (degrees) */
+	double dec,	/* Declination (degrees) */
+	int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+    double jd2hjd( /* Convert from Julian Date to Heliocentric Julian Date */
+	double  dj,     /* Julian date (geocentric) */
+	double ra,	/* Right ascension (degrees) */
+	double dec,	/* Declination (degrees) */
+	int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+    double hjd2jd( /* Convert from Heliocentric Julian Date to Julian Date */
+	double  dj,	/* Heliocentric Julian date */
+	double ra,	/* Right ascension (degrees) */
+	double dec,	/* Declination (degrees) */
+	int sys);	/* J2000, B1950, GALACTIC, ECLIPTIC */
+
+    void setdatedec(	/* Set number of decimal places in FITS dates */
+	int nd);	/* Number of decimal places in FITS dates */
diff --git a/Code/src/libwcs/fitshead.h b/Code/src/libwcs/fitshead.h
new file mode 100644
index 0000000000000000000000000000000000000000..843b0dae46956af8962bac13dbb7b10242ab1dcf
--- /dev/null
+++ b/Code/src/libwcs/fitshead.h
@@ -0,0 +1,438 @@
+/*** File fitshead.h  FITS header access subroutines
+ *** January 9, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* Declarations for subroutines in hget.c, hput.c, and iget.c */
+
+#ifndef _fitshead_h_
+#define _fitshead_h_
+
+#include <sys/types.h>
+
+#ifdef __cplusplus /* C++ prototypes */
+extern "C" {
+#endif
+
+
+#ifdef __STDC__   /* Full ANSI prototypes */
+
+/* Subroutines in hget.c */
+    int hgeti2(			/* Extract short value from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	short* val);		/* short integer value (returned) */
+    int hgeti4c(		/* Extract int value from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	const char* wchar,	/* WCS to use (A-Z or null) */
+	int* val);		/* integer value (returned) */
+    int hgeti4(			/* Extract int value from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	int* val);		/* integer value (returned) */
+    int hgetr4(			/* Extract float value from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	float* val);		/* float value (returned) */
+    int hgetr8c(		/* Extract double value from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	const char* wchar,	/* WCS to use (A-Z or null) */
+	double* val);		/* double value (returned) */
+    int hgetr8(			/* Extract double value from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	double* val);		/* double value (returned) */
+    int hgetra(			/* Extract right ascension from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	double* ra);		/* RA in degrees (returned) */
+    int hgetdec(		/* Extract declination from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	double* dec);		/* Dec in degrees (returned) */
+    int hgetdate(		/* Extract date from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	double* date);		/* Date in fractional years (returned) */
+    int hgetl(			/* Extract boolean value from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	int* lval);		/* 1 if T, 0 if F (returned) */
+    int hgetsc(			/* Extract string value from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	const char* wchar,	/* WCS to use (A-Z or null) */
+	const int lstr,		/* maximum length of returned string */
+	char* string);		/* null-terminated string value (returned) */
+    int hgets(			/* Extract string value from FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	const int lstr,		/* maximum length of returned string */
+	char* string);		/* null-terminated string value (returned) */
+    int hgetm (			/* Extract string from multiple keywords */
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	const int lstr,		/* maximum length of returned string */
+	char* string);		/* null-terminated string value (returned) */
+    int hgetndec(		/* Find number of decimal places in FITS value*/
+	const char* hstring,	/* FITS header string */
+	const char* keyword,	/* FITS keyword */
+	int* ndec);		/* number of decimal places (returned) */
+
+    char* hgetc(		/* Return pointer to value for FITS keyword */
+	const char* hstring,	/* FITS header string */
+	const char* keyword);	/* FITS keyword */
+
+    char* ksearch(		/* Return pointer to keyword in FITS header */
+	const char* hstring,	/* FITS header string */
+	const char* keyword);	/* FITS keyword */
+    char *blsearch (
+	const char* hstring,	/* FITS header string */
+	const char* keyword);	/* FITS keyword */
+
+    char *strsrch (		/* Find string s2 within string s1 */
+	const char* s1,		/* String to search */
+	const char* s2);	/* String to look for */
+    char *strnsrch (		/* Find string s2 within string s1 */
+	const char* s1,		/* String to search */
+	const char* s2,		/* String to look for */
+	const int ls1);		/* Length of string being searched */
+
+    char *strcsrch (		/* Find string s2 within string s1 (no case) */
+	const char* s1,		/* String to search */
+	const char* s2);	/* String to look for */
+    char *strncsrch (		/* Find string s2 within string s1 (no case) */
+	const char* s1,		/* String to search */
+	const char* s2,		/* String to look for */
+	const int ls1);		/* Length of string being searched */
+
+    int hlength(		/* Set length of unterminated FITS header */
+        const char *header,	/* FITS header */
+        const int lhead);	/* Allocated length of FITS header */
+    int gethlength(		/* Get length of current FITS header */
+        char* header);		/* FITS header */
+
+    double str2ra(		/* Return RA in degrees from string */
+	const char* in);	/* Character string (hh:mm:ss.sss or dd.dddd) */
+    double str2dec(		/* Return Dec in degrees from string */
+	const char* in);	/* Character string (dd:mm:ss.sss or dd.dddd) */
+
+    int isnum(			/* Return 1 if number, else 0 */
+	const char* string);	/* Character string which may be a number */
+    int notnum(			/* Return 0 if number, else 1 */
+	const char* string);	/* Character string which may be a number */
+    int numdec(			/* Return number of decimal places in number */
+	const char* string);	/* Character string which may be a number */
+    void strfix(		/* Clean up extraneous characters in string */
+	char* string,		/* Character string which may be a number */
+	int fillblank,		/* If 1, blanks are replaced by underscores */
+	int dropzero);		/* If 1, drop trailing zeroes from string */
+
+    char *getltime(void);	/* Return current local time in ISO format */
+    char *getutime(void);	/* Return current UT as an ISO-format string */
+
+/* Subroutines in iget.c */
+    int mgetstr(		/* Extract string from multiline FITS keyword */
+	const char* hstring,	/* FITS header string */
+	const char* mkey,	/* FITS keyword root _n added for extra lines */
+	const char* keyword,	/* IRAF keyword */
+	const int lstr,		/* maximum length of returned string */
+	char* string);		/* null-terminated string value (returned) */
+    int mgeti4(			/* Extract int from multiline FITS keyword */
+	const char* hstring,	/* FITS header string */
+	const char* mkey,	/* FITS keyword root _n added for extra lines */
+	const char* keyword,	/* IRAF keyword */
+	int* ival);		/* int keyword value (returned) */
+    int mgetr8(			/* Extract double from multiline FITS keyword */
+	const char* hstring,	/* FITS header string */
+	const char* mkey,	/* FITS keyword root _n added for extra lines */
+	const char* keyword,	/* IRAF keyword */
+	double* dval);		/* double keyword value (returned) */
+    int igeti4(			/* Extract int from IRAF keyword string */
+	const char* hstring,	/* Multiline IRAF keyword string value */
+	const char* keyword,	/* IRAF keyword */
+	int* val);		/* int value (returned) */
+    int igetr4(			/* Extract float from IRAF keyword string */
+	const char* hstring,	/* Multiline IRAF keyword string value */
+	const char* keyword,	/* IRAF keyword */
+	float* val);		/* float value (returned) */
+    int igetr8(			/* Extract double from IRAF keyword string */
+	const char* hstring,	/* Multiline IRAF keyword string value */
+	const char* keyword,	/* IRAF keyword */
+	double* val);		/* double value (returned) */
+    int igets(			/* Extract string from IRAF keyword string */
+	const char* hstring,	/* Multiline IRAF keyword string value */
+	const char* keyword,	/* IRAF keyword */
+	const int lstr,		/* maximum length of returned string */
+	char* string);		/* null-terminated string value (returned) */
+    char *igetc(		/* Extract string from IRAF keyword string */
+	const char* hstring,	/* Multiline IRAF keyword string value */
+	const char* keyword);	/* IRAF keyword */
+
+/* Subroutines in hput.c */
+/* All hput* routines return 0 if successful, else -1 */
+    int hputi2(		/* Implant short value into FITS header */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	short ival);	/* short value */
+    int hputi4(		/* Implant int value into FITS header */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const int ival);	/* int value */
+    int hputr4(		/* Implant float value into FITS header */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const float* rval);	/* float (4 byte) value */
+    int hputr8(		/* Implant short into FITS header */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const double dval);	/* double value */
+    int hputnr8(	/* double with specified number of decimal places */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const int ndec,	/* Number of decimal places in keyword value */
+	const double dval);	/* double value */
+    int hputs(			/* Quoted character string into FITS header */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const char* cval);	/* Character string value */
+    int hputm(		/* Quoted character string, mutiple keywords */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const char* cval);	/* Character string value */
+    int hputcom(	/* Add comment to keyword line in FITS header */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const char* comment);	/* Comment string */
+    int hputra(	/* Right ascension in degrees into hh:mm:ss.sss */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const double ra);	/* Right ascension in degrees */
+    int hputdec(		/* Declination in degrees into dd:mm:ss.ss */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const double dec);	/* Declination in degrees */
+    int hputl(			/* Implant boolean value into FITS header */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const int lval);	/* 0->F, else ->T */
+    int hputc(			/* Implant character string without quotes */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword,	/* FITS keyword */
+	const char* cval);	/* Character string value */
+
+    int hdel(			/* Delete a keyword line from a FITS header */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword);	/* FITS keyword to delete */
+    int hadd(			/* Add a keyword line from a FITS header */
+	char* hplace,		/* Location in FITS header string (modified) */
+	const char* keyword);	/* FITS keyword to add */
+    int hchange(		/* Change a keyword name in a FITS header */
+	char* hstring,		/* FITS header string (modified) */
+	const char* keyword1,	/* Current FITS keyword name */
+	const char* keyword2);	/* New FITS keyword name */
+
+    void ra2str(		/* Convert degrees to hh:mm:ss.ss */
+        char *string,		/* Character string (returned) */
+	int lstr,		/* Length of string */
+        const double ra,	/* Right ascension in degrees */
+        const int ndec);	/* Number of decimal places in seconds */
+    void dec2str(		/* Convert degrees to dd:mm:ss.ss */
+        char *string,		/* Character string (returned) */
+	int lstr,		/* Length of string */
+        const double dec,	/* Declination in degrees */
+        const int ndec);	/* Number of decimal places in arcseconds */
+    void deg2str(		/* Format angle into decimal degrees string */
+        char *string,		/* Character string (returned) */
+	int lstr,		/* Length of string */
+        const double deg,	/* Angle in degrees */
+        const int ndec);	/* Number of decimal places in degrees */
+    void num2str(		/* Format number into string */
+        char *string,		/* Character string (returned) */
+        const double  num,	/* Number */
+	const int field,	/* Total field size in characters */
+        const int ndec);	/* Number of decimal places */
+    void setheadshrink(		/* 0 to keep blank line when keyword deleted */
+	const int hsh);		/* 1 to shrink  header by one line */
+    void setleaveblank(		/* 1 to keep blank line where keyword deleted */
+	const int hsh);		/* 0 to shrink  header by one line */
+
+#else /* K&R prototypes */
+
+/* Subroutines in hget.c */
+
+/* Extract a value from a FITS header for given keyword */
+extern int hgeti4();	/* int (Multiple WCS) */
+extern int hgeti4c();	/* int */
+extern int hgeti2();	/* short */
+extern int hgetr4();	/* float */
+extern int hgetr8();	/* double */
+extern int hgetr8c();	/* double (Multiple WCS) */
+extern int hgetra();	/* Right ascension in degrees from string */
+extern int hgetdec();	/* Declination in degrees from string */
+extern int hgetdate();	/* Date in years from FITS date string */
+extern int hgetl();	/* T->1, F->0 from FITS logical entry */
+extern int hgets();	/* Previously allocated string */
+extern int hgetsc();	/* Previously allocated string (Multiple WCS) */
+extern int hgetm();	/* Previously allocated string from multiple keywords */
+extern char *hgetc();	/* Return pointer to string */
+extern int hgetndec();	/* Number of decimal places in keyword value */
+
+/* Subroutines to convert strings to RA and Dec in degrees */
+extern double str2ra();
+extern double str2dec();
+
+/* Check to see whether a string is a number or not */
+extern int isnum();
+extern int notnum();
+extern int decnum();
+
+/* Find given keyword entry in FITS header */
+extern char *ksearch();
+
+/* Find beginning of fillable blank line before FITS header keyword */
+extern char *blsearch();
+
+/* Search for substring s2 within string s1 */
+extern char *strsrch ();	/* s1 null-terminated */
+extern char *strnsrch ();	/* s1 ls1 characters long */
+extern char *strcsrch ();	/* s1 null-terminated (case-insensitive) */
+extern char *strncsrch ();	/* s1 ls1 characters long (case-insensitive) */
+extern void strfix();	/* Drop or change extraneous characters in string */
+
+/* Set length of header which is not null-terminated */
+extern int hlength();
+
+/* Get length of current FITS header */
+extern int gethlength();
+
+/* Subroutines in iget.c */
+extern int mgetstr();	/* Previously allocated string from multiline keyword */
+extern int mgetr8();	/* double from multiline keyword */
+extern int mgeti4();	/* int from multiline keyword */
+extern int igeti4();	/* long integer from IRAF compound keyword value */
+extern int igetr4();	/* real from IRAF compound keyword value */
+extern int igetr8();	/* double from IRAF compound keyword value */
+extern int igets();	/* character string from IRAF compound keyword value */
+extern char *igetc();	/* Extract string from IRAF keyword string */
+
+/* Subroutines in hput.c */
+
+/* Implant a value into a FITS header for given keyword */
+extern int hputi4();	/* int */
+extern int hputi2();	/* short */
+extern int hputr4();	/* float */
+extern int hputr8();	/* double */
+extern int hputnr8();	/* double with specified number of decimal places */
+extern int hputra();	/* Right ascension in degrees into hh:mm:ss.sss */
+extern int hputdec();	/* Declination in degrees into dd:mm:ss.ss */
+extern int hputl();	/* 0 -> F, else T FITS logical entry */
+extern int hputs();	/* Quoted character string */
+extern int hputm();	/* Quoted character string into mutiple keywords */
+extern int hputc();	/* Character string without quotes (returns 0 if OK) */
+extern int hputcom();	/* Comment after keyword=value (returns 0 if OK) */
+
+extern int hdel();	/* Delete a keyword line from a FITS header */
+extern int hadd();	/* Add a keyword line to a FITS header */
+extern int hchange();	/* Change a keyword name in a FITS header */
+extern void setheadshrink(); /* Set flag for deleted keyword space disposition*/
+extern void setleaveblank(); /* Set flag for deleted keyword space disposition*/
+
+/* Subroutines to convert RA and Dec in degrees to strings */
+extern void ra2str();
+extern void dec2str();
+
+extern void deg2str();
+extern void num2str();
+extern int numdec();	/* Return number of decimal places in number */
+
+extern char *getltime(); /* Return current local time in ISO format */
+extern char *getutime(); /* Return current UT as an ISO-format string */
+
+#endif	/* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif	/* __cplusplus */
+
+#endif	/* fitshead_h_ */
+
+/* Apr 26 1996	Add HGETDATE to get year from date string
+ * May 22 1996	Return double from STR2RA and STR2DEC
+ * May 31 1996	Use stream I/O for reading as well as writing
+ * Jun 12 1996	Add byte-swapping subroutines
+ * Jul 10 1996	FITS header now allocated in subroutines
+ * Jul 17 1996	Add FITS table column extraction subroutines
+ * Jul 19 1996	Add declarations for header implanting subroutines
+ * Aug  5 1996	Add HLENGTH for FITS headers which are not null-terminated
+ * Aug  5 1996	Add STRNSRCH for FITS headers which are not null-terminated
+ * Aug  6 1996	Add HPUTNR8 to save a specified number of decimal places
+ * Aug  6 1996	Add MOVEPIX, HDEL and HCHANGE declarations
+ * Nov  1 1996	Add DEG2STR
+ * Dec 12 1996	Add ISNUM
+ *
+ * Oct 10 1997	FITS file opening subroutines now return int instead of FILE *
+ *
+ * Mar 12 1998	Add NOTNUM
+ * Apr 30 1998	Clean up declarations and add more comments
+ * May 12 1998	Add MGETS, MGETR8, MGETI4 for IRAF multi-line keywords
+ * May 26 1998	Add HGETNDEC for number of decimal places in keyword value
+ * May 27 1998	Add BLSEARCH to find usable blank lines in header
+ * May 27 1998	Split off fitsio and imhio subroutines to fitsio.h
+ * May 27 1998	Add all subroutines in hget.c, hput.c, and iget.c to C++ dec.
+ * Jun 24 1998	Add string lengths to ra2str(), dec2str, and deg2str() calls
+ * Jun 25 1998	Fix other C++ declarations with added string lengths
+ * Aug 31 1998	Add current date subroutines getltime() and getutime()
+ * Oct 28 1998	Add missing hgetc() to non c++ declarations
+ *
+ * Oct  6 1999	Add gethlength() to return current size of header
+ * Oct 14 1999	All HPUT subroutines now return an error code, 0 if OK, else -1
+ * Oct 15 1999	Add hputcom() declaration
+ * Oct 21 1999	Add hgetm() declaration
+ *
+ * Mar 22 2000	Add int to iget*() declarations
+ * Mar 27 2000	Add hputm() declaration
+ *
+ * Apr  3 2002	Add hgeti4c(), hgetr8c(), and hgetsc()
+ * Apr  8 2002	Include sys/types.h
+ * Aug 30 2002	Add strcsrch() and strncsrch()
+ *
+ * Sep 23 2003	Change mgets() to mgetstr() to avoid name collision at UCO Lick
+ * Oct 20 2003	Add numdec() to return the number of decimal places in a string
+ *
+ * Feb 26 2004	Add igetc(), formerly internal to iget.c
+ * Jul  1 2004	Add setheadshrink() for hdel()
+ * Aug 30 2004	Add numdec() to non-C++ declarations
+ *
+ * May 22 2006	Add setleaveblank() to leave blank line where keyword is deleted
+ * Jun 28 2006	Add strfix() to clean up characters in strings
+ * Nov 29 2006	Drop semicolon at end of C++ ifdef
+ *
+ * Jan  9 2007	Fix declarations so ANSI prototypes are not just for C++
+ */
diff --git a/Code/src/libwcs/fitswcs.c b/Code/src/libwcs/fitswcs.c
new file mode 100644
index 0000000000000000000000000000000000000000..3db9b9b33fd2b37b81aa7ce109e408fc6fb80230
--- /dev/null
+++ b/Code/src/libwcs/fitswcs.c
@@ -0,0 +1,651 @@
+/*** File libwcs/fitswcs.c
+ *** December 20, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:      fitswcs.c (FITS file WCS reading and deleting)
+ * Purpose:     Read and delete FITS image world coordinate system keywords
+ *
+ * Subroutine:  GetWCSFITS (filename, verbose)
+ *		Open a FITS or IRAF image file and returns its WCS structure
+ * Subroutine:  GetFITShead (filename, verbose)
+ *		Open a FITS or IRAF image file and returns a FITS header
+ * Subroutine:  DelWCSFITS (header, verbose)
+ *		Delete all standard WCS keywords from a FITS header
+ * Subroutine:	PrintWCS (header, verbose)
+ *		Check the WCS fields and print any that are found if verbose.
+ * Subroutine:	SetFITSWCS (header, wcs)
+ *		Set FITS WCS keywords from WCS data structure
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "fitsfile.h"
+#include "wcs.h"
+
+struct WorldCoor *
+GetWCSFITS (filename, verbose)
+
+char *filename;	/* FITS or IRAF file filename */
+int verbose;	/* Print extra information if nonzero */
+
+{
+    char *header;		/* FITS header */
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+    char *GetFITShead();
+    char *cwcs;			/* Multiple wcs string (name or character) */
+
+    /* Read the FITS or IRAF image file header */
+    header = GetFITShead (filename, verbose);
+    if (header == NULL)
+	return (NULL);
+
+    /* Set the world coordinate system from the image header */
+    cwcs = strchr (filename, '%');
+    if (cwcs != NULL)
+	cwcs++;
+    wcs = wcsinitn (header, cwcs);
+    if (wcs == NULL) {
+	setwcsfile (filename);
+	if (verbose)
+	    wcserr ();
+	}
+    free (header);
+
+    return (wcs);
+}
+
+char *
+GetFITShead (filename, verbose)
+
+char *filename;	/* FITS or IRAF file filename */
+int verbose;	/* Print error messages if nonzero */
+
+{
+    char *header;		/* FITS header */
+    int lhead;			/* Maximum number of bytes in FITS header */
+    char *irafheader;		/* IRAF image header */
+    int nbiraf, nbfits;
+
+    /* Open IRAF image if .imh extension is present */
+    if (isiraf (filename)) {
+	if ((irafheader = irafrhead (filename, &nbiraf)) != NULL) {
+	    if ((header = iraf2fits (filename, irafheader, nbiraf, &lhead)) == NULL) {
+		if (verbose)
+		 fprintf (stderr, "Cannot translate IRAF header %s\n",filename);
+		free (irafheader);
+		irafheader = NULL;
+		return (NULL);
+		}
+	    free (irafheader);
+	    irafheader = NULL;
+	    }
+	else {
+	    if (verbose)
+		fprintf (stderr, "Cannot read IRAF header file %s\n", filename);
+	    return (NULL);
+	    }
+	}
+    else if (istiff (filename) || isgif (filename) || isjpeg (filename)) {
+	if ((header = fitsrtail (filename, &lhead, &nbfits)) == NULL) {
+	    if (verbose)
+		fprintf (stderr, "TIFF file %s has no appended header\n", filename);
+	    return (NULL);
+	    }
+	}
+
+
+    /* Open FITS file if .imh extension is not present */
+    else {
+	if ((header = fitsrhead (filename, &lhead, &nbfits)) == NULL) {
+	    if (verbose)
+		/* fprintf (stderr, "Cannot read FITS file %s\n", filename); */
+		fitserr ();
+	    return (NULL);
+	    }
+	}
+
+    return (header);
+}
+
+
+/* delete all the C* fields.
+ * return 0 if at least one such field is found, else -1.  */
+
+int
+DelWCSFITS (header, verbose)
+
+char *header;
+int verbose;
+
+{
+    static char flds[19][8];
+    char keyword[8];
+    int i;
+    int n, nfields;
+    double eq;
+    char rastr[32],decstr[32];
+
+    n = 0;
+
+    /* Delete standard WCS fields */
+    nfields = 19;
+    strcpy (flds[0], "CTYPE1");
+    strcpy (flds[1], "CTYPE2");
+    strcpy (flds[2], "CRVAL1");
+    strcpy (flds[3], "CRVAL2");
+    strcpy (flds[4], "CDELT1");
+    strcpy (flds[5], "CDELT2");
+    strcpy (flds[6], "CRPIX1");
+    strcpy (flds[7], "CRPIX2");
+    strcpy (flds[8], "CROTA1");
+    strcpy (flds[9], "CROTA2");
+    strcpy (flds[10], "IMWCS");
+    strcpy (flds[11], "CD1_1");
+    strcpy (flds[12], "CD1_2");
+    strcpy (flds[13], "CD2_1");
+    strcpy (flds[14], "CD2_2");
+    strcpy (flds[15], "PC1_1");
+    strcpy (flds[16], "PC1_2");
+    strcpy (flds[17], "PC2_1");
+    strcpy (flds[18], "PC2_2");
+    for (i = 0; i < nfields; i++) {
+	if (hdel (header, flds[i])) {
+	    n++;
+	    if (verbose)
+		fprintf (stderr,"%s: deleted\n", flds[i]);
+	    }
+	}
+
+    /* Delete projection parameters */
+    for (i = 0; i < 10; i++) {
+	sprintf (keyword, "PV1_%d", i);
+	if (hdel (header, keyword)) {
+	    n++;
+	    if (verbose)
+		fprintf (stderr,"%s: deleted\n", flds[i]);
+	    }
+	sprintf (keyword, "PV2_%d", i);
+	if (hdel (header, keyword)) {
+	    n++;
+	    if (verbose)
+		fprintf (stderr,"%s: deleted\n", flds[i]);
+	    }
+	}
+
+    /* Delete rotation matrix, if present */
+    if (hdel (header,"PC001001")) {
+	n++;
+	if (verbose)
+	    fprintf (stderr,"PC001001: deleted\n");
+	}
+    if (hdel (header,"PC001002")) {
+	n++;
+	if (verbose)
+	    fprintf (stderr,"PC001002: deleted\n");
+	}
+    if (hdel (header,"PC002001")) {
+	n++;
+	if (verbose)
+	    fprintf (stderr,"PC002001: deleted\n");
+	}
+    if (hdel (header,"PC002002")) {
+	n++;
+	if (verbose)
+	    fprintf (stderr,"PC002002: deleted\n");
+	}
+
+    if (verbose && n == 0)
+	fprintf (stderr,"DelWCSFITS: No WCS in header\n");
+
+    /* Delete RA DEC EPOCH, replacing with saved values, if present */
+    if (ksearch (header,"WRA")) {
+	hdel (header, "RA");
+	n++;
+	hchange (header, "WRA","RA");
+	if (ksearch (header,"WDEC")) {
+	    hdel (header, "DEC");
+	    n++;
+	    hchange (header, "WDEC", "DEC");
+	    }
+	if (ksearch (header,"WEPOCH")) {
+	    hdel (header, "EPOCH");
+	    n++;
+	    hchange (header, "WEPOCH", "EPOCH");
+	    }
+	if (ksearch (header,"WEQUINOX")) {
+	    hdel (header, "EQUINOX");
+	    n++;
+	    hchange (header, "WEQUINOX", "EQUINOX");
+	    }
+	if (ksearch (header, "EPOCH")) {
+	    hdel (header, "EQUINOX");
+	    n++;
+	    if (verbose)
+		fprintf (stderr,"EQUINOX deleted\n");
+	    }
+	hdel (header, "RADECSYS");
+	n++;
+	if (verbose)
+	    fprintf (stderr,"RADECSYS deleted\n");
+	hdel (header, "SECPIX1");
+	n++;
+	if (verbose)
+	    fprintf (stderr,"SECPIX1 deleted\n");
+	hdel (header, "SECPIX2");
+	n++;
+	if (verbose)
+	    fprintf (stderr,"SECPIX2 deleted\n");
+	if (verbose) {
+	    hgets (header,"RA", 31, rastr);
+	    hgets (header,"DEC", 31, decstr);
+	    eq = 0.0;
+	    hgetr8 (header,"EPOCH",&eq);
+	    if (eq == 0.0)
+		hgetr8 (header,"EQUINOX",&eq);
+	    fprintf (stderr,"DelWCS: Center reset to %s %s %.1f\n", rastr,decstr, eq);
+	    }
+	}
+    else if (ksearch (header, "EPOCH") && !ksearch (header, "PLTRAH")) {
+	if (hdel (header,"EQUINOX")) {
+	    if (verbose)
+		fprintf (stderr,"EQUINOX: deleted\n");
+	    n++;
+	    }
+	else if (verbose)
+	    fprintf (stderr,"DelWCS: EPOCH, but not EQUINOX found\n");
+	}
+
+    /* Delete IMWCS result keywords, if present */
+    if (ksearch (header, "WCSMATCH")) {
+	hdel (header, "WCSMATCH");
+	hdel (header, "WCSRFCAT");
+	hdel (header, "WCSIMCAT");
+	hdel (header, "WCSNREF");
+	hdel (header, "WCSTOL");
+	hdel (header, "WCSSEP");
+	}
+
+    /* Delete SAO polynomial, if present */
+    if (ksearch (header, "CO1_1")) {
+	int i;
+	char keyword[16];
+
+	for (i = 1; i < 13; i++) {
+	    sprintf (keyword,"CO1_%d", i);
+	    hdel (header, keyword);
+	    if (verbose)
+		fprintf (stderr,"%s deleted\n", keyword);
+	    n++;
+	    }
+	for (i = 1; i < 13; i++) {
+	    sprintf (keyword,"CO2_%d", i);
+	    hdel (header, keyword);
+	    if (verbose)
+		fprintf (stderr,"%s deleted\n", keyword);
+	    n++;
+	    }
+	}
+
+    /* Delete rotation matrix, if present */
+    if (ksearch (header, "CO1_1")) {
+	int i, j;
+	char keyword[16];
+	for (i = 1; i < 6; i++) {
+	    for (j = 1; i < 6; i++) {
+		sprintf (keyword,"PC%03d%03d", i, j);
+		hdel (header, keyword);
+		if (verbose)
+		    fprintf (stderr,"%s deleted\n", keyword);
+		n++;
+		}
+	    }
+	}
+
+    if (n > 0 && verbose)
+	fprintf (stderr,"%d keywords deleted\n", n);
+
+    return (n);
+}
+
+
+/* check the WCS fields and print any that are found if verbose.
+ * return 0 if all are found, else -1.
+ */
+int
+PrintWCS (header, verbose)
+char	*header;	/* FITS header */
+int	verbose;	/* 1 to print WCS header keyword values */
+
+{
+    char str[80];
+    double v;
+    int n, i;
+    char keyword[16];
+
+    n = 0;
+
+    if (hgets (header,"IMWCS",80,str)) {
+	if (verbose) fprintf (stderr,"IMWCS = %s\n", str);
+	n++;
+	}
+    if (hgets (header,"CTYPE1",16,str)) {
+	if (verbose) fprintf (stderr,"CTYPE1 = %s\n", str);
+	n++;
+	}
+    if (hgetr8 (header, "CRVAL1", &v)) {
+	if (verbose) fprintf (stderr,"CRVAL1 = %.8f\n", v);
+	n++;
+	}
+    if (hgetr8 (header, "CRPIX1", &v)) {
+	if (verbose) fprintf (stderr,"CRPIX1 = %.8f\n", v);
+	n++;
+	}
+
+    if (hgets (header,"CTYPE2",16,str)) {
+	if (verbose) fprintf (stderr,"CTYPE2 = %s\n", str);
+	n++;
+	}
+    if (hgetr8 (header, "CRVAL2", &v)) {
+	if (verbose) fprintf (stderr,"CRVAL2 = %.8f\n", v);
+	n++;
+	}
+    if (hgetr8 (header, "CRPIX2", &v)) {
+	if (verbose) fprintf (stderr,"CRPIX2 = %.8f\n", v);
+	n++;
+	}
+
+    /* Polynomial plate fit */
+    if (hgetr8 (header, "CO1_1", &v)) {
+	if (verbose) fprintf (stderr,"CO1_1 = %.8g\n", v);
+	for (i = 1; i < 20; i++) {
+	    sprintf (keyword,"CO1_%d",i+1);
+	    if (hgetr8 (header, keyword, &v)) {
+		if (verbose) fprintf (stderr,"%s = %.8g\n", keyword, v);
+		n++;
+		}
+	    }
+	}
+    if (hgetr8 (header, "CO2_1", &v)) {
+	if (verbose) fprintf (stderr,"CO2_1 = %.8g\n", v);
+	for (i = 1; i < 20; i++) {
+	    sprintf (keyword,"CO2_%d",i+1);
+	    if (hgetr8 (header, keyword, &v)) {
+		if (verbose) fprintf (stderr,"%s = %.8g\n", keyword, v);
+		n++;
+		}
+	    }
+	}
+
+    /* Plate scale and rotation from CD matrix */
+    if (hgetr8 (header, "CD1_1", &v)) {
+	if (verbose) fprintf (stderr,"CD1_1 = %.8g\n", v);
+	n++;
+	if (hgetr8 (header, "CD1_2", &v)) {
+	    if (verbose) fprintf (stderr,"CD1_2 = %.8g\n", v);
+	    n++;
+	    }
+	if (hgetr8 (header, "CD2_1", &v)) {
+	    if (verbose) fprintf (stderr,"CD2_1 = %.8g\n", v);
+	    n++;
+	    }
+	if (hgetr8 (header, "CD2_2", &v)) {
+	    if (verbose) fprintf (stderr,"CD2_2 = %.8g\n", v);
+	    n++;
+	    }
+	}
+
+    /* Plate scale and rotation from CDELTn and CROTAn */
+    else {
+	if (hgetr8 (header, "CDELT1", &v)) {
+	    if (verbose) fprintf (stderr,"CDELT1 = %.8f\n", v);
+	    n++;
+	    }
+	if (hgetr8 (header, "CROTA1", &v)) {
+	    if (verbose) fprintf (stderr,"CROTA1 = %.3f\n", v);
+	    n++;
+	    }
+	if (hgetr8 (header, "CDELT2", &v)) {
+	    if (verbose) fprintf (stderr,"CDELT2 = %.8f\n", v);
+	    n++;
+	    }
+	if (hgetr8 (header, "CROTA2", &v)) {
+	    if (verbose) fprintf (stderr,"CROTA2 = %.3f\n", v);
+	    n++;
+	    }
+	}
+
+    return (n > 8 ? 0 : -1);
+}
+
+static char wcsproj[8]="TAN";		/* WCS projection name */
+void
+setwcsproj (type)
+char *type;
+{ strcpy (wcsproj, type); return; }
+
+
+/* Set FITS C* fields, assuming RA/DEC refers to the reference pixel, CRPIX1/CRPIX2 */
+
+void
+SetFITSWCS (header, wcs)
+
+char	*header;	/* Image FITS header */
+struct WorldCoor *wcs;	/* WCS structure */
+
+{
+    double ep;
+    char wcstemp[16];
+    char *wcsdist;
+
+    /* Rename old center coordinates */
+    if (!ksearch (header,"WRA") && ksearch (header,"RA"))
+	hchange (header,"RA","WRA");
+    if (!ksearch (header,"WDEC") && ksearch (header,"DEC"))
+	hchange (header,"DEC","WDEC");
+
+    if (!ksearch (header,"WEQUINOX") && ksearch (header,"EQUINOX"))
+	hchange (header, "EQUINOX", "WEQUINOX");
+
+    /* Only change EPOCH if it is used instead of EQUINOX */
+    else if (!ksearch (header,"WEPOCH") && ksearch (header,"EPOCH"))
+	hchange (header, "EPOCH", "WEPOCH");
+
+
+    /* Set new center coordinates */
+    if (wcs->xref < 0)
+	wcs->xref = 360.0 + wcs->xref;
+    hputra (header,"RA",wcs->xref);
+    hputdec (header,"DEC",wcs->yref);
+    hputr8 (header, "EQUINOX", wcs->equinox);
+    if (hgetr8 (header, "WEPOCH", &ep))
+	hputr8 (header, "EPOCH", wcs->equinox);
+    else if (!hgetr8 (header, "EPOCH", &ep))
+	hputr8 (header, "EPOCH", wcs->equinox);
+
+    if (wcs->radecsys[0] == 'B' || wcs->radecsys[0] == 'b')
+	hputs (header, "RADECSYS", "FK4");
+    else if (wcs->radecsys[0] == 'I' || wcs->radecsys[0] == 'i')
+	hputs (header, "RADECSYS", "IRCS");
+    else if (wcs->radecsys[0] == 'J' || wcs->radecsys[0] == 'j')
+	hputs (header, "RADECSYS", "FK5");
+    else
+	hputs (header, "RADECSYS", wcs->radecsys);
+
+    /* Set standard FITS WCS keywords */
+    wcsdist = getdistcode (wcs);	/* FITS WCS distortion code */
+    strcpy (wcstemp, "RA---");
+    strcat (wcstemp, wcsproj);
+    if (wcsdist != NULL)
+	strcat (wcstemp, wcsdist);
+    hputs  (header, "CTYPE1", wcstemp);
+    strcpy (wcstemp, "DEC--");
+    strcat (wcstemp, wcsproj);
+    if (wcsdist != NULL)
+	strcat (wcstemp, wcsdist);
+    hputs  (header, "CTYPE2", wcstemp);
+
+    /* Reference pixel in WCS and image coordinates */
+    hputnr8 (header, "CRVAL1", 9, wcs->xref);
+    hputnr8 (header, "CRVAL2", 9, wcs->yref);
+    hputnr8 (header, "CRPIX1", 4, wcs->xrefpix);
+    hputnr8 (header, "CRPIX2", 4, wcs->yrefpix);
+
+    /* CD matrix (proposed FITS standard) */
+    if (wcs->rotmat) {
+	hputnr8 (header, "CD1_1", 12, wcs->cd[0]);
+	hputnr8 (header, "CD1_2", 12, wcs->cd[1]);
+	hputnr8 (header, "CD2_1", 12, wcs->cd[2]);
+	hputnr8 (header, "CD2_2", 12, wcs->cd[3]);
+	hdel (header, "CDELT1");
+	hdel (header, "CDELT2");
+	hdel (header, "CROTA1");
+	hdel (header, "CROTA2");
+	}
+
+    /* Scale and rotation (old FITS standard) */
+    else {
+	hputnr8 (header, "CDELT1", 12, wcs->xinc);
+	hputnr8 (header, "CDELT2", 12, wcs->yinc);
+	hputnr8 (header, "CROTA1", 6, wcs->rot);
+	hputnr8 (header, "CROTA2", 6, wcs->rot);
+	hputnr8 (header, "CD1_1", 12, wcs->cd[0]);
+	hputnr8 (header, "CD1_2", 12, wcs->cd[1]);
+	hputnr8 (header, "CD2_1", 12, wcs->cd[2]);
+	hputnr8 (header, "CD2_2", 12, wcs->cd[3]);
+	/* hdel (header, "CD1_1");
+	hdel (header, "CD1_2");
+	hdel (header, "CD2_1");
+	hdel (header, "CD2_2"); */
+	}
+
+    /* Plate scale at reference pixel */
+    if (-wcs->xinc != wcs->yinc) {
+	if (ksearch (header,"SECPIX"))
+	    hdel (header,"SECPIX");
+	hputnr8 (header, "SECPIX1", 4, -wcs->xinc*3600.0);
+	hputnr8 (header, "SECPIX2", 4, wcs->yinc*3600.0);
+	}
+    else {
+	if (ksearch (header,"SECPIX1"))
+	    hdel (header,"SECPIX1");
+	if (ksearch (header,"SECPIX2"))
+	    hdel (header,"SECPIX2");
+	hputnr8 (header, "SECPIX", 6, wcs->yinc*3600.0);
+	}
+
+    /* Plate fit coefficients, if present */
+    if (wcs->ncoeff1 > 0) {
+	char keyword[16];
+	int i;
+	for (i = 0; i < wcs->ncoeff1; i++) {
+	    sprintf (keyword, "CO1_%d",i+1);
+	    hputr8 (header, keyword, wcs->x_coeff[i]);
+	    }
+	}
+    if (wcs->ncoeff2 > 0) {
+	char keyword[16];
+	int i;
+	for (i = 0; i < wcs->ncoeff2; i++) {
+	    sprintf (keyword, "CO2_%d",i+1);
+	    hputr8 (header, keyword, wcs->y_coeff[i]);
+	    }
+	}
+
+    return;
+}
+
+
+/* May 29 1996	Change name from delWCSFITS to DelWCSFITS
+ * May 31 1996	Print single message if no WCS is found in header
+ * May 31 1996	Use stream I/O instead of standard I/O
+ * Jun 10 1996	Combine imgetwcs.c and imdelwcs.c into fitswcs.c
+ * Jun 17 1996	Delete IMWCS record, too
+ * Jul 16 1996	Update arguments for header-reading subroutines
+ * Aug  6 1996  Fixed small defects after lint
+ * Aug  8 1996  Restore old image center after deleting WCS
+ * Aug 26 1996	Fix subroutine arguments after lint
+ *
+ * Feb 21 1997  Check pointers against NULL explicitly for Linux
+ * Feb 21 1997  Add GetFITShead() subroutine and use it
+ * Mar 20 1997	Remove unused variables
+ * Nov  6 1997	Add PrintWCS() from IMWCS
+ *
+ * Jan  7 1998	Return NULL WCS structure if no FITS header can be read
+ * Feb 18 1998	Move SetFITSWCS() here from imsetwcs.c
+ * Feb 24 1998	Delete CD matrix in DelWCS()
+ * Mar 20 1998	Write CD matrix in SetFITSWCS()
+ * Mar 27 1998	Add plate constants in SetFITSWCS()
+ * Mar 27 1998	Delete plate constants in DelFITSWCS()
+ * Apr  6 1998	Change coefficient keywords from PLTij to COi_j
+ * Apr  7 1998	Change amd_i_coeff to i_coeff
+ * Apr 10 1998	Write out polynomial coefficients correctly
+ * Apr 13 1998	Print polynomial coefficients, if in header
+ * Apr 16 1998	Drop NCOEFF header parameter
+ * Apr 17 1998	Do not write W* keywords if they are already there
+ * May 27 1998	Include fitsio.h instead of fitshead.h
+ * Jun  1 1998	Print error message if WCS cannot be initialized
+ * Jun 11 1998	Change WCSTYPE to WCSPROJ to avoid conflict
+ * Jul 23 1998	In DelWCS, delete specific number of fields
+ * Jul 24 1998	Make irafheader char instead of int
+ * Jul 27 1998	Set irafheader pointer to NULL after use
+ * Aug  6 1998	Change fitsio.h to fitsfile.h
+ * Oct  5 1998	Use isiraf() to determine file type
+ * Oct 28 1998	Delete EQUINOX, RADECSYS, SECPIX1, SECPIX2 from imwcs
+ *
+ * Apr  7 1999	Add file name to error message if WCS error
+ * Jul  8 1999	Write RADECSYS as FK5 or FK4 instead of J2000 or B1950
+ * Jul 21 1999	Add SECPIX plate scale output to SetFITSWCS()
+ * Oct 21 1999	Fix declarations after lint
+ *
+ * Mar 23 2000	Fix bug in IRAF header error message
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Jan 31 2001	Add code to extract WCS name or character from filename
+ * Mar  8 2001	Change WCS character separator from : to % in FITS filenames
+ * Jul 11 2001	Add PC matrix to keywords deleted by DelWCS()
+ *
+ * Apr 23 2002	Always write CD matrix in SetFITSWCS()
+ * Jun 19 2002	Add verbose argument to GetWCSFITS() and GetFITShead()
+ *
+ * Feb 10 2003	Print 12 decimal places instead of 9 for CD matrix and CDELT
+ * Oct 23 2003	Add PCi_j to DelWCSFITS()
+ * Nov  3 2003	In SetFITSWCS(), add distortion code if in WCS
+ * Dec  5 2003	Fix bug, delete projection parameters in DelWCSFITS()
+ *
+ * Jul 19 2004	Print error message in verbose mode only
+ * Sep 16 2004	Add 360.0 to negative right ascensions in SetFITSWCS()
+ * Nov  1 2005	Set RADECSYS to ICRS if appropriate
+ *
+ * Feb 23 2006	Add code to read FITS header appended to TIFF file
+ * Jun  1 2006	Fix bug so CD matrix is deleted by DelWCSFITS()
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ *
+ * Apr  2 2007	Fix DelWCSFITS() argument description at top of file
+ * Apr 18 2007	Delete WCS result keywords with DelWCSFITS()
+ * Dec 20 2007	Print error message set by fitsrhead()
+ */
diff --git a/Code/src/libwcs/fortcat.c b/Code/src/libwcs/fortcat.c
new file mode 100644
index 0000000000000000000000000000000000000000..436d72f849a25cc666adc0ff93d733a6a35af377
--- /dev/null
+++ b/Code/src/libwcs/fortcat.c
@@ -0,0 +1,161 @@
+/*** File libwcs/fortcat.c
+ *** April 3, 2003
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2001-2003
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* Fortran wrappers for subroutines which read astronomical catalogs
+ *
+ * Catalogs supported include: USNO-A2.0, USNO_SA2.0, ACT, Tycho 2, SAO
+ * TDC binary format (SAO, PPM, Yale Bright Star, IRAS Point Source Catalogs)
+ * SAO TDC ASCII format, and Starbase tab-delimited format
+ *
+ * For shell-level searches, use WCSTools scat, documented at
+ * http://tdc-www.harvard.edu/software/wcstools/scat/
+ *
+ * int catread_()	Read catalog stars in specified region of the sky
+ * int catrnum_()	Read catalog stars with specified numbers
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <sys/types.h>
+#include "wcs.h"
+#include "wcscat.h"
+
+/* default pathname for catalog, used if catalog file not found in current
+   working directory, but overridden by WCS_CATDIR environment variable */
+char catdir[64]="/data/catalogs";
+
+static struct StarCat **starcat; /* Catalog data structure */
+
+/* CATREAD -- Read ASCII stars in specified region using ctgread() */
+
+void
+catread_ (catfile, distsort, cra, cdec, dra, ddec, drad,
+	  csysout, eqout, epout, mag1, mag2, nsmax, nlog, nstars,
+	  xnum, xra, xdec, xpra, xpdec, xmag, xmagb)
+
+char	*catfile;	/* Name of reference star catalog file */
+int	distsort;	/* 1 to sort stars by distance from center */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+char	*csysout;	/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int     nsmax;		/* Maximum number of stars to be returned */
+int     nlog;		/* Logging frequency */
+int	*nstars;		/* Number of catalog stars found (returned) */
+double	*xnum;		/* Array of ID numbers (returned) */
+double	*xra;		/* Array of right ascensions (returned) */
+double	*xdec;		/* Array of declinations (returned) */
+double	*xpra;		/* Array of right ascension proper motions (returned) */
+double	*xpdec;		/* Array of declination proper motions (returned) */
+double	*xmag;		/* Array of magnitudes (returned) */
+double	*xmagb;		/* Array of second magnitudes (returned) */
+
+{
+int     refcat;         /* Catalog code from wcscat.h */
+char    **tobj;         /* Array of object names (ignored) */
+int     *tc;            /* Array of fluxes (ignored) */
+int	nread;		/* Number of stars read from catalog */
+int     sysout;         /* Search coordinate system */
+
+    tc = NULL;
+    tobj = NULL;
+
+    refcat = CatCode (catfile);
+    sysout = wcscsys (csysout);
+
+    nread = ctgread (catfile, refcat, distsort, cra, cdec, dra, ddec, drad,
+         sysout, eqout, epout, mag1, mag2, nsmax, starcat,
+         xnum, xra, xdec, xpra, xpdec, xmag, xmagb, tc, tobj, nlog);
+
+    /* Return number of stars read or maximum, which ever is lower */
+    if (nread < nsmax)
+	*nstars = nread;
+    else
+	*nstars = nsmax;
+
+    return;
+}
+
+
+/* CATRNUM -- Read ASCII stars with specified numbers using ctgrnum() */
+
+void
+catrnum_ (catfile, nnum, csysout, eqout, epout, match, nlog, nstars,
+          xnum, xra, xdec, xpra, xpdec, xmag, xmagb)
+
+char    *catfile;       /* Name of reference star catalog file */
+int     nnum;           /* Number of stars to look for */
+char	*csysout;	/* Search coordinate system */
+double  eqout;          /* Search coordinate equinox */
+double  epout;          /* Proper motion epoch (0.0 for no proper motion) */
+int     match;          /* 1 to match star number exactly, else sequence num.*/
+int     nlog;		/* Logging frequency */
+int	*nstars;	/* Number of catalog stars found (returned) */
+double  *xnum;          /* Array of star numbers to look for */
+double  *xra;           /* Array of right ascensions (returned) */
+double  *xdec;          /* Array of declinations (returned) */
+double  *xpra;          /* Array of right ascension proper motions (returned) */
+double  *xpdec;         /* Array of declination proper motions (returned) */
+double  *xmag;          /* Array of magnitudes (returned) */
+double  *xmagb;         /* Array of second magnitudes (returned) */
+
+{
+int     refcat;         /* Catalog code from wcscat.h */
+int     *tc;            /* Array of fluxes (ignore) */
+char    **tobj;         /* Array of object names (ignored) */
+int     sysout;         /* Search coordinate system */
+int	nread;		/* Number of stars read from catalog */
+
+    tc = NULL;
+    tobj = NULL;
+
+    sysout = wcscsys (csysout);
+
+    refcat = CatCode (catfile);
+
+    nread = ctgrnum (catfile,refcat, nnum,sysout,eqout,epout,match,starcat,
+		     xnum,xra,xdec,xpra,xpdec,xmag,xmagb,tc,tobj,nlog);
+    *nstars = nread;
+
+    return;
+}
+/*
+ * Feb 16 2001	New subroutines
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ *
+ * Apr  3 2003	Use CatCode() instead of RefCat(); add nstars to catrnum_()
+ */
diff --git a/Code/src/libwcs/fortwcs.c b/Code/src/libwcs/fortwcs.c
new file mode 100644
index 0000000000000000000000000000000000000000..cbb232b68329ef60779821c8687fb1d3b4770f12
--- /dev/null
+++ b/Code/src/libwcs/fortwcs.c
@@ -0,0 +1,583 @@
+/*** File saoimage/wcslib/fortwcs.c
+ *** April 7, 2003
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2003
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	wcsfort.c (World Coordinate Systems)
+ * Purpose:	Fortran interface to C WCS subroutines in wcsinit.c and wcs.c
+ * Subroutine:	wcsinit_ (hstring, wcs) sets WCS structure from an image header
+ * Subroutine:	wcsxinit_ (cra,cdec,secpix,xrpix,yrpix,nxpix,nypix,rotate,
+		    equinox,epoch,proj)	sets WCS structure from arguments
+ * Subroutine:	wcskinit_ (nxpix,nypix,ctype1,ctype2,crpix1,crpix2,crval1,
+ *			   crval2, cd,cdelt1,cdelt2,crota,equinox,epoch)
+ *		    sets a WCS structure from keyword-based arguments
+ * Subroutine:	wcsclose_ (iwcs) closes and frees the specified WCS structure
+ * Subroutine:	wcssize_ (wcs, cra, cdec, dra, ddec)
+ *		    returns the image center and size in WCS units
+ * Subroutine:	wcsdist_ (x1,y1,x2,y2,diff)
+ *		    compute angular distance between ra/dec or lat/long
+ * Subroutine:	wcsoutinit (wcs,coor) sets up the output coordinate system
+ * Subroutine:	getwcsout_ (iwcs, coorsys)
+ *		Return WCS output coordinate system used by pix2wcs
+ * Subroutine:	wcsininit (wcs,coor) sets up the input coordinate system
+ * Subroutine:	getwcsin_ (iwcs, coorsys)
+ *		Return WCS output coordinate system used by pix2wcs
+ * Subroutine:	getwcsim_ (iwcs, coorsys) Return WCS coordinate system of image 
+ * Subroutine:	setwcslin_ (iwcs, mode)
+ *		Set output string mode for LINEAR coordinates
+ * Subroutine:	setwcsdeg_ (iwcs, mode)
+ *		Set output string mode as decimal degrees if not zero
+ * Subroutine:	pix2wcs_ (wcs,xpix,ypix,xpos,ypos)
+ *		    pixel coordinates -> sky coordinates
+ * Subroutine:	pix2wcst_ (wcs,xpix,ypix,wcstring,lstr)
+ *		    pixels -> sky coordinate string
+ * Subroutine:	wcs2pix_ (wcs,xpos,ypos,xpix,ypix)
+ *		    sky coordinates -> pixel coordinates
+
+ * Copyright:   2000-2003 Smithsonian Astrophysical Observatory
+ *              You may do anything you like with this file except remove
+ *              this copyright.  The Smithsonian Astrophysical Observatory
+ *              makes no representations about the suitability of this
+ *              software for any purpose.  It is provided "as is" without
+ *              express or implied warranty.
+ */
+
+#include <string.h>		/* strstr, NULL */
+#include <stdio.h>		/* stderr */
+#include <math.h>		/* stderr */
+#include "wcs.h"
+#ifndef VMS
+#include <stdlib.h>
+#endif
+
+static struct WorldCoor **pwcs;
+static int nwcs = 0;
+
+/* set up a WCS structure from a FITS or IRAF image header */
+
+/* Call WCSINIT (HSTRING, IWCS)
+ * where HSTRING is a Character string and IWCS is a returned integer
+ * identifying the data structure which is created */
+
+void
+wcsinit_ (hstring, iwcs, nc)
+
+char	*hstring;	/* Character string containing FITS header information
+			   in the format <keyword>= <value> {/ <comment>} */
+int	*iwcs;		/* Pointer to wcs structure (returned) */
+int	nc;		/* Number of characters in hstring (supplied by Fortran */
+{
+    int id;
+    struct WorldCoor *twcs, *wcsinit();
+
+    twcs = wcsinit (hstring);
+
+    /* Set index to -1 if no WCS is found in header */
+    if (twcs == NULL)
+	*iwcs = -1;
+
+    /* Otherwise, use first available index into vector of pointers */
+    else if (nwcs == 0) {
+	pwcs = (struct WorldCoor **) calloc (10, sizeof (void *));
+	nwcs = 10;
+	*iwcs = 0;
+	pwcs[0] = twcs;
+	}
+    else {
+	for (id = 0; id < nwcs; id++) {
+	    if (pwcs[id] == NULL) {
+		*iwcs = id;
+		pwcs[id] = twcs;
+		break;
+		}
+	    }
+	}
+    return;
+}
+
+/* set up a WCS structure from subroutine arguments*/
+
+void
+wcsxinit_ (iwcs,cra,cdec,secpix,xrpix,yrpix,nxpix,nypix,rotate,equinox,
+	   epoch,proj,np)
+
+int	*iwcs;		/* Pointer to wcs structure (returned) */
+double	*cra;		/* Center right ascension in degrees */
+double	*cdec;		/* Center declination in degrees */
+double	*secpix;	/* Number of arcseconds per pixel */
+double	*xrpix;		/* Reference pixel X coordinate */
+double	*yrpix;		/* Reference pixel X coordinate */
+int	*nxpix;		/* Number of pixels along x-axis */
+int	*nypix;		/* Number of pixels along y-axis */
+double	*rotate;	/* Rotation angle (clockwise positive) in degrees */
+int	*equinox;	/* Equinox of coordinates, 1950 and 2000 supported */
+double	*epoch;		/* Epoch of coordinates, used for FK4/FK5 conversion
+			 * no effect if 0 */
+char	*proj;		/* Projection */
+int	np;		/* Length of projection (supplied by Fortran) */
+
+{
+    int id;
+    struct WorldCoor *twcs;
+    
+    twcs = wcsxinit (*cra,*cdec,*secpix,*xrpix,*yrpix,*nxpix,*nypix,
+		     *rotate,*equinox,*epoch,proj);
+
+    /* Set index to -1 if no WCS is found in header */
+    if (twcs == NULL)
+	*iwcs = -1;
+
+    /* Otherwise, use first available index into vector of pointers */
+    else if (nwcs == 0) {
+	pwcs = (struct WorldCoor **) calloc (10, sizeof (void *));
+	nwcs = 10;
+	*iwcs = 0;
+	pwcs[0] = twcs;
+	}
+    else {
+	for (id = 0; id < 10; id++) {
+	    if (pwcs[id] == NULL) {
+		*iwcs = id;
+		pwcs[id] = twcs;
+		break;
+		}
+	    }
+	}
+    return;
+}
+
+/* set up a WCS structure from subroutine arguments matching FITS keywords*/
+
+void
+wcskinit_ (iwcs,naxis1,naxis2,ctype1,ctype2,crpix1,crpix2,crval1,crval2,cd,
+	   cdelt1,cdelt2,crota,equinox,epoch, nc1,nc2)
+
+int	*iwcs;		/* Pointer to wcs structure (returned) */
+int	*naxis1;	/* Number of pixels along x-axis */
+int	*naxis2;	/* Number of pixels along y-axis */
+char	*ctype1;	/* FITS WCS projection for axis 1 */
+char	*ctype2;	/* FITS WCS projection for axis 2 */
+double	*crpix1, *crpix2; /* Reference pixel coordinates */
+double	*crval1, *crval2; /* Coordinates at reference pixel in degrees */
+double	*cd;		/* Rotation matrix, used if all 4 elements nonzero */
+double	*cdelt1, *cdelt2; /* scale in degrees/pixel, ignored if cd is not NULL */
+double	*crota;		/* Rotation angle in degrees, ignored if cd is set */
+int	*equinox;	/* Equinox of coordinates, 1950 and 2000 supported */
+double	*epoch;		/* Epoch of coordinates, used for FK4/FK5 conversion
+			 * no effect if 0 */
+int	nc1, nc2;	/* Lengths of CTYPEs (supplied by Fortran) */
+
+{
+    int id;
+    struct WorldCoor *twcs;
+
+    twcs = wcskinit (*naxis1,*naxis2,ctype1,ctype2,*crpix1,*crpix2,*crval1,
+		     *crval2, cd, *cdelt1, *cdelt2, *crota, *equinox, *epoch);
+
+    /* Set index to -1 if no WCS is found in header */
+    if (twcs == NULL)
+	*iwcs = -1;
+
+    /* Otherwise, use first available index into vector of pointers */
+    else if (nwcs == 0) {
+	pwcs = (struct WorldCoor **) calloc (10, sizeof (void *));
+	nwcs = 10;
+	*iwcs = 0;
+	pwcs[0] = twcs;
+	}
+    else {
+	for (id = 0; id < nwcs; id++) {
+	    if (pwcs[id] == NULL) {
+		*iwcs = id;
+		pwcs[id] = twcs;
+		break;
+		}
+	    }
+	}
+    return;
+}
+
+
+void
+wcsclose_ (iwcs)
+
+int	*iwcs;		/* Index to WCS pointer array */
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+    if (wcs != NULL) {
+	wcsfree (wcs);
+	pwcs[*iwcs] = NULL;
+	}
+    return;
+}
+
+
+/* Return RA and Dec of image center, plus size in RA and Dec */
+
+void
+wcssize_ (iwcs, cra, cdec, dra, ddec, radecsys)
+
+int	*iwcs;		/* Index to WCS pointer array */
+double	*cra;		/* Right ascension of image center (rad) (returned) */
+double	*cdec;		/* Declination of image center radg) (returned) */
+double	*dra;		/* Half-width in radians (returned) */
+double	*ddec;		/* Half-height in radians (returned) */
+char	*radecsys;	/* Equinox (returned) */
+
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+    double width, height;
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    /* If there is no WCS defined, return all zeroes */
+    if (wcs == NULL) {
+	(void)fprintf (stderr,"No WCS info available\n");
+	*cra = 0.0;
+	*cdec = 0.0;
+	*dra = 0.0;
+	*ddec = 0.0;
+	}
+
+    else {
+	wcsfull (wcs, cra, cdec, &width, &height);
+	*dra = 0.5 * width;
+	*ddec = 0.5 * height;
+
+	/* Get coordinate system from structure */
+	strcpy (radecsys, wcs->radecsys);
+	}
+    return;
+}
+
+
+/* Compute distance in degrees between two sky coordinates */
+
+void
+wcsdist_ (x1,y1,x2,y2,diff)
+
+double	*x1,*y1;	/* (RA,Dec) or (Long,Lat) in degrees */
+double	*x2,*y2;	/* (RA,Dec) or (Long,Lat) in degrees */
+double	*diff;		/* Distance in degrees */
+
+{
+    double xr1, xr2, yr1, yr2;
+    double pos1[3], pos2[3], w, cosb;
+    int i;
+
+    /* Convert two vectors to direction cosines */
+    xr1 = degrad (*x1);
+    yr1 = degrad (*y1);
+    cosb = cos (yr1);
+    pos1[0] = cos (xr1) * cosb;
+    pos1[1] = sin (xr1) * cosb;
+    pos1[2] = sin (yr1);
+
+    xr2 = degrad (*x2);
+    yr2 = degrad (*y2);
+    cosb = cos (yr2);
+    pos2[0] = cos (xr2) * cosb;
+    pos2[1] = sin (xr2) * cosb;
+    pos2[2] = sin (yr2);
+
+    /* Modulus squared of half the difference vector */
+    w = 0.0;
+    for (i = 0; i < 3; i++) {
+	w = w + (pos1[i] - pos2[i]) * (pos1[i] - pos2[i]);
+	}
+    w = w / 4.0;
+    if (w > 1.0) w = 1.0;
+
+    /* Angle beween the vectors */
+    *diff = 2.0 * atan2 (sqrt (w), sqrt (1.0 - w));
+    *diff = raddeg (*diff);
+    return;
+}
+
+
+/* Initialize WCS output coordinate system used by pix2wcs */
+
+void
+wcsoutinit_ (iwcs, coorsys, nc)
+
+int	*iwcs;		/* Index to WCS structure */
+char	*coorsys;	/* Output coordinate system */
+int	nc;		/* Length of coorsys (set by Fortran) */
+
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	wcsoutinit (wcs, coorsys);
+
+    return;
+}
+
+
+/* Return WCS output coordinate system used by pix2wcs */
+
+void
+getwcsout_ (iwcs, coorsys, nc)
+
+int	*iwcs;		/* Index to WCS structure */
+char	*coorsys;	/* Output coordinate system (returned) */
+int	nc;		/* Length of coorsys (set by Fortran) */
+
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	strncpy (coorsys, getwcsout (wcs), nc);
+
+    return;
+}
+
+
+/* Initialize WCS input coordinate system used by wcs2pix */
+
+void
+wcsininit_ (iwcs, coorsys, nc)
+
+int	*iwcs;		/* Index to WCS structure */
+char	*coorsys;	/* Input coordinate system */
+int	nc;		/* Length of coorsys (Set by Fortran) */
+
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	wcsininit (wcs, coorsys);
+
+    return;
+}
+
+
+/* Return WCS input coordinate system used by wcs2pix */
+
+void
+getwcsin_ (iwcs, coorsys, nc)
+
+int	*iwcs;		/* Index to WCS structure */
+char	*coorsys;	/* Input coordinate system (returned) */
+int	nc;		/* Length of coorsys (Set by Fortran) */
+
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	strncpy (coorsys, getwcsin (wcs), nc);
+
+    return;
+}
+
+
+/* Return WCS coordinate system of image */
+
+void
+getwcsim_ (iwcs, coorsys, nc)
+
+int	*iwcs;		/* Index to WCS structure */
+char	*coorsys;	/* Image coordinate system (returned) */
+int	nc;		/* Length of coorsys (Set by Fortran) */
+
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	strncpy (coorsys, getradecsys (wcs), nc);
+
+    return;
+}
+
+
+/* Set WCS output in degrees or hh:mm:ss dd:mm:ss */
+
+void
+setwcsdeg_ (iwcs, mode)
+
+int	*iwcs;		/* Index to WCS structure */
+int	*mode;		/* mode = 0: h:m:s d:m:s
+			   mode = 1: fractional degrees */
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	wcs->degout = *mode;
+
+    return;
+}
+
+
+/* Set output string mode for LINEAR coordinates */
+
+void
+setwcslin_ (iwcs, mode)
+
+int	*iwcs;		/* World coordinate system structure */
+int	*mode;		/* mode = 0: x y linear
+			   mode = 1: x units x units
+			   mode = 2: x y linear units */
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	wcs->linmode = *mode;
+
+    return;
+}
+
+
+/* Convert pixels to sky coordinate string */
+
+void
+pix2wcst_ (iwcs,xpix,ypix,wcstring,lstr)
+
+int	*iwcs;		/* World coordinate system structure */
+double	*xpix, *ypix;	/* x and y image coordinates in pixels */
+char	*wcstring;	/* World coordinate string (returned) */
+int	lstr;		/* Length of world coordinate string (set by Fortran) */
+
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	pix2wcst (wcs, *xpix, *ypix, wcstring, lstr);
+
+    return;
+}
+
+
+/* Convert pixel coordinates to World Coordinates */
+
+void
+pix2wcs_ (iwcs, xpix, ypix, xpos, ypos)
+
+int	*iwcs;		/* World coordinate system structure */
+double	*xpix, *ypix;	/* x and y image coordinates in pixels */
+double	*xpos, *ypos;	/* RA and Dec in radians (returned) */
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	pix2wcs (wcs, *xpix, *ypix, xpos, ypos);
+
+    return;
+}
+
+
+/* Convert World Coordinates to pixel coordinates */
+
+void
+wcs2pix_ (iwcs, xpos, ypos, xpix, ypix, offscl)
+
+int	*iwcs;		/* World coordinate system structure */
+double	*xpos, *ypos;	/* World coordinates in degrees */
+double	*xpix, *ypix;	/* Image coordinates in pixels */
+int	*offscl;
+{
+    struct WorldCoor *wcs;	/* World coordinate system structure */
+
+    if (*iwcs >= 0 && *iwcs < nwcs)
+	wcs = pwcs[*iwcs];
+    else
+	wcs = pwcs[0];
+
+    if (wcs != NULL)
+	wcs2pix (wcs, *xpos, *ypos, xpix, ypix, offscl);
+
+    return;
+}
+/* Jan  4 1996	new program
+ * Jan 12 1996	Add WCSSET to set WCS without an image
+ *
+ * Jun 15 1998	rename wcsf77.c to wcsfort.c and move to libwcs
+ * Jun 15 1998	rename WCSSET WCSXINIT and update arguments
+ * Jul  7 1998	Change setlinmode to setwcslin_; add setwcsdeg_
+ *
+ * Oct 15 1999	Free wcs using wcsfree()
+ * Oct 21 1999	Add pix2wcst_(); drop unused variables after lint
+ * Dec 10 1999	Add error handling for iwcs; document all subroutines
+ *
+ * Jun  2 2000	Fix WCS structure pointers
+ *
+ * Feb 16 2001	Change name of file from wcsfort.c to fortwcs.c
+ *
+ * Apr  7 2003	Add wcsclose_() to list at top of file
+ * Apr  7 2003	Fix all init_() subroutines to work correctly on first call
+ */
diff --git a/Code/src/libwcs/gsc2read.c b/Code/src/libwcs/gsc2read.c
new file mode 100644
index 0000000000000000000000000000000000000000..708b9169cc349fe9516f82f645c37bbbf04cb2e8
--- /dev/null
+++ b/Code/src/libwcs/gsc2read.c
@@ -0,0 +1,373 @@
+/*** File libwcs/gsc2read.c
+ *** August 17, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2001-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "fitsfile.h"
+#include "wcs.h"
+#include "wcscat.h"
+
+static void parsex();
+
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+#define LINE    1024
+
+/* URL for GSC II search engine at STScI Catalogs and Surveys Branch */
+char gsc23url[64]="http://gsss.stsci.edu/webservices/GSC2/GSC2DataReturn.aspx";
+
+/* GSC2READ -- Read GSC II catalog stars over the web */
+
+int
+gsc2read (refcatname,cra,cdec,dra,ddec,drad,dradi,distsort,sysout,eqout,epout,
+	  mag1,mag2,sortmag,nstarmax,gnum,gobj,gra,gdec,gpra,gpdec,gmag,gtype,nlog)
+
+char	*refcatname;	/* Name of catalog (GSC2 for 2.2; GSC2.3 for 2.3) */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	distsort;	/* 1 to sort stars by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*gnum;		/* Array of Guide Star numbers (returned) */
+char	**gobj;		/* Array of object IDs (mixed characters and numbers) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double	*gpra;		/* Array of right ascension proper motions (returned) */
+double	*gpdec;		/* Array of declination proper motions (returned) */
+double	**gmag;		/* 2-D array of magnitudes (returned) */
+int	*gtype;		/* Array of object classes (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    char srchurl[LINE];
+    char temp[64];
+    struct TabTable *tabtable;
+    double dr;
+    struct StarCat *starcat;
+    int nstar, i;
+    int rah, ram, dd, dm;
+    double ras, ds;
+    char sr[4], sd[4];
+    double ra, dec, mag, ddra;
+    char rastr[32], decstr[32];
+    char *gsc2url;
+
+    /* Set URL for search command */
+    gsc2url = gsc23url;
+
+    if (nstarmax < 1)
+	nlog = -1;
+
+/* make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Set up query for STScI GSC II server */
+    ra = cra;
+    dec = cdec;
+    if (sysout != WCS_J2000)
+	wcscon (sysout, WCS_J2000, eqout, 2000.0, &ra, &dec, epout);
+    ra2str (rastr, 32, ra, 3);
+    dec2str (decstr, 32, dec, 2);
+
+    parsex (rastr, sr, &rah, &ram, &ras);
+    sprintf (srchurl, "?RAH=%d&RAM=%d&RAS=%.3f&", rah, ram, ras);
+    parsex (decstr, sd, &dd, &dm, &ds);
+    sprintf (temp, "DSN=%1s&DD=%d&DM=%d&DS=%.3f&", sd, dd, dm, ds);
+    strcat (srchurl, temp);
+    if (drad != 0.0) {
+	dr = drad * 60.0;
+	}
+    else {
+	ddra = dra * cos (degrad (cdec));
+	dr = sqrt (ddra*ddra + ddec*ddec) * 60.0;
+	}
+    sprintf (temp, "EQ=2000&SIZE=%.3f&SRCH=Radius&FORMAT=TSV&CAT=GSC23&", dr);
+    strcat (srchurl, temp);
+    sprintf (temp, "HSTID=&GSC1ID=");
+    strcat (srchurl, temp);
+
+    if (nlog > 0)
+	fprintf (stderr,"%s%s\n", gsc2url, srchurl);
+
+    /* Run search across the web */
+    if ((tabtable = webopen (gsc2url, srchurl, nlog)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: %s failed\n", srchurl);
+	return (0);
+	}
+
+    /* Return if no data */
+    if (tabtable->tabdata == NULL || strlen (tabtable->tabdata) == 0 ||
+	!strncasecmp (tabtable->tabdata, "[EOD]", 5)) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBRNUM: No data returned\n");
+	return (0);
+	}
+
+    /* Dump returned file and stop */
+    if (nlog < 0) {
+	(void) fwrite  (tabtable->tabbuff, tabtable->lbuff, 1, stdout);
+	exit (0);
+	}
+
+    /* Open returned Starbase table as a catalog */
+    if ((starcat = tabcatopen (gsc2url, tabtable,0)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: Could not open Starbase table as catalog\n");
+	return (0);
+	}
+
+    /* Set reference frame, epoch, and equinox of catalog */
+    /* starcat->rpmunit = PM_MASYR;
+    starcat->dpmunit = PM_MASYR; */
+    starcat->rpmunit = 0;
+    starcat->dpmunit = 0;
+    starcat->coorsys = WCS_J2000;
+    starcat->epoch = 2000.0;
+    starcat->equinox = 2000.0;
+
+    /* Extract desired sources from catalog  and return them */
+    nstar = tabread (gsc2url,distsort,cra,cdec,dra,ddec,drad,dradi,
+	     sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,&starcat,
+	     gnum,gra,gdec,gpra,gpdec,gmag,gtype,gobj,nlog);
+
+    tabcatclose (starcat);
+
+    /* Zero out any proper motions for GSC 3.3 and earlier */
+    if (!strchr (refcatname, '4')) {
+	for (i = 0; i < nstar; i++) {
+	    if (i < nstarmax) {
+		gpra[i] = 0.0;
+		gpdec[i] = 0.0;
+		}
+	    }
+	}
+
+    starcat = NULL;
+
+    return (nstar);
+}
+
+static void
+parsex (str, ss, d, m, s)
+
+char *str;
+char *ss;
+int *d;
+int *m;
+double *s;
+{
+    char *c0, *cstr;
+    c0 = str;
+    *ss = (char) 0;
+    *d = 0;
+    *m = 0;
+    *s = 0.0;
+
+    /* Skip over blanks at start of number */
+    while (*c0 == ' ')
+	c0++;
+    if (*c0 == '+' || *c0 == '-') {
+	ss[0] = *c0;
+	ss[1] = (char) 0;
+	c0++;
+	}
+    else {
+	ss[0] = '+';
+	ss[1] = (char) 0;
+	}
+    cstr = strchr (c0,':');
+    if (cstr > c0) {
+        *cstr = (char) 0;
+        *d = (int) atof (c0);
+        *cstr = ':';
+        c0 = cstr + 1;
+        cstr = strchr (c0,':');
+        if (cstr > c0) {
+            *cstr = '\0';
+            *m = (int) atof (c0);
+            *cstr = ':';
+            c0 = cstr + 1;
+            *s = atof (c0);
+            }
+        else
+            *m = (int) atof (c0);
+        }
+    else
+        *d = (int) atof (c0);
+}
+
+
+char *
+gsc2c2t (csvbuff)
+
+    char *csvbuff;	/* Input comma-separated table */
+
+{
+    char *tabbuff;	/* Output tab-separated table */
+    char *databuff;
+    char *lastbuff;
+    char *oldbuff;
+    char *colhead, *colsep;
+    int lhead, lbuff, i, j;
+    char ctab = (char) 9;
+    char ccom = ',';
+    char clf = '\n';
+    char ccr = '\r';
+    char csp = ' ';
+
+    /* First line of buffer is header */
+    databuff = strchr (csvbuff, clf) + 1;
+    lhead = (int) (databuff - csvbuff);
+
+    /* Allocate buffer for tab-separated table with header */
+    lbuff = strlen (databuff) + (2 * lhead);
+    tabbuff = (char *) calloc (lbuff, 1);
+
+    /* Copy header into new buffer with tabs instead of commas */
+    oldbuff = csvbuff;
+    i = 0;
+    while (oldbuff < databuff) {
+	if (*oldbuff == ccom)
+	    tabbuff[i++] = ctab;
+	else if (*oldbuff != csp && *oldbuff != ccr && *oldbuff != clf)
+	    tabbuff[i++] = *oldbuff;
+	oldbuff++;
+	}
+    tabbuff[i++] = clf;
+
+    /* Make separating line from first line of input file */
+    oldbuff = csvbuff;
+    while (oldbuff < databuff) {
+	if (*oldbuff == ccom)
+	    tabbuff[i++] = ctab;
+	else if (*oldbuff != csp && *oldbuff != ccr && *oldbuff != clf)
+	    tabbuff[i++] = '-';
+	oldbuff++;
+	}
+    tabbuff[i++] = clf;
+
+    /* Drop extraneous data after last linefeed */
+    lbuff = strlen (databuff);
+    if (lbuff > 0) {
+	lastbuff = strrchr (databuff, '\n');
+	if (lastbuff - databuff < lbuff)
+	    *(lastbuff+1) = (char) 0;
+
+	/* Convert commas in data table to tabs and drop spaces */
+	for (j = 0; j < lbuff; j++) {
+	    if (databuff[j] == ccom)
+		tabbuff[i++] = ctab;
+	    else if (databuff[j] != csp && databuff[j] != ccr)
+		tabbuff[i++] = databuff[j];
+	    }
+	}
+
+    return (tabbuff);
+}
+
+
+char *
+gsc2t2t (tsvbuff)
+
+    char *tsvbuff;	/* Input tab-separated table */
+
+{
+    char *tabbuff;	/* Output tab-separated table */
+    int lbuff, i, j;
+    char ctab = (char) 9;
+    char clf = '\n';
+    char ccr = '\r';
+    char csp = ' ';
+
+    /* Allocate buffer for tab-separated table with header */
+    lbuff = strlen (tsvbuff);
+    tabbuff = (char *) calloc (lbuff, 1);
+
+    /* Copy input into new buffer dropping extra carriage returns */
+    i = 0;
+    for (j = 0; j < lbuff; j++) {
+	if (tsvbuff[j] != csp && tsvbuff[j] != ccr)
+	    tabbuff[i++] = tsvbuff[j];
+	}
+    tabbuff[i++] = (char) 0;
+
+    return (tabbuff);
+}
+
+/* Jun 22 2001	New program
+ * Jun 28 2001	Set proper motion to milliarcseconds/year
+ * Jun 29 2001	Always set maximum magnitude to 99.9 to get Tycho-2 stars, too
+ * Sep 13 2001	Pass array of magnitudes, not vector
+ * Sep 14 2001	Add option to print entire returned file if nlog < 0
+ * Sep 20 2001	Make argument starcat, not *starcat in tabcatclose()
+ *
+ * Apr  8 2002	Fix bugs in null subroutine gsc2rnum()
+ * Oct  3 2002	If nstarmax is less than 1, print everything returned
+ *
+ * Feb  6 2003	Reset nmag to 4 because there is an epoch column
+ * Mar 11 2003	Fix URL for search
+ * Apr  3 2003	Drop unused variables after lint; drop gsc2rnum()
+ * Apr 24 2003	Set nmag to 5 to include epoch, which is not printed
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Nov 22 2003	Return object class (c column) as gtype
+ * Dec  3 2003	Add option to access GSC 2.3 over the Web
+ * Dec  4 2003	Add proper motions for GSC 2.3
+ * Dec 11 2003	Search to corners of rectangle, not to longest edge
+ * Dec 12 2003	Fix call to tabcatopen()
+ *
+ * Oct 18 2004	Divide RA by cos(Dec) when computing radius for rect. input
+ *
+ * Jun 20 2006	Cast fwrite to void
+ * Sep  8 2006	Fix comment which mentioned wrong catalog
+
+ * Mar 12 2007	Read from copy in STScI MAST GALEX archive
+ * Mar 12 2007	Add parsex \() to separate sexigesimal number into components
+ * Mar 13 2007	Add gsc2c2v() to convert comma-separated input to tab-separated
+ * Apr 11 2007	Return null data buffer from gsc2c2t() if no data
+ *
+ * Oct 24 2008	Reset to read from new CASB server and drop GALEX server
+ * Oct 24 2008	Add gsc2t2t to drop extra characters from returned table
+ *
+ * Aug 17 2009	Set proper motion to 0.0 for all versions
+ */
diff --git a/Code/src/libwcs/gscread.c b/Code/src/libwcs/gscread.c
new file mode 100644
index 0000000000000000000000000000000000000000..aad2605260d8b814dc226aa4450328862cb9797c
--- /dev/null
+++ b/Code/src/libwcs/gscread.c
@@ -0,0 +1,1562 @@
+/*** File libwcs/gscread.c
+ *** September 22, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "fitsfile.h"
+#include "wcs.h"
+#include "wcscat.h"
+
+/* Pathname of northern hemisphere GSC CDROM  or search engine URL */
+static char cdn[64]="/data/astrocat/gsc1";
+
+/* Uncomment following line to use ESO GSC server for GSC
+static char cdn[64]="http://archive.eso.org/skycat/servers/gsc-server";
+ */
+
+/* Pathname of southern hemisphere GSC CDROM */
+static char cds[64]="/data/astrocat/gsc2";
+
+/* Pathname of northern hemisphere GSC-ACT CDROM  or search engine URL */
+static char cdna[64]="/data/astrocat/gscact1";
+
+/* Pathname of southern hemisphere GSC-ACT CDROM */
+static char cdsa[64]="/data/astrocat/gscact2";
+
+static void gscpath();
+static int gscreg();
+
+static char *table = NULL;	/* FITS table buffer */
+static int ltab = 0;		/* Length of FITS table buffer */
+static double *gdist = NULL;	/* Array of distances to stars */
+static int ndist = 0;
+
+void
+gscfree()
+{ free ((void *)table); ltab = 0;
+  free ((void *)gdist); ndist = 0;
+  return; }
+
+static int classd = -1; /* Desired object class
+			   (-2=all objects, -1=all, 0=stars, 3=nonstars) */
+void
+setgsclass (class)
+int class;
+{ classd = class; return; }
+
+/* GSCREAD -- Read HST Guide Star Catalog stars from CDROM */
+
+int
+gscread (refcat,cra,cdec,dra,ddec,drad,dradi,distsort,sysout,eqout,epout,
+	 mag1,mag2,nstarmax,gnum,gra,gdec,gmag,gtype,nlog)
+
+int	refcat;		/* Catalog code (GSC or GSCACT) */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner separation in degrees for annulus (ignore if 0) */
+int	distsort;	/* 1 to sort stars by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*gnum;		/* Array of Guide Star numbers (returned) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double	**gmag;		/* Array of magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    double dist = 0.0;  /* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    int	faintstar=0;	/* Faintest star */
+    int	farstar=0;	/* Most distant star */
+    int magsort=0;
+    int nreg;		/* Number of input FITS tables files */
+    double xnum;		/* Guide Star number */
+    int rlist[100];	/* List of input FITS tables files */
+    char inpath[64];	/* Pathname for input FITS table file */
+    char entry[100];	/* Buffer for FITS table row */
+    int class;		/* Object class (0>star, 3>other) */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    struct Keyword kw[8];	/* Keyword structure */
+    struct Keyword *kwn;
+
+    int verbose;
+    int pass;
+    int rnum, num0, num, itot,ireg;
+    int ik,nk,itable,ntable,jstar;
+    int nbline,npos,nbhead;
+    int nbr,nrmax,nstar,i;
+    int ift;
+    int wrap;
+    double ra,rasum,dec,decsum,perr,perr2,perrsum,msum;
+    double mag,merr,merr2,merrsum;
+    int band0 = 0;
+    int band = 0;
+    int class0 = 0;
+    double merr0 = 0.0;
+    double mag0 = 0.0;
+    double perr0 = 0.0;
+    double ra0 = 0.0;
+    double dec0 = 0.0;
+    double rra1, rra2, rdec1, rdec2;
+    double rdist, ddist;
+    char *str;
+    char *url;
+    char *title;
+    char cstr[32], numstr[32], rastr[32], decstr[32], catid[16];
+
+    itot = 0;
+    if (nlog == 1)
+	verbose = 1;
+    else
+	verbose = 0;
+    verbose = nlog;
+
+    /* If root pathname is a URL, search and return */
+    url = cdn;
+    if (refcat == GSC) {
+	url = cdn;
+	if ((str = getenv("GSC_NORTH")) == NULL)
+	    str = getenv ("GSC_PATH");
+	if (str != NULL) url = str;
+	}
+    if (refcat == GSCACT) {
+	url = cdna;
+	if ((str = getenv("GSCACT_NORTH")) == NULL)
+	    str = getenv ("GSCACT_PATH");
+	if (str != NULL) url = str;
+	}
+    if (!strncmp (url, "http:",5))
+	return (webread (url,"gsc",distsort,cra,cdec,dra,ddec,drad,dradi,
+			 sysout,eqout,epout,mag1,mag2,magsort,nstarmax,
+			 gnum,gra,gdec,NULL,NULL,gmag,gtype,nlog));
+
+    /* Allocate FITS table buffer which is saved between calls */
+    if (ltab < 1) {
+	ltab = 10000;
+	table = (char *)calloc (ltab, sizeof (char));
+	if (table == NULL) {
+	    fprintf (stderr, "GSCREAD: cannot allocate FITS table buffer\n");
+	    return (0);
+	    }
+	}
+
+    for (i = 0; i < 100; i++)
+	entry[i] = 0;
+
+    /* Set path to Guide Star Catalog */
+    if (refcat == GSCACT) {
+	if ((str = getenv("GSCACT_NORTH")) != NULL )
+	    strcpy (cdna,str);
+	if ((str = getenv("GSCACT_SOUTH")) != NULL )
+	    strcpy (cdsa,str);
+	}
+    else {
+	if ((str = getenv("GSC_NORTH")) != NULL )
+	    strcpy (cdn,str);
+	if ((str = getenv("GSC_SOUTH")) != NULL )
+	    strcpy (cds,str);
+	}
+
+    wcscstr (cstr, sysout, eqout, epout);
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* Make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Find Guide Star Catalog regions in which to search */
+    nrmax = 100;
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,0.0,
+	    &rra1, &rra2, &rdec1, &rdec2,  &wrap, verbose);
+    nreg = gscreg (refcat,rra1,rra2,rdec1,rdec2,table,nrmax,rlist,verbose);
+    if (nreg <= 0) {
+	fprintf (stderr,"GSCREAD:  no Guide Star regions found\n");
+	return (0);
+	}
+
+    /* Allocate array for distances from search center */
+    if (nstarmax > ndist) {
+	if (ndist > 0)
+	    free ((void *)gdist);
+	gdist = (double *) malloc (nstarmax * sizeof (double));
+	if (gdist == NULL) {
+	    fprintf (stderr,"GSCREAD:  cannot allocate separation array\n");
+	    return (0);
+	    }
+	ndist = nstarmax;
+	}
+
+    /* Set keyword list */
+    nk = 8;
+    strcpy (kw[0].kname,"GSC_ID");
+    strcpy (kw[1].kname,"RA_DEG");
+    strcpy (kw[2].kname,"DEC_DEG");
+    strcpy (kw[3].kname,"POS_ERR");
+    strcpy (kw[4].kname,"MAG");
+    strcpy (kw[5].kname,"MAG_ERR");
+    strcpy (kw[6].kname,"MAG_BAND");
+    strcpy (kw[7].kname,"CLASS");
+    for (ik = 0; ik < nk; ik++) {
+	kw[ik].lname = (int) strlen (kw[ik].kname);
+	kw[ik].kn = 0;
+	kw[ik].kf = 0;
+	kw[ik].kl = 0;
+	}
+    nstar = 0;
+
+    /* Write header if printing star entries as found */
+    if (nstarmax < 1) {
+	char *revmessage;
+	revmessage = getrevmsg();
+	title = CatName (refcat, NULL);
+	printf ("catalog	%s\n", title);
+	free (title);
+	ra2str (rastr, 31, cra, 3);
+	printf ("ra	%s\n", rastr);
+	dec2str (decstr, 31, cdec, 2);
+	printf ("dec	%s\n", decstr);
+	if (drad != 0.0) {
+	    printf ("radmin	%.1f\n", drad*60.0);
+	    if (dradi > 0)
+		printf ("radimin	%.1f\n", dradi*60.0);
+	    }
+	else {
+	    printf ("dramin	%.1f\n", dra*60.0* cosdeg (cdec));
+	    printf ("ddecmin	%.1f\n", ddec*60.0);
+	    }
+	printf ("radecsys	%s\n", cstr);
+	printf ("equinox	%.3f\n", eqout);
+	printf ("epoch	%.3f\n", epout);
+	printf ("program	scat %s\n", revmessage);
+	CatID (catid, refcat);
+	printf ("%s	ra          	dec         	", catid);
+	printf ("magv 	class	band	n	arcmin\n");
+	printf ("---------	------------	------------	");
+	printf ("-----	-----	----	-	------\n");
+	}
+
+    /* Loop through region list */
+    for (ireg = 0; ireg < nreg; ireg++) {
+	gscpath (refcat, rlist[ireg], inpath);
+
+    /* Read size and keyword info from FITS table header */
+	kwn = kw;
+	ift = fitsrtopen (inpath,&nk,&kwn,&ntable,&nbline,&nbhead);
+
+	rnum = rlist[ireg];
+	num0 = 0;
+	rasum = 0.0;
+	decsum = 0.0;
+	msum = 0.0;
+	perrsum = 0.0;
+	merrsum = 0.0;
+	npos = 0;
+	num = 0;
+	fitsrtlset();
+	jstar = 0;
+	class = 0;
+
+	/* Loop through FITS table for this region */
+	for (itable = 0; itable <= ntable; itable++) {
+
+	    if (itable < ntable) {
+		nbr = fitsrtline (ift,nbhead,ltab,table,itable,nbline,entry);
+		if (nbr < nbline) {
+		    fprintf (stderr,"GSCREAD: %d / %d bytes read, line %d / %d, region %d\n",
+			      nbr,nbline,itable,ntable,rnum);
+		    break;
+		    }
+
+		/* Extract selected fields */
+
+		/* Star number within region */
+		num0 = ftgeti4 (entry, &kw[0]);
+
+		/* Right ascension in degrees */
+		ra0 = ftgetr8 (entry, &kw[1]);
+
+		/* Declination in degrees */
+		dec0 = ftgetr8 (entry, &kw[2]);
+
+		/* Position error */
+		perr0 = ftgetr8 (entry, &kw[3]);
+
+		/* Magnitude */
+		mag0 = ftgetr8 (entry, &kw[4]);
+
+		/* Magnitude error */
+		merr0 = ftgetr8 (entry, &kw[5]);
+
+		/* Bandpass code */
+		band0 = ftgeti4 (entry, &kw[6]);
+
+		/* Object class code */
+		class0 = ftgeti4 (entry, &kw[7]);
+		}
+	    else
+		num0 = 0;
+
+	/* Compute mean position and magnitude for object */
+	    if (itable > 0 && npos > 0 &&
+		((classd < -1 && band != band0) ||
+		(classd < -1 && class != class0) || 
+		num != num0)) {
+
+		pass = 1;
+		if (perrsum == 0.0 || merrsum == 0)
+		    pass = 0;
+		else {
+		    ra = rasum / perrsum;
+		    dec = decsum / perrsum;
+		    mag = msum / merrsum;
+		    }
+
+		if (pass > 0 && classd > -1 && class != classd)
+		    pass = 0;
+
+		/* Check magnitude and position limits */
+		if (pass > 0 && mag1 != mag2 && (mag < mag1 || mag > mag2))
+		    pass = 0;
+
+		if (pass) {
+		    wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+		
+		    /* Compute distance from search center */
+		    if (drad > 0 || distsort)
+			dist = wcsdist (cra,cdec,ra,dec);
+		    else
+			dist = 0.0;
+
+		    /* Check position limits */
+		    if (drad > 0) {
+			if (dist > drad)
+			    pass = 0;
+			if (dradi > 0.0 && dist < dradi)
+			    pass = 0;
+			}
+		    else {
+			ddist = wcsdist (cra,cdec,cra,dec);
+			if (ddist > ddec)
+			    pass = 0;
+			rdist = wcsdist (cra,dec,ra,dec);
+			if (rdist > dra)
+			    pass = 0;
+			}
+		    }
+
+		if (pass) {
+		    class = class + (band * 100) + (npos * 10000);
+		    xnum = (double)rnum + (0.0001 * (double) num);
+
+		    /* Write star position and magnitudes to stdout */
+		    if (nstarmax < 1) {
+			CatNum (refcat, -9, 0, xnum, numstr);
+			ra2str (rastr, 31, ra, 3);
+			dec2str (decstr, 31, dec, 2);
+			dist = wcsdist (cra,cdec,ra,dec) * 60.0;
+			printf ("%s	%s	%s", numstr,rastr,decstr);
+			printf ("	%.2f	%d	%d	%d	%.2f\n",
+				mag, class-(band*100)-(npos*10000), band, npos, dist);
+			}
+
+		    /* Save star position in table */
+		    else if (nstar < nstarmax) {
+			gnum[nstar] = xnum;
+			gra[nstar] = ra;
+			gdec[nstar] = dec;
+			gmag[0][nstar] = mag;
+			gtype[nstar] = class;
+			gdist[nstar] = dist;
+			if (dist > maxdist) {
+			    maxdist = dist;
+			    farstar = nstar;
+			    }
+			if (mag > faintmag) {
+			    faintmag = mag;
+			    faintstar = nstar;
+			    }
+			}
+
+		    /* If too many stars and distance sorting,
+			replace furthest star */
+		    else if (distsort) {
+			if (dist < maxdist) {
+			    gnum[farstar] = xnum;
+			    gra[farstar] = ra;
+			    gdec[farstar] = dec;
+			    gmag[0][farstar] = mag;
+			    gtype[farstar] = class;
+			    gdist[farstar] = dist;
+			    maxdist = 0.0;
+
+			    /* Find new farthest star */
+			    for (i = 0; i < nstarmax; i++) {
+				if (gdist[i] > maxdist) {
+				    maxdist = gdist[i];
+				    farstar = i;
+				    }
+				}
+			    }
+			}
+
+		    /* If too many stars, replace faintest star */
+		    else if (mag < faintmag) {
+			gnum[faintstar] = xnum;
+			gra[faintstar] = ra;
+			gdec[faintstar] = dec;
+			gmag[0][faintstar] = mag;
+			gtype[faintstar] = class;
+			gdist[faintstar] = dist;
+			faintmag = 0.0;
+
+			/* Find new faintest star */
+			for (i = 0; i < nstarmax; i++) {
+			    if (gmag[0][i] > faintmag) {
+				faintmag = gmag[0][i];
+				faintstar = i;
+				}
+			    }
+			}
+		    nstar++;
+		    jstar++;
+		    if (nlog == 1)
+			fprintf (stderr,"GSCREAD: %04d.%04d: %9.5f %9.5f %s %5.2f %d %d\n",
+				rnum,num,ra,dec,cstr,mag,class,npos);
+		    }
+
+	/* Reset star position for averaging */
+		rasum = 0.0;
+		decsum = 0.0;
+		msum = 0.0;
+		perrsum = 0.0;
+		merrsum = 0.0;
+		npos = 0;
+		}
+
+	/* Add information from current line to current object */
+
+	    /* Check object class */
+     	    if ((classd > -1 && class0 == classd) ||
+		classd < -2 || (classd < 0 && class0 != 5)) {
+		perr = perr0;
+		perr2 = perr * perr;
+		if (perr2 <= 0.0) perr2 = 0.01;
+		rasum = rasum + (ra0 / perr2);
+		decsum = decsum + (dec0 / perr2);
+		perrsum = perrsum + (1.0 / perr2);
+		if (merr0 <= 0.0) merr0 = 0.01;
+		merr = merr0;
+		merr2 = merr * merr;
+		msum = msum + (mag0 / merr2);
+		merrsum = merrsum + (1.0 / merr2);
+		num = num0;
+		class = class0;
+		band = band0;
+		npos++;
+		}
+
+	    /* Log operation */
+	    if (nlog > 0 && itable%nlog == 0)
+		fprintf (stderr,"GSCREAD: %4d / %4d: %5d / %5d  / %5d sources, region %4d.%04d\r",
+			 ireg,nreg,jstar,itable,ntable,rlist[ireg],num0);
+
+	/* End of region */
+	    }
+
+	/* Close region input file */
+	(void) close (ift);
+	itot = itot + itable;
+	if (nlog > 0)
+	    fprintf (stderr,"GSCREAD: %4d / %4d: %5d / %5d  / %5d sources from region %4d    \n",
+		     ireg+1,nreg,jstar,itable,ntable,rlist[ireg]);
+	}
+
+    /* Close output file and summarize transfer */
+    if (nlog > 0) {
+	if (nreg > 1)
+	    fprintf (stderr,"GSCREAD: %d regions: %d / %d found\n",nreg,nstar,itot);
+	else
+	    fprintf (stderr,"GSCREAD: 1 region: %d / %d found\n",nstar,itable);
+	if (nstar > nstarmax )
+	    fprintf (stderr,"GSCREAD: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+    return (nstar);
+}
+
+/* GSCRNUM -- Read HST Guide Star Catalog stars from CDROM */
+
+int
+gscrnum (refcat, nstars, sysout, eqout, epout, gnum,gra,gdec,gmag,gtype,nlog)
+
+int	refcat;		/* Catalog code (GSC or GSCACT) */
+int	nstars;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*gnum;		/* Array of Guide Star numbers (returned) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double	**gmag;		/* Array of magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    char *table;		/* FITS table */
+    char inpath[64];		/* Pathname for input FITS table file */
+    char entry[100];		/* Buffer for FITS table row */
+    int class;			/* Object class (0>star, 3>other) */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    struct Keyword kw[8];	/* Keyword structure */
+    struct Keyword *kwn;
+
+    int rnum, num0, num, itot;
+    int ik,nk,itable,ntable,jstar;
+    int nbline,npos,nbhead;
+    int nbr,nstar,i, snum;
+    int ift;
+    double dnum;
+    double ra,rasum,dec,decsum,perr,perr2,perrsum,msum;
+    double mag,merr,merr2,merrsum;
+    int band0 = 0;
+    int band = 0;
+    int class0 = 0;
+    double merr0 = 0.0;
+    double mag0 = 0.0;
+    double perr0 = 0.0;
+    double ra0 = 0.0;
+    double dec0 = 0.0;
+    char *str;
+    char *url;
+
+    itot = 0;
+
+    /* If root pathname is a URL, search and return */
+    url = cdn;
+    if (refcat == GSC) {
+	url = cdn;
+	if ((str = getenv("GSC_NORTH")) == NULL)
+	    str = getenv ("GSC_PATH");
+	if (str != NULL) url = str;
+	if (!strncmp (url, "http:",5))
+	    return (webrnum (str,"gsc",nstars,sysout,eqout,epout,1,
+			     gnum,gra,gdec,NULL,NULL,gmag,gtype,nlog));
+	}
+    if (refcat == GSCACT) {
+	url = cdna;
+	if ((str = getenv("GSCACT_NORTH")) == NULL)
+	    str = getenv ("GSCACT_PATH");
+	if (str != NULL) url = str;
+	}
+    if (!strncmp (url, "http:",5))
+	return (webrnum (str,"gsc",nstars,sysout,eqout,epout,1,
+			 gnum,gra,gdec,NULL,NULL,gmag,gtype,nlog));
+
+    if (ltab < 1) {
+	ltab = 10000;
+	table = (char *)calloc (ltab, sizeof (char));
+	}
+
+    for (i = 0; i < 100; i++)
+	entry[i] = 0;
+
+    /* Set path to Guide Star Catalog */
+    if ((str = getenv("GSC_NORTH")) != NULL )
+	strcpy (cdn,str);
+    if ((str = getenv("GSC_SOUTH")) != NULL )
+	strcpy (cds,str);
+
+    /* Set keyword list */
+    nk = 8;
+    strcpy (kw[0].kname,"GSC_ID");
+    strcpy (kw[1].kname,"RA_DEG");
+    strcpy (kw[2].kname,"DEC_DEG");
+    strcpy (kw[3].kname,"POS_ERR");
+    strcpy (kw[4].kname,"MAG");
+    strcpy (kw[5].kname,"MAG_ERR");
+    strcpy (kw[6].kname,"MAG_BAND");
+    strcpy (kw[7].kname,"CLASS");
+    for (ik = 0; ik < nk; ik++) {
+	kw[ik].lname = (int) strlen (kw[ik].kname);
+	kw[ik].kn = 0;
+	kw[ik].kf = 0;
+	kw[ik].kl = 0;
+	}
+    nstar = 0;
+
+    /* Loop through star list */
+    for (jstar = 0; jstar < nstars; jstar++) {
+	rnum = (int) gnum[jstar];
+	gscpath (refcat, rnum, inpath);
+	dnum = (gnum[jstar] - (double)rnum) * 10000.0;
+	snum = (int) (dnum + 0.5);
+
+    /* Read size and keyword info from FITS table header */
+	kwn = kw;
+	ift = fitsrtopen (inpath,&nk,&kwn,&ntable,&nbline,&nbhead);
+	if (ift < 0) {
+	    fprintf (stderr,"GSCRNUM: File %s not found\n",inpath);
+	    return (0);
+	    }
+	num0 = 0;
+	rasum = 0.0;
+	decsum = 0.0;
+	msum = 0.0;
+	perrsum = 0.0;
+	merrsum = 0.0;
+	npos = 0;
+	num = 0;
+	fitsrtlset();
+	class = 0;
+
+	/* Loop through FITS table for this region */
+	for (itable = 0; itable <= ntable; itable++) {
+
+	    if (itable < ntable) {
+		nbr = fitsrtline (ift,nbhead,ltab,table,itable,nbline,entry);
+		if (nbr < nbline) {
+		    fprintf (stderr,"GSCRNUM: %d / %d bytes read, line %d / %d, region %d\n",
+			      nbr,nbline,itable,ntable,rnum);
+		    break;
+		    }
+
+	 /* Extract selected fields */
+
+		/* Star number within region */
+		num0 = ftgeti4 (entry, &kw[0]);
+		if (num0 < snum)
+		    continue;
+		else if (num == 0)
+		    num = num0;
+
+		/* Right ascension in degrees */
+		ra0 = ftgetr8 (entry, &kw[1]);
+
+		/* Declination in degrees */
+		dec0 = ftgetr8 (entry, &kw[2]);
+
+		/* Position error */
+		perr0 = ftgetr8 (entry, &kw[3]);
+
+		/* Magnitude */
+		mag0 = ftgetr8 (entry, &kw[4]);
+
+		/* Magnitude error */
+		merr0 = ftgetr8 (entry, &kw[5]);
+
+		/* Bandpass code */
+		band0 = ftgeti4 (entry, &kw[6]);
+
+		/* Object class code */
+		class0 = ftgeti4 (entry, &kw[7]);
+		}
+	    else
+		num0 = 0;
+
+	    /* Compute mean position and magnitude for object */
+	    if (num != num0 && itable > 0 && npos > 0) {
+		ra = rasum / perrsum;
+		dec = decsum / perrsum;
+		wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+		mag = msum / merrsum;
+		class = class + (band * 100) + (npos * 10000);
+
+		/* Save star position in table */
+		gra[nstar] = ra;
+		gdec[nstar] = dec;
+		gmag[0][nstar] = mag;
+		gtype[nstar] = class;
+
+		nstar++;
+		if (nlog == 1)
+		    fprintf (stderr,"GSCRNUM: %04d.%04d: %9.5f %9.5f %5.2f %d %d\n",
+			     rnum,num,ra,dec,mag,class,npos);
+
+		/* Reset star position for averaging */
+		rasum = 0.0;
+		decsum = 0.0;
+		msum = 0.0;
+		perrsum = 0.0;
+		merrsum = 0.0;
+		npos = 0;
+		break;
+		}
+
+	    /* Add information from current line to current object */
+	    perr = perr0;
+	    perr2 = perr * perr;
+	    if (perr2 <= 0.0) perr2 = 0.01;
+	    rasum = rasum + (ra0 / perr2);
+	    decsum = decsum + (dec0 / perr2);
+	    perrsum = perrsum + (1.0 / perr2);
+	    if (merr0 <= 0.0) merr0 = 0.01;
+	    merr = merr0;
+	    merr2 = merr * merr;
+	    msum = msum + (mag0 / merr2);
+	    merrsum = merrsum + (1.0 / merr2);
+	    num = num0;
+	    class = class0;
+	    band = band0;
+	    npos = npos + 1;
+
+	    /* Log operation */
+	    if (nlog > 0 && itable%nlog == 0)
+		fprintf (stderr,"GSCRNUM: %4d / %4d: %5d / %5d sources, region %4d.%04d\r",
+			 jstar+1,nstars,itable,ntable,rnum,snum);
+
+	/* End of region */
+	    }
+
+	/* Close region input file */
+	(void) close (ift);
+	itot = itot + itable;
+	if (nlog > 0)
+	    fprintf (stderr,"GSCRNUM: %4d / %4d: %5d / %5d sources, region %4d.%04d\n",
+		     jstar+1,nstars,itable,ntable,rnum,snum);
+	}
+
+    return (nstars);
+}
+
+
+/* GSCBIN -- Fill FITS WCS image with HST Guide Star Catalog objects */
+
+int
+gscbin (refcat, wcs, header, image, mag1, mag2, magscale, nlog)
+
+int	refcat;		/* Catalog code (GSC or GSCACT) */
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int	sysout = wcs->syswcs;	/* Image coordinate system */
+    double eqout = wcs->equinox; /* Image coordinate equinox */
+    double epout = wcs->epoch;	/* Image epoch */
+    double ra1,ra2;	/* Limiting right ascensions of image in degrees */
+    double dec1,dec2;	/* Limiting declinations of image in degrees */
+    int nreg;		/* Number of input FITS tables files */
+    double xnum;		/* Guide Star number */
+    int rlist[100];	/* List of input FITS tables files */
+    char inpath[64];	/* Pathname for input FITS table file */
+    char entry[100];	/* Buffer for FITS table row */
+    int class, class0;	/* Object class (0>star, 3>other) */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    struct Keyword kw[8];	/* Keyword structure */
+    struct Keyword *kwn;
+
+    int verbose;
+    int pass;
+    int wrap;
+    int rnum, num0, num, itot,ireg;
+    int ik,nk,itable,ntable,jstar;
+    int nbline,npos,nbhead;
+    int nbr,nrmax,nstar,i;
+    int ift, band0, band;
+    double ra,ra0,rasum,dec,dec0,decsum,perr,perr0,perr2,perrsum,msum;
+    double mag,mag0,merr,merr0,merr2,merrsum;
+    double rra1, rra2, rdec1, rdec2;
+    double rdist, ddist;
+    char *str;
+    char cstr[32];
+    int bitpix, w, h;	/* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+    double xpix, ypix, flux;
+    int ix, iy;
+    int offscl;
+
+    itot = 0;
+    if (nlog == 1)
+	verbose = 1;
+    else
+	verbose = 0;
+    verbose = nlog;
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    if (refcat == GSCACT) {
+	if ((str = getenv("GSCACT_NORTH")) == NULL)
+	    str = getenv ("GSCACT_PATH");
+	}
+
+    /* Allocate FITS table buffer which is saved between calls */
+    if (ltab < 1) {
+	ltab = 10000;
+	table = (char *)calloc (ltab, sizeof (char));
+	if (table == NULL) {
+	    fprintf (stderr, "GSCBIN: cannot allocate FITS table buffer\n");
+	    return (0);
+	    }
+	}
+
+    for (i = 0; i < 100; i++)
+	entry[i] = 0;
+
+    /* Set path to Guide Star Catalog */
+    if (refcat == GSCACT) {
+	if ((str = getenv("GSCACT_NORTH")) != NULL )
+	    strcpy (cdna,str);
+	if ((str = getenv("GSCACT_SOUTH")) != NULL )
+	    strcpy (cdsa,str);
+	}
+    else {
+	if ((str = getenv("GSC_NORTH")) != NULL )
+	    strcpy (cdn,str);
+	if ((str = getenv("GSC_SOUTH")) != NULL )
+	    strcpy (cds,str);
+	}
+
+    wcscstr (cstr, sysout, eqout, epout);
+
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* Make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Find Guide Star Catalog regions in which to search */
+    nrmax = 100;
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epout,0.0,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+    nreg = gscreg (refcat,rra1,rra2,rdec1,rdec2,table,nrmax,rlist,verbose);
+    if (nreg <= 0) {
+	fprintf (stderr,"GSCBIN:  no Guide Star regions found\n");
+	return (0);
+	}
+
+    /* Set keyword list */
+    nk = 8;
+    strcpy (kw[0].kname,"GSC_ID");
+    strcpy (kw[1].kname,"RA_DEG");
+    strcpy (kw[2].kname,"DEC_DEG");
+    strcpy (kw[3].kname,"POS_ERR");
+    strcpy (kw[4].kname,"MAG");
+    strcpy (kw[5].kname,"MAG_ERR");
+    strcpy (kw[6].kname,"MAG_BAND");
+    strcpy (kw[7].kname,"CLASS");
+    for (ik = 0; ik < nk; ik++) {
+	kw[ik].lname = (int) strlen (kw[ik].kname);
+	kw[ik].kn = 0;
+	kw[ik].kf = 0;
+	kw[ik].kl = 0;
+	}
+    nstar = 0;
+
+    /* Loop through region list */
+    for (ireg = 0; ireg < nreg; ireg++) {
+	gscpath (refcat, rlist[ireg], inpath);
+
+    /* Read size and keyword info from FITS table header */
+	kwn = kw;
+	ift = fitsrtopen (inpath,&nk,&kwn,&ntable,&nbline,&nbhead);
+
+	rnum = rlist[ireg];
+	num0 = 0;
+	rasum = 0.0;
+	decsum = 0.0;
+	msum = 0.0;
+	perrsum = 0.0;
+	merrsum = 0.0;
+	npos = 0;
+	num = 0;
+	fitsrtlset();
+	jstar = 0;
+	class = 0;
+
+	/* Loop through FITS table for this region */
+	for (itable = 0; itable <= ntable; itable++) {
+
+	    if (itable < ntable) {
+		nbr = fitsrtline (ift,nbhead,ltab,table,itable,nbline,entry);
+		if (nbr < nbline) {
+		    fprintf (stderr,"GSCBIN: %d / %d bytes read, line %d / %d, region %d\n",
+			      nbr,nbline,itable,ntable,rnum);
+		    break;
+		    }
+
+		/* Extract selected fields */
+
+		/* Star number within region */
+		num0 = ftgeti4 (entry, &kw[0]);
+
+		/* Right ascension in degrees */
+		ra0 = ftgetr8 (entry, &kw[1]);
+
+		/* Declination in degrees */
+		dec0 = ftgetr8 (entry, &kw[2]);
+
+		/* Position error */
+		perr0 = ftgetr8 (entry, &kw[3]);
+
+		/* Magnitude */
+		mag0 = ftgetr8 (entry, &kw[4]);
+
+		/* Magnitude error */
+		merr0 = ftgetr8 (entry, &kw[5]);
+
+		/* Bandpass code */
+		band0 = ftgeti4 (entry, &kw[6]);
+
+		/* Object class code */
+		class0 = ftgeti4 (entry, &kw[7]);
+		}
+	    else
+		num0 = 0;
+
+	/* Compute mean position and magnitude for object */
+	    if (itable > 0 && npos > 0 &&
+		((classd < -1 && band != band0) ||
+		(classd < -1 && class != class0) || 
+		num != num0)) {
+
+		pass = 1;
+		if (perrsum == 0.0 || merrsum == 0)
+		    pass = 0;
+		else {
+		    ra = rasum / perrsum;
+		    dec = decsum / perrsum;
+		    mag = msum / merrsum;
+		    }
+
+		if (pass > 0 && classd > -1 && class != classd)
+		    pass = 0;
+
+		/* Check magnitude and position limits */
+		if (pass > 0 && mag1 != mag2 && (mag < mag1 || mag > mag2))
+		    pass = 0;
+
+		if (pass) {
+		    wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+		
+		    /* Check position limits */
+		    ddist = wcsdist (cra,cdec,cra,dec);
+		    if (ddist > ddec)
+			pass = 0;
+		    rdist = wcsdist (cra,dec,ra,dec);
+		    if (rdist > dra)
+			pass = 0;
+		    }
+
+		/* Save star in FITS image */
+		if (pass) {
+		    class = class + (band * 100) + (npos * 10000);
+		    xnum = (double)rnum + (0.0001 * (double) num);
+		    wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+		    if (!offscl) {
+			if (magscale > 0.0)
+			    flux = magscale * exp (logt * (-mag / 2.5));
+			else
+			    flux = 1.0;
+			ix = (int) (xpix + 0.5);
+			iy = (int) (ypix + 0.5);
+			addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+			nstar++;
+			jstar++;
+			}
+		    else {
+			ix = 0;
+			iy = 0;
+			}
+		    if (nlog == 1) {
+			fprintf (stderr,"GSCBIN: %04d.%04d: %9.5f %9.5f %s %d %d",
+				 rnum, num, ra, dec, cstr, class, npos);
+			if (magscale > 0.0)
+			    fprintf (stderr, " %5.2f", mag);
+			if (!offscl)
+			    flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+			else
+			    flux = 0.0;
+			fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+			}
+		    }
+
+	/* Reset star position for averaging */
+		rasum = 0.0;
+		decsum = 0.0;
+		msum = 0.0;
+		perrsum = 0.0;
+		merrsum = 0.0;
+		npos = 0;
+		}
+
+	/* Add information from current line to current object */
+
+	    /* Check object class */
+     	    if ((classd > -1 && class0 == classd) ||
+		classd < -2 || (classd < 0 && class0 != 5)) {
+		perr = perr0;
+		perr2 = perr * perr;
+		if (perr2 <= 0.0) perr2 = 0.01;
+		rasum = rasum + (ra0 / perr2);
+		decsum = decsum + (dec0 / perr2);
+		perrsum = perrsum + (1.0 / perr2);
+		if (merr0 <= 0.0) merr0 = 0.01;
+		merr = merr0;
+		merr2 = merr * merr;
+		msum = msum + (mag0 / merr2);
+		merrsum = merrsum + (1.0 / merr2);
+		num = num0;
+		class = class0;
+		band = band0;
+		npos++;
+		}
+
+	    /* Log operation */
+	    if (nlog > 0 && itable%nlog == 0)
+		fprintf (stderr,"GSCBIN: %4d / %4d: %5d / %5d  / %5d sources, region %4d.%04d\r",
+			 ireg,nreg,jstar,itable,ntable,rlist[ireg],num0);
+
+	/* End of region */
+	    }
+
+	/* Close region input file */
+	(void) close (ift);
+	itot = itot + itable;
+	if (nlog > 0)
+	    fprintf (stderr,"GSCBIN: %4d / %4d: %5d / %5d  / %5d sources from region %4d    \n",
+		     ireg+1,nreg,jstar,itable,ntable,rlist[ireg]);
+	}
+
+    /* Close output file and summarize transfer */
+    if (nlog > 0) {
+	if (nreg > 1)
+	    fprintf (stderr,"GSCBIN: %d regions: %d / %d found\n",nreg,nstar,itot);
+	else
+	    fprintf (stderr,"GSCBIN: 1 region: %d / %d found\n",nstar,itable);
+	}
+    return (nstar);
+}
+
+
+/* First region in each declination zone */
+int zreg1[24]={1,594,1178,1729,2259,2781,3246,3652,4014,4294, 4492,4615,
+	      4663,5260,5838,6412,6989,7523,8022,8464,8840,9134,9346,9490};
+
+/* Last region in each declination zone */
+int zreg2[24]={593,1177,1728,2258,2780,3245,3651,4013,4293,4491,4614,4662,
+	       5259,5837,6411,6988,7522,8021,8463,8839,9133,9345,9489,9537};
+
+/* Directory for each declination zone */
+char zdir[24][8]={"n0000","n0730","n1500","n2230","n3000","n3730","n4500",
+		"n5230","n6000","n6730","n7500","n8230","s0000","s0730",
+		"s1500","s2230","s3000","s3730","s4500","s5230","s6000",
+		"s6730","s7500","s8230"};
+
+static struct Keyword rkw[15];
+static int nrkw = 13;
+
+/* GSCREG -- search the HST Guide Star Catalog index table for fields
+ * in the specified range of coordinates and magnitudes.  Build a
+ * list containing the pathnames of the files on the cd-rom.
+ */
+
+static int
+gscreg (refcat, ra1, ra2, dec1, dec2, table, nrmax, rgns, verbose)
+
+int	refcat;		/* Catalog code (GSC or GSCACT) */
+double	ra1, ra2;	/* Right ascension limits in degrees */
+double	dec1, dec2; 	/* Declination limits in degrees */
+char	*table;		/* Table data buffer */
+int	nrmax;		/* Maximum number of regions to find */
+int	*rgns;		/* Region numbers (returned)*/
+int	verbose;	/* 1 for diagnostics */
+
+{
+    int nrgn;		/* Number of regions found (returned) */
+    char tabpath[64];	/* Pathname for regions table */
+    int nrows;		/* Number of entries in table */
+    int nchar;		/* Number of characters per line in table */
+    int nwrap;		/* 1 if 0h included in RA span*/
+    int iwrap;
+    struct Keyword *kwn;
+    double gscra(), gscdec();
+    int gsczone();
+
+    char fitsline[120];
+    int irow,iz1,iz2,ir1,ir2,jr1,jr2,i;
+    int nsrch,nsrch1,nbhead,nbr;
+    int ift;
+    double ralow, rahi;
+    double declow, dechi, decmin, decmax;
+    int regnum;
+
+    /* Set up keyword list for table entries to extract */
+    strcpy (rkw[0].kname,"REG_NO");
+    strcpy (rkw[1].kname,"RA_H_LOW");
+    strcpy (rkw[2].kname,"RA_M_LOW");
+    strcpy (rkw[3].kname,"RA_S_LOW");
+    strcpy (rkw[4].kname,"RA_H_HI");
+    strcpy (rkw[5].kname,"RA_M_HI");
+    strcpy (rkw[6].kname,"RA_S_HI");
+    strcpy (rkw[7].kname,"DECSI_LO");
+    strcpy (rkw[8].kname,"DEC_D_LO");
+    strcpy (rkw[9].kname,"DEC_M_LO");
+    strcpy (rkw[10].kname,"DECSI_HI");
+    strcpy (rkw[11].kname,"DEC_D_HI");
+    strcpy (rkw[12].kname,"DEC_M_HI");
+    rkw[13].kname[0] = 0;
+
+    /* Add lengths of keywords to keyword structures */
+    for (i = 0; i < 13; i++)
+	rkw[i].lname = strlen (rkw[i].kname);
+
+    /* Initialize region list to zeroes */
+    for (i = 0; i < nrmax; i++)
+	rgns[i] = 0;
+
+    /* Initialize FITS table line buffer to zeroes */
+    for (i = 0; i < 120; i++)
+	fitsline[i] = (char) 0;
+
+    nrgn = 0;
+
+    /* Set pathnames to Guide Star Catalog CDROM */
+    if (refcat == GSCACT)
+	strcpy (tabpath,cdna);
+    else
+	strcpy (tabpath,cdn);
+
+    /* Set pathname for region table file */
+    strcat (tabpath,"/tables/regions.tbl");
+
+    /* Open the index table */
+    kwn = rkw;
+    ift = fitsrtopen (tabpath,&nrkw,&kwn, &nrows, &nchar, &nbhead);
+
+    /* If the northern hemisphere CDROM cannot be read, try the southern */
+    if (ift < 0) {
+	if (refcat == GSCACT)
+	    strcpy (tabpath,cdsa);
+	else
+	    strcpy (tabpath,cds);
+	strcat (tabpath,"/tables/regions.tbl");
+	ift = fitsrtopen (tabpath,&nrkw,&kwn,&nchar,&nrows,&nbhead);
+	if (ift < 0) {
+	    fprintf (stderr,"GSCREG:  error reading region table %s\n",tabpath);
+	    return (0);
+	    }
+	}
+
+    /* Find region range to search based on declination */
+    iz1 = gsczone (dec1);
+    iz2 = gsczone (dec2);
+    jr1 = 0;
+    jr2 = 0;
+    nwrap = 1;
+
+    /* Search region northern hemisphere or only one region */
+    if (iz2 >= iz1) {
+	ir1 = zreg1[iz1];
+	ir2 = zreg2[iz2];
+	}
+
+    /* Search region in southern hemisphere with multiple regions */
+    else if (dec1 < 0 && dec2 < 0) {
+	ir1 = zreg1[iz2];
+	ir2 = zreg2[iz1];
+	}
+
+    /* Search region spans equator */
+    else if (dec1 < 0 && dec2 >= 0) {
+	ir1 = zreg1[12];
+	ir2 = zreg2[iz1];
+	jr1 = zreg1[0];
+	jr2 = zreg2[iz2];
+	nwrap = 2;
+	}
+    else {
+	ir1 = 1;
+	ir2 = 0;
+	}
+
+    nsrch = ir2 - ir1 + 1;
+    if (verbose)
+	fprintf (stderr,"GSCREG: searching %d regions: %d - %d\n",nsrch,ir1,ir2);
+    if (jr1 > 0) {
+	nsrch1 = jr2 - jr1 + 1;
+	if (verbose)
+	    fprintf (stderr,"GSCREG: searching %d regions: %d - %d\n",nsrch1,jr1,jr2);
+	}
+    if (verbose)
+	fprintf(stderr,"GSCREG: RA: %.5f - %.5f, Dec: %.5f - %.5f\n",ra1,ra2,dec1,dec2);
+
+    if (nsrch < 1) {
+	return (0);
+	}
+
+    nrgn = 0;
+
+    for (iwrap = 0; iwrap < nwrap; iwrap++) {
+
+	for (irow = ir1 - 1; irow < ir2; irow++) {
+
+	/* Read next line of region table */
+	    nbr = fitsrtline (ift,nbhead,ltab,table,irow,nchar,fitsline);
+	    if (nbr < nchar) {
+		fprintf (stderr,"GSREG: %d / %d bytes read for row %d\n",nbr,nchar,irow);
+		break;
+		}
+
+	/* Declination range of the gs region */
+	/* note:  southern dechi and declow are reversed */
+	    dechi = gscdec (fitsline, 10, 11, 12);
+	    declow = gscdec (fitsline, 7, 8, 9);
+	    if (dechi > declow) {
+		decmin = declow;
+		decmax = dechi;
+		}
+	    else {
+		decmax = declow;
+		decmin = dechi;
+		}
+
+	    if (decmax >= dec1 && decmin <= dec2) {
+
+	    /* Right ascension range of the Guide Star Catalog region */
+		ralow = gscra (fitsline, 1, 2, 3);
+		rahi = gscra (fitsline, 4, 5, 6);
+		if (rahi <= 0.0) rahi = 360.0;
+
+	    /* Check RA if 0h RA not between region RA limits */
+		if (ra1 < ra2) {
+		    if (ralow <= ra2 && rahi >= ra1) {
+
+			/* Get region number from FITS table */
+			regnum = ftgeti4 (fitsline, &rkw[0]);
+			if (verbose)
+			    fprintf (stderr,"GSCREG: Region %d added to search\n",regnum);
+
+			/* Add this region to list, if there is space */
+			if (nrgn < nrmax) {
+			    rgns[nrgn] = regnum;
+			    nrgn = nrgn + 1;
+			    }
+			}
+		    }
+
+	    /* Check RA if 0h RA is between region RA limits */
+		else {
+		    if (ralow > rahi) rahi = rahi + 360.0;
+		    if (ralow <= ra2 || rahi >= ra1) {
+
+		    /* Get region number from FITS table */
+			regnum = ftgeti4 (fitsline, &rkw[0]);
+			if (verbose)
+			    fprintf (stderr,"GSCREG: Region %d added to search\n",regnum);
+
+		    /* Add this region to list, if there is space */
+			if (nrgn < nrmax) {
+			    rgns[nrgn] = regnum;
+			    nrgn = nrgn + 1;
+			    }
+			}
+		    }
+		}
+	    }
+
+	/* Handle wrap-around through the equator */
+	ir1 = jr1;
+	ir2 = jr2;
+	jr1 = 0;
+	jr2 = 0;
+	}
+
+    (void) close (ift);
+    return (nrgn);
+}
+
+
+/* GSCRA -- returns right ascension in degrees from the GSC index table
+ *  This is converted from the hours, minutes, and seconds columns.
+ */
+
+double
+gscra (fitsline, hcol, mcol, scol)
+
+char *fitsline;		/* Index table line */
+int hcol;		/* Column index for hours */
+int mcol;		/* Column index for minutes */
+int scol;		/* Column index for seconds */
+
+{
+    double ra;		/* Right ascension in fractional degrees */
+    double hrs;		/* Hours of right ascension */
+    double min;		/* Minutes of right ascension */
+    double sec;		/* Seconds of right ascension */
+
+/*  hours of right ascension */
+    hrs = ftgetr8 (fitsline, &rkw[hcol]);
+
+/* minutes of right ascension */
+    min = ftgetr8 (fitsline, &rkw[mcol]);
+
+/* seconds of right ascension */
+    sec = ftgetr8 (fitsline, &rkw[scol]);
+
+/* right ascension in degrees */
+    ra = hrs + (min / 60.0) + (sec / 3600.0);
+    ra = ra * 15.0;
+
+    return (ra);
+}
+
+
+/*  GSCDEC -- returns the declination in degrees from the GSC index table.
+ *  This is converted from the sign, degrees, minutes, and seconds columns.
+ */
+
+double
+gscdec (fitsline, sgncol, dcol, mcol)
+
+char *fitsline;		/* Index table line */
+int sgncol;		/* Column index for sign */
+int dcol;		/* Column index for degrees */
+int mcol;		/* Column index for minutes */
+
+{
+double	dec;		/* Declination in fractional degrees */
+
+char sgn[4];		/* Sign of declination */
+double deg;		/* Degrees of declination*/
+double min;		/* Minutes of declination */
+
+    /* Get declination sign from table */
+    (void) ftgetc (fitsline, &rkw[sgncol], sgn, 3);
+
+    /* Get degrees of declination from table */
+    deg = ftgetr8 (fitsline, &rkw[dcol]);
+
+    /* Get minutes of declination from table */
+    min = ftgetr8 (fitsline, &rkw[mcol]);
+
+    dec = deg + (min / 60.0);
+
+    /* Negative declination */
+    if (strchr (sgn, '-') != NULL)
+	dec = -dec;
+
+    return (dec);
+}
+
+
+/*  GSCZONE -- find the zone number where a declination can be found */
+
+int
+gsczone (dec)
+
+double dec;	/* declination in degrees */
+
+{
+int zone;		/* gsc zone (returned) */
+double	zonesize;
+int ndeczones = 12;	/* number of declination zones per hemisphere */
+
+    /* Width of declination zones */
+    zonesize = 90.0 / ndeczones;
+
+    zone = ((int) (dec / zonesize));
+    if (dec < 0)
+	zone = ndeczones - zone;
+	
+    return (zone);
+}
+
+
+/* GSCPATH -- Get HST Guide Star Catalog region FITS file pathname */
+
+static void
+gscpath (refcat, regnum, path)
+
+int	refcat;		/* Catalog code (GSC or GSCACT) */
+int	regnum;		/* Guide Star Catalog region number */
+char	*path;		/* Pathname of GSC region FITS file */
+
+{
+    int zone = 0;	/* Name of Guide Star Catalog zone directory */
+    int i;
+
+    /* Get zone directory name given region number */
+    for (i = 0; i < 24; i++) {
+	if (regnum >= zreg1[i] && regnum <= zreg2[i]) {
+	    zone = i;
+	    break;
+	    }
+	}
+
+    /* Set the pathname using the appropriate GSC CDROM directory */
+
+    /* Northern hemisphere disk (volume 1) */
+    if (regnum < zreg1[13]) {
+	if (refcat == GSCACT)
+	    sprintf (path,"%s/%s/%04d.gsc", cdna, zdir[zone], regnum);
+	else
+	    sprintf (path,"%s/gsc/%s/%04d.gsc", cdn, zdir[zone], regnum);
+	}
+
+    /* Southern hemisphere disk (volume 2) */
+    else {
+	if (refcat == GSCACT)
+	    sprintf (path,"%s/%s/%04d.gsc", cdsa, zdir[zone], regnum);
+	else
+	    sprintf (path,"%s/gsc/%s/%04d.gsc", cds, zdir[zone], regnum);
+	}
+
+    return;
+}
+
+/* Feb 16 1996	New program
+ * May 14 1996	Change arguments to FITSRTOPEN
+ * May 17 1996	Fix bug so equal magnitude limits accepts anything
+ * May 17 1996	Use new FITSRTOPEN which internalizes FITS header
+ * May 24 1996	Fix string decoding bug and region search
+ * May 31 1996	Use stream I/O
+ * Jun 10 1996	Remove unused variables after using lint
+ * Jul  1 1996	Fix GSC pathname
+ * Aug  6 1996	Minor changes after lint
+ * Sep 17 1996	Fix bug causing incomplete region access; improve logging
+ * Oct 17 1996	Change FITS table entry calls
+ * Nov 13 1996	Return no more than maximum star number
+ * Nov 13 1996	Write all error messages to stderr with subroutine names
+ * Nov 15 1996	Implement search radius; change input arguments
+ * Dec 12 1996	Make logging run on single line per region
+ * Dec 17 1996  Add code to keep brightest or closest stars if too many found
+ * Dec 18 1996	Add code to get star information by number
+ *
+ * Mar 20 1997	Remove unused variables and fixed logging in GSCRNUM after lint
+ * Oct 10 1997	Use standard I/O instead of stream I/O when reading FITS files
+ * Nov  6 1997	Do not print overrun unless logging
+ *
+ * May 13 1998	Do not set classd in gscread()
+ * May 13 1998	Print all stars if classd is < -1
+ * May 27 1998	Include fitsio.h instead of fitshead.h
+ * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
+ * Aug  6 1998	Change fitsio.h to fitsfile.h
+ * Sep 16 1998	Use limiting radius, if present
+ * Sep 22 1998	Convert to desired output coordinate system
+ * Oct 26 1998	Fix bug in region selection
+ * Oct 29 1998	Correctly assign numbers when too many stars are found
+ *
+ * May 25 1999	Allocate table buffer only once
+ * Jun 16 1999	Use SearchLim()
+ * Aug 16 1999	Add RefLim() to get converted search coordinates right
+ * Aug 16 1999	Fix bug to fix failure to search across 0:00 RA
+ * Aug 25 1999	Return real number of stars from gscread()
+ * Sep 10 1999	Set class selection with subroutine, not argument
+ * Sep 16 1999	Fix bug which didn't always return closest stars
+ * Sep 16 1999	Add distsort argument so brightest stars in circle works, too
+ * Sep 22 1999	Rewrite table allocation so it works; make ltab static
+ * Oct 21 1999	Include wcscat.h, unistd.h
+ *
+ * Mar 27 2000	Drop unused variables after lint
+ * Mar 28 2000	Make default to read all classes
+ * Jun 26 2000	Add coordinate system to SearchLim() arguments
+ * Nov 29 2000	Add option to read cataog across the web
+ * Dec 11 2000	Allow search engine URL in cdn[]
+ * Dec 12 2000	Fix wrong web subroutine in gscrnum()
+ *
+ * Apr 24 2001	Add bandpass code and number of entries to object class
+ * Apr 24 2001	Return individual entries if class is < -1
+ * May 23 2001	Add support for GSC-ACT
+ * May 30 2001	Use GSC_NORTH and GSCACT_NORTH instead of *_PATH for consistency
+ * Jun 27 2001  Print stars as found in gscread() if nstarmax < 1
+ * Jun 27 2001	Allocate distance array only if larger one is needed
+ * Jun 27 2001	Add gscfree() to free table buffer and distance array
+ * Sep 11 2001	Use single magnitude argument to gscread() and webread()
+ * Sep 21 2001	Clean up web interface
+ * Nov 20 2001	Change cos(degrad)) to cosdeg()
+ *
+ * Mar 28 2002	Change pathnames to /data/astrocat
+ * Jul 31 2002	Always check for GSCACT_PATH, not GSCA_PATH
+ * Oct  2 2002	Fix pass-through tab table header
+ *
+ * Mar 10 2003	Improve position limits
+ * Apr  3 2003	Drop wrap in gscread(); add test for npos in gscread()
+ * Apr 14 2003	Explicitly get revision date if nstarmax < 1
+ * Aug 22 2003	Add dradi for inner edge of annulus search
+ * Sep 25 2003	Add gscbin() to fill an image with sources
+ * Oct  6 2003	Update gscread() and gscbin() for improved RefLim()
+ * Nov 18 2003	Initialize image size and bits/pixel from header in gscbin()
+ * Dec  1 2003	Add missing tab to n=-1 header
+ * Dec 12 2003	Fix bug in wcs2pix() call in gscbin()
+ *
+ * Aug 27 2004	Include math.h
+ *
+ * Jun 20 2006	Initialize uninitialized variables
+ * Nov 16 2006	Fix binning
+ *
+ * Jan 10 2007	Add match=1 argument to webrnum()
+ * Jan 10 2007	Rewrite web access in gscread() and gscrnum() to reduce code
+ *
+ * Sep 22 2009	Initialize lengths of FITS table columns
+ * Sep 22 2009	Change region table keywords from DEC*LOW to DEC*LO
+ */
diff --git a/Code/src/libwcs/hget.c b/Code/src/libwcs/hget.c
new file mode 100644
index 0000000000000000000000000000000000000000..54b7cf37d016bfd8f8a8738393c7a99713d20972
--- /dev/null
+++ b/Code/src/libwcs/hget.c
@@ -0,0 +1,1898 @@
+/*** File libwcs/hget.c
+ *** November 12, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1994-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	hget.c (Get FITS Header parameter values)
+ * Purpose:	Extract values for variables from FITS header string
+ * Subroutine:	hgeti2 (hstring,keyword,ival) returns short integer
+ * Subroutine:	hgeti4c (hstring,keyword,wchar,ival) returns long integer
+ * Subroutine:	hgeti4 (hstring,keyword,ival) returns long integer
+ * Subroutine:	hgetr4 (hstring,keyword,rval) returns real
+ * Subroutine:	hgetra (hstring,keyword,ra) returns double RA in degrees
+ * Subroutine:	hgetdec (hstring,keyword,dec) returns double Dec in degrees
+ * Subroutine:	hgetr8c (hstring,keyword,wchar,dval) returns double
+ * Subroutine:	hgetr8 (hstring,keyword,dval) returns double
+ * Subroutine:	hgetl  (hstring,keyword,lval) returns logical int (0=F, 1=T)
+ * Subroutine:	hgetsc (hstring,keyword,wchar,lstr,str) returns character string
+ * Subroutine:	hgets  (hstring,keyword, lstr, str) returns character string
+ * Subroutine:	hgetm  (hstring,keyword, lstr, str) returns multi-keyword string
+ * Subroutine:	hgetdate (hstring,keyword,date) returns date as fractional year
+ * Subroutine:  hgetndec (hstring, keyword, ndec) returns number of dec. places
+ * Subroutine:	hgetc  (hstring,keyword) returns character string
+ * Subroutine:	blsearch (hstring,keyword) returns pointer to blank lines
+		before keyword
+ * Subroutine:	ksearch (hstring,keyword) returns pointer to header string entry
+ * Subroutine:	str2ra (in) converts string to right ascension in degrees
+ * Subroutine:	str2dec (in) converts string to declination in degrees
+ * Subroutine:	strsrch (s1, s2) finds string s2 in null-terminated string s1
+ * Subroutine:	strnsrch (s1, s2, ls1) finds string s2 in ls1-byte string s1
+ * Subroutine:	hlength (header,lhead) sets length of FITS header for searching
+ * Subroutine:  isnum (string) returns 1 if integer, 2 if fp number, else 0
+ * Subroutine:  notnum (string) returns 0 if number, else 1
+ * Subroutine:  numdec (string) returns number of decimal places in numeric string
+ * Subroutine:	strfix (string,blankfill,zerodrop) removes extraneous characters
+ */
+
+#include <string.h>		/* NULL, strlen, strstr, strcpy */
+#include <stdio.h>
+#include "fitshead.h"	/* FITS header extraction subroutines */
+#include <stdlib.h>
+#ifndef VMS
+#include <limits.h>
+#else
+#define INT_MAX  2147483647 /* Biggest number that can fit in long */
+#define SHRT_MAX 32767
+#endif
+#define VLENGTH 81
+
+#ifdef USE_SAOLIB
+static int use_saolib=0;
+#endif
+
+char *hgetc ();
+
+static char val[VLENGTH+1];
+static int multiline = 0;
+
+static int lhead0 = 0;	/* Length of header string */
+
+/* Set the length of the header string, if not terminated by NULL */
+int
+hlength (header, lhead)
+const char *header; /* FITS header */
+int	lhead;	/* Maximum length of FITS header */
+{
+    char *hend;
+    if (lhead > 0)
+	lhead0 = lhead;
+    else {
+	lhead0 = 0;
+	hend = ksearch (header,"END");
+	lhead0 = hend + 80 - header;
+	}
+    return (lhead0);
+}
+
+/* Return the length of the header string, computing it if lhead0 not set */
+int
+gethlength (header)
+char	*header; /* FITS header */
+{
+    if (lhead0 > 0)
+	return (lhead0);
+    else
+	return (hlength (header, 0));
+}
+
+
+/* Extract Integer*4 value for variable from FITS header string */
+
+int
+hgeti4c (hstring,keyword,wchar,ival)
+
+const char *hstring;	/* character string containing FITS header information
+			   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+			   the value of which is returned.  hget searches for
+			   a line beginning with this string.  if "[n]" is
+			   present, the n'th token in the value is returned.
+			   (the first 8 characters must be unique) */
+const char *wchar;	/* Character of multiple WCS header; =0 if unused */
+int	*ival;		/* Keyword value returned */
+{
+    char keyword1[16];
+    int lkey;
+
+    if (wchar[0] < (char) 64)
+	return (hgeti4 (hstring, keyword, ival));
+    else {
+	strcpy (keyword1, keyword);
+	lkey = strlen (keyword);
+	keyword1[lkey] = wchar[0];
+	keyword1[lkey+1] = (char) 0;
+	return (hgeti4 (hstring, keyword1, ival));
+	}
+}
+
+
+/* Extract long value for variable from FITS header string */
+
+int
+hgeti4 (hstring,keyword,ival)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+int *ival;
+{
+    char *value;
+    double dval;
+    int minint;
+    int lval;
+    char *dchar;
+
+    /* Get value and comment from header string */
+    value = hgetc (hstring,keyword);
+
+    /* Translate value from ASCII to binary */
+    if (value != NULL) {
+	if (value[0] == '#') value++;
+	minint = -INT_MAX - 1;
+	lval = strlen (value);
+	if (lval > VLENGTH) {
+	    strncpy (val, value, VLENGTH);
+	    val[VLENGTH] = (char) 0;
+	    }
+	else
+	    strcpy (val, value);
+	if (isnum (val) == 2) {
+	    if ((dchar = strchr (val, 'D')))
+		*dchar = 'e';
+	    if ((dchar = strchr (val, 'd')))
+		*dchar = 'e';
+	    if ((dchar = strchr (val, 'E')))
+		*dchar = 'e';
+	    }
+	dval = atof (val);
+	if (dval+0.001 > INT_MAX)
+	    *ival = INT_MAX;
+	else if (dval >= 0)
+	    *ival = (int) (dval + 0.001);
+	else if (dval-0.001 < minint)
+	    *ival = minint;
+	else
+	    *ival = (int) (dval - 0.001);
+	return (1);
+	}
+    else {
+	return (0);
+	}
+}
+
+
+/* Extract integer*2 value for variable from fits header string */
+
+int
+hgeti2 (hstring,keyword,ival)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+short *ival;
+{
+    char *value;
+    double dval;
+    int minshort;
+    int lval;
+    char *dchar;
+
+    /* Get value and comment from header string */
+    value = hgetc (hstring,keyword);
+
+    /* Translate value from ASCII to binary */
+    if (value != NULL) {
+	if (value[0] == '#') value++;
+	lval = strlen (value);
+	if (lval > VLENGTH) {
+	    strncpy (val, value, VLENGTH);
+	    val[VLENGTH] = (char) 0;
+	    }
+	else
+	    strcpy (val, value);
+	if (isnum (val) == 2) {
+	    if ((dchar = strchr (val, 'D')))
+		*dchar = 'e';
+	    if ((dchar = strchr (val, 'd')))
+		*dchar = 'e';
+	    if ((dchar = strchr (val, 'E')))
+		*dchar = 'e';
+	    }
+	dval = atof (val);
+	minshort = -SHRT_MAX - 1;
+	if (dval+0.001 > SHRT_MAX)
+	    *ival = SHRT_MAX;
+	else if (dval >= 0)
+	    *ival = (short) (dval + 0.001);
+	else if (dval-0.001 < minshort)
+	    *ival = minshort;
+	else
+	    *ival = (short) (dval - 0.001);
+	return (1);
+	}
+    else {
+	return (0);
+	}
+}
+
+/* Extract real value for variable from FITS header string */
+
+int
+hgetr4 (hstring,keyword,rval)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+float *rval;
+{
+    char *value;
+    int lval;
+    char *dchar;
+
+    /* Get value and comment from header string */
+    value = hgetc (hstring,keyword);
+
+    /* translate value from ASCII to binary */
+    if (value != NULL) {
+	if (value[0] == '#') value++;
+	lval = strlen (value);
+	if (lval > VLENGTH) {
+	    strncpy (val, value, VLENGTH);
+	    val[VLENGTH] = (char) 0;
+	    }
+	else
+	    strcpy (val, value);
+	if (isnum (val) == 2) {
+	    if ((dchar = strchr (val, 'D')))
+		*dchar = 'e';
+	    if ((dchar = strchr (val, 'd')))
+		*dchar = 'e';
+	    if ((dchar = strchr (val, 'E')))
+		*dchar = 'e';
+	    }
+	*rval = (float) atof (val);
+	return (1);
+	}
+    else {
+	return (0);
+	}
+}
+
+
+/* Extract real*8 right ascension in degrees from FITS header string */
+
+int
+hgetra (hstring,keyword,dval)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+double *dval;	/* Right ascension in degrees (returned) */
+{
+    char *value;
+
+    /* Get value from header string */
+    value = hgetc (hstring,keyword);
+
+    /* Translate value from ASCII colon-delimited string to binary */
+    if (value != NULL) {
+	*dval = str2ra (value);
+	return (1);
+	}
+    else
+	return (0);
+}
+
+
+/* Extract real*8 declination in degrees from FITS header string */
+
+int
+hgetdec (hstring,keyword,dval)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+double *dval;	/* Right ascension in degrees (returned) */
+{
+    char *value;
+
+    /* Get value from header string */
+    value = hgetc (hstring,keyword);
+
+    /* Translate value from ASCII colon-delimited string to binary */
+    if (value != NULL) {
+	*dval = str2dec (value);
+	return (1);
+	}
+    else
+	return (0);
+}
+
+
+/* Extract real*8 value for variable from FITS header string */
+
+int
+hgetr8c (hstring,keyword,wchar,dval)
+
+const char *hstring;	/* character string containing FITS header information
+			   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+			   the value of which is returned.  hget searches for
+			   a line beginning with this string.  if "[n]" is
+			   present, the n'th token in the value is returned.
+			   (the first 8 characters must be unique) */
+const char *wchar;	/* Character of multiple WCS header; =0 if unused */
+double	*dval;		/* Keyword value returned */
+{
+    char keyword1[16];
+    int lkey;
+
+    if (wchar[0] < (char) 64)
+	return (hgetr8 (hstring, keyword, dval));
+    else {
+	strcpy (keyword1, keyword);
+	lkey = strlen (keyword);
+	keyword1[lkey] = wchar[0];
+	keyword1[lkey+1] = (char) 0;
+	return (hgetr8 (hstring, keyword1, dval));
+	}
+}
+
+
+
+/* Extract real*8 value for variable from FITS header string */
+
+int
+hgetr8 (hstring,keyword,dval)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+double *dval;
+{
+    char *value;
+    int lval;
+    char *dchar;
+
+    /* Get value and comment from header string */
+    value = hgetc (hstring,keyword);
+
+    /* Translate value from ASCII to binary */
+    if (value != NULL) {
+	if (value[0] == '#') value++;
+	lval = strlen (value);
+	if (lval > VLENGTH) {
+	    strncpy (val, value, VLENGTH);
+	    val[VLENGTH] = (char) 0;
+	    }
+	else
+	    strcpy (val, value);
+	if (isnum (val) == 2) {
+	    if ((dchar = strchr (val, 'D')))
+		*dchar = 'e';
+	    if ((dchar = strchr (val, 'd')))
+		*dchar = 'e';
+	    if ((dchar = strchr (val, 'E')))
+		*dchar = 'e';
+	    }
+	*dval = atof (val);
+	return (1);
+	}
+    else {
+	return (0);
+	}
+}
+
+
+/* Extract logical value for variable from FITS header string */
+
+int
+hgetl (hstring,keyword,ival)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+int *ival;
+{
+    char *value;
+    char newval;
+    int lval;
+
+    /* Get value and comment from header string */
+    value = hgetc (hstring,keyword);
+
+    /* Translate value from ASCII to binary */
+    if (value != NULL) {
+	lval = strlen (value);
+	if (lval > VLENGTH) {
+	    strncpy (val, value, VLENGTH);
+	    val[VLENGTH] = (char) 0;
+	    }
+	else
+	    strcpy (val, value);
+        newval = val[0];
+	if (newval == 't' || newval == 'T')
+	    *ival = 1;
+	else
+	    *ival = 0;
+	return (1);
+	}
+    else {
+	return (0);
+	}
+}
+
+
+/* Extract real*8 date from FITS header string (dd/mm/yy or dd-mm-yy) */
+
+int
+hgetdate (hstring,keyword,dval)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+double *dval;
+{
+    double yeardays, seconds, fday;
+    char *value,*sstr, *dstr, *tstr, *cstr, *nval;
+    int year, month, day, yday, i, hours, minutes;
+    static int mday[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
+
+    /* Get value and comment from header string */
+    value = hgetc (hstring,keyword);
+
+    /* Translate value from ASCII to binary */
+    if (value != NULL) {
+	sstr = strchr (value,'/');
+	dstr = strchr (value,'-');
+
+	/* Original FITS date format: dd/mm/yy */
+	if (sstr > value) {
+	    *sstr = '\0';
+	    day = (int) atof (value);
+	    *sstr = '/';
+	    nval = sstr + 1;
+	    sstr = strchr (nval,'/');
+	    if (sstr == NULL)
+		sstr = strchr (nval,'-');
+	    if (sstr > value) {
+		*sstr = '\0';
+		month = (int) atof (nval);
+		*sstr = '/';
+		nval = sstr + 1;
+		year = (int) atof (nval);
+		if (day > 31) {
+		    yday = year;
+		    year = day;
+		    day = yday;
+		    }
+		if (year >= 0 && year <= 49)
+		    year = year + 2000;
+		else if (year < 100)
+		    year = year + 1900;
+		if ((year % 4) == 0)
+		    mday[1] = 29;
+		else
+		    mday[1] = 28;
+		if ((year % 100) == 0 && (year % 400) != 0)
+		    mday[1] = 28;
+		if (day > mday[month-1])
+		    day = mday[month-1];
+		else if (day < 1)
+		    day = 1;
+		if (mday[1] == 28)
+		    yeardays = 365.0;
+		else
+		    yeardays = 366.0;
+		yday = day - 1;
+		for (i = 0; i < month-1; i++)
+		    yday = yday + mday[i];
+		*dval = (double) year + ((double)yday / yeardays);
+		return (1);
+		}
+	    else
+		return (0);
+	    }
+
+	/* New FITS date format: yyyy-mm-ddThh:mm:ss[.sss] */
+	else if (dstr > value) {
+	    *dstr = '\0';
+	    year = (int) atof (value);
+	    *dstr = '-';
+	    nval = dstr + 1;
+	    dstr = strchr (nval,'-');
+	    month = 1;
+	    day = 1;
+	    tstr = NULL;
+	    if (dstr > value) {
+		*dstr = '\0';
+		month = (int) atof (nval);
+		*dstr = '-';
+		nval = dstr + 1;
+		tstr = strchr (nval,'T');
+		if (tstr > value)
+		    *tstr = '\0';
+		day = (int) atof (nval);
+		if (tstr > value)
+		    *tstr = 'T';
+		}
+
+	    /* If year is < 32, it is really day of month in old format */
+	    if (year < 32) {
+		i = year;
+		year = day + 1900;
+		day = i;
+		}
+
+	    if ((year % 4) == 0)
+		mday[1] = 29;
+	    else
+		mday[1] = 28;
+	    if ((year % 100) == 0 && (year % 400) != 0)
+		mday[1] = 28;
+	    if (day > mday[month-1])
+		day = mday[month-1];
+	    else if (day < 1)
+		day = 1;
+	    if (mday[1] == 28)
+		yeardays = 365.0;
+	    else
+		yeardays = 366.0;
+	    yday = day - 1;
+	    for (i = 0; i < month-1; i++)
+		yday = yday + mday[i];
+	    *dval = (double) year + ((double)yday / yeardays);
+
+	    /* Extract time, if it is present */
+	    if (tstr > value) {
+		nval = tstr + 1;
+		hours = 0.0;
+		minutes = 0.0;
+		seconds = 0.0;
+		cstr = strchr (nval,':');
+		if (cstr > value) {
+		    *cstr = '\0';
+		    hours = (int) atof (nval);
+		    *cstr = ':';
+		    nval = cstr + 1;
+		    cstr = strchr (nval,':');
+		    if (cstr > value) {
+			*cstr = '\0';
+			minutes = (int) atof (nval);
+			*cstr = ':';
+			nval = cstr + 1;
+			seconds = atof (nval);
+			}
+		    else {
+			minutes = (int) atof (nval);
+			seconds = 0.0;
+			}
+		    }
+		fday = ((3.6e3 * (double)hours) + (6.e1 * (double)minutes) +
+		       seconds) / 8.64e4;
+		*dval = *dval + (fday / yeardays);
+		}
+	    return (1);
+	    }
+	else
+	    return (0);
+	}
+    else
+	return (0);
+}
+
+
+/* Extract IRAF multiple-keyword string value from FITS header string */
+
+int
+hgetm (hstring, keyword, lstr, str)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the root name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+const int lstr;	/* Size of str in characters */
+char *str;	/* String (returned) */
+{
+    char *value;
+    char *stri;
+    char keywordi[16];
+    int lval, lstri, ikey;
+    char keyform[8];
+
+    stri = str;
+    lstri = lstr;
+
+    sprintf (keywordi, "%s_1", keyword);
+    if (ksearch (hstring, keywordi))
+	strcpy (keyform, "%s_%d");
+    else {
+	sprintf (keywordi, "%s_01", keyword);
+	if (ksearch (hstring, keywordi))
+	    strcpy (keyform, "%s_%02d");
+	else {
+	    sprintf (keywordi, "%s_001", keyword);
+	    if (ksearch (hstring, keywordi))
+		strcpy (keyform, "%s_%03d");
+	    else
+		return (0);
+	    }
+	}
+
+    /* Loop through sequentially-named keywords */
+    multiline = 1;
+    for (ikey = 1; ikey < 500; ikey++) {
+	sprintf (keywordi, keyform, keyword, ikey);
+
+	/* Get value for this keyword */
+	value = hgetc (hstring, keywordi);
+	if (value != NULL) {
+	    lval = strlen (value);
+	    if (lval < lstri)
+		strcpy (stri, value);
+	    else if (lstri > 1) {
+		strncpy (stri, value, lstri-1);
+		stri[lstri] = (char) 0;
+		break;
+		}
+	    else {
+		str[0] = value[0];
+		break;
+		}
+	    }
+	else
+	    break;
+	stri = stri + lval;
+	lstri = lstri - lval;
+	}
+    multiline = 0;
+
+    /* Return 1 if any keyword found, else 0 */
+    if (ikey > 1)
+	return (1);
+    else
+	return (0);
+}
+
+
+/* Extract string value for variable from FITS header string */
+
+int
+hgetsc (hstring,keyword,wchar,lstr,str)
+
+const char *hstring;	/* character string containing FITS header information
+			   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+			   the value of which is returned.  hget searches for
+			   a line beginning with this string.  if "[n]" is
+			   present, the n'th token in the value is returned.
+			   (the first 8 characters must be unique) */
+const char *wchar;	/* Character of multiple WCS header; =0 if unused */
+const int lstr;		/* Size of str in characters */
+char	*str;		/* String (returned) */
+{
+    char keyword1[16];
+    int lkey;
+
+    if (wchar[0] < (char) 64)
+	return (hgets (hstring, keyword, lstr, str));
+    else {
+	strcpy (keyword1, keyword);
+	lkey = strlen (keyword);
+	keyword1[lkey] = wchar[0];
+	keyword1[lkey+1] = (char) 0;
+	return (hgets (hstring, keyword1, lstr, str));
+	}
+}
+
+
+/* Extract string value for variable from FITS header string */
+
+int
+hgets (hstring, keyword, lstr, str)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+const int lstr;	/* Size of str in characters */
+char *str;	/* String (returned) */
+{
+    char *value;
+    int lval;
+
+    /* Get value and comment from header string */
+    value = hgetc (hstring,keyword);
+
+    if (value != NULL) {
+	lval = strlen (value);
+	if (lval < lstr)
+	    strcpy (str, value);
+	else if (lstr > 1)
+	    strncpy (str, value, lstr-1);
+	else
+	    str[0] = value[0];
+	return (1);
+	}
+    else
+	return (0);
+}
+
+
+/* Extract number of decimal places for value in FITS header string */
+
+int
+hgetndec (hstring, keyword, ndec)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+int *ndec;	/* Number of decimal places in keyword value */
+{
+    char *value;
+    int i, nchar;
+
+    /* Get value and comment from header string */
+    value = hgetc (hstring,keyword);
+
+    /* Find end of string and count backward to decimal point */
+    *ndec = 0;
+    if (value != NULL) {
+	nchar = strlen (value);
+	for (i = nchar-1; i >= 0; i--) {
+	    if (value[i] == '.')
+		return (1);
+	    *ndec = *ndec + 1;
+	    }
+	return (1);
+	}
+    else
+	return (0);
+}
+
+
+/* Extract character value for variable from FITS header string */
+
+char *
+hgetc (hstring,keyword0)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword0;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+{
+    static char cval[80];
+    char *value;
+    char cwhite[2];
+    char squot[2], dquot[2], lbracket[2], rbracket[2], slash[2], comma[2];
+    char space;
+    char keyword[81]; /* large for ESO hierarchical keywords */
+    char line[100];
+    char *vpos, *cpar;
+    char *q1, *q2, *v1, *v2, *c1, *brack1, *brack2;
+    int ipar, i, lkey;
+
+#ifdef USE_SAOLIB
+    int iel=1, ip=1, nel, np, ier;
+    char *get_fits_head_str();
+
+    if( !use_saolib ){
+#endif
+
+    squot[0] = (char) 39;
+    squot[1] = (char) 0;
+    dquot[0] = (char) 34;
+    dquot[1] = (char) 0;
+    lbracket[0] = (char) 91;
+    lbracket[1] = (char) 0;
+    comma[0] = (char) 44;
+    comma[1] = (char) 0;
+    rbracket[0] = (char) 93;
+    rbracket[1] = (char) 0;
+    slash[0] = (char) 47;
+    slash[1] = (char) 0;
+    space = (char) 32;
+
+    /* Find length of variable name */
+    strncpy (keyword,keyword0, sizeof(keyword)-1);
+    brack1 = strsrch (keyword,lbracket);
+    if (brack1 == NULL)
+	brack1 = strsrch (keyword,comma);
+    if (brack1 != NULL) {
+	*brack1 = '\0';
+	brack1++;
+	}
+
+    /* Search header string for variable name */
+    vpos = ksearch (hstring,keyword);
+
+    /* Exit if not found */
+    if (vpos == NULL) {
+	return (NULL);
+	}
+
+    /* Initialize line to nulls */
+    for (i = 0; i < 100; i++)
+	line[i] = 0;
+
+/* In standard FITS, data lasts until 80th character */
+
+    /* Extract entry for this variable from the header */
+    strncpy (line,vpos,80);
+
+    /* Check for quoted value */
+    q1 = strsrch (line,squot);
+    c1 = strsrch (line,slash);
+    if (q1 != NULL) {
+	if (c1 != NULL && q1 < c1) {
+	    q2 = strsrch (q1+1,squot);
+	    if (q2 == NULL) {
+		q2 = c1 - 1;
+		while (*q2 == space)
+		    q2--;
+		q2++;
+		}
+	    else if (c1 < q2)
+		c1 = strsrch (q2,slash);
+	    }
+	else if (c1 == NULL) {
+	    q2 = strsrch (q1+1,squot);
+	    if (q2 == NULL) {
+		q2 = line + 79;
+		while (*q2 == space)
+		    q2--;
+		q2++;
+		}
+	    }
+	else
+	    q1 = NULL;
+	}
+    else {
+	q1 = strsrch (line,dquot);
+	if (q1 != NULL) {
+	    if (c1 != NULL && q1 < c1) {
+		q2 = strsrch (q1+1,dquot);
+		if (q2 == NULL) {
+		    q2 = c1 - 1;
+		    while (*q2 == space)
+			q2--;
+		    q2++;
+		    }
+		else if (c1 < q2)
+		    c1 = strsrch (q2,slash);
+		}
+	    else if (c1 == NULL) {
+		q2 = strsrch (q1+1,dquot);
+		if (q2 == NULL) {
+		    q2 = line + 79;
+		    while (*q2 == space)
+			q2--;
+		    q2++;
+		    }
+		}
+	    else
+		q1 = NULL;
+	    }
+	else {
+	    q1 = NULL;
+	    q2 = line + 10;
+	    }
+	}
+
+    /* Extract value and remove excess spaces */
+    if (q1 != NULL) {
+	v1 = q1 + 1;
+	v2 = q2;
+	}
+    else {
+	v1 = strsrch (line,"=");
+	if (v1 == NULL)
+	    v1 = line + 9;
+	else
+	    v1 = v1 + 1;
+	c1 = strsrch (line,"/");
+	if (c1 != NULL)
+	    v2 = c1;
+	else
+	    v2 = line + 79;
+	}
+
+    /* Ignore leading spaces if not multiline */
+    if (!multiline) {
+	while (*v1 == ' ' && v1 < v2) {
+	    v1++;
+	    }
+	}
+
+    /* Drop trailing spaces */
+    *v2 = '\0';
+    if (!multiline) {
+	v2--;
+	while ((*v2 == ' ' || *v2 == (char) 13) && v2 > v1) {
+	    *v2 = '\0';
+	    v2--;
+	    }
+	}
+
+    /* Convert -zero to just plain 0 */
+    if (!strcmp (v1, "-0"))
+	v1++;
+    strcpy (cval,v1);
+    value = cval;
+
+    /* If keyword has brackets, extract appropriate token from value */
+    if (brack1 != NULL) {
+	brack2 = strsrch (brack1,rbracket);
+	if (brack2 != NULL)
+	    *brack2 = '\0';
+	if (isnum (brack1)) {
+	    ipar = atoi (brack1);
+	    cwhite[0] = ' ';
+	    cwhite[1] = '\0';
+	    if (ipar > 0) {
+		for (i = 1; i <= ipar; i++) {
+		    cpar = strtok (v1,cwhite);
+		    v1 = NULL;
+		    }
+		if (cpar != NULL) {
+		    strcpy (cval,cpar);
+		    value = cval;
+		    }
+		else
+		    value = NULL;
+		}
+
+	    /* If token counter is negative, include rest of value */
+	    else if (ipar < 0) {
+		for (i = 1; i < -ipar; i++) {
+		    v1 = strchr (v1, ' ');
+		    if (v1 == NULL)
+			break;
+		    else
+			v1 = v1 + 1;
+		    }
+		if (v1 != NULL) {
+		    strcpy (cval, v1);
+		    value = cval;
+		    }
+		else
+		    value = NULL;
+		}
+	    }
+	else {
+	    lkey = strlen (brack1);
+	    for (i = 0; i < lkey; i++) {
+		if (brack1[i] > 64 && brack1[i] < 91)
+		    brack1[i] = brack1[i] + 32; 
+		}
+	    v1 = igetc (cval, brack1);
+	    if (v1) {
+		strcpy (cval,v1);
+		value = cval;
+		}
+	    else
+		value = NULL;
+	    }
+	}
+
+    return (value);
+#ifdef USE_SAOLIB
+    } else {
+	return(get_fits_head_str(keyword0, iel, ip, &nel, &np, &ier, hstring));
+    }
+#endif
+}
+
+
+/* Find beginning of fillable blank line before FITS header keyword line */
+
+char *
+blsearch (hstring,keyword)
+
+/* Find entry for keyword keyword in FITS header string hstring.
+   (the keyword may have a maximum of eight letters)
+   NULL is returned if the keyword is not found */
+
+const char *hstring;	/* character string containing fits-style header
+		information in the format <keyword>= <value> {/ <comment>}
+		the default is that each entry is 80 characters long;
+		however, lines may be of arbitrary length terminated by
+		nulls, carriage returns or linefeeds, if packed is true.  */
+const char *keyword;	/* character string containing the name of the variable
+		to be returned.  ksearch searches for a line beginning
+		with this string.  The string may be a character
+		literal or a character variable terminated by a null
+		or '$'.  it is truncated to 8 characters. */
+{
+    const char *headlast;
+    char *loc, *headnext, *pval, *lc, *line;
+    char *bval;
+    int icol, nextchar, lkey, nleft, lhstr;
+
+    pval = 0;
+
+    /* Search header string for variable name */
+    if (lhead0)
+	lhstr = lhead0;
+    else {
+	lhstr = 0;
+	while (lhstr < 256000 && hstring[lhstr] != 0)
+	    lhstr++;
+	}
+    headlast = hstring + lhstr;
+    headnext = (char *) hstring;
+    pval = NULL;
+    while (headnext < headlast) {
+	nleft = headlast - headnext;
+	loc = strncsrch (headnext, keyword, nleft);
+
+	/* Exit if keyword is not found */
+	if (loc == NULL) {
+	    break;
+	    }
+
+	icol = (loc - hstring) % 80;
+	lkey = strlen (keyword);
+	nextchar = (int) *(loc + lkey);
+
+	/* If this is not in the first 8 characters of a line, keep searching */
+	if (icol > 7)
+	    headnext = loc + 1;
+
+	/* If parameter name in header is longer, keep searching */
+	else if (nextchar != 61 && nextchar > 32 && nextchar < 127)
+	    headnext = loc + 1;
+
+	/* If preceeding characters in line are not blanks, keep searching */
+	else {
+	    line = loc - icol;
+	    for (lc = line; lc < loc; lc++) {
+		if (*lc != ' ')
+		    headnext = loc + 1;
+		}
+
+	/* Return pointer to start of line if match */
+	    if (loc >= headnext) {
+		pval = line;
+		break;
+		}
+	    }
+	}
+
+    /* Return NULL to calling program if keyword is not found */
+    if (pval == NULL)
+	return (pval);
+
+    /* Return NULL if keyword is found at start of FITS header string */
+    if (pval == hstring)
+	return (NULL);
+
+    /* Find last nonblank in FITS header string line before requested keyword */
+    bval = pval - 80;
+    while (!strncmp (bval,"        ",8) && bval >= hstring)
+	bval = bval - 80;
+    bval = bval + 80;
+
+    /* Return pointer to calling program if blank lines found */
+    if (bval < pval && bval >= hstring)
+	return (bval);
+    else
+	return (NULL);
+}
+
+
+/* Find FITS header line containing specified keyword */
+
+char *
+ksearch (hstring,keyword)
+
+/* Find entry for keyword keyword in FITS header string hstring.
+   (the keyword may have a maximum of eight letters)
+   NULL is returned if the keyword is not found */
+
+const char *hstring;	/* character string containing fits-style header
+		information in the format <keyword>= <value> {/ <comment>}
+		the default is that each entry is 80 characters long;
+		however, lines may be of arbitrary length terminated by
+		nulls, carriage returns or linefeeds, if packed is true.  */
+const char *keyword;	/* character string containing the name of the variable
+		to be returned.  ksearch searches for a line beginning
+		with this string.  The string may be a character
+		literal or a character variable terminated by a null
+		or '$'.  it is truncated to 8 characters. */
+{
+    const char *headlast;
+    char *loc, *headnext, *pval, *lc, *line;
+    int icol, nextchar, lkey, nleft, lhead, lmax;
+
+#ifdef USE_SAOLIB
+	int iel=1, ip=1, nel, np, ier;
+	char *get_fits_head_str();
+
+	if( !use_saolib ){
+#endif
+
+    pval = 0;
+
+/* Find current length of header string */
+    if (lhead0)
+	lmax = lhead0;
+    else
+	lmax = 256000;
+    for (lhead = 0; lhead < lmax; lhead++) {
+	if (hstring[lhead] == (char) 0)
+	    break;
+	}
+
+/* Search header string for variable name */
+    headlast = hstring + lhead;
+    headnext = (char *) hstring;
+    pval = NULL;
+    while (headnext < headlast) {
+	nleft = headlast - headnext;
+	loc = strncsrch (headnext, keyword, nleft);
+
+	/* Exit if keyword is not found */
+	if (loc == NULL) {
+	    break;
+	    }
+
+	icol = (loc - hstring) % 80;
+	lkey = strlen (keyword);
+	nextchar = (int) *(loc + lkey);
+
+	/* If this is not in the first 8 characters of a line, keep searching */
+	if (icol > 7)
+	    headnext = loc + 1;
+
+	/* If parameter name in header is longer, keep searching */
+	else if (nextchar != 61 && nextchar > 32 && nextchar < 127)
+	    headnext = loc + 1;
+
+	/* If preceeding characters in line are not blanks, keep searching */
+	else {
+	    line = loc - icol;
+	    for (lc = line; lc < loc; lc++) {
+		if (*lc != ' ')
+		    headnext = loc + 1;
+		}
+
+	/* Return pointer to start of line if match */
+	    if (loc >= headnext) {
+		pval = line;
+		break;
+		}
+	    }
+	}
+
+/* Return pointer to calling program */
+	return (pval);
+
+#ifdef USE_SAOLIB
+	}
+	else {
+	    if (get_fits_head_str(keyword,iel,ip,&nel,&np,&ier,hstring) != NULL)
+		return(hstring);
+	    else
+		return(NULL);
+	    }
+#endif
+}
+
+
+/* Return the right ascension in degrees from sexagesimal hours or decimal degrees */
+
+double
+str2ra (in)
+
+const char *in;	/* Character string of sexigesimal hours or decimal degrees */
+
+{
+    double ra;	/* Right ascension in degrees (returned) */
+
+    ra = str2dec (in);
+    if (strsrch (in,":"))
+	ra = ra * 15.0;
+
+    return (ra);
+}
+
+
+/* Return the declination in degrees from sexagesimal or decimal degrees */
+
+double
+str2dec (in)
+
+const char *in;	/* Character string of sexigesimal or decimal degrees */
+
+{
+    double dec;		/* Declination in degrees (returned) */
+    double deg, min, sec, sign;
+    char *value, *c1, *c2;
+    int lval;
+    char *dchar;
+
+    dec = 0.0;
+
+    /* Return 0.0 if string is null */
+    if (in == NULL)
+	return (dec);
+
+    /* Translate value from ASCII colon-delimited string to binary */
+    if (in[0]) {
+	value = (char *) in;
+
+	/* Remove leading spaces */
+	while (*value == ' ')
+	    value++;
+
+	/* Save sign */
+	if (*value == '-') {
+	    sign = -1.0;
+	    value++;
+	    }
+	else if (*value == '+') {
+	    sign = 1.0;
+	    value++;
+	    }
+	else
+	    sign = 1.0;
+
+	/* Remove trailing spaces */
+	lval = strlen (value);
+	while (value[lval-1] == ' ')
+	    lval--;
+	
+	if ((c1 = strsrch (value,":")) == NULL)
+	    c1 = strnsrch (value," ",lval);
+	if (c1 != NULL) {
+	    *c1 = 0;
+	    deg = (double) atoi (value);
+	    *c1 = ':';
+	    value = c1 + 1;
+	    if ((c2 = strsrch (value,":")) == NULL)
+		c2 = strsrch (value," ");
+	    if (c2 != NULL) {
+		*c2 = 0;
+		min = (double) atoi (value);
+		*c2 = ':';
+		value = c2 + 1;
+		sec = atof (value);
+		}
+	    else {
+		sec = 0.0;
+		if ((c1 = strsrch (value,".")) != NULL)
+		    min = atof (value);
+		if (strlen (value) > 0)
+		    min = (double) atoi (value);
+		}
+	    dec = sign * (deg + (min / 60.0) + (sec / 3600.0));
+	    }
+	else if (isnum (value) == 2) {
+	    if ((dchar = strchr (value, 'D')))
+		*dchar = 'e';
+	    if ((dchar = strchr (value, 'd')))
+		*dchar = 'e';
+	    if ((dchar = strchr (value, 'E')))
+		*dchar = 'e';
+	    dec = sign * atof (value);
+	    }
+	else 
+	    dec = sign * (double) atoi (value);
+	}
+    return (dec);
+}
+
+
+/* Find string s2 within null-terminated string s1 */
+
+char *
+strsrch (s1, s2)
+
+const char *s1;	/* String to search */
+const char *s2;	/* String to look for */
+
+{
+    int ls1;
+    ls1 = strlen (s1);
+    return (strnsrch (s1, s2, ls1));
+}
+
+
+/* Find string s2 within string s1 */
+
+char *
+strnsrch (s1, s2, ls1)
+
+const char *s1;	/* String to search */
+const char *s2;	/* String to look for */
+const int ls1;	/* Length of string being searched */
+
+{
+    char *s,*s1e;
+    char cfirst,clast;
+    int i,ls2;
+
+    /* Return null string if either pointer is NULL */
+    if (s1 == NULL || s2 == NULL)
+	return (NULL);
+
+    /* A zero-length pattern is found in any string */
+    ls2 = strlen (s2);
+    if (ls2 ==0)
+	return ((char *) s1);
+
+    /* Only a zero-length string can be found in a zero-length string */
+    if (ls1 ==0)
+	return (NULL);
+
+    cfirst = (char) s2[0];
+    clast = (char) s2[ls2-1];
+    s1e = (char *) s1 + (int) ls1 - ls2 + 1;
+    s = (char *) s1;
+    while (s < s1e) { 
+
+	/* Search for first character in pattern string */
+	if (*s == cfirst) {
+
+	    /* If single character search, return */
+	    if (ls2 == 1)
+		return (s);
+
+	    /* Search for last character in pattern string if first found */
+	    if (s[ls2-1] == clast) {
+
+		/* If two-character search, return */
+		if (ls2 == 2)
+		    return (s);
+
+		/* If 3 or more characters, check for rest of search string */
+		i = 1;
+		while (i < ls2 && s[i] == s2[i])
+		    i++;
+
+		/* If entire string matches, return */
+		if (i >= ls2)
+		    return (s);
+		}
+	    }
+	s++;
+	}
+    return (NULL);
+}
+
+
+/* Find string s2 within null-terminated string s1 (case-free search) */
+
+char *
+strcsrch (s1, s2)
+
+const char *s1;	/* String to search */
+const char *s2;	/* String to look for */
+
+{
+    int ls1;
+    ls1 = strlen ((char *) s1);
+    return (strncsrch (s1, s2, ls1));
+}
+
+
+/* Find string s2 within string s1 (case-free search) */
+
+char *
+strncsrch (s1, s2, ls1)
+
+const char *s1;	/* String to search */
+const char *s2;	/* String to look for */
+const int ls1;	/* Length of string being searched */
+
+{
+    char *s,*s1e, sl, *os2;
+    char cfirst,ocfirst;
+    char clast = ' ';
+    char oclast = ' ';
+    int i,ls2;
+
+    /* Return null string if either pointer is NULL */
+    if (s1 == NULL || s2 == NULL)
+	return (NULL);
+
+    /* A zero-length pattern is found in any string */
+    ls2 = strlen (s2);
+    if (ls2 ==0)
+	return ((char *) s1);
+
+    /* Only a zero-length string can be found in a zero-length string */
+    os2 = NULL;
+    if (ls1 ==0)
+	return (NULL);
+
+    /* For one or two characters, set opposite case first and last letters */
+    if (ls2 < 3) {
+	cfirst = (char) s2[0];
+	if (cfirst > 96 && cfirst < 123)
+	    ocfirst = cfirst - 32;
+	else if (cfirst > 64 && cfirst < 91)
+	    ocfirst = cfirst + 32;
+	else
+	    ocfirst = cfirst;
+	if (ls2 > 1) {
+	    clast = s2[1];
+	    if (clast > 96 && clast < 123)
+		oclast = clast - 32;
+	    else if (clast > 64 && clast < 91)
+		oclast = clast + 32;
+	    else
+		oclast = clast;
+	    }
+	}
+
+    /* Else duplicate string with opposite case letters for comparison */
+    else {
+	os2 = (char *) calloc (ls2, 1);
+	for (i = 0; i < ls2; i++) {
+	    if (s2[i] > 96 && s2[i] < 123)
+		os2[i] = s2[i] - 32;
+	    else if (s2[i] > 64 && s2[i] < 91)
+		os2[i] = s2[i] + 32;
+	    else
+		os2[i] = s2[i];
+	    }
+	cfirst = s2[0];
+	ocfirst = os2[0];
+	clast = s2[ls2-1];
+	oclast = os2[ls2-1];
+	}
+
+    /* Loop through input string, character by character */
+    s = (char *) s1;
+    s1e = s + (int) ls1 - ls2 + 1;
+    while (s < s1e) { 
+
+	/* Search for first character in pattern string */
+	if (*s == cfirst || *s == ocfirst) {
+
+	    /* If single character search, return */
+	    if (ls2 == 1)
+		return (s);
+
+	    /* Search for last character in pattern string if first found */
+	    sl = s[ls2-1];
+	    if (sl == clast || sl == oclast) {
+
+		/* If two-character search, return */
+		if (ls2 == 2)
+		    return (s);
+
+		/* If 3 or more characters, check for rest of search string */
+		i = 1;
+		while (i < ls2 && (s[i] == (char) s2[i] || s[i] == os2[i]))
+		    i++;
+
+		/* If entire string matches, return */
+		if (i >= ls2) {
+		    free (os2);
+		    return (s);
+		    }
+		}
+	    }
+	s++;
+	}
+    if (os2 != NULL)
+	free (os2);
+    return (NULL);
+}
+
+
+int
+notnum (string)
+
+const char *string;	/* Character string */
+{
+    if (isnum (string))
+	return (0);
+    else
+	return (1);
+}
+
+
+/* ISNUM-- Return 1 if string is an integer number,
+		  2 if floating point,
+		  3 if sexigesimal, with or without decimal point
+		  else 0
+ */
+
+int
+isnum (string)
+
+const char *string;	/* Character string */
+{
+    int lstr, i, nd, cl;
+    char cstr, cstr1;
+    int fpcode;
+
+    /* Return 0 if string is NULL */
+    if (string == NULL)
+	return (0);
+
+    lstr = strlen (string);
+    nd = 0;
+    cl = 0;
+    fpcode = 1;
+
+    /* Return 0 if string starts with a D or E */
+    cstr = string[0];
+    if (cstr == 'D' || cstr == 'd' ||
+	cstr == 'E' || cstr == 'e') {
+	return (0);
+	}
+
+    /* Remove trailing spaces */
+    while (string[lstr-1] == ' ')
+	lstr--;
+
+    /* Numeric strings contain 0123456789-+ and d or e for exponents */
+    for (i = 0; i < lstr; i++) {
+	cstr = string[i];
+	if (cstr == '\n')
+	    break;
+
+	/* Ignore leading spaces */
+	if (cstr == ' ' && nd == 0)
+	    continue;
+
+	if ((cstr < 48 || cstr > 57) &&
+	    cstr != '+' && cstr != '-' &&
+	    cstr != 'D' && cstr != 'd' &&
+	    cstr != 'E' && cstr != 'e' &&
+	    cstr != ':' && cstr != '.')
+	    return (0);
+	else if (cstr == '+' || cstr == '-') {
+	    if (string[i+1] == '-' || string[i+1] == '+')
+		return (0);
+	    else if (i > 0) {
+		cstr1 = string[i-1];
+		if (cstr1 != 'D' && cstr1 != 'd' &&
+		    cstr1 != 'E' && cstr1 != 'e' &&
+		    cstr1 != ':' && cstr1 != ' ')
+		    return (0);
+		}
+	    }
+	else if (cstr >= 47 && cstr <= 57)
+	    nd++;
+
+	/* Check for colon */
+	else if (cstr == 58)
+	    cl++;
+	if (cstr=='.' || cstr=='d' || cstr=='e' || cstr=='d' || cstr=='e')
+	    fpcode = 2;
+	}
+    if (nd > 0) {
+	if (cl)
+	    fpcode = 3;
+	return (fpcode);
+	}
+    else
+	return (0);
+}
+
+
+/* NUMDEC -- Return number of decimal places in numeric string (-1 if not number) */
+
+int
+numdec (string)
+
+const char *string;	/* Numeric string */
+{
+    char *cdot;
+    int lstr;
+
+    if (notnum (string) && !strchr (string, ':'))
+        return (-1);
+    else {
+        lstr = strlen (string);
+        if ((cdot = strchr (string, '.')) == NULL)
+            return (0);
+        else
+            return (lstr - (cdot - string) - 1);
+        }
+}
+
+
+#ifdef USE_SAOLIB
+int set_saolib(hstring)
+    void *hstring;
+{
+    if( *((int *)hstring) == 142857 )
+	use_saolib = 1;
+    else
+	use_saolib = 0;
+}
+
+#endif
+
+
+/* Remove exponent, leading #, surrounding parentheses,
+   and/or trailing zeroes, if reasonable */
+void
+strfix (string, fillblank, dropzero)
+
+char	*string;	/* String to modify */
+int	fillblank;	/* If nonzero, fill blanks with underscores */
+int	dropzero;	/* If nonzero, drop trailing zeroes */
+{
+    char *sdot, *s, *strend, *str, ctemp, *slast;
+    int ndek, lstr, i;
+
+    /* If number, ignore leading # and remove trailing non-numeric character */
+    if (string[0] == '#') {
+	strend = string + strlen (string);
+	str = string + 1;
+	strend = str + strlen (str) - 1;
+	ctemp = *strend;
+	if (!isnum (strend))
+	    *strend = (char) 0;
+	if (isnum (str)) {
+	    strend = string + strlen (string);
+	    for (str = string; str < strend; str++)
+		*str = *(str + 1);
+	    }
+	else
+	    *strend = ctemp;
+	}
+
+    /* Remove parentheses if they enclose the string */
+    if (string[0] == '(') {
+	lstr = strlen (string);
+	if (string[lstr-1] == ')') {
+	    string[lstr-1] = (char) 0;
+	    strend = string + lstr - 1;
+	    for (str = string; str < strend; str++)
+		*str = *(str+1);
+	    string[lstr-2] = (char) 0;
+	    }
+	}
+
+    /* Remove positive exponent if there are enough digits given */
+    if (isnum (string) > 1 && strsrch (string, "E+") != NULL) {
+	lstr = strlen (string);
+	ndek = (int) (string[lstr-1] - 48);
+	ndek = ndek + (10 * ((int) (string[lstr-2] - 48)));
+	if (ndek < lstr - 7) {
+	    lstr = lstr - 4;
+	    string[lstr] = (char) 0;
+	    string[lstr+1] = (char) 0;
+	    string[lstr+2] = (char) 0;
+	    string[lstr+3] = (char) 0;
+	    sdot = strchr (string, '.');
+	    if (ndek > 0 && sdot != NULL) {
+		for (i = 1; i <= ndek; i++) {
+		    *sdot = *(sdot+1);
+		    sdot++;
+		    *sdot = '.';
+		    }
+		}
+	    }
+	}
+
+    /* Remove trailing zeroes if they are not significant */
+    if (dropzero) {
+	if (isnum (string) > 1 && strchr (string, '.') != NULL &&
+	    strsrch (string, "E-") == NULL &&
+	    strsrch (string, "E+") == NULL &&
+	    strsrch (string, "e-") == NULL &&
+	    strsrch (string, "e+") == NULL) {
+	    lstr = strlen (string);
+	    s = string + lstr - 1;
+	    while (*s == '0' && lstr > 1) {
+		if (*(s - 1) != '.') {
+		    *s = (char) 0;
+		    lstr --;
+		    }
+		s--;
+		}
+	    }
+	}
+
+    /* Remove trailing decimal point */
+    lstr = strlen (string);
+    s = string + lstr - 1;
+    if (*s == '.')
+	*s = (char) 0;
+
+    /* Replace embedded blanks with underscores, if requested to */
+    if (fillblank) {
+	lstr = strlen (string);
+	slast = string + lstr;
+	for (s = string; s < slast; s++) {
+	    if (*s == ' ') *s = '_';
+	    }
+	}
+
+    return;
+
+}
+
+/* Oct 28 1994	New program
+ *
+ * Mar  1 1995	Search for / after second quote, not first one
+ * May  2 1995	Initialize line in HGETC; deal with logicals in HGETL better
+ * May  4 1995	Declare STRSRCH in KSEARCH
+ * Aug  7 1995  Fix line initialization in HGETC
+ * Dec 22 1995	Add HGETRA and HGETDEC to get degrees from xx:xx:xx.xxx string
+ *
+ * Jan 26 1996	Fix HGETL to not crash when parameter is not present
+ * Feb  1 1996	Fix HGETC to deal with quotes correctly
+ * Feb  1 1996	Fix HGETDEG to deal with sign correctly
+ * Feb  6 1996	Add HGETS to update character strings
+ * Feb  8 1996	Fix STRSRCH to find final characters in string
+ * Feb 23 1996	Add string to degree conversions
+ * Apr 26 1996	Add HGETDATE to get fractional year from date string
+ * May 22 1996	Fix documentation; return double from STR2RA and STR2DEC
+ * May 28 1996	Fix string translation of RA and Dec when no seconds
+ * Jun 10 1996	Remove unused variables after running lint
+ * Jun 17 1996	Fix bug which failed to return single character strings
+ * Jul  1 1996	Skip sign when reading declination after testing for it
+ * Jul 19 1996	Do not divide by 15 if RA header value is already in degrees
+ * Aug  5 1996	Add STRNSRCH to search strings which are not null-terminated
+ * Aug  6 1996	Make minor changes after lint
+ * Aug  8 1996	Fix ksearch bug which finds wrong keywords
+ * Aug 13 1996	Fix sign bug in STR2DEC for degrees
+ * Aug 26 1996	Drop unused variables ICOL0, NLINE, PREVCHAR from KSEARCH
+ * Sep 10 1996	Fix header length setting code
+ * Oct 15 1996	Clean up loops and fix ICOL assignment
+ * Nov 13 1996	Handle integer degrees correctly in STR2DEC
+ * Nov 21 1996	Make changes for Linux thanks to Sidik Isani
+ * Dec 12 1996	Add ISNUM to check to see whether strings are numbers
+ *
+ * Jan 22 1997	Add ifdefs for Eric Mandel (SAOtng)
+ * Jan 27 1997	Convert to integer through ATOF so exponents are recognized
+ * Jul 25 1997	Implement FITS version of ISO date format
+ * 
+ * Feb 24 1998	Implement code to return IRAF multiple-keyword strings
+ * Mar 12 1998	Add subroutine NOTNUM
+ * Mar 27 1998	Add changes to match SKYCAT version
+ * Apr 30 1998	Add BLSEARCH() to find blank lines before END
+ * May 27 1998	Add HGETNDEC() to get number of decimal places in entry
+ * Jun  1 1998	Add VMS patch from Harry Payne at StSci
+ * Jun 18 1998	Fix code which extracts tokens from string values
+ * Jul 21 1998	Drop minus sign for values of -0
+ * Sep 29 1998	Treat hyphen-separated date as old format if 2-digit year
+ * Oct  7 1998	Clean up search for last blank line
+ *
+ * Apr  5 1999	Check lengths of strings before copying them
+ * May  5 1999	values.h -> POSIX limits.h: MAXINT->INT_MAX, MAXSHORT->SHRT_MAX
+ * Jul 15 1999	Add hgetm() options of 1- or 2-digit keyword extensions
+ * Oct  6 1999	Add gethlength() to return header length
+ * Oct 14 1999	In ksearch(), search only to null not to end of buffer
+ * Oct 15 1999	Return 1 from hgetndec() if successful
+ * Oct 20 1999	Drop unused variable after lint (val in hgetndec)
+ * Dec  3 1999	Fix isnum() to reject strings starting with a d or e
+ * Dec 20 1999	Update hgetdate() to get minutes and seconds right
+ *
+ * Feb 10 2000	Parse RA and Dec with spaces as well as colons as separators
+ * Feb 11 2000	Add null at end of multi-line keyword value character string
+ * Feb 25 2000	Change max search string length from 57600 to 256000
+ * Mar 15 2000	Deal with missing second quotes in string values
+ * Mar 17 2000	Return 2 from isnum() if number is floating point (.de)
+ * Mar 17 2000	Ignore leading # for numeric values in header
+ * Mar 21 2000	Implement -n to get string value starting with nth token
+ * Apr  5 2000	Reject +- in isnum()
+ * Jun  9 2000	Read keyword values even if no equal sign is present
+ * Sep 20 2000	Ignore linefeed at end of number in isnum()
+ * Oct 23 2000	Fix handling of embedded + or - in isnum()
+ *
+ * Jan 19 2000	Return 0 from isnum(), str2ra(), and str2dec() if string is null
+ * Mar 30 2001	Fix header length finding algorithm in ksearch()
+ * Jul 13 2001	Make val[] static int instead of int; drop unused variables
+ * Sep 12 2001	Read yyyy/mm/dd dates as well as dd/mm/yyyy
+ * Sep 20 2001	Ignore leading spaces in str2dec()
+ * Sep 20 2001	Ignore trailing spaces in isnum()
+ *
+ * Apr  3 2002	Add hgetr8c(), hgeti4c(), and hgetsc() for multiple WCS handling
+ * Apr 26 2002	Fix bug in hgetsc(), hgeti4c(), and hgetr8c() found by Bill Joye
+ * Jun 26 2002	Do not drop leading or trailing spaces in multi-line values
+ * Aug  6 2002	Add strcsrch() and strncsrch() for case-insensitive searches
+ * Aug 30 2002	Fix bug so strcsrch() really is case-insensitive
+ * Oct 20 2003	Add numdec() to return number of decimal places in a string
+ * Dec  9 2003	Fix numdec() to return 0 if no digits after decimal point
+ *
+ * Feb 26 2004	Extract value from keyword=value strings within a keyword value
+ * Apr  9 2004	Use strncsrch() in ksearch() to find differently-cased keywords
+ * Apr 28 2004	Free os2 in strncsrch() only if it is allocated
+ * Jul 13 2004	Accept D, d, E, or e as exponent delimiter in floating points
+ * Aug 30 2004	Change numdec() to accept sexigesimal numbers (:'s)
+ *
+ * Jun 27 2005	Drop unused variables
+ * Aug 30 2005	Adjust code in hlength()
+ *
+ * Jun 20 2006	Initialize uninitialized variables in strnsrch()
+ * Jun 29 2006	Add new subroutine strfix() to clean strings for other uses
+ * Jul 13 2006	Increase maximum number of multiline keywords from 20 to 500
+ *
+ * Jan  4 2007  Declare header, keyword to be const
+ * Jan  4 2007	Change WCS letter from char to char*
+ * Feb 28 2007	If header length is not set in hlength, set it to 0
+ * May 31 2007	Add return value of 3 to isnum() if string has colon(s)
+ * Aug 22 2007	If closing quote not found, make one up
+ *
+ * Nov 12 2009	In strfix(), if drop enclosing parantheses
+ */
diff --git a/Code/src/libwcs/hput.c b/Code/src/libwcs/hput.c
new file mode 100644
index 0000000000000000000000000000000000000000..aa3269ef496de49afdcbaf4854d971bf3c0d4550
--- /dev/null
+++ b/Code/src/libwcs/hput.c
@@ -0,0 +1,1311 @@
+/*** File libwcs/hput.c
+ *** August 22, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1995-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	hput.c (Put FITS Header parameter values)
+ * Purpose:	Implant values for parameters into FITS header string
+ * Subroutine:	hputi4 (hstring,keyword,ival) sets int ival
+ * Subroutine:	hputr4 (hstring,keyword,rval) sets real*4 rval
+ * Subroutine:	hputr8 (hstring,keyword,dval) sets real*8 dval
+ * Subroutine:	hputnr8 (hstring,keyword,ndec,dval) sets real*8 dval
+ * Subroutine:	hputra (hstring,keyword,lval) sets right ascension as string
+ * Subroutine:	hputdec (hstring,keyword,lval) sets declination as string
+ * Subroutine:	hputl  (hstring,keyword,lval) sets logical lval
+ * Subroutine:	hputs  (hstring,keyword,cval) sets character string adding ''
+ * Subroutine:	hputm  (hstring,keyword,cval) sets multi-line character string
+ * Subroutine:	hputc  (hstring,keyword,cval) sets character string cval
+ * Subroutine:	hdel   (hstring,keyword) deletes entry for keyword keyword
+ * Subroutine:	hadd   (hplace,keyword) adds entry for keyword at hplace
+ * Subroutine:	hchange (hstring,keyword1,keyword2) changes keyword for entry
+ * Subroutine:	hputcom (hstring,keyword,comment) sets comment for parameter keyword
+ * Subroutine:	ra2str (out, lstr, ra, ndec) converts RA from degrees to string
+ * Subroutine:	dec2str (out, lstr, dec, ndec) converts Dec from degrees to string
+ * Subroutine:	deg2str (out, lstr, deg, ndec) converts degrees to string
+ * Subroutine:	num2str (out, num, field, ndec) converts number to string
+ * Subroutine:  getltime () returns current local time as ISO-style string
+ * Subroutine:  getutime () returns current UT as ISO-style string
+ */
+#include <sys/time.h>
+#include <string.h>             /* NULL, strlen, strstr, strcpy */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "fitshead.h"
+
+static int verbose=0;	/* Set to 1 to print error messages and other info */
+
+static void fixnegzero();
+
+
+/*  HPUTI4 - Set int keyword = ival in FITS header string */
+
+int
+hputi4 (hstring,keyword,ival)
+
+char *hstring;		/* FITS-style header information in the format
+			   <keyword>= <value> {/ <comment>}
+			   each entry is padded with spaces to 80 characters */
+
+const char *keyword;	/* Name of the variable in header to be returned.
+			   If no line begins with this string, one is created.
+		   	   The first 8 characters of keyword must be unique. */
+int ival;		/* int number */
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    sprintf (value,"%d",ival);
+
+    /* Put value into header string */
+    return (hputc (hstring,keyword,value));
+}
+
+
+/*  HPUTR4 - Set float keyword = rval in FITS header string */
+
+int
+hputr4 (hstring, keyword, rval)
+
+char *hstring;		/* FITS header string */
+const char *keyword;	/* Keyword name */
+const float *rval;	/* float number */
+
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    sprintf (value, "%f", *rval);
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputc (hstring, keyword, value));
+}
+
+
+/*  HPUTR8 - Set double keyword = dval in FITS header string */
+
+int
+hputr8 (hstring, keyword, dval)
+
+char	*hstring;	/* FITS header string */
+const char *keyword;	/* Keyword name */
+const double dval;	/* double number */
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    sprintf (value, "%g", dval);
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputc (hstring, keyword, value));
+}
+
+
+/*  HPUTNR8 - Set double keyword = dval in FITS header string */
+
+int
+hputnr8 (hstring, keyword, ndec, dval)
+
+char	*hstring;	/* FITS header string */
+const char *keyword;	/* Keyword name */
+const int ndec;		/* Number of decimal places to print */
+const double dval;	/* double number */
+{
+    char value[30];
+    char format[8];
+    int i, lval;
+
+    /* Translate value from binary to ASCII */
+    if (ndec < 0) {
+	sprintf (format, "%%.%dg", -ndec);
+	sprintf (value, format, dval);
+	lval = (int) strlen (value);
+	for (i = 0; i < lval; i++)
+	    if (value[i] == 'e') value[i] = 'E';
+	}
+    else {
+	sprintf (format, "%%.%df", ndec);
+	sprintf (value, format, dval);
+	}
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputc (hstring, keyword, value));
+}
+
+
+/*  HPUTRA - Set double keyword = hh:mm:ss.sss in FITS header string */
+
+int
+hputra (hstring, keyword, ra)
+
+char *hstring;		/* FITS header string */
+const char *keyword;	/* Keyword name */
+const double ra;		/* Right ascension in degrees */
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    ra2str (value, 30, ra, 3);
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputs (hstring, keyword, value));
+}
+
+
+/*  HPUTDEC - Set double keyword = dd:mm:ss.sss in FITS header string */
+
+int
+hputdec (hstring, keyword, dec)
+
+char *hstring;		/* FITS header string */
+const char *keyword;	/* Keyword name */
+const double dec;		/* Declination in degrees */
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    dec2str (value, 30, dec, 2);
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputs (hstring, keyword, value));
+}
+
+
+/* FIXNEGZERO -- Drop - sign from beginning of any string which is all zeros */
+
+static void
+fixnegzero (string)
+
+char *string;
+{
+    int i, lstr;
+
+    if (string[0] != '-')
+	return;
+
+    /* Drop out if any non-zero digits in this string */
+    lstr = (int) strlen (string);
+    for (i = 1; i < lstr; i++) {
+	if (string[i] > '0' && string[i] <= '9')
+	    return;
+	if (string[i] == 'd' || string[i] == 'e' || string[i] == ' ')
+	    break;
+	}
+
+    /* Drop - from start of string; overwrite string in place */
+    for (i = 1; i < lstr; i++)
+	string[i-1] = string[i];
+    string[lstr-1] = (char) 0;
+
+    return;
+}
+
+
+
+/*  HPUTL - Set keyword = F if lval=0, else T, in FITS header string */
+
+int
+hputl (hstring, keyword,lval)
+
+char *hstring;		/* FITS header */
+const char *keyword;	/* Keyword name */
+const int lval;		/* logical variable (0=false, else true) */
+{
+    char value[8];
+
+    /* Translate value from binary to ASCII */
+    if (lval)
+	strcpy (value, "T");
+    else
+	strcpy (value, "F");
+
+    /* Put value into header string */
+    return (hputc (hstring,keyword,value));
+}
+
+
+/*  HPUTM - Set multi-line character string in FITS header string */
+/*          return number of keywords written */
+
+int
+hputm (hstring,keyword,cval)
+
+char *hstring;	/* FITS header */
+const char *keyword;	/* Keyword name root (6 characters or less) */
+const char *cval;	/* character string containing the value for variable
+		   keyword.  trailing and leading blanks are removed.  */
+{
+    int lroot, lcv, i, ii, nkw, lkw, lval;
+    int comment = 0;
+    const char *v;
+    char keyroot[8], newkey[12], value[80];
+    char squot = 39;
+
+    /*  If COMMENT or HISTORY, use the same keyword on every line */
+    lkw = (int) strlen (keyword);
+    if (lkw == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
+	strncmp (keyword,"HISTORY",7) == 0))
+	comment = 1;
+
+    /* Set up keyword root, shortening it to 6 characters, if necessary */
+    else {
+	comment = 0;
+	strcpy (keyroot, keyword);
+	lroot = (int) strlen (keyroot);
+	if (lroot > 6) {
+	    keyroot[6] = (char) 0;
+	    lroot = 6;
+	    }
+	}
+
+    /* Write keyword value one line of up to 67 characters at a time */
+    ii = '1';
+    nkw = 0;
+    lcv = (int) strlen (cval);
+    if (!comment) {
+	strcpy (newkey, keyroot);
+	strcat (newkey, "_");
+	newkey[lroot+2] = (char) 0;
+	}
+    v = cval;
+    while (lcv > 0) {
+	if (lcv > 67)
+	    lval = 67;
+	else
+	    lval = lcv;
+	value[0] = squot;
+	for (i = 1; i <= lval; i++)
+	    value[i] = *v++;
+
+	/* Pad short strings to 8 characters */
+	if (lval < 8) {
+	    for (i = lval+1; i < 9; i++)
+		value[i] = ' ';
+	    lval = 8;
+	    }
+	value[lval+1] = squot;
+	value[lval+2] = (char) 0;
+
+	/* Add this line to the header */
+	if (comment)
+	    i = hputc (hstring, keyroot, value);
+	else {
+	    newkey[lroot+1] = ii;
+	    ii++;
+	    i = hputc (hstring, newkey, value);
+	    }
+	if (i != 0) return (i);
+	nkw++;
+	if (lcv > 67)
+	    lcv = lcv - 67;
+	else
+	    break;
+	}
+    return (nkw);
+}
+
+
+/*  HPUTS - Set character string keyword = 'cval' in FITS header string */
+
+int
+hputs (hstring,keyword,cval)
+
+char *hstring;	/* FITS header */
+const char *keyword; /* Keyword name */
+const char *cval; /* character string containing the value for variable
+		   keyword.  trailing and leading blanks are removed.  */
+{
+    char squot = 39;
+    char value[80];
+    int lcval, i, lkeyword;
+
+    /*  If COMMENT or HISTORY, just add it as is */
+    lkeyword = (int) strlen (keyword);
+    if (lkeyword == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
+	strncmp (keyword,"HISTORY",7) == 0))
+	return (hputc (hstring,keyword,cval));
+
+    /*  find length of variable string */
+    lcval = (int) strlen (cval);
+    if (lcval > 67)
+	lcval = 67;
+
+    /* Put single quote at start of string */
+    value[0] = squot;
+    strncpy (&value[1],cval,lcval);
+
+    /* If string is less than eight characters, pad it with spaces */
+    if (lcval < 8) {
+	for (i = lcval; i < 8; i++) {
+	    value[i+1] = ' ';
+	    }
+	lcval = 8;
+	}
+
+    /* Add single quote and null to end of string */
+    value[lcval+1] = squot;
+    value[lcval+2] = (char) 0;
+
+    /* Put value into header string */
+    return (hputc (hstring,keyword,value));
+}
+
+
+/*  HPUTC - Set character string keyword = value in FITS header string */
+/*          Return -1 if error, 0 if OK */
+
+int
+hputc (hstring,keyword,value)
+
+char *hstring;
+const char *keyword;
+const char *value; /* character string containing the value for variable
+		   keyword.  trailing and leading blanks are removed.  */
+{
+    char squot = 39;
+    char line[100];
+    char newcom[50];
+    char *vp, *v1, *v2, *q1, *q2, *c1, *ve;
+    int lkeyword, lcom, lval, lc, lv1, lhead, lblank, ln, nc, i;
+
+    /* Find length of keyword, value, and header */
+    lkeyword = (int) strlen (keyword);
+    lval = (int) strlen (value);
+    lhead = gethlength (hstring);
+
+    /*  If COMMENT or HISTORY, always add it just before the END */
+    if (lkeyword == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
+	strncmp (keyword,"HISTORY",7) == 0)) {
+	
+	/* First look for blank lines before END */
+        v1 = blsearch (hstring, "END");
+    
+	/*  Otherwise, create a space for it at the end of the header */
+	if (v1 == NULL) {
+
+	    /* Find end of header */
+	    v1 = ksearch (hstring,"END");
+
+	    /* Align pointer at start of 80-character line */
+	    lc = v1 - hstring;
+	    ln = lc / 80;
+	    nc = ln * 80;
+	    v1 = hstring + nc;
+	    v2 = v1 + 80;
+
+	    /* If header length is exceeded, return error code */
+	    if (v2 - hstring > lhead) {
+		return (-1);
+		}
+
+	    /* Move END down 80 characters */
+	    strncpy (v2, v1, 80);
+	    }
+	else
+	    v2 = v1 + 80;
+
+	/* Insert keyword */
+	strncpy (v1,keyword,7);
+
+	/* Pad with spaces */
+	for (vp = v1+lkeyword; vp < v2; vp++)
+	    *vp = ' ';
+
+	if (lval > 71)
+	    lv1 = 71;
+	else
+	    lv1 = lval;
+
+	/* Insert comment */
+	strncpy (v1+9,value,lv1);
+	return (0);
+	}
+
+    /* Otherwise search for keyword */
+    else
+	v1 = ksearch (hstring,keyword);
+
+    /*  If parameter is not found, find a place to put it */
+    if (v1 == NULL) {
+	
+	/* First look for blank lines before END */
+        v1 = blsearch (hstring, "END");
+    
+	/*  Otherwise, create a space for it at the end of the header */
+	if (v1 == NULL) {
+	    ve = ksearch (hstring,"END");
+	    v1 = ve;
+
+	    /* Align pointer at start of 80-character line */
+	    lc = v1 - hstring;
+	    ln = lc / 80;
+	    nc = ln * 80;
+	    v1 = hstring + nc;
+	    v2 = v1 + 80;
+
+	    /* If header length is exceeded, return error code */
+	    if (v2 - hstring > lhead) {
+		return (-1);
+		}
+
+	    strncpy (v2, ve, 80);
+	    }
+	else
+	    v2 = v1 + 80;
+	lcom = 0;
+	newcom[0] = 0;
+	}
+
+    /*  Otherwise, extract the entry for this keyword from the header */
+    else {
+
+	/* Align pointer at start of 80-character line */
+	lc = v1 - hstring;
+	ln = lc / 80;
+	nc = ln * 80;
+	v1 = hstring + nc;
+	v2 = v1 + 80;
+
+	strncpy (line, v1, 80);
+	line[80] = 0;
+	v2 = v1 + 80;
+
+	/*  check for quoted value */
+	q1 = strchr (line, squot);
+	if (q1 != NULL) {
+	    q2 = strchr (q1+1,squot);
+	    if (q2 != NULL)
+		c1 = strchr (q2,'/');
+	    else
+		c1 = strrchr (line+79,'/');
+	    }
+	else
+	    c1 = strchr (line,'/');
+
+	/*  extract comment and discount trailing spaces */
+	if (c1 != NULL) {
+	    lcom = 80 - (c1 + 2 - line);
+	    strncpy (newcom, c1+2, lcom);
+	    vp = newcom + lcom - 1;
+	    while (vp-- > newcom && *vp == ' ')
+		lcom--;
+	    }
+	else {
+	    newcom[0] = 0;
+	    lcom = 0;
+	    }
+	}
+
+    /* Fill new entry with spaces */
+    for (vp = v1; vp < v2; vp++)
+	*vp = ' ';
+
+    /*  Copy keyword to new entry */
+    strncpy (v1, keyword, lkeyword);
+
+    /*  Add parameter value in the appropriate place */
+    vp = v1 + 8;
+    *vp = '=';
+    vp = v1 + 9;
+    *vp = ' ';
+    vp = vp + 1;
+    if (*value == squot) {
+	strncpy (vp, value, lval);
+	if (lval+12 > 31)
+	    lc = lval + 12;
+	else
+	    lc = 30;
+	}
+    else {
+	vp = v1 + 30 - lval;
+	strncpy (vp, value, lval);
+	lc = 30;
+	}
+
+    /* Add comment in the appropriate place */
+	if (lcom > 0) {
+	    if (lc+2+lcom > 80)
+		lcom = 77 - lc;
+	    vp = v1 + lc;     /* Jul 16 1997: was vp = v1 + lc * 2 */
+	    *vp++ = ' ';
+	    *vp++ = '/';
+	    *vp++ = ' ';
+	    lblank = v2 - vp;
+	    for (i = 0; i < lblank; i++)
+		vp[i] = ' ';
+	    if (lcom > lblank)
+		lcom = lblank;
+	    strncpy (vp, newcom, lcom);
+	    }
+
+	if (verbose) {
+	    if (lcom > 0)
+		fprintf (stderr,"HPUT: %s  = %s  / %s\n",keyword, value, newcom);
+	    else
+		fprintf (stderr,"HPUT: %s  = %s\n",keyword, value);
+	    }
+
+	return (0);
+}
+
+
+/*  HPUTCOM - Set comment for keyword or on line in FITS header string */
+
+int
+hputcom (hstring,keyword,comment)
+
+  char *hstring;
+  const char *keyword;
+  const char *comment;
+{
+    char squot, slash, space;
+    char line[100];
+    int lkeyword, lcom, lhead, i, lblank, ln, nc, lc;
+    char *vp, *v1, *v2, *c0, *c1, *q1, *q2;
+
+    squot = (char) 39;
+    slash = (char) 47;
+    space = (char) 32;
+
+    /*  Find length of variable name */
+    lkeyword = (int) strlen (keyword);
+    lhead = gethlength (hstring);
+    lcom = (int) strlen (comment);
+
+    /*  If COMMENT or HISTORY, always add it just before the END */
+    if (lkeyword == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
+	strncmp (keyword,"HISTORY",7) == 0)) {
+
+	/* Find end of header */
+	v1 = ksearch (hstring,"END");
+
+	/* Align pointer at start of 80-character line */
+	lc = v1 - hstring;
+	ln = lc / 80;
+	nc = ln * 80;
+	v1 = hstring + nc;
+	v2 = v1 + 80;
+
+	/* If header length is exceeded, return error code */
+	if (v2 - hstring > lhead) {
+	    return (-1);
+	    }
+
+	/* Move END down 80 characters */
+	strncpy (v2, v1, 80);
+
+	/*  blank out new line and insert keyword */
+	for (vp = v1; vp < v2; vp++)
+	    *vp = ' ';
+	strncpy (v1, keyword, lkeyword);
+	c0 = v1 + lkeyword;
+	}
+
+    /* Search header string for variable name */
+    else {
+	v1 = ksearch (hstring,keyword);
+
+	/* If parameter is not found, return without doing anything */
+	if (v1 == NULL) {
+	    if (verbose)
+		fprintf (stderr,"HPUTCOM: %s not found\n",keyword);
+	    return (-1);
+	    }
+
+	/* Align pointer at start of 80-character line */
+	lc = v1 - hstring;
+	ln = lc / 80;
+	nc = ln * 80;
+	v1 = hstring + nc;
+	v2 = v1 + 80;
+
+	/* Extract entry for this variable from the header */
+	strncpy (line, v1, 80);
+	line[80] = '\0'; /* Null-terminate line before strchr call */
+
+	/* check for quoted value */
+	q1 = strchr (line,squot);
+	c1 = strchr (line,slash);
+	if (q1 != NULL) {
+	    if (c1 != NULL && q1 < c1) {
+		q2 = strchr (q1+1, squot);
+		if (q2 == NULL) {
+		    q2 = c1 - 1;
+		    while (*q2 == space)
+			q2--;
+		    q2++;
+		    }
+		else if (c1 < q2)
+		    c1 = strchr (q2, slash);
+		}
+	    else if (c1 == NULL) {
+		q2 = strchr (q1+1, squot);
+		if (q2 == NULL) {
+		    q2 = line + 79;
+		    while (*q2 == space)
+			q2--;
+		    q2++;
+		    }
+		}
+	    else
+		q1 = NULL;
+	    }
+
+	else
+	    q2 = NULL;
+
+	if (c1 != NULL)
+	    c0 = v1 + (c1 - line) - 1;
+	else if (q2 == NULL || q2-line < 30)
+	    c0 = v1 + 30;
+	else
+	    c0 = v1 + (q2 - line) + 1; /* allan: 1997-09-30, was c0=q2+2 */
+
+	/* If comment will not fit at all, return */
+	if (c0 - v1 > 77)
+	    return (-1);
+	strncpy (c0, " / ",3);
+	}
+
+    /* Create new entry */
+    if (lcom > 0) {
+	c1 = c0 + 3;
+	lblank = v1 + 79 - c1;
+	if (lcom > lblank)
+	    lcom = lblank;
+	for (i = 0; i < lblank; i++)
+	    c1[i] = ' ';
+	strncpy (c1, comment, lcom);
+	}
+
+    if (verbose) {
+	fprintf (stderr,"HPUTCOM: %s / %s\n",keyword,comment);
+	}
+    return (0);
+}
+
+
+static int leaveblank = 0;	/* If 1, leave blank line when deleting */
+void
+setleaveblank (lb)
+int lb; { leaveblank = lb; return; }
+
+static int headshrink=1; /* Set to 1 to drop line after deleting keyword */
+void
+setheadshrink (hsh)
+int hsh;
+{headshrink = hsh; return;}
+
+/*  HDEL - Set character string keyword = value in FITS header string
+ *	    returns 1 if entry deleted, else 0
+ */
+
+int
+hdel (hstring,keyword)
+
+char *hstring;		/* FITS header */
+const char *keyword;	/* Keyword of entry to be deleted */
+{
+    char *v, *v1, *v2, *ve;
+
+    /* Search for keyword */
+    v1 = ksearch (hstring,keyword);
+
+    /*  If keyword is not found, return header unchanged */
+    if (v1 == NULL) {
+	return (0);
+	}
+
+    /*  Find end of header */
+    ve = ksearch (hstring,"END");
+
+    /* If headshrink is 0, leave END where it is */
+    if (!leaveblank && !headshrink)
+	ve = ve - 80;
+
+    /* Cover deleted keyword line with spaces */
+    if (leaveblank) {
+	v2 = v1 + 80;
+	for (v = ve; v < v2; v++)
+	    *v = ' ';
+	}
+
+    /* Shift rest of header up one line */
+    else {
+	for (v = v1; v < ve; v = v + 80) {
+	    v2 = v + 80;
+	    strncpy (v, v2, 80);
+	    }
+
+	/* Cover former last line with spaces */
+	v2 = ve + 80;
+	for (v = ve; v < v2; v++)
+	    *v = ' ';
+	}
+
+    return (1);
+}
+
+
+/*  HADD - Add character string keyword = value to FITS header string
+ *	    returns 1 if entry added, else 0
+ *	    Call hputx() to put value into entry
+ */
+
+int
+hadd (hplace, keyword)
+
+char *hplace;		/* FITS header position for new keyword */
+const char *keyword;	/* Keyword of entry to be deleted */
+{
+    char *v, *v1, *v2, *ve;
+    int i, lkey;
+
+    /*  Find end of header */
+    ve = ksearch (hplace,"END");
+
+    /*  If END is not found, return header unchanged */
+    if (ve == NULL) {
+	return (0);
+	}
+
+    v1 = hplace;
+
+    /* Shift rest of header down one line */
+    /* limit bug found by Paolo Montegriffo fixed 2000-04-19 */
+    for (v = ve; v >= v1; v = v - 80) {
+	v2 = v + 80;
+	strncpy (v2, v, 80);
+	}
+
+    /* Cover former first line with new keyword */
+    lkey = (int) strlen (keyword);
+    strncpy (hplace, keyword, lkey);
+    if (lkey < 8) {
+	for (i = lkey; i < 8; i++)
+	    hplace[i] = ' ';
+	hplace[8] = '=';
+	}
+    for (i = 9; i < 80; i++)
+	hplace[i] = ' ';
+
+    return (1);
+}
+
+
+/*  HCHANGE - Changes keyword for entry from keyword1 to keyword2 in FITS
+              header string
+ *	      returns 1 if entry changed, else 0
+ */
+
+int
+hchange (hstring, keyword1, keyword2)
+
+char *hstring;		/* FITS header */
+const char *keyword1;	/* Keyword to be changed */
+const char *keyword2;	/* New keyword name */
+{
+    char *v, *v1;
+    const char *v2;
+    int lv2, i;
+
+    /* Search for keyword */
+    v1 = ksearch (hstring,keyword1);
+
+    /*  If keyword is not found, return header unchanged */
+    if (!v1)
+	return (0);
+
+    else {
+	lv2 = (int) strlen (keyword2);
+	v = v1;
+	v2 = keyword2;
+	for (i = 0; i < 8; i++) {
+	    if (i < lv2)
+		v[i] = v2[i];
+	    else
+		v[i] = ' ';
+	    }
+	}
+
+    return (1);
+}
+
+
+/* Write the right ascension ra in sexagesimal format into string*/
+
+void
+ra2str (string, lstr, ra, ndec)
+
+char	*string;	/* Character string (returned) */
+int	lstr;		/* Maximum number of characters in string */
+double	ra;		/* Right ascension in degrees */
+int	ndec;		/* Number of decimal places in seconds */
+
+{
+    double a,b;
+    double seconds;
+    char tstring[64];
+    int hours;
+    int minutes;
+    int isec, ltstr;
+    double dsgn;
+
+    /* Keep RA between 0 and 360 */
+    if (ra < 0.0 ) {
+	ra = -ra;
+	dsgn = -1.0;
+	}
+    else
+	dsgn = 1.0;
+    ra = fmod(ra, 360.0);
+    ra *= dsgn;
+    if (ra < 0.0)
+	ra = ra + 360.0;
+
+    a = ra / 15.0;
+
+    /* Convert to hours */
+    hours = (int) a;
+
+    /* Compute minutes */
+    b =  (a - (double)hours) * 60.0;
+    minutes = (int) b;
+
+    /* Compute seconds */
+    seconds = (b - (double)minutes) * 60.0;
+
+    if (ndec > 5) {
+	if (seconds > 59.999999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%09.6f",hours,minutes,seconds);
+	}
+    else if (ndec > 4) {
+	if (seconds > 59.99999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%08.5f",hours,minutes,seconds);
+	}
+    else if (ndec > 3) {
+	if (seconds > 59.9999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%07.4f",hours,minutes,seconds);
+	}
+    else if (ndec > 2) {
+	if (seconds > 59.999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%06.3f",hours,minutes,seconds);
+	}
+    else if (ndec > 1) {
+	if (seconds > 59.99) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%05.2f",hours,minutes,seconds);
+	}
+    else if (ndec > 0) {
+	if (seconds > 59.9) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%04.1f",hours,minutes,seconds);
+	}
+    else {
+	isec = (int)(seconds + 0.5);
+	if (isec > 59) {
+	    isec = 0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%02d",hours,minutes,isec);
+	}
+
+    /* Move formatted string to returned string */
+    ltstr = (int) strlen (tstring);
+    if (ltstr < lstr-1)
+	strcpy (string, tstring);
+    else {
+	strncpy (string, tstring, lstr-1);
+	string[lstr-1] = 0;
+	}
+    return;
+}
+
+
+/* Write the variable a in sexagesimal format into string */
+
+void
+dec2str (string, lstr, dec, ndec)
+
+char	*string;	/* Character string (returned) */
+int	lstr;		/* Maximum number of characters in string */
+double	dec;		/* Declination in degrees */
+int	ndec;		/* Number of decimal places in arcseconds */
+
+{
+    double a, b, dsgn, deg1;
+    double seconds;
+    char sign;
+    int degrees;
+    int minutes;
+    int isec, ltstr;
+    char tstring[64];
+
+    /* Keep angle between -180 and 360 degrees */
+    deg1 = dec;
+    if (deg1 < 0.0 ) {
+	deg1 = -deg1;
+	dsgn = -1.0;
+	}
+    else
+	dsgn = 1.0;
+    deg1 = fmod(deg1, 360.0);
+    deg1 *= dsgn;
+    if (deg1 <= -180.0)
+	deg1 = deg1 + 360.0;
+
+    a = deg1;
+
+    /* Set sign and do all the rest with a positive */
+    if (a < 0) {
+	sign = '-';
+	a = -a;
+	}
+    else
+	sign = '+';
+
+    /* Convert to degrees */
+    degrees = (int) a;
+
+    /* Compute minutes */
+    b =  (a - (double)degrees) * 60.0;
+    minutes = (int) b;
+
+    /* Compute seconds */
+    seconds = (b - (double)minutes) * 60.0;
+
+    if (ndec > 5) {
+	if (seconds > 59.999999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%09.6f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 4) {
+	if (seconds > 59.99999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%08.5f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 3) {
+	if (seconds > 59.9999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%07.4f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 2) {
+	if (seconds > 59.999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%06.3f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 1) {
+	if (seconds > 59.99) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%05.2f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 0) {
+	if (seconds > 59.9) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%04.1f",sign,degrees,minutes,seconds);
+	}
+    else {
+	isec = (int)(seconds + 0.5);
+	if (isec > 59) {
+	    isec = 0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%02d",sign,degrees,minutes,isec);
+	}
+
+    /* Move formatted string to returned string */
+    ltstr = (int) strlen (tstring);
+    if (ltstr < lstr-1)
+	strcpy (string, tstring);
+    else {
+	strncpy (string, tstring, lstr-1);
+	string[lstr-1] = 0;
+	}
+   return;
+}
+
+
+/* Write the angle a in decimal format into string */
+
+void
+deg2str (string, lstr, deg, ndec)
+
+char	*string;	/* Character string (returned) */
+int	lstr;		/* Maximum number of characters in string */
+double	deg;		/* Angle in degrees */
+int	ndec;		/* Number of decimal places in degree string */
+
+{
+    char degform[8];
+    int field, ltstr;
+    char tstring[64];
+    double deg1;
+    double dsgn;
+
+    /* Keep angle between -180 and 360 degrees */
+    deg1 = deg;
+    if (deg1 < 0.0 ) {
+	deg1 = -deg1;
+	dsgn = -1.0;
+	}
+    else
+	dsgn = 1.0;
+    deg1 = fmod(deg1, 360.0);
+    deg1 *= dsgn;
+    if (deg1 <= -180.0)
+	deg1 = deg1 + 360.0;
+
+    /* Write angle to string, adding 4 digits to number of decimal places */
+    field = ndec + 4;
+    if (ndec > 0) {
+	sprintf (degform, "%%%d.%df", field, ndec);
+	sprintf (tstring, degform, deg1);
+	}
+    else {
+	sprintf (degform, "%%%4d", field);
+	sprintf (tstring, degform, (int)deg1);
+	}
+
+    /* Move formatted string to returned string */
+    ltstr = (int) strlen (tstring);
+    if (ltstr < lstr-1)
+	strcpy (string, tstring);
+    else {
+	strncpy (string, tstring, lstr-1);
+	string[lstr-1] = 0;
+	}
+    return;
+}
+
+
+/* Write the variable a in decimal format into field-character string  */
+
+void
+num2str (string, num, field, ndec)
+
+char	*string;	/* Character string (returned) */
+double	num;		/* Number */
+int	field;		/* Number of characters in output field (0=any) */
+int	ndec;		/* Number of decimal places in degree string */
+
+{
+    char numform[8];
+
+    if (field > 0) {
+	if (ndec > 0) {
+	    sprintf (numform, "%%%d.%df", field, ndec);
+	    sprintf (string, numform, num);
+	    }
+	else {
+	    sprintf (numform, "%%%dd", field);
+	    sprintf (string, numform, (int)num);
+	    }
+	}
+    else {
+	if (ndec > 0) {
+	    sprintf (numform, "%%.%df", ndec);
+	    sprintf (string, numform, num);
+	    }
+	else {
+	    sprintf (string, "%d", (int)num);
+	    }
+	}
+    return;
+}
+
+/* Dec 14 1995	Original subroutines
+
+ * Feb  5 1996	Added HDEL to delete keyword entry from FITS header
+ * Feb  7 1996	Add EOS to LINE in HPUTC
+ * Feb 21 1996	Add RA2STR and DEC2STR string routines
+ * Jul 19 1996	Add HPUTRA and HPUTDEC
+ * Jul 22 1996	Add HCHANGE to change keywords
+ * Aug  5 1996	Add HPUTNR8 to save specific number of decimal places
+ * Oct 15 1996	Fix spelling
+ * Nov  1 1996	Add DEG2STR to set specific number of decimal places
+ * Nov  1 1996	Allow DEC2STR to handle upt to 6 decimal places
+ *
+ * Mar 20 1997	Fix format error in DEG2STR
+ * Jul  7 1997	Fix 2 errors in HPUTCOM found by Allan Brighton
+ * Jul 16 1997	Fix error in HPUTC found by Allan Brighton
+ * Jul 17 1997	Fix error in HPUTC found by Allan Brighton
+ * Sep 30 1997	Fix error in HPUTCOM found by Allan Brighton
+ * Dec 15 1997	Fix minor bugs after lint
+ * Dec 31 1997	Always put two hour digits in RA2STR
+ *
+ * Feb 25 1998	Add HADD to insert keywords at specific locations
+ * Mar 27 1998	If n is negative, write g format in HPUTNR8()
+ * Apr 24 1998	Add NUM2STR() for easy output formatting
+ * Apr 30 1998	Use BLSEARCH() to overwrite blank lines before END
+ * May 27 1998	Keep Dec between -90 and +90 in DEC2STR()
+ * May 28 1998	Keep RA between 0 and 360 in RA2STR()
+ * Jun  2 1998	Fix bug when filling in blank lines before END
+ * Jun 24 1998	Add string length to ra2str(), dec2str(), and deg2str()
+ * Jun 25 1998	Make string converstion subroutines more robust
+ * Aug 31 1998	Add getltime() and getutime()
+ * Sep 28 1998	Null-terminate comment in HPUTCOM (Allan Brighton)
+ * Oct  1 1998	Change clock declaration in getltime() from int (Allan Brighton)
+ *
+ * Jan 28 1999	Fix bug to avoid writing HISTORY or COMMENT past 80 characters
+ * Jul 14 1999	Pad string in hputs() to minimum of 8 characters
+ * Aug 16 1999	Keep angle between -180 and +360 in dec2str()
+ * Oct  6 1999	Reallocate header buffer if it is too small in hputc()
+ * Oct 14 1999	Do not reallocate header; return error if not successful
+ *
+ * Mar  2 2000	Do not add quotes if adding HISTORY or COMMENT with hputs()
+ * Mar 22 2000	Move getutime() and getltime() to dateutil.c
+ * Mar 27 2000	Add hputm() for muti-line keywords
+ * Mar 27 2000	Fix bug testing for space to fit comment in hputcom()
+ * Apr 19 2000	Fix bug in hadd() which overwrote line
+ * Jun  2 2000	Dropped unused variable lv in hputm() after lint
+ * Jul 20 2000	Drop unused variables blank and i in hputc()
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Jan 18 2001	Drop declaration of blsearch(); it is in fitshead.h
+ *
+ * Jan  4 2002	Fix placement of comments
+ *
+ * Jul  1 2004	Add headshrink to optionally keep blank lines in header
+ * Sep  3 2004	Fix bug so comments are not pushed onto next line if long value
+ * Sep 16 2004	Add fixnegzero() to avoid putting signed zero values in header
+ *
+ * May 22 2006	Add option to leave blank line when deleting a keyword
+ * Jun 15 2006	Fix comment alignment in hputc() and hputcom()
+ * Jun 20 2006	Initialized uninitialized variables in hputm() and hputcom()
+ *
+ * Jan  4 2007	Declare keyword to be const
+ * Jan  4 2007	Drop unused subroutine hputi2()
+ * Jan  5 2007	Drop ksearch() declarations; it is now in fitshead.h
+ * Jan 16 2007	Fix bugs in ra2str() and dec2str() so ndec=0 works
+ * Aug 20 2007	Fix bug so comments after quoted keywords work
+ * Aug 22 2007	If closing quote not found, make one up
+ */
diff --git a/Code/src/libwcs/hput1.c b/Code/src/libwcs/hput1.c
new file mode 100644
index 0000000000000000000000000000000000000000..93a3db34b777a34f9782436211a79e9cd5175a3e
--- /dev/null
+++ b/Code/src/libwcs/hput1.c
@@ -0,0 +1,1278 @@
+/*** File libwcs/hput.c
+ *** August 20, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1995-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	hput.c (Put FITS Header parameter values)
+ * Purpose:	Implant values for parameters into FITS header string
+ * Subroutine:	hputi4 (hstring,keyword,ival) sets int ival
+ * Subroutine:	hputr4 (hstring,keyword,rval) sets real*4 rval
+ * Subroutine:	hputr8 (hstring,keyword,dval) sets real*8 dval
+ * Subroutine:	hputnr8 (hstring,keyword,ndec,dval) sets real*8 dval
+ * Subroutine:	hputra (hstring,keyword,lval) sets right ascension as string
+ * Subroutine:	hputdec (hstring,keyword,lval) sets declination as string
+ * Subroutine:	hputl  (hstring,keyword,lval) sets logical lval
+ * Subroutine:	hputs  (hstring,keyword,cval) sets character string adding ''
+ * Subroutine:	hputm  (hstring,keyword,cval) sets multi-line character string
+ * Subroutine:	hputc  (hstring,keyword,cval) sets character string cval
+ * Subroutine:	hdel   (hstring,keyword) deletes entry for keyword keyword
+ * Subroutine:	hadd   (hplace,keyword) adds entry for keyword at hplace
+ * Subroutine:	hchange (hstring,keyword1,keyword2) changes keyword for entry
+ * Subroutine:	hputcom (hstring,keyword,comment) sets comment for parameter keyword
+ * Subroutine:	ra2str (out, lstr, ra, ndec) converts RA from degrees to string
+ * Subroutine:	dec2str (out, lstr, dec, ndec) converts Dec from degrees to string
+ * Subroutine:	deg2str (out, lstr, deg, ndec) converts degrees to string
+ * Subroutine:	num2str (out, num, field, ndec) converts number to string
+ * Subroutine:  getltime () returns current local time as ISO-style string
+ * Subroutine:  getutime () returns current UT as ISO-style string
+ */
+#include <sys/time.h>
+#include <string.h>             /* NULL, strlen, strstr, strcpy */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "fitshead.h"
+
+static int verbose=0;	/* Set to 1 to print error messages and other info */
+
+static void fixnegzero();
+
+
+/*  HPUTI4 - Set int keyword = ival in FITS header string */
+
+int
+hputi4 (hstring,keyword,ival)
+
+char *hstring;		/* FITS-style header information in the format
+			   <keyword>= <value> {/ <comment>}
+			   each entry is padded with spaces to 80 characters */
+
+const char *keyword;	/* Name of the variable in header to be returned.
+			   If no line begins with this string, one is created.
+		   	   The first 8 characters of keyword must be unique. */
+int ival;		/* int number */
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    sprintf (value,"%d",ival);
+
+    /* Put value into header string */
+    return (hputc (hstring,keyword,value));
+}
+
+
+/*  HPUTR4 - Set float keyword = rval in FITS header string */
+
+int
+hputr4 (hstring, keyword, rval)
+
+char *hstring;		/* FITS header string */
+const char *keyword;	/* Keyword name */
+const float *rval;	/* float number */
+
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    sprintf (value, "%f", *rval);
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputc (hstring, keyword, value));
+}
+
+
+/*  HPUTR8 - Set double keyword = dval in FITS header string */
+
+int
+hputr8 (hstring, keyword, dval)
+
+char	*hstring;	/* FITS header string */
+const char *keyword;	/* Keyword name */
+const double dval;	/* double number */
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    sprintf (value, "%g", dval);
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputc (hstring, keyword, value));
+}
+
+
+/*  HPUTNR8 - Set double keyword = dval in FITS header string */
+
+int
+hputnr8 (hstring, keyword, ndec, dval)
+
+char	*hstring;	/* FITS header string */
+const char *keyword;	/* Keyword name */
+const int ndec;		/* Number of decimal places to print */
+const double dval;	/* double number */
+{
+    char value[30];
+    char format[8];
+    int i, lval;
+
+    /* Translate value from binary to ASCII */
+    if (ndec < 0) {
+	sprintf (format, "%%.%dg", -ndec);
+	sprintf (value, format, dval);
+	lval = (int) strlen (value);
+	for (i = 0; i < lval; i++)
+	    if (value[i] == 'e') value[i] = 'E';
+	}
+    else {
+	sprintf (format, "%%.%df", ndec);
+	sprintf (value, format, dval);
+	}
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputc (hstring, keyword, value));
+}
+
+
+/*  HPUTRA - Set double keyword = hh:mm:ss.sss in FITS header string */
+
+int
+hputra (hstring, keyword, ra)
+
+char *hstring;		/* FITS header string */
+const char *keyword;	/* Keyword name */
+const double ra;		/* Right ascension in degrees */
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    ra2str (value, 30, ra, 3);
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputs (hstring, keyword, value));
+}
+
+
+/*  HPUTDEC - Set double keyword = dd:mm:ss.sss in FITS header string */
+
+int
+hputdec (hstring, keyword, dec)
+
+char *hstring;		/* FITS header string */
+const char *keyword;	/* Keyword name */
+const double dec;		/* Declination in degrees */
+{
+    char value[30];
+
+    /* Translate value from binary to ASCII */
+    dec2str (value, 30, dec, 2);
+
+    /* Remove sign if string is -0 or extension thereof */
+    fixnegzero (value);
+
+    /* Put value into header string */
+    return (hputs (hstring, keyword, value));
+}
+
+
+/* FIXNEGZERO -- Drop - sign from beginning of any string which is all zeros */
+
+static void
+fixnegzero (string)
+
+char *string;
+{
+    int i, lstr;
+
+    if (string[0] != '-')
+	return;
+
+    /* Drop out if any non-zero digits in this string */
+    lstr = (int) strlen (string);
+    for (i = 1; i < lstr; i++) {
+	if (string[i] > '0' && string[i] <= '9')
+	    return;
+	if (string[i] == 'd' || string[i] == 'e' || string[i] == ' ')
+	    break;
+	}
+
+    /* Drop - from start of string; overwrite string in place */
+    for (i = 1; i < lstr; i++)
+	string[i-1] = string[i];
+    string[lstr-1] = (char) 0;
+
+    return;
+}
+
+
+
+/*  HPUTL - Set keyword = F if lval=0, else T, in FITS header string */
+
+int
+hputl (hstring, keyword,lval)
+
+char *hstring;		/* FITS header */
+const char *keyword;	/* Keyword name */
+const int lval;		/* logical variable (0=false, else true) */
+{
+    char value[8];
+
+    /* Translate value from binary to ASCII */
+    if (lval)
+	strcpy (value, "T");
+    else
+	strcpy (value, "F");
+
+    /* Put value into header string */
+    return (hputc (hstring,keyword,value));
+}
+
+
+/*  HPUTM - Set multi-line character string in FITS header string */
+/*          return number of keywords written */
+
+int
+hputm (hstring,keyword,cval)
+
+char *hstring;	/* FITS header */
+const char *keyword;	/* Keyword name root (6 characters or less) */
+const char *cval;	/* character string containing the value for variable
+		   keyword.  trailing and leading blanks are removed.  */
+{
+    int lroot, lcv, i, ii, nkw, lkw, lval;
+    int comment = 0;
+    const char *v;
+    char keyroot[8], newkey[12], value[80];
+    char squot = 39;
+
+    /*  If COMMENT or HISTORY, use the same keyword on every line */
+    lkw = (int) strlen (keyword);
+    if (lkw == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
+	strncmp (keyword,"HISTORY",7) == 0))
+	comment = 1;
+
+    /* Set up keyword root, shortening it to 6 characters, if necessary */
+    else {
+	comment = 0;
+	strcpy (keyroot, keyword);
+	lroot = (int) strlen (keyroot);
+	if (lroot > 6) {
+	    keyroot[6] = (char) 0;
+	    lroot = 6;
+	    }
+	}
+
+    /* Write keyword value one line of up to 67 characters at a time */
+    ii = '1';
+    nkw = 0;
+    lcv = (int) strlen (cval);
+    if (!comment) {
+	strcpy (newkey, keyroot);
+	strcat (newkey, "_");
+	newkey[lroot+2] = (char) 0;
+	}
+    v = cval;
+    while (lcv > 0) {
+	if (lcv > 67)
+	    lval = 67;
+	else
+	    lval = lcv;
+	value[0] = squot;
+	for (i = 1; i <= lval; i++)
+	    value[i] = *v++;
+
+	/* Pad short strings to 8 characters */
+	if (lval < 8) {
+	    for (i = lval+1; i < 9; i++)
+		value[i] = ' ';
+	    lval = 8;
+	    }
+	value[lval+1] = squot;
+	value[lval+2] = (char) 0;
+
+	/* Add this line to the header */
+	if (comment)
+	    i = hputc (hstring, keyroot, value);
+	else {
+	    newkey[lroot+1] = ii;
+	    ii++;
+	    i = hputc (hstring, newkey, value);
+	    }
+	if (i != 0) return (i);
+	nkw++;
+	if (lcv > 67)
+	    lcv = lcv - 67;
+	else
+	    break;
+	}
+    return (nkw);
+}
+
+
+/*  HPUTS - Set character string keyword = 'cval' in FITS header string */
+
+int
+hputs (hstring,keyword,cval)
+
+char *hstring;	/* FITS header */
+const char *keyword; /* Keyword name */
+const char *cval; /* character string containing the value for variable
+		   keyword.  trailing and leading blanks are removed.  */
+{
+    char squot = 39;
+    char value[80];
+    int lcval, i, lkeyword;
+
+    /*  If COMMENT or HISTORY, just add it as is */
+    lkeyword = (int) strlen (keyword);
+    if (lkeyword == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
+	strncmp (keyword,"HISTORY",7) == 0))
+	return (hputc (hstring,keyword,cval));
+
+    /*  find length of variable string */
+    lcval = (int) strlen (cval);
+    if (lcval > 67)
+	lcval = 67;
+
+    /* Put single quote at start of string */
+    value[0] = squot;
+    strncpy (&value[1],cval,lcval);
+
+    /* If string is less than eight characters, pad it with spaces */
+    if (lcval < 8) {
+	for (i = lcval; i < 8; i++) {
+	    value[i+1] = ' ';
+	    }
+	lcval = 8;
+	}
+
+    /* Add single quote and null to end of string */
+    value[lcval+1] = squot;
+    value[lcval+2] = (char) 0;
+
+    /* Put value into header string */
+    return (hputc (hstring,keyword,value));
+}
+
+
+/*  HPUTC - Set character string keyword = value in FITS header string */
+/*          Return -1 if error, 0 if OK */
+
+int
+hputc (hstring,keyword,value)
+
+char *hstring;
+const char *keyword;
+const char *value; /* character string containing the value for variable
+		   keyword.  trailing and leading blanks are removed.  */
+{
+    char squot = 39;
+    char line[100];
+    char newcom[50];
+    char *vp, *v1, *v2, *q1, *q2, *c1, *ve;
+    int lkeyword, lcom, lval, lc, lv1, lhead, lblank, ln, nc, i;
+
+    /* Find length of keyword, value, and header */
+    lkeyword = (int) strlen (keyword);
+    lval = (int) strlen (value);
+    lhead = gethlength (hstring);
+
+    /*  If COMMENT or HISTORY, always add it just before the END */
+    if (lkeyword == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
+	strncmp (keyword,"HISTORY",7) == 0)) {
+	
+	/* First look for blank lines before END */
+        v1 = blsearch (hstring, "END");
+    
+	/*  Otherwise, create a space for it at the end of the header */
+	if (v1 == NULL) {
+
+	    /* Find end of header */
+	    v1 = ksearch (hstring,"END");
+
+	    /* Align pointer at start of 80-character line */
+	    lc = v1 - hstring;
+	    ln = lc / 80;
+	    nc = ln * 80;
+	    v1 = hstring + nc;
+	    v2 = v1 + 80;
+
+	    /* If header length is exceeded, return error code */
+	    if (v2 - hstring > lhead) {
+		return (-1);
+		}
+
+	    /* Move END down 80 characters */
+	    strncpy (v2, v1, 80);
+	    }
+	else
+	    v2 = v1 + 80;
+
+	/* Insert keyword */
+	strncpy (v1,keyword,7);
+
+	/* Pad with spaces */
+	for (vp = v1+lkeyword; vp < v2; vp++)
+	    *vp = ' ';
+
+	if (lval > 71)
+	    lv1 = 71;
+	else
+	    lv1 = lval;
+
+	/* Insert comment */
+	strncpy (v1+9,value,lv1);
+	return (0);
+	}
+
+    /* Otherwise search for keyword */
+    else
+	v1 = ksearch (hstring,keyword);
+
+    /*  If parameter is not found, find a place to put it */
+    if (v1 == NULL) {
+	
+	/* First look for blank lines before END */
+        v1 = blsearch (hstring, "END");
+    
+	/*  Otherwise, create a space for it at the end of the header */
+	if (v1 == NULL) {
+	    ve = ksearch (hstring,"END");
+	    v1 = ve;
+
+	    /* Align pointer at start of 80-character line */
+	    lc = v1 - hstring;
+	    ln = lc / 80;
+	    nc = ln * 80;
+	    v1 = hstring + nc;
+	    v2 = v1 + 80;
+
+	    /* If header length is exceeded, return error code */
+	    if (v2 - hstring > lhead) {
+		return (-1);
+		}
+
+	    strncpy (v2, ve, 80);
+	    }
+	else
+	    v2 = v1 + 80;
+	lcom = 0;
+	newcom[0] = 0;
+	}
+
+    /*  Otherwise, extract the entry for this keyword from the header */
+    else {
+
+	/* Align pointer at start of 80-character line */
+	lc = v1 - hstring;
+	ln = lc / 80;
+	nc = ln * 80;
+	v1 = hstring + nc;
+	v2 = v1 + 80;
+
+	strncpy (line, v1, 80);
+	line[80] = 0;
+	v2 = v1 + 80;
+
+	/*  check for quoted value */
+	q1 = strchr (line, squot);
+	if (q1 != NULL)
+	    q2 = strchr (q1+1,squot);
+	else
+	    q2 = line;
+
+	/*  extract comment and discount trailing spaces */
+	c1 = strchr (q2,'/');
+	if (c1 != NULL) {
+	    lcom = 80 - (c1 + 2 - line);
+	    strncpy (newcom, c1+2, lcom);
+	    vp = newcom + lcom - 1;
+	    while (vp-- > newcom && *vp == ' ')
+		lcom--;
+	    }
+	else {
+	    newcom[0] = 0;
+	    lcom = 0;
+	    }
+	}
+
+    /* Fill new entry with spaces */
+    for (vp = v1; vp < v2; vp++)
+	*vp = ' ';
+
+    /*  Copy keyword to new entry */
+    strncpy (v1, keyword, lkeyword);
+
+    /*  Add parameter value in the appropriate place */
+    vp = v1 + 8;
+    *vp = '=';
+    vp = v1 + 9;
+    *vp = ' ';
+    vp = vp + 1;
+    if (*value == squot) {
+	strncpy (vp, value, lval);
+	if (lval+12 > 31)
+	    lc = lval + 12;
+	else
+	    lc = 30;
+	}
+    else {
+	vp = v1 + 30 - lval;
+	strncpy (vp, value, lval);
+	lc = 30;
+	}
+
+    /* Add comment in the appropriate place */
+	if (lcom > 0) {
+	    if (lc+2+lcom > 80)
+		lcom = 77 - lc;
+	    vp = v1 + lc;     /* Jul 16 1997: was vp = v1 + lc * 2 */
+	    *vp++ = ' ';
+	    *vp++ = '/';
+	    *vp++ = ' ';
+	    lblank = v2 - vp;
+	    for (i = 0; i < lblank; i++)
+		vp[i] = ' ';
+	    if (lcom > lblank)
+		lcom = lblank;
+	    strncpy (vp, newcom, lcom);
+	    }
+
+	if (verbose) {
+	    if (lcom > 0)
+		fprintf (stderr,"HPUT: %s  = %s  / %s\n",keyword, value, newcom);
+	    else
+		fprintf (stderr,"HPUT: %s  = %s\n",keyword, value);
+	    }
+
+	return (0);
+}
+
+
+/*  HPUTCOM - Set comment for keyword or on line in FITS header string */
+
+int
+hputcom (hstring,keyword,comment)
+
+  char *hstring;
+  const char *keyword;
+  const char *comment;
+{
+    char squot;
+    char line[100];
+    int lkeyword, lcom, lhead, i, lblank, ln, nc, lc;
+    char *vp, *v1, *v2, *c0, *c1, *q1, *q2;
+
+    squot = 39;
+
+    /*  Find length of variable name */
+    lkeyword = (int) strlen (keyword);
+    lhead = gethlength (hstring);
+    lcom = (int) strlen (comment);
+
+    /*  If COMMENT or HISTORY, always add it just before the END */
+    if (lkeyword == 7 && (strncmp (keyword,"COMMENT",7) == 0 ||
+	strncmp (keyword,"HISTORY",7) == 0)) {
+
+	/* Find end of header */
+	v1 = ksearch (hstring,"END");
+
+	/* Align pointer at start of 80-character line */
+	lc = v1 - hstring;
+	ln = lc / 80;
+	nc = ln * 80;
+	v1 = hstring + nc;
+	v2 = v1 + 80;
+
+	/* If header length is exceeded, return error code */
+	if (v2 - hstring > lhead) {
+	    return (-1);
+	    }
+
+	/* Move END down 80 characters */
+	strncpy (v2, v1, 80);
+
+	/*  blank out new line and insert keyword */
+	for (vp = v1; vp < v2; vp++)
+	    *vp = ' ';
+	strncpy (v1, keyword, lkeyword);
+	c0 = v1 + lkeyword;
+	}
+
+    /* Search header string for variable name */
+    else {
+	v1 = ksearch (hstring,keyword);
+
+	/* If parameter is not found, return without doing anything */
+	if (v1 == NULL) {
+	    if (verbose)
+		fprintf (stderr,"HPUTCOM: %s not found\n",keyword);
+	    return (-1);
+	    }
+
+	/* Align pointer at start of 80-character line */
+	lc = v1 - hstring;
+	ln = lc / 80;
+	nc = ln * 80;
+	v1 = hstring + nc;
+	v2 = v1 + 80;
+
+	/* Extract entry for this variable from the header */
+	strncpy (line, v1, 80);
+	line[80] = '\0'; /* Null-terminate line before strchr call */
+
+	/* check for quoted value */
+	q1 = strchr (line,squot);
+	if (q1 != NULL)
+	    q2 = strchr (q1+1,squot);
+	else
+	    q2 = NULL;
+
+	if (q2 == NULL || q2-line < 30)
+	    c0 = v1 + 30;
+	else
+	    c0 = v1 + (q2 - line) + 1; /* allan: 1997-09-30, was c0=q2+2 */
+
+	/* If comment will not fit at all, return */
+	if (c0 - v1 > 77)
+	    return (-1);
+	strncpy (c0, " / ",3);
+	}
+
+    /* Create new entry */
+    if (lcom > 0) {
+	c1 = c0 + 3;
+	lblank = v2 - c1;
+	if (lcom > lblank)
+	    lcom = lblank;
+	for (i = 0; i < lblank; i++)
+	    c1[i] = ' ';
+	strncpy (c1, comment, lcom);
+	}
+
+    if (verbose) {
+	fprintf (stderr,"HPUTCOM: %s / %s\n",keyword,comment);
+	}
+    return (0);
+}
+
+
+static int leaveblank = 0;	/* If 1, leave blank line when deleting */
+void
+setleaveblank (lb)
+int lb; { leaveblank = lb; return; }
+
+static int headshrink=1; /* Set to 1 to drop line after deleting keyword */
+void
+setheadshrink (hsh)
+int hsh;
+{headshrink = hsh; return;}
+
+/*  HDEL - Set character string keyword = value in FITS header string
+ *	    returns 1 if entry deleted, else 0
+ */
+
+int
+hdel (hstring,keyword)
+
+char *hstring;		/* FITS header */
+const char *keyword;	/* Keyword of entry to be deleted */
+{
+    char *v, *v1, *v2, *ve;
+
+    /* Search for keyword */
+    v1 = ksearch (hstring,keyword);
+
+    /*  If keyword is not found, return header unchanged */
+    if (v1 == NULL) {
+	return (0);
+	}
+
+    /*  Find end of header */
+    ve = ksearch (hstring,"END");
+
+    /* If headshrink is 0, leave END where it is */
+    if (!leaveblank && !headshrink)
+	ve = ve - 80;
+
+    /* Cover deleted keyword line with spaces */
+    if (leaveblank) {
+	v2 = v1 + 80;
+	for (v = ve; v < v2; v++)
+	    *v = ' ';
+	}
+
+    /* Shift rest of header up one line */
+    else {
+	for (v = v1; v < ve; v = v + 80) {
+	    v2 = v + 80;
+	    strncpy (v, v2, 80);
+	    }
+
+	/* Cover former last line with spaces */
+	v2 = ve + 80;
+	for (v = ve; v < v2; v++)
+	    *v = ' ';
+	}
+
+    return (1);
+}
+
+
+/*  HADD - Add character string keyword = value to FITS header string
+ *	    returns 1 if entry added, else 0
+ *	    Call hputx() to put value into entry
+ */
+
+int
+hadd (hplace, keyword)
+
+char *hplace;		/* FITS header position for new keyword */
+const char *keyword;	/* Keyword of entry to be deleted */
+{
+    char *v, *v1, *v2, *ve;
+    int i, lkey;
+
+    /*  Find end of header */
+    ve = ksearch (hplace,"END");
+
+    /*  If END is not found, return header unchanged */
+    if (ve == NULL) {
+	return (0);
+	}
+
+    v1 = hplace;
+
+    /* Shift rest of header down one line */
+    /* limit bug found by Paolo Montegriffo fixed 2000-04-19 */
+    for (v = ve; v >= v1; v = v - 80) {
+	v2 = v + 80;
+	strncpy (v2, v, 80);
+	}
+
+    /* Cover former first line with new keyword */
+    lkey = (int) strlen (keyword);
+    strncpy (hplace, keyword, lkey);
+    if (lkey < 8) {
+	for (i = lkey; i < 8; i++)
+	    hplace[i] = ' ';
+	hplace[8] = '=';
+	}
+    for (i = 9; i < 80; i++)
+	hplace[i] = ' ';
+
+    return (1);
+}
+
+
+/*  HCHANGE - Changes keyword for entry from keyword1 to keyword2 in FITS
+              header string
+ *	      returns 1 if entry changed, else 0
+ */
+
+int
+hchange (hstring, keyword1, keyword2)
+
+char *hstring;		/* FITS header */
+const char *keyword1;	/* Keyword to be changed */
+const char *keyword2;	/* New keyword name */
+{
+    char *v, *v1;
+    const char *v2;
+    int lv2, i;
+
+    /* Search for keyword */
+    v1 = ksearch (hstring,keyword1);
+
+    /*  If keyword is not found, return header unchanged */
+    if (!v1)
+	return (0);
+
+    else {
+	lv2 = (int) strlen (keyword2);
+	v = v1;
+	v2 = keyword2;
+	for (i = 0; i < 8; i++) {
+	    if (i < lv2)
+		v[i] = v2[i];
+	    else
+		v[i] = ' ';
+	    }
+	}
+
+    return (1);
+}
+
+
+/* Write the right ascension ra in sexagesimal format into string*/
+
+void
+ra2str (string, lstr, ra, ndec)
+
+char	*string;	/* Character string (returned) */
+int	lstr;		/* Maximum number of characters in string */
+double	ra;		/* Right ascension in degrees */
+int	ndec;		/* Number of decimal places in seconds */
+
+{
+    double a,b;
+    double seconds;
+    char tstring[64];
+    int hours;
+    int minutes;
+    int isec, ltstr;
+    double dsgn;
+
+    /* Keep RA between 0 and 360 */
+    if (ra < 0.0 ) {
+	ra = -ra;
+	dsgn = -1.0;
+	}
+    else
+	dsgn = 1.0;
+    ra = fmod(ra, 360.0);
+    ra *= dsgn;
+    if (ra < 0.0)
+	ra = ra + 360.0;
+
+    a = ra / 15.0;
+
+    /* Convert to hours */
+    hours = (int) a;
+
+    /* Compute minutes */
+    b =  (a - (double)hours) * 60.0;
+    minutes = (int) b;
+
+    /* Compute seconds */
+    seconds = (b - (double)minutes) * 60.0;
+
+    if (ndec > 5) {
+	if (seconds > 59.999999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%09.6f",hours,minutes,seconds);
+	}
+    else if (ndec > 4) {
+	if (seconds > 59.99999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%08.5f",hours,minutes,seconds);
+	}
+    else if (ndec > 3) {
+	if (seconds > 59.9999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%07.4f",hours,minutes,seconds);
+	}
+    else if (ndec > 2) {
+	if (seconds > 59.999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%06.3f",hours,minutes,seconds);
+	}
+    else if (ndec > 1) {
+	if (seconds > 59.99) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%05.2f",hours,minutes,seconds);
+	}
+    else if (ndec > 0) {
+	if (seconds > 59.9) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%04.1f",hours,minutes,seconds);
+	}
+    else {
+	isec = (int)(seconds + 0.5);
+	if (isec > 59) {
+	    isec = 0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    hours = hours + 1;
+	    }
+	hours = hours % 24;
+	(void) sprintf (tstring,"%02d:%02d:%02d",hours,minutes,isec);
+	}
+
+    /* Move formatted string to returned string */
+    ltstr = (int) strlen (tstring);
+    if (ltstr < lstr-1)
+	strcpy (string, tstring);
+    else {
+	strncpy (string, tstring, lstr-1);
+	string[lstr-1] = 0;
+	}
+    return;
+}
+
+
+/* Write the variable a in sexagesimal format into string */
+
+void
+dec2str (string, lstr, dec, ndec)
+
+char	*string;	/* Character string (returned) */
+int	lstr;		/* Maximum number of characters in string */
+double	dec;		/* Declination in degrees */
+int	ndec;		/* Number of decimal places in arcseconds */
+
+{
+    double a, b, dsgn, deg1;
+    double seconds;
+    char sign;
+    int degrees;
+    int minutes;
+    int isec, ltstr;
+    char tstring[64];
+
+    /* Keep angle between -180 and 360 degrees */
+    deg1 = dec;
+    if (deg1 < 0.0 ) {
+	deg1 = -deg1;
+	dsgn = -1.0;
+	}
+    else
+	dsgn = 1.0;
+    deg1 = fmod(deg1, 360.0);
+    deg1 *= dsgn;
+    if (deg1 <= -180.0)
+	deg1 = deg1 + 360.0;
+
+    a = deg1;
+
+    /* Set sign and do all the rest with a positive */
+    if (a < 0) {
+	sign = '-';
+	a = -a;
+	}
+    else
+	sign = '+';
+
+    /* Convert to degrees */
+    degrees = (int) a;
+
+    /* Compute minutes */
+    b =  (a - (double)degrees) * 60.0;
+    minutes = (int) b;
+
+    /* Compute seconds */
+    seconds = (b - (double)minutes) * 60.0;
+
+    if (ndec > 5) {
+	if (seconds > 59.999999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%09.6f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 4) {
+	if (seconds > 59.99999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%08.5f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 3) {
+	if (seconds > 59.9999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%07.4f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 2) {
+	if (seconds > 59.999) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%06.3f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 1) {
+	if (seconds > 59.99) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%05.2f",sign,degrees,minutes,seconds);
+	}
+    else if (ndec > 0) {
+	if (seconds > 59.9) {
+	    seconds = 0.0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%04.1f",sign,degrees,minutes,seconds);
+	}
+    else {
+	isec = (int)(seconds + 0.5);
+	if (isec > 59) {
+	    isec = 0;
+	    minutes = minutes + 1;
+	    }
+	if (minutes > 59) {
+	    minutes = 0;
+	    degrees = degrees + 1;
+	    }
+	(void) sprintf (tstring,"%c%02d:%02d:%02d",sign,degrees,minutes,isec);
+	}
+
+    /* Move formatted string to returned string */
+    ltstr = (int) strlen (tstring);
+    if (ltstr < lstr-1)
+	strcpy (string, tstring);
+    else {
+	strncpy (string, tstring, lstr-1);
+	string[lstr-1] = 0;
+	}
+   return;
+}
+
+
+/* Write the angle a in decimal format into string */
+
+void
+deg2str (string, lstr, deg, ndec)
+
+char	*string;	/* Character string (returned) */
+int	lstr;		/* Maximum number of characters in string */
+double	deg;		/* Angle in degrees */
+int	ndec;		/* Number of decimal places in degree string */
+
+{
+    char degform[8];
+    int field, ltstr;
+    char tstring[64];
+    double deg1;
+    double dsgn;
+
+    /* Keep angle between -180 and 360 degrees */
+    deg1 = deg;
+    if (deg1 < 0.0 ) {
+	deg1 = -deg1;
+	dsgn = -1.0;
+	}
+    else
+	dsgn = 1.0;
+    deg1 = fmod(deg1, 360.0);
+    deg1 *= dsgn;
+    if (deg1 <= -180.0)
+	deg1 = deg1 + 360.0;
+
+    /* Write angle to string, adding 4 digits to number of decimal places */
+    field = ndec + 4;
+    if (ndec > 0) {
+	sprintf (degform, "%%%d.%df", field, ndec);
+	sprintf (tstring, degform, deg1);
+	}
+    else {
+	sprintf (degform, "%%%4d", field);
+	sprintf (tstring, degform, (int)deg1);
+	}
+
+    /* Move formatted string to returned string */
+    ltstr = (int) strlen (tstring);
+    if (ltstr < lstr-1)
+	strcpy (string, tstring);
+    else {
+	strncpy (string, tstring, lstr-1);
+	string[lstr-1] = 0;
+	}
+    return;
+}
+
+
+/* Write the variable a in decimal format into field-character string  */
+
+void
+num2str (string, num, field, ndec)
+
+char	*string;	/* Character string (returned) */
+double	num;		/* Number */
+int	field;		/* Number of characters in output field (0=any) */
+int	ndec;		/* Number of decimal places in degree string */
+
+{
+    char numform[8];
+
+    if (field > 0) {
+	if (ndec > 0) {
+	    sprintf (numform, "%%%d.%df", field, ndec);
+	    sprintf (string, numform, num);
+	    }
+	else {
+	    sprintf (numform, "%%%dd", field);
+	    sprintf (string, numform, (int)num);
+	    }
+	}
+    else {
+	if (ndec > 0) {
+	    sprintf (numform, "%%.%df", ndec);
+	    sprintf (string, numform, num);
+	    }
+	else {
+	    sprintf (string, "%d", (int)num);
+	    }
+	}
+    return;
+}
+
+/* Dec 14 1995	Original subroutines
+
+ * Feb  5 1996	Added HDEL to delete keyword entry from FITS header
+ * Feb  7 1996	Add EOS to LINE in HPUTC
+ * Feb 21 1996	Add RA2STR and DEC2STR string routines
+ * Jul 19 1996	Add HPUTRA and HPUTDEC
+ * Jul 22 1996	Add HCHANGE to change keywords
+ * Aug  5 1996	Add HPUTNR8 to save specific number of decimal places
+ * Oct 15 1996	Fix spelling
+ * Nov  1 1996	Add DEG2STR to set specific number of decimal places
+ * Nov  1 1996	Allow DEC2STR to handle upt to 6 decimal places
+ *
+ * Mar 20 1997	Fix format error in DEG2STR
+ * Jul  7 1997	Fix 2 errors in HPUTCOM found by Allan Brighton
+ * Jul 16 1997	Fix error in HPUTC found by Allan Brighton
+ * Jul 17 1997	Fix error in HPUTC found by Allan Brighton
+ * Sep 30 1997	Fix error in HPUTCOM found by Allan Brighton
+ * Dec 15 1997	Fix minor bugs after lint
+ * Dec 31 1997	Always put two hour digits in RA2STR
+ *
+ * Feb 25 1998	Add HADD to insert keywords at specific locations
+ * Mar 27 1998	If n is negative, write g format in HPUTNR8()
+ * Apr 24 1998	Add NUM2STR() for easy output formatting
+ * Apr 30 1998	Use BLSEARCH() to overwrite blank lines before END
+ * May 27 1998	Keep Dec between -90 and +90 in DEC2STR()
+ * May 28 1998	Keep RA between 0 and 360 in RA2STR()
+ * Jun  2 1998	Fix bug when filling in blank lines before END
+ * Jun 24 1998	Add string length to ra2str(), dec2str(), and deg2str()
+ * Jun 25 1998	Make string converstion subroutines more robust
+ * Aug 31 1998	Add getltime() and getutime()
+ * Sep 28 1998	Null-terminate comment in HPUTCOM (Allan Brighton)
+ * Oct  1 1998	Change clock declaration in getltime() from int (Allan Brighton)
+ *
+ * Jan 28 1999	Fix bug to avoid writing HISTORY or COMMENT past 80 characters
+ * Jul 14 1999	Pad string in hputs() to minimum of 8 characters
+ * Aug 16 1999	Keep angle between -180 and +360 in dec2str()
+ * Oct  6 1999	Reallocate header buffer if it is too small in hputc()
+ * Oct 14 1999	Do not reallocate header; return error if not successful
+ *
+ * Mar  2 2000	Do not add quotes if adding HISTORY or COMMENT with hputs()
+ * Mar 22 2000	Move getutime() and getltime() to dateutil.c
+ * Mar 27 2000	Add hputm() for muti-line keywords
+ * Mar 27 2000	Fix bug testing for space to fit comment in hputcom()
+ * Apr 19 2000	Fix bug in hadd() which overwrote line
+ * Jun  2 2000	Dropped unused variable lv in hputm() after lint
+ * Jul 20 2000	Drop unused variables blank and i in hputc()
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Jan 18 2001	Drop declaration of blsearch(); it is in fitshead.h
+ *
+ * Jan  4 2002	Fix placement of comments
+ *
+ * Jul  1 2004	Add headshrink to optionally keep blank lines in header
+ * Sep  3 2004	Fix bug so comments are not pushed onto next line if long value
+ * Sep 16 2004	Add fixnegzero() to avoid putting signed zero values in header
+ *
+ * May 22 2006	Add option to leave blank line when deleting a keyword
+ * Jun 15 2006	Fix comment alignment in hputc() and hputcom()
+ * Jun 20 2006	Initialized uninitialized variables in hputm() and hputcom()
+ *
+ * Jan  4 2007	Declare keyword to be const
+ * Jan  4 2007	Drop unused subroutine hputi2()
+ * Jan  5 2007	Drop ksearch() declarations; it is now in fitshead.h
+ * Jan 16 2007	Fix bugs in ra2str() and dec2str() so ndec=0 works
+ * Aug 20 2007	Fix bug so comments after quoted keywords work
+ */
diff --git a/Code/src/libwcs/iget.c b/Code/src/libwcs/iget.c
new file mode 100644
index 0000000000000000000000000000000000000000..5de21e619360bad34dd891790f4a93fdb4ef26c7
--- /dev/null
+++ b/Code/src/libwcs/iget.c
@@ -0,0 +1,531 @@
+/*** File libwcs/iget.c
+ *** January 4, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1998-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	iget.c (Get IRAF FITS Header parameter values)
+ * Purpose:	Extract values for variables from IRAF keyword value string
+ * Subroutine:	mgeti4 (hstring,mkey,keyword,ival) returns long integer
+ * Subroutine:	mgetr8 (hstring,mkey,keyword,dval) returns double
+ * Subroutine:	mgetstr (hstring,mkey,keyword,lstr,str) returns character string
+ * Subroutine:	igeti4 (hstring,keyword,ival) returns long integer
+ * Subroutine:	igetr4 (hstring,keyword,rval) returns real
+ * Subroutine:	igetr8 (hstring,keyword,dval) returns double
+ * Subroutine:	igets  (hstring,keyword,lstr,str) returns character string
+ * Subroutine:	igetc  (hstring,keyword) returns character string
+ * Subroutine:	isearch (hstring,keyword) returns pointer to header string entry
+ */
+
+#include <string.h>		/* NULL, strlen, strstr, strcpy */
+#include <stdio.h>
+#include "fitshead.h"	/* FITS header extraction subroutines */
+#include <stdlib.h>
+#ifndef VMS
+#include <limits.h>
+#else
+#define INT_MAX  2147483647 /* Biggest number that can fit in long */
+#define SHRT_MAX 32767
+#endif
+
+#define MAX_LVAL 2000
+
+static char *isearch();
+static char val[30];
+
+/* Extract long value for variable from IRAF multiline keyword value */
+
+int
+mgeti4 (hstring, mkey, keyword, ival)
+
+const char *hstring;	/* Character string containing FITS or IRAF header information
+		   in the format <keyword>= <value> ... */
+const char *mkey;	/* Character string containing the name of the multi-line
+		   keyword, the string value of which contains the desired
+		   keyword, the value of which is returned. */
+const char *keyword;	/* Character string containing the name of the keyword
+		   within the multiline IRAF keyword */
+int *ival;	/* Integer value returned */
+{
+    char *mstring;
+
+    mstring = malloc (MAX_LVAL);
+
+    if (hgetm (hstring, mkey, MAX_LVAL, mstring)) {
+	if (igeti4 (mstring, keyword, ival)) {
+	    free (mstring);
+	    return (1);
+	    }
+	else {
+	    free (mstring);
+	    return (0);
+	    }
+	}
+    else {
+	free (mstring);
+	return (0);
+	}
+}
+
+/* Extract double value for variable from IRAF multiline keyword value */
+
+int
+mgetr8 (hstring, mkey, keyword, dval)
+
+const char	*hstring; /* Character string containing FITS or IRAF header information
+		   in the format <keyword>= <value> ... */
+const char	*mkey;	  /* Character string containing the name of the multi-line
+		   keyword, the string value of which contains the desired
+		   keyword, the value of which is returned. */
+const char	*keyword; /* Character string containing the name of the keyword
+		   within the multiline IRAF keyword */
+double	*dval;	  /* Integer value returned */
+{
+    char *mstring;
+    mstring = malloc (MAX_LVAL);
+
+    if (hgetm (hstring, mkey, MAX_LVAL, mstring)) {
+	if (igetr8 (mstring, keyword, dval)) {
+	    free (mstring);
+	    return (1);
+	    }
+	else {
+	    free (mstring);
+	    return (0);
+	    }
+	}
+    else {
+	free (mstring);
+	return (0);
+	}
+}
+
+
+/* Extract string value for variable from IRAF keyword value string */
+
+int
+mgetstr (hstring, mkey, keyword, lstr, str)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *mkey;	/* Character string containing the name of the multi-line
+		   keyword, the string value of which contains the desired
+		   keyword, the value of which is returned. */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+const int lstr;	/* Size of str in characters */
+char *str;	/* String (returned) */
+{
+    char *mstring;
+    mstring = malloc (MAX_LVAL);
+
+    if (hgetm (hstring, mkey, MAX_LVAL, mstring)) {
+	if (igets (mstring, keyword, lstr, str)) {
+	    free (mstring);
+	    return (1);
+	    }
+	else {
+	    free (mstring);
+	    return (0);
+	    }
+	}
+    else {
+	free (mstring);
+	return (0);
+	}
+}
+
+
+/* Extract long value for variable from IRAF keyword value string */
+
+int
+igeti4 (hstring, keyword, ival)
+
+const char *hstring;	/* character string containing IRAF header information
+		   in the format <keyword>= <value> ... */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+int *ival;	/* Integer value returned */
+{
+char *value;
+double dval;
+int minint;
+
+/* Get value from header string */
+	value = igetc (hstring,keyword);
+
+/* Translate value from ASCII to binary */
+	if (value != NULL) {
+	    minint = -INT_MAX - 1;
+	    strcpy (val, value);
+	    dval = atof (val);
+	    if (dval+0.001 > INT_MAX)
+		*ival = INT_MAX;
+	    else if (dval >= 0)
+		*ival = (int) (dval + 0.001);
+	    else if (dval-0.001 < minint)
+		*ival = minint;
+	    else
+		*ival = (int) (dval - 0.001);
+	    return (1);
+	    }
+	else {
+	    return (0);
+	    }
+}
+
+
+/* Extract integer*2 value for variable from IRAF keyword value string */
+
+int
+igeti2 (hstring,keyword,ival)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+short *ival;
+{
+char *value;
+double dval;
+int minshort;
+
+/* Get value from header string */
+	value = igetc (hstring,keyword);
+
+/* Translate value from ASCII to binary */
+	if (value != NULL) {
+	    strcpy (val, value);
+	    dval = atof (val);
+	    minshort = -SHRT_MAX - 1;
+	    if (dval+0.001 > SHRT_MAX)
+		*ival = SHRT_MAX;
+	    else if (dval >= 0)
+		*ival = (short) (dval + 0.001);
+	    else if (dval-0.001 < minshort)
+		*ival = minshort;
+	    else
+		*ival = (short) (dval - 0.001);
+	    return (1);
+	    }
+	else {
+	    return (0);
+	    }
+}
+
+/* Extract real value for variable from IRAF keyword value string */
+
+int
+igetr4 (hstring,keyword,rval)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+float *rval;
+{
+	char *value;
+
+/* Get value from header string */
+	value = igetc (hstring,keyword);
+
+/* Translate value from ASCII to binary */
+	if (value != NULL) {
+	    strcpy (val, value);
+	    *rval = (float) atof (val);
+	    return (1);
+	    }
+	else {
+	    return (0);
+	    }
+}
+
+
+/* Extract real*8 value for variable from IRAF keyword value string */
+
+int
+igetr8 (hstring,keyword,dval)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+double *dval;
+{
+	char *value,val[30];
+
+/* Get value from header string */
+	value = igetc (hstring,keyword);
+
+/* Translate value from ASCII to binary */
+	if (value != NULL) {
+	    strcpy (val, value);
+	    *dval = atof (val);
+	    return (1);
+	    }
+	else {
+	    return (0);
+	    }
+}
+
+
+/* Extract string value for variable from IRAF keyword value string */
+
+int
+igets (hstring, keyword, lstr, str)
+
+const char *hstring;	/* character string containing FITS header information
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword;	/* character string containing the name of the keyword
+		   the value of which is returned.  hget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+const int lstr;	/* Size of str in characters */
+char *str;	/* String (returned) */
+{
+	char *value;
+	int lval;
+
+/* Get value from header string */
+	value = igetc (hstring,keyword);
+
+	if (value != NULL) {
+	    lval = strlen (value);
+	    if (lval < lstr)
+		strcpy (str, value);
+	    else if (lstr > 1)
+		strncpy (str, value, lstr-1);
+	    else
+		str[0] = value[0];
+	    return (1);
+	    }
+	else
+	    return (0);
+}
+
+
+/* Extract character value for variable from IRAF keyword value string */
+
+char *
+igetc (hstring,keyword0)
+
+const char *hstring;	/* character string containing IRAF keyword value string
+		   in the format <keyword>= <value> {/ <comment>} */
+const char *keyword0;	/* character string containing the name of the keyword
+		   the value of which is returned.  iget searches for a
+		   line beginning with this string.  if "[n]" is present,
+		   the n'th token in the value is returned.
+		   (the first 8 characters must be unique) */
+{
+	static char cval[MAX_LVAL];
+	char *value;
+	char cwhite[8];
+	char lbracket[2],rbracket[2];
+	char keyword[16];
+	char line[MAX_LVAL];
+	char *vpos,*cpar;
+	char *c1, *brack1, *brack2;
+	int ipar, i;
+
+	lbracket[0] = 91;
+	lbracket[1] = 0;
+	rbracket[0] = 93;
+	rbracket[1] = 0;
+
+/* Find length of variable name */
+	strcpy (keyword,keyword0);
+	brack1 = strsrch (keyword,lbracket);
+	if (brack1 != NULL) *brack1 = '\0';
+
+/* Search header string for variable name */
+	vpos = isearch (hstring,keyword);
+
+/* Exit if not found */
+	if (vpos == NULL) {
+	    return (NULL);
+	    }
+
+/* Initialize returned value to nulls */
+	 for (i = 0; i < MAX_LVAL; i++)
+	    line[i] = 0;
+
+/* If quoted value, copy until second quote is reached */
+	i = 0;
+	if (*vpos == '"') {
+	     vpos++;
+	     while (*vpos && *vpos != '"' && i < MAX_LVAL)
+		line[i++] = *vpos++;
+	     }
+
+/* Otherwise copy until next space or tab */
+	else {
+	     while (*vpos != ' ' && *vpos != (char)9 &&
+		    *vpos > 0 && i < MAX_LVAL)
+		line[i++] = *vpos++;
+	     }
+
+/* If keyword has brackets, extract appropriate token from value */
+	if (brack1 != NULL) {
+	    c1 = (char *) (brack1 + 1);
+	    brack2 = strsrch (c1, rbracket);
+	    if (brack2 != NULL) {
+		*brack2 = '\0';
+		ipar = atoi (c1);
+		if (ipar > 0) {
+		    cwhite[0] = ' ';
+		    cwhite[1] = ',';
+		    cwhite[2] = '\0';
+		    cpar = strtok (line, cwhite);
+		    for (i = 1; i < ipar; i++) {
+			cpar = strtok (NULL, cwhite);
+			}
+		    if (cpar != NULL) {
+			strcpy (cval,cpar);
+			}
+		    else
+			value = NULL;
+		    }
+		}
+	    }
+	else
+	    strcpy (cval, line);
+
+	value = cval;
+
+	return (value);
+}
+
+
+/* Find value for specified IRAF keyword */
+
+static char *
+isearch (hstring,keyword)
+
+/* Find entry for keyword keyword in IRAF keyword value string hstring.
+   NULL is returned if the keyword is not found */
+
+const char *hstring;	/* character string containing fits-style header
+		information in the format <keyword>= <value> {/ <comment>}
+		the default is that each entry is 80 characters long;
+		however, lines may be of arbitrary length terminated by
+		nulls, carriage returns or linefeeds, if packed is true.  */
+const char *keyword;	/* character string containing the name of the variable
+		to be returned.  isearch searches for a line beginning
+		with this string.  The string may be a character
+		literal or a character variable terminated by a null
+		or '$'.  it is truncated to 8 characters. */
+{
+    char *loc, *headnext, *headlast, *pval;
+    int lastchar, nextchar, lkey, nleft, lhstr;
+
+/* Search header string for variable name */
+    lhstr = 0;
+    while (lhstr < 57600 && hstring[lhstr] != 0)
+	lhstr++;
+    headlast = (char *) hstring + lhstr;
+    headnext = (char *) hstring;
+    pval = NULL;
+    lkey = strlen (keyword);
+    while (headnext < headlast) {
+	nleft = headlast - headnext;
+	loc = strnsrch (headnext, keyword, nleft);
+
+	/* Exit if keyword is not found */
+	if (loc == NULL) {
+	    break;
+	    }
+
+	nextchar = (int) *(loc + lkey);
+	lastchar = (int) *(loc - 1);
+
+	/* If parameter name in header is longer, keep searching */
+	if (nextchar != 61 && nextchar > 32 && nextchar < 127)
+	    headnext = loc + 1;
+
+	/* If start of string, keep it */
+	else if (loc == hstring) {
+	    pval = loc;
+	    break;
+	    }
+
+	/* If preceeded by a blank or tab, keep it */
+	else if (lastchar == 32 || lastchar == 9) {
+	    pval = loc;
+	    break;
+	    }
+
+	else
+	    headnext = loc + 1;
+	}
+
+    /* Find start of value string for this keyword */
+    if (pval != NULL) {
+	pval = pval + lkey;
+	while (*pval == ' ' || *pval == '=')
+	    pval++;
+	}
+
+    /* Return pointer to calling program */
+    return (pval);
+
+}
+
+/* Mar 12 1998	New subroutines
+ * Apr 15 1998	Set IGET() and ISEARCH() static when defined
+ * Apr 24 1998	Add MGETI4(), MGETR8(), and MGETS() for single step IRAF ext.
+ * Jun  1 1998	Add VMS patch from Harry Payne at STScI
+ * Jul  9 1998	Fix bracket token extraction after Paul Sydney
+
+ * May  5 1999	values.h -> POSIX limits.h: MAXINT->INT_MAX, MAXSHORT->SHRT_MAX
+ * Oct 21 1999	Fix declarations after lint
+ *
+ * Feb 11 2000	Stop search for end of quoted keyword if more than 500 chars
+ * Jul 20 2000	Drop unused variables squot, dquot, and slash in igetc()
+ *
+ * Jun 26 2002	Change maximum string length from 600 to 2000; use MAX_LVAL
+ * Jun 26 2002	Stop search for end of quoted keyword if > MAX_LVAL chars
+ *
+ * Sep 23 2003	Change mgets() to mgetstr() to avoid name collision at UCO Lick
+ *
+ * Feb 26 2004	Make igetc() accessible from outside this file
+ *
+ * Jan  4 2007	Declare header, keyword to be const
+ */
diff --git a/Code/src/libwcs/imgetwcs.c b/Code/src/libwcs/imgetwcs.c
new file mode 100644
index 0000000000000000000000000000000000000000..3d62c771e755278f4e96997d7c19b86042427903
--- /dev/null
+++ b/Code/src/libwcs/imgetwcs.c
@@ -0,0 +1,805 @@
+/*** File libwcs/imgetwcs.c
+ *** March 24, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu (remotely based on UIowa code)
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+	   Internet email: dmink@cfa.harvard.edu
+	   Postal address: Doug Mink
+	                   Smithsonian Astrophysical Observatory
+	                   60 Garden St.
+	                   Cambridge, MA 02138 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "wcs.h"
+#include "lwcs.h"
+
+/* Get the C* WCS fields in  a FITS header based on a reference catalog
+ * do it by finding stars in the image and in the reference catalog and
+ * finding the rotation and offsets which result in a best-fit.
+ * verbose generates extra info on stderr.
+ * try using deeper reference star catalog searches if there is trouble.
+ * return 1 if all ok, else 0
+ */
+
+/* These parameters can be set on the command line */
+static double secpix0 = PSCALE;		/* Set image scale--override header */
+static double secpix2 = PSCALE;		/* Set image scale 2--override header */
+static double *cd0 = NULL;		/* Set CD matrix--override header */
+static double rot0 = 361.0;		/* Initial image rotation */
+static int comsys = WCS_J2000;		/* Command line center coordinte system */
+static int wp0 = 0;			/* Initial width of image */
+static int hp0 = 0;			/* Initial height of image */
+static double ra0 = -99.0;		/* Initial center RA in degrees */
+static double dec0 = -99.0;		/* Initial center Dec in degrees */
+static double xref0 = -99999.0;		/* Reference pixel X coordinate */
+static double yref0 = -99999.0;		/* Reference pixel Y coordinate */
+static int ptype0 = -1;			/* Projection type to fit */
+static int  nctype = 28;		/* Number of possible projections */
+static char ctypes[32][4];		/* 3-letter codes for projections */
+static int usecdelt = 0;		/* Use CDELT if 1, else CD matrix */
+static char *dateobs0 = NULL;		/* Initial DATE-OBS value in FITS date format */
+
+struct WorldCoor *ChangeFITSWCS();
+
+/* Set a nominal world coordinate system from image header info.
+ * If the image center is not FK5 (J2000) equinox, convert it
+ * Return a WCS structure if OK, else return NULL
+ */
+
+struct WorldCoor *
+GetFITSWCS (filename, header, verbose, cra, cdec, dra, ddec, secpix, wp, hp,
+	    sysout, eqout)
+
+char	*filename;	/* FITS or IRAF file name */
+char	*header;	/* Image FITS header */
+int	verbose;	/* Extra printing if =1 */
+double	*cra;		/* Center right ascension in degrees (returned) */
+double	*cdec;		/* Center declination in degrees (returned) */
+double	*dra;		/* Right ascension half-width in degrees (returned) */
+double	*ddec;		/* Declination half-width in degrees (returned) */
+double	*secpix;	/* Arcseconds per pixel (returned) */
+int	*wp;		/* Image width in pixels (returned) */
+int	*hp;		/* Image height in pixels (returned) */
+int	*sysout;	/* Coordinate system to return (0=image, returned) */
+double	*eqout;		/* Equinox to return (0=image, returned) */
+{
+    int naxes;
+    double eq1, x, y;
+    double ra1, dec1, dx, dy;
+    double xmin, xmax, ymin, ymax, ra2, dec2, ra3, dec3, ra4, dec4;
+    double dra0, dra1, dra2, dra3, dra4;
+    struct WorldCoor *wcs;
+    char rstr[64], dstr[64], cstr[16];
+
+    /* Initialize WCS structure from possibly revised FITS header */
+    wcs = ChangeFITSWCS (filename, header, verbose);
+    if (wcs == NULL) {
+	return (NULL);
+	}
+    *hp = (int) wcs->nypix;
+    *wp = (int) wcs->nxpix;
+
+    /* If incomplete WCS in header, drop out */
+    if (nowcs (wcs)) {
+	setwcsfile (filename);
+	/* wcserr(); */
+	if (verbose)
+	    fprintf (stderr,"Insufficient information for initial WCS\n");
+	return (NULL);
+	}
+
+    /* If in linear coordinates, do not print as sexigesimal */
+    if (wcs->sysout < 1 || wcs->sysout == 6 || wcs->sysout == 10)
+	wcs->degout = 1;
+
+    /* Set flag to get appropriate equinox for catalog search */
+    if (!*sysout)
+	*sysout = wcs->syswcs;
+    if (*eqout == 0.0)
+	*eqout = wcs->equinox;
+    eq1 = wcs->equinox;
+    if (wcs->coorflip) {
+	ra1 = wcs->crval[1];
+	dec1 = wcs->crval[0];
+	}
+    else {
+	ra1 = wcs->crval[0];
+	dec1 = wcs->crval[1];
+	}
+
+    /* Print reference pixel position and value */
+    if (verbose && (eq1 != *eqout || wcs->syswcs != *sysout)) {
+	if (wcs->degout) {
+	    deg2str (rstr, 32, ra1, 6);
+	    deg2str (dstr, 32, dec1, 6);
+	    }
+	else {
+	    ra2str (rstr, 32, ra1, 3);
+	    dec2str (dstr, 32, dec1, 2);
+	    }
+	wcscstr (cstr, wcs->syswcs, wcs->equinox, wcs->epoch);
+	fprintf (stderr,"Reference pixel (%.2f,%.2f) %s %s %s\n",
+		 wcs->xrefpix, wcs->yrefpix, rstr, dstr, cstr);
+	}
+
+    /* Get coordinates of corners for size for catalog searching */
+    dx = wcs->nxpix;
+    dy = wcs->nypix;
+    xmin = 0.5;
+    ymin = 0.5;
+    xmax = 0.5 + dx;
+    ymax = 0.5 + dy;
+    pix2wcs (wcs, xmin, ymin, &ra1, &dec1);
+    pix2wcs (wcs, xmin, ymax, &ra2, &dec2);
+    pix2wcs (wcs, xmax, ymin, &ra3, &dec3);
+    pix2wcs (wcs, xmax, ymax, &ra4, &dec4);
+
+    /* Convert search corners to output coordinate system and equinox */
+    if (wcs->syswcs > 0 && wcs->syswcs != 6 && wcs->syswcs != 10) {
+	wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,&ra1,&dec1,wcs->epoch);
+	wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,&ra2,&dec2,wcs->epoch);
+	wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,&ra3,&dec3,wcs->epoch);
+	wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,&ra4,&dec4,wcs->epoch);
+	}
+
+    /* Find center and convert to output coordinate system and equinox */
+    x = 0.5 + (dx * 0.5);
+    y = 0.5 + (dy * 0.5);
+    pix2wcs (wcs, x, y, cra, cdec);
+    if (wcs->syswcs > 0 && wcs->syswcs != 6 && wcs->syswcs != 10)
+	wcscon (wcs->syswcs,*sysout,wcs->equinox,*eqout,cra,cdec,wcs->epoch);
+
+    /* Find maximum half-width in declination */
+    *ddec = fabs (dec1 - *cdec);
+    if (fabs (dec2 - *cdec) > *ddec)
+	*ddec = fabs (dec2 - *cdec);
+    if (fabs (dec3 - *cdec) > *ddec)
+	*ddec = fabs (dec3 - *cdec);
+    if (fabs (dec4 - *cdec) > *ddec)
+	*ddec = fabs (dec4 - *cdec);
+
+    /* Find maximum half-width in right ascension */
+    dra0 = (dx / dy) * (*ddec / cos (*cdec));
+    dra1 = ra1 - *cra;
+    dra2 = ra2 - *cra;
+    if (*cra < 0 && *cra + dra0 > 0.0) {
+	dra1 = -(dra1 - 360.0);
+	dra2 = -(dra2 - 360.0);
+	}
+    if (dra1 > 180.0)
+	dra1 = dra1 - 360.0;
+    else if (dra1 < -180.0)
+	dra1 = dra1 + 360.0;
+    else if (dra1 < 0.0)
+	dra1 = -dra1;
+    if (dra2 > 180.0)
+	dra2 = dra2 - 360.0;
+    else if (dra2 < -180.0)
+	dra2 = dra2 + 360.0;
+    else if (dra2 < 0.0)
+	dra2 = -dra2;
+    dra3 = *cra - ra3;
+    dra4 = *cra - ra4;
+    if (*cra > 0 && *cra - dra0 < 0.0) {
+	dra3 = dra3 + 360.0;
+	dra4 = dra4 + 360.0;
+	}
+    if (dra3 > 180.0)
+	dra3 = dra3 - 360.0;
+    else if (dra3 < -180.0)
+	dra3 = dra3 + 360.0;
+    else if (dra3 < 0.0)
+	dra3 = -dra3;
+    if (dra4 > 180.0)
+	dra4 = dra4 - 360.0;
+    else if (dra4 < -180.0)
+	dra4 = dra4 + 360.0;
+    else if (dra4 < 0.0)
+	dra4 = -dra4;
+    *dra = dra1;
+    if (dra2 > *dra)
+	*dra = dra2;
+    if (dra3 > *dra)
+	*dra = dra3;
+    if (dra4 > *dra)
+	*dra = dra4;
+
+    /* wcssize (wcs, cra, cdec, dra, ddec); */
+
+    /* Set reference pixel to center of image if it has not been set */
+    if (wcs->xref == -999.0 && wcs->yref == -999.0) {
+	wcs->xref = *cra;
+	wcs->cel.ref[0] = *cra;
+	wcs->crval[0] = *cra;
+	wcs->yref = *cdec;
+	wcs->cel.ref[1] = *cdec;
+	wcs->crval[1] = *cdec;
+	ra1 = *cra;
+	dec1 = *cdec;
+	if (wcs->xrefpix == 0.0 && wcs->yrefpix == 0.0) {
+	    wcs->xrefpix = 0.5 + (double) wcs->nxpix * 0.5;
+	    wcs->yrefpix = 0.5 + (double) wcs->nypix * 0.5;
+	    }
+	wcs->xinc = *dra * 2.0 / (double) wcs->nxpix;
+	wcs->yinc = *ddec * 2.0 / (double) wcs->nypix;
+	/* hchange (header,"PLTRAH","PLT0RAH");
+	wcs->plate_fit = 0; */
+	}
+
+    /* Convert center to desired coordinate system */
+    else if (wcs->syswcs != *sysout && wcs->equinox != *eqout) {
+	wcscon (wcs->syswcs, *sysout, wcs->equinox, *eqout, &ra1, &dec1, wcs->epoch);
+	if (wcs->coorflip) {
+	    wcs->yref = ra1;
+	    wcs->xref = dec1;
+	    }
+	else {
+	    wcs->xref = ra1;
+	    wcs->yref = dec1;
+	    }
+	}
+
+    /* Compute plate scale to return if it was not set on the command line */
+    if (secpix0 <= 0.0) {
+	pix2wcs (wcs, wcs->xrefpix-0.5, wcs->yrefpix, &ra1, &dec1);
+	pix2wcs (wcs, wcs->xrefpix+0.5, wcs->yrefpix, &ra2, &dec2);
+	*secpix = 3600.0 * wcsdist (ra1, dec1, ra2, dec2);
+	}
+
+    wcs->crval[0] = wcs->xref;
+    wcs->crval[1] = wcs->yref;
+    if (wcs->coorflip) {
+	wcs->cel.ref[0] = wcs->crval[1];
+	wcs->cel.ref[1] = wcs->crval[0];
+	}
+    else {
+	wcs->cel.ref[0] = wcs->crval[0];
+	wcs->cel.ref[1] = wcs->crval[1];
+	}
+
+    if (wcs->syswcs > 0 && wcs->syswcs != 6 && wcs->syswcs != 10) {
+	wcs->cel.flag = 0;
+	wcs->wcsl.flag = 0;
+	}
+    else {
+	wcs->lin.flag = LINSET;
+	wcs->wcsl.flag = WCSSET;
+	}
+
+    wcs->equinox = *eqout;
+    wcs->syswcs = *sysout;
+    wcs->sysout = *sysout;
+    wcs->eqout = *eqout;
+    wcs->sysin = *sysout;
+    wcs->eqin = *eqout;
+    wcscstr (cstr,*sysout,*eqout,wcs->epoch);
+    strcpy (wcs->radecsys, cstr);
+    strcpy (wcs->radecout, cstr);
+    strcpy (wcs->radecin, cstr);
+    wcsininit (wcs, wcs->radecsys);
+    wcsoutinit (wcs, wcs->radecsys);
+
+    naxes = wcs->naxis;
+    if (naxes < 1 || naxes > 9) {
+	naxes = wcs->naxes;
+	wcs->naxis = naxes;
+	}
+
+    if (usecdelt) {
+	hputnr8 (header, "CDELT1", 9, wcs->xinc);
+	if (naxes > 1) {
+	    hputnr8 (header, "CDELT2", 9, wcs->yinc);
+	    hputnr8 (header, "CROTA2", 9, wcs->rot);
+	    }
+	hdel (header, "CD1_1");
+	hdel (header, "CD1_2");
+	hdel (header, "CD2_1");
+	hdel (header, "CD2_2");
+	}
+    else {
+	hputnr8 (header, "CD1_1", 9, wcs->cd[0]);
+	if (naxes > 1) {
+	    hputnr8 (header, "CD1_2", 9, wcs->cd[1]);
+	    hputnr8 (header, "CD2_1", 9, wcs->cd[2]);
+	    hputnr8 (header, "CD2_2", 9, wcs->cd[3]);
+	    }
+	}
+
+    /* Print reference pixel position and value */
+    if (verbose) {
+	if (wcs->degout) {
+	    deg2str (rstr, 32, ra1, 6);
+            deg2str (dstr, 32, dec1, 6);
+	    }
+	else {
+	    ra2str (rstr, 32, ra1, 3);
+            dec2str (dstr, 32, dec1, 2);
+	    }
+	wcscstr (cstr,*sysout,*eqout,wcs->epoch);
+	fprintf (stderr,"Reference pixel (%.2f,%.2f) %s %s %s\n",
+		wcs->xrefpix, wcs->yrefpix, rstr, dstr, cstr);
+	}
+
+    /* Image size for catalog search */
+    if (verbose) {
+	if (wcs->degout) {
+	    deg2str (rstr, 32, *cra, 6);
+            deg2str (dstr, 32, *cdec, 6);
+	    }
+	else {
+	    ra2str (rstr, 32, *cra, 3);
+	    dec2str (dstr, 32, *cdec, 2);
+	    }
+	wcscstr (cstr, *sysout, *eqout, wcs->epoch);
+	fprintf (stderr,"Search at %s %s %s", rstr, dstr, cstr);
+	if (wcs->degout) {
+	    deg2str (rstr, 32, *dra, 6);
+            deg2str (dstr, 32, *ddec, 6);
+	    }
+	else {
+	    ra2str (rstr, 32, *dra, 3);
+	    dec2str (dstr, 32, *ddec, 2);
+	    }
+	fprintf (stderr," +- %s %s\n", rstr, dstr);
+	fprintf (stderr,"Image width=%d height=%d, %g arcsec/pixel\n",
+				*wp, *hp, *secpix);
+	}
+
+    return (wcs);
+}
+
+
+/* ChangeFITSWCS: Modify FITS WCS header from command line values */
+
+struct WorldCoor *
+ChangeFITSWCS (filename, header, verbose)
+
+char	*filename;	/* FITS or IRAF file name */
+char	*header;	/* Image FITS header */
+int	verbose;	/* Extra printing if =1 */
+{
+    int nax, i, hp, wp;
+    double xref, yref, degpix, secpix;
+    struct WorldCoor *wcs;
+    char temp[16];
+    char *cwcs;
+
+    /* Set the world coordinate system from the image header */
+    if (strlen (filename) > 0) {
+	cwcs = strchr (filename, '%');
+	if (cwcs != NULL)
+	    cwcs++;
+	}
+    if (!strncmp (header, "END", 3)) {
+	cwcs = NULL;
+	for (i = 0; i < 2880; i++)
+	    header[i] = (char) 32;
+	hputl (header, "SIMPLE", 1);
+	hputi4 (header, "BITPIX", 0);
+	hputi4 (header, "NAXIS", 2);
+	hputi4 (header, "NAXIS1", 1);
+	hputi4 (header, "NAXIS2", 1);
+	}
+
+    /* Set image dimensions */
+    nax = 0;
+    if (hp0 > 0 || wp0 > 0) {
+	hp = hp0;
+	wp = wp0;
+	if (hp > 0 && wp > 0)
+	    nax = 2;
+	else
+	    nax = 1;
+	hputi4 (header, "NAXIS", nax);
+	hputi4 (header, "NAXIS1", wp);
+	hputi4 (header, "NAXIS2", hp);
+	}
+    else if (hgeti4 (header,"NAXIS",&nax) < 1 || nax < 1) {
+	if (hgeti4 (header, "WCSAXES", &nax) < 1)
+	    return (NULL);
+	else {
+	    if (hgeti4 (header, "IMAGEW", &wp) < 1)
+		return (NULL);
+	    if (hgeti4 (header, "IMAGEH", &wp) < 1)
+		return (NULL);
+	    }
+	}
+    else {
+	if (hgeti4 (header,"NAXIS1",&wp) < 1)
+	    return (NULL);
+	if (hgeti4 (header,"NAXIS2",&hp) < 1)
+	    return (NULL);
+	}
+
+    /* Set plate center from command line, if it is there */
+    if (ra0 > -99.0 && dec0 > -99.0) {
+	hputnr8 (header, "CRVAL1" ,8,ra0);
+	hputnr8 (header, "CRVAL2" ,8,dec0);
+	hputra (header, "RA", ra0);
+	hputdec (header, "DEC", dec0);
+	if (comsys == WCS_B1950) {
+	    hputi4 (header, "EPOCH", 1950);
+	    hputi4 (header, "EQUINOX", 1950);
+	    hputs (header, "RADECSYS", "FK4");
+	    }
+	else {
+	    hputi4 (header, "EPOCH", 2000);
+	    hputi4 (header, "EQUINOX", 2000);
+	    if (comsys == WCS_GALACTIC)
+		hputs (header, "RADECSYS", "GALACTIC");
+	    else if (comsys == WCS_ECLIPTIC)
+		hputs (header, "RADECSYS", "ECLIPTIC");
+	    else if (comsys == WCS_ICRS)
+		hputs (header, "RADECSYS", "ICRS");
+	    else
+		hputs (header, "RADECSYS", "FK5");
+	    }
+	if (hgetr8 (header, "SECPIX", &secpix)) {
+	    degpix = secpix / 3600.0;
+	    hputnr8 (header, "CDELT1", 8, -degpix);
+	    hputnr8 (header, "CDELT2", 8, degpix);
+	    hdel (header, "CD1_1");
+	    hdel (header, "CD1_2");
+	    hdel (header, "CD2_1");
+	    hdel (header, "CD2_2");
+	    } 
+	}
+    if (ptype0 > -1 && ptype0 < nctype) {
+	strcpy (temp,"RA---");
+	strcat (temp, ctypes[ptype0]);
+	hputs (header, "CTYPE1", temp);
+	strcpy (temp,"DEC--");
+	strcat (temp, ctypes[ptype0]);
+	hputs (header, "CTYPE2", temp);
+	}
+
+    /* Set reference pixel from command line, if it is there */
+    if (xref0 > -99999.0 && yref0 > -99999.0) {
+	hputr8 (header, "CRPIX1", xref0);
+	hputr8 (header, "CRPIX2", yref0);
+	}
+    else if (hgetr8 (header, "CRPIX1", &xref) < 1) {
+	xref = 0.5 + (double) wp / 2.0;
+	yref = 0.5 + (double) hp / 2.0;
+	hputnr8 (header, "CRPIX1", 3, xref);
+	hputnr8 (header, "CRPIX2", 3, yref);
+	}
+
+    /* Set plate scale from command line, if it is there */
+    if (secpix0 != 0.0 || cd0 != NULL) {
+	if (secpix2 != 0.0) {
+	    secpix = 0.5 * (secpix0 + secpix2);
+	    hputnr8 (header, "SECPIX1", 5, secpix0);
+	    hputnr8 (header, "SECPIX2", 5, secpix2);
+	    degpix = -secpix0 / 3600.0;
+	    hputnr8 (header, "CDELT1", 8, degpix);
+	    degpix = secpix2 / 3600.0;
+	    hputnr8 (header, "CDELT2", 8, degpix);
+	    hdel (header, "CD1_1");
+	    hdel (header, "CD1_2");
+	    hdel (header, "CD2_1");
+	    hdel (header, "CD2_2");
+	    }
+	else if (secpix0 != 0.0) {
+	    secpix = secpix0;
+	    hputnr8 (header, "SECPIX", 5, secpix);
+	    degpix = secpix / 3600.0;
+	    hputnr8 (header, "CDELT1", 8, -degpix);
+	    hputnr8 (header, "CDELT2", 8, degpix);
+	    hdel (header, "CD1_1");
+	    hdel (header, "CD1_2");
+	    hdel (header, "CD2_1");
+	    hdel (header, "CD2_2");
+	    }
+	else {
+	    hputr8 (header, "CD1_1", cd0[0]);
+	    hputr8 (header, "CD1_2", cd0[1]);
+	    hputr8 (header, "CD2_1", cd0[2]);
+	    hputr8 (header, "CD2_2", cd0[3]);
+	    hdel (header, "CDELT1");
+	    hdel (header, "CDELT2");
+	    hdel (header, "CROTA1");
+	    hdel (header, "CROTA2");
+	    }
+	if (!ksearch (header,"CRVAL1")) {
+	    hgetra (header, "RA", &ra0);
+	    hgetdec (header, "DEC", &dec0);
+	    hputnr8 (header, "CRVAL1", 8, ra0);
+	    hputnr8 (header, "CRVAL2", 8, dec0);
+	    }
+	if (!ksearch (header,"CRPIX1")) {
+	    xref = (double) wp / 2.0;
+	    yref = (double) hp / 2.0;
+	    hputnr8 (header, "CRPIX1", 3, xref);
+	    hputnr8 (header, "CRPIX2", 3, yref);
+	    }
+	if (!ksearch (header,"CTYPE1")) {
+	    if (comsys == WCS_GALACTIC) {
+		hputs (header, "CTYPE1", "GLON-TAN");
+		hputs (header, "CTYPE2", "GLAT-TAN");
+		}
+	    else {
+		hputs (header, "CTYPE1", "RA---TAN");
+		hputs (header, "CTYPE2", "DEC--TAN");
+		}
+	    }
+	}
+
+    /* Set rotation angle from command line, if it is there */
+    if (rot0 < 361.0) {
+	hputnr8 (header, "CROTA1", 5, rot0);
+	hputnr8 (header, "CROTA2", 5, rot0);
+	}
+
+    /* Set observation date for epoch, if it is there */
+    if (dateobs0 != NULL)
+	hputs (header, "DATE-OBS", dateobs0);
+
+    /* Initialize WCS structure from FITS header */
+    wcs = wcsinitn (header, cwcs);
+
+    /* If incomplete WCS in header, drop out */
+    if (nowcs (wcs)) {
+	setwcsfile (filename);
+	/* wcserr(); */
+	if (verbose)
+	    fprintf (stderr,"Insufficient information for initial WCS\n");
+	return (NULL);
+	}
+    return (wcs);
+}
+
+void
+setcdelt()			/* Set flag to use CDELTn, not CD matrix */
+{usecdelt = 1; return;}
+
+void
+setnpix (nx, ny)		/* Set image size */
+int nx, ny;
+{ wp0 = nx; hp0 = ny; return; }
+
+void
+setrot (rot)
+double rot;
+{ rot0 = rot; return; }
+
+void
+setsecpix (secpix)		/* Set first axis arcseconds per pixel */
+double secpix;
+{ secpix0 = secpix; return; }
+
+double
+getsecpix ()		/* Return first axis arcseconds per pixel */
+{ return (secpix0); }
+
+void
+setsecpix2 (secpix)		/* Set second axis arcseconds per pixel */
+double secpix;
+{ secpix2 = secpix; return; }
+
+void
+setcd (cd)		/* Set initial CD matrix */
+double *cd;
+{ int i;
+  if (cd0 != NULL) free (cd0);
+  cd0 = (double *) calloc (4, sizeof (double));
+  for (i = 0; i < 4; i++) cd0[i] = cd[i];
+  return; }
+
+void
+setsys (comsys0)		/* Set WCS coordinates as FK4 */
+int comsys0;
+{ comsys = comsys0; return; }
+
+void
+setcenter (rastr, decstr)	/* Set center sky coordinates in strings */
+char *rastr, *decstr;
+{
+    ra0 = str2ra (rastr);
+    dec0 = str2dec (decstr);
+    return;
+}
+
+void
+setdcenter (ra, dec)		/* Set center sky coordinates in degrees */
+double ra, dec;
+{ ra0 = ra; dec0 = dec; return; }
+
+void
+getcenter (ra, dec)		/* Return initial reference sky coordinates */
+double *ra, *dec;
+{ *ra = ra0; *dec = dec0; return; }
+
+void
+setrefpix (x, y)		/* Set reference pixel image coordinates */
+double x, y;
+{ xref0 = x; yref0 = y; return; }
+
+void
+getrefpix (x, y)		/* Return initial ref pixel image coordinates */
+double *x, *y;
+{ *x = xref0; *y = yref0; return; }
+
+void
+setproj (ptype)
+char*	ptype;
+{
+    int i;
+
+    /* Set up array of projection types */
+    strcpy (ctypes[0], "LIN");
+    strcpy (ctypes[1], "AZP");
+    strcpy (ctypes[2], "SZP");
+    strcpy (ctypes[3], "TAN");
+    strcpy (ctypes[4], "SIN");
+    strcpy (ctypes[5], "STG");
+    strcpy (ctypes[6], "ARC");
+    strcpy (ctypes[7], "ZPN");
+    strcpy (ctypes[8], "ZEA");
+    strcpy (ctypes[9], "AIR");
+    strcpy (ctypes[10], "CYP");
+    strcpy (ctypes[11], "CAR");
+    strcpy (ctypes[12], "MER");
+    strcpy (ctypes[13], "CEA");
+    strcpy (ctypes[14], "COP");
+    strcpy (ctypes[15], "COD");
+    strcpy (ctypes[16], "COE");
+    strcpy (ctypes[17], "COO");
+    strcpy (ctypes[18], "BON");
+    strcpy (ctypes[19], "PCO");
+    strcpy (ctypes[20], "SFL");
+    strcpy (ctypes[21], "PAR");
+    strcpy (ctypes[22], "AIT");
+    strcpy (ctypes[23], "MOL");
+    strcpy (ctypes[24], "CSC");
+    strcpy (ctypes[25], "QSC");
+    strcpy (ctypes[26], "TSC");
+    strcpy (ctypes[27], "NCP");
+    strcpy (ctypes[28], "GLS");
+    strcpy (ctypes[29], "DSS");
+    strcpy (ctypes[30], "PLT");
+    strcpy (ctypes[31], "TNX");
+
+    ptype0 = -1;
+    for (i = 0; i < nctype; i++) {
+	if (!strcasecmp (ptype, ctypes[i]))
+	    ptype0 = i;
+	}
+    return;
+}
+
+void
+setdateobs (dateobs)
+char *dateobs;
+{ dateobs0 = calloc (strlen (dateobs), sizeof (char));
+  strcpy (dateobs0, dateobs);
+  return; }
+
+
+/* Feb 29 1996	New program
+ * May 23 1996	Use pre-existing WCS for center, if it is present
+ * May 29 1996	Simplify program by always using WCS structure
+ * Jun 12 1996	Be more careful with nominal WCS setting
+ * Jul  3 1996	Set epoch from old equinox if not already set
+ * Jul 19 1996	Set image center in WCS if DSS WCS
+ * Aug  5 1996	Check for SECPIX1 as well as SECPIX
+ * Aug  7 1996	Save specified number of decimal places in header parameters
+ * Aug  7 1996	Rename old center parameters
+ * Aug 26 1996	Decrease default pixel tolerance from 20 to 10
+ * Sep  1 1996	Set plate scale default in lwcs.h
+ * Sep  3 1996	Fix bug to set plate scale from command line
+ * Oct 15 1996	Break off from imsetwcs.c
+ * Oct 16 1996	Clean up center setting so eqref is used
+ * Oct 17 1996	Do not print error messages unless verbose is set
+ * Oct 30 1996	Keep equinox from image if EQREF is zero
+ * Nov  1 1996	Declare undeclared subroutines; remove unused variables
+ * Nov  4 1996	Add reference pixel and projection to wcsset() call
+ * Nov 14 1996	Add GetLimits() to deal with search limits around the poles
+ * Nov 15 1996	Drop GetLimits(); code moved to individual catalog routines
+ * Dec 10 1996	Fix precession and make equinox double
+
+ * Feb 19 1997	If eqout is 0, use equinox of image
+ * Feb 24 1997	Always convert center to output equinox (bug fix)
+ * Mar 20 1997	Declare EQ2 double instead of int, fixing a bug
+ * Jul 11 1997	Allow external (command line) setting of reference pixel coords
+ * Sep 26 1997	Set both equinox and epoch if input center coordinates
+ * Nov  3 1997	Separate WCS reference pixel from search center
+ * Dec  8 1997	Set CDELTn using SECPIX if it is in the header
+ *
+ * Jan  6 1998	Do not print anything unless verbose is set
+ * Jan 29 1998	Use flag to allow AIPS classic WCS subroutines
+ * Mar  1 1998	Set x and y plate scales from command line if there are two
+ * Mar  2 1998	Do not reset plate solution switch
+ * Mar  6 1998	Add option to reset center sky coordinates in degrees
+ * Mar  6 1998	Add option to set projection type
+ * Mar 18 1998	Initialize reference pixel if CDELTn's being set
+ * Mar 20 1998	Only set CTYPEn if CDELTn or CRVALn are set
+ * Apr 10 1998	Add option to set search area to circle or box
+ * Apr 20 1998	Move GetArea() to scat.c
+ * Apr 24 1998	Always convert image reference coordinate to catalog equinox
+ * Apr 28 1998	Change coordinate system flags to WCS_*
+ * Jun  1 1998	Print error message if WCS cannot be initialized
+ * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
+ * Jun 25 1998	Leave use of AIPS wcs to wcs.c file
+ * Sep 17 1998	Add sysout to argument list and use scscon() for conversion
+ * Sep 25 1998	Make sysout==0 indicate output in image coordinate system
+ * Oct 28 1998	Set coordinate system properly to sysout/eqout in GetFITSWCS()
+ *
+ * Apr  7 1999	Add filename argument to GetFITSWCS
+ * Apr 29 1999	Add option to set image size
+ * Jun  2 1999	Fix sign of CDELT1 if secpix2 and secpix0 are set
+ * Jul  7 1999	Fix conversion of center coordinates to refsys
+ * Jul  9 1999	Fix bug which reset command-line-set reference pixel coordinate
+ * Oct 21 1999	Fix declarations after lint
+ * Nov  1 1999	Add option to write CD matrix
+ * Nov  1 1999	If CDELTn set from command line delete previous header CD matrix
+ * Nov 12 1999	Add galactic coordinates as command line option
+ * Nov 16 1999	Set radecsys correctly for command line galactic
+ *
+ * Feb 15 2000	Add option to override the header CD matrix (like CDELTs)
+ * Feb 29 2000	Fix bug, converting reference pixel WCS coordinates everywhere
+ * Mar 27 2000	Drop unused subroutine setradius()
+ * May 24 2000	Print degrees in debugging messages if output format
+ * Sep 14 2000	Set xinc and yinc correctly if center pixel in header
+ *
+ * Jan 11 2001	All printing to stderr
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ * Oct 19 2001	Allow DATE-OBS to be set
+ *
+ * Apr  3 2002	Update projection types to match list in wcs.h and wcs.c
+ * Aug  2 2002	Add getsecpix(), getrefpix(), getcneter() to return presets
+ *
+ * Mar 25 2003	Fix GetFITSWCS() to return correct search area for rotated image
+ * Mar 25 2003	Write out CTYPEn with quotes
+ * Mar 27 2003	Fix half-pixel bug in computation of image center
+ * Apr  3 2003	Drop unused variables after lint
+ * Jun  6 2003	Set xref and yref to center if -999, not 0
+ * Jul 21 2003	Fix bug setting secpix if it was not set on the command line
+ *		(found by Takehiko Wada, ISAS)
+ * Sep 23 2003	In setproj(), use strcasecmp() instead of strcmp()
+ * Sep 26 2003	If reference pixel not set, set center correctly
+ * Oct  6 2003	Change wcs->naxes to wcs->naxis to match WCSLIB 3.2
+ * Dec  3 2003	Add wcs->naxes back as an alternative
+ *
+ * Jul 26 2004	Fix image size when wrapping around RA=0:00
+ * Sep 16 2004	Fix verbose mode search size in GetFITSWCS()
+ * Oct 29 2004	Fix problem setting RA size from limits
+ *
+ * Jan 20 2005	Fix cel.ref assignment if axis are switched
+ * Jul 20 2005	Fix bug which reversed dimensions when setting image size
+ * Jul 21 2005	Fix bug which caused bad results at RA ~= 0.0
+ * Aug 30 2005	Implement multiple WCS's, though not modification thereof, yet
+ * Nov  1 2005	Set RADECSYS to ICRS if appropriate
+ *
+ * Oct 30 2006	Do not precess LINEAR or XY coordinates
+ *
+ * Jun  1 2007	In GetFITSWCS, deal with no input file
+ * Jun  5 2007	Add ChangeFITSWCS to set header WCS arguments and WCS
+ * Jul  3 2007	Fix bug by setting hp and wp
+ * Jul 26 2007	If first line of header is END, initialize other needed values
+ * Oct 19 2007	Return NULL from GetFITSWCS() immediately if no WCS in header
+ *
+ * Mar 24 2009	Set dimensions from IMAGEW and IMAGEH if WCSAXES > 0
+ */
diff --git a/Code/src/libwcs/imhfile.c b/Code/src/libwcs/imhfile.c
new file mode 100644
index 0000000000000000000000000000000000000000..a4d58eedaf0588e40a11ee3093851b584d2be85c
--- /dev/null
+++ b/Code/src/libwcs/imhfile.c
@@ -0,0 +1,1933 @@
+/*** File imhfile.c
+ *** January 8, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:      imhfile.c (IRAF .imh image file reading and writing)
+ * Purpose:     Read and write IRAF image files (and translate headers)
+ * Subroutine:  check_immagic (irafheader, teststring )
+ *		Verify that file is valid IRAF imhdr or impix
+ * Subroutine:  irafrhead (filename, lfhead, fitsheader, lihead)
+ *              Read IRAF image header
+ * Subroutine:  irafrimage (fitsheader)
+ *              Read IRAF image pixels (call after irafrhead)
+ * Subroutine:	same_path (pixname, hdrname)
+ *		Put filename and header path together
+ * Subroutine:	iraf2fits (hdrname, irafheader, nbiraf, nbfits)
+ *		Convert IRAF image header to FITS image header
+ * Subroutine:	irafwhead (hdrname, irafheader, fitsheader)
+ *		Write IRAF header file
+ * Subroutine:	irafwimage (hdrname, irafheader, fitsheader, image )
+ *		Write IRAF image and header files
+ * Subroutine:	fits2iraf (fitsheader, irafheader)
+ *		Convert FITS image header to IRAF image header
+ * Subroutine:  irafgeti4 (irafheader, offset)
+ *		Get 4-byte integer from arbitrary part of IRAF header
+ * Subroutine:  irafgetc2 (irafheader, offset)
+ *		Get character string from arbitrary part of IRAF v.1 header
+ * Subroutine:  irafgetc (irafheader, offset)
+ *		Get character string from arbitrary part of IRAF header
+ * Subroutine:  iraf2str (irafstring, nchar)
+ * 		Convert 2-byte/char IRAF string to 1-byte/char string
+ * Subroutine:  str2iraf (string, irafstring, nchar)
+ * 		Convert 1-byte/char string to IRAF 2-byte/char string
+ * Subroutine:	irafswap (bitpix,string,nbytes)
+ *		Swap bytes in string in place, with FITS bits/pixel code
+ * Subroutine:	irafswap2 (string,nbytes)
+ *		Swap bytes in string in place
+ * Subroutine	irafswap4 (string,nbytes)
+ *		Reverse bytes of Integer*4 or Real*4 vector in place
+ * Subroutine	irafswap8 (string,nbytes)
+ *		Reverse bytes of Real*8 vector in place
+ * Subroutine	irafsize (filename)
+ *		Return length of file in bytes
+ * Subroutine	isiraf (filename)
+ *		Return 1 if IRAF .imh file, else 0
+
+
+ * Copyright:   2000 Smithsonian Astrophysical Observatory
+ *              You may do anything you like with this file except remove
+ *              this copyright.  The Smithsonian Astrophysical Observatory
+ *              makes no representations about the suitability of this
+ *              software for any purpose.  It is provided "as is" without
+ *              express or implied warranty.
+ */
+
+#include <stdio.h>		/* define stderr, FD, and NULL */
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include "fitsfile.h"
+
+/* Parameters from iraf/lib/imhdr.h for IRAF version 1 images */
+#define SZ_IMPIXFILE	 79		/* name of pixel storage file */
+#define SZ_IMHDRFILE	 79   		/* length of header storage file */
+#define SZ_IMTITLE	 79		/* image title string */
+#define LEN_IMHDR	2052		/* length of std header */
+
+/* Parameters from iraf/lib/imhdr.h for IRAF version 2 images */
+#define	SZ_IM2PIXFILE	255		/* name of pixel storage file */
+#define	SZ_IM2HDRFILE	255		/* name of header storage file */
+#define	SZ_IM2TITLE	383		/* image title string */
+#define LEN_IM2HDR	2046		/* length of std header */
+
+/* Offsets into header in bytes for parameters in IRAF version 1 images */
+#define IM_HDRLEN	 12		/* Length of header in 4-byte ints */
+#define IM_PIXTYPE       16             /* Datatype of the pixels */
+#define IM_NDIM          20             /* Number of dimensions */
+#define IM_LEN           24             /* Length (as stored) */
+#define IM_PHYSLEN       52             /* Physical length (as stored) */
+#define IM_PIXOFF        88             /* Offset of the pixels */
+#define IM_CTIME        108             /* Time of image creation */
+#define IM_MTIME        112             /* Time of last modification */
+#define IM_LIMTIME      116             /* Time of min,max computation */
+#define IM_MAX          120             /* Maximum pixel value */
+#define IM_MIN          124             /* Maximum pixel value */
+#define IM_PIXFILE      412             /* Name of pixel storage file */
+#define IM_HDRFILE      572             /* Name of header storage file */
+#define IM_TITLE        732             /* Image name string */
+
+/* Offsets into header in bytes for parameters in IRAF version 2 images */
+#define IM2_HDRLEN	  6		/* Length of header in 4-byte ints */
+#define IM2_PIXTYPE      10             /* Datatype of the pixels */
+#define IM2_SWAPPED      14             /* Pixels are byte swapped */
+#define IM2_NDIM         18             /* Number of dimensions */
+#define IM2_LEN          22             /* Length (as stored) */
+#define IM2_PHYSLEN      50             /* Physical length (as stored) */
+#define IM2_PIXOFF       86             /* Offset of the pixels */
+#define IM2_CTIME       106             /* Time of image creation */
+#define IM2_MTIME       110             /* Time of last modification */
+#define IM2_LIMTIME     114             /* Time of min,max computation */
+#define IM2_MAX         118             /* Maximum pixel value */
+#define IM2_MIN         122             /* Maximum pixel value */
+#define IM2_PIXFILE     126             /* Name of pixel storage file */
+#define IM2_HDRFILE     382             /* Name of header storage file */
+#define IM2_TITLE       638             /* Image name string */
+
+/* Codes from iraf/unix/hlib/iraf.h */
+#define	TY_CHAR		2
+#define	TY_SHORT	3
+#define	TY_INT		4
+#define	TY_LONG		5
+#define	TY_REAL		6
+#define	TY_DOUBLE	7
+#define	TY_COMPLEX	8
+#define TY_POINTER      9
+#define TY_STRUCT       10
+#define TY_USHORT       11
+#define TY_UBYTE        12
+
+#define LEN_IRAFHDR	25000
+#define LEN_PIXHDR	1024
+#define LEN_FITSHDR	11520
+
+int check_immagic();
+int irafgeti4();
+float irafgetr4();
+char *irafgetc2();
+char *irafgetc();
+char *iraf2str();
+static char *same_path();
+static void irafputr4();
+static void irafputi4();
+static void irafputc2();
+static void irafputc();
+static void str2iraf();
+static int headswap=-1;	/* =1 to swap data bytes of foreign IRAF file */
+static void irafswap();
+static void irafswap2();
+static void irafswap4();
+static void irafswap8();
+int head_version ();
+int pix_version ();
+int irafncmp ();
+static int machswap();
+static int irafsize();
+
+#define SECONDS_1970_TO_1980    315532800L
+
+/* Subroutine:	irafrhead
+ * Purpose:	Open and read the iraf .imh file, translating it to FITS, too.
+ * Returns:	NULL if failure, else pointer to IRAF .imh image header
+ * Notes:	The imhdr format is defined in iraf/lib/imhdr.h, some of
+ *		which defines or mimicked, above.
+ */
+
+char *
+irafrhead (filename, lihead)
+
+char	*filename;	/* Name of IRAF header file */
+int	*lihead;	/* Length of IRAF image header in bytes (returned) */
+{
+    FILE *fd;
+    int nbr;
+    char *irafheader;
+    int nbhead, nbytes;
+    int imhver;
+
+    headswap = -1;
+    *lihead = 0;
+
+    /* open the image header file */
+    fd = fopen (filename, "rb");
+    if (fd == NULL) {
+	fprintf (stderr, "IRAFRHEAD:  cannot open file %s to read\n", filename);
+	return (NULL);
+	}
+
+    /* Find size of image header file */
+    if ((nbhead = irafsize (fd)) <= 0) {
+	fprintf (stderr, "IRAFRHEAD:  cannot read file %s, size = %d\n",
+		 filename, nbhead);
+	return (NULL);
+	}
+
+    /* allocate initial sized buffer */
+    nbytes = nbhead + 5000;
+    irafheader = (char *) calloc (nbytes/4, 4);
+    if (irafheader == NULL) {
+	(void)fprintf(stderr, "IRAFRHEAD Cannot allocate %d-byte header\n",
+		      nbytes);
+	return (NULL);
+	}
+    *lihead = nbytes;
+
+    /* Read IRAF header */
+    nbr = fread (irafheader, 1, nbhead, fd);
+    fclose (fd);
+
+    /* Reject if header less than minimum length */
+    if (nbr < LEN_PIXHDR) {
+	(void)fprintf(stderr, "IRAFRHEAD header file %s: %d / %d bytes read.\n",
+		      filename,nbr,LEN_PIXHDR);
+	free (irafheader);
+	return (NULL);
+	}
+
+    /* Check header magic word */
+    imhver = head_version (irafheader);
+    if (imhver < 1) {
+	free (irafheader);
+	(void)fprintf(stderr, "IRAFRHEAD: %s is not a valid IRAF image header\n",
+		      filename);
+	return(NULL);
+	}
+
+    /* check number of image dimensions
+    if (imhver == 2)
+	ndim = irafgeti4 (irafheader, IM2_NDIM])
+    else
+	ndim = irafgeti4 (irafheader, IM_NDIM])
+    if (ndim < 2) {
+	free (irafheader);
+	(void)fprintf(stderr, "File %s does not contain 2d image\n", filename);
+	return (NULL);
+	} */
+
+    return (irafheader);
+}
+
+
+char *
+irafrimage (fitsheader)
+
+char	*fitsheader;	/* FITS image header (filled) */
+{
+    FILE *fd;
+    char *bang;
+    int naxis, naxis1, naxis2, naxis3, npaxis1, npaxis2,bitpix, bytepix, pixswap, i;
+    char *image;
+    int nbr, nbimage, nbaxis, nbl, nbdiff, lpname;
+    char *pixheader;
+    char *linebuff, *pixchar;
+    int imhver, lpixhead, len;
+    char pixname[SZ_IM2PIXFILE+1];
+    char newpixname[SZ_IM2HDRFILE+1];
+
+    /* Convert pixel file name to character string */
+    hgetm (fitsheader, "PIXFIL", SZ_IM2PIXFILE, pixname);
+
+    /* Drop trailing spaces */
+    lpname = strlen (pixname);
+    pixchar = pixname + lpname - 1;
+    while (*pixchar == ' ')
+	*pixchar = (char) 0;
+
+    hgeti4 (fitsheader, "PIXOFF", &lpixhead);
+
+    /* Open pixel file, ignoring machine name if present */
+    if ((bang = strchr (pixname, '!')) != NULL )
+	fd = fopen (bang + 1, "rb");
+    else
+	fd = fopen (pixname, "rb");
+
+    /* If not at pathname in header, try same directory as header file */
+    if (!fd) {
+	hgetm (fitsheader, "IMHFIL", SZ_IM2HDRFILE, newpixname);
+	len = strlen (newpixname);
+	newpixname[len-3] = 'p';
+	newpixname[len-2] = 'i';
+	newpixname[len-1] = 'x';
+	fd = fopen (newpixname, "rb");
+	}
+
+    /* Print error message and exit if pixel file is not found */
+    if (!fd) {
+	(void)fprintf(stderr,
+	     "IRAFRIMAGE: Cannot open IRAF pixel file %s\n", pixname);
+	return (NULL);
+	}
+
+    /* Read pixel header */
+    pixheader = (char *) calloc (lpixhead/4, 4);
+    if (pixheader == NULL) {
+	(void)fprintf(stderr, "IRAFRIMAGE Cannot allocate %d-byte pixel header\n",
+		lpixhead);
+	return (NULL);
+	}
+    nbr = fread (pixheader, 1, lpixhead, fd);
+
+    /* Check size of pixel header */
+    if (nbr < lpixhead) {
+	(void)fprintf(stderr, "IRAF pixel file %s: %d / %d bytes read.\n",
+		      pixname,nbr,LEN_PIXHDR);
+	free (pixheader);
+	fclose (fd);
+	return (NULL);
+	}
+
+    /* check pixel header magic word */
+    imhver = pix_version (pixheader);
+    if (imhver < 1) {
+	(void)fprintf(stderr, "File %s not valid IRAF pixel file.\n", pixname);
+	free (pixheader);
+	fclose (fd);
+	return(NULL);
+	}
+    free (pixheader);
+
+    /* Find number of bytes to read */
+    hgeti4 (fitsheader,"NAXIS",&naxis);
+    hgeti4 (fitsheader,"NAXIS1",&naxis1);
+    hgeti4 (fitsheader,"NAXIS2",&naxis2);
+    hgeti4 (fitsheader,"NPAXIS1",&npaxis1);
+    hgeti4 (fitsheader,"NPAXIS2",&npaxis2);
+    hgeti4 (fitsheader,"BITPIX",&bitpix);
+    if (bitpix < 0)
+	bytepix = -bitpix / 8;
+    else
+	bytepix = bitpix / 8;
+
+    /* If either dimension is one and image is 3-D, read all three dimensions */
+    if (naxis == 3 && ((naxis1 == 1) | (naxis2 == 1))) {
+	hgeti4 (fitsheader,"NAXIS3",&naxis3);
+	nbimage = naxis1 * naxis2 * naxis3 * bytepix;
+	}
+    else {
+	nbimage = naxis1 * naxis2 * bytepix;
+	naxis3 = 1;
+	}
+
+    if (bytepix > 4)
+	image =  (char *) calloc (nbimage/8, 8);
+    else if (bytepix > 2)
+	image =  (char *) calloc (nbimage/4, 4);
+    else if (bytepix > 1)
+	image =  (char *) calloc (nbimage/2, 2);
+    else
+	image =  (char *) calloc (nbimage, 1);
+    if (image == NULL) {
+	(void)fprintf(stderr, "IRAFRIMAGE Cannot allocate %d-byte image buffer\n",
+		nbimage);
+	return (NULL);
+	}
+
+    /* Read IRAF image all at once if physical and image dimensions are the same */
+    if (npaxis1 == naxis1)
+	nbr = fread (image, 1, nbimage, fd);
+
+    /* Read IRAF image one line at a time if physical and image dimensions differ */
+    else {
+	nbdiff = (npaxis1 - naxis1) * bytepix;
+	nbaxis = naxis1 * bytepix;
+	linebuff = image;
+	nbr = 0;
+	if (naxis2 == 1 && naxis3 > 1)
+	    naxis2 = naxis3;
+	for (i = 0; i < naxis2; i++) {
+	    nbl = fread (linebuff, 1, nbaxis, fd);
+	    nbr = nbr + nbl;
+	    (void) fseek (fd, nbdiff, SEEK_CUR);
+	    linebuff = linebuff + nbaxis;
+	    }
+	}
+    fclose (fd);
+
+    /* Check size of image */
+    if (nbr < nbimage) {
+	(void)fprintf(stderr, "IRAF pixel file %s: %d / %d bytes read.\n",
+		      pixname,nbr,nbimage);
+	free (image);
+	return (NULL);
+	}
+
+    /* Byte-reverse image, if necessary */
+    pixswap = 0;
+    hgetl (fitsheader, "PIXSWAP", &pixswap);
+    if (pixswap)
+	irafswap (bitpix, image, nbimage);
+
+    return (image);
+}
+
+
+/* Return IRAF image format version number from magic word in IRAF header*/
+
+int
+head_version (irafheader)
+
+char	*irafheader;	/* IRAF image header from file */
+
+{
+
+    /* Check header file magic word */
+    if (irafncmp (irafheader, "imhdr", 5) != 0 ) {
+	if (strncmp (irafheader, "imhv2", 5) != 0)
+	    return (0);
+	else
+	    return (2);
+	}
+    else
+	return (1);
+}
+
+
+/* Return IRAF image format version number from magic word in IRAF pixel file */
+
+int
+pix_version (irafheader)
+
+char	*irafheader;	/* IRAF image header from file */
+
+{
+
+    /* Check pixel file header magic word */
+    if (irafncmp (irafheader, "impix", 5) != 0) {
+	if (strncmp (irafheader, "impv2", 5) != 0)
+	    return (0);
+	else
+	    return (2);
+	}
+    else
+	return (1);
+}
+
+
+/* Verify that file is valid IRAF imhdr or impix by checking first 5 chars
+ * Returns:	0 on success, 1 on failure */
+
+int
+irafncmp (irafheader, teststring, nc)
+
+char	*irafheader;	/* IRAF image header from file */
+char	*teststring;	/* C character string to compare */
+int	nc;		/* Number of characters to compate */
+
+{
+    char *line;
+
+    headswap = -1;
+    if ((line = iraf2str (irafheader, nc)) == NULL)
+	return (1);
+    if (strncmp (line, teststring, nc) == 0) {
+	free (line);
+	return (0);
+	}
+    else {
+	free (line);
+	return (1);
+	}
+}
+
+/* Convert IRAF image header to FITS image header, returning FITS header */
+
+char *
+iraf2fits (hdrname, irafheader, nbiraf, nbfits)
+
+char	*hdrname;	/* IRAF header file name (may be path) */
+char	*irafheader;	/* IRAF image header */
+int	nbiraf;		/* Number of bytes in IRAF header */
+int	*nbfits;	/* Number of bytes in FITS header (returned) */
+
+{
+    char *objname;	/* object name from FITS file */
+    int lstr, i, j, k, ib, nax, nbits, nl;
+    int lname = 0;
+    char *pixname, *newpixname, *bang, *chead;
+    char *fitsheader;
+    int nblock, nlines;
+    char *fhead, *fhead1, *fp, endline[81];
+    char irafchar;
+    char fitsline[81];
+    char *dstring;
+    int pixtype;
+    int imhver, n, imu, pixoff, impixoff, immax, immin, imtime;
+    int imndim, imlen, imphyslen, impixtype, pixswap, hpixswap, mtime;
+    float rmax, rmin;
+
+    headswap = -1;
+
+    /* Set up last line of FITS header */
+    (void)strncpy (endline,"END", 3);
+    for (i = 3; i < 80; i++)
+	endline[i] = ' ';
+    endline[80] = 0;
+
+    /* Check header magic word */
+    imhver = head_version (irafheader);
+    if (imhver < 1) {
+	(void)fprintf(stderr, "File %s not valid IRAF image header\n",
+		      hdrname);
+	return(NULL);
+	}
+    if (imhver == 2) {
+	nlines = 24 + ((nbiraf - LEN_IM2HDR) / 81);
+	imndim = IM2_NDIM;
+	imlen = IM2_LEN;
+	imphyslen = IM2_PHYSLEN;
+	impixtype = IM2_PIXTYPE;
+	impixoff = IM2_PIXOFF;
+	imtime = IM2_MTIME;
+	immax = IM2_MAX;
+	immin = IM2_MIN;
+	}
+    else {
+	nlines = 24 + ((nbiraf - LEN_IMHDR) / 162);
+	imndim = IM_NDIM;
+	imlen = IM_LEN;
+	imphyslen = IM_PHYSLEN;
+	impixtype = IM_PIXTYPE;
+	impixoff = IM_PIXOFF;
+	imtime = IM_MTIME;
+	immax = IM_MAX;
+	immin = IM_MIN;
+	}
+
+    /*  Initialize FITS header */
+    nblock = (nlines * 80) / 2880;
+    *nbfits = (nblock + 5) * 2880 + 4;
+    fitsheader = (char *) calloc (*nbfits, 1);
+    if (fitsheader == NULL) {
+	(void)fprintf(stderr, "IRAF2FITS Cannot allocate %d-byte FITS header\n",
+		*nbfits);
+	return (NULL);
+	}
+    hlength (fitsheader, *nbfits);
+    fhead = fitsheader;
+    (void)strncpy (fitsheader, endline, 80);
+    hputl (fitsheader, "SIMPLE", 1);
+    fhead = fhead + 80;
+
+    /*  Set pixel size in FITS header */
+    pixtype = irafgeti4 (irafheader, impixtype);
+    switch (pixtype) {
+	case TY_CHAR:
+	    nbits = 8;
+	    break;
+	case TY_UBYTE:
+	    nbits = 8;
+	    break;
+	case TY_SHORT:
+	    nbits = 16;
+	    break;
+	case TY_USHORT:
+	    nbits = -16;
+	    break;
+	case TY_INT:
+	case TY_LONG:
+	    nbits = 32;
+	    break;
+	case TY_REAL:
+	    nbits = -32;
+	    break;
+	case TY_DOUBLE:
+	    nbits = -64;
+	    break;
+	default:
+	    (void)fprintf(stderr,"Unsupported data type: %d\n", pixtype);
+	    return (NULL);
+	}
+    hputi4 (fitsheader,"BITPIX",nbits);
+    hputcom (fitsheader,"BITPIX", "IRAF .imh pixel type");
+    fhead = fhead + 80;
+
+    /*  Set image dimensions in FITS header */
+    nax = irafgeti4 (irafheader, imndim);
+    hputi4 (fitsheader,"NAXIS",nax);
+    hputcom (fitsheader,"NAXIS", "IRAF .imh naxis");
+    fhead = fhead + 80;
+
+    n = irafgeti4 (irafheader, imlen);
+    hputi4 (fitsheader, "NAXIS1", n);
+    hputcom (fitsheader,"NAXIS1", "IRAF .imh image naxis[1]");
+    fhead = fhead + 80;
+
+    if (nax > 1) {
+	n = irafgeti4 (irafheader, imlen+4);
+	hputi4 (fitsheader, "NAXIS2", n);
+	hputcom (fitsheader,"NAXIS2", "IRAF .imh image naxis[2]");
+	}
+    else
+	hputi4 (fitsheader, "NAXIS2", 1);
+	hputcom (fitsheader,"NAXIS2", "IRAF .imh naxis[2]");
+    fhead = fhead + 80;
+
+    if (nax > 2) {
+	n = irafgeti4 (irafheader, imlen+8);
+	hputi4 (fitsheader, "NAXIS3", n);
+	hputcom (fitsheader,"NAXIS3", "IRAF .imh image naxis[3]");
+	fhead = fhead + 80;
+	}
+    if (nax > 3) {
+	n = irafgeti4 (irafheader, imlen+12);
+	hputi4 (fitsheader, "NAXIS4", n);
+	hputcom (fitsheader,"NAXIS4", "IRAF .imh image naxis[4]");
+	fhead = fhead + 80;
+	}
+
+    /* Set object name in FITS header */
+    if (imhver == 2)
+	objname = irafgetc (irafheader, IM2_TITLE, SZ_IM2TITLE);
+    else
+	objname = irafgetc2 (irafheader, IM_TITLE, SZ_IMTITLE);
+    if ((lstr = strlen (objname)) < 8) {
+	for (i = lstr; i < 8; i++)
+	    objname[i] = ' ';
+	objname[8] = 0;
+	}
+    hputs (fitsheader,"OBJECT",objname);
+    hputcom (fitsheader,"OBJECT", "IRAF .imh title");
+    free (objname);
+    fhead = fhead + 80;
+
+    /* Save physical axis lengths so image file can be read */
+    n = irafgeti4 (irafheader, imphyslen);
+    hputi4 (fitsheader, "NPAXIS1", n);
+    hputcom (fitsheader,"NPAXIS1", "IRAF .imh physical naxis[1]");
+    fhead = fhead + 80;
+    if (nax > 1) {
+	n = irafgeti4 (irafheader, imphyslen+4);
+	hputi4 (fitsheader, "NPAXIS2", n);
+	hputcom (fitsheader,"NPAXIS2", "IRAF .imh physical naxis[2]");
+	fhead = fhead + 80;
+	}
+    if (nax > 2) {
+	n = irafgeti4 (irafheader, imphyslen+8);
+	hputi4 (fitsheader, "NPAXIS3", n);
+	hputcom (fitsheader,"NPAXIS3", "IRAF .imh physical naxis[3]");
+	fhead = fhead + 80;
+	}
+    if (nax > 3) {
+	n = irafgeti4 (irafheader, imphyslen+12);
+	hputi4 (fitsheader, "NPAXIS4", n);
+	hputcom (fitsheader,"NPAXIS4", "IRAF .imh physical naxis[4]");
+	fhead = fhead + 80;
+	}
+
+    /* Save image minimum and maximum in header */
+    rmax = irafgetr4 (irafheader, immax);
+    rmin = irafgetr4 (irafheader, immin);
+    if (rmin != rmax) {
+	hputr4 (fitsheader, "IRAFMIN", &rmin);
+	fhead = fhead + 80;
+	hputcom (fitsheader,"IRAFMIN", "IRAF .imh minimum");
+	hputr4 (fitsheader, "IRAFMAX", &rmax);
+	hputcom (fitsheader,"IRAFMAX", "IRAF .imh maximum");
+	fhead = fhead + 80;
+	}
+
+    /* Save image header filename in header */
+    nl = hputm (fitsheader,"IMHFIL",hdrname);
+    if (nl > 0) {
+	lname = strlen (hdrname);
+	strcpy (fitsline, "IRAF header file name");
+	if (lname < 43)
+	    hputcom (fitsheader,"IMHFIL_1", fitsline);
+	else if (lname > 67 && lname < 110)
+	    hputcom (fitsheader,"IMHFIL_2", fitsline);
+	else if (lname > 134 && lname < 177)
+	    hputcom (fitsheader,"IMHFIL_3", fitsline);
+	}
+    if (nl > 0) fhead = fhead + (nl * 80);
+
+    /* Save image pixel file pathname in header */
+    if (imhver == 2)
+	pixname = irafgetc (irafheader, IM2_PIXFILE, SZ_IM2PIXFILE);
+    else
+	pixname = irafgetc2 (irafheader, IM_PIXFILE, SZ_IMPIXFILE);
+    if (strncmp(pixname, "HDR", 3) == 0 ) {
+	newpixname = same_path (pixname, hdrname);
+	free (pixname);
+	pixname = newpixname;
+	}
+    if (strchr (pixname, '/') == NULL && strchr (pixname, '$') == NULL) {
+	newpixname = same_path (pixname, hdrname);
+	free (pixname);
+	pixname = newpixname;
+	}
+	
+    if ((bang = strchr (pixname, '!')) != NULL )
+	nl = hputm (fitsheader,"PIXFIL",bang+1);
+    else
+	nl = hputm (fitsheader,"PIXFIL",pixname);
+    free (pixname);
+    if (nl > 0) {
+	strcpy (fitsline, "IRAF .pix pixel file");
+	if (lname < 43)
+	    hputcom (fitsheader,"PIXFIL_1", fitsline);
+	else if (lname > 67 && lname < 110)
+	    hputcom (fitsheader,"PIXFIL_2", fitsline);
+	else if (lname > 134 && lname < 177)
+	    hputcom (fitsheader,"PIXFIL_3", fitsline);
+	}
+    if (nl > 0) fhead = fhead + (nl * 80);
+
+    /* Save image offset from star of pixel file */
+    pixoff = irafgeti4 (irafheader, impixoff);
+    pixoff = (pixoff - 1) * 2;
+    hputi4 (fitsheader, "PIXOFF", pixoff);
+    hputcom (fitsheader,"PIXOFF", "IRAF .pix pixel offset (Do not change!)");
+    fhead = fhead + 80;
+
+    /* Save IRAF file format version in header */
+    hputi4 (fitsheader,"IMHVER",imhver);
+    hputcom (fitsheader,"IMHVER", "IRAF .imh format version (1 or 2)");
+    fhead = fhead + 80;
+
+    /* Set flag if header numbers are byte-reversed on this machine */
+    if (machswap() != headswap)
+	hputl (fitsheader, "HEADSWAP", 1);
+    else
+	hputl (fitsheader, "HEADSWAP", 0);
+    hputcom (fitsheader,"HEADSWAP", "IRAF header, FITS byte orders differ if T");
+    fhead = fhead + 80;
+
+    /* Set flag if image pixels are byte-reversed on this machine */
+    if (imhver == 2) {
+	hpixswap = irafgeti4 (irafheader, IM2_SWAPPED);
+	if (headswap && !hpixswap)
+	    pixswap = 1;
+	else if (!headswap && hpixswap)
+	    pixswap = 1;
+	else
+	    pixswap = 0;
+	}
+    else
+	pixswap = headswap;
+    if (machswap() != pixswap)
+	hputl (fitsheader, "PIXSWAP", 1);
+    else
+	hputl (fitsheader, "PIXSWAP", 0);
+    hputcom (fitsheader,"PIXSWAP", "IRAF pixels, FITS byte orders differ if T");
+    fhead = fhead + 80;
+
+    /* Read modification time */
+    mtime = irafgeti4 (irafheader, imtime);
+    if (mtime == 0)
+	dstring = lt2fd ();
+    else
+	dstring = tsi2fd (mtime);
+    hputs (fitsheader, "DATE-MOD", dstring);
+    hputcom (fitsheader,"DATE-MOD", "Date of latest file modification");
+    free (dstring);
+    fhead = fhead + 80;
+
+    /* Add user portion of IRAF header to FITS header */
+    fitsline[80] = 0;
+    if (imhver == 2) {
+	imu = LEN_IM2HDR;
+	chead = irafheader;
+	j = 0;
+	for (k = 0; k < 80; k++)
+	    fitsline[k] = ' ';
+	for (i = imu; i < nbiraf; i++) {
+	    irafchar = chead[i];
+	    if (irafchar == 0)
+		break;
+	    else if (irafchar == 10) {
+		(void)strncpy (fhead, fitsline, 80);
+		/* fprintf (stderr,"%80s\n",fitsline); */
+		if (strncmp (fitsline, "OBJECT ", 7) != 0) {
+		    fhead = fhead + 80;
+		    }
+		for (k = 0; k < 80; k++)
+		    fitsline[k] = ' ';
+		j = 0;
+		}
+	    else {
+		if (j > 80) {
+		    if (strncmp (fitsline, "OBJECT ", 7) != 0) {
+			(void)strncpy (fhead, fitsline, 80);
+			/* fprintf (stderr,"%80s\n",fitsline); */
+			j = 9;
+			fhead = fhead + 80;
+			}
+		    for (k = 0; k < 80; k++)
+			fitsline[k] = ' ';
+		    }
+		if (irafchar > 32 && irafchar < 127)
+		    fitsline[j] = irafchar;
+		j++;
+		}
+	    }
+	}
+    else {
+	imu = LEN_IMHDR;
+	chead = irafheader;
+	if (headswap == 1)
+	    ib = 0;
+	else
+	    ib = 1;
+	for (k = 0; k < 80; k++)
+	    fitsline[k] = ' ';
+	j = 0;
+	for (i = imu; i < nbiraf; i=i+2) {
+	    irafchar = chead[i+ib];
+	    if (irafchar == 0)
+		break;
+	    else if (irafchar == 10) {
+		if (strncmp (fitsline, "OBJECT ", 7) != 0) {
+		    (void)strncpy (fhead, fitsline, 80);
+		    fhead = fhead + 80;
+		    }
+		/* fprintf (stderr,"%80s\n",fitsline); */
+		j = 0;
+		for (k = 0; k < 80; k++)
+		    fitsline[k] = ' ';
+		}
+	    else {
+		if (j > 80) {
+		    if (strncmp (fitsline, "OBJECT ", 7) != 0) {
+			(void)strncpy (fhead, fitsline, 80);
+			j = 9;
+			fhead = fhead + 80;
+			}
+		    /* fprintf (stderr,"%80s\n",fitsline); */
+		    for (k = 0; k < 80; k++)
+			fitsline[k] = ' ';
+		    }
+		if (irafchar > 32 && irafchar < 127)
+		    fitsline[j] = irafchar;
+		j++;
+		}
+	    }
+	}
+
+    /* Add END to last line */
+    (void)strncpy (fhead, endline, 80);
+
+    /* Find end of last 2880-byte block of header */
+    fhead = ksearch (fitsheader, "END") + 80;
+    nblock = *nbfits / 2880;
+    fhead1 = fitsheader + (nblock * 2880);
+
+    /* Pad rest of header with spaces */
+    strncpy (endline,"   ",3);
+    for (fp = fhead; fp < fhead1; fp = fp + 80) {
+	(void)strncpy (fp, endline,80);
+	}
+
+    return (fitsheader);
+}
+
+
+int
+irafwhead (hdrname, lhead, irafheader, fitsheader)
+
+char	*hdrname;	/* Name of IRAF header file */
+int	lhead;		/* Length of IRAF header */
+char	*irafheader;	/* IRAF header */
+char	*fitsheader;	/* FITS image header */
+
+{
+    int fd;
+    int nbw, nbhead, lphead, pixswap;
+
+   /* Get rid of redundant header information */
+    hgeti4 (fitsheader, "PIXOFF", &lphead);
+    hgeti4 (fitsheader, "PIXSWAP", &pixswap);
+
+    /* Write IRAF header file */
+
+    /* Convert FITS header to IRAF header */
+    irafheader = fits2iraf (fitsheader, irafheader, lhead, &nbhead);
+    if (irafheader == NULL) {
+	fprintf (stderr, "IRAFWIMAGE:  file %s header error\n", hdrname);
+	return (-1);
+	}
+
+    /* Open the output file */
+    if (!access (hdrname, 0)) {
+	fd = open (hdrname, O_WRONLY);
+	if (fd < 3) {
+	    fprintf (stderr, "IRAFWIMAGE:  file %s not writeable\n", hdrname);
+	    return (0);
+	    }
+	}
+    else {
+	fd = open (hdrname, O_RDWR+O_CREAT, 0666);
+	if (fd < 3) {
+	    fprintf (stderr, "IRAFWIMAGE:  cannot create file %s\n", hdrname);
+	    return (0);
+	    }
+	}
+
+    /* Write IRAF header to disk file */
+    nbw = write (fd, irafheader, nbhead);
+    (void) ftruncate (fd, nbhead);
+    close (fd);
+    if (nbw < nbhead) {
+	(void)fprintf(stderr, "IRAF header file %s: %d / %d bytes written.\n",
+		      hdrname, nbw, nbhead);
+	return (-1);
+	}
+
+    return (nbw);
+}
+
+/* IRAFWIMAGE -- write IRAF .imh header file and .pix image file
+ * No matter what the input, this always writes in the local byte order */
+
+int
+irafwimage (hdrname, lhead, irafheader, fitsheader, image )
+
+char	*hdrname;	/* Name of IRAF header file */
+int	lhead;		/* Length of IRAF header */
+char	*irafheader;	/* IRAF header */
+char	*fitsheader;	/* FITS image header */
+char	*image;		/* IRAF image */
+
+{
+    int fd;
+    char *bang;
+    int nbw, bytepix, bitpix, naxis, naxis1, naxis2, nbimage, lphead;
+    char *pixn, *newpixname;
+    char pixname[SZ_IM2PIXFILE+1];
+    int imhver, pixswap;
+
+    hgeti4 (fitsheader, "IMHVER", &imhver);
+
+    if (!hgetm (fitsheader, "PIXFIL", SZ_IM2PIXFILE, pixname)) {
+	if (imhver == 2)
+	    pixn = irafgetc (irafheader, IM2_PIXFILE, SZ_IM2PIXFILE);
+	else
+	    pixn = irafgetc2 (irafheader, IM_PIXFILE, SZ_IMPIXFILE);
+	if (strncmp(pixn, "HDR", 3) == 0 ) {
+	    newpixname = same_path (pixn, hdrname);
+	    strcpy (pixname, newpixname);
+	    }
+	else {
+	    if ((bang = strchr (pixn, '!')) != NULL )
+		strcpy (pixname, bang+1);
+	    else
+		strcpy (pixname, pixn);
+	    }
+	free (pixn);
+        }
+
+    /* Find number of bytes to write */
+    hgeti4 (fitsheader,"NAXIS",&naxis);
+    hgeti4 (fitsheader,"NAXIS1",&naxis1);
+    hgeti4 (fitsheader,"NAXIS2",&naxis2);
+    hgeti4 (fitsheader,"BITPIX",&bitpix);
+    if (bitpix < 0)
+	bytepix = -bitpix / 8;
+    else
+	bytepix = bitpix / 8;
+
+    /* If either dimension is one and image is 3-D, read all three dimensions */
+    if (naxis == 3 && ((naxis1 == 1) | (naxis2 == 1))) {
+	int naxis3;
+	hgeti4 (fitsheader,"NAXIS3",&naxis3);
+	nbimage = naxis1 * naxis2 * naxis3 * bytepix;
+	}
+    else
+	nbimage = naxis1 * naxis2 * bytepix;
+
+   /* Read information about pixel file from header */
+    hgeti4 (fitsheader, "PIXOFF", &lphead);
+    hgeti4 (fitsheader, "PIXSWAP", &pixswap);
+
+    /* Write IRAF header file */
+    if (irafwhead (hdrname, lhead, irafheader, fitsheader))
+        return (0);
+
+    /* Open the output file */
+    if (!access (pixname, 0)) {
+	fd = open (pixname, O_WRONLY);
+	if (fd < 3) {
+	    fprintf (stderr, "IRAFWIMAGE:  file %s not writeable\n", pixname);
+	    return (0);
+	    }
+	}
+    else {
+	fd = open (pixname, O_RDWR+O_CREAT, 0666);
+	if (fd < 3) {
+	    fprintf (stderr, "IRAFWIMAGE:  cannot create file %s\n", pixname);
+	    return (0);
+	    }
+	}
+
+    /* Write header to IRAF pixel file */
+    if (imhver == 2)
+	irafputc ("impv2", irafheader, 0, 5);
+    else
+	irafputc2 ("impix", irafheader, 0, 5);
+    nbw = write (fd, irafheader, lphead);
+
+    /* Byte-reverse image, if necessary */
+    if (pixswap)
+	irafswap (bitpix, image, nbimage);
+
+    /* Write data to IRAF pixel file */
+    nbw = write (fd, image, nbimage);
+    close (fd);
+
+    free (pixname);
+    return (nbw);
+}
+
+
+/* Put filename and header path together */
+
+static char *
+same_path (pixname, hdrname)
+
+char	*pixname;	/* IRAF pixel file pathname */
+char	*hdrname;	/* IRAF image header file pathname */
+
+{
+    int len;
+    char *newpixname;
+
+    newpixname = (char *) calloc (SZ_IM2PIXFILE, 1);
+
+    /* Pixel file is in same directory as header */
+    if (strncmp(pixname, "HDR$", 4) == 0 ) {
+	(void)strncpy (newpixname, hdrname, SZ_IM2PIXFILE);
+
+	/* find the end of the pathname */
+	len = strlen (newpixname);
+#ifndef VMS
+	while( (len > 0) && (newpixname[len-1] != '/') )
+#else
+	while( (len > 0) && (newpixname[len-1] != ']') && (newpixname[len-1] != ':') )
+#endif
+	    len--;
+
+	/* add name */
+	newpixname[len] = '\0';
+	(void)strncat (newpixname, &pixname[4], SZ_IM2PIXFILE);
+	}
+
+    /* Bare pixel file with no path is assumed to be same as HDR$filename */
+    else if (strchr (pixname, '/') == NULL && strchr (pixname, '$') == NULL) {
+	(void)strncpy (newpixname, hdrname, SZ_IM2PIXFILE);
+
+	/* find the end of the pathname */
+	len = strlen (newpixname);
+#ifndef VMS
+	while( (len > 0) && (newpixname[len-1] != '/') )
+#else
+	while( (len > 0) && (newpixname[len-1] != ']') && (newpixname[len-1] != ':') )
+#endif
+	    len--;
+
+	/* add name */
+	newpixname[len] = '\0';
+	(void)strncat (newpixname, pixname, SZ_IM2PIXFILE);
+	}
+
+    /* Pixel file has same name as header file, but with .pix extension */
+    else if (strncmp (pixname, "HDR", 3) == 0) {
+
+	/* load entire header name string into name buffer */
+	(void)strncpy (newpixname, hdrname, SZ_IM2PIXFILE);
+	len = strlen (newpixname);
+	newpixname[len-3] = 'p';
+	newpixname[len-2] = 'i';
+	newpixname[len-1] = 'x';
+	}
+
+    return (newpixname);
+}
+
+/* Convert FITS image header to IRAF image header, returning IRAF header */
+/* No matter what the input, this always writes in the local byte order */
+
+char *
+fits2iraf (fitsheader, irafheader, nbhead, nbiraf)
+
+char	*fitsheader;	/* FITS image header */
+char	*irafheader;	/* IRAF image header (returned updated) */
+int	nbhead;		/* Length of IRAF header */
+int	*nbiraf;	/* Length of returned IRAF header */
+
+{
+    int i, n, pixoff, lhdrdir;
+    short *irafp, *irafs, *irafu;
+    char *iraf2u, *iraf2p, *filename, *hdrdir;
+    char *fitsend, *fitsp, pixfile[SZ_IM2PIXFILE], hdrfile[SZ_IM2HDRFILE];
+    char title[SZ_IM2TITLE], temp[80];
+    int	nax, nlfits, imhver, nbits, pixtype, hdrlength, mtime;
+    int imndim, imlen, imphyslen, impixtype, imhlen, imtime, immax, immin;
+    float rmax, rmin;
+
+    hgeti4 (fitsheader, "IMHVER", &imhver);
+    hdel (fitsheader, "IMHVER");
+    hdel (fitsheader, "IMHVER");
+    hgetl (fitsheader, "HEADSWAP", &headswap);
+    hdel (fitsheader, "HEADSWAP");
+    hdel (fitsheader, "HEADSWAP");
+    if (imhver == 2) {
+	imhlen = IM2_HDRLEN;
+	imndim = IM2_NDIM;
+	imlen = IM2_LEN;
+	imtime = IM2_MTIME;
+	imphyslen = IM2_PHYSLEN;
+	impixtype = IM2_PIXTYPE;
+	immax = IM2_MAX;
+	immin = IM2_MIN;
+	}
+    else {
+	imhlen = IM_HDRLEN;
+	imndim = IM_NDIM;
+	imlen = IM_LEN;
+	imtime = IM_MTIME;
+	imphyslen = IM_PHYSLEN;
+	impixtype = IM_PIXTYPE;
+	immax = IM_MAX;
+	immin = IM_MIN;
+	}
+
+    /* Delete FITS header keyword not needed by IRAF */
+    hdel (fitsheader,"SIMPLE");
+
+    /* Set IRAF image data type */
+    hgeti4 (fitsheader,"BITPIX", &nbits);
+    switch (nbits) {
+	case 8:
+	    pixtype = TY_CHAR;
+	    break;
+	case -8:
+	    pixtype = TY_UBYTE;
+	    break;
+	case 16:
+	    pixtype = TY_SHORT;
+	    break;
+	case -16:
+	    pixtype = TY_USHORT;
+	    break;
+	case 32:
+	    pixtype = TY_INT;
+	    break;
+	case -32:
+	    pixtype = TY_REAL;
+	    break;
+	case -64:
+	    pixtype = TY_DOUBLE;
+	    break;
+	default:
+	    (void)fprintf(stderr,"Unsupported data type: %d\n", nbits);
+	    return (NULL);
+	}
+    irafputi4 (irafheader, impixtype, pixtype);
+    hdel (fitsheader,"BITPIX");
+
+    /* Set IRAF image dimensions */
+    hgeti4 (fitsheader,"NAXIS",&nax);
+    irafputi4 (irafheader, imndim, nax);
+    hdel (fitsheader,"NAXIS");
+
+    hgeti4 (fitsheader, "NAXIS1", &n);
+    irafputi4 (irafheader, imlen, n);
+    irafputi4 (irafheader, imphyslen, n);
+    hdel (fitsheader,"NAXIS1");
+
+    hgeti4 (fitsheader,"NAXIS2",&n);
+    irafputi4 (irafheader, imlen+4, n);
+    irafputi4 (irafheader, imphyslen+4, n);
+    hdel (fitsheader,"NAXIS2");
+
+    if (nax > 2) {
+	hgeti4 (fitsheader,"NAXIS3",&n);
+	irafputi4 (irafheader, imlen+8, n);
+	irafputi4 (irafheader, imphyslen+8, n);
+	hdel (fitsheader,"NAXIS3");
+	}
+
+    if (nax > 3) {
+	hgeti4 (fitsheader,"NAXIS4",&n);
+	irafputi4 (irafheader, imlen+12, n);
+	irafputi4 (irafheader, imphyslen+12, n);
+	hdel (fitsheader,"NAXIS4");
+	}
+
+    /* Set image pixel value limits */
+    rmin = 0.0;
+    hgetr4 (fitsheader, "IRAFMIN", &rmin);
+    rmax = 0.0;
+    hgetr4 (fitsheader, "IRAFMAX", &rmax);
+    if (rmin != rmax) {
+	irafputr4 (irafheader, immax, rmax);
+	irafputr4 (irafheader, immin, rmin);
+	}
+    hdel (fitsheader, "IRAFMIN");
+    hdel (fitsheader, "IRAFMAX");
+
+    /* Replace pixel file name, if it is in the FITS header */
+    if (hgetm (fitsheader, "PIXFIL", SZ_IM2PIXFILE, pixfile)) {
+	if (strchr (pixfile, '/')) {
+	    if (hgetm (fitsheader, "IMHFIL", SZ_IM2HDRFILE, hdrfile)) {
+		hdrdir = strrchr (hdrfile, '/');
+		if (hdrdir != NULL) {
+		    lhdrdir = hdrdir - hdrfile + 1;
+		    if (!strncmp (pixfile, hdrfile, lhdrdir)) {
+			filename = pixfile + lhdrdir;
+			strcpy (temp, "HDR$");
+			strcat (temp,filename);
+			strcpy (pixfile, temp);
+			}
+		    }
+		if (pixfile[0] != '/' && pixfile[0] != 'H') {
+		    strcpy (temp, "HDR$");
+		    strcat (temp,pixfile);
+		    strcpy (pixfile, temp);
+		    }
+		}
+	    }
+
+	if (imhver == 2)
+            irafputc (pixfile, irafheader, IM2_PIXFILE, SZ_IM2PIXFILE);
+	else
+            irafputc2 (pixfile, irafheader, IM_PIXFILE, SZ_IMPIXFILE);
+	hdel (fitsheader,"PIXFIL_1");
+	hdel (fitsheader,"PIXFIL_2");
+	hdel (fitsheader,"PIXFIL_3");
+	hdel (fitsheader,"PIXFIL_4");
+	}
+
+    /* Replace header file name, if it is in the FITS header */
+    if (hgetm (fitsheader, "IMHFIL", SZ_IM2HDRFILE, pixfile)) {
+	if (!strchr (pixfile,'/') && !strchr (pixfile,'$')) {
+	    strcpy (temp, "HDR$");
+	    strcat (temp,pixfile);
+	    strcpy (pixfile, temp);
+	    }
+	if (imhver == 2)
+            irafputc (pixfile, irafheader, IM2_HDRFILE, SZ_IM2HDRFILE);
+	else
+            irafputc2 (pixfile, irafheader, IM_HDRFILE, SZ_IMHDRFILE);
+	hdel (fitsheader, "IMHFIL_1");
+	hdel (fitsheader, "IMHFIL_2");
+	hdel (fitsheader, "IMHFIL_3");
+	hdel (fitsheader, "IMHFIL_4");
+	}
+
+    /* Replace image title, if it is in the FITS header */
+    if (hgets (fitsheader, "OBJECT", SZ_IM2TITLE, title)) {
+	if (imhver == 2)
+            irafputc (title, irafheader, IM2_TITLE, SZ_IM2TITLE);
+	else
+            irafputc2 (title, irafheader, IM_TITLE, SZ_IMTITLE);
+	hdel (fitsheader, "OBJECT");
+	}
+    hgeti4 (fitsheader, "PIXOFF", &pixoff);
+    hdel (fitsheader, "PIXOFF");
+    hdel (fitsheader, "PIXOFF");
+    hdel (fitsheader, "PIXSWAP");
+    hdel (fitsheader, "PIXSWAP");
+    hdel (fitsheader, "DATE-MOD");
+    hdel (fitsheader, "DATE-MOD");
+    fitsend = ksearch (fitsheader,"END");
+
+    /* Find length of FITS header */
+    fitsend = ksearch (fitsheader,"END");
+    nlfits = ((fitsend - fitsheader) / 80);
+
+    /* Find new length of IRAF header */
+    if (imhver == 2)
+	*nbiraf = LEN_IM2HDR + (81 * nlfits);
+    else
+	*nbiraf = LEN_IMHDR + (162 * nlfits);
+    if (*nbiraf > nbhead)
+	irafheader = realloc (irafheader, *nbiraf);
+
+    /* Reset modification time */
+    mtime = lt2tsi ();
+    irafputi4 (irafheader, imtime, mtime);
+
+    /*  Replace user portion of IRAF header with remaining FITS header */
+    if (imhver == 2) {
+	iraf2u = irafheader + LEN_IM2HDR;
+	iraf2p = iraf2u;
+	for (fitsp = fitsheader; fitsp < fitsend; fitsp = fitsp + 80) {
+	    for (i = 0; i < 80; i++)
+		*iraf2p++ = fitsp[i];
+	    *iraf2p++ = 10;
+	    }
+	*iraf2p++ = 0;
+	*nbiraf = iraf2p - irafheader;
+	hdrlength = 1 + *nbiraf / 2;
+	}
+    else {
+	irafs = (short *)irafheader;
+	irafu = irafs + (LEN_IMHDR / 2);
+	irafp = irafu;
+	for (fitsp = fitsheader; fitsp < fitsend; fitsp = fitsp + 80) {
+	    for (i = 0; i < 80; i++)
+		*irafp++ = (short) fitsp[i];
+	    *irafp++ = 10;
+	    }
+	*irafp++ = 0;
+	*irafp++ = 32;
+	*nbiraf = 2 * (irafp - irafs);
+	hdrlength = *nbiraf / 4;
+	}
+
+    /* Length of header file */
+    irafputi4 (irafheader, imhlen, hdrlength);
+
+    /* Offset in .pix file to first pixel data
+    hputi4 (fitsheader, "PIXOFF", pixoff); */
+
+    /* Return number of bytes in new IRAF header */
+    return (irafheader);
+}
+
+
+int
+irafgeti4 (irafheader, offset)
+
+char	*irafheader;	/* IRAF image header */
+int	offset;		/* Number of bytes to skip before number */
+
+{
+    char *ctemp, *cheader;
+    int  temp;
+
+    cheader = irafheader;
+    ctemp = (char *) &temp;
+
+    /* If header swap flag not set, set it now */
+    if (headswap < 0) {
+	if (cheader[offset] > 0)
+	    headswap = 1;
+	else
+	    headswap = 0;
+	}
+
+    if (machswap() != headswap) {
+	ctemp[3] = cheader[offset];
+	ctemp[2] = cheader[offset+1];
+	ctemp[1] = cheader[offset+2];
+	ctemp[0] = cheader[offset+3];
+	}
+    else {
+	ctemp[0] = cheader[offset];
+	ctemp[1] = cheader[offset+1];
+	ctemp[2] = cheader[offset+2];
+	ctemp[3] = cheader[offset+3];
+	}
+    return (temp);
+}
+
+
+float
+irafgetr4 (irafheader, offset)
+
+char	*irafheader;	/* IRAF image header */
+int	offset;		/* Number of bytes to skip before number */
+
+{
+    char *ctemp, *cheader;
+    float  temp;
+
+    cheader = irafheader;
+    ctemp = (char *) &temp;
+
+    /* If header swap flag not set, set it now */
+    if (headswap < 0) {
+	if (cheader[offset] > 0)
+	    headswap = 1;
+	else
+	    headswap = 0;
+	}
+
+    if (machswap() != headswap) {
+	ctemp[3] = cheader[offset];
+	ctemp[2] = cheader[offset+1];
+	ctemp[1] = cheader[offset+2];
+	ctemp[0] = cheader[offset+3];
+	}
+    else {
+	ctemp[0] = cheader[offset];
+	ctemp[1] = cheader[offset+1];
+	ctemp[2] = cheader[offset+2];
+	ctemp[3] = cheader[offset+3];
+	}
+    return (temp);
+}
+
+
+/* IRAFGETC2 -- Get character string from arbitrary part of v.1 IRAF header */
+
+char *
+irafgetc2 (irafheader, offset, nc)
+
+char	*irafheader;	/* IRAF image header */
+int	offset;		/* Number of bytes to skip before string */
+int	nc;		/* Maximum number of characters in string */
+
+{
+    char *irafstring, *string;
+
+    irafstring = irafgetc (irafheader, offset, 2*(nc+1));
+    string = iraf2str (irafstring, nc);
+    free (irafstring);
+
+    return (string);
+}
+
+
+/* IRAFGETC -- Get character string from arbitrary part of IRAF header */
+
+char *
+irafgetc (irafheader, offset, nc)
+
+char	*irafheader;	/* IRAF image header */
+int	offset;		/* Number of bytes to skip before string */
+int	nc;		/* Maximum number of characters in string */
+
+{
+    char *ctemp, *cheader;
+    int i;
+
+    cheader = irafheader;
+    ctemp = (char *) calloc (nc+1, 1);
+    if (ctemp == NULL) {
+	(void)fprintf(stderr, "IRAFGETC Cannot allocate %d-byte variable\n",
+		nc+1);
+	return (NULL);
+	}
+    for (i = 0; i < nc; i++) {
+	ctemp[i] = cheader[offset+i];
+	if (ctemp[i] > 0 && ctemp[i] < 32)
+	    ctemp[i] = ' ';
+	}
+
+    return (ctemp);
+}
+
+
+/* Convert IRAF 2-byte/char string to 1-byte/char string */
+
+char *
+iraf2str (irafstring, nchar)
+
+char	*irafstring;	/* IRAF 2-byte/character string */
+int	nchar;		/* Number of characters in string */
+{
+    char *string;
+    int i, j;
+
+    /* Set swap flag according to position of nulls in 2-byte characters */
+    if (headswap < 0) {
+	if (irafstring[0] != 0 && irafstring[1] == 0)
+	    headswap = 1;
+	else if (irafstring[0] == 0 && irafstring[1] != 0)
+	    headswap = 0;
+	else
+	    return (NULL);
+	}
+
+    string = (char *) calloc (nchar+1, 1);
+    if (string == NULL) {
+	(void)fprintf(stderr, "IRAF2STR Cannot allocate %d-byte variable\n",
+		nchar+1);
+	return (NULL);
+	}
+
+    /* Swap bytes, if requested */
+    if (headswap)
+	j = 0;
+    else
+	j = 1;
+
+    /* Convert appropriate byte of input to output character */
+    for (i = 0; i < nchar; i++) {
+	string[i] = irafstring[j];
+	j = j + 2;
+	}
+
+    return (string);
+}
+
+
+/* IRAFPUTI4 -- Insert 4-byte integer into arbitrary part of IRAF header */
+
+static void
+irafputi4 (irafheader, offset, inum)
+
+char	*irafheader;	/* IRAF image header */
+int	offset;		/* Number of bytes to skip before number */
+int	inum;		/* Number to put into header */
+
+{
+    char *cn, *chead;
+
+    chead = irafheader;
+    cn = (char *) &inum;
+    if (headswap < 0)
+	headswap = 0;
+    if (headswap != machswap()) {
+	chead[offset+3] = cn[0];
+	chead[offset+2] = cn[1];
+	chead[offset+1] = cn[2];
+	chead[offset] = cn[3];
+	}
+    else {
+	chead[offset] = cn[0];
+	chead[offset+1] = cn[1];
+	chead[offset+2] = cn[2];
+	chead[offset+3] = cn[3];
+	}
+    return;
+}
+
+
+/* IRAFPUTR4 -- Insert 4-byte real number into arbitrary part of IRAF header */
+
+static void
+irafputr4 (irafheader, offset, rnum)
+
+char	*irafheader;	/* IRAF image header */
+int	offset;		/* Number of bytes to skip before number */
+float	rnum;		/* Number to put into header */
+
+{
+    char *cn, *chead;
+
+    chead = irafheader;
+    cn = (char *) &rnum;
+    if (headswap < 0)
+	headswap = 0;
+    if (headswap != machswap()) {
+	chead[offset+3] = cn[0];
+	chead[offset+2] = cn[1];
+	chead[offset+1] = cn[2];
+	chead[offset] = cn[3];
+	}
+    else {
+	chead[offset] = cn[0];
+	chead[offset+1] = cn[1];
+	chead[offset+2] = cn[2];
+	chead[offset+3] = cn[3];
+	}
+    return;
+}
+
+
+/* IRAFPUTC2 -- Insert character string into arbitrary part of v.1 IRAF header */
+
+static void
+irafputc2 (string, irafheader, offset, nc)
+
+char	*string;	/* String to insert into header */
+char	*irafheader;	/* IRAF image header */
+int	offset;		/* Number of bytes to skip before string */
+int	nc;		/* Maximum number of characters in string */
+
+{
+    char *irafstring;
+
+    irafstring = (char *) calloc (2 * nc, 1);
+    if (irafstring == NULL) {
+	(void)fprintf(stderr, "IRAFPUTC2 Cannot allocate %d-byte variable\n",
+		2 * nc);
+	}
+    str2iraf (string, irafstring, nc);
+    irafputc (irafstring, irafheader, offset, 2*nc);
+
+    return;
+}
+
+
+/* IRAFPUTC -- Insert character string into arbitrary part of IRAF header */
+
+static void
+irafputc (string, irafheader, offset, nc)
+
+char	*string;	/* String to insert into header */
+char	*irafheader;	/* IRAF image header */
+int	offset;		/* Number of bytes to skip before string */
+int	nc;		/* Maximum number of characters in string */
+
+{
+    char *chead;
+    int i;
+
+    chead = irafheader;
+    for (i = 0; i < nc; i++)
+	chead[offset+i] = string[i];
+
+    return;
+}
+
+
+/* STR2IRAF -- Convert 1-byte/char string to IRAF 2-byte/char string */
+
+static void
+str2iraf (string, irafstring, nchar)
+
+char	*string;	/* 1-byte/character string */
+char	*irafstring;	/* IRAF 2-byte/character string */
+int	nchar;		/* Maximum number of characters in IRAF string */
+{
+    int i, j, nc, nbytes;
+
+    nc = strlen (string);
+
+    /* Fill output string with zeroes */
+    nbytes = nchar * 2;
+    for (i = 0; i < nbytes; i++)
+	irafstring[i] = 0;
+
+    /* If swapped, start with first byte of 2-byte characters */
+    if (headswap)
+	j = 0;
+    else
+	j = 1;
+
+    /* Move input characters to appropriate bytes of output */
+    for (i = 0; i < nchar; i++) {
+	if (i > nc)
+	    irafstring[j] = 0;
+	else
+	    irafstring[j] = string[i];
+	j = j + 2;
+	}
+
+    return;
+}
+
+
+/* IRAFSWAP -- Reverse bytes of any type of vector in place */
+
+static void
+irafswap (bitpix, string, nbytes)
+
+int	bitpix;		/* Number of bits per pixel */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+char	*string;	/* Address of starting point of bytes to swap */
+int	nbytes;		/* Number of bytes to swap */
+
+{
+    switch (bitpix) {
+
+	case 16:
+	    if (nbytes < 2) return;
+	    irafswap2 (string,nbytes);
+	    break;
+
+	case 32:
+	    if (nbytes < 4) return;
+	    irafswap4 (string,nbytes);
+	    break;
+
+	case -16:
+	    if (nbytes < 2) return;
+	    irafswap2 (string,nbytes);
+	    break;
+
+	case -32:
+	    if (nbytes < 4) return;
+	    irafswap4 (string,nbytes);
+	    break;
+
+	case -64:
+	    if (nbytes < 8) return;
+	    irafswap8 (string,nbytes);
+	    break;
+
+	}
+    return;
+}
+
+
+/* IRAFSWAP2 -- Swap bytes in string in place */
+
+static void
+irafswap2 (string,nbytes)
+
+
+char *string;	/* Address of starting point of bytes to swap */
+int nbytes;	/* Number of bytes to swap */
+
+{
+    char *sbyte, temp, *slast;
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp = sbyte[0];
+	sbyte[0] = sbyte[1];
+	sbyte[1] = temp;
+	sbyte= sbyte + 2;
+	}
+    return;
+}
+
+
+/* IRAFSWAP4 -- Reverse bytes of Integer*4 or Real*4 vector in place */
+
+static void
+irafswap4 (string,nbytes)
+
+char *string;	/* Address of Integer*4 or Real*4 vector */
+int nbytes;	/* Number of bytes to reverse */
+
+{
+    char *sbyte, *slast;
+    char temp0, temp1, temp2, temp3;
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp3 = sbyte[0];
+	temp2 = sbyte[1];
+	temp1 = sbyte[2];
+	temp0 = sbyte[3];
+	sbyte[0] = temp0;
+	sbyte[1] = temp1;
+	sbyte[2] = temp2;
+	sbyte[3] = temp3;
+	sbyte = sbyte + 4;
+	}
+
+    return;
+}
+
+
+/* IRAFSWAP8 -- Reverse bytes of Real*8 vector in place */
+
+static void
+irafswap8 (string,nbytes)
+
+char *string;	/* Address of Real*8 vector */
+int nbytes;	/* Number of bytes to reverse */
+
+{
+    char *sbyte, *slast;
+    char temp[8];
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp[7] = sbyte[0];
+	temp[6] = sbyte[1];
+	temp[5] = sbyte[2];
+	temp[4] = sbyte[3];
+	temp[3] = sbyte[4];
+	temp[2] = sbyte[5];
+	temp[1] = sbyte[6];
+	temp[0] = sbyte[7];
+	sbyte[0] = temp[0];
+	sbyte[1] = temp[1];
+	sbyte[2] = temp[2];
+	sbyte[3] = temp[3];
+	sbyte[4] = temp[4];
+	sbyte[5] = temp[5];
+	sbyte[6] = temp[6];
+	sbyte[7] = temp[7];
+	sbyte = sbyte + 8;
+	}
+    return;
+}
+
+
+/* Set flag if machine on which program is executing is not FITS byte order
+ * ( i.e., if it is an Alpha or PC instead of a Sun ) */
+
+static int
+machswap ()
+
+{
+    char *ctest;
+    int itest;
+
+    itest = 1;
+    ctest = (char *)&itest;
+    if (*ctest)
+	return (1);
+    else
+	return (0);
+}
+
+
+/* ISIRAF -- return 1 if IRAF imh file, else 0 */
+
+int
+isiraf (filename)
+
+char	*filename;	/* Name of file for which to find size */
+{
+    if (strchr (filename, '='))
+	return (0);
+    else if (strsrch (filename, ".imh"))
+	return (1);
+    else
+	return (0);
+}
+
+
+/* IRAFSIZE -- return size of file in bytes */
+
+static int
+irafsize (diskfile)
+
+FILE *diskfile;		/* Descriptor of file for which to find size */
+{
+    long filesize;
+    long offset;
+
+    offset = (long) 0;
+
+    /* Move to end of the file */
+    if (fseek (diskfile, offset, SEEK_END) == 0) {
+
+ 	/* Position is the size of the file */
+	filesize = ftell (diskfile);
+
+	/* Move file pointer back tot he start of the file */
+	fseek (diskfile, offset, SEEK_SET);
+	}
+
+    else
+	filesize = -1;
+
+    return (filesize);
+}
+
+/* Feb 15 1996	New file
+ * Apr 10 1996	Add more documentation
+ * Apr 17 1996	Print error message on open failure
+ * Jun  5 1996	Add byte swapping (reversal); use streams
+ * Jun 10 1996	Make fixes after running lint
+ * Jun 12 1996	Use IMSWAP subroutines instead of local ones
+ * Jul  3 1996	Go back to using local IRAFSWAP subroutines
+ * Jul  3 1996	Write to pixel file from FITS header
+ * Jul 10 1996	Allocate all headers
+ * Aug 13 1996	Add unistd.h to include list
+ * Aug 26 1996	Allow 1-d images; fix comments; fix arguments after lint
+ * Aug 26 1996	Add IRAF header lingth argument to IRAFWIMAGE and IRAFWHEAD
+ * Aug 28 1996	Clean up code in IRAF2FITS
+ * Aug 30 1996	Use write instead of fwrite
+ * Sep  4 1996	Fix write mode bug
+ * Oct 15 1996	Drop unused variables
+ * Oct 17 1996	Minor fix after lint; cast arguments to STR2IRAF
+ *
+ * May 15 1997	Fix returned header length in IRAF2FITS
+ * Dec 19 1997	Add IRAF version 2 .imh files
+ *
+ * Jan  2 1998	Allow uneven length of user parameter lines in IRAF headers
+ * Jan  6 1998	Fix output of imh2 headers; allow newlines in imh1 headers
+ * Jan 14 1998	Handle byte reversing correctly
+ * Apr 17 1998	Add new IRAF data types unsigned char and unsigned short
+ * Apr 30 1998  Fix error return if illegal data type after Allan Brighton
+ * May 15 1998	Delete header keywords used for IRAF binary values
+ * May 15 1998	Fix bug so FITS OBJECT is put into IRAF title
+ * May 26 1998	Fix bug in fits2iraf keeping track of end of header
+ * May 27 1998	Include fitsio.h instead of fitshead.h
+ * Jun  4 1998	Write comments into header for converted IRAF binary values
+ * Jun  4 1998	Pad FITS strings to 8 character minimum
+ * Jul 24 1998	Write header file length to IRAF header file
+ * Jul 27 1998	Print error messages to stderr for all failed malloc's
+ * Jul 27 1998	Fix bug padding FITS header with spaces in iraf2fits
+ * Jul 27 1998	Write modification time to IRAF header file
+ * Aug  6 1998	Change fitsio.h to fitsfile.h; imhio.c to imhfile.c
+ * Oct  1 1998	Set irafswap flag only once per file
+ * Oct  5 1998	Add subroutines irafsize() and isiraf()
+ * Nov 16 1998	Fix byte-swap checking
+ *
+ * Jan 27 1999	Read and write all of 3D image if one dimension is =1
+ * Jul 13 1999	Improve error messages; change irafsize() argument to fd
+ * Sep 22 1999	Don't copy OBJECT keyword from .imh file; use binary title
+ * Oct 14 1999	Set FITS header length
+ * Oct 20 1999	Allocate 5000 extra bytes for IRAF header
+ * Nov  2 1999	Fix getclocktime() to use only time.h subroutines
+ * Nov  2 1999	Add modification date and time to FITS header in iraf2fits()
+ * Nov 24 1999	Delete HEADSWAP, IMHVER, DATE-MOD from header before writing
+ * Nov 29 1999	Delete PIXSWAP, IRAF-MIN, IRAF-MAX from header before writing
+ *
+ * Jan 13 2000	Fix bug which dropped characters in iraf2fits()
+ * Feb  3 2000	Declare timezone long, not time_t; drop unused variable
+ * Mar  7 2000	Add more code to keep pixel file path short
+ * Mar 10 2000	Fix bugs when writing .imh file headers
+ * Mar 21 2000	Change computation of IRAF time tags to use only data structure
+ * Mar 22 2000	Move IRAF time tag computation to lt2tsi() in dateutil.c
+ * Mar 24 2000	Use Unix file update time if none in header
+ * Mar 27 2000	Use hputm() to save file paths up to 256 characters
+ * Mar 27 2000	Write filename comments after 1st keyword with short value
+ * Mar 27 2000	Allocate pixel file name in same_path to imh2 length
+ * Mar 29 2000	Add space after last linefeed of header in fits2iraf()
+ * Apr 28 2000	Dimension pixname in irafwimage()
+ * May  1 2000	Fix code for updating pixel file name with HDR$ in fits2iraf()
+ * Jun  2 2000	Drop unused variables in fits2iraf() after lint
+ * Jun 12 2000	If pixel filename has no / or $, use same path as header file
+ * Sep  6 2000	Use header directory if pixel file not found at its pathname
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Aug 24 2001	In isiraf(), return 0 if argument contains an equal sign
+ *
+ * Apr  8 2002	Fix bug in error message for unidentified nbits in fits2iraf()
+ *
+ * Feb  4 2003	Open catalog file rb instead of r (Martin Ploner, Bern)
+ * Oct 31 2003	Read image only in irafrimage() if physical dimension > image dim.
+ * Nov  3 2003	Set NAXISi to image, not physical dimensions in iraf2fits()
+ *
+ * Jun 13 2005	Drop trailing spaces on pixel file name
+ *
+ * Jun 20 2006	Initialize uninitialized variables
+ *
+ * Jan  4 2007	Change hputr4() calls to send pointer to value
+ * Jan  8 2007	Drop unused variable nbx in irafrimage()
+ * Jan  8 2006	Align header and image buffers properly by 4 and by BITPIX
+ */
diff --git a/Code/src/libwcs/imio.c b/Code/src/libwcs/imio.c
new file mode 100644
index 0000000000000000000000000000000000000000..232f2b1db28822a431d42269fc3d2b9503a69fdf
--- /dev/null
+++ b/Code/src/libwcs/imio.c
@@ -0,0 +1,1541 @@
+/*** File wcslib/imio.c
+ *** June 11, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:      imio.c (image pixel manipulation)
+ * Purpose:     Read and write pixels from arbitrary data type 2D arrays
+ * Subroutine:	getpix (image, bitpix, w, h, bz, bs, x, y)
+ *		Read pixel from 2D image of any numeric type (0,0 lower left)
+ * Subroutine:	getpix1 (image, bitpix, w, h, bz, bs, x, y)
+ *		Read pixel from 2D image of any numeric type (1,1 lower left)
+ * Subroutine:	putpix (image, bitpix, w, h, bz, bs, x, y, dpix)
+ *		Write pixel into 2D image of any numeric type (0,0 lower left)
+ * Subroutine:	putpix1 (image, bitpix, w, h, bz, bs, x, y, dpix)
+ *		Write pixel into 2D image of any numeric type (1,1 lower left)
+ * Subroutine:	addpix (image, bitpix, w, h, bz, bs, x, y, dpix)
+ *		Copy pixel into 2D image of any numeric type (0,0 lower left)
+ * Subroutine:	addpix1 (image, bitpix, w, h, bz, bs, x, y, dpix)
+ *		Add pixel into 2D image of any numeric type (1,1 lower left)
+ * Subroutine:	maxvec (image, bitpix, bz, bs, pix1, npix)
+ *		Get maximum of vector from 2D image of any numeric type
+ * Subroutine:	minvec (image, bitpix, bz, bs, pix1, npix)
+ *		Get minimum of vector from 2D image of any numeric type
+ * Subroutine:	getvec (image, bitpix, bz, bs, pix1, npix, dvec)
+ *		Get vector from 2D image of any numeric type
+ * Subroutine:	putvec (image, bitpix, bz, bs, pix1, npix, dvec)
+ *		Copy pixel vector into a vector of any numeric type
+ * Subroutine:	addvec (image, bitpix, bz, bs, pix1, npix, dpix)
+ *		Add constant to pixel values in a vector
+ * Subroutine:	multvec (image, bitpix, bz, bs, pix1, npix, dpix)
+ *		Multiply pixel values in a vector by a constant
+ * Subroutine:	fillvec (image, bitpix, bz, bs, pix1, npix, dpix)
+ *		Copy pixel value in a vector of any numeric type
+ * Subroutine:	fillvec1 (image, bitpix, bz, bs, pix1, npix, dpix)
+ *		Copy pixel value int a vector of any numeric type
+ * Subroutine:	movepix (image1, bitpix, w1, x1, y1, image2, w2, x2, y2)
+ *		Copy pixel from one image location to another
+ * Subroutine:	imswap (bitpix,string,nbytes)
+ *		Swap bytes in string in place, with FITS bits/pixel code
+ * Subroutine:	imswap2 (string,nbytes)
+ *		Swap bytes in string in place
+ * Subroutine	imswap4 (string,nbytes)
+ *		Reverse bytes of Integer*4 or Real*4 vector in place
+ * Subroutine	imswap8 (string,nbytes)
+ *		Reverse bytes of Real*8 vector in place
+ * Subroutine	imswapped ()
+ *		Return 1 if PC/DEC byte order, else 0
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "fitsfile.h"
+
+static int scale = 1;	/* If 0, skip scaling step */
+void
+setscale (scale0)
+int scale0;
+{scale = scale0; return;}
+
+/* GETPIX1 -- Get pixel from 2D FITS image of any numeric type */
+
+double
+getpix1 (image, bitpix, w, h, bzero, bscale, x, y)
+
+char	*image;		/* Image array as 1-D vector */
+int	bitpix;		/* FITS bits per pixel */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+int	w;		/* Image width in pixels */
+int	h;		/* Image height in pixels */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	x;		/* One-based horizontal pixel number */
+int	y;		/* One-based vertical pixel number */
+
+{
+    return (getpix (image, bitpix, w, h, bzero, bscale, x-1, y-1));
+}
+
+
+/* GETPIX -- Get pixel from 2D image of any numeric type */
+
+double
+getpix (image, bitpix, w, h, bzero, bscale, x, y)
+
+char	*image;		/* Image array as 1-D vector */
+int	bitpix;		/* FITS bits per pixel */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+int	w;		/* Image width in pixels */
+int	h;		/* Image height in pixels */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	x;		/* Zero-based horizontal pixel number */
+int	y;		/* Zero-based vertical pixel number */
+
+{
+    short *im2;
+    int *im4;
+    unsigned short *imu;
+    float *imr;
+    double *imd;
+    double dpix;
+
+/* Return 0 if coordinates are not inside image */
+    if (x < 0 || x >= w)
+	return (0.0);
+    if (y < 0 || y >= h)
+	return (0.0);
+
+/* Extract pixel from appropriate type of array */
+    switch (bitpix) {
+
+	case 8:
+	  dpix = (double) image[(y*w) + x];
+	  break;
+
+	case 16:
+	  im2 = (short *)image;
+	  dpix = (double) im2[(y*w) + x];
+	  break;
+
+	case 32:
+	  im4 = (int *)image;
+	  dpix = (double) im4[(y*w) + x];
+	  break;
+
+	case -16:
+	  imu = (unsigned short *)image;
+	  dpix = (double) imu[(y*w) + x];
+	  break;
+
+	case -32:
+	  imr = (float *)image;
+	  dpix = (double) imr[(y*w) + x];
+	  break;
+
+	case -64:
+	  imd = (double *)image;
+	  dpix = imd[(y*w) + x];
+	  break;
+
+	default:
+	  dpix = 0.0;
+	}
+    if (scale)
+	return (bzero + (bscale * dpix));
+    else
+	return (dpix);
+}
+
+
+/* PUTPIX1 -- Copy pixel into 2D FITS image of any numeric type */
+
+void
+putpix1 (image, bitpix, w, h, bzero, bscale, x, y, dpix)
+
+char	*image;
+int	bitpix;		/* Number of bits per pixel */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+int	w;		/* Image width in pixels */
+int	h;		/* Image height in pixels */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	x;		/* One-based horizontal pixel number */
+int	y;		/* One-based vertical pixel number */
+double	dpix;
+
+{
+    putpix (image, bitpix, w, h, bzero, bscale, x-1, y-1, dpix);
+    return;
+}
+
+
+/* PUTPIX -- Copy pixel into 2D image of any numeric type */
+
+void
+putpix (image, bitpix, w, h, bzero, bscale, x, y, dpix)
+
+char	*image;
+int	bitpix;		/* Number of bits per pixel */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+int	w;		/* Image width in pixels */
+int	h;		/* Image height in pixels */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	x;
+int	y;
+double	dpix;
+
+{
+    short *im2;
+    int *im4;
+    unsigned short *imu;
+    float *imr;
+    double *imd;
+
+/* Return if coordinates are not inside image */
+    if (x < 0 || x >= w)
+	return;
+    if (y < 0 || y >= h)
+	return;
+
+    if (scale)
+	dpix = (dpix - bzero) / bscale;
+
+    switch (bitpix) {
+
+	case 8:
+	    if (dpix < 0)
+		image[(y*w) + x] = (char) (dpix - 0.5);
+	    else
+		image[(y*w) + x] = (char) (dpix + 0.5);
+	    break;
+
+	case 16:
+	    im2 = (short *)image;
+	    if (dpix < 0)
+		im2[(y*w) + x] = (short) (dpix - 0.5);
+	    else
+		im2[(y*w) + x] = (short) (dpix + 0.5);
+	    break;
+
+	case 32:
+	    im4 = (int *)image;
+	    if (dpix < 0)
+		im4[(y*w) + x] = (int) (dpix - 0.5);
+	    else
+		im4[(y*w) + x] = (int) (dpix + 0.5);
+	    break;
+
+	case -16:
+	    imu = (unsigned short *)image;
+	    if (dpix < 0)
+		imu[(y*w) + x] = (unsigned short) 0;
+	    else
+		imu[(y*w) + x] = (unsigned short) (dpix + 0.5);
+	    break;
+
+	case -32:
+	    imr = (float *)image;
+	    imr[(y*w) + x] = (float) dpix;
+	    break;
+
+	case -64:
+	    imd = (double *)image;
+	    imd[(y*w) + x] = dpix;
+	    break;
+
+	}
+    return;
+}
+
+
+/* ADDPIX1 -- Add pixel value into 2D FITS image of any numeric type */
+
+void
+addpix1 (image, bitpix, w, h, bzero, bscale, x, y, dpix)
+
+char	*image;
+int	bitpix;		/* Number of bits per pixel */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+int	w;		/* Image width in pixels */
+int	h;		/* Image height in pixels */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	x;		/* One-based horizontal pixel number */
+int	y;		/* One-based vertical pixel number */
+double	dpix;		/* Value to add to pixel */
+
+{
+    addpix (image, bitpix, w, h, bzero, bscale, x-1, y-1, dpix);
+    return;
+}
+
+
+/* ADDPIX -- Add constant to pixel values in 2D image of any numeric type */
+
+void
+addpix (image, bitpix, w, h, bzero, bscale, x, y, dpix)
+
+char	*image;
+int	bitpix;		/* Number of bits per pixel */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+int	w;		/* Image width in pixels */
+int	h;		/* Image height in pixels */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	x;		/* Zero-based horizontal pixel number */
+int	y;		/* Zero-based vertical pixel number */
+double	dpix;		/* Value to add to pixel */
+
+{
+    short *im2;
+    int *im4;
+    unsigned short *imu;
+    float *imr;
+    double *imd;
+    int ipix;
+
+/* Return if coordinates are not inside image */
+    if (x < 0 || x >= w)
+	return;
+    if (y < 0 || y >= h)
+	return;
+
+    if (scale)
+	dpix = (dpix - bzero) / bscale;
+    ipix = (y * w) + x;
+
+    switch (bitpix) {
+
+	case 8:
+	    if (dpix < 0)
+		image[ipix] = image[ipix] + (char) (dpix - 0.5);
+	    else
+		image[ipix] = image[ipix] + (char) (dpix + 0.5);
+	    break;
+
+	case 16:
+	    im2 = (short *)image;
+	    if (dpix < 0)
+		im2[ipix] = im2[ipix] + (short) (dpix - 0.5);
+	    else
+		im2[ipix] = im2[ipix] + (short) (dpix + 0.5);
+	    break;
+
+	case 32:
+	    im4 = (int *)image;
+	    if (dpix < 0)
+		im4[ipix] = im4[ipix] + (int) (dpix - 0.5);
+	    else
+		im4[ipix] = im4[ipix] + (int) (dpix + 0.5);
+	    break;
+
+	case -16:
+	    imu = (unsigned short *)image;
+	    if (dpix > 0)
+		imu[ipix] = imu[ipix] + (unsigned short) (dpix + 0.5);
+	    break;
+
+	case -32:
+	    imr = (float *)image;
+	    imr[ipix] = imr[ipix] + (float) dpix;
+	    break;
+
+	case -64:
+	    imd = (double *)image;
+	    imd[ipix] = imd[ipix] + dpix;
+	    break;
+
+	}
+    return;
+}
+
+
+/* MOVEPIX -- Copy pixel between images */
+
+void
+movepix (image1, bitpix1, w1, x1, y1, image2, bitpix2, w2, x2, y2)
+
+char	*image1;	/* Pointer to first pixel in input image */
+int	bitpix1;	/* Bits per input pixel (FITS codes) */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+int	w1;		/* Number of horizontal pixels in input image */
+int	x1, y1;		/* Row and column for input pixel */
+
+char	*image2;	/* Pointer to first pixel in output image */
+int	bitpix2;	/* Bits per output pixel (FITS codes) */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+int	w2;		/* Number of horizontal pixels in output image */
+int	x2, y2;		/* Row and column for output pixel */
+
+{
+    short *ims1, *ims2;
+    int *imi1, *imi2;
+    unsigned short *imu1, *imu2;
+    float rpix, *imr1, *imr2;
+    double dpix, *imd1, *imd2;
+
+    if (x1 < 0 || x2 < 0 || x1 >= w1 || x2 >= w2)
+	return;
+    if (y1 < 0 || y2 < 0)
+	return;
+
+    switch (bitpix1) {
+
+	case 8:
+	    switch (bitpix2) {
+		case 8:
+		    image2[(y2*w2) + x2] = image1[(y1*w1) + x1];
+		    break;
+		case 16:
+		    ims2 = (short *)image2;
+		    ims2[(y2*w2) + x2] = image1[(y1*w1) + x1];
+		    break;
+		case 32:
+		    imi2 = (int *)image2;
+		    imi2[(y2*w2) + x2] = (int) image1[(y1*w1) + x1];
+		    break;
+		case -16:
+		    imu2 = (unsigned short *)image2;
+		    imu2[(y2*w2) + x2] = (unsigned short) image1[(y1*w1) + x1];
+		    break;
+		case -32:
+		    imr2 = (float *)image2;
+		    imr2[(y2*w2) + x2] = (float) image1[(y1*w1) + x1];
+		    break;
+		case -64:
+		    imd2 = (double *)image2;
+		    imd2[(y2*w2) + x2] = (double) image1[(y1*w1) + x1];
+		    break;
+		}
+	    break;
+
+	case 16:
+	    switch (bitpix2) {
+		case 8:
+		    ims1 = (short *)image1;
+		    image2[(y2*w2) + x2] = (char) ims1[(y1*w1) + x1];
+		    break;
+		case 16:
+		    ims1 = (short *)image1;
+		    ims2 = (short *)image2;
+		    ims2[(y2*w2) + x2] = ims1[(y1*w1) + x1];
+		    break;
+		case 32:
+		    ims1 = (short *)image1;
+		    imi2 = (int *)image2;
+		    imi2[(y2*w2) + x2] = (int) ims1[(y1*w1) + x1];
+		    break;
+		case -16:
+		    ims1 = (short *)image1;
+		    imu2 = (unsigned short *)image2;
+		    imu2[(y2*w2) + x2] = (unsigned short) ims1[(y1*w1) + x1];
+		    break;
+		case -32:
+		    ims1 = (short *)image1;
+		    imr2 = (float *)image2;
+		    imr2[(y2*w2) + x2] = (float) ims1[(y1*w1) + x1];
+		    break;
+		case -64:
+		    ims1 = (short *)image1;
+		    imd2 = (double *)image2;
+		    imd2[(y2*w2) + x2] = (double) ims1[(y1*w1) + x1];
+		    break;
+		}
+	    break;
+
+	case 32:
+	    switch (bitpix2) {
+		case 8:
+		    imi1 = (int *)image1;
+		    image2[(y2*w2) + x2] = (char) imi1[(y1*w1) + x1];
+		    break;
+		case 16:
+		    imi1 = (int *)image1;
+		    ims2 = (short *)image2;
+		    ims2[(y2*w2) + x2] = (short) imi1[(y1*w1) + x1];
+		    break;
+		case 32:
+		    imi1 = (int *)image1;
+		    imi2 = (int *)image2;
+		    imi2[(y2*w2) + x2] = imi1[(y1*w1) + x1];
+		    break;
+		case -16:
+		    imi1 = (int *)image1;
+		    imu2 = (unsigned short *)image2;
+		    imu2[(y2*w2) + x2] = (unsigned short) imi1[(y1*w1) + x1];
+		    break;
+		case -32:
+		    imi1 = (int *)image1;
+		    imr2 = (float *)image2;
+		    imr2[(y2*w2) + x2] = (float) imi1[(y1*w1) + x1];
+		    break;
+		case -64:
+		    imi1 = (int *)image1;
+		    imd2 = (double *)image2;
+		    imd2[(y2*w2) + x2] = (double) imi1[(y1*w1) + x1];
+		    break;
+		}
+	    break;
+
+	case -16:
+	    switch (bitpix2) {
+		case 8:
+		    imu1 = (unsigned short *)image1;
+		    image2[(y2*w2) + x2] = (char) imu1[(y1*w1) + x1];
+		    break;
+		case 16:
+		    imu1 = (unsigned short *)image1;
+		    ims2 = (short *)image2;
+		    ims2[(y2*w2) + x2] = (short) imu1[(y1*w1) + x1];
+		    break;
+		case 32:
+		    imu1 = (unsigned short *)image1;
+		    imi2 = (int *)image2;
+		    imi2[(y2*w2) + x2] = (int) imu1[(y1*w1) + x1];
+		    break;
+		case -16:
+		    imu1 = (unsigned short *)image1;
+		    imu2 = (unsigned short *)image2;
+		    imu2[(y2*w2) + x2] = imu1[(y1*w1) + x1];
+		    break;
+		case -32:
+		    imu1 = (unsigned short *)image1;
+		    imr2 = (float *)image2;
+		    imr2[(y2*w2) + x2] = (float) imu1[(y1*w1) + x1];
+		    break;
+		case -64:
+		    imu1 = (unsigned short *)image1;
+		    imd2 = (double *)image2;
+		    imd2[(y2*w2) + x2] = (double) imu1[(y1*w1) + x1];
+		    break;
+		}
+	    break;
+
+	case -32:
+	    imr1 = (float *)image1;
+	    rpix = imr1[(y1*w1) + x1];
+	    switch (bitpix2) {
+		case 8:
+		    if (rpix < 0.0)
+			image2[(y2*w2) + x2] = (char) (rpix - 0.5);
+		    else
+			image2[(y2*w2) + x2] = (char) (rpix + 0.5);
+		    break;
+		case 16:
+		    ims2 = (short *)image2;
+		    if (rpix < 0.0)
+			ims2[(y2*w2) + x2] = (short) (rpix - 0.5);
+		    else
+			ims2[(y2*w2) + x2] = (short) (rpix + 0.5);
+		    break;
+		case 32:
+		    imi2 = (int *)image2;
+		    if (rpix < 0.0)
+			imi2[(y2*w2) + x2] = (int) (rpix - 0.5);
+		    else
+			imi2[(y2*w2) + x2] = (int) (rpix + 0.5);
+		    break;
+		case -16:
+		    imu2 = (unsigned short *)image2;
+		    if (rpix < 0.0)
+			imu2[(y2*w2) + x2] = (unsigned short) 0;
+		    else
+			imu2[(y2*w2) + x2] = (unsigned short) (rpix + 0.5);
+		    break;
+		case -32:
+		    imr2 = (float *)image2;
+		    imr2[(y2*w2) + x2] = rpix;
+		    break;
+		case -64:
+		    imd2 = (double *)image2;
+		    imd2[(y2*w2) + x2] = (double) rpix;
+		    break;
+		}
+	    break;
+
+	case -64:
+	    imd1 = (double *)image1;
+	    dpix = imd1[(y1*w1) + x1];
+	    switch (bitpix2) {
+		case 8:
+		    imd1 = (double *)image1;
+		    if (dpix < 0.0)
+			image2[(y2*w2) + x2] = (char) (dpix - 0.5);
+		    else
+			image2[(y2*w2) + x2] = (char) (dpix + 0.5);
+		    break;
+		case 16:
+		    ims2 = (short *)image2;
+		    if (dpix < 0.0)
+			ims2[(y2*w2) + x2] = (short) (dpix - 0.5);
+		    else
+			ims2[(y2*w2) + x2] = (short) (dpix + 0.5);
+		    break;
+		case 32:
+		    imi2 = (int *)image2;
+		    if (dpix < 0.0)
+			imi2[(y2*w2) + x2] = (int) (dpix - 0.5);
+		    else
+			imi2[(y2*w2) + x2] = (int) (dpix + 0.5);
+		    break;
+		case -16:
+		    imu2 = (unsigned short *)image2;
+		    if (dpix < 0.0)
+			imu2[(y2*w2) + x2] = (unsigned short) 0;
+		    else
+			imu2[(y2*w2) + x2] = (unsigned short) (dpix + 0.5);
+		    break;
+		case -32:
+		    imr2 = (float *)image2;
+		    imr2[(y2*w2) + x2] = (float) dpix;
+		    break;
+		case -64:
+		    imd2 = (double *)image2;
+		    imd2[(y2*w2) + x2] = dpix;
+		    break;
+		}
+	    break;
+	}
+    return;
+}
+
+
+/* MAXVEC -- Get maximum value in vector from 2D image of any numeric type */
+
+double
+maxvec (image, bitpix, bzero, bscale, pix1, npix)
+
+char	*image;		/* Image array from which to read vector */
+int	bitpix;		/* Number of bits per pixel in image */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	pix1;		/* Offset of first pixel to check */
+int	npix;		/* Number of pixels to check */
+
+{
+    short *im2, imax2, ip2;
+    int *im4, imax4, ip4;
+    unsigned short *imu, imaxu, ipu;
+    float *imr, imaxr, ipr;
+    double *imd;
+    double dmax = 0.0;
+    double ipd;
+    int ipix, pix2;
+    char imaxc, ipc;
+
+    pix2 = pix1 + npix;
+
+    switch (bitpix) {
+
+	case 8:
+	    imaxc = *(image + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ipc = *(image + ipix);
+		if (ipc > imaxc)
+		    imaxc = ipc;
+		}
+	    dmax = (double) imaxc;
+	    break;
+
+	case 16:
+	    im2 = (short *)image;
+	    imax2 = *(im2 + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ip2 = *(im2 + ipix);
+		if (ip2 > imax2)
+		    imax2 = ip2;
+		}
+	    dmax = (double) imax2;
+	    break;
+
+	case 32:
+	    im4 = (int *)image;
+	    imax4 = *(im4 + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ip4 = *(im4 + ipix);
+		if (ip4 > imax4)
+		    imax4 = ip4;
+		}
+	    dmax = (double) imax4;
+	    break;
+
+	case -16:
+	    imu = (unsigned short *)image;
+	    imaxu = *(imu + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ipu = *(imu + ipix);
+		if (ipu > imaxu)
+		    imaxu = ipu;
+		}
+	    dmax = (double) imaxu;
+	    break;
+
+	case -32:
+	    imr = (float *)image;
+	    imaxr = *(imr + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ipr = *(imr + ipix);
+		if (ipr > imaxr)
+		    imax2 = ipr;
+		}
+	    dmax = (double) imaxr;
+	    break;
+
+	case -64:
+	    imd = (double *)image;
+	    dmax = *(imd + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ipd = *(imd + ipix);
+		if (ipd > dmax)
+		    dmax = ipd;
+		}
+	    break;
+
+	}
+
+    /* Scale data if either BZERO or BSCALE keyword has been set */
+    if (scale && (bzero != 0.0 || bscale != 1.0))
+	dmax = (dmax * bscale) + bzero;
+
+    return (dmax);
+}
+
+
+/* MINVEC -- Get minimum value in vector from 2D image of any numeric type */
+
+double
+minvec (image, bitpix, bzero, bscale, pix1, npix)
+
+char	*image;		/* Image array from which to read vector */
+int	bitpix;		/* Number of bits per pixel in image */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	pix1;		/* Offset of first pixel to check */
+int	npix;		/* Number of pixels to check */
+
+{
+    short *im2, imin2, *ip2, *il2;
+    int *im4, imin4, ip4;
+    unsigned short *imu, iminu, ipu;
+    float *imr, iminr, ipr;
+    double *imd, ipd;
+    double dmin = 0.0;
+    int ipix, pix2;
+    char cmin, cp;
+
+    pix2 = pix1 + npix;
+
+    switch (bitpix) {
+
+	case 8:
+	    cmin = *(image + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		cp = *(image + ipix);
+		if (cp < cmin)
+		    cmin = cp;
+		}
+	    dmin = (double) cmin;
+	    break;
+
+	case 16:
+	    im2 = (short *)image + pix1;
+	    imin2 = *im2;
+	    il2 = im2 + npix;
+	    ip2 = im2;
+	    while (ip2 < il2) {
+		if (*ip2 < imin2)
+		    imin2 = *ip2;
+		ip2++;
+		}
+	    dmin = (double) imin2;
+	    break;
+
+	case 32:
+	    im4 = (int *)image;
+	    imin4 = *(im4 + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ip4 = *(im4 + ipix);
+		if (ip4 < imin4)
+		    imin4 = ip4;
+		}
+	    dmin = (double) imin4;
+	    break;
+
+	case -16:
+	    imu = (unsigned short *)image;
+	    iminu = *(imu + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ipu = *(imu + ipix);
+		if (ipu < iminu)
+		    iminu = ipu;
+		}
+	    dmin = (double) iminu;
+	    break;
+
+	case -32:
+	    imr = (float *)image;
+	    iminr = *(imr + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ipr = *(imr + ipix);
+		if (ipr < iminr)
+		    iminr = ipr;
+		}
+	    dmin = (double) iminr;
+	    break;
+
+	case -64:
+	    imd = (double *)image;
+	    dmin = *(imd + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		ipd = *(imd + ipix);
+		if (ipd < dmin)
+		    dmin = ipd;
+		}
+	    break;
+
+	}
+
+    /* Scale data if either BZERO or BSCALE keyword has been set */
+    if (scale && (bzero != 0.0 || bscale != 1.0))
+	dmin = (dmin * bscale) + bzero;
+
+    return (dmin);
+}
+
+
+/* ADDVEC -- Add constant to pixel values in 2D image of any numeric type */
+
+void
+addvec (image, bitpix, bzero, bscale, pix1, npix, dpix)
+
+char	*image;		/* Image array from which to extract vector */
+int	bitpix;		/* Number of bits per pixel in image */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	pix1;		/* Offset of first pixel to extract */
+int	npix;		/* Number of pixels to extract */
+double	dpix;		/* Value to add to pixels */
+
+{
+    char *imc, ccon;
+    short *im2, jcon;
+    int *im4, icon;
+    unsigned short *imu, ucon;
+    float *imr, rcon;
+    double *imd;
+    int ipix, pix2;
+
+    pix2 = pix1 + npix;
+
+    if (scale)
+	dpix = (dpix - bzero) / bscale;
+
+    switch (bitpix) {
+
+	case 8:
+	    imc = image + pix1;
+	    if (dpix < 0)
+		ccon = (char) (dpix - 0.5);
+	    else
+		ccon = (char) (dpix + 0.5);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*imc++ += ccon;
+	    break;
+
+	case 16:
+	    im2 = (short *) (image + pix1);
+	    if (dpix < 0)
+		jcon = (short) (dpix - 0.5);
+	    else
+		jcon = (short) (dpix + 0.5);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*im2++ += jcon;
+	    break;
+
+	case 32:
+	    im4 = (int *) (image + pix1);
+	    if (dpix < 0)
+		icon = (int) (dpix - 0.5);
+	    else
+		icon = (int) (dpix + 0.5);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*im4++ += icon;
+	    break;
+
+	case -16:
+	    imu = (unsigned short *) (image + pix1);
+	    if (dpix > 0) {
+		ucon = (unsigned short) (dpix + 0.5);
+		imu = (unsigned short *) (image + pix1);
+		for (ipix = pix1; ipix < pix2; ipix++)
+		    *imu++ += ucon;
+		}
+	    else {
+		icon = (int) (dpix - 0.5);
+		imu = (unsigned short *) (image + pix1);
+		for (ipix = pix1; ipix < pix2; ipix++) {
+		    unsigned short tmp = (icon + (int) *imu);
+		    *imu++ += tmp;
+		    }
+		}
+	    break;
+
+	case -32:
+	    rcon = (float) dpix;
+	    imr = (float *) (image + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*imr++ += rcon;
+	    break;
+
+	case -64:
+	    imd = (double *) (image + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*imd++ += dpix;
+	    break;
+	}
+    return;
+}
+
+
+/* MULTVEC -- Multiply pixel values in place in 2D image of any numeric type */
+
+void
+multvec (image, bitpix, bzero, bscale, pix1, npix, dpix)
+
+char	*image;		/* Image array from which to extract vector */
+int	bitpix;		/* Number of bits per pixel in image */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	pix1;		/* Offset of first pixel to extract */
+int	npix;		/* Number of pixels to extract */
+double	dpix;		/* Value by which to multiply pixels */
+
+{
+    char *imc, ccon;
+    short *im2, jcon;
+    int *im4, icon, isint;
+    unsigned short *imu, ucon;
+    float *imr, rcon;
+    double *imd, dcon, dval;
+    int ipix, pix2;
+
+    pix2 = pix1 + npix;
+
+    if (scale)
+	dpix = (dpix - bzero) / bscale;
+    ipix = (int) dpix;
+    dcon = (double) ipix;
+    if (dcon == dpix)
+	isint = 1;
+    else
+	isint = 0;
+
+    switch (bitpix) {
+
+	case 8:
+	    imc = image + pix1;
+	    if (isint) {
+		if (dpix < 0)
+		    ccon = (char) (dpix - 0.5);
+		else
+		    ccon = (char) (dpix + 0.5);
+		for (ipix = pix1; ipix < pix2; ipix++)
+		    *imc++ *= ccon;
+		}
+	    else {
+		for (ipix = pix1; ipix < pix2; ipix++) {
+		    dval = ((double) *imc) * dpix;
+		    if (dval < 256.0)
+			*imc++ = (char) dval;
+		    else
+			*imc++ = (char) 255;
+		    }
+		}
+	    break;
+
+	case 16:
+	    im2 = (short *) (image + pix1);
+	    if (isint) {
+		im2 = (short *)image;
+		if (dpix < 0)
+		    jcon = (short) (dpix - 0.5);
+		else
+		    jcon = (short) (dpix + 0.5);
+		for (ipix = pix1; ipix < pix2; ipix++)
+		    *im2++ *= jcon;
+		}
+	    else {
+		for (ipix = pix1; ipix < pix2; ipix++) {
+		    dval = ((double) *im2) * dpix;
+		    if (dval < 32768.0)
+			*im2++ = (short) dval;
+		    else
+			*im2++ = (short) 32767;
+		    }
+		}
+	    break;
+
+	case 32:
+	    im4 = (int *) (image + pix1);
+	    if (isint) {
+		if (dpix < 0)
+		    icon = (int) (dpix - 0.5);
+		else
+		    icon = (int) (dpix + 0.5);
+		for (ipix = pix1; ipix < pix2; ipix++)
+		    *im4++ *= icon;
+		}
+	    else {
+		for (ipix = pix1; ipix < pix2; ipix++) {
+		    dval = ((double) *im4) * dpix;
+		    if (dval < 32768.0)
+			*im4++ = (int) dval;
+		    else
+			*im4++ = (int) 32767;
+		    }
+		}
+	    break;
+
+	case -16:
+	    imu = (unsigned short *) (image + pix1);
+	    if (dpix > 0) {
+		ucon = (unsigned short) (dpix + 0.5);
+		imu = (unsigned short *) (image + pix1);
+		for (ipix = pix1; ipix < pix2; ipix++)
+		    *imu++ *= ucon;
+		}
+	    break;
+
+	case -32:
+	    rcon = (float) dpix;
+	    imr = (float *) (image + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*imr++ *= rcon;
+	    break;
+
+	case -64:
+	    imd = (double *) (image + pix1);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*imd++ *= dpix;
+	    break;
+
+	}
+    return;
+}
+
+
+/* GETVEC -- Get vector from 2D image of any numeric type */
+
+void
+getvec (image, bitpix, bzero, bscale, pix1, npix, dvec0)
+
+char	*image;		/* Image array from which to extract vector */
+int	bitpix;		/* Number of bits per pixel in image */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	pix1;		/* Offset of first pixel to extract */
+int	npix;		/* Number of pixels to extract */
+double	*dvec0;		/* Vector of pixels (returned) */
+
+{
+    short *im2;
+    int *im4;
+    unsigned short *imu;
+    float *imr;
+    double *imd;
+    double *dvec;
+    int ipix, pix2;
+
+    pix2 = pix1 + npix;
+    dvec = dvec0;
+
+    switch (bitpix) {
+
+	case 8:
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*dvec++ = (double) *(image + ipix);
+	    break;
+
+	case 16:
+	    im2 = (short *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*dvec++ = (double) *(im2 + ipix);
+	    break;
+
+	case 32:
+	    im4 = (int *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*dvec++ = (double) *(im4 + ipix);
+	    break;
+
+	case -16:
+	    imu = (unsigned short *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*dvec++ = (double) *(imu + ipix);
+	    break;
+
+	case -32:
+	    imr = (float *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*dvec++ = (double) *(imr + ipix);
+	    break;
+
+	case -64:
+	    imd = (double *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*dvec++ = (double) *(imd + ipix);
+	    break;
+
+	}
+
+    /* Scale data if either BZERO or BSCALE keyword has been set */
+    if (scale && (bzero != 0.0 || bscale != 1.0)) {
+	dvec = dvec0;
+	for (ipix = pix1; ipix < pix2; ipix++) {
+	    *dvec = (*dvec * bscale) + bzero;
+	    dvec++;
+	    }
+	}
+
+    return;
+}
+
+
+/* PUTVEC -- Copy pixel vector into 2D image of any numeric type */
+
+void
+putvec (image, bitpix, bzero, bscale, pix1, npix, dvec)
+
+char	*image;		/* Image into which to copy vector */
+int	bitpix;		/* Number of bits per pixel im image */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	pix1;		/* Offset of first pixel of vector in image */
+int	npix;		/* Number of pixels to copy */
+double	*dvec;		/* Vector of pixels to copy */
+
+{
+    short *im2;
+    int *im4;
+    unsigned short *imu;
+    float *imr;
+    double *imd;
+    int ipix, pix2;
+    double *dp = dvec;
+
+    pix2 = pix1 + npix;
+
+    /* Scale data if either BZERO or BSCALE keyword has been set */
+    if (scale && (bzero != 0.0 || bscale != 1.0)) {
+	for (ipix = pix1; ipix < pix2; ipix++) {
+	    *dp = (*dp - bzero) / bscale;
+	    dp++;
+	    }
+	dp = dvec;
+	}
+
+    switch (bitpix) {
+
+	case 8:
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*(image+ipix) = (char) *dp++;
+	    break;
+
+	case 16:
+	    im2 = (short *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		if (*dp < 0.0)
+		    *(im2+ipix) = (short) (*dp++ - 0.5);
+		else
+		    *(im2+ipix) = (short) (*dp++ + 0.5);
+		}
+	    break;
+
+	case 32:
+	    im4 = (int *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		if (*dp < 0.0)
+		    *(im4+ipix) = (int) (*dp++ - 0.5);
+		else
+		    *(im4+ipix) = (int) (*dp++ + 0.5);
+		}
+	    break;
+
+	case -16:
+	    imu = (unsigned short *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++) {
+		if (*dp < 0.0)
+		    *(imu+ipix) = (unsigned short) 0;
+		else
+		    *(imu+ipix) = (unsigned short) (*dp++ + 0.5);
+		}
+	    break;
+
+	case -32:
+	    imr = (float *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*(imr+ipix) = (float) *dp++;
+	    break;
+
+	case -64:
+	    imd = (double *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		*(imd+ipix) = (double) *dp++;
+	    break;
+	}
+    return;
+}
+
+
+/* FILLVEC1 -- Copy single value into a vector of any numeric type */
+
+void
+fillvec1 (image, bitpix, bzero, bscale, pix1, npix, dpix)
+
+char	*image;		/* Vector to fill */
+int	bitpix;		/* Number of bits per pixel im image */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	pix1;		/* First pixel to fill */
+int	npix;		/* Number of pixels to fill */
+double	dpix;		/* Value with which to fill pixels */
+{
+    fillvec (image, bitpix, bzero, bscale, pix1-1, npix, dpix);
+    return;
+}
+
+
+/* FILLVEC -- Copy single value into a vector of any numeric type */
+
+void
+fillvec (image, bitpix, bzero, bscale, pix1, npix, dpix)
+
+char	*image;		/* Vector to fill */
+int	bitpix;		/* Number of bits per pixel im image */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+double  bzero;		/* Zero point for pixel scaling */
+double  bscale;		/* Scale factor for pixel scaling */
+int	pix1;		/* First pixel to fill */
+int	npix;		/* Number of pixels to fill */
+double	dpix;		/* Value with which to fill pixels */
+{
+    char ipc;
+    short *im2, ip2;
+    int *im4, ip4;
+    unsigned short *imu, ipu;
+    float *imr, ipr;
+    double *imd;
+    int ipix, pix2;
+    double dp;
+
+    pix2 = pix1 + npix;
+
+    /* Scale data if either BZERO or BSCALE keyword has been set */
+    dp = dpix;
+    if (scale && (bzero != 0.0 || bscale != 1.0))
+	dp = (dp - bzero) / bscale;
+
+    switch (bitpix) {
+
+	case 8:
+	    if (dp < 0.0)
+		ipc = (char) (dp - 0.5);
+	    else
+		ipc = (char) (dp + 0.5);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		image[ipix] = ipc;
+	    break;
+
+	case 16:
+	    im2 = (short *)image;
+	    if (dp < 0.0)
+		ip2 = (short) (dp - 0.5);
+	    else
+		ip2 = (short) (dp + 0.5);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		im2[ipix] = ip2;
+	    break;
+
+	case 32:
+	    im4 = (int *)image;
+	    if (dp < 0.0)
+		ip4 = (int) (dp - 0.5);
+	    else
+		ip4 = (int) (dp + 0.5);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		im4[ipix] = ip4;
+	    break;
+
+	case -16:
+	    imu = (unsigned short *)image;
+	    if (dp < 0.0)
+		ipu = (unsigned short) (dp - 0.5);
+	    else
+		ipu = (unsigned short) (dp + 0.5);
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		imu[ipix] = ipu;
+	    break;
+
+	case -32:
+	    imr = (float *)image;
+	    ipr = (float) dp;
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		imr[ipix] = ipr;
+	    break;
+
+	case -64:
+	    imd = (double *)image;
+	    for (ipix = pix1; ipix < pix2; ipix++)
+		imd[ipix] = dp;
+	    break;
+	}
+    return;
+}
+
+
+/* IMSWAP -- Reverse bytes of any type of vector in place */
+
+void
+imswap (bitpix, string, nbytes)
+
+int	bitpix;		/* Number of bits per pixel */
+			/*  16 = short, -16 = unsigned short, 32 = int */
+			/* -32 = float, -64 = double */
+char	*string;	/* Address of starting point of bytes to swap */
+int	nbytes;		/* Number of bytes to swap */
+
+{
+    switch (bitpix) {
+
+	case 8:
+	    break;
+
+	case 16:
+	    if (nbytes < 2) return;
+	    imswap2 (string,nbytes);
+	    break;
+
+	case 32:
+	    if (nbytes < 4) return;
+	    imswap4 (string,nbytes);
+	    break;
+
+	case -16:
+	    if (nbytes < 2) return;
+	    imswap2 (string,nbytes);
+	    break;
+
+	case -32:
+	    if (nbytes < 4) return;
+	    imswap4 (string,nbytes);
+	    break;
+
+	case -64:
+	    if (nbytes < 8) return;
+	    imswap8 (string,nbytes);
+	    break;
+
+	}
+    return;
+}
+
+
+/* IMSWAP2 -- Swap bytes in string in place */
+
+void
+imswap2 (string,nbytes)
+
+
+char *string;	/* Address of starting point of bytes to swap */
+int nbytes;	/* Number of bytes to swap */
+
+{
+    char *sbyte, temp, *slast;
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp = sbyte[0];
+	sbyte[0] = sbyte[1];
+	sbyte[1] = temp;
+	sbyte= sbyte + 2;
+	}
+    return;
+}
+
+
+/* IMSWAP4 -- Reverse bytes of Integer*4 or Real*4 vector in place */
+
+void
+imswap4 (string,nbytes)
+
+char *string;	/* Address of Integer*4 or Real*4 vector */
+int nbytes;	/* Number of bytes to reverse */
+
+{
+    char *sbyte, *slast;
+    char temp0, temp1, temp2, temp3;
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp3 = sbyte[0];
+	temp2 = sbyte[1];
+	temp1 = sbyte[2];
+	temp0 = sbyte[3];
+	sbyte[0] = temp0;
+	sbyte[1] = temp1;
+	sbyte[2] = temp2;
+	sbyte[3] = temp3;
+	sbyte = sbyte + 4;
+	}
+
+    return;
+}
+
+
+/* IMSWAP8 -- Reverse bytes of Real*8 vector in place */
+
+void
+imswap8 (string,nbytes)
+
+char *string;	/* Address of Real*8 vector */
+int nbytes;	/* Number of bytes to reverse */
+
+{
+    char *sbyte, *slast;
+    char temp[8];
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp[7] = sbyte[0];
+	temp[6] = sbyte[1];
+	temp[5] = sbyte[2];
+	temp[4] = sbyte[3];
+	temp[3] = sbyte[4];
+	temp[2] = sbyte[5];
+	temp[1] = sbyte[6];
+	temp[0] = sbyte[7];
+	sbyte[0] = temp[0];
+	sbyte[1] = temp[1];
+	sbyte[2] = temp[2];
+	sbyte[3] = temp[3];
+	sbyte[4] = temp[4];
+	sbyte[5] = temp[5];
+	sbyte[6] = temp[6];
+	sbyte[7] = temp[7];
+	sbyte = sbyte + 8;
+	}
+    return;
+}
+
+/* IMSWAPPED -- Returns 0 if big-endian (Sun,Mac),
+		1 if little-endian(PC,Alpha) */
+
+int
+imswapped ()
+
+{
+    char *ctest;
+    int itest;
+
+    itest = 1;
+    ctest = (char *)&itest;
+    if (*ctest)
+	return (1);
+    else
+	return (0);
+}
+
+/* Apr 17 1996	New file
+ * May 22 1996	Add H so that PUTPIX and GETPIX can check coordinates
+ * Jun 11 1996	Simplify NEWIMAGE subroutine
+ * Jun 12 1996	Add byte-swapping subroutines
+ *
+ * Jul 24 1997	Add 8-bit option to subroutines
+ *
+ * May 27 1998	Include imio.h instead of fitshead.h
+ * Jun 17 1998	Fix bug, changing all unsigned int's to unsigned short's
+ *
+ * Apr 29 1999	Add scaling to getpix, putpix, getvec, and putvec
+ * Apr 29 1999	Fix bug in getvec in dealing with 1-byte data
+ * Sep 14 1999	Change dp incrementing so it works on Alpha compiler
+ * Sep 27 1999	Add interface for 1-based (FITS) image access
+ * Sep 27 1999	Add addpix() and addpix1()
+ * Dec 14 1999	In putpix(), addpix(), putvec(), round when output is integer
+ *
+ * Sep 20 2000	In getvec(), scale only if necessary
+ *
+ * Nov 27 2001	In movepix(), add char to char move
+ *
+ * Jan 23 2002	Add global scale switch to turn off scaling
+ * Jun  4 2002	In getvec() and putvec(), change dpix to dvec
+ * Jun  4 2002	Add addvec() to add to a vector
+ * Jul 19 2002	Fix getvec() bug rescaling scaled numbers
+ *
+ * May 20 2003	Declare scale0 in setscale()
+ *
+ * Jan 28 2004	Add image limit check to movepix()
+ * Feb 27 2004	Add fillvec() and fillvec1() to set vector to a constant
+ *
+ * Jun 27 2005	Fix major bug in fillvec(); pass value dpix in fillvec1(), too
+ * Aug 18 2005	Add maxvec(), addvec(), and multvec()
+ *
+ * Mar  1 2006	Fix bug of occasional double application of bscale in getvec()
+ * Apr  3 2006	Fix bad cast in unisigned int section of addvec()
+ * May  3 2006	Code fixes in addpix and multpix suggested by Robert Lupton
+ * Jun  8 2006	Drop erroneous second im2 assignment without offset in addvec()
+ * Jun 20 2006	Fix typos masquerading as unitialized variables
+ *
+ * Jan  8 2007	Include fitsfile.h instead of imio.h
+ * Jun 11 2007	Add minvec() and speed up maxvec()
+ */
diff --git a/Code/src/libwcs/imio.h b/Code/src/libwcs/imio.h
new file mode 100644
index 0000000000000000000000000000000000000000..39d89201cc0bbdb105f25adb332f8d7a69604c08
--- /dev/null
+++ b/Code/src/libwcs/imio.h
@@ -0,0 +1,64 @@
+/*** imio.h  memory access subroutines
+ *** September 27, 1999
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2002
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#ifndef imio_h_
+#define imio_h_
+
+/* Image pixel access subroutines in imio.c */
+extern double getpix(); /* Read one pixel from any data type 2-D array (0,0)*/
+extern double getpix1(); /* Read one pixel from any data type 2-D array (1,1)*/
+extern void putpix();   /* Write one pixel to any data type 2-D array (0,0)*/
+extern void putpix1();  /* Write one pixel to any data type 2-D array (1,1) */
+extern void addpix();   /* Add to one pixel in any data type 2-D array (0,0)*/
+extern void addpix1();  /* Add to one pixel in any data type 2-D array (1,1)*/
+extern void movepix();  /* Move one pixel value between two 2-D arrays (0,0) */
+extern void movepix1(); /* Move one pixel value between two 2-D arrays (1,1) */
+extern void getvec();   /* Read vector from a 2-D array */
+extern void putvec();   /* Write vector into a 2-D array */
+extern void fillvec();   /* Write constant into a vector */
+extern void fillvec1();   /* Write constant into a vector */
+extern void imswap();   /* Swap alternating bytes in a vector */
+extern void imswap2();  /* Swap bytes in a vector of 2-byte (short) integers */
+extern void imswap4();  /* Reverse bytes in a vector of 4-byte numbers */
+extern void imswap8();  /* Reverse bytes in a vector of 8-byte numbers */
+extern int imswapped(); /* Return 1 if machine byte order is not FITS order */
+
+#endif	/* imio_h_ */
+
+/* May 31 1996	Use stream I/O for reading as well as writing
+ * Jun 12 1996	Add byte-swapping subroutines
+ * Aug  6 1996	Add MOVEPIX, HDEL and HCHANGE declarations
+ *
+ * May 27 1998	Split off imio subroutines to imio.h
+
+ * Sep 27 1999	Add Fortran-indexed (1,1), not (0,0) image access *1()
+ * Sep 28 1999	Add addpix()
+ *
+ * Feb 27 2004	Add fillvec()
+ */
diff --git a/Code/src/libwcs/imrotate.c b/Code/src/libwcs/imrotate.c
new file mode 100644
index 0000000000000000000000000000000000000000..d2c88423185c2f03316a8ace8893ee656d14b87c
--- /dev/null
+++ b/Code/src/libwcs/imrotate.c
@@ -0,0 +1,660 @@
+/*** File libwcs/imrotate.c
+ *** June 26, 2008
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2008
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "fitsfile.h"
+
+static void RotWCSFITS();	/* rotate all the C* fields */
+
+/* Rotate an image by 90, 180, or 270 degrees, with an optional
+ * reflection across the vertical or horizontal axis.
+ * verbose generates extra info on stdout.
+ * return NULL if successful or rotated image.
+ */
+
+char *
+RotFITS (pathname,header,image0,xshift,yshift,rotate,mirror,bitpix2,rotwcs,verbose)
+
+char	*pathname;	/* Name of file which is being changed */
+char	*header;	/* FITS header */
+char	*image0;	/* Unrotated image pixels */
+int	xshift;		/* Number of pixels to shift image horizontally, +=right */
+int	yshift;		/* Number of pixels to shift image vertically, +=right */
+int	rotate;		/* Angle to by which to rotate image (90, 180, 270) */
+int	mirror;		/* Reflect image around 1=vertical, 2=horizontal axis */
+int	bitpix2;	/* Number of bits per pixel in output image */
+int	rotwcs;		/* If not =0, rotate WCS keywords, else leave them */
+int	verbose;
+
+{
+    int bitpix1, ny, nx, nax;
+    int x1, y1, x2, y2, nbytes;
+    char *rotimage;
+    char *image;
+    char history[128];
+    char *filename;
+    double crpix;
+
+    image = NULL;
+    rotimage = NULL;
+
+    if (rotate == 1)
+	rotate = 90;
+    else if (rotate == 2)
+	rotate = 180;
+    else if (rotate == 3)
+	rotate = 270;
+    else if (rotate < 0)
+	rotate = rotate + 360;
+
+    filename = strrchr (pathname,'/');
+    if (filename)
+	filename = filename + 1;
+    else
+	filename = pathname;
+
+    /* Get image size */
+    nax = 0;
+    if (hgeti4 (header,"NAXIS",&nax) < 1) {
+	if (verbose)
+	    printf ("RotFITS: Not an image (NAXIS=%d)\n",nax);
+	return (NULL);
+	}
+    else {
+	if (hgeti4 (header,"NAXIS1",&nx) < 1) {
+	    if (verbose)
+		printf ("RotFITS: Not an image (NAXIS1=%d)\n",nx);
+	    return (NULL);
+	    }
+	else {
+	    if (hgeti4 (header,"NAXIS2",&ny) < 1) {
+		if (verbose)
+		    printf ("RotFITS: Not an image (NAXIS2=%d)\n",ny);
+		return (NULL);
+		}
+	    }
+	}
+    bitpix1 = 16;
+    hgeti4 (header,"BITPIX", &bitpix1);
+    if (bitpix2 == 0)
+	bitpix2 = bitpix1;
+
+    /* Shift WCS fields in header */
+    if (rotwcs && hgetr8 (header, "CRPIX1", &crpix)) {
+	crpix = crpix + xshift;
+	hputr8 (header, "CRPIX1", crpix);
+	}
+    if (rotwcs && hgetr8 (header, "CRPIX2", &crpix)) {
+	crpix = crpix + yshift;
+	hputr8 (header, "CRPIX2", crpix);
+	}
+
+    /* Rotate WCS fields in header */
+    if (rotwcs && (rotate != 0 || mirror))
+	RotWCSFITS (header, rotate, mirror, verbose);
+
+    /* Compute size of image in bytes */
+    switch (bitpix2) {
+	case 8:
+	    nbytes = nx * ny;
+	    break;
+	case 16:
+	    nbytes = nx * ny * 2;
+	    break;
+	case 32:
+	    nbytes = nx * ny * 4;
+	    break;
+	case -16:
+	    nbytes = nx * ny * 2;
+	    break;
+	case -32:
+	    nbytes = nx * ny * 4;
+	    break;
+	case -64:
+	    nbytes = nx * ny * 8;
+	    break;
+	default:
+	    if (verbose)
+		printf ("RotFITS: Illegal BITPIX (%d)\n", bitpix2);
+	    return (NULL);
+	}
+
+    if (bitpix1 != bitpix2) {
+	sprintf (history,"Copy of image %s bits per pixel %d -> %d",
+		filename, bitpix1, bitpix2);
+	hputc (header,"HISTORY",history);
+	if (verbose)
+	    fprintf (stderr,"%s\n",history);
+	}
+
+    /* Shift image first */
+    if (xshift != 0 || yshift != 0) {
+
+	/* Allocate buffer for shifted image */
+	image = (char *) calloc (nbytes, 1);
+	if (image == NULL) {
+	    if (verbose)
+		printf ("RotFITS: Cannot allocate %d bytes for shifted image\n", nbytes);
+	    return (NULL);
+	    }
+
+	for (x1 = 0; x1 < nx; x1++) {
+	    x2 = x1 + xshift;
+	    for (y1 = 0; y1 < ny; y1++) {
+		y2 = y1 + yshift;
+		if (y2 < ny)
+		    movepix (image0,bitpix1,nx,x1,y1,image,bitpix2,nx,x2,y2);
+		}
+	    }
+	sprintf (history,"Copy of image %s shifted by dx=%d dy=%d",
+		 filename, xshift, yshift);
+	hputc (header,"HISTORY",history);
+	if (rotate == 0 && !mirror)
+	    return (image);
+	}
+    else
+	image = image0;
+
+    /* Allocate buffer for rotated image */
+    rotimage = (char *) calloc (nbytes, 1);
+    if (rotimage == NULL) {
+	if (verbose)
+	    printf ("RotFITS: Cannot allocate %d bytes for new image\n", nbytes);
+	return (NULL);
+	}
+
+    /* Mirror image without rotation */
+    if (rotate < 45 && rotate > -45) {
+	if (mirror == 1) {
+	    for (x1 = 0; x1 < nx; x1++) {
+		x2 = nx - x1 - 1;
+		for (y1 = 0; y1 < ny; y1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x2,y1);
+		    }
+		}
+	    sprintf (history,"Copy of image %s reflected",filename);
+	    hputc (header,"HISTORY",history);
+	    }
+	else if (mirror == 2) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		y2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x1,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s flipped",filename);
+	    hputc (header,"HISTORY",history);
+	    }
+	else {
+	    for (y1 = 0; y1 < ny; y1++) {
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x1,y1);
+		    }
+		}
+	    }
+	}
+
+    /* Rotate by 90 degrees */
+    else if (rotate >= 45 && rotate < 135) {
+	if (mirror == 1) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		x2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    y2 = nx - x1 - 1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s reflected, rotated 90 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else if (mirror == 2) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,y1,x1);
+		    }
+		}
+	    sprintf (history,"Copy of image %s flipped, rotated 90 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else {
+	    for (y1 = 0; y1 < ny; y1++) {
+		x2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    y2 = x1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s rotated 90 degrees",filename);
+            hputc (header,"HISTORY",history);
+	    }
+	hputi4 (header,"NAXIS1",ny);
+	hputi4 (header,"NAXIS2",nx);
+	}
+
+    /* Rotate by 180 degrees */
+    else if (rotate >= 135 && rotate < 225) {
+	if (mirror == 1) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		y2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x1,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s reflected, rotated 180 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else if (mirror == 2) {
+	    for (x1 = 0; x1 < nx; x1++) {
+		x2 = nx - x1 - 1;
+		for (y1 = 0; y1 < ny; y1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x2,y1);
+		    }
+		}
+	    sprintf (history,"Copy of image %s flipped, rotated 180 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else {
+	    for (y1 = 0; y1 < ny; y1++) {
+		y2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    x2 = nx - x1 - 1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s rotated 180 degrees",filename);
+            hputc (header,"HISTORY",history);
+	    }
+	}
+
+    /* Rotate by 270 degrees */
+    else if (rotate >= 225 && rotate < 315) {
+	if (mirror == 1) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,y1,x1);
+		    }
+		}
+	    sprintf (history,"Copy of image %s reflected, rotated 270 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else if (mirror == 2) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		x2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    y2 = nx - x1 - 1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s flipped, rotated 270 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else {
+	    for (y1 = 0; y1 < ny; y1++) {
+		x2 = y1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    y2 = nx - x1 - 1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s rotated 270 degrees",filename);
+            hputc (header,"HISTORY",history);
+	    }
+	hputi4 (header,"NAXIS1",ny);
+	hputi4 (header,"NAXIS2",nx);
+	}
+
+    /* If rotating by more than 315 degrees, assume top-bottom reflection */
+    else if (rotate >= 315 && mirror) {
+	for (y1 = 0; y1 < ny; y1++) {
+	    for (x1 = 0; x1 < nx; x1++) {
+		x2 = y1;
+		y2 = x1;
+		movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		}
+	    }
+	sprintf (history,"Copy of image %s reflected top to bottom",filename);
+        hputc (header,"HISTORY",history);
+	}
+    
+    if (verbose)
+	fprintf (stderr,"%s\n",history);
+
+    return (rotimage);
+}
+
+
+/* rotate all the C* fields.
+ * return 0 if at least one such field is found, else -1.  */
+
+static void
+RotWCSFITS (header, angle, mirror, verbose)
+
+char	*header;	/* FITS header */
+int	angle;		/* Angle to be rotated (0, 90, 180, 270) */
+int	mirror;		/* 1 if mirrored left to right, else 0 */
+int	verbose;	/* Print progress if 1 */
+
+{
+    static char flds[15][8];
+    char ctype1[16], ctype2[16];
+    double ctemp1, ctemp2, ctemp3, ctemp4, naxis1, naxis2;
+    int i, n, ndec1, ndec2, ndec3, ndec4;
+
+    strcpy (flds[0], "CTYPE1");
+    strcpy (flds[1], "CTYPE2");
+    strcpy (flds[2], "CRVAL1");
+    strcpy (flds[3], "CRVAL2");
+    strcpy (flds[4], "CDELT1");
+    strcpy (flds[5], "CDELT2");
+    strcpy (flds[6], "CRPIX1");
+    strcpy (flds[7], "CRPIX2");
+    strcpy (flds[8], "CROTA1");
+    strcpy (flds[9], "CROTA2");
+    strcpy (flds[10], "IMWCS");
+    strcpy (flds[11], "CD1_1");
+    strcpy (flds[12], "CD1_2");
+    strcpy (flds[13], "CD2_1");
+    strcpy (flds[14], "CD2_2");
+
+    n = 0;
+    hgetr8 (header, "NAXIS1", &naxis1);
+    hgetr8 (header, "NAXIS2", &naxis2);
+
+    /* Find out if there any WCS keywords in this header */
+    for (i = 0; i < sizeof(flds)/sizeof(flds[0]); i++) {
+	if (ksearch (header, flds[i]) != NULL) {
+	    n++;
+	    if (verbose)
+		fprintf (stderr,"%s: found\n", flds[i]);
+	    }
+	}
+
+    /* Return if no WCS keywords to change */
+    if (n == 0) {
+	if (verbose)
+	    fprintf (stderr,"RotWCSFITS: No WCS in header\n");
+	return;
+	}
+
+    /* Reset CROTAn and CD matrix if axes have been exchanged */
+    if (angle == 90) {
+	if (hgetr8 (header, "CROTA1", &ctemp1)) {
+	    hgetndec (header, "CROTA1", &ndec1);
+	    hputnr8 (header, "CROTA1", ndec1, ctemp1+90.0);
+	    }
+	if (hgetr8 (header, "CROTA2", &ctemp2)) {
+	    hgetndec (header, "CROTA2", &ndec2);
+	    hputnr8 (header, "CROTA2", ndec2, ctemp2+90.0);
+	    }
+	}
+
+    /* Negate rotation angle if mirrored */
+    if (mirror) {
+	if (hgetr8 (header, "CROTA1", &ctemp1)) {
+	    hgetndec (header, "CROTA1", &ndec1);
+	    hputnr8 (header, "CROTA1", ndec1, -ctemp1);
+	    }
+	if (hgetr8 (header, "CROTA2", &ctemp2)) {
+	    hgetndec (header, "CROTA2", &ndec2);
+	    hputnr8 (header, "CROTA2", ndec2, -ctemp2);
+	    }
+	if (hgetr8 (header, "LTM1_1", &ctemp1)) {
+	    hgetndec (header, "LTM1_1", &ndec1);
+	    hputnr8 (header, "LTM1_1", ndec1, -ctemp1);
+	    }
+	if (hgetr8 (header, "CD1_1", &ctemp1))
+	    hputr8 (header, "CD1_1", -ctemp1);
+	if (hgetr8 (header, "CD1_2", &ctemp1))
+	    hputr8 (header, "CD1_2", -ctemp1);
+	if (hgetr8 (header, "CD2_1", &ctemp1))
+	    hputr8 (header, "CD2_1", -ctemp1);
+	}
+
+    /* Unbin CRPIX and CD matrix */
+    if (hgetr8 (header, "LTM1_1", &ctemp1)) {
+	if (ctemp1 != 1.0) {
+	    if (hgetr8 (header, "LTM2_2", &ctemp2)) {
+		if (ctemp1 == ctemp2) {
+		    double ltv1 = 0.0;
+		    double ltv2 = 0.0;
+		    if (hgetr8 (header, "LTV1", &ltv1))
+			hdel (header, "LTV1");
+		    if (hgetr8 (header, "LTV2", &ltv1))
+			hdel (header, "LTV2");
+		    if (hgetr8 (header, "CRPIX1", &ctemp3))
+			hputr8 (header, "CRPIX1", (ctemp3-ltv1)/ctemp1);
+		    if (hgetr8 (header, "CRPIX2", &ctemp3))
+			hputr8 (header, "CRPIX2", (ctemp3-ltv2)/ctemp1);
+		    if (hgetr8 (header, "CD1_1", &ctemp3))
+			hputr8 (header, "CD1_1", ctemp3/ctemp1);
+		    if (hgetr8 (header, "CD1_2", &ctemp3))
+			hputr8 (header, "CD1_2", ctemp3/ctemp1);
+		    if (hgetr8 (header, "CD2_1", &ctemp3))
+			hputr8 (header, "CD2_1", ctemp3/ctemp1);
+		    if (hgetr8 (header, "CD2_2", &ctemp3))
+			hputr8 (header, "CD2_2", ctemp3/ctemp1);
+		    hdel (header, "LTM1_1");
+		    hdel (header, "LTM2_2");
+		    }
+		}
+	    }
+	}
+
+    /* Reset CRPIXn */
+    if (hgetr8 (header, "CRPIX1", &ctemp1) &&
+	hgetr8 (header, "CRPIX2", &ctemp2)) { 
+	hgetndec (header, "CRPIX1", &ndec1);
+	hgetndec (header, "CRPIX2", &ndec2);
+	if (mirror) {
+	    if (angle == 0)
+		hputnr8 (header, "CRPIX1", ndec1, naxis1-ctemp1);
+	    else if (angle == 90) {
+		hputnr8 (header, "CRPIX1", ndec2, naxis2-ctemp2);
+		hputnr8 (header, "CRPIX2", ndec1, naxis1-ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CRPIX1", ndec1, ctemp1);
+		hputnr8 (header, "CRPIX2", ndec2, naxis2-ctemp2);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CRPIX1", ndec2, ctemp2);
+		hputnr8 (header, "CRPIX2", ndec1, ctemp1);
+		}
+	    }
+	else {
+	    if (angle == 90) {
+		hputnr8 (header, "CRPIX1", ndec2, naxis2-ctemp2);
+		hputnr8 (header, "CRPIX2", ndec1, ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CRPIX1", ndec1, naxis1-ctemp1);
+		hputnr8 (header, "CRPIX2", ndec2, naxis2-ctemp2);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CRPIX1", ndec2, ctemp2);
+		hputnr8 (header, "CRPIX2", ndec1, naxis1-ctemp1);
+		}
+	    }
+	}
+
+    /* Reset CDELTn (degrees per pixel) */
+    if (hgetr8 (header, "CDELT1", &ctemp1) &&
+	hgetr8 (header, "CDELT2", &ctemp2)) { 
+	hgetndec (header, "CDELT1", &ndec1);
+	hgetndec (header, "CDELT2", &ndec2);
+	if (mirror) {
+	    if (angle == 0)
+		hputnr8 (header, "CDELT1", ndec1, -ctemp1);
+	    else if (angle == 90) {
+		hputnr8 (header, "CDELT1", ndec2, -ctemp2);
+		hputnr8 (header, "CDELT2", ndec1, -ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CDELT1", ndec1, ctemp1);
+		hputnr8 (header, "CDELT2", ndec2, -ctemp2);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CDELT1", ndec2, ctemp2);
+		hputnr8 (header, "CDELT2", ndec1, ctemp1);
+		}
+	    }
+	else {
+	    if (angle == 90) {
+		hputnr8 (header, "CDELT1", ndec2, -ctemp2);
+		hputnr8 (header, "CDELT2", ndec1, ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CDELT1", ndec1, -ctemp1);
+		hputnr8 (header, "CDELT2", ndec2, -ctemp2);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CDELT1", ndec2, ctemp2);
+		hputnr8 (header, "CDELT2", ndec1, -ctemp1);
+		}
+	    }
+	}
+
+    /* Reset CD matrix, if present */
+    ctemp1 = 0.0;
+    ctemp2 = 0.0;
+    ctemp3 = 0.0;
+    ctemp4 = 0.0;
+    if (hgetr8 (header, "CD1_1", &ctemp1)) {
+	hgetr8 (header, "CD1_2", &ctemp2);
+	hgetr8 (header, "CD2_1", &ctemp3);
+	hgetr8 (header, "CD2_2", &ctemp4);
+	hgetndec (header, "CD1_1", &ndec1);
+	hgetndec (header, "CD1_2", &ndec2);
+	hgetndec (header, "CD2_1", &ndec3);
+	hgetndec (header, "CD2_2", &ndec4);
+	if (mirror) {
+	    if (angle == 0) {
+		hputnr8 (header, "CD1_2", ndec2, -ctemp2);
+		hputnr8 (header, "CD2_1", ndec3, -ctemp3);
+		}
+	    else if (angle == 90) {
+		hputnr8 (header, "CD1_1", ndec4, -ctemp4);
+		hputnr8 (header, "CD1_2", ndec3, -ctemp3);
+		hputnr8 (header, "CD2_1", ndec2, -ctemp2);
+		hputnr8 (header, "CD2_2", ndec1, -ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CD1_1", ndec1, ctemp1);
+		hputnr8 (header, "CD1_2", ndec2, ctemp2);
+		hputnr8 (header, "CD2_1", ndec3, -ctemp3);
+		hputnr8 (header, "CD2_2", ndec4, -ctemp4);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CD1_1", ndec4, ctemp4);
+		hputnr8 (header, "CD1_2", ndec3, ctemp3);
+		hputnr8 (header, "CD2_1", ndec2, ctemp2);
+		hputnr8 (header, "CD2_2", ndec1, ctemp1);
+		}
+	    }
+	else {
+	    if (angle == 90) {
+		hputnr8 (header, "CD1_1", ndec4, -ctemp4);
+		hputnr8 (header, "CD1_2", ndec3, -ctemp3);
+		hputnr8 (header, "CD2_1", ndec2, ctemp2);
+		hputnr8 (header, "CD2_2", ndec1, ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CD1_1", ndec1, -ctemp1);
+		hputnr8 (header, "CD1_2", ndec2, -ctemp2);
+		hputnr8 (header, "CD2_1", ndec3, -ctemp3);
+		hputnr8 (header, "CD2_2", ndec4, -ctemp4);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CD1_1", ndec4, ctemp4);
+		hputnr8 (header, "CD1_2", ndec3, ctemp3);
+		hputnr8 (header, "CD2_1", ndec2, -ctemp2);
+		hputnr8 (header, "CD2_2", ndec1, -ctemp1);
+		}
+	    }
+	}
+
+    /* Delete any polynomial solution */
+    /* (These could maybe be switched, but I don't want to work them out yet */
+    if (ksearch (header, "CO1_1")) {
+	int i;
+	char keyword[16];
+
+	for (i = 1; i < 13; i++) {
+	    sprintf (keyword,"CO1_%d", i);
+	    hdel (header, keyword);
+	    }
+	for (i = 1; i < 13; i++) {
+	    sprintf (keyword,"CO2_%d", i);
+	    hdel (header, keyword);
+	    }
+	}
+
+    return;
+}
+
+/* May 29 1996	Change name from rotFITS to RotFITS
+ * Jun  4 1996	Fix bug when handling assymetrical images
+ * Jun  5 1996	Print filename, not pathname, in history
+ * Jun 10 1996	Remove unused variables after running lint
+ * Jun 13 1996	Replace image with rotated image
+ * Jun 18 1996	Fix formatting bug in history
+ *
+ * Jul 11 1997	If rotation is 360, flip top bottom if mirror flat is set
+ *
+ * Feb 23 1998	Do not delete WCS if image not rotated or mirrored
+ * May 26 1998	Rotate WCS instead of deleting it
+ * May 27 1998	Include imio.h
+
+ * Jun  8 1999	Return new image pointer instead of flag; do not free old image
+ * Jun  9 1999	Make history buffer 128 instead of 72 to avoid overflows
+ * Jun 10 1999	Drop image0; use image
+ * Oct 21 1999	Fix hputnr8() calls after lint
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Jan 17 2001	Reset coordinate direction if image is mirrored
+ * Jan 18 2001	Reset WCS scale if image is binned
+ * Nov 27 2001	Add error messages for all null returns
+ * Nov 27 2001	Add bitpix=8
+ *
+ * Jan 28 2004	Add xshift and yshift arguments to shift image
+ * Sep 15 2004	Fix bugs in calls to hgetr8 for crpix (found by Rob Creager)
+ *
+ * Aug 17 2005	Add mirror = 2 flag indicating a flip across x axis
+ *
+ * Jun 26 2008	Shift pixels if either xshift or yshift is not zero
+ */
diff --git a/Code/src/libwcs/imrotate1.c b/Code/src/libwcs/imrotate1.c
new file mode 100644
index 0000000000000000000000000000000000000000..4a000f5a5dc8dce585fae124f0325a30c0c2693a
--- /dev/null
+++ b/Code/src/libwcs/imrotate1.c
@@ -0,0 +1,668 @@
+/*** File libwcs/imrotate.c
+ *** June 30, 2008
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2008
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "fitsfile.h"
+
+static void RotWCSFITS();	/* rotate all the C* fields */
+
+/* Rotate an image by 90, 180, or 270 degrees, with an optional
+ * reflection across the vertical or horizontal axis.
+ * verbose generates extra info on stdout.
+ * return NULL if successful or rotated image.
+ */
+
+char *
+RotFITS (pathname,header,image0,xshift,yshift,rotate,mirror,bitpix2,rotwcs,
+	 verbose)
+
+char	*pathname;	/* Name of file which is being changed */
+char	*header;	/* FITS header */
+char	*image0;	/* Unrotated image pixels */
+int	xshift;		/* Number of pixels to shift image horizontally, +=right */
+int	yshift;		/* Number of pixels to shift image vertically, +=right */
+int	rotate;		/* Angle to by which to rotate image (90, 180, 270) */
+int	mirror;		/* Reflect image around 1=vertical, 2=horizontal axis */
+int	bitpix2;	/* Number of bits per pixel in output image */
+int	rotwcs;		/* If not =0, rotate WCS keywords, else leave them */
+int	verbose;
+
+{
+    int bitpix1, ny, nx, nax;
+    int x1, y1, x2, y2, nbytes;
+    char *rotimage;
+    char *image;
+    char history[128];
+    char *filename;
+    double crpix;
+
+    image = NULL;
+    rotimage = NULL;
+
+    if (rotate == 1)
+	rotate = 90;
+    else if (rotate == 2)
+	rotate = 180;
+    else if (rotate == 3)
+	rotate = 270;
+    else if (rotate < 0)
+	rotate = rotate + 360;
+
+    filename = strrchr (pathname,'/');
+    if (filename)
+	filename = filename + 1;
+    else
+	filename = pathname;
+
+    /* Get image size */
+    nax = 0;
+    if (hgeti4 (header,"NAXIS",&nax) < 1) {
+	if (verbose)
+	    printf ("RotFITS: Not an image (NAXIS=%d)\n",nax);
+	return (NULL);
+	}
+    else {
+	if (hgeti4 (header,"NAXIS1",&nx) < 1) {
+	    if (verbose)
+		printf ("RotFITS: Not an image (NAXIS1=%d)\n",nx);
+	    return (NULL);
+	    }
+	else {
+	    if (hgeti4 (header,"NAXIS2",&ny) < 1) {
+		if (verbose)
+		    printf ("RotFITS: Not an image (NAXIS2=%d)\n",ny);
+		return (NULL);
+		}
+	    }
+	}
+    bitpix1 = 16;
+    hgeti4 (header,"BITPIX", &bitpix1);
+    if (bitpix2 == 0)
+	bitpix2 = bitpix1;
+
+    /* Shift WCS fields in header */
+    if (rotwcs && hgetr8 (header, "CRPIX1", &crpix)) {
+	crpix = crpix + xshift;
+	hputr8 (header, "CRPIX1", crpix);
+	}
+    if (rotwcs && hgetr8 (header, "CRPIX2", &crpix)) {
+	crpix = crpix + yshift;
+	hputr8 (header, "CRPIX2", crpix);
+	}
+
+    /* Rotate WCS fields in header */
+    if (rotwcs && (rotate != 0 || mirror))
+	RotWCSFITS (header, rotate, mirror, verbose);
+
+    /* Compute size of image in bytes */
+    switch (bitpix2) {
+	case 8:
+	    nbytes = nx * ny;
+	    break;
+	case 16:
+	    nbytes = nx * ny * 2;
+	    break;
+	case 32:
+	    nbytes = nx * ny * 4;
+	    break;
+	case -16:
+	    nbytes = nx * ny * 2;
+	    break;
+	case -32:
+	    nbytes = nx * ny * 4;
+	    break;
+	case -64:
+	    nbytes = nx * ny * 8;
+	    break;
+	default:
+	    if (verbose)
+		printf ("RotFITS: Illegal BITPIX (%d)\n", bitpix2);
+	    return (NULL);
+	}
+
+    if (bitpix1 != bitpix2) {
+	sprintf (history,"Copy of image %s bits per pixel %d -> %d",
+		filename, bitpix1, bitpix2);
+	hputc (header,"HISTORY",history);
+	if (verbose)
+	    fprintf (stderr,"%s\n",history);
+	}
+
+    /* Shift image first */
+    if (xshift != 0 || yshift != 0) {
+
+	/* Allocate buffer for shifted image */
+	image = (char *) calloc (nbytes, 1);
+	if (image == NULL) {
+	    if (verbose)
+		printf ("RotFITS: Cannot allocate %d bytes for shifted image\n", nbytes);
+	    return (NULL);
+	    }
+
+	for (x1 = 0; x1 < nx; x1++) {
+	    x2 = x1 + xshift;
+	    for (y1 = 0; y1 < ny; y1++) {
+		y2 = y1 + yshift;
+		if (y2 < ny)
+		    movepix (image0,bitpix1,nx,x1,y1,image,bitpix2,nx,x2,y2);
+		}
+	    }
+	sprintf (history,"Copy of image %s shifted by dx=%d dy=%d",
+		 filename, xshift, yshift);
+	hputc (header,"HISTORY",history);
+	if (rotate == 0 && !mirror)
+	    return (image);
+	}
+    else
+	image = image0;
+
+    /* Allocate buffer for rotated image */
+    rotimage = (char *) calloc (nbytes, 1);
+    if (rotimage == NULL) {
+	if (verbose)
+	    printf ("RotFITS: Cannot allocate %d bytes for new image\n", nbytes);
+	return (NULL);
+	}
+
+    /* Mirror image without rotation */
+    if (rotate < 45 && rotate > -45) {
+	if (mirror == 1) {
+	    for (x1 = 0; x1 < nx; x1++) {
+		x2 = nx - x1 - 1;
+		for (y1 = 0; y1 < ny; y1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x2,y1);
+		    }
+		}
+	    sprintf (history,"Copy of image %s reflected",filename);
+	    hputc (header,"HISTORY",history);
+	    }
+	else if (mirror == 2) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		y2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x1,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s flipped",filename);
+	    hputc (header,"HISTORY",history);
+	    }
+	else {
+	    for (y1 = 0; y1 < ny; y1++) {
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x1,y1);
+		    }
+		}
+	    }
+	}
+
+    /* Rotate by 90 degrees */
+    else if (rotate >= 45 && rotate < 135) {
+	if (mirror == 1) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		x2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    y2 = nx - x1 - 1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s reflected, rotated 90 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else if (mirror == 2) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,y1,x1);
+		    }
+		}
+	    sprintf (history,"Copy of image %s flipped, rotated 90 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else {
+	    for (y1 = 0; y1 < ny; y1++) {
+		x2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    y2 = x1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s rotated 90 degrees",filename);
+            hputc (header,"HISTORY",history);
+	    }
+	hputi4 (header,"NAXIS1",ny);
+	hputi4 (header,"NAXIS2",nx);
+	}
+
+    /* Rotate by 180 degrees */
+    else if (rotate >= 135 && rotate < 225) {
+	if (mirror == 1) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		y2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x1,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s reflected, rotated 180 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else if (mirror == 2) {
+	    for (x1 = 0; x1 < nx; x1++) {
+		x2 = nx - x1 - 1;
+		for (y1 = 0; y1 < ny; y1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x2,y1);
+		    }
+		}
+	    sprintf (history,"Copy of image %s flipped, rotated 180 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else {
+	    for (y1 = 0; y1 < ny; y1++) {
+		y2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    x2 = nx - x1 - 1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,nx,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s rotated 180 degrees",filename);
+            hputc (header,"HISTORY",history);
+	    }
+	}
+
+    /* Rotate by 270 degrees */
+    else if (rotate >= 225 && rotate < 315) {
+	if (mirror == 1) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		for (x1 = 0; x1 < nx; x1++) {
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,y1,x1);
+		    }
+		}
+	    sprintf (history,"Copy of image %s reflected, rotated 270 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else if (mirror == 2) {
+	    for (y1 = 0; y1 < ny; y1++) {
+		x2 = ny - y1 - 1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    y2 = nx - x1 - 1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s flipped, rotated 270 degrees",
+		     filename);
+            hputc (header,"HISTORY",history);
+	    }
+	else {
+	    for (y1 = 0; y1 < ny; y1++) {
+		x2 = y1;
+		for (x1 = 0; x1 < nx; x1++) {
+		    y2 = nx - x1 - 1;
+		    movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		    }
+		}
+	    sprintf (history,"Copy of image %s rotated 270 degrees",filename);
+            hputc (header,"HISTORY",history);
+	    }
+	hputi4 (header,"NAXIS1",ny);
+	hputi4 (header,"NAXIS2",nx);
+	}
+
+    /* If rotating by more than 315 degrees, assume top-bottom reflection */
+    else if (rotate >= 315 && mirror) {
+	for (y1 = 0; y1 < ny; y1++) {
+	    for (x1 = 0; x1 < nx; x1++) {
+		x2 = y1;
+		y2 = x1;
+		movepix (image,bitpix1,nx,x1,y1,rotimage,bitpix2,ny,x2,y2);
+		}
+	    }
+	sprintf (history,"Copy of image %s reflected top to bottom",filename);
+        hputc (header,"HISTORY",history);
+	}
+    
+    if (verbose)
+	fprintf (stderr,"%s\n",history);
+
+    return (rotimage);
+}
+
+
+/* rotate all the C* fields.
+ * return 0 if at least one such field is found, else -1.  */
+
+static void
+RotWCSFITS (header, angle, mirror, verbose)
+
+char	*header;	/* FITS header */
+int	angle;		/* Angle to be rotated (0, 90, 180, 270) */
+int	mirror;		/* Reflect image around 1=vertical, 2=horizontal axis */
+int	verbose;	/* Print progress if 1 */
+
+{
+    static char flds[15][8];
+    char ctype1[16], ctype2[16];
+    double ctemp1, ctemp2, ctemp3, ctemp4, naxis1, naxis2;
+    int i, n, ndec1, ndec2, ndec3, ndec4;
+
+    strcpy (flds[0], "CTYPE1");
+    strcpy (flds[1], "CTYPE2");
+    strcpy (flds[2], "CRVAL1");
+    strcpy (flds[3], "CRVAL2");
+    strcpy (flds[4], "CDELT1");
+    strcpy (flds[5], "CDELT2");
+    strcpy (flds[6], "CRPIX1");
+    strcpy (flds[7], "CRPIX2");
+    strcpy (flds[8], "CROTA1");
+    strcpy (flds[9], "CROTA2");
+    strcpy (flds[10], "IMWCS");
+    strcpy (flds[11], "CD1_1");
+    strcpy (flds[12], "CD1_2");
+    strcpy (flds[13], "CD2_1");
+    strcpy (flds[14], "CD2_2");
+
+    n = 0;
+    hgetr8 (header, "NAXIS1", &naxis1);
+    hgetr8 (header, "NAXIS2", &naxis2);
+
+    /* Find out if there any WCS keywords in this header */
+    for (i = 0; i < sizeof(flds)/sizeof(flds[0]); i++) {
+	if (ksearch (header, flds[i]) != NULL) {
+	    n++;
+	    if (verbose)
+		fprintf (stderr,"%s: found\n", flds[i]);
+	    }
+	}
+
+    /* Return if no WCS keywords to change */
+    if (n == 0) {
+	if (verbose)
+	    fprintf (stderr,"RotWCSFITS: No WCS in header\n");
+	return;
+	}
+
+    /* Reset CROTAn and CD matrix if axes have been exchanged */
+    if (angle == 90) {
+	if (hgetr8 (header, "CROTA1", &ctemp1)) {
+	    hgetndec (header, "CROTA1", &ndec1);
+	    hputnr8 (header, "CROTA1", ndec1, ctemp1+90.0);
+	    }
+	if (hgetr8 (header, "CROTA2", &ctemp2)) {
+	    hgetndec (header, "CROTA2", &ndec2);
+	    hputnr8 (header, "CROTA2", ndec2, ctemp2+90.0);
+	    }
+	}
+
+    /* Negate rotation angle if mirrored or flipped */
+    if (mirror > 0) {
+	if (hgetr8 (header, "CROTA1", &ctemp1)) {
+	    hgetndec (header, "CROTA1", &ndec1);
+	    hputnr8 (header, "CROTA1", ndec1, -ctemp1);
+	    }
+	if (hgetr8 (header, "CROTA2", &ctemp2)) {
+	    hgetndec (header, "CROTA2", &ndec2);
+	    hputnr8 (header, "CROTA2", ndec2, -ctemp2);
+	    }
+	if (hgetr8 (header, "LTM1_1", &ctemp1)) {
+	    hgetndec (header, "LTM1_1", &ndec1);
+	    hputnr8 (header, "LTM1_1", ndec1, -ctemp1);
+	    }
+	if (mirror == 1) {
+	    if (hgetr8 (header, "CD1_1", &ctemp1))
+		hputr8 (header, "CD1_1", -ctemp1);
+	    }
+	if (mirror == 2) {
+	    if (hgetr8 (header, "CD2_2", &ctemp1))
+		hputr8 (header, "CD2_2", -ctemp1);
+	    }
+	if (hgetr8 (header, "CD1_2", &ctemp1))
+	    hputr8 (header, "CD1_2", -ctemp1);
+	if (hgetr8 (header, "CD2_1", &ctemp1))
+	    hputr8 (header, "CD2_1", -ctemp1);
+	}
+
+    /* Unbin CRPIX and CD matrix */
+    if (hgetr8 (header, "LTM1_1", &ctemp1)) {
+	if (ctemp1 != 1.0) {
+	    if (hgetr8 (header, "LTM2_2", &ctemp2)) {
+		if (ctemp1 == ctemp2) {
+		    double ltv1 = 0.0;
+		    double ltv2 = 0.0;
+		    if (hgetr8 (header, "LTV1", &ltv1))
+			hdel (header, "LTV1");
+		    if (hgetr8 (header, "LTV2", &ltv1))
+			hdel (header, "LTV2");
+		    if (hgetr8 (header, "CRPIX1", &ctemp3))
+			hputr8 (header, "CRPIX1", (ctemp3-ltv1)/ctemp1);
+		    if (hgetr8 (header, "CRPIX2", &ctemp3))
+			hputr8 (header, "CRPIX2", (ctemp3-ltv2)/ctemp1);
+		    if (hgetr8 (header, "CD1_1", &ctemp3))
+			hputr8 (header, "CD1_1", ctemp3/ctemp1);
+		    if (hgetr8 (header, "CD1_2", &ctemp3))
+			hputr8 (header, "CD1_2", ctemp3/ctemp1);
+		    if (hgetr8 (header, "CD2_1", &ctemp3))
+			hputr8 (header, "CD2_1", ctemp3/ctemp1);
+		    if (hgetr8 (header, "CD2_2", &ctemp3))
+			hputr8 (header, "CD2_2", ctemp3/ctemp1);
+		    hdel (header, "LTM1_1");
+		    hdel (header, "LTM2_2");
+		    }
+		}
+	    }
+	}
+
+    /* Reset CRPIXn */
+    if (hgetr8 (header, "CRPIX1", &ctemp1) &&
+	hgetr8 (header, "CRPIX2", &ctemp2)) { 
+	hgetndec (header, "CRPIX1", &ndec1);
+	hgetndec (header, "CRPIX2", &ndec2);
+	if (mirror) {
+	    if (angle == 0)
+		hputnr8 (header, "CRPIX1", ndec1, naxis1-ctemp1);
+	    else if (angle == 90) {
+		hputnr8 (header, "CRPIX1", ndec2, naxis2-ctemp2);
+		hputnr8 (header, "CRPIX2", ndec1, naxis1-ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CRPIX1", ndec1, ctemp1);
+		hputnr8 (header, "CRPIX2", ndec2, naxis2-ctemp2);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CRPIX1", ndec2, ctemp2);
+		hputnr8 (header, "CRPIX2", ndec1, ctemp1);
+		}
+	    }
+	else {
+	    if (angle == 90) {
+		hputnr8 (header, "CRPIX1", ndec2, naxis2-ctemp2);
+		hputnr8 (header, "CRPIX2", ndec1, ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CRPIX1", ndec1, naxis1-ctemp1);
+		hputnr8 (header, "CRPIX2", ndec2, naxis2-ctemp2);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CRPIX1", ndec2, ctemp2);
+		hputnr8 (header, "CRPIX2", ndec1, naxis1-ctemp1);
+		}
+	    }
+	}
+
+    /* Reset CDELTn (degrees per pixel) */
+    if (hgetr8 (header, "CDELT1", &ctemp1) &&
+	hgetr8 (header, "CDELT2", &ctemp2)) { 
+	hgetndec (header, "CDELT1", &ndec1);
+	hgetndec (header, "CDELT2", &ndec2);
+	if (mirror) {
+	    if (angle == 0)
+		hputnr8 (header, "CDELT1", ndec1, -ctemp1);
+	    else if (angle == 90) {
+		hputnr8 (header, "CDELT1", ndec2, -ctemp2);
+		hputnr8 (header, "CDELT2", ndec1, -ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CDELT1", ndec1, ctemp1);
+		hputnr8 (header, "CDELT2", ndec2, -ctemp2);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CDELT1", ndec2, ctemp2);
+		hputnr8 (header, "CDELT2", ndec1, ctemp1);
+		}
+	    }
+	else {
+	    if (angle == 90) {
+		hputnr8 (header, "CDELT1", ndec2, -ctemp2);
+		hputnr8 (header, "CDELT2", ndec1, ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CDELT1", ndec1, -ctemp1);
+		hputnr8 (header, "CDELT2", ndec2, -ctemp2);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CDELT1", ndec2, ctemp2);
+		hputnr8 (header, "CDELT2", ndec1, -ctemp1);
+		}
+	    }
+	}
+
+    /* Reset CD matrix, if present */
+    ctemp1 = 0.0;
+    ctemp2 = 0.0;
+    ctemp3 = 0.0;
+    ctemp4 = 0.0;
+    if (hgetr8 (header, "CD1_1", &ctemp1)) {
+	hgetr8 (header, "CD1_2", &ctemp2);
+	hgetr8 (header, "CD2_1", &ctemp3);
+	hgetr8 (header, "CD2_2", &ctemp4);
+	hgetndec (header, "CD1_1", &ndec1);
+	hgetndec (header, "CD1_2", &ndec2);
+	hgetndec (header, "CD2_1", &ndec3);
+	hgetndec (header, "CD2_2", &ndec4);
+	if (mirror == 1) {
+	    if (angle == 0) {
+		hputnr8 (header, "CD1_2", ndec2, -ctemp2);
+		hputnr8 (header, "CD2_1", ndec3, -ctemp3);
+		}
+	    else if (angle == 90) {
+		hputnr8 (header, "CD1_1", ndec4, -ctemp4);
+		hputnr8 (header, "CD1_2", ndec3, -ctemp3);
+		hputnr8 (header, "CD2_1", ndec2, -ctemp2);
+		hputnr8 (header, "CD2_2", ndec1, -ctemp1);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CD1_1", ndec1, ctemp1);
+		hputnr8 (header, "CD1_2", ndec2, ctemp2);
+		hputnr8 (header, "CD2_1", ndec3, -ctemp3);
+		hputnr8 (header, "CD2_2", ndec4, -ctemp4);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CD1_1", ndec4, ctemp4);
+		hputnr8 (header, "CD1_2", ndec3, ctemp3);
+		hputnr8 (header, "CD2_1", ndec2, ctemp2);
+		hputnr8 (header, "CD2_2", ndec1, ctemp1);
+		}
+	    }
+	else {
+	    if (angle == 90) {
+		hputnr8 (header, "CD1_1", ndec2, -ctemp2);
+		hputnr8 (header, "CD1_2", ndec1, ctemp1);
+		hputnr8 (header, "CD2_1", ndec4, -ctemp4);
+		hputnr8 (header, "CD2_2", ndec3, ctemp3);
+		}
+	    else if (angle == 180) {
+		hputnr8 (header, "CD1_1", ndec1, -ctemp1);
+		hputnr8 (header, "CD1_2", ndec2, -ctemp2);
+		hputnr8 (header, "CD2_1", ndec3, -ctemp3);
+		hputnr8 (header, "CD2_2", ndec4, -ctemp4);
+		}
+	    else if (angle == 270) {
+		hputnr8 (header, "CD1_1", ndec2, ctemp2);
+		hputnr8 (header, "CD1_2", ndec1, -ctemp1);
+		hputnr8 (header, "CD2_1", ndec4, ctemp4);
+		hputnr8 (header, "CD2_2", ndec3, -ctemp3);
+		}
+	    }
+	}
+
+    /* Delete any polynomial solution */
+    /* (These could maybe be switched, but I don't want to work them out yet */
+    if (ksearch (header, "CO1_1")) {
+	int i;
+	char keyword[16];
+
+	for (i = 1; i < 13; i++) {
+	    sprintf (keyword,"CO1_%d", i);
+	    hdel (header, keyword);
+	    }
+	for (i = 1; i < 13; i++) {
+	    sprintf (keyword,"CO2_%d", i);
+	    hdel (header, keyword);
+	    }
+	}
+
+    return;
+}
+
+/* May 29 1996	Change name from rotFITS to RotFITS
+ * Jun  4 1996	Fix bug when handling assymetrical images
+ * Jun  5 1996	Print filename, not pathname, in history
+ * Jun 10 1996	Remove unused variables after running lint
+ * Jun 13 1996	Replace image with rotated image
+ * Jun 18 1996	Fix formatting bug in history
+ *
+ * Jul 11 1997	If rotation is 360, flip top bottom if mirror flat is set
+ *
+ * Feb 23 1998	Do not delete WCS if image not rotated or mirrored
+ * May 26 1998	Rotate WCS instead of deleting it
+ * May 27 1998	Include imio.h
+
+ * Jun  8 1999	Return new image pointer instead of flag; do not free old image
+ * Jun  9 1999	Make history buffer 128 instead of 72 to avoid overflows
+ * Jun 10 1999	Drop image0; use image
+ * Oct 21 1999	Fix hputnr8() calls after lint
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Jan 17 2001	Reset coordinate direction if image is mirrored
+ * Jan 18 2001	Reset WCS scale if image is binned
+ * Nov 27 2001	Add error messages for all null returns
+ * Nov 27 2001	Add bitpix=8
+ *
+ * Jan 28 2004	Add xshift and yshift arguments to shift image
+ * Sep 15 2004	Fix bugs in calls to hgetr8 for crpix (found by Rob Creager)
+ *
+ * Aug 17 2005	Add mirror = 2 flag indicating a flip across x axis
+ *
+ * Jun 26 2008	Shift pixels if either xshift or yshift is not zero
+ * Jun 30 2008	Correct WCS changes as suggested by Ed Los
+ */
diff --git a/Code/src/libwcs/imsetwcs.c b/Code/src/libwcs/imsetwcs.c
new file mode 100644
index 0000000000000000000000000000000000000000..40fd05f1229ee0ab94b1f6be67970b970e0bb411
--- /dev/null
+++ b/Code/src/libwcs/imsetwcs.c
@@ -0,0 +1,1403 @@
+/*** File libwcs/imsetwcs.c
+ *** September 24, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu (based on UIowa code)
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#include "wcs.h"
+#include "lwcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+extern int FindStars();
+extern int TriMatch();
+extern int FocasMatch();
+extern int StarMatch();
+extern int ReadMatch();
+extern int FitMatch();
+extern int WCSMatch();
+extern int FitPlate();
+extern struct WorldCoor *GetFITSWCS ();
+extern char *getimcat();
+extern void SetFITSWCS();
+extern int iscdfit();
+extern void setminbin();
+extern void setnfit();
+extern int getnfit();
+
+/* Set the C* WCS fields in a FITS header based on a reference catalog
+ * by finding stars in the image and in the reference catalog and
+ * fitting the scaling, rotation, and offsets.
+ * verbose generates extra info on stderr.
+ * Try using deeper reference star catalog searches if there is trouble.
+ * Return 1 if all ok, else 0
+ */
+
+/* These parameters can be set on the command line */
+static double tolerance = PIXDIFF;	/* +/- this many pixels is a hit */
+static double refmag1 = MAGLIM1;	/* reference catalog magnitude limit */
+static double refmag2 = MAGLIM2;	/* reference catalog magnitude limit */
+static double frac = 1.0;	/* Additional catalog/image stars */
+static int nofit = 0;		/* if =1, do not fit WCS */
+static int maxcat = MAXSTARS;	/* Maximum number of catalog stars to use */
+static int fitwcs = 1;		/* If 1, fit WCS, else use current WCS */
+static int fitplate = 0;	/* If 1, fit polynomial, else do not */
+static double imfrac0 = 0.0;	/* If > 0.0, multiply image dimensions
+					   by this for search */
+static int iterate0 = 0;	/* If 1, search field again */
+static int toliterate0 = 0;	/* if 1, halve tolerances when iter */
+static int nfiterate0 = 0;	/* if 1, add two parameters to fit */
+static int recenter0 = 0;	/* If 1, search again with new center*/
+static char matchcat[32]="";	/* Match catalog name */
+static int irafout = 0;		/* if 1, write X Y RA Dec out */
+static int magfit = 0;		/* If 1, write magnitude polynomial(s) */
+static int sortmag = 1;		/* Magnitude by which to sort stars */
+static int minstars0 = MINSTARS;	/* Number of star matches for fit */
+static int nxydec = NXYDEC;	/* Number of decimal places in image coordinates */
+static void PrintRes();
+static void CompRes();
+extern void SetFITSPlate();
+
+static char *kwt = NULL;        /* Keyword returned by ctgread() */
+void settabkrw (keyword0)
+char *keyword0;
+{ kwt = keyword0; return; }
+
+
+/* Set the C* WCS fields in the input image header based on the given limiting
+ * reference mag.
+ * Finding stars in the input image and in the reference catalog between
+ * refmag1 and refmag2 and compute the angle and offsets which result in the best fit.
+ * verbose generates extra info on stdout.
+ * return 0 if all ok, else -1
+ */
+
+int
+SetWCSFITS (filename, header, image, refcatname, verbose)
+
+char	*filename;	/* image file name */
+char	*header;	/* FITS header */
+char	*image;		/* Image pixels */
+char	*refcatname;	/* Name of reference catalog */
+int	verbose;
+
+{
+    double *gnum;	/* Reference star numbers */
+    double *gra;	/* Reference star right ascensions in degrees */
+    double *gdec;	/* Reference star declinations in degrees */
+    double *gpra;	/* Reference star right ascension proper motions (deg)*/
+    double *gpdec;	/* Reference star declination proper motions (deg) */
+    double **gm;	/* Reference star magnitudes */
+    double *gx;		/* Reference star image X-coordinates in pixels */
+    double *gy;		/* Reference star image Y-coordinates in pixels */
+    int *gc;		/* Reference object types */
+    int *goff;		/* Reference star offscale flags */
+    int ng;		/* Number of reference stars in image */
+    int nbg;		/* Number of brightest reference stars from search */
+    int nrg;		/* Number of brightest reference stars actually used */
+    double *sx;		/* Image star image X-coordinates in pixels */
+    double *sy;		/* Image star image X-coordinates in pixels */
+    double *sm;		/* Image star instrumental magnitude */
+    int *sp;		/* Image star peak fluxes in counts */
+    int ns;		/* Number of image stars */
+    int nbs;		/* Number of brightest image stars actually used */
+    double cra, cdec;	/* Nominal center in degrees from RA/DEC FITS fields */
+    double dra, ddec;	/* Image half-widths in degrees */
+    double secpix;	/* Pixel size in arcseconds */
+    int imw, imh;	/* Image size, pixels */
+    int imsearch = 1;	/* Flag set if image should be searched for sources */
+    int nmax;		/* Maximum number of matches possible (nrg or nbs) */
+    int lofld = 0;	/* Length of object name field in output */
+    double mag1,mag2;
+    int refcat;		/* reference catalog switch */
+    int nmag, mprop;
+    double dxys;
+    char numstr[32];
+    int minstars;
+    int ngmax;
+    int nbin, nbytes;
+    int iterate, toliterate, nfiterate;
+    int imag, magsort;
+    int niter = 0;
+    int recenter = recenter0;
+    int ret = 0;
+    int is, ig, igs, i, j;
+    char rstr[32], dstr[32];
+    double refeq, refep;
+    double maxnum;
+    int nnfld;
+    int refsys;
+    char refcoor[8];
+    char title[80];
+    char *imcatname;	/* file name for image star catalog, if used */
+    struct WorldCoor *wcs=0;	/* WCS structure */
+    double *sx1, *sy1, *sm1, *gra1, *gdec1, *gnum1, *gm1;
+    char **gobj, **gobj1;	/* Catalog star object names */
+    double imfrac = imfrac0;
+    int nmatch;
+    double ra,dec;
+    double dx, dy, dx2, dy2, dxy;
+    struct StarCat *starcat;
+    int npfit;
+    int nndec;
+    extern int NParamFit();
+    extern void setdcenter(),setsys(),setrefpix(),setsecpix();
+    extern void setsecpix2(),setrot();
+
+    iterate = iterate0;
+    toliterate = toliterate0;
+    nfiterate = nfiterate0;
+    gnum = NULL;
+    gra = NULL;
+    gdec = NULL;
+    gpra = NULL;
+    gpdec = NULL;
+    gm = NULL;
+    gx = NULL;
+    gy = NULL;
+    gc = NULL;
+    gobj = NULL;
+    gobj1 = NULL;
+    goff = NULL;
+    sm = NULL;
+    sx = NULL;
+    sy = NULL;
+    sp = NULL;
+    starcat = NULL;
+    imcatname = NULL;
+    ns = 0;
+
+    if (refmag1 == refmag2) {
+	mag1 = 0.0;
+	mag2 = 0.0;
+	}
+    else {
+	mag1 = refmag1;
+	mag2 = refmag2;
+	}
+
+    /* Use already-matched stars first, if they are present */
+    if (strlen (matchcat) > 0) {
+	refsys = WCS_J2000;
+	refeq = 2000.0;
+	if ((nbin = ReadMatch (matchcat, &sx, &sy, &gra, &gdec, verbose)) < 1) {
+	    ret = 0;
+	    goto out;
+	    }
+
+	if (nbin > 1)
+	    WCSMatch (nbin, sx, sy, gra, gdec, verbose);
+
+	/* Set WCS from image header and command line */
+	wcs = GetFITSWCS (filename,header,verbose,&cra,&cdec,&dra,&ddec,
+			  &secpix, &imw,&imh,&refsys, &refeq);
+	if (nowcs (wcs)) {
+	    ret = 0;
+	    goto out;
+	    }
+	if (getnfit() == 0) {
+	    SetFITSWCS (header, wcs);
+	    return (1);
+	    }
+	nbin = FitMatch (nbin, sx, sy, gra, gdec, wcs, verbose);
+	sm = (double *) calloc (nbin, sizeof (double));
+	hputs (header, "WCSRFCAT", matchcat);
+	hputs (header, "WCSIMCAT", matchcat);
+	hputi4 (header, "WCSMATCH", nbin);
+	hputi4 (header, "WCSNREF", nbin);
+	if (!(gnum = (double *) calloc (nbin, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for gnum\n",
+		     nbin*sizeof(double));
+	for (is = 0; is < nbin; is++)
+	    gnum[is] = (double)(is + 1);
+	if (!(gx = (double *) calloc (nbin, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for gx\n",
+		     nbin*sizeof(double));
+	if (!(gy = (double *) calloc (nbin, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for gy\n",
+		     nbin*sizeof(double));
+	if (!(goff = (int *) calloc (nbin, sizeof(int))))
+	    fprintf (stderr, "Could not calloc %d bytes for goff\n",
+		     nbin*sizeof(double));
+
+	SetFITSWCS (header, wcs);
+	nrg = nbin;
+	ns = nbin;
+	if (refcatname == NULL)
+	    goto match;
+	}
+
+    /* Set reference catalog coordinate system and epoch */
+    if (nofit) {
+	refsys = 0;
+	refeq = 0.0;
+	refcat = 0;
+	}
+    else {
+	refcat = RefCat (refcatname,title,&refsys,&refeq,&refep,&mprop,&nmag);
+	wcscstr (refcoor, refsys, refeq, refep);
+	}
+
+    /* get nominal position and scale */
+getfield:
+    wcs = GetFITSWCS (filename,header,verbose,&cra,&cdec,&dra,&ddec,&secpix,
+		      &imw,&imh,&refsys, &refeq);
+    if (nowcs (wcs)) {
+	ret = 0;
+	goto out;
+	}
+
+    refep = wcs->epoch;
+    if (nofit) {
+	SetFITSWCS (header, wcs);
+	ret = 1;
+	goto out;
+	}
+
+    if (fitwcs) {
+	wcs->prjcode = WCS_TAN;
+	wcseqset (wcs, refeq);
+	}
+
+    if (refcatname == NULL) {
+	refcatname = CatName (refcat, refcatname);
+	if (refcatname == NULL) {
+	    ret = 0;
+	    goto out;
+	    }
+	}
+
+    if (imfrac > 0.0) {
+	dra = dra * imfrac;
+	ddec = ddec * imfrac;
+	}
+    if (sortmag > 9)
+	sortmag = CatMagNum (sortmag, refcat);
+
+    /* Allocate arrays for results of reference star search */
+    ngmax = maxcat;
+    if (imfrac > 1.0)
+	ngmax = (int) ((double) ngmax * imfrac * imfrac);
+    nbytes = ngmax * sizeof (double);
+    if (!(gnum = (double *) calloc (ngmax, sizeof(double))))
+	fprintf (stderr, "Could not calloc %d bytes for gnum\n",
+		 ngmax*sizeof(double));
+    if (!(gra = (double *) calloc (ngmax, sizeof(double))))
+	fprintf (stderr, "Could not calloc %d bytes for gra\n",
+		 ngmax*sizeof(double));
+    if (!(gdec = (double *) calloc (ngmax, sizeof(double))))
+	fprintf (stderr, "Could not calloc %d bytes for gdec\n",
+		 ngmax*sizeof(double));
+    if (!(gpra = (double *) calloc (ngmax, sizeof(double))))
+	fprintf (stderr, "Could not calloc %d bytes for gpra\n",
+		 ngmax*sizeof(double));
+    if (!(gpdec = (double *) calloc (ngmax, sizeof(double))))
+	fprintf (stderr, "Could not calloc %d bytes for gpdec\n",
+		 ngmax*sizeof(double));
+    if (!(gm = (double **) calloc (nmag, sizeof(double *))))
+	fprintf (stderr, "Could not calloc %d bytes for gm\n",
+		 nmag*sizeof(double *));
+    else {
+	for (imag = 0; imag < nmag; imag++) {
+	    if (!(gm[imag] = (double *) calloc (ngmax, sizeof(double))))
+		fprintf (stderr, "Could not calloc %d bytes for gm\n",
+		    ngmax*sizeof(double));
+	    }
+	}
+    if (!(gc = (int *) calloc (ngmax, sizeof(int))))
+	fprintf (stderr, "Could not calloc %d bytes for gc\n",
+		 ngmax*sizeof(int));
+    if (!(gobj = (char **) calloc (ngmax, sizeof(char *))))
+	fprintf (stderr, "Could not calloc %d bytes for obj\n",
+		 ngmax*sizeof(char *));
+    else {
+	for (i = 0; i < ngmax; i++)
+	    gobj[i] = NULL;
+	}
+
+    /* Find the nearby reference stars, in ra/dec */
+getstars:
+    ng = ctgread (refcatname,refcat,0,cra,cdec,dra,ddec,0.0,0.0,refsys,refeq,
+		  refep,mag1,mag2,sortmag,ngmax,&starcat,
+		  gnum,gra,gdec,gpra,gpdec,gm,gc,gobj,verbose*100);
+    if (ng > ngmax)
+	nrg = ngmax;
+    else
+	nrg = ng;
+    if (gobj[0] == NULL)
+	gobj1 = NULL;
+    else
+	gobj1 = gobj;
+
+    minstars = minstars0;
+    npfit = NParamFit (100);
+    if (npfit < minstars0)
+	minstars = 1;
+
+    if (sortmag > 0 && sortmag <= nmag)
+	magsort = sortmag - 1;
+    else
+	magsort = 0;
+
+    /* Sort reference stars by brightness (magnitude) */
+    MagSortStars (gnum, gra, gdec, gpra, gpdec, NULL, NULL, gm, gc, gobj1, nrg, 
+		  nmag, sortmag);
+
+    /* Project the reference stars into pixels on a plane at ra0/dec0 */
+    if (!(gx = (double *) calloc (ngmax, sizeof(double))))
+	fprintf (stderr, "Could not calloc %d bytes for gx\n",
+		 ngmax*sizeof(double));
+    if (!(gy = (double *) calloc (ngmax, sizeof(double))))
+	fprintf (stderr, "Could not calloc %d bytes for gy\n",
+		 ngmax*sizeof(double));
+    if (!(goff = (int *) calloc (ngmax, sizeof(int))))
+	fprintf (stderr, "Could not calloc %d bytes for gy\n",
+		 ngmax*sizeof(double));
+    if (!gx || !gy || !goff) {
+	ret = 0;
+	goto out;
+	}
+
+    /* use the nominal WCS info to find x/y on image */
+    for (ig = 0; ig < nrg; ig++) {
+	gx[ig] = 0.0;
+	gy[ig] = 0.0;
+	wcs2pix (wcs, gra[ig], gdec[ig], &gx[ig], &gy[ig], &goff[ig]);
+	}
+
+    /* Note how reference stars were selected */
+    if (ng > ngmax) {
+	if (verbose)
+	    fprintf (stderr,"Using %d / %d reference stars brighter than %.1f\n",
+		     nrg, ng, gm[magsort][nrg-1]);
+	}
+    else {
+	if (verbose) {
+	    if (refmag1 > 0.0 && refmag2 > 0.0)
+		fprintf (stderr,"Using all %d reference stars from %.1f to %.1f mag.\n",
+			ng, refmag1, refmag2);
+	    else if (refmag2 > 0.0)
+		fprintf (stderr,"Using all %d reference stars brighter than %.1f\n",
+			ng,refmag2);
+	    else
+		fprintf (stderr,"using all %d reference stars\n", ng);
+	    }
+	}
+
+    if (verbose) {
+	fprintf (stderr,"%s:\n",refcatname);
+	for (ig = 0; ig < nrg; ig++) {
+	    if (ig == 0)
+		maxnum = gnum[ig];
+	    else if (gnum[ig] > maxnum)
+		maxnum = gnum[ig];
+	    }
+	nnfld = CatNumLen (refcat, maxnum, 0);
+	nndec = 0;
+	for (ig = 0; ig < nrg; ig++) {
+	    ra2str (rstr, 32, gra[ig], 3);
+	    dec2str (dstr, 32, gdec[ig], 2);
+
+	    /* Set up object name or number to print */
+            if (starcat != NULL) {
+		if (starcat->stnum < 0 && gobj1 != NULL) {
+		    strncpy (numstr, gobj1[ig], 32);
+		    if (lofld > 0) {
+			for (j = 0; j < lofld; j++) {
+			    if (!numstr[j])
+				numstr[j] = ' ';
+			    }
+			}
+		    }
+		else
+		    CatNum (refcat,-nnfld,starcat->nndec,gnum[ig],numstr);
+		}
+	    else
+		CatNum (refcat, -nnfld, nndec, gnum[ig], numstr);
+
+	    if (nmag > 0)
+		fprintf (stderr,"%s %s %s %5.2f %6.1f %6.1f\r",
+		    numstr,rstr,dstr,gm[magsort][ig],gx[ig],gy[ig]);
+	    else
+		fprintf (stderr,"%s %s %s %6.1f %6.1f\r",
+		    numstr,rstr,dstr,gx[ig],gy[ig]);
+	    }
+	fprintf (stderr,"\n");
+	}
+
+    if (nrg < minstars) {
+	if (ng < 0)
+	    fprintf (stderr, "Error getting reference stars: %d\n", ng);
+	else if (ng == 0)
+	    fprintf (stderr,"No reference stars found in image area\n");
+	else if (fitwcs)
+	    fprintf (stderr, "Found only %d out of %d reference stars needed\n",
+			     nrg, minstars);
+	if (ng <= 0 || fitwcs) {
+	    ret = 0;
+	    goto out;
+	    }
+	}
+
+    /* Discover star-like things in the image, in pixels */
+    if (imsearch) {
+	ns = FindStars (header, image, &sx, &sy, &sm, &sp, verbose, 0);
+	if (ns < minstars) {
+	    if (ns < 0)
+		fprintf (stderr, "Error getting image stars: %d\n", ns);
+	    else if (ns == 0)
+		fprintf (stderr,"No stars found in image\n");
+	    else if (fitwcs)
+		fprintf (stderr, "Need at least %d image stars but only found %d\n",
+			 minstars, ns);
+	    if (ns <= 0 || fitwcs) {
+		ret = 0;
+		iterate = 0;
+		recenter = 0;
+		goto out;
+		}
+	    }
+	imsearch = 0;
+	}
+
+    /* Fit a world coordinate system if requested */
+    if (fitwcs) {
+	niter++;
+
+	/* Sort star-like objects in image by brightness (magnitude) */
+	MagSortStars (NULL,NULL,NULL,NULL,NULL,sx,sy,&sm,sp,NULL,ns,1,1);
+
+	/* If matching a catalog field the same size as the image field,
+	   use only as many star-like objects as reference stars.  If using
+	   a larger catalog field (imfrac > 0), increase the number of stars
+	   proportionately (imfrac^2 * the number of image stars).  To adjust
+	   for different bandpasses between the image and the reference catalog
+	   use frac * the number of reference stars if more than image stars
+	   or frac * the number of image stars if more than reference stars.
+	*/
+	if (imfrac > 0.0) {
+	    double imfrac2 = imfrac * imfrac;
+	    if (ns > (nrg / imfrac2)) {
+		nbs = nrg * frac / imfrac2;
+		if (nbs > ns)
+		    nbs = ns;
+		nbg = nrg;
+		}
+	    else {
+		nbs = ns;
+		nbg = (int) ((double) nbs * imfrac2);
+		if (nbg > nrg)
+		    nbg = nrg;
+		}
+	    }
+	else {
+	    if (ns > nrg) {
+		nbs = nrg * frac;
+		if (nbs > ns)
+		    nbs = ns;
+		nbg = nrg;
+		}
+	    else {
+		nbs = ns;
+		nbg = nbs * frac;
+		if (nbg > nrg)
+		    nbg = nrg;
+		}
+	    }
+	if (verbose) {
+	    if (nbg == ng)
+		fprintf (stderr,"Using all %d reference stars\n", ng);
+	    else
+		fprintf (stderr,"Using brightest %d / %d reference stars\n", nbg, ng);
+	    if (nbs == ns)
+		fprintf (stderr,"Using all %d image stars\n", ns);
+	    else
+		fprintf (stderr,"Using brightest %d / %d image stars\n", nbs,ns);
+	    }
+
+	/* Print nominal positions of image stars */
+	if (verbose) {
+	    char rastr[32], decstr[32];
+	    double xmag, mdiff, ra, dec;
+	    for (is = 0; is < nbs; is++) {
+		pix2wcs (wcs, sx[is], sy[is], &ra, &dec);
+		ra2str (rastr, 32, ra, 3);
+		dec2str (decstr, 32, dec, 2);
+		xmag = sm[is];
+		if (nmag > 0 && !is) {
+		    mdiff = gm[magsort][0] - xmag;
+		    }
+		else {
+		    mdiff = 0.0;
+		    }
+		xmag = xmag + mdiff;
+		fprintf (stderr,"%4d %s %s %6.2f %6.1f %6.1f %d\r",
+			is+1, rastr, decstr, xmag, sx[is], sy[is], sp[is]);
+		}
+	    fprintf (stderr,"\n");
+	    }
+
+	/* Match offsets between all pairs of image stars and reference stars
+	   and fit WCS to matches */
+	nbin = StarMatch (nbs,sx,sy,refcat,nbg,gnum,gra,gdec,goff,gx,gy,
+			  tolerance,wcs,verbose);
+
+	if (nbin < 0) {
+	    fprintf (stderr, "Star registration failed.\n");
+	    ret = 0;
+	    goto done;
+	    }
+	else if (nbin < minstars0) {
+	    fprintf (stderr, "Only %d matches, registration failed.\n", nbin);
+	    ret = 0;
+	    goto done;
+	    }
+	else if (verbose)
+	    fprintf (stderr,"%d / %d bin hits\n", nbin, nbg);
+
+	hputs (header, "WCSRFCAT", refcatname);
+	imcatname = getimcat ();
+	if (strlen (imcatname) == 0)
+	    hputs (header, "WCSIMCAT", filename);
+	else
+	    hputs (header, "WCSIMCAT", imcatname);
+	hputi4 (header, "WCSMATCH", nbin);
+	if (ns < nbg)
+	    hputi4 (header, "WCSNREF", ns);
+	else
+	    hputi4 (header, "WCSNREF", nbg);
+	hputnr8 (header, "WCSTOL", 4, tolerance);
+
+	SetFITSWCS (header, wcs);
+	}
+
+    /* Match reference and image stars */
+match:
+    nmatch = 0;
+
+    if (verbose || !fitwcs) {
+	imcatname = getimcat ();
+	if (wcs->ncoeff1 > 0)
+	    printf ("# %d-term x, %d-term y polynomial fit\n",
+		    wcs->ncoeff1, wcs->ncoeff2);
+	else
+	    printf ("# Arcsec/Pixel= %.6f %.6f  Rotation= %.6f degrees\n",
+		    3600.0*wcs->xinc, 3600.0*wcs->yinc, wcs->rot);
+	ra2str (rstr, 32, wcs->xref, 3);
+	dec2str (dstr, 32, wcs->yref, 2);
+	printf ("# Optical axis= %s  %s %s x= %.2f y= %.2f\n",
+		rstr,dstr, refcoor, wcs->xrefpix, wcs->yrefpix);
+	ra = wcs->xref;
+	dec = wcs->yref;
+	if (refsys == WCS_J2000) {
+	    fk524e (&ra, &dec, refep);
+	    ra2str (rstr, 32, ra, 3);
+	    dec2str (dstr, 32, dec, 2);
+	    printf ("# Optical axis= %s  %s B1950  x= %.2f y= %.2f\n",
+		    rstr,dstr, wcs->xrefpix, wcs->yrefpix);
+	    }
+	else {
+	    fk425e (&ra, &dec, refep);
+	    ra2str (rstr, 32, ra, 3);
+	    dec2str (dstr, 32, dec, 2);
+	    printf ("# Optical axis= %s  %s J2000  x= %.2f y= %.2f\n",
+		    rstr,dstr, wcs->xrefpix, wcs->yrefpix);
+	    }
+	}
+
+    /* Find star matches for this offset and print them */
+
+    /* Use the fit WCS info to find catalog star x/y on image */
+    for (ig = 0; ig < nrg; ig++) {
+	gx[ig] = 0.0;
+	gy[ig] = 0.0;
+	wcs2pix (wcs, gra[ig], gdec[ig], &gx[ig], &gy[ig], &goff[ig]);
+	}
+
+    /* Set maximum number of matches which are possible */
+    if (nrg < ns)
+	nmax = nrg;
+    else
+	nmax = ns;
+
+    /* Find best catalog matches to stars in image */
+    nmatch = 0;
+    nbytes = ns * sizeof (double);
+    if (!(gra1 = (double *) calloc (ns, sizeof(double))))
+            fprintf (stderr, "Could not calloc %d bytes for gra1\n",nbytes);
+    if (!(gdec1 = (double *) calloc (ns, sizeof(double))))
+            fprintf (stderr, "Could not calloc %d bytes for gdec1\n",nbytes);
+    if (!(gm1 = (double *) calloc (ns, sizeof(double))))
+            fprintf (stderr, "Could not calloc %d bytes for gm1\n",nbytes);
+    if (!(gnum1 = (double *) calloc (ns, sizeof(double))))
+            fprintf (stderr, "Could not calloc %d bytes for gnum1\n",nbytes);
+    if (!(sx1 = (double *) calloc (ns, sizeof(double))))
+            fprintf (stderr, "Could not calloc %d bytes for sx1\n",nbytes);
+    if (!(sy1 = (double *) calloc (ns, sizeof(double))))
+            fprintf (stderr, "Could not calloc %d bytes for sy1\n",nbytes);
+    if (!(sm1 = (double *) calloc (ns, sizeof(double))))
+            fprintf (stderr, "Could not calloc %d bytes for sm1\n",nbytes);
+    for (is = 0; is < ns; is++) {
+	dxys = tolerance * tolerance;
+	igs = -1;
+	for (ig = 0; ig < nrg; ig++) {
+	    if (!goff[ig]) {
+		dx = gx[ig] - sx[is];
+		dy = gy[ig] - sy[is];
+		dx2 = dx * dx;
+		dy2 = dy * dy;
+		dxy = dx2 + dy2;
+		if (dxy < dxys) {
+		    dxys = dxy;
+		    igs = ig;
+		    }
+		}
+	    }
+	if (igs > -1) {
+	    gnum1[nmatch] = gnum[igs];
+	    if (gm != NULL && nmag > 0)
+		gm1[nmatch] = gm[magsort][igs];
+	    else
+		gm1[nmatch] = 0.0;
+	    gra1[nmatch] = gra[igs];
+	    gdec1[nmatch] = gdec[igs];
+	    sx1[nmatch] = sx[is];
+	    sy1[nmatch] = sy[is];
+	    sm1[nmatch] = sm[is];
+	    nmatch++;
+	    }
+	}
+
+    /* If there were any matches found, print them */
+    if (nmatch > 0) {
+	int rprint = verbose || !fitwcs;
+	hputi4 (header, "WCSMATCH", nmatch);
+	hputi4 (header, "WCSNREF", nmax);
+	hputnr8 (header, "WCSTOL", 4, tolerance);
+	if (rprint) {
+
+	    PrintRes (header,wcs,nmatch,sx1,sy1,sm1,gra1,gdec1,gm1,gnum1,
+		      refcat,rprint);
+	    if (refcatname == NULL)
+		printf ("# nmatch= %d nstars= %d in and %s  niter= %d\n",
+			nmatch, nmax, matchcat, niter);
+	    else if (strlen (imcatname) == 0)
+		printf ("# nmatch= %d nstars= %d between %s and %s  niter= %d\n",
+			nmatch, nmax, refcatname, filename, niter);
+	    else
+		printf ("# nmatch= %d nstars= %d between %s and %s  niter= %d\n",
+			nmatch, nmax, refcatname, imcatname, niter);
+	    }
+	else
+	    CompRes (header,wcs,nmatch,sx1,sy1,sm1,gra1,gdec1,gm1,gnum1);
+
+	/* Fit the matched catalog and image stars with a polynomial */
+	if (!iterate && !recenter && fitplate && refcatname != NULL) {
+
+	    if (verbose)
+		fprintf (stderr,"Fitting matched stars with a polynomial\n");
+
+	    /* Fit residuals */
+	    if (FitPlate (wcs, sx1, sy1, gra1, gdec1, nmatch, fitplate,
+			  verbose))
+		fprintf (stderr,"FitPlate cannot fit matches\n");
+
+	    /* Print the new residuals */
+	    else if (rprint) {
+		PrintRes (header,wcs,nmatch,sx1,sy1,sm1,gra1,gdec1,gm1,gnum1,
+			  refcat,verbose);
+		if (refcatname == NULL)
+		    printf ("# nmatch= %d nstars= %d in %s niter= %d\n",
+			    nmatch, nmax, matchcat, niter);
+		else if (strlen (imcatname) == 0)
+		printf ("# nmatch= %d nstars= %d between %s and %s niter= %d\n",
+			nmatch, nmax, refcatname, filename, niter);
+		else
+		printf ("# nmatch= %d nstars= %d between %s and %s niter= %d\n",
+			nmatch, nmax, refcatname, imcatname, niter);
+		SetFITSPlate (header, wcs);
+		}
+	    else {
+		CompRes (header,wcs,nmatch,sx1,sy1,sm1,gra1,gdec1,gm1,gnum1);
+		SetFITSPlate (header, wcs);
+		}
+	    }
+	}
+
+    else {
+	if (refcatname == NULL)
+	    fprintf (stderr, "SetWCSFITS: No matches in %s:\n",
+		     matchcat);
+	else if (strlen (imcatname) == 0)
+	    fprintf (stderr, "SetWCSFITS: No matches between %s and %s:\n",
+		     refcatname, filename);
+	else
+	    fprintf (stderr, "SetWCSFITS: No matches between %s and %s:\n",
+		     refcatname, imcatname);
+	hputi4 (header, "WCSMATCH", 0);
+	}
+    if (gra1) free ((char *)gra1);
+    if (gdec1) free ((char *)gdec1);
+    if (gm1) free ((char *)gm1);
+    if (gnum1) free ((char *)gnum1);
+    if (sx1) free ((char *)sx1);
+    if (sy1) free ((char *)sy1);
+    if (sm1) free ((char *)sm1);
+
+    ret = 1;
+
+    out:
+
+    if (iterate) {
+/*	setdcenter (wcs->xref, wcs->yref);
+	setsys (wcs->syswcs);
+	setrefpix (wcs->xrefpix, wcs->yrefpix);
+	if (iscdfit())
+	    setcd (wcs->cd);
+	else {
+	    setsecpix (-3600.0 * wcs->xinc);
+	    setsecpix2 (3600.0 * wcs->yinc);
+	    setrot (wcs->rot);
+	    }
+	wcsfree (wcs); */
+
+	wcssize (wcs, &cra, &cdec, &dra, &ddec);
+	if (cra < 0.0) cra = cra + 360.0;
+	iterate--;
+	imfrac = 0.0;
+	goto getstars;
+	}
+    if (toliterate) {
+/*	setdcenter (wcs->xref, wcs->yref);
+	setsys (wcs->syswcs);
+	setrefpix (wcs->xrefpix, wcs->yrefpix);
+	if (iscdfit())
+	    setcd (wcs->cd);
+	else {
+	    setsecpix (-3600.0 * wcs->xinc);
+	    setsecpix2 (3600.0 * wcs->yinc);
+	    setrot (wcs->rot);
+	    }
+	wcsfree (wcs); */
+
+	wcssize (wcs, &cra, &cdec, &dra, &ddec);
+	tolerance = tolerance * 0.5;
+	toliterate--;
+	imfrac = 0.0;
+	goto getstars;
+	}
+    if (recenter) {
+	double ra, dec, x, y;
+	x = 0.5 * wcs->nxpix;
+	y = 0.5 * wcs->nypix;
+	pix2wcs (wcs, x, y, &ra, &dec);
+	setdcenter (ra, dec);
+	setsys (wcs->syswcs);
+	setrefpix (x, y);
+	setsecpix (-3600.0 * wcs->xinc);
+	setsecpix2 (3600.0 * wcs->yinc);
+	setrot (wcs->rot);
+	recenter = 0;
+	imfrac = 0.0;
+	if (wcs) {
+	    wcsfree (wcs);
+	    wcs = NULL;
+	    }
+	goto getfield;
+	}
+    if (nfiterate) {
+	int nfit = getnfit ();
+	wcssize (wcs, &cra, &cdec, &dra, &ddec);
+	if (verbose)
+	    printf ("\n fitting %d instead of %d parameters\n", nfit+2, nfit);
+	if (nfit < 7)
+	    nfit = nfit + 2;
+	setnfit (nfit);
+	nfiterate--;
+	goto getstars;
+	}
+
+done:
+    if (wcs) {
+	wcsfree (wcs);
+	wcs = NULL;
+	}
+
+    /* Free catalog source arrays */
+    if (gra) free ((char *)gra);
+    if (gdec) free ((char *)gdec);
+    if (gpra) free ((char *)gpra);
+    if (gpdec) free ((char *)gpdec);
+    if (gm) {
+	for (imag = 0; imag < nmag; imag++) {
+	    if (gm[imag])
+		free ((char *)gm[imag]);
+	    }
+	free ((char *)gm);
+	}
+    if (gnum) free ((char *)gnum);
+    if (gx) free ((char *)gx);
+    if (gy) free ((char *)gy);
+    if (goff) free ((char *)goff);
+    if (gc) free ((char *)gc);
+
+    /* Free memory used for object names in reference catalog */
+    if (gobj1 != NULL) {
+	for (i = 0; i < ngmax; i++) {
+	    if (gobj[i] != NULL) {
+		free (gobj[i]);
+		gobj[i] = NULL;
+		}
+	    }
+	}
+    if (gobj) {
+	free ((char *) gobj);
+	gobj = NULL;
+	}
+
+    /* Free image source arrays */
+    if (sx) free ((char *)sx);
+    if (sy) free ((char *)sy);
+    if (sm) free ((char *)sm);
+    if (sp) free ((char *)sp);
+
+    return (ret);
+}
+
+
+static void
+PrintRes (header,wcs,nmatch,sx1,sy1,sm1,gra1,gdec1,gm1,gnum1,refcat,verbose)
+
+char	*header;	/* Image FITS header */
+struct WorldCoor *wcs;	/* Image World Coordinate System */
+int	nmatch;		/* Number of image/catalog matches */
+double	*sx1, *sy1;	/* Image star pixel coordinates */
+double	*sm1;		/* Plate magnitudes */
+double	*gra1, *gdec1;	/* Reference catalog sky coordinates */
+double	*gm1;		/* Reference catalog magnitudes */
+double	*gnum1;		/* Reference catalog numbers */
+int	refcat;		/* Reference catalog code */
+int	verbose;	/* True for more information */
+
+{
+    int i, goff;
+    double gx, gy, dx, dy, dx2, dy2, dxy, mag0;
+    double sep, sep2, rsep, rsep2, dsep, dsep2;
+    double dmatch, dmatch1, sra, sdec;
+    double sepsum = 0.0;
+    double rsepsum = 0.0;
+    double rsep2sum = 0.0;
+    double dsepsum = 0.0;
+    double dsep2sum = 0.0;
+    double sep2sum = 0.0;
+    double dxsum = 0.0;
+    double dysum = 0.0;
+    double dx2sum = 0.0;
+    double dy2sum = 0.0;
+    double dxysum = 0.0;
+    double coeff[5];
+    double msig;
+    double maxnum;
+    double cmax;
+    int nnfld;
+    int nxyfld;
+    char rstr[32], dstr[32], numstr[32], xstr[32], ystr[32];
+
+    maxnum = 0.0;
+    for (i = 0; i < nmatch; i++) {
+	if (i == 0)
+	    maxnum = gnum1[i];
+	else if (gnum1[i] > maxnum)
+	    maxnum = gnum1[i];
+	}
+    nnfld = CatNumLen (refcat, maxnum, 0);
+
+    CatID (numstr, refcat);
+    if (irafout)
+	printf ("#   x      y        ra2000   dec2000  mag %s", numstr); 
+    else
+	printf ("# %s ra2000       dec2000    magc    X      Y     magi",
+		numstr);
+    printf ("    dra   ddec   sep\n");
+
+    /* Find maximum image coordinates and set field size accordingly */
+    cmax = 0.0;
+    for (i = 0; i < nmatch; i++) {
+	if (sx1[i] > cmax) cmax = sx1[i];
+	if (sy1[i] > cmax) cmax = sy1[i];
+	}
+    if (cmax > 9999.0)
+	nxyfld = 6 + nxydec;
+    else if (cmax > 999.0)
+	nxyfld = 5 + nxydec;
+    else
+	nxyfld = 4 + nxydec;
+
+    for (i = 0; i < nmatch; i++) {
+	wcs2pix (wcs, gra1[i], gdec1[i], &gx, &gy, &goff);
+	dx = gx - sx1[i];
+	dy = gy - sy1[i];
+	dx2 = dx * dx;
+	dy2 = dy * dy;
+	dxy = dx2 + dy2;
+	dxsum = dxsum + dx;
+	dysum = dysum + dy;
+	dx2sum = dx2sum + dx2;
+	dy2sum = dy2sum + dy2;
+	dxysum = dxysum + sqrt (dxy);
+	pix2wcs (wcs, sx1[i], sy1[i], &sra, &sdec);
+	sep = 3600.0 * wcsdist(gra1[i],gdec1[i],sra,sdec);
+	rsep = 3600.0 * ((gra1[i]-sra) * cos(degrad(sdec)));
+	if (rsep > sep)
+	    rsep = 3600.0 * ((gra1[i] - sra - 360.0) * cos(degrad(sdec)));
+	rsep2 = rsep * rsep;
+	dsep = 3600.0 * (gdec1[i] - sdec);
+	dsep2 = dsep * dsep;
+	sepsum = sepsum + sep;
+	rsepsum = rsepsum + rsep;
+	dsepsum = dsepsum + dsep;
+	rsep2sum = rsep2sum + rsep2;
+	dsep2sum = dsep2sum + dsep2;
+	sep2sum = sep2sum + (sep*sep);
+	ra2str (rstr, 32, gra1[i], 3);
+	dec2str (dstr, 32, gdec1[i], 2);
+	num2str (xstr, sx1[i], nxyfld, nxydec);
+	num2str (ystr, sy1[i], nxyfld, nxydec);
+	CatNum (refcat, -nnfld, 0, gnum1[i], numstr);
+	if (irafout)
+	    printf (" %s %s %s %s %5.2f %s",
+		    xstr, ystr, rstr, dstr, gm1[i], numstr);
+	else
+	    printf ("%s %s %s %5.2f %s %s %6.2f ",
+		    numstr, rstr, dstr, gm1[i], xstr, ystr, sm1[i]);
+	printf ("%6.2f %6.2f %6.2f\n", rsep, dsep, sep);
+	}
+    dmatch = (double) nmatch;
+    dmatch1 = (double) (nmatch - 1);
+    dx = dxsum / dmatch;
+    dy = dysum / dmatch;
+    dx2 = sqrt (dx2sum / dmatch1);
+    dy2 = sqrt (dy2sum / dmatch1);
+    dxy = dxysum / dmatch;
+    rsep = rsepsum / dmatch;
+    dsep = dsepsum / dmatch;
+    rsep2 = sqrt (rsep2sum / dmatch1);
+    dsep2 = sqrt (dsep2sum / dmatch1);
+    sep = sepsum / dmatch;
+    sep2 = sqrt (sep2sum / dmatch1);
+    printf ("# Mean  dx= %.4f/%.4f  dy= %.4f/%.4f  dxy= %.4f\n",
+	    dx, dx2, dy, dy2, dxy);
+    printf ("# Mean dra= %.4f/%.4f  ddec= %.4f/%.4f sep= %.4f/%.4f\n",
+	    rsep, rsep2, dsep, dsep2, sep, sep2);
+
+    /* Fit and save image to catalog magnitude calibration polynomial */
+    if (magfit) {
+	mag0 = sm1[0];
+	coeff[0] = 0.0;
+	coeff[1] = 0.0;
+	coeff[2] = 0.0;
+	coeff[3] = 0.0;
+	coeff[4] = 0.0;
+	polfit (sm1, gm1, mag0, nmatch, 4, coeff, &msig);
+	printf ("# Plate to catalog mag: mag0=%.6f mcoeff0=%.6f mcoeff1=%.6f\n",
+		mag0, coeff[0], coeff[1]);
+	printf ("# Plate to catalog mag: mcoeff2=%.6f mcoeff3=%.6f sigma=%.3f\n",
+		coeff[2], coeff[3], msig);
+	}
+    hputi4 (header, "WCSMATCH", nmatch);
+    hputnr8 (header, "WCSSEP", 3, sep);
+
+    return;
+}
+
+
+static void
+CompRes (header,wcs,nmatch,sx1,sy1,sm1,gra1,gdec1,gm1,gnum1)
+
+char	*header;	/* Image FITS header */
+struct WorldCoor *wcs;	/* Image World Coordinate System */
+int	nmatch;		/* Number of image/catalog matches */
+double	*sx1, *sy1;	/* Image star pixel coordinates */
+double	*sm1;		/* Plate magnitudes */
+double	*gra1, *gdec1;	/* Reference catalog sky coordinates */
+double	*gm1;		/* Reference catalog magnitudes */
+double	*gnum1;		/* Reference catalog numbers */
+
+{
+    int i, goff;
+    double gx, gy, dx, dy, dx2, dy2, dxy, mag0;
+    double sep, sep2, rsep, rsep2, dsep, dsep2;
+    double dmatch, dmatch1, sra, sdec;
+    double sepsum = 0.0;
+    double rsepsum = 0.0;
+    double rsep2sum = 0.0;
+    double dsepsum = 0.0;
+    double dsep2sum = 0.0;
+    double sep2sum = 0.0;
+    double dxsum = 0.0;
+    double dysum = 0.0;
+    double dx2sum = 0.0;
+    double dy2sum = 0.0;
+    double dxysum = 0.0;
+
+    for (i = 0; i < nmatch; i++) {
+	wcs2pix (wcs, gra1[i], gdec1[i], &gx, &gy, &goff);
+	dx = gx - sx1[i];
+	dy = gy - sy1[i];
+	dx2 = dx * dx;
+	dy2 = dy * dy;
+	dxy = dx2 + dy2;
+	dxsum = dxsum + dx;
+	dysum = dysum + dy;
+	dx2sum = dx2sum + dx2;
+	dy2sum = dy2sum + dy2;
+	dxysum = dxysum + sqrt (dxy);
+	pix2wcs (wcs, sx1[i], sy1[i], &sra, &sdec);
+	sep = 3600.0 * wcsdist(gra1[i],gdec1[i],sra,sdec);
+	rsep = 3600.0 * ((gra1[i]-sra) * cos(degrad(sdec)));
+	if (rsep > sep)
+	    rsep = 3600.0 * ((gra1[i] - sra - 360.0) * cos(degrad(sdec)));
+	rsep2 = rsep * rsep;
+	dsep = 3600.0 * (gdec1[i] - sdec);
+	dsep2 = dsep * dsep;
+	sepsum = sepsum + sep;
+	rsepsum = rsepsum + rsep;
+	dsepsum = dsepsum + dsep;
+	rsep2sum = rsep2sum + rsep2;
+	dsep2sum = dsep2sum + dsep2;
+	sep2sum = sep2sum + (sep*sep);
+	}
+    dmatch = (double) nmatch;
+    dmatch1 = (double) (nmatch - 1);
+    dx = dxsum / dmatch;
+    dy = dysum / dmatch;
+    dx2 = sqrt (dx2sum / dmatch1);
+    dy2 = sqrt (dy2sum / dmatch1);
+    dxy = dxysum / dmatch;
+    rsep = rsepsum / dmatch;
+    dsep = dsepsum / dmatch;
+    rsep2 = sqrt (rsep2sum / dmatch1);
+    dsep2 = sqrt (dsep2sum / dmatch1);
+    sep = sepsum / dmatch;
+    sep2 = sqrt (sep2sum / dmatch1);
+
+    hputi4 (header, "WCSMATCH", nmatch);
+    hputnr8 (header, "WCSSEP", 3, sep);
+
+    return;
+}
+
+/* Subroutines to initialize various parameters */
+
+void
+settolerance (tol)
+double tol;
+{ tolerance = tol; return; }
+
+
+/* Number of decimal places in X and Y image coordinates of sources */
+void
+setnxydec (ndec)
+int ndec;
+{ nxydec = ndec; return; }
+
+void
+setirafout ()
+{ irafout = 1; return; }
+
+void
+setmatch (cat)
+char *cat;
+{ strcpy (matchcat, cat); return; }
+
+void
+setreflim (lim1, lim2)
+double lim1, lim2;
+{ refmag2 = lim2;
+  if (lim1 > -2.0) refmag1 = lim1;
+  return; }
+
+void
+setfitwcs (wfit)
+int wfit;
+{ fitwcs = wfit; return; }
+
+void
+setfitplate (nc)
+int nc;
+{ fitplate = nc; return; }
+
+void
+setminstars (minstars)
+int minstars;
+{ minstars0 = minstars;
+  setminbin (minstars);
+  return; }
+
+void
+setnofit ()
+{ nofit = 1; return; }
+
+void
+setfrac (frac0)
+double frac0;
+{ if (frac0 < 1.0) frac = 1.0 + frac0;
+    else frac = frac0;
+  return; }
+
+/* Fraction by which to increase dimensions of area to be searched */
+void
+setimfrac (frac0)
+double frac0;
+{ if (frac0 > 0.0) imfrac0 = frac0;
+  else imfrac0 = 1.0;
+  return; }
+
+void
+setmaxcat (ncat)
+int ncat;
+{ if (ncat < 1) maxcat = 25;
+  else maxcat = ncat;
+  return; }
+
+void
+setiterate (iter)
+int iter;
+{ iterate0 = iterate0 + iter;
+  return; }
+
+void
+setnfiterate (iter)
+int iter;
+{ nfiterate0 = nfiterate0 + iter;
+  return; }
+
+void
+setiteratet (iter)
+int iter;
+{ toliterate0 = toliterate0 + iter;
+  return; }
+
+void
+setrecenter (recenter)
+int recenter;
+{ recenter0 = recenter;
+  return; }
+
+void
+setsortmag (imag)
+int imag;
+{ sortmag = imag;
+  return; }
+
+void
+setmagfit ()
+{magfit++; return;}
+
+/* Feb 29 1996	New program
+ * Apr 30 1996	Add FOCAS-style catalog matching
+ * May  1 1996	Add initial image center from command line
+ * May  2 1996	Set up four star matching modes
+ * May 15 1996	Pass verbose flag; allow other reference catalogs
+ * May 16 1996	Remove sorting to separate file sortstar.c
+ * May 17 1996	Add class and verbose arguments
+ * May 22 1996  Allow for named reference catalog
+ * May 23 1996	Use pre-existing WCS for center, if it is present
+ * May 29 1996	Simplify program by always using WCS structure
+ * May 30 1996	Make reference/image pair matching the default method
+ * Jun 11 1996  Number and zero positions of image stars
+ * Jun 12 1996	Be more careful with nominal WCS setting
+ * Jun 14 1996	Add residual table
+ * Jun 28 1996	Set FITS header from WCS
+ * Jul  3 1996	Set epoch from old equinox if not already set
+ * Jul 19 1996	Declare tabread
+ * Jul 19 1996	Set image center in WCS if DSS WCS
+ * Jul 22 1996	Debug tab table reading
+ * Aug  5 1996	Add option to change WCS projection
+ * Aug  5 1996	Check for SECPIX1 as well as SECPIX
+ * Aug  5 1996	Set number of parameters to fit here
+ * Aug  7 1996	Save specified number of decimal places in header parameters
+ * Aug  7 1996	Rename old center parameters
+ * Aug 26 1996	Decrease default pixel tolerance from 20 to 10
+ * Aug 26 1996	Drop unused variable EQ in setfitswcs
+ * Aug 28 1996	Improve output format for matched stars
+ * Sep  1 1996	Set some defaults in lwcs.h
+ * Sep  3 1996	Fix bug to set plate scale from command line
+ * Sep  4 1996	Print reference catalog name on separate line from entries
+ * Sep 17 1996	Clean up code
+ * Oct 15 1996	Break off getfitswcs into separate file
+ * Nov 18 1996	Add USNO A catalog searching
+ * Nov 18 1996	Write same number into CROAT2 as CROTA1
+ * Nov 19 1996	If EPOCH was equinox in original image or not set, set it
+ * Dec 10 1996	Make equinox double in getfitswcs call
+ *
+ * Mar 17 1997	Print found reference stars even when there are not enough
+ * Jul 14 1997	If nfit is negative return with header set for nominal WCS
+ * Aug  4 1997	Reset nfit limit to 7 for reference pixel fit and fix nfit0 bug
+ * Aug 20 1997	Make maximum number of reference stars settable on the command line
+ * Aug 28 1997	Print star ID numbers in appropriate format for each catalog
+ * Aug 28 1997	Add option to match image to reference stars without fitting WCS
+ * Sep  3 1997	Add option to change image dimensions by a fraction
+ * Sep  9 1997	Return with default WCS in header only if nfit < -7
+ * Sep  9 1997	Print separate right ascension and declination residuals
+ * Sep 11 1997	Print average magnitude as well as value of residuals
+ * Oct 16 1997	Print same information for image stars as for reference stars
+ * Oct 22 1997	Print result of chip rotation as well as optical axis rotation
+ * Nov  6 1997	Move nfit entirely to matchstar
+ * Nov  6 1997	Rearrange output for IMMATCH use, adding filename argument
+ * Nov 14 1997	Increase, not multiply, dimensions by IMFRAC
+ * Nov 17 1997	Initialize both magnitude limits
+ * Dec  1 1997	Fix bug computing RA separation
+ * Dec 16 1997	Fix bug printing no match error message
+ *
+ * Jan 26 1998	Remove chip rotation code
+ * Jan 27 1998	Add switch to use either Calabretta or classic AIPS WCS
+ * Jan 29 1998	Fix summary to keep only one reference star per image star
+ * Feb  3 1998	Add option to improve WCS by fitting residuals
+ * Feb 12 1998	Add USNO SA catalog to reference catalog options
+ * Feb 12 1998	Match stars even if less than the minimum if no WCS fit
+ * Feb 19 1998	Increase number of reference stars if IMFRAC > 1
+ * Feb 22 1998	Fix residual fitting
+ * Mar  3 1998	Repeat field search and match after first try
+ * Mar  4 1998	Use imfrac on first pass, but not second
+ * Mar  5 1998	Correct number of stars used if IMFRAC > 1
+ * Mar  6 1998	Add option to use image center for second pass
+ * Mar 25 1998	Change residual polynomial fit to full plate-style polynomial
+ * Mar 25 1998	Move residual printing to a subroutine
+ * Mar 26 1998	Do not fit polynomial until both recenter and iterate done
+ * Mar 27 1998	Save plate fit coefficients to FITS header
+ * Apr  8 1998	Reset equinox to that of reference catalog
+ * Apr 30 1998	Handle prematched star/pixel file
+ * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
+ * Sep 17 1998	Allow use of catalogs with other than J2000 coordinates
+ * Sep 17 1998	Add coordinate system argument to GetFITSWCS()
+ * Sep 28 1998	Add SAO binary format catalogs (SAO, PPM, IRAS, Tycho)
+ * Sep 28 1998	Pass system, equinox, and epoch to all catalog search programs
+ * Oct  2 1998	Fix arguments in call to GetFITSWCS
+ * Oct  7 1998	Set projection to TAN before fitting
+ * Oct 16 1998	Add option to read from TDC ASCII format catalog
+ * Oct 26 1998	Use passed refcatname and new RefCat subroutine and wcscat.h
+ * Oct 26 1998	Add TDC binary catalog option
+ * Oct 28 1998	Only search for sources in image once
+ * Nov 19 1998	Add catalog name to uacread() call
+ * Dec  1 1998	Add version 2.0 of USNO A and SA catalogs
+ * Dec  8 1998	Add support for ACT and Hipparcos catalogs
+
+ * Jan  9 1999	Fix bug so that no fit option works
+ * Jan 26 1999	Add option to output matched image/catalog stars
+ * Feb 10 1999	Finish support for ACT reference catalog
+ * Apr  7 1999	Add file name to GetFITSWCS call
+ * Apr 21 1999	Fix RA residual bug: *cos(dec), not /cos(dec)
+ * Jul  7 1999	Fix bug setting secpix when iterating
+ * Jul  7 1999	List catalog and image stars without linefeeds
+ * Jul  9 1999	Log tabread() every 100 if verbose
+ * Jul 23 1999	Add BSC for very wide fields
+ * Jul 26 1999	Add WCSIMCAT, WCSFRCAT, WCSMATCH, and WCSSEP to header
+ * Jul 26 1999	Always compute residuals so WCSMATCH and WCSSEP match WCS
+ * Jul 27 1999	Add WCSNREF, maximum possible matches
+ * Aug 26 1999	Handle true number return from search subroutines
+ * Aug 31 1999	Set image catalog name when only matching stars
+ * Sep 13 1999	Do all catalog searches through catread()
+ * Sep 15 1999	Fix improper uses of ng instead of nrg
+ * Sep 16 1999	Add zero distsort argument to catread() call
+ * Sep 29 1999	Add option to start with pre-matched stars
+ * Oct 22 1999	Change catread() to ctgread() to avoid system conflict
+ * Nov 23 1999	Free wcs only after it is used to set up iterate or recenter
+ *
+ * Feb  8 2000	If iterating, halve pixel tolerance on second pass
+ * Feb 11 2000	Print maximum number of matches with number matched
+ * Feb 15 2000	Drop maximum number of image stars
+ * Feb 15 2000	When iterating, reinitialize CD matrix if it's being fit
+ * Mar  1 2000	Modify residual output so = used instead of :
+ * Mar  1 2000	Add seperate option to tighten tolerances when iterating
+ * Mar 10 2000	Add proper motion arguments to ctgread()
+ * Mar 10 2000	Do not change WCS unless fitting
+ * Mar 13 2000	Use PropCat() to dind out whether catalog has proper motion
+ * Mar 15 2000	Add proper motion arguments to RASortStars() and ctgread()
+ * Mar 28 2000	Separate tolerance reducing iterations and other iterations
+ * May 26 2000	Set catalog number field size using CatNumLen()
+ * Jun 22 2000	Fix bug created in last update (found by J.-B. Marquette)
+ * Jul 12 2000	Add catalog data structre to ctgread() call
+ * Jul 25 2000	Pass address of star catalog data structure address
+ * Dec  6 2000	If no reference catalog is set, skip catalog fit
+ * Dec  6 2000	Drop static refcatname and setrefcat()
+ * Dec 18 2000	Always allocate proper motion arrays; clean up code after lint
+ * Dec 18 2000	Call ReadMatch() to read file of X/Y/RA/Dec matches
+ *
+ * Jan  8 2001	Add verbose flag to ReadMatch() call
+ * Jan  9 2001	Fix bug in FitMatch() call
+ * Jan 11 2001	All output except residuals to stderr
+ * Mar  1 2001	Fill in catalog name using CatName() if not set
+ * Jun  7 2001	Add proper motion flag and number of magnitudes to RefCat()
+ * Jun 11 2001	Set refep from wcs->epoch after WCS is set
+ * Jul 20 2001	FindStars() now returns magnitude instead of flux
+ * Jul 24 2001	Add code to fit plate to catalog magnitude polynomial
+ * Jul 25 2001	Add headings to residual table
+ * Aug  2 2001	If fitting fewer than MINSTARS parameters, allow 1 match
+ * Sep 11 2001	Add magnitude selection
+ * Sep 11 2001	Use single, 2-D magnitude argument to ctgread()
+ * Sep 13 2001	Add reference catalog magnitude selection
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ * Oct 16 2001	Add command line parameter setting using keyword=value
+ * Oct 29 2001	Print number of matches after residuals to avoid scroll-off
+ * Nov  1 2001	Add goff to StarMatch() arguments
+ * Nov  5 2001	Add refcat and gnum to StarMatch() arguments
+ * Nov  6 2001	If iterating keep exact fit WCS after first pass
+ * Nov  6 2001	Change sb to sm because it is now always a magnitude
+ * Nov  6 2001	Fix image star magnitude sorting error
+ * Nov  7 2001	Allow minimum number of stars for match to be set externally
+ * Nov  7 2001	Drop out if match is unsuccessful
+ *
+ * Jan 18 2002	Allocate sm when using prematched stars
+ * Jan 23 2002	Add zap=0 to FindStar() argument list
+ * Jan 24 2002	Handle catalog nmag = 0 safely
+ * Apr 10 2002	Allow sort/limit magnitude to be specified by letter as well as number
+ * Jul 31 2002	Add iteration with increasing number of parameters to be fit
+ * Aug  2 2002	Use WCSMatch() to set initial values for pre-matched stars
+ * Sep  4 2002	Don't iterate if there is no catalog
+ *
+ * Aug 22 2003	Add inner radius =0.0 argument to ctgread call
+ * Dec 12 2003	Add second argument to CatName()
+ *
+ * Aug 30 2004	Declare void undeclared set*() subroutines
+ *
+ * Mar 30 2006	Allow number of decimal places in image coordinates to be set
+ * Jun  8 2006	Print object name instead of number if necessary
+ * Jun 19 2006	Initialize uninitialized variables
+ *
+ * Jan  9 2006	Drop declarations of fk425e() and fk524e(); moved to wcs.h
+ * Jan 10 2007	Drop setclass() and setplate(); it did not do anything
+ * Jan 11 2007	Include fitsfile.h
+ * Mar 14 2007	Return if -n 0 after computing WCS from match
+ *
+ * Aug  3 2009	If not printing residuals, still compute WCSSEP using CompRes()
+ * Sep 24 2009	Free pointers more carefully
+ */
diff --git a/Code/src/libwcs/imutil.c b/Code/src/libwcs/imutil.c
new file mode 100644
index 0000000000000000000000000000000000000000..4e1291dcedb8d839e61f175d0210abc396c59383
--- /dev/null
+++ b/Code/src/libwcs/imutil.c
@@ -0,0 +1,2791 @@
+/*** File libwcs/imutil.c
+ *** January 11, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2006-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+ */
+
+/* Smooth, fill, or shrink an image */
+
+/* char *FiltFITS(header, image, filter, xsize, ysize, nlog)
+ *	Return filtered image buffer
+ * char *FillFITS(header, image, filter, xsize, ysize, nlog)
+ *	Return image bufer with bad pixels replaced by filter value
+ * SetBadFITS (header, image, badheader, badimage, nlog)
+ *	Set bad pixels in image to BLANK using bad pixel file
+ * SetBadVal (header, image, minpixval, maxpixval, nlog)
+ *	Set bad pixels in image to BLANK if value less than minpixval
+ *
+ * char *medfilt (buff, header, ndx, ndy, nlog)
+ *	Median filter an image
+ * char *medfill (buff, header, ndx, ndy, nlog)
+ *	Set blank pixels to the median of a box around each one
+ * medpixi2 (x, ival, ix, iy, nx, ny, ndx, ndy)
+ * medpixi4 (x, ival, ix, iy, nx, ny, ndx, ndy)
+ * medpixr4 (x, ival, ix, iy, nx, ny, ndx, ndy)
+ * medpixr8 (x, ival, ix, iy, nx, ny, ndx, ndy)
+ *	Compute median of rectangular group of pixels
+ *
+ * char *meanfilt (buff, header, ndx, ndy, nlog)
+ *	Mean filter an image
+ * char *meanfill (buff, header, ndx, ndy, nlog)
+ *	Set blank pixels to the mean of a box around each one
+ * meanpixi2 (x, ival, ix, iy, nx, ny, ndx, ndy)
+ * meanpixi4 (x, ival, ix, iy, nx, ny, ndx, ndy)
+ * meanpixr4 (x, ival, ix, iy, nx, ny, ndx, ndy)
+ * meanpixr8 (x, ival, ix, iy, nx, ny, ndx, ndy)
+ *	Compute mean of rectangular group of pixels
+ *
+ * char *gaussfilt (buff, header, ndx, ndy, nlog)
+ *	Gaussian filter an image
+ * char *gaussfill (buff, header, ndx, ndy, nlog)
+ *	Set blank pixels to the Gaussian weighted sum of a box around each one
+ * gausswt (mx, my, nx)
+ *	Compute Gaussian weights for a square region
+ * gausspixi2 (image, ival, ix, iy, nx, ny)
+ * gausspixi4 (image, ival, ix, iy, nx, ny)
+ * gausspixr4 (image, ival, ix, iy, nx, ny)
+ * gausspixr8 (image, ival, ix, iy, nx, ny)
+ *	Compute Gaussian-weighted mean of a square group of pixels
+ *
+ * char *ShrinkFITSImage (header, image, xfactor, yfactor, mean, bitpix, nlog)
+ *	Return image buffer reduced by a given factor
+ * char *ShrinkFITSHeader (filename, header, xfactor, yfactor, mean, bitpix)
+ 		Return image header with dimensions reduced by a given factor
+ * double PhotPix (imbuff,header,cx,cy,rad,sumw)
+ *	Compute counts within a strictly defined circular aperture
+ */
+
+#include <string.h>             /* NULL, strlen, strstr, strcpy */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "fitsfile.h"
+
+#define MEDIAN 1
+#define MEAN 2
+#define GAUSSIAN 3
+
+char *medfilt();
+char *meanfilt();
+char *gaussfilt();
+char *badfill();
+char *medfill();
+char *meanfill();
+char *gaussfill();
+void gausswt();
+
+short medpixi2();
+short meanpixi2();
+short gausspixi2();
+int medpixi4();
+int meanpixi4();
+int gausspixi4();
+float medpixr4();
+float meanpixr4();
+float gausspixr4();
+double medpixr8();
+double meanpixr8();
+double gausspixr8();
+
+static int bpvalset = 0;
+static double bpval = -9999.0;
+static short bpvali2;
+static int bpvali4;
+static float bpvalr4;
+static int nfilled;
+
+int
+getnfilled ()
+{ return (nfilled); }
+
+void
+setbadpix (bpval0)
+double bpval0;
+{ bpvalset = 1; bpval = bpval0; return; }
+
+
+/* Set all pixels to a value computed from a box around each one */
+
+char *
+FiltFITS (header, image, filter, xsize, ysize, nlog)
+
+char	*header;	/* Image header */
+char	*image;		/* Image bytes to be filtered */
+int	filter;		/* Smoothing filter (median,mean,gaussian) */
+int	xsize;		/* Number of pixels in x (odd, horizontal) */
+int	ysize;		/* Number of pixels in y (odd, vertical) */
+int	nlog;		/* Logging interval in lines */
+
+{
+    if (filter == MEDIAN)
+	return (medfilt (image, header, xsize, ysize, nlog));
+    else if (filter == GAUSSIAN)
+	return (gaussfilt (image, header, xsize, ysize, nlog));
+    else
+	return (meanfilt (image, header, xsize, ysize, nlog));
+}
+
+/* Set BLANK pixels to a value computed from a box around each one */
+
+char *
+FillFITS (header, image, filter, xsize, ysize, nlog)
+
+char	*header;	/* Image header */
+char	*image;		/* Image bytes to be filtered */
+int	filter;		/* Smoothing filter (median,mean,gaussian) */
+int	xsize;		/* Number of pixels in x (odd, horizontal) */
+int	ysize;		/* Number of pixels in y (odd, vertical) */
+int	nlog;		/* Logging interval in lines */
+
+{
+    if (filter == MEDIAN)
+	return (medfill (image, header, xsize, ysize, nlog));
+    else if (filter == GAUSSIAN)
+	return (gaussfill (image, header, xsize, ysize, nlog));
+    else
+	return (meanfill (image, header, xsize, ysize, nlog));
+}
+
+
+/*	Set bad pixels in image to BLANK using bad pixel file */
+
+char *
+SetBadFITS (header, image, badheader, badimage, nlog)
+
+char	*header;	/* FITS image header */
+char	*image;		/* Image buffer */
+char	*badheader;	/* FITS bad pixel file image header */
+			/* Bad pixels have non-zero values */
+char	*badimage;	/* Bad pixel file image buffer */
+int	nlog;		/* Logging interval in pixels */
+
+{
+char	*buffret;	/* Modified image buffer (returned) */
+int	nx,ny;		/* Number of columns and rows in image */
+int	ix,iy;		/* Pixel around which to compute mean */
+int	npix;		/* Number of pixels in image */
+int	bitpix;		/* Number of bits per pixel (<0=floating point) */
+int	bitpixb;	/* Number of bits per pixel in bad pixel file */
+int	nxb,nyb;	/* Number of columns and rows in bad pixel image */
+int	naxes;
+char	*buff;
+char	*buffbad;
+
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS", &naxes);
+    hgeti4 (header, "NAXIS1", &nx);
+    if (naxes > 1)
+	hgeti4 (header, "NAXIS2", &ny);
+    else
+	ny = 1;
+    npix = nx * ny;
+    if (!bpvalset)
+	hgetr8 (header, "BLANK", &bpval);
+
+    hgeti4 (badheader, "BITPIX", &bitpixb);
+    hgeti4 (badheader, "NAXIS", &naxes);
+    hgeti4 (badheader, "NAXIS1", &nxb);
+    if (naxes > 1)
+	hgeti4 (badheader, "NAXIS2", &nyb);
+    else
+	nyb = 1;
+    if (nx != nxb || ny != nyb) {
+	fprintf (stderr, "SetBadFITS: Data file and bad pixel file mismatch %dx%d not %dx%d\n",
+		 nx,ny,nxb,nyb);
+	return (NULL);
+	}
+
+    nfilled = 0;
+
+    buff = image;
+    buffbad = badimage;
+    buffret = NULL;
+    if (bitpix == 16) {
+	short *b1, *b2, *buffout;
+	bpvali2 = (short) bpval;
+	buffret = (char *) calloc (npix, sizeof (short));
+	buffout = (short *) buffret;
+	b1 = (short *) buff;
+	b2 = buffout;
+	if (bitpixb == 16) {
+	    short *bb;
+	    bb = (short *) buffbad;
+	    for (iy = 0; iy < ny; iy++) {
+		for (ix = 0; ix < nx; ix++) {
+		    if (*bb++) {
+			*b2++ = bpvali2;
+			nfilled++;
+			*b1++;
+			}
+		    else {
+			*b2++ = *b1++;
+			}
+		    }
+		if (nlog > 0 && (iy+1)%nlog == 0)
+		    fprintf (stderr,"SetBadFITS: %d lines, %d bad pixels set\r",
+			     iy+1, nfilled);
+		}
+	    }
+	else if (bitpixb == 32) {
+	    int *bb;
+	    bb = (int *) buffbad;
+	    for (iy = 0; iy < ny; iy++) {
+		for (ix = 0; ix < nx; ix++) {
+		    if (*bb++) {
+			*b2++ = bpvali2;
+			nfilled++;
+			*b1++;
+			}
+		    else {
+			*b2++ = *b1++;
+			}
+		    }
+		if (nlog > 0 && (iy+1)%nlog == 0)
+		    fprintf (stderr,"SetBadFITS: %d lines, %d bad pixels set\r",
+			     iy+1, nfilled);
+		}
+	    }
+	}
+    else if (bitpix == 32) {
+	int *b1, *b2, *buffout;
+	bpvali4 = (int) bpval;
+	buffret = (char *) calloc (npix, sizeof (int));
+	buffout = (int *) buffret;
+	b1 = (int *) buff;
+	b2 = buffout;
+	if (bitpixb == 16) {
+	    short *bb;
+	    bb = (short *) buffbad;
+	    for (iy = 0; iy < ny; iy++) {
+		for (ix = 0; ix < nx; ix++) {
+		    if (*bb++) {
+			*b2++ = bpvali4;
+			nfilled++;
+			*b1++;
+			}
+		    else {
+			*b2++ = *b1++;
+			}
+		    }
+		if (nlog > 0 && (iy+1)%nlog == 0)
+		    fprintf (stderr,"SetBadFITS: %d lines, %d bad pixels set\r",
+			     iy+1, nfilled);
+		}
+	    }
+	else if (bitpixb == 32) {
+	    int *bb;
+	    bb = (int *) buffbad;
+	    for (iy = 0; iy < ny; iy++) {
+		for (ix = 0; ix < nx; ix++) {
+		    if (*bb++) {
+			*b2++ = bpvali4;
+			nfilled++;
+			*b1++;
+			}
+		    else {
+			*b2++ = *b1++;
+			}
+		    }
+		if (nlog > 0 && (iy+1)%nlog == 0)
+		    fprintf (stderr,"SetBadFITS: %d lines, %d bad pixels set\r",
+			     iy+1, nfilled);
+		}
+	    }
+	fprintf (stderr,"\n");
+	}
+    else if (bitpix == -32) {
+	float *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (float));
+	buffout = (float *) buffret;
+	bpvalr4 = (float) bpval;
+	b1 = (float *) buff;
+	b2 = buffout;
+	if (bitpixb == 16) {
+	    short *bb;
+	    bb = (short *) buffbad;
+	    for (iy = 0; iy < ny; iy++) {
+		for (ix = 0; ix < nx; ix++) {
+		    if (*bb++) {
+			*b2++ = bpvalr4;
+			nfilled++;
+			*b1++;
+			}
+		    else {
+			*b2++ = *b1++;
+			}
+		    }
+		if (nlog > 0 && (iy+1)%nlog == 0)
+		    fprintf (stderr,"SetBadFITS: %d lines, %d bad pixels set\r",
+			     iy+1, nfilled);
+		}
+	    }
+	else if (bitpixb == 32) {
+	    int *bb;
+	    bb = (int *) buffbad;
+	    for (iy = 0; iy < ny; iy++) {
+		for (ix = 0; ix < nx; ix++) {
+		    if (*bb++) {
+			*b2++ = bpvalr4;
+			nfilled++;
+			*b1++;
+			}
+		    else {
+			*b2++ = *b1++;
+			}
+		    }
+		if (nlog > 0 && (iy+1)%nlog == 0)
+		    fprintf (stderr,"SetBadFITS: %d lines, %d bad pixels set\r",
+			     iy+1, nfilled);
+		}
+	    }
+	}
+    else if (bitpix == -64) {
+	double *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (double));
+	buffout = (double *) buffret;
+	b1 = (double *) buff;
+	b2 = buffout;
+	if (bitpixb == 16) {
+	    short *bb;
+	    bb = (short *) buffbad;
+	    for (iy = 0; iy < ny; iy++) {
+		for (ix = 0; ix < nx; ix++) {
+		    if (*bb++) {
+			*b2++ = bpval;
+			nfilled++;
+			*b1++;
+			}
+		    else {
+			*b2++ = *b1++;
+			}
+		    }
+		if (nlog > 0 && (iy+1)%nlog == 0)
+		    fprintf (stderr,"SetBadFITS: %d lines, %d bad pixels set\r",
+			     iy+1, nfilled);
+		}
+	    }
+	else if (bitpixb == 32) {
+	    int *bb;
+	    bb = (int *) buffbad;
+	    for (iy = 0; iy < ny; iy++) {
+		for (ix = 0; ix < nx; ix++) {
+		    if (*bb++) {
+			*b2++ = bpval;
+			nfilled++;
+			*b1++;
+			}
+		    else {
+			*b2++ = *b1++;
+			}
+		    }
+		if (nlog > 0 && (iy+1)%nlog == 0)
+		    fprintf (stderr,"SetBadFITS: %d lines, %d bad pixels set\r",
+			     iy+1, nfilled);
+		}
+	    }
+	}
+    if (nlog > 0)
+	fprintf (stderr,"SetBadFITS: %d lines, %d bad pixels set\n",
+		 iy, nfilled);
+    return (buffret);
+}
+
+
+/* Set bad pixels in image to BLANK if less than minpixval */
+
+char *
+SetBadVal (header, image, minval, maxval, nlog)
+
+char	*header;	/* FITS image header */
+char	*image;		/* Image buffer */
+double	minval;		/* Minimum good pixel value */
+double	maxval;		/* Maximum good pixel value */
+int	nlog;		/* Logging interval in pixels */
+
+{
+char	*buffret;	/* Modified image buffer (returned) */
+int	nx,ny;		/* Number of columns and rows in image */
+int	ix,iy;		/* Pixel around which to compute mean */
+int	npix;		/* Number of pixels in image */
+int	bitpix;		/* Number of bits per pixel (<0=floating point) */
+int	checkmin = 0;
+int	checkmax = 0;
+int	naxes;
+char	*buff;
+
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS", &naxes);
+    hgeti4 (header, "NAXIS1", &nx);
+    if (naxes > 1)
+	hgeti4 (header, "NAXIS2", &ny);
+    else
+	ny = 1;
+    npix = nx * ny;
+    if (!bpvalset)
+	hgetr8 (header, "BLANK", &bpval);
+
+    if (minval != -9999.0)
+	checkmin++;
+    if (maxval != -9999.0)
+	checkmax++;
+
+    nfilled = 0;
+
+    buff = image;
+    buffret = NULL;
+    if (bitpix == 16) {
+	short *bb, *b1, *b2, *buffout, bpvali2, mnvali2,mxvali2;
+	bpvali2 = (short) bpval;
+	mnvali2 = (short) minval;
+	mxvali2 = (short) maxval;
+	buffret = (char *) calloc (npix, sizeof (short));
+	buffout = (short *) buffret;
+	bb = (short *) buff;
+	b1 = (short *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (checkmin && *bb < mnvali2) {
+		    *b2++ = bpvali2;
+		    nfilled++;
+		    *b1++;
+		    }
+		else if (checkmax && *bb > mxvali2) {
+		    *b2++ = bpvali2;
+		    nfilled++;
+		    *b1++;
+		    }
+		else {
+		    *b2++ = *b1++;
+		    }
+		bb++;
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"SetBadVal: %d lines, %d bad pixels set\r",
+			 iy+1, nfilled);
+	    }
+	}
+    else if (bitpix == 32) {
+	int *bb, *b1, *b2, *buffout, mnvali4, mxvali4, bpvali4;
+	bpvali4 = (int) bpval;
+	mnvali4 = (int) minval;
+	mxvali4 = (int) maxval;
+	buffret = (char *) calloc (npix, sizeof (int));
+	buffout = (int *) buffret;
+	bb = (int *) buff;
+	b1 = (int *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (checkmin && *bb < mnvali4) {
+		    *b2++ = bpvali4;
+		    nfilled++;
+		    *b1++;
+		    }
+		else if (checkmax && *bb > mxvali4) {
+		    *b2++ = bpvali4;
+		    nfilled++;
+		    *b1++;
+		    }
+		else {
+		    *b2++ = *b1++;
+		    }
+		bb++;
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"SetBadVal: %d lines, %d bad pixels set\r",
+			 iy+1, nfilled);
+	    }
+	}
+    else if (bitpix == -32) {
+	float *bb, *b1, *b2, *buffout, bpvalr4, mnvalr4, mxvalr4;
+	buffret = (char *) calloc (npix, sizeof (float));
+	buffout = (float *) buffret;
+	bpvalr4 = (float) bpval;
+	mnvalr4 = (float) minval;
+	mxvalr4 = (float) maxval;
+	bb = (float *) buff;
+	b1 = (float *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (checkmin && *bb < mnvalr4) {
+		    *b2++ = bpvalr4;
+		    nfilled++;
+		    *b1++;
+		    }
+		else if (checkmax && *bb > mxvalr4) {
+		    *b2++ = bpvalr4;
+		    nfilled++;
+		    *b1++;
+		    }
+		else {
+		    *b2++ = *b1++;
+		    }
+		bb++;
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"SetBadVal: %d lines, %d bad pixels set\r",
+			 iy+1, nfilled);
+	    }
+	}
+    else if (bitpix == -64) {
+	double *bb, *b1, *b2, *buffout, bpvalr8, mnvalr8, mxvalr8;
+	buffret = (char *) calloc (npix, sizeof (double));
+	buffout = (double *) buffret;
+	bpvalr8 = (double) bpval;
+	mnvalr8 = (double) minval;
+	mxvalr8 = (double) maxval;
+	bb = (double *) buff;
+	b1 = (double *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (checkmin && *bb < mnvalr8) {
+		    *b2++ = bpvalr8;
+		    nfilled++;
+		    *b1++;
+		    }
+		else if (checkmax && *bb > mxvalr8) {
+		    *b2++ = bpvalr8;
+		    nfilled++;
+		    *b1++;
+		    }
+		else {
+		    *b2++ = *b1++;
+		    }
+		bb++;
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"SetBadVal: %d lines, %d bad pixels set\r",
+			 iy+1, nfilled);
+	    }
+	}
+    if (nlog > 0)
+	fprintf (stderr,"SetBadVal: %d lines, %d bad pixels set\n",
+		 iy, nfilled);
+    return (buffret);
+}
+
+
+/* Median filter an image */
+
+static short *vi2;	/* Working vector to sort for median */
+static int *vi4;	/* Working vector to sort for median */
+static float *vr4;	/* Working vector to sort for median */
+static double *vr8;	/* Working vector to sort for median */
+
+char *
+medfilt (buff, header, ndx, ndy, nlog)
+
+char	*buff;	/* Image buffer */
+char	*header; /* FITS image header */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+int	nlog;	/* Logging interval in pixels */
+
+{
+char	*buffret;	/* Modified image buffer (returned) */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ix,iy;	/* Pixel around which to compute median */
+int	npix;	/* Number of pixels in image */
+int	bitpix;	/* Number of bits per pixel (<0=floating point) */
+int	naxes;
+
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS", &naxes);
+    hgeti4 (header, "NAXIS1", &nx);
+    if (naxes > 1)
+	hgeti4 (header, "NAXIS2", &ny);
+    else
+	ny = 1;
+    npix = nx * ny;
+    hgetr8 (header, "BLANK", &bpval);
+
+    buffret = NULL;
+    if (bitpix == 16) {
+	short *b1,*b2, *buffout;
+	bpvali2 = (short) bpval;
+	vi2 = NULL;
+	buffret = (char *) calloc (npix, sizeof (short));
+	buffout = (short *) buffret;
+	b1 = (short *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = medpixi2 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEDFILT: %d lines filtered\r", iy+1);
+	    }
+	fprintf (stderr,"\n");
+	free (vi2);
+	vi2 = NULL;
+	}
+    else if (bitpix == 32) {
+	int *b1, *b2, *buffout;
+	bpvali4 = (int) bpval;
+	vi4 = NULL;
+	buffret = (char *) calloc (npix, sizeof (int));
+	buffout = (int *) buffret;
+	b1 = (int *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = medpixi4 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEDFILT: %d lines filtered\r", iy+1);
+	    }
+	fprintf (stderr,"\n");
+	free (vi4);
+	vi4 = NULL;
+	}
+    else if (bitpix == -32) {
+	float *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (float));
+	buffout = (float *) buffret;
+	b1 = (float *) buff;
+	b2 = buffout;
+	bpvalr4 = (float) bpval;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = medpixr4 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEDFILT: %d lines filtered\r", iy+1);
+	    }
+	fprintf (stderr,"\n");
+	free (vr4);
+	vr4 = NULL;
+	}
+    else if (bitpix == -64) {
+	double *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (double));
+	buffout = (double *) buffret;
+	b1 = (double *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = medpixr8 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEDFILT: %d lines filtered\r", iy+1);
+	    }
+	fprintf (stderr,"\n");
+	free (vr8);
+	vr8 = NULL;
+	}
+    if (nlog > 0)
+	fprintf (stderr,"MEDFILT: %d lines filtered\n", iy);
+    return (buffret);
+}
+
+
+/* Set blank pixels to the median of a box around each one */
+
+char *
+medfill (buff, header, ndx, ndy, nlog)
+
+char	*buff;	/* Image buffer */
+char	*header; /* FITS image header */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+int	nlog;	/* Logging interval in pixels */
+
+{
+char	*buffret;	/* Modified image buffer (returned) */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ix,iy;	/* Pixel around which to compute median */
+int	npix;	/* Number of pixels in image */
+int	bitpix;	/* Number of bits per pixel (<0=floating point) */
+int	naxes;
+
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS", &naxes);
+    hgeti4 (header, "NAXIS1", &nx);
+    if (naxes > 1)
+	hgeti4 (header, "NAXIS2", &ny);
+    else
+	ny = 1;
+    npix = nx * ny;
+    hgetr8 (header, "BLANK", &bpval);
+
+    nfilled = 0;
+
+    buffret = NULL;
+    if (bitpix == 16) {
+	short *b1,*b2, *buffout;
+	bpvali2 = (short) bpval;
+	vi2 = NULL;
+	buffret = (char *) calloc (npix, sizeof (short));
+	buffout = (short *) buffret;
+	b1 = (short *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpvali2)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = medpixi2 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEDFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	free (vi2);
+	vi2 = NULL;
+	}
+    else if (bitpix == 32) {
+	int *b1, *b2, *buffout;
+	bpvali4 = (int) bpval;
+	vi4 = NULL;
+	buffret = (char *) calloc (npix, sizeof (int));
+	buffout = (int *) buffret;
+	b1 = (int *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpvali4)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = medpixi4 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEDFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	free (vi4);
+	vi4 = NULL;
+	}
+    else if (bitpix == -32) {
+	float *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (float));
+	buffout = (float *) buffret;
+	bpvalr4 = (float) bpval;
+	b1 = (float *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpvalr4)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = medpixr4 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEDFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	free (vr4);
+	vr4 = NULL;
+	}
+    else if (bitpix == -64) {
+	double *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (double));
+	buffout = (double *) buffret;
+	b1 = (double *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpval)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = medpixr8 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEDFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	free (vr8);
+	vr8 = NULL;
+	}
+    if (nlog > 0)
+	fprintf (stderr,"MEDFILL: %d lines, %d pixels filled\n", iy, nfilled);
+    return (buffret);
+}
+
+
+/* Compute median of rectangular group of pixels */
+
+short
+medpixi2 (x, ival, ix, iy, nx, ny, ndx, ndy)
+
+short	*x;	/* Image buffer */
+short	ival;	/* Value at this pixel */
+int	ix,iy;	/* Pixel around which to compute median */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+
+{
+    short xx, *vecj, *img;
+    int n, i, j;
+    int  nx2, ny2, npix;
+    int jx, jx1, jx2, jy, jy1, jy2;
+
+    /* Allocate working buffer if it hasn't already been allocated */
+    npix = ndx * ndy;
+    if (vi2 == NULL) {
+	vi2 = (short *) calloc (npix, sizeof (short));
+	if (vi2 == NULL) {
+	    fprintf (stderr, "MEDPIXI2: Could not allocate %d-pixel buffer\n",npix);
+	    return (0);
+	    }
+	}
+
+    n = ndx * ndy;
+    if (n <= 0)
+	return (0.0);
+    else if (n == 1)
+	return (*(x + (iy * ny) + ix));
+
+    /* Compute limits for this pixel */
+    nx2 = ndx / 2;
+    jx1 = ix - nx2;
+    if (jx1 < 0)
+	jx1 = 0;
+    jx2 = ix + nx2 + 1;
+    if (jx2 > nx)
+	jx2 = nx;
+    ny2 = ndy / 2;
+    jy1 = iy - ny2;
+    if (jy1 < 0)
+	jy1 = 0;
+    jy2 = iy + ny2 + 1;
+    if (jy2 > ny)
+	jy2 = ny;
+
+    /* Initialize actual number of pixels used for this pixel */
+    n = 0;
+
+    /* Set up working vector for this pixel */
+    vecj = vi2;
+    for (jy = jy1; jy < jy2; jy++) {
+	img = x + (jy * nx) + jx1;
+	for (jx = jx1; jx < jx2; jx++) {
+	    if (*img != bpvali2) {
+		*vecj++ = *img;
+		n++;
+		}
+	    img++;
+	    }
+	}
+
+    /* If no good pixels, return old value */
+    if (n < 1)
+	return (ival);
+
+    /* Sort numbers in working vector */
+    else {
+	for (j = 2; j <= n; j++) {
+	    xx = vi2[j];
+	    i = j - 1;
+	    while (i > 0 && vi2[i] > xx) {
+		vi2[i+1] = vi2[i];
+		i--;
+		}
+	    vi2[i+1] = xx;
+	    }
+
+	/* Middle number is the median */
+	return (vi2[n/2]);
+	}
+}
+
+
+/* Compute median of rectangular group of pixels */
+
+int
+medpixi4 (x, ival, ix, iy, nx, ny, ndx, ndy)
+
+int	*x;	/* Image buffer */
+int	ival;	/* Current pixel */
+int	ix,iy;	/* Pixel around which to compute median */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+
+{
+    int xx, *vecj, *img;
+    int n, i, j;
+    int  nx2, ny2, npix;
+    int jx, jx1, jx2, jy, jy1, jy2;
+
+    /* Allocate working buffer if it hasn't already been allocated */
+    npix = ndx * ndy;
+    if (vi4 == NULL) {
+	vi4 = (int *) calloc (npix, sizeof (int));
+	if (vi4 == NULL) {
+	    fprintf (stderr, "MEDIANI4: Could not allocate %d-pixel buffer\n",npix);
+	    return (0);
+	    }
+	}
+
+    n = ndx * ndy;
+    if (n <= 0)
+	return (0.0);
+    else if (n == 1)
+	return (*(x + (iy * ny) + ix));
+
+    /* Compute limits for this pixel */
+    nx2 = ndx / 2;
+    jx1 = ix - nx2;
+    if (jx1 < 0)
+	jx1 = 0;
+    jx2 = ix + nx2 + 1;
+    if (jx2 > nx)
+	jx2 = nx;
+    ny2 = ndy / 2;
+    jy1 = iy - ny2;
+    if (jy1 < 0)
+	jy1 = 0;
+    jy2 = iy + ny2 + 1;
+    if (jy2 > ny)
+	jy2 = ny;
+
+    /* Intitialize actual number of pixels used for this pixel */
+    n = 0;
+
+    /* Set up working vector for this pixel */
+    vecj = vi4;
+    for (jy = jy1; jy < jy2; jy++) {
+	img = x + (jy * nx) + jx1;
+	for (jx = jx1; jx < jx2; jx++) {
+	    if (*img != bpvali4) {
+		*vecj++ = *img;
+		n++;
+		}
+	    img++;
+	    }
+	}
+
+    /* If no good pixels, return old value */
+    if (n < 1)
+	return (ival);
+
+    /* Sort numbers in working vector */
+    else {
+	for (j = 2; j <= n; j++) {
+	    xx = vi4[j];
+	    i = j - 1;
+	    while (i > 0 && vi4[i] > xx) {
+		vi4[i+1] = vi4[i];
+		i--;
+		}
+	    vi4[i+1] = xx;
+	    }
+
+	/* Middle number is the median */
+	return (vi4[n/2]);
+	}
+}
+
+
+float
+medpixr4 (x, rval, ix, iy, nx, ny, ndx, ndy)
+
+float	*x;	/* Image buffer */
+float	rval;	/* Image value at this pixel */
+int	ix,iy;	/* Pixel around which to compute median */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+
+{
+    float xx, *vecj, *img;
+    int n, i, j;
+    int  nx2, ny2, npix;
+    int jx, jx1, jx2, jy, jy1, jy2;
+
+    /* Allocate working buffer if it hasn't already been allocated */
+    npix = ndx * ndy;
+    if (vr4 == NULL) {
+	vr4 = (float *) calloc (npix, sizeof (float));
+	if (vr4 == NULL) {
+	    fprintf (stderr, "MEDIANR4: Could not allocate %d-pixel buffer\n",npix);
+	    return (0);
+	    }
+	}
+
+    n = ndx * ndy;
+    if (n <= 0)
+	return (0.0);
+    else if (n == 1)
+	return (*(x + (iy * ny) + ix));
+
+    /* Compute limits for this pixel */
+    nx2 = ndx / 2;
+    jx1 = ix - nx2;
+    if (jx1 < 0)
+	jx1 = 0;
+    jx2 = ix + nx2 + 1;
+    if (jx2 > nx)
+	jx2 = nx;
+    ny2 = ndy / 2;
+    jy1 = iy - ny2;
+    if (jy1 < 0)
+	jy1 = 0;
+    jy2 = iy + ny2 + 1;
+    if (jy2 > ny)
+	jy2 = ny;
+
+    /* Initialize actual number of pixels used for this pixel */
+    n = 0;
+
+    /* Set up working vector for this pixel */
+    vecj = vr4;
+    for (jy = jy1; jy < jy2; jy++) {
+	img = x + (jy * nx) + jx1;
+	for (jx = jx1; jx < jx2; jx++) {
+	    if (*img != bpvalr4) {
+		*vecj++ = *img;
+		n++;
+		}
+	    img++;
+	    }
+	}
+
+    /* If no good pixels, return old value */
+    if (n < 1)
+	return (rval);
+
+    /* Sort numbers in working vector */
+    else {
+	for (j = 2; j <= n; j++) {
+	    xx = vr4[j];
+	    i = j - 1;
+	    while (i > 0 && vr4[i] > xx) {
+		vr4[i+1] = vr4[i];
+		i--;
+		}
+	    vr4[i+1] = xx;
+	    }
+
+	/* Middle number is the median */
+	return (vr4[n/2]);
+	}
+}
+
+
+double
+medpixr8 (x, dval, ix, iy, nx, ny, ndx, ndy)
+
+double	*x;	/* Image buffer */
+double	dval;	/* Image value of this pixel */
+int	ix,iy;	/* Pixel around which to compute median */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+
+{
+    double xx, *vecj, *img;
+    int n, i, j;
+    int  nx2, ny2, npix;
+    int jx, jx1, jx2, jy, jy1, jy2;
+
+    /* Allocate working buffer if it hasn't already been allocated */
+    npix = ndx * ndy;
+    if (vr8 == NULL) {
+	vr8 = (double *) calloc (npix, sizeof (double));
+	if (vr8 == NULL) {
+	    fprintf (stderr, "MEDIANR8: Could not allocate %d-pixel buffer\n",npix);
+	    return (0);
+	    }
+	}
+
+    n = ndx * ndy;
+    if (n <= 0)
+	return (0.0);
+    else if (n == 1)
+	return (*(x + (iy * ny) + ix));
+
+    /* Compute limits for this pixel */
+    nx2 = ndx / 2;
+    jx1 = ix - nx2;
+    if (jx1 < 0)
+	jx1 = 0;
+    jx2 = ix + nx2 + 1;
+    if (jx2 > nx)
+	jx2 = nx;
+    ny2 = ndy / 2;
+    jy1 = iy - ny2;
+    if (jy1 < 0)
+	jy1 = 0;
+    jy2 = iy + ny2 + 1;
+    if (jy2 > ny)
+	jy2 = ny;
+
+    /* Initialize actual number of pixels used for this pixel */
+    n = 0;
+
+    /* Set up working vector for this pixel */
+    vecj = vr8;
+    for (jy = jy1; jy < jy2; jy++) {
+	img = x + (jy * nx) + jx1;
+	for (jx = jx1; jx < jx2; jx++) {
+	    if (*img != bpval) {
+		*vecj++ = *img;
+		n++;
+		}
+	    img++;
+	    }
+	}
+
+    /* If no good pixels, return old value */
+    if (n < 1)
+	return (dval);
+
+    /* Sort numbers in working vector */
+    else {
+	for (j = 2; j <= n; j++) {
+	    xx = vr8[j];
+	    i = j - 1;
+	    while (i > 0 && vr8[i] > xx) {
+		vr8[i+1] = vr8[i];
+		i--;
+		}
+	    vr8[i+1] = xx;
+	    }
+
+	/* Middle number is the median */
+	return (vr8[n/2]);
+	}
+}
+
+
+/* Mean filter an image */
+
+char *
+meanfilt (buff, header, ndx, ndy, nlog)
+
+char	*buff;	/* Image buffer */
+char	*header; /* FITS image header */
+int	ndx;	/* Number of columns over which to compute the mean */
+int	ndy;	/* Number of rows over which to compute the mean */
+int	nlog;	/* Logging interval in pixels */
+
+{
+char	*buffret;	/* Modified image buffer (returned) */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ix,iy;	/* Pixel around which to compute mean */
+int	npix;	/* Number of pixels in image */
+int	bitpix;	/* Number of bits per pixel (<0=floating point) */
+int	naxes;
+
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS", &naxes);
+    hgeti4 (header, "NAXIS1", &nx);
+    if (naxes > 1)
+	hgeti4 (header, "NAXIS2", &ny);
+    else
+	ny = 1;
+    npix = nx * ny;
+    hgetr8 (header, "BLANK", &bpval);
+
+    buffret = NULL;
+    if (bitpix == 16) {
+	short *b1, *b2, *buffout;
+	bpvali2 = (short) bpval;
+	vi2 = NULL;
+	buffret = (char *) calloc (npix, sizeof (short));
+	buffout = (short *) buffret;
+	b1 = (short *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = meanpixi2 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEANFILT: %d lines filtered\r", iy+1);
+	    }
+	free (vi2);
+	vi2 = NULL;
+	}
+    else if (bitpix == 32) {
+	int *b1, *b2, *buffout;
+	bpvali4 = (int) bpval;
+	vi4 = NULL;
+	buffret = (char *) calloc (npix, sizeof (int));
+	buffout = (int *) buffret;
+	b1 = (int *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = meanpixi4 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEANFILT: %d lines filtered\r", iy+1);
+	    }
+	free (vi4);
+	vi4 = NULL;
+	}
+    else if (bitpix == -32) {
+	float *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (float));
+	buffout = (float *) buffret;
+	bpvalr4 = (float) bpval;
+	b1 = (float *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = meanpixr4 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEANFILT: %d lines filtered\r", iy+1);
+	    }
+	free (vr4);
+	vr4 = NULL;
+	}
+    else if (bitpix == -64) {
+	double *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (double));
+	buffout = (double *) buffret;
+	b1 = (double *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = meanpixr8 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEANFILT: %d lines filtered\r", iy+1);
+	    }
+	free (vr8);
+	vr8 = NULL;
+	}
+    if (nlog > 0)
+	fprintf (stderr,"MEANFILT: %d lines filtered\n", iy);
+    return (buffret);
+}
+
+
+/* Set blank pixels to the mean of a box around each one */
+
+char *
+meanfill (buff, header, ndx, ndy, nlog)
+
+char	*buff;	/* Image buffer */
+char	*header; /* FITS image header */
+int	ndx;	/* Number of columns over which to compute the mean */
+int	ndy;	/* Number of rows over which to compute the mean */
+int	nlog;	/* Logging interval in pixels */
+
+{
+char	*buffret;	/* Modified image buffer (returned) */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ix,iy;	/* Pixel around which to compute mean */
+int	npix;	/* Number of pixels in image */
+int	bitpix;	/* Number of bits per pixel (<0=floating point) */
+int	naxes;
+
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS", &naxes);
+    hgeti4 (header, "NAXIS1", &nx);
+    if (naxes > 1)
+	hgeti4 (header, "NAXIS2", &ny);
+    else
+	ny = 1;
+    npix = nx * ny;
+    hgetr8 (header, "BLANK", &bpval);
+
+    nfilled = 0;
+
+    buffret = NULL;
+    if (bitpix == 16) {
+	short *b1, *b2, *buffout;
+	bpvali2 = (short) bpval;
+	vi2 = NULL;
+	buffret = (char *) calloc (npix, sizeof (short));
+	buffout = (short *) buffret;
+	b1 = (short *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpvali2)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = meanpixi2 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEANFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	free (vi2);
+	vi2 = NULL;
+	}
+    else if (bitpix == 32) {
+	int *b1, *b2, *buffout;
+	bpvali4 = (int) bpval;
+	vi4 = NULL;
+	buffret = (char *) calloc (npix, sizeof (int));
+	buffout = (int *) buffret;
+	b1 = (int *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpvali4)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = meanpixi4 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEANFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	free (vi4);
+	vi4 = NULL;
+	}
+    else if (bitpix == -32) {
+	float *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (float));
+	buffout = (float *) buffret;
+	bpvalr4 = (float) bpval;
+	b1 = (float *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpvalr4)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = meanpixr4 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEANFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	free (vr4);
+	vr4 = NULL;
+	}
+    else if (bitpix == -64) {
+	double *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (double));
+	buffout = (double *) buffret;
+	b1 = (double *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpval)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = meanpixr8 (buff, *b1++, ix, iy, nx, ny, ndx, ndy);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"MEANFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	free (vr8);
+	vr8 = NULL;
+	}
+    if (nlog > 0)
+	fprintf (stderr,"MEANFILL: %d lines, %d pixels filled\n", iy, nfilled);
+    return (buffret);
+}
+
+
+/* Compute mean of rectangular group of pixels */
+
+short
+meanpixi2 (x, ival, ix, iy, nx, ny, ndx, ndy)
+
+short	*x;	/* Image buffer */
+short	ival;	/* Image pixel value */
+int	ix,iy;	/* Pixel around which to compute median */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+
+{
+    double sum;
+    short *img;
+    int n, nx2, ny2;
+    int jx, jx1, jx2, jy, jy1, jy2;
+
+    n = ndx * ndy;
+    if (n <= 0)
+	return (0.0);
+    else if (n == 1)
+	return (*(x + (iy * ny) + ix));
+
+    /* Compute limits for this pixel */
+    nx2 = ndx / 2;
+    jx1 = ix - nx2;
+    if (jx1 < 0)
+	jx1 = 0;
+    jx2 = ix + nx2 + 1;
+    if (jx2 > nx)
+	jx2 = nx;
+    ny2 = ndy / 2;
+    jy1 = iy - ny2;
+    if (jy1 < 0)
+	jy1 = 0;
+    jy2 = iy + ny2 + 1;
+    if (jy2 > ny)
+	jy2 = ny;
+
+    /* Initialize actual number of pixels used for this pixel */
+    n = 0;
+
+    /* Compute total counts around this pixel */
+    sum = 0.0;
+    for (jy = jy1; jy < jy2; jy++) {
+	img = x + (jy * nx) + jx1;
+	for (jx = jx1; jx < jx2; jx++) {
+	    if (*img != bpvali2) {
+		sum = sum + (double) *img;
+		n++;
+		}
+	    img++;
+	    }
+	}
+
+    if (n < 1)
+	return (ival);
+    else
+	return ((short) (sum / (double) n));
+}
+
+
+int
+meanpixi4 (x, ival, ix, iy, nx, ny, ndx, ndy)
+
+int	*x;	/* Image buffer */
+int	ival;	/* Image pixel value */
+int	ix,iy;	/* Pixel around which to compute median */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+
+{
+    double sum;
+    int *img;
+    int n, nx2, ny2;
+    int jx, jx1, jx2, jy, jy1, jy2;
+
+    n = ndx * ndy;
+    if (n <= 0)
+	return (0.0);
+    else if (n == 1)
+	return (*(x + (iy * ny) + ix));
+
+    /* Compute limits for this pixel */
+    nx2 = ndx / 2;
+    jx1 = ix - nx2;
+    if (jx1 < 0)
+	jx1 = 0;
+    jx2 = ix + nx2 + 1;
+    if (jx2 > nx)
+	jx2 = nx;
+    ny2 = ndy / 2;
+    jy1 = iy - ny2;
+    if (jy1 < 0)
+	jy1 = 0;
+    jy2 = iy + ny2 + 1;
+    if (jy2 > ny)
+	jy2 = ny;
+
+    /* Initialize actual number of pixels used for this pixel */
+    n = 0;
+
+    /* Set up working vector for this pixel */
+    sum = 0.0;
+    for (jy = jy1; jy < jy2; jy++) {
+	img = x + (jy * nx) + jx1;
+	for (jx = jx1; jx < jx2; jx++) {
+	    if (*img != bpvali4) {
+		sum = sum + (double) *img;
+		n++;
+		}
+	    img++;
+	    }
+	}
+
+    if (n < 1)
+	return (ival);
+    else
+	return ((int) (sum / (double) n));
+}
+
+
+float
+meanpixr4 (x, rval, ix, iy, nx, ny, ndx, ndy)
+
+float	*x;	/* Image buffer */
+float	rval;	/* Image pixel value */
+int	ix,iy;	/* Pixel around which to compute median */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+
+{
+    double sum;
+    float *img;
+    int n, nx2, ny2;
+    int jx, jx1, jx2, jy, jy1, jy2;
+
+    n = ndx * ndy;
+    if (n <= 0)
+	return (0.0);
+    else if (n == 1)
+	return (*(x + (iy * ny) + ix));
+
+    /* Compute limits for this pixel */
+    nx2 = ndx / 2;
+    jx1 = ix - nx2;
+    if (jx1 < 0)
+	jx1 = 0;
+    jx2 = ix + nx2 + 1;
+    if (jx2 > nx)
+	jx2 = nx;
+    ny2 = ndy / 2;
+    jy1 = iy - ny2;
+    if (jy1 < 0)
+	jy1 = 0;
+    jy2 = iy + ny2 + 1;
+    if (jy2 > ny)
+	jy2 = ny;
+
+    /* Initialize actual number of pixels used for this pixel */
+    n = 0;
+
+    /* Set up working vector for this pixel */
+    sum = 0.0;
+    for (jy = jy1; jy < jy2; jy++) {
+	img = x + (jy * nx) + jx1;
+	for (jx = jx1; jx < jx2; jx++) {
+	    if (*img != bpvalr4) {
+		sum = sum + (double) *img;
+		n++;
+		}
+	    img++;
+	    }
+	}
+
+    if (n < 1)
+	return (rval);
+    else
+	return ((float) (sum / (double) n));
+}
+
+
+double
+meanpixr8 (x, dval, ix, iy, nx, ny, ndx, ndy)
+
+double	*x;	/* Image buffer */
+double	dval;	/* Image pixel value */
+int	ix,iy;	/* Pixel around which to compute median */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ndx;	/* Number of columns over which to compute the median */
+int	ndy;	/* Number of rows over which to compute the median */
+
+{
+    double *img;
+    double sum;
+    int n, nx2, ny2;
+    int jx, jx1, jx2, jy, jy1, jy2;
+
+    n = ndx * ndy;
+    if (n <= 0)
+	return (0.0);
+    else if (n == 1)
+	return (*(x + (iy * ny) + ix));
+
+    /* Compute limits for this pixel */
+    nx2 = ndx / 2;
+    jx1 = ix - nx2;
+    if (jx1 < 0)
+	jx1 = 0;
+    jx2 = ix + nx2 + 1;
+    if (jx2 > nx)
+	jx2 = nx;
+    ny2 = ndy / 2;
+    jy1 = iy - ny2;
+    if (jy1 < 0)
+	jy1 = 0;
+    jy2 = iy + ny2 + 1;
+    if (jy2 > ny)
+	jy2 = ny;
+
+    /* Initialize actual number of pixels used for this pixel */
+    n = 0;
+
+    /* Set up working vector for this pixel */
+    sum = 0.0;
+    for (jy = jy1; jy < jy2; jy++) {
+	img = x + (jy * nx) + jx1;
+	for (jx = jx1; jx < jx2; jx++) {
+	    if (*img != bpval) {
+		sum = sum + (double) *img;
+		n++;
+		}
+	    img++;
+	    }
+	}
+
+    if (n < 1)
+	return (dval);
+    else
+	return (sum / (double) n);
+}
+
+
+/* Gaussian filter an image */
+
+char *
+gaussfilt (buff, header, ndx, ndy, nlog)
+
+char	*buff;		/* Image buffer */
+char	*header;	/* FITS image header */
+int	ndx;		/* Number of columns over which to compute the Gaussian */
+int	ndy;		/* Number of rows over which to compute the Gaussian */
+int	nlog;		/* Logging interval in pixels */
+
+{
+    char *buffret;	/* Modified image buffer (returned) */
+    int nx,ny;		/* Number of columns and rows in image */
+    int ix,iy;		/* Pixel around which to compute median */
+    int npix;		/* Number of pixels in image */
+    int bitpix;		/* Number of bits per pixel (<0=floating point) */
+    int naxes;
+
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS", &naxes);
+    hgeti4 (header, "NAXIS1", &nx);
+    if (naxes > 1)
+	hgeti4 (header, "NAXIS2", &ny);
+    else
+	ny = 1;
+    npix = nx * ny;
+    hgetr8 (header, "BLANK", &bpval);
+
+    gausswt (ndx, ndy, nx);
+
+    buffret = NULL;
+    if (bitpix == 16) {
+	short *b1, *b2, *buffout;
+	bpvali2 = (short) bpval;
+	buffret = (char *) calloc (npix, sizeof (short));
+	buffout = (short *) buffret;
+	b1 = (short *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = gausspixi2 (buff, *b1++, ix, iy, nx, ny);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"GAUSSFILT: %d/%d lines filtered\r", iy+1,ny);
+	    }
+	fprintf (stderr,"\n");
+	}
+    else if (bitpix == 32) {
+	int *b1, *b2, *buffout;
+	bpvali4 = (int) bpval;
+	buffret = (char *) calloc (npix, sizeof (int));
+	buffout = (int *) buffret;
+	b1 = (int *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = gausspixi4 (buff, *b1++, ix, iy, nx, ny);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"GAUSSFILT: %d/%d lines filtered\r", iy+1,ny);
+	}
+	fprintf (stderr,"\n");
+	}
+    else if (bitpix == -32) {
+	float *b1, *b2, *buffout;
+	bpvalr4 = (float) bpval;
+	buffret = (char *) calloc (npix, sizeof (float));
+	buffout = (float *) buffret;
+	b1 = (float *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = gausspixr4 (buff, *b1++, ix, iy, nx, ny);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"GAUSSFILT: %d/%d lines filtered\r", iy+1,ny);
+	    }
+	fprintf (stderr,"\n");
+	}
+    else if (bitpix == -64) {
+	double *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (double));
+	buffout = (double *) buffret;
+	b1 = (double *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		*b2++ = gausspixr8 (buff, *b1++, ix, iy, nx, ny);
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"GAUSSFILT: %d/%d lines filtered\r", iy+1,ny);
+	    }
+	fprintf (stderr,"\n");
+	}
+    if (nlog > 0 && (iy+1)%nlog == 0)
+	fprintf (stderr,"GAUSSFILT: %d/%d lines filtered\n", iy,ny);
+    return (buffret);
+}
+
+
+/* Set blank pixels to the Gaussian weighted sum of a box around each one */
+
+char *
+gaussfill (buff, header, ndx, ndy, nlog)
+
+char	*buff;	/* Image buffer */
+char	*header; /* FITS image header */
+int	ndx;	/* Number of columns over which to compute the mean */
+int	ndy;	/* Number of rows over which to compute the mean */
+int	nlog;	/* Logging interval in pixels */
+
+{
+char	*buffret;	/* Modified image buffer (returned) */
+int	nx,ny;	/* Number of columns and rows in image */
+int	ix,iy;	/* Pixel around which to compute median */
+int	npix;	/* Number of pixels in image */
+int	bitpix;	/* Number of bits per pixel (<0=floating point) */
+int	naxes;
+
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS", &naxes);
+    hgeti4 (header, "NAXIS1", &nx);
+    if (naxes > 1)
+	hgeti4 (header, "NAXIS2", &ny);
+    else
+	ny = 1;
+    npix = nx * ny;
+    hgetr8 (header, "BLANK", &bpval);
+
+    nfilled = 0;
+
+    gausswt (ndx, nx);
+
+    buffret = NULL;
+    if (bitpix == 16) {
+	short *b1, *b2, *buffout;
+	bpvali2 = (short) bpval;
+	buffret = (char *) calloc (npix, sizeof (short));
+	buffout = (short *) buffret;
+	b1 = (short *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpvali2)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = gausspixi2 (buff, *b1++, ix, iy, nx, ny);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"GAUSSFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	}
+    else if (bitpix == 32) {
+	int *b1, *b2, *buffout;
+	bpvali4 = (int) bpval;
+	buffret = (char *) calloc (npix, sizeof (int));
+	buffout = (int *) buffret;
+	b1 = (int *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpvali4)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = gausspixi4 (buff, *b1++, ix, iy, nx, ny);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"GAUSSFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	}
+    else if (bitpix == -32) {
+	float *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (float));
+	buffout = (float *) buffret;
+	bpvalr4 = (float) bpval;
+	b1 = (float *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpvalr4)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = gausspixr4 (buff, *b1++, ix, iy, nx, ny);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"GAUSSFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	}
+    else if (bitpix == -64) {
+	double *b1, *b2, *buffout;
+	buffret = (char *) calloc (npix, sizeof (double));
+	buffout = (double *) buffret;
+	b1 = (double *) buff;
+	b2 = buffout;
+	for (iy = 0; iy < ny; iy++) {
+	    for (ix = 0; ix < nx; ix++) {
+		if (*b1 != bpval)
+		    *b2++ = *b1++;
+		else {
+		    *b2++ = gausspixr8 (buff, *b1++, ix, iy, nx, ny);
+		    nfilled++;
+		    }
+		}
+	    if (nlog > 0 && (iy+1)%nlog == 0)
+		fprintf (stderr,"GAUSSFILL: %d lines, %d pixels filled\r", iy+1, nfilled);
+	    }
+	}
+    if (nlog > 0)
+	fprintf (stderr,"GAUSSFILL: %d lines, %d pixels filled\n", iy, nfilled);
+    return (buffret);
+}
+
+/* Compute Gaussian weighting function */
+
+static double *gwt = NULL;
+static int *ixbox;	/* Vector of x offsets in image */
+static int *iybox;	/* Vector of y offsets in image */
+static int *ipbox;	/* Vector of pixel offsets in image */
+static int npbox;	/* Number of pixels in replacement vector */
+
+static int mpbox = 1;	/* Minimum number of good pixels to use value */
+void
+setminpix (minpix)
+int minpix;
+{ mpbox = minpix; return; }
+
+static double hwidth = 1.0;	/* Half-width at half-height of Gaussian */
+void
+setghwidth (ghwidth)
+double ghwidth;
+{ hwidth = ghwidth; return; }
+
+static int nsub = 1;	/* Number of sub-computations per pixel */
+void
+setsubpix (nsubpix)
+double nsubpix;
+{ nsub = nsubpix; return; }
+
+/* Compute Gaussian weights for a square region */
+
+void 
+gausswt (mx, my, nx)
+
+int	mx;	/* Width in pixels over which to compute the Gaussian */
+int	my;	/* Height in pixels over which to compute the Gaussian */
+int	nx;	/* Number of columns (naxis1) in image */
+{
+    int	i, idr, idc, jx, jy, jr;
+    double dsub, xd0, xd, xdr, xdc, twt, rad2;
+    extern void setscale();
+
+    setscale (0);
+
+    dsub = (double) nsub;
+    xd0 = (dsub - 1.0) / (dsub * 2.0);
+    xd = 1.0 / (hwidth * dsub);
+    twt = 0.0;
+
+    if (gwt != NULL) {
+	free (gwt);
+	free (ixbox);
+	free (iybox);
+	free (ipbox);
+	}
+    npbox = mx * my;
+    gwt = (double *) calloc (npbox, sizeof(double));
+    ixbox = (int *) calloc (npbox, sizeof(int));
+    iybox = (int *) calloc (npbox, sizeof(int));
+    ipbox = (int *) calloc (npbox, sizeof(int));
+    idr = (-my / 2) - 1;
+    i = 0;
+    for (jy = 0; jy < my; jy++) {
+	idr++;
+	idc = (-mx / 2) - 1;
+	for (jx = 0; jx < mx; jx++) {
+	    idc++;
+	    gwt[i] = 0.0;
+	    xdr = ((double) idr - xd0) / hwidth;
+	    for (jr = 0; jr < nsub; jr++) {
+		xdc = ((double)(idc) - xd0) / hwidth;
+		for (jr = 0; jr < nsub; jr++) {
+		    rad2 = (xdc * xdc) + (xdr * xdr);
+		    gwt[i] = gwt[i] + exp (-rad2 / 2.0);
+		    xdc = xdc + xd;
+		    }
+		xdr = xdr + xd;
+		}
+	    twt = twt + gwt[i];
+	    iybox[i] = idc;
+	    ixbox[i] = idr;
+	    ipbox[i] = (idr * nx) + idc;
+	    i++;
+	    }
+	}
+
+    /* Normalize to 1.0 */
+    for (i = 0; i < npbox; i++)
+	gwt[i] = gwt[i] / twt;
+
+    return;
+}
+
+
+/* Compute Gaussian-weighted mean of a square group of pixels */
+/* gausswt() must be called first to set up weighting vector */
+
+short
+gausspixi2 (image, ival, ix, iy, nx, ny)
+
+short	*image;	/* Image buffer */
+short	ival;	/* Pixel value */
+int	ix,iy;	/* Pixel around which to compute Gaussian-weighted mean */
+int	nx,ny;	/* Number of columns and rows in image */
+{
+    short *img;
+    double twt, tpix;
+    double flux;
+    int i, ixi, iyi;
+    int  np;
+
+    if (npbox <= 1)
+	return (ival);
+
+    twt = 0.0;
+    tpix = 0.0;
+    np = 0;
+    for (i = 0; i < npbox; i++) {
+	ixi = ix + ixbox[i];
+	iyi = iy + iybox[i];
+	if (ixi > -1 && iyi > -1 && ixi < nx && iyi < ny) {
+	    img = image + (iyi * ny) + ixi;
+	    if (*img != bpvali2) {
+		flux = (double) *img;
+		twt = twt + gwt[i];
+		tpix = tpix + gwt[i] * flux;
+		np++;
+		}
+	    }
+	}
+
+    /* If enough surrounding pixels are non-zero, replace the current pixel */
+    if (np > mpbox && twt > 0.0) {
+	if (twt < 1.0)
+	    tpix = tpix / twt;
+	return ((short) tpix);
+	}
+    else
+	return (ival);
+}
+
+
+int
+gausspixi4 (image, ival, ix, iy, nx, ny)
+
+int	*image;	/* Image buffer */
+int	ival;	/* Pixel value */
+int	ix,iy;	/* Pixel around which to compute Gaussian-weighted mean */
+int	nx,ny;	/* Number of columns and rows in image */
+{
+    double twt, tpix;
+    double flux;
+    int *img;
+    int i, ixi, iyi;
+    int  np;
+
+    if (npbox <= 1)
+	return (ival);
+
+    twt = 0.0;
+    tpix = 0.0;
+    np = 0;
+    for (i = 0; i < npbox; i++) {
+	ixi = ix + ixbox[i];
+	iyi = iy + iybox[i];
+	if (ixi > -1 && iyi > -1 && ixi < nx && iyi < ny) {
+	    img = image + (iyi * ny) + ixi;
+	    if (*img != bpvali4) {
+		flux = (double) *img;
+		twt = twt + gwt[i];
+		tpix = tpix + gwt[i] * flux;
+		np++;
+		}
+	    }
+	}
+
+    /* If enough surrounding pixels are non-zero, replace the current pixel */
+    if (np > mpbox && twt > 0.0) {
+	if (twt < 1.0)
+	    tpix = tpix / twt;
+	return ((int) tpix);
+	}
+    else
+	return (ival);
+}
+
+
+float
+gausspixr4 (image, rval, ix, iy, nx, ny)
+
+float	*image;	/* Image buffer */
+float	rval;	/* Pixel value */
+int	ix,iy;	/* Pixel around which to compute Gaussian-weighted mean */
+int	nx,ny;	/* Number of columns and rows in image */
+{
+    double twt, tpix;
+    double flux;
+    float *img;
+    int i, ixi, iyi;
+    int  np;
+
+    if (npbox <= 1)
+	return (rval);
+
+    twt = 0.0;
+    tpix = 0.0;
+    np = 0;
+    for (i = 0; i < npbox; i++) {
+	ixi = ix + ixbox[i];
+	iyi = iy + iybox[i];
+	if (ixi > -1 && iyi > -1 && ixi < nx && iyi < ny) {
+	    img = image + (iyi * ny) + ixi;
+	    if (*img != bpvalr4) {
+		flux = (double) image[ixi + (iyi * ny)];
+		twt = twt + gwt[i];
+		tpix = tpix + gwt[i] * flux;
+		np++;
+		}
+	    }
+	}
+
+    /* If enough surrounding pixels are non-zero, replace the current pixel */
+    if (np > mpbox && twt > 0.0) {
+	if (twt < 1.0)
+	    tpix = tpix / twt;
+	return ((float) tpix);
+	}
+    else
+	return (rval);
+}
+
+
+double
+gausspixr8 (image, dval, ix, iy, nx, ny)
+
+double	*image;	/* Image buffer */
+double	dval;	/* Pixel value */
+int	ix,iy;	/* Pixel around which to compute Gaussian-weighted mean */
+int	nx,ny;	/* Number of columns and rows in image */
+{
+    double *img;
+    double twt, tpix;
+    double flux;
+    int i, ixi, iyi;
+    int  np;
+
+    if (npbox <= 1)
+	return (dval);
+
+    twt = 0.0;
+    tpix = 0.0;
+    np = 0;
+    for (i = 0; i < npbox; i++) {
+	ixi = ix + ixbox[i];
+	iyi = iy + iybox[i];
+	if (ixi > -1 && iyi > -1 && ixi < nx && iyi < ny) {
+	    img = image + (iyi * ny) + ixi;
+	    if (*img != bpval) {
+		flux = image[ixi + (iyi * ny)];
+		twt = twt + gwt[i];
+		tpix = tpix + gwt[i] * flux;
+		np++;
+		}
+	    }
+	}
+
+    /* If enough surrounding pixels are non-zero, replace the current pixel */
+    if (np > mpbox && twt > 0.0) {
+	if (twt < 1.0)
+	    tpix = tpix / twt;
+	return (tpix);
+	}
+    else
+	return (dval);
+}
+
+
+/* Return image buffer reduced by a given factor */
+
+char *
+ShrinkFITSImage (header, image, xfactor, yfactor, mean, bitpix, nlog)
+
+char	*header;	/* Image header */
+char	*image;		/* Image bytes to be filtered */
+int	xfactor;	/* Factor by which to reduce horizontal size of image */
+int	yfactor;	/* Factor by which to reduce vertical size of image */
+int	mean;		/* If 0, sum pixels, else substitute mean */
+int	bitpix;		/* Number of bits per output pixel (neg=f.p.) */
+int	nlog;		/* Logging interval in lines */
+
+{
+
+char	*image1;
+int	nx,ny;		/* Number of columns and rows in input image */
+int	nx1,ny1;	/* Number of columns and rows in input image */
+int	ix,iy;		/* Output pixel coordinates */
+int	jx,jy;		/* Input pixel coordinates */
+int	kx, ky;
+int	nxf, nyf;
+int	npix1;		/* Number of pixels in output image */
+int	bitsin;		/* Number of bits per input pixel (<0=floating point) */
+int	naxes;
+double	pixij;		/* Summed value of rebinned pixel */
+double	bzero, bscale;
+double	pixval, dnp;
+short	*buffi2 = NULL;
+int	*buffi4 = NULL;
+float	*buffr4 = NULL;
+double	*buffr8 = NULL;
+
+    /* Get bits per pixel in input image */
+    hgeti4 (header, "BITPIX", &bitsin);
+    if (bitpix == 0) {
+	bitpix = bitsin;
+	mean = 1;
+	}
+
+    /* Get scaling of input image, if any */
+    bzero = 0.0;
+    hgetr8 (header, "BZERO", &bzero);
+    bscale = 1.0;
+    hgetr8 (header, "BSCALE", &bscale);
+
+    /* Get size of input horizontal axis */
+    hgeti4 (header, "NAXIS1", &nx);
+
+    /* Set horizontal axis for output image */
+    if (nx > xfactor)
+	nx1 = nx / xfactor;
+    else
+	nx1 = nx;
+
+    /* Get number of axes */
+    hgeti4 (header, "NAXIS", &naxes);
+
+    /* Set vertical axis for output image */
+    if (naxes > 1) {
+	hgeti4 (header, "NAXIS2", &ny);
+	if (ny > yfactor)
+	    ny1 = ny / yfactor;
+	else
+	    ny1 = ny;
+	}
+    else {
+	ny = 1;
+	ny1 = 1;
+	}
+    npix1 = nx1 * ny1;
+
+    /* Allocate output image buffer and initialize pointer into it */
+    image1 = NULL;
+    if (bitpix == 16) {
+	image1 = (char *) calloc (npix1, sizeof (short));
+	buffi2 = (short *) image1;
+	}
+    else if (bitpix == 32) {
+	image1 = (char *) calloc (npix1, sizeof (int));
+	buffi4 = (int *) image1;
+	}
+    else if (bitpix == -32) {
+	image1 = (char *) calloc (npix1, sizeof (float));
+	buffr4 = (float *) image1;
+	}
+    else if (bitpix == -64) {
+	image1 = (char *) calloc (npix1, sizeof (double));
+	buffr8 = (double *) image1;
+	}
+
+    /* Fill output buffer */
+    for (jy = 0; jy < ny1; jy++) {
+	for (jx = 0; jx < nx1; jx++) {
+	    pixij = 0.0;
+	    ky = (jy * yfactor);
+	    if (ky + yfactor > ny)
+		nyf = ny - ky + 1;
+	    else
+		nyf = yfactor;
+	    dnp = 0.0;
+	    for (iy = 0; iy < nyf; iy++) {
+		kx = (jx * xfactor);
+		if (kx + xfactor > nx)
+		    nxf = nx - kx + 1;
+		else
+		    nxf = xfactor;
+		for (ix = 0; ix < nxf; ix++) {
+		    pixval = getpix (image, bitsin, nx,ny,bzero,bscale,kx++,ky);
+		    pixij = pixij + pixval;
+		    dnp++;
+		    }
+		ky++;
+		}
+	    if (mean) {
+	 	switch (bitpix) {
+		    case 16:
+			*buffi2++ = (short) (pixij / dnp);
+			break;
+		    case 32:
+			*buffi4++ = (int) (pixij / dnp);
+			break;
+		    case -32:
+			*buffr4++ = (float) (pixij / dnp);
+			break;
+		    case -64:
+			*buffr8++ = (pixij / dnp);
+			break;
+		    }
+		}
+	    else {
+	 	switch (bitpix) {
+		    case 16:
+			if (pixij < 32768.0)
+			    *buffi2++ = (short) pixij;
+			else
+			    *buffi2++ = 32767;
+			break;
+		    case 32:
+			*buffi4++ = (int) pixij;
+			break;
+		    case -32:
+			*buffr4++ = (float) pixij;
+			break;
+		    case -64:
+			*buffr8++ = pixij;
+			break;
+		    }
+		}
+	    }
+	if ((jy+1)%nlog == 0)
+	    fprintf (stderr,"IMRESIZE: %d/%d lines created\r", jy+1, ny1);
+	}
+    if (nlog > 0)
+	fprintf (stderr,"\n");
+
+    return (image1);
+}
+
+/* Return image header with dimensions reduced by a given factor */
+
+char *
+ShrinkFITSHeader (filename, header, xfactor, yfactor, mean, bitpix)
+
+char	*filename;	/* Name of image file before shrinking */
+char	*header;	/* Image header */
+int	xfactor;	/* Factor by which to reduce horizontal size of image */
+int	yfactor;	/* Factor by which to reduce vertical size of image */
+int	mean;		/* If 0, sum pixels, else substitute mean */
+int	bitpix;		/* Number of bits per output pixel (neg=f.p.) */
+
+{
+    char *newhead;	/* New header for shrunken file */
+    char history[64];
+    int nbhead;		/* Number of bytes in header */
+    int naxes;
+    int nblocks;
+    int nx, ny, nx1, ny1;
+    double crpix1, crpix2, cdelt1, cdelt2;
+    double dfac;
+
+    nbhead = strlen (header);
+    nblocks = nbhead / 2880;
+    if (nblocks * 2880 < nbhead)
+	nblocks = nblocks + 1;
+    nbhead = 2880 * (nblocks +1);
+    newhead = (char *) calloc (nbhead, 1);
+    strcpy (newhead, header);
+
+    /* Set pixel size in bits */
+    if (bitpix == 0) {
+	hgeti4 (header, "BITPIX", &bitpix);
+	mean = 1;
+	}
+    hputi4 (newhead, "BITPIX", bitpix);
+
+    /* Set new image horizontal dimension */
+    hgeti4 (header, "NAXIS1", &nx);
+    if (nx > xfactor)
+	nx1 = nx / xfactor;
+    else
+	nx1 = nx;
+    hputi4 (newhead, "NAXIS1", nx1);
+
+    /* Set new image vertical dimension */
+    hgeti4 (header, "NAXIS", &naxes);
+    if (naxes > 1) {
+	hgeti4 (header, "NAXIS2", &ny);
+	if (ny > yfactor)
+	    ny1 = ny / yfactor;
+	else
+	    ny1 = ny;
+	hputi4 (newhead, "NAXIS2", ny1);
+	}
+    else {
+	ny = 1;
+	ny1 = 1;
+	}
+
+    /* Fix WCS */
+    dfac = (double) xfactor;
+    if (hgetr8 (header, "CRPIX1", &crpix1)) {
+	crpix1 = (crpix1 / dfac) + 0.5;
+	hputr8 (newhead, "CRPIX1", crpix1);
+	}
+    if (hgetr8 (header, "CDELT1", &cdelt1)) {
+	cdelt1 = (cdelt1 * dfac);
+	hputr8 (newhead, "CDELT1", cdelt1);
+	}
+    if (hgetr8 (header, "CD1_1", &cdelt1)) {
+	cdelt1 = (cdelt1 * dfac);
+	hputr8 (newhead, "CD1_1", cdelt1);
+	}
+    if (hgetr8 (header, "CD1_2", &cdelt1)) {
+	cdelt1 = (cdelt1 * dfac);
+	hputr8 (newhead, "CD1_2", cdelt1);
+	}
+    dfac = (double) yfactor;
+    if (hgetr8 (header, "CRPIX2", &crpix2)) {
+	crpix2 = (crpix2 / dfac) + 0.5;
+	hputr8 (newhead, "CRPIX2", crpix2);
+	}
+    if (hgetr8 (header, "CDELT2", &cdelt2)) {
+	cdelt2 = (cdelt2 * dfac);
+	hputr8 (newhead, "CDELT2", cdelt2);
+	}
+    if (hgetr8 (header, "CD2_1", &cdelt2)) {
+	cdelt2 = (cdelt2 * dfac);
+	hputr8 (newhead, "CD2_1", cdelt2);
+	}
+    if (hgetr8 (header, "CD2_2", &cdelt2)) {
+	cdelt2 = (cdelt2 * dfac);
+	hputr8 (newhead, "CD2_2", cdelt2);
+	}
+
+    /* Add keyword to denote this operation */
+    if (strlen (filename) < 40)
+	sprintf (history, "%s blocked %dx%d", filename, xfactor, yfactor);
+    else
+	sprintf (history, "%40s blocked / %dx%d", filename, xfactor, yfactor);
+    if (mean)
+	strcat (history, " mean");
+    else
+	strcat (history, " sum");
+    hputs (newhead, "IMSHRINK", history);
+    return (newhead);
+}
+
+static double apint();
+static double imapfr();
+
+/* PhotPix -- Compute counts within a strictly defined circular aperture
+ *	      returns sum of pixel flux
+ *	      Jul  4 1984 Original Fortran program by Sam Conner at MIT
+ *	      Mar  2 1987 Ported to Unix by Doug Mink at SAO
+ *	      Jan 30 2002 translated from Fortran to C by Doug Mink at SAO
+ */
+
+double
+PhotPix (imbuff,header,cx,cy,rad,sumw)
+
+char	*imbuff;		/* address of start of image buffer */
+char	*header;	/* image FITS header */
+double	cx,cy;		/* center x,y of aperture */
+double	rad;		/* radius of aperture */
+double	*sumw;		/* sum of values of pixel weights (returned) */
+{
+    double x, y, factor, flux, bs, bz, wsum;
+    int ix1, ix2, iy1, iy2, ix, iy, bitpix, nx, ny;
+
+    *sumw = 0.0;
+    wsum = 0.0;
+    hgeti4 (header, "BITPIX", &bitpix);
+    hgeti4 (header, "NAXIS1", &nx);
+    hgeti4 (header, "NAXIS2", &ny);
+    hgetr8 (header, "BSCALE", &bs);
+    hgetr8 (header, "BZERO", &bz);
+
+    /* Find range of ys to check */
+    iy1 = (int) (cy - rad);
+    iy2 = (int) (cy + rad + 0.99999);
+    if (iy1 < 1)
+	iy1 = 1;
+    if (iy2 > ny)
+	iy2 = ny;
+
+    /* Find range of xs to check */
+    ix1 = (int) (cx - rad);
+    ix2 = (int) (cx + rad + 0.99999);
+    if (ix1 < 1)
+	ix1 = 1;
+    if (ix2 > nx)
+	ix2 = nx;
+
+    for (iy = iy1; iy <= iy2; iy++) {
+	y = (double) iy;
+	for (ix = ix1; ix <= ix2; ix++) {
+	    x = (double) ix;
+	    factor = imapfr (x,y,cx,cy,rad);
+	    *sumw = *sumw + factor;
+	    flux = getpix1 (imbuff, bitpix, nx, ny, bz, bs, ix, iy);
+	    wsum = wsum + (factor * flux);
+
+	    /* fprintf (stderr, "IMAPSB: (%d,%d)= %f weight= %f\n",
+		    ix,iy,flux,factor); */
+	    }
+	}
+
+ /* fprintf (stderr, "IMAPSB: sum of weights = %f\n", *sumw);
+    fprintf (stderr, "IMAPSB: sum of weighted intensity = %f\n", wsum);
+ */
+
+    return (wsum);
+}
+
+/* IMAPFR -- Determine the fraction of a square pixel that is included
+ *	     within the boundary of a circle of arbitrary center and radius
+ */
+
+static double
+imapfr (pcol,prow,ccol,crow,rad)
+
+double	pcol,prow;	/* column, row of pixel */
+double	ccol,crow;	/* column, row of center of circle */
+double	rad;		/* radius of circle in pixels */
+
+{
+    int icorn[4], ncorn, j, i;
+    double dist, xdiff, y0, y1, ydiff, x0, x1, x, y, dc5, dr5;
+    double cterm, ulim, llim, dc, dr, dx, dy;
+    double frac;		/* fraction of pixel within circle */
+
+    /* Compute distance to center of circle from most distant corner of pixel */
+    dc = fabs (pcol - ccol);
+    dr = fabs (prow - crow);
+    dc5 = dc + 0.5;
+    dr5 = dr + 0.5;
+    dist = sqrt ((dc5 * dc5) + (dr5 * dr5));
+    dc5 = dc - 0.5;
+    dr5 = dr - 0.5;
+
+    /* if the pixel is completely enclosed by the aperture, return 1 */
+    if (dist <= rad)
+	return (1.0);
+
+    /* if the pixel is not completely included, compute distance from the
+       center of the circle to the nearest point on the periphery of the pixel*/
+    if ((pcol-0.5) < ccol && ccol < (pcol+.5)) {
+	dist = dr5;
+
+	/* the pixel is completely excluded from the aperture */
+	if (dist >= rad)
+	      return (0.0);
+
+	else if ((prow - 0.5) < crow && crow < (prow + 0.5)) {
+	    dist = dc5;
+
+	    /* the pixel is completely excluded from the aperture */
+	    if (dist >= rad)
+	      return (0.0);
+	    }
+
+	else
+	    dist = sqrt (dc5*dc5 + dr5*dr5);
+
+	/* the pixel is completely excluded from the aperture */
+	if (dist >= rad)
+	    return (0.0);
+
+	}
+
+    /* If the pixel is partially included in the aperture, determine how
+       many corners are enclosed */
+    ncorn = 1;
+    y = prow - 1.5;
+    for (i = 0; i < 2; i++) {
+	y = y + 1.0;
+	x = pcol - 1.50;
+	for (j = 0; j < 2; j++) {
+	    icorn[j+(i*2)] = 0;
+	    x = x + 1.0;
+	    dx = x - ccol;
+	    dy = y - crow;
+	    dist = sqrt (dx*dx + dy*dy);
+	    if (dist < rad) {
+	        ncorn = ncorn + 1;
+	        icorn[j+(i*2)] = 1;
+	        }
+	    }
+	}
+
+    /* Depending on number of corners enclosed,
+       branch to appropriate computation */
+
+    /* If no corners are enclosed, determine whether the slice is vertical or
+       horizontal */
+    if (ncorn < 1) {
+
+	/* If the slice is vertical (at the sides of the aperture,
+	   at extreme x), determine the limits of integration */
+	if ((pcol - 0.5) >= ccol || ccol >= (pcol + 0.5)) {
+	    xdiff = fabs (pcol - ccol) - 0.5;
+	    dx = sqrt (rad*rad - xdiff*xdiff);
+	    y0 = crow - dx;
+	    y1 = crow + dx;
+	    if (ccol >= pcol)
+		x = pcol + 0.5;
+	    else
+		x = pcol - 0.5;
+
+	    /* Evaluate integral */
+	    frac = -xdiff * (y1 - y0) + apint ((y1 - crow), rad) -
+		   apint ((y0 - crow), rad);
+	    return (frac);
+	    }
+
+	/* If the slice is horizontal (at top or bottom of the aperture, at
+	   extreme y), determine the limits of integration. */
+	else {
+	    ydiff = fabs (prow - crow) - 0.5;
+	    dy = sqrt (rad*rad - ydiff*ydiff);
+	    x0 = ccol - dy;
+	    x1 = ccol + dy;
+	    if (crow >= prow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+
+	    /* Evaluate integral */
+	    frac = -ydiff*(x1 - x0) + apint ((x1 - ccol),rad) -
+		   apint ((x0 - ccol),rad);
+	    return (frac);
+	    }
+	}
+
+    /* if one corner is enclosed, find direction (ne,nw,se,sw) from the center*/
+    else if (ncorn < 2) {
+
+	if (pcol < ccol) {
+
+	    /* Determine the limits of integration */
+	    ydiff = fabs (prow - crow) - 0.5;;
+	    x0 = ccol - sqrt (rad*rad - ydiff*ydiff);
+	    if (crow >= prow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+
+	    /* Evaluate integral */
+	    frac = -ydiff*(pcol + 0.5 - x0) + apint ((pcol + 0.5 - ccol),rad) -
+		   apint ((x0 - ccol),rad);
+	    return (frac);
+	    }
+
+	else {
+
+	    /* Determine the limits of integration */
+	    ydiff = fabs (crow - prow) - 0.5;
+	    x1 = ccol + sqrt (rad*rad - ydiff*ydiff);
+	    if (crow >= prow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+
+	    /* Evaluate integral */
+	    frac = -ydiff*(x1 - pcol + 0.5) + apint ((x1 - ccol),rad) -
+		   apint ((pcol - .5 - ccol),rad);
+	    return (frac);
+	    }
+	}
+
+    /* If two corners are enclosed, this problem has two cases: one in
+       which the aperture boundary intersects the pixel boundary twice,
+       and another in which it intersects the pixel boundaries 4 times */
+
+    /* determine whether this may be a 4-intersection configuration */
+    else if (ncorn < 3) {
+
+	if (((pcol - 0.5) < ccol && ccol < (pcol + .5) &&
+	    (fabs(crow - prow) + 0.5) < rad) ||
+	    ((prow - 0.5) < crow && crow < (prow + 0.5) &&
+	    (fabs(ccol - pcol) + 0.5) < rad)) {
+
+/* if the pixel boundary intersects the aperture boundary in four places,
+   determine whether the pixel is east-west or north-south. */
+
+	    /* pixel is east-west */
+	    if ((pcol - 0.5) >= ccol || (pcol + 0.5) <= ccol) {
+		xdiff = dc + 0.5;
+
+		/* Determine the limits of integration */
+		dx = sqrt (rad*rad - xdiff*xdiff);
+		y0 = crow - dx;
+		y1 = crow + dx;
+		if (pcol >= ccol)
+		    x = pcol + .5;
+		else
+		    x = pcol - .5;
+		frac = 1. - xdiff*(y0 - y1 + 1.0) +
+			apint ((y0 - crow), rad) -
+			apint ((prow - 0.5 - crow), rad) +
+			apint ((prow + 0.5 - crow), rad) -
+			apint ((y1 - crow), rad);
+		return (frac);
+		}
+
+	    /* Pixel is north-south */
+
+	    else {
+		ydiff = dr + 0.5;
+
+		/* determine the limits of integration */
+		dy = sqrt (rad*rad - ydiff*ydiff);
+
+		/* x0 is the x-coordinate of the small x intercept */
+		x0 = ccol - dy;
+
+		/* x1 is the x-coordinate of the large x intercept */
+		x1 = ccol + dy;
+		if (prow >= crow)
+		    y = prow + 0.5;
+		else
+		    y = prow - 0.5;
+		frac = 1. - ydiff * (x0 - x1 + 1.0) +
+			apint ((x0 - ccol), rad) -
+			apint ((pcol - 0.5 - ccol), rad) +
+			apint ((pcol + 0.5 - ccol), rad) -
+			apint ((x1 - ccol), rad);
+		return (frac);
+		}
+	    }
+
+	/* in the two-intersection case, determine which corners are included */
+	else {
+
+	    /* the pixel is north or south of the center of the aperture */
+	    if (icorn[0]*icorn[1] == 1 || icorn[2]*icorn[3] == 1) {
+		cterm = 0.5 - dr;
+		ulim = apint ((pcol + 0.5 - ccol) ,rad);
+		llim = apint ((pcol - 0.5 - ccol) ,rad);
+		frac = cterm + ulim - llim;
+		return (frac);
+		}
+
+	    /* the pixel is east or west of the center */
+	    else {
+		cterm = 0.5 - dc;
+		ulim = apint ((prow + 0.5 - crow), rad);
+		llim = apint ((prow - 0.5 - crow), rad);
+		frac = cterm + ulim - llim;
+		return (frac);
+		}
+	    }
+	}
+
+    /* if three corners are enclosed, determine whether the corner in question
+       is east or west of the center of the aperture */
+
+    else {
+	if (pcol > ccol) {
+	    ydiff = dr + 0.5;
+	    if (prow >= crow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+
+	    x0 = ccol + sqrt (rad*rad - ydiff*ydiff);
+	    frac = 1.0 - (ydiff * (pcol + 0.5 - x0)) +
+		   apint ((pcol + 0.5 - ccol), rad) -
+		   apint ((x0 - ccol), rad);
+	    return (frac);
+	    }
+	else {
+	    ydiff = dr + 0.5;
+	    if (prow >= crow)
+		y = prow + 0.5;
+	    else
+		y = prow - 0.5;
+	    x0 = ccol - sqrt (rad*rad - ydiff*ydiff);
+	    frac = 1. - ydiff*(x0 - pcol + 0.5) +
+		   apint ((x0 - ccol),rad) -
+		   apint ((pcol - 0.5 - ccol),rad);
+	    return (frac);
+	    }
+	}
+}
+
+
+/* APINT -- Evaluate the integral of sqrt (rad**2 - x**2) dx at one limit */
+
+static double
+apint (x, rad)
+
+double	x;
+double	rad;
+{
+    double x2,rad2,arg,arg2,arcsin,pi;
+
+    pi = 3.141592654;
+
+    arg = x / rad;
+    x2 = x * x;
+    rad2 = rad * rad;
+    arg2 = x2 / rad2;
+
+    arcsin = atan2 (arg, sqrt (1.0 - arg2));
+
+    if ((1. - fabs (arg)) < 0.000001) {
+	if (arg >= 0)
+	    arcsin = pi / 2.0;
+	else
+	    arcsin = -pi / 2.0;
+	}
+
+    return (0.5 * (x * sqrt (rad2 - x2) + rad2 * arcsin));
+}
+
+
+/* Oct 25 2005	New subroutine translated from Fortran imlib/smooth.f
+ *
+ * Jan 25 2006	Add subroutines to shrink an image
+ * Mar  1 2006	Add subroutines for Gaussian smoothing/filling
+ * Apr  3 2006	Fix error return in gausspix()
+ * Apr  3 2006	Include math.h and fitsfile.h (instead of fitshead.h)
+ * Apr  7 2006	Add filling subroutines medfill(), meanfill(), gaussfill()
+ * Apr  7 2006	Add subtroutine to set bad pixels from second image
+ * Apr 27 2006	Add and correct comments
+ * May  8 2006	Print final number of pixels filled; add getnfilled()
+ * May 16 2006	Do not use BLANK from bad pixel file if set on command line 
+ * Jun 20 2006	Drop unused variables
+ * Jun 30 2006	Add number of lines in output image to log message
+ * Jul  5 2006	Add SetBadVal() to set pixels less than min to bad
+ * Jul  6 2006	Make both dimensions variable for Gaussian
+ * Jul  7 2006	SetBadVal() now checks min and max allowable values
+ * Sep 25 2006	Fix bug so CDELT1 and CDELT2 are scaled correctly
+ *
+ * Jan  8 2007	Drop unused variables from SetBadVal()
+ * Jan 11 2007	Add circular aperture photometry in PhotPix()
+ */
diff --git a/Code/src/libwcs/libwcs.a b/Code/src/libwcs/libwcs.a
new file mode 100644
index 0000000000000000000000000000000000000000..47592b08688e142aaa3e23dda75ac0e47ee64934
Binary files /dev/null and b/Code/src/libwcs/libwcs.a differ
diff --git a/Code/src/libwcs/lin.c b/Code/src/libwcs/lin.c
new file mode 100644
index 0000000000000000000000000000000000000000..999a9d00f65ea077bbf6058f969092b0bcde5789
--- /dev/null
+++ b/Code/src/libwcs/lin.c
@@ -0,0 +1,448 @@
+/*=============================================================================
+*
+*   WCSLIB - an implementation of the FITS WCS proposal.
+*   Copyright (C) 1995-2002, Mark Calabretta
+*
+*   This library is free software; you can redistribute it and/or
+*   modify it under the terms of the GNU Lesser General Public
+*   License as published by the Free Software Foundation; either
+*   version 2 of the License, or (at your option) any later version.
+*
+*   This library is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+*   Lesser General Public License for more details.
+*   
+*   You should have received a copy of the GNU Lesser General Public
+*   License along with this library; if not, write to the Free Software
+*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*   Correspondence concerning WCSLIB may be directed to:
+*      Internet email: mcalabre@atnf.csiro.au
+*      Postal address: Dr. Mark Calabretta,
+*                      Australia Telescope National Facility,
+*                      P.O. Box 76,
+*                      Epping, NSW, 2121,
+*                      AUSTRALIA
+*
+*=============================================================================
+*
+*   C routines which implement the FITS World Coordinate System (WCS)
+*   convention.
+*
+*   Summary of routines
+*   -------------------
+*   These utility routines apply the linear transformation defined by the WCS
+*   FITS header cards.  There are separate routines for the image-to-pixel,
+*   linfwd(), and pixel-to-image, linrev(), transformations.
+*
+*   An initialization routine, linset(), computes intermediate values from
+*   the transformation parameters but need not be called explicitly - see the
+*   explanation of lin.flag below.
+*
+*   An auxiliary matrix inversion routine, matinv(), is included.  It uses
+*   LU-triangular factorization with scaled partial pivoting.
+*
+*
+*   Initialization routine; linset()
+*   --------------------------------
+*   Initializes members of a linprm data structure which hold intermediate
+*   values.  Note that this routine need not be called directly; it will be
+*   invoked by linfwd() and linrev() if the "flag" structure member is
+*   anything other than a predefined magic value.
+*
+*   Given and/or returned:
+*      lin      linprm*  Linear transformation parameters (see below).
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Memory allocation error.
+*                           2: PC matrix is singular.
+*
+*   Forward transformation; linfwd()
+*   --------------------------------
+*   Compute pixel coordinates from image coordinates.  Note that where
+*   celestial coordinate systems are concerned the image coordinates
+*   correspond to (x,y) in the plane of projection, not celestial (lng,lat).
+*
+*   Given:
+*      imgcrd   const double[]
+*                        Image (world) coordinate.
+*
+*   Given and returned:
+*      lin      linprm*  Linear transformation parameters (see below).
+*
+*   Returned:
+*      pixcrd   d[]      Pixel coordinate.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: The transformation is not invertible.
+*
+*   Reverse transformation; linrev()
+*   --------------------------------
+*   Compute image coordinates from pixel coordinates.  Note that where
+*   celestial coordinate systems are concerned the image coordinates
+*   correspond to (x,y) in the plane of projection, not celestial (lng,lat).
+*
+*   Given:
+*      pixcrd   const double[]
+*                        Pixel coordinate.
+*
+*   Given and/or returned:
+*      lin      linprm*  Linear transformation parameters (see below).
+*
+*   Returned:
+*      imgcrd   d[]      Image (world) coordinate.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Error.
+*
+*   Linear transformation parameters
+*   --------------------------------
+*   The linprm struct consists of the following:
+*
+*      int flag
+*         This flag must be set to zero whenever any of the following members
+*         are set or modified.  This signals the initialization routine,
+*         linset(), to recompute intermediaries.
+*      int naxis
+*         Number of image axes.
+*      double *crpix
+*         Pointer to the first element of an array of double containing the
+*         coordinate reference pixel, CRPIXn.
+*      double *pc
+*         Pointer to the first element of the PC (pixel coordinate)
+*         transformation matrix.  The expected order is
+*
+*            lin.pc = {PC1_1, PC1_2, PC2_1, PC2_2};
+*
+*         This may be conveniently constructed from a two-dimensional array
+*         via
+*
+*            double m[2][2] = {{PC1_1, PC1_2},
+*                              {PC2_1, PC2_2}};
+*         
+*         which is equivalent to,
+*
+*            double m[2][2];
+*            m[0][0] = PC1_1;
+*            m[0][1] = PC1_2;
+*            m[1][0] = PC2_1;
+*            m[1][1] = PC2_2;
+*
+*         for which the storage order is
+*
+*            PC1_1, PC1_2, PC2_1, PC2_2
+*
+*         so it would be legitimate to set lin.pc = *m.
+*      double *cdelt
+*         Pointer to the first element of an array of double containing the
+*         coordinate increments, CDELTn.
+*
+*   The remaining members of the linprm struct are maintained by the
+*   initialization routine and should not be modified.
+*
+*      double *piximg
+*         Pointer to the first element of the matrix containing the product
+*         of the CDELTn diagonal matrix and the PC matrix.
+*      double *imgpix
+*         Pointer to the first element of the inverse of the piximg matrix.
+*
+*   linset allocates storage for the above arrays using malloc().  Note,
+*   however, that these routines do not free this storage so if a linprm
+*   variable has itself been malloc'd then these structure members must be
+*   explicitly freed before the linprm variable is free'd otherwise a memory
+*   leak will result.
+*
+*   Author: Mark Calabretta, Australia Telescope National Facility
+*   $Id: lin.c,v 2.8 2002/01/30 06:04:03 mcalabre Exp $
+*===========================================================================*/
+
+#include <stdlib.h>
+#include <math.h>
+#include "wcslib.h"
+
+/* Map error number to error message for each function. */
+const char *linset_errmsg[] = {
+   0,
+   "Memory allocation error",
+   "PC matrix is singular"};
+
+const char *linfwd_errmsg[] = {
+   0,
+   "Memory allocation error",
+   "PC matrix is singular"};
+
+const char *linrev_errmsg[] = {
+   0,
+   "Memory allocation error",
+   "PC matrix is singular"};
+
+int linset(lin)
+
+struct linprm *lin;
+
+{
+   int i, ij, j, mem, n;
+
+   n = lin->naxis;
+
+   /* Allocate memory for internal arrays. */
+   mem = n * n * sizeof(double);
+   lin->piximg = (double*)malloc(mem);
+   if (lin->piximg == (double*)0) return 1;
+
+   lin->imgpix = (double*)malloc(mem);
+   if (lin->imgpix == (double*)0) {
+      free(lin->piximg);
+      return 1;
+   }
+
+   /* Compute the pixel-to-image transformation matrix. */
+   for (i = 0, ij = 0; i < n; i++) {
+      for (j = 0; j < n; j++, ij++) {
+         lin->piximg[ij] = lin->cdelt[i] * lin->pc[ij];
+      }
+   }
+
+   /* Compute the image-to-pixel transformation matrix. */
+   if (matinv(n, lin->piximg, lin->imgpix)) return 2;
+
+   lin->flag = LINSET;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int linfwd(imgcrd, lin, pixcrd)
+
+const double imgcrd[];
+struct linprm *lin;
+double pixcrd[];
+
+{
+   int i, ij, j, n;
+
+   n = lin->naxis;
+
+   if (lin->flag != LINSET) {
+      if (linset(lin)) return 1;
+   }
+
+   for (i = 0, ij = 0; i < n; i++) {
+      pixcrd[i] = 0.0;
+      for (j = 0; j < n; j++, ij++) {
+         pixcrd[i] += lin->imgpix[ij] * imgcrd[j];
+      }
+   }
+
+   for (j = 0; j < n; j++) {
+      pixcrd[j] += lin->crpix[j];
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int linrev(pixcrd, lin, imgcrd)
+
+const double pixcrd[];
+struct linprm *lin;
+double imgcrd[];
+
+{
+   int i, ij, j, n;
+   double temp;
+
+   n = lin->naxis;
+
+   if (lin->flag != LINSET) {
+      if (linset(lin)) return 1;
+   }
+
+   for (i = 0; i < n; i++) {
+      imgcrd[i] = 0.0;
+   }
+
+   for (j = 0; j < n; j++) {
+      temp = pixcrd[j] - lin->crpix[j];
+      for (i = 0, ij = j; i < n; i++, ij+=n) {
+         imgcrd[i] += lin->piximg[ij] * temp;
+      }
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int matinv(n, mat, inv)
+
+const int n;
+const double mat[];
+double inv[];
+
+{
+   register int i, ij, ik, j, k, kj, pj;
+   int    itemp, mem, *mxl, *lxm, pivot;
+   double colmax, *lu, *rowmax, dtemp;
+
+
+   /* Allocate memory for internal arrays. */
+   mem = n * sizeof(int);
+   if ((mxl = (int*)malloc(mem)) == (int*)0) return 1;
+   if ((lxm = (int*)malloc(mem)) == (int*)0) {
+      free(mxl);
+      return 1;
+   }
+
+   mem = n * sizeof(double);
+   if ((rowmax = (double*)malloc(mem)) == (double*)0) {
+      free(mxl);
+      free(lxm);
+      return 1;
+   }
+
+   mem *= n;
+   if ((lu = (double*)malloc(mem)) == (double*)0) {
+      free(mxl);
+      free(lxm);
+      free(rowmax);
+      return 1;
+   }
+
+
+   /* Initialize arrays. */
+   for (i = 0, ij = 0; i < n; i++) {
+      /* Vector which records row interchanges. */
+      mxl[i] = i;
+
+      rowmax[i] = 0.0;
+
+      for (j = 0; j < n; j++, ij++) {
+         dtemp = fabs(mat[ij]);
+         if (dtemp > rowmax[i]) rowmax[i] = dtemp;
+
+         lu[ij] = mat[ij];
+      }
+
+      /* A row of zeroes indicates a singular matrix. */
+      if (rowmax[i] == 0.0) {
+         free(mxl);
+         free(lxm);
+         free(rowmax);
+         free(lu);
+         return 2;
+      }
+   }
+
+
+   /* Form the LU triangular factorization using scaled partial pivoting. */
+   for (k = 0; k < n; k++) {
+      /* Decide whether to pivot. */
+      colmax = fabs(lu[k*n+k]) / rowmax[k];
+      pivot = k;
+
+      for (i = k+1; i < n; i++) {
+         ik = i*n + k;
+         dtemp = fabs(lu[ik]) / rowmax[i];
+         if (dtemp > colmax) {
+            colmax = dtemp;
+            pivot = i;
+         }
+      }
+
+      if (pivot > k) {
+         /* We must pivot, interchange the rows of the design matrix. */
+         for (j = 0, pj = pivot*n, kj = k*n; j < n; j++, pj++, kj++) {
+            dtemp = lu[pj];
+            lu[pj] = lu[kj];
+            lu[kj] = dtemp;
+         }
+
+         /* Amend the vector of row maxima. */
+         dtemp = rowmax[pivot];
+         rowmax[pivot] = rowmax[k];
+         rowmax[k] = dtemp;
+
+         /* Record the interchange for later use. */
+         itemp = mxl[pivot];
+         mxl[pivot] = mxl[k];
+         mxl[k] = itemp;
+      }
+
+      /* Gaussian elimination. */
+      for (i = k+1; i < n; i++) {
+         ik = i*n + k;
+
+         /* Nothing to do if lu[ik] is zero. */
+         if (lu[ik] != 0.0) {
+            /* Save the scaling factor. */
+            lu[ik] /= lu[k*n+k];
+
+            /* Subtract rows. */
+            for (j = k+1; j < n; j++) {
+               lu[i*n+j] -= lu[ik]*lu[k*n+j];
+            }
+         }
+      }
+   }
+
+
+   /* mxl[i] records which row of mat corresponds to row i of lu.  */
+   /* lxm[i] records which row of lu  corresponds to row i of mat. */
+   for (i = 0; i < n; i++) {
+      lxm[mxl[i]] = i;
+   }
+
+
+   /* Determine the inverse matrix. */
+   for (i = 0, ij = 0; i < n; i++) {
+      for (j = 0; j < n; j++, ij++) {
+         inv[ij] = 0.0;
+      }
+   }
+
+   for (k = 0; k < n; k++) {
+      inv[lxm[k]*n+k] = 1.0;
+
+      /* Forward substitution. */
+      for (i = lxm[k]+1; i < n; i++) {
+         for (j = lxm[k]; j < i; j++) {
+            inv[i*n+k] -= lu[i*n+j]*inv[j*n+k];
+         }
+      }
+
+      /* Backward substitution. */
+      for (i = n-1; i >= 0; i--) {
+         for (j = i+1; j < n; j++) {
+            inv[i*n+k] -= lu[i*n+j]*inv[j*n+k];
+         }
+         inv[i*n+k] /= lu[i*n+i];
+      }
+   }
+
+   free(mxl);
+   free(lxm);
+   free(rowmax);
+   free(lu);
+
+   return 0;
+}
+/* Dec 20 1999	Doug Mink - Include wcslib.h, which includes lin.h
+ *
+ * Feb 15 2001	Doug Mink - Add comments for WCSLIB 2.6; no code changes
+ * Sep 19 2001	Doug Mink - Add above change to WCSLIB 2.7 code
+ * Nov 20 2001	Doug Mink - Always include stdlib.h
+ *
+ * Jan 15 2002	Bill Joye - Add ifdef so this compiles on MacOS/X
+ *
+ * Nov 18 2003	Doug Mink - Include stdlib.h instead of malloc.h
+ */
diff --git a/Code/src/libwcs/log.c b/Code/src/libwcs/log.c
new file mode 100644
index 0000000000000000000000000000000000000000..ca9b3f9f84c4d481f5c15967df4fb3d6a7ace3dc
--- /dev/null
+++ b/Code/src/libwcs/log.c
@@ -0,0 +1,116 @@
+/*============================================================================
+*
+*   WCSLIB 4.2 - an implementation of the FITS WCS standard.
+*   Copyright (C) 1995-2005, Mark Calabretta
+*
+*   WCSLIB is free software; you can redistribute it and/or modify it under
+*   the terms of the GNU General Public License as published by the Free
+*   Software Foundation; either version 2 of the License, or (at your option)
+*   any later version.
+*
+*   WCSLIB is distributed in the hope that it will be useful, but WITHOUT ANY
+*   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+*   FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+*   details.
+*
+*   You should have received a copy of the GNU General Public License along
+*   with WCSLIB; if not, write to the Free Software Foundation, Inc.,
+*   59 Temple Place, Suite 330, Boston, MA  02111-1307, USA
+*
+*   Correspondence concerning WCSLIB may be directed to:
+*      Internet email: mcalabre@atnf.csiro.au
+*      Postal address: Dr. Mark Calabretta
+*                      Australia Telescope National Facility, CSIRO
+*                      PO Box 76
+*                      Epping NSW 1710
+*                      AUSTRALIA
+*
+*   Author: Mark Calabretta, Australia Telescope National Facility
+*   http://www.atnf.csiro.au/~mcalabre/index.html
+*   $Id: log.c,v 4.2 2005/09/21 13:21:57 cal103 Exp $
+*===========================================================================*/
+
+#include <math.h>
+
+#include "log.h"
+
+/* Map status return value to message. */
+const char *log_errmsg[] = {
+   "Success",
+   "",
+   "Invalid log-coordinate reference value",
+   "One or more of the x coordinates were invalid"};
+
+
+/*--------------------------------------------------------------------------*/
+
+int logx2s(
+   double crval,
+   int nx,
+   int sx,
+   int slogc,
+   const double x[],
+   double logc[],
+   int stat[])
+
+{
+   register int ix;
+   register int *statp;
+   register const double *xp;
+   register double *logcp;
+
+
+   if (crval <= 0.0) {
+      return 2;
+   }
+
+   xp = x;
+   logcp = logc;
+   statp = stat;
+   for (ix = 0; ix < nx; ix++, xp += sx, logcp += slogc) {
+      *logcp = crval * exp((*xp) / crval);
+      *(statp++) = 0;
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int logs2x(
+   double crval,
+   int nlogc,
+   int slogc,
+   int sx,
+   const double logc[],
+   double x[],
+   int stat[])
+
+{
+   int status;
+   register int ilogc;
+   register int *statp;
+   register const double *logcp;
+   register double *xp;
+
+
+   if (crval <= 0.0) {
+      return 2;
+   }
+
+   xp = x;
+   logcp = logc;
+   statp = stat;
+   status = 0;
+   for (ilogc = 0; ilogc < nlogc; ilogc++, logcp += slogc, xp += sx) {
+      if (*logcp > 0.0) {
+         *xp = crval * log(*logcp / crval);
+         *(statp++) = 0;
+      } else {
+         *(statp++) = 1;
+         status = 4;
+      }
+   }
+
+   return status;
+}
diff --git a/Code/src/libwcs/lwcs.h b/Code/src/libwcs/lwcs.h
new file mode 100644
index 0000000000000000000000000000000000000000..f892c167d0041243c4a46c89c14b682d5551de8f
--- /dev/null
+++ b/Code/src/libwcs/lwcs.h
@@ -0,0 +1,71 @@
+/*** File lwcs.h
+ *** April 25, 2006
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1999-2006
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* The following are used in star finding (findstar.c) */
+#define	NSTATPIX	25	/* Stats are computed for +- this many pixels */
+#define	ISTATPIX	10	/* Stats are computed every this many pixels */
+#define	MAXWALK		20	/* Farthest distance to walk from seed */
+#define	BURNEDOUT	0	/* Clamp pixels brighter than this, if > 0 */
+#define NITERATE	5	/* Number of iterations for sigma clipping */
+#define RNOISE	 	50	/* Mean noise is from center +- this many pixels */
+
+#define STARSIGMA	5.0	/* Stars must be this many sigmas above mean */
+#define BORDER		10	/* Ignore this much of the edge */
+#define MAXRAD		20	/* Maximum radius for a star */
+#define MINRAD		1	/* Minimum radius for a star */
+#define MINPEAK		10	/* Minimum peak for a star */
+#define MINSEP		10	/* Minimum separations for stars */
+
+/* The following are used in star matching (matchstar.c) */
+#define	FTOL	0.0000001	/* Fractional change of chisqr() to be done */
+#define NMAX		3000	/* Maximum number of minimization iterations */
+#define	NPEAKS		20	/* Binning peak history */
+#define MINMATCH	50	/* Stars to match to drop out of loop */
+
+/* The following are used in world coordinate system fitting (imsetwcs.c) */
+#define MINSTARS	3	/* Minimum stars from reference and image */
+#define MAXSTARS	50	/* Default max star pairs to try matching */
+#define MAGLIM1		0.0	/* Faintest reference catalog magnitude to use*/
+#define MAGLIM2		0.0	/* Faintest reference catalog magnitude to use*/
+#define PIXDIFF		10	/* +- this many pixels is a match */
+#define PSCALE		0	/* Plate scale in arcsec/pixel */
+				/* (if nonzero, this overrides image header) */
+#define NXYDEC		2	/* Number of decimal places in image coords */
+
+#define MAXCAT		100	/* Max reference stars to keep in scat or imcat */
+
+/* Jun 11 1999	Set BURNEDOUT to 0 so it is ignored
+ *
+ * Feb 15 2000	Drop MAXREF; add MAXCAT for imcat and scat; MAXSTARS from 25 to 50
+ *
+ * Oct 31 2001	Add MINMATCH and set default value to 50
+ *
+ * Mar 30 2006	Add NXYDEC and set default to 2 (constant value was 1)
+ * Apr 25 2006	Add RNOISE and set default to previous constant value of 50
+ */
diff --git a/Code/src/libwcs/matchstar.c b/Code/src/libwcs/matchstar.c
new file mode 100644
index 0000000000000000000000000000000000000000..67b9485eae1ecabc9963758bdd54f7f4f51cc60d
--- /dev/null
+++ b/Code/src/libwcs/matchstar.c
@@ -0,0 +1,2042 @@
+/*** File libwcs/matchstar.c
+ *** July 20, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* StarMatch (ns, sx, sy, ng, gra, gdec, goff, gx, gy, tol, wcs, nfit, debug)
+ *  Find shift, scale, and rotation of image stars to best-match reference stars
+ *
+ * ReadMatch (filename, sx, sy, gra, gdec, debug)
+ *  Read in x, y, RA, and Dec of pre-match stars in image
+ *
+ * WCSMatch (nmatch, sbx, sby, gbra, gbdec, debug)
+ *  Find shift, scale, and rotation of image stars to best-match reference stars
+ *
+ * FitMatch (ns, sx, sy, ng, gra, gdec, gx, gy, tol, wcs, nfit, debug)
+ *  Fit shift, scale, and rotation of image stars to RA/Dec/X/Y matches
+ *
+ * wcs_amoeba (wcs0) Set up temp arrays and call multivariate solver
+ * chisqr (v) Compute the chisqr of the vector v
+ * amoeba (p, y, ndim, ftol, itmax, funk, nfunk)
+ *    Multivariate solver from Numerical Recipes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "wcs.h"
+#include "lwcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+#define NPAR 8
+#define NPAR1 9
+
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+
+static void wcs_amoeba ();
+extern void setnofit();
+extern int getfilelines();
+
+/* Statics used by the chisqr evaluator */
+static double	*sx_p;
+static double	*sy_p;
+static double	*gra_p;
+static double	*gdec_p;
+static double	xref_p, yref_p;
+static double	xrefpix, yrefpix;
+static int	nbin_p;
+static int	nfit;	/* Number of parameters to fit */
+static int	pfit0 = 0;	/* List of parameters to fit, 1 per digit */
+static int	cdfit = 0;	/* 1 if CD matrix has been fit */
+static int	resid_refine = 0;
+static int	minbin=2;	/* Minimum number of coincidence hits needed */
+static int	minmatch0 = MINMATCH;	/* matches to drop out of loop */
+static int	nitmax0 = NMAX;		/* max iterations to stop fit */
+static int	binarray;	/* If =1, bin matched stars */
+static int	vfit[NPAR1]; /* Parameters being fit: index to value vector
+				1= RA,		  2= Dec,
+				3= X plate scale, 4= Y plate scale
+				5= rotation,	  6= second rotation (skew),
+				7= optical axis X,8= optical axis Y */
+
+/* Find shift, scale, and rotation of image stars to best-match reference stars
+ * Get best match by finding which offsets between pairs of s's and g's
+ * work for the most other pairs of s's and g's
+ * N.B. we assume rotation will be "small enough" so that initial guesses can
+ *   be done using just shifts.
+ * Return count of total coincidences found, else 0 if none or -1 if trouble.
+ */
+
+int
+StarMatch (ns,sx,sy,refcat,ng,gnum,gra,gdec,goff,gx,gy,tol,wcs,debug)
+
+int	ns;		/* Number of image stars */
+double	*sx;		/* Image star X coordinates in pixels */
+double	*sy;		/* Image star Y coordinates in pixels */
+int	refcat;		/* Reference Catalog code */
+int	ng;		/* Number of reference stars */
+double	*gnum;		/* Reference star catalog numbers */
+double	*gra;		/* Reference star right ascensions in degrees */
+double	*gdec;		/* Reference star right ascensions in degrees */
+int	*goff;		/* Reference star offscale flags */
+double	*gx;		/* Reference star X coordinates in pixels */
+double	*gy;		/* Reference star Y coordinates in pixels */
+double	tol;		/* +/- this many pixels is a hit */
+struct WorldCoor *wcs;	/* World coordinate structure (fit returned) */
+int	debug;
+
+{
+    double dx, bestdx, dxi;
+    double dy, bestdy, dyi;
+    double dx2, dy2, dxy, dxys, dxs, dys, dxsum, dysum;
+    double *mx, *my, *mxy;
+    int nmatch;
+    int s, g, si, gi, igs;
+    int nbin;
+    double *sbx, *sby;	/* malloced array of s stars in best bin */
+    double *gbra, *gbdec;	/* malloced array of g stars in best bin */
+    int peaks[NPEAKS+1];	/* history of bin counts */
+    int dxpeaks[NPEAKS+1], dypeaks[NPEAKS+1]; /* history of dx/dy at peaks */
+    int npeaks;		/* entries in use in peaks[] */
+    int maxnbin, i, nmatchd;
+    int minmatch;
+    int *is, *ig, *ibs, *ibg;
+    char rastr[32], decstr[32], numstr[32];
+    double xref0, yref0, xinc0, yinc0, rot0, xrefpix0, yrefpix0, cd0[4];
+    int bestbin;	/* Number of coincidences for refit */
+    int pfit;		/* List of parameters to fit, 1 per digit */
+    char vpar[16];	/* List of parameters to fit */
+    char *vi;
+    char vc;
+    int ParamFit();
+    double tol2 = tol * tol;
+    double maxnum;
+    int nnfld = 0;
+
+    /* Set minimum number of matches between image and reference stars to fit */
+    if (ns > ng) {
+	minmatch = 0.5 * ng;
+	if (minmatch > minmatch0)
+	    minmatch = 0.25 * ng;
+	if (minmatch > minmatch0)
+	    minmatch = minmatch0;
+	}
+    else {
+	minmatch = 0.5 * ns;
+	if (minmatch > minmatch0)
+	    minmatch = 0.25 * ns;
+	if (minmatch > minmatch0)
+	    minmatch = minmatch0;
+	}
+
+    /* Set format for numbers, if listed */
+    if (debug) {
+	maxnum = gnum[0];
+	for (gi = 1; gi < ng; gi++) {
+	    if (gnum[gi] > maxnum)
+		maxnum = gnum[gi];
+	    }
+	nnfld = CatNumLen (refcat, maxnum, 0);
+        }
+
+    /* Set maximum number of matches and allocate match indices */
+    if (ng > ns)
+	maxnbin = (int) ((double) ng * 1.25);
+    else
+	maxnbin = (int) ((double) ns * 1.25);
+    if (debug)
+	fprintf (stderr,"Match history: nim=%d nref=%d tol=%3.0f minbin=%d minmatch=%d):\n",
+		 ns, ng, tol, minbin, minmatch);
+
+    /* Allocate arrays in which to save match information */
+    is = (int *) calloc (maxnbin, sizeof(int));
+    ig = (int *) calloc (maxnbin, sizeof(int));
+    ibs = (int *) calloc (maxnbin, sizeof(int));
+    ibg = (int *) calloc (maxnbin, sizeof(int));
+
+    /* Try matching stars using the current WCS first */
+    nmatch = 0;
+    bestdx = 0.0;
+    bestdy = 0.0;
+    dxsum = 0.0;
+    dysum = 0.0;
+    dxs = 0.0;
+    dys = 0.0;
+
+    /* Build 2-d histogram of offset distribution */
+    if (binarray) {
+	int nbx, nnbx, nbx2, nby, nnby, nby2;
+	int idxmax, idymax, idx, idy, tol2;
+	int *obin, *obini, nset, npos;
+
+	/* Loop through image stars */
+	tol2 = 2 * (int) tol;
+	nbx = wcs->nxpix / tol2;
+	nnbx = -nbx;
+	nbx2 = 2 * nbx;
+	nby = wcs->nypix / tol2;
+	nby2 = 2 * nby;
+	nnby = -nby;
+	obin = calloc (nbx2 * nby2, sizeof(int));
+	nset = 0;
+	npos = 0;
+	for (s = 0; s < ns; s++) {
+
+	    /* Loop through reference catalog stars */
+	    for (g = 0; g < ng; g++) {
+		dx  = gx[g] - sx[s];
+		dy = gy[g] - sy[s];
+
+		/* Add to number in this offset bin */
+		idx = (int) (dx / tol2);
+		idy = (int) (dy / tol2);
+		if (idx > nnbx && idx < nbx && idy > nnby && idy < nby) {
+		    obin[((idy+nby) * nbx2) + idx + nbx]++;
+		    nset++;
+		    }
+		npos++;
+		}
+	    }
+
+	/* Find offset bin with maximim number of entries */
+	idxmax = 0;
+	idymax = 0;
+	nmatch = 0;
+	obini = obin;
+	for (idy = nnby; idy < nby; idy++) {
+	    for (idx = nnbx; idx < nbx; idx++) {
+		if (*obini > nmatch) {
+		    idxmax = idx;
+		    idymax = idy;
+		    nmatch = *obini;
+		    if (debug)
+			fprintf (stderr, "%5d at offset %5d, %5d\n", *obini,
+				 idx*tol2, idy*tol2);
+		    }
+		obini++;
+		}
+	    }
+	/* If we found enough matches, we can proceed with this offset */
+	if (nmatch >= minmatch) {
+	    bestdx = tol2 * (double) (idxmax - nbx);
+	    bestdy = tol2 * (double) (idymax - nby);
+	    if (debug)
+		fprintf (stderr, "%d matches found at mean offset %6.3f %6.3f\n",
+			nmatch, bestdx, bestdy);
+	    }
+	free (obin);
+	}
+
+    /* Vote for closest match */
+    else {
+	mx = (double *) calloc (maxnbin, sizeof(double));
+	my = (double *) calloc (maxnbin, sizeof(double));
+	mxy = (double *) calloc (maxnbin, sizeof(double));
+
+	/* Loop through image stars */
+	for (s = 0; s < ns; s++) {
+	    dxys = tol2;
+	    igs = -1;
+
+	    /* Loop through reference catalog stars */
+	    for (g = 0; g < ng; g++) {
+
+		/* Try reference catalog star only if it is on the image */
+		dx = gx[g] - sx[s];
+		dy = gy[g] - sy[s];
+		dx2 = dx * dx;
+		dy2 = dy * dy;
+		dxy = dx2 + dy2;
+
+		/* Check offset less than tolerance or this star's closest match */
+		if (dxy < dxys) {
+		    dxys = dxy;
+		    dxs = dx;
+		    dys = dy;
+		    igs = g;
+		    ibs[nmatch] = s;
+		    ibg[nmatch] = g;
+		    }
+		}
+
+	    /* If a match was found */
+	    if (igs > -1) {
+
+		/* if new match is closer than old match, replace it */
+		if (mxy[igs] > 0.0) {
+		    if (dxy < mxy[igs]) {
+			dxsum = dxsum - mx[igs];
+			dysum = dysum - my[igs];
+			dxsum = dxsum + dxs;
+			dysum = dysum + dys;
+			if (debug) {
+			    CatNum (refcat, nnfld, 0, gnum[ibg[nmatch]], numstr);
+			    ra2str (rastr, 31, gra[ibg[nmatch]], 3);
+			    dec2str (decstr, 31, gdec[ibg[nmatch]], 2);
+			    fprintf (stderr, "*%3d %s %s %s %7.2f %7.2f %7.2f %7.2f %5.2f %5.2f %5.2f\n",
+				 nmatch, numstr, rastr, decstr, 
+				 gx[ibg[nmatch]], gy[ibg[nmatch]], 
+				 sx[ibs[nmatch]], sy[ibs[nmatch]], 
+				 dxs, dys, sqrt (dxys));
+			    }
+			}
+		    }
+	
+		/* If not matched before, use new match */
+		else {
+		    dxsum = dxsum + dxs;
+		    dysum = dysum + dys;
+		    if (debug) {
+			CatNum (refcat, nnfld, 0, gnum[ibg[nmatch]], numstr);
+			ra2str (rastr, 31, gra[ibg[nmatch]], 3);
+			dec2str (decstr, 31, gdec[ibg[nmatch]], 2);
+			fprintf (stderr, " %3d %s %s %s %7.2f %7.2f %7.2f %7.2f %5.2f %5.2f %5.2f\n",
+			     nmatch, numstr, rastr, decstr, 
+			     gx[ibg[nmatch]], gy[ibg[nmatch]], 
+			     sx[ibs[nmatch]], sy[ibs[nmatch]], 
+			     dxs, dys, sqrt (dxys));
+			}
+		    nmatch++;
+		    mx[igs] = dxs;
+		    my[igs] = dys;
+		    mxy[igs] = dxy;
+		    }
+		}
+	    }
+	free (mxy);
+	free (mx);
+	free (my);
+
+	/* If we found enough matches, we can proceed with this offset */
+	if (nmatch >= minmatch) {
+	    bestdx = dxsum / (double) nmatch;
+	    bestdy = dysum / (double) nmatch;
+	    if (debug)
+		fprintf (stderr, "%d matches found at mean offset %6.3f %6.3f\n",
+			nmatch, bestdx, bestdy);
+	    }
+	}
+
+    /* Otherwise, we will look for a coarse alignment assuming no additional rotation.
+     * This will allow us to collect a set of stars that correspond and
+     * establish an initial guess of the solution.
+     */
+    if (nmatch < minmatch) {
+	if (debug)
+	    fprintf (stderr, "%d matches found  less than %d minimum\n",
+			nmatch, minmatch);
+	npeaks = 0;
+	nmatch = 0;
+	for (i = 0; i < NPEAKS; i++) {
+	    peaks[i] = 0;
+	    dxpeaks[i] = 0;
+	    dypeaks[i] = 0;
+	    }
+	bestdx = 0.0;
+	bestdy = 0.0;
+	for (s = 0; s < ns; s++) {
+	    for (g = 0; g < ng; g++) {
+		dx = gx[g] - sx[s];
+		dy = gy[g] - sy[s];
+		nbin = 0;
+		for (gi = 0; gi < ng; gi++) {
+		    for (si = 0; si < ns; si++) {
+			dxi = gx[gi] - sx[si] - dx;
+			if (dxi < 0)
+			    dxi = -dxi;
+			dyi = gy[gi] - sy[si] - dy;
+			if (dyi < 0)
+			    dyi = -dyi;
+			if (dxi <= tol && dyi <= tol) {
+			/* if (debug)
+			    fprintf (stderr,"%d %d %d %d %5.1f %5.1f %5.1f %5.1f\n",
+				     g,s,gi,si,dx,dy,dxi,dyi); */
+			    is[nbin] = si;
+			    ig[nbin] = gi;
+			    nbin++;
+			    }
+			}
+		    }
+		/* if (debug)
+		    fprintf (stderr,"%d %d %d %d %d\n", g,s,gi,si,nbin); */
+		if (nbin > 1 && nbin >= nmatch) {
+		    int i;
+		    nmatch = nbin;
+		    bestdx = (double) dx;
+		    bestdy = (double) dy;
+		    for (i = 0; i < nbin; i++) {
+			ibs[i] = is[i];
+			ibg[i] = ig[i];
+			}
+	
+		    /* keep last NPEAKS nmatchs, dx and dy;
+		     * put newest first in arrays */
+		    if (npeaks > 0) {
+			for (i = npeaks; i > 0; i--) {
+			    peaks[i] = peaks[i-1];
+			    dxpeaks[i] = dxpeaks[i-1];
+			    dypeaks[i] = dypeaks[i-1];
+			    }
+			}
+		    peaks[0] = nmatch;
+		    if (bestdx > 0.0)
+			dxpeaks[0] = (int) (bestdx + 0.5);
+		    else
+			dxpeaks[0] = (int) (bestdx - 0.5);
+		    if (bestdy > 0)
+			dypeaks[0] = (int) (bestdy + 0.5);
+		    else
+			dypeaks[0] = (int) (bestdy - 0.5);
+		    if (npeaks < NPEAKS)
+			npeaks++;
+		    if (debug)
+			fprintf (stderr,"%d: %d/%d matches at image %d cat %d: dx= %d dy= %d\n",
+				npeaks, nmatch, minmatch, s, g, dxpeaks[0], dypeaks[0]);
+		    }
+		if (nmatch > minmatch)
+		    break;
+		}
+	    if (nmatch > minmatch)
+		break;
+	    }
+
+	/* if (debug) {
+	    int i;
+	    for (i = 0; i < npeaks; i++)
+		fprintf (stderr," %d bins at dx=%d dy=%d\n",
+			 peaks[i], dxpeaks[i], dypeaks[i]);
+	    } */
+
+	/* peak is broad */
+	if (npeaks < 2 || peaks[1] == peaks[0]) {
+	    if (debug)
+		fprintf (stderr,"  Broad peak of %d bins at dx=%.0f dy=%.0f\n",
+		         peaks[0], bestdx, bestdy);
+	    }
+	}
+
+    /* too few hits */
+    if (nmatch < minbin)
+	return (nmatch);
+
+    /* Get X and Y coordinates of matches from best binning */
+    nmatchd = nmatch * sizeof (double);
+    if (!(sbx = (double *) malloc (nmatchd)))
+	fprintf (stderr," Could not allocate %d bytes for SBX\n", nmatchd);
+    if (!(sby = (double *) malloc (nmatchd)))
+	fprintf (stderr," Could not allocate %d bytes for SBY\n", nmatchd);
+    if (!(gbra = (double *) malloc (nmatchd)))
+	fprintf (stderr," Could not allocate %d bytes for GBRA\n", nmatchd);
+    if (!(gbdec = (double *) malloc (nmatchd)))
+	fprintf (stderr," Could not allocate %d bytes for GBDEC\n", nmatchd);
+    for (i = 0; i < nmatch; i++) {
+	sbx[i] = sx[ibs[i]];
+	sby[i] = sy[ibs[i]];
+	gbra[i] = gra[ibg[i]];
+	gbdec[i] = gdec[ibg[i]];
+	}
+
+    /* Reset image center based on star matching */
+    wcs->xref = wcs->xref + (bestdx * wcs->xinc);
+    if (wcs->xref < 0.0) wcs->xref = 360.0 + wcs->xref;
+    wcs->yref = wcs->yref + (bestdy * wcs->yinc);
+
+    /* Fit WCS to matched stars */
+
+    /* Provide non-parametric access to the star lists */
+    sx_p = sbx;
+    sy_p = sby;
+    gra_p = gbra;
+    gdec_p = gbdec;
+    xref_p = wcs->xref;
+    yref_p = wcs->yref;
+    xrefpix = wcs->xrefpix;
+    yrefpix = wcs->yrefpix;
+    nbin_p = nmatch;
+
+    /* Number of parameters to fit from command line or number of matches */
+    pfit = ParamFit (nmatch);
+
+    /* Get parameters to fit from digits of pfit */
+    sprintf (vpar, "%d", pfit);
+    nfit = 0;
+    vfit[0] = -1;
+    for (i = 1; i < NPAR1; i++) {
+	vc = i + 48;
+	vi = strchr (vpar, vc);
+	if (vi != NULL) {
+	    vfit[i] = vi - vpar;
+	    nfit++;
+	    }
+	else
+	    vfit[i] = -1;
+	}
+
+    /* Set initial guesses for parameters which are being fit */
+    xref0 = wcs->xref;
+    yref0 = wcs->yref;
+    xinc0 = wcs->xinc;
+    yinc0 = wcs->yinc;
+    rot0 = wcs->rot;
+    xrefpix0 = wcs->xrefpix;
+    yrefpix0 = wcs->yrefpix;
+    cd0[0] = wcs->cd[0];
+    cd0[1] = wcs->cd[1];
+    cd0[2] = wcs->cd[2];
+    cd0[3] = wcs->cd[3];
+    if (vfit[6] > -1)
+	cdfit = 1;
+    else
+	cdfit = 0;
+
+    /* Fit image star coordinates to reference star positions */
+    wcs_amoeba (wcs);
+
+    if (debug) {
+	fprintf (stderr,"\nAmoeba fit:\n");
+	ra2str (rastr, 31, xref0, 3);
+	dec2str (decstr, 31, yref0, 2);
+	fprintf (stderr,"   initial guess:\n");
+	if (vfit[6] > -1)
+	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
+		     rastr, decstr, cd0[0], cd0[1], cd0[2], cd0[3]);
+	else
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		     rastr, decstr, xinc0*3600.0, yinc0*3600.0, rot0);
+	fprintf (stderr,"(%8.2f,%8.2f\n", xrefpix0, yrefpix0);
+
+	ra2str (rastr, 31, wcs->xref, 3);
+	dec2str (decstr, 31, wcs->yref, 2);
+	fprintf (stderr,"\nfirst solution:\n");
+	if (vfit[6] > -1)
+	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
+		     rastr,decstr,wcs->cd[0],wcs->cd[1],wcs->cd[2],wcs->cd[3]);
+	else
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		     rastr,decstr,3600.0*wcs->xinc,3600.0*wcs->yinc,wcs->rot);
+	fprintf (stderr,"(%8.2f,%8.2f)\n", wcs->xrefpix, wcs->yrefpix);
+	}
+
+    /* If we have extra bins, repeat with the best ones */
+    bestbin = nfit + 1;
+    if (resid_refine && nmatch > bestbin) {
+	double *resid = (double *) malloc (nmatch * sizeof(double));
+	double *xe = (double *) malloc (nmatch * sizeof(double));
+	double *ye = (double *) malloc (nmatch * sizeof(double));
+	int i, j;
+	double xmean, ymean, rmean, xsumsq, ysumsq, diff;
+	double mx, my, xsig, ysig, rsig, siglim;
+	char wcstring[64];
+	double xsum = 0.0;
+	double ysum = 0.0;
+	double rsum = 0.0;
+	double dmatch = (double)nmatch;
+	double dmatch1 = (double)(nmatch - 1);
+
+	/* Compute residuals at each star location */
+	for (i = 0; i < nmatch; i++) {
+	    pix2wcs (wcs, sbx[i], sby[i], &mx, &my);
+	    xe[i] = (mx - gbra[i]) * 3600.0;
+	    ye[i] = (my - gbdec[i]) * 3600.0;
+	    resid[i] = sqrt (xe[i]*xe[i] + ye[i]*ye[i]);
+	    if (debug) {
+		pix2wcst (wcs, sbx[i], sby[i], wcstring, 64);
+		fprintf (stderr,"%3d (%8.3f,%8.3f) -> %s %6.3f %6.3f %6.3f\n",
+		    i, sbx[i], sby[i], wcstring, xe[i], ye[i], resid[i]);
+		}
+	    xsum = xsum + xe[i];
+	    ysum = ysum + ye[i];
+	    rsum = rsum + resid[i];
+	    }
+
+	/* Compute means and standard deviations */
+	xmean = xsum / dmatch;
+	ymean = ysum / dmatch;
+	rmean = rsum / dmatch;
+	xsumsq = 0.0;
+	ysumsq = 0.0;
+	for (i = 0; i < nmatch; i++) {
+	    diff = xe[i] - xmean;
+	    xsumsq = xsumsq + (diff * diff);
+	    diff = ye[i] - ymean;
+	    ysumsq = ysumsq + (diff * diff);
+	    }
+	xsig = sqrt (xsumsq / dmatch1);
+	ysig = sqrt (ysumsq / dmatch1);
+	rsig = sqrt ((xsumsq + ysumsq)/ dmatch1);
+	siglim = 2.0 * rsig;
+	if (debug) {
+	    fprintf (stderr,"Mean x: %6.3f/%6.3f y: %6.3f/%6.3f r: %6.3f/%6.3f\n",
+		    xmean, xsig, ymean, ysig, rmean, rsig);
+	    }
+
+	/* sort by increasing total residual */
+	for (i = 0; i < nmatch-1; i++) {
+	    for (j = i+1; j < nmatch; j++) {
+		if (resid[j] < resid[i]) {
+		    double tmp;
+
+		    tmp = sbx[i]; sbx[i] = sbx[j]; sbx[j] = tmp;
+		    tmp = sby[i]; sby[i] = sby[j]; sby[j] = tmp;
+		    tmp = gbra[i]; gbra[i] = gbra[j]; gbra[j] = tmp;
+		    tmp = gbdec[i]; gbdec[i] = gbdec[j]; gbdec[j] = tmp;
+		    tmp = resid[i]; resid[i] = resid[j]; resid[j] = tmp;
+		    }
+		}
+	    }
+
+	/* Cut off points at residual of two sigma */
+	for (i = 0; i < nmatch; i++) {
+	    if (resid[i] > siglim) {
+		if (i > bestbin) bestbin = i - 1;
+		break;
+		}
+	    }
+
+	xref_p = wcs->xref;
+	if (xref_p < 0.0) xref_p = 360.0 + xref_p;
+	yref_p = wcs->yref;
+	xrefpix = wcs->xrefpix;
+	yrefpix = wcs->yrefpix;
+	nbin_p = bestbin;
+	wcs_amoeba (wcs);
+
+	if (debug) {
+	    ra2str (rastr, 31, wcs->xref, 3);
+	    dec2str (decstr, 31, wcs->yref, 2);
+	    fprintf (stderr,"\nresid solution:\n");
+	    fprintf (stderr,"\n%d points < %.3f arcsec residuals refit\n",
+			    bestbin, siglim);
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		 rastr, decstr, 3600.0*wcs->xinc, 3600.0*wcs->yinc, wcs->rot);
+	    fprintf (stderr,"(%8.2f,%8.2f)\n", wcs->xrefpix, wcs->yrefpix);
+	    }
+	free (resid);
+	free (xe);
+	free (ye);
+	}
+
+    free (sbx);
+    free (sby);
+    free (gbra);
+    free (gbdec);
+    free (is);
+    free (ig);
+    free (ibs);
+    free (ibg);
+
+    return (nmatch);
+}
+
+int
+ParamFit (nbin)
+
+int	nbin;	/* Number of point to be fit */
+{
+    int pfit;
+
+    if (pfit0 != 0) {
+	if (pfit0 < 3)
+	    pfit = 12;
+	else if (pfit0 == 3)	/* Fit center and plate scale */
+	    pfit = 123;
+	else if (pfit0 == 4)	/* Fit center, plate scale, rotation */
+	    pfit = 1235;
+	else if (pfit0 == 5)	/* Fit center, x&y plate scales, rotation */
+	    pfit = 12345;
+	else if (pfit0 == 6)	/* Fit center, x&y plate scales, x&y rotations */
+	    pfit = 123456;
+	else if (pfit0 == 7)	/* Fit center, x&y plate scales, rotation, refpix */
+	    pfit = 1234578;
+	else if (pfit0 == 8)	/* Fit center, x&y plate scales, x&y rotation, refpix */
+	    pfit = 12345678;
+	else
+	    pfit = pfit0;
+	}
+    else if (nbin < 4)
+	pfit = 12;
+    else if (nbin < 6)
+	pfit = 123;
+    else
+	pfit = 12345;
+    return (pfit);
+}
+
+
+int
+NParamFit (nbin)
+
+int	nbin;	/* Number of point to be fit */
+{
+    int pfit;
+
+    pfit = ParamFit (nbin);
+    if (pfit < 1)
+	return (0);
+    else if (pfit < 10)
+	return (1);
+    else if (pfit < 100)
+	return (2);
+    else if (pfit < 1000)
+	return (3);
+    else if (pfit < 10000)
+	return (4);
+    else if (pfit < 100000)
+	return (5);
+    else if (pfit < 1000000)
+	return (6);
+    else if (pfit < 10000000)
+	return (7);
+    else
+	return (8);
+}
+
+
+int
+ReadMatch (filename, sx, sy, sra, sdec, debug)
+
+char	*filename;	/* Name of file containing matches */
+double	**sx;		/* Image star X coordinates in pixels */
+double	**sy;		/* Image star Y coordinates in pixels */
+double	**sra;		/* Probable image star right ascensions in degrees */
+double	**sdec;		/* Probable image star declinations in degrees */
+int	debug;		/* Printed debugging information if not zero */
+
+{
+    int nbytes, nread, ir, ntok, itok, iytok;
+    double *tx, *ty, *tra, *tdec, ra, dec, x, y;
+    int ndec;
+    int nmatch = 0;	/* Number of matches read from file */
+    char rastr[32], decstr[32];
+
+    /* If tab file, read from ra, dec, x, y  columns */
+    if (istab (filename)) {
+	}
+
+    /* Otherwise, assume first 4 columns are x, y, ra, dec */
+    else {
+	char line[1025];
+	char *nextline, *lastchar;
+	FILE *fd;
+	struct Tokens tokens;	/* Token structure */
+	char *cwhite;		/* additional whitespace characters */
+	char token[256];
+
+	cwhite = NULL;
+
+	/* Open input file */
+	if (!strcmp (filename, "stdin")) {
+	    fd = stdin;
+	    nread = 1000;
+	    }
+	else {
+	    nread = getfilelines (filename);
+	    if (!(fd = fopen (filename, "r"))) {
+	    fprintf (stderr, "ReadMatch: Match file %s could not be opened\n",
+		     filename);
+		return (0);
+		}
+	    }
+	nbytes = nread * sizeof (double);
+	if (!(tra = (double *) calloc (nread, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for gra\n", nbytes);
+	if (!(tdec = (double *) calloc (nread, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for gdec\n", nbytes);
+	if (!(tx = (double *) calloc (nread, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for sx\n", nbytes);
+	if (!(ty = (double *) calloc (nread, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for sy\n", nbytes);
+	*sra = tra;
+	*sdec = tdec;
+	*sx = tx;
+	*sy = ty;
+
+	nmatch = 0;
+	nextline = line;
+        for (ir = 0; ir < nread; ir++) {
+	    if (fgets (line, 1024, fd) == NULL)
+		break;
+
+	    /* Skip lines with comments */
+	    if (line[0] == '#')
+		continue;
+
+	    /* Drop linefeeds */
+	    lastchar = nextline + strlen(nextline) - 1;
+	    if (*lastchar < 32)
+		*lastchar = (char) 0;
+
+	    /* Read X, Y, RA, and Dec from each line,
+		skipping line if all four are not present and numbers */
+	    ntok = setoken (&tokens, line, cwhite);
+	    if (ntok < 1)
+		break;
+	    if (ntok < 4)
+		continue;
+
+	    /* if (debug)
+		fprintf (stderr, "%d: %s\n", nmatch, line); */
+
+	    /* Image X coordinate or RA */
+	    itok = 1;
+	    if (getoken(&tokens, itok, token, 256)) {
+
+		/* Read RA, Dec, X, Y if first token has : in it */
+		if (strchr (token, ':') != NULL) {
+		    ra = str2ra (token);
+		    iytok = 4;
+		    if (getoken(&tokens, 2, token, 256))
+			dec = str2dec (token);
+		    if (getoken(&tokens, 3, token, 256)) {
+			if (isnum (token))
+			    x = atof (token);
+			else {
+			    iytok = 5;
+			    if (getoken(&tokens, 4, token, 256)) {
+				if (isnum (token))
+				    x = atof (token);
+				else
+				    continue;
+				}
+			    }
+			}
+		    if (getoken(&tokens, iytok, token, 256)) {
+			if (isnum (token))
+			    y = atof (token);
+			else
+			    continue;
+			}
+		    tx[nmatch] = x;
+		    ty[nmatch] = y;
+		    tra[nmatch] = ra;
+		    tdec[nmatch] = dec;
+		    nmatch++;
+		    continue;
+		    }
+		if (isnum (token))
+		    x = atof (token);
+		else
+		    continue;
+		}
+	    else
+		continue;
+
+	    /* Image Y coordinate */
+	    itok++;
+	    if (getoken(&tokens, itok, token, 256)) {
+		if (isnum (token))
+		    y = atof (token);
+		else
+		    continue;
+		}
+	    else
+		continue;
+
+	    /* Right ascension */
+	    itok++;
+	    if (getoken(&tokens, itok, token, 256)) {
+
+		/* If first number is integer, read as h m s */
+		if (isnum (token) == 1) {
+		    ra = atof (token);
+		    itok++;
+		    if (getoken(&tokens, itok, token, 256)) {
+			if (isnum (token) == 2)
+			    ra = ra + (atof (token) / 60.0);
+			else if (isnum (token) == 1) {
+			    ra = ra + (atof (token) / 60.0);
+			    itok++;
+			    if (getoken(&tokens, itok, token, 256)) {
+				if (isnum (token))
+				    ra = ra + (atof (token) / 3600.0);
+				}
+			    }
+			}
+		    ra = ra * 15.0;
+		    }
+		else
+		    ra = str2ra (token);
+		}
+	    else
+		continue;
+
+	    /* Declination */
+	    itok++;
+	    if (getoken(&tokens, itok, token, 256)) {
+		if (isnum (token) == 1) {
+		    dec = atof (token);
+		    itok++;
+		    if (strchr (token, '-') != NULL)
+			ndec = 1;
+		    else
+			ndec = 0;
+		    if (getoken(&tokens, itok, token, 256)) {
+			if (isnum (token) == 2) {
+			    if (ndec)
+				dec = dec - (atof (token) / 60.0);
+			    else
+				dec = dec + (atof (token) / 60.0);
+			    }
+			else if (isnum (token) == 1) {
+			    if (ndec)
+				dec = dec - (atof (token) / 60.0);
+			    else
+				dec = dec + (atof (token) / 60.0);
+			    itok++;
+			    if (getoken(&tokens, itok, token, 256)) {
+				if (isnum (token)) {
+				    if (ndec)
+					dec = dec - (atof (token) / 3600.0);
+				    else
+					dec = dec + (atof (token) / 3600.0);
+				    }
+				}
+			    }
+			}
+		    }
+		else
+		    dec = str2dec (token);
+		}
+	    else
+		continue;
+	    tx[nmatch] = x;
+	    ty[nmatch] = y;
+	    tra[nmatch] = ra;
+	    tdec[nmatch] = dec;
+	    if (debug) {
+		ra2str (rastr, 31, tra[nmatch], 3);
+		dec2str (decstr, 31, tdec[nmatch], 2);
+		fprintf (stderr, "ReadMatch: %d: %8.3f %8.3f %s %s\n", nmatch,
+			 tx[nmatch], ty[nmatch], rastr, decstr);
+		}
+	    nmatch++;
+	    }
+	}
+
+    return (nmatch);
+}
+
+
+/* Find shift, scale, and rotation of image stars to best-match reference stars
+ */
+
+void
+WCSMatch (nmatch, sbx, sby, gbra, gbdec, debug)
+
+int	nmatch;		/* Number of matched stars */
+double	*sbx;		/* Image star X coordinates in pixels */
+double	*sby;		/* Image star Y coordinates in pixels */
+double	*gbra;		/* Reference star right ascensions in degrees */
+double	*gbdec;		/* Reference star right ascensions in degrees */
+int	debug;		/* Printed debugging information if not zero */
+
+{
+    int i;
+    double xdiff, ydiff;
+    int nsc, j, nq[5];
+    double dnsc, tx, ty, tra, tdec, tdiff;
+    double cra, cdec, cx, cy, scale;
+    double rai;
+    double dmatch;
+    double skydiff, imdiff;
+    char rastr[32], decstr[32];
+    extern double getsecpix();
+    extern void getcenter(),getrefpix(),setdcenter(),setrefpix(),setsecpix();
+
+    dmatch = (double) nmatch;
+
+    /* Too few hits */
+    if (nmatch < 2) {
+	if (debug) {
+	    fprintf (stderr, "WCSMatch: %d matched stars < 2\n", nmatch);
+	    }
+	return;
+	}
+
+    /* Check for RA crossing 0:00:00 */
+    for (i = 0; i < 5; i++)
+	nq[i] = 0;
+
+    /* Get number of matches in each quadrant */
+    for (i = 0; i < nmatch; i++) {
+	rai = gbra[i];
+	if (rai >= 0.0 && rai < 90.0)
+	    nq[1] = nq[1] + 1;
+	else if (rai >= 90.0 && rai < 180.0)
+	    nq[2] = nq[2] + 1;
+	else if (rai >= 180.0 && rai < 270.0)
+	    nq[3] = nq[3] + 1;
+	else
+	    nq[4] = nq[4] + 1;
+	}
+    if (debug) {
+	fprintf (stderr,"WCSMatch: %d matched stars: %d %d %d %d per quadrant:\n",
+		 nmatch, nq[1], nq[2], nq[3], nq[4]);
+	}
+
+    /* If matches in quadrants 1 and 4, but not 2 or 3, center RAs on 0:00 */
+    if (nq[1] > 0 && nq[4] > 0 && nq[2] < 1 && nq[3] < 1) {
+	for (i = 0; i < nmatch; i++) {
+	    if (gbra[i] >= 270.0 && gbra[i] < 361.0)
+		gbra[i] = gbra[i] - 360.0;
+	    if (debug) {
+		dec2str (rastr, 31, gbra[i], 3);
+		dec2str (decstr, 31, gbdec[i], 2);
+		fprintf (stderr, "%d: %8.3f %8.3f %s %s\n", i,
+			 sbx[i], sby[i], rastr, decstr);
+		}
+	    }
+	}
+
+    /* Compute plate scale and center of stars */
+    dnsc = 0.0;
+    tx = 0.0;
+    ty = 0.0;
+    tra = 0.0;
+    tdec = 0.0;
+    tdiff = 0.0;
+    for (i = 0; i < nmatch-1; i++) {
+	tx = tx + sbx[i];
+	ty = ty + sby[i];
+	tra = tra + gbra[i];
+	tdec = tdec + gbdec[i];
+	for (j = i+1; j < nmatch; j++) {
+	    skydiff = wcsdist (gbra[i], gbdec[i], gbra[j], gbdec[j]);
+	    xdiff = sbx[j] - sbx[i];
+	    ydiff = sby[j] - sby[i];
+	    imdiff = sqrt ((xdiff * xdiff) + (ydiff * ydiff));
+	    if (imdiff > 0) {
+		scale = skydiff / imdiff;
+		tdiff = tdiff + scale;
+		dnsc = dnsc + 1.0;
+		}
+	    else
+		scale = 0.0;
+	    if (debug) {
+		fprintf (stderr,"%d %d: sky: %8g, image: %8g, %8g deg/pix", 
+			i, j, skydiff, imdiff, scale);
+		fprintf (stderr," = %8g arcsec/pix %10g\n", scale * 3600.0, tdiff);
+		}
+	    }
+	}
+    
+    /* Reset image center based on star matching */
+    cra = -99.0;
+    cdec = -99.0;
+    getcenter (&cra, &cdec);
+    if (cra == -99.0 && cdec == -99.0) {
+	cra = tra / dmatch;
+	cdec = tdec / dmatch;
+	setdcenter (cra, cdec);
+	}
+    cx = -99999.0;
+    cy = -99999.0;
+    getrefpix (&cx, &cy);
+    if (cx == -99999.0) {
+	cx = tx / dmatch;
+	cy = ty / dmatch;
+	setrefpix (cx, cy);
+	}
+    scale = 0.0;
+    scale = getsecpix();
+    if (scale == 0.0) {
+	scale = tdiff / dnsc;
+	setsecpix (3600.0 * scale);
+	}
+    if (debug) {
+	fprintf (stderr, "cra= %8g  cdec = %8g xref=%8g yref=%8g\n",
+		 cra, cdec, cx, cy);
+	fprintf (stderr,"scale = %8g deg/pix = %8g arcsec/pix\n",
+		 scale, scale*3600.0);
+	}
+    return;
+}
+
+
+/* Find shift, scale, and rotation of image stars to best-match reference stars
+ * Return count of total coincidences found, else 0 if none or -1 if trouble.
+ */
+
+int
+FitMatch (nmatch, sbx, sby, gbra, gbdec, wcs, debug)
+
+int	nmatch;		/* Number of matched stars */
+double	*sbx;		/* Image star X coordinates in pixels */
+double	*sby;		/* Image star Y coordinates in pixels */
+double	*gbra;		/* Reference star right ascensions in degrees */
+double	*gbdec;		/* Reference star right ascensions in degrees */
+struct WorldCoor *wcs;	/* World coordinate structure (fit returned) */
+int	debug;		/* Printed debugging information if not zero */
+
+{
+    int i;
+    char rastr[32], decstr[32];
+    double xref0, yref0, xinc0, yinc0, rot0, xrefpix0, yrefpix0, cd0[4];
+    int bestbin;	/* Number of coincidences for refit */
+    int pfit;		/* List of parameters to fit, 1 per digit */
+    char vpar[16];	/* List of parameters to fit */
+    double xdiff, ydiff;
+    char *vi;
+    char vc;
+    int nsc, j;
+/*    double equinox = wcs->equinox; */
+    double tx = 0.0;
+    double ty = 0.0;
+    double tra = 0.0;
+    double tdec = 0.0;
+    double tdiff = 0.0;
+    double scale;
+/*    double dmatch; */
+    double skydiff, imdiff;
+
+/*    dmatch = (double) nmatch; */
+
+    /* Too few hits */
+    if (nmatch < minbin) {
+	if (debug) {
+	    fprintf (stderr,"%d matched stars < %d\n", nmatch, minbin);
+	    }
+	return (nmatch);
+	}
+    else if (debug) {
+	fprintf (stderr,"%d matched stars:\n", nmatch);
+	}
+
+    /* Compute plate scale and center of stars */
+    nsc = 0;
+    for (i = 0; i < nmatch; i++) {
+	tx = tx + sbx[i];
+	ty = ty + sby[i];
+	tra = tra + gbra[i];
+	tdec = tdec + gbdec[i];
+	for (j = i+1; j < nmatch; j++) {
+	    skydiff = wcsdist (gbra[i], gbdec[i], gbra[j], gbdec[j]);
+	    xdiff = sbx[j] - sbx[i];
+	    ydiff = sby[j] - sby[i];
+	    imdiff = sqrt ((xdiff * xdiff) + (ydiff * ydiff));
+	    scale = skydiff / imdiff;
+	    tdiff = tdiff + scale;
+	    nsc++;
+	    if (debug) {
+		fprintf (stderr,"%d %d: sky: %8g, image: %8g, %8g deg/pix", 
+			i, j, skydiff, imdiff, scale);
+		fprintf (stderr," = %8g arcsec/pix\n", scale * 3600.0);
+		}
+	    }
+	}
+    
+    /* Reset image center in WCS data structure based on star matching */
+    /* cra = tra / dmatch;
+    cdec = tdec / dmatch;
+    cx = tx / dmatch;
+    cy = ty / dmatch;
+    scale = tdiff / (double) nsc;
+    if (debug)
+	fprintf (stderr,"scale = %8g deg/pix = %8g arcsec/pix\n", scale, scale*3600.0);
+    wcsreset (wcs, cx, cy, cra, cdec, scale, 0.0, 0.0, NULL, equinox); */
+
+    /* Provide non-parametric access to the star lists */
+    sx_p = sbx;
+    sy_p = sby;
+    gra_p = gbra;
+    gdec_p = gbdec;
+    xref_p = wcs->xref;
+    if (xref_p < 0.0) xref_p = 360.0 + xref_p;
+    yref_p = wcs->yref;
+    xrefpix = wcs->xrefpix;
+    yrefpix = wcs->yrefpix;
+    nbin_p = nmatch;
+
+    /* Number of parameters to fit from command line or number of matches */
+    if (pfit0 != 0) {
+	if (pfit0 < 3)
+	    pfit = 12;
+	else if (pfit0 == 3)	/* Fit center and plate scale */
+	    pfit = 123;
+	else if (pfit0 == 4)	/* Fit center, plate scale, rotation */
+	    pfit = 1235;
+	else if (pfit0 == 5)	/* Fit center, x&y plate scales, rotation */
+	    pfit = 12345;
+	else if (pfit0 == 6)	/* Fit center, x&y plate scales, x&y rotations */
+	    pfit = 123456;
+	else if (pfit0 == 7)	/* Fit center, x&y plate scales, rotation, refpix */
+	    pfit = 1234578;
+	else if (pfit0 == 8)	/* Fit center, x&y plate scales, x&y rotation, refpix */
+	    pfit = 12345678;
+	else
+	    pfit = pfit0;
+	}
+    else if (nmatch < 4)
+	pfit = 12;
+    else if (nmatch < 6)
+	pfit = 123;
+    else
+	pfit = 12345;
+
+    /* Get parameters to fit from digits of pfit */
+    sprintf (vpar, "%d", pfit);
+    nfit = 0;
+    vfit[0] = -1;
+    for (i = 1; i < NPAR1; i++) {
+	vc = i + 48;
+	vi = strchr (vpar, vc);
+	if (vi != NULL) {
+	    vfit[i] = vi - vpar;
+	    nfit++;
+	    }
+	else
+	    vfit[i] = -1;
+	}
+
+    /* Set initial guesses for parameters which are being fit */
+    xref0 = wcs->xref;
+    yref0 = wcs->yref;
+    xinc0 = wcs->xinc;
+    yinc0 = wcs->yinc;
+    rot0 = wcs->rot;
+    xrefpix0 = wcs->xrefpix;
+    yrefpix0 = wcs->yrefpix;
+    cd0[0] = wcs->cd[0];
+    cd0[1] = wcs->cd[1];
+    cd0[2] = wcs->cd[2];
+    cd0[3] = wcs->cd[3];
+    if (vfit[6] > -1)
+	cdfit = 1;
+    else
+	cdfit = 0;
+
+    /* Fit image star coordinates to reference star positions */
+    wcs_amoeba (wcs);
+
+    if (debug) {
+	fprintf (stderr,"\nAmoeba fit:\n");
+	ra2str (rastr, 31, xref0, 3);
+	dec2str (decstr, 31, yref0, 2);
+	fprintf (stderr,"   initial guess:\n");
+	if (vfit[6] > -1)
+	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
+		     rastr, decstr, cd0[0], cd0[1], cd0[2], cd0[3]);
+	else
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		     rastr, decstr, xinc0*3600.0, yinc0*3600.0, rot0);
+	fprintf (stderr,"(%8.2f,%8.2f\n", xrefpix0, yrefpix0);
+
+	ra2str (rastr, 31, wcs->xref, 3);
+	dec2str (decstr, 31, wcs->yref, 2);
+	fprintf (stderr,"\nfirst solution:\n");
+	if (vfit[6] > -1)
+	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
+		     rastr,decstr,wcs->cd[0],wcs->cd[1],wcs->cd[2],wcs->cd[3]);
+	else
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		     rastr,decstr,3600.0*wcs->xinc,3600.0*wcs->yinc,wcs->rot);
+	fprintf (stderr,"(%8.2f,%8.2f)\n", wcs->xrefpix, wcs->yrefpix);
+	}
+
+    /* If we have extra bins, repeat with the best ones */
+    bestbin = nfit + 1;
+    if (resid_refine && nmatch > bestbin) {
+	double *resid = (double *) malloc (nmatch * sizeof(double));
+	double *xe = (double *) malloc (nmatch * sizeof(double));
+	double *ye = (double *) malloc (nmatch * sizeof(double));
+	int i, j;
+	double xmean, ymean, rmean, xsumsq, ysumsq, diff;
+	double mra, mdec, xsig, ysig, rsig, siglim;
+	char wcstring[64];
+	double xsum = 0.0;
+	double ysum = 0.0;
+	double rsum = 0.0;
+	double dmatch = (double)nmatch;
+	double dmatch1 = (double)(nmatch - 1);
+
+	/* Compute residuals at each star location */
+	for (i = 0; i < nmatch; i++) {
+	    pix2wcs (wcs, sbx[i], sby[i], &mra, &mdec);
+	    xe[i] = (mra - gbra[i]) * 3600.0;
+	    ye[i] = (mdec - gbdec[i]) * 3600.0;
+	    resid[i] = sqrt (xe[i]*xe[i] + ye[i]*ye[i]);
+	    if (debug) {
+		pix2wcst (wcs, sbx[i], sby[i], wcstring, 64);
+		fprintf (stderr,"%3d (%8.3f,%8.3f) -> %s %6.3f %6.3f %6.3f\n",
+		    i, sbx[i], sby[i], wcstring, xe[i], ye[i], resid[i]);
+		}
+	    xsum = xsum + xe[i];
+	    ysum = ysum + ye[i];
+	    rsum = rsum + resid[i];
+	    }
+
+	/* Compute means and standard deviations */
+	xmean = xsum / dmatch;
+	ymean = ysum / dmatch;
+	rmean = rsum / dmatch;
+	xsumsq = 0.0;
+	ysumsq = 0.0;
+	for (i = 0; i < nmatch; i++) {
+	    diff = xe[i] - xmean;
+	    xsumsq = xsumsq + (diff * diff);
+	    diff = ye[i] - ymean;
+	    ysumsq = ysumsq + (diff * diff);
+	    }
+	xsig = sqrt (xsumsq / dmatch1);
+	ysig = sqrt (ysumsq / dmatch1);
+	rsig = sqrt ((xsumsq + ysumsq)/ dmatch1);
+	siglim = 2.0 * rsig;
+	if (debug) {
+	    fprintf (stderr,"Mean x: %6.3f/%6.3f y: %6.3f/%6.3f r: %6.3f/%6.3f\n",
+		    xmean, xsig, ymean, ysig, rmean, rsig);
+	    }
+
+	/* sort by increasing total residual */
+	for (i = 0; i < nmatch-1; i++) {
+	    for (j = i+1; j < nmatch; j++) {
+		if (resid[j] < resid[i]) {
+		    double tmp;
+
+		    tmp = sbx[i]; sbx[i] = sbx[j]; sbx[j] = tmp;
+		    tmp = sby[i]; sby[i] = sby[j]; sby[j] = tmp;
+		    tmp = gbra[i]; gbra[i] = gbra[j]; gbra[j] = tmp;
+		    tmp = gbdec[i]; gbdec[i] = gbdec[j]; gbdec[j] = tmp;
+		    tmp = resid[i]; resid[i] = resid[j]; resid[j] = tmp;
+		    }
+		}
+	    }
+
+	/* Cut off points at residual of two sigma */
+	for (i = 0; i < nmatch; i++) {
+	    if (resid[i] > siglim) {
+		if (i > bestbin) bestbin = i - 1;
+		break;
+		}
+	    }
+
+	xref_p = wcs->xref;
+	if (xref_p < 0.0) xref_p = 360.0 + xref_p;
+	yref_p = wcs->yref;
+	xrefpix = wcs->xrefpix;
+	yrefpix = wcs->yrefpix;
+	nbin_p = bestbin;
+	wcs_amoeba (wcs);
+
+	if (debug) {
+	    ra2str (rastr, 31, wcs->xref, 3);
+	    dec2str (decstr, 31, wcs->yref, 2);
+	    fprintf (stderr,"\nresid solution:\n");
+	    fprintf (stderr,"\n%d points < %.3f arcsec residuals refit\n",
+			    bestbin, siglim);
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		 rastr, decstr, 3600.0*wcs->xinc, 3600.0*wcs->yinc, wcs->rot);
+	    fprintf (stderr,"(%8.2f,%8.2f)\n", wcs->xrefpix, wcs->yrefpix);
+	    }
+	free (resid);
+	free (xe);
+	free (ye);
+	}
+
+    return (nmatch);
+}
+
+struct WorldCoor *wcsf;
+
+static double wcs_chisqr ();
+
+/* From Numerical Recipes */
+void amoeba();
+static double amotry();
+
+
+/* Set up the necessary temp arrays and call the amoeba() multivariate solver */
+
+static void
+wcs_amoeba (wcs0)
+
+struct WorldCoor *wcs0;
+
+{
+    double *p[NPAR1];				  /* used as p[NPAR1][NPAR] */
+    double vguess[NPAR], vp[NPAR], vdiff[NPAR];
+    double p0[NPAR], p1[NPAR], p2[NPAR], p3[NPAR], p4[NPAR],
+	   p5[NPAR], p6[NPAR], p7[NPAR], p8[NPAR]; /* used as px[0..NPAR-1] */
+    double y[NPAR1];				  /* used as y[1..NPAR] */
+    double xinc1, yinc1, xrefpix1, yrefpix1, rot, cd[4];
+    double sumx, sumy, sumr;
+    int iter;
+    int i, j;
+    int nfit1;
+    char rastr[32],decstr[32];
+    int nitmax;
+
+    nitmax = nitmax0;
+    if (nfit > NPAR)
+	nfit = NPAR;
+    nfit1 = nfit + 1;
+    wcsf = wcs0;
+
+/* Initialize guess and difference vectors to zero */
+    for (i = 0; i < NPAR; i++) {
+	vguess[i] = 0.0;
+	vdiff[i] = 0.0;
+	}
+
+    /* Optical axis center (RA and Dec degrees) */
+    if (vfit[1] > -1) {
+	vguess[vfit[1]] = 0.0;
+	vdiff[vfit[1]] = 5.0 * wcsf->xinc;
+	}
+    if (vfit[2] > -1) {
+	vguess[vfit[2]] = 0.0;
+	vdiff[vfit[2]] = 5.0 * wcsf->yinc;
+	}
+    /* Second rotation about optical axis (degrees) -> CD matrix */
+    if (vfit[6] > -1) {
+	wcsf->rotmat = 1;
+	vguess[vfit[3]] = wcsf->cd[0];
+	vdiff[vfit[3]] = wcsf->xinc * 0.03;
+	vguess[vfit[4]] = wcsf->cd[1];
+	vdiff[vfit[4]] = wcsf->yinc * 0.03;
+	vguess[vfit[5]] = wcsf->cd[2];
+	vdiff[vfit[5]] = wcsf->xinc * 0.03;
+	vguess[vfit[6]] = wcsf->cd[3];
+	vdiff[vfit[6]] = wcsf->yinc * 0.03;
+	}
+
+    else {
+
+    /* Plate scale at optical axis right ascension or both (degrees/pixel) */
+	if (vfit[3] > -1) {
+	    vguess[vfit[3]] = wcsf->xinc;
+	    vdiff[vfit[3]] = wcsf->xinc * 0.03;
+	    }
+
+    /* Plate scale in declination at optical axis (degrees/pixel) */
+	if (vfit[4] > -1) {
+	    vguess[vfit[4]] = wcsf->yinc;
+	    vdiff[vfit[4]] = wcsf->yinc * 0.03;
+	    }
+
+    /* Rotation about optical axis in degrees */
+	if (vfit[5] > -1) {
+	    vguess[vfit[5]] = wcsf->rot;
+	    vdiff[vfit[5]] = 0.5;
+	    }
+	}
+
+/* Reference pixel (optical axis) */
+    if (vfit[7] > -1) {
+	vguess[vfit[7]] = 0.0;
+	vdiff[vfit[7]] = 10.0;
+	}
+    if (vfit[8] > -1) {
+	vguess[vfit[8]] = 0.0;
+	vdiff[vfit[8]] = 10.0;
+	}
+
+/* Set up matrix of nfit+1 initial guesses.
+ * The supplied guess, plus one for each parameter altered by a small amount
+ */
+    p[0] = p0;
+    if (nfit > 0) p[1] = p1;
+    if (nfit > 1) p[2] = p2;
+    if (nfit > 2) p[3] = p3;
+    if (nfit > 3) p[4] = p4;
+    if (nfit > 4) p[5] = p5;
+    if (nfit > 5) p[6] = p6;
+    if (nfit > 6) p[7] = p7;
+    if (nfit > 7) p[8] = p8;
+    for (i = 0; i <= nfit; i++) {
+	for (j = 0; j < nfit; j++)
+	    p[i][j] = vguess[j];
+	if (i > 0 && i <= nfit)
+	    p[i][i-1] = vguess[i-1] + vdiff[i-1];
+	 y[i] = wcs_chisqr (p[i], -i);
+	}
+
+#define	PDUMP
+#ifdef	PDUMP
+    fprintf (stderr,"Before:\n");
+    for (i = 0; i < nfit1; i++) {
+	if (vfit[1] > -1)
+	    ra2str (rastr, 31, p[i][vfit[1]] + xref_p, 3);
+	else
+	    ra2str (rastr, 31, wcsf->xref, 3);
+	if (vfit[2] > -1)
+	    dec2str (decstr, 16, p[i][vfit[2]]+yref_p, 2);
+	else
+	    dec2str (decstr, 16, wcsf->yref, 2);
+	if (vfit[6] > -1) {
+	    cd[0] = p[i][vfit[3]];
+	    cd[1] = p[i][vfit[4]];
+	    cd[2] = p[i][vfit[5]];
+	    cd[3] = p[i][vfit[6]];
+	    fprintf (stderr,"%d: %s %s CD: %7.5f,%7.5f,%7.5f,%7.5f ",
+		    i, rastr, decstr, cd[0],cd[1],cd[2],cd[3]);
+	    }
+	else {
+	    if (vfit[3] > -1)
+		xinc1 = p[i][vfit[3]];
+	    else
+		xinc1 = wcsf->xinc;
+	    if (vfit[4] > -1)
+		yinc1 = p[i][vfit[4]];
+	    else if (vfit[3] > -1) {
+		if (xinc1 < 0)
+		    yinc1 = -xinc1;
+		else
+		    yinc1 = xinc1;
+		}
+	    else
+		yinc1 = wcsf->yinc;
+	    if (vfit[5] > -1)
+		rot = p[i][vfit[5]];
+	    else
+		rot = wcsf->rot;
+	    fprintf (stderr,"%d: %s %s del=%6.4f,%6.4f rot=%5.3f ",
+		    i, rastr, decstr, 3600.0*xinc1, 3600.0*yinc1, rot);
+	    }
+
+	if (vfit[7] > -1)
+	    xrefpix1 = xrefpix + p[i][vfit[7]];
+	else
+	    xrefpix1 = wcsf->xrefpix;
+	if (vfit[8] > -1)
+	    yrefpix1 = yrefpix + p[i][vfit[8]];
+	else
+	    yrefpix1 = wcsf->yrefpix;
+	fprintf (stderr,"(%8.2f,%8.2f) y=%g\n", xrefpix1, yrefpix1, y[i]);
+	}
+#endif
+
+    amoeba (p, y, nfit, FTOL, nitmax, wcs_chisqr, &iter);
+
+#define	PDUMP
+#ifdef	PDUMP
+    fprintf (stderr,"\nAfter:\n");
+    for (i = 0; i < nfit1; i++) {
+	if (vfit[1] > -1)
+	    ra2str (rastr, 31, p[i][vfit[1]] + xref_p, 3);
+	else
+	    ra2str (rastr, 31, wcsf->xref, 3);
+	if (vfit[2] > -1)
+	    dec2str (decstr, 31, p[i][vfit[2]]+yref_p, 2);
+	else
+	    dec2str (decstr, 31, wcsf->yref, 2);
+	if (vfit[6] > -1) {
+	    cd[0] = p[i][vfit[3]];
+	    cd[1] = p[i][vfit[4]];
+	    cd[2] = p[i][vfit[5]];
+	    cd[3] = p[i][vfit[6]];
+	    fprintf (stderr,"%d: %s %s CD: %7.5f,%7.5f,%7.5f,%7.5f ",
+		    i, rastr, decstr, cd[0],cd[1],cd[2],cd[3]);
+	    }
+	else {
+	    if (vfit[3] > -1)
+		xinc1 = p[i][vfit[3]];
+	    else
+		xinc1 = wcsf->xinc;
+	    if (vfit[4] > -1)
+		yinc1 = p[i][vfit[4]];
+	    else if (vfit[3] > -1) {
+		if (xinc1 < 0)
+		    yinc1 = -xinc1;
+		else
+		    yinc1 = xinc1;
+		}
+	    else
+		yinc1 = wcsf->yinc;
+	    if (vfit[5] > -1)
+		rot = p[i][vfit[5]];
+	    else
+		rot = wcsf->rot;
+	    fprintf (stderr,"%d: %s %s del=%6.4f,%6.4f rot=%5.3f ",
+		    i,rastr,decstr, 3600.0*xinc1, 3600.0*yinc1, rot);
+	    }
+	if (vfit[7] > -1)
+	    xrefpix1 = xrefpix + p[i][vfit[7]];
+	else
+	    xrefpix1 = wcsf->xrefpix;
+	if (vfit[8] > -1)
+	    yrefpix1 = yrefpix + p[i][vfit[8]];
+	else
+	    yrefpix1 = wcsf->yrefpix;
+	fprintf (stderr,"(%8.2f,%8.2f) y=%g\n", xrefpix1, yrefpix1, y[i]);
+	}
+#endif
+
+    /* On return, all entries in p[1..NPAR] are within FTOL;
+     * Return the average, though you could just pick the first one
+     */
+    for (j = 0; j < nfit; j++) {
+	double sum = 0.0;
+	for (i = 0; i < nfit1; i++)
+	    sum += p[i][j];
+	vp[j] = sum / (double)nfit1;
+	}
+    if (vfit[1] > -1) {
+	wcsf->xref = xref_p + vp[vfit[1]];
+	if (wcsf->xref < 0.0) wcsf->xref = 360.0 + wcsf->xref;
+	}
+    if (vfit[2] > -1)
+	wcsf->yref = yref_p + vp[vfit[2]];
+    if (vfit[6] > -1) {
+	wcsf->cd[0] = vp[vfit[3]];
+	wcsf->cd[1] = vp[vfit[4]];
+	wcsf->cd[2] = vp[vfit[5]];
+	wcsf->cd[3] = vp[vfit[6]];
+	}
+    else {
+	if (vfit[3] > -1)
+	    wcsf->xinc = vp[vfit[3]];
+	if (vfit[4] > -1)
+	    wcsf->yinc = vp[vfit[4]];
+	else if (vfit[3] > -1) {
+	    if (wcsf->xinc < 0)
+		wcsf->yinc = -wcsf->xinc;
+	    else
+		wcsf->yinc = wcsf->xinc;
+	    }
+	if (vfit[5] > -1)
+	    wcsf->rot = vp[vfit[5]];
+	}
+    if (vfit[7] > -1)
+	wcsf->xrefpix = xrefpix + vp[vfit[7]];
+    if (vfit[8] > -1)
+	wcsf->yrefpix = yrefpix + vp[vfit[8]];
+
+#define RESIDDUMP
+#ifdef RESIDDUMP
+    ra2str (rastr, 31, wcsf->xref, 3);
+    dec2str (decstr, 31, wcsf->yref, 2);
+
+    if (vfit[6] > -1)
+	fprintf (stderr,"iter=%d\n cra= %s cdec= %s CD=%9.7f,%9.7f,%9.7f,%9.7f ", iter,
+		rastr, decstr, wcsf->cd[0], wcsf->cd[1], wcsf->cd[2],
+		wcsf->cd[3]);
+    else
+	fprintf (stderr,"iter=%d\n cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ", iter,
+		rastr, decstr, wcsf->xinc*3600.0, wcsf->yinc*3600.0, wcsf->rot);
+    fprintf (stderr,"(%8.2f,%8.2f)\n", wcsf->xrefpix, wcsf->yrefpix);
+    sumx = 0.0;
+    sumy = 0.0;
+    sumr = 0.0;
+    for (i = 0; i < nbin_p; i++) {
+	double mra, mdec, ex, ey, er;
+	char rastr[32], decstr[32];
+
+	pix2wcs (wcsf, sx_p[i], sy_p[i], &mra, &mdec);
+	ex = 3600.0 * (mra - gra_p[i]);
+	ey = 3600.0 * (mdec - gdec_p[i]);
+	er = sqrt (ex * ex + ey * ey);
+	sumx = sumx + ex;
+	sumy = sumy + ey;
+	sumr = sumr + er;
+
+	ra2str (rastr, 31, gra_p[i], 3);
+	dec2str (decstr, 31, gdec_p[i], 2);
+	fprintf (stderr,"%2d: c: %s %s ", i+1, rastr, decstr);
+	ra2str (rastr, 31, mra, 3);
+	dec2str (decstr, 31, mdec, 2);
+	fprintf (stderr, "i: %s %s %6.3f %6.3f %6.3f\n",
+		rastr, decstr, 3600.0*ex, 3600.0*ey,
+		3600.0*sqrt(ex*ex + ey*ey));
+	}
+    sumx = sumx / (double)nbin_p;
+    sumy = sumy / (double)nbin_p;
+    sumr = sumr / (double)nbin_p;
+    fprintf (stderr,"mean dra: %6.3f, ddec: %6.3f, dr = %6.3f\n", sumx, sumy, sumr);
+#endif
+}
+
+
+/* Compute the chisqr of the vector v, where
+ * v[0]=cra, v[1]=cdec, v[2]=ra deg/pix, v[3]=dec deg/pix,
+ * v[4]=rotation, v[5]=2nd rotation->CD matrix, v[6]=ref x, and v[7] = ref y
+ * chisqr is in arcsec^2
+ */
+
+static double
+wcs_chisqr (v, iter)
+
+double	*v;	/* Vector of parameter values */
+int	iter;	/* Number of iterations */
+
+{
+    double chsq;
+    char rastr[32],decstr[32];
+    double xmp, ymp, dx, dy, cd[4], *cdx;
+    double crval1, crval2, cdelt1, cdelt2, crota, crpix1, crpix2;
+    int i, offscale;
+
+    /* Set WCS parameters from fit parameter vector */
+
+    /* Sky coordinates at optical axis (degrees) */
+    if (vfit[1] > -1)
+	crval1 = xref_p + v[vfit[1]];
+    else
+	crval1 = wcsf->xref;
+    if (vfit[2] > -1)
+	crval2 = yref_p + v[vfit[2]];
+    else
+	crval2 = wcsf->yref;
+
+    /* CD matrix */
+    if (vfit[6] > -1) {
+	cdelt1 = 0.0;
+	cdelt2 = 0.0;
+	crota = 0.0;
+	cd[0] = v[vfit[3]];
+	cd[1] = v[vfit[4]];
+	cd[2] = v[vfit[5]];
+	cd[3] = v[vfit[6]];
+	cdx = cd;
+	}
+
+    else {
+	/* Plate scale (degrees/pixel) */
+	if (vfit[3] > -1)
+	    cdelt1 = v[vfit[3]];
+	else
+	    cdelt1 = wcsf->xinc;
+	if (vfit[4] > -1)
+	    cdelt2 = v[vfit[4]];
+	else if (vfit[3] > -1) {
+	    if (cdelt1 < 0)
+		cdelt2 = -cdelt1;
+	    else
+		cdelt2 = cdelt1;
+	    }
+	else
+	    cdelt2 = wcsf->yinc;
+
+	/* Rotation angle (degrees) */
+	if (vfit[5] > -1)
+	    crota = v[vfit[5]];
+	else
+	    crota = wcsf->rot;
+	cdx = NULL;
+	}
+
+    /* Optical axis pixel coordinates */
+    if (vfit[7] > -1)
+	crpix1 = xrefpix + v[vfit[7]];
+    else
+	crpix1 = wcsf->xrefpix;
+    if (vfit[8] > -1)
+	crpix2 = yrefpix + v[vfit[8]];
+    else
+	crpix2 = wcsf->yrefpix;
+    if (wcsreset (wcsf,crpix1,crpix2,crval1,crval2,cdelt1,cdelt2,crota,cdx)) {
+	fprintf (stderr,"CHISQR: Cannot reset WCS!\n");
+	return (0.0);
+	}
+
+    /* Compute sum of squared residuals for these parameters */
+    chsq = 0.0;
+    for (i = 0; i < nbin_p; i++) {
+	wcs2pix (wcsf, gra_p[i], gdec_p[i], &xmp, &ymp, &offscale);
+	/* if (!offscale) { */
+	    dx = xmp - sx_p[i];
+	    dy = ymp - sy_p[i];
+	    chsq += dx*dx + dy*dy;
+	    /* } */
+	}
+
+#define TRACE_CHSQR
+#ifdef TRACE_CHSQR
+    ra2str (rastr, 31, wcsf->xref, 3);
+    dec2str (decstr, 31, wcsf->yref, 2);
+    if (vfit[6] > -1)
+	fprintf (stderr,"%4d: %s %s CD: %9.7f,%9.7f,%9.7f,%9.7f ",
+		iter, rastr, decstr, wcsf->cd[0],wcsf->cd[1],wcsf->cd[2],
+		wcsf->cd[3]);
+    else
+	fprintf (stderr,"%4d: %s %s %9.7f,%9.7f %8.5f ",
+		iter, rastr, decstr, wcsf->xinc*3600.0, wcsf->yinc*3600.0,
+		wcsf->rot);
+    fprintf (stderr,"(%8.2f,%8.2f) -> %f\r",
+	     wcsf->xrefpix, wcsf->yrefpix, chsq);
+#endif
+    return (chsq);
+}
+
+/* The following subroutines are based on those in Numerical Recipes in C */
+
+/* amoeba.c */
+
+#define ALPHA 1.0
+#define BETA 0.5
+#define GAMMA 2.0
+
+void
+amoeba (p, y, ndim, ftol, itmax, funk, nfunk)
+
+double	**p;
+double	y[];
+double	ftol;
+int	itmax;
+double	(*funk)();
+int	ndim;
+int	*nfunk;
+
+{
+int i,j,ilo,ihi,inhi,ndim1=ndim+1;
+double ytry,ysave,sum,rtol,*psum;
+
+    psum = (double *) malloc ((unsigned)ndim * sizeof(double));
+    *nfunk = 0;
+    for (j=0; j<ndim; j++) {
+	for (i=0,sum=0.0; i<ndim1; i++)
+	    sum += p[i][j]; psum[j]=sum;
+	}
+    for (;;) {
+	ilo=1;
+	if (y[0] > y[1]) {
+	    inhi = 1;
+	    ihi = 0;
+	    }
+	else {
+	    inhi = 0;
+	    ihi = 1;
+	    }
+	for (i = 0; i < ndim1; i++) {
+	    if (y[i] < y[ilo])
+		ilo=i;
+	    if (y[i] > y[ihi]) {
+		inhi=ihi;
+		ihi=i;
+		}
+	    else if (y[i] > y[inhi])
+		if (i != ihi)
+		    inhi=i;
+	    }
+	rtol = 2.0 * fabs(y[ihi]-y[ilo]) / (fabs(y[ihi]) + fabs(y[ilo]));
+	if (rtol < ftol)
+	    break;
+	if (*nfunk >= itmax) {
+	    fprintf (stderr,"Too many iterations in amoeba fit %d > %d",*nfunk,itmax);
+	    return;
+	    }
+	ytry = amotry (p, y, psum, ndim, funk, ihi, nfunk, -ALPHA);
+	if (ytry <= y[ilo])
+	    ytry = amotry (p, y, psum, ndim, funk, ihi, nfunk, GAMMA);
+	else if (ytry >= y[inhi]) {
+	    ysave = y[ihi];
+	    ytry = amotry (p,y,psum,ndim,funk,ihi,nfunk,BETA);
+	    if (ytry >= ysave) {
+		for (i = 0; i < ndim1; i++) {
+		    if (i != ilo) {
+			for (j = 0; j < ndim; j++) {
+			    psum[j] = 0.5 * (p[i][j] + p[ilo][j]);
+			    p[i][j] = psum[j];
+			    }
+			y[i]=(*funk)(psum, *nfunk);
+			}
+		    }
+		*nfunk += ndim;
+		for (j=0; j<ndim; j++) {
+		    for (i=0,sum=0.0; i<ndim1; i++)
+			sum += p[i][j]; psum[j]=sum;
+		    }
+		}
+	    }
+	}
+    free (psum);
+    return;
+}
+
+
+static double
+amotry (p, y, psum, ndim, funk, ihi, nfunk, fac)
+
+double	**p;
+double	*y;
+double	*psum;
+double	(*funk)();
+double	fac;
+int	ndim;
+int	ihi;
+int	*nfunk;
+
+{
+    int j;
+    double fac1,fac2,ytry,*ptry;
+
+    ptry = (double *) malloc ((unsigned) ndim * sizeof(double));
+    fac1 = (1.0 - fac) / ndim;
+    fac2 = fac1 - fac;
+    for (j = 0; j < ndim; j++)
+	ptry[j] = psum[j] * fac1 - p[ihi][j] * fac2;
+    ytry = (*funk)(ptry, *nfunk);
+    ++(*nfunk);
+    if (ytry < y[ihi]) {
+	y[ihi] = ytry;
+	for (j = 0; j < ndim; j++) {
+    	    psum[j] +=  ptry[j] - p[ihi][j];
+    	    p[ihi][j] = ptry[j];
+	    }
+	}
+    free (ptry);
+    return ytry;
+}
+
+void
+setbin (binflag)
+int binflag;
+{ binarray = binflag; return;}
+
+void
+setresid_refine (refine)
+int refine;
+{ resid_refine = refine; return; }
+
+int
+getresid_refine ()
+{ return (resid_refine); }
+
+void
+setnfit (nfit)
+int nfit;
+{
+    if (nfit == 0)
+	setnofit();
+    else if (nfit < 0) {
+	pfit0 = -nfit;
+	resid_refine = 1;
+	}
+    else {
+	pfit0 = nfit;
+	resid_refine = 0;
+	}
+    return;
+}
+
+int
+getnfit ()
+{ return (pfit0); }
+
+int
+iscdfit ()
+{ return (cdfit); }
+
+void
+setminmatch (minmatch)
+int minmatch;
+{ minmatch0 = minmatch; return; }
+
+void
+setminbin (minbin1)
+int minbin1;
+{ minbin = minbin1; return; }
+
+void
+setnitmax (nitmax)
+int nitmax;
+{ nitmax0 = nitmax; return; }
+
+/* Aug  6 1996	New subroutine
+ * Sep  1 1996	Move constants to lwcs.h
+ * Sep  3 1996	Use offscale pixels for chi^2 computation
+ * Sep  3 1996	Overprint chi^2 in verbose mode
+ * Oct 15 1996	Fix am* subroutine declarations
+ * Nov 19 1996	Fix bug regarding rotation
+ *
+ * Jul 21 1997	Add reference pixel position fitting
+ * Aug  4 1997	Increase maximum iterations from 750 to 1000 in lwcs.h
+ * Aug 28 1997	Fix VGUESS dimension bug
+ * Sep  9 1997	Print RA and Dec offsets in residual listing
+ * Sep  9 1997	Turn on resid_refinement if number of parameters to fit negated
+ * Sep  9 1997	Fit separate horizontal and vertical plate scales if nfit=5
+ * Sep  9 1997	Fix bugs associated with fitting optical axis
+ * Sep 12 1997	Add chip rotation instead of second plate scale
+ * Oct  2 1997	Keep second plate scale AND chip rotation
+ * Oct 16 1997	Try to deal with reference pixel position correctly
+ * Nov  5 1997	Select parameters one at a time, in any order
+ * Nov 12 1997	Add PFIT=3 to fit center and plate scale only
+ * Dec 15 1997	Fix minor bugs after lint
+ *
+ * Jan 26 1998	Remove chip rotation code
+ * Jan 29 1998	Streamline initialization code
+ * Feb 19 1998	Fix bug in initialization code
+ * Mar  3 1998	Fix residual-refining code
+ * Mar 20 1998	Add option to fit CD matrix
+ * Mar 25 1998	Make amoeba() externally callable
+ * Mar 26 1998	Return instead of crashing when too many iterations
+ * Apr 21 1998	Drop out of loop if more than half of stars are matched
+ * Apr 27 1998	Fix bug handling nfit=8
+ * Jun 24 1998	Fix bug summing unitialized values for mean after fit
+ * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
+ * Oct  8 1998	Initialize bestdx and bestdy to zero
+ * Dec  8 1998	Fix declaration of amotry()
+ *
+ * Apr 21 1999	Add subroutines to set and retrieve resid_refine independently
+ * Jul 21 1999	Add FitMatch() to fit WCS to already-matched stars
+ * Sep  8 1999	Fix bug found by Jean-Baptiste Marquette
+ * Oct  1 1999	Add ReadMatch() to read a set of matches from a file
+ * Oct 20 1999	Include wcscat.h
+ *
+ * Feb 15 2000	Add iscdfit() to return whether CD matrix is being fit
+ * Mar 10 2000	Add debug statement to list max matches as they are found
+ * Mar 10 2000	Change loop order to image stars first
+ * Dec 18 2000	Write half of ReadMatch() to deal with ASCII files
+ *
+ * Jan  2 2001	Modify ReadMatch() to read hh mm ss dd mm ss, too
+ * Jan  9 2001	Work on FitMatch()
+ * Jan 11 2001	All diagnostic printing goes to stderr
+ * Feb 28 2001	Ignore coordinate system if present after match file coordinates
+ * Jun 18 2001	Add maximum length of returned string to getoken()
+ * Aug  2 2001	Separate parameter listing and counting into subroutines
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ * Sep 24 2001	Ease match numeric criterium if half num is > 40
+ * Oct 15 2001	Simplify error message
+ * Oct 16 2001	Read minimum match to drop out of loop from lwcs.h
+ * Oct 31 2001	Simplify innermost loop to try for more speed
+ * Nov  1 2001	Add goff to StarMatch() arguments
+ * Nov  5 2001	Use current WCS with no offset before trying offset matching
+ * Nov  6 2001	Add setnitmax() to set maximum number of amoeba iterations
+ * Nov  7 2001	Add setminbin to set minimum number of matches for fit
+ * Nov 16 2001	Allocate slightly more than maxbin to handle dense fields
+ *
+ * Jul 31 2002	Add getnfit() to return current number of parameters being fit
+ * Aug 30 2002	Fix WCSMatch() to set scale in arcsec, not degrees
+ *
+ * Jan 30 2003	Remove uninitialized variable in WCSMatch()
+ * Mar 13 2003	Do not include malloc.h on Apples and Convexes
+ * Apr  3 2003	Clean up code with lint
+ * Nov 18 2003	Drop include of malloc.h; it is in stdlib.h
+ *
+ * Aug 30 2004	Declare void various external set*() calls
+ *
+ * Jun 19 2006	Initialize unitialized variables dxs and dys
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ * Oct 23 2006	Add bin array option to see if it works better
+ *
+ * Jan  8 2007	Drop unused variables; fix two format disagreements
+ * Jan 11 2007	Include fitsfile.h
+ *
+ * Jul 20 2009	Fixed matched star wrap around RA = 0:00:00
+ * Jul 20 2009	Fixed matched star wrap for bad matches
+ */ 
diff --git a/Code/src/libwcs/matchstar1.c b/Code/src/libwcs/matchstar1.c
new file mode 100644
index 0000000000000000000000000000000000000000..8a92c40ea1116653aa2b590e36911b841ea28516
--- /dev/null
+++ b/Code/src/libwcs/matchstar1.c
@@ -0,0 +1,1861 @@
+/*** File libwcs/matchstar.c
+ *** June 19, 2006
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2006
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* StarMatch (ns, sx, sy, ng, gra, gdec, goff, gx, gy, tol, wcs, nfit, debug)
+ *  Find shift, scale, and rotation of image stars to best-match reference stars
+ *
+ * ReadMatch (filename, sx, sy, gra, gdec, debug)
+ *  Read in x, y, RA, and Dec of pre-match stars in image
+ *
+ * WCSMatch (nmatch, sbx, sby, gbra, gbdec, debug)
+ *  Find shift, scale, and rotation of image stars to best-match reference stars
+ *
+ * FitMatch (ns, sx, sy, ng, gra, gdec, gx, gy, tol, wcs, nfit, debug)
+ *  Fit shift, scale, and rotation of image stars to RA/Dec/X/Y matches
+ *
+ * wcs_amoeba (wcs0) Set up temp arrays and call multivariate solver
+ * chisqr (v) Compute the chisqr of the vector v
+ * amoeba (p, y, ndim, ftol, itmax, funk, nfunk)
+ *    Multivariate solver from Numerical Recipes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "wcs.h"
+#include "lwcs.h"
+#include "wcscat.h"
+
+#define NPAR 8
+#define NPAR1 9
+
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+
+static void wcs_amoeba ();
+extern void setnofit();
+extern int getfilelines();
+
+/* Statics used by the chisqr evaluator */
+static double	*sx_p;
+static double	*sy_p;
+static double	*gra_p;
+static double	*gdec_p;
+static double	xref_p, yref_p;
+static double	xrefpix, yrefpix;
+static int	nbin_p;
+static int	nfit;	/* Number of parameters to fit */
+static int	pfit0 = 0;	/* List of parameters to fit, 1 per digit */
+static int	cdfit = 0;	/* 1 if CD matrix has been fit */
+static int	resid_refine = 0;
+static int	minbin=2;	/* Minimum number of coincidence hits needed */
+static int	minmatch0 = MINMATCH;	/* matches to drop out of loop */
+static int	nitmax0 = NMAX;		/* max iterations to stop fit */
+static int	vfit[NPAR1]; /* Parameters being fit: index to value vector
+				1= RA,		  2= Dec,
+				3= X plate scale, 4= Y plate scale
+				5= rotation,	  6= second rotation (skew),
+				7= optical axis X,8= optical axis Y */
+
+/* Find shift, scale, and rotation of image stars to best-match reference stars
+ * Get best match by finding which offsets between pairs of s's and g's
+ * work for the most other pairs of s's and g's
+ * N.B. we assume rotation will be "small enough" so that initial guesses can
+ *   be done using just shifts.
+ * Return count of total coincidences found, else 0 if none or -1 if trouble.
+ */
+
+int
+StarMatch (ns,sx,sy,refcat,ng,gnum,gra,gdec,goff,gx,gy,tol,wcs,debug)
+
+int	ns;		/* Number of image stars */
+double	*sx;		/* Image star X coordinates in pixels */
+double	*sy;		/* Image star Y coordinates in pixels */
+int	refcat;		/* Reference Catalog code */
+int	ng;		/* Number of reference stars */
+double	*gnum;		/* Reference star catalog numbers */
+double	*gra;		/* Reference star right ascensions in degrees */
+double	*gdec;		/* Reference star right ascensions in degrees */
+int	*goff;		/* Reference star offscale flags */
+double	*gx;		/* Reference star X coordinates in pixels */
+double	*gy;		/* Reference star Y coordinates in pixels */
+double	tol;		/* +/- this many pixels is a hit */
+struct WorldCoor *wcs;	/* World coordinate structure (fit returned) */
+int	debug;
+
+{
+    double dx, bestdx, dxi;
+    double dy, bestdy, dyi;
+    double dx2, dy2, dxy, dxys, dxs, dys, dxsum, dysum;
+    double *mx, *my, *mxy;
+    int nmatch;
+    int s, g, si, gi, igs;
+    int nbin;
+    double *sbx, *sby;	/* malloced array of s stars in best bin */
+    double *gbra, *gbdec;	/* malloced array of g stars in best bin */
+    int peaks[NPEAKS+1];	/* history of bin counts */
+    int dxpeaks[NPEAKS+1], dypeaks[NPEAKS+1]; /* history of dx/dy at peaks */
+    int npeaks;		/* entries in use in peaks[] */
+    int maxnbin, i, nmatchd;
+    int minmatch;
+    int *is, *ig, *ibs, *ibg;
+    char rastr[16], decstr[16], numstr[16];
+    double xref0, yref0, xinc0, yinc0, rot0, xrefpix0, yrefpix0, cd0[4];
+    int bestbin;	/* Number of coincidences for refit */
+    int pfit;		/* List of parameters to fit, 1 per digit */
+    char vpar[16];	/* List of parameters to fit */
+    char *vi;
+    char vc;
+    int ParamFit();
+    double tol2 = tol * tol;
+    double maxnum;
+    int nnfld = 0;
+
+    /* Set minimum number of matches between image and reference stars to fit */
+    if (ns > ng) {
+	minmatch = 0.5 * ng;
+	if (minmatch > minmatch0)
+	    minmatch = 0.25 * ng;
+	if (minmatch > minmatch0)
+	    minmatch = minmatch0;
+	}
+    else {
+	minmatch = 0.5 * ns;
+	if (minmatch > minmatch0)
+	    minmatch = 0.25 * ns;
+	if (minmatch > minmatch0)
+	    minmatch = minmatch0;
+	}
+
+    /* Set format for numbers, if listed */
+    if (debug) {
+	maxnum = gnum[0];
+	for (gi = 1; gi < ng; gi++) {
+	    if (gnum[gi] > maxnum)
+		maxnum = gnum[gi];
+	    }
+	nnfld = CatNumLen (refcat, maxnum, 0);
+        }
+
+    /* Set maximum number of matches and allocate match indices */
+    if (ng > ns)
+	maxnbin = (int) ((double) ng * 1.25);
+    else
+	maxnbin = (int) ((double) ns * 1.25);
+    if (debug)
+	fprintf (stderr,"Match history: nim=%d nref=%d tol=%3.0f minbin=%d minmatch=%d):\n",
+		 ns, ng, tol, minbin, minmatch);
+
+    /* Allocate arrays in which to save match information */
+    mx = (double *) calloc (maxnbin, sizeof(double));
+    my = (double *) calloc (maxnbin, sizeof(double));
+    mxy = (double *) calloc (maxnbin, sizeof(double));
+    is = (int *) calloc (maxnbin, sizeof(int));
+    ig = (int *) calloc (maxnbin, sizeof(int));
+    ibs = (int *) calloc (maxnbin, sizeof(int));
+    ibg = (int *) calloc (maxnbin, sizeof(int));
+
+    /* Try matching stars using the current WCS first */
+    nmatch = 0;
+    bestdx = 0.0;
+    bestdy = 0.0;
+    dxsum = 0.0;
+    dysum = 0.0;
+    dxs = 0.0;
+    dys = 0.0;
+
+    /* Loop through image stars */
+    nbx = nxpix / (int) tol;
+    nby = nypix / (int) tol;
+    dbin = calloc (4 * nbx * nby, sizeof(double));
+    for (s = 0; s < ns; s++) {
+
+	/* Loop through reference catalog stars */
+	for (g = 0; g < ng; g++) {
+	    dx  = gx[g] - sx[s];
+	    dy = gy[g] - sy[s];
+
+	    /* Add to number in this offset bin */
+	    idx = (dx / tol)
+	    idy = (dy / tol)
+	    if ((idx > -nbx && idx < nbx && idy > -nby && idy < nby)
+		dbin[((idy+nby) * nbx) + idx + nbx]++;
+	    }
+	}
+
+    /* Find offset bin with maximim number of entries
+    idxmax = 0;
+    idymax = 0;
+    nmatch = 0;
+    dbini = dbin
+    for (idy = -nby; idy < nby; idy++) {
+	for (idx = -nbx; idx < nbx; idx++) {
+	    if (*dbini > ndmax) {
+		idxmax = idx;
+		idymax - idy;
+		nmatch = *dbini;
+		}
+	    dbini++;
+	    }
+	}
+    bestdx = tol * (double) (idxmax + nbx);
+    bestdy = tol * (double) (idymax + nby);
+
+    /* If we found enough matches, we can proceed with this offset */
+    if (nmatch > minmatch) {
+	if (debug)
+	    fprintf (stderr, "%d matches found at mean offset %6.3f %6.3f\n",
+		nmatch, bestdx, bestdy);
+	}
+
+    /* Otherwise, look for a coarse alignment assuming no additional rotation.
+     * This will collect a set of stars that correspond and
+     * establish an initial guess of the solution.
+     */
+    else {
+	npeaks = 0;
+	nmatch = 0;
+	for (i = 0; i < NPEAKS; i++) {
+	    peaks[i] = 0;
+	    dxpeaks[i] = 0;
+	    dypeaks[i] = 0;
+	    }
+	bestdx = 0.0;
+	bestdy = 0.0;
+	for (s = 0; s < ns; s++) {
+	    for (g = 0; g < ng; g++) {
+		dx = gx[g] - sx[s];
+		dy = gy[g] - sy[s];
+		nbin = 0;
+		for (gi = 0; gi < ng; gi++) {
+		    for (si = 0; si < ns; si++) {
+			dxi = gx[gi] - sx[si] - dx;
+			if (dxi < 0)
+			    dxi = -dxi;
+			dyi = gy[gi] - sy[si] - dy;
+			if (dyi < 0)
+			    dyi = -dyi;
+			if (dxi <= tol && dyi <= tol) {
+			/* if (debug)
+			    fprintf (stderr,"%d %d %d %d %5.1f %5.1f %5.1f %5.1f\n",
+				     g,s,gi,si,dx,dy,dxi,dyi); */
+			    is[nbin] = si;
+			    ig[nbin] = gi;
+			    nbin++;
+			    }
+			}
+		    }
+		/* if (debug)
+		    fprintf (stderr,"%d %d %d %d %d\n", g,s,gi,si,nbin); */
+		if (nbin > 1 && nbin >= nmatch) {
+		    int i;
+		    nmatch = nbin;
+		    bestdx = (double) dx;
+		    bestdy = (double) dy;
+		    for (i = 0; i < nbin; i++) {
+			ibs[i] = is[i];
+			ibg[i] = ig[i];
+			}
+	
+		    /* keep last NPEAKS nmatchs, dx and dy;
+		     * put newest first in arrays */
+		    if (npeaks > 0) {
+			for (i = npeaks; i > 0; i--) {
+			    peaks[i] = peaks[i-1];
+			    dxpeaks[i] = dxpeaks[i-1];
+			    dypeaks[i] = dypeaks[i-1];
+			    }
+			}
+		    peaks[0] = nmatch;
+		    if (bestdx > 0.0)
+			dxpeaks[0] = (int) (bestdx + 0.5);
+		    else
+			dxpeaks[0] = (int) (bestdx - 0.5);
+		    if (bestdy > 0)
+			dypeaks[0] = (int) (bestdy + 0.5);
+		    else
+			dypeaks[0] = (int) (bestdy - 0.5);
+		    if (npeaks < NPEAKS)
+			npeaks++;
+		    if (debug)
+			fprintf (stderr,"%d: %d/%d matches at image %d cat %d: dx= %d dy= %d\n",
+				npeaks, nmatch, minmatch, s, g, dxpeaks[0], dypeaks[0]);
+		    }
+		if (nmatch > minmatch)
+		    break;
+		}
+	    if (nmatch > minmatch)
+		break;
+	    }
+
+	/* if (debug) {
+	    int i;
+	    for (i = 0; i < npeaks; i++)
+		fprintf (stderr," %d bins at dx=%d dy=%d\n",
+			 peaks[i], dxpeaks[i], dypeaks[i]);
+	    } */
+
+	/* peak is broad */
+	if (npeaks < 2 || peaks[1] == peaks[0]) {
+	    if (debug)
+		fprintf (stderr,"  Broad peak of %d bins at dx=%.0f dy=%.0f\n",
+		         peaks[0], bestdx, bestdy);
+	    }
+	}
+
+    /* too few hits */
+    if (nmatch < minbin)
+	return (nmatch);
+
+    /* Get X and Y coordinates of matches from best binning */
+    nmatchd = nmatch * sizeof (double);
+    if (!(sbx = (double *) malloc (nmatchd)))
+	fprintf (stderr," Could not allocate %d bytes for SBX\n", nmatchd);
+    if (!(sby = (double *) malloc (nmatchd)))
+	fprintf (stderr," Could not allocate %d bytes for SBY\n", nmatchd);
+    if (!(gbra = (double *) malloc (nmatchd)))
+	fprintf (stderr," Could not allocate %d bytes for GBRA\n", nmatchd);
+    if (!(gbdec = (double *) malloc (nmatchd)))
+	fprintf (stderr," Could not allocate %d bytes for GBDEC\n", nmatchd);
+    for (i = 0; i < nmatch; i++) {
+	sbx[i] = sx[ibs[i]];
+	sby[i] = sy[ibs[i]];
+	gbra[i] = gra[ibg[i]];
+	gbdec[i] = gdec[ibg[i]];
+	}
+
+    /* Reset image center based on star matching */
+    wcs->xref = wcs->xref + (bestdx * wcs->xinc);
+    if (wcs->xref < 0.0) wcs->xref = 360.0 + wcs->xref;
+    wcs->yref = wcs->yref + (bestdy * wcs->yinc);
+
+    /* Fit WCS to matched stars */
+
+    /* Provide non-parametric access to the star lists */
+    sx_p = sbx;
+    sy_p = sby;
+    gra_p = gbra;
+    gdec_p = gbdec;
+    xref_p = wcs->xref;
+    yref_p = wcs->yref;
+    xrefpix = wcs->xrefpix;
+    yrefpix = wcs->yrefpix;
+    nbin_p = nmatch;
+
+    /* Number of parameters to fit from command line or number of matches */
+    pfit = ParamFit (nmatch);
+
+    /* Get parameters to fit from digits of pfit */
+    sprintf (vpar, "%d", pfit);
+    nfit = 0;
+    vfit[0] = -1;
+    for (i = 1; i < NPAR1; i++) {
+	vc = i + 48;
+	vi = strchr (vpar, vc);
+	if (vi != NULL) {
+	    vfit[i] = vi - vpar;
+	    nfit++;
+	    }
+	else
+	    vfit[i] = -1;
+	}
+
+    /* Set initial guesses for parameters which are being fit */
+    xref0 = wcs->xref;
+    yref0 = wcs->yref;
+    xinc0 = wcs->xinc;
+    yinc0 = wcs->yinc;
+    rot0 = wcs->rot;
+    xrefpix0 = wcs->xrefpix;
+    yrefpix0 = wcs->yrefpix;
+    cd0[0] = wcs->cd[0];
+    cd0[1] = wcs->cd[1];
+    cd0[2] = wcs->cd[2];
+    cd0[3] = wcs->cd[3];
+    if (vfit[6] > -1)
+	cdfit = 1;
+    else
+	cdfit = 0;
+
+    /* Fit image star coordinates to reference star positions */
+    wcs_amoeba (wcs);
+
+    if (debug) {
+	fprintf (stderr,"\nAmoeba fit:\n");
+	ra2str (rastr, 16, xref0, 3);
+	dec2str (decstr, 16, yref0, 2);
+	fprintf (stderr,"   initial guess:\n");
+	if (vfit[6] > -1)
+	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
+		     rastr, decstr, cd0[0], cd0[1], cd0[2], cd0[3]);
+	else
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		     rastr, decstr, xinc0*3600.0, yinc0*3600.0, rot0);
+	fprintf (stderr,"(%8.2f,%8.2f\n", xrefpix0, yrefpix0);
+
+	ra2str (rastr, 16, wcs->xref, 3);
+	dec2str (decstr, 16, wcs->yref, 2);
+	fprintf (stderr,"\nfirst solution:\n");
+	if (vfit[6] > -1)
+	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
+		     rastr,decstr,wcs->cd[0],wcs->cd[1],wcs->cd[2],wcs->cd[3]);
+	else
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		     rastr,decstr,3600.0*wcs->xinc,3600.0*wcs->yinc,wcs->rot);
+	fprintf (stderr,"(%8.2f,%8.2f)\n", wcs->xrefpix, wcs->yrefpix);
+	}
+
+    /* If we have extra bins, repeat with the best ones */
+    bestbin = nfit + 1;
+    if (resid_refine && nmatch > bestbin) {
+	double *resid = (double *) malloc (nmatch * sizeof(double));
+	double *xe = (double *) malloc (nmatch * sizeof(double));
+	double *ye = (double *) malloc (nmatch * sizeof(double));
+	int i, j;
+	double xmean, ymean, rmean, xsumsq, ysumsq, diff;
+	double mx, my, xsig, ysig, rsig, siglim;
+	char wcstring[64];
+	double xsum = 0.0;
+	double ysum = 0.0;
+	double rsum = 0.0;
+	double dmatch = (double)nmatch;
+	double dmatch1 = (double)(nmatch - 1);
+
+	/* Compute residuals at each star location */
+	for (i = 0; i < nmatch; i++) {
+	    pix2wcs (wcs, sbx[i], sby[i], &mx, &my);
+	    xe[i] = (mx - gbra[i]) * 3600.0;
+	    ye[i] = (my - gbdec[i]) * 3600.0;
+	    resid[i] = sqrt (xe[i]*xe[i] + ye[i]*ye[i]);
+	    if (debug) {
+		pix2wcst (wcs, sbx[i], sby[i], wcstring, 64);
+		fprintf (stderr,"%3d (%8.3f,%8.3f) -> %s %6.3f %6.3f %6.3f\n",
+		    i, sbx[i], sby[i], wcstring, xe[i], ye[i], resid[i]);
+		}
+	    xsum = xsum + xe[i];
+	    ysum = ysum + ye[i];
+	    rsum = rsum + resid[i];
+	    }
+
+	/* Compute means and standard deviations */
+	xmean = xsum / dmatch;
+	ymean = ysum / dmatch;
+	rmean = rsum / dmatch;
+	xsumsq = 0.0;
+	ysumsq = 0.0;
+	for (i = 0; i < nmatch; i++) {
+	    diff = xe[i] - xmean;
+	    xsumsq = xsumsq + (diff * diff);
+	    diff = ye[i] - ymean;
+	    ysumsq = ysumsq + (diff * diff);
+	    }
+	xsig = sqrt (xsumsq / dmatch1);
+	ysig = sqrt (ysumsq / dmatch1);
+	rsig = sqrt ((xsumsq + ysumsq)/ dmatch1);
+	siglim = 2.0 * rsig;
+	if (debug) {
+	    fprintf (stderr,"Mean x: %6.3f/%6.3f y: %6.3f/%6.3f r: %6.3f/%6.3f\n",
+		    xmean, xsig, ymean, ysig, rmean, rsig);
+	    }
+
+	/* sort by increasing total residual */
+	for (i = 0; i < nmatch-1; i++) {
+	    for (j = i+1; j < nmatch; j++) {
+		if (resid[j] < resid[i]) {
+		    double tmp;
+
+		    tmp = sbx[i]; sbx[i] = sbx[j]; sbx[j] = tmp;
+		    tmp = sby[i]; sby[i] = sby[j]; sby[j] = tmp;
+		    tmp = gbra[i]; gbra[i] = gbra[j]; gbra[j] = tmp;
+		    tmp = gbdec[i]; gbdec[i] = gbdec[j]; gbdec[j] = tmp;
+		    tmp = resid[i]; resid[i] = resid[j]; resid[j] = tmp;
+		    }
+		}
+	    }
+
+	/* Cut off points at residual of two sigma */
+	for (i = 0; i < nmatch; i++) {
+	    if (resid[i] > siglim) {
+		if (i > bestbin) bestbin = i - 1;
+		break;
+		}
+	    }
+
+	xref_p = wcs->xref;
+	if (xref_p < 0.0) xref_p = 360.0 + xref_p;
+	yref_p = wcs->yref;
+	xrefpix = wcs->xrefpix;
+	yrefpix = wcs->yrefpix;
+	nbin_p = bestbin;
+	wcs_amoeba (wcs);
+
+	if (debug) {
+	    ra2str (rastr, 16, wcs->xref, 3);
+	    dec2str (decstr, 16, wcs->yref, 2);
+	    fprintf (stderr,"\nresid solution:\n");
+	    fprintf (stderr,"\n%d points < %.3f arcsec residuals refit\n",
+			    bestbin, siglim);
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		 rastr, decstr, 3600.0*wcs->xinc, 3600.0*wcs->yinc, wcs->rot);
+	    fprintf (stderr,"(%8.2f,%8.2f)\n", wcs->xrefpix, wcs->yrefpix);
+	    }
+	free (resid);
+	free (xe);
+	free (ye);
+	}
+
+    free (sbx);
+    free (sby);
+    free (gbra);
+    free (gbdec);
+    free (is);
+    free (ig);
+    free (ibs);
+    free (ibg);
+
+    return (nmatch);
+}
+
+int
+ParamFit (nbin)
+
+int	nbin;	/* Number of point to be fit */
+{
+    int pfit;
+
+    if (pfit0 != 0) {
+	if (pfit0 < 3)
+	    pfit = 12;
+	else if (pfit0 == 3)	/* Fit center and plate scale */
+	    pfit = 123;
+	else if (pfit0 == 4)	/* Fit center, plate scale, rotation */
+	    pfit = 1235;
+	else if (pfit0 == 5)	/* Fit center, x&y plate scales, rotation */
+	    pfit = 12345;
+	else if (pfit0 == 6)	/* Fit center, x&y plate scales, x&y rotations */
+	    pfit = 123456;
+	else if (pfit0 == 7)	/* Fit center, x&y plate scales, rotation, refpix */
+	    pfit = 1234578;
+	else if (pfit0 == 8)	/* Fit center, x&y plate scales, x&y rotation, refpix */
+	    pfit = 12345678;
+	else
+	    pfit = pfit0;
+	}
+    else if (nbin < 4)
+	pfit = 12;
+    else if (nbin < 6)
+	pfit = 123;
+    else
+	pfit = 12345;
+    return (pfit);
+}
+
+
+int
+NParamFit (nbin)
+
+int	nbin;	/* Number of point to be fit */
+{
+    int pfit;
+
+    pfit = ParamFit (nbin);
+    if (pfit < 1)
+	return (0);
+    else if (pfit < 10)
+	return (1);
+    else if (pfit < 100)
+	return (2);
+    else if (pfit < 1000)
+	return (3);
+    else if (pfit < 10000)
+	return (4);
+    else if (pfit < 100000)
+	return (5);
+    else if (pfit < 1000000)
+	return (6);
+    else if (pfit < 10000000)
+	return (7);
+    else
+	return (8);
+}
+
+
+int
+ReadMatch (filename, sx, sy, sra, sdec, debug)
+
+char	*filename;	/* Name of file containing matches */
+double	**sx;		/* Image star X coordinates in pixels */
+double	**sy;		/* Image star Y coordinates in pixels */
+double	**sra;		/* Probable image star right ascensions in degrees */
+double	**sdec;		/* Probable image star declinations in degrees */
+int	debug;		/* Printed debugging information if not zero */
+
+{
+    int nbytes, nread, ir, ntok, itok, iytok;
+    double *tx, *ty, *tra, *tdec, ra, dec, x, y;
+    int ndec;
+    int nmatch = 0;	/* Number of matches read from file */
+    char rastr[32], decstr[32];
+
+    /* If tab file, read from ra, dec, x, y  columns */
+    if (istab (filename)) {
+	}
+
+    /* Otherwise, assume first 4 columns are x, y, ra, dec */
+    else {
+	char line[1025];
+	char *nextline, *lastchar;
+	FILE *fd;
+	struct Tokens tokens;	/* Token structure */
+	char *cwhite;		/* additional whitespace characters */
+	char token[256];
+
+	cwhite = NULL;
+
+	/* Open input file */
+	if (!strcmp (filename, "stdin")) {
+	    fd = stdin;
+	    nread = 1000;
+	    }
+	else {
+	    nread = getfilelines (filename);
+	    if (!(fd = fopen (filename, "r"))) {
+	    fprintf (stderr, "SetWCSFITS: Match file %s could not be opened\n",
+		     filename);
+		return (0);
+		}
+	    }
+	nbytes = nread * sizeof (double);
+	if (!(tra = (double *) calloc (nread, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for gra\n", nbytes);
+	if (!(tdec = (double *) calloc (nread, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for gdec\n", nbytes);
+	if (!(tx = (double *) calloc (nread, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for sx\n", nbytes);
+	if (!(ty = (double *) calloc (nread, sizeof(double))))
+	    fprintf (stderr, "Could not calloc %d bytes for sy\n", nbytes);
+	*sra = tra;
+	*sdec = tdec;
+	*sx = tx;
+	*sy = ty;
+
+	nmatch = 0;
+	nextline = line;
+        for (ir = 0; ir < nread; ir++) {
+	    if (fgets (line, 1024, fd) == NULL)
+		break;
+
+	    /* Skip lines with comments */
+	    if (line[0] == '#')
+		continue;
+
+	    /* Drop linefeeds */
+	    lastchar = nextline + strlen(nextline) - 1;
+	    if (*lastchar < 32)
+		*lastchar = (char) 0;
+
+	    /* Read X, Y, RA, and Dec from each line,
+		skipping line if all four are not present and numbers */
+	    ntok = setoken (&tokens, line, cwhite);
+	    if (ntok < 1)
+		break;
+	    if (ntok < 4)
+		continue;
+
+	    /* if (debug)
+		fprintf (stderr, "%d: %s\n", nmatch, line); */
+
+	    /* Image X coordinate or RA */
+	    itok = 1;
+	    if (getoken(&tokens, itok, token, 256)) {
+
+		/* Read RA, Dec, X, Y if first token has : in it */
+		if (strchr (token, ':') != NULL) {
+		    ra = str2ra (token);
+		    iytok = 4;
+		    if (getoken(&tokens, 2, token, 256))
+			dec = str2dec (token);
+		    if (getoken(&tokens, 3, token, 256)) {
+			if (isnum (token))
+			    x = atof (token);
+			else {
+			    iytok = 5;
+			    if (getoken(&tokens, 4, token, 256)) {
+				if (isnum (token))
+				    x = atof (token);
+				else
+				    continue;
+				}
+			    }
+			}
+		    if (getoken(&tokens, iytok, token, 256)) {
+			if (isnum (token))
+			    y = atof (token);
+			else
+			    continue;
+			}
+		    tx[nmatch] = x;
+		    ty[nmatch] = y;
+		    tra[nmatch] = ra;
+		    tdec[nmatch] = dec;
+		    nmatch++;
+		    continue;
+		    }
+		if (isnum (token))
+		    x = atof (token);
+		else
+		    continue;
+		}
+	    else
+		continue;
+
+	    /* Image Y coordinate */
+	    itok++;
+	    if (getoken(&tokens, itok, token, 256)) {
+		if (isnum (token))
+		    y = atof (token);
+		else
+		    continue;
+		}
+	    else
+		continue;
+
+	    /* Right ascension */
+	    itok++;
+	    if (getoken(&tokens, itok, token, 256)) {
+		if (isnum (token) == 1) {
+		    ra = atof (token);
+		    itok++;
+		    if (getoken(&tokens, itok, token, 256)) {
+			if (isnum (token) == 2)
+			    ra = ra + (atof (token) / 60.0);
+			else if (isnum (token) == 1) {
+			    ra = ra + (atof (token) / 60.0);
+			    itok++;
+			    if (getoken(&tokens, itok, token, 256)) {
+				if (isnum (token))
+				    ra = ra + (atof (token) / 3600.0);
+				}
+			    }
+			}
+		    ra = ra * 15.0;
+		    }
+		else
+		    ra = str2ra (token);
+		}
+	    else
+		continue;
+
+	    /* Declination */
+	    itok++;
+	    if (getoken(&tokens, itok, token, 256)) {
+		if (isnum (token) == 1) {
+		    dec = atof (token);
+		    itok++;
+		    if (strchr (token, '-') != NULL)
+			ndec = 1;
+		    else
+			ndec = 0;
+		    if (getoken(&tokens, itok, token, 256)) {
+			if (isnum (token) == 2) {
+			    if (ndec)
+				dec = dec - (atof (token) / 60.0);
+			    else
+				dec = dec + (atof (token) / 60.0);
+			    }
+			else if (isnum (token) == 1) {
+			    if (ndec)
+				dec = dec - (atof (token) / 60.0);
+			    else
+				dec = dec + (atof (token) / 60.0);
+			    itok++;
+			    if (getoken(&tokens, itok, token, 256)) {
+				if (isnum (token)) {
+				    if (ndec)
+					dec = dec - (atof (token) / 3600.0);
+				    else
+					dec = dec + (atof (token) / 3600.0);
+				    }
+				}
+			    }
+			}
+		    }
+		else
+		    dec = str2dec (token);
+		}
+	    else
+		continue;
+	    tx[nmatch] = x;
+	    ty[nmatch] = y;
+	    tra[nmatch] = ra;
+	    tdec[nmatch] = dec;
+	    if (debug) {
+		ra2str (rastr, 32, tra[nmatch], 3);
+		dec2str (decstr, 32, tdec[nmatch], 2);
+		fprintf (stderr, "%d: %8.3f %8.3f %s %s\n", nmatch,
+			 tx[nmatch], ty[nmatch], rastr, decstr);
+		}
+	    nmatch++;
+	    }
+	}
+
+    return (nmatch);
+}
+
+
+/* Find shift, scale, and rotation of image stars to best-match reference stars
+ */
+
+void
+WCSMatch (nmatch, sbx, sby, gbra, gbdec, debug)
+
+int	nmatch;		/* Number of matched stars */
+double	*sbx;		/* Image star X coordinates in pixels */
+double	*sby;		/* Image star Y coordinates in pixels */
+double	*gbra;		/* Reference star right ascensions in degrees */
+double	*gbdec;		/* Reference star right ascensions in degrees */
+int	debug;		/* Printed debugging information if not zero */
+
+{
+    int i;
+    double xdiff, ydiff;
+    int nsc, j;
+    double tx = 0.0;
+    double ty = 0.0;
+    double tra = 0.0;
+    double tdec = 0.0;
+    double tdiff = 0.0;
+    double cra, cdec, cx, cy, scale;
+    double dmatch;
+    double skydiff, imdiff;
+    extern double getsecpix();
+    extern void getcenter(),getrefpix(),setdcenter(),setrefpix(),setsecpix();
+
+    dmatch = (double) nmatch;
+
+    if (debug) {
+	fprintf (stderr,"%d matched stars:\n", nmatch);
+	}
+
+    /* too few hits */
+    if (nmatch < 2)
+	return;
+
+    /* Compute plate scale and center of stars */
+    nsc = 0;
+    for (i = 0; i < nmatch; i++) {
+	tx = tx + sbx[i];
+	ty = ty + sby[i];
+	tra = tra + gbra[i];
+	tdec = tdec + gbdec[i];
+	for (j = i+1; j < nmatch; j++) {
+	    skydiff = wcsdist (gbra[i], gbdec[i], gbra[j], gbdec[j]);
+	    xdiff = sbx[j] - sbx[i];
+	    ydiff = sby[j] - sby[i];
+	    imdiff = sqrt ((xdiff * xdiff) + (ydiff * ydiff));
+	    scale = skydiff / imdiff;
+	    tdiff = tdiff + scale;
+	    nsc++;
+	    if (debug) {
+		fprintf (stderr,"%d %d: sky: %8g, image: %8g, %8g deg/pix", 
+			i, j, skydiff, imdiff, scale);
+		fprintf (stderr," = %8g arcsec/pix\n", scale * 3600.0);
+		}
+	    }
+	}
+    
+    /* Reset image center based on star matching */
+    getcenter (&cra, &cdec);
+    if (cra == -99.0 && cdec == -99.0) {
+	cra = tra / dmatch;
+	cdec = tdec / dmatch;
+	setdcenter (cra, cdec);
+	}
+    getrefpix (&cx, &cy);
+    if (cx == -99999.0) {
+	cx = tx / dmatch;
+	cy = ty / dmatch;
+	setrefpix (cx, cy);
+	}
+    scale = getsecpix();
+    if (scale == 0.0) {
+	scale = tdiff / (double) nsc;
+	setsecpix (3600.0 * scale);
+	}
+    if (debug)
+	fprintf (stderr,"scale = %8g deg/pix = %8g arcsec/pix\n", scale, scale*3600.0);
+    return;
+}
+
+
+/* Find shift, scale, and rotation of image stars to best-match reference stars
+ * Return count of total coincidences found, else 0 if none or -1 if trouble.
+ */
+
+int
+FitMatch (nmatch, sbx, sby, gbra, gbdec, wcs, debug)
+
+int	nmatch;		/* Number of matched stars */
+double	*sbx;		/* Image star X coordinates in pixels */
+double	*sby;		/* Image star Y coordinates in pixels */
+double	*gbra;		/* Reference star right ascensions in degrees */
+double	*gbdec;		/* Reference star right ascensions in degrees */
+struct WorldCoor *wcs;	/* World coordinate structure (fit returned) */
+int	debug;		/* Printed debugging information if not zero */
+
+{
+    int i;
+    char rastr[16], decstr[16];
+    double xref0, yref0, xinc0, yinc0, rot0, xrefpix0, yrefpix0, cd0[4];
+    int bestbin;	/* Number of coincidences for refit */
+    int pfit;		/* List of parameters to fit, 1 per digit */
+    char vpar[16];	/* List of parameters to fit */
+    double xdiff, ydiff;
+    char *vi;
+    char vc;
+    int nsc, j;
+/*    double equinox = wcs->equinox; */
+    double tx = 0.0;
+    double ty = 0.0;
+    double tra = 0.0;
+    double tdec = 0.0;
+    double tdiff = 0.0;
+    double scale;
+/*    double dmatch; */
+    double skydiff, imdiff;
+
+/*    dmatch = (double) nmatch; */
+
+    if (debug) {
+	fprintf (stderr,"%d matched stars:\n", nmatch);
+	}
+
+    /* too few hits */
+    if (nmatch < minbin)
+	return (nmatch);
+
+    /* Compute plate scale and center of stars */
+    nsc = 0;
+    for (i = 0; i < nmatch; i++) {
+	tx = tx + sbx[i];
+	ty = ty + sby[i];
+	tra = tra + gbra[i];
+	tdec = tdec + gbdec[i];
+	for (j = i+1; j < nmatch; j++) {
+	    skydiff = wcsdist (gbra[i], gbdec[i], gbra[j], gbdec[j]);
+	    xdiff = sbx[j] - sbx[i];
+	    ydiff = sby[j] - sby[i];
+	    imdiff = sqrt ((xdiff * xdiff) + (ydiff * ydiff));
+	    scale = skydiff / imdiff;
+	    tdiff = tdiff + scale;
+	    nsc++;
+	    if (debug) {
+		fprintf (stderr,"%d %d: sky: %8g, image: %8g, %8g deg/pix", 
+			i, j, skydiff, imdiff, scale);
+		fprintf (stderr," = %8g arcsec/pix\n", scale * 3600.0);
+		}
+	    }
+	}
+    
+    /* Reset image center in WCS data structure based on star matching */
+    /* cra = tra / dmatch;
+    cdec = tdec / dmatch;
+    cx = tx / dmatch;
+    cy = ty / dmatch;
+    scale = tdiff / (double) nsc;
+    if (debug)
+	fprintf (stderr,"scale = %8g deg/pix = %8g arcsec/pix\n", scale, scale*3600.0);
+    wcsreset (wcs, cx, cy, cra, cdec, scale, 0.0, 0.0, NULL, equinox); */
+
+    /* Provide non-parametric access to the star lists */
+    sx_p = sbx;
+    sy_p = sby;
+    gra_p = gbra;
+    gdec_p = gbdec;
+    xref_p = wcs->xref;
+    if (xref_p < 0.0) xref_p = 360.0 + xref_p;
+    yref_p = wcs->yref;
+    xrefpix = wcs->xrefpix;
+    yrefpix = wcs->yrefpix;
+    nbin_p = nmatch;
+
+    /* Number of parameters to fit from command line or number of matches */
+    if (pfit0 != 0) {
+	if (pfit0 < 3)
+	    pfit = 12;
+	else if (pfit0 == 3)	/* Fit center and plate scale */
+	    pfit = 123;
+	else if (pfit0 == 4)	/* Fit center, plate scale, rotation */
+	    pfit = 1235;
+	else if (pfit0 == 5)	/* Fit center, x&y plate scales, rotation */
+	    pfit = 12345;
+	else if (pfit0 == 6)	/* Fit center, x&y plate scales, x&y rotations */
+	    pfit = 123456;
+	else if (pfit0 == 7)	/* Fit center, x&y plate scales, rotation, refpix */
+	    pfit = 1234578;
+	else if (pfit0 == 8)	/* Fit center, x&y plate scales, x&y rotation, refpix */
+	    pfit = 12345678;
+	else
+	    pfit = pfit0;
+	}
+    else if (nmatch < 4)
+	pfit = 12;
+    else if (nmatch < 6)
+	pfit = 123;
+    else
+	pfit = 12345;
+
+    /* Get parameters to fit from digits of pfit */
+    sprintf (vpar, "%d", pfit);
+    nfit = 0;
+    vfit[0] = -1;
+    for (i = 1; i < NPAR1; i++) {
+	vc = i + 48;
+	vi = strchr (vpar, vc);
+	if (vi != NULL) {
+	    vfit[i] = vi - vpar;
+	    nfit++;
+	    }
+	else
+	    vfit[i] = -1;
+	}
+
+    /* Set initial guesses for parameters which are being fit */
+    xref0 = wcs->xref;
+    yref0 = wcs->yref;
+    xinc0 = wcs->xinc;
+    yinc0 = wcs->yinc;
+    rot0 = wcs->rot;
+    xrefpix0 = wcs->xrefpix;
+    yrefpix0 = wcs->yrefpix;
+    cd0[0] = wcs->cd[0];
+    cd0[1] = wcs->cd[1];
+    cd0[2] = wcs->cd[2];
+    cd0[3] = wcs->cd[3];
+    if (vfit[6] > -1)
+	cdfit = 1;
+    else
+	cdfit = 0;
+
+    /* Fit image star coordinates to reference star positions */
+    wcs_amoeba (wcs);
+
+    if (debug) {
+	fprintf (stderr,"\nAmoeba fit:\n");
+	ra2str (rastr, 16, xref0, 3);
+	dec2str (decstr, 16, yref0, 2);
+	fprintf (stderr,"   initial guess:\n");
+	if (vfit[6] > -1)
+	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
+		     rastr, decstr, cd0[0], cd0[1], cd0[2], cd0[3]);
+	else
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		     rastr, decstr, xinc0*3600.0, yinc0*3600.0, rot0);
+	fprintf (stderr,"(%8.2f,%8.2f\n", xrefpix0, yrefpix0);
+
+	ra2str (rastr, 16, wcs->xref, 3);
+	dec2str (decstr, 16, wcs->yref, 2);
+	fprintf (stderr,"\nfirst solution:\n");
+	if (vfit[6] > -1)
+	    fprintf (stderr," cra= %s cdec= %s cd = %9.7f,%9.7f,%9.7f,%9.7f ",
+		     rastr,decstr,wcs->cd[0],wcs->cd[1],wcs->cd[2],wcs->cd[3]);
+	else
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		     rastr,decstr,3600.0*wcs->xinc,3600.0*wcs->yinc,wcs->rot);
+	fprintf (stderr,"(%8.2f,%8.2f)\n", wcs->xrefpix, wcs->yrefpix);
+	}
+
+    /* If we have extra bins, repeat with the best ones */
+    bestbin = nfit + 1;
+    if (resid_refine && nmatch > bestbin) {
+	double *resid = (double *) malloc (nmatch * sizeof(double));
+	double *xe = (double *) malloc (nmatch * sizeof(double));
+	double *ye = (double *) malloc (nmatch * sizeof(double));
+	int i, j;
+	double xmean, ymean, rmean, xsumsq, ysumsq, diff;
+	double mra, mdec, xsig, ysig, rsig, siglim;
+	char wcstring[64];
+	double xsum = 0.0;
+	double ysum = 0.0;
+	double rsum = 0.0;
+	double dmatch = (double)nmatch;
+	double dmatch1 = (double)(nmatch - 1);
+
+	/* Compute residuals at each star location */
+	for (i = 0; i < nmatch; i++) {
+	    pix2wcs (wcs, sbx[i], sby[i], &mra, &mdec);
+	    xe[i] = (mra - gbra[i]) * 3600.0;
+	    ye[i] = (mdec - gbdec[i]) * 3600.0;
+	    resid[i] = sqrt (xe[i]*xe[i] + ye[i]*ye[i]);
+	    if (debug) {
+		pix2wcst (wcs, sbx[i], sby[i], wcstring, 64);
+		fprintf (stderr,"%3d (%8.3f,%8.3f) -> %s %6.3f %6.3f %6.3f\n",
+		    i, sbx[i], sby[i], wcstring, xe[i], ye[i], resid[i]);
+		}
+	    xsum = xsum + xe[i];
+	    ysum = ysum + ye[i];
+	    rsum = rsum + resid[i];
+	    }
+
+	/* Compute means and standard deviations */
+	xmean = xsum / dmatch;
+	ymean = ysum / dmatch;
+	rmean = rsum / dmatch;
+	xsumsq = 0.0;
+	ysumsq = 0.0;
+	for (i = 0; i < nmatch; i++) {
+	    diff = xe[i] - xmean;
+	    xsumsq = xsumsq + (diff * diff);
+	    diff = ye[i] - ymean;
+	    ysumsq = ysumsq + (diff * diff);
+	    }
+	xsig = sqrt (xsumsq / dmatch1);
+	ysig = sqrt (ysumsq / dmatch1);
+	rsig = sqrt ((xsumsq + ysumsq)/ dmatch1);
+	siglim = 2.0 * rsig;
+	if (debug) {
+	    fprintf (stderr,"Mean x: %6.3f/%6.3f y: %6.3f/%6.3f r: %6.3f/%6.3f\n",
+		    xmean, xsig, ymean, ysig, rmean, rsig);
+	    }
+
+	/* sort by increasing total residual */
+	for (i = 0; i < nmatch-1; i++) {
+	    for (j = i+1; j < nmatch; j++) {
+		if (resid[j] < resid[i]) {
+		    double tmp;
+
+		    tmp = sbx[i]; sbx[i] = sbx[j]; sbx[j] = tmp;
+		    tmp = sby[i]; sby[i] = sby[j]; sby[j] = tmp;
+		    tmp = gbra[i]; gbra[i] = gbra[j]; gbra[j] = tmp;
+		    tmp = gbdec[i]; gbdec[i] = gbdec[j]; gbdec[j] = tmp;
+		    tmp = resid[i]; resid[i] = resid[j]; resid[j] = tmp;
+		    }
+		}
+	    }
+
+	/* Cut off points at residual of two sigma */
+	for (i = 0; i < nmatch; i++) {
+	    if (resid[i] > siglim) {
+		if (i > bestbin) bestbin = i - 1;
+		break;
+		}
+	    }
+
+	xref_p = wcs->xref;
+	if (xref_p < 0.0) xref_p = 360.0 + xref_p;
+	yref_p = wcs->yref;
+	xrefpix = wcs->xrefpix;
+	yrefpix = wcs->yrefpix;
+	nbin_p = bestbin;
+	wcs_amoeba (wcs);
+
+	if (debug) {
+	    ra2str (rastr, 16, wcs->xref, 3);
+	    dec2str (decstr, 16, wcs->yref, 2);
+	    fprintf (stderr,"\nresid solution:\n");
+	    fprintf (stderr,"\n%d points < %.3f arcsec residuals refit\n",
+			    bestbin, siglim);
+	    fprintf (stderr," cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ",
+		 rastr, decstr, 3600.0*wcs->xinc, 3600.0*wcs->yinc, wcs->rot);
+	    fprintf (stderr,"(%8.2f,%8.2f)\n", wcs->xrefpix, wcs->yrefpix);
+	    }
+	free (resid);
+	free (xe);
+	free (ye);
+	}
+
+    return (nmatch);
+}
+
+struct WorldCoor *wcsf;
+
+static double wcs_chisqr ();
+
+/* From Numerical Recipes */
+void amoeba();
+static double amotry();
+
+
+/* Set up the necessary temp arrays and call the amoeba() multivariate solver */
+
+static void
+wcs_amoeba (wcs0)
+
+struct WorldCoor *wcs0;
+
+{
+    double *p[NPAR1];				  /* used as p[NPAR1][NPAR] */
+    double vguess[NPAR], vp[NPAR], vdiff[NPAR];
+    double p0[NPAR], p1[NPAR], p2[NPAR], p3[NPAR], p4[NPAR],
+	   p5[NPAR], p6[NPAR], p7[NPAR], p8[NPAR]; /* used as px[0..NPAR-1] */
+    double y[NPAR1];				  /* used as y[1..NPAR] */
+    double xinc1, yinc1, xrefpix1, yrefpix1, rot, cd[4];
+    double sumx, sumy, sumr;
+    int iter;
+    int i, j;
+    int nfit1;
+    char rastr[16],decstr[16];
+    int nitmax;
+
+    nitmax = nitmax0;
+    if (nfit > NPAR)
+	nfit = NPAR;
+    nfit1 = nfit + 1;
+    wcsf = wcs0;
+
+/* Initialize guess and difference vectors to zero */
+    for (i = 0; i < NPAR; i++) {
+	vguess[i] = 0.0;
+	vdiff[i] = 0.0;
+	}
+
+    /* Optical axis center (RA and Dec degrees) */
+    if (vfit[1] > -1) {
+	vguess[vfit[1]] = 0.0;
+	vdiff[vfit[1]] = 5.0 * wcsf->xinc;
+	}
+    if (vfit[2] > -1) {
+	vguess[vfit[2]] = 0.0;
+	vdiff[vfit[2]] = 5.0 * wcsf->yinc;
+	}
+    /* Second rotation about optical axis (degrees) -> CD matrix */
+    if (vfit[6] > -1) {
+	wcsf->rotmat = 1;
+	vguess[vfit[3]] = wcsf->cd[0];
+	vdiff[vfit[3]] = wcsf->xinc * 0.03;
+	vguess[vfit[4]] = wcsf->cd[1];
+	vdiff[vfit[4]] = wcsf->yinc * 0.03;
+	vguess[vfit[5]] = wcsf->cd[2];
+	vdiff[vfit[5]] = wcsf->xinc * 0.03;
+	vguess[vfit[6]] = wcsf->cd[3];
+	vdiff[vfit[6]] = wcsf->yinc * 0.03;
+	}
+
+    else {
+
+    /* Plate scale at optical axis right ascension or both (degrees/pixel) */
+	if (vfit[3] > -1) {
+	    vguess[vfit[3]] = wcsf->xinc;
+	    vdiff[vfit[3]] = wcsf->xinc * 0.03;
+	    }
+
+    /* Plate scale in declination at optical axis (degrees/pixel) */
+	if (vfit[4] > -1) {
+	    vguess[vfit[4]] = wcsf->yinc;
+	    vdiff[vfit[4]] = wcsf->yinc * 0.03;
+	    }
+
+    /* Rotation about optical axis in degrees */
+	if (vfit[5] > -1) {
+	    vguess[vfit[5]] = wcsf->rot;
+	    vdiff[vfit[5]] = 0.5;
+	    }
+	}
+
+/* Reference pixel (optical axis) */
+    if (vfit[7] > -1) {
+	vguess[vfit[7]] = 0.0;
+	vdiff[vfit[7]] = 10.0;
+	}
+    if (vfit[8] > -1) {
+	vguess[vfit[8]] = 0.0;
+	vdiff[vfit[8]] = 10.0;
+	}
+
+/* Set up matrix of nfit+1 initial guesses.
+ * The supplied guess, plus one for each parameter altered by a small amount
+ */
+    p[0] = p0;
+    if (nfit > 0) p[1] = p1;
+    if (nfit > 1) p[2] = p2;
+    if (nfit > 2) p[3] = p3;
+    if (nfit > 3) p[4] = p4;
+    if (nfit > 4) p[5] = p5;
+    if (nfit > 5) p[6] = p6;
+    if (nfit > 6) p[7] = p7;
+    if (nfit > 7) p[8] = p8;
+    for (i = 0; i <= nfit; i++) {
+	for (j = 0; j < nfit; j++)
+	    p[i][j] = vguess[j];
+	if (i > 0 && i <= nfit)
+	    p[i][i-1] = vguess[i-1] + vdiff[i-1];
+	 y[i] = wcs_chisqr (p[i], -i);
+	}
+
+#define	PDUMP
+#ifdef	PDUMP
+    fprintf (stderr,"Before:\n");
+    for (i = 0; i < nfit1; i++) {
+	if (vfit[1] > -1)
+	    ra2str (rastr, 16, p[i][vfit[1]] + xref_p, 3);
+	else
+	    ra2str (rastr, 16, wcsf->xref, 3);
+	if (vfit[2] > -1)
+	    dec2str (decstr, 16, p[i][vfit[2]]+yref_p, 2);
+	else
+	    dec2str (decstr, 16, wcsf->yref, 2);
+	if (vfit[6] > -1) {
+	    cd[0] = p[i][vfit[3]];
+	    cd[1] = p[i][vfit[4]];
+	    cd[2] = p[i][vfit[5]];
+	    cd[3] = p[i][vfit[6]];
+	    fprintf (stderr,"%d: %s %s CD: %7.5f,%7.5f,%7.5f,%7.5f ",
+		    i, rastr, decstr, cd[0],cd[1],cd[2],cd[3]);
+	    }
+	else {
+	    if (vfit[3] > -1)
+		xinc1 = p[i][vfit[3]];
+	    else
+		xinc1 = wcsf->xinc;
+	    if (vfit[4] > -1)
+		yinc1 = p[i][vfit[4]];
+	    else if (vfit[3] > -1) {
+		if (xinc1 < 0)
+		    yinc1 = -xinc1;
+		else
+		    yinc1 = xinc1;
+		}
+	    else
+		yinc1 = wcsf->yinc;
+	    if (vfit[5] > -1)
+		rot = p[i][vfit[5]];
+	    else
+		rot = wcsf->rot;
+	    fprintf (stderr,"%d: %s %s del=%6.4f,%6.4f rot=%5.3f ",
+		    i, rastr, decstr, 3600.0*xinc1, 3600.0*yinc1, rot);
+	    }
+
+	if (vfit[7] > -1)
+	    xrefpix1 = xrefpix + p[i][vfit[7]];
+	else
+	    xrefpix1 = wcsf->xrefpix;
+	if (vfit[8] > -1)
+	    yrefpix1 = yrefpix + p[i][vfit[8]];
+	else
+	    yrefpix1 = wcsf->yrefpix;
+	fprintf (stderr,"(%8.2f,%8.2f) y=%g\n", xrefpix1, yrefpix1, y[i]);
+	}
+#endif
+
+    amoeba (p, y, nfit, FTOL, nitmax, wcs_chisqr, &iter);
+
+#define	PDUMP
+#ifdef	PDUMP
+    fprintf (stderr,"\nAfter:\n");
+    for (i = 0; i < nfit1; i++) {
+	if (vfit[1] > -1)
+	    ra2str (rastr, 16, p[i][vfit[1]] + xref_p, 3);
+	else
+	    ra2str (rastr, 16, wcsf->xref, 3);
+	if (vfit[2] > -1)
+	    dec2str (decstr, 16, p[i][vfit[2]]+yref_p, 2);
+	else
+	    dec2str (decstr, 16, wcsf->yref, 2);
+	if (vfit[6] > -1) {
+	    cd[0] = p[i][vfit[3]];
+	    cd[1] = p[i][vfit[4]];
+	    cd[2] = p[i][vfit[5]];
+	    cd[3] = p[i][vfit[6]];
+	    fprintf (stderr,"%d: %s %s CD: %7.5f,%7.5f,%7.5f,%7.5f ",
+		    i, rastr, decstr, cd[0],cd[1],cd[2],cd[3]);
+	    }
+	else {
+	    if (vfit[3] > -1)
+		xinc1 = p[i][vfit[3]];
+	    else
+		xinc1 = wcsf->xinc;
+	    if (vfit[4] > -1)
+		yinc1 = p[i][vfit[4]];
+	    else if (vfit[3] > -1) {
+		if (xinc1 < 0)
+		    yinc1 = -xinc1;
+		else
+		    yinc1 = xinc1;
+		}
+	    else
+		yinc1 = wcsf->yinc;
+	    if (vfit[5] > -1)
+		rot = p[i][vfit[5]];
+	    else
+		rot = wcsf->rot;
+	    fprintf (stderr,"%d: %s %s del=%6.4f,%6.4f rot=%5.3f ",
+		    i,rastr,decstr, 3600.0*xinc1, 3600.0*yinc1, rot);
+	    }
+	if (vfit[7] > -1)
+	    xrefpix1 = xrefpix + p[i][vfit[7]];
+	else
+	    xrefpix1 = wcsf->xrefpix;
+	if (vfit[8] > -1)
+	    yrefpix1 = yrefpix + p[i][vfit[8]];
+	else
+	    yrefpix1 = wcsf->yrefpix;
+	fprintf (stderr,"(%8.2f,%8.2f) y=%g\n", xrefpix1, yrefpix1, y[i]);
+	}
+#endif
+
+    /* On return, all entries in p[1..NPAR] are within FTOL;
+     * Return the average, though you could just pick the first one
+     */
+    for (j = 0; j < nfit; j++) {
+	double sum = 0.0;
+	for (i = 0; i < nfit1; i++)
+	    sum += p[i][j];
+	vp[j] = sum / (double)nfit1;
+	}
+    if (vfit[1] > -1) {
+	wcsf->xref = xref_p + vp[vfit[1]];
+	if (wcsf->xref < 0.0) wcsf->xref = 360.0 + wcsf->xref;
+	}
+    if (vfit[2] > -1)
+	wcsf->yref = yref_p + vp[vfit[2]];
+    if (vfit[6] > -1) {
+	wcsf->cd[0] = vp[vfit[3]];
+	wcsf->cd[1] = vp[vfit[4]];
+	wcsf->cd[2] = vp[vfit[5]];
+	wcsf->cd[3] = vp[vfit[6]];
+	}
+    else {
+	if (vfit[3] > -1)
+	    wcsf->xinc = vp[vfit[3]];
+	if (vfit[4] > -1)
+	    wcsf->yinc = vp[vfit[4]];
+	else if (vfit[3] > -1) {
+	    if (wcsf->xinc < 0)
+		wcsf->yinc = -wcsf->xinc;
+	    else
+		wcsf->yinc = wcsf->xinc;
+	    }
+	if (vfit[5] > -1)
+	    wcsf->rot = vp[vfit[5]];
+	}
+    if (vfit[7] > -1)
+	wcsf->xrefpix = xrefpix + vp[vfit[7]];
+    if (vfit[8] > -1)
+	wcsf->yrefpix = yrefpix + vp[vfit[8]];
+
+#define RESIDDUMP
+#ifdef RESIDDUMP
+    ra2str (rastr, 16, wcsf->xref, 3);
+    dec2str (decstr, 16, wcsf->yref, 2);
+
+    if (vfit[6] > -1)
+	fprintf (stderr,"iter=%d\n cra= %s cdec= %s CD=%9.7f,%9.7f,%9.7f,%9.7f ", iter,
+		rastr, decstr, wcsf->cd[0], wcsf->cd[1], wcsf->cd[2],
+		wcsf->cd[3]);
+    else
+	fprintf (stderr,"iter=%d\n cra= %s cdec= %s del=%7.4f,%7.4f rot=%7.4f ", iter,
+		rastr, decstr, wcsf->xinc*3600.0, wcsf->yinc*3600.0, wcsf->rot);
+    fprintf (stderr,"(%8.2f,%8.2f)\n", wcsf->xrefpix, wcsf->yrefpix);
+    sumx = 0.0;
+    sumy = 0.0;
+    sumr = 0.0;
+    for (i = 0; i < nbin_p; i++) {
+	double mra, mdec, ex, ey, er;
+	char rastr[16], decstr[16];
+
+	pix2wcs (wcsf, sx_p[i], sy_p[i], &mra, &mdec);
+	ex = 3600.0 * (mra - gra_p[i]);
+	ey = 3600.0 * (mdec - gdec_p[i]);
+	er = sqrt (ex * ex + ey * ey);
+	sumx = sumx + ex;
+	sumy = sumy + ey;
+	sumr = sumr + er;
+
+	ra2str (rastr, 16, gra_p[i], 3);
+	dec2str (decstr, 16, gdec_p[i], 2);
+	fprintf (stderr,"%2d: c: %s %s ", i+1, rastr, decstr);
+	ra2str (rastr, 16, mra, 3);
+	dec2str (decstr, 16, mdec, 2);
+	fprintf (stderr, "i: %s %s %6.3f %6.3f %6.3f\n",
+		rastr, decstr, 3600.0*ex, 3600.0*ey,
+		3600.0*sqrt(ex*ex + ey*ey));
+	}
+    sumx = sumx / (double)nbin_p;
+    sumy = sumy / (double)nbin_p;
+    sumr = sumr / (double)nbin_p;
+    fprintf (stderr,"mean dra: %6.3f, ddec: %6.3f, dr = %6.3f\n", sumx, sumy, sumr);
+#endif
+}
+
+
+/* Compute the chisqr of the vector v, where
+ * v[0]=cra, v[1]=cdec, v[2]=ra deg/pix, v[3]=dec deg/pix,
+ * v[4]=rotation, v[5]=2nd rotation->CD matrix, v[6]=ref x, and v[7] = ref y
+ * chisqr is in arcsec^2
+ */
+
+static double
+wcs_chisqr (v, iter)
+
+double	*v;	/* Vector of parameter values */
+int	iter;	/* Number of iterations */
+
+{
+    double chsq;
+    char rastr[16],decstr[16];
+    double xmp, ymp, dx, dy, cd[4], *cdx;
+    double crval1, crval2, cdelt1, cdelt2, crota, crpix1, crpix2;
+    int i, offscale;
+
+    /* Set WCS parameters from fit parameter vector */
+
+    /* Sky coordinates at optical axis (degrees) */
+    if (vfit[1] > -1)
+	crval1 = xref_p + v[vfit[1]];
+    else
+	crval1 = wcsf->xref;
+    if (vfit[2] > -1)
+	crval2 = yref_p + v[vfit[2]];
+    else
+	crval2 = wcsf->yref;
+
+    /* CD matrix */
+    if (vfit[6] > -1) {
+	cdelt1 = 0.0;
+	cdelt2 = 0.0;
+	crota = 0.0;
+	cd[0] = v[vfit[3]];
+	cd[1] = v[vfit[4]];
+	cd[2] = v[vfit[5]];
+	cd[3] = v[vfit[6]];
+	cdx = cd;
+	}
+
+    else {
+	/* Plate scale (degrees/pixel) */
+	if (vfit[3] > -1)
+	    cdelt1 = v[vfit[3]];
+	else
+	    cdelt1 = wcsf->xinc;
+	if (vfit[4] > -1)
+	    cdelt2 = v[vfit[4]];
+	else if (vfit[3] > -1) {
+	    if (cdelt1 < 0)
+		cdelt2 = -cdelt1;
+	    else
+		cdelt2 = cdelt1;
+	    }
+	else
+	    cdelt2 = wcsf->yinc;
+
+	/* Rotation angle (degrees) */
+	if (vfit[5] > -1)
+	    crota = v[vfit[5]];
+	else
+	    crota = wcsf->rot;
+	cdx = NULL;
+	}
+
+    /* Optical axis pixel coordinates */
+    if (vfit[7] > -1)
+	crpix1 = xrefpix + v[vfit[7]];
+    else
+	crpix1 = wcsf->xrefpix;
+    if (vfit[8] > -1)
+	crpix2 = yrefpix + v[vfit[8]];
+    else
+	crpix2 = wcsf->yrefpix;
+    if (wcsreset (wcsf,crpix1,crpix2,crval1,crval2,cdelt1,cdelt2,crota,cdx)) {
+	fprintf (stderr,"CHISQR: Cannot reset WCS!\n");
+	return (0.0);
+	}
+
+    /* Compute sum of squared residuals for these parameters */
+    chsq = 0.0;
+    for (i = 0; i < nbin_p; i++) {
+	wcs2pix (wcsf, gra_p[i], gdec_p[i], &xmp, &ymp, &offscale);
+	/* if (!offscale) { */
+	    dx = xmp - sx_p[i];
+	    dy = ymp - sy_p[i];
+	    chsq += dx*dx + dy*dy;
+	    /* } */
+	}
+
+#define TRACE_CHSQR
+#ifdef TRACE_CHSQR
+    ra2str (rastr, 16, wcsf->xref, 3);
+    dec2str (decstr, 16, wcsf->yref, 2);
+    if (vfit[6] > -1)
+	fprintf (stderr,"%4d: %s %s CD: %9.7f,%9.7f,%9.7f,%9.7f ",
+		iter, rastr, decstr, wcsf->cd[0],wcsf->cd[1],wcsf->cd[2],
+		wcsf->cd[3]);
+    else
+	fprintf (stderr,"%4d: %s %s %9.7f,%9.7f %8.5f ",
+		iter, rastr, decstr, wcsf->xinc*3600.0, wcsf->yinc*3600.0,
+		wcsf->rot);
+    fprintf (stderr,"(%8.2f,%8.2f) -> %f\r",
+	     wcsf->xrefpix, wcsf->yrefpix, chsq);
+#endif
+    return (chsq);
+}
+
+/* The following subroutines are based on those in Numerical Recipes in C */
+
+/* amoeba.c */
+
+#define ALPHA 1.0
+#define BETA 0.5
+#define GAMMA 2.0
+
+void
+amoeba (p, y, ndim, ftol, itmax, funk, nfunk)
+
+double	**p;
+double	y[];
+double	ftol;
+int	itmax;
+double	(*funk)();
+int	ndim;
+int	*nfunk;
+
+{
+int i,j,ilo,ihi,inhi,ndim1=ndim+1;
+double ytry,ysave,sum,rtol,*psum;
+
+    psum = (double *) malloc ((unsigned)ndim * sizeof(double));
+    *nfunk = 0;
+    for (j=0; j<ndim; j++) {
+	for (i=0,sum=0.0; i<ndim1; i++)
+	    sum += p[i][j]; psum[j]=sum;
+	}
+    for (;;) {
+	ilo=1;
+	if (y[0] > y[1]) {
+	    inhi = 1;
+	    ihi = 0;
+	    }
+	else {
+	    inhi = 0;
+	    ihi = 1;
+	    }
+	for (i = 0; i < ndim1; i++) {
+	    if (y[i] < y[ilo])
+		ilo=i;
+	    if (y[i] > y[ihi]) {
+		inhi=ihi;
+		ihi=i;
+		}
+	    else if (y[i] > y[inhi])
+		if (i != ihi)
+		    inhi=i;
+	    }
+	rtol = 2.0 * fabs(y[ihi]-y[ilo]) / (fabs(y[ihi]) + fabs(y[ilo]));
+	if (rtol < ftol)
+	    break;
+	if (*nfunk >= itmax) {
+	    fprintf (stderr,"Too many iterations in amoeba fit %d > %d",*nfunk,itmax);
+	    return;
+	    }
+	ytry = amotry (p, y, psum, ndim, funk, ihi, nfunk, -ALPHA);
+	if (ytry <= y[ilo])
+	    ytry = amotry (p, y, psum, ndim, funk, ihi, nfunk, GAMMA);
+	else if (ytry >= y[inhi]) {
+	    ysave = y[ihi];
+	    ytry = amotry (p,y,psum,ndim,funk,ihi,nfunk,BETA);
+	    if (ytry >= ysave) {
+		for (i = 0; i < ndim1; i++) {
+		    if (i != ilo) {
+			for (j = 0; j < ndim; j++) {
+			    psum[j] = 0.5 * (p[i][j] + p[ilo][j]);
+			    p[i][j] = psum[j];
+			    }
+			y[i]=(*funk)(psum, *nfunk);
+			}
+		    }
+		*nfunk += ndim;
+		for (j=0; j<ndim; j++) {
+		    for (i=0,sum=0.0; i<ndim1; i++)
+			sum += p[i][j]; psum[j]=sum;
+		    }
+		}
+	    }
+	}
+    free (psum);
+    return;
+}
+
+
+static double
+amotry (p, y, psum, ndim, funk, ihi, nfunk, fac)
+
+double	**p;
+double	*y;
+double	*psum;
+double	(*funk)();
+double	fac;
+int	ndim;
+int	ihi;
+int	*nfunk;
+
+{
+    int j;
+    double fac1,fac2,ytry,*ptry;
+
+    ptry = (double *) malloc ((unsigned) ndim * sizeof(double));
+    fac1 = (1.0 - fac) / ndim;
+    fac2 = fac1 - fac;
+    for (j = 0; j < ndim; j++)
+	ptry[j] = psum[j] * fac1 - p[ihi][j] * fac2;
+    ytry = (*funk)(ptry, *nfunk);
+    ++(*nfunk);
+    if (ytry < y[ihi]) {
+	y[ihi] = ytry;
+	for (j = 0; j < ndim; j++) {
+    	    psum[j] +=  ptry[j] - p[ihi][j];
+    	    p[ihi][j] = ptry[j];
+	    }
+	}
+    free (ptry);
+    return ytry;
+}
+
+void
+setresid_refine (refine)
+int refine;
+{ resid_refine = refine; return; }
+
+int
+getresid_refine ()
+{ return (resid_refine); }
+
+void
+setnfit (nfit)
+int nfit;
+{
+    if (nfit == 0)
+	setnofit();
+    else if (nfit < 0) {
+	pfit0 = -nfit;
+	resid_refine = 1;
+	}
+    else {
+	pfit0 = nfit;
+	resid_refine = 0;
+	}
+    return;
+}
+
+int
+getnfit ()
+{ return (pfit0); }
+
+int
+iscdfit ()
+{ return (cdfit); }
+
+void
+setminmatch (minmatch)
+int minmatch;
+{ minmatch0 = minmatch; return; }
+
+void
+setminbin (minbin1)
+int minbin1;
+{ minbin = minbin1; return; }
+
+void
+setnitmax (nitmax)
+int nitmax;
+{ nitmax0 = nitmax; return; }
+
+/* Aug  6 1996	New subroutine
+ * Sep  1 1996	Move constants to lwcs.h
+ * Sep  3 1996	Use offscale pixels for chi^2 computation
+ * Sep  3 1996	Overprint chi^2 in verbose mode
+ * Oct 15 1996	Fix am* subroutine declarations
+ * Nov 19 1996	Fix bug regarding rotation
+ *
+ * Jul 21 1997	Add reference pixel position fitting
+ * Aug  4 1997	Increase maximum iterations from 750 to 1000 in lwcs.h
+ * Aug 28 1997	Fix VGUESS dimension bug
+ * Sep  9 1997	Print RA and Dec offsets in residual listing
+ * Sep  9 1997	Turn on resid_refinement if number of parameters to fit negated
+ * Sep  9 1997	Fit separate horizontal and vertical plate scales if nfit=5
+ * Sep  9 1997	Fix bugs associated with fitting optical axis
+ * Sep 12 1997	Add chip rotation instead of second plate scale
+ * Oct  2 1997	Keep second plate scale AND chip rotation
+ * Oct 16 1997	Try to deal with reference pixel position correctly
+ * Nov  5 1997	Select parameters one at a time, in any order
+ * Nov 12 1997	Add PFIT=3 to fit center and plate scale only
+ * Dec 15 1997	Fix minor bugs after lint
+ *
+ * Jan 26 1998	Remove chip rotation code
+ * Jan 29 1998	Streamline initialization code
+ * Feb 19 1998	Fix bug in initialization code
+ * Mar  3 1998	Fix residual-refining code
+ * Mar 20 1998	Add option to fit CD matrix
+ * Mar 25 1998	Make amoeba() externally callable
+ * Mar 26 1998	Return instead of crashing when too many iterations
+ * Apr 21 1998	Drop out of loop if more than half of stars are matched
+ * Apr 27 1998	Fix bug handling nfit=8
+ * Jun 24 1998	Fix bug summing unitialized values for mean after fit
+ * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
+ * Oct  8 1998	Initialize bestdx and bestdy to zero
+ * Dec  8 1998	Fix declaration of amotry()
+ *
+ * Apr 21 1999	Add subroutines to set and retrieve resid_refine independently
+ * Jul 21 1999	Add FitMatch() to fit WCS to already-matched stars
+ * Sep  8 1999	Fix bug found by Jean-Baptiste Marquette
+ * Oct  1 1999	Add ReadMatch() to read a set of matches from a file
+ * Oct 20 1999	Include wcscat.h
+ *
+ * Feb 15 2000	Add iscdfit() to return whether CD matrix is being fit
+ * Mar 10 2000	Add debug statement to list max matches as they are found
+ * Mar 10 2000	Change loop order to image stars first
+ * Dec 18 2000	Write half of ReadMatch() to deal with ASCII files
+ *
+ * Jan  2 2001	Modify ReadMatch() to read hh mm ss dd mm ss, too
+ * Jan  9 2001	Work on FitMatch()
+ * Jan 11 2001	All diagnostic printing goes to stderr
+ * Feb 28 2001	Ignore coordinate system if present after match file coordinates
+ * Jun 18 2001	Add maximum length of returned string to getoken()
+ * Aug  2 2001	Separate parameter listing and counting into subroutines
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ * Sep 24 2001	Ease match numeric criterium if half num is > 40
+ * Oct 15 2001	Simplify error message
+ * Oct 16 2001	Read minimum match to drop out of loop from lwcs.h
+ * Oct 31 2001	Simplify innermost loop to try for more speed
+ * Nov  1 2001	Add goff to StarMatch() arguments
+ * Nov  5 2001	Use current WCS with no offset before trying offset matching
+ * Nov  6 2001	Add setnitmax() to set maximum number of amoeba iterations
+ * Nov  7 2001	Add setminbin to set minimum number of matches for fit
+ * Nov 16 2001	Allocate slightly more than maxbin to handle dense fields
+ *
+ * Jul 31 2002	Add getnfit() to return current number of parameters being fit
+ * Aug 30 2002	Fix WCSMatch() to set scale in arcsec, not degrees
+ *
+ * Jan 30 2003	Remove uninitialized variable in WCSMatch()
+ * Mar 13 2003	Do not include malloc.h on Apples and Convexes
+ * Apr  3 2003	Clean up code with lint
+ * Nov 18 2003	Drop include of malloc.h; it is in stdlib.h
+ *
+ * Aug 30 2004	Declare void various external set*() calls
+ *
+ * Jun 19 2006	Initialize unitialized variables dxs and dys
+ */ 
diff --git a/Code/src/libwcs/nut1981.c b/Code/src/libwcs/nut1981.c
new file mode 100644
index 0000000000000000000000000000000000000000..cecaa9d7529dc1d7f53fbe937f508799d7d5dd1b
--- /dev/null
+++ b/Code/src/libwcs/nut1981.c
@@ -0,0 +1,521 @@
+
+
+/* Compute the longitude and obliquity components of nutation and
+ * mean obliquity from the IAU 1980 theory
+ * References:
+ *    Final Report of the IAU Working Group on Nutation,
+ *    Chairman P.K.Seidelmann, 1980.
+ *    Kaplan,G.H., 1981, USNO Circular No. 163, pa3-6.
+ *
+ * From Fortran code by P.T. Wallace   Starlink   september 1987
+ */
+
+void
+compnut (dj, dpsi, deps, eps0)
+
+double dj;	/* TDB (loosely ET or TT) as Julian Date */
+double *dpsi;	/* Nutation in longitude in radians (returned) */
+double *deps;	/* Nutation in obliquity in radians (returned) */
+double *eps0;	/* Mean obliquity in radians (returned) */
+{
+    double t2as,as2r,u2r;
+    double t,el,el2,el3;
+    double elp,elp2;
+    double f,f2,f4;
+    double d,d2,d4;
+    double om,om2;
+    double dp,de;
+    double a;
+
+    /* Turns to arc seconds */
+    t2as = 1296000.0;
+
+    /* Arc seconds to radians */
+    as2r = 0.000004848136811095359949;
+
+    /* Units of 0.0001 arcsec to radians */
+    u2r = as2r / 10000.0;
+
+    /* Basic epoch J2000.0 to current epoch in Julian Centuries */
+    t = (dj - 2400000.5  - 51544.5 ) / 36525.0;
+
+    /* Fundamental arguments in the FK5 reference system */
+
+    /* mean longitude of the moon minus mean longitude of the moon's perigee */
+    el = as2r*(485866.733 + (1325.0 * t2as+715922.633 + (31.310 +0.064*t)*t)*t);
+
+    /* mean longitude of the sun minus mean longitude of the sun's perigee */
+    elp = as2r*(1287099.804 + (99.0 * t2as+1292581.224 + (-0.577 -0.012*t)*t)*t);
+
+    /* mean longitude of the moon minus mean longitude of the moon's node */
+    f = as2r*(335778.877 + (1342.0 * t2as+295263.137 + (-13.257 + 0.011*t)*t)*t);
+
+    /* mean elongation of the moon from the sun */
+    d = as2r*(1072261.307 + (1236.0 * t2as+1105601.328 + (-6.891 + 0.019*t)*t)*t);
+
+    /* longitude of the mean ascending node of the lunar orbit on the */
+    /*  ecliptic, measured from the mean equinox of date */
+    om = as2r * (450160.280 + (-5.0 * t2as-482890.539 + (7.455 +0.008*t)*t)*t);
+
+    /* Multiples of arguments */
+    el2 = el + el;
+    el3 = el2 + el;
+    elp2 = elp + elp;
+    f2 = f + f;
+    f4 = f2 + f2;
+    d2 = d + d;
+    d4 = d2 + d2;
+    om2 = om + om;
+
+    /* Series for the nutation */
+    dp = 0.0;
+    de = 0.0;
+
+    /* 106 */
+    dp = dp + sin (elp+d);
+
+    /* 105 */
+    dp = dp - sin (f2 + d4 + om2);
+
+    /* 104 */
+    dp = dp + sin (el2 + d2);
+
+    /* 103 */
+    dp = dp - sin (el - f2 + d2);
+
+    /* 102 */
+    dp = dp - sin (el + elp - d2 + om);
+
+    /* 101 */
+    dp = dp - sin (-elp + f2 + om);
+
+    /* 100 */
+    dp = dp - sin (el - f2 - d2);
+
+    /* 99 */
+    dp = dp - sin (elp + d2);
+
+    /* 98 */
+    dp = dp - sin (f2 - d + om2);
+
+    /* 97 */
+    dp = dp - sin (-f2 + om);
+
+    /* 96 */
+    dp = dp + sin (-el - elp + d2 + om);
+
+    /* 95 */
+    dp = dp + sin (elp + f2 + om);
+
+    /* 94 */
+    dp = dp - sin (el + f2 - d2);
+
+    /* 93 */
+    dp = dp + sin(el3 + f2 - d2 + om2);
+
+    /* 92 */
+    dp = dp + sin(f4 - d2 + om2);
+
+    /* 91 */
+    dp = dp - sin(el + d2 + om);
+
+    /* 90 */
+    dp = dp - sin(el2 + f2 + d2 + om2);
+
+    /* 89 */
+    a = el2 + f2 - d2 + om;
+    dp = dp + sin(a);
+    de = de - cos(a);
+
+    /* 88 */
+    dp = dp + (sin(el - elp - d2));
+
+    /* 87 */
+    dp = dp + (sin(-el + f4 + om2));
+
+    /* 86 */
+    a = -el2 + f2 + d4 + om2;
+    dp = dp - sin(a);
+    de = de + cos(a);
+
+    /* 85 */
+    a = el + f2 + d2 + om;
+    dp = dp - sin(a);
+    de = de + cos(a);
+
+    /* 84 */
+    a = el + elp + f2 - d2 + om2;
+    dp = dp + sin(a);
+    de = de - cos(a);
+
+    /* 83 */
+    dp = dp - sin(el2 - d4);
+
+    /* 82 */
+    a = -el + f2 + d4 + om2;
+    dp = dp - (2.0 * sin(a));
+    de = de + cos(a);
+
+    /* 81 */
+    a = -el2 + f2 + d2 + om2;
+    dp = dp + sin(a);
+    de = de - cos(a);
+
+    /* 80 */
+    dp = dp - sin(el - d4);
+
+    /* 79 */
+    a = -el + om2;
+    dp = dp + sin(a);
+    de = de - cos(a);
+
+    /* 78 */
+    a = f2 + d + om2;
+    dp = dp + (2.0 * sin(a));
+    de = de - cos(a);
+
+    /* 77 */
+    dp = dp + (2.0 * sin(el3));
+
+    /* 76 */
+    a = el + om2;
+    dp = dp - (2.0 * sin(a));
+    de = de + cos(a);
+
+    /* 75 */
+    a = el2 + om;
+    dp = dp + (2.0 * sin(a));
+    de = de - cos(a);
+
+    /* 74 */
+    a =  - el + f2 - d2 + om;
+    dp = dp - (2.0 * sin(a));
+    de = de + cos(a);
+
+    /* 73 */
+    a = el + elp + f2 + om2;
+    dp = dp + (2.0 * sin(a));
+    de = de - cos(a);
+
+    /* 72 */
+    a = -elp + f2 + d2 + om2;
+    dp = dp - (3.0 * sin(a));
+    de = de + cos(a);
+
+    /* 71 */
+    a = el3 + f2 + om2;
+    dp = dp - (3.0 * sin(a));
+    de = de + cos(a);
+
+    /* 70 */
+    a = -el2 + om;
+    dp = dp - (2.0 * sin(a));
+    de = de + cos(a);
+
+    /* 69 */
+    a = -el - elp + f2 + d2 + om2;
+    dp = dp - (3.0 * sin(a));
+    de = de + cos(a);
+
+    /* 68 */
+    a = el - elp + f2 + om2;
+    dp = dp - (3.0 * sin(a));
+    de = de + cos(a);
+
+    /* 67 */
+    dp = dp + (3.0 * sin(el + f2));
+
+    /* 66 */
+    dp = dp - (3.0 * sin(el + elp));
+
+    /* 65 */
+    dp = dp - (4.0 * sin(d));
+
+    /* 64 */
+    dp = dp + (4.0 * sin(el - f2));
+
+    /* 63 */
+    dp = dp - (4.0 * sin(elp - d2));
+
+    /* 62 */
+    a = el2 + f2 + om;
+    dp = dp - (5.0 * sin(a));
+    de = de + (3.0 * cos(a));
+
+    /* 61 */
+    dp = dp + (5.0 * sin(el - elp));
+
+    /* 60 */
+    a = -d2 + om;
+    dp = dp - (5.0 * sin(a));
+    de = de + (3.0 * cos(a));
+
+    /* 59 */
+    a = el + f2 - d2 + om;
+    dp = dp + (6.0 * sin(a));
+    de = de - (3.0 * cos(a));
+
+    /* 58 */
+    a = f2 + d2 + om;
+    dp = dp - (7.0 * sin(a));
+    de = de + (3.0 * cos(a));
+
+    /* 57 */
+    a = d2 + om;
+    dp = dp - (6.0 * sin(a));
+    de = de + (3.0 * cos(a));
+
+    /* 56 */
+    a = el2 + f2 - d2 + om2;
+    dp = dp + (6.0 * sin(a));
+    de = de - (3.0 * cos(a));
+
+    /* 55 */
+    dp = dp + (6.0 * sin(el + d2));
+
+    /* 54 */
+    a = el + f2 + d2 + om2;
+    dp = dp - (8.0 * sin(a));
+    de = de + (3.0 * cos(a));
+
+    /* 53 */
+    a = -elp + f2 + om2;
+    dp = dp - (7.0 * sin(a));
+    de = de + (3.0 * cos(a));
+
+    /* 52 */
+    a = elp + f2 + om2;
+    dp = dp + (7.0 * sin(a));
+    de = de - (3.0 * cos(a));
+
+    /* 51 */
+    dp = dp - (7.0 * sin(el + elp - d2));
+
+    /* 50 */
+    a = -el + f2 + d2 + om;
+    dp = dp - (10.0 * sin(a));
+    de = de + (5.0 * cos(a));
+
+    /* 49 */
+    a = el - d2 + om;
+    dp = dp - (13.0 * sin(a));
+    de = de + (7.0 * cos(a));
+
+    /* 48 */
+    a = -el + d2 + om;
+    dp = dp + (16.0 * sin(a));
+    de = de - (8.0 * cos(a));
+
+    /* 47 */
+    a =  - el + f2 + om;
+    dp = dp + (21.0 * sin(a));
+    de = de - (10.0 * cos(a));
+
+    /* 46 */
+    dp = dp + (26.0 * sin(f2));
+    de = de - cos(f2);
+
+    /* 45 */
+    a = el2 + f2 + om2;
+    dp = dp - (31.0 * sin(a));
+    de = de + (13.0 * cos(a));
+
+    /* 44 */
+    a = el + f2 - d2 + om2;
+    dp = dp + (29.0 * sin(a));
+    de = de - (12.0 * cos(a));
+
+    /* 43 */
+    dp = dp + (29.0 * sin(el2));
+    de = de - cos(el2);
+
+    /* 42 */
+    a = f2 + d2 + om2;
+    dp = dp - (38.0 * sin(a));
+    de = de + (16.0 * cos(a));
+
+    /* 41 */
+    a = el + f2 + om;
+    dp = dp - (51.0 * sin(a));
+    de = de + (27.0 * cos(a));
+
+    /* 40 */
+    a =  -el + f2 + d2 + om2;
+    dp = dp - (59.0 * sin(a));
+    de = de + (26.0 * cos(a));
+
+    /* 39 */
+    a =  -el + om;
+    dp = dp + ((-58.0 - 0.1 * t) * sin(a));
+    de = de + (32.0 * cos(a));
+
+    /* 38 */
+    a = el + om;
+    dp = dp + ((63.0 + 0.1 * t) * sin(a));
+    de = de - (33.0 * cos(a));
+
+    /* 37 */
+    dp = dp + (63.0 * sin(d2));
+    de = de - (2.0 * cos(d2));
+
+    /* 36 */
+    a =  -el + f2 + om2;
+    dp = dp + (123.0 * sin(a));
+    de = de - (53.0 * cos(a));
+
+    /* 35 */
+    a = el - d2;
+    dp = dp - (158.0 * sin(a));
+    de = de - cos(a);
+
+    /* 34 */
+    a = el + f2 + om2;
+    dp = dp - (301.0 * sin(a));
+    de = de + ((129.0 - 0.1 * t) * cos(a));
+
+    /* 33 */
+    a = f2 + om;
+    dp = dp + ((-386.0  - 0.4 * t) * sin(a));
+    de = de + (200.0 * cos(a));
+
+    /* 32 */
+    dp = dp + ((712.0  + 0.1 * t) * sin(el));
+    de = de - (7.0 * cos(el));
+
+    /* 31 */
+    a = f2 + om2;
+    dp = dp + ((-2274.0  - 0.2 * t) * sin(a));
+    de = de + ((977.0  - 0.5 * t) * cos(a));
+
+    /* 30 */
+    dp = dp - sin(elp + f2 - d2);
+
+    /* 29 */
+    dp = dp + sin(-el + d + om);
+
+    /* 28 */
+    dp = dp + sin(elp + om2);
+
+    /* 27 */
+    dp = dp - sin(elp - f2 + d2);
+
+    /* 26 */
+    dp = dp + sin(-f2 + d2 + om);
+
+    /* 25 */
+    dp = dp + sin(el2 + elp - d2);
+
+    /* 24 */
+    dp = dp - (4.0 * sin(el - d));
+
+    /* 23 */
+    a = elp + f2 - d2 + om;
+    dp = dp + (4.0 * sin(a));
+    de = de - (2.0 * cos(a));
+
+    /* 22 */
+    a = el2 - d2 + om;
+    dp = dp + (4.0 * sin(a));
+    de = de - (2.0 * cos(a));
+
+    /* 21 */
+    a = -elp + f2 - d2 + om;
+    dp = dp - (5.0 * sin(a));
+    de = de + (3.0 * cos(a));
+
+    /* 20 */
+    a = -el2 + d2 + om;
+    dp = dp - (6.0 * sin(a));
+    de = de + (3.0 * cos(a));
+
+    /* 19 */
+    a = -elp + om;
+    dp = dp - (12.0 * sin(a));
+    de = de + (6.0 * cos(a));
+
+    /* 18 */
+    a = elp2 + f2 - d2 + om2;
+    dp = dp + ((-16.0  + (0.1 * t)) * sin(a));
+    de = de + (7.0 * cos(a));
+
+    /* 17 */
+    a = elp + om;
+    dp = dp - (15.0 * sin(a));
+    de = de + (9.0 * cos(a));
+
+    /* 16 */
+    dp = dp + ((17.0  - (0.1 * t)) * sin(elp2));
+
+    /* 15 */
+    dp = dp - (22.0 * sin(f2 - d2));
+
+    /* 14 */
+    a = el2 - d2;
+    dp = dp + (48.0 * sin(a));
+    de = de + cos(a);
+
+    /* 13 */
+    a = f2 - d2 + om;
+    dp = dp + ((129.0 + (0.1 * t)) * sin(a));
+    de = de - (70.0 * cos(a));
+
+    /* 12 */
+    a =  - elp + f2 - d2 + om2;
+    dp = dp + ((217.0  - 0.5 * t) * sin(a));
+    de = de + ((-95.0  + 0.3 * t) * cos(a));
+
+    /* 11 */
+    a = elp + f2 - d2 + om2;
+    dp = dp + ((-517.0  + (1.2 * t)) * sin(a));
+    de = de + ((224.0  - (0.6 * t)) * cos(a));
+
+    /* 10 */
+    dp = dp + ((1426.0 - (3.4 * t)) * sin(elp));
+    de = de + ((54.0 - (0.1 * t)) * cos(elp));
+
+    /* 9 */
+    a = f2 - d2 + om2;
+    dp = dp + ((-13187.0  - (1.6 * t)) * sin(a));
+    de = de + ((5736.0  - (3.1 * t)) * cos(a));
+
+    /* 8 */
+    dp = dp + sin(el2 - f2 + om);
+
+    /* 7 */
+    a =  -elp2 + f2 - d2 + om;
+    dp = dp - (2.0 * sin(a));
+    de = de + (1.0 * cos(a));
+
+    /* 6 */
+    dp = dp - (3.0 * sin(el - elp - d));
+
+    /* 5 */
+    a =  - el2 + f2 + om2;
+    dp = dp - (3.0 * sin(a));
+    de = de + (1.0 * cos(a));
+
+    /* 4 */
+    dp = dp + (11.0 * sin (el2 - f2));
+
+    /* 3 */
+    a =  - el2 + f2 + om;
+    dp = dp + (46.0 * sin(a));
+    de = de - (24.0 * cos(a));
+
+    /*  2 */
+    dp = dp + ((2062.0 + (0.2 * t)) * sin(om2));
+    de = de + ((-895.0 + (0.5 * t)) * cos(om2));
+
+    /* 1 */
+    dp = dp + ((-171996.0 - (174.2 * t)) * sin(om));
+    de = de + ((92025.0 + (8.9 * t)) * cos(om));
+
+    /* Convert results to radians */
+    *dpsi = dp * u2r;
+    *deps = de * u2r;
+
+    /* Mean Obliquity in radians */
+    *eps0 = as2r * (84381.448 + (-46.8150 + (-0.00059 + (0.001813*t)*t)*t));
+
+    return;
+}
diff --git a/Code/src/libwcs/nut2006.c b/Code/src/libwcs/nut2006.c
new file mode 100644
index 0000000000000000000000000000000000000000..00913fcddae7d0554eb92644bbd3174e9753130f
--- /dev/null
+++ b/Code/src/libwcs/nut2006.c
@@ -0,0 +1,362 @@
+/*  Nutation, IAU 2000b model */
+/*  Translated from Pat Wallace's Fortran subroutine iau_nut00b (June 26 2007)
+    into C by Doug Mink on September 5, 2008 */
+
+#define NLS	77 /* number of terms in the luni-solar nutation model */
+
+void
+compnut (dj, dpsi, deps, eps0)
+
+double	dj;	/* Julian Date */
+double *dpsi;   /* Nutation in longitude in radians (returned) */
+double *deps;   /* Nutation in obliquity in radians (returned) */
+double *eps0;   /* Mean obliquity in radians (returned) */
+
+/*  This routine is translated from the International Astronomical Union's
+ *  SOFA (Standards Of Fundamental Astronomy) software collection.
+ *
+ *  notes:
+ *
+ *  1) the nutation components in longitude and obliquity are in radians
+ *     and with respect to the equinox and ecliptic of date.  the
+ *     obliquity at j2000 is assumed to be the lieske et al. (1977) value
+ *     of 84381.448 arcsec.  (the errors that result from using this
+ *     routine with the iau 2006 value of 84381.406 arcsec can be
+ *     neglected.)
+ *
+ *     the nutation model consists only of luni-solar terms, but includes
+ *     also a fixed offset which compensates for certain long-period
+ *     planetary terms (note 7).
+ *
+ *  2) this routine is an implementation of the iau 2000b abridged
+ *     nutation model formally adopted by the iau general assembly in
+ *     2000.  the routine computes the mhb_2000_short luni-solar nutation
+ *     series (luzum 2001), but without the associated corrections for
+ *     the precession rate adjustments and the offset between the gcrs
+ *     and j2000 mean poles.
+ *
+ *  3) the full IAU 2000a (mhb2000) nutation model contains nearly 1400
+ *     terms.  the IAU 2000b model (mccarthy & luzum 2003) contains only
+ *     77 terms, plus additional simplifications, yet still delivers
+ *     results of 1 mas accuracy at present epochs.  this combination of
+ *     accuracy and size makes the IAU 2000b abridged nutation model
+ *     suitable for most practical applications.
+ *
+ *     the routine delivers a pole accurate to 1 mas from 1900 to 2100
+ *     (usually better than 1 mas, very occasionally just outside 1 mas).
+ *     the full IAU 2000a model, which is implemented in the routine
+ *     iau_nut00a (q.v.), delivers considerably greater accuracy at
+ *     current epochs;  however, to realize this improved accuracy,
+ *     corrections for the essentially unpredictable free-core-nutation
+ *     (fcn) must also be included.
+ *
+ *  4) the present routine provides classical nutation.  the
+ *     mhb_2000_short algorithm, from which it is adapted, deals also
+ *     with (i) the offsets between the gcrs and mean poles and (ii) the
+ *     adjustments in longitude and obliquity due to the changed
+ *     precession rates.  these additional functions, namely frame bias
+ *     and precession adjustments, are supported by the sofa routines
+ *     iau_bi00 and iau_pr00.
+ *
+ *  6) the mhb_2000_short algorithm also provides "total" nutations,
+ *     comprising the arithmetic sum of the frame bias, precession
+ *     adjustments, and nutation (luni-solar + planetary).  these total
+ *     nutations can be used in combination with an existing IAU 1976
+ *     precession implementation, such as iau_pmat76, to deliver gcrs-to-
+ *     true predictions of mas accuracy at current epochs.  however, for
+ *     symmetry with the iau_nut00a routine (q.v. for the reasons), the
+ *     sofa routines do not generate the "total nutations" directly.
+ *     should they be required, they could of course easily be generated
+ *     by calling iau_bi00, iau_pr00 and the present routine and adding
+ *     the results.
+ *
+ *  7) the IAU 2000b model includes "planetary bias" terms that are fixed
+ *     in size but compensate for long-period nutations.  the amplitudes
+ *     quoted in mccarthy & luzum (2003), namely dpsi = -1.5835 mas and
+ *     depsilon = +1.6339 mas, are optimized for the "total nutations"
+ *     method described in note 6.  the luzum (2001) values used in this
+ *     sofa implementation, namely -0.135 mas and +0.388 mas, are
+ *     optimized for the "rigorous" method, where frame bias, precession
+ *     and nutation are applied separately and in that order.  during the
+ *     interval 1995-2050, the sofa implementation delivers a maximum
+ *     error of 1.001 mas (not including fcn).
+ *
+ *  References from original Fortran subroutines:
+ *
+ *     Hilton, J. et al., 2006, Celest.Mech.Dyn.Astron. 94, 351
+ *
+ *     Lieske, J.H., Lederle, T., Fricke, W., Morando, B., "Expressions
+ *     for the precession quantities based upon the IAU 1976 system of
+ *     astronomical constants", Astron.Astrophys. 58, 1-2, 1-16. (1977)
+ *
+ *     Luzum, B., private communication, 2001 (Fortran code
+ *     mhb_2000_short)
+ *
+ *     McCarthy, D.D. & Luzum, B.J., "An abridged model of the
+ *     precession-nutation of the celestial pole", Cel.Mech.Dyn.Astron.
+ *     85, 37-49 (2003)
+ *
+ *     Simon, J.-L., Bretagnon, P., Chapront, J., Chapront-Touze, M.,
+ *     Francou, G., Laskar, J., Astron.Astrophys. 282, 663-683 (1994)
+ *
+ */
+
+{ 
+    double as2r = 0.000004848136811095359935899141; /* arcseconds to radians */
+
+    double dmas2r = as2r / 1000.0;	/* milliarcseconds to radians */
+
+    double as2pi = 1296000.0;	/* arc seconds in a full circle */
+
+    double d2pi = 6.283185307179586476925287; /* 2pi */
+
+    double u2r = as2r / 10000000.0;  /* units of 0.1 microarcsecond to radians */
+
+    double dj0 = 2451545.0;	/* reference epoch (j2000), jd */
+
+    double djc = 36525.0;	/* Days per julian century */
+
+    /*  Miscellaneous */
+    double t, el, elp, f, d, om, arg, dp, de, sarg, carg;
+    double dpsils, depsls, dpsipl, depspl;
+    int i, j;
+
+    int nls = NLS; /* number of terms in the luni-solar nutation model */
+
+    /* Fixed offset in lieu of planetary terms (radians) */
+    double dpplan = - 0.135 * dmas2r;
+    double deplan = + 0.388 * dmas2r;
+
+/* Tables of argument and term coefficients */
+
+    /* Coefficients for fundamental arguments
+    /* Luni-solar argument multipliers: */
+    /*       l     l'    f     d     om */
+static int nals[5*NLS]=
+	    {0,    0,    0,    0,    1,
+             0,    0,    2,   -2,    2,
+             0,    0,    2,    0,    2,
+             0,    0,    0,    0,    2,
+             0,    1,    0,    0,    0,
+             0,    1,    2,   -2,    2,
+             1,    0,    0,    0,    0,
+             0,    0,    2,    0,    1,
+             1,    0,    2,    0,    2,
+             0,   -1,    2,   -2,    2,
+             0,    0,    2,   -2,    1,
+            -1,    0,    2,    0,    2,
+            -1,    0,    0,    2,    0,
+             1,    0,    0,    0,    1,
+            -1,    0,    0,    0,    1,
+            -1,    0,    2,    2,    2,
+             1,    0,    2,    0,    1,
+            -2,    0,    2,    0,    1,
+             0,    0,    0,    2,    0,
+             0,    0,    2,    2,    2,
+             0,   -2,    2,   -2,    2,
+            -2,    0,    0,    2,    0,
+             2,    0,    2,    0,    2,
+             1,    0,    2,   -2,    2,
+            -1,    0,    2,    0,    1,
+             2,    0,    0,    0,    0,
+             0,    0,    2,    0,    0,
+             0,    1,    0,    0,    1,
+            -1,    0,    0,    2,    1,
+             0,    2,    2,   -2,    2,
+             0,    0,   -2,    2,    0,
+             1,    0,    0,   -2,    1,
+             0,   -1,    0,    0,    1,
+            -1,    0,    2,    2,    1,
+             0,    2,    0,    0,    0,
+             1,    0,    2,    2,    2,
+            -2,    0,    2,    0,    0,
+             0,    1,    2,    0,    2,
+             0,    0,    2,    2,    1,
+             0,   -1,    2,    0,    2,
+             0,    0,    0,    2,    1,
+             1,    0,    2,   -2,    1,
+             2,    0,    2,   -2,    2,
+            -2,    0,    0,    2,    1,
+             2,    0,    2,    0,    1,
+             0,   -1,    2,   -2,    1,
+             0,    0,    0,   -2,    1,
+            -1,   -1,    0,    2,    0,
+             2,    0,    0,   -2,    1,
+             1,    0,    0,    2,    0,
+             0,    1,    2,   -2,    1,
+             1,   -1,    0,    0,    0,
+            -2,    0,    2,    0,    2,
+             3,    0,    2,    0,    2,
+             0,   -1,    0,    2,    0,
+             1,   -1,    2,    0,    2,
+             0,    0,    0,    1,    0,
+            -1,   -1,    2,    2,    2,
+            -1,    0,    2,    0,    0,
+             0,   -1,    2,    2,    2,
+            -2,    0,    0,    0,    1,
+             1,    1,    2,    0,    2,
+             2,    0,    0,    0,    1,
+            -1,    1,    0,    1,    0,
+             1,    1,    0,    0,    0,
+             1,    0,    2,    0,    0,
+            -1,    0,    2,   -2,    1,
+             1,    0,    0,    0,    2,
+            -1,    0,    0,    1,    0,
+             0,    0,    2,    1,    2,
+            -1,    0,    2,    4,    2,
+            -1,    1,    0,    1,    1,
+             0,   -2,    2,   -2,    1,
+             1,    0,    2,    2,    1,
+            -2,    0,    2,    2,    2,
+            -1,    0,    0,    0,    2,
+             1,    1,    2,   -2,    2};
+
+    /* Luni-solar nutation coefficients, in 1e-7 arcsec */
+    /* longitude (sin, t*sin, cos), obliquity (cos, t*cos, sin) */
+static double cls[6*NLS]=
+   {-172064161.0, -174666.0,  33386.0, 92052331.0,  9086.0, 15377.0,
+     -13170906.0,   -1675.0, -13696.0,  5730336.0, -3015.0, -4587.0,
+      -2276413.0,    -234.0,   2796.0,   978459.0,  -485.0,  1374.0,
+       2074554.0,     207.0,   -698.0,  -897492.0,   470.0,  -291.0,
+       1475877.0,   -3633.0,  11817.0,    73871.0,  -184.0, -1924.0,
+       -516821.0,    1226.0,   -524.0,   224386.0,  -677.0,  -174.0,
+        711159.0,      73.0,   -872.0,    -6750.0,     0.0,   358.0,
+       -387298.0,    -367.0,    380.0,   200728.0,    18.0,   318.0,
+       -301461.0,     -36.0,    816.0,   129025.0,   -63.0,   367.0,
+        215829.0,    -494.0,    111.0,   -95929.0,   299.0,   132.0,
+        128227.0,     137.0,    181.0,   -68982.0,    -9.0,    39.0,
+        123457.0,      11.0,     19.0,   -53311.0,    32.0,    -4.0,
+        156994.0,      10.0,   -168.0,    -1235.0,     0.0,    82.0,
+         63110.0,      63.0,     27.0,   -33228.0,     0.0,    -9.0,
+        -57976.0,     -63.0,   -189.0,    31429.0,     0.0,   -75.0,
+        -59641.0,     -11.0,    149.0,    25543.0,   -11.0,    66.0,
+        -51613.0,     -42.0,    129.0,    26366.0,     0.0,    78.0,
+         45893.0,      50.0,     31.0,   -24236.0,   -10.0,    20.0,
+         63384.0,      11.0,   -150.0,    -1220.0,     0.0,    29.0,
+        -38571.0,      -1.0,    158.0,    16452.0,   -11.0,    68.0,
+         32481.0,       0.0,      0.0,   -13870.0,     0.0,     0.0,
+        -47722.0,       0.0,    -18.0,      477.0,     0.0,   -25.0,
+        -31046.0,      -1.0,    131.0,    13238.0,   -11.0,    59.0,
+         28593.0,       0.0,     -1.0,   -12338.0,    10.0,    -3.0,
+         20441.0,      21.0,     10.0,   -10758.0,     0.0,    -3.0,
+         29243.0,       0.0,    -74.0,     -609.0,     0.0,    13.0,
+         25887.0,       0.0,    -66.0,     -550.0,     0.0,    11.0,
+        -14053.0,     -25.0,     79.0,     8551.0,    -2.0,   -45.0,
+         15164.0,      10.0,     11.0,    -8001.0,     0.0,    -1.0,
+        -15794.0,      72.0,    -16.0,     6850.0,   -42.0,    -5.0,
+         21783.0,       0.0,     13.0,     -167.0,     0.0,    13.0,
+        -12873.0,     -10.0,    -37.0,     6953.0,     0.0,   -14.0,
+        -12654.0,      11.0,     63.0,     6415.0,     0.0,    26.0,
+        -10204.0,       0.0,     25.0,     5222.0,     0.0,    15.0,
+         16707.0,     -85.0,    -10.0,      168.0,    -1.0,    10.0,
+         -7691.0,       0.0,     44.0,     3268.0,     0.0,    19.0,
+        -11024.0,       0.0,    -14.0,      104.0,     0.0,     2.0,
+          7566.0,     -21.0,    -11.0,    -3250.0,     0.0,    -5.0,
+         -6637.0,     -11.0,     25.0,     3353.0,     0.0,    14.0,
+         -7141.0,      21.0,      8.0,     3070.0,     0.0,     4.0,
+         -6302.0,     -11.0,      2.0,     3272.0,     0.0,     4.0,
+          5800.0,      10.0,      2.0,    -3045.0,     0.0,    -1.0,
+          6443.0,       0.0,     -7.0,    -2768.0,     0.0,    -4.0,
+         -5774.0,     -11.0,    -15.0,     3041.0,     0.0,    -5.0,
+         -5350.0,       0.0,     21.0,     2695.0,     0.0,    12.0,
+         -4752.0,     -11.0,     -3.0,     2719.0,     0.0,    -3.0,
+         -4940.0,     -11.0,    -21.0,     2720.0,     0.0,    -9.0,
+          7350.0,       0.0,     -8.0,      -51.0,     0.0,     4.0,
+          4065.0,       0.0,      6.0,    -2206.0,     0.0,     1.0,
+          6579.0,       0.0,    -24.0,     -199.0,     0.0,     2.0,
+          3579.0,       0.0,      5.0,    -1900.0,     0.0,     1.0,
+          4725.0,       0.0,     -6.0,      -41.0,     0.0,     3.0,
+         -3075.0,       0.0,     -2.0,     1313.0,     0.0,    -1.0,
+         -2904.0,       0.0,     15.0,     1233.0,     0.0,     7.0,
+          4348.0,       0.0,    -10.0,      -81.0,     0.0,     2.0,
+         -2878.0,       0.0,      8.0,     1232.0,     0.0,     4.0,
+         -4230.0,       0.0,      5.0,      -20.0,     0.0,    -2.0,
+         -2819.0,       0.0,      7.0,     1207.0,     0.0,     3.0,
+         -4056.0,       0.0,      5.0,       40.0,     0.0,    -2.0,
+         -2647.0,       0.0,     11.0,     1129.0,     0.0,     5.0,
+         -2294.0,       0.0,    -10.0,     1266.0,     0.0,    -4.0,
+          2481.0,       0.0,     -7.0,    -1062.0,     0.0,    -3.0,
+          2179.0,       0.0,     -2.0,    -1129.0,     0.0,    -2.0,
+          3276.0,       0.0,      1.0,       -9.0,     0.0,     0.0,
+         -3389.0,       0.0,      5.0,       35.0,     0.0,    -2.0,
+          3339.0,       0.0,    -13.0,     -107.0,     0.0,     1.0,
+         -1987.0,       0.0,     -6.0,     1073.0,     0.0,    -2.0,
+         -1981.0,       0.0,      0.0,      854.0,     0.0,     0.0,
+          4026.0,       0.0,   -353.0,     -553.0,     0.0,  -139.0,
+          1660.0,       0.0,     -5.0,     -710.0,     0.0,    -2.0,
+         -1521.0,       0.0,      9.0,      647.0,     0.0,     4.0,
+          1314.0,       0.0,      0.0,     -700.0,     0.0,     0.0,
+         -1283.0,       0.0,      0.0,      672.0,     0.0,     0.0,
+         -1331.0,       0.0,      8.0,      663.0,     0.0,     4.0,
+          1383.0,       0.0,     -2.0,     -594.0,     0.0,    -2.0,
+          1405.0,       0.0,      4.0,     -610.0,     0.0,     2.0,
+          1290.0,       0.0,      0.0,     -556.0,     0.0,     0.0};
+
+    /* Interval between fundamental epoch J2000.0 and given date (JC) */
+    t = (dj - dj0) / djc;
+
+/* Luni-solar nutation */
+
+/* Fundamental (delaunay) arguments from Simon et al. (1994) */
+
+    /* Mean anomaly of the moon */
+    el  = fmod (485868.249036 + (1717915923.2178 * t), as2pi) * as2r;
+
+    /* Mean anomaly of the sun */
+    elp = fmod (1287104.79305 + (129596581.0481 * t), as2pi) * as2r;
+
+    /* Mean argument of the latitude of the moon */
+    f   = fmod (335779.526232 + (1739527262.8478 * t), as2pi) * as2r;
+
+    /* Mean elongation of the moon from the sun */
+    d   = fmod (1072260.70369 + (1602961601.2090 * t), as2pi ) * as2r;
+
+    /* Mean longitude of the ascending node of the moon */
+    om  = fmod (450160.398036 - (6962890.5431 * t), as2pi ) * as2r;
+
+    /* Initialize the nutation values */
+    dp = 0.0;
+    de = 0.0;
+
+    /* Summation of luni-solar nutation series (in reverse order) */
+    for (i = nls; i > 0; i=i-1) {
+	j = i - 1;
+
+	/* Argument and functions */
+	arg = fmod ( (double) (nals[5*j]) * el +
+		     (double) (nals[1+5*j]) * elp +
+		     (double) (nals[2+5*j]) * f +
+		     (double) (nals[3+5*j]) * d +
+		     (double) (nals[4+5*j]) * om, d2pi);
+	sarg = sin (arg);
+	carg = cos (arg);
+
+	/* Terms */
+	dp = dp + (cls[6*j] + cls[1+6*j] * t) * sarg + cls[2+6*j] * carg;
+	de = de + (cls[3+6*j] + cls[4+6*j] * t) * carg + cls[5+6*j] * sarg;
+	}
+
+    /* Convert from 0.1 microarcsec units to radians */
+    dpsils = dp * u2r;
+    depsls = de * u2r;
+
+/* In lieu of planetary nutation */
+
+    /* Fixed offset to correct for missing terms in truncated series */
+    dpsipl = dpplan;
+    depspl = deplan;
+
+/* Results */
+
+    /* Add luni-solar and planetary components */
+    *dpsi = dpsils + dpsipl;
+    *deps = depsls + depspl;
+
+    /* Mean Obliquity in radians (IAU 2006, Hilton, et al.) */
+    *eps0 = ( 84381.406     +
+	    ( -46.836769    +
+	    (  -0.0001831   +
+	    (   0.00200340  +
+	    (  -0.000000576 +
+	    (  -0.0000000434 ) * t ) * t ) * t ) * t ) * t ) * as2r;
+}
diff --git a/Code/src/libwcs/platefit.c b/Code/src/libwcs/platefit.c
new file mode 100644
index 0000000000000000000000000000000000000000..445bdcc6aa55de82226c445eec4ee2e5079c6b95
--- /dev/null
+++ b/Code/src/libwcs/platefit.c
@@ -0,0 +1,308 @@
+/*** File libwcs/platefit.c
+ *** September 26, 2006
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1998-2006
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/*  Nonlinear least squares fitting program using data arrays starting
+ *  at x and y to fit array starting at z.
+ *  Contains convergence oscillation damping and optional normalization 
+ *  Fits up to MAXPAR parameters
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "wcs.h"
+#include "lwcs.h"
+
+static void plate_amoeba();
+static double plate_chisqr();
+static int ncoeff=0;
+static double   *sx_p;
+static double   *sy_p;
+static double   *gx_p;
+static double   *gy_p;
+static int	nbin_p;
+extern int SetPlate();
+
+#define MAXPAR 26
+#define MAXPAR1 27
+#define NITMAX 2500
+
+int
+FitPlate (wcs, x, y, x1, y1, np, ncoeff0, debug)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double	*x, *y;		/* Image WCS coordinates */
+double	*x1, *y1;	/* Image pixel coordinates */
+int	np;		/* Number of points to fit */
+int	ncoeff0;	/* Order of polynomial terms in x and y */
+int	debug;
+
+{
+    sx_p = x;
+    sy_p = y;
+    gx_p = x1;
+    gy_p = y1;
+    nbin_p = np;
+    ncoeff = ncoeff0;
+
+    /* Fit polynomials */
+    plate_amoeba (wcs);
+
+    return (0);
+}
+
+static struct WorldCoor *wcsp;
+
+/* Set up the necessary temp arrays and call the amoeba() multivariate solver */
+
+static void
+plate_amoeba (wcs0)
+
+struct WorldCoor *wcs0;
+
+{
+    double *p[MAXPAR1];				  /* used as p[NPAR1][NPAR] */
+    double vguess[MAXPAR], vp[MAXPAR], vdiff[MAXPAR];
+    double y[MAXPAR1];				  /* used as y[1..NPAR] */
+    double sumx, sumy, sumr;
+    int nbytes;
+    int iter;
+    int i, j;
+    int nfit, nfit1;
+    int nitmax;
+    extern void amoeba();
+
+    /* Allocate memory for fit */
+    nfit = ncoeff * 2;
+    nfit1 = nfit + 1;
+    nbytes = nfit * sizeof (double);
+    for (i = 0; i < nfit1; i++)
+	p[i] = (double *) malloc (nbytes);
+
+    nitmax = 6000;
+    wcsp = wcs0;
+
+/* Zero guess and difference vectors */
+    for (i = 0; i < MAXPAR; i++) {
+	vguess[i] = 0.0;
+	vdiff[i] = 0.0;
+	vp[i] = 0.0;
+	}
+
+    if (nfit > 0) {
+	double dra = wcsp->cdelt[0];
+	double ddec = wcsp->cdelt[1];
+	vguess[0] = 0.0;
+	vdiff[0] = 5.0 * dra;
+	vguess[1] = wcsp->cd[0];
+	vdiff[1] = 0.05 * dra;
+	vguess[2] = wcsp->cd[1];
+	vdiff[2] = 0.05 * ddec;
+	vguess[3] = 0.0;
+	vdiff[3] = 0.001 * dra;
+	vguess[4] = 0.0;
+	vdiff[4] = 0.001 * ddec;
+	vguess[5] = 0.0;
+	vdiff[5] = 0.001 * ddec;
+	if (ncoeff > 6) {
+	    vguess[6] = 0.0;
+	    vdiff[6] = 0.001 * ddec;
+	    vguess[7] = 0.0;
+	    vdiff[7] = 0.001 * ddec;
+	    }
+	vguess[ncoeff+0] = 0.0;
+	vdiff[ncoeff+0] = 5.0 * ddec;
+	vguess[ncoeff+1] = wcsp->cd[2];
+	vdiff[ncoeff+1] = 0.05 * ddec;
+	vguess[ncoeff+2] = wcsp->cd[3];
+	vdiff[ncoeff+2] = 0.05 * dra;
+	vguess[ncoeff+3] = 0.0;
+	vdiff[ncoeff+3] = 0.001 * ddec;
+	vguess[ncoeff+4] = 0.0;
+	vdiff[ncoeff+4] = 0.001 * dra;
+	vguess[ncoeff+5] = 0.0;
+	vdiff[ncoeff+5] = 0.001 * ddec;
+	if (ncoeff > 6) {
+	    vguess[ncoeff+6] = 0.0;
+	    vdiff[ncoeff+6] = 0.001 * dra;
+	    vguess[ncoeff+7] = 0.0;
+	    vdiff[ncoeff+7] = 0.001 * ddec;
+	    }
+	}
+
+    /* Set up matrix of nfit+1 initial guesses.
+     * The supplied guess, plus one for each parameter altered by a small amount
+     */
+    for (i = 0; i < nfit1; i++) {
+	for (j = 0; j < nfit; j++)
+	    p[i][j] = vguess[j];
+	if (i > 0)
+	    p[i][i-1] = vguess[i-1] + vdiff[i-1];
+	y[i] = plate_chisqr (p[i], -i);
+	}
+
+#define	PDUMP
+#ifdef	PDUMP
+    fprintf (stderr,"Before:\n");
+    for (i = 0; i < nfit1; i++) {
+	fprintf (stderr,"%3d: ", i);
+	for (j = 0; j < ncoeff; j++)
+	    fprintf (stderr," %9.7f",p[i][j]);
+	fprintf (stderr,"\n     ");
+	for (j = 0; j < ncoeff; j++)
+	    fprintf (stderr," %9.7f",p[i][ncoeff+j]);
+	fprintf (stderr,"\n");
+	}
+#endif
+
+    amoeba (p, y, nfit, FTOL, nitmax, plate_chisqr, &iter);
+
+#define	PDUMP
+#ifdef	PDUMP
+    fprintf (stderr,"\nAfter:\n");
+    for (i = 0; i < nfit1; i++) {
+	fprintf (stderr,"%3d: ", i);
+	for (j = 0; j < ncoeff; j++)
+	    fprintf (stderr," %9.7f",p[i][j]);
+	fprintf (stderr,"\n     ");
+	for (j = 0; j < ncoeff; j++)
+	    fprintf (stderr," %9.7f",p[i][ncoeff+j]);
+	fprintf (stderr,"\n");
+	}
+#endif
+
+    /* on return, all entries in p[1..NPAR] are within FTOL; average them */
+    for (j = 0; j < nfit; j++) {
+	double sum = 0.0;
+        for (i = 0; i < nfit1; i++)
+	    sum += p[i][j];
+	vp[j] = sum / (double)nfit1;
+	}
+    (void)SetPlate (wcsp, ncoeff, ncoeff, vp);
+
+#define RESIDDUMP
+#ifdef RESIDDUMP
+    fprintf (stderr,"iter=%4d\n  ", iter);
+    for (j = 0; j < ncoeff; j++)
+	    fprintf (stderr," %9.7f",vp[j]);
+    fprintf (stderr,"\n    ");
+    for (j = 0; j < ncoeff; j++)
+	    fprintf (stderr," %9.7f",vp[j+6]);
+    fprintf (stderr,"\n");
+
+    sumx = 0.0;
+    sumy = 0.0;
+    sumr = 0.0;
+    for (i = 0; i < nbin_p; i++) {
+	double mx, my, ex, ey, er;
+	char rastr[32], decstr[32];
+
+	pix2wcs (wcsp, sx_p[i], sy_p[i], &mx, &my);
+	ex = 3600.0 * (mx - gx_p[i]);
+	ey = 3600.0 * (my - gy_p[i]);
+	er = sqrt (ex * ex + ey * ey);
+	sumx = sumx + ex;
+	sumy = sumy + ey;
+	sumr = sumr + er;
+
+	ra2str (rastr, 31, gx_p[i], 3);
+	dec2str (decstr, 31, gy_p[i], 2);
+	fprintf (stderr,"%2d: c: %s %s ", i+1, rastr, decstr);
+	ra2str (rastr, 31, mx, 3);
+	dec2str (decstr, 31, my, 2);
+	fprintf (stderr,"i: %s %s %6.3f %6.3f %6.3f\n",
+		rastr, decstr, 3600.0*ex, 3600.0*ey,
+		3600.0*sqrt(ex*ex + ey*ey));
+	}
+    sumx = sumx / (double)nbin_p;
+    sumy = sumy / (double)nbin_p;
+    sumr = sumr / (double)nbin_p;
+    fprintf (stderr,"mean dra: %6.3f, ddec: %6.3f, dr = %6.3f\n", sumx, sumy, sumr);
+#endif
+
+    for (i = 0; i < nfit1; i++)
+	free (p[i]);
+    return;
+}
+
+
+/* Compute the chisqr of the vector v, where v[i]=plate fit coeffients
+ * chisqr is in arcsec^2
+ */
+
+static double
+plate_chisqr (v, iter)
+
+double	*v;	/* Vector of parameter values */
+int	iter;	/* Number of iterations */
+
+{
+    double chsq;
+    double xsp, ysp, dx, dy;
+    int i, j;
+    extern int SetPlate();
+
+    /* Set plate constants from fit parameter vector */
+    if (SetPlate (wcsp, ncoeff, ncoeff, v)) {
+	fprintf (stderr,"CHISQR: Cannot reset WCS!\n");
+	return (0.0);
+	}
+
+    /* Compute sum of squared residuals for these parameters */
+    chsq = 0.0;
+    for (i = 0; i < nbin_p; i++) {
+	pix2wcs (wcsp, sx_p[i], sy_p[i], &xsp, &ysp);
+	dx =3600.0 * (xsp - gx_p[i]);
+	dy = 3600.0 * (ysp - gy_p[i]);
+	chsq += dx*dx + dy*dy;
+	}
+
+#define TRACE_CHSQR
+#ifdef TRACE_CHSQR
+    fprintf (stderr,"%4d:", iter);
+    for (j = 0; j < ncoeff; j++)
+	fprintf (stderr," %9.4g",v[j]);
+    for (j = 0; j < ncoeff; j++)
+	fprintf (stderr," %9.4g",v[ncoeff+j]);
+    fprintf (stderr," -> %f\r", chsq);
+#endif
+    return (chsq);
+}
+
+/* Mar 30 1998	New subroutines
+ * Apr  7 1998	Add x^3 and y^3 terms
+ * Apr 10 1998	Add second number of coefficients
+ * May 14 1998	include stdio.h for stderr
+ * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
+ * Oct 15 1999	Include stdlib.h for malloc() declaration
+ *
+ * Jan 11 2001	Print all messages to stderr
+ *
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ */
diff --git a/Code/src/libwcs/platepos.c b/Code/src/libwcs/platepos.c
new file mode 100644
index 0000000000000000000000000000000000000000..abd31237a5e83c6601b855e53a01267e9ef7ad24
--- /dev/null
+++ b/Code/src/libwcs/platepos.c
@@ -0,0 +1,391 @@
+/*** File saoimage/wcslib/platepos.c
+ *** February 29, 2000
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1998-2002
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	platepos.c (Plate solution WCS conversion
+ * Purpose:	Compute WCS from plate fit
+ * Subroutine:	platepos() converts from pixel location to RA,Dec 
+ * Subroutine:	platepix() converts from RA,Dec to pixel location   
+
+    These functions are based on the astrmcal.c portion of GETIMAGE by
+    J. Doggett and the documentation distributed with the Digital Sky Survey.
+
+*/
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "wcs.h"
+
+int
+platepos (xpix, ypix, wcs, xpos, ypos)
+
+/* Routine to determine accurate position for pixel coordinates */
+/* returns 0 if successful otherwise 1 = angle too large for projection; */
+/* based on amdpos() from getimage */
+
+/* Input: */
+double	xpix;		/* x pixel number  (RA or long without rotation) */
+double	ypix;		/* y pixel number  (dec or lat without rotation) */
+struct WorldCoor *wcs;	/* WCS parameter structure */
+
+/* Output: */
+double	*xpos;		/* Right ascension or longitude in degrees */
+double	*ypos;		/* Declination or latitude in degrees */
+
+{
+    double x, y, x2, y2, x3, y3, r2;
+    double xi, xir, eta, etar, raoff, ra, dec, ra0, dec0;
+    double twopi = 6.28318530717959;
+    double ctan, ccos;
+    int ncoeff1 = wcs->ncoeff1;
+    int ncoeff2 = wcs->ncoeff2;
+
+    /*  Ignore magnitude and color terms 
+    double mag = 0.0;
+    double color = 0.0; */
+
+    /* Convert from pixels to millimeters */
+    x = xpix - wcs->crpix[0];
+    y = ypix - wcs->crpix[1];
+    x2 = x * x;
+    y2 = y * y;
+    x3 = x * x2;
+    y3 = y * y2;
+    r2 = x2 + y2;
+
+    /*  Compute xi,eta coordinates in degrees from x,y and plate model */
+    xi =  wcs->x_coeff[ 0]		+ wcs->x_coeff[ 1]*x +
+	  wcs->x_coeff[ 2]*y	+ wcs->x_coeff[ 3]*x2 +
+	  wcs->x_coeff[ 4]*y2	+ wcs->x_coeff[ 5]*x*y;
+
+    if (ncoeff1 > 6)
+	  xi = xi + wcs->x_coeff[ 6]*x3	+ wcs->x_coeff[ 7]*y3;
+
+    if (ncoeff1 > 8) {
+	xi = xi + wcs->x_coeff[ 8]*x2*y	+ wcs->x_coeff[ 9]*x*y2 +
+		  wcs->x_coeff[10]*(r2)	+ wcs->x_coeff[11]*x*r2 +
+		  wcs->x_coeff[12]*y*r2;
+	}
+
+    eta = wcs->y_coeff[ 0]		+ wcs->y_coeff[ 1]*x +
+	  wcs->y_coeff[ 2]*y	+ wcs->y_coeff[ 3]*x2 +
+	  wcs->y_coeff[ 4]*y2	+ wcs->y_coeff[ 5]*x*y;
+
+    if (ncoeff2 > 6)
+	eta = eta + wcs->y_coeff[ 6]*x3	+ wcs->y_coeff[ 7]*y3;
+
+    if (ncoeff2 > 8) {
+	eta = eta + wcs->y_coeff[ 8]*x2*y + wcs->y_coeff[ 9]*y2*x +
+		    wcs->y_coeff[10]*r2   + wcs->y_coeff[11]*x*r2 +
+		    wcs->y_coeff[12]*y*r2;
+	}
+
+    /* Convert to radians */
+    xir = degrad (xi);
+    etar = degrad (eta);
+
+    /* Convert to RA and Dec */
+    ra0 = degrad (wcs->crval[0]);
+    dec0 = degrad (wcs->crval[1]);
+    ctan = tan (dec0);
+    ccos = cos (dec0);
+    raoff = atan2 (xir / ccos, 1.0 - etar * ctan);
+    ra = raoff + ra0;
+    if (ra < 0.0) ra = ra + twopi;
+    *xpos = raddeg (ra);
+
+    dec = atan (cos (raoff) / ((1.0 - (etar * ctan)) / (etar + ctan)));
+    *ypos = raddeg (dec);
+    return 0;
+}
+
+
+int
+platepix (xpos, ypos, wcs, xpix, ypix)
+
+/* Routine to determine pixel coordinates for sky position */
+/* returns 0 if successful otherwise 1 = angle too large for projection; */
+/* based on amdinv() from getimage */
+
+/* Input: */
+double	xpos;		/* Right ascension or longitude in degrees */
+double	ypos;		/* Declination or latitude in degrees */
+struct WorldCoor *wcs;	/* WCS parameter structure */
+
+/* Output: */
+double	*xpix;		/* x pixel number  (RA or long without rotation) */
+double	*ypix;		/* y pixel number  (dec or lat without rotation) */
+
+{
+    double xi,eta,x,y,xy,x2,y2,x2y,y2x,x3,y3,r2,dx,dy;
+    double tdec,ctan,ccos,traoff, craoff, etar, xir;
+    double f,fx,fy,g,gx,gy;
+    double ra0, dec0, ra, dec;
+    double tolerance = 0.0000005;
+    int    max_iterations = 50;
+    int    i;
+    int	ncoeff1 = wcs->ncoeff1;
+    int	ncoeff2 = wcs->ncoeff2;
+
+    /* Convert RA and Dec in radians to standard coordinates on a plate */
+    ra = degrad (xpos);
+    dec = degrad (ypos);
+    tdec = tan (dec);
+    ra0 = degrad (wcs->crval[0]);
+    dec0 = degrad (wcs->crval[1]);
+    ctan = tan (dec0);
+    ccos = cos (dec0);
+    traoff = tan (ra - ra0);
+    craoff = cos (ra - ra0);
+    etar = (1.0 - ctan * craoff / tdec) / (ctan + (craoff / tdec));
+    xir = traoff * ccos * (1.0 - (etar * ctan));
+    xi = raddeg (xir);
+    eta = raddeg (etar);
+
+    /* Set initial value for x,y */
+    x = xi * wcs->dc[0] + eta * wcs->dc[1];
+    y = xi * wcs->dc[2] + eta * wcs->dc[3];
+
+    /* if (wcs->x_coeff[1] == 0.0)
+	x = xi - wcs->x_coeff[0];
+    else
+	x = (xi - wcs->x_coeff[0]) / wcs->x_coeff[1];
+    if (wcs->y_coeff[2] == 0.0)
+	y = eta - wcs->y_coeff[0];
+    else
+	y = (eta - wcs->y_coeff[0]) / wcs->y_coeff[2]; */
+
+    /* Iterate by Newton's method */
+    for (i = 0; i < max_iterations; i++) {
+
+	/* X plate model */
+	xy = x * y;
+	x2 = x * x;
+	y2 = y * y;
+	x3 = x2 * x;
+	y3 = y2 * y;
+	x2y = x2 * y;
+	y2x = y2 * x;
+	r2 = x2 + y2;
+
+	f = wcs->x_coeff[0]	+ wcs->x_coeff[1]*x +
+	    wcs->x_coeff[2]*y	+ wcs->x_coeff[3]*x2 +
+	    wcs->x_coeff[4]*y2	+ wcs->x_coeff[5]*xy;
+
+	/*  Derivative of X model wrt x */
+	fx = wcs->x_coeff[1]	+ wcs->x_coeff[3]*2.0*x +
+	     wcs->x_coeff[5]*y;
+
+	/* Derivative of X model wrt y */
+	fy = wcs->x_coeff[2]	+ wcs->x_coeff[4]*2.0*y +
+	     wcs->x_coeff[5]*x;
+
+	if (ncoeff1 > 6) {
+	    f = f + wcs->x_coeff[6]*x3	+ wcs->x_coeff[7]*y3;
+	    fx = fx + wcs->x_coeff[6]*3.0*x2;
+	    fy = fy + wcs->x_coeff[7]*3.0*y2;
+	    }
+
+	if (ncoeff1 > 8) {
+	    f = f +
+		wcs->x_coeff[8]*x2y	+ wcs->x_coeff[9]*y2x +
+		wcs->x_coeff[10]*r2 + wcs->x_coeff[11]*x*r2 +
+		wcs->x_coeff[12]*y*r2;
+
+	    fx = fx +	wcs->x_coeff[8]*2.0*xy + 
+			wcs->x_coeff[9]*y2 +
+	 		wcs->x_coeff[10]*2.0*x +
+			wcs->x_coeff[11]*(3.0*x2+y2) +
+			wcs->x_coeff[12]*2.0*xy;
+
+	    fy = fy +	wcs->x_coeff[8]*x2 +
+			wcs->x_coeff[9]*2.0*xy +
+			wcs->x_coeff[10]*2.0*y +
+			wcs->x_coeff[11]*2.0*xy +
+			wcs->x_coeff[12]*(3.0*y2+x2);
+	    }
+
+	/* Y plate model */
+	g = wcs->y_coeff[0]	+ wcs->y_coeff[1]*x +
+	    wcs->y_coeff[2]*y	+ wcs->y_coeff[3]*x2 +
+	    wcs->y_coeff[4]*y2	+ wcs->y_coeff[5]*xy;
+
+	/* Derivative of Y model wrt x */
+	gx = wcs->y_coeff[1]	+ wcs->y_coeff[3]*2.0*x +
+	     wcs->y_coeff[5]*y;
+
+	/* Derivative of Y model wrt y */
+	gy = wcs->y_coeff[2]	+ wcs->y_coeff[4]*2.0*y +
+	     wcs->y_coeff[5]*x;
+
+	if (ncoeff2 > 6) {
+	    g = g + wcs->y_coeff[6]*x3	+ wcs->y_coeff[7]*y3;
+	    gx = gx + wcs->y_coeff[6]*3.0*x2;
+	    gy = gy + wcs->y_coeff[7]*3.0*y2;
+	    }
+
+	if (ncoeff2 > 8) {
+	    g = g +
+		wcs->y_coeff[8]*x2y	+ wcs->y_coeff[9]*y2x +
+		wcs->y_coeff[10]*r2	+ wcs->y_coeff[11]*x*r2 +
+		wcs->y_coeff[12]*y*r2;
+
+	    gx = gx +	wcs->y_coeff[8]*2.0*xy + 
+			wcs->y_coeff[9]*y2 +
+	 		wcs->y_coeff[10]*2.0*x +
+			wcs->y_coeff[11]*(3.0*x2+y2) +
+			wcs->y_coeff[12]*2.0*xy;
+
+	    gy = gy +	wcs->y_coeff[8]*x2 +
+			wcs->y_coeff[9]*2.0*xy +
+			wcs->y_coeff[10]*2.0*y +
+			wcs->y_coeff[11]*2.0*xy +
+			wcs->y_coeff[12]*(3.0*y2+x2);
+	    }
+
+	f = f - xi;
+	g = g - eta;
+	dx = ((-f * gy) + (g * fy)) / ((fx * gy) - (fy * gx));
+	dy = ((-g * fx) + (f * gx)) / ((fx * gy) - (fy * gx));
+	x = x + dx;
+	y = y + dy;
+	if ((fabs(dx) < tolerance) && (fabs(dy) < tolerance)) break;
+	}
+
+    /* Convert from plate pixels to image pixels */
+    *xpix = x + wcs->crpix[0];
+    *ypix = y + wcs->crpix[1];
+
+    /* If position is off of the image, return offscale code */
+    if (*xpix < 0.5 || *xpix > wcs->nxpix+0.5)
+	return -1;
+    if (*ypix < 0.5 || *ypix > wcs->nypix+0.5)
+	return -1;
+
+    return 0;
+}
+
+
+/* Set plate fit coefficients in structure from arguments */
+int
+SetPlate (wcs, ncoeff1, ncoeff2, coeff)
+
+struct WorldCoor *wcs;  /* World coordinate system structure */
+int	ncoeff1;		/* Number of coefficients for x */
+int	ncoeff2;		/* Number of coefficients for y */
+double	*coeff;		/* Plate fit coefficients */
+
+{
+    int i;
+
+    if (nowcs (wcs) || (ncoeff1 < 1 && ncoeff2 < 1))
+	return 1;
+
+    wcs->ncoeff1 = ncoeff1;
+    wcs->ncoeff2 = ncoeff2;
+    wcs->prjcode = WCS_PLT;
+
+    for (i = 0; i < 20; i++) {
+	if (i < ncoeff1)
+	    wcs->x_coeff[i] = coeff[i];
+	else
+	    wcs->x_coeff[i] = 0.0;
+	}
+
+    for (i = 0; i < 20; i++) {
+	if (i < ncoeff2)
+	    wcs->y_coeff[i] = coeff[ncoeff1+i];
+	else
+	    wcs->y_coeff[i] = 0.0;
+	}
+    return 0;
+}
+
+
+/* Return plate fit coefficients from structure in arguments */
+int
+GetPlate (wcs, ncoeff1, ncoeff2, coeff)
+
+struct WorldCoor *wcs;  /* World coordinate system structure */
+int	*ncoeff1;	/* Number of coefficients for x */
+int	*ncoeff2;	/* Number of coefficients for y) */
+double	*coeff;		/* Plate fit coefficients */
+
+{
+    int i;
+
+    if (nowcs (wcs))
+	return 1;
+
+    *ncoeff1 = wcs->ncoeff1;
+    *ncoeff2 = wcs->ncoeff2;
+
+    for (i = 0; i < *ncoeff1; i++)
+	coeff[i] = wcs->x_coeff[i];
+
+    for (i = 0; i < *ncoeff2; i++)
+	coeff[*ncoeff1+i] = wcs->y_coeff[i];
+
+    return 0;
+}
+
+
+/* Set FITS header plate fit coefficients from structure */
+void
+SetFITSPlate (header, wcs)
+
+char    *header;        /* Image FITS header */
+struct WorldCoor *wcs;  /* WCS structure */
+
+{
+    char keyword[16];
+    int i;
+
+    for (i = 0; i < wcs->ncoeff1; i++) {
+	sprintf (keyword,"CO1_%d",i+1);
+	hputnr8 (header, keyword, -15, wcs->x_coeff[i]);
+	}
+    for (i = 0; i < wcs->ncoeff2; i++) {
+	sprintf (keyword,"CO2_%d",i+1);
+	hputnr8 (header, keyword, -15, wcs->y_coeff[i]);
+	}
+    return;
+}
+
+/* Mar 27 1998	New subroutines for direct image pixel <-> sky polynomials
+ * Apr 10 1998	Make terms identical for both x and y polynomials
+ * Apr 10 1998	Allow different numbers of coefficients for x and y
+ * Apr 16 1998	Drom NCOEFF header parameter
+ * Apr 28 1998  Change projection flags to WCS_*
+ * Sep 10 1998	Check for xc1 and yc2 divide by zero after Allen Harris, SAO
+ *
+ * Oct 21 1999	Drop unused variables after lint
+ *
+ * Feb 29 2000	Use inverse CD matrix to get initial X,Y in platepix()
+ *		as suggested by Paolo Montegriffo from Bologna Ast. Obs.
+ */
diff --git a/Code/src/libwcs/polfit.c b/Code/src/libwcs/polfit.c
new file mode 100644
index 0000000000000000000000000000000000000000..ac1e594057d57b5a3531e26ce6b62f70a4ecce4a
--- /dev/null
+++ b/Code/src/libwcs/polfit.c
@@ -0,0 +1,192 @@
+/*** File polfit.c
+ *** April 3, 2003
+ *** By Doug Mink, after Bevington, page 141
+
+ *--- Polynomial least squares fitting program, almost identical to the
+ *    one in Bevington, "Data Reduction and Error Analysis for the
+ *    Physical Sciences," page 141.  The argument list was changed and
+ *    the weighting removed.
+ *      y = a(1) + a(2)*(x-x0) + a(3)*(x-x0)**2 + a(3)*(x-x0)**3 + . . .
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "wcscat.h"
+
+static double determ();
+
+void
+polfit (x, y, x0, npts, nterms, a, stdev)
+
+double	*x;		/* Array of independent variable points */
+double	*y;		/* Array of dependent variable points */
+double	x0;		/* Offset to independent variable */
+int	npts;		/* Number of data points to fit */
+int	nterms;		/* Number of parameters to fit */
+double	*a;		/* Vector containing current fit values */
+double	*stdev; 	/* Standard deviation of fit (returned) */
+{
+    double sigma2sum;
+    double xterm,yterm,xi,yi;
+    double *sumx, *sumy;
+    double *array;
+    int i,j,k,l,n,nmax;
+    double delta;
+
+    /* accumulate weighted sums */
+    nmax = 2 * nterms - 1;
+    sumx = (double *) calloc (nmax, sizeof(double));
+    sumy = (double *) calloc (nterms, sizeof(double));
+    for (n = 0; n < nmax; n++)
+	sumx[n] = 0.0;
+    for (j = 0; j < nterms; j++)
+	sumy[j] = 0.0;
+    for (i = 0; i < npts; i++) {
+	xi = x[i] - x0;
+	yi = y[i];
+	xterm = 1.0;
+	for (n = 0; n < nmax; n++) {
+	    sumx[n] = sumx[n] + xterm;
+	    xterm = xterm * xi;
+	    }
+	yterm = yi;
+	for (n = 0; n < nterms; n++) {
+	    sumy[n] = sumy[n] + yterm;
+	    yterm = yterm * xi;
+	    }
+	}
+
+    /* Construct matrices and calculate coeffients */
+    array = (double *) calloc (nterms*nterms, sizeof(double));
+    for (j = 0; j < nterms; j++) {
+	for (k = 0; k < nterms; k++) {
+	    n = j + k;
+	    array[j+k*nterms] = sumx[n];
+	    }
+	}
+    delta = determ (array, nterms);
+    if (delta == 0.0) {
+	*stdev = 0.;
+	for (j = 0; j < nterms; j++)
+	    a[j] = 0. ;
+	free (array);
+	free (sumx);
+	free (sumy);
+	return;
+	}
+
+    for (l = 0; l < nterms; l++) {
+	for (j = 0; j < nterms; j++) {
+	    for (k = 0; k < nterms; k++) {
+		n = j + k;
+		array[j+k*nterms] = sumx[n];
+		}
+	    array[j+l*nterms] = sumy[j];
+	    }
+	a[l] = determ (array, nterms) / delta;
+	}
+
+    /* Calculate sigma */
+    sigma2sum = 0.0;
+    for (i = 0; i < npts; i++) {
+	yi = polcomp (x[i], x0, nterms, a);
+	sigma2sum = sigma2sum + ((y[i] - yi) * (y[i] - yi));
+	}
+    *stdev = sqrt (sigma2sum / (double) (npts - 1));
+
+    free (array);
+    free (sumx);
+    free (sumy);
+    return;
+}
+
+
+/*--- Calculate the determinant of a square matrix
+ *    This subprogram destroys the input matrix array
+ *    From Bevington, page 294.
+ */
+
+static double
+determ (array, norder)
+
+double	*array;		/* Input matrix array */
+int	norder;		/* Order of determinant (degree of matrix) */
+
+{
+    double save, det;
+    int i,j,k,k1, zero;
+
+    det = 1.0;
+    for (k = 0; k < norder; k++) {
+
+	/* Interchange columns if diagonal element is zero */
+	if (array[k+k*norder] == 0) {
+	    zero = 1;
+	    for (j = k; j < norder; j++) {
+		if (array[k+j*norder] != 0.0)
+		    zero = 0;
+		}
+	    if (zero)
+		return (0.0);
+
+	    for (i = k; i < norder; i++) {
+		save = array[i+j*norder]; 
+		array[i+j*norder] = array[i+k*norder];
+		array[i+k*norder] = save ;
+		}
+	    det = -det;
+	    }
+
+	/* Subtract row k from lower rows to get diagonal matrix */
+	det = det * array[k+k*norder];
+	if (k < norder - 1) {
+	    k1 = k + 1;
+	    for (i = k1; i < norder; i++) {
+		for (j = k1; j < norder; j++) {
+		    array[i+j*norder] = array[i+j*norder] -
+				      (array[i+k*norder] * array[k+j*norder] /
+				      array[k+k*norder]);
+		    }
+		}
+	    }
+	}
+	return (det);
+}
+
+/* POLCOMP -- Polynomial evaluation
+ *	Y = A(1) + A(2)*X + A(3)*X**2 + A(3)*X**3 + . . . */
+
+double
+polcomp (xi, x0, norder, a)
+
+double	xi;	/* Independent variable */
+double	x0;	/* Offset to independent variable */
+int	norder;	/* Number of coefficients */
+double	*a;	/* Vector containing coeffiecients */
+{
+    double xterm, x, y;
+    int iterm;
+
+    /* Accumulate polynomial value */
+    x = xi - x0;
+    y = 0.0;
+    xterm = 1.0;
+    for (iterm = 0; iterm < norder; iterm++) {
+	y = y + a[iterm] * xterm;
+	xterm = xterm + x;
+	}
+    return (y);
+}
+
+/* Sep 10 1987	Program written
+ *
+ * Mar 17 1993	Add x offset
+ *
+ * Feb 23 1998	Translate to C
+ *
+ * Jul 25 2001	Add polcomp to return computed values
+ *
+ * Apr  3 2003	Drop unused variable freedom in polfit()
+ */
diff --git a/Code/src/libwcs/proj.c b/Code/src/libwcs/proj.c
new file mode 100644
index 0000000000000000000000000000000000000000..c8ce4e93a646833d455c2cefa07ffeebaf914dc7
--- /dev/null
+++ b/Code/src/libwcs/proj.c
@@ -0,0 +1,4351 @@
+/*============================================================================
+*
+*   WCSLIB - an implementation of the FITS WCS proposal.
+*   Copyright (C) 1995-2002, Mark Calabretta
+*
+*   This library is free software; you can redistribute it and/or
+*   modify it under the terms of the GNU Lesser General Public
+*   License as published by the Free Software Foundation; either
+*   version 2 of the License, or (at your option) any later version.
+*
+*   This library is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+*   Lesser General Public License for more details.
+*   
+*   You should have received a copy of the GNU Lesser General Public
+*   License along with this library; if not, write to the Free Software
+*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*   Correspondence concerning WCSLIB may be directed to:
+*      Internet email: mcalabre@atnf.csiro.au
+*      Postal address: Dr. Mark Calabretta,
+*                      Australia Telescope National Facility,
+*                      P.O. Box 76,
+*                      Epping, NSW, 2121,
+*                      AUSTRALIA
+*
+*=============================================================================
+*
+*   C implementation of the spherical map projections recognized by the FITS
+*   "World Coordinate System" (WCS) convention.
+*
+*   Summary of routines
+*   -------------------
+*   Each projection is implemented via separate functions for the forward,
+*   *fwd(), and reverse, *rev(), transformation.
+*
+*   Initialization routines, *set(), compute intermediate values from the
+*   projection parameters but need not be called explicitly - see the
+*   explanation of prj.flag below.
+*
+*      prjset prjfwd prjrev   Driver routines (see below).
+*
+*      azpset azpfwd azprev   AZP: zenithal/azimuthal perspective
+*      szpset szpfwd szprev   SZP: slant zenithal perspective
+*      tanset tanfwd tanrev   TAN: gnomonic
+*      stgset stgfwd stgrev   STG: stereographic
+*      sinset sinfwd sinrev   SIN: orthographic/synthesis
+*      arcset arcfwd arcrev   ARC: zenithal/azimuthal equidistant
+*      zpnset zpnfwd zpnrev   ZPN: zenithal/azimuthal polynomial
+*      zeaset zeafwd zearev   ZEA: zenithal/azimuthal equal area
+*      airset airfwd airrev   AIR: Airy
+*      cypset cypfwd cyprev   CYP: cylindrical perspective
+*      ceaset ceafwd cearev   CEA: cylindrical equal area
+*      carset carfwd carrev   CAR: Cartesian
+*      merset merfwd merrev   MER: Mercator
+*      sflset sflfwd sflrev   SFL: Sanson-Flamsteed
+*      parset parfwd parrev   PAR: parabolic
+*      molset molfwd molrev   MOL: Mollweide
+*      aitset aitfwd aitrev   AIT: Hammer-Aitoff
+*      copset copfwd coprev   COP: conic perspective
+*      coeset coefwd coerev   COE: conic equal area
+*      codset codfwd codrev   COD: conic equidistant
+*      cooset coofwd coorev   COO: conic orthomorphic
+*      bonset bonfwd bonrev   BON: Bonne
+*      pcoset pcofwd pcorev   PCO: polyconic
+*      tscset tscfwd tscrev   TSC: tangential spherical cube
+*      cscset cscfwd cscrev   CSC: COBE quadrilateralized spherical cube
+*      qscset qscfwd qscrev   QSC: quadrilateralized spherical cube
+*
+*
+*   Driver routines; prjset(), prjfwd() & prjrev()
+*   ----------------------------------------------
+*   A set of driver routines are available for use as a generic interface to
+*   the specific projection routines.  The interfaces to prjfwd() and prjrev()
+*   are the same as those of the forward and reverse transformation routines
+*   for the specific projections (see below).
+*
+*   The interface to prjset() differs slightly from that of the initialization
+*   routines for the specific projections and unlike them it must be invoked
+*   explicitly to use prjfwd() and prjrev().
+*
+*   Given:
+*      pcode[4] const char
+*                        WCS projection code.
+*
+*   Given and/or returned:
+*      prj      prjprm*  Projection parameters (see below).
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*
+*
+*   Initialization routine; *set()
+*   ------------------------------
+*   Initializes members of a prjprm data structure which hold intermediate
+*   values.  Note that this routine need not be called directly; it will be
+*   invoked by prjfwd() and prjrev() if the "flag" structure member is
+*   anything other than a predefined magic value.
+*
+*   Given and/or returned:
+*      prj      prjprm*  Projection parameters (see below).
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Invalid projection parameters.
+*
+*   Forward transformation; *fwd()
+*   -----------------------------
+*   Compute (x,y) coordinates in the plane of projection from native spherical
+*   coordinates (phi,theta).
+*
+*   Given:
+*      phi,     const double
+*      theta             Longitude and latitude of the projected point in
+*                        native spherical coordinates, in degrees.
+*
+*   Given and returned:
+*      prj      prjprm*  Projection parameters (see below).
+*
+*   Returned:
+*      x,y      double*  Projected coordinates.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Invalid projection parameters.
+*                           2: Invalid value of (phi,theta).
+*
+*   Reverse transformation; *rev()
+*   -----------------------------
+*   Compute native spherical coordinates (phi,theta) from (x,y) coordinates in
+*   the plane of projection.
+*
+*   Given:
+*      x,y      const double
+*                        Projected coordinates.
+*
+*   Given and returned:
+*      prj      prjprm*  Projection parameters (see below).
+*
+*   Returned:
+*      phi,     double*  Longitude and latitude of the projected point in
+*      theta             native spherical coordinates, in degrees.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Invalid projection parameters.
+*                           2: Invalid value of (x,y).
+*                           1: Invalid projection parameters.
+*
+*   Projection parameters
+*   ---------------------
+*   The prjprm struct consists of the following:
+*
+*      int flag
+*         This flag must be set to zero whenever any of p[10] or r0 are set
+*         or changed.  This signals the initialization routine to recompute
+*         intermediaries.  flag may also be set to -1 to disable strict bounds
+*         checking for the AZP, SZP, TAN, SIN, ZPN, and COP projections.
+*
+*      double r0
+*         r0; The radius of the generating sphere for the projection, a linear
+*         scaling parameter.  If this is zero, it will be reset to the default
+*         value of 180/pi (the value for FITS WCS).
+*
+*      double p[10]
+*         The first 10 elements contain projection parameters which correspond
+*         to the PROJPn keywords in FITS, so p[0] is PROJP0, and p[9] is
+*         PROJP9.  Many projections use p[1] (PROJP1) and some also use p[2]
+*         (PROJP2).  ZPN is the only projection which uses any of the others.
+*
+*   The remaining members of the prjprm struct are maintained by the
+*   initialization routines and should not be modified.  This is done for the
+*   sake of efficiency and to allow an arbitrary number of contexts to be
+*   maintained simultaneously.
+*
+*      char code[4]
+*         Three-letter projection code.
+*
+*      double phi0, theta0
+*         Native longitude and latitude of the reference point, in degrees.
+*
+*      double w[10]
+*      int n
+*         Intermediate values derived from the projection parameters.
+*
+*      int (*prjfwd)()
+*      int (*prjrev)()
+*         Pointers to the forward and reverse projection routines.
+*
+*   Usage of the p[] array as it applies to each projection is described in
+*   the prologue to each trio of projection routines.
+*
+*   Argument checking
+*   -----------------
+*   Forward routines:
+*
+*      The values of phi and theta (the native longitude and latitude)
+*      normally lie in the range [-180,180] for phi, and [-90,90] for theta.
+*      However, all forward projections will accept any value of phi and will
+*      not normalize it.
+*
+*      The forward projection routines do not explicitly check that theta lies
+*      within the range [-90,90].  They do check for any value of theta which
+*      produces an invalid argument to the projection equations (e.g. leading
+*      to division by zero).  The forward routines for AZP, SZP, TAN, SIN,
+*      ZPN, and COP also return error 2 if (phi,theta) corresponds to the
+*      overlapped (far) side of the projection but also return the
+*      corresponding value of (x,y).  This strict bounds checking may be
+*      relaxed by setting prj->flag to -1 (rather than 0) when these
+*      projections are initialized.
+*
+*   Reverse routines:
+*
+*      Error checking on the projected coordinates (x,y) is limited to that
+*      required to ascertain whether a solution exists.  Where a solution does
+*      exist no check is made that the value of phi and theta obtained lie
+*      within the ranges [-180,180] for phi, and [-90,90] for theta.
+*
+*   Accuracy
+*   --------
+*   Closure to a precision of at least 1E-10 degree of longitude and latitude
+*   has been verified for typical projection parameters on the 1 degree grid
+*   of native longitude and latitude (to within 5 degrees of any latitude
+*   where the projection may diverge).
+*
+*   Author: Mark Calabretta, Australia Telescope National Facility
+*   $Id: proj.c,v 2.20 2002/04/03 01:25:29 mcalabre Exp $
+*===========================================================================*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "wcslib.h"
+
+int  npcode = 26;
+char pcodes[26][4] =
+      {"AZP", "SZP", "TAN", "STG", "SIN", "ARC", "ZPN", "ZEA", "AIR", "CYP",
+       "CEA", "CAR", "MER", "COP", "COE", "COD", "COO", "SFL", "PAR", "MOL",
+       "AIT", "BON", "PCO", "TSC", "CSC", "QSC"};
+
+const int AZP = 101;
+const int SZP = 102;
+const int TAN = 103;
+const int STG = 104;
+const int SIN = 105;
+const int ARC = 106;
+const int ZPN = 107;
+const int ZEA = 108;
+const int AIR = 109;
+const int CYP = 201;
+const int CEA = 202;
+const int CAR = 203;
+const int MER = 204;
+const int SFL = 301;
+const int PAR = 302;
+const int MOL = 303;
+const int AIT = 401;
+const int COP = 501;
+const int COE = 502;
+const int COD = 503;
+const int COO = 504;
+const int BON = 601;
+const int PCO = 602;
+const int TSC = 701;
+const int CSC = 702;
+const int QSC = 703;
+
+/* Map error number to error message for each function. */
+const char *prjset_errmsg[] = {
+   0,
+   "Invalid projection parameters"};
+
+const char *prjfwd_errmsg[] = {
+   0,
+   "Invalid projection parameters",
+   "Invalid value of (phi,theta)"};
+
+const char *prjrev_errmsg[] = {
+   0,
+   "Invalid projection parameters",
+   "Invalid value of (x,y)"};
+
+#define copysgn(X, Y) ((Y) < 0.0 ? -fabs(X) : fabs(X))
+#define copysgni(X, Y) ((Y) < 0 ? -abs(X) : abs(X))
+
+/*==========================================================================*/
+
+int prjset(pcode, prj)
+
+const char pcode[4];
+struct prjprm *prj;
+
+{
+   /* Set pointers to the forward and reverse projection routines. */
+   if (strcmp(pcode, "AZP") == 0) {
+      azpset(prj);
+   } else if (strcmp(pcode, "SZP") == 0) {
+      szpset(prj);
+   } else if (strcmp(pcode, "TAN") == 0) {
+      tanset(prj);
+   } else if (strcmp(pcode, "STG") == 0) {
+      stgset(prj);
+   } else if (strcmp(pcode, "SIN") == 0) {
+      sinset(prj);
+   } else if (strcmp(pcode, "ARC") == 0) {
+      arcset(prj);
+   } else if (strcmp(pcode, "ZPN") == 0) {
+      zpnset(prj);
+   } else if (strcmp(pcode, "ZEA") == 0) {
+      zeaset(prj);
+   } else if (strcmp(pcode, "AIR") == 0) {
+      airset(prj);
+   } else if (strcmp(pcode, "CYP") == 0) {
+      cypset(prj);
+   } else if (strcmp(pcode, "CEA") == 0) {
+      ceaset(prj);
+   } else if (strcmp(pcode, "CAR") == 0) {
+      carset(prj);
+   } else if (strcmp(pcode, "MER") == 0) {
+      merset(prj);
+   } else if (strcmp(pcode, "SFL") == 0) {
+      sflset(prj);
+   } else if (strcmp(pcode, "PAR") == 0) {
+      parset(prj);
+   } else if (strcmp(pcode, "MOL") == 0) {
+      molset(prj);
+   } else if (strcmp(pcode, "AIT") == 0) {
+      aitset(prj);
+   } else if (strcmp(pcode, "COP") == 0) {
+      copset(prj);
+   } else if (strcmp(pcode, "COE") == 0) {
+      coeset(prj);
+   } else if (strcmp(pcode, "COD") == 0) {
+      codset(prj);
+   } else if (strcmp(pcode, "COO") == 0) {
+      cooset(prj);
+   } else if (strcmp(pcode, "BON") == 0) {
+      bonset(prj);
+   } else if (strcmp(pcode, "PCO") == 0) {
+      pcoset(prj);
+   } else if (strcmp(pcode, "TSC") == 0) {
+      tscset(prj);
+   } else if (strcmp(pcode, "CSC") == 0) {
+      cscset(prj);
+   } else if (strcmp(pcode, "QSC") == 0) {
+      qscset(prj);
+   } else {
+      /* Unrecognized projection code. */
+      return 1;
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int prjfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   return prj->prjfwd(phi, theta, prj, x, y);
+}
+
+/*--------------------------------------------------------------------------*/
+
+int prjrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   return prj->prjrev(x, y, prj, phi, theta);
+}
+
+/*============================================================================
+*   AZP: zenithal/azimuthal perspective projection.
+*
+*   Given:
+*      prj->p[1]    Distance parameter, mu in units of r0.
+*      prj->p[2]    Tilt angle, gamma in degrees.
+*
+*   Given and/or returned:
+*      prj->flag    AZP, or -AZP if prj->flag is given < 0.
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "AZP"
+*      prj->phi0     0.0
+*      prj->theta0  90.0
+*      prj->w[0]    r0*(mu+1)
+*      prj->w[1]    tan(gamma)
+*      prj->w[2]    sec(gamma)
+*      prj->w[3]    cos(gamma)
+*      prj->w[4]    sin(gamma)
+*      prj->w[5]    asin(-1/mu) for |mu| >= 1, -90 otherwise
+*      prj->w[6]    mu*cos(gamma)
+*      prj->w[7]    1 if |mu*cos(gamma)| < 1, 0 otherwise
+*      prj->prjfwd  Pointer to azpfwd().
+*      prj->prjrev  Pointer to azprev().
+*===========================================================================*/
+
+int azpset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "AZP");
+   prj->flag   = copysgni (AZP, prj->flag);
+   prj->phi0   =  0.0;
+   prj->theta0 = 90.0;
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   prj->w[0] = prj->r0*(prj->p[1] + 1.0);
+   if (prj->w[0] == 0.0) {
+      return 1;
+   }
+
+   prj->w[3] = cosdeg (prj->p[2]);
+   if (prj->w[3] == 0.0) {
+      return 1;
+   }
+
+   prj->w[2] = 1.0/prj->w[3];
+   prj->w[4] = sindeg (prj->p[2]);
+   prj->w[1] = prj->w[4] / prj->w[3];
+
+   if (fabs(prj->p[1]) > 1.0) {
+      prj->w[5] = asindeg (-1.0/prj->p[1]);
+   } else {
+      prj->w[5] = -90.0;
+   }
+
+   prj->w[6] = prj->p[1] * prj->w[3];
+   prj->w[7] = (fabs(prj->w[6]) < 1.0) ? 1.0 : 0.0;
+
+   prj->prjfwd = azpfwd;
+   prj->prjrev = azprev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int azpfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double a, b, cphi, cthe, r, s, t;
+
+   if (abs(prj->flag) != AZP) {
+      if (azpset(prj)) return 1;
+   }
+
+   cphi = cosdeg (phi);
+   cthe = cosdeg (theta);
+
+   s = prj->w[1]*cphi;
+   t = (prj->p[1] + sindeg (theta)) + cthe*s;
+   if (t == 0.0) {
+      return 2;
+   }
+
+   r  =  prj->w[0]*cthe/t;
+   *x =  r*sindeg (phi);
+   *y = -r*cphi*prj->w[2];
+
+   /* Bounds checking. */
+   if (prj->flag > 0) {
+      /* Overlap. */
+      if (theta < prj->w[5]) {
+         return 2;
+      }
+
+      /* Divergence. */
+      if (prj->w[7] > 0.0) {
+         t = prj->p[1] / sqrt(1.0 + s*s);
+
+         if (fabs(t) <= 1.0) {
+            s = atandeg (-s);
+            t = asindeg (t);
+            a = s - t;
+            b = s + t + 180.0;
+
+            if (a > 90.0) a -= 360.0;
+            if (b > 90.0) b -= 360.0;
+
+            if (theta < ((a > b) ? a : b)) {
+               return 2;
+            }
+         }
+      }
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int azprev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double a, b, r, s, t, ycosg;
+   const double tol = 1.0e-13;
+
+   if (abs(prj->flag) != AZP) {
+      if (azpset(prj)) return 1;
+   }
+
+   ycosg = y*prj->w[3];
+
+   r = sqrt(x*x + ycosg*ycosg);
+   if (r == 0.0) {
+      *phi   =  0.0;
+      *theta = 90.0;
+   } else {
+      *phi = atan2deg (x, -ycosg);
+
+      s = r / (prj->w[0] + y*prj->w[4]);
+      t = s*prj->p[1]/sqrt(s*s + 1.0);
+
+      s = atan2deg (1.0, s);
+
+      if (fabs(t) > 1.0) {
+         t = copysgn (90.0,t);
+         if (fabs(t) > 1.0+tol) {
+            return 2;
+         }
+      } else {
+         t = asindeg (t);
+      }
+
+      a = s - t;
+      b = s + t + 180.0;
+
+      if (a > 90.0) a -= 360.0;
+      if (b > 90.0) b -= 360.0;
+
+      *theta = (a > b) ? a : b;
+   }
+
+   return 0;
+}
+
+/*============================================================================
+*   SZP: slant zenithal perspective projection.
+*
+*   Given:
+*      prj->p[1]    Distance of the point of projection from the centre of the
+*                   generating sphere, mu in units of r0.
+*      prj->p[2]    Native longitude, phi_c, and ...
+*      prj->p[3]    Native latitude, theta_c, on the planewards side of the
+*                   intersection of the line through the point of projection
+*                   and the centre of the generating sphere, phi_c in degrees.
+*
+*   Given and/or returned:
+*      prj->flag    SZP, or -SZP if prj->flag is given < 0.
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "SZP"
+*      prj->phi0     0.0
+*      prj->theta0  90.0
+*      prj->w[0]    1/r0
+*      prj->w[1]    xp = -mu*cos(theta_c)*sin(phi_c)
+*      prj->w[2]    yp =  mu*cos(theta_c)*cos(phi_c)
+*      prj->w[3]    zp =  mu*sin(theta_c) + 1
+*      prj->w[4]    r0*xp
+*      prj->w[5]    r0*yp
+*      prj->w[6]    r0*zp
+*      prj->w[7]    (zp - 1)^2
+*      prj->w[8]    asin(1-zp) if |1 - zp| < 1, -90 otherwise
+*      prj->prjfwd  Pointer to szpfwd().
+*      prj->prjrev  Pointer to szprev().
+*===========================================================================*/
+
+int szpset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "SZP");
+   prj->flag   = copysgni (SZP, prj->flag);
+   prj->phi0   =  0.0;
+   prj->theta0 = 90.0;
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   prj->w[0] = 1.0/prj->r0;
+
+   prj->w[3] = prj->p[1] * sindeg (prj->p[3]) + 1.0;
+   if (prj->w[3] == 0.0) {
+      return 1;
+   }
+
+   prj->w[1] = -prj->p[1] * cosdeg (prj->p[3]) * sindeg (prj->p[2]);
+   prj->w[2] =  prj->p[1] * cosdeg (prj->p[3]) * cosdeg (prj->p[2]);
+   prj->w[4] =  prj->r0 * prj->w[1];
+   prj->w[5] =  prj->r0 * prj->w[2];
+   prj->w[6] =  prj->r0 * prj->w[3];
+   prj->w[7] =  (prj->w[3] - 1.0) * prj->w[3] - 1.0;
+
+   if (fabs(prj->w[3] - 1.0) < 1.0) {
+      prj->w[8] = asindeg (1.0 - prj->w[3]);
+   } else {
+      prj->w[8] = -90.0;
+   }
+
+   prj->prjfwd = szpfwd;
+   prj->prjrev = szprev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int szpfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double a, b, cphi, cthe, s, sphi, t;
+
+   if (abs(prj->flag) != SZP) {
+      if (szpset(prj)) return 1;
+   }
+
+   cphi = cosdeg (phi);
+   sphi = sindeg (phi);
+   cthe = cosdeg (theta);
+   s = 1.0 - sindeg (theta);
+
+   t = prj->w[3] - s;
+   if (t == 0.0) {
+      return 2;
+   }
+
+   *x =  (prj->w[6]*cthe*sphi - prj->w[4]*s)/t;
+   *y = -(prj->w[6]*cthe*cphi + prj->w[5]*s)/t;
+
+   /* Bounds checking. */
+   if (prj->flag > 0) {
+      /* Divergence. */
+      if (theta < prj->w[8]) {
+         return 2;
+      }
+
+      /* Overlap. */
+      if (fabs(prj->p[1]) > 1.0) {
+         s = prj->w[1]*sphi - prj->w[2]*cphi;
+         t = 1.0/sqrt(prj->w[7] + s*s);
+
+         if (fabs(t) <= 1.0) {
+            s = atan2deg (s, prj->w[3] - 1.0);
+            t = asindeg (t);
+            a = s - t;
+            b = s + t + 180.0;
+
+            if (a > 90.0) a -= 360.0;
+            if (b > 90.0) b -= 360.0;
+
+            if (theta < ((a > b) ? a : b)) {
+               return 2;
+            }
+         }
+      }
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int szprev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double a, b, c, d, r2, sth1, sth2, sthe, sxy, t, x1, xp, y1, yp, z;
+   const double tol = 1.0e-13;
+
+   if (abs(prj->flag) != SZP) {
+      if (szpset(prj)) return 1;
+   }
+
+   xp = x*prj->w[0];
+   yp = y*prj->w[0];
+   r2 = xp*xp + yp*yp;
+
+   x1 = (xp - prj->w[1])/prj->w[3];
+   y1 = (yp - prj->w[2])/prj->w[3];
+   sxy = xp*x1 + yp*y1;
+
+   if (r2 < 1.0e-10) {
+      /* Use small angle formula. */
+      z = r2/2.0;
+      *theta = 90.0 - R2D*sqrt(r2/(1.0 + sxy));
+
+   } else {
+      t = x1*x1 + y1*y1;
+      a = t + 1.0;
+      b = sxy - t;
+      c = r2 - sxy - sxy + t - 1.0;
+      d = b*b - a*c;
+
+      /* Check for a solution. */
+      if (d < 0.0) {
+         return 2;
+      }
+      d = sqrt(d);
+
+      /* Choose solution closest to pole. */
+      sth1 = (-b + d)/a;
+      sth2 = (-b - d)/a;
+      sthe = (sth1 > sth2) ? sth1 : sth2;
+      if (sthe > 1.0) {
+         if (sthe-1.0 < tol) {
+            sthe = 1.0;
+         } else {
+            sthe = (sth1 < sth2) ? sth1 : sth2;
+         }
+      }
+
+      if (sthe < -1.0) {
+         if (sthe+1.0 > -tol) {
+            sthe = -1.0;
+         }
+      }
+
+      if (sthe > 1.0 || sthe < -1.0) {
+         return 2;
+      }
+
+      *theta = asindeg (sthe);
+
+      z = 1.0 - sthe;
+   }
+
+   *phi = atan2deg (xp - x1*z, -(yp - y1*z));
+
+   return 0;
+}
+
+/*============================================================================
+*   TAN: gnomonic projection.
+*
+*   Given and/or returned:
+*      prj->flag    TAN, or -TAN if prj->flag is given < 0.
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "TAN"
+*      prj->phi0     0.0
+*      prj->theta0  90.0
+*      prj->prjfwd  Pointer to tanfwd().
+*      prj->prjrev  Pointer to tanrev().
+*===========================================================================*/
+
+int tanset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "TAN");
+   prj->flag   = copysgni (TAN, prj->flag);
+   prj->phi0   =  0.0;
+   prj->theta0 = 90.0;
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   prj->prjfwd = tanfwd;
+   prj->prjrev = tanrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int tanfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double r, s;
+
+   if (abs(prj->flag) != TAN) {
+      if(tanset(prj)) return 1;
+   }
+
+   s = sindeg (theta);
+   if (s <= 0.0) {
+      return 2;
+   }
+
+   r =  prj->r0*cosdeg (theta)/s;
+   *x =  r*sindeg (phi);
+   *y = -r*cosdeg (phi);
+
+   if (prj->flag > 0 && s < 0.0) {
+      return 2;
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int tanrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double r;
+
+   if (abs(prj->flag) != TAN) {
+      if (tanset(prj)) return 1;
+   }
+
+   r = sqrt(x*x + y*y);
+   if (r == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = atan2deg (x, -y);
+   }
+   *theta = atan2deg (prj->r0, r);
+
+   return 0;
+}
+
+/*============================================================================
+*   STG: stereographic projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "STG"
+*      prj->flag     STG
+*      prj->phi0     0.0
+*      prj->theta0  90.0
+*      prj->w[0]    2*r0
+*      prj->w[1]    1/(2*r0)
+*      prj->prjfwd  Pointer to stgfwd().
+*      prj->prjrev  Pointer to stgrev().
+*===========================================================================*/
+
+int stgset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "STG");
+   prj->flag   =  STG;
+   prj->phi0   =  0.0;
+   prj->theta0 = 90.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 360.0/PI;
+      prj->w[1] = PI/360.0;
+   } else {
+      prj->w[0] = 2.0*prj->r0;
+      prj->w[1] = 1.0/prj->w[0];
+   }
+
+   prj->prjfwd = stgfwd;
+   prj->prjrev = stgrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int stgfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double r, s;
+
+   if (prj->flag != STG) {
+      if (stgset(prj)) return 1;
+   }
+
+   s = 1.0 + sindeg (theta);
+   if (s == 0.0) {
+      return 2;
+   }
+
+   r =  prj->w[0]*cosdeg (theta)/s;
+   *x =  r*sindeg (phi);
+   *y = -r*cosdeg (phi);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int stgrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double r;
+
+   if (prj->flag != STG) {
+      if (stgset(prj)) return 1;
+   }
+
+   r = sqrt(x*x + y*y);
+   if (r == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = atan2deg (x, -y);
+   }
+   *theta = 90.0 - 2.0*atandeg (r*prj->w[1]);
+
+   return 0;
+}
+
+/*============================================================================
+*   SIN: orthographic/synthesis projection.
+*
+*   Given:
+*      prj->p[1:2]  Obliqueness parameters, xi and eta.
+*
+*   Given and/or returned:
+*      prj->flag    SIN, or -SIN if prj->flag is given < 0.
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "SIN"
+*      prj->phi0     0.0
+*      prj->theta0  90.0
+*      prj->w[0]    1/r0
+*      prj->w[1]    xi**2 + eta**2
+*      prj->w[2]    xi**2 + eta**2 + 1
+*      prj->w[3]    xi**2 + eta**2 - 1
+*      prj->prjfwd  Pointer to sinfwd().
+*      prj->prjrev  Pointer to sinrev().
+*===========================================================================*/
+
+int sinset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "SIN");
+   prj->flag   = copysgni (SIN, prj->flag);
+   prj->phi0   =  0.0;
+   prj->theta0 = 90.0;
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   prj->w[0] = 1.0/prj->r0;
+   prj->w[1] = prj->p[1]*prj->p[1] + prj->p[2]*prj->p[2];
+   prj->w[2] = prj->w[1] + 1.0;
+   prj->w[3] = prj->w[1] - 1.0;
+
+   prj->prjfwd = sinfwd;
+   prj->prjrev = sinrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int sinfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double cphi, cthe, sphi, t, z;
+
+   if (abs(prj->flag) != SIN) {
+      if (sinset(prj)) return 1;
+   }
+
+   t = (90.0 - fabs(theta))*D2R;
+   if (t < 1.0e-5) {
+      if (theta > 0.0) {
+         z = t*t/2.0;
+      } else {
+         z = 2.0 - t*t/2.0;
+      }
+      cthe = t;
+   } else {
+      z =  1.0 - sindeg (theta);
+      cthe = cosdeg (theta);
+   }
+
+   cphi = cosdeg (phi);
+   sphi = sindeg (phi);
+   *x =  prj->r0*(cthe*sphi + prj->p[1]*z);
+   *y = -prj->r0*(cthe*cphi - prj->p[2]*z);
+
+   /* Validate this solution. */
+   if (prj->flag > 0) {
+      if (prj->w[1] == 0.0) {
+         /* Orthographic projection. */
+         if (theta < 0.0) {
+            return 2;
+         }
+      } else {
+         /* "Synthesis" projection. */
+         t = -atandeg (prj->p[1]*sphi - prj->p[2]*cphi);
+         if (theta < t) {
+            return 2;
+         }
+      }
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int sinrev (x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   const double tol = 1.0e-13;
+   double a, b, c, d, r2, sth1, sth2, sthe, sxy, x0, x1, xp, y0, y1, yp, z;
+
+   if (abs(prj->flag) != SIN) {
+      if (sinset(prj)) return 1;
+   }
+
+   /* Compute intermediaries. */
+   x0 = x*prj->w[0];
+   y0 = y*prj->w[0];
+   r2 = x0*x0 + y0*y0;
+
+   if (prj->w[1] == 0.0) {
+      /* Orthographic projection. */
+      if (r2 != 0.0) {
+         *phi = atan2deg (x0, -y0);
+      } else {
+         *phi = 0.0;
+      }
+
+      if (r2 < 0.5) {
+         *theta = acosdeg (sqrt(r2));
+      } else if (r2 <= 1.0) {
+         *theta = asindeg (sqrt(1.0 - r2));
+      } else {
+         return 2;
+      }
+
+   } else {
+      /* "Synthesis" projection. */
+      x1 = prj->p[1];
+      y1 = prj->p[2];
+      sxy = x0*x1 + y0*y1;
+
+      if (r2 < 1.0e-10) {
+         /* Use small angle formula. */
+         z = r2/2.0;
+         *theta = 90.0 - R2D*sqrt(r2/(1.0 + sxy));
+
+      } else {
+         a = prj->w[2];
+         b = sxy - prj->w[1];
+         c = r2 - sxy - sxy + prj->w[3];
+         d = b*b - a*c;
+
+         /* Check for a solution. */
+         if (d < 0.0) {
+            return 2;
+         }
+         d = sqrt(d);
+
+         /* Choose solution closest to pole. */
+         sth1 = (-b + d)/a;
+         sth2 = (-b - d)/a;
+         sthe = (sth1 > sth2) ? sth1 : sth2;
+         if (sthe > 1.0) {
+            if (sthe-1.0 < tol) {
+               sthe = 1.0;
+            } else {
+               sthe = (sth1 < sth2) ? sth1 : sth2;
+            }
+         }
+
+         if (sthe < -1.0) {
+            if (sthe+1.0 > -tol) {
+               sthe = -1.0;
+            }
+         }
+
+         if (sthe > 1.0 || sthe < -1.0) {
+            return 2;
+         }
+
+         *theta = asindeg (sthe);
+         z = 1.0 - sthe;
+      }
+
+      xp = -y0 + prj->p[2]*z;
+      yp =  x0 - prj->p[1]*z;
+      if (xp == 0.0 && yp == 0.0) {
+         *phi = 0.0;
+      } else {
+         *phi = atan2deg (yp,xp);
+      }
+   }
+
+   return 0;
+}
+
+/*============================================================================
+*   ARC: zenithal/azimuthal equidistant projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "ARC"
+*      prj->flag     ARC
+*      prj->phi0     0.0
+*      prj->theta0  90.0
+*      prj->w[0]    r0*(pi/180)
+*      prj->w[1]    (180/pi)/r0
+*      prj->prjfwd  Pointer to arcfwd().
+*      prj->prjrev  Pointer to arcrev().
+*===========================================================================*/
+
+int arcset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "ARC");
+   prj->flag   =  ARC;
+   prj->phi0   =  0.0;
+   prj->theta0 = 90.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 1.0;
+      prj->w[1] = 1.0;
+   } else {
+      prj->w[0] = prj->r0*D2R;
+      prj->w[1] = 1.0/prj->w[0];
+   }
+
+   prj->prjfwd = arcfwd;
+   prj->prjrev = arcrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int arcfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double r;
+
+   if (prj->flag != ARC) {
+      if (arcset(prj)) return 1;
+   }
+
+   r =  prj->w[0]*(90.0 - theta);
+   *x =  r*sindeg (phi);
+   *y = -r*cosdeg (phi);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int arcrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double r;
+
+   if (prj->flag != ARC) {
+      if (arcset(prj)) return 1;
+   }
+
+   r = sqrt(x*x + y*y);
+   if (r == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = atan2deg (x, -y);
+   }
+   *theta = 90.0 - r*prj->w[1];
+
+   return 0;
+}
+
+/*============================================================================
+*   ZPN: zenithal/azimuthal polynomial projection.
+*
+*   Given:
+*      prj->p[0:9]  Polynomial coefficients.
+*
+*   Given and/or returned:
+*      prj->flag    ZPN, or -ZPN if prj->flag is given < 0.
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "ZPN"
+*      prj->phi0     0.0
+*      prj->theta0  90.0
+*      prj->n       Degree of the polynomial, N.
+*      prj->w[0]    Co-latitude of the first point of inflection (N > 2).
+*      prj->w[1]    Radius of the first point of inflection (N > 2).
+*      prj->prjfwd  Pointer to zpnfwd().
+*      prj->prjrev  Pointer to zpnrev().
+*===========================================================================*/
+
+int zpnset(prj)
+
+struct prjprm *prj;
+
+{
+   int   i, j, k;
+   double d, d1, d2, r, zd, zd1, zd2;
+   const double tol = 1.0e-13;
+
+   strcpy(prj->code, "ZPN");
+   prj->flag   = copysgni (ZPN, prj->flag);
+   prj->phi0   =  0.0;
+   prj->theta0 = 90.0;
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   /* Find the highest non-zero coefficient. */
+   for (k = 9; k >= 0 && prj->p[k] == 0.0; k--);
+   if (k < 0) return 1;
+
+   prj->n = k;
+
+   if (k >= 3) {
+      /* Find the point of inflection closest to the pole. */
+      zd1 = 0.0;
+      d1  = prj->p[1];
+      if (d1 <= 0.0) {
+         return 1;
+      }
+
+      /* Find the point where the derivative first goes negative. */
+      for (i = 0; i < 180; i++) {
+         zd2 = i*D2R;
+         d2  = 0.0;
+         for (j = k; j > 0; j--) {
+            d2 = d2*zd2 + j*prj->p[j];
+         }
+
+         if (d2 <= 0.0) break;
+         zd1 = zd2;
+         d1  = d2;
+      }
+
+      if (i == 180) {
+         /* No negative derivative -> no point of inflection. */
+         zd = PI;
+      } else {
+         /* Find where the derivative is zero. */
+         for (i = 1; i <= 10; i++) {
+            zd = zd1 - d1*(zd2-zd1)/(d2-d1);
+
+            d = 0.0;
+            for (j = k; j > 0; j--) {
+               d = d*zd + j*prj->p[j];
+            }
+
+            if (fabs(d) < tol) break;
+
+            if (d < 0.0) {
+               zd2 = zd;
+               d2  = d;
+            } else {
+               zd1 = zd;
+               d1  = d;
+            }
+         }
+      }
+
+      r = 0.0;
+      for (j = k; j >= 0; j--) {
+         r = r*zd + prj->p[j];
+      }
+      prj->w[0] = zd;
+      prj->w[1] = r;
+   }
+
+   prj->prjfwd = zpnfwd;
+   prj->prjrev = zpnrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int zpnfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   int   j;
+   double r, s;
+
+   if (abs(prj->flag) != ZPN) {
+      if (zpnset(prj)) return 1;
+   }
+
+   s = (90.0 - theta)*D2R;
+
+   r = 0.0;
+   for (j = 9; j >= 0; j--) {
+      r = r*s + prj->p[j];
+   }
+   r = prj->r0*r;
+
+   *x =  r*sindeg (phi);
+   *y = -r*cosdeg (phi);
+
+   if (prj->flag > 0 && s > prj->w[0]) {
+      return 2;
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int zpnrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   int   i, j, k;
+   double a, b, c, d, lambda, r, r1, r2, rt, zd, zd1, zd2;
+   const double tol = 1.0e-13;
+
+   if (abs(prj->flag) != ZPN) {
+      if (zpnset(prj)) return 1;
+   }
+
+   k = prj->n;
+
+   r = sqrt(x*x + y*y)/prj->r0;
+
+   if (k < 1) {
+      /* Constant - no solution. */
+      return 1;
+   } else if (k == 1) {
+      /* Linear. */
+      zd = (r - prj->p[0])/prj->p[1];
+   } else if (k == 2) {
+      /* Quadratic. */
+      a = prj->p[2];
+      b = prj->p[1];
+      c = prj->p[0] - r;
+
+      d = b*b - 4.0*a*c;
+      if (d < 0.0) {
+         return 2;
+      }
+      d = sqrt(d);
+
+      /* Choose solution closest to pole. */
+      zd1 = (-b + d)/(2.0*a);
+      zd2 = (-b - d)/(2.0*a);
+      zd  = (zd1<zd2) ? zd1 : zd2;
+      if (zd < -tol) zd = (zd1>zd2) ? zd1 : zd2;
+      if (zd < 0.0) {
+         if (zd < -tol) {
+            return 2;
+         }
+         zd = 0.0;
+      } else if (zd > PI) {
+         if (zd > PI+tol) {
+            return 2;
+         }
+         zd = PI;
+      }
+   } else {
+      /* Higher order - solve iteratively. */
+      zd1 = 0.0;
+      r1  = prj->p[0];
+      zd2 = prj->w[0];
+      r2  = prj->w[1];
+
+      if (r < r1) {
+         if (r < r1-tol) {
+            return 2;
+         }
+         zd = zd1;
+      } else if (r > r2) {
+         if (r > r2+tol) {
+            return 2;
+         }
+         zd = zd2;
+      } else {
+         /* Disect the interval. */
+         for (j = 0; j < 100; j++) {
+            lambda = (r2 - r)/(r2 - r1);
+            if (lambda < 0.1) {
+               lambda = 0.1;
+            } else if (lambda > 0.9) {
+               lambda = 0.9;
+            }
+
+            zd = zd2 - lambda*(zd2 - zd1);
+
+            rt = 0.0;
+            for (i = k; i >= 0; i--) {
+                rt = (rt * zd) + prj->p[i];
+            }
+
+            if (rt < r) {
+                if (r-rt < tol) break;
+                r1 = rt;
+                zd1 = zd;
+            } else {
+                if (rt-r < tol) break;
+                r2 = rt;
+                zd2 = zd;
+            }
+
+            if (fabs(zd2-zd1) < tol) break;
+         }
+      }
+   }
+
+   if (r == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = atan2deg (x, -y);
+   }
+   *theta = 90.0 - zd*R2D;
+
+   return 0;
+}
+
+/*============================================================================
+*   ZEA: zenithal/azimuthal equal area projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "ZEA"
+*      prj->flag     ZEA
+*      prj->phi0     0.0
+*      prj->theta0  90.0
+*      prj->w[0]    2*r0
+*      prj->w[1]    1/(2*r0)
+*      prj->prjfwd  Pointer to zeafwd().
+*      prj->prjrev  Pointer to zearev().
+*===========================================================================*/
+
+int zeaset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "ZEA");
+   prj->flag   =  ZEA;
+   prj->phi0   =  0.0;
+   prj->theta0 = 90.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 360.0/PI;
+      prj->w[1] = PI/360.0;
+   } else {
+      prj->w[0] = 2.0*prj->r0;
+      prj->w[1] = 1.0/prj->w[0];
+   }
+
+   prj->prjfwd = zeafwd;
+   prj->prjrev = zearev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int zeafwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double r;
+
+   if (prj->flag != ZEA) {
+      if (zeaset(prj)) return 1;
+   }
+
+   r =  prj->w[0]*sindeg ((90.0 - theta)/2.0);
+   *x =  r*sindeg (phi);
+   *y = -r*cosdeg (phi);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int zearev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double r, s;
+   const double tol = 1.0e-12;
+
+   if (prj->flag != ZEA) {
+      if (zeaset(prj)) return 1;
+   }
+
+   r = sqrt(x*x + y*y);
+   if (r == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = atan2deg (x, -y);
+   }
+
+   s = r*prj->w[1];
+   if (fabs(s) > 1.0) {
+      if (fabs(r - prj->w[0]) < tol) {
+         *theta = -90.0;
+      } else {
+         return 2;
+      }
+   } else {
+      *theta = 90.0 - 2.0*asindeg (s);
+   }
+
+   return 0;
+}
+
+/*============================================================================
+*   AIR: Airy's projection.
+*
+*   Given:
+*      prj->p[1]    Latitude theta_b within which the error is minimized, in
+*                   degrees.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "AIR"
+*      prj->flag     AIR
+*      prj->phi0     0.0
+*      prj->theta0  90.0
+*      prj->w[0]    2*r0
+*      prj->w[1]    ln(cos(xi_b))/tan(xi_b)**2, where xi_b = (90-theta_b)/2
+*      prj->w[2]    1/2 - prj->w[1]
+*      prj->w[3]    2*r0*prj->w[2]
+*      prj->w[4]    tol, cutoff for using small angle approximation, in
+*                   radians.
+*      prj->w[5]    prj->w[2]*tol
+*      prj->w[6]    (180/pi)/prj->w[2]
+*      prj->prjfwd  Pointer to airfwd().
+*      prj->prjrev  Pointer to airrev().
+*===========================================================================*/
+
+int airset(prj)
+
+struct prjprm *prj;
+
+{
+   const double tol = 1.0e-4;
+   double cxi;
+
+   strcpy(prj->code, "AIR");
+   prj->flag   =  AIR;
+   prj->phi0   =  0.0;
+   prj->theta0 = 90.0;
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   prj->w[0] = 2.0*prj->r0;
+   if (prj->p[1] == 90.0) {
+      prj->w[1] = -0.5;
+      prj->w[2] =  1.0;
+   } else if (prj->p[1] > -90.0) {
+      cxi = cosdeg ((90.0 - prj->p[1])/2.0);
+      prj->w[1] = log(cxi)*(cxi*cxi)/(1.0-cxi*cxi);
+      prj->w[2] = 0.5 - prj->w[1];
+   } else {
+      return 1;
+   }
+
+   prj->w[3] = prj->w[0] * prj->w[2];
+   prj->w[4] = tol;
+   prj->w[5] = prj->w[2]*tol;
+   prj->w[6] = R2D/prj->w[2];
+
+   prj->prjfwd = airfwd;
+   prj->prjrev = airrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int airfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double cxi, r, txi, xi;
+
+   if (prj->flag != AIR) {
+      if (airset(prj)) return 1;
+   }
+
+   if (theta == 90.0) {
+      r = 0.0;
+   } else if (theta > -90.0) {
+      xi = D2R*(90.0 - theta)/2.0;
+      if (xi < prj->w[4]) {
+         r = xi*prj->w[3];
+      } else {
+         cxi = cosdeg ((90.0 - theta)/2.0);
+         txi = sqrt(1.0-cxi*cxi)/cxi;
+         r = -prj->w[0]*(log(cxi)/txi + prj->w[1]*txi);
+      }
+   } else {
+      return 2;
+   }
+
+   *x =  r*sindeg (phi);
+   *y = -r*cosdeg (phi);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int airrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   int   j;
+   double cxi, lambda, r, r1, r2, rt, txi, x1, x2, xi;
+   const double tol = 1.0e-12;
+
+   if (prj->flag != AIR) {
+      if (airset(prj)) return 1;
+   }
+
+   r = sqrt(x*x + y*y)/prj->w[0];
+
+   if (r == 0.0) {
+      xi = 0.0;
+   } else if (r < prj->w[5]) {
+      xi = r*prj->w[6];
+   } else {
+      /* Find a solution interval. */
+      x1 = 1.0;
+      r1 = 0.0;
+      for (j = 0; j < 30; j++) {
+         x2 = x1/2.0;
+         txi = sqrt(1.0-x2*x2)/x2;
+         r2 = -(log(x2)/txi + prj->w[1]*txi);
+
+         if (r2 >= r) break;
+         x1 = x2;
+         r1 = r2;
+      }
+      if (j == 30) return 2;
+
+      for (j = 0; j < 100; j++) {
+         /* Weighted division of the interval. */
+         lambda = (r2-r)/(r2-r1);
+         if (lambda < 0.1) {
+            lambda = 0.1;
+         } else if (lambda > 0.9) {
+            lambda = 0.9;
+         }
+         cxi = x2 - lambda*(x2-x1);
+
+         txi = sqrt(1.0-cxi*cxi)/cxi;
+         rt = -(log(cxi)/txi + prj->w[1]*txi);
+
+         if (rt < r) {
+             if (r-rt < tol) break;
+             r1 = rt;
+             x1 = cxi;
+         } else {
+             if (rt-r < tol) break;
+             r2 = rt;
+             x2 = cxi;
+         }
+      }
+      if (j == 100) return 2;
+
+      xi = acosdeg (cxi);
+   }
+
+   if (r == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = atan2deg (x, -y);
+   }
+   *theta = 90.0 - 2.0*xi;
+
+   return 0;
+}
+
+/*============================================================================
+*   CYP: cylindrical perspective projection.
+*
+*   Given:
+*      prj->p[1]    Distance of point of projection from the centre of the
+*                   generating sphere, mu, in units of r0.
+*      prj->p[2]    Radius of the cylinder of projection, lambda, in units of
+*                   r0.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "CYP"
+*      prj->flag    CYP
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*lambda*(pi/180)
+*      prj->w[1]    (180/pi)/(r0*lambda)
+*      prj->w[2]    r0*(mu + lambda)
+*      prj->w[3]    1/(r0*(mu + lambda))
+*      prj->prjfwd  Pointer to cypfwd().
+*      prj->prjrev  Pointer to cyprev().
+*===========================================================================*/
+
+int cypset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "CYP");
+   prj->flag   = CYP;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+
+      prj->w[0] = prj->p[2];
+      if (prj->w[0] == 0.0) {
+         return 1;
+      }
+
+      prj->w[1] = 1.0/prj->w[0];
+
+      prj->w[2] = R2D*(prj->p[1] + prj->p[2]);
+      if (prj->w[2] == 0.0) {
+         return 1;
+      }
+
+      prj->w[3] = 1.0/prj->w[2];
+   } else {
+      prj->w[0] = prj->r0*prj->p[2]*D2R;
+      if (prj->w[0] == 0.0) {
+         return 1;
+      }
+
+      prj->w[1] = 1.0/prj->w[0];
+
+      prj->w[2] = prj->r0*(prj->p[1] + prj->p[2]);
+      if (prj->w[2] == 0.0) {
+         return 1;
+      }
+
+      prj->w[3] = 1.0/prj->w[2];
+   }
+
+   prj->prjfwd = cypfwd;
+   prj->prjrev = cyprev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int cypfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double s;
+
+   if (prj->flag != CYP) {
+      if (cypset(prj)) return 1;
+   }
+
+   s = prj->p[1] + cosdeg (theta);
+   if (s == 0.0) {
+         return 2;
+      }
+
+   *x = prj->w[0]*phi;
+   *y = prj->w[2]*sindeg (theta)/s;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int cyprev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double eta;
+
+   if (prj->flag != CYP) {
+      if (cypset(prj)) return 1;
+   }
+
+   *phi   = x*prj->w[1];
+   eta    = y*prj->w[3];
+   *theta = atan2deg (eta,1.0) + asindeg (eta*prj->p[1]/sqrt(eta*eta+1.0));
+
+   return 0;
+}
+
+/*============================================================================
+*   CEA: cylindrical equal area projection.
+*
+*   Given:
+*      prj->p[1]    Square of the cosine of the latitude at which the
+*                   projection is conformal, lambda.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "CEA"
+*      prj->flag    CEA
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*(pi/180)
+*      prj->w[1]    (180/pi)/r0
+*      prj->w[2]    r0/lambda
+*      prj->w[3]    lambda/r0
+*      prj->prjfwd  Pointer to ceafwd().
+*      prj->prjrev  Pointer to cearev().
+*===========================================================================*/
+
+int ceaset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "CEA");
+   prj->flag   = CEA;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 1.0;
+      prj->w[1] = 1.0;
+      if (prj->p[1] <= 0.0 || prj->p[1] > 1.0) {
+         return 1;
+      }
+      prj->w[2] = prj->r0/prj->p[1];
+      prj->w[3] = prj->p[1]/prj->r0;
+   } else {
+      prj->w[0] = prj->r0*D2R;
+      prj->w[1] = R2D/prj->r0;
+      if (prj->p[1] <= 0.0 || prj->p[1] > 1.0) {
+         return 1;
+      }
+      prj->w[2] = prj->r0/prj->p[1];
+      prj->w[3] = prj->p[1]/prj->r0;
+   }
+
+   prj->prjfwd = ceafwd;
+   prj->prjrev = cearev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int ceafwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   if (prj->flag != CEA) {
+      if (ceaset(prj)) return 1;
+   }
+
+   *x = prj->w[0]*phi;
+   *y = prj->w[2]*sindeg (theta);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int cearev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double s;
+   const double tol = 1.0e-13;
+
+   if (prj->flag != CEA) {
+      if (ceaset(prj)) return 1;
+   }
+
+   s = y*prj->w[3];
+   if (fabs(s) > 1.0) {
+      if (fabs(s) > 1.0+tol) {
+         return 2;
+      }
+      s = copysgn (1.0,s);
+   }
+
+   *phi   = x*prj->w[1];
+   *theta = asindeg (s);
+
+   return 0;
+}
+
+/*============================================================================
+*   CAR: Cartesian projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "CAR"
+*      prj->flag    CAR
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*(pi/180)
+*      prj->w[1]    (180/pi)/r0
+*      prj->prjfwd  Pointer to carfwd().
+*      prj->prjrev  Pointer to carrev().
+*===========================================================================*/
+
+int carset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "CAR");
+   prj->flag   = CAR;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 1.0;
+      prj->w[1] = 1.0;
+   } else {
+      prj->w[0] = prj->r0*D2R;
+      prj->w[1] = 1.0/prj->w[0];
+   }
+
+   prj->prjfwd = carfwd;
+   prj->prjrev = carrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int carfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   if (prj->flag != CAR) {
+      if (carset(prj)) return 1;
+   }
+
+   *x = prj->w[0]*phi;
+   *y = prj->w[0]*theta;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int carrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   if (prj->flag != CAR) {
+      if (carset(prj)) return 1;
+   }
+
+   *phi   = prj->w[1]*x;
+   *theta = prj->w[1]*y;
+
+   return 0;
+}
+
+/*============================================================================
+*   MER: Mercator's projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "MER"
+*      prj->flag    MER
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*(pi/180)
+*      prj->w[1]    (180/pi)/r0
+*      prj->prjfwd  Pointer to merfwd().
+*      prj->prjrev  Pointer to merrev().
+*===========================================================================*/
+
+int merset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "MER");
+   prj->flag   = MER;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 1.0;
+      prj->w[1] = 1.0;
+   } else {
+      prj->w[0] = prj->r0*D2R;
+      prj->w[1] = 1.0/prj->w[0];
+   }
+
+   prj->prjfwd = merfwd;
+   prj->prjrev = merrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int merfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   if (prj->flag != MER) {
+      if (merset(prj)) return 1;
+   }
+
+   if (theta <= -90.0 || theta >= 90.0) {
+      return 2;
+   }
+
+   *x = prj->w[0]*phi;
+   *y = prj->r0*log(tandeg ((90.0+theta)/2.0));
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int merrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   if (prj->flag != MER) {
+      if (merset(prj)) return 1;
+   }
+
+   *phi   = x*prj->w[1];
+   *theta = 2.0*atandeg (exp(y/prj->r0)) - 90.0;
+
+   return 0;
+}
+
+/*============================================================================
+*   SFL: Sanson-Flamsteed ("global sinusoid") projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "SFL"
+*      prj->flag    SFL
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*(pi/180)
+*      prj->w[1]    (180/pi)/r0
+*      prj->prjfwd  Pointer to sflfwd().
+*      prj->prjrev  Pointer to sflrev().
+*===========================================================================*/
+
+int sflset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "SFL");
+   prj->flag   = SFL;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 1.0;
+      prj->w[1] = 1.0;
+   } else {
+      prj->w[0] = prj->r0*D2R;
+      prj->w[1] = 1.0/prj->w[0];
+   }
+
+   prj->prjfwd = sflfwd;
+   prj->prjrev = sflrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int sflfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   if (prj->flag != SFL) {
+      if (sflset(prj)) return 1;
+   }
+
+   *x = prj->w[0]*phi*cosdeg (theta);
+   *y = prj->w[0]*theta;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int sflrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double w;
+
+   if (prj->flag != SFL) {
+      if (sflset(prj)) return 1;
+   }
+
+   w = cos(y/prj->r0);
+   if (w == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = x*prj->w[1]/cos(y/prj->r0);
+   }
+   *theta = y*prj->w[1];
+
+   return 0;
+}
+
+/*============================================================================
+*   PAR: parabolic projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "PAR"
+*      prj->flag    PAR
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*(pi/180)
+*      prj->w[1]    (180/pi)/r0
+*      prj->w[2]    pi*r0
+*      prj->w[3]    1/(pi*r0)
+*      prj->prjfwd  Pointer to parfwd().
+*      prj->prjrev  Pointer to parrev().
+*===========================================================================*/
+
+int parset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "PAR");
+   prj->flag   = PAR;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 1.0;
+      prj->w[1] = 1.0;
+      prj->w[2] = 180.0;
+      prj->w[3] = 1.0/prj->w[2];
+   } else {
+      prj->w[0] = prj->r0*D2R;
+      prj->w[1] = 1.0/prj->w[0];
+      prj->w[2] = PI*prj->r0;
+      prj->w[3] = 1.0/prj->w[2];
+   }
+
+   prj->prjfwd = parfwd;
+   prj->prjrev = parrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int parfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double s;
+
+   if (prj->flag != PAR) {
+      if (parset(prj)) return 1;
+   }
+
+   s = sindeg (theta/3.0);
+   *x = prj->w[0]*phi*(1.0 - 4.0*s*s);
+   *y = prj->w[2]*s;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int parrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double s, t;
+
+   if (prj->flag != PAR) {
+      if (parset(prj)) return 1;
+   }
+
+   s = y*prj->w[3];
+   if (s > 1.0 || s < -1.0) {
+      return 2;
+   }
+
+   t = 1.0 - 4.0*s*s;
+   if (t == 0.0) {
+      if (x == 0.0) {
+         *phi = 0.0;
+      } else {
+         return 2;
+      }
+   } else {
+      *phi = prj->w[1]*x/t;
+   }
+
+   *theta = 3.0*asindeg (s);
+
+   return 0;
+}
+
+/*============================================================================
+*   MOL: Mollweide's projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "MOL"
+*      prj->flag    MOL
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    sqrt(2)*r0
+*      prj->w[1]    sqrt(2)*r0/90
+*      prj->w[2]    1/(sqrt(2)*r0)
+*      prj->w[3]    90/r0
+*      prj->prjfwd  Pointer to molfwd().
+*      prj->prjrev  Pointer to molrev().
+*===========================================================================*/
+
+int molset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "MOL");
+   prj->flag   = MOL;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   prj->w[0] = SQRT2*prj->r0;
+   prj->w[1] = prj->w[0]/90.0;
+   prj->w[2] = 1.0/prj->w[0];
+   prj->w[3] = 90.0/prj->r0;
+   prj->w[4] = 2.0/PI;
+
+   prj->prjfwd = molfwd;
+   prj->prjrev = molrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int molfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   int   j;
+   double gamma, resid, u, v, v0, v1;
+   const double tol = 1.0e-13;
+
+   if (prj->flag != MOL) {
+      if (molset(prj)) return 1;
+   }
+
+   if (fabs(theta) == 90.0) {
+      *x = 0.0;
+      *y = copysgn (prj->w[0],theta);
+   } else if (theta == 0.0) {
+      *x = prj->w[1]*phi;
+      *y = 0.0;
+   } else {
+      u  = PI*sindeg (theta);
+      v0 = -PI;
+      v1 =  PI;
+      v  = u;
+      for (j = 0; j < 100; j++) {
+         resid = (v - u) + sin(v);
+         if (resid < 0.0) {
+            if (resid > -tol) break;
+            v0 = v;
+         } else {
+            if (resid < tol) break;
+            v1 = v;
+         }
+         v = (v0 + v1)/2.0;
+      }
+
+      gamma = v/2.0;
+      *x = prj->w[1]*phi*cos(gamma);
+      *y = prj->w[0]*sin(gamma);
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int molrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double s, y0, z;
+   const double tol = 1.0e-12;
+
+   if (prj->flag != MOL) {
+      if (molset(prj)) return 1;
+   }
+
+   y0 = y/prj->r0;
+   s  = 2.0 - y0*y0;
+   if (s <= tol) {
+      if (s < -tol) {
+         return 2;
+      }
+      s = 0.0;
+
+      if (fabs(x) > tol) {
+         return 2;
+      }
+      *phi = 0.0;
+   } else {
+      s = sqrt(s);
+      *phi = prj->w[3]*x/s;
+   }
+
+   z = y*prj->w[2];
+   if (fabs(z) > 1.0) {
+      if (fabs(z) > 1.0+tol) {
+         return 2;
+      }
+      z = copysgn (1.0,z) + y0*s/PI;
+   } else {
+      z = asin(z)*prj->w[4] + y0*s/PI;
+   }
+
+   if (fabs(z) > 1.0) {
+      if (fabs(z) > 1.0+tol) {
+         return 2;
+      }
+      z = copysgn (1.0,z);
+   }
+
+   *theta = asindeg (z);
+
+   return 0;
+}
+
+/*============================================================================
+*   AIT: Hammer-Aitoff projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "AIT"
+*      prj->flag    AIT
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    2*r0**2
+*      prj->w[1]    1/(2*r0)**2
+*      prj->w[2]    1/(4*r0)**2
+*      prj->w[3]    1/(2*r0)
+*      prj->prjfwd  Pointer to aitfwd().
+*      prj->prjrev  Pointer to aitrev().
+*===========================================================================*/
+
+int aitset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "AIT");
+   prj->flag   = AIT;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   prj->w[0] = 2.0*prj->r0*prj->r0;
+   prj->w[1] = 1.0/(2.0*prj->w[0]);
+   prj->w[2] = prj->w[1]/4.0;
+   prj->w[3] = 1.0/(2.0*prj->r0);
+
+   prj->prjfwd = aitfwd;
+   prj->prjrev = aitrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int aitfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double cthe, w;
+
+   if (prj->flag != AIT) {
+      if (aitset(prj)) return 1;
+   }
+
+   cthe = cosdeg (theta);
+   w = sqrt(prj->w[0]/(1.0 + cthe*cosdeg (phi/2.0)));
+   *x = 2.0*w*cthe*sindeg (phi/2.0);
+   *y = w*sindeg (theta);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int aitrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double s, u, xp, yp, z;
+   const double tol = 1.0e-13;
+
+   if (prj->flag != AIT) {
+      if (aitset(prj)) return 1;
+   }
+
+   u = 1.0 - x*x*prj->w[2] - y*y*prj->w[1];
+   if (u < 0.0) {
+      if (u < -tol) {
+         return 2;
+      }
+
+      u = 0.0;
+   }
+
+   z = sqrt(u);
+   s = z*y/prj->r0;
+   if (fabs(s) > 1.0) {
+      if (fabs(s) > 1.0+tol) {
+         return 2;
+      }
+      s = copysgn (1.0,s);
+   }
+
+   xp = 2.0*z*z - 1.0;
+   yp = z*x*prj->w[3];
+   if (xp == 0.0 && yp == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = 2.0*atan2deg (yp, xp);
+   }
+   *theta = asindeg (s);
+
+   return 0;
+}
+
+/*============================================================================
+*   COP: conic perspective projection.
+*
+*   Given:
+*      prj->p[1]    sigma = (theta2+theta1)/2
+*      prj->p[2]    delta = (theta2-theta1)/2, where theta1 and theta2 are the
+*                   latitudes of the standard parallels, in degrees.
+*
+*   Given and/or returned:
+*      prj->flag    COP, or -COP if prj->flag is given < 0.
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "COP"
+*      prj->phi0     0.0
+*      prj->theta0  sigma
+*      prj->w[0]    C  = sin(sigma)
+*      prj->w[1]    1/C
+*      prj->w[2]    Y0 = r0*cos(delta)*cot(sigma)
+*      prj->w[3]    r0*cos(delta)
+*      prj->w[4]    1/(r0*cos(delta)
+*      prj->w[5]    cot(sigma)
+*      prj->prjfwd  Pointer to copfwd().
+*      prj->prjrev  Pointer to coprev().
+*===========================================================================*/
+
+int copset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "COP");
+   prj->flag   = copysgni (COP, prj->flag);
+   prj->phi0   = 0.0;
+   prj->theta0 = prj->p[1];
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   prj->w[0] = sindeg (prj->p[1]);
+   if (prj->w[0] == 0.0) {
+      return 1;
+   }
+
+   prj->w[1] = 1.0/prj->w[0];
+
+   prj->w[3] = prj->r0*cosdeg (prj->p[2]);
+   if (prj->w[3] == 0.0) {
+      return 1;
+   }
+
+   prj->w[4] = 1.0/prj->w[3];
+   prj->w[5] = 1.0/tandeg (prj->p[1]);
+
+   prj->w[2] = prj->w[3]*prj->w[5];
+
+   prj->prjfwd = copfwd;
+   prj->prjrev = coprev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int copfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double a, r, s, t;
+
+   if (abs(prj->flag) != COP) {
+      if (copset(prj)) return 1;
+   }
+
+   t = theta - prj->p[1];
+   s = cosdeg (t);
+   if (s == 0.0) {
+      return 2;
+   }
+
+   a = prj->w[0]*phi;
+   r = prj->w[2] - prj->w[3]*sindeg (t)/s;
+
+   *x =             r*sindeg (a);
+   *y = prj->w[2] - r*cosdeg (a);
+
+   if (prj->flag > 0 && r*prj->w[0] < 0.0) {
+      return 2;
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int coprev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double a, dy, r;
+
+   if (abs(prj->flag) != COP) {
+      if (copset(prj)) return 1;
+   }
+
+   dy = prj->w[2] - y;
+   r  = sqrt(x*x + dy*dy);
+   if (prj->p[1] < 0.0) r = -r;
+
+   if (r == 0.0) {
+      a = 0.0;
+   } else {
+      a = atan2deg (x/r, dy/r);
+   }
+
+   *phi   = a*prj->w[1];
+   *theta = prj->p[1] + atandeg (prj->w[5] - r*prj->w[4]);
+
+   return 0;
+}
+
+/*============================================================================
+*   COE: conic equal area projection.
+*
+*   Given:
+*      prj->p[1]    sigma = (theta2+theta1)/2
+*      prj->p[2]    delta = (theta2-theta1)/2, where theta1 and theta2 are the
+*                   latitudes of the standard parallels, in degrees.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "COE"
+*      prj->flag    COE
+*      prj->phi0    0.0
+*      prj->theta0  sigma
+*      prj->w[0]    C = (sin(theta1) + sin(theta2))/2
+*      prj->w[1]    1/C
+*      prj->w[2]    Y0 = chi*sqrt(psi - 2C*sindeg (sigma))
+*      prj->w[3]    chi = r0/C
+*      prj->w[4]    psi = 1 + sin(theta1)*sin(theta2)
+*      prj->w[5]    2C
+*      prj->w[6]    (1 + sin(theta1)*sin(theta2))*(r0/C)**2
+*      prj->w[7]    C/(2*r0**2)
+*      prj->w[8]    chi*sqrt(psi + 2C)
+*      prj->prjfwd  Pointer to coefwd().
+*      prj->prjrev  Pointer to coerev().
+*===========================================================================*/
+
+int coeset(prj)
+
+struct prjprm *prj;
+
+{
+   double theta1, theta2;
+
+   strcpy(prj->code, "COE");
+   prj->flag   = COE;
+   prj->phi0   = 0.0;
+   prj->theta0 = prj->p[1];
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   theta1 = prj->p[1] - prj->p[2];
+   theta2 = prj->p[1] + prj->p[2];
+
+   prj->w[0] = (sindeg (theta1) + sindeg (theta2))/2.0;
+   if (prj->w[0] == 0.0) {
+      return 1;
+   }
+
+   prj->w[1] = 1.0/prj->w[0];
+
+   prj->w[3] = prj->r0/prj->w[0];
+   prj->w[4] = 1.0 + sindeg (theta1)*sindeg (theta2);
+   prj->w[5] = 2.0*prj->w[0];
+   prj->w[6] = prj->w[3]*prj->w[3]*prj->w[4];
+   prj->w[7] = 1.0/(2.0*prj->r0*prj->w[3]);
+   prj->w[8] = prj->w[3]*sqrt(prj->w[4] + prj->w[5]);
+
+   prj->w[2] = prj->w[3]*sqrt(prj->w[4] - prj->w[5]*sindeg (prj->p[1]));
+
+   prj->prjfwd = coefwd;
+   prj->prjrev = coerev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int coefwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double a, r;
+
+   if (prj->flag != COE) {
+      if (coeset(prj)) return 1;
+   }
+
+   a = phi*prj->w[0];
+   if (theta == -90.0) {
+      r = prj->w[8];
+   } else {
+      r = prj->w[3]*sqrt(prj->w[4] - prj->w[5]*sindeg (theta));
+   }
+
+   *x =             r*sindeg (a);
+   *y = prj->w[2] - r*cosdeg (a);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int coerev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double a, dy, r, w;
+   const double tol = 1.0e-12;
+
+   if (prj->flag != COE) {
+      if (coeset(prj)) return 1;
+   }
+
+   dy = prj->w[2] - y;
+   r  = sqrt(x*x + dy*dy);
+   if (prj->p[1] < 0.0) r = -r;
+
+   if (r == 0.0) {
+      a = 0.0;
+   } else {
+      a = atan2deg (x/r, dy/r);
+   }
+
+   *phi = a*prj->w[1];
+   if (fabs(r - prj->w[8]) < tol) {
+      *theta = -90.0;
+   } else {
+      w = (prj->w[6] - r*r)*prj->w[7];
+      if (fabs(w) > 1.0) {
+         if (fabs(w-1.0) < tol) {
+            *theta = 90.0;
+         } else if (fabs(w+1.0) < tol) {
+            *theta = -90.0;
+         } else {
+            return 2;
+         }
+      } else {
+         *theta = asindeg (w);
+      }
+   }
+
+   return 0;
+}
+
+/*============================================================================
+*   COD: conic equidistant projection.
+*
+*   Given:
+*      prj->p[1]    sigma = (theta2+theta1)/2
+*      prj->p[2]    delta = (theta2-theta1)/2, where theta1 and theta2 are the
+*                   latitudes of the standard parallels, in degrees.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "COD"
+*      prj->flag    COD
+*      prj->phi0    0.0
+*      prj->theta0  sigma
+*      prj->w[0]    C = r0*sin(sigma)*sin(delta)/delta
+*      prj->w[1]    1/C
+*      prj->w[2]    Y0 = delta*cot(delta)*cot(sigma)
+*      prj->w[3]    Y0 + sigma
+*      prj->prjfwd  Pointer to codfwd().
+*      prj->prjrev  Pointer to codrev().
+*===========================================================================*/
+
+int codset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "COD");
+   prj->flag   = COD;
+   prj->phi0   = 0.0;
+   prj->theta0 = prj->p[1];
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   if (prj->p[2] == 0.0) {
+      prj->w[0] = prj->r0*sindeg (prj->p[1])*D2R;
+   } else {
+      prj->w[0] = prj->r0*sindeg (prj->p[1])*sindeg (prj->p[2])/prj->p[2];
+   }
+
+   if (prj->w[0] == 0.0) {
+      return 1;
+   }
+
+   prj->w[1] = 1.0/prj->w[0];
+   prj->w[2] = prj->r0*cosdeg (prj->p[2])*cosdeg (prj->p[1])/prj->w[0];
+   prj->w[3] = prj->w[2] + prj->p[1];
+
+   prj->prjfwd = codfwd;
+   prj->prjrev = codrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int codfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double a, r;
+
+   if (prj->flag != COD) {
+      if (codset(prj)) return 1;
+   }
+
+   a = prj->w[0]*phi;
+   r = prj->w[3] - theta;
+
+   *x =             r*sindeg (a);
+   *y = prj->w[2] - r*cosdeg (a);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int codrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double a, dy, r;
+
+   if (prj->flag != COD) {
+      if (codset(prj)) return 1;
+   }
+
+   dy = prj->w[2] - y;
+   r  = sqrt(x*x + dy*dy);
+   if (prj->p[1] < 0.0) r = -r;
+
+   if (r == 0.0) {
+      a = 0.0;
+   } else {
+      a = atan2deg (x/r, dy/r);
+   }
+
+   *phi   = a*prj->w[1];
+   *theta = prj->w[3] - r;
+
+   return 0;
+}
+
+/*============================================================================
+*   COO: conic orthomorphic projection.
+*
+*   Given:
+*      prj->p[1]    sigma = (theta2+theta1)/2
+*      prj->p[2]    delta = (theta2-theta1)/2, where theta1 and theta2 are the
+*                   latitudes of the standard parallels, in degrees.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "COO"
+*      prj->flag    COO
+*      prj->phi0    0.0
+*      prj->theta0  sigma
+*      prj->w[0]    C = ln(cos(theta2)/cos(theta1))/ln(tan(tau2)/tan(tau1))
+*                       where tau1 = (90 - theta1)/2
+*                             tau2 = (90 - theta2)/2
+*      prj->w[1]    1/C
+*      prj->w[2]    Y0 = psi*tan((90-sigma)/2)**C
+*      prj->w[3]    psi = (r0*cos(theta1)/C)/tan(tau1)**C
+*      prj->w[4]    1/psi
+*      prj->prjfwd  Pointer to coofwd().
+*      prj->prjrev  Pointer to coorev().
+*===========================================================================*/
+
+int cooset(prj)
+
+struct prjprm *prj;
+
+{
+   double cos1, cos2, tan1, tan2, theta1, theta2;
+
+   strcpy(prj->code, "COO");
+   prj->flag   = COO;
+   prj->phi0   = 0.0;
+   prj->theta0 = prj->p[1];
+
+   if (prj->r0 == 0.0) prj->r0 = R2D;
+
+   theta1 = prj->p[1] - prj->p[2];
+   theta2 = prj->p[1] + prj->p[2];
+
+   tan1 = tandeg ((90.0 - theta1)/2.0);
+   cos1 = cosdeg (theta1);
+
+   if (theta1 == theta2) {
+      prj->w[0] = sindeg (theta1);
+   } else {
+      tan2 = tandeg ((90.0 - theta2)/2.0);
+      cos2 = cosdeg (theta2);
+      prj->w[0] = log(cos2/cos1)/log(tan2/tan1);
+   }
+   if (prj->w[0] == 0.0) {
+      return 1;
+   }
+
+   prj->w[1] = 1.0/prj->w[0];
+
+   prj->w[3] = prj->r0*(cos1/prj->w[0])/pow(tan1,prj->w[0]);
+   if (prj->w[3] == 0.0) {
+      return 1;
+   }
+   prj->w[2] = prj->w[3]*pow(tandeg ((90.0 - prj->p[1])/2.0),prj->w[0]);
+   prj->w[4] = 1.0/prj->w[3];
+
+   prj->prjfwd = coofwd;
+   prj->prjrev = coorev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int coofwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double a, r;
+
+   if (prj->flag != COO) {
+      if (cooset(prj)) return 1;
+   }
+
+   a = prj->w[0]*phi;
+   if (theta == -90.0) {
+      if (prj->w[0] < 0.0) {
+         r = 0.0;
+      } else {
+         return 2;
+      }
+   } else {
+      r = prj->w[3]*pow(tandeg ((90.0 - theta)/2.0),prj->w[0]);
+   }
+
+   *x =             r*sindeg (a);
+   *y = prj->w[2] - r*cosdeg (a);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int coorev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double a, dy, r;
+
+   if (prj->flag != COO) {
+      if (cooset(prj)) return 1;
+   }
+
+   dy = prj->w[2] - y;
+   r  = sqrt(x*x + dy*dy);
+   if (prj->p[1] < 0.0) r = -r;
+
+   if (r == 0.0) {
+      a = 0.0;
+   } else {
+      a = atan2deg (x/r, dy/r);
+   }
+
+   *phi = a*prj->w[1];
+   if (r == 0.0) {
+      if (prj->w[0] < 0.0) {
+         *theta = -90.0;
+      } else {
+         return 2;
+      }
+   } else {
+      *theta = 90.0 - 2.0*atandeg (pow(r*prj->w[4],prj->w[1]));
+   }
+
+   return 0;
+}
+
+/*============================================================================
+*   BON: Bonne's projection.
+*
+*   Given:
+*      prj->p[1]    Bonne conformal latitude, theta1, in degrees.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "BON"
+*      prj->flag    BON
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[1]    r0*pi/180
+*      prj->w[2]    Y0 = r0*(cot(theta1) + theta1*pi/180)
+*      prj->prjfwd  Pointer to bonfwd().
+*      prj->prjrev  Pointer to bonrev().
+*===========================================================================*/
+
+int bonset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "BON");
+   prj->flag   = BON;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[1] = 1.0;
+      prj->w[2] = prj->r0*cosdeg (prj->p[1])/sindeg (prj->p[1]) + prj->p[1];
+   } else {
+      prj->w[1] = prj->r0*D2R;
+      prj->w[2] = prj->r0*(cosdeg (prj->p[1])/sindeg (prj->p[1]) + prj->p[1]*D2R);
+   }
+
+   prj->prjfwd = bonfwd;
+   prj->prjrev = bonrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int bonfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double a, r;
+
+   if (prj->p[1] == 0.0) {
+      /* Sanson-Flamsteed. */
+      return sflfwd(phi, theta, prj, x, y);
+   }
+
+   if (prj->flag != BON) {
+      if (bonset(prj)) return 1;
+   }
+
+   r = prj->w[2] - theta*prj->w[1];
+   a = prj->r0*phi*cosdeg (theta)/r;
+
+   *x =             r*sindeg (a);
+   *y = prj->w[2] - r*cosdeg (a);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int bonrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double a, cthe, dy, r;
+
+   if (prj->p[1] == 0.0) {
+      /* Sanson-Flamsteed. */
+      return sflrev(x, y, prj, phi, theta);
+   }
+
+   if (prj->flag != BON) {
+      if (bonset(prj)) return 1;
+   }
+
+   dy = prj->w[2] - y;
+   r = sqrt(x*x + dy*dy);
+   if (prj->p[1] < 0.0) r = -r;
+
+   if (r == 0.0) {
+      a = 0.0;
+   } else {
+      a = atan2deg (x/r, dy/r);
+   }
+
+   *theta = (prj->w[2] - r)/prj->w[1];
+   cthe = cosdeg (*theta);
+   if (cthe == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = a*(r/prj->r0)/cthe;
+   }
+
+   return 0;
+}
+
+/*============================================================================
+*   PCO: polyconic projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "PCO"
+*      prj->flag    PCO
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*(pi/180)
+*      prj->w[1]    1/r0
+*      prj->w[2]    2*r0
+*      prj->prjfwd  Pointer to pcofwd().
+*      prj->prjrev  Pointer to pcorev().
+*===========================================================================*/
+
+int pcoset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "PCO");
+   prj->flag   = PCO;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 1.0;
+      prj->w[1] = 1.0;
+      prj->w[2] = 360.0/PI;
+   } else {
+      prj->w[0] = prj->r0*D2R;
+      prj->w[1] = 1.0/prj->w[0];
+      prj->w[2] = 2.0*prj->r0;
+   }
+
+   prj->prjfwd = pcofwd;
+   prj->prjrev = pcorev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int pcofwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   double a, cthe, cotthe, sthe;
+
+   if (prj->flag != PCO) {
+      if (pcoset(prj)) return 1;
+   }
+
+   cthe = cosdeg (theta);
+   sthe = sindeg (theta);
+   a = phi*sthe;
+
+   if (sthe == 0.0) {
+      *x = prj->w[0]*phi;
+      *y = 0.0;
+   } else {
+      cotthe = cthe/sthe;
+      *x = prj->r0*cotthe*sindeg (a);
+      *y = prj->r0*(cotthe*(1.0 - cosdeg (a)) + theta*D2R);
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int pcorev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   int   j;
+   double f, fneg, fpos, lambda, tanthe, theneg, thepos, w, xp, xx, ymthe, yp;
+   const double tol = 1.0e-12;
+
+   if (prj->flag != PCO) {
+      if (pcoset(prj)) return 1;
+   }
+
+   w = fabs(y*prj->w[1]);
+   if (w < tol) {
+      *phi = x*prj->w[1];
+      *theta = 0.0;
+   } else if (fabs(w-90.0) < tol) {
+      *phi = 0.0;
+      *theta = copysgni (90.0,y);
+   } else {
+      /* Iterative solution using weighted division of the interval. */
+      if (y > 0.0) {
+         thepos =  90.0;
+      } else {
+         thepos = -90.0;
+      }
+      theneg = 0.0;
+
+      xx = x*x;
+      ymthe = y - prj->w[0]*thepos;
+      fpos = xx + ymthe*ymthe;
+      fneg = -999.0;
+
+      for (j = 0; j < 64; j++) {
+         if (fneg < -100.0) {
+            /* Equal division of the interval. */
+            *theta = (thepos+theneg)/2.0;
+         } else {
+            /* Weighted division of the interval. */
+            lambda = fpos/(fpos-fneg);
+            if (lambda < 0.1) {
+               lambda = 0.1;
+            } else if (lambda > 0.9) {
+               lambda = 0.9;
+            }
+            *theta = thepos - lambda*(thepos-theneg);
+         }
+
+         /* Compute the residue. */
+         ymthe = y - prj->w[0]*(*theta);
+         tanthe = tandeg (*theta);
+         f = xx + ymthe*(ymthe - prj->w[2]/tanthe);
+
+         /* Check for convergence. */
+         if (fabs(f) < tol) break;
+         if (fabs(thepos-theneg) < tol) break;
+
+         /* Redefine the interval. */
+         if (f > 0.0) {
+            thepos = *theta;
+            fpos = f;
+         } else {
+            theneg = *theta;
+            fneg = f;
+         }
+      }
+
+      xp = prj->r0 - ymthe*tanthe;
+      yp = x*tanthe;
+      if (xp == 0.0 && yp == 0.0) {
+         *phi = 0.0;
+      } else {
+         *phi = atan2deg (yp, xp)/sindeg (*theta);
+      }
+   }
+
+   return 0;
+}
+
+/*============================================================================
+*   TSC: tangential spherical cube projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "TSC"
+*      prj->flag    TSC
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*(pi/4)
+*      prj->w[1]    (4/pi)/r0
+*      prj->prjfwd  Pointer to tscfwd().
+*      prj->prjrev  Pointer to tscrev().
+*===========================================================================*/
+
+int tscset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "TSC");
+   prj->flag   = TSC;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 45.0;
+      prj->w[1] = 1.0/45.0;
+   } else {
+      prj->w[0] = prj->r0*PI/4.0;
+      prj->w[1] = 1.0/prj->w[0];
+   }
+
+   prj->prjfwd = tscfwd;
+   prj->prjrev = tscrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int tscfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   int   face;
+   double cthe, l, m, n, rho;
+   double x0 = 0.0;
+   double y0 = 0.0;
+   double xf = 0.0;
+   double yf = 0.0;
+   const double tol = 1.0e-12;
+
+   if (prj->flag != TSC) {
+      if (tscset(prj)) return 1;
+   }
+
+   cthe = cosdeg (theta);
+   l = cthe*cosdeg (phi);
+   m = cthe*sindeg (phi);
+   n = sindeg (theta);
+
+   face = 0;
+   rho  = n;
+   if (l > rho) {
+      face = 1;
+      rho  = l;
+   }
+   if (m > rho) {
+      face = 2;
+      rho  = m;
+   }
+   if (-l > rho) {
+      face = 3;
+      rho  = -l;
+   }
+   if (-m > rho) {
+      face = 4;
+      rho  = -m;
+   }
+   if (-n > rho) {
+      face = 5;
+      rho  = -n;
+   }
+
+   if (face == 0) {
+      xf =  m/rho;
+      yf = -l/rho;
+      x0 =  0.0;
+      y0 =  2.0;
+   } else if (face == 1) {
+      xf =  m/rho;
+      yf =  n/rho;
+      x0 =  0.0;
+      y0 =  0.0;
+   } else if (face == 2) {
+      xf = -l/rho;
+      yf =  n/rho;
+      x0 =  2.0;
+      y0 =  0.0;
+   } else if (face == 3) {
+      xf = -m/rho;
+      yf =  n/rho;
+      x0 =  4.0;
+      y0 =  0.0;
+   } else if (face == 4) {
+      xf =  l/rho;
+      yf =  n/rho;
+      x0 =  6.0;
+      y0 =  0.0;
+   } else if (face == 5) {
+      xf =  m/rho;
+      yf =  l/rho;
+      x0 =  0.0;
+      y0 = -2.0;
+   }
+
+   if (fabs(xf) > 1.0) {
+      if (fabs(xf) > 1.0+tol) {
+         return 2;
+      }
+      xf = copysgn (1.0,xf);
+   }
+   if (fabs(yf) > 1.0) {
+      if (fabs(yf) > 1.0+tol) {
+         return 2;
+      }
+      yf = copysgn (1.0,yf);
+   }
+
+   *x = prj->w[0]*(xf + x0);
+   *y = prj->w[0]*(yf + y0);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int tscrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   double l, m, n, xf, yf;
+
+   if (prj->flag != TSC) {
+      if (tscset(prj)) return 1;
+   }
+
+   xf = x*prj->w[1];
+   yf = y*prj->w[1];
+
+   /* Check bounds. */
+   if (fabs(xf) <= 1.0) {
+      if (fabs(yf) > 3.0) return 2;
+   } else {
+      if (fabs(xf) > 7.0) return 2;
+      if (fabs(yf) > 1.0) return 2;
+   }
+
+   /* Map negative faces to the other side. */
+   if (xf < -1.0) xf += 8.0;
+
+   /* Determine the face. */
+   if (xf > 5.0) {
+      /* face = 4 */
+      xf = xf - 6.0;
+      m  = -1.0/sqrt(1.0 + xf*xf + yf*yf);
+      l  = -m*xf;
+      n  = -m*yf;
+   } else if (xf > 3.0) {
+      /* face = 3 */
+      xf = xf - 4.0;
+      l  = -1.0/sqrt(1.0 + xf*xf + yf*yf);
+      m  =  l*xf;
+      n  = -l*yf;
+   } else if (xf > 1.0) {
+      /* face = 2 */
+      xf = xf - 2.0;
+      m  =  1.0/sqrt(1.0 + xf*xf + yf*yf);
+      l  = -m*xf;
+      n  =  m*yf;
+   } else if (yf > 1.0) {
+      /* face = 0 */
+      yf = yf - 2.0;
+      n  = 1.0/sqrt(1.0 + xf*xf + yf*yf);
+      l  = -n*yf;
+      m  =  n*xf;
+   } else if (yf < -1.0) {
+      /* face = 5 */
+      yf = yf + 2.0;
+      n  = -1.0/sqrt(1.0 + xf*xf + yf*yf);
+      l  = -n*yf;
+      m  = -n*xf;
+   } else {
+      /* face = 1 */
+      l  =  1.0/sqrt(1.0 + xf*xf + yf*yf);
+      m  =  l*xf;
+      n  =  l*yf;
+   }
+
+   if (l == 0.0 && m == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = atan2deg (m, l);
+   }
+   *theta = asindeg (n);
+
+   return 0;
+}
+
+/*============================================================================
+*   CSC: COBE quadrilateralized spherical cube projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "CSC"
+*      prj->flag    CSC
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*(pi/4)
+*      prj->w[1]    (4/pi)/r0
+*      prj->prjfwd  Pointer to cscfwd().
+*      prj->prjrev  Pointer to cscrev().
+*===========================================================================*/
+
+int cscset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "CSC");
+   prj->flag   = CSC;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 45.0;
+      prj->w[1] = 1.0/45.0;
+   } else {
+      prj->w[0] = prj->r0*PI/4.0;
+      prj->w[1] = 1.0/prj->w[0];
+   }
+
+   prj->prjfwd = cscfwd;
+   prj->prjrev = cscrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int cscfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   int   face;
+   double cthe, eta, l, m, n, rho, xi;
+   const float tol = 1.0e-7;
+
+   float a, a2, a2b2, a4, ab, b, b2, b4, ca2, cb2;
+   float x0 = 0.0;
+   float y0 = 0.0;
+   float xf = 0.0;
+   float yf = 0.0;
+   const float gstar  =  1.37484847732;
+   const float mm     =  0.004869491981;
+   const float gamma  = -0.13161671474;
+   const float omega1 = -0.159596235474;
+   const float d0  =  0.0759196200467;
+   const float d1  = -0.0217762490699;
+   const float c00 =  0.141189631152;
+   const float c10 =  0.0809701286525;
+   const float c01 = -0.281528535557;
+   const float c11 =  0.15384112876;
+   const float c20 = -0.178251207466;
+   const float c02 =  0.106959469314;
+
+   if (prj->flag != CSC) {
+      if (cscset(prj)) return 1;
+   }
+
+   cthe = cosdeg (theta);
+   l = cthe*cosdeg (phi);
+   m = cthe*sindeg (phi);
+   n = sindeg (theta);
+
+   face = 0;
+   rho  = n;
+   if (l > rho) {
+      face = 1;
+      rho  = l;
+   }
+   if (m > rho) {
+      face = 2;
+      rho  = m;
+   }
+   if (-l > rho) {
+      face = 3;
+      rho  = -l;
+   }
+   if (-m > rho) {
+      face = 4;
+      rho  = -m;
+   }
+   if (-n > rho) {
+      face = 5;
+      rho  = -n;
+   }
+
+   if (face == 0) {
+      xi  =  m;
+      eta = -l;
+      x0  =  0.0;
+      y0  =  2.0;
+   } else if (face == 1) {
+      xi  =  m;
+      eta =  n;
+      x0  =  0.0;
+      y0  =  0.0;
+   } else if (face == 2) {
+      xi  = -l;
+      eta =  n;
+      x0  =  2.0;
+      y0  =  0.0;
+   } else if (face == 3) {
+      xi  = -m;
+      eta =  n;
+      x0  =  4.0;
+      y0  =  0.0;
+   } else if (face == 4) {
+      xi  =  l;
+      eta =  n;
+      x0  =  6.0;
+      y0  =  0.0;
+   } else if (face == 5) {
+      xi  =  m;
+      eta =  l;
+      x0  =  0.0;
+      y0  = -2.0;
+   }
+
+   a =  xi/rho;
+   b = eta/rho;
+
+   a2 = a*a;
+   b2 = b*b;
+   ca2 = 1.0 - a2;
+   cb2 = 1.0 - b2;
+
+   /* Avoid floating underflows. */
+   ab   = fabs(a*b);
+   a4   = (a2 > 1.0e-16) ? a2*a2 : 0.0;
+   b4   = (b2 > 1.0e-16) ? b2*b2 : 0.0;
+   a2b2 = (ab > 1.0e-16) ? a2*b2 : 0.0;
+
+   xf = a*(a2 + ca2*(gstar + b2*(gamma*ca2 + mm*a2 +
+          cb2*(c00 + c10*a2 + c01*b2 + c11*a2b2 + c20*a4 + c02*b4)) +
+          a2*(omega1 - ca2*(d0 + d1*a2))));
+   yf = b*(b2 + cb2*(gstar + a2*(gamma*cb2 + mm*b2 +
+          ca2*(c00 + c10*b2 + c01*a2 + c11*a2b2 + c20*b4 + c02*a4)) +
+          b2*(omega1 - cb2*(d0 + d1*b2))));
+
+   if (fabs(xf) > 1.0) {
+      if (fabs(xf) > 1.0+tol) {
+         return 2;
+      }
+      xf = copysgn (1.0,xf);
+   }
+   if (fabs(yf) > 1.0) {
+      if (fabs(yf) > 1.0+tol) {
+         return 2;
+      }
+      yf = copysgn (1.0,yf);
+   }
+
+   *x = prj->w[0]*(x0 + xf);
+   *y = prj->w[0]*(y0 + yf);
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int cscrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   int   face;
+   double l = 0.0;
+   double m = 0.0;
+   double n = 0.0;
+
+   float     a, b, xf, xx, yf, yy, z0, z1, z2, z3, z4, z5, z6;
+   const float p00 = -0.27292696;
+   const float p10 = -0.07629969;
+   const float p20 = -0.22797056;
+   const float p30 =  0.54852384;
+   const float p40 = -0.62930065;
+   const float p50 =  0.25795794;
+   const float p60 =  0.02584375;
+   const float p01 = -0.02819452;
+   const float p11 = -0.01471565;
+   const float p21 =  0.48051509;
+   const float p31 = -1.74114454;
+   const float p41 =  1.71547508;
+   const float p51 = -0.53022337;
+   const float p02 =  0.27058160;
+   const float p12 = -0.56800938;
+   const float p22 =  0.30803317;
+   const float p32 =  0.98938102;
+   const float p42 = -0.83180469;
+   const float p03 = -0.60441560;
+   const float p13 =  1.50880086;
+   const float p23 = -0.93678576;
+   const float p33 =  0.08693841;
+   const float p04 =  0.93412077;
+   const float p14 = -1.41601920;
+   const float p24 =  0.33887446;
+   const float p05 = -0.63915306;
+   const float p15 =  0.52032238;
+   const float p06 =  0.14381585;
+
+   if (prj->flag != CSC) {
+      if (cscset(prj)) return 1;
+   }
+
+   xf = x*prj->w[1];
+   yf = y*prj->w[1];
+
+   /* Check bounds. */
+   if (fabs(xf) <= 1.0) {
+      if (fabs(yf) > 3.0) return 2;
+   } else {
+      if (fabs(xf) > 7.0) return 2;
+      if (fabs(yf) > 1.0) return 2;
+   }
+
+   /* Map negative faces to the other side. */
+   if (xf < -1.0) xf += 8.0;
+
+   /* Determine the face. */
+   if (xf > 5.0) {
+      face = 4;
+      xf = xf - 6.0;
+   } else if (xf > 3.0) {
+      face = 3;
+      xf = xf - 4.0;
+   } else if (xf > 1.0) {
+      face = 2;
+      xf = xf - 2.0;
+   } else if (yf > 1.0) {
+      face = 0;
+      yf = yf - 2.0;
+   } else if (yf < -1.0) {
+      face = 5;
+      yf = yf + 2.0;
+   } else {
+      face = 1;
+   }
+
+   xx  =  xf*xf;
+   yy  =  yf*yf;
+
+   z0 = p00 + xx*(p10 + xx*(p20 + xx*(p30 + xx*(p40 + xx*(p50 + xx*(p60))))));
+   z1 = p01 + xx*(p11 + xx*(p21 + xx*(p31 + xx*(p41 + xx*(p51)))));
+   z2 = p02 + xx*(p12 + xx*(p22 + xx*(p32 + xx*(p42))));
+   z3 = p03 + xx*(p13 + xx*(p23 + xx*(p33)));
+   z4 = p04 + xx*(p14 + xx*(p24));
+   z5 = p05 + xx*(p15);
+   z6 = p06;
+
+   a = z0 + yy*(z1 + yy*(z2 + yy*(z3 + yy*(z4 + yy*(z5 + yy*z6)))));
+   a = xf + xf*(1.0 - xx)*a;
+
+   z0 = p00 + yy*(p10 + yy*(p20 + yy*(p30 + yy*(p40 + yy*(p50 + yy*(p60))))));
+   z1 = p01 + yy*(p11 + yy*(p21 + yy*(p31 + yy*(p41 + yy*(p51)))));
+   z2 = p02 + yy*(p12 + yy*(p22 + yy*(p32 + yy*(p42))));
+   z3 = p03 + yy*(p13 + yy*(p23 + yy*(p33)));
+   z4 = p04 + yy*(p14 + yy*(p24));
+   z5 = p05 + yy*(p15);
+   z6 = p06;
+
+   b = z0 + xx*(z1 + xx*(z2 + xx*(z3 + xx*(z4 + xx*(z5 + xx*z6)))));
+   b = yf + yf*(1.0 - yy)*b;
+
+   if (face == 0) {
+      n =  1.0/sqrt(a*a + b*b + 1.0);
+      l = -b*n;
+      m =  a*n;
+   } else if (face == 1) {
+      l =  1.0/sqrt(a*a + b*b + 1.0);
+      m =  a*l;
+      n =  b*l;
+   } else if (face == 2) {
+      m =  1.0/sqrt(a*a + b*b + 1.0);
+      l = -a*m;
+      n =  b*m;
+   } else if (face == 3) {
+      l = -1.0/sqrt(a*a + b*b + 1.0);
+      m =  a*l;
+      n = -b*l;
+   } else if (face == 4) {
+      m = -1.0/sqrt(a*a + b*b + 1.0);
+      l = -a*m;
+      n = -b*m;
+   } else if (face == 5) {
+      n = -1.0/sqrt(a*a + b*b + 1.0);
+      l = -b*n;
+      m = -a*n;
+   }
+
+   if (l == 0.0 && m == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = atan2deg (m, l);
+   }
+   *theta = asindeg (n);
+
+   return 0;
+}
+
+/*============================================================================
+*   QSC: quadrilaterilized spherical cube projection.
+*
+*   Given and/or returned:
+*      prj->r0      r0; reset to 180/pi if 0.
+*
+*   Returned:
+*      prj->code    "QSC"
+*      prj->flag    QSC
+*      prj->phi0    0.0
+*      prj->theta0  0.0
+*      prj->w[0]    r0*(pi/4)
+*      prj->w[1]    (4/pi)/r0
+*      prj->prjfwd  Pointer to qscfwd().
+*      prj->prjrev  Pointer to qscrev().
+*===========================================================================*/
+
+int qscset(prj)
+
+struct prjprm *prj;
+
+{
+   strcpy(prj->code, "QSC");
+   prj->flag   = QSC;
+   prj->phi0   = 0.0;
+   prj->theta0 = 0.0;
+
+   if (prj->r0 == 0.0) {
+      prj->r0 = R2D;
+      prj->w[0] = 45.0;
+      prj->w[1] = 1.0/45.0;
+   } else {
+      prj->w[0] = prj->r0*PI/4.0;
+      prj->w[1] = 1.0/prj->w[0];
+   }
+
+   prj->prjfwd = qscfwd;
+   prj->prjrev = qscrev;
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int qscfwd(phi, theta, prj, x, y)
+
+const double phi, theta;
+struct prjprm *prj;
+double *x, *y;
+
+{
+   int   face;
+   double cthe, l, m, n, omega, p, rho, rhu, t, tau;
+   double xi = 0.0;
+   double eta = 0.0;
+   double x0 = 0.0;
+   double y0 = 0.0;
+   double xf = 0.0;
+   double yf = 0.0;
+   const double tol = 1.0e-12;
+
+   if (prj->flag != QSC) {
+      if (qscset(prj)) return 1;
+   }
+
+   if (fabs(theta) == 90.0) {
+      *x = 0.0;
+      *y = copysgn (2.0*prj->w[0],theta);
+      return 0;
+   }
+
+   cthe = cosdeg (theta);
+   l = cthe*cosdeg (phi);
+   m = cthe*sindeg (phi);
+   n = sindeg (theta);
+
+   face = 0;
+   rho  = n;
+   if (l > rho) {
+      face = 1;
+      rho  = l;
+   }
+   if (m > rho) {
+      face = 2;
+      rho  = m;
+   }
+   if (-l > rho) {
+      face = 3;
+      rho  = -l;
+   }
+   if (-m > rho) {
+      face = 4;
+      rho  = -m;
+   }
+   if (-n > rho) {
+      face = 5;
+      rho  = -n;
+   }
+
+   rhu = 1.0 - rho;
+
+   if (face == 0) {
+      xi  =  m;
+      eta = -l;
+      if (rhu < 1.0e-8) {
+         /* Small angle formula. */
+         t = (90.0 - theta)*D2R;
+         rhu = t*t/2.0;
+      }
+      x0  =  0.0;
+      y0  =  2.0;
+   } else if (face == 1) {
+      xi  =  m;
+      eta =  n;
+      if (rhu < 1.0e-8) {
+         /* Small angle formula. */
+         t = theta*D2R;
+         p = fmod(phi,360.0);
+         if (p < -180.0) p += 360.0;
+         if (p >  180.0) p -= 360.0;
+         p *= D2R;
+         rhu = (p*p + t*t)/2.0;
+      }
+      x0  =  0.0;
+      y0  =  0.0;
+   } else if (face == 2) {
+      xi  = -l;
+      eta =  n;
+      if (rhu < 1.0e-8) {
+         /* Small angle formula. */
+         t = theta*D2R;
+         p = fmod(phi,360.0);
+         if (p < -180.0) p += 360.0;
+         p = (90.0 - p)*D2R;
+         rhu = (p*p + t*t)/2.0;
+      }
+      x0  =  2.0;
+      y0  =  0.0;
+   } else if (face == 3) {
+      xi  = -m;
+      eta =  n;
+      if (rhu < 1.0e-8) {
+         /* Small angle formula. */
+         t = theta*D2R;
+         p = fmod(phi,360.0);
+         if (p < 0.0) p += 360.0;
+         p = (180.0 - p)*D2R;
+         rhu = (p*p + t*t)/2.0;
+      }
+      x0  =  4.0;
+      y0  =  0.0;
+   } else if (face == 4) {
+      xi  =  l;
+      eta =  n;
+      if (rhu < 1.0e-8) {
+         /* Small angle formula. */
+         t = theta*D2R;
+         p = fmod(phi,360.0);
+         if (p > 180.0) p -= 360.0;
+         p *= (90.0 + p)*D2R;
+         rhu = (p*p + t*t)/2.0;
+      }
+      x0  =  6;
+      y0  =  0.0;
+   } else if (face == 5) {
+      xi  =  m;
+      eta =  l;
+      if (rhu < 1.0e-8) {
+         /* Small angle formula. */
+         t = (90.0 + theta)*D2R;
+         rhu = t*t/2.0;
+      }
+      x0  =  0.0;
+      y0  = -2;
+   }
+
+   if (xi == 0.0 && eta == 0.0) {
+      xf  = 0.0;
+      yf  = 0.0;
+   } else if (-xi >= fabs(eta)) {
+      omega = eta/xi;
+      tau = 1.0 + omega*omega;
+      xf  = -sqrt(rhu/(1.0-1.0/sqrt(1.0+tau)));
+      yf  = (xf/15.0)*(atandeg (omega) - asindeg (omega/sqrt(tau+tau)));
+   } else if (xi >= fabs(eta)) {
+      omega = eta/xi;
+      tau = 1.0 + omega*omega;
+      xf  =  sqrt(rhu/(1.0-1.0/sqrt(1.0+tau)));
+      yf  = (xf/15.0)*(atandeg (omega) - asindeg (omega/sqrt(tau+tau)));
+   } else if (-eta > fabs(xi)) {
+      omega = xi/eta;
+      tau = 1.0 + omega*omega;
+      yf  = -sqrt(rhu/(1.0-1.0/sqrt(1.0+tau)));
+      xf  = (yf/15.0)*(atandeg (omega) - asindeg (omega/sqrt(tau+tau)));
+   } else if (eta > fabs(xi)) {
+      omega = xi/eta;
+      tau = 1.0 + omega*omega;
+      yf  =  sqrt(rhu/(1.0-1.0/sqrt(1.0+tau)));
+      xf  = (yf/15.0)*(atandeg (omega) - asindeg (omega/sqrt(tau+tau)));
+   }
+
+   if (fabs(xf) > 1.0) {
+      if (fabs(xf) > 1.0+tol) {
+         return 2;
+      }
+      xf = copysgn (1.0,xf);
+   }
+   if (fabs(yf) > 1.0) {
+      if (fabs(yf) > 1.0+tol) {
+         return 2;
+      }
+      yf = copysgn (1.0,yf);
+   }
+
+   *x = prj->w[0]*(xf + x0);
+   *y = prj->w[0]*(yf + y0);
+
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int qscrev(x, y, prj, phi, theta)
+
+const double x, y;
+struct prjprm *prj;
+double *phi, *theta;
+
+{
+   int   direct, face;
+   double omega, rho, rhu, tau, xf, yf, w;
+   double l = 0.0;
+   double m = 0.0;
+   double n = 0.0;
+   const double tol = 1.0e-12;
+
+   if (prj->flag != QSC) {
+      if (qscset(prj)) return 1;
+   }
+
+   xf = x*prj->w[1];
+   yf = y*prj->w[1];
+
+   /* Check bounds. */
+   if (fabs(xf) <= 1.0) {
+      if (fabs(yf) > 3.0) return 2;
+   } else {
+      if (fabs(xf) > 7.0) return 2;
+      if (fabs(yf) > 1.0) return 2;
+   }
+
+   /* Map negative faces to the other side. */
+   if (xf < -1.0) xf += 8.0;
+
+   /* Determine the face. */
+   if (xf > 5.0) {
+      face = 4;
+      xf = xf - 6.0;
+   } else if (xf > 3.0) {
+      face = 3;
+      xf = xf - 4.0;
+   } else if (xf > 1.0) {
+      face = 2;
+      xf = xf - 2.0;
+   } else if (yf > 1.0) {
+      face = 0;
+      yf = yf - 2.0;
+   } else if (yf < -1.0) {
+      face = 5;
+      yf = yf + 2.0;
+   } else {
+      face = 1;
+   }
+
+   direct = (fabs(xf) > fabs(yf));
+   if (direct) {
+      if (xf == 0.0) {
+         omega = 0.0;
+         tau = 1.0;
+         rho = 1.0;
+         rhu = 0.0;
+      } else {
+         w = 15.0*yf/xf;
+         omega = sindeg (w)/(cosdeg (w) - SQRT2INV);
+         tau = 1.0 + omega*omega;
+         rhu = xf*xf*(1.0 - 1.0/sqrt(1.0 + tau));
+         rho = 1.0 - rhu;
+      }
+   } else {
+      if (yf == 0.0) {
+         omega = 0.0;
+         tau = 1.0;
+         rho = 1.0;
+         rhu = 0.0;
+      } else {
+         w = 15.0*xf/yf;
+         omega = sindeg (w)/(cosdeg (w) - SQRT2INV);
+         tau = 1.0 + omega*omega;
+         rhu = yf*yf*(1.0 - 1.0/sqrt(1.0 + tau));
+         rho = 1.0 - rhu;
+      }
+   }
+
+   if (rho < -1.0) {
+      if (rho < -1.0-tol) {
+         return 2;
+      }
+
+      rho = -1.0;
+      rhu =  2.0;
+      w   =  0.0;
+   } else {
+      w = sqrt(rhu*(2.0-rhu)/tau);
+   }
+
+   if (face == 0) {
+      n = rho;
+      if (direct) {
+         m = w;
+         if (xf < 0.0) m = -m;
+         l = -m*omega;
+      } else {
+         l = w;
+         if (yf > 0.0) l = -l;
+         m = -l*omega;
+      }
+   } else if (face == 1) {
+      l = rho;
+      if (direct) {
+         m = w;
+         if (xf < 0.0) m = -m;
+         n = m*omega;
+      } else {
+         n = w;
+         if (yf < 0.0) n = -n;
+         m = n*omega;
+      }
+   } else if (face == 2) {
+      m = rho;
+      if (direct) {
+         l = w;
+         if (xf > 0.0) l = -l;
+         n = -l*omega;
+      } else {
+         n = w;
+         if (yf < 0.0) n = -n;
+         l = -n*omega;
+      }
+   } else if (face == 3) {
+      l = -rho;
+      if (direct) {
+         m = w;
+         if (xf > 0.0) m = -m;
+         n = -m*omega;
+      } else {
+         n = w;
+         if (yf < 0.0) n = -n;
+         m = -n*omega;
+      }
+   } else if (face == 4) {
+      m = -rho;
+      if (direct) {
+         l = w;
+         if (xf < 0.0) l = -l;
+         n = l*omega;
+      } else {
+         n = w;
+         if (yf < 0.0) n = -n;
+         l = n*omega;
+      }
+   } else if (face == 5) {
+      n = -rho;
+      if (direct) {
+         m = w;
+         if (xf < 0.0) m = -m;
+         l = m*omega;
+      } else {
+         l = w;
+         if (yf < 0.0) l = -l;
+         m = l*omega;
+      }
+   }
+
+   if (l == 0.0 && m == 0.0) {
+      *phi = 0.0;
+   } else {
+      *phi = atan2deg (m, l);
+   }
+   *theta = asindeg (n);
+
+   return 0;
+}
+/* Dec 20 1999  Doug Mink - Change cosd() and sind() to cosdeg() and sindeg()
+ * Dec 20 1999  Doug Mink - Include wcslib.h, which includes proj.h, wcsmath.h
+ * Dec 20 1999  Doug Mink - Define copysign only if it is not defined
+ * Dec 20 1999  Doug Mink - tanfwd() returns error if s<=0.0, not only if s==0.0
+ *
+ * Jun  2 2000  Doug Mink - include stdlib.h to get abs()
+ *
+ * Feb 15 2001  Doug Mink - update zearev() for WCSLIB 2.6
+ * Sep 19 2001  Doug Mink - Make above changes for WCSLIB 2.7
+ *
+ * Mar 15 2002	Doug Mink - Make above changes for WCSLIB 2.8.2
+ *
+ * Feb  3 2003	Doug Mink - Use locally defined copysgn() and copysgni(),
+ *		            not copysign()
+ * Apr  1 2003	Doug Mink - include string.h for strcpy() and strcmp()
+ */
diff --git a/Code/src/libwcs/sdssread.c b/Code/src/libwcs/sdssread.c
new file mode 100644
index 0000000000000000000000000000000000000000..edf3622c431932d0e5d04c696be3b717dfac542e
--- /dev/null
+++ b/Code/src/libwcs/sdssread.c
@@ -0,0 +1,262 @@
+/*** File libwcs/sdssread.c
+ *** October 22, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2004-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "fitsfile.h"
+#include "wcs.h"
+#include "wcscat.h"
+
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+#define LINE    1024
+
+/* SDSS DR1 search engine URL
+char sdssrurl[64]="http://skyserver.sdss.org/cas/en/tools/search/x_radial.asp";
+char sdssburl[64]="http://skyserver.sdss.org/cas/en/tools/search/x_rect.asp"; */
+
+/* SDSS DR4 search engine URL
+char sdssrurl[64]="http://cas.sdss.org/dr4/en/tools/search/x_radial.asp";
+char sdssburl[64]="http://cas.sdss.org/dr4/en/tools/search/x_rect.asp"; */
+
+/* SDSS DR5 search engine URL
+char sdssrurl[64]="http://cas.sdss.org/dr5/en/tools/search/x_radial.asp";
+char sdssburl[64]="http://cas.sdss.org/dr5/en/tools/search/x_rect.asp"; */
+
+/* SDSS DR6 search engine URL
+char sdssrurl[64]="http://cas.sdss.org/dr6/en/tools/search/x_radial.asp";
+char sdssburl[64]="http://cas.sdss.org/dr6/en/tools/search/x_rect.asp"; */
+
+/* SDSS DR7 search engine URL */
+char sdssrurl[64]="http://cas.sdss.org/dr7/en/tools/search/x_radial.asp";
+char sdssburl[64]="http://cas.sdss.org/dr7/en/tools/search/x_rect.asp";
+
+/* SDSS magnitudes */
+char sdssmag[6]="ugriz";
+
+/* SDSSREAD -- Read Sloan Digital Sky Survey catalog stars over the web */
+
+int
+sdssread (cra,cdec,dra,ddec,drad,dradi,distsort,sysout,eqout,epout,
+	  mag1,mag2,sortmag,nstarmax,gnum,gobj,gra,gdec,gmag,gtype,nlog)
+
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	distsort;	/* 1 to sort stars by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*gnum;		/* Array of catalog numbers (returned) */
+char	**gobj;		/* Array of object IDs (too long for integer*4) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double	**gmag;		/* 2-D array of magnitudes (returned) */
+int	*gtype;		/* Array of object classes (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    char srchurl[LINE];
+    char temp[64];
+    char cmag;
+    struct TabTable *tabtable;
+    double dtemp;
+    double *gpra, *gpdec;
+    struct StarCat *starcat;
+    int nstar, nlog0;
+    double ra, dec, mag;
+    char rastr[32], decstr[32];
+    char *sdssurl;
+
+    gpra = NULL;
+    gpdec = NULL;
+
+    nlog0 = nlog;
+    if (nstarmax < 1)
+	nlog = -1;
+
+/* make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+    if (mag1 < 0)
+	mag1 = 0.0;
+
+    /* Set up query for STScI GSC II server */
+    ra = cra;
+    dec = cdec;
+    if (sysout != WCS_J2000)
+	wcscon (sysout, WCS_J2000, eqout, 2000.0, &ra, &dec, epout);
+
+    sdssurl = sdssrurl;
+    deg2str (rastr, 32, ra, 5);
+    deg2str (decstr, 32, dec, 5);
+
+    /* Radius or box size */
+    if (drad != 0.0) {
+	dtemp = drad * 60.0;
+	sprintf (srchurl, "?ra=%.5f&dec=%.5f&radius=%.3f",
+		 ra, dec, dtemp);
+	}
+    else {
+	dtemp = sqrt (dra*dra + ddec*ddec) * 60.0;
+	sprintf (srchurl, "?ra=%.5f&dec=%.5f&radius=%.3f",
+		 ra, dec, dtemp);
+	}
+
+    /* Magnitude limit, if any */
+    if (sortmag < 1)
+	cmag = 'g';
+    else
+	cmag = sdssmag[sortmag - 1];
+    if (mag1 < mag2) {
+	sprintf (temp, "&check_%c=%c&min_%c=%.2f&max_%c=%.2f",
+		 cmag, cmag, cmag, mag1, cmag, mag2);
+	strcat (srchurl, temp);
+	}
+    nstar = 50000;
+    sprintf (temp, "&entries=top&topnum=%d&format=csv",nstar);
+    strcat (srchurl, temp);
+    if (nlog0 > 0)
+	fprintf (stderr,"%s%s\n", sdssurl, srchurl);
+
+    /* Run search across the web */
+    if ((tabtable = webopen (sdssurl, srchurl, nlog)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: %s failed\n", srchurl);
+	return (0);
+	}
+
+    /* Return if no data */
+    if (tabtable->tabdata == NULL || strlen (tabtable->tabdata) == 0 ||
+	!strncasecmp (tabtable->tabdata, "[EOD]", 5)) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBRNUM: No data returned\n");
+	return (0);
+	}
+
+    /* Dump returned file and stop */
+    if (nlog < 0) {
+	(void) fwrite  (tabtable->tabbuff, tabtable->lbuff, 1, stdout);
+	exit (0);
+	}
+
+    /* Open returned Starbase table as a catalog */
+    if ((starcat = tabcatopen (sdssurl, tabtable,0)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: Could not open Starbase table as catalog\n");
+	return (0);
+	}
+
+    /* Set reference frame, epoch, and equinox of catalog */
+    starcat->coorsys = WCS_J2000;
+    starcat->epoch = 2000.0;
+    starcat->equinox = 2000.0;
+    starcat->nmag = 5;
+
+    /* Extract desired sources from catalog  and return them */
+    nstar = tabread (sdssurl,distsort,cra,cdec,dra,ddec,drad,dradi,
+	     sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,&starcat,
+	     gnum,gra,gdec,gpra,gpdec,gmag,gtype,gobj,nlog);
+
+    tabcatclose (starcat);
+
+    starcat = NULL;
+
+    return (nstar);
+}
+
+char *
+sdssc2t (csvbuff)
+
+    char *csvbuff;	/* Input comma-separated table */
+
+{
+    char colhead[180]="objID             	run	rerun	camcol	field	obj	type	ra        	dec      	umag	gmag	rmag	imag	zmag	uerr    	gerr   	rerr    	ierr    	zerr   \n";
+    char colsep[180]="------------------	---	-----	------	-----	---	----	----------	---------	------	------	------	------	------	--------	------	--------	--------	-------\n";
+    char *tabbuff;	/* Output tab-separated table */
+    char *databuff;
+    char *lastbuff;
+    int lbuff, i;
+    char ctab = (char) 9;
+    char ccom = ',';
+
+    /* Skip first line of returned header */
+    databuff = strchr (csvbuff, '\n') + 1;
+
+    /* Drop extraneous data after last linefeed */
+    lbuff = strlen (databuff);
+    lastbuff = strrchr (databuff, '\n');
+    if (lastbuff - databuff < lbuff)
+	*(lastbuff+1) = (char) 0;
+
+    /* Convert commas in table to tabs */
+    lbuff = strlen (databuff);
+    for (i = 0; i < lbuff; i++) {
+	if (databuff[i] == ccom)
+	    databuff[i] = ctab;
+	}
+
+    /* Allocate buffer for tab-separated table with header */
+    lbuff = strlen (databuff) + strlen (colhead) + strlen (colsep);
+    tabbuff = (char *) calloc (lbuff, 1);
+
+    /* Copy column headings, separator, and data to output buffer */
+    strcpy (tabbuff, colhead);
+    strcat (tabbuff, colsep);
+    strcat (tabbuff, databuff);
+
+    return (tabbuff);
+}
+
+/* Jan  5 2004	New program
+ *
+ * Apr  6 2006	Use different server to get DR4 data
+ * Jun 20 2006	Drop unused variables
+ * Jul 11 2006	Change path to Data Release 5
+ * Oct 30 2006	Print URL in verbose mode when printing web-returned sources
+ * Oct 30 2006	Fix bug in buffer length when setting up tab table
+ * Nov  3 2006	Drop extra characters from end of data returned from SDSS
+ * Nov  6 2006	Pass SDSS ID as character string because it is too long integer
+ *
+ * Jan  8 2007	Drop unused variables
+ * Jan  9 2007	Drop refcatname from argument list; it is not used
+ * Jan 10 2007	Drop gnum argument from sdssread(); gobj replaced it
+ * Oct 22 2007	Change path to Data Release 6
+ */
diff --git a/Code/src/libwcs/shrink.c b/Code/src/libwcs/shrink.c
new file mode 100644
index 0000000000000000000000000000000000000000..994818a7eb04513701a90c9ef4befb14b4520b81
--- /dev/null
+++ b/Code/src/libwcs/shrink.c
@@ -0,0 +1,168 @@
+/* File libwcs/shrink.c
+ * January 24, 2006
+ * By Doug Mink, Harvard-Smithsonian Center for Astrophysics
+ */
+
+/* Return image buffer reduced by a given factor */
+
+#include <string.h>             /* NULL, strlen, strstr, strcpy */
+#include <stdio.h>
+#include <stdlib.h>
+#include "fitshead.h"
+
+char *
+ShrinkFITSImage (header, image, factor, floor, bitpix, nlog)
+
+char	*header;	/* Image header */
+char	*image;		/* Image bytes to be filtered */
+int	factor;		/* Factor by which to reduce size of image */
+int	floor;		/* Subtract this number from every value */
+int	bitpix;		/* Number of bits per output pixel (neg=f.p.) */
+int	nlog;		/* Logging interval in lines */
+
+{
+
+char	*image1;
+int	nx,ny;		/* Number of columns and rows in input image */
+int	nx1,ny1;	/* Number of columns and rows in input image */
+int	ix,iy;		/* Output pixel coordinates */
+int	jx,jy;		/* Input pixel coordinates */
+int	npix;		/* Number of pixels in input image */
+int	npix1;		/* Number of pixels in output image */
+int	bitsin;		/* Number of bits per input pixel (<0=floating point) */
+int	bytesin;	/* Number of bytes per input pixel */
+int	naxes;
+double	pixij;		/* Summed value of rebinned pixel */
+double	bzero, bscale;
+double	pixval, pixij, dnp;
+short	*buffi2;
+int	*buffi4;
+float	*buffr4;
+double	*buffr8;
+
+    hgeti4 (header, "BITPIX", &bitsin);
+    bytesin = bitsin / 8;
+    if (bytesin < 0)
+	bytesin = -bytesin;
+    hgeti4 (header, "NAXIS", &naxes);
+    hgeti4 (header, "NAXIS1", &nx);
+    if (naxes > 1)
+	hgeti4 (header, "NAXIS2", &ny);
+    else
+	ny = 1;
+    bzero = 0.0;
+    hgetr8 (header, "BZERO", &bzero);
+    bscale = 1.0;
+    hgetr8 (header, "BSCALE", &bscale);
+    npix = nx * ny;
+    nx1 = nx1 / factor;
+    ny1 = ny / factor;
+    npix1 = nx1 * ny1;
+
+    image1 = NULL;
+    if (bitpix == 16) {
+	image1 = (char *) calloc (npix1, sizeof (short));
+	buffi2 = (short *) image1;
+	}
+    else if (bitpix == 32) {
+	image1 = (char *) calloc (npix1, sizeof (int));
+	buffi4 = (int *) image1;
+	}
+    else if (bitpix == -32) {
+	image1 = (char *) calloc (npix1, sizeof (float));
+	buffr4 = (float *) image1;
+	}
+    else if (bitpix == -64) {
+	image1 = (char *) calloc (npix1, sizeof (double));
+	buffr8 = (double *) image1;
+	}
+
+		if (mean)
+		    *buffout++ = (short) (pixij / dnp);
+		else {
+		    if (pixij < 32768.0)
+			*buffout++ = (short) pixij;
+		    else
+			*buffout++ = 32767;
+		    }
+		}
+	    if ((jy+1)%nlog == 0)
+		fprintf (stderr,"SHRINK: %d lines created\r", jy+1);
+	    }
+	if (nlog > 0)
+	    fprintf (stderr,"\n");
+	}
+
+
+    for (jy = 0; jy < ny1; jy++) {
+	for (jx = 0; jx < nx1; jx++) {
+	    pixij = 0.0;
+	    ky = (jy * factor);
+	    if (ky + factor > ny)
+		nyf = ny - ky + 1;
+	    else
+		nyf = factor;
+	    dnp = 0.0;
+	    for (iy = 0; iy < nyf; iy++) {
+		kx = (jx * factor);
+		if (kx + factor > nx)
+		    nxf = nx - kx + 1;
+		else
+		    nxf = factor;
+		for (ix = 0; ix < nxf; ix++) {
+		    pixval = getpix (image, bitsin, nx,ny,bzero,bscale,kx++,ky);
+		    pixfal = pixval - floor;
+		    if (pixval < 0.0)
+			pixval = 0.0;
+		    pixij = pixij + pixval;
+		    dnp++;
+		    }
+		ky++;
+		}
+	    if (mean) {
+	 	switch (bitpix) {
+		    case 16:
+			*buffi2++ = (short) (pixij / dnp);
+			break;
+		    case 32:
+			*buffi4++ = (int) (pixij / dnp);
+			break;
+		    case -32:
+			*buffr4++ = (float) (pixij / dnp);
+			break;
+		    case -64:
+			*buffr8++ = (pixij / dnp);
+			break;
+		    }
+		}
+	    else {
+	 	switch (bitpix) {
+		    case 16:
+			if (pixij < 32768.0)
+			    *buffi2++ = (short) pixij;
+			else
+			    *buffi2++ = 32767;
+			break;
+		    case 32:
+			*buffi4++ = (int) pixij;
+			break;
+		    case -32:
+			*buffr4++ = (float) pixij;
+			break;
+		    case -64:
+			*buffr8++ = pixij;
+			break;
+		    }
+		}
+	    }
+	if ((jy+1)%nlog == 0)
+	    fprintf (stderr,"SHRINK: %d lines created\r", jy+1);
+	}
+    if (nlog > 0)
+	fprintf (stderr,"\n");
+
+    return (image1);
+}
+
+/* Jan 25 2006	New subroutine based on libwcs/filter.c
+ */
diff --git a/Code/src/libwcs/skybotread.c b/Code/src/libwcs/skybotread.c
new file mode 100644
index 0000000000000000000000000000000000000000..be16227e24f1eb04ac7f1d36e5c70094de68356a
--- /dev/null
+++ b/Code/src/libwcs/skybotread.c
@@ -0,0 +1,483 @@
+/*** File libwcs/skybotread.c
+ *** May 5, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2004-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "fitsfile.h"
+#include "wcs.h"
+#include "wcscat.h"
+
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+#define LINE    1024
+
+static int obscode = 801;	/* Default to Harvard Observatory, Oak Ridge */
+void setobs(newobs)
+int newobs;
+{obscode = newobs; return; }
+
+static char tabhead[500];	/* Starbase header for returned data */
+
+void
+setobsname (obsname)
+char *obsname;
+{
+    if (strcsrch (obsname, "mmt"))
+	obscode = 696;	/* Whipple Observatory, Mt. Hopkins */
+    else if (strcsrch (obsname, "whip"))
+	obscode = 696;	/* Whipple Observatory, Mt. Hopkins */
+    else if (strcsrch (obsname, "flw"))
+	obscode = 696;	/* Whipple Observatory, Mt. Hopkins */
+    else if (strcsrch (obsname, "oak"))
+	obscode = 801;	/* Harvard Observatory, Oak Ridge */
+    else if (strcsrch (obsname, "hco"))
+	obscode = 802;	/* Harvard Observatory, Cambridge */
+    else if (strcsrch (obsname, "boy"))
+	obscode = 074;	/* Boyden Observatory, Bloemfontein */
+    else if (strcsrch (obsname, "are"))
+	obscode = 800;	/* Harvard Observatory, Arequipa */
+    else
+	obscode = 500;	/* Geocenter */
+    return;
+}
+
+char *
+getobsname (code)
+int code;
+{
+    char *obsname;
+    obsname = (char *) calloc (64, 1);
+    if (code == 696)
+	strcpy (obsname, "FLWO Whipple Observatory, Mt. Hopkins");
+    else if (code == 801)
+	strcpy (obsname, "HCO Oak Ridge");
+    else if (code == 802)
+	strcpy (obsname, "HCO Cambridge");
+    else if (code == 074)
+	strcpy (obsname, "Boyden Observatory, Bloemfontein");
+    else if (code == 800)
+	strcpy (obsname, "HCO Arequipa, Peru");
+    else if (code == 500)
+	strcpy (obsname, "Geocenter");
+    else
+	sprintf (obsname, "IAU %d", obscode);
+    return (obsname);
+}
+
+/* SkyBot search engine URL */
+static char skyboturl[128]="http://www.imcce.fr/webservices/skybot/skybot_query.php";
+
+/* skybotread -- Read IMCCE SkyBot server astroids over the web */
+
+int
+skybotread (cra,cdec,dra,ddec,drad,distsort,sysout,eqout,epout,mag1,mag2,
+	    sortmag,nstarmax,gnum,gobj,gra,gdec,gpra,gpdec,gmag,gtype,nlog)
+
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+int	distsort;	/* 1 to sort asteroids by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Julian date for positions (current time if zero) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*gnum;		/* Array of asteroid numbers (returned) */
+char	**gobj;		/* Array of object IDs (too long for integer*4) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double	*gpra;		/* Array of right ascension motions (returned) */
+double	*gpdec;		/* Array of declination motions (returned) */
+double	**gmag;		/* 2-D array of magnitudes and other info (returned) */
+int	*gtype;		/* Array of object classes (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    char srchurl[LINE];
+    char cmag;
+    char *obs;
+    char *dstr;
+    struct TabTable *tabtable;
+    struct StarCat *starcat; /* Star catalog data structure */
+    double dtemp, jdout;
+    int nstar, nlog0;
+    double ra, dec, mag, dradi, dradx;
+    char rastr[32], decstr[32], temp[256], tstr[80];
+
+    /* Set up header for returned Starbase table */
+    strcpy (tabhead, "catalog\tSkyBot\n");
+    strcat (tabhead, "equinox\t2000.0\n");
+    strcat (tabhead, "radecsys\tFK5\n");
+    obs = getobsname (obscode);
+    sprintf (tstr, "obs\t%s\n", obs);
+    strcpy (tabhead, tstr);
+
+    dradi = 0.0;
+
+    nlog0 = nlog;
+    if (nstarmax < 1)
+	nlog = -1;
+
+    /* mag1 is always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+    if (mag1 < 0)
+	mag1 = 0.0;
+
+    /* Set up query for SkyBot server */
+    ra = cra;
+    dec = cdec;
+    if (sysout != WCS_J2000)
+	wcscon (sysout, WCS_J2000, eqout, 2000.0, &ra, &dec, epout);
+
+    /* Epoch for positions */
+    jdout = ep2jd (epout);
+    sprintf (srchurl, "?-ep=%.5f&", jdout);
+    dstr = jd2fd (jdout);
+    sprintf (tstr, "epoch\t%s\n",dstr);
+    strcat (tabhead, tstr);
+
+    /* Search center */
+    sprintf (temp, "-ra=%.5f&-dec=%.5f&", ra, dec);
+    strcat (srchurl, temp);
+    deg2str (rastr, 32, ra, 5);
+    deg2str (decstr, 32, dec, 5);
+    sprintf (tstr, "sra\t%s\n",rastr);
+    strcat (tabhead, tstr);
+    sprintf (tstr, "sdec\t%s\n",decstr);
+    strcat (tabhead, tstr);
+
+    /* Radius in degrees */
+    if (drad != 0.0) {
+	if (drad < 0.0) {
+	    dradx = -drad * sqrt (2.0);
+	    if (dradx > 10.0)
+		dradx = 10.0;
+	    sprintf (temp, "-rd=%.5f&", dradx);
+	    sprintf (tstr, "dra\t%.5f\n", -drad);
+	    strcat (tabhead, tstr);
+	    sprintf (tstr, "ddec\t%.5f\n", -drad);
+	    strcat (tabhead, tstr);
+	    }
+	else {
+	    if (drad > 10.0)
+		dradx = 10.0;
+	    else
+		dradx = drad;
+	    sprintf (temp, "-rd=%.5f&", dradx);
+	    sprintf (tstr, "rad\t%.5f\n", dradx);
+	    strcat (tabhead, tstr);
+	    }
+	}
+
+    /* Box size in degrees */
+    else {
+	dradx = sqrt ((dra * dra) + (ddec * ddec));
+	sprintf (temp, "-rd=%.5f&", dradx);
+	sprintf (tstr, "dra\t%.6f\n", dra);
+	strcat (tabhead, tstr);
+	sprintf (tstr, "ddec\t%.6f\n", ddec);
+	strcat (tabhead, tstr);
+	}
+
+    /* Units for motion on the sky */
+    strcat (tabhead, "rpmunit\tarcsec/hour\n");
+    strcat (tabhead, "dpmunit\tarcsec/hour\n");
+
+    strcat (srchurl, temp);
+
+    /* Output type */
+    strcat (srchurl, "-mime=text&");
+
+    /* IAU observatory code*/
+    sprintf (temp, "loc=%03d&", obscode);
+    strcat (srchurl, temp);
+
+    /* Drop comets to save time */
+    strcat (srchurl, "-objFilter=110&");
+
+    /* Drop planets to save time (unused)
+    strcat (srchurl, "-objFilter=100&"); */
+
+    /* Source of search */
+    strcat (srchurl, "-from=WCSTools");
+
+    if (nlog0 > 0)
+	fprintf (stderr,"%s%s\n", skyboturl, srchurl);
+
+    /* Run search across the web */
+    if ((tabtable = webopen (skyboturl, srchurl, nlog)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "SKYBOTREAD: %s failed\n", srchurl);
+	return (0);
+	}
+
+    /* Return if no data */
+    if (tabtable->tabdata == NULL || strlen (tabtable->tabdata) == 0 ||
+	!strncasecmp (tabtable->tabdata, "[EOD]", 5)) {
+	if (nlog > 0)
+	    fprintf (stderr, "SKYBOTREAD: No data returned\n");
+	return (0);
+	}
+
+    /* Dump returned file and stop */
+    if (nlog < 0) {
+	(void) fwrite  (tabtable->tabbuff, tabtable->lbuff, 1, stdout);
+	exit (0);
+	}
+
+    /* Open returned Starbase table as a catalog */
+    if ((starcat = tabcatopen (skyboturl, tabtable,0)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "SKYBOTREAD: Could not open Starbase table as catalog\n");
+	return (0);
+	}
+
+    /* Set reference frame, epoch, and equinox of catalog */
+    starcat->coorsys = WCS_J2000;
+    starcat->epoch = 2000.0;
+    starcat->equinox = 2000.0;
+    starcat->nmag = 3;
+
+    /* Extract desired sources from catalog  and return them */
+    nstar = tabread (skyboturl,distsort,cra,cdec,dra,ddec,drad,dradi,
+	     sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,&starcat,
+	     gnum,gra,gdec,gpra,gpdec,gmag,gtype,gobj,nlog);
+
+    tabcatclose (starcat);
+
+    starcat = NULL;
+
+    return (nstar);
+}
+
+char *
+skybot2tab (skybuff)
+
+    char *skybuff;	/* Input comma-separated table */
+{
+    char *heading;
+    char *colhead;
+    char *colsep;
+    char *tabbuff;	/* Output tab-separated table */
+    char *tbuff;
+    char *databuff;
+    char *dbuff;
+    char *buffer;
+    char *lastbuff;
+    char *endhead;
+    char *colend;
+    char *chead;
+    char *head;
+    char *tbuffi;
+    char temp[16], format[16];
+    int lbuff, i, lra, icol;
+    int lhead, ldata;
+    int addname;
+    int lobj;
+    char cbuff;
+    char *buff;
+    char cbar = '|';
+    char ccom = ',';
+    char cminus = '-';
+    char cspace = ' ';
+    char ctab = (char) 9;
+    char clf = (char) 10;
+    double ra;
+
+    /* Skip first two lines of returned header */
+    buffer = strchr (skybuff, '\n') + 1;
+    buffer = strchr (buffer, '\n') + 1;
+
+    /* Skip header line as it is recreated */
+    buffer = strchr (buffer, '\n') + 1;
+
+    /* Allocate starbase table for output */
+    lbuff = strlen (skybuff) + strlen (tabhead) + 200;
+    tabbuff = (char *) calloc (lbuff, 1);
+
+    /* Add metadata */
+    strcpy (tabbuff, tabhead);
+
+    /* Set up tabbed column headings */
+    i = 0;
+    lhead = 0;
+    colhead = tabbuff + strlen (tabbuff);
+
+    /* Combine number and name into first column */
+    strcpy (colhead, "object          ");
+    strcat (colhead,"\t");
+
+    /* Fix heading for RA column, which will be degrees, not hours */
+    strcat (colhead, "ra           ");
+    strcat (colhead,"\t");
+
+    /* Fix colheading for Dec column */
+    strcat (colhead, "dec         ");
+    strcat (colhead,"\t");
+
+    strcat (colhead, "class ");
+    strcat (colhead,"\t");
+
+    strcat (colhead, "vmag ");
+    strcat (colhead,"\t");
+
+    strcat (colhead, "poserr");
+    strcat (colhead,"\t");
+
+    strcat (colhead, "offset");
+    strcat (colhead,"\t");
+
+    strcat (colhead, "rapm  ");
+    strcat (colhead,"\t");
+
+    strcat (colhead, "decpm ");
+    strcat (colhead,"\t");
+
+    strcat (colhead, "gdist       ");
+    strcat (colhead,"\t");
+
+    strcat (colhead, "hdist       ");
+    strcat (colhead,"\n");
+    lhead = strlen (colhead);
+
+    /* Set up tabbed separator line */
+    colsep = colhead + lhead;
+    for (i = 0; i < lhead; i++) {
+	cbuff = colhead[i];
+	if (cbuff == ctab)
+	    colsep[i] = ctab;
+	else if (cbuff == (char) 10)
+	    colsep[i] = (char) 10;
+	else
+	    colsep[i] = '-';
+	}
+
+    /* Copy input through final linefeed */
+    lastbuff = strrchr (buffer, '\n') + 1;
+
+    /* Copy data to output buffer, dropping leading and trailing spaces
+       and converting vertical bars in table to tabs */
+    dbuff = buffer;
+    tbuff = tabbuff + strlen (tabbuff);
+    icol = 0;
+    while (dbuff < lastbuff) {
+
+	/* Drop out if blank line encountered */
+	if (icol == 0 && *dbuff == clf)
+	    break;
+
+	/* Combine ID and name in first column */
+	if (icol == 0) {
+	    tbuffi = tbuff;
+	    while (*dbuff == cspace)
+		dbuff++;
+	    if (*dbuff == cminus) {
+		addname = 0;
+		dbuff++;
+		}
+	    else
+		addname = 1;
+	    while (*dbuff != cbar) {
+		if (*dbuff != cspace)
+		    *tbuff++ = *dbuff;
+		dbuff++;
+		}
+	    if (*dbuff == cbar) {
+		icol++;
+		dbuff++;
+		}
+	    if (addname)
+		*tbuff++ = '(';
+	    while (*dbuff != cbar) {
+		if (*dbuff != cspace)
+		    *tbuff++ = *dbuff;
+		dbuff++;
+		}
+	    if (addname)
+		*tbuff++ = ')';
+	    lobj = tbuff - tbuffi;
+	    if (lobj < 16) {
+		for (i = lobj; i < 16; i++)
+		    *tbuff++ = ' ';
+		}
+	    }
+	if (*dbuff == cbar) {
+	    *tbuff++ = ctab;
+	    icol++;
+	    dbuff++;
+
+	    /* Convert RA from fractional hours to fractional degrees */
+	    if (icol == 2) {
+		ra = atof (dbuff) * 15.0;
+		colend = strchr (dbuff, cbar) - 1;
+		lra = colend - dbuff;
+		sprintf (format,"%%%d.%df",lra,lra-4);
+		sprintf (temp, format, ra);
+		for (i = 0; i < lra; i++)
+		    dbuff[i] = temp[i];
+		dbuff--;
+		}
+
+	    /* Shorten "Satellite" to Sat */
+	    if (icol == 4) {
+		if (!strncmp (dbuff, " Sat", 4)) {
+		    *tbuff++ = 'S';
+		    *tbuff++ = 'a';
+		    *tbuff++ = 't';
+		    dbuff = dbuff + 9;
+		    }
+		else
+		    dbuff--;
+		}
+	    }
+	else if (*dbuff == clf) {
+	    *tbuff++ = *dbuff;
+	    icol = 0;
+	    }
+	else if (*dbuff != cspace)
+	    *tbuff++ = *dbuff;
+	dbuff++;
+	}
+
+    return (tabbuff);
+}
+
+/* Jul 26 2004	New program
+ *
+ * May  5 2009	Add -objFilter=110 to drop comets (someday, this might be optional)
+ */
diff --git a/Code/src/libwcs/sortstar.c b/Code/src/libwcs/sortstar.c
new file mode 100644
index 0000000000000000000000000000000000000000..86cb7fb542eed359b3d927041708df81cac8a299
--- /dev/null
+++ b/Code/src/libwcs/sortstar.c
@@ -0,0 +1,1136 @@
+/*** File libwcs/sortstar.c
+ *** January 11, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+/* void FluxSortStars()		Sort star list based on brightness
+ * int StarFluxSort()		Return brightest of two stars based on flux
+ * void MagSortStars()		Sort stars list based on magnitude
+ * int StarMagSort()		Return brightest of two stars based on mag.
+ * void RASortStars()		Sort stars based on right ascension
+ * int StarRASort()		Return star with lowest right ascension
+ * void DecSortStars()		Sort stars based on declination
+ * int StarDecSort()		Return star with lowest declination
+ * void IDSortStars()		Sort stars based on ID number
+ * int StarIDSort()		Return star with lowest ID number
+ * void XSortStars()		Sort stars based on X coordinate in image
+ * int StarXSort()		Return star with lowest X coordinate
+ * void YSortStars()		Sort stars based on Y coordinate in image
+ * int StarYSort()		Return star with lowest Y coordinate
+ * int MergeStars()		Merge multiple entries within given radius
+ * int StarMerge()		Merge stars, called by MergeStars()
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+/* structure for star lists needed for sorting */
+typedef struct {
+    double n;		/* Identifying number */
+    double ra;		/* Right Ascension */
+    double dec;		/* Declination */
+    double pra;		/* Right Ascension proper motion */
+    double pdec;	/* Declination proper motion */
+    double m[MAXNMAG+1];	/* Magnitude */
+    double b;		/* flux */
+    double x;		/* Image X coordinate */
+    double y;		/* Image Y coordinate */
+    int    c;		/* Other 4-byte information */
+    char   *obj;	/* Object name */
+} StarInfo;
+
+/* Sort image stars by decreasing flux */
+
+void
+FluxSortStars (sx, sy, sb, sc, ns)
+
+double	*sx;
+double	*sy;
+double	*sb;
+int	*sc;
+int	ns;
+
+{
+    StarInfo *stars;
+    int StarFluxSort ();
+    int i;
+
+    stars = (StarInfo *) calloc ((unsigned int)ns, sizeof(StarInfo));
+
+    for (i = 0; i < ns; i++) {
+	stars[i].x = sx[i];
+	stars[i].y = sy[i];
+	stars[i].b = sb[i];
+	stars[i].c = sc[i];
+	}
+
+    qsort ((char *)stars, ns, sizeof(StarInfo), StarFluxSort);
+
+    for (i = 0; i < ns; i++) {
+	sx[i] = stars[i].x;
+	sy[i] = stars[i].y;
+	sb[i] = stars[i].b;
+	sc[i] = stars[i].c;
+	}
+
+    free ((char *)stars);
+    return;
+}
+
+
+/* StarFluxSort -- Order stars in decreasing flux called by qsort */
+
+int
+StarFluxSort (ssp1, ssp2)
+
+void *ssp1, *ssp2;
+
+{
+    double b1 = ((StarInfo *)ssp1)->b;
+    double b2 = ((StarInfo *)ssp2)->b;
+
+    if (b2 > b1)
+	return (1);
+    else if (b2 < b1)
+	return (-1);
+    else
+	return (0);
+}
+
+
+/* MagSortStars -- Sort image stars by increasing magnitude */
+static int magsort = 0;
+
+void
+MagSortStars (sn, sra, sdec, spra, spdec, sx, sy, sm, sc, sobj, ns, nm, ms)
+
+double *sn;		/* Identifying number */
+double *sra;		/* Right Ascension */
+double *sdec;		/* Declination */
+double *spra;		/* Right Ascension proper motion */
+double *spdec;		/* Declination proper motion */
+double *sx;		/* Image X coordinate */
+double *sy;		/* Image Y coordinate */
+double **sm;		/* Magnitudes */
+int    *sc;		/* Other 4-byte information */
+char   **sobj;		/* Object name */
+int	ns;		/* Number of stars to sort */
+int	nm;		/* Number of magnitudes per star */
+int	ms;		/* Magnitude by which to sort (1 to nmag) */
+
+{
+    StarInfo *stars;
+    int i, j, hasnum, haspos, hasobj, haspm, hasxy;
+    int StarMagSort();
+
+    stars = (StarInfo *) calloc ((unsigned int)ns, sizeof(StarInfo));
+
+    if (ms > 0 && ms <= nm)
+	magsort = ms - 1;
+
+    if (sn == NULL)
+	hasnum = 0;
+    else
+	hasnum = 1;
+    if (sra != NULL && sdec != NULL)
+	haspos = 1;
+    else
+	haspos = 0;
+    if (spra != NULL && spdec != NULL)
+	haspm = 1;
+    else
+	haspm = 0;
+    if (sx != NULL && sy != NULL)
+	hasxy = 1;
+    else
+	hasxy = 0;
+    if (sobj == NULL)
+	hasobj = 0;
+    else
+	hasobj = 1;
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    stars[i].n = sn[i];
+	if (haspos) {
+	    stars[i].ra = sra[i];
+	    stars[i].dec = sdec[i];
+	    }
+	if (haspm) {
+	    stars[i].pra = spra[i];
+	    stars[i].pdec = spdec[i];
+	    }
+	if (hasxy) {
+	    stars[i].x = sx[i];
+	    stars[i].y = sy[i];
+	    }
+	for (j = 0; j < nm; j++)
+	    stars[i].m[j] = sm[j][i];
+	stars[i].c = sc[i];
+	if (hasobj)
+	    stars[i].obj = sobj[i];
+	}
+
+    qsort ((char *)stars, ns, sizeof(StarInfo), StarMagSort);
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    sn[i] = stars[i].n;
+	if (haspos) {
+	    sra[i] = stars[i].ra;
+	    sdec[i] = stars[i].dec;
+	    }
+	if (haspm) {
+	    spra[i] = stars[i].pra;
+	    spdec[i] = stars[i].pdec;
+	    }
+	if (hasxy) {
+	    sx[i] = stars[i].x;
+	    sy[i] = stars[i].y;
+	    }
+	for (j = 0; j < nm; j++)
+	    sm[j][i] = stars[i].m[j];
+	sc[i] = stars[i].c;
+	if (hasobj)
+	    sobj[i] = stars[i].obj;
+	}
+
+    free ((char *)stars);
+    return;
+}
+
+
+/* StarMagSort -- Order stars in decreasing flux called by qsort */
+
+int
+StarMagSort (ssp1, ssp2)
+
+void *ssp1, *ssp2;
+
+{
+    double b1 = ((StarInfo *)ssp1)->m[magsort];
+    double b2 = ((StarInfo *)ssp2)->m[magsort];
+
+    /* If sort magnitude is not set, check the others until one is found */
+    if (b1 > 100.0)
+	b1 = b1 - 100.0;
+    if (b1 == 99.90)
+	b1 = ((StarInfo *)ssp1)->m[0];
+    if (b1 == 99.90)
+	b1 = ((StarInfo *)ssp1)->m[1];
+    if (b1 == 99.90)
+	b1 = ((StarInfo *)ssp1)->m[2];
+    if (b1 == 99.90)
+	b1 = ((StarInfo *)ssp1)->m[3];
+
+    /* If sort magnitude is not set, check the others until one is found */
+    if (b2 > 100.0)
+	b2 = b2 - 100.0;
+    if (b2 == 99.90)
+	b2 = ((StarInfo *)ssp2)->m[0];
+    if (b2 == 99.90)
+	b2 = ((StarInfo *)ssp2)->m[1];
+    if (b2 == 99.90)
+	b2 = ((StarInfo *)ssp2)->m[2];
+    if (b2 == 99.90)
+	b2 = ((StarInfo *)ssp2)->m[3];
+
+    if (b2 < b1)
+	return (1);
+    else if (b2 > b1)
+	return (-1);
+    else
+	return (0);
+}
+
+
+/* IDSortStars -- Sort image stars by increasing ID Number value */
+
+void
+IDSortStars (sn, sra, sdec, spra, spdec, sx, sy, sm, sc, sobj, ns, nm)
+
+double *sn;		/* Identifying number */
+double *sra;		/* Right Ascension */
+double *sdec;		/* Declination */
+double *spra;		/* Right Ascension proper motion */
+double *spdec;		/* Declination proper motion */
+double *sx;		/* Image X coordinate */
+double *sy;		/* Image Y coordinate */
+double **sm;		/* Magnitudes */
+int    *sc;		/* Other 4-byte information */
+char   **sobj;		/* Object name */
+int	ns;		/* Number of stars to sort */
+int	nm;		/* Number of magnitudes per star */
+{
+    StarInfo *stars;
+    int i, j, hasnum, hasobj, haspos, haspm, hasxy;
+    int StarIDSort ();
+
+    stars = (StarInfo *) calloc ((unsigned int)ns, sizeof(StarInfo));
+    if (sn == NULL)
+	return;
+    else
+	hasnum = 1;
+    if (sra != NULL && sdec != NULL)
+	haspos = 1;
+    else
+	haspos = 0;
+    if (spra != NULL && spdec != NULL)
+	haspm = 1;
+    else
+	haspm = 0;
+    if (sobj == NULL)
+	hasobj = 0;
+    else
+	hasobj = 1;
+    if (sx != NULL && sy != NULL)
+	hasxy = 1;
+    else
+	hasxy = 0;
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    stars[i].n = sn[i];
+	if (haspos) {
+	    stars[i].ra = sra[i];
+	    stars[i].dec = sdec[i];
+	    }
+	if (haspm) {
+	    stars[i].pra = spra[i];
+	    stars[i].pdec = spdec[i];
+	    }
+	if (hasxy) {
+	    stars[i].x = sx[i];
+	    stars[i].y = sy[i];
+	    }
+	for (j = 0; j < nm; j++)
+	    stars[i].m[j] = sm[j][i];
+	stars[i].c = sc[i];
+	if (hasobj)
+	    stars[i].obj = sobj[i];
+	}
+
+    qsort ((char *)stars, ns, sizeof(StarInfo), StarIDSort);
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    sn[i] = stars[i].n;
+	if (haspos) {
+	    sra[i] = stars[i].ra;
+	    sdec[i] = stars[i].dec;
+	    }
+	if (haspm) {
+	    spra[i] = stars[i].pra;
+	    spdec[i] = stars[i].pdec;
+	    }
+	if (hasxy) {
+	    sx[i] = stars[i].x;
+	    sy[i] = stars[i].y;
+	    }
+	for (j = 0; j < nm; j++)
+	    sm[j][i] = stars[i].m[j];
+	sc[i] = stars[i].c;
+	if (hasobj)
+	    sobj[i] = stars[i].obj;
+	}
+
+    free ((char *)stars);
+    return;
+}
+
+
+/* StarIDSort -- Order stars in increasing ID value called by qsort */
+
+int
+StarIDSort (ssp1, ssp2)
+
+void *ssp1, *ssp2;
+
+{
+    double n1 = ((StarInfo *)ssp1)->n;
+    double n2 = ((StarInfo *)ssp2)->n;
+
+    if (n2 < n1)
+	return (1);
+    else if (n2 > n1)
+	return (-1);
+    else
+	return (0);
+}
+
+
+/* Sort image stars by increasing right ascension */
+
+void
+RASortStars (sn, sra, sdec, spra, spdec, sx, sy, sm, sc, sobj, ns, nm)
+
+double *sn;		/* Identifying number */
+double *sra;		/* Right Ascension */
+double *sdec;		/* Declination */
+double *spra;		/* Right Ascension proper motion */
+double *spdec;		/* Declination proper motion */
+double *sx;		/* Image X coordinate */
+double *sy;		/* Image Y coordinate */
+double **sm;		/* Magnitudes */
+int    *sc;		/* Other 4-byte information */
+char   **sobj;		/* Object name */
+int	ns;		/* Number of stars to sort */
+int	nm;		/* Number of magnitudes per star */
+{
+    StarInfo *stars;
+    int i, j, hasnum, hasobj, haspm, hasxy;
+    int StarRASort();
+
+    stars = (StarInfo *) calloc ((unsigned int)ns, sizeof(StarInfo));
+
+    if (sn == NULL)
+	hasnum = 0;
+    else
+	hasnum = 1;
+    if (spra != NULL && spdec != NULL)
+	haspm = 1;
+    if (sx != NULL && sy != NULL)
+	hasxy = 1;
+    else
+	hasxy = 0;
+    if (sobj == NULL)
+	hasobj = 0;
+    else
+	hasobj = 1;
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    stars[i].n = sn[i];
+	stars[i].ra = sra[i];
+	stars[i].dec = sdec[i];
+	if (haspm) {
+	    stars[i].pra = spra[i];
+	    stars[i].pdec = spdec[i];
+	    }
+	if (hasxy) {
+	    stars[i].x = sx[i];
+	    stars[i].y = sy[i];
+	    }
+	for (j = 0; j < nm; j++)
+	    stars[i].m[j] = sm[j][i];
+	stars[i].c = sc[i];
+	if (hasobj)
+	    stars[i].obj = sobj[i];
+	}
+
+    qsort ((char *)stars, ns, sizeof(StarInfo), StarRASort);
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    sn[i] = stars[i].n;
+	sra[i] = stars[i].ra;
+	sdec[i] = stars[i].dec;
+	if (haspm) {
+	    spra[i] = stars[i].pra;
+	    spdec[i] = stars[i].pdec;
+	    }
+	if (hasxy) {
+	    sx[i] = stars[i].x;
+	    sy[i] = stars[i].y;
+	    }
+	for (j = 0; j < nm; j++)
+	    sm[j][i] = stars[i].m[j];
+	sc[i] = stars[i].c;
+	if (hasobj)
+	    sobj[i] = stars[i].obj;
+	}
+
+    free ((char *)stars);
+    return;
+}
+
+
+/* Order stars in increasing right ascension (called by qsort) */
+
+int
+StarRASort (ssp1, ssp2)
+
+const void *ssp1, *ssp2;
+
+{
+    double ra1 = ((StarInfo *)ssp1)->ra;
+    double ra2 = ((StarInfo *)ssp2)->ra;
+
+    if (ra2 > ra1)
+	return (-1);
+    else if (ra2 < ra1)
+	return (1);
+    else
+	return (0);
+}
+
+
+/* Sort image stars by increasing declination */
+
+void
+DecSortStars (sn, sra, sdec, spra, spdec, sx, sy, sm, sc, sobj, ns, nm)
+
+double *sn;		/* Identifying number */
+double *sra;		/* Right Ascension */
+double *sdec;		/* Declination */
+double *spra;		/* Right Ascension proper motion */
+double *spdec;		/* Declination proper motion */
+double *sx;		/* Image X coordinate */
+double *sy;		/* Image Y coordinate */
+double **sm;		/* Magnitudes */
+int    *sc;		/* Other 4-byte information */
+char   **sobj;		/* Object name */
+int	ns;		/* Number of stars to sort */
+int	nm;		/* Number of magnitudes per star */
+{
+    StarInfo *stars;
+    int i, j, hasnum, hasobj, haspm, hasxy;
+    int StarDecSort ();
+
+    stars = (StarInfo *) calloc ((unsigned int)ns, sizeof(StarInfo));
+
+    if (sn == NULL)
+	hasnum = 0;
+    else
+	hasnum = 1;
+    if (spra != NULL && spdec != NULL)
+	haspm = 1;
+    else
+	haspm = 0;
+    if (sx != NULL && sy != NULL)
+	hasxy = 1;
+    else
+	hasxy = 0;
+    if (sobj == NULL)
+	hasobj = 0;
+    else
+	hasobj = 1;
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    stars[i].n = sn[i];
+	stars[i].ra = sra[i];
+	stars[i].dec = sdec[i];
+	if (haspm) {
+	    stars[i].pra = spra[i];
+	    stars[i].pdec = spdec[i];
+	    }
+	if (hasxy) {
+	    stars[i].x = sx[i];
+	    stars[i].y = sy[i];
+	    }
+	for (j = 0; j < nm; j++)
+	    stars[i].m[j] = sm[j][i];
+	stars[i].c = sc[i];
+	if (hasobj)
+	    stars[i].obj = sobj[i];
+	}
+
+    qsort ((char *)stars, ns, sizeof(StarInfo), StarDecSort);
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    sn[i] = stars[i].n;
+	sra[i] = stars[i].ra;
+	sdec[i] = stars[i].dec;
+	if (haspm) {
+	    spra[i] = stars[i].pra;
+	    spdec[i] = stars[i].pdec;
+	    }
+	if (hasxy) {
+	    sx[i] = stars[i].x;
+	    sy[i] = stars[i].y;
+	    }
+	for (j = 0; j < nm; j++)
+	    sm[j][i] = stars[i].m[j];
+	sc[i] = stars[i].c;
+	if (hasobj)
+	    sobj[i] = stars[i].obj;
+	}
+
+    free ((char *)stars);
+    return;
+}
+
+
+/* Order stars in increasing declination (called by qsort) */
+
+int
+StarDecSort (ssp1, ssp2)
+
+void *ssp1, *ssp2;
+
+{
+    double dec1 = ((StarInfo *)ssp1)->dec;
+    double dec2 = ((StarInfo *)ssp2)->dec;
+
+    if (dec2 > dec1)
+	return (-1);
+    else if (dec2 < dec1)
+	return (1);
+    else
+	return (0);
+}
+
+
+/* XSortStars -- Sort image stars by increasing X value */
+
+void
+XSortStars (sn, sra, sdec, spra, spdec, sx, sy, sm, sc, sobj, ns, nm)
+
+double *sn;		/* Identifying number */
+double *sra;		/* Right Ascension */
+double *sdec;		/* Declination */
+double *spra;		/* Right Ascension proper motion */
+double *spdec;		/* Declination proper motion */
+double *sx;		/* Image X coordinate */
+double *sy;		/* Image Y coordinate */
+double **sm;		/* Magnitudes */
+int    *sc;		/* Other 4-byte information */
+char   **sobj;		/* Object name */
+int	ns;		/* Number of stars to sort */
+int	nm;		/* Number of magnitudes per star */
+{
+    StarInfo *stars;
+    int i, j, hasnum, hasobj, haspos, haspm;
+    int StarXSort ();
+
+    stars = (StarInfo *) calloc ((unsigned int)ns, sizeof(StarInfo));
+    if (sn == NULL)
+	hasnum = 0;
+    else
+	hasnum = 1;
+    if (sra != NULL && sdec != NULL)
+	haspos = 1;
+    else
+	haspos = 0;
+    if (spra != NULL && spdec != NULL)
+	haspm = 1;
+    else
+	haspm = 0;
+    if (sobj == NULL)
+	hasobj = 0;
+    else
+	hasobj = 1;
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    stars[i].n = sn[i];
+	if (haspos) {
+	    stars[i].ra = sra[i];
+	    stars[i].dec = sdec[i];
+	    }
+	if (haspm) {
+	    stars[i].pra = spra[i];
+	    stars[i].pdec = spdec[i];
+	    }
+	stars[i].x = sx[i];
+	stars[i].y = sy[i];
+	for (j = 0; j < nm; j++)
+	    stars[i].m[j] = sm[j][i];
+	stars[i].c = sc[i];
+	if (hasobj)
+	    stars[i].obj = sobj[i];
+	}
+
+    qsort ((char *)stars, ns, sizeof(StarInfo), StarXSort);
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    sn[i] = stars[i].n;
+	if (haspos) {
+	    sra[i] = stars[i].ra;
+	    sdec[i] = stars[i].dec;
+	    }
+	if (haspm) {
+	    spra[i] = stars[i].pra;
+	    spdec[i] = stars[i].pdec;
+	    }
+	sx[i] = stars[i].x;
+	sy[i] = stars[i].y;
+	for (j = 0; j < nm; j++)
+	    sm[j][i] = stars[i].m[j];
+	sc[i] = stars[i].c;
+	if (hasobj)
+	    sobj[i] = stars[i].obj;
+	}
+
+    free ((char *)stars);
+    return;
+}
+
+
+/* StarXSort -- Order stars in decreasing X value called by qsort */
+
+int
+StarXSort (ssp1, ssp2)
+
+void *ssp1, *ssp2;
+
+{
+    double x1 = ((StarInfo *)ssp1)->x;
+    double x2 = ((StarInfo *)ssp2)->x;
+
+    if (x2 < x1)
+	return (1);
+    else if (x2 > x1)
+	return (-1);
+    else
+	return (0);
+}
+
+
+/* YSortStars -- Sort image stars by increasing Y value */
+
+void
+YSortStars (sn, sra, sdec, spra, spdec, sx, sy, sm, sc, sobj, ns, nm)
+
+double *sn;		/* Identifying number */
+double *sra;		/* Right Ascension */
+double *sdec;		/* Declination */
+double *spra;		/* Right Ascension proper motion */
+double *spdec;		/* Declination proper motion */
+double *sx;		/* Image X coordinate */
+double *sy;		/* Image Y coordinate */
+double **sm;		/* Magnitudes */
+int    *sc;		/* Other 4-byte information */
+char   **sobj;		/* Object name */
+int	ns;		/* Number of stars to sort */
+int	nm;		/* Number of magnitudes per star */
+{
+    StarInfo *stars;
+    int i, j, hasnum, hasobj, haspm, haspos;
+    int StarYSort ();
+
+    stars = (StarInfo *) calloc ((unsigned int)ns, sizeof(StarInfo));
+    if (sn == NULL)
+	hasnum = 0;
+    else
+	hasnum = 1;
+    if (sra != NULL && sdec != NULL)
+	haspos = 1;
+    else
+	haspos = 0;
+    if (spra != NULL && spdec != NULL)
+	haspm = 1;
+    else
+	haspm = 0;
+    if (sobj == NULL)
+	hasobj = 0;
+    else
+	hasobj = 1;
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    stars[i].n = sn[i];
+	if (haspos) {
+	    stars[i].ra = sra[i];
+	    stars[i].dec = sdec[i];
+	    }
+	if (haspm) {
+	    stars[i].pra = spra[i];
+	    stars[i].pdec = spdec[i];
+	    }
+	stars[i].x = sx[i];
+	stars[i].y = sy[i];
+	for (j = 0; j < nm; j++)
+	    stars[i].m[j] = sm[j][i];
+	stars[i].c = sc[i];
+	if (hasobj)
+	    stars[i].obj = sobj[i];
+	}
+
+    qsort ((char *)stars, ns, sizeof(StarInfo), StarYSort);
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    sn[i] = stars[i].n;
+	if (haspos) {
+	    sra[i] = stars[i].ra;
+	    sdec[i] = stars[i].dec;
+	    }
+	if (haspm) {
+	    spra[i] = stars[i].pra;
+	    spdec[i] = stars[i].pdec;
+	    }
+	sx[i] = stars[i].x;
+	sy[i] = stars[i].y;
+	for (j = 0; j < nm; j++)
+	    sm[j][i] = stars[i].m[j];
+	sc[i] = stars[i].c;
+	if (hasobj)
+	    sobj[i] = stars[i].obj;
+	}
+
+    free ((char *)stars);
+    return;
+}
+
+
+/* StarYSort -- Order stars in decreasing Y value called by qsort */
+
+int
+StarYSort (ssp1, ssp2)
+
+void *ssp1, *ssp2;
+
+{
+    double y1 = ((StarInfo *)ssp1)->y;
+    double y2 = ((StarInfo *)ssp2)->y;
+
+    if (y2 < y1)
+	return (1);
+    else if (y2 > y1)
+	return (-1);
+    else
+	return (0);
+}
+
+static int logmerge = 0;
+
+/* MergeStars -- Merge multiple entries within given radius */
+/*               return mean ra, dec, proper motion, and magnitude(s) */
+
+int
+MergeStars (sn,sra,sdec,spra,spdec,sx,sy,sm,sc,sobj,ns,nm,rad,log)
+
+double *sn;		/* Identifying number */
+double *sra;		/* Right Ascension */
+double *sdec;		/* Declination */
+double *spra;		/* Right Ascension proper motion */
+double *spdec;		/* Declination proper motion */
+double *sx;		/* Image X coordinate */
+double *sy;		/* Image Y coordinate */
+double **sm;		/* Magnitudes */
+int    *sc;		/* Other 4-byte information */
+char   **sobj;		/* Object name */
+int	ns;		/* Number of stars to sort */
+int	nm;		/* Number of magnitudes per star */
+double	rad;		/* Maximum separation in arcseconds to merge */
+int	log;		/* If >0, log progress every time mod number written */
+{
+    StarInfo *stars;
+    int i, j, hasnum, hasobj, haspm, hasxy;
+    int StarMerge();
+    int nns;		/* Number of stars in merged catalog (returned) */
+    double drad;	/* Maximum separation in degrees to merge */
+    int StarRASort();
+
+    stars = (StarInfo *) calloc ((unsigned int)ns, sizeof(StarInfo));
+    drad = rad / 3600.0;
+    logmerge = log;
+
+    if (sn == NULL)
+	hasnum = 0;
+    else
+	hasnum = 1;
+    if (spra != NULL && spdec != NULL)
+	haspm = 1;
+    if (sx != NULL && sy != NULL)
+	hasxy = 1;
+    else
+	hasxy = 0;
+    if (sobj == NULL)
+	hasobj = 0;
+    else
+	hasobj = 1;
+
+    for (i = 0; i < ns; i++) {
+	if (hasnum)
+	    stars[i].n = sn[i];
+	else
+	    stars[i].n = (double) i;
+	stars[i].ra = sra[i];
+	stars[i].dec = sdec[i];
+	if (haspm) {
+	    stars[i].pra = spra[i];
+	    stars[i].pdec = spdec[i];
+	    }
+	else {
+	    stars[i].pra = 0.0;
+	    stars[i].pdec = 0.0;
+	    }
+	if (hasxy) {
+	    stars[i].x = sx[i];
+	    stars[i].y = sy[i];
+	    }
+	else {
+	    stars[i].x = 0.0;
+	    stars[i].y = 0.0;
+	    }
+	for (j = 0; j < nm; j++)
+	    stars[i].m[j] = sm[j][i];
+	stars[i].c = sc[i];
+	if (hasobj)
+	    stars[i].obj = sobj[i];
+	}
+
+    /* Sort stars by right ascension */
+    if (logmerge)
+	fprintf (stderr, "MergeStars: Sorting %d stars\n", ns);
+    qsort ((char *)stars, ns, sizeof(StarInfo), StarRASort);
+
+    /* Merge RA-sorted catalogued stars within drad arcseconds of each other */
+    /* (array stars is replaced by shorter array) */
+    if (logmerge)
+	fprintf (stderr, "MergeStars: Merging %d stars\n", ns);
+    nns = StarMerge (ns, nm, &stars, drad);
+
+    /* Re-sort stars by right ascension */
+    if (logmerge)
+	fprintf (stderr, "MergeStars: Sorting %d stars\n", nns);
+    qsort ((char *)stars, nns, sizeof(StarInfo), StarRASort);
+
+    for (i = 0; i < nns; i++) {
+	if (hasnum)
+	    sn[i] = stars[i].n;
+	sra[i] = stars[i].ra;
+	sdec[i] = stars[i].dec;
+	if (haspm) {
+	    spra[i] = stars[i].pra;
+	    spdec[i] = stars[i].pdec;
+	    }
+	if (hasxy) {
+	    sx[i] = stars[i].x;
+	    sy[i] = stars[i].y;
+	    }
+	for (j = 0; j < nm; j++)
+	    sm[j][i] = stars[i].m[j];
+	sc[i] = stars[i].c;
+	if (hasobj)
+	    sobj[i] = stars[i].obj;
+	}
+
+    free ((char *)stars);
+    return (nns);
+}
+
+#define MAXMERGE	32
+
+/* StarMerge -- Merge stars, called by MergeStars() */
+
+int
+StarMerge (ns, nm, stars0, rad)
+
+int	ns;		/* Number of stars to merge */
+int	nm; 		/* Number of magnitudes per star */
+StarInfo **stars0;
+double	rad;		/* Maximum separation in arcseconds to merge */
+
+{
+    StarInfo *stars, *newstars;
+    double ra, dec, rsec, dsec, pra, pdec, mag[11];
+    double rai, deci, dn, dmax;
+    int i, is, js, nthis, im, no, nums[MAXMERGE];
+
+    stars = *stars0;
+    newstars = (StarInfo *) calloc ((unsigned int)ns, sizeof(StarInfo));
+    dmax = rad + 1.0;
+
+    /* Loop through stars in input catalog */
+    no = 0;
+    for (is = 1; is < ns; is++) {
+
+	/* Ignore star if has already been used */
+	if (stars[is].y == -999.0)
+	    continue;
+
+	/* Initialize position to that of current star */
+	nthis = 1;
+	ra = stars[is].ra;
+	dec = stars[is].dec;
+
+	/* Find all stars within rad arcseconds of current input star */
+	/* Search forward (upward in RA) */
+	for (js = is; js < ns; js++) {
+	    dsec = (stars[js].ra - stars[is].ra) * 3600.0;
+	    if (dsec > dmax)
+		break;
+	    if (is != js && stars[js].y != -999.0) {
+		rsec = wcsdist (stars[is].ra, stars[is].dec,
+				stars[js].ra, stars[js].dec);
+		if (rsec <= rad) {
+		    nthis = nthis + 1;
+		    ra = ra + stars[js].ra;
+		    dec = dec + stars[js].dec;
+		    }
+		}
+	    }
+
+	/* Search backward (downward in RA) */
+	for (js = is; js > 0; js--) {
+	    dsec = (stars[is].ra - stars[js].ra) * 3600.0;
+	    if (dsec > dmax)
+		break;
+	    if (is != js && stars[js].y != -999.0) {
+		rsec = wcsdist (stars[is].ra, stars[is].dec,
+				stars[js].ra, stars[js].dec);
+		if (rsec <= rad) {
+		    nthis = nthis + 1;
+		    ra = ra + stars[js].ra;
+		    dec = dec + stars[js].dec;
+		    }
+		}
+	    }
+
+	/* Compute mean position for this star */
+	rai = ra / (double) nthis;
+	deci = dec / (double) nthis;
+	/* if (nthis > 1)
+	    printf ("StarMerge: Merging %d stars at %d\n", nthis, is); */
+
+	/* Initialize output star parameters */
+	ra = 0.0;
+	dec = 0.0;
+	pra = 0.0;
+	pdec = 0.0;
+	for (im = 0; im < nm; im++)
+	    mag[im] = 0.0;
+	nthis = 0;
+	for (i = 0; i < MAXMERGE; i++)
+	    nums[i] = 0;
+
+	/* Find all stars within rad arcseconds of current mean position */
+	i = 0;
+	dmax = rad + 2.0;
+
+	/* Search forward (upward in RA) */
+	for (js = is; js < ns; js++) {
+	    dsec = (stars[js].ra - rai) * 3600.0;
+	    if (dsec > dmax)
+		break;
+	    if (stars[js].y != -999.0) {
+		rsec = wcsdist (rai, deci, stars[js].ra, stars[js].dec);
+		if (rsec <= rad) {
+		    nthis = nthis + 1;
+		    ra = ra + stars[js].ra;
+		    dec = dec + stars[js].dec;
+		    pra = pra + stars[js].pra;
+		    pdec = pdec + stars[js].pdec;
+		    for (im = 0; im < nm; im++)
+			mag[im] = mag[im] + stars[js].m[im];
+		    stars[js].y = -999.0;
+		    nums[i++] = js;
+		    }
+		}
+	    }
+
+	/* Search backward (downward in RA) */
+	for (js = is; js > -1; js--) {
+	    dsec = (rai - stars[js].ra) * 3600.0;
+	    if (dsec > dmax)
+		break;
+	    if (stars[js].y != -999.0) {
+		rsec = wcsdist (rai, deci, stars[js].ra, stars[js].dec);
+		if (rsec <= rad) {
+		    nthis = nthis + 1;
+		    ra = ra + stars[js].ra;
+		    dec = dec + stars[js].dec;
+		    pra = pra + stars[js].pra;
+		    pdec = pdec + stars[js].pdec;
+		    for (im = 0; im < nm; im++)
+			mag[im] = mag[im] + stars[js].m[im];
+		    stars[js].y = -999.0;
+		    nums[i++] = js;
+		    }
+		}
+	    }
+	if (nthis > 0) {
+	    dn = (double) nthis;
+	    newstars[no].ra = ra / dn;
+	    newstars[no].dec = dec / dn;
+	    newstars[no].pra = pra / dn;
+	    newstars[no].pdec = pdec / dn;
+	    for (im = 0; im < nm; im++)
+		newstars[no].m[im] = mag[im] / dn;
+	    newstars[no].x = dn;
+	    newstars[no].y = dn;
+	    no = no + 1;
+	    }
+
+	if (logmerge && no%logmerge == 0)
+	    fprintf (stderr, "Merged %6d from %6d stars\r", no, is);
+	}
+
+    /* Free input star entries */
+    free (stars);
+    fprintf (stderr, "\n");
+
+    /* Reset input pointer to merged entries */
+    *stars0 = newstars;
+    return (no);
+}
+
+/* Jun 13 1996	New program
+ * Oct 18 1996	Add sorting by X value
+ * Nov 13 1996	Add second magnitude
+ * Jan 10 1997	Fix bug in RASortStars to return correct red magnitude
+ *
+ * Mar  2 1998	Make number and second magnitude optional
+ * Oct 21 1998	Add RefCat() to set reference catalog code
+ * Oct 26 1998	Include object names in star catalog entry structure
+ * Oct 29 1998	Return coordinate system and title from RefCat
+ * Nov 20 1998	Add USNO A-2.0 catalog and return different code
+ * Dec  9 1998	Add Hipparcos and Tycho catalogs
+ *
+ * Jan 26 1999	Add subroutines to deal with ranges of numbers
+ * Feb  8 1999	Fix bug initializing ACT catalog
+ * Feb 11 1999	Change starcat.insys to starcat.coorsys
+ * May 19 1999	Move catalog subroutines to catutil()
+ * Aug 26 1999	Compare pointers to NULL, not 0
+ *
+ * Mar 14 2000	Add proper motions
+ *
+ * May 22 2001	Add sort by declination
+ * Jun 28 2001	In MagSort, if b mag is 99.9, try r mag
+ * Jul 20 2001	In MagSort, allow for absence of ra and dec
+ * Sep 12 2001	Allow up to 11 magnitudes; add nm and magsort
+ * Sep 13 2001	Add YSortStars() to sort by Y coordinate
+ * Sep 18 2001	Subtract 100 in MagSort if magnitude is greater than 100
+ * Nov  6 2001	Allow missing x and y in MagSort, RASort, and DecSort
+ * Nov  6 2001	Allow missing ra and dec in XSort and YSort
+ *
+ * Apr  8 2002	Drop static subroutine declarations
+ *
+ * Sep 23 2003	Add MergeStars() and StarMerge() to merge input catalog
+ *
+ * Mar  4 2004	Fix rad in MergeStars()
+ * Mar 17 2004	RA-sort before and after merging catalog in MergeStars()
+ * Mar 17 2004	Rewrite StarMerge() to merge RA-sorted catalog quickly
+ * Mar 18 2004	Add logging to StarMerge()
+ * Aug 30 2004	Fix bad declaration
+ *
+ * Apr 13 2006	Add sort by ID number
+ *
+ * Jan 11 2007	Include fitsfile.h
+ *
+ * Nov  6 2009	Set number of magnitudes from MAXNMAG parameter in wcscat.h
+ */
diff --git a/Code/src/libwcs/sph.c b/Code/src/libwcs/sph.c
new file mode 100644
index 0000000000000000000000000000000000000000..b8ba23d31804dfac654c9ed2b3a4b5633dd67c9d
--- /dev/null
+++ b/Code/src/libwcs/sph.c
@@ -0,0 +1,234 @@
+/*============================================================================
+*
+*   WCSLIB - an implementation of the FITS WCS proposal.
+*   Copyright (C) 1995-2002, Mark Calabretta
+*
+*   This library is free software; you can redistribute it and/or
+*   modify it under the terms of the GNU Lesser General Public
+*   License as published by the Free Software Foundation; either
+*   version 2 of the License, or (at your option) any later version.
+*
+*   This library is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+*   Lesser General Public License for more details.
+*   
+*   You should have received a copy of the GNU Lesser General Public
+*   License along with this library; if not, write to the Free Software
+*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*   Correspondence concerning WCSLIB may be directed to:
+*      Internet email: mcalabre@atnf.csiro.au
+*      Postal address: Dr. Mark Calabretta,
+*                      Australia Telescope National Facility,
+*                      P.O. Box 76,
+*                      Epping, NSW, 2121,
+*                      AUSTRALIA
+*
+*=============================================================================
+*
+*   C routines for the spherical coordinate transformations used by the FITS
+*   "World Coordinate System" (WCS) convention.
+*
+*   Summary of routines
+*   -------------------
+*   The spherical coordinate transformations are implemented via separate
+*   functions for the transformation in each direction.
+*
+*   Forward transformation; sphfwd()
+*   --------------------------------
+*   Transform celestial coordinates to the native coordinates of a projection.
+*
+*   Given:
+*      lng,lat  double   Celestial longitude and latitude, in degrees.
+*      eul[5]   double   Euler angles for the transformation:
+*                          0: Celestial longitude of the native pole, in
+*                             degrees.
+*                          1: Celestial colatitude of the native pole, or
+*                             native colatitude of the celestial pole, in
+*                             degrees.
+*                          2: Native longitude of the celestial pole, in
+*                             degrees.
+*                          3: cos(eul[1])
+*                          4: sin(eul[1])
+*
+*   Returned:
+*      phi,     double   Longitude and latitude in the native coordinate
+*      theta             system of the projection, in degrees.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*
+*   Reverse transformation; sphrev()
+*   --------------------------------
+*   Transform native coordinates of a projection to celestial coordinates.
+*
+*   Given:
+*      phi,     double   Longitude and latitude in the native coordinate
+*      theta             system of the projection, in degrees.
+*      eul[5]   double   Euler angles for the transformation:
+*                          0: Celestial longitude of the native pole, in
+*                             degrees.
+*                          1: Celestial colatitude of the native pole, or
+*                             native colatitude of the celestial pole, in
+*                             degrees.
+*                          2: Native longitude of the celestial pole, in
+*                             degrees.
+*                          3: cos(eul[1])
+*                          4: sin(eul[1])
+*
+*   Returned:
+*      lng,lat  double   Celestial longitude and latitude, in degrees.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*
+*   Author: Mark Calabretta, Australia Telescope National Facility
+*   $Id: sph.c,v 2.7 2002/04/03 01:25:29 mcalabre Exp $
+*===========================================================================*/
+
+#include <math.h>
+#include "wcslib.h"
+
+#ifndef __STDC__
+#ifndef const
+#define const
+#endif
+#endif
+
+const double tol = 1.0e-5;
+
+int sphfwd (lng, lat, eul, phi, theta)
+
+const double lat, lng, eul[5];
+double *phi, *theta;
+
+{
+   double coslat, coslng, dlng, dphi, sinlat, sinlng, x, y, z;
+
+   coslat = cosdeg (lat);
+   sinlat = sindeg (lat);
+
+   dlng = lng - eul[0];
+   coslng = cosdeg (dlng);
+   sinlng = sindeg (dlng);
+
+   /* Compute the native longitude. */
+   x = sinlat*eul[4] - coslat*eul[3]*coslng;
+   if (fabs(x) < tol) {
+      /* Rearrange formula to reduce roundoff errors. */
+      x = -cosdeg (lat+eul[1]) + coslat*eul[3]*(1.0 - coslng);
+   }
+   y = -coslat*sinlng;
+   if (x != 0.0 || y != 0.0) {
+      dphi = atan2deg (y, x);
+   } else {
+      /* Change of origin of longitude. */
+      dphi = dlng - 180.0;
+   }
+   *phi = eul[2] + dphi;
+
+   /* Normalize the native longitude. */
+   if (*phi > 180.0) {
+      *phi -= 360.0;
+   } else if (*phi < -180.0) {
+      *phi += 360.0;
+   }
+
+   /* Compute the native latitude. */
+   if (fmod(dlng,180.0) == 0.0) {
+      *theta = lat + coslng*eul[1];
+      if (*theta >  90.0) *theta =  180.0 - *theta;
+      if (*theta < -90.0) *theta = -180.0 - *theta;
+   } else {
+      z = sinlat*eul[3] + coslat*eul[4]*coslng;
+      /* Use an alternative formula for greater numerical accuracy. */
+      if (fabs(z) > 0.99) {
+	if (z < 0)
+           *theta = -acosdeg (sqrt(x*x+y*y));
+	else
+           *theta =  acosdeg (sqrt(x*x+y*y));
+      } else {
+         *theta = asindeg (z);
+      }
+   }
+
+   return 0;
+}
+
+/*-----------------------------------------------------------------------*/
+
+int sphrev (phi, theta, eul, lng, lat)
+
+const double phi, theta, eul[5];
+double *lng, *lat;
+
+{
+   double cosphi, costhe, dlng, dphi, sinphi, sinthe, x, y, z;
+
+   costhe = cosdeg (theta);
+   sinthe = sindeg (theta);
+
+   dphi = phi - eul[2];
+   cosphi = cosdeg (dphi);
+   sinphi = sindeg (dphi);
+
+   /* Compute the celestial longitude. */
+   x = sinthe*eul[4] - costhe*eul[3]*cosphi;
+   if (fabs(x) < tol) {
+      /* Rearrange formula to reduce roundoff errors. */
+      x = -cosdeg (theta+eul[1]) + costhe*eul[3]*(1.0 - cosphi);
+   }
+   y = -costhe*sinphi;
+   if (x != 0.0 || y != 0.0) {
+      dlng = atan2deg (y, x);
+   } else {
+      /* Change of origin of longitude. */
+      dlng = dphi + 180.0;
+   }
+   *lng = eul[0] + dlng;
+
+   /* Normalize the celestial longitude. */
+   if (eul[0] >= 0.0) {
+      if (*lng < 0.0) *lng += 360.0;
+   } else {
+      if (*lng > 0.0) *lng -= 360.0;
+   }
+
+   if (*lng > 360.0) {
+      *lng -= 360.0;
+   } else if (*lng < -360.0) {
+      *lng += 360.0;
+   }
+
+   /* Compute the celestial latitude. */
+   if (fmod(dphi,180.0) == 0.0) {
+      *lat = theta + cosphi*eul[1];
+      if (*lat >  90.0) *lat =  180.0 - *lat;
+      if (*lat < -90.0) *lat = -180.0 - *lat;
+   } else {
+      z = sinthe*eul[3] + costhe*eul[4]*cosphi;
+
+      /* Use an alternative formula for greater numerical accuracy. */
+      if (fabs(z) > 0.99) {
+	 if (z < 0)
+            *lat = -acosdeg (sqrt(x*x+y*y));
+	 else
+            *lat =  acosdeg (sqrt(x*x+y*y));
+      } else {
+         *lat = asindeg (z);
+      }
+   }
+
+   return 0;
+}
+/* Dec 20 1999	Doug Mink - Change cosd() and sind() to cosdeg() and sindeg()
+ * Dec 20 1999	Doug Mink - Include wcslib.h, which includes wcstrig.h, sph.h
+ * Dec 20 1999	Doug Mink - Define copysign only if it is not already defined
+ *
+ * Jan  5 2000	Doug Mink - Drop copysign
+ *
+ * Sep 19 2001	Doug Mink - No change for WCSLIB 2.7
+ */
diff --git a/Code/src/libwcs/str2ang.c b/Code/src/libwcs/str2ang.c
new file mode 100644
index 0000000000000000000000000000000000000000..ee1ad720c301803e50ea43d7cfb85697b934f39b
--- /dev/null
+++ b/Code/src/libwcs/str2ang.c
@@ -0,0 +1,104 @@
+
+
+/* Return the right ascension in degrees from sexagesimal hours or decimal degrees */
+
+double
+str2ra (in)
+
+const char *in;	/* Character string of sexigesimal hours or decimal degrees */
+
+{
+    double ra;	/* Right ascension in degrees (returned) */
+
+    ra = str2dec (in);
+    if (strsrch (in,":"))
+	ra = ra * 15.0;
+
+    return (ra);
+}
+
+
+/* Return the declination in degrees from sexagesimal or decimal degrees */
+
+double
+str2dec (in)
+
+const char *in;	/* Character string of sexigesimal or decimal degrees */
+
+{
+    double dec;		/* Declination in degrees (returned) */
+    double deg, min, sec, sign;
+    char *value, *c1, *c2;
+    int lval;
+    char *dchar;
+
+    dec = 0.0;
+
+    /* Return 0.0 if string is null */
+    if (in == NULL)
+	return (dec);
+
+    /* Translate value from ASCII colon-delimited string to binary */
+    if (in[0]) {
+	value = (char *) in;
+
+	/* Remove leading spaces */
+	while (*value == ' ')
+	    value++;
+
+	/* Save sign */
+	if (*value == '-') {
+	    sign = -1.0;
+	    value++;
+	    }
+	else if (*value == '+') {
+	    sign = 1.0;
+	    value++;
+	    }
+	else
+	    sign = 1.0;
+
+	/* Remove trailing spaces */
+	lval = strlen (value);
+	while (value[lval-1] == ' ')
+	    lval--;
+	
+	if ((c1 = strsrch (value,":")) == NULL)
+	    c1 = strnsrch (value," ",lval);
+	if (c1 != NULL) {
+	    *c1 = 0;
+	    deg = (double) atoi (value);
+	    *c1 = ':';
+	    value = c1 + 1;
+	    if ((c2 = strsrch (value,":")) == NULL)
+		c2 = strsrch (value," ");
+	    if (c2 != NULL) {
+		*c2 = 0;
+		min = (double) atoi (value);
+		*c2 = ':';
+		value = c2 + 1;
+		sec = atof (value);
+		}
+	    else {
+		sec = 0.0;
+		if ((c1 = strsrch (value,".")) != NULL)
+		    min = atof (value);
+		if (strlen (value) > 0)
+		    min = (double) atoi (value);
+		}
+	    dec = sign * (deg + (min / 60.0) + (sec / 3600.0));
+	    }
+	else if (isnum (value) == 2) {
+	    if ((dchar = strchr (value, 'D')))
+		*dchar = 'e';
+	    if ((dchar = strchr (value, 'd')))
+		*dchar = 'e';
+	    if ((dchar = strchr (value, 'E')))
+		*dchar = 'e';
+	    dec = sign * atof (value);
+	    }
+	else 
+	    dec = sign * (double) atoi (value);
+	}
+    return (dec);
+}
diff --git a/Code/src/libwcs/str2dcpp.c b/Code/src/libwcs/str2dcpp.c
new file mode 100644
index 0000000000000000000000000000000000000000..7186a5841b516ebfd4d74d76c6ddf3f457fed768
--- /dev/null
+++ b/Code/src/libwcs/str2dcpp.c
@@ -0,0 +1,158 @@
+//
+// strtod.c
+//
+// Convert string to double 
+//
+// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
+// http://www.jbox.dk/sanos/source/lib/strtod.c.html
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+// 
+// 1. Redistributions of source code must retain the above copyright 
+//    notice, this list of conditions and the following disclaimer.  
+// 2. Redistributions in binary form must reproduce the above copyright
+//    notice, this list of conditions and the following disclaimer in the
+//    documentation and/or other materials provided with the distribution.  
+// 3. Neither the name of the project nor the names of its contributors
+//    may be used to endorse or promote products derived from this software
+//    without specific prior written permission. 
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+// SUCH DAMAGE.
+// 
+
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+#include <float.h>
+#include <stdlib.h>
+
+double strtod(const char *str, char **endptr)
+{
+  double number;
+  int exponent;
+  int negative;
+  char *p = (char *) str;
+  double p10;
+  int n;
+  int num_digits;
+  int num_decimals;
+
+  // Skip leading whitespace
+  while (isspace(*p)) p++;
+
+  // Handle optional sign
+  negative = 0;
+  switch (*p) 
+  {             
+    case '-': negative = 1; // Fall through to increment position
+    case '+': p++;
+  }
+
+  number = 0.;
+  exponent = 0;
+  num_digits = 0;
+  num_decimals = 0;
+
+  // Process string of digits
+  while (isdigit(*p))
+  {
+    number = number * 10. + (*p - '0');
+    p++;
+    num_digits++;
+  }
+
+  // Process decimal part
+  if (*p == '.') 
+  {
+    p++;
+
+    while (isdigit(*p))
+    {
+      number = number * 10. + (*p - '0');
+      p++;
+      num_digits++;
+      num_decimals++;
+    }
+
+    exponent -= num_decimals;
+  }
+
+  if (num_digits == 0)
+  {
+    errno = ERANGE;
+    return 0.0;
+  }
+
+  // Correct for sign
+  if (negative) number = -number;
+
+  // Process an exponent string
+  if (*p == 'e' || *p == 'E') 
+  {
+    // Handle optional sign
+    negative = 0;
+    switch(*++p) 
+    {   
+      case '-': negative = 1;   // Fall through to increment pos
+      case '+': p++;
+    }
+
+    // Process string of digits
+    n = 0;
+    while (isdigit(*p)) 
+    {   
+      n = n * 10 + (*p - '0');
+      p++;
+    }
+
+    if (negative) 
+      exponent -= n;
+    else
+      exponent += n;
+  }
+
+  if (exponent < DBL_MIN_EXP  || exponent > DBL_MAX_EXP)
+  {
+    errno = ERANGE;
+    return HUGE_VAL;
+  }
+
+  // Scale the result
+  p10 = 10.;
+  n = exponent;
+  if (n < 0) n = -n;
+  while (n) 
+  {
+    if (n & 1) 
+    {
+      if (exponent < 0)
+        number /= p10;
+      else
+        number *= p10;
+    }
+    n >>= 1;
+    p10 *= p10;
+  }
+
+  if (number == HUGE_VAL) errno = ERANGE;
+  if (endptr) *endptr = p;
+
+  return number;
+}
+
+double atof(const char *str)
+{
+  return strtod(str, NULL);
+}
diff --git a/Code/src/libwcs/str2dsun.c b/Code/src/libwcs/str2dsun.c
new file mode 100644
index 0000000000000000000000000000000000000000..9774edcaa585fe7bf34a4362d14f371d75da2b04
--- /dev/null
+++ b/Code/src/libwcs/str2dsun.c
@@ -0,0 +1,255 @@
+/* 
+ * strtod.c --
+ *
+ *	Source code for the "strtod" library procedure.
+ *
+ * Copyright (c) 1988-1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ * https://svn.greensocs.com/public/packages/tcl/tcl8.5.0/compat/strtod.c
+ *
+ * RCS: @(#) $Id: strtod.c,v 1.8 2007/04/16 13:36:34 dkf Exp $
+ */
+
+#include "tclInt.h"
+#include <ctype.h>
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+#ifndef NULL
+#define NULL 0
+#endif
+
+static int maxExponent = 511;	/* Largest possible base 10 exponent.  Any
+				 * exponent larger than this will already
+				 * produce underflow or overflow, so there's
+				 * no need to worry about additional digits.
+				 */
+static double powersOf10[] = {	/* Table giving binary powers of 10.  Entry */
+    10.,			/* is 10^2^i.  Used to convert decimal */
+    100.,			/* exponents into floating-point numbers. */
+    1.0e4,
+    1.0e8,
+    1.0e16,
+    1.0e32,
+    1.0e64,
+    1.0e128,
+    1.0e256
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * strtod --
+ *
+ *	This procedure converts a floating-point number from an ASCII
+ *	decimal representation to internal double-precision format.
+ *
+ * Results:
+ *	The return value is the double-precision floating-point
+ *	representation of the characters in string.  If endPtr isn't
+ *	NULL, then *endPtr is filled in with the address of the
+ *	next character after the last one that was part of the
+ *	floating-point number.
+ *
+ * Side effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+double
+strtod(
+    CONST char *string,		/* A decimal ASCII floating-point number,
+				 * optionally preceded by white space. Must
+				 * have form "-I.FE-X", where I is the integer
+				 * part of the mantissa, F is the fractional
+				 * part of the mantissa, and X is the
+				 * exponent. Either of the signs may be "+",
+				 * "-", or omitted. Either I or F may be
+				 * omitted, or both. The decimal point isn't
+				 * necessary unless F is present. The "E" may
+				 * actually be an "e". E and X may both be
+				 * omitted (but not just one). */
+    char **endPtr)		/* If non-NULL, store terminating character's
+				 * address here. */
+{
+    int sign, expSign = FALSE;
+    double fraction, dblExp, *d;
+    register CONST char *p;
+    register int c;
+    int exp = 0;		/* Exponent read from "EX" field. */
+    int fracExp = 0;		/* Exponent that derives from the fractional
+				 * part. Under normal circumstatnces, it is
+				 * the negative of the number of digits in F.
+				 * However, if I is very long, the last digits
+				 * of I get dropped (otherwise a long I with a
+				 * large negative exponent could cause an
+				 * unnecessary overflow on I alone). In this
+				 * case, fracExp is incremented one for each
+				 * dropped digit. */
+    int mantSize;		/* Number of digits in mantissa. */
+    int decPt;			/* Number of mantissa digits BEFORE decimal
+				 * point. */
+    CONST char *pExp;		/* Temporarily holds location of exponent in
+				 * string. */
+
+    /*
+     * Strip off leading blanks and check for a sign.
+     */
+
+    p = string;
+    while (isspace(UCHAR(*p))) {
+	p += 1;
+    }
+    if (*p == '-') {
+	sign = TRUE;
+	p += 1;
+    } else {
+	if (*p == '+') {
+	    p += 1;
+	}
+	sign = FALSE;
+    }
+
+    /*
+     * Count the number of digits in the mantissa (including the decimal
+     * point), and also locate the decimal point.
+     */
+
+    decPt = -1;
+    for (mantSize = 0; ; mantSize += 1)
+    {
+	c = *p;
+	if (!isdigit(c)) {
+	    if ((c != '.') || (decPt >= 0)) {
+		break;
+	    }
+	    decPt = mantSize;
+	}
+	p += 1;
+    }
+
+    /*
+     * Now suck up the digits in the mantissa. Use two integers to collect 9
+     * digits each (this is faster than using floating-point). If the mantissa
+     * has more than 18 digits, ignore the extras, since they can't affect the
+     * value anyway.
+     */
+    
+    pExp  = p;
+    p -= mantSize;
+    if (decPt < 0) {
+	decPt = mantSize;
+    } else {
+	mantSize -= 1;		/* One of the digits was the point. */
+    }
+    if (mantSize > 18) {
+	fracExp = decPt - 18;
+	mantSize = 18;
+    } else {
+	fracExp = decPt - mantSize;
+    }
+    if (mantSize == 0) {
+	fraction = 0.0;
+	p = string;
+	goto done;
+    } else {
+	int frac1, frac2;
+
+	frac1 = 0;
+	for ( ; mantSize > 9; mantSize -= 1) {
+	    c = *p;
+	    p += 1;
+	    if (c == '.') {
+		c = *p;
+		p += 1;
+	    }
+	    frac1 = 10*frac1 + (c - '0');
+	}
+	frac2 = 0;
+	for (; mantSize > 0; mantSize -= 1) {
+	    c = *p;
+	    p += 1;
+	    if (c == '.') {
+		c = *p;
+		p += 1;
+	    }
+	    frac2 = 10*frac2 + (c - '0');
+	}
+	fraction = (1.0e9 * frac1) + frac2;
+    }
+
+    /*
+     * Skim off the exponent.
+     */
+
+    p = pExp;
+    if ((*p == 'E') || (*p == 'e')) {
+	p += 1;
+	if (*p == '-') {
+	    expSign = TRUE;
+	    p += 1;
+	} else {
+	    if (*p == '+') {
+		p += 1;
+	    }
+	    expSign = FALSE;
+	}
+	if (!isdigit(UCHAR(*p))) {
+	    p = pExp;
+	    goto done;
+	}
+	while (isdigit(UCHAR(*p))) {
+	    exp = exp * 10 + (*p - '0');
+	    p += 1;
+	}
+    }
+    if (expSign) {
+	exp = fracExp - exp;
+    } else {
+	exp = fracExp + exp;
+    }
+
+    /*
+     * Generate a floating-point number that represents the exponent. Do this
+     * by processing the exponent one bit at a time to combine many powers of
+     * 2 of 10. Then combine the exponent with the fraction.
+     */
+    
+    if (exp < 0) {
+	expSign = TRUE;
+	exp = -exp;
+    } else {
+	expSign = FALSE;
+    }
+    if (exp > maxExponent) {
+	exp = maxExponent;
+	errno = ERANGE;
+    }
+    dblExp = 1.0;
+    for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
+	if (exp & 01) {
+	    dblExp *= *d;
+	}
+    }
+    if (expSign) {
+	fraction /= dblExp;
+    } else {
+	fraction *= dblExp;
+    }
+
+  done:
+    if (endPtr != NULL) {
+	*endPtr = (char *) p;
+    }
+
+    if (sign) {
+	return -fraction;
+    }
+    return fraction;
+}
diff --git a/Code/src/libwcs/tabread.c b/Code/src/libwcs/tabread.c
new file mode 100644
index 0000000000000000000000000000000000000000..846a193905dff5dc4d03c128baeb82d60bd451bb
--- /dev/null
+++ b/Code/src/libwcs/tabread.c
@@ -0,0 +1,2903 @@
+/*** File libwcs/tabread.c
+ *** September 30, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+	   Internet email: dmink@cfa.harvard.edu
+	   Postal address: Doug Mink
+	                   Smithsonian Astrophysical Observatory
+	                   60 Garden St.
+	                   Cambridge, MA 02138 USA
+ */
+
+/* int tabread()	Read tab table stars in specified region
+ * int tabrnum()	Read tab table stars with specified numbers
+ * int tabbin ()	Bin tab table stars in specified region into an image
+ * int tabxyread()	Read x, y, and magnitude from tab table star list
+ * int tabrkey()	Read single keyword from specified tab table stars
+ * struct StarCat tabcatopen()	Open tab table catalog, return number of entries
+ * struct TabTable *tabopen()	Open tab table, returning number of entries
+ * char *tabline()	Get tab table entry for one line
+ * double tabgetra()	Return double right ascension in degrees
+ * double tabgetdec()	Return double declination in degrees
+ * double tabgetr8()	Return 8-byte floating point number from tab table line
+ * int tabgeti4()	Return 4-byte integer from tab table line
+ * int tabgetk()	Return character entry from tab table line for column
+ * int tabgetc()	Return n'th character entry from tab table line
+ * int tabhgetr8()	Return 8-byte floating point keyword value from header
+ * int tabhgeti4()	Return 4-byte integer keyword value from header
+ * int tabhgetc()	Return character keyword value from header
+ * int tabparse()	Make a table of column headings
+ * int tabcol()		Find entry in a table of column headings (case-dependent)
+ * int tabccol()	Find entry in a table of column headings (case-independent)
+ * int tabsize()	Return length of file in bytes
+ * int istab()		Return 1 if first line of file contains a tab, else 0
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <sys/types.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+
+static int tabhgetr8();
+static int tabhgeti4();
+static int tabhgetc();
+static int tabcont();
+static int tabccont();
+static int tabsize();
+static int nndec = 0;
+static int verbose = 0;
+static char *taberr;
+static struct Tokens startok;
+
+char *gettaberr ()
+{ return (taberr); }
+
+int gettabndec()
+{ return (nndec); }
+
+static char *kwo = NULL;	/* Keyword returned by tabread(), tabrnum() */
+void settabkey (keyword0)
+char *keyword0;
+{ kwo = keyword0; return; }
+
+
+/* TABREAD -- Read tab table stars in specified region */
+
+int
+tabread (tabcatname,distsort,cra,cdec,dra,ddec,drad,dradi,
+	 sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,starcat,
+	 tnum,tra,tdec,tpra,tpdec,tmag,tpeak,tkey,nlog)
+
+char	*tabcatname;	/* Name of reference star catalog file */
+int	distsort;	/* 1 to sort stars by distance from center */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+struct StarCat **starcat; /* Star catalog data structure */
+double	*tnum;		/* Array of UJ numbers (returned) */
+double	*tra;		/* Array of right ascensions (returned) */
+double	*tdec;		/* Array of declinations (returned) */
+double	*tpra;		/* Array of right ascension proper motions (returned) */
+double	*tpdec;		/* Array of declination proper motions (returned) */
+double	**tmag;		/* 2-D Array of magnitudes (returned) */
+int	*tpeak;		/* Array of peak counts (returned) */
+char	**tkey;		/* Array of values of additional keyword */
+int	nlog;
+{
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    double rra1,rra2;	/* Catalog coordinate limiting right ascension (deg) */
+    double rdec1,rdec2;	/* Catalog coordinate limiting declination (deg) */
+    double dist = 0.0;  /* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    int faintstar=0;    /* Faintest star */
+    int farstar=0;      /* Most distant star */
+    double *tdist;      /* Array of distances to stars */
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog equinox */
+    double epref;	/* Catalog epoch */
+    double secmarg = 0.0; /* Arcsec/century margin for proper motion */
+    double magt;
+    double rdist, ddist;
+    int pass;
+    char cstr[32];
+    struct Star *star;
+    struct StarCat *sc;	/* Star catalog data structure */
+
+    int wrap;
+    int jstar;
+    int magsort;
+    int nstar;
+    char *objname;
+    int lname;
+    int imag;
+    double ra,dec, rapm, decpm;
+    double mag, parallax, rv;
+    double num;
+    int peak, i;
+    int istar, nstars, lstar;
+
+    sc = *starcat;
+
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* mag1 is always the smallest magnitude limit */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Logging interval */
+    nstar = 0;
+    tdist = (double *) calloc (nstarmax, sizeof (double));
+
+    lstar = sizeof (struct Star);
+    star = (struct Star *) calloc (1, lstar);
+    if (sc == NULL)
+	sc = tabcatopen (tabcatname, NULL, 0);
+    *starcat = sc;
+    if (sc == NULL || sc->nstars <= 0) {
+	if (taberr != NULL)
+	    fprintf (stderr,"%s\n", taberr);
+	fprintf (stderr,"TABREAD: Cannot read catalog %s\n", tabcatname);
+	free (star);
+	sc = NULL;
+	return (0);
+	}
+
+    nstars = sc->nstars;
+    jstar = 0;
+
+    if (sortmag > 0 && sortmag <= sc->nmag)
+	magsort = sortmag - 1;
+    else 
+	magsort = 0;
+
+    /* Set catalog coordinate system */
+    if (sc->equinox != 0.0)
+	eqref = sc->equinox;
+    else
+	eqref = eqout;
+    if (sc->epoch != 0.0)
+	epref = sc->epoch;
+    else
+	epref = epout;
+    if (sc->coorsys)
+	sysref = sc->coorsys;
+    else
+	sysref = sysout;
+    wcscstr (cstr, sysout, eqout, epout);
+
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    if (sc->mprop > 0)
+	secmarg = 60.0;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    /* Loop through catalog */
+    for (istar = 1; istar <= nstars; istar++) {
+
+	/* Read position of next star */
+	if (tabstar (istar, sc, star, verbose)) {
+	    if (verbose)
+		fprintf (stderr,"TABREAD: Cannot read star %d\n", istar);
+	    break;
+	    }
+
+	/* Set magnitude to test */
+	if (sc->nmag > 0) {
+	    magt = star->xmag[magsort];
+	    if (sortmag < 1) {
+		imag = 0;
+		while (magt == 99.90 && imag < sc->nmag)
+		    magt = star->xmag[imag++];
+		if (magt > 100.0)
+		    magt = magt - 100.0;
+		}
+	    }
+	else
+	    magt = mag1;
+
+	/* Check magnitude limits */
+	pass = 1;
+	if (mag1 != mag2 && (magt < mag1 || magt > mag2))
+	    pass = 0;
+
+	/* Check rough position limits */
+	ra = star->ra;
+	dec = star->dec;
+	if  ((!wrap && (ra < rra1 || ra > rra2)) ||
+	    (wrap && (ra < rra1 && ra > rra2)) ||
+	    dec < rdec1 || dec > rdec2)
+	    pass = 0;
+
+	/* Convert coordinate system for this star and test it*/
+	if (pass) {
+	    sysref = star->coorsys;
+	    eqref = star->equinox;
+	    epref = star->epoch;
+
+	    /* Extract selected fields  */
+	    num = star->num;
+	    rapm = star->rapm;
+	    decpm = star->decpm;
+	    parallax = star->parallax;
+	    rv = star->radvel;
+
+	    /* Convert from catalog to search coordinate system */
+	    if (sc->entpx || sc->entrv)
+		wcsconv (sysref, sysout, eqref, eqout, epref, epout,
+		     &ra, &dec, &rapm, &decpm, &parallax, &rv);
+	    else if (sc->mprop == 1)
+		wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		     &ra, &dec, &rapm, &decpm);
+	    else
+		wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+	    if (sc->sptype)
+		peak = (1000 * (int) star->isp[0]) + (int)star->isp[1];
+	    else
+		peak = star->peak;
+
+	    /* Compute distance from search center */
+	    if (drad > 0 || distsort)
+		dist = wcsdist (cra,cdec,ra,dec);
+	    else
+		dist = 0.0;
+
+	    /* Check radial distance to search center */
+	    if (drad > 0) {
+		if (dist > drad)
+		    pass = 0;
+		if (dradi > 0.0 && dist < dradi)
+		    pass = 0;
+		}
+
+	    /* Check distance along RA and Dec axes */
+	    else {
+		ddist = wcsdist (cra,cdec,cra,dec);
+		if (ddist > ddec)
+		    pass = 0;
+		rdist = wcsdist (cra,dec,ra,dec);
+		if (rdist > dra)
+		   pass = 0;
+		}
+	    }
+
+	/* Add this star's information to the list if still OK */
+	if (pass) {
+
+	    /* Save star position and magnitude in table */
+	    if (nstar < nstarmax) {
+		tnum[nstar] = num;
+		tra[nstar] = ra;
+		tdec[nstar] = dec;
+		if (sc->mprop == 1) {
+		    tpra[nstar] = rapm;
+		    tpdec[nstar] = decpm;
+		    }
+		for (imag = 0; imag < sc->nmag; imag++) {
+		    if (tmag[imag] != NULL)
+			tmag[imag][nstar] = star->xmag[imag];
+		    }
+		if (tpeak)
+		    tpeak[nstar] = peak;
+		tdist[nstar] = dist;
+		lname = strlen (star->objname);
+		if (lname > 0) {
+		    objname = (char *)calloc (lname+1, 1);
+		    strcpy (objname, star->objname);
+		    if (tkey[nstar]) free(tkey[nstar]);
+		    tkey[nstar] = objname;
+		    }
+		if (dist > maxdist) {
+		    maxdist = dist;
+		    farstar = nstar;
+		    }
+		if (sc->nmag > 0 && magt > faintmag) {
+		    faintmag = magt;
+		    faintstar = nstar;
+		    }
+		}
+
+	    /* If radial search & too many stars, replace furthest star */
+	    else if (distsort) {
+		if (dist < maxdist) {
+		    tnum[farstar] = num;
+		    tra[farstar] = ra;
+		    tdec[farstar] = dec;
+		    if (sc->mprop == 1) {
+			tpra[farstar] = rapm;
+			tpdec[farstar] = decpm;
+			}
+		    for (imag = 0; imag < sc->nmag; imag++) {
+			if (tmag[imag] != NULL)
+			    tmag[imag][farstar] = star->xmag[imag];
+			}
+		    tpeak[farstar] = peak;
+		    tdist[farstar] = dist;
+		    lname = strlen (star->objname);
+		    if (lname > 0) {
+			objname = (char *)calloc (lname+1, 1);
+			strcpy (objname, star->objname);
+			if (tkey[farstar]) free(tkey[farstar]);
+			tkey[farstar] = objname;
+			}
+
+		    /* Find new farthest star */
+		    maxdist = 0.0;
+		    for (i = 0; i < nstarmax; i++) {
+			if (tdist[i] > maxdist) {
+			    maxdist = tdist[i];
+			    farstar = i;
+			    }
+			}
+		    }
+		}
+
+	    /* Otherwise if too many stars, replace faintest star */
+	    else if (sc->nmag > 0 && magt < faintmag) {
+		tnum[faintstar] = num;
+		tra[faintstar] = ra;
+		tdec[faintstar] = dec;
+		if (sc->mprop == 1) {
+		    tpra[faintstar] = rapm;
+		    tpdec[faintstar] = decpm;
+		    }
+		for (imag = 0; imag < sc->nmag; imag++) {
+		    if (tmag[imag] != NULL)
+			tmag[imag][faintstar] = star->xmag[imag];
+		    }
+		tpeak[faintstar] = peak;
+		tdist[faintstar] = dist;
+		lname = strlen (star->objname);
+		if (lname > 0) {
+		    objname = (char *)calloc (lname+1, 1);
+		    strcpy (objname, star->objname);
+		    if (tkey[faintstar]) free(tkey[faintstar]);
+		    tkey[faintstar] = objname;
+		    }
+		faintmag = 0.0;
+
+		/* Find new faintest star */
+		for (i = 0; i < nstarmax; i++) {
+		    magt = tmag[magsort][i];
+		    imag = 0;
+		    while (magt == 99.90 && imag < sc->nmag)
+			magt = tmag[imag++][i];
+		    if (magt > 100.0)
+			magt = magt - 100.0;
+		    if (magt > faintmag) {
+			faintmag = magt;
+			faintstar = i;
+			}
+		    }
+		}
+		
+	    nstar++;
+	    jstar++;
+	    if (nlog == 1)
+		fprintf (stderr,"TABREAD: %11.6f: %9.5f %9.5f %s %5.2f %d    \n",
+			 num,ra,dec,cstr,magt,peak);
+
+	    /* End of accepted star processing */
+	    }
+
+	/* Log operation */
+	if (nlog > 0 && istar%nlog == 0)
+		fprintf (stderr,"TABREAD: %5d / %5d / %5d sources catalog %s\r",
+			jstar,istar,nstars,tabcatname);
+
+	/* End of star loop */
+	}
+
+    /* Summarize search */
+    if (nlog > 0) {
+	fprintf (stderr,"TABREAD: Catalog %s : %d / %d / %d found\n",tabcatname,
+		 jstar,istar,nstars);
+	if (nstar > nstarmax)
+	    fprintf (stderr,"TABREAD: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+
+    free ((char *) tdist);
+    free ((char *) star);
+    return (nstar);
+}
+
+
+/* TABRNUM -- Read tab table stars with specified numbers */
+
+int
+tabrnum (tabcatname, nnum, sysout, eqout, epout, starcat, match,
+	 tnum,tra,tdec,tpra,tpdec,tmag,tpeak,tkey,nlog)
+
+char	*tabcatname;	/* Name of reference star catalog file */
+int	nnum;		/* Number of stars to look for */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+struct StarCat **starcat; /* Star catalog data structure */
+int	match;		/* 1 to match star number exactly, else sequence num.*/
+double	*tnum;		/* Array of star numbers to look for */
+double	*tra;		/* Array of right ascensions (returned) */
+double	*tdec;		/* Array of declinations (returned) */
+double	*tpra;		/* Array of right ascension proper motions (returned) */
+double	*tpdec;		/* Array of declination proper motions (returned) */
+double	**tmag;		/* 2-D array of magnitudes (returned) */
+int	*tpeak;		/* Array of peak counts (returned) */
+char	**tkey;		/* Array of additional keyword values */
+int	nlog;
+{
+    int jnum;
+    int nstar;
+    double ra,dec, rapm, decpm;
+    double mag, parallax, rv;
+    double num;
+    int peak;
+    int istar, istar0, nstars;
+    int imag;
+    char *line;
+    char numstr[32];	/* Catalog number */
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog equinox */
+    double epref;	/* Catalog epoch */
+    char cstr[32];
+    char str[32];
+    char *objname;
+    char *datestring;
+    int lname, lstar;
+    int ireg, inum, nnfld, i;
+    char rastr[32], decstr[32];
+    struct TabTable *startab;
+    struct StarCat *sc;
+    struct Star *star;
+
+    line = NULL;
+    nnfld = 0;
+
+    nstar = 0;
+    nndec = 0;
+
+    /* Allocate catalog entry buffer */
+    lstar = sizeof (struct Star);
+    star = (struct Star *) calloc (1, lstar);
+
+    /* Open star catalog */
+    sc = *starcat;
+    if (sc == NULL)
+	sc = tabcatopen (tabcatname, NULL, 0);
+    *starcat = sc;
+    if (sc == NULL || sc->nstars <= 0) {
+	if (taberr != NULL)
+	    fprintf (stderr,"%s\n", taberr);
+	fprintf (stderr,"TABRNUM: Cannot read catalog %s\n", tabcatname);
+	free (star);
+	return (0);
+	}
+    startab = sc->startab;
+    nstars = sc->nstars;
+
+    /* Set catalog coordinate system */
+    if (sc->equinox != 0.0)
+	eqref = sc->equinox;
+    else
+	eqref = eqout;
+    if (sc->epoch != 0.0)
+	epref = sc->epoch;
+    else
+	epref = epout;
+    if (sc->coorsys)
+	sysref = sc->coorsys;
+    else
+	sysref = sysout;
+    wcscstr (cstr, sysout, eqout, epout);
+
+    /* Write header if printing star entries as found */
+    if (nlog < 0) {
+	char *revmessage;
+	revmessage = getrevmsg();
+	printf ("catalog	%s\n", tabcatname);
+	printf ("radecsys	%s\n", cstr);
+	printf ("equinox	%.3f\n", eqout);
+	printf ("epoch  	%.3f\n", epout);
+	printf ("program	scat %s\n", revmessage);
+	if (sc->nnfld > 0)
+	    nnfld = sc->nnfld;
+	else
+	    nnfld = CatNumLen (TABCAT, tnum[nnum-1], sc->nndec);
+	printf ("id   	ra          	dec        ");
+	for (i = 1; i < sc->nmag+1; i++) {
+	    if (i == sc->nmag && sc->entepoch)
+		printf ("	epoch  	");
+	    else
+		printf ("	%s", sc->keymag[i-1]);
+	    }
+	if (kwo != NULL)
+	    printf ("	%s\n", kwo);
+	else
+	    printf ("\n");
+	printf ("-----	------------	------------");
+	for (i = 1; i < sc->nmag+1; i++) {
+	    if (i == sc->nmag && sc->entepoch)
+		printf ("	--------");
+	    else
+		printf ("	-----");
+	    }
+	if (kwo != NULL)
+	    printf ("	---------\n");
+	else
+	    printf ("\n");
+	}
+
+    star->num = 0.0;
+    istar0 = 0;
+    num = 0.0;
+
+    /* Loop through star list */
+    line = startab->tabdata;
+    for (jnum = 0; jnum < nnum; jnum++) {
+
+	/* Read forward from the last star if possible */
+	inum = (int) (tnum[jnum] + 0.5);
+	if ((double)inum != tnum[jnum] || inum < istar0 || istar0 == 0) {
+	    istar0 = 1;
+	    }
+
+	/* Loop through catalog to star */
+	for (istar = istar0; istar <= nstars; istar++) {
+	    if (!match && istar == inum)
+		break;
+	    if (num < tnum[jnum]) {
+		if ((line = gettabline (startab, istar)) == NULL) {
+		    if (nlog)
+			fprintf (stderr,"TABRNUM: Cannot read star %d\n", istar);
+		    break;
+		    }
+		}
+
+	    /* Check ID number first */
+	    (void) setoken (&startok, line, "tab");
+	    if (!strcmp (sc->isfil,"gsc-server")) {
+   		if (tabgetc (&startok, sc->entid, str, 24))
+		    num = 0.0;
+		else {
+		    num = atof (str+3) * 0.00001;
+		    ireg = (int) num;
+		    inum = (int) (((num - (double)ireg) * 100000.0) + 0.5);
+		    num = (double) ireg + 0.0001 * (double) inum;
+		    }
+		}
+	    else
+		num = tabgetr8 (&startok,sc->entid);
+	    if (num == 0.0)
+		num = (double) istar;
+	    if (num > tnum[jnum]) {
+		break;
+		}
+	    if (num == tnum[jnum])
+		break;
+	    }
+
+	/* If star has been found in table, read rest of entry */
+	if ((match && num == tnum[jnum]) || (!match && inum == istar)) {
+	    istar0 = istar;
+	    sc->istar = startab->iline;
+	    if (tabstar (istar, sc, star, nlog))
+		fprintf (stderr,"TABRNUM: Cannot read star %d\n", istar);
+
+	    /* If star entry has been read successfully */
+	    else {
+
+		/* Set coordinate system for this star */
+		sysref = star->coorsys;
+		eqref = star->equinox;
+
+		/* Extract selected fields  */
+		num = star->num;
+		ra = star->ra;
+		dec = star->dec;
+		rapm = star->rapm;
+		decpm = star->decpm;
+		parallax = star->parallax;
+		rv = star->radvel;
+		if (sc->entrv > 0)
+		    star->xmag[sc->nmag-1] = rv;
+		
+		if (sc->entpx || sc->entrv)
+		    wcsconv (sysref, sysout, eqref, eqout, epref, epout,
+			     &ra, &dec, &rapm, &decpm, &parallax, &rv);
+		else if (sc->mprop == 1)
+		    wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+			     &ra, &dec, &rapm, &decpm);
+		else
+		    wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+		if (sc->nmag > 0)
+		    mag = star->xmag[0];
+		else
+		    mag = 99.99;
+		if (sc->sptype)
+		    peak = (1000 * (int) star->isp[0]) + (int)star->isp[1];
+		else
+		    peak = star->peak;
+		if (nlog < 0) {
+		    CatNum (TABCAT, -nnfld, sc->nndec, num, numstr);
+		    ra2str (rastr, 31, ra, 3);
+		    dec2str (decstr, 31, dec, 2);
+		    printf ("%s	%s	%s", numstr,rastr,decstr);
+		    for (imag = 0; imag < sc->nmag; imag++)
+			if (imag == sc->nmag-1 && sc->entepoch) {
+			    datestring = DateString (star->xmag[imag], 1);
+			    printf ("%s", datestring);
+			    free (datestring);
+			    }
+			else
+			    printf ("	%.2f", star->xmag[imag]);
+		    if (kwo != NULL)
+			printf ("	%s\n", star->objname);
+		    else
+			printf ("\n");
+		    continue;
+		    }
+
+		/* Save star position and magnitude in table */
+		tnum[jnum] = num;
+		tra[jnum] = ra;
+		tdec[jnum] = dec;
+		if (sc->mprop == 1) {
+		    tpra[jnum] = rapm;
+		    tpdec[jnum] = decpm;
+		    }
+		for (imag = 0; imag < sc->nmag; imag++) {
+		    if (tmag[imag] != NULL)
+			tmag[imag][jnum] = star->xmag[imag];
+		    }
+		tpeak[jnum] = peak;
+		lname = strlen (star->objname);
+		if (lname > 0) {
+		    objname = (char *)calloc (lname+1, 1);
+		    strcpy (objname, star->objname);
+		    if (tkey[jnum]) free(tkey[jnum]);
+		    tkey[jnum] = objname;
+		    }
+		nstar++;
+		if (nlog == 1)
+		    fprintf (stderr,"TABRNUM: %11.6f: %9.5f %9.5f %s %5.2f %d    \n",
+			     num,ra,dec,cstr,mag,peak);
+		/* End of accepted star processing */
+		}
+	    }
+	else {
+	    nstar++;
+	    istar0 = istar;
+	    if (nlog < 0) {
+		CatNum (TABCAT, -nnfld, sc->nndec, tnum[jnum], numstr);
+		ra = 0.0;
+		ra2str (rastr, 31, ra, 3);
+		dec = 0.0;
+		dec2str (decstr, 31, dec, 2);
+		printf ("%s	%s	%s", numstr,rastr,decstr);
+		for (imag = 0; imag < sc->nmag; imag++)
+		    printf ("	99.0");
+		printf ("\n");
+		}
+	    }
+
+	/* Log operation */
+	if (nlog > 0 && jnum%nlog == 0)
+	    fprintf (stderr,"TABRNUM: %5d / %5d / %5d sources catalog %s\r",
+		     nstar,jnum,nstars,tabcatname);
+
+	/* End of star loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"TABRNUM: Catalog %s : %d / %d found\n",
+		 tabcatname,nstar,nstars);
+
+    free ((char *) star);
+    return (nstar);
+}
+
+
+/* TABBIN -- Bin tab table stars in specified region into an image */
+
+int
+tabbin (tabcatname, wcs, header, image, mag1, mag2, sortmag, magscale, nlog)
+
+char	*tabcatname;	/* Name of reference star catalog file */
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    double rra1,rra2;	/* Catalog coordinate limiting right ascension (deg) */
+    double rdec1,rdec2;	/* Catalog coordinate limiting declination (deg) */
+    int sysref;		/* Catalog coordinate system */
+    double eqref;	/* Catalog equinox */
+    double epref;	/* Catalog epoch */
+    double secmarg = 0.0; /* Arcsec/century margin for proper motion */
+    double magt;
+    double rdist, ddist;
+    int ix, iy;
+    int pass;
+    char cstr[32];
+    struct Star *star;
+    struct StarCat *sc;	/* Star catalog data structure */
+
+    int wrap;
+    int jstar;
+    int magsort;
+    int nstar;
+    int imag;
+    double ra,dec, rapm, decpm;
+    double mag, parallax, rv;
+    double num;
+    int peak;
+    int istar, nstars, lstar;
+    double xpix, ypix, flux;
+    int offscl;
+    int bitpix, w, h;   /* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* mag1 is always the smallest magnitude limit */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Logging interval */
+    nstar = 0;
+
+    lstar = sizeof (struct Star);
+    star = (struct Star *) calloc (1, lstar);
+    sc = tabcatopen (tabcatname, NULL, 0);
+    if (sc == NULL || sc->nstars <= 0) {
+	if (taberr != NULL)
+	    fprintf (stderr,"%s\n", taberr);
+	fprintf (stderr,"TABBIN: Cannot read catalog %s\n", tabcatname);
+	free (star);
+	sc = NULL;
+	return (0);
+	}
+
+    nstars = sc->nstars;
+    jstar = 0;
+
+    if (sortmag > 0 && sortmag <= sc->nmag)
+	magsort = sortmag - 1;
+    else 
+	magsort = 0;
+
+    /* Set catalog coordinate system */
+    if (sc->equinox != 0.0)
+	eqref = sc->equinox;
+    else
+	eqref = eqout;
+    if (sc->epoch != 0.0)
+	epref = sc->epoch;
+    else
+	epref = epout;
+    if (sc->coorsys)
+	sysref = sc->coorsys;
+    else
+	sysref = sysout;
+    wcscstr (cstr, sysout, eqout, epout);
+
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    if (sc->mprop > 0)
+	secmarg = 60.0;
+    RefLim(cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    /* If RA range includes zero, split it in two */
+    if (rra1 > rra2)
+	wrap = 1;
+    else
+	wrap = 0;
+
+    /* Loop through catalog */
+    for (istar = 1; istar <= nstars; istar++) {
+
+	/* Read position of next star */
+	if (tabstar (istar, sc, star, verbose)) {
+	    if (verbose)
+		fprintf (stderr,"TABBIN: Cannot read star %d\n", istar);
+	    break;
+	    }
+
+	/* Set magnitude to test */
+	if (sc->nmag > 0) {
+	    magt = star->xmag[magsort];
+	    imag = 0;
+	    while (magt == 99.90 && imag < sc->nmag)
+		magt = star->xmag[imag++];
+	    if (magt > 100.0)
+		magt = magt - 100.0;
+	    }
+	else
+	    magt = mag1;
+
+	/* Check magnitude limits */
+	pass = 1;
+	if (mag1 != mag2 && (magt < mag1 || magt > mag2))
+	    pass = 0;
+
+	/* Check rough position limits */
+	ra = star->ra;
+	dec = star->dec;
+	if  ((!wrap && (ra < rra1 || ra > rra2)) ||
+	    (wrap && (ra < rra1 && ra > rra2)) ||
+	    dec < rdec1 || dec > rdec2)
+	    pass = 0;
+
+	/* Convert coordinate system for this star and test it*/
+	if (pass) {
+	    sysref = star->coorsys;
+	    eqref = star->equinox;
+	    epref = star->epoch;
+
+	    /* Extract selected fields  */
+	    num = star->num;
+	    rapm = star->rapm;
+	    decpm = star->decpm;
+	    parallax = star->parallax;
+	    rv = star->radvel;
+
+	    /* Convert from catalog to search coordinate system */
+	    if (sc->entpx || sc->entrv)
+		wcsconv (sysref, sysout, eqref, eqout, epref, epout,
+		     &ra, &dec, &rapm, &decpm, &parallax, &rv);
+	    else if (sc->mprop == 1)
+		wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		     &ra, &dec, &rapm, &decpm);
+	    else
+		wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+	    if (sc->sptype)
+		peak = (1000 * (int) star->isp[0]) + (int)star->isp[1];
+	    else
+		peak = star->peak;
+
+	    /* Check distance along RA and Dec axes */
+	    ddist = wcsdist (cra,cdec,cra,dec);
+	    if (ddist > ddec)
+		pass = 0;
+	    rdist = wcsdist (cra,dec,ra,dec);
+	    if (rdist > dra)
+		pass = 0;
+	    }
+
+	/* Save star in FITS image */
+	if (pass) {
+	    wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+	    if (!offscl) {
+		if (magscale > 0.0)
+		    flux = magscale * exp (logt * (-magt / 2.5));
+		else
+		    flux = 1.0;
+		ix = (int) (xpix + 0.5);
+		iy = (int) (ypix + 0.5);
+		addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+		nstar++;
+		jstar++;
+		}
+	    else {
+		ix = 0;
+		iy = 0;
+		}
+	    if (nlog == 1) {
+		fprintf (stderr,"TABBIN: %11.6f: %9.5f %9.5f %s",
+			 num,ra,dec,cstr);
+		if (magscale > 0.0)
+		    fprintf (stderr, " %5.2f", mag);
+		if (!offscl)
+		    flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+		else
+		    flux = 0.0;
+		fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+		}
+
+	    /* End of accepted star processing */
+	    }
+
+	/* Log operation */
+	if (nlog > 0 && istar%nlog == 0)
+		fprintf (stderr,"TABBIN: %5d / %5d / %5d sources catalog %s\r",
+			jstar,istar,nstars,tabcatname);
+
+	/* End of star loop */
+	}
+
+    /* Summarize search */
+    if (nlog > 0) {
+	fprintf (stderr,"TABBIN: Catalog %s : %d / %d / %d found\n",tabcatname,
+		 jstar,istar,nstars);
+	}
+
+    return (nstar);
+}
+
+
+/* TABXYREAD -- Read X, Y, and magnitude of tab table stars */
+
+int
+tabxyread (tabcatname, xa, ya, ba, pa, nlog)
+
+char	*tabcatname;	/* Name of reference star catalog file */
+double	**xa;		/* Array of x coordinates (returned) */
+double	**ya;		/* Array of y coordinates (returned) */
+double	**ba;		/* Array of magnitudes (returned) */
+int	**pa;		/* Array of fluxes (returned) */
+int	nlog;
+{
+    double xi, yi, magi, flux;
+    char *line;
+    int istar, nstars;
+    struct TabTable *startab;
+    int entx, enty, entmag;
+
+    /* Open tab table file */
+    nndec = 0;
+    startab = tabopen (tabcatname, 0);
+    if (startab == NULL || startab->nlines <= 0) {
+	fprintf (stderr,"TABXYREAD: Cannot read catalog %s\n", tabcatname);
+	return (0);
+	}
+
+    /* Find columns for X and Y */
+    entx = tabccol (startab, "x");
+    enty = tabccol (startab, "y");
+
+    /* Find column for magnitude */
+    if (!(entmag = tabccol (startab, "mag"))) {
+	if (!(entmag = tabccol (startab, "magv"))) {
+	    if (!(entmag = tabccol (startab, "magj")))
+		entmag = tabccol (startab, "magr");
+	    }
+	}
+
+    /* Allocate vectors for x, y, magnitude, and flux */
+    nstars = startab->nlines;
+    *xa = (double *) realloc(*xa, nstars*sizeof(double));
+    if (*xa == NULL) {
+	fprintf (stderr,"TABXYREAD: Cannot allocate memory for x\n");
+	return (0);
+	}
+    *ya = (double *) realloc(*ya, nstars*sizeof(double));
+    if (*ya == NULL) {
+	fprintf (stderr,"TABXYREAD: Cannot allocate memory for y\n");
+	return (0);
+	}
+    *ba = (double *) realloc(*ba, nstars*sizeof(double));
+    if (*ba == NULL) {
+	fprintf (stderr,"TABXYREAD: Cannot allocate memory for mag\n");
+	return (0);
+	}
+    *pa = (int *) realloc(*pa, nstars*sizeof(int));
+    if (*pa == NULL) {
+	fprintf (stderr,"TABXYREAD: Cannot allocate memory for flux\n");
+	return (0);
+	}
+
+    /* Loop through catalog */
+    for (istar = 0; istar < nstars; istar++) {
+
+	/* Read line for next star */
+	if ((line = gettabline (startab, istar+1)) == NULL) {
+	    fprintf (stderr,"TABXYREAD: Cannot read star %d\n", istar);
+	    break;
+	    }
+
+	/* Extract x, y, and magnitude */
+	(void) setoken (&startok, line, "tab");
+	xi = tabgetr8 (&startok, entx);
+	yi = tabgetr8 (&startok, enty);
+	magi = tabgetr8 (&startok, entmag);
+
+	(*xa)[istar] = xi;
+	(*ya)[istar] = yi;
+	(*ba)[istar] = magi;
+	flux = 1000000000.0 * pow (10.0, (-magi / 2.5));
+	(*pa)[istar] = (int) flux;
+
+	if (nlog == 1)
+	    fprintf (stderr,"DAOREAD: %6d/%6d: %9.5f %9.5f %6.2f %15.4f\n",
+		     istar,nstars,xi,yi,magi,flux);
+
+	/* Log operation */
+	if (nlog > 1 && istar%nlog == 0)
+	    fprintf (stderr,"TABXYREAD: %5d / %5d sources catalog %s\r",
+		     istar,nstars,tabcatname);
+
+	/* End of star loop */
+	}
+
+    /* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"TABXYREAD: Catalog %s : %d / %d found\n",tabcatname,
+		 istar,nstars);
+
+    /* Free table */
+    tabclose (startab);
+    if (istar < nstars-1)
+	return (istar + 1);
+    else
+	return (nstars);
+}
+
+
+#define TABMAX 64
+
+/* TABRKEY -- Read single keyword from tab table stars with specified numbers */
+
+int
+tabrkey (tabcatname, starcat, nnum, tnum, keyword, tval)
+
+char	*tabcatname;	/* Name of reference star catalog file */
+struct StarCat **starcat; /* Star catalog data structure */
+int	nnum;		/* Number of stars to look for */
+double	*tnum;		/* Array of star numbers to look for */
+char	*keyword;	/* Keyword for which to return values */
+char	**tval;		/* Returned values for specified keyword */
+{
+    int jnum, lval;
+    int nstar;
+    int istar, nstars;
+    double num;
+    char *line;
+    char *tvalue;
+    char value[TABMAX];
+    struct TabTable *startab;
+    struct StarCat *sc;	/* Star catalog data structure */
+
+    nstar = 0;
+
+    /* Open star catalog */
+    sc = *starcat;
+    if (sc == NULL)
+	sc = tabcatopen (tabcatname, NULL, 0);
+    *starcat = sc;
+    if (sc == NULL || sc->nstars <= 0) {
+	if (taberr != NULL)
+	    fprintf (stderr,"%s\n", taberr);
+	fprintf (stderr,"TABRKEY: Cannot read catalog %s\n", tabcatname);
+	return (0);
+	}
+    startab = sc->startab;
+    if (startab == NULL || startab->nlines <= 0) {
+	fprintf (stderr,"TABRKEY: Cannot read catalog %s\n", tabcatname);
+	return (0);
+	}
+
+    /* Loop through star list */
+    nstars = startab->nlines;
+    for (jnum = 0; jnum < nnum; jnum++) {
+
+	/* Loop through catalog to star */
+	for (istar = 1; istar <= nstars; istar++) {
+	    if ((line = gettabline (startab, istar)) == NULL) {
+		fprintf (stderr,"TABRKEY: Cannot read star %d\n", istar);
+		num = 0.0;
+		break;
+		}
+
+	    /* Check ID number */
+	    (void) setoken (&startok, line, "tab");
+	    if ((num = tabgetr8 (&startok,sc->entid)) == 0.0)
+		num = (double) istar;
+	    if (num == tnum[jnum])
+		break;
+	    }
+
+	/* If star has been found in table */
+	if (num == tnum[jnum]) {
+	    nstar++;
+
+	    /* Extract selected field */
+	    (void) tabgetk (startab, &startok, keyword, value, TABMAX);
+	    lval = strlen (value);
+	    if (lval > 0) {
+		tvalue = (char *) calloc (1, lval+1);
+		strcpy (tvalue, value);
+		}
+	    else
+		tvalue = NULL;
+	    if (tval[jnum]) free(tval[jnum]);
+	    tval[jnum] = tvalue;
+	    }
+	}
+
+    return (nstars);
+}
+
+static char newline = 10;
+static char tab = 9;
+
+
+/* TABCATOPEN -- Open tab table catalog, returning number of entries */
+
+struct StarCat *
+tabcatopen (tabpath, tabtable, nbbuff)
+
+char *tabpath;		/* Tab table catalog file pathname */
+struct TabTable *tabtable;
+int	nbbuff;		/* Number of bytes in buffer; 0=read whole file */
+{
+    char cstr[32];
+    char keyword[16];
+    char *tabname;
+    struct TabTable *startab;
+    struct StarCat *sc;
+    int i, lnum, ndec, istar, nbsc, icol, j, nnfld;
+    int entpmq, entnid;
+    char *line;
+    double dnum;
+
+    /* Open the tab table file */
+    if (tabtable != NULL)
+	startab = tabtable;
+    else if ((startab = tabopen (tabpath, nbbuff)) == NULL)
+	return (NULL);
+
+    /* Allocate catalog data structure */
+    nbsc = sizeof (struct StarCat);
+    sc = (struct StarCat *) calloc (1, nbsc);
+    sc->startab = startab;
+
+    /* Save name of catalog */
+    tabname = strrchr (tabpath, '/');
+    if (tabname)
+	tabname = tabname + 1;
+    else
+	tabname = tabpath;
+    if (strlen (tabname) < 24)
+	strcpy (sc->isfil, tabname);
+    else {
+	strncpy (sc->isfil, tabname, 23);
+	sc->isfil[23] = (char) 0;
+	}
+
+    /* Find column and name of object identifier */
+    sc->entid = -1;
+    sc->keyid[0] = (char) 0;
+    if ((sc->entid = tabccol (startab, "id"))) {
+	i = sc->entid - 1;
+	strncpy (sc->keyid, startab->colname[i], startab->lcol[i]);
+	}
+    else if ((sc->entid = tabccont (startab, "_id"))) {
+	i = sc->entid - 1;
+	strncpy (sc->keyid, startab->colname[i], startab->lcol[i]);
+	}
+    else if ((sc->entid = tabccont (startab, "id"))) {
+	i = sc->entid - 1;
+	strncpy (sc->keyid, startab->colname[i], startab->lcol[i]);
+	}
+    else if ((sc->entid = tabccont (startab, "num"))) {
+	i = sc->entid - 1;
+	strncpy (sc->keyid, startab->colname[i], startab->lcol[i]);
+	}
+    else if ((sc->entid = tabccont (startab, "name"))) {
+	i = sc->entid - 1;
+	strncpy (sc->keyid, startab->colname[i], startab->lcol[i]);
+	}
+    else if ((sc->entid = tabccont (startab, "obj"))) {
+	i = sc->entid - 1;
+	strncpy (sc->keyid, startab->colname[i], startab->lcol[i]);
+	}
+    sc->nndec = nndec;
+
+    /* Figure out what the coordinate system is */
+    if (tabhgetc (startab, "radecsys", cstr)) {
+	if (!strcmp (cstr, "galactic"))
+	    sc->coorsys = WCS_GALACTIC;
+	else if (!strcmp (cstr, "ecliptic"))
+	    sc->coorsys = WCS_ECLIPTIC;
+	else if (!strcmp (cstr, "fk4"))
+	    sc->coorsys = WCS_B1950;
+	else
+	    sc->coorsys = WCS_J2000;
+	}
+    else
+	sc->coorsys = WCS_J2000;
+
+    /* Find column and name of object right ascension */
+    sc->entra = -1;
+    sc->keyra[0] = (char) 0;
+    if (sc->coorsys == WCS_GALACTIC) {
+	if ((sc->entra = tabccol (startab, "long_gal")))
+	    strcpy (sc->keyra, "long_gal");
+	else if ((sc->entra = tabccol (startab, "long")))
+	    strcpy (sc->keyra, "long_gal");
+	else if ((sc->entra = tabccont (startab, "long"))) {
+	    i = sc->entra - 1;
+	    strncpy (sc->keyra, startab->colname[i], startab->lcol[i]);
+	    }
+	}
+    else if (sc->coorsys == WCS_ECLIPTIC) {
+	if ((sc->entra = tabccol (startab, "long_ecl")))
+	    strcpy (sc->keyra, "long_ecl");
+	else if ((sc->entra = tabccol (startab, "long")))
+	    strcpy (sc->keyra, "long_ecl");
+	else if ((sc->entra = tabccont (startab, "long"))) {
+	    i = sc->entra - 1;
+	    strncpy (sc->keyra, startab->colname[i], startab->lcol[i]);
+	    }
+	}
+    else {
+	if ((sc->entra = tabccol (startab, "ra"))) {
+	    i = sc->entra - 1;
+	    strncpy (sc->keyra, startab->colname[i], startab->lcol[i]);
+	    }
+	else if ((sc->entra = tabccont (startab, "ra"))) {
+	    i = sc->entra - 1;
+	    strncpy (sc->keyra, startab->colname[i], startab->lcol[i]);
+	    }
+	}
+
+    /* Find column and name of object declination */
+    sc->entdec = -1;
+    sc->keydec[0] = (char) 0;
+    if (sc->coorsys == WCS_GALACTIC) {
+	if ((sc->entdec = tabccol (startab, "lat_gal")))
+	    strcpy (sc->keydec, "lat_gal");
+	else if ((sc->entdec = tabccol (startab, "lat")))
+	    strcpy (sc->keydec, "lat_gal");
+	else if ((sc->entdec = tabccont (startab, "lat"))) {
+	    i = sc->entdec - 1;
+	    strncpy (sc->keydec, startab->colname[i], startab->lcol[i]);
+	    }
+	}
+    else if (sc->coorsys == WCS_ECLIPTIC) {
+	if ((sc->entdec = tabccol (startab, "lat_ecl")))
+	    strcpy (sc->keydec, "lat_ecl");
+	else if ((sc->entdec = tabccol (startab, "lat")))
+	    strcpy (sc->keydec, "lat_ecl");
+	else if ((sc->entdec = tabccont (startab, "lat"))) {
+	    i = sc->entdec - 1;
+	    strncpy (sc->keydec, startab->colname[i], startab->lcol[i]);
+	    }
+	}
+    else {
+	if ((sc->entdec = tabccol (startab, "de(deg)"))) {
+	    i = sc->entdec - 1;
+	    strcpy (sc->keydec, "dec");
+	    }
+	else if ((sc->entdec = tabccol (startab, "dec"))) {
+	    i = sc->entdec - 1;
+	    strncpy (sc->keydec, startab->colname[i], startab->lcol[i]);
+	    }
+	else if ((sc->entdec = tabccont (startab, "dec"))) {
+	    i = sc->entdec - 1;
+	    strncpy (sc->keydec, startab->colname[i], startab->lcol[i]);
+	    }
+	}
+
+    /* Check columns for magnitudes and save columns and names */
+    sc->nmag = 0;
+    icol = 0;
+    for (i = 0; i < startab->ncols; i++) {
+	icol = icol + 1;
+	if (icol == sc->entid) continue;
+	if (icol == sc->entra) continue;
+	if (icol == sc->entdec) continue;
+	for (j = 0; j < 16; j++) keyword[j] = (char)0;
+	if (startab->lcol[i] < 16)
+	    strncpy (keyword, startab->colname[i], startab->lcol[i]);
+	else
+	    strncpy (keyword, startab->colname[i], 15);
+	if (strcsrch (keyword, "mag") && !strcsrch (keyword, "err")) {
+	    strcpy (sc->keymag[sc->nmag], keyword);
+	    sc->entmag[sc->nmag] = icol;
+	    sc->nmag++;
+	    }
+	else if (strcsrch (keyword, "exp")) {
+	    strcpy (sc->keymag[sc->nmag], keyword);
+	    sc->entmag[sc->nmag] = icol;
+	    sc->nmag++;
+	    }
+	else if (strcsrch (keyword, "dist")) {
+	    strcpy (sc->keymag[sc->nmag], keyword);
+	    sc->entmag[sc->nmag] = icol;
+	    sc->nmag++;
+	    }
+	else if ((keyword[0] == 'M' || keyword[0] == 'm') &&
+		 !strcsrch (keyword, "err") &&
+		 !strcsrch (keyword, "flag")) {
+	    strcpy (sc->keymag[sc->nmag], keyword);
+	    sc->entmag[sc->nmag] = icol;
+	    sc->nmag++;
+	    }
+	}
+
+    /* Find column and name of object right ascension proper motion */
+    sc->entrpm = -1;
+    sc->keyrpm[0] = (char) 0;
+    if ((sc->entrpm = tabccol (startab, "ura")))
+	strcpy (sc->keyrpm, "ura");
+    else if ((sc->entrpm = tabccol (startab, "rapm")))
+	strcpy (sc->keyrpm, "rapm");
+    else if ((sc->entrpm = tabccol (startab, "pmra")))
+	strcpy (sc->keyrpm, "pmra");
+    else if ((sc->entrpm = tabccol (startab, "dra")))
+	strcpy (sc->keyrpm, "dra");
+    else if ((sc->entrpm = tabccol (startab, "ux")))
+	strcpy (sc->keyrpm, "ux");
+
+    /* Find column and name of object declination proper motion */
+    sc->entdpm = -1;
+    sc->keydpm[0] = (char) 0;
+    if ((sc->entdpm = tabccol (startab, "udec")))
+	strcpy (sc->keydpm, "udec");
+    else if ((sc->entdpm = tabccol (startab, "decpm")))
+	strcpy (sc->keydpm, "decpm");
+    else if ((sc->entdpm = tabccol (startab, "pmdec")))
+	strcpy (sc->keydpm, "pmdec");
+    else if ((sc->entrpm = tabccol (startab, "ddec")))
+	strcpy (sc->keyrpm, "ddec");
+    else if ((sc->entdpm = tabccol (startab, "uy")))
+	strcpy (sc->keydpm, "uy");
+
+    /* Find units for RA proper motion */
+    sc->mprop = 0;
+    cstr[0] = 0;
+    if (!tabhgetc (startab,"RPMUNIT", cstr)) {
+	if (!tabhgetc (startab,"rpmunit", cstr)) {
+	    if (!tabhgetc (startab,"pmunit", cstr)) {
+		if (sc->entdpm > 0 && sc->entrpm > 0)
+		    strcpy (cstr,"mas/yr");
+		}
+	    }
+	}
+    if (strlen (cstr) > 0) {
+	sc->mprop = 1;
+	if (!strcmp (cstr, "mas/yr") || !strcmp (cstr, "mas/year"))
+	    sc->rpmunit = PM_MASYR;
+	else if (!strcmp (cstr, "ms/yr") || !strcmp (cstr, "millisec/year"))
+	    sc->rpmunit = PM_MTSYR;
+	else if (!strcmp (cstr, "arcsec/yr") || !strcmp (cstr, "arcsec/year"))
+	    sc->rpmunit = PM_ARCSECYR;
+	else if (!strcmp (cstr, "arcsec/hr") || !strcmp (cstr, "arcsec/hour"))
+	    sc->rpmunit = PM_ARCSECHR;
+	else if (!strcmp (cstr, "arcsec/cen") || !strcmp (cstr, "arcsec/century"))
+	    sc->rpmunit = PM_ARCSECCEN;
+	else if (!strcmp (cstr, "rad/yr") || !strcmp (cstr, "rad/year"))
+	    sc->rpmunit = PM_RADYR;
+	else if (!strcmp (cstr, "sec/yr") || !strcmp (cstr, "sec/year"))
+	    sc->rpmunit = PM_TSECYR;
+	else if (!strcmp (cstr, "tsec/yr") || !strcmp (cstr, "tsec/year"))
+	    sc->rpmunit = PM_TSECYR;
+	else if (!strcmp (cstr, "tsec/cen") || !strcmp (cstr, "tsec/century"))
+	    sc->rpmunit = PM_TSECCEN;
+	else
+	    sc->rpmunit = PM_DEGYR;
+	}
+    else if (sc->entrpm > 0)
+	sc->rpmunit = PM_TSECCEN;
+
+    /* Find units for Dec proper motion */
+    cstr[0] = 0;
+    if (!tabhgetc (startab,"DPMUNIT", cstr)) {
+	if (!tabhgetc (startab,"dpmunit", cstr)) {
+	    if (!tabhgetc (startab,"pmunit", cstr))
+		tabhgetc (startab,"pmunit", cstr);
+	    }
+	}
+    if (strlen (cstr) > 0) {
+	sc->mprop = 1;
+	if (!strcmp (cstr, "mas/yr") || !strcmp (cstr, "mas/year"))
+	    sc->dpmunit = PM_MASYR;
+	else if (!strcmp (cstr, "sec/yr") || !strcmp (cstr, "sec/year"))
+	    sc->dpmunit = PM_ARCSECYR;
+	else if (!strcmp (cstr, "tsec/yr") || !strcmp (cstr, "tsec/year"))
+	    sc->dpmunit = PM_ARCSECYR;
+	else if (!strcmp (cstr, "arcsec/hr") || !strcmp (cstr, "arcsec/hour"))
+	    sc->dpmunit = PM_ARCSECHR;
+	else if (!strcmp (cstr, "arcsec/cen") || !strcmp (cstr, "arcsec/century"))
+	    sc->dpmunit = PM_ARCSECCEN;
+	else if (!strcmp (cstr, "arcsec/yr") || !strcmp (cstr, "arcsec/year"))
+	    sc->dpmunit = PM_ARCSECYR;
+	else if (!strcmp (cstr, "rad/yr") || !strcmp (cstr, "rad/year"))
+	    sc->dpmunit = PM_RADYR;
+	else
+	    sc->dpmunit = PM_DEGYR;
+	}
+    else if (sc->entdpm > 0)
+	sc->dpmunit = PM_ARCSECCEN;
+
+    /* Find column for parallax */
+    sc->entpx = 0;
+    sc->entpx = tabccol (startab, "px");
+
+    /* Find column for parallax error */
+    sc->entpxe = 0;
+    sc->entpxe = tabccol (startab, "pxerr");
+
+    /* Find column for radial velocity */
+    sc->entrv = 0;
+    sc->keyrv[0] = (char) 0;
+    if ((sc->entrv = tabccol (startab, "rv")))
+	strcpy (sc->keyrv, "rv");
+    else if ((sc->entrv = tabccol (startab, "cz")))
+	strcpy (sc->keyrv, "cz");
+    if (sc->entrv > 0 && sc->nmag < 10) {
+	strcpy (sc->keymag[sc->nmag], sc->keyrv);
+	sc->entmag[sc->nmag] = sc->entrv;
+	sc->nmag++;
+	}
+
+    /* Find column for epoch */
+    sc->entepoch = 0;
+    sc->keyepoch[0] = (char) 0;
+    if ((sc->entepoch = tabccol (startab, "epoch")))
+	strcpy (sc->keyepoch, "epoch");
+    else if ((sc->entepoch = tabccol (startab, "ep")))
+	strcpy (sc->keyepoch, "ep");
+    if (sc->entepoch > 0 && sc->nmag < 10) {
+	strcpy (sc->keymag[sc->nmag], sc->keyepoch);
+	sc->entmag[sc->nmag] = sc->entepoch;
+	sc->nmag++;
+	sc->nepoch = 1;
+	}
+
+    /* Find column for date (in FITS format as epoch alternate) */
+    sc->entdate = 0;
+    sc->entdate = tabccol (startab, "date");
+
+    /* Find column and name of object peak or plate number */
+    sc->entpeak = -1;
+    sc->keypeak[0] = (char) 0;
+    if ((sc->entpeak = tabccol (startab, "PEAK")))
+	strcpy (sc->keypeak, "PEAK");
+    else if ((sc->entpeak = tabccol (startab, "peak")))
+	strcpy (sc->keypeak, "peak");
+    else if ((sc->entpeak = tabccol (startab, "plate"))) {
+	strcpy (sc->keypeak, "plate");
+	sc->plate = 1;
+	}
+    else if ((sc->entpeak = tabccol (startab, "field"))) {
+	strcpy (sc->keypeak, "field");
+	sc->plate = 1;
+	}
+    else if ((sc->entpeak = tabccol (startab, "Class"))) {
+	strcpy (sc->keypeak, "class");
+	sc->plate = 1;
+	}
+    else if ((sc->entpeak = tabcol (startab, "c"))) {
+	strcpy (sc->keypeak, "class");
+	sc->plate = 1;
+	}
+    else if ((entpmq = tabccol (startab, "pm")) > 0 &&
+	     (entnid = tabccol (startab, "ni")) > 0) {
+	sc->entpeak = (entpmq * 100) + entnid;
+	}
+
+    /* Find column and name of object spectral type */
+    sc->enttype = 0;
+    sc->keytype[0] = (char) 0;
+    if ((sc->enttype = tabccol (startab, "SpT")))
+	strcpy (sc->keytype, "spt");
+    else if ((sc->enttype = tabccol (startab, "TYPE")))
+	strcpy (sc->keytype, "type");
+    else if ((sc->enttype = tabccont (startab, "typ"))) {
+	i = sc->enttype - 1;
+	strncpy (sc->keytype, startab->colname[i], startab->lcol[i]);
+	}
+    if (sc->enttype > 0)
+	sc->sptype = 1;
+
+    sc->entadd = -1;
+    sc->keyadd[0] = (char) 0;
+    if (kwo != NULL) {
+	sc->entadd = tabccol (startab, kwo);
+	strcpy (sc->keyadd, kwo);
+	}
+
+    /* Set catalog coordinate system */
+    sc->coorsys = 0;
+    sc->equinox = 0.0;
+    sc->epoch = 0.0;
+    if (tabhgetc (startab,"RADECSYS", cstr)) {
+	sc->coorsys = wcscsys (cstr);
+	if (!tabhgetr8 (startab,"EQUINOX", &sc->equinox))
+	    sc->equinox = wcsceq (cstr);
+	if (!tabhgetr8 (startab,"EPOCH",&sc->epoch))
+	    sc->epoch = sc->equinox;
+	}
+    if (tabhgetc (startab,"radecsys", cstr)) {
+	sc->coorsys = wcscsys (cstr);
+	if (!tabhgetr8 (startab,"equinox", &sc->equinox))
+	    sc->equinox = wcsceq (cstr);
+	if (!tabhgetr8 (startab,"epoch",&sc->epoch))
+	    sc->epoch = sc->equinox;
+	}
+    else if (tabhgetr8 (startab,"EQUINOX", &sc->equinox)) {
+	if (!tabhgetr8 (startab,"EPOCH",&sc->epoch))
+	    sc->epoch = sc->equinox;
+	if (sc->equinox == 1950.0)
+	    sc->coorsys = WCS_B1950;
+	else
+	    sc->coorsys = WCS_J2000;
+	}
+    else if (tabhgetr8 (startab,"equinox", &sc->equinox)) {
+	if (!tabhgetr8 (startab,"epoch",&sc->epoch))
+	    sc->epoch = sc->equinox;
+	if (sc->equinox == 1950.0)
+	    sc->coorsys = WCS_B1950;
+	else
+	    sc->coorsys = WCS_J2000;
+	}
+    else if (tabhgetr8 (startab,"EPOCH", &sc->epoch)) {
+	sc->equinox = sc->epoch;
+	if (sc->equinox == 1950.0)
+	    sc->coorsys = WCS_B1950;
+	else
+	    sc->coorsys = WCS_J2000;
+	}
+    else if (tabhgetr8 (startab,"epoch", &sc->epoch)) {
+	sc->equinox = sc->epoch;
+	if (sc->equinox == 1950.0)
+	    sc->coorsys = WCS_B1950;
+	else
+	    sc->coorsys = WCS_J2000;
+	}
+
+    /* Check whether catalog is sorted by right ascension */
+    if (tabhgetc (startab, "rasort", cstr))
+	sc->rasorted = 1;
+    else
+	sc->rasorted = 0;
+
+    /* Set other stuff */
+    sc->nstars = startab->nlines;
+    if (sc->entid)
+	sc->stnum = 1;
+    else
+	sc->stnum = 0;
+
+    /* Find out if ID is number, and if so, how many decimal places it has */
+    if (sc->entid) {
+
+	istar = 1;
+	if ((line = gettabline (startab, istar)) == NULL) {
+	    fprintf (stderr,"TABCATOPEN: Cannot read first star\n");
+	    tabcatclose (sc);
+	    return (NULL);
+	    }
+	if (setoken (&startok, line, "tab") < 2) {
+	    fprintf (stderr,"TABCATOPEN: First star has too few columns\n");
+	    tabcatclose (sc);
+	    return (NULL);
+	    }
+	tabgetc (&startok, sc->entid, cstr, 32);
+
+	/* Find length of identifier */
+	if (tabhgeti4 (startab,"nfield", &nnfld))
+	    sc->nnfld = nnfld;
+	else
+	    sc->nnfld = strlen (cstr);
+
+	/* Find number of decimal places in identifier */
+	if (tabhgeti4 (startab, "ndec", &nndec)) {
+	    sc->nndec = nndec;
+	    if (!isnum (cstr))
+		sc->stnum = -nndec;
+	    }
+	else if (isnum (cstr)) {
+	    dnum = tabgetr8 (&startok,sc->entid);
+	    sprintf (cstr,"%.0f", (dnum * 100000000.0) + 0.1);
+	    lnum = strlen (cstr);
+	    for (i = 0; i < 8; i++) {
+		if (cstr[lnum-i-1] != '0') {
+		    ndec = 8 - i;
+		    if (ndec > nndec) {
+			nndec = ndec;
+			sc->nndec = nndec;
+			}
+		    break;
+		    }
+		}
+	    sc->nndec = nndec;
+	    }
+	else {
+	    sc->stnum = -strlen (cstr);
+	    sc->nndec = nndec;
+	    }
+	}
+    sc->refcat = TABCAT;
+
+    return (sc);
+}
+
+
+/* TABCATCLOSE -- Close tab table catalog and free associated data structures */
+
+void
+tabcatclose (sc)
+    struct StarCat *sc;
+{
+    tabclose (sc->startab);
+    free (sc);
+    return;
+}
+
+
+/* TABSTAR -- Get tab table catalog entry for one star;
+   return 0 if successful, else -1 */
+
+int
+tabstar (istar, sc, st, verbose)
+
+int	istar;		/* Star sequence number in tab table catalog */
+struct StarCat *sc;	/* Star catalog data structure */
+struct Star *st;	/* Star data structure, updated on return */
+int	verbose;	/* 1 to print error messages */
+{
+    struct TabTable *startab = sc->startab;
+    char *line;
+    char *uscore;
+    char cnum[64];
+    char temp[64];
+    double ydate;
+    char *cn;
+    int ndec, i, imag, ltok;
+    int lnum, ireg, inum;
+    int pmq, nid;
+    int lcn;
+    char str[24];
+
+    if ((line = gettabline (startab, istar)) == NULL) {
+	if (verbose)
+	    fprintf (stderr,"TABSTAR: Cannot read star %d\n", istar);
+	return (-1);
+	}
+
+    /* Parse line for rapid field extraction */
+    if (setoken (&startok, line, "tab") < 2) {
+	fprintf (stderr,"TABSTAR: Star %d entry too short\n", istar);
+	return (-1);
+	}
+
+    /* Extract ID  */
+    st->objname[0] = (char) 0;
+    if (sc->entid) {
+	tabgetc (&startok, sc->entid, cnum, 64);
+	if (!strcmp (sc->isfil,"usnoa-server")) {
+	    if ((uscore = strchr (cnum, '_')) != NULL)
+		*uscore = '.';
+	    cn = cnum + 1;
+	    }
+	else if (!strcmp (sc->isfil,"gsc-server"))
+	    cn = cnum + 3;
+	else
+	    cn = cnum;
+	lcn = strlen (cn);
+	/* if (lcn < 16 && (isnum (cn) || isnum (cn+1))) { */
+	if (lcn < 16 && isnum (cn)) {
+	    if (isnum(cnum)) {
+		lcn = strlen (cn);
+		if (lcn > 15)
+		    st->num = atof (cn + lcn - 15);
+		else
+		    st->num = atof (cn);
+		}
+	    else {
+		if (cnum[0] == 'S')
+		    st->num = -atof (cn+1);
+		else
+		    st->num = atof (cn+1);
+		}
+	    if (!strcmp (sc->isfil,"gsc-server")) {
+		ireg = atoi (cn) / 100000;
+		inum = atoi (cn) % 100000;
+		st->num = (double) ireg + 0.0001 * (double) inum;
+		}
+
+	    /* Find field length of identifier */
+	    sc->nnfld = strlen (cn);
+
+	    /* Find number of decimal places in identifier */
+	    if (strchr (cn,'.') == NULL) {
+		nndec = 0;
+		sc->nndec = nndec;
+		}
+	    else {
+		sprintf (cn,"%.0f", (st->num * 100000000.0) + 0.1);
+		lnum = strlen (cnum);
+		for (i = 0; i < 8; i++) {
+		    if (cn[lnum-i-1] != '0') {
+			ndec = 8 - i;
+			if (ndec > nndec) {
+			    nndec = ndec;
+			    sc->nndec = nndec;
+			    }
+			break;
+			}
+		    }
+		}
+	    }
+	else {
+	    strcpy (st->objname, cnum);
+	    st->num = st->num + 1.0;
+	    }
+	}
+    else {
+	st->num = (double) istar;
+	nndec = 0;
+	sc->nndec = nndec;
+	}
+
+    /* Right ascension */
+    st->ra = tabgetra (&startok, sc->entra);
+
+    /* Declination */
+    st->dec = tabgetdec (&startok, sc->entdec);
+
+    /* Magnitudes */
+    for (imag = 0; imag < sc->nmag; imag++) {
+	if (sc->entmag[imag]) {
+	    if (tabgetc (&startok, sc->entmag[imag], str, 24))
+		return (0);
+	    ltok = strlen (str);
+	    if (str[ltok-1] == 'L') {
+		str[ltok-1] = (char) 0;
+		if (isnum (str))
+		    st->xmag[imag] = 100.0 + atof (str);
+		else
+		    st->xmag[imag] = 0.0;
+		}
+	    else
+		st->xmag[imag] = tabgetr8 (&startok, sc->entmag[imag]);
+	    }
+	else
+	    st->xmag[imag] = 0.0;
+	}
+
+    /* Convert right ascension proper motion to degrees/year */
+    st->rapm = tabgetr8 (&startok, sc->entrpm);
+    if (sc->rpmunit == PM_MASYR)
+	st->rapm = (st->rapm / 3600000.0) / cosdeg(st->dec);
+    else if (sc->rpmunit == PM_MTSYR)
+	st->rapm = st->rapm / 240000.0;
+    else if (sc->rpmunit == PM_ARCSECYR)
+	st->rapm = (st->rapm / 3600.0) / cosdeg (st->dec);
+    else if (sc->rpmunit == PM_ARCSECCEN)
+	st->rapm = (st->rapm / 360000.0) / cosdeg (st->dec);
+    else if (sc->rpmunit == PM_TSECYR)
+	st->rapm = st->rapm / 240.0;
+    else if (sc->rpmunit == PM_TSECCEN)
+	st->rapm = st->rapm / 24000.0;
+    else if (sc->rpmunit == PM_RADYR)
+	st->rapm = raddeg (st->rapm);
+    else
+	st->rapm = 0.0;
+
+    /* Convert declination proper motion to degrees/year */
+    st->decpm = tabgetr8 (&startok, sc->entdpm);
+    if (sc->dpmunit == PM_MASYR)
+	st->decpm = st->decpm / 3600000.0;
+    else if (sc->dpmunit == PM_ARCSECYR)
+	st->decpm = st->decpm / 3600.0;
+    else if (sc->dpmunit == PM_ARCSECCEN)
+	st->decpm = st->decpm / 360000.0;
+    else if (sc->dpmunit == PM_RADYR)
+	st->decpm = raddeg (st->decpm);
+    else
+	st->decpm = 0.0;
+
+    /* Parallax */
+    if (sc->entpx)
+	st->parallax = tabgetr8 (&startok, sc->entpx);
+    else
+	st->parallax = 0.0;
+
+    /* Radial velocity */
+    if (sc->entrv)
+	st->radvel = tabgetr8 (&startok, sc->entrv);
+    else
+	st->radvel = 0.0;
+
+    /* Epoch */
+    if (sc->entepoch) {
+	tabgetc (&startok, sc->entepoch, temp, 10);
+	if (temp[0] == '_') {
+	    if (sc->entdate > 0) {
+		tabgetc (&startok, sc->entdate, temp, 10);
+		st->epoch = fd2ep (temp);
+		}
+	    else
+		st->epoch = sc->epoch;
+	    }
+	else if (strchr (temp, '-') != NULL)
+	    st->epoch = fd2ep (temp);
+	else {
+	    ydate = tabgetr8 (&startok, sc->entepoch);
+	    if (ydate < 3000.0 && ydate > 0.0)
+		st->epoch = dt2ep (ydate, 12.0);
+	    else if (ydate < 100000.0)
+		st->epoch = mjd2ep (ydate);
+	    else
+		st->epoch = jd2ep (ydate);
+	    if (st->epoch > 2005.000)
+		printf ("TABSTAR: %s = %.5f -> %.5f\n", temp, ydate, st->epoch);
+	    }
+	st->xmag[sc->nmag-1] = st->epoch;
+	}
+    else
+	st->epoch = sc->epoch;
+
+    /* Peak counts */
+    if (sc->entpeak > 100) {
+	pmq = tabgeti4 (&startok, sc->entpeak/100);
+	nid = tabgeti4 (&startok, sc->entpeak%100);
+	st->peak = (pmq * 100) + nid;
+	}
+    else if (sc->entpeak > 0)
+	st->peak = tabgeti4 (&startok, sc->entpeak);
+    else
+	st->peak = 0;
+
+    /* Spectral type */
+    if (sc->enttype > 0) {
+	strcpy (st->isp, "__");
+	tabgetc (&startok, sc->enttype, st->isp, 24);
+	}
+
+    /* Extract selected field */
+    if (kwo != NULL)
+	(void) tabgetk (startab, &startok, kwo, st->objname, 79);
+
+    st->coorsys = sc->coorsys;
+    st->equinox = sc->equinox;
+    return (0);
+}
+
+
+/* TABOPEN -- Open tab table file, returning number of entries */
+
+struct TabTable *
+tabopen (tabfile, nbbuff)
+
+char	*tabfile;	/* Tab table catalog file name */
+int	nbbuff;		/* Number of bytes in buffer; 0=read whole file */
+{
+    FILE *fcat;
+    int nr, lfile, lname, lline;
+    char *tabnew, *tabline, *lastline;
+    char *tabcomma, *nextline;
+    char *thisname, *tabname;
+    int thistab, itab, nchar, nbtab;
+    int formfeed = (char) 12;
+    struct TabTable *tabtable;
+
+    tabcomma = NULL;
+    if (taberr != NULL) {
+	free (taberr);
+	taberr = NULL;
+	}
+
+    tabname = NULL;
+    if (!strcmp (tabfile, "stdin")) {
+	lfile = 100000;
+	fcat = stdin;
+	}
+    else {
+
+	/* Separate table name from file name, if necessary */
+	if ((tabcomma = strchr (tabfile, ',')) != NULL) {
+	    tabname = (char *) calloc (1,64);
+	    strcpy (tabname, tabcomma+1);
+	    *tabcomma = (char) 0;
+	    }
+
+	/* Find length of tab table catalog */
+	lfile = tabsize (tabfile);
+	if (nbbuff > 1 && nbbuff < lfile)
+	    lfile = nbbuff;
+	else if (nbbuff == 1 && lfile > 10000)
+	    lfile = 10000;
+	if (lfile < 1) {
+	    taberr = (char *) calloc (64 + strlen (tabfile), 1);
+	    sprintf (taberr,"TABOPEN: Tab table file %s has no entries",
+		     tabfile);
+	    if (tabcomma != NULL) *tabcomma = ',';
+	    return (NULL);
+	    }
+
+	/* Open tab table catalog */
+	if (!(fcat = fopen (tabfile, "r"))) {
+	    taberr = (char *) calloc (64 + strlen (tabfile), 1);
+	    sprintf (taberr,"TABOPEN: Tab table file %s cannot be read",
+		     tabfile);
+	    if (tabcomma != NULL) *tabcomma = ',';
+	    return (NULL);
+	    }
+	else if (verbose) {
+	    fprintf (stderr,"TABOPEN: tab table %s opened", tabfile);
+	}
+	}
+
+    /* Allocate tab table structure */
+    nbtab = sizeof(struct TabTable);
+    if ((tabtable=(struct TabTable *) calloc(1,nbtab)) == NULL){
+	taberr = (char *) calloc (64 + strlen (tabfile), 1);
+	sprintf (taberr,"TABOPEN: cannot allocate %d bytes for tab table structure for %s",
+		 nbtab, tabfile);
+	if (tabcomma != NULL) *tabcomma = ',';
+	return (NULL);
+	}
+    else if (verbose) {
+	fprintf (stderr,"TABOPEN: allocated %d bytes for tab table structure for %s",
+		 nbtab, tabfile);
+	}
+
+    tabtable->tabname = tabname;
+
+    /* Allocate space in structure for filename and save it */
+    lname = strlen (tabfile) + 2;
+    if ((tabtable->filename = (char *)calloc (1, lname)) == NULL) {
+	taberr = (char *) calloc (64 + strlen (tabfile), 1);
+	sprintf (taberr,"TABOPEN: cannot allocate filename %s in structure",
+		 tabfile);
+	(void) fclose (fcat);
+	tabclose (tabtable);
+	if (tabcomma != NULL) *tabcomma = ',';
+	return (NULL);
+	}
+    strcpy (tabtable->filename, tabfile);
+
+    /* Allocate buffer to hold entire catalog (or buffer length) and read it */
+    if ((tabtable->tabbuff = (char *) calloc (1, lfile+2)) == NULL) {
+	taberr = (char *) calloc (64 + strlen (tabfile), 1);
+	sprintf (taberr,"TABOPEN: cannot allocate buffer for tab table %s",
+		 tabfile);
+	(void) fclose (fcat);
+	tabclose (tabtable);
+	if (tabcomma != NULL) *tabcomma = ',';
+	return (NULL);
+	}
+    else {
+	if (verbose) {
+	    fprintf (stderr,"TABOPEN: allocated %d bytes for tab table for %s",
+		 lfile+2, tabfile);
+	    }
+	nr = fread (tabtable->tabbuff, 1, lfile, fcat);
+	if (fcat != stdin && nr < lfile) {
+	    fprintf (stderr,"TABOPEN: read only %d / %d bytes of file %s\n",
+		     nr, lfile, tabfile);
+	    (void) fclose (fcat);
+	    tabclose (tabtable);
+	    if (tabcomma != NULL) *tabcomma = ',';
+	    return (NULL);
+	    }
+	else if (verbose) {
+	    fprintf (stderr,"TABOPEN: read %d byte tab table from %s",
+		 lfile, tabfile);
+	    }
+	tabtable->tabbuff[lfile] = (char) 0;
+
+	/* Check for named table within a file */
+	if (tabname != NULL) {
+	    if (isnum (tabname)) {
+		itab = atoi (tabname);
+		thisname = tabtable->tabbuff;
+		thistab = 1;
+		if (itab > 1) {
+		    while (thistab < itab && thisname != NULL) {
+			thisname = strchr (thisname, formfeed);
+			if (thisname != NULL)
+			    thisname++;
+			thistab++;
+			}
+		    }
+		if (thisname == NULL) {
+		    fprintf (stderr, "GETTAB:  There are < %d tables in %s\n",
+			itab, tabfile);
+		    return (NULL);
+		    }
+		while (*thisname==' ' || *thisname==newline ||
+		       *thisname==formfeed || *thisname==(char)13)
+		    thisname++;
+		tabline = strchr (thisname, newline);
+		if (tabline != NULL) {
+		    nchar = tabline - thisname;
+		    if (strchr (thisname, tab) > tabline)
+			strncpy (tabtable->tabname, thisname, nchar);
+		    }
+		}
+	    else {
+		lname = strlen (tabname);
+		thisname = tabtable->tabbuff;
+		while (*thisname != (char) 0) {
+		    while (*thisname==' ' || *thisname==newline ||
+		   	   *thisname==formfeed || *thisname==(char)13)
+			thisname++;
+		    if (!strncmp (tabname, thisname, lname))
+			break;
+		    else
+			thisname = strchr (thisname, formfeed);
+		    }
+		}
+	    if (thisname == NULL) {
+		fprintf (stderr, "TABOPEN: table %s in file %s not found\n",
+			 tabname, tabfile);
+		if (tabcomma != NULL) *tabcomma = ',';
+		return (NULL);
+		}
+	    else
+		tabtable->tabheader = strchr (thisname, newline) + 1;
+	    }
+	else
+	    tabtable->tabheader = tabtable->tabbuff;
+
+	tabline = tabtable->tabheader;
+	lastline = NULL;
+	while (*tabline!='-' && tabline!=NULL && tabline < tabtable->tabbuff+lfile) {
+	    lastline = tabline;
+	    if ((tabline = strchr (lastline,newline)) != NULL)
+		tabline++;
+	    else
+		break;
+	    }
+	if (tabline == NULL || *tabline != '-') {
+	    taberr = (char *) calloc (64 + strlen (tabfile), 1);
+	    sprintf (taberr,"TABOPEN: No - line in tab table %s",tabfile);
+	    (void) fclose (fcat);
+	    tabclose (tabtable);
+	    if (tabcomma != NULL) *tabcomma = ',';
+	    return (NULL);
+	    }
+	tabtable->tabhead = lastline;
+	tabtable->tabdash = tabline;
+	tabtable->tabdata = strchr (tabline, newline) + 1;
+
+	/* Extract positions of keywords we will want to use */
+	if (!tabparse (tabtable)) {
+	    fprintf (stderr,"TABOPEN: No columns in tab table %s\n",tabfile);
+	    (void) fclose (fcat);
+	    tabclose (tabtable);
+	    if (tabcomma != NULL) *tabcomma = ',';
+	    return (NULL);
+	    }
+	else if (verbose) {
+	    fprintf (stderr,"TABOPEN: tab table %s header parsed", tabfile);
+	    }
+
+    /* Enumerate entries in tab table catalog by counting newlines */
+	if (nbbuff > 0)
+	    tabtable->nlines = 10000000;
+	else {
+	    tabnew = tabtable->tabdata;
+	    tabtable->nlines = 0;
+	    while ((tabnew = strchr (tabnew, newline)) != NULL) {
+		tabnew = tabnew + 1;
+		tabtable->nlines = tabtable->nlines + 1;
+		if (*tabnew == formfeed)
+		    break;
+		}
+	    }
+	if (verbose) {
+	    fprintf (stderr,"TABOPEN: %d lines in tab table %s", tabtable->nlines, tabfile);
+	    }
+	}
+
+    /* Set up line buffer, and put first line in it */
+    if (nbbuff > 0) {
+	tabtable->lhead = tabtable->tabdata - tabtable->tabheader;
+	tabtable->tcat = fcat;
+	nextline = strchr (tabtable->tabdata, newline) + 1;
+	tabtable->lline = (nextline - tabtable->tabdata) * 2;
+	tabtable->tabline = (char *) calloc (tabtable->lline, 1);
+	fseek (tabtable->tcat, (long) tabtable->lhead, SEEK_SET);
+	(void) fgets (tabtable->tabline, tabtable->lline, tabtable->tcat);
+	lline = strlen (tabtable->tabline);
+	if (tabtable->tabline[lline-1] < 32)
+	     tabtable->tabline[lline-1] = (char) 0;
+	tabtable->tabdata = tabtable->tabline;
+	}
+
+    /* Close catalog file if not reading one line at a time */
+    else {
+	tabtable->lhead = 0;
+	(void) fclose (fcat);
+	tabtable->tcat = NULL;
+	}
+
+    tabtable->tabline = tabtable->tabdata;
+    tabtable->iline = 1;
+    if (tabcomma != NULL) *tabcomma = ',';
+    return (tabtable);
+}
+
+
+void
+tabclose (tabtable)
+
+    struct TabTable *tabtable;
+{
+    if (tabtable != NULL) {
+	if (tabtable->filename != NULL) free (tabtable->filename);
+	if (tabtable->tabname != NULL) free (tabtable->tabname);
+	if (tabtable->tabbuff != NULL) free (tabtable->tabbuff);
+	if (tabtable->colname != NULL) free (tabtable->colname);
+	if (tabtable->lcol != NULL) free (tabtable->lcol);
+	if (tabtable->lcfld != NULL) free (tabtable->lcfld);
+	if (tabtable->tcat != NULL) fclose (tabtable->tcat);
+	free (tabtable);
+	}
+    return;
+}
+
+
+/* TABLINE -- Get tab table entry for one line;
+	      return NULL if unsuccessful */
+
+char *
+gettabline (tabtable, iline)
+
+struct TabTable *tabtable;	/* Tab table structure */
+int iline;	/* Line sequence number in tab table */
+{
+    char *nextline = tabtable->tabline;
+    char *next;
+    int lline, i;
+
+    /* Return NULL if tab table has not been opened */
+    if (tabtable == NULL)
+	return (NULL);
+
+    /* Read one line at a time if file is still open */
+    if (tabtable->tcat != NULL) {
+
+	/* Return current line from buffer */
+	if (iline == tabtable->iline)
+	    return (tabtable->tabline);
+
+	/* Read next line from file */
+	if (iline < 1 || iline > tabtable->iline) {
+	    for (i = tabtable->iline; i < iline; i++) {
+		next = fgets (tabtable->tabline, tabtable->lline, tabtable->tcat);
+		if (next == NULL || *next == EOF)
+		    return (NULL);
+		tabtable->iline++;
+		}
+	    lline = strlen (tabtable->tabline);
+	    if (lline < 2)
+		return (NULL);
+	    if (tabtable->tabline[lline-1] < 32)
+		tabtable->tabline[lline-1] = (char) 0;
+	    }
+	else if (iline < tabtable->iline) {
+	    fseek (tabtable->tcat, (long) tabtable->lhead, SEEK_SET);
+	    tabtable->iline = 0;
+	    for (i = tabtable->iline; i < iline; i++) {
+		(void) fgets (tabtable->tabline, tabtable->lline, tabtable->tcat);
+		tabtable->iline++;
+		}
+	    lline = strlen (tabtable->tabline);
+	    if (tabtable->tabline[lline-1] < 32)
+		tabtable->tabline[lline-1] = (char) 0;
+	    }
+	return (tabtable->tabline);
+	}
+
+    /* Return NULL if trying to read past last line */
+    if (iline > tabtable->nlines) {
+	fprintf (stderr, "TABLINE:  line %d is not in table\n",iline);
+	return (NULL);
+	}
+
+    /* If iline is 0 or less, just read next line from table */
+    else if (iline < 1 && nextline) {
+	tabtable->iline++;
+	if (tabtable->iline > tabtable->nlines) {
+	    fprintf (stderr, "TABLINE:  line %d is not in table\n",iline);
+	    return (NULL);
+	    }
+	nextline = strchr (nextline, newline) + 1;
+	}
+
+    /* If iline is before current line, read from start of file */
+    else if (iline < tabtable->iline) {
+	tabtable->iline = 1;
+	tabtable->tabline = tabtable->tabdata;
+	while (tabtable->iline < iline) {
+	    tabtable->tabline = strchr (tabtable->tabline, newline) + 1;
+	    tabtable->iline ++;
+	    }
+	}
+    /* If iline is after current line, read forward */
+    else if (iline > tabtable->iline) {
+	while (tabtable->iline < iline) {
+	    tabtable->tabline = strchr (tabtable->tabline, newline) + 1;
+	    tabtable->iline ++;
+	    }
+	}
+
+    return (tabtable->tabline);
+}
+
+
+/* TABGETRA -- returns double right ascension in degrees */
+
+double
+tabgetra (tabtok, ientry)
+
+struct Tokens *tabtok;	/* Line token structure */
+int	ientry;	/* sequence of entry on line */
+{
+    char str[24];
+
+    strcpy (str, "0.0");
+    if (tabgetc (tabtok, ientry, str, 24))
+	return (0.0);
+    else
+	return (str2ra (str));
+}
+
+
+/* TABGETDEC -- returns double declination in degrees */
+
+double
+tabgetdec (tabtok, ientry)
+
+struct Tokens *tabtok;	/* Line token structure */
+int	ientry;		/* sequence of entry on line */
+{
+    char str[24];
+
+    strcpy (str, "0.0");
+    if (tabgetc (tabtok, ientry, str, 24))
+	return (0.0);
+    else
+	return (str2dec (str));
+}
+
+
+/* TABGETR8 -- returns 8-byte floating point number from tab table line */
+
+double
+tabgetr8 (tabtok, ientry)
+
+struct Tokens *tabtok;	/* Line token structure */
+int	ientry;		/* sequence of entry on line */
+{
+    char str[24];
+
+    strcpy (str, "0.0");
+    if (tabgetc (tabtok, ientry, str, 24))
+	return (0.0);
+    else if (isnum (str))
+	return (atof (str));
+    else
+	return (0.0);
+}
+
+
+/* TABGETI4 -- returns a 4-byte integer from tab table line */
+
+int
+tabgeti4 (tabtok, ientry)
+
+struct Tokens *tabtok;	/* Line token structure */
+int	ientry;		/* sequence of entry on line */
+{
+    char str[24];
+
+    strcpy (str, "0");
+    if (tabgetc (tabtok, ientry, str, 24))
+	return (0);
+    else if (isnum (str))
+	return ((int) atof (str));
+    else
+	return (0);
+}
+
+
+/* TABGETK -- returns a character entry from tab table line for named column */
+
+int
+tabgetk (tabtable, tabtok, keyword, string, maxchar)
+
+struct TabTable *tabtable;	/* Tab table structure */
+struct Tokens *tabtok;		/* Line token structure */
+char	*keyword;		/* column header of desired value */
+char	*string;		/* character string (returned) */
+int	maxchar;	/* Maximum number of characters in returned string */
+{
+    int ientry = tabccol (tabtable, keyword);
+
+    return (tabgetc (tabtok, ientry, string, maxchar));
+}
+
+
+/* TABGETC -- returns n'th entry from tab table line as character string */
+
+int
+tabgetc (tabtok, ientry, string, maxchar)
+
+struct Tokens *tabtok;	/* Line token structure */
+int	ientry;		/* Sequence of entry on line (1-ncol) */
+char	*string;	/* Character string (returned) */
+int	maxchar;	/* Maximum number of characters in returned string */
+{
+
+    if (ientry > tabtok->ntok)
+	return (0);
+    else if (getoken (tabtok, ientry, string, maxchar))
+	return (0);
+    else
+	return (-1);
+}
+
+
+/* TABHGETR8 -- read an 8-byte floating point number from a tab table header */
+
+static int
+tabhgetr8 (tabtable, keyword, result)
+
+struct TabTable *tabtable;	/* Tab table structure */
+char	*keyword;		/* sequence of entry on line */
+double	*result;
+{
+    char value[24];
+
+    if (tabhgetc (tabtable, keyword, value)) {
+	*result = atof (value);
+	return (1);
+	}
+    else
+	return (0);
+}
+
+
+/* TABHGETI4 -- read a 4-byte integer from a tab table header */
+
+static int
+tabhgeti4 (tabtable, keyword, result)
+
+struct TabTable *tabtable;	/* Tab table structure */
+char	*keyword;		/* sequence of entry on line */
+int	*result;
+{
+    char value[24];
+
+    if (tabhgetc (tabtable, keyword, value)) {
+	*result  = (int) atof (value);
+	return (1);
+	}
+    else
+	return (0);
+}
+
+
+/* TABHGETC -- read a string from a tab table header */
+
+static int
+tabhgetc (tabtable, keyword, result)
+
+struct TabTable *tabtable;	/* Tab table structure */
+char	*keyword;		/* sequence of entry on line */
+char	*result;
+{
+    char *str0, *str1, *line, *head, keylow[24], keyup[24];
+    int ncstr, lkey, i;
+
+    head = tabtable->tabbuff;
+    str0 = 0;
+
+    /* Make all-upper-case and all-lower-case versions of keyword */
+    lkey = strlen (keyword);
+    if (lkey > 24) lkey = 24;
+    for (i = 0; i < lkey; i++) {
+	if (keyword[i] > 96 && keyword[i] < 123)
+	    keyup[i] = keyword[i] - 32;
+	else
+	    keyup[i] = keyword[i];
+	if (keyword[i] > 64 && keyword[i] < 91)
+	    keylow[i] = keyword[i] + 32;
+	else
+	    keylow[i] = keyword[i];
+	}
+    keyup[lkey] = (char) 0;
+    keylow[lkey] = (char) 0;
+
+    /* Find keyword or all-upper-case or all-lower-case version in header */
+    while (head < tabtable->tabhead) {
+	line = strsrch (head, keyword);
+	if (line == NULL)
+	    line = strsrch (head, keylow);
+	if (line == NULL)
+	    line = strsrch (head, keyup);
+	if (line == NULL)
+	    break;
+	if (line == tabtable->tabbuff || line[-1] == newline) {
+	    str0 = strchr (line, tab) + 1;
+	    str1 = strchr (str0, newline);
+	    break;
+	    }
+	else
+	    head = line + 1;
+	}
+
+    /* Return value as a character string and 1 if found */
+    if (str0) {
+	ncstr = str1 - str0;
+	strncpy (result, str0, ncstr);
+	result[ncstr] = (char)0;
+	return (1);
+	}
+    else
+	return (0);
+}
+
+
+/* TABPARSE -- Make a table of column headings */
+
+int
+tabparse (tabtable)
+
+struct TabTable *tabtable;	/* Tab table structure */
+{
+    char *colhead;	/* Column heading first character */
+    char *endcol;	/* Column heading last character */
+    char *headlast;
+    char *hyphens;
+    char *hyphlast;
+    char *nextab;
+    int icol;
+    int nbytes, nba;
+
+    /* Return if no column names in header */
+    headlast = strchr (tabtable->tabhead, newline);
+    if (headlast == tabtable->tabhead)
+	return (0);
+
+    /* Count columns in table header */
+    tabtable->ncols = 1;
+    for (colhead = tabtable->tabhead; colhead < headlast; colhead++) {
+	if (*colhead == tab)
+	    tabtable->ncols++;
+	}
+
+    /* Tabulate column names */
+    nbytes = tabtable->ncols * sizeof (char *);
+    nba = nbytes / 64;
+    if (nbytes > nba * 64)
+	nba = (nba + 1) * 64;
+    else
+	nba = nba * 64;
+    tabtable->colname = (char **)calloc (tabtable->ncols, sizeof (char *));
+    tabtable->lcol = (int *) calloc (tabtable->ncols, sizeof (int));
+    colhead = tabtable->tabhead;
+    for (icol = 0; icol < tabtable->ncols; icol++) {
+	nextab = strchr (colhead, tab);
+	if (nextab < headlast)
+	    endcol = nextab - 1;
+	else
+	    endcol = headlast - 1;
+	while (*endcol == ' ')
+	    endcol = endcol - 1;
+	tabtable->lcol[icol] = (int) (endcol - colhead) + 1;
+	tabtable->colname[icol] = colhead;
+	colhead = nextab + 1;
+	if (colhead > headlast)
+	    break;
+	}
+
+    /* Tabulate field widths */
+    hyphens = headlast + 1;
+    hyphlast = strchr (hyphens, newline);
+    if (hyphlast == hyphens)
+	return (0);
+    tabtable->lcfld = (int *) calloc (tabtable->ncols, sizeof (int));
+    colhead = hyphens;
+    for (icol = 0; icol < tabtable->ncols; icol++) {
+	if ((nextab = strchr (colhead, tab)) == NULL)
+	    endcol = hyphlast - 1;
+	else
+	    endcol = nextab - 1;
+	tabtable->lcfld[icol] = (int) (endcol - colhead) + 1;
+	if (nextab != NULL)
+	    colhead = nextab + 1;
+	else
+	    break;
+	}
+
+    return (tabtable->ncols);
+}
+
+
+/* Search table of column headings for a particlar entry (case-dependent) */
+
+int
+tabcol (tabtable, keyword)
+
+struct TabTable *tabtable;	/* Tab table structure */
+char	*keyword;		/* Column heading to find */
+
+{
+    int i, lkey, lcol;
+    lkey = strlen (keyword);
+
+    for (i = 0; i < tabtable->ncols; i++) {
+	lcol = tabtable->lcol[i];
+	if (lcol == lkey &&
+	    !strncmp (keyword, tabtable->colname[i], lcol)) {
+	    return (i + 1);
+	    }
+	}
+    return (0);
+}
+
+
+
+/* Search table of column headings for a particlar entry (case-independent) */
+
+int
+tabccol (tabtable, keyword)
+
+struct TabTable *tabtable;	/* Tab table structure */
+char	*keyword;		/* Column heading to find */
+
+{
+    int i, lkey, lcol;
+    lkey = strlen (keyword);
+
+    for (i = 0; i < tabtable->ncols; i++) {
+	lcol = tabtable->lcol[i];
+	if (lcol == lkey &&
+	    !strncasecmp (keyword, tabtable->colname[i], lcol)) {
+	    return (i + 1);
+	    }
+	}
+    return (0);
+}
+
+
+/* Search table of column headings for first with string (case-dependent) */
+
+static int
+tabcont (tabtable, keyword)
+
+struct TabTable *tabtable;	/* Tab table structure */
+char	*keyword;		/* Part of column heading to find */
+
+{
+    int i;
+
+    for (i = 0; i < tabtable->ncols; i++) {
+	if (strnsrch (tabtable->colname[i], keyword, tabtable->lcol[i])) {
+	    return (i + 1);
+	    }
+	}
+    return (0);
+}
+
+
+/* Search table of column headings for first with string (case-independent) */
+
+static int
+tabccont (tabtable, keyword)
+
+struct TabTable *tabtable;	/* Tab table structure */
+char	*keyword;		/* Part of column heading to find */
+
+{
+    int i;
+
+    for (i = 0; i < tabtable->ncols; i++) {
+	if (strncsrch (tabtable->colname[i], keyword, tabtable->lcol[i])) {
+	    return (i + 1);
+	    }
+	}
+    return (0);
+}
+
+
+
+/* TABSIZE -- return size of file in bytes */
+
+static int
+tabsize (filename)
+
+char	*filename;	/* Name of file for which to find size */
+{
+    FILE *diskfile;
+    long filesize;
+
+    /* Open file */
+    if ((diskfile = fopen (filename, "r")) == NULL)
+	return (-1);
+
+    /* Move to end of the file */
+    if (fseek (diskfile, 0, 2) == 0)
+
+ 	/* Position is the size of the file */
+	filesize = ftell (diskfile);
+
+    else
+	filesize = -1;
+
+    fclose (diskfile);
+
+    return (filesize);
+}
+
+
+/* ISTAB -- Return 1 if tab table file, else 0 */
+
+int
+istab (filename)
+
+char    *filename;      /* Name of file to check */
+{
+    struct TabTable *tabtable;
+
+    /* First check file extension */
+    if (strsrch (filename, ".tab"))
+	return (1);
+
+    /* If no .tab file extension, try opening the file */
+    else {
+	if ((tabtable = tabopen (filename, 10000)) != NULL) {
+	    tabclose (tabtable);
+	    return (1);
+	    }
+	else
+	    return (0);
+	}
+}
+
+/* Jul 18 1996	New subroutines
+ * Aug  6 1996	Remove unused variables after lint
+ * Aug  8 1996	Fix bugs in entry reading and logging
+ * Oct 15 1996  Add comparison when testing an assignment
+ * Nov  5 1996	Drop unnecessary static declarations
+ * Nov 13 1996	Return no more than maximum star number
+ * Nov 13 1996	Write all error messages to stderr with subroutine names
+ * Nov 15 1996  Implement search radius; change input arguments
+ * Nov 19 1996	Allow lower case column headings
+ * Dec 18 1996	Add UJCRNUM to read specified catalog entries
+ * Dec 18 1996  Keep closest stars, not brightest, if searching within radius
+ *
+ * Mar 20 1997	Clean up code in TABRNUM
+ * May  7 1997	Set entry number to zero if column not found
+ * May 29 1997	Add TABPARSE and TABCOL to more easily extract specific columns
+ * May 29 1997	Add TABCLOSE to free memory from outside this file
+ * Jun  4 1997	Set ID to sequence number in table if no ID/id entry present
+ *
+ * Jun  2 1998	Fix bug parsing last column of header
+ * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
+ * Sep 16 1998	Use limiting radius correctly
+ * Sep 22 1998	Convert to output coordinate system
+ * Oct 15 1998	Add tabsize() and istab()
+ * Oct 21 1998	Add tabrkey() to read values of keyword for list of stars
+ * Oct 29 1998	Correctly assign numbers when too many stars are found
+ * Oct 30 1998	Fix istab() to check only first line of file
+ * Dec  8 1998	Do not declare tabsize() static
+
+ * Jan 20 1999	Add tabcatopen() and keep table info in structure, not global
+ * Jan 25 1999	Add lcfld to structure to keep track of field widths
+ * Jan 29 1999	Add current line number and pointer to table structure
+ * Feb  2 1999	Allow for equinox other than 2000.0 in tab table header
+ * Feb  2 1999	Add tabhgetc() to read char string values from tab table header
+ * Feb 17 1999	Increase maximum line in istab() from 80 to 1024
+ * Mar  2 1999	Fix bugs calling tabhgetx()
+ * Mar  2 1999	Rewrite tabhgetx() to use tabhgetc() for all header reading
+ * May 28 1999	Add tabcatopen() and tabstar() and use them
+ * Jun  3 1999	Fix bug so header parameters are read correctly
+ * Jun 16 1999	Use SearchLim()
+ * Aug 16 1999	Fix bug to fix failure to search across 0:00 RA
+ * Aug 25 1999  Return real number of stars from tabread()
+ * Sep 10 1999	Fix bug setting equinox and coordinate system in tabcatopen()
+ * Sep 10 1999	Set additional keyword selection with subroutine
+ * Sep 13 1999	Fix comment for tabstar()
+ * Sep 16 1999	Fix bug which didn't always return closest stars
+ * Sep 16 1999	Add distsort argument so brightest stars in circle works, too
+ * Oct 21 1999	Clean up code after lint
+ * Oct 25 1999	Fix subroutine declaration inconsistency
+ * Oct 25 1999	Replace malloc() calls with calloc()
+ * Oct 29 1999	Add tabxyread() for image catalogs
+ * Nov 23 1999	Improve error checking on Starbase tables; istab() opens file
+ * Nov 30 1999	Fix bugs found when compiling under SunOS 4.1.3
+ *
+ * Jan  4 2000	Always close file and print error message on tabopen() failures
+ * Jan  6 2000	If "id" not found, try heading with "_id" to catch scat output
+ * Jan 10 2000	Add second magnitude; save column headers in catalog structure
+ * Feb 10 2000	Implement proper motion in source catalogs
+ * Feb 10 2000	Accept first mag-containing column as first magnitude
+ * Feb 10 2000	Clean up id reading: no. decimals, non-numeric id
+ * Feb 14 2000	Save table opening errors in string
+ * Feb 16 2000	Lengthen short calloc() lengths
+ * Feb 16 2000	Pad tabbuff with 2 nulls so end can be found
+ * Mar 10 2000	Return proper motions from tabread() and tabrnum()
+ * Mar 13 2000	Do not free tabtable structure if it is null
+ * Mar 27 2000	Clean up code after lint
+ * May 26 2000	Add ability to read named tables in a multi-table file
+ * Jun 26 2000	Add coordinate system to SearchLim() arguments
+ * Jul  5 2000	Check for additional column heading variations
+ * Jul 10 2000	Deal with number of decimal places and name/number in tabcatopen()
+ * Jul 12 2000	Add star catalog data structure to tabread() argument list
+ * Jul 13 2000	Use nndec header parameter to optionally set decimal places
+ * Jul 25 2000	Pass star catalog address of data structure address
+ * Aug  3 2000	Skip first character of ID if rest is number
+ * Aug  3 2000	If no decimal point in numeric ID, set ndec to zero
+ * Sep 27 2000	Use first column with name if no id column is found
+ * Oct 26 2000	Add proper motion in seconds and arcseconds per century
+ * Oct 31 2000	Add proper motion in milliseconds of time per year
+ * Nov 22 2000	Add tabtable argument to tabcatopen() for URL search returns
+ * Nov 28 2000	Add starcat as argument to tabrnum() as well as tabread()
+ * Nov 29 2000	Do not set tmagb if it is null; set type if present
+ * Nov 30 2000	Add spectral type as possible column
+ * Dec  1 2000	Add field as synonym for plate
+ * Dec 18 2000	Clean up after lint
+ * Dec 29 2000	Clean up after lint
+ *
+ * May 29 2001	Save length of identifier in catalog structure
+ * May 30 2001	Rewrite to deal with up to 3 magnitudes
+ * Jun 11 2001	Add one-line-at-a-time catalog reading from tabcatopen()
+ * Jun 14 2001	In tabopen(), use actual file size if less than buffer size
+ * Jun 14 2001	In tabcol() make sure lengths are equal, too
+ * Jun 18 2001	Fix bug finding of length of token string extracted
+ * Jun 19 2001	Use setoken() to parse each catalog line
+ * Jun 20 2001	Add fourth magnitude for GSC II
+ * Jun 25 2001	Check fgets() return in gettabline()
+ * Jun 25 2001	Print star read errors only in verbose mode
+ * Jun 28 2001	If no proper motion unit is set, set proper motion to 0.0
+ * Jun 28 2001	If first magnitude is 99.90, sort by second magnitude
+ * Jun 28 2001	Up default line limit from 1 million to 10 million
+ * Jul  2 2001	Fix order of 3rd and 4th magnitudes
+ * Jul 20 2001	Return magnitude as well as flux from tabxyread()
+ * Aug  8 2001	Return radial velocity as additional magnitude
+ * Aug 17 2001	Fix bug reading radial velocity
+ * Aug 21 2001	Check numbers using isnum() in tabgetr8() and tabgeti4()
+ * Aug 21 2001	Add starcat to tabrkey() argument list
+ * Aug 21 2001	Read object name into tkey if it is present
+ * Sep 11 2001	Allow an arbitrary number of magnitudes
+ * Sep 11 2001  Add sort magnitude argument to tabread()
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ * Sep 20 2001	Clean up pointers for Alpha and Linux; drop tabgetpm()
+ * Sep 20 2001	Change _ to . in ESO server USNO-A2.0 numbers
+ * Sep 21 2001	Rearrange ESO server GSC numbers
+ * Oct 12 2001	Check for additional column names for magnitude
+ * Oct 15 2001	Read first star only once, compute relative flux in tabxyread() (bug fix)
+ * Oct 16 2001	Add pointer to line of dashes to table structure
+ * Dec  3 2001	Initialize keyword search variables in tabhgetc()
+ *
+ * May  6 2002	Allow object names to be up to 79 characters long
+ * Aug  5 2002	Deal correctly with magnitude-less catalogs
+ * Aug  5 2002	Add magu, magb, magv for UBV magnitudes
+ * Aug  6 2002	Pass through magnitude keywords
+ * Aug  6 2002	Return initial string if token not found by tabgetc()
+ * Oct 30 2002	Add code to pass epoch as a final magnitude (but not RV, yet)
+ *
+ * Jan 26 2003	Add code to pass USNO-B1.0 pm quality and no. of ids
+ * Jan 28 2003	Improve spatial position test
+ * Feb  4 2003	Compare character to 0, not NULL
+ * Mar 11 2003	Fix limit setting
+ * Apr  3 2003	Drop unused variables after lint
+ * May 28 2003	Read long and lat if radecsys is "galactic" or "ecliptic"
+ * Jun  2 003	Divide by cos(dec) for arcsec and mas RA proper motions
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Sep 25 2003	Add tabbin() to fill an image with sources
+ * Oct  6 2003	Update tabread() and tabbin() for improved RefLim()
+ * Nov 18 2003	Initialize image size and bits/pixel from header in tabbin()
+ * Nov 22 2003	Add GSC II object class (c) as possible content for tpeak
+ * Dec  4 2003	Add default of arcsec/century as proper motion unit
+ * Dec 10 2003	If magnitude ends in L and is a number, add 100.0
+ * Dec 12 2003	Fix bug in wcs2pix() call in tabbin()
+ *
+ * Jan  5 2004	If more than 15 digits in numberic ID, drop excess off front
+ * Mar 16 2004	Be more clever about reading by number
+ * Mar 19 2004	Make verbose flag global
+ * Nov 17 2004	Accept SpT and spt before type for spectral type
+ *
+ * May 12 2005	Add "num" as a possible substring to identify an ID column
+ * Jul 26 2005	Fix bug to set magnitudes and keywords correctly in tabrnum()
+ * Aug  3 2005	If nlog < 0 in tabrnum(), print objects as found
+ * Aug 10 2005	Add "exp" as possible "magnitude" column heading
+ * Aug 11 2005	Do not re-read lines if missing object numbers in tabrnum()
+ * Aug 17 2005	If nmax is -1, print magnitude keywords from file
+ * Sep 29 2005	Read first 10000 characters for istab() to capture long heads
+ * Sep 29 2005	Add rasort header flag if catalog is sorted by RA
+ *
+ * Jun 15 2006	Read ID field length from header, if present
+ * Jun 20 2006	Drop unused variables; initialize uninitialized variables
+ * Jun 30 2006	Add match argument to tabrnum() to add sequential read option
+ *
+ * Jan 11 2007	Switch order of wcscat.h and fitsfile.h includes
+ * Mar 13 2007	Return object name if first character of ID is non-numeric
+ * Mar 13 2007	Check for "Class" for GSC2 object class
+ * Jul  9 2007	Reject mflag as a magnitude
+ * Jul  9 2007	Sort by designated magnitude only unless sort mag is 0
+ * Jul 18 2007	Add tabccol() and tabccont() case-insensitive header searches
+ * Jul 19 2007	Add proper motion in arsec/hour for solar system objects
+ * Jul 23 2007	Add ...obj... as possible identifier
+ * Jul 23 2007	Add ...dist... as possible "magnitude"
+ *
+ * Aug 17 2009	Fix columns for declination column name
+ * Sep 25 2009	Fix memory leaks found by Douglas Burke
+ * Sep 30 2009	Fix bugs freeing object names for first pass and farthest star
+ */
diff --git a/Code/src/libwcs/tabsort.c b/Code/src/libwcs/tabsort.c
new file mode 100644
index 0000000000000000000000000000000000000000..4f2764b9b1593dc134ecc700543bc6fbdf2aacd6
--- /dev/null
+++ b/Code/src/libwcs/tabsort.c
@@ -0,0 +1,104 @@
+/* void TabSort()               Sort table based on possible sexigesimal entry
+ * int SortTab()                Return lowest of two entries
+ */
+
+/* Sort catalog by increasing angle, returning sorted buffer */
+
+/* Structure for sorting lines */
+typedef struct {
+    double ang;		/* Selected angle */
+    char *entry;	/* Entire entry */
+} TabInfo;
+
+char *
+SortTab (buffer, isort);
+
+char	*buffer;	/* White-space separated table */
+int	isort;		/* Column by which to sort */
+
+{
+    TabInfo *table;
+    char line[500];
+    char token[32];
+    char *bufline;
+    char *buffout;
+    char newline = 10;
+    char czero;
+    char *cwhite;
+    int TabSort ();
+    int nlines = 0;
+    int i;
+    int ntok;
+    struct Tokens tokens;   /* Token structure */
+
+    if (buffer == NULL)
+	return;
+
+    cwhite = NULL;
+    czero = (char) 0;
+
+    /* Count lines in file */
+    bufline = buffer;
+    nlines = 0;
+    while ((bufline = strchr (bufline, newline)) != NULL) {
+	bufline = bufline + 1;
+	nlines++;
+	}
+
+    /* Allocate array for lines of table */
+    table = (TabInfo *) calloc ((unsigned int)nlines, sizeof(TabInfo));
+
+   /* Fill out data structure of lines and indices */
+    line1 = buffer;
+    for (i = 0; i < 500; i++)
+	line[i] = czero;
+    i = 0;
+    while ((line2 = strchr (bufline, newline)) != NULL) {
+	nchar = line2 - line1 + 1;
+	table[i]->entry = (char *)calloc (1, nchar);
+	line = table[i]->entry;
+	strncpy (line, line1, nchar);
+	line[nchar] = czero;
+	ntok = setoken (&tokens, line, cwhite);
+	if (getoken(&tokens, itok, token, 32)) {
+	    if (strchr (token, ':')
+		table[i]->ang = str2dec (token);
+	    else
+		table[i]->ang = atof (token);
+	i++;
+	}
+
+    qsort ((char *)table, nlines, sizeof(TabInfo), TabSort);
+
+    /* Allocate output buffer the same size as input buffer */
+    lbuff = strlen (buffer) + 1;
+    buffout = (char *) calloc (lbuff, 1);
+    strcpy (buffout, table[0]->entry);
+    for (i = 1; i < nlines; i++)
+	strcat (buffout, table[i]->entry);
+	free (table[i]->entry);
+	}
+    free (table);
+    return (buffout);
+}
+
+
+/* TabSort -- Order stars in decreasing flux called by qsort */
+
+int
+TabSort (ssp1, ssp2)
+
+void *ssp1, *ssp2;
+
+{
+    double b1 = ((TabInfo *)ssp1)->ang;
+    double b2 = ((TabInfo *)ssp2)->ang;
+
+    if (b2 > b1)
+	return (1);
+    else if (b2 < b1)
+	return (-1);
+    else
+	return (0);
+}
+
diff --git a/Code/src/libwcs/tmcread.c b/Code/src/libwcs/tmcread.c
new file mode 100644
index 0000000000000000000000000000000000000000..6d2f710e4b83d6a0c55336b8c2796884bd169d6f
--- /dev/null
+++ b/Code/src/libwcs/tmcread.c
@@ -0,0 +1,1538 @@
+/*** File libwcs/tmcread.c
+ *** September 28, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2001-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include "fitsfile.h"
+#include "wcs.h"
+#include "wcscat.h"
+
+#define MAXREG 1800
+
+#define MINRA 1
+#define MAXRA 2
+
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+
+/* pathname of 2MASS point source catalog root directory
+   or catalog search engine URL */
+char tmc2path[64]="/data/astrocat/2MASS";
+char tmcapath[64]="/data/astrocat/tmc";
+char tmcepath[64]="/data/astrocat2/tmce";
+char tmxpath[64]="/data/astrocat/tmx";
+char *tmcpath;
+
+static double *gdist;	/* Array of distances to stars */
+static int ndist = 0;
+static int linedump = 0;
+static char *catfile = NULL;
+
+static int tmcreg();
+struct StarCat *tmcopen();
+void tmcclose();
+static int tmcstar();
+static int tmcsdec();
+static int tmcsra();
+
+/* TMCREAD -- Read 2MASS catalog stars from disk files */
+
+int
+tmcread (refcat,cra,cdec,dra,ddec,drad,dradi,distsort,sysout,eqout,epout,
+	 mag1,mag2,sortmag,nstarmax,gnum,gra,gdec,gmag,gtype,nlog)
+
+int	refcat;		/* Code for catalog file from wcscat.h */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	distsort;	/* 1 to sort stars by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*gnum;		/* Array of catalog numbers (returned) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double	**gmag;		/* Array of visual magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    double dist = 0.0;  /* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    int	faintstar=0;	/* Faintest star */
+    int	farstar=0;	/* Most distant star */
+    int nreg = 0;	/* Number of 2MASS point source regions in search */
+    int rlist[MAXREG];	/* List of regions */
+    char inpath[128];	/* Pathname for input region file */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    double size = 0.0;		/* Semi-major axis of extended source */
+    struct StarCat *starcat;
+    struct Star *star;
+    int verbose;
+    int wrap;
+    int pass;
+    int ireg;
+    int imag, nmag;
+    int jstar, iw;
+    int zone;
+    int magsort;
+    int nrmax = MAXREG;
+    int nstar,i, ntot;
+    int istar, istar1, istar2;
+    double num, ra, dec, mag;
+    double rra1, rra2, rra2a, rdec1, rdec2;
+    double rdist, ddist;
+    char cstr[32], rastr[32], decstr[32], numstr[32];
+    char *str;
+    char tmcenv[16];
+
+    /* Choose appropriate catalog to read */
+    if (refcat == TMIDR2) {
+	tmcpath = tmc2path;
+	strcpy (tmcenv, "TMCIDR2_PATH");
+	nmag = 3;
+	}
+    else if (refcat == TMXSC) {
+	tmcpath = tmxpath;
+	strcpy (tmcenv, "TMX_PATH");
+	nmag = 3;
+	}
+    else if (refcat == TMPSCE) {
+	tmcpath = tmcepath;
+	strcpy (tmcenv, "TMCE_PATH");
+	nmag = 6;
+	}
+    else {
+	tmcpath = tmcapath;
+	strcpy (tmcenv, "TMC_PATH");
+	nmag = 3;
+	}
+
+    ntot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else if (nlog < 0) {
+	linedump = 0;
+	verbose = 0;
+	}
+    else
+	verbose = 0;
+
+    /* If pathname is set in environment, override local value */
+    if ((str = getenv(tmcenv)) != NULL )
+	tmcpath = str;
+
+    /* If pathname is a URL, search and return */
+    if (!strncmp (tmcpath, "http:",5)) {
+	if (catfile == NULL)
+	    catfile = (char *) calloc (8, 1);
+	if (refcat == TMPSC)
+	    strcpy (catfile, "tmc");
+	else if (refcat == TMPSCE)
+	    strcpy (catfile, "tmce");
+	else if (refcat == TMXSC)
+	    strcpy (catfile, "tmx");
+	else if (refcat == TMIDR2)
+	    strcpy (catfile, "tmidr2");
+	return (webread (str,catfile,distsort,cra,cdec,dra,ddec,drad,dradi,
+			 sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,
+			 gnum,gra,gdec,NULL,NULL,gmag,gtype,nlog));
+	}
+    wcscstr (cstr, sysout, eqout, epout);
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* If RA range includes zero, split it in two */
+    wrap = 0;
+    if (ra1 > ra2)
+	wrap = 1;
+    else
+	wrap = 0;
+
+    /* make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Allocate table for distances of stars from search center */
+    if (nstarmax > ndist) {
+	if (ndist > 0)
+	    free ((void *)gdist);
+	gdist = (double *) malloc (nstarmax * sizeof (double));
+	if (gdist == NULL) {
+	    fprintf (stderr,"TMCREAD:  cannot allocate separation array\n");
+	    return (0);
+	    }
+	ndist = nstarmax;
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    nstar = 0;
+    jstar = 0;
+
+    if (sortmag > 0 && sortmag < 4)
+	magsort = sortmag - 1;
+    else 
+	magsort = 0;
+
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,0.0,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+    if (wrap) {
+	rra2a = rra2;
+	rra2 = 360.0;
+	}
+    else
+	rra2a = 0.0;
+
+    /* Write header if printing star entries as found */
+    if (nstarmax < 1) {
+	char *revmessage;
+	revmessage = getrevmsg();
+	if (refcat == TMXSC)
+	    printf ("catalog	2MASS Extended Source Catalog\n");
+	else
+	    printf ("catalog	2MASS Point Source Catalog\n");
+	ra2str (rastr, 31, cra, 3);
+	printf ("ra	%s\n", rastr);
+	dec2str (decstr, 31, cdec, 2);
+	printf ("dec	%s\n", decstr);
+	if (drad != 0.0) {
+	    printf ("radmin	%.1f\n", drad*60.0);
+	    if (dradi > 0)
+		printf ("radimin	%.1f\n", dradi*60.0);
+	    }
+	else {
+	    printf ("dramin	%.1f\n", dra*60.0* cosdeg (cdec));
+	    printf ("ddecmin	%.1f\n", ddec*60.0);
+	    }
+	printf ("radecsys	%s\n", cstr);
+	printf ("equinox	%.3f\n", eqout);
+	printf ("epoch	%.3f\n", epout);
+	if (refcat == TMXSC)
+	    printf ("program	stmx %s\n", revmessage);
+	else
+	    printf ("program	stmc %s\n", revmessage);
+	if (refcat == TMXSC)
+	    printf ("2mx_id    	ra          	dec         	");
+	else
+	    printf ("2mass_id  	ra          	dec         	");
+	printf ("magj  	magh  	magk  ");
+	if (refcat == TMPSCE)
+	    printf ("magje  	maghe  	magke ");
+	if (refcat == TMXSC)
+	    printf (" 	size  ");
+	printf (" 	arcmin\n");
+	printf ("----------	------------	------------	");
+	printf ("------	------	------");
+	if (refcat == TMPSCE)
+	    printf ("------	------	------");
+	if (refcat == TMXSC)
+	    printf ("	------");
+	printf ("	------\n");
+	}
+
+    /* If searching through RA = 0:00, split search in two */
+    for (iw = 0; iw <= wrap; iw++) {
+
+	/* Find 2MASS Point Source Catalog regions in which to search */
+	nreg = tmcreg (refcat, rra1,rra2,rdec1,rdec2,nrmax,rlist,verbose);
+	if (nreg <= 0) {
+	    fprintf (stderr,"TMCREAD:  no 2MASS regions found\n");
+	    return (0);
+	    }
+
+	/* Loop through zone or region list */
+	for (ireg = 0; ireg < nreg; ireg++) {
+
+	    /* Open file for this region of 2MASS point source catalog */
+	    zone = rlist[ireg];
+	    starcat = tmcopen (refcat, zone);
+	    if (starcat == NULL) {
+		fprintf (stderr,"TMCREAD: File %s not found\n",inpath);
+		return (0);
+		}
+
+	    /* Find first and last stars in this region */
+	    if (refcat == TMPSC || refcat == TMPSCE) {
+		istar1 = tmcsra (starcat, star, zone, rra1, MINRA);
+		istar2 = tmcsra (starcat, star, zone, rra2, MAXRA);
+		}
+	    else if (refcat == TMXSC) {
+		istar1 = tmcsra (starcat, star, zone, rra1, MINRA);
+		istar2 = tmcsra (starcat, star, zone, rra2, MAXRA);
+		/* istar1 = 1;
+		istar2 = starcat->nstars; */
+		}
+	    else {
+		istar1 = tmcsdec (starcat, star, zone, rdec1);
+		istar2 = tmcsdec (starcat, star, zone, rdec2);
+		}
+	    if (verbose)
+		fprintf (stderr,"TMCREAD: Searching stars %d through %d in region %d\n",
+			istar1, istar2-1, zone);
+
+	    /* Loop through catalog for this region */
+	    for (istar = istar1; istar < istar2; istar++) {
+		if (tmcstar (starcat, star, zone, istar)) {
+		    fprintf (stderr,"TMCREAD: Cannot read star %d\n", istar);
+		    break;
+		    }
+
+		/* ID number */
+		num = star->num;
+
+		/* Magnitude */
+		mag = star->xmag[0];
+
+		/* Semi-major axis of extended source */
+		if (refcat == TMXSC)
+		    size = star->size;
+
+		/* Check magnitude limits */
+		pass = 1;
+		if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+		    pass = 0;
+
+		if (pass) {
+
+		    /* Get position in output coordinate system */
+		    ra = star->ra;
+		    dec = star->dec;
+		    wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+
+		    /* Compute distance from search center */
+		    if (drad > 0 || distsort)
+			dist = wcsdist (cra,cdec,ra,dec);
+		    else
+			dist = 0.0;
+
+		    /* Check radial distance to search center */
+		    if (drad > 0) {
+			if (dist > drad)
+			    pass = 0;
+			if (dradi > 0.0 && dist < dradi)
+			    pass = 0;
+			}
+
+		    /* Check distance along RA and Dec axes */
+		    else {
+			ddist = wcsdist (cra,cdec,cra,dec);
+			if (ddist > ddec)
+			    pass = 0;
+			rdist = wcsdist (cra,dec,ra,dec);
+		        if (rdist > dra)
+			   pass = 0;
+			}
+		    }
+
+		if (pass) {
+
+		    /* Write star position and magnitudes to stdout */
+		    if (nstarmax < 1) {
+			CatNum (TMPSC, -10, 0, num, numstr);
+			ra2str (rastr, 31, ra, 3);
+			dec2str (decstr, 31, dec, 2);
+			dist = wcsdist (cra,cdec,ra,dec) * 60.0;
+                        printf ("%s	%s	%s", numstr,rastr,decstr);
+			for (imag = 0; imag < 3; imag++) {
+			    if (star->xmag[imag] > 100.0)
+				printf ("	%.3fL", star->xmag[imag]-100.0);
+			    else
+				printf ("	%.3f ", star->xmag[imag]);
+			    }
+			if (refcat == TMPSCE) {
+			    for (imag = 3; imag < 6; imag++) {
+				printf ("	%.3f ", star->xmag[imag]);
+				}
+			    }
+			if (refcat == TMXSC)
+			    printf ("	%.1f", size);
+			printf ("	%.2f\n", dist);
+			}
+
+		    /* Save star position and magnitudes in table */
+		    else if (nstar < nstarmax) {
+			gnum[nstar] = num;
+			gra[nstar] = ra;
+			gdec[nstar] = dec;
+			for (imag = 0; imag < nmag; imag++) {
+			    if (gmag[imag] != NULL)
+				gmag[imag][nstar] = star->xmag[imag];
+			    }
+			if (refcat == TMXSC)
+			    gtype[nstar] = (int) ((size + 0.05) * 10.0);
+			else
+			    gtype[nstar] = 0;
+			gdist[nstar] = dist;
+			if (dist > maxdist) {
+			    maxdist = dist;
+			    farstar = nstar;
+			    }
+			if (mag > faintmag) {
+			    faintmag = mag;
+			    faintstar = nstar;
+			    }
+			}
+
+		    /* If too many stars and distance sorting,
+		       replace farthest star */
+		    else if (distsort) {
+			if (dist < maxdist) {
+			    gnum[farstar] = num;
+			    gra[farstar] = ra;
+			    gdec[farstar] = dec;
+			    for (imag = 0; imag < nmag; imag++) {
+				if (gmag[imag] != NULL)
+				    gmag[imag][farstar] = star->xmag[imag];
+				}
+			    if (refcat == TMXSC)
+				gtype[farstar] = (int) ((size + 0.05) * 10.0);
+			    else
+				gtype[farstar] = 0;
+			    gdist[farstar] = dist;
+
+			    /* Find new farthest star */
+			    maxdist = 0.0;
+			    for (i = 0; i < nstarmax; i++) {
+				if (gdist[i] > maxdist) {
+				    maxdist = gdist[i];
+				    farstar = i;
+				    }
+				}
+			    }
+			}
+
+		    /* Else if too many stars, replace faintest star */
+		    else if (mag < faintmag) {
+			gnum[faintstar] = num;
+			gra[faintstar] = ra;
+			gdec[faintstar] = dec;
+			for (imag = 0; imag < nmag; imag++) {
+			    if (gmag[imag] != NULL)
+				gmag[imag][faintstar] = star->xmag[imag];
+			    }
+			if (refcat == TMXSC)
+			    gtype[faintstar] = (int) ((size + 0.05) * 10.0);
+			else
+			    gtype[faintstar] = 0;
+			gdist[faintstar] = dist;
+			faintmag = 0.0;
+
+			/* Find new faintest star */
+			for (i = 0; i < nstarmax; i++) {
+			    if (gmag[magsort][i] > faintmag) {
+				faintmag = gmag[magsort][i];
+				faintstar = i;
+				}
+			    }
+			}
+
+		    nstar++;
+		    if (nlog == 1)
+			fprintf (stderr,"TMCREAD: %11.6f: %9.5f %9.5f %5.2f %5.2f %5.2f\n",
+				 num,ra,dec,star->xmag[0],star->xmag[1],star->xmag[2]);
+
+		    /* End of accepted star processing */
+		    }
+
+		/* Log operation */
+		jstar++;
+		if (nlog > 0 && istar%nlog == 0)
+		    fprintf (stderr,"TMCREAD: %5d / %5d / %5d sources\r",
+			     nstar,jstar,starcat->nstars);
+
+		/* End of star loop */
+		}
+
+	    ntot = ntot + starcat->nstars;
+	    if (nlog > 0)
+		fprintf (stderr,"TMCREAD: %4d / %4d: %5d / %5d  / %5d sources from region %4d    \n",
+		 	 ireg+1,nreg,nstar,jstar,starcat->nstars,zone);
+
+	    /* Close region input file */
+	    tmcclose (starcat);
+	    }
+	rra1 = 0.0;
+	rra2 = rra2a;
+	}
+
+/* close output file and summarize transfer */
+    if (nlog > 0) {
+	if (nreg > 1)
+	    fprintf (stderr,"TMCREAD: %d regions: %d / %d found\n",nreg,nstar,ntot);
+	else
+	    fprintf (stderr,"TMCREAD: 1 region: %d / %d found\n",nstar,ntot);
+	if (nstar > nstarmax)
+	    fprintf (stderr,"TMCREAD: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+    return (nstar);
+}
+
+/* TMCRNUM -- Read HST Guide Star Catalog stars from CDROM */
+
+int
+tmcrnum (refcat,nstars,sysout,eqout,epout,gnum,gra,gdec,gmag,gtype,nlog)
+
+int	refcat;		/* Code for catalog file from wcscat.h */
+int	nstars;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*gnum;		/* Array of source numbers for which to search */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double	**gmag;		/* 2-D array of magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    char inpath[128];	/* Pathname for input region file */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    struct StarCat *starcat;
+    struct Star *star;
+    char *str;
+
+    int rnum;
+    int jstar;
+    int imag, nmag;
+    int istar, nstar;
+    double num, ra, dec, rapm, decpm, dstar;
+    char tmcenv[16];
+
+    /* Choose appropriate catalog */
+    if (refcat == TMIDR2) {
+	tmcpath = tmc2path;
+	strcpy (tmcenv, "TMCIDR2_PATH");
+	nmag = 3;
+	}
+    else if (refcat == TMXSC) {
+	tmcpath = tmxpath;
+	strcpy (tmcenv, "TMX_PATH");
+	nmag = 3;
+	}
+    else if (refcat == TMPSCE) {
+	tmcpath = tmcepath;
+	strcpy (tmcenv, "TMCE_PATH");
+	nmag = 6;
+	}
+    else {
+	tmcpath = tmcapath;
+	strcpy (tmcenv, "TMC_PATH");
+	nmag = 3;
+	}
+
+    if (nlog < 0)
+	linedump = 1;
+
+    /* If pathname is set in environment, override local value */
+    if ((str = getenv(tmcenv)) != NULL )
+	tmcpath = str;
+
+    /* If pathname is a URL, search and return */
+    if (!strncmp (tmcpath, "http:",5))
+	return (webrnum (tmcpath,"tmc",nstars,sysout,eqout,epout,1,
+			 gnum,gra,gdec,NULL,NULL,gmag,gtype,nlog));
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+    nstar = 0;
+
+    /* Loop through star list */
+    for (jstar = 0; jstar < nstars; jstar++) {
+	rnum = (int) (gnum[jstar] + 0.0000000001);
+	starcat = tmcopen (refcat, rnum);
+    	if (starcat == NULL) {
+	    fprintf (stderr,"TMCRNUM: File %s not found\n",inpath);
+	    return (0);
+	    }
+	if (refcat == TMIDR2)
+	    dstar = (gnum[jstar] - (double)rnum) * 10000000.0;
+	else
+	    dstar = (gnum[jstar] - (double)rnum) * 1000000.0;
+	istar = (int) (dstar + 0.5);
+	if (tmcstar (starcat, star, rnum, istar)) {
+	    fprintf (stderr,"TMCRNUM: Cannot read star %d\n", istar);
+	    gra[jstar] = 0.0;
+	    gdec[jstar] = 0.0;
+	    for (imag = 0; imag < nmag; imag++) {
+		gmag[imag][jstar] = 0.0;
+		}
+	    gtype[jstar] = 0;
+	    continue;
+	    }
+
+	/* If star has been found in catalog */
+
+	/* ID number */
+	num = star->num;
+
+	/* Position in degrees at designated epoch */
+	ra = star->ra;
+	dec = star->dec;
+	rapm = star->rapm;
+	decpm = star->decpm;
+	wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		     &ra, &dec, &rapm, &decpm);
+
+	/* Save star position and magnitude in table */
+	gnum[jstar] = num;
+	gra[jstar] = ra;
+	gdec[jstar] = dec;
+	for (imag = 0; imag < nmag; imag++) {
+	    gmag[imag][jstar] = star->xmag[imag];
+	    }
+	if (refcat == TMXSC)
+	    gtype[jstar] = (int) ((star->size * 10.0) + 0.5);
+	else
+	    gtype[jstar] = 0;
+	if (nlog == 1) {
+	    fprintf (stderr,"TMCRNUM: %11.6f: %9.5f %9.5f %5.2f %5.2f %5.2f",
+		     num, ra, dec, star->xmag[0],star->xmag[1],star->xmag[2]);
+	    if (nmag > 3) {
+		fprintf (stderr," %5.2f %5.2f %5.2f",
+			 star->xmag[3],star->xmag[4],star->xmag[5]);
+		}
+	    fprintf (stderr, "\n");
+	    }
+
+	/* End of star loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"TMCRNUM: %d / %d found\n",nstar, nstars);
+
+    tmcclose (starcat);
+    return (nstars);
+}
+
+
+/* TMCBIN -- Fill FITS WCS image with 2MASS point source catalog objects */
+
+int
+tmcbin (refcat, wcs, header, image, mag1, mag2, sortmag, magscale, nlog)
+
+int	refcat;		/* Code for catalog file from wcscat.h */
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int	sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    int nreg = 0;	/* Number of 2MASS point source regions in search */
+    int rlist[MAXREG];	/* List of regions */
+    char inpath[128];	/* Pathname for input region file */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    struct StarCat *starcat;
+    struct Star *star;
+    int verbose;
+    int wrap;
+    int pass;
+    int ireg;
+    int jstar, iw;
+    int zone;
+    int magsort;
+    int nrmax = MAXREG;
+    int nstar, ntot;
+    int istar, istar1, istar2;
+    double num, ra, dec, mag;
+    double rra1, rra2, rra2a, rdec1, rdec2;
+    double rdist, ddist;
+    char cstr[32];
+    char *str;
+    char tmcenv[16];
+    double xpix, ypix, flux;
+    int offscl;
+    int ix, iy;
+    int bitpix, w, h;   /* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+
+    /* Choose appropriate catalog */
+    if (refcat == TMIDR2) {
+	tmcpath = tmc2path;
+	strcpy (tmcenv, "TMCIDR2_PATH");
+	}
+    else if (refcat == TMXSC) {
+	tmcpath = tmxpath;
+	strcpy (tmcenv, "TMX_PATH");
+	}
+    else if (refcat == TMPSCE) {
+	tmcpath = tmcepath;
+	strcpy (tmcenv, "TMCE_PATH");
+	}
+    else {
+	tmcpath = tmcapath;
+	strcpy (tmcenv, "TMC_PATH");
+	}
+
+    ntot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else if (nlog < 0) {
+	linedump = 0;
+	verbose = 0;
+	}
+    else
+	verbose = 0;
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    /* If pathname is set in environment, override local value */
+    if ((str = getenv(tmcenv)) != NULL )
+	tmcpath = str;
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* If RA range includes zero, split it in two */
+    wrap = 0;
+    if (ra1 > ra2)
+	wrap = 1;
+    else
+	wrap = 0;
+
+    /* make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    nstar = 0;
+    jstar = 0;
+
+    if (sortmag > 0 && sortmag < 4)
+	magsort = sortmag - 1;
+    else 
+	magsort = 0;
+
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,0.0,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+    if (wrap) {
+	rra2a = rra2;
+	rra2 = 360.0;
+	}
+    else
+	rra2a = 0.0;
+
+    /* If searching through RA = 0:00, split search in two */
+    for (iw = 0; iw <= wrap; iw++) {
+
+	/* Find 2MASS Point Source Catalog regions in which to search */
+	nreg = tmcreg (refcat, rra1,rra2,rdec1,rdec2,nrmax,rlist,verbose);
+	if (nreg <= 0) {
+	    fprintf (stderr,"TMCBIN:  no 2MASS regions found\n");
+	    return (0);
+	    }
+
+	/* Loop through zone or region list */
+	for (ireg = 0; ireg < nreg; ireg++) {
+
+	    /* Open file for this region of 2MASS point source catalog */
+	    zone = rlist[ireg];
+	    starcat = tmcopen (refcat, zone);
+	    if (starcat == NULL) {
+		fprintf (stderr,"TMCBIN: File %s not found\n",inpath);
+		return (0);
+		}
+
+	    /* Find first and last stars in this region */
+	    if (refcat == TMPSC || refcat == TMPSCE || refcat == TMXSC) {
+		istar1 = tmcsra (starcat, star, zone, rra1, MINRA);
+		istar2 = tmcsra (starcat, star, zone, rra2, MAXRA);
+		}
+	    else {
+		istar1 = tmcsdec (starcat, star, zone, rdec1);
+		istar2 = tmcsdec (starcat, star, zone, rdec2);
+		}
+	    if (verbose)
+		fprintf (stderr,"TMCBIN: Searching stars %d through %d\n",
+			istar1, istar2-1);
+
+	    /* Loop through catalog for this region */
+	    for (istar = istar1; istar < istar2; istar++) {
+		if (tmcstar (starcat, star, zone, istar)) {
+		    fprintf (stderr,"TMCBIN: Cannot read star %d\n", istar);
+		    break;
+		    }
+
+		/* ID number */
+		num = star->num;
+
+		/* Magnitude */
+		mag = star->xmag[0];
+
+		/* Check magnitude limits */
+		pass = 1;
+		if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+		    pass = 0;
+
+		if (pass) {
+
+		    /* Get position in output coordinate system */
+		    ra = star->ra;
+		    dec = star->dec;
+		    wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+
+		    /* Check distance along RA and Dec axes */
+		    ddist = wcsdist (cra,cdec,cra,dec);
+		    if (ddist > ddec)
+			pass = 0;
+		    rdist = wcsdist (cra,dec,ra,dec);
+		    if (rdist > dra)
+			pass = 0;
+		    }
+
+		/* Save star in FITS image */
+		if (pass) {
+		    wcs2pix (wcs, ra, dec,&xpix,&ypix,&offscl);
+		    if (!offscl) {
+			if (magscale > 0.0)
+			    flux = magscale * exp (logt * (-mag / 2.5));
+			else
+			    flux = 1.0;
+			ix = (int) (xpix + 0.5);
+			iy = (int) (ypix + 0.5);
+			addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+			nstar++;
+			jstar++;
+			}
+		    else {
+			ix = 0;
+			iy = 0;
+			}
+		    if (nlog == 1) {
+			fprintf (stderr,"TMCBIN: %11.6f: %9.5f %9.5f %s",
+				 num,ra,dec,cstr);
+			if (magscale > 0.0)
+			    fprintf (stderr, " %5.2f", mag);
+			if (!offscl)
+			    flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+			else
+			    flux = 0.0;
+			fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+			}
+
+		    /* End of accepted star processing */
+		    }
+
+		/* Log operation */
+		jstar++;
+		if (nlog > 0 && istar%nlog == 0)
+		    fprintf (stderr,"TMCBIN: %5d / %5d / %5d sources\r",
+			     nstar,jstar,starcat->nstars);
+
+		/* End of star loop */
+		}
+
+	    ntot = ntot + starcat->nstars;
+	    if (nlog > 0)
+		fprintf (stderr,"TMCBIN: %4d / %4d: %5d / %5d  / %5d sources from region %4d    \n",
+		 	 ireg+1,nreg,nstar,jstar,starcat->nstars,zone);
+
+	    /* Close region input file */
+	    tmcclose (starcat);
+	    }
+	rra1 = 0.0;
+	rra2 = rra2a;
+	}
+
+/* close output file and summarize transfer */
+    if (nlog > 0) {
+	if (nreg > 1)
+	    fprintf (stderr,"TMCBIN: %d regions: %d / %d found\n",nreg,nstar,ntot);
+	else
+	    fprintf (stderr,"TMCBIN: 1 region: %d / %d found\n",nstar,ntot);
+	}
+    return (nstar);
+}
+
+char rdir[50][4]={"0", "1", "2", "3", "4", "5a", "5b", "6a", "6b", "6c",
+	"6d", "7a", "7b", "7c", "7d", "8a", "8b", "9", "10", "11", "12",
+	"13", "14", "15", "16a", "16b", "17a", "17b", "17c", "17d", "17e",
+	"17f", "17g", "17h", "18a", "18b", "18c", "18d", "19a", "19b",
+	"19c", "19d", "20a", "20b", "20c", "20d", "21", "22", "23", ""};
+double zmax[50]={00.000, 01.000, 02.000, 03.000, 04.000, 05.000, 05.500,
+		 06.000, 06.250, 06.500, 06.750, 07.000, 07.250, 07.500,
+		 07.750, 08.000, 08.500, 09.000, 10.000, 11.000, 12.000,
+		 13.000, 14.000, 15.000, 16.000, 16.500, 17.000, 17.125,
+		 17.250, 17.375, 17.500, 17.625, 17.750, 17.875, 18.000,
+		 18.250, 18.500, 18.750, 19.000, 19.250, 19.500, 19.750,
+		 20.000, 20.250, 20.500, 20.750, 21.000, 22.000, 23.000,
+		 24.000};
+
+
+/* TMCREG -- find the regions contained by the given RA/Dec limits
+ * Build lists containing the first star and number of stars for each range.
+ */
+
+static int
+tmcreg (refcat, ra1, ra2, dec1, dec2, nrmax, regions, verbose)
+
+int	refcat;		/* Code for catalog file from wcscat.h */
+double	ra1, ra2;	/* Right ascension limits in degrees */
+double	dec1, dec2; 	/* Declination limits in degrees */
+int	nrmax;		/* Maximum number of regions to find */
+int	*regions;	/* Region numbers to search (returned)*/
+int	verbose;	/* 1 for diagnostics */
+
+{
+    int nrgn;		/* Number of regions found (returned) */
+    int ir;
+    int iz1 = 0;
+    int iz2 = 0;
+    int ir1,ir2,jr1,jr2,i;
+    int ispd1, ispd2, ispd;
+    int nsrch;
+    double rah1,rah2, spd1, spd2;
+
+    nrgn = 0;
+
+    /* Find region range to search based on right ascension */
+    if (refcat == TMIDR2) {
+	rah1 = ra1 / 15.0;
+	for (i = 1; i < 50; i++) {
+	    if (rah1 < zmax[i]) {
+		iz1 = i - 1;
+		break;
+		}
+	    }
+	rah2 = ra2 / 15.0;
+	for (i = 1; i < 50; i++) {
+	    if (rah2 < zmax[i]) {
+		iz2 = i - 1;
+		break;
+		}
+	    }
+	if (iz2 >= iz1) {
+	    ir1 = iz1;
+	    ir2 = iz2;
+	    jr1 = 0;
+	    jr2 = 0;
+	    nsrch = iz2 - iz1 + 1;
+	    }
+	else {
+	    ir1 = iz1;
+	    ir2 = 48;
+	    jr1 = 0;
+	    jr2 = iz2;
+	    nsrch = 48 - iz1 + 1 + iz2 + 1;
+	    }
+
+	/* Search region northern hemisphere or only one region */
+	if (verbose) {
+	    fprintf(stderr,"TMCREG: RA: %.5f - %.5f, Dec: %.5f - %.5f\n",
+		ra1,ra2,dec1,dec2);
+	    if (nsrch == 1)
+		fprintf (stderr,"TMCREG: searching region %d", ir1);
+	    else
+		fprintf (stderr,"TMCREG: searching %d regions: %d - %d",
+		 nsrch, ir1, ir2);
+	    if (jr1 > 0 && jr2 > 0)
+		fprintf (stderr,", %d - %d", jr1, jr2);
+	    fprintf (stderr,"\n");
+	    }
+
+	/* Loop through first section of sky */
+	nrgn = 0;
+	for (ir = ir1; ir <= ir2; ir++) {
+	    if (verbose)
+		fprintf (stderr,"TMCREG: Region %d (%s) added to search\n",
+		    ir, rdir[ir]);
+
+	    /* Add this region to list, if there is space */
+	    if (nrgn < nrmax) {
+		regions[nrgn] = ir;
+		nrgn++;
+		}
+	    }
+
+	/* Loop through second section of sky */
+	for (ir = jr1; ir < jr2; ir++) {
+	    if (verbose)
+		fprintf (stderr,"TMCREG: Region %d %s) added to search\n",
+		     ir, rdir[ir]);
+
+	    /* Add this region to list, if there is space */
+	    if (nrgn < nrmax) {
+		regions[nrgn] = ir;
+		nrgn++;
+		}
+	    }
+	}
+
+    /* Compute SPD regions for all-sky release of point source catalog */
+    else {
+	if (dec1 < dec2) {
+	    spd1 = dec1 + 90.0;
+	    spd2 = dec2 + 90.0;
+	    }
+	else {
+	    spd1 = dec2 + 90.0;
+	    spd2 = dec1 + 90.0;
+	    }
+	ispd1 = (int) (spd1 * 10.0);
+	ispd2 = (int) (spd2 * 10.0);
+	if (ispd2 > 1799) ispd2 = 1799;
+	for (ispd = ispd1; ispd <= ispd2; ispd++) {
+	    /* Add this region to list, if there is space */
+	    if (nrgn < nrmax) {
+		regions[nrgn] = ispd;
+		nrgn++;
+		}
+	    }
+	}
+
+    return (nrgn);
+}
+
+
+/* TMCOPEN -- Open 2MASS point source catalog file, returning catalog structure */
+
+struct StarCat *
+tmcopen (refcat, zone)
+
+int	refcat;		/* Code for catalog file from wcscat.h */
+int	zone;		/* RA zone (hours) to read */
+
+{
+    FILE *fcat;
+    struct StarCat *sc;
+    int lfile, lpath, izone, ireg;
+    char *zonefile;
+    char *zonepath;	/* Full pathname for catalog file */
+
+    /* Set path to 2MASS Point Source Catalog zone */
+    if (refcat == TMPSC || refcat == TMPSCE || refcat == TMXSC) {
+	izone = zone / 10;
+	ireg = zone % 10;
+	lpath = strlen (tmcpath) + 18;
+	zonepath = (char *) malloc (lpath);
+	sprintf (zonepath, "%s/%03d/t%04d.cat", tmcpath, izone, ireg);
+	}
+    else {
+	lpath = strlen (tmcpath) + 18;
+	zonepath = (char *) malloc (lpath);
+	sprintf (zonepath, "%s/idr2psc%s.tbl", tmcpath, rdir[zone]);
+	}
+
+    /* Find length of 2MASS catalog file */
+    lfile = getfilesize (zonepath);
+
+    /* Check for existence of catalog */
+    if (lfile < 2) {
+	fprintf (stderr,"TMCOPEN: Binary catalog %s has no entries\n",zonepath);
+	free (zonepath);
+	return (NULL);
+	}
+
+    /* Open 2MASS point source catalog zone file */
+    if (!(fcat = fopen (zonepath, "r"))) {
+	fprintf (stderr,"TMCOPEN: 2MASS PSC file %s cannot be read\n",zonepath);
+	free (zonepath);
+	return (0);
+	}
+
+    /* Set 2MASS PSC catalog header information */
+    sc = (struct StarCat *) calloc (1, sizeof (struct StarCat));
+    sc->byteswapped = 0;
+    sc->refcat = refcat;
+
+    if (refcat == TMPSC) {
+	sc->entra = 0;
+	sc->entdec = 10;
+	sc->entname = 0;
+	sc->entmag[0] = 39;
+	sc->entmag[1] = 46;
+	sc->entmag[2] = 53;
+	sc->entadd = 61;
+	sc->nbent = 69;
+	}
+    else if (refcat == TMPSCE) {
+	sc->entra = 0;
+	sc->entdec = 10;
+	sc->entname = 0;
+	sc->entmag[0] = 39;
+	sc->entmag[1] = 46;
+	sc->entmag[2] = 53;
+	sc->entmag[3] = 60;
+	sc->entmag[4] = 66;
+	sc->entmag[5] = 72;
+	sc->entadd = 79;
+	sc->nbent = 87;
+	}
+    else if (refcat == TMXSC) {
+	sc->entra = 0;
+	sc->entdec = 10;
+	sc->entname = 0;
+	sc->entmag[0] = 39;
+	sc->entmag[1] = 46;
+	sc->entmag[2] = 53;
+	sc->entsize = 60;
+	sc->nbent = 68;
+	}
+    else {
+	sc->entra = 0;
+	sc->entdec = 10;
+	sc->entmag[0] = 53;
+	sc->entmag[1] = 72;
+	sc->entmag[2] = 91;
+	sc->entadd = 110;
+	sc->nbent = 302;
+	}
+    sc->nstars = lfile / sc->nbent;
+
+    /* Separate filename from pathname and save in structure */
+    zonefile = strrchr (zonepath,'/');
+    if (zonefile)
+	zonefile = zonefile + 1;
+    else
+	zonefile = zonepath;
+    if (strlen (zonefile) < 24)
+	strcpy (sc->isfil, zonefile);
+    else
+	strncpy (sc->isfil, zonefile, 23);
+
+    /* Set other catalog information in structure */
+    sc->inform = 'J';
+    sc->coorsys = WCS_J2000;
+    sc->epoch = 2000.0;
+    sc->equinox = 2000.0;
+    sc->ifcat = fcat;
+    sc->sptype = 2;
+
+    /* All-sky release 2MASS catalogs are RA-sorted within Dec zones */
+    if (refcat == TMPSC || refcat == TMPSCE)
+	sc->rasorted = 1;
+    /* Pre-release 2MASS catalogs were Dec-sorted within RA zones */
+    else
+	sc->rasorted = 0;
+
+    free (zonepath);
+    return (sc);
+}
+
+
+void
+tmcclose (sc)
+struct StarCat *sc;	/* Star catalog descriptor */
+{
+    fclose (sc->ifcat);
+    if (sc->catdata != NULL)
+	free (sc->catdata);
+    free (sc);
+    return;
+}
+
+
+/* TMCSDEC -- Find 2MASS star closest to specified declination */
+
+static int
+tmcsdec (starcat, star, zone, decx0)
+
+struct StarCat *starcat; /* Star catalog descriptor */
+struct Star *star;	/* Current star entry */
+int	zone;		/* RA zone in which search is occuring */
+double	decx0;		/* Declination in degrees for which to search */
+{
+    int istar, istar1, istar2, nrep;
+    double decx, dec1, dec, rdiff, rdiff1, rdiff2, sdiff;
+    char decstrx[32];
+    int debug = 0;
+
+    decx = decx0;
+    if (debug)
+	dec2str (decstrx, 31, decx, 3);
+    istar1 = 1;
+    if (tmcstar (starcat, star, zone, istar1))
+	return (0);
+    dec1 = star->dec;
+    istar = starcat->nstars;
+    nrep = 0;
+    while (istar != istar1 && nrep < 20) {
+	if (tmcstar (starcat, star, zone, istar))
+	    break;
+	else {
+	    dec = star->dec;
+	    if (dec == dec1)
+		break;
+	    if (debug) {
+		char decstr[32];
+		dec2str (decstr, 31, dec, 3);
+		fprintf (stderr,"TMCSRA %d %d: %s (%s)\n",
+			 nrep,istar,decstr,decstrx);
+		}
+	    rdiff = dec1 - dec;
+	    rdiff1 = dec1 - decx;
+	    rdiff2 = dec - decx;
+	    if (nrep > 20 && ABS(rdiff2) > ABS(rdiff1)) {
+		istar = istar1;
+		break;
+		}
+	    nrep++;
+	    sdiff = (double)(istar - istar1) * rdiff1 / rdiff;
+	    istar2 = istar1 + (int) (sdiff + 0.5);
+	    dec1 = dec;
+	    istar1 = istar;
+	    istar = istar2;
+	    if (debug) {
+		fprintf (stderr," dec1=    %.5f dec=     %.5f decx=    %.5f\n",
+			 dec1,dec,decx);
+		fprintf (stderr," rdiff=  %.5f rdiff1= %.5f rdiff2= %.5f\n",
+			 rdiff,rdiff1,rdiff2);
+		fprintf (stderr," istar1= %d istar= %d istar1= %d\n",
+			 istar1,istar,istar2);
+		}
+	    if (istar < 1)
+		istar = 1;
+	    if (istar > starcat->nstars)
+		istar = starcat->nstars;
+	    if (istar == istar1)
+		break;
+	    }
+	}
+    return (istar);
+}
+
+
+/* TMCSRA -- Find 2MASS star closest to specified right ascension */
+
+static int
+tmcsra (starcat, star, zone, rax0, minmax)
+
+struct StarCat *starcat; /* Star catalog descriptor */
+struct Star *star;	/* Current star entry */
+int	zone;		/* Declination zone in which search is occurring */
+double	rax0;		/* Right ascension in degrees for which to search */
+int	minmax;		/* Flag to say whether this is a min or max RA */
+{
+    int istar, istar0, istar1, nrep, i;
+    double rax, ra0, ra1, ra, sdiff;
+    char rastrx[32];
+    char rastr[32];
+    int debug = 0;
+
+    rax = rax0;
+    ra0 = -1.0;
+    ra1 = star->ra;
+    if (debug) {
+	ra2str (rastrx, 31, rax, 3);
+	ra2str (rastr, 31, ra1, 3);
+	nrep = -1;
+	istar = (int) star->num;
+	fprintf (stderr,"TMCSRA %d %d: %s (%s)\n",
+		 nrep,istar,rastr,rastrx);
+	}
+    istar0 = 1;
+    if (tmcstar (starcat, star, zone, istar0))
+	return (0);
+    ra0 = star->ra;
+    istar1 = starcat->nstars;
+    if (tmcstar (starcat, star, zone, istar1))
+	return (0);
+    ra1 = star->ra;
+    istar = starcat->nstars / 2;
+    if (tmcstar (starcat, star, zone, istar))
+	return (0);
+    ra = star->ra;
+    nrep = 0;
+    while (istar != istar1 && nrep < 20) {
+	if (ra < rax) {
+	    sdiff = 0.5 * (double) (istar1 - istar);
+	    if (sdiff < 1.0)
+		break;
+	    istar0 = istar;
+	    istar = istar + (int) (sdiff + 0.5);
+	    }
+	else if (ra > rax) {
+	    sdiff = 0.5 * (double) (istar - istar0);
+	    if (sdiff < 1.0)
+		break;
+	    istar1 = istar;
+	    istar = istar - (int) (sdiff + 0.5);
+	    }
+	else
+	    break;
+	if (debug) {
+	    fprintf (stderr," istar0= %d istar1= %d istar= %d\n",
+		     istar0, istar1,istar);
+	    fprintf (stderr," ra1=    %.5f ra=     %.5f rax=    %.5f\n",
+			 ra0,ra,rax);
+	    }
+	if (istar == 1 || istar == istar1)
+	    break;
+	if (tmcstar (starcat, star, zone, istar))
+	    break;
+	ra = star->ra;
+	nrep++;
+	}
+
+    /* For small catalogs, linear projection of RA's doesn't work */
+    /* Check lower numbers if low end of range is being set */
+    if (minmax == MINRA) {
+	for (i = 1; i < 5; i++) {
+	    istar0 = istar - 1;
+	    if (istar0 < 1)
+		break;
+	    if (tmcstar (starcat, star, zone, istar0))
+		    break;
+	    if (star->ra < rax)
+		break;
+	    else
+		istar = istar0;
+	    }
+	}
+
+    /* Check higher numbers if top end of range is being set */
+    else {
+	for (i = 1; i < 5; i++) {
+	    istar0 = istar + 1;
+	    if (istar0 > starcat->nstars)
+		break;
+	    if (tmcstar (starcat, star, zone, istar0))
+		break;
+	    if (star->ra > rax)
+		break;
+	    else
+		istar = istar0;
+	    }
+	}
+    
+    return (istar);
+}
+
+
+/* TMCSTAR -- Get 2MASS Point Source Catalog entry for one star;
+              return 0 if successful */
+
+static int
+tmcstar (sc, st, zone, istar)
+
+struct StarCat *sc;	/* Star catalog descriptor */
+struct Star *st;	/* Current star entry */
+int	zone;		/* Zone catalog number (1-49) */
+int	istar;		/* Star sequence in 2MASS zone file */
+{
+    char line[500];
+    int nbskip, nbr, iflag;
+
+    /* Drop out if catalog pointer is not set */
+    if (sc == NULL)
+	return (1);
+
+    /* Drop out if catalog is not open */
+    if (sc->ifcat == NULL)
+	return (2);
+
+    /* Drop out if star number is too large */
+    if (istar > sc->nstars) {
+	fprintf (stderr, "TMCSTAR:  %d  > %d is not in catalog\n",
+		 istar, sc->nstars);
+	return (3);
+	}
+
+    /* Read entry for one star */
+    nbskip = sc->nbent * (istar - 1);
+    if (fseek (sc->ifcat,nbskip,SEEK_SET))
+	return (-1);
+    nbr = fread (line, sc->nbent, 1, sc->ifcat) * sc->nbent;
+    if (nbr < sc->nbent) {
+	fprintf (stderr, "tmcstar %d / %d bytes read\n",nbr, sc->nbent);
+	return (-2);
+	}
+
+    /* Make up source number from zone number and star number */
+    if (sc->refcat == TMIDR2)
+	st->num = zone + (0.0000001 * (double) istar);
+    else
+	st->num = zone + (0.000001 * (double) istar);
+
+    /* Read position in degrees */
+    st->ra = atof (line);
+    st->dec = atof (line+sc->entdec);
+
+    /* No proper motion */
+    st->rapm = 0.0;
+    st->decpm = 0.0;
+
+    /* Set J magnitude */
+    st->xmag[0] = atof (line+sc->entmag[0]);
+
+    /* Set H magnitude */
+    st->xmag[1] = atof (line+sc->entmag[1]);
+
+    /* Set K magnitude */
+    st->xmag[2] = atof (line+sc->entmag[2]);
+
+    /* Add J, H, K errors if needed */
+    if (sc->refcat == TMPSCE) {
+	st->xmag[3] = atof (line+sc->entmag[3]);
+	st->xmag[4] = atof (line+sc->entmag[4]);
+	st->xmag[5] = atof (line+sc->entmag[5]);
+	}
+
+    /* Add 100 to magnitude if it isn't a good one */
+    if (sc->refcat == TMPSC || sc->refcat == TMPSCE || sc->refcat == TMXSC) {
+	if (line[sc->entadd] == 'U')
+	    st->xmag[0] = st->xmag[0] + 100.0;
+	if (line[sc->entadd + 1] == 'U')
+	    st->xmag[1] = st->xmag[1] + 100.0;
+	if (line[sc->entadd + 2] == 'U')
+	    st->xmag[2] = st->xmag[2] + 100.0;
+	}
+
+	/* Preliminary data release data quality flag */
+    else {
+	iflag = ((int) line[sc->entadd]) - 48;
+	if (iflag < 1 || iflag == 3 || iflag > 4)
+	    st->xmag[0] = st->xmag[0] + 100.0;
+	iflag = ((int) line[sc->entadd + 1]) - 48;
+	if (iflag < 1 || iflag == 3 || iflag > 4)
+	    st->xmag[1] = st->xmag[1] + 100.0;
+	iflag = ((int) line[sc->entadd + 2]) - 48;
+	if (iflag < 1 || iflag == 3 || iflag > 4)
+	    st->xmag[2] = st->xmag[2] + 100.0;
+	}
+    if (sc->refcat == TMXSC)
+	st->size = atof (line+sc->entsize);
+    else
+	st->size = 0.0;
+
+    if (linedump)
+	printf ("%s\n",line);
+
+    return (0);
+}
+
+/* May 29 2001	New program, based on ty2read.c and uacread.c
+ * May 30 2001	Round K magnitude to nearest 1000th
+ * Jun 13 2001	Round star number up to avoid truncation problem
+ * Jun 27 2001	Add code to print one entry at time if nstars < 1
+ * Jun 27 2001	Allocate gdist only if larger array is needed
+ * Sep 11 2001	Return all three magnitudes
+ * Sep 17 2001	Print line from catalog if nlog is < 0
+ * Sep 17 2001	Flag bad magnitudes by adding 100 to them
+ * Sep 18 2001	Fix bug in magnitudes returned if not distance sorted
+ * Nov 20 2001	Change cos(degrad()) to cosdeg()
+ * Nov 29 2001	Declare undeclared subroutine tmcsdec
+ * Dec  3 2001	Change default catalog directory to /data/astrocat/2MASS
+ *
+ * Feb 13 2002	Fix catalog name in web access
+ * Oct  3 2002	Use global variable for scat version
+ *
+ * Apr  3 2003	Drop unused variables after lint
+ * Apr 14 2003	Explicitly get revision date if nstarmax < 1
+ * May 19 2003	Add code to read dec-zoned, ra-sorted All-Sky release
+ * May 21 2003	Add catfile argument to read both IDR2 and All-Sky releases
+ * May 27 2003	Allow IDR2 and Allsky release to be set by catalog name
+ * May 28 2003	Star ID numbers from All-Sky release have 6 decimal places
+ * Jul  2 2003	Fix limiting magnitude for All-Sky Release
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Sep 25 2003	Add tmcbin() to fill an image with sources
+ * Oct  6 2003	Update tmcread() and tmcbin() for improved RefLim()
+ * Nov 18 2003	Initialize image size and bits/pixel from header in tmcbin()
+ *
+ * Jan 13 2004	Add support for 2MASS Extended Source Catalog
+ * Jan 14 2004	Add code to fix convergence in tmcsra()
+ * Jan 23 2004	Fix search algorith in tmcsra()
+ * Nov 10 2004	Fix region computation at north pole
+ *
+ * Aug  5 2005	Add JHK errors as additional option
+ * Aug  5 2005	Avoid extra work by passing refcat catalog code
+ *
+ * Jun 20 2006	Initialize uninitialized variables
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ * Nov 15 2006	Fix binning
+ *
+ * Jan  8 2007	Drop unused variables
+ * Jan  9 2007	Relabel number arrays
+ * Jan 10 2007	Add match=1 argument to webrnum()
+ * Oct 31 2007	Properly return magnitude errors from tmcrnum(), if present
+ * Nov 20 2007	Fix bug which offset limit flag by one (found by Gus Muensch)
+ *
+ * Sep 28 2009	Print correct heading for n<0 Extended Source tab table
+ */
diff --git a/Code/src/libwcs/tnxpos.c b/Code/src/libwcs/tnxpos.c
new file mode 100644
index 0000000000000000000000000000000000000000..be71a0a7d1977117a0cb9a25fdee1b35e7cfe874
--- /dev/null
+++ b/Code/src/libwcs/tnxpos.c
@@ -0,0 +1,1234 @@
+/*** File wcslib/tnxpos.c
+ *** September 17, 2008
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** After IRAF mwcs/wftnx.x and mwcs/wfgsurfit.x
+ *** Copyright (C) 1998-2008
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "wcs.h"
+
+#define SPHTOL 0.00001
+#define BADCVAL 0.0
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+/* wftnx -- wcs function driver for the gnomonic projection with correction.
+ *    tnxinit (header, wcs)
+ *    tnxclose (wcs)
+ *    tnxfwd (xpix, ypix, wcs, xpos, ypos)	Pixels to WCS
+ *    tnxrev (xpos, ypos, wcs, xpix, ypix)	WCS to pixels
+ */
+
+#define	max_niter	500
+#define	SZ_ATSTRING	2000
+static void wf_gsclose();
+static void wf_gsb1pol();
+static void wf_gsb1leg();
+static void wf_gsb1cheb();
+
+/* tnxinit -- initialize the gnomonic forward or inverse transform.
+ * initialization for this transformation consists of, determining which
+ * axis is ra / lon and which is dec / lat, computing the celestial longitude
+ * and colatitude of the native pole, reading in the the native longitude
+ * of the pole of the celestial coordinate system longpole from the attribute
+ * list, precomputing euler angles and various intermediaries derived from the
+ * coordinate reference values, and reading in the projection parameter ro
+ * from the attribute list. if longpole is undefined then a value of 180.0
+ * degrees is assumed. if ro is undefined a value of 180.0 / pi is assumed.
+ * the tan projection is equivalent to the azp projection with mu set to 0.0.
+ * in order to determine the axis order, the parameter "axtype={ra|dec}
+ * {xlon|glat}{xlon|elat}" must have been set in the attribute list for the
+ * function. the longpole and ro parameters may be set in either or both of
+ * the axes attribute lists, but the value in the ra axis attribute list takes
+ * precedence. 
+ */
+
+int
+tnxinit (header, wcs)
+
+const char *header;	/* FITS header */
+struct WorldCoor *wcs;	/* pointer to WCS structure */
+{
+    struct IRAFsurface *wf_gsopen();
+    char *str1, *str2, *lngstr, *latstr;
+    extern void wcsrotset();
+
+    /* allocate space for the attribute strings */
+    str1 = malloc (SZ_ATSTRING);
+    str2 = malloc (SZ_ATSTRING);
+    hgetm (header, "WAT1", SZ_ATSTRING, str1);
+    hgetm (header, "WAT2", SZ_ATSTRING, str2);
+
+    lngstr = malloc (SZ_ATSTRING);
+    latstr = malloc (SZ_ATSTRING);
+
+    /* determine the native longitude of the pole of the celestial
+	coordinate system corresponding to the FITS keyword longpole.
+	this number has no default and should normally be set to 180
+	degrees. search both axes for this quantity. */
+
+    if (wcs->longpole > 360.0) {
+	if (!igetr8 (str1, "longpole", &wcs->longpole)) {
+	    if (!igetr8 (str2, "longpole", &wcs->longpole))
+		wcs->longpole = 180.0;
+	    }
+	}
+
+    /*  Fetch the ro projection parameter which is the radius of the
+	generating sphere for the projection. if ro is absent which
+	is the usual case set it to 180 / pi. search both axes for
+	this quantity. */
+
+    if (!igetr8 (str1, "ro", &wcs->rodeg)) {
+	if (!igetr8 (str2, "ro", &wcs->rodeg))
+	    wcs->rodeg = 180.0 / PI;
+	}
+
+    /*  Fetch the longitude correction surface. note that the attribute
+	string may be of any length so the length of atvalue may have
+	to be adjusted. */
+
+    if (!igets (str1, "lngcor", SZ_ATSTRING, lngstr)) {
+	if (!igets (str2, "lngcor", SZ_ATSTRING, lngstr))
+	    wcs->lngcor = NULL;
+	else
+	    wcs->lngcor = wf_gsopen (lngstr);
+	}
+    else
+	wcs->lngcor = wf_gsopen (lngstr);
+
+    /*  Fetch the latitude correction surface. note that the attribute
+	string may be of any length so the length of atvalue may have
+	to be adjusted. */
+
+    if (!igets (str2, "latcor", SZ_ATSTRING, latstr)) {
+	if (!igets (str1, "latcor", SZ_ATSTRING, latstr))
+	    wcs->latcor = NULL;
+	else
+	    wcs->latcor = wf_gsopen (latstr);
+	}
+    else
+	wcs->latcor = wf_gsopen (latstr);
+
+    /* Compute image rotation */
+    wcsrotset (wcs);
+
+    /* free working space. */
+    free (str1);
+    free (str2);
+    free (lngstr);
+    free (latstr);
+
+    /* Return 1 if there are no correction coefficients */
+    if (wcs->latcor == NULL && wcs->lngcor == NULL)
+	return (1);
+    else
+	return (0);
+}
+
+
+/* tnxpos -- forward transform (physical to world) gnomonic projection. */
+
+int
+tnxpos (xpix, ypix, wcs, xpos, ypos)
+
+double	xpix, ypix;	/*i physical coordinates (x, y) */
+struct WorldCoor *wcs;	/*i pointer to WCS descriptor */
+double	*xpos, *ypos;	/*o world coordinates (ra, dec) */
+{
+    int	ira, idec;
+    double x, y, r, phi, theta, costhe, sinthe, dphi, cosphi, sinphi, dlng, z;
+    double colatp, coslatp, sinlatp, longp;
+    double xs, ys, ra, dec, xp, yp;
+    double wf_gseval();
+
+    /* Convert from pixels to image coordinates */
+    xpix = xpix - wcs->crpix[0];
+    ypix = ypix - wcs->crpix[1];
+
+    /* Scale and rotate using CD matrix */
+    if (wcs->rotmat) {
+	x = xpix * wcs->cd[0] + ypix * wcs->cd[1];
+	y = xpix * wcs->cd[2] + ypix * wcs->cd[3];
+	}
+
+    else {
+
+	/* Check axis increments - bail out if either 0 */
+	if (wcs->cdelt[0] == 0.0 || wcs->cdelt[1] == 0.0) {
+	    *xpos = 0.0;
+	    *ypos = 0.0;
+	    return 2;
+	    }
+
+	/* Scale using CDELT */
+	xs = xpix * wcs->cdelt[0];
+	ys = ypix * wcs->cdelt[1];
+
+	/* Take out rotation from CROTA */
+	if (wcs->rot != 0.0) {
+	    double cosr = cos (degrad (wcs->rot));
+	    double sinr = sin (degrad (wcs->rot));
+	    x = xs * cosr - ys * sinr;
+	    y = xs * sinr + ys * cosr;
+    	    }
+	else {
+	    x = xs;
+	    y = ys;
+	    }
+	}
+
+    /* get the axis numbers */
+    if (wcs->coorflip) {
+	ira = 1;
+	idec = 0;
+	}
+    else {
+	ira = 0;
+	idec = 1;
+	}
+    colatp = degrad (90.0 - wcs->crval[idec]);
+    coslatp = cos(colatp);
+    sinlatp = sin(colatp);
+    longp = degrad(wcs->longpole);
+
+    /*  Compute native spherical coordinates phi and theta in degrees from the
+	projected coordinates. this is the projection part of the computation */
+    if (wcs->lngcor != NULL)
+	xp = x + wf_gseval (wcs->lngcor, x, y);
+    else
+	xp = x;
+    if (wcs->latcor != NULL)
+	yp = y + wf_gseval (wcs->latcor, x, y);
+    else
+	yp = y;
+    x = xp;
+    y = yp;
+    r = sqrt (x * x + y * y);
+
+    /* Compute phi */
+    if (r == 0.0)
+	phi = 0.0;
+    else
+	phi = atan2 (x, -y);
+
+    /* Compute theta */
+    theta = atan2 (wcs->rodeg, r);
+
+    /*  Compute the celestial coordinates ra and dec from the native
+	coordinates phi and theta. this is the spherical geometry part
+	of the computation */
+
+    costhe = cos (theta);
+    sinthe = sin (theta);
+    dphi = phi - longp;
+    cosphi = cos (dphi);
+    sinphi = sin (dphi);
+
+    /* Compute the ra */
+    x = sinthe * sinlatp - costhe * coslatp * cosphi;
+    if (fabs (x) < SPHTOL)
+	x = -cos (theta + colatp) + costhe * coslatp * (1.0 - cosphi);
+    y = -costhe * sinphi;
+    if (x != 0.0 || y != 0.0)
+	dlng = atan2 (y, x);
+    else
+	dlng = dphi + PI ;
+    ra =  wcs->crval[ira] + raddeg(dlng);
+
+    /* normalize ra */
+    if (wcs->crval[ira] >= 0.0) {
+	if (ra < 0.0)
+	    ra = ra + 360.0;
+	}
+    else {
+	if (ra > 0.0)
+	    ra = ra - 360.0;
+	}
+    if (ra > 360.0)
+	ra = ra - 360.0;
+    else if (ra < -360.0)
+	ra = ra + 360.0;
+
+    /* compute the dec */
+    if (fmod (dphi, PI) == 0.0) {
+	dec = raddeg(theta + cosphi * colatp);
+	if (dec > 90.0)
+	    dec = 180.0 - dec;
+	if (dec < -90.0)
+	    dec = -180.0 - dec;
+	}
+    else {
+	z = sinthe * coslatp + costhe * sinlatp * cosphi;
+	if (fabs(z) > 0.99) {
+	    if (z >= 0.0)
+		dec = raddeg(acos (sqrt(x * x + y * y)));
+	    else
+		dec = raddeg(-acos (sqrt(x * x + y * y)));
+	    }
+	else
+		dec = raddeg(asin (z));
+	}
+
+    /* store the results */
+    *xpos  = ra;
+    *ypos = dec;
+    return (0);
+}
+
+
+/* tnxpix -- inverse transform (world to physical) gnomonic projection */
+
+int
+tnxpix (xpos, ypos, wcs, xpix, ypix)
+
+double	xpos, ypos;	/*i world coordinates (ra, dec) */
+struct WorldCoor *wcs;	/*i pointer to WCS descriptor */
+double	*xpix, *ypix;	/*o physical coordinates (x, y) */
+{
+    int	ira, idec, niter;
+    double ra, dec, cosdec, sindec, cosra, sinra, x, y, phi, theta;
+    double s, r, dphi, z, dpi, dhalfpi, twopi, tx;
+    double xm, ym, f, fx, fy, g, gx, gy, denom, dx, dy;
+    double colatp, coslatp, sinlatp, longp, sphtol;
+    double wf_gseval(), wf_gsder();
+
+    /* get the axis numbers */
+    if (wcs->coorflip) {
+	ira = 1;
+	idec = 0;
+	}
+    else {
+	ira = 0;
+	idec = 1;
+	}
+
+    /*  Compute the transformation from celestial coordinates ra and
+	dec to native coordinates phi and theta. this is the spherical
+	geometry part of the transformation */
+
+    ra  = degrad (xpos - wcs->crval[ira]);
+    dec = degrad (ypos);
+    cosra = cos (ra);
+    sinra = sin (ra);
+    cosdec = cos (dec);
+    sindec = sin (dec);
+    colatp = degrad (90.0 - wcs->crval[idec]);
+    coslatp = cos (colatp);
+    sinlatp = sin (colatp);
+    if (wcs->longpole == 999.0)
+	longp = degrad (180.0);
+    else
+	longp = degrad(wcs->longpole);
+    dpi = PI;
+    dhalfpi = dpi * 0.5;
+    twopi = PI + PI;
+    sphtol = SPHTOL;
+
+    /* Compute phi */
+    x = sindec * sinlatp - cosdec * coslatp * cosra;
+    if (fabs(x) < sphtol)
+	x = -cos (dec + colatp) + cosdec * coslatp * (1.0 - cosra);
+    y = -cosdec * sinra;
+    if (x != 0.0 || y != 0.0)
+	dphi = atan2 (y, x);
+    else
+	dphi = ra - dpi;
+    phi = longp + dphi;
+    if (phi > dpi)
+	phi = phi - twopi;
+    else if (phi < -dpi)
+	phi = phi + twopi;
+
+    /* Compute theta */
+    if (fmod (ra, dpi) == 0.0) {
+	theta = dec + cosra * colatp;
+	if (theta > dhalfpi)
+	    theta = dpi - theta;
+	if (theta < -dhalfpi)
+	    theta = -dpi - theta;
+	}
+    else {
+	z = sindec * coslatp + cosdec * sinlatp * cosra;
+	if (fabs (z) > 0.99) {
+	    if (z >= 0.0)
+		theta = acos (sqrt(x * x + y * y));
+	    else
+		theta = -acos (sqrt(x * x + y * y));
+	    }
+	else
+	    theta = asin (z);
+	}
+
+    /*  Compute the transformation from native coordinates phi and theta
+	to projected coordinates x and y */
+
+    s = sin (theta);
+    if (s == 0.0) {
+	x = BADCVAL;
+	y = BADCVAL;
+	}
+    else {
+	r = wcs->rodeg * cos (theta) / s;
+	if (wcs->lngcor == NULL && wcs->latcor == NULL) {
+	    if (wcs->coorflip) {
+		y  = r * sin (phi);
+		x = -r * cos (phi);
+		}
+	    else {
+		x  = r * sin (phi);
+		y = -r * cos (phi);
+		}
+	    }
+	else {
+	    xm  = r * sin (phi);
+	    ym = -r * cos (phi);
+	    x = xm;
+	    y = ym;
+	    niter = 0;
+	    while (niter < max_niter) {
+		if (wcs->lngcor != NULL) {
+		    f = x + wf_gseval (wcs->lngcor, x, y) - xm;
+		    fx = wf_gsder (wcs->lngcor, x, y, 1, 0);
+		    fx = 1.0 + fx;
+		    fy = wf_gsder (wcs->lngcor, x, y, 0, 1);
+		    }
+		else {
+		    f = x - xm;
+		    fx = 1.0 ;
+		    fy = 0.0;
+		    }
+		if (wcs->latcor != NULL) {
+		    g = y + wf_gseval (wcs->latcor, x, y) - ym;
+		    gx = wf_gsder (wcs->latcor, x, y, 1, 0);
+		    gy = wf_gsder (wcs->latcor, x, y, 0, 1);
+		    gy = 1.0 + gy;
+		    }
+		else {
+		    g = y - ym;
+		    gx = 0.0 ;
+		    gy = 1.0;
+		    }
+
+		denom = fx * gy - fy * gx;
+		if (denom == 0.0)
+		    break;
+		dx = (-f * gy + g * fy) / denom;
+		dy = (-g * fx + f * gx) / denom;
+		x = x + dx;
+		y = y + dy;
+		if (MAX(MAX(fabs(dx),fabs(dy)),MAX(fabs(f),fabs(g))) < 2.80e-8)
+		    break;
+
+		niter = niter + 1;
+		}
+
+	    /* Reverse x and y if axes flipped */
+	    if (wcs->coorflip) {
+		tx = x;
+		x = y;
+		y = tx;
+		}
+	    }
+	}
+
+    /* Scale and rotate using CD matrix */
+    if (wcs->rotmat) {
+	*xpix = x * wcs->dc[0] + y * wcs->dc[1];
+	*ypix = x * wcs->dc[2] + y * wcs->dc[3];
+	}
+
+    else {
+
+	/* Correct for rotation */
+	if (wcs->rot!=0.0) {
+	    double cosr = cos (degrad (wcs->rot));
+	    double sinr = sin (degrad (wcs->rot));
+	    *xpix = x * cosr + y * sinr;
+	    *ypix = y * cosr - x * sinr;
+	    }
+	else {
+	    *xpix = x;
+	    *ypix = y;
+	    }
+
+	/* Scale using CDELT */
+	if (wcs->xinc != 0.)
+	    *xpix = *xpix / wcs->xinc;
+	if (wcs->yinc != 0.)
+	    *ypix = *ypix / wcs->yinc;
+	}
+
+    /* Convert to pixels  */
+    *xpix = *xpix + wcs->xrefpix;
+    *ypix = *ypix + wcs->yrefpix;
+
+    return (0);
+}
+
+
+/* TNXCLOSE -- free up the distortion surface pointers */
+
+void
+tnxclose (wcs)
+
+struct WorldCoor *wcs;		/* pointer to the WCS descriptor */
+
+{
+    if (wcs->lngcor != NULL)
+	wf_gsclose (wcs->lngcor);
+    if (wcs->latcor != NULL)
+	wf_gsclose (wcs->latcor);
+    return;
+}
+
+/* copyright(c) 1986 association of universities for research in astronomy inc.
+ * wfgsurfit.x -- surface fitting package used by wcs function drivers.
+ * Translated to C from SPP by Doug Mink, SAO, May 26, 1998
+ *
+ * the following routines are used by the experimental function drivers tnx
+ * and zpx to decode polynomial fits stored in the image header in the form
+ * of a list of parameters and coefficients into surface descriptors in
+ * ra / dec or longitude latitude. the polynomial surfaces so encoded consist
+ * of corrections to function drivers tan and zpn. the package routines are
+ * modelled after the equivalent gsurfit routines and are consistent with them.
+ * the routines are:
+ *
+ *                 sf = wf_gsopen (wattstr)
+ *                     wf_gsclose (sf)
+ *
+ *                  z = wf_gseval (sf, x, y)
+ *             ncoeff = wf_gscoeff (sf, coeff)
+ *               zder = wf_gsder (sf, x, y, nxder, nyder)
+ *
+ * wf_gsopen is used to open a surface fit encoded in a wcs attribute, returning
+ * the sf surface fitting descriptor.  wf_gsclose should be called later to free
+ * the descriptor.  wf_gseval is called to evaluate the surface at a point.
+ */
+
+
+#define  SZ_GSCOEFFBUF     20
+
+/* define the structure elements for the wf_gsrestore task */
+#define  TNX_SAVETYPE     0
+#define  TNX_SAVEXORDER   1
+#define  TNX_SAVEYORDER   2
+#define  TNX_SAVEXTERMS   3
+#define  TNX_SAVEXMIN     4
+#define  TNX_SAVEXMAX     5
+#define  TNX_SAVEYMIN     6
+#define  TNX_SAVEYMAX     7
+#define  TNX_SAVECOEFF    8
+
+
+/* wf_gsopen -- decode the longitude / latitude or ra / dec mwcs attribute
+ * and return a gsurfit compatible surface descriptor.
+ */
+
+struct IRAFsurface *
+wf_gsopen (astr)
+
+char    *astr;		/* the input mwcs attribute string */
+
+{
+    double dval;
+    char *estr;
+    int npar, szcoeff;
+    double *coeff;
+    struct IRAFsurface *gs;
+    struct IRAFsurface *wf_gsrestore();
+
+    if (astr[1] == 0)
+	return (NULL);
+
+    gs = NULL;
+    npar = 0;
+    szcoeff = SZ_GSCOEFFBUF;
+    coeff = (double *) malloc (szcoeff * sizeof (double));
+
+    estr = astr;
+    while (*estr != (char) 0) {
+	dval = strtod (astr, &estr);
+	if (*estr == '.')
+	    estr++;
+	if (*estr != (char) 0) {
+	    npar++;
+	    if (npar >= szcoeff) {
+		szcoeff = szcoeff + SZ_GSCOEFFBUF;
+		coeff = (double *) realloc (coeff, (szcoeff * sizeof (double)));
+		}
+	    coeff[npar-1] = dval;
+	    astr = estr;
+	    while (*astr == ' ') astr++;
+	    }
+        }
+
+    gs = wf_gsrestore (coeff);
+
+    free (coeff);
+
+    if (npar == 0)
+	return (NULL);
+    else
+	return (gs);
+}
+
+
+/* wf_gsclose -- procedure to free the surface descriptor */
+
+static void
+wf_gsclose (sf)
+
+struct IRAFsurface *sf;	/* the surface descriptor */
+
+{
+    if (sf != NULL) {
+	if (sf->xbasis != NULL)
+	    free (sf->xbasis);
+	if (sf->ybasis != NULL)
+	    free (sf->ybasis);
+	if (sf->coeff != NULL)
+	    free (sf->coeff);
+	free (sf);
+	}
+    return;
+}
+
+
+/* wf_gseval -- procedure to evaluate the fitted surface at a single point.
+ * the wf->ncoeff coefficients are stored in the vector pointed to by sf->coeff.
+ */
+
+double
+wf_gseval (sf, x, y)
+
+struct IRAFsurface *sf;	/* pointer to surface descriptor structure */
+double  x;		/* x value */
+double  y;		/* y value */
+{
+    double sum, accum;
+    int i, ii, k, maxorder, xorder;
+
+    /* Calculate the basis functions */
+    switch (sf->type) {
+        case TNX_CHEBYSHEV:
+            wf_gsb1cheb (x, sf->xorder, sf->xmaxmin, sf->xrange, sf->xbasis);
+            wf_gsb1cheb (y, sf->yorder, sf->ymaxmin, sf->yrange, sf->ybasis);
+	    break;
+        case TNX_LEGENDRE:
+            wf_gsb1leg (x, sf->xorder, sf->xmaxmin, sf->xrange, sf->xbasis);
+            wf_gsb1leg (y, sf->yorder, sf->ymaxmin, sf->yrange, sf->ybasis);
+	    break;
+        case TNX_POLYNOMIAL:
+            wf_gsb1pol (x, sf->xorder, sf->xbasis);
+            wf_gsb1pol (y, sf->yorder, sf->ybasis);
+	    break;
+        default:
+            fprintf (stderr,"TNX_GSEVAL: unknown surface type\n");
+	    return (0.0);
+        }
+
+    /* Initialize accumulator basis functions */
+    sum = 0.0;
+
+    /* Loop over y basis functions */
+    if (sf->xorder > sf->yorder)
+	maxorder = sf->xorder + 1;
+    else
+	maxorder = sf->yorder + 1;
+    xorder = sf->xorder;
+    ii = 0;
+
+    for (i = 0; i < sf->yorder; i++) {
+
+	/* Loop over the x basis functions */
+	accum = 0.0;
+	for (k = 0; k < xorder; k++) {
+	    accum = accum + sf->coeff[ii] * sf->xbasis[k];
+	    ii = ii + 1;
+	    }
+	accum = accum * sf->ybasis[i];
+	sum = sum + accum;
+
+        /* Elements of the coefficient vector where neither k = 1 or i = 1
+           are not calculated if sf->xterms = no. */
+        if (sf->xterms == TNX_XNONE)
+            xorder = 1;
+        else if (sf->xterms == TNX_XHALF) {
+            if ((i + 1 + sf->xorder + 1) > maxorder)
+                xorder = xorder - 1;
+	    }
+        }
+
+    return (sum);
+}
+
+
+/* TNX_GSCOEFF -- procedure to fetch the number and magnitude of the coefficients
+ * if the sf->xterms = wf_xbi (yes) then the number of coefficients will be
+ * (sf->xorder * sf->yorder); if wf_xterms is wf_xtri then the number
+ * of coefficients will be (sf->xorder *  sf->yorder - order *
+ * (order - 1) / 2) where order is the minimum of the x and yorders;  if
+ * sf->xterms = TNX_XNONE then the number of coefficients will be
+ * (sf->xorder + sf->yorder - 1).
+ */
+
+int
+wf_gscoeff (sf, coeff)
+
+struct IRAFsurface *sf;	/* pointer to the surface fitting descriptor */
+double	*coeff;		/* the coefficients of the fit */
+
+{
+    int ncoeff;		/* the number of coefficients */
+    int i;
+
+    /* Exctract coefficients from data structure and calculate their number */
+    ncoeff = sf->ncoeff;
+    for (i = 0; i < ncoeff; i++)
+	coeff[i] = sf->coeff[i];
+    return (ncoeff);
+}
+
+
+static double *coeff = NULL;
+static int nbcoeff = 0;
+
+/* wf_gsder -- procedure to calculate a new surface which is a derivative of
+ * the input surface.
+ */
+
+double
+wf_gsder (sf1, x, y, nxd, nyd)
+
+struct IRAFsurface *sf1; /* pointer to the previous surface */
+double	x;		/* x values */
+double	y;		/* y values */
+int	nxd, nyd;	/* order of the derivatives in x and y */
+{
+    int nxder, nyder, i, j, k, nbytes;
+    int order, maxorder1, maxorder2, nmove1, nmove2;
+    struct IRAFsurface *sf2 = 0;
+    double *ptr1, *ptr2;
+    double zfit, norm;
+    double wf_gseval();
+
+    if (sf1 == NULL)
+	return (0.0);
+
+    if (nxd < 0 || nyd < 0) {
+	fprintf (stderr, "TNX_GSDER: order of derivatives cannot be < 0\n");
+	return (0.0);
+	}
+
+    if (nxd == 0 && nyd == 0) {
+	zfit = wf_gseval (sf1, x, y);
+	return (zfit);
+	}
+
+    /* Allocate space for new surface */
+    sf2 = (struct IRAFsurface *) malloc (sizeof (struct IRAFsurface));
+
+    /* Check the order of the derivatives */
+    nxder = MIN (nxd, sf1->xorder - 1);
+    nyder = MIN (nyd, sf1->yorder - 1);
+
+    /* Set up new surface */
+    sf2->type = sf1->type;
+
+    /* Set the derivative surface parameters */
+    if (sf2->type == TNX_LEGENDRE ||
+	sf2->type == TNX_CHEBYSHEV ||
+	sf2->type == TNX_POLYNOMIAL) {
+
+	sf2->xterms = sf1->xterms;
+
+	/* Find the order of the new surface */
+	switch (sf2->xterms) {
+	    case TNX_XNONE: 
+		if (nxder > 0 && nyder > 0) {
+		    sf2->xorder = 1;
+		    sf2->yorder = 1;
+		    sf2->ncoeff = 1;
+		    }
+		else if (nxder > 0) {
+		    sf2->xorder = MAX (1, sf1->xorder - nxder);
+		    sf2->yorder = 1;
+		    sf2->ncoeff = sf2->xorder;
+		    }
+		else if (nyder > 0) {
+		    sf2->xorder = 1;
+		    sf2->yorder = MAX (1, sf1->yorder - nyder);
+		    sf2->ncoeff = sf2->yorder;
+		    }
+		break;
+
+	    case TNX_XHALF:
+		maxorder1 = MAX (sf1->xorder+1, sf1->yorder+1);
+		order = MAX(1, MIN(maxorder1-1-nyder-nxder,sf1->xorder-nxder));
+		sf2->xorder = order;
+		order = MAX(1, MIN(maxorder1-1-nyder-nxder,sf1->yorder-nyder));
+		sf2->yorder = order;
+		order = MIN (sf2->xorder, sf2->yorder);
+		sf2->ncoeff = sf2->xorder * sf2->yorder - (order*(order-1)/2);
+		break;
+
+	    default:
+		sf2->xorder = MAX (1, sf1->xorder - nxder);
+		sf2->yorder = MAX (1, sf1->yorder - nyder);
+		sf2->ncoeff = sf2->xorder * sf2->yorder;
+	    }
+
+	/* define the data limits */
+	sf2->xrange = sf1->xrange;
+	sf2->xmaxmin = sf1->xmaxmin;
+	sf2->yrange = sf1->yrange;
+	sf2->ymaxmin = sf1->ymaxmin;
+	}
+
+    else {
+	fprintf (stderr, "TNX_GSDER: unknown surface type %d\n", sf2->type);
+	return (0.0);
+	}
+
+    /* Allocate space for coefficients and basis functions */
+    nbytes = sf2->ncoeff * sizeof(double);
+    sf2->coeff = (double *) malloc (nbytes);
+    nbytes = sf2->xorder * sizeof(double);
+    sf2->xbasis = (double *) malloc (nbytes);
+    nbytes = sf2->yorder * sizeof(double);
+    sf2->ybasis = (double *) malloc (nbytes);
+
+    /* Get coefficients */
+    nbytes = sf1->ncoeff * sizeof(double);
+    if (nbytes > nbcoeff) {
+	if (nbcoeff > 0)
+	    coeff = (double *) realloc (coeff, nbytes);
+	else
+	    coeff = (double *) malloc (nbytes);
+	nbcoeff = nbytes;
+	}
+    (void) wf_gscoeff (sf1, coeff);
+
+    /* Compute the new coefficients */
+    switch (sf2->xterms) {
+	case TNX_XFULL:
+	    ptr2 = sf2->coeff + (sf2->yorder - 1) * sf2->xorder;
+	    ptr1 = coeff + (sf1->yorder - 1) * sf1->xorder;
+	    for (i = sf1->yorder - 1; i >= nyder; i--) {
+		for (j = i; j >= i-nyder+1; j--) {
+		    for (k = 0; k < sf2->xorder; k++)
+			ptr1[nxder+k] = ptr1[nxder+k] * (double)(j);
+		    }
+		for (j = sf1->xorder; j >= nxder+1; j--) {
+		    for (k = j; k >= j-nxder+1; k--)
+			ptr1[j-1] = ptr1[j-1] * (double)(k - 1);
+		    }
+		for (j = 0; j < sf2->xorder; j++)
+		    ptr2[j] = ptr1[nxder+j];
+		ptr2 = ptr2 - sf2->xorder;
+		ptr1 = ptr1 - sf1->xorder;
+		}
+	    break;
+
+	case TNX_XHALF:
+	    maxorder1 = MAX (sf1->xorder + 1, sf1->yorder + 1);
+	    maxorder2 = MAX (sf2->xorder + 1, sf2->yorder + 1);
+	    ptr2 = sf2->coeff + sf2->ncoeff;
+	    ptr1 = coeff + sf1->ncoeff;
+	    for (i = sf1->yorder; i >= nyder+1; i--) {
+		nmove1 = MAX (0, MIN (maxorder1 - i, sf1->xorder));
+		nmove2 = MAX (0, MIN (maxorder2 - i + nyder, sf2->xorder));
+		ptr1 = ptr1 - nmove1;
+		ptr2 = ptr2 - nmove2;
+		for (j = i; j > i - nyder + 1; j--) {
+		    for (k = 0; k < nmove2; k++)
+			ptr1[nxder+k] = ptr1[nxder+k] * (double)(j-1);
+		    }
+		for (j = nmove1; j >= nxder+1; j--) {
+		    for (k = j;  k >= j-nxder+1; k--)
+			ptr1[j-1] = ptr1[j-1] * (double)(k - 1);
+		    }
+		for (j = 0; j < nmove2; j++)
+		    ptr2[j] = ptr1[nxder+j];
+		}
+	    break;
+
+	default:
+	    if (nxder > 0 && nyder > 0)
+		sf2->coeff[0] = 0.0;
+
+	    else if (nxder > 0) { 
+		ptr1 = coeff;
+		ptr2 = sf2->coeff + sf2->ncoeff - 1;
+		for (j = sf1->xorder; j >= nxder+1; j--) {
+		    for (k = j; k >= j - nxder + 1; k--)
+			ptr1[j-1] = ptr1[j-1] * (double)(k - 1);
+		    ptr2[0] = ptr1[j-1];
+		    ptr2 = ptr2 - 1;
+		    }
+		}
+
+	    else if (nyder > 0) {
+		ptr1 = coeff + sf1->ncoeff - 1;
+		ptr2 = sf2->coeff;
+		for (i = sf1->yorder; i >= nyder + 1; i--) {
+		    for (j = i; j >= i - nyder + 1; j--)
+			*ptr1 = *ptr1 * (double)(j - 1);
+		    ptr1 = ptr1 - 1;
+		    }
+		for (i = 0; i < sf2->ncoeff; i++)
+		    ptr2[i] = ptr1[i+1];
+		}
+	}
+
+    /* evaluate the derivatives */
+    zfit = wf_gseval (sf2, x, y);
+
+    /* normalize */
+    if (sf2->type != TNX_POLYNOMIAL) { 
+	norm = pow (sf2->xrange, (double)nxder) *
+	       pow (sf2->yrange, (double)nyder);
+	zfit = norm * zfit;
+	}
+
+    /* free the space */
+    wf_gsclose (sf2);
+
+    return (zfit);
+}
+
+
+/* wf_gsrestore -- procedure to restore the surface fit encoded in the
+   image header as a list of double precision parameters and coefficients
+   to the surface descriptor for use by the evaluating routines. the
+   surface parameters, surface type, xorder (or number of polynomial
+   terms in x), yorder (or number of polynomial terms in y), xterms,
+   xmin, xmax and ymin and ymax, are stored in the first eight elements
+   of the double array fit, followed by the wf->ncoeff surface coefficients.
+ */
+
+struct IRAFsurface *
+wf_gsrestore (fit)
+
+double	*fit;			/* array containing the surface parameters
+				   and coefficients */
+{
+    struct IRAFsurface	*sf;	/* surface descriptor */
+    int	surface_type, xorder, yorder, order, i;
+    double xmin, xmax, ymin, ymax;
+
+    xorder = (int) (fit[TNX_SAVEXORDER] + 0.5);
+    if (xorder < 1) {
+	fprintf (stderr, "wf_gsrestore: illegal x order %d\n", xorder);
+	return (NULL);
+	}
+
+    yorder = (int) (fit[TNX_SAVEYORDER] + 0.5);
+    if (yorder < 1) {
+	fprintf (stderr, "wf_gsrestore: illegal y order %d\n", yorder);
+	return (NULL);
+	}
+
+    xmin = fit[TNX_SAVEXMIN];
+    xmax = fit[TNX_SAVEXMAX];
+    if (xmax <= xmin) {
+	fprintf (stderr, "wf_gsrestore: illegal x range %f-%f\n",xmin,xmax);
+	return (NULL);
+	}
+    ymin = fit[TNX_SAVEYMIN];
+    ymax = fit[TNX_SAVEYMAX];
+    if (ymax <= ymin) {
+	fprintf (stderr, "wf_gsrestore: illegal y range %f-%f\n",ymin,ymax);
+	return (NULL);
+	}
+
+    /* Set surface type dependent surface descriptor parameters */
+    surface_type = (int) (fit[TNX_SAVETYPE] + 0.5);
+
+    if (surface_type == TNX_LEGENDRE ||
+	surface_type == TNX_CHEBYSHEV ||
+	surface_type == TNX_POLYNOMIAL) {
+
+	/* allocate space for the surface descriptor */
+	sf = (struct IRAFsurface *) malloc (sizeof (struct IRAFsurface));
+	sf->xorder = xorder;
+	sf->xrange = 2.0 / (xmax - xmin);
+	sf->xmaxmin =  - (xmax + xmin) / 2.0;
+	sf->yorder = yorder;
+	sf->yrange = 2.0 / (ymax - ymin);
+	sf->ymaxmin =  - (ymax + ymin) / 2.0;
+	sf->xterms = fit[TNX_SAVEXTERMS];
+	switch (sf->xterms) {
+	    case TNX_XNONE:
+		sf->ncoeff = sf->xorder + sf->yorder - 1;
+		break;
+	    case TNX_XHALF:
+		order = MIN (xorder, yorder);
+		sf->ncoeff = sf->xorder * sf->yorder - order * (order-1) / 2;
+		break;
+	    case TNX_XFULL:
+		sf->ncoeff = sf->xorder * sf->yorder;
+		break;
+	    }
+	}
+    else {
+	fprintf (stderr, "wf_gsrestore: unknown surface type %d\n", surface_type);
+	return (NULL);
+	}
+
+    /* Set remaining curve parameters */
+    sf->type = surface_type;
+
+    /* Restore coefficient array */
+    sf->coeff = (double *) malloc (sf->ncoeff*sizeof (double));
+    for (i = 0; i < sf->ncoeff; i++)
+	sf->coeff[i] = fit[TNX_SAVECOEFF+i];
+
+    /* Allocate space for basis vectors */
+    sf->xbasis = (double *) malloc (sf->xorder*sizeof (double));
+    sf->ybasis = (double *) malloc (sf->yorder*sizeof (double));
+
+    return (sf);
+}
+
+
+/* wf_gsb1pol -- procedure to evaluate all the non-zero polynomial functions
+   for a single point and given order. */
+
+static void
+wf_gsb1pol (x, order, basis)
+
+double  x;		/*i data point */
+int     order;		/*i order of polynomial, order = 1, constant */
+double  *basis;		/*o basis functions */
+{
+    int     i;
+
+    basis[0] = 1.0;
+    if (order == 1)
+	return;
+
+    basis[1] = x;
+    if (order == 2)
+	return;
+
+    for (i = 2; i < order; i++)
+	basis[i] = x * basis[i-1];
+
+    return;
+}
+
+
+/* wf_gsb1leg -- procedure to evaluate all the non-zero legendre functions for
+   a single point and given order. */
+
+static void
+wf_gsb1leg (x, order, k1, k2, basis)
+
+double  x;		/*i data point */
+int     order;		/*i order of polynomial, order = 1, constant */
+double  k1, k2;		/*i normalizing constants */
+double	*basis;		/*o basis functions */
+{
+    int i;
+    double ri, xnorm;
+
+    basis[0] = 1.0;
+    if (order == 1)
+	return;
+
+    xnorm = (x + k1) * k2 ;
+    basis[1] = xnorm;
+    if (order == 2)
+        return;
+
+    for (i = 2; i < order; i++) {
+	ri = i;
+        basis[i] = ((2.0 * ri - 1.0) * xnorm * basis[i-1] -
+                       (ri - 1.0) * basis[i-2]) / ri;
+        }
+
+    return;
+}
+
+
+/* wf_gsb1cheb -- procedure to evaluate all the non-zero chebyshev function
+   coefficients for a given x and order. */
+
+static void
+wf_gsb1cheb (x, order, k1, k2, basis)
+
+double	x;		/*i number of data points */
+int	order;		/*i order of polynomial, 1 is a constant */
+double	k1, k2;		/*i normalizing constants */
+double	*basis;		/*o array of basis functions */
+{
+    int i;
+    double xnorm;
+
+    basis[0] = 1.0;
+    if (order == 1)
+	return;
+
+    xnorm = (x + k1) * k2;
+    basis[1] = xnorm;
+    if (order == 2)
+	return;
+
+    for (i = 2; i < order; i++)
+	basis[i] = 2. * xnorm * basis[i-1] - basis[i-2];
+
+    return;
+}
+
+/* Set surface polynomial from arguments */
+
+int
+tnxpset (wcs, xorder, yorder, xterms, coeff)
+
+struct WorldCoor *wcs;  /* World coordinate system structure */
+int	xorder;		/* Number of x coefficients (same for x and y) */
+int	yorder;		/* Number of y coefficients (same for x and y) */
+int	xterms;		/* Number of xy coefficients (same for x and y) */
+double	*coeff;		/* Plate fit coefficients */
+
+{
+    double *ycoeff;
+    struct IRAFsurface *wf_gspset ();
+
+    wcs->prjcode = WCS_TNX;
+
+    wcs->lngcor = wf_gspset (xorder, yorder, xterms, coeff);
+    ycoeff = coeff + wcs->lngcor->ncoeff;
+    wcs->latcor = wf_gspset (xorder, yorder, xterms, ycoeff);
+
+    return 0;
+}
+
+
+/* wf_gspset -- procedure to set the surface descriptor for use by the
+   evaluating routines.  from arguments.  The surface parameters are
+   surface type, xorder (number of polynomial terms in x), yorder (number
+   of polynomial terms in y), xterms, and the surface coefficients.
+ */
+
+struct IRAFsurface *
+wf_gspset (xorder, yorder, xterms, coeff)
+
+int	xorder;
+int	yorder;
+int	xterms;
+double	*coeff;
+{
+    struct IRAFsurface	*sf;	/* surface descriptor */
+    int	surface_type, order, i;
+    double xmin, xmax;
+    double ymin, ymax;
+
+    surface_type = TNX_POLYNOMIAL;
+    xmin = 0.0;
+    xmax = 0.0;
+    ymin = 0.0;
+    ymax = 0.0;
+
+    if (surface_type == TNX_LEGENDRE ||
+	surface_type == TNX_CHEBYSHEV ||
+	surface_type == TNX_POLYNOMIAL) {
+
+	/* allocate space for the surface descriptor */
+	sf = (struct IRAFsurface *) malloc (sizeof (struct IRAFsurface));
+	sf->xorder = xorder;
+	sf->xrange = 2.0 / (xmax - xmin);
+	sf->xmaxmin =  -(xmax + xmin) / 2.0;
+	sf->yorder = yorder;
+	sf->yrange = 2.0 / (ymax - ymin);
+	sf->ymaxmin =  - (ymax + ymin) / 2.0;
+	sf->xterms = xterms;
+	switch (sf->xterms) {
+	    case TNX_XNONE:
+		sf->ncoeff = sf->xorder + sf->yorder - 1;
+		break;
+	    case TNX_XHALF:
+		order = MIN (xorder, yorder);
+		sf->ncoeff = sf->xorder * sf->yorder - order * (order-1) / 2;
+		break;
+	    case TNX_XFULL:
+		sf->ncoeff = sf->xorder * sf->yorder;
+		break;
+	    }
+	}
+    else {
+	fprintf (stderr, "TNX_GSSET: unknown surface type %d\n", surface_type);
+	return (NULL);
+	}
+
+    /* Set remaining curve parameters */
+    sf->type = surface_type;
+
+    /* Restore coefficient array */
+    sf->coeff = (double *) malloc (sf->ncoeff*sizeof (double));
+    for (i = 0; i < sf->ncoeff; i++)
+	sf->coeff[i] = coeff[i];
+
+    /* Allocate space for basis vectors */
+    sf->xbasis = (double *) malloc (sf->xorder*sizeof (double));
+    sf->ybasis = (double *) malloc (sf->yorder*sizeof (double));
+
+    return (sf);
+}
+
+/* Mar 26 1998	New subroutines, translated from SPP
+ * Apr 28 1998  Change all local flags to TNX_* and projection flag to WCS_TNX
+ * May 11 1998	Fix use of pole longitude default
+ * Sep  4 1998	Fix missed assignment in tnxpos from Allen Harris, SAO
+ * Sep 10 1998	Fix bugs in tnxpix()
+ * Sep 10 1998	Fix missed assignment in tnxpix from Allen Harris, SAO
+ *
+ * Oct 22 1999	Drop unused variables, fix case statements after lint
+ * Dec 10 1999	Fix bug in gsder() which failed to allocate enough memory
+ * Dec 10 1999	Compute wcs->rot using wcsrotset() in tnxinit()
+ *
+ * Feb 14 2001	Fixed off-by-one bug in legendre evaluation (Mike Jarvis)
+ *
+ * Apr 11 2002	Fix bug when .-terminated substring in wf_gsopen()
+ * Apr 29 2002	Clean up code
+ * Jun 26 2002	Increase size of WAT strings from 500 to 2000
+ *
+ * Jun 27 2005	Drop unused arguments k1 and k2 from wf_gsb1pol()
+ *
+ * Jan  8 2007	Drop unused variable ncoeff in wf_gsder()
+ * Jan  9 2007	Declare header const char in tnxinit()
+ * Apr  3 2007	Fix offsets to hit last cooefficient in wf_gsder()
+ *
+ * Sep  5 2008	Fix wf_gseval() call in tnxpos() so unmodified x and y are used
+ * Sep  9 2008	Fix loop in TNX_XFULL section of wf_gsder()
+ * 		(last two bugs found by Ed Los)
+ * Sep 17 2008	Fix tnxpos for null correction case (fix by Ed Los)
+ */
diff --git a/Code/src/libwcs/ty2read.c b/Code/src/libwcs/ty2read.c
new file mode 100644
index 0000000000000000000000000000000000000000..e35b83d226439ebb70645948af4e9858c271b69a
--- /dev/null
+++ b/Code/src/libwcs/ty2read.c
@@ -0,0 +1,1464 @@
+/*** File libwcs/ty2read.c
+ *** July 9, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2000-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "fitsfile.h"
+#include "wcs.h"
+#include "wcscat.h"
+
+#define MAXREG 10000
+
+/* pathname of Tycho 2 CDROM or catalog search engine URL */
+char ty2cd[64]="/data/astrocat/tycho2";
+
+static double *gdist;	/* Array of distances to stars */
+static int ndist = 0;
+
+static int ty2reg();
+static int ty2regn();
+static int ty2zone();
+static int ty2size();
+struct StarCat *ty2open();
+void ty2close();
+static int ty2star();
+static int ty2size();
+
+
+/* TY2READ -- Read Tycho 2 Star Catalog stars from CDROM */
+
+int
+ty2read (refcat,cra,cdec,dra,ddec,drad,dradi,distsort,sysout,eqout,epout,
+	 mag1,mag2,sortmag,nstarmax,gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog)
+
+int	refcat;		/* Catalog code from wcscat.h */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	distsort;	/* 1 to sort stars by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 or 2) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*gnum;		/* Array of Guide Star numbers (returned) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double  *gpra;          /* Array of right ascension proper motions (returned) */
+double  *gpdec;         /* Array of declination proper motions (returned) */
+double	**gmag;		/* Array of b and v magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    double dist = 0.0;  /* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    int	faintstar=0;	/* Faintest star */
+    int	farstar=0;	/* Most distant star */
+    int nreg = 0;	/* Number of Tycho 2 regions in search */
+    int regnum[MAXREG];	/* List of region numbers */
+    int rlist[MAXREG];	/* List of first stars in regions */
+    int nlist[MAXREG];	/* List of number of stars per region */
+    char inpath[128];	/* Pathname for input region file */
+    int sysref = WCS_J2000;	/* Catalog coordinate system */
+    double eqref = 2000.0;	/* Catalog equinox */
+    double epref = 2000.0;	/* Catalog epoch */
+    double secmarg = 60.0;	/* Arcsec/century margin for proper motion */
+    struct StarCat *starcat;
+    struct Star *star;
+    int verbose;
+    int wrap;
+    int ireg;
+    int ierr;
+    int magsort, magsort1;
+    int jstar, iw;
+    int nrmax = MAXREG;
+    int nstar,i, ntot;
+    int istar, istar1, istar2;
+/*    int isp; */
+    int pass;
+    double num, ra, dec, rapm, decpm, mag, magb, magv, magve, magbe;
+    double rra1, rra2, rra2a, rdec1, rdec2;
+    double rdist, ddist;
+    char cstr[32], rastr[32], decstr[32];
+    char *str;
+
+    ntot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* If pathname is a URL, search and return */
+    if ((str = getenv("TY2_PATH")) == NULL )
+	str = ty2cd;
+    else
+	strncpy (ty2cd, str, 64);
+    if (!strncmp (str, "http:",5)) {
+	return (webread (ty2cd,"tycho2",distsort,cra,cdec,dra,ddec,drad,dradi,
+			 sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,
+			 gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog));
+	}
+
+    wcscstr (cstr, sysout, eqout, epout);
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* Make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+   if (sortmag == 2) {
+	magsort = 0;
+	magsort1 = 1;
+	}
+    else {
+	magsort = 1;
+	magsort1 = 0;
+	}
+
+    /* Allocate table for distances of stars from search center */
+    if (nstarmax > ndist) {
+	if (ndist > 0)
+	    free ((void *)gdist);
+	gdist = (double *) malloc (nstarmax * sizeof (double));
+	if (gdist == NULL) {
+	    fprintf (stderr,"TY2READ:  cannot allocate separation array\n");
+	    return (0);
+	    }
+	ndist = nstarmax;
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    nstar = 0;
+    jstar = 0;
+
+    /* Get RA and Dec limits in catalog (J2000) coordinates */
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,
+	    secmarg, &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+    if (wrap) {
+	rra2a = rra2;
+	rra2 = 360.0;
+	}
+    else {
+	rra2a = 0;
+	}
+
+    /* Write header if printing star entries as found */
+    if (nstarmax < 1) {
+	char *revmessage;
+	revmessage = getrevmsg();
+	printf ("catalog	Tycho-2\n");
+	ra2str (rastr, 31, cra, 3);
+	printf ("ra	%s\n", rastr);
+	dec2str (decstr, 31, cdec, 2);
+	printf ("dec	%s\n", decstr);
+	printf ("rpmunit	mas/year\n");
+	printf ("dpmunit	mas/year\n");
+	if (drad != 0.0) {
+	    printf ("radmin	%.1f\n", drad*60.0);
+	    if (dradi > 0)
+		printf ("radimin	%.1f\n", dradi*60.0);
+	    }
+	else {
+	    printf ("dramin	%.1f\n", dra*60.0* cosdeg (cdec));
+	    printf ("ddecmin	%.1f\n", ddec*60.0);
+	    }
+	printf ("radecsys	%s\n", cstr);
+	printf ("equinox	%.3f\n", eqout);
+	printf ("epoch	%.3f\n", epout);
+	printf ("program	scat %s\n", revmessage);
+	printf ("tycho2_id	ra          	dec         ");
+	printf ("	magb 	magv");
+	if (refcat == TYCHO2E)
+	    printf (" 	magbe	magve");
+	printf ("	ura   	udec  	arcmin\n");
+	printf ("----------	------------	------------");
+	printf ("	-----	-----");
+	if (refcat == TYCHO2E)
+	    printf ("	-----	-----");
+	printf ("	------	------	------\n");
+	}
+
+    /* If searching through RA = 0:00, split search in two */
+    for (iw = 0; iw <= wrap; iw++) {
+
+	/* Find Tycho 2 Star Catalog regions in which to search */
+	nreg = ty2reg (rra1,rra2,rdec1,rdec2,nrmax,regnum,rlist,nlist,verbose);
+	if (nreg <= 0) {
+	    fprintf (stderr,"TY2READ:  no Tycho 2 region for %.2f-%.2f %.2f %.2f\n",
+		     rra1, rra2, rdec1, rdec2);
+	    rra1 = 0.0;
+	    rra2 = rra2a;
+	    continue;
+	    }
+
+	/* Loop through region list */
+	for (ireg = 0; ireg < nreg; ireg++) {
+
+	    /* Open catalog file for this region */
+	    istar1 = rlist[ireg];
+	    istar2 = istar1 + nlist[ireg];
+	    if (verbose)
+		fprintf (stderr,"TY2READ: Searching stars %d through %d\n",
+			istar1, istar2-1);
+
+	    /* Open file for this region of Tycho 2 catalog */
+	    starcat = ty2open (rlist[ireg], nlist[ireg]);
+	    if (starcat == NULL) {
+		fprintf (stderr,"TY2READ: File %s not found\n",inpath);
+		return (0);
+		}
+
+	    /* Loop through catalog for this region */
+	    for (istar = istar1; istar < istar2; istar++) {
+		if ((ierr = ty2star (starcat, star, istar))) {
+		    /* fprintf (stderr,"TY2READ: Cannot read star %d\n", istar); */
+		    if (ierr < 3)
+			break;
+		    else
+			continue;
+		    }
+
+		/* ID number */
+		num = star->num;
+
+		/* Magnitude */
+		magb = star->xmag[0];
+		magv = star->xmag[1];
+		magbe = star->xmag[2];
+		magve = star->xmag[3];
+		mag = star->xmag[magsort];
+
+		/* Check magnitude limits */
+		pass = 1;
+		if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+		    pass = 0;
+
+		/* Check position limits */
+		if (pass) {
+
+		    /* Get position in output coordinate system */
+		    rapm = star->rapm;
+		    decpm = star->decpm;
+		    ra = star->ra;
+		    dec = star->dec;
+		    wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		 	     &ra, &dec, &rapm, &decpm);
+
+		    /* Compute distance from search center */
+		    if (drad > 0 || distsort)
+			dist = wcsdist (cra,cdec,ra,dec);
+		    else
+			dist = 0.0;
+
+		    /* Check radial distance to search center */
+		    if (drad > 0) {
+			if (dist > drad)
+			    pass = 0;
+			if (dradi > 0.0 && dist < dradi)
+			    pass = 0;
+			}
+
+		    /* Check distance along RA and Dec axes */
+		    else {
+			ddist = wcsdist (cra,cdec,cra,dec);
+			if (ddist > ddec)
+			    pass = 0;
+			rdist = wcsdist (cra,dec,ra,dec);
+		        if (rdist > dra)
+			   pass = 0;
+			}
+		    }
+
+		if (pass) {
+
+		/* Spectral Type
+		isp = (1000 * (int) star->isp[0]) + (int)star->isp[1]; */
+
+		/* Write star position and magnitudes to stdout */
+		    if (nstarmax < 1) {
+			ra2str (rastr, 31, ra, 3);
+			dec2str (decstr, 31, dec, 2);
+			dist = wcsdist (cra,cdec,ra,dec) * 60.0;
+			printf ("%010.5f	%s	%s", num,rastr,decstr);
+			printf ("	%5.2f	%5.2f", magb, magv);
+			if (refcat == TYCHO2E)
+			    printf ("	%5.2f	%5.2f", magbe, magve);
+			printf ("	%6.1f	%6.1f	%.2f\n",
+				rapm * 3600000.0 * cosdeg(dec),
+				decpm * 3600000.0, dist / 60.0);
+			}
+
+		    /* Save star position and magnitude in table */
+		    if (nstar < nstarmax) {
+			gnum[nstar] = num;
+			gra[nstar] = ra;
+			gdec[nstar] = dec;
+			gpra[nstar] = rapm;
+			gpdec[nstar] = decpm;
+			gmag[0][nstar] = magb;
+			gmag[1][nstar] = magv;
+			if (refcat == TYCHO2E) {
+			    gmag[2][nstar] = star->xmag[2];
+			    gmag[3][nstar] = star->xmag[3];
+			    }
+			gdist[nstar] = dist;
+			if (dist > maxdist) {
+			    maxdist = dist;
+			    farstar = nstar;
+			    }
+			if (mag > faintmag) {
+			    faintmag = mag;
+			    faintstar = nstar;
+			    }
+			}
+
+		    /* If too many stars and distance sorting,
+		       replace farthest star */
+		    else if (distsort) {
+			if (dist < maxdist) {
+			    gnum[farstar] = num;
+			    gra[farstar] = ra;
+			    gdec[farstar] = dec;
+			    gpra[farstar] = rapm;
+			    gpdec[farstar] = decpm;
+			    gmag[0][farstar] = magb;
+			    gmag[1][farstar] = magv;
+			    if (refcat == TYCHO2E) {
+				gmag[2][farstar] = star->xmag[2];
+				gmag[3][farstar] = star->xmag[3];
+				}
+			    gdist[farstar] = dist;
+
+			    /* Find new farthest star */
+			    maxdist = 0.0;
+			    for (i = 0; i < nstarmax; i++) {
+				if (gdist[i] > maxdist) {
+				    maxdist = gdist[i];
+				    farstar = i;
+				    }
+				}
+			    }
+			}
+
+		    /* Else if too many stars, replace faintest star */
+		    else if (mag < faintmag) {
+			gnum[faintstar] = num;
+			gra[faintstar] = ra;
+			gdec[faintstar] = dec;
+			gpra[faintstar] = rapm;
+			gpdec[faintstar] = decpm;
+			gmag[0][faintstar] = magb;
+			gmag[1][faintstar] = magv;
+			if (refcat == TYCHO2E) {
+			    gmag[2][faintstar] = star->xmag[2];
+			    gmag[3][faintstar] = star->xmag[3];
+			    }
+			gdist[faintstar] = dist;
+			faintmag = 0.0;
+
+			/* Find new faintest star */
+			for (i = 0; i < nstarmax; i++) {
+			    if (gmag[magsort1][i] > faintmag) {
+				faintmag = gmag[magsort1][i];
+				faintstar = i;
+				}
+			    }
+			}
+
+		    nstar++;
+		    if (nlog == 1)
+			fprintf (stderr,"TY2READ: %11.6f: %9.5f %9.5f %5.2f %5.2f\n",
+				 num,ra,dec,magb,magv);
+
+		    /* End of accepted star processing */
+		    }
+
+		/* Log operation */
+		jstar++;
+		if (nlog > 0 && istar%nlog == 0)
+		    fprintf (stderr,"TY2READ: %5d / %5d / %5d sources\r",
+			     nstar,jstar,starcat->nstars);
+
+		/* End of star loop */
+		}
+
+	    ntot = ntot + starcat->nstars;
+	    if (nlog > 0)
+		fprintf (stderr,"TY2READ: %4d / %4d: %5d / %5d  / %5d sources from region %4d    \n",
+		 	 ireg+1,nreg,nstar,jstar,starcat->nstars,regnum[ireg]);
+
+	    /* Close region input file */
+	    ty2close (starcat);
+	    }
+	rra1 = 0.0;
+	rra2 = rra2a;
+	}
+
+/* close output file and summarize transfer */
+    if (nlog > 0) {
+	if (nreg > 1)
+	    fprintf (stderr,"TY2READ: %d regions: %d / %d found\n",nreg,nstar,ntot);
+	else
+	    fprintf (stderr,"TY2READ: 1 region: %d / %d found\n",nstar,ntot);
+	if (nstar > nstarmax)
+	    fprintf (stderr,"TY2READ: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+    return (nstar);
+}
+
+/* TY2RNUM -- Read HST Guide Star Catalog stars from CDROM */
+
+int
+ty2rnum (refcat, nstars,sysout,eqout,epout,
+	 gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog)
+
+int	refcat;		/* Catalog code from wcscat.h */
+int	nstars;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*gnum;		/* Array of Guide Star numbers (returned) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double  *gpra;          /* Array of right ascension proper motions (returned) */
+double  *gpdec;         /* Array of declination proper motions (returned) */
+double	**gmag;		/* Array of B and V magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    char inpath[128];	/* Pathname for input region file */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    struct StarCat *starcat;
+    struct Star *star;
+    char *str;
+
+    int verbose;
+    int rnum;
+    int ierr;
+    int jstar;
+    int istar, istar1, istar2, jstar1, jstar2, nstar;
+/*    int isp; */
+    double num, ra, dec, rapm, decpm, magb, magv;
+
+    if (nlog == 1)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* If pathname is a URL, search and return */
+    if ((str = getenv("TY2_PATH")) == NULL )
+	str = ty2cd;
+    if (!strncmp (str, "http:",5))
+	return (webrnum (str,"tycho2",nstars,sysout,eqout,epout,1,
+			 gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog));
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+    nstar = 0;
+
+/* Loop through star list */
+    for (jstar = 0; jstar < nstars; jstar++) {
+	rnum = (int) (gnum[jstar] + 0.0000001);
+	
+	/* Find numbered stars (rrrr.nnnnn) */
+	if (gnum[jstar]-(double)rnum > 0.0000001) {
+	    ty2regn (rnum, &istar1, &istar2, verbose);
+	    nstar = istar2 - istar1 + 1;
+	    starcat = ty2open (istar1, nstar);
+    	    if (starcat == NULL) {
+		fprintf (stderr,"TY2RNUM: File %s not found\n",inpath);
+		return (0);
+		}
+	    for (istar = istar1; istar < istar2; istar++) {
+		if ((ierr = ty2star (starcat, star, istar))) {
+		    /* fprintf (stderr,"TY2RNUM: Cannot read star %d\n", istar); */
+		    gra[jstar] = 0.0;
+		    gdec[jstar] = 0.0;
+		    gmag[0][jstar] = 0.0;
+		    gmag[1][jstar] = 0.0;
+		    gmag[2][jstar] = 0.0;
+		    gmag[3][jstar] = 0.0;
+		    gtype[jstar] = 0;
+		    if (ierr < 3)
+			break;
+		    }
+		else {
+		    if (fabs (gnum[jstar] - star->num) < 0.0000005)
+			break;
+		    }
+		}
+	    ty2close (starcat);
+	    }
+	/* Find nth sequential stars in catalog (not rrrr.nnnnn) */
+	else {
+	    /* Find out whether file has CR/LF or LF only at end of lines */
+	    rnum = 1;
+	    ty2regn (rnum, &jstar1, &jstar2, verbose);
+
+	    istar = (int) (gnum[jstar] + 0.01);
+	    starcat = ty2open (istar, 10);
+    	    if (starcat == NULL) {
+		fprintf (stderr,"TY2RNUM: File %s not found\n",inpath);
+		return (0);
+		}
+	    if ((ierr = ty2star (starcat, star, istar))) {
+		/* fprintf (stderr,"TY2RNUM: Cannot read star %d\n", istar); */
+		gra[jstar] = 0.0;
+		gdec[jstar] = 0.0;
+		gmag[0][jstar] = 0.0;
+		gmag[1][jstar] = 0.0;
+		if (refcat == TYCHO2E) {
+		    gmag[2][jstar] = 0.0;
+		    gmag[3][jstar] = 0.0;
+		    }
+		gtype[jstar] = 0;
+		if (ierr < 3)
+		    break;
+		else
+		    continue;
+		}
+	    ty2close (starcat);
+	    }
+
+	/* If star has been found in catalog */
+
+	/* ID number */
+	num = star->num;
+
+	/* Position in degrees at designated epoch */
+	ra = star->ra;
+	dec = star->dec;
+	rapm = star->rapm;
+	decpm = star->decpm;
+	wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		     &ra, &dec, &rapm, &decpm);
+
+	/* Magnitude */
+	magb = star->xmag[0];
+	magv = star->xmag[1];
+
+	/* Spectral Type
+	isp = (1000 * (int) star->isp[0]) + (int)star->isp[1]; */
+
+	/* Save star position and magnitude in table */
+	gnum[jstar] = num;
+	gra[jstar] = ra;
+	gdec[jstar] = dec;
+	gpra[jstar] = rapm;
+	gpdec[jstar] = decpm;
+	gmag[0][jstar] = magb;
+	gmag[1][jstar] = magv;
+	if (refcat == TYCHO2E) {
+	    gmag[2][jstar] = star->xmag[2];
+	    gmag[3][jstar] = star->xmag[3];
+	    }
+	/* gtype[jstar] = isp; */
+	if (nlog == 1)
+	    fprintf (stderr,"TY2RNUM: %11.6f: %9.5f %9.5f %5.2f %5.2f %s  \n",
+		     num, ra, dec, magb, magv, star->isp);
+
+	/* End of star loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"TY2RNUM: %d / %d found\n", nstar, nstars);
+
+    return (nstars);
+}
+
+
+/* TY2BIN -- Read Tycho 2 Star Catalog stars from CDROM */
+
+int
+ty2bin (wcs, header, image, mag1, mag2, sortmag, magscale, nlog)
+
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 or 2) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    int nreg = 0;	/* Number of Tycho 2 regions in search */
+    int regnum[MAXREG];	/* List of region numbers */
+    int rlist[MAXREG];	/* List of first stars in regions */
+    int nlist[MAXREG];	/* List of number of stars per region */
+    char inpath[128];	/* Pathname for input region file */
+    int sysref = WCS_J2000;	/* Catalog coordinate system */
+    double eqref = 2000.0;	/* Catalog equinox */
+    double epref = 2000.0;	/* Catalog epoch */
+    double secmarg = 60.0;	/* Arcsec/century margin for proper motion */
+    struct StarCat *starcat;
+    struct Star *star;
+    int verbose;
+    int wrap;
+    int ireg;
+    int ierr;
+    int magsort;
+    int jstar, iw;
+    int nrmax = MAXREG;
+    int nstar, ntot;
+    int istar, istar1, istar2;
+    int pass;
+    double num, ra, dec, mag, magb, magv;
+    double rra1, rra2, rra2a, rdec1, rdec2;
+    char cstr[32];
+    char *str;
+    double xpix, ypix, flux;
+    int ix, iy, offscl;
+    int bitpix, w, h;   /* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+
+    /* Get image dimensions */
+    bitpix = 0;
+    (void)hgeti4 (header,"BITPIX",&bitpix);
+    w = 0;
+    (void)hgeti4 (header,"NAXIS1",&w);
+    h = 0;
+    (void)hgeti4 (header,"NAXIS2",&h);
+    if (bitpix * w * h < 1) {
+	fprintf (stderr, "TY2BIN: No pixels in image = %d bytes x %d x %d\n",
+		 bitpix, w, h);
+	return (0);
+	}
+
+    ntot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* If pathname is a URL, search and return */
+    if ((str = getenv("TY2_PATH")) != NULL )
+	strncpy (ty2cd, str, 64);
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* Make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+   if (sortmag == 2)
+	magsort = 0;
+    else
+	magsort = 1;
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    nstar = 0;
+    jstar = 0;
+
+    /* Get RA and Dec limits in catalog (J2000) coordinates */
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,
+	    secmarg, &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+    if (wrap) {
+	rra2a = rra2;
+	rra2 = 360.0;
+	}
+    else {
+	rra2a = 0.0;
+	}
+
+    /* If searching through RA = 0:00, split search in two */
+    for (iw = 0; iw <= wrap; iw++) {
+
+	/* Find Tycho 2 Star Catalog regions in which to search */
+	nreg = ty2reg (rra1,rra2,rdec1,rdec2,nrmax,regnum,rlist,nlist,verbose);
+	if (nreg <= 0) {
+	    fprintf (stderr,"TY2BIN:  no Tycho 2 region for %.2f-%.2f %.2f %.2f\n",
+		     rra1, rra2, rdec1, rdec2);
+	    rra1 = 0.0;
+	    rra2 = rra2a;
+	    continue;
+	    }
+
+	/* Loop through region list */
+	for (ireg = 0; ireg < nreg; ireg++) {
+
+	    /* Open catalog file for this region */
+	    istar1 = rlist[ireg];
+	    istar2 = istar1 + nlist[ireg];
+	    /* if (verbose)
+		fprintf (stderr,"TY2BIN: Searching stars %d through %d\n",
+			istar1, istar2-1); */
+
+	    /* Open file for this region of Tycho 2 catalog */
+	    starcat = ty2open (rlist[ireg], nlist[ireg]);
+	    if (starcat == NULL) {
+		fprintf (stderr,"TY2BIN: File %s not found\n",inpath);
+		return (0);
+		}
+
+	    /* Loop through catalog for this region */
+	    for (istar = istar1; istar < istar2; istar++) {
+		if ((ierr = ty2star (starcat, star, istar))) {
+		    /* fprintf (stderr,"TY2BIN: Cannot read star %d\n", istar); */
+		    if (ierr < 3)
+			break;
+		    else
+			continue;
+		    }
+
+		/* ID number */
+		num = star->num;
+		ra = star->ra;
+		dec = star->dec;
+
+		/* Magnitude */
+		magb = star->xmag[0];
+		magv = star->xmag[1];
+		mag = star->xmag[magsort];
+
+		/* Check magnitude limits */
+		pass = 1;
+		if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+		    pass = 0;
+
+		/* If this star was searched in first pass, skip it */
+		if (iw > 0 && ra > rra2)
+		    pass = 0;
+
+		/* Save star in FITS image */
+		if (pass) {
+		    wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+		    if (!offscl) {
+			if (magscale > 0.0)
+			    flux = magscale * exp (logt * (-mag / 2.5));
+			else
+			    flux = 1.0;
+			ix = (int) (xpix + 0.5);
+			iy = (int) (ypix + 0.5);
+			addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+			nstar++;
+			jstar++;
+			}
+		    else {
+			ix = 0;
+			iy = 0;
+			}
+		    if (nlog == 1) {
+			fprintf (stderr,"TY2BIN: %11.5f: %9.5f %9.5f %s",
+				 num,ra,dec,cstr);
+			if (magscale > 0.0)
+			    fprintf (stderr, " %5.2f", mag);
+			if (!offscl)
+			    flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+			else
+			    flux = 0.0;
+			fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+			}
+
+		    /* End of accepted star processing */
+		    }
+
+		/* Log operation
+		jstar++;
+		if (nlog > 0 && istar%nlog == 0)
+		    fprintf (stderr,"TY2BIN: %5d / %5d / %5d sources\r",
+			     nstar,jstar,starcat->nstars);
+
+		   End of star loop */
+		}
+
+	    ntot = ntot + starcat->nstars;
+	    if (nlog > 0)
+		fprintf (stderr,"TY2BIN: %4d / %4d: %5d / %5d  / %5d sources from region %4d    \r",
+		 	 ireg+1,nreg,nstar,jstar,starcat->nstars,regnum[ireg]);
+
+	    /* Close region input file */
+	    ty2close (starcat);
+	    }
+	rra1 = 0.0;
+	rra2 = rra2a;
+	}
+
+/* close output file and summarize transfer */
+    if (nlog > 0) {
+	if (nreg > 1)
+	    fprintf (stderr,"\nTY2BIN: %d regions: %d / %d found\n",nreg,nstar,ntot);
+	else
+	    fprintf (stderr,"\nTY2BIN: 1 region: %d / %d found\n",nstar,ntot);
+	}
+    return (nstar);
+}
+
+
+/* Tycho 2 region index for ty2regn() and ty2reg() */
+
+/* First region in each declination zone */
+static int treg1[24]={9490,9346,9134,8840,8464,8022,7523,6989,6412,5838,5260,4663,
+	       1,594,1178,1729,2259,2781,3246,3652,4014,4294,4492,4615};
+ 
+/* Last region in each declination zone */
+static int treg2[24]={9537,9489,9345,9133,8839,8463,8021,7522,6988,6411,5837,5259,
+	       593,1177,1728,2258,2780,3245,3651,4013,4293,4491,4614,4662};
+
+static int indnchar = 0;	/* Number of characters per line in table */
+
+/* TY2REGN -- read the range of stars in a region from the Tycho 2 Catalog
+ * index table.
+ */
+
+static int
+ty2regn (region, star1, star2, verbose)
+
+int	region;		/* Region to find */
+int	*star1;		/* First star number in region (returned)*/
+int	*star2;		/* Last star number in region (returned)*/
+int	verbose;	/* 1 for diagnostics */
+
+{
+    char *tabpath;	/* Pathname for regions table */
+    char *buffer;	/* Buffer to hold index table */
+    char *line;
+    char *str;
+    char lf=(char)10;
+    int deczone;
+    int lpath;
+
+    *star1 = 0;
+    *star2 = 0;
+
+/* Find declination zone(s) in which this region exists */
+    for (deczone = 0; deczone < 24; deczone++) {
+	if (region >= treg1[deczone] && region <= treg2[deczone])
+	    break;
+	}
+    if (deczone > 24)
+	return (0);
+
+/* Set path to Tycho 2 Catalog CDROM */
+    if ((str = getenv("TY2_PATH")) != NULL ) {
+	lpath = strlen (str) + 16;
+	tabpath = (char *) malloc (lpath);
+	strcpy (tabpath, str);
+	}
+    else {
+	lpath = strlen (ty2cd) + 16;
+	tabpath = (char *) malloc (lpath);
+	strcpy (tabpath, ty2cd);
+	}
+
+/* Set pathname for index table file */
+    strcat (tabpath,"/data/index.dat");
+
+/* Read the index table */
+    if ((buffer = getfilebuff (tabpath)) == NULL) {
+	fprintf (stderr,"TY2REG:  error reading region table %s\n",tabpath);
+	return (0);
+	}
+
+/* Figure out whether index file has LF or CRLF at end of lines */
+    if (buffer[42] == lf)
+	indnchar = 43;
+    else
+	indnchar = 44;
+
+/* Read first star from regionth line of region table */
+    line = buffer + ((region - 1) * indnchar);
+    *star1 = atoi (line);
+
+/* Read last star + 1 from region+1th line of region table */
+    *star2 = atoi (line+indnchar);
+    free (buffer);
+    free (tabpath);
+    return (1);
+}
+
+
+/* TY2REG -- search the Tycho 2 Catalog index table for fields
+ * in the specified range of coordinates and magnitudes.
+ * Build lists containing the first star and number of stars for each range.
+ */
+
+static int
+ty2reg (ra1, ra2, dec1, dec2, nrmax, regnum, rstar, nstar, verbose)
+
+double	ra1, ra2;	/* Right ascension limits in degrees */
+double	dec1, dec2; 	/* Declination limits in degrees */
+int	nrmax;		/* Maximum number of regions to find */
+int	*regnum;	/* Region numbers (returned)*/
+int	*rstar;		/* Region first star numbers (returned)*/
+int	*nstar;		/* Region numbers of stars (returned)*/
+int	verbose;	/* 1 for diagnostics */
+
+{
+    int nrgn;		/* Number of regions found (returned) */
+    char *tabpath;	/* Pathname for regions table */
+    char *buffer;	/* Buffer to hold index table */
+    char *line;
+    char *str;
+    char lf=(char)10;
+    int nwrap;		/* 1 if 0h included in RA span*/
+    int iwrap;
+    int num1, num2;
+    int irow,iz1,iz2,jr1,jr2,i;
+    int ir1 = 0;
+    int ir2 = 0;
+    int nsrch,nsrch1;
+    double ralow, rahi;
+    double declow, dechi, decmin, decmax;
+
+    for (i = 0; i < nrmax; i++) {
+	rstar[i] = 0;
+	nstar[i] = 0;
+	}
+    nrgn = 0;
+
+/* Set path to Tycho 2 Catalog CDROM */
+    if ((str = getenv("TY2_PATH")) != NULL ) {
+	tabpath = (char *) malloc (strlen (str) + 16);
+	strcpy (tabpath, str);
+	}
+    else {
+	tabpath = (char *) malloc (strlen (ty2cd) + 16);
+	strcpy (tabpath, ty2cd);
+	}
+
+/* Set pathname for index table file */
+    strcat (tabpath,"/data/index.dat");
+
+/* Read the index table */
+    if ((buffer = getfilebuff (tabpath)) == NULL) {
+	fprintf (stderr,"TY2REG:  error reading region table %s\n",tabpath);
+	return (0);
+	}
+
+/* Figure out whether index file has LF or CRLF at end of lines */
+    if (buffer[42] == lf)
+	indnchar = 43;
+    else
+	indnchar = 44;
+
+/* Find region range to search based on declination */
+    iz1 = ty2zone (dec1);
+    iz2 = ty2zone (dec2);
+    jr1 = 0;
+    jr2 = 0;
+    nwrap = 1;
+
+/* Search in only one region */
+    if (iz1 == iz2) {
+	ir1 = treg1[iz1];
+	ir2 = treg2[iz1];
+	}
+/* Search region in northern hemisphere */
+    if (dec1 >= 0 && dec2 >= 0) {
+	if (dec1 < dec2) {
+	    ir1 = treg1[iz1];
+	    ir2 = treg2[iz2];
+	    }
+	else {
+	    ir1 = treg1[iz2];
+	    ir2 = treg2[iz1];
+	    }
+	}
+
+/* Search region in southern hemisphere with multiple regions */
+    else if (dec1 < 0 && dec2 < 0) {
+	if (dec1 < dec2) {
+	    ir1 = treg1[iz2];
+	    ir2 = treg2[iz1];
+	    }
+	else {
+	    ir1 = treg1[iz1];
+	    ir2 = treg2[iz2];
+	    }
+	}
+
+/* Search region spans equator */
+    else if (dec1 < 0 && dec2 >= 0) {
+	nwrap = 2;
+
+	/* southern part */
+	jr1 = treg1[11];
+	jr2 = treg2[iz1];
+
+	/* northern part */
+	ir1 = treg1[12];
+	ir2 = treg2[iz2];
+	}
+
+    nsrch = ir2 - ir1 + 1;
+    if (verbose)
+	fprintf (stderr,"TY2REG: searching %d regions: %d - %d\n",nsrch,ir1,ir2);
+    if (jr1 > 0) {
+	nsrch1 = jr2 - jr1 + 1;
+	if (verbose)
+	    fprintf (stderr,"TY2REG: searching %d regions: %d - %d\n",nsrch1,jr1,jr2);
+	}
+    if (verbose)
+	fprintf(stderr,"TY2REG: RA: %.5f - %.5f, Dec: %.5f - %.5f\n",ra1,ra2,dec1,dec2);
+
+    nrgn = 0;
+
+    for (iwrap = 0; iwrap < nwrap; iwrap++) {
+
+	for (irow = ir1 - 1; irow < ir2; irow++) {
+
+	/* Read next line of region table */
+	    line = buffer + (irow * indnchar);
+
+	/* Declination range of the gs region */
+	/* note:  southern dechi and declow are reversed */
+	    num1 = atoi (line);
+	    num2 = atoi (line+indnchar);
+	    dechi = atof (line + 29);
+	    declow = atof (line + 36);
+	    if (dechi > declow) {
+		decmin = declow - 0.1;
+		decmax = dechi + 0.1;
+		}
+	    else {
+		decmax = declow + 0.1;
+		decmin = dechi - 0.1;
+		}
+
+	    if (decmax >= dec1 && decmin <= dec2) {
+
+	    /* Right ascension range of the Guide Star Catalog region */
+		ralow = atof (line + 15) - 0.1;
+		if (ralow <= 0.0) ralow = 0.0;
+		rahi = atof (line + 22) + 0.1;
+		if (rahi > 360.0) rahi = 360.0;
+		if (rahi <= 0.0) rahi = 360.0;
+
+	    /* Check RA if 0h RA not between region RA limits */
+		if (ra1 < ra2) {
+
+		    /* Add this region to list, if there is space */
+		    if (ralow <= ra2 && rahi >= ra1) {
+			/* if (verbose)
+			    fprintf (stderr,"TY2REG: Region %d added to search\n",irow);
+			    */
+
+			if (nrgn < nrmax) {
+			    regnum[nrgn] = irow;
+			    rstar[nrgn] = num1;
+			    nstar[nrgn] = num2 - num1;
+			    nrgn = nrgn + 1;
+			    }
+			}
+		    }
+
+	    /* Check RA if 0h RA is between region RA limits */
+		else {
+		    if (ralow > rahi) rahi = rahi + 360.0;
+		    if (ralow <= ra2 || rahi >= ra1) {
+
+		    /* Add this region to list, if there is space */
+			/* if (verbose)
+			    fprintf (stderr,"TY2REG: Region %d added to search\n", irow);
+			    */
+
+			if (nrgn < nrmax) {
+			    regnum[nrgn] = irow;
+			    rstar[nrgn] = num1;
+			    nstar[nrgn] = num2 - num1;
+			    nrgn = nrgn + 1;
+			    }
+			}
+		    }
+		}
+	    }
+
+/* Handle wrap-around through the equator */
+	ir1 = jr1;
+	ir2 = jr2;
+	jr1 = 0;
+	jr2 = 0;
+	}
+
+    free (buffer);
+    return (nrgn);
+}
+
+ 
+ 
+/*  TY2ZONE -- find the zone number where a declination can be found */
+ 
+static int
+ty2zone (dec)
+ 
+double dec;		/* declination in degrees */
+ 
+{
+int zone;		/* gsc zone (returned) */
+double  zonesize;
+int ndeczones = 12;	/* number of declination zones per hemisphere */
+double zdec = dec + 90.0;
+ 
+/* width of declination zones */
+    zonesize = 90.0 / ndeczones;
+ 
+    zone = (int) (zdec / zonesize);
+    if (zone < 0)
+	zone = 0;
+    if (zone > 23)
+	zone = 23;
+ 
+    return (zone);
+}
+
+
+
+/* TY2OPEN -- Open Tycho 2 catalog file, returning number of entries */
+
+struct StarCat *
+ty2open (nstar, nread)
+
+int	nstar;	/* Number of first star to read */
+int	nread;	/* Number of star entries to read */
+
+{
+    FILE *fcat;
+    struct StarCat *sc;
+    int lfile, lpath;
+    int lread, lskip, nr;
+    char *str;
+    char *ty2file;
+    char *ty2path;	/* Full pathname for catalog file */
+
+    /* Set path to Tycho 2 Catalog CDROM */
+    if ((str = getenv("TY2_PATH")) != NULL ) {
+	lpath = strlen(str) + 18;
+	ty2path = (char *) malloc (lpath);
+	strcpy (ty2path, str);
+	}
+    else {
+	lpath = strlen(ty2cd) + 18;
+	ty2path = (char *) malloc (lpath);
+	strcpy (ty2path, ty2cd);
+	}
+
+    /* Set pathname for catalog file */
+    strcat (ty2path, "/data/catalog.dat");
+
+    /* Find length of Tycho 2 catalog file */
+    lfile = ty2size (ty2path);
+
+    /* Check for existence of catalog */
+    if (lfile < 2) {
+	fprintf (stderr,"TY2OPEN: Binary catalog %s has no entries\n",ty2path);
+	free (ty2path);
+	return (NULL);
+	}
+
+    /* Open Tycho 2 file */
+    if (!(fcat = fopen (ty2path, "r"))) {
+	fprintf (stderr,"TY2OPEN: Tycho 2 file %s cannot be read\n",ty2path);
+	free (ty2path);
+	return (0);
+	}
+
+    /* Set Tycho 2 catalog header information */
+    sc = (struct StarCat *) calloc (1, sizeof (struct StarCat));
+    sc->byteswapped = 0;
+
+    if (indnchar == 44)
+	sc->nbent = 208;
+    else
+	sc->nbent = 207;
+    sc->nstars = lfile / sc->nbent;
+
+    /* Separate filename from pathname and save in structure */
+    ty2file = strrchr (ty2path,'/');
+    if (ty2file)
+	ty2file = ty2file + 1;
+    else
+	ty2file = ty2path;
+    if (strlen (ty2file) < 24)
+	strcpy (sc->isfil, ty2file);
+    else
+	strncpy (sc->isfil, ty2file, 23);
+
+    /* Set other catalog information in structure */
+    sc->inform = 'J';
+    sc->coorsys = WCS_J2000;
+    sc->epoch = 2000.0;
+    sc->equinox = 2000.0;
+    sc->ifcat = fcat;
+    sc->sptype = 2;
+
+    /* Tycho 2 stars are not RA-sorted within regions */
+    sc->rasorted = 0;
+
+    /* Read part of catalog into a buffer */
+    lread = nread * sc->nbent;
+    lskip = (nstar - 1) * sc->nbent;
+    sc->catdata = NULL;
+    if ((sc->catdata = calloc (1, lread+1)) != NULL) {
+	fseek (fcat, lskip, 0);
+	nr = fread (sc->catdata, 1, lread, fcat);
+	if (nr < lread) {
+	    fprintf (stderr,"TY2OPEN: Read %d / %d bytes\n", nr, lread);
+            ty2close (sc);
+	    free (ty2path);
+            return (NULL);
+            }
+	sc->catlast = sc->catdata + lread;
+	}
+    else {
+	fprintf (stderr,"TY2OPEN: Cannot allocate %d-byte buffer.\n", lread);
+        ty2close (sc);
+	free (ty2path);
+	return (NULL);
+	}
+    sc->istar = nstar;
+    free (ty2path);
+    return (sc);
+}
+
+
+void
+ty2close (sc)
+struct StarCat *sc;	/* Star catalog descriptor */
+{
+    fclose (sc->ifcat);
+    if (sc->catdata != NULL)
+	free (sc->catdata);
+    free (sc);
+    return;
+}
+
+
+/* TY2STAR -- Get Tycho 2 catalog entry for one star;
+              return 0 if successful */
+
+static int
+ty2star (sc, st, istar)
+
+struct StarCat *sc;	/* Star catalog descriptor */
+struct Star *st;	/* Current star entry */
+int istar;	/* Star sequence number in Tycho 2 catalog region file */
+{
+    char *line;
+    double regnum, starnum, multnum;
+
+    /* Drop out if catalog pointer is not set */
+    if (sc == NULL) {
+	fprintf (stderr, "TY2STAR:  Catalog pointer not set\n");
+	return (1);
+	}
+
+    /* Drop out if catalog is not open */
+    if (sc->ifcat == NULL) {
+	fprintf (stderr, "TY2STAR:  Catalog is not open\n");
+	return (2);
+	}
+
+    /* Drop out if star number is too large */
+    if (istar > sc->nstars) {
+	fprintf (stderr, "TY2STAR:  %d  > %d is not in catalog\n",
+		 istar, sc->nstars);
+	return (3);
+	}
+
+    /* Move buffer pointer to start of correct star entry */
+    if (istar > 0) {
+	line = sc->catdata + ((istar - sc->istar) * sc->nbent);
+	if (line >= sc->catlast) {
+	    fprintf (stderr, "TY2STAR:  star %d past buffer\n", istar);
+	    return (4);
+	    }
+	}
+    else {
+	line = sc->catdata;
+	}
+
+    /* Read catalog entry */
+    if (sc->nbent > sc->catlast-line) {
+	fprintf (stderr, "TY2STAR:  %d / %d bytes read for star %d\n",
+		 sc->catlast - line, sc->nbent, istar);
+	return (5);
+	}
+
+    regnum = atof (line);
+    starnum = atof (line+5);
+    multnum = atof (line+11);
+    st->num = regnum + (0.0001 * starnum) + (0.00001 * multnum);
+
+    if (line[13] == 'X') {
+	fprintf (stderr, "TY2STAR:  No position for star %010.5f\n", st->num);
+	return (6);
+	}
+
+    /* Read position in degrees */
+    st->ra = atof (line+15);
+    st->dec = atof (line+28);
+
+    /* Read proper motion and convert it to degrees (of RA and Dec) per year */
+    st->rapm = (atof (line+41) / 3600000.0) / cosdeg (st->dec);
+    st->decpm = atof (line+49) / 3600000.0;
+
+    /* Set B magnitude and error */
+    st->xmag[0] = atof (line+110);
+    st->xmag[2] = atof (line+117);
+
+    /* Set V magnitude and error */
+    st->xmag[1] = atof (line+123);
+    st->xmag[3] = atof (line+130);
+
+    /* Set main sequence spectral type
+    st->isp[0] = (char)0;
+    st->isp[1] = (char)0;
+    bv2sp (NULL, st->xmag[1], st->xmag[0], st->isp); */
+
+    return (0);
+}
+
+/* TY2SIZE -- return size of Tycho 2 catalog file in bytes */
+
+static int
+ty2size (filename)
+
+char	*filename;	/* Name of file for which to find size */
+{
+    FILE *diskfile;
+    long filesize;
+
+    /* Open file */
+    if ((diskfile = fopen (filename, "r")) == NULL)
+	return (-1);
+
+    /* Move to end of the file */
+    if (fseek (diskfile, 0, 2) == 0)
+
+	/* Position is the size of the file */
+	filesize = ftell (diskfile);
+
+    else
+	filesize = -1;
+
+    fclose (diskfile);
+
+    return (filesize);
+}
+
+
+/* Jun  2 2000	New program, based on actread.c and gscread.c
+ * Jun 13 2000	Correctly order magnitudes: 0=V, 1=B
+ * Jun 26 2000	Add coordinate system to SearchLim() arguments
+ * Sep 25 2000	Set sc->sptype to 2 to indicate presence of spectral type
+ * Nov 29 2000	Add option to read catalog using HTTP
+ * Dec 11 2000	Accept catalog search engine URL in ty2cd[]
+ *
+ * Jan 11 2001	All printing goes to stderr
+ * Jun 14 2001	Drop spectral type approximation
+ * Jun 15 2001	In ty2reg(), add 0.1 to tabulated region limits
+ * Jun 19 2001	When no region found, print RA and Dec limits used
+ * Jun 27 2001	Allocate gdist only if needed
+ * Sep 11 2001	Change to single magnitude argeument
+ * Sep 11 2001	Add sort magnitude argument to uacread()
+ * Nov 20 2001	Change cos(degrad()) to cosdeg()
+ * Dec  3 2001	Change default directory to /data/astrocat/tycho2
+ *
+ * Apr  3 2002	Fix bug so magnitude filtering is actually done (all passed)
+ * Apr  8 2002	Fix uninitialized variable
+ * Apr 10 2002	Separate catalog and output sort mags (in:vb out: bv)
+ * Oct  3 2002	Print stars as found in ty2read() if nstarmax < 1
+ *
+ * Feb  3 2003	Include math.h because of fabs()
+ * Feb 27 2003	Add 60 arcsec/century to margins of search box to get moving stars
+ * Mar 11 2003	Fix position limit testing
+ * Apr  3 2003	Drop unused variables after lint
+ * Apr 14 2003	Explicitly get revision date if nstarmax < 1
+ * Jun  2 2003	Print proper motion as mas/year
+ * Aug  8 2003	Increase MAXREG from 100 to 1000
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Sep 26 2003	Add ty2bin() to fill an image with sources
+ * Sep 29 2003	Rewrite zone computation to deal with +-90 correctly
+ * Oct  1 2003	Use wcs2pix() to decide whether to accept position in ty2bin()
+ * Oct  6 2003	Update ty2read() and ty2bin() for improved RefLim()
+ * Nov 18 2003	Fix bugs in ty2bin()
+ * Dec  1 2003	Add missing tab to n=-1 header
+ *
+ * Apr 30 2004	Allow either LF or CRLF at end of lines in index and catalog
+ *
+ * May 18 2005	Add magnitude errors
+ * Aug  5 2005	Make magnitude errors an option if refcat is TYCHO2E
+ *
+ * Apr  3 2006	Add refcat definition to ty2rnum()
+ * Jun 20 2006	Initialize uninitialized variables
+ * Oct  5 2006	Fix order of magnitudes to Bt-Vt from Vt-Bt
+ * Nov 16 2006	Fix binning
+ *
+ * Jan 10 2007	Add dradi argument to webread() call
+ * Jan 10 2007	Add match=1 argument to webrnum()
+ * Jan 10 2007	Rewrite web access in ty2rnum() to reduce code
+ * Jul  6 2007	Skip stars with no positions (=unfound Guide Stars?)
+ * Jul  6 2007	Skip stars with entry read errors; stop if catalog problem
+ * Jul  6 2007	Print read errors in ty2star() only
+ * Jun  9 2007	Fix bug so that sequential catalog entry reading works
+ */
diff --git a/Code/src/libwcs/uacread.c b/Code/src/libwcs/uacread.c
new file mode 100644
index 0000000000000000000000000000000000000000..b1565f11bca78f5d7ef3acb0caa7da827d218fe6
--- /dev/null
+++ b/Code/src/libwcs/uacread.c
@@ -0,0 +1,1537 @@
+/*** File libwcs/uacread.c
+ *** January 11, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Subroutines to read from the USNO A and SA catalogs
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+static int ucat=UA2;
+
+/* USNO A-2.0 directory pathname; replaced by UA2_PATH environment variable
+ * Use this if CDROMs have been transferred to a single hard disk
+ * Otherwise set to null string ("") and use cdroot
+ * This may also be a URL to a catalog search engine */
+static char ua2path[64]="/data/astrocat/ua2";
+
+/* Uncomment following line to use ESO USNO-A server for UA2
+static char ua2path[64]="http://archive.eso.org/skycat/servers/usnoa-server";
+ */
+
+/* USNO SA-2.0 directory pathname; replaced by USA2_PATH environment variable
+ * This may also be a URL to a catalog search engine */
+static char usa2path[64]="/data/astrocat/usnosa20";
+
+/* USNO SA-1.0 directory pathname; replaced by USA1_PATH environment variable
+ * This may also be a URL to a catalog search engine */
+static char usa1path[64]="/data/astrocat/usnosa10";
+
+/* USNO A-1.0 directory pathname; replaced by UA1_PATH environment variable
+ * Use this if CDROMs have been transferred to a single hard disk
+ * Otherwise set to null string ("") and use cdroot
+ * This may also be a URL to a catalog search engine */
+static char ua1path[64]="/data/astrocat/ua1";
+
+static char *uapath;
+
+/* Root directory for CDROMs; replaced by UA_ROOT environment variable */
+/* Ignored if uapath or UA*_PATH are set */
+static char cdroot[32]="/cdrom";
+
+/* Names of CDROM's for USNO A Catalogs */
+static char cdname[11][8]={"ua001","ua002","ua003","ua004","ua005","ua006",
+			"ua007","ua008","ua009","ua010","ua011"};
+
+/* Disks for 24 zones of USNO A-1.0 Catalog */
+static int zdisk1[24]={1,1,6,5,3,2,1,4,6,5,7,10,8,7,8,9,9,4,10,3,2,6,2,3};
+
+/* Disks for 24 zones of USNO A-2.0 Catalog */
+static int zdisk2[24]={1,1,9,7,5,4,3,2,1,6,7,10,9,8,8,11,10,11,6,4,2,3,3,2};
+
+typedef struct {
+    int rasec, decsec, magetc;
+} UACstar;
+
+static int nstars;	/* Number of stars in catalog */
+static int cswap = 0;	/* Byte reverse catalog to Intel/DEC order if 1 */
+static double *udist;	/* Array of distances to stars */
+static int ndist = 0;
+
+static FILE *fcat;
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+#define NZONES 24
+
+static double uacra();
+static double uacdec();
+static double uacmagr();
+static double uacmagb();
+int uacmagerr();
+int uacgsc();
+static int uacplate();
+
+static int uaczones();
+static int uaczone();
+static int uacsra();
+static int uacopen();
+static int uacpath();
+static int uacstar();
+static void uacswap();
+
+static int xplate = 0;	/* If nonzero, use objects only from this plate */
+void setuplate (xplate0)
+int xplate0;
+{ xplate = xplate0; return; }
+int getuplate ()
+{ return (xplate); }
+
+
+/* USACREAD -- Read USNO SA Catalog stars from CDROM */
+
+int
+usaread (cra,cdec,dra,ddec,drad,dradi,distsort,sysout,eqout,epout,mag1,mag2,
+	 sortmag,nstarmax,unum,ura,udec,umag,uplate,nlog)
+
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	distsort;	/* 1 to sort stars by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+			/* If < 1, print stars to stdout as found */
+double	*unum;		/* Array of UA numbers (returned) */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	**umag;		/* Array of red and blue magnitudes (returned) */
+int	*uplate;	/* Array of plate numbers (returned) */
+int	nlog;		/* Logging interval */
+{
+    int i;
+    int uacread();
+
+    ucat = USA2;
+    i = uacread ("usac",distsort,cra,cdec,dra,ddec,drad,dradi,
+		 sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,
+		 unum,ura,udec,umag,uplate,nlog);
+    ucat = USA2;
+    return (i);
+}
+
+
+/* UACREAD -- Read USNO A or SA Catalog stars from CDROM */
+
+int
+uacread (refcatname,distsort,cra,cdec,dra,ddec,drad,dradi,sysout,eqout,epout,
+	 mag1,mag2,sortmag,nstarmax,unum,ura,udec,umag,uplate,nlog)
+
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+int	distsort;	/* 1 to sort stars by distance from center */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 or 2) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*unum;		/* Array of UA numbers (returned) */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	**umag;		/* Array of red and blue magnitudes (returned) */
+int	*uplate;	/* Array of plate numbers (returned) */
+int	nlog;		/* Logging interval */
+{
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    int nz;		/* Number of input UA zone files */
+    int zlist[NZONES];	/* List of input UA zones */
+    UACstar star;	/* UA catalog entry for one star */
+    double dist = 0.0;	/* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    int	faintstar=0;	/* Faintest star */
+    int	farstar=0;	/* Most distant star */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+
+    double rra1, rra2, rdec1, rdec2;
+    double num;		/* UA numbers */
+    int wrap, iwrap;
+    int verbose;
+    int znum, itot,iz, i;
+    int jtable,jstar;
+    int itable = 0;
+    int nstar, nread;
+    int uara1, uara2, uadec1, uadec2;
+    double ra,dec, rdist, ddist;
+    double mag, magb, magr;
+    int istar, istar1, istar2, plate;
+    int nzmax = NZONES;	/* Maximum number of declination zones */
+/*    int isp;
+    char ispc[2]; */
+    int pass;
+    int magsort;
+    char *str;
+    char cstr[32], rastr[32], numstr[32], decstr[32], catid[32];
+    char *title;
+
+    itot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set catalog code and path to catalog */
+    if (strncmp (refcatname,"us",2)==0 ||
+        strncmp (refcatname,"US",2)==0) {
+	if (strchr (refcatname, '2') != NULL) {
+	    if ((str = getenv("USA2_PATH")) != NULL)
+		strcpy (usa2path,str);
+	    ucat = USA2;
+	    uapath = usa2path;
+	    }
+	else {
+	    if ((str = getenv("USA1_PATH")) != NULL)
+		strcpy (usa1path,str);
+	    ucat = USA1;
+	    uapath = usa1path;
+	    }
+	}
+    else if (strncmp (refcatname,"ua",2)==0 ||
+        strncmp (refcatname,"UA",2)==0) {
+	if (strchr (refcatname, '2') != NULL) {
+	    if ((str = getenv("UA2_PATH")) != NULL)
+		strcpy (ua2path,str);
+	    else if ((str = getenv("UA2_ROOT")) != NULL) {
+		ua2path[0] = 0;
+		strcpy (cdroot,str);
+		}
+	    ucat = UA2;
+	    uapath = ua2path;
+	    }
+	else {
+	    if ((str = getenv("UA1_PATH")) != NULL)
+		strcpy (ua1path,str);
+	    else if ((str = getenv("UA1_ROOT")) != NULL) {
+		ua1path[0] = 0;
+		strcpy (cdroot,str);
+		}
+	    ucat = UA1;
+	    uapath = ua1path;
+	    }
+	}
+    else {
+	fprintf (stderr, "UACREAD:  %s not a USNO catalog\n", refcatname);
+	return (0);
+	}
+
+    /* If root pathname is a URL, search and return */
+    if (!strncmp (uapath, "http:",5)) {
+	return (webread (uapath,refcatname,distsort,cra,cdec,dra,ddec,drad,
+			 dradi,sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,
+			 unum,ura,udec,NULL,NULL,umag,uplate,nlog));
+	}
+
+    wcscstr (cstr, sysout, eqout, epout);
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* mag1 is always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+    if (sortmag == 1)
+	magsort = 0;
+    else
+	magsort = 1;
+
+    /* Find UA Star Catalog regions in which to search */
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra, cdec, dra, ddec, sysout, sysref, eqout, eqref, epout, epref, 0.0,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+    nz = uaczones (rra1, rra2, rdec1, rdec2, nzmax, zlist, verbose);
+    if (nz <= 0) {
+	fprintf (stderr, "UACREAD:  no USNO A zones found\n");
+	return (0);
+	}
+
+    /* Write header if printing star entries as found */
+    if (nstarmax < 1) {
+	char *revmessage;
+	revmessage = getrevmsg();
+	title = CatName (ucat, refcatname);
+	printf ("catalog        %s\n", title);
+	free ((char *)title);
+	ra2str (rastr, 31, cra, 3);
+	printf ("ra     %s\n", rastr);
+	dec2str (decstr, 31, cdec, 2);
+	printf ("dec    %s\n", decstr);
+	if (drad != 0.0) {
+	    printf ("radmin     %.1f\n", drad*60.0);
+	    if (dradi > 0)
+		printf ("radimin	%.1f\n", dradi*60.0);
+	    }
+	else {
+	    printf ("dramin     %.1f\n", dra*60.0* cosdeg (cdec));
+	    printf ("ddecmin    %.1f\n", ddec*60.0);
+	    }
+	printf ("radecsys       %s\n", cstr);
+	printf ("equinox        %.3f\n", eqout);
+	printf ("epoch  %.3f\n", epout);
+	printf ("program        scat %s\n", revmessage);
+	CatID (catid, ucat);
+	printf ("%s	ra          	dec         	", catid);
+	printf ("magb 	magr  	arcmin\n");
+	printf ("-------------	------------	------------	");
+	printf ("-----	-----	------\n");
+	}
+
+    uara1 = (int) (rra1 * 360000.0 + 0.5);
+    uara2 = (int) (rra2 * 360000.0 + 0.5);
+    uadec1 = (int) ((rdec1 * 360000.0) + 32400000.5);
+    uadec2 = (int) ((rdec2 * 360000.0) + 32400000.5);
+    
+    if (nstarmax > ndist) {
+	if (ndist > 0)
+	    free ((void *)udist);
+	udist = (double *) malloc (nstarmax * sizeof (double));
+	if (udist == NULL) {
+	    fprintf (stderr,"UACREAD:  cannot allocate separation array\n");
+	    return (0);
+	    }
+	ndist = nstarmax;
+	}
+
+    /* Loop through region list */
+    nstar = 0;
+    for (iz = 0; iz < nz; iz++) {
+
+    /* Get path to zone catalog */
+	znum = zlist[iz];
+	if ((nstars = uacopen (znum)) != 0) {
+
+	    jstar = 0;
+	    jtable = 0;
+	    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+	    /* Find first star based on RA */
+		if (iwrap == 0 || wrap == 0)
+		    istar1 = uacsra (rra1);
+		else
+		    istar1 = 1;
+
+	    /* Find last star based on RA */
+		if (iwrap == 1 || wrap == 0)
+		    istar2 = uacsra (rra2);
+		else
+		    istar2 = nstars;
+
+		if (istar1 == 0 || istar2 == 0)
+		    break;
+
+		nread = istar2 - istar1 + 1;
+		itable = 0;
+
+	    /* Loop through zone catalog for this region */
+		for (istar = istar1; istar <= istar2; istar++) {
+		    itable ++;
+		    jtable ++;
+
+		    if (uacstar (istar, &star)) {
+			fprintf (stderr,"UACREAD: Cannot read star %d\n", istar);
+			break;
+			}
+
+		/* Extract selected fields */
+		    else {
+
+		    /* Check position limits */
+     			if ((star.decsec >= uadec1 && star.decsec <= uadec2) &&
+			    ((wrap && (star.rasec>=uara1 || star.rasec<=uara2)) ||
+			     (!wrap && (star.rasec>=uara1 && star.rasec<=uara2))
+			    )){
+
+			/* Check magnitude, distance, and plate number */
+			    magb = uacmagb (star.magetc);
+			    magr = uacmagr (star.magetc);
+			    if (magsort == 1)
+				mag = magr;
+			    else
+				mag = magb;
+
+			    /* Check magnitude limits */
+			    pass = 1;
+			    if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+				pass = 0;
+
+			    /* Check plate ID */
+			    plate = uacplate (star.magetc);
+			    if (xplate != 0 && plate != xplate)
+				pass = 0;
+
+			    /* Check position limits */
+			    if (pass) {
+				ra = uacra (star.rasec);
+				dec = uacdec (star.decsec);
+				wcscon (sysref,sysout,eqref,eqout,&ra,&dec,epout);
+
+				/* Compute distance from search center */
+				if (distsort || drad > 0)
+				    dist = wcsdist (cra,cdec,ra,dec);
+				else
+				    dist = 0.0;
+			    
+				/* Check radial distance to search center */
+				if (drad > 0.0) {
+				    if (dist > drad)
+					pass = 0;
+				    if (dradi > 0.0 && dist < dradi)
+					pass = 0;
+				    }
+
+				/* Check distance along RA and Dec axes */
+				else {
+				    ddist = wcsdist (cra,cdec,cra,dec);
+				    if (ddist > ddec)
+					pass = 0;
+				    rdist = wcsdist (cra,dec,ra,dec);
+				    if (rdist > dra)
+					pass = 0;
+				    }
+				}
+
+			    if (pass) {
+				num = (double) znum +
+				      (0.00000001 * (double)istar);
+
+			    /* Write star position and magnitudes to stdout */
+				if (nstarmax < 1) {
+				    CatNum (ucat, -13, 0, num, numstr);
+				    ra2str (rastr, 31, ra, 3);
+				    dec2str (decstr, 31, dec, 2);
+				    dist = wcsdist (cra,cdec,ra,dec) * 60.0;
+				    printf ("%s	%s	%s", numstr,rastr,decstr);
+				    printf ("	%.2f	%.2f	%.2f\n",
+					magb, magr, dist / 60.0);
+				    }
+
+			    /* Save star position and magnitude in table */
+				else if (nstar < nstarmax) {
+				    unum[nstar] = num;
+				    ura[nstar] = ra;
+				    udec[nstar] = dec;
+				    umag[0][nstar] = magb;
+				    umag[1][nstar] = magr;
+				    uplate[nstar] = plate;
+				    udist[nstar] = dist;
+				    if (dist > maxdist) {
+					maxdist = dist;
+					farstar = nstar;
+					}
+				    if (mag > faintmag) {
+					faintmag = mag;
+					faintstar = nstar;
+					}
+				    }
+
+			    /* If too many stars and distance sorting,
+				replace furthest star */
+				else if (distsort) {
+				    if (dist < maxdist) {
+					unum[farstar] = num;
+					ura[farstar] = ra;
+					udec[farstar] = dec;
+					umag[0][farstar] = magb;
+					umag[1][farstar] = magr;
+					uplate[farstar] = plate;
+					udist[farstar] = dist;
+
+				    /* Find new farthest star */
+					maxdist = 0.0;
+					for (i = 0; i < nstarmax; i++) {
+					    if (udist[i] > maxdist) {
+						maxdist = udist[i];
+						farstar = i;
+						}
+					    }
+					}
+				    }
+
+			    /* If too many stars, replace faintest star */
+				else if (mag < faintmag) {
+				    unum[faintstar] = num;
+				    ura[faintstar] = ra;
+				    udec[faintstar] = dec;
+				    umag[0][faintstar] = magb;
+				    umag[1][faintstar] = magr;
+				    uplate[faintstar] = plate;
+				    udist[faintstar] = dist;
+
+			    /* Find new faintest star */
+				    faintmag = 0.0;
+				    for (i = 0; i < nstarmax; i++) {
+					if (umag[magsort][i] > faintmag) {
+					    faintmag = umag[magsort][i];
+					    faintstar = i;
+					    }
+					}
+				    }
+				nstar++;
+				jstar++;
+				if (nlog == 1)
+				    fprintf (stderr,"UACREAD: %04d.%08d: %9.5f %9.5f %s %5.2f %5.2f\n",
+					znum,istar,ra,dec,cstr,magb,magr);
+
+			    /* End of accepted star processing */
+				}
+			    }
+
+		    /* End of individual star processing */
+			}
+
+		/* Log operation */
+		    if (nlog > 0 && itable%nlog == 0)
+			fprintf (stderr,"UACREAD: zone %d (%2d / %2d) %8d / %8d / %8d sources\r",
+				znum, iz+1, nz, jstar, itable, nread);
+
+		/* End of star loop */
+		    }
+
+		/* End of wrap loop */
+		}
+
+	/* Close zone input file */
+	    (void) fclose (fcat);
+	    itot = itot + itable;
+	    if (nlog > 0)
+		fprintf (stderr,"UACREAD: zone %d (%2d / %2d) %8d / %8d / %8d sources      \n",
+			znum, iz+1, nz, jstar, jtable, nstars);
+
+	/* End of zone processing */
+	    }
+
+    /* End of zone loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0) {
+	if (nz > 1)
+	    fprintf (stderr,"UACREAD: %d zones: %d / %d found\n",nz,nstar,itot);
+	else
+	    fprintf (stderr,"UACREAD: 1 zone: %d / %d found\n",nstar,itable);
+	if (nstar > nstarmax)
+	    fprintf (stderr,"UACREAD: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+    return (nstar);
+}
+
+
+int
+usarnum (nnum,sysout,eqout,epout,unum,ura,udec,umag,uplate,nlog)
+
+int	nnum;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*unum;		/* Array of UA numbers to find */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	**umag;		/* Array of blue and red magnitudes (returned) */
+int	*uplate;	/* Array of plate numbers (returned) */
+int	nlog;		/* Logging interval */
+{
+    int i;
+    int uacrnum();
+
+    ucat = USA1;
+    i = uacrnum ("USAC",nnum,sysout,eqout,epout,unum,ura,udec,umag,uplate,nlog);
+    ucat = UA1;
+    return (i);
+}
+
+
+int
+uacrnum (refcatname,nnum,sysout,eqout,epout,unum,ura,udec,umag,uplate,nlog)
+
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+int	nnum;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*unum;		/* Array of UA numbers to find */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	**umag;		/* Array of blue and red magnitudes (returned) */
+int	*uplate;	/* Array of plate numbers (returned) */
+int	nlog;		/* Logging interval */
+{
+    UACstar star;	/* UA catalog entry for one star */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+/*    int isp;
+    char ispc[2]; */
+
+    int znum;
+    int jnum;
+    int nzone;
+    int nfound = 0;
+    double ra,dec;
+    double magr, magb;
+    double dstar;
+    int istar, plate;
+    char *str;
+
+    /* Set catalog code and path to catalog */
+    if (strncmp (refcatname,"us",2)==0 ||
+        strncmp (refcatname,"US",2)==0) {
+	if (strchr (refcatname, '2') != NULL) {
+	    if ((str = getenv("USA2_PATH")) != NULL)
+		strcpy (usa2path,str);
+	    ucat = USA2;
+	    uapath = usa2path;
+	    }
+	else {
+	    if ((str = getenv("USA1_PATH")) != NULL)
+		strcpy (usa1path,str);
+	    ucat = USA1;
+	    uapath = usa1path;
+	    }
+	}
+    else if (strncmp (refcatname,"ua",2)==0 ||
+        strncmp (refcatname,"UA",2)==0) {
+	if (strchr (refcatname, '2') != NULL) {
+	    if ((str = getenv("UA2_PATH")) != NULL)
+		strcpy (ua2path,str);
+	    else if ((str = getenv("UA2_ROOT")) != NULL) {
+		ua2path[0] = 0;
+		strcpy (cdroot,str);
+		}
+	    ucat = UA2;
+	    uapath = ua2path;
+	    }
+	else {
+	    if ((str = getenv("UA1_PATH")) != NULL)
+		strcpy (ua1path,str);
+	    else if ((str = getenv("UA1_ROOT")) != NULL) {
+		ua1path[0] = 0;
+		strcpy (cdroot,str);
+		}
+	    ucat = UA1;
+	    uapath = ua1path;
+	    }
+	}
+    else {
+	fprintf (stderr, "UACREAD:  %s not a USNO catalog\n", refcatname);
+	return (0);
+	}
+
+    /* If root pathname is a URL, search and return */
+    if (!strncmp (uapath, "http:",5)) {
+	return (webrnum (uapath,refcatname,nnum,sysout,eqout,epout,1,
+			 unum,ura,udec,NULL,NULL,umag,uplate,nlog));
+	}
+
+
+/* Loop through star list */
+    for (jnum = 0; jnum < nnum; jnum++) {
+
+    /* Get path to zone catalog */
+	znum = (int) unum[jnum];
+	if ((nzone = uacopen (znum)) != 0) {
+	    dstar = (unum[jnum] - znum) * 100000000.0;
+	    istar = (int) (dstar + 0.5);
+	    if (istar > nzone) {
+		fprintf (stderr,"UACRNUM: Star %d > max. in zone %d\n",
+			 istar,nzone);
+		break;
+		}
+
+	    if (uacstar (istar, &star)) {
+		fprintf (stderr,"UACRNUM: Cannot read star %d\n", istar);
+		break;
+		}
+
+	    /* Extract selected fields */
+	    else {
+		ra = uacra (star.rasec); /* Right ascension in degrees */
+		dec = uacdec (star.decsec); /* Declination in degrees */
+		magb = uacmagb (star.magetc); /* Blue magnitude */
+		magr = uacmagr (star.magetc); /* Red magnitude */
+		plate = uacplate (star.magetc);	/* Plate number */
+		wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+
+		/* Save star position and magnitude in table */
+		ura[nfound] = ra;
+		udec[nfound] = dec;
+		umag[0][nfound] = magb;
+		umag[1][nfound] = magr;
+		/* br2sp (NULL, magb, magr, ispc);
+		isp = (1000 * (int)ispc[0]) + (int)ispc[1]; */
+		uplate[nfound] = plate;
+
+		nfound++;
+		if (nlog == 1)
+		    fprintf (stderr,"UACRNUM: %04d.%08d: %9.5f %9.5f %5.2f %5.2f\n",
+			     znum,istar,ra,dec,magb,magr);
+
+		/* Log operation */
+		if (nlog > 0 && jnum%nlog == 0)
+		    fprintf (stderr,"UACRNUM: %4d.%8d  %8d / %8d sources\r",
+			     znum, istar, jnum, nnum);
+
+		(void) fclose (fcat);
+		/* End of star processing */
+		}
+
+	    /* End of star */
+	    }
+
+	/* End of star loop */
+	}
+
+    /* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"UACRNUM:  %d / %d found\n",nfound,nnum);
+
+    return (nfound);
+}
+
+
+/* USACBIN -- Fill a FITS WCS image with USNO SA Catalog stars */
+
+int
+usabin (wcs, header, image, mag1, mag2, sortmag, magscale, nlog)
+
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;		/* Logging interval */
+{
+    int i;
+    int uacbin();
+
+    ucat = USA2;
+    i = uacbin ("usac", wcs, header, image, mag1, mag2, sortmag, magscale, nlog);
+    ucat = USA2;
+    return (i);
+}
+
+
+/* UACBIN -- Fill a FITS WCS image with USNO A or SA Catalog stars */
+
+int
+uacbin (refcatname, wcs, header, image, mag1, mag2, sortmag, magscale, nlog)
+
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 or 2) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;		/* Logging interval */
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    int nz;		/* Number of input UA zone files */
+    int zlist[NZONES];	/* List of input UA zones */
+    UACstar star;	/* UA catalog entry for one star */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+
+    double rra1, rra2, rdec1, rdec2;
+    int wrap, iwrap;
+    int verbose;
+    int znum, itot,iz;
+    int jtable,jstar;
+    int itable = 0;
+    int nstar, nread;
+    int uara1, uara2, uadec1, uadec2;
+    double ra,dec, rdist, ddist;
+    double mag, magb, magr;
+    int istar, istar1, istar2, plate;
+    int nzmax = NZONES;	/* Maximum number of declination zones */
+/*    int isp;
+    char ispc[2]; */
+    int pass;
+    int ix, iy;
+    int magsort;
+    char *str;
+    char cstr[32];
+    double xpix, ypix, flux;
+    int offscl;
+    int bitpix, w, h;   /* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+
+    itot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    /* Set catalog code and path to catalog */
+    if (strncmp (refcatname,"us",2)==0 ||
+        strncmp (refcatname,"US",2)==0) {
+	if (strchr (refcatname, '2') != NULL) {
+	    if ((str = getenv("USA2_PATH")) != NULL)
+		strcpy (usa2path,str);
+	    ucat = USA2;
+	    uapath = usa2path;
+	    }
+	else {
+	    if ((str = getenv("USA1_PATH")) != NULL)
+		strcpy (usa1path,str);
+	    ucat = USA1;
+	    uapath = usa1path;
+	    }
+	}
+    else if (strncmp (refcatname,"ua",2)==0 ||
+        strncmp (refcatname,"UA",2)==0) {
+	if (strchr (refcatname, '2') != NULL) {
+	    if ((str = getenv("UA2_PATH")) != NULL)
+		strcpy (ua2path,str);
+	    else if ((str = getenv("UA2_ROOT")) != NULL) {
+		ua2path[0] = 0;
+		strcpy (cdroot,str);
+		}
+	    ucat = UA2;
+	    uapath = ua2path;
+	    }
+	else {
+	    if ((str = getenv("UA1_PATH")) != NULL)
+		strcpy (ua1path,str);
+	    else if ((str = getenv("UA1_ROOT")) != NULL) {
+		ua1path[0] = 0;
+		strcpy (cdroot,str);
+		}
+	    ucat = UA1;
+	    uapath = ua1path;
+	    }
+	}
+    else {
+	fprintf (stderr, "UACBIN:  %s not a USNO catalog\n", refcatname);
+	return (0);
+	}
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* mag1 is always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+    if (sortmag == 1)
+	magsort = 0;
+    else
+	magsort = 1;
+
+    /* Find UA Star Catalog regions in which to search */
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra, cdec, dra, ddec, sysout, sysref, eqout, eqref, epout, epref, 0.0,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+    nz = uaczones (rra1, rra2, rdec1, rdec2, nzmax, zlist, verbose);
+    if (nz <= 0) {
+	fprintf (stderr, "UACBIN:  no USNO A zones found\n");
+	return (0);
+	}
+
+    uara1 = (int) (rra1 * 360000.0 + 0.5);
+    uara2 = (int) (rra2 * 360000.0 + 0.5);
+    uadec1 = (int) ((rdec1 * 360000.0) + 32400000.5);
+    uadec2 = (int) ((rdec2 * 360000.0) + 32400000.5);
+    
+    /* Loop through region list */
+    nstar = 0;
+    for (iz = 0; iz < nz; iz++) {
+
+    /* Get path to zone catalog */
+	znum = zlist[iz];
+	if ((nstars = uacopen (znum)) != 0) {
+
+	    jstar = 0;
+	    jtable = 0;
+	    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+	    /* Find first star based on RA */
+		if (iwrap == 0 || wrap == 0)
+		    istar1 = uacsra (rra1);
+		else
+		    istar1 = 1;
+
+	    /* Find last star based on RA */
+		if (iwrap == 1 || wrap == 0)
+		    istar2 = uacsra (rra2);
+		else
+		    istar2 = nstars;
+
+		if (istar1 == 0 || istar2 == 0)
+		    break;
+
+		nread = istar2 - istar1 + 1;
+		itable = 0;
+
+	    /* Loop through zone catalog for this region */
+		for (istar = istar1; istar <= istar2; istar++) {
+		    itable ++;
+		    jtable ++;
+
+		    if (uacstar (istar, &star)) {
+			fprintf (stderr,"UACBIN: Cannot read star %d\n", istar);
+			break;
+			}
+
+		/* Extract selected fields */
+		    else {
+
+		    /* Check position limits */
+     			if ((star.decsec >= uadec1 && star.decsec <= uadec2) &&
+			    ((wrap && (star.rasec>=uara1 || star.rasec<=uara2)) ||
+			     (!wrap && (star.rasec>=uara1 && star.rasec<=uara2))
+			    )){
+
+			/* Check magnitude, distance, and plate number */
+			    magb = uacmagb (star.magetc);
+			    magr = uacmagr (star.magetc);
+			    if (magsort == 1)
+				mag = magr;
+			    else
+				mag = magb;
+
+			    /* Check magnitude limits */
+			    pass = 1;
+			    if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+				pass = 0;
+
+			    /* Check plate ID */
+			    plate = uacplate (star.magetc);
+			    if (xplate != 0 && plate != xplate)
+				pass = 0;
+
+			    /* Check position limits */
+			    if (pass) {
+				ra = uacra (star.rasec);
+				dec = uacdec (star.decsec);
+				wcscon (sysref,sysout,eqref,eqout,&ra,&dec,epout);
+
+				/* Check distance along RA and Dec axes */
+				ddist = wcsdist (cra,cdec,cra,dec);
+				if (ddist > ddec)
+				    pass = 0;
+				rdist = wcsdist (cra,dec,ra,dec);
+				if (rdist > dra)
+				    pass = 0;
+				}
+
+			    /* Save star in FITS image */
+			    if (pass) {
+				wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+				if (!offscl) {
+				    if (magscale > 0.0)
+					flux = magscale * exp (logt * (-mag / 2.5));
+				    else
+					flux = 1.0;
+				    ix = (int) (xpix + 0.5);
+				    iy = (int) (ypix + 0.5);
+				    addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+				    nstar++;
+				    jstar++;
+				    }
+				else {
+				    ix = 0;
+				    iy = 0;
+				    }
+				if (nlog == 1) {
+				    fprintf (stderr,"UACBIN: %04d.%08d: %9.5f %9.5f %s",
+					     znum,istar,ra,dec,cstr);
+				    if (magscale > 0.0)
+					fprintf (stderr, " %5.2f", mag);
+				    if (!offscl)
+					flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+				    else
+					flux = 0.0;
+				    fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+				    }
+
+			    /* End of accepted star processing */
+				}
+			    }
+
+		    /* End of individual star processing */
+			}
+
+		/* Log operation */
+		    if (nlog > 0 && itable%nlog == 0)
+			fprintf (stderr,"UACBIN: zone %d (%2d / %2d) %8d / %8d / %8d sources\r",
+				znum, iz+1, nz, jstar, itable, nread);
+
+		/* End of star loop */
+		    }
+
+		/* End of wrap loop */
+		}
+
+	/* Close zone input file */
+	    (void) fclose (fcat);
+	    itot = itot + itable;
+	    if (nlog > 0)
+		fprintf (stderr,"UACBIN: zone %d (%2d / %2d) %8d / %8d / %8d sources      \n",
+			znum, iz+1, nz, jstar, jtable, nstars);
+
+	/* End of zone processing */
+	    }
+
+    /* End of zone loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0) {
+	if (nz > 1)
+	    fprintf (stderr,"UACBIN: %d zones: %d / %d found\n",nz,nstar,itot);
+	else
+	    fprintf (stderr,"UACBIN: 1 zone: %d / %d found\n",nstar,itable);
+	}
+    return (nstar);
+}
+
+
+/* Declination zone numbers */
+int azone[NZONES]={0,75,150,225,300,375,450,525,600,675,750,825,900,
+	      975,1050,1125,1200,1275,1350,1425,1500,1575,1650,1725};
+
+/* UACZONES -- figure out which UA zones will need to be searched */
+
+static int
+uaczones (ra1, ra2, dec1, dec2, nzmax, zones, verbose)
+
+double	ra1, ra2;	/* Right ascension limits in degrees */
+double	dec1, dec2; 	/* Declination limits in degrees */
+int	nzmax;		/* Maximum number of zones to find */
+int	*zones;		/* Region numbers (returned)*/
+int	verbose;	/* 1 for diagnostics */
+
+{
+    int nrgn;		/* Number of zones found (returned) */
+    int iz,iz1,iz2,i;
+
+    for (i = 0; i < nzmax; i++)
+	zones[i] = 0;
+
+    nrgn = 0;
+
+/* Find zone range to search based on declination */
+    iz1 = uaczone (dec1);
+    iz2 = uaczone (dec2);
+
+/* Tabulate zones to search */
+    i = 0;
+    if (iz2 >= iz1) {
+	for (iz = iz1; iz <= iz2; iz++)
+	    zones[i++] = azone[iz];
+	}
+    else {
+	for (iz = iz2; iz <= iz1; iz++)
+	    zones[i++] = azone[iz];
+	}
+
+    nrgn = i;
+    if (verbose) {
+	fprintf(stderr,"UACZONES:  %d zones: %d - %d\n",nrgn,zones[0],zones[i-1]);
+	fprintf(stderr,"UACZONES: RA: %.5f - %.5f, Dec: %.5f - %.5f\n",ra1,ra2,dec1,dec2);
+	}
+
+    return (nrgn);
+}
+
+
+/* UACRA -- returns right ascension in degrees from the UA star structure */
+
+static double
+uacra (rasec)
+
+int rasec;	/* RA in 100ths of arcseconds from UA catalog entry */
+{
+    return ((double) (rasec) / 360000.0);
+}
+
+
+/* UACDEC -- returns the declination in degrees from the UA star structure */
+
+static double
+uacdec (decsec)
+
+int decsec;	/* Declination in 100ths of arcseconds from UA catalog entry */
+{
+    return ((double) (decsec - 32400000) / 360000.0);
+}
+
+
+/* UACMAGR -- returns the red magnitude from the UA star structure */
+
+static double
+uacmagr (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UA catalog entry */
+{
+    if (magetc < 0)
+	return ((double) (-magetc % 1000) * 0.1);
+    else
+	return ((double) (magetc % 1000) * 0.1);
+}
+
+
+/* UACMAGB -- returns the blue magnitude from the UA star structure */
+
+static double
+uacmagb (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UA catalog entry */
+{
+    if (magetc < 0)
+	return ((double) ((-magetc / 1000) % 1000) * 0.1);
+    else
+	return ((double) ((magetc / 1000) % 1000) * 0.1);
+}
+
+
+/* UACMAGERR -- returns 1 if magnitude is uncertain from UA star structure */
+
+int
+uacmagerr (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UA catalog entry */
+{
+    if (magetc < 0)
+	return ((-magetc / 1000000000) % 10);
+    else
+	return ((magetc / 1000000000) % 10);
+}
+
+
+/* UACGSC -- returns 1 if UA star is in the HST Guide Star Catalog */
+
+int
+uacgsc (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UA catalog entry */
+{
+    if (magetc < 0)
+	return (1);
+    else
+	return (0);
+}
+
+
+/* UACPLATE -- returns the plate number from the UA star structure */
+
+static int
+uacplate (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UA catalog entry */
+{
+    if (magetc < 0)
+	return ((-magetc / 1000000) % 1000);
+    else
+	return ((magetc / 1000000) % 1000);
+}
+
+
+/* UACZONE -- find the UA zone number where a declination can be found */
+
+static int
+uaczone (dec)
+
+double dec;	/* declination in degrees */
+{
+    double zonesize = 7.5;	/* number of degrees per declination zone */
+    int zone;
+
+    zone = (int) ((dec + 90.0) / zonesize);
+    if (zone > 23)
+	zone = 23;
+    else if (zone < 0)
+	zone = 0;
+    return (zone);
+}
+
+
+/* UACSRA -- Find UA star closest to specified right ascension */
+
+static int
+uacsra (rax0)
+
+double	rax0;		/* Right ascension in degrees for which to search */
+{
+    int istar, istar1, istar2, nrep;
+    double rax, ra1, ra, rdiff, rdiff1, rdiff2, sdiff;
+    UACstar star;	/* UA catalog entry for one star */
+    char rastrx[32];
+    int debug = 0;
+
+    rax = rax0;
+    if (debug)
+	ra2str (rastrx, 31, rax, 3);
+    istar1 = 1;
+    if (uacstar (istar1, &star))
+	return (0);
+    ra1 = uacra (star.rasec);
+    istar = nstars;
+    nrep = 0;
+    while (istar != istar1 && nrep < 20) {
+	if (uacstar (istar, &star))
+	    break;
+	else {
+	    ra = uacra (star.rasec);
+	    if (ra == ra1)
+		break;
+	    if (debug) {
+		char rastr[32];
+		ra2str (rastr, 31, ra, 3);
+		fprintf (stderr,"UACSRA %d %d: %s (%s)\n",
+			 nrep,istar,rastr,rastrx);
+		}
+	    rdiff = ra1 - ra;
+	    rdiff1 = ra1 - rax;
+	    rdiff2 = ra - rax;
+	    if (nrep > 20 && ABS(rdiff2) > ABS(rdiff1)) {
+		istar = istar1;
+		break;
+		}
+	    nrep++;
+	    sdiff = (double)(istar - istar1) * rdiff1 / rdiff;
+	    istar2 = istar1 + (int) (sdiff + 0.5);
+	    ra1 = ra;
+	    istar1 = istar;
+	    istar = istar2;
+	    if (debug) {
+		fprintf (stderr," ra1=    %.5f ra=     %.5f rax=    %.5f\n",
+			 ra1,ra,rax);
+		fprintf (stderr," rdiff=  %.5f rdiff1= %.5f rdiff2= %.5f\n",
+			 rdiff,rdiff1,rdiff2);
+		fprintf (stderr," istar1= %d istar= %d istar1= %d\n",
+			 istar1,istar,istar2);
+		}
+	    if (istar < 1)
+		istar = 1;
+	    if (istar > nstars)
+		istar = nstars;
+	    if (istar == istar1)
+		break;
+	    }
+	}
+    return (istar);
+}
+
+/* UACOPEN -- Open UA Catalog zone catalog, returning number of entries */
+
+static int
+uacopen (znum)
+
+int znum;	/* UA Catalog zone */
+{
+    char zonepath[64];	/* Pathname for input UA zone file */
+    UACstar star;	/* UA catalog entry for one star */
+    int lfile;
+    
+/* Get path to zone catalog */
+    if (uacpath (znum, zonepath)) {
+	fprintf (stderr, "UACOPEN: Cannot find zone catalog for %d\n", znum);
+	return (0);
+	}
+
+/* Find number of stars in zone catalog by its length */
+    lfile = getfilesize (zonepath);
+    if (lfile < 2) {
+	fprintf (stderr,"UA zone catalog %s has no entries\n",zonepath);
+	return (0);
+	}
+    else
+	nstars = lfile / 12;
+
+/* Open zone catalog */
+    if (!(fcat = fopen (zonepath, "rb"))) {
+	fprintf (stderr,"UA zone catalog %s cannot be read\n",zonepath);
+	return (0);
+	}
+
+/* Check to see if byte-swapping is necessary */
+    cswap = 0;
+    if (uacstar (1, &star)) {
+	fprintf (stderr,"UACOPEN: cannot read star 1 from UA zone catalog %s\n",
+		 zonepath);
+	return (0);
+	}
+    else {
+	if (star.rasec > 360 * 360000 || star.rasec < 0) {
+	    cswap = 1;
+	    /* fprintf (stderr,"UACOPEN: swapping bytes in UA zone catalog %s\n",
+		     zonepath); */
+	    }
+	else if (star.decsec > 180 * 360000 || star.decsec < 0) {
+	    cswap = 1;
+	    /* fprintf (stderr,"UACOPEN: swapping bytes in UA zone catalog %s\n",
+		     zonepath); */
+	    }
+	else
+	    cswap = 0;
+	}
+
+    return (nstars);
+}
+
+
+/* UACPATH -- Get UA Catalog region file pathname */
+
+static int
+uacpath (zn, path)
+
+int zn;		/* UA zone number */
+char *path;	/* Pathname of UA zone file */
+
+{
+    int iz;		/* Zone index (0000 = 0, 0075 = 1, ...) */
+    int icd;		/* CDROM number if multiple CDROMs used */
+
+    /* Return error code and null path if zone is out of range */
+    if (zn < 0 || zn > 1725) {
+	fprintf (stderr, "UACPATH: zone %d out of range 0-1725\n",zn);
+	path[0] = 0;
+	return (-1);
+	}
+
+    /* Set path for USNO SA zone catalog */
+    if (ucat == USA1 || ucat == USA2)
+	sprintf (path,"%s/zone%04d.cat", uapath, zn);
+
+    /* Set zone catalog path when USNO A is in a single directory */
+    else if (strlen (uapath) > 0)
+	sprintf (path,"%s/zone%04d.cat", uapath, zn);
+
+    /* Set zone catalog path when USNO A is read from CDROMs */
+    else {
+	iz = zn / 75;
+	if (ucat == UA1)
+	    icd = zdisk1[iz];
+	else
+	    icd = zdisk2[iz];
+	sprintf (path,"%s/%s/zone%04d.cat", cdroot, cdname[icd-1], zn);
+	}
+
+    return (0);
+}
+
+
+/* UACSTAR -- Get UA catalog entry for one star; return 0 if successful */
+
+static int
+uacstar (istar, star)
+
+int istar;	/* Star sequence number in UA zone catalog */
+UACstar *star;	/* UA catalog entry for one star */
+{
+    int nbs, nbr, nbskip;
+
+    if (istar < 1 || istar > nstars) {
+	fprintf (stderr, "UACstar %d is not in catalog\n",istar);
+	return (-1);
+	}
+    nbskip = 12 * (istar - 1);
+    if (fseek (fcat,nbskip,SEEK_SET))
+	return (-1);
+    nbs = sizeof (UACstar);
+    nbr = fread (star, nbs, 1, fcat) * nbs;
+    if (nbr < nbs) {
+	fprintf (stderr, "UACstar %d / %d bytes read\n",nbr, nbs);
+	return (-2);
+	}
+    if (cswap)
+	uacswap ((char *)star);
+    return (0);
+}
+
+
+/* UACSWAP -- Reverse bytes of UA Catalog entry */
+
+static void
+uacswap (string)
+
+char *string;	/* Start of vector of 4-byte ints */
+
+{
+char *sbyte, *slast;
+char temp0, temp1, temp2, temp3;
+int nbytes = 12; /* Number of bytes to reverse */
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp3 = sbyte[0];
+	temp2 = sbyte[1];
+	temp1 = sbyte[2];
+	temp0 = sbyte[3];
+	sbyte[0] = temp0;
+	sbyte[1] = temp1;
+	sbyte[2] = temp2;
+	sbyte[3] = temp3;
+	sbyte = sbyte + 4;
+	}
+    return;
+}
+
+/* Nov 15 1996	New subroutine
+ * Dec 11 1996	Set ra<0 to ra+360 and ra>360 to ra-360
+ * Dec 16 1996	Add code to read a specific star
+ * Dec 17 1996	Keep closest stars, not brightest, if searching within radius
+ *
+ * Mar 12 1997	Set paths for SA-1.0 and multiple CDROM A-1.0
+ * Mar 20 1997	Clean up UACRNUM after lint
+ * Apr 23 1997	Fix bug which rejected stars in HST Guide Star Catalog
+ * Nov  6 1997	Don't print star overrun unless logging
+ *
+ * Feb 20 1998	Speed up processing by searching in arcseconds, not degrees
+ * Feb 20 1998	Speed up processing by searching RA and Dec, then rest
+ * Apr 20 1998	Fix bug so stars within radius can be found
+ * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
+ * Jun 24 1998	Initialize byte-swapping flag in UACOPEN()
+ * Sep 15 1998	Fix bug setting A 1.0 region path on CDROM found by Naoki Yasuda
+ * Sep 22 1998	Convert coordinates in these subroutines
+ * Oct  8 1998	Fix bug in uacread call in usaread() and uacrnum call in usarnum()
+ * Oct 26 1998	Fix bug in search algorith for non-J2000 searches
+ * Oct 29 1998	Correctly assign numbers when too many stars are found
+ * Nov 20 1998	Add support for USNO A-2.0 and SA-2.0 catalogs
+ * Nov 24 1998	Fix bug reading SA-2.0 catalog
+ *
+ * Feb  9 1999	Improve documentation
+ * Jun 16 1999	Use SearchLim()
+ * Aug 16 1999	Add RefLim() to get converted search coordinates right
+ * Aug 16 1999  Fix bug to fix failure to search across 0:00 RA
+ * Aug 25 1999  Return real number of stars from uacread()
+ * Sep 10 1999	Set plate selection with subroutine, not argument
+ * Sep 16 1999	Fix bug which didn't always return closest stars
+ * Sep 16 1999	Add distsort argument so brightest stars in circle works, too
+ * Oct 20 1999	Include wcscat.h
+ * Oct 21 1999	Clean up code after lint
+ *
+ * Jun  9 2000	Fix bug detecting swapped files on Alphas and PCs if RA=0
+ * Jun 26 2000	Add coordinate system to SearchLim() arguments
+ * Sep 22 2000	Return approximate spectral type instead of plate number
+ * Nov 28 2000	Add option to read catalog using HTTP
+ * Dec  1 2000	Return plate number, not bad spectral type
+ * Dec 11 2000	Path may be set to catalog search engine URL
+ * Dec 15 2000	Do away with separate usapath variable; use uapath
+ *
+ * Jun 14 2001	Make sure star number is correct in uacrnum()
+ * Jun 27 2001	Use RefCat codes for ucat
+ * Jun 27 2001	Print stars as found in uacread() if nstarmax < 1
+ * Jun 27 2001	Allocate udist only when larger array is needed
+ * Sep 11 2001	Change to single magnitude argeument
+ * Sep 11 2001	Add sort magnitude argument to uacread()
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ * Sep 21 2001	Add commented-out URL of ESO web catalog server
+ * Nov 20 2001	Change cos(degrad()) to cosdeg()
+ *
+ * Apr 10 2002	Simplify use of magsort
+ * Jul 31 2002	Drop extra magb argument in uacrnum()
+ * Oct  2 2002	Print current scat revision message
+ *
+ * Jan 21 2003	Print arcminute radial distance, not arcsecond for instant out
+ * Feb  4 2003	Open catalog file rb instead of r (Martin Ploner, Bern)
+ * Mar 10 2003	Improve test for position
+ * Apr  3 2003	Drop unused variables after lint
+ * Apr 14 2003	Explicitly get revision date if nstarmax < 1
+ * May 27 2003	Use getfilesize() to get file size
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Sep 25 2003	Add usabin() and uacbin() to fill an image with sources
+ * Oct  6 2003	Update uacread() and uacbin() for improved RefLim()
+ * Nov 18 2003	Initialize image size and bits/pixel from header in uacbin()
+ * Dec  1 2003	Add missing tab to n=-1 header
+ * Dec 12 2003	Fix bug in wcs2pix() call in uacbin(); fix usacbin() subroutine
+ *
+ * Aug 30 2004	Include fitsfile.h and math.h
+ *
+ * Sep 13 2006	Change default paths to /data/astrocat
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ * Nov 15 2006	Fix binning
+ *
+ * Jan 10 2007	Add match=1 argument to webrnum()
+ * Jan 10 2007	Add dradi arguemnt to uacread() call in usacread()
+ */
diff --git a/Code/src/libwcs/ubcread.c b/Code/src/libwcs/ubcread.c
new file mode 100644
index 0000000000000000000000000000000000000000..5fca19e5ec740325b8460798a7874a594c532f69
--- /dev/null
+++ b/Code/src/libwcs/ubcread.c
@@ -0,0 +1,1525 @@
+/*** File libwcs/ubcread.c
+ *** December 05, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2003-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Subroutines to read from the USNO-B1.0 catalog
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+static int ucat=UB1;
+
+/* USNO B-1.0 directory pathname; replaced by UB1_PATH environment variable.
+ * This may also be a URL to a catalog search engine */
+static char ub1path[64]="/data/ub1";
+
+/* USNO YB6 directory pathname; replaced by YB6_PATH environment variable.
+ * This may also be a URL to a catalog search engine */
+static char yb6path[64]="/data/astrocat2/usnoyb6";
+
+static char *upath;
+
+typedef struct {
+    int rasec, decsec, pm, pmerr, poserr, mag[5], magerr[5], index[5];
+} UBCstar;
+
+static int nstars;	/* Number of stars in catalog */
+static int cswap = 0;	/* Byte reverse catalog to Intel/DEC order if 1 */
+static double *udist;	/* Array of distances to stars */
+static int ndist = 0;	/* Number of stars in distance array */
+static int minpmqual = 3; /* Proper motion quality limit (0=bad, 9=good)*/
+void setminpmqual (n)
+int n; { minpmqual = n; return; }
+int getminpmqual ()
+{ return (minpmqual); }
+
+static int minid = 0; /* Minimum number of plate ID's (<0 excludes Tycho-2) */
+void setminid (n)
+int n; { minid = n; return; }
+int getminid ()
+{ return (minid); }
+
+
+static FILE *fcat;
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+#define NZONES 1800
+
+static double ubcra();
+static double ubcdec();
+static double ubcmag();
+static double ubcpra();
+static double ubcpdec();
+static int ubcpmq();
+static int ubcsg();
+int ubcmagerr();
+static int ubcndet();
+
+static int ubczones();
+static int ubczone();
+static int ubcsra();
+static int ubcopen();
+static int ubcpath();
+static int ubcstar();
+static void ubcswap();
+static int nbent = 80;
+
+
+/* UBCREAD -- Return USNO B1.0 or YB6 sources in specified region */
+
+int
+ubcread (refcatname,distsort,cra,cdec,dra,ddec,drad,dradi,sysout,eqout,epout,
+	 mag1,mag2,sortmag,nstarmax,unum,ura,udec,upra,updec,umag,upmni,nlog)
+
+char	*refcatname;	/* Name of catalog (UB1 only, for now) */
+int	distsort;	/* 1 to sort stars by distance from center */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of search annulus in degrees (ignore if 0) */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 or 2) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*unum;		/* Array of UB numbers (returned) */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	*upra;		/* Array of right ascension proper motions (returned) */
+double	*updec;		/* Array of declination proper motions (returned) */
+double	**umag;		/* Array of red and blue magnitudes (returned) */
+int	*upmni;		/* Array of number of ids and pm quality (returned) */
+int	nlog;		/* Logging interval */
+{
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    int nz;		/* Number of input UB zone files */
+    int zlist[NZONES];	/* List of input UB zones */
+    UBCstar star;	/* UB catalog entry for one star */
+    double dist = 0.0;	/* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    double rdist, ddist;
+    int	faintstar=0;	/* Faintest star */
+    int	farstar=0;	/* Most distant star */
+    int pmqual;
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+
+    double rra1, rra2, rdec1, rdec2;
+    double num;		/* UB numbers */
+    double rapm, decpm, rapm0, decpm0;
+    int wrap, iwrap;
+    int verbose;
+    int znum, itot,iz, i;
+    int jtable,jstar;
+    int itable = 0;
+    int objtype = 0;
+    int nstar, nread, pass;
+    int ubra1, ubra2, ubdec1, ubdec2;
+    int nsg, isg, qsg;
+    double ra,dec, ra0, dec0;
+    double mag, magtest, secmarg;
+    int istar, istar1, istar2, pmni, nid;
+    int nzmax = NZONES;	/* Maximum number of declination zones */
+    int magsort;
+    char *str;
+    char cstr[32], rastr[32], numstr[32], decstr[32], catid[32];
+    char *title;
+
+    itot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set catalog code and path to catalog */
+    if (strncasecmp (refcatname,"ub",2)==0) {
+	if ((str = getenv("UB1_PATH")) != NULL)
+	    strcpy (ub1path,str);
+	ucat = UB1;
+	upath = ub1path;
+	}
+    else if (strncasecmp (refcatname,"yb",2)==0) {
+	if ((str = getenv("YB6_PATH")) != NULL)
+	    strcpy (yb6path,str);
+	ucat = YB6;
+	upath = yb6path;
+	}
+    else {
+	fprintf (stderr, "UBCREAD:  %s not a USNO catalog\n", refcatname);
+	return (0);
+	}
+
+    if (strchr (refcatname, 't'))
+	objtype = 1;
+    else
+	objtype = 0;
+
+    /* If root pathname is a URL, search and return */
+    if (!strncmp (upath, "http:",5)) {
+	return (webread (upath,refcatname,distsort,cra,cdec,dra,ddec,drad,
+			 dradi,sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,
+			 unum,ura,udec,upra,updec,umag,upmni,nlog));
+	}
+
+    wcscstr (cstr, sysout, eqout, epout);
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* mag1 is always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Sort by 4th magnitude = R2 if sort magnitude is not set */
+    if (sortmag > 0 && sortmag < 6)
+	magsort = sortmag - 1;
+    else
+	magsort = 3;
+
+    /* Add 60 arcsec/century margins to region to get most stars which move */
+    if (minpmqual < 11 && (epout != 0.0 || sysout != sysref))
+	secmarg = 60.0;
+    else
+	secmarg = 0.0;
+
+    /* Find RA and Dec limits in catalog coordinate system */
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    /* Find declination zones to search */
+    nz = ubczones (rra1, rra2, rdec1, rdec2, nzmax, zlist, verbose);
+    if (nz <= 0) {
+	fprintf (stderr, "UBCREAD:  no USNO B zones found\n");
+	return (0);
+	}
+
+    /* Write header if printing star entries as found */
+    if (nstarmax < 1) {
+	char *revmessage;
+	revmessage = getrevmsg();
+	title = CatName (ucat, refcatname);
+	printf ("catalog	%s\n", title);
+	free ((char *)title);
+	ra2str (rastr, 31, cra, 3);
+	printf ("ra	%s\n", rastr);
+	dec2str (decstr, 31, cdec, 2);
+	printf ("dec	%s\n", decstr);
+	printf ("rpmunit	mas/year\n");
+	printf ("dpmunit	mas/year\n");
+	if (drad != 0.0) {
+	    printf ("radmin	%.1f\n", drad*60.0);
+	    if (dradi > 0)
+		printf ("radimin	%.1f\n", dradi*60.0);
+	    }
+	else {
+	    printf ("dramin	%.1f\n", dra*60.0* cosdeg (cdec));
+	    printf ("ddecmin	%.1f\n", ddec*60.0);
+	    }
+	printf ("radecsys	%s\n", cstr);
+	printf ("equinox	%.3f\n", eqout);
+	printf ("epoch  %.3f\n", epout);
+	printf ("program        scat %s\n", revmessage);
+	CatID (catid, ucat);
+	if (objtype) {
+	    printf ("%s	ra          	dec         	", catid);
+	    printf ("magb1 	magr1 	magb1 	magb2 	magn  	");
+	    printf ("sgb1 	sgr1 	sgb1 	sgb2	");
+	    printf ("ura  	udec	pm	ni	qsg	arcmin\n");
+	    printf ("------------	------------	------------	");
+	    printf ("-----	-----	-----	-----	-----	");
+	    printf ("----	----	----	----	");
+	    printf ("-----	-----	--	--	---	------\n");
+	    }
+	else {
+	    printf ("%s	ra          	dec         	", catid);
+	    printf ("magb1 	magr1 	magb1 	magb2 	magn  	ura  	");
+	    printf ("udec	pm	ni	qsg	arcmin\n");
+	    printf ("------------	------------	------------	");
+	    printf ("-----	-----	-----	-----	-----	-----	");
+	    printf ("-----	--	--	---	------\n");
+	    }
+	}
+
+    /* Convert RA and Dec limits to same units as catalog for quick filter */
+    ubra1 = (int) (rra1 * 360000.0 + 0.5);
+    ubra2 = (int) (rra2 * 360000.0 + 0.5);
+    ubdec1 = (int) ((rdec1 * 360000.0) + 32400000.5);
+    ubdec2 = (int) ((rdec2 * 360000.0) + 32400000.5);
+
+    /* Convert dra to angular units for rectangular box on sky */
+    dra = dra / cos (degrad (cdec));
+    
+    if (nstarmax > ndist) {
+	if (ndist > 0)
+	    free ((void *)udist);
+	udist = (double *) malloc (nstarmax * sizeof (double));
+	if (udist == NULL) {
+	    fprintf (stderr,"UBCREAD:  cannot allocate separation array\n");
+	    return (0);
+	    }
+	ndist = nstarmax;
+	}
+
+    /* Loop through region list */
+    nstar = 0;
+    for (iz = 0; iz < nz; iz++) {
+
+    /* Get path to zone catalog */
+	znum = zlist[iz];
+	if ((nstars = ubcopen (znum)) != 0) {
+
+	    jstar = 0;
+	    jtable = 0;
+	    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+	    /* Find first star based on RA */
+		if (iwrap == 0 || wrap == 0) {
+		    istar1 = ubcsra (rra1);
+		    if (istar1 > 1)
+			istar1 = istar1 - 1;
+		    }
+		else
+		    istar1 = 1;
+
+	    /* Find last star based on RA */
+		if (iwrap == 1 || wrap == 0) {
+		    istar2 = ubcsra (rra2);
+		    if (istar2 < nstars)
+			istar2 = istar2 + 1;
+		    }
+		else
+		    istar2 = nstars;
+
+		if (istar1 == 0 || istar2 == 0)
+		    break;
+
+		nread = istar2 - istar1 + 1;
+		itable = 0;
+
+	    /* Loop through zone catalog for this region */
+		for (istar = istar1; istar <= istar2; istar++) {
+		    itable ++;
+		    jtable ++;
+
+		    if (ubcstar (istar, &star)) {
+			fprintf (stderr,"UBCREAD: Cannot read star %d\n", istar);
+			break;
+			}
+
+		/* Extract selected fields */
+
+		/* Check rough position limits */
+     		    if ((star.decsec >= ubdec1 && star.decsec <= ubdec2) &&
+			((wrap && (star.rasec>=ubra1 || star.rasec<=ubra2)) ||
+			(!wrap && (star.rasec>=ubra1 && star.rasec<=ubra2))
+			)){
+
+			/* Set magnitude by which to sort and test */
+			mag = ubcmag (star.mag[magsort]);
+			if (sortmag == 0) {
+			    if (mag > 30.0)
+				mag = ubcmag (star.mag[1]);
+			    if (mag > 30.0)
+				mag = ubcmag (star.mag[2]);
+			    if (mag > 30.0)
+				mag = ubcmag (star.mag[0]);
+			    }
+			pass = 1;
+			if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+			    pass = 0;
+
+			if (pass) {
+			    nid = ubcndet (star.pmerr);
+			    if (nid < minid) {
+				if (minid > 0 && nid > 0)
+				    pass = 0;
+				}
+			    if (minid < 0 && nid < -minid)
+				pass = 0;
+			    }
+
+			/* Test distance limits */
+			if (pass) {
+			    ra0 = ubcra (star.rasec);
+			    dec0 = ubcdec (star.decsec);
+			    ra = ra0;
+			    dec = dec0;
+			    pmqual = ubcpmq (star.pm);
+			    if (nid == 0)
+				pmqual = 10;
+			    nsg = 0;
+			    qsg = 0;
+			    for (i = 0; i < 4; i++) {
+				if (star.mag[i] > 0) {
+				    isg = ubcsg (star.mag[i]);
+				    if (isg > 0) {
+					nsg++;
+					qsg = qsg + isg;
+					}
+				    }
+				}
+			    if (pmqual == 10 || nsg < 1)
+				qsg = 12;
+			    else
+				qsg = qsg / nsg;
+			    pmni = (10000 * qsg) + (100 * pmqual) + nid;
+
+			    /* Convert to search equinox and epoch */
+			    if (pmqual < minpmqual) {
+				rapm = 0.0;
+				decpm = 0.0;
+				rapm0 = 0.0;
+				decpm0 = 0.0;
+				wcscon (sysref,sysout,eqref,eqout,
+					&ra,&dec,epout);
+				}
+			    else {
+				rapm0 = ubcpra (star.pm) / cos (degrad (dec));
+				decpm0 = ubcpdec (star.pm);
+				rapm = rapm0;
+				decpm = decpm0;
+				wcsconp (sysref,sysout,eqref,eqout,epref,epout,
+					 &ra, &dec, &rapm, &decpm);
+				}
+
+			    if (distsort || drad > 0.0)
+				dist = wcsdist (cra,cdec,ra,dec);
+			    else
+				dist = 0.0;
+
+			/* Test spatial limits */
+			    if (drad > 0.0) {
+				if (dist > drad)
+				    pass = 0;
+				if (dradi > 0.0 && dist < dradi)
+				    pass = 0;
+				}
+			    else {
+				rdist = wcsdist (cra,dec,ra,dec);
+				if (rdist > dra)
+				    pass = 0;
+				ddist = wcsdist (ra,cdec,ra,dec);
+				if (ddist > ddec)
+				    pass = 0;
+				}
+			    }
+
+			if (pass) {
+			    num = (double) znum +
+				  (0.0000001 * (double)istar);
+
+			/* Write star position and magnitudes to stdout */
+			    if (nstarmax < 1) {
+				CatNum (ucat, -12, 0, num, numstr);
+				ra2str (rastr, 31, ra, 3);
+				dec2str (decstr, 31, dec, 2);
+				dist = wcsdist (cra,cdec,ra,dec) * 60.0;
+				printf ("%s	%s	%s", numstr,rastr,decstr);
+				for (i = 0; i < 5; i++)
+				    printf ("	%.2f",ubcmag(star.mag[i]));
+				if (objtype) {
+				    for (i = 0; i < 4; i++)
+					printf ("	%2d",ubcsg(star.mag[i]));
+				    }
+				printf ("	%6.1f	%6.1f",
+					rapm * 3600000.0 * cosdeg(dec),
+					decpm * 3600000.0);
+				printf ("	%d	%d	%d",
+					pmqual, nid, qsg);
+				printf ("	%.2f\n", dist/60.0);
+				}
+
+			    /* Save star position and magnitude in table */
+			    else if (nstar < nstarmax) {
+				unum[nstar] = num;
+				ura[nstar] = ra;
+				udec[nstar] = dec;
+				upra[nstar] = rapm;
+				updec[nstar] = decpm;
+				for (i = 0; i < 5; i++)
+				    umag[i][nstar] = ubcmag (star.mag[i]);
+				upmni[nstar] = pmni;
+				udist[nstar] = dist;
+				if (dist > maxdist) {
+				    maxdist = dist;
+				    farstar = nstar;
+				    }
+				if (mag > faintmag) {
+				    faintmag = mag;
+				    faintstar = nstar;
+				    }
+				}
+
+			    /* If too many stars and distance sorting,
+				replace furthest star */
+			    else if (distsort) {
+				if (dist < maxdist) {
+				    unum[farstar] = num;
+				    ura[farstar] = ra;
+				    udec[farstar] = dec;
+				    udec[farstar] = dec;
+				    upra[farstar] = rapm;
+				    for (i = 0; i < 5; i++)
+					umag[i][farstar] = ubcmag (star.mag[i]);
+				    upmni[farstar] = pmni;
+				    udist[farstar] = dist;
+
+				/* Find new farthest star */
+				    if (nstarmax > 1) {
+					maxdist = 0.0;
+					for (i = 0; i < nstarmax; i++) {
+					    if (udist[i] > maxdist) {
+						maxdist = udist[i];
+						farstar = i;
+						}
+					    }
+					}
+				    else {
+					maxdist = dist;
+					farstar = 0;
+					}
+				    }
+				}
+
+			    /* If too many stars, replace faintest star */
+			    else if (mag < faintmag) {
+				unum[faintstar] = num;
+				ura[faintstar] = ra;
+				udec[faintstar] = dec;
+				upra[faintstar] = rapm;
+				updec[faintstar] = decpm;
+				for (i = 0; i < 5; i++)
+				    umag[i][faintstar] = ubcmag (star.mag[i]);
+				upmni[faintstar] = pmni;
+				udist[faintstar] = dist;
+
+			    /* Find new faintest star */
+				faintmag = 0.0;
+				for (i = 0; i < nstarmax; i++) {
+				    magtest = umag[magsort][i];
+				    if (sortmag == 0) {
+					if (faintmag < 30.0)
+					    faintmag = umag[1][i];
+					if (faintmag < 30.0)
+					    faintmag = umag[2][i];
+					if (faintmag < 30.0)
+					    faintmag = umag[0][i];
+					}
+				    if (magtest > faintmag) {
+					faintmag = magtest;
+					faintstar = i;
+					}
+				    }
+				}
+			    nstar++;
+			    jstar++;
+			    if (nlog == 1) {
+				fprintf (stderr,"UBCREAD: %04d.%07d: %9.5f %9.5f %s\n",
+					znum,istar,ra,dec,cstr);
+				for (i = 0; i < 5; i++)
+				    fprintf (stderr, " %5.2f", ubcmag(star.mag[i]));
+				fprintf (stderr,"\n");
+				}
+
+			    /* End of accepted star processing */
+			    }
+
+		    /* End of individual star processing */
+			}
+
+		/* Log operation */
+		    if (nlog > 0 && itable%nlog == 0)
+			fprintf (stderr,"UBCREAD: zone %d (%2d / %2d) %8d / %8d / %8d sources\r",
+				znum, iz+1, nz, jstar, itable, nread);
+
+		/* End of star loop */
+		    }
+
+		/* End of wrap loop */
+		}
+
+	/* Close zone input file */
+	    (void) fclose (fcat);
+	    itot = itot + itable;
+	    if (nlog > 0)
+		fprintf (stderr,"UBCREAD: zone %d (%2d / %2d) %8d / %8d / %8d sources      \n",
+			znum, iz+1, nz, jstar, jtable, nstars);
+
+	/* End of zone processing */
+	    }
+
+    /* End of zone loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0) {
+	if (nz > 1)
+	    fprintf (stderr,"UBCREAD: %d zones: %d / %d found\n",nz,nstar,itot);
+	else
+	    fprintf (stderr,"UBCREAD: 1 zone: %d / %d found\n",nstar,itable);
+	if (nstar > nstarmax)
+	    fprintf (stderr,"UBCREAD: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+    return (nstar);
+}
+
+
+/* UBCRNUM -- Return USNO-B1.0 sources with specified ID numbers */
+
+int
+ubcrnum (refcatname,nnum,sysout,eqout,epout,unum,ura,udec,upra,updec,umag,upmni,nlog)
+
+char	*refcatname;	/* Name of catalog (UBC, USAC, UBC2, USAC2) */
+int	nnum;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*unum;		/* Array of UB numbers to find */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	*upra;		/* Array of right ascension proper motions (returned) */
+double	*updec;		/* Array of declination proper motions (returned) */
+double	**umag;		/* Array of blue and red magnitudes (returned) */
+int	*upmni;		/* Array of number of ids and pm quality (returned) */
+int	nlog;		/* Logging interval */
+{
+    UBCstar star;	/* UB catalog entry for one star */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+
+    int znum;
+    int jnum;
+    int i;
+    int nzone;
+    int nfound = 0;
+    int pmqual;
+    double ra,dec;
+    double rapm, decpm;
+    double dstar;
+    int istar, pmni, nid;
+    int nsg, qsg;
+    char *str;
+
+    /* Set catalog code and path to catalog */
+    if (strncasecmp (refcatname,"ub",2)==0) {
+	if ((str = getenv("UB1_PATH")) != NULL)
+	    strcpy (ub1path,str);
+	ucat = UB1;
+	upath = ub1path;
+	}
+    else if (strncasecmp (refcatname,"yb",2)==0) {
+	if ((str = getenv("YB6_PATH")) != NULL)
+	    strcpy (yb6path,str);
+	ucat = YB6;
+	upath = yb6path;
+	}
+    else {
+	fprintf (stderr, "UBCREAD:  %s not a USNO catalog\n", refcatname);
+	return (0);
+	}
+
+    /* If root pathname is a URL, search and return */
+    if (!strncmp (upath, "http:",5)) {
+	return (webrnum (upath,refcatname,nnum,sysout,eqout,epout,1,
+			 unum,ura,udec,upra,updec,umag,upmni,nlog));
+	}
+
+
+/* Loop through star list */
+    for (jnum = 0; jnum < nnum; jnum++) {
+
+    /* Get path to zone catalog */
+	znum = (int) unum[jnum];
+	if ((nzone = ubcopen (znum)) != 0) {
+	    dstar = (unum[jnum] - znum) * 10000000.0;
+	    istar = (int) (dstar + 0.5);
+	    if (istar > nzone) {
+		fprintf (stderr,"UBCRNUM: Star %d > max. in zone %d\n",
+			 istar,nzone);
+		break;
+		}
+
+	    if (ubcstar (istar, &star)) {
+		fprintf (stderr,"UBCRNUM: Cannot read star %d\n", istar);
+		break;
+		}
+
+	    /* Extract selected fields */
+	    else {
+		ra = ubcra (star.rasec); /* Right ascension in degrees */
+		dec = ubcdec (star.decsec); /* Declination in degrees */
+		pmqual = ubcpmq (star.pm);
+		nid = ubcndet (star.pmerr);
+		if (nid == 0)
+		    pmqual = 10;
+		nsg = 0;
+		qsg = 0;
+		for (i = 0; i < 4; i++) {
+		    if (star.mag[i] > 0) {
+			nsg++;
+			qsg = qsg + ubcsg (star.mag[i]);
+			}
+		    }
+		if (pmqual == 10 || nsg < 1)
+		    qsg = 12;
+		else
+		    qsg = qsg / nsg;
+		pmni = (10000 * qsg) + (100 * pmqual) + nid;
+
+		/* Convert to desired equinox and epoch */
+		if (pmqual < minpmqual) {
+		    rapm = 0.0;
+		    decpm = 0.0;
+		    wcscon (sysref,sysout,eqref,eqout,&ra,&dec,epout);
+		    }
+		else {
+		    rapm = ubcpra (star.pm);
+		    decpm = ubcpdec (star.pm);
+		    wcsconp (sysref,sysout,eqref,eqout,epref,epout,
+			     &ra, &dec, &rapm, &decpm);
+		    }
+
+		/* Save star position and magnitude in table */
+		ura[nfound] = ra;
+		udec[nfound] = dec;
+		upra[nfound] = rapm;
+		updec[nfound] = decpm;
+		upmni[nfound] = pmni;
+		for (i = 0; i< 5; i++)
+		    umag[i][nfound] = ubcmag (star.mag[i]);
+
+		nfound++;
+		if (nlog == 1) {
+		    fprintf (stderr,"UBCRNUM: %04d.%08d: %9.5f %9.5f",
+			     znum,istar,ra,dec);
+		    for (i = 0; i < 5; i++)
+			fprintf (stderr, " %5.2f", ubcmag(star.mag[i]));
+		    fprintf (stderr, "\n");
+		    }
+
+		/* Log operation */
+		if (nlog > 0 && jnum%nlog == 0)
+		    fprintf (stderr,"UBCRNUM: %4d.%8d  %8d / %8d sources\r",
+			     znum, istar, jnum, nnum);
+
+		(void) fclose (fcat);
+		/* End of star processing */
+		}
+
+	    /* End of star */
+	    }
+
+	/* End of star loop */
+	}
+
+    /* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"UBCRNUM:  %d / %d found\n",nfound,nnum);
+
+    return (nfound);
+}
+
+
+/* UBCBIN -- Fill FITS WCS image with USNO-B1.0 sources */
+
+int
+ubcbin (refcatname, wcs, header, image, mag1, mag2, sortmag, magscale, nlog)
+
+char	*refcatname;	/* Name of catalog (UB1 only, for now) */
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 or 2) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;		/* Logging interval */
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int	sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    int nz;		/* Number of input UB zone files */
+    int zlist[NZONES];	/* List of input UB zones */
+    UBCstar star;	/* UB catalog entry for one star */
+    double rdist, ddist;
+    int pmqual;
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+
+    double rra1, rra2, rdec1, rdec2;
+    double rapm, decpm, rapm0, decpm0;
+    double xpix, ypix, flux;
+    int offscl;
+    int wrap, iwrap;
+    int verbose;
+    int znum, itot,iz, i;
+    int ix, iy;
+    int nsg, qsg;
+    int jtable,jstar;
+    int itable = 0;
+    int nstar, nread, pass;
+    int ubra1, ubra2, ubdec1, ubdec2;
+    double ra,dec, ra0, dec0;
+    double mag, secmarg;
+    int istar, istar1, istar2, pmni, nid;
+    int nzmax = NZONES;	/* Maximum number of declination zones */
+    int bitpix, w, h;	/* Image bits/pixel and pixel width and height */
+    int magsort;
+    char *str;
+    char cstr[32];
+    double logt = log(10.0);
+
+    itot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set catalog code and path to catalog */
+    if (strncasecmp (refcatname,"ub",2)==0) {
+	if ((str = getenv("UB1_PATH")) != NULL)
+	    strcpy (ub1path,str);
+	ucat = UB1;
+	upath = ub1path;
+	}
+    else if (strncasecmp (refcatname,"yb",2)==0) {
+	if ((str = getenv("YB6_PATH")) != NULL)
+	    strcpy (yb6path,str);
+	ucat = YB6;
+	upath = yb6path;
+	}
+    else {
+	fprintf (stderr, "UBCBIN:  %s not a USNO catalog\n", refcatname);
+	return (0);
+	}
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* mag1 is always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    /* Bin using 4th magnitude = R2 if sort magnitude is not set */
+    if (sortmag > 0 && sortmag < 6)
+	magsort = sortmag - 1;
+    else
+	magsort = 3;
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    /* Add 60 arcsec/century margins to region to get most stars which move */
+    if (minpmqual < 11 && (epout != 0.0 || sysout != sysref))
+	secmarg = 60.0;
+    else
+	secmarg = 0.0;
+
+    /* Find RA and Dec limits in catalog coordinate system */
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    /* Find declination zones to search */
+    nz = ubczones (rra1, rra2, rdec1, rdec2, nzmax, zlist, verbose);
+    if (nz <= 0) {
+	fprintf (stderr, "UBCBIN:  no USNO B zones found\n");
+	return (0);
+	}
+
+    /* Convert RA and Dec limits to same units as catalog for quick filter */
+    ubra1 = (int) (rra1 * 360000.0 + 0.5);
+    ubra2 = (int) (rra2 * 360000.0 + 0.5);
+    ubdec1 = (int) ((rdec1 * 360000.0) + 32400000.5);
+    ubdec2 = (int) ((rdec2 * 360000.0) + 32400000.5);
+
+    /* Convert dra to angular units for rectangular box on sky */
+    dra = dra / cos (degrad (cdec));
+    
+    /* Loop through region list */
+    nstar = 0;
+    for (iz = 0; iz < nz; iz++) {
+
+    /* Get path to zone catalog */
+	znum = zlist[iz];
+	if ((nstars = ubcopen (znum)) != 0) {
+
+	    jstar = 0;
+	    jtable = 0;
+	    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+	    /* Find first star based on RA */
+		if (iwrap == 0 || wrap == 0) {
+		    istar1 = ubcsra (rra1);
+		    if (istar1 > 1)
+			istar1 = istar1 - 1;
+		    }
+		else
+		    istar1 = 1;
+
+	    /* Find last star based on RA */
+		if (iwrap == 1 || wrap == 0) {
+		    istar2 = ubcsra (rra2);
+		    if (istar2 < nstars)
+			istar2 = istar2 + 1;
+		    }
+		else
+		    istar2 = nstars;
+
+		if (istar1 == 0 || istar2 == 0)
+		    break;
+
+		nread = istar2 - istar1 + 1;
+		itable = 0;
+
+	    /* Loop through zone catalog for this region */
+		for (istar = istar1; istar <= istar2; istar++) {
+		    itable ++;
+		    jtable ++;
+
+		    if (ubcstar (istar, &star)) {
+			fprintf (stderr,"UBCBIN: Cannot read star %d\n", istar);
+			break;
+			}
+
+		/* Extract selected fields */
+
+		/* Check rough position limits */
+     		    if ((star.decsec >= ubdec1 && star.decsec <= ubdec2) &&
+			((wrap && (star.rasec>=ubra1 || star.rasec<=ubra2)) ||
+			(!wrap && (star.rasec>=ubra1 && star.rasec<=ubra2))
+			)){
+
+			/* Set magnitude by which to sort and test */
+			mag = ubcmag (star.mag[magsort]);
+			if (sortmag == 0) {
+			    if (mag > 30.0)
+				mag = ubcmag (star.mag[1]);
+			    if (mag > 30.0)
+				mag = ubcmag (star.mag[2]);
+			    if (mag > 30.0)
+				mag = ubcmag (star.mag[0]);
+			    }
+			pass = 1;
+			if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+			    pass = 0;
+
+			if (pass) {
+			    nid = ubcndet (star.pmerr);
+			    if (nid < minid) {
+				if (minid > 0 && nid > 0)
+				    pass = 0;
+				}
+			    if (minid < 0 && nid < -minid)
+				pass = 0;
+			    }
+
+			/* Test distance limits */
+			if (pass) {
+			    ra0 = ubcra (star.rasec);
+			    dec0 = ubcdec (star.decsec);
+			    ra = ra0;
+			    dec = dec0;
+			    pmqual = ubcpmq (star.pm);
+			    if (nid == 0)
+				pmqual = 10;
+			    nsg = 0;
+			    qsg = 0;
+			    for (i = 0; i < 4; i++) {
+				if (star.mag[i] > 0) {
+				    nsg++;
+				    qsg = qsg + ubcsg (star.mag[i]);
+				    }
+				}
+			    if (pmqual == 10 || nsg < 1)
+				qsg = 12;
+			    else
+				qsg = qsg / nsg;
+			    pmni = (10000 * qsg) + (100 * pmqual) + nid;
+
+			    /* Convert to search equinox and epoch */
+			    if (pmqual < minpmqual) {
+				rapm = 0.0;
+				decpm = 0.0;
+				rapm0 = 0.0;
+				decpm0 = 0.0;
+				wcscon (sysref,sysout,eqref,eqout,
+					&ra,&dec,epout);
+				}
+			    else {
+				rapm0 = ubcpra (star.pm) / cos (degrad (dec));
+				decpm0 = ubcpdec (star.pm);
+				rapm = rapm0;
+				decpm = decpm0;
+				wcsconp (sysref,sysout,eqref,eqout,epref,epout,
+					 &ra, &dec, &rapm, &decpm);
+				}
+
+			/* Test spatial limits */
+			    rdist = wcsdist (cra,dec,ra,dec);
+			    if (rdist > dra)
+				pass = 0;
+			    ddist = wcsdist (ra,cdec,ra,dec);
+			    if (ddist > ddec)
+				pass = 0;
+			    }
+
+			/* Save star in FITS image */
+			if (pass) {
+			    wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+			    if (!offscl) {
+				if (magscale > 0.0)
+				    flux = magscale * exp (logt * (-mag / 2.5));
+				else
+				    flux = 1.0;
+				ix = (int) (xpix + 0.5);
+				iy = (int) (ypix + 0.5);
+				addpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy, flux);
+				nstar++;
+				jstar++;
+				if (nlog == 1) {
+				    flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+				    fprintf (stderr,"UBCBIN: %d %04d.%07d: %9.5f %9.5f %s",
+					nstar,znum,nstar,ra,dec,cstr);
+				    if (magscale > 0.0)
+					fprintf (stderr, " %5.2f", mag);
+				    fprintf (stderr," %5d %5d: %f\n", ix, iy, flux);
+				    }
+				}
+
+			    /* End of accepted star processing */
+			    }
+
+		    /* End of individual star processing */
+			}
+
+		/* Log operation */
+		    if (nlog > 0 && itable%nlog == 0)
+			fprintf (stderr,"UBCBIN: zone %d (%2d / %2d) %8d / %8d / %8d sources\r",
+				znum, iz+1, nz, jstar, itable, nread);
+
+		/* End of star loop */
+		    }
+
+		/* End of wrap loop */
+		}
+
+	/* Close zone input file */
+	    (void) fclose (fcat);
+	    itot = itot + itable;
+	    if (nlog > 0)
+		fprintf (stderr,"UBCBIN: zone %d (%2d / %2d) %8d / %8d / %8d sources      \n",
+			znum, iz+1, nz, jstar, jtable, nstars);
+
+	/* End of zone processing */
+	    }
+
+    /* End of zone loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0) {
+	if (nz > 1)
+	    fprintf (stderr,"UBCBIN: %d zones: %d / %d found\n",nz,nstar,itot);
+	else
+	    fprintf (stderr,"UBCBIN: 1 zone: %d / %d found\n",nstar,itable);
+	}
+    return (nstar);
+}
+
+
+/* UBCZONES -- figure out which UB zones will need to be searched */
+
+static int
+ubczones (ra1, ra2, dec1, dec2, nzmax, zones, verbose)
+
+double	ra1, ra2;	/* Right ascension limits in degrees */
+double	dec1, dec2; 	/* Declination limits in degrees */
+int	nzmax;		/* Maximum number of zones to find */
+int	*zones;		/* Region numbers (returned)*/
+int	verbose;	/* 1 for diagnostics */
+
+{
+    int nrgn;		/* Number of zones found (returned) */
+    int iz,iz1,iz2,i;
+
+    for (i = 0; i < nzmax; i++)
+	zones[i] = 0;
+
+    nrgn = 0;
+
+/* Find zone range to search based on declination */
+    iz1 = ubczone (dec1);
+    iz2 = ubczone (dec2);
+
+/* Tabulate zones to search */
+    i = 0;
+    if (iz2 >= iz1) {
+	for (iz = iz1; iz <= iz2; iz++)
+	    zones[i++] = iz;
+	}
+    else {
+	for (iz = iz2; iz <= iz1; iz++)
+	    zones[i++] = iz;
+	}
+
+    nrgn = i;
+    if (verbose) {
+	fprintf(stderr,"UBCZONES:  %d zones: %d - %d\n",nrgn,zones[0],zones[i-1]);
+	fprintf(stderr,"UBCZONES: RA: %.5f - %.5f, Dec: %.5f - %.5f\n",ra1,ra2,dec1,dec2);
+	}
+
+    return (nrgn);
+}
+
+
+/* UBCRA -- returns right ascension in degrees from the UB star structure */
+
+static double
+ubcra (rasec)
+
+int rasec;	/* RA in 100ths of arcseconds from UB catalog entry */
+{
+    return ((double) (rasec) / 360000.0);
+}
+
+
+/* UBCDEC -- returns the declination in degrees from the UB star structure */
+
+static double
+ubcdec (decsec)
+
+int decsec;	/* Declination in 100ths of arcseconds from UB catalog entry */
+{
+    return ((double) (decsec - 32400000) / 360000.0);
+}
+
+
+/* UBCMAG -- returns a magnitude from the UBC star structure */
+
+static double
+ubcmag (magetc)
+
+int magetc;	/* Magnitude 4 bytes from UB catalog entry */
+{
+    double xmag;
+
+    if (ucat == YB6)
+	xmag =  0.001 * (double) magetc;
+    else if (magetc < 0)
+	xmag = (double) (-magetc % 10000) * 0.01;
+    else
+	xmag = (double) (magetc % 10000) * 0.01;
+    if (xmag == 0.00)
+	xmag = 99.99;
+    return (xmag);
+}
+
+
+/* UBCPRA -- returns RA proper motion in arcsec/year from UBC star structure */
+
+static double
+ubcpra (magetc)
+
+int magetc;	/* Proper motion field from UB catalog entry */
+{
+    double pm;
+
+    if (magetc < 0)
+	pm = (double) (-magetc % 10000);
+    else
+	pm = (double) (magetc % 10000);
+    pm = ((pm * 0.002) - 10.0) / 3600.0;
+    return (pm);
+}
+
+
+/* UBCPDEC -- returns Dec proper motion in arcsec/year from UBC star structure */
+
+static double
+ubcpdec (magetc)
+
+int magetc;	/* Proper motion field from UB catalog entry */
+{
+    double pm;
+    if (ucat == YB6)
+	pm = (double) (magetc / 10000);
+    else if (magetc < 0)
+	pm = (double) ((-magetc % 100000000) / 10000);
+    else
+	pm = (double) ((magetc % 100000000) / 10000);
+    pm = ((pm * 0.002) - 10.0) / 3600.0;
+    return (pm);
+}
+
+
+/* UBCPMQ -- returns proper motion probability (1-9) */
+
+static int
+ubcpmq (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UB catalog entry */
+{
+    if (magetc < 0)
+	return (-magetc / 100000000);
+    else
+	return (magetc / 100000000);
+}
+
+
+/* UBCMAGERR -- returns 1 if magnitude is uncertain from UB star structure */
+
+int
+ubcmagerr (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UB catalog entry */
+{
+    if (magetc < 0)
+	return ((-magetc / 1000000000) % 10);
+    else
+	return ((magetc / 1000000000) % 10);
+}
+
+
+/* UBCNDET -- returns number of detections; 0 = Tycho-2 Catalog */
+
+static int
+ubcndet (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UB catalog entry */
+{
+    if (ucat == YB6)
+	return (0);
+    else if (magetc < 0)
+	return (-magetc / 100000000);
+    else
+	return (magetc / 100000000);
+}
+
+
+/* UBCSG -- returns closeness to star PSF (0-11) */
+
+static int
+ubcsg (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UB catalog entry */
+{
+    if (ucat == YB6)
+	return (0);
+    else if (magetc < 0)
+	return (-magetc / 100000000);
+    else
+	return (magetc / 100000000);
+}
+
+
+/* UBCZONE -- find the UB zone number where a declination can be found */
+
+static int
+ubczone (dec)
+
+double dec;	/* declination in degrees */
+{
+    double zonesize = 0.1;	/* number of degrees per declination zone */
+    int zone;
+
+    zone = (int) ((dec + 90.0) / zonesize);
+    if (zone > 1799)
+	zone = 1799;
+    else if (zone < 0)
+	zone = 0;
+    return (zone);
+}
+
+
+/* UBCSRA -- Find UB star closest to specified right ascension */
+
+static int
+ubcsra (rax0)
+
+double	rax0;		/* Right ascension in degrees for which to search */
+{
+    int istar, istar1, istar2, nrep;
+    double rax, ra1, ra, rdiff, rdiff1, rdiff2, sdiff;
+    UBCstar star;	/* UB catalog entry for one star */
+    char rastrx[32];
+    int debug = 0;
+
+    rax = rax0;
+    if (debug)
+	ra2str (rastrx, 31, rax, 3);
+    istar1 = 1;
+    if (ubcstar (istar1, &star))
+	return (0);
+    ra1 = ubcra (star.rasec);
+    istar = nstars;
+    nrep = 0;
+    while (istar != istar1 && nrep < 30) {
+	if (ubcstar (istar, &star))
+	    break;
+	else {
+	    ra = ubcra (star.rasec);
+	    if (ra == ra1)
+		break;
+	    if (debug) {
+		char rastr[32];
+		ra2str (rastr, 31, ra, 3);
+		fprintf (stderr,"UBCSRA %d %d: %s (%s)\n",
+			 nrep,istar,rastr,rastrx);
+		}
+	    rdiff = ra1 - ra;
+	    rdiff1 = ra1 - rax;
+	    rdiff2 = ra - rax;
+	    if (nrep > 25 && ABS(rdiff2) > ABS(rdiff1)) {
+		istar = istar1;
+		break;
+		}
+	    nrep++;
+	    sdiff = (double)(istar - istar1) * rdiff1 / rdiff;
+	    istar2 = istar1 + (int) (sdiff + 0.5);
+	    ra1 = ra;
+	    if (debug) {
+		fprintf (stderr," ra1=    %.5f ra=     %.5f rax=    %.5f\n",
+			 ra1,ra,rax);
+		fprintf (stderr," rdiff=  %.5f rdiff1= %.5f rdiff2= %.5f\n",
+			 rdiff,rdiff1,rdiff2);
+		fprintf (stderr," istar1= %d istar= %d istar2= %d\n",
+			 istar1,istar,istar2);
+		}
+	    istar1 = istar;
+	    istar = istar2;
+	    if (istar < 1)
+		istar = 1;
+	    if (istar > nstars)
+		istar = nstars;
+	    if (istar == istar1)
+		break;
+	    }
+	}
+    return (istar);
+}
+
+/* UBCOPEN -- Open UB Catalog zone catalog, returning number of entries */
+
+static int
+ubcopen (znum)
+
+int znum;	/* UB Catalog zone */
+{
+    char zonepath[64];	/* Pathname for input UB zone file */
+    UBCstar star;	/* UB catalog entry for one star */
+    int lfile;
+    
+/* Get path to zone catalog */
+    if (ubcpath (znum, zonepath)) {
+	fprintf (stderr, "UBCOPEN: Cannot find zone catalog for %d\n", znum);
+	return (0);
+	}
+
+/* Find number of stars in zone catalog by its length */
+    lfile = getfilesize (zonepath);
+    if (lfile < 2) {
+	fprintf (stderr,"UB zone catalog %s has no entries\n",zonepath);
+	return (0);
+	}
+    else
+	nstars = lfile / nbent;
+
+/* Open zone catalog */
+    if (!(fcat = fopen (zonepath, "rb"))) {
+	fprintf (stderr,"UB zone catalog %s cannot be read\n",zonepath);
+	return (0);
+	}
+
+/* Check to see if byte-swapping is necessary */
+    cswap = 0;
+    if (ubcstar (1, &star)) {
+	fprintf (stderr,"UBCOPEN: cannot read star 1 from UB zone catalog %s\n",
+		 zonepath);
+	return (0);
+	}
+    else {
+	if (star.rasec > 360 * 360000 || star.rasec < 0) {
+	    cswap = 1;
+	    /* fprintf (stderr,"UBCOPEN: swapping bytes in UB zone catalog %s\n",
+		     zonepath); */
+	    }
+	else if (star.decsec > 180 * 360000 || star.decsec < 0) {
+	    cswap = 1;
+	    /* fprintf (stderr,"UBCOPEN: swapping bytes in UB zone catalog %s\n",
+		     zonepath); */
+	    }
+	else
+	    cswap = 0;
+	}
+
+    return (nstars);
+}
+
+
+/* UBCPATH -- Get UB Catalog region file pathname */
+
+static int
+ubcpath (zn, path)
+
+int zn;		/* UB zone number */
+char *path;	/* Pathname of UB zone file */
+
+{
+    /* Return error code and null path if zone is out of range */
+    if (zn < 0 || zn > 1799) {
+	fprintf (stderr, "UBCPATH: zone %d out of range 0-1799\n",zn);
+	path[0] = (char) 0;
+	return (-1);
+	}
+
+    /* Set path for USNO-B1.0 zone catalog */
+    sprintf (path,"%s/%03d/b%04d.cat", upath, zn/10, zn);
+
+    return (0);
+}
+
+
+/* UBCSTAR -- Get UB catalog entry for one star; return 0 if successful */
+
+static int
+ubcstar (istar, star)
+
+int istar;	/* Star sequence number in UB zone catalog */
+UBCstar *star;	/* UB catalog entry for one star */
+{
+    int nbs, nbr, nbskip;
+
+    if (istar < 1 || istar > nstars) {
+	fprintf (stderr, "UBCstar %d is not in catalog\n",istar);
+	return (-1);
+	}
+    nbskip = nbent * (istar - 1);
+    if (fseek (fcat,nbskip,SEEK_SET))
+	return (-1);
+    nbs = sizeof (UBCstar);
+    nbr = fread (star, nbs, 1, fcat) * nbs;
+    if (nbr < nbs) {
+	fprintf (stderr, "UBCstar %d / %d bytes read\n",nbr, nbs);
+	return (-2);
+	}
+    if (cswap)
+	ubcswap ((char *)star);
+    return (0);
+}
+
+
+/* UBCSWAP -- Reverse bytes of UB Catalog entry */
+
+static void
+ubcswap (string)
+
+char *string;	/* Start of vector of 4-byte ints */
+
+{
+char *sbyte, *slast;
+char temp0, temp1, temp2, temp3;
+int nbytes = nbent; /* Number of bytes to reverse */
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp3 = sbyte[0];
+	temp2 = sbyte[1];
+	temp1 = sbyte[2];
+	temp0 = sbyte[3];
+	sbyte[0] = temp0;
+	sbyte[1] = temp1;
+	sbyte[2] = temp2;
+	sbyte[3] = temp3;
+	sbyte = sbyte + 4;
+	}
+    return;
+}
+
+/* Jan 30 2003	New subroutine based on ubcread.c
+ * Feb  4 2003	Open catalog file rb instead of r (Martin Ploner, Bern)
+ * Mar 21 2003	Improve search limit test by always using wcsdist()
+ * Apr 15 2003	Explicitly get revision date if nstarmax < 1
+ * May 27 2003	Use getfilesize() to get file size
+ * Jun  2 2003	Print proper motion as mas/year
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Sep 25 2003	Add ubcbin() to fill an image with sources
+ * Oct  6 2003	Update ubcread() and ubcbin() for improved RefLim()
+ * Dec  1 2003	Add missing tab to n=-1 header
+ * Dec  4 2003	Add USNO YB6 catalog
+ * Dec 12 2003	Fix bug in wcs2pix() call in ubcbin()
+ *
+ * Aug 27 2004	Include fitsfile.h
+ * Nov 19 2004	Return galaxy/star type code (qsg=0-11) in upmni vector
+ *
+ * Jan 12 2005	Declare ubcsg()
+ *
+ * Jun 20 2006	Drop unused variables
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ * Nov 15 2006	Print coordinates if in verbose mode; convert coords to integer
+ *
+ * Jan 10 2007	Add match=1 argument to webrnum()
+ * Nov 26 2007	Add one at each end of search range in ubcread()
+ * Dec 05 2007	Add option to print per magnitude star/galaxy discriminators if ub1t
+ * Dec 05 2007	Drop sg flag=0 from average qsg
+ */
diff --git a/Code/src/libwcs/ucacread.c b/Code/src/libwcs/ucacread.c
new file mode 100644
index 0000000000000000000000000000000000000000..63667c2f5d029497c08b3ec787ad57663db00d85
--- /dev/null
+++ b/Code/src/libwcs/ucacread.c
@@ -0,0 +1,1559 @@
+/*** File libwcs/ucacread.c
+ *** November 5, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 2003-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * ucacread()	Read UCAC Star Catalog stars in a rectangle on the sky
+ * ucacrnum()	Read UCAC Star Catalog stars by number 
+ * ucacbin()	Fill a FITS WECS image with UCAC Star Catalog stars
+ * ucaczones()	Make list of zones covered by a range of declinations
+ * ucacsra (sc,st,zone,rax0)   Find UCAC star closest to specified right ascension
+ * ucacopen(zone, nstars)   Open UCAC catalog file, returning number of entries
+ * ucacclose (sc)	    Close UCAC catalog file 
+ * ucacstar (sc,st,zone,istar) Get UCAC catalog entry for one star
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "fitsfile.h"
+#include "wcs.h"
+#include "wcscat.h"
+
+#define MAXZONE 100
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+
+typedef struct {
+    int rasec, decsec;
+    short cmag;
+    unsigned char era, edec, nobs, rflg, ncat, cflg;
+    short epra, epdec;
+    int rapm, decpm;
+    unsigned char erapm, edecpm, qrapm, qdecpm;
+    int id2m;
+    short jmag, hmag, kmag, qm, cc;
+} UCAC2star;
+
+typedef struct {
+    int rasec, decsec;
+    short mmag, amag, sigmag;
+    char objt, dsf;
+    short sigra, sigdec;
+    char na1, nu1, us1, cn1;
+    short cepra, cepdec;
+    int rapm, decpm;
+    short sigpmr, sigpmd;
+    int id2m;
+    short jmag, hmag, kmag;
+    char jq, hq, kq;
+    char e2mpho[3];
+    short bmag, rmag, imag;
+    char clbl;
+    char bq, rq, iq;
+    char catflg[10];
+    char g1, c1, leda, x2m;
+    int rn;
+} UCAC3star;
+
+
+/* pathname of UCAC1 decompressed data files or search engine URL */
+char ucac1path[64]="/data/astrocat/ucac1";
+
+/* pathname of UCAC2 decompressed data files or search engine URL */
+char ucac2path[64]="/data/astrocat/ucac2";
+
+/* pathname of UCAC3 decompressed data files or search engine URL */
+char ucac3path[64]="/data/astrocat/ucac3";
+
+char *ucacpath;
+static int ucat = 0;
+
+static double *gdist;	/* Array of distances to stars */
+static int ndist = 0;
+static int cswap = 0;   /* Byte reverse catalog to Mac/Sun/network order if 1 */
+
+static int ucaczones();
+struct StarCat *ucacopen();
+void ucacclose();
+static int ucacsra();
+static int ucacstar();
+static void ucacswap4();
+static void ucacswap2();
+
+
+/* UCACREAD -- Read UCAC Star Catalog stars */
+
+int
+ucacread (refcatname,cra,cdec,dra,ddec,drad,dradi,distsort,sysout,eqout,epout,
+	  mag1,mag2,sortmag,nstarmax,gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog)
+
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	distsort;	/* 1 to sort stars by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 or 2) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*gnum;		/* Array of Guide Star numbers (returned) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double  *gpra;          /* Array of right ascension proper motions (returned) */
+double  *gpdec;         /* Array of declination proper motions (returned) */
+double	**gmag;		/* Array of b and v magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double ra1,ra2;		/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;		/* Limiting declinations of region in degrees */
+    double dist = 0.0;		/* Distance from search center in degrees */
+    double faintmag=0.0;	/* Faintest magnitude */
+    double maxdist=0.0;		/* Largest distance */
+    int	faintstar=0;		/* Faintest star */
+    int	farstar=0;		/* Most distant star */
+    int nz;			/* Number of UCAC regions in search */
+    int zlist[MAXZONE];		/* List of region numbers */
+    int sysref = WCS_J2000;	/* Catalog coordinate system */
+    double eqref = 2000.0;	/* Catalog equinox */
+    double epref = 2000.0;	/* Catalog epoch */
+    double secmarg = 60.0;	/* Arcsec/century margin for proper motion */
+    struct StarCat *starcat;	/* Star catalog data structure */
+    struct Star *star;		/* Single star cata structure */
+    double errra, errdec, errpmr, errpmd;
+    int nim, ncat;
+    int verbose;
+    int wrap;
+    int iz;
+    int magsort;
+    int jstar;
+    int nrmax = MAXZONE;
+    int nstar,i, ntot, imag;
+    int istar, istar1, istar2;
+    int jtable,iwrap, nread;
+    int pass;
+    int zone;
+    int nmag, nmag1;
+    double num, ra, dec, rapm, decpm, mag;
+    double rra1, rra2, rdec1, rdec2;
+    double rdist, ddist;
+    char cstr[32], rastr[32], decstr[32];
+    char ucacenv[16];
+    char *str;
+
+    ntot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set catalog code and path to catalog */
+    if (strncmp (refcatname,"ucac2",5)==0 ||
+        strncmp (refcatname,"UCAC2",5)==0) {
+	ucat = UCAC2;
+	ucacpath = ucac2path;
+	strcpy (ucacenv, "UCAC2_PATH");
+	nmag = 4;
+	nmag1 = 4;
+	}
+    else if (strncmp (refcatname,"ucac3",5)==0 ||
+        strncmp (refcatname,"UCAC3",5)==0) {
+	ucat = UCAC3;
+	ucacpath = ucac3path;
+	strcpy (ucacenv, "UCAC3_PATH");
+	nmag = 8;
+	nmag1 = 12;
+	}
+    else {
+	ucat = UCAC1;
+	ucacpath = ucac1path;
+	strcpy (ucacenv, "UCAC1_PATH");
+	nmag = 1;
+	nmag1 = 1;
+	}
+
+    /* If pathname is set in environment, override local value */
+    if ((str = getenv (ucacenv)) != NULL )
+	ucacpath = str;
+
+    /* If pathname is a URL, search and return */
+    if (!strncmp (ucacpath, "http:",5)) {
+	return (webread (ucacpath,refcatname,distsort,cra,cdec,dra,ddec,drad,
+			 dradi,sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,
+			 gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog));
+	}
+
+    wcscstr (cstr, sysout, eqout, epout);
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* Make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+   if (sortmag < 1)
+	magsort = 0;
+    else if (sortmag > nmag)
+	magsort = nmag - 1;
+    else
+	magsort = sortmag - 1;
+
+    /* Allocate table for distances of stars from search center */
+    if (nstarmax > ndist) {
+	if (ndist > 0)
+	    free ((void *)gdist);
+	gdist = (double *) malloc (nstarmax * sizeof (double));
+	if (gdist == NULL) {
+	    fprintf (stderr,"UCACREAD:  cannot allocate separation array\n");
+	    return (0);
+	    }
+	ndist = nstarmax;
+	}
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    nstar = 0;
+    jstar = 0;
+
+    /* Get RA and Dec limits in catalog (J2000) coordinates */
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    /* Find UCAC Star Catalog zones in which to search */
+    nz = ucaczones (rdec1,rdec2,nrmax,zlist,verbose);
+    if (nz <= 0) {
+	fprintf (stderr,"UCACREAD:  no UCAC zone for %.2f-%.2f %.2f %.2f\n",
+		 rra1, rra2, rdec1, rdec2);
+	return (0);
+	}
+
+    /* Write header if printing star entries as found */
+    if (nstarmax < 1) {
+	char *revmessage;
+	revmessage = getrevmsg();
+	printf ("catalog	UCAC1\n");
+	ra2str (rastr, 31, cra, 3);
+	printf ("ra	%s\n", rastr);
+	dec2str (decstr, 31, cdec, 2);
+	printf ("dec	%s\n", decstr);
+	printf ("rpmunit	mas/year\n");
+	printf ("dpmunit	mas/year\n");
+	if (drad != 0.0) {
+	    printf ("radmin	%.1f\n", drad*60.0);
+	    if (dradi > 0)
+		printf ("radimin	%.1f\n", dradi*60.0);
+	    }
+	else {
+	    printf ("dramin	%.1f\n", dra*60.0* cosdeg (cdec));
+	    printf ("ddecmin	%.1f\n", ddec*60.0);
+	    }
+	printf ("radecsys	%s\n", cstr);
+	printf ("equinox	%.3f\n", eqout);
+	printf ("epoch	%.3f\n", epout);
+	printf ("program	scat %s\n", revmessage);
+	printf ("ucac_id   	ra          	dec         	");
+	if (ucat == UCAC1) {
+	    printf ("mag 	ura   	udec  	arcmin\n");
+	    printf ("----------	------------	------------	");
+	    printf ("-----	------	------	------\n");
+	    }
+	else if (ucat == UCAC2) {
+	    printf ("raerr 	decerr	magc 	");
+	    printf ("magj	magh	magk 	mag	");
+	    printf ("pmra  	pmdec  	pmrerr 	pmderr 	");
+	    printf ("ni	nc	arcsec\n");
+	    printf ("----------	------------	------------    ");
+	    printf ("------	------	-----	-----	-----	");
+	    printf ("-----	-----	------	------	-----	-----	");
+	    printf ("--	--	------\n");
+	    }
+	else if (ucat == UCAC3) {
+	    printf ("raerr 	decerr	magb 	magr 	magi 	");
+	    printf ("magj 	magh 	magk 	maga 	magm 	");
+	    printf ("mag  	pmra  	pmdec  	pmrerr 	pmderr 	");
+	    printf ("ni	nc	arcsec\n");
+	    printf ("----------	------------	------------	");
+	    printf ("------	------	-----	------	-----	");
+	    printf ("-----	-----	-----	-----	-----	");
+	    printf ("-----	-----	-----	------	------	");
+	    printf ("--	--	------\n");
+	    }
+	}
+
+    /* Loop through zone list */
+    nstar = 0;
+    for (iz = 0; iz < nz; iz++) {
+
+	/* Get path to zone catalog */
+	zone = zlist[iz];
+	if ((starcat = ucacopen (zone)) != 0) {
+
+	    jstar = 0;
+	    jtable = 0;
+	    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+		/* Find first star based on RA */
+		if (iwrap == 0 || wrap == 0) {
+		    istar1 = ucacsra (starcat, star, zone, rra1);
+		    if (istar > 5)
+			istar = istar - 5;
+		    else
+			istar = 1;
+		    }
+		else
+		    istar1 = 1;
+
+		/* Find last star based on RA */
+		if (iwrap == 1 || wrap == 0) {
+		    istar2 = ucacsra (starcat, star, zone, rra2);
+		    if (istar2 < starcat->nstars - 5)
+			istar2 = istar2 + 5;
+		    else
+			istar2 = starcat->nstars;
+		    }
+		else
+		    istar2 = starcat->nstars;
+
+		if (istar1 == 0 || istar2 == 0)
+		    break;
+
+		nread = istar2 - istar1 + 1;
+
+		/* Loop through zone catalog for this region */
+		for (istar = istar1; istar <= istar2; istar++) {
+		    jtable ++;
+
+		    if (ucacstar (starcat, star, zone, istar)) {
+			fprintf(stderr,"UCACREAD: Cannot read star %d\n",istar);
+			break;
+			}
+
+		    /* ID number */
+		    num = star->num;
+
+		    /* Magnitude */
+		    mag = star->xmag[magsort];
+
+		    /* Check magnitude limits */
+		    pass = 1;
+		    if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+			pass = 0;
+
+		    /* Check position limits */
+		    if (pass) {
+
+			/* Get position in output coordinate system */
+			ra = star->ra;
+			dec = star->dec;
+			rapm = star->rapm;
+			decpm = star->decpm;
+			errra = star->errra;
+			errdec = star->errdec;
+			errpmr = star->errpmr;
+			errpmd = star->errpmd;
+			nim = star->nimage;
+			ncat = star->ncat;
+			wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		 	     &ra, &dec, &rapm, &decpm);
+
+			/* Compute distance from search center */
+			if (drad > 0 || distsort)
+			    dist = wcsdist (cra,cdec,ra,dec);
+			else
+			    dist = 0.0;
+
+			/* Check radial distance to search center */
+			if (drad > 0) {
+			    if (dist > drad)
+				pass = 0;
+			    if (dradi > 0.0 && dist < dradi)
+				pass = 0;
+			    }
+
+			/* Check distance along RA and Dec axes */
+			else {
+			    ddist = wcsdist (cra,cdec,cra,dec);
+			    if (ddist > ddec)
+				pass = 0;
+			    rdist = wcsdist (cra,dec,ra,dec);
+		            if (rdist > dra)
+				pass = 0;
+			    }
+			}
+
+		    if (pass) {
+
+		    /* Write star position and magnitude to stdout */
+			if (nstarmax < 1) {
+			    ra2str (rastr, 31, ra, 3);
+			    dec2str (decstr, 31, dec, 2);
+			    dist = wcsdist (cra,cdec,ra,dec) * 3600.0;
+			    printf ("%010.6f	%s	%s", num,rastr,decstr);
+			    if (ucat == UCAC2 || ucat == UCAC3)
+				printf ("	%5.3f	%5.3f",
+				errra * 3600.0 * cosdeg (dec), errdec * 3600.0);
+			    if (ucat == UCAC1)
+				printf ("	%5.2f", mag);
+			    else
+				printf ("	%5.2f	%5.2f	%5.2f	%5.2f",
+					star->xmag[0], star->xmag[1],
+					star->xmag[2], star->xmag[3]);
+			    if (ucat == UCAC3)
+				printf ("	%5.2f	%5.2f	%5.2f	%5.2f",
+					star->xmag[4], star->xmag[5],
+					star->xmag[6], star->xmag[7]);
+			    printf ("	%5.2f	%6.1f	%6.1f",
+				mag, rapm*3600000.0*cosdeg (dec), decpm*3600000.0);
+			    printf ("	%6.1f	%6.1f",
+				errpmr*3600000.0, errpmd*3600000.0);
+			    printf ("	%2d	%2d	%.3f\n",
+				nim, ncat, dist);
+			    }
+
+			/* Save star position and magnitude in table */
+			else if (nstar < nstarmax) {
+			    gnum[nstar] = num;
+			    gra[nstar] = ra;
+			    gdec[nstar] = dec;
+			    gpra[nstar] = rapm;
+			    gpdec[nstar] = decpm;
+			    if (ucat == UCAC1)
+				gmag[0][nstar] = mag;
+			    else {
+				for (imag = 0; imag < nmag; imag++)
+				    gmag[imag][nstar] = star->xmag[imag];
+
+				gmag[nmag][nstar] = star->errra;
+				gmag[nmag+1][nstar] = star->errdec;
+				gmag[nmag+2][nstar] = star->errpmr;
+				gmag[nmag+3][nstar] = star->errpmd;
+				gtype[nstar] = (1000 * nim) + ncat;
+				}
+			    gdist[nstar] = dist;
+			    if (dist > maxdist) {
+				maxdist = dist;
+				farstar = nstar;
+				}
+			    if (mag > faintmag) {
+				faintmag = mag;
+				faintstar = nstar;
+				}
+			    }
+
+			/* If too many stars and distance sorting,
+			   replace farthest star */
+			else if (distsort) {
+			    if (dist < maxdist) {
+				gnum[farstar] = num;
+				gra[farstar] = ra;
+				gdec[farstar] = dec;
+				gpra[farstar] = rapm;
+				gpdec[farstar] = decpm;
+				if (ucat == UCAC1)
+				    gmag[0][farstar] = mag;
+				else {
+				    for (imag = 0; imag < nmag; imag++)
+					gmag[imag][farstar] = star->xmag[imag];
+					gmag[nmag][farstar] = star->errra;
+					gmag[nmag+1][farstar] = star->errdec;
+					gmag[nmag+2][farstar] = star->errpmr;
+					gmag[nmag+3][farstar] = star->errpmd;
+					gtype[farstar] = (1000 * nim) + ncat;
+				    }
+				gdist[farstar] = dist;
+
+				/* Find new farthest star */
+				maxdist = 0.0;
+				for (i = 0; i < nstarmax; i++) {
+				    if (gdist[i] > maxdist) {
+					maxdist = gdist[i];
+					farstar = i;
+					}
+				    }
+				}
+			    }
+
+			/* Else if too many stars, replace faintest star */
+			else if (mag < faintmag) {
+			    gnum[faintstar] = num;
+			    gra[faintstar] = ra;
+			    gdec[faintstar] = dec;
+			    gpra[faintstar] = rapm;
+			    gpdec[faintstar] = decpm;
+			    if (ucat == UCAC1)
+				gmag[0][faintstar] = mag;
+			    else {
+				for (imag = 0; imag < nmag; imag++)
+				    gmag[imag][faintstar] = star->xmag[imag];
+				gmag[nmag][faintstar] = star->errra;
+				gmag[nmag+1][faintstar] = star->errdec;
+				gmag[nmag+2][faintstar] = star->errpmr;
+				gmag[nmag+3][faintstar] = star->errpmd;
+				gtype[faintstar] = (1000 * nim) + ncat;
+				}
+			    gdist[faintstar] = dist;
+			    faintmag = 0.0;
+
+			    /* Find new faintest star */
+			    for (i = 0; i < nstarmax; i++) {
+				if (gmag[magsort][i] > faintmag) {
+				    faintmag = gmag[magsort][i];
+				    faintstar = i;
+				    }
+				}
+			    }
+
+			nstar++;
+			if (nlog == 1)
+			    fprintf (stderr,"UCACREAD: %11.6f: %9.5f %9.5f %5.2f\n",
+				 num,ra,dec,mag);
+
+			/* End of accepted star processing */
+			}
+
+		    /* Log operation */
+		    jstar++;
+		    if (nlog > 0 && istar%nlog == 0)
+			fprintf (stderr,"UCACREAD: %5d / %5d / %5d sources\r",
+				 nstar,jstar,starcat->nstars);
+
+		    /* End of star loop */
+		    }
+
+		/* End of 0:00 RA wrap loop */
+		}
+
+	    /* End of successful zone file loop */
+	    ntot = ntot + starcat->nstars;
+	    if (nlog > 0)
+		fprintf (stderr,"UCACREAD: %4d / %4d: %5d / %5d  / %5d sources from zone %4d    \n",
+		 	 iz+1,nz,nstar,jstar,starcat->nstars,zlist[iz]);
+
+	    /* Close region input file */
+	    ucacclose (starcat);
+	    }
+
+	/* End of zone loop */
+	}
+
+    /* Summarize transfer */
+    if (nlog > 0) {
+	if (nz > 1)
+	    fprintf (stderr,"UCACREAD: %d zones: %d / %d found\n",nz,nstar,ntot);
+	else
+	    fprintf (stderr,"UCACREAD: 1 region: %d / %d found\n",nstar,ntot);
+	if (nstar > nstarmax)
+	    fprintf (stderr,"UCACREAD: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+    return (nstar);
+}
+
+/* UCACRNUM -- Read HST Guide Star Catalog stars from CDROM */
+
+int
+ucacrnum (refcatname,nstars,sysout,eqout,epout,
+	 gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog)
+
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+int	nstars;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*gnum;		/* Array of Guide Star numbers (returned) */
+double	*gra;		/* Array of right ascensions (returned) */
+double	*gdec;		/* Array of declinations (returned) */
+double  *gpra;          /* Array of right ascension proper motions (returned) */
+double  *gpdec;         /* Array of declination proper motions (returned) */
+double	**gmag;		/* Array of B and V magnitudes (returned) */
+int	*gtype;		/* Array of object types (returned) */
+int	nlog;		/* 1 for diagnostics */
+{
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    struct StarCat *starcat;
+    struct Star *star;
+    char *str;
+    char ucacenv[16];
+
+    int verbose;
+    int zone, zone0;
+    int jstar, imag, nmag, nmag1;
+    int istar, nstar;
+    double num, ra, dec, rapm, decpm, mag;
+
+    if (nlog == 1)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set catalog code and path to catalog */
+    if (strncmp (refcatname,"ucac3",5)==0 ||
+        strncmp (refcatname,"UCAC3",5)==0) {
+	ucat = UCAC3;
+	ucacpath = ucac3path;
+	strcpy (ucacenv, "UCAC3_PATH");
+	nmag = 8;
+	nmag1 = 12;
+	}
+    else if (strncmp (refcatname,"ucac2",5)==0 ||
+        strncmp (refcatname,"UCAC2",5)==0) {
+	ucat = UCAC2;
+	ucacpath = ucac2path;
+	strcpy (ucacenv, "UCAC2_PATH");
+	nmag = 4;
+	nmag1 = 4;
+	}
+    else {
+	ucat = UCAC1;
+	ucacpath = ucac1path;
+	strcpy (ucacenv, "UCAC1_PATH");
+	nmag = 1;
+	nmag1 = 1;
+	}
+
+    /* If pathname is set in environment, override local value */
+    if ((str = getenv(ucacenv)) != NULL )
+	ucacpath = str;
+
+    /* If pathname is a URL, search and return */
+    if (!strncmp (ucacpath, "http:",5))
+	return (webrnum (ucacpath,refcatname,nstars,sysout,eqout,epout,1,
+			 gnum,gra,gdec,gpra,gpdec,gmag,gtype,nlog));
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+    nstar = 0;
+    zone0 = 0;
+    starcat = NULL;
+
+/* Loop through star list */
+    for (jstar = 0; jstar < nstars; jstar++) {
+	zone = (int) (gnum[jstar] + 0.0000001);
+
+	/* Find numbered stars (rrr.nnnnnn) */
+	istar = (int) ((gnum[jstar] - (double) zone + 0.0000001) * 1000000.0);
+	if (istar > 0) {
+	    if (zone != zone0) {
+		if (starcat != NULL)
+		    ucacclose (starcat);
+		starcat = ucacopen (zone);
+		}
+    	    if (starcat == NULL) {
+		fprintf (stderr,"UCACRNUM: Zone %d file not found\n", zone);
+		return (0);
+		}
+	    if (ucacstar (starcat, star, zone, istar)) {
+		fprintf (stderr,"UCACRNUM: Cannot read star %d.%06d\n", zone, istar);
+		gra[jstar] = 0.0;
+		gdec[jstar] = 0.0;
+		if (ucat == UCAC1)
+		    gmag[0][jstar] = 0.0;
+		else {
+		    for (imag = 0; imag < nmag; imag++)
+			gmag[imag][jstar] = 0.0;
+		    }
+		gtype[jstar] = 0;
+		continue;
+		}
+
+	    /* If star has been found in catalog */
+
+	    /* ID number */
+	    num = star->num;
+
+	    /* Position in degrees at designated epoch */
+	    ra = star->ra;
+	    dec = star->dec;
+	    rapm = star->rapm;
+	    decpm = star->decpm;
+	    wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		     &ra, &dec, &rapm, &decpm);
+
+	    /* Magnitude */
+	    mag = star->xmag[0];
+
+	    /* Save star position and magnitude in table */
+	    gnum[jstar] = num;
+	    gra[jstar] = ra;
+	    gdec[jstar] = dec;
+	    gpra[jstar] = rapm;
+	    gpdec[jstar] = decpm;
+	    gmag[0][jstar] = mag;
+    	    gtype[jstar] = (1000 * star->nimage) + star->ncat;
+	    if (ucat == UCAC1)
+		gmag[0][jstar] = star->xmag[0];
+	    else {
+		for (imag = 0; imag < nmag; imag++)
+		    gmag[imag][jstar] = star->xmag[imag];
+		gmag[nmag][jstar] = star->errra;
+		gmag[nmag+1][jstar] = star->errdec;
+		gmag[nmag+2][jstar] = star->errpmr;
+		gmag[nmag+3][jstar] = star->errpmd;
+		gtype[jstar] = (1000 * star->nimage) + star->ncat;
+		}
+	    if (nlog == 1) {
+		if (ucat == UCAC1)
+		    fprintf (stderr,"UCACRNUM: %11.6f: %9.5f %9.5f %5.2f %s  \n",
+			     num, ra, dec, mag, star->isp);
+		else if (ucat == UCAC2) {
+		    fprintf (stderr,"UCACRNUM: %11.6f: %9.5f %9.5f %5.2f",
+			     num, ra, dec, star->xmag[0]);
+		    fprintf (stderr," %5.2f %5.2f %5.2f %d %d",
+			     star->xmag[1],star->xmag[2],star->xmag[3]);
+		    fprintf (stderr," %d %d\n",star->nimage,star->ncat);
+		    }
+		else
+		    fprintf (stderr,"UCACRNUM: %11.6f: %9.5f %9.5f %5.2f %5.2f",
+			     num, ra, dec, star->xmag[0],star->xmag[1]);
+		    fprintf (stderr," %5.2f %5.2f %5.2f",
+			     star->xmag[2], star->xmag[3], star->xmag[4]);
+		    fprintf (stderr," %5.2f %5.2f %5.2f",
+			     star->xmag[5], star->xmag[6], star->xmag[7]);
+		    fprintf (stderr," %d %d\n",star->nimage,star->ncat);
+		}
+	    }
+
+	/* End of star loop */
+	}
+
+/* Summarize search */
+    ucacclose (starcat);
+    if (nlog > 0)
+	fprintf (stderr,"UCACRNUM: %d / %d found\n",nstar,starcat->nstars);
+
+    return (nstars);
+}
+
+
+/* UCACBIN -- Fill a FITS WCS image with UCAC Star Catalog stars */
+
+int
+ucacbin (refcatname, wcs, header, image, mag1, mag2, sortmag, magscale, nlog)
+
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 or 2) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	nlog;		/* 1 for diagnostics */
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;		/* Limiting declinations of region in degrees */
+    int nz;			/* Number of UCAC regions in search */
+    int zlist[MAXZONE];		/* List of region numbers */
+    int sysref = WCS_J2000;	/* Catalog coordinate system */
+    double eqref = 2000.0;	/* Catalog equinox */
+    double epref = 2000.0;	/* Catalog epoch */
+    double secmarg = 60.0;	/* Arcsec/century margin for proper motion */
+    struct StarCat *starcat;	/* Star catalog data structure */
+    struct Star *star;		/* Single star cata structure */
+    int verbose;
+    int wrap;
+    int ix, iy, iz;
+    int magsort;
+    int jstar;
+    int nrmax = MAXZONE;
+    int nstar, ntot;
+    int istar, istar1, istar2;
+    int jtable,iwrap, nread;
+    int pass;
+    int zone;
+    double num, ra, dec, rapm, decpm, mag;
+    double rra1, rra2, rdec1, rdec2;
+    double rdist, ddist;
+    char cstr[32];
+    char ucacenv[16];
+    char *str;
+    double xpix, ypix, flux;
+    int offscl;
+    int bitpix, w, h;   /* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+
+    ntot = 0;
+    if (nlog > 0)
+	verbose = 1;
+    else
+	verbose = 0;
+
+    /* Set catalog code and path to catalog */
+    if (strncmp (refcatname,"ucac3",5)==0 ||
+        strncmp (refcatname,"UCAC3",5)==0) {
+	ucat = UCAC3;
+	ucacpath = ucac3path;
+	strcpy (ucacenv, "UCAC3_PATH");
+	}
+   else if (strncmp (refcatname,"ucac2",5)==0 ||
+        strncmp (refcatname,"UCAC2",5)==0) {
+	ucat = UCAC2;
+	ucacpath = ucac2path;
+	strcpy (ucacenv, "UCAC2_PATH");
+	}
+    else {
+	ucat = UCAC1;
+	ucacpath = ucac1path;
+	strcpy (ucacenv, "UCAC1_PATH");
+	}
+
+    /* If pathname is set in environment, override local value */
+    if ((str = getenv (ucacenv)) != NULL )
+	ucacpath = str;
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* Make mag1 always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+   if (sortmag < 1)
+	magsort = 0;
+    else if (sortmag > 4)
+	magsort = 3;
+    else
+	magsort = sortmag - 1;
+
+    /* Allocate catalog entry buffer */
+    star = (struct Star *) calloc (1, sizeof (struct Star));
+    star->num = 0.0;
+
+    nstar = 0;
+    jstar = 0;
+
+    /* Get RA and Dec limits in catalog (J2000) coordinates */
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,secmarg,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    /* Find UCAC Star Catalog zones in which to search */
+    nz = ucaczones (rdec1,rdec2,nrmax,zlist,verbose);
+    if (nz <= 0) {
+	fprintf (stderr,"UCACBIN:  no UCAC zone for %.2f-%.2f %.2f %.2f\n",
+		 rra1, rra2, rdec1, rdec2);
+	return (0);
+	}
+
+    /* Loop through zone list */
+    nstar = 0;
+    for (iz = 0; iz < nz; iz++) {
+
+	/* Get path to zone catalog */
+	zone = zlist[iz];
+	if ((starcat = ucacopen (zone)) != 0) {
+
+	    jstar = 0;
+	    jtable = 0;
+	    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+		/* Find first star based on RA */
+		if (iwrap == 0 || wrap == 0)
+		    istar1 = ucacsra (starcat, star, zone, rra1);
+		else
+		    istar1 = 1;
+
+		/* Find last star based on RA */
+		if (iwrap == 1 || wrap == 0)
+		    istar2 = ucacsra (starcat, star, zone, rra2);
+		else
+		    istar2 = starcat->nstars;
+
+		if (istar1 == 0 || istar2 == 0)
+		    break;
+
+		nread = istar2 - istar1 + 1;
+
+		/* Loop through zone catalog for this region */
+		for (istar = istar1; istar <= istar2; istar++) {
+		    jtable ++;
+
+		    if (ucacstar (starcat, star, zone, istar)) {
+			fprintf(stderr,"UCACBIN: Cannot read star %d\n",istar);
+			break;
+			}
+
+		    /* ID number */
+		    num = star->num;
+
+		    /* Magnitude */
+		    mag = star->xmag[magsort];
+
+		    /* Check magnitude limits */
+		    pass = 1;
+		    if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+			pass = 0;
+
+		    /* Check position limits */
+		    if (pass) {
+
+			/* Get position in output coordinate system */
+			rapm = star->rapm;
+			decpm = star->decpm;
+			ra = star->ra;
+			dec = star->dec;
+			wcsconp (sysref, sysout, eqref, eqout, epref, epout,
+		 	     &ra, &dec, &rapm, &decpm);
+
+			/* Check distance along RA and Dec axes */
+			ddist = wcsdist (cra,cdec,cra,dec);
+			if (ddist > ddec)
+			    pass = 0;
+			rdist = wcsdist (cra,dec,ra,dec);
+		        if (rdist > dra)
+			    pass = 0;
+			}
+
+		    /* Save star in FITS image */
+		    if (pass) {
+			wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+			if (!offscl) {
+			    if (magscale > 0.0)
+				flux = magscale * exp (logt * (-mag / 2.5));
+			    else
+				flux = 1.0;
+			    ix = (int) (xpix + 0.5);
+			    iy = (int) (ypix + 0.5);
+			    addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+			    nstar++;
+			    jstar++;
+			    }
+			else {
+			    ix = 0;
+			    iy = 0;
+			    }
+			if (nlog == 1) {
+			    fprintf (stderr,"UCACBIN: %11.6f: %9.5f %9.5f %s",
+				     num,ra,dec,cstr);
+			    if (magscale > 0.0)
+				fprintf (stderr, " %5.2f", mag);
+			    if (!offscl)
+				flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+			    else
+				flux = 0.0;
+			    fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+			    }
+
+			/* End of accepted star processing */
+			}
+
+		    /* Log operation */
+		    jstar++;
+		    if (nlog > 0 && istar%nlog == 0)
+			fprintf (stderr,"UCACBIN: %5d / %5d / %5d sources\r",
+				 nstar,jstar,starcat->nstars);
+
+		    /* End of star loop */
+		    }
+
+		/* End of 0:00 RA wrap loop */
+		}
+
+	    /* End of successful zone file loop */
+	    ntot = ntot + starcat->nstars;
+	    if (nlog > 0)
+		fprintf (stderr,"UCACBIN: %4d / %4d: %5d / %5d  / %5d sources from zone %4d    \n",
+		 	 iz+1,nz,nstar,jstar,starcat->nstars,zlist[iz]);
+
+	    /* Close region input file */
+	    ucacclose (starcat);
+	    }
+
+	/* End of zone loop */
+	}
+
+    /* Summarize transfer */
+    if (nlog > 0) {
+	if (nz > 1)
+	    fprintf (stderr,"UCACBIN: %d zones: %d / %d found\n",nz,nstar,ntot);
+	else
+	    fprintf (stderr,"UCACBIN: 1 region: %d / %d found\n",nstar,ntot);
+	}
+    return (nstar);
+}
+
+
+/* UCACZONE -- Compute the zones over which to search
+ * in the specified range of coordinates.
+ * Build lists containing the first star and number of stars for each range.
+ */
+
+static int
+ucaczones (dec1, dec2, nzmax, zones, verbose)
+
+double	dec1, dec2; 	/* Declination limits in degrees */
+int	nzmax;		/* Maximum number of zones to find */
+int	*zones;		/* Zone numbers (returned)*/
+int	verbose;	/* 1 for diagnostics */
+
+{
+    int nz;		/* Number of declination zones found (returned) */
+    int iz, iz1, iz2;
+    int i;
+    double spd1, spd2;
+
+    for (i = 0; i < nzmax; i++)
+	zones[i] = 0;
+
+/* Find first and last declination zone to search */
+    spd1 = 90.0 + dec1;
+    iz1 = (int) ((spd1 * 2.0) + 0.99999);
+    if (iz1 < 1) iz1 = 1;
+    spd2 = 90.0 + dec2;
+    iz2 = (int) ((spd2 * 2.0) + 0.99999);
+    if (ucat == UCAC1 && iz2 > 169) iz2 = 169;
+    if (ucat == UCAC2 && iz2 > 288) iz2 = 288;
+    if (ucat == UCAC3 && iz2 > 360) iz2 = 360;
+    if (iz1 > iz2)
+	return (0);
+
+    nz = iz2 - iz1 + 1;
+    if (verbose) {
+	fprintf (stderr,"UCACZONES: searching %d zones: %d - %d\n",nz,iz1,iz2);
+	fprintf(stderr,"UCACZONES: Dec: %.5f - %.5f\n", dec1,dec2);
+	}
+
+    i = 0;
+    for (iz = iz1; iz <= iz2; iz++) {
+	zones[i] = iz;
+	i++;
+	}
+
+    return (nz);
+}
+
+
+/* UCACSRA -- Find UCAC star closest to specified right ascension */
+
+static int
+ucacsra (sc, st, zone, rax0)
+
+struct StarCat *sc;	/* Star catalog descriptor */
+struct Star *st;	/* Current star entry */
+int	zone;		/* Declination zone */
+double	rax0;		/* Right ascension in degrees for which to search */
+{
+    int istar, istar1, istar2, nrep;
+    double rax, ra1, ra, rdiff, rdiff1, rdiff2, sdiff;
+    char rastrx[32];
+    int debug = 0;
+
+    rax = rax0;
+    if (debug)
+	ra2str (rastrx, 31, rax, 3);
+    istar1 = 1;
+    ucacstar (sc, st, zone, istar1);
+    ra1 = st->ra;
+    istar = sc->nstars;
+    nrep = 0;
+    while (istar != istar1 && nrep < 20) {
+	if (ucacstar (sc, st, zone, istar))
+	    break;
+	else {
+	    ra = st->ra;
+	    if (ra == ra1)
+		break;
+	    if (debug) {
+		char rastr[32];
+		ra2str (rastr, 31, ra, 3);
+		fprintf (stderr,"UCACSRA %d %d: %s (%s)\n",
+			 nrep,istar,rastr,rastrx);
+		}
+	    rdiff = ra1 - ra;
+	    rdiff1 = ra1 - rax;
+	    rdiff2 = ra - rax;
+	    if (nrep > 20 && ABS(rdiff2) > ABS(rdiff1)) {
+		istar = istar1;
+		break;
+		}
+	    nrep++;
+	    sdiff = (double)(istar - istar1) * rdiff1 / rdiff;
+	    istar2 = istar1 + (int) (sdiff + 0.5);
+	    ra1 = ra;
+	    istar1 = istar;
+	    istar = istar2;
+	    if (debug) {
+		fprintf (stderr," ra1=    %.5f ra=     %.5f rax=    %.5f\n",
+			 ra1,ra,rax);
+		fprintf (stderr," rdiff=  %.5f rdiff1= %.5f rdiff2= %.5f\n",
+			 rdiff,rdiff1,rdiff2);
+		fprintf (stderr," istar1= %d istar= %d istar1= %d\n",
+			 istar1,istar,istar2);
+		}
+	    if (istar < 1)
+		istar = 1;
+	    if (istar > sc->nstars)
+		istar = sc->nstars;
+	    if (istar == istar1)
+		break;
+	    }
+	}
+    return (istar);
+}
+
+ 
+/* UCACOPEN -- Open UCAC catalog file, returning number of entries */
+
+struct StarCat *
+ucacopen (zone)
+
+int	zone;	/* Number of catalog zone to read */
+
+{
+    FILE *fcat;
+    struct StarCat *sc;
+    int lfile, lpath;
+    char *zonefile;
+    char *zonepath;	/* Full pathname for catalog file */
+
+    /* Set pathname for catalog file */
+    lpath = strlen (ucacpath) + 16;
+    zonepath = (char *) malloc (lpath);
+    if (ucat == UCAC1)
+	sprintf (zonepath, "%s/u1/z%03d", ucacpath, zone);
+    else if (ucat == UCAC2)
+	sprintf (zonepath, "%s/u2/z%03d", ucacpath, zone);
+    else
+	sprintf (zonepath, "%s/z%03d", ucacpath, zone);
+
+    /* Set UCAC catalog header information */
+    sc = (struct StarCat *) calloc (1, sizeof (struct StarCat));
+    sc->byteswapped = 0;
+
+    /* Set number of stars in this zone catalog */
+    if (ucat == UCAC1)
+	sc->nbent = 67;
+    else if (ucat == UCAC2)
+	sc->nbent = 44;
+    else
+	sc->nbent = 84;
+    lfile = getfilesize (zonepath);
+    if (lfile < 2) {
+        fprintf (stderr,"UCAC zone catalog %s has no entries\n",zonepath);
+	free (sc);
+	sc = NULL;
+        return (NULL);
+        }
+    else
+        sc->nstars = lfile / sc->nbent;
+
+    /* Open UCAC file */
+    if (!(fcat = fopen (zonepath, "r"))) {
+	fprintf (stderr,"UCACOPEN: UCAC file %s cannot be read\n",zonepath);
+	free (sc);
+	return (NULL);
+	}
+
+    /* Separate filename from pathname and save in structure */
+    zonefile = strrchr (zonepath,'/');
+    if (zonefile)
+	zonefile = zonefile + 1;
+    else
+	zonefile = zonepath;
+    if (strlen (zonefile) < 24)
+	strcpy (sc->isfil, zonefile);
+    else
+	strncpy (sc->isfil, zonefile, 23);
+
+    /* Set other catalog information in structure */
+    sc->inform = 'J';
+    sc->coorsys = WCS_J2000;
+    sc->epoch = 2000.0;
+    sc->equinox = 2000.0;
+    sc->ifcat = fcat;
+    sc->sptype = 0;
+    if (ucat == UCAC1)
+	sc->nmag = 1;
+    else if (ucat == UCAC2)
+	sc->nmag = 4;
+    else
+	sc->nmag = 8;
+
+    /* UCAC stars are RA-sorted within declination zones */
+    sc->rasorted = 1;
+
+/* Check to see if byte-swapping is necessary */
+    cswap = 0;
+    if (ucat == UCAC2) {
+	UCAC2star us2;	/* UCAC2 catalog entry for one star */
+	int nbr;
+
+	nbr = fread (&us2, 1, sc->nbent, sc->ifcat);
+	if (nbr < 1) {
+	    fprintf (stderr,
+		 "UCACOPEN: cannot read star 1 from UCAC2 zone catalog %s\n",
+		 zonepath);
+	    return (0);
+	    }
+
+	/* RA should be between 0 and 360 degrees in milliarcseconds */
+	if (us2.rasec > 360 * 3600000 || us2.rasec < 0)
+	    cswap = 1;
+
+	/* Dec should be between -90 and +90 degrees in milliarcseconds */
+	else if (us2.decsec > 90 * 3600000 || us2.decsec < -90 * 3600000)
+	    cswap = 1;
+
+	/* J H K magnitudes should be near-positive */
+	else if (us2.jmag < -1000 || us2.hmag < -1000  || us2.kmag < -1000)
+	    cswap = 1;
+	else
+	    cswap = 0;
+
+	/* if (cswap)
+	    fprintf (stderr,
+		    "UCACOPEN: swapping bytes in UCAC2 zone catalog %s\n",
+		     zonepath); */
+	}
+    else if (ucat == UCAC3) {
+	UCAC3star us3;	/* UCAC3 catalog entry for one star */
+	int nbr;
+
+	nbr = fread (&us3, 1, sc->nbent, sc->ifcat);
+	if (nbr < 1) {
+	    fprintf (stderr,
+		 "UCACOPEN: cannot read star 1 from UCAC3 zone catalog %s\n",
+		 zonepath);
+	    return (0);
+	    }
+
+	/* RA should be between 0 and 360 degrees in milliarcseconds */
+	if (us3.rasec > 360 * 3600000 || us3.rasec < 0)
+	    cswap = 1;
+
+	/* Dec should be between -90 and +90 degrees in milliarcseconds */
+	else if (us3.decsec > 180 * 3600000 || us3.decsec < -180 * 3600000)
+	    cswap = 1;
+
+	/* J H K magnitudes should be near-positive */
+	else if (us3.jmag < -1000 || us3.hmag < -1000  || us3.kmag < -1000)
+	    cswap = 1;
+	else
+	    cswap = 0;
+
+	/* if (cswap)
+	    fprintf (stderr,
+	    "UCACOPEN: swapping bytes in UCAC2 zone catalog %s\n",zomepath); */
+	}
+
+    sc->istar = 0;
+    free (zonepath);
+    return (sc);
+}
+
+
+void
+ucacclose (sc)
+struct StarCat *sc;	/* Star catalog descriptor */
+{
+    fclose (sc->ifcat);
+    free (sc);
+    return;
+}
+
+
+/* UCACSTAR -- Get UCAC catalog entry for one star;
+              return 0 if successful */
+
+static int
+ucacstar (sc, st, zone, istar)
+
+struct StarCat *sc;	/* Star catalog descriptor */
+struct Star *st;	/* Current star entry */
+int	zone;		/* Declination zone */
+int	istar;		/* Star sequence number in UCAC catalog region file */
+{
+    char line[256];
+    int nbr, nbskip;
+    UCAC2star us2;	/* UCAC2 catalog entry for one star */
+    UCAC3star us3;	/* UCAC3 catalog entry for one star */
+
+    /* Drop out if catalog pointer is not set */
+    if (sc == NULL)
+	return (1);
+
+    /* Drop out if catalog is not open */
+    if (sc->ifcat == NULL)
+	return (2);
+
+    /* Drop out if star number is too small or too large */
+    if (istar < 1 || istar > sc->nstars) {
+        fprintf (stderr, "UCAC star %d is not in catalog\n",istar);
+        return (-1);
+        }
+
+    /* Move file pointer to start of correct star entry */
+    nbskip = sc->nbent * (istar - 1);
+    if (fseek (sc->ifcat,nbskip,SEEK_SET))
+        return (-1);
+
+    if (ucat == UCAC1)
+	nbr = fread (line, 1, sc->nbent, sc->ifcat);
+    else if (ucat == UCAC2)
+	nbr = fread (&us2, 1, sc->nbent, sc->ifcat);
+    else
+	nbr = fread (&us3, 1, sc->nbent, sc->ifcat);
+    if (nbr < sc->nbent) {
+        fprintf (stderr, "UCACSTAR %d / %d bytes read\n",nbr, sc->nbent);
+        return (-2);
+        }
+
+    /* Star ID number = region.sequence */
+    st->num = (double) zone + (0.000001 * (double) istar);
+
+    /* Read UCAC1 position and proper motion from ASCII file */
+    if (ucat == UCAC1) {
+
+	/* Read position in degrees */
+	st->ra = atof (line) / 3600000.0;
+	st->dec = (atof (line+10) / 3600000.0) - 90.0;
+
+	/* Read proper motion and convert it to to degrees/year */
+	st->rapm = (atof (line+41) / 3600000.0) / cosdeg (st->dec);
+	st->decpm = atof (line+48) / 3600000.0;
+
+	/* Set V magnitude */
+	st->xmag[0] = atof (line+20) * 0.01;
+	}
+
+    /* Read UCAC2 position, proper motion, and magnitudes from binary file */
+    else if (ucat == UCAC2) {
+	if (cswap) {
+	    ucacswap4 (&us2.rasec);
+	    ucacswap4 (&us2.decsec);
+	    ucacswap4 (&us2.rapm);
+	    ucacswap4 (&us2.decpm);
+	    ucacswap2 (&us2.cmag);
+	    ucacswap2 (&us2.jmag);
+	    ucacswap2 (&us2.hmag);
+	    ucacswap2 (&us2.kmag);
+	    }
+	st->ra  = (double) us2.rasec  / 3600000.0;
+	st->dec = (double) us2.decsec / 3600000.0;
+	st->errra  = (double) us2.era / 3600000.0;	/* mas */
+	st->errra  = st->errra / cosdeg (st->dec);	/* to RA deg */
+	st->errdec = (double) us2.edec / 3600000.0;	/* mas */
+	st->rapm  = (double) us2.rapm  / 36000000.0;	/* 0.1mas/yr */
+	st->decpm = (double) us2.decpm / 36000000.0;	/* 0.1mas/yr */
+	st->errpmr  = (double) us2.erapm / 36000000.0;	/* 0.1mas/yr */
+	st->errpmr  = st->errpmr / cosdeg (st->dec);	/* to RA deg */
+	st->errpmd = (double) us2.edecpm / 36000000.0;	/* 0.1mas/yr */
+	st->xmag[0] = ((double) us2.jmag) / 1000.0;
+	st->xmag[1] = ((double) us2.hmag) / 1000.0;
+	st->xmag[2] = ((double) us2.kmag) / 1000.0;
+	st->xmag[3] = ((double) us2.cmag) / 100.0;
+	st->nimage = (int) us2.nobs;
+	st->ncat = (int) us2.ncat;
+	}
+
+    /* Read UCAC3 position, proper motion, and magnitudes from binary file */
+    else {
+	if (cswap) {
+	    ucacswap4 (&us3.rasec);
+	    ucacswap4 (&us3.decsec);
+	    ucacswap4 (&us3.rapm);
+	    ucacswap4 (&us3.decpm);
+	    ucacswap2 (&us3.sigra);
+	    ucacswap2 (&us3.sigdec);
+	    ucacswap2 (&us3.sigpmr);
+	    ucacswap2 (&us3.sigpmd);
+	    ucacswap2 (&us3.mmag);
+	    ucacswap2 (&us3.amag);
+	    ucacswap2 (&us3.jmag);
+	    ucacswap2 (&us3.hmag);
+	    ucacswap2 (&us3.kmag);
+	    ucacswap2 (&us3.bmag);
+	    ucacswap2 (&us3.rmag);
+	    ucacswap2 (&us3.imag);
+	    }
+	st->ra  = (double) us3.rasec  / 3600000.0;	/* mas */
+	st->dec = (double) us3.decsec / 3600000.0;	/* mas */
+	st->dec = st->dec - 90.0;
+	st->errra  = (double) us3.sigra / 3600000.0;	/* mas */
+	st->errra  = st->errra / cosdeg (st->dec);	/* to RA deg */
+	st->errdec = (double) us3.sigdec / 3600000.0;	/* mas */
+	st->rapm  = (double) us3.rapm  / 36000000.0;	/* 0.1mas/yr */
+	st->rapm  = st->rapm / cosdeg (st->dec);	/* to RA deg */
+	st->decpm = (double) us3.decpm / 36000000.0;	/* 0.1mas/yr */
+	st->errpmr  = (double) us3.sigpmr / 36000000.0;	/* 0.1mas/yr */
+	st->errpmr  = st->errpmr / cosdeg (st->dec);	/* to RA deg */
+	st->errpmd = (double) us3.sigpmd / 36000000.0;	/* 0.1mas/yr */
+	st->nimage = (int) us3.nu1;
+	st->ncat = (int) us3.us1;
+	if (us3.bmag == 0)
+	    st->xmag[0] = 99.990;
+	else
+	    st->xmag[0] = ((double) us3.bmag) / 1000.0;
+	if (us3.rmag == 0)
+	    st->xmag[1] = 99.990;
+	else
+	    st->xmag[1] = ((double) us3.rmag) / 1000.0;
+	if (us3.imag == 0)
+	    st->xmag[2] = 99.990;
+	else
+	    st->xmag[2] = ((double) us3.imag) / 1000.0;
+	if (us3.jmag == 0)
+	    st->xmag[3] = 99.990;
+	else
+	    st->xmag[3] = ((double) us3.jmag) / 1000.0;
+	if (us3.hmag == 0)
+	    st->xmag[4] = 99.990;
+	else
+	    st->xmag[4] = ((double) us3.hmag) / 1000.0;
+	if (us3.kmag == 0)
+	    st->xmag[5] = 99.990;
+	else
+	    st->xmag[5] = ((double) us3.kmag) / 1000.0;
+	if (us3.mmag == 0)
+	    st->xmag[6] = 99.990;
+	else
+	    st->xmag[6] = ((double) us3.mmag) / 1000.0;
+	if (us3.amag == 0)
+	    st->xmag[7] = 99.990;
+	else
+	    st->xmag[7] = ((double) us3.amag) / 1000.0;
+	}
+
+    return (0);
+}
+
+
+/* UCACSWAP2 -- Swap bytes in Integer*2 number in place */
+
+static void
+ucacswap2 (string)
+
+
+char *string;	/* Address of starting point of bytes to swap */
+
+{
+    char *sbyte, temp;
+
+    sbyte = string;
+    temp = sbyte[0];
+    sbyte[0] = sbyte[1];
+    sbyte[1] = temp;
+    return;
+}
+
+
+/* UCACSWAP4 -- Reverse bytes of Integer*4 or Real*4 number in place */
+
+static void
+ucacswap4 (string)
+
+char *string;	/* Address of Integer*4 or Real*4 vector */
+
+{
+    char temp0, temp1, temp2, temp3;
+
+    temp3 = string[0];
+    temp2 = string[1];
+    temp1 = string[2];
+    temp0 = string[3];
+    string[0] = temp0;
+    string[1] = temp1;
+    string[2] = temp2;
+    string[3] = temp3;
+
+    return;
+}
+
+/* Apr 24 2003	New subroutines, based on ty2read.c and uacread.c
+ * May 30 2003	Add UCAC2, compute file size rather than reading it from file
+ * Jun  2 2003	Print proper motions as mas/year
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Sep 25 2003	Add ucacbin() to fill an image with sources
+ * Oct  6 2003	Update ubcread() and ubcbin() for improved RefLim()
+ * Nov 10 2003	Fix byte-swapping test in ucacopen() found by Ed Beshore
+ * Nov 18 2003	Initialize image size and bits/pixel from header in ucacbin()
+ * Dec  1 2003	Add missing tab to n=-1 header
+ * Dec 12 2003	Fix bug in wcs2pix() call in ucacbin()
+ *
+ * Jan  4 2005	Fix bug in if statement on line 626 found by Dan Katz at JPL
+ *
+ * Jun 20 2006	Drop unused variables
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ * Nov 16 2006	Fix binning
+ *
+ * Jan  8 2007	Drop unused variable in ucacbin()
+ * Jan 10 2007	Add match=1 argument to webrnum()
+ * Jul 11 2007	Add magnitude byte-swap check
+ *
+ * Oct  5 2009	Add UCAC3
+ * Oct 15 2009	Read extra stars in RA
+ * Oct 22 2009	Set UCAC3 magnitudes to 99.99 if zero
+ * Oct 30 2009	Add position and proper motion error to Star structure
+ * Nov  2 2009	Print UCAC3 errors if n = -1
+ * Nov  5 2009	Return number of images and catalogs in gtype
+ * Nov  5 2009	Return errors in position and proper motion as magnitudes
+ * Nov  5 2009	Return UCAC2 and UCAC3 RA proper motion and error as RA degrees
+ */
diff --git a/Code/src/libwcs/ujcread.c b/Code/src/libwcs/ujcread.c
new file mode 100644
index 0000000000000000000000000000000000000000..6ef05685908bb5d709c36a49c2130bb11f54f23d
--- /dev/null
+++ b/Code/src/libwcs/ujcread.c
@@ -0,0 +1,1108 @@
+/*** File libwcs/ujcread.c
+ *** July 5, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1996-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+static char cdu[64]="/data/ujcat/catalog"; /* pathname of UJ 1.0 CDROM */
+
+typedef struct {
+    int rasec, decsec, magetc;
+} UJCstar;
+
+static int nstars;	/* Number of stars in catalog */
+static int cswap = 0;	/* Byte reverse catalog to Intel/DEC order if 1 */
+static FILE *fcat;
+static int refcat;	/* Code for catalog */
+static char *catname;
+
+#define ABS(a) ((a) < 0 ? (-(a)) : (a))
+#define NZONES 24
+
+static double ujcra();
+static double ujcdec();
+static double ujcmag();
+static int ujcplate();
+static int ujczones();
+static int ujczone();
+static int ujcsra();
+static int ujcopen();
+static int ujcpath();
+static int ujcstar();
+static void ujcswap();
+
+
+/* UJCREAD -- Read USNO J Catalog stars from CDROM or plate catalog from file */
+
+int
+ujcread (refcatname,cra,cdec,dra,ddec,drad,dradi,distsort,sysout,eqout,epout,
+	 mag1,mag2,nstarmax,unum,ura,udec,umag,uplate,verbose)
+
+char    *refcatname;    /* Name of catalog (UJC, xxxxx.usno) */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	distsort;	/* 1 to sort stars by distance from center */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*unum;		/* Array of UJ numbers (returned) */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	**umag;		/* Array of magnitudes (returned) */
+int	*uplate;	/* Array of plate numbers (returned) */
+int	verbose;	/* 1 for diagnostics */
+{
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    double dist = 0.0;  /* Distance from search center in degrees */
+    double faintmag=0.0; /* Faintest magnitude */
+    double maxdist=0.0; /* Largest distance */
+    int	faintstar=0;	/* Faintest star */
+    int	farstar=0;	/* Most distant star */
+    int magsort=0;
+    double *udist;	/* Array of distances to stars */
+    int nz;		/* Number of input UJ zone files */
+    int zlist[NZONES];	/* List of input UJ zones */
+    UJCstar star;	/* UJ catalog entry for one star */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    char cstr[32];
+    double num;		/* UJ number */
+    int xplate;		/* If nonzero, use objects only from this plate */
+
+    double rra1, rra2, rdec1, rdec2;
+    int wrap, iwrap;
+    int znum, itot,iz;
+    int nlog,jstar, mprop, nmag;
+    int itable = 0;
+    int nstar, i;
+    int pass;
+    double ra,dec;
+    double mag;
+    double rdist, ddist;
+    int istar, istar1, istar2, plate;
+    int nzmax = NZONES;	/* Maximum number of declination zones */
+    char *str;
+    char title[128];
+
+    itot = 0;
+    xplate = getuplate ();
+
+    /* Set catalog code and path to catalog */
+    catname = refcatname;
+    refcat = RefCat (refcatname,title,&sysref,&eqref,&epref,&mprop,&nmag);
+    if (refcat == UJC && (str = getenv("UJ_PATH")) != NULL ) {
+
+	/* If pathname is a URL, search and return */
+	if (!strncmp (str, "http:",5)) {
+	    return (webread (str,"ujc",distsort,cra,cdec,dra,ddec,drad,dradi,
+			     sysout,eqout,epout,mag1,mag2,magsort,nstarmax,
+			     unum,ura,udec,NULL,NULL,umag,uplate,verbose));
+	    }
+	else
+	    strcpy (cdu,str);
+	}
+
+    wcscstr (cstr, sysout, eqout, epout);
+
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* mag1 is always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,0.0,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    /* Find UJ Star Catalog regions in which to search */
+    if (refcat == UJC) {
+	nz = ujczones (rra1, rra2, rdec1, rdec2, nzmax, zlist, verbose);
+	if (nz <= 0) {
+	    fprintf (stderr,"UJCREAD:  no UJ zones found\n");
+	    return (0);
+	    }
+	}
+    else
+	nz = 1;
+
+    udist = (double *) calloc (nstarmax, sizeof (double));
+
+    /* Logging interval */
+    if (verbose)
+	nlog = 100;
+    else
+	nlog = 0;
+    nstar = 0;
+
+    /* Loop through region list */
+    for (iz = 0; iz < nz; iz++) {
+
+	/* Get path to zone catalog */
+	znum = zlist[iz];
+	if ((nstars = ujcopen (znum)) != 0) {
+
+	    jstar = 0;
+	    itable = 0;
+	    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+		/* Find first star based on RA */
+		if (iwrap == 0 || wrap == 0)
+		    istar1 = ujcsra (rra1);
+		else
+		    istar1 = 1;
+
+		/* Find last star based on RA */
+		if (iwrap == 1 || wrap == 0)
+		    istar2 = ujcsra (rra2);
+		else
+		    istar2 = nstars;
+
+		if (istar1 == 0 || istar2 == 0)
+		    break;
+
+		/* Loop through zone catalog for this region */
+		for (istar = istar1; istar <= istar2; istar++) {
+		    itable ++;
+
+		    if (ujcstar (istar, &star)) {
+			fprintf (stderr,"UJCREAD: Cannot read star %d\n", istar);
+			break;
+			}
+
+		    /* Extract selected fields if not probable duplicate */
+		    else if (star.magetc > 0) {
+			mag = ujcmag (star.magetc);	/* Magnitude */
+
+			/* Check magnitude limits */
+			pass = 1;
+			if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+			    pass = 0;
+
+			/* Check plate number */
+			plate = ujcplate (star.magetc);	/* Plate number */
+			if (xplate != 0 && plate != xplate)
+			    pass = 0;
+
+			/* Check position limits */
+			if (pass) {
+			    ra = ujcra (star.rasec);	/* RA in degrees */
+			    dec = ujcdec (star.decsec);	/* Dec in degrees */
+
+			    /* Get position in output coordinate system */
+			    wcscon (sysref,sysout,eqref,eqout,&ra,&dec,epout);
+
+			    /* Compute distance from search center */
+			    if (drad > 0 || distsort)
+				dist = wcsdist (cra,cdec,ra,dec);
+			    else
+				dist = 0.0;
+
+			    /* Check radial distance to search center */
+			    if (drad > 0) {
+				if (dist > drad)
+				    pass = 0;
+				if (dradi > 0.0 && dist < dradi)
+				    pass = 0;
+				}
+
+			    /* Check distance along RA and Dec axes */
+			    else {
+				ddist = wcsdist (cra,cdec,cra,dec);
+				if (ddist > ddec)
+				    pass = 0;
+				rdist = wcsdist (cra,dec,ra,dec);
+			        if (rdist > dra)
+				   pass = 0;
+				}
+			    }
+
+			if (pass) {
+			    if (refcat == UJC)
+				num = (double) znum + (0.0000001*(double)istar);
+			    else
+				num = (double)istar;
+
+			    /* Save star position and magnitude in table */
+			    if (nstar < nstarmax) {
+				unum[nstar] = num;
+				ura[nstar] = ra;
+				udec[nstar] = dec;
+				umag[0][nstar] = mag;
+				uplate[nstar] = plate;
+				udist[nstar] = dist;
+				if (dist > maxdist) {
+				    maxdist = dist;
+				    farstar = nstar;
+				    }
+				if (mag > faintmag) {
+				    faintmag = mag;
+				    faintstar = nstar;
+				    }
+				}
+
+			    /* If too many stars and distance sorting,
+				replace furthest star */
+			    else if (distsort) {
+				if (dist < maxdist) {
+				    unum[farstar] = num;
+				    ura[farstar] = ra;
+				    udec[farstar] = dec;
+				    umag[0][farstar] = mag;
+				    uplate[farstar] = plate;
+				    udist[farstar] = dist;
+
+				/* Find new farthest star */
+				    maxdist = 0.0;
+				    for (i = 0; i < nstarmax; i++) {
+					if (udist[i] > maxdist) {
+					    maxdist = udist[i];
+					    farstar = i;
+					    }
+					}
+				    }
+				}
+
+			    /* If too many stars, replace faintest star */
+			    else if (mag < faintmag) {
+				unum[faintstar] = num;
+				ura[faintstar] = ra;
+				udec[faintstar] = dec;
+				umag[0][faintstar] = mag;
+				uplate[faintstar] = plate;
+				udist[faintstar] = dist;
+				faintmag = 0.0;
+
+				/* Find new faintest star */
+				for (i = 0; i < nstarmax; i++) {
+				    if (umag[0][i] > faintmag) {
+					faintmag = umag[0][i];
+					faintstar = i;
+					}
+				    }
+				}
+			    nstar++;
+			    jstar++;
+			    if (nlog == 1)
+				fprintf (stderr,"UJCREAD: %04d.%04d: %9.5f %9.5f %s %5.2f\n",
+				    znum,istar,ra,dec,cstr,mag);
+
+			    /* End of accepted star processing */
+			    }
+
+			/* End of individual star processing */
+			}
+
+		    /* Log operation */
+		    if (nlog > 0 && itable%nlog == 0)
+			fprintf (stderr,"UJCREAD: zone %d (%4d / %4d) %6d / %6d sources\r",
+				znum, iz+1, nz, jstar, itable);
+
+		    /* End of star loop */
+		    }
+
+		/* End of wrap loop */
+		}
+
+	/* Close zone input file */
+	    (void) fclose (fcat);
+	    itot = itot + itable;
+	    if (nlog > 0)
+		fprintf (stderr,"UJCREAD: zone %d (%4d / %4d) %6d / %6d / %8d sources\n",
+			znum, iz+1, nz, jstar, itable, nstars);
+
+	/* End of zone processing */
+	    }
+
+    /* End of zone loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0) {
+	if (nz > 1)
+	    fprintf (stderr,"UJCREAD: %d zone: %d / %d found\n",nz,nstar,itot);
+	else
+	    fprintf (stderr,"UJCREAD: 1 zone: %d / %d found\n",nstar,itable);
+	if (nstar > nstarmax)
+	    fprintf (stderr,"UJCREAD: %d stars found; only %d returned\n",
+		     nstar,nstarmax);
+	}
+    free ((char *)udist);
+    return (nstar);
+}
+
+/* UJCRNUM -- Read USNO J Catalog stars from CDROM or plate catalog from file */
+
+int
+ujcrnum (refcatname,nnum,sysout,eqout,epout,unum,ura,udec,umag,uplate,nlog)
+
+char    *refcatname;    /* Name of catalog (UAC, USAC, UAC2, USAC2) */
+int	nnum;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*unum;		/* Array of UA numbers to find */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	**umag;		/* Array of red magnitudes (returned) */
+int	*uplate;	/* Array of plate numbers (returned) */
+int	nlog;		/* Logging interval */
+{
+    UJCstar star;	/* UJ catalog entry for one star */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+
+    int znum;
+    int jnum;
+    int nzone;
+    int nfound = 0;
+    double ra,dec;
+    double mag;
+    int istar, plate, mprop, nmag;
+    char *str;
+    char title[128];
+
+    /* Set catalog code and path to catalog */
+    catname = refcatname;
+    refcat = RefCat (refcatname,title,&sysref,&eqref,&epref,&mprop,&nmag);
+
+    if (refcat == UJC && (str = getenv("UJ_PATH")) != NULL ) {
+
+	/* If pathname is a URL, search and return */
+	if (!strncmp (str, "http:",5)) {
+	    return (webrnum (str,"ujc",nnum,sysout,eqout,epout,1,
+			     unum,ura,udec,NULL,NULL,umag,uplate,nlog));
+	    }
+	else
+	    strcpy (cdu,str);
+	}
+
+/* Loop through star list */
+    for (jnum = 0; jnum < nnum; jnum++) {
+
+    /* Get path to zone catalog */
+	znum = (int) unum[jnum];
+	if ((nzone = ujcopen (znum)) != 0) {
+
+	    if (refcat == UJC)
+		istar = (int) (((unum[jnum] - znum) * 100000000.0) + 0.5);
+	    else
+		istar = (int) (unum[jnum] + 0.5);
+
+	/* Check to make sure star can be in this zone */
+	    if (istar > nzone) {
+		fprintf (stderr,"UJCRNUM: Star %d > zone max. %d\n",
+			 istar, nzone);
+		break;
+		}
+
+	/* Read star entry from catalog */
+	    if (ujcstar (istar, &star)) {
+		fprintf (stderr,"UJCRNUM: Cannot read star %d\n", istar);
+		break;
+		}
+
+	    /* Extract selected fields if not probable duplicate */
+	    else if (star.magetc > 0) {
+		ra = ujcra (star.rasec); /* Right ascension in degrees */
+		dec = ujcdec (star.decsec); /* Declination in degrees */
+		wcscon (sysref, sysout, eqref, eqout, &ra, &dec, epout);
+		mag = ujcmag (star.magetc); /* Magnitude */
+		plate = ujcplate (star.magetc);	/* Plate number */
+
+		/* Save star position and magnitude in table */
+		ura[nfound] = ra;
+		udec[nfound] = dec;
+		umag[0][nfound] = mag;
+		uplate[nfound] = plate;
+
+		nfound++;
+		if (nlog == 1)
+		    fprintf (stderr,"UJCRNUM: %04d.%08d: %9.5f %9.5f %5.2f\n",
+			     znum,istar,ra,dec,mag);
+
+		/* Log operation */
+		if (nlog > 0 && jnum%nlog == 0)
+		    fprintf (stderr,"UJCRNUM: %04d.%08d  %8d / %8d sources\r",
+			     znum, istar, jnum, nnum);
+
+		(void) fclose (fcat);
+		/* End of star processing */
+		}
+
+	    /* End of star */
+	    }
+
+	/* End of star loop */
+	}
+
+    /* Summarize search */
+    if (nlog > 0)
+	fprintf (stderr,"UJCRNUM:  %d / %d found\n",nfound,nnum);
+
+    return (nfound);
+}
+
+
+/* UJCBIN -- Fill a FITS WCS image with USNO J Catalog stars */
+
+int
+ujcbin (refcatname, wcs, header, image, mag1,mag2, magscale, verbose)
+
+char    *refcatname;    /* Name of catalog (UJC, xxxxx.usno) */
+struct WorldCoor *wcs;	/* World coordinate system for image */
+char	*header;	/* FITS header for output image */
+char	*image;		/* Output FITS image */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+double	magscale;	/* Scaling factor for magnitude to pixel flux
+			 * (number of catalog objects per bin if 0) */
+int	verbose;	/* 1 for diagnostics */
+{
+    double cra;		/* Search center J2000 right ascension in degrees */
+    double cdec;	/* Search center J2000 declination in degrees */
+    double dra;		/* Search half width in right ascension in degrees */
+    double ddec;	/* Search half-width in declination in degrees */
+    int sysout;		/* Search coordinate system */
+    double eqout;	/* Search coordinate equinox */
+    double epout;	/* Proper motion epoch (0.0 for no proper motion) */
+    double ra1,ra2;	/* Limiting right ascensions of region in degrees */
+    double dec1,dec2;	/* Limiting declinations of region in degrees */
+    int nz;		/* Number of input UJ zone files */
+    int zlist[NZONES];	/* List of input UJ zones */
+    UJCstar star;	/* UJ catalog entry for one star */
+    int sysref=WCS_J2000;	/* Catalog coordinate system */
+    double eqref=2000.0;	/* Catalog equinox */
+    double epref=2000.0;	/* Catalog epoch */
+    char cstr[32];
+    int xplate;		/* If nonzero, use objects only from this plate */
+
+    double rra1, rra2, rdec1, rdec2;
+    int wrap, iwrap;
+    int znum, itot,iz;
+    int nlog,jstar, mprop, nmag;
+    int itable = 0;
+    int nstar;
+    int pass;
+    int ix, iy;
+    double ra,dec;
+    double mag;
+    double rdist, ddist;
+    int istar, istar1, istar2, plate;
+    int nzmax = NZONES;	/* Maximum number of declination zones */
+    char *str;
+    char title[128];
+    double xpix, ypix, flux;
+    int offscl;
+    int bitpix, w, h;   /* Image bits/pixel and pixel width and height */
+    double logt = log(10.0);
+
+    itot = 0;
+    xplate = getuplate ();
+
+    /* Set image parameters */
+    bitpix = 0;
+    (void)hgeti4 (header, "BITPIX", &bitpix);
+    w = 0;
+    (void)hgeti4 (header, "NAXIS1", &w);
+    h = 0;
+    (void)hgeti4 (header, "NAXIS2", &h);
+
+    /* Set catalog code and path to catalog */
+    catname = refcatname;
+    refcat = RefCat (refcatname,title,&sysref,&eqref,&epref,&mprop,&nmag);
+    if (refcat == UJC && (str = getenv("UJ_PATH")) != NULL )
+	strcpy (cdu,str);
+
+    /* Set catalog search limits from image WCS information */
+    sysout = wcs->syswcs;
+    eqout = wcs->equinox;
+    epout = wcs->epoch;
+    wcscstr (cstr, sysout, eqout, epout);
+    wcssize (wcs, &cra, &cdec, &dra, &ddec);
+    SearchLim (cra,cdec,dra,ddec,sysout,&ra1,&ra2,&dec1,&dec2,verbose);
+
+    /* mag1 is always the smallest magnitude */
+    if (mag2 < mag1) {
+	mag = mag2;
+	mag2 = mag1;
+	mag1 = mag;
+	}
+
+    rra1 = ra1;
+    rra2 = ra2;
+    rdec1 = dec1;
+    rdec2 = dec2;
+    RefLim (cra,cdec,dra,ddec,sysout,sysref,eqout,eqref,epout,epref,0.0,
+	    &rra1, &rra2, &rdec1, &rdec2, &wrap, verbose);
+
+    /* Find UJ Star Catalog regions in which to search */
+    if (refcat == UJC) {
+	nz = ujczones (rra1, rra2, rdec1, rdec2, nzmax, zlist, verbose);
+	if (nz <= 0) {
+	    fprintf (stderr,"UJCBIN:  no UJ zones found\n");
+	    return (0);
+	    }
+	}
+    else
+	nz = 1;
+
+    /* Logging interval */
+    if (verbose)
+	nlog = 100;
+    else
+	nlog = 0;
+    nstar = 0;
+
+    /* Loop through region list */
+    for (iz = 0; iz < nz; iz++) {
+
+	/* Get path to zone catalog */
+	znum = zlist[iz];
+	if ((nstars = ujcopen (znum)) != 0) {
+
+	    jstar = 0;
+	    itable = 0;
+	    for (iwrap = 0; iwrap <= wrap; iwrap++) {
+
+		/* Find first star based on RA */
+		if (iwrap == 0 || wrap == 0)
+		    istar1 = ujcsra (rra1);
+		else
+		    istar1 = 1;
+
+		/* Find last star based on RA */
+		if (iwrap == 1 || wrap == 0)
+		    istar2 = ujcsra (rra2);
+		else
+		    istar2 = nstars;
+
+		if (istar1 == 0 || istar2 == 0)
+		    break;
+
+		/* Loop through zone catalog for this region */
+		for (istar = istar1; istar <= istar2; istar++) {
+		    itable ++;
+
+		    if (ujcstar (istar, &star)) {
+			fprintf (stderr,"UJCBIN: Cannot read star %d\n", istar);
+			break;
+			}
+
+		    /* Extract selected fields if not probable duplicate */
+		    else if (star.magetc > 0) {
+			mag = ujcmag (star.magetc);	/* Magnitude */
+
+			/* Check magnitude limits */
+			pass = 1;
+			if (mag1 != mag2 && (mag < mag1 || mag > mag2))
+			    pass = 0;
+
+			/* Check plate number */
+			plate = ujcplate (star.magetc);	/* Plate number */
+			if (xplate != 0 && plate != xplate)
+			    pass = 0;
+
+			/* Check position limits */
+			if (pass) {
+			    ra = ujcra (star.rasec);	/* RA in degrees */
+			    dec = ujcdec (star.decsec);	/* Dec in degrees */
+
+			    /* Get position in output coordinate system */
+			    wcscon (sysref,sysout,eqref,eqout,&ra,&dec,epout);
+
+			    /* Check distance along RA and Dec axes */
+			    ddist = wcsdist (cra,cdec,cra,dec);
+			    if (ddist > ddec)
+				pass = 0;
+			    rdist = wcsdist (cra,dec,ra,dec);
+			    if (rdist > dra)
+				pass = 0;
+			    }
+
+			/* Save star in FITS image */
+			if (pass) {
+			    wcs2pix (wcs, ra, dec, &xpix, &ypix, &offscl);
+			    if (!offscl) {
+				if (magscale > 0.0)
+				    flux = magscale * exp (logt * (-mag / 2.5));
+				else
+				    flux = 1.0;
+				ix = (int) (xpix + 0.5);
+				iy = (int) (ypix + 0.5);
+				addpix1 (image, bitpix, w,h, 0.0,1.0, xpix,ypix, flux);
+				nstar++;
+				jstar++;
+				}
+			    else {
+				ix = 0;
+				iy = 0;
+				}
+			    if (nlog == 1) {
+				fprintf (stderr,"UJCBIN: %04d.%04d: %9.5f %9.5f %s",
+					 znum, istar,ra,dec,cstr);
+				if (magscale > 0.0)
+				    fprintf (stderr, " %5.2f", mag);
+				if (!offscl)
+				    flux = getpix1 (image, bitpix, w, h, 0.0, 1.0, ix, iy);
+				else
+				    flux = 0.0;
+				fprintf (stderr," (%d,%d): %f\n", ix, iy, flux);
+				}
+
+			    /* End of accepted star processing */
+			    }
+
+			/* End of individual star processing */
+			}
+
+		    /* Log operation */
+		    if (nlog > 0 && itable%nlog == 0)
+			fprintf (stderr,"UJCBIN: zone %d (%4d / %4d) %6d / %6d sources\r",
+				znum, iz+1, nz, jstar, itable);
+
+		    /* End of star loop */
+		    }
+
+		/* End of wrap loop */
+		}
+
+	/* Close zone input file */
+	    (void) fclose (fcat);
+	    itot = itot + itable;
+	    if (nlog > 0)
+		fprintf (stderr,"UJCBIN: zone %d (%4d / %4d) %6d / %6d / %8d sources\n",
+			znum, iz+1, nz, jstar, itable, nstars);
+
+	/* End of zone processing */
+	    }
+
+    /* End of zone loop */
+	}
+
+/* Summarize search */
+    if (nlog > 0) {
+	if (nz > 1)
+	    fprintf (stderr,"UJCBIN: %d zone: %d / %d found\n",nz,nstar,itot);
+	else
+	    fprintf (stderr,"UJCBIN: 1 zone: %d / %d found\n",nstar,itable);
+	}
+    return (nstar);
+}
+
+
+/* Declination zone numbers */
+int zone[NZONES]={0,75,150,225,300,375,450,525,600,675,750,825,900,
+	      975,1050,1125,1200,1275,1350,1425,1500,1575,1650,1725};
+
+/* UJCZONES -- figure out which UJ zones will need to be searched */
+
+static int
+ujczones (ra1, ra2, dec1, dec2, nzmax, zones, verbose)
+
+double	ra1, ra2;	/* Right ascension limits in degrees */
+double	dec1, dec2; 	/* Declination limits in degrees */
+int	nzmax;		/* Maximum number of zones to find */
+int	*zones;		/* Region numbers (returned)*/
+int	verbose;	/* 1 for diagnostics */
+
+{
+    int nrgn;		/* Number of zones found (returned) */
+    int iz,iz1,iz2,i;
+
+    for (i = 0; i < nzmax; i++)
+	zones[i] = 0;
+
+    nrgn = 0;
+
+/* Find zone range to search based on declination */
+    iz1 = ujczone (dec1);
+    iz2 = ujczone (dec2);
+
+/* Tabulate zones to search */
+    i = 0;
+    if (iz2 >= iz1) {
+	for (iz = iz1; iz <= iz2; iz++)
+	    zones[i++] = zone[iz];
+	}
+    else {
+	for (iz = iz2; iz <= iz1; iz++)
+	    zones[i++] = zone[iz];
+	}
+
+    nrgn = i;
+    if (verbose) {
+	fprintf (stderr,"UJCREG:  %d zones: %d - %d\n",nrgn,zones[0],zones[i-1]);
+	fprintf(stderr,"UJCREG: RA: %.5f - %.5f, Dec: %.5f - %.5f\n",ra1,ra2,dec1,dec2);
+	}
+
+    return (nrgn);
+}
+
+
+/* UJCRA -- returns right ascension in degrees from the UJ star structure */
+
+static double
+ujcra (rasec)
+
+int rasec;	/* RA in 100ths of arcseconds from UJ catalog entry */
+{
+    return ((double) (rasec) / 360000.0);
+}
+
+
+/* UJCDEC -- returns the declination in degrees from the UJ star structure */
+
+static double
+ujcdec (decsec)
+
+int decsec;	/* Declination in 100ths of arcseconds from UJ catalog entry */
+{
+    return ((double) (decsec - 32400000) / 360000.0);
+}
+
+
+/* UJCMAG -- returns the magnitude from the UJ star structure */
+
+static double
+ujcmag (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UJ catalog entry */
+{
+    if (magetc < 0)
+	return ((double) (-magetc % 10000) * 0.01);
+    else
+	return ((double) (magetc % 10000) * 0.01);
+}
+
+
+/* UJCPLATE -- returns the plate number from the UJ star structure */
+
+static int
+ujcplate (magetc)
+
+int magetc;	/* Quality, plate, and magnitude from UJ catalog entry */
+{
+    if (magetc < 0)
+	return ( (-magetc % 10000000) / 10000);
+    else
+	return ( (magetc % 10000000) / 10000);
+}
+
+
+/* UJCZONE -- find the UJ zone number where a declination can be found */
+
+static int
+ujczone (dec)
+
+double dec;	/* declination in degrees */
+{
+    double zonesize = 7.5;	/* number of degrees per declination zone */
+
+    return ((int) ((dec + 90.0) / zonesize));
+}
+
+
+/* UJCSRA -- Find UJ star closest to specified right ascension */
+
+static int
+ujcsra (rax0)
+
+double	rax0;		/* Right ascension for which to search */
+{
+    int istar, istar1, istar2, nrep;
+    double rax, ra1, ra, rdiff, rdiff1, rdiff2, sdiff;
+    UJCstar star;	/* UJ catalog entry for one star */
+    char rastrx[32];
+    int debug = 0;
+
+    rax = rax0;
+    ra2str (rastrx, 31, rax, 3);
+    istar1 = 1;
+    if (ujcstar (istar1, &star))
+	return (0);
+    ra1 = ujcra (star.rasec);
+    istar = nstars;
+    nrep = 0;
+    while (istar != istar1 && nrep < 20) {
+	if (ujcstar (istar, &star))
+	    break;
+	else {
+	    ra = ujcra (star.rasec);
+	    if (ra == ra1)
+		break;
+	    if (debug) {
+		char rastr[32];
+		ra2str (rastr, 31, ra, 3);
+		fprintf (stderr,"UJCSRA %d %d: %s (%s)\n",
+			 nrep,istar,rastr,rastrx);
+		}
+	    rdiff = ra1 - ra;
+	    rdiff1 = ra1 - rax;
+	    rdiff2 = ra - rax;
+	    if (nrep > 20 && ABS(rdiff2) > ABS(rdiff1)) {
+		istar = istar1;
+		break;
+		}
+	    nrep++;
+	    sdiff = (double)(istar - istar1) * rdiff1 / rdiff;
+	    istar2 = istar1 + (int) (sdiff + 0.5);
+	    ra1 = ra;
+	    istar1 = istar;
+	    istar = istar2;
+	    if (debug) {
+		fprintf (stderr,"UJCSRA: ra1=    %.5f ra=     %.5f rax=    %.5f\n",
+			 ra1,ra,rax);
+		fprintf (stderr,"UJCSRA: rdiff=  %.5f rdiff1= %.5f rdiff2= %.5f\n",
+			 rdiff,rdiff1,rdiff2);
+		fprintf (stderr,"UJCSRA: istar1= %d istar= %d istar1= %d\n",
+			 istar1,istar,istar2);
+		}
+	    if (istar < 1)
+		istar = 1;
+	    if (istar > nstars)
+		istar = nstars;
+	    if (istar == istar1)
+		break;
+	    }
+	}
+    return (istar);
+}
+
+/* UJCOPEN -- Open UJ Catalog zone catalog, returning number of entries */
+
+static int
+ujcopen (znum)
+
+int znum;	/* UJ Catalog zone */
+{
+    char zonepath[128];	/* Pathname for input UJ zone file */
+    UJCstar star;	/* UJ catalog entry for one star */
+    int lfile;
+
+/* Get path to zone catalog */
+    if (ujcpath (znum, zonepath)) {
+	fprintf (stderr, "UJCOPEN: Cannot find zone catalog for %d\n", znum);
+	return (0);
+	}
+
+/* Find number of stars in zone catalog by its length */
+    lfile = getfilesize (zonepath);
+    if (lfile < 2) {
+	fprintf (stderr,"UJCOPEN: Zone catalog %s has no entries\n",zonepath);
+	return (0);
+	}
+    else
+	nstars = lfile / 12;
+
+/* Open zone catalog */
+    if (!(fcat = fopen (zonepath, "rb"))) {
+	fprintf (stderr,"UJCOPEN: Zone catalog %s cannot be read\n",zonepath);
+	return (0);
+	}
+
+/* Check to see if byte-swapping is necessary */
+    cswap = 0;
+    if (ujcstar (1, &star)) {
+	fprintf (stderr,"UJCOPEN: cannot read star 1 from UJ zone catalog %s\n",
+		 zonepath);
+	return (0);
+	}
+    else {
+	if (star.decsec < 0) {
+	    cswap = 1;
+	    fprintf (stderr,"UJCOPEN: swapping bytes in UJ zone catalog %s\n",
+		     zonepath);
+	    }
+	else
+	    cswap = 0;
+	}
+
+    return (nstars);
+}
+
+
+/* UJCPATH -- Get UJ Catalog region file pathname */
+
+static int
+ujcpath (zn, path)
+
+int zn;		/* UJ zone number */
+char *path;	/* Pathname of UJ zone file */
+
+{
+    if (refcat == USNO) {
+	strcpy (path, catname);
+	return (0);
+	}
+    else if (zn < 0 || zn > 1725) {
+	fprintf (stderr, "UJCPATH: zone %d out of range 0-1725\n",zn);
+	return (-1);
+	}
+    else if (strchr (cdu,'C'))
+	sprintf (path,"%s/ZONE%04d.CAT", cdu, zn);
+    else
+	sprintf (path,"%s/zone%04d.cat", cdu, zn);
+
+    return (0);
+}
+
+
+/* UJCSTAR -- Get UJ catalog entry for one star; return 0 if successful */
+
+static int
+ujcstar (istar, star)
+
+int istar;	/* Star sequence number in UJ zone catalog */
+UJCstar *star;	/* UJ catalog entry for one star */
+{
+    int nbs, nbr, nbskip;
+
+    if (istar < 1 || istar > nstars) {
+	fprintf (stderr, "UJCstar %d is not in catalog\n",istar);
+	return (-1);
+	}
+    nbskip = 12 * (istar - 1);
+    if (fseek (fcat,nbskip,SEEK_SET))
+	return (-1);
+    nbs = sizeof (UJCstar);
+    nbr = fread (star, nbs, 1, fcat) * nbs;
+    if (nbr < nbs) {
+	fprintf (stderr, "UJCstar %d / %d bytes read\n",nbr, nbs);
+	return (-2);
+	}
+    if (cswap)
+	ujcswap ((char *)star);
+    return (0);
+}
+
+
+/* UJCSWAP -- Reverse bytes of UJ Catalog entry */
+
+static void
+ujcswap (string)
+
+char *string;	/* Start of vector of 4-byte ints */
+
+{
+char *sbyte, *slast;
+char temp0, temp1, temp2, temp3;
+int nbytes = 12; /* Number of bytes to reverse */
+
+    slast = string + nbytes;
+    sbyte = string;
+    while (sbyte < slast) {
+	temp3 = sbyte[0];
+	temp2 = sbyte[1];
+	temp1 = sbyte[2];
+	temp0 = sbyte[3];
+	sbyte[0] = temp0;
+	sbyte[1] = temp1;
+	sbyte[2] = temp2;
+	sbyte[3] = temp3;
+	sbyte = sbyte + 4;
+	}
+    return;
+}
+
+/* May 20 1996	New subroutine
+ * May 23 1996	Add optional plate number check
+ * Aug  6 1996	Remove unused variables after lint
+ * Oct 15 1996	Add comparison when testing an assignment
+ * Oct 17 1996	Fix after lint: cast argument to UJCSWAP
+ * Nov 13 1996	Return no more than maximum star number
+ * Nov 13 1996	Write all error messages to stderr with subroutine names
+ * Nov 15 1996  Implement search radius; change input arguments
+ * Dec 12 1996	Improve logging
+ * Dec 17 1996	Add code to keep brightest or closest stars if too many found
+ * Dec 18 1996	Add code to read a specific star
+ * Mar 20 1997	Clean up UJCRNUM after lint
+ *
+ * Jun 24 1998	Add string lengths to ra2str() and dec2str() calls
+ * Jun 24 1998	Initialize byte-swapping flag in UJCOPEN()
+ * Sep 22 1998	Convert to desired output coordinate system
+ * Oct 29 1998	Correctly assign numbers when too many stars are found
+ *
+ * Jun 16 1999	Use SearchLim()
+ * Aug 16 1999	Add RefLim() to get converted search coordinates right
+ * Aug 16 1999  Fix bug to fix failure to search across 0:00 RA
+ * Aug 25 1999  Return real number of stars from ujcread()
+ * Aug 26 1999	Set nlog to 100 if verbose mode is on
+ * Sep 10 1999	Set plate selection with subroutine, not argument
+ * Sep 16 1999	Fix bug which didn't always return closest stars
+ * Sep 16 1999	Add distsort argument so brightest stars in circle works, too
+ * Oct 20 1999	Include wcscat.h
+ * Oct 21 1999	Delete unused variables after lint
+ *
+ * Jun 26 2000	Add coordinate system to SearchLim() arguments
+ * Oct 25 2000	Add USNO plate catalogs; fix byte order test
+ * Nov 29 2000	Add option to read UJ catalog using HTTP
+ *
+ * Jan 11 2001	All printing is to stderr
+ * Jun  7 2001	Add proper motion flag and number of magnitudes to RefCat()
+ * Sep 19 2001	Drop fitshead.h; it is in wcs.h
+ *
+ * Feb  4 2003	Open catalog file rb instead of r (Martin Ploner, Bern)
+ * Mar 11 2003	Improve position filtering
+ * May 27 2003	Use getfilesize() to get file length
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Sep 25 2003	Add ujcbin() to fill an image with sources
+ * Oct  6 2003	Update ujcread() and ujcbin() for improved RefLim()
+ * Nov 18 2003	Initialize image size and bits/pixel from header in ujcbin()
+ * Dec 12 2003	Fix bug in wcs2pix() call in ujcbin()
+ *
+ * Aug 30 2004	Include fitsfile.h and math.h
+ *
+ * Jun 20 2006	Initialize uninitialized variables
+ * Sep 26 2006	Increase length of rastr and destr from 16 to 32
+ * Nov 16 2006	Fix binning
+ *
+ * Jan  8 2007	Fix bad format statement in ujcbin()
+ * Jan 10 2007	Add match=1 argument to webrnum()
+ * Jul  5 2007	Fix bug in ujcread() and ujcbin() which always rejected stars
+ */
diff --git a/Code/src/libwcs/wcs.c b/Code/src/libwcs/wcs.c
new file mode 100644
index 0000000000000000000000000000000000000000..67e40cfa21657fdf83587eca499891dde6cf19d8
--- /dev/null
+++ b/Code/src/libwcs/wcs.c
@@ -0,0 +1,2937 @@
+/*** File libwcs/wcs.c
+ *** July 25, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1994-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	wcs.c (World Coordinate Systems)
+ * Purpose:	Convert FITS WCS to pixels and vice versa:
+ * Subroutine:	wcsxinit (cra,cdec,secpix,xrpix,yrpix,nxpix,nypix,rotate,equinox,epoch,proj)
+ *		sets a WCS structure from arguments
+ * Subroutine:	wcskinit (nxpix,nypix,ctype1,ctype2,crpix1,crpix2,crval1,crval2,
+		cd,cdelt1,cdelt2,crota,equinox,epoch)
+ *		sets a WCS structure from keyword-based arguments
+ * Subroutine:	wcsreset (wcs,crpix1,crpix2,crval1,crval2,cdelt1,cdelt2,crota,cd, equinox)
+ *		resets an existing WCS structure from arguments
+ * Subroutine:	wcsdeltset (wcs,cdelt1,cdelt2,crota) sets rotation and scaling
+ * Subroutine:	wcscdset (wcs, cd) sets rotation and scaling from a CD matrix
+ * Subroutine:	wcspcset (wcs,cdelt1,cdelt2,pc) sets rotation and scaling
+ * Subroutine:	wcseqset (wcs, equinox) resets an existing WCS structure to new equinox
+ * Subroutine:	iswcs(wcs) returns 1 if WCS structure is filled, else 0
+ * Subroutine:	nowcs(wcs) returns 0 if WCS structure is filled, else 1
+ * Subroutine:	wcscent (wcs) prints the image center and size in WCS units
+ * Subroutine:	wcssize (wcs, cra, cdec, dra, ddec) returns image center and size
+ * Subroutine:	wcsfull (wcs, cra, cdec, width, height) returns image center and size
+ * Subroutine:	wcsrange (wcs, ra1, ra2, dec1, dec2) returns image coordinate limits
+
+ * Subroutine:	wcsshift (wcs,cra,cdec) resets the center of a WCS structure
+ * Subroutine:	wcsdist (x1,y1,x2,y2) compute angular distance between ra/dec or lat/long
+ * Subroutine:	wcsdiff (x1,y1,x2,y2) compute angular distance between ra/dec or lat/long
+ * Subroutine:	wcscominit (wcs,command) sets up a command format for execution by wcscom
+ * Subroutine:	wcsoutinit (wcs,coor) sets up the coordinate system used by pix2wcs
+ * Subroutine:	getwcsout (wcs) returns current output coordinate system used by pix2wcs
+ * Subroutine:	wcsininit (wcs,coor) sets up the coordinate system used by wcs2pix
+ * Subroutine:	getwcsin (wcs) returns current input coordinate system used by wcs2pix
+ * Subroutine:	setwcsdeg(wcs, new) sets WCS output in degrees or hh:mm:ss
+ * Subroutine:	getradecsys(wcs) returns current coordinate system type
+ * Subroutine:	wcscom (wcs,file,x,y,wcstr) executes a command using the current world coordinates
+ * Subroutine:	setwcslin (wcs, mode) sets output string mode for LINEAR
+ * Subroutine:	pix2wcst (wcs,xpix,ypix,wcstring,lstr) pixels -> sky coordinate string
+ * Subroutine:	pix2wcs (wcs,xpix,ypix,xpos,ypos) pixel coordinates -> sky coordinates
+ * Subroutine:	wcsc2pix (wcs,xpos,ypos,coorsys,xpix,ypix,offscl) sky coordinates -> pixel coordinates
+ * Subroutine:	wcs2pix (wcs,xpos,ypos,xpix,ypix,offscl) sky coordinates -> pixel coordinates
+ * Subroutine:  wcszin (izpix) sets third dimension for pix2wcs() and pix2wcst()
+ * Subroutine:  wcszout (wcs) returns third dimension from wcs2pix()
+ * Subroutine:	setwcsfile (filename)  Set file name for error messages 
+ * Subroutine:	setwcserr (errmsg)  Set error message 
+ * Subroutine:	wcserr()  Print error message 
+ * Subroutine:	setdefwcs (wcsproj)  Set flag to choose AIPS or WCSLIB WCS subroutines 
+ * Subroutine:	getdefwcs()  Get flag to switch between AIPS and WCSLIB subroutines 
+ * Subroutine:	savewcscoor (wcscoor)
+ * Subroutine:	getwcscoor()  Return preset output default coordinate system 
+ * Subroutine:	savewcscom (i, wcscom)  Save specified WCS command 
+ * Subroutine:	setwcscom (wcs)  Initialize WCS commands 
+ * Subroutine:	getwcscom (i)  Return specified WCS command 
+ * Subroutine:	wcsfree (wcs)  Free storage used by WCS structure
+ * Subroutine:	freewcscom (wcs)  Free storage used by WCS commands 
+ * Subroutine:  cpwcs (&header, cwcs)
+ */
+
+#include <string.h>		/* strstr, NULL */
+#include <stdio.h>		/* stderr */
+#include <math.h>
+#include "wcs.h"
+#ifndef VMS
+#include <stdlib.h>
+#endif
+
+static char wcserrmsg[80];
+static char wcsfile[256]={""};
+static void wcslibrot();
+void wcsrotset();
+static int wcsproj0 = 0;
+static int izpix = 0;
+static double zpix = 0.0;
+
+void
+wcsfree (wcs)
+struct WorldCoor *wcs;	/* WCS structure */
+{
+    if (nowcs (wcs)) {
+
+	/* Free WCS structure if allocated but not filled */
+	if (wcs)
+	    free (wcs);
+
+	return;
+	}
+
+    freewcscom (wcs);
+    if (wcs->wcsname != NULL)
+	free (wcs->wcsname);
+    if (wcs->lin.imgpix != NULL)
+	free (wcs->lin.imgpix);
+    if (wcs->lin.piximg != NULL)
+	free (wcs->lin.piximg);
+    free (wcs);
+    return;
+}
+
+/* Set up a WCS structure from subroutine arguments */
+
+struct WorldCoor *
+wcsxinit (cra,cdec,secpix,xrpix,yrpix,nxpix,nypix,rotate,equinox,epoch,proj)
+
+double	cra;	/* Center right ascension in degrees */
+double	cdec;	/* Center declination in degrees */
+double	secpix;	/* Number of arcseconds per pixel */
+double	xrpix;	/* Reference pixel X coordinate */
+double	yrpix;	/* Reference pixel X coordinate */
+int	nxpix;	/* Number of pixels along x-axis */
+int	nypix;	/* Number of pixels along y-axis */
+double	rotate;	/* Rotation angle (clockwise positive) in degrees */
+int	equinox; /* Equinox of coordinates, 1950 and 2000 supported */
+double	epoch;	/* Epoch of coordinates, used for FK4/FK5 conversion
+		 * no effect if 0 */
+char	*proj;	/* Projection */
+
+{
+    struct WorldCoor *wcs;
+    double cdelt1, cdelt2;
+
+    wcs = (struct WorldCoor *) calloc (1, sizeof(struct WorldCoor));
+
+    /* Set WCSLIB flags so that structures will be reinitialized */
+    wcs->cel.flag = 0;
+    wcs->lin.flag = 0;
+    wcs->wcsl.flag = 0;
+
+    /* Image dimensions */
+    wcs->naxis = 2;
+    wcs->naxes = 2;
+    wcs->lin.naxis = 2;
+    wcs->nxpix = nxpix;
+    wcs->nypix = nypix;
+
+    wcs->wcsproj = wcsproj0;
+
+    wcs->crpix[0] = xrpix;
+    wcs->crpix[1] = yrpix;
+    wcs->xrefpix = wcs->crpix[0];
+    wcs->yrefpix = wcs->crpix[1];
+    wcs->lin.crpix = wcs->crpix;
+
+    wcs->crval[0] = cra;
+    wcs->crval[1] = cdec;
+    wcs->xref = wcs->crval[0];
+    wcs->yref = wcs->crval[1];
+    wcs->cel.ref[0] = wcs->crval[0];
+    wcs->cel.ref[1] = wcs->crval[1];
+    wcs->cel.ref[2] = 999.0;
+
+    strcpy (wcs->c1type,"RA");
+    strcpy (wcs->c2type,"DEC");
+
+/* Allan Brighton: 28.4.98: for backward compat., remove leading "--" */
+    while (proj && *proj == '-')
+	proj++;
+    strcpy (wcs->ptype,proj);
+    strcpy (wcs->ctype[0],"RA---");
+    strcpy (wcs->ctype[1],"DEC--");
+    strcat (wcs->ctype[0],proj);
+    strcat (wcs->ctype[1],proj);
+
+    if (wcstype (wcs, wcs->ctype[0], wcs->ctype[1])) {
+	wcsfree (wcs);
+	return (NULL);
+	}
+    
+    /* Approximate world coordinate system from a known plate scale */
+    cdelt1 = -secpix / 3600.0;
+    cdelt2 = secpix / 3600.0;
+    wcsdeltset (wcs, cdelt1, cdelt2, rotate);
+    wcs->lin.cdelt = wcs->cdelt;
+    wcs->lin.pc = wcs->pc;
+
+    /* Coordinate reference frame and equinox */
+    wcs->equinox =  (double) equinox;
+    if (equinox > 1980)
+	strcpy (wcs->radecsys,"FK5");
+    else
+	strcpy (wcs->radecsys,"FK4");
+    if (epoch > 0)
+	wcs->epoch = epoch;
+    else
+	wcs->epoch = 0.0;
+    wcs->wcson = 1;
+
+    wcs->syswcs = wcscsys (wcs->radecsys);
+    wcsoutinit (wcs, wcs->radecsys);
+    wcsininit (wcs, wcs->radecsys);
+    wcs->eqout = 0.0;
+    wcs->printsys = 1;
+    wcs->tabsys = 0;
+
+    /* Initialize special WCS commands */
+    setwcscom (wcs);
+
+    return (wcs);
+}
+
+
+/* Set up a WCS structure from subroutine arguments based on FITS keywords */
+
+struct WorldCoor *
+wcskinit (naxis1, naxis2, ctype1, ctype2, crpix1, crpix2, crval1, crval2,
+	  cd, cdelt1, cdelt2, crota, equinox, epoch)
+
+int	naxis1;		/* Number of pixels along x-axis */
+int	naxis2;		/* Number of pixels along y-axis */
+char	*ctype1;	/* FITS WCS projection for axis 1 */
+char	*ctype2;	/* FITS WCS projection for axis 2 */
+double	crpix1, crpix2;	/* Reference pixel coordinates */
+double	crval1, crval2;	/* Coordinates at reference pixel in degrees */
+double	*cd;		/* Rotation matrix, used if not NULL */
+double	cdelt1, cdelt2;	/* scale in degrees/pixel, ignored if cd is not NULL */
+double	crota;		/* Rotation angle in degrees, ignored if cd is not NULL */
+int	equinox; /* Equinox of coordinates, 1950 and 2000 supported */
+double	epoch;	/* Epoch of coordinates, used for FK4/FK5 conversion
+		 * no effect if 0 */
+{
+    struct WorldCoor *wcs;
+
+    wcs = (struct WorldCoor *) calloc (1, sizeof(struct WorldCoor));
+
+    /* Set WCSLIB flags so that structures will be reinitialized */
+    wcs->cel.flag = 0;
+    wcs->lin.flag = 0;
+    wcs->wcsl.flag = 0;
+
+    /* Image dimensions */
+    wcs->naxis = 2;
+    wcs->naxes = 2;
+    wcs->lin.naxis = 2;
+    wcs->nxpix = naxis1;
+    wcs->nypix = naxis2;
+
+    wcs->wcsproj = wcsproj0;
+
+    wcs->crpix[0] = crpix1;
+    wcs->crpix[1] = crpix2;
+    wcs->xrefpix = wcs->crpix[0];
+    wcs->yrefpix = wcs->crpix[1];
+    wcs->lin.crpix = wcs->crpix;
+
+    if (wcstype (wcs, ctype1, ctype2)) {
+	wcsfree (wcs);
+	return (NULL);
+	}
+    if (wcs->latbase == 90)
+	crval2 = 90.0 - crval2;
+    else if (wcs->latbase == -90)
+	crval2 = crval2 - 90.0;
+
+    wcs->crval[0] = crval1;
+    wcs->crval[1] = crval2;
+    wcs->xref = wcs->crval[0];
+    wcs->yref = wcs->crval[1];
+    wcs->cel.ref[0] = wcs->crval[0];
+    wcs->cel.ref[1] = wcs->crval[1];
+    wcs->cel.ref[2] = 999.0;
+
+    if (cd != NULL)
+	wcscdset (wcs, cd);
+
+    else if (cdelt1 != 0.0)
+	wcsdeltset (wcs, cdelt1, cdelt2, crota);
+
+    else {
+	wcsdeltset (wcs, 1.0, 1.0, crota);
+	setwcserr ("WCSRESET: setting CDELT to 1");
+	}
+    wcs->lin.cdelt = wcs->cdelt;
+    wcs->lin.pc = wcs->pc;
+
+    /* Coordinate reference frame and equinox */
+    wcs->equinox =  (double) equinox;
+    if (equinox > 1980)
+	strcpy (wcs->radecsys,"FK5");
+    else
+	strcpy (wcs->radecsys,"FK4");
+    if (epoch > 0)
+	wcs->epoch = epoch;
+    else
+	wcs->epoch = 0.0;
+    wcs->wcson = 1;
+
+    strcpy (wcs->radecout, wcs->radecsys);
+    wcs->syswcs = wcscsys (wcs->radecsys);
+    wcsoutinit (wcs, wcs->radecsys);
+    wcsininit (wcs, wcs->radecsys);
+    wcs->eqout = 0.0;
+    wcs->printsys = 1;
+    wcs->tabsys = 0;
+
+    /* Initialize special WCS commands */
+    setwcscom (wcs);
+
+    return (wcs);
+}
+
+
+/* Set projection in WCS structure from FITS keyword values */
+
+int
+wcstype (wcs, ctype1, ctype2)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+char	*ctype1;	/* FITS WCS projection for axis 1 */
+char	*ctype2;	/* FITS WCS projection for axis 2 */
+
+{
+    int i, iproj;
+    int nctype = 32;
+    char ctypes[32][4];
+    char dtypes[10][4];
+
+    /* Initialize projection types */
+    strcpy (ctypes[0], "LIN");
+    strcpy (ctypes[1], "AZP");
+    strcpy (ctypes[2], "SZP");
+    strcpy (ctypes[3], "TAN");
+    strcpy (ctypes[4], "SIN");
+    strcpy (ctypes[5], "STG");
+    strcpy (ctypes[6], "ARC");
+    strcpy (ctypes[7], "ZPN");
+    strcpy (ctypes[8], "ZEA");
+    strcpy (ctypes[9], "AIR");
+    strcpy (ctypes[10], "CYP");
+    strcpy (ctypes[11], "CAR");
+    strcpy (ctypes[12], "MER");
+    strcpy (ctypes[13], "CEA");
+    strcpy (ctypes[14], "COP");
+    strcpy (ctypes[15], "COD");
+    strcpy (ctypes[16], "COE");
+    strcpy (ctypes[17], "COO");
+    strcpy (ctypes[18], "BON");
+    strcpy (ctypes[19], "PCO");
+    strcpy (ctypes[20], "SFL");
+    strcpy (ctypes[21], "PAR");
+    strcpy (ctypes[22], "AIT");
+    strcpy (ctypes[23], "MOL");
+    strcpy (ctypes[24], "CSC");
+    strcpy (ctypes[25], "QSC");
+    strcpy (ctypes[26], "TSC");
+    strcpy (ctypes[27], "NCP");
+    strcpy (ctypes[28], "GLS");
+    strcpy (ctypes[29], "DSS");
+    strcpy (ctypes[30], "PLT");
+    strcpy (ctypes[31], "TNX");
+
+    /* Initialize distortion types */
+    strcpy (dtypes[1], "SIP");
+
+    if (!strncmp (ctype1, "LONG",4))
+	strncpy (ctype1, "XLON",4);
+
+    strcpy (wcs->ctype[0], ctype1);
+    strcpy (wcs->c1type, ctype1);
+    strcpy (wcs->ptype, ctype1);
+
+    /* Linear coordinates */
+    if (!strncmp (ctype1,"LINEAR",6))
+	wcs->prjcode = WCS_LIN;
+
+    /* Pixel coordinates */
+    else if (!strncmp (ctype1,"PIXEL",6))
+	wcs->prjcode = WCS_PIX;
+
+    /*Detector pixel coordinates */
+    else if (strsrch (ctype1,"DET"))
+	wcs->prjcode = WCS_PIX;
+
+    /* Set up right ascension, declination, latitude, or longitude */
+    else if (ctype1[0] == 'R' || ctype1[0] == 'D' ||
+	     ctype1[0] == 'A' || ctype1[1] == 'L') {
+	wcs->c1type[0] = ctype1[0];
+	wcs->c1type[1] = ctype1[1];
+	if (ctype1[2] == '-') {
+	    wcs->c1type[2] = 0;
+	    iproj = 3;
+	    }
+	else {
+	    wcs->c1type[2] = ctype1[2];
+	    iproj = 4;
+	    if (ctype1[3] == '-') {
+		wcs->c1type[3] = 0;
+		}
+	    else {
+		wcs->c1type[3] = ctype1[3];
+		wcs->c1type[4] = 0;
+		}
+	    }
+	if (ctype1[iproj] == '-') iproj = iproj + 1;
+	if (ctype1[iproj] == '-') iproj = iproj + 1;
+	if (ctype1[iproj] == '-') iproj = iproj + 1;
+	if (ctype1[iproj] == '-') iproj = iproj + 1;
+	wcs->ptype[0] = ctype1[iproj];
+	wcs->ptype[1] = ctype1[iproj+1];
+	wcs->ptype[2] = ctype1[iproj+2];
+	wcs->ptype[3] = 0;
+	sprintf (wcs->ctype[0],"%-4s%4s",wcs->c1type,wcs->ptype);
+	for (i = 0; i < 8; i++)
+	    if (wcs->ctype[0][i] == ' ') wcs->ctype[0][i] = '-';
+
+	/*  Find projection type  */
+	wcs->prjcode = 0;  /* default type is linear */
+	for (i = 1; i < nctype; i++) {
+	    if (!strncmp(wcs->ptype, ctypes[i], 3))
+		wcs->prjcode = i;
+	    }
+
+	/* Handle "obsolete" NCP projection (now WCSLIB should be OK)
+	if (wcs->prjcode == WCS_NCP) {
+	    if (wcs->wcsproj == WCS_BEST)
+		wcs->wcsproj = WCS_OLD;
+	    else if (wcs->wcsproj == WCS_ALT)
+		wcs->wcsproj = WCS_NEW;
+	    } */
+
+	/* Work around bug in WCSLIB handling of CAR projection
+	else if (wcs->prjcode == WCS_CAR) {
+	    if (wcs->wcsproj == WCS_BEST)
+		wcs->wcsproj = WCS_OLD;
+	    else if (wcs->wcsproj == WCS_ALT)
+		wcs->wcsproj = WCS_NEW;
+	    } */
+
+	/* Work around bug in WCSLIB handling of COE projection
+	else if (wcs->prjcode == WCS_COE) {
+	    if (wcs->wcsproj == WCS_BEST)
+		wcs->wcsproj = WCS_OLD;
+	    else if (wcs->wcsproj == WCS_ALT)
+		wcs->wcsproj = WCS_NEW;
+	    }
+
+	else if (wcs->wcsproj == WCS_BEST) */
+	if (wcs->wcsproj == WCS_BEST)
+	    wcs->wcsproj = WCS_NEW;
+
+	else if (wcs->wcsproj == WCS_ALT)
+	    wcs->wcsproj = WCS_OLD;
+
+	/* if (wcs->wcsproj == WCS_OLD && (
+	    wcs->prjcode != WCS_STG && wcs->prjcode != WCS_AIT &&
+	    wcs->prjcode != WCS_MER && wcs->prjcode != WCS_GLS &&
+	    wcs->prjcode != WCS_ARC && wcs->prjcode != WCS_TAN &&
+	    wcs->prjcode != WCS_TNX && wcs->prjcode != WCS_SIN &&
+	    wcs->prjcode != WCS_PIX && wcs->prjcode != WCS_LIN &&
+	    wcs->prjcode != WCS_CAR && wcs->prjcode != WCS_COE &&
+	    wcs->prjcode != WCS_NCP))
+	    wcs->wcsproj = WCS_NEW; */
+
+	/* Handle NOAO corrected TNX as uncorrected TAN if oldwcs is set */
+	if (wcs->wcsproj == WCS_OLD && wcs->prjcode == WCS_TNX) {
+	    wcs->ctype[0][6] = 'A';
+	    wcs->ctype[0][7] = 'N';
+	    wcs->prjcode = WCS_TAN;
+	    }
+	}
+
+    /* If not sky coordinates, assume linear */
+    else {
+	wcs->prjcode = WCS_LIN;
+	return (0);
+	}
+
+    /* Second coordinate type */
+    if (!strncmp (ctype2, "NPOL",4)) {
+	ctype2[0] = ctype1[0];
+	strncpy (ctype2+1, "LAT",3);
+	wcs->latbase = 90;
+	strcpy (wcs->radecsys,"NPOLE");
+	wcs->syswcs = WCS_NPOLE;
+	}
+    else if (!strncmp (ctype2, "SPA-",4)) {
+	ctype2[0] = ctype1[0];
+	strncpy (ctype2+1, "LAT",3);
+	wcs->latbase = -90;
+	strcpy (wcs->radecsys,"SPA");
+	wcs->syswcs = WCS_SPA;
+	}
+    else
+	wcs->latbase = 0;
+    strcpy (wcs->ctype[1], ctype2);
+    strcpy (wcs->c2type, ctype2);
+
+    /* Linear coordinates */
+    if (!strncmp (ctype2,"LINEAR",6))
+	wcs->prjcode = WCS_LIN;
+
+    /* Pixel coordinates */
+    else if (!strncmp (ctype2,"PIXEL",6))
+	wcs->prjcode = WCS_PIX;
+
+    /* Set up right ascension, declination, latitude, or longitude */
+    else if (ctype2[0] == 'R' || ctype2[0] == 'D' ||
+	     ctype2[0] == 'A' || ctype2[1] == 'L') {
+	wcs->c2type[0] = ctype2[0];
+	wcs->c2type[1] = ctype2[1];
+	if (ctype2[2] == '-') {
+	    wcs->c2type[2] = 0;
+	    iproj = 3;
+	    }
+	else {
+	    wcs->c2type[2] = ctype2[2];
+	    iproj = 4;
+	    if (ctype2[3] == '-') {
+		wcs->c2type[3] = 0;
+		}
+	    else {
+		wcs->c2type[3] = ctype2[3];
+		wcs->c2type[4] = 0;
+		}
+	    }
+	if (ctype2[iproj] == '-') iproj = iproj + 1;
+	if (ctype2[iproj] == '-') iproj = iproj + 1;
+	if (ctype2[iproj] == '-') iproj = iproj + 1;
+	if (ctype2[iproj] == '-') iproj = iproj + 1;
+	wcs->ptype[0] = ctype2[iproj];
+	wcs->ptype[1] = ctype2[iproj+1];
+	wcs->ptype[2] = ctype2[iproj+2];
+	wcs->ptype[3] = 0;
+
+	if (!strncmp (ctype1, "DEC", 3) ||
+	    !strncmp (ctype1+1, "LAT", 3))
+	    wcs->coorflip = 1;
+	else
+	    wcs->coorflip = 0;
+	if (ctype2[1] == 'L' || ctype2[0] == 'A') {
+	    wcs->degout = 1;
+	    wcs->ndec = 5;
+	    }
+	else {
+	    wcs->degout = 0;
+	    wcs->ndec = 3;
+	    }
+	sprintf (wcs->ctype[1],"%-4s%4s",wcs->c2type,wcs->ptype);
+	for (i = 0; i < 8; i++)
+	    if (wcs->ctype[1][i] == ' ') wcs->ctype[1][i] = '-';
+	}
+
+    /* If not sky coordinates, assume linear */
+    else {
+	wcs->prjcode = WCS_LIN;
+	}
+
+    /* Set distortion code from CTYPE1 extension */
+    setdistcode (wcs, ctype1);
+
+    return (0);
+}
+
+
+int
+wcsreset (wcs, crpix1, crpix2, crval1, crval2, cdelt1, cdelt2, crota, cd)
+
+struct WorldCoor *wcs;		/* World coordinate system data structure */
+double crpix1, crpix2;		/* Reference pixel coordinates */
+double crval1, crval2;		/* Coordinates at reference pixel in degrees */
+double cdelt1, cdelt2;		/* scale in degrees/pixel, ignored if cd is not NULL */
+double crota;			/* Rotation angle in degrees, ignored if cd is not NULL */
+double *cd;			/* Rotation matrix, used if not NULL */
+{
+
+    if (nowcs (wcs))
+	return (-1);
+
+    /* Set WCSLIB flags so that structures will be reinitialized */
+    wcs->cel.flag = 0;
+    wcs->lin.flag = 0;
+    wcs->wcsl.flag = 0;
+
+    /* Reference pixel coordinates and WCS value */
+    wcs->crpix[0] = crpix1;
+    wcs->crpix[1] = crpix2;
+    wcs->xrefpix = wcs->crpix[0];
+    wcs->yrefpix = wcs->crpix[1];
+    wcs->lin.crpix = wcs->crpix;
+
+    wcs->crval[0] = crval1;
+    wcs->crval[1] = crval2;
+    wcs->xref = wcs->crval[0];
+    wcs->yref = wcs->crval[1];
+    if (wcs->coorflip) {
+	wcs->cel.ref[1] = wcs->crval[0];
+	wcs->cel.ref[0] = wcs->crval[1];
+	}
+    else {
+	wcs->cel.ref[0] = wcs->crval[0];
+	wcs->cel.ref[1] = wcs->crval[1];
+	}
+    /* Keep ref[2] and ref[3] from input */
+
+    /* Initialize to no plate fit */
+    wcs->ncoeff1 = 0;
+    wcs->ncoeff2 = 0;
+
+    if (cd != NULL)
+	wcscdset (wcs, cd);
+
+    else if (cdelt1 != 0.0)
+	wcsdeltset (wcs, cdelt1, cdelt2, crota);
+
+    else {
+	wcs->xinc = 1.0;
+	wcs->yinc = 1.0;
+	setwcserr ("WCSRESET: setting CDELT to 1");
+	}
+
+    /* Coordinate reference frame, equinox, and epoch */
+    if (!strncmp (wcs->ptype,"LINEAR",6) ||
+	!strncmp (wcs->ptype,"PIXEL",5))
+	wcs->degout = -1;
+
+    wcs->wcson = 1;
+    return (0);
+}
+
+void
+wcseqset (wcs, equinox)
+
+struct WorldCoor *wcs;		/* World coordinate system data structure */
+double equinox;			/* Desired equinox as fractional year */
+{
+
+    if (nowcs (wcs))
+	return;
+
+    /* Leave WCS alone if already at desired equinox */
+    if (wcs->equinox == equinox)
+	return;
+
+    /* Convert center from B1950 (FK4) to J2000 (FK5) */
+    if (equinox == 2000.0 && wcs->equinox == 1950.0) {
+	if (wcs->coorflip) { 
+	    fk425e (&wcs->crval[1], &wcs->crval[0], wcs->epoch);
+	    wcs->cel.ref[1] = wcs->crval[0];
+	    wcs->cel.ref[0] = wcs->crval[1];
+	    }
+	else {
+	    fk425e (&wcs->crval[0], &wcs->crval[1], wcs->epoch);
+	    wcs->cel.ref[0] = wcs->crval[0];
+	    wcs->cel.ref[1] = wcs->crval[1];
+	    }
+	wcs->xref = wcs->crval[0];
+	wcs->yref = wcs->crval[1];
+	wcs->equinox = 2000.0;
+	strcpy (wcs->radecsys, "FK5");
+	wcs->syswcs = WCS_J2000;
+	wcs->cel.flag = 0;
+	wcs->wcsl.flag = 0;
+	}
+
+    /* Convert center from J2000 (FK5) to B1950 (FK4) */
+    else if (equinox == 1950.0 && wcs->equinox == 2000.0) {
+	if (wcs->coorflip) { 
+	    fk524e (&wcs->crval[1], &wcs->crval[0], wcs->epoch);
+	    wcs->cel.ref[1] = wcs->crval[0];
+	    wcs->cel.ref[0] = wcs->crval[1];
+	    }
+	else {
+	    fk524e (&wcs->crval[0], &wcs->crval[1], wcs->epoch);
+	    wcs->cel.ref[0] = wcs->crval[0];
+	    wcs->cel.ref[1] = wcs->crval[1];
+	    }
+	wcs->xref = wcs->crval[0];
+	wcs->yref = wcs->crval[1];
+	wcs->equinox = 1950.0;
+	strcpy (wcs->radecsys, "FK4");
+	wcs->syswcs = WCS_B1950;
+	wcs->cel.flag = 0;
+	wcs->wcsl.flag = 0;
+	}
+    wcsoutinit (wcs, wcs->radecsys);
+    wcsininit (wcs, wcs->radecsys);
+    return;
+}
+
+
+/* Set scale and rotation in WCS structure */
+
+void
+wcscdset (wcs, cd)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double *cd;			/* CD matrix, ignored if NULL */
+{
+    double tcd;
+
+    if (cd == NULL)
+	return;
+
+    wcs->rotmat = 1;
+    wcs->cd[0] = cd[0];
+    wcs->cd[1] = cd[1];
+    wcs->cd[2] = cd[2];
+    wcs->cd[3] = cd[3];
+    (void) matinv (2, wcs->cd, wcs->dc);
+
+    /* Compute scale */
+    wcs->xinc = sqrt (cd[0]*cd[0] + cd[2]*cd[2]);
+    wcs->yinc = sqrt (cd[1]*cd[1] + cd[3]*cd[3]);
+
+    /* Deal with x=Dec/y=RA case */
+    if (wcs->coorflip) {
+	tcd = cd[1];
+	cd[1] = -cd[2];
+	cd[2] = -tcd;
+	}
+    wcslibrot (wcs);
+    wcs->wcson = 1;
+
+    /* Compute image rotation */
+    wcsrotset (wcs);
+
+    wcs->cdelt[0] = wcs->xinc;
+    wcs->cdelt[1] = wcs->yinc;
+
+    return;
+}
+
+
+/* Set scale and rotation in WCS structure from axis scale and rotation */
+
+void
+wcsdeltset (wcs, cdelt1, cdelt2, crota)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double cdelt1;		/* degrees/pixel in first axis (or both axes) */
+double cdelt2;		/* degrees/pixel in second axis if nonzero */
+double crota;		/* Rotation counterclockwise in degrees */
+{
+    double *pci;
+    double crot, srot;
+    int i, j, naxes;
+
+    naxes = wcs->naxis;
+    if (naxes > 2)
+	naxes = 2;
+    wcs->cdelt[0] = cdelt1;
+    if (cdelt2 != 0.0)
+	wcs->cdelt[1] = cdelt2;
+    else
+	wcs->cdelt[1] = cdelt1;
+    wcs->xinc = wcs->cdelt[0];
+    wcs->yinc = wcs->cdelt[1];
+    pci = wcs->pc;
+    for (i = 0; i < naxes; i++) {
+	for (j = 0; j < naxes; j++) {
+	    if (i ==j)
+		*pci = 1.0;
+	    else
+		*pci = 0.0;
+	    pci++;
+	    }
+	}
+    wcs->rotmat = 0;
+
+    /* If image is reversed, value of CROTA is flipped, too */
+    wcs->rot = crota;
+    if (wcs->rot < 0.0)
+	wcs->rot = wcs->rot + 360.0;
+    if (wcs->rot >= 360.0)
+	wcs->rot = wcs->rot - 360.0;
+    crot = cos (degrad(wcs->rot));
+    if (cdelt1 * cdelt2 > 0)
+	srot = sin (-degrad(wcs->rot));
+    else
+	srot = sin (degrad(wcs->rot));
+
+    /* Set CD matrix */
+    wcs->cd[0] = wcs->cdelt[0] * crot;
+    if (wcs->cdelt[0] < 0)
+	wcs->cd[1] = -fabs (wcs->cdelt[1]) * srot;
+    else
+	wcs->cd[1] = fabs (wcs->cdelt[1]) * srot;
+    if (wcs->cdelt[1] < 0)
+	wcs->cd[2] = fabs (wcs->cdelt[0]) * srot;
+    else
+	wcs->cd[2] = -fabs (wcs->cdelt[0]) * srot;
+    wcs->cd[3] = wcs->cdelt[1] * crot;
+    (void) matinv (2, wcs->cd, wcs->dc);
+
+    /* Set rotation matrix */
+    wcslibrot (wcs);
+
+    /* Set image rotation and mirroring */
+    if (wcs->coorflip) {
+	if (wcs->cdelt[0] < 0 && wcs->cdelt[1] > 0) {
+	    wcs->imflip = 1;
+	    wcs->imrot = wcs->rot - 90.0;
+	    if (wcs->imrot < -180.0) wcs->imrot = wcs->imrot + 360.0;
+	    wcs->pa_north = wcs->rot;
+	    wcs->pa_east = wcs->rot - 90.0;
+	    if (wcs->pa_east < -180.0) wcs->pa_east = wcs->pa_east + 360.0;
+	    }
+	else if (wcs->cdelt[0] > 0 && wcs->cdelt[1] < 0) {
+	    wcs->imflip = 1;
+	    wcs->imrot = wcs->rot + 90.0;
+	    if (wcs->imrot > 180.0) wcs->imrot = wcs->imrot - 360.0;
+	    wcs->pa_north = wcs->rot;
+	    wcs->pa_east = wcs->rot - 90.0;
+	    if (wcs->pa_east < -180.0) wcs->pa_east = wcs->pa_east + 360.0;
+	    }
+	else if (wcs->cdelt[0] > 0 && wcs->cdelt[1] > 0) {
+	    wcs->imflip = 0;
+	    wcs->imrot = wcs->rot + 90.0;
+	    if (wcs->imrot > 180.0) wcs->imrot = wcs->imrot - 360.0;
+	    wcs->pa_north = wcs->imrot;
+	    wcs->pa_east = wcs->rot + 90.0;
+	    if (wcs->pa_east > 180.0) wcs->pa_east = wcs->pa_east - 360.0;
+	    }
+	else if (wcs->cdelt[0] < 0 && wcs->cdelt[1] < 0) {
+	    wcs->imflip = 0;
+	    wcs->imrot = wcs->rot - 90.0;
+	    if (wcs->imrot < -180.0) wcs->imrot = wcs->imrot + 360.0;
+	    wcs->pa_north = wcs->imrot;
+	    wcs->pa_east = wcs->rot + 90.0;
+	    if (wcs->pa_east > 180.0) wcs->pa_east = wcs->pa_east - 360.0;
+	    }
+	}
+    else {
+	if (wcs->cdelt[0] < 0 && wcs->cdelt[1] > 0) {
+	    wcs->imflip = 0;
+	    wcs->imrot = wcs->rot;
+	    wcs->pa_north = wcs->rot + 90.0;
+	    if (wcs->pa_north > 180.0) wcs->pa_north = wcs->pa_north - 360.0;
+	    wcs->pa_east = wcs->rot + 180.0;
+	    if (wcs->pa_east > 180.0) wcs->pa_east = wcs->pa_east - 360.0;
+	    }
+	else if (wcs->cdelt[0] > 0 && wcs->cdelt[1] < 0) {
+	    wcs->imflip = 0;
+	    wcs->imrot = wcs->rot + 180.0;
+	    if (wcs->imrot > 180.0) wcs->imrot = wcs->imrot - 360.0;
+	    wcs->pa_north = wcs->imrot + 90.0;
+	    if (wcs->pa_north > 180.0) wcs->pa_north = wcs->pa_north - 360.0;
+	    wcs->pa_east = wcs->imrot + 180.0;
+	    if (wcs->pa_east > 180.0) wcs->pa_east = wcs->pa_east - 360.0;
+	    }
+	else if (wcs->cdelt[0] > 0 && wcs->cdelt[1] > 0) {
+	    wcs->imflip = 1;
+	    wcs->imrot = -wcs->rot;
+	    wcs->pa_north = wcs->imrot + 90.0;
+	    if (wcs->pa_north > 180.0) wcs->pa_north = wcs->pa_north - 360.0;
+	    wcs->pa_east = wcs->rot;
+	    }
+	else if (wcs->cdelt[0] < 0 && wcs->cdelt[1] < 0) {
+	    wcs->imflip = 1;
+	    wcs->imrot = wcs->rot + 180.0;
+	    if (wcs->imrot > 180.0) wcs->imrot = wcs->imrot - 360.0;
+	    wcs->pa_north = wcs->imrot + 90.0;
+	    if (wcs->pa_north > 180.0) wcs->pa_north = wcs->pa_north - 360.0;
+	    wcs->pa_east = wcs->rot + 90.0;
+	    if (wcs->pa_east > 180.0) wcs->pa_east = wcs->pa_east - 360.0;
+	    }
+	}
+
+    return;
+}
+
+
+/* Set scale and rotation in WCS structure */
+
+void
+wcspcset (wcs, cdelt1, cdelt2, pc)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double cdelt1;		/* degrees/pixel in first axis (or both axes) */
+double cdelt2;		/* degrees/pixel in second axis if nonzero */
+double *pc;		/* Rotation matrix, ignored if NULL */
+{
+    double *pci, *pc0i;
+    int i, j, naxes;
+
+    if (pc == NULL)
+	return;
+
+    naxes = wcs->naxis;
+/*   if (naxes > 2)
+	naxes = 2; */
+    if (naxes < 1 || naxes > 9) {
+	naxes = wcs->naxes;
+	wcs->naxis = naxes;
+	}
+    wcs->cdelt[0] = cdelt1;
+    if (cdelt2 != 0.0)
+	wcs->cdelt[1] = cdelt2;
+    else
+	wcs->cdelt[1] = cdelt1;
+    wcs->xinc = wcs->cdelt[0];
+    wcs->yinc = wcs->cdelt[1];
+
+    /* Set rotation matrix */
+    pci = wcs->pc;
+    pc0i = pc;
+    for (i = 0; i < naxes; i++) {
+	for (j = 0; j < naxes; j++) {
+	    *pci = *pc0i;
+	    pci++;
+	    pc0i++;
+	    }
+	}
+
+    /* Set CD matrix */
+    if (naxes > 1) {
+	wcs->cd[0] = pc[0] * wcs->cdelt[0];
+	wcs->cd[1] = pc[1] * wcs->cdelt[0];
+	wcs->cd[2] = pc[naxes] * wcs->cdelt[1];
+	wcs->cd[3] = pc[naxes+1] * wcs->cdelt[1];
+	}
+    else {
+	wcs->cd[0] = pc[0] * wcs->cdelt[0];
+	wcs->cd[1] = 0.0;
+	wcs->cd[2] = 0.0;
+	wcs->cd[3] = 1.0;
+	}
+    (void) matinv (2, wcs->cd, wcs->dc);
+    wcs->rotmat = 1;
+
+    (void)linset (&wcs->lin);
+    wcs->wcson = 1;
+
+    wcsrotset (wcs);
+
+    return;
+}
+
+
+/* Set up rotation matrix for WCSLIB projection subroutines */
+
+static void
+wcslibrot (wcs)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+
+{
+    int i, mem, naxes;
+
+    naxes = wcs->naxis;
+    if (naxes > 2)
+	naxes = 2;
+    if (naxes < 1 || naxes > 9) {
+	naxes = wcs->naxes;
+	wcs->naxis = naxes;
+	}
+    mem = naxes * naxes * sizeof(double);
+    if (wcs->lin.piximg == NULL)
+	wcs->lin.piximg = (double*)malloc(mem);
+    if (wcs->lin.piximg != NULL) {
+	if (wcs->lin.imgpix == NULL)
+	    wcs->lin.imgpix = (double*)malloc(mem);
+	if (wcs->lin.imgpix != NULL) {
+	    wcs->lin.flag = LINSET;
+	    if (naxes == 2) {
+		for (i = 0; i < 4; i++) {
+		    wcs->lin.piximg[i] = wcs->cd[i];
+		    }
+		}
+	    else if (naxes == 3) {
+		for (i = 0; i < 9; i++)
+		    wcs->lin.piximg[i] = 0.0;
+		wcs->lin.piximg[0] = wcs->cd[0];
+		wcs->lin.piximg[1] = wcs->cd[1];
+		wcs->lin.piximg[3] = wcs->cd[2];
+		wcs->lin.piximg[4] = wcs->cd[3];
+		wcs->lin.piximg[8] = 1.0;
+		}
+	    else if (naxes == 4) {
+		for (i = 0; i < 16; i++)
+		    wcs->lin.piximg[i] = 0.0;
+		wcs->lin.piximg[0] = wcs->cd[0];
+		wcs->lin.piximg[1] = wcs->cd[1];
+		wcs->lin.piximg[4] = wcs->cd[2];
+		wcs->lin.piximg[5] = wcs->cd[3];
+		wcs->lin.piximg[10] = 1.0;
+		wcs->lin.piximg[15] = 1.0;
+		}
+	    (void) matinv (naxes, wcs->lin.piximg, wcs->lin.imgpix);
+	    wcs->lin.crpix = wcs->crpix;
+	    wcs->lin.cdelt = wcs->cdelt;
+	    wcs->lin.pc = wcs->pc;
+	    wcs->lin.flag = LINSET;
+	    }
+	}
+    return;
+}
+
+
+/* Compute image rotation */
+
+void
+wcsrotset (wcs)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+{
+    int off;
+    double cra, cdec, xc, xn, xe, yc, yn, ye;
+
+    /* If image is one-dimensional, leave rotation angle alone */
+    if (wcs->nxpix < 1.5 || wcs->nypix < 1.5) {
+	wcs->imrot = wcs->rot;
+	wcs->pa_north = wcs->rot + 90.0;
+	wcs->pa_east = wcs->rot + 180.0;
+	return;
+	}
+
+
+    /* Do not try anything if image is LINEAR (not Cartesian projection) */
+    if (wcs->syswcs == WCS_LINEAR)
+	return;
+
+    wcs->xinc = fabs (wcs->xinc);
+    wcs->yinc = fabs (wcs->yinc);
+
+    /* Compute position angles of North and East in image */
+    xc = wcs->xrefpix;
+    yc = wcs->yrefpix;
+    pix2wcs (wcs, xc, yc, &cra, &cdec);
+    if (wcs->coorflip) {
+	wcs2pix (wcs, cra+wcs->yinc, cdec, &xe, &ye, &off);
+	wcs2pix (wcs, cra, cdec+wcs->xinc, &xn, &yn, &off);
+	}
+    else {
+	wcs2pix (wcs, cra+wcs->xinc, cdec, &xe, &ye, &off);
+	wcs2pix (wcs, cra, cdec+wcs->yinc, &xn, &yn, &off);
+	}
+    wcs->pa_north = raddeg (atan2 (yn-yc, xn-xc));
+    if (wcs->pa_north < -90.0)
+	wcs->pa_north = wcs->pa_north + 360.0;
+    wcs->pa_east = raddeg (atan2 (ye-yc, xe-xc));
+    if (wcs->pa_east < -90.0)
+	wcs->pa_east = wcs->pa_east + 360.0;
+
+    /* Compute image rotation angle from North */
+    if (wcs->pa_north < -90.0)
+	wcs->imrot = 270.0 + wcs->pa_north;
+    else
+	wcs->imrot = wcs->pa_north - 90.0;
+
+    /* Compute CROTA */
+    if (wcs->coorflip) {
+	wcs->rot = wcs->imrot + 90.0;
+	if (wcs->rot < 0.0)
+	    wcs->rot = wcs->rot + 360.0;
+	}
+    else
+	wcs->rot = wcs->imrot;
+    if (wcs->rot < 0.0)
+	wcs->rot = wcs->rot + 360.0;
+    if (wcs->rot >= 360.0)
+	wcs->rot = wcs->rot - 360.0;
+
+    /* Set image mirror flag based on axis orientation */
+    wcs->imflip = 0;
+    if (wcs->pa_east - wcs->pa_north < -80.0 &&
+	wcs->pa_east - wcs->pa_north > -100.0)
+	wcs->imflip = 1;
+    if (wcs->pa_east - wcs->pa_north < 280.0 &&
+	wcs->pa_east - wcs->pa_north > 260.0)
+	wcs->imflip = 1;
+    if (wcs->pa_north - wcs->pa_east > 80.0 &&
+	wcs->pa_north - wcs->pa_east < 100.0)
+	wcs->imflip = 1;
+    if (wcs->coorflip) {
+	if (wcs->imflip)
+	    wcs->yinc = -wcs->yinc;
+	}
+    else {
+	if (!wcs->imflip)
+	    wcs->xinc = -wcs->xinc;
+	}
+
+    return;
+}
+
+
+/* Return 1 if WCS structure is filled, else 0 */
+
+int
+iswcs (wcs)
+
+struct WorldCoor *wcs;		/* World coordinate system structure */
+
+{
+    if (wcs == NULL)
+	return (0);
+    else
+	return (wcs->wcson);
+}
+
+
+/* Return 0 if WCS structure is filled, else 1 */
+
+int
+nowcs (wcs)
+
+struct WorldCoor *wcs;		/* World coordinate system structure */
+
+{
+    if (wcs == NULL)
+	return (1);
+    else
+	return (!wcs->wcson);
+}
+
+
+/* Reset the center of a WCS structure */
+
+void
+wcsshift (wcs,rra,rdec,coorsys)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double	rra;		/* Reference pixel right ascension in degrees */
+double	rdec;		/* Reference pixel declination in degrees */
+char	*coorsys;	/* FK4 or FK5 coordinates (1950 or 2000) */
+
+{
+    if (nowcs (wcs))
+	return;
+
+/* Approximate world coordinate system from a known plate scale */
+    wcs->crval[0] = rra;
+    wcs->crval[1] = rdec;
+    wcs->xref = wcs->crval[0];
+    wcs->yref = wcs->crval[1];
+
+
+/* Coordinate reference frame */
+    strcpy (wcs->radecsys,coorsys);
+    wcs->syswcs = wcscsys (coorsys);
+    if (wcs->syswcs == WCS_B1950)
+	wcs->equinox = 1950.0;
+    else
+	wcs->equinox = 2000.0;
+
+    return;
+}
+
+/* Print position of WCS center, if WCS is set */
+
+void
+wcscent (wcs)
+
+struct WorldCoor *wcs;		/* World coordinate system structure */
+
+{
+    double	xpix,ypix, xpos1, xpos2, ypos1, ypos2;
+    char wcstring[32];
+    double width, height, secpix, secpixh, secpixw;
+    int lstr = 32;
+
+    if (nowcs (wcs))
+	(void)fprintf (stderr,"No WCS information available\n");
+    else {
+	if (wcs->prjcode == WCS_DSS)
+	    (void)fprintf (stderr,"WCS plate center  %s\n", wcs->center);
+	xpix = 0.5 * wcs->nxpix;
+	ypix = 0.5 * wcs->nypix;
+	(void) pix2wcst (wcs,xpix,ypix,wcstring, lstr);
+	(void)fprintf (stderr,"WCS center %s %s %s %s at pixel (%.2f,%.2f)\n",
+		     wcs->ctype[0],wcs->ctype[1],wcstring,wcs->ptype,xpix,ypix);
+
+	/* Image width */
+	(void) pix2wcs (wcs,1.0,ypix,&xpos1,&ypos1);
+	(void) pix2wcs (wcs,wcs->nxpix,ypix,&xpos2,&ypos2);
+	if (wcs->syswcs == WCS_LINEAR) {
+	    width = xpos2 - xpos1;
+	    if (width < 100.0)
+	    (void)fprintf (stderr, "WCS width = %.5f %s ",width, wcs->units[0]);
+	    else
+	    (void)fprintf (stderr, "WCS width = %.3f %s ",width, wcs->units[0]);
+	    }
+	else {
+	    width = wcsdist (xpos1,ypos1,xpos2,ypos2);
+	    if (width < 1/60.0)
+		(void)fprintf (stderr, "WCS width = %.2f arcsec ",width*3600.0);
+	    else if (width < 1.0)
+		(void)fprintf (stderr, "WCS width = %.2f arcmin ",width*60.0);
+	    else
+		(void)fprintf (stderr, "WCS width = %.3f degrees ",width);
+	    }
+	secpixw = width / (wcs->nxpix - 1.0);
+
+	/* Image height */
+	(void) pix2wcs (wcs,xpix,1.0,&xpos1,&ypos1);
+	(void) pix2wcs (wcs,xpix,wcs->nypix,&xpos2,&ypos2);
+	if (wcs->syswcs == WCS_LINEAR) {
+	    height = ypos2 - ypos1;
+	    if (height < 100.0)
+	    (void)fprintf (stderr, " height = %.5f %s ",height, wcs->units[1]);
+	    else
+	    (void)fprintf (stderr, " height = %.3f %s ",height, wcs->units[1]);
+	    }
+	else {
+	    height = wcsdist (xpos1,ypos1,xpos2,ypos2);
+	    if (height < 1/60.0)
+		(void) fprintf (stderr, " height = %.2f arcsec",height*3600.0);
+	    else if (height < 1.0)
+		(void) fprintf (stderr, " height = %.2f arcmin",height*60.0);
+	    else
+		(void) fprintf (stderr, " height = %.3f degrees",height);
+	    }
+	secpixh = height / (wcs->nypix - 1.0);
+
+	/* Image scale */
+	if (wcs->syswcs == WCS_LINEAR) {
+	    (void) fprintf (stderr,"\n");
+	    (void) fprintf (stderr,"WCS  %.5f %s/pixel, %.5f %s/pixel\n",
+			    wcs->xinc,wcs->units[0],wcs->yinc,wcs->units[1]);
+	    }
+	else {
+	    if (wcs->xinc != 0.0 && wcs->yinc != 0.0)
+		secpix = (fabs(wcs->xinc) + fabs(wcs->yinc)) * 0.5 * 3600.0;
+	    else if (secpixh > 0.0 && secpixw > 0.0)
+		secpix = (secpixw + secpixh) * 0.5 * 3600.0;
+	    else if (wcs->xinc != 0.0 || wcs->yinc != 0.0)
+		secpix = (fabs(wcs->xinc) + fabs(wcs->yinc)) * 3600.0;
+	    else
+		secpix = (secpixw + secpixh) * 3600.0;
+	    if (secpix < 100.0)
+		(void) fprintf (stderr, "  %.3f arcsec/pixel\n",secpix);
+	    else if (secpix < 3600.0)
+		(void) fprintf (stderr, "  %.3f arcmin/pixel\n",secpix/60.0);
+	    else
+		(void) fprintf (stderr, "  %.3f degrees/pixel\n",secpix/3600.0);
+	    }
+	}
+    return;
+}
+
+/* Return RA and Dec of image center, plus size in RA and Dec */
+
+void
+wcssize (wcs, cra, cdec, dra, ddec)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double	*cra;		/* Right ascension of image center (deg) (returned) */
+double	*cdec;		/* Declination of image center (deg) (returned) */
+double	*dra;		/* Half-width in right ascension (deg) (returned) */
+double	*ddec;		/* Half-width in declination (deg) (returned) */
+
+{
+    double width, height;
+
+    /* Find right ascension and declination of coordinates */
+    if (iswcs(wcs)) {
+	wcsfull (wcs, cra, cdec, &width, &height);
+	*dra = 0.5 * width / cos (degrad (*cdec));
+	*ddec = 0.5 * height;
+	}
+    else {
+	*cra = 0.0;
+	*cdec = 0.0;
+	*dra = 0.0;
+	*ddec = 0.0;
+	}
+    return;
+}
+
+
+/* Return RA and Dec of image center, plus size in degrees */
+
+void
+wcsfull (wcs, cra, cdec, width, height)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double	*cra;		/* Right ascension of image center (deg) (returned) */
+double	*cdec;		/* Declination of image center (deg) (returned) */
+double	*width;		/* Width in degrees (returned) */
+double	*height;	/* Height in degrees (returned) */
+
+{
+    double xpix, ypix, xpos1, xpos2, ypos1, ypos2, xcpix, ycpix;
+    double xcent, ycent;
+
+    /* Find right ascension and declination of coordinates */
+    if (iswcs(wcs)) {
+	xcpix = (0.5 * wcs->nxpix) + 0.5;
+	ycpix = (0.5 * wcs->nypix) + 0.5;
+	(void) pix2wcs (wcs,xcpix,ycpix,&xcent, &ycent);
+	*cra = xcent;
+	*cdec = ycent;
+
+	/* Compute image width in degrees */
+	xpix = 0.500001;
+	(void) pix2wcs (wcs,xpix,ycpix,&xpos1,&ypos1);
+	xpix = wcs->nxpix + 0.499999;
+	(void) pix2wcs (wcs,xpix,ycpix,&xpos2,&ypos2);
+	if (strncmp (wcs->ptype,"LINEAR",6) &&
+	    strncmp (wcs->ptype,"PIXEL",5)) {
+	    *width = wcsdist (xpos1,ypos1,xpos2,ypos2);
+	    }
+	else
+	    *width = sqrt (((ypos2-ypos1) * (ypos2-ypos1)) +
+		     ((xpos2-xpos1) * (xpos2-xpos1)));
+
+	/* Compute image height in degrees */
+	ypix = 0.5;
+	(void) pix2wcs (wcs,xcpix,ypix,&xpos1,&ypos1);
+	ypix = wcs->nypix + 0.5;
+	(void) pix2wcs (wcs,xcpix,ypix,&xpos2,&ypos2);
+	if (strncmp (wcs->ptype,"LINEAR",6) &&
+	    strncmp (wcs->ptype,"PIXEL",5))
+	    *height = wcsdist (xpos1,ypos1,xpos2,ypos2);
+	else
+	    *height = sqrt (((ypos2-ypos1) * (ypos2-ypos1)) +
+		      ((xpos2-xpos1) * (xpos2-xpos1)));
+	}
+
+    else {
+	*cra = 0.0;
+	*cdec = 0.0;
+	*width = 0.0;
+	*height = 0.0;
+	}
+
+    return;
+}
+
+
+/* Return minimum and maximum RA and Dec of image in degrees */
+
+void
+wcsrange (wcs, ra1, ra2, dec1, dec2)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double	*ra1;		/* Minimum right ascension of image (deg) (returned) */
+double	*ra2;		/* Maximum right ascension of image (deg) (returned) */
+double	*dec1;		/* Minimum declination of image (deg) (returned) */
+double	*dec2;		/* Maximum declination of image (deg) (returned) */
+
+{
+    double xpos1, xpos2, xpos3, xpos4, ypos1, ypos2, ypos3, ypos4, temp;
+
+    if (iswcs(wcs)) {
+
+	/* Compute image corner coordinates in degrees */
+	(void) pix2wcs (wcs,1.0,1.0,&xpos1,&ypos1);
+	(void) pix2wcs (wcs,1.0,wcs->nypix,&xpos2,&ypos2);
+	(void) pix2wcs (wcs,wcs->nxpix,1.0,&xpos3,&ypos3);
+	(void) pix2wcs (wcs,wcs->nxpix,wcs->nypix,&xpos4,&ypos4);
+
+	/* Find minimum right ascension or longitude */
+	*ra1 = xpos1;
+	if (xpos2 < *ra1) *ra1 = xpos2;
+	if (xpos3 < *ra1) *ra1 = xpos3;
+	if (xpos4 < *ra1) *ra1 = xpos4;
+
+	/* Find maximum right ascension or longitude */
+	*ra2 = xpos1;
+	if (xpos2 > *ra2) *ra2 = xpos2;
+	if (xpos3 > *ra2) *ra2 = xpos3;
+	if (xpos4 > *ra2) *ra2 = xpos4;
+
+	if (wcs->syswcs != WCS_LINEAR && wcs->syswcs != WCS_XY) {
+	    if (*ra2 - *ra1 > 180.0) {
+		temp = *ra1;
+		*ra1 = *ra2;
+		*ra2 = temp;
+		}
+	    }
+
+	/* Find minimum declination or latitude */
+	*dec1 = ypos1;
+	if (ypos2 < *dec1) *dec1 = ypos2;
+	if (ypos3 < *dec1) *dec1 = ypos3;
+	if (ypos4 < *dec1) *dec1 = ypos4;
+
+	/* Find maximum declination or latitude */
+	*dec2 = ypos1;
+	if (ypos2 > *dec2) *dec2 = ypos2;
+	if (ypos3 > *dec2) *dec2 = ypos3;
+	if (ypos4 > *dec2) *dec2 = ypos4;
+	}
+
+    else {
+	*ra1 = 0.0;
+	*ra2 = 0.0;
+	*dec1 = 0.0;
+	*dec2 = 0.0;
+	}
+
+    return;
+}
+
+
+/* Compute distance in degrees between two sky coordinates */
+
+double
+wcsdist (x1,y1,x2,y2)
+
+double	x1,y1;	/* (RA,Dec) or (Long,Lat) in degrees */
+double	x2,y2;	/* (RA,Dec) or (Long,Lat) in degrees */
+
+{
+	double d1, d2, r, diffi;
+	double pos1[3], pos2[3], w, diff;
+	int i;
+
+	/* Convert two vectors to direction cosines */
+	r = 1.0;
+	d2v3 (x1, y1, r, pos1);
+	d2v3 (x2, y2, r, pos2);
+
+	/* Modulus squared of half the difference vector */
+	w = 0.0;
+	for (i = 0; i < 3; i++) {
+	    diffi = pos1[i] - pos2[i];
+	    w = w + (diffi * diffi);
+	    }
+	w = w / 4.0;
+	if (w > 1.0) w = 1.0;
+
+	/* Angle beween the vectors */
+	diff = 2.0 * atan2 (sqrt (w), sqrt (1.0 - w));
+	diff = raddeg (diff);
+
+	w = 0.0;
+	d1 = 0.0;
+	d2 = 0.0;
+	for (i = 0; i < 3; i++) {
+	    w = w + (pos1[i] * pos2[i]);
+	    d1 = d1 + (pos1[i] * pos1[i]);
+	    d2 = d2 + (pos2[i] * pos2[i]);
+	    }
+	diff = acosdeg (w / (sqrt (d1) * sqrt (d2)));
+	return (diff);
+}
+
+
+/* Compute distance in degrees between two sky coordinates  away from pole */
+
+double
+wcsdiff (x1,y1,x2,y2)
+
+double	x1,y1;	/* (RA,Dec) or (Long,Lat) in degrees */
+double	x2,y2;	/* (RA,Dec) or (Long,Lat) in degrees */
+
+{
+    double xdiff, ydiff, ycos, diff;
+
+    ycos = cos (degrad ((y2 + y1) / 2.0));
+    xdiff = x2 - x1;
+    if (xdiff > 180.0)
+	xdiff = xdiff - 360.0;
+    if (xdiff < -180.0)
+	xdiff = xdiff + 360.0;
+    xdiff = xdiff / ycos;
+    ydiff = (y2 - y1);
+    diff = sqrt ((xdiff * xdiff) + (ydiff * ydiff));
+    return (diff);
+}
+
+
+/* Initialize catalog search command set by -wcscom */
+
+void
+wcscominit (wcs, i, command)
+
+struct WorldCoor *wcs;		/* World coordinate system structure */
+int	i;			/* Number of command (0-9) to initialize */
+char	*command;		/* command with %s where coordinates will go */
+
+{
+    int lcom,icom;
+
+    if (iswcs(wcs)) {
+	lcom = strlen (command);
+	if (lcom > 0) {
+	    if (wcs->command_format[i] != NULL)
+		free (wcs->command_format[i]);
+	    wcs->command_format[i] = (char *) calloc (lcom+2, 1);
+	    if (wcs->command_format[i] == NULL)
+		return;
+	    for (icom = 0; icom < lcom; icom++) {
+		if (command[icom] == '_')
+		    wcs->command_format[i][icom] = ' ';
+		else
+		    wcs->command_format[i][icom] = command[icom];
+		}
+	    wcs->command_format[i][lcom] = 0;
+	    }
+	}
+    return;
+}
+
+
+/* Execute Unix command with world coordinates (from x,y) and/or filename */
+
+void
+wcscom ( wcs, i, filename, xfile, yfile, wcstring )
+
+struct WorldCoor *wcs;		/* World coordinate system structure */
+int	i;			/* Number of command (0-9) to execute */
+char	*filename;		/* Image file name */
+double	xfile,yfile;		/* Image pixel coordinates for WCS command */
+char	*wcstring;		/* WCS String from pix2wcst() */
+{
+    char command[120];
+    char comform[120];
+    char xystring[32];
+    char *fileform, *posform, *imform;
+    int ier;
+
+    if (nowcs (wcs)) {
+	(void)fprintf(stderr,"WCSCOM: no WCS\n");
+	return;
+	}
+
+    if (wcs->command_format[i] != NULL)
+	strcpy (comform, wcs->command_format[i]);
+    else
+	strcpy (comform, "sgsc -ah %s");
+
+    if (comform[0] > 0) {
+
+	/* Create and execute search command */
+	fileform = strsrch (comform,"%f");
+	imform = strsrch (comform,"%x");
+	posform = strsrch (comform,"%s");
+	if (imform != NULL) {
+	    *(imform+1) = 's';
+	    (void)sprintf (xystring, "%.2f %.2f", xfile, yfile);
+	    if (fileform != NULL) {
+		*(fileform+1) = 's';
+		if (posform == NULL) {
+		    if (imform < fileform)
+			(void)sprintf(command, comform, xystring, filename);
+		    else
+			(void)sprintf(command, comform, filename, xystring);
+		    }
+		else if (fileform < posform) {
+		    if (imform < fileform)
+			(void)sprintf(command, comform, xystring, filename,
+				      wcstring);
+		    else if (imform < posform)
+			(void)sprintf(command, comform, filename, xystring,
+				      wcstring);
+		    else
+			(void)sprintf(command, comform, filename, wcstring,
+				      xystring);
+		    }
+		else
+		    if (imform < posform)
+			(void)sprintf(command, comform, xystring, wcstring,
+				      filename);
+		    else if (imform < fileform)
+			(void)sprintf(command, comform, wcstring, xystring,
+				      filename);
+		    else
+			(void)sprintf(command, comform, wcstring, filename,
+				      xystring);
+		}
+	    else if (posform == NULL)
+		(void)sprintf(command, comform, xystring);
+	    else if (imform < posform)
+		(void)sprintf(command, comform, xystring, wcstring);
+	    else
+		(void)sprintf(command, comform, wcstring, xystring);
+	    }
+	else if (fileform != NULL) {
+	    *(fileform+1) = 's';
+	    if (posform == NULL)
+		(void)sprintf(command, comform, filename);
+	    else if (fileform < posform)
+		(void)sprintf(command, comform, filename, wcstring);
+	    else
+		(void)sprintf(command, comform, wcstring, filename);
+	    }
+	else
+	    (void)sprintf(command, comform, wcstring);
+	ier = system (command);
+	if (ier)
+	    (void)fprintf(stderr,"WCSCOM: %s failed %d\n",command,ier);
+	}
+    return;
+}
+
+/* Initialize WCS output coordinate system for use by PIX2WCS() */
+
+void
+wcsoutinit (wcs, coorsys)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+char	*coorsys;	/* Input world coordinate system:
+			   FK4, FK5, B1950, J2000, GALACTIC, ECLIPTIC
+			   fk4, fk5, b1950, j2000, galactic, ecliptic */
+{
+    int sysout, i;
+
+    if (nowcs (wcs))
+	return;
+
+    /* If argument is null, set to image system and equinox */
+    if (coorsys == NULL || strlen (coorsys) < 1 ||
+	!strcmp(coorsys,"IMSYS") || !strcmp(coorsys,"imsys")) {
+	sysout = wcs->syswcs;
+	strcpy (wcs->radecout, wcs->radecsys);
+	wcs->eqout = wcs->equinox;
+	if (sysout == WCS_B1950) {
+	    if (wcs->eqout != 1950.0) {
+		wcs->radecout[0] = 'B';
+		sprintf (wcs->radecout+1,"%.4f", wcs->equinox);
+		i = strlen(wcs->radecout) - 1;
+		if (wcs->radecout[i] == '0')
+		    wcs->radecout[i] = (char)0;
+		i = strlen(wcs->radecout) - 1;
+		if (wcs->radecout[i] == '0')
+		    wcs->radecout[i] = (char)0;
+		i = strlen(wcs->radecout) - 1;
+		if (wcs->radecout[i] == '0')
+		    wcs->radecout[i] = (char)0;
+		}
+	    else
+		strcpy (wcs->radecout, "B1950");
+	    }
+	else if (sysout == WCS_J2000) {
+	    if (wcs->eqout != 2000.0) {
+		wcs->radecout[0] = 'J';
+		sprintf (wcs->radecout+1,"%.4f", wcs->equinox);
+		i = strlen(wcs->radecout) - 1;
+		if (wcs->radecout[i] == '0')
+		    wcs->radecout[i] = (char)0;
+		i = strlen(wcs->radecout) - 1;
+		if (wcs->radecout[i] == '0')
+		    wcs->radecout[i] = (char)0;
+		i = strlen(wcs->radecout) - 1;
+		if (wcs->radecout[i] == '0')
+		    wcs->radecout[i] = (char)0;
+		}
+	    else
+		strcpy (wcs->radecout, "J2000");
+	    }
+	}
+
+    /* Ignore new coordinate system if it is not supported */
+    else {
+	if ((sysout = wcscsys (coorsys)) < 0)
+	return;
+
+	/* Do not try to convert linear or alt-az coordinates */
+	if (sysout != wcs->syswcs &&
+	    (wcs->syswcs == WCS_LINEAR || wcs->syswcs == WCS_ALTAZ))
+	    return;
+
+	strcpy (wcs->radecout, coorsys);
+	wcs->eqout = wcsceq (coorsys);
+	}
+
+    wcs->sysout = sysout;
+    if (wcs->wcson) {
+
+	/* Set output in degrees flag and number of decimal places */
+	if (wcs->sysout == WCS_GALACTIC || wcs->sysout == WCS_ECLIPTIC ||
+	    wcs->sysout == WCS_PLANET) {
+	    wcs->degout = 1;
+	    wcs->ndec = 5;
+	    }
+	else if (wcs->sysout == WCS_ALTAZ) {
+	    wcs->degout = 1;
+	    wcs->ndec = 5;
+	    }
+	else if (wcs->sysout == WCS_NPOLE || wcs->sysout == WCS_SPA) {
+	    wcs->degout = 1;
+	    wcs->ndec = 5;
+	    }
+	else {
+	    wcs->degout = 0;
+	    wcs->ndec = 3;
+	    }
+	}
+    return;
+}
+
+
+/* Return current value of WCS output coordinate system set by -wcsout */
+char *
+getwcsout(wcs)
+struct	WorldCoor *wcs; /* World coordinate system structure */
+{
+    if (nowcs (wcs))
+	return (NULL);
+    else
+	return(wcs->radecout);
+}
+
+
+/* Initialize WCS input coordinate system for use by WCS2PIX() */
+
+void
+wcsininit (wcs, coorsys)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+char	*coorsys;	/* Input world coordinate system:
+			   FK4, FK5, B1950, J2000, GALACTIC, ECLIPTIC
+			   fk4, fk5, b1950, j2000, galactic, ecliptic */
+{
+    int sysin, i;
+
+    if (nowcs (wcs))
+	return;
+
+    /* If argument is null, set to image system and equinox */
+    if (coorsys == NULL || strlen (coorsys) < 1) {
+	wcs->sysin = wcs->syswcs;
+	strcpy (wcs->radecin, wcs->radecsys);
+	wcs->eqin = wcs->equinox;
+	if (wcs->sysin == WCS_B1950) {
+	    if (wcs->eqin != 1950.0) {
+		wcs->radecin[0] = 'B';
+		sprintf (wcs->radecin+1,"%.4f", wcs->equinox);
+		i = strlen(wcs->radecin) - 1;
+		if (wcs->radecin[i] == '0')
+		    wcs->radecin[i] = (char)0;
+		i = strlen(wcs->radecin) - 1;
+		if (wcs->radecin[i] == '0')
+		    wcs->radecin[i] = (char)0;
+		i = strlen(wcs->radecin) - 1;
+		if (wcs->radecin[i] == '0')
+		    wcs->radecin[i] = (char)0;
+		}
+	    else
+		strcpy (wcs->radecin, "B1950");
+	    }
+	else if (wcs->sysin == WCS_J2000) {
+	    if (wcs->eqin != 2000.0) {
+		wcs->radecin[0] = 'J';
+		sprintf (wcs->radecin+1,"%.4f", wcs->equinox);
+		i = strlen(wcs->radecin) - 1;
+		if (wcs->radecin[i] == '0')
+		    wcs->radecin[i] = (char)0;
+		i = strlen(wcs->radecin) - 1;
+		if (wcs->radecin[i] == '0')
+		    wcs->radecin[i] = (char)0;
+		i = strlen(wcs->radecin) - 1;
+		if (wcs->radecin[i] == '0')
+		    wcs->radecin[i] = (char)0;
+		}
+	    else
+		strcpy (wcs->radecin, "J2000");
+	    }
+	}
+
+    /* Ignore new coordinate system if it is not supported */
+    if ((sysin = wcscsys (coorsys)) < 0)
+	return;
+
+    wcs->sysin = sysin;
+    wcs->eqin = wcsceq (coorsys);
+    strcpy (wcs->radecin, coorsys);
+    return;
+}
+
+
+/* Return current value of WCS input coordinate system set by wcsininit */
+char *
+getwcsin (wcs)
+struct	WorldCoor *wcs; /* World coordinate system structure */
+{
+    if (nowcs (wcs))
+	return (NULL);
+    else
+	return (wcs->radecin);
+}
+
+
+/* Set WCS output in degrees or hh:mm:ss dd:mm:ss, returning old flag value */
+int
+setwcsdeg(wcs, new)
+struct	WorldCoor *wcs;	/* World coordinate system structure */
+int	new;		/* 1: degrees, 0: h:m:s d:m:s */
+{
+    int old;
+
+    if (nowcs (wcs))
+	return (0);
+    old = wcs->degout;
+    wcs->degout = new;
+    if (new == 1 && old == 0 && wcs->ndec == 3)
+	wcs->ndec = 6;
+    if (new == 0 && old == 1 && wcs->ndec == 5)
+	wcs->ndec = 3;
+    return(old);
+}
+
+
+/* Set number of decimal places in pix2wcst output string */
+int
+wcsndec (wcs, ndec)
+struct	WorldCoor *wcs;	/* World coordinate system structure */
+int	ndec;		/* Number of decimal places in output string */
+			/* If < 0, return current unchanged value */
+{
+    if (nowcs (wcs))
+	return (0);
+    else if (ndec >= 0)
+	wcs->ndec = ndec;
+    return (wcs->ndec);
+}
+
+
+
+/* Return current value of coordinate system */
+char *
+getradecsys(wcs)
+struct     WorldCoor *wcs; /* World coordinate system structure */
+{
+    if (nowcs (wcs))
+	return (NULL);
+    else
+	return (wcs->radecsys);
+}
+
+
+/* Set output string mode for LINEAR coordinates */
+
+void
+setwcslin (wcs, mode)
+struct	WorldCoor *wcs; /* World coordinate system structure */
+int	mode;		/* mode = 0: x y linear
+			   mode = 1: x units x units
+			   mode = 2: x y linear units */
+{
+    if (iswcs (wcs))
+	wcs->linmode = mode;
+    return;
+}
+
+
+/* Convert pixel coordinates to World Coordinate string */
+
+int
+pix2wcst (wcs, xpix, ypix, wcstring, lstr)
+
+struct	WorldCoor *wcs;	/* World coordinate system structure */
+double	xpix,ypix;	/* Image coordinates in pixels */
+char	*wcstring;	/* World coordinate string (returned) */
+int	lstr;		/* Length of world coordinate string (returned) */
+{
+	double	xpos,ypos;
+	char	rastr[32], decstr[32];
+	int	minlength, lunits, lstring;
+
+	if (nowcs (wcs)) {
+	    if (lstr > 0)
+		wcstring[0] = 0;
+	    return(0);
+	    }
+
+	pix2wcs (wcs,xpix,ypix,&xpos,&ypos);
+
+	/* If point is off scale, set string accordingly */
+	if (wcs->offscl) {
+	    (void)sprintf (wcstring,"Off map");
+	    return (1);
+	    }
+
+	/* Print coordinates in degrees */
+	else if (wcs->degout == 1) {
+	    minlength = 9 + (2 * wcs->ndec);
+	    if (lstr > minlength) {
+		deg2str (rastr, 32, xpos, wcs->ndec);
+		deg2str (decstr, 32, ypos, wcs->ndec);
+		if (wcs->tabsys)
+		    (void)sprintf (wcstring,"%s	%s", rastr, decstr);
+		else
+		    (void)sprintf (wcstring,"%s %s", rastr, decstr);
+		lstr = lstr - minlength;
+		}
+	    else {
+		if (wcs->tabsys)
+		    strncpy (wcstring,"*********	**********",lstr);
+		else
+		    strncpy (wcstring,"*******************",lstr);
+		lstr = 0;
+		}
+	    }
+
+	/* print coordinates in sexagesimal notation */
+	else if (wcs->degout == 0) {
+	    minlength = 18 + (2 * wcs->ndec);
+	    if (lstr > minlength) {
+		if (wcs->sysout == WCS_J2000 || wcs->sysout == WCS_B1950) {
+		    ra2str (rastr, 32, xpos, wcs->ndec);
+		    dec2str (decstr, 32, ypos, wcs->ndec-1);
+		    }
+		else {
+		    dec2str (rastr, 32, xpos, wcs->ndec);
+		    dec2str (decstr, 32, ypos, wcs->ndec);
+		    }
+		if (wcs->tabsys) {
+		    (void)sprintf (wcstring,"%s	%s", rastr, decstr);
+		    }
+		else {
+		    (void)sprintf (wcstring,"%s %s", rastr, decstr);
+		    }
+	        lstr = lstr - minlength;
+		}
+	    else {
+		if (wcs->tabsys) {
+		    strncpy (wcstring,"*************	*************",lstr);
+		    }
+		else {
+		    strncpy (wcstring,"**************************",lstr);
+		    }
+		lstr = 0;
+		}
+	    }
+
+	/* Label galactic coordinates */
+	if (wcs->sysout == WCS_GALACTIC) {
+	    if (lstr > 9 && wcs->printsys) {
+		if (wcs->tabsys)
+		    strcat (wcstring,"	galactic");
+		else
+		    strcat (wcstring," galactic");
+		}
+	    }
+
+	/* Label ecliptic coordinates */
+	else if (wcs->sysout == WCS_ECLIPTIC) {
+	    if (lstr > 9 && wcs->printsys) {
+		if (wcs->tabsys)
+		    strcat (wcstring,"	ecliptic");
+		else
+		    strcat (wcstring," ecliptic");
+		}
+	    }
+
+	/* Label planet coordinates */
+	else if (wcs->sysout == WCS_PLANET) {
+	    if (lstr > 9 && wcs->printsys) {
+		if (wcs->tabsys)
+		    strcat (wcstring,"	planet");
+		else
+		    strcat (wcstring," planet");
+		}
+	    }
+
+	/* Label alt-az coordinates */
+	else if (wcs->sysout == WCS_ALTAZ) {
+	    if (lstr > 7 && wcs->printsys) {
+		if (wcs->tabsys)
+		    strcat (wcstring,"	alt-az");
+		else
+		    strcat (wcstring," alt-az");
+		}
+	    }
+
+	/* Label north pole angle coordinates */
+	else if (wcs->sysout == WCS_NPOLE) {
+	    if (lstr > 7 && wcs->printsys) {
+		if (wcs->tabsys)
+		    strcat (wcstring,"	long-npa");
+		else
+		    strcat (wcstring," long-npa");
+		}
+	    }
+
+	/* Label south pole angle coordinates */
+	else if (wcs->sysout == WCS_SPA) {
+	    if (lstr > 7 && wcs->printsys) {
+		if (wcs->tabsys)
+		    strcat (wcstring,"	long-spa");
+		else
+		    strcat (wcstring," long-spa");
+		}
+	    }
+
+	/* Label equatorial coordinates */
+	else if (wcs->sysout==WCS_B1950 || wcs->sysout==WCS_J2000) {
+	    if (lstr > (int) strlen(wcs->radecout)+1 && wcs->printsys) {
+		if (wcs->tabsys)
+		    strcat (wcstring,"	");
+		else
+		    strcat (wcstring," ");
+		strcat (wcstring, wcs->radecout);
+		}
+	    }
+
+	/* Output linear coordinates */
+	else {
+	    num2str (rastr, xpos, 0, wcs->ndec);
+	    num2str (decstr, ypos, 0, wcs->ndec);
+	    lstring = strlen (rastr) + strlen (decstr) + 1;
+	    lunits = strlen (wcs->units[0]) + strlen (wcs->units[1]) + 2;
+	    if (wcs->syswcs == WCS_LINEAR && wcs->linmode == 1) {
+		if (lstr > lstring + lunits) {
+		    if (strlen (wcs->units[0]) > 0) {
+			strcat (rastr, " ");
+			strcat (rastr, wcs->units[0]);
+			}
+		    if (strlen (wcs->units[1]) > 0) {
+			strcat (decstr, " ");
+			strcat (decstr, wcs->units[1]);
+			}
+		    lstring = lstring + lunits;
+		    }
+		}
+	    if (lstr > lstring) {
+		if (wcs->tabsys)
+		    (void)sprintf (wcstring,"%s	%s", rastr, decstr);
+		else
+		    (void)sprintf (wcstring,"%s %s", rastr, decstr);
+		}
+	    else {
+		if (wcs->tabsys)
+		    strncpy (wcstring,"**********	*********",lstr);
+		else
+		    strncpy (wcstring,"*******************",lstr);
+		}
+	    if (wcs->syswcs == WCS_LINEAR && wcs->linmode != 1 &&
+		lstr > lstring + 7)
+		strcat (wcstring, " linear");
+	    if (wcs->syswcs == WCS_LINEAR && wcs->linmode == 2 &&
+		lstr > lstring + lunits + 7) {
+		if (strlen (wcs->units[0]) > 0) {
+		    strcat (wcstring, " ");
+		    strcat (wcstring, wcs->units[0]);
+		    }
+		if (strlen (wcs->units[1]) > 0) {
+		    strcat (wcstring, " ");
+		    strcat (wcstring, wcs->units[1]);
+		    }
+		    
+		}
+	    }
+	return (1);
+}
+
+
+/* Convert pixel coordinates to World Coordinates */
+
+void
+pix2wcs (wcs,xpix,ypix,xpos,ypos)
+
+struct WorldCoor *wcs;		/* World coordinate system structure */
+double	xpix,ypix;	/* x and y image coordinates in pixels */
+double	*xpos,*ypos;	/* RA and Dec in degrees (returned) */
+{
+    double	xpi, ypi, xp, yp;
+    double	eqin, eqout;
+    int wcspos();
+
+    if (nowcs (wcs))
+	return;
+    wcs->xpix = xpix;
+    wcs->ypix = ypix;
+    wcs->zpix = zpix;
+    wcs->offscl = 0;
+
+    /* If this WCS is converted from another WCS rather than pixels, convert now */
+    if (wcs->wcs != NULL) {
+	pix2wcs (wcs->wcs, xpix, ypix, &xpi, &ypi);
+	}
+    else {
+	pix2foc (wcs, xpix, ypix, &xpi, &ypi);
+	}
+
+    /* Convert image coordinates to sky coordinates */
+
+    /* Use Digitized Sky Survey plate fit */
+    if (wcs->prjcode == WCS_DSS) {
+	if (dsspos (xpi, ypi, wcs, &xp, &yp))
+	    wcs->offscl = 1;
+	}
+
+    /* Use SAO plate fit */
+    else if (wcs->prjcode == WCS_PLT) {
+	if (platepos (xpi, ypi, wcs, &xp, &yp))
+	    wcs->offscl = 1;
+	}
+
+    /* Use NOAO IRAF corrected plane tangent projection */
+    else if (wcs->prjcode == WCS_TNX) {
+	if (tnxpos (xpi, ypi, wcs, &xp, &yp))
+	    wcs->offscl = 1;
+	}
+
+    /* Use Classic AIPS projections */
+    else if (wcs->wcsproj == WCS_OLD || wcs->prjcode <= 0) {
+	if (worldpos (xpi, ypi, wcs, &xp, &yp))
+	    wcs->offscl = 1;
+	}
+
+    /* Use Mark Calabretta's WCSLIB projections */
+    else if (wcspos (xpi, ypi, wcs, &xp, &yp))
+	wcs->offscl = 1;
+	    	
+
+    /* Do not change coordinates if offscale */
+    if (wcs->offscl) {
+	*xpos = 0.0;
+	*ypos = 0.0;
+	}
+    else {
+
+	/* Convert coordinates to output system, if not LINEAR */
+        if (wcs->prjcode > 0) {
+
+	    /* Convert coordinates to desired output system */
+	    eqin = wcs->equinox;
+	    eqout = wcs->eqout;
+	    wcscon (wcs->syswcs,wcs->sysout,eqin,eqout,&xp,&yp,wcs->epoch);
+	    }
+	if (wcs->latbase == 90)
+	    yp = 90.0 - yp;
+	else if (wcs->latbase == -90)
+	    yp = yp - 90.0;
+	wcs->xpos = xp;
+	wcs->ypos = yp;
+	*xpos = xp;
+	*ypos = yp;
+	}
+
+    /* Keep RA/longitude within range if spherical coordinate output
+       (Not LINEAR or XY) */
+    if (wcs->sysout > 0 && wcs->sysout != 6 && wcs->sysout != 10) {
+	if (*xpos < 0.0)
+	    *xpos = *xpos + 360.0;
+	else if (*xpos > 360.0)
+	    *xpos = *xpos - 360.0;
+	}
+
+    return;
+}
+
+
+/* Convert World Coordinates to pixel coordinates */
+
+void
+wcs2pix (wcs, xpos, ypos, xpix, ypix, offscl)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double	xpos,ypos;	/* World coordinates in degrees */
+double	*xpix,*ypix;	/* Image coordinates in pixels */
+int	*offscl;	/* 0 if within bounds, else off scale */
+{
+    wcsc2pix (wcs, xpos, ypos, wcs->radecin, xpix, ypix, offscl);
+    return;
+}
+
+/* Convert World Coordinates to pixel coordinates */
+
+void
+wcsc2pix (wcs, xpos, ypos, coorsys, xpix, ypix, offscl)
+
+struct WorldCoor *wcs;	/* World coordinate system structure */
+double	xpos,ypos;	/* World coordinates in degrees */
+char	*coorsys;	/* Input world coordinate system:
+			   FK4, FK5, B1950, J2000, GALACTIC, ECLIPTIC
+			   fk4, fk5, b1950, j2000, galactic, ecliptic
+			   * If NULL, use image WCS */
+double	*xpix,*ypix;	/* Image coordinates in pixels */
+int	*offscl;	/* 0 if within bounds, else off scale */
+{
+    struct WorldCoor *depwcs;	/* Dependent WCS structure */
+    double xp, yp, xpi, ypi;
+    double eqin, eqout;
+    int sysin;
+    int wcspix();
+
+    if (nowcs (wcs))
+	return;
+
+    *offscl = 0;
+    xp = xpos;
+    yp = ypos;
+    if (wcs->latbase == 90)
+	yp = 90.0 - yp;
+    else if (wcs->latbase == -90)
+	yp = yp - 90.0;
+    if (coorsys == NULL) {
+	sysin = wcs->syswcs;
+	eqin = wcs->equinox;
+	}
+    else {
+	sysin = wcscsys (coorsys);
+	eqin = wcsceq (coorsys);
+	}
+    wcs->zpix = 1.0;
+
+    /* Convert coordinates to same system as image */
+    if (sysin > 0 && sysin != 6 && sysin != 10) {
+	eqout = wcs->equinox;
+	wcscon (sysin, wcs->syswcs, eqin, eqout, &xp, &yp, wcs->epoch);
+	}
+
+    /* Convert sky coordinates to image coordinates */
+
+    /* Use Digitized Sky Survey plate fit */
+    if (wcs->prjcode == WCS_DSS) {
+	if (dsspix (xp, yp, wcs, &xpi, &ypi))
+	    *offscl = 1;
+	}
+
+    /* Use SAO polynomial plate fit */
+    else if (wcs->prjcode == WCS_PLT) {
+	if (platepix (xp, yp, wcs, &xpi, &ypi))
+	    *offscl = 1;
+	}
+
+    /* Use NOAO IRAF corrected plane tangent projection */
+    else if (wcs->prjcode == WCS_TNX) {
+	if (tnxpix (xp, yp, wcs, &xpi, &ypi))
+	    *offscl = 1;
+	}
+
+    /* Use Classic AIPS projections */
+    else if (wcs->wcsproj == WCS_OLD || wcs->prjcode <= 0) {
+	if (worldpix (xp, yp, wcs, &xpi, &ypi))
+	    *offscl = 1;
+	}
+
+    /* Use Mark Calabretta's WCSLIB projections */
+    else if (wcspix (xp, yp, wcs, &xpi, &ypi)) {
+	*offscl = 1;
+	}
+
+    /* If this WCS is converted from another WCS rather than pixels, convert now */
+    if (wcs->wcs != NULL) {
+	wcsc2pix (wcs->wcs, xpi, ypi, NULL, xpix, ypix, offscl);
+	}
+    else {
+	foc2pix (wcs, xpi, ypi, xpix, ypix);
+
+	/* Set off-scale flag to 2 if off image but within bounds of projection */
+	if (!*offscl) {
+	    if (*xpix < 0.5 || *ypix < 0.5)
+		*offscl = 2;
+	    else if (*xpix > wcs->nxpix + 0.5 || *ypix > wcs->nypix + 0.5)
+		*offscl = 2;
+	    }
+	}
+
+    wcs->offscl = *offscl;
+    wcs->xpos = xpos;
+    wcs->ypos = ypos;
+    wcs->xpix = *xpix;
+    wcs->ypix = *ypix;
+
+    /* If this WCS is converted to another WCS rather than pixels, convert now */
+    if (wcs->wcsdep != NULL) {
+	xpos = *xpix;
+	ypos = *ypix;
+	depwcs = wcs->wcsdep;
+	wcsc2pix (wcs->wcsdep, xpos, ypos, depwcs->radecin, xpix, ypix, offscl);
+	}
+    return;
+}
+
+
+int
+wcspos (xpix, ypix, wcs, xpos, ypos)
+
+/* Input: */
+double  xpix;          /* x pixel number  (RA or long without rotation) */
+double  ypix;          /* y pixel number  (dec or lat without rotation) */
+struct WorldCoor *wcs;  /* WCS parameter structure */
+
+/* Output: */
+double  *xpos;           /* x (RA) coordinate (deg) */
+double  *ypos;           /* y (dec) coordinate (deg) */
+{
+    int offscl;
+    int i;
+    int wcsrev();
+    double wcscrd[4], imgcrd[4], pixcrd[4];
+    double phi, theta;
+    
+    *xpos = 0.0;
+    *ypos = 0.0;
+
+    pixcrd[0] = xpix;
+    pixcrd[1] = ypix;
+    if (wcs->prjcode == WCS_CSC || wcs->prjcode == WCS_QSC ||
+	wcs->prjcode == WCS_TSC)
+	pixcrd[2] = (double) (izpix + 1);
+    else
+	pixcrd[2] = zpix;
+    pixcrd[3] = 1.0;
+    for (i = 0; i < 4; i++)
+	imgcrd[i] = 0.0;
+    offscl = wcsrev ((void *)&wcs->ctype, &wcs->wcsl, pixcrd, &wcs->lin, imgcrd,
+		    &wcs->prj, &phi, &theta, wcs->crval, &wcs->cel, wcscrd);
+    if (offscl == 0) {
+	*xpos = wcscrd[wcs->wcsl.lng];
+	*ypos = wcscrd[wcs->wcsl.lat];
+	}
+    return (offscl);
+}
+
+int
+wcspix (xpos, ypos, wcs, xpix, ypix)
+
+/* Input: */
+double  xpos;           /* x (RA) coordinate (deg) */
+double  ypos;           /* y (dec) coordinate (deg) */
+struct WorldCoor *wcs;  /* WCS parameter structure */
+
+/* Output: */
+double  *xpix;          /* x pixel number  (RA or long without rotation) */
+double  *ypix;          /* y pixel number  (dec or lat without rotation) */
+
+{
+    int offscl;
+    int wcsfwd();
+    double wcscrd[4], imgcrd[4], pixcrd[4];
+    double phi, theta;
+
+    *xpix = 0.0;
+    *ypix = 0.0;
+    if (wcs->wcsl.flag != WCSSET) {
+	if (wcsset (wcs->lin.naxis, (void *)&wcs->ctype, &wcs->wcsl) )
+	    return (1);
+	}
+
+    /* Set input for WCSLIB subroutines */
+    wcscrd[0] = 0.0;
+    wcscrd[1] = 0.0;
+    wcscrd[2] = 0.0;
+    wcscrd[3] = 0.0;
+    wcscrd[wcs->wcsl.lng] = xpos;
+    wcscrd[wcs->wcsl.lat] = ypos;
+
+    /* Initialize output for WCSLIB subroutines */
+    pixcrd[0] = 0.0;
+    pixcrd[1] = 0.0;
+    pixcrd[2] = 1.0;
+    pixcrd[3] = 1.0;
+    imgcrd[0] = 0.0;
+    imgcrd[1] = 0.0;
+    imgcrd[2] = 1.0;
+    imgcrd[3] = 1.0;
+
+    /* Invoke WCSLIB subroutines for coordinate conversion */
+    offscl = wcsfwd ((void *)&wcs->ctype, &wcs->wcsl, wcscrd, wcs->crval, &wcs->cel,
+		     &phi, &theta, &wcs->prj, imgcrd, &wcs->lin, pixcrd);
+
+    if (!offscl) {
+	*xpix = pixcrd[0];
+	*ypix = pixcrd[1];
+	if (wcs->prjcode == WCS_CSC || wcs->prjcode == WCS_QSC ||
+	    wcs->prjcode == WCS_TSC)
+	    wcs->zpix = pixcrd[2] - 1.0;
+	else
+	    wcs->zpix = pixcrd[2];
+	}
+    return (offscl);
+}
+
+
+/* Set third dimension for cube projections */
+
+int
+wcszin (izpix0)
+
+int	izpix0;		/* coordinate in third dimension
+			   (if < 0, return current value without changing it */
+{
+    if (izpix0 > -1) {
+	izpix = izpix0;
+	zpix = (double) izpix0;
+	}
+    return (izpix);
+}
+
+
+/* Return third dimension for cube projections */
+
+int
+wcszout (wcs)
+
+struct WorldCoor *wcs;  /* WCS parameter structure */
+{
+    return ((int) wcs->zpix);
+}
+
+/* Set file name for error messages */
+void
+setwcsfile (filename)
+char *filename;	/* FITS or IRAF file with WCS */
+{   if (strlen (filename) < 256)
+	strcpy (wcsfile, filename);
+    else
+	strncpy (wcsfile, filename, 255);
+    return; }
+
+/* Set error message */
+void
+setwcserr (errmsg)
+char *errmsg;	/* Error mesage  < 80 char */
+{ strcpy (wcserrmsg, errmsg); return; }
+
+/* Print error message */
+void
+wcserr ()
+{   if (strlen (wcsfile) > 0)
+	fprintf (stderr, "%s in file %s\n",wcserrmsg, wcsfile);
+    else
+	fprintf (stderr, "%s\n",wcserrmsg);
+    return; }
+
+
+/* Flag to use AIPS WCS subroutines instead of WCSLIB */
+void
+setdefwcs (wp)
+int wp;
+{ wcsproj0 = wp; return; }
+
+int
+getdefwcs ()
+{ return (wcsproj0); }
+
+/* Save output default coordinate system */
+static char wcscoor0[16];
+
+void
+savewcscoor (wcscoor)
+char *wcscoor;
+{ strcpy (wcscoor0, wcscoor); return; }
+
+/* Return preset output default coordinate system */
+char *
+getwcscoor ()
+{ return (wcscoor0); }
+
+
+/* Save default commands */
+static char *wcscom0[10];
+
+void
+savewcscom (i, wcscom)
+int i;
+char *wcscom;
+{
+    int lcom;
+    if (i < 0) i = 0;
+    else if (i > 9) i = 9;
+    lcom = strlen (wcscom) + 2;
+    wcscom0[i] = (char *) calloc (lcom, 1);
+    if (wcscom0[i] != NULL)
+	strcpy (wcscom0[i], wcscom);
+    return;
+}
+
+void
+setwcscom (wcs)
+struct WorldCoor *wcs;  /* WCS parameter structure */
+{
+    char envar[16];
+    int i;
+    char *str;
+    if (nowcs(wcs))
+	return;
+    for (i = 0; i < 10; i++) {
+	if (i == 0)
+	    strcpy (envar, "WCS_COMMAND");
+	else
+	    sprintf (envar, "WCS_COMMAND%d", i);
+	if (wcscom0[i] != NULL)
+	    wcscominit (wcs, i, wcscom0[i]);
+	else if ((str = getenv (envar)) != NULL)
+	    wcscominit (wcs, i, str);
+	else if (i == 1)
+	    wcscominit (wcs, i, "sua2 -ah %s");	/* F1= Search USNO-A2.0 Catalog */
+	else if (i == 2)
+	    wcscominit (wcs, i, "sgsc -ah %s");	/* F2= Search HST GSC */
+	else if (i == 3)
+	    wcscominit (wcs, i, "sty2 -ah %s"); /* F3= Search Tycho-2 Catalog */
+	else if (i == 4)
+	    wcscominit (wcs, i, "sppm -ah %s");	/* F4= Search PPM Catalog */
+	else if (i == 5)
+	    wcscominit (wcs, i, "ssao -ah %s");	/* F5= Search SAO Catalog */
+	else
+	    wcs->command_format[i] = NULL;
+	}
+    return;
+}
+
+char *
+getwcscom (i)
+int i;
+{ return (wcscom0[i]); }
+
+
+void
+freewcscom (wcs)
+struct WorldCoor *wcs;  /* WCS parameter structure */
+{
+    int i;
+    for (i = 0; i < 10; i++) {
+	if (wcscom0[i] != NULL) {
+	    free (wcscom0[i]);
+	    wcscom0[i] = NULL;
+	    }
+	}
+    if (iswcs (wcs)) {
+	for (i = 0; i < 10; i++) {
+	    if (wcs->command_format[i] != NULL) {
+		free (wcs->command_format[i]);
+		}
+	    }
+	}
+    return;
+}
+
+int
+cpwcs (header, cwcs)
+
+char **header;	/* Pointer to start of FITS header */
+char *cwcs;	/* Keyword suffix character for output WCS */
+{
+    double tnum;
+    int dkwd[100];
+    int i, maxnkwd, ikwd, nleft, lbuff, lhead, nkwd, nbytes;
+    int nkwdw;
+    char **kwd;
+    char *newhead, *oldhead;
+    char kwdc[16], keyword[16];
+    char tstr[80];
+
+    /* Allocate array of keywords to be transferred */
+    maxnkwd = 100;
+    kwd = (char **)calloc (maxnkwd, sizeof(char *));
+    for (ikwd = 0; ikwd < maxnkwd; ikwd++)
+	kwd[ikwd] = (char *) calloc (16, 1);
+
+    /* Make list of all possible keywords to be transferred */
+    nkwd = 0;
+    strcpy (kwd[++nkwd], "EPOCH");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "EQUINOX");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "RADECSYS");
+    dkwd[nkwd] = 0;
+    strcpy (kwd[++nkwd], "CTYPE1");
+    dkwd[nkwd] = 0;
+    strcpy (kwd[++nkwd], "CTYPE2");
+    dkwd[nkwd] = 0;
+    strcpy (kwd[++nkwd], "CRVAL1");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CRVAL2");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CDELT1");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CDELT2");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CRPIX1");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CRPIX2");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CROTA1");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CROTA2");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CD1_1");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CD1_2");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CD2_1");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "CD2_2");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "PC1_1");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "PC1_2");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "PC2_1");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "PC2_2");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "PC001001");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "PC001002");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "PC002001");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "PC002002");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "LATPOLE");
+    dkwd[nkwd] = 1;
+    strcpy (kwd[++nkwd], "LONPOLE");
+    dkwd[nkwd] = 1;
+    for (i = 1; i < 13; i++) {
+	sprintf (keyword,"CO1_%d", i);
+	strcpy (kwd[++nkwd], keyword);
+	dkwd[nkwd] = 1;
+	}
+    for (i = 1; i < 13; i++) {
+	sprintf (keyword,"CO2_%d", i);
+	strcpy (kwd[++nkwd], keyword);
+	dkwd[nkwd] = 1;
+	}
+    for (i = 0; i < 10; i++) {
+	sprintf (keyword,"PROJP%d", i);
+	strcpy (kwd[++nkwd], keyword);
+	dkwd[nkwd] = 1;
+	}
+    for (i = 0; i < 10; i++) {
+	sprintf (keyword,"PV1_%d", i);
+	strcpy (kwd[++nkwd], keyword);
+	dkwd[nkwd] = 1;
+	}
+    for (i = 0; i < 10; i++) {
+	sprintf (keyword,"PV2_%d", i);
+	strcpy (kwd[++nkwd], keyword);
+	dkwd[nkwd] = 1;
+	}
+
+    /* Allocate new header buffer if needed */
+    lhead = (ksearch (*header, "END") - *header)  + 80;
+    lbuff = gethlength (*header);
+    nleft = (lbuff - lhead) / 80;
+    if (nleft < nkwd) {
+	nbytes = lhead + (nkwd * 80) + 400;
+	newhead = (char *) calloc (1, nbytes);
+	strncpy (newhead, *header, lhead);
+	oldhead = *header;
+	header = &newhead;
+	free (oldhead);
+	}
+    
+    /* Copy keywords to new WCS ID in header */
+    nkwdw = 0;
+    for (i = 0; i < nkwd; i++) {
+	if (dkwd[i]) {
+	    if (hgetr8 (*header, kwd[i], &tnum)) {
+		nkwdw++;
+		if (!strncmp (kwd[i], "PC0", 3)) {
+		    if (!strcmp (kwd[i], "PC001001"))
+			strcpy (kwdc, "PC1_1");
+		    else if (!strcmp (kwd[i], "PC001002"))
+			strcpy (kwdc, "PC1_2");
+		    else if (!strcmp (kwd[i], "PC002001"))
+			strcpy (kwdc, "PC2_1");
+		    else
+			strcpy (kwdc, "PC2_2");
+		    }
+		else
+		    strcpy (kwdc, kwd[i]);
+		strncat (kwdc, cwcs, 1);
+		(void)hputr8 (*header, kwdc, tnum);
+		}
+	    }
+	else {
+	    if (hgets (*header, kwd[i], 80, tstr)) {
+		nkwdw++;
+		if (!strncmp (kwd[i], "RADECSYS", 8))
+		    strcpy (kwdc, "RADECSY");
+		else
+		    strcpy (kwdc, kwd[i]);
+		strncat (kwdc, cwcs, 1);
+		hputs (*header, kwdc, tstr);
+		}
+	    }
+	}
+    
+    /* Free keyword list array */
+    for (ikwd = 0; ikwd < maxnkwd; ikwd++)
+	free (kwd[ikwd]);
+    free (kwd);
+    kwd = NULL;
+    return (nkwdw);
+}
+
+
+/* Oct 28 1994	new program
+ * Dec 21 1994	Implement CD rotation matrix
+ * Dec 22 1994	Allow RA and DEC to be either x,y or y,x
+ *
+ * Mar  6 1995	Add Digital Sky Survey plate fit
+ * May  2 1995	Add prototype of PIX2WCST to WCSCOM
+ * May 25 1995	Print leading zero for hours and degrees
+ * Jun 21 1995	Add WCS2PIX to get pixels from WCS
+ * Jun 21 1995	Read plate scale from FITS header for plate solution
+ * Jul  6 1995	Pass WCS structure as argument; malloc it in WCSINIT
+ * Jul  6 1995	Check string lengths in PIX2WCST
+ * Aug 16 1995	Add galactic coordinate conversion to PIX2WCST
+ * Aug 17 1995	Return 0 from iswcs if wcs structure is not yet set
+ * Sep  8 1995	Do not include malloc.h if VMS
+ * Sep  8 1995	Check for legal WCS before trying anything
+ * Sep  8 1995	Do not try to set WCS if missing key keywords
+ * Oct 18 1995	Add WCSCENT and WCSDIST to print center and size of image
+ * Nov  6 1995	Include stdlib.h instead of malloc.h
+ * Dec  6 1995	Fix format statement in PIX2WCST
+ * Dec 19 1995	Change MALLOC to CALLOC to initialize array to zeroes
+ * Dec 19 1995	Explicitly initialize rotation matrix and yinc
+ * Dec 22 1995	If SECPIX is set, use approximate WCS
+ * Dec 22 1995	Always print coordinate system
+ *
+ * Jan 12 1996	Use plane-tangent, not linear, projection if SECPIX is set
+ * Jan 12 1996  Add WCSSET to set WCS without an image
+ * Feb 15 1996	Replace all calls to HGETC with HGETS
+ * Feb 20 1996	Add tab table output from PIX2WCST
+ * Apr  2 1996	Convert all equinoxes to B1950 or J2000
+ * Apr 26 1996	Get and use image epoch for accurate FK4/FK5 conversions
+ * May 16 1996	Clean up internal documentation
+ * May 17 1996	Return width in right ascension degrees, not sky degrees
+ * May 24 1996	Remove extraneous print command from WCSSIZE
+ * May 28 1996	Add NOWCS and WCSSHIFT subroutines
+ * Jun 11 1996	Drop unused variables after running lint
+ * Jun 12 1996	Set equinox as well as system in WCSSHIFT
+ * Jun 14 1996	Make DSS keyword searches more robust
+ * Jul  1 1996	Allow for SECPIX1 and SECPIX2 keywords
+ * Jul  2 1996	Test for CTYPE1 instead of CRVAL1
+ * Jul  5 1996	Declare all subroutines in wcs.h
+ * Jul 19 1996	Add subroutine WCSFULL to return real image size
+ * Aug 12 1996	Allow systemless coordinates which cannot be converted
+ * Aug 15 1996	Allow LINEAR WCS to pass numbers through transparently
+ * Aug 15 1996	Add WCSERR to print error message under calling program control
+ * Aug 16 1996	Add latitude and longitude as image coordinate types
+ * Aug 26 1996	Fix arguments to HLENGTH in WCSNINIT
+ * Aug 28 1996	Explicitly set OFFSCL in WCS2PIX if coordinates outside image
+ * Sep  3 1996	Return computed pixel values even if they are offscale
+ * Sep  6 1996	Allow filename to be passed by WCSCOM
+ * Oct  8 1996	Default to 2000 for EQUINOX and EPOCH and FK5 for RADECSYS
+ * Oct  8 1996	If EPOCH is 0 and EQUINOX is not set, default to 1950 and FK4
+ * Oct 15 1996  Add comparison when testing an assignment
+ * Oct 16 1996  Allow PIXEL CTYPE which means WCS is same as image coordinates
+ * Oct 21 1996	Add WCS_COMMAND environment variable
+ * Oct 25 1996	Add image scale to WCSCENT
+ * Oct 30 1996	Fix bugs in WCS2PIX
+ * Oct 31 1996	Fix CD matrix rotation angle computation
+ * Oct 31 1996	Use inline degree <-> radian conversion functions
+ * Nov  1 1996	Add option to change number of decimal places in PIX2WCST
+ * Nov  5 1996	Set wcs->crot to 1 if rotation matrix is used
+ * Dec  2 1996	Add altitide/azimuth coordinates
+ * Dec 13 1996	Fix search format setting from environment
+ *
+ * Jan 22 1997	Add ifdef for Eric Mandel (SAOtng)
+ * Feb  5 1997	Add wcsout for Eric Mandel
+ * Mar 20 1997	Drop unused variable STR in WCSCOM
+ * May 21 1997	Do not make pixel coordinates mod 360 in PIX2WCST
+ * May 22 1997	Add PIXEL prjcode = -1;
+ * Jul 11 1997	Get center pixel x and y from header even if no WCS
+ * Aug  7 1997	Add NOAO PIXSCALi keywords for default WCS
+ * Oct 15 1997	Do not reset reference pixel in WCSSHIFT
+ * Oct 20 1997	Set chip rotation
+ * Oct 24 1997	Keep longitudes between 0 and 360, not -180 and +180
+ * Nov  5 1997	Do no compute crot and srot; they are now computed in worldpos
+ * Dec 16 1997	Set rotation and axis increments from CD matrix
+ *
+ * Jan  6 1998	Deal with J2000 and B1950 as EQUINOX values (from ST)
+ * Jan  7 1998	Read INSTRUME and DETECTOR header keywords
+ * Jan  7 1998	Fix tab-separated output
+ * Jan  9 1998	Precess coordinates if either FITS projection or *DSS plate*
+ * Jan 16 1998	Change PTYPE to not include initial hyphen
+ * Jan 16 1998	Change WCSSET to WCSXINIT to avoid conflict with Calabretta
+ * Jan 23 1998	Change PCODE to PRJCODE to avoid conflict with Calabretta
+ * Jan 27 1998	Add LATPOLE and LONGPOLE for Calabretta projections
+ * Feb  5 1998	Make cd and dc into vectors; use matinv() to invert cd
+ * Feb  5 1998	In wcsoutinit(), check that corsys is a valid pointer
+ * Feb 18 1998	Fix bugs for Calabretta projections
+ * Feb 19 1998	Add wcs structure access subroutines from Eric Mandel
+ * Feb 19 1998	Add wcsreset() to make sure derived values are reset
+ * Feb 19 1998	Always set oldwcs to 1 if NCP projection
+ * Feb 19 1998	Add subroutine to set oldwcs default
+ * Feb 20 1998	Initialize projection types one at a time for SunOS C
+ * Feb 23 1998	Add TNX projection from NOAO; treat it as TAN
+ * Feb 23 1998	Compute size based on max and min coordinates, not sides
+ * Feb 26 1998	Add code to set center pixel if part of detector array
+ * Mar  6 1998	Write 8-character values to RADECSYS
+ * Mar  9 1998	Add naxis to WCS structure
+ * Mar 16 1998	Use separate subroutine for IRAF TNX projection
+ * Mar 20 1998	Set PC matrix if more than two axes and it's not in header
+ * Mar 20 1998	Reset lin flag in WCSRESET if CDELTn
+ * Mar 20 1998	Set CD matrix with CDELTs and CROTA in wcsinit and wcsreset
+ * Mar 20 1998	Allow initialization of rotation angle alone
+ * Mar 23 1998	Use dsspos() and dsspix() instead of platepos() and platepix()
+ * Mar 24 1998	Set up PLT/PLATE plate polynomial fit using platepos() and platepix()
+ * Mar 25 1998	Read plate fit coefficients from header in getwcscoeffs()
+ * Mar 27 1998	Check for FITS WCS before DSS WCS
+ * Mar 27 1998	Compute scale from edges if xinc and yinc not set in wcscent()
+ * Apr  6 1998	Change plate coefficient keywords from PLTij to COi_j
+ * Apr  6 1998	Read all coefficients in line instead of with subroutine
+ * Apr  7 1998	Change amd_i_coeff to i_coeff
+ * Apr  8 1998	Add wcseqset to change equinox after wcs has been set
+ * Apr 10 1998	Use separate counters for x and y polynomial coefficients
+ * Apr 13 1998	Use CD/CDELT+CDROTA if oldwcs is set
+ * Apr 14 1998	Use codes instead of strings for various coordinate systems
+ * Apr 14 1998	Separate input coordinate conversion from output conversion
+ * Apr 14 1998	Use wcscon() for most coordinate conversion
+ * Apr 17 1998	Always compute cdelt[]
+ * Apr 17 1998	Deal with reversed axis more correctly
+ * Apr 17 1998	Compute rotation angle and approximate CDELTn for polynomial
+ * Apr 23 1998	Deprecate xref/yref in favor of crval[]
+ * Apr 23 1998	Deprecate xrefpix/yrefpix in favor of crpix[]
+ * Apr 23 1998	Add LINEAR to coordinate system types
+ * Apr 23 1998	Always use AIPS subroutines for LINEAR or PIXEL
+ * Apr 24 1998	Format linear coordinates better
+ * Apr 28 1998	Change coordinate system flags to WCS_*
+ * Apr 28 1998  Change projection flags to WCS_*
+ * Apr 28 1998	Add subroutine wcsc2pix for coordinates to pixels with system
+ * Apr 28 1998	Add setlinmode() to set output string mode for LINEAR coordinates
+ * Apr 30 1998	Fix bug by setting degree flag for lat and long in wcsinit()
+ * Apr 30 1998	Allow leading "-"s in projecting in wcsxinit()
+ * May  1 1998	Assign new output coordinate system only if legitimate system
+ * May  1 1998	Do not allow oldwcs=1 unless allowed projection
+ * May  4 1998	Fix bug in units reading for LINEAR coordinates
+ * May  6 1998	Initialize to no CD matrix
+ * May  6 1998	Use TAN instead of TNX if oldwcs
+ * May 12 1998	Set 3rd and 4th coordinates in wcspos()
+ * May 12 1998	Return *xpos and *ypos = 0 in pix2wcs() if offscale
+ * May 12 1998	Declare undeclared external subroutines after lint
+ * May 13 1998	Add equinox conversion to specified output equinox
+ * May 13 1998	Set output or input system to image with null argument
+ * May 15 1998	Return reference pixel, cdelts, and rotation for DSS
+ * May 20 1998	Fix bad bug so setlinmode() is no-op if wcs not set
+ * May 20 1998	Fix bug so getwcsout() returns null pointer if wcs not set
+ * May 27 1998	Change WCS_LPR back to WCS_LIN; allow CAR in oldwcs
+ * May 28 1998	Go back to old WCSFULL, computing height and width from center
+ * May 29 1998	Add wcskinit() to initialize WCS from arguments
+ * May 29 1998	Add wcstype() to set projection from arguments
+ * May 29 1998	Add wcscdset(), and wcsdeltset() to set scale from arguments
+ * Jun  1 1998	In wcstype(), reconstruct ctype for WCS structure
+ * Jun 11 1998	Split off header-dependent subroutines to wcsinit.c
+ * Jun 18 1998	Add wcspcset() for PC matrix initialization
+ * Jun 24 1998	Add string lengths to ra2str(), dec2str, and deg2str() calls
+ * Jun 25 1998	Use AIPS software for CAR projection
+ * Jun 25 1998	Add wcsndec to set number of decimal places in output string
+ * Jul  6 1998	Add wcszin() and wcszout() to use third dimension of images
+ * Jul  7 1998	Change setlinmode() to setwcslin(); setdegout() to setwcsdeg()
+ * Jul 10 1998	Initialize matrices correctly for naxis > 2 in wcs<>set()
+ * Jul 16 1998	Initialize coordinates to be returned in wcspos()
+ * Jul 17 1998	Link lin structure arrays to wcs structure arrays
+ * Jul 20 1998	In wcscdset() compute sign of xinc and yinc from CD1_1, CD 2_2
+ * Jul 20 1998	In wcscdset() compute sign of rotation based on CD1_1, CD 1_2
+ * Jul 22 1998	Add wcslibrot() to compute lin() rotation matrix
+ * Jul 30 1998	Set wcs->naxes and lin.naxis in wcsxinit() and wcskinit()
+ * Aug  5 1998	Use old WCS subroutines to deal with COE projection (for ESO)
+ * Aug 14 1998	Add option to print image coordinates with wcscom()
+ * Aug 14 1998	Add multiple command options to wcscom*()
+ * Aug 31 1998	Declare undeclared arguments to wcspcset()
+ * Sep  3 1998	Set CD rotation and cdelts from sky axis position angles
+ * Sep 16 1998	Add option to use North Polar Angle instead of Latitude
+ * Sep 29 1998	Initialize additional WCS commands from the environment
+ * Oct 14 1998	Fix bug in wcssize() which didn't divide dra by cos(dec)
+ * Nov 12 1998	Fix sign of CROTA when either axis is reflected
+ * Dec  2 1998	Fix non-arcsecond scale factors in wcscent()
+ * Dec  2 1998	Add PLANET coordinate system to pix2wcst()
+
+ * Jan 20 1999	Free lin.imgpix and lin.piximg in wcsfree()
+ * Feb 22 1999	Fix bug setting latitude reference value of latbase != 0
+ * Feb 22 1999	Fix bug so that quad cube faces are 0-5, not 1-6
+ * Mar 16 1999	Always initialize all 4 imgcrds and pixcrds in wcspix()
+ * Mar 16 1999	Always return (0,0) from wcs2pix if offscale
+ * Apr  7 1999	Add code to put file name in error messages
+ * Apr  7 1999	Document utility subroutines at end of file
+ * May  6 1999	Fix bug printing height of LINEAR image
+ * Jun 16 1999	Add wcsrange() to return image RA and Dec limits
+ * Jul  8 1999	Always use FK5 and FK4 instead of J2000 and B1950 in RADECSYS
+ * Aug 16 1999	Print dd:mm:ss dd:mm:ss if not J2000 or B1950 output
+ * Aug 20 1999	Add WCS string argument to wcscom(); don't compute it there
+ * Aug 20 1999	Change F3 WCS command default from Tycho to ACT
+ * Oct 15 1999	Free wcs using wcsfree()
+ * Oct 21 1999	Drop declarations of unused functions after lint
+ * Oct 25 1999	Drop out of wcsfree() if wcs is null pointer
+ * Nov 17 1999	Fix bug which caused software to miss NCP projection
+ *
+ * Jan 24 2000	Default to AIPS for NCP, CAR, and COE proj.; if -z use WCSLIB
+ * Feb 24 2000	If coorsys is null in wcsc2pix, wcs->radecin is assumed
+ * May 10 2000	In wcstype(), default to WCS_LIN, not error (after Bill Joye)
+ * Jun 22 2000	In wcsrotset(), leave rotation angle alone in 1-d image
+ * Jul  3 2000	Initialize wcscrd[] to zero in wcspix()
+ *
+ * Feb 20 2001	Add recursion to wcs2pix() and pix2wcs() for dependent WCS's
+ * Mar 20 2001	Add braces to avoid ambiguity in if/else groupings
+ * Mar 22 2001	Free WCS structure in wcsfree even if it is not filled
+ * Sep 12 2001	Fix bug which omitted tab in pix2wcst() galactic coord output
+ *
+ * Mar  7 2002	Fix bug which gave wrong pa's and rotation for reflected RA
+ *		(but correct WCS conversions!)
+ * Mar 28 2002	Add SZP projection
+ * Apr  3 2002	Synchronize projection types with other subroutines
+ * Apr  3 2002	Drop special cases of projections
+ * Apr  9 2002	Implement inversion of multiple WCSs in wcsc2pix()
+ * Apr 25 2002	Use Tycho-2 catalog instead of ACT in setwcscom()
+ * May 13 2002	Free WCSNAME in wcsfree()
+ *
+ * Mar 31 2003	Add distcode to wcstype()
+ * Apr  1 2003	Add calls to foc2pix() in wcs2pix() and pix2foc() in pix2wcs()
+ * May 20 2003	Declare argument i in savewcscom()
+ * Sep 29 2003	Fix bug to compute width and height correctly in wcsfull()
+ * Sep 29 2003	Fix bug to deal with all-sky images orrectly in wcsfull()
+ * Oct  1 2003	Rename wcs->naxes to wcs->naxis to match WCSLIB 3.2
+ * Nov  3 2003	Set distortion code by calling setdistcode() in wcstype()
+ * Dec  3 2003	Add back wcs->naxes for compatibility
+ * Dec  3 2003	Add braces in if...else in pix2wcst()
+ *
+ * Sep 17 2004	If spherical coordinate output, keep 0 < long/RA < 360
+ * Sep 17 2004	Fix bug in wcsfull() when wrapping around RA=0:00
+ * Nov  1 2004	Keep wcs->rot between 0 and 360
+ *
+ * Mar  9 2005	Fix bug in wcsrotset() which set rot > 360 to 360
+ * Jun 27 2005	Fix ctype in calls to wcs subroutines
+ * Jul 21 2005	Fix bug in wcsrange() at RA ~= 0.0
+ *
+ * Apr 24 2006	Always set inverse CD matrix to 2 dimensions in wcspcset()
+ * May  3 2006	(void *) pointers so arguments match type, from Robert Lupton
+ * Jun 30 2006	Set only 2-dimensional PC matrix; that is all lin* can deal with
+ * Oct 30 2006	In pix2wcs(), do not limit x to between 0 and 360 if XY or LINEAR
+ * Oct 30 2006	In wcsc2pix(), do not precess LINEAR or XY coordinates
+ * Dec 21 2006	Add cpwcs() to copy WCS keywords to new suffix
+ *
+ * Jan  4 2007	Fix pointer to header in cpwcs()
+ * Jan  5 2007	Drop declarations of wcscon(); it is in wcs.h
+ * Jan  9 2006	Drop declarations of fk425e() and fk524e(); moved to wcs.h
+ * Jan  9 2006	Drop *pix() and *pos() external declarations; moved to wcs.h
+ * Jan  9 2006	Drop matinv() external declaration; it is already in wcslib.h
+ * Feb 15 2007	If CTYPEi contains DET, set to WCS_PIX projection
+ * Feb 23 2007	Fix bug when checking for "DET" in CTYPEi
+ * Apr  2 2007	Fix PC to CD matrix conversion
+ * Jul 25 2007	Compute distance between two coordinates using d2v3()
+ */
diff --git a/Code/src/libwcs/wcs.h b/Code/src/libwcs/wcs.h
new file mode 100644
index 0000000000000000000000000000000000000000..3012519c7618e43a43b5cdf2bee31e38d53457fe
--- /dev/null
+++ b/Code/src/libwcs/wcs.h
@@ -0,0 +1,917 @@
+/*** File libwcs/wcs.h
+ *** July 25, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1994-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#ifndef _wcs_h_
+#define _wcs_h_
+
+#include "wcslib.h"
+#include "fitshead.h"
+
+/* SIRTF distortion matrix coefficients */
+#define DISTMAX 10
+struct Distort {
+  int    a_order;                /* max power for the 1st dimension */
+  double a[DISTMAX][DISTMAX];  /* coefficient array of 1st dimension */
+  int    b_order;                /* max power for 1st dimension */
+  double b[DISTMAX][DISTMAX];  /* coefficient array of 2nd dimension */
+  int    ap_order;               /* max power for the 1st dimension */
+  double ap[DISTMAX][DISTMAX]; /* coefficient array of 1st dimension */
+  int    bp_order;               /* max power for 1st dimension */
+  double bp[DISTMAX][DISTMAX]; /* coefficient array of 2nd dimension */
+};
+
+struct WorldCoor {
+  double	xref;		/* X reference coordinate value (deg) */
+  double	yref;		/* Y reference coordinate value (deg) */
+  double	xrefpix;	/* X reference pixel */
+  double	yrefpix;	/* Y reference pixel */
+  double	xinc;		/* X coordinate increment (deg) */
+  double	yinc;		/* Y coordinate increment (deg) */
+  double	rot;		/* rotation around axis (deg) (N through E) */
+  double	cd[4];		/* rotation matrix */
+  double	dc[4];		/* inverse rotation matrix */
+  double	equinox;	/* Equinox of coordinates default to 1950.0 */
+  double	epoch;		/* Epoch of coordinates default to equinox */
+  double	nxpix;		/* Number of pixels in X-dimension of image */
+  double	nypix;		/* Number of pixels in Y-dimension of image */
+  double	plate_ra;	/* Right ascension of plate center */
+  double	plate_dec;	/* Declination of plate center */
+  double	plate_scale;	/* Plate scale in arcsec/mm */
+  double	x_pixel_offset;	/* X pixel offset of image lower right */
+  double	y_pixel_offset;	/* Y pixel offset of image lower right */
+  double	x_pixel_size;	/* X pixel_size */
+  double	y_pixel_size;	/* Y pixel_size */
+  double	ppo_coeff[6];	/* pixel to plate coefficients for DSS */
+  double	x_coeff[20];	/* X coefficients for plate model */
+  double	y_coeff[20];	/* Y coefficients for plate model */
+  double	xpix;		/* X (RA) coordinate (pixels) */
+  double	ypix;		/* Y (dec) coordinate (pixels) */
+  double	zpix;		/* Z (face) coordinate (pixels) */
+  double	xpos;		/* X (RA) coordinate (deg) */
+  double	ypos;		/* Y (dec) coordinate (deg) */
+  double	crpix[9];	/* Values of CRPIXn keywords */
+  double	crval[9];	/* Values of CRVALn keywords */
+  double	cdelt[9];	/* Values of CDELTn keywords */
+  double	pc[81];		/* Values of PCiiijjj keywords */
+  double	projp[10];	/* Constants for various projections */
+  double	longpole;	/* Longitude of North Pole in degrees */
+  double	latpole;	/* Latitude of North Pole in degrees */
+  double	rodeg;		/* Radius of the projection generating sphere */
+  double	imrot;		/* Rotation angle of north pole */
+  double	pa_north;	/* Position angle of north (0=horizontal) */
+  double	pa_east;	/* Position angle of east (0=horizontal) */
+  double	radvel;		/* Radial velocity (km/sec away from observer)*/
+  double	zvel;		/* Radial velocity (v/c away from observer)*/
+  int		imflip;		/* If not 0, image is reflected around axis */
+  int		prjcode;	/* projection code (-1-32) */
+  int		latbase;	/* Latitude base 90 (NPA), 0 (LAT), -90 (SPA) */
+  int		ncoeff1;	/* Number of x-axis plate fit coefficients */
+  int		ncoeff2;	/* Number of y-axis plate fit coefficients */
+  int		changesys;	/* 1 for FK4->FK5, 2 for FK5->FK4 */
+  				/* 3 for FK4->galactic, 4 for FK5->galactic */
+  int		printsys;	/* 1 to print coordinate system, else 0 */
+  int		ndec;		/* Number of decimal places in PIX2WCST */
+  int		degout;		/* 1 to always print degrees in PIX2WCST */
+  int		tabsys;		/* 1 to put tab between RA & Dec, else 0 */
+  int		rotmat;		/* 0 if CDELT, CROTA; 1 if CD */
+  int		coorflip;	/* 0 if x=RA, y=Dec; 1 if x=Dec, y=RA */
+  int		offscl;		/* 0 if OK, 1 if offscale */
+  int		wcson;		/* 1 if WCS is set, else 0 */
+  int		naxis;		/* Number of axes in image (for WCSLIB 3.0) */
+  int		naxes;		/* Number of axes in image */
+  int		wcsproj;	/* WCS_OLD: AIPS worldpos() and worldpix()
+				   WCS_NEW: Mark Calabretta's WCSLIB subroutines
+				   WCS_BEST: WCSLIB for all but CAR,COE,NCP
+				   WCS_ALT:  AIPS for all but CAR,COE,NCP */
+  int		linmode;	/* 0=system only, 1=units, 2=system+units */
+  int		detector;	/* Instrument detector number */
+  char		instrument[32];	/* Instrument name */
+  char		ctype[9][9];	/* Values of CTYPEn keywords */
+  char		c1type[9];	/*  1st coordinate type code:
+					RA--, GLON, ELON */
+  char		c2type[9];	/*  2nd coordinate type code:
+					DEC-, GLAT, ELAT */
+  char		ptype[9];	/*  projection type code:
+				    SIN, TAN, ARC, NCP, GLS, MER, AIT, etc */
+  char		units[9][32];	/* Units if LINEAR */
+  char		radecsys[32];	/* Reference frame: FK4, FK4-NO-E, FK5, GAPPT*/
+  char		radecout[32];	/* Output reference frame: FK4,FK5,GAL,ECL */
+  char		radecin[32];	/* Input reference frame: FK4,FK5,GAL,ECL */
+  double	eqin;		/* Input equinox (match sysin if 0.0) */
+  double	eqout;		/* Output equinox (match sysout if 0.0) */
+  int		sysin;		/* Input coordinate system code */
+  int		syswcs;		/* WCS coordinate system code */
+  int		sysout;		/* Output coordinate system code */
+				/* WCS_B1950, WCS_J2000, WCS_ICRS, WCS_GALACTIC,
+				 * WCS_ECLIPTIC, WCS_LINEAR, WCS_ALTAZ  */
+  char		center[32];	/* Center coordinates (with frame) */
+  struct wcsprm wcsl;		/* WCSLIB main projection parameters */
+  struct linprm lin;		/* WCSLIB image/pixel conversion parameters */
+  struct celprm cel;		/* WCSLIB projection type */
+  struct prjprm prj;		/* WCSLIB projection parameters */
+  struct IRAFsurface *lngcor;	/* RA/longitude correction structure */
+  struct IRAFsurface *latcor;	/* Dec/latitude correction structure */
+  int		distcode;	/* Distortion code 0=none 1=SIRTF */
+  struct Distort distort;	/* SIRTF distortion coefficients */
+  char *command_format[10];	/* WCS command formats */
+				/* where %s is replaced by WCS coordinates */
+				/* where %f is replaced by the image filename */
+				/* where %x is replaced by image coordinates */
+  double	ltm[4];		/* Image rotation matrix */
+  double	ltv[2];		/* Image offset */
+  int		idpix[2];	/* First pixel to use in image (x, y) */
+  int		ndpix[2];	/* Number of pixels to use in image (x, y) */
+  struct WorldCoor *wcs;	/* WCS upon which this WCS depends */
+  struct WorldCoor *wcsdep;	/* WCS depending on this WCS */
+  char		*wcsname;	/* WCS name (defaults to NULL pointer) */
+  char		wcschar;	/* WCS character (A-Z, null, space) */
+  int		logwcs;		/* 1 if DC-FLAG is set for log wavelength */
+};
+
+/* Projections (1-26 are WCSLIB) (values for wcs->prjcode) */
+#define WCS_PIX -1	/* Pixel WCS */
+#define WCS_LIN  0	/* Linear projection */
+#define WCS_AZP  1	/* Zenithal/Azimuthal Perspective */
+#define WCS_SZP  2	/* Zenithal/Azimuthal Perspective */
+#define WCS_TAN  3	/* Gnomonic = Tangent Plane */
+#define WCS_SIN  4	/* Orthographic/synthesis */
+#define WCS_STG  5	/* Stereographic */
+#define WCS_ARC  6	/* Zenithal/azimuthal equidistant */
+#define WCS_ZPN  7	/* Zenithal/azimuthal PolyNomial */
+#define WCS_ZEA  8	/* Zenithal/azimuthal Equal Area */
+#define WCS_AIR  9	/* Airy */
+#define WCS_CYP 10	/* CYlindrical Perspective */
+#define WCS_CAR 11	/* Cartesian */
+#define WCS_MER 12	/* Mercator */
+#define WCS_CEA 13	/* Cylindrical Equal Area */
+#define WCS_COP 14	/* Conic PerSpective (COP) */
+#define WCS_COD 15	/* COnic equiDistant */
+#define WCS_COE 16	/* COnic Equal area */
+#define WCS_COO 17	/* COnic Orthomorphic */
+#define WCS_BON 18	/* Bonne */
+#define WCS_PCO 19	/* Polyconic */
+#define WCS_SFL 20	/* Sanson-Flamsteed (GLobal Sinusoidal) */
+#define WCS_PAR 21	/* Parabolic */
+#define WCS_AIT 22	/* Hammer-Aitoff */
+#define WCS_MOL 23	/* Mollweide */
+#define WCS_CSC 24	/* COBE quadrilateralized Spherical Cube */
+#define WCS_QSC 25	/* Quadrilateralized Spherical Cube */
+#define WCS_TSC 26	/* Tangential Spherical Cube */
+#define WCS_NCP 27	/* Special case of SIN */
+#define WCS_GLS 28	/* Same as SFL */
+#define WCS_DSS 29	/* Digitized Sky Survey plate solution */
+#define WCS_PLT 30	/* Plate fit polynomials (SAO) */
+#define WCS_TNX 31	/* Gnomonic = Tangent Plane (NOAO with corrections) */
+
+/* Coordinate systems */
+#define WCS_J2000	1	/* J2000(FK5) right ascension and declination */
+#define WCS_B1950	2	/* B1950(FK4) right ascension and declination */
+#define WCS_GALACTIC	3	/* Galactic longitude and latitude */
+#define WCS_ECLIPTIC	4	/* Ecliptic longitude and latitude */
+#define WCS_ALTAZ	5	/* Azimuth and altitude/elevation */
+#define WCS_LINEAR	6	/* Linear with optional units */
+#define WCS_NPOLE	7	/* Longitude and north polar angle */
+#define WCS_SPA		8	/* Longitude and south polar angle */
+#define WCS_PLANET	9	/* Longitude and latitude on planet */
+#define WCS_XY		10	/* X-Y Cartesian coordinates */
+#define WCS_ICRS	11	/* ICRS right ascension and declination */
+
+/* Method to use */
+#define WCS_BEST	0	/* Use best WCS projections */
+#define WCS_ALT		1	/* Use not best WCS projections */
+#define WCS_OLD		2	/* Use AIPS WCS projections */
+#define WCS_NEW		3	/* Use WCSLIB 2.5 WCS projections */
+
+/* Distortion codes (values for wcs->distcode) */
+#define DISTORT_NONE	0	/* No distortion coefficients */
+#define DISTORT_SIRTF	1	/* SIRTF distortion matrix */
+
+#ifndef PI
+#define PI	3.141592653589793238462643
+#endif
+
+/* pi/(180*3600):  arcseconds to radians */
+#define AS2R		4.8481368110953e-6
+
+/* Conversions among hours of RA, degrees and radians. */
+#define degrad(x)	((x)*PI/180.)
+#define raddeg(x)	((x)*180./PI)
+#define hrdeg(x)	((x)*15.)
+#define deghr(x)	((x)/15.)
+#define hrrad(x)	degrad(hrdeg(x))
+#define radhr(x)	deghr(raddeg(x))
+#define secrad(x)	((x)*AS2R)
+
+/* TNX surface fitting structure and flags */
+struct IRAFsurface {
+  double xrange;	/* 2. / (xmax - xmin), polynomials */
+  double xmaxmin;	/* - (xmax + xmin) / 2., polynomials */
+  double yrange;	/* 2. / (ymax - ymin), polynomials */
+  double ymaxmin;	/* - (ymax + ymin) / 2., polynomials */
+  int	 type;		/* type of curve to be fitted */
+  int    xorder;	/* order of the fit in x */
+  int    yorder;	/* order of the fit in y */
+  int    xterms;	/* cross terms for polynomials */
+  int    ncoeff;	/* total number of coefficients */
+  double *coeff;	/* pointer to coefficient vector */
+  double *xbasis;	/* pointer to basis functions (all x) */
+  double *ybasis;	/* pointer to basis functions (all y) */
+};
+
+/* TNX permitted types of surfaces */
+#define  TNX_CHEBYSHEV    1
+#define  TNX_LEGENDRE     2
+#define  TNX_POLYNOMIAL   3
+
+/* TNX cross-terms flags */
+#define	TNX_XNONE	0	/* no x-terms (old no) */
+#define	TNX_XFULL	1	/* full x-terms (new yes) */
+#define	TNX_XHALF	2	/* half x-terms (new) */
+
+#ifdef __cplusplus /* C++ prototypes */
+extern "C" {
+#endif
+
+#ifdef __STDC__   /* Full ANSI prototypes */
+
+    /* WCS data structure initialization subroutines in wcsinit.c */
+    struct WorldCoor *wcsinit ( /* set up WCS structure from a FITS image header */
+	const char* hstring);
+
+    struct WorldCoor *wcsninit ( /* set up WCS structure from a FITS image header */
+	const char* hstring,	/* FITS header */
+	int len);		/* Length of FITS header */
+
+    struct WorldCoor *wcsinitn ( /* set up WCS structure from a FITS image header */
+	const char* hstring,	/* FITS header */
+	const char* wcsname);	/* WCS name */
+
+    struct WorldCoor *wcsninitn ( /* set up WCS structure from a FITS image header */
+	const char* hstring,	/* FITS header */
+	int len,		/* Length of FITS header */
+	const char* wcsname);	/* WCS name */
+
+    struct WorldCoor *wcsinitc ( /* set up WCS structure from a FITS image header */
+	const char* hstring,	/* FITS header */
+	char *wcschar);		/* WCS character (A-Z) */
+
+    struct WorldCoor *wcsninitc ( /* set up WCS structure from a FITS image header */
+	const char* hstring,	/* FITS header */
+	int len,		/* Length of FITS header */
+	char *wcschar);		/* WCS character (A-Z) */
+
+    /* WCS subroutines in wcs.c */
+    void wcsfree (		/* Free a WCS structure and its contents */
+	struct WorldCoor *wcs);	/* World coordinate system structure */
+
+    int wcstype(		/* Set projection type from header CTYPEs */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	char *ctype1,		/* FITS WCS projection for axis 1 */
+	char *ctype2);		/* FITS WCS projection for axis 2 */
+
+    int iswcs(		/* Returns 1 if wcs structure set, else 0 */
+	struct WorldCoor *wcs);	/* World coordinate system structure */
+    int nowcs(		/* Returns 0 if wcs structure set, else 1 */
+	struct WorldCoor *wcs);	/* World coordinate system structure */
+
+    int pix2wcst (	/* Convert pixel coordinates to World Coordinate string */
+        struct WorldCoor *wcs,  /* World coordinate system structure */
+        double xpix, 	/* Image horizontal coordinate in pixels */
+        double ypix,	/* Image vertical coordinate in pixels */
+        char *wcstring,	/* World coordinate string (returned) */
+        int lstr);	/* Length of world coordinate string (returned) */
+
+    void pix2wcs (	/* Convert pixel coordinates to World Coordinates */
+        struct WorldCoor *wcs,  /* World coordinate system structure */
+        double xpix,	/* Image horizontal coordinate in pixels */	
+        double ypix,	/* Image vertical coordinate in pixels */
+        double *xpos,	/* Longitude/Right Ascension in degrees (returned) */
+        double *ypos);	/* Latitude/Declination in degrees (returned) */
+
+    void wcsc2pix (	/* Convert World Coordinates to pixel coordinates */
+        struct WorldCoor *wcs,  /* World coordinate system structure */
+        double xpos,	/* Longitude/Right Ascension in degrees */
+        double ypos,	/* Latitude/Declination in degrees */
+	char *coorsys,	/* Coordinate system (B1950, J2000, etc) */
+        double *xpix,	/* Image horizontal coordinate in pixels (returned) */
+        double *ypix,	/* Image vertical coordinate in pixels (returned) */
+        int *offscl);
+
+    void wcs2pix (	/* Convert World Coordinates to pixel coordinates */
+        struct WorldCoor *wcs,  /* World coordinate system structure */
+        double xpos,	/* Longitude/Right Ascension in degrees */
+        double ypos,	/* Latitude/Declination in degrees */
+        double *xpix,	/* Image horizontal coordinate in pixels (returned) */
+        double *ypix,	/* Image vertical coordinate in pixels (returned) */
+        int *offscl);
+
+    double wcsdist(	/* Compute angular distance between 2 sky positions */
+	double ra1,	/* First longitude/right ascension in degrees */
+	double dec1,	/* First latitude/declination in degrees */
+	double ra2,	/* Second longitude/right ascension in degrees */
+	double dec2);	/* Second latitude/declination in degrees */
+
+    double wcsdiff(	/* Compute angular distance between 2 sky positions */
+	double ra1,	/* First longitude/right ascension in degrees */
+	double dec1,	/* First latitude/declination in degrees */
+	double ra2,	/* Second longitude/right ascension in degrees */
+	double dec2);	/* Second latitude/declination in degrees */
+
+    struct WorldCoor* wcsxinit( /* set up a WCS structure from arguments */
+        double cra,	/* Center right ascension in degrees */
+        double cdec,	/* Center declination in degrees */
+        double secpix,	/* Number of arcseconds per pixel */
+        double xrpix,	/* Reference pixel X coordinate */
+        double yrpix,	/* Reference pixel X coordinate */
+        int nxpix,	/* Number of pixels along x-axis */
+        int nypix,	/* Number of pixels along y-axis */
+        double rotate,	/* Rotation angle (clockwise positive) in degrees */
+        int equinox,	/* Equinox of coordinates, 1950 and 2000 supported */
+        double epoch,	/* Epoch of coordinates, used for FK4/FK5 conversion
+                         * no effect if 0 */
+        char *proj);	/* Projection */
+
+    struct WorldCoor* wcskinit( /* set up WCS structure from keyword values */
+	int naxis1,	/* Number of pixels along x-axis */
+	int naxis2,	/* Number of pixels along y-axis */
+	char *ctype1,	/* FITS WCS projection for axis 1 */
+	char *ctype2,	/* FITS WCS projection for axis 2 */
+	double crpix1,	/* Reference pixel coordinates */
+	double crpix2,	/* Reference pixel coordinates */
+	double crval1,	/* Coordinate at reference pixel in degrees */
+	double crval2,	/* Coordinate at reference pixel in degrees */
+	double *cd,	/* Rotation matrix, used if not NULL */
+	double cdelt1,	/* scale in degrees/pixel, if cd is NULL */
+	double cdelt2,	/* scale in degrees/pixel, if cd is NULL */
+	double crota,	/* Rotation angle in degrees, if cd is NULL */
+	int equinox,	/* Equinox of coordinates, 1950 and 2000 supported */
+	double epoch);	/* Epoch of coordinates, for FK4/FK5 conversion */
+
+    void wcsshift(	/* Change center of WCS */
+        struct WorldCoor *wcs,  /* World coordinate system structure */
+        double cra,	/* New center right ascension in degrees */
+        double cdec,	/* New center declination in degrees */
+        char *coorsys); /* FK4 or FK5 coordinates (1950 or 2000) */
+
+    void wcsfull(	/* Return RA and Dec of image center, size in degrees */
+        struct WorldCoor *wcs,  /* World coordinate system structure */
+        double  *cra,	/* Right ascension of image center (deg) (returned) */
+        double  *cdec,	/* Declination of image center (deg) (returned) */
+        double  *width,	/* Width in degrees (returned) */
+        double  *height); /* Height in degrees (returned) */
+
+    void wcscent(	/* Print the image center and size in WCS units */
+        struct WorldCoor *wcs); /* World coordinate system structure */
+
+    void wcssize(	/* Return image center and size in RA and Dec */
+        struct WorldCoor *wcs,  /* World coordinate system structure */
+        double *cra,	/* Right ascension of image center (deg) (returned) */
+        double *cdec,	/* Declination of image center (deg) (returned) */
+        double *dra,	/* Half-width in right ascension (deg) (returned) */
+        double *ddec);	/* Half-width in declination (deg) (returned) */
+
+    void wcsrange(	/* Return min and max RA and Dec of image in degrees */
+        struct WorldCoor *wcs,  /* World coordinate system structure */
+        double  *ra1,	/* Min. right ascension of image (deg) (returned) */
+        double  *ra2,	/* Max. right ascension of image (deg) (returned) */
+        double  *dec1,	/* Min. declination of image (deg) (returned) */
+        double  *dec2);	/* Max. declination of image (deg) (returned) */
+
+    void wcscdset(	/* Set scaling and rotation from CD matrix */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	double *cd);	/* CD matrix, ignored if NULL */
+
+    void wcsdeltset(	/* set scaling, rotation from CDELTi, CROTA2 */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	double cdelt1,	/* degrees/pixel in first axis (or both axes) */
+	double cdelt2,	/* degrees/pixel in second axis if nonzero */
+	double crota);	/* Rotation counterclockwise in degrees */
+
+    void wcspcset(	/* set scaling, rotation from CDELTs and PC matrix */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	double cdelt1,	/* degrees/pixel in first axis (or both axes) */
+	double cdelt2,	/* degrees/pixel in second axis if nonzero */
+	double *pc);	/* Rotation matrix, ignored if NULL */
+
+    void setwcserr(	/* Set WCS error message for later printing */
+	char *errmsg);	/* Error mesage < 80 char */
+    void wcserr(void);	/* Print WCS error message to stderr */
+
+    void setdefwcs(	/* Set flag to use AIPS WCS instead of WCSLIB */
+	int oldwcs);	/* 1 for AIPS WCS subroutines, else WCSLIB */
+    int getdefwcs(void);	/* Return flag for AIPS WCS set by setdefwcs */
+
+    char *getradecsys(	/* Return name of image coordinate system */
+        struct WorldCoor *wcs);	/* World coordinate system structure */
+	
+    void wcsoutinit(	/* Set output coordinate system for pix2wcs */
+        struct WorldCoor *wcs,	/* World coordinate system structure */
+	char *coorsys);	/* Coordinate system (B1950, J2000, etc) */
+
+    char *getwcsout(	/* Return current output coordinate system */
+        struct WorldCoor *wcs);	/* World coordinate system structure */
+
+    void wcsininit(	/* Set input coordinate system for wcs2pix */
+        struct WorldCoor *wcs,	/* World coordinate system structure */
+	char *coorsys);	/* Coordinate system (B1950, J2000, etc) */
+
+    char *getwcsin(	/* Return current input coordinate system */
+        struct WorldCoor *wcs);	/* World coordinate system structure */
+
+    int setwcsdeg(	/* Set WCS coordinate output format */
+        struct WorldCoor *wcs,	/* World coordinate system structure */
+	int degout);	/* 1= degrees, 0= hh:mm:ss dd:mm:ss */
+
+    int wcsndec(	/* Set or get number of output decimal places */
+        struct WorldCoor *wcs,	/* World coordinate system structure */
+	int ndec);	/* Number of decimal places in output string
+			   if < 0, return current ndec unchanged */
+
+    int wcsreset(	/* Change WCS using arguments */
+	struct WorldCoor *wcs,	/* World coordinate system data structure */
+	double crpix1,	/* Horizontal reference pixel */
+	double crpix2,	/* Vertical reference pixel */
+	double crval1,	/* Reference pixel horizontal coordinate in degrees */
+	double crval2,	/* Reference pixel vertical coordinate in degrees */
+	double cdelt1,	/* Horizontal scale in degrees/pixel, ignored if cd is not NULL */
+	double cdelt2,	/* Vertical scale in degrees/pixel, ignored if cd is not NULL */
+	double crota,	/* Rotation angle in degrees, ignored if cd is not NULL */
+	double *cd);	/* Rotation matrix, used if not NULL */
+
+    void wcseqset(	/* Change equinox of reference pixel coordinates in WCS */
+	struct WorldCoor *wcs,	/* World coordinate system data structure */
+	double equinox);	/* Desired equinox as fractional year */
+
+    void setwcslin(	/* Set pix2wcst() mode for LINEAR coordinates */
+        struct WorldCoor *wcs,	/* World coordinate system structure */
+	int mode);	/* 0: x y linear, 1: x units x units
+			   2: x y linear units */
+
+    int wcszin(		/* Set third dimension for cube projections */
+	int izpix);	/* Set coordinate in third dimension (face) */
+
+    int wcszout (	/* Return coordinate in third dimension */
+        struct WorldCoor *wcs);	/* World coordinate system structure */
+
+    void wcscominit(	/* Initialize catalog search command set by -wcscom */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	int i,		/* Number of command (0-9) to initialize */
+	char *command);	/* command with %s where coordinates will go */
+
+    void wcscom(	/* Execute catalog search command set by -wcscom */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	int i,		/* Number of command (0-9) to execute */
+	char *filename,	/* Image file name */
+	double xfile,	/* Horizontal image pixel coordinates for WCS command */
+	double yfile,	/* Vertical image pixel coordinates for WCS command */
+	char *wcstring); /* WCS String from pix2wcst() */
+
+    void savewcscom(	/* Save WCS shell command */
+	int i,		/* i of 10 possible shell commands */
+	char *wcscom);	/* Shell command using output WCS string */
+    char *getwcscom(	/* Return WCS shell command */
+	int i);		/* i of 10 possible shell commands */
+    void setwcscom(	/* Set WCS shell commands from stored values */
+        struct WorldCoor *wcs);	/* World coordinate system structure */
+    void freewcscom(	/* Free memory storing WCS shell commands */
+        struct WorldCoor *wcs);	/* World coordinate system structure */
+
+    void setwcsfile(	/* Set filename for WCS error message */
+	char *filename); /* FITS or IRAF file name */
+    int cpwcs (		/* Copy WCS keywords with no suffix to ones with suffix */
+	char **header,	/* Pointer to start of FITS header */
+	char *cwcs);	/* Keyword suffix character for output WCS */
+
+    void savewcscoor(	/* Save output coordinate system */
+	char *wcscoor);	/* coordinate system (J2000, B1950, galactic) */
+    char *getwcscoor(void); /* Return output coordinate system */
+
+    /* Coordinate conversion subroutines in wcscon.c */
+    void wcsconv(	/* Convert between coordinate systems and equinoxes */
+	int sys1,	/* Input coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+	int sys2,	/* Output coordinate system (J2000, B1950, ECLIPTIC, G ALACTIC */
+	double eq1,	/* Input equinox (default of sys1 if 0.0) */
+	double eq2,	/* Output equinox (default of sys2 if 0.0) */
+	double ep1,	/* Input Besselian epoch in years */
+	double ep2,	/* Output Besselian epoch in years */
+	double *dtheta,	/* Longitude or right ascension in degrees
+			   Input in sys1, returned in sys2 */
+	double *dphi,	/* Latitude or declination in degrees
+			   Input in sys1, returned in sys2 */
+	double *ptheta,	/* Longitude or right ascension proper motion in deg/year
+			   Input in sys1, returned in sys2 */
+	double *pphi,	/* Latitude or declination proper motion in deg/year */
+	double *px,	/* Parallax in arcseconds */
+	double *rv);	/* Radial velocity in km/sec */
+    void wcsconp(	/* Convert between coordinate systems and equinoxes */
+	int sys1,	/* Input coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+	int sys2,	/* Output coordinate system (J2000, B1950, ECLIPTIC, G ALACTIC */
+	double eq1,	/* Input equinox (default of sys1 if 0.0) */
+	double eq2,	/* Output equinox (default of sys2 if 0.0) */
+	double ep1,	/* Input Besselian epoch in years */
+	double ep2,	/* Output Besselian epoch in years */
+	double *dtheta,	/* Longitude or right ascension in degrees
+			   Input in sys1, returned in sys2 */
+	double *dphi,	/* Latitude or declination in degrees
+			   Input in sys1, returned in sys2 */
+	double *ptheta,	/* Longitude or right ascension proper motion in degrees/year
+			   Input in sys1, returned in sys2 */
+	double *pphi);	/* Latitude or declination proper motion in degrees/year
+			   Input in sys1, returned in sys2 */
+    void wcscon(	/* Convert between coordinate systems and equinoxes */
+	int sys1,	/* Input coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+	int sys2,	/* Output coordinate system (J2000, B1950, ECLIPTIC, G ALACTIC */
+	double eq1,	/* Input equinox (default of sys1 if 0.0) */
+	double eq2,	/* Output equinox (default of sys2 if 0.0) */
+	double *dtheta,	/* Longitude or right ascension in degrees
+			   Input in sys1, returned in sys2 */
+	double *dphi,	/* Latitude or declination in degrees
+			   Input in sys1, returned in sys2 */
+	double epoch);	/* Besselian epoch in years */
+    void fk425e (	/* Convert B1950(FK4) to J2000(FK5) coordinates */
+	double *ra,	/* Right ascension in degrees (B1950 in, J2000 out) */
+	double *dec,	/* Declination in degrees (B1950 in, J2000 out) */
+	double epoch);	/* Besselian epoch in years */
+    void fk524e (	/* Convert J2000(FK5) to B1950(FK4) coordinates */
+	double *ra,	/* Right ascension in degrees (J2000 in, B1950 out) */
+	double *dec,	/* Declination in degrees (J2000 in, B1950 out) */
+	double epoch);	/* Besselian epoch in years */
+    int wcscsys(	/* Return code for coordinate system in string */
+	char *coorsys);	 /* Coordinate system (B1950, J2000, etc) */
+    double wcsceq (	/* Set equinox from string (return 0.0 if not obvious) */
+	char *wcstring);  /* Coordinate system (B1950, J2000, etc) */
+    void wcscstr (	/* Set coordinate system type string from system and equinox */
+	char   *cstr,	 /* Coordinate system string (returned) */
+	int    syswcs,	/* Coordinate system code */
+	double equinox,	/* Equinox of coordinate system */
+	double epoch);	/* Epoch of coordinate system */
+    void d2v3 (		/* Convert RA and Dec in degrees and distance to vector */
+	double	rra,	/* Right ascension in degrees */
+	double	rdec,	/* Declination in degrees */
+	double	r,	/* Distance to object in same units as pos */
+	double pos[3]);	/* x,y,z geocentric equatorial position of object (returned) */
+    void s2v3 (		/* Convert RA and Dec in radians and distance to vector */
+	double	rra,	/* Right ascension in radians */
+	double	rdec,	/* Declination in radians */
+	double	r,	/* Distance to object in same units as pos */
+	double pos[3]);	/* x,y,z geocentric equatorial position of object (returned) */
+    void v2d3 (		/* Convert vector to RA and Dec in degrees and distance */
+	double	pos[3],	/* x,y,z geocentric equatorial position of object */
+	double	*rra,	/* Right ascension in degrees (returned) */
+	double	*rdec,	/* Declination in degrees (returned) */
+	double	*r);	/* Distance to object in same units as pos (returned) */
+    void v2s3 (		/* Convert vector to RA and Dec in radians and distance */
+	double	pos[3],	/* x,y,z geocentric equatorial position of object */
+	double	*rra,	/* Right ascension in radians (returned) */
+	double	*rdec,	/* Declination in radians (returned) */
+	double	*r);	/* Distance to object in same units as pos (returned) */
+
+/* Distortion model subroutines in distort.c */
+    void distortinit (	/* Set distortion coefficients from FITS header */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	const char* hstring);	/* FITS header */
+    void setdistcode (	/* Set WCS distortion code string from CTYPEi value */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	char	*ctype);	/* CTYPE value from FITS header */
+    char *getdistcode (	/* Return distortion code string for CTYPEi */
+	struct WorldCoor *wcs);	/* World coordinate system structure */
+    int DelDistort (	/* Delete all distortion-related fields */
+	char *header,	/* FITS header */
+	int verbose);	/* If !=0, print keywords as deleted */
+    void pix2foc (	/* Convert pixel to focal plane coordinates */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	double x,	/* Image pixel horizontal coordinate */
+	double y,	/* Image pixel vertical coordinate */
+	double *u,	/* Focal plane horizontal coordinate(returned) */
+	double *v);	/* Focal plane vertical coordinate (returned) */
+    void foc2pix (	/* Convert focal plane to pixel coordinates */
+	struct WorldCoor *wcs,	/* World coordinate system structure */
+	double u,	/* Focal plane horizontal coordinate */
+	double v,	/* Focal plane vertical coordinate */
+	double *x,	/* Image pixel horizontal coordinate(returned) */
+	double *y);	/* Image pixel vertical coordinate (returned) */
+
+/* Other projection subroutines */
+
+/* 8 projections using AIPS algorithms (worldpos.c) */
+    int worldpos (	/* Convert from pixel location to RA,Dec */
+	double xpix,	/* x pixel number  (RA or long without rotation) */
+	double ypix,	/* y pixel number  (Dec or lat without rotation) */
+	struct WorldCoor *wcs, /* WCS parameter structure */
+	double *xpos,	/* x (RA) coordinate (deg) (returned) */
+	double *ypos);	/* y (dec) coordinate (deg) (returned) */
+    int worldpix (	/* Convert from RA,Dec to pixel location */
+	double xpos,	/* x (RA) coordinate (deg) */
+	double ypos,	/* y (dec) coordinate (deg) */
+	struct WorldCoor *wcs, /* WCS parameter structure */
+	double *xpix,	/* x pixel number (RA or long without rotation) */
+	double *ypix);	/* y pixel number (dec or lat without rotation) */
+
+/* Digital Sky Survey projection (dsspos.c) */
+    int dsspos (	/* Convert from pixel location to RA,Dec */
+	double xpix,	/* x pixel number  (RA or long without rotation) */
+	double ypix,	/* y pixel number  (Dec or lat without rotation) */
+	struct WorldCoor *wcs, /* WCS parameter structure */
+	double *xpos,	/* x (RA) coordinate (deg) (returned) */
+	double *ypos);	/* y (dec) coordinate (deg) (returned) */
+    int dsspix (	/* Convert from RA,Dec to pixel location */
+	double xpos,	/* x (RA) coordinate (deg) */
+	double ypos,	/* y (dec) coordinate (deg) */
+	struct WorldCoor *wcs, /* WCS parameter structure */
+	double *xpix,	/* x pixel number (RA or long without rotation) */
+	double *ypix);	/* y pixel number (dec or lat without rotation) */
+
+/* SAO TDC TAN projection with higher order terms (platepos.c) */
+    int platepos (	/* Convert from pixel location to RA,Dec */
+	double xpix,	/* x pixel number  (RA or long without rotation) */
+	double ypix,	/* y pixel number  (Dec or lat without rotation) */
+	struct WorldCoor *wcs, /* WCS parameter structure */
+	double *xpos,	/* x (RA) coordinate (deg) (returned) */
+	double *ypos);	/* y (dec) coordinate (deg) (returned) */
+    int platepix (	/* Convert from RA,Dec to pixel location */
+	double xpos,	/* x (RA) coordinate (deg) */
+	double ypos,	/* y (dec) coordinate (deg) */
+	struct WorldCoor *wcs, /* WCS parameter structure */
+	double *xpix,	/* x pixel number (RA or long without rotation) */
+	double *ypix);	/* y pixel number (dec or lat without rotation) */
+    void SetFITSPlate (	/* Set FITS header plate fit coefficients from structure */
+	char *header,	/* Image FITS header */
+	struct WorldCoor *wcs); /* WCS structure */
+    int SetPlate (	/* Set plate fit coefficients in structure from arguments */
+	struct WorldCoor *wcs, /* World coordinate system structure */
+	int ncoeff1,	/* Number of coefficients for x */
+	int ncoeff2,	/* Number of coefficients for y */
+	double *coeff);	/* Plate fit coefficients */
+    int GetPlate (	/* Return plate fit coefficients from structure in arguments */
+	struct WorldCoor *wcs, /* World coordinate system structure */
+	int *ncoeff1,	/* Number of coefficients for x */
+	int *ncoeff2,	/* Number of coefficients for y) */
+	double *coeff);	/* Plate fit coefficients */
+
+/* IRAF TAN projection with higher order terms (tnxpos.c) */
+    int tnxinit (	/* initialize the gnomonic forward or inverse transform */
+	const char *header, /* FITS header */
+	struct WorldCoor *wcs); /* pointer to WCS structure */
+    int tnxpos (	/* forward transform (physical to world) gnomonic projection. */
+	double xpix,	/* Image X coordinate */
+	double ypix,	/* Image Y coordinate */
+	struct WorldCoor *wcs, /* pointer to WCS descriptor */
+	double *xpos,	/* Right ascension (returned) */
+	double *ypos);	/* Declination (returned) */
+    int tnxpix (	/* Inverse transform (world to physical) gnomonic projection */
+	double xpos,     /* Right ascension */
+	double ypos,     /* Declination */
+	struct WorldCoor *wcs, /* Pointer to WCS descriptor */
+	double *xpix,	/* Image X coordinate (returned) */
+	double *ypix);	/* Image Y coordinate (returned) */
+
+
+#else /* K&R prototypes */
+
+/* WCS subroutines in wcs.c */
+struct WorldCoor *wcsinit(); /* set up a WCS structure from a FITS image header */
+struct WorldCoor *wcsninit(); /* set up a WCS structure from a FITS image header */
+struct WorldCoor *wcsinitn(); /* set up a WCS structure from a FITS image header */
+struct WorldCoor *wcsninitn(); /* set up a WCS structure from a FITS image header */
+struct WorldCoor *wcsinitc(); /* set up a WCS structure from a FITS image header */
+struct WorldCoor *wcsninitc(); /* set up a WCS structure from a FITS image header */
+struct WorldCoor *wcsxinit(); /* set up a WCS structure from arguments */
+struct WorldCoor *wcskinit(); /* set up a WCS structure from keyword values */
+void wcsfree(void);		/* Free a WCS structure and its contents */
+int wcstype();		/* Set projection type from header CTYPEs */
+void wcscdset();	/* Set scaling and rotation from CD matrix */
+void wcsdeltset();	/* set scaling and rotation from CDELTs and CROTA2 */
+void wcspcset();	/* set scaling and rotation from CDELTs and PC matrix */
+int iswcs();		/* Return 1 if WCS structure is filled, else 0 */
+int nowcs();		/* Return 0 if WCS structure is filled, else 1 */
+void wcsshift();	/* Reset the center of a WCS structure */
+void wcscent();		/* Print the image center and size in WCS units */
+void wcssize();		/* Return RA and Dec of image center, size in RA and Dec */
+void wcsfull();		/* Return RA and Dec of image center, size in degrees */
+void wcsrange();	/* Return min and max RA and Dec of image in degrees */
+double wcsdist();	/* Distance in degrees between two sky coordinates */
+double wcsdiff();	/* Distance in degrees between two sky coordinates */
+void wcscominit();	/* Initialize catalog search command set by -wcscom */
+void wcscom();		/* Execute catalog search command set by -wcscom */
+char *getradecsys();	/* Return current value of coordinate system */
+void wcsoutinit();	/* Initialize WCS output coordinate system for use by pix2wcs */
+char *getwcsout();	/* Return current value of WCS output coordinate system */
+void wcsininit();	/* Initialize WCS input coordinate system for use by wcs2pix */
+char *getwcsin();	/* Return current value of WCS input coordinate system */
+int setwcsdeg();	/* Set WCS output in degrees (1) or hh:mm:ss dd:mm:ss (0) */
+int wcsndec();		/* Set or get number of output decimal places */
+int wcsreset();		/* Change WCS using arguments */
+void wcseqset();	/* Change equinox of reference pixel coordinates in WCS */
+void wcscstr();		/* Return system string from system code, equinox, epoch */
+void setwcslin();	/* Set output string mode for LINEAR coordinates */
+int pix2wcst();		/* Convert pixel coordinates to World Coordinate string */
+void pix2wcs();		/* Convert pixel coordinates to World Coordinates */
+void wcsc2pix();	/* Convert World Coordinates to pixel coordinates */
+void wcs2pix();		/* Convert World Coordinates to pixel coordinates */
+void setdefwcs();	/* Call to use AIPS classic WCS (also not PLT or TNX */
+int getdefwcs();	/* Call to get flag for AIPS classic WCS */
+int wcszin();		/* Set coordinate in third dimension (face) */
+int wcszout();		/* Return coordinate in third dimension */
+void wcserr();		/* Print WCS error message to stderr */
+void setwcserr();	/* Set WCS error message for later printing */
+void savewcscoor();	/* Save output coordinate system */
+char *getwcscoor();	/* Return output coordinate system */
+void savewcscom();	/* Save WCS shell command */
+char *getwcscom();	/* Return WCS shell command */
+void setwcscom();	/* Set WCS shell commands from stored values */
+void freewcscom();	/* Free memory used to store WCS shell commands */
+void setwcsfile();	/* Set filename for WCS error message */
+int cpwcs();		/* Copy WCS keywords with no suffix to ones with suffix */
+
+/* Coordinate conversion subroutines in wcscon.c */
+void wcscon();		/* Convert between coordinate systems and equinoxes */
+void wcsconp();		/* Convert between coordinate systems and equinoxes */
+void wcsconv();		/* Convert between coordinate systems and equinoxes */
+void fk425e();		/* Convert B1950(FK4) to J2000(FK5) coordinates */
+void fk524e();		/* Convert J2000(FK5) to B1950(FK4) coordinates */
+int wcscsys();		/* Set coordinate system from string */
+double wcsceq();	/* Set equinox from string (return 0.0 if not obvious) */
+void d2v3();		/* Convert RA and Dec in degrees and distance to vector */
+void s2v3();		/* Convert RA and Dec in radians and distance to vector */
+void v2d3();		/* Convert vector to RA and Dec in degrees and distance */
+void v2s3();		/* Convert vector to RA and Dec in radians and distance */
+
+/* Distortion model subroutines in distort.c */
+void distortinit();	/* Set distortion coefficients from FITS header */
+void setdistcode();	/* Set WCS distortion code string from CTYPEi value */
+char *getdistcode();	/* Return distortion code string for CTYPEi */
+int DelDistort();	/* Delete all distortion-related fields */
+void pix2foc();		/*  pixel coordinates -> focal plane coordinates */
+void foc2pix();		/*  focal plane coordinates -> pixel coordinates */
+
+/* Other projection subroutines */
+
+/* 8 projections using AIPS algorithms (worldpos.c) */
+extern int worldpos();	/* Convert from pixel location to RA,Dec */
+extern int worldpix();	/* Convert from RA,Dec to pixel location */
+
+/* Digital Sky Survey projection (dsspos.c) */
+extern int dsspos();	/* Convert from pixel location to RA,Dec */
+extern int dsspix();	/* Convert from RA,Dec to pixel location */
+
+/* SAO TDC TAN projection with higher order terms (platepos.c) */
+extern int platepos();	/* Convert from pixel location to RA,Dec */
+extern int platepix();	/* Convert from RA,Dec to pixel location */
+extern void SetFITSPlate(); /* Set FITS header plate fit coefficients from structure */
+extern int SetPlate();	/* Set plate fit coefficients in structure from arguments */
+extern int GetPlate();	/* Return plate fit coefficients from structure in arguments */
+
+/* IRAF TAN projection with higher order terms (tnxpos.c) */
+extern int tnxinit();	/* initialize the gnomonic forward or inverse transform */
+extern int tnxpos();	/* forward transform (physical to world) gnomonic projection. */
+extern int tnxpix();	/* Inverse transform (world to physical) gnomonic projection */
+
+#endif	/* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _wcs_h_ */
+
+/* Oct 26 1994	New file
+ * Dec 21 1994	Add rotation matrix
+ * Dec 22 1994	Add flag for coordinate reversal
+
+ * Mar  6 1995	Add parameters for Digital Sky Survey plate fit
+ * Jun  8 1995	Add parameters for coordinate system change
+ * Jun 21 1995	Add parameter for plate scale
+ * Jul  6 1995	Add parameter to note whether WCS is set
+ * Aug  8 1995	Add parameter to note whether to print coordinate system
+ * Oct 16 1995	Add parameters to save image dimensions and center coordinates
+
+ * Feb 15 1996	Add coordinate conversion functions
+ * Feb 20 1996	Add flag for tab tables
+ * Apr 26 1996	Add epoch of positions (actual date of image)
+ * Jul  5 1996	Add subroutine declarations
+ * Jul 19 1996	Add WCSFULL declaration
+ * Aug  5 1996	Add WCSNINIT to initialize WCS for non-terminated header
+ * Oct 31 1996	Add DCnn inverse rotation matrix
+ * Nov  1 1996	Add NDEC number of decimal places in output
+ *
+ * May 22 1997	Change range of pcode from 1-8 to -1-8 for linear transform
+ * Sep 12 1997	Add chip rotation MROT, XMPIX, YMPIX
+ *
+ * Jan  7 1998	Add INSTRUME and DETECTOR for HST metric correction
+ * Jan 16 1998	Add Mark Calabretta's WCSLIB data structures
+ * Jan 16 1998	Add LONGPOLE, LATPOLE, and PROJP constants for Calabretta
+ * Jan 22 1998	Add ctype[], crpix[], crval[], and cdelt[] for Calabretta
+ * Jan 23 1998	Change wcsset() to wcsxinit() and pcode to prjcode
+ * Jan 23 1998	Define projection type flags
+ * Jan 26 1998	Remove chip rotation
+ * Jan 26 1998	Add chip correction polynomial
+ * Feb  3 1998	Add number of coefficients for residual fit
+ * Feb  5 1998	Make cd and dc matrices vectors, not individual elements
+ * Feb 19 1998	Add projection names
+ * Feb 23 1998	Add TNX projection from NOAO
+ * Mar  3 1998	Add NOAO plate fit and residual fit
+ * Mar 12 1998	Add variables for TNX correction surface
+ * Mar 23 1998	Add PLT plate fit polynomial projection; reassign DSS
+ * Mar 23 1998	Drop plate_fit flag from structure
+ * Mar 25 1998	Add npcoeff to wcs structure for new plate fit WCS
+ * Apr  7 1998	Change amd_i_coeff to i_coeff
+ * Apr  8 1998	Add wcseqset() and wcsreset() subroutine declarations
+ * Apr 10 1998	Rearrange order of nonstandard WCS types
+ * Apr 13 1998	Add setdefwcs() subroutine declaration
+ * Apr 14 1998	Add coordinate systems and wcscoor()
+ * Apr 24 1998	Add units
+ * Apr 28 1998	Change coordinate system flags to WCS_*
+ * Apr 28 1998	Change projection flags to WCS_*
+ * Apr 28 1998	Add wcsc2pix()
+ * May  7 1998	Add C++ declarations
+ * May 13 1998	Add eqin and eqout for conversions to and from equinoxes
+ * May 14 1998	Add declarations for coordinate conversion subroutines
+ * May 27 1998	Add blsearch()
+ * May 27 1998	Change linear projection back to WCS_LIN from WCS_LPR
+ * May 27 1998	Move hget.c and hput.c C++ declarations to fitshead.h
+ * May 27 1998	Include fitshead.h
+ * May 29 1998	Add wcskinit()
+ * Jun  1 1998	Add wcserr()
+ * Jun 11 1998	Add initialization support subroutines
+ * Jun 18 1998	Add wcspcset()
+ * Jun 25 1998	Add wcsndec()
+ * Jul  6 1998	Add wcszin() and wcszout() to use third dimension of images
+ * Jul  7 1998	Change setdegout() to setwcsdeg(); setlinmode() to setwcslin()
+ * Jul 17 1998	Add savewcscoor(), getwcscoor(), savewcscom(), and getwcscom()
+ * Aug 14 1998	Add freewcscom(), setwcscom(), and multiple WCS commands
+ * Sep  3 1998	Add pa_north, pa_east, imrot and imflip to wcs structure
+ * Sep 14 1998	Add latbase for AXAF North Polar angle (NPOL not LAT-)
+ * Sep 16 1998	Make WCS_system start at 1; add NPOLE
+ * Sep 17 1998	Add wcscstr()
+ * Sep 21 1998	Add wcsconp() to convert proper motions, too.
+ * Dec  2 1998	Add WCS type for planet surface
+
+ * Jan 20 1999	Add declaration of wcsfree()
+ * Jun 16 1999	Add declaration of wcsrange()
+ * Oct 21 1999	Add declaration of setwcsfile()
+ *
+ * Jan 28 2000	Add flags for choice of WCS projection subroutines
+ * Jun 26 2000	Add XY coordinate system
+ * Nov  2 2000	Add wcsconv() to convert coordinates when parallax or rv known
+ *
+ * Jan 17 2001	Add idpix and ndpix for trim section, ltm for readout rotation
+ * Jan 31 2001	Add wcsinitn(), wcsninitn(), wcsinitc(), and wcsninitc()
+ * Feb 20 2001	Add wcs->wcs to main data structure
+ * Mar 20 2001	Close unclosed comment in wcsconv() argument list
+ *
+ * Apr  3 2002	Add SZP and second GLS/SFL projection
+ * Apr  9 2002	Add wcs->wcsdep for pointer to WCS depending on this WCS
+ * Apr 26 2002	Add wcs->wcsname and wcs->wcschar to identify WCS structure
+ * May  9 2002	Add wcs->radvel and wcs->zvel for radial velocity in km/sec
+ *
+ * Apr  1 2003	Add wcs->distort Distort structure for distortion correction
+ * Apr  1 2003	Add foc2pix() and pix2foc() subroutines for distortion correction
+ * May  1 2003	Add missing semicolons after C++ declarations of previous two functions
+ * Oct  1 2003	Rename wcs->naxes to wcs->naxis to match WCSLIB 3.2
+ * Nov  3 2003	Add distinit(), setdistcode(), and getdistcode() to distort.c
+ * Dec  3 2003	Add back wcs->naxes for backward compatibility
+ *
+ * Aug 30 2004	Add DelDistort()
+ *
+ * Nov  1 2005	Add WCS_ICRS
+ *
+ * Jan  5 2006	Add secrad()
+ * Apr 21 2006	Increase maximum number of axes from 4 to 8
+ * Apr 24 2006	Increase maximum number of axes to 9
+ * Nov 29 2006	Drop semicolon at end of C++ ifdef
+ * Dec 21 2006	Add cpwcs()
+ *
+ * Jan  4 2007	Drop extra declaration of wcscstr()
+ * Jan  4 2007	Fix declarations so ANSI prototypes are not just for C++
+ * Jan  9 2007	Add fk425e() and fk524e() subroutines
+ * Jan  9 2007	Add worldpos.c, dsspos.c, platepos.c, and tnxpos.c subroutines
+ * Jan 10 2007	Add ANSI prototypes for all subroutines
+ * Feb  1 2007	Add wcs.wcslog for log wavelength
+ * Jul 25 2007	Add v2s3(), s2v3(), d2v3(), v2d3() for coordinate-vector conversion
+ */
diff --git a/Code/src/libwcs/wcscat.h b/Code/src/libwcs/wcscat.h
new file mode 100644
index 0000000000000000000000000000000000000000..7e1f8a63ea4146e3f02f1e6ec3bbbf54b2eda1e9
--- /dev/null
+++ b/Code/src/libwcs/wcscat.h
@@ -0,0 +1,1692 @@
+/*** File libwcs/wcscat.h
+ *** November 2, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Copyright (C) 1998-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#ifndef _wcscat_h_
+#define _wcscat_h_
+#define MAXNMAG	20
+
+/* Data structure for SAO TDC ASCII and binary star catalog entries */
+struct Star {
+    float rdum;
+    float xno;		/* Catalog number */
+    double ra;		/* Right Ascension (degrees) */
+    double dec;		/* Declination (degrees) */
+    double errra;	/* Right Ascension (degrees) */
+    double errdec;	/* Declination (degrees) */
+    char isp[24];	/* Spectral type or other 2-char identifier */
+    short mag[MAXNMAG+1]; /* Up to MAXNMAG Magnitudes * 100 */
+    double rapm;	/* RA proper motion (degrees per year) */
+    double decpm;	/* Dec proper motion (degrees per year) */
+    double errpmr;	/* RA proper motion error (degrees per year) */
+    double errpmd;	/* Dec proper motion error (degrees per year) */
+    double xmag[MAXNMAG+1]; /* Up to MAXNMAG Magnitudes */
+    double num;		/* Actual star number */
+    int coorsys;	/* Coordinate system (WCS_J2000, WCS_B1950,...) */
+    double equinox;	/* Equinox of coordinate system as fractional year */
+    double epoch;	/* Epoch of position as fractional year */
+    double parallax;	/* Parallax in arcseconds */
+    double pxerror;	/* Parallax error in arcseconds */
+    double radvel;	/* Radial velocity in km/sec, positive away */
+    double dist;	/* Distance from search center in arcseconds */
+    double size;	/* Semi-major axis in arcseconds */
+    int nimage;		/* Number of images for catalog position */
+    int ncat;		/* Number of catalogs for catalog proper motion */
+    char *entry;	/* Line copied from input catalog */
+    char objname[80];	/* Object name */
+    int peak;		/* Peak flux per pixel in star image */
+};
+
+/* Catalog proper motion units */
+#define PM_MASYR		1	/* milliarcseconds per year */
+#define PM_ARCSECYR		2	/* arcseconds per year */
+#define PM_DEGYR		3	/* degrees per year */
+#define PM_RADYR		4	/* radians per year */
+#define PM_TSECYR		5	/* seconds of time (RA) per century */
+#define PM_ARCSECCEN		6	/* arcseconds per year */
+#define PM_TSECCEN		7	/* seconds of time (RA) per century */
+#define PM_MTSYR		8	/* milliseconds of time (RA) per year */
+#define PM_ARCSECHR		9	/* arcseconds per hour (solar system) */
+
+/* Data structure for SAO TDC ASCII and binary star catalogs */
+struct StarCat {
+    int star0;		/* Subtract from star number for file sequence number */
+    int star1;		/* First star number in file */
+    int nstars;		/* Number of stars in file */
+    int stnum;		/* Star number format in catalog file:
+			  <0: -stnum-character name at end instead of number
+			   0:  no star i.d. numbers
+			   1: Real*4 star i.d. numbers
+			   2: Integer*4 <region><nnnn>
+			   3: Integer*4 <region><nnnnn>
+			   4: Integer*4 <nnnnnnnnn>
+			   5: Character ID instead of number in ASCII files */
+    int mprop;		/* 1 if proper motion is included */
+			/* 2 if radial velocity is included */
+    int nmag;		/* Number of magnitudes present
+			   Negative for J2000 catalog */
+    int nbent;		/* Number of bytes per star entry */
+    int	rasorted;	/* 1 if RA-sorted, else 0 */
+    int	ignore;		/* 1 if ignoring info after position and magnitude */
+    FILE *ifcat;	/* File descriptor for catalog file */
+    char isfil[24];	/* Star catalog file name */
+    char isname[64];	/* Star catalog description */
+    int  byteswapped;	/* 1 if catalog is byte-reversed from CPU */
+    int  refcat;	/* Code for type of catalog (TXTCAT, BINCAT, etc.) */
+    int  coorsys;	/* Coordinate system
+			   B1950 J2000 Galactic Ecliptic */
+    double epoch;	/* Epoch of catalog coordinates in years */
+    double equinox;	/* Equinox of catalog coordinates in years */
+    char inform;	/* Coordinate format
+			   (B>inary D>egrees H>MS T>able U>SNO) */
+    char incdir[128];	/* Catalog directory pathname */
+    char incfile[32];	/* Catalog file name */
+    int ncobj;		/* Length of object name in binary star entry */
+    int nnfld;		/* Length of star number  */
+    int nndec;		/* Number of decimal places in star number */
+    int nepoch;		/* 1 if epoch of coordinates is present */
+    int sptype;		/* 1 if spectral type is present in catalog */
+    int plate;		/* 1 if plate or field number is present in catalog */
+    char *catbuff;	/* Pointer to start of catalog */
+    char *catdata;	/* Pointer to first entry in catalog */
+    char *catline;	/* Pointer to current entry in catalog */
+    char *catlast;	/* Pointer to one past end of last entry in catalog */
+    int  istar;		/* Number of current catalog entry */
+    struct TabTable *startab;	/* Structure for tab table catalog */
+    int entid;		/* Entry number for ID */
+    int entra;		/* Entry number for right ascension */
+    int entdec;		/* Entry number for declination */
+    int entmag[MAXNMAG+1]; /* Entry numbers for up to MAXNMAG magnitudes */
+    int entpeak;	/* Entry number for peak counts */
+    int entepoch;	/* Entry number for epoch of observation */
+    int entdate;	/* Entry number for FITS-format date of observation */
+    int entname;	/* Entry number for object name */
+    int entadd;		/* Entry number for additional keyword */
+    int entrpm;		/* Entry number for proper motion in right ascension */
+    int entdpm;		/* Entry number for proper motion in declination */
+    int entpx;		/* Entry number for parallax */
+    int entpxe;		/* Entry number for parallax error */
+    int entrv;		/* Entry number for radial velocity */
+    int enttype;	/* Entry number for spectral type */
+    int entsize;	/* Entry number for size of object */
+    int rpmunit;	/* Units for RA proper motion (PM_x) */
+    int dpmunit;	/* Units for DEC proper motion (PM_x) */
+    char *caturl;	/* set if web search, else NULL */
+    char keyid[16];	/* Entry name for ID */
+    char keyra[16];	/* Entry name for right ascension */
+    char keydec[16];	/* Entry name for declination */
+    char keymag[MAXNMAG+1][16]; /* Entry name for up to MAXNMAG magnitudes */
+    char keyrpm[16];	/* Entry name for right ascension proper motion */
+    char keydpm[16];	/* Entry name for declination proper motion */
+    char keypeak[16];	/* Entry name for integer code */
+    char keytype[16];	/* Entry name for spectral type */
+    char keyrv[16];	/* Entry name for radial velocity */
+    char keyadd[16];	/* Entry name for additional keyword */
+    char keyepoch[16];	/* Entry name for epoch */
+};
+
+/* Data structure for tab table files */
+struct TabTable {
+    char *filename;	/* Name of tab table file */
+    int nlines;		/* Number of entries in table */
+    char *tabname;	/* Name of this table or NULL */
+    char *tabbuff;	/* Pointer to start of saved tab table in memory */
+    char *tabheader;	/* Pointer to start of line containing table header */
+    char *tabhead;	/* Pointer to start of line containing column heading */
+    char *tabdash;	/* Pointer to start of line with dashes after column headings */
+    char *tabdata;	/* Pointer to start of first line of table data */
+    int lhead;		/* Number of bytes before first data line */
+    int iline;		/* Number of current line (1=first) */
+    int lline;		/* Length in bytes of line buffer */
+    char *tabline;	/* Pointer to start of current line */
+    FILE *tcat;		/* File descriptor for tab table file */
+    int ncols;		/* Number of columns per table entry */
+    char **colname;	/* Column names */
+    int *lcol;		/* Lengths of column header names */
+    int *lcfld;		/* Number of columns in field (hyphens) */
+    int lbuff;		/* Number of bytes in entire tab table */
+};
+
+/* Source catalog flags and subroutines */
+
+/* Source catalog flags returned from CatCode */
+#define GSC		1	/* HST Guide Star Catalog */
+#define UJC		2	/* USNO UJ Star Catalog */
+#define UAC		3	/* USNO A Star Catalog */
+#define USAC		4	/* USNO SA Star Catalog */
+#define SAO		5	/* SAO Star Catalog */
+#define IRAS		6	/* IRAS Point Source Catalog */
+#define PPM		7	/* PPM Star Catalog */
+#define TYCHO		8	/* Tycho Star Catalog */
+#define UA1		9	/* USNO A-1.0 Star Catalog */
+#define UA2		10	/* USNO A-2.0 Star Catalog */
+#define USA1		11	/* USNO SA-1.0 Star Catalog */
+#define USA2		12	/* USNO SA-2.0 Star Catalog */
+#define HIP		13	/* Hipparcos Star Catalog */
+#define ACT		14	/* USNO ACT Star Catalog */
+#define BSC		15	/* Yale Bright Star Catalog */
+#define TYCHO2		16	/* Tycho-2 Star Catalog */
+#define USNO		17	/* USNO-format plate catalog */
+#define TMPSC		18	/* 2MASS All-Sky Point Source Catalog */
+#define GSCACT		19	/* GSC-ACT revised Guide Star Catalog */
+#define GSC2		20	/* GSC II version 2.2 */
+#define UB1		21	/* USNO B-1.0 Star Catalog */
+#define UCAC1		22	/* USNO CCD Astrograph Catalog 1.0 */
+#define UCAC2		23	/* USNO CCD Astrograph Catalog 2.0 */
+#define TMIDR2		24	/* 2MASS IDR2 Point Source Catalog */
+#define YB6		25	/* USNO YB6 Catalog */
+#define SDSS		26	/* Sloan Digital Sky Survey Catalog */
+#define TMXSC		27	/* 2MASS Extended Source Catalog */
+#define TMPSCE		28	/* 2MASS Point Source Catalog with mag errors */
+#define TYCHO2E		29	/* Tycho-2 Star Catalog with magnitude errors */
+#define SKY2K		30	/* SKY2000 Master Catalog */
+#define SKYBOT		31	/* SKYBOT Solar System Objects */
+#define UCAC3		32	/* USNO CCD Astrograph Catalog 3.0 (2009) */
+#define TABCAT		-1	/* StarBase tab table catalog */
+#define BINCAT		-2	/* TDC binary catalog */
+#define TXTCAT		-3	/* TDC ASCII catalog */
+#define WEBCAT		-4	/* Tab catalog via the web */
+#define NUMCAT		31	/* Number of predefined catalogs */
+
+#define EP_EP   1	/* Output epoch as fractional year */
+#define EP_JD   2	/* Output epoch as Julian Date */
+#define EP_MJD  3	/* Ouput epoch as Modified Julian Date */
+#define EP_FD   4	/* Output epoch in FITS format (yyyy-mm-dd) */
+#define EP_ISO  5	/* Output epoch in ISO format (yyyy-mm-ddThh:mm:ss) */
+
+/* Structure for dealing with ranges */
+#define MAXRANGE 20
+struct Range {
+    double first;	/* Current minimum value */
+    double last;	/* Current maximum value */
+    double step;	/* Current step in value */
+    double value;	/* Current value */
+    double ranges[MAXRANGE*3];	/* nranges sets of first, last, step */
+    int nvalues;	/* Total number of values in all ranges */
+    int nranges;	/* Number of ranges */
+    int irange;		/* Index of current range */
+};
+
+/* Flags for sorting catalog search results */
+#define SORT_UNSET	-1	/* Catalog sort flag not set yet */
+#define SORT_NONE	0	/* Do not sort catalog output */
+#define SORT_MAG	1	/* Sort output by magnitude */
+#define SORT_DIST	2	/* Sort output by distance from center */
+#define SORT_RA		3	/* Sort output by right ascension */
+#define SORT_DEC	4	/* Sort output by declination */
+#define SORT_X		5	/* Sort output by image X coordinate */
+#define SORT_Y		6	/* Sort output by image Y coordinate */
+#define SORT_ID		7	/* Merge close catalog objects */
+#define SORT_MERGE	8	/* Merge close catalog objects */
+
+/* Shapes for SAOimage region file output */
+#define WCS_CIRCLE 1	/* circle shape for SAOimage plotting */
+#define WCS_SQUARE 2	/* square shape for SAOimage plotting */
+#define WCS_DIAMOND 3	/* diamond shape for SAOimage plotting */
+#define WCS_CROSS 4	/* cross shape for SAOimage plotting */
+#define WCS_EX 5	/* x shape for SAOimage plotting */
+#define WCS_VAR 6	/* variable (+ and x) shape for HSTGSC plotting */
+#define WCS_PCIRCLE 11	/* pixel circle shape for SAOimage plotting */
+#define WCS_PSQUARE 12	/* pixel square shape for SAOimage plotting */
+#define WCS_PDIAMOND 13	/* pixel diamond shape for SAOimage plotting */
+#define WCS_PCROSS 14	/* pixel cross shape for SAOimage plotting */
+#define WCS_PEX 15	/* pixel ex shape for SAOimage plotting */
+#define WCS_PVAR 16	/* pixel variable (+ and x) shape for HSTGSC plotting */
+
+/* Subroutines for extracting sources from catalogs */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __STDC__   /* Full ANSI prototypes */
+
+/* Subroutines for reading any catalogs, including TDC ASCII catalogs */
+
+    int ctgread(	/* Read sources by sky region from any catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat,	/* Catalog code from wcscat.h */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nsmax,	/* Maximum number of stars to be returned */
+	struct StarCat **starcat, /* Catalog data structure */
+	double *tnum,	/* Array of ID numbers (returned) */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D array of magnitudes (returned) */
+	int *tc,	/* Array of fluxes (returned) */
+	char **tobj,	/* Array of object names (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ctgrnum(	/* Read sources by number from any catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat,	/* Catalog code from wcscat.h */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	int match,	/* 1 to match star number exactly, else sequence num */
+	struct StarCat **starcat, /* Star catalog data structure */
+	double *tnum,	/* Array of source numbers to look for */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D Array of magnitudes (returned) */
+	int *tpeak,	/* Array of peak counts (returned) */
+	char **tkey,	/* Array of values of additional keyword */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ctgrdate(	/* Read sources by date from SAO TDC ASCII format catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat,	/* Catalog code from wcscat.h */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	struct StarCat **starcat, /* Star catalog data structure */
+	double date1,	/* Start time as Modified Julian Date or Julian Date */
+	double date2,	/* End time as Modified Julian Date or Julian Date */
+	int nmax,	/* Maximum number of stars to look for */
+	double *tnum,	/* Array of source numbers (returned) */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D Array of magnitudes (returned) */
+	int *tc,	/* Array of fluxes (returned) */
+	char **tobj,	/* Array of object names (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ctgbin(		/* Bin sources from SAO TDC ASCII format catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat,	/* Catalog code from wcscat.h */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude by which to sort (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ctgstar(		/* Read one star entry from ASCII catalog, 0 if OK */
+	int istar,	/* Star sequence number in ASCII catalog */
+	struct StarCat *sc, /* Star catalog data structure */
+	struct Star *st); /* Star data structure, updated on return */
+    int isacat(		/* Return 1 if string is name of ASCII catalog file */
+	char *catpath);	/* Path to file to check */
+    struct StarCat *ctgopen( /* Open a Starbase, TDC ASCII, or TDC binary catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat);	/* Catalog code from wcscat.h */
+    void ctgclose(	/* Close Starbase, TDC ASCII, or TDC binary catalog
+			 * and free data structures */
+	struct StarCat *sc); /* Star catalog data structure */
+
+/* Subroutines for extracting sources from HST Guide Star Catalog */
+    int gscread(	/* Read sources by sky region from HST Guide Star Catalog */
+	int refcat,	/* Catalog code from wcscat.h (GSC or GSCACT) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int gscrnum(	/* Read sources by ID number from HST Guide Star Catalog */
+	int refcat,	/* Catalog code from wcscat.h (GSC or GSCACT) */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if >1, number of sources per log line */
+    int gscbin(		/* Bin sources from HST Guide Star Catalog */
+	int refcat,	/* Catalog code from wcscat.h (GSC or GSCACT) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if >1, number of sources per log line */
+    void setgsclass( int clas);		/* Set GSC object class to return (<0=all) */ /* Class of objects to return */
+
+/* Subroutine to read GSC II catalog over the web */
+    int gsc2read(	/* Read sources by sky region from GSC II Catalog */
+	char *refcatname, /* Name of catalog (GSC2 for 2.2; GSC2.3 for 2.3) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	char **gobj,	/* Array of object IDs (mixed letters and numbers) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if >1, number of sources per log line */
+    char *gsc2c2t(	/* Convert GSC2 buffer from comma- to tab-separated */
+	char *csvbuff);	/* Input comma-separated table */
+    char *gsc2t2t(	/* Clean up GSC2 tab-separated buffer */
+	char *tsvbuff);	/* Input tab-separated table */
+
+/* Subroutine to read SDSS catalog over the web */
+    int sdssread(	/* Read sources by sky region from SDSS Catalog */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned from tabread) */
+	char **gobj,	/* Array of object IDs (too long for integer*4) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if >1, number of sources per log line */
+    char *sdssc2t(	/* Convert SDSS buffer from comma- to tab-separated */
+	char *csvbuff);	/* Input comma-separated table */
+
+/* Subroutines to read local copy of 2MASS Point Source Catalog */
+    int tmcread(	/* Read sources by sky region from 2MASS Point Source Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TMPSC or TMXSC or TMPSCE) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of catalog numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tmcrnum(	/* Read sources by ID number from 2MASS Point Source Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TMPSC or TMXSC or TMPSCE) */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tmcbin(		/* Bin sources from 2MASS Point Source Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TMPSC or TMXSC or TMPSCE) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+/* Subroutines to read local copies of USNO catalogs */
+    int uacread(	/* Read sources by sky region from USNO A or SA Catalog */
+	char *refcatname, /* Name of catalog (UAC, USAC, UAC2, USAC2) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *unum,	/* Array of catalog numbers (returned) */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *uplate,	/* Array of plate numbers (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int uacrnum(	/* Read sources by ID number from USNO A or SA Catalog */
+	char *refcatname, /* Name of catalog (UAC, USAC, UAC2, USAC2) */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *unum,	/* Array of source numbers to look for */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *uplate,	/* Array of plate numbers (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int uacbin(		/* Bin sources from USNO A or SA Catalog */
+	char *refcatname, /* Name of catalog (UAC, USAC, UAC2, USAC2) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    void setuplate(	/* Set USNO catalog plate number to search */
+	int xplate);	/* If nonzero, use objects only from this plate */
+    int getuplate(void); /* Get USNO catalog plate number to search */
+
+    int ubcread(	/* Read sources by sky region from USNO B Catalog */
+	char *refcatname, /* Name of catalog (UB1 only for now) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *unum,	/* Array of ID numbers (returned) */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double *upra,	/* Array of right ascension proper motions (returned) */
+	double *updec,	/* Array of declination proper motions (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *upmni,	/* Array of number of ids and pm quality (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ubcrnum(	/* Read sources by ID number from USNO B Catalog */
+	char *refcatname, /* Name of catalog (UB1 only for now) */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *unum,	/* Array of source numbers to look for */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double *upra,	/* Array of right ascension proper motions (returned) */
+	double *updec,	/* Array of declination proper motions (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *upmni,	/* Array of number of ids and pm quality (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ubcbin(		/* Bin sources from USNO B Catalog */
+	char *refcatname, /* Name of catalog (UB1 only for now) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+    int ucacread(	/* Read sources by sky region from USNO UCAC 1 Catalog */
+	char *refcatname, /* Name of catalog (UCAC1 or UCAC2) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ucacrnum(	/* Read sources by ID number from USNO UCAC 1 Catalog */
+	char *refcatname, /* Name of catalog (UCAC1 or UCAC2) */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ucacbin(	/* Bin sources from USNO UCAC 1 Catalog */
+	char *refcatname, /* Name of catalog (UCAC1 or UCAC2) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+    int ujcread(	/* Read sources by sky region from USNO J Catalog */
+	char *refcatname, /* Name of catalog (UJC, xxxxx.usno) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *unum,	/* Array of catalog numbers (returned) */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *uplate,	/* Array of plate numbers (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ujcrnum(	/* Read sources by ID number from USNO J Catalog */
+	char *refcatname, /* Name of catalog (UJC, xxxxx.usno) */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *unum,	/* Array of source numbers to look for */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *uplate,	/* Array of plate numbers (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ujcbin(		/* Bin sources from USNO J Catalog */
+	char *refcatname, /* Name of catalog (UJC, xxxxx.usno) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+/* Subroutines to read a local copy of the Tycho-2 catalog */
+    int ty2read(	/* Read sources by sky region from Tycho 2 Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TYCHO2 or TYCHO2E */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ty2rnum(	/* Read sources by ID number from Tycho 2 Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TYCHO2 or TYCHO2E */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ty2bin(		/* Bin sources from Tycho 2 Catalog */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+/* Subroutines to read a local copy of the ACT catalog */
+    int actread(	/* Read sources by sky region from USNO ACT Catalog */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int actrnum(	/* Read sources by ID number from USNO ACT Catalog */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int actbin(		/* Bin sources from USNO ACT Catalog */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+    int skybotread (	/* Find solar system objects from SkyBot */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort asteroids by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Julian date for positions (current time if zero) */
+	double mag1,	/* Lower limiting magnitude (none if equal to mag2) */
+	double mag2,	/* Upper limiting magnitude (none if equal to mag1) */
+	int sortmag,	/* Magnitude by which to sort (1 to nmag) */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of asteroid numbers (returned) */
+	char **gobj,	/* Array of object IDs (too long for integer*4) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension motions (returned) */
+	double *gpdec,	/* Array of declination motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes and other info (returned) */
+	int *gtype,	/* Array of object classes (returned) */
+	int nlog);	/* 1 for diagnostics */
+
+    char *skybot2tab(	/* Convert SkyBot buffer from space- to tab-separated */
+	char *csvbuff);	/* Input comma-separated table */
+
+/* Subroutines to read SAO-TDC binary format catalogs */
+    int binread(	/* Read from sky region from SAO TDC binary format catalog */
+	char *bincat,	/* Name of reference star catalog file */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	struct StarCat **starcat, /* Star catalog data structure */
+	double *tnum,	/* Array of ID numbers (returned) */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D array of magnitudes (returned) */
+	int *tpeak,	/* Array of encoded spectral types (returned) */
+	char **tobj,	/* Array of object names (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int binrnum(	/* Read sources by ID number from SAO TDC binary format catalog */
+	char *bincat,	/* Name of reference star catalog file */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	int match,	/* If 1, match number exactly, else number is sequence*/
+	double *tnum,	/* Array of source numbers to look for */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D array of magnitudes (returned) */
+	int *tpeak,	/* Array of encoded spectral types (returned) */
+	char **tobj,	/* Array of object names (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int binbin(		/* Bin sources from SAO TDC binary format catalog */
+	char *bincat,	/* Name of reference star catalog file */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+    int binstar(	/* Read one star entry from binary catalog, 0 if OK */
+	struct StarCat *sc, /* Star catalog descriptor */
+	struct Star *st, /* Current star entry (returned) */
+	int istar);	/* Star sequence number in binary catalog */
+    struct StarCat *binopen( /* Open binary catalog, returning number of entries */
+	char *bincat);	/* Name of reference star catalog file */
+    void binclose(	/* Close binary catalog */
+	struct StarCat *sc); /* Star catalog descriptor */
+    int isbin(		/* Return 1 if TDC binary catalog file, else 0 */
+	char *filename); /* Name of file to check */
+
+/* Subroutines for extracting tab table information (in tabread.c) */
+    int tabread(	/* Read sources from tab table catalog */
+	char *tabcatname, /* Name of reference star catalog file */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude by which to sort (1 to nmag) */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	struct StarCat **starcat, /* Star catalog data structure */
+	double *tnum,	/* Array of source numbers (returned) */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D Array of magnitudes (returned) */
+	int *tpeak,	/* Array of peak counts (returned) */
+	char **tkey,	/* Array of values of additional keyword */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tabrnum(	/* Read sources from tab table catalog */
+	char *tabcatname, /* Name of reference star catalog file */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	struct StarCat **starcat, /* Star catalog data structure */
+	int match,	/* 1 to match star number exactly, else sequence num */
+	double *tnum,	/* Array of source numbers to look for */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D Array of magnitudes (returned) */
+	int *tpeak,	/* Array of peak counts (returned) */
+	char **tkey,	/* Array of values of additional keyword */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tabbin(		/* Read sources from tab table catalog */
+	char *tabcatname, /* Name of reference star catalog file */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude by which to sort (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tabxyread(	/* Read x, y, and magnitude from tab table star list */
+	char *tabcatname, /* Name of reference star catalog file */
+	double **xa,	/* Array of x coordinates (returned) */
+	double **ya,	/* Array of y coordinates (returned) */
+	double **ba,	/* Array of magnitudes (returned) */
+	int **pa,	/* Array of fluxes (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tabrkey(		/* Keyword values from tab table catalogs */
+	char *tabcatname, /* Name of reference star catalog file */
+	struct StarCat **starcat, /* Star catalog data structure */
+	int nnum,	/* Number of stars to look for */
+	double *tnum,	/* Array of source numbers to look for */
+	char *keyword,	/* Keyword for which to return values */
+	char **tval);	/* Returned values for specified keyword */
+    struct StarCat *tabcatopen(	/* Open tab table catalog */
+	char *tabpath,	/* Tab table catalog file pathname */
+	struct TabTable *tabtable, /* Tab table data structure */
+	int nbbuff);	/* Number of bytes in buffer; 0=read whole file */
+    void tabcatclose(	/* Close tab table catalog */
+	struct StarCat *sc);	/* Source catalog data structure */
+    int tabstar(	/* Read one star entry from tab table catalog, 0 if OK */
+	int istar,		/* Source sequence number in tab table catalog */
+	struct StarCat *sc,	/* Source catalog data structure */
+	struct Star *st,	/* Star data structure, updated on return */
+	int verbose);		/* 1 to print error messages */
+    struct TabTable *tabopen(	/* Open tab table file */
+	char *tabfile,	/* Tab table catalog file name */
+	int nbbuff);	/* Number of bytes in buffer; 0=read whole file */
+    void tabclose(	/* Free all arrays left open by tab table structure */
+	struct TabTable *tabtable); /* Tab table data structure */
+    char *gettabline(	/* Find a specified line in a tab table */
+	struct TabTable *tabtable, /* Tab table data structure */
+	int iline);	/* Line sequence number in tab table */
+    double tabgetra(	/* Return right ascension in degrees from tab table*/
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry);	/* sequence of entry on line */
+    double tabgetdec(	/* Return declination in degrees from tab table*/
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry);	/* sequence of entry on line */
+    double tabgetr8(	/* Return double number from tab table line */
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry);	/* sequence of entry on line */
+    int tabgeti4(	/* Return 4-byte integer from tab table line */
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry);	/* sequence of entry on line */
+    void settabkey(	/* Set tab table keyword to read for object */
+	char *keyword);	/* column header of desired value */
+    int tabgetk(	/* Get tab table entries for named column */
+	struct TabTable *tabtable, /* Tab table data structure */
+	struct Tokens *tabtok,  /* Line token structure */
+	char *keyword,	/* column header of desired value */
+	char *string,	/* character string (returned) */
+	int maxchar);	/* Maximum number of characters in returned string */
+    int tabgetc(	/* Get tab table entry for named column */
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry,	/* sequence of entry on line */
+	char *string,	/* character string (returned) */
+	int maxchar);	/* Maximum number of characters in returned string */
+    int tabparse(		/* Aeturn column names and positions in tabtable */
+	struct TabTable *tabtable); /* Tab table data structure */
+    int tabcol(		/* Find column for name (case-sensitive) */
+	struct TabTable *tabtable, /* Tab table data structure */
+	char *keyword);	/* column header of desired value */
+    int tabccol(	/* Find column for name  (case-insensitive) */
+	struct TabTable *tabtable, /* Tab table data structure */
+	char *keyword);	/* column header of desired value */
+    int istab(		/* Return 1 if tab table file, else 0 */
+	char *filename); /* Name of file to check */
+    char *gettaberr();	/* Return most recent tab table error message */
+    int gettabndec();	/* Return number of decimal places in tab catalog ids */
+
+/* Subroutines to read catalogs over the web, from SCAT, HST, ESO, or SDSS servers */
+    int webread(	/* Read sources by sky region from WWW catalog */
+	char *caturl,	/* URL of search engine */
+	char *refcatname, /* Name of catalog */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *unum,	/* Array of ID numbers (returned) */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double *upra,	/* Array of right ascension proper motions (returned) */
+	double *updec,	/* Array of declination proper motions (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *utype,	/* Array of integer catalog values (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int webrnum(	/* Read sources by ID number from WWW catalog */
+	char *caturl,	/* URL of search engine */
+	char *refcatname, /* Name of catalog */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	int match,	/* 1 to match star number exactly, else sequence num */
+	double *unum,	/* Array of source numbers to look for */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double *upra,	/* Array of right ascension proper motions (returned) */
+	double *updec,	/* Array of declination proper motions (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *utype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    char *webbuff(	/* Read URL into buffer across the web */
+	char *url,	/* URL to read */
+	int diag,	/* 1 to print diagnostic messages */
+	int *lbuff);	/* Length of buffer (returned) */
+    struct TabTable *webopen(	/* Open tab table across the web */
+	char *caturl,	/* URL of search engine */
+	char *srchpar,	/* Search engine parameters to append */
+	int nlog);	/* 1 to print diagnostic messages */
+
+/* Subroutines to read DAOPHOT-style catalogs of sources found in an image */
+    int daoread(	/* Read image source positions from x y mag file */
+	char *daocat,	/* Name of DAOFIND catalog file */
+	double **xa,	/* X and Y coordinates of stars, array returned */
+	double **ya,	/* X and Y coordinates of stars, array returned */
+	double **ba,	/* Instrumental magnitudes of stars, array returned */
+	int **pa,	/* Peak counts of stars in counts, array returned */
+	int nlog);	/* 1 to print each star's position */
+    int daoopen(	/* Open image source position x y mag file */
+	char *daofile);	/* DAOFIND catalog file name */
+    char *daoline(	/* Read line from image source position x y mag file */
+	int iline,	/* Star sequence number in DAOFIND catalog */
+	char *line);	/* Pointer to iline'th entry (returned updated) */
+
+/* Subroutines for sorting tables of star positions and magnitudes from sortstar.c */
+    void FluxSortStars(	/* Sort image stars by decreasing flux */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double *sb,	/* Brighness in counts */
+	int *sc,	/* Other 4-byte information */
+	int ns);	/* Number of stars to sort */
+    void MagSortStars(	/* Sort image stars by increasing magnitude */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm,		/* Number of magnitudes per star */
+	int ms);	/* Magnitude by which to sort (1 to nmag) */
+    void IDSortStars(	/* Sort image stars by increasing ID Number value */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    void RASortStars(	/* Sort image stars by increasing right ascension */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    void DecSortStars(	/* Sort image stars by increasing declination */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    void XSortStars(	/* Sort image stars by increasing image X value */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    void YSortStars(	/* Sort image stars by increasing image Y value */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    int MergeStars(	/* Merge multiple entries within given radius
+			 * return mean ra, dec, proper motion, and magnitude(s) */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm,		/* Number of magnitudes per star */
+	double rad,	/* Maximum separation in arcseconds to merge */
+	int log);	/* If >0, log progress every time mod number written */
+
+/* Catalog utility subroutines from catutil.c */
+
+/* Subroutines for dealing with catalogs */
+    int RefCat(		/* Return catalog type code, title, coord. system */
+	char *refcatname, /* Name of reference catalog */
+	char *title,	/* Description of catalog (returned) */
+	int *syscat,	/* Catalog coordinate system (returned) */
+	double *eqcat,	/* Equinox of catalog (returned) */
+	double *epcat,	/* Epoch of catalog (returned) */
+	int *catprop,	/* 1 if proper motion in catalog (returned) */
+	int *nmag);	/* Number of magnitudes in catalog (returned) */
+    int CatCode(	/* Return catalog type code */
+	char *refcatname); /* Name of reference catalog */
+    char *CatName(	/* Return catalog name given catalog type code */
+	int refcat,	/* Catalog code */
+	char *refcatname); /* Name of reference catalog */
+    char *CatSource(	/* Return catalog source description given catalog type code */
+	int refcat,	/* Catalog code */
+	char *refcatname); /* Name of reference catalog */
+    void CatID(		/* Return catalog ID keyword given catalog type code */
+	char *catid,	/* Catalog ID (returned) */
+	int refcat);	/* Catalog code */
+    double CatRad(	/* Return default search radius for given catalog */
+	int refcat);	/* Catalog code */
+    char *ProgName(	/* Return program name given program path used */
+	char *progpath0); /* Pathname by which program is invoked */
+    char *ProgCat(	/* Return catalog name given program name used */
+	char *progname); /* Program name which might contain catalog code */
+    void CatNum(	/* Return formatted source number */
+	int refcat,	/* Catalog code */
+	int nnfld,	/* Number of characters in number (from CatNumLen)
+			 * Print leading zeroes if negative */
+	int nndec,	/* Number of decimal places ( >= 0)
+			 * Omit leading spaces if negative */
+	double dnum,	/* Catalog number of source */
+	char *numstr);	/* Formatted number (returned) */
+    int CatNumLen(	/* Return length of source numbers */
+	int refcat,	/* Catalog code */
+	double maxnum,	/* Maximum ID number
+			 * (Ignored for standard catalogs) */
+	int nndec);	/* Number of decimal places ( >= 0) */
+    int CatNdec(	/* Return number of decimal places in source numbers */
+	int refcat);	/* Catalog code */
+    void CatMagName(	/* Return name of specified magnitude */
+	int imag,	/* Sequence number of magnitude */
+	int refcat,	/* Catalog code */
+	char *magname); /* Name of magnitude, returned */
+    int CatMagNum(	/* Returns number of magnitude specified by letter as int */
+	int imag,	/* int of magnitude letter */
+	int refcat);	/* Catalog code */
+    void CatTabHead (	/* Print heading for catalog search result table */
+	int refcat,	/* Catalog being searched */
+	int sysout,	/* Output coordinate system */
+	int nnfld,	/* Number of characters in ID column */
+	int mprop,	/* 1 if proper motion in catalog */
+	int nmag,	/* Number of magnitudes */
+	char *ranges,	/* Catalog numbers to print */
+	char *keyword,	/* Column to add to tab table output */
+	int gcset,	/* 1 if there are any values in gc[] */
+	int tabout,	/* 1 if output is tab-delimited */
+	int classd,	/* GSC object class to accept (-1=all) */
+	int printxy,	/* 1 if X and Y included in output */
+	char **gobj1,	/* Pointer to array of object names; NULL if none */
+	FILE *fd);	/* Output file descriptor; none if NULL */
+    int StrNdec(	/* Return number of decimal places in numeric string */
+	char *string);	/* Numeric string */
+    int NumNdec(	/* Return number of decimal places in a number */
+	double number); /* Floating point number */
+    void setdateform (	/* Set date format code */
+	int dateform0);	/* Date format code */
+    char *DateString(	/* Return string with epoch of position in desired format */
+	double epoch,	/* Date as fraction of a year */
+	int tabout);	/* 1 for tab-preceded output string, else space-preceded */
+    void setlimdeg(	/* Limit output in degrees (1) or hh:mm:ss dd:mm:ss (0) */
+	int degout);	/* 1 for fractional degrees, else sexagesimal hours, degrees */
+
+    void SearchLim(	/* Compute limiting RA and Dec */
+	double cra,	/* Longitude/Right Ascension of Center of search area in degrees */
+	double cdec,	/* Latitude/Declination of search area in degrees */
+	double dra,	/* Horizontal half-width in degrees */
+	double ddec,	/* Vertical half-width in degrees */
+	int syscoor,	/* Coordinate system */
+	double *ra1,	/* Lower right ascension limit in degrees (returned) */
+	double *ra2,	/* Upper right ascension limit in degrees (returned) */
+	double *dec1,	/* Lower declination limit in degrees (returned) */
+	double *dec2,	/* Upper declination limit in degrees (returned) */
+	int verbose);	/* 1 to print limits, else 0 */
+    void RefLim(	/* Compute limiting RA and Dec in new system */
+	double cra,	/* Longitude/Right Ascension of Center of search area in degrees */
+	double cdec,	/* Latitude/Declination of search area in degrees */
+	double dra,	/* Horizontal half-width in degrees */
+	double ddec,	/* Vertical half-width in degrees */
+	int sysc,	/* System of search coordinates */
+	int sysr,	/* System of reference catalog coordinates */
+	double eqc,	/* Equinox of search coordinates in years */
+	double eqr,	/* Equinox of reference catalog in years */
+	double epc,	/* Epoch of search coordinates in years */
+	double epr,	/* Epoch of reference catalog coordinates in years */
+	double secmarg,	/* Margin in arcsec/century to catch moving stars */
+	double *ramin,	/* Lower right ascension limit in degrees (returned) */
+	double *ramax,	/* Upper right ascension limit in degrees (returned) */
+	double *decmin,	/* Lower declination limit in degrees (returned) */
+	double *decmax,	/* Upper declination limit in degrees (returned) */
+	int *wrap,	/* 1 if search passes through 0:00:00 RA */
+	int verbose);	/* 1 to print limits, else 0 */
+    void movebuff (	/* Copy nbytes bytes from source+offs to dest+offd */
+	char *source,	/* Pointer to source */
+	char *dest,	/* Pointer to destination */
+	int nbytes,	/* Number of bytes to move */
+	int offs,	/* Offset in bytes in source from which to start copying */
+	int offd);	/* Offset in bytes in destination to which to start copying */
+
+/* Subroutines for dealing with ranges */
+    struct Range *RangeInit(	/* Initialize range structure from string */
+	char *string,	/* String containing numbers separated by , and - */
+	int ndef);	/* Maximum allowable range value */
+    int isrange(	/* Return 1 if string is a range of numbers, else 0 */
+	char *string);	/* String which might be a range of numbers */
+    void rstart(	/* Restart range */
+	struct Range *range); /* Range structure */
+    int rgetn(		/* Return number of values in all ranges */
+	struct Range *range); /* Range structure */
+    int rgeti4(		/* Return next number in range as integer */
+	struct Range *range); /* Range structure */
+    double rgetr8(	/* Return next number in range as double */
+	struct Range *range); /* Range structure */
+
+/* Subroutines for read values from keyword=value in blocks of text */
+    int ageti4(		/* Extract int value from keyword= value in string */
+	char *string,	/* character string containing <keyword>= <value> */
+	char *keyword,	/* character string containing the name of the keyword
+			 * the value of which is returned.  hget searches for a
+			 * line beginning with this string.  if "[n]" or ",n" is
+			 * present, the n'th token in the value is returned. */
+	int *ival);	/* Integer value, returned */
+    int agetr8(		/* Extract double value from keyword= value in string */
+	char *string,	/* character string containing <keyword>= <value> */
+	char *keyword,	/* character string containing the name of the keyword */
+	double *dval);	/* Double value, returned */
+    int agets(		/* Extract value from keyword= value in string */
+	char *string,	/* character string containing <keyword>= <value> */
+	char *keyword,	/* character string containing the name of the keyword */
+	int lval,	/* Size of value in characters
+			 * If negative, value ends at end of line */
+	char *value);	/* String (returned) */
+
+    int tmcid(		/* Return 1 if string is 2MASS ID, else 0 */
+	char *string,	/* Character string to check */
+	double *ra,	/* Right ascension (returned) */
+	double *dec);	/* Declination (returned) */
+
+/* Subroutines for VOTable output */
+    int vothead(	/* Print heading for VOTable SCAT output */
+	int refcat,	/* Catalog code */
+	char *refcatname, /* Name of catalog */
+	int mprop,	/* Proper motion flag */
+	int typecol,	/* Flag for spectral type */
+	int ns,		/* Number of sources found in catalog */
+	double cra,	/* Search center right ascension */
+	double cdec,	/* Search center declination */
+	double drad);	/* Radius to search in degrees */
+    void vottail();	/* Terminate VOTable SCAT output */
+
+/* Subroutines for version/date string */
+    void setrevmsg(	/* Set version/date string */
+	char *revmsg);	/* Version/date string */
+    char *getrevmsg(void); /* Return version/date string */
+
+/* Subroutines for fitting and evaluating polynomials */
+    void polfit(	/* Fit polynomial coefficients */
+	double *x,	/* Array of independent variable points */
+	double *y,	/* Array of dependent variable points */
+	double x0,	/* Offset to independent variable */
+	int npts,	/* Number of data points to fit */
+	int nterms,	/* Number of parameters to fit */
+	double *a,	/* Vector containing current fit values */
+	double *stdev);	/* Standard deviation of fit (returned) */
+    double polcomp(	/* Evaluate polynomial from polfit coefficients */
+	double xi,	/* Independent variable */
+	double x0,	/* Offset to independent variable */
+	int norder,	/* Number of coefficients */
+	double *a);	/* Vector containing coeffiecients */
+
+#else /* K&R prototypes */
+
+/* Subroutines for reading TDC ASCII catalogs (ctgread.c) */
+int ctgread();		/* Read sources by sky region from SAO TDC ASCII format catalog */
+int ctgrnum();		/* Read sources by number from SAO TDC ASCII format catalog */
+int ctgrdate();		/* Read sources by date range from SAO TDC ASCII format catalog */
+int ctgbin();		/* Bin sources from SAO TDC ASCII format catalog */
+int ctgstar();		/* Read one star entry from ASCII catalog, 0 if OK */
+int isacat();		/* Return 1 if string is name of ASCII catalog file */
+struct StarCat *ctgopen();
+void ctgclose();
+
+/* Subroutines for extracting sources from HST Guide Star Catalog */
+int gscread();		/* Read sources by sky region from HST Guide Star Catalog */
+int gscrnum();		/* Read sources by ID number from HST Guide Star Catalog */
+int gscbin();		/* Bin sources from HST Guide Star Catalog */
+void setgsclass();	/* Set GSC object class */
+
+/* Subroutine to read GSC II catalog over the web (gsc2read.c) */
+int gsc2read();		/* Read sources by sky region from GSC II Catalog */
+char *gsc2c2t();	/* Convert GSC2 buffer from comma- to tab-separated */
+char *gsc2t2t();	/* Clean up GSC2 tab-separated buffer */
+
+/* Subroutine to read SDSS catalog over the web (sdssread.c) */
+int sdssread();		/* Read sources by sky region from SDSS Catalog */
+char *sdssc2t();	/* Convert SDSS buffer from comma- to tab-separated */
+
+/* Subroutines to read local copy of 2MASS Point Source Catalog (tmcread.c) */
+int tmcread();		/* Read sources by sky region from 2MASS Point Source Catalog */
+int tmcrnum();		/* Read sources by ID number from 2MASS Point Source Catalog */
+int tmcbin();		/* Bin sources from 2MASS Point Source Catalog */
+
+/* Subroutines to read local copies of USNO A and SA catalogs (uacread.c) */
+int uacread();		/* Read sources by sky region from USNO A or SA Catalog */
+int uacrnum();		/* Read sources by ID number from USNO A or SA Catalog */
+int uacbin();		/* Bin sources from USNO A or SA Catalog */
+void setuplate();	/* Set USNO catalog plate number to search */
+int getuplate();	/* Get USNO catalog plate number to search */
+
+/* Subroutines to read local copies of USNO B catalogs (ubcread.c) */
+int ubcread();		/* Read sources by sky region from USNO B Catalog */
+int ubcrnum();		/* Read sources by ID number from USNO B Catalog */
+int ubcbin();		/* Bin sources from USNO B Catalog */
+
+/* Subroutines to read local copies of USNO UCAC catalogs (ucacread.c) */
+int ucacread();		/* Read sources by sky region from USNO UCAC 1 Catalog */
+int ucacrnum();		/* Read sources by ID number from USNO UCAC 1 Catalog */
+int ucacbin();		/* Bin sources from USNO UCAC 1 Catalog */
+
+/* Subroutines to read local copies of USNO UJ catalog (ucacread.c) */
+int ujcread();		/* Read sources by sky region from USNO J Catalog */
+int ujcrnum();		/* Read sources by ID number from USNO J Catalog */
+int ujcbin();		/* Bin sources from USNO J Catalog */
+
+/* Subroutines to read a local copy of the Tycho-2 catalog (ty2read.c) */
+int ty2read();		/* Read sources by sky region from Tycho 2 Catalog */
+int ty2rnum();		/* Read sources by ID number from Tycho 2 Catalog */
+int ty2bin();		/* Bin sources from Tycho 2 Catalog */
+
+/* Subroutines to read a local copy of the ACT catalog (actread.c) */
+int actread();		/* Read sources by sky region from USNO ACT Catalog */
+int actrnum();		/* Read sources by ID number from USNO ACT Catalog */
+int actbin();		/* Bin sources from USNO ACT Catalog */
+
+/* Subroutines to retrieve solar system objects over the web from SkyBot */
+int skybotread();	/* Find solar system objects from SkyBot */
+char *skybot2tab();	/* Convert SkyBot returned data to Starbase table */
+void setobs();		/* Set observatory using IAU code */
+void setobsname();	/* Set IAU code from observatory name */
+
+/* Subroutines to read SAO-TDC binary format catalogs (binread.c) */
+int binread();		/* Read sources by sky region from SAO TDC binary format catalog */
+int binrnum();		/* Read sources by ID number from SAO TDC binary format catalog */
+int binbin();		/* Bin sources from SAO TDC binary format catalog */
+int binstar();		/* Read one star entry from binary catalog, 0 if OK */
+int isbin();
+struct StarCat *binopen();
+void binclose();
+
+/* Subroutines for extracting tab table information (tabread.c) */
+int tabread();		/* Read sources from tab table catalog */
+int tabrnum();		/* Read sources from tab table catalog */
+int tabbin();		/* Read sources from tab table catalog */
+struct TabTable *tabopen();	/* Open tab table file */
+struct StarCat *tabcatopen();	/* Open tab table catalog */
+void tabcatclose();	/* Close tab table catalog */
+int tabxyread();	/* Read x, y, and magnitude from tab table star list */
+void settabkey();	/* Set tab table keyword to read for object */
+char *gettabline();	/* Find a specified line in a tab table */
+int tabrkey();		/* Keyword values from tab table catalogs */
+int tabcol();		/* Find column for name (case-sensitive) */
+int tabccol();		/* Find column for name (case-insensitive) */
+int tabgetk();		/* Get tab table entries for named column */
+int tabgetc();		/* Get tab table entry for named column */
+int tabgeti4();		/* Return 4-byte integer from tab table line */
+int tabparse();		/* Aeturn column names and positions in tabtable */
+double tabgetra();	/* Return right ascension in degrees from tab table*/
+double tabgetdec();	/* Return declination in degrees from tab table*/
+double tabgetr8();	/* Return double number from tab table line */
+void tabclose();	/* Free all arrays left open by tab table structure */
+char *gettaberr();	/* Return most recent tab table error message */
+int istab();		/* Return 1 if tab table file, else 0 */
+int gettabndec();	/* Return number of decimal places in tab catalog ids */
+
+/* Subroutines to read catalogs over the web, from SCAT, HST, ESO, or SDSS servers */
+int webread();		/* Read sources by sky region from catalog on the World Wide Web */
+int webrnum();		/* Read sources by ID number from catalog on the World Wide Web */
+char *webbuff();	/* Read URL into buffer across the web */
+struct TabTable *webopen();	/* Open tab table across the web */
+
+/* Subroutines to read DAOPHOT-style catalogs of sources found in an image */
+int daoread();		/* Read image source positions from x y mag file */
+int daoopen();		/* Open image source position x y mag file */
+char *daoline();	/* Read line from image source position x y mag file */
+
+/* Subroutines for sorting tables of star positions and magnitudes from sortstar.c */
+void FluxSortStars();	/* Sort image stars by decreasing flux */
+void MagSortStars();	/* Sort image stars by increasing magnitude */
+void IDSortStars();	/* Sort image stars by increasing ID Number value */
+void RASortStars();	/* Sort image stars by increasing right ascension */
+void DecSortStars();	/* Sort image stars by increasing declination */
+void XSortStars();	/* Sort image stars by increasing image X value */
+void YSortStars();	/* Sort image stars by increasing image Y value */
+int MergeStars();	/* Merge multiple entries within given radius */
+
+/* Catalog utility subroutines from catutil.c */
+
+/* Subroutines for dealing with catalogs */
+int CatCode();		/* Return catalog type code */
+int RefCat();		/* Return catalog type code, title, coord. system */
+char *CatName();	/* Return catalog name given catalog type code */
+char *CatSource();	/* Return catalog source description given catalog type code */
+char *ProgCat();	/* Return catalog name given program name used */
+char *ProgName();	/* Return program name given program path used */
+char *CatName();	/* Return catalog name given catalog type code */
+void CatID();		/* Return catalog ID keyword given catalog type code */
+void CatNum();		/* Return formatted source number */
+int CatNumLen();	/* Return length of source numbers */
+int CatNdec();		/* Return number of decimal places in source numbers */
+void CatMagName();	/* Return name of specified magnitude */
+int CatMagNum();	/* Returns number of magnitude specified by letter as int */
+double CatRad();	/* Return default search radius for given catalog */
+int tmcid();		/* Return 1 if string is 2MASS ID, else 0 */
+
+int NumNdec();		/* Return number of decimal places in a number */
+int StrNdec();		/* Return number of decimal places in numeric string */
+void setdateform();	/* Set date format code */
+void setlimdeg();	/* Limit output in degrees (1) or hh:mm:ss dd:mm:ss (0) */
+char *DateString();		/* Convert epoch to output format */
+void SearchLim();	/* Compute limiting RA and Dec */
+void RefLim();		/* Compute limiting RA and Dec in new system */
+int ageti4();		/* Extract int value from keyword= value in string */
+int agetr8();		/* Extract double value from keyword= value in string */
+int agets();		/* Extract value from keyword= value in string */
+void bv2sp();		/* Approximate main sequence spectral type from B - V */
+void moveb();		/* Copy nbytes bytes from source+offs to dest+offd */
+
+/* Subroutines for dealing with ranges */
+struct Range *RangeInit();	/* Initialize range structure from string */
+int isrange();		/* Return 1 if string is a range of numbers, else 0 */
+int rgetn();		/* Return number of values in all ranges */
+int rgeti4();		/* Return next number in range as integer */
+double rgetr8();	/* Return next number in range as double */
+void rstart();		/* Restart range */
+
+/* Subroutines for VOTable output */
+int vothead();		/* Print heading for VOTable SCAT output */
+void vottail();		/* Terminate VOTable SCAT output */
+
+/* Subroutines for version/date string */
+void setrevmsg();	/* Set version/date string */
+char *getrevmsg();	/* Return version/date string */
+
+/* Subroutines for fitting and evaluating polynomials */
+void polfit();		/* Fit polynomial coefficients */
+double polcomp();	/* Evaluate polynomial from polfit coefficients */
+
+#endif  /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif  /* _wcscat_h_ */
+
+/* Sep 22 1998  New header file (star.h)
+ * Oct 16 1998  Add more options for ASCII catalogs
+ * Oct 20 1998  Add object name to binary files
+ * Oct 21 1998	New file (wcscat.h)
+ * Oct 26 1998	Combined wcscat.h and star.h
+ * Oct 27 1998	Add SAOimage region shapes
+ * Nov  9 1998	Add rasorted flag to catalog structure
+ * Nov 20 1998	Add support for USNO A-2.0 and SA-2.0 catalogs
+ * Dec  8 1998	Add support for the Hipparcos and ACT catalogs
+ *
+ * Jan 25 1999	Add declarations for tab table access
+ * Jan 25 1999	Add declarations for dealing with ranges of numbers
+ * Feb  2 1999	Add number of decimal places in star number to StarCat
+ * Feb 11 1999	Add coordinate system info to star structure
+ * Feb 11 1999	Change starcat.insys to starcat.coorsys for consistency
+ * May 14 1999	Update Star and StarCat structure to cover tab tables
+ * May 19 1999	Update StarCat structure to include epoch from catalog
+ * June 4 1999	Add CatNumLen()
+ * Jun 14 1999	Add SearchLim()
+ * Jun 30 1999	Add isrange()
+ * Jul  1 1999	Add declarations for date/time conversions in dateutil.c
+ * Jul  2 1999	Add rstart()
+ * Jul 26 1999	Add Yale Bright Star Catalog
+ * Aug 16 1999	Add RefLim() to get converted search coordinates right
+ * Aug 25 1999	Add ACT catalog
+ * Sep 10 1999	Move special case setting from argument list to subroutines
+ * Sep 13 1999	Add subroutines to access data structure for single stars
+ * Oct  1 1999	Add structure and subroutines for tokenized strings
+ * Oct 22 1999	Change cat*() to ctg*() to avoid system conflict
+ * Oct 29 1999	Add tabget() subroutines
+ * Nov  1 1999	Increase maximum number of tokens on a line from 20 to 100
+ * Nov  2 1999	Move date utilities to fitsfile.h
+ *
+ * Jan 10 2000	Add column names to catalog data structure
+ * Jan 11 2000	Add gettabndec()
+ * Feb  9 2000	Add proper motion entry information to star data structure
+ * Feb 16 2000	Add gettaberr() to return tab table error message
+ * Mar  1 2000	Add isfile() and agets() to help with ASCII files
+ * Mar  8 2000	Add ProgCat() to return catalog name from program name used
+ * Mar  8 2000	Add ProgName() to extract program name from path used
+ * Mar 10 2000	Add PropCat() to tell whether a catalog has proper motions
+ * Mar 27 2000	Add tabxyread()
+ * Apr  3 2000	Add option in catalog structure to ignore extra info
+ * May 22 2000	Add Tycho 2 support, bv2sp()
+ * May 26 2000	Add separate pointer to header in tab table structure
+ * May 26 2000	Add separate pointer to table name in tab table structure
+ * Jul 12 2000	Add catalog type code to ctalog data structure
+ * Sep 20 2000	Add isacat() to detect ASCII catalog files
+ * Sep 25 2000	Add starcat.sptype to flag spectral type in catalog
+ * Oct 23 2000	Add USNO plate catalog to catalog type table
+ * Oct 26 2000	Add proper motion flags for seconds and arcseconds per century
+ * Oct 31 2000	Add proper motion flags for milliseconds per year
+ * Nov  2 2000	Add parallax and radial velocity to star structure
+ * Nov 21 2000	Add WEBCAT catalog type for tab ctalogs returned from the Web
+ * Nov 22 2000	Add webread() and webrnum()
+ * Nov 28 2000	Add tabparse()
+ * Nov 30 2000	Add spectral type to catalog header; make star->isp 4 char.
+ * Dec 13 2000	Add StrNdec() to get number of decimal places in number strings
+ * Dec 15 2000	Add CatNdec() to get number of decimal places in source numbers
+ * Dec 18 2000	Drop PropCat(), a cludgy proper motion flag
+ *
+ * Mar 22 2001	Add web search flag in catalog data structure
+ * Mar 27 2001	Add shapes in pixels to SAOimage region options
+ * May 14 2001	Add 2MASS Point Source Catalog flags
+ * May 22 2001	Add declination sorting
+ * May 24 2001	Add 2MASS Point Source Catalog subroutines
+ * May 29 2001	Add length of star number to catalog structure
+ * May 30 2001	Add third magnitude for tab tables to catalog structure
+ * Jun 15 2001	Add CatName() and CatID()
+ * Jun 19 2001	Add parallax error to catalog and star structures
+ * Jun 20 2001	Add webopen(), GSC2, fourth magnitude to star and starcat
+ * Jul 12 2001	Add separate web access subroutine, webbuff()
+ * Jul 23 2001	Add ageti4() and agetr8()
+ * Jul 24 2001	Add polfit() and polcomp()
+ * Aug  8 2001	Add keyrv and option to set mprop to 2 to include rv/cz
+ * Sep 10 2001	Add entry line and distance from search center to Star
+ * Sep 13 2001	Add YSortStars() and SORT_Y
+ * Sep 14 2001	Add lbuff to TabTable structure
+ * Sep 20 2001	Add CatMagName()
+ * Sep 25 2001	Move isfile() to fitsfile.h
+ * Oct 16 2001	Add tabdash pointer to tabtable data structure
+ *
+ * Apr  9 2002	Fix typo in gettaberr() declaration
+ * Apr 10 2002	Add CatMagNum()
+ * May  6 2002	Increase object name length from 31 to 79 characters
+ * May 13 2002	Add NumNdec(), gsc2read(), and gsc2rnum()
+ * Aug  6 2002	Make magnitude entries and positions vectors of 10
+ * Oct 30 2002	Add epoch keyword and FITS date to StarCat data structure
+ *
+ * Jan 16 2003	Add USNO-B1.0 catalog
+ * Mar 24 2003	Add CatCde() to get only catalog code
+ * Apr  3 2003	Add ubcread(), ubcrnum(), and FluxSortStars()
+ * Apr  3 2003	Drop gsc2rnum()
+ * Apr 14 2003	Add setrevmsg() and getrevmsg()
+ * Apr 24 2003	Add UCAC1 and UCAC2, ucacread() and ucacrnum()
+ * May 20 2003	Add TMIDR2 for 2MASS PSC Interim Data Release 2
+ * Sep 16 2003	Add SORT_MERGE for scat
+ * Sep 25 2003	Add *bin() subroutines for catalog binning
+ * Dec  3 2003	Add USNO YB6 catalog
+ *
+ * Jan  5 2004	Add SDSS catalog
+ * Jan 12 2004	Add 2MASS Extended Source catalog and size to star structure
+ * Jan 14 2004	Add CatSource() subroutine to simplify help message creation
+ * Jan 22 2004	Add setlimdeg() to print limit coordinates in degrees
+ * Mar 16 2004	Add MergeStars()
+ * Apr 23 2004	Add ctgrdate()
+ * Aug 31 2004	Increase MAXTOKENS from 100 to 200
+ * Sep  2 2004	Increase MAXTOKENS from 200 to 1000
+ *
+ * Jul 27 2005	Add date format codes and DateString()
+ * Aug  5 2005	Add Tycho-2 and 2MASS PSC with magnitude errors
+ *
+ * Jan  6 2006	Add CatRad() subroutine
+ * Mar 17 2006	Make vothead() int as it now returns the number of fields
+ * Apr  3 2006	Add tmcid() to check for 2MASS identifiers
+ * Apr  3 2006	Add setdateform() to set output date format
+ * Apr 12 2006	Add SORT_ID for scat to sort catalog entries by ID number
+ * Jun 20 2006	Add IDSortStars()
+ *
+ * Jan 10 2006	Add ANSI C function prototypes
+ * Jan 11 2007	Move token access subroutines to fileutil.c/fitsfile.h
+ * Mar 13 2007	Add gsc2c2t() to convert CSV GSC2 buffer to TSV table
+ * Mar 13 2007	Add gobj object name to gsc2read()
+ * Jul  5 2007	Add SKYBOT=31 to catalog list
+ * Jul 13 2007	Add skybotread() and skybot2tab()
+ * Jul 18 2007	Add tabccol() and PM_ARCSECHR for SkyBot
+ * Nov 28 2007	Add moveb() which used to be local to binread()
+ *
+ * Oct 24 2008	Add gsct2t() to clean up tab-separated table from STScI
+CASB
+ *
+ * Sep 25 2009	Rename moveb() to movebuff()
+ * Sep 25 2009	Add UCAC3 as catalog code 32
+ * Oct 30 2009	Add position and proper motion error to star structure
+ * Nov  2 2009	Add numbers of images and catalogs to star structure
+ * Nov  3 2009	Parameterize as MAXNMAG the maximum number of magnitudes
+ */
diff --git a/Code/src/libwcs/wcscat1.h b/Code/src/libwcs/wcscat1.h
new file mode 100644
index 0000000000000000000000000000000000000000..bf092b9b5552cfe1fa3638c0ff301dc1d4f50d60
--- /dev/null
+++ b/Code/src/libwcs/wcscat1.h
@@ -0,0 +1,1637 @@
+/*** File libwcs/wcscat.h
+ *** January 10, 2007
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Copyright (C) 1998-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#ifndef _wcscat_h_
+#define _wcscat_h_
+
+/* Data structure for SAO TDC ASCII and binary star catalog entries */
+struct Star {
+    float rdum;
+    float xno;		/* Catalog number */
+    double ra;		/* Right Ascension (degrees) */
+    double dec;		/* Declination (degrees) */
+    char isp[24];	/* Spectral type or other 2-char identifier */
+    short mag[11];	/* Up to 10 Magnitudes * 100 */
+    double rapm;	/* RA proper motion (degrees per year) */
+    double decpm;	/* Dec proper motion (degrees per year) */
+    double xmag[11];	/* Up to 10 Magnitudes */
+    double num;		/* Actual star number */
+    int coorsys;	/* Coordinate system (WCS_J2000, WCS_B1950,...) */
+    double equinox;	/* Equinox of coordinate system as fractional year */
+    double epoch;	/* Epoch of position as fractional year */
+    double parallax;	/* Parallax in arcseconds */
+    double pxerror;	/* Parallax error in arcseconds */
+    double radvel;	/* Radial velocity in km/sec, positive away */
+    double dist;	/* Distance from search center in arcseconds */
+    double size;	/* Semi-major axis in arcseconds */
+    char *entry;	/* Line copied from input catalog */
+    char objname[80];	/* Object name */
+    int peak;		/* Peak flux per pixel in star image */
+};
+
+/* Catalog proper motion units */
+#define PM_MASYR		1	/* milliarcseconds per year */
+#define PM_ARCSECYR		2	/* arcseconds per year */
+#define PM_DEGYR		3	/* degrees per year */
+#define PM_RADYR		4	/* radians per year */
+#define PM_TSECYR		5	/* seconds of time (RA) per century */
+#define PM_ARCSECCEN		6	/* arcseconds per year */
+#define PM_TSECCEN		7	/* seconds of time (RA) per century */
+#define PM_MTSYR		8	/* milliseconds of time (RA) per year */
+
+/* Data structure for SAO TDC ASCII and binary star catalogs */
+struct StarCat {
+    int star0;		/* Subtract from star number for file sequence number */
+    int star1;		/* First star number in file */
+    int nstars;		/* Number of stars in file */
+    int stnum;		/* Star number format in catalog file:
+			  <0: -stnum-character name at end instead of number
+			   0:  no star i.d. numbers
+			   1: Real*4 star i.d. numbers
+			   2: Integer*4 <region><nnnn>
+			   3: Integer*4 <region><nnnnn>
+			   4: Integer*4 <nnnnnnnnn>
+			   5: Character ID instead of number in ASCII files */
+    int mprop;		/* 1 if proper motion is included */
+			/* 2 if radial velocity is included */
+    int nmag;		/* Number of magnitudes present
+			   Negative for J2000 catalog */
+    int nbent;		/* Number of bytes per star entry */
+    int	rasorted;	/* 1 if RA-sorted, else 0 */
+    int	ignore;		/* 1 if ignoring info after position and magnitude */
+    FILE *ifcat;	/* File descriptor for catalog file */
+    char isfil[24];	/* Star catalog file name */
+    char isname[64];	/* Star catalog description */
+    int  byteswapped;	/* 1 if catalog is byte-reversed from CPU */
+    int  refcat;	/* Code for type of catalog (TXTCAT, BINCAT, etc.) */
+    int  coorsys;	/* Coordinate system
+			   B1950 J2000 Galactic Ecliptic */
+    double epoch;	/* Epoch of catalog coordinates in years */
+    double equinox;	/* Equinox of catalog coordinates in years */
+    char inform;	/* Coordinate format
+			   (B>inary D>egrees H>MS T>able U>SNO) */
+    char incdir[128];	/* Catalog directory pathname */
+    char incfile[32];	/* Catalog file name */
+    int ncobj;		/* Length of object name in binary star entry */
+    int nnfld;		/* Length of star number  */
+    int nndec;		/* Number of decimal places in star number */
+    int nepoch;		/* 1 if epoch of coordinates is present */
+    int sptype;		/* 1 if spectral type is present in catalog */
+    int plate;		/* 1 if plate or field number is present in catalog */
+    char *catbuff;	/* Pointer to start of catalog */
+    char *catdata;	/* Pointer to first entry in catalog */
+    char *catline;	/* Pointer to current entry in catalog */
+    char *catlast;	/* Pointer to one past end of last entry in catalog */
+    int  istar;		/* Number of current catalog entry */
+    struct TabTable *startab;	/* Structure for tab table catalog */
+    int entid;		/* Entry number for ID */
+    int entra;		/* Entry number for right ascension */
+    int entdec;		/* Entry number for declination */
+    int entmag[10];	/* Entry numbers for up to 10 magnitudes */
+    int entpeak;	/* Entry number for peak counts */
+    int entepoch;	/* Entry number for epoch of observation */
+    int entdate;	/* Entry number for FITS-format date of observation */
+    int entname;	/* Entry number for object name */
+    int entadd;		/* Entry number for additional keyword */
+    int entrpm;		/* Entry number for proper motion in right ascension */
+    int entdpm;		/* Entry number for proper motion in declination */
+    int entpx;		/* Entry number for parallax */
+    int entpxe;		/* Entry number for parallax error */
+    int entrv;		/* Entry number for radial velocity */
+    int enttype;	/* Entry number for spectral type */
+    int entsize;	/* Entry number for size of object */
+    int rpmunit;	/* Units for RA proper motion (PM_x) */
+    int dpmunit;	/* Units for DEC proper motion (PM_x) */
+    char *caturl;	/* set if web search, else NULL */
+    char keyid[16];	/* Entry name for ID */
+    char keyra[16];	/* Entry name for right ascension */
+    char keydec[16];	/* Entry name for declination */
+    char keymag[10][16]; /* Entry name for up to 10 magnitudes */
+    char keyrpm[16];	/* Entry name for right ascension proper motion */
+    char keydpm[16];	/* Entry name for declination proper motion */
+    char keypeak[16];	/* Entry name for integer code */
+    char keytype[16];	/* Entry name for spectral type */
+    char keyrv[16];	/* Entry name for radial velocity */
+    char keyadd[16];	/* Entry name for additional keyword */
+    char keyepoch[16];	/* Entry name for epoch */
+};
+
+/* Data structure for tab table files */
+struct TabTable {
+    char *filename;	/* Name of tab table file */
+    int nlines;		/* Number of entries in table */
+    char *tabname;	/* Name of this table or NULL */
+    char *tabbuff;	/* Pointer to start of saved tab table in memory */
+    char *tabheader;	/* Pointer to start of line containing table header */
+    char *tabhead;	/* Pointer to start of line containing column heading */
+    char *tabdash;	/* Pointer to start of line with dashes after column headings */
+    char *tabdata;	/* Pointer to start of first line of table data */
+    int lhead;		/* Number of bytes before first data line */
+    int iline;		/* Number of current line (1=first) */
+    int lline;		/* Length in bytes of line buffer */
+    char *tabline;	/* Pointer to start of current line */
+    FILE *tcat;		/* File descriptor for tab table file */
+    int ncols;		/* Number of columns per table entry */
+    char **colname;	/* Column names */
+    int *lcol;		/* Lengths of column header names */
+    int *lcfld;		/* Number of columns in field (hyphens) */
+    int lbuff;		/* Number of bytes in entire tab table */
+};
+
+/* Source catalog flags and subroutines */
+
+/* Source catalog flags returned from CatCode */
+#define GSC		1	/* HST Guide Star Catalog */
+#define UJC		2	/* USNO UJ Star Catalog */
+#define UAC		3	/* USNO A Star Catalog */
+#define USAC		4	/* USNO SA Star Catalog */
+#define SAO		5	/* SAO Star Catalog */
+#define IRAS		6	/* IRAS Point Source Catalog */
+#define PPM		7	/* PPM Star Catalog */
+#define TYCHO		8	/* Tycho Star Catalog */
+#define UA1		9	/* USNO A-1.0 Star Catalog */
+#define UA2		10	/* USNO A-2.0 Star Catalog */
+#define USA1		11	/* USNO SA-1.0 Star Catalog */
+#define USA2		12	/* USNO SA-2.0 Star Catalog */
+#define HIP		13	/* Hipparcos Star Catalog */
+#define ACT		14	/* USNO ACT Star Catalog */
+#define BSC		15	/* Yale Bright Star Catalog */
+#define TYCHO2		16	/* Tycho-2 Star Catalog */
+#define USNO		17	/* USNO-format plate catalog */
+#define TMPSC		18	/* 2MASS All-Sky Point Source Catalog */
+#define GSCACT		19	/* GSC-ACT revised Guide Star Catalog */
+#define GSC2		20	/* GSC II version 2.2 */
+#define UB1		21	/* USNO B-1.0 Star Catalog */
+#define UCAC1		22	/* USNO CCD Astrograph Catalog 1.0 */
+#define UCAC2		23	/* USNO CCD Astrograph Catalog 2.0 */
+#define TMIDR2		24	/* 2MASS IDR2 Point Source Catalog */
+#define YB6		25	/* USNO YB6 Catalog */
+#define SDSS		26	/* Sloan Digital Sky Survey Catalog */
+#define TMXSC		27	/* 2MASS Extended Source Catalog */
+#define TMPSCE		28	/* 2MASS Point Source Catalog with mag errors */
+#define TYCHO2E		29	/* Tycho-2 Star Catalog with magnitude errors */
+#define SKY2K		30	/* SKY2000 Master Catalog */
+#define TABCAT		-1	/* StarBase tab table catalog */
+#define BINCAT		-2	/* TDC binary catalog */
+#define TXTCAT		-3	/* TDC ASCII catalog */
+#define WEBCAT		-4	/* Tab catalog via the web */
+#define NUMCAT		30	/* Number of predefined catalogs */
+
+/* Structure for access to tokens within a string */
+#define MAXTOKENS 1000    /* Maximum number of tokens to parse */
+#define MAXWHITE 20     /* Maximum number of different whitespace characters */
+struct Tokens {
+    char *line;		/* Line which has been parsed */
+    int lline;		/* Number of characters in line */
+    int ntok;		/* Number of tokens on line */
+    int nwhite;		/* Number of whitespace characters */
+    char white[MAXWHITE]; /* Whitespace (separator) characters */
+    char *tok1[MAXTOKENS]; /* Pointers to start of tokens */
+    int ltok[MAXTOKENS]; /* Lengths of tokens */
+    int itok;		/* Current token number */
+};
+#define EP_EP   1	/* Output epoch as fractional year */
+#define EP_JD   2	/* Output epoch as Julian Date */
+#define EP_MJD  3	/* Ouput epoch as Modified Julian Date */
+#define EP_FD   4	/* Output epoch in FITS format (yyyy-mm-dd) */
+#define EP_ISO  5	/* Output epoch in ISO format (yyyy-mm-ddThh:mm:ss) */
+
+/* Structure for dealing with ranges */
+#define MAXRANGE 20
+struct Range {
+    double first;	/* Current minimum value */
+    double last;	/* Current maximum value */
+    double step;	/* Current step in value */
+    double value;	/* Current value */
+    double ranges[MAXRANGE*3];	/* nranges sets of first, last, step */
+    int nvalues;	/* Total number of values in all ranges */
+    int nranges;	/* Number of ranges */
+    int irange;		/* Index of current range */
+};
+
+/* Flags for sorting catalog search results */
+#define SORT_UNSET	-1	/* Catalog sort flag not set yet */
+#define SORT_NONE	0	/* Do not sort catalog output */
+#define SORT_MAG	1	/* Sort output by magnitude */
+#define SORT_DIST	2	/* Sort output by distance from center */
+#define SORT_RA		3	/* Sort output by right ascension */
+#define SORT_DEC	4	/* Sort output by declination */
+#define SORT_X		5	/* Sort output by image X coordinate */
+#define SORT_Y		6	/* Sort output by image Y coordinate */
+#define SORT_ID		7	/* Merge close catalog objects */
+#define SORT_MERGE	8	/* Merge close catalog objects */
+
+/* Shapes for SAOimage region file output */
+#define WCS_CIRCLE 1	/* circle shape for SAOimage plotting */
+#define WCS_SQUARE 2	/* square shape for SAOimage plotting */
+#define WCS_DIAMOND 3	/* diamond shape for SAOimage plotting */
+#define WCS_CROSS 4	/* cross shape for SAOimage plotting */
+#define WCS_EX 5	/* x shape for SAOimage plotting */
+#define WCS_VAR 6	/* variable (+ and x) shape for HSTGSC plotting */
+#define WCS_PCIRCLE 11	/* pixel circle shape for SAOimage plotting */
+#define WCS_PSQUARE 12	/* pixel square shape for SAOimage plotting */
+#define WCS_PDIAMOND 13	/* pixel diamond shape for SAOimage plotting */
+#define WCS_PCROSS 14	/* pixel cross shape for SAOimage plotting */
+#define WCS_PEX 15	/* pixel ex shape for SAOimage plotting */
+#define WCS_PVAR 16	/* pixel variable (+ and x) shape for HSTGSC plotting */
+
+/* Subroutines for extracting sources from catalogs */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __STDC__   /* Full ANSI prototypes */
+
+/* Subroutines for reading any catalogs, including TDC ASCII catalogs */
+
+    int ctgread(	/* Read sources by sky region from any catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat,	/* Catalog code from wcscat.h */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nsmax,	/* Maximum number of stars to be returned */
+	struct StarCat **starcat, /* Catalog data structure */
+	double *tnum,	/* Array of ID numbers (returned) */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D array of magnitudes (returned) */
+	int *tc,	/* Array of fluxes (returned) */
+	char **tobj,	/* Array of object names (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ctgrnum(	/* Read sources by number from any catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat,	/* Catalog code from wcscat.h */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	struct StarCat **starcat, /* Star catalog data structure */
+	int match,	/* 1 to match star number exactly, else sequence num */
+	double *tnum,	/* Array of source numbers to look for */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D Array of magnitudes (returned) */
+	int *tpeak,	/* Array of peak counts (returned) */
+	char **tkey,	/* Array of values of additional keyword */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ctgrdate(	/* Read sources by date from SAO TDC ASCII format catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat,	/* Catalog code from wcscat.h */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	struct StarCat **starcat, /* Star catalog data structure */
+	double date1,	/* Start time as Modified Julian Date or Julian Date */
+	double date2,	/* End time as Modified Julian Date or Julian Date */
+	int nmax,	/* Maximum number of stars to look for */
+	double *tnum,	/* Array of source numbers (returned) */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D Array of magnitudes (returned) */
+	int *tc,	/* Array of fluxes (returned) */
+	char **tobj,	/* Array of object names (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ctgbin(		/* Bin sources from SAO TDC ASCII format catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat,	/* Catalog code from wcscat.h */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude by which to sort (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ctgstar(		/* Read one star entry from ASCII catalog, 0 if OK */
+	int istar,	/* Star sequence number in ASCII catalog */
+	struct StarCat *sc, /* Star catalog data structure */
+	struct Star *st); /* Star data structure, updated on return */
+    int isacat(		/* Return 1 if string is name of ASCII catalog file */
+	char *catpath);	/* Path to file to check */
+    struct StarCat *ctgopen( /* Open a Starbase, TDC ASCII, or TDC binary catalog */
+	char *catfile,	/* Name of reference star catalog file */
+	int refcat);	/* Catalog code from wcscat.h */
+    void ctgclose(	/* Close Starbase, TDC ASCII, or TDC binary catalog
+			 * and free data structures */
+	struct StarCat *sc); /* Star catalog data structure */
+
+/* Subroutines for extracting sources from HST Guide Star Catalog */
+    int gscread(	/* Read sources by sky region from HST Guide Star Catalog */
+	int refcat,	/* Catalog code from wcscat.h (GSC or GSCACT) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int gscrnum(	/* Read sources by ID number from HST Guide Star Catalog */
+	int refcat,	/* Catalog code from wcscat.h (GSC or GSCACT) */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	int match,	/* 1 to match star number exactly, else sequence num */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int gscbin(		/* Bin sources from HST Guide Star Catalog */
+	int refcat,	/* Catalog code from wcscat.h (GSC or GSCACT) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    void setgsclass(	/* Set GSC object class to return (<0=all) */
+	int class);	/* Class of objects to return */
+
+/* Subroutine to read GSC II catalog over the web */
+    int gsc2read(	/* Read sources by sky region from GSC II Catalog */
+	char *refcatname, /* Name of catalog (GSC2 for 2.2; GSC2.3 for 2.3) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+/* Subroutine to read SDSS catalog over the web */
+    int sdssread(	/* Read sources by sky region from SDSS Catalog */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	char **gobj,	/* Array of object IDs (too long for integer*4) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    char *sdssc2t(	/* Convert SDSS buffer from comma- to tab-separated */
+	char *csvbuff);	/* Input comma-separated table */
+
+/* Subroutines to read local copy of 2MASS Point Source Catalog */
+    int tmcread(	/* Read sources by sky region from 2MASS Point Source Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TMPSC or TMXSC or TMPSCE) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of catalog numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tmcrnum(	/* Read sources by ID number from 2MASS Point Source Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TMPSC or TMXSC or TMPSCE) */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tmcbin(		/* Bin sources from 2MASS Point Source Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TMPSC or TMXSC or TMPSCE) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+/* Subroutines to read local copies of USNO catalogs */
+    int uacread(	/* Read sources by sky region from USNO A or SA Catalog */
+	char *refcatname, /* Name of catalog (UAC, USAC, UAC2, USAC2) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *unum,	/* Array of catalog numbers (returned) */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *uplate,	/* Array of plate numbers (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int uacrnum(	/* Read sources by ID number from USNO A or SA Catalog */
+	char *refcatname, /* Name of catalog (UAC, USAC, UAC2, USAC2) */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *unum,	/* Array of source numbers to look for */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *uplate,	/* Array of plate numbers (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int uacbin(		/* Bin sources from USNO A or SA Catalog */
+	char *refcatname, /* Name of catalog (UAC, USAC, UAC2, USAC2) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    void setuplate(	/* Set USNO catalog plate number to search */
+	int xplate);	/* If nonzero, use objects only from this plate */
+    int getuplate(void); /* Get USNO catalog plate number to search */
+
+    int ubcread(	/* Read sources by sky region from USNO B Catalog */
+	char *refcatname, /* Name of catalog (UB1 only for now) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *unum,	/* Array of ID numbers (returned) */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double *upra,	/* Array of right ascension proper motions (returned) */
+	double *updec,	/* Array of declination proper motions (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *upmni,	/* Array of number of ids and pm quality (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ubcrnum(	/* Read sources by ID number from USNO B Catalog */
+	char *refcatname, /* Name of catalog (UB1 only for now) */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *unum,	/* Array of source numbers to look for */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double *upra,	/* Array of right ascension proper motions (returned) */
+	double *updec,	/* Array of declination proper motions (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *upmni,	/* Array of number of ids and pm quality (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ubcbin(		/* Bin sources from USNO B Catalog */
+	char *refcatname, /* Name of catalog (UB1 only for now) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+    int ucacread(	/* Read sources by sky region from USNO UCAC 1 Catalog */
+	char *refcatname, /* Name of catalog (UCAC1 or UCAC2) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ucacrnum(	/* Read sources by ID number from USNO UCAC 1 Catalog */
+	char *refcatname, /* Name of catalog (UCAC1 or UCAC2) */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ucacbin(	/* Bin sources from USNO UCAC 1 Catalog */
+	char *refcatname, /* Name of catalog (UCAC1 or UCAC2) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+    int ujcread(	/* Read sources by sky region from USNO J Catalog */
+	char *refcatname, /* Name of catalog (UJC, xxxxx.usno) */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *unum,	/* Array of catalog numbers (returned) */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *uplate,	/* Array of plate numbers (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ujcrnum(	/* Read sources by ID number from USNO J Catalog */
+	char *refcatname, /* Name of catalog (UJC, xxxxx.usno) */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *unum,	/* Array of source numbers to look for */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *uplate,	/* Array of plate numbers (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ujcbin(		/* Bin sources from USNO J Catalog */
+	char *refcatname, /* Name of catalog (UJC, xxxxx.usno) */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+/* Subroutines to read a local copy of the Tycho-2 catalog */
+    int ty2read(	/* Read sources by sky region from Tycho 2 Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TYCHO2 or TYCHO2E */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ty2rnum(	/* Read sources by ID number from Tycho 2 Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TYCHO2 or TYCHO2E */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int ty2bin(		/* Bin sources from Tycho 2 Catalog */
+	int refcat,	/* Catalog code from wcscat.h (TYCHO2 or TYCHO2E */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+/* Subroutines to read a local copy of the ACT catalog */
+    int actread(	/* Read sources by sky region from USNO ACT Catalog */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int distsort,	/* 1 to sort stars by distance from center */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *gnum,	/* Array of ID numbers (returned) */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int actrnum(	/* Read sources by ID number from USNO ACT Catalog */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *gnum,	/* Array of source numbers to look for */
+	double *gra,	/* Array of right ascensions (returned) */
+	double *gdec,	/* Array of declinations (returned) */
+	double *gpra,	/* Array of right ascension proper motions (returned) */
+	double *gpdec,	/* Array of declination proper motions (returned) */
+	double **gmag,	/* 2-D array of magnitudes (returned) */
+	int *gtype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int actbin(		/* Bin sources from USNO ACT Catalog */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+/* Subroutines to read SAO-TDC binary format catalogs */
+    int binread(	/* Read from sky region from SAO TDC binary format catalog */
+	char *bincat,	/* Name of reference star catalog file */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	struct StarCat **starcat, /* Star catalog data structure */
+	double *tnum,	/* Array of ID numbers (returned) */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D array of magnitudes (returned) */
+	int *tpeak,	/* Array of encoded spectral types (returned) */
+	char **tobj,	/* Array of object names (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int binrnum(	/* Read sources by ID number from SAO TDC binary format catalog */
+	char *bincat,	/* Name of reference star catalog file */
+	int nstars,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	int match,	/* If 1, match number exactly, else number is sequence*/
+	double *tnum,	/* Array of source numbers to look for */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D array of magnitudes (returned) */
+	int *tpeak,	/* Array of encoded spectral types (returned) */
+	char **tobj,	/* Array of object names (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int binbin(		/* Bin sources from SAO TDC binary format catalog */
+	char *bincat,	/* Name of reference star catalog file */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude to use (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+
+    int binstar(	/* Read one star entry from binary catalog, 0 if OK */
+	struct StarCat *sc, /* Star catalog descriptor */
+	struct Star *st, /* Current star entry (returned) */
+	int istar);	/* Star sequence number in binary catalog */
+    struct StarCat *binopen( /* Open binary catalog, returning number of entries */
+	char *bincat);	/* Name of reference star catalog file */
+    void binclose(	/* Close binary catalog */
+	struct StarCat *sc); /* Star catalog descriptor */
+    int isbin(		/* Return 1 if TDC binary catalog file, else 0 */
+	char *filename); /* Name of file to check */
+
+/* Subroutines for extracting tab table information (in tabread.c) */
+    int tabread(	/* Read sources from tab table catalog */
+	char *tabcatname, /* Name of reference star catalog file */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude by which to sort (1 to nmag) */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	struct StarCat **starcat, /* Star catalog data structure */
+	double *tnum,	/* Array of source numbers (returned) */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D Array of magnitudes (returned) */
+	int *tpeak,	/* Array of peak counts (returned) */
+	char **tkey,	/* Array of values of additional keyword */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tabrnum(	/* Read sources from tab table catalog */
+	char *tabcatname, /* Name of reference star catalog file */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	struct StarCat **starcat, /* Star catalog data structure */
+	int match,	/* 1 to match star number exactly, else sequence num */
+	double *tnum,	/* Array of source numbers to look for */
+	double *tra,	/* Array of right ascensions (returned) */
+	double *tdec,	/* Array of declinations (returned) */
+	double *tpra,	/* Array of right ascension proper motions (returned) */
+	double *tpdec,	/* Array of declination proper motions (returned) */
+	double **tmag,	/* 2-D Array of magnitudes (returned) */
+	int *tpeak,	/* Array of peak counts (returned) */
+	char **tkey,	/* Array of values of additional keyword */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tabbin(		/* Read sources from tab table catalog */
+	char *tabcatname, /* Name of reference star catalog file */
+	struct WorldCoor *wcs, /* World coordinate system for image */
+	char *header,	/* FITS header for output image */
+	char *image,	/* Output FITS image */
+	double mag1,	/* Minimum (brightest) magnitude (no limits if equal) */
+	double mag2,	/* Maximum (faintest) magnitude (no limits if equal) */
+	int sortmag,	/* Magnitude by which to sort (1 to nmag) */
+	double magscale, /* Scaling factor for magnitude to pixel flux
+			 * (image of number of catalog objects per bin if 0) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tabxyread(	/* Read x, y, and magnitude from tab table star list */
+	char *tabcatname, /* Name of reference star catalog file */
+	double **xa,	/* Array of x coordinates (returned) */
+	double **ya,	/* Array of y coordinates (returned) */
+	double **ba,	/* Array of magnitudes (returned) */
+	int **pa,	/* Array of fluxes (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int tabrkey(		/* Keyword values from tab table catalogs */
+	char *tabcatname, /* Name of reference star catalog file */
+	struct StarCat **starcat, /* Star catalog data structure */
+	int nnum,	/* Number of stars to look for */
+	double *tnum,	/* Array of source numbers to look for */
+	char *keyword,	/* Keyword for which to return values */
+	char **tval);	/* Returned values for specified keyword */
+    struct StarCat *tabcatopen(	/* Open tab table catalog */
+	char *tabpath,	/* Tab table catalog file pathname */
+	struct TabTable *tabtable, /* Tab table data structure */
+	int nbbuff);	/* Number of bytes in buffer; 0=read whole file */
+    void tabcatclose(	/* Close tab table catalog */
+	struct StarCat *sc);	/* Source catalog data structure */
+    int tabstar(	/* Read one star entry from tab table catalog, 0 if OK */
+	int istar,		/* Source sequence number in tab table catalog */
+	struct StarCat *sc,	/* Source catalog data structure */
+	struct Star *st,	/* Star data structure, updated on return */
+	int verbose);		/* 1 to print error messages */
+    struct TabTable *tabopen(	/* Open tab table file */
+	char *tabfile,	/* Tab table catalog file name */
+	int nbbuff);	/* Number of bytes in buffer; 0=read whole file */
+    void tabclose(	/* Free all arrays left open by tab table structure */
+	struct TabTable *tabtable); /* Tab table data structure */
+    char *gettabline(	/* Find a specified line in a tab table */
+	struct TabTable *tabtable, /* Tab table data structure */
+	int iline);	/* Line sequence number in tab table */
+    double tabgetra(	/* Return right ascension in degrees from tab table*/
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry);	/* sequence of entry on line */
+    double tabgetdec(	/* Return declination in degrees from tab table*/
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry);	/* sequence of entry on line */
+    double tabgetr8(	/* Return double number from tab table line */
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry);	/* sequence of entry on line */
+    int tabgeti4(	/* Return 4-byte integer from tab table line */
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry);	/* sequence of entry on line */
+    void settabkey(	/* Set tab table keyword to read for object */
+	char *keyword);	/* column header of desired value */
+    int tabgetk(	/* Get tab table entries for named column */
+	struct TabTable *tabtable, /* Tab table data structure */
+	struct Tokens *tabtok,  /* Line token structure */
+	char *keyword,	/* column header of desired value */
+	char *string,	/* character string (returned) */
+	int maxchar);	/* Maximum number of characters in returned string */
+    int tabgetc(	/* Get tab table entry for named column */
+	struct Tokens *tabtok,  /* Line token structure */
+	int ientry,	/* sequence of entry on line */
+	char *string,	/* character string (returned) */
+	int maxchar);	/* Maximum number of characters in returned string */
+    int tabparse(		/* Aeturn column names and positions in tabtable */
+	struct TabTable *tabtable); /* Tab table data structure */
+    int tabcol(		/* Find column for name */
+	struct TabTable *tabtable, /* Tab table data structure */
+	char *keyword);	/* column header of desired value */
+    int istab(		/* Return 1 if tab table file, else 0 */
+	char *filename); /* Name of file to check */
+    char *gettaberr();	/* Return most recent tab table error message */
+    int gettabndec();	/* Return number of decimal places in tab catalog ids */
+
+/* Subroutines to read catalogs over the web, from SCAT, HST, ESO, or SDSS servers */
+    int webread(	/* Read sources by sky region from WWW catalog */
+	char *caturl,	/* URL of search engine */
+	char *refcatname, /* Name of catalog */
+	int distsort,	/* 1 to sort stars by distance from center */
+	double cra,	/* Search center J2000 right ascension in degrees */
+	double cdec,	/* Search center J2000 declination in degrees */
+	double dra,	/* Search half width in right ascension in degrees */
+	double ddec,	/* Search half-width in declination in degrees */
+	double drad,	/* Limiting separation in degrees (ignore if 0) */
+	double dradi,	/* Inner edge of annulus in degrees (ignore if 0) */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double mag1,	/* Limiting magnitudes (none if equal) */
+	double mag2,	/* Limiting magnitudes (none if equal) */
+	int sortmag,	/* Number of magnitude by which to limit and sort */
+	int nstarmax,	/* Maximum number of stars to be returned */
+	double *unum,	/* Array of ID numbers (returned) */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double *upra,	/* Array of right ascension proper motions (returned) */
+	double *updec,	/* Array of declination proper motions (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *utype,	/* Array of integer catalog values (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    int webrnum(	/* Read sources by ID number from WWW catalog */
+	char *caturl,	/* URL of search engine */
+	char *refcatname, /* Name of catalog */
+	int nnum,	/* Number of stars to look for */
+	int sysout,	/* Search coordinate system */
+	double eqout,	/* Search coordinate equinox */
+	double epout,	/* Proper motion epoch (0.0 for no proper motion) */
+	double *unum,	/* Array of source numbers to look for */
+	double *ura,	/* Array of right ascensions (returned) */
+	double *udec,	/* Array of declinations (returned) */
+	double *upra,	/* Array of right ascension proper motions (returned) */
+	double *updec,	/* Array of declination proper motions (returned) */
+	double **umag,	/* 2-D array of magnitudes (returned) */
+	int *utype,	/* Array of object types (returned) */
+	int nlog);	/* Verbose mode if > 1, number of sources per log line */
+    char *webbuff(	/* Read URL into buffer across the web */
+	char *url,	/* URL to read */
+	int diag,	/* 1 to print diagnostic messages */
+	int *lbuff);	/* Length of buffer (returned) */
+    struct TabTable *webopen(	/* Open tab table across the web */
+	char *caturl,	/* URL of search engine */
+	char *srchpar,	/* Search engine parameters to append */
+	int nlog);	/* 1 to print diagnostic messages */
+
+/* Subroutines to read DAOPHOT-style catalogs of sources found in an image */
+    int daoread(	/* Read image source positions from x y mag file */
+	char *daocat,	/* Name of DAOFIND catalog file */
+	double **xa,	/* X and Y coordinates of stars, array returned */
+	double **ya,	/* X and Y coordinates of stars, array returned */
+	double **ba,	/* Instrumental magnitudes of stars, array returned */
+	int **pa,	/* Peak counts of stars in counts, array returned */
+	int nlog);	/* 1 to print each star's position */
+    int daoopen(	/* Open image source position x y mag file */
+	char *daofile);	/* DAOFIND catalog file name */
+    char *daoline(	/* Read line from image source position x y mag file */
+	int iline,	/* Star sequence number in DAOFIND catalog */
+	char *line);	/* Pointer to iline'th entry (returned updated) */
+
+/* Subroutines for sorting tables of star positions and magnitudes from sortstar.c */
+    void FluxSortStars(	/* Sort image stars by decreasing flux */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double *sb,	/* Brighness in counts */
+	int *sc,	/* Other 4-byte information */
+	int ns);	/* Number of stars to sort */
+    void MagSortStars(	/* Sort image stars by increasing magnitude */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm,		/* Number of magnitudes per star */
+	int ms);	/* Magnitude by which to sort (1 to nmag) */
+    void IDSortStars(	/* Sort image stars by increasing ID Number value */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    void RASortStars(	/* Sort image stars by increasing right ascension */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    void DecSortStars(	/* Sort image stars by increasing declination */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    void XSortStars(	/* Sort image stars by increasing image X value */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    void YSortStars(	/* Sort image stars by increasing image Y value */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm);	/* Number of magnitudes per star */
+    int MergeStars(	/* Merge multiple entries within given radius
+			 * return mean ra, dec, proper motion, and magnitude(s) */
+	double *sn,	/* Identifying number */
+	double *sra,	/* Right Ascension */
+	double *sdec,	/* Declination */
+	double *spra,	/* Right Ascension proper motion */
+	double *spdec,	/* Declination proper motion */
+	double *sx,	/* Image X coordinate */
+	double *sy,	/* Image Y coordinate */
+	double **sm,	/* Magnitudes */
+	int *sc,	/* Other 4-byte information */
+	char **sobj,	/* Object name */
+	int ns,		/* Number of stars to sort */
+	int nm,		/* Number of magnitudes per star */
+	double rad,	/* Maximum separation in arcseconds to merge */
+	int log);	/* If >0, log progress every time mod number written */
+
+/* Catalog utility subroutines from catutil.c */
+
+/* Subroutines for dealing with catalogs */
+    int RefCat(		/* Return catalog type code, title, coord. system */
+	char *refcatname, /* Name of reference catalog */
+	char *title,	/* Description of catalog (returned) */
+	int *syscat,	/* Catalog coordinate system (returned) */
+	double *eqcat,	/* Equinox of catalog (returned) */
+	double *epcat,	/* Epoch of catalog (returned) */
+	int *catprop,	/* 1 if proper motion in catalog (returned) */
+	int *nmag);	/* Number of magnitudes in catalog (returned) */
+    int CatCode(	/* Return catalog type code */
+	char *refcatname); /* Name of reference catalog */
+    char *CatName(	/* Return catalog name given catalog type code */
+	int refcat,	/* Catalog code */
+	char *refcatname); /* Name of reference catalog */
+    char *CatSource(	/* Return catalog source description given catalog type code */
+	int refcat,	/* Catalog code */
+	char *refcatname); /* Name of reference catalog */
+    void CatID(		/* Return catalog ID keyword given catalog type code */
+	char *catid,	/* Catalog ID (returned) */
+	int refcat);	/* Catalog code */
+    double CatRad(	/* Return default search radius for given catalog */
+	int refcat);	/* Catalog code */
+    char *ProgName(	/* Return program name given program path used */
+	char *progpath0); /* Pathname by which program is invoked */
+    char *ProgCat(	/* Return catalog name given program name used */
+	char *progname); /* Program name which might contain catalog code */
+    void CatNum(	/* Return formatted source number */
+	int refcat,	/* Catalog code */
+	int nnfld,	/* Number of characters in number (from CatNumLen)
+			 * Print leading zeroes if negative */
+	int nndec,	/* Number of decimal places ( >= 0)
+			 * Omit leading spaces if negative */
+	double dnum,	/* Catalog number of source */
+	char *numstr);	/* Formatted number (returned) */
+    int CatNumLen(	/* Return length of source numbers */
+	int refcat,	/* Catalog code */
+	double maxnum,	/* Maximum ID number
+			 * (Ignored for standard catalogs) */
+	int nndec);	/* Number of decimal places ( >= 0) */
+    int CatNdec(	/* Return number of decimal places in source numbers */
+	int refcat);	/* Catalog code */
+    void CatMagName(	/* Return name of specified magnitude */
+	int imag,	/* Sequence number of magnitude */
+	int refcat,	/* Catalog code */
+	char *magname); /* Name of magnitude, returned */
+    int CatMagNum(	/* Returns number of magnitude specified by letter as int */
+	int imag,	/* int of magnitude letter */
+	int refcat);	/* Catalog code */
+    void CatTabHead (	/* Print heading for catalog search result table */
+	int refcat,	/* Catalog being searched */
+	int sysout,	/* Output coordinate system */
+	int nnfld,	/* Number of characters in ID column */
+	int mprop,	/* 1 if proper motion in catalog */
+	int nmag,	/* Number of magnitudes */
+	char *ranges,	/* Catalog numbers to print */
+	char *keyword,	/* Column to add to tab table output */
+	int gcset,	/* 1 if there are any values in gc[] */
+	int tabout,	/* 1 if output is tab-delimited */
+	int classd,	/* GSC object class to accept (-1=all) */
+	int printxy,	/* 1 if X and Y included in output */
+	char **gobj1,	/* Pointer to array of object names; NULL if none */
+	FILE *fd);	/* Output file descriptor; none if NULL */
+    int StrNdec(	/* Return number of decimal places in numeric string */
+	char *string);	/* Numeric string */
+    int NumNdec(	/* Return number of decimal places in a number */
+	double number); /* Floating point number */
+    void setdateform (	/* Set date format code */
+	int dateform0);	/* Date format code */
+    char *DateString(	/* Return string with epoch of position in desired format */
+	double epoch,	/* Date as fraction of a year */
+	int tabout);	/* 1 for tab-preceded output string, else space-preceded */
+    void setlimdeg(	/* Limit output in degrees (1) or hh:mm:ss dd:mm:ss (0) */
+	int degout);	/* 1 for fractional degrees, else sexagesimal hours, degrees */
+
+    void SearchLim(	/* Compute limiting RA and Dec */
+	double cra,	/* Longitude/Right Ascension of Center of search area in degrees */
+	double cdec,	/* Latitude/Declination of search area in degrees */
+	double dra,	/* Horizontal half-width in degrees */
+	double ddec,	/* Vertical half-width in degrees */
+	int syscoor,	/* Coordinate system */
+	double *ra1,	/* Lower right ascension limit in degrees (returned) */
+	double *ra2,	/* Upper right ascension limit in degrees (returned) */
+	double *dec1,	/* Lower declination limit in degrees (returned) */
+	double *dec2,	/* Upper declination limit in degrees (returned) */
+	int verbose);	/* 1 to print limits, else 0 */
+    void RefLim(	/* Compute limiting RA and Dec in new system */
+	double cra,	/* Longitude/Right Ascension of Center of search area in degrees */
+	double cdec,	/* Latitude/Declination of search area in degrees */
+	double dra,	/* Horizontal half-width in degrees */
+	double ddec,	/* Vertical half-width in degrees */
+	int sysc,	/* System of search coordinates */
+	int sysr,	/* System of reference catalog coordinates */
+	double eqc,	/* Equinox of search coordinates in years */
+	double epr,	/* Epoch of reference catalog coordinates in years */
+	double secmarg,	/* Margin in arcsec/century to catch moving stars */
+	double *ramin,	/* Lower right ascension limit in degrees (returned) */
+	double *ramax,	/* Upper right ascension limit in degrees (returned) */
+	double *decmin,	/* Lower declination limit in degrees (returned) */
+	double *decmax,	/* Upper declination limit in degrees (returned) */
+	int verbose);	/* 1 to print limits, else 0 */
+
+/* Subroutines for dealing with ranges */
+    struct Range *RangeInit(	/* Initialize range structure from string */
+	char *string,	/* String containing numbers separated by , and - */
+	int ndef);	/* Maximum allowable range value */
+    int isrange(	/* Return 1 if string is a range of numbers, else 0 */
+	char *string);	/* String which might be a range of numbers */
+    void rstart(	/* Restart range */
+	struct Range *range); /* Range structure */
+    int rgetn(		/* Return number of values in all ranges */
+	struct Range *range); /* Range structure */
+    int rgeti4(		/* Return next number in range as integer */
+	struct Range *range); /* Range structure */
+    double rgetr8(	/* Return next number in range as double */
+	struct Range *range); /* Range structure */
+
+/* Subroutines for access to tokens within a string */
+    int setoken(	/* Tokenize a string for easy decoding */
+	struct Tokens *tokens, /* Token structure returned */
+	char    *string, /* character string to tokenize */
+	char *cwhite);	/* additional whitespace characters
+			 * if = tab, disallow spaces and commas */
+    int nextoken(	/* Get next token from tokenized string */
+	struct Tokens *tokens, /* Token structure returned */
+	char *token,	/* token (returned) */
+	int maxchars);	/* Maximum length of token */
+    int getoken(	/* Get specified token from tokenized string */
+	struct Tokens *tokens, /* Token structure returned */
+	int itok,	/* token sequence number of token
+			 * if <0, get whole string after token -itok
+			 * if =0, get whole string */
+	char *token,	/* token (returned) */
+	int maxchars);	/* Maximum length of token */
+
+    int ageti4(		/* Extract int value from keyword= value in string */
+	char *string,	/* character string containing <keyword>= <value> */
+	char *keyword,	/* character string containing the name of the keyword
+			 * the value of which is returned.  hget searches for a
+			 * line beginning with this string.  if "[n]" or ",n" is
+			 * present, the n'th token in the value is returned. */
+	int *ival);	/* Integer value, returned */
+    int agetr8(		/* Extract double value from keyword= value in string */
+	char *string,	/* character string containing <keyword>= <value> */
+	char *keyword,	/* character string containing the name of the keyword */
+	double *dval);	/* Double value, returned */
+    int agets(		/* Extract value from keyword= value in string */
+	char *string,	/* character string containing <keyword>= <value> */
+	char *keyword,	/* character string containing the name of the keyword */
+	int lval,	/* Size of value in characters
+			 * If negative, value ends at end of line */
+	char *value);	/* String (returned) */
+
+    int tmcid(		/* Return 1 if string is 2MASS ID, else 0 */
+	char *string,	/* Character string to check */
+	double *ra,	/* Right ascension (returned) */
+	double *dec);	/* Declination (returned) */
+
+/* Subroutines for VOTable output */
+    int vothead(	/* Print heading for VOTable SCAT output */
+	int refcat,	/* Catalog code */
+	char *refcatname, /* Name of catalog */
+	int mprop,	/* Proper motion flag */
+	int typecol,	/* Flag for spectral type */
+	int ns,		/* Number of sources found in catalog */
+	double cra,	/* Search center right ascension */
+	double cdec,	/* Search center declination */
+	double drad);	/* Radius to search in degrees */
+    void vottail();	/* Terminate VOTable SCAT output */
+
+/* Subroutines for version/date string */
+    void setrevmsg(	/* Set version/date string */
+    char *getrevmsg(	/* Return version/date string */
+
+/* Subroutines for fitting and evaluating polynomials */
+    void polfit(	/* Fit polynomial coefficients */
+    double polcomp(	/* Evaluate polynomial from polfit coefficients */
+
+#else /* K&R prototypes */
+
+/* Subroutines for reading TDC ASCII catalogs (ctgread.c) */
+int ctgread();		/* Read sources by sky region from SAO TDC ASCII format catalog */
+int ctgrnum();		/* Read sources by number from SAO TDC ASCII format catalog */
+int ctgrdate();		/* Read sources by date range from SAO TDC ASCII format catalog */
+int ctgbin();		/* Bin sources from SAO TDC ASCII format catalog */
+int ctgstar();		/* Read one star entry from ASCII catalog, 0 if OK */
+int isacat();		/* Return 1 if string is name of ASCII catalog file */
+struct StarCat *ctgopen();
+void ctgclose();
+
+/* Subroutines for extracting sources from HST Guide Star Catalog */
+int gscread();		/* Read sources by sky region from HST Guide Star Catalog */
+int gscrnum();		/* Read sources by ID number from HST Guide Star Catalog */
+int gscbin();		/* Bin sources from HST Guide Star Catalog */
+void setgsclass();	/* Set GSC object class */
+
+/* Subroutine to read GSC II catalog over the web (gsc2read.c) */
+int gsc2read();		/* Read sources by sky region from GSC II Catalog */
+
+/* Subroutine to read SDSS catalog over the web (sdssread.c) */
+int sdssread();		/* Read sources by sky region from SDSS Catalog */
+char *sdssc2t();	/* Convert SDSS buffer from comma- to tab-separated */
+
+/* Subroutines to read local copy of 2MASS Point Source Catalog (tmcread.c) */
+int tmcread();		/* Read sources by sky region from 2MASS Point Source Catalog */
+int tmcrnum();		/* Read sources by ID number from 2MASS Point Source Catalog */
+int tmcbin();		/* Bin sources from 2MASS Point Source Catalog */
+
+/* Subroutines to read local copies of USNO A and SA catalogs (uacread.c) */
+int uacread();		/* Read sources by sky region from USNO A or SA Catalog */
+int uacrnum();		/* Read sources by ID number from USNO A or SA Catalog */
+int uacbin();		/* Bin sources from USNO A or SA Catalog */
+void setuplate();	/* Set USNO catalog plate number to search */
+int getuplate();	/* Get USNO catalog plate number to search */
+
+/* Subroutines to read local copies of USNO B catalogs (ubcread.c) */
+int ubcread();		/* Read sources by sky region from USNO B Catalog */
+int ubcrnum();		/* Read sources by ID number from USNO B Catalog */
+int ubcbin();		/* Bin sources from USNO B Catalog */
+
+/* Subroutines to read local copies of USNO UCAC catalogs (ucacread.c) */
+int ucacread();		/* Read sources by sky region from USNO UCAC 1 Catalog */
+int ucacrnum();		/* Read sources by ID number from USNO UCAC 1 Catalog */
+int ucacbin();		/* Bin sources from USNO UCAC 1 Catalog */
+
+/* Subroutines to read local copies of USNO UJ catalog (ucacread.c) */
+int ujcread();		/* Read sources by sky region from USNO J Catalog */
+int ujcrnum();		/* Read sources by ID number from USNO J Catalog */
+int ujcbin();		/* Bin sources from USNO J Catalog */
+
+/* Subroutines to read a local copy of the Tycho-2 catalog (ty2read.c) */
+int ty2read();		/* Read sources by sky region from Tycho 2 Catalog */
+int ty2rnum();		/* Read sources by ID number from Tycho 2 Catalog */
+int ty2bin();		/* Bin sources from Tycho 2 Catalog */
+
+/* Subroutines to read a local copy of the ACT catalog (actread.c) */
+int actread();		/* Read sources by sky region from USNO ACT Catalog */
+int actrnum();		/* Read sources by ID number from USNO ACT Catalog */
+int actbin();		/* Bin sources from USNO ACT Catalog */
+
+/* Subroutines to read SAO-TDC binary format catalogs (binread.c) */
+int binread();		/* Read sources by sky region from SAO TDC binary format catalog */
+int binrnum();		/* Read sources by ID number from SAO TDC binary format catalog */
+int binbin();		/* Bin sources from SAO TDC binary format catalog */
+int binstar();		/* Read one star entry from binary catalog, 0 if OK */
+int isbin();
+struct StarCat *binopen();
+void binclose();
+
+/* Subroutines for extracting tab table information (tabread.c) */
+int tabread();		/* Read sources from tab table catalog */
+int tabrnum();		/* Read sources from tab table catalog */
+int tabbin();		/* Read sources from tab table catalog */
+struct TabTable *tabopen();	/* Open tab table file */
+struct StarCat *tabcatopen();	/* Open tab table catalog */
+void tabcatclose();	/* Close tab table catalog */
+int tabxyread();	/* Read x, y, and magnitude from tab table star list */
+void settabkey();	/* Set tab table keyword to read for object */
+char *gettabline();	/* Find a specified line in a tab table */
+int tabrkey();		/* Keyword values from tab table catalogs */
+int tabcol();		/* Find column for name */
+int tabgetk();		/* Get tab table entries for named column */
+int tabgetc();		/* Get tab table entry for named column */
+int tabgeti4();		/* Return 4-byte integer from tab table line */
+int tabparse();		/* Aeturn column names and positions in tabtable */
+double tabgetra();	/* Return right ascension in degrees from tab table*/
+double tabgetdec();	/* Return declination in degrees from tab table*/
+double tabgetr8();	/* Return double number from tab table line */
+void tabclose();	/* Free all arrays left open by tab table structure */
+char *gettaberr();	/* Return most recent tab table error message */
+int istab();		/* Return 1 if tab table file, else 0 */
+int gettabndec();	/* Return number of decimal places in tab catalog ids */
+
+/* Subroutines to read catalogs over the web, from SCAT, HST, ESO, or SDSS servers */
+int webread();		/* Read sources by sky region from catalog on the World Wide Web */
+int webrnum();		/* Read sources by ID number from catalog on the World Wide Web */
+char *webbuff();	/* Read URL into buffer across the web */
+struct TabTable *webopen();	/* Open tab table across the web */
+
+/* Subroutines to read DAOPHOT-style catalogs of sources found in an image */
+int daoread();		/* Read image source positions from x y mag file */
+int daoopen();		/* Open image source position x y mag file */
+char *daoline();	/* Read line from image source position x y mag file */
+
+/* Subroutines for sorting tables of star positions and magnitudes from sortstar.c */
+void FluxSortStars();	/* Sort image stars by decreasing flux */
+void MagSortStars();	/* Sort image stars by increasing magnitude */
+void IDSortStars();	/* Sort image stars by increasing ID Number value */
+void RASortStars();	/* Sort image stars by increasing right ascension */
+void DecSortStars();	/* Sort image stars by increasing declination */
+void XSortStars();	/* Sort image stars by increasing image X value */
+void YSortStars();	/* Sort image stars by increasing image Y value */
+int MergeStars();	/* Merge multiple entries within given radius */
+
+/* Catalog utility subroutines from catutil.c */
+
+/* Subroutines for dealing with catalogs */
+int CatCode();		/* Return catalog type code */
+int RefCat();		/* Return catalog type code, title, coord. system */
+char *CatName();	/* Return catalog name given catalog type code */
+char *CatSource();	/* Return catalog source description given catalog type code */
+char *ProgCat();	/* Return catalog name given program name used */
+char *ProgName();	/* Return program name given program path used */
+char *CatName();	/* Return catalog name given catalog type code */
+void CatID();		/* Return catalog ID keyword given catalog type code */
+void CatNum();		/* Return formatted source number */
+int CatNumLen();	/* Return length of source numbers */
+int CatNdec();		/* Return number of decimal places in source numbers */
+void CatMagName();	/* Return name of specified magnitude */
+int CatMagNum();	/* Returns number of magnitude specified by letter as int */
+double CatRad();	/* Return default search radius for given catalog */
+int tmcid();		/* Return 1 if string is 2MASS ID, else 0 */
+
+int NumNdec();		/* Return number of decimal places in a number */
+int StrNdec();		/* Return number of decimal places in numeric string */
+void setdateform();	/* Set date format code */
+void setlimdeg();	/* Limit output in degrees (1) or hh:mm:ss dd:mm:ss (0) */
+char *DateString();		/* Convert epoch to output format */
+void SearchLim();	/* Compute limiting RA and Dec */
+void RefLim();		/* Compute limiting RA and Dec in new system */
+int ageti4();		/* Extract int value from keyword= value in string */
+int agetr8();		/* Extract double value from keyword= value in string */
+int agets();		/* Extract value from keyword= value in string */
+void bv2sp();		/* Approximate main sequence spectral type from B - V */
+
+/* Subroutines for dealing with ranges */
+struct Range *RangeInit();	/* Initialize range structure from string */
+int isrange();		/* Return 1 if string is a range of numbers, else 0 */
+int rgetn();		/* Return number of values in all ranges */
+int rgeti4();		/* Return next number in range as integer */
+double rgetr8();	/* Return next number in range as double */
+void rstart();		/* Restart range */
+
+/* Subroutines for access to tokens within a string */
+int setoken();		/* Tokenize a string for easy decoding */
+int nextoken();		/* Get next token from tokenized string */
+int getoken();		/* Get specified token from tokenized string */
+
+/* Subroutines for VOTable output */
+int vothead();		/* Print heading for VOTable SCAT output */
+void vottail();		/* Terminate VOTable SCAT output */
+
+/* Subroutines for version/date string */
+void setrevmsg();	/* Set version/date string */
+char *getrevmsg();	/* Return version/date string */
+
+/* Subroutines for fitting and evaluating polynomials */
+void polfit();		/* Fit polynomial coefficients */
+double polcomp();	/* Evaluate polynomial from polfit coefficients */
+
+#endif  /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif __cplusplus
+
+#endif  /* _wcscat_h_ */
+
+/* Sep 22 1998  New header file (star.h)
+ * Oct 16 1998  Add more options for ASCII catalogs
+ * Oct 20 1998  Add object name to binary files
+ * Oct 21 1998	New file (wcscat.h)
+ * Oct 26 1998	Combined wcscat.h and star.h
+ * Oct 27 1998	Add SAOimage region shapes
+ * Nov  9 1998	Add rasorted flag to catalog structure
+ * Nov 20 1998	Add support for USNO A-2.0 and SA-2.0 catalogs
+ * Dec  8 1998	Add support for the Hipparcos and ACT catalogs
+ *
+ * Jan 25 1999	Add declarations for tab table access
+ * Jan 25 1999	Add declarations for dealing with ranges of numbers
+ * Feb  2 1999	Add number of decimal places in star number to StarCat
+ * Feb 11 1999	Add coordinate system info to star structure
+ * Feb 11 1999	Change starcat.insys to starcat.coorsys for consistency
+ * May 14 1999	Update Star and StarCat structure to cover tab tables
+ * May 19 1999	Update StarCat structure to include epoch from catalog
+ * June 4 1999	Add CatNumLen()
+ * Jun 14 1999	Add SearchLim()
+ * Jun 30 1999	Add isrange()
+ * Jul  1 1999	Add declarations for date/time conversions in dateutil.c
+ * Jul  2 1999	Add rstart()
+ * Jul 26 1999	Add Yale Bright Star Catalog
+ * Aug 16 1999	Add RefLim() to get converted search coordinates right
+ * Aug 25 1999	Add ACT catalog
+ * Sep 10 1999	Move special case setting from argument list to subroutines
+ * Sep 13 1999	Add subroutines to access data structure for single stars
+ * Oct  1 1999	Add structure and subroutines for tokenized strings
+ * Oct 22 1999	Change cat*() to ctg*() to avoid system conflict
+ * Oct 29 1999	Add tabget() subroutines
+ * Nov  1 1999	Increase maximum number of tokens on a line from 20 to 100
+ * Nov  2 1999	Move date utilities to fitsfile.h
+ *
+ * Jan 10 2000	Add column names to catalog data structure
+ * Jan 11 2000	Add gettabndec()
+ * Feb  9 2000	Add proper motion entry information to star data structure
+ * Feb 16 2000	Add gettaberr() to return tab table error message
+ * Mar  1 2000	Add isfile() and agets() to help with ASCII files
+ * Mar  8 2000	Add ProgCat() to return catalog name from program name used
+ * Mar  8 2000	Add ProgName() to extract program name from path used
+ * Mar 10 2000	Add PropCat() to tell whether a catalog has proper motions
+ * Mar 27 2000	Add tabxyread()
+ * Apr  3 2000	Add option in catalog structure to ignore extra info
+ * May 22 2000	Add Tycho 2 support, bv2sp()
+ * May 26 2000	Add separate pointer to header in tab table structure
+ * May 26 2000	Add separate pointer to table name in tab table structure
+ * Jul 12 2000	Add catalog type code to ctalog data structure
+ * Sep 20 2000	Add isacat() to detect ASCII catalog files
+ * Sep 25 2000	Add starcat.sptype to flag spectral type in catalog
+ * Oct 23 2000	Add USNO plate catalog to catalog type table
+ * Oct 26 2000	Add proper motion flags for seconds and arcseconds per century
+ * Oct 31 2000	Add proper motion flags for milliseconds per year
+ * Nov  2 2000	Add parallax and radial velocity to star structure
+ * Nov 21 2000	Add WEBCAT catalog type for tab ctalogs returned from the Web
+ * Nov 22 2000	Add webread() and webrnum()
+ * Nov 28 2000	Add tabparse()
+ * Nov 30 2000	Add spectral type to catalog header; make star->isp 4 char.
+ * Dec 13 2000	Add StrNdec() to get number of decimal places in number strings
+ * Dec 15 2000	Add CatNdec() to get number of decimal places in source numbers
+ * Dec 18 2000	Drop PropCat(), a cludgy proper motion flag
+ *
+ * Mar 22 2001	Add web search flag in catalog data structure
+ * Mar 27 2001	Add shapes in pixels to SAOimage region options
+ * May 14 2001	Add 2MASS Point Source Catalog flags
+ * May 22 2001	Add declination sorting
+ * May 24 2001	Add 2MASS Point Source Catalog subroutines
+ * May 29 2001	Add length of star number to catalog structure
+ * May 30 2001	Add third magnitude for tab tables to catalog structure
+ * Jun 15 2001	Add CatName() and CatID()
+ * Jun 19 2001	Add parallax error to catalog and star structures
+ * Jun 20 2001	Add webopen(), GSC2, fourth magnitude to star and starcat
+ * Jul 12 2001	Add separate web access subroutine, webbuff()
+ * Jul 23 2001	Add ageti4() and agetr8()
+ * Jul 24 2001	Add polfit() and polcomp()
+ * Aug  8 2001	Add keyrv and option to set mprop to 2 to include rv/cz
+ * Sep 10 2001	Add entry line and distance from search center to Star
+ * Sep 13 2001	Add YSortStars() and SORT_Y
+ * Sep 14 2001	Add lbuff to TabTable structure
+ * Sep 20 2001	Add CatMagName()
+ * Sep 25 2001	Move isfile() to fitsfile.h
+ * Oct 16 2001	Add tabdash pointer to tabtable data structure
+ *
+ * Apr  9 2002	Fix typo in gettaberr() declaration
+ * Apr 10 2002	Add CatMagNum()
+ * May  6 2002	Increase object name length from 31 to 79 characters
+ * May 13 2002	Add NumNdec(), gsc2read(), and gsc2rnum()
+ * Aug  6 2002	Make magnitude entries and positions vectors of 10
+ * Oct 30 2002	Add epoch keyword and FITS date to StarCat data structure
+ *
+ * Jan 16 2003	Add USNO-B1.0 catalog
+ * Mar 24 2003	Add CatCde() to get only catalog code
+ * Apr  3 2003	Add ubcread(), ubcrnum(), and FluxSortStars()
+ * Apr  3 2003	Drop gsc2rnum()
+ * Apr 14 2003	Add setrevmsg() and getrevmsg()
+ * Apr 24 2003	Add UCAC1 and UCAC2, ucacread() and ucacrnum()
+ * May 20 2003	Add TMIDR2 for 2MASS PSC Interim Data Release 2
+ * Sep 16 2003	Add SORT_MERGE for scat
+ * Sep 25 2003	Add *bin() subroutines for catalog binning
+ * Dec  3 2003	Add USNO YB6 catalog
+ *
+ * Jan  5 2004	Add SDSS catalog
+ * Jan 12 2004	Add 2MASS Extended Source catalog and size to star structure
+ * Jan 14 2004	Add CatSource() subroutine to simplify help message creation
+ * Jan 22 2004	Add setlimdeg() to print limit coordinates in degrees
+ * Mar 16 2004	Add MergeStars()
+ * Apr 23 2004	Add ctgrdate()
+ * Aug 31 2004	Increase MAXTOKENS from 100 to 200
+ * Sep  2 2004	Increase MAXTOKENS from 200 to 1000
+ *
+ * Jul 27 2005	Add date format codes and DateString()
+ * Aug  5 2005	Add Tycho-2 and 2MASS PSC with magnitude errors
+ *
+ * Jan  6 2006	Add CatRad() subroutine
+ * Mar 17 2006	Make vothead() int as it now returns the number of fields
+ * Apr  3 2006	Add tmcid() to check for 2MASS identifiers
+ * Apr  3 2006	Add setdateform() to set output date format
+ * Apr 12 2006	Add SORT_ID for scat to sort catalog entries by ID number
+ * Jun 20 2006	Add IDSortStars()
+ *
+ * Jan 10 2006	Add ANSI C function prototypes
+ */
diff --git a/Code/src/libwcs/wcscon.c b/Code/src/libwcs/wcscon.c
new file mode 100644
index 0000000000000000000000000000000000000000..b454e76cd40bd91641fe81eb9d61d9879c3f8d37
--- /dev/null
+++ b/Code/src/libwcs/wcscon.c
@@ -0,0 +1,2328 @@
+/*** File wcscon.c
+ *** November 8, 2007
+ *** Doug Mink, Harvard-Smithsonian Center for Astrophysics
+ *** Some subroutines are based on Starlink subroutines by Patrick Wallace
+ *** Copyright (C) 1995-2007
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	wcscon.c (World Coordinate System conversion)
+ * Purpose:	Convert between various sky coordinate systems
+ * Subroutine:	wcscon (sys1,sys2,eq1,eq2,theta,phi,epoch)
+ *		convert between coordinate systems
+ * Subroutine:  wcsconp (sys1,sys2,eq1,eq2,ep1,ep2,dtheta,dphi,ptheta,pphi)
+ *              convert coordinates and proper motion between coordinate systems
+ * Subroutine:  wcsconv (sys1,sys2,eq1,eq2,ep1,ep2,dtheta,dphi,ptheta,pphi,px,rv)
+ *              convert coordinates and proper motion between coordinate systems
+ * Subroutine:	wcscsys (cstring) returns code for coordinate system in string
+ * Subroutine:	wcsceq (wcstring) returns equinox in years from system string
+ * Subroutine:	wcscstr (sys,equinox,epoch) returns system string from equinox
+ * Subroutine:	fk524 (ra,dec) Convert J2000(FK5) to B1950(FK4) coordinates
+ * Subroutine:	fk524e (ra, dec, epoch) (more accurate for known position epoch)
+ * Subroutine:	fk524m (ra,dec,rapm,decpm) exact
+ * Subroutine:	fk524pv (ra,dec,rapm,decpm,parallax,rv) more exact
+ * Subroutine:	fk425 (ra,dec) Convert B1950(FK4) to J2000(FK5) coordinates
+ * Subroutine:	fk425e (ra, dec, epoch) (more accurate for known position epoch)
+ * Subroutine:	fk425m (ra, dec, rapm, decpm) exact
+ * Subroutine:	fk425pv (ra,dec,rapm,decpm,parallax,rv) more exact
+ * Subroutine:	fk42gal (dtheta,dphi) Convert B1950(FK4) to galactic coordinates
+ * Subroutine:	fk52gal (dtheta,dphi) Convert J2000(FK5) to galactic coordinates
+ * Subroutine:	gal2fk4 (dtheta,dphi) Convert galactic coordinates to B1950(FK4)
+ * Subroutine:	gal2fk5 (dtheta,dphi) Convert galactic coordinates to J2000<FK5)
+ * Subroutine:	fk42ecl (dtheta,dphi,epoch) Convert B1950(FK4) to ecliptic coordinates
+ * Subroutine:	fk52ecl (dtheta,dphi,epoch) Convert J2000(FK5) to ecliptic coordinates
+ * Subroutine:	ecl2fk4 (dtheta,dphi,epoch) Convert ecliptic coordinates to B1950(FK4)
+ * Subroutine:	ecl2fk5 (dtheta,dphi,epoch) Convert ecliptic coordinates to J2000<FK5)
+ * Subroutine:  fk5prec (ep0, ep1, ra, dec) Precession ep0 to ep1, FK5 system
+ * Subroutine:  fk4prec (ep0, ep1, ra, dec) Precession ep0 to ep1, FK4 system
+ * Subroutine:  d2v3 (rra, rdec, r, pos) RA and Dec in degrees, Distance to Cartesian
+ * Subroutine:  v2d3 (pos, rra, rdec, r) Cartesian to RA and Dec in degrees, Distance
+ * Subroutine:  s2v3 (rra, rdec, r, pos) RA, Dec, Distance to Cartesian
+ * Subroutine:  v2s3 (pos, rra, rdec, r) Cartesian to RA, Dec, Distance
+ * Subroutine:  rotmat (axes, rot1, rot2, rot3, matrix) Rotation angles to matrix
+ *
+ * Note: Proper motions are always in RA/Dec degrees/year; no cos(Dec) correction
+ */
+
+#include <math.h>
+#ifndef VMS
+#include <stdlib.h>
+#endif
+#include <stdio.h>	/* for fprintf() and sprintf() */
+#include <ctype.h>
+#include <string.h>
+#include "wcs.h"
+
+void fk524(), fk524e(), fk524m(), fk524pv();
+void fk425(), fk425e(), fk425m(), fk425pv();
+void fk42gal(), fk52gal(), gal2fk4(), gal2fk5();
+void fk42ecl(), fk52ecl(), ecl2fk4(), ecl2fk5();
+
+/* Convert from coordinate system sys1 to coordinate system sys2, converting
+   proper motions, too, and adding them if an epoch is specified */
+
+void
+wcsconp (sys1, sys2, eq1, eq2, ep1, ep2, dtheta, dphi, ptheta, pphi)
+
+int	sys1;	/* Input coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+int	sys2;	/* Output coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+double	eq1;	/* Input equinox (default of sys1 if 0.0) */
+double	eq2;	/* Output equinox (default of sys2 if 0.0) */
+double	ep1;	/* Input Besselian epoch in years (for proper motion) */
+double	ep2;	/* Output Besselian epoch in years (for proper motion) */
+double	*dtheta; /* Longitude or right ascension in degrees
+		   Input in sys1, returned in sys2 */
+double	*dphi;	/* Latitude or declination in degrees
+		   Input in sys1, returned in sys2 */
+double	*ptheta; /* Longitude or right ascension proper motion in RA degrees/year
+		   Input in sys1, returned in sys2 */
+double	*pphi;	/* Latitude or declination proper motion in Dec degrees/year
+		   Input in sys1, returned in sys2 */
+
+{
+    void fk5prec(), fk4prec();
+
+    /* Set equinoxes if 0.0 */
+    if (eq1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    eq1 = 1950.0;
+	else
+	    eq1 = 2000.0;
+	}
+    if (eq2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    eq2 = 1950.0;
+	else
+	    eq2 = 2000.0;
+	}
+
+    /* Set epochs if 0.0 */
+    if (ep1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    ep1 = 1950.0;
+	else
+	    ep1 = 2000.0;
+	}
+    if (ep2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    ep2 = 1950.0;
+	else
+	    ep2 = 2000.0;
+	}
+
+    if (sys1 == WCS_ICRS && sys2 == WCS_ICRS)
+	eq2 = eq1;
+
+    if (sys1 == WCS_J2000 && sys2 == WCS_ICRS && eq1 == 2000.0) {
+	eq2 = eq1;
+	sys1 = sys2;
+	}
+
+    /* Set systems and equinoxes so that ICRS coordinates are not precessed */
+    if (sys1 == WCS_ICRS && sys2 == WCS_J2000 && eq2 == 2000.0) {
+	eq1 = eq2;
+	sys1 = sys2;
+	}
+
+    /* If systems and equinoxes are the same, add proper motion and return */
+    if (sys2 == sys1 && eq1 == eq2) {
+	if (ep1 != ep2) {
+	    if (sys1 == WCS_J2000) {
+		*dtheta = *dtheta + ((ep2 - ep1) * *ptheta);
+		*dphi = *dphi + ((ep2 - ep1) * *pphi);
+		}
+	    else if (sys1 == WCS_B1950) {
+		*dtheta = *dtheta + ((ep2 - ep1) * *ptheta);
+		*dphi = *dphi + ((ep2 - ep1) * *pphi);
+		}
+	    }
+	if (eq1 != eq2) {
+	    if (sys1 == WCS_B1950)
+		fk4prec (eq1, eq2, dtheta, dphi);
+	    if (sys1 == WCS_J2000)
+		fk5prec (eq1, 2000.0, dtheta, dphi);
+	    }
+	return;
+	}
+
+    /* Precess from input equinox to input system equinox, if necessary */
+    if (sys1 == WCS_B1950 && eq1 != 1950.0)
+	fk4prec (eq1, 1950.0, dtheta, dphi);
+    if (sys1 == WCS_J2000 && eq1 != 2000.0)
+	fk5prec (eq1, 2000.0, dtheta, dphi);
+
+    /* Convert to B1950 FK4 */
+    if (sys2 == WCS_B1950) {
+	if (sys1 == WCS_J2000) {
+	    if (*ptheta != 0.0 || *pphi != 0.0) {
+		fk524m (dtheta, dphi, ptheta, pphi);
+		if (ep1 == 2000.0)
+		    ep1 = 1950.0;
+		if (ep2 != 1950.0) {
+		    *dtheta = *dtheta + ((ep2 - 1950.0) * *ptheta);
+		    *dphi = *dphi + ((ep2 - 1950.0) * *pphi);
+		    }
+		}
+	    else if (ep2 != 1950.0)
+		fk524e (dtheta, dphi, ep2);
+	    else
+		fk524 (dtheta, dphi);
+	    }
+	else if (sys1 == WCS_GALACTIC) 
+	    gal2fk4 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC)
+	    ecl2fk4 (dtheta, dphi, ep2);
+	}
+
+    else if (sys2 == WCS_J2000) {
+        if (sys1 == WCS_B1950) {
+	    if (*ptheta != 0.0 || *pphi != 0.0) {
+		fk425m (dtheta, dphi, ptheta, pphi);
+		if (ep2 != 2000.0) {
+		    *dtheta = *dtheta + ((ep2 - 2000.0) * *ptheta);
+		    *dphi = *dphi + ((ep2 - 2000.0) * *pphi);
+		    }
+		}
+            else if (ep2 > 0.0)
+                fk425e (dtheta, dphi, ep2);
+            else
+                fk425 (dtheta, dphi);
+            }
+        else if (sys1 == WCS_GALACTIC)
+            gal2fk5 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC)
+	    ecl2fk5 (dtheta, dphi, ep2);
+	}
+
+    else if (sys2 == WCS_GALACTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk42gal (dtheta, dphi);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk52gal (dtheta, dphi);
+	    }
+        else if (sys1 == WCS_ECLIPTIC) {
+	    ecl2fk5 (dtheta, dphi, ep2);
+	    fk52gal (dtheta, dphi);
+	    }
+	}
+
+    else if (sys2 == WCS_ECLIPTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    if (ep2 > 0.0)
+		fk42ecl (dtheta, dphi, ep2);
+	    else
+		fk42ecl (dtheta, dphi, 1950.0);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk52ecl (dtheta, dphi, ep2);
+	    }
+        else if (sys1 == WCS_GALACTIC) {
+	    gal2fk5 (dtheta, dphi);
+	    fk52ecl (dtheta, dphi, ep2);
+	    }
+	}
+
+    /* Precess to desired equinox, if necessary */
+    if (sys2 == WCS_B1950 && eq2 != 1950.0)
+	fk4prec (1950.0, eq2, dtheta, dphi);
+    if (sys2 == WCS_J2000 && eq2 != 2000.0)
+	fk5prec (2000.0, eq2, dtheta, dphi);
+
+    /* Keep latitude/declination between +90 and -90 degrees */
+    if (*dphi > 90.0) {
+	*dphi = 180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+    else if (*dphi < -90.0) {
+	*dphi = -180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+
+    /* Keep longitude/right ascension between 0 and 360 degrees */
+    if (*dtheta > 360.0)
+	*dtheta = *dtheta - 360.0;
+    else if (*dtheta < 0.0)
+	*dtheta = *dtheta + 360.0;
+    return;
+}
+
+
+/* Convert from coordinate system sys1 to coordinate system sys2, converting
+   proper motions, too, and adding them if an epoch is specified */
+
+void
+wcsconv (sys1, sys2, eq1, eq2, ep1, ep2, dtheta, dphi, ptheta, pphi, px, rv)
+
+int	sys1;	/* Input coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+int	sys2;	/* Output coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+double	eq1;	/* Input equinox (default of sys1 if 0.0) */
+double	eq2;	/* Output equinox (default of sys2 if 0.0) */
+double	ep1;	/* Input Besselian epoch in years (for proper motion) */
+double	ep2;	/* Output Besselian epoch in years (for proper motion) */
+double	*dtheta; /* Longitude or right ascension in degrees
+		   Input in sys1, returned in sys2 */
+double	*dphi;	/* Latitude or declination in degrees
+		   Input in sys1, returned in sys2 */
+double	*ptheta; /* Longitude or right ascension proper motion in degrees/year
+		   Input in sys1, returned in sys2 */
+double	*pphi;	/* Latitude or declination proper motion in degrees/year
+		   Input in sys1, returned in sys2 */
+double	*px;	/* Parallax in arcseconds */
+double	*rv;	/* Radial velocity in km/sec */
+
+{
+    void fk5prec(), fk4prec();
+
+    /* Set equinoxes if 0.0 */
+    if (eq1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    eq1 = 1950.0;
+	else
+	    eq1 = 2000.0;
+	}
+    if (eq2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    eq2 = 1950.0;
+	else
+	    eq2 = 2000.0;
+	}
+
+    /* Set epochs if 0.0 */
+    if (ep1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    ep1 = 1950.0;
+	else
+	    ep1 = 2000.0;
+	}
+    if (ep2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    ep2 = 1950.0;
+	else
+	    ep2 = 2000.0;
+	}
+
+    /* Set systems and equinoxes so that ICRS coordinates are not precessed */
+    if (sys1 == WCS_ICRS && sys2 == WCS_ICRS)
+	eq2 = eq1;
+
+    if (sys1 == WCS_J2000 && sys2 == WCS_ICRS && eq1 == 2000.0) {
+	eq2 = eq1;
+	sys1 = sys2;
+	}
+
+    if (sys1 == WCS_ICRS && sys2 == WCS_J2000 && eq2 == 2000.0) {
+	eq1 = eq2;
+	sys1 = sys2;
+	}
+
+    /* If systems and equinoxes are the same, add proper motion and return */
+    if (sys2 == sys1 && eq1 == eq2) {
+	if (ep1 != ep2) {
+	    if (sys1 == WCS_J2000) {
+		*dtheta = *dtheta + ((ep2 - ep1) * *ptheta);
+		*dphi = *dphi + ((ep2 - ep1) * *pphi);
+		}
+	    else if (sys1 == WCS_B1950) {
+		*dtheta = *dtheta + ((ep2 - ep1) * *ptheta);
+		*dphi = *dphi + ((ep2 - ep1) * *pphi);
+		}
+	    }
+	return;
+	}
+
+    /* Precess from input equinox to input system equinox, if necessary */
+    if (eq1 != eq2) {
+	if (sys1 == WCS_B1950 && eq1 != 1950.0)
+	   fk4prec (eq1, 1950.0, dtheta, dphi);
+	if (sys1 == WCS_J2000 && eq1 != 2000.0)
+	   fk5prec (eq1, 2000.0, dtheta, dphi);
+	}
+
+    /* Convert to B1950 FK4 */
+    if (sys2 == WCS_B1950) {
+	if (sys1 == WCS_J2000) {
+	    if (*ptheta != 0.0 || *pphi != 0.0) {
+		if (*px != 0.0 || *rv != 0.0)
+		    fk524pv (dtheta, dphi, ptheta, pphi, px, rv);
+		else
+		    fk524m (dtheta, dphi, ptheta, pphi);
+		if (ep1 == 2000.0)
+		    ep1 = 1950.0;
+		if (ep2 != 1950.0) {
+		    *dtheta = *dtheta + ((ep2 - 1950.0) * *ptheta);
+		    *dphi = *dphi + ((ep2 - 1950.0) * *pphi);
+		    }
+		}
+	    else if (ep2 != 1950.0)
+		fk524e (dtheta, dphi, ep2);
+	    else
+		fk524 (dtheta, dphi);
+	    }
+	else if (sys1 == WCS_GALACTIC) 
+	    gal2fk4 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC)
+	    ecl2fk4 (dtheta, dphi, ep2);
+	}
+
+    else if (sys2 == WCS_J2000) {
+        if (sys1 == WCS_B1950) {
+	    if (*ptheta != 0.0 || *pphi != 0.0) {
+		if (*px != 0.0 || *rv != 0.0)
+		    fk425pv (dtheta, dphi, ptheta, pphi, px, rv);
+		else
+		    fk425m (dtheta, dphi, ptheta, pphi);
+		if (ep2 != 2000.0) {
+		    *dtheta = *dtheta + ((ep2 - 2000.0) * *ptheta);
+		    *dphi = *dphi + ((ep2 - 2000.0) * *pphi);
+		    }
+		}
+            else if (ep2 > 0.0)
+                fk425e (dtheta, dphi, ep2);
+            else
+                fk425 (dtheta, dphi);
+            }
+        else if (sys1 == WCS_GALACTIC)
+            gal2fk5 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC)
+	    ecl2fk5 (dtheta, dphi, ep2);
+	}
+
+    else if (sys2 == WCS_GALACTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk42gal (dtheta, dphi);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk52gal (dtheta, dphi);
+	    }
+        else if (sys1 == WCS_ECLIPTIC) {
+	    ecl2fk5 (dtheta, dphi, ep2);
+	    fk52gal (dtheta, dphi);
+	    }
+	}
+
+    else if (sys2 == WCS_ECLIPTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    if (ep2 > 0.0)
+		fk42ecl (dtheta, dphi, ep2);
+	    else
+		fk42ecl (dtheta, dphi, 1950.0);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk52ecl (dtheta, dphi, ep2);
+	    }
+        else if (sys1 == WCS_GALACTIC) {
+	    gal2fk5 (dtheta, dphi);
+	    fk52ecl (dtheta, dphi, ep2);
+	    }
+	}
+
+    /* Precess to desired equinox, if necessary */
+    if (eq1 != eq2) {
+	if (sys2 == WCS_B1950 && eq2 != 1950.0)
+	    fk4prec (1950.0, eq2, dtheta, dphi);
+	if (sys2 == WCS_J2000 && eq2 != 2000.0)
+	    fk5prec (2000.0, eq2, dtheta, dphi);
+	}
+
+    /* Keep latitude/declination between +90 and -90 degrees */
+    if (*dphi > 90.0) {
+	*dphi = 180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+    else if (*dphi < -90.0) {
+	*dphi = -180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+
+    /* Keep longitude/right ascension between 0 and 360 degrees */
+    if (*dtheta > 360.0)
+	*dtheta = *dtheta - 360.0;
+    else if (*dtheta < 0.0)
+	*dtheta = *dtheta + 360.0;
+    return;
+}
+
+
+/* Convert from coordinate system sys1 to coordinate system sys2 */
+
+void
+wcscon (sys1, sys2, eq1, eq2, dtheta, dphi, epoch)
+
+int	sys1;	/* Input coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+int	sys2;	/* Output coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+double	eq1;	/* Input equinox (default of sys1 if 0.0) */
+double	eq2;	/* Output equinox (default of sys2 if 0.0) */
+double	*dtheta; /* Longitude or right ascension in degrees
+		   Input in sys1, returned in sys2 */
+double	*dphi;	/* Latitude or declination in degrees
+		   Input in sys1, returned in sys2 */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    void fk5prec(), fk4prec();
+
+    /* Set equinoxes if 0.0 */
+    if (eq1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    eq1 = 1950.0;
+	else
+	    eq1 = 2000.0;
+	}
+    if (eq2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    eq2 = 1950.0;
+	else
+	    eq2 = 2000.0;
+	}
+
+    /* Set systems and equinoxes so that ICRS coordinates are not precessed */
+    if (sys1 == WCS_ICRS && sys2 == WCS_ICRS)
+	eq2 = eq1;
+
+    if (sys1 == WCS_J2000 && sys2 == WCS_ICRS && eq1 == 2000.0) {
+	eq2 = eq1;
+	sys1 = sys2;
+	}
+
+    if (sys1 == WCS_ICRS && sys2 == WCS_J2000 && eq2 == 2000.0) {
+	eq1 = eq2;
+	sys1 = sys2;
+	}
+
+    /* If systems and equinoxes are the same, return */
+    if (sys2 == sys1 && eq1 == eq2)
+	return;
+
+    /* Precess from input equinox, if necessary */
+    if (eq1 != eq2) {
+	if (sys1 == WCS_B1950 && eq1 != 1950.0)
+	   fk4prec (eq1, 1950.0, dtheta, dphi);
+	if (sys1 == WCS_J2000 && eq1 != 2000.0)
+	   fk5prec (eq1, 2000.0, dtheta, dphi);
+	}
+
+    /* Convert to B1950 FK4 */
+    if (sys2 == WCS_B1950) {
+	if (sys1 == WCS_J2000) {
+	    if (epoch > 0)
+		fk524e (dtheta, dphi, epoch);
+	    else
+		fk524 (dtheta, dphi);
+	    }
+	else if (sys1 == WCS_GALACTIC) 
+	    gal2fk4 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC) {
+	    if (epoch > 0)
+		ecl2fk4 (dtheta, dphi, epoch);
+	    else
+		ecl2fk4 (dtheta, dphi, 1950.0);
+	    }
+	}
+
+    else if (sys2 == WCS_J2000) {
+        if (sys1 == WCS_B1950) {
+            if (epoch > 0)
+                fk425e (dtheta, dphi, epoch);
+            else
+                fk425 (dtheta, dphi);
+            }
+        else if (sys1 == WCS_GALACTIC)
+            gal2fk5 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC) {
+	    if (epoch > 0)
+		ecl2fk5 (dtheta, dphi, epoch);
+	    else
+		ecl2fk5 (dtheta, dphi, 2000.0);
+	    }
+	}
+
+    else if (sys2 == WCS_GALACTIC) {
+        if (sys1 == WCS_B1950)
+	    fk42gal (dtheta, dphi);
+        else if (sys1 == WCS_J2000)
+	    fk52gal (dtheta, dphi);
+        else if (sys1 == WCS_ECLIPTIC) {
+	    if (epoch > 0)
+		ecl2fk5 (dtheta, dphi, epoch);
+	    else
+		ecl2fk5 (dtheta, dphi, 2000.0);
+	    fk52gal (dtheta, dphi);
+	    }
+	}
+
+    else if (sys2 == WCS_ECLIPTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (epoch > 0)
+		fk42ecl (dtheta, dphi, epoch);
+	    else
+		fk42ecl (dtheta, dphi, 1950.0);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (epoch > 0)
+		fk52ecl (dtheta, dphi, epoch);
+	    else
+		fk52ecl (dtheta, dphi, 2000.0);
+	    }
+        else if (sys1 == WCS_GALACTIC) {
+	    gal2fk5 (dtheta, dphi);
+	    if (epoch > 0)
+		fk52ecl (dtheta, dphi, epoch);
+	    else
+		fk52ecl (dtheta, dphi, 2000.0);
+	    }
+	}
+
+    /* Precess to desired equinox, if necessary */
+    if (eq1 != eq2) {
+	if (sys2 == WCS_B1950 && eq2 != 1950.0)
+	    fk4prec (1950.0, eq2, dtheta, dphi);
+	if (sys2 == WCS_J2000 && eq2 != 2000.0)
+	    fk5prec (2000.0, eq2, dtheta, dphi);
+	}
+
+    /* Keep latitude/declination between +90 and -90 degrees */
+    if (*dphi > 90.0) {
+	*dphi = 180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+    else if (*dphi < -90.0) {
+	*dphi = -180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+
+    /* Keep longitude/right ascension between 0 and 360 degrees */
+    if (*dtheta > 360.0)
+	*dtheta = *dtheta - 360.0;
+    else if (*dtheta < 0.0)
+	*dtheta = *dtheta + 360.0;
+
+    return;
+}
+
+
+/* Set coordinate system from string */
+int
+wcscsys (wcstring)
+
+char *wcstring;		/* Name of coordinate system */
+{
+    double equinox;
+
+    if (wcstring[0] == 'J' || wcstring[0] == 'j' ||
+	!strcmp (wcstring,"2000") || !strcmp (wcstring, "2000.0") ||
+	!strcmp (wcstring,"ICRS") || !strcmp (wcstring, "icrs") ||
+	!strncmp (wcstring,"FK5",3) || !strncmp (wcstring, "fk5",3))
+	return WCS_J2000;
+
+    if (wcstring[0] == 'B' || wcstring[0] == 'b' ||
+	!strcmp (wcstring,"1950") || !strcmp (wcstring, "1950.0") ||
+	!strncmp (wcstring,"FK4",3) || !strncmp (wcstring, "fk4",3))
+	return WCS_B1950;
+
+    else if (wcstring[0] == 'I' || wcstring[0] == 'i' )
+	return WCS_ICRS;
+
+    else if (wcstring[0] == 'G' || wcstring[0] == 'g' )
+	return WCS_GALACTIC;
+
+    else if (wcstring[0] == 'E' || wcstring[0] == 'e' )
+	return WCS_ECLIPTIC;
+
+    else if (wcstring[0] == 'A' || wcstring[0] == 'a' )
+	return WCS_ALTAZ;
+
+    else if (wcstring[0] == 'N' || wcstring[0] == 'n' )
+	return WCS_NPOLE;
+
+    else if (wcstring[0] == 'L' || wcstring[0] == 'l' )
+	return WCS_LINEAR;
+
+    else if (!strncasecmp (wcstring, "pixel", 5))
+	return WCS_XY;
+
+    else if (wcstring[0] == 'P' || wcstring[0] == 'p' )
+	return WCS_PLANET;
+
+    else if (isnum (wcstring)) {
+	equinox = atof (wcstring);
+	if (equinox > 1980.0)
+	    return WCS_J2000;
+	else if (equinox > 1900.0)
+	    return WCS_B1950;
+	else
+	    return -1;
+	}
+    else
+	return -1;
+}
+
+
+/* Set equinox from string (return 0.0 if not obvious) */
+
+double
+wcsceq (wcstring)
+
+char *wcstring;		/* Name of coordinate system */
+{
+    if (wcstring[0] == 'J' || wcstring[0] == 'j' ||
+	wcstring[0] == 'B' || wcstring[0] == 'b')
+	return (atof (wcstring+1));
+    else if (!strncmp (wcstring, "FK4",3) ||
+	     !strncmp (wcstring, "fk4",3))
+	return (1950.0);
+    else if (!strncmp (wcstring, "FK5",3) ||
+	     !strncmp (wcstring, "fk5",3))
+	return (2000.0);
+    else if (!strncmp (wcstring, "ICRS",4) ||
+	     !strncmp (wcstring, "icrs",4))
+	return (2000.0);
+    else if (wcstring[0] == '1' || wcstring[0] == '2')
+	return (atof (wcstring));
+    else
+	return (0.0);
+}
+
+
+/* Set coordinate system type string from system and equinox */
+
+void
+wcscstr (cstr, syswcs, equinox, epoch)
+
+char	*cstr;		/* Coordinate system string (returned) */
+int	syswcs;		/* Coordinate system code */
+double	equinox;	/* Equinox of coordinate system */
+double	epoch;		/* Epoch of coordinate system */
+{
+
+    char *estr;
+
+    if (syswcs == WCS_XY) {
+	strcpy (cstr, "XY");
+	return;
+	}
+
+    /* Try to figure out coordinate system if it is not set */
+    if (epoch == 0.0)
+	epoch = equinox;
+    if (syswcs < 0) {
+	if (equinox > 0.0) {
+	    if (equinox == 2000.0)
+		syswcs = WCS_J2000;
+	    else if (equinox == 1950.0)
+		syswcs = WCS_B1950;
+	    }
+	else if (epoch > 0.0) {
+	    if (epoch > 1980.0) {
+		syswcs = WCS_J2000;
+		equinox = 2000.0;
+		}
+	    else {
+		syswcs = WCS_B1950;
+		equinox = 1950.0;
+		}
+	    }
+	else
+	    syswcs = WCS_J2000;
+	}
+
+    /* Set coordinate system string from system flag and epoch */
+    if (syswcs == WCS_B1950) {
+	if (epoch == 1950.0 || epoch == 0.0)
+	    strcpy (cstr, "B1950");
+	else
+	    sprintf (cstr, "B%7.2f", equinox);
+	if ((estr = strsrch (cstr,".00")) != NULL) {
+	    estr[0] = (char) 0;
+	    estr[1] = (char) 0;
+	    estr[2] = (char) 0;
+	    }
+	}
+    else if (syswcs == WCS_GALACTIC)
+	strcpy (cstr, "galactic");
+    else if (syswcs == WCS_ECLIPTIC)
+	strcpy (cstr, "ecliptic");
+    else if (syswcs == WCS_J2000) {
+	if (epoch == 2000.0 || epoch == 0.0)
+	    strcpy (cstr, "J2000");
+	else
+	    sprintf (cstr, "J%7.2f", equinox);
+	if ((estr = strsrch (cstr,".00")) != NULL) {
+	    estr[0] = (char) 0;
+	    estr[1] = (char) 0;
+	    estr[2] = (char) 0;
+	    }
+	}
+    else if (syswcs == WCS_ICRS) {
+	strcpy (cstr, "ICRS");
+	}
+    else if (syswcs == WCS_PLANET) {
+	strcpy (cstr, "PLANET");
+	}
+    else if (syswcs == WCS_LINEAR || syswcs == WCS_XY) {
+	strcpy (cstr, "LINEAR");
+	}
+    return;
+}
+
+
+/*  Constant vector and matrix (by columns)
+    These values were obtained by inverting C.Hohenkerk's forward matrix
+    (private communication), which agrees with the one given in reference
+    2 but which has one additional decimal place.  */
+
+static double a[3] = {-1.62557e-6, -0.31919e-6, -0.13843e-6};
+static double ad[3] = {1.245e-3,  -1.580e-3,  -0.659e-3};
+static double d2pi = 6.283185307179586476925287;	/* two PI */
+static double tiny = 1.e-30; /* small number to avoid arithmetic problems */
+
+/* FK524  convert J2000 FK5 star data to B1950 FK4
+   based on Starlink sla_fk524 by P.T.Wallace 27 October 1987 */
+
+static double emi[6][6] = {
+    {	 0.9999256795,		/* emi[0][0] */
+	 0.0111814828,		/* emi[0][1] */
+	 0.0048590039,		/* emi[0][2] */
+	-0.00000242389840,	/* emi[0][3] */
+	-0.00000002710544,	/* emi[0][4] */
+	-0.00000001177742 },	/* emi[0][5] */
+ 
+    {	-0.0111814828,		/* emi[1][0] */
+	 0.9999374849,		/* emi[1][1] */
+	-0.0000271771,		/* emi[1][2] */
+	 0.00000002710544,	/* emi[1][3] */
+	-0.00000242392702,	/* emi[1][4] */
+	 0.00000000006585 },	/* emi[1][5] */
+ 
+    {	-0.0048590040,		/* emi[2][0] */
+	-0.0000271557,		/* emi[2][1] */
+	 0.9999881946,		/* emi[2][2] */
+	 0.00000001177742,	/* emi[2][3] */
+	 0.00000000006585,	/* emi[2][4] */
+	-0.00000242404995 },	/* emi[2][5] */
+ 
+    {	-0.000551,		/* emi[3][0] */
+	 0.238509,		/* emi[3][1] */
+	-0.435614,		/* emi[3][2] */
+	 0.99990432,		/* emi[3][3] */
+	 0.01118145,		/* emi[3][4] */
+	 0.00485852 },		/* emi[3][5] */
+ 
+    {	-0.238560,		/* emi[4][0] */
+	-0.002667,		/* emi[4][1] */
+	 0.012254,		/* emi[4][2] */
+	-0.01118145,		/* emi[4][3] */
+	 0.99991613,		/* emi[4][4] */
+	-0.00002717 },		/* emi[4][5] */
+ 
+    {	 0.435730,		/* emi[5][0] */
+	-0.008541,		/* emi[5][1] */
+	 0.002117,		/* emi[5][2] */
+	-0.00485852,		/* emi[5][3] */
+	-0.00002716,		/* emi[5][4] */
+	 0.99996684 }		/* emi[5][5] */
+    };
+
+void
+fk524 (ra,dec)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+
+{
+    double	rapm;	/* Proper motion in right ascension */
+    double	decpm;	/* Proper motion in declination  */
+			/* In:  deg/jul.yr.  Out: deg/trop.yr.  */
+
+    rapm = (double) 0.0;
+    decpm = (double) 0.0;
+    fk524m (ra, dec, &rapm, &decpm);
+    return;
+}
+
+void
+fk524e (ra, dec, epoch)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+double	epoch;		/* Besselian epoch in years */
+
+{
+    double	rapm;	/* Proper motion in right ascension */
+    double	decpm;	/* Proper motion in declination  */
+			/* In:  deg/jul.yr.  Out: deg/trop.yr.  */
+
+    rapm = (double) 0.0;
+    decpm = (double) 0.0;
+    fk524m (ra, dec, &rapm, &decpm);
+    *ra = *ra + (rapm * (epoch - 1950.0));
+    *dec = *dec + (decpm * (epoch - 1950.0));
+    return;
+}
+
+void
+fk524m (ra,dec,rapm,decpm)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+double	*rapm;		/* Proper motion in right ascension */
+double	*decpm;		/* Proper motion in declination */
+			/* In:  ra/dec deg/jul.yr.  Out: ra/dec deg/trop.yr.  */
+
+{
+    double parallax = 0.0;
+    double rv = 0.0;
+
+    fk524pv (ra, dec, rapm, decpm, &parallax, &rv);
+    return;
+}
+
+
+void
+fk524pv (ra,dec,rapm,decpm, parallax, rv)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+double	*rapm;		/* Proper motion in right ascension */
+double	*decpm;		/* Proper motion in declination
+			 * In:  ra/dec degrees/Julian year (not ra*cos(dec))
+			 * Out: ra/dec degrees/tropical year */
+double *parallax;	/* Parallax (arcsec) */
+double *rv;		/* Rradial velocity (km/s, +ve = moving away) */
+
+/*  This routine converts stars from the IAU 1976 FK5 Fricke
+    system, to the old Bessel-Newcomb FK4 system, using Yallop's
+    implementation (see ref 2) of a matrix method due to Standish
+    (see ref 3).  The numerical values of ref 2 are used canonically.
+
+ *  Conversion from other than Julian epoch 2000.0 to other than Besselian
+    epoch 1950.0 will require use of the appropriate precession, proper
+    motion, and e-terms routines before and/or after fk524 is called.
+ 
+ *  In the FK4 catalogue the proper motions of stars within 10 degrees
+    of the poles do not embody the differential e-term effect and should,
+    strictly speaking, be handled in a different manner from stars outside
+    these regions.  however, given the general lack of homogeneity of the
+    star data available for routine astrometry, the difficulties of handling
+    positions that may have been determined from astrometric fields spanning
+    the polar and non-polar regions, the likelihood that the differential
+    e-terms effect was not taken into account when allowing for proper motion
+    in past astrometry, and the undesirability of a discontinuity in the
+    algorithm, the decision has been made in this routine to include the
+    effect of differential e-terms on the proper motions for all stars,
+    whether polar or not, at epoch 2000, and measuring on the sky rather
+    than in terms of dra, the errors resulting from this simplification are
+    less than 1 milliarcsecond in position and 1 milliarcsecond per century
+    in proper motion.
+
+    References:
+
+      1  "Mean and apparent place computations in the new IAU System.
+          I. The transformation of astrometric catalog systems to the
+ 	  equinox J2000.0." Smith, C.A.; Kaplan, G.H.; Hughes, J.A.;
+	  Seidelmann, P.K.; Yallop, B.D.; Hohenkerk, C.Y.
+ 	  Astronomical Journal vol. 97, Jan. 1989, p. 265-273.
+
+      2  "Mean and apparent place computations in the new IAU System.
+	  II. Transformation of mean star places from FK4 B1950.0 to
+ 	  FK5 J2000.0 using matrices in 6-space."  Yallop, B.D.;
+	  Hohenkerk, C.Y.; Smith, C.A.; Kaplan, G.H.; Hughes, J.A.;
+	  Seidelmann, P.K.; Astronomical Journal vol. 97, Jan. 1989,
+	  p. 274-279.
+ 
+      3  Seidelmann, P.K. (ed), 1992.  "Explanatory Supplement to
+         the Astronomical Almanac", ISBN 0-935702-68-7.
+
+      4  "Conversion of positions and proper motions from B1950.0 to the
+	  IAU system at J2000.0", Standish, E.M.  Astronomy and
+	  Astrophysics, vol. 115, no. 1, Nov. 1982, p. 20-22.
+
+   P.T.Wallace   Starlink   19 December 1993
+   Doug Mink     Smithsonian Astrophysical Observatory 1 November 2000 */
+
+{
+    double r2000,d2000;		/* J2000.0 ra,dec (radians) */
+    double r1950,d1950;		/* B1950.0 ra,dec (rad) */
+
+    /* Miscellaneous */
+    double ur,ud;
+    double sr, cr, sd, cd, x, y, z, w, wd;
+    double v1[6],v2[6];
+    double xd,yd,zd;
+    double rxyz, rxysq, rxy;
+    double dra,ddec;
+    int	i,j;
+    int	diag = 0;
+
+    /* Constants */
+    double zero = (double) 0.0;
+    double vf = 21.095;	/* Km per sec to AU per tropical century */
+			/* = 86400 * 36524.2198782 / 149597870 */
+
+    /* Convert J2000 RA and Dec from degrees to radians */
+    r2000 = degrad (*ra);
+    d2000 = degrad (*dec);
+
+    /* Convert J2000 RA and Dec proper motion from degrees/year to arcsec/tc */
+    ur = *rapm  * 360000.0;
+    ud = *decpm * 360000.0;
+
+    /* Spherical to Cartesian */
+    sr = sin (r2000);
+    cr = cos (r2000);
+    sd = sin (d2000);
+    cd = cos (d2000);
+
+    x = cr * cd;
+    y = sr * cd;
+    z = sd;
+
+    v1[0] = x;
+    v1[1] = y;
+    v1[2] = z;
+ 
+    if (ur != zero || ud != zero) {
+	v1[3] = -(ur*y) - (cr*sd*ud);
+	v1[4] =  (ur*x) - (sr*sd*ud);
+	v1[5] =          (cd*ud);
+	}
+    else {
+	v1[3] = zero;
+	v1[4] = zero;
+	v1[5] = zero;
+	}
+ 
+    /* Convert position + velocity vector to bn system */
+    for (i = 0; i < 6; i++) {
+	w = zero;
+	for (j = 0; j < 6; j++) {
+	    w = w + emi[i][j] * v1[j];
+	    }
+	v2[i] = w;
+	}
+ 
+    /* Vector components */
+    x = v2[0];
+    y = v2[1];
+    z = v2[2];
+    rxyz = sqrt (x*x + y*y + z*z);
+
+    /* Magnitude of position vector */
+    rxyz = sqrt (x*x + y*y + z*z);
+ 
+    /* Apply e-terms to position */
+    w = (x * a[0]) + (y * a[1]) + (z * a[2]);
+    x = x + (a[0] * rxyz) - (w * x);
+    y = y + (a[1] * rxyz) - (w * z);
+    z = z + (a[2] * rxyz) - (w * z);
+ 
+    /* Recompute magnitude of position vector */
+    rxyz = sqrt (x*x + y*y + z*z);
+
+    /* Apply e-terms to position and velocity */
+    x = v2[0];
+    y = v2[1];
+    z = v2[2];
+    w = (x * a[0]) + (y * a[1]) + (z * a[2]);
+    wd = (x * ad[0]) + (y * ad[1]) + (z * ad[2]);
+    x = x + (a[0] * rxyz) - (w * x);
+    y = y + (a[1] * rxyz) - (w * y);
+    z = z + (a[2] * rxyz) - (w * z);
+    xd = v2[3] + (ad[0] * rxyz) - (wd * x);
+    yd = v2[4] + (ad[1] * rxyz) - (wd * y);
+    zd = v2[5] + (ad[2] * rxyz) - (wd * z);
+
+    /*  Convert to spherical  */
+    rxysq = (x * x) + (y * y);
+    rxy = sqrt (rxysq);
+
+    /* Convert back to spherical coordinates */
+    if (x == zero && y == zero)
+	r1950 = zero;
+    else {
+	r1950 = atan2 (y,x);
+	if (r1950 < zero)
+	    r1950 = r1950 + d2pi;
+	}
+    d1950 = atan2 (z,rxy);
+
+    if (rxy > tiny) {
+	ur = (x*yd - y*xd) / rxysq;
+	ud = (zd*rxysq - z * (x*xd + y*yd)) / ((rxysq + z*z) * rxy);
+	}
+
+    if (*parallax > tiny) {
+	*rv = ((x * xd) + (y * yd) + (z * zd)) / (*parallax * vf * rxyz);
+	*parallax = *parallax / rxyz;
+	}
+
+    /* Return results */
+    *ra = raddeg (r1950);
+    *dec = raddeg (d1950);
+    *rapm  = ur / 360000.0;
+    *decpm = ud / 360000.0;
+
+    if (diag) {
+	dra = 240.0 * raddeg (r1950 - r2000);
+	ddec = 3600.0 * raddeg (d1950 - d2000);
+	fprintf(stderr,"B1950-J2000: dra= %11.5f sec  ddec= %f11.5f arcsec\n",
+		dra, ddec);
+	}
+
+    return;
+}
+
+
+/* Convert B1950.0 FK4 star data to J2000.0 FK5 */
+static double em[6][6] = {
+    {	 0.9999256782,		/* em[0][0] */
+	-0.0111820611,		/* em[0][1] */
+	-0.0048579477,		/* em[0][2] */
+	 0.00000242395018,	/* em[0][3] */
+	-0.00000002710663,	/* em[0][4] */
+	-0.00000001177656 },	/* em[0][5] */
+ 
+    {	 0.0111820610,		/* em[1][0] */
+	 0.9999374784,		/* em[1][1] */
+	-0.0000271765,		/* em[1][2] */
+	 0.00000002710663,	/* em[1][3] */
+	 0.00000242397878,	/* em[1][4] */
+	-0.00000000006587 },	/* em[1][5] */
+ 
+    {	 0.0048579479,		/* em[2][0] */
+	-0.0000271474,		/* em[2][1] */
+	 0.9999881997,		/* em[2][2] */
+	 0.00000001177656,	/* em[2][3] */
+	-0.00000000006582,	/* em[2][4] */
+	 0.00000242410173 },	/* em[2][5] */
+ 
+    {	-0.000551,		/* em[3][0] */
+	-0.238565,		/* em[3][1] */
+	 0.435739,		/* em[3][2] */
+	 0.99994704,		/* em[3][3] */
+	-0.01118251,		/* em[3][4] */
+	-0.00485767 },		/* em[3][5] */
+ 
+    {	 0.238514,		/* em[4][0] */
+	-0.002667,		/* em[4][1] */
+	-0.008541,		/* em[4][2] */
+	 0.01118251,		/* em[4][3] */
+	 0.99995883,		/* em[4][4] */
+	-0.00002718 },		/* em[4][5] */
+ 
+    {	-0.435623,		/* em[5][0] */
+	 0.012254,		/* em[5][1] */
+	 0.002117,		/* em[5][2] */
+	 0.00485767,		/* em[5][3] */
+	-0.00002714,		/* em[5][4] */
+	 1.00000956 }		/* em[5][5] */
+    };
+
+void
+fk425 (ra, dec)
+
+double	*ra;		/* Right ascension in degrees (B1950 in, J2000 out) */
+double	*dec;		/* Declination in degrees (B1950 in, J2000 out) */
+
+{
+double	rapm;		/* Proper motion in right ascension */
+double	decpm;		/* Proper motion in declination  */
+			/* In: rad/trop.yr.  Out:  rad/jul.yr. */
+
+    rapm = (double) 0.0;
+    decpm = (double) 0.0;
+    fk425m (ra, dec, &rapm, &decpm);
+    return;
+}
+
+
+void
+fk425e (ra, dec, epoch)
+
+double	*ra;		/* Right ascension in degrees (B1950 in, J2000 out) */
+double	*dec;		/* Declination in degrees (B1950 in, J2000 out) */
+double	epoch;		/* Besselian epoch in years */
+{
+double	rapm;		/* Proper motion in right ascension */
+double	decpm;		/* Proper motion in declination  */
+			/* In: rad/trop.yr.  Out:  rad/jul.yr. */
+
+    rapm = (double) 0.0;
+    decpm = (double) 0.0;
+    fk425m (ra, dec, &rapm, &decpm);
+    *ra = *ra + (rapm * (epoch - 2000.0));
+    *dec = *dec + (decpm * (epoch - 2000.0));
+    return;
+}
+
+void
+fk425m (ra, dec, rapm, decpm)
+
+double	*ra, *dec;	/* Right ascension and declination in degrees
+			   input:  B1950.0,FK4	returned:  J2000.0,FK5 */
+double	*rapm, *decpm;	/* Proper motion in right ascension and declination
+			   input:  B1950.0,FK4	returned:  J2000.0,FK5
+			           ra/dec deg/trop.yr.     ra/dec deg/jul.yr.  */
+{
+    double parallax = 0.0;
+    double rv = 0.0;
+
+    fk425pv (ra, dec, rapm, decpm, &parallax, &rv);
+    return;
+}
+
+
+void
+fk425pv (ra,dec,rapm,decpm, parallax, rv)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+double	*rapm;		/* Proper motion in right ascension */
+double	*decpm;		/* Proper motion in declination
+			 * In:  ra/dec degrees/Julian year (not ra*cos(dec))
+			 * Out: ra/dec degrees/tropical year */
+double *parallax;	/* Parallax (arcsec) */
+double *rv;		/* Rradial velocity (km/s, +ve = moving away) */
+
+/*  This routine converts stars from the old Bessel-Newcomb FK4 system
+    to the IAU 1976 FK5 Fricke system, using Yallop's implementation
+    (see ref 2) of a matrix method due to Standish (see ref 3).  The
+    numerical values of ref 2 are used canonically.
+
+ *  Conversion from other than Besselian epoch 1950.0 to other than Julian
+    epoch 2000.0 will require use of the appropriate precession, proper
+    motion, and e-terms routines before and/or after fk425 is called.
+ 
+ *  In the FK4 catalogue the proper motions of stars within 10 degrees
+    of the poles do not embody the differential e-term effect and should,
+    strictly speaking, be handled in a different manner from stars outside
+    these regions.  however, given the general lack of homogeneity of the
+    star data available for routine astrometry, the difficulties of handling
+    positions that may have been determined from astrometric fields spanning
+    the polar and non-polar regions, the likelihood that the differential
+    e-terms effect was not taken into account when allowing for proper motion
+    in past astrometry, and the undesirability of a discontinuity in the
+    algorithm, the decision has been made in this routine to include the
+    effect of differential e-terms on the proper motions for all stars,
+    whether polar or not, at epoch 2000, and measuring on the sky rather
+    than in terms of dra, the errors resulting from this simplification are
+    less than 1 milliarcsecond in position and 1 milliarcsecond per century
+    in proper motion.
+
+    References:
+
+      1  "Mean and apparent place computations in the new IAU System.
+          I. The transformation of astrometric catalog systems to the
+ 	  equinox J2000.0." Smith, C.A.; Kaplan, G.H.; Hughes, J.A.;
+	  Seidelmann, P.K.; Yallop, B.D.; Hohenkerk, C.Y.
+ 	  Astronomical Journal vol. 97, Jan. 1989, p. 265-273.
+
+      2  "Mean and apparent place computations in the new IAU System.
+	  II. Transformation of mean star places from FK4 B1950.0 to
+ 	  FK5 J2000.0 using matrices in 6-space."  Yallop, B.D.;
+	  Hohenkerk, C.Y.; Smith, C.A.; Kaplan, G.H.; Hughes, J.A.;
+	  Seidelmann, P.K.; Astronomical Journal vol. 97, Jan. 1989,
+	  p. 274-279.
+
+      3  "Conversion of positions and proper motions from B1950.0 to the
+	  IAU system at J2000.0", Standish, E.M.  Astronomy and
+	  Astrophysics, vol. 115, no. 1, Nov. 1982, p. 20-22.
+
+   P.T.Wallace   Starlink   20 December 1993
+   Doug Mink     Smithsonian Astrophysical Observatory  7 June 1995 */
+
+{
+    double r1950,d1950;		/* B1950.0 ra,dec (rad) */
+    double r2000,d2000;		/* J2000.0 ra,dec (rad) */
+
+    /* Miscellaneous */
+    double ur,ud,sr,cr,sd,cd,w,wd;
+    double x,y,z,xd,yd,zd, dra,ddec;
+    double rxyz, rxysq, rxy, rxyzsq, spxy, spxyz;
+    int	i,j;
+    int	diag = 0;
+
+    double r0[3],rd0[3];	/* star position and velocity vectors */
+    double v1[6],v2[6];		/* combined position and velocity vectors */
+
+    /* Constants */
+    double zero = (double) 0.0;
+    double vf = 21.095;	/* Km per sec to AU per tropical century */
+			/* = 86400 * 36524.2198782 / 149597870 */
+
+    /* Convert B1950 RA and Dec from degrees to radians */
+    r1950 = degrad (*ra);
+    d1950 = degrad (*dec);
+
+    /* Convert B1950 RA and Dec proper motion from degrees/year to arcsec/tc */
+    ur = *rapm  * 360000.0;
+    ud = *decpm * 360000.0;
+
+    /* Convert direction to Cartesian */
+    sr = sin (r1950);
+    cr = cos (r1950);
+    sd = sin (d1950);
+    cd = cos (d1950);
+    r0[0] = cr * cd;
+    r0[1] = sr * cd;
+    r0[2] = sd;
+
+    /* Convert motion to Cartesian */
+    w = vf * *rv * *parallax;
+    if (ur != zero || ud != zero || (*rv != zero && *parallax != zero)) {
+	rd0[0] = (-sr * cd * ur) - (cr * sd * ud) + (w * r0[0]);
+	rd0[1] =  (cr * cd * ur) - (sr * sd * ud) + (w * r0[1]);
+	rd0[2] = 	                (cd * ud) + (w * r0[2]);
+	}
+    else {
+	rd0[0] = zero;
+	rd0[1] = zero;
+	rd0[2] = zero;
+	}
+
+    /* Remove e-terms from position and express as position+velocity 6-vector */
+    w = (r0[0] * a[0]) + (r0[1] * a[1]) + (r0[2] * a[2]);
+    for (i = 0; i < 3; i++)
+	v1[i] = r0[i] - a[i] + (w * r0[i]);
+
+    /* Remove e-terms from proper motion and express as 6-vector */
+    wd = (r0[0] * ad[0]) + (r0[1] * ad[1]) + (r0[2] * ad[2]);
+    for (i = 0; i < 3; i++)
+	v1[i+3] = rd0[i] - ad[i] + (wd * r0[i]);
+
+    /* Alternately: Put proper motion in 6-vector without adding e-terms
+    for (i = 0; i < 3; i++)
+	v1[i+3] = rd0[i]; */
+
+    /* Convert position + velocity vector to FK5 system */
+    for (i = 0; i < 6; i++) {
+	w = zero;
+	for (j = 0; j < 6; j++) {
+	    w += em[i][j] * v1[j];
+	    }
+	v2[i] = w;
+	}
+
+    /* Vector components */
+    x = v2[0];
+    y = v2[1];
+    z = v2[2];
+    xd = v2[3];
+    yd = v2[4];
+    zd = v2[5];
+
+    /* Magnitude of position vector */
+    rxysq = x*x + y*y;
+    rxy = sqrt (rxysq);
+    rxyzsq = rxysq + z*z;
+    rxyz = sqrt (rxyzsq);
+
+    spxy = (x * xd) + (y * yd);
+    spxyz = spxy + (z * zd);
+
+    /* Convert back to spherical coordinates */
+    if (x == zero && y == zero)
+	r2000 = zero;
+    else {
+	r2000 = atan2 (y,x);
+	if (r2000 < zero)
+	    r2000 = r2000 + d2pi;
+	}
+    d2000 = atan2 (z,rxy);
+
+    if (rxy > tiny) {
+	ur = ((x * yd) - (y * xd)) / rxysq;
+	ud = ((zd * rxysq) - (z * spxy)) / (rxyzsq * rxy);
+	}
+
+    if (*parallax > tiny) {
+	*rv = spxyz / (*parallax * rxyz * vf);
+	*parallax = *parallax / rxyz;
+	}
+
+    /* Return results */
+    *ra = raddeg (r2000);
+    *dec = raddeg (d2000);
+    *rapm  = ur / 360000.0;
+    *decpm = ud / 360000.0;
+
+    if (diag) {
+	dra = 240.0 * raddeg (r2000 - r1950);
+	ddec = 3600.0 * raddeg (d2000 - d1950);
+	fprintf(stderr,"J2000-B1950: dra= %11.5f sec  ddec= %f11.5f arcsec\n",
+		dra, ddec);
+	}
+    return;
+}
+
+int	idg=0;
+
+/*  l2,b2 system of galactic coordinates
+ *  p = 192.25       ra of galactic north pole (mean b1950.0)
+ *  q =  62.6        inclination of galactic to mean b1950.0 equator
+ *  r =  33          longitude of ascending node
+ *  p,q,r are degrees
+
+ *  Equatorial to galactic rotation matrix
+    (The Eulerian angles are p, q, 90-r)
+	+cp.cq.sr-sp.cr	+sp.cq.sr+cp.cr	-sq.sr
+	-cp.cq.cr-sp.sr	-sp.cq.cr+cp.sr	+sq.cr
+	cp.sq		+sp.sq		+cq
+ */
+
+static
+double bgal[3][3] =
+	{{-0.066988739415,-0.872755765852,-0.483538914632},
+	{0.492728466075,-0.450346958020, 0.744584633283},
+	{-0.867600811151,-0.188374601723, 0.460199784784}};
+
+/*---  Transform B1950.0 FK4 equatorial coordinates to
+ *     IAU 1958 galactic coordinates */
+
+void
+fk42gal (dtheta,dphi)
+
+double *dtheta;	/* B1950.0 FK4 right ascension in degrees
+		   Galactic longitude (l2) in degrees (returned) */
+double *dphi;	/* B1950.0 FK4 declination in degrees
+		   Galactic latitude (b2) in degrees (returned) */
+
+/*  Input equatorial coordinates are B1950 FK4.
+    Use fk52gal() to convert from j2000.0 coordinates.
+    Reference: Blaauw et al, MNRAS,121,123 (1960) */
+{
+    double pos[3],pos1[3],r,dl,db,rl,rb,rra,rdec,dra,ddec;
+    void v2s3(),s2v3();
+    int i;
+    char *eqcoor, *eqstrn();
+
+    dra = *dtheta;
+    ddec = *dphi;
+    rra = degrad (dra);
+    rdec = degrad (ddec);
+
+    /*  remove e-terms */
+    /*	call jpabe (rra,rdec,-1,idg) */
+
+    /*  Spherical to Cartesian */
+    r = 1.;
+    s2v3 (rra,rdec,r,pos);
+
+    /*  rotate to galactic */
+    for (i = 0; i<3; i++) {
+	pos1[i] = pos[0]*bgal[i][0] + pos[1]*bgal[i][1] + pos[2]*bgal[i][2];
+	}
+
+    /*  Cartesian to spherical */
+    v2s3 (pos1,&rl,&rb,&r);
+
+    dl = raddeg (rl);
+    db = raddeg (rb);
+    *dtheta = dl;
+    *dphi = db;
+
+    /*  Print result if in diagnostic mode */
+    if (idg) {
+	eqcoor = eqstrn (dra,ddec);
+	fprintf (stderr,"FK42GAL: B1950 RA,Dec= %s\n",eqcoor);
+	fprintf (stderr,"FK42GAL: long = %.5f lat = %.5f\n",dl,db);
+	free (eqcoor);
+	}
+
+    return;
+}
+
+
+/*--- Transform IAU 1958 galactic coordinates to B1950.0 'FK4'
+ *    equatorial coordinates */
+
+void
+gal2fk4 (dtheta,dphi)
+
+double *dtheta;	/* Galactic longitude (l2) in degrees
+		   B1950 FK4 RA in degrees (returned) */
+double *dphi;	/* Galactic latitude (b2) in degrees
+		   B1950 FK4 Dec in degrees (returned) */
+
+/*  Output equatorial coordinates are B1950.0 FK4.
+    Use gal2fk5() to convert to J2000 coordinates.
+    Reference:  Blaauw et al, MNRAS,121,123 (1960) */
+
+{
+    double pos[3],pos1[3],r,dl,db,rl,rb,rra,rdec,dra,ddec;
+    void v2s3(),s2v3();
+    char *eqcoor, *eqstrn();
+    int i;
+
+    /*  spherical to cartesian */
+    dl = *dtheta;
+    db = *dphi;
+    rl = degrad (dl);
+    rb = degrad (db);
+    r = 1.0;
+    s2v3 (rl,rb,r,pos);
+
+    /*  rotate to equatorial coordinates */
+    for (i = 0; i < 3; i++) {
+	pos1[i] = pos[0]*bgal[0][i] + pos[1]*bgal[1][i] + pos[2]*bgal[2][i];
+	}
+
+    /*  cartesian to spherical */
+    v2s3 (pos1,&rra,&rdec,&r);
+
+/*  introduce e-terms */
+/*	jpabe (rra,rdec,-1,idg); */
+
+    dra = raddeg (rra);
+    ddec = raddeg (rdec);
+    *dtheta = dra;
+    *dphi = ddec;
+
+    /*  print result if in diagnostic mode */
+    if (idg) {
+	fprintf (stderr,"GAL2FK4: long = %.5f lat = %.5f\n",dl,db);
+	eqcoor = eqstrn (dra,ddec);
+	fprintf (stderr,"GAL2FK4: B1950 RA,Dec= %s\n",eqcoor);
+	free (eqcoor);
+	}
+
+    return;
+}
+
+
+/*  l2,b2 system of galactic coordinates
+    p = 192.25       ra of galactic north pole (mean b1950.0)
+    q =  62.6        inclination of galactic to mean b1950.0 equator
+    r =  33          longitude of ascending node
+    p,q,r are degrees */
+
+/*  Equatorial to galactic rotation matrix
+    The eulerian angles are p, q, 90-r
+	+cp.cq.sr-sp.cr     +sp.cq.sr+cp.cr     -sq.sr
+	-cp.cq.cr-sp.sr     -sp.cq.cr+cp.sr     +sq.cr
+	+cp.sq              +sp.sq              +cq		*/
+
+static
+double jgal[3][3] =
+	{{-0.054875539726,-0.873437108010,-0.483834985808},
+	{0.494109453312,-0.444829589425, 0.746982251810},
+	{-0.867666135858,-0.198076386122, 0.455983795705}};
+
+/* Transform J2000 equatorial coordinates to IAU 1958 galactic coordinates */
+
+void
+fk52gal (dtheta,dphi)
+
+double *dtheta;	/* J2000 right ascension in degrees
+		   Galactic longitude (l2) in degrees (returned) */
+double *dphi;	/* J2000 declination in degrees
+		   Galactic latitude (b2) in degrees (returned) */
+
+/* Rotation matrices by P.T.Wallace, Starlink eqgal and galeq, March 1986 */
+
+/*  Input equatorial coordinates are J2000 FK5.
+    Use gal2fk4() if converting from B1950 FK4 coordinates.
+    Reference: Blaauw et al, MNRAS,121,123 (1960) */
+{
+    double pos[3],pos1[3],r,dl,db,rl,rb,rra,rdec,dra,ddec;
+    void v2s3(),s2v3();
+    char *eqcoor, *eqstrn();
+    int i;
+
+    /*  Spherical to cartesian */
+    dra = *dtheta;
+    ddec = *dphi;
+    rra = degrad (dra);
+    rdec = degrad (ddec);
+    r = 1.0;
+    (void)s2v3 (rra,rdec,r,pos);
+
+    /*  Rotate to galactic */
+    for (i = 0; i < 3; i++) {
+	pos1[i] = pos[0]*jgal[i][0] + pos[1]*jgal[i][1] + pos[2]*jgal[i][2];
+	}
+
+    /*  Cartesian to spherical */
+    v2s3 (pos1,&rl,&rb,&r);
+
+    dl = raddeg (rl);
+    db = raddeg (rb);
+    *dtheta = dl;
+    *dphi = db;
+
+    /*  Print result if in diagnostic mode */
+    if (idg) {
+	eqcoor = eqstrn (dra,ddec);
+	fprintf (stderr,"FK52GAL: J2000 RA,Dec= %s\n",eqcoor);
+	fprintf (stderr,"FK52GAL: long = %.5f lat = %.5f\n",dl,db);
+	free (eqcoor);
+	}
+
+    return;
+}
+
+
+/*--- Transform IAU 1958 galactic coordinates to J2000 equatorial coordinates */
+
+void
+gal2fk5 (dtheta,dphi)
+
+double *dtheta;	/* Galactic longitude (l2) in degrees
+		   J2000.0 ra in degrees (returned) */
+double *dphi;	/* Galactic latitude (b2) in degrees
+		   J2000.0 dec in degrees (returned) */
+
+/*  Output equatorial coordinates are J2000.
+   Use gal2fk4() to convert to B1950 coordinates.
+    Reference: Blaauw et al, MNRAS,121,123 (1960) */
+
+{
+    double pos[3],pos1[3],r,dl,db,rl,rb,rra,rdec,dra,ddec;
+    void v2s3(),s2v3();
+    int i;
+    char *eqcoor, *eqstrn();
+
+    /*  Spherical to Cartesian */
+    dl = *dtheta;
+    db = *dphi;
+    rl = degrad (dl);
+    rb = degrad (db);
+    r = 1.0;
+    s2v3 (rl,rb,r,pos);
+
+    /*  Rotate to equatorial coordinates */
+    for (i = 0; i < 3; i++) {
+	    pos1[i] = pos[0]*jgal[0][i] + pos[1]*jgal[1][i] + pos[2]*jgal[2][i];
+	    }
+
+    /*  Cartesian to Spherical */
+    v2s3 (pos1,&rra,&rdec,&r);
+    dra = raddeg (rra);
+    ddec = raddeg (rdec);
+    *dtheta = dra;
+    *dphi = ddec;
+
+    /*  Print result if in diagnostic mode */
+    if (idg) {
+	fprintf (stderr,"GAL2FK5: long = %.5f lat = %.5f\n",dl,db);
+	eqcoor = eqstrn (dra,ddec);
+	fprintf (stderr,"GAL2FK5: J2000 RA,Dec= %s\n",eqcoor);
+	free (eqcoor);
+	}
+
+    return;
+}
+
+
+/* Return string with right ascension in hours and declination in degrees */
+
+char *eqstrn (dra, ddec)
+
+double	dra;		/* Right ascension in degrees */
+double	ddec;		/* Declination in degrees */
+
+{
+char	*eqcoor;	/* ASCII character string of position (returned) */
+char	decp;
+int	rah,irm,decd,decm;
+double	xpos,ypos,xp,yp,ras,decs;
+
+    /*  Right ascension to hours, minutes, and seconds */
+    xpos = dra / 15.0;
+    rah = (int) xpos;
+    xp = (double) 60.0 * (xpos - (double) rah);
+    irm = (int) xp;
+    ras = (double) 60.0 * (xp - (double) irm);
+
+    /* Declination to degrees, minutes, seconds */
+    if (ddec < 0) {
+	ypos = -ddec;
+	decp = '-';
+	}
+    else {
+	decp = '+';
+	ypos = ddec;
+	}
+    decd = (int) ypos;
+    yp = (double) 60.0 * (ypos - (double) decd);
+    decm = (int) yp;
+    decs = (double) 60.0 * (yp - (double) decm);
+
+    eqcoor = malloc (32);
+    (void)sprintf (eqcoor,"%02d:%02d:%06.3f %c%02d:%02d:%05.2f",
+		   rah,irm,ras,decp,decd,decm,decs);
+    if (eqcoor[6] == ' ')
+	eqcoor[6] = '0';
+    if (eqcoor[20] == ' ')
+	eqcoor[20] = '0';
+
+    return (eqcoor);
+}
+
+
+/* Convert geocentric equatorial rectangular coordinates to
+   right ascension and declination, and distance */
+
+
+/* These routines are based on similar ones in Pat Wallace's slalib package */
+
+/* Convert B1950 right ascension and declination to ecliptic coordinates */
+
+void
+fk42ecl (dtheta, dphi, epoch)
+
+double *dtheta;	/* B1950 right ascension in degrees
+		   Galactic longitude (l2) in degrees (returned) */
+double *dphi;	/* B1950 declination in degrees
+		   Galactic latitude (b2) in degrees (returned) */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    void fk425e(), fk52ecl();
+
+    /* Convert from B1950 to J2000 coordinates */
+    fk425e (dtheta, dphi, epoch);
+
+    /* Convert from J2000 to ecliptic coordinates */
+    fk52ecl (dtheta, dphi, epoch);
+
+    return;
+}
+
+/* Convert J2000 right ascension and declination to ecliptic coordinates */
+
+void
+fk52ecl (dtheta, dphi, epoch)
+
+double *dtheta;	/* J2000 right ascension in degrees
+		   Galactic longitude (l2) in degrees (returned) */
+double *dphi;	/* J2000 declination in degrees
+		   Galactic latitude (b2) in degrees (returned) */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    int i, j;
+    double t, eps0, rphi, rtheta;
+    double v1[3], v2[3], r;
+    double rmat[9], *rmati;	/* Rotation matrix  */
+
+    void rotmat(), v2s3(), s2v3(), fk5prec();
+
+    /* Precess coordinates from J2000 to epoch */
+    if (epoch != 2000.0)
+	fk5prec (2000.0, epoch, dtheta, dphi);
+
+    /* Convert from degrees to radians */
+    rtheta = degrad (*dtheta);
+    rphi = degrad (*dphi);
+
+    /* Convert RA,Dec to x,y,z */
+    r = 1.0;
+    s2v3 (rtheta, rphi, r, v1);
+
+    /* Interval between basic epoch J2000.0 and current epoch (JC) in centuries*/
+    t = (epoch - 2000.0) * 0.01;
+ 
+    /* Mean obliquity */
+    eps0 = secrad ((84381.448 + (-46.8150 + (-0.00059 + 0.001813*t) * t) * t));
+ 
+    /* Form the equatorial to ecliptic rotation matrix (IAU 1980 theory).
+     *  References: Murray, C.A., Vectorial Astrometry, section 4.3.
+     *    The matrix is in the sense   v[ecl]  =  rmat * v[equ];  the
+     *    equator, equinox and ecliptic are mean of date. */
+    rotmat (1, eps0, 0.0, 0.0, rmat);
+
+    /* Multiply position vector by equatoria to eccliptic rotation matrix */
+    rmati = rmat;
+    for (i = 0; i < 3; i++) {
+	v2[i] = 0;
+	for (j = 0; j < 3; j++)
+	    v2[i] = v2[i] + (*rmati++ * v1[j]);
+	}
+
+    /* Convert x,y,z to latitude, longitude */
+    v2s3 (v2, &rtheta, &rphi, &r);
+
+    /* Convert from radians to degrees */
+    *dtheta = raddeg (rtheta);
+    *dphi = raddeg (rphi);
+}
+
+
+/* Convert ecliptic coordinates to B1950 right ascension and declination */
+
+void
+ecl2fk4 (dtheta, dphi, epoch)
+
+double *dtheta;	/* Galactic longitude (l2) in degrees
+		   B1950 right ascension in degrees (returned) */
+double *dphi;	/* Galactic latitude (b2) in degrees
+		   B1950 declination in degrees (returned) */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    void ecl2fk5(), fk524e();
+
+    /* Convert from ecliptic to J2000 coordinates */
+    ecl2fk5 (dtheta, dphi, epoch);
+
+    /* Convert from J2000 to B1950 coordinates */
+    fk524e (dtheta, dphi, epoch);
+
+    return;
+}
+
+
+
+/* Convert ecliptic coordinates to J2000 right ascension and declination */
+
+void
+ecl2fk5 (dtheta, dphi, epoch)
+
+double *dtheta;	/* Galactic longitude (l2) in degrees
+		   J2000 right ascension in degrees  (returned) */
+double *dphi;	/* Galactic latitude (b2) in degrees
+		   J2000 declination in degrees (returned) */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    int i, j;
+    double rtheta, rphi, v1[3], v2[3];
+    double t, eps0, r;
+    double rmat[9];	/* Rotation matrix */
+    void v2s3(),s2v3(), fk5prec(), rotmat();
+
+    rtheta = degrad (*dtheta);
+    rphi = degrad (*dphi);
+
+    /* Convert RA,Dec to x,y,z */
+    r = 1.0;
+    s2v3 (rtheta, rphi, r, v1);
+
+    /* Interval between basic epoch J2000.0 and current epoch (JC) in centuries*/
+    t = (epoch - 2000.0) * 0.01;
+ 
+    /* Mean obliquity */
+    eps0 = secrad ((84381.448 + (-46.8150 + (-0.00059 + 0.001813*t) * t) * t));
+ 
+    /* Form the equatorial to ecliptic rotation matrix (IAU 1980 theory).
+     *  References: Murray, C.A., Vectorial Astrometry, section 4.3.
+     *    The matrix is in the sense   v[ecl]  =  rmat * v[equ];  the
+     *    equator, equinox and ecliptic are mean of date. */
+    rotmat (1, eps0, 0.0, 0.0, rmat);
+ 
+    /* Multiply position vector by ecliptic to equatorial rotation matrix */
+    for (i = 0; i < 3; i++) {
+	v2[i] = 0;
+	for (j = 0; j < 3; j++)
+	    v2[i] = v2[i] + (rmat[3*j + i] * v1[j]);
+	}
+
+    /* Cartesian to spherical */
+    v2s3 (v2, &rtheta, &rphi, &r);
+
+    /* Convert from radians to degrees */
+    *dtheta = raddeg (rtheta);
+    *dphi = raddeg (rphi);
+
+    if (epoch != 2000.0)
+	fk5prec (epoch, 2000.0, dtheta, dphi);
+}
+
+
+/* The following routines are modified from Patrick Wallace's SLALIB */
+
+/* Precess coordinates between epochs in FK4 */
+void
+fk4prec (ep0, ep1, ra, dec)
+
+double ep0;	/* Starting Besselian epoch */
+double ep1;	/* Ending Besselian epoch */
+double *ra;	/* RA in degrees mean equator & equinox of epoch ep0
+		      mean equator & equinox of epoch ep1 (returned) */
+double *dec;	/* Dec in degrees mean equator & equinox of epoch ep0
+		       mean equator & equinox of epoch ep1 (returned) */
+/*
+**  Precession - FK4 (Bessel-Newcomb, pre-IAU1976)
+**
+**  This routine will not correctly convert between FK4 and FK5
+**  For output in FK5, precess to 1950.0 and use fk425() on result.
+**
+**  Based on slaPreces(), P.T.Wallace   Starlink   22 December 1993
+*/
+{
+    int i, j;
+    double pm[9], *pmi, v1[3], v2[3], rra, rdec, r;
+    void v2s3(),s2v3(), mprecfk4();
+
+    rra = degrad (*ra);
+    rdec = degrad (*dec);
+    r = 1.0;
+ 
+    /* Generate appropriate precession matrix */
+    mprecfk4 ( ep0, ep1, pm );
+ 
+    /* Convert RA,Dec to x,y,z */
+    s2v3 (rra, rdec, r, v1);
+ 
+    /* Multiply position vector by precession matrix */
+    pmi = pm;
+    for (i = 0; i < 3; i++) {
+	v2[i] = 0;
+	for (j = 0; j < 3; j++)
+	    v2[i] = v2[i] + (*pmi++ * v1[j]);
+	}
+ 
+    /* Back to RA,Dec */
+    v2s3 (v2, &rra, &rdec, &r);
+
+    /* Convert from radians to degrees */
+    *ra = raddeg (rra);
+    *dec = raddeg (rdec);
+}
+
+void
+fk5prec (ep0, ep1, ra, dec)
+
+double ep0;	/* Starting epoch */
+double ep1;	/* Ending epoch */
+double *ra;	/* RA in degrees mean equator & equinox of epoch ep0
+		      mean equator & equinox of epoch ep1 (returned) */
+double *dec;	/* Dec in degrees mean equator & equinox of epoch ep0
+		       mean equator & equinox of epoch ep1 (returned) */
+/*
+**  Precession -  FK5 (Fricke, post-IAU1976)
+**
+**  This routine will not correctly convert between FK5 and FK4.
+**  For output in FK4, precess to 2000.0 and use fk524() on result.
+**
+**  Based on slaPreces(), P.T.Wallace   Starlink   22 December 1993
+*/
+{
+    int i, j;
+    double pm[9], *pmi, v1[3], v2[3], rra, rdec, r;
+    void v2s3(),s2v3(), mprecfk5();
+
+    rra = degrad (*ra);
+    rdec = degrad (*dec);
+    r = 1.0;
+ 
+    /* Generate appropriate precession matrix */
+    mprecfk5 (ep0, ep1, pm);
+ 
+    /* Convert RA,Dec to x,y,z */
+    s2v3 (rra, rdec, r, v1);
+ 
+    /* Multiply position vector by precession matrix */
+    pmi = pm;
+    for (i = 0; i < 3; i++) {
+	v2[i] = 0;
+	for (j = 0; j < 3; j++)
+	    v2[i] = v2[i] + ( v1[j] * *pmi++ );
+	}
+ 
+    /* Back to RA,Dec */
+    v2s3 (v2, &rra, &rdec, &r);
+
+    /* Convert from radians to degrees */
+    *ra = raddeg (rra);
+    *dec = raddeg (rdec);
+    return;
+}
+
+
+void
+mprecfk4 (bep0, bep1, rmatp)
+
+double bep0;		/* Beginning Besselian epoch */
+double bep1;		/* Ending Besselian epoch */
+double rmatp[9];	/* 3x3 Precession matrix (returned) */
+
+/*
+**  Generate the matrix of precession between two epochs,
+**  using the old, pre-IAU1976, Bessel-Newcomb model, using
+**  Kinoshita's formulation (double precision)
+**
+**  The matrix is in the sense   v(bep1)  =  rmatp * v(bep0)
+**
+**  Reference:
+**     Kinoshita, H. (1975) 'Formulas for precession', SAO Special
+**     Report No. 364, Smithsonian Institution Astrophysical
+**     Observatory, Cambridge, Massachusetts.
+**
+**  Based on slaPrebn() by P.T.Wallace   Starlink   30 October 1993
+*/
+{
+    double bigt, t, tas2r, w, zeta, z, theta;
+    void rotmat();
+ 
+    /* Interval between basic epoch B1850.0 and beginning epoch in TC */
+    bigt  = ( bep0 - 1850.0 ) / 100.0;
+ 
+    /* Interval over which precession required, in tropical centuries */
+    t = ( bep1 - bep0 ) / 100.0;
+ 
+    /* Euler angles */
+    tas2r = secrad (t);
+    w = 2303.5548 + ( 1.39720 + 0.000059 * bigt ) * bigt;
+    zeta = (w + ( 0.30242 - 0.000269 * bigt + 0.017996 * t ) * t ) * tas2r;
+    z = (w + ( 1.09478 + 0.000387 * bigt + 0.018324 * t ) * t ) * tas2r;
+    theta = ( 2005.1125 + ( - 0.85294 - 0.000365* bigt ) * bigt +
+	    ( - 0.42647 - 0.000365 * bigt - 0.041802 * t ) * t ) * tas2r;
+ 
+    /* Rotation matrix */
+    rotmat (323, -zeta, theta, -z, rmatp);
+    return;
+}
+
+
+void
+mprecfk5 (ep0, ep1, rmatp)
+
+double ep0;		/* Beginning epoch */
+double ep1;		/* Ending epoch */
+double rmatp[9];	/* 3x3 Precession matrix (returned) */
+
+/*
+**  Form the matrix of precession between two epochs (IAU 1976, FK5).
+**  Notes:
+**  1)  The epochs are TDB (loosely ET) Julian epochs.
+**  2)  The matrix is in the sense   v(ep1)  =  rmatp * v(ep0) .
+**
+**  References:
+**     Lieske,J.H., 1979. Astron. Astrophys.,73,282.
+**          equations (6) & (7), p283.
+**     Kaplan,G.H., 1981. USNO circular no. 163, pa2.
+**
+**  Based on slaPrec(), P.T.Wallace   Starlink   31 October 1993
+*/
+{
+    double t0, t, tas2r, w, zeta, z, theta;
+    void rotmat();
+ 
+    /* Interval between basic epoch J2000.0 and beginning epoch (JC) */
+    t0 = ( ep0 - 2000.0 ) / 100.0;
+ 
+    /* Interval over which precession required (JC) */
+    t =  ( ep1 - ep0 ) / 100.0;
+ 
+    /* Euler angles */
+    tas2r = secrad (t);
+    w = 2306.2181 + ( ( 1.39656 - ( 0.000139 * t0 ) ) * t0 );
+    zeta = (w + ( ( 0.30188 - 0.000344 * t0 ) + 0.017998 * t ) * t ) * tas2r;
+    z = (w + ( ( 1.09468 + 0.000066 * t0 ) + 0.018203 * t ) * t ) * tas2r;
+    theta = ( ( 2004.3109 + ( - 0.85330 - 0.000217 * t0 ) * t0 )
+	  + ( ( -0.42665 - 0.000217 * t0 ) - 0.041833 * t ) * t ) * tas2r;
+ 
+    /* Rotation matrix */
+    rotmat (323, -zeta, theta, -z, rmatp);
+    return;
+}
+
+
+/* Make 3-D rotation matrix from up to three rotations */
+
+void
+rotmat (axes, rot1, rot2, rot3, matrix)
+
+int axes;	/* Axes about which coordinates are rotated (1=x, 2=y, 3=z) */
+double rot1;	/* First rotation in degrees */
+double rot2;	/* Second rotation in degrees */
+double rot3;	/* Third rotation in degrees */
+double *matrix;	/* 3x3 rotation matrix (returned) */
+
+{
+    int i, j, k, naxis, iaxes, iaxis;
+    double rot[3], srot, crot, *mati, w, wm[9], *wmi, matn[9];
+    int axis[3];
+
+    /* Initial final rotation matrix */
+    mati = matrix;
+    for (i = 0; i < 3; i++) {
+	for (j=0; j < 3; j++) {
+	    if (i == j)
+		*mati++ = 1.0;
+	    else
+		*mati++ = 0.0;
+	    }
+	}
+
+    /* Separate digits of rotation axis string and count rotations */
+    naxis = 0;
+    iaxes = axes;
+    axis[0] = iaxes / 100;
+    if (axis[0] > 0) {
+	naxis++;
+	iaxes = iaxes - (100 * axis[0]);
+	}
+    axis[naxis] = iaxes / 10;
+    if (axis[naxis] > 0) {
+	iaxes = iaxes - (10 * axis[naxis]);
+	naxis++;
+	}
+    axis[naxis] = iaxes;
+    if (axis[naxis] > 0)
+	naxis++;
+
+    /* Set up rotation angles */
+    rot[0] = rot1;
+    rot[1] = rot2;
+    rot[2] = rot3;
+
+    /* For each digit of axis string, set up matrix */
+    for (iaxis = 0; iaxis < naxis; iaxis++) {
+
+	/* Initialize current rotation matrix */
+	mati = matn;
+	for (i = 0; i < 3; i++) {
+	    for (j=0; j < 3; j++) {
+		if (i == j)
+		    *mati++ = 1.0;
+		else
+		    *mati++ = 0.0;
+		}
+	    }
+
+	srot = sin (rot[iaxis]);
+	crot = cos (rot[iaxis]);
+	
+	/* Matrix for rotation in X */
+	if (axis[iaxis] == 1) {
+	    matn[4] = crot;
+	    matn[5] = srot;
+	    matn[7] = -srot;
+	    matn[8] = crot;
+	    }
+	
+	/* Matrix for rotation in Y */
+	else if (axis[iaxis] == 2) {
+	    matn[0] = crot;
+	    matn[2] = -srot;
+	    matn[6] = srot;
+	    matn[8] = crot;
+	    }
+	
+	/* Matrix for rotation in Z */
+	else {
+	    matn[0] = crot;
+	    matn[1] = srot;
+	    matn[3] = -srot;
+	    matn[4] = crot;
+	    }
+
+	/* Multiply existing rotation matrix by new rotation matrix */
+	for (i = 0; i < 3; i++) {
+	    for (j = 0; j < 3; j++) {
+		w = 0.0;
+		for (k = 0; k < 3; k++)
+		    w+= matn[3*i + k] * matrix[3*k + j];
+		wm[3*i + j] = w;
+		}
+	    }
+
+	/* Update output matrix */
+	mati = matrix;
+	wmi = wm;
+	for (i = 0; i < 9; i++) {
+	    *mati++ = *wmi++;
+	    }
+	}
+    return;
+}
+
+
+/* The following routines are from Doug Mink's Fortran ephemeris library */
+
+/* Convert right ascensiona and declination in degrees and distance to
+   geocentric equatorial rectangular coordinates */
+
+void
+d2v3 (rra,rdec,r,pos)
+
+double rra;	/* Right ascension in degrees */
+double rdec;	/* Declination in degrees */
+double r;	/* Distance to object in same units as pos */
+double pos[3];	/* x,y,z geocentric equatorial position of object (returned) */
+{
+    s2v3 (degrad (rra), degrad (rdec), r, pos);
+
+    return;
+}
+
+
+/* Convert right ascension, declination, and distance to
+   geocentric equatorial rectangular coordinates */
+
+void
+s2v3 (rra,rdec,r,pos)
+
+double rra;	/* Right ascension in radians */
+double rdec;	/* Declination in radians */
+double r;	/* Distance to object in same units as pos */
+double pos[3];	/* x,y,z geocentric equatorial position of object (returned) */
+{
+    pos[0] = r * cos (rra) * cos (rdec);
+    pos[1] = r * sin (rra) * cos (rdec);
+    pos[2] = r * sin (rdec);
+
+    return;
+}
+
+
+/* Convert geocentric equatorial rectangular coordinates to
+   right ascension and declination in degrees and distance */
+
+void
+v2d3 (pos,rra,rdec,r)
+
+double pos[3];	/* x,y,z geocentric equatorial position of object */
+double *rra;	/* Right ascension in degrees (returned) */
+double *rdec;	/* Declination in degrees (returned) */
+double *r;	/* Distance to object in same units as pos (returned) */
+{
+    v2s3 (pos, rra, rdec, r);
+    *rra = raddeg (*rra);
+    *rdec = raddeg (*rdec);
+    return;
+}
+
+/* Convert geocentric equatorial rectangular coordinates to
+   right ascension, declination, and distance */
+
+void
+v2s3 (pos,rra,rdec,r)
+
+double pos[3];	/* x,y,z geocentric equatorial position of object */
+double *rra;	/* Right ascension in radians (returned) */
+double *rdec;	/* Declination in radians (returned) */
+double *r;	/* Distance to object in same units as pos (returned) */
+{
+    double x,y,z,rxy,rxy2,z2;
+
+    x = pos[0];
+    y = pos[1];
+    z = pos[2];
+
+    *rra = atan2 (y, x);
+
+    /* Keep RA within 0 to 2pi range */
+    if (*rra < 0.0)
+	*rra = *rra + (2.0 * PI);
+    if (*rra > 2.0 * PI)
+	*rra = *rra - (2.0 * PI);
+
+    rxy2 = x*x + y*y;
+    rxy = sqrt (rxy2);
+    *rdec = atan2 (z, rxy);
+
+    z2 = z * z;
+    *r = sqrt (rxy2 + z2);
+
+    return;
+}
+
+/*
+ * Nov  6 1995	Include stdlib.h instead of malloc.h
+ * Apr  1 1996	Add arbitrary epoch precession
+ * Apr 26 1996	Add FK4 <-> FK5 subroutines for use when epoch is known
+ * Aug  6 1996	Clean up after lint
+ * Nov  4 1996	Break SLA subroutines into separate file slasubs.c
+ * Dec  9 1996	Change arguments to degrees in FK4 and FK5 precession programs
+ * Dec 10 1996	All subroutine arguments are degrees except vector conversions
+ *
+ * Mar 20 1997	Drop unused variables after lint
+ *
+ * Apr 14 1998	Add ecliptic coordinate conversions and general conversion routines
+ * Apr 23 1998	Add LINEAR coordinate system
+ * Apr 28 1998	Change coordinate system flags to WCS_*
+ * Apr 28 1998	Return -1 from wcscsys if not a legal coordinate system
+ * May  7 1998	Keep theta within 0 to 2pi in ecl2fk5()
+ * May 13 1998	Add wcsceq()
+ * May 13 1998	Add equinox arguments to wcscon()
+ * Jun 24 1998	Set J2000 from ICRS in wcscsys()
+ * Jul  9 1998	Include stdio.h for fprintf() and sprintf() declarations
+ * Sep 17 1998	Add wcscstr() to get coordinate string
+ * Sep 21 1998	Fix bug in wcscstr() which returned B2000 instead of J2000
+ * Sep 21 1998	Add subroutine to convert proper motions, too.
+ * Oct 21 1998	In wcscstr(), drop .00 from returned string
+ * Nov 18 1998	Rename jpcop() v2s3() and jpcon() s2v3() (spherical to vector)
+ * Dec  2 1998	Add PLANET coordinate system to wcscsys() and wcscstr()
+ *
+ * Mar 10 2000	Precess coordinates correctly from other than 1950.0 and 2000.0
+ * Mar 10 2000	Set coordinate system to J2000 or B1950 if string is numeric
+ * Mar 14 2000	Clean up code in fk524m() and fk425m()
+ * May 31 2000	Add proper motion correctly if proper motion precessed
+ * Jun 26 2000	Add some support for WCS_XY image coordinates
+ * Sep 14 2000	Return -1 from wcscsys if equinox is less than 1900.0
+ * Oct 31 2000	Add proper motion after fk425 or fk524 from system epoch
+ * Oct 31 2000	Fix proper motion units in fk524p() and fk425p()
+ * Nov  6 2000	Update fk425 and fk524 algorithms to include parallax and rv
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Mar 21 2001	Move braces around bgal[] and jgal[] matrix initialization
+ *
+ * Feb 13 2002	Fix precession units problem in ecl2fk5() and fk52ecl()
+ *
+ * Apr 13 2005	Replace all sla_lib calls with local code
+ * Nov  1 2005	Add WCS_ICRS, and unprecessable system
+ *
+ * Jan  5 2006	Fix bugs in precession subroutines mprecxxx()
+ * May  3 2006	Drop declarations of unused variables suggested by Robert Lupton
+ * Oct  6 2006	If pixel coordinates, set system to WCS_XY in wcscsys()
+ * Oct 30 2006	Add LINEAR and ICRS to wcscstr() returns
+ *
+ * Aug 15 2007	Clean up code in rotmat()
+ * Nov  8 2007	In wcsconp, make it clear that proper motion is in spherical coordinates
+ */
diff --git a/Code/src/libwcs/wcscon1.c b/Code/src/libwcs/wcscon1.c
new file mode 100644
index 0000000000000000000000000000000000000000..27c7eaf258685893d1f4bb118b9cbae64e437379
--- /dev/null
+++ b/Code/src/libwcs/wcscon1.c
@@ -0,0 +1,2414 @@
+/*** File wcscon.c
+ *** November 29, 2006
+ *** Doug Mink, Harvard-Smithsonian Center for Astrophysics
+ *** Some subroutines are based on Starlink subroutines by Patrick Wallace
+ *** Copyright (C) 1995-2006
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	wcscon.c (World Coordinate System conversion)
+ * Purpose:	Convert between various sky coordinate systems
+ * Subroutine:	wcscon (sys1,sys2,eq1,eq2,theta,phi,epoch)
+ *		convert between coordinate systems
+ * Subroutine:  wcsconp (sys1,sys2,eq1,eq2,ep1,ep2,dtheta,dphi,ptheta,pphi)
+ *              convert coordinates and proper motion between coordinate systems
+ * Subroutine:  wcsconv (sys1,sys2,eq1,eq2,ep1,ep2,dtheta,dphi,ptheta,pphi,px,rv)
+ *              convert coordinates and proper motion between coordinate systems
+ * Subroutine:	wcscsys (cstring) returns code for coordinate system in string
+ * Subroutine:	wcsceq (wcstring) returns equinox in years from system string
+ * Subroutine:	wcscstr (sys,equinox,epoch) returns system string from equinox
+ * Subroutine:	fk524 (ra,dec) Convert J2000(FK5) to B1950(FK4) coordinates
+ * Subroutine:	fk524e (ra, dec, epoch) (more accurate for known position epoch)
+ * Subroutine:	fk524m (ra,dec,rapm,decpm) exact
+ * Subroutine:	fk524pv (ra,dec,rapm,decpm,parallax,rv) more exact
+ * Subroutine:	fk425 (ra,dec) Convert B1950(FK4) to J2000(FK5) coordinates
+ * Subroutine:	fk425e (ra, dec, epoch) (more accurate for known position epoch)
+ * Subroutine:	fk425m (ra, dec, rapm, decpm) exact
+ * Subroutine:	fk425pv (ra,dec,rapm,decpm,parallax,rv) more exact
+ * Subroutine:	fk42gal (dtheta,dphi) Convert B1950(FK4) to galactic coordinates
+ * Subroutine:	fk52gal (dtheta,dphi) Convert J2000(FK5) to galactic coordinates
+ * Subroutine:	gal2fk4 (dtheta,dphi) Convert galactic coordinates to B1950(FK4)
+ * Subroutine:	gal2fk5 (dtheta,dphi) Convert galactic coordinates to J2000<FK5)
+ * Subroutine:	fk42ecl (dtheta,dphi,epoch) Convert B1950(FK4) to ecliptic coordinates
+ * Subroutine:	fk52ecl (dtheta,dphi,epoch) Convert J2000(FK5) to ecliptic coordinates
+ * Subroutine:	ecl2fk4 (dtheta,dphi,epoch) Convert ecliptic coordinates to B1950(FK4)
+ * Subroutine:	ecl2fk5 (dtheta,dphi,epoch) Convert ecliptic coordinates to J2000<FK5)
+ * Subroutine:  fk5prec (ep0, ep1, ra, dec) Precession ep0 to ep1, FK5 system
+ * Subroutine:  fk4prec (ep0, ep1, ra, dec) Precession ep0 to ep1, FK4 system
+ * Subroutine:  s2v3 (rra, rdec, r, pos) RA, Dec, Distance to Cartesian
+ * Subroutine:  v2s3 (pos, rra, rdec, r) Cartesion to RA, Dec, Distance
+ * Subroutine:  rotmat (axes, rot1, rot2, rot3, matrix) Rotation angles to matrix
+ */
+
+#include <math.h>
+#ifndef VMS
+#include <stdlib.h>
+#endif
+#include <stdio.h>	/* for fprintf() and sprintf() */
+#include <ctype.h>
+#include <string.h>
+#include "wcs.h"
+
+void fk524(), fk524e(), fk524m(), fk524pv();
+void fk425(), fk425e(), fk425m(), fk425pv();
+void fk42gal(), fk52gal(), gal2fk4(), gal2fk5();
+void fk42ecl(), fk52ecl(), ecl2fk4(), ecl2fk5();
+
+/* Convert from coordinate system sys1 to coordinate system sys2, converting
+   proper motions, too, and adding them if an epoch is specified */
+
+void
+wcsconp (sys1, sys2, eq1, eq2, ep1, ep2, dtheta, dphi, ptheta, pphi)
+
+int	sys1;	/* Input coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+int	sys2;	/* Output coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+double	eq1;	/* Input equinox (default of sys1 if 0.0) */
+double	eq2;	/* Output equinox (default of sys2 if 0.0) */
+double	ep1;	/* Input Besselian epoch in years (for proper motion) */
+double	ep2;	/* Output Besselian epoch in years (for proper motion) */
+double	*dtheta; /* Longitude or right ascension in degrees
+		   Input in sys1, returned in sys2 */
+double	*dphi;	/* Latitude or declination in degrees
+		   Input in sys1, returned in sys2 */
+double	*ptheta; /* Longitude or right ascension proper motion in degrees/year
+		   Input in sys1, returned in sys2 */
+double	*pphi;	/* Latitude or declination proper motion in degrees/year
+		   Input in sys1, returned in sys2 */
+
+{
+    void fk5prec(), fk4prec();
+
+    /* Set equinoxes if 0.0 */
+    if (eq1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    eq1 = 1950.0;
+	else
+	    eq1 = 2000.0;
+	}
+    if (eq2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    eq2 = 1950.0;
+	else
+	    eq2 = 2000.0;
+	}
+
+    /* Set epochs if 0.0 */
+    if (ep1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    ep1 = 1950.0;
+	else
+	    ep1 = 2000.0;
+	}
+    if (ep2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    ep2 = 1950.0;
+	else
+	    ep2 = 2000.0;
+	}
+
+    if (sys1 == WCS_ICRS && sys2 == WCS_ICRS)
+	eq2 = eq1;
+
+    if (sys1 == WCS_J2000 && sys2 == WCS_ICRS && eq1 == 2000.0) {
+	eq2 = eq1;
+	sys1 = sys2;
+	}
+
+    /* Set systems and equinoxes so that ICRS coordinates are not precessed */
+    if (sys1 == WCS_ICRS && sys2 == WCS_J2000 && eq2 == 2000.0) {
+	eq1 = eq2;
+	sys1 = sys2;
+	}
+
+    /* If systems and equinoxes are the same, add proper motion and return */
+    if (sys2 == sys1 && eq1 == eq2) {
+	if (ep1 != ep2) {
+	    if (sys1 == WCS_J2000) {
+		*dtheta = *dtheta + ((ep2 - ep1) * *ptheta);
+		*dphi = *dphi + ((ep2 - ep1) * *pphi);
+		}
+	    else if (sys1 == WCS_B1950) {
+		*dtheta = *dtheta + ((ep2 - ep1) * *ptheta);
+		*dphi = *dphi + ((ep2 - ep1) * *pphi);
+		}
+	    }
+	if (eq1 != eq2) {
+	    if (sys1 == WCS_B1950)
+		fk4prec (eq1, eq2, dtheta, dphi);
+	    if (sys1 == WCS_J2000)
+		fk5prec (eq1, 2000.0, dtheta, dphi);
+	    }
+	return;
+	}
+
+    /* Precess from input equinox to input system equinox, if necessary */
+    if (sys1 == WCS_B1950 && eq1 != 1950.0)
+	fk4prec (eq1, 1950.0, dtheta, dphi);
+    if (sys1 == WCS_J2000 && eq1 != 2000.0)
+	fk5prec (eq1, 2000.0, dtheta, dphi);
+
+    /* Convert to B1950 FK4 */
+    if (sys2 == WCS_B1950) {
+	if (sys1 == WCS_J2000) {
+	    if (*ptheta != 0.0 || *pphi != 0.0) {
+		fk524m (dtheta, dphi, ptheta, pphi);
+		if (ep1 == 2000.0)
+		    ep1 = 1950.0;
+		if (ep2 != 1950.0) {
+		    *dtheta = *dtheta + ((ep2 - 1950.0) * *ptheta);
+		    *dphi = *dphi + ((ep2 - 1950.0) * *pphi);
+		    }
+		}
+	    else if (ep2 != 1950.0)
+		fk524e (dtheta, dphi, ep2);
+	    else
+		fk524 (dtheta, dphi);
+	    }
+	else if (sys1 == WCS_GALACTIC) 
+	    gal2fk4 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC)
+	    ecl2fk4 (dtheta, dphi, ep2);
+	}
+
+    else if (sys2 == WCS_J2000) {
+        if (sys1 == WCS_B1950) {
+	    if (*ptheta != 0.0 || *pphi != 0.0) {
+		fk425m (dtheta, dphi, ptheta, pphi);
+		if (ep2 != 2000.0) {
+		    *dtheta = *dtheta + ((ep2 - 2000.0) * *ptheta);
+		    *dphi = *dphi + ((ep2 - 2000.0) * *pphi);
+		    }
+		}
+            else if (ep2 > 0.0)
+                fk425e (dtheta, dphi, ep2);
+            else
+                fk425 (dtheta, dphi);
+            }
+        else if (sys1 == WCS_GALACTIC)
+            gal2fk5 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC)
+	    ecl2fk5 (dtheta, dphi, ep2);
+	}
+
+    else if (sys2 == WCS_GALACTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk42gal (dtheta, dphi);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk52gal (dtheta, dphi);
+	    }
+        else if (sys1 == WCS_ECLIPTIC) {
+	    ecl2fk5 (dtheta, dphi, ep2);
+	    fk52gal (dtheta, dphi);
+	    }
+	}
+
+    else if (sys2 == WCS_ECLIPTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    if (ep2 > 0.0)
+		fk42ecl (dtheta, dphi, ep2);
+	    else
+		fk42ecl (dtheta, dphi, 1950.0);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk52ecl (dtheta, dphi, ep2);
+	    }
+        else if (sys1 == WCS_GALACTIC) {
+	    gal2fk5 (dtheta, dphi);
+	    fk52ecl (dtheta, dphi, ep2);
+	    }
+	}
+
+    /* Precess to desired equinox, if necessary */
+    if (sys2 == WCS_B1950 && eq2 != 1950.0)
+	fk4prec (1950.0, eq2, dtheta, dphi);
+    if (sys2 == WCS_J2000 && eq2 != 2000.0)
+	fk5prec (2000.0, eq2, dtheta, dphi);
+
+    /* Keep latitude/declination between +90 and -90 degrees */
+    if (*dphi > 90.0) {
+	*dphi = 180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+    else if (*dphi < -90.0) {
+	*dphi = -180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+
+    /* Keep longitude/right ascension between 0 and 360 degrees */
+    if (*dtheta > 360.0)
+	*dtheta = *dtheta - 360.0;
+    else if (*dtheta < 0.0)
+	*dtheta = *dtheta + 360.0;
+    return;
+}
+
+
+/* Convert from coordinate system sys1 to coordinate system sys2, converting
+   proper motions, too, and adding them if an epoch is specified */
+
+void
+wcsconv (sys1, sys2, eq1, eq2, ep1, ep2, dtheta, dphi, ptheta, pphi, px, rv)
+
+int	sys1;	/* Input coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+int	sys2;	/* Output coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+double	eq1;	/* Input equinox (default of sys1 if 0.0) */
+double	eq2;	/* Output equinox (default of sys2 if 0.0) */
+double	ep1;	/* Input Besselian epoch in years (for proper motion) */
+double	ep2;	/* Output Besselian epoch in years (for proper motion) */
+double	*dtheta; /* Longitude or right ascension in degrees
+		   Input in sys1, returned in sys2 */
+double	*dphi;	/* Latitude or declination in degrees
+		   Input in sys1, returned in sys2 */
+double	*ptheta; /* Longitude or right ascension proper motion in degrees/year
+		   Input in sys1, returned in sys2 */
+double	*pphi;	/* Latitude or declination proper motion in degrees/year
+		   Input in sys1, returned in sys2 */
+double	*px;	/* Parallax in arcseconds */
+double	*rv;	/* Radial velocity in km/sec */
+
+{
+    void fk5prec(), fk4prec();
+
+    /* Set equinoxes if 0.0 */
+    if (eq1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    eq1 = 1950.0;
+	else
+	    eq1 = 2000.0;
+	}
+    if (eq2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    eq2 = 1950.0;
+	else
+	    eq2 = 2000.0;
+	}
+
+    /* Set epochs if 0.0 */
+    if (ep1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    ep1 = 1950.0;
+	else
+	    ep1 = 2000.0;
+	}
+    if (ep2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    ep2 = 1950.0;
+	else
+	    ep2 = 2000.0;
+	}
+
+    /* Set systems and equinoxes so that ICRS coordinates are not precessed */
+    if (sys1 == WCS_ICRS && sys2 == WCS_ICRS)
+	eq2 = eq1;
+
+    if (sys1 == WCS_J2000 && sys2 == WCS_ICRS && eq1 == 2000.0) {
+	eq2 = eq1;
+	sys1 = sys2;
+	}
+
+    if (sys1 == WCS_ICRS && sys2 == WCS_J2000 && eq2 == 2000.0) {
+	eq1 = eq2;
+	sys1 = sys2;
+	}
+
+    /* If systems and equinoxes are the same, add proper motion and return */
+    if (sys2 == sys1 && eq1 == eq2) {
+	if (ep1 != ep2) {
+	    if (sys1 == WCS_J2000) {
+		*dtheta = *dtheta + ((ep2 - ep1) * *ptheta);
+		*dphi = *dphi + ((ep2 - ep1) * *pphi);
+		}
+	    else if (sys1 == WCS_B1950) {
+		*dtheta = *dtheta + ((ep2 - ep1) * *ptheta);
+		*dphi = *dphi + ((ep2 - ep1) * *pphi);
+		}
+	    }
+	return;
+	}
+
+    /* Precess from input equinox to input system equinox, if necessary */
+    if (eq1 != eq2) {
+	if (sys1 == WCS_B1950 && eq1 != 1950.0)
+	   fk4prec (eq1, 1950.0, dtheta, dphi);
+	if (sys1 == WCS_J2000 && eq1 != 2000.0)
+	   fk5prec (eq1, 2000.0, dtheta, dphi);
+	}
+
+    /* Convert to B1950 FK4 */
+    if (sys2 == WCS_B1950) {
+	if (sys1 == WCS_J2000) {
+	    if (*ptheta != 0.0 || *pphi != 0.0) {
+		if (*px != 0.0 || *rv != 0.0)
+		    fk524pv (dtheta, dphi, ptheta, pphi, px, rv);
+		else
+		    fk524m (dtheta, dphi, ptheta, pphi);
+		if (ep1 == 2000.0)
+		    ep1 = 1950.0;
+		if (ep2 != 1950.0) {
+		    *dtheta = *dtheta + ((ep2 - 1950.0) * *ptheta);
+		    *dphi = *dphi + ((ep2 - 1950.0) * *pphi);
+		    }
+		}
+	    else if (ep2 != 1950.0)
+		fk524e (dtheta, dphi, ep2);
+	    else
+		fk524 (dtheta, dphi);
+	    }
+	else if (sys1 == WCS_GALACTIC) 
+	    gal2fk4 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC)
+	    ecl2fk4 (dtheta, dphi, ep2);
+	}
+
+    else if (sys2 == WCS_J2000) {
+        if (sys1 == WCS_B1950) {
+	    if (*ptheta != 0.0 || *pphi != 0.0) {
+		if (*px != 0.0 || *rv != 0.0)
+		    fk425pv (dtheta, dphi, ptheta, pphi, px, rv);
+		else
+		    fk425m (dtheta, dphi, ptheta, pphi);
+		if (ep2 != 2000.0) {
+		    *dtheta = *dtheta + ((ep2 - 2000.0) * *ptheta);
+		    *dphi = *dphi + ((ep2 - 2000.0) * *pphi);
+		    }
+		}
+            else if (ep2 > 0.0)
+                fk425e (dtheta, dphi, ep2);
+            else
+                fk425 (dtheta, dphi);
+            }
+        else if (sys1 == WCS_GALACTIC)
+            gal2fk5 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC)
+	    ecl2fk5 (dtheta, dphi, ep2);
+	}
+
+    else if (sys2 == WCS_GALACTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk42gal (dtheta, dphi);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk52gal (dtheta, dphi);
+	    }
+        else if (sys1 == WCS_ECLIPTIC) {
+	    ecl2fk5 (dtheta, dphi, ep2);
+	    fk52gal (dtheta, dphi);
+	    }
+	}
+
+    else if (sys2 == WCS_ECLIPTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    if (ep2 > 0.0)
+		fk42ecl (dtheta, dphi, ep2);
+	    else
+		fk42ecl (dtheta, dphi, 1950.0);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (ep2 != 0.0 && (*ptheta != 0.0 || *pphi != 0.0)) {
+		*dtheta = *dtheta + (*ptheta * (ep2 - ep1));
+		*dphi = *dphi + (*pphi * (ep2 - ep1));
+		}
+	    fk52ecl (dtheta, dphi, ep2);
+	    }
+        else if (sys1 == WCS_GALACTIC) {
+	    gal2fk5 (dtheta, dphi);
+	    fk52ecl (dtheta, dphi, ep2);
+	    }
+	}
+
+    /* Precess to desired equinox, if necessary */
+    if (eq1 != eq2) {
+	if (sys2 == WCS_B1950 && eq2 != 1950.0)
+	    fk4prec (1950.0, eq2, dtheta, dphi);
+	if (sys2 == WCS_J2000 && eq2 != 2000.0)
+	    fk5prec (2000.0, eq2, dtheta, dphi);
+	}
+
+    /* Keep latitude/declination between +90 and -90 degrees */
+    if (*dphi > 90.0) {
+	*dphi = 180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+    else if (*dphi < -90.0) {
+	*dphi = -180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+
+    /* Keep longitude/right ascension between 0 and 360 degrees */
+    if (*dtheta > 360.0)
+	*dtheta = *dtheta - 360.0;
+    else if (*dtheta < 0.0)
+	*dtheta = *dtheta + 360.0;
+    return;
+}
+
+
+/* Convert from coordinate system sys1 to coordinate system sys2 */
+
+void
+wcscon (sys1, sys2, eq1, eq2, dtheta, dphi, epoch)
+
+int	sys1;	/* Input coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+int	sys2;	/* Output coordinate system (J2000, B1950, ECLIPTIC, GALACTIC */
+double	eq1;	/* Input equinox (default of sys1 if 0.0) */
+double	eq2;	/* Output equinox (default of sys2 if 0.0) */
+double	*dtheta; /* Longitude or right ascension in degrees
+		   Input in sys1, returned in sys2 */
+double	*dphi;	/* Latitude or declination in degrees
+		   Input in sys1, returned in sys2 */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    void fk5prec(), fk4prec();
+
+    /* Set equinoxes if 0.0 */
+    if (eq1 == 0.0) {
+	if (sys1 == WCS_B1950)
+	    eq1 = 1950.0;
+	else
+	    eq1 = 2000.0;
+	}
+    if (eq2 == 0.0) {
+	if (sys2 == WCS_B1950)
+	    eq2 = 1950.0;
+	else
+	    eq2 = 2000.0;
+	}
+
+    /* Set systems and equinoxes so that ICRS coordinates are not precessed */
+    if (sys1 == WCS_ICRS && sys2 == WCS_ICRS)
+	eq2 = eq1;
+
+    if (sys1 == WCS_J2000 && sys2 == WCS_ICRS && eq1 == 2000.0) {
+	eq2 = eq1;
+	sys1 = sys2;
+	}
+
+    if (sys1 == WCS_ICRS && sys2 == WCS_J2000 && eq2 == 2000.0) {
+	eq1 = eq2;
+	sys1 = sys2;
+	}
+
+    /* If systems and equinoxes are the same, return */
+    if (sys2 == sys1 && eq1 == eq2)
+	return;
+
+    /* Precess from input equinox, if necessary */
+    if (eq1 != eq2) {
+	if (sys1 == WCS_B1950 && eq1 != 1950.0)
+	   fk4prec (eq1, 1950.0, dtheta, dphi);
+	if (sys1 == WCS_J2000 && eq1 != 2000.0)
+	   fk5prec (eq1, 2000.0, dtheta, dphi);
+	}
+
+    /* Convert to B1950 FK4 */
+    if (sys2 == WCS_B1950) {
+	if (sys1 == WCS_J2000) {
+	    if (epoch > 0)
+		fk524e (dtheta, dphi, epoch);
+	    else
+		fk524 (dtheta, dphi);
+	    }
+	else if (sys1 == WCS_GALACTIC) 
+	    gal2fk4 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC) {
+	    if (epoch > 0)
+		ecl2fk4 (dtheta, dphi, epoch);
+	    else
+		ecl2fk4 (dtheta, dphi, 1950.0);
+	    }
+	}
+
+    else if (sys2 == WCS_J2000) {
+        if (sys1 == WCS_B1950) {
+            if (epoch > 0)
+                fk425e (dtheta, dphi, epoch);
+            else
+                fk425 (dtheta, dphi);
+            }
+        else if (sys1 == WCS_GALACTIC)
+            gal2fk5 (dtheta, dphi);
+	else if (sys1 == WCS_ECLIPTIC) {
+	    if (epoch > 0)
+		ecl2fk5 (dtheta, dphi, epoch);
+	    else
+		ecl2fk5 (dtheta, dphi, 2000.0);
+	    }
+	}
+
+    else if (sys2 == WCS_GALACTIC) {
+        if (sys1 == WCS_B1950)
+	    fk42gal (dtheta, dphi);
+        else if (sys1 == WCS_J2000)
+	    fk52gal (dtheta, dphi);
+        else if (sys1 == WCS_ECLIPTIC) {
+	    if (epoch > 0)
+		ecl2fk5 (dtheta, dphi, epoch);
+	    else
+		ecl2fk5 (dtheta, dphi, 2000.0);
+	    fk52gal (dtheta, dphi);
+	    }
+	}
+
+    else if (sys2 == WCS_ECLIPTIC) {
+        if (sys1 == WCS_B1950) {
+	    if (epoch > 0)
+		fk42ecl (dtheta, dphi, epoch);
+	    else
+		fk42ecl (dtheta, dphi, 1950.0);
+	    }
+        else if (sys1 == WCS_J2000) {
+	    if (epoch > 0)
+		fk52ecl (dtheta, dphi, epoch);
+	    else
+		fk52ecl (dtheta, dphi, 2000.0);
+	    }
+        else if (sys1 == WCS_GALACTIC) {
+	    gal2fk5 (dtheta, dphi);
+	    if (epoch > 0)
+		fk52ecl (dtheta, dphi, epoch);
+	    else
+		fk52ecl (dtheta, dphi, 2000.0);
+	    }
+	}
+
+    /* Precess to desired equinox, if necessary */
+    if (eq1 != eq2) {
+	if (sys2 == WCS_B1950 && eq2 != 1950.0)
+	    fk4prec (1950.0, eq2, dtheta, dphi);
+	if (sys2 == WCS_J2000 && eq2 != 2000.0)
+	    fk5prec (2000.0, eq2, dtheta, dphi);
+	}
+
+    /* Keep latitude/declination between +90 and -90 degrees */
+    if (*dphi > 90.0) {
+	*dphi = 180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+    else if (*dphi < -90.0) {
+	*dphi = -180.0 - *dphi;
+	*dtheta = *dtheta + 180.0;
+	}
+
+    /* Keep longitude/right ascension between 0 and 360 degrees */
+    if (*dtheta > 360.0)
+	*dtheta = *dtheta - 360.0;
+    else if (*dtheta < 0.0)
+	*dtheta = *dtheta + 360.0;
+
+    return;
+}
+
+
+/* Set coordinate system from string */
+int
+wcscsys (wcstring)
+
+char *wcstring;		/* Name of coordinate system */
+{
+    double equinox;
+
+    if (wcstring[0] == 'J' || wcstring[0] == 'j' ||
+	!strcmp (wcstring,"2000") || !strcmp (wcstring, "2000.0") ||
+	!strcmp (wcstring,"ICRS") || !strcmp (wcstring, "icrs") ||
+	!strncmp (wcstring,"FK5",3) || !strncmp (wcstring, "fk5",3))
+	return WCS_J2000;
+
+    if (wcstring[0] == 'B' || wcstring[0] == 'b' ||
+	!strcmp (wcstring,"1950") || !strcmp (wcstring, "1950.0") ||
+	!strncmp (wcstring,"FK4",3) || !strncmp (wcstring, "fk4",3))
+	return WCS_B1950;
+
+    else if (wcstring[0] == 'I' || wcstring[0] == 'i' )
+	return WCS_ICRS;
+
+    else if (wcstring[0] == 'G' || wcstring[0] == 'g' )
+	return WCS_GALACTIC;
+
+    else if (wcstring[0] == 'E' || wcstring[0] == 'e' )
+	return WCS_ECLIPTIC;
+
+    else if (wcstring[0] == 'A' || wcstring[0] == 'a' )
+	return WCS_ALTAZ;
+
+    else if (wcstring[0] == 'N' || wcstring[0] == 'n' )
+	return WCS_NPOLE;
+
+    else if (wcstring[0] == 'L' || wcstring[0] == 'l' )
+	return WCS_LINEAR;
+
+    else if (!strncasecmp (wcstring, "pixel", 5))
+	return WCS_XY;
+
+    else if (wcstring[0] == 'P' || wcstring[0] == 'p' )
+	return WCS_PLANET;
+
+    else if (isnum (wcstring)) {
+	equinox = atof (wcstring);
+	if (equinox > 1980.0)
+	    return WCS_J2000;
+	else if (equinox > 1900.0)
+	    return WCS_B1950;
+	else
+	    return -1;
+	}
+    else
+	return -1;
+}
+
+
+/* Set equinox from string (return 0.0 if not obvious) */
+
+double
+wcsceq (wcstring)
+
+char *wcstring;		/* Name of coordinate system */
+{
+    if (wcstring[0] == 'J' || wcstring[0] == 'j' ||
+	wcstring[0] == 'B' || wcstring[0] == 'b')
+	return (atof (wcstring+1));
+    else if (!strncmp (wcstring, "FK4",3) ||
+	     !strncmp (wcstring, "fk4",3))
+	return (1950.0);
+    else if (!strncmp (wcstring, "FK5",3) ||
+	     !strncmp (wcstring, "fk5",3))
+	return (2000.0);
+    else if (!strncmp (wcstring, "ICRS",4) ||
+	     !strncmp (wcstring, "icrs",4))
+	return (2000.0);
+    else if (wcstring[0] == '1' || wcstring[0] == '2')
+	return (atof (wcstring));
+    else
+	return (0.0);
+}
+
+
+/* Set coordinate system type string from system and equinox */
+
+void
+wcscstr (cstr, syswcs, equinox, epoch)
+
+char	*cstr;		/* Coordinate system string (returned) */
+int	syswcs;		/* Coordinate system code */
+double	equinox;	/* Equinox of coordinate system */
+double	epoch;		/* Epoch of coordinate system */
+{
+
+    char *estr;
+
+    if (syswcs == WCS_XY) {
+	strcpy (cstr, "XY");
+	return;
+	}
+
+    /* Try to figure out coordinate system if it is not set */
+    if (epoch == 0.0)
+	epoch = equinox;
+    if (syswcs < 0) {
+	if (equinox > 0.0) {
+	    if (equinox == 2000.0)
+		syswcs = WCS_J2000;
+	    else if (equinox == 1950.0)
+		syswcs = WCS_B1950;
+	    }
+	else if (epoch > 0.0) {
+	    if (epoch > 1980.0) {
+		syswcs = WCS_J2000;
+		equinox = 2000.0;
+		}
+	    else {
+		syswcs = WCS_B1950;
+		equinox = 1950.0;
+		}
+	    }
+	else
+	    syswcs = WCS_J2000;
+	}
+
+    /* Set coordinate system string from system flag and epoch */
+    if (syswcs == WCS_B1950) {
+	if (epoch == 1950.0 || epoch == 0.0)
+	    strcpy (cstr, "B1950");
+	else
+	    sprintf (cstr, "B%7.2f", equinox);
+	if ((estr = strsrch (cstr,".00")) != NULL) {
+	    estr[0] = (char) 0;
+	    estr[1] = (char) 0;
+	    estr[2] = (char) 0;
+	    }
+	}
+    else if (syswcs == WCS_GALACTIC)
+	strcpy (cstr, "galactic");
+    else if (syswcs == WCS_ECLIPTIC)
+	strcpy (cstr, "ecliptic");
+    else if (syswcs == WCS_J2000) {
+	if (epoch == 2000.0 || epoch == 0.0)
+	    strcpy (cstr, "J2000");
+	else
+	    sprintf (cstr, "J%7.2f", equinox);
+	if ((estr = strsrch (cstr,".00")) != NULL) {
+	    estr[0] = (char) 0;
+	    estr[1] = (char) 0;
+	    estr[2] = (char) 0;
+	    }
+	}
+    else if (syswcs == WCS_ICRS) {
+	strcpy (cstr, "ICRS");
+	}
+    else if (syswcs == WCS_PLANET) {
+	strcpy (cstr, "PLANET");
+	}
+    else if (syswcs == WCS_LINEAR || syswcs == WCS_XY) {
+	strcpy (cstr, "LINEAR");
+	}
+    return;
+}
+
+
+/*  Constant vector and matrix (by columns)
+    These values were obtained by inverting C.Hohenkerk's forward matrix
+    (private communication), which agrees with the one given in reference
+    2 but which has one additional decimal place.  */
+
+static double a[3] = {-1.62557e-6, -0.31919e-6, -0.13843e-6};
+static double ad[3] = {1.245e-3,  -1.580e-3,  -0.659e-3};
+static double d2pi = 6.283185307179586476925287;	/* two PI */
+static double tiny = 1.e-30; /* small number to avoid arithmetic problems */
+
+/* FK524  convert J2000 FK5 star data to B1950 FK4
+   based on Starlink sla_fk524 by P.T.Wallace 27 October 1987 */
+
+static double emi[6][6] = {
+    {	 0.9999256795,		/* emi[0][0] */
+	 0.0111814828,		/* emi[0][1] */
+	 0.0048590039,		/* emi[0][2] */
+	-0.00000242389840,	/* emi[0][3] */
+	-0.00000002710544,	/* emi[0][4] */
+	-0.00000001177742 },	/* emi[0][5] */
+ 
+    {	-0.0111814828,		/* emi[1][0] */
+	 0.9999374849,		/* emi[1][1] */
+	-0.0000271771,		/* emi[1][2] */
+	 0.00000002710544,	/* emi[1][3] */
+	-0.00000242392702,	/* emi[1][4] */
+	 0.00000000006585 },	/* emi[1][5] */
+ 
+    {	-0.0048590040,		/* emi[2][0] */
+	-0.0000271557,		/* emi[2][1] */
+	 0.9999881946,		/* emi[2][2] */
+	 0.00000001177742,	/* emi[2][3] */
+	 0.00000000006585,	/* emi[2][4] */
+	-0.00000242404995 },	/* emi[2][5] */
+ 
+    {	-0.000551,		/* emi[3][0] */
+	 0.238509,		/* emi[3][1] */
+	-0.435614,		/* emi[3][2] */
+	 0.99990432,		/* emi[3][3] */
+	 0.01118145,		/* emi[3][4] */
+	 0.00485852 },		/* emi[3][5] */
+ 
+    {	-0.238560,		/* emi[4][0] */
+	-0.002667,		/* emi[4][1] */
+	 0.012254,		/* emi[4][2] */
+	-0.01118145,		/* emi[4][3] */
+	 0.99991613,		/* emi[4][4] */
+	-0.00002717 },		/* emi[4][5] */
+ 
+    {	 0.435730,		/* emi[5][0] */
+	-0.008541,		/* emi[5][1] */
+	 0.002117,		/* emi[5][2] */
+	-0.00485852,		/* emi[5][3] */
+	-0.00002716,		/* emi[5][4] */
+	 0.99996684 }		/* emi[5][5] */
+    };
+
+void
+fk524 (ra,dec)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+
+{
+    double	rapm;	/* Proper motion in right ascension */
+    double	decpm;	/* Proper motion in declination  */
+			/* In:  deg/jul.yr.  Out: deg/trop.yr.  */
+
+    rapm = (double) 0.0;
+    decpm = (double) 0.0;
+    fk524m (ra, dec, &rapm, &decpm);
+    return;
+}
+
+void
+fk524e (ra, dec, epoch)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+double	epoch;		/* Besselian epoch in years */
+
+{
+    double	rapm;	/* Proper motion in right ascension */
+    double	decpm;	/* Proper motion in declination  */
+			/* In:  deg/jul.yr.  Out: deg/trop.yr.  */
+
+    rapm = (double) 0.0;
+    decpm = (double) 0.0;
+    fk524m (ra, dec, &rapm, &decpm);
+    *ra = *ra + (rapm * (epoch - 1950.0));
+    *dec = *dec + (decpm * (epoch - 1950.0));
+    return;
+}
+
+void
+fk524m (ra,dec,rapm,decpm)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+double	*rapm;		/* Proper motion in right ascension */
+double	*decpm;		/* Proper motion in declination */
+			/* In:  ra/dec deg/jul.yr.  Out: ra/dec deg/trop.yr.  */
+
+{
+    double parallax = 0.0;
+    double rv = 0.0;
+
+    fk524pv (ra, dec, rapm, decpm, &parallax, &rv);
+    return;
+}
+
+
+void
+fk524pv (ra,dec,rapm,decpm, parallax, rv)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+double	*rapm;		/* Proper motion in right ascension */
+double	*decpm;		/* Proper motion in declination
+			 * In:  ra/dec degrees/Julian year (not ra*cos(dec))
+			 * Out: ra/dec degrees/tropical year */
+double *parallax;	/* Parallax (arcsec) */
+double *rv;		/* Rradial velocity (km/s, +ve = moving away) */
+
+/*  This routine converts stars from the IAU 1976 FK5 Fricke
+    system, to the old Bessel-Newcomb FK4 system, using Yallop's
+    implementation (see ref 2) of a matrix method due to Standish
+    (see ref 3).  The numerical values of ref 2 are used canonically.
+
+ *  Conversion from other than Julian epoch 2000.0 to other than Besselian
+    epoch 1950.0 will require use of the appropriate precession, proper
+    motion, and e-terms routines before and/or after fk524 is called.
+ 
+ *  In the FK4 catalogue the proper motions of stars within 10 degrees
+    of the poles do not embody the differential e-term effect and should,
+    strictly speaking, be handled in a different manner from stars outside
+    these regions.  however, given the general lack of homogeneity of the
+    star data available for routine astrometry, the difficulties of handling
+    positions that may have been determined from astrometric fields spanning
+    the polar and non-polar regions, the likelihood that the differential
+    e-terms effect was not taken into account when allowing for proper motion
+    in past astrometry, and the undesirability of a discontinuity in the
+    algorithm, the decision has been made in this routine to include the
+    effect of differential e-terms on the proper motions for all stars,
+    whether polar or not, at epoch 2000, and measuring on the sky rather
+    than in terms of dra, the errors resulting from this simplification are
+    less than 1 milliarcsecond in position and 1 milliarcsecond per century
+    in proper motion.
+
+    References:
+
+      1  "Mean and apparent place computations in the new IAU System.
+          I. The transformation of astrometric catalog systems to the
+ 	  equinox J2000.0." Smith, C.A.; Kaplan, G.H.; Hughes, J.A.;
+	  Seidelmann, P.K.; Yallop, B.D.; Hohenkerk, C.Y.
+ 	  Astronomical Journal vol. 97, Jan. 1989, p. 265-273.
+
+      2  "Mean and apparent place computations in the new IAU System.
+	  II. Transformation of mean star places from FK4 B1950.0 to
+ 	  FK5 J2000.0 using matrices in 6-space."  Yallop, B.D.;
+	  Hohenkerk, C.Y.; Smith, C.A.; Kaplan, G.H.; Hughes, J.A.;
+	  Seidelmann, P.K.; Astronomical Journal vol. 97, Jan. 1989,
+	  p. 274-279.
+ 
+      3  Seidelmann, P.K. (ed), 1992.  "Explanatory Supplement to
+         the Astronomical Almanac", ISBN 0-935702-68-7.
+
+      4  "Conversion of positions and proper motions from B1950.0 to the
+	  IAU system at J2000.0", Standish, E.M.  Astronomy and
+	  Astrophysics, vol. 115, no. 1, Nov. 1982, p. 20-22.
+
+   P.T.Wallace   Starlink   19 December 1993
+   Doug Mink     Smithsonian Astrophysical Observatory 1 November 2000 */
+
+{
+    double r2000,d2000;		/* J2000.0 ra,dec (radians) */
+    double r1950,d1950;		/* B1950.0 ra,dec (rad) */
+
+    /* Miscellaneous */
+    double ur,ud;
+    double sr, cr, sd, cd, x, y, z, w, wd;
+    double v1[6],v2[6];
+    double xd,yd,zd;
+    double rxyz, rxysq, rxy;
+    double dra,ddec;
+    int	i,j;
+    int	diag = 0;
+
+    /* Constants */
+    double zero = (double) 0.0;
+    double vf = 21.095;	/* Km per sec to AU per tropical century */
+			/* = 86400 * 36524.2198782 / 149597870 */
+
+    /* Convert J2000 RA and Dec from degrees to radians */
+    r2000 = degrad (*ra);
+    d2000 = degrad (*dec);
+
+    /* Convert J2000 RA and Dec proper motion from degrees/year to arcsec/tc */
+    ur = *rapm  * 360000.0;
+    ud = *decpm * 360000.0;
+
+    /* Spherical to Cartesian */
+    sr = sin (r2000);
+    cr = cos (r2000);
+    sd = sin (d2000);
+    cd = cos (d2000);
+
+    x = cr * cd;
+    y = sr * cd;
+    z = sd;
+
+    v1[0] = x;
+    v1[1] = y;
+    v1[2] = z;
+ 
+    if (ur != zero || ud != zero) {
+	v1[3] = -(ur*y) - (cr*sd*ud);
+	v1[4] =  (ur*x) - (sr*sd*ud);
+	v1[5] =          (cd*ud);
+	}
+    else {
+	v1[3] = zero;
+	v1[4] = zero;
+	v1[5] = zero;
+	}
+ 
+    /* Convert position + velocity vector to bn system */
+    for (i = 0; i < 6; i++) {
+	w = zero;
+	for (j = 0; j < 6; j++) {
+	    w = w + emi[i][j] * v1[j];
+	    }
+	v2[i] = w;
+	}
+ 
+    /* Vector components */
+    x = v2[0];
+    y = v2[1];
+    z = v2[2];
+    rxyz = sqrt (x*x + y*y + z*z);
+
+    /* Magnitude of position vector */
+    rxyz = sqrt (x*x + y*y + z*z);
+ 
+    /* Apply e-terms to position */
+    w = (x * a[0]) + (y * a[1]) + (z * a[2]);
+    x = x + (a[0] * rxyz) - (w * x);
+    y = y + (a[1] * rxyz) - (w * z);
+    z = z + (a[2] * rxyz) - (w * z);
+ 
+    /* Recompute magnitude of position vector */
+    rxyz = sqrt (x*x + y*y + z*z);
+
+    /* Apply e-terms to position and velocity */
+    x = v2[0];
+    y = v2[1];
+    z = v2[2];
+    w = (x * a[0]) + (y * a[1]) + (z * a[2]);
+    wd = (x * ad[0]) + (y * ad[1]) + (z * ad[2]);
+    x = x + (a[0] * rxyz) - (w * x);
+    y = y + (a[1] * rxyz) - (w * y);
+    z = z + (a[2] * rxyz) - (w * z);
+    xd = v2[3] + (ad[0] * rxyz) - (wd * x);
+    yd = v2[4] + (ad[1] * rxyz) - (wd * y);
+    zd = v2[5] + (ad[2] * rxyz) - (wd * z);
+
+    /*  Convert to spherical  */
+    rxysq = (x * x) + (y * y);
+    rxy = sqrt (rxysq);
+
+    /* Convert back to spherical coordinates */
+    if (x == zero && y == zero)
+	r1950 = zero;
+    else {
+	r1950 = atan2 (y,x);
+	if (r1950 < zero)
+	    r1950 = r1950 + d2pi;
+	}
+    d1950 = atan2 (z,rxy);
+
+    if (rxy > tiny) {
+	ur = (x*yd - y*xd) / rxysq;
+	ud = (zd*rxysq - z * (x*xd + y*yd)) / ((rxysq + z*z) * rxy);
+	}
+
+    if (*parallax > tiny) {
+	*rv = ((x * xd) + (y * yd) + (z * zd)) / (*parallax * vf * rxyz);
+	*parallax = *parallax / rxyz;
+	}
+
+    /* Return results */
+    *ra = raddeg (r1950);
+    *dec = raddeg (d1950);
+    *rapm  = ur / 360000.0;
+    *decpm = ud / 360000.0;
+
+    if (diag) {
+	dra = 240.0 * raddeg (r1950 - r2000);
+	ddec = 3600.0 * raddeg (d1950 - d2000);
+	fprintf(stderr,"B1950-J2000: dra= %11.5f sec  ddec= %f11.5f arcsec\n",
+		dra, ddec);
+	}
+
+    return;
+}
+
+
+/* Convert B1950.0 FK4 star data to J2000.0 FK5 */
+static double em[6][6] = {
+    {	 0.9999256782,		/* em[0][0] */
+	-0.0111820611,		/* em[0][1] */
+	-0.0048579477,		/* em[0][2] */
+	 0.00000242395018,	/* em[0][3] */
+	-0.00000002710663,	/* em[0][4] */
+	-0.00000001177656 },	/* em[0][5] */
+ 
+    {	 0.0111820610,		/* em[1][0] */
+	 0.9999374784,		/* em[1][1] */
+	-0.0000271765,		/* em[1][2] */
+	 0.00000002710663,	/* em[1][3] */
+	 0.00000242397878,	/* em[1][4] */
+	-0.00000000006587 },	/* em[1][5] */
+ 
+    {	 0.0048579479,		/* em[2][0] */
+	-0.0000271474,		/* em[2][1] */
+	 0.9999881997,		/* em[2][2] */
+	 0.00000001177656,	/* em[2][3] */
+	-0.00000000006582,	/* em[2][4] */
+	 0.00000242410173 },	/* em[2][5] */
+ 
+    {	-0.000551,		/* em[3][0] */
+	-0.238565,		/* em[3][1] */
+	 0.435739,		/* em[3][2] */
+	 0.99994704,		/* em[3][3] */
+	-0.01118251,		/* em[3][4] */
+	-0.00485767 },		/* em[3][5] */
+ 
+    {	 0.238514,		/* em[4][0] */
+	-0.002667,		/* em[4][1] */
+	-0.008541,		/* em[4][2] */
+	 0.01118251,		/* em[4][3] */
+	 0.99995883,		/* em[4][4] */
+	-0.00002718 },		/* em[4][5] */
+ 
+    {	-0.435623,		/* em[5][0] */
+	 0.012254,		/* em[5][1] */
+	 0.002117,		/* em[5][2] */
+	 0.00485767,		/* em[5][3] */
+	-0.00002714,		/* em[5][4] */
+	 1.00000956 }		/* em[5][5] */
+    };
+
+void
+fk425 (ra, dec)
+
+double	*ra;		/* Right ascension in degrees (B1950 in, J2000 out) */
+double	*dec;		/* Declination in degrees (B1950 in, J2000 out) */
+
+{
+double	rapm;		/* Proper motion in right ascension */
+double	decpm;		/* Proper motion in declination  */
+			/* In: rad/trop.yr.  Out:  rad/jul.yr. */
+
+    rapm = (double) 0.0;
+    decpm = (double) 0.0;
+    fk425m (ra, dec, &rapm, &decpm);
+    return;
+}
+
+
+void
+fk425e (ra, dec, epoch)
+
+double	*ra;		/* Right ascension in degrees (B1950 in, J2000 out) */
+double	*dec;		/* Declination in degrees (B1950 in, J2000 out) */
+double	epoch;		/* Besselian epoch in years */
+{
+double	rapm;		/* Proper motion in right ascension */
+double	decpm;		/* Proper motion in declination  */
+			/* In: rad/trop.yr.  Out:  rad/jul.yr. */
+
+    rapm = (double) 0.0;
+    decpm = (double) 0.0;
+    fk425m (ra, dec, &rapm, &decpm);
+    *ra = *ra + (rapm * (epoch - 2000.0));
+    *dec = *dec + (decpm * (epoch - 2000.0));
+    return;
+}
+
+void
+fk425m (ra, dec, rapm, decpm)
+
+double	*ra, *dec;	/* Right ascension and declination in degrees
+			   input:  B1950.0,FK4	returned:  J2000.0,FK5 */
+double	*rapm, *decpm;	/* Proper motion in right ascension and declination
+			   input:  B1950.0,FK4	returned:  J2000.0,FK5
+			           ra/dec deg/trop.yr.     ra/dec deg/jul.yr.  */
+{
+    double parallax = 0.0;
+    double rv = 0.0;
+
+    fk425pv (ra, dec, rapm, decpm, &parallax, &rv);
+    return;
+}
+
+
+void
+fk425pv (ra,dec,rapm,decpm, parallax, rv)
+
+double	*ra;		/* Right ascension in degrees (J2000 in, B1950 out) */
+double	*dec;		/* Declination in degrees (J2000 in, B1950 out) */
+double	*rapm;		/* Proper motion in right ascension */
+double	*decpm;		/* Proper motion in declination
+			 * In:  ra/dec degrees/Julian year (not ra*cos(dec))
+			 * Out: ra/dec degrees/tropical year */
+double *parallax;	/* Parallax (arcsec) */
+double *rv;		/* Rradial velocity (km/s, +ve = moving away) */
+
+/*  This routine converts stars from the old Bessel-Newcomb FK4 system
+    to the IAU 1976 FK5 Fricke system, using Yallop's implementation
+    (see ref 2) of a matrix method due to Standish (see ref 3).  The
+    numerical values of ref 2 are used canonically.
+
+ *  Conversion from other than Besselian epoch 1950.0 to other than Julian
+    epoch 2000.0 will require use of the appropriate precession, proper
+    motion, and e-terms routines before and/or after fk425 is called.
+ 
+ *  In the FK4 catalogue the proper motions of stars within 10 degrees
+    of the poles do not embody the differential e-term effect and should,
+    strictly speaking, be handled in a different manner from stars outside
+    these regions.  however, given the general lack of homogeneity of the
+    star data available for routine astrometry, the difficulties of handling
+    positions that may have been determined from astrometric fields spanning
+    the polar and non-polar regions, the likelihood that the differential
+    e-terms effect was not taken into account when allowing for proper motion
+    in past astrometry, and the undesirability of a discontinuity in the
+    algorithm, the decision has been made in this routine to include the
+    effect of differential e-terms on the proper motions for all stars,
+    whether polar or not, at epoch 2000, and measuring on the sky rather
+    than in terms of dra, the errors resulting from this simplification are
+    less than 1 milliarcsecond in position and 1 milliarcsecond per century
+    in proper motion.
+
+    References:
+
+      1  "Mean and apparent place computations in the new IAU System.
+          I. The transformation of astrometric catalog systems to the
+ 	  equinox J2000.0." Smith, C.A.; Kaplan, G.H.; Hughes, J.A.;
+	  Seidelmann, P.K.; Yallop, B.D.; Hohenkerk, C.Y.
+ 	  Astronomical Journal vol. 97, Jan. 1989, p. 265-273.
+
+      2  "Mean and apparent place computations in the new IAU System.
+	  II. Transformation of mean star places from FK4 B1950.0 to
+ 	  FK5 J2000.0 using matrices in 6-space."  Yallop, B.D.;
+	  Hohenkerk, C.Y.; Smith, C.A.; Kaplan, G.H.; Hughes, J.A.;
+	  Seidelmann, P.K.; Astronomical Journal vol. 97, Jan. 1989,
+	  p. 274-279.
+
+      3  "Conversion of positions and proper motions from B1950.0 to the
+	  IAU system at J2000.0", Standish, E.M.  Astronomy and
+	  Astrophysics, vol. 115, no. 1, Nov. 1982, p. 20-22.
+
+   P.T.Wallace   Starlink   20 December 1993
+   Doug Mink     Smithsonian Astrophysical Observatory  7 June 1995 */
+
+{
+    double r1950,d1950;		/* B1950.0 ra,dec (rad) */
+    double r2000,d2000;		/* J2000.0 ra,dec (rad) */
+
+    /* Miscellaneous */
+    double ur,ud,sr,cr,sd,cd,w,wd;
+    double x,y,z,xd,yd,zd, dra,ddec;
+    double rxyz, rxysq, rxy, rxyzsq, spxy, spxyz;
+    int	i,j;
+    int	diag = 0;
+
+    double r0[3],rd0[3];	/* star position and velocity vectors */
+    double v1[6],v2[6];		/* combined position and velocity vectors */
+
+    /* Constants */
+    double zero = (double) 0.0;
+    double vf = 21.095;	/* Km per sec to AU per tropical century */
+			/* = 86400 * 36524.2198782 / 149597870 */
+
+    /* Convert B1950 RA and Dec from degrees to radians */
+    r1950 = degrad (*ra);
+    d1950 = degrad (*dec);
+
+    /* Convert B1950 RA and Dec proper motion from degrees/year to arcsec/tc */
+    ur = *rapm  * 360000.0;
+    ud = *decpm * 360000.0;
+
+    /* Convert direction to Cartesian */
+    sr = sin (r1950);
+    cr = cos (r1950);
+    sd = sin (d1950);
+    cd = cos (d1950);
+    r0[0] = cr * cd;
+    r0[1] = sr * cd;
+    r0[2] = sd;
+
+    /* Convert motion to Cartesian */
+    w = vf * *rv * *parallax;
+    if (ur != zero || ud != zero || (*rv != zero && *parallax != zero)) {
+	rd0[0] = (-sr * cd * ur) - (cr * sd * ud) + (w * r0[0]);
+	rd0[1] =  (cr * cd * ur) - (sr * sd * ud) + (w * r0[1]);
+	rd0[2] = 	                (cd * ud) + (w * r0[2]);
+	}
+    else {
+	rd0[0] = zero;
+	rd0[1] = zero;
+	rd0[2] = zero;
+	}
+
+    /* Remove e-terms from position and express as position+velocity 6-vector */
+    w = (r0[0] * a[0]) + (r0[1] * a[1]) + (r0[2] * a[2]);
+    for (i = 0; i < 3; i++)
+	v1[i] = r0[i] - a[i] + (w * r0[i]);
+
+    /* Remove e-terms from proper motion and express as 6-vector */
+    wd = (r0[0] * ad[0]) + (r0[1] * ad[1]) + (r0[2] * ad[2]);
+    for (i = 0; i < 3; i++)
+	v1[i+3] = rd0[i] - ad[i] + (wd * r0[i]);
+
+    /* Alternately: Put proper motion in 6-vector without adding e-terms
+    for (i = 0; i < 3; i++)
+	v1[i+3] = rd0[i]; */
+
+    /* Convert position + velocity vector to FK5 system */
+    for (i = 0; i < 6; i++) {
+	w = zero;
+	for (j = 0; j < 6; j++) {
+	    w += em[i][j] * v1[j];
+	    }
+	v2[i] = w;
+	}
+
+    /* Vector components */
+    x = v2[0];
+    y = v2[1];
+    z = v2[2];
+    xd = v2[3];
+    yd = v2[4];
+    zd = v2[5];
+
+    /* Magnitude of position vector */
+    rxysq = x*x + y*y;
+    rxy = sqrt (rxysq);
+    rxyzsq = rxysq + z*z;
+    rxyz = sqrt (rxyzsq);
+
+    spxy = (x * xd) + (y * yd);
+    spxyz = spxy + (z * zd);
+
+    /* Convert back to spherical coordinates */
+    if (x == zero && y == zero)
+	r2000 = zero;
+    else {
+	r2000 = atan2 (y,x);
+	if (r2000 < zero)
+	    r2000 = r2000 + d2pi;
+	}
+    d2000 = atan2 (z,rxy);
+
+    if (rxy > tiny) {
+	ur = ((x * yd) - (y * xd)) / rxysq;
+	ud = ((zd * rxysq) - (z * spxy)) / (rxyzsq * rxy);
+	}
+
+    if (*parallax > tiny) {
+	*rv = spxyz / (*parallax * rxyz * vf);
+	*parallax = *parallax / rxyz;
+	}
+
+    /* Return results */
+    *ra = raddeg (r2000);
+    *dec = raddeg (d2000);
+    *rapm  = ur / 360000.0;
+    *decpm = ud / 360000.0;
+
+    if (diag) {
+	dra = 240.0 * raddeg (r2000 - r1950);
+	ddec = 3600.0 * raddeg (d2000 - d1950);
+	fprintf(stderr,"J2000-B1950: dra= %11.5f sec  ddec= %f11.5f arcsec\n",
+		dra, ddec);
+	}
+    return;
+}
+
+int	idg=0;
+
+/*  l2,b2 system of galactic coordinates
+ *  p = 192.25       ra of galactic north pole (mean b1950.0)
+ *  q =  62.6        inclination of galactic to mean b1950.0 equator
+ *  r =  33          longitude of ascending node
+ *  p,q,r are degrees
+
+ *  Equatorial to galactic rotation matrix
+    (The Eulerian angles are p, q, 90-r)
+	+cp.cq.sr-sp.cr	+sp.cq.sr+cp.cr	-sq.sr
+	-cp.cq.cr-sp.sr	-sp.cq.cr+cp.sr	+sq.cr
+	cp.sq		+sp.sq		+cq
+ */
+
+static
+double bgal[3][3] =
+	{{-0.066988739415,-0.872755765852,-0.483538914632},
+	{0.492728466075,-0.450346958020, 0.744584633283},
+	{-0.867600811151,-0.188374601723, 0.460199784784}};
+
+/*---  Transform B1950.0 FK4 equatorial coordinates to
+ *     IAU 1958 galactic coordinates */
+
+void
+fk42gal (dtheta,dphi)
+
+double *dtheta;	/* B1950.0 FK4 right ascension in degrees
+		   Galactic longitude (l2) in degrees (returned) */
+double *dphi;	/* B1950.0 FK4 declination in degrees
+		   Galactic latitude (b2) in degrees (returned) */
+
+/*  Input equatorial coordinates are B1950 FK4.
+    Use fk52gal() to convert from j2000.0 coordinates.
+    Reference: Blaauw et al, MNRAS,121,123 (1960) */
+{
+    double pos[3],pos1[3],r,dl,db,rl,rb,rra,rdec,dra,ddec;
+    void v2s3(),s2v3();
+    int i;
+    char *eqcoor, *eqstrn();
+
+    dra = *dtheta;
+    ddec = *dphi;
+    rra = degrad (dra);
+    rdec = degrad (ddec);
+
+    /*  remove e-terms */
+    /*	call jpabe (rra,rdec,-1,idg) */
+
+    /*  Spherical to Cartesian */
+    r = 1.;
+    s2v3 (rra,rdec,r,pos);
+
+    /*  rotate to galactic */
+    for (i = 0; i<3; i++) {
+	pos1[i] = pos[0]*bgal[i][0] + pos[1]*bgal[i][1] + pos[2]*bgal[i][2];
+	}
+
+    /*  Cartesian to spherical */
+    v2s3 (pos1,&rl,&rb,&r);
+
+    dl = raddeg (rl);
+    db = raddeg (rb);
+    *dtheta = dl;
+    *dphi = db;
+
+    /*  Print result if in diagnostic mode */
+    if (idg) {
+	eqcoor = eqstrn (dra,ddec);
+	fprintf (stderr,"FK42GAL: B1950 RA,Dec= %s\n",eqcoor);
+	fprintf (stderr,"FK42GAL: long = %.5f lat = %.5f\n",dl,db);
+	free (eqcoor);
+	}
+
+    return;
+}
+
+
+/*--- Transform IAU 1958 galactic coordinates to B1950.0 'FK4'
+ *    equatorial coordinates */
+
+void
+gal2fk4 (dtheta,dphi)
+
+double *dtheta;	/* Galactic longitude (l2) in degrees
+		   B1950 FK4 RA in degrees (returned) */
+double *dphi;	/* Galactic latitude (b2) in degrees
+		   B1950 FK4 Dec in degrees (returned) */
+
+/*  Output equatorial coordinates are B1950.0 FK4.
+    Use gal2fk5() to convert to J2000 coordinates.
+    Reference:  Blaauw et al, MNRAS,121,123 (1960) */
+
+{
+    double pos[3],pos1[3],r,dl,db,rl,rb,rra,rdec,dra,ddec;
+    void v2s3(),s2v3();
+    char *eqcoor, *eqstrn();
+    int i;
+
+    /*  spherical to cartesian */
+    dl = *dtheta;
+    db = *dphi;
+    rl = degrad (dl);
+    rb = degrad (db);
+    r = 1.0;
+    s2v3 (rl,rb,r,pos);
+
+    /*  rotate to equatorial coordinates */
+    for (i = 0; i < 3; i++) {
+	pos1[i] = pos[0]*bgal[0][i] + pos[1]*bgal[1][i] + pos[2]*bgal[2][i];
+	}
+
+    /*  cartesian to spherical */
+    v2s3 (pos1,&rra,&rdec,&r);
+
+/*  introduce e-terms */
+/*	jpabe (rra,rdec,-1,idg); */
+
+    dra = raddeg (rra);
+    ddec = raddeg (rdec);
+    *dtheta = dra;
+    *dphi = ddec;
+
+    /*  print result if in diagnostic mode */
+    if (idg) {
+	fprintf (stderr,"GAL2FK4: long = %.5f lat = %.5f\n",dl,db);
+	eqcoor = eqstrn (dra,ddec);
+	fprintf (stderr,"GAL2FK4: B1950 RA,Dec= %s\n",eqcoor);
+	free (eqcoor);
+	}
+
+    return;
+}
+
+
+/*  l2,b2 system of galactic coordinates
+    p = 192.25       ra of galactic north pole (mean b1950.0)
+    q =  62.6        inclination of galactic to mean b1950.0 equator
+    r =  33          longitude of ascending node
+    p,q,r are degrees */
+
+/*  Equatorial to galactic rotation matrix
+    The eulerian angles are p, q, 90-r
+	+cp.cq.sr-sp.cr     +sp.cq.sr+cp.cr     -sq.sr
+	-cp.cq.cr-sp.sr     -sp.cq.cr+cp.sr     +sq.cr
+	+cp.sq              +sp.sq              +cq		*/
+
+static
+double jgal[3][3] =
+	{{-0.054875539726,-0.873437108010,-0.483834985808},
+	{0.494109453312,-0.444829589425, 0.746982251810},
+	{-0.867666135858,-0.198076386122, 0.455983795705}};
+
+/* Transform J2000 equatorial coordinates to IAU 1958 galactic coordinates */
+
+void
+fk52gal (dtheta,dphi)
+
+double *dtheta;	/* J2000 right ascension in degrees
+		   Galactic longitude (l2) in degrees (returned) */
+double *dphi;	/* J2000 declination in degrees
+		   Galactic latitude (b2) in degrees (returned) */
+
+/* Rotation matrices by P.T.Wallace, Starlink eqgal and galeq, March 1986 */
+
+/*  Input equatorial coordinates are J2000 FK5.
+    Use gal2fk4() if converting from B1950 FK4 coordinates.
+    Reference: Blaauw et al, MNRAS,121,123 (1960) */
+{
+    double pos[3],pos1[3],r,dl,db,rl,rb,rra,rdec,dra,ddec;
+    void v2s3(),s2v3();
+    char *eqcoor, *eqstrn();
+    int i;
+
+    /*  Spherical to cartesian */
+    dra = *dtheta;
+    ddec = *dphi;
+    rra = degrad (dra);
+    rdec = degrad (ddec);
+    r = 1.0;
+    (void)s2v3 (rra,rdec,r,pos);
+
+    /*  Rotate to galactic */
+    for (i = 0; i < 3; i++) {
+	pos1[i] = pos[0]*jgal[i][0] + pos[1]*jgal[i][1] + pos[2]*jgal[i][2];
+	}
+
+    /*  Cartesian to spherical */
+    v2s3 (pos1,&rl,&rb,&r);
+
+    dl = raddeg (rl);
+    db = raddeg (rb);
+    *dtheta = dl;
+    *dphi = db;
+
+    /*  Print result if in diagnostic mode */
+    if (idg) {
+	eqcoor = eqstrn (dra,ddec);
+	fprintf (stderr,"FK52GAL: J2000 RA,Dec= %s\n",eqcoor);
+	fprintf (stderr,"FK52GAL: long = %.5f lat = %.5f\n",dl,db);
+	free (eqcoor);
+	}
+
+    return;
+}
+
+
+/*--- Transform IAU 1958 galactic coordinates to J2000 equatorial coordinates */
+
+void
+gal2fk5 (dtheta,dphi)
+
+double *dtheta;	/* Galactic longitude (l2) in degrees
+		   J2000.0 ra in degrees (returned) */
+double *dphi;	/* Galactic latitude (b2) in degrees
+		   J2000.0 dec in degrees (returned) */
+
+/*  Output equatorial coordinates are J2000.
+   Use gal2fk4() to convert to B1950 coordinates.
+    Reference: Blaauw et al, MNRAS,121,123 (1960) */
+
+{
+    double pos[3],pos1[3],r,dl,db,rl,rb,rra,rdec,dra,ddec;
+    void v2s3(),s2v3();
+    int i;
+    char *eqcoor, *eqstrn();
+
+    /*  Spherical to Cartesian */
+    dl = *dtheta;
+    db = *dphi;
+    rl = degrad (dl);
+    rb = degrad (db);
+    r = 1.0;
+    s2v3 (rl,rb,r,pos);
+
+    /*  Rotate to equatorial coordinates */
+    for (i = 0; i < 3; i++) {
+	    pos1[i] = pos[0]*jgal[0][i] + pos[1]*jgal[1][i] + pos[2]*jgal[2][i];
+	    }
+
+    /*  Cartesian to Spherical */
+    v2s3 (pos1,&rra,&rdec,&r);
+    dra = raddeg (rra);
+    ddec = raddeg (rdec);
+    *dtheta = dra;
+    *dphi = ddec;
+
+    /*  Print result if in diagnostic mode */
+    if (idg) {
+	fprintf (stderr,"GAL2FK5: long = %.5f lat = %.5f\n",dl,db);
+	eqcoor = eqstrn (dra,ddec);
+	fprintf (stderr,"GAL2FK5: J2000 RA,Dec= %s\n",eqcoor);
+	free (eqcoor);
+	}
+
+    return;
+}
+
+
+/* Return string with right ascension in hours and declination in degrees */
+
+char *eqstrn (dra, ddec)
+
+double	dra;		/* Right ascension in degrees */
+double	ddec;		/* Declination in degrees */
+
+{
+char	*eqcoor;	/* ASCII character string of position (returned) */
+char	decp;
+int	rah,irm,decd,decm;
+double	xpos,ypos,xp,yp,ras,decs;
+
+    /*  Right ascension to hours, minutes, and seconds */
+    xpos = dra / 15.0;
+    rah = (int) xpos;
+    xp = (double) 60.0 * (xpos - (double) rah);
+    irm = (int) xp;
+    ras = (double) 60.0 * (xp - (double) irm);
+
+    /* Declination to degrees, minutes, seconds */
+    if (ddec < 0) {
+	ypos = -ddec;
+	decp = '-';
+	}
+    else {
+	decp = '+';
+	ypos = ddec;
+	}
+    decd = (int) ypos;
+    yp = (double) 60.0 * (ypos - (double) decd);
+    decm = (int) yp;
+    decs = (double) 60.0 * (yp - (double) decm);
+
+    eqcoor = malloc (32);
+    (void)sprintf (eqcoor,"%02d:%02d:%06.3f %c%02d:%02d:%05.2f",
+		   rah,irm,ras,decp,decd,decm,decs);
+    if (eqcoor[6] == ' ')
+	eqcoor[6] = '0';
+    if (eqcoor[20] == ' ')
+	eqcoor[20] = '0';
+
+    return (eqcoor);
+}
+
+
+/* Convert geocentric equatorial rectangular coordinates to
+   right ascension and declination, and distance */
+
+
+/* These routines are based on similar ones in Pat Wallace's slalib package */
+
+/* Convert B1950 right ascension and declination to ecliptic coordinates */
+
+void
+fk42ecl (dtheta, dphi, epoch)
+
+double *dtheta;	/* B1950 right ascension in degrees
+		   Galactic longitude (l2) in degrees (returned) */
+double *dphi;	/* B1950 declination in degrees
+		   Galactic latitude (b2) in degrees (returned) */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    void fk425e(), fk52ecl();
+
+    /* Convert from B1950 to J2000 coordinates */
+    fk425e (dtheta, dphi, epoch);
+
+    /* Convert from J2000 to ecliptic coordinates */
+    fk52ecl (dtheta, dphi, epoch);
+
+    return;
+}
+
+/* Convert J2000 right ascension and declination to ecliptic coordinates */
+
+void
+fk52ecl (dtheta, dphi, epoch)
+
+double *dtheta;	/* J2000 right ascension in degrees
+		   Galactic longitude (l2) in degrees (returned) */
+double *dphi;	/* J2000 declination in degrees
+		   Galactic latitude (b2) in degrees (returned) */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    int i, j;
+    double t, eps0, rphi, rtheta;
+    double v1[3], v2[3], r;
+    double rmat[9], *rmati;	/* Rotation matrix  */
+
+    void rotmat(), v2s3(), s2v3(), fk5prec();
+
+    /* Precess coordinates from J2000 to epoch */
+    if (epoch != 2000.0)
+	fk5prec (2000.0, epoch, dtheta, dphi);
+
+    /* Convert from degrees to radians */
+    rtheta = degrad (*dtheta);
+    rphi = degrad (*dphi);
+
+    /* Convert RA,Dec to x,y,z */
+    r = 1.0;
+    s2v3 (rtheta, rphi, r, v1);
+
+    /* Interval between basic epoch J2000.0 and current epoch (JC) in centuries*/
+    t = (epoch - 2000.0) * 0.01;
+ 
+    /* Mean obliquity */
+    eps0 = secrad ((84381.448 + (-46.8150 + (-0.00059 + 0.001813*t) * t) * t));
+ 
+    /* Form the equatorial to ecliptic rotation matrix (IAU 1980 theory).
+     *  References: Murray, C.A., Vectorial Astrometry, section 4.3.
+     *    The matrix is in the sense   v[ecl]  =  rmat * v[equ];  the
+     *    equator, equinox and ecliptic are mean of date. */
+    rotmat (1, eps0, 0.0, 0.0, rmat);
+
+    /* Multiply position vector by equatoria to eccliptic rotation matrix */
+    rmati = rmat;
+    for (i = 0; i < 3; i++) {
+	v2[i] = 0;
+	for (j = 0; j < 3; j++)
+	    v2[i] = v2[i] + (*rmati++ * v1[j]);
+	}
+
+    /* Convert x,y,z to latitude, longitude */
+    v2s3 (v2, &rtheta, &rphi, &r);
+
+    /* Convert from radians to degrees */
+    *dtheta = raddeg (rtheta);
+    *dphi = raddeg (rphi);
+}
+
+
+/* Convert ecliptic coordinates to B1950 right ascension and declination */
+
+void
+ecl2fk4 (dtheta, dphi, epoch)
+
+double *dtheta;	/* Galactic longitude (l2) in degrees
+		   B1950 right ascension in degrees (returned) */
+double *dphi;	/* Galactic latitude (b2) in degrees
+		   B1950 declination in degrees (returned) */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    void ecl2fk5(), fk524e();
+
+    /* Convert from ecliptic to J2000 coordinates */
+    ecl2fk5 (dtheta, dphi, epoch);
+
+    /* Convert from J2000 to B1950 coordinates */
+    fk524e (dtheta, dphi, epoch);
+
+    return;
+}
+
+
+
+/* Convert ecliptic coordinates to J2000 right ascension and declination */
+
+void
+ecl2fk5 (dtheta, dphi, epoch)
+
+double *dtheta;	/* Galactic longitude (l2) in degrees
+		   J2000 right ascension in degrees  (returned) */
+double *dphi;	/* Galactic latitude (b2) in degrees
+		   J2000 declination in degrees (returned) */
+double	epoch;	/* Besselian epoch in years */
+
+{
+    int i, j;
+    double rtheta, rphi, v1[3], v2[3];
+    double t, eps0, r;
+    double rmat[9];	/* Rotation matrix */
+    void v2s3(),s2v3(), fk5prec(), rotmat();
+
+    rtheta = degrad (*dtheta);
+    rphi = degrad (*dphi);
+
+    /* Convert RA,Dec to x,y,z */
+    r = 1.0;
+    s2v3 (rtheta, rphi, r, v1);
+
+    /* Interval between basic epoch J2000.0 and current epoch (JC) in centuries*/
+    t = (epoch - 2000.0) * 0.01;
+ 
+    /* Mean obliquity */
+    eps0 = secrad ((84381.448 + (-46.8150 + (-0.00059 + 0.001813*t) * t) * t));
+ 
+    /* Form the equatorial to ecliptic rotation matrix (IAU 1980 theory).
+     *  References: Murray, C.A., Vectorial Astrometry, section 4.3.
+     *    The matrix is in the sense   v[ecl]  =  rmat * v[equ];  the
+     *    equator, equinox and ecliptic are mean of date. */
+    rotmat (1, eps0, 0.0, 0.0, rmat);
+ 
+    /* Multiply position vector by ecliptic to equatorial rotation matrix */
+    for (i = 0; i < 3; i++) {
+	v2[i] = 0;
+	for (j = 0; j < 3; j++)
+	    v2[i] = v2[i] + (rmat[3*j + i] * v1[j]);
+	}
+
+    /* Cartesian to spherical */
+    v2s3 (v2, &rtheta, &rphi, &r);
+
+    /* Convert from radians to degrees */
+    *dtheta = raddeg (rtheta);
+    *dphi = raddeg (rphi);
+
+    if (epoch != 2000.0)
+	fk5prec (epoch, 2000.0, dtheta, dphi);
+}
+
+
+/* The following routines are modified from Patrick Wallace's SLALIB */
+
+/* Precess coordinates between epochs in FK4 */
+void
+fk4prec (ep0, ep1, ra, dec)
+
+double ep0;	/* Starting Besselian epoch */
+double ep1;	/* Ending Besselian epoch */
+double *ra;	/* RA in degrees mean equator & equinox of epoch ep0
+		      mean equator & equinox of epoch ep1 (returned) */
+double *dec;	/* Dec in degrees mean equator & equinox of epoch ep0
+		       mean equator & equinox of epoch ep1 (returned) */
+/*
+**  Precession - FK4 (Bessel-Newcomb, pre-IAU1976)
+**
+**  This routine will not correctly convert between FK4 and FK5
+**  For output in FK5, precess to 1950.0 and use fk425() on result.
+**
+**  Based on slaPreces(), P.T.Wallace   Starlink   22 December 1993
+*/
+{
+    int i, j;
+    double pm[9], *pmi, v1[3], v2[3], rra, rdec, r;
+    void v2s3(),s2v3(), mprecfk4();
+
+    rra = degrad (*ra);
+    rdec = degrad (*dec);
+    r = 1.0;
+ 
+    /* Generate appropriate precession matrix */
+    mprecfk4 ( ep0, ep1, pm );
+ 
+    /* Convert RA,Dec to x,y,z */
+    s2v3 (rra, rdec, r, v1);
+ 
+    /* Multiply position vector by precession matrix */
+    pmi = pm;
+    for (i = 0; i < 3; i++) {
+	v2[i] = 0;
+	for (j = 0; j < 3; j++)
+	    v2[i] = v2[i] + (*pmi++ * v1[j]);
+	}
+ 
+    /* Back to RA,Dec */
+    v2s3 (v2, &rra, &rdec, &r);
+
+    /* Convert from radians to degrees */
+    *ra = raddeg (rra);
+    *dec = raddeg (rdec);
+}
+
+void
+fk5prec (ep0, ep1, ra, dec)
+
+double ep0;	/* Starting epoch */
+double ep1;	/* Ending epoch */
+double *ra;	/* RA in degrees mean equator & equinox of epoch ep0
+		      mean equator & equinox of epoch ep1 (returned) */
+double *dec;	/* Dec in degrees mean equator & equinox of epoch ep0
+		       mean equator & equinox of epoch ep1 (returned) */
+/*
+**  Precession -  FK5 (Fricke, post-IAU2000)
+**
+**  This routine will not correctly convert between FK5 and FK4.
+**  For output in FK4, precess to 2000.0 and use fk524() on result.
+*/
+{
+    void fk5ep2j(), fk5j2ep();
+
+    fk5ep2j (ep0, ra, dec);
+    fk5j2ep (ep1, ra, dec);
+
+    return;
+}
+
+
+void
+mprecfk4 (bep0, bep1, rmatp)
+
+double bep0;		/* Beginning Besselian epoch */
+double bep1;		/* Ending Besselian epoch */
+double rmatp[9];	/* 3x3 Precession matrix (returned) */
+
+/*
+**  Generate the matrix of precession between two epochs,
+**  using the old, pre-IAU1976, Bessel-Newcomb model, using
+**  Kinoshita's formulation (double precision)
+**
+**  The matrix is in the sense   v(bep1)  =  rmatp * v(bep0)
+**
+**  Reference:
+**     Kinoshita, H. (1975) 'Formulas for precession', SAO Special
+**     Report No. 364, Smithsonian Institution Astrophysical
+**     Observatory, Cambridge, Massachusetts.
+**
+**  Based on slaPrebn() by P.T.Wallace   Starlink   30 October 1993
+*/
+{
+    double bigt, t, tas2r, w, zeta, z, theta;
+    void rotmat();
+ 
+    /* Interval between basic epoch B1850.0 and beginning epoch in TC */
+    bigt  = ( bep0 - 1850.0 ) / 100.0;
+ 
+    /* Interval over which precession required, in tropical centuries */
+    t = ( bep1 - bep0 ) / 100.0;
+ 
+    /* Euler angles */
+    tas2r = secrad (t);
+    w = 2303.5548 + ( 1.39720 + 0.000059 * bigt ) * bigt;
+    zeta = (w + ( 0.30242 - 0.000269 * bigt + 0.017996 * t ) * t ) * tas2r;
+    z = (w + ( 1.09478 + 0.000387 * bigt + 0.018324 * t ) * t ) * tas2r;
+    theta = ( 2005.1125 + ( - 0.85294 - 0.000365* bigt ) * bigt +
+	    ( - 0.42647 - 0.000365 * bigt - 0.041802 * t ) * t ) * tas2r;
+ 
+    /* Rotation matrix */
+    rotmat (323, -zeta, theta, -z, rmatp);
+    return;
+}
+
+
+void
+mprecfk5 (ep0, ep1, rmatp)
+
+double ep0;		/* Beginning epoch */
+double ep1;		/* Ending epoch */
+double rmatp[9];	/* 3x3 Precession matrix (returned) */
+
+/*
+**  Form the matrix of precession between two epochs (IAU 1976, FK5).
+**  Notes:
+**  1)  The epochs are TDB (loosely ET) Julian epochs.
+**  2)  The matrix is in the sense   v(ep1)  =  rmatp * v(ep0) .
+**
+**  References:
+**     Lieske,J.H., 1979. Astron. Astrophys.,73,282.
+**          equations (6) & (7), p283.
+**     Kaplan,G.H., 1981. USNO circular no. 163, pa2.
+**
+**  Based on slaPrec(), P.T.Wallace   Starlink   31 October 1993
+*/
+{
+    double t0, t, tas2r, w, zeta, z, theta;
+    void rotmat();
+
+    /* Interval in Julian centuries  between J2000.0 and beginning epoch */
+    t0 = ( ep0 - 2000.0 ) / 100.0;
+ 
+    /* Interval over which precession required (JC) */
+    t =  ( ep1 - ep0 ) / 100.0;
+ 
+    /* Euler angles */
+    tas2r = secrad (t);
+    w = 2306.2181 + ( ( 1.39656 - ( 0.000139 * t0 ) ) * t0 );
+    zeta = (w + ( ( 0.30188 - 0.000344 * t0 ) + 0.017998 * t ) * t ) * tas2r;
+    z = (w + ( ( 1.09468 + 0.000066 * t0 ) + 0.018203 * t ) * t ) * tas2r;
+    theta = ( ( 2004.3109 + ( - 0.85330 - 0.000217 * t0 ) * t0 )
+	  + ( ( -0.42665 - 0.000217 * t0 ) - 0.041833 * t ) * t ) * tas2r;
+ 
+    /* Rotation matrix */
+    rotmat (323, -zeta, theta, -z, rmatp);
+    return;
+}
+
+
+/* Precess coordinates to J2000 in FK5 */
+
+void
+fk5ep2j (ep, ra, dec)
+
+double ep;	/* Starting Julian epoch */
+double *ra;	/* RA in degrees mean equator & equinox of epoch ep0
+		      mean equator & equinox of epoch ep1 (returned) */
+double *dec;	/* Dec in degrees mean equator & equinox of epoch ep0
+		       mean equator & equinox of epoch ep1 (returned) */
+/*
+**  Precession -  FK5 (Fricke, post-IAU2000)
+**
+**  This routine will not correctly convert between FK5 and FK4.
+**  For output in FK4, precess to 2000.0 and use fk524() on result.
+**
+**  Based on slaPreces(), P.T.Wallace   Starlink   22 December 1993
+*/
+{
+    int i, j;
+    double pm[9], pmt[9], *pmi, v1[3], v2[3], rra, rdec, r;
+    void v2s3(),s2v3(), mfk5j2ep();
+
+    rra = degrad (*ra);
+    rdec = degrad (*dec);
+    r = 1.0;
+ 
+    /* Convert RA,Dec to x,y,z */
+    s2v3 (rra, rdec, r, v1);
+ 
+    /* Generate and transpose precession matrix */
+    mfk5j2ep ( ep, pm );
+    for (i = 0; i < 3; i++) {
+	for (j = 0; j < 3; j++) {
+	    pmt[i+(j*3)] = pm[j+(i*3)];
+	    }
+	}
+ 
+    /* Multiply position vector by precession matrix */
+    pmi = pmt;
+    for (i = 0; i < 3; i++) {
+	v2[i] = 0;
+	for (j = 0; j < 3; j++)
+	    v2[i] = v2[i] + (*pmi++ * v1[j]);
+	}
+ 
+    /* Back to RA,Dec */
+    v2s3 (v2, &rra, &rdec, &r);
+
+    /* Convert from radians to degrees */
+    *ra = raddeg (rra);
+    *dec = raddeg (rdec);
+}
+
+
+/* Precess coordinates from J2000 in FK5 */
+
+void
+fk5j2ep (ep, ra, dec)
+
+double ep;	/* Starting Julian epoch */
+double *ra;	/* RA in degrees mean equator & equinox of epoch ep0
+		      mean equator & equinox of epoch ep1 (returned) */
+double *dec;	/* Dec in degrees mean equator & equinox of epoch ep0
+		       mean equator & equinox of epoch ep1 (returned) */
+/*
+**  Precession -  FK5 (Fricke, post-IAU2000)
+**
+**  This routine will not correctly convert between FK5 and FK4.
+**  For output in FK4, precess to 2000.0 and use fk524() on result.
+**
+**  Based on slaPreces(), P.T.Wallace   Starlink   22 December 1993
+*/
+{
+    int i, j;
+    double pm[9], *pmi, v1[3], v2[3], rra, rdec, r;
+    void v2s3(), s2v3(), mfk5j2ep();
+
+    rra = degrad (*ra);
+    rdec = degrad (*dec);
+    r = 1.0;
+ 
+    /* Convert RA,Dec to x,y,z */
+    s2v3 (rra, rdec, r, v1);
+ 
+    /* Generate precession matrix */
+    mfk5j2ep ( ep, pm );
+ 
+    /* Multiply position vector by precession matrix */
+    pmi = pm;
+    for (i = 0; i < 3; i++) {
+	v2[i] = 0;
+	for (j = 0; j < 3; j++)
+	    v2[i] = v2[i] + (*pmi++ * v1[j]);
+	}
+ 
+    /* Back to RA,Dec */
+    v2s3 (v2, &rra, &rdec, &r);
+
+    /* Convert from radians to degrees */
+    *ra = raddeg (rra);
+    *dec = raddeg (rdec);
+}
+
+
+void
+mfk5j2ep (ep, rmatp)
+
+double ep;		/* Beginning epoch */
+double rmatp[9];	/* 3x3 Precession matrix (returned) */
+
+/*
+**  Form the matrix of precession between two epochs (IAU 2000, FK5).
+**  Notes:
+**  1)  The epochs are TDB (loosely ET) Julian epochs.
+**  2)  The matrix is in the sense   v(ep)  =  rmatp * v(J2000) .
+**
+**  References:
+**     Lieske,J.H., 1979. Astron. Astrophys.,73,282.
+**          equations (6) & (7), p283.
+**     Kaplan,G.H., 2005. USNO circular no. 179, p. 44.
+**
+**  Based on slaPrec(), P.T.Wallace   Starlink   31 October 1993
+*/
+{
+    double t, t2, t3, t4, tas2r, zeta, z, theta;
+    void rotmat();
+ 
+    /* Interval in Julian centuries  between J2000.0 and beginning epoch */
+    t = ( ep - 2000.0 ) / 100.0;
+    t2 = t * t;
+    t3 = t2 * t;
+    t4 = t3 * t;
+ 
+    /* Euler angles */
+    tas2r = secrad (t);
+    zeta = 2.65045 + (2306.083227 + (0.2988499 * t) + (0.01801828 * t2)
+	  - (0.000005971 * t3) - (0.0000003173 * t4)) * tas2r;
+    z = -2.650545 + (2306.077181 + (1.0927348 * t) + (0.01801828 * t2)
+	   - (0.000005971 * t3) - (0.0000003173 * t4)) * tas2r;
+    theta = (2004.191903 - (0.4294934 * t) - (0.04182264 * t2)
+	    - (0.000007089 * t3) - (0.0000001274 * t4)) * tas2r;
+ 
+    /* Rotation matrix */
+    rotmat (323, -z, theta, -zeta, rmatp);
+    return;
+}
+
+
+/* Make 3-D rotation matrix from up to three rotations */
+
+void
+rotmat (axes, rot1, rot2, rot3, matrix)
+
+int axes;	/* Axes about which coordinates are rotated (1=x, 2=y, 3=z) */
+double rot1;	/* First rotation in degrees */
+double rot2;	/* Second rotation in degrees */
+double rot3;	/* Third rotation in degrees */
+double *matrix;	/* 3x3 rotation matrix (returned) */
+
+{
+    int i, j, k, naxis, iaxes, iaxis;
+    double rot, srot, crot, *mati, w, wm[9], *wmi, matn[9];
+    int axis[3];
+
+    /* Initial final rotation matrix */
+    mati = matrix;
+    for (i = 0; i < 3; i++) {
+	for (j=0; j < 3; j++) {
+	    if (i == j)
+		*mati++ = 1.0;
+	    else
+		*mati++ = 0.0;
+	    }
+	}
+
+    /* Separate digits of rotation axis string and count rotations */
+    naxis = 0;
+    iaxes = axes;
+    axis[0] = iaxes / 100;
+    if (axis[0] > 0) {
+	naxis++;
+	iaxes = iaxes - (100 * axis[0]);
+	}
+    axis[naxis] = iaxes / 10;
+    if (axis[naxis] > 0) {
+	iaxes = iaxes - (10 * axis[naxis]);
+	naxis++;
+	}
+    axis[naxis] = iaxes;
+    if (axis[naxis] > 0)
+	naxis++;
+
+    /* For each digit of axis string, set up matrix */
+    for (iaxis = 0; iaxis < naxis; iaxis++) {
+
+	/* Initialize current rotation matrix */
+	mati = matn;
+	for (i = 0; i < 3; i++) {
+	    for (j=0; j < 3; j++) {
+		if (i == j)
+		    *mati++ = 1.0;
+		else
+		    *mati++ = 0.0;
+		}
+	    }
+
+	/* Select rotation angle from argument list */
+	if (axis[iaxis] == 1)
+	    rot = rot1;
+	else if (axis[iaxis] == 2)
+	    rot = rot2;
+	else
+	    rot = rot3;
+	srot = sin (rot);
+	crot = cos (rot);
+	
+	/* Matrix for rotation in X */
+	if (axis[iaxis] == 1) {
+	    matn[4] = crot;
+	    matn[5] = srot;
+	    matn[7] = -srot;
+	    matn[8] = crot;
+	    }
+	
+	/* Matrix for rotation in Y */
+	else if (axis[iaxis] == 2) {
+	    matn[0] = crot;
+	    matn[2] = -srot;
+	    matn[6] = srot;
+	    matn[8] = crot;
+	    }
+	
+	/* Matrix for rotation in Z */
+	else {
+	    matn[0] = crot;
+	    matn[1] = srot;
+	    matn[3] = -srot;
+	    matn[4] = crot;
+	    }
+
+	/* Multiply existing rotation matrix by new rotation matrix */
+	for (i = 0; i < 3; i++) {
+	    for (j = 0; j < 3; j++) {
+		w = 0.0;
+		for (k = 0; k < 3; k++)
+		    w+= matn[3*i + k] * matrix[3*k + j];
+		wm[3*i + j] = w;
+		}
+	    }
+
+	/* Update output matrix */
+	mati = matrix;
+	wmi = wm;
+	for (i = 0; i < 9; i++) {
+	    *mati++ = *wmi++;
+	    }
+	}
+    return;
+}
+
+
+/* The following routines are from Doug Mink's Fortran ephemeris library */
+
+/* Convert right ascension, declination, and distance to
+   geocentric equatorial rectangular coordinates */
+
+void
+s2v3 (rra,rdec,r,pos)
+
+double rra;	/* Right ascension in radians */
+double rdec;	/* Declination in radians */
+double r;	/* Distance to object in same units as pos */
+double pos[3];	/* x,y,z geocentric equatorial position of object (returned) */
+{
+    pos[0] = r * cos (rra) * cos (rdec);
+    pos[1] = r * sin (rra) * cos (rdec);
+    pos[2] = r * sin (rdec);
+
+    return;
+}
+
+/* Convert geocentric equatorial rectangular coordinates to
+   right ascension, declination, and distance */
+
+void
+v2s3 (pos,rra,rdec,r)
+
+double pos[3];	/* x,y,z geocentric equatorial position of object */
+double *rra;	/* Right ascension in radians (returned) */
+double *rdec;	/* Declination in radians (returned) */
+double *r;	/* Distance to object in same units as pos (returned) */
+
+{
+    double x,y,z,rxy,rxy2,z2;
+
+    x = pos[0];
+    y = pos[1];
+    z = pos[2];
+
+    *rra = atan2 (y, x);
+
+    /* Keep RA within 0 to 2pi range */
+    if (*rra < 0.0)
+	*rra = *rra + (2.0 * PI);
+    if (*rra > 2.0 * PI)
+	*rra = *rra - (2.0 * PI);
+
+    rxy2 = x*x + y*y;
+    rxy = sqrt (rxy2);
+    *rdec = atan2 (z, rxy);
+
+    z2 = z * z;
+    *r = sqrt (rxy2 + z2);
+
+    return;
+}
+
+/*
+ * Nov  6 1995	Include stdlib.h instead of malloc.h
+ * Apr  1 1996	Add arbitrary epoch precession
+ * Apr 26 1996	Add FK4 <-> FK5 subroutines for use when epoch is known
+ * Aug  6 1996	Clean up after lint
+ * Nov  4 1996	Break SLA subroutines into separate file slasubs.c
+ * Dec  9 1996	Change arguments to degrees in FK4 and FK5 precession programs
+ * Dec 10 1996	All subroutine arguments are degrees except vector conversions
+ *
+ * Mar 20 1997	Drop unused variables after lint
+ *
+ * Apr 14 1998	Add ecliptic coordinate conversions and general conversion routines
+ * Apr 23 1998	Add LINEAR coordinate system
+ * Apr 28 1998	Change coordinate system flags to WCS_*
+ * Apr 28 1998	Return -1 from wcscsys if not a legal coordinate system
+ * May  7 1998	Keep theta within 0 to 2pi in ecl2fk5()
+ * May 13 1998	Add wcsceq()
+ * May 13 1998	Add equinox arguments to wcscon()
+ * Jun 24 1998	Set J2000 from ICRS in wcscsys()
+ * Jul  9 1998	Include stdio.h for fprintf() and sprintf() declarations
+ * Sep 17 1998	Add wcscstr() to get coordinate string
+ * Sep 21 1998	Fix bug in wcscstr() which returned B2000 instead of J2000
+ * Sep 21 1998	Add subroutine to convert proper motions, too.
+ * Oct 21 1998	In wcscstr(), drop .00 from returned string
+ * Nov 18 1998	Rename jpcop() v2s3() and jpcon() s2v3() (spherical to vector)
+ * Dec  2 1998	Add PLANET coordinate system to wcscsys() and wcscstr()
+ *
+ * Mar 10 2000	Precess coordinates correctly from other than 1950.0 and 2000.0
+ * Mar 10 2000	Set coordinate system to J2000 or B1950 if string is numeric
+ * Mar 14 2000	Clean up code in fk524m() and fk425m()
+ * May 31 2000	Add proper motion correctly if proper motion precessed
+ * Jun 26 2000	Add some support for WCS_XY image coordinates
+ * Sep 14 2000	Return -1 from wcscsys if equinox is less than 1900.0
+ * Oct 31 2000	Add proper motion after fk425 or fk524 from system epoch
+ * Oct 31 2000	Fix proper motion units in fk524p() and fk425p()
+ * Nov  6 2000	Update fk425 and fk524 algorithms to include parallax and rv
+ *
+ * Jan 11 2001	Print all messages to stderr
+ * Mar 21 2001	Move braces around bgal[] and jgal[] matrix initialization
+ *
+ * Feb 13 2002	Fix precession units problem in ecl2fk5() and fk52ecl()
+ *
+ * Apr 13 2005	Replace all sla_lib calls with local code
+ * Nov  1 2005	Add WCS_ICRS, and unprecessable system
+ *
+ * Jan  5 2006	Fix bugs in precession subroutines mprecxxx()
+ * May  3 2006	Drop declarations of unused variables suggested by Robert Lupton
+ * Oct  6 2006	If pixel coordinates, set system to WCS_XY in wcscsys()
+ * Oct 30 2006	Add LINEAR and ICRS to wcscstr() returns
+ * Nov 29 2006	Rewrite FK5 precession subroutines to IAU 2000 standard
+ */
diff --git a/Code/src/libwcs/wcsinit.c b/Code/src/libwcs/wcsinit.c
new file mode 100644
index 0000000000000000000000000000000000000000..1ff99435e6d91a9f0944f8bacd3077fde5a6eddf
--- /dev/null
+++ b/Code/src/libwcs/wcsinit.c
@@ -0,0 +1,1383 @@
+/*** File libwcs/wcsinit.c
+ *** March 24, 2009
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** Copyright (C) 1998-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+
+ * Module:	wcsinit.c (World Coordinate Systems)
+ * Purpose:	Convert FITS WCS to pixels and vice versa:
+ * Subroutine:	wcsinit (hstring) sets a WCS structure from an image header
+ * Subroutine:	wcsninit (hstring,lh) sets a WCS structure from fixed-length header
+ * Subroutine:	wcsinitn (hstring, name) sets a WCS structure for specified WCS
+ * Subroutine:	wcsninitn (hstring,lh, name) sets a WCS structure for specified WCS
+ * Subroutine:	wcsinitc (hstring, mchar) sets a WCS structure if multiple
+ * Subroutine:	wcsninitc (hstring,lh,mchar) sets a WCS structure if multiple
+ * Subroutine:	wcschar (hstring, name) returns suffix for specifed WCS
+ * Subroutine:	wcseq (hstring, wcs) set radecsys and equinox from image header
+ * Subroutine:	wcseqm (hstring, wcs, mchar) set radecsys and equinox if multiple
+ */
+
+#include <string.h>		/* strstr, NULL */
+#include <stdio.h>		/* stderr */
+#include <math.h>
+#include "wcs.h"
+#ifndef VMS
+#include <stdlib.h>
+#endif
+
+static void wcseq();
+static void wcseqm();
+static void wcsioset();
+void wcsrotset();
+char wcschar();
+
+/* set up a WCS structure from a FITS image header lhstring bytes long
+ * for a specified WCS name */
+
+struct WorldCoor *
+wcsninitn (hstring, lhstring, name)
+
+const char *hstring;	/* character string containing FITS header information
+            in the format <keyword>= <value> [/ <comment>] */
+int	lhstring;	/* Length of FITS header in bytes */
+const char *name;		/* character string with identifying name of WCS */
+{
+    hlength (hstring, lhstring);
+    return (wcsinitn (hstring, name));
+}
+
+
+/* set up a WCS structure from a FITS image header for specified WCSNAME */
+
+struct WorldCoor *
+wcsinitn (hstring, name)
+
+const char *hstring;	/* character string containing FITS header information
+               in the format <keyword>= <value> [/ <comment>] */
+const char *name;		/* character string with identifying name of WCS */
+{
+    char mchar;		/* Suffix character for one of multiple WCS */
+
+    mchar = wcschar (hstring, name);
+    if (mchar == '_') {
+    fprintf (stderr, "WCSINITN: WCS name %s not matched in FITS header\n",
+         name);
+    return (NULL);
+    }
+    return (wcsinitc (hstring, &mchar));
+}
+
+
+/* WCSCHAR -- Find the letter for a specific WCS conversion */
+
+char
+wcschar (hstring, name)
+
+const char *hstring;	/* character string containing FITS header information
+            in the format <keyword>= <value> [/ <comment>] */
+const char *name;		/* Name of WCS conversion to be matched
+               (case-independent) */
+{
+    char *upname, *uppercase();
+    char cwcs, charwcs;
+    int iwcs;
+    char keyword[12];
+    char *upval, value[72];
+
+    /* If no WCS character, return 0 */
+    if (name == NULL)
+    return ((char) 0);
+
+    /* Convert input name to upper case */
+    upname = uppercase (name);
+
+    /* If single character name, return that character */
+    if (strlen (upname) == 1)
+    return (upname[0]);
+
+    /* Try to match input name to available WCSNAME names in header */
+    strcpy (keyword, "WCSNAME");
+    keyword[8] = (char) 0;
+    charwcs = '_';
+    for (iwcs = 0; iwcs < 27; iwcs++) {
+    if (iwcs > 0)
+        cwcs = (char) (64 + iwcs);
+    else
+        cwcs = (char) 0;
+    keyword[7] = cwcs;
+    if (hgets (hstring, keyword, 72, value)) {
+        upval = uppercase (value);
+        if (!strcmp (upval, upname))
+        charwcs = cwcs;
+        free (upval);
+        }
+    }
+    free (upname);
+    return (charwcs);
+}
+
+
+/* Make string of arbitrary case all uppercase */
+
+char *
+uppercase (string)
+char *string;
+{
+    int lstring, i;
+    char *upstring;
+
+    lstring = strlen (string);
+    upstring = (char *) calloc (1,lstring+1);
+    for (i = 0; i < lstring; i++) {
+    if (string[i] > 96 && string[i] < 123)
+        upstring[i] = string[i] - 32;
+    else
+        upstring[i] = string[i];
+    }
+    upstring[lstring] = (char) 0;
+    return (upstring);
+}
+
+
+/* set up a WCS structure from a FITS image header lhstring bytes long */
+
+struct WorldCoor *
+wcsninit (hstring, lhstring)
+
+const char *hstring;	/* character string containing FITS header information
+            in the format <keyword>= <value> [/ <comment>] */
+int	lhstring;	/* Length of FITS header in bytes */
+{
+    char mchar;		/* Suffix character for one of multiple WCS */
+    mchar = (char) 0;
+    hlength (hstring, lhstring);
+    return (wcsinitc (hstring, &mchar));
+}
+
+
+/* set up a WCS structure from a FITS image header lhstring bytes long */
+
+struct WorldCoor *
+wcsninitc (hstring, lhstring, mchar)
+
+const char *hstring;	/* character string containing FITS header information
+            in the format <keyword>= <value> [/ <comment>] */
+int	lhstring;	/* Length of FITS header in bytes */
+char	*mchar;		/* Suffix character for one of multiple WCS */
+{
+    hlength (hstring, lhstring);
+    if (mchar[0] == ' ')
+    mchar[0] = (char) 0;
+    return (wcsinitc (hstring, mchar));
+}
+
+
+/* set up a WCS structure from a FITS image header */
+
+struct WorldCoor *
+wcsinit (hstring)
+
+const char *hstring;	/* character string containing FITS header information
+               in the format <keyword>= <value> [/ <comment>] */
+{
+    char mchar;		/* Suffix character for one of multiple WCS */
+    mchar = (char) 0;
+    return (wcsinitc (hstring, &mchar));
+}
+
+
+/* set up a WCS structure from a FITS image header for specified suffix */
+
+struct WorldCoor *
+wcsinitc (hstring, wchar)
+
+const char *hstring;	/* character string containing FITS header information
+               in the format <keyword>= <value> [/ <comment>] */
+char *wchar;		/* Suffix character for one of multiple WCS */
+{
+    struct WorldCoor *wcs, *depwcs;
+    char ctype1[32], ctype2[32], tstring[32];
+    char pvkey1[8],pvkey2[8],pvkey3[8];
+    char *hcoeff;		/* pointer to first coeff's in header */
+    char decsign;
+    double rah,ram,ras, dsign,decd,decm,decs;
+    double dec_deg,ra_hours, secpix, ra0, ra1, dec0, dec1, cvel;
+    double cdelt1, cdelt2, cd[4], pc[81];
+    char keyword[16];
+    int ieq, i, j, k, naxes, cd11p, cd12p, cd21p, cd22p;
+    int ilat;	/* coordinate for latitude or declination */
+    /*
+    int ix1, ix2, iy1, iy2, idx1, idx2, idy1, idy2;
+    double dxrefpix, dyrefpix;
+    */
+    char temp[80];
+    char wcsname[64];	/* Name of WCS depended on by current WCS */
+    char mchar;
+    char cspace = (char) ' ';
+    char cnull = (char) 0;
+    double mjd;
+    double rot;
+    double ut;
+    int nax;
+    int twod;
+    int iszpx = 0;
+    extern int tnxinit();
+    extern int platepos();
+    extern int dsspos();
+
+    wcs = (struct WorldCoor *) calloc (1, sizeof(struct WorldCoor));
+
+    /* Set WCS character and name in structure */
+    mchar = wchar[0];
+    if (mchar == ' ')
+    mchar = cnull;
+    wcs->wcschar = mchar;
+    if (hgetsc (hstring, "WCSNAME", &mchar, 63, wcsname)) {
+    wcs->wcsname = (char *) calloc (strlen (wcsname)+2, 1);
+    strcpy (wcs->wcsname, wcsname);
+    }
+
+
+    /* Set WCSLIB flags so that structures will be reinitialized */
+    wcs->cel.flag = 0;
+    wcs->lin.flag = 0;
+    wcs->wcsl.flag = 0;
+    wcs->wcsl.cubeface = -1;
+
+    /* Initialize to no plate fit */
+    wcs->ncoeff1 = 0;
+    wcs->ncoeff2 = 0;
+
+    /* Initialize to no CD matrix */
+    cdelt1 = 0.0;
+    cdelt2 = 0.0;
+    cd[0] = 0.0;
+    cd[1] = 0.0;
+    cd[2] = 0.0;
+    cd[3] = 0.0;
+    pc[0] = 0.0;
+    wcs->rotmat = 0;
+    wcs->rot = 0.0;
+
+    /* Header parameters independent of projection */
+    naxes = 0;
+    hgeti4c (hstring, "WCSAXES", &mchar, &naxes);
+    if (naxes == 0)
+    hgeti4 (hstring, "WCSAXES", &naxes);
+    if (naxes == 0)
+    hgeti4 (hstring, "NAXIS", &naxes);
+    if (naxes == 0)
+    hgeti4 (hstring, "WCSDIM", &naxes);
+    if (naxes < 1) {
+    setwcserr ("WCSINIT: No WCSAXES, NAXIS, or WCSDIM keyword");
+    wcsfree (wcs);
+    return (NULL);
+    }
+    if (naxes > 2)
+    naxes = 2;
+    wcs->naxis = naxes;
+    wcs->naxes = naxes;
+    wcs->lin.naxis = naxes;
+    wcs->nxpix = 0;
+    hgetr8 (hstring, "NAXIS1", &wcs->nxpix);
+    if (wcs->nxpix < 1)
+    hgetr8 (hstring, "IMAGEW", &wcs->nxpix);
+    if (wcs->nxpix < 1) {
+    setwcserr ("WCSINIT: No NAXIS1 or IMAGEW keyword");
+    wcsfree (wcs);
+    return (NULL);
+    }
+    wcs->nypix = 0;
+    hgetr8 (hstring, "NAXIS2", &wcs->nypix);
+    if (wcs->nypix < 1)
+    hgetr8 (hstring, "IMAGEH", &wcs->nypix);
+    if (naxes > 1 && wcs->nypix < 1) {
+    setwcserr ("WCSINIT: No NAXIS2 or IMAGEH keyword");
+    wcsfree (wcs);
+    return (NULL);
+    }
+
+    /* Reset number of axes to only those with dimension greater than one */
+    nax = 0;
+    for (i = 0; i < naxes; i++) {
+
+    /* Check for number of pixels in axis more than one */
+    strcpy (keyword, "NAXIS");
+    sprintf (temp, "%d", i+1);
+    strcat (keyword, temp);
+    if (!hgeti4 (hstring, keyword, &j)) {
+        if (i == 0 && wcs->nxpix > 1) {
+        /* fprintf (stderr,"WCSINIT: Missing keyword %s set to %.0f from IMAGEW\n",
+             keyword, wcs->nxpix); */
+        j = wcs->nxpix;
+        }
+        else if (i == 1 && wcs->nypix > 1) {
+        /* fprintf (stderr,"WCSINIT: Missing keyword %s set to %.0f from IMAGEH\n",
+             keyword, wcs->nypix); */
+        j = wcs->nypix;
+        }
+        else
+        fprintf (stderr,"WCSINIT: Missing keyword %s assumed 1\n",keyword);
+        }
+
+    /* Check for TAB WCS in axis */
+    strcpy (keyword, "CTYPE");
+    strcat (keyword, temp);
+    if (hgets (hstring, keyword, 16, temp)) {
+        if (strsrch (temp, "-TAB"))
+        j = 0;
+        }
+    if (j > 1) nax = nax + 1;
+    }
+    naxes = nax;
+    wcs->naxes = nax;
+    wcs->naxis = nax;
+
+    hgets (hstring, "INSTRUME", 16, wcs->instrument);
+    hgeti4 (hstring, "DETECTOR", &wcs->detector);
+    wcs->wcsproj = getdefwcs();
+    wcs->logwcs = 0;
+    hgeti4 (hstring, "DC-FLAG", &wcs->logwcs);
+
+    /* Initialize rotation matrices */
+    for (i = 0; i < 81; i++) wcs->pc[i] = 0.0;
+    for (i = 0; i < 81; i++) pc[i] = 0.0;
+    for (i = 0; i < naxes; i++) wcs->pc[(i*naxes)+i] = 1.0;
+    for (i = 0; i < naxes; i++) pc[(i*naxes)+i] = 1.0;
+    for (i = 0; i < 9; i++) wcs->cdelt[i] = 0.0;
+    for (i = 0; i < naxes; i++) wcs->cdelt[i] = 1.0;
+
+    /* If the current world coordinate system depends on another, set it now */
+    if (hgetsc (hstring, "WCSDEP",&mchar, 63, wcsname)) {
+    if ((wcs->wcs = wcsinitn (hstring, wcsname)) == NULL) {
+        setwcserr ("WCSINIT: depended on WCS could not be set");
+        wcsfree (wcs);
+        return (NULL);
+        }
+    depwcs = wcs->wcs;
+    depwcs->wcsdep = wcs;
+    }
+    else
+    wcs->wcs = NULL;
+
+    /* Read radial velocity from image header */
+    wcs->radvel = 0.0;
+    wcs->zvel = 0.0;
+    cvel = 299792.5;
+    if (hgetr8c (hstring, "VSOURCE", &mchar, &wcs->radvel))
+    wcs->zvel = wcs->radvel / cvel;
+    else if (hgetr8c (hstring, "ZSOURCE", &mchar, &wcs->zvel))
+    wcs->radvel = wcs->zvel * cvel;
+    else if (hgetr8 (hstring, "VELOCITY", &wcs->radvel))
+    wcs->zvel = wcs->radvel / cvel;
+
+    for (i = 0; i < 10; i++) {
+    wcs->prj.p[i] = 0.0;
+    }
+
+    /* World coordinate system reference coordinate information */
+    if (hgetsc (hstring, "CTYPE1", &mchar, 16, ctype1)) {
+    if (!strncmp (ctype1+5,"ZPX", 3)) {
+        iszpx = 1;
+        ctype1[7] = 'N';
+
+        /* IRAF ZPX parameters for ZPN projection */
+        for (i = 0; i < 10; i++) {
+        sprintf (keyword,"projp%d",i);
+        mgetr8 (hstring, "WAT1",keyword, &wcs->prj.p[i]);
+        }
+        }
+    else
+        iszpx = 0;
+
+    /* Read second coordinate type */
+    strcpy (ctype2, ctype1);
+    if (!hgetsc (hstring, "CTYPE2", &mchar, 16, ctype2))
+        twod = 0;
+    else {
+        twod = 1;
+        if (!strncmp (ctype2+5,"ZPX", 3)) {
+        iszpx = 1;
+        ctype2[7] = 'N';
+        }
+        }
+    strcpy (wcs->ctype[0], ctype1);
+    strcpy (wcs->ctype[1], ctype2);
+    if (strsrch (ctype2, "LAT") || strsrch (ctype2, "DEC"))
+        ilat = 2;
+    else
+        ilat = 1;
+
+    /* Read third and fourth coordinate types, if present */
+    strcpy (wcs->ctype[2], "");
+    hgetsc (hstring, "CTYPE3", &mchar, 9, wcs->ctype[2]);
+    strcpy (wcs->ctype[3], "");
+    hgetsc (hstring, "CTYPE4", &mchar, 9, wcs->ctype[3]);
+
+    /* Set projection type in WCS data structure */
+    if (wcstype (wcs, ctype1, ctype2)) {
+        wcsfree (wcs);
+        return (NULL);
+        }
+
+    /* Get units, if present, for linear coordinates */
+    if (wcs->prjcode == WCS_LIN) {
+        if (!hgetsc (hstring, "CUNIT1", &mchar, 16, wcs->units[0])) {
+        if (!mgetstr (hstring, "WAT1", "units", 16, wcs->units[0])) {
+            wcs->units[0][0] = 0;
+            }
+        }
+        if (!strcmp (wcs->units[0], "pixel"))
+        wcs->prjcode = WCS_PIX;
+        if (twod) {
+        if (!hgetsc (hstring, "CUNIT2", &mchar, 16, wcs->units[1])) {
+            if (!mgetstr (hstring, "WAT2", "units", 16, wcs->units[1])) {
+            wcs->units[1][0] = 0;
+            }
+            }
+        if (!strcmp (wcs->units[0], "pixel"))
+            wcs->prjcode = WCS_PIX;
+        }
+        }
+
+    /* Reference pixel coordinates and WCS value */
+    wcs->crpix[0] = 1.0;
+    hgetr8c (hstring, "CRPIX1", &mchar, &wcs->crpix[0]);
+    wcs->crpix[1] = 1.0;
+    hgetr8c (hstring, "CRPIX2", &mchar, &wcs->crpix[1]);
+    wcs->xrefpix = wcs->crpix[0];
+    wcs->yrefpix = wcs->crpix[1];
+    wcs->crval[0] = 0.0;
+    hgetr8c (hstring, "CRVAL1", &mchar, &wcs->crval[0]);
+    wcs->crval[1] = 0.0;
+    hgetr8c (hstring, "CRVAL2", &mchar, &wcs->crval[1]);
+    if (wcs->syswcs == WCS_NPOLE)
+        wcs->crval[1] = 90.0 - wcs->crval[1];
+    if (wcs->syswcs == WCS_SPA)
+        wcs->crval[1] = wcs->crval[1] - 90.0;
+    wcs->xref = wcs->crval[0];
+    wcs->yref = wcs->crval[1];
+    if (wcs->coorflip) {
+        wcs->cel.ref[0] = wcs->crval[1];
+        wcs->cel.ref[1] = wcs->crval[0];
+        }
+    else {
+        wcs->cel.ref[0] = wcs->crval[0];
+        wcs->cel.ref[1] = wcs->crval[1];
+        }
+    wcs->longpole = 999.0;
+    hgetr8c (hstring, "LONPOLE", &mchar, &wcs->longpole);
+    wcs->cel.ref[2] = wcs->longpole;
+    wcs->latpole = 999.0;
+    hgetr8c (hstring, "LATPOLE", &mchar, &wcs->latpole);
+    wcs->cel.ref[3] = wcs->latpole;
+    wcs->lin.crpix = wcs->crpix;
+    wcs->lin.cdelt = wcs->cdelt;
+    wcs->lin.pc = wcs->pc;
+
+    /* Projection constants (this should be projection-dependent */
+    wcs->prj.r0 = 0.0;
+    hgetr8c (hstring, "PROJR0", &mchar, &wcs->prj.r0);
+
+    /* FITS WCS interim proposal projection constants */
+    for (i = 0; i < 10; i++) {
+        sprintf (keyword,"PROJP%d",i);
+        hgetr8c (hstring, keyword, &mchar, &wcs->prj.p[i]);
+        }
+
+    sprintf (pvkey1, "PV%d_1", ilat);
+    sprintf (pvkey2, "PV%d_2", ilat);
+    sprintf (pvkey3, "PV%d_3", ilat);
+
+    /* FITS WCS standard projection constants (projection-dependent) */
+    if (wcs->prjcode == WCS_AZP || wcs->prjcode == WCS_SIN ||
+        wcs->prjcode == WCS_COP || wcs->prjcode == WCS_COE ||
+        wcs->prjcode == WCS_COD || wcs->prjcode == WCS_COO) {
+        hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+        hgetr8c (hstring, pvkey2, &mchar, &wcs->prj.p[2]);
+        }
+    else if (wcs->prjcode == WCS_SZP) {
+        hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+        hgetr8c (hstring, pvkey2, &mchar, &wcs->prj.p[2]);
+        if (wcs->prj.p[3] == 0.0)
+        wcs->prj.p[3] = 90.0;
+        hgetr8c (hstring, pvkey3, &mchar, &wcs->prj.p[3]);
+        }
+    else if (wcs->prjcode == WCS_CEA) {
+        if (wcs->prj.p[1] == 0.0)
+        wcs->prj.p[1] = 1.0;
+        hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+        }
+    else if (wcs->prjcode == WCS_CYP) {
+        if (wcs->prj.p[1] == 0.0)
+        wcs->prj.p[1] = 1.0;
+        hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+        if (wcs->prj.p[2] == 0.0)
+        wcs->prj.p[2] = 1.0;
+        hgetr8c (hstring, pvkey2, &mchar, &wcs->prj.p[2]);
+        }
+    else if (wcs->prjcode == WCS_AIR) {
+        if (wcs->prj.p[1] == 0.0)
+        wcs->prj.p[1] = 90.0;
+        hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+        }
+    else if (wcs->prjcode == WCS_BON) {
+        hgetr8c (hstring, pvkey1, &mchar, &wcs->prj.p[1]);
+        }
+    else if (wcs->prjcode == WCS_ZPN) {
+        for (i = 0; i < 10; i++) {
+        sprintf (keyword,"PV%d_%d", ilat, i);
+        hgetr8c (hstring, keyword, &mchar, &wcs->prj.p[i]);
+        }
+        }
+
+    /* Initialize TNX, defaulting to TAN if there is a problem */
+    if (wcs->prjcode == WCS_TNX) {
+        if (tnxinit (hstring, wcs)) {
+        wcs->ctype[0][6] = 'A';
+        wcs->ctype[0][7] = 'N';
+        wcs->ctype[1][6] = 'A';
+        wcs->ctype[1][7] = 'N';
+        wcs->prjcode = WCS_TAN;
+        }
+        }
+
+    /* If ZPX, read coefficients from WATi keyword */
+    if (iszpx) {
+        char mkey[8];
+        sprintf (mkey,"WAT%d", ilat);
+        for (i = 0; i < 10; i++) {
+        wcs->prj.p[i] = 0.0;
+        sprintf (keyword,"projp%d",i);
+        mgetr8 (hstring, mkey, keyword, &wcs->prj.p[i]);
+        }
+        }
+
+
+    /* Coordinate reference frame, equinox, and epoch */
+    if (wcs->wcsproj > 0)
+        wcseqm (hstring, wcs, &mchar);
+    wcsioset (wcs);
+
+    /* Read distortion coefficients, if present */
+    distortinit (wcs, hstring);
+
+    /* Use polynomial fit instead of projection, if present */
+    wcs->ncoeff1 = 0;
+    wcs->ncoeff2 = 0;
+    cd11p = hgetr8c (hstring, "CD1_1", &mchar, &cd[0]);
+    cd12p = hgetr8c (hstring, "CD1_2", &mchar, &cd[1]);
+    cd21p = hgetr8c (hstring, "CD2_1", &mchar, &cd[2]);
+    cd22p = hgetr8c (hstring, "CD2_2", &mchar, &cd[3]);
+    if (wcs->wcsproj != WCS_OLD &&
+        (hcoeff = ksearch (hstring,"CO1_1")) != NULL) {
+        wcs->prjcode = WCS_PLT;
+        (void)strcpy (wcs->ptype, "PLATE");
+        for (i = 0; i < 20; i++) {
+        sprintf (keyword,"CO1_%d", i+1);
+        wcs->x_coeff[i] = 0.0;
+        if (hgetr8 (hcoeff, keyword, &wcs->x_coeff[i]))
+            wcs->ncoeff1 = i + 1;
+        }
+        hcoeff = ksearch (hstring,"CO2_1");
+        for (i = 0; i < 20; i++) {
+        sprintf (keyword,"CO2_%d",i+1);
+        wcs->y_coeff[i] = 0.0;
+        if (hgetr8 (hcoeff, keyword, &wcs->y_coeff[i]))
+            wcs->ncoeff2 = i + 1;
+        }
+
+        /* Compute a nominal scale factor */
+        platepos (wcs->crpix[0], wcs->crpix[1], wcs, &ra0, &dec0);
+        platepos (wcs->crpix[0], wcs->crpix[1]+1.0, wcs, &ra1, &dec1);
+        wcs->yinc = dec1 - dec0;
+        wcs->xinc = -wcs->yinc;
+
+        /* Compute image rotation angle */
+        wcs->wcson = 1;
+        wcsrotset (wcs);
+        rot = degrad (wcs->rot);
+
+        /* Compute scale at reference pixel */
+        platepos (wcs->crpix[0], wcs->crpix[1], wcs, &ra0, &dec0);
+        platepos (wcs->crpix[0]+cos(rot),
+              wcs->crpix[1]+sin(rot), wcs, &ra1, &dec1);
+        wcs->cdelt[0] = -wcsdist (ra0, dec0, ra1, dec1);
+        wcs->xinc = wcs->cdelt[0];
+        platepos (wcs->crpix[0]+sin(rot),
+              wcs->crpix[1]+cos(rot), wcs, &ra1, &dec1);
+        wcs->cdelt[1] = wcsdist (ra0, dec0, ra1, dec1);
+        wcs->yinc = wcs->cdelt[1];
+
+        /* Set CD matrix from header */
+        wcs->cd[0] = cd[0];
+        wcs->cd[1] = cd[1];
+        wcs->cd[2] = cd[2];
+        wcs->cd[3] = cd[3];
+        (void) matinv (2, wcs->cd, wcs->dc);
+        }
+
+    /* Else use CD matrix, if present */
+    else if (cd11p || cd12p || cd21p || cd22p) {
+        wcs->rotmat = 1;
+        wcscdset (wcs, cd);
+        }
+
+    /* Else get scaling from CDELT1 and CDELT2 */
+    else if (hgetr8c (hstring, "CDELT1", &mchar, &cdelt1) != 0) {
+        hgetr8c (hstring, "CDELT2", &mchar, &cdelt2);
+
+        /* If CDELT1 or CDELT2 is 0 or missing */
+        if (cdelt1 == 0.0 || (wcs->nypix > 1 && cdelt2 == 0.0)) {
+        if (ksearch (hstring,"SECPIX") != NULL ||
+            ksearch (hstring,"PIXSCALE") != NULL ||
+            ksearch (hstring,"PIXSCAL1") != NULL ||
+            ksearch (hstring,"XPIXSIZE") != NULL ||
+            ksearch (hstring,"SECPIX1") != NULL) {
+            secpix = 0.0;
+            hgetr8 (hstring,"SECPIX",&secpix);
+            if (secpix == 0.0)
+            hgetr8 (hstring,"PIXSCALE",&secpix);
+            if (secpix == 0.0) {
+            hgetr8 (hstring,"SECPIX1",&secpix);
+            if (secpix != 0.0) {
+                if (cdelt1 == 0.0)
+                cdelt1 = -secpix / 3600.0;
+                if (cdelt2 == 0.0) {
+                hgetr8 (hstring,"SECPIX2",&secpix);
+                cdelt2 = secpix / 3600.0;
+                }
+                }
+            else {
+                hgetr8 (hstring,"XPIXSIZE",&secpix);
+                if (secpix != 0.0) {
+                if (cdelt1 == 0.0)
+                    cdelt1 = -secpix / 3600.0;
+                if (cdelt2 == 0.0) {
+                    hgetr8 (hstring,"YPIXSIZE",&secpix);
+                    cdelt2 = secpix / 3600.0;
+                    }
+                }
+                else {
+                hgetr8 (hstring,"PIXSCAL1",&secpix);
+                if (secpix != 0.0 && cdelt1 == 0.0)
+                    cdelt1 = -secpix / 3600.0;
+                if (cdelt2 == 0.0) {
+                    hgetr8 (hstring,"PIXSCAL2",&secpix);
+                    cdelt2 = secpix / 3600.0;
+                    }
+                }
+                }
+            }
+            else {
+            if (cdelt1 == 0.0)
+                cdelt1 = -secpix / 3600.0;
+            if (cdelt2 == 0.0)
+                cdelt2 = secpix / 3600.0;
+            }
+            }
+        }
+        if (cdelt2 == 0.0 && wcs->nypix > 1)
+        cdelt2 = -cdelt1;
+        wcs->cdelt[2] = 1.0;
+        wcs->cdelt[3] = 1.0;
+
+        /* Initialize rotation matrix */
+        for (i = 0; i < 81; i++) {
+        pc[i] = 0.0;
+        wcs->pc[i] = 0.0;
+        }
+        for (i = 0; i < naxes; i++)
+        pc[(i*naxes)+i] = 1.0;
+
+        /* Read FITS WCS interim rotation matrix */
+        if (!mchar && hgetr8 (hstring,"PC001001",&pc[0]) != 0) {
+        k = 0;
+        for (i = 0; i < naxes; i++) {
+            for (j = 0; j < naxes; j++) {
+            if (i == j)
+                pc[k] = 1.0;
+            else
+                pc[k] = 0.0;
+            sprintf (keyword, "PC00%1d00%1d", i+1, j+1);
+            hgetr8 (hstring, keyword, &pc[k++]);
+            }
+            }
+        wcspcset (wcs, cdelt1, cdelt2, pc);
+        }
+
+        /* Read FITS WCS standard rotation matrix */
+        else if (hgetr8c (hstring, "PC1_1", &mchar, &pc[0]) != 0) {
+        k = 0;
+        for (i = 0; i < naxes; i++) {
+            for (j = 0; j < naxes; j++) {
+            if (i == j)
+                pc[k] = 1.0;
+            else
+                pc[k] = 0.0;
+            sprintf (keyword, "PC%1d_%1d", i+1, j+1);
+            hgetr8c (hstring, keyword, &mchar, &pc[k++]);
+            }
+            }
+        wcspcset (wcs, cdelt1, cdelt2, pc);
+        }
+
+        /* Otherwise, use CROTAn */
+        else {
+        rot = 0.0;
+        if (ilat == 2)
+            hgetr8c (hstring, "CROTA2", &mchar, &rot);
+        else
+            hgetr8c (hstring,"CROTA1", &mchar, &rot);
+        wcsdeltset (wcs, cdelt1, cdelt2, rot);
+        }
+        }
+
+    /* If no scaling is present, set to 1 per pixel, no rotation */
+    else {
+        wcs->xinc = 1.0;
+        wcs->yinc = 1.0;
+        wcs->cdelt[0] = 1.0;
+        wcs->cdelt[1] = 1.0;
+        wcs->rot = 0.0;
+        wcs->rotmat = 0;
+        setwcserr ("WCSINIT: setting CDELT to 1");
+        }
+
+    /* If linear or pixel WCS, print "degrees" */
+    if (!strncmp (wcs->ptype,"LINEAR",6) ||
+        !strncmp (wcs->ptype,"PIXEL",5)) {
+        wcs->degout = -1;
+        wcs->ndec = 5;
+        }
+
+    /* Epoch of image (from observation date, if possible) */
+    if (hgetr8 (hstring, "MJD-OBS", &mjd))
+        wcs->epoch = 1900.0 + (mjd - 15019.81352) / 365.242198781;
+    else if (!hgetdate (hstring,"DATE-OBS",&wcs->epoch)) {
+        if (!hgetdate (hstring,"DATE",&wcs->epoch)) {
+        if (!hgetr8 (hstring,"EPOCH",&wcs->epoch))
+            wcs->epoch = wcs->equinox;
+        }
+        }
+
+    /* Add time of day if not part of DATE-OBS string */
+    else {
+        hgets (hstring,"DATE-OBS",32,tstring);
+        if (!strchr (tstring,'T')) {
+        if (hgetr8 (hstring, "UT",&ut))
+            wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+        else if (hgetr8 (hstring, "UTMID",&ut))
+            wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+        }
+        }
+
+    wcs->wcson = 1;
+    }
+
+    else if (mchar != cnull && mchar != cspace) {
+    (void) sprintf (temp, "WCSINITC: No image scale for WCS %c", mchar);
+    setwcserr (temp);
+    wcsfree (wcs);
+    return (NULL);
+    }
+
+    /* Plate solution coefficients */
+    else if (ksearch (hstring,"PLTRAH") != NULL) {
+    wcs->prjcode = WCS_DSS;
+    hcoeff = ksearch (hstring,"PLTRAH");
+    hgetr8 (hcoeff,"PLTRAH",&rah);
+    hgetr8 (hcoeff,"PLTRAM",&ram);
+    hgetr8 (hcoeff,"PLTRAS",&ras);
+    ra_hours = rah + (ram / (double)60.0) + (ras / (double)3600.0);
+    wcs->plate_ra = hrrad (ra_hours);
+    decsign = '+';
+    hgets (hcoeff,"PLTDECSN", 1, &decsign);
+    if (decsign == '-')
+        dsign = -1.;
+    else
+        dsign = 1.;
+    hgetr8 (hcoeff,"PLTDECD",&decd);
+    hgetr8 (hcoeff,"PLTDECM",&decm);
+    hgetr8 (hcoeff,"PLTDECS",&decs);
+    dec_deg = dsign * (decd+(decm/(double)60.0)+(decs/(double)3600.0));
+    wcs->plate_dec = degrad (dec_deg);
+    hgetr8 (hstring,"EQUINOX",&wcs->equinox);
+    hgeti4 (hstring,"EQUINOX",&ieq);
+    if (ieq == 1950)
+        strcpy (wcs->radecsys,"FK4");
+    else
+        strcpy (wcs->radecsys,"FK5");
+    wcs->epoch = wcs->equinox;
+    hgetr8 (hstring,"EPOCH",&wcs->epoch);
+    (void)sprintf (wcs->center,"%2.0f:%2.0f:%5.3f %c%2.0f:%2.0f:%5.3f %s",
+               rah,ram,ras,decsign,decd,decm,decs,wcs->radecsys);
+    hgetr8 (hstring,"PLTSCALE",&wcs->plate_scale);
+    hgetr8 (hstring,"XPIXELSZ",&wcs->x_pixel_size);
+    hgetr8 (hstring,"YPIXELSZ",&wcs->y_pixel_size);
+    hgetr8 (hstring,"CNPIX1",&wcs->x_pixel_offset);
+    hgetr8 (hstring,"CNPIX2",&wcs->y_pixel_offset);
+    hcoeff = ksearch (hstring,"PPO1");
+    for (i = 0; i < 6; i++) {
+        sprintf (keyword,"PPO%d", i+1);
+        wcs->ppo_coeff[i] = 0.0;
+        hgetr8 (hcoeff,keyword,&wcs->ppo_coeff[i]);
+        }
+    hcoeff = ksearch (hstring,"AMDX1");
+    for (i = 0; i < 20; i++) {
+        sprintf (keyword,"AMDX%d", i+1);
+        wcs->x_coeff[i] = 0.0;
+        hgetr8 (hcoeff, keyword, &wcs->x_coeff[i]);
+        }
+    hcoeff = ksearch (hstring,"AMDY1");
+    for (i = 0; i < 20; i++) {
+        sprintf (keyword,"AMDY%d",i+1);
+        wcs->y_coeff[i] = 0.0;
+        hgetr8 (hcoeff, keyword, &wcs->y_coeff[i]);
+        }
+    wcs->wcson = 1;
+    (void)strcpy (wcs->c1type, "RA");
+    (void)strcpy (wcs->c2type, "DEC");
+    (void)strcpy (wcs->ptype, "DSS");
+    wcs->degout = 0;
+    wcs->ndec = 3;
+
+    /* Compute a nominal reference pixel at the image center */
+    strcpy (wcs->ctype[0], "RA---DSS");
+    strcpy (wcs->ctype[1], "DEC--DSS");
+    wcs->crpix[0] = 0.5 * wcs->nxpix;
+    wcs->crpix[1] = 0.5 * wcs->nypix;
+    wcs->xrefpix = wcs->crpix[0];
+    wcs->yrefpix = wcs->crpix[1];
+    dsspos (wcs->crpix[0], wcs->crpix[1], wcs, &ra0, &dec0);
+    wcs->crval[0] = ra0;
+    wcs->crval[1] = dec0;
+    wcs->xref = wcs->crval[0];
+    wcs->yref = wcs->crval[1];
+
+    /* Compute a nominal scale factor */
+    dsspos (wcs->crpix[0], wcs->crpix[1]+1.0, wcs, &ra1, &dec1);
+    wcs->yinc = dec1 - dec0;
+    wcs->xinc = -wcs->yinc;
+    wcsioset (wcs);
+
+    /* Compute image rotation angle */
+    wcs->wcson = 1;
+    wcsrotset (wcs);
+    rot = degrad (wcs->rot);
+
+    /* Compute image scale at center */
+    dsspos (wcs->crpix[0]+cos(rot),
+        wcs->crpix[1]+sin(rot), wcs, &ra1, &dec1);
+    wcs->cdelt[0] = -wcsdist (ra0, dec0, ra1, dec1);
+    dsspos (wcs->crpix[0]+sin(rot),
+        wcs->crpix[1]+cos(rot), wcs, &ra1, &dec1);
+    wcs->cdelt[1] = wcsdist (ra0, dec0, ra1, dec1);
+
+    /* Set all other image scale parameters */
+    wcsdeltset (wcs, wcs->cdelt[0], wcs->cdelt[1], wcs->rot);
+    }
+
+    /* Approximate world coordinate system if plate scale is known */
+    else if ((ksearch (hstring,"SECPIX") != NULL ||
+         ksearch (hstring,"PIXSCALE") != NULL ||
+         ksearch (hstring,"PIXSCAL1") != NULL ||
+         ksearch (hstring,"XPIXSIZE") != NULL ||
+         ksearch (hstring,"SECPIX1") != NULL)) {
+    secpix = 0.0;
+    hgetr8 (hstring,"SECPIX",&secpix);
+    if (secpix == 0.0)
+        hgetr8 (hstring,"PIXSCALE",&secpix);
+    if (secpix == 0.0) {
+        hgetr8 (hstring,"SECPIX1",&secpix);
+        if (secpix != 0.0) {
+        cdelt1 = -secpix / 3600.0;
+        hgetr8 (hstring,"SECPIX2",&secpix);
+        cdelt2 = secpix / 3600.0;
+        }
+        else {
+        hgetr8 (hstring,"XPIXSIZE",&secpix);
+        if (secpix != 0.0) {
+            cdelt1 = -secpix / 3600.0;
+            hgetr8 (hstring,"YPIXSIZE",&secpix);
+            cdelt2 = secpix / 3600.0;
+            }
+        else {
+            hgetr8 (hstring,"PIXSCAL1",&secpix);
+            cdelt1 = -secpix / 3600.0;
+            hgetr8 (hstring,"PIXSCAL2",&secpix);
+            cdelt2 = secpix / 3600.0;
+            }
+        }
+        }
+    else {
+        cdelt2 = secpix / 3600.0;
+        cdelt1 = -cdelt2;
+        }
+
+    /* Get rotation angle from the header, if it's there */
+    rot = 0.0;
+    hgetr8 (hstring,"CROTA1", &rot);
+    if (wcs->rot == 0.)
+        hgetr8 (hstring,"CROTA2", &rot);
+
+    /* Set CD and PC matrices */
+    wcsdeltset (wcs, cdelt1, cdelt2, rot);
+
+    /* By default, set reference pixel to center of image */
+    wcs->crpix[0] = 0.5 + (wcs->nxpix * 0.5);
+    wcs->crpix[1] = 0.5 + (wcs->nypix * 0.5);
+
+    /* Get reference pixel from the header, if it's there */
+    if (ksearch (hstring,"CRPIX1") != NULL) {
+        hgetr8 (hstring,"CRPIX1",&wcs->crpix[0]);
+        hgetr8 (hstring,"CRPIX2",&wcs->crpix[1]);
+        }
+
+    /* Use center of detector array as reference pixel
+    else if (ksearch (hstring,"DETSIZE") != NULL ||
+         ksearch (hstring,"DETSEC") != NULL) {
+        char *ic;
+        hgets (hstring, "DETSIZE", 32, temp);
+        ic = strchr (temp, ':');
+        if (ic != NULL)
+        *ic = ' ';
+        ic = strchr (temp, ',');
+        if (ic != NULL)
+        *ic = ' ';
+        ic = strchr (temp, ':');
+        if (ic != NULL)
+        *ic = ' ';
+        ic = strchr (temp, ']');
+        if (ic != NULL)
+        *ic = cnull;
+        sscanf (temp, "%d %d %d %d", &idx1, &idx2, &idy1, &idy2);
+        dxrefpix = 0.5 * (double) (idx1 + idx2 - 1);
+        dyrefpix = 0.5 * (double) (idy1 + idy2 - 1);
+        hgets (hstring, "DETSEC", 32, temp);
+        ic = strchr (temp, ':');
+        if (ic != NULL)
+        *ic = ' ';
+        ic = strchr (temp, ',');
+        if (ic != NULL)
+        *ic = ' ';
+        ic = strchr (temp, ':');
+        if (ic != NULL)
+        *ic = ' ';
+        ic = strchr (temp, ']');
+        if (ic != NULL)
+        *ic = cnull;
+        sscanf (temp, "%d %d %d %d", &ix1, &ix2, &iy1, &iy2);
+        wcs->crpix[0] = dxrefpix - (double) (ix1 - 1);
+        wcs->crpix[1] = dyrefpix - (double) (iy1 - 1);
+        } */
+    wcs->xrefpix = wcs->crpix[0];
+    wcs->yrefpix = wcs->crpix[1];
+
+    wcs->crval[0] = -999.0;
+    if (!hgetra (hstring,"RA",&wcs->crval[0])) {
+        setwcserr ("WCSINIT: No RA with SECPIX, no WCS");
+        wcsfree (wcs);
+        return (NULL);
+        }
+    wcs->crval[1] = -999.0;
+    if (!hgetdec (hstring,"DEC",&wcs->crval[1])) {
+        setwcserr ("WCSINIT No DEC with SECPIX, no WCS");
+        wcsfree (wcs);
+        return (NULL);
+        }
+    wcs->xref = wcs->crval[0];
+    wcs->yref = wcs->crval[1];
+    wcs->coorflip = 0;
+
+    wcs->cel.ref[0] = wcs->crval[0];
+    wcs->cel.ref[1] = wcs->crval[1];
+    wcs->cel.ref[2] = 999.0;
+    if (!hgetr8 (hstring,"LONPOLE",&wcs->cel.ref[2]))
+        hgetr8 (hstring,"LONGPOLE",&wcs->cel.ref[2]);
+    wcs->cel.ref[3] = 999.0;
+    hgetr8 (hstring,"LATPOLE",&wcs->cel.ref[3]);
+
+    /* Epoch of image (from observation date, if possible) */
+    if (hgetr8 (hstring, "MJD-OBS", &mjd))
+        wcs->epoch = 1900.0 + (mjd - 15019.81352) / 365.242198781;
+    else if (!hgetdate (hstring,"DATE-OBS",&wcs->epoch)) {
+        if (!hgetdate (hstring,"DATE",&wcs->epoch)) {
+        if (!hgetr8 (hstring,"EPOCH",&wcs->epoch))
+            wcs->epoch = wcs->equinox;
+        }
+        }
+
+    /* Add time of day if not part of DATE-OBS string */
+    else {
+        hgets (hstring,"DATE-OBS",32,tstring);
+        if (!strchr (tstring,'T')) {
+        if (hgetr8 (hstring, "UT",&ut))
+            wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+        else if (hgetr8 (hstring, "UTMID",&ut))
+            wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+        }
+        }
+
+    /* Coordinate reference frame and equinox */
+    (void) wcstype (wcs, "RA---TAN", "DEC--TAN");
+    wcs->coorflip = 0;
+    wcseq (hstring,wcs);
+    wcsioset (wcs);
+    wcs->degout = 0;
+    wcs->ndec = 3;
+    wcs->wcson = 1;
+    }
+
+    else {
+    setwcserr ("WCSINIT: No image scale");
+    wcsfree (wcs);
+    return (NULL);
+    }
+
+    wcs->lin.crpix = wcs->crpix;
+    wcs->lin.cdelt = wcs->cdelt;
+    wcs->lin.pc = wcs->pc;
+
+    wcs->printsys = 1;
+    wcs->tabsys = 0;
+    wcs->linmode = 0;
+
+    /* Initialize special WCS commands */
+    setwcscom (wcs);
+
+    return (wcs);
+}
+
+
+/* Set coordinate system of image, input, and output */
+
+static void
+wcsioset (wcs)
+
+struct WorldCoor *wcs;
+{
+    if (strlen (wcs->radecsys) == 0 || wcs->prjcode == WCS_LIN)
+    strcpy (wcs->radecsys, "LINEAR");
+    if (wcs->prjcode == WCS_PIX)
+    strcpy (wcs->radecsys, "PIXEL");
+    wcs->syswcs = wcscsys (wcs->radecsys);
+
+    if (wcs->syswcs == WCS_B1950)
+    strcpy (wcs->radecout, "FK4");
+    else if (wcs->syswcs == WCS_J2000)
+    strcpy (wcs->radecout, "FK5");
+    else
+    strcpy (wcs->radecout, wcs->radecsys);
+    wcs->sysout = wcscsys (wcs->radecout);
+    wcs->eqout = wcs->equinox;
+    strcpy (wcs->radecin, wcs->radecsys);
+    wcs->sysin = wcscsys (wcs->radecin);
+    wcs->eqin = wcs->equinox;
+    return;
+}
+
+
+static void
+wcseq (hstring, wcs)
+
+char	*hstring;	/* character string containing FITS header information
+            in the format <keyword>= <value> [/ <comment>] */
+struct WorldCoor *wcs;	/* World coordinate system data structure */
+{
+    char mchar;		/* Suffix character for one of multiple WCS */
+    mchar = (char) 0;
+    wcseqm (hstring, wcs, &mchar);
+    return;
+}
+
+
+static void
+wcseqm (hstring, wcs, mchar)
+
+char	*hstring;	/* character string containing FITS header information
+            in the format <keyword>= <value> [/ <comment>] */
+struct WorldCoor *wcs;	/* World coordinate system data structure */
+char	*mchar;		/* Suffix character for one of multiple WCS */
+{
+    int ieq = 0;
+    int eqhead = 0;
+    char systring[32], eqstring[32];
+    char radeckey[16], eqkey[16];
+    char tstring[32];
+    double ut;
+
+    /* Set equinox from EQUINOX, EPOCH, or RADECSYS; default to 2000 */
+    systring[0] = 0;
+    eqstring[0] = 0;
+    if (mchar[0]) {
+    sprintf (eqkey, "EQUINOX%c", mchar[0]);
+    sprintf (radeckey, "RADECSYS%c", mchar[0]);
+    }
+    else {
+    strcpy (eqkey, "EQUINOX");
+    sprintf (radeckey, "RADECSYS");
+    }
+    if (!hgets (hstring, eqkey, 31, eqstring)) {
+    if (hgets (hstring, "EQUINOX", 31, eqstring))
+        strcpy (eqkey, "EQUINOX");
+    }
+    if (!hgets (hstring, radeckey, 31, systring)) {
+    if (hgets (hstring, "RADECSYS", 31, systring))
+        sprintf (radeckey, "RADECSYS");
+    }
+
+    if (eqstring[0] == 'J') {
+    wcs->equinox = atof (eqstring+1);
+    ieq = atoi (eqstring+1);
+    strcpy (systring, "FK5");
+    }
+    else if (eqstring[0] == 'B') {
+    wcs->equinox = atof (eqstring+1);
+    ieq = (int) atof (eqstring+1);
+    strcpy (systring, "FK4");
+    }
+    else if (hgeti4 (hstring, eqkey, &ieq)) {
+    hgetr8 (hstring, eqkey, &wcs->equinox);
+    eqhead = 1;
+    }
+
+    else if (hgeti4 (hstring,"EPOCH",&ieq)) {
+    if (ieq == 0) {
+        ieq = 1950;
+        wcs->equinox = 1950.0;
+        }
+    else {
+            hgetr8 (hstring,"EPOCH",&wcs->equinox);
+        eqhead = 1;
+        }
+    }
+
+    else if (systring[0] != (char)0) {
+    if (!strncmp (systring,"FK4",3)) {
+        wcs->equinox = 1950.0;
+        ieq = 1950;
+        }
+    else if (!strncmp (systring,"ICRS",4)) {
+        wcs->equinox = 2000.0;
+        ieq = 2000;
+        }
+    else if (!strncmp (systring,"FK5",3)) {
+        wcs->equinox = 2000.0;
+        ieq = 2000;
+        }
+    else if (!strncmp (systring,"GAL",3)) {
+        wcs->equinox = 2000.0;
+        ieq = 2000;
+        }
+    else if (!strncmp (systring,"ECL",3)) {
+        wcs->equinox = 2000.0;
+        ieq = 2000;
+        }
+    }
+
+    if (ieq == 0) {
+    wcs->equinox = 2000.0;
+    ieq = 2000;
+    if (!strncmp (wcs->c1type, "RA",2) || !strncmp (wcs->c1type,"DEC",3))
+        strcpy (systring,"FK5");
+    }
+
+    /* Epoch of image (from observation date, if possible) */
+    if (!hgetdate (hstring,"DATE-OBS",&wcs->epoch)) {
+    if (!hgetdate (hstring,"DATE",&wcs->epoch)) {
+        if (!hgetr8 (hstring,"EPOCH",&wcs->epoch))
+        wcs->epoch = wcs->equinox;
+        }
+    }
+
+    /* Add time of day if not part of DATE-OBS string */
+    else {
+    hgets (hstring,"DATE-OBS",32,tstring);
+    if (!strchr (tstring,'T')) {
+        if (hgetr8 (hstring, "UT",&ut))
+        wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+        else if (hgetr8 (hstring, "UTMID",&ut))
+        wcs->epoch = wcs->epoch + (ut / (24.0 * 365.242198781));
+        }
+    }
+    if (wcs->epoch == 0.0)
+    wcs->epoch = wcs->equinox;
+
+    /* Set coordinate system from keyword, if it is present */
+    if (systring[0] == (char) 0)
+     hgets (hstring, radeckey, 31, systring);
+    if (systring[0] != (char) 0) {
+    strcpy (wcs->radecsys,systring);
+    if (!eqhead) {
+        if (!strncmp (wcs->radecsys,"FK4",3))
+        wcs->equinox = 1950.0;
+        else if (!strncmp (wcs->radecsys,"FK5",3))
+        wcs->equinox = 2000.0;
+        else if (!strncmp (wcs->radecsys,"ICRS",4))
+        wcs->equinox = 2000.0;
+        else if (!strncmp (wcs->radecsys,"GAL",3) && ieq == 0)
+        wcs->equinox = 2000.0;
+        }
+    }
+
+    /* Otherwise set coordinate system from equinox */
+    /* Systemless coordinates cannot be translated using b, j, or g commands */
+    else if (wcs->syswcs != WCS_NPOLE) {
+    if (ieq > 1980)
+        strcpy (wcs->radecsys,"FK5");
+    else
+        strcpy (wcs->radecsys,"FK4");
+    }
+
+    /* Set galactic coordinates if GLON or GLAT are in C1TYPE */
+    if (wcs->c1type[0] == 'G')
+    strcpy (wcs->radecsys,"GALACTIC");
+    else if (wcs->c1type[0] == 'E')
+    strcpy (wcs->radecsys,"ECLIPTIC");
+    else if (wcs->c1type[0] == 'S')
+    strcpy (wcs->radecsys,"SGALACTC");
+    else if (wcs->c1type[0] == 'H')
+    strcpy (wcs->radecsys,"HELIOECL");
+    else if (wcs->c1type[0] == 'A')
+    strcpy (wcs->radecsys,"ALTAZ");
+    else if (wcs->c1type[0] == 'L')
+    strcpy (wcs->radecsys,"LINEAR");
+
+    wcs->syswcs = wcscsys (wcs->radecsys);
+
+    return;
+}
+
+/* Jun 11 1998	Split off header-dependent WCS initialization from other subs
+ * Jun 15 1998	Fix major bug in wcsinit() when synthesizing WCS from header
+ * Jun 18 1998	Fix bug in CD initialization; split PC initialization off
+ * Jun 18 1998	Split PC initialization off into subroutine wcspcset()
+ * Jun 24 1998	Set equinox from RADECSYS only if EQUINOX and EPOCH not present
+ * Jul  6 1998  Read third and fourth axis CTYPEs
+ * Jul  7 1998  Initialize eqin and eqout to equinox,
+ * Jul  9 1998	Initialize rotation matrices correctly
+ * Jul 13 1998	Initialize rotation, scale for polynomial and DSS projections
+ * Aug  6 1998	Fix CROTA computation for DSS projection
+ * Sep  4 1998	Fix CROTA, CDELT computation for DSS and polynomial projections
+ * Sep 14 1998	If DATE-OBS not found, check for DATE
+ * Sep 14 1998	If B or J present in EQUINOX, use that info to set system
+ * Sep 29 1998  Initialize additional WCS commands from the environment
+ * Sep 29 1998	Fix bug which read DATE as number rather than formatted date
+ * Dec  2 1998	Read projection constants from header (bug fix)
+ *
+ * Feb  9 1999	Set rotation angle correctly when using DSS projection
+ * Feb 19 1999	Fill in CDELTs from scale keyword if absent or zero
+ * Feb 19 1999	Add PIXSCALE as possible default arcseconds per pixel
+ * Apr  7 1999	Add error checking for NAXIS and NAXIS1 keywords
+ * Apr  7 1999	Do not set systring if epoch is 0 and not RA/Dec
+ * Jul  8 1999	In RADECSYS, use FK5 and FK4 instead of J2000 and B1950
+ * Oct 15 1999	Free wcs using wcsfree()
+ * Oct 20 1999	Add multiple WCS support using new subroutine names
+ * Oct 21 1999	Delete unused variables after lint; declare dsspos()
+ * Nov  9 1999	Add wcschar() to check WCSNAME keywords for desired WCS
+ * Nov  9 1999	Check WCSPREx keyword to find out if chained WCS's
+ *
+ * Jan  6 1999	Add wcsinitn() to initialize from specific WCSNAME
+ * Jan 24 2000  Set CD matrix from header even if using polynomial
+ * Jan 27 2000  Fix MJD to epoch conversion for when MJD-OBS is the only date
+ * Jan 28 2000  Set CD matrix for DSS projection, too
+ * Jan 28 2000	Use wcsproj instead of oldwcs
+ * Dec 18 2000	Fix error in hgets() call in wcschar()
+ * Dec 29 2000  Compute inverse CD matrix even if polynomial solution
+ * Dec 29 2000  Add PROJR0 keyword for WCSLIB projections
+ * Dec 29 2000  Use CDi_j matrix if any elements are present
+ *
+ * Jan 31 2001	Fix to allow 1D WCS
+ * Jan 31 2001	Treat single character WCS name as WCS character
+ * Feb 20 2001	Implement WCSDEPx nested WCS's
+ * Feb 23 2001	Initialize all 4 terms of CD matrix
+ * Feb 28 2001	Fix bug which read CRPIX1 into CRPIX2
+ * Mar 20 2001	Compare mchar to (char)0, not null
+ * Mar 21 2001	Move ic declaration into commented out code
+ * Jul 12 2001	Read PROJPn constants into proj.p array instead of PVn
+ * Sep  7 2001	Set system to galactic or ecliptic based on CTYPE, not RADECSYS
+ * Oct 11 2001	Set ctype[0] as well as ctype[1] to TAN for TNX projections
+ * Oct 19 2001	WCSDIM keyword overrides zero value of NAXIS
+ *
+ * Feb 19 2002	Add XPIXSIZE/YPIXSIZE (KPNO) as default image scale keywords
+ * Mar 12 2002	Add LONPOLE as well as LONGPOLE for WCSLIB 2.8
+ * Apr  3 2002	Implement hget8c() and hgetsc() to simplify code
+ * Apr  3 2002	Add PVj_n projection constants in addition to PROJPn
+ * Apr 19 2002	Increase numeric keyword value length from 16 to 31
+ * Apr 19 2002	Fix bug which didn't set radecsys keyword name
+ * Apr 24 2002	If no WCS present for specified letter, return null
+ * Apr 26 2002	Implement WCSAXESa keyword as first choice for number of axes
+ * Apr 26 2002	Add wcschar and wcsname to WCS structure
+ * May  9 2002	Add radvel and zvel to WCS structure
+ * May 13 2002	Free everything which is allocated
+ * May 28 2002	Read 10 prj.p instead of maximum of 100
+ * May 31 2002	Fix bugs with PV reading
+ * May 31 2002	Initialize syswcs, sysin, sysout in wcsioset()
+ * Sep 25 2002	Fix subroutine calls for radvel and latpole
+ * Dec  6 2002	Correctly compute pixel at center of image for default CRPIX
+ *
+ * Jan  2 2002	Do not reinitialize projection vector for PV input
+ * Jan  3 2002	For ZPN, read PVi_0 to PVi_9, not PVi_1 to PVi_10
+ * Mar 27 2003	Clean up default center computation
+ * Apr  3 2003	Add input for SIRTF distortion coefficients
+ * May  8 2003	Change PROJP reading to start with 0 instead of 1
+ * May 22 2003	Add ZPX approximation, reading projpn from WATi
+ * May 28 2003	Avoid reinitializing coefficients set by PROJP
+ * Jun 26 2003	Initialize xref and yref to -999.0
+ * Sep 23 2003	Change mgets() to mgetstr() to avoid name collision at UCO Lick
+ * Oct  1 2003	Rename wcs->naxes to wcs->naxis to match WCSLIB 3.2
+ * Nov  3 2003	Initialize distortion coefficients in distortinit() in distort.c
+ * Dec  1 2003	Change p[0,1,2] initializations to p[1,2,3]
+ * Dec  3 2003	Add back wcs->naxes for backward compatibility
+ * Dec  3 2003	Remove unused variables j,m in wcsinitc()
+ * Dec 12 2003	Fix call to setwcserr() with format in it
+ *
+ * Feb 26 2004	Add parameters for ZPX projection
+ *
+ * Jun 22 2005	Drop declaration of variable wcserrmsg which is not used
+ * Nov  9 2005	Use CROTA1 if CTYPE1 is LAT/DEC, CROTA2 if CTYPE2 is LAT/DEC
+ *
+ * Mar  9 2006	Get Epoch of observation from MJD-OBS or DATE-OBS/UT unless DSS
+ * Apr 24 2006	Initialize rotation matrices
+ * Apr 25 2006	Ignore axes with dimension of one
+ * May 19 2006	Initialize all of 9x9 PC matrix; read in loops
+ * Aug 21 2006	Limit naxes to 2 everywhere; RA and DEC should always be 1st
+ * Oct  6 2006	If units are pixels, projection type is PIXEL
+ * Oct 30 2006	Initialize cube face to -1, not a cube projection
+ *
+ * Jan  4 2007	Drop declarations of wcsinitc() and wcsinitn() already in wcs.h
+ * Jan  8 2007	Change WCS letter from char to char*
+ * Feb  1 2007	Read IRAF log wavelength flag DC-FLAG to wcs.logwcs
+ * Feb 15 2007	Check for wcs->wcsproj > 0 instead of CTYPEi != LINEAR or PIXEL
+ * Mar 13 2007	Try for RA, DEC, SECPIX if WCS character is space or null
+ * Apr 27 2007	Ignore axes with TAB WCS for now
+ * Oct 17 2007	Fix bug testing &mchar instead of mchar in if statement
+ *
+ * May  9 2008	Initialize TNX projection when projection types first set
+ * Jun 27 2008	If NAXIS1 and NAXIS2 not present, check for IMAGEW and IMAGEH
+ *
+ * Mar 24 2009	Fix dimension bug if NAXISi not present (fix from John Burns)
+ */
diff --git a/Code/src/libwcs/wcslib.c b/Code/src/libwcs/wcslib.c
new file mode 100644
index 0000000000000000000000000000000000000000..31313e139e2252383ab8715d907557fea3dd8cb4
--- /dev/null
+++ b/Code/src/libwcs/wcslib.c
@@ -0,0 +1,1334 @@
+/*=============================================================================
+*
+*   WCSLIB - an implementation of the FITS WCS proposal.
+*   Copyright (C) 1995-2002, Mark Calabretta
+*
+*   This library is free software; you can redistribute it and/or
+*   modify it under the terms of the GNU Lesser General Public
+*   License as published by the Free Software Foundation; either
+*   version 2 of the License, or (at your option) any later version.
+*
+*   This library is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+*   Lesser General Public License for more details.
+*   
+*   You should have received a copy of the GNU Lesser General Public
+*   License along with this library; if not, write to the Free Software
+*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*   Correspondence concerning WCSLIB may be directed to:
+*      Internet email: mcalabre@atnf.csiro.au
+*      Postal address: Dr. Mark Calabretta,
+*                      Australia Telescope National Facility,
+*                      P.O. Box 76,
+*                      Epping, NSW, 2121,
+*                      AUSTRALIA
+*
+*=============================================================================
+*
+*   C routines which implement the FITS World Coordinate System (WCS)
+*   convention.
+*
+*   Summary of routines
+*   -------------------
+*   wcsfwd() and wcsrev() are high level driver routines for the WCS linear
+*   transformation, spherical coordinate transformation, and spherical
+*   projection routines.
+*
+*   Given either the celestial longitude or latitude plus an element of the
+*   pixel coordinate a hybrid routine, wcsmix(), iteratively solves for the
+*   unknown elements.
+*
+*   An initialization routine, wcsset(), computes indices from the ctype
+*   array but need not be called explicitly - see the explanation of
+*   wcs.flag below.
+*
+*
+*   Initialization routine; wcsset()
+*   --------------------------------
+*   Initializes elements of a wcsprm data structure which holds indices into
+*   the coordinate arrays.  Note that this routine need not be called directly;
+*   it will be invoked by wcsfwd() and wcsrev() if the "flag" structure member
+*   is anything other than a predefined magic value.
+*
+*   Given:
+*      naxis    const int
+*                        Number of image axes.
+*      ctype[][9]
+*               const char
+*                        Coordinate axis types corresponding to the FITS
+*                        CTYPEn header cards.
+*
+*   Returned:
+*      wcs      wcsprm*  Indices for the celestial coordinates obtained
+*                        by parsing the ctype[] array (see below).
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Inconsistent or unrecognized coordinate axis
+*                              types.
+*
+*
+*   Forward transformation; wcsfwd()
+*   --------------------------------
+*   Compute the pixel coordinate for given world coordinates.
+*
+*   Given:
+*      ctype[][9]
+*               const char
+*                        Coordinate axis types corresponding to the FITS
+*                        CTYPEn header cards.
+*
+*   Given or returned:
+*      wcs      wcsprm*  Indices for the celestial coordinates obtained
+*                        by parsing the ctype[] array (see below).
+*
+*   Given:
+*      world    const double[]
+*                        World coordinates.  world[wcs->lng] and
+*                        world[wcs->lat] are the celestial longitude and
+*                        latitude, in degrees.
+*
+*   Given:
+*      crval    const double[]
+*                        Coordinate reference values corresponding to the FITS
+*                        CRVALn header cards (see note 2).
+*
+*   Given and returned:
+*      cel      celprm*  Spherical coordinate transformation parameters (usage
+*                        is described in the prologue to "cel.c").
+*
+*   Returned:
+*      phi,     double*  Longitude and latitude in the native coordinate
+*      theta             system of the projection, in degrees.
+*
+*   Given and returned:
+*      prj      prjprm*  Projection parameters (usage is described in the
+*                        prologue to "proj.c").
+*
+*   Returned:
+*      imgcrd   double[] Image coordinate.  imgcrd[wcs->lng] and
+*                        imgcrd[wcs->lat] are the projected x-, and
+*                        y-coordinates, in "degrees".  For quadcube
+*                        projections with a CUBEFACE axis the face number is
+*                        also returned in imgcrd[wcs->cubeface].
+*
+*   Given and returned:
+*      lin      linprm*  Linear transformation parameters (usage is described
+*                        in the prologue to "lin.c").
+*
+*   Returned:
+*      pixcrd   double[] Pixel coordinate.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Invalid coordinate transformation parameters.
+*                           2: Invalid projection parameters.
+*                           3: Invalid world coordinate.
+*                           4: Invalid linear transformation parameters.
+*
+*
+*   Reverse transformation; wcsrev()
+*   --------------------------------
+*   Compute world coordinates for a given pixel coordinate.
+*
+*   Given:
+*      ctype[][9]
+*               const char
+*                        Coordinate axis types corresponding to the FITS
+*                        CTYPEn header cards.
+*
+*   Given or returned:
+*      wcs      wcsprm*  Indices for the celestial coordinates obtained
+*                        by parsing the ctype[] array (see below).
+*
+*   Given:
+*      pixcrd   const double[]
+*                        Pixel coordinate.
+*
+*   Given and returned:
+*      lin      linprm*  Linear transformation parameters (usage is described
+*                        in the prologue to "lin.c").
+*
+*   Returned:
+*      imgcrd   double[] Image coordinate.  imgcrd[wcs->lng] and
+*                        imgcrd[wcs->lat] are the projected x-, and
+*                        y-coordinates, in "degrees".
+*
+*   Given and returned:
+*      prj      prjprm*  Projection parameters (usage is described in the
+*                        prologue to "proj.c").
+*
+*   Returned:
+*      phi,     double*  Longitude and latitude in the native coordinate
+*      theta             system of the projection, in degrees.
+*
+*   Given:
+*      crval    const double[]
+*                        Coordinate reference values corresponding to the FITS
+*                        CRVALn header cards (see note 2).
+*
+*   Given and returned:
+*      cel      celprm*  Spherical coordinate transformation parameters
+*                        (usage is described in the prologue to "cel.c").
+*
+*   Returned:
+*      world    double[] World coordinates.  world[wcs->lng] and
+*                        world[wcs->lat] are the celestial longitude and
+*                        latitude, in degrees.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Invalid coordinate transformation parameters.
+*                           2: Invalid projection parameters.
+*                           3: Invalid pixel coordinate.
+*                           4: Invalid linear transformation parameters.
+*
+*
+*   Hybrid transformation; wcsmix()
+*   -------------------------------
+*   Given either the celestial longitude or latitude plus an element of the
+*   pixel coordinate solve for the remaining elements by iterating on the
+*   unknown celestial coordinate element using wcsfwd().
+*
+*   Given:
+*      ctype[][9]
+*               const char
+*                        Coordinate axis types corresponding to the FITS
+*                        CTYPEn header cards.
+*
+*   Given or returned:
+*      wcs      wcsprm*  Indices for the celestial coordinates obtained
+*                        by parsing the ctype[] array (see below).
+*
+*   Given:
+*      mixpix   const int
+*                        Which element of the pixel coordinate is given.
+*      mixcel   const int
+*                        Which element of the celestial coordinate is
+*                        given:
+*                           1: Celestial longitude is given in
+*                              world[wcs->lng], latitude returned in
+*                              world[wcs->lat].
+*                           2: Celestial latitude is given in
+*                              world[wcs->lat], longitude returned in
+*                              world[wcs->lng].
+*      vspan[2] const double
+*                        Solution interval for the celestial coordinate, in
+*                        degrees.  The ordering of the two limits is
+*                        irrelevant.  Longitude ranges may be specified with
+*                        any convenient normalization, for example [-120,+120]
+*                        is the same as [240,480], except that the solution
+*                        will be returned with the same normalization, i.e.
+*                        lie within the interval specified.
+*      vstep    const double
+*                        Step size for solution search, in degrees.  If zero,
+*                        a sensible, although perhaps non-optimal default will
+*                        be used.
+*      viter    int
+*                        If a solution is not found then the step size will be
+*                        halved and the search recommenced.  viter controls
+*                        how many times the step size is halved.  The allowed
+*                        range is 5 - 10.
+*
+*   Given and returned:
+*      world    double[] World coordinates.  world[wcs->lng] and
+*                        world[wcs->lat] are the celestial longitude and
+*                        latitude, in degrees.  Which is given and which
+*                        returned depends on the value of mixcel.  All other
+*                        elements are given.
+*
+*   Given:
+*      crval    const double[]
+*                        Coordinate reference values corresponding to the FITS
+*                        CRVALn header cards (see note 2).
+*
+*   Given and returned:
+*      cel      celprm*  Spherical coordinate transformation parameters
+*                        (usage is described in the prologue to "cel.c").
+*
+*   Returned:
+*      phi,     double*  Longitude and latitude in the native coordinate
+*      theta             system of the projection, in degrees.
+*
+*   Given and returned:
+*      prj      prjprm*  Projection parameters (usage is described in the
+*                        prologue to "proj.c").
+*
+*   Returned:
+*      imgcrd   double[] Image coordinate.  imgcrd[wcs->lng] and
+*                        imgcrd[wcs->lat] are the projected x-, and
+*                        y-coordinates, in "degrees".
+*
+*   Given and returned:
+*      lin      linprm*  Linear transformation parameters (usage is described
+*                        in the prologue to "lin.c").
+*
+*   Given and returned:
+*      pixcrd   double[] Pixel coordinate.  The element indicated by mixpix is
+*                        given and the remaining elements are returned.
+*
+*   Function return value:
+*               int      Error status
+*                           0: Success.
+*                           1: Invalid coordinate transformation parameters.
+*                           2: Invalid projection parameters.
+*                           3: Coordinate transformation error.
+*                           4: Invalid linear transformation parameters.
+*                           5: No solution found in the specified interval.
+*
+*
+*   Notes
+*   -----
+*    1) The CTYPEn must in be upper case and there must be 0 or 1 pair of
+*       matched celestial axis types.  The ctype[][9] should be padded with
+*       blanks on the right and null-terminated.
+*
+*    2) Elements of the crval[] array which correspond to celestial axes are
+*       ignored, the reference coordinate values in cel->ref[0] and
+*       cel->ref[1] are the ones used.
+*
+*    3) These functions recognize the NCP projection and convert it to the
+*       equivalent SIN projection.
+*
+*       They also recognize GLS as a synonym for SFL.
+*
+*    4) The quadcube projections (TSC, CSC, QSC) may be represented in FITS in
+*       either of two ways:
+*
+*          a) The six faces may be laid out in one plane and numbered as
+*             follows:
+*
+*                                       0
+*
+*                              4  3  2  1  4  3  2
+*
+*                                       5
+*
+*             Faces 2, 3 and 4 may appear on one side or the other (or both).
+*             The forward routines map faces 2, 3 and 4 to the left but the
+*             inverse routines accept them on either side.
+*
+*          b) The "COBE" convention in which the six faces are stored in a
+*             three-dimensional structure using a "CUBEFACE" axis indexed from
+*             0 to 5 as above.
+*
+*       These routines support both methods; wcsset() determines which is
+*       being used by the presence or absence of a CUBEFACE axis in ctype[].
+*       wcsfwd() and wcsrev() translate the CUBEFACE axis representation to
+*       the single plane representation understood by the lower-level WCSLIB
+*       projection routines.
+*
+*
+*   WCS indexing parameters
+*   -----------------------
+*   The wcsprm struct consists of the following:
+*
+*      int flag
+*         The wcsprm struct contains indexes and other information derived
+*         from the CTYPEn.  Whenever any of the ctype[] are set or changed
+*         this flag must be set to zero to signal the initialization routine,
+*         wcsset() to redetermine the indices.  The flag is set to 999 if
+*         there is no celestial axis pair in the CTYPEn.
+*
+*      char pcode[4]
+*         The WCS projection code.
+*
+*      char lngtyp[5], lattyp[5]
+*         WCS celestial axis types.
+*
+*      int lng,lat
+*         Indices into the imgcrd[], and world[] arrays as described above.
+*         These may also serve as indices for the celestial longitude and
+*         latitude axes in the pixcrd[] array provided that the PC matrix
+*         does not transpose axes.
+*
+*      int cubeface
+*         Index into the pixcrd[] array for the CUBEFACE axis.  This is
+*         optionally used for the quadcube projections where each cube face is
+*         stored on a separate axis.
+*
+*
+*   wcsmix() algorithm
+*   ------------------
+*      Initially the specified solution interval is checked to see if it's a
+*      "crossing" interval.  If it isn't, a search is made for a crossing
+*      solution by iterating on the unknown celestial coordinate starting at
+*      the upper limit of the solution interval and decrementing by the
+*      specified step size.  A crossing is indicated if the trial value of the
+*      pixel coordinate steps through the value specified.  If a crossing
+*      interval is found then the solution is determined by a modified form of
+*      "regula falsi" division of the crossing interval.  If no crossing
+*      interval was found within the specified solution interval then a search
+*      is made for a "non-crossing" solution as may arise from a point of
+*      tangency.  The process is complicated by having to make allowance for
+*      the discontinuities that occur in all map projections.
+*
+*      Once one solution has been determined others may be found by subsequent
+*      invokations of wcsmix() with suitably restricted solution intervals.
+*
+*      Note the circumstance which arises when the solution point lies at a
+*      native pole of a projection in which the pole is represented as a
+*      finite curve, for example the zenithals and conics.  In such cases two
+*      or more valid solutions may exist but WCSMIX only ever returns one.
+*
+*      Because of its generality wcsmix() is very compute-intensive.  For
+*      compute-limited applications more efficient special-case solvers could
+*      be written for simple projections, for example non-oblique cylindrical
+*      projections.
+*
+*   Author: Mark Calabretta, Australia Telescope National Facility
+*   $Id: wcs.c,v 2.23 2002/04/03 01:25:29 mcalabre Exp $
+*===========================================================================*/
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "wcslib.h"
+
+/* Map error number to error message for each function. */
+const char *wcsset_errmsg[] = {
+   0,
+   "Inconsistent or unrecognized coordinate axis types"};
+
+const char *wcsfwd_errmsg[] = {
+   0,
+   "Invalid coordinate transformation parameters",
+   "Invalid projection parameters",
+   "Invalid world coordinate",
+   "Invalid linear transformation parameters"};
+
+const char *wcsrev_errmsg[] = {
+   0,
+   "Invalid coordinate transformation parameters",
+   "Invalid projection parameters",
+   "Invalid pixel coordinate",
+   "Invalid linear transformation parameters"};
+
+const char *wcsmix_errmsg[] = {
+   0,
+   "Invalid coordinate transformation parameters",
+   "Invalid projection parameters",
+   "Coordinate transformation error",
+   "Invalid linear transformation parameters",
+   "No solution found in the specified interval"};
+
+#define signb(X) ((X) < 0.0 ? 1 : 0)
+
+int
+wcsset (naxis, ctype, wcs)
+
+const int naxis;
+const char ctype[][9];
+struct wcsprm *wcs;
+
+{
+   int  nalias = 2;
+   char aliases [2][4] = {"NCP", "GLS"};
+
+   int j, k;
+   int *ndx = NULL;
+   char requir[9];
+
+   strcpy(wcs->pcode, "");
+   strcpy(requir, "");
+   wcs->lng = -1;
+   wcs->lat = -1;
+   wcs->cubeface = -1;
+
+   for (j = 0; j < naxis; j++) {
+      if (ctype[j][4] != '-') {
+         if (strcmp(ctype[j], "CUBEFACE") == 0) {
+            if (wcs->cubeface == -1) {
+               wcs->cubeface = j;
+            } else {
+               /* Multiple CUBEFACE axes! */
+               return 1;
+            }
+         }
+         continue;
+      }
+
+      /* Got an axis qualifier, is it a recognized WCS projection? */
+      for (k = 0; k < npcode; k++) {
+         if (strncmp(&ctype[j][5], pcodes[k], 3) == 0) break;
+      }
+
+      if (k == npcode) {
+         /* Maybe it's a projection alias. */
+         for (k = 0; k < nalias; k++) {
+            if (strncmp(&ctype[j][5], aliases[k], 3) == 0) break;
+         }
+
+         /* Not recognized. */
+         if (k == nalias) {
+            continue;
+         }
+      }
+
+      /* Parse the celestial axis type. */
+      if (strcmp(wcs->pcode, "") == 0) {
+         sprintf(wcs->pcode, "%.3s", &ctype[j][5]);
+
+         if (strncmp(ctype[j], "RA--", 4) == 0) {
+            wcs->lng = j;
+            strcpy(wcs->lngtyp, "RA");
+            strcpy(wcs->lattyp, "DEC");
+            ndx = &wcs->lat;
+            sprintf(requir, "DEC--%s", wcs->pcode);
+         } else if (strncmp(ctype[j], "DEC-", 4) == 0) {
+            wcs->lat = j;
+            strcpy(wcs->lngtyp, "RA");
+            strcpy(wcs->lattyp, "DEC");
+            ndx = &wcs->lng;
+            sprintf(requir, "RA---%s", wcs->pcode);
+         } else if (strncmp(&ctype[j][1], "LON", 3) == 0) {
+            wcs->lng = j;
+            sprintf(wcs->lngtyp, "%cLON", ctype[j][0]);
+            sprintf(wcs->lattyp, "%cLAT", ctype[j][0]);
+            ndx = &wcs->lat;
+            sprintf(requir, "%s-%s", wcs->lattyp, wcs->pcode);
+         } else if (strncmp(&ctype[j][1], "LAT", 3) == 0) {
+            wcs->lat = j;
+            sprintf(wcs->lngtyp, "%cLON", ctype[j][0]);
+            sprintf(wcs->lattyp, "%cLAT", ctype[j][0]);
+            ndx = &wcs->lng;
+            sprintf(requir, "%s-%s", wcs->lngtyp, wcs->pcode);
+         } else if (strncmp(&ctype[j][2], "LN", 2) == 0) {
+            wcs->lng = j;
+            sprintf(wcs->lngtyp, "%c%cLN", ctype[j][0], ctype[j][1]);
+            sprintf(wcs->lattyp, "%c%cLT", ctype[j][0], ctype[j][1]);
+            ndx = &wcs->lat;
+            sprintf(requir, "%s-%s", wcs->lattyp, wcs->pcode);
+         } else if (strncmp(&ctype[j][2], "LT", 2) == 0) {
+            wcs->lat = j;
+            sprintf(wcs->lngtyp, "%c%cLN", ctype[j][0], ctype[j][1]);
+            sprintf(wcs->lattyp, "%c%cLT", ctype[j][0], ctype[j][1]);
+            ndx = &wcs->lng;
+            sprintf(requir, "%s-%s", wcs->lngtyp, wcs->pcode);
+         } else {
+            /* Unrecognized celestial type. */
+            return 1;
+         }
+      } else {
+         if (strncmp(ctype[j], requir, 8) != 0) {
+            /* Inconsistent projection types. */
+            return 1;
+         }
+
+	if (ndx == NULL)
+	    return 1;
+         *ndx = j;
+         strcpy(requir, "");
+      }
+   }
+
+   if (strcmp(requir, "")) {
+      /* Unmatched celestial axis. */
+      return 1;
+   }
+
+   /* Do simple alias translations. */
+   if (strncmp(wcs->pcode, "GLS", 3) == 0) {
+      strcpy(wcs->pcode, "SFL");
+   }
+
+   if (strcmp(wcs->pcode, "")) {
+      wcs->flag = WCSSET;
+   } else {
+      /* Signal for no celestial axis pair. */
+      wcs->flag = 999;
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int
+wcsfwd(ctype, wcs, world, crval, cel, phi, theta, prj, imgcrd, lin, pixcrd)
+
+const char ctype[][9];
+struct wcsprm* wcs;
+const double world[];
+const double crval[];
+struct celprm *cel;
+double *phi, *theta;
+struct prjprm *prj;
+double imgcrd[];
+struct linprm *lin;
+double pixcrd[];
+
+{
+   int    err, j;
+   double offset;
+
+   /* Initialize if required. */
+   if (wcs->flag != WCSSET) {
+      if (wcsset(lin->naxis, ctype, wcs)) return 1;
+   }
+
+   /* Convert to relative physical coordinates. */
+   for (j = 0; j < lin->naxis; j++) {
+      if (j == wcs->lng) continue;
+      if (j == wcs->lat) continue;
+      imgcrd[j] = world[j] - crval[j];
+   }
+
+   if (wcs->flag != 999) {
+      /* Compute projected coordinates. */
+      if (strcmp(wcs->pcode, "NCP") == 0) {
+         /* Convert NCP to SIN. */
+         if (cel->ref[1] == 0.0) {
+            return 2;
+         }
+
+         strcpy(wcs->pcode, "SIN");
+         prj->p[1] = 0.0;
+         prj->p[2] = cosdeg (cel->ref[1])/sindeg (cel->ref[1]);
+         prj->flag = (prj->flag < 0) ? -1 : 0;
+      }
+
+      if ((err = celfwd(wcs->pcode, world[wcs->lng], world[wcs->lat], cel,
+                   phi, theta, prj, &imgcrd[wcs->lng], &imgcrd[wcs->lat]))) {
+         return err;
+      }
+
+      /* Do we have a CUBEFACE axis? */
+      if (wcs->cubeface != -1) {
+         /* Separation between faces. */
+         if (prj->r0 == 0.0) {
+            offset = 90.0;
+         } else {
+            offset = prj->r0*PI/2.0;
+         }
+
+         /* Stack faces in a cube. */
+         if (imgcrd[wcs->lat] < -0.5*offset) {
+            imgcrd[wcs->lat] += offset;
+            imgcrd[wcs->cubeface] = 5.0;
+         } else if (imgcrd[wcs->lat] > 0.5*offset) {
+            imgcrd[wcs->lat] -= offset;
+            imgcrd[wcs->cubeface] = 0.0;
+         } else if (imgcrd[wcs->lng] > 2.5*offset) {
+            imgcrd[wcs->lng] -= 3.0*offset;
+            imgcrd[wcs->cubeface] = 4.0;
+         } else if (imgcrd[wcs->lng] > 1.5*offset) {
+            imgcrd[wcs->lng] -= 2.0*offset;
+            imgcrd[wcs->cubeface] = 3.0;
+         } else if (imgcrd[wcs->lng] > 0.5*offset) {
+            imgcrd[wcs->lng] -= offset;
+            imgcrd[wcs->cubeface] = 2.0;
+         } else {
+            imgcrd[wcs->cubeface] = 1.0;
+         }
+      }
+   }
+
+   /* Apply forward linear transformation. */
+   if (linfwd(imgcrd, lin, pixcrd)) {
+      return 4;
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int
+wcsrev(ctype, wcs, pixcrd, lin, imgcrd, prj, phi, theta, crval, cel, world)
+
+const char ctype[][9];
+struct wcsprm *wcs;
+const double pixcrd[];
+struct linprm *lin;
+double imgcrd[];
+struct prjprm *prj;
+double *phi, *theta;
+const double crval[];
+struct celprm *cel;
+double world[];
+
+{
+   int    err, face, j;
+   double offset;
+
+   /* Initialize if required. */
+   if (wcs->flag != WCSSET) {
+      if (wcsset(lin->naxis, ctype, wcs)) return 1;
+   }
+
+   /* Apply reverse linear transformation. */
+   if (linrev(pixcrd, lin, imgcrd)) {
+      return 4;
+   }
+
+   /* Convert to world coordinates. */
+   for (j = 0; j < lin->naxis; j++) {
+      if (j == wcs->lng) continue;
+      if (j == wcs->lat) continue;
+      world[j] = imgcrd[j] + crval[j];
+   }
+
+
+   if (wcs->flag != 999) {
+      /* Do we have a CUBEFACE axis? */
+      if (wcs->cubeface != -1) {
+         face = (int)(imgcrd[wcs->cubeface] + 0.5);
+         if (fabs(imgcrd[wcs->cubeface]-face) > 1e-10) {
+            return 3;
+         }
+
+         /* Separation between faces. */
+         if (prj->r0 == 0.0) {
+            offset = 90.0;
+         } else {
+            offset = prj->r0*PI/2.0;
+         }
+
+         /* Lay out faces in a plane. */
+         switch (face) {
+         case 0:
+            imgcrd[wcs->lat] += offset;
+            break;
+         case 1:
+            break;
+         case 2:
+            imgcrd[wcs->lng] += offset;
+            break;
+         case 3:
+            imgcrd[wcs->lng] += offset*2;
+            break;
+         case 4:
+            imgcrd[wcs->lng] += offset*3;
+            break;
+         case 5:
+            imgcrd[wcs->lat] -= offset;
+            break;
+         default:
+            return 3;
+         }
+      }
+
+      /* Compute celestial coordinates. */
+      if (strcmp(wcs->pcode, "NCP") == 0) {
+         /* Convert NCP to SIN. */
+         if (cel->ref[1] == 0.0) {
+            return 2;
+         }
+
+         strcpy(wcs->pcode, "SIN");
+         prj->p[1] = 0.0;
+         prj->p[2] = cosdeg (cel->ref[1])/sindeg (cel->ref[1]);
+         prj->flag = (prj->flag < 0) ? -1 : 0;
+      }
+
+      if ((err = celrev(wcs->pcode, imgcrd[wcs->lng], imgcrd[wcs->lat], prj,
+                   phi, theta, cel, &world[wcs->lng], &world[wcs->lat]))) {
+         return err;
+      }
+   }
+
+   return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+int
+wcsmix(ctype, wcs, mixpix, mixcel, vspan, vstep, viter, world, crval, cel,
+           phi, theta, prj, imgcrd, lin, pixcrd)
+
+const char ctype[][9];
+struct wcsprm *wcs;
+const int mixpix, mixcel;
+const double vspan[2], vstep;
+int viter;
+double world[];
+const double crval[];
+struct celprm *cel;
+double *phi, *theta;
+struct prjprm *prj;
+double imgcrd[];
+struct linprm *lin;
+double pixcrd[];
+
+{
+   const int niter = 60;
+   int    crossed, err, istep, iter, j, k, nstep, retry;
+   const double tol  = 1.0e-10;
+   const double tol2 = 100.0*tol;
+   double lambda, span[2], step;
+   double pixmix;
+   double dlng, lng, lng0, lng0m, lng1, lng1m;
+   double dlat, lat, lat0, lat0m, lat1, lat1m;
+   double d, d0, d0m, d1, d1m;
+   double dx = 0.0;
+   double dabs, dmin, lmin;
+   double dphi, phi0, phi1;
+   struct celprm cel0;
+
+   /* Initialize if required. */
+   if (wcs->flag != WCSSET) {
+      if (wcsset(lin->naxis, ctype, wcs)) return 1;
+   }
+
+   /* Check vspan. */
+   if (vspan[0] <= vspan[1]) {
+      span[0] = vspan[0];
+      span[1] = vspan[1];
+   } else {
+      /* Swap them. */
+      span[0] = vspan[1];
+      span[1] = vspan[0];
+   }
+
+   /* Check vstep. */
+   step = fabs(vstep);
+   if (step == 0.0) {
+      step = (span[1] - span[0])/10.0;
+      if (step > 1.0 || step == 0.0) step = 1.0;
+   }
+
+   /* Check viter. */
+   nstep = viter;
+   if (nstep < 5) {
+      nstep = 5;
+   } else if (nstep > 10) {
+      nstep = 10;
+   }
+
+   /* Given pixel element. */
+   pixmix = pixcrd[mixpix];
+
+   /* Iterate on the step size. */
+   for (istep = 0; istep <= nstep; istep++) {
+      if (istep) step /= 2.0;
+
+      /* Iterate on the sky coordinate between the specified range. */
+      if (mixcel == 1) {
+         /* Celestial longitude is given. */
+
+         /* Check whether the solution interval is a crossing interval. */
+         lat0 = span[0];
+         world[wcs->lat] = lat0;
+         if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta, prj,
+                          imgcrd, lin, pixcrd))) {
+            return err;
+         }
+         d0 = pixcrd[mixpix] - pixmix;
+
+         dabs = fabs(d0);
+         if (dabs < tol) return 0;
+
+         lat1 = span[1];
+         world[wcs->lat] = lat1;
+         if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta, prj,
+                          imgcrd, lin, pixcrd))) {
+            return err;
+         }
+         d1 = pixcrd[mixpix] - pixmix;
+
+         dabs = fabs(d1);
+         if (dabs < tol) return 0;
+
+         lmin = lat1;
+         dmin = dabs;
+
+         /* Check for a crossing point. */
+         if (signb(d0) != signb(d1)) {
+            crossed = 1;
+            dx = d1;
+         } else {
+            crossed = 0;
+            lat0 = span[1];
+         }
+
+         for (retry = 0; retry < 4; retry++) {
+            /* Refine the solution interval. */
+            while (lat0 > span[0]) {
+               lat0 -= step;
+               if (lat0 < span[0]) lat0 = span[0];
+               world[wcs->lat] = lat0;
+               if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                prj, imgcrd, lin, pixcrd))) {
+                  return err;
+               }
+               d0 = pixcrd[mixpix] - pixmix;
+
+               /* Check for a solution. */
+               dabs = fabs(d0);
+               if (dabs < tol) return 0;
+
+               /* Record the point of closest approach. */
+               if (dabs < dmin) {
+                  lmin = lat0;
+                  dmin = dabs;
+               }
+
+               /* Check for a crossing point. */
+               if (signb(d0) != signb(d1)) {
+                  crossed = 2;
+                  dx = d0;
+                  break;
+               }
+
+               /* Advance to the next subinterval. */
+               lat1 = lat0;
+               d1 = d0;
+            }
+
+            if (crossed) {
+               /* A crossing point was found. */
+               for (iter = 0; iter < niter; iter++) {
+                  /* Use regula falsi division of the interval. */
+                  lambda = d0/(d0-d1);
+                  if (lambda < 0.1) {
+                     lambda = 0.1;
+                  } else if (lambda > 0.9) {
+                     lambda = 0.9;
+                  }
+
+                  dlat = lat1 - lat0;
+                  lat = lat0 + lambda*dlat;
+                  world[wcs->lat] = lat;
+                  if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                   prj, imgcrd, lin, pixcrd))) {
+                     return err;
+                  }
+
+                  /* Check for a solution. */
+                  d = pixcrd[mixpix] - pixmix;
+                  dabs = fabs(d);
+                  if (dabs < tol) return 0;
+
+                  if (dlat < tol) {
+                     /* An artifact of numerical imprecision. */
+                     if (dabs < tol2) return 0;
+
+                     /* Must be a discontinuity. */
+                     break;
+                  }
+
+                  /* Record the point of closest approach. */
+                  if (dabs < dmin) {
+                     lmin = lat;
+                     dmin = dabs;
+                  }
+
+                  if (signb(d0) == signb(d)) {
+                     lat0 = lat;
+                     d0 = d;
+                  } else {
+                     lat1 = lat;
+                     d1 = d;
+                  }
+               }
+
+               /* No convergence, must have been a discontinuity. */
+               if (crossed == 1) lat0 = span[1];
+               lat1 = lat0;
+               d1 = dx;
+               crossed = 0;
+
+            } else {
+               /* No crossing point; look for a tangent point. */
+               if (lmin == span[0]) break;
+               if (lmin == span[1]) break;
+
+               lat = lmin;
+               lat0 = lat - step;
+               if (lat0 < span[0]) lat0 = span[0];
+               lat1 = lat + step;
+               if (lat1 > span[1]) lat1 = span[1];
+
+               world[wcs->lat] = lat0;
+               if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                prj, imgcrd, lin, pixcrd))) {
+                  return err;
+               }
+               d0 = fabs(pixcrd[mixpix] - pixmix);
+
+               d  = dmin;
+
+               world[wcs->lat] = lat1;
+               if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                prj, imgcrd, lin, pixcrd))) {
+                  return err;
+               }
+               d1 = fabs(pixcrd[mixpix] - pixmix);
+
+               for (iter = 0; iter < niter; iter++) {
+                  lat0m = (lat0 + lat)/2.0;
+                  world[wcs->lat] = lat0m;
+                  if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                   prj, imgcrd, lin, pixcrd))) {
+                     return err;
+                  }
+                  d0m = fabs(pixcrd[mixpix] - pixmix);
+
+                  if (d0m < tol) return 0;
+
+                  lat1m = (lat1 + lat)/2.0;
+                  world[wcs->lat] = lat1m;
+                  if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                   prj, imgcrd, lin, pixcrd))) {
+                     return err;
+                  }
+                  d1m = fabs(pixcrd[mixpix] - pixmix);
+
+                  if (d1m < tol) return 0;
+
+                  if (d0m < d && d0m <= d1m) {
+                     lat1 = lat;
+                     d1   = d;
+                     lat  = lat0m;
+                     d    = d0m;
+                  } else if (d1m < d) {
+                     lat0 = lat;
+                     d0   = d;
+                     lat  = lat1m;
+                     d    = d1m;
+                  } else {
+                     lat0 = lat0m;
+                     d0   = d0m;
+                     lat1 = lat1m;
+                     d1   = d1m;
+                  }
+               }
+            }
+         }
+
+      } else {
+         /* Celestial latitude is given. */
+
+         /* Check whether the solution interval is a crossing interval. */
+         lng0 = span[0];
+         world[wcs->lng] = lng0;
+         if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta, prj,
+                          imgcrd, lin, pixcrd))) {
+            return err;
+         }
+         d0 = pixcrd[mixpix] - pixmix;
+
+         dabs = fabs(d0);
+         if (dabs < tol) return 0;
+
+         lng1 = span[1];
+         world[wcs->lng] = lng1;
+         if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta, prj,
+                          imgcrd, lin, pixcrd))) {
+            return err;
+         }
+         d1 = pixcrd[mixpix] - pixmix;
+
+         dabs = fabs(d1);
+         if (dabs < tol) return 0;
+         lmin = lng1;
+         dmin = dabs;
+
+         /* Check for a crossing point. */
+         if (signb(d0) != signb(d1)) {
+            crossed = 1;
+            dx = d1;
+         } else {
+            crossed = 0;
+            lng0 = span[1];
+         }
+
+         for (retry = 0; retry < 4; retry++) {
+            /* Refine the solution interval. */
+            while (lng0 > span[0]) {
+               lng0 -= step;
+               if (lng0 < span[0]) lng0 = span[0];
+               world[wcs->lng] = lng0;
+               if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                          prj, imgcrd, lin, pixcrd))) {
+                  return err;
+               }
+               d0 = pixcrd[mixpix] - pixmix;
+
+               /* Check for a solution. */
+               dabs = fabs(d0);
+               if (dabs < tol) return 0;
+
+               /* Record the point of closest approach. */
+               if (dabs < dmin) {
+                  lmin = lng0;
+                  dmin = dabs;
+               }
+
+               /* Check for a crossing point. */
+               if (signb(d0) != signb(d1)) {
+                  crossed = 2;
+                  dx = d0;
+                  break;
+               }
+
+               /* Advance to the next subinterval. */
+               lng1 = lng0;
+               d1 = d0;
+            }
+
+            if (crossed) {
+               /* A crossing point was found. */
+               for (iter = 0; iter < niter; iter++) {
+                  /* Use regula falsi division of the interval. */
+                  lambda = d0/(d0-d1);
+                  if (lambda < 0.1) {
+                     lambda = 0.1;
+                  } else if (lambda > 0.9) {
+                     lambda = 0.9;
+                  }
+
+                  dlng = lng1 - lng0;
+                  lng = lng0 + lambda*dlng;
+                  world[wcs->lng] = lng;
+                  if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                   prj, imgcrd, lin, pixcrd))) {
+                     return err;
+                  }
+
+                  /* Check for a solution. */
+                  d = pixcrd[mixpix] - pixmix;
+                  dabs = fabs(d);
+                  if (dabs < tol) return 0;
+
+                  if (dlng < tol) {
+                     /* An artifact of numerical imprecision. */
+                     if (dabs < tol2) return 0;
+
+                     /* Must be a discontinuity. */
+                     break;
+                  }
+
+                  /* Record the point of closest approach. */
+                  if (dabs < dmin) {
+                     lmin = lng;
+                     dmin = dabs;
+                  }
+
+                  if (signb(d0) == signb(d)) {
+                     lng0 = lng;
+                     d0 = d;
+                  } else {
+                     lng1 = lng;
+                     d1 = d;
+                  }
+               }
+
+               /* No convergence, must have been a discontinuity. */
+               if (crossed == 1) lng0 = span[1];
+               lng1 = lng0;
+               d1 = dx;
+               crossed = 0;
+
+            } else {
+               /* No crossing point; look for a tangent point. */
+               if (lmin == span[0]) break;
+               if (lmin == span[1]) break;
+
+               lng = lmin;
+               lng0 = lng - step;
+               if (lng0 < span[0]) lng0 = span[0];
+               lng1 = lng + step;
+               if (lng1 > span[1]) lng1 = span[1];
+
+               world[wcs->lng] = lng0;
+               if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                prj, imgcrd, lin, pixcrd))) {
+                  return err;
+               }
+               d0 = fabs(pixcrd[mixpix] - pixmix);
+
+               d  = dmin;
+
+               world[wcs->lng] = lng1;
+               if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                prj, imgcrd, lin, pixcrd))) {
+                  return err;
+               }
+               d1 = fabs(pixcrd[mixpix] - pixmix);
+
+               for (iter = 0; iter < niter; iter++) {
+                  lng0m = (lng0 + lng)/2.0;
+                  world[wcs->lng] = lng0m;
+                  if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                   prj, imgcrd, lin, pixcrd))) {
+                     return err;
+                  }
+                  d0m = fabs(pixcrd[mixpix] - pixmix);
+
+                  if (d0m < tol) return 0;
+
+                  lng1m = (lng1 + lng)/2.0;
+                  world[wcs->lng] = lng1m;
+                  if ((err = wcsfwd(ctype, wcs, world, crval, cel, phi, theta,
+                                   prj, imgcrd, lin, pixcrd))) {
+                     return err;
+                  }
+                  d1m = fabs(pixcrd[mixpix] - pixmix);
+
+                  if (d1m < tol) return 0;
+
+                  if (d0m < d && d0m <= d1m) {
+                     lng1 = lng;
+                     d1   = d;
+                     lng  = lng0m;
+                     d    = d0m;
+                  } else if (d1m < d) {
+                     lng0 = lng;
+                     d0   = d;
+                     lng  = lng1m;
+                     d    = d1m;
+                  } else {
+                     lng0 = lng0m;
+                     d0   = d0m;
+                     lng1 = lng1m;
+                     d1   = d1m;
+                  }
+               }
+            }
+         }
+      }
+   }
+
+
+   /* Set cel0 to the unity transformation. */
+   cel0.flag = CELSET;
+   cel0.ref[0] = cel->ref[0];
+   cel0.ref[1] = cel->ref[1];
+   cel0.ref[2] = cel->ref[2];
+   cel0.ref[3] = cel->ref[3];
+   cel0.euler[0] = -90.0;
+   cel0.euler[1] =   0.0;
+   cel0.euler[2] =  90.0;
+   cel0.euler[3] =   1.0;
+   cel0.euler[4] =   0.0;
+
+   /* No convergence, check for aberrant behaviour at a native pole. */
+   *theta = -90.0;
+   for (j = 1; j <= 2; j++) {
+      /* Could the celestial coordinate element map to a native pole? */
+      *theta = -*theta;
+      err = sphrev(0.0, *theta, cel->euler, &lng, &lat);
+
+      if (mixcel == 1) {
+         if (fabs(fmod(world[wcs->lng]-lng,360.0)) > tol) continue;
+         if (lat < span[0]) continue;
+         if (lat > span[1]) continue;
+         world[wcs->lat] = lat;
+      } else {
+         if (fabs(world[wcs->lat]-lat) > tol) continue;
+         if (lng < span[0]) lng += 360.0;
+         if (lng > span[1]) lng -= 360.0;
+         if (lng < span[0]) continue;
+         if (lng > span[1]) continue;
+         world[wcs->lng] = lng;
+      }
+
+      /* Is there a solution for the given pixel coordinate element? */
+      lng = world[wcs->lng];
+      lat = world[wcs->lat];
+
+      /* Feed native coordinates to wcsfwd() with cel0 set to unity. */
+      world[wcs->lng] = -180.0;
+      world[wcs->lat] = *theta;
+      if ((err = wcsfwd(ctype, wcs, world, crval, &cel0, phi, theta, prj,
+                       imgcrd, lin, pixcrd))) {
+         return err;
+      }
+      d0 = pixcrd[mixpix] - pixmix;
+
+      /* Check for a solution. */
+      if (fabs(d0) < tol) {
+         /* Recall saved world coordinates. */
+         world[wcs->lng] = lng;
+         world[wcs->lat] = lat;
+         return 0;
+      }
+
+      /* Search for a crossing interval. */
+      phi0 = -180.0;
+      for (k = -179; k <= 180; k++) {
+         phi1 = (double) k;
+         world[wcs->lng] = phi1;
+         if ((err = wcsfwd(ctype, wcs, world, crval, &cel0, phi, theta, prj,
+                          imgcrd, lin, pixcrd))) {
+            return err;
+         }
+         d1 = pixcrd[mixpix] - pixmix;
+
+         /* Check for a solution. */
+         dabs = fabs(d1);
+         if (dabs < tol) {
+            /* Recall saved world coordinates. */
+            world[wcs->lng] = lng;
+            world[wcs->lat] = lat;
+            return 0;
+         }
+
+         /* Is it a crossing interval? */
+         if (signb(d0) != signb(d1)) break;
+
+         phi0 = phi1;
+         d0 = d1;
+      }
+
+      for (iter = 1; iter <= niter; iter++) {
+         /* Use regula falsi division of the interval. */
+         lambda = d0/(d0-d1);
+         if (lambda < 0.1) {
+            lambda = 0.1;
+         } else if (lambda > 0.9) {
+            lambda = 0.9;
+         }
+
+         dphi = phi1 - phi0;
+         world[wcs->lng] = phi0 + lambda*dphi;
+         if ((err = wcsfwd(ctype, wcs, world, crval, &cel0, phi, theta, prj,
+                          imgcrd, lin, pixcrd))) {
+            return err;
+         }
+
+         /* Check for a solution. */
+         d = pixcrd[mixpix] - pixmix;
+         dabs = fabs(d);
+         if (dabs < tol || (dphi < tol && dabs < tol2)) {
+            /* Recall saved world coordinates. */
+            world[wcs->lng] = lng;
+            world[wcs->lat] = lat;
+            return 0;
+         }
+
+         if (signb(d0) == signb(d)) {
+            phi0 = world[wcs->lng];
+            d0 = d;
+         } else {
+            phi1 = world[wcs->lng];
+            d1 = d;
+         }
+      }
+   }
+
+
+   /* No solution. */
+   return 5;
+
+}
+/* Dec 20 1999  Doug Mink - Change signbit() to signb() and always define it
+ * Dec 20 1999	Doug Mink - Include wcslib.h, which includes wcs.h, wcstrig.h
+ *
+ * Mar 20 2001	Doug Mink - Include stdio.h for sprintf()
+ * Mar 20 2001	Doug Mink - Add () around err assignments in if statements
+ * Sep 19 2001	Doug Mink - Add above changes to WCSLIB-2.7 version
+ *
+ * Mar 15 2002	Doug Mink - Add above changes to WCSLIB-2.8.2
+ * Apr  3 2002	Mark Calabretta - Fix bug in code checking section
+ *
+ * Jun 20 2006	Doug Mink - Initialized uninitialized variables
+ */
diff --git a/Code/src/libwcs/wcslib.h b/Code/src/libwcs/wcslib.h
new file mode 100644
index 0000000000000000000000000000000000000000..9bca932241af7bdfb8a7617a14a7c4d535c5f661
--- /dev/null
+++ b/Code/src/libwcs/wcslib.h
@@ -0,0 +1,390 @@
+#ifndef wcslib_h_
+#define wcslib_h_
+
+/*=============================================================================
+*
+*   WCSLIB - an implementation of the FITS WCS proposal.
+*   Copyright (C) 1995-2002, Mark Calabretta
+*
+*   This library is free software; you can redistribute it and/or
+*   modify it under the terms of the GNU Lesser General Public
+*   License as published by the Free Software Foundation; either
+*   version 2 of the License, or (at your option) any later version.
+*
+*   This library is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+*   Lesser General Public License for more details.
+*   
+*   You should have received a copy of the GNU Lesser General Public
+*   License along with this library; if not, write to the Free Software
+*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*   Correspondence concerning WCSLIB may be directed to:
+*      Internet email: mcalabre@atnf.csiro.au
+*      Postal address: Dr. Mark Calabretta,
+*                      Australia Telescope National Facility,
+*                      P.O. Box 76,
+*                      Epping, NSW, 2121,
+*                      AUSTRALIA
+*
+*   Author: Mark Calabretta, Australia Telescope National Facility
+*   $Id: wcs.h,v 2.9 2002/04/03 01:25:29 mcalabre Exp $
+*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined(__STDC__) && !defined(__cplusplus)
+#ifndef const
+#define const
+#endif
+#endif
+
+extern int npcode;
+extern char pcodes[26][4];
+
+struct prjprm {
+   char   code[4];
+   int flag;
+   double phi0, theta0;
+   double r0;
+   double p[10];
+   double w[20];
+   int    n;
+
+#if __STDC__  || defined(__cplusplus)
+   int (*prjfwd)(const double, const double,
+                 struct prjprm *,
+                 double *, double *);
+   int (*prjrev)(const double, const double,
+                 struct prjprm *,
+                 double *, double *);
+#else
+   int (*prjfwd)();
+   int (*prjrev)();
+#endif
+};
+
+#if __STDC__ || defined(__cplusplus)
+   int prjset(const char [], struct prjprm *);
+   int prjfwd(const double, const double, struct prjprm *, double *, double *);
+   int prjrev(const double, const double, struct prjprm *, double *, double *);
+   int azpset(struct prjprm *);
+   int azpfwd(const double, const double, struct prjprm *, double *, double *);
+   int azprev(const double, const double, struct prjprm *, double *, double *);
+   int szpset(struct prjprm *);
+   int szpfwd(const double, const double, struct prjprm *, double *, double *);
+   int szprev(const double, const double, struct prjprm *, double *, double *);
+   int tanset(struct prjprm *);
+   int tanfwd(const double, const double, struct prjprm *, double *, double *);
+   int tanrev(const double, const double, struct prjprm *, double *, double *);
+   int stgset(struct prjprm *);
+   int stgfwd(const double, const double, struct prjprm *, double *, double *);
+   int stgrev(const double, const double, struct prjprm *, double *, double *);
+   int sinset(struct prjprm *);
+   int sinfwd(const double, const double, struct prjprm *, double *, double *);
+   int sinrev(const double, const double, struct prjprm *, double *, double *);
+   int arcset(struct prjprm *);
+   int arcfwd(const double, const double, struct prjprm *, double *, double *);
+   int arcrev(const double, const double, struct prjprm *, double *, double *);
+   int zpnset(struct prjprm *);
+   int zpnfwd(const double, const double, struct prjprm *, double *, double *);
+   int zpnrev(const double, const double, struct prjprm *, double *, double *);
+   int zeaset(struct prjprm *);
+   int zeafwd(const double, const double, struct prjprm *, double *, double *);
+   int zearev(const double, const double, struct prjprm *, double *, double *);
+   int airset(struct prjprm *);
+   int airfwd(const double, const double, struct prjprm *, double *, double *);
+   int airrev(const double, const double, struct prjprm *, double *, double *);
+   int cypset(struct prjprm *);
+   int cypfwd(const double, const double, struct prjprm *, double *, double *);
+   int cyprev(const double, const double, struct prjprm *, double *, double *);
+   int ceaset(struct prjprm *);
+   int ceafwd(const double, const double, struct prjprm *, double *, double *);
+   int cearev(const double, const double, struct prjprm *, double *, double *);
+   int carset(struct prjprm *);
+   int carfwd(const double, const double, struct prjprm *, double *, double *);
+   int carrev(const double, const double, struct prjprm *, double *, double *);
+   int merset(struct prjprm *);
+   int merfwd(const double, const double, struct prjprm *, double *, double *);
+   int merrev(const double, const double, struct prjprm *, double *, double *);
+   int sflset(struct prjprm *);
+   int sflfwd(const double, const double, struct prjprm *, double *, double *);
+   int sflrev(const double, const double, struct prjprm *, double *, double *);
+   int parset(struct prjprm *);
+   int parfwd(const double, const double, struct prjprm *, double *, double *);
+   int parrev(const double, const double, struct prjprm *, double *, double *);
+   int molset(struct prjprm *);
+   int molfwd(const double, const double, struct prjprm *, double *, double *);
+   int molrev(const double, const double, struct prjprm *, double *, double *);
+   int aitset(struct prjprm *);
+   int aitfwd(const double, const double, struct prjprm *, double *, double *);
+   int aitrev(const double, const double, struct prjprm *, double *, double *);
+   int copset(struct prjprm *);
+   int copfwd(const double, const double, struct prjprm *, double *, double *);
+   int coprev(const double, const double, struct prjprm *, double *, double *);
+   int coeset(struct prjprm *);
+   int coefwd(const double, const double, struct prjprm *, double *, double *);
+   int coerev(const double, const double, struct prjprm *, double *, double *);
+   int codset(struct prjprm *);
+   int codfwd(const double, const double, struct prjprm *, double *, double *);
+   int codrev(const double, const double, struct prjprm *, double *, double *);
+   int cooset(struct prjprm *);
+   int coofwd(const double, const double, struct prjprm *, double *, double *);
+   int coorev(const double, const double, struct prjprm *, double *, double *);
+   int bonset(struct prjprm *);
+   int bonfwd(const double, const double, struct prjprm *, double *, double *);
+   int bonrev(const double, const double, struct prjprm *, double *, double *);
+   int pcoset(struct prjprm *);
+   int pcofwd(const double, const double, struct prjprm *, double *, double *);
+   int pcorev(const double, const double, struct prjprm *, double *, double *);
+   int tscset(struct prjprm *);
+   int tscfwd(const double, const double, struct prjprm *, double *, double *);
+   int tscrev(const double, const double, struct prjprm *, double *, double *);
+   int cscset(struct prjprm *);
+   int cscfwd(const double, const double, struct prjprm *, double *, double *);
+   int cscrev(const double, const double, struct prjprm *, double *, double *);
+   int qscset(struct prjprm *);
+   int qscfwd(const double, const double, struct prjprm *, double *, double *);
+   int qscrev(const double, const double, struct prjprm *, double *, double *);
+#else
+   int prjset(), prjfwd(), prjrev();
+   int azpset(), azpfwd(), azprev();
+   int szpset(), szpfwd(), szprev();
+   int tanset(), tanfwd(), tanrev();
+   int stgset(), stgfwd(), stgrev();
+   int sinset(), sinfwd(), sinrev();
+   int arcset(), arcfwd(), arcrev();
+   int zpnset(), zpnfwd(), zpnrev();
+   int zeaset(), zeafwd(), zearev();
+   int airset(), airfwd(), airrev();
+   int cypset(), cypfwd(), cyprev();
+   int ceaset(), ceafwd(), cearev();
+   int carset(), carfwd(), carrev();
+   int merset(), merfwd(), merrev();
+   int sflset(), sflfwd(), sflrev();
+   int parset(), parfwd(), parrev();
+   int molset(), molfwd(), molrev();
+   int aitset(), aitfwd(), aitrev();
+   int copset(), copfwd(), coprev();
+   int coeset(), coefwd(), coerev();
+   int codset(), codfwd(), codrev();
+   int cooset(), coofwd(), coorev();
+   int bonset(), bonfwd(), bonrev();
+   int pcoset(), pcofwd(), pcorev();
+   int tscset(), tscfwd(), tscrev();
+   int cscset(), cscfwd(), cscrev();
+   int qscset(), qscfwd(), qscrev();
+#endif
+
+extern const char *prjset_errmsg[];
+extern const char *prjfwd_errmsg[];
+extern const char *prjrev_errmsg[];
+
+#define PRJSET 137
+
+struct celprm {
+   int flag;
+   double ref[4];
+   double euler[5];
+};
+
+#if __STDC__  || defined(__cplusplus)
+   int celset(const char *, struct celprm *, struct prjprm *);
+   int celfwd(const char *,
+              const double, const double,
+              struct celprm *,
+              double *, double *,
+              struct prjprm *,
+              double *, double *);
+   int celrev(const char *,
+              const double, const double,
+              struct prjprm *,
+              double *, double *,
+              struct celprm *,
+              double *, double *);
+#else
+   int celset(), celfwd(), celrev();
+#endif
+
+extern const char *celset_errmsg[];
+extern const char *celfwd_errmsg[];
+extern const char *celrev_errmsg[];
+
+#define CELSET 137
+
+struct linprm {
+   int flag;
+   int naxis;
+   double *crpix;
+   double *pc;
+   double *cdelt;
+
+   /* Intermediates. */
+   double *piximg;
+   double *imgpix;
+};
+
+#if __STDC__  || defined(__cplusplus)
+   int linset(struct linprm *);
+   int linfwd(const double[], struct linprm *, double[]);
+   int linrev(const double[], struct linprm *, double[]);
+   int matinv(const int, const double [], double []);
+#else
+   int linset(), linfwd(), linrev(), matinv();
+#endif
+
+extern const char *linset_errmsg[];
+extern const char *linfwd_errmsg[];
+extern const char *linrev_errmsg[];
+
+#define LINSET 137
+
+
+struct wcsprm {
+   int flag;
+   char pcode[4];
+   char lngtyp[5], lattyp[5];
+   int lng, lat;
+   int cubeface;
+};
+
+#if __STDC__ || defined(__cplusplus)
+   int wcsset(const int,
+              const char[][9],
+              struct wcsprm *);
+
+   int wcsfwd(const char[][9],
+              struct wcsprm *,
+              const double[],
+              const double[],
+              struct celprm *, 
+              double *,
+              double *, 
+              struct prjprm *, 
+              double[], 
+              struct linprm *,
+              double[]);
+
+   int wcsrev(const char[][9],
+              struct wcsprm *,
+              const double[], 
+              struct linprm *,
+              double[], 
+              struct prjprm *, 
+              double *,
+              double *, 
+              const double[], 
+              struct celprm *, 
+              double[]);
+
+   int wcsmix(const char[][9],
+              struct wcsprm *,
+              const int,
+              const int,
+              const double[],
+              const double,
+              int,
+              double[],
+              const double[],
+              struct celprm *,
+              double *,
+              double *,
+              struct prjprm *,
+              double[], 
+              struct linprm *,
+              double[]);
+
+#else
+   int wcsset(), wcsfwd(), wcsrev(), wcsmix();
+#endif
+
+extern const char *wcsset_errmsg[];
+extern const char *wcsfwd_errmsg[];
+extern const char *wcsrev_errmsg[];
+extern const char *wcsmix_errmsg[];
+
+#define WCSSET 137
+
+
+#if __STDC__  || defined(__cplusplus)
+   int sphfwd(const double, const double,
+              const double [],
+              double *, double *);
+   int sphrev(const double, const double,
+              const double [],
+              double *, double *);
+#else
+   int sphfwd(), sphrev();
+#endif
+
+#ifdef PI
+#undef PI
+#endif
+
+#ifdef D2R
+#undef D2R
+#endif
+
+#ifdef R2D
+#undef R2D
+#endif
+
+#ifdef SQRT2
+#undef SQRT2
+#endif
+
+#ifdef SQRT2INV
+#undef SQRT2INV
+#endif
+
+#define PI 3.141592653589793238462643
+#define D2R PI/180.0
+#define R2D 180.0/PI
+#define SQRT2 1.4142135623730950488
+#define SQRT2INV 1.0/SQRT2
+
+#if !defined(__STDC__) && !defined(__cplusplus)
+#ifndef const
+#define const
+#endif
+#endif
+
+#if __STDC__ || defined(__cplusplus)
+   double cosdeg(const double);
+   double sindeg(const double);
+   double tandeg(const double);
+   double acosdeg(const double);
+   double asindeg(const double);
+   double atandeg(const double);
+   double atan2deg(const double, const double);
+#else
+   double cosdeg();
+   double sindeg();
+   double tandeg();
+   double acosdeg();
+   double asindeg();
+   double atandeg();
+   double atan2deg();
+#endif
+
+/* Domain tolerance for asin and acos functions. */
+#define WCSTRIG_TOL 1e-10
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* wcslib_h_ */
+
+/* Feb  3 2000	Doug Mink - Make cplusplus ifdefs for braces all-inclusive
+ *
+ * Feb 15 2001	Doug Mink - Undefine math constants if already defined
+ * Sep 19 2001	Doug Mink - Update for WCSLIB 2.7, especially proj.h and cel.h
+ *
+ * Mar 12 2002	Doug Mink - Update for WCSLIB 2.8.2, especially proj.h
+ * Nov 29 2006	Doug Mink - Drop semicolon at end of C++ ifdef
+ * Jan  4 2007	Doug Mink - Drop extra declarations of SZP subroutines
+ */
diff --git a/Code/src/libwcs/wcstrig.c b/Code/src/libwcs/wcstrig.c
new file mode 100644
index 0000000000000000000000000000000000000000..6a30d033bea1b0df104b505f410c8aaec0dda362
--- /dev/null
+++ b/Code/src/libwcs/wcstrig.c
@@ -0,0 +1,189 @@
+/*============================================================================
+*
+*   WCSLIB - an implementation of the FITS WCS proposal.
+*   Copyright (C) 1995-2002, Mark Calabretta
+*
+*   This library is free software; you can redistribute it and/or
+*   modify it under the terms of the GNU Lesser General Public
+*   License as published by the Free Software Foundation; either
+*   version 2 of the License, or (at your option) any later version.
+*
+*   This library is distributed in the hope that it will be useful,
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+*   Lesser General Public License for more details.
+*   
+*   You should have received a copy of the GNU Lesser General Public
+*   License along with this library; if not, write to the Free Software
+*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*
+*   Correspondence concerning WCSLIB may be directed to:
+*      Internet email: mcalabre@atnf.csiro.au
+*      Postal address: Dr. Mark Calabretta,
+*                      Australia Telescope National Facility,
+*                      P.O. Box 76,
+*                      Epping, NSW, 2121,
+*                      AUSTRALIA
+*
+*=============================================================================
+*
+*   The functions defined herein are trigonometric or inverse trigonometric
+*   functions which take or return angular arguments in decimal degrees.
+*
+*   $Id: wcstrig.c,v 2.8 2002/04/03 01:25:29 mcalabre Exp $
+*---------------------------------------------------------------------------*/
+
+#include <math.h>
+#include "wcslib.h"
+const double d2r = PI / 180.0;
+const double r2d = 180.0 / PI;
+
+
+double cosdeg (angle)
+
+const double angle;
+
+{
+   double resid;
+
+   resid = fabs(fmod(angle,360.0));
+   if (resid == 0.0) {
+      return 1.0;
+   } else if (resid == 90.0) {
+      return 0.0;
+   } else if (resid == 180.0) {
+      return -1.0;
+   } else if (resid == 270.0) {
+      return 0.0;
+   }
+
+   return cos(angle*d2r);
+}
+
+/*--------------------------------------------------------------------------*/
+
+double sindeg (angle)
+
+const double angle;
+
+{
+   double resid;
+
+   resid = fmod(angle-90.0,360.0);
+   if (resid == 0.0) {
+      return 1.0;
+   } else if (resid == 90.0) {
+      return 0.0;
+   } else if (resid == 180.0) {
+      return -1.0;
+   } else if (resid == 270.0) {
+      return 0.0;
+   }
+
+   return sin(angle*d2r);
+}
+
+/*--------------------------------------------------------------------------*/
+
+double tandeg (angle)
+
+const double angle;
+
+{
+   double resid;
+
+   resid = fmod(angle,360.0);
+   if (resid == 0.0 || fabs(resid) == 180.0) {
+      return 0.0;
+   } else if (resid == 45.0 || resid == 225.0) {
+      return 1.0;
+   } else if (resid == -135.0 || resid == -315.0) {
+      return -1.0;
+   }
+
+   return tan(angle*d2r);
+}
+
+/*--------------------------------------------------------------------------*/
+
+double acosdeg(v)
+
+const double v;
+
+{
+   if (v >= 1.0) {
+      if (v-1.0 <  WCSTRIG_TOL) return 0.0;
+   } else if (v == 0.0) {
+      return 90.0;
+   } else if (v <= -1.0) {
+      if (v+1.0 > -WCSTRIG_TOL) return 180.0;
+   }
+
+   return acos(v)*r2d;
+}
+
+/*--------------------------------------------------------------------------*/
+
+double asindeg (v)
+
+const double v;
+
+{
+   if (v <= -1.0) {
+      if (v+1.0 > -WCSTRIG_TOL) return -90.0;
+   } else if (v == 0.0) {
+      return 0.0;
+   } else if (v >= 1.0) {
+      if (v-1.0 <  WCSTRIG_TOL) return 90.0;
+   }
+
+   return asin(v)*r2d;
+}
+
+/*--------------------------------------------------------------------------*/
+
+double atandeg (v)
+
+const double v;
+
+{
+   if (v == -1.0) {
+      return -45.0;
+   } else if (v == 0.0) {
+      return 0.0;
+   } else if (v == 1.0) {
+      return 45.0;
+   }
+
+   return atan(v)*r2d;
+}
+
+/*--------------------------------------------------------------------------*/
+
+double atan2deg (y, x)
+
+const double x, y;
+
+{
+   if (y == 0.0) {
+      if (x >= 0.0) {
+         return 0.0;
+      } else if (x < 0.0) {
+         return 180.0;
+      }
+   } else if (x == 0.0) {
+      if (y > 0.0) {
+         return 90.0;
+      } else if (y < 0.0) {
+         return -90.0;
+      }
+   }
+
+   return atan2(y,x)*r2d;
+}
+/* Dec 20 1999	Doug Mink - Change cosd() and sind() to cosdeg() and sindeg()
+ * Dec 20 1999	Doug Mink - Include wcslib.h, which includes wcstrig.h
+ * Dec 20 1999	Doug Mink - Use PI from wcslib.h, not locally defined
+ *
+ * Sep 19 2001	Doug Mink - No change for WCSLIB 2.7
+ */
diff --git a/Code/src/libwcs/webread.c b/Code/src/libwcs/webread.c
new file mode 100644
index 0000000000000000000000000000000000000000..5fef0014c8923814ea9047152c015be2d069bd92
--- /dev/null
+++ b/Code/src/libwcs/webread.c
@@ -0,0 +1,1036 @@
+/*** File webread.c
+ *** September 25, 2008
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** (http code from John Roll)
+ *** Copyright (C) 2000-2009
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+#define CHUNK   8192
+#define LINE    1024
+#define MAXHOSTNAMELENGTH	256
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#include <sys/fcntl.h>
+#endif
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+/* static int FileINetParse (char *file,int port,struct sockaddr_in *adrinet);*/
+static int FileINetParse();
+
+static FILE *SokOpen();
+#define XFREAD  1
+#define XFWRITE 2
+#define XFCREAT 4
+
+#define File    FILE *
+#define FileFd(fd)              fileno(fd)
+static char newline = '\n';
+
+
+/* WEBREAD -- Read a catalog over the web and return results */
+
+int
+webread (caturl,refcatname,distsort,cra,cdec,dra,ddec,drad,dradi,sysout,
+                 eqout,epout,mag1,mag2,sortmag,nstarmax,
+		 unum,ura,udec,upra,updec,umag,utype,nlog)
+
+char	*caturl;	/* URL of search engine */
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+int	distsort;	/* 1 to sort stars by distance from center */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*unum;		/* Array of UA numbers (returned) */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	*upra;		/* Array of right ascension proper motions (returned) */
+double	*updec;		/* Array of declination proper motions (returned) */
+double	**umag;		/* Array of magnitudes (returned) */
+int	*utype;		/* Array of plate numbers (returned) */
+int	nlog;		/* Logging interval (-1 to dump returned file) */
+{
+    char srchurl[LINE];
+    char temp[64];
+    struct TabTable *tabtable;
+    double dtemp;
+    int lurl;
+    int nsmax;
+    struct StarCat *starcat;
+    char cstr[32];
+    double ra, dec;
+    int nstar;
+
+    if (nstarmax < 1)
+	nlog = -1;
+
+    /* Convert coordinate system to string */
+    wcscstr (cstr, sysout, eqout, epout);
+
+    /* Set up search query from arguments */
+    lurl = strlen (caturl);
+
+    /* Set up query for scat used as server */
+    if (!strncmp (caturl+lurl-4,"scat",4)) {
+
+	/* Center coordinates of search */
+	sprintf (srchurl, "?catalog=%s&ra=%.7f&dec=%.7f&system=%s&format=tab",
+		 refcatname, cra, cdec, cstr);
+
+	/* Search radius or box size */
+	if (drad != 0.0) {
+	    dtemp = drad * 3600.0;
+	    sprintf (temp, "&rad=%.3f",dtemp);
+	    strcat (srchurl, temp);
+	    if (dradi > 0.0) {
+		dtemp = dradi * 3600.0;
+		sprintf (temp, "&inrad=%.3f",dtemp);
+		strcat (srchurl, temp);
+		}
+	    }
+	else {
+	    dtemp = dra * 3600.0;
+	    sprintf (temp, "&dra=%.3f",dtemp);
+	    strcat (srchurl, temp);
+	    dtemp = ddec * 3600.0;
+	    sprintf (temp, "&ddec=%.3f",dtemp);
+	    strcat (srchurl, temp);
+	    }
+
+	/* Sort by magnitude or distance for cutoff */
+	if (sortmag > 0) {
+	    sprintf (temp,"&sort=m%d", sortmag);
+	    strcat (srchurl, temp);
+	    }
+	if (distsort)
+	    strcat (srchurl, "&sort=distance");
+
+	/* Magnitude limits */
+	if (mag1 != mag2) {
+	    sprintf (temp, "&mag1=%.2f&mag=%.2f",mag1,mag2);
+	    strcat (srchurl, temp);
+	    }
+
+	/* Epoch for coordinates */
+	if (epout != 0.0) {
+	    sprintf (temp, "&epoch=%.5f", epout);
+	    strcat (srchurl, temp);
+	    }
+
+	/* Number of decimal places in RA seconds */
+	sprintf (temp, "&ndec=4");
+
+	/* Maximum number of stars to return */
+	if (nstarmax > 0) {
+	    sprintf (temp, "&nstar=%d", nstarmax);
+	    strcat (srchurl, temp);
+	    }
+	if (nlog > 0)
+	    fprintf (stderr, "%s%s\n", caturl, srchurl);
+	}
+
+    /* Set up query for ESO GSC server */
+    else if (!strncmp (caturl+lurl-10,"gsc-server",10)) {
+	ra = cra;
+	dec = cdec;
+	if (sysout != WCS_J2000)
+	    wcscon (sysout, WCS_J2000, eqout, 2000.0, &ra, &dec, epout);
+	if (dec < 0.0)
+	    sprintf (srchurl, "?%.7f%.7f&", ra/15.0, dec);
+	else
+	    sprintf (srchurl, "?%.7f+%.7f&", ra/15.0, dec);
+	if (drad > 0.0)
+	    dtemp = drad * 60.0;
+	else
+	    dtemp = 60.0 * sqrt (dra*dra + ddec*ddec);
+	sprintf (temp, "r=0,%.3f&",dtemp);
+	strcat (srchurl, temp);
+	nstar = 100000;
+	sprintf (temp, "nout=%d&f=8", nstar);
+	strcat (srchurl, temp);
+	if (nlog > 0)
+	    fprintf (stderr, "%s%s\n", caturl, srchurl);
+	}
+
+    /* Set up query for ESO USNO A server */
+    else if (!strncmp (caturl+lurl-12,"usnoa-server",12)) {
+	ra = cra;
+	dec = cdec;
+	if (sysout != WCS_J2000)
+	    wcscon (sysout, WCS_J2000, eqout, 2000.0, &ra, &dec, epout);
+	if (dec < 0.0)
+	    sprintf (srchurl, "?%.7f%.7f&", ra, dec);
+	else
+	    sprintf (srchurl, "?%.7f+%.7f&", ra, dec);
+	if (drad > 0.0)
+	    dtemp = drad * 60.0;
+	else
+	    dtemp = 60.0 * sqrt (dra*dra + ddec*ddec);
+	sprintf (temp, "radius=0,%.3f&", dtemp);
+	strcat (srchurl, temp);
+	if (mag1 != mag2) {
+	    sprintf (temp, "mag=%.2f,%.2f&", mag1, mag2);
+	    strcat (srchurl, temp);
+	    }
+	if (sortmag == 2)
+	    sprintf (temp, "format=8&sort=mr&");
+	else
+	    sprintf (temp, "format=8&sort=m&");
+	strcat (srchurl, temp);
+	nsmax = nstarmax * 4;
+	sprintf (temp, "n=%d", nsmax);
+	strcat (srchurl, temp);
+	if (nlog > 0)
+	    fprintf (stderr,"%s%s\n", caturl, srchurl);
+	}
+
+    /* Run search across the web */
+    if ((tabtable = webopen (caturl, srchurl, nlog)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: %s failed\n", srchurl);
+	return (0);
+	}
+
+    /* Return if no data */
+    if (tabtable->tabdata == NULL || strlen (tabtable->tabdata) == 0) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: No data returned\n");
+	return (0);
+	}
+
+    /* If scat, make sure that tab table has tabs */
+    if (!strncmp (caturl+lurl-4,"scat",4)) {
+	}
+
+    /* Dump returned file and stop */
+    if (nlog < 0) {
+	(void) fwrite  (tabtable->tabbuff, tabtable->lbuff, 1, stdout);
+	exit (0);
+	}
+
+    /* Open returned Starbase table as a catalog */
+    if ((starcat = tabcatopen (caturl, tabtable, 0)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: Could not open Starbase table as catalog\n");
+	return (0);
+	}
+
+    if (!strncmp (caturl+lurl-12,"usnoa-server",12)) {
+	starcat->coorsys = WCS_J2000;
+	starcat->epoch = 2000.0;
+	starcat->equinox = 2000.0;
+	starcat->nmag = 2;
+	starcat->entmag[0] = 5;
+	starcat->entmag[1] = 4;
+	strcpy (starcat->keymag[0], "magb");
+	strcpy (starcat->keymag[1], "magr");
+	}
+
+    /* Extract desired sources from catalog  and return them */
+    return (tabread (caturl,distsort,cra,cdec,dra,ddec,drad,dradi,
+	     sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,&starcat,
+	     unum,ura,udec,upra,updec,umag,utype,NULL,nlog));
+}
+
+
+int
+webrnum (caturl,refcatname,nnum,sysout,eqout,epout,match,
+	 unum,ura,udec,upra,updec,umag,utype,nlog)
+
+char	*caturl;	/* URL of search engine */
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+int	nnum;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*unum;		/* Array of UA numbers to find */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	*upra;		/* Array of right ascensions proper motion (returned) */
+double	*updec;		/* Array of declination proper motions (returned) */
+double	**umag;		/* Array of magnitudes (returned) */
+int	*utype;		/* Array of spectral types (returned) */
+int	nlog;		/* Logging interval (-1 to dump returned file) */
+{
+    char srchurl[LINE];
+    char numlist[LINE];
+    char numstr[32];
+    char csys[32];
+    struct TabTable *tabtable;
+    int i, refcat, nfld, nmag, mprop;
+    int lurl;
+    char title[64];	/* Description of catalog (returned) */
+    int syscat;		/* Catalog coordinate system (returned) */
+    double eqcat;	/* Equinox of catalog (returned) */
+    double epcat;	/* Epoch of catalog (returned) */
+    int ireg, istar;
+    char cstr[32];
+    char temp[64];
+    struct StarCat *starcat;
+
+    /* Set up search query from arguments */
+    lurl = strlen (caturl);
+
+    /* Set up query for scat used as server */
+    if (!strncmp (caturl+lurl-4,"scat",4)) {
+
+	/* Make list of catalog numbers */
+	refcat = RefCat (refcatname,title,&syscat,&eqcat,&epcat,&mprop,&nmag);
+	for (i = 0; i < nnum; i++) {
+	    nfld = CatNumLen (refcat, unum[i], 0);
+	    CatNum (refcat, -nfld, 0, unum[i], numstr);
+	    if (i > 0) {
+		strcat (numlist, ",");
+		strcat (numlist, numstr);
+	    }
+	    else
+		strcpy (numlist, numstr);
+	    }
+
+	/* Set up search query */
+	wcscstr (cstr, sysout, eqout, epout);
+	sprintf (srchurl, "?catalog=%s&num=%s&ndec=4&outsys=%s",refcatname,numlist,csys);
+	if (epout != 0.0) {
+	    sprintf (temp, "&epoch=%.5f", epout);
+	    strcat (srchurl, temp);
+	    }
+	}
+
+    /* Set up query for ESO GSC server */
+    else if (!strncmp (caturl+lurl-10,"gsc-server",10)) {
+	ireg = (int) unum[0];
+	istar = (int) (10000.0 * (unum[0] - (double) ireg) + 0.5);
+	sprintf (srchurl, "?object=GSC%05d%05d&nout=1&f=8", ireg, istar);
+	if (nlog > 0)
+	    fprintf (stderr, "%s%s\n", caturl, srchurl);
+	}
+
+    /* Set up query for ESO USNO A server */
+    else if (!strncmp (caturl+lurl-12,"usnoa-server",12)) {
+	ireg = (int) unum[0];
+	istar = (int) (100000000.0 * (unum[0] - (double) ireg) + 0.5);
+	sprintf (srchurl, "?object=U%04d_%08d&n=1&format=8&", ireg, istar);
+	if (nlog > 0)
+	    fprintf (stderr,"%s%s\n", caturl, srchurl);
+	}
+
+    /* Run search across the web */
+    if ((tabtable = webopen (caturl, srchurl, nlog)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBRNUM: %s failed\n", srchurl);
+	return (0);
+	}
+
+    /* Return if no data */
+    if (tabtable->tabdata == NULL || strlen (tabtable->tabdata) == 0) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBRNUM: No data returned\n");
+	return (0);
+	}
+
+    /* Dump returned file and stop */
+    if (nlog < 0) {
+	(void) fwrite  (tabtable->tabbuff, tabtable->lbuff, 1, stdout);
+	exit (0);
+	}
+
+    /* Open returned Starbase table as a catalog */
+    if ((starcat = tabcatopen (caturl, tabtable, 0)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBRNUM: Could not open Starbase table as catalog\n");
+	return (0);
+	}
+
+    /* Extract desired sources from catalog  and return them */
+    return (tabrnum (srchurl, nnum, sysout, eqout, epout, &starcat, match,
+         unum, ura, udec, upra, updec, umag, utype, NULL, nlog));
+}
+
+
+struct TabTable *
+webopen (caturl, srchpar, nlog)
+
+char	*caturl;	/* URL of search engine */
+char	*srchpar;	/* Search engine parameters to append */
+int	nlog;		/* 1 to print diagnostic messages */
+{
+    char *srchurl;
+    int lsrch;
+    char *tabbuff;
+    int	lbuff = 0;
+    char *tabnew, *tabline, *lastline, *tempbuff, *tabold;
+    int formfeed = (char) 12;
+    struct TabTable *tabtable;
+    int ltab, lname;
+    int diag;
+    int tabdiff;
+    char *space2tab();
+
+    if (nlog == 1)
+	diag = 1;
+    else
+	diag = 0;
+
+    /* Combine catalog search engine URL and arguments */
+    lsrch = strlen (srchpar) + strlen (caturl) + 2;
+    if ((srchurl = (char *) malloc (lsrch)) == NULL)
+	return (NULL);
+    strcpy (srchurl, caturl);
+    strcat (srchurl, srchpar);
+
+    /* Open port to HTTP server, send command, and fill buffer with return */
+    if ((tabbuff = webbuff (srchurl, diag, &lbuff)) == NULL) {
+	fprintf (stderr,"WEBOPEN: cannot read URL %s\n", srchurl);
+	free (srchurl);
+	return (NULL);
+	}
+    if (!strchr (tabbuff, '	') && !strchr (tabbuff, ',') && !strchr (tabbuff, '|')) {
+	if (diag) {
+	    fprintf (stderr,"Message returned from %s\n", srchurl);
+	    fprintf (stderr,"%s\n", tabbuff);
+	    }
+	free (srchurl);
+	return (NULL);
+	}
+
+    /* Transform SDSS return into tab table */
+    if (strsrch (srchurl, "sdss")) {
+	tempbuff = tabbuff;
+	tabbuff = sdssc2t (tempbuff);
+	lbuff = strlen (tabbuff);
+	free (tempbuff);
+	}
+
+    /* Transform MAST GALEX  GSC 2 return into tab table */
+    else if (strsrch (srchurl, "galex")) {
+	tempbuff = tabbuff;
+	tabbuff = gsc2c2t (tempbuff);
+	lbuff = strlen (tabbuff);
+	free (tempbuff);
+	}
+
+    /* Transform CASB GSC 2 return into tab table */
+    else if (strsrch (srchurl, "gsss")) {
+	tempbuff = tabbuff;
+	tabbuff = gsc2t2t (tempbuff);
+	lbuff = strlen (tabbuff);
+	free (tempbuff);
+	}
+
+    /* Transform SkyBot return into tab table */
+    else if (strsrch (srchurl, "skybot")) {
+	tempbuff = tabbuff;
+	tabbuff = skybot2tab (tempbuff);
+	lbuff = strlen (tabbuff);
+	free (tempbuff);
+	}
+
+    /* Make sure that scat data is tab-separated (3 tabs found) */
+    else if (strsrch (srchurl, "scat")) {
+	tempbuff = strchr (tabbuff, '\t');
+	if (tempbuff != NULL) {
+	    tempbuff = strchr (tempbuff+1, '\t');
+	    if (tempbuff != NULL)
+		tempbuff = strchr (tempbuff+1, '\t');
+	    }	
+	if (tempbuff == NULL) {
+	    tempbuff = tabbuff;
+	    tabbuff = space2tab (tempbuff);
+	    lbuff = strlen (tabbuff);
+	    free (tempbuff);
+	    }
+	}
+    
+    /* Allocate tab table structure */
+    ltab = sizeof (struct TabTable);
+    if ((tabtable = (struct TabTable *) calloc (1, ltab)) == NULL) {
+	fprintf (stderr,"WEBOPEN: cannot allocate Tab Table structure for %s",
+	         srchurl);
+	free (srchurl);
+	return (NULL);
+	}
+
+    /* Save pointers to file contents */
+    tabtable->tabbuff = tabbuff;
+    tabtable->tabheader = tabtable->tabbuff;
+    tabtable->lbuff = lbuff;
+
+    /* Allocate space for and save catalog URL as filename */
+    lname = strlen (caturl) + 2;
+    if ((tabtable->filename = (char *) calloc (1, lname)) == NULL) {
+	fprintf (stderr,"WEBOPEN: cannot allocate filename %s in structure",
+	         caturl);
+	tabclose (tabtable);
+	free (srchurl);
+	return (NULL);
+	}
+    strcpy (tabtable->filename, caturl);
+
+    /* Allocate space for and save search string as tabname */
+    lname = strlen (srchpar) + 2;
+    if ((tabtable->tabname = (char *) calloc (1, lname)) == NULL) {
+	fprintf (stderr,"WEBOPEN: cannot allocate tabname %s in structure",
+	         srchurl);
+	tabclose (tabtable);
+	free (srchurl);
+	return (NULL);
+	}
+    strcpy (tabtable->tabname, srchpar);
+
+    /* Find column headings and start of data */
+    tabline = tabtable->tabheader;
+    lastline = NULL;
+    while (*tabline != '-' && tabline < tabtable->tabbuff+lbuff) {
+	lastline = tabline;
+	tabline = strchr (tabline,newline) + 1;
+	}
+    if (*tabline != '-') {
+	fprintf (stderr,"WEBOPEN: No - line in tab table %s",srchurl);
+	tabclose (tabtable);
+	free (srchurl);
+	return (NULL);
+	}
+    tabtable->tabhead = lastline;
+    tabtable->tabdata = strchr (tabline, newline) + 1;
+
+    /* Extract positions of keywords we will want to use */
+    if (!tabparse (tabtable)) {
+	fprintf (stderr,"TABOPEN: No columns in tab table %s\n",srchurl);
+	tabclose (tabtable);
+	free (srchurl);
+	return (NULL);
+	}
+
+    /* Enumerate entries in tab table catalog by counting newlines */
+    tabnew = tabtable->tabdata;
+    tabold = tabnew;
+    tabdiff = 0;
+    tabtable->nlines = 0;
+    while ((tabnew = strchr (tabnew, newline)) != NULL) {
+	tabdiff = tabnew - tabold;
+	tabnew = tabnew + 1;
+	tabtable->nlines = tabtable->nlines + 1;
+	if (*tabnew == formfeed)
+	    break;
+	if (!strncasecmp (tabnew, "[EOD]", 5))
+	    break;
+	tabold = tabnew;
+	}
+    if (tabdiff < 2 && tabtable->nlines > 0)
+	tabtable->nlines = tabtable->nlines - 1;
+
+    tabtable->tabline = tabtable->tabdata;
+    tabtable->iline = 1;
+
+    free (srchurl);
+    return (tabtable);
+}
+
+
+/* WEBBUFF -- Return character buffer from given URL */
+
+char *
+webbuff (url, diag, lbuff)
+
+char	*url;	/* URL to read */
+int	diag;	/* 1 to print diagnostic messages */
+int	*lbuff;	/* Length of buffer (returned) */
+{
+    File sok;
+    char *server;
+    char linebuff[LINE];
+    char *buff;
+    char *tabbuff;
+    char *newbuff;
+    char *urlpath;
+    char *servurl;
+    int	status;
+    int lserver;
+    int chunked = 0;
+    int lread;
+    int lchunk, lline;
+    int nbcont = 0;
+    int lcbuff;
+    int lb;
+    int ltbuff;
+    int lcom;
+    char *cbcont;
+    char *newserver;
+    char *sokptr;
+
+    *lbuff = 0;
+    newbuff = NULL;
+    diag = 0;
+
+    /* Extract server name and path from URL */
+    servurl = url;
+    if (!strncmp(url, "http://", 7))
+	servurl = servurl + 7;
+    urlpath = strchr (servurl, '/');
+    lserver = urlpath - servurl;
+    if ((server = (char *) malloc (lserver+2)) == NULL)
+	return (NULL);
+    strncpy (server, servurl, lserver);
+    server[lserver] = (char) 0;
+
+    /* Open port to HTTP server */
+    if ( !(sok = SokOpen (server, 80, XFREAD | XFWRITE)) ) {
+	free (server);
+	return (NULL);
+	}
+
+    /* Send HTTP GET command */
+    fprintf (sok, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n",urlpath,server);
+    fflush(sok);
+    free (server);
+
+    (void) fscanf(sok, "%*s %d %s\r\n", &status, linebuff);
+
+    /* If Redirect code encounter, go to alternate URL at Location: */
+    if ( status == 301 || status == 302 || status == 303 || status == 307 ) {
+	char redirect[LINE];
+	while ((servurl = fgets (redirect, LINE, sok))) {
+	    if (!(strncmp (redirect, "Location:", 9)))
+		break;
+	    }
+	(void) fclose (sok);
+	if (servurl == NULL) {
+	    if (diag)
+		fprintf (stderr,"WEBBUFF: No forward for HTTP Code %d\n", status);
+	    return (NULL);
+	    }
+	if ((servurl = strsrch (servurl, "http://")) == NULL) {
+	    if (diag)
+		fprintf (stderr,"WEBBUFF: No forward URL for HTTP Code %d\n", status);
+	    return (NULL);
+	    }
+	servurl = servurl + 7;
+	urlpath = strchr (servurl, '/');
+	lserver = urlpath - servurl;
+	if ((server = (char *) malloc (lserver+2)) == NULL) {
+	    return (NULL);
+	    }
+	strncpy (server, servurl, lserver);
+	server[lserver] = (char) 0;
+
+	if (diag)
+	    fprintf (stderr,"WEBBUFF: HTTP Code %d: Temporary Redirect to %s\n",
+		     status, server);
+
+	/* Open port to HTTP server */
+	if ( (sok = SokOpen (server, 80, XFREAD | XFWRITE)) == NULL ) {
+	    free (server);
+	    return (NULL);
+	    }
+
+	/* Send HTTP GET command (simbad forward fails if HTTP/1.1 included) */
+	fprintf(sok, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n",urlpath,server);
+	fflush(sok);
+	free (server);
+
+	(void) fscanf(sok, "%*s %d %*s\r\n", &status);
+	}
+
+    /* Skip continue lines
+    if (status == 100) {
+	while (status == 100)
+	    fscanf(sok, "%*s %d %*s\n", &status);
+	} */
+
+    /* If status is not 200 return without data */
+    if ( status != 200 ) {
+	if (diag)
+	    fprintf (stderr,"HTTP Code %d from  %s\n", status, server);
+	return (NULL);
+	}
+
+    /* Skip over http header of returned stuff */
+    while (fgets (linebuff, LINE, sok) ) {
+	if (diag)
+	    fprintf (stderr, "%s", linebuff);
+	if (strcsrch (linebuff, "chunked") != NULL)
+	    chunked = 1;
+	if (strcsrch (linebuff, "Content-length") != NULL) {
+	    if ((cbcont = strchr (linebuff, ':')) != NULL)
+		nbcont = atoi (cbcont+1);
+	    }
+	if (*linebuff == '\n') break;
+	if (*linebuff == '\r') break;
+	}
+
+    /* Read table into buffer in memory a chunk at a time */
+    tabbuff = NULL;
+    lb = 0;
+    if (chunked) {
+	lchunk = 1;
+	lline = 1;
+	*lbuff = 0;
+	ltbuff = 0;
+	while (lline > 0) {
+	    (void) fgets (linebuff, LINE, sok);
+	    lline = strlen (linebuff);
+	    if (lline < 1)
+		break;
+	    if (linebuff[lline-1] < 32)
+		linebuff[lline-1] = (char) 0;
+	    if (linebuff[lline-2] < 32)
+		linebuff[lline-2] = (char) 0;
+	    if (strlen (linebuff) <= 0)
+		continue;
+	    lchunk = (int) strtol (linebuff, NULL, 16);
+	    if (lchunk < 1)
+		break;
+	    /* else if (lchunk == 1)
+		continue; */
+	    if (diag)
+		fprintf (stderr, "%s (=%d)\n", linebuff, lchunk);
+	    lcbuff = ltbuff;
+	    ltbuff = ltbuff + lchunk;
+
+	    /* Allocate buffer on first time through */
+	    if (tabbuff == NULL) {
+		lb = 10 * ltbuff;
+		tabbuff = (char *) calloc ((size_t)lb, (size_t)1);
+		buff = tabbuff;
+		}
+
+	    /* Increase buffer size if this chunk will push it over current limit */
+	    else if (ltbuff > lb) {
+		lb = lb * 10;
+		newbuff = (char *) calloc ((size_t)lb, (size_t)1); 
+		movebuff (tabbuff, newbuff, lcbuff, 0, 0);
+		free (tabbuff);
+		tabbuff = newbuff;
+		buff = tabbuff + lcbuff;
+		newbuff = NULL;
+		}
+	    else {
+		buff = tabbuff + lcbuff;
+		}
+            (void) fread (buff, 1, lchunk, sok);
+	    buff[lchunk] = (char) 0;
+	    if (diag)
+		fprintf (stderr, "%s\n", buff);
+	    *lbuff = ltbuff;
+	    }
+	}
+
+    /* Read table all at once if total length is passed */
+    else if (nbcont > 0) {
+	tabbuff = (char *) calloc (1, nbcont+1);
+	tabbuff[nbcont] = (char) 0;
+	if ((lread = fread (tabbuff, 1, nbcont, sok)) <= 0) {
+	    free (tabbuff);
+	    tabbuff = NULL;
+	    }
+	}
+
+    /* Read table into buffer in memory a buffer-full at a time */
+    else {
+	lchunk = 8192;
+	*lbuff = 0;
+	buff = (char *) calloc (1, lchunk+8);
+	if (buff == NULL) {
+	    fprintf (stderr, "WEBBUFF: unable to allocate chunk buffer of %d bytes\n", lchunk + 8);
+	    return (NULL);
+	    }
+	while ( (lread = fread (buff, 1, lchunk, sok)) > 0 ) {
+	    lcbuff = *lbuff;
+	    *lbuff = *lbuff + lread;
+	    if (tabbuff == NULL) {
+		tabbuff = (char *) malloc (*lbuff+8);
+		if (tabbuff == NULL) {
+		    fprintf (stderr, "WEBBUFF: unable to allocate buffer of %d bytes\n", *lbuff + 8);
+		    return (NULL);
+		    }
+		movebuff (buff, tabbuff, lread, 0, 0);
+		}
+	    else {
+		newbuff = (char *) malloc (*lbuff+8);
+		if (newbuff == NULL) {
+		    fprintf (stderr, "WEBBUFF: unable to allocate new buffer of %d bytes\n", *lbuff + 8);
+		    return (NULL);
+		    }
+		movebuff (tabbuff, newbuff, lcbuff, 0, 0);
+		free (tabbuff);
+		tabbuff = newbuff;
+		movebuff (buff, tabbuff, lread, 0, lcbuff);
+		}
+	    if (diag)
+		fprintf (stderr, "%s\n", buff);
+	    }
+	free (buff);
+	buff = NULL;
+	}
+    (void) fclose (sok);
+
+    return (tabbuff);
+}
+
+/* sokFile.c
+ * copyright 1991, 1993, 1995, 1999 John B. Roll jr.
+ */
+
+static FILE *
+SokOpen(name, port, mode)
+	char *name;             /* "host:port" socket to open */
+	int   port;
+	int   mode;             /* mode of socket to open */
+{
+    int             xfd;        /* socket descriptor */
+    int             type;       /* type returned from FileINetParse */
+    struct sockaddr_in adrinet; /* socket structure parsed from name */
+    int             reuse_addr = 1;
+
+
+    File            f;          /* returned file descriptor */
+
+    if (!(type = FileINetParse(name, port, &adrinet)))
+	return NULL;
+
+    if ( type == 1 
+     || (mode & XFCREAT && mode & XFREAD && !(mode & XFWRITE)) ) {
+	if ( ((xfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+	  ||  setsockopt(xfd, SOL_SOCKET, SO_REUSEADDR,
+	             (char *) &reuse_addr, sizeof(reuse_addr)) < 0
+	  || (bind(xfd, (struct sockaddr *) & adrinet
+	                 ,sizeof(adrinet)) != 0)
+	  ||  listen(xfd, 5) ) {
+	    close(xfd);
+	    return NULL;
+	}
+      } else {
+	if (((xfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+	           || (connect(xfd, (struct sockaddr *) & adrinet
+	                       ,sizeof(adrinet)) != 0)) {
+	    close(xfd);
+	    return NULL;
+	}
+    }
+
+    f = fdopen (xfd, "r+");
+
+    return f;
+}
+
+
+static int
+FileINetParse(file, port, adrinet)
+	char *file;             /* host/socket pair to parse? */
+	int   port;
+	struct sockaddr_in *adrinet; /* socket info structure to fill? */
+{
+    struct hostent *hp;         /* -> hostent structure for host */
+    char            hostname[MAXHOSTNAMELENGTH + 12]; /* name of host */
+    char           *portstr;    /* internet port number (ascii) */
+    int             type = 2;   /* return code */
+    extern int gethostname();
+
+    if ( !strncmp(file, "http://", 7) ) {
+	file += 7;
+	if ( port == -1 ) port  = 80;
+    }
+
+    strcpy(hostname, file);
+
+#ifdef msdos
+    /* This is a DOS disk discriptor, not a machine name */
+    if ((!(file[0] == '.')) && file[1] == ':')
+	return 0;
+#endif
+
+    if ( (portstr = strchr(hostname, '/')) ) {
+	*portstr = '\0';
+    }
+
+    if ( (portstr = strchr(hostname, ':')) ) {
+	*portstr++ = '\0';
+
+	if ((port = strtol(portstr, NULL, 0)) == 0) {
+	    struct servent *service;
+
+	    if ((service = getservbyname(portstr, NULL)) == NULL)
+	        return 0;
+	    port = service->s_port;
+	}
+    }
+
+    if ( port == -1 ) return 0;
+
+    if (hostname[0] == '\0')
+	type = 1;
+    if (hostname[0] == '\0' || hostname[0] == '.')
+	if (gethostname(hostname, MAXHOSTNAMELENGTH) == -1)
+	    return 0;
+
+    if ((hp = gethostbyname(hostname)) == NULL)
+	return 0;
+
+    memset(adrinet, 0, sizeof(struct sockaddr_in));
+    adrinet->sin_family = AF_INET;
+    adrinet->sin_port = htons(port);
+    memcpy(&adrinet->sin_addr, hp->h_addr, hp->h_length);
+
+    return type;
+}
+
+char *
+space2tab (tabbuff)
+    char *tabbuff;	/* Tab table filled with spaces */
+{
+    char *newbuff, *line0, *line1, *ic, *icn;
+    char cspace, ctab, cdash;
+    int lbuff;
+    int alltab = 0;
+    int notab = 0;
+
+    cspace = ' ';
+    cdash = '-';
+    ctab = '\t';
+    line0 = tabbuff;
+    lbuff = strlen (tabbuff);
+    newbuff = (char *) calloc (lbuff, 1);
+    icn = newbuff;
+    while ((line1 = strchr (line0, newline))) {
+	if (alltab == 0 && *(line1+1) == cdash) {
+	    alltab = 1;
+	    }
+	ic = line0;
+	notab = 1;
+	while (ic <= line1) {
+	    if (*ic != cspace)
+		*icn++ = *ic++;
+	    else {
+		if (alltab) {
+		    *icn++ = ctab;
+		    while (*ic++ == cspace) {
+			}
+		    ic--;
+		    }
+		else if (notab) {
+		    notab = 0;
+		    *icn++ = ctab;
+		    while (*ic++ == cspace) {
+			}
+		    ic--;
+		    }
+		else
+		    *icn++ = *ic++;
+		}
+	    }
+	line0 = line1 + 1;
+	notab = 1;
+	if (strlen (line0) < 1) {
+	    *icn++ = (char) 0;
+	    break;
+	    }
+	}
+    return (newbuff);
+}
+
+/* Nov 29 2000	New subroutines
+ * Dec 11 2000	Do not print messages unless nlog > 0
+ * Dec 12 2000	Fix problems with return if no stars
+ * Dec 18 2000	Clean up code after lint
+ *
+ * Jan  2 2001	Set MAXHOSTNAMELENGTH to 256, bypassing system constant
+ * Jan  3 2001	Include string.h, not strings.h
+ * Mar 19 2001	Drop argument types from declaration
+ * Mar 23 2001	Put number into argument list correctly in webrnum()
+ * Jun  7 2001	Add proper motion flag and number of magnitudes to RefCat()
+ * Jun 20 2001	Move webopen() declaration to wcscat.h
+ * Jun 28 2001	When reading chunked data, loop until nothing is read or [EOD]
+ * Jul 12 2001	Break out web access into subroutine webbuff()
+ * Sep  7 2001	Free server in webbuff()
+ * Sep 11 2001	Pass array of magnitude vectors
+ * Sep 14 2001	Pass sort type, if distance or magnitude
+ * Sep 14 2001	Add option to print entire returned file if nlog < 0
+ * Sep 21 2001	Debug searches of ESO USNO-A2.0 and GSC catalogs
+ *
+ * Apr  8 2002	Fix bug in ESO USNO-A2.0 server code
+ * Aug  6 2002	Make starcat->entmag and starcat->keymag into vectors
+ * Oct  3 2002	If nstarmax is less than 1, print results from web directly
+ *
+ * Jan 27 2003	Add maximum number of stars to be returned to webread()
+ * Jan 28 2003	Add number of decimal places to webread() and webrnum()
+ * Mar 12 2003	Fix bug in USNO-A2 server code
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Nov 22 2003	Increase buffer size faster than reading in webbuff()
+ * Dec 12 2003	Fix calls to CatNumLen() and tabcatopen()
+ *
+ * Jan  5 2004	Convert SDSS table from comma-separated to tab-separated
+		in webopen(); initialize nbcont to 0 in webbuff()
+ * Jan 14 2004	Return error if data but no objects returned in webopen()
+ * Aug 30 2004	Send CR-LF termination to HTTP GET, not just LF
+ * Sep 10 2004	Print server messages only if verbose flag is on
+ *
+ * Jan  9 2006	Multiply max number of stars for ESO search to get all
+ * Apr  6 2006	Check for sdss in URL for Sloan parsing
+ * Jun 20 2006	Cast most stream I/O calls to void
+ * Oct 30 2006	Reset buffer length for SDSS tables
+ *
+ * Jan 10 2007	Add match to webrnum argument list for tabrnum()
+ * Jan 11 2007	Include fitsfile.h
+ * Mar 13 2007	Process CSV data from STScI MAST GALEX GSC2 catalog
+ * Mar 13 2007	Caselessly search for header info
+ * Apr 11 2007	Terminate buffer read as number of characters
+ * Jul 13 2007	Add SkyBot data transformation to webopen()
+ * Jul 17 2007	Change order of arguments in movebuff() so destination is first
+ * Jul 18 2007	Fix bug in chunked data reading
+ * Jul 19 2007	If last line of table has no content, drop it
+ * Aug 24 2007	Fix tab tables filled with spaces
+ * Aug 28 2007	Fix space2tab() declarations which passed on Solaris, not Linux
+ * Dec 31 2007	Fix chunk reading code in webbuff()
+ *
+ * Jan  8 2008	Forward automatically if status=301|302|303|307 (code from Ed Los)
+ *
+ * Sep 25 2009	Reverse movebuff() source, destination arguments for compatibility
+ * Sep 25 2009	Free allocated pointers before returning after Douglas Burke
+ */
diff --git a/Code/src/libwcs/webread0.c b/Code/src/libwcs/webread0.c
new file mode 100644
index 0000000000000000000000000000000000000000..48733fca46a1d13b98df674649ee1749dc9e0d26
--- /dev/null
+++ b/Code/src/libwcs/webread0.c
@@ -0,0 +1,1033 @@
+/*** File webread.c
+ *** January 5, 2008
+ *** By Doug Mink, dmink@cfa.harvard.edu
+ *** Harvard-Smithsonian Center for Astrophysics
+ *** (http code from John Roll)
+ *** Copyright (C) 2000-2008
+ *** Smithsonian Astrophysical Observatory, Cambridge, MA, USA
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    Correspondence concerning WCSTools should be addressed as follows:
+           Internet email: dmink@cfa.harvard.edu
+           Postal address: Doug Mink
+                           Smithsonian Astrophysical Observatory
+                           60 Garden St.
+                           Cambridge, MA 02138 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include "wcs.h"
+#include "fitsfile.h"
+#include "wcscat.h"
+
+#define CHUNK   8192
+#define LINE    1024
+#define MAXHOSTNAMELENGTH	256
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#include <sys/fcntl.h>
+#endif
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+/* static int FileINetParse (char *file,int port,struct sockaddr_in *adrinet);*/
+static int FileINetParse();
+static void movebuff();
+
+static FILE *SokOpen();
+#define XFREAD  1
+#define XFWRITE 2
+#define XFCREAT 4
+
+#define File    FILE *
+#define FileFd(fd)              fileno(fd)
+static char newline = '\n';
+
+
+/* WEBREAD -- Read a catalog over the web and return results */
+
+int
+webread (caturl,refcatname,distsort,cra,cdec,dra,ddec,drad,dradi,sysout,
+                 eqout,epout,mag1,mag2,sortmag,nstarmax,
+		 unum,ura,udec,upra,updec,umag,utype,nlog)
+
+char	*caturl;	/* URL of search engine */
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+int	distsort;	/* 1 to sort stars by distance from center */
+double	cra;		/* Search center J2000 right ascension in degrees */
+double	cdec;		/* Search center J2000 declination in degrees */
+double	dra;		/* Search half width in right ascension in degrees */
+double	ddec;		/* Search half-width in declination in degrees */
+double	drad;		/* Limiting separation in degrees (ignore if 0) */
+double	dradi;		/* Inner edge of annulus in degrees (ignore if 0) */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	mag1,mag2;	/* Limiting magnitudes (none if equal) */
+int	sortmag;	/* Magnitude by which to sort (1 to nmag) */
+int	nstarmax;	/* Maximum number of stars to be returned */
+double	*unum;		/* Array of UA numbers (returned) */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	*upra;		/* Array of right ascension proper motions (returned) */
+double	*updec;		/* Array of declination proper motions (returned) */
+double	**umag;		/* Array of magnitudes (returned) */
+int	*utype;		/* Array of plate numbers (returned) */
+int	nlog;		/* Logging interval (-1 to dump returned file) */
+{
+    char srchurl[LINE];
+    char temp[64];
+    struct TabTable *tabtable;
+    double dtemp;
+    int lurl;
+    int nsmax;
+    struct StarCat *starcat;
+    char cstr[32];
+    double ra, dec;
+    int nstar;
+
+    if (nstarmax < 1)
+	nlog = -1;
+
+    /* Convert coordinate system to string */
+    wcscstr (cstr, sysout, eqout, epout);
+
+    /* Set up search query from arguments */
+    lurl = strlen (caturl);
+
+    /* Set up query for scat used as server */
+    if (!strncmp (caturl+lurl-4,"scat",4)) {
+
+	/* Center coordinates of search */
+	sprintf (srchurl, "?catalog=%s&ra=%.7f&dec=%.7f&system=%s&format=tab",
+		 refcatname, cra, cdec, cstr);
+
+	/* Search radius or box size */
+	if (drad != 0.0) {
+	    dtemp = drad * 3600.0;
+	    sprintf (temp, "&rad=%.3f",dtemp);
+	    strcat (srchurl, temp);
+	    if (dradi > 0.0) {
+		dtemp = dradi * 3600.0;
+		sprintf (temp, "&inrad=%.3f",dtemp);
+		strcat (srchurl, temp);
+		}
+	    }
+	else {
+	    dtemp = dra * 3600.0;
+	    sprintf (temp, "&dra=%.3f",dtemp);
+	    strcat (srchurl, temp);
+	    dtemp = ddec * 3600.0;
+	    sprintf (temp, "&ddec=%.3f",dtemp);
+	    strcat (srchurl, temp);
+	    }
+
+	/* Sort by magnitude or distance for cutoff */
+	if (sortmag > 0) {
+	    sprintf (temp,"&sort=m%d", sortmag);
+	    strcat (srchurl, temp);
+	    }
+	if (distsort)
+	    strcat (srchurl, "&sort=distance");
+
+	/* Magnitude limits */
+	if (mag1 != mag2) {
+	    sprintf (temp, "&mag1=%.2f&mag=%.2f",mag1,mag2);
+	    strcat (srchurl, temp);
+	    }
+
+	/* Epoch for coordinates */
+	if (epout != 0.0) {
+	    sprintf (temp, "&epoch=%.5f", epout);
+	    strcat (srchurl, temp);
+	    }
+
+	/* Number of decimal places in RA seconds */
+	sprintf (temp, "&ndec=4");
+
+	/* Maximum number of stars to return */
+	if (nstarmax > 0) {
+	    sprintf (temp, "&nstar=%d", nstarmax);
+	    strcat (srchurl, temp);
+	    }
+	if (nlog > 0)
+	    fprintf (stderr, "%s%s\n", caturl, srchurl);
+	}
+
+    /* Set up query for ESO GSC server */
+    else if (!strncmp (caturl+lurl-10,"gsc-server",10)) {
+	ra = cra;
+	dec = cdec;
+	if (sysout != WCS_J2000)
+	    wcscon (sysout, WCS_J2000, eqout, 2000.0, &ra, &dec, epout);
+	if (dec < 0.0)
+	    sprintf (srchurl, "?%.7f%.7f&", ra/15.0, dec);
+	else
+	    sprintf (srchurl, "?%.7f+%.7f&", ra/15.0, dec);
+	if (drad > 0.0)
+	    dtemp = drad * 60.0;
+	else
+	    dtemp = 60.0 * sqrt (dra*dra + ddec*ddec);
+	sprintf (temp, "r=0,%.3f&",dtemp);
+	strcat (srchurl, temp);
+	nstar = 100000;
+	sprintf (temp, "nout=%d&f=8", nstar);
+	strcat (srchurl, temp);
+	if (nlog > 0)
+	    fprintf (stderr, "%s%s\n", caturl, srchurl);
+	}
+
+    /* Set up query for ESO USNO A server */
+    else if (!strncmp (caturl+lurl-12,"usnoa-server",12)) {
+	ra = cra;
+	dec = cdec;
+	if (sysout != WCS_J2000)
+	    wcscon (sysout, WCS_J2000, eqout, 2000.0, &ra, &dec, epout);
+	if (dec < 0.0)
+	    sprintf (srchurl, "?%.7f%.7f&", ra, dec);
+	else
+	    sprintf (srchurl, "?%.7f+%.7f&", ra, dec);
+	if (drad > 0.0)
+	    dtemp = drad * 60.0;
+	else
+	    dtemp = 60.0 * sqrt (dra*dra + ddec*ddec);
+	sprintf (temp, "radius=0,%.3f&", dtemp);
+	strcat (srchurl, temp);
+	if (mag1 != mag2) {
+	    sprintf (temp, "mag=%.2f,%.2f&", mag1, mag2);
+	    strcat (srchurl, temp);
+	    }
+	if (sortmag == 2)
+	    sprintf (temp, "format=8&sort=mr&");
+	else
+	    sprintf (temp, "format=8&sort=m&");
+	strcat (srchurl, temp);
+	nsmax = nstarmax * 4;
+	sprintf (temp, "n=%d", nsmax);
+	strcat (srchurl, temp);
+	if (nlog > 0)
+	    fprintf (stderr,"%s%s\n", caturl, srchurl);
+	}
+
+    /* Run search across the web */
+    if ((tabtable = webopen (caturl, srchurl, nlog)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: %s failed\n", srchurl);
+	return (0);
+	}
+
+    /* Return if no data */
+    if (tabtable->tabdata == NULL || strlen (tabtable->tabdata) == 0) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: No data returned\n");
+	return (0);
+	}
+
+    /* If scat, make sure that tab table has tabs */
+    if (!strncmp (caturl+lurl-4,"scat",4)) {
+	}
+
+    /* Dump returned file and stop */
+    if (nlog < 0) {
+	(void) fwrite  (tabtable->tabbuff, tabtable->lbuff, 1, stdout);
+	exit (0);
+	}
+
+    /* Open returned Starbase table as a catalog */
+    if ((starcat = tabcatopen (caturl, tabtable, 0)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBREAD: Could not open Starbase table as catalog\n");
+	return (0);
+	}
+
+    if (!strncmp (caturl+lurl-12,"usnoa-server",12)) {
+	starcat->coorsys = WCS_J2000;
+	starcat->epoch = 2000.0;
+	starcat->equinox = 2000.0;
+	starcat->nmag = 2;
+	starcat->entmag[0] = 5;
+	starcat->entmag[1] = 4;
+	strcpy (starcat->keymag[0], "magb");
+	strcpy (starcat->keymag[1], "magr");
+	}
+
+    /* Extract desired sources from catalog  and return them */
+    return (tabread (caturl,distsort,cra,cdec,dra,ddec,drad,dradi,
+	     sysout,eqout,epout,mag1,mag2,sortmag,nstarmax,&starcat,
+	     unum,ura,udec,upra,updec,umag,utype,NULL,nlog));
+}
+
+
+int
+webrnum (caturl,refcatname,nnum,sysout,eqout,epout,match,
+	 unum,ura,udec,upra,updec,umag,utype,nlog)
+
+char	*caturl;	/* URL of search engine */
+char	*refcatname;	/* Name of catalog (UAC, USAC, UAC2, USAC2) */
+int	nnum;		/* Number of stars to find */
+int	sysout;		/* Search coordinate system */
+double	eqout;		/* Search coordinate equinox */
+double	epout;		/* Proper motion epoch (0.0 for no proper motion) */
+double	*unum;		/* Array of UA numbers to find */
+double	*ura;		/* Array of right ascensions (returned) */
+double	*udec;		/* Array of declinations (returned) */
+double	*upra;		/* Array of right ascensions proper motion (returned) */
+double	*updec;		/* Array of declination proper motions (returned) */
+double	**umag;		/* Array of magnitudes (returned) */
+int	*utype;		/* Array of spectral types (returned) */
+int	nlog;		/* Logging interval (-1 to dump returned file) */
+{
+    char srchurl[LINE];
+    char numlist[LINE];
+    char numstr[32];
+    char csys[32];
+    struct TabTable *tabtable;
+    int i, refcat, nfld, nmag, mprop;
+    int lurl;
+    char title[64];	/* Description of catalog (returned) */
+    int syscat;		/* Catalog coordinate system (returned) */
+    double eqcat;	/* Equinox of catalog (returned) */
+    double epcat;	/* Epoch of catalog (returned) */
+    int ireg, istar;
+    char cstr[32];
+    char temp[64];
+    struct StarCat *starcat;
+
+    /* Set up search query from arguments */
+    lurl = strlen (caturl);
+
+    /* Set up query for scat used as server */
+    if (!strncmp (caturl+lurl-4,"scat",4)) {
+
+	/* Make list of catalog numbers */
+	refcat = RefCat (refcatname,title,&syscat,&eqcat,&epcat,&mprop,&nmag);
+	for (i = 0; i < nnum; i++) {
+	    nfld = CatNumLen (refcat, unum[i], 0);
+	    CatNum (refcat, -nfld, 0, unum[i], numstr);
+	    if (i > 0) {
+		strcat (numlist, ",");
+		strcat (numlist, numstr);
+	    }
+	    else
+		strcpy (numlist, numstr);
+	    }
+
+	/* Set up search query */
+	wcscstr (cstr, sysout, eqout, epout);
+	sprintf (srchurl, "?catalog=%s&num=%s&ndec=4&outsys=%s",refcatname,numlist,csys);
+	if (epout != 0.0) {
+	    sprintf (temp, "&epoch=%.5f", epout);
+	    strcat (srchurl, temp);
+	    }
+	}
+
+    /* Set up query for ESO GSC server */
+    else if (!strncmp (caturl+lurl-10,"gsc-server",10)) {
+	ireg = (int) unum[0];
+	istar = (int) (10000.0 * (unum[0] - (double) ireg) + 0.5);
+	sprintf (srchurl, "?object=GSC%05d%05d&nout=1&f=8", ireg, istar);
+	if (nlog > 0)
+	    fprintf (stderr, "%s%s\n", caturl, srchurl);
+	}
+
+    /* Set up query for ESO USNO A server */
+    else if (!strncmp (caturl+lurl-12,"usnoa-server",12)) {
+	ireg = (int) unum[0];
+	istar = (int) (100000000.0 * (unum[0] - (double) ireg) + 0.5);
+	sprintf (srchurl, "?object=U%04d_%08d&n=1&format=8&", ireg, istar);
+	if (nlog > 0)
+	    fprintf (stderr,"%s%s\n", caturl, srchurl);
+	}
+
+    /* Run search across the web */
+    if ((tabtable = webopen (caturl, srchurl, nlog)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBRNUM: %s failed\n", srchurl);
+	return (0);
+	}
+
+    /* Return if no data */
+    if (tabtable->tabdata == NULL || strlen (tabtable->tabdata) == 0) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBRNUM: No data returned\n");
+	return (0);
+	}
+
+    /* Dump returned file and stop */
+    if (nlog < 0) {
+	(void) fwrite  (tabtable->tabbuff, tabtable->lbuff, 1, stdout);
+	exit (0);
+	}
+
+    /* Open returned Starbase table as a catalog */
+    if ((starcat = tabcatopen (caturl, tabtable, 0)) == NULL) {
+	if (nlog > 0)
+	    fprintf (stderr, "WEBRNUM: Could not open Starbase table as catalog\n");
+	return (0);
+	}
+
+    /* Extract desired sources from catalog  and return them */
+    return (tabrnum (srchurl, nnum, sysout, eqout, epout, &starcat, match,
+         unum, ura, udec, upra, updec, umag, utype, NULL, nlog));
+}
+
+
+struct TabTable *
+webopen (caturl, srchpar, nlog)
+
+char	*caturl;	/* URL of search engine */
+char	*srchpar;	/* Search engine parameters to append */
+int	nlog;		/* 1 to print diagnostic messages */
+{
+    char *srchurl;
+    int lsrch;
+    char *tabbuff;
+    int	lbuff = 0;
+    char *tabnew, *tabline, *lastline, *tempbuff, *tabold;
+    int formfeed = (char) 12;
+    struct TabTable *tabtable;
+    int ltab, lname;
+    int diag;
+    int tabdiff;
+    char *space2tab();
+
+    if (nlog == 1)
+	diag = 1;
+    else
+	diag = 0;
+
+    /* Combine catalog search engine URL and arguments */
+    lsrch = strlen (srchpar) + strlen (caturl) + 2;
+    if ((srchurl = (char *) malloc (lsrch)) == NULL)
+	return (NULL);
+    strcpy (srchurl, caturl);
+    strcat (srchurl, srchpar);
+
+    /* Open port to HTTP server, send command, and fill buffer with return */
+    if ((tabbuff = webbuff (srchurl, diag, &lbuff)) == NULL) {
+	fprintf (stderr,"WEBOPEN: cannot read URL %s\n", srchurl);
+	return (NULL);
+	}
+    if (!strchr (tabbuff, '	') && !strchr (tabbuff, ',') && !strchr (tabbuff, '|')) {
+	if (diag) {
+	    fprintf (stderr,"Message returned from %s\n", srchurl);
+	    fprintf (stderr,"%s\n", tabbuff);
+	    }
+	return (NULL);
+	}
+
+    /* Transform SDSS return into tab table */
+    if (strsrch (srchurl, "sdss")) {
+	tempbuff = tabbuff;
+	tabbuff = sdssc2t (tempbuff);
+	lbuff = strlen (tabbuff);
+	free (tempbuff);
+	}
+
+    /* Transform MAST GALEX  GSC 2 return into tab table */
+    else if (strsrch (srchurl, "galex")) {
+	tempbuff = tabbuff;
+	tabbuff = gsc2c2t (tempbuff);
+	lbuff = strlen (tabbuff);
+	free (tempbuff);
+	}
+
+    /* Transform CASB GSC 2 return into tab table */
+    else if (strsrch (srchurl, "gsss")) {
+	tempbuff = tabbuff;
+	tabbuff = gsc2t2t (tempbuff);
+	lbuff = strlen (tabbuff);
+	free (tempbuff);
+	}
+
+    /* Transform SkyBot return into tab table */
+    else if (strsrch (srchurl, "skybot")) {
+	tempbuff = tabbuff;
+	tabbuff = skybot2tab (tempbuff);
+	lbuff = strlen (tabbuff);
+	free (tempbuff);
+	}
+
+    /* Make sure that scat data is tab-separated (3 tabs found) */
+    else if (strsrch (srchurl, "scat")) {
+	tempbuff = strchr (tabbuff, '\t');
+	if (tempbuff != NULL) {
+	    tempbuff = strchr (tempbuff+1, '\t');
+	    if (tempbuff != NULL)
+		tempbuff = strchr (tempbuff+1, '\t');
+	    }	
+	if (tempbuff == NULL) {
+	    tempbuff = tabbuff;
+	    tabbuff = space2tab (tempbuff);
+	    lbuff = strlen (tabbuff);
+	    free (tempbuff);
+	    }
+	}
+    
+    /* Allocate tab table structure */
+    ltab = sizeof (struct TabTable);
+    if ((tabtable = (struct TabTable *) calloc (1, ltab)) == NULL) {
+	fprintf (stderr,"WEBOPEN: cannot allocate Tab Table structure for %s",
+	         srchurl);
+	return (NULL);
+	}
+
+    /* Save pointers to file contents */
+    tabtable->tabbuff = tabbuff;
+    tabtable->tabheader = tabtable->tabbuff;
+    tabtable->lbuff = lbuff;
+
+    /* Allocate space for and save catalog URL as filename */
+    lname = strlen (caturl) + 2;
+    if ((tabtable->filename = (char *) calloc (1, lname)) == NULL) {
+	fprintf (stderr,"WEBOPEN: cannot allocate filename %s in structure",
+	         caturl);
+	tabclose (tabtable);
+	return (NULL);
+	}
+    strcpy (tabtable->filename, caturl);
+
+    /* Allocate space for and save search string as tabname */
+    lname = strlen (srchpar) + 2;
+    if ((tabtable->tabname = (char *) calloc (1, lname)) == NULL) {
+	fprintf (stderr,"WEBOPEN: cannot allocate tabname %s in structure",
+	         srchurl);
+	tabclose (tabtable);
+	return (NULL);
+	}
+    strcpy (tabtable->tabname, srchpar);
+
+    /* Find column headings and start of data */
+    tabline = tabtable->tabheader;
+    lastline = NULL;
+    while (*tabline != '-' && tabline < tabtable->tabbuff+lbuff) {
+	lastline = tabline;
+	tabline = strchr (tabline,newline) + 1;
+	}
+    if (*tabline != '-') {
+	fprintf (stderr,"WEBOPEN: No - line in tab table %s",srchurl);
+	tabclose (tabtable);
+	return (NULL);
+	}
+    tabtable->tabhead = lastline;
+    tabtable->tabdata = strchr (tabline, newline) + 1;
+
+    /* Extract positions of keywords we will want to use */
+    if (!tabparse (tabtable)) {
+	fprintf (stderr,"TABOPEN: No columns in tab table %s\n",srchurl);
+	tabclose (tabtable);
+	return (NULL);
+	}
+
+    /* Enumerate entries in tab table catalog by counting newlines */
+    tabnew = tabtable->tabdata;
+    tabold = tabnew;
+    tabdiff = 0;
+    tabtable->nlines = 0;
+    while ((tabnew = strchr (tabnew, newline)) != NULL) {
+	tabdiff = tabnew - tabold;
+	tabnew = tabnew + 1;
+	tabtable->nlines = tabtable->nlines + 1;
+	if (*tabnew == formfeed)
+	    break;
+	if (!strncasecmp (tabnew, "[EOD]", 5))
+	    break;
+	tabold = tabnew;
+	}
+    if (tabdiff < 2 && tabtable->nlines > 0)
+	tabtable->nlines = tabtable->nlines - 1;
+
+    tabtable->tabline = tabtable->tabdata;
+    tabtable->iline = 1;
+    return (tabtable);
+}
+
+
+/* WEBBUFF -- Return character buffer from given URL */
+
+char *
+webbuff (url, diag, lbuff)
+
+char	*url;	/* URL to read */
+int	diag;	/* 1 to print diagnostic messages */
+int	*lbuff;	/* Length of buffer (returned) */
+{
+    File sok,*sok1;
+    char *server;
+    char linebuff[LINE];
+    char *buff;
+    char *tabbuff;
+    char *newbuff;
+    char *urlpath;
+    char *servurl;
+    int	status;
+    int lserver;
+    int chunked = 0;
+    int lread;
+    int lchunk, lline;
+    int nbcont = 0;
+    int lcbuff;
+    int lb, i, j;
+    int ltbuff;
+    int lcom;
+    char *cbcont;
+    char *newserver;
+    char *command;
+    char *sokptr;
+
+    *lbuff = 0;
+    newbuff = NULL;
+    diag = 0;
+
+    /* Extract server name and path from URL */
+    servurl = url;
+    if (!strncmp(url, "http://", 7))
+	servurl = servurl + 7;
+    urlpath = strchr (servurl, '/');
+    lserver = urlpath - servurl;
+    if ((server = (char *) malloc (lserver+2)) == NULL)
+	return (NULL);
+    strncpy (server, servurl, lserver);
+    server[lserver] = (char) 0;
+
+    /* Open port to HTTP server */
+    if ( !(sok = SokOpen (server, 80, XFREAD | XFWRITE)) ) {
+	free (server);
+	return (NULL);
+	}
+
+    /* Send HTTP GET command */
+    lcom = 32 + strlen (urlpath) + strlen (server);
+    command = (char *) calloc (lcom, 1);
+    sprintf (command, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n",urlpath,server);
+    fprintf (sok, command);
+    fflush(sok);
+    free (server);
+    for (i = 0; i < lcom; i++)
+	command[i] = (char) 0;
+    free (command);
+
+    (void) fscanf(sok, "%*s %d %*s\r\n", &status);
+
+    /* If Redirect code encounter, go to alternate URL at Location: */
+    if ( status == 301 || status == 302 || status == 303 || status == 307 ) {
+	sokptr = strsrch ((char *) sok->_ptr, "Location:") + 10;
+	newserver = strsrch (sokptr, "http");
+	j = 7;
+	while (newserver[j] != '/')
+	    j = j + 1;
+	if ((server = (char *) malloc (j+2)) == NULL) {
+	    (void) fclose (sok);
+	    return (NULL);
+	    }
+	strncpy (server, sokptr, j);
+	server[j] = (char) 0;
+
+	if (diag)
+	    fprintf (stderr,"HTTP Code %d: Temporary Redirect to %s\n",
+		     status, server);
+
+	/* Open port to HTTP server */
+	if ( (sok1 = SokOpen (server, 80, XFREAD | XFWRITE)) == NULL ) {
+	    free (server);
+	    return (NULL);
+	    }
+
+	/* Send HTTP GET command (simbad forward fails if HTTP/1.1 included) */
+	lcom = 32 + strlen (urlpath) + strlen (server);
+	command = (char *) calloc (lcom, 1);
+	sprintf(command, "GET %s\r\nHost: %s\r\n\r\n",urlpath,server);
+	fprintf(sok1, command);
+	fflush(sok1);
+	free (server);
+	for (i = 0; i < lcom; i++)
+	    command[i] = (char) 0;
+	free (command);
+
+	(void) fscanf(sok1, "%*s %d %*s\r\n", &status);
+
+	/* Close old socket structure and assign new structure to it */
+	(void) fclose (sok);
+	sok = sok1;
+	}
+
+    /* Skip continue lines
+    if (status == 100) {
+	while (status == 100)
+	    fscanf(sok, "%*s %d %*s\n", &status);
+	} */
+
+    /* If status is not 200 return without data */
+    if ( status != 200 ) {
+	if (diag)
+	    fprintf (stderr,"HTTP Code %d from  %s\n", status, server);
+	return (NULL);
+	}
+
+    /* Skip over http header of returned stuff */
+    while (fgets (linebuff, LINE, sok) ) {
+	if (diag)
+	    fprintf (stderr, "%s", linebuff);
+	if (strcsrch (linebuff, "chunked") != NULL)
+	    chunked = 1;
+	if (strcsrch (linebuff, "Content-length") != NULL) {
+	    if ((cbcont = strchr (linebuff, ':')) != NULL)
+		nbcont = atoi (cbcont+1);
+	    }
+	if (*linebuff == '\n') break;
+	if (*linebuff == '\r') break;
+	}
+
+    /* Read table into buffer in memory a chunk at a time */
+    tabbuff = NULL;
+    lb = 0;
+    if (chunked) {
+	lchunk = 1;
+	lline = 1;
+	*lbuff = 0;
+	ltbuff = 0;
+	while (lline > 0) {
+	    (void) fgets (linebuff, LINE, sok);
+	    lline = strlen (linebuff);
+	    if (lline < 1)
+		break;
+	    if (linebuff[lline-1] < 32)
+		linebuff[lline-1] = (char) 0;
+	    if (linebuff[lline-2] < 32)
+		linebuff[lline-2] = (char) 0;
+	    if (strlen (linebuff) <= 0)
+		continue;
+	    lchunk = (int) strtol (linebuff, NULL, 16);
+	    if (lchunk < 1)
+		break;
+	    /* else if (lchunk == 1)
+		continue; */
+	    if (diag)
+		fprintf (stderr, "%s (=%d)\n", linebuff, lchunk);
+	    lcbuff = ltbuff;
+	    ltbuff = ltbuff + lchunk;
+
+	    /* Allocate buffer on first time through */
+	    if (tabbuff == NULL) {
+		lb = 10 * ltbuff;
+		tabbuff = (char *) calloc ((size_t)lb, (size_t)1);
+		buff = tabbuff;
+		}
+
+	    /* Increase buffer size if this chunk will push it over current limit */
+	    else if (ltbuff > lb) {
+		lb = lb * 10;
+		newbuff = (char *) calloc ((size_t)lb, (size_t)1); 
+		movebuff (newbuff, tabbuff, lcbuff, 0, 0);
+		free (tabbuff);
+		tabbuff = newbuff;
+		buff = tabbuff + lcbuff;
+		newbuff = NULL;
+		}
+	    else {
+		buff = tabbuff + lcbuff;
+		}
+            (void) fread (buff, 1, lchunk, sok);
+	    buff[lchunk] = (char) 0;
+	    if (diag)
+		fprintf (stderr, "%s\n", buff);
+	    *lbuff = ltbuff;
+	    }
+	}
+
+    /* Read table all at once if total length is passed */
+    else if (nbcont > 0) {
+	tabbuff = (char *) calloc (1, nbcont+1);
+	tabbuff[nbcont] = (char) 0;
+	if ((lread = fread (tabbuff, 1, nbcont, sok)) <= 0) {
+	    free (tabbuff);
+	    tabbuff = NULL;
+	    }
+	}
+
+    /* Read table into buffer in memory a buffer-full at a time */
+    else {
+	lchunk = 8192;
+	*lbuff = 0;
+	buff = (char *) calloc (1, lchunk+8);
+	while ( (lread = fread (buff, 1, lchunk, sok)) > 0 ) {
+	    lcbuff = *lbuff;
+	    *lbuff = *lbuff + lread;
+	    if (tabbuff == NULL) {
+		tabbuff = (char *) malloc (*lbuff+8);
+		movebuff (tabbuff, buff, lread, 0, 0);
+		}
+	    else {
+		newbuff = (char *) malloc (*lbuff+8);
+		movebuff (newbuff, tabbuff, lcbuff, 0, 0);
+		free (tabbuff);
+		tabbuff = newbuff;
+		movebuff (tabbuff, buff, lread, lcbuff, 0);
+		}
+	    if (diag)
+		fprintf (stderr, "%s\n", buff);
+	    }
+	}
+    (void) fclose (sok);
+
+    return (tabbuff);
+}
+
+static void
+movebuff (dest, source, nbytes, offd, offs)
+
+char	*dest;		/* First byte of destination buffer */
+char	*source;	/* First byte of source buffer */
+int	nbytes;		/* Number of bytes to move */
+int	offd;		/* Offset from first byte of destination buffer */
+int	offs;		/* Offset from first byte of source buffer */
+{
+char *from, *last, *to;
+        from = source + offs;
+        to = dest + offd;
+        last = from + nbytes;
+        while (from < last) *(to++) = *(from++);
+        return;
+}
+
+
+/* sokFile.c
+ * copyright 1991, 1993, 1995, 1999 John B. Roll jr.
+ */
+
+static FILE *
+SokOpen(name, port, mode)
+	char *name;             /* "host:port" socket to open */
+	int   port;
+	int   mode;             /* mode of socket to open */
+{
+    int             xfd;        /* socket descriptor */
+    int             type;       /* type returned from FileINetParse */
+    struct sockaddr_in adrinet; /* socket structure parsed from name */
+    int             reuse_addr = 1;
+
+
+    File            f;          /* returned file descriptor */
+
+    if (!(type = FileINetParse(name, port, &adrinet)))
+	return NULL;
+
+    if ( type == 1 
+     || (mode & XFCREAT && mode & XFREAD && !(mode & XFWRITE)) ) {
+	if ( ((xfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+	  ||  setsockopt(xfd, SOL_SOCKET, SO_REUSEADDR,
+	             (char *) &reuse_addr, sizeof(reuse_addr)) < 0
+	  || (bind(xfd, (struct sockaddr *) & adrinet
+	                 ,sizeof(adrinet)) != 0)
+	  ||  listen(xfd, 5) ) {
+	    close(xfd);
+	    return NULL;
+	}
+      } else {
+	if (((xfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+	           || (connect(xfd, (struct sockaddr *) & adrinet
+	                       ,sizeof(adrinet)) != 0)) {
+	    close(xfd);
+	    return NULL;
+	}
+    }
+
+    f = fdopen (xfd, "r+");
+
+    return f;
+}
+
+
+static int
+FileINetParse(file, port, adrinet)
+	char *file;             /* host/socket pair to parse? */
+	int   port;
+	struct sockaddr_in *adrinet; /* socket info structure to fill? */
+{
+    struct hostent *hp;         /* -> hostent structure for host */
+    char            hostname[MAXHOSTNAMELENGTH + 12]; /* name of host */
+    char           *portstr;    /* internet port number (ascii) */
+    int             type = 2;   /* return code */
+    extern int gethostname();
+
+    if ( !strncmp(file, "http://", 7) ) {
+	file += 7;
+	if ( port == -1 ) port  = 80;
+    }
+
+    strcpy(hostname, file);
+
+#ifdef msdos
+    /* This is a DOS disk discriptor, not a machine name */
+    if ((!(file[0] == '.')) && file[1] == ':')
+	return 0;
+#endif
+
+    if ( (portstr = strchr(hostname, '/')) ) {
+	*portstr = '\0';
+    }
+
+    if ( (portstr = strchr(hostname, ':')) ) {
+	*portstr++ = '\0';
+
+	if ((port = strtol(portstr, NULL, 0)) == 0) {
+	    struct servent *service;
+
+	    if ((service = getservbyname(portstr, NULL)) == NULL)
+	        return 0;
+	    port = service->s_port;
+	}
+    }
+
+    if ( port == -1 ) return 0;
+
+    if (hostname[0] == '\0')
+	type = 1;
+    if (hostname[0] == '\0' || hostname[0] == '.')
+	if (gethostname(hostname, MAXHOSTNAMELENGTH) == -1)
+	    return 0;
+
+    if ((hp = gethostbyname(hostname)) == NULL)
+	return 0;
+
+    memset(adrinet, 0, sizeof(struct sockaddr_in));
+    adrinet->sin_family = AF_INET;
+    adrinet->sin_port = htons(port);
+    memcpy(&adrinet->sin_addr, hp->h_addr, hp->h_length);
+
+    return type;
+}
+
+char *
+space2tab (tabbuff)
+    char *tabbuff;	/* Tab table filled with spaces */
+{
+    char *newbuff, *line0, *line1, *ic, *icn, *tstart;
+    char cspace, ctab, cdash;
+    int lbuff;
+    int alltab = 0;
+    int notab = 0;
+
+    cspace = ' ';
+    cdash = '-';
+    ctab = '\t';
+    line0 = tabbuff;
+    lbuff = strlen (tabbuff);
+    newbuff = (char *) calloc (lbuff, 1);
+    icn = newbuff;
+    while ((line1 = strchr (line0, newline))) {
+	if (alltab == 0 && *(line1+1) == cdash) {
+	    alltab = 1;
+	    }
+	ic = line0;
+	notab = 1;
+	while (ic <= line1) {
+	    if (*ic != cspace)
+		*icn++ = *ic++;
+	    else {
+		if (alltab) {
+		    *icn++ = ctab;
+		    while (*ic++ == cspace) {
+			}
+		    ic--;
+		    }
+		else if (notab) {
+		    notab = 0;
+		    *icn++ = ctab;
+		    while (*ic++ == cspace) {
+			}
+		    ic--;
+		    }
+		else
+		    *icn++ = *ic++;
+		}
+	    }
+	line0 = line1 + 1;
+	notab = 1;
+	if (strlen (line0) < 1) {
+	    *icn++ = (char) 0;
+	    break;
+	    }
+	}
+    return (newbuff);
+}
+
+/* Nov 29 2000	New subroutines
+ * Dec 11 2000	Do not print messages unless nlog > 0
+ * Dec 12 2000	Fix problems with return if no stars
+ * Dec 18 2000	Clean up code after lint
+ *
+ * Jan  2 2001	Set MAXHOSTNAMELENGTH to 256, bypassing system constant
+ * Jan  3 2001	Include string.h, not strings.h
+ * Mar 19 2001	Drop argument types from declaration
+ * Mar 23 2001	Put number into argument list correctly in webrnum()
+ * Jun  7 2001	Add proper motion flag and number of magnitudes to RefCat()
+ * Jun 20 2001	Move webopen() declaration to wcscat.h
+ * Jun 28 2001	When reading chunked data, loop until nothing is read or [EOD]
+ * Jul 12 2001	Break out web access into subroutine webbuff()
+ * Sep  7 2001	Free server in webbuff()
+ * Sep 11 2001	Pass array of magnitude vectors
+ * Sep 14 2001	Pass sort type, if distance or magnitude
+ * Sep 14 2001	Add option to print entire returned file if nlog < 0
+ * Sep 21 2001	Debug searches of ESO USNO-A2.0 and GSC catalogs
+ *
+ * Apr  8 2002	Fix bug in ESO USNO-A2.0 server code
+ * Aug  6 2002	Make starcat->entmag and starcat->keymag into vectors
+ * Oct  3 2002	If nstarmax is less than 1, print results from web directly
+ *
+ * Jan 27 2003	Add maximum number of stars to be returned to webread()
+ * Jan 28 2003	Add number of decimal places to webread() and webrnum()
+ * Mar 12 2003	Fix bug in USNO-A2 server code
+ * Aug 22 2003	Add radi argument for inner edge of search annulus
+ * Nov 22 2003	Increase buffer size faster than reading in webbuff()
+ * Dec 12 2003	Fix calls to CatNumLen() and tabcatopen()
+ *
+ * Jan  5 2004	Convert SDSS table from comma-separated to tab-separated
+		in webopen(); initialize nbcont to 0 in webbuff()
+ * Jan 14 2004	Return error if data but no objects returned in webopen()
+ * Aug 30 2004	Send CR-LF termination to HTTP GET, not just LF
+ * Sep 10 2004	Print server messages only if verbose flag is on
+ *
+ * Jan  9 2006	Multiply max number of stars for ESO search to get all
+ * Apr  6 2006	Check for sdss in URL for Sloan parsing
+ * Jun 20 2006	Cast most stream I/O calls to void
+ * Oct 30 2006	Reset buffer length for SDSS tables
+ *
+ * Jan 10 2007	Add match to webrnum argument list for tabrnum()
+ * Jan 11 2007	Include fitsfile.h
+ * Mar 13 2007	Process CSV data from STScI MAST GALEX GSC2 catalog
+ * Mar 13 2007	Caselessly search for header info
+ * Apr 11 2007	Terminate buffer read as number of characters
+ * Jul 13 2007	Add SkyBot data transformation to webopen()
+ * Jul 17 2007	Change order of arguments in movebuff() so destination is first
+ * Jul 18 2007	Fix bug in chunked data reading
+ * Jul 19 2007	If last line of table has no content, drop it
+ * Aug 24 2007	Fix tab tables filled with spaces
+ * Aug 28 2007	Fix space2tab() declarations which passed on Solaris, not Linux
+ * Dec 31 2007	Fix chunk reading code in webbuff()
+ *
+ * Jan  5 2008	Add code to forward automatically if status=301|302|303|307
+ */
diff --git a/Code/src/libwcs/worldpos.c b/Code/src/libwcs/worldpos.c
new file mode 100644
index 0000000000000000000000000000000000000000..ac6a30d48c913f9a52166c9045208789a87cceee
--- /dev/null
+++ b/Code/src/libwcs/worldpos.c
@@ -0,0 +1,684 @@
+/*  worldpos.c -- WCS Algorithms from Classic AIPS.
+ *  June 20, 2006
+ *  Copyright (C) 1994-2006
+ *  Associated Universities, Inc. Washington DC, USA.
+ *  With code added by Doug Mink, Smithsonian Astrophysical Observatory
+ *                 and Allan Brighton and Andreas Wicenec, ESO
+
+ * Module:	worldpos.c
+ * Purpose:	Perform forward and reverse WCS computations for 8 projections
+ * Subroutine:	worldpos() converts from pixel location to RA,Dec 
+ * Subroutine:	worldpix() converts from RA,Dec         to pixel location   
+
+    -=-=-=-=-=-=-
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+    
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+   
+    Correspondence concerning AIPS should be addressed as follows:
+	   Internet email: aipsmail@nrao.edu
+	   Postal address: AIPS Group
+	                   National Radio Astronomy Observatory
+	                   520 Edgemont Road
+	                   Charlottesville, VA 22903-2475 USA
+
+    -=-=-=-=-=-=-
+
+    These two ANSI C functions, worldpos() and worldpix(), perform
+    forward and reverse WCS computations for 8 types of projective
+    geometries ("-SIN", "-TAN", "-ARC", "-NCP", "-GLS" or "-SFL", "-MER",
+     "-AIT", "-STG", "CAR", and "COE"):
+
+	worldpos() converts from pixel location to RA,Dec 
+	worldpix() converts from RA,Dec         to pixel location   
+
+    where "(RA,Dec)" are more generically (long,lat). These functions
+    are based on the WCS implementation of Classic AIPS, an
+    implementation which has been in production use for more than ten
+    years. See the two memos by Eric Greisen
+
+	ftp://fits.cv.nrao.edu/fits/documents/wcs/aips27.ps.Z
+	ftp://fits.cv.nrao.edu/fits/documents/wcs/aips46.ps.Z
+
+    for descriptions of the 8 projective geometries and the
+    algorithms.  Footnotes in these two documents describe the
+    differences between these algorithms and the 1993-94 WCS draft
+    proposal (see URL below). In particular, these algorithms support
+    ordinary field rotation, but not skew geometries (CD or PC matrix
+    cases). Also, the MER and AIT algorithms work correctly only for
+    CRVALi=(0,0). Users should note that GLS projections with yref!=0
+    will behave differently in this code than in the draft WCS
+    proposal.  The NCP projection is now obsolete (it is a special
+    case of SIN).  WCS syntax and semantics for various advanced
+    features is discussed in the draft WCS proposal by Greisen and
+    Calabretta at:
+    
+	ftp://fits.cv.nrao.edu/fits/documents/wcs/wcs.all.ps.Z
+    
+	        -=-=-=-
+
+    The original version of this code was Emailed to D.Wells on
+    Friday, 23 September by Bill Cotton <bcotton@gorilla.cv.nrao.edu>,
+    who described it as a "..more or less.. exact translation from the
+    AIPSish..". Changes were made by Don Wells <dwells@nrao.edu>
+    during the period October 11-13, 1994:
+    1) added GNU license and header comments
+    2) added testpos.c program to perform extensive circularity tests
+    3) changed float-->double to get more than 7 significant figures
+    4) testpos.c circularity test failed on MER and AIT. B.Cotton
+       found that "..there were a couple of lines of code [in] the wrong
+       place as a result of merging several Fortran routines." 
+    5) testpos.c found 0h wraparound in worldpix() and worldpos().
+    6) E.Greisen recommended removal of various redundant if-statements,
+       and addition of a 360d difference test to MER case of worldpos(). 
+    7) D.Mink changed input to data structure and implemented rotation matrix.
+*/
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include "wcs.h"
+
+int
+worldpos (xpix, ypix, wcs, xpos, ypos)
+
+/* Routine to determine accurate position for pixel coordinates */
+/* returns 0 if successful otherwise 1 = angle too large for projection; */
+/* does: -SIN, -TAN, -ARC, -NCP, -GLS or -SFL, -MER, -AIT projections */
+/* anything else is linear */
+
+/* Input: */
+double	xpix;		/* x pixel number  (RA or long without rotation) */
+double	ypix;		/* y pixel number  (Dec or lat without rotation) */
+struct WorldCoor *wcs;		/* WCS parameter structure */
+
+/* Output: */
+double	*xpos;		/* x (RA) coordinate (deg) */
+double	*ypos;		/* y (dec) coordinate (deg) */
+
+{
+  double cosr, sinr, dx, dy, dz, tx;
+  double sins, coss, dt, l, m, mg, da, dd, cos0, sin0;
+  double rat = 0.0;
+  double dect = 0.0;
+  double mt, a, y0, td, r2;  /* allan: for COE */
+  double dec0, ra0, decout, raout;
+  double geo1, geo2, geo3;
+  double cond2r=1.745329252e-2;
+  double twopi = 6.28318530717959;
+  double deps = 1.0e-5;
+
+  /* Structure elements */
+  double xref;		/* X reference coordinate value (deg) */
+  double yref;		/* Y reference coordinate value (deg) */
+  double xrefpix;	/* X reference pixel */
+  double yrefpix;	/* Y reference pixel */
+  double xinc;		/* X coordinate increment (deg) */
+  double yinc;		/* Y coordinate increment (deg) */
+  double rot;		/* Optical axis rotation (deg)  (N through E) */
+  int itype = wcs->prjcode;
+
+  /* Set local projection parameters */
+  xref = wcs->xref;
+  yref = wcs->yref;
+  xrefpix = wcs->xrefpix;
+  yrefpix = wcs->yrefpix;
+  xinc = wcs->xinc;
+  yinc = wcs->yinc;
+  rot = degrad (wcs->rot);
+  cosr = cos (rot);
+  sinr = sin (rot);
+
+  /* Offset from ref pixel */
+  dx = xpix - xrefpix;
+  dy = ypix - yrefpix;
+
+  /* Scale and rotate using CD matrix */
+  if (wcs->rotmat) {
+    tx = dx * wcs->cd[0] + dy * wcs->cd[1];
+    dy = dx * wcs->cd[2] + dy * wcs->cd[3];
+    dx = tx;
+    }
+
+  /* Scale and rotate using CDELTn and CROTA2 */
+  else {
+
+    /* Check axis increments - bail out if either 0 */
+    if ((xinc==0.0) || (yinc==0.0)) {
+      *xpos=0.0;
+      *ypos=0.0;
+      return 2;
+      }
+
+    /* Scale using CDELT */
+    dx = dx * xinc;
+    dy = dy * yinc;
+
+    /* Take out rotation from CROTA */
+    if (rot != 0.0) {
+      tx = dx * cosr - dy * sinr;
+      dy = dx * sinr + dy * cosr;
+      dx = tx;
+      }
+    }
+
+  /* Flip coordinates if necessary */
+  if (wcs->coorflip) {
+    tx = dx;
+    dx = dy;
+    dy = tx;
+    }
+
+  /* Default, linear result for error or pixel return  */
+  *xpos = xref + dx;
+  *ypos = yref + dy;
+  if (itype <= 0)
+    return 0;
+
+  /* Convert to radians  */
+  if (wcs->coorflip) {
+    dec0 = degrad (xref);
+    ra0 = degrad (yref);
+    }
+  else {
+    ra0 = degrad (xref);
+    dec0 = degrad (yref);
+    }
+  l = degrad (dx);
+  m = degrad (dy);
+  sins = l*l + m*m;
+  decout = 0.0;
+  raout = 0.0;
+  cos0 = cos (dec0);
+  sin0 = sin (dec0);
+
+  /* Process by case  */
+  switch (itype) {
+
+    case WCS_CAR:   /* -CAR Cartesian (was WCS_PIX pixel and WCS_LIN linear) */
+      rat =  ra0 + l;
+      dect = dec0 + m;
+      break;
+
+    case WCS_SIN: /* -SIN sin*/ 
+      if (sins>1.0) return 1;
+      coss = sqrt (1.0 - sins);
+      dt = sin0 * coss + cos0 * m;
+      if ((dt>1.0) || (dt<-1.0)) return 1;
+      dect = asin (dt);
+      rat = cos0 * coss - sin0 * m;
+      if ((rat==0.0) && (l==0.0)) return 1;
+      rat = atan2 (l, rat) + ra0;
+      break;
+
+    case WCS_TAN:   /* -TAN tan */
+    case WCS_TNX:   /* -TNX tan with polynomial correction */
+      if (sins>1.0) return 1;
+      dect = cos0 - m * sin0;
+      if (dect==0.0) return 1;
+      rat = ra0 + atan2 (l, dect);
+      dect = atan (cos(rat-ra0) * (m * cos0 + sin0) / dect);
+      break;
+
+    case WCS_ARC:   /* -ARC Arc*/
+      if (sins>=twopi*twopi/4.0) return 1;
+      sins = sqrt(sins);
+      coss = cos (sins);
+      if (sins!=0.0) sins = sin (sins) / sins;
+      else
+	sins = 1.0;
+      dt = m * cos0 * sins + sin0 * coss;
+      if ((dt>1.0) || (dt<-1.0)) return 1;
+      dect = asin (dt);
+      da = coss - dt * sin0;
+      dt = l * sins * cos0;
+      if ((da==0.0) && (dt==0.0)) return 1;
+      rat = ra0 + atan2 (dt, da);
+      break;
+
+    case WCS_NCP:   /* -NCP North celestial pole*/
+      dect = cos0 - m * sin0;
+      if (dect==0.0) return 1;
+      rat = ra0 + atan2 (l, dect);
+      dt = cos (rat-ra0);
+      if (dt==0.0) return 1;
+      dect = dect / dt;
+      if ((dect>1.0) || (dect<-1.0)) return 1;
+      dect = acos (dect);
+      if (dec0<0.0) dect = -dect;
+      break;
+
+    case WCS_GLS:   /* -GLS global sinusoid */
+    case WCS_SFL:   /* -SFL Samson-Flamsteed */
+      dect = dec0 + m;
+      if (fabs(dect)>twopi/4.0) return 1;
+      coss = cos (dect);
+      if (fabs(l)>twopi*coss/2.0) return 1;
+      rat = ra0;
+      if (coss>deps) rat = rat + l / coss;
+      break;
+
+    case WCS_MER:   /* -MER mercator*/
+      dt = yinc * cosr + xinc * sinr;
+      if (dt==0.0) dt = 1.0;
+      dy = degrad (yref/2.0 + 45.0);
+      dx = dy + dt / 2.0 * cond2r;
+      dy = log (tan (dy));
+      dx = log (tan (dx));
+      geo2 = degrad (dt) / (dx - dy);
+      geo3 = geo2 * dy;
+      geo1 = cos (degrad (yref));
+      if (geo1<=0.0) geo1 = 1.0;
+      rat = l / geo1 + ra0;
+      if (fabs(rat - ra0) > twopi) return 1; /* added 10/13/94 DCW/EWG */
+      dt = 0.0;
+      if (geo2!=0.0) dt = (m + geo3) / geo2;
+      dt = exp (dt);
+      dect = 2.0 * atan (dt) - twopi / 4.0;
+      break;
+
+    case WCS_AIT:   /* -AIT Aitoff*/
+      dt = yinc*cosr + xinc*sinr;
+      if (dt==0.0) dt = 1.0;
+      dt = degrad (dt);
+      dy = degrad (yref);
+      dx = sin(dy+dt)/sqrt((1.0+cos(dy+dt))/2.0) -
+	  sin(dy)/sqrt((1.0+cos(dy))/2.0);
+      if (dx==0.0) dx = 1.0;
+      geo2 = dt / dx;
+      dt = xinc*cosr - yinc* sinr;
+      if (dt==0.0) dt = 1.0;
+      dt = degrad (dt);
+      dx = 2.0 * cos(dy) * sin(dt/2.0);
+      if (dx==0.0) dx = 1.0;
+      geo1 = dt * sqrt((1.0+cos(dy)*cos(dt/2.0))/2.0) / dx;
+      geo3 = geo2 * sin(dy) / sqrt((1.0+cos(dy))/2.0);
+      rat = ra0;
+      dect = dec0;
+      if ((l==0.0) && (m==0.0)) break;
+      dz = 4.0 - l*l/(4.0*geo1*geo1) - ((m+geo3)/geo2)*((m+geo3)/geo2) ;
+      if ((dz>4.0) || (dz<2.0)) return 1;;
+      dz = 0.5 * sqrt (dz);
+      dd = (m+geo3) * dz / geo2;
+      if (fabs(dd)>1.0) return 1;;
+      dd = asin (dd);
+      if (fabs(cos(dd))<deps) return 1;;
+      da = l * dz / (2.0 * geo1 * cos(dd));
+      if (fabs(da)>1.0) return 1;;
+      da = asin (da);
+      rat = ra0 + 2.0 * da;
+      dect = dd;
+      break;
+
+    case WCS_STG:   /* -STG Sterographic*/
+      dz = (4.0 - sins) / (4.0 + sins);
+      if (fabs(dz)>1.0) return 1;
+      dect = dz * sin0 + m * cos0 * (1.0+dz) / 2.0;
+      if (fabs(dect)>1.0) return 1;
+      dect = asin (dect);
+      rat = cos(dect);
+      if (fabs(rat)<deps) return 1;
+      rat = l * (1.0+dz) / (2.0 * rat);
+      if (fabs(rat)>1.0) return 1;
+      rat = asin (rat);
+      mg = 1.0 + sin(dect) * sin0 + cos(dect) * cos0 * cos(rat);
+      if (fabs(mg)<deps) return 1;
+      mg = 2.0 * (sin(dect) * cos0 - cos(dect) * sin0 * cos(rat)) / mg;
+      if (fabs(mg-m)>deps) rat = twopi/2.0 - rat;
+      rat = ra0 + rat;
+      break;
+
+    case WCS_COE:    /* COE projection code from Andreas Wicenic, ESO */
+      td = tan (dec0);
+      y0 = 1.0 / td;
+      mt = y0 - m;
+      if (dec0 < 0.)
+	a = atan2 (l,-mt);
+      else
+	a = atan2 (l, mt);
+      rat = ra0 - (a / sin0);
+      r2 = (l * l) + (mt * mt);
+      dect = asin (1.0 / (sin0 * 2.0) * (1.0 + sin0*sin0 * (1.0 - r2)));
+      break;
+  }
+
+  /* Return RA in range  */
+  raout = rat;
+  decout = dect;
+  if (raout-ra0>twopi/2.0) raout = raout - twopi;
+  if (raout-ra0<-twopi/2.0) raout = raout + twopi;
+  if (raout < 0.0) raout += twopi; /* added by DCW 10/12/94 */
+
+  /* Convert units back to degrees  */
+  *xpos = raddeg (raout);
+  *ypos = raddeg (decout);
+
+  return 0;
+}  /* End of worldpos */
+
+
+int
+worldpix (xpos, ypos, wcs, xpix, ypix)
+
+/*-----------------------------------------------------------------------*/
+/* routine to determine accurate pixel coordinates for an RA and Dec     */
+/* returns 0 if successful otherwise:                                    */
+/*  1 = angle too large for projection;                                  */
+/*  2 = bad values                                                       */
+/* does: SIN, TAN, ARC, NCP, GLS or SFL, MER, AIT, STG, CAR, COE projections    */
+/* anything else is linear                                               */
+
+/* Input: */
+double	xpos;		/* x (RA) coordinate (deg) */
+double	ypos;		/* y (dec) coordinate (deg) */
+struct WorldCoor *wcs;	/* WCS parameter structure */
+
+/* Output: */
+double	*xpix;		/* x pixel number  (RA or long without rotation) */
+double	*ypix;		/* y pixel number  (dec or lat without rotation) */
+{
+  double dx, dy, ra0, dec0, ra, dec, coss, sins, dt, da, dd, sint;
+  double l, m, geo1, geo2, geo3, sinr, cosr, tx, x, a2, a3, a4;
+  double rthea,gamby2,a,b,c,phi,an,rap,v,tthea,co1,co2,co3,co4,ansq; /* COE */
+  double cond2r=1.745329252e-2, deps=1.0e-5, twopi=6.28318530717959;
+
+/* Structure elements */
+  double xref;		/* x reference coordinate value (deg) */
+  double yref;		/* y reference coordinate value (deg) */
+  double xrefpix;	/* x reference pixel */
+  double yrefpix;	/* y reference pixel */
+  double xinc;		/* x coordinate increment (deg) */
+  double yinc;		/* y coordinate increment (deg) */
+  double rot;		/* Optical axis rotation (deg)  (from N through E) */
+  int itype;
+
+  /* Set local projection parameters */
+  xref = wcs->xref;
+  yref = wcs->yref;
+  xrefpix = wcs->xrefpix;
+  yrefpix = wcs->yrefpix;
+  xinc = wcs->xinc;
+  yinc = wcs->yinc;
+  rot = degrad (wcs->rot);
+  cosr = cos (rot);
+  sinr = sin (rot);
+
+  /* Projection type */
+  itype = wcs->prjcode;
+
+  /* Nonlinear position */
+  if (itype > 0) {
+    if (wcs->coorflip) {
+      dec0 = degrad (xref);
+      ra0 = degrad (yref);
+      dt = xpos - yref;
+      }
+    else {
+      ra0 = degrad (xref);
+      dec0 = degrad (yref);
+      dt = xpos - xref;
+      }
+
+    /* 0h wrap-around tests added by D.Wells 10/12/1994: */
+    /* Modified to exclude weird reference pixels by D.Mink 2/3/2004 */
+    if (xrefpix*xinc > 180.0 || xrefpix*xinc < -180.0) {
+	if (dt > 360.0) xpos -= 360.0;
+	if (dt < 0.0) xpos += 360.0;
+	}
+    else {
+	if (dt > 180.0) xpos -= 360.0;
+	if (dt < -180.0) xpos += 360.0;
+	}
+    /* NOTE: changing input argument xpos is OK (call-by-value in C!) */
+
+    ra = degrad (xpos);
+    dec = degrad (ypos);
+
+    /* Compute direction cosine */
+    coss = cos (dec);
+    sins = sin (dec);
+    l = sin(ra-ra0) * coss;
+    sint = sins * sin(dec0) + coss * cos(dec0) * cos(ra-ra0);
+    }
+  else {
+    l = 0.0;
+    sint = 0.0;
+    sins = 0.0;
+    coss = 0.0;
+    ra = 0.0;
+    dec = 0.0;
+    ra0 = 0.0;
+    dec0 = 0.0;
+    m = 0.0;
+    }
+
+  /* Process by case  */
+  switch (itype) {
+
+    case WCS_CAR:   /* -CAR Cartesian */
+      l = ra - ra0;
+      m = dec - dec0;
+      break;
+
+    case WCS_SIN:   /* -SIN sin*/ 
+	if (sint<0.0) return 1;
+	m = sins * cos(dec0) - coss * sin(dec0) * cos(ra-ra0);
+	break;
+
+    case WCS_TAN:   /* -TAN tan */
+	if (sint<=0.0) return 1;
+ 	m = sins * sin(dec0) + coss * cos(dec0) * cos(ra-ra0);
+	l = l / m;
+	m = (sins * cos(dec0) - coss * sin(dec0) * cos(ra-ra0)) / m;
+	break;
+
+    case WCS_ARC:   /* -ARC Arc*/
+	m = sins * sin(dec0) + coss * cos(dec0) * cos(ra-ra0);
+	if (m<-1.0) m = -1.0;
+	if (m>1.0) m = 1.0;
+	m = acos (m);
+	if (m!=0) 
+	    m = m / sin(m);
+	else
+	    m = 1.0;
+	l = l * m;
+	m = (sins * cos(dec0) - coss * sin(dec0) * cos(ra-ra0)) * m;
+	break;
+
+    case WCS_NCP:   /* -NCP North celestial pole*/
+	if (dec0==0.0) 
+	    return 1;  /* can't stand the equator */
+	else
+	    m = (cos(dec0) - coss * cos(ra-ra0)) / sin(dec0);
+	break;
+
+    case WCS_GLS:   /* -GLS global sinusoid */
+    case WCS_SFL:   /* -SFL Samson-Flamsteed */
+	dt = ra - ra0;
+	if (fabs(dec)>twopi/4.0) return 1;
+	if (fabs(dec0)>twopi/4.0) return 1;
+	m = dec - dec0;
+	l = dt * coss;
+	break;
+
+    case WCS_MER:   /* -MER mercator*/
+	dt = yinc * cosr + xinc * sinr;
+	if (dt==0.0) dt = 1.0;
+	dy = degrad (yref/2.0 + 45.0);
+	dx = dy + dt / 2.0 * cond2r;
+	dy = log (tan (dy));
+	dx = log (tan (dx));
+	geo2 = degrad (dt) / (dx - dy);
+	geo3 = geo2 * dy;
+	geo1 = cos (degrad (yref));
+	if (geo1<=0.0) geo1 = 1.0;
+	dt = ra - ra0;
+	l = geo1 * dt;
+	dt = dec / 2.0 + twopi / 8.0;
+	dt = tan (dt);
+	if (dt<deps) return 2;
+	m = geo2 * log (dt) - geo3;
+	break;
+
+    case WCS_AIT:   /* -AIT Aitoff*/
+	l = 0.0;
+	m = 0.0;
+	da = (ra - ra0) / 2.0;
+	if (fabs(da)>twopi/4.0) return 1;
+	dt = yinc*cosr + xinc*sinr;
+	if (dt==0.0) dt = 1.0;
+	dt = degrad (dt);
+	dy = degrad (yref);
+	dx = sin(dy+dt)/sqrt((1.0+cos(dy+dt))/2.0) -
+	     sin(dy)/sqrt((1.0+cos(dy))/2.0);
+	if (dx==0.0) dx = 1.0;
+	geo2 = dt / dx;
+	dt = xinc*cosr - yinc* sinr;
+	if (dt==0.0) dt = 1.0;
+	dt = degrad (dt);
+	dx = 2.0 * cos(dy) * sin(dt/2.0);
+	if (dx==0.0) dx = 1.0;
+	geo1 = dt * sqrt((1.0+cos(dy)*cos(dt/2.0))/2.0) / dx;
+	geo3 = geo2 * sin(dy) / sqrt((1.0+cos(dy))/2.0);
+	dt = sqrt ((1.0 + cos(dec) * cos(da))/2.0);
+	if (fabs(dt)<deps) return 3;
+	l = 2.0 * geo1 * cos(dec) * sin(da) / dt;
+	m = geo2 * sin(dec) / dt - geo3;
+	break;
+
+    case WCS_STG:   /* -STG Sterographic*/
+	da = ra - ra0;
+	if (fabs(dec)>twopi/4.0) return 1;
+	dd = 1.0 + sins * sin(dec0) + coss * cos(dec0) * cos(da);
+	if (fabs(dd)<deps) return 1;
+	dd = 2.0 / dd;
+	l = l * dd;
+	m = dd * (sins * cos(dec0) - coss * sin(dec0) * cos(da));
+	break;
+
+    case WCS_COE:    /* allan: -COE projection added, AW, ESO*/
+	gamby2 = sin (dec0);
+	tthea = tan (dec0);
+	rthea = 1. / tthea;
+	a = -2. * tthea;
+	b = tthea * tthea;
+	c = tthea / 3.;
+	a2 = a * a;
+	a3 = a2 * a;
+	a4 = a2 * a2;
+	co1 = a/2.;
+	co2 = -0.125 * a2 + b/2.;
+	co3 = -0.25 * a*b + 0.0625 * a3 + c/2.0;
+	co4 = -0.125 * b*b - 0.25 * a*c + 0.1875 * b*a2 - (5.0/128.0)*a4;
+	phi = ra0 - ra;
+	an = phi * gamby2;
+	v = dec - dec0;
+	rap = rthea * (1.0 + v * (co1+v * (co2+v * (co3+v * co4))));
+	ansq = an * an;
+	if (wcs->rotmat)
+	    l = rap * an * (1.0 - ansq/6.0) * (wcs->cd[0] / fabs(wcs->cd[0]));
+	else
+	    l = rap * an * (1.0 - ansq/6.0) * (xinc / fabs(xinc));
+	m = rthea - (rap * (1.0 - ansq/2.0));
+	break;
+
+    }  /* end of itype switch */
+
+  /* Convert back to degrees  */
+  if (itype > 0) {
+    dx = raddeg (l);
+    dy = raddeg (m);
+    }
+
+  /* For linear or pixel projection */
+  else {
+    dx = xpos - xref;
+    dy = ypos - yref;
+    }
+
+  if (wcs->coorflip) {
+    tx = dx;
+    dx = dy;
+    dy = tx;
+    }
+
+  /* Scale and rotate using CD matrix */
+  if (wcs->rotmat) {
+    tx = dx * wcs->dc[0] + dy * wcs->dc[1];
+    dy = dx * wcs->dc[2] + dy * wcs->dc[3];
+    dx = tx;
+    }
+
+  /* Scale and rotate using CDELTn and CROTA2 */
+  else {
+
+    /* Correct for rotation */
+    if (rot!=0.0) {
+      tx = dx*cosr + dy*sinr;
+      dy = dy*cosr - dx*sinr;
+      dx = tx;
+      }
+
+    /* Scale using CDELT */
+    if (xinc != 0.)
+      dx = dx / xinc;
+    if (yinc != 0.)
+      dy = dy / yinc;
+    }
+
+  /* Convert to pixels  */
+  *xpix = dx + xrefpix;
+  if (itype == WCS_CAR) {
+    if (*xpix > wcs->nxpix) {
+      x = *xpix - (360.0 / xinc);
+      if (x > 0.0) *xpix = x;
+      }
+    else if (*xpix < 0) {
+      x = *xpix + (360.0 / xinc);
+      if (x <= wcs->nxpix) *xpix = x;
+      }
+    }
+  *ypix = dy + yrefpix;
+
+  return 0;
+}  /* end worldpix */
+
+ 
+/* Oct 26 1995	Fix bug which interchanged RA and Dec twice when coorflip
+ *
+ * Oct 31 1996	Fix CD matrix use in WORLDPIX
+ * Nov  4 1996	Eliminate extra code for linear projection in WORLDPIX
+ * Nov  5 1996	Add coordinate flip in WORLDPIX
+ *
+ * May 22 1997	Avoid angle wraparound when CTYPE is pixel
+ * Jun  4 1997	Return without angle conversion from worldpos if type is PIXEL
+ *
+ * Oct 20 1997	Add chip rotation; compute rotation angle trig functions
+ * Jan 23 1998	Change PCODE to PRJCODE
+ * Jan 26 1998	Remove chip rotation code
+ * Feb  5 1998	Make cd[] and dc[] vectors; use xinc, yinc, rot from init
+ * Feb 23 1998	Add NOAO TNX projection as TAN
+ * Apr 28 1998  Change projection flags to WCS_*
+ * May 27 1998	Skip limit checking for linear projection
+ * Jun 25 1998	Fix inverse for CAR projection
+ * Aug  5 1998	Allan Brighton: Added COE projection (code from A. Wicenec, ESO)
+ * Sep 30 1998	Fix bug in COE inverse code to get sign correct
+ *
+ * Oct 21 1999	Drop unused y from worldpix()
+ *
+ * Apr  3 2002	Use GLS and SFL interchangeably
+ *
+ * Feb  3 2004	Let ra be >180 in worldpix() if ref pixel is >180 deg away
+ *
+ * Jun 20 2006	Initialize uninitialized variables
+ */
diff --git a/Code/src/loadingwidget.h b/Code/src/loadingwidget.h
new file mode 100644
index 0000000000000000000000000000000000000000..39406b09079b87f0a35c53dfee4432dbf7000d3f
--- /dev/null
+++ b/Code/src/loadingwidget.h
@@ -0,0 +1,29 @@
+#ifndef LOADINGWIDGET_H
+#define LOADINGWIDGET_H
+
+#include <QWidget>
+
+namespace Ui {
+class LoadingWidget;
+}
+
+class LoadingWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit LoadingWidget(QWidget *parent = 0);
+    ~LoadingWidget();
+    void loadingEnded();
+    void init();
+    void setFileName(QString name);
+
+
+private slots:
+    void on_dismissPushButton_clicked();
+
+private:
+    Ui::LoadingWidget *ui;
+};
+
+#endif // LOADINGWIDGET_H
diff --git a/Code/src/lutcustomize.cpp b/Code/src/lutcustomize.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4a2b80cfb86d164d14d5b4289fd5392c816fba33
--- /dev/null
+++ b/Code/src/lutcustomize.cpp
@@ -0,0 +1,195 @@
+#include "lutcustomize.h"
+#include "ui_lutcustomize.h"
+#include "vtkExtractHistogram.h"
+#include "vispoint.h"
+#include "vtkTable.h"
+#include "vtkDoubleArray.h"
+
+LutCustomize::LutCustomize(vtkwindow_new *v, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::LutCustomize)
+{
+    ui->setupUi(this);
+    // this->setWindowFlags(Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
+
+    vtkwin=v;
+
+    setRange();
+    plotHistogram();
+
+    ui->fromSlider->setMinimum(range[0]);
+    ui->fromSlider->setMaximum(range[1]);
+
+    ui->toSlider->setMinimum(range[0]);
+    ui->toSlider->setMaximum(range[1]);
+
+    ui->fromSlider->setValue(range[0]);
+    ui->toSlider->setValue(range[1]);
+
+
+}
+
+LutCustomize::~LutCustomize()
+{
+    delete ui;
+}
+
+
+void LutCustomize::setRange()
+{
+    range=new double[2];
+    vtkwin->pp->getPolyData()->GetPointData()->GetScalars(vtkwin->ui->scalarComboBox->currentText().toStdString().c_str())->GetRange(range);
+
+    ui->fromValue->setText( QString::number(  vtkwin->pp->actualFrom ) );
+    ui->toValue->setText(  QString::number( vtkwin->pp->actualTo ) );
+    /*
+   // drawLine(vtkwin->pp->actualFrom,vtkwin->pp->actualTo);
+    ui->fromValue->setText( QString::number( range[0] ) );
+    ui->toValue->setText( QString::number( range[1] ) );
+*/
+}
+
+void LutCustomize::plotHistogram()
+{
+    qDebug()<<"plotHistogram";
+    vtkwin->pp->getPolyData()->GetPointData()->GetScalars(vtkwin->ui->scalarComboBox->currentText().toStdString().c_str())->GetRange(range);
+    qDebug()<<"get range";
+    vtkSmartPointer<vtkExtractHistogram> extraction = vtkSmartPointer<vtkExtractHistogram>::New();
+    extraction->SetInputData( vtkwin->pp->getPolyData() );
+    extraction->UseCustomBinRangesOn();
+    extraction->SetBinCount(vtkwin->vispoint->getOrigin()->getbinNumber());
+    extraction->SetCustomBinRanges(range);
+    extraction->Update();
+    qDebug()<<"set extraction";
+    vtkTable* const histogram = extraction->GetOutput();
+    qDebug()<<"create histogram";
+    vtkDoubleArray* const bin_extents = vtkDoubleArray::SafeDownCast(histogram->GetRowData()->GetArray("bin_extents"));
+    vtkIntArray* const bin_values = vtkIntArray::SafeDownCast(histogram->GetRowData()->GetArray((int)1));
+    qDebug()<<"create bin "<<QString::number(bin_extents->GetSize())<<" "<<QString::number(bin_values->GetSize());
+    y_range=new double[2];
+    bin_values->GetRange(y_range);
+    qDebug()<<"set range";
+    QVector<double> x(vtkwin->vispoint->getOrigin()->getbinNumber());
+    QVector<double> y(vtkwin->vispoint->getOrigin()->getbinNumber());
+    qDebug()<<"set x y "<<QString::number(x.size())<<" "<<QString::number(y.size());
+    vtkIdType nPoints = bin_extents->GetDataSize();
+    qDebug()<<"nPoints "<<QString::number(nPoints);
+    int cnt=0;
+    for (vtkIdType i = 0; i < nPoints; ++i)
+    {
+        qDebug()<<"i "<<QString::number(i);
+        qDebug()<<QString::number(bin_extents->GetValue(i));
+        //x[cnt]=bin_extents->GetValue(i);
+        x.push_back(bin_extents->GetValue(i));
+        qDebug()<<QString::number(bin_values->GetValue(i));
+        //y[cnt]=bin_values->GetValue(i);
+        y.push_back(bin_values->GetValue(i));
+        cnt++;
+    }
+    qDebug()<<"calculated histogram";
+
+    // create graph and assign data to it:
+    ui->histogramWidget->addGraph();
+    ui->histogramWidget->graph(0)->setData(x, y);
+    if (vtkwin->getSelectedScale()=="Log")
+    {
+        ui->histogramWidget->xAxis->setScaleType(QCPAxis::stLogarithmic);
+        ui->histogramWidget->xAxis->setScaleLogBase(10);
+    }
+    // give the axes some labels:
+    // ui->histogramWidget->xAxis->setTickLabels(false);
+    ui->histogramWidget->yAxis->setTickLabels(false);
+
+
+    // set axes ranges, so we see all data:
+    ui->histogramWidget->xAxis->setRange(range[0],range[1]);
+    ui->histogramWidget->yAxis->setRange(y_range[0], y_range[1]);
+    ui->histogramWidget->replot();
+
+
+
+}
+
+void LutCustomize::drawLine(double from, double to)
+{
+
+
+    if(fromLine!=0 && toLine!=0 )
+    {
+
+        ui->histogramWidget->removeItem(fromLine);
+        ui->histogramWidget->removeItem(toLine);
+    }
+
+
+    fromLine = new QCPItemLine(ui->histogramWidget);
+    toLine = new QCPItemLine(ui->histogramWidget);
+
+    double *range=new double[2];
+
+    QPen pen;
+    pen.setColor( Qt::red );
+
+    fromLine->setPen( pen );
+    pen.setColor( Qt::green );
+    toLine->setPen( pen);
+
+    fromLine->start->setCoords(from, 0);
+    fromLine->end->setCoords(from, y_range[1]);
+
+    toLine->start->setCoords(to, 0);
+    toLine->end->setCoords(to, y_range[1]);
+
+    ui->histogramWidget->addItem(fromLine);
+    ui->histogramWidget->addItem(toLine);
+
+    ui->histogramWidget->replot();
+
+
+
+}
+
+
+void LutCustomize::on_cancelPushButton_clicked()
+{
+    this->close();
+}
+
+void LutCustomize::closeEvent ( QCloseEvent * event )
+{
+    vtkwin->activateWindow();
+    vtkwin->show();
+
+}
+
+void LutCustomize::on_ShowColorbarCheckBox_clicked(bool checked)
+{
+    vtkwin->showColorbar(checked);
+}
+
+void LutCustomize::on_fromSlider_sliderMoved(int position)
+{
+    ui->fromValue->setText(QString::number(position));
+    drawLine(position,ui->toValue->text().toDouble());
+}
+
+
+
+void LutCustomize::on_toSlider_sliderMoved(int position)
+{
+    ui->toValue->setText(QString::number(position));
+    drawLine(ui->fromValue->text().toDouble(),position);
+}
+
+void LutCustomize::on_okPushButton_clicked()
+{
+    vtkwin->pp->setLookupTable(ui->fromValue->text().toDouble(), ui->toValue->text().toDouble());
+    //ES
+    if(vtkwin->ui->glyphShapeComboBox->isEnabled()){
+        qDebug()<<"Re draw glyph";
+        vtkwin->getGlyphActor()->GetMapper()->SetLookupTable(vtkwin->pp->getLookupTable());
+        vtkwin->getGlyphActor()->GetMapper()->SetScalarRange(vtkwin->pp->getLookupTable()->GetRange());
+    }
+    //End ES
+    this->close();
+}
diff --git a/Code/src/lutcustomize.h b/Code/src/lutcustomize.h
new file mode 100644
index 0000000000000000000000000000000000000000..7451139b81c4f1cc4aa8123e8e84a6751c9ff22a
--- /dev/null
+++ b/Code/src/lutcustomize.h
@@ -0,0 +1,45 @@
+#ifndef LUTCUSTOMIZE_H
+#define LUTCUSTOMIZE_H
+
+#include <QWidget>
+#include "vtkwindow_new.h"
+#include "qcustomplot.h"
+
+namespace Ui {
+class LutCustomize;
+}
+
+class LutCustomize : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit LutCustomize( vtkwindow_new *v,QWidget *parent = 0);
+    ~LutCustomize();
+
+private slots:
+    void on_cancelPushButton_clicked();
+    void closeEvent ( QCloseEvent * event );
+    void on_ShowColorbarCheckBox_clicked(bool checked);
+    void plotHistogram();
+    void drawLine(double from, double to);
+    void setRange();
+
+
+    void on_fromSlider_sliderMoved(int position);
+
+
+    void on_toSlider_sliderMoved(int position);
+
+    void on_okPushButton_clicked();
+
+private:
+    Ui::LutCustomize *ui;
+    vtkwindow_new *vtkwin;
+    QCPItemLine *fromLine ;
+    QCPItemLine *toLine;
+    double *range;
+    double *y_range;
+};
+
+#endif // LUTCUSTOMIZE_H
diff --git a/Code/src/luteditor.cpp b/Code/src/luteditor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ffbc581ecc791ef956218e80293287af52620e8b
--- /dev/null
+++ b/Code/src/luteditor.cpp
@@ -0,0 +1,2800 @@
+/***************************************************************************
+ *   Copyright (C) 2008 by Gabriella Caniglia *
+ *  gabriella.caniglia@oact.inaf.it *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc->,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include <cstdlib>
+#include <cstring>
+
+#include "luteditor.h"
+#include "color.h"
+#include <QString>
+#include <QStringList>
+#include "qdebug.h"
+
+#include "vtkLookupTable.h"
+
+
+//---------------------------------------------------------------------
+void SelectLookTable(  QString palette, vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+
+
+    QStringList palettes;
+    palettes  << "Default"
+              << "Default Step"
+              << "EField"
+              << "Glow"
+              << "Gray"
+              << "MinMax"
+              << "PhysicsContour"
+              << "PureRed"
+              << "PureGreen"
+              << "PureBlue"
+              << "Run1"
+              << "Run2"
+              << "Sar"
+              << "Temperature"
+              << "TenStep"
+              << "VolRenGlow"
+              << "VolRenRGB"
+              << "VolRenRGB"
+              << "VolRenTwoLev"
+              << "AllYellow"
+              << "AllCyane"
+              << "AllViolet"
+              << "AllWhite"
+              << "AllBlack"
+              << "AllRed"
+              << "AllGreen"
+              << "AllBlu";
+
+    switch( palettes.indexOf(palette))
+    {
+    case 0:  lutDefault( lut);        break;
+    case 1:  lutDefaultStep( lut);    break;
+    case 2:  lutEField( lut);         break;
+    case 3:  lutGlow( lut);           break;
+    case 4:  lutGray( lut);           break;
+    case 5:  lutMinMax( lut);         break;
+    case 6:  lutPhysicsContour( lut); break;
+    case 7:  lutPureRed( lut);        break;
+    case 8:  lutPureGreen( lut);      break;
+    case 9:  lutPureBlue( lut);       break;
+    case 10:  lutRun1( lut);          break;
+    case 11: lutRun2( lut);           break;
+    case 12: lutSar( lut);            break;
+    case 13: lutTemperature( lut);    break;
+    case 14: lutTenStep( lut);        break;
+    case 15: lutVolRenGlow( lut);     break;
+    case 16: lutVolRenGreen( lut);    break;
+    case 17: lutVolRenRGB( lut);      break;
+    case 18: lutVolRenTwoLev( lut);   break;
+    case 19: lutAllYellow( lut);      break;
+    case 20: lutAllCyane( lut);      break;
+    case 21: lutAllViolet( lut);      break;
+    case 22: lutAllWhite( lut);      break;
+    case 23: lutAllBlack( lut);      break;
+    case 24: lutAllRed( lut);      break;
+    case 25: lutAllGreen( lut);      break;
+    case 26: lutAllBlu( lut);      break;
+
+    default: lutDefault( lut);        break;
+    }
+}
+
+//---------------------------------------------------------------------
+void lutVolRenGreen( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //VolRenGreen
+    unsigned char v[256*4] =
+    {
+        0, 0, 255, 0,
+        0, 1, 253, 0,
+        0, 3, 252, 1,
+        0, 5, 251, 1,
+        0, 7, 249, 2,
+        0, 9, 248, 3,
+        0, 11, 247, 3,
+        0, 13, 245, 4,
+        0, 14, 244, 5,
+        0, 16, 243, 5,
+        0, 18, 241, 6,
+        0, 20, 240, 6,
+        0, 22, 239, 7,
+        0, 24, 237, 8,
+        0, 26, 236, 8,
+        0, 28, 235, 9,
+        0, 29, 233, 10,
+        0, 31, 232, 10,
+        0, 33, 231, 11,
+        0, 35, 229, 11,
+        0, 37, 228, 12,
+        0, 39, 227, 13,
+        0, 41, 225, 13,
+        0, 43, 224, 14,
+        0, 44, 223, 15,
+        0, 46, 221, 15,
+        0, 48, 220, 16,
+        0, 50, 219, 16,
+        0, 52, 217, 17,
+        0, 54, 216, 18,
+        0, 56, 215, 18,
+        0, 58, 213, 19,
+        0, 59, 212, 20,
+        0, 61, 211, 20,
+        0, 63, 209, 21,
+        0, 65, 208, 21,
+        0, 67, 207, 22,
+        0, 69, 206, 23,
+        0, 71, 204, 23,
+        0, 73, 203, 24,
+        0, 75, 202, 25,
+        0, 76, 200, 25,
+        0, 78, 199, 26,
+        0, 80, 198, 26,
+        0, 82, 196, 27,
+        0, 84, 195, 28,
+        0, 86, 194, 28,
+        0, 88, 192, 29,
+        0, 89, 191, 30,
+        0, 91, 190, 30,
+        0, 93, 188, 31,
+        0, 95, 187, 31,
+        0, 97, 186, 32,
+        0, 99, 184, 33,
+        0, 101, 183, 33,
+        0, 103, 182, 34,
+        0, 105, 180, 35,
+        0, 106, 179, 35,
+        0, 108, 178, 36,
+        0, 110, 176, 36,
+        0, 112, 175, 37,
+        0, 114, 174, 38,
+        0, 116, 172, 38,
+        0, 118, 171, 39,
+        0, 119, 170, 40,
+        0, 121, 168, 40,
+        0, 123, 167, 41,
+        0, 125, 166, 41,
+        0, 127, 164, 42,
+        0, 129, 163, 43,
+        0, 131, 162, 43,
+        0, 133, 160, 44,
+        0, 135, 159, 45,
+        0, 136, 158, 45,
+        0, 138, 157, 46,
+        0, 140, 155, 46,
+        0, 142, 154, 47,
+        0, 144, 153, 48,
+        0, 146, 151, 48,
+        0, 148, 150, 49,
+        0, 149, 149, 50,
+        0, 151, 147, 50,
+        0, 153, 146, 51,
+        0, 155, 145, 51,
+        0, 157, 143, 52,
+        0, 159, 142, 53,
+        0, 161, 141, 53,
+        0, 163, 139, 54,
+        0, 165, 138, 55,
+        0, 166, 137, 55,
+        0, 168, 135, 56,
+        0, 170, 134, 56,
+        0, 172, 133, 57,
+        0, 174, 131, 58,
+        0, 176, 130, 58,
+        0, 178, 129, 59,
+        0, 179, 127, 60,
+        0, 181, 126, 60,
+        0, 183, 125, 61,
+        0, 185, 123, 61,
+        0, 187, 122, 62,
+        0, 189, 121, 63,
+        0, 191, 119, 63,
+        0, 193, 118, 64,
+        0, 195, 117, 65,
+        0, 196, 115, 65,
+        0, 198, 114, 66,
+        0, 200, 113, 66,
+        0, 202, 111, 67,
+        0, 204, 110, 68,
+        0, 206, 109, 68,
+        0, 208, 108, 69,
+        0, 210, 106, 70,
+        0, 211, 105, 70,
+        0, 213, 104, 71,
+        0, 215, 102, 71,
+        0, 217, 101, 72,
+        0, 219, 100, 73,
+        0, 221, 98, 73,
+        0, 223, 97, 74,
+        0, 225, 96, 75,
+        0, 226, 94, 75,
+        0, 228, 93, 76,
+        0, 230, 92, 76,
+        0, 232, 90, 77,
+        0, 234, 89, 78,
+        0, 236, 88, 78,
+        0, 238, 86, 79,
+        0, 239, 85, 80,
+        0, 241, 84, 80,
+        0, 243, 82, 81,
+        0, 245, 81, 81,
+        0, 247, 80, 82,
+        0, 249, 78, 83,
+        0, 251, 77, 83,
+        0, 253, 76, 84,
+        0, 255, 74, 85,
+        1, 254, 75, 85,
+        3, 255, 76, 86,
+        5, 254, 76, 86,
+        7, 255, 77, 87,
+        9, 255, 78, 88,
+        11, 255, 78, 88,
+        13, 255, 79, 89,
+        15, 255, 80, 90,
+        17, 255, 80, 90,
+        19, 255, 81, 91,
+        21, 255, 82, 91,
+        22, 255, 82, 92,
+        24, 255, 83, 93,
+        26, 255, 84, 93,
+        28, 255, 84, 94,
+        30, 255, 85, 95,
+        32, 255, 86, 95,
+        34, 255, 86, 96,
+        36, 255, 87, 96,
+        38, 255, 88, 97,
+        40, 255, 88, 98,
+        42, 255, 89, 98,
+        43, 255, 90, 99,
+        45, 255, 90, 100,
+        47, 255, 91, 100,
+        49, 255, 92, 101,
+        51, 255, 92, 101,
+        53, 255, 93, 102,
+        55, 255, 94, 103,
+        57, 254, 94, 103,
+        59, 255, 95, 104,
+        61, 254, 96, 105,
+        63, 255, 96, 105,
+        65, 255, 97, 106,
+        66, 255, 98, 106,
+        68, 255, 98, 107,
+        70, 255, 99, 108,
+        72, 255, 100, 108,
+        74, 255, 100, 109,
+        76, 255, 101, 110,
+        78, 255, 102, 110,
+        80, 255, 102, 111,
+        82, 255, 103, 111,
+        84, 255, 104, 112,
+        86, 255, 104, 113,
+        87, 255, 105, 113,
+        89, 255, 106, 114,
+        91, 255, 106, 115,
+        93, 255, 107, 115,
+        95, 255, 108, 116,
+        97, 255, 108, 117,
+        99, 254, 109, 117,
+        101, 255, 110, 118,
+        103, 254, 110, 118,
+        105, 255, 111, 119,
+        107, 255, 112, 120,
+        109, 255, 112, 120,
+        110, 255, 113, 121,
+        112, 255, 114, 122,
+        114, 255, 114, 122,
+        116, 255, 115, 123,
+        118, 255, 116, 123,
+        120, 255, 116, 124,
+        122, 255, 117, 125,
+        124, 255, 118, 125,
+        126, 255, 118, 126,
+        128, 255, 119, 127,
+        130, 255, 120, 127,
+        131, 255, 120, 128,
+        133, 255, 121, 128,
+        135, 254, 122, 129,
+        137, 255, 122, 130,
+        139, 254, 123, 130,
+        141, 255, 124, 131,
+        143, 255, 124, 132,
+        145, 255, 125, 132,
+        147, 255, 126, 133,
+        149, 255, 126, 133,
+        151, 255, 127, 134,
+        153, 255, 128, 135,
+        154, 255, 128, 135,
+        156, 255, 129, 136,
+        158, 255, 130, 137,
+        160, 255, 130, 137,
+        162, 255, 131, 138,
+        164, 255, 132, 138,
+        166, 255, 132, 139,
+        168, 255, 133, 140,
+        170, 255, 134, 140,
+        172, 254, 134, 141,
+        174, 255, 135, 142,
+        175, 255, 136, 142,
+        177, 255, 136, 143,
+        179, 254, 137, 143,
+        181, 255, 138, 144,
+        183, 255, 138, 145,
+        185, 255, 139, 145,
+        187, 255, 140, 146,
+        189, 255, 140, 147,
+        191, 255, 141, 147,
+        193, 255, 142, 148,
+        195, 255, 142, 148,
+        197, 254, 143, 149,
+        198, 255, 144, 150,
+        200, 254, 144, 150,
+        202, 255, 145, 151,
+        204, 255, 146, 152,
+        206, 255, 146, 152,
+        208, 255, 147, 153,
+        210, 255, 148, 153,
+        212, 255, 148, 154,
+        214, 255, 149, 155,
+        216, 254, 150, 155,
+        218, 255, 150, 156,
+        219, 255, 151, 157,
+        221, 255, 152, 157,
+        223, 255, 152, 158,
+        225, 255, 153, 158,
+        227, 255, 154, 159
+    };
+
+    lut->SetNumberOfTableValues(256);
+    for(int i=0; i<256; i++)
+    {
+        int k = i*4;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+    }
+};
+
+//---------------------------------------------------------------------
+void lutVolRenGlow( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //volrenGlow
+    unsigned char v[256*4] =
+    {
+        0, 0, 0, 0,
+        0, 0, 0, 1,
+        5, 0, 0, 2,
+        5, 0, 0, 3,
+        9, 0, 0, 4,
+        9, 0, 0, 5,
+        13, 0, 0, 6,
+        13, 0, 5, 7,
+        17, 0, 5, 8,
+        17, 0, 5, 9,
+        21, 0, 5, 10,
+        21, 0, 5, 11,
+        25, 0, 5, 12,
+        25, 0, 5, 13,
+        29, 0, 5, 14,
+        29, 0, 9, 15,
+        33, 0, 9, 16,
+        33, 0, 9, 17,
+        37, 0, 9, 18,
+        37, 0, 9, 19,
+        41, 0, 9, 20,
+        41, 0, 9, 21,
+        45, 0, 9, 22,
+        45, 0, 9, 23,
+        49, 0, 13, 24,
+        49, 0, 13, 25,
+        53, 0, 13, 26,
+        53, 0, 13, 27,
+        57, 0, 13, 28,
+        57, 0, 13, 29,
+        61, 0, 13, 30,
+        61, 5, 13, 31,
+        65, 5, 13, 32,
+        65, 5, 13, 33,
+        69, 5, 13, 34,
+        69, 5, 13, 35,
+        73, 5, 17, 36,
+        73, 5, 17, 37,
+        77, 5, 17, 38,
+        81, 5, 17, 39,
+        81, 5, 17, 40,
+        85, 5, 17, 41,
+        85, 5, 17, 42,
+        89, 5, 17, 43,
+        89, 9, 17, 44,
+        93, 9, 17, 45,
+        93, 9, 17, 46,
+        97, 9, 17, 47,
+        97, 9, 17, 48,
+        101, 9, 17, 49,
+        101, 9, 17, 50,
+        105, 9, 17, 51,
+        105, 9, 17, 52,
+        109, 13, 17, 53,
+        109, 13, 17, 54,
+        113, 13, 17, 55,
+        113, 13, 17, 56,
+        117, 13, 17, 57,
+        117, 13, 17, 58,
+        121, 13, 17, 59,
+        125, 13, 17, 60,
+        125, 13, 17, 61,
+        128, 17, 17, 62,
+        128, 17, 17, 63,
+        132, 17, 21, 64,
+        132, 17, 21, 65,
+        136, 17, 21, 66,
+        136, 17, 21, 67,
+        140, 17, 21, 68,
+        140, 21, 21, 69,
+        144, 21, 21, 70,
+        144, 21, 21, 71,
+        148, 21, 21, 72,
+        148, 21, 21, 73,
+        152, 25, 21, 74,
+        152, 25, 21, 75,
+        156, 25, 21, 76,
+        156, 25, 21, 77,
+        156, 25, 21, 78,
+        160, 29, 21, 79,
+        160, 29, 21, 80,
+        164, 29, 21, 81,
+        164, 29, 21, 82,
+        168, 33, 21, 83,
+        168, 33, 21, 84,
+        168, 33, 21, 85,
+        172, 33, 21, 86,
+        172, 37, 21, 87,
+        176, 37, 21, 88,
+        176, 37, 21, 89,
+        176, 37, 21, 90,
+        180, 41, 21, 91,
+        180, 41, 21, 92,
+        180, 41, 21, 93,
+        184, 45, 21, 94,
+        184, 45, 21, 95,
+        188, 45, 21, 96,
+        188, 49, 21, 97,
+        188, 49, 21, 98,
+        192, 49, 21, 99,
+        192, 53, 21, 100,
+        192, 53, 21, 101,
+        192, 53, 21, 102,
+        196, 57, 21, 103,
+        196, 57, 21, 104,
+        196, 61, 21, 105,
+        200, 61, 21, 106,
+        200, 61, 21, 107,
+        200, 65, 21, 108,
+        200, 65, 21, 109,
+        204, 69, 21, 110,
+        204, 69, 21, 111,
+        204, 69, 21, 112,
+        208, 73, 21, 113,
+        208, 73, 21, 114,
+        208, 77, 21, 115,
+        208, 77, 21, 116,
+        208, 81, 21, 117,
+        212, 81, 21, 118,
+        212, 85, 21, 119,
+        212, 85, 21, 120,
+        212, 89, 21, 121,
+        212, 89, 21, 122,
+        216, 93, 21, 123,
+        216, 93, 21, 124,
+        216, 97, 21, 125,
+        216, 97, 21, 126,
+        216, 101, 21, 127,
+        220, 101, 21, 128,
+        220, 105, 21, 129,
+        220, 105, 21, 130,
+        220, 109, 21, 131,
+        220, 109, 21, 132,
+        220, 113, 21, 133,
+        220, 113, 21, 134,
+        224, 117, 21, 135,
+        224, 117, 25, 136,
+        224, 121, 25, 137,
+        224, 121, 25, 138,
+        224, 125, 25, 139,
+        224, 125, 25, 140,
+        224, 128, 25, 141,
+        228, 128, 25, 142,
+        228, 132, 25, 143,
+        228, 132, 29, 144,
+        228, 136, 29, 145,
+        228, 136, 29, 146,
+        228, 140, 29, 147,
+        228, 140, 29, 148,
+        228, 144, 33, 149,
+        232, 144, 33, 150,
+        232, 144, 33, 151,
+        232, 148, 33, 152,
+        232, 148, 33, 153,
+        232, 152, 37, 154,
+        232, 152, 37, 155,
+        232, 156, 37, 156,
+        232, 156, 37, 157,
+        236, 160, 41, 158,
+        236, 160, 41, 159,
+        236, 160, 41, 160,
+        236, 164, 41, 161,
+        236, 164, 45, 162,
+        236, 168, 45, 163,
+        236, 168, 45, 164,
+        236, 172, 49, 165,
+        236, 172, 49, 166,
+        236, 172, 49, 167,
+        240, 176, 53, 168,
+        240, 176, 53, 169,
+        240, 180, 53, 170,
+        240, 180, 57, 171,
+        240, 180, 57, 172,
+        240, 184, 61, 173,
+        240, 184, 61, 174,
+        240, 188, 61, 175,
+        240, 188, 65, 176,
+        240, 188, 65, 177,
+        240, 192, 69, 178,
+        244, 192, 69, 179,
+        244, 192, 73, 180,
+        244, 192, 73, 181,
+        244, 192, 77, 182,
+        244, 200, 77, 183,
+        244, 200, 81, 184,
+        244, 200, 81, 185,
+        244, 204, 85, 186,
+        244, 204, 85, 187,
+        244, 204, 89, 188,
+        244, 208, 89, 189,
+        244, 208, 93, 190,
+        244, 208, 93, 191,
+        248, 212, 97, 192,
+        248, 212, 97, 193,
+        248, 212, 101, 194,
+        248, 216, 105, 195,
+        248, 216, 105, 196,
+        248, 216, 109, 197,
+        248, 220, 109, 198,
+        248, 220, 113, 199,
+        248, 220, 113, 200,
+        248, 224, 117, 201,
+        248, 224, 121, 202,
+        248, 224, 121, 203,
+        248, 224, 125, 204,
+        248, 228, 125, 205,
+        248, 228, 128, 206,
+        248, 228, 128, 207,
+        248, 228, 132, 208,
+        248, 232, 136, 209,
+        248, 232, 136, 210,
+        252, 232, 140, 211,
+        252, 232, 144, 212,
+        252, 236, 144, 213,
+        252, 236, 148, 214,
+        252, 236, 148, 215,
+        252, 236, 152, 216,
+        252, 240, 156, 217,
+        252, 240, 156, 218,
+        252, 240, 160, 219,
+        252, 240, 160, 220,
+        252, 240, 164, 221,
+        252, 240, 168, 222,
+        252, 244, 168, 223,
+        252, 244, 172, 224,
+        252, 244, 172, 225,
+        252, 244, 176, 226,
+        252, 244, 180, 227,
+        252, 244, 180, 228,
+        252, 248, 184, 229,
+        252, 248, 188, 230,
+        252, 248, 188, 231,
+        252, 248, 192, 232,
+        252, 248, 192, 233,
+        252, 248, 196, 234,
+        252, 248, 200, 235,
+        252, 248, 200, 236,
+        252, 252, 204, 237,
+        252, 252, 208, 238,
+        252, 252, 208, 239,
+        252, 252, 212, 240,
+        252, 252, 212, 241,
+        252, 252, 216, 242,
+        252, 252, 220, 243,
+        252, 252, 220, 244,
+        252, 252, 224, 245,
+        252, 252, 228, 246,
+        252, 252, 228, 247,
+        252, 252, 232, 248,
+        252, 252, 236, 249,
+        252, 252, 236, 250,
+        252, 252, 240, 251,
+        252, 252, 244, 252,
+        252, 252, 244, 253,
+        252, 252, 248, 254,
+        255, 255, 255, 255
+    };
+
+    lut->SetNumberOfTableValues(256);
+    for(int i=0; i<256; i++)
+    {
+        int k = i*4;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+    }
+};
+
+//---------------------------------------------------------------------
+void lutTenStep( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //tensteps
+    unsigned char v[10*3] =
+    {
+        29,  0,   134,
+        0,   18,  163,
+        0,   74,  188,
+        0,   159, 195,
+        0,   201, 150,
+        0,   209, 12,
+        141, 217, 0,
+        220, 221, 0,
+        226, 138, 0,
+        231, 48,  0,
+    };
+
+    lut->SetNumberOfTableValues(10);
+    for(int i=0; i<10; i++)
+    {
+        int k = i*3;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+    }
+};
+
+//---------------------------------------------------------------------
+void lutTemperature( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //temperature
+    unsigned char v[256*3] =
+    {
+        255,255,255,
+        251,251,253,
+        249,247,255,
+        246,244,253,
+        242,242,255,
+        239,239,253,
+        237,235,255,
+        233,232,255,
+        230,230,253,
+        228,226,255,
+        225,223,253,
+        221,221,255,
+        219,218,253,
+        216,214,255,
+        212,212,255,
+        209,209,255,
+        207,205,255,
+        205,202,253,
+        202,200,255,
+        198,196,253,
+        195,193,255,
+        193,191,253,
+        189,188,255,
+        186,184,255,
+        184,181,255,
+        181,179,255,
+        177,175,253,
+        175,172,255,
+        172,170,253,
+        168,167,255,
+        165,163,255,
+        163,161,255,
+        160,158,255,
+        156,154,253,
+        154,151,255,
+        151,149,253,
+        149,144,255,
+        145,142,253,
+        142,138,255,
+        140,135,255,
+        137,133,255,
+        133,130,255,
+        130,126,253,
+        128,124,255,
+        124,121,253,
+        121,117,255,
+        119,114,255,
+        116,112,255,
+        112,109,255,
+        110,105,253,
+        107,103,255,
+        105,100,253,
+        102,96,255,
+        98,93,253,
+        96,91,255,
+        93,87,255,
+        89,84,253,
+        86,82,255,
+        84,79,253,
+        80,75,255,
+        77,73,253,
+        75,70,255,
+        72,66,255,
+        68,63,255,
+        66,61,255,
+        66,59,253,
+        68,59,251,
+        70,59,249,
+        70,58,249,
+        72,58,247,
+        73,56,246,
+        73,56,244,
+        75,56,244,
+        77,54,242,
+        77,54,240,
+        79,54,239,
+        79,52,237,
+        80,52,237,
+        82,52,235,
+        84,52,233,
+        84,51,232,
+        86,49,232,
+        86,49,230,
+        87,49,228,
+        89,49,228,
+        91,47,226,
+        91,47,225,
+        93,47,223,
+        93,45,221,
+        96,45,221,
+        96,45,219,
+        98,43,218,
+        98,43,216,
+        100,42,214,
+        100,42,214,
+        103,42,212,
+        103,40,211,
+        105,40,209,
+        105,40,209,
+        107,38,207,
+        107,38,205,
+        110,38,205,
+        110,38,202,
+        112,36,202,
+        112,35,200,
+        114,35,198,
+        114,35,198,
+        117,33,196,
+        117,33,195,
+        119,33,193,
+        119,33,193,
+        121,31,191,
+        121,31,189,
+        124,31,188,
+        124,29,186,
+        126,29,186,
+        126,28,184,
+        128,28,182,
+        128,28,181,
+        130,26,181,
+        131,26,179,
+        133,26,177,
+        133,24,175,
+        135,24,175,
+        135,24,174,
+        137,24,172,
+        138,22,170,
+        140,22,170,
+        140,22,168,
+        142,21,167,
+        142,19,165,
+        144,19,163,
+        145,19,163,
+        147,19,161,
+        147,17,160,
+        149,17,158,
+        149,17,158,
+        151,15,156,
+        153,15,154,
+        154,15,154,
+        154,14,151,
+        156,14,151,
+        156,12,149,
+        158,12,147,
+        160,12,147,
+        161,10,145,
+        161,10,144,
+        163,10,142,
+        165,10,142,
+        167,8,137,
+        168,8,135,
+        172,8,131,
+        174,8,128,
+        175,8,126,
+        179,8,121,
+        181,8,119,
+        182,7,116,
+        186,7,112,
+        188,7,110,
+        191,7,107,
+        193,5,103,
+        195,5,100,
+        198,5,96,
+        200,5,93,
+        202,5,91,
+        205,5,86,
+        207,5,84,
+        209,5,80,
+        212,3,77,
+        214,3,75,
+        216,3,72,
+        219,3,68,
+        221,3,65,
+        223,3,61,
+        226,3,59,
+        228,1,56,
+        230,1,52,
+        232,1,49,
+        235,1,45,
+        237,1,42,
+        239,1,40,
+        242,1,36,
+        244,1,33,
+        246,0,29,
+        249,0,26,
+        249,0,26,
+        249,0,26,
+        249,0,26,
+        251,0,26,
+        251,0,24,
+        251,0,24,
+        251,0,24,
+        251,0,24,
+        251,0,24,
+        251,0,24,
+        251,0,22,
+        253,0,22,
+        253,0,22,
+        253,0,22,
+        253,0,22,
+        253,0,22,
+        253,0,21,
+        253,0,21,
+        253,0,21,
+        255,0,21,
+        255,3,21,
+        255,8,21,
+        255,12,21,
+        255,17,21,
+        255,22,21,
+        255,26,22,
+        255,31,22,
+        255,35,22,
+        255,40,22,
+        255,45,22,
+        255,49,22,
+        255,54,22,
+        255,59,22,
+        255,63,22,
+        255,68,22,
+        255,73,22,
+        255,77,24,
+        255,82,24,
+        255,86,24,
+        255,91,24,
+        255,96,24,
+        255,100,24,
+        255,105,24,
+        255,110,24,
+        255,114,24,
+        255,119,24,
+        255,123,24,
+        255,128,26,
+        255,133,26,
+        255,137,24,
+        255,142,24,
+        255,147,24,
+        255,154,22,
+        255,158,22,
+        255,163,22,
+        255,168,21,
+        255,175,21,
+        255,179,19,
+        255,184,19,
+        255,191,19,
+        255,195,17,
+        255,200,17,
+        255,205,17,
+        255,212,17,
+        255,216,15,
+        255,221,15,
+        255,226,15,
+        255,232,14,
+        255,237,14,
+        255,242,12,
+        255,249,12,
+    };
+
+    lut->SetNumberOfTableValues(256);
+
+    for(int i=0; i<256; i++)
+    {
+        int k = i*3;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+    }
+
+
+};
+
+//---------------------------------------------------------------------
+void lutSar( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //sar
+    unsigned char v[256*3] =
+    {
+        255,255,255,
+        251,251,253,
+        246,246,253,
+        244,246,253,
+        242,242,253,
+        239,239,251,
+        235,237,251,
+        232,232,251,
+        230,232,251,
+        226,228,249,
+        223,226,249,
+        221,223,249,
+        218,219,249,
+        214,219,246,
+        212,214,246,
+        209,214,246,
+        207,212,246,
+        204,209,246,
+        200,207,246,
+        198,205,246,
+        195,204,244,
+        191,202,244,
+        188,198,244,
+        186,198,244,
+        182,195,242,
+        179,195,242,
+        177,193,242,
+        174,189,242,
+        172,188,239,
+        168,186,239,
+        167,186,239,
+        163,186,239,
+        161,184,239,
+        158,181,239,
+        156,179,239,
+        153,181,237,
+        149,177,237,
+        147,177,237,
+        144,175,237,
+        142,174,235,
+        137,175,235,
+        137,174,235,
+        133,174,235,
+        130,172,232,
+        128,170,232,
+        126,170,232,
+        123,168,232,
+        121,170,232,
+        117,168,232,
+        116,167,232,
+        112,168,230,
+        110,167,230,
+        107,167,230,
+        105,167,230,
+        103,167,228,
+        100,165,228,
+        98,165,228,
+        93,163,228,
+        89,163,226,
+        87,163,226,
+        86,165,226,
+        84,165,226,
+        80,163,225,
+        79,165,225,
+        77,165,225,
+        75,163,223,
+        73,165,223,
+        68,163,223,
+        66,163,223,
+        65,167,221,
+        61,167,221,
+        59,165,221,
+        56,168,221,
+        54,168,219,
+        51,168,219,
+        51,168,219,
+        47,170,219,
+        45,168,218,
+        42,168,218,
+        40,172,216,
+        36,172,216,
+        35,172,216,
+        33,172,216,
+        33,174,216,
+        28,175,214,
+        28,174,214,
+        24,175,214,
+        24,175,214,
+        21,179,212,
+        17,179,212,
+        17,179,212,
+        14,181,212,
+        10,184,211,
+        10,182,211,
+        7,186,209,
+        5,186,209,
+        1,186,209,
+        1,188,209,
+        0,191,209,
+        0,193,209,
+        0,195,209,
+        0,200,209,
+        0,200,209,
+        0,204,209,
+        0,207,209,
+        0,211,209,
+        0,211,207,
+        0,211,205,
+        0,212,202,
+        0,212,198,
+        0,212,198,
+        0,212,193,
+        0,212,193,
+        0,212,189,
+        0,212,186,
+        0,214,184,
+        0,214,184,
+        0,214,181,
+        0,214,179,
+        0,214,177,
+        0,214,174,
+        0,214,172,
+        0,216,167,
+        0,216,165,
+        0,216,163,
+        0,216,161,
+        0,216,158,
+        0,216,156,
+        0,216,154,
+        0,218,151,
+        0,218,147,
+        0,218,145,
+        0,219,142,
+        0,219,140,
+        0,219,137,
+        0,219,137,
+        0,219,133,
+        0,219,130,
+        0,219,128,
+        0,221,124,
+        0,221,123,
+        0,221,121,
+        0,221,117,
+        0,221,114,
+        0,221,110,
+        0,221,110,
+        0,223,107,
+        0,223,103,
+        0,223,102,
+        0,223,100,
+        0,223,98,
+        0,223,93,
+        0,223,91,
+        0,225,87,
+        0,225,84,
+        0,225,84,
+        0,226,80,
+        0,226,77,
+        0,226,73,
+        0,226,72,
+        0,226,70,
+        0,226,68,
+        0,226,63,
+        0,228,59,
+        0,228,58,
+        0,228,56,
+        0,228,54,
+        0,228,51,
+        0,228,47,
+        0,228,43,
+        0,230,40,
+        0,230,38,
+        0,230,35,
+        0,230,35,
+        0,230,29,
+        0,230,28,
+        0,230,26,
+        0,232,22,
+        0,232,17,
+        0,232,15,
+        0,232,14,
+        0,232,12,
+        0,232,7,
+        0,232,3,
+        0,232,3,
+        0,232,0,
+        0,232,0,
+        5,235,0,
+        8,235,0,
+        8,235,0,
+        14,235,0,
+        14,235,0,
+        19,235,0,
+        24,235,0,
+        31,237,0,
+        38,237,0,
+        45,237,0,
+        51,237,0,
+        58,237,0,
+        66,237,0,
+        72,237,0,
+        79,239,0,
+        86,239,0,
+        93,239,0,
+        98,239,0,
+        107,239,0,
+        114,239,0,
+        121,239,0,
+        128,239,0,
+        133,239,0,
+        140,239,0,
+        149,242,0,
+        154,242,0,
+        161,242,0,
+        170,242,0,
+        177,242,0,
+        184,242,0,
+        191,242,0,
+        198,244,0,
+        205,244,0,
+        212,244,0,
+        219,244,0,
+        228,244,0,
+        233,244,0,
+        239,244,0,
+        246,242,0,
+        246,235,0,
+        246,226,0,
+        246,221,0,
+        246,214,0,
+        246,205,0,
+        246,198,0,
+        246,193,0,
+        246,186,0,
+        246,179,0,
+        249,174,0,
+        249,168,0,
+        249,160,0,
+        249,153,0,
+        249,144,0,
+        249,137,0,
+        249,131,0,
+        251,124,0,
+        251,119,0,
+        251,112,0,
+        251,103,0,
+        251,96,0,
+        251,89,0,
+        251,82,0,
+        253,77,0,
+        253,70,0,
+        253,61,0,
+        253,56,0,
+        253,47,0,
+        253,40,0,
+        255,33,0
+    };
+
+
+    lut->SetNumberOfTableValues(256);
+    for(int i=0; i<256; i++)
+    {
+        int k = i*3;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+    }
+};
+
+//---------------------------------------------------------------------
+void lutPhysicsContour( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //physicsContour
+    unsigned char v[256*3] =
+    {
+        101, 4, 255,
+        97, 4, 253,
+        92, 4, 250,
+        89, 4, 248,
+        83, 4, 245,
+        80, 4, 243,
+        76, 4, 241,
+        73, 4, 238,
+        68, 4, 236,
+        65, 2, 233,
+        62, 2, 231,
+        57, 2, 229,
+        54, 2, 226,
+        50, 2, 224,
+        48, 2, 221,
+        43, 2, 219,
+        41, 2, 217,
+        37, 2, 214,
+        35, 2, 212,
+        31, 1, 209,
+        28, 1, 207,
+        26, 1, 204,
+        22, 1, 202,
+        19, 1, 200,
+        16, 1, 197,
+        13, 1, 195,
+        12, 0, 192,
+        9, 0, 190,
+        6, 0, 188,
+        4, 0, 185,
+        1, 0, 183,
+        0, 1, 180,
+        0, 3, 178,
+        0, 6, 176,
+        0, 8, 173,
+        0, 10, 171,
+        0, 12, 168,
+        0, 13, 166,
+        0, 15, 164,
+        0, 18, 161,
+        1, 19, 159,
+        2, 22, 156,
+        3, 24, 154,
+        3, 25, 152,
+        4, 28, 149,
+        4, 29, 147,
+        5, 31, 144,
+        6, 33, 142,
+        7, 33, 140,
+        6, 35, 137,
+        7, 37, 135,
+        8, 37, 132,
+        8, 39, 130,
+        9, 40, 128,
+        19, 84, 255,
+        20, 85, 253,
+        21, 88, 250,
+        22, 91, 248,
+        22, 94, 245,
+        23, 95, 243,
+        24, 97, 241,
+        25, 100, 238,
+        25, 102, 236,
+        26, 103, 233,
+        27, 105, 231,
+        28, 107, 229,
+        29, 109, 226,
+        29, 111, 224,
+        30, 111, 221,
+        30, 113, 219,
+        29, 116, 217,
+        29, 118, 214,
+        29, 119, 212,
+        28, 121, 209,
+        28, 123, 207,
+        28, 124, 204,
+        27, 125, 202,
+        27, 126, 200,
+        27, 128, 197,
+        26, 129, 195,
+        26, 131, 192,
+        26, 132, 190,
+        25, 133, 188,
+        25, 134, 185,
+        25, 135, 183,
+        24, 136, 180,
+        24, 137, 178,
+        23, 137, 176,
+        23, 138, 173,
+        22, 138, 171,
+        22, 139, 168,
+        21, 140, 166,
+        21, 139, 164,
+        21, 140, 161,
+        20, 140, 159,
+        20, 140, 156,
+        20, 141, 154,
+        19, 140, 152,
+        19, 140, 149,
+        19, 140, 147,
+        18, 140, 144,
+        18, 140, 142,
+        18, 140, 140,
+        17, 137, 135,
+        17, 135, 131,
+        17, 132, 127,
+        16, 130, 123,
+        16, 128, 118,
+        32, 255, 232,
+        32, 253, 226,
+        31, 250, 221,
+        31, 248, 215,
+        31, 245, 209,
+        29, 243, 203,
+        29, 241, 198,
+        29, 238, 193,
+        28, 236, 187,
+        28, 233, 181,
+        28, 231, 176,
+        27, 229, 171,
+        27, 226, 167,
+        27, 224, 158,
+        26, 221, 151,
+        26, 219, 144,
+        26, 217, 138,
+        25, 214, 131,
+        25, 212, 124,
+        25, 209, 117,
+        24, 207, 111,
+        24, 204, 104,
+        23, 202, 98,
+        22, 200, 92,
+        22, 197, 86,
+        22, 195, 79,
+        21, 192, 74,
+        21, 190, 68,
+        21, 188, 63,
+        21, 185, 57,
+        20, 183, 52,
+        20, 180, 47,
+        20, 178, 42,
+        19, 176, 37,
+        19, 173, 32,
+        18, 171, 27,
+        18, 168, 23,
+        17, 166, 18,
+        21, 164, 17,
+        25, 161, 17,
+        28, 159, 17,
+        31, 156, 16,
+        35, 154, 16,
+        38, 152, 16,
+        41, 149, 15,
+        44, 147, 15,
+        47, 144, 15,
+        49, 142, 14,
+        52, 140, 14,
+        54, 137, 13,
+        57, 135, 13,
+        59, 132, 13,
+        61, 130, 13,
+        63, 128, 12,
+        133, 255, 25,
+        138, 253, 25,
+        142, 250, 24,
+        148, 248, 24,
+        152, 245, 24,
+        157, 243, 22,
+        161, 241, 22,
+        162, 238, 22,
+        164, 236, 22,
+        164, 233, 21,
+        165, 231, 21,
+        167, 229, 21,
+        167, 226, 21,
+        168, 224, 21,
+        169, 221, 20,
+        169, 219, 20,
+        170, 217, 20,
+        170, 214, 20,
+        171, 212, 19,
+        172, 209, 19,
+        172, 207, 19,
+        173, 204, 19,
+        173, 202, 18,
+        172, 200, 18,
+        173, 197, 18,
+        174, 195, 18,
+        173, 192, 17,
+        174, 190, 16,
+        173, 188, 16,
+        173, 185, 16,
+        174, 183, 16,
+        174, 180, 16,
+        174, 178, 15,
+        175, 176, 15,
+        173, 172, 15,
+        171, 167, 15,
+        168, 162, 14,
+        166, 159, 14,
+        164, 154, 14,
+        161, 149, 14,
+        159, 145, 14,
+        156, 140, 13,
+        154, 136, 13,
+        152, 132, 13,
+        149, 128, 13,
+        147, 123, 12,
+        144, 119, 12,
+        142, 115, 11,
+        140, 111, 11,
+        137, 107, 11,
+        135, 103, 11,
+        132, 99, 11,
+        130, 96, 10,
+        128, 92, 10,
+        255, 181, 20,
+        255, 178, 20,
+        255, 174, 20,
+        255, 170, 20,
+        255, 167, 20,
+        255, 164, 20,
+        255, 159, 20,
+        255, 155, 20,
+        255, 152, 20,
+        255, 148, 20,
+        255, 145, 19,
+        255, 142, 19,
+        255, 136, 19,
+        255, 133, 19,
+        255, 130, 19,
+        255, 126, 19,
+        255, 123, 19,
+        255, 119, 19,
+        255, 115, 19,
+        255, 111, 19,
+        255, 108, 19,
+        255, 105, 19,
+        255, 100, 19,
+        255, 96, 19,
+        255, 93, 19,
+        255, 89, 19,
+        255, 85, 19,
+        255, 82, 19,
+        255, 79, 18,
+        255, 74, 18,
+        255, 71, 18,
+        255, 67, 18,
+        255, 63, 18,
+        255, 60, 18,
+        255, 56, 18,
+        255, 52, 18,
+        255, 48, 18,
+        255, 45, 18,
+        255, 41, 18,
+        255, 37, 18,
+    };
+
+    lut->SetNumberOfTableValues(256);
+    for(int i=0; i<256; i++)
+    {
+        int k = i*3;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+    }
+};
+
+//---------------------------------------------------------------------
+void lutGlow( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //glow
+    unsigned char v[256*3] =
+    {
+        0, 0, 0,
+        0, 0, 0,
+        5, 0, 0,
+        5, 0, 0,
+        9, 0, 0,
+        9, 0, 0,
+        13, 0, 0,
+        13, 0, 5,
+        17, 0, 5,
+        17, 0, 5,
+        21, 0, 5,
+        21, 0, 5,
+        25, 0, 5,
+        25, 0, 5,
+        29, 0, 5,
+        29, 0, 9,
+        33, 0, 9,
+        33, 0, 9,
+        37, 0, 9,
+        37, 0, 9,
+        41, 0, 9,
+        41, 0, 9,
+        45, 0, 9,
+        45, 0, 9,
+        49, 0, 13,
+        49, 0, 13,
+        53, 0, 13,
+        53, 0, 13,
+        57, 0, 13,
+        57, 0, 13,
+        61, 0, 13,
+        61, 5, 13,
+        65, 5, 13,
+        65, 5, 13,
+        69, 5, 13,
+        69, 5, 13,
+        73, 5, 17,
+        73, 5, 17,
+        77, 5, 17,
+        81, 5, 17,
+        81, 5, 17,
+        85, 5, 17,
+        85, 5, 17,
+        89, 5, 17,
+        89, 9, 17,
+        93, 9, 17,
+        93, 9, 17,
+        97, 9, 17,
+        97, 9, 17,
+        101, 9, 17,
+        101, 9, 17,
+        105, 9, 17,
+        105, 9, 17,
+        109, 13, 17,
+        109, 13, 17,
+        113, 13, 17,
+        113, 13, 17,
+        117, 13, 17,
+        117, 13, 17,
+        121, 13, 17,
+        125, 13, 17,
+        125, 13, 17,
+        128, 17, 17,
+        128, 17, 17,
+        132, 17, 21,
+        132, 17, 21,
+        136, 17, 21,
+        136, 17, 21,
+        140, 17, 21,
+        140, 21, 21,
+        144, 21, 21,
+        144, 21, 21,
+        148, 21, 21,
+        148, 21, 21,
+        152, 25, 21,
+        152, 25, 21,
+        156, 25, 21,
+        156, 25, 21,
+        156, 25, 21,
+        160, 29, 21,
+        160, 29, 21,
+        164, 29, 21,
+        164, 29, 21,
+        168, 33, 21,
+        168, 33, 21,
+        168, 33, 21,
+        172, 33, 21,
+        172, 37, 21,
+        176, 37, 21,
+        176, 37, 21,
+        176, 37, 21,
+        180, 41, 21,
+        180, 41, 21,
+        180, 41, 21,
+        184, 45, 21,
+        184, 45, 21,
+        188, 45, 21,
+        188, 49, 21,
+        188, 49, 21,
+        192, 49, 21,
+        192, 53, 21,
+        192, 53, 21,
+        192, 53, 21,
+        196, 57, 21,
+        196, 57, 21,
+        196, 61, 21,
+        200, 61, 21,
+        200, 61, 21,
+        200, 65, 21,
+        200, 65, 21,
+        204, 69, 21,
+        204, 69, 21,
+        204, 69, 21,
+        208, 73, 21,
+        208, 73, 21,
+        208, 77, 21,
+        208, 77, 21,
+        208, 81, 21,
+        212, 81, 21,
+        212, 85, 21,
+        212, 85, 21,
+        212, 89, 21,
+        212, 89, 21,
+        216, 93, 21,
+        216, 93, 21,
+        216, 97, 21,
+        216, 97, 21,
+        216, 101, 21,
+        220, 101, 21,
+        220, 105, 21,
+        220, 105, 21,
+        220, 109, 21,
+        220, 109, 21,
+        220, 113, 21,
+        220, 113, 21,
+        224, 117, 21,
+        224, 117, 25,
+        224, 121, 25,
+        224, 121, 25,
+        224, 125, 25,
+        224, 125, 25,
+        224, 128, 25,
+        228, 128, 25,
+        228, 132, 25,
+        228, 132, 29,
+        228, 136, 29,
+        228, 136, 29,
+        228, 140, 29,
+        228, 140, 29,
+        228, 144, 33,
+        232, 144, 33,
+        232, 144, 33,
+        232, 148, 33,
+        232, 148, 33,
+        232, 152, 37,
+        232, 152, 37,
+        232, 156, 37,
+        232, 156, 37,
+        236, 160, 41,
+        236, 160, 41,
+        236, 160, 41,
+        236, 164, 41,
+        236, 164, 45,
+        236, 168, 45,
+        236, 168, 45,
+        236, 172, 49,
+        236, 172, 49,
+        236, 172, 49,
+        240, 176, 53,
+        240, 176, 53,
+        240, 180, 53,
+        240, 180, 57,
+        240, 180, 57,
+        240, 184, 61,
+        240, 184, 61,
+        240, 188, 61,
+        240, 188, 65,
+        240, 188, 65,
+        240, 192, 69,
+        244, 192, 69,
+        244, 192, 73,
+        244, 192, 73,
+        244, 192, 77,
+        244, 200, 77,
+        244, 200, 81,
+        244, 200, 81,
+        244, 204, 85,
+        244, 204, 85,
+        244, 204, 89,
+        244, 208, 89,
+        244, 208, 93,
+        244, 208, 93,
+        248, 212, 97,
+        248, 212, 97,
+        248, 212, 101,
+        248, 216, 105,
+        248, 216, 105,
+        248, 216, 109,
+        248, 220, 109,
+        248, 220, 113,
+        248, 220, 113,
+        248, 224, 117,
+        248, 224, 121,
+        248, 224, 121,
+        248, 224, 125,
+        248, 228, 125,
+        248, 228, 129,
+        248, 228, 129,
+        248, 228, 133,
+        248, 232, 137,
+        248, 232, 137,
+        252, 232, 141,
+        252, 232, 145,
+        252, 236, 145,
+        252, 236, 148,
+        252, 236, 148,
+        252, 236, 152,
+        252, 240, 156,
+        252, 240, 156,
+        252, 240, 160,
+        252, 240, 160,
+        252, 240, 164,
+        252, 240, 168,
+        252, 244, 168,
+        252, 244, 172,
+        252, 244, 172,
+        252, 244, 176,
+        252, 244, 180,
+        252, 244, 180,
+        252, 248, 184,
+        252, 248, 188,
+        252, 248, 188,
+        252, 248, 192,
+        252, 248, 192,
+        252, 248, 196,
+        252, 248, 200,
+        252, 248, 200,
+        252, 252, 204,
+        252, 252, 208,
+        252, 252, 208,
+        252, 252, 212,
+        252, 252, 212,
+        252, 252, 216,
+        252, 252, 220,
+        252, 252, 220,
+        252, 252, 224,
+        252, 252, 228,
+        252, 252, 228,
+        252, 252, 232,
+        252, 252, 236,
+        252, 252, 236,
+        252, 252, 240,
+        252, 252, 244,
+        252, 252, 244,
+        252, 252, 248,
+        252, 252, 255,
+    };
+
+    lut->SetNumberOfTableValues(256);
+    for(int i=0; i<256; i++)
+    {
+        int k = i*3;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+    }
+};
+
+
+//---------------------------------------------------------------------
+void lutEField( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //efield
+    unsigned char v[256*3] =
+    {
+        237,253,135,
+        233,251,137,
+        232,251,137,
+        230,249,140,
+        226,249,140,
+        225,247,142,
+        223,246,144,
+        221,246,145,
+        218,244,147,
+        216,244,149,
+        214,242,149,
+        211,242,151,
+        209,240,153,
+        207,239,154,
+        205,239,156,
+        202,239,158,
+        200,237,158,
+        198,237,161,
+        195,235,161,
+        193,235,163,
+        191,233,165,
+        188,232,167,
+        186,232,168,
+        184,230,170,
+        181,230,170,
+        179,228,172,
+        177,228,174,
+        175,226,175,
+        172,226,177,
+        170,226,179,
+        168,225,179,
+        165,223,181,
+        163,223,182,
+        161,221,184,
+        158,221,186,
+        156,219,188,
+        154,219,188,
+        151,218,191,
+        149,216,191,
+        147,216,193,
+        144,214,195,
+        142,214,195,
+        140,214,198,
+        137,212,198,
+        135,212,200,
+        133,211,202,
+        130,209,204,
+        128,209,205,
+        126,207,207,
+        124,207,207,
+        121,205,209,
+        119,205,211,
+        117,204,212,
+        114,202,214,
+        112,202,216,
+        110,202,216,
+        107,200,219,
+        105,200,219,
+        103,198,221,
+        100,198,223,
+        98,196,225,
+        96,195,226,
+        93,195,228,
+        91,193,228,
+        89,193,230,
+        86,191,232,
+        84,191,233,
+        82,189,235,
+        80,189,237,
+        77,188,237,
+        75,188,239,
+        73,186,240,
+        70,186,242,
+        68,184,244,
+        66,184,246,
+        65,182,246,
+        61,181,249,
+        59,181,249,
+        58,179,251,
+        54,179,253,
+        52,177,255,
+        54,175,255,
+        54,174,255,
+        54,172,255,
+        56,170,255,
+        56,168,255,
+        58,167,255,
+        58,165,255,
+        59,163,255,
+        59,161,255,
+        61,158,255,
+        61,156,255,
+        61,154,255,
+        63,154,255,
+        63,151,255,
+        63,149,255,
+        65,147,255,
+        66,145,255,
+        66,144,255,
+        66,142,255,
+        68,140,255,
+        68,140,255,
+        70,137,255,
+        70,135,255,
+        70,135,255,
+        72,133,255,
+        73,131,255,
+        73,130,255,
+        73,128,255,
+        75,126,255,
+        75,124,255,
+        75,124,255,
+        77,121,255,
+        77,121,255,
+        79,119,255,
+        79,117,255,
+        79,117,255,
+        80,114,255,
+        82,114,255,
+        82,112,255,
+        82,110,255,
+        84,110,255,
+        84,107,255,
+        84,107,255,
+        86,105,255,
+        86,105,255,
+        87,103,255,
+        87,102,255,
+        89,100,255,
+        89,100,255,
+        91,98,255,
+        91,96,255,
+        89,94,255,
+        89,91,255,
+        87,89,255,
+        89,86,255,
+        89,86,255,
+        89,84,255,
+        91,84,255,
+        91,82,255,
+        93,82,255,
+        93,80,255,
+        94,79,255,
+        96,79,255,
+        98,77,255,
+        98,77,255,
+        100,75,255,
+        100,75,255,
+        102,73,255,
+        103,73,255,
+        105,72,255,
+        105,70,255,
+        107,70,255,
+        107,68,255,
+        110,68,255,
+        110,66,255,
+        112,65,255,
+        114,63,255,
+        114,63,255,
+        117,61,255,
+        117,61,255,
+        119,59,255,
+        121,59,255,
+        123,58,255,
+        124,56,255,
+        126,56,255,
+        128,54,255,
+        128,54,255,
+        130,52,255,
+        131,52,253,
+        133,52,253,
+        135,51,253,
+        137,49,253,
+        137,49,253,
+        138,49,253,
+        140,47,253,
+        142,47,253,
+        144,47,251,
+        144,45,251,
+        147,45,251,
+        147,45,251,
+        149,45,251,
+        151,43,251,
+        153,42,251,
+        154,42,251,
+        156,42,251,
+        156,40,249,
+        158,40,249,
+        160,40,249,
+        161,38,249,
+        163,38,249,
+        165,38,249,
+        167,36,249,
+        168,36,249,
+        170,35,247,
+        170,35,247,
+        172,35,247,
+        175,33,247,
+        175,33,246,
+        177,33,246,
+        179,31,246,
+        181,31,246,
+        182,31,246,
+        184,29,246,
+        186,29,246,
+        188,28,246,
+        189,28,246,
+        191,28,244,
+        193,26,244,
+        195,26,244,
+        195,26,244,
+        198,26,244,
+        200,24,244,
+        202,24,244,
+        202,24,244,
+        205,22,242,
+        207,22,242,
+        209,22,242,
+        209,21,242,
+        212,21,242,
+        214,19,242,
+        216,19,242,
+        218,19,242,
+        219,17,242,
+        221,17,240,
+        223,17,240,
+        225,15,240,
+        226,15,240,
+        228,15,239,
+        230,15,239,
+        232,14,239,
+        233,12,239,
+        235,12,239,
+        237,12,239,
+        239,12,237,
+        239,10,235,
+        239,10,232,
+        237,10,230,
+        237,8,228,
+        237,8,226,
+        237,8,223,
+        237,8,221,
+        237,7,219,
+        237,5,216,
+        237,5,214,
+        235,5,212,
+        235,5,209,
+        235,3,207,
+        235,3,204,
+        235,3,202,
+        235,1,200,
+        235,1,196,
+        235,1,195,
+        235,1,191,
+        233,0,189,
+        233,0,186,
+    };
+
+    lut->SetNumberOfTableValues(256);
+    for(int i=0; i<256; i++)
+    {
+        int k = i*3;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+    }
+};
+
+
+//---------------------------------------------------------------------
+void lutDefault( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    Color *c = new Color();
+
+    lut->SetNumberOfTableValues(256);
+
+    for(int i=0; i<256; i++)
+    {
+        int h = (i*240.0)/255.0;
+        c->setHSV(h,255,255);
+        lut->SetTableValue(i, c->m_red/255.0, c->m_green/255.0, c->m_blue/255.0, 1);
+    }
+};
+//---------------------------------------------------------------------
+void lutMinMax( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    Color *c = new Color();
+    int i;
+    lut->SetNumberOfTableValues(256);
+
+    for(i=0; i<128; i++)
+    {
+        int h = (i*240.0)/255.0;
+        c->setHSV(h,255,255);
+        lut->SetTableValue(i, c->m_red/255.0, c->m_green/255.0, c->m_blue/255.0, (128-i)/128.0);
+    }
+    for(i=128; i<256; i++)
+    {
+        int h = (i*240.0)/255.0;
+        c->setHSV(h,255,255);
+        lut->SetTableValue(i, c->m_red/255.0, c->m_green/255.0, c->m_blue/255.0, (i-128)/128.0);
+    }
+};
+//---------------------------------------------------------------------
+void lutDefaultStep( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lutDefault( lut);
+    Color *c = new Color(128,128,128);
+
+    for(int i=0; i<256; i+=16)
+        lut->SetTableValue(i, c->m_red/255.0, c->m_green/255.0, c->m_blue/255.0, 1);
+};
+//---------------------------------------------------------------------
+void lutGray( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(128);
+
+    for(int i=0; i<128; i++)
+        lut->SetTableValue(i, i/128.0, i/128.0, i/128.0, 1);
+};
+//---------------------------------------------------------------------
+void lutPureRed( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(128);
+
+    for(int i=0; i<128; i++)
+    {
+        lut->SetTableValue(i, i/128.0, 0, 0, 1);
+    }
+};
+//---------------------------------------------------------------------
+void lutPureGreen( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(128);
+
+    for(int i=0; i<128; i++)
+        lut->SetTableValue(i, 0, i/128.0, 0, 1);
+};
+//---------------------------------------------------------------------
+void lutPureBlue( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(128);
+
+    for(int i=0; i<128; i++)
+        lut->SetTableValue(i, 0, 0, i/128.0, 1);
+};
+
+//---------------------------------------------------------------------
+void lutAllYellow( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+
+    for(int i=0; i<1; i++)
+        lut->SetTableValue(i, 1, 1, 0, 1);
+};
+//---------------------------------------------------------------------
+void lutAllCyane( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+
+    for(int i=0; i<1; i++)
+        lut->SetTableValue(i, 0, 1, 1, 1);
+};
+//---------------------------------------------------------------------
+void lutAllViolet( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+
+    for(int i=0; i<1; i++)
+        lut->SetTableValue(i, 1, 0, 1, 1);
+};
+//---------------------------------------------------------------------
+void lutAllWhite( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+
+    for(int i=0; i<1; i++)
+        lut->SetTableValue(i, 1, 1, 1, 1);
+};
+//---------------------------------------------------------------------
+void lutAllBlack( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+
+    for(int i=0; i<1; i++)
+        lut->SetTableValue(i, 0, 0, 0, 1);
+};
+//---------------------------------------------------------------------
+void lutAllRed( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+
+    lut->SetNumberOfTableValues(1);
+
+    for(int i=0; i<1; i++)
+        lut->SetTableValue(i, 1, 0, 0, 1);
+};
+//---------------------------------------------------------------------
+void lutAllGreen( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+
+    for(int i=0; i<1; i++)
+        lut->SetTableValue(i, 0, 1, 0, 1);
+};
+//---------------------------------------------------------------------
+void lutAllBlu( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+
+    for(int i=0; i<1; i++)
+        lut->SetTableValue(i, 0, 0, 1, 1);
+};
+
+//---------------------------------------------------------------------
+void lutRun1( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //Run1
+    unsigned char v[256*4] =
+    {
+        0,255,0,0,
+        64,198,255,0,
+        128,3,255,233,
+        192,78,47,255,
+        1,255,4,0,
+        65,193,255,0,
+        129,4,255,238,
+        193,80,45,255,
+        2,255,9,0,
+        66,189,255,0,
+        130,5,255,243,
+        194,82,43,255,
+        3,255,14,0,
+        67,182,255,0,
+        131,6,255,248,
+        195,85,41,255,
+        4,255,19,0,
+        68,175,255,0,
+        132,7,255,253,
+        196,87,39,255,
+        5,255,24,0,
+        69,168,255,0,
+        133,8,252,255,
+        197,90,37,255,
+        6,255,29,0,
+        70,161,255,0,
+        134,9,247,255,
+        198,93,35,255,
+        7,255,34,0,
+        71,154,255,0,
+        135,10,242,255,
+        199,96,33,255,
+        8,255,38,0,
+        72,147,255,0,
+        136,10,238,255,
+        200,98,31,255,
+        9,255,43,0,
+        73,140,255,0,
+        137,11,233,255,
+        201,101,29,255,
+        10,255,48,0,
+        74,133,255,0,
+        138,12,228,255,
+        202,104,27,255,
+        11,255,53,0,
+        75,126,255,0,
+        139,13,224,255,
+        203,108,25,255,
+        12,255,58,0,
+        76,119,255,0,
+        140,14,219,255,
+        204,111,23,255,
+        13,255,63,0,
+        77,112,255,0,
+        141,15,215,255,
+        205,114,21,255,
+        14,255,68,0,
+        78,105,255,0,
+        142,16,210,255,
+        206,117,19,255,
+        15,255,72,0,
+        79,98,255,0,
+        143,17,206,255,
+        207,121,17,255,
+        16,255,77,0,
+        80,91,255,0,
+        144,18,202,255,
+        208,124,15,255,
+        17,255,82,0,
+        81,84,255,0,
+        145,19,197,255,
+        209,128,13,255,
+        18,255,87,0,
+        82,77,255,0,
+        146,20,193,255,
+        210,131,11,255,
+        19,255,92,0,
+        83,70,255,0,
+        147,20,189,255,
+        211,135,9,255,
+        20,255,97,0,
+        84,64,255,0,
+        148,21,185,255,
+        212,139,7,255,
+        21,255,102,0,
+        85,57,255,0,
+        149,22,180,255,
+        213,143,5,255,
+        22,255,106,0,
+        86,50,255,0,
+        150,23,176,255,
+        214,147,3,255,
+        23,255,111,0,
+        87,43,255,0,
+        151,24,172,255,
+        215,151,1,255,
+        24,255,116,0,
+        88,36,255,0,
+        152,25,168,255,
+        216,155,0,255,
+        25,255,121,0,
+        89,29,255,0,
+        153,26,164,255,
+        217,159,0,255,
+        26,255,126,0,
+        90,22,255,0,
+        154,27,160,255,
+        218,163,0,255,
+        27,255,131,0,
+        91,15,255,0,
+        155,28,156,255,
+        219,167,0,255,
+        28,255,136,0,
+        92,8,255,0,
+        156,29,152,255,
+        220,172,0,255,
+        29,255,141,0,
+        93,1,255,0,
+        157,30,148,255,
+        221,176,0,255,
+        30,255,145,0,
+        94,0,255,5,
+        158,30,144,255,
+        222,180,0,255,
+        31,255,150,0,
+        95,0,255,12,
+        159,31,140,255,
+        223,184,0,255,
+        32,255,155,0,
+        96,0,255,19,
+        160,32,136,255,
+        224,188,0,255,
+        33,255,160,0,
+        97,0,255,26,
+        161,33,133,255,
+        225,192,0,255,
+        34,255,165,0,
+        98,0,255,33,
+        162,34,129,255,
+        226,197,0,255,
+        35,255,170,0,
+        99,0,255,40,
+        163,35,125,255,
+        227,201,0,255,
+        36,255,175,0,
+        100,0,255,47,
+        164,36,122,255,
+        228,205,0,255,
+        37,255,179,0,
+        101,0,255,54,
+        165,37,118,255,
+        229,209,0,255,
+        38,255,184,0,
+        102,0,255,60,
+        166,38,114,255,
+        230,213,0,255,
+        39,255,189,0,
+        103,0,255,67,
+        167,39,111,255,
+        231,217,0,255,
+        40,255,194,0,
+        104,0,255,74,
+        168,40,107,255,
+        232,222,0,255,
+        41,255,199,0,
+        105,0,255,81,
+        169,40,104,255,
+        233,226,0,255,
+        42,255,204,0,
+        106,0,255,88,
+        170,41,100,255,
+        234,230,0,255,
+        43,255,209,0,
+        107,0,255,95,
+        171,42,97,255,
+        235,234,0,255,
+        44,255,213,0,
+        108,0,255,102,
+        172,43,93,255,
+        236,238,0,255,
+        45,255,218,0,
+        109,0,255,109,
+        173,44,90,255,
+        237,242,0,255,
+        46,255,223,0,
+        110,0,255,116,
+        174,45,87,255,
+        238,247,0,255,
+        47,255,228,0,
+        111,0,255,123,
+        175,46,84,255,
+        239,251,0,255,
+        48,255,233,0,
+        112,0,255,130,
+        176,47,80,255,
+        240,255,0,254,
+        49,255,238,0,
+        113,0,255,137,
+        177,48,77,255,
+        241,255,0,250,
+        50,255,243,0,
+        114,0,255,144,
+        178,49,74,255,
+        242,255,0,246,
+        51,255,248,0,
+        115,0,255,151,
+        179,50,71,255,
+        243,255,0,242,
+        52,255,252,0,
+        116,0,255,158,
+        180,50,68,255,
+        244,255,0,237,
+        53,252,255,0,
+        117,0,255,165,
+        181,53,66,255,
+        245,255,0,233,
+        54,247,255,0,
+        118,0,255,172,
+        182,55,64,255,
+        246,255,0,229,
+        55,242,255,0,
+        119,0,255,178,
+        183,57,62,255,
+        247,255,0,225,
+        56,237,255,0,
+        120,0,255,185,
+        184,59,60,255,
+        248,255,0,221,
+        57,232,255,0,
+        121,0,255,192,
+        185,63,61,255,
+        249,255,0,217,
+        58,227,255,0,
+        122,0,255,199,
+        186,65,59,255,
+        250,255,0,212,
+        59,223,255,0,
+        123,0,255,206,
+        187,67,57,255,
+        251,255,0,208,
+        60,218,255,0,
+        124,0,255,213,
+        188,69,55,255,
+        252,255,0,204,
+        61,213,255,0,
+        125,0,255,218,
+        189,71,53,255,
+        253,255,0,200,
+        62,208,255,0,
+        126,1,255,223,
+        190,73,51,255,
+        254,255,0,196,
+        63,203,255,0,
+        127,2,255,228,
+        191,75,49,255,
+        255,255,0,191
+    };
+
+    lut->SetNumberOfTableValues(256);
+    for(int i=0; i<256; i++)
+    {
+        int k = i*4;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+    }
+};
+
+//---------------------------------------------------------------------
+void lutRun2( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //Run2
+    unsigned char v[256*4] =
+    {
+        0,255,89,89,
+        64,255,151,151,
+        128,255,255,255,
+        192,130,142,255,
+        1,255,90,90,
+        65,255,153,153,
+        129,253,253,255,
+        193,128,141,255,
+        2,255,91,91,
+        66,255,154,154,
+        130,251,251,255,
+        194,127,140,255,
+        3,255,92,92,
+        67,255,156,156,
+        131,249,249,255,
+        195,126,139,255,
+        4,255,93,93,
+        68,255,157,157,
+        132,247,247,255,
+        196,125,138,255,
+        5,255,94,94,
+        69,255,159,159,
+        133,245,246,255,
+        197,124,137,255,
+        6,255,95,95,
+        70,255,161,161,
+        134,243,244,255,
+        198,123,136,255,
+        7,255,96,96,
+        71,255,162,162,
+        135,241,242,255,
+        199,121,135,255,
+        8,255,97,97,
+        72,255,164,164,
+        136,239,240,255,
+        200,120,134,255,
+        9,255,97,97,
+        73,255,165,165,
+        137,237,239,255,
+        201,119,133,255,
+        10,255,98,98,
+        74,255,167,167,
+        138,235,237,255,
+        202,118,132,255,
+        11,255,99,99,
+        75,255,169,169,
+        139,233,235,255,
+        203,117,130,255,
+        12,255,100,100,
+        76,255,170,170,
+        140,231,233,255,
+        204,116,129,255,
+        13,255,101,101,
+        77,255,172,172,
+        141,229,232,255,
+        205,114,128,255,
+        14,255,102,102,
+        78,255,174,174,
+        142,227,230,255,
+        206,113,127,255,
+        15,255,103,103,
+        79,255,175,175,
+        143,225,228,255,
+        207,112,126,255,
+        16,255,104,104,
+        80,255,177,177,
+        144,223,226,255,
+        208,111,125,255,
+        17,255,105,105,
+        81,255,178,178,
+        145,221,224,255,
+        209,110,124,255,
+        18,255,106,106,
+        82,255,180,180,
+        146,219,223,255,
+        210,108,123,255,
+        19,255,107,107,
+        83,255,182,182,
+        147,217,221,255,
+        211,107,122,255,
+        20,255,108,108,
+        84,255,183,183,
+        148,215,219,255,
+        212,106,121,255,
+        21,255,109,109,
+        85,255,185,185,
+        149,213,217,255,
+        213,105,120,255,
+        22,255,110,110,
+        86,255,187,187,
+        150,211,216,255,
+        214,104,119,255,
+        23,255,111,111,
+        87,255,188,188,
+        151,209,214,255,
+        215,103,118,255,
+        24,255,112,112,
+        88,255,190,190,
+        152,207,212,255,
+        216,101,117,255,
+        25,255,113,113,
+        89,255,191,191,
+        153,205,210,255,
+        217,100,116,255,
+        26,255,114,114,
+        90,255,193,193,
+        154,203,209,255,
+        218,99,115,255,
+        27,255,115,115,
+        91,255,195,195,
+        155,201,207,255,
+        219,98,114,255,
+        28,255,116,116,
+        92,255,196,196,
+        156,200,205,255,
+        220,97,112,255,
+        29,255,117,117,
+        93,255,198,198,
+        157,198,203,255,
+        221,96,111,255,
+        30,255,118,118,
+        94,255,199,199,
+        158,196,201,255,
+        222,94,110,255,
+        31,255,119,119,
+        95,255,201,201,
+        159,194,200,255,
+        223,93,109,255,
+        32,255,120,120,
+        96,255,203,203,
+        160,192,198,255,
+        224,92,108,255,
+        33,255,121,121,
+        97,255,204,204,
+        161,190,196,255,
+        225,91,107,255,
+        34,255,122,122,
+        98,255,206,206,
+        162,188,194,255,
+        226,90,106,255,
+        35,255,123,123,
+        99,255,208,208,
+        163,186,193,255,
+        227,88,105,255,
+        36,255,124,124,
+        100,255,209,209,
+        164,184,191,255,
+        228,87,104,255,
+        37,255,125,125,
+        101,255,211,211,
+        165,182,189,255,
+        229,86,103,255,
+        38,255,126,126,
+        102,255,212,212,
+        166,180,187,255,
+        230,85,102,255,
+        39,255,127,127,
+        103,255,214,214,
+        167,178,186,255,
+        231,84,101,255,
+        40,255,128,128,
+        104,255,216,216,
+        168,176,184,255,
+        232,83,100,255,
+        41,255,129,129,
+        105,255,217,217,
+        169,174,182,255,
+        233,81,99,255,
+        42,255,130,130,
+        106,255,219,219,
+        170,172,180,255,
+        234,80,98,255,
+        43,255,131,131,
+        107,255,221,221,
+        171,170,178,255,
+        235,79,97,255,
+        44,255,131,131,
+        108,255,222,222,
+        172,168,177,255,
+        236,78,96,255,
+        45,255,132,132,
+        109,255,224,224,
+        173,166,175,255,
+        237,77,95,255,
+        46,255,133,133,
+        110,255,225,225,
+        174,164,173,255,
+        238,76,93,255,
+        47,255,134,134,
+        111,255,227,227,
+        175,162,171,255,
+        239,74,92,255,
+        48,255,135,135,
+        112,255,229,229,
+        176,160,170,255,
+        240,73,91,255,
+        49,255,136,136,
+        113,255,230,230,
+        177,158,168,255,
+        241,72,90,255,
+        50,255,137,137,
+        114,255,232,232,
+        178,156,166,255,
+        242,71,89,255,
+        51,255,138,138,
+        115,255,233,233,
+        179,154,164,255,
+        243,70,88,255,
+        52,255,139,139,
+        116,255,235,235,
+        180,152,163,255,
+        244,69,87,255,
+        53,255,140,140,
+        117,255,237,237,
+        181,150,161,255,
+        245,67,86,255,
+        54,255,141,141,
+        118,255,238,238,
+        182,148,159,255,
+        246,66,85,255,
+        55,255,142,142,
+        119,255,240,240,
+        183,146,157,255,
+        247,65,84,255,
+        56,255,143,143,
+        120,255,242,242,
+        184,145,156,255,
+        248,64,83,255,
+        57,255,144,144,
+        121,255,243,243,
+        185,143,154,255,
+        249,63,82,255,
+        58,255,145,145,
+        122,255,245,245,
+        186,141,152,255,
+        250,61,81,255,
+        59,255,146,146,
+        123,255,246,246,
+        187,139,150,255,
+        251,60,80,255,
+        60,255,147,147,
+        124,255,248,248,
+        188,137,148,255,
+        252,59,79,255,
+        61,255,148,148,
+        125,255,250,250,
+        189,135,147,255,
+        253,58,78,255,
+        62,255,149,149,
+        126,255,251,251,
+        190,133,145,255,
+        254,57,77,255,
+        63,255,150,150,
+        127,255,253,253,
+        191,131,143,255,
+        255,56,75,255
+    };
+
+    lut->SetNumberOfTableValues(256);
+    for(int i=0; i<256; i++)
+    {
+        int k = i*4;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+    }
+};
+
+//---------------------------------------------------------------------
+void lutVolRenRGB( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //Run2
+    unsigned char v[4*4] =
+    {
+        128,128,128,0,
+        255,0,0,50,
+        0,255,0,100,
+        0,0,255,150,
+    };
+
+    lut->SetNumberOfTableValues(4);
+    for(int i=0; i<4; i++)
+    {
+        int k = i*4;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+    }
+};
+
+//---------------------------------------------------------------------
+void lutVolRenTwoLev( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    //Run2
+    unsigned char v[8*4] =
+    {
+        128,128,128,0,
+        128,128,128,0,
+
+        255,255,0,50,
+
+        128,128,128,0,
+        128,128,128,0,
+
+        255,50,0,100,
+
+        128,128,128,0,
+        128,128,128,0,
+    };
+
+    lut->SetNumberOfTableValues(8);
+    for(int i=0; i<8; i++)
+    {
+        int k = i*4;
+        lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+    }
+}
+
+
diff --git a/Code/src/luteditor.h b/Code/src/luteditor.h
new file mode 100644
index 0000000000000000000000000000000000000000..83175a5d70641cee62017fe541f0e68a0c9ad1ac
--- /dev/null
+++ b/Code/src/luteditor.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ *   Copyright (C) 2008 by Gabriella Caniglia *
+ *  gabriella.caniglia@oact.inaf.it *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef LUTEDITOR_H
+#define LUTEDITOR_H
+
+
+class vtkLookupTable;
+class QString;
+class Color;
+
+void SelectLookTable(  QString palette,vtkLookupTable *lut=NULL);
+
+void lutVolRenGreen( vtkLookupTable *lut);
+void lutVolRenGlow( vtkLookupTable *lut);
+void lutTenStep( vtkLookupTable *lut);
+void lutTemperature( vtkLookupTable *lut);
+void lutSar( vtkLookupTable *lut);
+void lutPhysicsContour( vtkLookupTable *lut);
+void lutGlow( vtkLookupTable *lut);
+void lutEField( vtkLookupTable *lut);
+void lutDefault( vtkLookupTable *lut);
+void lutDefaultStep( vtkLookupTable *lut);
+void lutGray( vtkLookupTable *lut);
+void lutRun1( vtkLookupTable *lut);
+void lutRun2( vtkLookupTable *lut);
+void lutPureRed( vtkLookupTable *lut);
+void lutPureGreen( vtkLookupTable *lut);
+void lutPureBlue( vtkLookupTable *lut);
+
+void lutAllYellow( vtkLookupTable *lut);
+void lutAllCyane( vtkLookupTable *lut);
+void lutAllViolet( vtkLookupTable *lut);
+void lutAllWhite( vtkLookupTable *lut);
+void lutAllBlack( vtkLookupTable *lut);
+void lutAllRed( vtkLookupTable *lut);
+void lutAllGreen( vtkLookupTable *lut);
+void lutAllBlu( vtkLookupTable *lut);
+
+void lutMinMax( vtkLookupTable *lut);
+void lutVolRenRGB( vtkLookupTable *lut);
+void lutVolRenTwoLev( vtkLookupTable *lut);
+
+#endif
+
+
diff --git a/Code/src/lutselector.cpp b/Code/src/lutselector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..10ec9fd001b6ae3707199a04a78269c1ffd0c47c
--- /dev/null
+++ b/Code/src/lutselector.cpp
@@ -0,0 +1,2806 @@
+#include "lutselector.h"
+#include <cstdlib>
+#include <cstring>
+#include "color.h"
+#include "vtkLookupTable.h"
+
+
+LutSelector::LutSelector()
+{
+}
+
+
+//---------------------------------------------------------------------
+void LutSelector::SelectLookTable(  int idx,vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  switch(idx)
+  {
+    case 0:  lutDefault(lut);        break;
+    case 1:  lutDefaultStep(lut);    break;
+    case 2:  lutEField(lut);         break;
+    case 3:  lutGlow(lut);           break;
+    case 4:  lutGray(lut);           break;
+    case 5:  lutMinMax(lut);         break;
+    case 6:  lutPhysicsContour(lut); break;
+    case 7:  lutPureRed(lut);        break;
+    case 8:  lutPureGreen(lut);      break;
+    case 9:  lutPureBlue(lut);       break;
+    case 10:  lutRun1(lut);          break;
+    case 11: lutRun2(lut);           break;
+    case 12: lutSar(lut);            break;
+    case 13: lutTemperature(lut);    break;
+    case 14: lutTenStep(lut);        break;
+    case 15: lutVolRenGlow(lut);     break;
+    case 16: lutVolRenGreen(lut);    break;
+    case 17: lutVolRenRGB(lut);      break;
+    case 18: lutVolRenTwoLev(lut);   break;
+    case 19: lutAllYellow(lut);      break;
+    case 20: lutAllCyane(lut);      break;
+    case 21: lutAllViolet(lut);      break;
+    case 22: lutAllWhite(lut);      break;
+    case 23: lutAllBlack(lut);      break;
+    case 24: lutAllRed(lut);      break;
+    case 25: lutAllGreen(lut);      break;
+    case 26: lutAllBlu(lut);      break;
+
+    case -1: lutFile(lut);	         break;
+    default: lutDefault(lut);        break;
+  }
+}
+//---------------------------------------------------------------------
+void LutSelector::lutVolRenGreen( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //VolRenGreen
+  unsigned char v[256*4] =
+  {
+    0, 0, 255, 0,
+    0, 1, 253, 0,
+    0, 3, 252, 1,
+    0, 5, 251, 1,
+    0, 7, 249, 2,
+    0, 9, 248, 3,
+    0, 11, 247, 3,
+    0, 13, 245, 4,
+    0, 14, 244, 5,
+    0, 16, 243, 5,
+    0, 18, 241, 6,
+    0, 20, 240, 6,
+    0, 22, 239, 7,
+    0, 24, 237, 8,
+    0, 26, 236, 8,
+    0, 28, 235, 9,
+    0, 29, 233, 10,
+    0, 31, 232, 10,
+    0, 33, 231, 11,
+    0, 35, 229, 11,
+    0, 37, 228, 12,
+    0, 39, 227, 13,
+    0, 41, 225, 13,
+    0, 43, 224, 14,
+    0, 44, 223, 15,
+    0, 46, 221, 15,
+    0, 48, 220, 16,
+    0, 50, 219, 16,
+    0, 52, 217, 17,
+    0, 54, 216, 18,
+    0, 56, 215, 18,
+    0, 58, 213, 19,
+    0, 59, 212, 20,
+    0, 61, 211, 20,
+    0, 63, 209, 21,
+    0, 65, 208, 21,
+    0, 67, 207, 22,
+    0, 69, 206, 23,
+    0, 71, 204, 23,
+    0, 73, 203, 24,
+    0, 75, 202, 25,
+    0, 76, 200, 25,
+    0, 78, 199, 26,
+    0, 80, 198, 26,
+    0, 82, 196, 27,
+    0, 84, 195, 28,
+    0, 86, 194, 28,
+    0, 88, 192, 29,
+    0, 89, 191, 30,
+    0, 91, 190, 30,
+    0, 93, 188, 31,
+    0, 95, 187, 31,
+    0, 97, 186, 32,
+    0, 99, 184, 33,
+    0, 101, 183, 33,
+    0, 103, 182, 34,
+    0, 105, 180, 35,
+    0, 106, 179, 35,
+    0, 108, 178, 36,
+    0, 110, 176, 36,
+    0, 112, 175, 37,
+    0, 114, 174, 38,
+    0, 116, 172, 38,
+    0, 118, 171, 39,
+    0, 119, 170, 40,
+    0, 121, 168, 40,
+    0, 123, 167, 41,
+    0, 125, 166, 41,
+    0, 127, 164, 42,
+    0, 129, 163, 43,
+    0, 131, 162, 43,
+    0, 133, 160, 44,
+    0, 135, 159, 45,
+    0, 136, 158, 45,
+    0, 138, 157, 46,
+    0, 140, 155, 46,
+    0, 142, 154, 47,
+    0, 144, 153, 48,
+    0, 146, 151, 48,
+    0, 148, 150, 49,
+    0, 149, 149, 50,
+    0, 151, 147, 50,
+    0, 153, 146, 51,
+    0, 155, 145, 51,
+    0, 157, 143, 52,
+    0, 159, 142, 53,
+    0, 161, 141, 53,
+    0, 163, 139, 54,
+    0, 165, 138, 55,
+    0, 166, 137, 55,
+    0, 168, 135, 56,
+    0, 170, 134, 56,
+    0, 172, 133, 57,
+    0, 174, 131, 58,
+    0, 176, 130, 58,
+    0, 178, 129, 59,
+    0, 179, 127, 60,
+    0, 181, 126, 60,
+    0, 183, 125, 61,
+    0, 185, 123, 61,
+    0, 187, 122, 62,
+    0, 189, 121, 63,
+    0, 191, 119, 63,
+    0, 193, 118, 64,
+    0, 195, 117, 65,
+    0, 196, 115, 65,
+    0, 198, 114, 66,
+    0, 200, 113, 66,
+    0, 202, 111, 67,
+    0, 204, 110, 68,
+    0, 206, 109, 68,
+    0, 208, 108, 69,
+    0, 210, 106, 70,
+    0, 211, 105, 70,
+    0, 213, 104, 71,
+    0, 215, 102, 71,
+    0, 217, 101, 72,
+    0, 219, 100, 73,
+    0, 221, 98, 73,
+    0, 223, 97, 74,
+    0, 225, 96, 75,
+    0, 226, 94, 75,
+    0, 228, 93, 76,
+    0, 230, 92, 76,
+    0, 232, 90, 77,
+    0, 234, 89, 78,
+    0, 236, 88, 78,
+    0, 238, 86, 79,
+    0, 239, 85, 80,
+    0, 241, 84, 80,
+    0, 243, 82, 81,
+    0, 245, 81, 81,
+    0, 247, 80, 82,
+    0, 249, 78, 83,
+    0, 251, 77, 83,
+    0, 253, 76, 84,
+    0, 255, 74, 85,
+    1, 254, 75, 85,
+    3, 255, 76, 86,
+    5, 254, 76, 86,
+    7, 255, 77, 87,
+    9, 255, 78, 88,
+    11, 255, 78, 88,
+    13, 255, 79, 89,
+    15, 255, 80, 90,
+    17, 255, 80, 90,
+    19, 255, 81, 91,
+    21, 255, 82, 91,
+    22, 255, 82, 92,
+    24, 255, 83, 93,
+    26, 255, 84, 93,
+    28, 255, 84, 94,
+    30, 255, 85, 95,
+    32, 255, 86, 95,
+    34, 255, 86, 96,
+    36, 255, 87, 96,
+    38, 255, 88, 97,
+    40, 255, 88, 98,
+    42, 255, 89, 98,
+    43, 255, 90, 99,
+    45, 255, 90, 100,
+    47, 255, 91, 100,
+    49, 255, 92, 101,
+    51, 255, 92, 101,
+    53, 255, 93, 102,
+    55, 255, 94, 103,
+    57, 254, 94, 103,
+    59, 255, 95, 104,
+    61, 254, 96, 105,
+    63, 255, 96, 105,
+    65, 255, 97, 106,
+    66, 255, 98, 106,
+    68, 255, 98, 107,
+    70, 255, 99, 108,
+    72, 255, 100, 108,
+    74, 255, 100, 109,
+    76, 255, 101, 110,
+    78, 255, 102, 110,
+    80, 255, 102, 111,
+    82, 255, 103, 111,
+    84, 255, 104, 112,
+    86, 255, 104, 113,
+    87, 255, 105, 113,
+    89, 255, 106, 114,
+    91, 255, 106, 115,
+    93, 255, 107, 115,
+    95, 255, 108, 116,
+    97, 255, 108, 117,
+    99, 254, 109, 117,
+    101, 255, 110, 118,
+    103, 254, 110, 118,
+    105, 255, 111, 119,
+    107, 255, 112, 120,
+    109, 255, 112, 120,
+    110, 255, 113, 121,
+    112, 255, 114, 122,
+    114, 255, 114, 122,
+    116, 255, 115, 123,
+    118, 255, 116, 123,
+    120, 255, 116, 124,
+    122, 255, 117, 125,
+    124, 255, 118, 125,
+    126, 255, 118, 126,
+    128, 255, 119, 127,
+    130, 255, 120, 127,
+    131, 255, 120, 128,
+    133, 255, 121, 128,
+    135, 254, 122, 129,
+    137, 255, 122, 130,
+    139, 254, 123, 130,
+    141, 255, 124, 131,
+    143, 255, 124, 132,
+    145, 255, 125, 132,
+    147, 255, 126, 133,
+    149, 255, 126, 133,
+    151, 255, 127, 134,
+    153, 255, 128, 135,
+    154, 255, 128, 135,
+    156, 255, 129, 136,
+    158, 255, 130, 137,
+    160, 255, 130, 137,
+    162, 255, 131, 138,
+    164, 255, 132, 138,
+    166, 255, 132, 139,
+    168, 255, 133, 140,
+    170, 255, 134, 140,
+    172, 254, 134, 141,
+    174, 255, 135, 142,
+    175, 255, 136, 142,
+    177, 255, 136, 143,
+    179, 254, 137, 143,
+    181, 255, 138, 144,
+    183, 255, 138, 145,
+    185, 255, 139, 145,
+    187, 255, 140, 146,
+    189, 255, 140, 147,
+    191, 255, 141, 147,
+    193, 255, 142, 148,
+    195, 255, 142, 148,
+    197, 254, 143, 149,
+    198, 255, 144, 150,
+    200, 254, 144, 150,
+    202, 255, 145, 151,
+    204, 255, 146, 152,
+    206, 255, 146, 152,
+    208, 255, 147, 153,
+    210, 255, 148, 153,
+    212, 255, 148, 154,
+    214, 255, 149, 155,
+    216, 254, 150, 155,
+    218, 255, 150, 156,
+    219, 255, 151, 157,
+    221, 255, 152, 157,
+    223, 255, 152, 158,
+    225, 255, 153, 158,
+    227, 255, 154, 159
+  };
+
+  lut->SetNumberOfTableValues(256);
+  for(int i=0; i<256; i++)
+  {
+    int k = i*4;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutVolRenGlow( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //volrenGlow
+  unsigned char v[256*4] =
+  {
+    0, 0, 0, 0,
+    0, 0, 0, 1,
+    5, 0, 0, 2,
+    5, 0, 0, 3,
+    9, 0, 0, 4,
+    9, 0, 0, 5,
+    13, 0, 0, 6,
+    13, 0, 5, 7,
+    17, 0, 5, 8,
+    17, 0, 5, 9,
+    21, 0, 5, 10,
+    21, 0, 5, 11,
+    25, 0, 5, 12,
+    25, 0, 5, 13,
+    29, 0, 5, 14,
+    29, 0, 9, 15,
+    33, 0, 9, 16,
+    33, 0, 9, 17,
+    37, 0, 9, 18,
+    37, 0, 9, 19,
+    41, 0, 9, 20,
+    41, 0, 9, 21,
+    45, 0, 9, 22,
+    45, 0, 9, 23,
+    49, 0, 13, 24,
+    49, 0, 13, 25,
+    53, 0, 13, 26,
+    53, 0, 13, 27,
+    57, 0, 13, 28,
+    57, 0, 13, 29,
+    61, 0, 13, 30,
+    61, 5, 13, 31,
+    65, 5, 13, 32,
+    65, 5, 13, 33,
+    69, 5, 13, 34,
+    69, 5, 13, 35,
+    73, 5, 17, 36,
+    73, 5, 17, 37,
+    77, 5, 17, 38,
+    81, 5, 17, 39,
+    81, 5, 17, 40,
+    85, 5, 17, 41,
+    85, 5, 17, 42,
+    89, 5, 17, 43,
+    89, 9, 17, 44,
+    93, 9, 17, 45,
+    93, 9, 17, 46,
+    97, 9, 17, 47,
+    97, 9, 17, 48,
+    101, 9, 17, 49,
+    101, 9, 17, 50,
+    105, 9, 17, 51,
+    105, 9, 17, 52,
+    109, 13, 17, 53,
+    109, 13, 17, 54,
+    113, 13, 17, 55,
+    113, 13, 17, 56,
+    117, 13, 17, 57,
+    117, 13, 17, 58,
+    121, 13, 17, 59,
+    125, 13, 17, 60,
+    125, 13, 17, 61,
+    128, 17, 17, 62,
+    128, 17, 17, 63,
+    132, 17, 21, 64,
+    132, 17, 21, 65,
+    136, 17, 21, 66,
+    136, 17, 21, 67,
+    140, 17, 21, 68,
+    140, 21, 21, 69,
+    144, 21, 21, 70,
+    144, 21, 21, 71,
+    148, 21, 21, 72,
+    148, 21, 21, 73,
+    152, 25, 21, 74,
+    152, 25, 21, 75,
+    156, 25, 21, 76,
+    156, 25, 21, 77,
+    156, 25, 21, 78,
+    160, 29, 21, 79,
+    160, 29, 21, 80,
+    164, 29, 21, 81,
+    164, 29, 21, 82,
+    168, 33, 21, 83,
+    168, 33, 21, 84,
+    168, 33, 21, 85,
+    172, 33, 21, 86,
+    172, 37, 21, 87,
+    176, 37, 21, 88,
+    176, 37, 21, 89,
+    176, 37, 21, 90,
+    180, 41, 21, 91,
+    180, 41, 21, 92,
+    180, 41, 21, 93,
+    184, 45, 21, 94,
+    184, 45, 21, 95,
+    188, 45, 21, 96,
+    188, 49, 21, 97,
+    188, 49, 21, 98,
+    192, 49, 21, 99,
+    192, 53, 21, 100,
+    192, 53, 21, 101,
+    192, 53, 21, 102,
+    196, 57, 21, 103,
+    196, 57, 21, 104,
+    196, 61, 21, 105,
+    200, 61, 21, 106,
+    200, 61, 21, 107,
+    200, 65, 21, 108,
+    200, 65, 21, 109,
+    204, 69, 21, 110,
+    204, 69, 21, 111,
+    204, 69, 21, 112,
+    208, 73, 21, 113,
+    208, 73, 21, 114,
+    208, 77, 21, 115,
+    208, 77, 21, 116,
+    208, 81, 21, 117,
+    212, 81, 21, 118,
+    212, 85, 21, 119,
+    212, 85, 21, 120,
+    212, 89, 21, 121,
+    212, 89, 21, 122,
+    216, 93, 21, 123,
+    216, 93, 21, 124,
+    216, 97, 21, 125,
+    216, 97, 21, 126,
+    216, 101, 21, 127,
+    220, 101, 21, 128,
+    220, 105, 21, 129,
+    220, 105, 21, 130,
+    220, 109, 21, 131,
+    220, 109, 21, 132,
+    220, 113, 21, 133,
+    220, 113, 21, 134,
+    224, 117, 21, 135,
+    224, 117, 25, 136,
+    224, 121, 25, 137,
+    224, 121, 25, 138,
+    224, 125, 25, 139,
+    224, 125, 25, 140,
+    224, 128, 25, 141,
+    228, 128, 25, 142,
+    228, 132, 25, 143,
+    228, 132, 29, 144,
+    228, 136, 29, 145,
+    228, 136, 29, 146,
+    228, 140, 29, 147,
+    228, 140, 29, 148,
+    228, 144, 33, 149,
+    232, 144, 33, 150,
+    232, 144, 33, 151,
+    232, 148, 33, 152,
+    232, 148, 33, 153,
+    232, 152, 37, 154,
+    232, 152, 37, 155,
+    232, 156, 37, 156,
+    232, 156, 37, 157,
+    236, 160, 41, 158,
+    236, 160, 41, 159,
+    236, 160, 41, 160,
+    236, 164, 41, 161,
+    236, 164, 45, 162,
+    236, 168, 45, 163,
+    236, 168, 45, 164,
+    236, 172, 49, 165,
+    236, 172, 49, 166,
+    236, 172, 49, 167,
+    240, 176, 53, 168,
+    240, 176, 53, 169,
+    240, 180, 53, 170,
+    240, 180, 57, 171,
+    240, 180, 57, 172,
+    240, 184, 61, 173,
+    240, 184, 61, 174,
+    240, 188, 61, 175,
+    240, 188, 65, 176,
+    240, 188, 65, 177,
+    240, 192, 69, 178,
+    244, 192, 69, 179,
+    244, 192, 73, 180,
+    244, 192, 73, 181,
+    244, 192, 77, 182,
+    244, 200, 77, 183,
+    244, 200, 81, 184,
+    244, 200, 81, 185,
+    244, 204, 85, 186,
+    244, 204, 85, 187,
+    244, 204, 89, 188,
+    244, 208, 89, 189,
+    244, 208, 93, 190,
+    244, 208, 93, 191,
+    248, 212, 97, 192,
+    248, 212, 97, 193,
+    248, 212, 101, 194,
+    248, 216, 105, 195,
+    248, 216, 105, 196,
+    248, 216, 109, 197,
+    248, 220, 109, 198,
+    248, 220, 113, 199,
+    248, 220, 113, 200,
+    248, 224, 117, 201,
+    248, 224, 121, 202,
+    248, 224, 121, 203,
+    248, 224, 125, 204,
+    248, 228, 125, 205,
+    248, 228, 128, 206,
+    248, 228, 128, 207,
+    248, 228, 132, 208,
+    248, 232, 136, 209,
+    248, 232, 136, 210,
+    252, 232, 140, 211,
+    252, 232, 144, 212,
+    252, 236, 144, 213,
+    252, 236, 148, 214,
+    252, 236, 148, 215,
+    252, 236, 152, 216,
+    252, 240, 156, 217,
+    252, 240, 156, 218,
+    252, 240, 160, 219,
+    252, 240, 160, 220,
+    252, 240, 164, 221,
+    252, 240, 168, 222,
+    252, 244, 168, 223,
+    252, 244, 172, 224,
+    252, 244, 172, 225,
+    252, 244, 176, 226,
+    252, 244, 180, 227,
+    252, 244, 180, 228,
+    252, 248, 184, 229,
+    252, 248, 188, 230,
+    252, 248, 188, 231,
+    252, 248, 192, 232,
+    252, 248, 192, 233,
+    252, 248, 196, 234,
+    252, 248, 200, 235,
+    252, 248, 200, 236,
+    252, 252, 204, 237,
+    252, 252, 208, 238,
+    252, 252, 208, 239,
+    252, 252, 212, 240,
+    252, 252, 212, 241,
+    252, 252, 216, 242,
+    252, 252, 220, 243,
+    252, 252, 220, 244,
+    252, 252, 224, 245,
+    252, 252, 228, 246,
+    252, 252, 228, 247,
+    252, 252, 232, 248,
+    252, 252, 236, 249,
+    252, 252, 236, 250,
+    252, 252, 240, 251,
+    252, 252, 244, 252,
+    252, 252, 244, 253,
+    252, 252, 248, 254,
+    255, 255, 255, 255
+  };
+
+
+  lut->SetNumberOfTableValues(256);
+  for(int i=0; i<256; i++)
+  {
+    int k = i*4;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutTenStep( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //tensteps
+  unsigned char v[10*3] =
+  {
+    29,  0,   134,
+    0,   18,  163,
+    0,   74,  188,
+    0,   159, 195,
+    0,   201, 150,
+    0,   209, 12,
+    141, 217, 0,
+    220, 221, 0,
+    226, 138, 0,
+    231, 48,  0,
+  };
+
+
+  lut->SetNumberOfTableValues(10);
+  for(int i=0; i<10; i++)
+  {
+    int k = i*3;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutTemperature( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //temperature
+  unsigned char v[256*3] =
+  {
+    255,255,255,
+    251,251,253,
+    249,247,255,
+    246,244,253,
+    242,242,255,
+    239,239,253,
+    237,235,255,
+    233,232,255,
+    230,230,253,
+    228,226,255,
+    225,223,253,
+    221,221,255,
+    219,218,253,
+    216,214,255,
+    212,212,255,
+    209,209,255,
+    207,205,255,
+    205,202,253,
+    202,200,255,
+    198,196,253,
+    195,193,255,
+    193,191,253,
+    189,188,255,
+    186,184,255,
+    184,181,255,
+    181,179,255,
+    177,175,253,
+    175,172,255,
+    172,170,253,
+    168,167,255,
+    165,163,255,
+    163,161,255,
+    160,158,255,
+    156,154,253,
+    154,151,255,
+    151,149,253,
+    149,144,255,
+    145,142,253,
+    142,138,255,
+    140,135,255,
+    137,133,255,
+    133,130,255,
+    130,126,253,
+    128,124,255,
+    124,121,253,
+    121,117,255,
+    119,114,255,
+    116,112,255,
+    112,109,255,
+    110,105,253,
+    107,103,255,
+    105,100,253,
+    102,96,255,
+    98,93,253,
+    96,91,255,
+    93,87,255,
+    89,84,253,
+    86,82,255,
+    84,79,253,
+    80,75,255,
+    77,73,253,
+    75,70,255,
+    72,66,255,
+    68,63,255,
+    66,61,255,
+    66,59,253,
+    68,59,251,
+    70,59,249,
+    70,58,249,
+    72,58,247,
+    73,56,246,
+    73,56,244,
+    75,56,244,
+    77,54,242,
+    77,54,240,
+    79,54,239,
+    79,52,237,
+    80,52,237,
+    82,52,235,
+    84,52,233,
+    84,51,232,
+    86,49,232,
+    86,49,230,
+    87,49,228,
+    89,49,228,
+    91,47,226,
+    91,47,225,
+    93,47,223,
+    93,45,221,
+    96,45,221,
+    96,45,219,
+    98,43,218,
+    98,43,216,
+    100,42,214,
+    100,42,214,
+    103,42,212,
+    103,40,211,
+    105,40,209,
+    105,40,209,
+    107,38,207,
+    107,38,205,
+    110,38,205,
+    110,38,202,
+    112,36,202,
+    112,35,200,
+    114,35,198,
+    114,35,198,
+    117,33,196,
+    117,33,195,
+    119,33,193,
+    119,33,193,
+    121,31,191,
+    121,31,189,
+    124,31,188,
+    124,29,186,
+    126,29,186,
+    126,28,184,
+    128,28,182,
+    128,28,181,
+    130,26,181,
+    131,26,179,
+    133,26,177,
+    133,24,175,
+    135,24,175,
+    135,24,174,
+    137,24,172,
+    138,22,170,
+    140,22,170,
+    140,22,168,
+    142,21,167,
+    142,19,165,
+    144,19,163,
+    145,19,163,
+    147,19,161,
+    147,17,160,
+    149,17,158,
+    149,17,158,
+    151,15,156,
+    153,15,154,
+    154,15,154,
+    154,14,151,
+    156,14,151,
+    156,12,149,
+    158,12,147,
+    160,12,147,
+    161,10,145,
+    161,10,144,
+    163,10,142,
+    165,10,142,
+    167,8,137,
+    168,8,135,
+    172,8,131,
+    174,8,128,
+    175,8,126,
+    179,8,121,
+    181,8,119,
+    182,7,116,
+    186,7,112,
+    188,7,110,
+    191,7,107,
+    193,5,103,
+    195,5,100,
+    198,5,96,
+    200,5,93,
+    202,5,91,
+    205,5,86,
+    207,5,84,
+    209,5,80,
+    212,3,77,
+    214,3,75,
+    216,3,72,
+    219,3,68,
+    221,3,65,
+    223,3,61,
+    226,3,59,
+    228,1,56,
+    230,1,52,
+    232,1,49,
+    235,1,45,
+    237,1,42,
+    239,1,40,
+    242,1,36,
+    244,1,33,
+    246,0,29,
+    249,0,26,
+    249,0,26,
+    249,0,26,
+    249,0,26,
+    251,0,26,
+    251,0,24,
+    251,0,24,
+    251,0,24,
+    251,0,24,
+    251,0,24,
+    251,0,24,
+    251,0,22,
+    253,0,22,
+    253,0,22,
+    253,0,22,
+    253,0,22,
+    253,0,22,
+    253,0,21,
+    253,0,21,
+    253,0,21,
+    255,0,21,
+    255,3,21,
+    255,8,21,
+    255,12,21,
+    255,17,21,
+    255,22,21,
+    255,26,22,
+    255,31,22,
+    255,35,22,
+    255,40,22,
+    255,45,22,
+    255,49,22,
+    255,54,22,
+    255,59,22,
+    255,63,22,
+    255,68,22,
+    255,73,22,
+    255,77,24,
+    255,82,24,
+    255,86,24,
+    255,91,24,
+    255,96,24,
+    255,100,24,
+    255,105,24,
+    255,110,24,
+    255,114,24,
+    255,119,24,
+    255,123,24,
+    255,128,26,
+    255,133,26,
+    255,137,24,
+    255,142,24,
+    255,147,24,
+    255,154,22,
+    255,158,22,
+    255,163,22,
+    255,168,21,
+    255,175,21,
+    255,179,19,
+    255,184,19,
+    255,191,19,
+    255,195,17,
+    255,200,17,
+    255,205,17,
+    255,212,17,
+    255,216,15,
+    255,221,15,
+    255,226,15,
+    255,232,14,
+    255,237,14,
+    255,242,12,
+    255,249,12,
+  };
+
+  lut->SetNumberOfTableValues(256);
+
+  for(int i=0; i<256; i++)
+  {
+    int k = i*3;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+  }
+
+
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutSar( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //sar
+  unsigned char v[256*3] =
+  {
+    255,255,255,
+    251,251,253,
+    246,246,253,
+    244,246,253,
+    242,242,253,
+    239,239,251,
+    235,237,251,
+    232,232,251,
+    230,232,251,
+    226,228,249,
+    223,226,249,
+    221,223,249,
+    218,219,249,
+    214,219,246,
+    212,214,246,
+    209,214,246,
+    207,212,246,
+    204,209,246,
+    200,207,246,
+    198,205,246,
+    195,204,244,
+    191,202,244,
+    188,198,244,
+    186,198,244,
+    182,195,242,
+    179,195,242,
+    177,193,242,
+    174,189,242,
+    172,188,239,
+    168,186,239,
+    167,186,239,
+    163,186,239,
+    161,184,239,
+    158,181,239,
+    156,179,239,
+    153,181,237,
+    149,177,237,
+    147,177,237,
+    144,175,237,
+    142,174,235,
+    137,175,235,
+    137,174,235,
+    133,174,235,
+    130,172,232,
+    128,170,232,
+    126,170,232,
+    123,168,232,
+    121,170,232,
+    117,168,232,
+    116,167,232,
+    112,168,230,
+    110,167,230,
+    107,167,230,
+    105,167,230,
+    103,167,228,
+    100,165,228,
+    98,165,228,
+    93,163,228,
+    89,163,226,
+    87,163,226,
+    86,165,226,
+    84,165,226,
+    80,163,225,
+    79,165,225,
+    77,165,225,
+    75,163,223,
+    73,165,223,
+    68,163,223,
+    66,163,223,
+    65,167,221,
+    61,167,221,
+    59,165,221,
+    56,168,221,
+    54,168,219,
+    51,168,219,
+    51,168,219,
+    47,170,219,
+    45,168,218,
+    42,168,218,
+    40,172,216,
+    36,172,216,
+    35,172,216,
+    33,172,216,
+    33,174,216,
+    28,175,214,
+    28,174,214,
+    24,175,214,
+    24,175,214,
+    21,179,212,
+    17,179,212,
+    17,179,212,
+    14,181,212,
+    10,184,211,
+    10,182,211,
+    7,186,209,
+    5,186,209,
+    1,186,209,
+    1,188,209,
+    0,191,209,
+    0,193,209,
+    0,195,209,
+    0,200,209,
+    0,200,209,
+    0,204,209,
+    0,207,209,
+    0,211,209,
+    0,211,207,
+    0,211,205,
+    0,212,202,
+    0,212,198,
+    0,212,198,
+    0,212,193,
+    0,212,193,
+    0,212,189,
+    0,212,186,
+    0,214,184,
+    0,214,184,
+    0,214,181,
+    0,214,179,
+    0,214,177,
+    0,214,174,
+    0,214,172,
+    0,216,167,
+    0,216,165,
+    0,216,163,
+    0,216,161,
+    0,216,158,
+    0,216,156,
+    0,216,154,
+    0,218,151,
+    0,218,147,
+    0,218,145,
+    0,219,142,
+    0,219,140,
+    0,219,137,
+    0,219,137,
+    0,219,133,
+    0,219,130,
+    0,219,128,
+    0,221,124,
+    0,221,123,
+    0,221,121,
+    0,221,117,
+    0,221,114,
+    0,221,110,
+    0,221,110,
+    0,223,107,
+    0,223,103,
+    0,223,102,
+    0,223,100,
+    0,223,98,
+    0,223,93,
+    0,223,91,
+    0,225,87,
+    0,225,84,
+    0,225,84,
+    0,226,80,
+    0,226,77,
+    0,226,73,
+    0,226,72,
+    0,226,70,
+    0,226,68,
+    0,226,63,
+    0,228,59,
+    0,228,58,
+    0,228,56,
+    0,228,54,
+    0,228,51,
+    0,228,47,
+    0,228,43,
+    0,230,40,
+    0,230,38,
+    0,230,35,
+    0,230,35,
+    0,230,29,
+    0,230,28,
+    0,230,26,
+    0,232,22,
+    0,232,17,
+    0,232,15,
+    0,232,14,
+    0,232,12,
+    0,232,7,
+    0,232,3,
+    0,232,3,
+    0,232,0,
+    0,232,0,
+    5,235,0,
+    8,235,0,
+    8,235,0,
+    14,235,0,
+    14,235,0,
+    19,235,0,
+    24,235,0,
+    31,237,0,
+    38,237,0,
+    45,237,0,
+    51,237,0,
+    58,237,0,
+    66,237,0,
+    72,237,0,
+    79,239,0,
+    86,239,0,
+    93,239,0,
+    98,239,0,
+    107,239,0,
+    114,239,0,
+    121,239,0,
+    128,239,0,
+    133,239,0,
+    140,239,0,
+    149,242,0,
+    154,242,0,
+    161,242,0,
+    170,242,0,
+    177,242,0,
+    184,242,0,
+    191,242,0,
+    198,244,0,
+    205,244,0,
+    212,244,0,
+    219,244,0,
+    228,244,0,
+    233,244,0,
+    239,244,0,
+    246,242,0,
+    246,235,0,
+    246,226,0,
+    246,221,0,
+    246,214,0,
+    246,205,0,
+    246,198,0,
+    246,193,0,
+    246,186,0,
+    246,179,0,
+    249,174,0,
+    249,168,0,
+    249,160,0,
+    249,153,0,
+    249,144,0,
+    249,137,0,
+    249,131,0,
+    251,124,0,
+    251,119,0,
+    251,112,0,
+    251,103,0,
+    251,96,0,
+    251,89,0,
+    251,82,0,
+    253,77,0,
+    253,70,0,
+    253,61,0,
+    253,56,0,
+    253,47,0,
+    253,40,0,
+    255,33,0
+  };
+
+
+  lut->SetNumberOfTableValues(256);
+  for(int i=0; i<256; i++)
+  {
+    int k = i*3;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+  }
+}
+
+//---------------------------------------------------------------------
+    void LutSelector::lutPhysicsContour( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //physicsContour
+  unsigned char v[256*3] =
+  {
+    101, 4, 255,
+    97, 4, 253,
+    92, 4, 250,
+    89, 4, 248,
+    83, 4, 245,
+    80, 4, 243,
+    76, 4, 241,
+    73, 4, 238,
+    68, 4, 236,
+    65, 2, 233,
+    62, 2, 231,
+    57, 2, 229,
+    54, 2, 226,
+    50, 2, 224,
+    48, 2, 221,
+    43, 2, 219,
+    41, 2, 217,
+    37, 2, 214,
+    35, 2, 212,
+    31, 1, 209,
+    28, 1, 207,
+    26, 1, 204,
+    22, 1, 202,
+    19, 1, 200,
+    16, 1, 197,
+    13, 1, 195,
+    12, 0, 192,
+    9, 0, 190,
+    6, 0, 188,
+    4, 0, 185,
+    1, 0, 183,
+    0, 1, 180,
+    0, 3, 178,
+    0, 6, 176,
+    0, 8, 173,
+    0, 10, 171,
+    0, 12, 168,
+    0, 13, 166,
+    0, 15, 164,
+    0, 18, 161,
+    1, 19, 159,
+    2, 22, 156,
+    3, 24, 154,
+    3, 25, 152,
+    4, 28, 149,
+    4, 29, 147,
+    5, 31, 144,
+    6, 33, 142,
+    7, 33, 140,
+    6, 35, 137,
+    7, 37, 135,
+    8, 37, 132,
+    8, 39, 130,
+    9, 40, 128,
+    19, 84, 255,
+    20, 85, 253,
+    21, 88, 250,
+    22, 91, 248,
+    22, 94, 245,
+    23, 95, 243,
+    24, 97, 241,
+    25, 100, 238,
+    25, 102, 236,
+    26, 103, 233,
+    27, 105, 231,
+    28, 107, 229,
+    29, 109, 226,
+    29, 111, 224,
+    30, 111, 221,
+    30, 113, 219,
+    29, 116, 217,
+    29, 118, 214,
+    29, 119, 212,
+    28, 121, 209,
+    28, 123, 207,
+    28, 124, 204,
+    27, 125, 202,
+    27, 126, 200,
+    27, 128, 197,
+    26, 129, 195,
+    26, 131, 192,
+    26, 132, 190,
+    25, 133, 188,
+    25, 134, 185,
+    25, 135, 183,
+    24, 136, 180,
+    24, 137, 178,
+    23, 137, 176,
+    23, 138, 173,
+    22, 138, 171,
+    22, 139, 168,
+    21, 140, 166,
+    21, 139, 164,
+    21, 140, 161,
+    20, 140, 159,
+    20, 140, 156,
+    20, 141, 154,
+    19, 140, 152,
+    19, 140, 149,
+    19, 140, 147,
+    18, 140, 144,
+    18, 140, 142,
+    18, 140, 140,
+    17, 137, 135,
+    17, 135, 131,
+    17, 132, 127,
+    16, 130, 123,
+    16, 128, 118,
+    32, 255, 232,
+    32, 253, 226,
+    31, 250, 221,
+    31, 248, 215,
+    31, 245, 209,
+    29, 243, 203,
+    29, 241, 198,
+    29, 238, 193,
+    28, 236, 187,
+    28, 233, 181,
+    28, 231, 176,
+    27, 229, 171,
+    27, 226, 167,
+    27, 224, 158,
+    26, 221, 151,
+    26, 219, 144,
+    26, 217, 138,
+    25, 214, 131,
+    25, 212, 124,
+    25, 209, 117,
+    24, 207, 111,
+    24, 204, 104,
+    23, 202, 98,
+    22, 200, 92,
+    22, 197, 86,
+    22, 195, 79,
+    21, 192, 74,
+    21, 190, 68,
+    21, 188, 63,
+    21, 185, 57,
+    20, 183, 52,
+    20, 180, 47,
+    20, 178, 42,
+    19, 176, 37,
+    19, 173, 32,
+    18, 171, 27,
+    18, 168, 23,
+    17, 166, 18,
+    21, 164, 17,
+    25, 161, 17,
+    28, 159, 17,
+    31, 156, 16,
+    35, 154, 16,
+    38, 152, 16,
+    41, 149, 15,
+    44, 147, 15,
+    47, 144, 15,
+    49, 142, 14,
+    52, 140, 14,
+    54, 137, 13,
+    57, 135, 13,
+    59, 132, 13,
+    61, 130, 13,
+    63, 128, 12,
+    133, 255, 25,
+    138, 253, 25,
+    142, 250, 24,
+    148, 248, 24,
+    152, 245, 24,
+    157, 243, 22,
+    161, 241, 22,
+    162, 238, 22,
+    164, 236, 22,
+    164, 233, 21,
+    165, 231, 21,
+    167, 229, 21,
+    167, 226, 21,
+    168, 224, 21,
+    169, 221, 20,
+    169, 219, 20,
+    170, 217, 20,
+    170, 214, 20,
+    171, 212, 19,
+    172, 209, 19,
+    172, 207, 19,
+    173, 204, 19,
+    173, 202, 18,
+    172, 200, 18,
+    173, 197, 18,
+    174, 195, 18,
+    173, 192, 17,
+    174, 190, 16,
+    173, 188, 16,
+    173, 185, 16,
+    174, 183, 16,
+    174, 180, 16,
+    174, 178, 15,
+    175, 176, 15,
+    173, 172, 15,
+    171, 167, 15,
+    168, 162, 14,
+    166, 159, 14,
+    164, 154, 14,
+    161, 149, 14,
+    159, 145, 14,
+    156, 140, 13,
+    154, 136, 13,
+    152, 132, 13,
+    149, 128, 13,
+    147, 123, 12,
+    144, 119, 12,
+    142, 115, 11,
+    140, 111, 11,
+    137, 107, 11,
+    135, 103, 11,
+    132, 99, 11,
+    130, 96, 10,
+    128, 92, 10,
+    255, 181, 20,
+    255, 178, 20,
+    255, 174, 20,
+    255, 170, 20,
+    255, 167, 20,
+    255, 164, 20,
+    255, 159, 20,
+    255, 155, 20,
+    255, 152, 20,
+    255, 148, 20,
+    255, 145, 19,
+    255, 142, 19,
+    255, 136, 19,
+    255, 133, 19,
+    255, 130, 19,
+    255, 126, 19,
+    255, 123, 19,
+    255, 119, 19,
+    255, 115, 19,
+    255, 111, 19,
+    255, 108, 19,
+    255, 105, 19,
+    255, 100, 19,
+    255, 96, 19,
+    255, 93, 19,
+    255, 89, 19,
+    255, 85, 19,
+    255, 82, 19,
+    255, 79, 18,
+    255, 74, 18,
+    255, 71, 18,
+    255, 67, 18,
+    255, 63, 18,
+    255, 60, 18,
+    255, 56, 18,
+    255, 52, 18,
+    255, 48, 18,
+    255, 45, 18,
+    255, 41, 18,
+    255, 37, 18,
+  };
+
+
+  lut->SetNumberOfTableValues(256);
+  for(int i=0; i<256; i++)
+  {
+    int k = i*3;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutGlow( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+   //glow
+  unsigned char v[256*3] =
+  {
+    0, 0, 0,
+    0, 0, 0,
+    5, 0, 0,
+    5, 0, 0,
+    9, 0, 0,
+    9, 0, 0,
+    13, 0, 0,
+    13, 0, 5,
+    17, 0, 5,
+    17, 0, 5,
+    21, 0, 5,
+    21, 0, 5,
+    25, 0, 5,
+    25, 0, 5,
+    29, 0, 5,
+    29, 0, 9,
+    33, 0, 9,
+    33, 0, 9,
+    37, 0, 9,
+    37, 0, 9,
+    41, 0, 9,
+    41, 0, 9,
+    45, 0, 9,
+    45, 0, 9,
+    49, 0, 13,
+    49, 0, 13,
+    53, 0, 13,
+    53, 0, 13,
+    57, 0, 13,
+    57, 0, 13,
+    61, 0, 13,
+    61, 5, 13,
+    65, 5, 13,
+    65, 5, 13,
+    69, 5, 13,
+    69, 5, 13,
+    73, 5, 17,
+    73, 5, 17,
+    77, 5, 17,
+    81, 5, 17,
+    81, 5, 17,
+    85, 5, 17,
+    85, 5, 17,
+    89, 5, 17,
+    89, 9, 17,
+    93, 9, 17,
+    93, 9, 17,
+    97, 9, 17,
+    97, 9, 17,
+    101, 9, 17,
+    101, 9, 17,
+    105, 9, 17,
+    105, 9, 17,
+    109, 13, 17,
+    109, 13, 17,
+    113, 13, 17,
+    113, 13, 17,
+    117, 13, 17,
+    117, 13, 17,
+    121, 13, 17,
+    125, 13, 17,
+    125, 13, 17,
+    128, 17, 17,
+    128, 17, 17,
+    132, 17, 21,
+    132, 17, 21,
+    136, 17, 21,
+    136, 17, 21,
+    140, 17, 21,
+    140, 21, 21,
+    144, 21, 21,
+    144, 21, 21,
+    148, 21, 21,
+    148, 21, 21,
+    152, 25, 21,
+    152, 25, 21,
+    156, 25, 21,
+    156, 25, 21,
+    156, 25, 21,
+    160, 29, 21,
+    160, 29, 21,
+    164, 29, 21,
+    164, 29, 21,
+    168, 33, 21,
+    168, 33, 21,
+    168, 33, 21,
+    172, 33, 21,
+    172, 37, 21,
+    176, 37, 21,
+    176, 37, 21,
+    176, 37, 21,
+    180, 41, 21,
+    180, 41, 21,
+    180, 41, 21,
+    184, 45, 21,
+    184, 45, 21,
+    188, 45, 21,
+    188, 49, 21,
+    188, 49, 21,
+    192, 49, 21,
+    192, 53, 21,
+    192, 53, 21,
+    192, 53, 21,
+    196, 57, 21,
+    196, 57, 21,
+    196, 61, 21,
+    200, 61, 21,
+    200, 61, 21,
+    200, 65, 21,
+    200, 65, 21,
+    204, 69, 21,
+    204, 69, 21,
+    204, 69, 21,
+    208, 73, 21,
+    208, 73, 21,
+    208, 77, 21,
+    208, 77, 21,
+    208, 81, 21,
+    212, 81, 21,
+    212, 85, 21,
+    212, 85, 21,
+    212, 89, 21,
+    212, 89, 21,
+    216, 93, 21,
+    216, 93, 21,
+    216, 97, 21,
+    216, 97, 21,
+    216, 101, 21,
+    220, 101, 21,
+    220, 105, 21,
+    220, 105, 21,
+    220, 109, 21,
+    220, 109, 21,
+    220, 113, 21,
+    220, 113, 21,
+    224, 117, 21,
+    224, 117, 25,
+    224, 121, 25,
+    224, 121, 25,
+    224, 125, 25,
+    224, 125, 25,
+    224, 128, 25,
+    228, 128, 25,
+    228, 132, 25,
+    228, 132, 29,
+    228, 136, 29,
+    228, 136, 29,
+    228, 140, 29,
+    228, 140, 29,
+    228, 144, 33,
+    232, 144, 33,
+    232, 144, 33,
+    232, 148, 33,
+    232, 148, 33,
+    232, 152, 37,
+    232, 152, 37,
+    232, 156, 37,
+    232, 156, 37,
+    236, 160, 41,
+    236, 160, 41,
+    236, 160, 41,
+    236, 164, 41,
+    236, 164, 45,
+    236, 168, 45,
+    236, 168, 45,
+    236, 172, 49,
+    236, 172, 49,
+    236, 172, 49,
+    240, 176, 53,
+    240, 176, 53,
+    240, 180, 53,
+    240, 180, 57,
+    240, 180, 57,
+    240, 184, 61,
+    240, 184, 61,
+    240, 188, 61,
+    240, 188, 65,
+    240, 188, 65,
+    240, 192, 69,
+    244, 192, 69,
+    244, 192, 73,
+    244, 192, 73,
+    244, 192, 77,
+    244, 200, 77,
+    244, 200, 81,
+    244, 200, 81,
+    244, 204, 85,
+    244, 204, 85,
+    244, 204, 89,
+    244, 208, 89,
+    244, 208, 93,
+    244, 208, 93,
+    248, 212, 97,
+    248, 212, 97,
+    248, 212, 101,
+    248, 216, 105,
+    248, 216, 105,
+    248, 216, 109,
+    248, 220, 109,
+    248, 220, 113,
+    248, 220, 113,
+    248, 224, 117,
+    248, 224, 121,
+    248, 224, 121,
+    248, 224, 125,
+    248, 228, 125,
+    248, 228, 129,
+    248, 228, 129,
+    248, 228, 133,
+    248, 232, 137,
+    248, 232, 137,
+    252, 232, 141,
+    252, 232, 145,
+    252, 236, 145,
+    252, 236, 148,
+    252, 236, 148,
+    252, 236, 152,
+    252, 240, 156,
+    252, 240, 156,
+    252, 240, 160,
+    252, 240, 160,
+    252, 240, 164,
+    252, 240, 168,
+    252, 244, 168,
+    252, 244, 172,
+    252, 244, 172,
+    252, 244, 176,
+    252, 244, 180,
+    252, 244, 180,
+    252, 248, 184,
+    252, 248, 188,
+    252, 248, 188,
+    252, 248, 192,
+    252, 248, 192,
+    252, 248, 196,
+    252, 248, 200,
+    252, 248, 200,
+    252, 252, 204,
+    252, 252, 208,
+    252, 252, 208,
+    252, 252, 212,
+    252, 252, 212,
+    252, 252, 216,
+    252, 252, 220,
+    252, 252, 220,
+    252, 252, 224,
+    252, 252, 228,
+    252, 252, 228,
+    252, 252, 232,
+    252, 252, 236,
+    252, 252, 236,
+    252, 252, 240,
+    252, 252, 244,
+    252, 252, 244,
+    252, 252, 248,
+    252, 252, 255,
+  };
+
+
+  lut->SetNumberOfTableValues(256);
+  for(int i=0; i<256; i++)
+  {
+    int k = i*3;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+  }
+}
+
+
+//---------------------------------------------------------------------
+void LutSelector::lutEField( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //efield
+  unsigned char v[256*3] =
+  {
+    237,253,135,
+    233,251,137,
+    232,251,137,
+    230,249,140,
+    226,249,140,
+    225,247,142,
+    223,246,144,
+    221,246,145,
+    218,244,147,
+    216,244,149,
+    214,242,149,
+    211,242,151,
+    209,240,153,
+    207,239,154,
+    205,239,156,
+    202,239,158,
+    200,237,158,
+    198,237,161,
+    195,235,161,
+    193,235,163,
+    191,233,165,
+    188,232,167,
+    186,232,168,
+    184,230,170,
+    181,230,170,
+    179,228,172,
+    177,228,174,
+    175,226,175,
+    172,226,177,
+    170,226,179,
+    168,225,179,
+    165,223,181,
+    163,223,182,
+    161,221,184,
+    158,221,186,
+    156,219,188,
+    154,219,188,
+    151,218,191,
+    149,216,191,
+    147,216,193,
+    144,214,195,
+    142,214,195,
+    140,214,198,
+    137,212,198,
+    135,212,200,
+    133,211,202,
+    130,209,204,
+    128,209,205,
+    126,207,207,
+    124,207,207,
+    121,205,209,
+    119,205,211,
+    117,204,212,
+    114,202,214,
+    112,202,216,
+    110,202,216,
+    107,200,219,
+    105,200,219,
+    103,198,221,
+    100,198,223,
+    98,196,225,
+    96,195,226,
+    93,195,228,
+    91,193,228,
+    89,193,230,
+    86,191,232,
+    84,191,233,
+    82,189,235,
+    80,189,237,
+    77,188,237,
+    75,188,239,
+    73,186,240,
+    70,186,242,
+    68,184,244,
+    66,184,246,
+    65,182,246,
+    61,181,249,
+    59,181,249,
+    58,179,251,
+    54,179,253,
+    52,177,255,
+    54,175,255,
+    54,174,255,
+    54,172,255,
+    56,170,255,
+    56,168,255,
+    58,167,255,
+    58,165,255,
+    59,163,255,
+    59,161,255,
+    61,158,255,
+    61,156,255,
+    61,154,255,
+    63,154,255,
+    63,151,255,
+    63,149,255,
+    65,147,255,
+    66,145,255,
+    66,144,255,
+    66,142,255,
+    68,140,255,
+    68,140,255,
+    70,137,255,
+    70,135,255,
+    70,135,255,
+    72,133,255,
+    73,131,255,
+    73,130,255,
+    73,128,255,
+    75,126,255,
+    75,124,255,
+    75,124,255,
+    77,121,255,
+    77,121,255,
+    79,119,255,
+    79,117,255,
+    79,117,255,
+    80,114,255,
+    82,114,255,
+    82,112,255,
+    82,110,255,
+    84,110,255,
+    84,107,255,
+    84,107,255,
+    86,105,255,
+    86,105,255,
+    87,103,255,
+    87,102,255,
+    89,100,255,
+    89,100,255,
+    91,98,255,
+    91,96,255,
+    89,94,255,
+    89,91,255,
+    87,89,255,
+    89,86,255,
+    89,86,255,
+    89,84,255,
+    91,84,255,
+    91,82,255,
+    93,82,255,
+    93,80,255,
+    94,79,255,
+    96,79,255,
+    98,77,255,
+    98,77,255,
+    100,75,255,
+    100,75,255,
+    102,73,255,
+    103,73,255,
+    105,72,255,
+    105,70,255,
+    107,70,255,
+    107,68,255,
+    110,68,255,
+    110,66,255,
+    112,65,255,
+    114,63,255,
+    114,63,255,
+    117,61,255,
+    117,61,255,
+    119,59,255,
+    121,59,255,
+    123,58,255,
+    124,56,255,
+    126,56,255,
+    128,54,255,
+    128,54,255,
+    130,52,255,
+    131,52,253,
+    133,52,253,
+    135,51,253,
+    137,49,253,
+    137,49,253,
+    138,49,253,
+    140,47,253,
+    142,47,253,
+    144,47,251,
+    144,45,251,
+    147,45,251,
+    147,45,251,
+    149,45,251,
+    151,43,251,
+    153,42,251,
+    154,42,251,
+    156,42,251,
+    156,40,249,
+    158,40,249,
+    160,40,249,
+    161,38,249,
+    163,38,249,
+    165,38,249,
+    167,36,249,
+    168,36,249,
+    170,35,247,
+    170,35,247,
+    172,35,247,
+    175,33,247,
+    175,33,246,
+    177,33,246,
+    179,31,246,
+    181,31,246,
+    182,31,246,
+    184,29,246,
+    186,29,246,
+    188,28,246,
+    189,28,246,
+    191,28,244,
+    193,26,244,
+    195,26,244,
+    195,26,244,
+    198,26,244,
+    200,24,244,
+    202,24,244,
+    202,24,244,
+    205,22,242,
+    207,22,242,
+    209,22,242,
+    209,21,242,
+    212,21,242,
+    214,19,242,
+    216,19,242,
+    218,19,242,
+    219,17,242,
+    221,17,240,
+    223,17,240,
+    225,15,240,
+    226,15,240,
+    228,15,239,
+    230,15,239,
+    232,14,239,
+    233,12,239,
+    235,12,239,
+    237,12,239,
+    239,12,237,
+    239,10,235,
+    239,10,232,
+    237,10,230,
+    237,8,228,
+    237,8,226,
+    237,8,223,
+    237,8,221,
+    237,7,219,
+    237,5,216,
+    237,5,214,
+    235,5,212,
+    235,5,209,
+    235,3,207,
+    235,3,204,
+    235,3,202,
+    235,1,200,
+    235,1,196,
+    235,1,195,
+    235,1,191,
+    233,0,189,
+    233,0,186,
+  };
+
+
+  lut->SetNumberOfTableValues(256);
+  for(int i=0; i<256; i++)
+  {
+    int k = i*3;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, 1);
+  }
+}
+
+
+//---------------------------------------------------------------------
+void LutSelector::lutDefault( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  Color c;
+
+
+    lut->SetNumberOfTableValues(256);
+  for(int i=0; i<256; i++)
+  {
+    int h = (i*240.0)/255.0;
+    c.setHSV(h,255,255);
+    lut->SetTableValue(i, c.m_red/255.0, c.m_green/255.0, c.m_blue/255.0, 1);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutMinMax( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  Color c;
+
+  lut->SetNumberOfTableValues(256);
+  int i;
+  for(i=0; i<128; i++)
+  {
+    int h = (i*240.0)/255.0;
+    c.setHSV(h,255,255);
+    lut->SetTableValue(i, c.m_red/255.0, c.m_green/255.0, c.m_blue/255.0, (128-i)/128.0);
+  }
+  for(i=128; i<256; i++)
+  {
+    int h = (i*240.0)/255.0;
+    c.setHSV(h,255,255);
+
+      lut->SetTableValue(i, c.m_red/255.0, c.m_green/255.0, c.m_blue/255.0, (i-128)/128.0);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutDefaultStep( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  lutDefault(lut);
+
+  Color c(128,128,128);
+
+  for(int i=0; i<256; i+=16)
+  {
+
+    lut->SetTableValue(i, c.m_red/255.0, c.m_green/255.0, c.m_blue/255.0, 1);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutGray( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(128);
+  for(int i=0; i<128; i++)
+  {
+
+   lut->SetTableValue(i, i/128.0, i/128.0, i/128.0, 1);
+  }
+};
+//---------------------------------------------------------------------
+void LutSelector::lutPureRed( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(128);
+  for(int i=0; i<128; i++)
+  {
+
+      lut->SetTableValue(i, i/128.0, 0, 0, 1);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutPureGreen( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(128);
+  for(int i=0; i<128; i++)
+  {
+
+      lut->SetTableValue(i, 0, i/128.0, 0, 1);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutPureBlue( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(128);
+  for(int i=0; i<128; i++)
+  {
+
+    lut->SetTableValue(i, 0, 0, i/128.0, 1);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutAllYellow( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+  for(int i=0; i<1; i++)
+  {
+
+      lut->SetTableValue(i, 1, 1, 0, 1);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutAllCyane( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+  for(int i=0; i<1; i++)
+  {
+
+      lut->SetTableValue(i, 0, 1, 1, 1);
+  }
+
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutAllViolet( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+  for(int i=0; i<1; i++)
+  {
+
+      lut->SetTableValue(i, 1, 0, 1, 1);
+  }
+
+}
+//---------------------------------------------------------------------
+void LutSelector::lutAllWhite( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+    lut->SetNumberOfTableValues(1);
+  for(int i=0; i<1; i++)
+  {
+
+      lut->SetTableValue(i, 1, 1, 1, 1);
+  }
+
+}
+//---------------------------------------------------------------------
+void LutSelector::lutAllBlack( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+
+    lut->SetNumberOfTableValues(1);
+  for(int i=0; i<1; i++)
+  {
+
+      lut->SetTableValue(i, 0, 0, 0, 1);
+  }
+
+}
+//---------------------------------------------------------------------
+void LutSelector::lutAllRed( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+
+    lut->SetNumberOfTableValues(1);
+  for(int i=0; i<1; i++)
+  {
+
+      lut->SetTableValue(i, 1, 0, 0, 1);
+  }
+
+}
+//---------------------------------------------------------------------
+void LutSelector::lutAllGreen( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+
+    lut->SetNumberOfTableValues(1);
+  for(int i=0; i<1; i++)
+  {
+
+      lut->SetTableValue(i, 0, 1, 0, 1);
+  }
+
+}
+//---------------------------------------------------------------------
+void LutSelector::lutAllBlu( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+
+    lut->SetNumberOfTableValues(1);
+  for(int i=0; i<1; i++)
+  {
+
+      lut->SetTableValue(i, 0, 0, 1, 1);
+  }
+}
+
+
+//---------------------------------------------------------------------
+void LutSelector::lutRun1( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //Run1
+  unsigned char v[256*4] =
+  {
+    0,255,0,0,
+    64,198,255,0,
+    128,3,255,233,
+    192,78,47,255,
+    1,255,4,0,
+    65,193,255,0,
+    129,4,255,238,
+    193,80,45,255,
+    2,255,9,0,
+    66,189,255,0,
+    130,5,255,243,
+    194,82,43,255,
+    3,255,14,0,
+    67,182,255,0,
+    131,6,255,248,
+    195,85,41,255,
+    4,255,19,0,
+    68,175,255,0,
+    132,7,255,253,
+    196,87,39,255,
+    5,255,24,0,
+    69,168,255,0,
+    133,8,252,255,
+    197,90,37,255,
+    6,255,29,0,
+    70,161,255,0,
+    134,9,247,255,
+    198,93,35,255,
+    7,255,34,0,
+    71,154,255,0,
+    135,10,242,255,
+    199,96,33,255,
+    8,255,38,0,
+    72,147,255,0,
+    136,10,238,255,
+    200,98,31,255,
+    9,255,43,0,
+    73,140,255,0,
+    137,11,233,255,
+    201,101,29,255,
+    10,255,48,0,
+    74,133,255,0,
+    138,12,228,255,
+    202,104,27,255,
+    11,255,53,0,
+    75,126,255,0,
+    139,13,224,255,
+    203,108,25,255,
+    12,255,58,0,
+    76,119,255,0,
+    140,14,219,255,
+    204,111,23,255,
+    13,255,63,0,
+    77,112,255,0,
+    141,15,215,255,
+    205,114,21,255,
+    14,255,68,0,
+    78,105,255,0,
+    142,16,210,255,
+    206,117,19,255,
+    15,255,72,0,
+    79,98,255,0,
+    143,17,206,255,
+    207,121,17,255,
+    16,255,77,0,
+    80,91,255,0,
+    144,18,202,255,
+    208,124,15,255,
+    17,255,82,0,
+    81,84,255,0,
+    145,19,197,255,
+    209,128,13,255,
+    18,255,87,0,
+    82,77,255,0,
+    146,20,193,255,
+    210,131,11,255,
+    19,255,92,0,
+    83,70,255,0,
+    147,20,189,255,
+    211,135,9,255,
+    20,255,97,0,
+    84,64,255,0,
+    148,21,185,255,
+    212,139,7,255,
+    21,255,102,0,
+    85,57,255,0,
+    149,22,180,255,
+    213,143,5,255,
+    22,255,106,0,
+    86,50,255,0,
+    150,23,176,255,
+    214,147,3,255,
+    23,255,111,0,
+    87,43,255,0,
+    151,24,172,255,
+    215,151,1,255,
+    24,255,116,0,
+    88,36,255,0,
+    152,25,168,255,
+    216,155,0,255,
+    25,255,121,0,
+    89,29,255,0,
+    153,26,164,255,
+    217,159,0,255,
+    26,255,126,0,
+    90,22,255,0,
+    154,27,160,255,
+    218,163,0,255,
+    27,255,131,0,
+    91,15,255,0,
+    155,28,156,255,
+    219,167,0,255,
+    28,255,136,0,
+    92,8,255,0,
+    156,29,152,255,
+    220,172,0,255,
+    29,255,141,0,
+    93,1,255,0,
+    157,30,148,255,
+    221,176,0,255,
+    30,255,145,0,
+    94,0,255,5,
+    158,30,144,255,
+    222,180,0,255,
+    31,255,150,0,
+    95,0,255,12,
+    159,31,140,255,
+    223,184,0,255,
+    32,255,155,0,
+    96,0,255,19,
+    160,32,136,255,
+    224,188,0,255,
+    33,255,160,0,
+    97,0,255,26,
+    161,33,133,255,
+    225,192,0,255,
+    34,255,165,0,
+    98,0,255,33,
+    162,34,129,255,
+    226,197,0,255,
+    35,255,170,0,
+    99,0,255,40,
+    163,35,125,255,
+    227,201,0,255,
+    36,255,175,0,
+    100,0,255,47,
+    164,36,122,255,
+    228,205,0,255,
+    37,255,179,0,
+    101,0,255,54,
+    165,37,118,255,
+    229,209,0,255,
+    38,255,184,0,
+    102,0,255,60,
+    166,38,114,255,
+    230,213,0,255,
+    39,255,189,0,
+    103,0,255,67,
+    167,39,111,255,
+    231,217,0,255,
+    40,255,194,0,
+    104,0,255,74,
+    168,40,107,255,
+    232,222,0,255,
+    41,255,199,0,
+    105,0,255,81,
+    169,40,104,255,
+    233,226,0,255,
+    42,255,204,0,
+    106,0,255,88,
+    170,41,100,255,
+    234,230,0,255,
+    43,255,209,0,
+    107,0,255,95,
+    171,42,97,255,
+    235,234,0,255,
+    44,255,213,0,
+    108,0,255,102,
+    172,43,93,255,
+    236,238,0,255,
+    45,255,218,0,
+    109,0,255,109,
+    173,44,90,255,
+    237,242,0,255,
+    46,255,223,0,
+    110,0,255,116,
+    174,45,87,255,
+    238,247,0,255,
+    47,255,228,0,
+    111,0,255,123,
+    175,46,84,255,
+    239,251,0,255,
+    48,255,233,0,
+    112,0,255,130,
+    176,47,80,255,
+    240,255,0,254,
+    49,255,238,0,
+    113,0,255,137,
+    177,48,77,255,
+    241,255,0,250,
+    50,255,243,0,
+    114,0,255,144,
+    178,49,74,255,
+    242,255,0,246,
+    51,255,248,0,
+    115,0,255,151,
+    179,50,71,255,
+    243,255,0,242,
+    52,255,252,0,
+    116,0,255,158,
+    180,50,68,255,
+    244,255,0,237,
+    53,252,255,0,
+    117,0,255,165,
+    181,53,66,255,
+    245,255,0,233,
+    54,247,255,0,
+    118,0,255,172,
+    182,55,64,255,
+    246,255,0,229,
+    55,242,255,0,
+    119,0,255,178,
+    183,57,62,255,
+    247,255,0,225,
+    56,237,255,0,
+    120,0,255,185,
+    184,59,60,255,
+    248,255,0,221,
+    57,232,255,0,
+    121,0,255,192,
+    185,63,61,255,
+    249,255,0,217,
+    58,227,255,0,
+    122,0,255,199,
+    186,65,59,255,
+    250,255,0,212,
+    59,223,255,0,
+    123,0,255,206,
+    187,67,57,255,
+    251,255,0,208,
+    60,218,255,0,
+    124,0,255,213,
+    188,69,55,255,
+    252,255,0,204,
+    61,213,255,0,
+    125,0,255,218,
+    189,71,53,255,
+    253,255,0,200,
+    62,208,255,0,
+    126,1,255,223,
+    190,73,51,255,
+    254,255,0,196,
+    63,203,255,0,
+    127,2,255,228,
+    191,75,49,255,
+    255,255,0,191
+  };
+
+  lut->SetNumberOfTableValues(256);
+  for(int i=0; i<256; i++)
+  {
+    int k = i*4;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutRun2( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //Run2
+  unsigned char v[256*4] =
+  {
+    0,255,89,89,
+    64,255,151,151,
+    128,255,255,255,
+    192,130,142,255,
+    1,255,90,90,
+    65,255,153,153,
+    129,253,253,255,
+    193,128,141,255,
+    2,255,91,91,
+    66,255,154,154,
+    130,251,251,255,
+    194,127,140,255,
+    3,255,92,92,
+    67,255,156,156,
+    131,249,249,255,
+    195,126,139,255,
+    4,255,93,93,
+    68,255,157,157,
+    132,247,247,255,
+    196,125,138,255,
+    5,255,94,94,
+    69,255,159,159,
+    133,245,246,255,
+    197,124,137,255,
+    6,255,95,95,
+    70,255,161,161,
+    134,243,244,255,
+    198,123,136,255,
+    7,255,96,96,
+    71,255,162,162,
+    135,241,242,255,
+    199,121,135,255,
+    8,255,97,97,
+    72,255,164,164,
+    136,239,240,255,
+    200,120,134,255,
+    9,255,97,97,
+    73,255,165,165,
+    137,237,239,255,
+    201,119,133,255,
+    10,255,98,98,
+    74,255,167,167,
+    138,235,237,255,
+    202,118,132,255,
+    11,255,99,99,
+    75,255,169,169,
+    139,233,235,255,
+    203,117,130,255,
+    12,255,100,100,
+    76,255,170,170,
+    140,231,233,255,
+    204,116,129,255,
+    13,255,101,101,
+    77,255,172,172,
+    141,229,232,255,
+    205,114,128,255,
+    14,255,102,102,
+    78,255,174,174,
+    142,227,230,255,
+    206,113,127,255,
+    15,255,103,103,
+    79,255,175,175,
+    143,225,228,255,
+    207,112,126,255,
+    16,255,104,104,
+    80,255,177,177,
+    144,223,226,255,
+    208,111,125,255,
+    17,255,105,105,
+    81,255,178,178,
+    145,221,224,255,
+    209,110,124,255,
+    18,255,106,106,
+    82,255,180,180,
+    146,219,223,255,
+    210,108,123,255,
+    19,255,107,107,
+    83,255,182,182,
+    147,217,221,255,
+    211,107,122,255,
+    20,255,108,108,
+    84,255,183,183,
+    148,215,219,255,
+    212,106,121,255,
+    21,255,109,109,
+    85,255,185,185,
+    149,213,217,255,
+    213,105,120,255,
+    22,255,110,110,
+    86,255,187,187,
+    150,211,216,255,
+    214,104,119,255,
+    23,255,111,111,
+    87,255,188,188,
+    151,209,214,255,
+    215,103,118,255,
+    24,255,112,112,
+    88,255,190,190,
+    152,207,212,255,
+    216,101,117,255,
+    25,255,113,113,
+    89,255,191,191,
+    153,205,210,255,
+    217,100,116,255,
+    26,255,114,114,
+    90,255,193,193,
+    154,203,209,255,
+    218,99,115,255,
+    27,255,115,115,
+    91,255,195,195,
+    155,201,207,255,
+    219,98,114,255,
+    28,255,116,116,
+    92,255,196,196,
+    156,200,205,255,
+    220,97,112,255,
+    29,255,117,117,
+    93,255,198,198,
+    157,198,203,255,
+    221,96,111,255,
+    30,255,118,118,
+    94,255,199,199,
+    158,196,201,255,
+    222,94,110,255,
+    31,255,119,119,
+    95,255,201,201,
+    159,194,200,255,
+    223,93,109,255,
+    32,255,120,120,
+    96,255,203,203,
+    160,192,198,255,
+    224,92,108,255,
+    33,255,121,121,
+    97,255,204,204,
+    161,190,196,255,
+    225,91,107,255,
+    34,255,122,122,
+    98,255,206,206,
+    162,188,194,255,
+    226,90,106,255,
+    35,255,123,123,
+    99,255,208,208,
+    163,186,193,255,
+    227,88,105,255,
+    36,255,124,124,
+    100,255,209,209,
+    164,184,191,255,
+    228,87,104,255,
+    37,255,125,125,
+    101,255,211,211,
+    165,182,189,255,
+    229,86,103,255,
+    38,255,126,126,
+    102,255,212,212,
+    166,180,187,255,
+    230,85,102,255,
+    39,255,127,127,
+    103,255,214,214,
+    167,178,186,255,
+    231,84,101,255,
+    40,255,128,128,
+    104,255,216,216,
+    168,176,184,255,
+    232,83,100,255,
+    41,255,129,129,
+    105,255,217,217,
+    169,174,182,255,
+    233,81,99,255,
+    42,255,130,130,
+    106,255,219,219,
+    170,172,180,255,
+    234,80,98,255,
+    43,255,131,131,
+    107,255,221,221,
+    171,170,178,255,
+    235,79,97,255,
+    44,255,131,131,
+    108,255,222,222,
+    172,168,177,255,
+    236,78,96,255,
+    45,255,132,132,
+    109,255,224,224,
+    173,166,175,255,
+    237,77,95,255,
+    46,255,133,133,
+    110,255,225,225,
+    174,164,173,255,
+    238,76,93,255,
+    47,255,134,134,
+    111,255,227,227,
+    175,162,171,255,
+    239,74,92,255,
+    48,255,135,135,
+    112,255,229,229,
+    176,160,170,255,
+    240,73,91,255,
+    49,255,136,136,
+    113,255,230,230,
+    177,158,168,255,
+    241,72,90,255,
+    50,255,137,137,
+    114,255,232,232,
+    178,156,166,255,
+    242,71,89,255,
+    51,255,138,138,
+    115,255,233,233,
+    179,154,164,255,
+    243,70,88,255,
+    52,255,139,139,
+    116,255,235,235,
+    180,152,163,255,
+    244,69,87,255,
+    53,255,140,140,
+    117,255,237,237,
+    181,150,161,255,
+    245,67,86,255,
+    54,255,141,141,
+    118,255,238,238,
+    182,148,159,255,
+    246,66,85,255,
+    55,255,142,142,
+    119,255,240,240,
+    183,146,157,255,
+    247,65,84,255,
+    56,255,143,143,
+    120,255,242,242,
+    184,145,156,255,
+    248,64,83,255,
+    57,255,144,144,
+    121,255,243,243,
+    185,143,154,255,
+    249,63,82,255,
+    58,255,145,145,
+    122,255,245,245,
+    186,141,152,255,
+    250,61,81,255,
+    59,255,146,146,
+    123,255,246,246,
+    187,139,150,255,
+    251,60,80,255,
+    60,255,147,147,
+    124,255,248,248,
+    188,137,148,255,
+    252,59,79,255,
+    61,255,148,148,
+    125,255,250,250,
+    189,135,147,255,
+    253,58,78,255,
+    62,255,149,149,
+    126,255,251,251,
+    190,133,145,255,
+    254,57,77,255,
+    63,255,150,150,
+    127,255,253,253,
+    191,131,143,255,
+    255,56,75,255
+  };
+
+
+  lut->SetNumberOfTableValues(256);
+  for(int i=0; i<256; i++)
+  {
+    int k = i*4;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutVolRenRGB( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //Run2
+  unsigned char v[4*4] =
+  {
+    128,128,128,0,
+    255,0,0,50,
+    0,255,0,100,
+    0,0,255,150,
+  };
+
+  lut->SetNumberOfTableValues(4);
+  for(int i=0; i<4; i++)
+  {
+    int k = i*4;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutVolRenTwoLev( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+  //Run2
+  unsigned char v[8*4] =
+  {
+    128,128,128,0,
+    128,128,128,0,
+
+    255,255,0,50,
+
+    128,128,128,0,
+    128,128,128,0,
+
+    255,50,0,100,
+
+    128,128,128,0,
+    128,128,128,0,
+  };
+
+
+  lut->SetNumberOfTableValues(8);
+  for(int i=0; i<8; i++)
+  {
+    int k = i*4;
+    lut->SetTableValue(i, v[k]/255.0, v[k+1]/255.0, v[k+2]/255.0, v[k+3]/255.0);
+  }
+}
+
+//---------------------------------------------------------------------
+void LutSelector::lutFile( vtkLookupTable *lut)
+//---------------------------------------------------------------------
+{
+
+}
diff --git a/Code/src/lutselector.h b/Code/src/lutselector.h
new file mode 100644
index 0000000000000000000000000000000000000000..eefa374e463bf0008b751c54e4928d62f2a77e2c
--- /dev/null
+++ b/Code/src/lutselector.h
@@ -0,0 +1,42 @@
+#ifndef LUTSELECTOR_H
+#define LUTSELECTOR_H
+
+#include "vtkLookupTable.h"
+
+class LutSelector
+{
+public:
+    LutSelector();
+    void SelectLookTable(int idx,vtkLookupTable *lut=NULL);
+    void lutVolRenGreen(vtkLookupTable *lut);
+    void lutVolRenGlow( vtkLookupTable *lut);
+    void lutTenStep( vtkLookupTable *lut);
+    void lutTemperature( vtkLookupTable *lut);
+    void lutSar( vtkLookupTable *lut);
+    void lutPhysicsContour( vtkLookupTable *lut);
+    void lutGlow( vtkLookupTable *lut);
+    void lutEField( vtkLookupTable *lut);
+    void lutDefault( vtkLookupTable *lut);
+    void lutDefaultStep( vtkLookupTable *lut);
+    void lutGray( vtkLookupTable *lut);
+    void lutRun1( vtkLookupTable *lut);
+    void lutRun2( vtkLookupTable *lut);
+    void lutPureRed( vtkLookupTable *lut);
+    void lutPureGreen( vtkLookupTable *lut);
+    void lutPureBlue( vtkLookupTable *lut);
+    void lutAllYellow( vtkLookupTable *lut);
+    void lutAllCyane( vtkLookupTable *lut);
+    void lutAllViolet( vtkLookupTable *lut);
+    void lutAllWhite( vtkLookupTable *lut);
+    void lutAllBlack( vtkLookupTable *lut);
+    void lutAllRed( vtkLookupTable *lut);
+    void lutAllGreen( vtkLookupTable *lut);
+    void lutAllBlu( vtkLookupTable *lut);
+    void lutMinMax( vtkLookupTable *lut);
+    void lutVolRenRGB( vtkLookupTable *lut);
+    void lutVolRenTwoLev( vtkLookupTable *lut);
+    void lutFile( vtkLookupTable *lut);
+
+};
+
+#endif // LUTSELECTOR_H
diff --git a/Code/src/main.cpp b/Code/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d968862d2a0178b06995d2bf6bc9acbb83b54a44
--- /dev/null
+++ b/Code/src/main.cpp
@@ -0,0 +1,24 @@
+#include "mainwindow.h"
+#include <QApplication>
+#include "singleton.h"
+#include <QSplashScreen>
+#include <QBitmap>
+#include <QDebug>
+//#include "vosamp.h"
+
+int main(int argc, char *argv[])
+{
+
+    QApplication a(argc, argv);
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+    a.setApplicationName("Vialactea - Visual Analytics client");
+
+
+ //  vosamp *samp = &Singleton<vosamp>::Instance();
+
+   // w->show();
+
+
+    return a.exec();
+
+}
diff --git a/Code/src/mainwindow.cpp b/Code/src/mainwindow.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eda269d81377f9741a31098fb3010fd18e51b183
--- /dev/null
+++ b/Code/src/mainwindow.cpp
@@ -0,0 +1,1100 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Alessandro Costa                                *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+#include <QDebug>
+#include <QFileDialog>
+#include <QTreeView>
+#include <QFile>
+#include <QFileInfo>
+#include <QThread>
+#include <QIcon>
+#include <QFont>
+#include <QFileSystemModel>
+#include "vtkwindow_new.h"
+#include "VisIVOImporterDesktop.h"
+#include "vispoint.h"
+#include "singleton.h"
+#include "vtkfitsreader.h"
+#include "vtkSmartPointer.h"
+#include "vialactea.h"
+#include "sed.h"
+//#include "vosamp.h"
+#include "visivofilterdesktop.h"
+#include "customprocess.h"
+
+
+extern "C" {
+#include "visivo.h"
+}
+
+
+#include "vtkwindow_new.h"
+
+/*
+ *  type
+ * 0 = ASCII
+ * 1 = CSV
+ * 2 = VOTABLE
+ * 3 = BINARY
+ *
+ */
+
+
+MainWindow::MainWindow(QWidget *parent)
+    : QMainWindow(parent), ui(new Ui::MainWindow)
+{
+
+
+    //m_RenderingWindow = NULL;
+    // QWidget::setWindowIcon(QIcon( ":/icons/VisIVODesktop.icns" ));
+    ui->setupUi(this);
+    createModel();
+    ui->treeView->setModel( m_VisIVOTreeModel );
+    m_VisPointsObject = new VisPoint();
+    QObject::connect(ui->actionClose, SIGNAL(triggered()), this, SLOT(close()));
+    QObject::connect(ui->actionImportAscii, SIGNAL(triggered()), this, SLOT(importAscii()));
+    QObject::connect(ui->actionImportVoTable, SIGNAL(triggered()), this, SLOT(importVoTable()));
+    QObject::connect(ui->actionImportBinary, SIGNAL(triggered()), this, SLOT(importBinary()));
+    QObject::connect(ui->actionVTK_ImageData, SIGNAL(triggered()), this, SLOT(importVTI()));
+    // QObject::connect(ui->actionVTK_PolyData, SIGNAL(TreeModeltriggered()), this, SLOT(importVTP()));
+    QDir::home().mkdir("VisIVODesktopTemp");
+    QDir tmp_download(QDir::homePath()+"/VisIVODesktopTemp/tmp_download");
+    qDebug()<<QDir::homePath()+"/VisIVODesktopTemp/tmp_download";
+    tmp_download.removeRecursively();
+    QDir::home().mkdir("VisIVODesktopTemp/tmp_download");
+
+    workingPath = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp");
+    importer= QString("/opt/VisIVOServer-vtk6/VisIVOImporter");
+    filter = QString("/opt/VisIVOServer-vtk6/VisIVOFilters");
+    util = QString("/opt/VisIVOServer/VisIVOUtils");
+
+
+    queue = &Singleton<OperationQueue>::Instance();
+
+
+    ui->importerGroupBox->hide();
+    ui->filterGroupBox->hide();
+    hideAllFilterParameter();
+
+    // ui->addIdentifierParameterGroupBox->hide();
+    // ui->appendParameterGroupBox->hide();
+
+    ui->volumeGroupBox->hide();
+    on_actionVialactea_triggered();
+
+}
+
+MainWindow::~MainWindow()
+{
+    delete ui;
+
+}
+void MainWindow::createModel()
+{
+    QStringList headerLabels;
+    headerLabels << "Item";
+    headerLabels << "Name";
+    m_VisIVOTreeModel = new TreeModel(headerLabels,"");
+    ui->tabWidget->setCurrentWidget(ui->tabObjectTree);
+}
+void MainWindow::resetInterface()
+{
+    ui->buttonUndo->setEnabled(false);
+    ui->buttonResetCameraObject->setEnabled(false);
+    ui->buttonPlot->setEnabled(false);
+    ui->buttonMergeDO->setEnabled(false);
+    ui->buttonMathOp->setEnabled(false);
+    ui->buttonDefineVector->setEnabled(false);
+    ui->buttonCreateGrid->setEnabled(false);
+    ui->buttonCreateDO->setEnabled(false);
+}
+
+void MainWindow::importAscii()
+{
+    genericImport(0);
+}
+
+void MainWindow::genericImport(int t)
+{
+    int errorCode;
+
+    fileName = QFileDialog::getOpenFileName(this,tr("Import a file"), "", tr("(*.*)"));
+    if (fileName.compare("")!=0)
+    {
+        type=t;
+        ui->tabWidget->setCurrentWidget(ui->tabOpParameters);
+        ui->importerGroupBox->show();
+        ui->filterGroupBox->hide();
+        ui->addIdentifierParameterGroupBox->hide();
+    }
+
+}
+
+
+void MainWindow::importAsciiFilaments(QString fileName, vtkwindow_new *v)
+{
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI= new VisIVOImporterDesktop(fileName,v);
+
+}
+
+void MainWindow::importAsciiBubbles(QString fileName, vtkwindow_new *v)
+{
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI= new VisIVOImporterDesktop(fileName,v,false,true);
+
+}
+
+void MainWindow::importAscii3dSelection(QString fileName, vtkwindow_new *v)
+{
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI= new VisIVOImporterDesktop(fileName,v,false);
+}
+
+
+void MainWindow::importAscii(QString fileName, QString wavelen, bool higal, bool bm, vtkwindow_new *v)
+{
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI;
+    if(higal)
+    {
+        VI = new VisIVOImporterDesktop(fileName,m_VisIVOTreeModel,bm,v,wavelen);
+    }
+    else
+    {
+        VI = new VisIVOImporterDesktop("ascii",fileName,m_VisIVOTreeModel);
+        VI->setBm(bm);
+        VI->setVtkWin(v);
+        //        ui->tabWidget->setCurrentWidget(ui->tabViewSettings);
+        //        ui->tabWidget->setCurrentWidget(ui->tabObjectTree);
+        // ui->tabWidget->setCurrentWidget(ui->tabOpParameters);
+        VI->doImport(wavelen,true);
+
+    }
+}
+
+
+void MainWindow::importCSV(QString fileName)
+{
+
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI;
+    VI = new VisIVOImporterDesktop("csv",fileName,m_VisIVOTreeModel);
+    VI->doImport("",true);
+
+
+}
+
+void MainWindow::importVOTable(QString fileName)
+{
+
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI;
+    VI = new VisIVOImporterDesktop("votable",fileName,m_VisIVOTreeModel);
+    VI->doImport("",true);
+
+
+}
+
+
+void MainWindow::importVoTable() {
+
+    genericImport(2);
+
+    /*
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Import a file"), "", tr("(*.*)"));
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI = new VisIVOImporterDesktop("votable",fileName,m_VisIVOTreeModel);
+    VI->doImport();
+    ui->tabWidget->setCurrentWidget(ui->tabViewSettings);
+    ui->tabWidget->setCurrentWidget(ui->tabObjectTree);
+    //ui->treeView->update();
+    */
+}
+
+void MainWindow::importBinary() {
+
+    genericImport(3);
+
+
+    /*Browse and choose the filename*/
+    /*
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Import a file"), "", tr("(*.*)"));
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+
+
+    VisIVOImporterDesktop *VI = new VisIVOImporterDesktop("binary",fileName,m_VisIVOTreeModel);
+    VI->doImport();
+
+    ui->tabWidget->setCurrentWidget(ui->tabViewSettings);
+    ui->tabWidget->setCurrentWidget(ui->tabObjectTree);
+    //ui->treeView->update();
+    */
+}
+
+void MainWindow::importBinaryTable(QString fileName)
+{
+
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI;
+    VI = new VisIVOImporterDesktop("binary",fileName,m_VisIVOTreeModel);
+    VI->doImport("",true);
+
+
+}
+
+void MainWindow::importVTI() {
+    /*Browse and choose the filename*/
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Import a file"), "", tr("(*.*)"));
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI = new VisIVOImporterDesktop("VTI",fileName,m_VisIVOTreeModel);
+    VI->doImport();
+
+    ui->tabWidget->setCurrentWidget(ui->tabViewSettings);
+    ui->tabWidget->setCurrentWidget(ui->tabObjectTree);
+    //ui->treeView->update();
+    // Check if the importing is OK
+    if (VI->getStatus()) return;
+    return;
+}
+
+void MainWindow::importVTP() {
+    /*Browse and choose the filename*/
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Import a file"), "", tr("(*.*)"));
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI = new VisIVOImporterDesktop("VTP",fileName,m_VisIVOTreeModel);
+    VI->doImport();
+
+    ui->tabWidget->setCurrentWidget(ui->tabViewSettings);
+    ui->tabWidget->setCurrentWidget(ui->tabObjectTree);
+    //ui->treeView->update();
+    // Check if the importing is OK
+    if (VI->getStatus()) return;
+    return;
+}
+
+void MainWindow::on_actionDatacube_triggered()
+{
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Import a file"), "", tr("(*.*)"));
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    importFitsImage(fileName);
+
+    //IMPORT DATACUBE
+}
+
+
+void MainWindow::on_actionImportFitsImage_triggered()
+{
+
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Import a file"), "", tr("(*.*)"));
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+
+    importFitsImage(fileName);
+}
+
+void MainWindow::importFitsImage(QString filename, QList<QMap<QString, QString> > elementsOnDb, QString l,QString b,QString r, QString db,QString dl, bool layer )
+{
+
+    VisIVOImporterDesktop *VI = new VisIVOImporterDesktop("FITSIMG",filename,m_VisIVOTreeModel);
+    VI->doImport();
+
+    class_l=l;
+    class_b=b;
+    class_r=r;
+    class_db=db;
+    class_dl=dl;
+
+    int rows = m_VisIVOTreeModel->rowCount()-1;
+
+    QModelIndex first = m_VisIVOTreeModel->index(rows, 0, QModelIndex());
+    ui->treeView->setCurrentIndex(first);
+    itemSelected(elementsOnDb,layer);
+
+}
+
+void MainWindow::switchTab(int index)
+{
+    switch (index){
+    case 0:break;  // tabObjectTree
+    case 1:// tabViewSettings
+    {
+        MainWindow::viewSetting();
+    }
+    case 2:break;  // tabOpParameters
+    default:
+        return;
+    }
+}
+
+TreeModel* MainWindow::getTreeModel()
+{
+    return m_VisIVOTreeModel;
+}
+
+void MainWindow::viewSetting() {
+
+    qDebug()<<"***pre.setCurrentWidget.viewSetting().MainWindow";
+    ui->tabWidget->setCurrentWidget(ui->tabViewSettings);
+    qDebug()<<"***post.setCurrentWidget.viewSetting().MainWindow";
+
+    // Retrieve the first index among the selected indexes
+    QModelIndexList list = ui->treeView->selectionModel()->selectedIndexes();
+
+    // Don't use an empty list (with no selected items)
+    // Hide the tabViewSettings widget
+    if (list.isEmpty()) {
+        //qDebug() << "MainWindow::viewSetting: No selected items";
+        ui->tabViewSettings->hide();
+        return;
+    }
+
+    QModelIndex  selectedItemIndex  = list[0];
+
+    TreeItem::TreeItemType type = m_VisIVOTreeModel->getType(selectedItemIndex);
+
+    // a TreeModel Object can return a table both for PointTable and VisualObject objects
+    m_Table= m_VisIVOTreeModel->getTable(selectedItemIndex);
+
+    // Don't use an invalid table
+    if (!m_Table->checkTable())
+    {
+        qDebug() << "MainWindow::viewSetting: Error in importing the table";
+        ui->tabViewSettings->hide();
+        return;
+    }
+
+    ui->tabViewSettings->show();
+    unsigned int numberOfFields;
+    numberOfFields = m_Table->getNumberOfColumns();
+
+    QString name = QString(m_Table->getName().c_str());
+    QStringList fields;
+
+    ui->XcomboBox->clear();
+    ui->YcomboBox->clear();
+    ui->ZcomboBox->clear();
+    ui->XVectorsComboBox->clear();
+    ui->YVectorsComboBox->clear();
+    ui->ZVectorsComboBox->clear();
+
+    switch(type){
+    case TreeItem::Null: break;
+    case TreeItem::VTI: break;
+    case TreeItem::VTP: break;
+    case TreeItem::VisualObject: {
+        m_VisPointsObject = m_VisIVOTreeModel->getVisualObject(selectedItemIndex);
+        ui->XLogCheckBox->setChecked(m_VisPointsObject->isEnabledLogX());
+        ui->YLogCheckBox->setChecked(m_VisPointsObject->isEnabledLogY());
+        ui->ZLogCheckBox->setChecked(m_VisPointsObject->isEnabledLogZ());
+
+        ui->ShowPointsCheckBox->setChecked(m_VisPointsObject->isEnabledShowPoints());
+        ui->ScaleCheckBox->setChecked(m_VisPointsObject->isEnabledScale());
+        ui->ShowAxesCheckBox->setChecked(m_VisPointsObject->isEnabledShowAxes());
+        ui->ShowBoxCheckBox->setChecked(m_VisPointsObject->isEnabledShowBox());
+        ui->ShowVectorsCheckBox->setChecked(m_VisPointsObject->isEnabledShowVectors());
+
+        ui->namelineEdit->setText(m_VisPointsObject->getName());
+        for (unsigned int i =0; i < numberOfFields;i++)
+        {
+            QString field =  QString(m_Table->getColName(i).c_str());
+            fields << field;
+            ui->XcomboBox->addItem(field);
+            ui->YcomboBox->addItem(field);
+            ui->ZcomboBox->addItem(field);
+
+            ui->XVectorsComboBox->addItem(field);
+            ui->YVectorsComboBox->addItem(field);
+            ui->ZVectorsComboBox->addItem(field);
+        }
+
+
+        ui->XcomboBox->setCurrentIndex(fields.indexOf(m_VisPointsObject->getX()));
+        ui->YcomboBox->setCurrentIndex(fields.indexOf(m_VisPointsObject->getY()));
+        ui->ZcomboBox->setCurrentIndex(fields.indexOf(m_VisPointsObject->getZ()));
+
+        ui->XVectorsComboBox->setCurrentIndex(fields.indexOf(m_VisPointsObject->getVectorX()));
+        ui->YVectorsComboBox->setCurrentIndex(fields.indexOf(m_VisPointsObject->getVectorY()));
+        ui->ZVectorsComboBox->setCurrentIndex(fields.indexOf(m_VisPointsObject->getVectorZ()));
+        // hide some controls...
+        ui->XLogCheckBox->hide();
+        ui->YLogCheckBox->hide();
+        ui->ZLogCheckBox->hide();
+
+        ui->XVectorsComboBox->hide();
+        ui->YVectorsComboBox->hide();
+        ui->ZVectorsComboBox->hide();
+
+        ui->XVectorsLabel->hide();
+        ui->YVectorsLabel->hide();
+        ui->ZVectorsLabel->hide();
+
+        ui->ShowPointsCheckBox->hide();
+
+        ui->ScaleCheckBox->hide();
+        ui->ShowAxesCheckBox->hide();
+        ui->ShowBoxCheckBox->hide();
+
+        ui->ShowVectorsCheckBox->hide();
+        // end hide some controls...
+        // end control setting case TreeItem::VisualObject
+
+    }
+    case TreeItem::VolumeTable: break;
+    case TreeItem::PointTable:
+    {
+        m_VisPointsObject = new VisPoint(m_Table);
+
+        // hide some controls...
+        ui->XLogCheckBox->hide();
+        ui->YLogCheckBox->hide();
+        ui->ZLogCheckBox->hide();
+
+        ui->XVectorsComboBox->hide();
+        ui->YVectorsComboBox->hide();
+        ui->ZVectorsComboBox->hide();
+
+        ui->XVectorsLabel->hide();
+        ui->YVectorsLabel->hide();
+        ui->ZVectorsLabel->hide();
+
+        ui->ShowPointsCheckBox->hide();
+
+        ui->ScaleCheckBox->hide();
+        ui->ShowAxesCheckBox->hide();
+        ui->ShowBoxCheckBox->hide();
+
+        ui->ShowVectorsCheckBox->hide();
+        // end hide some controls...
+
+        // control setting case TreeItem::PointTable
+        ui->namelineEdit->setText(m_VisPointsObject->getName());
+
+        for (unsigned int i =0; i < numberOfFields;i++)
+        {
+            QString field =  QString(m_Table->getColName(i).c_str());
+            fields << field;
+            ui->XcomboBox->addItem(field);
+            ui->YcomboBox->addItem(field);
+            ui->ZcomboBox->addItem(field);
+
+            ui->XVectorsComboBox->addItem(field);
+            ui->YVectorsComboBox->addItem(field);
+            ui->ZVectorsComboBox->addItem(field);
+        }
+        // Set some default values for the following ComboBox:
+        ui->XcomboBox->setCurrentIndex(0);
+        ui->YcomboBox->setCurrentIndex(1);
+        ui->ZcomboBox->setCurrentIndex(2);
+
+        ui->XVectorsComboBox->setCurrentIndex(0);
+        ui->YVectorsComboBox->setCurrentIndex(1);
+        ui->ZVectorsComboBox->setCurrentIndex(2);
+
+        ui->XLogCheckBox->setChecked(m_VisPointsObject->isEnabledLogX());
+        ui->YLogCheckBox->setChecked(m_VisPointsObject->isEnabledLogY());
+        ui->ZLogCheckBox->setChecked(m_VisPointsObject->isEnabledLogZ());
+
+        ui->ShowPointsCheckBox->setChecked(m_VisPointsObject->isEnabledShowPoints());
+        ui->ScaleCheckBox->setChecked(m_VisPointsObject->isEnabledScale());
+        ui->ShowAxesCheckBox->setChecked(m_VisPointsObject->isEnabledShowAxes());
+        ui->ShowBoxCheckBox->setChecked(m_VisPointsObject->isEnabledShowBox());
+        ui->ShowVectorsCheckBox->setChecked(m_VisPointsObject->isEnabledShowVectors());
+        // end control setting case TreeItem::PointTable
+        return;
+    }
+    default:
+        return;
+    }
+
+}
+
+void MainWindow::viewSettingOk()
+{
+    // Retrieve the first index among the selected indexes
+    QModelIndexList list = ui->treeView->selectionModel()->selectedIndexes();
+
+    // Don't use an empty list (from an empty model)
+    if (list.isEmpty()) return;
+
+    QModelIndex  selectedItemIndex  = list[0];
+
+    TreeItem::TreeItemType type = m_VisIVOTreeModel->getType(selectedItemIndex);
+
+    // a TreeModel Object can return a table both for PointTable and VisualObject objects
+    m_Table= m_VisIVOTreeModel->getTable(selectedItemIndex);
+
+    // Don't use an invalid table
+    if (!m_Table->checkTable())
+    {
+        qDebug() << "MainWindow::viewSettingOk: Error in importing the table";
+        return;
+    }
+
+
+    unsigned int numberOfFields;
+    numberOfFields = m_Table->getNumberOfColumns();
+    if (numberOfFields < 3 )
+    {
+        qDebug() << "numberOfFields < 3";
+        return;
+    }
+    QString name = QString(m_Table->getName().c_str());
+    QStringList fields;
+
+    // A brand new m_VisPointsObject is needed if type==PoinTable
+    if (type==TreeItem::PointTable)
+    {
+        m_VisPointsObject = new VisPoint(m_Table);
+    }
+    if (type==TreeItem::PointTable || type==TreeItem::VisualObject)
+    {
+
+
+        m_VisPointsObject->setLogX(ui->XLogCheckBox->isChecked());
+        m_VisPointsObject->setLogY(ui->YLogCheckBox->isChecked());
+        m_VisPointsObject->setLogZ(ui->ZLogCheckBox->isChecked());
+
+        m_VisPointsObject->setShowPoints(ui->ShowPointsCheckBox->isChecked());
+        m_VisPointsObject->setScale(ui->ScaleCheckBox->isChecked());
+        m_VisPointsObject->setShowAxes(ui->ShowAxesCheckBox->isChecked());
+        m_VisPointsObject->setShowBox(ui->ShowBoxCheckBox->isChecked());
+        m_VisPointsObject->setShowVectors(ui->ShowVectorsCheckBox->isChecked());
+
+        m_VisPointsObject->setX(ui->XcomboBox->currentText());
+        m_VisPointsObject->setY(ui->YcomboBox->currentText());
+        m_VisPointsObject->setZ(ui->ZcomboBox->currentText());
+
+        m_VisPointsObject->setVectorX(ui->XVectorsComboBox->currentText());
+        m_VisPointsObject->setVectorY(ui->YVectorsComboBox->currentText());
+        m_VisPointsObject->setVectorZ(ui->ZVectorsComboBox->currentText());
+
+        // bisogna espandere prima un elemento perchè lo slot expand(QModelIndex) fallisce
+        // mentre lo slot expandall() cancella tutta la vista dell'albero
+        // indagare please....
+        ui->treeView->expand(selectedItemIndex);
+        QModelIndex  parentIndex  = selectedItemIndex;
+
+        // If the selected item is a VisualObject you will create another Visual Object with the same parent
+        if (type==TreeItem::VisualObject) parentIndex = m_VisIVOTreeModel->parent(parentIndex);
+
+        int rows = m_VisIVOTreeModel->rowCount(parentIndex);
+        if (m_VisIVOTreeModel->insertRows(rows,1,parentIndex))
+        {
+            m_VisIVOTreeModel->setVisualObject(m_VisIVOTreeModel->index(rows,0,parentIndex),m_VisPointsObject);
+            m_VisIVOTreeModel->setData(m_VisIVOTreeModel->index(rows,0,parentIndex),QIcon( ":/icons/VBT_CLOUD.bmp" ));
+            m_VisIVOTreeModel->setData(m_VisIVOTreeModel->index(rows,1,parentIndex),"VisualObject");
+            // Set tabObjectTree as Current tab
+            //ui->tabWidget->setCurrentWidget(ui->tabObjectTree);
+        }
+        if (m_VisPointsObject->isOriginSpecified())
+        {
+            ui->tabWidget->setCurrentWidget(ui->tabObjectTree);
+            m_OldRenderingWindow = new vtkwindow_new(this, m_VisPointsObject);
+            /*
+            // We will open a single Rendering Window
+            if (m_RenderingWindow == NULL)
+            {
+                m_OldRenderingWindow = new vtkwindow(this, m_VisPointsObject);
+            }
+            else
+            {
+                m_RenderingWindow->update(m_VisIVOTreeModel);
+            }
+            */
+        }
+    }
+    /*
+QModelIndexList lista = m_VisIVOTreeModel->getActiveViewList();
+int numberOfVisualObject = lista.count();
+int b=numberOfVisualObject;
+*/
+}
+
+void MainWindow::itemSelected(QList<QMap<QString, QString> > elementsOnDb,bool layer)
+{
+    qDebug()<<"itemSelected";
+    // Retrieve the first index among the selected indexes
+    QModelIndexList list = ui->treeView->selectionModel()->selectedIndexes();
+
+    // Don't use an empty list (with no selected items)
+    // Hide the tabViewSettings widget
+    if (list.isEmpty()) {
+        //qDebug() << "MainWindow::viewSetting: No selected items";
+        ui->tabViewSettings->hide();
+        return;
+    }
+
+    QModelIndex  selectedItemIndex  = list[0];
+    TreeItem::TreeItemType type = m_VisIVOTreeModel->getType(selectedItemIndex);
+
+    if(type == TreeItem::FITSIMAGE )
+    {
+        qDebug()<<"mainwindow::itemSelected layer: "<<layer;
+        if(layer)
+        {
+            qDebug()<<"*******++ "<<survey<<" "<<species<<" "<<transition;
+
+            myCallingVtkWindow->addLayerImage(m_VisIVOTreeModel->getFITSIMG(selectedItemIndex),survey,species,transition);
+        }
+        else
+        {
+            vtkSmartPointer<vtkFitsReader> fitsreader= m_VisIVOTreeModel->getFITSIMG(selectedItemIndex);
+            fitsreader->setSurvey(survey);
+            fitsreader->setSpecies(species);
+            fitsreader->setTransition(transition);
+
+            qDebug()<<"*******++ "<<survey<<" "<<species<<" "<<transition;
+
+            m_OldRenderingWindow = new vtkwindow_new(this, m_VisIVOTreeModel->getFITSIMG(selectedItemIndex));
+
+            if (elementsOnDb.size()>0)
+            {
+                m_OldRenderingWindow->setDbElements(elementsOnDb);
+                m_OldRenderingWindow->setCallingL(class_l);
+                m_OldRenderingWindow->setCallingB(class_b);
+                m_OldRenderingWindow->setCallingR(class_r);
+                m_OldRenderingWindow->setCallingDl(class_dl);
+                m_OldRenderingWindow->setCallingDb(class_db);
+            }
+            if (selectedSurvey.size()>1)
+            {
+
+                m_OldRenderingWindow->setCallingL(class_l);
+                m_OldRenderingWindow->setCallingB(class_b);
+                m_OldRenderingWindow->setCallingR(class_r);
+                m_OldRenderingWindow->setCallingDl(class_dl);
+                m_OldRenderingWindow->setCallingDb(class_db);
+                m_OldRenderingWindow->downloadStartingLayers(selectedSurvey);
+            }
+
+        }
+
+    }
+    //IF IS NOT FITS IMAGE
+    else
+    {
+        m_Table= m_VisIVOTreeModel->getTable(selectedItemIndex);
+
+
+        // Don't use an invalid table
+        if (!m_Table->checkTable())
+        {
+            ui->tabViewSettings->hide();
+            return;
+        }
+
+        unsigned int numberOfElements;
+        unsigned int numberOfFields;
+        numberOfFields = m_Table->getNumberOfColumns();
+        numberOfElements = m_Table->getNumberOfRows();
+
+        QString name = QString(m_Table->getName().c_str());
+
+        QStringList fields;
+        ui->fieldTable->clear();
+        ui->fieldTable->setRowCount(0);
+        ui->fieldTable->setColumnCount(0);
+
+        ui->elementLabel->clear();
+        ui->fieldLabel->clear();
+        ui->nameLabel->clear();
+        ui->typeLabel->clear();
+
+        ui->valueNameLabel->clear();
+        ui->valueTypeLabel->clear();
+        ui->valueFieldLabel->clear();
+        ui->valueElementLabel->clear();
+
+        ui->buttonUndo->setEnabled(false);
+        ui->buttonResetCameraAll->setEnabled(false);
+        ui->buttonResetCameraObject->setEnabled(false);
+        ui->buttonCreateDO->setEnabled(false);
+        ui->buttonFilter->setEnabled(false);
+        ui->buttonCreateGrid->setEnabled(false);
+        ui->buttonDefineVector->setEnabled(false);
+        ui->buttonMathOp->setEnabled(false);
+        ui->buttonPlot->setEnabled(false);
+        ui->buttonMergeDO->setEnabled(false);
+
+
+        switch(type){
+        case TreeItem::Null: break;
+        case TreeItem::VTI: break;
+        case TreeItem::VTP: break;
+        case TreeItem::VisualObject:
+        {
+
+            ui->typeLabel->setText("Type");
+            ui->nameLabel_2->setText("Name");
+
+            ui->valueNameLabel->setText(name);
+            ui->valueTypeLabel->setText("Visual Object");
+        }
+        case TreeItem::VolumeTable: break;
+        case TreeItem::PointTable:
+        {
+
+            ui->buttonCreateDO->setEnabled(true);
+            ui->buttonFilter->setEnabled(true);
+
+            ui->fieldTable->setColumnCount(3);
+            QStringList headers;
+            headers << "Field Name" << "Min" << "Max";
+
+            ui->fieldTable->setHorizontalHeaderLabels(headers);
+
+            for (unsigned int i =0; i < numberOfFields;i++)
+            {
+
+                QString field =  QString(m_Table->getColName(i).c_str());
+                fields << field;
+
+                ui->fieldTable->insertRow(i);
+
+                QTableWidgetItem *nameItem= new QTableWidgetItem(field);
+                nameItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
+                ui->fieldTable->setItem(i,0, nameItem);
+
+                float *range = new float[2];
+                m_Table->getRange(i,range);
+                QTableWidgetItem *minItem= new QTableWidgetItem(QString::number(range[0]));
+                minItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
+
+                ui->fieldTable->setItem(i,1, minItem);
+
+                QTableWidgetItem *maxItem= new QTableWidgetItem(QString::number(range[1]));
+                maxItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
+                ui->fieldTable->setItem(i,2, maxItem);
+            }
+
+
+            ui->typeLabel->setText("Type");
+            ui->nameLabel_2->setText("Name");
+            ui->elementLabel->setText("Elements");
+            ui->fieldLabel->setText("Fields");
+
+            ui->valueNameLabel->setText(name);
+            ui->valueTypeLabel->setText("Point Table");
+            ui->valueFieldLabel->setText(QString::number(numberOfFields));
+            ui->valueElementLabel->setText(QString::number(numberOfElements));
+
+        }
+        default:return;
+        }
+
+    }
+
+
+}
+
+void MainWindow::on_actionOperation_queue_triggered()
+{
+    queue->show();
+}
+
+void MainWindow::on_actionHi_Gal_triggered()
+{
+
+    //HiGal *higalWin = &Singleton<HiGal>::Instance();
+    //higalWin->show();
+
+}
+
+void MainWindow::on_actionVialactea_triggered()
+{
+    ViaLactea *vialactealWin = &Singleton<ViaLactea>::Instance();
+ //   vialactealWin->showMaximized();
+
+    vialactealWin->show();
+
+}
+
+void MainWindow::on_actionCsv_triggered()
+{
+
+    genericImport(1);
+
+
+    /*Browse and choose the filename*/
+    /*
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Import a file"), "", tr("(*.*)"));
+    // It Checks for an empty filename
+    if (fileName.isEmpty()) return;
+    VisIVOImporterDesktop *VI = new VisIVOImporterDesktop("csv",fileName,m_VisIVOTreeModel);
+    VI->doImport();
+    ui->tabWidget->setCurrentWidget(ui->tabViewSettings);
+    ui->tabWidget->setCurrentWidget(ui->tabObjectTree);
+    //ui->treeView->update();
+    */
+}
+
+void MainWindow::on_actionTEST_DC3D_triggered()
+{
+    
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Import a file"), "", tr("(*.*)"));
+    
+    if (!fileName.isEmpty() )
+    {
+
+        vtkSmartPointer<vtkFitsReader> fitsReader = vtkSmartPointer<vtkFitsReader>::New();
+        fitsReader->is3D=true;
+        fitsReader->SetFileName(fileName.toStdString());
+        fitsReader->GetOutput();
+
+        //TEST
+        //new vtkwindow(this,fitsReader,1);
+        vtkwindow_new *nn=new vtkwindow_new(this,fitsReader,1);
+        //  nn->showNormal();
+        //END TEST
+
+    }
+    
+}
+
+void MainWindow::importFitsDC(QString fileName)
+{
+    vtkSmartPointer<vtkFitsReader> fitsReader = vtkSmartPointer<vtkFitsReader>::New();
+    fitsReader->is3D=true;
+    fitsReader->SetFileName(fileName.toStdString());
+    // fitsReader->GetOutput();
+
+    //TEST
+    //new vtkwindow(this,fitsReader,1);
+    new vtkwindow_new(this,fitsReader,1,myCallingVtkWindow);
+}
+
+void   MainWindow::closeEvent(QCloseEvent*)
+{
+    qDebug()<<"close";
+
+    // vosamp *samp = &Singleton<vosamp>::Instance();
+    // samp->unregister();
+
+    qApp->quit();
+}
+
+void MainWindow::on_volumeRadioButton_toggled(bool checked)
+{
+    if (checked)
+    {
+        ui->volumeGroupBox->show();
+    }
+    else
+        ui->volumeGroupBox->hide();
+
+}
+
+void MainWindow::on_importPushButton_clicked()
+{
+    qDebug()<<"filename: "<<fileName;
+    qDebug()<<"type: "<<type;
+    int errorCode;
+
+    switch (type) {
+    case 0:
+        importAscii(fileName);
+        break;
+    case 1:
+        importCSV(fileName);
+        break;
+    case 2:
+        importVOTable(fileName);
+        break;
+    case 3:
+        importBinaryTable(fileName);
+        break;
+    default:
+        break;
+    }
+
+
+    VisIVOImporter envVI1;
+    errorCode=VI_SetAtt(&envVI1,VI_SET_FFORMAT,"ascii");
+    errorCode=VI_SetAtt(&envVI1,VI_SET_FILEPATH, fileName.toLocal8Bit().data());
+
+    QFileInfo infoFile = QFileInfo(fileName);
+
+
+    QString outputPathString=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/"+infoFile.fileName();
+    char *outputPath= outputPathString.toLocal8Bit().data();
+    errorCode=VI_SetAtt(&envVI1,VI_SET_OUTFILEVBT,outputPath);
+
+    VI_Import(&envVI1);
+
+
+}
+
+void MainWindow::on_buttonFilter_clicked()
+{
+    ui->importerGroupBox->hide();
+    ui->filterGroupBox->show();
+    ui->addIdentifierParameterGroupBox->show();
+
+    ui->tabWidget->setCurrentWidget(ui->tabOpParameters);
+
+    QModelIndex  selectedItemIndex  = ui->treeView->selectionModel()->selectedIndexes()[0];
+
+
+    selectedFile=QString::fromStdString( m_VisIVOTreeModel->getTable(selectedItemIndex)->getLocator() );
+
+}
+
+void MainWindow::on_selectFilterComboBox_currentIndexChanged(int index)
+{
+    hideAllFilterParameter();
+    switch (index) {
+    //add identifier
+    case 0:
+        ui->filterDescriptionTextBrowser->setText("This filter adds a new column to the input table. The column name is given in Column Name field, with a sequence of value starting from a Start Number value (Default value is 0).");
+        ui->addIdentifierParameterGroupBox->show();
+        break;
+        //Append
+    case 1:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a new table appending data from a list of selected tables. The selected Tables must contain the same numbers of columns.");
+
+        // add vbt on left
+        for(int i=0; i<m_VisIVOTreeModel->rowCount();i++)
+        {
+            QModelIndex itemIndex  = ui->treeView->model()->index(i,0);
+            TreeItem::TreeItemType type = m_VisIVOTreeModel->getType(itemIndex);
+            int colNum=m_VisIVOTreeModel->getTable(itemIndex)->getNumberOfColumns();
+            qDebug()<<"type: "<<type <<" colNum: "<<colNum<<" oriColNum: "<< m_VisIVOTreeModel->getTable(ui->treeView->selectionModel()->selectedIndexes()[0])->getNumberOfColumns();
+            if(type == TreeItem::PointTable && colNum== m_VisIVOTreeModel->getTable(ui->treeView->selectionModel()->selectedIndexes()[0])->getNumberOfColumns() )
+            {
+                qDebug()<<"stica :"<<i;
+            }
+
+        }
+
+        ui->selectedVBT->setText(selectedFile);
+        ui->appendParameterGroupBox->show();
+        break;
+        //Cartesian 2 Polar
+    case 2:
+        ui->filterDescriptionTextBrowser->setText("This filter creates creates three new columns with names given in Column name for fields as the result of the spherical polar transformation of three existing columns given in Columns for point coordinates fields. A new table is created if the New Table option is selected.");
+        break;
+        //cut
+    case 3:
+        ui->filterDescriptionTextBrowser->setText("This filter fixes to a given threshold all the column values included in an interval. Limits of the interval on all selected fields can combined with logic AND/OR operator.The 'unlimited' word can be used to indicate the infinite value.");
+        break;
+        //Decimator
+    case 4:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a subtable as a regular subsample from the input table. Values are extracted in a regular sequence, skipping step element every time. The skip value is an integer number > 1 and represents the number of skipped values.");
+        break;
+        //Extract List
+    case 5:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a new table from an input table with the elements (rows) listed in a given multi-list.");
+        break;
+        //Extraction
+    case 6:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a new table from a sub-box or from a sphere in a 3D space. Three fields must be selected and the value must be given as follow: Sphere: sphere center coordinates Box: rectangular region center coordinates Corner: lower corner region coordinates. The Size field represent the sub volume dimension.");
+        break;
+        //Grid 2 Point
+    case 7:
+        ui->filterDescriptionTextBrowser->setText("This operation distributes a volume property to a point data set on the same computational domain using a field distribution (CIC/NGP/TSC algorithm) on a regular mesh. CIC is the default adopted algorithm. The Cell geometry is considered only to compute the cell volume value in this operation. This filter produces a new table or adds a new field to the input table.");
+        break;
+        //Include
+    case 8:
+        ui->filterDescriptionTextBrowser->setText("This operation assigns a different offset value to points inside and/or outside a sphere in the domain. This filter produces a new table or adds a new field to the input table. Points inside the sphere (given with center and radius) will have the value invalue, otherwise outvalue.");
+        break;
+        //Math Operation
+    case 9:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a new field in a new table or add a new field as the result of a mathematical operation between the existing fields.");
+        break;
+        //Merge
+    case 10:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a new table from two or more existing data tables. The user must select tables to be merged The filter produces a new table having the size of the smallest table or a new table having the size of the greatest table. In this case the PAD field value is used to pad the table rows of the smallest table.");
+        break;
+        //Module
+    case 11:
+        ui->filterDescriptionTextBrowser->setText("This operation computes the module of three fields (x, y, z) of the input data table sqrt(x^2+y^2+z^2).");
+        break;
+        //Multi Resolution
+    case 12:
+        ui->filterDescriptionTextBrowser->setText("This operation creates a new VBT as a random subsample from the input table, with different resolutions. Starting from a fixed position, that represent the center of inner sphere, concentric spheres are considered. Different level of randomization can be given, creating more detail table in the inner sphere and lower detail in the outer regions, or vice versa. The region that is external to the last sphere represents the background.");
+        break;
+        //Point distribute
+    case 13:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a volume using a field distribution, CIC (default) or NGP or TSC algorithm, on a regular mesh. The filter produces (by default) a density field: the field is distributed and divided for the cell volume.");
+        break;
+        //Point Property
+    case 14:
+        ui->filterDescriptionTextBrowser->setText("This operation assigns a property to each data point on the table. The operation performs the following: 1) It creates a temporary volume using a field distribution (CIC algorithm) on a regular mesh. 2) It computes, with the same CIC algorithm, the property for each data point, considering the cells where the point is spread on the volume; 3) It saves the property in a new table or adds the field to the original input table.");
+        break;
+        //Randomizer
+    case 15:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a new table as a random subsample from the input table. Percentage (from 0.0 to 95.0) of the input file obtained in the output file. Note: only the first decimal place is considered.");
+        break;
+        //Scalar distribution
+    case 16:
+        ui->filterDescriptionTextBrowser->setText("This filter computes average, min and max value of fields and creates an histogram of selected fields.");
+        break;
+        //Select Columns
+    case 17:
+        ui->filterDescriptionTextBrowser->setText("This filter creates e new table using one or more fields of a data table. 'Extract' produces the output table including only the selected fields, 'Remove' produces the output table excluding the selected fields.");
+        break;
+        //Select Rows
+    case 18:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a new table setting limits on one or more fields of a the table. Limits on all selected can combined with logic AND/OR operator.The 'unlimited' word can be used to indicate the infinite value.");
+        break;
+        //Sigma Contours
+    case 19:
+        ui->filterDescriptionTextBrowser->setText("This filter creates a new table where one or more fields of a data table have values within (or outside) N sigma contours. The filter can be applied on fields that have a Gaussian distribution.");
+        break;
+        //Split tables
+    case 20:
+        ui->filterDescriptionTextBrowser->setText("This filter divides an existing table into two or more tables, using a field that will be used to divide the table. In case of Volume the split direction [1-3] must be specified.");
+        break;
+        //Write VO Table
+    case 21:
+        ui->filterDescriptionTextBrowser->setText("This filter produces a VOTable 1.2 with the selected fields.");
+        break;
+    default:
+        break;
+    }
+}
+
+
+void MainWindow::hideAllFilterParameter()
+{
+
+    ui->addIdentifierParameterGroupBox->hide();
+    ui->appendParameterGroupBox->hide();
+
+}
+
+void MainWindow::on_runFilterPushButton_clicked()
+{
+    VisIVOFilterDesktop::runFilter(ui->selectFilterComboBox->currentIndex());
+}
diff --git a/Code/src/mainwindow.h b/Code/src/mainwindow.h
new file mode 100644
index 0000000000000000000000000000000000000000..de0e0a94f625b38041f9d64002a334add28c6614
--- /dev/null
+++ b/Code/src/mainwindow.h
@@ -0,0 +1,142 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Alessandro Costa                                *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+/**
+        @author Alessandro Costa <alessandro.costa@oact.inaf.it>
+*/
+
+#include <QMainWindow>
+#include <QProcess>
+#include "ui_mainwindow.h"
+#include <QFile>
+#include <QString>
+#include <QDir>
+#include <map>
+#include "vstabledesktop.h"
+#include "treemodel.h"
+#include "treeitem.h"
+#include "vispoint.h"
+#include "operationqueue.h"
+
+class vtkwindow_new;
+
+namespace Ui
+{
+    class MainWindow;
+}
+
+class MainWindow : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    MainWindow(QWidget *parent = 0);
+    ~MainWindow();
+    QString workingPath;
+    QString importer;
+    QString filter;
+    QString util;
+    void setCallingVtkWindow(vtkwindow_new *v){myCallingVtkWindow=v;}
+    Ui::MainWindow *ui;
+    QString selectedFile;
+    TreeModel *m_VisIVOTreeModel;
+
+public slots:
+    void importAscii();
+    void importAscii(QString fileName, QString wavelen="", bool higal=false, bool bm=false, vtkwindow_new *v=NULL);
+    void importAsciiFilaments(QString fileName, vtkwindow_new *v);
+    void importAsciiBubbles(QString fileName, vtkwindow_new *v);
+    void importAscii3dSelection(QString fileName, vtkwindow_new *v);
+    void genericImport(int t);
+    void importCSV(QString fileName);
+    void importVOTable(QString fileName);
+    void importBinaryTable(QString fileName);
+    void setSelectedSurveyMap(QList < QPair<QString, QString> > s){selectedSurvey=s;}
+
+
+    void importBinary();
+    void importVoTable();
+    void importVTP();
+    void importVTI();
+    void viewSetting();
+    void switchTab(int index );
+    void viewSettingOk();
+    void itemSelected(QList<QMap<QString, QString> > elementsOnDb=QList<QMap<QString, QString> >(), bool layer=false);
+    void importFitsImage(QString filename, QList<QMap<QString, QString> > elementsOnDb=QList<QMap<QString, QString> >(), QString l="", QString b="", QString r="", QString db="", QString dl="", bool layer=false);
+    TreeModel* getTreeModel();
+    vtkwindow_new* getLastVtkWin(){return m_OldRenderingWindow;}
+    void on_actionTEST_DC3D_triggered();
+    void importFitsDC(QString fileName);
+
+    void setSurvey(QString s){survey=s;}
+    void setSpecies(QString s){species=s;}
+    void setTransition(QString t){transition=t;}
+
+private slots:
+    void on_actionOperation_queue_triggered();
+    void on_actionHi_Gal_triggered();
+    void on_actionImportFitsImage_triggered();
+    void on_actionVialactea_triggered();
+    void on_actionDatacube_triggered();
+    void on_actionCsv_triggered();
+
+    void on_volumeRadioButton_toggled(bool checked);
+
+    void on_importPushButton_clicked();
+
+    void on_buttonFilter_clicked();
+
+    void on_selectFilterComboBox_currentIndexChanged(int index);
+
+    void on_runFilterPushButton_clicked();
+
+private:
+    vtkwindow_new * m_OldRenderingWindow;
+    void createModel();
+    void resetInterface();
+    void hideAllFilterParameter();
+
+    //QList <VisPoint> m_VisPointsObjectList;
+    VisPoint *m_VisPointsObject;
+    VSTableDesktop * m_Table;
+    QStandardItem *m_ParentItem;
+    OperationQueue *queue;
+    bool higal;
+    QString class_l;
+    QString class_b;
+    QString class_r;
+    QString class_dl;
+    QString class_db;
+    vtkwindow_new *myCallingVtkWindow;
+    QString fileName;
+    QString survey,species,transition;
+    int type;
+    QList < QPair<QString, QString> > selectedSurvey;
+
+
+protected:
+    void  closeEvent(QCloseEvent*);
+
+
+};
+
+#endif // MAINWINDOW_H
diff --git a/Code/src/observedobject.cpp b/Code/src/observedobject.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4c67c45b150eac6eb1c2ad06562bbc44dbc9e605
--- /dev/null
+++ b/Code/src/observedobject.cpp
@@ -0,0 +1,44 @@
+#include "observedobject.h"
+#include"treeitem.h"
+
+ObservedObject::ObservedObject()
+{
+    m_OriginType = TreeItem::Null;
+    setShowAxes(true);
+    setShowBox(true);
+    setName("Visual Object");
+    m_isSetOrigin=false;
+    m_OriginType = TreeItem::TreeItemType(NULL);
+}
+void ObservedObject::setShowAxes(bool value)
+{
+    m_ShowAxes=value;
+}
+void ObservedObject::setShowBox(bool value)
+{
+    m_ShowBox=value;
+}
+void ObservedObject::setName(QString name)
+{
+    m_Name=name;
+}
+bool ObservedObject::isOriginSpecified()
+{
+    return  m_isSetOrigin;
+}
+bool ObservedObject::isVisible()
+{
+    return m_isVisible;
+}
+QString ObservedObject::getName()
+{
+    return m_Name;
+}
+bool ObservedObject::isEnabledShowAxes()
+{
+    return m_ShowAxes;
+}
+bool ObservedObject::isEnabledShowBox()
+{
+    return m_ShowBox;
+}
diff --git a/Code/src/observedobject.h b/Code/src/observedobject.h
new file mode 100644
index 0000000000000000000000000000000000000000..cdfb95ae8f5b7e4863440054795228009fb774ff
--- /dev/null
+++ b/Code/src/observedobject.h
@@ -0,0 +1,29 @@
+#ifndef OBSERVEDOBJECT_H
+#define OBSERVEDOBJECT_H
+#include"treeitem.h"
+
+class ObservedObject
+{
+
+
+public:
+    ObservedObject();
+    QString m_Name;
+    bool m_isVisible;
+    bool m_isSetOrigin;
+    bool m_ShowAxes;
+    bool m_ShowBox;
+    TreeItem::TreeItemType m_OriginType;
+    bool isOriginSpecified();
+    bool isVisible();
+    void setName(QString name);
+    TreeItem::TreeItemType getOriginType();
+    void setShowAxes(bool value);
+    void setShowBox(bool value);
+
+    QString getName();
+    bool isEnabledShowBox();
+    bool isEnabledShowAxes();
+};
+
+#endif // OBSERVEDOBJECT_H
diff --git a/Code/src/operationqueue.cpp b/Code/src/operationqueue.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..120fe21285b23f9a26305f9a1135dd2b5a94dc29
--- /dev/null
+++ b/Code/src/operationqueue.cpp
@@ -0,0 +1,49 @@
+#include "operationqueue.h"
+#include "ui_operationqueue.h"
+#include "qdebug.h"
+
+OperationQueue::OperationQueue(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::OperationQueue)
+{
+    ui->setupUi(this);
+
+    QHeaderView* header = ui->tableWidget->horizontalHeader();
+    header->setVisible(true);
+    header->sectionResizeMode(QHeaderView::Stretch);
+}
+
+OperationQueue::~OperationQueue()
+{
+    delete ui;
+}
+
+int OperationQueue::addOperation(QString name)
+{
+
+    int id= ui->tableWidget->rowCount();
+    ui->tableWidget->insertRow(id);
+
+    QTableWidgetItem* idItem = new QTableWidgetItem(QString::number(id));
+    QTableWidgetItem* nameItem = new QTableWidgetItem(name);
+    QTableWidgetItem* statusItem = new QTableWidgetItem("in progress...");
+    ui->tableWidget->setItem(id, 0, idItem);
+    ui->tableWidget->setItem(id, 1, nameItem);
+    ui->tableWidget->setItem(id, 2, statusItem);
+
+    QApplication::processEvents();
+
+
+    return id;
+
+}
+
+void OperationQueue::editOperation(int id, QString status)
+{
+
+    QTableWidgetItem* statusItem = new QTableWidgetItem(status);
+
+    ui->tableWidget->setItem(id,2, statusItem);
+    QApplication::processEvents();
+
+}
diff --git a/Code/src/operationqueue.h b/Code/src/operationqueue.h
new file mode 100644
index 0000000000000000000000000000000000000000..90a0387d0233ec73dfaf23bdcfd7b257d4a4cd98
--- /dev/null
+++ b/Code/src/operationqueue.h
@@ -0,0 +1,26 @@
+#ifndef OPERATIONQUEUE_H
+#define OPERATIONQUEUE_H
+
+#include <QWidget>
+
+namespace Ui {
+class OperationQueue;
+}
+
+class OperationQueue : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit OperationQueue(QWidget *parent = 0);
+    ~OperationQueue();
+    int addOperation(QString name);
+    void editOperation(int id, QString status);
+
+
+private:
+    Ui::OperationQueue *ui;
+
+};
+
+#endif // OPERATIONQUEUE_H
diff --git a/Code/src/osxhelper.h b/Code/src/osxhelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..15d7ee52e75bdf655e956713d278d6c8bb4561fc
--- /dev/null
+++ b/Code/src/osxhelper.h
@@ -0,0 +1,5 @@
+#ifndef _OSX_HELPER_
+#define _OSX_HELPER_
+
+void disableGLHiDPI( long a_id );
+#endif
diff --git a/Code/src/osxhelper.mm b/Code/src/osxhelper.mm
new file mode 100644
index 0000000000000000000000000000000000000000..409420a00b26a325d6a9cf140e6e8516603ca617
--- /dev/null
+++ b/Code/src/osxhelper.mm
@@ -0,0 +1,7 @@
+#include <Cocoa/Cocoa.h>
+#include "osxHelper.h"
+
+void disableGLHiDPI( long a_id ){
+    NSView* view = reinterpret_cast<NSView*>( a_id );
+    [view setWantsBestResolutionOpenGLSurface:NO];
+}
diff --git a/Code/src/pipe.cpp b/Code/src/pipe.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7ca46ca51ede408a9926d97be531928f5599a905
--- /dev/null
+++ b/Code/src/pipe.cpp
@@ -0,0 +1,586 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+ *   ugo.becciani@oact.inaf.it                                             *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include <sstream>
+#include <cstdlib>
+#include <cstring>
+#include <map>
+#include <QStringList>
+#include <QString>
+#include "pipe.h"
+#include "visivoutilsdesktop.h"
+#include "vstabledesktop.h"
+#include "vtkWindowToImageFilter.h"
+#include "vtkPNGWriter.h"
+#include "vtkRenderWindow.h"
+#include "vtkCamera.h"
+#include "vtkRenderer.h"
+#include "vtkLookupTable.h"
+#include "vtkPolyData.h"
+
+#include "vtkOutlineCornerFilter.h"
+#include "vtkProperty.h"
+#include "vtkScalarBarActor.h"
+
+#include "vtkCubeAxesActor2D.h"
+#include "vtkDataSet.h"
+#include "vstabledesktop.h"
+#include "vtkLODActor.h"
+
+#include "qdebug.h"
+#include "vtkwindow_new.h"
+#include "vispoint.h"
+
+#include "vtkPolygon.h"
+
+Pipe::Pipe(VSTableDesktop *table)
+{
+    m_VSTable = table;
+}
+
+VSTableDesktop* Pipe::getTable()
+{
+    return m_VSTable;
+}
+
+void Pipe::constructVTK(vtkwindow_new *v)
+{
+    m_lut           = vtkLookupTable::New();
+    vtkwin=v;
+    // m_pRenderer     = vtkRenderer::New();
+    //  m_pRenderWindow = vtkRenderWindow::New();
+    m_pRenderer = vtkwin -> m_Ren1;
+    m_pRenderWindow = vtkwin -> renwin;
+
+
+
+}
+
+int Pipe::createPipe ()
+
+{
+    return 0;
+}
+
+int  Pipe::getCamera ()
+
+{
+    return 0;
+}
+
+/*
+bool Pipe::readData (QStringList list)
+{
+    std::ifstream inFile;
+    m_path=m_VSTable->getLocator();
+
+    inFile.open(m_path.c_str(), ios::binary);
+    if(!inFile.good())
+    {
+        std::cerr<<"Error: binary data does not exist"<<std::endl;
+        exit(1);
+    }
+
+    //--------------------------------------------------------------------------
+    // m_fieldNames is a Vector of strings containing field names
+    //--------------------------------------------------------------------------
+
+    m_fieldNames.push_back(list[0].toStdString().c_str());
+    m_fieldNames.push_back(list[1].toStdString().c_str());
+    m_fieldNames.push_back(list[2].toStdString().c_str());
+
+
+    m_nRows= m_VSTable->getNumberOfRows();
+    m_nCols=0;
+
+    //--------------------------------------------------------------------------
+    // m_colNames is a  map with valid fieldNames & corresponding ColID
+    //--------------------------------------------------------------------------
+    for (int i = 0; i < m_fieldNames.size(); i++)
+    {
+        if (m_VSTable->getColId(m_fieldNames[i])>=0)
+        {
+            m_colNames.insert(make_pair(m_fieldNames[i],m_nCols));
+            m_nCols++; // m_nCols will be  m_colNames.size()
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    // colList is an array containing indexes of the m_colNames map
+    //--------------------------------------------------------------------------
+    unsigned int *colList;
+    colList = new unsigned int [m_nCols];
+    int count=0;
+    for (int i = 0; i < m_fieldNames.size(); i++)
+    {
+        if (m_VSTable->getColId(m_fieldNames[i])>=0)
+        {
+            colList[count]=m_VSTable->getColId(m_fieldNames[i]);
+            count++;
+        }
+    }
+
+    //--------------------------------------------------------------------------
+    // m_tableData is a bidimens. float array m_nCols*m_nRows
+    //--------------------------------------------------------------------------
+    m_tableData = new float* [m_nCols];
+    for (int i = 0; i < m_nCols; i++)
+
+    {
+        try
+        {
+            m_tableData[i]=new  float[m_nRows];
+        }
+        catch(std::bad_alloc &e)
+        {
+            m_tableData[i]=NULL;
+        }
+        if(m_tableData[i]==NULL)
+        {
+            for(unsigned int j=0;j<i;j++) delete [] m_tableData[j];
+            delete [] m_tableData;
+            return false;
+        }
+    }
+
+ //int VSTable::getColumn(unsigned int *colList, unsigned int nOfCol, unsigned long long int fromRow, unsigned long long int toRow, float **fArray)
+
+    //--------------------------------------------------------------------------
+    // VSTable::getColumn reads the data
+    //--------------------------------------------------------------------------
+    m_VSTable->getColumn(colList,m_nCols,0,m_nRows-1, m_tableData);
+    //delete table;
+    return true;
+}
+*/
+
+bool Pipe::readData (QStringList list)
+{
+    //  std::ifstream inFile;
+    m_path=m_VSTable->getLocator();
+    /*
+    inFile.open(m_path.c_str(), ios::binary);
+    if(!inFile.good())
+    {
+        std::cerr<<"Error: binary data does not exist"<<std::endl;
+        exit(1);
+    }
+*/
+    //--------------------------------------------------------------------------
+    // m_fieldNames is a Vector of strings containing field names
+    //--------------------------------------------------------------------------
+
+
+    m_fieldNames.push_back(list[0].toStdString().c_str());
+    m_fieldNames.push_back(list[1].toStdString().c_str());
+    m_fieldNames.push_back(list[2].toStdString().c_str());
+
+    m_nRows= m_VSTable->getNumberOfRows();
+    m_nCols= m_VSTable->getNumberOfColumns();
+
+    qDebug()<<"m_nCols="<<m_nCols<<" m_nRows: "<<m_nRows;
+    qDebug()<<list[0];
+    qDebug()<<list[1];
+    qDebug()<<list[2];
+
+    //  unsigned int *colList;
+    //  colList = new unsigned int [m_nCols];
+    /*
+    //--------------------------------------------------------------------------
+    // m_colNames is a  map with valid fieldNames & corresponding ColID
+    //--------------------------------------------------------------------------
+    for (int i = 0; i < m_nCols; i++)
+    {
+
+        m_colNames.insert(make_pair(m_VSTable->getColName(i),i));
+
+        colList[i]=m_VSTable->getColId(m_VSTable->getColName(i));
+        //qDebug()<<"i="<<i<<" collist[]="<<colList[i]<<m_VSTable->getColName(i).c_str();
+
+    }
+*/
+
+    //--------------------------------------------------------------------------
+    // colList is an array containing indexes of the m_colNames map
+    //--------------------------------------------------------------------------
+    unsigned int *colList;
+    colList = new unsigned int [m_nCols];
+    int count=0;
+    for (int i = 0; i < m_fieldNames.size(); i++)
+    {
+        if (m_VSTable->getColId(m_fieldNames[i])>=0)
+        {
+            m_colNames.insert(make_pair(m_VSTable->getColName(i),i));
+
+            colList[count]=m_VSTable->getColId(m_fieldNames[i]);
+            count++;
+        }
+    }
+
+
+    //--------------------------------------------------------------------------
+    // m_tableData is a bidimens. float array m_nCols*m_nRows
+    //--------------------------------------------------------------------------
+    m_tableData = new float* [m_nCols];
+    for (int i = 0; i < m_nCols; i++)
+    {
+        try
+        {
+            m_tableData[i]=new  float[m_nRows];
+        }
+        catch(std::bad_alloc &e)
+        {
+            m_tableData[i]=NULL;
+        }
+        if(m_tableData[i]==NULL)
+        {
+            for(unsigned int j=0;j<i;j++) delete [] m_tableData[j];
+            delete [] m_tableData;
+            return false;
+        }
+    }
+
+
+    //--------------------------------------------------------------------------
+    // VSTable::getColumn reads the data
+    //--------------------------------------------------------------------------
+    m_VSTable->getColumn(colList,m_nCols,0,m_nRows-1, m_tableData);
+
+    //delete table;
+    return true;
+}
+
+void Pipe::destroyVTK()
+
+{
+    if ( m_pRenderer != 0 )
+        m_pRenderer->Delete();
+    if(m_pRenderWindow!=0)
+        m_pRenderWindow->Delete();
+    if ( m_lut!=0)
+        m_lut->Delete() ;
+}
+
+void Pipe::saveImageAsPng(int num )
+
+{
+
+    int magnification=1;
+    vtkWindowToImageFilter *w2i=vtkWindowToImageFilter::New();
+    std::string path;
+    std::string numero;
+    std::stringstream ss;
+    ss<<num;
+    ss>>numero;
+    std::string fileName;
+    w2i->SetInput(m_pRenderWindow);
+    w2i->SetMagnification(magnification);
+    w2i->Update();
+
+
+    this->destroyVTK();
+    return ;
+
+}
+
+
+void Pipe::setCamera ()
+{
+
+    m_camera =m_pRenderer->GetActiveCamera();
+
+    //m_camera->SetFocalPoint(0,0,0);
+    m_camera->Azimuth(0.0);
+    m_camera->Elevation(0.0);
+    m_camera->Zoom (1);
+
+    vtkwin->updateScene();
+
+}
+
+void Pipe::setBoundingBox ( vtkDataObject *data )
+{
+
+    qDebug()<<"setBoundingBox";
+
+    vtkSmartPointer<vtkPoints> points =
+            vtkSmartPointer<vtkPoints>::New();
+
+    //qui
+    points->InsertNextPoint(-15000, -10000, -200*scalingFactors);
+    points->InsertNextPoint(15000, -10000, -200*scalingFactors);
+    points->InsertNextPoint(-15000, 25000, -200*scalingFactors);
+    points->InsertNextPoint(15000, 25000, -200*scalingFactors);
+
+    points->InsertNextPoint(-15000, -10000, 200*scalingFactors);
+    points->InsertNextPoint(15000, -10000, 200*scalingFactors);
+    points->InsertNextPoint(-15000, 25000, 200*scalingFactors);
+    points->InsertNextPoint(15000, 25000, 200*scalingFactors);
+
+
+
+    // Create the polygon
+    vtkSmartPointer<vtkPolygon> polygon =
+            vtkSmartPointer<vtkPolygon>::New();
+    polygon->GetPointIds()->SetNumberOfIds(8); //make a quad
+    polygon->GetPointIds()->SetId(0, 0);
+    polygon->GetPointIds()->SetId(1, 1);
+    polygon->GetPointIds()->SetId(2, 2);
+    polygon->GetPointIds()->SetId(3, 3);
+    polygon->GetPointIds()->SetId(4, 4);
+    polygon->GetPointIds()->SetId(5, 5);
+    polygon->GetPointIds()->SetId(6, 6);
+    polygon->GetPointIds()->SetId(7, 7);
+
+    // Add the polygon to a list of polygons
+    vtkSmartPointer<vtkCellArray> polygons =
+            vtkSmartPointer<vtkCellArray>::New();
+    polygons->InsertNextCell(polygon);
+
+    // Create a PolyData
+    vtkSmartPointer<vtkPolyData> polygonPolyData =
+            vtkSmartPointer<vtkPolyData>::New();
+    polygonPolyData->SetPoints(points);
+    polygonPolyData->SetPolys(polygons);
+
+
+
+
+    bool showBox=true;
+    vtkOutlineCornerFilter*corner= vtkOutlineCornerFilter::New();
+    // corner->SetInputData(data);
+    corner->SetInputData(polygonPolyData);
+    corner->ReleaseDataFlagOn();
+
+    vtkPolyDataMapper *outlineMapper = vtkPolyDataMapper::New();
+    //   outlineMapper->SetInput ( corner->GetOutput() );
+    outlineMapper->SetInputConnection( corner->GetOutputPort() );
+
+    vtkProperty *outlineProperty = vtkProperty::New();
+    outlineProperty->SetColor ( 1,1,1 ); // Set the color to white
+    outlineProperty->SetAmbient ( 1 );
+    if(showBox)
+        outlineProperty->SetOpacity ( 0.999 );
+    else
+        outlineProperty->SetOpacity ( 0.0 );
+    outlineProperty->SetRepresentationToWireframe();
+    outlineProperty->SetInterpolationToFlat();
+
+    //vtkLODActor* outlineActor = vtkLODActor::New();
+    outlineActor = vtkLODActor::New();
+    outlineActor->SetMapper ( outlineMapper );
+    outlineActor->SetProperty ( outlineProperty );
+    outlineActor->SetPickable ( false );
+    outlineActor->SetVisibility ( true );
+
+
+    m_pRenderer->AddActor ( outlineActor );
+
+    if (outlineActor!=0)
+        outlineActor->Delete();
+    if (outlineMapper!=0)
+        outlineMapper->Delete();
+    if (outlineProperty!=0)
+        outlineProperty->Delete();
+    if (corner!=0)
+        corner->Delete();
+
+
+    qDebug()<<"setBoundingBox - end";
+
+}
+
+/*
+void Pipe::setBoundingBox ( vtkDataObject *data )
+{
+
+    qDebug()<<"*******************SETTO IL BOUNDING BOX";
+    bool showBox=true;
+    vtkOutlineCornerFilter*corner= vtkOutlineCornerFilter::New();
+    corner->SetInput(data);
+    corner->ReleaseDataFlagOn();
+
+    vtkPolyDataMapper *outlineMapper = vtkPolyDataMapper::New();
+    outlineMapper->SetInput ( corner->GetOutput() );
+
+    vtkProperty *outlineProperty = vtkProperty::New();
+    outlineProperty->SetColor ( 1,1,1 ); // Set the color to white
+    outlineProperty->SetAmbient ( 1 );
+    if(showBox)
+        outlineProperty->SetOpacity ( 0.999 );
+    else
+        outlineProperty->SetOpacity ( 0.0 );
+    outlineProperty->SetRepresentationToWireframe();
+    outlineProperty->SetInterpolationToFlat();
+
+    vtkLODActor* outlineActor = vtkLODActor::New();
+    outlineActor->SetMapper ( outlineMapper );
+    outlineActor->SetProperty ( outlineProperty );
+    outlineActor->SetPickable ( false );
+    outlineActor->SetVisibility ( true );
+
+
+    qDebug()<<"******************* ADD ACTOR";
+
+    m_pRenderer->AddActor ( outlineActor );
+
+    if (outlineActor!=0)
+        outlineActor->Delete();
+    if (outlineMapper!=0)
+        outlineMapper->Delete();
+    if (outlineProperty!=0)
+        outlineProperty->Delete();
+    if (corner!=0)
+        corner->Delete();
+}
+
+*/
+
+
+void Pipe::colorBar (bool showColorBar)
+{
+
+
+
+
+    if (scalarBar != 0)
+    {
+
+        //scalarBar->Delete();
+        m_pRenderer->RemoveActor( scalarBar );
+
+    }
+
+
+    scalarBar=vtkScalarBarActor::New();
+    scalarBar->SetTitle ( colorScalar.c_str());
+
+    scalarBar->SetLabelFormat ( "%.3g" );
+    scalarBar->SetOrientationToHorizontal();
+    scalarBar->SetPosition ( 0.1,0 );
+    scalarBar->SetPosition2 ( 0.8,0.1 );
+    scalarBar->SetLookupTable (m_lut );
+    m_pRenderer->AddActor ( scalarBar );
+    scalarBar->SetVisibility(showColorBar);
+
+    /*
+    if (scalarBar!=0)
+        scalarBar->Delete();
+*/
+}
+
+void Pipe::setAxes ( vtkDataSet *data, double *bounds  )
+{
+
+    double size = (bounds[5] - bounds[4] != 0 ? bounds[5] - bounds[4] : bounds[5]);
+    scalingFactors = size * 0.1;
+    scalingFactors = 1000/scalingFactors;
+    scalingFactors=40;
+    qDebug()<<"size: "<<size<<" "<<scalingFactors;
+    vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
+    points->InsertNextPoint(-15000, -10000, -200*scalingFactors);
+    points->InsertNextPoint(15000, -10000, -200*scalingFactors);
+    points->InsertNextPoint(-15000, 25000, -200*scalingFactors);
+    points->InsertNextPoint(15000, 25000, -200*scalingFactors);
+
+    points->InsertNextPoint(-15000, -10000, 200*scalingFactors);
+    points->InsertNextPoint(15000, -10000, 200*scalingFactors);
+    points->InsertNextPoint(-15000, 25000, 200*scalingFactors);
+    points->InsertNextPoint(15000, 25000, 200*scalingFactors);
+
+
+
+    // Create the polygon
+    vtkSmartPointer<vtkPolygon> polygon =
+            vtkSmartPointer<vtkPolygon>::New();
+    polygon->GetPointIds()->SetNumberOfIds(8); //make a quad
+    polygon->GetPointIds()->SetId(0, 0);
+    polygon->GetPointIds()->SetId(1, 1);
+    polygon->GetPointIds()->SetId(2, 2);
+    polygon->GetPointIds()->SetId(3, 3);
+    polygon->GetPointIds()->SetId(4, 4);
+    polygon->GetPointIds()->SetId(5, 5);
+    polygon->GetPointIds()->SetId(6, 6);
+    polygon->GetPointIds()->SetId(7, 7);
+
+    // Add the polygon to a list of polygons
+    vtkSmartPointer<vtkCellArray> polygons =
+            vtkSmartPointer<vtkCellArray>::New();
+    polygons->InsertNextCell(polygon);
+
+    // Create a PolyData
+    vtkSmartPointer<vtkPolyData> polygonPolyData =
+            vtkSmartPointer<vtkPolyData>::New();
+    polygonPolyData->SetPoints(points);
+    polygonPolyData->SetPolys(polygons);
+
+
+
+
+
+
+    axesActor=vtkCubeAxesActor2D::New();
+
+
+    //axesActor->SetInputData ( data );
+    axesActor->SetInputData ( polygonPolyData );
+
+
+    axesActor->UseRangesOn();
+
+
+    //FUNZIONANTI
+    //axesActor->SetBounds ( bounds[0],bounds[1],bounds[2],bounds[3],bounds[4],bounds[5]);
+    //axesActor->SetRanges (  bounds[0],bounds[1],bounds[2],bounds[3],bounds[4],bounds[5] );
+
+    axesActor->SetBounds ( -15000, 15000, -10000,25000,-200*scalingFactors,200*scalingFactors);
+    axesActor->SetRanges (  -15000,15000, -10000,25000,-200,200);
+
+
+    axesActor->SetNumberOfLabels(6);
+
+    axesActor->SetViewProp ( NULL );
+    axesActor->SetScaling ( 0 );
+    axesActor->SetPickable ( 0 );
+    axesActor->SetCamera ( m_pRenderer->GetActiveCamera() );
+    axesActor->SetCornerOffset ( 0.1 );
+
+    //axesActor->SetLabelFormat ( "%6.5g" );
+
+    axesActor->SetLabelFormat ( "%3.3g" );
+
+    axesActor->SetInertia ( 100 );
+    axesActor->SetFlyModeToOuterEdges();
+    axesActor->SetVisibility ( true );
+
+    axesActor->SetXLabel (vtkwin->vispoint->getX().toUtf8().constData() );
+    axesActor->SetYLabel (vtkwin->vispoint->getY().toUtf8().constData() );
+    axesActor->SetZLabel ( vtkwin->vispoint->getZ().toUtf8().constData());
+
+    axesActor->Modified();
+
+    m_pRenderer->AddActor2D ( axesActor );
+
+
+
+    if(axesActor!=0)
+        axesActor->Delete();
+}
diff --git a/Code/src/pipe.h b/Code/src/pipe.h
new file mode 100644
index 0000000000000000000000000000000000000000..70858eac0adfcff56110e5929c5dc72115f66ea1
--- /dev/null
+++ b/Code/src/pipe.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+ *   ugo.becciani@oact.inaf.it                                             *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef PIPE_H
+#define PIPE_H
+
+/**
+        @author Alessandro Costa <alessandro.costa@oact.inaf.it>
+        @author Ugo Becciani <ugo.becciani@oact.inaf.it>
+*/
+
+#include "vtkPolyDataMapper.h"
+#include <vector>
+#include <map>
+#include "vstabledesktop.h"
+#include "vtkScalarBarActor.h"
+
+class vtkRenderer;
+class vtkRenderWindow;
+class vtkCamera;
+class vtkLookupTable;
+class VSTableDesktop;
+class vtkwindow_new;
+class vtkLODActor;
+class vtkCubeAxesActor2D;
+
+class Pipe
+{
+    VSTableDesktop *m_VSTable;
+
+public:
+    Pipe(VSTableDesktop *table);
+    VSTableDesktop *getTable();
+    void saveImageAsPng(int num );
+    virtual  int createPipe();
+    virtual  void destroyAll(){};
+    virtual  bool readData(QStringList list);
+    bool readDataFor3D(QStringList list);
+
+    virtual  int getCamera();
+    vtkLODActor* outlineActor;
+    vtkCubeAxesActor2D* axesActor;
+    std::string colorScalar;
+    vtkScalarBarActor *scalarBar;
+
+    vtkLookupTable* getLookupTable(){return m_lut;}
+    int getRows(){return m_nRows;}
+
+
+protected:
+
+    void setCamera();
+    void constructVTK(vtkwindow_new *v);
+    void destroyVTK();
+    void setBoundingBox ( vtkDataObject *data );
+    void colorBar (bool showColorBar);
+    virtual  void setAxes(vtkDataSet *data,double *bounds);
+    int m_nRows;
+    int m_nCols;
+    std::map<std::string, int> m_colNames;
+    vtkCamera           *m_camera;
+    vtkRenderer         *m_pRenderer;
+    vtkRenderWindow     *m_pRenderWindow;
+    vtkLookupTable      *m_lut;
+    vtkwindow_new       *vtkwin;
+    double scalingFactors;
+
+    //new stuff here...
+    float **m_tableData;
+    std::vector<std::string> m_fieldNames;
+    std::string m_path;
+};
+
+#endif
+
diff --git a/Code/src/plotwindow.cpp b/Code/src/plotwindow.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..725e20b95dca57e9ca4fef09e8915d305fa34be5
--- /dev/null
+++ b/Code/src/plotwindow.cpp
@@ -0,0 +1,96 @@
+#include "plotwindow.h"
+#include "ui_plotwindow.h"
+
+PlotWindow::PlotWindow(vtkwindow_new *v, QList<QListWidgetItem*> selItems, int id, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::PlotWindow)
+{
+    ui->setupUi(this);
+
+    QString title="Plot #"+QString::number(id);
+
+    this->setWindowTitle(title);
+
+    ui->customPlot->addGraph();
+    // ui->customPlot->xAxis2->setVisible(true);
+    // ui->customPlot->yAxis2->setVisible(true);
+
+
+
+    vtkwin=v;
+    selectedItems=selItems;
+
+    table =vtkwin->getEllipseList().value(selItems.at(0)->text())->getTable();
+
+    for(int i=0;i<table->getNumberOfColumns();i++)
+    {
+        ui->xComboBox->addItem(QString::fromStdString(table->getColName(i)));
+        ui->yComboBox->addItem(QString::fromStdString(table->getColName(i)));
+    }
+
+
+}
+
+PlotWindow::~PlotWindow()
+{
+    delete ui;
+}
+
+void PlotWindow::on_plotButton_clicked()
+{
+    qDebug()<<"index: "<<ui->xComboBox->currentIndex();
+    QVector<double> x(selectedItems.size()), y(selectedItems.size());
+
+
+    int row;
+    for (int i=0; i<selectedItems.size(); i++)
+    {
+
+        row = vtkwin->getEllipseList().value(selectedItems.at(i)->text())->getRowInTable();
+
+        x[i] = atof(table->getTableData()[ui->xComboBox->currentIndex()][row].c_str());
+        y[i] = atof(table->getTableData()[ui->yComboBox->currentIndex()][row].c_str());
+
+
+    }
+
+
+
+
+    // create graph and assign data to it:
+    ui->customPlot->graph(0)->setData(x, y);
+
+    if(ui->logxCheckBox->isChecked())
+    {
+        ui->customPlot->xAxis->setScaleType(QCPAxis::stLogarithmic);
+        ui->customPlot->xAxis->setLabel("Log "+ui->xComboBox->currentText() );
+    }
+    else
+    {
+        ui->customPlot->xAxis->setScaleType(QCPAxis::stLinear);
+        ui->customPlot->xAxis->setLabel(ui->xComboBox->currentText() );
+    }
+    if(ui->logyCheckBox->isChecked())
+    {
+        ui->customPlot->yAxis->setScaleType(QCPAxis::stLogarithmic);
+        ui->customPlot->yAxis->setLabel("Log "+ui->yComboBox->currentText());
+
+    }
+    else
+    {
+        ui->customPlot->yAxis->setScaleType(QCPAxis::stLinear);
+        ui->customPlot->yAxis->setLabel(ui->yComboBox->currentText());
+
+    }
+
+
+    ui->customPlot->graph(0)->setLineStyle(QCPGraph::lsNone);
+    ui->customPlot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 4));
+
+    ui->customPlot->graph(0)->rescaleAxes();
+
+    //ui->customPlot->xAxis->setRange(x.at(x.first()), x.at(x.last()));
+    // ui->customPlot->yAxis->setRange(y.at(y.first()), y.at(y.last()));
+    ui->customPlot->replot();
+
+}
diff --git a/Code/src/plotwindow.h b/Code/src/plotwindow.h
new file mode 100644
index 0000000000000000000000000000000000000000..77ce759b4c8feaad91fb741ce5b8213280b92759
--- /dev/null
+++ b/Code/src/plotwindow.h
@@ -0,0 +1,31 @@
+#ifndef PLOTWINDOW_H
+#define PLOTWINDOW_H
+
+#include <QWidget>
+#include "vtkwindow_new.h"
+#include <QListWidgetItem>
+#include "vstabledesktop.h"
+
+namespace Ui {
+class PlotWindow;
+}
+
+class PlotWindow : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit PlotWindow(vtkwindow_new *v, QList<QListWidgetItem*> selItems, int id,QWidget *parent = 0);
+    ~PlotWindow();
+
+private slots:
+    void on_plotButton_clicked();
+
+private:
+    Ui::PlotWindow *ui;
+    QList<QListWidgetItem*> selectedItems;
+    vtkwindow_new *vtkwin;
+    VSTableDesktop *table;
+};
+
+#endif // PLOTWINDOW_H
diff --git a/Code/src/pointspipe.cpp b/Code/src/pointspipe.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e182a57c2804002748960f835f02cce74a293062
--- /dev/null
+++ b/Code/src/pointspipe.cpp
@@ -0,0 +1,1261 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+ *   ugo.becciani@oact.inaf.it                                         *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+
+#include <cstdlib>
+#include <cstring>
+
+#include "pointspipe.h"
+
+#include "visivoutilsdesktop.h"
+#include "luteditor.h"
+
+#include "extendedglyph3d.h"
+
+#include <sstream>
+#include <algorithm>
+
+#include "vtkSphereSource.h"
+#include "vtkConeSource.h"
+#include "vtkCylinderSource.h"
+#include "vtkCubeSource.h"
+
+#include "vtkCamera.h"
+#include "vtkPointData.h"
+#include "vtkCellData.h"
+#include "vtkLookupTable.h"
+
+#include "vtkFloatArray.h"
+#include "vtkCellArray.h"
+#include"vtkGlyph3D.h"
+#include "vtkScalarBarActor.h"
+#include "vtkOutlineCornerFilter.h"
+#include "vtkProperty.h"
+
+#include "vtkGenericRenderWindowInteractor.h"
+#include "vtkRenderWindow.h"
+#include "vtkRenderer.h"
+#include "vtkLODActor.h"
+#include "vtkAxesActor.h"
+#include "vtkUnstructuredGrid.h"
+
+#include "vispoint.h"
+#include "qdebug.h"
+
+#include "vtkTransform.h"
+#include "vtkSmartPointer.h"
+#include "vtkTransformPolyDataFilter.h"
+
+#include "vtkRectilinearGrid.h"
+#include "vtkRectilinearGridGeometryFilter.h"
+#include "vtkPlaneSource.h"
+#include "vtkIdFilter.h"
+#include "vtkDataSetSurfaceFilter.h"
+#include "vtkStringArray.h"
+#include "vtkwindow_new.h"
+
+#include "math.h"
+
+//---------------------------------------------------------------------
+PointsPipe::PointsPipe (VSTableDesktop *table): Pipe(table)
+  //---------------------------------------------------------------------
+{
+    //constructVTK();
+    m_glyphFilter   = ExtendedGlyph3D::New();
+    m_glyph         = vtkGlyph3D::New();
+    m_pActor    = vtkLODActor::New();
+    m_polyData      = vtkPolyData::New();
+    m_pUnstructuredGrid = vtkUnstructuredGrid::New();
+    m_pMapper   = vtkPolyDataMapper::New();
+
+    m_VSTable = table;
+}
+
+//---------------------------------
+PointsPipe::~PointsPipe()
+//---------------------------------
+{
+    destroyVTK();
+    if ( m_glyph!=0)
+        m_glyph->Delete() ;
+    if ( m_glyphFilter!=0)
+        m_glyphFilter->Delete() ;
+    if ( m_pMapper != 0 )
+        m_pMapper->Delete();
+    if ( m_pActor != 0 )
+        m_pActor->Delete();
+    if ( m_polyData!=0)
+        m_polyData->Delete() ;
+}
+//---------------------------------
+void PointsPipe::destroyAll()
+//---------------------------------
+{
+    if ( m_glyph!=0)
+        m_glyph->Delete() ;
+    if ( m_glyphFilter!=0)
+        m_glyphFilter->Delete() ;
+    if ( m_pMapper != 0 )
+        m_pMapper->Delete();
+    if ( m_pActor != 0 )
+        m_pActor->Delete();
+    if ( m_polyData!=0)
+        m_polyData->Delete() ;
+}
+
+
+void PointsPipe::setVtkWindow(vtkwindow_new *v)
+{
+    vtkwin=v;
+    constructVTK(vtkwin);
+
+}
+
+int PointsPipe::createPipeFor3dSelection()
+{
+
+    int i = 0;
+    int j = 0;
+
+    radius=1;
+    height=1;
+
+    scaleGlyphs="none";
+    radiusscalar="none";
+    heightscalar="none";
+
+
+    vtkFloatArray *radiusArrays =vtkFloatArray::New();
+
+    vtkFloatArray *xAxis=vtkFloatArray::New();
+    vtkFloatArray *yAxis=vtkFloatArray::New();
+    vtkFloatArray *zAxis=vtkFloatArray::New();
+
+    m_nRows= m_VSTable->getNumberOfRows();
+
+    xAxis->SetNumberOfTuples(m_nRows);
+    yAxis->SetNumberOfTuples(m_nRows);
+    zAxis->SetNumberOfTuples(m_nRows);
+
+    xAxis->SetName(vtkwin->vispoint->getX().toUtf8().constData());
+    yAxis->SetName(vtkwin->vispoint->getY().toUtf8().constData());
+    zAxis->SetName(vtkwin->vispoint->getZ().toUtf8().constData());
+
+    int xIndex, yIndex,zIndex;
+
+    xIndex= m_VSTable->getColId(xAxis->GetName());
+    yIndex= m_VSTable->getColId(yAxis->GetName());
+    zIndex= m_VSTable->getColId(zAxis->GetName());
+    /*
+     *     points->InsertNextPoint(-15000, -10000, -200*scalingFactors);
+    points->InsertNextPoint(15000, -10000, -200*scalingFactors);
+    points->InsertNextPoint(-15000, 25000, -200*scalingFactors);
+    points->InsertNextPoint(15000, 25000, -200*scalingFactors);
+
+    points->InsertNextPoint(-15000, -10000, 200*scalingFactors);
+    points->InsertNextPoint(15000, -10000, 200*scalingFactors);
+    points->InsertNextPoint(-15000, 25000, 200*scalingFactors);
+    points->InsertNextPoint(15000, 25000, 200*scalingFactors);
+
+    */
+    double xval, yval, zval;
+    for(i=0; i<m_nRows;i++)
+    {
+        xval = atof(m_VSTable->getTableData()[xIndex][i].c_str());
+        yval = atof(m_VSTable->getTableData()[yIndex][i].c_str());
+        zval = atof(m_VSTable->getTableData()[zIndex][i].c_str());
+
+        if (xval> -15000 && xval<15000 && yval>-10000 && yval< 25000 && zval > -200 && zval < 200 )
+        {
+            xAxis->  SetValue(i,atof(m_VSTable->getTableData()[xIndex][i].c_str()));
+            yAxis->  SetValue(i,atof(m_VSTable->getTableData()[yIndex][i].c_str()));
+            zAxis->  SetValue(i,atof(m_VSTable->getTableData()[zIndex][i].c_str()));
+
+        }
+
+    }
+    xAxis->GetRange(m_xRange);  //!minimum and maximum value
+    yAxis->GetRange(m_yRange);  //!minimum and maximum value
+    zAxis->GetRange(m_zRange);  //!minimum and maximum value
+
+    qDebug()<<"m_xRange: "<<m_xRange[0]<<" "<<m_xRange[1]<<" ";
+
+
+    SetXYZ(xAxis,yAxis,zAxis);
+
+
+
+    m_polyData->SetPoints(m_points);
+    m_pUnstructuredGrid->SetPoints(m_points);
+    activateScale(true);
+
+    int nPoints = m_polyData->GetNumberOfPoints();
+    qDebug()<<"punti: "<<nPoints;
+
+    vtkCellArray *newVerts = vtkCellArray::New();
+    newVerts->EstimateSize ( nPoints, 1  );
+
+
+    for ( int i = 0; i < nPoints; i++ )
+    {
+        //   labels->InsertNextValue("id_"+i);
+        newVerts->InsertNextCell(1);
+        newVerts->InsertCellPoint ( i );
+    }
+    m_polyData->SetVerts(newVerts);
+
+
+    xAxis->Delete();
+    yAxis->Delete();
+    zAxis->Delete();
+    m_points->Delete();
+
+    float tmp[1];
+
+    colorScalar = vtkwin->vispoint->getX().toUtf8().constData();
+    color = "yes";
+    palette = "AllWhite";
+    showColorBar=false;
+
+
+    //draw axis cartesian 0,0,0
+    double p0[3] = {0.0, -10000, 0.0};
+    double p1[3] = {0.0, 25000, 0.0};
+    vtkSmartPointer<vtkLineSource> lineSource =
+            vtkSmartPointer<vtkLineSource>::New();
+    lineSource->SetPoint1(p0);
+    lineSource->SetPoint2(p1);
+    lineSource->Update();
+
+    vtkSmartPointer<vtkPolyDataMapper> mapper =
+            vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputConnection(lineSource->GetOutputPort());
+    vtkSmartPointer<vtkActor> actorLine =
+            vtkSmartPointer<vtkActor>::New();
+    actorLine->SetMapper(mapper);
+    actorLine->GetProperty()->SetLineWidth(1);
+    m_pRenderer->AddActor ( actorLine );
+
+    double p0_1[3] = {-15000, 0, 0.0};
+    double p1_1[3] = {15000, 0, 0.0};
+    vtkSmartPointer<vtkLineSource> lineSource_1 =
+            vtkSmartPointer<vtkLineSource>::New();
+    lineSource_1->SetPoint1(p0_1);
+    lineSource_1->SetPoint2(p1_1);
+    lineSource_1->Update();
+
+    vtkSmartPointer<vtkPolyDataMapper> mapper_1 =
+            vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper_1->SetInputConnection(lineSource_1->GetOutputPort());
+    vtkSmartPointer<vtkActor> actorLine_1 =
+            vtkSmartPointer<vtkActor>::New();
+    actorLine_1->SetMapper(mapper_1);
+    actorLine_1->GetProperty()->SetLineWidth(1);
+    m_pRenderer->AddActor ( actorLine_1 );
+
+    //set a symbol on 0,8400,0
+
+    vtkSmartPointer<vtkSphereSource> sphereSource =
+            vtkSmartPointer<vtkSphereSource>::New();
+    double cone_center[3] = {0, 8400, 0};
+    sphereSource->SetCenter(cone_center);
+    sphereSource->SetRadius(100);
+    sphereSource->Update();
+
+    //Create a mapper and actor
+    vtkSmartPointer<vtkPolyDataMapper> mapper_cone =
+            vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper_cone->SetInputConnection(sphereSource->GetOutputPort());
+
+    vtkSmartPointer<vtkActor> coneActor =
+            vtkSmartPointer<vtkActor>::New();
+    coneActor->SetMapper(mapper_cone);
+
+    double color[3] ={1,0,0};
+    coneActor->GetProperty()->SetColor(color);
+    m_pRenderer->AddActor ( coneActor );
+    /*
+
+      double p0_center_point[3] = {0.0, 8398, 0.0};
+      double p1_center_point[3] = {0.0, 8402, 0.0};
+      vtkSmartPointer<vtkLineSource> lineSource_center_point =
+        vtkSmartPointer<vtkLineSource>::New();
+      lineSource_center_point->SetPoint1(p0_center_point);
+      lineSource_center_point->SetPoint2(p1_center_point);
+      lineSource_center_point->Update();
+
+      vtkSmartPointer<vtkPolyDataMapper> mapper_center_point =
+         vtkSmartPointer<vtkPolyDataMapper>::New();
+       mapper_center_point->SetInputConnection(lineSource_center_point->GetOutputPort());
+       vtkSmartPointer<vtkActor> actorLine_center_point =
+         vtkSmartPointer<vtkActor>::New();
+       actorLine_center_point->SetMapper(mapper_center_point);
+       actorLine_center_point->GetProperty()->SetLineWidth(30);
+
+       double color[3] ={1,0,0};
+       actorLine_center_point->GetProperty()->SetColor(color);
+
+       m_pRenderer->AddActor ( actorLine_center_point );
+
+
+       double p0_2[3] = {-2, 8400, 0.0};
+       double p1_2[3] = {2, 8400, 0.0};
+       vtkSmartPointer<vtkLineSource> lineSource_center_point_2 =
+         vtkSmartPointer<vtkLineSource>::New();
+       lineSource_center_point_2->SetPoint1(p0_2);
+       lineSource_center_point_2->SetPoint2(p1_2);
+       lineSource_center_point_2->Update();
+
+       vtkSmartPointer<vtkPolyDataMapper> mapper_center_point_2 =
+          vtkSmartPointer<vtkPolyDataMapper>::New();
+        mapper_center_point_2->SetInputConnection(lineSource_center_point_2->GetOutputPort());
+        vtkSmartPointer<vtkActor> actorLine_center_point_2 =
+          vtkSmartPointer<vtkActor>::New();
+        actorLine_center_point_2->SetMapper(mapper_center_point_2);
+        actorLine_center_point_2->GetProperty()->SetLineWidth(30);
+
+        actorLine_center_point_2->GetProperty()->SetColor(color);
+
+        m_pRenderer->AddActor ( actorLine_center_point_2 );
+*/
+
+
+
+    // m_pMapper->SetInput (m_polyData );
+    m_pMapper->SetInputData (m_polyData );
+
+    m_pActor->SetMapper ( m_pMapper );
+    m_pRenderer->AddActor ( m_pActor );
+
+    //setGlyphs (1);
+
+
+    float opacity=0.650; // a default value
+
+    if (opacity<0 )
+        opacity=0;
+
+    else if( opacity>1)
+        opacity=1;
+
+    m_pActor->GetProperty()->SetOpacity ( opacity);
+
+    //setLookupTable();
+    setCamera ();
+    qDebug()<<"fine camera";
+
+
+
+
+    double *bounds;
+    bounds=new double[6];
+    bounds[0]=m_xRange[0];
+    bounds[1]=m_xRange[1];
+    bounds[2]=m_yRange[0];
+    bounds[3]=m_yRange[1];
+    bounds[4]=m_zRange[0];
+    bounds[5]=m_zRange[1];
+
+    bool showAxes=true;
+    if(showAxes) setAxes (m_polyData,bounds );
+    delete [] bounds;
+
+    setBoundingBox (m_polyData);
+
+
+    // m_pRenderWindow->Render();
+
+    radiusArrays->Delete();
+
+
+    return 0;
+
+
+}
+
+
+//-----------------------------------------------------------------------------------
+int PointsPipe::createPipe ()
+//------------------------------------------------------------------------------------
+{
+    int i = 0;
+    int j = 0;
+
+    vtkFloatArray *radiusArrays =vtkFloatArray::New();
+
+    vtkFloatArray *xAxis=vtkFloatArray::New();
+    vtkFloatArray *yAxis=vtkFloatArray::New();
+    vtkFloatArray *zAxis=vtkFloatArray::New();
+
+    xAxis->SetNumberOfTuples(m_nRows);
+    yAxis->SetNumberOfTuples(m_nRows);
+    zAxis->SetNumberOfTuples(m_nRows);
+
+    qDebug()<<"post set tuple";
+
+    xAxis->SetName(vtkwin->vispoint->getX().toUtf8().constData());
+    yAxis->SetName(vtkwin->vispoint->getY().toUtf8().constData());
+    zAxis->SetName(vtkwin->vispoint->getZ().toUtf8().constData());
+
+    qDebug()<<"post set name"<<vtkwin->vispoint->getX();
+
+    int xIndex, yIndex,zIndex;
+
+    xIndex= m_colNames.find(xAxis->GetName())->second;
+    yIndex= m_colNames.find(yAxis->GetName())->second;
+    zIndex= m_colNames.find(zAxis->GetName())->second;
+
+    qDebug()<<"index: "<<xIndex<<" "<<yIndex<<" "<<zIndex;
+
+
+    for(i=0; i<m_nRows;i++)
+    {
+        xAxis->  SetValue(i,m_tableData[xIndex][i]); //! this is the row that fill xAxis array
+        yAxis->  SetValue(i,m_tableData[yIndex][i]);
+        zAxis->  SetValue(i,m_tableData[zIndex][i]);
+    }
+
+
+    xAxis->GetRange(m_xRange);  //!minimum and maximum value
+    yAxis->GetRange(m_yRange);  //!minimum and maximum value
+    zAxis->GetRange(m_zRange);  //!minimum and maximum value
+
+    SetXYZ(xAxis,yAxis,zAxis);
+
+
+    //m_polyData->GetPointData()->AddArray()
+
+    m_polyData->SetPoints(m_points);
+    m_pUnstructuredGrid->SetPoints(m_points);
+    /*
+    vtkSmartPointer<vtkIdFilter> idFilter = vtkSmartPointer<vtkIdFilter>::New();
+    idFilter->SetInput(m_pUnstructuredGrid);
+    idFilter->SetIdsArrayName("OriginalIds");
+    idFilter->Update();
+*/
+
+    int nPoints = m_polyData->GetNumberOfPoints();
+    qDebug()<<"punti: "<<nPoints;
+
+    vtkCellArray *newVerts = vtkCellArray::New();
+    newVerts->EstimateSize ( nPoints, 1  );
+
+    /*
+    vtkSmartPointer<vtkStringArray> labels = vtkSmartPointer<vtkStringArray>::New();
+    labels->SetNumberOfValues(nPoints);
+    labels->SetName("labels");
+
+*/
+    for ( int i = 0; i < nPoints; i++ )
+    {
+        //   labels->InsertNextValue("id_"+i);
+        newVerts->InsertNextCell(1);
+        newVerts->InsertCellPoint ( i );
+    }
+    m_polyData->SetVerts(newVerts);
+    // m_polyData->GetPointData()->AddArray(labels);
+
+
+    //START
+    /*
+    std::cout << "There are " << m_polyData->GetNumberOfPoints() << " points." << std::endl;
+    std::cout << "There are " << m_polyData->GetNumberOfCells() << " cells." << std::endl;
+
+    vtkSmartPointer<vtkIdFilter> idFilter =
+            vtkSmartPointer<vtkIdFilter>::New();
+    idFilter->SetInputConnection(m_polyData->GetProducerPort());
+    idFilter->SetIdsArrayName("ids");
+    idFilter->Update();
+
+    vtkSmartPointer<vtkDataSetSurfaceFilter> surfaceFilter = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
+    surfaceFilter->SetInputConnection(idFilter->GetOutputPort());
+    surfaceFilter->Update();
+
+    //vtkPolyData* input = surfaceFilter->GetOutput();
+    m_polyData=surfaceFilter->GetOutput();
+
+
+
+    vtkIdTypeArray* pointIds = vtkIdTypeArray::SafeDownCast(idFilter->GetOutput()->GetPointData()->GetArray("ids"));
+    std::cout << "There are " << pointIds->GetNumberOfTuples() << " point ids" << std::endl;
+
+    vtkIdTypeArray* cellIds = vtkIdTypeArray::SafeDownCast(idFilter->GetOutput()->GetCellData()->GetArray("ids"));
+    std::cout << "There are " << cellIds->GetNumberOfTuples() << " point ids" << std::endl;
+
+    vtkIdTypeArray* ids = vtkIdTypeArray::SafeDownCast(m_polyData->GetPointData()->GetArray("ids"));
+    for(vtkIdType i = 0; i < ids->GetNumberOfTuples(); i++)
+    {
+        std::cout << "Id " << i << " : " << ids->GetValue(i) << std::endl;
+    }
+*/
+    //END
+
+
+    xAxis->Delete();
+    yAxis->Delete();
+    zAxis->Delete();
+    m_points->Delete();
+
+    float tmp[1];
+
+    colorScalar = vtkwin->vispoint->getX().toUtf8().constData();
+    color = "yes";
+    palette = "AllWhite";
+    showColorBar=false;
+
+
+
+
+    // m_pMapper->SetInput (m_polyData );
+    m_pMapper->SetInputData (m_polyData );
+
+    m_pActor->SetMapper ( m_pMapper );
+
+    //
+    // m_pActor->PickableOn();
+
+    //
+
+
+    m_pRenderer->AddActor ( m_pActor );
+
+    float opacity=0.650; // a default value
+
+
+    if (opacity<0 )
+        opacity=0;
+
+    else if( opacity>1)
+        opacity=1;
+
+    m_pActor->GetProperty()->SetOpacity ( opacity);
+
+
+
+    setLookupTable();
+
+    setCamera ();
+    //qDebug()<<"fine camera";
+
+    double *bounds;
+    bounds=new double[6];
+    bounds[0]=m_xRange[0];
+    bounds[1]=m_xRange[1];
+    bounds[2]=m_yRange[0];
+    bounds[3]=m_yRange[1];
+    bounds[4]=m_zRange[0];
+    bounds[5]=m_zRange[1];
+
+    bool showAxes=true;
+    if(showAxes) setAxes (m_polyData,bounds );
+    delete [] bounds;
+
+    setBoundingBox (m_polyData);
+
+
+    // m_pRenderWindow->Render();
+
+    radiusArrays->Delete();
+    return 0;
+}
+
+//-----------------------------------------------------------------------------------
+vtkRenderer *PointsPipe::getRenderer ()
+//-----------------------------------------------------------------------------------
+{
+    return m_pRenderer;
+}
+
+//---------------------------------------------------------------------
+vtkLODActor *PointsPipe::getActor ()
+//---------------------------------------------------------------------
+{
+    return m_pActor;
+}
+
+//---------------------------------------------------------------------
+vtkPolyDataMapper *PointsPipe::getMapper ()
+//---------------------------------------------------------------------
+{
+    return m_pMapper;
+}
+
+vtkUnstructuredGrid *PointsPipe::getUnstructuredGrid()
+{
+    return m_pUnstructuredGrid;
+}
+//---------------------------------------------------------------------
+vtkPolyData *PointsPipe::getPolyData ()
+//---------------------------------------------------------------------
+{
+    return m_polyData;
+}
+
+void PointsPipe::addScalar(std::string myScalar, bool color)
+{
+    vtkFloatArray *lutArrays =vtkFloatArray::New();
+    vtkFloatArray *newArrays =vtkFloatArray::New();
+
+    lutArrays->SetNumberOfTuples(m_nRows);
+
+    qDebug()<<"m_nRows: "<<m_nRows;
+
+    int ilut;
+    std::map<std::string, int>::iterator p;
+    ilut=m_VSTable->getColId(myScalar);
+    qDebug()<<"pre  "<<QString::fromStdString(myScalar) <<" ilut: "<<ilut;
+
+
+
+    for(int i=0; i<m_nRows;i++){
+        double value = atof(m_VSTable->getTableData()[ilut][i].c_str());
+        //if(value!=-9999){
+        lutArrays -> SetValue(i, atof(m_VSTable->getTableData()[ilut][i].c_str()));
+        //}
+    }
+
+    lutArrays->SetName(myScalar.c_str());
+    
+    if(!color){
+
+        float *range= new float[2];
+        lutArrays->GetValueRange(range,0);
+        qDebug()<<"Range: "<<range[0]<<" "<<range[1];
+
+        newArrays->SetNumberOfTuples(m_nRows);
+
+        for(int i=0; i<m_nRows;i++){
+            double value = lutArrays->GetValue(i);
+            double new_value=(1*(value-range[0]))/(range[1]-range[0])+0.5;
+            newArrays -> SetValue(i, new_value);
+        }
+
+        float *newrange= new float[2];
+        newArrays->GetValueRange(newrange,0);
+        qDebug()<<"New Range: "<<newrange[0]<<" "<<newrange[1];
+        newArrays->SetName("scaleGlyph");
+
+        m_polyData->GetPointData()->SetScalars(newArrays);
+    }else{
+        m_polyData->GetPointData()->AddArray(lutArrays);
+    }
+}
+
+
+
+
+
+/*
+void PointsPipe::initLut()
+{
+    this->addScalar(colorScalar);
+}
+*/
+
+void PointsPipe::initLut()
+{
+    vtkFloatArray *lutArrays =vtkFloatArray::New();
+    lutArrays->SetNumberOfTuples(m_nRows);
+
+    qDebug()<<"m_nRows: "<<m_nRows;
+
+    int ilut;
+    std::map<std::string, int>::iterator p;
+    ilut=m_VSTable->getColId(colorScalar);
+    qDebug()<<"pre  "<<QString::fromStdString(colorScalar) <<" ilut: "<<ilut;
+
+
+
+    for(int i=0; i<m_nRows;i++){
+        double value = atof(m_VSTable->getTableData()[ilut][i].c_str());
+        //if(value!=-9999){
+        lutArrays -> SetValue(i, atof(m_VSTable->getTableData()[ilut][i].c_str()) );
+        //}
+    }
+
+    lutArrays->SetName(colorScalar.c_str());
+    m_polyData->GetPointData()->SetScalars(lutArrays);
+
+}
+
+void PointsPipe::setLookupTable ()
+{
+
+
+    initLut();
+
+    double *b;
+    b=new double[2];
+
+    m_polyData->GetPointData()->SetActiveScalars(colorScalar.c_str());
+    m_polyData->GetPointData()->GetScalars(colorScalar.c_str())->GetRange(b);
+
+    setLookupTable (b[0], b[1]);
+
+
+}
+
+void PointsPipe::setActiveScalar()
+{
+    m_polyData->GetPointData()->SetActiveScalars(colorScalar.c_str());
+    vtkwin->updateScene();
+}
+
+void PointsPipe::setLookupTableScale ()
+{
+    initLut();
+    double *b;
+    b=new double[2];
+    
+    m_polyData->GetPointData()->SetActiveScalars(colorScalar.c_str());
+    m_polyData->GetPointData()->GetScalars(colorScalar.c_str())->GetRange(b);
+
+
+    setLookupTable (b[0], b[1]);
+    if(scale=="Linear")
+        m_lut->SetScaleToLinear();
+    else if (scale=="Log")
+        m_lut->SetScaleToLog10();
+
+    //    vtkwin->updateScene();
+
+}
+
+void PointsPipe::setLookupTable (float from, float to)
+{
+
+    qDebug()<<"from: "<<from<<" to: "<<to;
+    actualFrom=from;
+    actualTo=to;
+
+    // initLut();
+
+    m_lut->SetTableRange(from,to);
+    //    m_lut->SetScaleToLinear();
+    // m_lut->SetScaleToLog10();
+
+    m_lut->Build();
+
+    SelectLookTable(palette.c_str(),m_lut);
+
+    qDebug()<<" "<<palette.c_str();
+
+    m_pMapper->SetLookupTable(m_lut);
+    m_pMapper->SetScalarVisibility(1);
+    m_pMapper->UseLookupTableScalarRangeOn();
+    m_pActor->SetMapper(m_pMapper);
+
+    colorBar(showColorBar);
+
+
+}
+
+/*
+//---------------------------------------------------------------------
+    void PointsPipe::setRadius ()
+//---------------------------------------------------------------------
+{
+  if (m_visOpt.nGlyphs==1)
+    m_sphere->SetRadius ( m_visOpt.radius);
+
+
+  else if (m_visOpt.nGlyphs==2)
+  {
+
+    m_cone->SetRadius ( m_visOpt.radius );
+    m_cone->SetHeight (m_visOpt.height );
+  }
+
+  else if (m_visOpt.nGlyphs==3)
+  {
+
+    m_cylinder->SetRadius (m_visOpt.radius );
+    m_cylinder->SetHeight ( m_visOpt.height );
+  }
+  else if (m_visOpt.nGlyphs==4)
+  {
+    m_cube->SetXLength ( m_visOpt.radius );
+    m_cube->SetYLength ( m_visOpt.height );
+    m_cube->SetZLength ( 1 );
+
+  }
+}
+
+//---------------------------------------------------------------------
+void PointsPipe::setResolution ()
+//---------------------------------------------------------------------
+{
+  if (m_visOpt.nGlyphs==1)
+  {
+    m_sphere->SetPhiResolution ( 10 );
+    m_sphere->SetThetaResolution ( 20 );
+  }
+
+  else if (m_visOpt.nGlyphs==2)
+    m_cone->SetResolution ( 10 );
+
+  else if (m_visOpt.nGlyphs==3)
+    m_cylinder->SetResolution ( 10);
+
+
+}
+
+*/
+//-------------------------------------------------------------------------
+bool PointsPipe::SetXYZ(vtkFloatArray *xField, vtkFloatArray *yField, vtkFloatArray *zField  )
+//-------------------------------------------------------------------------
+{
+    double scalingFactors[3];
+    scalingFactors[0]=scalingFactors[1]=scalingFactors[2]=0;
+
+    m_points=vtkPoints::New();
+    m_points->SetNumberOfPoints(m_nRows);
+
+    m_scaled_points=vtkPoints::New();
+    m_scaled_points->SetNumberOfPoints(m_nRows);
+
+    if(xField->GetNumberOfComponents() != yField->GetNumberOfComponents())
+    {
+        if(zField && (xField->GetNumberOfComponents() != zField->GetNumberOfComponents() \
+                      || yField->GetNumberOfComponents() != zField->GetNumberOfComponents()))
+        {
+            false;
+        }
+        false; // component mismatch, do nothing
+    }
+
+    std::string scale = "no";
+    // if(scale=="yes")
+    // {
+
+    double size = 0;
+
+
+    size = (m_xRange[1] - m_xRange[0] != 0 ? m_xRange[1] - m_xRange[0] : m_xRange[1]);
+    scalingFactors[0] = size * 0.1;
+
+
+    size = (m_yRange[1] - m_yRange[0] != 0 ? m_yRange[1] - m_yRange[0] : m_yRange[1]);
+    scalingFactors[1] = size * 0.1;
+
+    qDebug()<<"m_zRange: "<< m_zRange[1]<<" - "<<m_zRange[0];
+
+    size = (m_zRange[1] - m_zRange[0] != 0 ? m_zRange[1] - m_zRange[0] : m_zRange[1]);
+    scalingFactors[2] = size * 0.1;
+    //   }
+
+    //double scalingFactorsInv[3];
+
+    int i = 0;
+    for(i = 0; i < 3; i++)
+        scalingFactorsInv[i] = ((scalingFactors && scalingFactors[i] != 0) ? 1/scalingFactors[i] : 0);
+
+
+    // scalingFactorsInv[2]*=1000;
+    scalingFactorsInv[2]=40;
+    // Set the points data
+
+    //  if(scale=="yes")
+    //  {
+    for(i = 0; i < m_nRows; i++)
+    {
+        float inPoint[3];
+        float outPoint[3];
+        /*
+        inPoint[0] = outPoint[0] = xField->GetValue(i) * scalingFactorsInv[0];
+        inPoint[1] = outPoint[1] = yField->GetValue(i) * scalingFactorsInv[1];
+        inPoint[2] = outPoint[2] = zField->GetValue(i) * scalingFactorsInv[2];
+           */
+        inPoint[0] = outPoint[0] = xField->GetValue(i) ;
+        inPoint[1] = outPoint[1] = yField->GetValue(i) ;
+        inPoint[2] = outPoint[2] = zField->GetValue(i) * scalingFactorsInv[2];
+
+        m_scaled_points->SetPoint(i,outPoint);
+
+        outPoint[0] = xField->GetValue(i) ;
+        outPoint[1] = yField->GetValue(i) ;
+        outPoint[2] = zField->GetValue(i) ;
+
+        m_points->SetPoint(i,outPoint);
+
+
+    }
+
+
+
+    return true;
+}
+
+void PointsPipe::activateGrid(bool active)
+{
+    if(active)
+    {
+
+        int size_x_plan1 = m_xRange[1] - m_xRange[0];
+
+        qDebug()<<"m_yRange[0]: "<<m_xRange[0]<<" scaled: "<<m_xRange[1]*scalingFactorsInv[0];
+        qDebug()<<"m_yRange[1]: "<<m_yRange[1]<<" scaled: "<<m_yRange[1]*scalingFactorsInv[0];
+        qDebug()<<"m_zRange[1]: "<<m_zRange[1]<<" scaled: "<<m_zRange[1]*scalingFactorsInv[0];
+        qDebug()<<"size_x_plan1: "<<size_x_plan1;
+
+
+        int num_element_x= ceil(size_x_plan1/5000.0);
+        double step=size_x_plan1/(double)num_element_x;
+
+        float *x= new float[num_element_x+2];
+        for (int i=0; i<num_element_x+2; i++)
+        {
+            // x[i]= (m_xRange[0]+(5000*i)) *scalingFactorsInv[0];
+            x[i]= (m_xRange[0]+(step*i)) *scalingFactorsInv[0];
+
+            qDebug()<<"x["<<i<<"]="<<x[i];
+
+        }
+
+        int size_y_plan1 = m_yRange[1] - m_yRange[0];
+
+        qDebug()<<"m_yRange[0]: "<<m_yRange[0]<<" scaled: "<<m_yRange[1]*scalingFactorsInv[1];
+        qDebug()<<"m_yRange[1]: "<<m_yRange[1]<<" scaled: "<<m_yRange[1]*scalingFactorsInv[1];
+        qDebug()<<"size_y_plan1: "<<size_y_plan1;
+        int num_element_y= ceil(size_y_plan1/5000.0);
+
+        qDebug()<<"num_element_y: "<<num_element_y;
+
+        step= size_y_plan1/(double)num_element_y;
+
+        qDebug()<<"step: "<<step;
+
+
+        float *y= new float[num_element_y+1];
+        for (int i=0; i<num_element_y+1; i++)
+        {
+            //y[i]=(m_yRange[0]+(5000*i))*scalingFactorsInv[1];
+            y[i]=(m_yRange[0]+(step*i))*scalingFactorsInv[1];
+            qDebug()<<"y["<<i<<"]="<<y[i];
+        }
+
+
+        int i;
+
+        // Create a rectilinear grid by defining three arrays specifying the
+        // coordinates in the x-y-z directions.
+        vtkFloatArray *xCoords = vtkFloatArray::New();
+        for (i=0; i<num_element_x+2; i++){
+
+            qDebug()<<"x["<<i<<"]="<<x[i];
+
+            xCoords->InsertNextValue(x[i]);
+        }
+        vtkFloatArray *yCoords = vtkFloatArray::New();
+        for (i=0; i<num_element_y+1; i++) yCoords->InsertNextValue(y[i]);
+
+        vtkFloatArray *zCoords = vtkFloatArray::New();
+        zCoords->InsertNextValue(m_zRange[0]*scalingFactorsInv[2]);
+
+        // The coordinates are assigned to the rectilinear grid. Make sure that
+        // the number of values in each of the XCoordinates, YCoordinates,
+        // and ZCoordinates is equal to what is defined in SetDimensions().
+        //
+        vtkRectilinearGrid *rgrid = vtkRectilinearGrid::New();
+        rgrid->SetDimensions(num_element_x,num_element_y,1);
+        rgrid->SetXCoordinates(xCoords);
+        rgrid->SetYCoordinates(yCoords);
+        rgrid->SetZCoordinates(zCoords);
+
+        // Extract a plane from the grid to see what we've got.
+        vtkRectilinearGridGeometryFilter *plane = vtkRectilinearGridGeometryFilter::New();
+        // plane->SetInput(rgrid);
+        plane->SetInputData(rgrid);
+        plane->SetExtent (0,10000, 0,100000, 4,4);
+
+        vtkPolyDataMapper *rgridMapper = vtkPolyDataMapper::New();
+        rgridMapper->SetInputConnection(plane->GetOutputPort());
+
+        vtkActor *wireActor = vtkActor::New();
+        wireActor->SetMapper(rgridMapper);
+        wireActor->GetProperty()->SetRepresentationToWireframe();
+        wireActor->GetProperty()->SetColor(0,1,0);
+
+        m_pRenderer->AddActor(wireActor);
+
+        /*
+        int size2_1 = m_xRange[1] - m_xRange[0];
+
+        float *x2= new float[size2_1/1000];
+        for (int i=0; i<size2_1/1000; i++)
+        {
+           // qDebug()<<"x["<<i<<"]="<<x2[i];
+        }
+
+        int size2_2 = m_yRange[1] - m_yRange[0];
+        float *y2= new float[size2_2/1000];
+        for (int i=0; i<size2_2/1000; i++)
+        {
+            y2[i]=(m_yRange[0]+(1000*i))*scalingFactorsInv[1];
+         //   qDebug()<<"y["<<i<<"]="<<y2[i];
+        }
+
+
+
+
+        // Create a rectilinear grid by defining three arrays specifying the
+        // coordinates in the x-y-z directions.
+        vtkFloatArray *xCoords2 = vtkFloatArray::New();
+          for (i=0; i<size2_1/1000; i++) xCoords2->InsertNextValue(x2[i]);
+
+        vtkFloatArray *yCoords2 = vtkFloatArray::New();
+        //for (i=0; i<33; i++) yCoords->InsertNextValue(y[i]);
+         for (i=0; i<size2_2/1000; i++) yCoords2->InsertNextValue(y2[i]);
+
+        vtkFloatArray *zCoords2 = vtkFloatArray::New();
+        zCoords2->InsertNextValue(m_zRange[0]*scalingFactorsInv[2]);
+
+        //qDebug()<<m_zRange[0]<<" sc:"<<scalingFactorsInv[2];
+
+        // The coordinates are assigned to the rectilinear grid. Make sure that
+        // the number of values in each of the XCoordinates, YCoordinates,
+        // and ZCoordinates is equal to what is defined in SetDimensions().
+        //
+        vtkRectilinearGrid *rgrid2 = vtkRectilinearGrid::New();
+        rgrid2->SetDimensions(size2_1/1000,size2_2/1000,1);
+        rgrid2->SetXCoordinates(xCoords2);
+        rgrid2->SetYCoordinates(yCoords2);
+        rgrid2->SetZCoordinates(zCoords2);
+
+        // Extract a plane from the grid to see what we've got.
+        vtkRectilinearGridGeometryFilter *plane2 = vtkRectilinearGridGeometryFilter::New();
+        plane2->SetInput(rgrid2);
+        plane2->SetExtent (0,10000, 0,100000, 4,4);
+
+        vtkPolyDataMapper *rgridMapper2 = vtkPolyDataMapper::New();
+        rgridMapper2->SetInputConnection(plane2->GetOutputPort());
+
+        vtkActor *wireActor2 = vtkActor::New();
+        wireActor2->SetMapper(rgridMapper2);
+        wireActor2->GetProperty()->SetRepresentationToWireframe();
+        wireActor2->GetProperty()->SetColor(1,0,0);
+
+        m_pRenderer->AddActor(wireActor2);
+*/
+
+
+
+
+
+    }
+    vtkwin->updateScene();
+}
+
+//---------------------------------------------------------------------
+void PointsPipe::activateScale (bool active)
+//---------------------------------------------------------------------
+{
+
+
+    if(active)
+        m_polyData->SetPoints(m_scaled_points);
+    else
+        m_polyData->SetPoints(m_points);
+
+    isScaleActive=active;
+
+    vtkwin->updateScene();
+
+
+    /*
+    for(vtkIdType i = 0; i < m_polyData->GetNumberOfPoints(); i++)
+    {
+        double outPoint[3];
+        m_polyData->GetPoint(i,outPoint);
+
+        outPoint[0]  =  outPoint[0] * scalingFactorsInv[0];
+        outPoint[1]  = outPoint[0] * scalingFactorsInv[1];
+        outPoint[2]  =outPoint[0] * scalingFactorsInv[2];
+
+        m_polyData->SetPoint(outPoint);
+
+
+    }
+*/
+
+    /*
+
+    m_polyData->GetPoints()->
+
+
+    m_polyData->SetPoints(m_points);
+    m_pMapper->SetInput (m_polyData );
+    m_pActor->SetMapper(m_pMapper);
+
+    vtkwin->updateScene();
+
+*/
+
+}
+
+void PointsPipe::setScaling ()
+{
+    m_glyphFilter->SetUseSecondScalar(true);
+    m_glyphFilter->SetUseThirdScalar(true);
+
+    m_glyphFilter->SetScaling(1);
+
+
+    if( heightscalar!="none" && scaleGlyphs!="none" && nGlyphs!=0 && nGlyphs!=1)
+        m_glyphFilter->SetInputScalarsSelectionY("x");
+    //m_glyphFilter->SetInputScalarsSelectionY(m_visOpt.heightscalar.c_str());
+
+    if( radiusscalar!="none" && scaleGlyphs!="none" && nGlyphs!=0)
+        m_glyphFilter->SetInputScalarsSelectionXZ("y");
+    // m_glyphFilter->SetInputScalarsSelectionXZ(m_visOpt.heightscalar.c_str());
+
+
+    if( nGlyphs!=0)
+        m_glyphFilter->SetScaleModeToScaleByScalar();
+    else
+        m_glyphFilter->ScalarVisibilityOff();
+
+
+}
+
+//---------------------------------------------------------------------
+void PointsPipe::setGlyphs ( int n)
+//---------------------------------------------------------------------
+{
+    int max=3000;
+
+    qDebug()<<"setGlyphs type: "<<n<<" m_nRows: "<<m_nRows<<" max: "<<max;
+
+    nGlyphs=n;
+    if ( m_nRows<max )
+    {
+        qDebug()<<"set Input m_glyph";
+        m_glyph->SetInputData(m_polyData );
+
+
+        if (isScaleActive)
+            m_glyph->SetScaleFactor ( 0.04 );
+        else
+            m_glyph->SetScaleFactor ( 2.5 );
+
+
+        qDebug()<<"set Input m_pMapper";
+        m_pMapper->SetInputConnection( m_glyph->GetOutputPort() );
+
+
+        if (nGlyphs==1)
+        {
+            m_sphere   = vtkSmartPointer<vtkSphereSource>::New();
+            setResolution ( );
+            setRadius ();
+            m_glyph->SetSourceData( m_sphere->GetOutput() );
+            qDebug()<<"post m_sphere";
+
+        }
+
+        else if (nGlyphs==2)
+        {
+            m_cone   = vtkConeSource::New();
+            setResolution ( );
+            setRadius ();
+            m_glyph->SetSourceData ( m_cone->GetOutput() );
+            m_cone->Delete();
+        }
+
+        else if (nGlyphs==3)
+        {
+            m_cylinder   = vtkCylinderSource::New();
+            setResolution ( );
+            setRadius ();
+            m_glyph->SetSourceData ( m_cylinder->GetOutput() );
+            m_cylinder->Delete();
+        }
+
+        else if (nGlyphs==4)
+        {
+            m_cube   = vtkCubeSource::New();
+            setRadius ();
+            m_glyph->SetSourceData ( m_cube->GetOutput() );
+            m_cube->Delete();
+        }
+
+
+    }
+}
+
+//---------------------------------------------------------------------
+void PointsPipe::setRadius ()
+//---------------------------------------------------------------------
+{
+    qDebug()<<"setRadius";
+
+    if (nGlyphs==1)
+    {
+        m_sphere->SetRadius ( radius);
+        qDebug()<<"post radius: "<<radius;
+
+    }
+
+    else if (nGlyphs==2)
+    {
+        m_cone->SetRadius ( radius );
+        m_cone->SetHeight (height );
+    }
+
+    else if (nGlyphs==3)
+    {
+        m_cylinder->SetRadius (radius );
+        m_cylinder->SetHeight ( height );
+    }
+    else if (nGlyphs==4)
+    {
+        m_cube->SetXLength ( radius );
+        m_cube->SetYLength ( height );
+        m_cube->SetZLength ( 1 );
+
+    }
+}
+
+//---------------------------------------------------------------------
+void PointsPipe::setResolution ()
+//---------------------------------------------------------------------
+{
+    qDebug()<<"Set Res";
+
+    if (nGlyphs==1)
+    {
+        m_sphere->SetPhiResolution ( 10 );
+        m_sphere->SetThetaResolution ( 20 );
+        qDebug()<<"post res";
+
+    }
+
+    else if (nGlyphs==2)
+        m_cone->SetResolution ( 10 );
+
+    else if (nGlyphs==3)
+        m_cylinder->SetResolution ( 10);
+
+
+}
+
+
+
diff --git a/Code/src/pointspipe.h b/Code/src/pointspipe.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b8068b0bf03b7fe1bc25a81001d61a332a18524
--- /dev/null
+++ b/Code/src/pointspipe.h
@@ -0,0 +1,125 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+ *   ugo.becciani@oact.inaf.it                                             *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef POINTSPIPE_H
+#define POINTSPIPE_H
+
+/**
+        @author Alessandro Costa <alessandro.costa@oact.inaf.it>
+        @author Ugo Becciani <ugo.becciani@oact.inaf.it>
+*/
+
+#include "pipe.h"
+//#include "vtkwindow.h"
+
+//#include "optionssetter.h"
+#include "vtkSmartPointer.h"
+
+class vtkLODActor;
+class vtkSphereSource;
+class vtkConeSource;
+class vtkCylinderSource;
+class vtkCubeSource;
+class vtkGlyph3D;
+class vtkPoints;
+class vtkUnstructuredGrid;
+class ExtendedGlyph3D;
+class vtkwindow_new;
+
+
+
+// Define a new frame type: this is going to be our main frame
+class PointsPipe: public Pipe
+{
+public:
+    PointsPipe(VSTableDesktop *table);
+
+    vtkRenderer *getRenderer();
+    vtkLODActor *getActor();
+    vtkPolyData *getPolyData();
+    vtkPolyDataMapper *getMapper();
+    vtkUnstructuredGrid *getUnstructuredGrid();
+    ~PointsPipe();
+    int createPipe();
+    int createPipeFor3dSelection();
+    void setGlyphs (int nGlyphs);
+
+    void setVtkWindow(vtkwindow_new *v);
+    std::string color;
+    std::string palette;
+    std::string scale;
+    bool showColorBar;
+    void setLookupTable( );
+    void setLookupTable (float from, float to );
+    void setLookupTableScale();
+    void setActiveScalar();
+    void initLut();
+    void addScalar(std::string myScalar, bool color);
+    void activateScale (bool active);
+    void activateGrid (bool active);
+    double actualFrom;
+    double actualTo;
+
+  /*
+    vtkFloatArray *xAxis;
+    vtkFloatArray *yAxis;
+    vtkFloatArray *zAxis;
+*/
+//  bool SetXYZ(vtkFloatArray *xField, vtkFloatArray *yField, vtkFloatArray *zField, bool scale=false  );
+    bool SetXYZ(vtkFloatArray *xField, vtkFloatArray *yField, vtkFloatArray *zField  );
+    void setScaling();
+protected:
+    void destroyAll();
+
+private:
+    void setRadius ();
+    void setResolution ();
+   // void setScaling ();
+
+
+    VSTableDesktop *m_VSTable;
+    vtkPolyDataMapper   *m_pMapper;
+    vtkLODActor         *m_pActor;
+    vtkPolyData         *m_polyData;
+    vtkGlyph3D          *m_glyph ;
+    vtkSmartPointer <vtkSphereSource> m_sphere;
+    vtkConeSource       *m_cone;
+    vtkCylinderSource   *m_cylinder;
+    vtkCubeSource       *m_cube;
+    vtkPoints           *m_points;
+    vtkPoints           *m_scaled_points;
+    vtkUnstructuredGrid * m_pUnstructuredGrid;
+    ExtendedGlyph3D     *m_glyphFilter;
+    vtkwindow_new *vtkwin;
+
+    bool isScaleActive;
+    double m_xRange[2] ,m_yRange[2] , m_zRange[2];
+    double scalingFactorsInv[3];
+    int nGlyphs;
+    //! value of radius and hieght for glyphs. the usere can use this with scaling or not. default is 1 for both
+    double radius,height ;
+    //! name of field the isd used for scale the glyphs by radius and/or heigth
+    std::string radiusscalar,heightscalar;
+    //! if is yes the user can select the scaling for radius and/or glyphs
+    std::string scaleGlyphs;
+};
+
+#endif
diff --git a/Code/src/qcustomplot.cpp b/Code/src/qcustomplot.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9d6eeb771a3207a32ffc196dd149921884273b6d
--- /dev/null
+++ b/Code/src/qcustomplot.cpp
@@ -0,0 +1,23538 @@
+/***************************************************************************
+**                                                                        **
+**  QCustomPlot, an easy to use, modern plotting widget for Qt            **
+**  Copyright (C) 2011-2015 Emanuel Eichhammer                            **
+**                                                                        **
+**  This program is free software: you can redistribute it and/or modify  **
+**  it under the terms of the GNU General Public License as published by  **
+**  the Free Software Foundation, either version 3 of the License, or     **
+**  (at your option) any later version.                                   **
+**                                                                        **
+**  This program is distributed in the hope that it will be useful,       **
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         **
+**  GNU General Public License for more details.                          **
+**                                                                        **
+**  You should have received a copy of the GNU General Public License     **
+**  along with this program.  If not, see http://www.gnu.org/licenses/.   **
+**                                                                        **
+****************************************************************************
+**           Author: Emanuel Eichhammer                                   **
+**  Website/Contact: http://www.qcustomplot.com/                          **
+**             Date: 25.04.15                                             **
+**          Version: 1.3.1                                                **
+****************************************************************************/
+
+#include "qcustomplot.h"
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPPainter
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPPainter
+  \brief QPainter subclass used internally
+
+  This QPainter subclass is used to provide some extended functionality e.g. for tweaking position
+  consistency between antialiased and non-antialiased painting. Further it provides workarounds
+  for QPainter quirks.
+
+  \warning This class intentionally hides non-virtual functions of QPainter, e.g. setPen, save and
+  restore. So while it is possible to pass a QCPPainter instance to a function that expects a
+  QPainter pointer, some of the workarounds and tweaks will be unavailable to the function (because
+  it will call the base class implementations of the functions actually hidden by QCPPainter).
+*/
+
+/*!
+  Creates a new QCPPainter instance and sets default values
+*/
+QCPPainter::QCPPainter() :
+  QPainter(),
+  mModes(pmDefault),
+  mIsAntialiasing(false)
+{
+  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
+  // a call to begin() will follow
+}
+
+/*!
+  Creates a new QCPPainter instance on the specified paint \a device and sets default values. Just
+  like the analogous QPainter constructor, begins painting on \a device immediately.
+
+  Like \ref begin, this method sets QPainter::NonCosmeticDefaultPen in Qt versions before Qt5.
+*/
+QCPPainter::QCPPainter(QPaintDevice *device) :
+  QPainter(device),
+  mModes(pmDefault),
+  mIsAntialiasing(false)
+{
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
+  if (isActive())
+    setRenderHint(QPainter::NonCosmeticDefaultPen);
+#endif
+}
+
+QCPPainter::~QCPPainter()
+{
+}
+
+/*!
+  Sets the pen of the painter and applies certain fixes to it, depending on the mode of this
+  QCPPainter.
+
+  \note this function hides the non-virtual base class implementation.
+*/
+void QCPPainter::setPen(const QPen &pen)
+{
+  QPainter::setPen(pen);
+  if (mModes.testFlag(pmNonCosmetic))
+    makeNonCosmetic();
+}
+
+/*! \overload
+
+  Sets the pen (by color) of the painter and applies certain fixes to it, depending on the mode of
+  this QCPPainter.
+
+  \note this function hides the non-virtual base class implementation.
+*/
+void QCPPainter::setPen(const QColor &color)
+{
+  QPainter::setPen(color);
+  if (mModes.testFlag(pmNonCosmetic))
+    makeNonCosmetic();
+}
+
+/*! \overload
+
+  Sets the pen (by style) of the painter and applies certain fixes to it, depending on the mode of
+  this QCPPainter.
+
+  \note this function hides the non-virtual base class implementation.
+*/
+void QCPPainter::setPen(Qt::PenStyle penStyle)
+{
+  QPainter::setPen(penStyle);
+  if (mModes.testFlag(pmNonCosmetic))
+    makeNonCosmetic();
+}
+
+/*! \overload
+
+  Works around a Qt bug introduced with Qt 4.8 which makes drawing QLineF unpredictable when
+  antialiasing is disabled. Thus when antialiasing is disabled, it rounds the \a line to
+  integer coordinates and then passes it to the original drawLine.
+
+  \note this function hides the non-virtual base class implementation.
+*/
+void QCPPainter::drawLine(const QLineF &line)
+{
+  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
+    QPainter::drawLine(line);
+  else
+    QPainter::drawLine(line.toLine());
+}
+
+/*!
+  Sets whether painting uses antialiasing or not. Use this method instead of using setRenderHint
+  with QPainter::Antialiasing directly, as it allows QCPPainter to regain pixel exactness between
+  antialiased and non-antialiased painting (Since Qt < 5.0 uses slightly different coordinate systems for
+  AA/Non-AA painting).
+*/
+void QCPPainter::setAntialiasing(bool enabled)
+{
+  setRenderHint(QPainter::Antialiasing, enabled);
+  if (mIsAntialiasing != enabled)
+  {
+    mIsAntialiasing = enabled;
+    if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
+    {
+      if (mIsAntialiasing)
+        translate(0.5, 0.5);
+      else
+        translate(-0.5, -0.5);
+    }
+  }
+}
+
+/*!
+  Sets the mode of the painter. This controls whether the painter shall adjust its
+  fixes/workarounds optimized for certain output devices.
+*/
+void QCPPainter::setModes(QCPPainter::PainterModes modes)
+{
+  mModes = modes;
+}
+
+/*!
+  Sets the QPainter::NonCosmeticDefaultPen in Qt versions before Qt5 after beginning painting on \a
+  device. This is necessary to get cosmetic pen consistency across Qt versions, because since Qt5,
+  all pens are non-cosmetic by default, and in Qt4 this render hint must be set to get that
+  behaviour.
+
+  The Constructor \ref QCPPainter(QPaintDevice *device) which directly starts painting also sets
+  the render hint as appropriate.
+
+  \note this function hides the non-virtual base class implementation.
+*/
+bool QCPPainter::begin(QPaintDevice *device)
+{
+  bool result = QPainter::begin(device);
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
+  if (result)
+    setRenderHint(QPainter::NonCosmeticDefaultPen);
+#endif
+  return result;
+}
+
+/*! \overload
+
+  Sets the mode of the painter. This controls whether the painter shall adjust its
+  fixes/workarounds optimized for certain output devices.
+*/
+void QCPPainter::setMode(QCPPainter::PainterMode mode, bool enabled)
+{
+  if (!enabled && mModes.testFlag(mode))
+    mModes &= ~mode;
+  else if (enabled && !mModes.testFlag(mode))
+    mModes |= mode;
+}
+
+/*!
+  Saves the painter (see QPainter::save). Since QCPPainter adds some new internal state to
+  QPainter, the save/restore functions are reimplemented to also save/restore those members.
+
+  \note this function hides the non-virtual base class implementation.
+
+  \see restore
+*/
+void QCPPainter::save()
+{
+  mAntialiasingStack.push(mIsAntialiasing);
+  QPainter::save();
+}
+
+/*!
+  Restores the painter (see QPainter::restore). Since QCPPainter adds some new internal state to
+  QPainter, the save/restore functions are reimplemented to also save/restore those members.
+
+  \note this function hides the non-virtual base class implementation.
+
+  \see save
+*/
+void QCPPainter::restore()
+{
+  if (!mAntialiasingStack.isEmpty())
+    mIsAntialiasing = mAntialiasingStack.pop();
+  else
+    qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
+  QPainter::restore();
+}
+
+/*!
+  Changes the pen width to 1 if it currently is 0. This function is called in the \ref setPen
+  overrides when the \ref pmNonCosmetic mode is set.
+*/
+void QCPPainter::makeNonCosmetic()
+{
+  if (qFuzzyIsNull(pen().widthF()))
+  {
+    QPen p = pen();
+    p.setWidth(1);
+    QPainter::setPen(p);
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPScatterStyle
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPScatterStyle
+  \brief Represents the visual appearance of scatter points
+
+  This class holds information about shape, color and size of scatter points. In plottables like
+  QCPGraph it is used to store how scatter points shall be drawn. For example, \ref
+  QCPGraph::setScatterStyle takes a QCPScatterStyle instance.
+
+  A scatter style consists of a shape (\ref setShape), a line color (\ref setPen) and possibly a
+  fill (\ref setBrush), if the shape provides a fillable area. Further, the size of the shape can
+  be controlled with \ref setSize.
+
+  \section QCPScatterStyle-defining Specifying a scatter style
+
+  You can set all these configurations either by calling the respective functions on an instance:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-1
+
+  Or you can use one of the various constructors that take different parameter combinations, making
+  it easy to specify a scatter style in a single call, like so:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-creation-2
+
+  \section QCPScatterStyle-undefinedpen Leaving the color/pen up to the plottable
+
+  There are two constructors which leave the pen undefined: \ref QCPScatterStyle() and \ref
+  QCPScatterStyle(ScatterShape shape, double size). If those constructors are used, a call to \ref
+  isPenDefined will return false. It leads to scatter points that inherit the pen from the
+  plottable that uses the scatter style. Thus, if such a scatter style is passed to QCPGraph, the line
+  color of the graph (\ref QCPGraph::setPen) will be used by the scatter points. This makes
+  it very convenient to set up typical scatter settings:
+
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpscatterstyle-shortcreation
+
+  Notice that it wasn't even necessary to explicitly call a QCPScatterStyle constructor. This works
+  because QCPScatterStyle provides a constructor that can transform a \ref ScatterShape directly
+  into a QCPScatterStyle instance (that's the \ref QCPScatterStyle(ScatterShape shape, double size)
+  constructor with a default for \a size). In those cases, C++ allows directly supplying a \ref
+  ScatterShape, where actually a QCPScatterStyle is expected.
+
+  \section QCPScatterStyle-custompath-and-pixmap Custom shapes and pixmaps
+
+  QCPScatterStyle supports drawing custom shapes and arbitrary pixmaps as scatter points.
+
+  For custom shapes, you can provide a QPainterPath with the desired shape to the \ref
+  setCustomPath function or call the constructor that takes a painter path. The scatter shape will
+  automatically be set to \ref ssCustom.
+
+  For pixmaps, you call \ref setPixmap with the desired QPixmap. Alternatively you can use the
+  constructor that takes a QPixmap. The scatter shape will automatically be set to \ref ssPixmap.
+  Note that \ref setSize does not influence the appearance of the pixmap.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn bool QCPScatterStyle::isNone() const
+
+  Returns whether the scatter shape is \ref ssNone.
+
+  \see setShape
+*/
+
+/*! \fn bool QCPScatterStyle::isPenDefined() const
+
+  Returns whether a pen has been defined for this scatter style.
+
+  The pen is undefined if a constructor is called that does not carry \a pen as parameter. Those are
+  \ref QCPScatterStyle() and \ref QCPScatterStyle(ScatterShape shape, double size). If the pen is
+  left undefined, the scatter color will be inherited from the plottable that uses this scatter
+  style.
+
+  \see setPen
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates a new QCPScatterStyle instance with size set to 6. No shape, pen or brush is defined.
+
+  Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited
+  from the plottable that uses this scatter style.
+*/
+QCPScatterStyle::QCPScatterStyle() :
+  mSize(6),
+  mShape(ssNone),
+  mPen(Qt::NoPen),
+  mBrush(Qt::NoBrush),
+  mPenDefined(false)
+{
+}
+
+/*!
+  Creates a new QCPScatterStyle instance with shape set to \a shape and size to \a size. No pen or
+  brush is defined.
+
+  Since the pen is undefined (\ref isPenDefined returns false), the scatter color will be inherited
+  from the plottable that uses this scatter style.
+*/
+QCPScatterStyle::QCPScatterStyle(ScatterShape shape, double size) :
+  mSize(size),
+  mShape(shape),
+  mPen(Qt::NoPen),
+  mBrush(Qt::NoBrush),
+  mPenDefined(false)
+{
+}
+
+/*!
+  Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color,
+  and size to \a size. No brush is defined, i.e. the scatter point will not be filled.
+*/
+QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, double size) :
+  mSize(size),
+  mShape(shape),
+  mPen(QPen(color)),
+  mBrush(Qt::NoBrush),
+  mPenDefined(true)
+{
+}
+
+/*!
+  Creates a new QCPScatterStyle instance with shape set to \a shape, the pen color set to \a color,
+  the brush color to \a fill (with a solid pattern), and size to \a size.
+*/
+QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
+  mSize(size),
+  mShape(shape),
+  mPen(QPen(color)),
+  mBrush(QBrush(fill)),
+  mPenDefined(true)
+{
+}
+
+/*!
+  Creates a new QCPScatterStyle instance with shape set to \a shape, the pen set to \a pen, the
+  brush to \a brush, and size to \a size.
+
+  \warning In some cases it might be tempting to directly use a pen style like <tt>Qt::NoPen</tt> as \a pen
+  and a color like <tt>Qt::blue</tt> as \a brush. Notice however, that the corresponding call\n
+  <tt>QCPScatterStyle(QCPScatterShape::ssCircle, Qt::NoPen, Qt::blue, 5)</tt>\n
+  doesn't necessarily lead C++ to use this constructor in some cases, but might mistake
+  <tt>Qt::NoPen</tt> for a QColor and use the
+  \ref QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size)
+  constructor instead (which will lead to an unexpected look of the scatter points). To prevent
+  this, be more explicit with the parameter types. For example, use <tt>QBrush(Qt::blue)</tt>
+  instead of just <tt>Qt::blue</tt>, to clearly point out to the compiler that this constructor is
+  wanted.
+*/
+QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
+  mSize(size),
+  mShape(shape),
+  mPen(pen),
+  mBrush(brush),
+  mPenDefined(pen.style() != Qt::NoPen)
+{
+}
+
+/*!
+  Creates a new QCPScatterStyle instance which will show the specified \a pixmap. The scatter shape
+  is set to \ref ssPixmap.
+*/
+QCPScatterStyle::QCPScatterStyle(const QPixmap &pixmap) :
+  mSize(5),
+  mShape(ssPixmap),
+  mPen(Qt::NoPen),
+  mBrush(Qt::NoBrush),
+  mPixmap(pixmap),
+  mPenDefined(false)
+{
+}
+
+/*!
+  Creates a new QCPScatterStyle instance with a custom shape that is defined via \a customPath. The
+  scatter shape is set to \ref ssCustom.
+
+  The custom shape line will be drawn with \a pen and filled with \a brush. The size has a slightly
+  different meaning than for built-in scatter points: The custom path will be drawn scaled by a
+  factor of \a size/6.0. Since the default \a size is 6, the custom path will appear at a its
+  natural size by default. To double the size of the path for example, set \a size to 12.
+*/
+QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
+  mSize(size),
+  mShape(ssCustom),
+  mPen(pen),
+  mBrush(brush),
+  mCustomPath(customPath),
+  mPenDefined(pen.style() != Qt::NoPen)
+{
+}
+
+/*!
+  Sets the size (pixel diameter) of the drawn scatter points to \a size.
+
+  \see setShape
+*/
+void QCPScatterStyle::setSize(double size)
+{
+  mSize = size;
+}
+
+/*!
+  Sets the shape to \a shape.
+
+  Note that the calls \ref setPixmap and \ref setCustomPath automatically set the shape to \ref
+  ssPixmap and \ref ssCustom, respectively.
+
+  \see setSize
+*/
+void QCPScatterStyle::setShape(QCPScatterStyle::ScatterShape shape)
+{
+  mShape = shape;
+}
+
+/*!
+  Sets the pen that will be used to draw scatter points to \a pen.
+
+  If the pen was previously undefined (see \ref isPenDefined), the pen is considered defined after
+  a call to this function, even if \a pen is <tt>Qt::NoPen</tt>.
+
+  \see setBrush
+*/
+void QCPScatterStyle::setPen(const QPen &pen)
+{
+  mPenDefined = true;
+  mPen = pen;
+}
+
+/*!
+  Sets the brush that will be used to fill scatter points to \a brush. Note that not all scatter
+  shapes have fillable areas. For example, \ref ssPlus does not while \ref ssCircle does.
+
+  \see setPen
+*/
+void QCPScatterStyle::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the pixmap that will be drawn as scatter point to \a pixmap.
+
+  Note that \ref setSize does not influence the appearance of the pixmap.
+
+  The scatter shape is automatically set to \ref ssPixmap.
+*/
+void QCPScatterStyle::setPixmap(const QPixmap &pixmap)
+{
+  setShape(ssPixmap);
+  mPixmap = pixmap;
+}
+
+/*!
+  Sets the custom shape that will be drawn as scatter point to \a customPath.
+
+  The scatter shape is automatically set to \ref ssCustom.
+*/
+void QCPScatterStyle::setCustomPath(const QPainterPath &customPath)
+{
+  setShape(ssCustom);
+  mCustomPath = customPath;
+}
+
+/*!
+  Applies the pen and the brush of this scatter style to \a painter. If this scatter style has an
+  undefined pen (\ref isPenDefined), sets the pen of \a painter to \a defaultPen instead.
+
+  This function is used by plottables (or any class that wants to draw scatters) just before a
+  number of scatters with this style shall be drawn with the \a painter.
+
+  \see drawShape
+*/
+void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
+{
+  painter->setPen(mPenDefined ? mPen : defaultPen);
+  painter->setBrush(mBrush);
+}
+
+/*!
+  Draws the scatter shape with \a painter at position \a pos.
+
+  This function does not modify the pen or the brush on the painter, as \ref applyTo is meant to be
+  called before scatter points are drawn with \ref drawShape.
+
+  \see applyTo
+*/
+void QCPScatterStyle::drawShape(QCPPainter *painter, QPointF pos) const
+{
+  drawShape(painter, pos.x(), pos.y());
+}
+
+/*! \overload
+  Draws the scatter shape with \a painter at position \a x and \a y.
+*/
+void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
+{
+  double w = mSize/2.0;
+  switch (mShape)
+  {
+    case ssNone: break;
+    case ssDot:
+    {
+      painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
+      break;
+    }
+    case ssCross:
+    {
+      painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
+      painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
+      break;
+    }
+    case ssPlus:
+    {
+      painter->drawLine(QLineF(x-w,   y, x+w,   y));
+      painter->drawLine(QLineF(  x, y+w,   x, y-w));
+      break;
+    }
+    case ssCircle:
+    {
+      painter->drawEllipse(QPointF(x , y), w, w);
+      break;
+    }
+    case ssDisc:
+    {
+      QBrush b = painter->brush();
+      painter->setBrush(painter->pen().color());
+      painter->drawEllipse(QPointF(x , y), w, w);
+      painter->setBrush(b);
+      break;
+    }
+    case ssSquare:
+    {
+      painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
+      break;
+    }
+    case ssDiamond:
+    {
+      painter->drawLine(QLineF(x-w,   y,   x, y-w));
+      painter->drawLine(QLineF(  x, y-w, x+w,   y));
+      painter->drawLine(QLineF(x+w,   y,   x, y+w));
+      painter->drawLine(QLineF(  x, y+w, x-w,   y));
+      break;
+    }
+    case ssStar:
+    {
+      painter->drawLine(QLineF(x-w,   y, x+w,   y));
+      painter->drawLine(QLineF(  x, y+w,   x, y-w));
+      painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
+      painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
+      break;
+    }
+    case ssTriangle:
+    {
+       painter->drawLine(QLineF(x-w, y+0.755*w, x+w, y+0.755*w));
+       painter->drawLine(QLineF(x+w, y+0.755*w,   x, y-0.977*w));
+       painter->drawLine(QLineF(  x, y-0.977*w, x-w, y+0.755*w));
+      break;
+    }
+    case ssTriangleInverted:
+    {
+       painter->drawLine(QLineF(x-w, y-0.755*w, x+w, y-0.755*w));
+       painter->drawLine(QLineF(x+w, y-0.755*w,   x, y+0.977*w));
+       painter->drawLine(QLineF(  x, y+0.977*w, x-w, y-0.755*w));
+      break;
+    }
+    case ssCrossSquare:
+    {
+       painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
+       painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
+       painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
+      break;
+    }
+    case ssPlusSquare:
+    {
+       painter->drawLine(QLineF(x-w,   y, x+w*0.95,   y));
+       painter->drawLine(QLineF(  x, y+w,        x, y-w));
+       painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
+      break;
+    }
+    case ssCrossCircle:
+    {
+       painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
+       painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
+       painter->drawEllipse(QPointF(x, y), w, w);
+      break;
+    }
+    case ssPlusCircle:
+    {
+       painter->drawLine(QLineF(x-w,   y, x+w,   y));
+       painter->drawLine(QLineF(  x, y+w,   x, y-w));
+       painter->drawEllipse(QPointF(x, y), w, w);
+      break;
+    }
+    case ssPeace:
+    {
+       painter->drawLine(QLineF(x, y-w,         x,       y+w));
+       painter->drawLine(QLineF(x,   y, x-w*0.707, y+w*0.707));
+       painter->drawLine(QLineF(x,   y, x+w*0.707, y+w*0.707));
+       painter->drawEllipse(QPointF(x, y), w, w);
+      break;
+    }
+    case ssPixmap:
+    {
+      painter->drawPixmap(x-mPixmap.width()*0.5, y-mPixmap.height()*0.5, mPixmap);
+      break;
+    }
+    case ssCustom:
+    {
+      QTransform oldTransform = painter->transform();
+      painter->translate(x, y);
+      painter->scale(mSize/6.0, mSize/6.0);
+      painter->drawPath(mCustomPath);
+      painter->setTransform(oldTransform);
+      break;
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPLayer
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPLayer
+  \brief A layer that may contain objects, to control the rendering order
+
+  The Layering system of QCustomPlot is the mechanism to control the rendering order of the
+  elements inside the plot.
+
+  It is based on the two classes QCPLayer and QCPLayerable. QCustomPlot holds an ordered list of
+  one or more instances of QCPLayer (see QCustomPlot::addLayer, QCustomPlot::layer,
+  QCustomPlot::moveLayer, etc.). When replotting, QCustomPlot goes through the list of layers
+  bottom to top and successively draws the layerables of the layers.
+
+  A QCPLayer contains an ordered list of QCPLayerable instances. QCPLayerable is an abstract base
+  class from which almost all visible objects derive, like axes, grids, graphs, items, etc.
+
+  Initially, QCustomPlot has five layers: "background", "grid", "main", "axes" and "legend" (in
+  that order). The top two layers "axes" and "legend" contain the default axes and legend, so they
+  will be drawn on top. In the middle, there is the "main" layer. It is initially empty and set as
+  the current layer (see QCustomPlot::setCurrentLayer). This means, all new plottables, items etc.
+  are created on this layer by default. Then comes the "grid" layer which contains the QCPGrid
+  instances (which belong tightly to QCPAxis, see \ref QCPAxis::grid). The Axis rect background
+  shall be drawn behind everything else, thus the default QCPAxisRect instance is placed on the
+  "background" layer. Of course, the layer affiliation of the individual objects can be changed as
+  required (\ref QCPLayerable::setLayer).
+
+  Controlling the ordering of objects is easy: Create a new layer in the position you want it to
+  be, e.g. above "main", with QCustomPlot::addLayer. Then set the current layer with
+  QCustomPlot::setCurrentLayer to that new layer and finally create the objects normally. They will
+  be placed on the new layer automatically, due to the current layer setting. Alternatively you
+  could have also ignored the current layer setting and just moved the objects with
+  QCPLayerable::setLayer to the desired layer after creating them.
+
+  It is also possible to move whole layers. For example, If you want the grid to be shown in front
+  of all plottables/items on the "main" layer, just move it above "main" with
+  QCustomPlot::moveLayer.
+
+  The rendering order within one layer is simply by order of creation or insertion. The item
+  created last (or added last to the layer), is drawn on top of all other objects on that layer.
+
+  When a layer is deleted, the objects on it are not deleted with it, but fall on the layer below
+  the deleted layer, see QCustomPlot::removeLayer.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QList<QCPLayerable*> QCPLayer::children() const
+
+  Returns a list of all layerables on this layer. The order corresponds to the rendering order:
+  layerables with higher indices are drawn above layerables with lower indices.
+*/
+
+/*! \fn int QCPLayer::index() const
+
+  Returns the index this layer has in the QCustomPlot. The index is the integer number by which this layer can be
+  accessed via \ref QCustomPlot::layer.
+
+  Layers with higher indices will be drawn above layers with lower indices.
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates a new QCPLayer instance.
+
+  Normally you shouldn't directly instantiate layers, use \ref QCustomPlot::addLayer instead.
+
+  \warning It is not checked that \a layerName is actually a unique layer name in \a parentPlot.
+  This check is only performed by \ref QCustomPlot::addLayer.
+*/
+QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
+  QObject(parentPlot),
+  mParentPlot(parentPlot),
+  mName(layerName),
+  mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
+  mVisible(true)
+{
+  // Note: no need to make sure layerName is unique, because layer
+  // management is done with QCustomPlot functions.
+}
+
+QCPLayer::~QCPLayer()
+{
+  // If child layerables are still on this layer, detach them, so they don't try to reach back to this
+  // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
+  // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
+  // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
+
+  while (!mChildren.isEmpty())
+    mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
+
+  if (mParentPlot->currentLayer() == this)
+    qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand.";
+}
+
+/*!
+  Sets whether this layer is visible or not. If \a visible is set to false, all layerables on this
+  layer will be invisible.
+
+  This function doesn't change the visibility property of the layerables (\ref
+  QCPLayerable::setVisible), but the \ref QCPLayerable::realVisibility of each layerable takes the
+  visibility of the parent layer into account.
+*/
+void QCPLayer::setVisible(bool visible)
+{
+  mVisible = visible;
+}
+
+/*! \internal
+
+  Adds the \a layerable to the list of this layer. If \a prepend is set to true, the layerable will
+  be prepended to the list, i.e. be drawn beneath the other layerables already in the list.
+
+  This function does not change the \a mLayer member of \a layerable to this layer. (Use
+  QCPLayerable::setLayer to change the layer of an object, not this function.)
+
+  \see removeChild
+*/
+void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
+{
+  if (!mChildren.contains(layerable))
+  {
+    if (prepend)
+      mChildren.prepend(layerable);
+    else
+      mChildren.append(layerable);
+  } else
+    qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
+}
+
+/*! \internal
+
+  Removes the \a layerable from the list of this layer.
+
+  This function does not change the \a mLayer member of \a layerable. (Use QCPLayerable::setLayer
+  to change the layer of an object, not this function.)
+
+  \see addChild
+*/
+void QCPLayer::removeChild(QCPLayerable *layerable)
+{
+  if (!mChildren.removeOne(layerable))
+    qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPLayerable
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPLayerable
+  \brief Base class for all drawable objects
+
+  This is the abstract base class most visible objects derive from, e.g. plottables, axes, grid
+  etc.
+
+  Every layerable is on a layer (QCPLayer) which allows controlling the rendering order by stacking
+  the layers accordingly.
+
+  For details about the layering mechanism, see the QCPLayer documentation.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QCPLayerable *QCPLayerable::parentLayerable() const
+
+  Returns the parent layerable of this layerable. The parent layerable is used to provide
+  visibility hierarchies in conjunction with the method \ref realVisibility. This way, layerables
+  only get drawn if their parent layerables are visible, too.
+
+  Note that a parent layerable is not necessarily also the QObject parent for memory management.
+  Further, a layerable doesn't always have a parent layerable, so this function may return 0.
+
+  A parent layerable is set implicitly with when placed inside layout elements and doesn't need to be
+  set manually by the user.
+*/
+
+/* end documentation of inline functions */
+/* start documentation of pure virtual functions */
+
+/*! \fn virtual void QCPLayerable::applyDefaultAntialiasingHint(QCPPainter *painter) const = 0
+  \internal
+
+  This function applies the default antialiasing setting to the specified \a painter, using the
+  function \ref applyAntialiasingHint. It is the antialiasing state the painter is put in, when
+  \ref draw is called on the layerable. If the layerable has multiple entities whose antialiasing
+  setting may be specified individually, this function should set the antialiasing state of the
+  most prominent entity. In this case however, the \ref draw function usually calls the specialized
+  versions of this function before drawing each entity, effectively overriding the setting of the
+  default antialiasing hint.
+
+  <b>First example:</b> QCPGraph has multiple entities that have an antialiasing setting: The graph
+  line, fills, scatters and error bars. Those can be configured via QCPGraph::setAntialiased,
+  QCPGraph::setAntialiasedFill, QCPGraph::setAntialiasedScatters etc. Consequently, there isn't
+  only the QCPGraph::applyDefaultAntialiasingHint function (which corresponds to the graph line's
+  antialiasing), but specialized ones like QCPGraph::applyFillAntialiasingHint and
+  QCPGraph::applyScattersAntialiasingHint. So before drawing one of those entities, QCPGraph::draw
+  calls the respective specialized applyAntialiasingHint function.
+
+  <b>Second example:</b> QCPItemLine consists only of a line so there is only one antialiasing
+  setting which can be controlled with QCPItemLine::setAntialiased. (This function is inherited by
+  all layerables. The specialized functions, as seen on QCPGraph, must be added explicitly to the
+  respective layerable subclass.) Consequently it only has the normal
+  QCPItemLine::applyDefaultAntialiasingHint. The \ref QCPItemLine::draw function doesn't need to
+  care about setting any antialiasing states, because the default antialiasing hint is already set
+  on the painter when the \ref draw function is called, and that's the state it wants to draw the
+  line with.
+*/
+
+/*! \fn virtual void QCPLayerable::draw(QCPPainter *painter) const = 0
+  \internal
+
+  This function draws the layerable with the specified \a painter. It is only called by
+  QCustomPlot, if the layerable is visible (\ref setVisible).
+
+  Before this function is called, the painter's antialiasing state is set via \ref
+  applyDefaultAntialiasingHint, see the documentation there. Further, the clipping rectangle was
+  set to \ref clipRect.
+*/
+
+/* end documentation of pure virtual functions */
+/* start documentation of signals */
+
+/*! \fn void QCPLayerable::layerChanged(QCPLayer *newLayer);
+
+  This signal is emitted when the layer of this layerable changes, i.e. this layerable is moved to
+  a different layer.
+
+  \see setLayer
+*/
+
+/* end documentation of signals */
+
+/*!
+  Creates a new QCPLayerable instance.
+
+  Since QCPLayerable is an abstract base class, it can't be instantiated directly. Use one of the
+  derived classes.
+
+  If \a plot is provided, it automatically places itself on the layer named \a targetLayer. If \a
+  targetLayer is an empty string, it places itself on the current layer of the plot (see \ref
+  QCustomPlot::setCurrentLayer).
+
+  It is possible to provide 0 as \a plot. In that case, you should assign a parent plot at a later
+  time with \ref initializeParentPlot.
+
+  The layerable's parent layerable is set to \a parentLayerable, if provided. Direct layerable parents
+  are mainly used to control visibility in a hierarchy of layerables. This means a layerable is
+  only drawn, if all its ancestor layerables are also visible. Note that \a parentLayerable does
+  not become the QObject-parent (for memory management) of this layerable, \a plot does.
+*/
+QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
+  QObject(plot),
+  mVisible(true),
+  mParentPlot(plot),
+  mParentLayerable(parentLayerable),
+  mLayer(0),
+  mAntialiased(true)
+{
+  if (mParentPlot)
+  {
+    if (targetLayer.isEmpty())
+      setLayer(mParentPlot->currentLayer());
+    else if (!setLayer(targetLayer))
+      qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
+  }
+}
+
+QCPLayerable::~QCPLayerable()
+{
+  if (mLayer)
+  {
+    mLayer->removeChild(this);
+    mLayer = 0;
+  }
+}
+
+/*!
+  Sets the visibility of this layerable object. If an object is not visible, it will not be drawn
+  on the QCustomPlot surface, and user interaction with it (e.g. click and selection) is not
+  possible.
+*/
+void QCPLayerable::setVisible(bool on)
+{
+  mVisible = on;
+}
+
+/*!
+  Sets the \a layer of this layerable object. The object will be placed on top of the other objects
+  already on \a layer.
+
+  Returns true on success, i.e. if \a layer is a valid layer.
+*/
+bool QCPLayerable::setLayer(QCPLayer *layer)
+{
+  return moveToLayer(layer, false);
+}
+
+/*! \overload
+  Sets the layer of this layerable object by name
+
+  Returns true on success, i.e. if \a layerName is a valid layer name.
+*/
+bool QCPLayerable::setLayer(const QString &layerName)
+{
+  if (!mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
+    return false;
+  }
+  if (QCPLayer *layer = mParentPlot->layer(layerName))
+  {
+    return setLayer(layer);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
+    return false;
+  }
+}
+
+/*!
+  Sets whether this object will be drawn antialiased or not.
+
+  Note that antialiasing settings may be overridden by QCustomPlot::setAntialiasedElements and
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPLayerable::setAntialiased(bool enabled)
+{
+  mAntialiased = enabled;
+}
+
+/*!
+  Returns whether this layerable is visible, taking the visibility of the layerable parent and the
+  visibility of the layer this layerable is on into account. This is the method that is consulted
+  to decide whether a layerable shall be drawn or not.
+
+  If this layerable has a direct layerable parent (usually set via hierarchies implemented in
+  subclasses, like in the case of QCPLayoutElement), this function returns true only if this
+  layerable has its visibility set to true and the parent layerable's \ref realVisibility returns
+  true.
+
+  If this layerable doesn't have a direct layerable parent, returns the state of this layerable's
+  visibility.
+*/
+bool QCPLayerable::realVisibility() const
+{
+  return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
+}
+
+/*!
+  This function is used to decide whether a click hits a layerable object or not.
+
+  \a pos is a point in pixel coordinates on the QCustomPlot surface. This function returns the
+  shortest pixel distance of this point to the object. If the object is either invisible or the
+  distance couldn't be determined, -1.0 is returned. Further, if \a onlySelectable is true and the
+  object is not selectable, -1.0 is returned, too.
+
+  If the object is represented not by single lines but by an area like a \ref QCPItemText or the
+  bars of a \ref QCPBars plottable, a click inside the area should also be considered a hit. In
+  these cases this function thus returns a constant value greater zero but still below the parent
+  plot's selection tolerance. (typically the selectionTolerance multiplied by 0.99).
+
+  Providing a constant value for area objects allows selecting line objects even when they are
+  obscured by such area objects, by clicking close to the lines (i.e. closer than
+  0.99*selectionTolerance).
+
+  The actual setting of the selection state is not done by this function. This is handled by the
+  parent QCustomPlot when the mouseReleaseEvent occurs, and the finally selected object is notified
+  via the selectEvent/deselectEvent methods.
+
+  \a details is an optional output parameter. Every layerable subclass may place any information
+  in \a details. This information will be passed to \ref selectEvent when the parent QCustomPlot
+  decides on the basis of this selectTest call, that the object was successfully selected. The
+  subsequent call to \ref selectEvent will carry the \a details. This is useful for multi-part
+  objects (like QCPAxis). This way, a possibly complex calculation to decide which part was clicked
+  is only done once in \ref selectTest. The result (i.e. the actually clicked part) can then be
+  placed in \a details. So in the subsequent \ref selectEvent, the decision which part was
+  selected doesn't have to be done a second time for a single selection operation.
+
+  You may pass 0 as \a details to indicate that you are not interested in those selection details.
+
+  \see selectEvent, deselectEvent, QCustomPlot::setInteractions
+*/
+double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(pos)
+  Q_UNUSED(onlySelectable)
+  Q_UNUSED(details)
+  return -1.0;
+}
+
+/*! \internal
+
+  Sets the parent plot of this layerable. Use this function once to set the parent plot if you have
+  passed 0 in the constructor. It can not be used to move a layerable from one QCustomPlot to
+  another one.
+
+  Note that, unlike when passing a non-null parent plot in the constructor, this function does not
+  make \a parentPlot the QObject-parent of this layerable. If you want this, call
+  QObject::setParent(\a parentPlot) in addition to this function.
+
+  Further, you will probably want to set a layer (\ref setLayer) after calling this function, to
+  make the layerable appear on the QCustomPlot.
+
+  The parent plot change will be propagated to subclasses via a call to \ref parentPlotInitialized
+  so they can react accordingly (e.g. also initialize the parent plot of child layerables, like
+  QCPLayout does).
+*/
+void QCPLayerable::initializeParentPlot(QCustomPlot *parentPlot)
+{
+  if (mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
+    return;
+  }
+
+  if (!parentPlot)
+    qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
+
+  mParentPlot = parentPlot;
+  parentPlotInitialized(mParentPlot);
+}
+
+/*! \internal
+
+  Sets the parent layerable of this layerable to \a parentLayerable. Note that \a parentLayerable does not
+  become the QObject-parent (for memory management) of this layerable.
+
+  The parent layerable has influence on the return value of the \ref realVisibility method. Only
+  layerables with a fully visible parent tree will return true for \ref realVisibility, and thus be
+  drawn.
+
+  \see realVisibility
+*/
+void QCPLayerable::setParentLayerable(QCPLayerable *parentLayerable)
+{
+  mParentLayerable = parentLayerable;
+}
+
+/*! \internal
+
+  Moves this layerable object to \a layer. If \a prepend is true, this object will be prepended to
+  the new layer's list, i.e. it will be drawn below the objects already on the layer. If it is
+  false, the object will be appended.
+
+  Returns true on success, i.e. if \a layer is a valid layer.
+*/
+bool QCPLayerable::moveToLayer(QCPLayer *layer, bool prepend)
+{
+  if (layer && !mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
+    return false;
+  }
+  if (layer && layer->parentPlot() != mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
+    return false;
+  }
+
+  QCPLayer *oldLayer = mLayer;
+  if (mLayer)
+    mLayer->removeChild(this);
+  mLayer = layer;
+  if (mLayer)
+    mLayer->addChild(this, prepend);
+  if (mLayer != oldLayer)
+    emit layerChanged(mLayer);
+  return true;
+}
+
+/*! \internal
+
+  Sets the QCPainter::setAntialiasing state on the provided \a painter, depending on the \a
+  localAntialiased value as well as the overrides \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements. Which override enum this function takes into account is
+  controlled via \a overrideElement.
+*/
+void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
+{
+  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
+    painter->setAntialiasing(false);
+  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
+    painter->setAntialiasing(true);
+  else
+    painter->setAntialiasing(localAntialiased);
+}
+
+/*! \internal
+
+  This function is called by \ref initializeParentPlot, to allow subclasses to react on the setting
+  of a parent plot. This is the case when 0 was passed as parent plot in the constructor, and the
+  parent plot is set at a later time.
+
+  For example, QCPLayoutElement/QCPLayout hierarchies may be created independently of any
+  QCustomPlot at first. When they are then added to a layout inside the QCustomPlot, the top level
+  element of the hierarchy gets its parent plot initialized with \ref initializeParentPlot. To
+  propagate the parent plot to all the children of the hierarchy, the top level element then uses
+  this function to pass the parent plot on to its child elements.
+
+  The default implementation does nothing.
+
+  \see initializeParentPlot
+*/
+void QCPLayerable::parentPlotInitialized(QCustomPlot *parentPlot)
+{
+   Q_UNUSED(parentPlot)
+}
+
+/*! \internal
+
+  Returns the selection category this layerable shall belong to. The selection category is used in
+  conjunction with \ref QCustomPlot::setInteractions to control which objects are selectable and
+  which aren't.
+
+  Subclasses that don't fit any of the normal \ref QCP::Interaction values can use \ref
+  QCP::iSelectOther. This is what the default implementation returns.
+
+  \see QCustomPlot::setInteractions
+*/
+QCP::Interaction QCPLayerable::selectionCategory() const
+{
+  return QCP::iSelectOther;
+}
+
+/*! \internal
+
+  Returns the clipping rectangle of this layerable object. By default, this is the viewport of the
+  parent QCustomPlot. Specific subclasses may reimplement this function to provide different
+  clipping rects.
+
+  The returned clipping rect is set on the painter before the draw function of the respective
+  object is called.
+*/
+QRect QCPLayerable::clipRect() const
+{
+  if (mParentPlot)
+    return mParentPlot->viewport();
+  else
+    return QRect();
+}
+
+/*! \internal
+
+  This event is called when the layerable shall be selected, as a consequence of a click by the
+  user. Subclasses should react to it by setting their selection state appropriately. The default
+  implementation does nothing.
+
+  \a event is the mouse event that caused the selection. \a additive indicates, whether the user
+  was holding the multi-select-modifier while performing the selection (see \ref
+  QCustomPlot::setMultiSelectModifier). if \a additive is true, the selection state must be toggled
+  (i.e. become selected when unselected and unselected when selected).
+
+  Every selectEvent is preceded by a call to \ref selectTest, which has returned positively (i.e.
+  returned a value greater than 0 and less than the selection tolerance of the parent QCustomPlot).
+  The \a details data you output from \ref selectTest is fed back via \a details here. You may
+  use it to transport any kind of information from the selectTest to the possibly subsequent
+  selectEvent. Usually \a details is used to transfer which part was clicked, if it is a layerable
+  that has multiple individually selectable parts (like QCPAxis). This way selectEvent doesn't need
+  to do the calculation again to find out which part was actually clicked.
+
+  \a selectionStateChanged is an output parameter. If the pointer is non-null, this function must
+  set the value either to true or false, depending on whether the selection state of this layerable
+  was actually changed. For layerables that only are selectable as a whole and not in parts, this
+  is simple: if \a additive is true, \a selectionStateChanged must also be set to true, because the
+  selection toggles. If \a additive is false, \a selectionStateChanged is only set to true, if the
+  layerable was previously unselected and now is switched to the selected state.
+
+  \see selectTest, deselectEvent
+*/
+void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
+{
+  Q_UNUSED(event)
+  Q_UNUSED(additive)
+  Q_UNUSED(details)
+  Q_UNUSED(selectionStateChanged)
+}
+
+/*! \internal
+
+  This event is called when the layerable shall be deselected, either as consequence of a user
+  interaction or a call to \ref QCustomPlot::deselectAll. Subclasses should react to it by
+  unsetting their selection appropriately.
+
+  just as in \ref selectEvent, the output parameter \a selectionStateChanged (if non-null), must
+  return true or false when the selection state of this layerable has changed or not changed,
+  respectively.
+
+  \see selectTest, selectEvent
+*/
+void QCPLayerable::deselectEvent(bool *selectionStateChanged)
+{
+  Q_UNUSED(selectionStateChanged)
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPRange
+////////////////////////////////////////////////////////////////////////////////////////////////////
+/*! \class QCPRange
+  \brief Represents the range an axis is encompassing.
+
+  contains a \a lower and \a upper double value and provides convenience input, output and
+  modification functions.
+
+  \see QCPAxis::setRange
+*/
+
+/*!
+  Minimum range size (\a upper - \a lower) the range changing functions will accept. Smaller
+  intervals would cause errors due to the 11-bit exponent of double precision numbers,
+  corresponding to a minimum magnitude of roughly 1e-308.
+  \see validRange, maxRange
+*/
+const double QCPRange::minRange = 1e-280;
+
+/*!
+  Maximum values (negative and positive) the range will accept in range-changing functions.
+  Larger absolute values would cause errors due to the 11-bit exponent of double precision numbers,
+  corresponding to a maximum magnitude of roughly 1e308.
+  Since the number of planck-volumes in the entire visible universe is only ~1e183, this should
+  be enough.
+  \see validRange, minRange
+*/
+const double QCPRange::maxRange = 1e250;
+
+/*!
+  Constructs a range with \a lower and \a upper set to zero.
+*/
+QCPRange::QCPRange() :
+  lower(0),
+  upper(0)
+{
+}
+
+/*! \overload
+  Constructs a range with the specified \a lower and \a upper values.
+*/
+QCPRange::QCPRange(double lower, double upper) :
+  lower(lower),
+  upper(upper)
+{
+  normalize();
+}
+
+/*!
+  Returns the size of the range, i.e. \a upper-\a lower
+*/
+double QCPRange::size() const
+{
+  return upper-lower;
+}
+
+/*!
+  Returns the center of the range, i.e. (\a upper+\a lower)*0.5
+*/
+double QCPRange::center() const
+{
+  return (upper+lower)*0.5;
+}
+
+/*!
+  Makes sure \a lower is numerically smaller than \a upper. If this is not the case, the values
+  are swapped.
+*/
+void QCPRange::normalize()
+{
+  if (lower > upper)
+    qSwap(lower, upper);
+}
+
+/*!
+  Expands this range such that \a otherRange is contained in the new range. It is assumed that both
+  this range and \a otherRange are normalized (see \ref normalize).
+
+  If \a otherRange is already inside the current range, this function does nothing.
+
+  \see expanded
+*/
+void QCPRange::expand(const QCPRange &otherRange)
+{
+  if (lower > otherRange.lower)
+    lower = otherRange.lower;
+  if (upper < otherRange.upper)
+    upper = otherRange.upper;
+}
+
+
+/*!
+  Returns an expanded range that contains this and \a otherRange. It is assumed that both this
+  range and \a otherRange are normalized (see \ref normalize).
+
+  \see expand
+*/
+QCPRange QCPRange::expanded(const QCPRange &otherRange) const
+{
+  QCPRange result = *this;
+  result.expand(otherRange);
+  return result;
+}
+
+/*!
+  Returns a sanitized version of the range. Sanitized means for logarithmic scales, that
+  the range won't span the positive and negative sign domain, i.e. contain zero. Further
+  \a lower will always be numerically smaller (or equal) to \a upper.
+
+  If the original range does span positive and negative sign domains or contains zero,
+  the returned range will try to approximate the original range as good as possible.
+  If the positive interval of the original range is wider than the negative interval, the
+  returned range will only contain the positive interval, with lower bound set to \a rangeFac or
+  \a rangeFac *\a upper, whichever is closer to zero. Same procedure is used if the negative interval
+  is wider than the positive interval, this time by changing the \a upper bound.
+*/
+QCPRange QCPRange::sanitizedForLogScale() const
+{
+  double rangeFac = 1e-3;
+  QCPRange sanitizedRange(lower, upper);
+  sanitizedRange.normalize();
+  // can't have range spanning negative and positive values in log plot, so change range to fix it
+  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
+  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
+  {
+    // case lower is 0
+    if (rangeFac < sanitizedRange.upper*rangeFac)
+      sanitizedRange.lower = rangeFac;
+    else
+      sanitizedRange.lower = sanitizedRange.upper*rangeFac;
+  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
+  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
+  {
+    // case upper is 0
+    if (-rangeFac > sanitizedRange.lower*rangeFac)
+      sanitizedRange.upper = -rangeFac;
+    else
+      sanitizedRange.upper = sanitizedRange.lower*rangeFac;
+  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
+  {
+    // find out whether negative or positive interval is wider to decide which sign domain will be chosen
+    if (-sanitizedRange.lower > sanitizedRange.upper)
+    {
+      // negative is wider, do same as in case upper is 0
+      if (-rangeFac > sanitizedRange.lower*rangeFac)
+        sanitizedRange.upper = -rangeFac;
+      else
+        sanitizedRange.upper = sanitizedRange.lower*rangeFac;
+    } else
+    {
+      // positive is wider, do same as in case lower is 0
+      if (rangeFac < sanitizedRange.upper*rangeFac)
+        sanitizedRange.lower = rangeFac;
+      else
+        sanitizedRange.lower = sanitizedRange.upper*rangeFac;
+    }
+  }
+  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
+  return sanitizedRange;
+}
+
+/*!
+  Returns a sanitized version of the range. Sanitized means for linear scales, that
+  \a lower will always be numerically smaller (or equal) to \a upper.
+*/
+QCPRange QCPRange::sanitizedForLinScale() const
+{
+  QCPRange sanitizedRange(lower, upper);
+  sanitizedRange.normalize();
+  return sanitizedRange;
+}
+
+/*!
+  Returns true when \a value lies within or exactly on the borders of the range.
+*/
+bool QCPRange::contains(double value) const
+{
+  return value >= lower && value <= upper;
+}
+
+/*!
+  Checks, whether the specified range is within valid bounds, which are defined
+  as QCPRange::maxRange and QCPRange::minRange.
+  A valid range means:
+  \li range bounds within -maxRange and maxRange
+  \li range size above minRange
+  \li range size below maxRange
+*/
+bool QCPRange::validRange(double lower, double upper)
+{
+  /*
+  return (lower > -maxRange &&
+          upper < maxRange &&
+          qAbs(lower-upper) > minRange &&
+          (lower < -minRange || lower > minRange) &&
+          (upper < -minRange || upper > minRange));
+          */
+  return (lower > -maxRange &&
+          upper < maxRange &&
+          qAbs(lower-upper) > minRange &&
+          qAbs(lower-upper) < maxRange);
+}
+
+/*!
+  \overload
+  Checks, whether the specified range is within valid bounds, which are defined
+  as QCPRange::maxRange and QCPRange::minRange.
+  A valid range means:
+  \li range bounds within -maxRange and maxRange
+  \li range size above minRange
+  \li range size below maxRange
+*/
+bool QCPRange::validRange(const QCPRange &range)
+{
+  /*
+  return (range.lower > -maxRange &&
+          range.upper < maxRange &&
+          qAbs(range.lower-range.upper) > minRange &&
+          qAbs(range.lower-range.upper) < maxRange &&
+          (range.lower < -minRange || range.lower > minRange) &&
+          (range.upper < -minRange || range.upper > minRange));
+          */
+  return (range.lower > -maxRange &&
+          range.upper < maxRange &&
+          qAbs(range.lower-range.upper) > minRange &&
+          qAbs(range.lower-range.upper) < maxRange);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPMarginGroup
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPMarginGroup
+  \brief A margin group allows synchronization of margin sides if working with multiple layout elements.
+
+  QCPMarginGroup allows you to tie a margin side of two or more layout elements together, such that
+  they will all have the same size, based on the largest required margin in the group.
+
+  \n
+  \image html QCPMarginGroup.png "Demonstration of QCPMarginGroup"
+  \n
+
+  In certain situations it is desirable that margins at specific sides are synchronized across
+  layout elements. For example, if one QCPAxisRect is below another one in a grid layout, it will
+  provide a cleaner look to the user if the left and right margins of the two axis rects are of the
+  same size. The left axis of the top axis rect will then be at the same horizontal position as the
+  left axis of the lower axis rect, making them appear aligned. The same applies for the right
+  axes. This is what QCPMarginGroup makes possible.
+
+  To add/remove a specific side of a layout element to/from a margin group, use the \ref
+  QCPLayoutElement::setMarginGroup method. To completely break apart the margin group, either call
+  \ref clear, or just delete the margin group.
+
+  \section QCPMarginGroup-example Example
+
+  First create a margin group:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-1
+  Then set this group on the layout element sides:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpmargingroup-creation-2
+  Here, we've used the first two axis rects of the plot and synchronized their left margins with
+  each other and their right margins with each other.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QList<QCPLayoutElement*> QCPMarginGroup::elements(QCP::MarginSide side) const
+
+  Returns a list of all layout elements that have their margin \a side associated with this margin
+  group.
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates a new QCPMarginGroup instance in \a parentPlot.
+*/
+QCPMarginGroup::QCPMarginGroup(QCustomPlot *parentPlot) :
+  QObject(parentPlot),
+  mParentPlot(parentPlot)
+{
+  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
+  mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
+  mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
+  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
+}
+
+QCPMarginGroup::~QCPMarginGroup()
+{
+  clear();
+}
+
+/*!
+  Returns whether this margin group is empty. If this function returns true, no layout elements use
+  this margin group to synchronize margin sides.
+*/
+bool QCPMarginGroup::isEmpty() const
+{
+  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
+  while (it.hasNext())
+  {
+    it.next();
+    if (!it.value().isEmpty())
+      return false;
+  }
+  return true;
+}
+
+/*!
+  Clears this margin group. The synchronization of the margin sides that use this margin group is
+  lifted and they will use their individual margin sizes again.
+*/
+void QCPMarginGroup::clear()
+{
+  // make all children remove themselves from this margin group:
+  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
+  while (it.hasNext())
+  {
+    it.next();
+    const QList<QCPLayoutElement*> elements = it.value();
+    for (int i=elements.size()-1; i>=0; --i)
+      elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
+  }
+}
+
+/*! \internal
+
+  Returns the synchronized common margin for \a side. This is the margin value that will be used by
+  the layout element on the respective side, if it is part of this margin group.
+
+  The common margin is calculated by requesting the automatic margin (\ref
+  QCPLayoutElement::calculateAutoMargin) of each element associated with \a side in this margin
+  group, and choosing the largest returned value. (QCPLayoutElement::minimumMargins is taken into
+  account, too.)
+*/
+int QCPMarginGroup::commonMargin(QCP::MarginSide side) const
+{
+  // query all automatic margins of the layout elements in this margin group side and find maximum:
+  int result = 0;
+  const QList<QCPLayoutElement*> elements = mChildren.value(side);
+  for (int i=0; i<elements.size(); ++i)
+  {
+    if (!elements.at(i)->autoMargins().testFlag(side))
+      continue;
+    int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
+    if (m > result)
+      result = m;
+  }
+  return result;
+}
+
+/*! \internal
+
+  Adds \a element to the internal list of child elements, for the margin \a side.
+
+  This function does not modify the margin group property of \a element.
+*/
+void QCPMarginGroup::addChild(QCP::MarginSide side, QCPLayoutElement *element)
+{
+  if (!mChildren[side].contains(element))
+    mChildren[side].append(element);
+  else
+    qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
+}
+
+/*! \internal
+
+  Removes \a element from the internal list of child elements, for the margin \a side.
+
+  This function does not modify the margin group property of \a element.
+*/
+void QCPMarginGroup::removeChild(QCP::MarginSide side, QCPLayoutElement *element)
+{
+  if (!mChildren[side].removeOne(element))
+    qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPLayoutElement
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPLayoutElement
+  \brief The abstract base class for all objects that form \ref thelayoutsystem "the layout system".
+
+  This is an abstract base class. As such, it can't be instantiated directly, rather use one of its subclasses.
+
+  A Layout element is a rectangular object which can be placed in layouts. It has an outer rect
+  (QCPLayoutElement::outerRect) and an inner rect (\ref QCPLayoutElement::rect). The difference
+  between outer and inner rect is called its margin. The margin can either be set to automatic or
+  manual (\ref setAutoMargins) on a per-side basis. If a side is set to manual, that margin can be
+  set explicitly with \ref setMargins and will stay fixed at that value. If it's set to automatic,
+  the layout element subclass will control the value itself (via \ref calculateAutoMargin).
+
+  Layout elements can be placed in layouts (base class QCPLayout) like QCPLayoutGrid. The top level
+  layout is reachable via \ref QCustomPlot::plotLayout, and is a \ref QCPLayoutGrid. Since \ref
+  QCPLayout itself derives from \ref QCPLayoutElement, layouts can be nested.
+
+  Thus in QCustomPlot one can divide layout elements into two categories: The ones that are
+  invisible by themselves, because they don't draw anything. Their only purpose is to manage the
+  position and size of other layout elements. This category of layout elements usually use
+  QCPLayout as base class. Then there is the category of layout elements which actually draw
+  something. For example, QCPAxisRect, QCPLegend and QCPPlotTitle are of this category. This does
+  not necessarily mean that the latter category can't have child layout elements. QCPLegend for
+  instance, actually derives from QCPLayoutGrid and the individual legend items are child layout
+  elements in the grid layout.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QCPLayout *QCPLayoutElement::layout() const
+
+  Returns the parent layout of this layout element.
+*/
+
+/*! \fn QRect QCPLayoutElement::rect() const
+
+  Returns the inner rect of this layout element. The inner rect is the outer rect (\ref
+  setOuterRect) shrinked by the margins (\ref setMargins, \ref setAutoMargins).
+
+  In some cases, the area between outer and inner rect is left blank. In other cases the margin
+  area is used to display peripheral graphics while the main content is in the inner rect. This is
+  where automatic margin calculation becomes interesting because it allows the layout element to
+  adapt the margins to the peripheral graphics it wants to draw. For example, \ref QCPAxisRect
+  draws the axis labels and tick labels in the margin area, thus needs to adjust the margins (if
+  \ref setAutoMargins is enabled) according to the space required by the labels of the axes.
+*/
+
+/*! \fn virtual void QCPLayoutElement::mousePressEvent(QMouseEvent *event)
+
+  This event is called, if the mouse was pressed while being inside the outer rect of this layout
+  element.
+*/
+
+/*! \fn virtual void QCPLayoutElement::mouseMoveEvent(QMouseEvent *event)
+
+  This event is called, if the mouse is moved inside the outer rect of this layout element.
+*/
+
+/*! \fn virtual void QCPLayoutElement::mouseReleaseEvent(QMouseEvent *event)
+
+  This event is called, if the mouse was previously pressed inside the outer rect of this layout
+  element and is now released.
+*/
+
+/*! \fn virtual void QCPLayoutElement::mouseDoubleClickEvent(QMouseEvent *event)
+
+  This event is called, if the mouse is double-clicked inside the outer rect of this layout
+  element.
+*/
+
+/*! \fn virtual void QCPLayoutElement::wheelEvent(QWheelEvent *event)
+
+  This event is called, if the mouse wheel is scrolled while the cursor is inside the rect of this
+  layout element.
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates an instance of QCPLayoutElement and sets default values.
+*/
+QCPLayoutElement::QCPLayoutElement(QCustomPlot *parentPlot) :
+  QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
+  mParentLayout(0),
+  mMinimumSize(),
+  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
+  mRect(0, 0, 0, 0),
+  mOuterRect(0, 0, 0, 0),
+  mMargins(0, 0, 0, 0),
+  mMinimumMargins(0, 0, 0, 0),
+  mAutoMargins(QCP::msAll)
+{
+}
+
+QCPLayoutElement::~QCPLayoutElement()
+{
+  setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
+  // unregister at layout:
+  if (qobject_cast<QCPLayout*>(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor
+    mParentLayout->take(this);
+}
+
+/*!
+  Sets the outer rect of this layout element. If the layout element is inside a layout, the layout
+  sets the position and size of this layout element using this function.
+
+  Calling this function externally has no effect, since the layout will overwrite any changes to
+  the outer rect upon the next replot.
+
+  The layout element will adapt its inner \ref rect by applying the margins inward to the outer rect.
+
+  \see rect
+*/
+void QCPLayoutElement::setOuterRect(const QRect &rect)
+{
+  if (mOuterRect != rect)
+  {
+    mOuterRect = rect;
+    mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
+  }
+}
+
+/*!
+  Sets the margins of this layout element. If \ref setAutoMargins is disabled for some or all
+  sides, this function is used to manually set the margin on those sides. Sides that are still set
+  to be handled automatically are ignored and may have any value in \a margins.
+
+  The margin is the distance between the outer rect (controlled by the parent layout via \ref
+  setOuterRect) and the inner \ref rect (which usually contains the main content of this layout
+  element).
+
+  \see setAutoMargins
+*/
+void QCPLayoutElement::setMargins(const QMargins &margins)
+{
+  if (mMargins != margins)
+  {
+    mMargins = margins;
+    mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
+  }
+}
+
+/*!
+  If \ref setAutoMargins is enabled on some or all margins, this function is used to provide
+  minimum values for those margins.
+
+  The minimum values are not enforced on margin sides that were set to be under manual control via
+  \ref setAutoMargins.
+
+  \see setAutoMargins
+*/
+void QCPLayoutElement::setMinimumMargins(const QMargins &margins)
+{
+  if (mMinimumMargins != margins)
+  {
+    mMinimumMargins = margins;
+  }
+}
+
+/*!
+  Sets on which sides the margin shall be calculated automatically. If a side is calculated
+  automatically, a minimum margin value may be provided with \ref setMinimumMargins. If a side is
+  set to be controlled manually, the value may be specified with \ref setMargins.
+
+  Margin sides that are under automatic control may participate in a \ref QCPMarginGroup (see \ref
+  setMarginGroup), to synchronize (align) it with other layout elements in the plot.
+
+  \see setMinimumMargins, setMargins
+*/
+void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
+{
+  mAutoMargins = sides;
+}
+
+/*!
+  Sets the minimum size for the inner \ref rect of this layout element. A parent layout tries to
+  respect the \a size here by changing row/column sizes in the layout accordingly.
+
+  If the parent layout size is not sufficient to satisfy all minimum size constraints of its child
+  layout elements, the layout may set a size that is actually smaller than \a size. QCustomPlot
+  propagates the layout's size constraints to the outside by setting its own minimum QWidget size
+  accordingly, so violations of \a size should be exceptions.
+*/
+void QCPLayoutElement::setMinimumSize(const QSize &size)
+{
+  if (mMinimumSize != size)
+  {
+    mMinimumSize = size;
+    if (mParentLayout)
+      mParentLayout->sizeConstraintsChanged();
+  }
+}
+
+/*! \overload
+
+  Sets the minimum size for the inner \ref rect of this layout element.
+*/
+void QCPLayoutElement::setMinimumSize(int width, int height)
+{
+  setMinimumSize(QSize(width, height));
+}
+
+/*!
+  Sets the maximum size for the inner \ref rect of this layout element. A parent layout tries to
+  respect the \a size here by changing row/column sizes in the layout accordingly.
+*/
+void QCPLayoutElement::setMaximumSize(const QSize &size)
+{
+  if (mMaximumSize != size)
+  {
+    mMaximumSize = size;
+    if (mParentLayout)
+      mParentLayout->sizeConstraintsChanged();
+  }
+}
+
+/*! \overload
+
+  Sets the maximum size for the inner \ref rect of this layout element.
+*/
+void QCPLayoutElement::setMaximumSize(int width, int height)
+{
+  setMaximumSize(QSize(width, height));
+}
+
+/*!
+  Sets the margin \a group of the specified margin \a sides.
+
+  Margin groups allow synchronizing specified margins across layout elements, see the documentation
+  of \ref QCPMarginGroup.
+
+  To unset the margin group of \a sides, set \a group to 0.
+
+  Note that margin groups only work for margin sides that are set to automatic (\ref
+  setAutoMargins).
+*/
+void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
+{
+  QVector<QCP::MarginSide> sideVector;
+  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
+  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
+  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
+  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
+
+  for (int i=0; i<sideVector.size(); ++i)
+  {
+    QCP::MarginSide side = sideVector.at(i);
+    if (marginGroup(side) != group)
+    {
+      QCPMarginGroup *oldGroup = marginGroup(side);
+      if (oldGroup) // unregister at old group
+        oldGroup->removeChild(side, this);
+
+      if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
+      {
+        mMarginGroups.remove(side);
+      } else // setting to a new group
+      {
+        mMarginGroups[side] = group;
+        group->addChild(side, this);
+      }
+    }
+  }
+}
+
+/*!
+  Updates the layout element and sub-elements. This function is automatically called before every
+  replot by the parent layout element. It is called multiple times, once for every \ref
+  UpdatePhase. The phases are run through in the order of the enum values. For details about what
+  happens at the different phases, see the documentation of \ref UpdatePhase.
+
+  Layout elements that have child elements should call the \ref update method of their child
+  elements, and pass the current \a phase unchanged.
+
+  The default implementation executes the automatic margin mechanism in the \ref upMargins phase.
+  Subclasses should make sure to call the base class implementation.
+*/
+void QCPLayoutElement::update(UpdatePhase phase)
+{
+  if (phase == upMargins)
+  {
+    if (mAutoMargins != QCP::msNone)
+    {
+      // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
+      QMargins newMargins = mMargins;
+      QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
+      foreach (QCP::MarginSide side, allMarginSides)
+      {
+        if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
+        {
+          if (mMarginGroups.contains(side))
+            QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
+          else
+            QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
+          // apply minimum margin restrictions:
+          if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
+            QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
+        }
+      }
+      setMargins(newMargins);
+    }
+  }
+}
+
+/*!
+  Returns the minimum size this layout element (the inner \ref rect) may be compressed to.
+
+  if a minimum size (\ref setMinimumSize) was not set manually, parent layouts consult this
+  function to determine the minimum allowed size of this layout element. (A manual minimum size is
+  considered set if it is non-zero.)
+*/
+QSize QCPLayoutElement::minimumSizeHint() const
+{
+  return mMinimumSize;
+}
+
+/*!
+  Returns the maximum size this layout element (the inner \ref rect) may be expanded to.
+
+  if a maximum size (\ref setMaximumSize) was not set manually, parent layouts consult this
+  function to determine the maximum allowed size of this layout element. (A manual maximum size is
+  considered set if it is smaller than Qt's QWIDGETSIZE_MAX.)
+*/
+QSize QCPLayoutElement::maximumSizeHint() const
+{
+  return mMaximumSize;
+}
+
+/*!
+  Returns a list of all child elements in this layout element. If \a recursive is true, all
+  sub-child elements are included in the list, too.
+
+  \warning There may be entries with value 0 in the returned list. (For example, QCPLayoutGrid may have
+  empty cells which yield 0 at the respective index.)
+*/
+QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
+{
+  Q_UNUSED(recursive)
+  return QList<QCPLayoutElement*>();
+}
+
+/*!
+  Layout elements are sensitive to events inside their outer rect. If \a pos is within the outer
+  rect, this method returns a value corresponding to 0.99 times the parent plot's selection
+  tolerance. However, layout elements are not selectable by default. So if \a onlySelectable is
+  true, -1.0 is returned.
+
+  See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
+
+  QCPLayoutElement subclasses may reimplement this method to provide more specific selection test
+  behaviour.
+*/
+double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+
+  if (onlySelectable)
+    return -1;
+
+  if (QRectF(mOuterRect).contains(pos))
+  {
+    if (mParentPlot)
+      return mParentPlot->selectionTolerance()*0.99;
+    else
+    {
+      qDebug() << Q_FUNC_INFO << "parent plot not defined";
+      return -1;
+    }
+  } else
+    return -1;
+}
+
+/*! \internal
+
+  propagates the parent plot initialization to all child elements, by calling \ref
+  QCPLayerable::initializeParentPlot on them.
+*/
+void QCPLayoutElement::parentPlotInitialized(QCustomPlot *parentPlot)
+{
+  foreach (QCPLayoutElement* el, elements(false))
+  {
+    if (!el->parentPlot())
+      el->initializeParentPlot(parentPlot);
+  }
+}
+
+/*! \internal
+
+  Returns the margin size for this \a side. It is used if automatic margins is enabled for this \a
+  side (see \ref setAutoMargins). If a minimum margin was set with \ref setMinimumMargins, the
+  returned value will not be smaller than the specified minimum margin.
+
+  The default implementation just returns the respective manual margin (\ref setMargins) or the
+  minimum margin, whichever is larger.
+*/
+int QCPLayoutElement::calculateAutoMargin(QCP::MarginSide side)
+{
+  return qMax(QCP::getMarginValue(mMargins, side), QCP::getMarginValue(mMinimumMargins, side));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPLayout
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPLayout
+  \brief The abstract base class for layouts
+
+  This is an abstract base class for layout elements whose main purpose is to define the position
+  and size of other child layout elements. In most cases, layouts don't draw anything themselves
+  (but there are exceptions to this, e.g. QCPLegend).
+
+  QCPLayout derives from QCPLayoutElement, and thus can itself be nested in other layouts.
+
+  QCPLayout introduces a common interface for accessing and manipulating the child elements. Those
+  functions are most notably \ref elementCount, \ref elementAt, \ref takeAt, \ref take, \ref
+  simplify, \ref removeAt, \ref remove and \ref clear. Individual subclasses may add more functions
+  to this interface which are more specialized to the form of the layout. For example, \ref
+  QCPLayoutGrid adds functions that take row and column indices to access cells of the layout grid
+  more conveniently.
+
+  Since this is an abstract base class, you can't instantiate it directly. Rather use one of its
+  subclasses like QCPLayoutGrid or QCPLayoutInset.
+
+  For a general introduction to the layout system, see the dedicated documentation page \ref
+  thelayoutsystem "The Layout System".
+*/
+
+/* start documentation of pure virtual functions */
+
+/*! \fn virtual int QCPLayout::elementCount() const = 0
+
+  Returns the number of elements/cells in the layout.
+
+  \see elements, elementAt
+*/
+
+/*! \fn virtual QCPLayoutElement* QCPLayout::elementAt(int index) const = 0
+
+  Returns the element in the cell with the given \a index. If \a index is invalid, returns 0.
+
+  Note that even if \a index is valid, the respective cell may be empty in some layouts (e.g.
+  QCPLayoutGrid), so this function may return 0 in those cases. You may use this function to check
+  whether a cell is empty or not.
+
+  \see elements, elementCount, takeAt
+*/
+
+/*! \fn virtual QCPLayoutElement* QCPLayout::takeAt(int index) = 0
+
+  Removes the element with the given \a index from the layout and returns it.
+
+  If the \a index is invalid or the cell with that index is empty, returns 0.
+
+  Note that some layouts don't remove the respective cell right away but leave an empty cell after
+  successful removal of the layout element. To collapse empty cells, use \ref simplify.
+
+  \see elementAt, take
+*/
+
+/*! \fn virtual bool QCPLayout::take(QCPLayoutElement* element) = 0
+
+  Removes the specified \a element from the layout and returns true on success.
+
+  If the \a element isn't in this layout, returns false.
+
+  Note that some layouts don't remove the respective cell right away but leave an empty cell after
+  successful removal of the layout element. To collapse empty cells, use \ref simplify.
+
+  \see takeAt
+*/
+
+/* end documentation of pure virtual functions */
+
+/*!
+  Creates an instance of QCPLayout and sets default values. Note that since QCPLayout
+  is an abstract base class, it can't be instantiated directly.
+*/
+QCPLayout::QCPLayout()
+{
+}
+
+/*!
+  First calls the QCPLayoutElement::update base class implementation to update the margins on this
+  layout.
+
+  Then calls \ref updateLayout which subclasses reimplement to reposition and resize their cells.
+
+  Finally, \ref update is called on all child elements.
+*/
+void QCPLayout::update(UpdatePhase phase)
+{
+  QCPLayoutElement::update(phase);
+
+  // set child element rects according to layout:
+  if (phase == upLayout)
+    updateLayout();
+
+  // propagate update call to child elements:
+  const int elCount = elementCount();
+  for (int i=0; i<elCount; ++i)
+  {
+    if (QCPLayoutElement *el = elementAt(i))
+      el->update(phase);
+  }
+}
+
+/* inherits documentation from base class */
+QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
+{
+  const int c = elementCount();
+  QList<QCPLayoutElement*> result;
+#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
+  result.reserve(c);
+#endif
+  for (int i=0; i<c; ++i)
+    result.append(elementAt(i));
+  if (recursive)
+  {
+    for (int i=0; i<c; ++i)
+    {
+      if (result.at(i))
+        result << result.at(i)->elements(recursive);
+    }
+  }
+  return result;
+}
+
+/*!
+  Simplifies the layout by collapsing empty cells. The exact behavior depends on subclasses, the
+  default implementation does nothing.
+
+  Not all layouts need simplification. For example, QCPLayoutInset doesn't use explicit
+  simplification while QCPLayoutGrid does.
+*/
+void QCPLayout::simplify()
+{
+}
+
+/*!
+  Removes and deletes the element at the provided \a index. Returns true on success. If \a index is
+  invalid or points to an empty cell, returns false.
+
+  This function internally uses \ref takeAt to remove the element from the layout and then deletes
+  the returned element. Note that some layouts don't remove the respective cell right away but leave an
+  empty cell after successful removal of the layout element. To collapse empty cells, use \ref
+  simplify.
+
+  \see remove, takeAt
+*/
+bool QCPLayout::removeAt(int index)
+{
+  if (QCPLayoutElement *el = takeAt(index))
+  {
+    delete el;
+    return true;
+  } else
+    return false;
+}
+
+/*!
+  Removes and deletes the provided \a element. Returns true on success. If \a element is not in the
+  layout, returns false.
+
+  This function internally uses \ref takeAt to remove the element from the layout and then deletes
+  the element. Note that some layouts don't remove the respective cell right away but leave an
+  empty cell after successful removal of the layout element. To collapse empty cells, use \ref
+  simplify.
+
+  \see removeAt, take
+*/
+bool QCPLayout::remove(QCPLayoutElement *element)
+{
+  if (take(element))
+  {
+    delete element;
+    return true;
+  } else
+    return false;
+}
+
+/*!
+  Removes and deletes all layout elements in this layout. Finally calls \ref simplify to make sure
+  all empty cells are collapsed.
+
+  \see remove, removeAt
+*/
+void QCPLayout::clear()
+{
+  for (int i=elementCount()-1; i>=0; --i)
+  {
+    if (elementAt(i))
+      removeAt(i);
+  }
+  simplify();
+}
+
+/*!
+  Subclasses call this method to report changed (minimum/maximum) size constraints.
+
+  If the parent of this layout is again a QCPLayout, forwards the call to the parent's \ref
+  sizeConstraintsChanged. If the parent is a QWidget (i.e. is the \ref QCustomPlot::plotLayout of
+  QCustomPlot), calls QWidget::updateGeometry, so if the QCustomPlot widget is inside a Qt QLayout,
+  it may update itself and resize cells accordingly.
+*/
+void QCPLayout::sizeConstraintsChanged() const
+{
+  if (QWidget *w = qobject_cast<QWidget*>(parent()))
+    w->updateGeometry();
+  else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
+    l->sizeConstraintsChanged();
+}
+
+/*! \internal
+
+  Subclasses reimplement this method to update the position and sizes of the child elements/cells
+  via calling their \ref QCPLayoutElement::setOuterRect. The default implementation does nothing.
+
+  The geometry used as a reference is the inner \ref rect of this layout. Child elements should stay
+  within that rect.
+
+  \ref getSectionSizes may help with the reimplementation of this function.
+
+  \see update
+*/
+void QCPLayout::updateLayout()
+{
+}
+
+
+/*! \internal
+
+  Associates \a el with this layout. This is done by setting the \ref QCPLayoutElement::layout, the
+  \ref QCPLayerable::parentLayerable and the QObject parent to this layout.
+
+  Further, if \a el didn't previously have a parent plot, calls \ref
+  QCPLayerable::initializeParentPlot on \a el to set the paret plot.
+
+  This method is used by subclass specific methods that add elements to the layout. Note that this
+  method only changes properties in \a el. The removal from the old layout and the insertion into
+  the new layout must be done additionally.
+*/
+void QCPLayout::adoptElement(QCPLayoutElement *el)
+{
+  if (el)
+  {
+    el->mParentLayout = this;
+    el->setParentLayerable(this);
+    el->setParent(this);
+    if (!el->parentPlot())
+      el->initializeParentPlot(mParentPlot);
+  } else
+    qDebug() << Q_FUNC_INFO << "Null element passed";
+}
+
+/*! \internal
+
+  Disassociates \a el from this layout. This is done by setting the \ref QCPLayoutElement::layout
+  and the \ref QCPLayerable::parentLayerable to zero. The QObject parent is set to the parent
+  QCustomPlot.
+
+  This method is used by subclass specific methods that remove elements from the layout (e.g. \ref
+  take or \ref takeAt). Note that this method only changes properties in \a el. The removal from
+  the old layout must be done additionally.
+*/
+void QCPLayout::releaseElement(QCPLayoutElement *el)
+{
+  if (el)
+  {
+    el->mParentLayout = 0;
+    el->setParentLayerable(0);
+    el->setParent(mParentPlot);
+    // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
+  } else
+    qDebug() << Q_FUNC_INFO << "Null element passed";
+}
+
+/*! \internal
+
+  This is a helper function for the implementation of \ref updateLayout in subclasses.
+
+  It calculates the sizes of one-dimensional sections with provided constraints on maximum section
+  sizes, minimum section sizes, relative stretch factors and the final total size of all sections.
+
+  The QVector entries refer to the sections. Thus all QVectors must have the same size.
+
+  \a maxSizes gives the maximum allowed size of each section. If there shall be no maximum size
+  imposed, set all vector values to Qt's QWIDGETSIZE_MAX.
+
+  \a minSizes gives the minimum allowed size of each section. If there shall be no minimum size
+  imposed, set all vector values to zero. If the \a minSizes entries add up to a value greater than
+  \a totalSize, sections will be scaled smaller than the proposed minimum sizes. (In other words,
+  not exceeding the allowed total size is taken to be more important than not going below minimum
+  section sizes.)
+
+  \a stretchFactors give the relative proportions of the sections to each other. If all sections
+  shall be scaled equally, set all values equal. If the first section shall be double the size of
+  each individual other section, set the first number of \a stretchFactors to double the value of
+  the other individual values (e.g. {2, 1, 1, 1}).
+
+  \a totalSize is the value that the final section sizes will add up to. Due to rounding, the
+  actual sum may differ slightly. If you want the section sizes to sum up to exactly that value,
+  you could distribute the remaining difference on the sections.
+
+  The return value is a QVector containing the section sizes.
+*/
+QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
+{
+  if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
+  {
+    qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
+    return QVector<int>();
+  }
+  if (stretchFactors.isEmpty())
+    return QVector<int>();
+  int sectionCount = stretchFactors.size();
+  QVector<double> sectionSizes(sectionCount);
+  // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
+  int minSizeSum = 0;
+  for (int i=0; i<sectionCount; ++i)
+    minSizeSum += minSizes.at(i);
+  if (totalSize < minSizeSum)
+  {
+    // new stretch factors are minimum sizes and minimum sizes are set to zero:
+    for (int i=0; i<sectionCount; ++i)
+    {
+      stretchFactors[i] = minSizes.at(i);
+      minSizes[i] = 0;
+    }
+  }
+
+  QList<int> minimumLockedSections;
+  QList<int> unfinishedSections;
+  for (int i=0; i<sectionCount; ++i)
+    unfinishedSections.append(i);
+  double freeSize = totalSize;
+
+  int outerIterations = 0;
+  while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
+  {
+    ++outerIterations;
+    int innerIterations = 0;
+    while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
+    {
+      ++innerIterations;
+      // find section that hits its maximum next:
+      int nextId = -1;
+      double nextMax = 1e12;
+      for (int i=0; i<unfinishedSections.size(); ++i)
+      {
+        int secId = unfinishedSections.at(i);
+        double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
+        if (hitsMaxAt < nextMax)
+        {
+          nextMax = hitsMaxAt;
+          nextId = secId;
+        }
+      }
+      // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section
+      // actually hits its maximum, without exceeding the total size when we add up all sections)
+      double stretchFactorSum = 0;
+      for (int i=0; i<unfinishedSections.size(); ++i)
+        stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
+      double nextMaxLimit = freeSize/stretchFactorSum;
+      if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
+      {
+        for (int i=0; i<unfinishedSections.size(); ++i)
+        {
+          sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
+          freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
+        }
+        unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
+      } else // next maximum isn't hit, just distribute rest of free space on remaining sections
+      {
+        for (int i=0; i<unfinishedSections.size(); ++i)
+          sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
+        unfinishedSections.clear();
+      }
+    }
+    if (innerIterations == sectionCount*2)
+      qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
+
+    // now check whether the resulting section sizes violate minimum restrictions:
+    bool foundMinimumViolation = false;
+    for (int i=0; i<sectionSizes.size(); ++i)
+    {
+      if (minimumLockedSections.contains(i))
+        continue;
+      if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
+      {
+        sectionSizes[i] = minSizes.at(i); // set it to minimum
+        foundMinimumViolation = true; // make sure we repeat the whole optimization process
+        minimumLockedSections.append(i);
+      }
+    }
+    if (foundMinimumViolation)
+    {
+      freeSize = totalSize;
+      for (int i=0; i<sectionCount; ++i)
+      {
+        if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
+          unfinishedSections.append(i);
+        else
+          freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
+      }
+      // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
+      for (int i=0; i<unfinishedSections.size(); ++i)
+        sectionSizes[unfinishedSections.at(i)] = 0;
+    }
+  }
+  if (outerIterations == sectionCount*2)
+    qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
+
+  QVector<int> result(sectionCount);
+  for (int i=0; i<sectionCount; ++i)
+    result[i] = qRound(sectionSizes.at(i));
+  return result;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPLayoutGrid
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPLayoutGrid
+  \brief A layout that arranges child elements in a grid
+
+  Elements are laid out in a grid with configurable stretch factors (\ref setColumnStretchFactor,
+  \ref setRowStretchFactor) and spacing (\ref setColumnSpacing, \ref setRowSpacing).
+
+  Elements can be added to cells via \ref addElement. The grid is expanded if the specified row or
+  column doesn't exist yet. Whether a cell contains a valid layout element can be checked with \ref
+  hasElement, that element can be retrieved with \ref element. If rows and columns that only have
+  empty cells shall be removed, call \ref simplify. Removal of elements is either done by just
+  adding the element to a different layout or by using the QCPLayout interface \ref take or \ref
+  remove.
+
+  Row and column insertion can be performed with \ref insertRow and \ref insertColumn.
+*/
+
+/*!
+  Creates an instance of QCPLayoutGrid and sets default values.
+*/
+QCPLayoutGrid::QCPLayoutGrid() :
+  mColumnSpacing(5),
+  mRowSpacing(5)
+{
+}
+
+QCPLayoutGrid::~QCPLayoutGrid()
+{
+  // clear all child layout elements. This is important because only the specific layouts know how
+  // to handle removing elements (clear calls virtual removeAt method to do that).
+  clear();
+}
+
+/*!
+  Returns the element in the cell in \a row and \a column.
+
+  Returns 0 if either the row/column is invalid or if the cell is empty. In those cases, a qDebug
+  message is printed. To check whether a cell exists and isn't empty, use \ref hasElement.
+
+  \see addElement, hasElement
+*/
+QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
+{
+  if (row >= 0 && row < mElements.size())
+  {
+    if (column >= 0 && column < mElements.first().size())
+    {
+      if (QCPLayoutElement *result = mElements.at(row).at(column))
+        return result;
+      else
+        qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
+    } else
+      qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
+  } else
+    qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
+  return 0;
+}
+
+/*!
+  Returns the number of rows in the layout.
+
+  \see columnCount
+*/
+int QCPLayoutGrid::rowCount() const
+{
+  return mElements.size();
+}
+
+/*!
+  Returns the number of columns in the layout.
+
+  \see rowCount
+*/
+int QCPLayoutGrid::columnCount() const
+{
+  if (mElements.size() > 0)
+    return mElements.first().size();
+  else
+    return 0;
+}
+
+/*!
+  Adds the \a element to cell with \a row and \a column. If \a element is already in a layout, it
+  is first removed from there. If \a row or \a column don't exist yet, the layout is expanded
+  accordingly.
+
+  Returns true if the element was added successfully, i.e. if the cell at \a row and \a column
+  didn't already have an element.
+
+  \see element, hasElement, take, remove
+*/
+bool QCPLayoutGrid::addElement(int row, int column, QCPLayoutElement *element)
+{
+  if (element)
+  {
+    if (!hasElement(row, column))
+    {
+      if (element->layout()) // remove from old layout first
+        element->layout()->take(element);
+      expandTo(row+1, column+1);
+      mElements[row][column] = element;
+      adoptElement(element);
+      return true;
+    } else
+      qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
+  } else
+    qDebug() << Q_FUNC_INFO << "Can't add null element to row/column:" << row << column;
+  return false;
+}
+
+/*!
+  Returns whether the cell at \a row and \a column exists and contains a valid element, i.e. isn't
+  empty.
+
+  \see element
+*/
+bool QCPLayoutGrid::hasElement(int row, int column)
+{
+  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
+    return mElements.at(row).at(column);
+  else
+    return false;
+}
+
+/*!
+  Sets the stretch \a factor of \a column.
+
+  Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
+  their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
+  QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
+
+  The default stretch factor of newly created rows/columns is 1.
+
+  \see setColumnStretchFactors, setRowStretchFactor
+*/
+void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
+{
+  if (column >= 0 && column < columnCount())
+  {
+    if (factor > 0)
+      mColumnStretchFactors[column] = factor;
+    else
+      qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
+  } else
+    qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
+}
+
+/*!
+  Sets the stretch \a factors of all columns. \a factors must have the size \ref columnCount.
+
+  Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
+  their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
+  QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
+
+  The default stretch factor of newly created rows/columns is 1.
+
+  \see setColumnStretchFactor, setRowStretchFactors
+*/
+void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
+{
+  if (factors.size() == mColumnStretchFactors.size())
+  {
+    mColumnStretchFactors = factors;
+    for (int i=0; i<mColumnStretchFactors.size(); ++i)
+    {
+      if (mColumnStretchFactors.at(i) <= 0)
+      {
+        qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
+        mColumnStretchFactors[i] = 1;
+      }
+    }
+  } else
+    qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
+}
+
+/*!
+  Sets the stretch \a factor of \a row.
+
+  Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
+  their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
+  QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
+
+  The default stretch factor of newly created rows/columns is 1.
+
+  \see setColumnStretchFactors, setRowStretchFactor
+*/
+void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
+{
+  if (row >= 0 && row < rowCount())
+  {
+    if (factor > 0)
+      mRowStretchFactors[row] = factor;
+    else
+      qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
+  } else
+    qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
+}
+
+/*!
+  Sets the stretch \a factors of all rows. \a factors must have the size \ref rowCount.
+
+  Stretch factors control the relative sizes of rows and columns. Cells will not be resized beyond
+  their minimum and maximum widths/heights (\ref QCPLayoutElement::setMinimumSize, \ref
+  QCPLayoutElement::setMaximumSize), regardless of the stretch factor.
+
+  The default stretch factor of newly created rows/columns is 1.
+
+  \see setRowStretchFactor, setColumnStretchFactors
+*/
+void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
+{
+  if (factors.size() == mRowStretchFactors.size())
+  {
+    mRowStretchFactors = factors;
+    for (int i=0; i<mRowStretchFactors.size(); ++i)
+    {
+      if (mRowStretchFactors.at(i) <= 0)
+      {
+        qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
+        mRowStretchFactors[i] = 1;
+      }
+    }
+  } else
+    qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
+}
+
+/*!
+  Sets the gap that is left blank between columns to \a pixels.
+
+  \see setRowSpacing
+*/
+void QCPLayoutGrid::setColumnSpacing(int pixels)
+{
+  mColumnSpacing = pixels;
+}
+
+/*!
+  Sets the gap that is left blank between rows to \a pixels.
+
+  \see setColumnSpacing
+*/
+void QCPLayoutGrid::setRowSpacing(int pixels)
+{
+  mRowSpacing = pixels;
+}
+
+/*!
+  Expands the layout to have \a newRowCount rows and \a newColumnCount columns. So the last valid
+  row index will be \a newRowCount-1, the last valid column index will be \a newColumnCount-1.
+
+  If the current column/row count is already larger or equal to \a newColumnCount/\a newRowCount,
+  this function does nothing in that dimension.
+
+  Newly created cells are empty, new rows and columns have the stretch factor 1.
+
+  Note that upon a call to \ref addElement, the layout is expanded automatically to contain the
+  specified row and column, using this function.
+
+  \see simplify
+*/
+void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
+{
+  // add rows as necessary:
+  while (rowCount() < newRowCount)
+  {
+    mElements.append(QList<QCPLayoutElement*>());
+    mRowStretchFactors.append(1);
+  }
+  // go through rows and expand columns as necessary:
+  int newColCount = qMax(columnCount(), newColumnCount);
+  for (int i=0; i<rowCount(); ++i)
+  {
+    while (mElements.at(i).size() < newColCount)
+      mElements[i].append(0);
+  }
+  while (mColumnStretchFactors.size() < newColCount)
+    mColumnStretchFactors.append(1);
+}
+
+/*!
+  Inserts a new row with empty cells at the row index \a newIndex. Valid values for \a newIndex
+  range from 0 (inserts a row at the top) to \a rowCount (appends a row at the bottom).
+
+  \see insertColumn
+*/
+void QCPLayoutGrid::insertRow(int newIndex)
+{
+  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
+  {
+    expandTo(1, 1);
+    return;
+  }
+
+  if (newIndex < 0)
+    newIndex = 0;
+  if (newIndex > rowCount())
+    newIndex = rowCount();
+
+  mRowStretchFactors.insert(newIndex, 1);
+  QList<QCPLayoutElement*> newRow;
+  for (int col=0; col<columnCount(); ++col)
+    newRow.append((QCPLayoutElement*)0);
+  mElements.insert(newIndex, newRow);
+}
+
+/*!
+  Inserts a new column with empty cells at the column index \a newIndex. Valid values for \a
+  newIndex range from 0 (inserts a row at the left) to \a rowCount (appends a row at the right).
+
+  \see insertRow
+*/
+void QCPLayoutGrid::insertColumn(int newIndex)
+{
+  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
+  {
+    expandTo(1, 1);
+    return;
+  }
+
+  if (newIndex < 0)
+    newIndex = 0;
+  if (newIndex > columnCount())
+    newIndex = columnCount();
+
+  mColumnStretchFactors.insert(newIndex, 1);
+  for (int row=0; row<rowCount(); ++row)
+    mElements[row].insert(newIndex, (QCPLayoutElement*)0);
+}
+
+/* inherits documentation from base class */
+void QCPLayoutGrid::updateLayout()
+{
+  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
+  getMinimumRowColSizes(&minColWidths, &minRowHeights);
+  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
+
+  int totalRowSpacing = (rowCount()-1) * mRowSpacing;
+  int totalColSpacing = (columnCount()-1) * mColumnSpacing;
+  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
+  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
+
+  // go through cells and set rects accordingly:
+  int yOffset = mRect.top();
+  for (int row=0; row<rowCount(); ++row)
+  {
+    if (row > 0)
+      yOffset += rowHeights.at(row-1)+mRowSpacing;
+    int xOffset = mRect.left();
+    for (int col=0; col<columnCount(); ++col)
+    {
+      if (col > 0)
+        xOffset += colWidths.at(col-1)+mColumnSpacing;
+      if (mElements.at(row).at(col))
+        mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
+    }
+  }
+}
+
+/* inherits documentation from base class */
+int QCPLayoutGrid::elementCount() const
+{
+  return rowCount()*columnCount();
+}
+
+/* inherits documentation from base class */
+QCPLayoutElement *QCPLayoutGrid::elementAt(int index) const
+{
+  if (index >= 0 && index < elementCount())
+    return mElements.at(index / columnCount()).at(index % columnCount());
+  else
+    return 0;
+}
+
+/* inherits documentation from base class */
+QCPLayoutElement *QCPLayoutGrid::takeAt(int index)
+{
+  if (QCPLayoutElement *el = elementAt(index))
+  {
+    releaseElement(el);
+    mElements[index / columnCount()][index % columnCount()] = 0;
+    return el;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
+    return 0;
+  }
+}
+
+/* inherits documentation from base class */
+bool QCPLayoutGrid::take(QCPLayoutElement *element)
+{
+  if (element)
+  {
+    for (int i=0; i<elementCount(); ++i)
+    {
+      if (elementAt(i) == element)
+      {
+        takeAt(i);
+        return true;
+      }
+    }
+    qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
+  } else
+    qDebug() << Q_FUNC_INFO << "Can't take null element";
+  return false;
+}
+
+/* inherits documentation from base class */
+QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
+{
+  QList<QCPLayoutElement*> result;
+  int colC = columnCount();
+  int rowC = rowCount();
+#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
+  result.reserve(colC*rowC);
+#endif
+  for (int row=0; row<rowC; ++row)
+  {
+    for (int col=0; col<colC; ++col)
+    {
+      result.append(mElements.at(row).at(col));
+    }
+  }
+  if (recursive)
+  {
+    int c = result.size();
+    for (int i=0; i<c; ++i)
+    {
+      if (result.at(i))
+        result << result.at(i)->elements(recursive);
+    }
+  }
+  return result;
+}
+
+/*!
+  Simplifies the layout by collapsing rows and columns which only contain empty cells.
+*/
+void QCPLayoutGrid::simplify()
+{
+  // remove rows with only empty cells:
+  for (int row=rowCount()-1; row>=0; --row)
+  {
+    bool hasElements = false;
+    for (int col=0; col<columnCount(); ++col)
+    {
+      if (mElements.at(row).at(col))
+      {
+        hasElements = true;
+        break;
+      }
+    }
+    if (!hasElements)
+    {
+      mRowStretchFactors.removeAt(row);
+      mElements.removeAt(row);
+      if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
+        mColumnStretchFactors.clear();
+    }
+  }
+
+  // remove columns with only empty cells:
+  for (int col=columnCount()-1; col>=0; --col)
+  {
+    bool hasElements = false;
+    for (int row=0; row<rowCount(); ++row)
+    {
+      if (mElements.at(row).at(col))
+      {
+        hasElements = true;
+        break;
+      }
+    }
+    if (!hasElements)
+    {
+      mColumnStretchFactors.removeAt(col);
+      for (int row=0; row<rowCount(); ++row)
+        mElements[row].removeAt(col);
+    }
+  }
+}
+
+/* inherits documentation from base class */
+QSize QCPLayoutGrid::minimumSizeHint() const
+{
+  QVector<int> minColWidths, minRowHeights;
+  getMinimumRowColSizes(&minColWidths, &minRowHeights);
+  QSize result(0, 0);
+  for (int i=0; i<minColWidths.size(); ++i)
+    result.rwidth() += minColWidths.at(i);
+  for (int i=0; i<minRowHeights.size(); ++i)
+    result.rheight() += minRowHeights.at(i);
+  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
+  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
+  return result;
+}
+
+/* inherits documentation from base class */
+QSize QCPLayoutGrid::maximumSizeHint() const
+{
+  QVector<int> maxColWidths, maxRowHeights;
+  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
+
+  QSize result(0, 0);
+  for (int i=0; i<maxColWidths.size(); ++i)
+    result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
+  for (int i=0; i<maxRowHeights.size(); ++i)
+    result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
+  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing + mMargins.left() + mMargins.right();
+  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing + mMargins.top() + mMargins.bottom();
+  return result;
+}
+
+/*! \internal
+
+  Places the minimum column widths and row heights into \a minColWidths and \a minRowHeights
+  respectively.
+
+  The minimum height of a row is the largest minimum height of any element in that row. The minimum
+  width of a column is the largest minimum width of any element in that column.
+
+  This is a helper function for \ref updateLayout.
+
+  \see getMaximumRowColSizes
+*/
+void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
+{
+  *minColWidths = QVector<int>(columnCount(), 0);
+  *minRowHeights = QVector<int>(rowCount(), 0);
+  for (int row=0; row<rowCount(); ++row)
+  {
+    for (int col=0; col<columnCount(); ++col)
+    {
+      if (mElements.at(row).at(col))
+      {
+        QSize minHint = mElements.at(row).at(col)->minimumSizeHint();
+        QSize min = mElements.at(row).at(col)->minimumSize();
+        QSize final(min.width() > 0 ? min.width() : minHint.width(), min.height() > 0 ? min.height() : minHint.height());
+        if (minColWidths->at(col) < final.width())
+          (*minColWidths)[col] = final.width();
+        if (minRowHeights->at(row) < final.height())
+          (*minRowHeights)[row] = final.height();
+      }
+    }
+  }
+}
+
+/*! \internal
+
+  Places the maximum column widths and row heights into \a maxColWidths and \a maxRowHeights
+  respectively.
+
+  The maximum height of a row is the smallest maximum height of any element in that row. The
+  maximum width of a column is the smallest maximum width of any element in that column.
+
+  This is a helper function for \ref updateLayout.
+
+  \see getMinimumRowColSizes
+*/
+void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
+{
+  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
+  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
+  for (int row=0; row<rowCount(); ++row)
+  {
+    for (int col=0; col<columnCount(); ++col)
+    {
+      if (mElements.at(row).at(col))
+      {
+        QSize maxHint = mElements.at(row).at(col)->maximumSizeHint();
+        QSize max = mElements.at(row).at(col)->maximumSize();
+        QSize final(max.width() < QWIDGETSIZE_MAX ? max.width() : maxHint.width(), max.height() < QWIDGETSIZE_MAX ? max.height() : maxHint.height());
+        if (maxColWidths->at(col) > final.width())
+          (*maxColWidths)[col] = final.width();
+        if (maxRowHeights->at(row) > final.height())
+          (*maxRowHeights)[row] = final.height();
+      }
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPLayoutInset
+////////////////////////////////////////////////////////////////////////////////////////////////////
+/*! \class QCPLayoutInset
+  \brief A layout that places child elements aligned to the border or arbitrarily positioned
+
+  Elements are placed either aligned to the border or at arbitrary position in the area of the
+  layout. Which placement applies is controlled with the \ref InsetPlacement (\ref
+  setInsetPlacement).
+
+  Elements are added via \ref addElement(QCPLayoutElement *element, Qt::Alignment alignment) or
+  addElement(QCPLayoutElement *element, const QRectF &rect). If the first method is used, the inset
+  placement will default to \ref ipBorderAligned and the element will be aligned according to the
+  \a alignment parameter. The second method defaults to \ref ipFree and allows placing elements at
+  arbitrary position and size, defined by \a rect.
+
+  The alignment or rect can be set via \ref setInsetAlignment or \ref setInsetRect, respectively.
+
+  This is the layout that every QCPAxisRect has as \ref QCPAxisRect::insetLayout.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn virtual void QCPLayoutInset::simplify()
+
+  The QCPInsetLayout does not need simplification since it can never have empty cells due to its
+  linear index structure. This method does nothing.
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates an instance of QCPLayoutInset and sets default values.
+*/
+QCPLayoutInset::QCPLayoutInset()
+{
+}
+
+QCPLayoutInset::~QCPLayoutInset()
+{
+  // clear all child layout elements. This is important because only the specific layouts know how
+  // to handle removing elements (clear calls virtual removeAt method to do that).
+  clear();
+}
+
+/*!
+  Returns the placement type of the element with the specified \a index.
+*/
+QCPLayoutInset::InsetPlacement QCPLayoutInset::insetPlacement(int index) const
+{
+  if (elementAt(index))
+    return mInsetPlacement.at(index);
+  else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
+    return ipFree;
+  }
+}
+
+/*!
+  Returns the alignment of the element with the specified \a index. The alignment only has a
+  meaning, if the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned.
+*/
+Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
+{
+  if (elementAt(index))
+    return mInsetAlignment.at(index);
+  else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
+    return 0;
+  }
+}
+
+/*!
+  Returns the rect of the element with the specified \a index. The rect only has a
+  meaning, if the inset placement (\ref setInsetPlacement) is \ref ipFree.
+*/
+QRectF QCPLayoutInset::insetRect(int index) const
+{
+  if (elementAt(index))
+    return mInsetRect.at(index);
+  else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
+    return QRectF();
+  }
+}
+
+/*!
+  Sets the inset placement type of the element with the specified \a index to \a placement.
+
+  \see InsetPlacement
+*/
+void QCPLayoutInset::setInsetPlacement(int index, QCPLayoutInset::InsetPlacement placement)
+{
+  if (elementAt(index))
+    mInsetPlacement[index] = placement;
+  else
+    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
+}
+
+/*!
+  If the inset placement (\ref setInsetPlacement) is \ref ipBorderAligned, this function
+  is used to set the alignment of the element with the specified \a index to \a alignment.
+
+  \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
+  Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
+  alignment flags will be ignored.
+*/
+void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
+{
+  if (elementAt(index))
+    mInsetAlignment[index] = alignment;
+  else
+    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
+}
+
+/*!
+  If the inset placement (\ref setInsetPlacement) is \ref ipFree, this function is used to set the
+  position and size of the element with the specified \a index to \a rect.
+
+  \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
+  will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
+  corner of the layout, with 35% width and height of the parent layout.
+
+  Note that the minimum and maximum sizes of the embedded element (\ref
+  QCPLayoutElement::setMinimumSize, \ref QCPLayoutElement::setMaximumSize) are enforced.
+*/
+void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
+{
+  if (elementAt(index))
+    mInsetRect[index] = rect;
+  else
+    qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
+}
+
+/* inherits documentation from base class */
+void QCPLayoutInset::updateLayout()
+{
+  for (int i=0; i<mElements.size(); ++i)
+  {
+    QRect insetRect;
+    QSize finalMinSize, finalMaxSize;
+    QSize minSizeHint = mElements.at(i)->minimumSizeHint();
+    QSize maxSizeHint = mElements.at(i)->maximumSizeHint();
+    finalMinSize.setWidth(mElements.at(i)->minimumSize().width() > 0 ? mElements.at(i)->minimumSize().width() : minSizeHint.width());
+    finalMinSize.setHeight(mElements.at(i)->minimumSize().height() > 0 ? mElements.at(i)->minimumSize().height() : minSizeHint.height());
+    finalMaxSize.setWidth(mElements.at(i)->maximumSize().width() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().width() : maxSizeHint.width());
+    finalMaxSize.setHeight(mElements.at(i)->maximumSize().height() < QWIDGETSIZE_MAX ? mElements.at(i)->maximumSize().height() : maxSizeHint.height());
+    if (mInsetPlacement.at(i) == ipFree)
+    {
+      insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
+                        rect().y()+rect().height()*mInsetRect.at(i).y(),
+                        rect().width()*mInsetRect.at(i).width(),
+                        rect().height()*mInsetRect.at(i).height());
+      if (insetRect.size().width() < finalMinSize.width())
+        insetRect.setWidth(finalMinSize.width());
+      if (insetRect.size().height() < finalMinSize.height())
+        insetRect.setHeight(finalMinSize.height());
+      if (insetRect.size().width() > finalMaxSize.width())
+        insetRect.setWidth(finalMaxSize.width());
+      if (insetRect.size().height() > finalMaxSize.height())
+        insetRect.setHeight(finalMaxSize.height());
+    } else if (mInsetPlacement.at(i) == ipBorderAligned)
+    {
+      insetRect.setSize(finalMinSize);
+      Qt::Alignment al = mInsetAlignment.at(i);
+      if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
+      else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
+      else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
+      if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
+      else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
+      else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
+    }
+    mElements.at(i)->setOuterRect(insetRect);
+  }
+}
+
+/* inherits documentation from base class */
+int QCPLayoutInset::elementCount() const
+{
+  return mElements.size();
+}
+
+/* inherits documentation from base class */
+QCPLayoutElement *QCPLayoutInset::elementAt(int index) const
+{
+  if (index >= 0 && index < mElements.size())
+    return mElements.at(index);
+  else
+    return 0;
+}
+
+/* inherits documentation from base class */
+QCPLayoutElement *QCPLayoutInset::takeAt(int index)
+{
+  if (QCPLayoutElement *el = elementAt(index))
+  {
+    releaseElement(el);
+    mElements.removeAt(index);
+    mInsetPlacement.removeAt(index);
+    mInsetAlignment.removeAt(index);
+    mInsetRect.removeAt(index);
+    return el;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
+    return 0;
+  }
+}
+
+/* inherits documentation from base class */
+bool QCPLayoutInset::take(QCPLayoutElement *element)
+{
+  if (element)
+  {
+    for (int i=0; i<elementCount(); ++i)
+    {
+      if (elementAt(i) == element)
+      {
+        takeAt(i);
+        return true;
+      }
+    }
+    qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
+  } else
+    qDebug() << Q_FUNC_INFO << "Can't take null element";
+  return false;
+}
+
+/*!
+  The inset layout is sensitive to events only at areas where its (visible) child elements are
+  sensitive. If the selectTest method of any of the child elements returns a positive number for \a
+  pos, this method returns a value corresponding to 0.99 times the parent plot's selection
+  tolerance. The inset layout is not selectable itself by default. So if \a onlySelectable is true,
+  -1.0 is returned.
+
+  See \ref QCPLayerable::selectTest for a general explanation of this virtual method.
+*/
+double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable)
+    return -1;
+
+  for (int i=0; i<mElements.size(); ++i)
+  {
+    // inset layout shall only return positive selectTest, if actually an inset object is at pos
+    // else it would block the entire underlying QCPAxisRect with its surface.
+    if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
+      return mParentPlot->selectionTolerance()*0.99;
+  }
+  return -1;
+}
+
+/*!
+  Adds the specified \a element to the layout as an inset aligned at the border (\ref
+  setInsetAlignment is initialized with \ref ipBorderAligned). The alignment is set to \a
+  alignment.
+
+  \a alignment is an or combination of the following alignment flags: Qt::AlignLeft,
+  Qt::AlignHCenter, Qt::AlighRight, Qt::AlignTop, Qt::AlignVCenter, Qt::AlignBottom. Any other
+  alignment flags will be ignored.
+
+  \see addElement(QCPLayoutElement *element, const QRectF &rect)
+*/
+void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
+{
+  if (element)
+  {
+    if (element->layout()) // remove from old layout first
+      element->layout()->take(element);
+    mElements.append(element);
+    mInsetPlacement.append(ipBorderAligned);
+    mInsetAlignment.append(alignment);
+    mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
+    adoptElement(element);
+  } else
+    qDebug() << Q_FUNC_INFO << "Can't add null element";
+}
+
+/*!
+  Adds the specified \a element to the layout as an inset with free positioning/sizing (\ref
+  setInsetAlignment is initialized with \ref ipFree). The position and size is set to \a
+  rect.
+
+  \a rect is given in fractions of the whole inset layout rect. So an inset with rect (0, 0, 1, 1)
+  will span the entire layout. An inset with rect (0.6, 0.1, 0.35, 0.35) will be in the top right
+  corner of the layout, with 35% width and height of the parent layout.
+
+  \see addElement(QCPLayoutElement *element, Qt::Alignment alignment)
+*/
+void QCPLayoutInset::addElement(QCPLayoutElement *element, const QRectF &rect)
+{
+  if (element)
+  {
+    if (element->layout()) // remove from old layout first
+      element->layout()->take(element);
+    mElements.append(element);
+    mInsetPlacement.append(ipFree);
+    mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
+    mInsetRect.append(rect);
+    adoptElement(element);
+  } else
+    qDebug() << Q_FUNC_INFO << "Can't add null element";
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPLineEnding
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPLineEnding
+  \brief Handles the different ending decorations for line-like items
+
+  \image html QCPLineEnding.png "The various ending styles currently supported"
+
+  For every ending a line-like item has, an instance of this class exists. For example, QCPItemLine
+  has two endings which can be set with QCPItemLine::setHead and QCPItemLine::setTail.
+
+  The styles themselves are defined via the enum QCPLineEnding::EndingStyle. Most decorations can
+  be modified regarding width and length, see \ref setWidth and \ref setLength. The direction of
+  the ending decoration (e.g. direction an arrow is pointing) is controlled by the line-like item.
+  For example, when both endings of a QCPItemLine are set to be arrows, they will point to opposite
+  directions, e.g. "outward". This can be changed by \ref setInverted, which would make the
+  respective arrow point inward.
+
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify a
+  QCPLineEnding::EndingStyle where actually a QCPLineEnding is expected, e.g.
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcplineending-sethead
+*/
+
+/*!
+  Creates a QCPLineEnding instance with default values (style \ref esNone).
+*/
+QCPLineEnding::QCPLineEnding() :
+  mStyle(esNone),
+  mWidth(8),
+  mLength(10),
+  mInverted(false)
+{
+}
+
+/*!
+  Creates a QCPLineEnding instance with the specified values.
+*/
+QCPLineEnding::QCPLineEnding(QCPLineEnding::EndingStyle style, double width, double length, bool inverted) :
+  mStyle(style),
+  mWidth(width),
+  mLength(length),
+  mInverted(inverted)
+{
+}
+
+/*!
+  Sets the style of the ending decoration.
+*/
+void QCPLineEnding::setStyle(QCPLineEnding::EndingStyle style)
+{
+  mStyle = style;
+}
+
+/*!
+  Sets the width of the ending decoration, if the style supports it. On arrows, for example, the
+  width defines the size perpendicular to the arrow's pointing direction.
+
+  \see setLength
+*/
+void QCPLineEnding::setWidth(double width)
+{
+  mWidth = width;
+}
+
+/*!
+  Sets the length of the ending decoration, if the style supports it. On arrows, for example, the
+  length defines the size in pointing direction.
+
+  \see setWidth
+*/
+void QCPLineEnding::setLength(double length)
+{
+  mLength = length;
+}
+
+/*!
+  Sets whether the ending decoration shall be inverted. For example, an arrow decoration will point
+  inward when \a inverted is set to true.
+
+  Note that also the \a width direction is inverted. For symmetrical ending styles like arrows or
+  discs, this doesn't make a difference. However, asymmetric styles like \ref esHalfBar are
+  affected by it, which can be used to control to which side the half bar points to.
+*/
+void QCPLineEnding::setInverted(bool inverted)
+{
+  mInverted = inverted;
+}
+
+/*! \internal
+
+  Returns the maximum pixel radius the ending decoration might cover, starting from the position
+  the decoration is drawn at (typically a line ending/\ref QCPItemPosition of an item).
+
+  This is relevant for clipping. Only omit painting of the decoration when the position where the
+  decoration is supposed to be drawn is farther away from the clipping rect than the returned
+  distance.
+*/
+double QCPLineEnding::boundingDistance() const
+{
+  switch (mStyle)
+  {
+    case esNone:
+      return 0;
+
+    case esFlatArrow:
+    case esSpikeArrow:
+    case esLineArrow:
+    case esSkewedBar:
+      return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
+
+    case esDisc:
+    case esSquare:
+    case esDiamond:
+    case esBar:
+    case esHalfBar:
+      return mWidth*1.42; // items that only have a width -> width*sqrt(2)
+
+  }
+  return 0;
+}
+
+/*!
+  Starting from the origin of this line ending (which is style specific), returns the length
+  covered by the line ending symbol, in backward direction.
+
+  For example, the \ref esSpikeArrow has a shorter real length than a \ref esFlatArrow, even if
+  both have the same \ref setLength value, because the spike arrow has an inward curved back, which
+  reduces the length along its center axis (the drawing origin for arrows is at the tip).
+
+  This function is used for precise, style specific placement of line endings, for example in
+  QCPAxes.
+*/
+double QCPLineEnding::realLength() const
+{
+  switch (mStyle)
+  {
+    case esNone:
+    case esLineArrow:
+    case esSkewedBar:
+    case esBar:
+    case esHalfBar:
+      return 0;
+
+    case esFlatArrow:
+      return mLength;
+
+    case esDisc:
+    case esSquare:
+    case esDiamond:
+      return mWidth*0.5;
+
+    case esSpikeArrow:
+      return mLength*0.8;
+  }
+  return 0;
+}
+
+/*! \internal
+
+  Draws the line ending with the specified \a painter at the position \a pos. The direction of the
+  line ending is controlled with \a dir.
+*/
+void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const
+{
+  if (mStyle == esNone)
+    return;
+
+  QVector2D lengthVec(dir.normalized());
+  if (lengthVec.isNull())
+    lengthVec = QVector2D(1, 0);
+  QVector2D widthVec(-lengthVec.y(), lengthVec.x());
+  lengthVec *= (float)(mLength*(mInverted ? -1 : 1));
+  widthVec *= (float)(mWidth*0.5*(mInverted ? -1 : 1));
+
+  QPen penBackup = painter->pen();
+  QBrush brushBackup = painter->brush();
+  QPen miterPen = penBackup;
+  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
+  QBrush brush(painter->pen().color(), Qt::SolidPattern);
+  switch (mStyle)
+  {
+    case esNone: break;
+    case esFlatArrow:
+    {
+      QPointF points[3] = {pos.toPointF(),
+                           (pos-lengthVec+widthVec).toPointF(),
+                           (pos-lengthVec-widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->setBrush(brush);
+      painter->drawConvexPolygon(points, 3);
+      painter->setBrush(brushBackup);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esSpikeArrow:
+    {
+      QPointF points[4] = {pos.toPointF(),
+                           (pos-lengthVec+widthVec).toPointF(),
+                           (pos-lengthVec*0.8f).toPointF(),
+                           (pos-lengthVec-widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->setBrush(brush);
+      painter->drawConvexPolygon(points, 4);
+      painter->setBrush(brushBackup);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esLineArrow:
+    {
+      QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
+                           pos.toPointF(),
+                           (pos-lengthVec-widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->drawPolyline(points, 3);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esDisc:
+    {
+      painter->setBrush(brush);
+      painter->drawEllipse(pos.toPointF(),  mWidth*0.5, mWidth*0.5);
+      painter->setBrush(brushBackup);
+      break;
+    }
+    case esSquare:
+    {
+      QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
+      QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
+                           (pos-widthVecPerp-widthVec).toPointF(),
+                           (pos+widthVecPerp-widthVec).toPointF(),
+                           (pos+widthVecPerp+widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->setBrush(brush);
+      painter->drawConvexPolygon(points, 4);
+      painter->setBrush(brushBackup);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esDiamond:
+    {
+      QVector2D widthVecPerp(-widthVec.y(), widthVec.x());
+      QPointF points[4] = {(pos-widthVecPerp).toPointF(),
+                           (pos-widthVec).toPointF(),
+                           (pos+widthVecPerp).toPointF(),
+                           (pos+widthVec).toPointF()
+                          };
+      painter->setPen(miterPen);
+      painter->setBrush(brush);
+      painter->drawConvexPolygon(points, 4);
+      painter->setBrush(brushBackup);
+      painter->setPen(penBackup);
+      break;
+    }
+    case esBar:
+    {
+      painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
+      break;
+    }
+    case esHalfBar:
+    {
+      painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
+      break;
+    }
+    case esSkewedBar:
+    {
+      if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
+      {
+        // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
+        painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)).toPointF(),
+                          (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)).toPointF());
+      } else
+      {
+        // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
+        painter->drawLine((pos+widthVec+lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
+                          (pos-widthVec-lengthVec*0.2f*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
+      }
+      break;
+    }
+  }
+}
+
+/*! \internal
+  \overload
+
+  Draws the line ending. The direction is controlled with the \a angle parameter in radians.
+*/
+void QCPLineEnding::draw(QCPPainter *painter, const QVector2D &pos, double angle) const
+{
+  draw(painter, pos, QVector2D(qCos(angle), qSin(angle)));
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPGrid
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPGrid
+  \brief Responsible for drawing the grid of a QCPAxis.
+
+  This class is tightly bound to QCPAxis. Every axis owns a grid instance and uses it to draw the
+  grid lines, sub grid lines and zero-line. You can interact with the grid of an axis via \ref
+  QCPAxis::grid. Normally, you don't need to create an instance of QCPGrid yourself.
+
+  The axis and grid drawing was split into two classes to allow them to be placed on different
+  layers (both QCPAxis and QCPGrid inherit from QCPLayerable). Thus it is possible to have the grid
+  in the background and the axes in the foreground, and any plottables/items in between. This
+  described situation is the default setup, see the QCPLayer documentation.
+*/
+
+/*!
+  Creates a QCPGrid instance and sets default values.
+
+  You shouldn't instantiate grids on their own, since every QCPAxis brings its own QCPGrid.
+*/
+QCPGrid::QCPGrid(QCPAxis *parentAxis) :
+  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
+  mParentAxis(parentAxis)
+{
+  // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
+  setParent(parentAxis);
+  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
+  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
+  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
+  setSubGridVisible(false);
+  setAntialiased(false);
+  setAntialiasedSubGrid(false);
+  setAntialiasedZeroLine(false);
+}
+
+/*!
+  Sets whether grid lines at sub tick marks are drawn.
+
+  \see setSubGridPen
+*/
+void QCPGrid::setSubGridVisible(bool visible)
+{
+  mSubGridVisible = visible;
+}
+
+/*!
+  Sets whether sub grid lines are drawn antialiased.
+*/
+void QCPGrid::setAntialiasedSubGrid(bool enabled)
+{
+  mAntialiasedSubGrid = enabled;
+}
+
+/*!
+  Sets whether zero lines are drawn antialiased.
+*/
+void QCPGrid::setAntialiasedZeroLine(bool enabled)
+{
+  mAntialiasedZeroLine = enabled;
+}
+
+/*!
+  Sets the pen with which (major) grid lines are drawn.
+*/
+void QCPGrid::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen with which sub grid lines are drawn.
+*/
+void QCPGrid::setSubGridPen(const QPen &pen)
+{
+  mSubGridPen = pen;
+}
+
+/*!
+  Sets the pen with which zero lines are drawn.
+
+  Zero lines are lines at value coordinate 0 which may be drawn with a different pen than other grid
+  lines. To disable zero lines and just draw normal grid lines at zero, set \a pen to Qt::NoPen.
+*/
+void QCPGrid::setZeroLinePen(const QPen &pen)
+{
+  mZeroLinePen = pen;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing the major grid lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+
+  \see setAntialiased
+*/
+void QCPGrid::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeGrid);
+}
+
+/*! \internal
+
+  Draws grid lines and sub grid lines at the positions of (sub) ticks of the parent axis, spanning
+  over the complete axis rect. Also draws the zero line, if appropriate (\ref setZeroLinePen).
+*/
+void QCPGrid::draw(QCPPainter *painter)
+{
+  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
+
+  if (mSubGridVisible)
+    drawSubGridLines(painter);
+  drawGridLines(painter);
+}
+
+/*! \internal
+
+  Draws the main grid lines and possibly a zero line with the specified painter.
+
+  This is a helper function called by \ref draw.
+*/
+void QCPGrid::drawGridLines(QCPPainter *painter) const
+{
+  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
+
+  int lowTick = mParentAxis->mLowestVisibleTick;
+  int highTick = mParentAxis->mHighestVisibleTick;
+  double t; // helper variable, result of coordinate-to-pixel transforms
+  if (mParentAxis->orientation() == Qt::Horizontal)
+  {
+    // draw zeroline:
+    int zeroLineIndex = -1;
+    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
+    {
+      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
+      painter->setPen(mZeroLinePen);
+      double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
+      for (int i=lowTick; i <= highTick; ++i)
+      {
+        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
+        {
+          zeroLineIndex = i;
+          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
+          painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
+          break;
+        }
+      }
+    }
+    // draw grid lines:
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mPen);
+    for (int i=lowTick; i <= highTick; ++i)
+    {
+      if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
+      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
+      painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
+    }
+  } else
+  {
+    // draw zeroline:
+    int zeroLineIndex = -1;
+    if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
+    {
+      applyAntialiasingHint(painter, mAntialiasedZeroLine, QCP::aeZeroLine);
+      painter->setPen(mZeroLinePen);
+      double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
+      for (int i=lowTick; i <= highTick; ++i)
+      {
+        if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
+        {
+          zeroLineIndex = i;
+          t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
+          painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
+          break;
+        }
+      }
+    }
+    // draw grid lines:
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mPen);
+    for (int i=lowTick; i <= highTick; ++i)
+    {
+      if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
+      t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
+      painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
+    }
+  }
+}
+
+/*! \internal
+
+  Draws the sub grid lines with the specified painter.
+
+  This is a helper function called by \ref draw.
+*/
+void QCPGrid::drawSubGridLines(QCPPainter *painter) const
+{
+  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
+
+  applyAntialiasingHint(painter, mAntialiasedSubGrid, QCP::aeSubGrid);
+  double t; // helper variable, result of coordinate-to-pixel transforms
+  painter->setPen(mSubGridPen);
+  if (mParentAxis->orientation() == Qt::Horizontal)
+  {
+    for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
+    {
+      t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // x
+      painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
+    }
+  } else
+  {
+    for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
+    {
+      t = mParentAxis->coordToPixel(mParentAxis->mSubTickVector.at(i)); // y
+      painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPAxis
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPAxis
+  \brief Manages a single axis inside a QCustomPlot.
+
+  Usually doesn't need to be instantiated externally. Access %QCustomPlot's default four axes via
+  QCustomPlot::xAxis (bottom), QCustomPlot::yAxis (left), QCustomPlot::xAxis2 (top) and
+  QCustomPlot::yAxis2 (right).
+
+  Axes are always part of an axis rect, see QCPAxisRect.
+  \image html AxisNamesOverview.png
+  <center>Naming convention of axis parts</center>
+  \n
+
+  \image html AxisRectSpacingOverview.png
+  <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed gray line
+  on the left represents the QCustomPlot widget border.</center>
+
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn Qt::Orientation QCPAxis::orientation() const
+
+  Returns the orientation of this axis. The axis orientation (horizontal or vertical) is deduced
+  from the axis type (left, top, right or bottom).
+
+  \see orientation(AxisType type)
+*/
+
+/*! \fn QCPGrid *QCPAxis::grid() const
+
+  Returns the \ref QCPGrid instance belonging to this axis. Access it to set details about the way the
+  grid is displayed.
+*/
+
+/*! \fn static Qt::Orientation QCPAxis::orientation(AxisType type)
+
+  Returns the orientation of the specified axis type
+
+  \see orientation()
+*/
+
+/* end of documentation of inline functions */
+/* start of documentation of signals */
+
+/*! \fn void QCPAxis::ticksRequest()
+
+  This signal is emitted when \ref setAutoTicks is false and the axis is about to generate tick
+  labels for a replot.
+
+  Modifying the tick positions can be done with \ref setTickVector. If you also want to control the
+  tick labels, set \ref setAutoTickLabels to false and also provide the labels with \ref
+  setTickVectorLabels.
+
+  If you only want static ticks you probably don't need this signal, since you can just set the
+  tick vector (and possibly tick label vector) once. However, if you want to provide ticks (and
+  maybe labels) dynamically, e.g. depending on the current axis range, connect a slot to this
+  signal and set the vector/vectors there.
+*/
+
+/*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange)
+
+  This signal is emitted when the range of this axis has changed. You can connect it to the \ref
+  setRange slot of another axis to communicate the new range to the other axis, in order for it to
+  be synchronized.
+
+  You may also manipulate/correct the range with \ref setRange in a slot connected to this signal.
+  This is useful if for example a maximum range span shall not be exceeded, or if the lower/upper
+  range shouldn't go beyond certain values. For example, the following slot would limit the x axis
+  to only positive ranges:
+  \code
+  if (newRange.lower < 0)
+    plot->xAxis->setRange(0, newRange.size());
+  \endcode
+*/
+
+/*! \fn void QCPAxis::rangeChanged(const QCPRange &newRange, const QCPRange &oldRange)
+  \overload
+
+  Additionally to the new range, this signal also provides the previous range held by the axis as
+  \a oldRange.
+*/
+
+/*! \fn void QCPAxis::scaleTypeChanged(QCPAxis::ScaleType scaleType);
+
+  This signal is emitted when the scale type changes, by calls to \ref setScaleType
+*/
+
+/*! \fn void QCPAxis::selectionChanged(QCPAxis::SelectableParts selection)
+
+  This signal is emitted when the selection state of this axis has changed, either by user interaction
+  or by a direct call to \ref setSelectedParts.
+*/
+
+/*! \fn void QCPAxis::selectableChanged(const QCPAxis::SelectableParts &parts);
+
+  This signal is emitted when the selectability changes, by calls to \ref setSelectableParts
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs an Axis instance of Type \a type for the axis rect \a parent.
+
+  Usually it isn't necessary to instantiate axes directly, because you can let QCustomPlot create
+  them for you with \ref QCPAxisRect::addAxis. If you want to use own QCPAxis-subclasses however,
+  create them manually and then inject them also via \ref QCPAxisRect::addAxis.
+*/
+QCPAxis::QCPAxis(QCPAxisRect *parent, AxisType type) :
+  QCPLayerable(parent->parentPlot(), QString(), parent),
+  // axis base:
+  mAxisType(type),
+  mAxisRect(parent),
+  mPadding(5),
+  mOrientation(orientation(type)),
+  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
+  mSelectedParts(spNone),
+  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  mSelectedBasePen(QPen(Qt::blue, 2)),
+  // axis label:
+  mLabel(),
+  mLabelFont(mParentPlot->font()),
+  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
+  mLabelColor(Qt::black),
+  mSelectedLabelColor(Qt::blue),
+  // tick labels:
+  mTickLabels(true),
+  mAutoTickLabels(true),
+  mTickLabelType(ltNumber),
+  mTickLabelFont(mParentPlot->font()),
+  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
+  mTickLabelColor(Qt::black),
+  mSelectedTickLabelColor(Qt::blue),
+  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
+  mDateTimeSpec(Qt::LocalTime),
+  mNumberPrecision(6),
+  mNumberFormatChar('g'),
+  mNumberBeautifulPowers(true),
+  // ticks and subticks:
+  mTicks(true),
+  mTickStep(1),
+  mSubTickCount(4),
+  mAutoTickCount(6),
+  mAutoTicks(true),
+  mAutoTickStep(true),
+  mAutoSubTicks(true),
+  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  mSelectedTickPen(QPen(Qt::blue, 2)),
+  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  mSelectedSubTickPen(QPen(Qt::blue, 2)),
+  // scale and range:
+  mRange(0, 5),
+  mRangeReversed(false),
+  mScaleType(stLinear),
+  mScaleLogBase(10),
+  mScaleLogBaseLogInv(1.0/qLn(mScaleLogBase)),
+  // internal members:
+  mGrid(new QCPGrid(this)),
+  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
+  mLowestVisibleTick(0),
+  mHighestVisibleTick(-1),
+  mCachedMarginValid(false),
+  mCachedMargin(0)
+{
+  mGrid->setVisible(false);
+  setAntialiased(false);
+  setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again
+
+  if (type == atTop)
+  {
+    setTickLabelPadding(3);
+    setLabelPadding(6);
+  } else if (type == atRight)
+  {
+    setTickLabelPadding(7);
+    setLabelPadding(12);
+  } else if (type == atBottom)
+  {
+    setTickLabelPadding(3);
+    setLabelPadding(3);
+  } else if (type == atLeft)
+  {
+    setTickLabelPadding(5);
+    setLabelPadding(10);
+  }
+}
+
+QCPAxis::~QCPAxis()
+{
+  delete mAxisPainter;
+  delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
+}
+
+/* No documentation as it is a property getter */
+int QCPAxis::tickLabelPadding() const
+{
+  return mAxisPainter->tickLabelPadding;
+}
+
+/* No documentation as it is a property getter */
+double QCPAxis::tickLabelRotation() const
+{
+  return mAxisPainter->tickLabelRotation;
+}
+
+/* No documentation as it is a property getter */
+QCPAxis::LabelSide QCPAxis::tickLabelSide() const
+{
+  return mAxisPainter->tickLabelSide;
+}
+
+/* No documentation as it is a property getter */
+QString QCPAxis::numberFormat() const
+{
+  QString result;
+  result.append(mNumberFormatChar);
+  if (mNumberBeautifulPowers)
+  {
+    result.append(QLatin1Char('b'));
+    if (mAxisPainter->numberMultiplyCross)
+      result.append(QLatin1Char('c'));
+  }
+  return result;
+}
+
+/* No documentation as it is a property getter */
+int QCPAxis::tickLengthIn() const
+{
+  return mAxisPainter->tickLengthIn;
+}
+
+/* No documentation as it is a property getter */
+int QCPAxis::tickLengthOut() const
+{
+  return mAxisPainter->tickLengthOut;
+}
+
+/* No documentation as it is a property getter */
+int QCPAxis::subTickLengthIn() const
+{
+  return mAxisPainter->subTickLengthIn;
+}
+
+/* No documentation as it is a property getter */
+int QCPAxis::subTickLengthOut() const
+{
+  return mAxisPainter->subTickLengthOut;
+}
+
+/* No documentation as it is a property getter */
+int QCPAxis::labelPadding() const
+{
+  return mAxisPainter->labelPadding;
+}
+
+/* No documentation as it is a property getter */
+int QCPAxis::offset() const
+{
+  return mAxisPainter->offset;
+}
+
+/* No documentation as it is a property getter */
+QCPLineEnding QCPAxis::lowerEnding() const
+{
+  return mAxisPainter->lowerEnding;
+}
+
+/* No documentation as it is a property getter */
+QCPLineEnding QCPAxis::upperEnding() const
+{
+  return mAxisPainter->upperEnding;
+}
+
+/*!
+  Sets whether the axis uses a linear scale or a logarithmic scale. If \a type is set to \ref
+  stLogarithmic, the logarithm base can be set with \ref setScaleLogBase. In logarithmic axis
+  scaling, major tick marks appear at all powers of the logarithm base. Properties like tick step
+  (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but less major
+  ticks, consider choosing a logarithm base of 100, 1000 or even higher.
+
+  If \a type is \ref stLogarithmic and the number format (\ref setNumberFormat) uses the 'b' option
+  (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
+  [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
+  part). To only display the decimal power, set the number precision to zero with
+  \ref setNumberPrecision.
+*/
+void QCPAxis::setScaleType(QCPAxis::ScaleType type)
+{
+  if (mScaleType != type)
+  {
+    mScaleType = type;
+    if (mScaleType == stLogarithmic)
+      setRange(mRange.sanitizedForLogScale());
+    mCachedMarginValid = false;
+    emit scaleTypeChanged(mScaleType);
+  }
+}
+
+/*!
+  If \ref setScaleType is set to \ref stLogarithmic, \a base will be the logarithm base of the
+  scaling. In logarithmic axis scaling, major tick marks appear at all powers of \a base.
+
+  Properties like tick step (\ref setTickStep) don't apply in logarithmic scaling. If you wish a decimal base but
+  less major ticks, consider choosing \a base 100, 1000 or even higher.
+*/
+void QCPAxis::setScaleLogBase(double base)
+{
+  if (base > 1)
+  {
+    mScaleLogBase = base;
+    mScaleLogBaseLogInv = 1.0/qLn(mScaleLogBase); // buffer for faster baseLog() calculation
+    mCachedMarginValid = false;
+  } else
+    qDebug() << Q_FUNC_INFO << "Invalid logarithmic scale base (must be greater 1):" << base;
+}
+
+/*!
+  Sets the range of the axis.
+
+  This slot may be connected with the \ref rangeChanged signal of another axis so this axis
+  is always synchronized with the other axis range, when it changes.
+
+  To invert the direction of an axis, use \ref setRangeReversed.
+*/
+void QCPAxis::setRange(const QCPRange &range)
+{
+  if (range.lower == mRange.lower && range.upper == mRange.upper)
+    return;
+
+  if (!QCPRange::validRange(range)) return;
+  QCPRange oldRange = mRange;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = range.sanitizedForLogScale();
+  } else
+  {
+    mRange = range.sanitizedForLinScale();
+  }
+  mCachedMarginValid = false;
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains iSelectAxes.)
+
+  However, even when \a selectable is set to a value not allowing the selection of a specific part,
+  it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
+  directly.
+
+  \see SelectablePart, setSelectedParts
+*/
+void QCPAxis::setSelectableParts(const SelectableParts &selectable)
+{
+  if (mSelectableParts != selectable)
+  {
+    mSelectableParts = selectable;
+    emit selectableChanged(mSelectableParts);
+  }
+}
+
+/*!
+  Sets the selected state of the respective axis parts described by \ref SelectablePart. When a part
+  is selected, it uses a different pen/font.
+
+  The entire selection mechanism for axes is handled automatically when \ref
+  QCustomPlot::setInteractions contains iSelectAxes. You only need to call this function when you
+  wish to change the selection state manually.
+
+  This function can change the selection state of a part, independent of the \ref setSelectableParts setting.
+
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+
+  \see SelectablePart, setSelectableParts, selectTest, setSelectedBasePen, setSelectedTickPen, setSelectedSubTickPen,
+  setSelectedTickLabelFont, setSelectedLabelFont, setSelectedTickLabelColor, setSelectedLabelColor
+*/
+void QCPAxis::setSelectedParts(const SelectableParts &selected)
+{
+  if (mSelectedParts != selected)
+  {
+    mSelectedParts = selected;
+    emit selectionChanged(mSelectedParts);
+  }
+}
+
+/*!
+  \overload
+
+  Sets the lower and upper bound of the axis range.
+
+  To invert the direction of an axis, use \ref setRangeReversed.
+
+  There is also a slot to set a range, see \ref setRange(const QCPRange &range).
+*/
+void QCPAxis::setRange(double lower, double upper)
+{
+  if (lower == mRange.lower && upper == mRange.upper)
+    return;
+
+  if (!QCPRange::validRange(lower, upper)) return;
+  QCPRange oldRange = mRange;
+  mRange.lower = lower;
+  mRange.upper = upper;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = mRange.sanitizedForLogScale();
+  } else
+  {
+    mRange = mRange.sanitizedForLinScale();
+  }
+  mCachedMarginValid = false;
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  \overload
+
+  Sets the range of the axis.
+
+  The \a position coordinate indicates together with the \a alignment parameter, where the new
+  range will be positioned. \a size defines the size of the new axis range. \a alignment may be
+  Qt::AlignLeft, Qt::AlignRight or Qt::AlignCenter. This will cause the left border, right border,
+  or center of the range to be aligned with \a position. Any other values of \a alignment will
+  default to Qt::AlignCenter.
+*/
+void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
+{
+  if (alignment == Qt::AlignLeft)
+    setRange(position, position+size);
+  else if (alignment == Qt::AlignRight)
+    setRange(position-size, position);
+  else // alignment == Qt::AlignCenter
+    setRange(position-size/2.0, position+size/2.0);
+}
+
+/*!
+  Sets the lower bound of the axis range. The upper bound is not changed.
+  \see setRange
+*/
+void QCPAxis::setRangeLower(double lower)
+{
+  if (mRange.lower == lower)
+    return;
+
+  QCPRange oldRange = mRange;
+  mRange.lower = lower;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = mRange.sanitizedForLogScale();
+  } else
+  {
+    mRange = mRange.sanitizedForLinScale();
+  }
+  mCachedMarginValid = false;
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Sets the upper bound of the axis range. The lower bound is not changed.
+  \see setRange
+*/
+void QCPAxis::setRangeUpper(double upper)
+{
+  if (mRange.upper == upper)
+    return;
+
+  QCPRange oldRange = mRange;
+  mRange.upper = upper;
+  if (mScaleType == stLogarithmic)
+  {
+    mRange = mRange.sanitizedForLogScale();
+  } else
+  {
+    mRange = mRange.sanitizedForLinScale();
+  }
+  mCachedMarginValid = false;
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Sets whether the axis range (direction) is displayed reversed. Normally, the values on horizontal
+  axes increase left to right, on vertical axes bottom to top. When \a reversed is set to true, the
+  direction of increasing values is inverted.
+
+  Note that the range and data interface stays the same for reversed axes, e.g. the \a lower part
+  of the \ref setRange interface will still reference the mathematically smaller number than the \a
+  upper part.
+*/
+void QCPAxis::setRangeReversed(bool reversed)
+{
+  if (mRangeReversed != reversed)
+  {
+    mRangeReversed = reversed;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets whether the tick positions should be calculated automatically (either from an automatically
+  generated tick step or a tick step provided manually via \ref setTickStep, see \ref setAutoTickStep).
+
+  If \a on is set to false, you must provide the tick positions manually via \ref setTickVector.
+  For these manual ticks you may let QCPAxis generate the appropriate labels automatically by
+  leaving \ref setAutoTickLabels set to true. If you also wish to control the displayed labels
+  manually, set \ref setAutoTickLabels to false and provide the label strings with \ref
+  setTickVectorLabels.
+
+  If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
+  vectors in a slot connected to the \ref ticksRequest signal.
+
+  \see setAutoTickLabels, setAutoSubTicks, setAutoTickCount, setAutoTickStep
+*/
+void QCPAxis::setAutoTicks(bool on)
+{
+  if (mAutoTicks != on)
+  {
+    mAutoTicks = on;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  When \ref setAutoTickStep is true, \a approximateCount determines how many ticks should be
+  generated in the visible range, approximately.
+
+  It's not guaranteed that this number of ticks is met exactly, but approximately within a
+  tolerance of about two.
+
+  Only values greater than zero are accepted as \a approximateCount.
+
+  \see setAutoTickStep, setAutoTicks, setAutoSubTicks
+*/
+void QCPAxis::setAutoTickCount(int approximateCount)
+{
+  if (mAutoTickCount != approximateCount)
+  {
+    if (approximateCount > 0)
+    {
+      mAutoTickCount = approximateCount;
+      mCachedMarginValid = false;
+    } else
+      qDebug() << Q_FUNC_INFO << "approximateCount must be greater than zero:" << approximateCount;
+  }
+}
+
+/*!
+  Sets whether the tick labels are generated automatically. Depending on the tick label type (\ref
+  ltNumber or \ref ltDateTime), the labels will either show the coordinate as floating point
+  number (\ref setNumberFormat), or a date/time formatted according to \ref setDateTimeFormat.
+
+  If \a on is set to false, you should provide the tick labels via \ref setTickVectorLabels. This
+  is usually used in a combination with \ref setAutoTicks set to false for complete control over
+  tick positions and labels, e.g. when the ticks should be at multiples of pi and show "2pi", "3pi"
+  etc. as tick labels.
+
+  If you need dynamically calculated tick vectors (and possibly tick label vectors), set the
+  vectors in a slot connected to the \ref ticksRequest signal.
+
+  \see setAutoTicks
+*/
+void QCPAxis::setAutoTickLabels(bool on)
+{
+  if (mAutoTickLabels != on)
+  {
+    mAutoTickLabels = on;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets whether the tick step, i.e. the interval between two (major) ticks, is calculated
+  automatically. If \a on is set to true, the axis finds a tick step that is reasonable for human
+  readable plots.
+
+  The number of ticks the algorithm aims for within the visible range can be specified with \ref
+  setAutoTickCount.
+
+  If \a on is set to false, you may set the tick step manually with \ref setTickStep.
+
+  \see setAutoTicks, setAutoSubTicks, setAutoTickCount
+*/
+void QCPAxis::setAutoTickStep(bool on)
+{
+  if (mAutoTickStep != on)
+  {
+    mAutoTickStep = on;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets whether the number of sub ticks in one tick interval is determined automatically. This
+  works, as long as the tick step mantissa is a multiple of 0.5. When \ref setAutoTickStep is
+  enabled, this is always the case.
+
+  When \a on is set to false, you may set the sub tick count with \ref setSubTickCount manually.
+
+  \see setAutoTickCount, setAutoTicks, setAutoTickStep
+*/
+void QCPAxis::setAutoSubTicks(bool on)
+{
+  if (mAutoSubTicks != on)
+  {
+    mAutoSubTicks = on;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets whether tick marks are displayed.
+
+  Note that setting \a show to false does not imply that tick labels are invisible, too. To achieve
+  that, see \ref setTickLabels.
+*/
+void QCPAxis::setTicks(bool show)
+{
+  if (mTicks != show)
+  {
+    mTicks = show;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets whether tick labels are displayed. Tick labels are the numbers drawn next to tick marks.
+*/
+void QCPAxis::setTickLabels(bool show)
+{
+  if (mTickLabels != show)
+  {
+    mTickLabels = show;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the distance between the axis base line (including any outward ticks) and the tick labels.
+  \see setLabelPadding, setPadding
+*/
+void QCPAxis::setTickLabelPadding(int padding)
+{
+  if (mAxisPainter->tickLabelPadding != padding)
+  {
+    mAxisPainter->tickLabelPadding = padding;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets whether the tick labels display numbers or dates/times.
+
+  If \a type is set to \ref ltNumber, the format specifications of \ref setNumberFormat apply.
+
+  If \a type is set to \ref ltDateTime, the format specifications of \ref setDateTimeFormat apply.
+
+  In QCustomPlot, date/time coordinates are <tt>double</tt> numbers representing the seconds since
+  1970-01-01T00:00:00 UTC. This format can be retrieved from QDateTime objects with the
+  QDateTime::toTime_t() function. Since this only gives a resolution of one second, there is also
+  the QDateTime::toMSecsSinceEpoch() function which returns the timespan described above in
+  milliseconds. Divide its return value by 1000.0 to get a value with the format needed for
+  date/time plotting, with a resolution of one millisecond.
+
+  Using the toMSecsSinceEpoch function allows dates that go back to 2nd January 4713 B.C.
+  (represented by a negative number), unlike the toTime_t function, which works with unsigned
+  integers and thus only goes back to 1st January 1970. So both for range and accuracy, use of
+  toMSecsSinceEpoch()/1000.0 should be preferred as key coordinate for date/time axes.
+
+  \see setTickLabels
+*/
+void QCPAxis::setTickLabelType(LabelType type)
+{
+  if (mTickLabelType != type)
+  {
+    mTickLabelType = type;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the font of the tick labels.
+
+  \see setTickLabels, setTickLabelColor
+*/
+void QCPAxis::setTickLabelFont(const QFont &font)
+{
+  if (font != mTickLabelFont)
+  {
+    mTickLabelFont = font;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the color of the tick labels.
+
+  \see setTickLabels, setTickLabelFont
+*/
+void QCPAxis::setTickLabelColor(const QColor &color)
+{
+  if (color != mTickLabelColor)
+  {
+    mTickLabelColor = color;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the rotation of the tick labels. If \a degrees is zero, the labels are drawn normally. Else,
+  the tick labels are drawn rotated by \a degrees clockwise. The specified angle is bound to values
+  from -90 to 90 degrees.
+
+  If \a degrees is exactly -90, 0 or 90, the tick labels are centered on the tick coordinate. For
+  other angles, the label is drawn with an offset such that it seems to point toward or away from
+  the tick mark.
+*/
+void QCPAxis::setTickLabelRotation(double degrees)
+{
+  if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
+  {
+    mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets whether the tick labels (numbers) shall appear inside or outside the axis rect.
+
+  The usual and default setting is \ref lsOutside. Very compact plots sometimes require tick labels
+  to be inside the axis rect, to save space. If \a side is set to \ref lsInside, the tick labels
+  appear on the inside are additionally clipped to the axis rect.
+*/
+void QCPAxis::setTickLabelSide(LabelSide side)
+{
+  mAxisPainter->tickLabelSide = side;
+  mCachedMarginValid = false;
+}
+
+/*!
+  Sets the format in which dates and times are displayed as tick labels, if \ref setTickLabelType is \ref ltDateTime.
+  for details about the \a format string, see the documentation of QDateTime::toString().
+
+  Newlines can be inserted with "\n".
+
+  \see setDateTimeSpec
+*/
+void QCPAxis::setDateTimeFormat(const QString &format)
+{
+  if (mDateTimeFormat != format)
+  {
+    mDateTimeFormat = format;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the time spec that is used for the date time values when \ref setTickLabelType is \ref
+  ltDateTime.
+
+  The default value of QDateTime objects (and also QCustomPlot) is <tt>Qt::LocalTime</tt>. However,
+  if the date time values passed to QCustomPlot are given in the UTC spec, set \a
+  timeSpec to <tt>Qt::UTC</tt> to get the correct axis labels.
+
+  \see setDateTimeFormat
+*/
+void QCPAxis::setDateTimeSpec(const Qt::TimeSpec &timeSpec)
+{
+  mDateTimeSpec = timeSpec;
+}
+
+/*!
+  Sets the number format for the numbers drawn as tick labels (if tick label type is \ref
+  ltNumber). This \a formatCode is an extended version of the format code used e.g. by
+  QString::number() and QLocale::toString(). For reference about that, see the "Argument Formats"
+  section in the detailed description of the QString class. \a formatCode is a string of one, two
+  or three characters. The first character is identical to the normal format code used by Qt. In
+  short, this means: 'e'/'E' scientific format, 'f' fixed format, 'g'/'G' scientific or fixed,
+  whichever is shorter.
+
+  The second and third characters are optional and specific to QCustomPlot:\n
+  If the first char was 'e' or 'g', numbers are/might be displayed in the scientific format, e.g.
+  "5.5e9", which is ugly in a plot. So when the second char of \a formatCode is set to 'b' (for
+  "beautiful"), those exponential numbers are formatted in a more natural way, i.e. "5.5
+  [multiplication sign] 10 [superscript] 9". By default, the multiplication sign is a centered dot.
+  If instead a cross should be shown (as is usual in the USA), the third char of \a formatCode can
+  be set to 'c'. The inserted multiplication signs are the UTF-8 characters 215 (0xD7) for the
+  cross and 183 (0xB7) for the dot.
+
+  If the scale type (\ref setScaleType) is \ref stLogarithmic and the \a formatCode uses the 'b'
+  option (beautifully typeset decimal powers), the display usually is "1 [multiplication sign] 10
+  [superscript] n", which looks unnatural for logarithmic scaling (the "1 [multiplication sign]"
+  part). To only display the decimal power, set the number precision to zero with \ref
+  setNumberPrecision.
+
+  Examples for \a formatCode:
+  \li \c g normal format code behaviour. If number is small, fixed format is used, if number is large,
+  normal scientific format is used
+  \li \c gb If number is small, fixed format is used, if number is large, scientific format is used with
+  beautifully typeset decimal powers and a dot as multiplication sign
+  \li \c ebc All numbers are in scientific format with beautifully typeset decimal power and a cross as
+  multiplication sign
+  \li \c fb illegal format code, since fixed format doesn't support (or need) beautifully typeset decimal
+  powers. Format code will be reduced to 'f'.
+  \li \c hello illegal format code, since first char is not 'e', 'E', 'f', 'g' or 'G'. Current format
+  code will not be changed.
+*/
+void QCPAxis::setNumberFormat(const QString &formatCode)
+{
+  if (formatCode.isEmpty())
+  {
+    qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
+    return;
+  }
+  mCachedMarginValid = false;
+
+  // interpret first char as number format char:
+  QString allowedFormatChars(QLatin1String("eEfgG"));
+  if (allowedFormatChars.contains(formatCode.at(0)))
+  {
+    mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
+    return;
+  }
+  if (formatCode.length() < 2)
+  {
+    mNumberBeautifulPowers = false;
+    mAxisPainter->numberMultiplyCross = false;
+    return;
+  }
+
+  // interpret second char as indicator for beautiful decimal powers:
+  if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
+  {
+    mNumberBeautifulPowers = true;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
+    return;
+  }
+  if (formatCode.length() < 3)
+  {
+    mAxisPainter->numberMultiplyCross = false;
+    return;
+  }
+
+  // interpret third char as indicator for dot or cross multiplication symbol:
+  if (formatCode.at(2) == QLatin1Char('c'))
+  {
+    mAxisPainter->numberMultiplyCross = true;
+  } else if (formatCode.at(2) == QLatin1Char('d'))
+  {
+    mAxisPainter->numberMultiplyCross = false;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
+    return;
+  }
+}
+
+/*!
+  Sets the precision of the tick label numbers. See QLocale::toString(double i, char f, int prec)
+  for details. The effect of precisions are most notably for number Formats starting with 'e', see
+  \ref setNumberFormat
+
+  If the scale type (\ref setScaleType) is \ref stLogarithmic and the number format (\ref
+  setNumberFormat) uses the 'b' format code (beautifully typeset decimal powers), the display
+  usually is "1 [multiplication sign] 10 [superscript] n", which looks unnatural for logarithmic
+  scaling (the redundant "1 [multiplication sign]" part). To only display the decimal power "10
+  [superscript] n", set \a precision to zero.
+*/
+void QCPAxis::setNumberPrecision(int precision)
+{
+  if (mNumberPrecision != precision)
+  {
+    mNumberPrecision = precision;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  If \ref setAutoTickStep is set to false, use this function to set the tick step manually.
+  The tick step is the interval between (major) ticks, in plot coordinates.
+  \see setSubTickCount
+*/
+void QCPAxis::setTickStep(double step)
+{
+  if (mTickStep != step)
+  {
+    mTickStep = step;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  If you want full control over what ticks (and possibly labels) the axes show, this function is
+  used to set the coordinates at which ticks will appear.\ref setAutoTicks must be disabled, else
+  the provided tick vector will be overwritten with automatically generated tick coordinates upon
+  replot. The labels of the ticks can be generated automatically when \ref setAutoTickLabels is
+  left enabled. If it is disabled, you can set the labels manually with \ref setTickVectorLabels.
+
+  \a vec is a vector containing the positions of the ticks, in plot coordinates.
+
+  \warning \a vec must be sorted in ascending order, no additional checks are made to ensure this.
+
+  \see setTickVectorLabels
+*/
+void QCPAxis::setTickVector(const QVector<double> &vec)
+{
+  // don't check whether mTickVector != vec here, because it takes longer than we would save
+  mTickVector = vec;
+  mCachedMarginValid = false;
+}
+
+/*!
+  If you want full control over what ticks and labels the axes show, this function is used to set a
+  number of QStrings that will be displayed at the tick positions which you need to provide with
+  \ref setTickVector. These two vectors should have the same size. (Note that you need to disable
+  \ref setAutoTicks and \ref setAutoTickLabels first.)
+
+  \a vec is a vector containing the labels of the ticks. The entries correspond to the respective
+  indices in the tick vector, passed via \ref setTickVector.
+
+  \see setTickVector
+*/
+void QCPAxis::setTickVectorLabels(const QVector<QString> &vec)
+{
+  // don't check whether mTickVectorLabels != vec here, because it takes longer than we would save
+  mTickVectorLabels = vec;
+  mCachedMarginValid = false;
+}
+
+/*!
+  Sets the length of the ticks in pixels. \a inside is the length the ticks will reach inside the
+  plot and \a outside is the length they will reach outside the plot. If \a outside is greater than
+  zero, the tick labels and axis label will increase their distance to the axis accordingly, so
+  they won't collide with the ticks.
+
+  \see setSubTickLength, setTickLengthIn, setTickLengthOut
+*/
+void QCPAxis::setTickLength(int inside, int outside)
+{
+  setTickLengthIn(inside);
+  setTickLengthOut(outside);
+}
+
+/*!
+  Sets the length of the inward ticks in pixels. \a inside is the length the ticks will reach
+  inside the plot.
+
+  \see setTickLengthOut, setTickLength, setSubTickLength
+*/
+void QCPAxis::setTickLengthIn(int inside)
+{
+  if (mAxisPainter->tickLengthIn != inside)
+  {
+    mAxisPainter->tickLengthIn = inside;
+  }
+}
+
+/*!
+  Sets the length of the outward ticks in pixels. \a outside is the length the ticks will reach
+  outside the plot. If \a outside is greater than zero, the tick labels and axis label will
+  increase their distance to the axis accordingly, so they won't collide with the ticks.
+
+  \see setTickLengthIn, setTickLength, setSubTickLength
+*/
+void QCPAxis::setTickLengthOut(int outside)
+{
+  if (mAxisPainter->tickLengthOut != outside)
+  {
+    mAxisPainter->tickLengthOut = outside;
+    mCachedMarginValid = false; // only outside tick length can change margin
+  }
+}
+
+/*!
+  Sets the number of sub ticks in one (major) tick step. A sub tick count of three for example,
+  divides the tick intervals in four sub intervals.
+
+  By default, the number of sub ticks is chosen automatically in a reasonable manner as long as the
+  mantissa of the tick step is a multiple of 0.5. When \ref setAutoTickStep is enabled, this is
+  always the case.
+
+  If you want to disable automatic sub tick count and use this function to set the count manually,
+  see \ref setAutoSubTicks.
+*/
+void QCPAxis::setSubTickCount(int count)
+{
+  mSubTickCount = count;
+}
+
+/*!
+  Sets the length of the subticks in pixels. \a inside is the length the subticks will reach inside
+  the plot and \a outside is the length they will reach outside the plot. If \a outside is greater
+  than zero, the tick labels and axis label will increase their distance to the axis accordingly,
+  so they won't collide with the ticks.
+
+  \see setTickLength, setSubTickLengthIn, setSubTickLengthOut
+*/
+void QCPAxis::setSubTickLength(int inside, int outside)
+{
+  setSubTickLengthIn(inside);
+  setSubTickLengthOut(outside);
+}
+
+/*!
+  Sets the length of the inward subticks in pixels. \a inside is the length the subticks will reach inside
+  the plot.
+
+  \see setSubTickLengthOut, setSubTickLength, setTickLength
+*/
+void QCPAxis::setSubTickLengthIn(int inside)
+{
+  if (mAxisPainter->subTickLengthIn != inside)
+  {
+    mAxisPainter->subTickLengthIn = inside;
+  }
+}
+
+/*!
+  Sets the length of the outward subticks in pixels. \a outside is the length the subticks will reach
+  outside the plot. If \a outside is greater than zero, the tick labels will increase their
+  distance to the axis accordingly, so they won't collide with the ticks.
+
+  \see setSubTickLengthIn, setSubTickLength, setTickLength
+*/
+void QCPAxis::setSubTickLengthOut(int outside)
+{
+  if (mAxisPainter->subTickLengthOut != outside)
+  {
+    mAxisPainter->subTickLengthOut = outside;
+    mCachedMarginValid = false; // only outside tick length can change margin
+  }
+}
+
+/*!
+  Sets the pen, the axis base line is drawn with.
+
+  \see setTickPen, setSubTickPen
+*/
+void QCPAxis::setBasePen(const QPen &pen)
+{
+  mBasePen = pen;
+}
+
+/*!
+  Sets the pen, tick marks will be drawn with.
+
+  \see setTickLength, setBasePen
+*/
+void QCPAxis::setTickPen(const QPen &pen)
+{
+  mTickPen = pen;
+}
+
+/*!
+  Sets the pen, subtick marks will be drawn with.
+
+  \see setSubTickCount, setSubTickLength, setBasePen
+*/
+void QCPAxis::setSubTickPen(const QPen &pen)
+{
+  mSubTickPen = pen;
+}
+
+/*!
+  Sets the font of the axis label.
+
+  \see setLabelColor
+*/
+void QCPAxis::setLabelFont(const QFont &font)
+{
+  if (mLabelFont != font)
+  {
+    mLabelFont = font;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the color of the axis label.
+
+  \see setLabelFont
+*/
+void QCPAxis::setLabelColor(const QColor &color)
+{
+  mLabelColor = color;
+}
+
+/*!
+  Sets the text of the axis label that will be shown below/above or next to the axis, depending on
+  its orientation. To disable axis labels, pass an empty string as \a str.
+*/
+void QCPAxis::setLabel(const QString &str)
+{
+  if (mLabel != str)
+  {
+    mLabel = str;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the distance between the tick labels and the axis label.
+
+  \see setTickLabelPadding, setPadding
+*/
+void QCPAxis::setLabelPadding(int padding)
+{
+  if (mAxisPainter->labelPadding != padding)
+  {
+    mAxisPainter->labelPadding = padding;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the padding of the axis.
+
+  When \ref QCPAxisRect::setAutoMargins is enabled, the padding is the additional outer most space,
+  that is left blank.
+
+  The axis padding has no meaning if \ref QCPAxisRect::setAutoMargins is disabled.
+
+  \see setLabelPadding, setTickLabelPadding
+*/
+void QCPAxis::setPadding(int padding)
+{
+  if (mPadding != padding)
+  {
+    mPadding = padding;
+    mCachedMarginValid = false;
+  }
+}
+
+/*!
+  Sets the offset the axis has to its axis rect side.
+
+  If an axis rect side has multiple axes and automatic margin calculation is enabled for that side,
+  only the offset of the inner most axis has meaning (even if it is set to be invisible). The
+  offset of the other, outer axes is controlled automatically, to place them at appropriate
+  positions.
+*/
+void QCPAxis::setOffset(int offset)
+{
+  mAxisPainter->offset = offset;
+}
+
+/*!
+  Sets the font that is used for tick labels when they are selected.
+
+  \see setTickLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedTickLabelFont(const QFont &font)
+{
+  if (font != mSelectedTickLabelFont)
+  {
+    mSelectedTickLabelFont = font;
+    // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
+  }
+}
+
+/*!
+  Sets the font that is used for the axis label when it is selected.
+
+  \see setLabelFont, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedLabelFont(const QFont &font)
+{
+  mSelectedLabelFont = font;
+  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
+}
+
+/*!
+  Sets the color that is used for tick labels when they are selected.
+
+  \see setTickLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedTickLabelColor(const QColor &color)
+{
+  if (color != mSelectedTickLabelColor)
+  {
+    mSelectedTickLabelColor = color;
+  }
+}
+
+/*!
+  Sets the color that is used for the axis label when it is selected.
+
+  \see setLabelColor, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedLabelColor(const QColor &color)
+{
+  mSelectedLabelColor = color;
+}
+
+/*!
+  Sets the pen that is used to draw the axis base line when selected.
+
+  \see setBasePen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedBasePen(const QPen &pen)
+{
+  mSelectedBasePen = pen;
+}
+
+/*!
+  Sets the pen that is used to draw the (major) ticks when selected.
+
+  \see setTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedTickPen(const QPen &pen)
+{
+  mSelectedTickPen = pen;
+}
+
+/*!
+  Sets the pen that is used to draw the subticks when selected.
+
+  \see setSubTickPen, setSelectableParts, setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPAxis::setSelectedSubTickPen(const QPen &pen)
+{
+  mSelectedSubTickPen = pen;
+}
+
+/*!
+  Sets the style for the lower axis ending. See the documentation of QCPLineEnding for available
+  styles.
+
+  For horizontal axes, this method refers to the left ending, for vertical axes the bottom ending.
+  Note that this meaning does not change when the axis range is reversed with \ref
+  setRangeReversed.
+
+  \see setUpperEnding
+*/
+void QCPAxis::setLowerEnding(const QCPLineEnding &ending)
+{
+  mAxisPainter->lowerEnding = ending;
+}
+
+/*!
+  Sets the style for the upper axis ending. See the documentation of QCPLineEnding for available
+  styles.
+
+  For horizontal axes, this method refers to the right ending, for vertical axes the top ending.
+  Note that this meaning does not change when the axis range is reversed with \ref
+  setRangeReversed.
+
+  \see setLowerEnding
+*/
+void QCPAxis::setUpperEnding(const QCPLineEnding &ending)
+{
+  mAxisPainter->upperEnding = ending;
+}
+
+/*!
+  If the scale type (\ref setScaleType) is \ref stLinear, \a diff is added to the lower and upper
+  bounds of the range. The range is simply moved by \a diff.
+
+  If the scale type is \ref stLogarithmic, the range bounds are multiplied by \a diff. This
+  corresponds to an apparent "linear" move in logarithmic scaling by a distance of log(diff).
+*/
+void QCPAxis::moveRange(double diff)
+{
+  QCPRange oldRange = mRange;
+  if (mScaleType == stLinear)
+  {
+    mRange.lower += diff;
+    mRange.upper += diff;
+  } else // mScaleType == stLogarithmic
+  {
+    mRange.lower *= diff;
+    mRange.upper *= diff;
+  }
+  mCachedMarginValid = false;
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Scales the range of this axis by \a factor around the coordinate \a center. For example, if \a
+  factor is 2.0, \a center is 1.0, then the axis range will double its size, and the point at
+  coordinate 1.0 won't have changed its position in the QCustomPlot widget (i.e. coordinates
+  around 1.0 will have moved symmetrically closer to 1.0).
+*/
+void QCPAxis::scaleRange(double factor, double center)
+{
+  QCPRange oldRange = mRange;
+  if (mScaleType == stLinear)
+  {
+    QCPRange newRange;
+    newRange.lower = (mRange.lower-center)*factor + center;
+    newRange.upper = (mRange.upper-center)*factor + center;
+    if (QCPRange::validRange(newRange))
+      mRange = newRange.sanitizedForLinScale();
+  } else // mScaleType == stLogarithmic
+  {
+    if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
+    {
+      QCPRange newRange;
+      newRange.lower = qPow(mRange.lower/center, factor)*center;
+      newRange.upper = qPow(mRange.upper/center, factor)*center;
+      if (QCPRange::validRange(newRange))
+        mRange = newRange.sanitizedForLogScale();
+    } else
+      qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
+  }
+  mCachedMarginValid = false;
+  emit rangeChanged(mRange);
+  emit rangeChanged(mRange, oldRange);
+}
+
+/*!
+  Scales the range of this axis to have a certain scale \a ratio to \a otherAxis. The scaling will
+  be done around the center of the current axis range.
+
+  For example, if \a ratio is 1, this axis is the \a yAxis and \a otherAxis is \a xAxis, graphs
+  plotted with those axes will appear in a 1:1 aspect ratio, independent of the aspect ratio the
+  axis rect has.
+
+  This is an operation that changes the range of this axis once, it doesn't fix the scale ratio
+  indefinitely. Note that calling this function in the constructor of the QCustomPlot's parent
+  won't have the desired effect, since the widget dimensions aren't defined yet, and a resizeEvent
+  will follow.
+*/
+void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
+{
+  int otherPixelSize, ownPixelSize;
+
+  if (otherAxis->orientation() == Qt::Horizontal)
+    otherPixelSize = otherAxis->axisRect()->width();
+  else
+    otherPixelSize = otherAxis->axisRect()->height();
+
+  if (orientation() == Qt::Horizontal)
+    ownPixelSize = axisRect()->width();
+  else
+    ownPixelSize = axisRect()->height();
+
+  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
+  setRange(range().center(), newRangeSize, Qt::AlignCenter);
+}
+
+/*!
+  Changes the axis range such that all plottables associated with this axis are fully visible in
+  that dimension.
+
+  \see QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
+*/
+void QCPAxis::rescale(bool onlyVisiblePlottables)
+{
+  QList<QCPAbstractPlottable*> p = plottables();
+  QCPRange newRange;
+  bool haveRange = false;
+  for (int i=0; i<p.size(); ++i)
+  {
+    if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
+      continue;
+    QCPRange plottableRange;
+    bool currentFoundRange;
+    QCPAbstractPlottable::SignDomain signDomain = QCPAbstractPlottable::sdBoth;
+    if (mScaleType == stLogarithmic)
+      signDomain = (mRange.upper < 0 ? QCPAbstractPlottable::sdNegative : QCPAbstractPlottable::sdPositive);
+    if (p.at(i)->keyAxis() == this)
+      plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
+    else
+      plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
+    if (currentFoundRange)
+    {
+      if (!haveRange)
+        newRange = plottableRange;
+      else
+        newRange.expand(plottableRange);
+      haveRange = true;
+    }
+  }
+  if (haveRange)
+  {
+    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
+    {
+      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
+      if (mScaleType == stLinear)
+      {
+        newRange.lower = center-mRange.size()/2.0;
+        newRange.upper = center+mRange.size()/2.0;
+      } else // mScaleType == stLogarithmic
+      {
+        newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
+        newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
+      }
+    }
+    setRange(newRange);
+  }
+}
+
+/*!
+  Transforms \a value, in pixel coordinates of the QCustomPlot widget, to axis coordinates.
+*/
+double QCPAxis::pixelToCoord(double value) const
+{
+  if (orientation() == Qt::Horizontal)
+  {
+    if (mScaleType == stLinear)
+    {
+      if (!mRangeReversed)
+        return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
+      else
+        return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
+    } else // mScaleType == stLogarithmic
+    {
+      if (!mRangeReversed)
+        return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
+      else
+        return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
+    }
+  } else // orientation() == Qt::Vertical
+  {
+    if (mScaleType == stLinear)
+    {
+      if (!mRangeReversed)
+        return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
+      else
+        return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
+    } else // mScaleType == stLogarithmic
+    {
+      if (!mRangeReversed)
+        return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
+      else
+        return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
+    }
+  }
+}
+
+/*!
+  Transforms \a value, in coordinates of the axis, to pixel coordinates of the QCustomPlot widget.
+*/
+double QCPAxis::coordToPixel(double value) const
+{
+  if (orientation() == Qt::Horizontal)
+  {
+    if (mScaleType == stLinear)
+    {
+      if (!mRangeReversed)
+        return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
+      else
+        return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
+    } else // mScaleType == stLogarithmic
+    {
+      if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
+        return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
+      else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
+        return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
+      else
+      {
+        if (!mRangeReversed)
+          return baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
+        else
+          return baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
+      }
+    }
+  } else // orientation() == Qt::Vertical
+  {
+    if (mScaleType == stLinear)
+    {
+      if (!mRangeReversed)
+        return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
+      else
+        return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
+    } else // mScaleType == stLogarithmic
+    {
+      if (value >= 0 && mRange.upper < 0) // invalid value for logarithmic scale, just draw it outside visible range
+        return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
+      else if (value <= 0 && mRange.upper > 0) // invalid value for logarithmic scale, just draw it outside visible range
+        return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
+      else
+      {
+        if (!mRangeReversed)
+          return mAxisRect->bottom()-baseLog(value/mRange.lower)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
+        else
+          return mAxisRect->bottom()-baseLog(mRange.upper/value)/baseLog(mRange.upper/mRange.lower)*mAxisRect->height();
+      }
+    }
+  }
+}
+
+/*!
+  Returns the part of the axis that is hit by \a pos (in pixels). The return value of this function
+  is independent of the user-selectable parts defined with \ref setSelectableParts. Further, this
+  function does not change the current selection state of the axis.
+
+  If the axis is not visible (\ref setVisible), this function always returns \ref spNone.
+
+  \see setSelectedParts, setSelectableParts, QCustomPlot::setInteractions
+*/
+QCPAxis::SelectablePart QCPAxis::getPartAt(const QPointF &pos) const
+{
+  if (!mVisible)
+    return spNone;
+
+  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
+    return spAxis;
+  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
+    return spTickLabels;
+  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
+    return spAxisLabel;
+  else
+    return spNone;
+}
+
+/* inherits documentation from base class */
+double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  if (!mParentPlot) return -1;
+  SelectablePart part = getPartAt(pos);
+  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
+    return -1;
+
+  if (details)
+    details->setValue(part);
+  return mParentPlot->selectionTolerance()*0.99;
+}
+
+/*!
+  Returns a list of all the plottables that have this axis as key or value axis.
+
+  If you are only interested in plottables of type QCPGraph, see \ref graphs.
+
+  \see graphs, items
+*/
+QList<QCPAbstractPlottable*> QCPAxis::plottables() const
+{
+  QList<QCPAbstractPlottable*> result;
+  if (!mParentPlot) return result;
+
+  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
+  {
+    if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
+      result.append(mParentPlot->mPlottables.at(i));
+  }
+  return result;
+}
+
+/*!
+  Returns a list of all the graphs that have this axis as key or value axis.
+
+  \see plottables, items
+*/
+QList<QCPGraph*> QCPAxis::graphs() const
+{
+  QList<QCPGraph*> result;
+  if (!mParentPlot) return result;
+
+  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
+  {
+    if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
+      result.append(mParentPlot->mGraphs.at(i));
+  }
+  return result;
+}
+
+/*!
+  Returns a list of all the items that are associated with this axis. An item is considered
+  associated with an axis if at least one of its positions uses the axis as key or value axis.
+
+  \see plottables, graphs
+*/
+QList<QCPAbstractItem*> QCPAxis::items() const
+{
+  QList<QCPAbstractItem*> result;
+  if (!mParentPlot) return result;
+
+  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
+  {
+    QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
+    for (int posId=0; posId<positions.size(); ++posId)
+    {
+      if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
+      {
+        result.append(mParentPlot->mItems.at(itemId));
+        break;
+      }
+    }
+  }
+  return result;
+}
+
+/*!
+  Transforms a margin side to the logically corresponding axis type. (QCP::msLeft to
+  QCPAxis::atLeft, QCP::msRight to QCPAxis::atRight, etc.)
+*/
+QCPAxis::AxisType QCPAxis::marginSideToAxisType(QCP::MarginSide side)
+{
+  switch (side)
+  {
+    case QCP::msLeft: return atLeft;
+    case QCP::msRight: return atRight;
+    case QCP::msTop: return atTop;
+    case QCP::msBottom: return atBottom;
+    default: break;
+  }
+  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
+  return atLeft;
+}
+
+/*!
+  Returns the axis type that describes the opposite axis of an axis with the specified \a type.
+*/
+QCPAxis::AxisType QCPAxis::opposite(QCPAxis::AxisType type)
+{
+  switch (type)
+  {
+    case atLeft: return atRight; break;
+    case atRight: return atLeft; break;
+    case atBottom: return atTop; break;
+    case atTop: return atBottom; break;
+    default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
+  }
+}
+
+/*! \internal
+
+  This function is called to prepare the tick vector, sub tick vector and tick label vector. If
+  \ref setAutoTicks is set to true, appropriate tick values are determined automatically via \ref
+  generateAutoTicks. If it's set to false, the signal ticksRequest is emitted, which can be used to
+  provide external tick positions. Then the sub tick vectors and tick label vectors are created.
+*/
+void QCPAxis::setupTickVectors()
+{
+  if (!mParentPlot) return;
+  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
+
+  // fill tick vectors, either by auto generating or by notifying user to fill the vectors himself
+  if (mAutoTicks)
+  {
+    generateAutoTicks();
+  } else
+  {
+    emit ticksRequest();
+  }
+
+  visibleTickBounds(mLowestVisibleTick, mHighestVisibleTick);
+  if (mTickVector.isEmpty())
+  {
+    mSubTickVector.clear();
+    return;
+  }
+
+  // generate subticks between ticks:
+  mSubTickVector.resize((mTickVector.size()-1)*mSubTickCount);
+  if (mSubTickCount > 0)
+  {
+    double subTickStep = 0;
+    double subTickPosition = 0;
+    int subTickIndex = 0;
+    bool done = false;
+    int lowTick = mLowestVisibleTick > 0 ? mLowestVisibleTick-1 : mLowestVisibleTick;
+    int highTick = mHighestVisibleTick < mTickVector.size()-1 ? mHighestVisibleTick+1 : mHighestVisibleTick;
+    for (int i=lowTick+1; i<=highTick; ++i)
+    {
+      subTickStep = (mTickVector.at(i)-mTickVector.at(i-1))/(double)(mSubTickCount+1);
+      for (int k=1; k<=mSubTickCount; ++k)
+      {
+        subTickPosition = mTickVector.at(i-1) + k*subTickStep;
+        if (subTickPosition < mRange.lower)
+          continue;
+        if (subTickPosition > mRange.upper)
+        {
+          done = true;
+          break;
+        }
+        mSubTickVector[subTickIndex] = subTickPosition;
+        subTickIndex++;
+      }
+      if (done) break;
+    }
+    mSubTickVector.resize(subTickIndex);
+  }
+
+  // generate tick labels according to tick positions:
+  if (mAutoTickLabels)
+  {
+    int vecsize = mTickVector.size();
+    mTickVectorLabels.resize(vecsize);
+    if (mTickLabelType == ltNumber)
+    {
+      for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
+        mTickVectorLabels[i] = mParentPlot->locale().toString(mTickVector.at(i), mNumberFormatChar.toLatin1(), mNumberPrecision);
+    } else if (mTickLabelType == ltDateTime)
+    {
+      for (int i=mLowestVisibleTick; i<=mHighestVisibleTick; ++i)
+      {
+#if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) // use fromMSecsSinceEpoch function if available, to gain sub-second accuracy on tick labels (e.g. for format "hh:mm:ss:zzz")
+        mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromTime_t(mTickVector.at(i)).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
+#else
+        mTickVectorLabels[i] = mParentPlot->locale().toString(QDateTime::fromMSecsSinceEpoch(mTickVector.at(i)*1000).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
+#endif
+      }
+    }
+  } else // mAutoTickLabels == false
+  {
+    if (mAutoTicks) // ticks generated automatically, but not ticklabels, so emit ticksRequest here for labels
+    {
+      emit ticksRequest();
+    }
+    // make sure provided tick label vector has correct (minimal) length:
+    if (mTickVectorLabels.size() < mTickVector.size())
+      mTickVectorLabels.resize(mTickVector.size());
+  }
+}
+
+/*! \internal
+
+  If \ref setAutoTicks is set to true, this function is called by \ref setupTickVectors to
+  generate reasonable tick positions (and subtick count). The algorithm tries to create
+  approximately <tt>mAutoTickCount</tt> ticks (set via \ref setAutoTickCount).
+
+  If the scale is logarithmic, \ref setAutoTickCount is ignored, and one tick is generated at every
+  power of the current logarithm base, set via \ref setScaleLogBase.
+*/
+void QCPAxis::generateAutoTicks()
+{
+  if (mScaleType == stLinear)
+  {
+    if (mAutoTickStep)
+    {
+      // Generate tick positions according to linear scaling:
+      mTickStep = mRange.size()/(double)(mAutoTickCount+1e-10); // mAutoTickCount ticks on average, the small addition is to prevent jitter on exact integers
+      double magnitudeFactor = qPow(10.0, qFloor(qLn(mTickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
+      double tickStepMantissa = mTickStep/magnitudeFactor;
+      if (tickStepMantissa < 5)
+      {
+        // round digit after decimal point to 0.5
+        mTickStep = (int)(tickStepMantissa*2)/2.0*magnitudeFactor;
+      } else
+      {
+        // round to first digit in multiples of 2
+        mTickStep = (int)(tickStepMantissa/2.0)*2.0*magnitudeFactor;
+      }
+    }
+    if (mAutoSubTicks)
+      mSubTickCount = calculateAutoSubTickCount(mTickStep);
+    // Generate tick positions according to mTickStep:
+    qint64 firstStep = floor(mRange.lower/mTickStep); // do not use qFloor here, or we'll lose 64 bit precision
+    qint64 lastStep = ceil(mRange.upper/mTickStep); // do not use qCeil here, or we'll lose 64 bit precision
+    int tickcount = lastStep-firstStep+1;
+    if (tickcount < 0) tickcount = 0;
+    mTickVector.resize(tickcount);
+    for (int i=0; i<tickcount; ++i)
+      mTickVector[i] = (firstStep+i)*mTickStep;
+  } else // mScaleType == stLogarithmic
+  {
+    // Generate tick positions according to logbase scaling:
+    if (mRange.lower > 0 && mRange.upper > 0) // positive range
+    {
+      double lowerMag = basePow(qFloor(baseLog(mRange.lower)));
+      double currentMag = lowerMag;
+      mTickVector.clear();
+      mTickVector.append(currentMag);
+      while (currentMag < mRange.upper && currentMag > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
+      {
+        currentMag *= mScaleLogBase;
+        mTickVector.append(currentMag);
+      }
+    } else if (mRange.lower < 0 && mRange.upper < 0) // negative range
+    {
+      double lowerMag = -basePow(qCeil(baseLog(-mRange.lower)));
+      double currentMag = lowerMag;
+      mTickVector.clear();
+      mTickVector.append(currentMag);
+      while (currentMag < mRange.upper && currentMag < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
+      {
+        currentMag /= mScaleLogBase;
+        mTickVector.append(currentMag);
+      }
+    } else // invalid range for logarithmic scale, because lower and upper have different sign
+    {
+      mTickVector.clear();
+      qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << mRange.lower << "-" << mRange.upper;
+    }
+  }
+}
+
+/*! \internal
+
+  Called by generateAutoTicks when \ref setAutoSubTicks is set to true. Depending on the \a
+  tickStep between two major ticks on the axis, a different number of sub ticks is appropriate. For
+  Example taking 4 sub ticks for a \a tickStep of 1 makes more sense than taking 5 sub ticks,
+  because this corresponds to a sub tick step of 0.2, instead of the less intuitive 0.16667. Note
+  that a subtick count of 4 means dividing the major tick step into 5 sections.
+
+  This is implemented by a hand made lookup for integer tick steps as well as fractional tick steps
+  with a fractional part of (approximately) 0.5. If a tick step is different (i.e. has no
+  fractional part close to 0.5), the currently set sub tick count (\ref setSubTickCount) is
+  returned.
+*/
+int QCPAxis::calculateAutoSubTickCount(double tickStep) const
+{
+  int result = mSubTickCount; // default to current setting, if no proper value can be found
+
+  // get mantissa of tickstep:
+  double magnitudeFactor = qPow(10.0, qFloor(qLn(tickStep)/qLn(10.0))); // get magnitude factor e.g. 0.01, 1, 10, 1000 etc.
+  double tickStepMantissa = tickStep/magnitudeFactor;
+
+  // separate integer and fractional part of mantissa:
+  double epsilon = 0.01;
+  double intPartf;
+  int intPart;
+  double fracPart = modf(tickStepMantissa, &intPartf);
+  intPart = intPartf;
+
+  // handle cases with (almost) integer mantissa:
+  if (fracPart < epsilon || 1.0-fracPart < epsilon)
+  {
+    if (1.0-fracPart < epsilon)
+      ++intPart;
+    switch (intPart)
+    {
+      case 1: result = 4; break; // 1.0 -> 0.2 substep
+      case 2: result = 3; break; // 2.0 -> 0.5 substep
+      case 3: result = 2; break; // 3.0 -> 1.0 substep
+      case 4: result = 3; break; // 4.0 -> 1.0 substep
+      case 5: result = 4; break; // 5.0 -> 1.0 substep
+      case 6: result = 2; break; // 6.0 -> 2.0 substep
+      case 7: result = 6; break; // 7.0 -> 1.0 substep
+      case 8: result = 3; break; // 8.0 -> 2.0 substep
+      case 9: result = 2; break; // 9.0 -> 3.0 substep
+    }
+  } else
+  {
+    // handle cases with significantly fractional mantissa:
+    if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
+    {
+      switch (intPart)
+      {
+        case 1: result = 2; break; // 1.5 -> 0.5 substep
+        case 2: result = 4; break; // 2.5 -> 0.5 substep
+        case 3: result = 4; break; // 3.5 -> 0.7 substep
+        case 4: result = 2; break; // 4.5 -> 1.5 substep
+        case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with autoTickStep from here on)
+        case 6: result = 4; break; // 6.5 -> 1.3 substep
+        case 7: result = 2; break; // 7.5 -> 2.5 substep
+        case 8: result = 4; break; // 8.5 -> 1.7 substep
+        case 9: result = 4; break; // 9.5 -> 1.9 substep
+      }
+    }
+    // if mantissa fraction isnt 0.0 or 0.5, don't bother finding good sub tick marks, leave default
+  }
+
+  return result;
+}
+
+/* inherits documentation from base class */
+void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
+{
+  Q_UNUSED(event)
+  SelectablePart part = details.value<SelectablePart>();
+  if (mSelectableParts.testFlag(part))
+  {
+    SelectableParts selBefore = mSelectedParts;
+    setSelectedParts(additive ? mSelectedParts^part : part);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelectedParts != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPAxis::deselectEvent(bool *selectionStateChanged)
+{
+  SelectableParts selBefore = mSelectedParts;
+  setSelectedParts(mSelectedParts & ~mSelectableParts);
+  if (selectionStateChanged)
+    *selectionStateChanged = mSelectedParts != selBefore;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing axis lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+
+  \see setAntialiased
+*/
+void QCPAxis::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeAxes);
+}
+
+/*! \internal
+
+  Draws the axis with the specified \a painter, using the internal QCPAxisPainterPrivate instance.
+
+*/
+void QCPAxis::draw(QCPPainter *painter)
+{
+  const int lowTick = mLowestVisibleTick;
+  const int highTick = mHighestVisibleTick;
+  QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
+  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
+  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
+  tickPositions.reserve(highTick-lowTick+1);
+  tickLabels.reserve(highTick-lowTick+1);
+  subTickPositions.reserve(mSubTickVector.size());
+
+  if (mTicks)
+  {
+    for (int i=lowTick; i<=highTick; ++i)
+    {
+      tickPositions.append(coordToPixel(mTickVector.at(i)));
+      if (mTickLabels)
+        tickLabels.append(mTickVectorLabels.at(i));
+    }
+
+    if (mSubTickCount > 0)
+    {
+      const int subTickCount = mSubTickVector.size();
+      for (int i=0; i<subTickCount; ++i) // no need to check bounds because subticks are always only created inside current mRange
+        subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
+    }
+  }
+  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
+  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
+  mAxisPainter->type = mAxisType;
+  mAxisPainter->basePen = getBasePen();
+  mAxisPainter->labelFont = getLabelFont();
+  mAxisPainter->labelColor = getLabelColor();
+  mAxisPainter->label = mLabel;
+  mAxisPainter->substituteExponent = mAutoTickLabels && mNumberBeautifulPowers && mTickLabelType == ltNumber;
+  mAxisPainter->tickPen = getTickPen();
+  mAxisPainter->subTickPen = getSubTickPen();
+  mAxisPainter->tickLabelFont = getTickLabelFont();
+  mAxisPainter->tickLabelColor = getTickLabelColor();
+  mAxisPainter->axisRect = mAxisRect->rect();
+  mAxisPainter->viewportRect = mParentPlot->viewport();
+  mAxisPainter->abbreviateDecimalPowers = mScaleType == stLogarithmic;
+  mAxisPainter->reversedEndings = mRangeReversed;
+  mAxisPainter->tickPositions = tickPositions;
+  mAxisPainter->tickLabels = tickLabels;
+  mAxisPainter->subTickPositions = subTickPositions;
+  mAxisPainter->draw(painter);
+}
+
+/*! \internal
+
+  Returns via \a lowIndex and \a highIndex, which ticks in the current tick vector are visible in
+  the current range. The return values are indices of the tick vector, not the positions of the
+  ticks themselves.
+
+  The actual use of this function is when an external tick vector is provided, since it might
+  exceed far beyond the currently displayed range, and would cause unnecessary calculations e.g. of
+  subticks.
+
+  If all ticks are outside the axis range, an inverted range is returned, i.e. highIndex will be
+  smaller than lowIndex. There is one case, where this function returns indices that are not really
+  visible in the current axis range: When the tick spacing is larger than the axis range size and
+  one tick is below the axis range and the next tick is already above the axis range. Because in
+  such cases it is usually desirable to know the tick pair, to draw proper subticks.
+*/
+void QCPAxis::visibleTickBounds(int &lowIndex, int &highIndex) const
+{
+  bool lowFound = false;
+  bool highFound = false;
+  lowIndex = 0;
+  highIndex = -1;
+
+  for (int i=0; i < mTickVector.size(); ++i)
+  {
+    if (mTickVector.at(i) >= mRange.lower)
+    {
+      lowFound = true;
+      lowIndex = i;
+      break;
+    }
+  }
+  for (int i=mTickVector.size()-1; i >= 0; --i)
+  {
+    if (mTickVector.at(i) <= mRange.upper)
+    {
+      highFound = true;
+      highIndex = i;
+      break;
+    }
+  }
+
+  if (!lowFound && highFound)
+    lowIndex = highIndex+1;
+  else if (lowFound && !highFound)
+    highIndex = lowIndex-1;
+}
+
+/*! \internal
+
+  A log function with the base mScaleLogBase, used mostly for coordinate transforms in logarithmic
+  scales with arbitrary log base. Uses the buffered mScaleLogBaseLogInv for faster calculation.
+  This is set to <tt>1.0/qLn(mScaleLogBase)</tt> in \ref setScaleLogBase.
+
+  \see basePow, setScaleLogBase, setScaleType
+*/
+double QCPAxis::baseLog(double value) const
+{
+  return qLn(value)*mScaleLogBaseLogInv;
+}
+
+/*! \internal
+
+  A power function with the base mScaleLogBase, used mostly for coordinate transforms in
+  logarithmic scales with arbitrary log base.
+
+  \see baseLog, setScaleLogBase, setScaleType
+*/
+double QCPAxis::basePow(double value) const
+{
+  return qPow(mScaleLogBase, value);
+}
+
+/*! \internal
+
+  Returns the pen that is used to draw the axis base line. Depending on the selection state, this
+  is either mSelectedBasePen or mBasePen.
+*/
+QPen QCPAxis::getBasePen() const
+{
+  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
+}
+
+/*! \internal
+
+  Returns the pen that is used to draw the (major) ticks. Depending on the selection state, this
+  is either mSelectedTickPen or mTickPen.
+*/
+QPen QCPAxis::getTickPen() const
+{
+  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
+}
+
+/*! \internal
+
+  Returns the pen that is used to draw the subticks. Depending on the selection state, this
+  is either mSelectedSubTickPen or mSubTickPen.
+*/
+QPen QCPAxis::getSubTickPen() const
+{
+  return mSelectedParts.testFlag(spAxis) ? mSelectedSubTickPen : mSubTickPen;
+}
+
+/*! \internal
+
+  Returns the font that is used to draw the tick labels. Depending on the selection state, this
+  is either mSelectedTickLabelFont or mTickLabelFont.
+*/
+QFont QCPAxis::getTickLabelFont() const
+{
+  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelFont : mTickLabelFont;
+}
+
+/*! \internal
+
+  Returns the font that is used to draw the axis label. Depending on the selection state, this
+  is either mSelectedLabelFont or mLabelFont.
+*/
+QFont QCPAxis::getLabelFont() const
+{
+  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelFont : mLabelFont;
+}
+
+/*! \internal
+
+  Returns the color that is used to draw the tick labels. Depending on the selection state, this
+  is either mSelectedTickLabelColor or mTickLabelColor.
+*/
+QColor QCPAxis::getTickLabelColor() const
+{
+  return mSelectedParts.testFlag(spTickLabels) ? mSelectedTickLabelColor : mTickLabelColor;
+}
+
+/*! \internal
+
+  Returns the color that is used to draw the axis label. Depending on the selection state, this
+  is either mSelectedLabelColor or mLabelColor.
+*/
+QColor QCPAxis::getLabelColor() const
+{
+  return mSelectedParts.testFlag(spAxisLabel) ? mSelectedLabelColor : mLabelColor;
+}
+
+/*! \internal
+
+  Returns the appropriate outward margin for this axis. It is needed if \ref
+  QCPAxisRect::setAutoMargins is set to true on the parent axis rect. An axis with axis type \ref
+  atLeft will return an appropriate left margin, \ref atBottom will return an appropriate bottom
+  margin and so forth. For the calculation, this function goes through similar steps as \ref draw,
+  so changing one function likely requires the modification of the other one as well.
+
+  The margin consists of the outward tick length, tick label padding, tick label size, label
+  padding, label size, and padding.
+
+  The margin is cached internally, so repeated calls while leaving the axis range, fonts, etc.
+  unchanged are very fast.
+*/
+int QCPAxis::calculateMargin()
+{
+  if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
+    return 0;
+
+  if (mCachedMarginValid)
+    return mCachedMargin;
+
+  // run through similar steps as QCPAxis::draw, and caluclate margin needed to fit axis and its labels
+  int margin = 0;
+
+  int lowTick, highTick;
+  visibleTickBounds(lowTick, highTick);
+  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
+  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
+  tickPositions.reserve(highTick-lowTick+1);
+  tickLabels.reserve(highTick-lowTick+1);
+  if (mTicks)
+  {
+    for (int i=lowTick; i<=highTick; ++i)
+    {
+      tickPositions.append(coordToPixel(mTickVector.at(i)));
+      if (mTickLabels)
+        tickLabels.append(mTickVectorLabels.at(i));
+    }
+  }
+  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
+  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
+  mAxisPainter->type = mAxisType;
+  mAxisPainter->labelFont = getLabelFont();
+  mAxisPainter->label = mLabel;
+  mAxisPainter->tickLabelFont = mTickLabelFont;
+  mAxisPainter->axisRect = mAxisRect->rect();
+  mAxisPainter->viewportRect = mParentPlot->viewport();
+  mAxisPainter->tickPositions = tickPositions;
+  mAxisPainter->tickLabels = tickLabels;
+  margin += mAxisPainter->size();
+  margin += mPadding;
+
+  mCachedMargin = margin;
+  mCachedMarginValid = true;
+  return margin;
+}
+
+/* inherits documentation from base class */
+QCP::Interaction QCPAxis::selectionCategory() const
+{
+  return QCP::iSelectAxes;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPAxisPainterPrivate
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPAxisPainterPrivate
+
+  \internal
+  \brief (Private)
+
+  This is a private class and not part of the public QCustomPlot interface.
+
+  It is used by QCPAxis to do the low-level drawing of axis backbone, tick marks, tick labels and
+  axis label. It also buffers the labels to reduce replot times. The parameters are configured by
+  directly accessing the public member variables.
+*/
+
+/*!
+  Constructs a QCPAxisPainterPrivate instance. Make sure to not create a new instance on every
+  redraw, to utilize the caching mechanisms.
+*/
+QCPAxisPainterPrivate::QCPAxisPainterPrivate(QCustomPlot *parentPlot) :
+  type(QCPAxis::atLeft),
+  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  lowerEnding(QCPLineEnding::esNone),
+  upperEnding(QCPLineEnding::esNone),
+  labelPadding(0),
+  tickLabelPadding(0),
+  tickLabelRotation(0),
+  tickLabelSide(QCPAxis::lsOutside),
+  substituteExponent(true),
+  numberMultiplyCross(false),
+  tickLengthIn(5),
+  tickLengthOut(0),
+  subTickLengthIn(2),
+  subTickLengthOut(0),
+  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
+  offset(0),
+  abbreviateDecimalPowers(false),
+  reversedEndings(false),
+  mParentPlot(parentPlot),
+  mLabelCache(16) // cache at most 16 (tick) labels
+{
+}
+
+QCPAxisPainterPrivate::~QCPAxisPainterPrivate()
+{
+}
+
+/*! \internal
+
+  Draws the axis with the specified \a painter.
+
+  The selection boxes (mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox) are set
+  here, too.
+*/
+void QCPAxisPainterPrivate::draw(QCPPainter *painter)
+{
+  QByteArray newHash = generateLabelParameterHash();
+  if (newHash != mLabelParameterHash)
+  {
+    mLabelCache.clear();
+    mLabelParameterHash = newHash;
+  }
+
+  QPoint origin;
+  switch (type)
+  {
+    case QCPAxis::atLeft:   origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
+    case QCPAxis::atRight:  origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
+    case QCPAxis::atTop:    origin = axisRect.topLeft()    +QPoint(0, -offset); break;
+    case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
+  }
+
+  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
+  switch (type)
+  {
+    case QCPAxis::atTop: yCor = -1; break;
+    case QCPAxis::atRight: xCor = 1; break;
+    default: break;
+  }
+  int margin = 0;
+  // draw baseline:
+  QLineF baseLine;
+  painter->setPen(basePen);
+  if (QCPAxis::orientation(type) == Qt::Horizontal)
+    baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
+  else
+    baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
+  if (reversedEndings)
+    baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
+  painter->drawLine(baseLine);
+
+  // draw ticks:
+  if (!tickPositions.isEmpty())
+  {
+    painter->setPen(tickPen);
+    int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
+    if (QCPAxis::orientation(type) == Qt::Horizontal)
+    {
+      for (int i=0; i<tickPositions.size(); ++i)
+        painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
+    } else
+    {
+      for (int i=0; i<tickPositions.size(); ++i)
+        painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
+    }
+  }
+
+  // draw subticks:
+  if (!subTickPositions.isEmpty())
+  {
+    painter->setPen(subTickPen);
+    // direction of ticks ("inward" is right for left axis and left for right axis)
+    int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
+    if (QCPAxis::orientation(type) == Qt::Horizontal)
+    {
+      for (int i=0; i<subTickPositions.size(); ++i)
+        painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
+    } else
+    {
+      for (int i=0; i<subTickPositions.size(); ++i)
+        painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
+    }
+  }
+  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
+
+  // draw axis base endings:
+  bool antialiasingBackup = painter->antialiasing();
+  painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
+  painter->setBrush(QBrush(basePen.color()));
+  QVector2D baseLineVector(baseLine.dx(), baseLine.dy());
+  if (lowerEnding.style() != QCPLineEnding::esNone)
+    lowerEnding.draw(painter, QVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
+  if (upperEnding.style() != QCPLineEnding::esNone)
+    upperEnding.draw(painter, QVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
+  painter->setAntialiasing(antialiasingBackup);
+
+  // tick labels:
+  QRect oldClipRect;
+  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
+  {
+    oldClipRect = painter->clipRegion().boundingRect();
+    painter->setClipRect(axisRect);
+  }
+  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
+  if (!tickLabels.isEmpty())
+  {
+    if (tickLabelSide == QCPAxis::lsOutside)
+      margin += tickLabelPadding;
+    painter->setFont(tickLabelFont);
+    painter->setPen(QPen(tickLabelColor));
+    const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
+    int distanceToAxis = margin;
+    if (tickLabelSide == QCPAxis::lsInside)
+      distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
+    for (int i=0; i<maxLabelIndex; ++i)
+      placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
+    if (tickLabelSide == QCPAxis::lsOutside)
+      margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
+  }
+  if (tickLabelSide == QCPAxis::lsInside)
+    painter->setClipRect(oldClipRect);
+
+  // axis label:
+  QRect labelBounds;
+  if (!label.isEmpty())
+  {
+    margin += labelPadding;
+    painter->setFont(labelFont);
+    painter->setPen(QPen(labelColor));
+    labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
+    if (type == QCPAxis::atLeft)
+    {
+      QTransform oldTransform = painter->transform();
+      painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
+      painter->rotate(-90);
+      painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
+      painter->setTransform(oldTransform);
+    }
+    else if (type == QCPAxis::atRight)
+    {
+      QTransform oldTransform = painter->transform();
+      painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
+      painter->rotate(90);
+      painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
+      painter->setTransform(oldTransform);
+    }
+    else if (type == QCPAxis::atTop)
+      painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
+    else if (type == QCPAxis::atBottom)
+      painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
+  }
+
+  // set selection boxes:
+  int selectionTolerance = 0;
+  if (mParentPlot)
+    selectionTolerance = mParentPlot->selectionTolerance();
+  else
+    qDebug() << Q_FUNC_INFO << "mParentPlot is null";
+  int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
+  int selAxisInSize = selectionTolerance;
+  int selTickLabelSize;
+  int selTickLabelOffset;
+  if (tickLabelSide == QCPAxis::lsOutside)
+  {
+    selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
+    selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
+  } else
+  {
+    selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
+    selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
+  }
+  int selLabelSize = labelBounds.height();
+  int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
+  if (type == QCPAxis::atLeft)
+  {
+    mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
+    mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
+    mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
+  } else if (type == QCPAxis::atRight)
+  {
+    mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
+    mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
+    mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
+  } else if (type == QCPAxis::atTop)
+  {
+    mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
+    mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
+    mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
+  } else if (type == QCPAxis::atBottom)
+  {
+    mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
+    mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
+    mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
+  }
+  mAxisSelectionBox = mAxisSelectionBox.normalized();
+  mTickLabelsSelectionBox = mTickLabelsSelectionBox.normalized();
+  mLabelSelectionBox = mLabelSelectionBox.normalized();
+  // draw hitboxes for debug purposes:
+  //painter->setBrush(Qt::NoBrush);
+  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
+}
+
+/*! \internal
+
+  Returns the size ("margin" in QCPAxisRect context, so measured perpendicular to the axis backbone
+  direction) needed to fit the axis.
+*/
+int QCPAxisPainterPrivate::size() const
+{
+  int result = 0;
+
+  // get length of tick marks pointing outwards:
+  if (!tickPositions.isEmpty())
+    result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
+
+  // calculate size of tick labels:
+  if (tickLabelSide == QCPAxis::lsOutside)
+  {
+    QSize tickLabelsSize(0, 0);
+    if (!tickLabels.isEmpty())
+    {
+      for (int i=0; i<tickLabels.size(); ++i)
+        getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
+      result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
+    result += tickLabelPadding;
+    }
+  }
+
+  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
+  if (!label.isEmpty())
+  {
+    QFontMetrics fontMetrics(labelFont);
+    QRect bounds;
+    bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
+    result += bounds.height() + labelPadding;
+  }
+
+  return result;
+}
+
+/*! \internal
+
+  Clears the internal label cache. Upon the next \ref draw, all labels will be created new. This
+  method is called automatically in \ref draw, if any parameters have changed that invalidate the
+  cached labels, such as font, color, etc.
+*/
+void QCPAxisPainterPrivate::clearCache()
+{
+  mLabelCache.clear();
+}
+
+/*! \internal
+
+  Returns a hash that allows uniquely identifying whether the label parameters have changed such
+  that the cached labels must be refreshed (\ref clearCache). It is used in \ref draw. If the
+  return value of this method hasn't changed since the last redraw, the respective label parameters
+  haven't changed and cached labels may be used.
+*/
+QByteArray QCPAxisPainterPrivate::generateLabelParameterHash() const
+{
+  QByteArray result;
+  result.append(QByteArray::number(tickLabelRotation));
+  result.append(QByteArray::number((int)tickLabelSide));
+  result.append(QByteArray::number((int)substituteExponent));
+  result.append(QByteArray::number((int)numberMultiplyCross));
+  result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
+  result.append(tickLabelFont.toString().toLatin1());
+  return result;
+}
+
+/*! \internal
+
+  Draws a single tick label with the provided \a painter, utilizing the internal label cache to
+  significantly speed up drawing of labels that were drawn in previous calls. The tick label is
+  always bound to an axis, the distance to the axis is controllable via \a distanceToAxis in
+  pixels. The pixel position in the axis direction is passed in the \a position parameter. Hence
+  for the bottom axis, \a position would indicate the horizontal pixel position (not coordinate),
+  at which the label should be drawn.
+
+  In order to later draw the axis label in a place that doesn't overlap with the tick labels, the
+  largest tick label size is needed. This is acquired by passing a \a tickLabelsSize to the \ref
+  drawTickLabel calls during the process of drawing all tick labels of one axis. In every call, \a
+  tickLabelsSize is expanded, if the drawn label exceeds the value \a tickLabelsSize currently
+  holds.
+
+  The label is drawn with the font and pen that are currently set on the \a painter. To draw
+  superscripted powers, the font is temporarily made smaller by a fixed factor (see \ref
+  getTickLabelData).
+*/
+void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
+{
+  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
+  if (text.isEmpty()) return;
+  QSize finalSize;
+  QPointF labelAnchor;
+  switch (type)
+  {
+    case QCPAxis::atLeft:   labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
+    case QCPAxis::atRight:  labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
+    case QCPAxis::atTop:    labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
+    case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
+  }
+  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
+  {
+    CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache
+    if (!cachedLabel)  // no cached label existed, create it
+    {
+      cachedLabel = new CachedLabel;
+      TickLabelData labelData = getTickLabelData(painter->font(), text);
+      cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
+      cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
+      cachedLabel->pixmap.fill(Qt::transparent);
+      QCPPainter cachePainter(&cachedLabel->pixmap);
+      cachePainter.setPen(painter->pen());
+      drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
+    }
+    // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
+    bool labelClippedByBorder = false;
+    if (tickLabelSide == QCPAxis::lsOutside)
+    {
+      if (QCPAxis::orientation(type) == Qt::Horizontal)
+        labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
+      else
+        labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
+    }
+    if (!labelClippedByBorder)
+    {
+      painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
+      finalSize = cachedLabel->pixmap.size();
+    }
+    mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created
+  } else // label caching disabled, draw text directly on surface:
+  {
+    TickLabelData labelData = getTickLabelData(painter->font(), text);
+    QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
+    // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
+     bool labelClippedByBorder = false;
+    if (tickLabelSide == QCPAxis::lsOutside)
+    {
+      if (QCPAxis::orientation(type) == Qt::Horizontal)
+        labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
+      else
+        labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
+    }
+    if (!labelClippedByBorder)
+    {
+      drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
+      finalSize = labelData.rotatedTotalBounds.size();
+    }
+  }
+
+  // expand passed tickLabelsSize if current tick label is larger:
+  if (finalSize.width() > tickLabelsSize->width())
+    tickLabelsSize->setWidth(finalSize.width());
+  if (finalSize.height() > tickLabelsSize->height())
+    tickLabelsSize->setHeight(finalSize.height());
+}
+
+/*! \internal
+
+  This is a \ref placeTickLabel helper function.
+
+  Draws the tick label specified in \a labelData with \a painter at the pixel positions \a x and \a
+  y. This function is used by \ref placeTickLabel to create new tick labels for the cache, or to
+  directly draw the labels on the QCustomPlot surface when label caching is disabled, i.e. when
+  QCP::phCacheLabels plotting hint is not set.
+*/
+void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
+{
+  // backup painter settings that we're about to change:
+  QTransform oldTransform = painter->transform();
+  QFont oldFont = painter->font();
+
+  // transform painter to position/rotation:
+  painter->translate(x, y);
+  if (!qFuzzyIsNull(tickLabelRotation))
+    painter->rotate(tickLabelRotation);
+
+  // draw text:
+  if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
+  {
+    painter->setFont(labelData.baseFont);
+    painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
+    painter->setFont(labelData.expFont);
+    painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip,  labelData.expPart);
+  } else
+  {
+    painter->setFont(labelData.baseFont);
+    painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
+  }
+
+  // reset painter settings to what it was before:
+  painter->setTransform(oldTransform);
+  painter->setFont(oldFont);
+}
+
+/*! \internal
+
+  This is a \ref placeTickLabel helper function.
+
+  Transforms the passed \a text and \a font to a tickLabelData structure that can then be further
+  processed by \ref getTickLabelDrawOffset and \ref drawTickLabel. It splits the text into base and
+  exponent if necessary (member substituteExponent) and calculates appropriate bounding boxes.
+*/
+QCPAxisPainterPrivate::TickLabelData QCPAxisPainterPrivate::getTickLabelData(const QFont &font, const QString &text) const
+{
+  TickLabelData result;
+
+  // determine whether beautiful decimal powers should be used
+  bool useBeautifulPowers = false;
+  int ePos = -1;
+  if (substituteExponent)
+  {
+    ePos = text.indexOf(QLatin1Char('e'));
+    if (ePos > -1)
+      useBeautifulPowers = true;
+  }
+
+  // calculate text bounding rects and do string preparation for beautiful decimal powers:
+  result.baseFont = font;
+  if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
+    result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
+  if (useBeautifulPowers)
+  {
+    // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
+    result.basePart = text.left(ePos);
+    // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
+    if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
+      result.basePart = QLatin1String("10");
+    else
+      result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
+    result.expPart = text.mid(ePos+1);
+    // clip "+" and leading zeros off expPart:
+    while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
+      result.expPart.remove(1, 1);
+    if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
+      result.expPart.remove(0, 1);
+    // prepare smaller font for exponent:
+    result.expFont = font;
+    if (result.expFont.pointSize() > 0)
+      result.expFont.setPointSize(result.expFont.pointSize()*0.75);
+    else
+      result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
+    // calculate bounding rects of base part, exponent part and total one:
+    result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
+    result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
+    result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA
+  } else // useBeautifulPowers == false
+  {
+    result.basePart = text;
+    result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
+  }
+  result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler
+
+  // calculate possibly different bounding rect after rotation:
+  result.rotatedTotalBounds = result.totalBounds;
+  if (!qFuzzyIsNull(tickLabelRotation))
+  {
+    QTransform transform;
+    transform.rotate(tickLabelRotation);
+    result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
+  }
+
+  return result;
+}
+
+/*! \internal
+
+  This is a \ref placeTickLabel helper function.
+
+  Calculates the offset at which the top left corner of the specified tick label shall be drawn.
+  The offset is relative to a point right next to the tick the label belongs to.
+
+  This function is thus responsible for e.g. centering tick labels under ticks and positioning them
+  appropriately when they are rotated.
+*/
+QPointF QCPAxisPainterPrivate::getTickLabelDrawOffset(const TickLabelData &labelData) const
+{
+  /*
+    calculate label offset from base point at tick (non-trivial, for best visual appearance): short
+    explanation for bottom axis: The anchor, i.e. the point in the label that is placed
+    horizontally under the corresponding tick is always on the label side that is closer to the
+    axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
+    is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
+    will be centered under the tick (i.e. displaced horizontally by half its height). At the same
+    time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
+    labels.
+  */
+  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
+  bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
+  double radians = tickLabelRotation/180.0*M_PI;
+  int x=0, y=0;
+  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
+  {
+    if (doRotation)
+    {
+      if (tickLabelRotation > 0)
+      {
+        x = -qCos(radians)*labelData.totalBounds.width();
+        y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
+      } else
+      {
+        x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
+        y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
+      }
+    } else
+    {
+      x = -labelData.totalBounds.width();
+      y = -labelData.totalBounds.height()/2.0;
+    }
+  } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
+  {
+    if (doRotation)
+    {
+      if (tickLabelRotation > 0)
+      {
+        x = +qSin(radians)*labelData.totalBounds.height();
+        y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
+      } else
+      {
+        x = 0;
+        y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
+      }
+    } else
+    {
+      x = 0;
+      y = -labelData.totalBounds.height()/2.0;
+    }
+  } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
+  {
+    if (doRotation)
+    {
+      if (tickLabelRotation > 0)
+      {
+        x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
+        y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
+      } else
+      {
+        x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
+        y = -qCos(-radians)*labelData.totalBounds.height();
+      }
+    } else
+    {
+      x = -labelData.totalBounds.width()/2.0;
+      y = -labelData.totalBounds.height();
+    }
+  } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
+  {
+    if (doRotation)
+    {
+      if (tickLabelRotation > 0)
+      {
+        x = +qSin(radians)*labelData.totalBounds.height()/2.0;
+        y = 0;
+      } else
+      {
+        x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
+        y = +qSin(-radians)*labelData.totalBounds.width();
+      }
+    } else
+    {
+      x = -labelData.totalBounds.width()/2.0;
+      y = 0;
+    }
+  }
+
+  return QPointF(x, y);
+}
+
+/*! \internal
+
+  Simulates the steps done by \ref placeTickLabel by calculating bounding boxes of the text label
+  to be drawn, depending on number format etc. Since only the largest tick label is wanted for the
+  margin calculation, the passed \a tickLabelsSize is only expanded, if it's currently set to a
+  smaller width/height.
+*/
+void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text,  QSize *tickLabelsSize) const
+{
+  // note: this function must return the same tick label sizes as the placeTickLabel function.
+  QSize finalSize;
+  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
+  {
+    const CachedLabel *cachedLabel = mLabelCache.object(text);
+    finalSize = cachedLabel->pixmap.size();
+  } else // label caching disabled or no label with this text cached:
+  {
+    TickLabelData labelData = getTickLabelData(font, text);
+    finalSize = labelData.rotatedTotalBounds.size();
+  }
+
+  // expand passed tickLabelsSize if current tick label is larger:
+  if (finalSize.width() > tickLabelsSize->width())
+    tickLabelsSize->setWidth(finalSize.width());
+  if (finalSize.height() > tickLabelsSize->height())
+    tickLabelsSize->setHeight(finalSize.height());
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPAbstractPlottable
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPAbstractPlottable
+  \brief The abstract base class for all data representing objects in a plot.
+
+  It defines a very basic interface like name, pen, brush, visibility etc. Since this class is
+  abstract, it can't be instantiated. Use one of the subclasses or create a subclass yourself to
+  create new ways of displaying data (see "Creating own plottables" below).
+
+  All further specifics are in the subclasses, for example:
+  \li A normal graph with possibly a line, scatter points and error bars: \ref QCPGraph
+  (typically created with \ref QCustomPlot::addGraph)
+  \li A parametric curve: \ref QCPCurve
+  \li A bar chart: \ref QCPBars
+  \li A statistical box plot: \ref QCPStatisticalBox
+  \li A color encoded two-dimensional map: \ref QCPColorMap
+  \li An OHLC/Candlestick chart: \ref QCPFinancial
+
+  \section plottables-subclassing Creating own plottables
+
+  To create an own plottable, you implement a subclass of QCPAbstractPlottable. These are the pure
+  virtual functions, you must implement:
+  \li \ref clearData
+  \li \ref selectTest
+  \li \ref draw
+  \li \ref drawLegendIcon
+  \li \ref getKeyRange
+  \li \ref getValueRange
+
+  See the documentation of those functions for what they need to do.
+
+  For drawing your plot, you can use the \ref coordsToPixels functions to translate a point in plot
+  coordinates to pixel coordinates. This function is quite convenient, because it takes the
+  orientation of the key and value axes into account for you (x and y are swapped when the key axis
+  is vertical and the value axis horizontal). If you are worried about performance (i.e. you need
+  to translate many points in a loop like QCPGraph), you can directly use \ref
+  QCPAxis::coordToPixel. However, you must then take care about the orientation of the axis
+  yourself.
+
+  Here are some important members you inherit from QCPAbstractPlottable:
+  <table>
+  <tr>
+    <td>QCustomPlot *\b mParentPlot</td>
+    <td>A pointer to the parent QCustomPlot instance. The parent plot is inferred from the axes that are passed in the constructor.</td>
+  </tr><tr>
+    <td>QString \b mName</td>
+    <td>The name of the plottable.</td>
+  </tr><tr>
+    <td>QPen \b mPen</td>
+    <td>The generic pen of the plottable. You should use this pen for the most prominent data representing lines in the plottable (e.g QCPGraph uses this pen for its graph lines and scatters)</td>
+  </tr><tr>
+    <td>QPen \b mSelectedPen</td>
+    <td>The generic pen that should be used when the plottable is selected (hint: \ref mainPen gives you the right pen, depending on selection state).</td>
+  </tr><tr>
+    <td>QBrush \b mBrush</td>
+    <td>The generic brush of the plottable. You should use this brush for the most prominent fillable structures in the plottable (e.g. QCPGraph uses this brush to control filling under the graph)</td>
+  </tr><tr>
+    <td>QBrush \b mSelectedBrush</td>
+    <td>The generic brush that should be used when the plottable is selected (hint: \ref mainBrush gives you the right brush, depending on selection state).</td>
+  </tr><tr>
+    <td>QPointer<QCPAxis>\b mKeyAxis, \b mValueAxis</td>
+    <td>The key and value axes this plottable is attached to. Call their QCPAxis::coordToPixel functions to translate coordinates to pixels in either the key or value dimension.
+        Make sure to check whether the pointer is null before using it. If one of the axes is null, don't draw the plottable.</td>
+  </tr><tr>
+    <td>bool \b mSelected</td>
+    <td>indicates whether the plottable is selected or not.</td>
+  </tr>
+  </table>
+*/
+
+/* start of documentation of pure virtual functions */
+
+/*! \fn void QCPAbstractPlottable::clearData() = 0
+  Clears all data in the plottable.
+*/
+
+/*! \fn void QCPAbstractPlottable::drawLegendIcon(QCPPainter *painter, const QRect &rect) const = 0
+  \internal
+
+  called by QCPLegend::draw (via QCPPlottableLegendItem::draw) to create a graphical representation
+  of this plottable inside \a rect, next to the plottable name.
+
+  The passed \a painter has its cliprect set to \a rect, so painting outside of \a rect won't
+  appear outside the legend icon border.
+*/
+
+/*! \fn QCPRange QCPAbstractPlottable::getKeyRange(bool &foundRange, SignDomain inSignDomain) const = 0
+  \internal
+
+  called by rescaleAxes functions to get the full data key bounds. For logarithmic plots, one can
+  set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
+  returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
+  to \ref sdNegative and all positive points will be ignored for range calculation. For no
+  restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
+  parameter that indicates whether a range could be found or not. If this is false, you shouldn't
+  use the returned range (e.g. no points in data).
+
+  Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
+  this function may have size zero, which wouldn't count as a valid range.
+
+  \see rescaleAxes, getValueRange
+*/
+
+/*! \fn QCPRange QCPAbstractPlottable::getValueRange(bool &foundRange, SignDomain inSignDomain) const = 0
+  \internal
+
+  called by rescaleAxes functions to get the full data value bounds. For logarithmic plots, one can
+  set \a inSignDomain to either \ref sdNegative or \ref sdPositive in order to restrict the
+  returned range to that sign domain. E.g. when only negative range is wanted, set \a inSignDomain
+  to \ref sdNegative and all positive points will be ignored for range calculation. For no
+  restriction, just set \a inSignDomain to \ref sdBoth (default). \a foundRange is an output
+  parameter that indicates whether a range could be found or not. If this is false, you shouldn't
+  use the returned range (e.g. no points in data).
+
+  Note that \a foundRange is not the same as \ref QCPRange::validRange, since the range returned by
+  this function may have size zero, which wouldn't count as a valid range.
+
+  \see rescaleAxes, getKeyRange
+*/
+
+/* end of documentation of pure virtual functions */
+/* start of documentation of signals */
+
+/*! \fn void QCPAbstractPlottable::selectionChanged(bool selected)
+
+  This signal is emitted when the selection state of this plottable has changed, either by user
+  interaction or by a direct call to \ref setSelected.
+*/
+
+/*! \fn void QCPAbstractPlottable::selectableChanged(bool selectable);
+
+  This signal is emitted when the selectability of this plottable has changed.
+
+  \see setSelectable
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs an abstract plottable which uses \a keyAxis as its key axis ("x") and \a valueAxis as
+  its value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance
+  and have perpendicular orientations. If either of these restrictions is violated, a corresponding
+  message is printed to the debug output (qDebug), the construction is not aborted, though.
+
+  Since QCPAbstractPlottable is an abstract class that defines the basic interface to plottables,
+  it can't be directly instantiated.
+
+  You probably want one of the subclasses like \ref QCPGraph or \ref QCPCurve instead.
+*/
+QCPAbstractPlottable::QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
+  mName(),
+  mAntialiasedFill(true),
+  mAntialiasedScatters(true),
+  mAntialiasedErrorBars(false),
+  mPen(Qt::black),
+  mSelectedPen(Qt::black),
+  mBrush(Qt::NoBrush),
+  mSelectedBrush(Qt::NoBrush),
+  mKeyAxis(keyAxis),
+  mValueAxis(valueAxis),
+  mSelectable(true),
+  mSelected(false)
+{
+  if (keyAxis->parentPlot() != valueAxis->parentPlot())
+    qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
+  if (keyAxis->orientation() == valueAxis->orientation())
+    qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
+}
+
+/*!
+   The name is the textual representation of this plottable as it is displayed in the legend
+   (\ref QCPLegend). It may contain any UTF-8 characters, including newlines.
+*/
+void QCPAbstractPlottable::setName(const QString &name)
+{
+  mName = name;
+}
+
+/*!
+  Sets whether fills of this plottable are drawn antialiased or not.
+
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAbstractPlottable::setAntialiasedFill(bool enabled)
+{
+  mAntialiasedFill = enabled;
+}
+
+/*!
+  Sets whether the scatter symbols of this plottable are drawn antialiased or not.
+
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAbstractPlottable::setAntialiasedScatters(bool enabled)
+{
+  mAntialiasedScatters = enabled;
+}
+
+/*!
+  Sets whether the error bars of this plottable are drawn antialiased or not.
+
+  Note that this setting may be overridden by \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+*/
+void QCPAbstractPlottable::setAntialiasedErrorBars(bool enabled)
+{
+  mAntialiasedErrorBars = enabled;
+}
+
+
+/*!
+  The pen is used to draw basic lines that make up the plottable representation in the
+  plot.
+
+  For example, the \ref QCPGraph subclass draws its graph lines with this pen.
+
+  \see setBrush
+*/
+void QCPAbstractPlottable::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  When the plottable is selected, this pen is used to draw basic lines instead of the normal
+  pen set via \ref setPen.
+
+  \see setSelected, setSelectable, setSelectedBrush, selectTest
+*/
+void QCPAbstractPlottable::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  The brush is used to draw basic fills of the plottable representation in the
+  plot. The Fill can be a color, gradient or texture, see the usage of QBrush.
+
+  For example, the \ref QCPGraph subclass draws the fill under the graph with this brush, when
+  it's not set to Qt::NoBrush.
+
+  \see setPen
+*/
+void QCPAbstractPlottable::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  When the plottable is selected, this brush is used to draw fills instead of the normal
+  brush set via \ref setBrush.
+
+  \see setSelected, setSelectable, setSelectedPen, selectTest
+*/
+void QCPAbstractPlottable::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/*!
+  The key axis of a plottable can be set to any axis of a QCustomPlot, as long as it is orthogonal
+  to the plottable's value axis. This function performs no checks to make sure this is the case.
+  The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and the
+  y-axis (QCustomPlot::yAxis) as value axis.
+
+  Normally, the key and value axes are set in the constructor of the plottable (or \ref
+  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
+
+  \see setValueAxis
+*/
+void QCPAbstractPlottable::setKeyAxis(QCPAxis *axis)
+{
+  mKeyAxis = axis;
+}
+
+/*!
+  The value axis of a plottable can be set to any axis of a QCustomPlot, as long as it is
+  orthogonal to the plottable's key axis. This function performs no checks to make sure this is the
+  case. The typical mathematical choice is to use the x-axis (QCustomPlot::xAxis) as key axis and
+  the y-axis (QCustomPlot::yAxis) as value axis.
+
+  Normally, the key and value axes are set in the constructor of the plottable (or \ref
+  QCustomPlot::addGraph when working with QCPGraphs through the dedicated graph interface).
+
+  \see setKeyAxis
+*/
+void QCPAbstractPlottable::setValueAxis(QCPAxis *axis)
+{
+  mValueAxis = axis;
+}
+
+/*!
+  Sets whether the user can (de-)select this plottable by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains iSelectPlottables.)
+
+  However, even when \a selectable was set to false, it is possible to set the selection manually,
+  by calling \ref setSelected directly.
+
+  \see setSelected
+*/
+void QCPAbstractPlottable::setSelectable(bool selectable)
+{
+  if (mSelectable != selectable)
+  {
+    mSelectable = selectable;
+    emit selectableChanged(mSelectable);
+  }
+}
+
+/*!
+  Sets whether this plottable is selected or not. When selected, it uses a different pen and brush
+  to draw its lines and fills, see \ref setSelectedPen and \ref setSelectedBrush.
+
+  The entire selection mechanism for plottables is handled automatically when \ref
+  QCustomPlot::setInteractions contains iSelectPlottables. You only need to call this function when
+  you wish to change the selection state manually.
+
+  This function can change the selection state even when \ref setSelectable was set to false.
+
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+
+  \see setSelectable, selectTest
+*/
+void QCPAbstractPlottable::setSelected(bool selected)
+{
+  if (mSelected != selected)
+  {
+    mSelected = selected;
+    emit selectionChanged(mSelected);
+  }
+}
+
+/*!
+  Rescales the key and value axes associated with this plottable to contain all displayed data, so
+  the whole plottable is visible. If the scaling of an axis is logarithmic, rescaleAxes will make
+  sure not to rescale to an illegal range i.e. a range containing different signs and/or zero.
+  Instead it will stay in the current sign domain and ignore all parts of the plottable that lie
+  outside of that domain.
+
+  \a onlyEnlarge makes sure the ranges are only expanded, never reduced. So it's possible to show
+  multiple plottables in their entirety by multiple calls to rescaleAxes where the first call has
+  \a onlyEnlarge set to false (the default), and all subsequent set to true.
+
+  \see rescaleKeyAxis, rescaleValueAxis, QCustomPlot::rescaleAxes, QCPAxis::rescale
+*/
+void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
+{
+  rescaleKeyAxis(onlyEnlarge);
+  rescaleValueAxis(onlyEnlarge);
+}
+
+/*!
+  Rescales the key axis of the plottable so the whole plottable is visible.
+
+  See \ref rescaleAxes for detailed behaviour.
+*/
+void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
+
+  SignDomain signDomain = sdBoth;
+  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
+    signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
+
+  bool foundRange;
+  QCPRange newRange = getKeyRange(foundRange, signDomain);
+  if (foundRange)
+  {
+    if (onlyEnlarge)
+      newRange.expand(keyAxis->range());
+    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
+    {
+      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
+      if (keyAxis->scaleType() == QCPAxis::stLinear)
+      {
+        newRange.lower = center-keyAxis->range().size()/2.0;
+        newRange.upper = center+keyAxis->range().size()/2.0;
+      } else // scaleType() == stLogarithmic
+      {
+        newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
+        newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
+      }
+    }
+    keyAxis->setRange(newRange);
+  }
+}
+
+/*!
+  Rescales the value axis of the plottable so the whole plottable is visible.
+
+  Returns true if the axis was actually scaled. This might not be the case if this plottable has an
+  invalid range, e.g. because it has no data points.
+
+  See \ref rescaleAxes for detailed behaviour.
+*/
+void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge) const
+{
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
+
+  SignDomain signDomain = sdBoth;
+  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
+    signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
+
+  bool foundRange;
+  QCPRange newRange = getValueRange(foundRange, signDomain);
+  if (foundRange)
+  {
+    if (onlyEnlarge)
+      newRange.expand(valueAxis->range());
+    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
+    {
+      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
+      if (valueAxis->scaleType() == QCPAxis::stLinear)
+      {
+        newRange.lower = center-valueAxis->range().size()/2.0;
+        newRange.upper = center+valueAxis->range().size()/2.0;
+      } else // scaleType() == stLogarithmic
+      {
+        newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
+        newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
+      }
+    }
+    valueAxis->setRange(newRange);
+  }
+}
+
+/*!
+  Adds this plottable to the legend of the parent QCustomPlot (QCustomPlot::legend).
+
+  Normally, a QCPPlottableLegendItem is created and inserted into the legend. If the plottable
+  needs a more specialized representation in the legend, this function will take this into account
+  and instead create the specialized subclass of QCPAbstractLegendItem.
+
+  Returns true on success, i.e. when the legend exists and a legend item associated with this plottable isn't already in
+  the legend.
+
+  \see removeFromLegend, QCPLegend::addItem
+*/
+bool QCPAbstractPlottable::addToLegend()
+{
+  if (!mParentPlot || !mParentPlot->legend)
+    return false;
+
+  if (!mParentPlot->legend->hasItemWithPlottable(this))
+  {
+    mParentPlot->legend->addItem(new QCPPlottableLegendItem(mParentPlot->legend, this));
+    return true;
+  } else
+    return false;
+}
+
+/*!
+  Removes the plottable from the legend of the parent QCustomPlot. This means the
+  QCPAbstractLegendItem (usually a QCPPlottableLegendItem) that is associated with this plottable
+  is removed.
+
+  Returns true on success, i.e. if the legend exists and a legend item associated with this
+  plottable was found and removed.
+
+  \see addToLegend, QCPLegend::removeItem
+*/
+bool QCPAbstractPlottable::removeFromLegend() const
+{
+  if (!mParentPlot->legend)
+    return false;
+
+  if (QCPPlottableLegendItem *lip = mParentPlot->legend->itemWithPlottable(this))
+    return mParentPlot->legend->removeItem(lip);
+  else
+    return false;
+}
+
+/* inherits documentation from base class */
+QRect QCPAbstractPlottable::clipRect() const
+{
+  if (mKeyAxis && mValueAxis)
+    return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
+  else
+    return QRect();
+}
+
+/* inherits documentation from base class */
+QCP::Interaction QCPAbstractPlottable::selectionCategory() const
+{
+  return QCP::iSelectPlottables;
+}
+
+/*! \internal
+
+  Convenience function for transforming a key/value pair to pixels on the QCustomPlot surface,
+  taking the orientations of the axes associated with this plottable into account (e.g. whether key
+  represents x or y).
+
+  \a key and \a value are transformed to the coodinates in pixels and are written to \a x and \a y.
+
+  \see pixelsToCoords, QCPAxis::coordToPixel
+*/
+void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+
+  if (keyAxis->orientation() == Qt::Horizontal)
+  {
+    x = keyAxis->coordToPixel(key);
+    y = valueAxis->coordToPixel(value);
+  } else
+  {
+    y = keyAxis->coordToPixel(key);
+    x = valueAxis->coordToPixel(value);
+  }
+}
+
+/*! \internal
+  \overload
+
+  Returns the input as pixel coordinates in a QPointF.
+*/
+const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
+
+  if (keyAxis->orientation() == Qt::Horizontal)
+    return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
+  else
+    return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
+}
+
+/*! \internal
+
+  Convenience function for transforming a x/y pixel pair on the QCustomPlot surface to plot coordinates,
+  taking the orientations of the axes associated with this plottable into account (e.g. whether key
+  represents x or y).
+
+  \a x and \a y are transformed to the plot coodinates and are written to \a key and \a value.
+
+  \see coordsToPixels, QCPAxis::coordToPixel
+*/
+void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+
+  if (keyAxis->orientation() == Qt::Horizontal)
+  {
+    key = keyAxis->pixelToCoord(x);
+    value = valueAxis->pixelToCoord(y);
+  } else
+  {
+    key = keyAxis->pixelToCoord(y);
+    value = valueAxis->pixelToCoord(x);
+  }
+}
+
+/*! \internal
+  \overload
+
+  Returns the pixel input \a pixelPos as plot coordinates \a key and \a value.
+*/
+void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
+{
+  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines of the plottable. Returns mPen when the
+  graph is not selected and mSelectedPen when it is.
+*/
+QPen QCPAbstractPlottable::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the plottable. Returns mBrush when the
+  graph is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPAbstractPlottable::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing plottable lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+
+  \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
+*/
+void QCPAbstractPlottable::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aePlottables);
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing plottable fills.
+
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+
+  \see setAntialiased, applyDefaultAntialiasingHint, applyScattersAntialiasingHint, applyErrorBarsAntialiasingHint
+*/
+void QCPAbstractPlottable::applyFillAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiasedFill, QCP::aeFills);
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing plottable scatter points.
+
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+
+  \see setAntialiased, applyFillAntialiasingHint, applyDefaultAntialiasingHint, applyErrorBarsAntialiasingHint
+*/
+void QCPAbstractPlottable::applyScattersAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiasedScatters, QCP::aeScatters);
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing plottable error bars.
+
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+
+  \see setAntialiased, applyFillAntialiasingHint, applyScattersAntialiasingHint, applyDefaultAntialiasingHint
+*/
+void QCPAbstractPlottable::applyErrorBarsAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiasedErrorBars, QCP::aeErrorBars);
+}
+
+/*! \internal
+
+  Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
+  end.
+
+  This function may be used to help with the implementation of the \ref selectTest function for
+  specific plottables.
+
+  \note This function is identical to QCPAbstractItem::distSqrToLine
+*/
+double QCPAbstractPlottable::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
+{
+  QVector2D a(start);
+  QVector2D b(end);
+  QVector2D p(point);
+  QVector2D v(b-a);
+
+  double vLengthSqr = v.lengthSquared();
+  if (!qFuzzyIsNull(vLengthSqr))
+  {
+    double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
+    if (mu < 0)
+      return (a-p).lengthSquared();
+    else if (mu > 1)
+      return (b-p).lengthSquared();
+    else
+      return ((a + mu*v)-p).lengthSquared();
+  } else
+    return (a-p).lengthSquared();
+}
+
+/* inherits documentation from base class */
+void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
+{
+  Q_UNUSED(event)
+  Q_UNUSED(details)
+  if (mSelectable)
+  {
+    bool selBefore = mSelected;
+    setSelected(additive ? !mSelected : true);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelected != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
+{
+  if (mSelectable)
+  {
+    bool selBefore = mSelected;
+    setSelected(false);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelected != selBefore;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemAnchor
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemAnchor
+  \brief An anchor of an item to which positions can be attached to.
+
+  An item (QCPAbstractItem) may have one or more anchors. Unlike QCPItemPosition, an anchor doesn't
+  control anything on its item, but provides a way to tie other items via their positions to the
+  anchor.
+
+  For example, a QCPItemRect is defined by its positions \a topLeft and \a bottomRight.
+  Additionally it has various anchors like \a top, \a topRight or \a bottomLeft etc. So you can
+  attach the \a start (which is a QCPItemPosition) of a QCPItemLine to one of the anchors by
+  calling QCPItemPosition::setParentAnchor on \a start, passing the wanted anchor of the
+  QCPItemRect. This way the start of the line will now always follow the respective anchor location
+  on the rect item.
+
+  Note that QCPItemPosition derives from QCPItemAnchor, so every position can also serve as an
+  anchor to other positions.
+
+  To learn how to provide anchors in your own item subclasses, see the subclassing section of the
+  QCPAbstractItem documentation.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn virtual QCPItemPosition *QCPItemAnchor::toQCPItemPosition()
+
+  Returns 0 if this instance is merely a QCPItemAnchor, and a valid pointer of type QCPItemPosition* if
+  it actually is a QCPItemPosition (which is a subclass of QCPItemAnchor).
+
+  This safe downcast functionality could also be achieved with a dynamic_cast. However, QCustomPlot avoids
+  dynamic_cast to work with projects that don't have RTTI support enabled (e.g. -fno-rtti flag with
+  gcc compiler).
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates a new QCPItemAnchor. You shouldn't create QCPItemAnchor instances directly, even if
+  you want to make a new item subclass. Use \ref QCPAbstractItem::createAnchor instead, as
+  explained in the subclassing section of the QCPAbstractItem documentation.
+*/
+QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId) :
+  mName(name),
+  mParentPlot(parentPlot),
+  mParentItem(parentItem),
+  mAnchorId(anchorId)
+{
+}
+
+QCPItemAnchor::~QCPItemAnchor()
+{
+  // unregister as parent at children:
+  foreach (QCPItemPosition *child, mChildrenX.toList())
+  {
+    if (child->parentAnchorX() == this)
+      child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
+  }
+  foreach (QCPItemPosition *child, mChildrenY.toList())
+  {
+    if (child->parentAnchorY() == this)
+      child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
+  }
+}
+
+/*!
+  Returns the final absolute pixel position of the QCPItemAnchor on the QCustomPlot surface.
+
+  The pixel information is internally retrieved via QCPAbstractItem::anchorPixelPosition of the
+  parent item, QCPItemAnchor is just an intermediary.
+*/
+QPointF QCPItemAnchor::pixelPoint() const
+{
+  if (mParentItem)
+  {
+    if (mAnchorId > -1)
+    {
+      return mParentItem->anchorPixelPoint(mAnchorId);
+    } else
+    {
+      qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
+      return QPointF();
+    }
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "no parent item set";
+    return QPointF();
+  }
+}
+
+/*! \internal
+
+  Adds \a pos to the childX list of this anchor, which keeps track of which children use this
+  anchor as parent anchor for the respective coordinate. This is necessary to notify the children
+  prior to destruction of the anchor.
+
+  Note that this function does not change the parent setting in \a pos.
+*/
+void QCPItemAnchor::addChildX(QCPItemPosition *pos)
+{
+  if (!mChildrenX.contains(pos))
+    mChildrenX.insert(pos);
+  else
+    qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
+}
+
+/*! \internal
+
+  Removes \a pos from the childX list of this anchor.
+
+  Note that this function does not change the parent setting in \a pos.
+*/
+void QCPItemAnchor::removeChildX(QCPItemPosition *pos)
+{
+  if (!mChildrenX.remove(pos))
+    qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
+}
+
+/*! \internal
+
+  Adds \a pos to the childY list of this anchor, which keeps track of which children use this
+  anchor as parent anchor for the respective coordinate. This is necessary to notify the children
+  prior to destruction of the anchor.
+
+  Note that this function does not change the parent setting in \a pos.
+*/
+void QCPItemAnchor::addChildY(QCPItemPosition *pos)
+{
+  if (!mChildrenY.contains(pos))
+    mChildrenY.insert(pos);
+  else
+    qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
+}
+
+/*! \internal
+
+  Removes \a pos from the childY list of this anchor.
+
+  Note that this function does not change the parent setting in \a pos.
+*/
+void QCPItemAnchor::removeChildY(QCPItemPosition *pos)
+{
+  if (!mChildrenY.remove(pos))
+    qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemPosition
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemPosition
+  \brief Manages the position of an item.
+
+  Every item has at least one public QCPItemPosition member pointer which provides ways to position the
+  item on the QCustomPlot surface. Some items have multiple positions, for example QCPItemRect has two:
+  \a topLeft and \a bottomRight.
+
+  QCPItemPosition has a type (\ref PositionType) that can be set with \ref setType. This type
+  defines how coordinates passed to \ref setCoords are to be interpreted, e.g. as absolute pixel
+  coordinates, as plot coordinates of certain axes, etc. For more advanced plots it is also
+  possible to assign different types per X/Y coordinate of the position (see \ref setTypeX, \ref
+  setTypeY). This way an item could be positioned at a fixed pixel distance from the top in the Y
+  direction, while following a plot coordinate in the X direction.
+
+  A QCPItemPosition may have a parent QCPItemAnchor, see \ref setParentAnchor. This way you can tie
+  multiple items together. If the QCPItemPosition has a parent, its coordinates (\ref setCoords)
+  are considered to be absolute pixels in the reference frame of the parent anchor, where (0, 0)
+  means directly ontop of the parent anchor. For example, You could attach the \a start position of
+  a QCPItemLine to the \a bottom anchor of a QCPItemText to make the starting point of the line
+  always be centered under the text label, no matter where the text is moved to. For more advanced
+  plots, it is possible to assign different parent anchors per X/Y coordinate of the position, see
+  \ref setParentAnchorX, \ref setParentAnchorY. This way an item could follow another item in the X
+  direction but stay at a fixed position in the Y direction. Or even follow item A in X, and item B
+  in Y.
+
+  Note that every QCPItemPosition inherits from QCPItemAnchor and thus can itself be used as parent
+  anchor for other positions.
+
+  To set the apparent pixel position on the QCustomPlot surface directly, use \ref setPixelPoint. This
+  works no matter what type this QCPItemPosition is or what parent-child situation it is in, as \ref
+  setPixelPoint transforms the coordinates appropriately, to make the position appear at the specified
+  pixel values.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QCPItemPosition::PositionType *QCPItemPosition::type() const
+
+  Returns the current position type.
+
+  If different types were set for X and Y (\ref setTypeX, \ref setTypeY), this method returns the
+  type of the X coordinate. In that case rather use \a typeX() and \a typeY().
+
+  \see setType
+*/
+
+/*! \fn QCPItemAnchor *QCPItemPosition::parentAnchor() const
+
+  Returns the current parent anchor.
+
+  If different parent anchors were set for X and Y (\ref setParentAnchorX, \ref setParentAnchorY),
+  this method returns the parent anchor of the Y coordinate. In that case rather use \a
+  parentAnchorX() and \a parentAnchorY().
+
+  \see setParentAnchor
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates a new QCPItemPosition. You shouldn't create QCPItemPosition instances directly, even if
+  you want to make a new item subclass. Use \ref QCPAbstractItem::createPosition instead, as
+  explained in the subclassing section of the QCPAbstractItem documentation.
+*/
+QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name) :
+  QCPItemAnchor(parentPlot, parentItem, name),
+  mPositionTypeX(ptAbsolute),
+  mPositionTypeY(ptAbsolute),
+  mKey(0),
+  mValue(0),
+  mParentAnchorX(0),
+  mParentAnchorY(0)
+{
+}
+
+QCPItemPosition::~QCPItemPosition()
+{
+  // unregister as parent at children:
+  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
+  //       the setParentAnchor(0) call the correct QCPItemPosition::pixelPoint function instead of QCPItemAnchor::pixelPoint
+  foreach (QCPItemPosition *child, mChildrenX.toList())
+  {
+    if (child->parentAnchorX() == this)
+      child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
+  }
+  foreach (QCPItemPosition *child, mChildrenY.toList())
+  {
+    if (child->parentAnchorY() == this)
+      child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
+  }
+  // unregister as child in parent:
+  if (mParentAnchorX)
+    mParentAnchorX->removeChildX(this);
+  if (mParentAnchorY)
+    mParentAnchorY->removeChildY(this);
+}
+
+/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
+QCPAxisRect *QCPItemPosition::axisRect() const
+{
+  return mAxisRect.data();
+}
+
+/*!
+  Sets the type of the position. The type defines how the coordinates passed to \ref setCoords
+  should be handled and how the QCPItemPosition should behave in the plot.
+
+  The possible values for \a type can be separated in two main categories:
+
+  \li The position is regarded as a point in plot coordinates. This corresponds to \ref ptPlotCoords
+  and requires two axes that define the plot coordinate system. They can be specified with \ref setAxes.
+  By default, the QCustomPlot's x- and yAxis are used.
+
+  \li The position is fixed on the QCustomPlot surface, i.e. independent of axis ranges. This
+  corresponds to all other types, i.e. \ref ptAbsolute, \ref ptViewportRatio and \ref
+  ptAxisRectRatio. They differ only in the way the absolute position is described, see the
+  documentation of \ref PositionType for details. For \ref ptAxisRectRatio, note that you can specify
+  the axis rect with \ref setAxisRect. By default this is set to the main axis rect.
+
+  Note that the position type \ref ptPlotCoords is only available (and sensible) when the position
+  has no parent anchor (\ref setParentAnchor).
+
+  If the type is changed, the apparent pixel position on the plot is preserved. This means
+  the coordinates as retrieved with coords() and set with \ref setCoords may change in the process.
+
+  This method sets the type for both X and Y directions. It is also possible to set different types
+  for X and Y, see \ref setTypeX, \ref setTypeY.
+*/
+void QCPItemPosition::setType(QCPItemPosition::PositionType type)
+{
+  setTypeX(type);
+  setTypeY(type);
+}
+
+/*!
+  This method sets the position type of the X coordinate to \a type.
+
+  For a detailed description of what a position type is, see the documentation of \ref setType.
+
+  \see setType, setTypeY
+*/
+void QCPItemPosition::setTypeX(QCPItemPosition::PositionType type)
+{
+  if (mPositionTypeX != type)
+  {
+    // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
+    // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
+    bool retainPixelPosition = true;
+    if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
+      retainPixelPosition = false;
+    if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
+      retainPixelPosition = false;
+
+    QPointF pixel;
+    if (retainPixelPosition)
+      pixel = pixelPoint();
+
+    mPositionTypeX = type;
+
+    if (retainPixelPosition)
+      setPixelPoint(pixel);
+  }
+}
+
+/*!
+  This method sets the position type of the Y coordinate to \a type.
+
+  For a detailed description of what a position type is, see the documentation of \ref setType.
+
+  \see setType, setTypeX
+*/
+void QCPItemPosition::setTypeY(QCPItemPosition::PositionType type)
+{
+  if (mPositionTypeY != type)
+  {
+    // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
+    // were deleted), don't try to recover the pixelPoint() because it would output a qDebug warning.
+    bool retainPixelPosition = true;
+    if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
+      retainPixelPosition = false;
+    if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
+      retainPixelPosition = false;
+
+    QPointF pixel;
+    if (retainPixelPosition)
+      pixel = pixelPoint();
+
+    mPositionTypeY = type;
+
+    if (retainPixelPosition)
+      setPixelPoint(pixel);
+  }
+}
+
+/*!
+  Sets the parent of this QCPItemPosition to \a parentAnchor. This means the position will now
+  follow any position changes of the anchor. The local coordinate system of positions with a parent
+  anchor always is absolute pixels, with (0, 0) being exactly on top of the parent anchor. (Hence
+  the type shouldn't be set to \ref ptPlotCoords for positions with parent anchors.)
+
+  if \a keepPixelPosition is true, the current pixel position of the QCPItemPosition is preserved
+  during reparenting. If it's set to false, the coordinates are set to (0, 0), i.e. the position
+  will be exactly on top of the parent anchor.
+
+  To remove this QCPItemPosition from any parent anchor, set \a parentAnchor to 0.
+
+  If the QCPItemPosition previously had no parent and the type is \ref ptPlotCoords, the type is
+  set to \ref ptAbsolute, to keep the position in a valid state.
+
+  This method sets the parent anchor for both X and Y directions. It is also possible to set
+  different parents for X and Y, see \ref setParentAnchorX, \ref setParentAnchorY.
+*/
+bool QCPItemPosition::setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
+{
+  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
+  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
+  return successX && successY;
+}
+
+/*!
+  This method sets the parent anchor of the X coordinate to \a parentAnchor.
+
+  For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
+
+  \see setParentAnchor, setParentAnchorY
+*/
+bool QCPItemPosition::setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
+{
+  // make sure self is not assigned as parent:
+  if (parentAnchor == this)
+  {
+    qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
+    return false;
+  }
+  // make sure no recursive parent-child-relationships are created:
+  QCPItemAnchor *currentParent = parentAnchor;
+  while (currentParent)
+  {
+    if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
+    {
+      // is a QCPItemPosition, might have further parent, so keep iterating
+      if (currentParentPos == this)
+      {
+        qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
+        return false;
+      }
+      currentParent = currentParentPos->parentAnchorX();
+    } else
+    {
+      // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
+      // same, to prevent a position being child of an anchor which itself depends on the position,
+      // because they're both on the same item:
+      if (currentParent->mParentItem == mParentItem)
+      {
+        qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
+        return false;
+      }
+      break;
+    }
+  }
+
+  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
+  if (!mParentAnchorX && mPositionTypeX == ptPlotCoords)
+    setTypeX(ptAbsolute);
+
+  // save pixel position:
+  QPointF pixelP;
+  if (keepPixelPosition)
+    pixelP = pixelPoint();
+  // unregister at current parent anchor:
+  if (mParentAnchorX)
+    mParentAnchorX->removeChildX(this);
+  // register at new parent anchor:
+  if (parentAnchor)
+    parentAnchor->addChildX(this);
+  mParentAnchorX = parentAnchor;
+  // restore pixel position under new parent:
+  if (keepPixelPosition)
+    setPixelPoint(pixelP);
+  else
+    setCoords(0, coords().y());
+  return true;
+}
+
+/*!
+  This method sets the parent anchor of the Y coordinate to \a parentAnchor.
+
+  For a detailed description of what a parent anchor is, see the documentation of \ref setParentAnchor.
+
+  \see setParentAnchor, setParentAnchorX
+*/
+bool QCPItemPosition::setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition)
+{
+  // make sure self is not assigned as parent:
+  if (parentAnchor == this)
+  {
+    qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
+    return false;
+  }
+  // make sure no recursive parent-child-relationships are created:
+  QCPItemAnchor *currentParent = parentAnchor;
+  while (currentParent)
+  {
+    if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
+    {
+      // is a QCPItemPosition, might have further parent, so keep iterating
+      if (currentParentPos == this)
+      {
+        qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
+        return false;
+      }
+      currentParent = currentParentPos->parentAnchorY();
+    } else
+    {
+      // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
+      // same, to prevent a position being child of an anchor which itself depends on the position,
+      // because they're both on the same item:
+      if (currentParent->mParentItem == mParentItem)
+      {
+        qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
+        return false;
+      }
+      break;
+    }
+  }
+
+  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
+  if (!mParentAnchorY && mPositionTypeY == ptPlotCoords)
+    setTypeY(ptAbsolute);
+
+  // save pixel position:
+  QPointF pixelP;
+  if (keepPixelPosition)
+    pixelP = pixelPoint();
+  // unregister at current parent anchor:
+  if (mParentAnchorY)
+    mParentAnchorY->removeChildY(this);
+  // register at new parent anchor:
+  if (parentAnchor)
+    parentAnchor->addChildY(this);
+  mParentAnchorY = parentAnchor;
+  // restore pixel position under new parent:
+  if (keepPixelPosition)
+    setPixelPoint(pixelP);
+  else
+    setCoords(coords().x(), 0);
+  return true;
+}
+
+/*!
+  Sets the coordinates of this QCPItemPosition. What the coordinates mean, is defined by the type
+  (\ref setType, \ref setTypeX, \ref setTypeY).
+
+  For example, if the type is \ref ptAbsolute, \a key and \a value mean the x and y pixel position
+  on the QCustomPlot surface. In that case the origin (0, 0) is in the top left corner of the
+  QCustomPlot viewport. If the type is \ref ptPlotCoords, \a key and \a value mean a point in the
+  plot coordinate system defined by the axes set by \ref setAxes. By default those are the
+  QCustomPlot's xAxis and yAxis. See the documentation of \ref setType for other available
+  coordinate types and their meaning.
+
+  If different types were configured for X and Y (\ref setTypeX, \ref setTypeY), \a key and \a
+  value must also be provided in the different coordinate systems. Here, the X type refers to \a
+  key, and the Y type refers to \a value.
+
+  \see setPixelPoint
+*/
+void QCPItemPosition::setCoords(double key, double value)
+{
+  mKey = key;
+  mValue = value;
+}
+
+/*! \overload
+
+  Sets the coordinates as a QPointF \a pos where pos.x has the meaning of \a key and pos.y the
+  meaning of \a value of the \ref setCoords(double key, double value) method.
+*/
+void QCPItemPosition::setCoords(const QPointF &pos)
+{
+  setCoords(pos.x(), pos.y());
+}
+
+/*!
+  Returns the final absolute pixel position of the QCPItemPosition on the QCustomPlot surface. It
+  includes all effects of type (\ref setType) and possible parent anchors (\ref setParentAnchor).
+
+  \see setPixelPoint
+*/
+QPointF QCPItemPosition::pixelPoint() const
+{
+  QPointF result;
+
+  // determine X:
+  switch (mPositionTypeX)
+  {
+    case ptAbsolute:
+    {
+      result.rx() = mKey;
+      if (mParentAnchorX)
+        result.rx() += mParentAnchorX->pixelPoint().x();
+      break;
+    }
+    case ptViewportRatio:
+    {
+      result.rx() = mKey*mParentPlot->viewport().width();
+      if (mParentAnchorX)
+        result.rx() += mParentAnchorX->pixelPoint().x();
+      else
+        result.rx() += mParentPlot->viewport().left();
+      break;
+    }
+    case ptAxisRectRatio:
+    {
+      if (mAxisRect)
+      {
+        result.rx() = mKey*mAxisRect.data()->width();
+        if (mParentAnchorX)
+          result.rx() += mParentAnchorX->pixelPoint().x();
+        else
+          result.rx() += mAxisRect.data()->left();
+      } else
+        qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
+      break;
+    }
+    case ptPlotCoords:
+    {
+      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
+        result.rx() = mKeyAxis.data()->coordToPixel(mKey);
+      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
+        result.rx() = mValueAxis.data()->coordToPixel(mValue);
+      else
+        qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
+      break;
+    }
+  }
+
+  // determine Y:
+  switch (mPositionTypeY)
+  {
+    case ptAbsolute:
+    {
+      result.ry() = mValue;
+      if (mParentAnchorY)
+        result.ry() += mParentAnchorY->pixelPoint().y();
+      break;
+    }
+    case ptViewportRatio:
+    {
+      result.ry() = mValue*mParentPlot->viewport().height();
+      if (mParentAnchorY)
+        result.ry() += mParentAnchorY->pixelPoint().y();
+      else
+        result.ry() += mParentPlot->viewport().top();
+      break;
+    }
+    case ptAxisRectRatio:
+    {
+      if (mAxisRect)
+      {
+        result.ry() = mValue*mAxisRect.data()->height();
+        if (mParentAnchorY)
+          result.ry() += mParentAnchorY->pixelPoint().y();
+        else
+          result.ry() += mAxisRect.data()->top();
+      } else
+        qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
+      break;
+    }
+    case ptPlotCoords:
+    {
+      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
+        result.ry() = mKeyAxis.data()->coordToPixel(mKey);
+      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
+        result.ry() = mValueAxis.data()->coordToPixel(mValue);
+      else
+        qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
+      break;
+    }
+  }
+
+  return result;
+}
+
+/*!
+  When \ref setType is \ref ptPlotCoords, this function may be used to specify the axes the
+  coordinates set with \ref setCoords relate to. By default they are set to the initial xAxis and
+  yAxis of the QCustomPlot.
+*/
+void QCPItemPosition::setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
+{
+  mKeyAxis = keyAxis;
+  mValueAxis = valueAxis;
+}
+
+/*!
+  When \ref setType is \ref ptAxisRectRatio, this function may be used to specify the axis rect the
+  coordinates set with \ref setCoords relate to. By default this is set to the main axis rect of
+  the QCustomPlot.
+*/
+void QCPItemPosition::setAxisRect(QCPAxisRect *axisRect)
+{
+  mAxisRect = axisRect;
+}
+
+/*!
+  Sets the apparent pixel position. This works no matter what type (\ref setType) this
+  QCPItemPosition is or what parent-child situation it is in, as coordinates are transformed
+  appropriately, to make the position finally appear at the specified pixel values.
+
+  Only if the type is \ref ptAbsolute and no parent anchor is set, this function's effect is
+  identical to that of \ref setCoords.
+
+  \see pixelPoint, setCoords
+*/
+void QCPItemPosition::setPixelPoint(const QPointF &pixelPoint)
+{
+  double x = pixelPoint.x();
+  double y = pixelPoint.y();
+
+  switch (mPositionTypeX)
+  {
+    case ptAbsolute:
+    {
+      if (mParentAnchorX)
+        x -= mParentAnchorX->pixelPoint().x();
+      break;
+    }
+    case ptViewportRatio:
+    {
+      if (mParentAnchorX)
+        x -= mParentAnchorX->pixelPoint().x();
+      else
+        x -= mParentPlot->viewport().left();
+      x /= (double)mParentPlot->viewport().width();
+      break;
+    }
+    case ptAxisRectRatio:
+    {
+      if (mAxisRect)
+      {
+        if (mParentAnchorX)
+          x -= mParentAnchorX->pixelPoint().x();
+        else
+          x -= mAxisRect.data()->left();
+        x /= (double)mAxisRect.data()->width();
+      } else
+        qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
+      break;
+    }
+    case ptPlotCoords:
+    {
+      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
+        x = mKeyAxis.data()->pixelToCoord(x);
+      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
+        y = mValueAxis.data()->pixelToCoord(x);
+      else
+        qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
+      break;
+    }
+  }
+
+  switch (mPositionTypeY)
+  {
+    case ptAbsolute:
+    {
+      if (mParentAnchorY)
+        y -= mParentAnchorY->pixelPoint().y();
+      break;
+    }
+    case ptViewportRatio:
+    {
+      if (mParentAnchorY)
+        y -= mParentAnchorY->pixelPoint().y();
+      else
+        y -= mParentPlot->viewport().top();
+      y /= (double)mParentPlot->viewport().height();
+      break;
+    }
+    case ptAxisRectRatio:
+    {
+      if (mAxisRect)
+      {
+        if (mParentAnchorY)
+          y -= mParentAnchorY->pixelPoint().y();
+        else
+          y -= mAxisRect.data()->top();
+        y /= (double)mAxisRect.data()->height();
+      } else
+        qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
+      break;
+    }
+    case ptPlotCoords:
+    {
+      if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
+        x = mKeyAxis.data()->pixelToCoord(y);
+      else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
+        y = mValueAxis.data()->pixelToCoord(y);
+      else
+        qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
+      break;
+    }
+  }
+
+  setCoords(x, y);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPAbstractItem
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPAbstractItem
+  \brief The abstract base class for all items in a plot.
+
+  In QCustomPlot, items are supplemental graphical elements that are neither plottables
+  (QCPAbstractPlottable) nor axes (QCPAxis). While plottables are always tied to two axes and thus
+  plot coordinates, items can also be placed in absolute coordinates independent of any axes. Each
+  specific item has at least one QCPItemPosition member which controls the positioning. Some items
+  are defined by more than one coordinate and thus have two or more QCPItemPosition members (For
+  example, QCPItemRect has \a topLeft and \a bottomRight).
+
+  This abstract base class defines a very basic interface like visibility and clipping. Since this
+  class is abstract, it can't be instantiated. Use one of the subclasses or create a subclass
+  yourself to create new items.
+
+  The built-in items are:
+  <table>
+  <tr><td>QCPItemLine</td><td>A line defined by a start and an end point. May have different ending styles on each side (e.g. arrows).</td></tr>
+  <tr><td>QCPItemStraightLine</td><td>A straight line defined by a start and a direction point. Unlike QCPItemLine, the straight line is infinitely long and has no endings.</td></tr>
+  <tr><td>QCPItemCurve</td><td>A curve defined by start, end and two intermediate control points. May have different ending styles on each side (e.g. arrows).</td></tr>
+  <tr><td>QCPItemRect</td><td>A rectangle</td></tr>
+  <tr><td>QCPItemEllipse</td><td>An ellipse</td></tr>
+  <tr><td>QCPItemPixmap</td><td>An arbitrary pixmap</td></tr>
+  <tr><td>QCPItemText</td><td>A text label</td></tr>
+  <tr><td>QCPItemBracket</td><td>A bracket which may be used to reference/highlight certain parts in the plot.</td></tr>
+  <tr><td>QCPItemTracer</td><td>An item that can be attached to a QCPGraph and sticks to its data points, given a key coordinate.</td></tr>
+  </table>
+
+  \section items-clipping Clipping
+
+  Items are by default clipped to the main axis rect (they are only visible inside the axis rect).
+  To make an item visible outside that axis rect, disable clipping via \ref setClipToAxisRect
+  "setClipToAxisRect(false)".
+
+  On the other hand if you want the item to be clipped to a different axis rect, specify it via
+  \ref setClipAxisRect. This clipAxisRect property of an item is only used for clipping behaviour, and
+  in principle is independent of the coordinate axes the item might be tied to via its position
+  members (\ref QCPItemPosition::setAxes). However, it is common that the axis rect for clipping
+  also contains the axes used for the item positions.
+
+  \section items-using Using items
+
+  First you instantiate the item you want to use and add it to the plot:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-1
+  by default, the positions of the item are bound to the x- and y-Axis of the plot. So we can just
+  set the plot coordinates where the line should start/end:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-2
+  If we don't want the line to be positioned in plot coordinates but a different coordinate system,
+  e.g. absolute pixel positions on the QCustomPlot surface, we need to change the position type like this:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-3
+  Then we can set the coordinates, this time in pixels:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-4
+  and make the line visible on the entire QCustomPlot, by disabling clipping to the axis rect:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpitemline-creation-5
+
+  For more advanced plots, it is even possible to set different types and parent anchors per X/Y
+  coordinate of an item position, using for example \ref QCPItemPosition::setTypeX or \ref
+  QCPItemPosition::setParentAnchorX. For details, see the documentation of \ref QCPItemPosition.
+
+  \section items-subclassing Creating own items
+
+  To create an own item, you implement a subclass of QCPAbstractItem. These are the pure
+  virtual functions, you must implement:
+  \li \ref selectTest
+  \li \ref draw
+
+  See the documentation of those functions for what they need to do.
+
+  \subsection items-positioning Allowing the item to be positioned
+
+  As mentioned, item positions are represented by QCPItemPosition members. Let's assume the new item shall
+  have only one point as its position (as opposed to two like a rect or multiple like a polygon). You then add
+  a public member of type QCPItemPosition like so:
+
+  \code QCPItemPosition * const myPosition;\endcode
+
+  the const makes sure the pointer itself can't be modified from the user of your new item (the QCPItemPosition
+  instance it points to, can be modified, of course).
+  The initialization of this pointer is made easy with the \ref createPosition function. Just assign
+  the return value of this function to each QCPItemPosition in the constructor of your item. \ref createPosition
+  takes a string which is the name of the position, typically this is identical to the variable name.
+  For example, the constructor of QCPItemExample could look like this:
+
+  \code
+  QCPItemExample::QCPItemExample(QCustomPlot *parentPlot) :
+    QCPAbstractItem(parentPlot),
+    myPosition(createPosition("myPosition"))
+  {
+    // other constructor code
+  }
+  \endcode
+
+  \subsection items-drawing The draw function
+
+  To give your item a visual representation, reimplement the \ref draw function and use the passed
+  QCPPainter to draw the item. You can retrieve the item position in pixel coordinates from the
+  position member(s) via \ref QCPItemPosition::pixelPoint.
+
+  To optimize performance you should calculate a bounding rect first (don't forget to take the pen
+  width into account), check whether it intersects the \ref clipRect, and only draw the item at all
+  if this is the case.
+
+  \subsection items-selection The selectTest function
+
+  Your implementation of the \ref selectTest function may use the helpers \ref distSqrToLine and
+  \ref rectSelectTest. With these, the implementation of the selection test becomes significantly
+  simpler for most items. See the documentation of \ref selectTest for what the function parameters
+  mean and what the function should return.
+
+  \subsection anchors Providing anchors
+
+  Providing anchors (QCPItemAnchor) starts off like adding a position. First you create a public
+  member, e.g.
+
+  \code QCPItemAnchor * const bottom;\endcode
+
+  and create it in the constructor with the \ref createAnchor function, assigning it a name and an
+  anchor id (an integer enumerating all anchors on the item, you may create an own enum for this).
+  Since anchors can be placed anywhere, relative to the item's position(s), your item needs to
+  provide the position of every anchor with the reimplementation of the \ref anchorPixelPoint(int
+  anchorId) function.
+
+  In essence the QCPItemAnchor is merely an intermediary that itself asks your item for the pixel
+  position when anything attached to the anchor needs to know the coordinates.
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn QList<QCPItemPosition*> QCPAbstractItem::positions() const
+
+  Returns all positions of the item in a list.
+
+  \see anchors, position
+*/
+
+/*! \fn QList<QCPItemAnchor*> QCPAbstractItem::anchors() const
+
+  Returns all anchors of the item in a list. Note that since a position (QCPItemPosition) is always
+  also an anchor, the list will also contain the positions of this item.
+
+  \see positions, anchor
+*/
+
+/* end of documentation of inline functions */
+/* start documentation of pure virtual functions */
+
+/*! \fn void QCPAbstractItem::draw(QCPPainter *painter) = 0
+  \internal
+
+  Draws this item with the provided \a painter.
+
+  The cliprect of the provided painter is set to the rect returned by \ref clipRect before this
+  function is called. The clipRect depends on the clipping settings defined by \ref
+  setClipToAxisRect and \ref setClipAxisRect.
+*/
+
+/* end documentation of pure virtual functions */
+/* start documentation of signals */
+
+/*! \fn void QCPAbstractItem::selectionChanged(bool selected)
+  This signal is emitted when the selection state of this item has changed, either by user interaction
+  or by a direct call to \ref setSelected.
+*/
+
+/* end documentation of signals */
+
+/*!
+  Base class constructor which initializes base class members.
+*/
+QCPAbstractItem::QCPAbstractItem(QCustomPlot *parentPlot) :
+  QCPLayerable(parentPlot),
+  mClipToAxisRect(false),
+  mSelectable(true),
+  mSelected(false)
+{
+  QList<QCPAxisRect*> rects = parentPlot->axisRects();
+  if (rects.size() > 0)
+  {
+    setClipToAxisRect(true);
+    setClipAxisRect(rects.first());
+  }
+}
+
+QCPAbstractItem::~QCPAbstractItem()
+{
+  // don't delete mPositions because every position is also an anchor and thus in mAnchors
+  qDeleteAll(mAnchors);
+}
+
+/* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
+QCPAxisRect *QCPAbstractItem::clipAxisRect() const
+{
+  return mClipAxisRect.data();
+}
+
+/*!
+  Sets whether the item shall be clipped to an axis rect or whether it shall be visible on the
+  entire QCustomPlot. The axis rect can be set with \ref setClipAxisRect.
+
+  \see setClipAxisRect
+*/
+void QCPAbstractItem::setClipToAxisRect(bool clip)
+{
+  mClipToAxisRect = clip;
+  if (mClipToAxisRect)
+    setParentLayerable(mClipAxisRect.data());
+}
+
+/*!
+  Sets the clip axis rect. It defines the rect that will be used to clip the item when \ref
+  setClipToAxisRect is set to true.
+
+  \see setClipToAxisRect
+*/
+void QCPAbstractItem::setClipAxisRect(QCPAxisRect *rect)
+{
+  mClipAxisRect = rect;
+  if (mClipToAxisRect)
+    setParentLayerable(mClipAxisRect.data());
+}
+
+/*!
+  Sets whether the user can (de-)select this item by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains QCustomPlot::iSelectItems.)
+
+  However, even when \a selectable was set to false, it is possible to set the selection manually,
+  by calling \ref setSelected.
+
+  \see QCustomPlot::setInteractions, setSelected
+*/
+void QCPAbstractItem::setSelectable(bool selectable)
+{
+  if (mSelectable != selectable)
+  {
+    mSelectable = selectable;
+    emit selectableChanged(mSelectable);
+  }
+}
+
+/*!
+  Sets whether this item is selected or not. When selected, it might use a different visual
+  appearance (e.g. pen and brush), this depends on the specific item though.
+
+  The entire selection mechanism for items is handled automatically when \ref
+  QCustomPlot::setInteractions contains QCustomPlot::iSelectItems. You only need to call this
+  function when you wish to change the selection state manually.
+
+  This function can change the selection state even when \ref setSelectable was set to false.
+
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+
+  \see setSelectable, selectTest
+*/
+void QCPAbstractItem::setSelected(bool selected)
+{
+  if (mSelected != selected)
+  {
+    mSelected = selected;
+    emit selectionChanged(mSelected);
+  }
+}
+
+/*!
+  Returns the QCPItemPosition with the specified \a name. If this item doesn't have a position by
+  that name, returns 0.
+
+  This function provides an alternative way to access item positions. Normally, you access
+  positions direcly by their member pointers (which typically have the same variable name as \a
+  name).
+
+  \see positions, anchor
+*/
+QCPItemPosition *QCPAbstractItem::position(const QString &name) const
+{
+  for (int i=0; i<mPositions.size(); ++i)
+  {
+    if (mPositions.at(i)->name() == name)
+      return mPositions.at(i);
+  }
+  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
+  return 0;
+}
+
+/*!
+  Returns the QCPItemAnchor with the specified \a name. If this item doesn't have an anchor by
+  that name, returns 0.
+
+  This function provides an alternative way to access item anchors. Normally, you access
+  anchors direcly by their member pointers (which typically have the same variable name as \a
+  name).
+
+  \see anchors, position
+*/
+QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
+{
+  for (int i=0; i<mAnchors.size(); ++i)
+  {
+    if (mAnchors.at(i)->name() == name)
+      return mAnchors.at(i);
+  }
+  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
+  return 0;
+}
+
+/*!
+  Returns whether this item has an anchor with the specified \a name.
+
+  Note that you can check for positions with this function, too. This is because every position is
+  also an anchor (QCPItemPosition inherits from QCPItemAnchor).
+
+  \see anchor, position
+*/
+bool QCPAbstractItem::hasAnchor(const QString &name) const
+{
+  for (int i=0; i<mAnchors.size(); ++i)
+  {
+    if (mAnchors.at(i)->name() == name)
+      return true;
+  }
+  return false;
+}
+
+/*! \internal
+
+  Returns the rect the visual representation of this item is clipped to. This depends on the
+  current setting of \ref setClipToAxisRect as well as the axis rect set with \ref setClipAxisRect.
+
+  If the item is not clipped to an axis rect, the \ref QCustomPlot::viewport rect is returned.
+
+  \see draw
+*/
+QRect QCPAbstractItem::clipRect() const
+{
+  if (mClipToAxisRect && mClipAxisRect)
+    return mClipAxisRect.data()->rect();
+  else
+    return mParentPlot->viewport();
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing item lines.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+
+  \see setAntialiased
+*/
+void QCPAbstractItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeItems);
+}
+
+/*! \internal
+
+  Finds the shortest squared distance of \a point to the line segment defined by \a start and \a
+  end.
+
+  This function may be used to help with the implementation of the \ref selectTest function for
+  specific items.
+
+  \note This function is identical to QCPAbstractPlottable::distSqrToLine
+
+  \see rectSelectTest
+*/
+double QCPAbstractItem::distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const
+{
+  QVector2D a(start);
+  QVector2D b(end);
+  QVector2D p(point);
+  QVector2D v(b-a);
+
+  double vLengthSqr = v.lengthSquared();
+  if (!qFuzzyIsNull(vLengthSqr))
+  {
+    double mu = QVector2D::dotProduct(p-a, v)/vLengthSqr;
+    if (mu < 0)
+      return (a-p).lengthSquared();
+    else if (mu > 1)
+      return (b-p).lengthSquared();
+    else
+      return ((a + mu*v)-p).lengthSquared();
+  } else
+    return (a-p).lengthSquared();
+}
+
+/*! \internal
+
+  A convenience function which returns the selectTest value for a specified \a rect and a specified
+  click position \a pos. \a filledRect defines whether a click inside the rect should also be
+  considered a hit or whether only the rect border is sensitive to hits.
+
+  This function may be used to help with the implementation of the \ref selectTest function for
+  specific items.
+
+  For example, if your item consists of four rects, call this function four times, once for each
+  rect, in your \ref selectTest reimplementation. Finally, return the minimum of all four returned
+  values.
+
+  \see distSqrToLine
+*/
+double QCPAbstractItem::rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const
+{
+  double result = -1;
+
+  // distance to border:
+  QList<QLineF> lines;
+  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
+        << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
+  double minDistSqr = std::numeric_limits<double>::max();
+  for (int i=0; i<lines.size(); ++i)
+  {
+    double distSqr = distSqrToLine(lines.at(i).p1(), lines.at(i).p2(), pos);
+    if (distSqr < minDistSqr)
+      minDistSqr = distSqr;
+  }
+  result = qSqrt(minDistSqr);
+
+  // filled rect, allow click inside to count as hit:
+  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
+  {
+    if (rect.contains(pos))
+      result = mParentPlot->selectionTolerance()*0.99;
+  }
+  return result;
+}
+
+/*! \internal
+
+  Returns the pixel position of the anchor with Id \a anchorId. This function must be reimplemented in
+  item subclasses if they want to provide anchors (QCPItemAnchor).
+
+  For example, if the item has two anchors with id 0 and 1, this function takes one of these anchor
+  ids and returns the respective pixel points of the specified anchor.
+
+  \see createAnchor
+*/
+QPointF QCPAbstractItem::anchorPixelPoint(int anchorId) const
+{
+  qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Creates a QCPItemPosition, registers it with this item and returns a pointer to it. The specified
+  \a name must be a unique string that is usually identical to the variable name of the position
+  member (This is needed to provide the name-based \ref position access to positions).
+
+  Don't delete positions created by this function manually, as the item will take care of it.
+
+  Use this function in the constructor (initialization list) of the specific item subclass to
+  create each position member. Don't create QCPItemPositions with \b new yourself, because they
+  won't be registered with the item properly.
+
+  \see createAnchor
+*/
+QCPItemPosition *QCPAbstractItem::createPosition(const QString &name)
+{
+  if (hasAnchor(name))
+    qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
+  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
+  mPositions.append(newPosition);
+  mAnchors.append(newPosition); // every position is also an anchor
+  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
+  newPosition->setType(QCPItemPosition::ptPlotCoords);
+  if (mParentPlot->axisRect())
+    newPosition->setAxisRect(mParentPlot->axisRect());
+  newPosition->setCoords(0, 0);
+  return newPosition;
+}
+
+/*! \internal
+
+  Creates a QCPItemAnchor, registers it with this item and returns a pointer to it. The specified
+  \a name must be a unique string that is usually identical to the variable name of the anchor
+  member (This is needed to provide the name based \ref anchor access to anchors).
+
+  The \a anchorId must be a number identifying the created anchor. It is recommended to create an
+  enum (e.g. "AnchorIndex") for this on each item that uses anchors. This id is used by the anchor
+  to identify itself when it calls QCPAbstractItem::anchorPixelPoint. That function then returns
+  the correct pixel coordinates for the passed anchor id.
+
+  Don't delete anchors created by this function manually, as the item will take care of it.
+
+  Use this function in the constructor (initialization list) of the specific item subclass to
+  create each anchor member. Don't create QCPItemAnchors with \b new yourself, because then they
+  won't be registered with the item properly.
+
+  \see createPosition
+*/
+QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
+{
+  if (hasAnchor(name))
+    qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
+  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
+  mAnchors.append(newAnchor);
+  return newAnchor;
+}
+
+/* inherits documentation from base class */
+void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
+{
+  Q_UNUSED(event)
+  Q_UNUSED(details)
+  if (mSelectable)
+  {
+    bool selBefore = mSelected;
+    setSelected(additive ? !mSelected : true);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelected != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
+{
+  if (mSelectable)
+  {
+    bool selBefore = mSelected;
+    setSelected(false);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelected != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+QCP::Interaction QCPAbstractItem::selectionCategory() const
+{
+  return QCP::iSelectItems;
+}
+
+
+/*! \file */
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCustomPlot
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCustomPlot
+
+  \brief The central class of the library. This is the QWidget which displays the plot and
+  interacts with the user.
+
+  For tutorials on how to use QCustomPlot, see the website\n
+  http://www.qcustomplot.com/
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn QRect QCustomPlot::viewport() const
+
+  Returns the viewport rect of this QCustomPlot instance. The viewport is the area the plot is
+  drawn in, all mechanisms, e.g. margin caluclation take the viewport to be the outer border of the
+  plot. The viewport normally is the rect() of the QCustomPlot widget, i.e. a rect with top left
+  (0, 0) and size of the QCustomPlot widget.
+
+  Don't confuse the viewport with the axis rect (QCustomPlot::axisRect). An axis rect is typically
+  an area enclosed by four axes, where the graphs/plottables are drawn in. The viewport is larger
+  and contains also the axes themselves, their tick numbers, their labels, the plot title etc.
+
+  Only when saving to a file (see \ref savePng, \ref savePdf etc.) the viewport is temporarily
+  modified to allow saving plots with sizes independent of the current widget size.
+*/
+
+/*! \fn QCPLayoutGrid *QCustomPlot::plotLayout() const
+
+  Returns the top level layout of this QCustomPlot instance. It is a \ref QCPLayoutGrid, initially containing just
+  one cell with the main QCPAxisRect inside.
+*/
+
+/* end of documentation of inline functions */
+/* start of documentation of signals */
+
+/*! \fn void QCustomPlot::mouseDoubleClick(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse double click event.
+*/
+
+/*! \fn void QCustomPlot::mousePress(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse press event.
+
+  It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
+  connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
+  QCPAxisRect::setRangeDragAxes.
+*/
+
+/*! \fn void QCustomPlot::mouseMove(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse move event.
+
+  It is emitted before QCustomPlot handles any other mechanism like range dragging. So a slot
+  connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeDrag or \ref
+  QCPAxisRect::setRangeDragAxes.
+
+  \warning It is discouraged to change the drag-axes with \ref QCPAxisRect::setRangeDragAxes here,
+  because the dragging starting point was saved the moment the mouse was pressed. Thus it only has
+  a meaning for the range drag axes that were set at that moment. If you want to change the drag
+  axes, consider doing this in the \ref mousePress signal instead.
+*/
+
+/*! \fn void QCustomPlot::mouseRelease(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse release event.
+
+  It is emitted before QCustomPlot handles any other mechanisms like object selection. So a
+  slot connected to this signal can still influence the behaviour e.g. with \ref setInteractions or
+  \ref QCPAbstractPlottable::setSelectable.
+*/
+
+/*! \fn void QCustomPlot::mouseWheel(QMouseEvent *event)
+
+  This signal is emitted when the QCustomPlot receives a mouse wheel event.
+
+  It is emitted before QCustomPlot handles any other mechanisms like range zooming. So a slot
+  connected to this signal can still influence the behaviour e.g. with \ref QCPAxisRect::setRangeZoom, \ref
+  QCPAxisRect::setRangeZoomAxes or \ref QCPAxisRect::setRangeZoomFactor.
+*/
+
+/*! \fn void QCustomPlot::plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
+
+  This signal is emitted when a plottable is clicked.
+
+  \a event is the mouse event that caused the click and \a plottable is the plottable that received
+  the click.
+
+  \see plottableDoubleClick
+*/
+
+/*! \fn void QCustomPlot::plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event)
+
+  This signal is emitted when a plottable is double clicked.
+
+  \a event is the mouse event that caused the click and \a plottable is the plottable that received
+  the click.
+
+  \see plottableClick
+*/
+
+/*! \fn void QCustomPlot::itemClick(QCPAbstractItem *item, QMouseEvent *event)
+
+  This signal is emitted when an item is clicked.
+
+  \a event is the mouse event that caused the click and \a item is the item that received the
+  click.
+
+  \see itemDoubleClick
+*/
+
+/*! \fn void QCustomPlot::itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
+
+  This signal is emitted when an item is double clicked.
+
+  \a event is the mouse event that caused the click and \a item is the item that received the
+  click.
+
+  \see itemClick
+*/
+
+/*! \fn void QCustomPlot::axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
+
+  This signal is emitted when an axis is clicked.
+
+  \a event is the mouse event that caused the click, \a axis is the axis that received the click and
+  \a part indicates the part of the axis that was clicked.
+
+  \see axisDoubleClick
+*/
+
+/*! \fn void QCustomPlot::axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
+
+  This signal is emitted when an axis is double clicked.
+
+  \a event is the mouse event that caused the click, \a axis is the axis that received the click and
+  \a part indicates the part of the axis that was clicked.
+
+  \see axisClick
+*/
+
+/*! \fn void QCustomPlot::legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
+
+  This signal is emitted when a legend (item) is clicked.
+
+  \a event is the mouse event that caused the click, \a legend is the legend that received the
+  click and \a item is the legend item that received the click. If only the legend and no item is
+  clicked, \a item is 0. This happens for a click inside the legend padding or the space between
+  two items.
+
+  \see legendDoubleClick
+*/
+
+/*! \fn void QCustomPlot::legendDoubleClick(QCPLegend *legend,  QCPAbstractLegendItem *item, QMouseEvent *event)
+
+  This signal is emitted when a legend (item) is double clicked.
+
+  \a event is the mouse event that caused the click, \a legend is the legend that received the
+  click and \a item is the legend item that received the click. If only the legend and no item is
+  clicked, \a item is 0. This happens for a click inside the legend padding or the space between
+  two items.
+
+  \see legendClick
+*/
+
+/*! \fn void QCustomPlot:: titleClick(QMouseEvent *event, QCPPlotTitle *title)
+
+  This signal is emitted when a plot title is clicked.
+
+  \a event is the mouse event that caused the click and \a title is the plot title that received
+  the click.
+
+  \see titleDoubleClick
+*/
+
+/*! \fn void QCustomPlot::titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title)
+
+  This signal is emitted when a plot title is double clicked.
+
+  \a event is the mouse event that caused the click and \a title is the plot title that received
+  the click.
+
+  \see titleClick
+*/
+
+/*! \fn void QCustomPlot::selectionChangedByUser()
+
+  This signal is emitted after the user has changed the selection in the QCustomPlot, e.g. by
+  clicking. It is not emitted when the selection state of an object has changed programmatically by
+  a direct call to setSelected() on an object or by calling \ref deselectAll.
+
+  In addition to this signal, selectable objects also provide individual signals, for example
+  QCPAxis::selectionChanged or QCPAbstractPlottable::selectionChanged. Note that those signals are
+  emitted even if the selection state is changed programmatically.
+
+  See the documentation of \ref setInteractions for details about the selection mechanism.
+
+  \see selectedPlottables, selectedGraphs, selectedItems, selectedAxes, selectedLegends
+*/
+
+/*! \fn void QCustomPlot::beforeReplot()
+
+  This signal is emitted immediately before a replot takes place (caused by a call to the slot \ref
+  replot).
+
+  It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
+  replot synchronously, it won't cause an infinite recursion.
+
+  \see replot, afterReplot
+*/
+
+/*! \fn void QCustomPlot::afterReplot()
+
+  This signal is emitted immediately after a replot has taken place (caused by a call to the slot \ref
+  replot).
+
+  It is safe to mutually connect the replot slot with this signal on two QCustomPlots to make them
+  replot synchronously, it won't cause an infinite recursion.
+
+  \see replot, beforeReplot
+*/
+
+/* end of documentation of signals */
+/* start of documentation of public members */
+
+/*! \var QCPAxis *QCustomPlot::xAxis
+
+  A pointer to the primary x Axis (bottom) of the main axis rect of the plot.
+
+  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
+  yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
+  axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
+  layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
+  QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
+  default legend is removed due to manipulation of the layout system (e.g. by removing the main
+  axis rect), the corresponding pointers become 0.
+*/
+
+/*! \var QCPAxis *QCustomPlot::yAxis
+
+  A pointer to the primary y Axis (left) of the main axis rect of the plot.
+
+  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
+  yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
+  axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
+  layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
+  QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
+  default legend is removed due to manipulation of the layout system (e.g. by removing the main
+  axis rect), the corresponding pointers become 0.
+*/
+
+/*! \var QCPAxis *QCustomPlot::xAxis2
+
+  A pointer to the secondary x Axis (top) of the main axis rect of the plot. Secondary axes are
+  invisible by default. Use QCPAxis::setVisible to change this (or use \ref
+  QCPAxisRect::setupFullAxesBox).
+
+  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
+  yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
+  axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
+  layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
+  QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
+  default legend is removed due to manipulation of the layout system (e.g. by removing the main
+  axis rect), the corresponding pointers become 0.
+*/
+
+/*! \var QCPAxis *QCustomPlot::yAxis2
+
+  A pointer to the secondary y Axis (right) of the main axis rect of the plot. Secondary axes are
+  invisible by default. Use QCPAxis::setVisible to change this (or use \ref
+  QCPAxisRect::setupFullAxesBox).
+
+  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
+  yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
+  axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
+  layout system\endlink to add multiple axis rects or multiple axes to one side, use the \ref
+  QCPAxisRect::axis interface to access the new axes. If one of the four default axes or the
+  default legend is removed due to manipulation of the layout system (e.g. by removing the main
+  axis rect), the corresponding pointers become 0.
+*/
+
+/*! \var QCPLegend *QCustomPlot::legend
+
+  A pointer to the default legend of the main axis rect. The legend is invisible by default. Use
+  QCPLegend::setVisible to change this.
+
+  QCustomPlot offers convenient pointers to the axes (\ref xAxis, \ref yAxis, \ref xAxis2, \ref
+  yAxis2) and the \ref legend. They make it very easy working with plots that only have a single
+  axis rect and at most one axis at each axis rect side. If you use \link thelayoutsystem the
+  layout system\endlink to add multiple legends to the plot, use the layout system interface to
+  access the new legend. For example, legends can be placed inside an axis rect's \ref
+  QCPAxisRect::insetLayout "inset layout", and must then also be accessed via the inset layout. If
+  the default legend is removed due to manipulation of the layout system (e.g. by removing the main
+  axis rect), the corresponding pointer becomes 0.
+*/
+
+/* end of documentation of public members */
+
+/*!
+  Constructs a QCustomPlot and sets reasonable default values.
+*/
+QCustomPlot::QCustomPlot(QWidget *parent) :
+  QWidget(parent),
+  xAxis(0),
+  yAxis(0),
+  xAxis2(0),
+  yAxis2(0),
+  legend(0),
+  mPlotLayout(0),
+  mAutoAddPlottableToLegend(true),
+  mAntialiasedElements(QCP::aeNone),
+  mNotAntialiasedElements(QCP::aeNone),
+  mInteractions(0),
+  mSelectionTolerance(8),
+  mNoAntialiasingOnDrag(false),
+  mBackgroundBrush(Qt::white, Qt::SolidPattern),
+  mBackgroundScaled(true),
+  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
+  mCurrentLayer(0),
+  mPlottingHints(QCP::phCacheLabels|QCP::phForceRepaint),
+  mMultiSelectModifier(Qt::ControlModifier),
+  mPaintBuffer(size()),
+  mMouseEventElement(0),
+  mReplotting(false)
+{
+  setAttribute(Qt::WA_NoMousePropagation);
+  setAttribute(Qt::WA_OpaquePaintEvent);
+  setMouseTracking(true);
+  QLocale currentLocale = locale();
+  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
+  setLocale(currentLocale);
+
+  // create initial layers:
+  mLayers.append(new QCPLayer(this, QLatin1String("background")));
+  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
+  mLayers.append(new QCPLayer(this, QLatin1String("main")));
+  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
+  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
+  updateLayerIndices();
+  setCurrentLayer(QLatin1String("main"));
+
+  // create initial layout, axis rect and legend:
+  mPlotLayout = new QCPLayoutGrid;
+  mPlotLayout->initializeParentPlot(this);
+  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
+  mPlotLayout->setLayer(QLatin1String("main"));
+  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
+  mPlotLayout->addElement(0, 0, defaultAxisRect);
+  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
+  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
+  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
+  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
+  legend = new QCPLegend;
+  legend->setVisible(false);
+  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
+  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
+
+  defaultAxisRect->setLayer(QLatin1String("background"));
+  xAxis->setLayer(QLatin1String("axes"));
+  yAxis->setLayer(QLatin1String("axes"));
+  xAxis2->setLayer(QLatin1String("axes"));
+  yAxis2->setLayer(QLatin1String("axes"));
+  xAxis->grid()->setLayer(QLatin1String("grid"));
+  yAxis->grid()->setLayer(QLatin1String("grid"));
+  xAxis2->grid()->setLayer(QLatin1String("grid"));
+  yAxis2->grid()->setLayer(QLatin1String("grid"));
+  legend->setLayer(QLatin1String("legend"));
+
+  setViewport(rect()); // needs to be called after mPlotLayout has been created
+
+  replot();
+}
+
+QCustomPlot::~QCustomPlot()
+{
+  clearPlottables();
+  clearItems();
+
+  if (mPlotLayout)
+  {
+    delete mPlotLayout;
+    mPlotLayout = 0;
+  }
+
+  mCurrentLayer = 0;
+  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
+  mLayers.clear();
+}
+
+/*!
+  Sets which elements are forcibly drawn antialiased as an \a or combination of QCP::AntialiasedElement.
+
+  This overrides the antialiasing settings for whole element groups, normally controlled with the
+  \a setAntialiasing function on the individual elements. If an element is neither specified in
+  \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
+  each individual element instance is used.
+
+  For example, if \a antialiasedElements contains \ref QCP::aePlottables, all plottables will be
+  drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
+  to.
+
+  if an element in \a antialiasedElements is already set in \ref setNotAntialiasedElements, it is
+  removed from there.
+
+  \see setNotAntialiasedElements
+*/
+void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
+{
+  mAntialiasedElements = antialiasedElements;
+
+  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
+  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
+    mNotAntialiasedElements |= ~mAntialiasedElements;
+}
+
+/*!
+  Sets whether the specified \a antialiasedElement is forcibly drawn antialiased.
+
+  See \ref setAntialiasedElements for details.
+
+  \see setNotAntialiasedElement
+*/
+void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
+{
+  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
+    mAntialiasedElements &= ~antialiasedElement;
+  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
+    mAntialiasedElements |= antialiasedElement;
+
+  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
+  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
+    mNotAntialiasedElements |= ~mAntialiasedElements;
+}
+
+/*!
+  Sets which elements are forcibly drawn not antialiased as an \a or combination of
+  QCP::AntialiasedElement.
+
+  This overrides the antialiasing settings for whole element groups, normally controlled with the
+  \a setAntialiasing function on the individual elements. If an element is neither specified in
+  \ref setAntialiasedElements nor in \ref setNotAntialiasedElements, the antialiasing setting on
+  each individual element instance is used.
+
+  For example, if \a notAntialiasedElements contains \ref QCP::aePlottables, no plottables will be
+  drawn antialiased, no matter what the specific QCPAbstractPlottable::setAntialiased value was set
+  to.
+
+  if an element in \a notAntialiasedElements is already set in \ref setAntialiasedElements, it is
+  removed from there.
+
+  \see setAntialiasedElements
+*/
+void QCustomPlot::setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
+{
+  mNotAntialiasedElements = notAntialiasedElements;
+
+  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
+  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
+    mAntialiasedElements |= ~mNotAntialiasedElements;
+}
+
+/*!
+  Sets whether the specified \a notAntialiasedElement is forcibly drawn not antialiased.
+
+  See \ref setNotAntialiasedElements for details.
+
+  \see setAntialiasedElement
+*/
+void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
+{
+  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
+    mNotAntialiasedElements &= ~notAntialiasedElement;
+  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
+    mNotAntialiasedElements |= notAntialiasedElement;
+
+  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
+  if ((mNotAntialiasedElements & mAntialiasedElements) != 0)
+    mAntialiasedElements |= ~mNotAntialiasedElements;
+}
+
+/*!
+  If set to true, adding a plottable (e.g. a graph) to the QCustomPlot automatically also adds the
+  plottable to the legend (QCustomPlot::legend).
+
+  \see addPlottable, addGraph, QCPLegend::addItem
+*/
+void QCustomPlot::setAutoAddPlottableToLegend(bool on)
+{
+  mAutoAddPlottableToLegend = on;
+}
+
+/*!
+  Sets the possible interactions of this QCustomPlot as an or-combination of \ref QCP::Interaction
+  enums. There are the following types of interactions:
+
+  <b>Axis range manipulation</b> is controlled via \ref QCP::iRangeDrag and \ref QCP::iRangeZoom. When the
+  respective interaction is enabled, the user may drag axes ranges and zoom with the mouse wheel.
+  For details how to control which axes the user may drag/zoom and in what orientations, see \ref
+  QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeDragAxes,
+  \ref QCPAxisRect::setRangeZoomAxes.
+
+  <b>Plottable selection</b> is controlled by \ref QCP::iSelectPlottables. If \ref QCP::iSelectPlottables is
+  set, the user may select plottables (graphs, curves, bars,...) by clicking on them or in their
+  vicinity (\ref setSelectionTolerance). Whether the user can actually select a plottable can
+  further be restricted with the \ref QCPAbstractPlottable::setSelectable function on the specific
+  plottable. To find out whether a specific plottable is selected, call
+  QCPAbstractPlottable::selected(). To retrieve a list of all currently selected plottables, call
+  \ref selectedPlottables. If you're only interested in QCPGraphs, you may use the convenience
+  function \ref selectedGraphs.
+
+  <b>Item selection</b> is controlled by \ref QCP::iSelectItems. If \ref QCP::iSelectItems is set, the user
+  may select items (QCPItemLine, QCPItemText,...) by clicking on them or in their vicinity. To find
+  out whether a specific item is selected, call QCPAbstractItem::selected(). To retrieve a list of
+  all currently selected items, call \ref selectedItems.
+
+  <b>Axis selection</b> is controlled with \ref QCP::iSelectAxes. If \ref QCP::iSelectAxes is set, the user
+  may select parts of the axes by clicking on them. What parts exactly (e.g. Axis base line, tick
+  labels, axis label) are selectable can be controlled via \ref QCPAxis::setSelectableParts for
+  each axis. To retrieve a list of all axes that currently contain selected parts, call \ref
+  selectedAxes. Which parts of an axis are selected, can be retrieved with QCPAxis::selectedParts().
+
+  <b>Legend selection</b> is controlled with \ref QCP::iSelectLegend. If this is set, the user may
+  select the legend itself or individual items by clicking on them. What parts exactly are
+  selectable can be controlled via \ref QCPLegend::setSelectableParts. To find out whether the
+  legend or any of its child items are selected, check the value of QCPLegend::selectedParts. To
+  find out which child items are selected, call \ref QCPLegend::selectedItems.
+
+  <b>All other selectable elements</b> The selection of all other selectable objects (e.g.
+  QCPPlotTitle, or your own layerable subclasses) is controlled with \ref QCP::iSelectOther. If set, the
+  user may select those objects by clicking on them. To find out which are currently selected, you
+  need to check their selected state explicitly.
+
+  If the selection state has changed by user interaction, the \ref selectionChangedByUser signal is
+  emitted. Each selectable object additionally emits an individual selectionChanged signal whenever
+  their selection state has changed, i.e. not only by user interaction.
+
+  To allow multiple objects to be selected by holding the selection modifier (\ref
+  setMultiSelectModifier), set the flag \ref QCP::iMultiSelect.
+
+  \note In addition to the selection mechanism presented here, QCustomPlot always emits
+  corresponding signals, when an object is clicked or double clicked. see \ref plottableClick and
+  \ref plottableDoubleClick for example.
+
+  \see setInteraction, setSelectionTolerance
+*/
+void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
+{
+  mInteractions = interactions;
+}
+
+/*!
+  Sets the single \a interaction of this QCustomPlot to \a enabled.
+
+  For details about the interaction system, see \ref setInteractions.
+
+  \see setInteractions
+*/
+void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
+{
+  if (!enabled && mInteractions.testFlag(interaction))
+    mInteractions &= ~interaction;
+  else if (enabled && !mInteractions.testFlag(interaction))
+    mInteractions |= interaction;
+}
+
+/*!
+  Sets the tolerance that is used to decide whether a click selects an object (e.g. a plottable) or
+  not.
+
+  If the user clicks in the vicinity of the line of e.g. a QCPGraph, it's only regarded as a
+  potential selection when the minimum distance between the click position and the graph line is
+  smaller than \a pixels. Objects that are defined by an area (e.g. QCPBars) only react to clicks
+  directly inside the area and ignore this selection tolerance. In other words, it only has meaning
+  for parts of objects that are too thin to exactly hit with a click and thus need such a
+  tolerance.
+
+  \see setInteractions, QCPLayerable::selectTest
+*/
+void QCustomPlot::setSelectionTolerance(int pixels)
+{
+  mSelectionTolerance = pixels;
+}
+
+/*!
+  Sets whether antialiasing is disabled for this QCustomPlot while the user is dragging axes
+  ranges. If many objects, especially plottables, are drawn antialiased, this greatly improves
+  performance during dragging. Thus it creates a more responsive user experience. As soon as the
+  user stops dragging, the last replot is done with normal antialiasing, to restore high image
+  quality.
+
+  \see setAntialiasedElements, setNotAntialiasedElements
+*/
+void QCustomPlot::setNoAntialiasingOnDrag(bool enabled)
+{
+  mNoAntialiasingOnDrag = enabled;
+}
+
+/*!
+  Sets the plotting hints for this QCustomPlot instance as an \a or combination of QCP::PlottingHint.
+
+  \see setPlottingHint
+*/
+void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
+{
+  mPlottingHints = hints;
+}
+
+/*!
+  Sets the specified plotting \a hint to \a enabled.
+
+  \see setPlottingHints
+*/
+void QCustomPlot::setPlottingHint(QCP::PlottingHint hint, bool enabled)
+{
+  QCP::PlottingHints newHints = mPlottingHints;
+  if (!enabled)
+    newHints &= ~hint;
+  else
+    newHints |= hint;
+
+  if (newHints != mPlottingHints)
+    setPlottingHints(newHints);
+}
+
+/*!
+  Sets the keyboard modifier that will be recognized as multi-select-modifier.
+
+  If \ref QCP::iMultiSelect is specified in \ref setInteractions, the user may select multiple objects
+  by clicking on them one after the other while holding down \a modifier.
+
+  By default the multi-select-modifier is set to Qt::ControlModifier.
+
+  \see setInteractions
+*/
+void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
+{
+  mMultiSelectModifier = modifier;
+}
+
+/*!
+  Sets the viewport of this QCustomPlot. The Viewport is the area that the top level layout
+  (QCustomPlot::plotLayout()) uses as its rect. Normally, the viewport is the entire widget rect.
+
+  This function is used to allow arbitrary size exports with \ref toPixmap, \ref savePng, \ref
+  savePdf, etc. by temporarily changing the viewport size.
+*/
+void QCustomPlot::setViewport(const QRect &rect)
+{
+  mViewport = rect;
+  if (mPlotLayout)
+    mPlotLayout->setOuterRect(mViewport);
+}
+
+/*!
+  Sets \a pm as the viewport background pixmap (see \ref setViewport). The pixmap is always drawn
+  below all other objects in the plot.
+
+  For cases where the provided pixmap doesn't have the same size as the viewport, scaling can be
+  enabled with \ref setBackgroundScaled and the scaling mode (whether and how the aspect ratio is
+  preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
+  consider using the overloaded version of this function.
+
+  If a background brush was set with \ref setBackground(const QBrush &brush), the viewport will
+  first be filled with that brush, before drawing the background pixmap. This can be useful for
+  background pixmaps with translucent areas.
+
+  \see setBackgroundScaled, setBackgroundScaledMode
+*/
+void QCustomPlot::setBackground(const QPixmap &pm)
+{
+  mBackgroundPixmap = pm;
+  mScaledBackgroundPixmap = QPixmap();
+}
+
+/*!
+  Sets the background brush of the viewport (see \ref setViewport).
+
+  Before drawing everything else, the background is filled with \a brush. If a background pixmap
+  was set with \ref setBackground(const QPixmap &pm), this brush will be used to fill the viewport
+  before the background pixmap is drawn. This can be useful for background pixmaps with translucent
+  areas.
+
+  Set \a brush to Qt::NoBrush or Qt::Transparent to leave background transparent. This can be
+  useful for exporting to image formats which support transparency, e.g. \ref savePng.
+
+  \see setBackgroundScaled, setBackgroundScaledMode
+*/
+void QCustomPlot::setBackground(const QBrush &brush)
+{
+  mBackgroundBrush = brush;
+}
+
+/*! \overload
+
+  Allows setting the background pixmap of the viewport, whether it shall be scaled and how it
+  shall be scaled in one call.
+
+  \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
+*/
+void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
+{
+  mBackgroundPixmap = pm;
+  mScaledBackgroundPixmap = QPixmap();
+  mBackgroundScaled = scaled;
+  mBackgroundScaledMode = mode;
+}
+
+/*!
+  Sets whether the viewport background pixmap shall be scaled to fit the viewport. If \a scaled is
+  set to true, control whether and how the aspect ratio of the original pixmap is preserved with
+  \ref setBackgroundScaledMode.
+
+  Note that the scaled version of the original pixmap is buffered, so there is no performance
+  penalty on replots. (Except when the viewport dimensions are changed continuously.)
+
+  \see setBackground, setBackgroundScaledMode
+*/
+void QCustomPlot::setBackgroundScaled(bool scaled)
+{
+  mBackgroundScaled = scaled;
+}
+
+/*!
+  If scaling of the viewport background pixmap is enabled (\ref setBackgroundScaled), use this
+  function to define whether and how the aspect ratio of the original pixmap is preserved.
+
+  \see setBackground, setBackgroundScaled
+*/
+void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
+{
+  mBackgroundScaledMode = mode;
+}
+
+/*!
+  Returns the plottable with \a index. If the index is invalid, returns 0.
+
+  There is an overloaded version of this function with no parameter which returns the last added
+  plottable, see QCustomPlot::plottable()
+
+  \see plottableCount, addPlottable
+*/
+QCPAbstractPlottable *QCustomPlot::plottable(int index)
+{
+  if (index >= 0 && index < mPlottables.size())
+  {
+    return mPlottables.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*! \overload
+
+  Returns the last plottable that was added with \ref addPlottable. If there are no plottables in
+  the plot, returns 0.
+
+  \see plottableCount, addPlottable
+*/
+QCPAbstractPlottable *QCustomPlot::plottable()
+{
+  if (!mPlottables.isEmpty())
+  {
+    return mPlottables.last();
+  } else
+    return 0;
+}
+
+/*!
+  Adds the specified plottable to the plot and, if \ref setAutoAddPlottableToLegend is enabled, to
+  the legend (QCustomPlot::legend). QCustomPlot takes ownership of the plottable.
+
+  Returns true on success, i.e. when \a plottable isn't already in the plot and the parent plot of
+  \a plottable is this QCustomPlot (the latter is controlled by what axes were passed in the
+  plottable's constructor).
+
+  \see plottable, plottableCount, removePlottable, clearPlottables
+*/
+bool QCustomPlot::addPlottable(QCPAbstractPlottable *plottable)
+{
+  if (mPlottables.contains(plottable))
+  {
+    qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
+    return false;
+  }
+  if (plottable->parentPlot() != this)
+  {
+    qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
+    return false;
+  }
+
+  mPlottables.append(plottable);
+  // possibly add plottable to legend:
+  if (mAutoAddPlottableToLegend)
+    plottable->addToLegend();
+  // special handling for QCPGraphs to maintain the simple graph interface:
+  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
+    mGraphs.append(graph);
+  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
+    plottable->setLayer(currentLayer());
+  return true;
+}
+
+/*!
+  Removes the specified plottable from the plot and, if necessary, from the legend (QCustomPlot::legend).
+
+  Returns true on success.
+
+  \see addPlottable, clearPlottables
+*/
+bool QCustomPlot::removePlottable(QCPAbstractPlottable *plottable)
+{
+  if (!mPlottables.contains(plottable))
+  {
+    qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
+    return false;
+  }
+
+  // remove plottable from legend:
+  plottable->removeFromLegend();
+  // special handling for QCPGraphs to maintain the simple graph interface:
+  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
+    mGraphs.removeOne(graph);
+  // remove plottable:
+  delete plottable;
+  mPlottables.removeOne(plottable);
+  return true;
+}
+
+/*! \overload
+
+  Removes the plottable by its \a index.
+*/
+bool QCustomPlot::removePlottable(int index)
+{
+  if (index >= 0 && index < mPlottables.size())
+    return removePlottable(mPlottables[index]);
+  else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return false;
+  }
+}
+
+/*!
+  Removes all plottables from the plot (and the QCustomPlot::legend, if necessary).
+
+  Returns the number of plottables removed.
+
+  \see removePlottable
+*/
+int QCustomPlot::clearPlottables()
+{
+  int c = mPlottables.size();
+  for (int i=c-1; i >= 0; --i)
+    removePlottable(mPlottables[i]);
+  return c;
+}
+
+/*!
+  Returns the number of currently existing plottables in the plot
+
+  \see plottable, addPlottable
+*/
+int QCustomPlot::plottableCount() const
+{
+  return mPlottables.size();
+}
+
+/*!
+  Returns a list of the selected plottables. If no plottables are currently selected, the list is empty.
+
+  There is a convenience function if you're only interested in selected graphs, see \ref selectedGraphs.
+
+  \see setInteractions, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
+*/
+QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
+{
+  QList<QCPAbstractPlottable*> result;
+  foreach (QCPAbstractPlottable *plottable, mPlottables)
+  {
+    if (plottable->selected())
+      result.append(plottable);
+  }
+  return result;
+}
+
+/*!
+  Returns the plottable at the pixel position \a pos. Plottables that only consist of single lines
+  (like graphs) have a tolerance band around them, see \ref setSelectionTolerance. If multiple
+  plottables come into consideration, the one closest to \a pos is returned.
+
+  If \a onlySelectable is true, only plottables that are selectable
+  (QCPAbstractPlottable::setSelectable) are considered.
+
+  If there is no plottable at \a pos, the return value is 0.
+
+  \see itemAt, layoutElementAt
+*/
+QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
+{
+  QCPAbstractPlottable *resultPlottable = 0;
+  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
+
+  foreach (QCPAbstractPlottable *plottable, mPlottables)
+  {
+    if (onlySelectable && !plottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable
+      continue;
+    if ((plottable->keyAxis()->axisRect()->rect() & plottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
+    {
+      double currentDistance = plottable->selectTest(pos, false);
+      if (currentDistance >= 0 && currentDistance < resultDistance)
+      {
+        resultPlottable = plottable;
+        resultDistance = currentDistance;
+      }
+    }
+  }
+
+  return resultPlottable;
+}
+
+/*!
+  Returns whether this QCustomPlot instance contains the \a plottable.
+
+  \see addPlottable
+*/
+bool QCustomPlot::hasPlottable(QCPAbstractPlottable *plottable) const
+{
+  return mPlottables.contains(plottable);
+}
+
+/*!
+  Returns the graph with \a index. If the index is invalid, returns 0.
+
+  There is an overloaded version of this function with no parameter which returns the last created
+  graph, see QCustomPlot::graph()
+
+  \see graphCount, addGraph
+*/
+QCPGraph *QCustomPlot::graph(int index) const
+{
+  if (index >= 0 && index < mGraphs.size())
+  {
+    return mGraphs.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*! \overload
+
+  Returns the last graph, that was created with \ref addGraph. If there are no graphs in the plot,
+  returns 0.
+
+  \see graphCount, addGraph
+*/
+QCPGraph *QCustomPlot::graph() const
+{
+  if (!mGraphs.isEmpty())
+  {
+    return mGraphs.last();
+  } else
+    return 0;
+}
+
+/*!
+  Creates a new graph inside the plot. If \a keyAxis and \a valueAxis are left unspecified (0), the
+  bottom (xAxis) is used as key and the left (yAxis) is used as value axis. If specified, \a
+  keyAxis and \a valueAxis must reside in this QCustomPlot.
+
+  \a keyAxis will be used as key axis (typically "x") and \a valueAxis as value axis (typically
+  "y") for the graph.
+
+  Returns a pointer to the newly created graph, or 0 if adding the graph failed.
+
+  \see graph, graphCount, removeGraph, clearGraphs
+*/
+QCPGraph *QCustomPlot::addGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
+{
+  if (!keyAxis) keyAxis = xAxis;
+  if (!valueAxis) valueAxis = yAxis;
+  if (!keyAxis || !valueAxis)
+  {
+    qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
+    return 0;
+  }
+  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
+  {
+    qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
+    return 0;
+  }
+
+  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
+  if (addPlottable(newGraph))
+  {
+    newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
+    return newGraph;
+  } else
+  {
+    delete newGraph;
+    return 0;
+  }
+}
+
+/*!
+  Removes the specified \a graph from the plot and, if necessary, from the QCustomPlot::legend. If
+  any other graphs in the plot have a channel fill set towards the removed graph, the channel fill
+  property of those graphs is reset to zero (no channel fill).
+
+  Returns true on success.
+
+  \see clearGraphs
+*/
+bool QCustomPlot::removeGraph(QCPGraph *graph)
+{
+  return removePlottable(graph);
+}
+
+/*! \overload
+
+  Removes the graph by its \a index.
+*/
+bool QCustomPlot::removeGraph(int index)
+{
+  if (index >= 0 && index < mGraphs.size())
+    return removeGraph(mGraphs[index]);
+  else
+    return false;
+}
+
+/*!
+  Removes all graphs from the plot (and the QCustomPlot::legend, if necessary).
+
+  Returns the number of graphs removed.
+
+  \see removeGraph
+*/
+int QCustomPlot::clearGraphs()
+{
+  int c = mGraphs.size();
+  for (int i=c-1; i >= 0; --i)
+    removeGraph(mGraphs[i]);
+  return c;
+}
+
+/*!
+  Returns the number of currently existing graphs in the plot
+
+  \see graph, addGraph
+*/
+int QCustomPlot::graphCount() const
+{
+  return mGraphs.size();
+}
+
+/*!
+  Returns a list of the selected graphs. If no graphs are currently selected, the list is empty.
+
+  If you are not only interested in selected graphs but other plottables like QCPCurve, QCPBars,
+  etc., use \ref selectedPlottables.
+
+  \see setInteractions, selectedPlottables, QCPAbstractPlottable::setSelectable, QCPAbstractPlottable::setSelected
+*/
+QList<QCPGraph*> QCustomPlot::selectedGraphs() const
+{
+  QList<QCPGraph*> result;
+  foreach (QCPGraph *graph, mGraphs)
+  {
+    if (graph->selected())
+      result.append(graph);
+  }
+  return result;
+}
+
+/*!
+  Returns the item with \a index. If the index is invalid, returns 0.
+
+  There is an overloaded version of this function with no parameter which returns the last added
+  item, see QCustomPlot::item()
+
+  \see itemCount, addItem
+*/
+QCPAbstractItem *QCustomPlot::item(int index) const
+{
+  if (index >= 0 && index < mItems.size())
+  {
+    return mItems.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*! \overload
+
+  Returns the last item, that was added with \ref addItem. If there are no items in the plot,
+  returns 0.
+
+  \see itemCount, addItem
+*/
+QCPAbstractItem *QCustomPlot::item() const
+{
+  if (!mItems.isEmpty())
+  {
+    return mItems.last();
+  } else
+    return 0;
+}
+
+/*!
+  Adds the specified item to the plot. QCustomPlot takes ownership of the item.
+
+  Returns true on success, i.e. when \a item wasn't already in the plot and the parent plot of \a
+  item is this QCustomPlot.
+
+  \see item, itemCount, removeItem, clearItems
+*/
+bool QCustomPlot::addItem(QCPAbstractItem *item)
+{
+  if (!mItems.contains(item) && item->parentPlot() == this)
+  {
+    mItems.append(item);
+    return true;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "item either already in list or not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
+    return false;
+  }
+}
+
+/*!
+  Removes the specified item from the plot.
+
+  Returns true on success.
+
+  \see addItem, clearItems
+*/
+bool QCustomPlot::removeItem(QCPAbstractItem *item)
+{
+  if (mItems.contains(item))
+  {
+    delete item;
+    mItems.removeOne(item);
+    return true;
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
+    return false;
+  }
+}
+
+/*! \overload
+
+  Removes the item by its \a index.
+*/
+bool QCustomPlot::removeItem(int index)
+{
+  if (index >= 0 && index < mItems.size())
+    return removeItem(mItems[index]);
+  else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return false;
+  }
+}
+
+/*!
+  Removes all items from the plot.
+
+  Returns the number of items removed.
+
+  \see removeItem
+*/
+int QCustomPlot::clearItems()
+{
+  int c = mItems.size();
+  for (int i=c-1; i >= 0; --i)
+    removeItem(mItems[i]);
+  return c;
+}
+
+/*!
+  Returns the number of currently existing items in the plot
+
+  \see item, addItem
+*/
+int QCustomPlot::itemCount() const
+{
+  return mItems.size();
+}
+
+/*!
+  Returns a list of the selected items. If no items are currently selected, the list is empty.
+
+  \see setInteractions, QCPAbstractItem::setSelectable, QCPAbstractItem::setSelected
+*/
+QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
+{
+  QList<QCPAbstractItem*> result;
+  foreach (QCPAbstractItem *item, mItems)
+  {
+    if (item->selected())
+      result.append(item);
+  }
+  return result;
+}
+
+/*!
+  Returns the item at the pixel position \a pos. Items that only consist of single lines (e.g. \ref
+  QCPItemLine or \ref QCPItemCurve) have a tolerance band around them, see \ref
+  setSelectionTolerance. If multiple items come into consideration, the one closest to \a pos is
+  returned.
+
+  If \a onlySelectable is true, only items that are selectable (QCPAbstractItem::setSelectable) are
+  considered.
+
+  If there is no item at \a pos, the return value is 0.
+
+  \see plottableAt, layoutElementAt
+*/
+QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
+{
+  QCPAbstractItem *resultItem = 0;
+  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
+
+  foreach (QCPAbstractItem *item, mItems)
+  {
+    if (onlySelectable && !item->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
+      continue;
+    if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
+    {
+      double currentDistance = item->selectTest(pos, false);
+      if (currentDistance >= 0 && currentDistance < resultDistance)
+      {
+        resultItem = item;
+        resultDistance = currentDistance;
+      }
+    }
+  }
+
+  return resultItem;
+}
+
+/*!
+  Returns whether this QCustomPlot contains the \a item.
+
+  \see addItem
+*/
+bool QCustomPlot::hasItem(QCPAbstractItem *item) const
+{
+  return mItems.contains(item);
+}
+
+/*!
+  Returns the layer with the specified \a name. If there is no layer with the specified name, 0 is
+  returned.
+
+  Layer names are case-sensitive.
+
+  \see addLayer, moveLayer, removeLayer
+*/
+QCPLayer *QCustomPlot::layer(const QString &name) const
+{
+  foreach (QCPLayer *layer, mLayers)
+  {
+    if (layer->name() == name)
+      return layer;
+  }
+  return 0;
+}
+
+/*! \overload
+
+  Returns the layer by \a index. If the index is invalid, 0 is returned.
+
+  \see addLayer, moveLayer, removeLayer
+*/
+QCPLayer *QCustomPlot::layer(int index) const
+{
+  if (index >= 0 && index < mLayers.size())
+  {
+    return mLayers.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*!
+  Returns the layer that is set as current layer (see \ref setCurrentLayer).
+*/
+QCPLayer *QCustomPlot::currentLayer() const
+{
+  return mCurrentLayer;
+}
+
+/*!
+  Sets the layer with the specified \a name to be the current layer. All layerables (\ref
+  QCPLayerable), e.g. plottables and items, are created on the current layer.
+
+  Returns true on success, i.e. if there is a layer with the specified \a name in the QCustomPlot.
+
+  Layer names are case-sensitive.
+
+  \see addLayer, moveLayer, removeLayer, QCPLayerable::setLayer
+*/
+bool QCustomPlot::setCurrentLayer(const QString &name)
+{
+  if (QCPLayer *newCurrentLayer = layer(name))
+  {
+    return setCurrentLayer(newCurrentLayer);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
+    return false;
+  }
+}
+
+/*! \overload
+
+  Sets the provided \a layer to be the current layer.
+
+  Returns true on success, i.e. when \a layer is a valid layer in the QCustomPlot.
+
+  \see addLayer, moveLayer, removeLayer
+*/
+bool QCustomPlot::setCurrentLayer(QCPLayer *layer)
+{
+  if (!mLayers.contains(layer))
+  {
+    qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
+    return false;
+  }
+
+  mCurrentLayer = layer;
+  return true;
+}
+
+/*!
+  Returns the number of currently existing layers in the plot
+
+  \see layer, addLayer
+*/
+int QCustomPlot::layerCount() const
+{
+  return mLayers.size();
+}
+
+/*!
+  Adds a new layer to this QCustomPlot instance. The new layer will have the name \a name, which
+  must be unique. Depending on \a insertMode, it is positioned either below or above \a otherLayer.
+
+  Returns true on success, i.e. if there is no other layer named \a name and \a otherLayer is a
+  valid layer inside this QCustomPlot.
+
+  If \a otherLayer is 0, the highest layer in the QCustomPlot will be used.
+
+  For an explanation of what layers are in QCustomPlot, see the documentation of \ref QCPLayer.
+
+  \see layer, moveLayer, removeLayer
+*/
+bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
+{
+  if (!otherLayer)
+    otherLayer = mLayers.last();
+  if (!mLayers.contains(otherLayer))
+  {
+    qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
+    return false;
+  }
+  if (layer(name))
+  {
+    qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
+    return false;
+  }
+
+  QCPLayer *newLayer = new QCPLayer(this, name);
+  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
+  updateLayerIndices();
+  return true;
+}
+
+/*!
+  Removes the specified \a layer and returns true on success.
+
+  All layerables (e.g. plottables and items) on the removed layer will be moved to the layer below
+  \a layer. If \a layer is the bottom layer, the layerables are moved to the layer above. In both
+  cases, the total rendering order of all layerables in the QCustomPlot is preserved.
+
+  If \a layer is the current layer (\ref setCurrentLayer), the layer below (or above, if bottom
+  layer) becomes the new current layer.
+
+  It is not possible to remove the last layer of the plot.
+
+  \see layer, addLayer, moveLayer
+*/
+bool QCustomPlot::removeLayer(QCPLayer *layer)
+{
+  if (!mLayers.contains(layer))
+  {
+    qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
+    return false;
+  }
+  if (mLayers.size() < 2)
+  {
+    qDebug() << Q_FUNC_INFO << "can't remove last layer";
+    return false;
+  }
+
+  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
+  int removedIndex = layer->index();
+  bool isFirstLayer = removedIndex==0;
+  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
+  QList<QCPLayerable*> children = layer->children();
+  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
+  {
+    for (int i=children.size()-1; i>=0; --i)
+      children.at(i)->moveToLayer(targetLayer, true);
+  } else  // append normally
+  {
+    for (int i=0; i<children.size(); ++i)
+      children.at(i)->moveToLayer(targetLayer, false);
+  }
+  // if removed layer is current layer, change current layer to layer below/above:
+  if (layer == mCurrentLayer)
+    setCurrentLayer(targetLayer);
+  // remove layer:
+  delete layer;
+  mLayers.removeOne(layer);
+  updateLayerIndices();
+  return true;
+}
+
+/*!
+  Moves the specified \a layer either above or below \a otherLayer. Whether it's placed above or
+  below is controlled with \a insertMode.
+
+  Returns true on success, i.e. when both \a layer and \a otherLayer are valid layers in the
+  QCustomPlot.
+
+  \see layer, addLayer, moveLayer
+*/
+bool QCustomPlot::moveLayer(QCPLayer *layer, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
+{
+  if (!mLayers.contains(layer))
+  {
+    qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
+    return false;
+  }
+  if (!mLayers.contains(otherLayer))
+  {
+    qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
+    return false;
+  }
+
+  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
+  updateLayerIndices();
+  return true;
+}
+
+/*!
+  Returns the number of axis rects in the plot.
+
+  All axis rects can be accessed via QCustomPlot::axisRect().
+
+  Initially, only one axis rect exists in the plot.
+
+  \see axisRect, axisRects
+*/
+int QCustomPlot::axisRectCount() const
+{
+  return axisRects().size();
+}
+
+/*!
+  Returns the axis rect with \a index.
+
+  Initially, only one axis rect (with index 0) exists in the plot. If multiple axis rects were
+  added, all of them may be accessed with this function in a linear fashion (even when they are
+  nested in a layout hierarchy or inside other axis rects via QCPAxisRect::insetLayout).
+
+  \see axisRectCount, axisRects
+*/
+QCPAxisRect *QCustomPlot::axisRect(int index) const
+{
+  const QList<QCPAxisRect*> rectList = axisRects();
+  if (index >= 0 && index < rectList.size())
+  {
+    return rectList.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
+    return 0;
+  }
+}
+
+/*!
+  Returns all axis rects in the plot.
+
+  \see axisRectCount, axisRect
+*/
+QList<QCPAxisRect*> QCustomPlot::axisRects() const
+{
+  QList<QCPAxisRect*> result;
+  QStack<QCPLayoutElement*> elementStack;
+  if (mPlotLayout)
+    elementStack.push(mPlotLayout);
+
+  while (!elementStack.isEmpty())
+  {
+    foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
+    {
+      if (element)
+      {
+        elementStack.push(element);
+        if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
+          result.append(ar);
+      }
+    }
+  }
+
+  return result;
+}
+
+/*!
+  Returns the layout element at pixel position \a pos. If there is no element at that position,
+  returns 0.
+
+  Only visible elements are used. If \ref QCPLayoutElement::setVisible on the element itself or on
+  any of its parent elements is set to false, it will not be considered.
+
+  \see itemAt, plottableAt
+*/
+QCPLayoutElement *QCustomPlot::layoutElementAt(const QPointF &pos) const
+{
+  QCPLayoutElement *currentElement = mPlotLayout;
+  bool searchSubElements = true;
+  while (searchSubElements && currentElement)
+  {
+    searchSubElements = false;
+    foreach (QCPLayoutElement *subElement, currentElement->elements(false))
+    {
+      if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
+      {
+        currentElement = subElement;
+        searchSubElements = true;
+        break;
+      }
+    }
+  }
+  return currentElement;
+}
+
+/*!
+  Returns the axes that currently have selected parts, i.e. whose selection state is not \ref
+  QCPAxis::spNone.
+
+  \see selectedPlottables, selectedLegends, setInteractions, QCPAxis::setSelectedParts,
+  QCPAxis::setSelectableParts
+*/
+QList<QCPAxis*> QCustomPlot::selectedAxes() const
+{
+  QList<QCPAxis*> result, allAxes;
+  foreach (QCPAxisRect *rect, axisRects())
+    allAxes << rect->axes();
+
+  foreach (QCPAxis *axis, allAxes)
+  {
+    if (axis->selectedParts() != QCPAxis::spNone)
+      result.append(axis);
+  }
+
+  return result;
+}
+
+/*!
+  Returns the legends that currently have selected parts, i.e. whose selection state is not \ref
+  QCPLegend::spNone.
+
+  \see selectedPlottables, selectedAxes, setInteractions, QCPLegend::setSelectedParts,
+  QCPLegend::setSelectableParts, QCPLegend::selectedItems
+*/
+QList<QCPLegend*> QCustomPlot::selectedLegends() const
+{
+  QList<QCPLegend*> result;
+
+  QStack<QCPLayoutElement*> elementStack;
+  if (mPlotLayout)
+    elementStack.push(mPlotLayout);
+
+  while (!elementStack.isEmpty())
+  {
+    foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
+    {
+      if (subElement)
+      {
+        elementStack.push(subElement);
+        if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
+        {
+          if (leg->selectedParts() != QCPLegend::spNone)
+            result.append(leg);
+        }
+      }
+    }
+  }
+
+  return result;
+}
+
+/*!
+  Deselects all layerables (plottables, items, axes, legends,...) of the QCustomPlot.
+
+  Since calling this function is not a user interaction, this does not emit the \ref
+  selectionChangedByUser signal. The individual selectionChanged signals are emitted though, if the
+  objects were previously selected.
+
+  \see setInteractions, selectedPlottables, selectedItems, selectedAxes, selectedLegends
+*/
+void QCustomPlot::deselectAll()
+{
+  foreach (QCPLayer *layer, mLayers)
+  {
+    foreach (QCPLayerable *layerable, layer->children())
+      layerable->deselectEvent(0);
+  }
+}
+
+/*!
+  Causes a complete replot into the internal buffer. Finally, update() is called, to redraw the
+  buffer on the QCustomPlot widget surface. This is the method that must be called to make changes,
+  for example on the axis ranges or data points of graphs, visible.
+
+  Under a few circumstances, QCustomPlot causes a replot by itself. Those are resize events of the
+  QCustomPlot widget and user interactions (object selection and range dragging/zooming).
+
+  Before the replot happens, the signal \ref beforeReplot is emitted. After the replot, \ref
+  afterReplot is emitted. It is safe to mutually connect the replot slot with any of those two
+  signals on two QCustomPlots to make them replot synchronously, it won't cause an infinite
+  recursion.
+*/
+void QCustomPlot::replot(QCustomPlot::RefreshPriority refreshPriority)
+{
+  if (mReplotting) // incase signals loop back to replot slot
+    return;
+  mReplotting = true;
+  emit beforeReplot();
+
+  mPaintBuffer.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent);
+  QCPPainter painter;
+  painter.begin(&mPaintBuffer);
+  if (painter.isActive())
+  {
+    painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
+    if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush)
+      painter.fillRect(mViewport, mBackgroundBrush);
+    draw(&painter);
+    painter.end();
+    if ((refreshPriority == rpHint && mPlottingHints.testFlag(QCP::phForceRepaint)) || refreshPriority==rpImmediate)
+      repaint();
+    else
+      update();
+  } else // might happen if QCustomPlot has width or height zero
+    qDebug() << Q_FUNC_INFO << "Couldn't activate painter on buffer. This usually happens because QCustomPlot has width or height zero.";
+
+  emit afterReplot();
+  mReplotting = false;
+}
+
+/*!
+  Rescales the axes such that all plottables (like graphs) in the plot are fully visible.
+
+  if \a onlyVisiblePlottables is set to true, only the plottables that have their visibility set to true
+  (QCPLayerable::setVisible), will be used to rescale the axes.
+
+  \see QCPAbstractPlottable::rescaleAxes, QCPAxis::rescale
+*/
+void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
+{
+  QList<QCPAxis*> allAxes;
+  foreach (QCPAxisRect *rect, axisRects())
+    allAxes << rect->axes();
+
+  foreach (QCPAxis *axis, allAxes)
+    axis->rescale(onlyVisiblePlottables);
+}
+
+/*!
+  Saves a PDF with the vectorized plot to the file \a fileName. The axis ratio as well as the scale
+  of texts and lines will be derived from the specified \a width and \a height. This means, the
+  output will look like the normal on-screen output of a QCustomPlot widget with the corresponding
+  pixel width and height. If either \a width or \a height is zero, the exported image will have the
+  same dimensions as the QCustomPlot widget currently has.
+
+  \a noCosmeticPen disables the use of cosmetic pens when drawing to the PDF file. Cosmetic pens
+  are pens with numerical width 0, which are always drawn as a one pixel wide line, no matter what
+  zoom factor is set in the PDF-Viewer. For more information about cosmetic pens, see the QPainter
+  and QPen documentation.
+
+  The objects of the plot will appear in the current selection state. If you don't want any
+  selected objects to be painted in their selected look, deselect everything with \ref deselectAll
+  before calling this function.
+
+  Returns true on success.
+
+  \warning
+  \li If you plan on editing the exported PDF file with a vector graphics editor like
+  Inkscape, it is advised to set \a noCosmeticPen to true to avoid losing those cosmetic lines
+  (which might be quite many, because cosmetic pens are the default for e.g. axes and tick marks).
+  \li If calling this function inside the constructor of the parent of the QCustomPlot widget
+  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
+  explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
+  function uses the current width and height of the QCustomPlot widget. However, in Qt, these
+  aren't defined yet inside the constructor, so you would get an image that has strange
+  widths/heights.
+
+  \a pdfCreator and \a pdfTitle may be used to set the according metadata fields in the resulting
+  PDF file.
+
+  \note On Android systems, this method does nothing and issues an according qDebug warning
+  message. This is also the case if for other reasons the define flag QT_NO_PRINTER is set.
+
+  \see savePng, saveBmp, saveJpg, saveRastered
+*/
+bool QCustomPlot::savePdf(const QString &fileName, bool noCosmeticPen, int width, int height, const QString &pdfCreator, const QString &pdfTitle)
+{
+  bool success = false;
+#ifdef QT_NO_PRINTER
+  Q_UNUSED(fileName)
+  Q_UNUSED(noCosmeticPen)
+  Q_UNUSED(width)
+  Q_UNUSED(height)
+  Q_UNUSED(pdfCreator)
+  Q_UNUSED(pdfTitle)
+  qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
+#else
+  int newWidth, newHeight;
+  if (width == 0 || height == 0)
+  {
+    newWidth = this->width();
+    newHeight = this->height();
+  } else
+  {
+    newWidth = width;
+    newHeight = height;
+  }
+
+  QPrinter printer(QPrinter::ScreenResolution);
+  printer.setOutputFileName(fileName);
+  printer.setOutputFormat(QPrinter::PdfFormat);
+  printer.setColorMode(QPrinter::Color);
+  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
+  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
+  QRect oldViewport = viewport();
+  setViewport(QRect(0, 0, newWidth, newHeight));
+#if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
+  printer.setFullPage(true);
+  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
+#else
+  QPageLayout pageLayout;
+  pageLayout.setMode(QPageLayout::FullPageMode);
+  pageLayout.setOrientation(QPageLayout::Portrait);
+  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
+  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
+  printer.setPageLayout(pageLayout);
+#endif
+  QCPPainter printpainter;
+  if (printpainter.begin(&printer))
+  {
+    printpainter.setMode(QCPPainter::pmVectorized);
+    printpainter.setMode(QCPPainter::pmNoCaching);
+    printpainter.setMode(QCPPainter::pmNonCosmetic, noCosmeticPen);
+    printpainter.setWindow(mViewport);
+    if (mBackgroundBrush.style() != Qt::NoBrush &&
+        mBackgroundBrush.color() != Qt::white &&
+        mBackgroundBrush.color() != Qt::transparent &&
+        mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
+      printpainter.fillRect(viewport(), mBackgroundBrush);
+    draw(&printpainter);
+    printpainter.end();
+    success = true;
+  }
+  setViewport(oldViewport);
+#endif // QT_NO_PRINTER
+  return success;
+}
+
+/*!
+  Saves a PNG image file to \a fileName on disc. The output plot will have the dimensions \a width
+  and \a height in pixels. If either \a width or \a height is zero, the exported image will have
+  the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
+  scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
+
+  For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
+  image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
+  texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
+  200*200 pixel resolution.
+
+  If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
+  temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
+  QCustomPlot to place objects with sub-pixel accuracy.
+
+  \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
+  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
+  explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
+  function uses the current width and height of the QCustomPlot widget. However, in Qt, these
+  aren't defined yet inside the constructor, so you would get an image that has strange
+  widths/heights.
+
+  The objects of the plot will appear in the current selection state. If you don't want any selected
+  objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
+  this function.
+
+  If you want the PNG to have a transparent background, call \ref setBackground(const QBrush
+  &brush) with no brush (Qt::NoBrush) or a transparent color (Qt::transparent), before saving.
+
+  PNG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
+  -1 to use the default setting.
+
+  Returns true on success. If this function fails, most likely the PNG format isn't supported by
+  the system, see Qt docs about QImageWriter::supportedImageFormats().
+
+  \see savePdf, saveBmp, saveJpg, saveRastered
+*/
+bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality)
+{
+  return saveRastered(fileName, width, height, scale, "PNG", quality);
+}
+
+/*!
+  Saves a JPG image file to \a fileName on disc. The output plot will have the dimensions \a width
+  and \a height in pixels. If either \a width or \a height is zero, the exported image will have
+  the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
+  scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
+
+  For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
+  image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
+  texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
+  200*200 pixel resolution.
+
+  If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
+  temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
+  QCustomPlot to place objects with sub-pixel accuracy.
+
+  \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
+  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
+  explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
+  function uses the current width and height of the QCustomPlot widget. However, in Qt, these
+  aren't defined yet inside the constructor, so you would get an image that has strange
+  widths/heights.
+
+  The objects of the plot will appear in the current selection state. If you don't want any selected
+  objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
+  this function.
+
+  JPG compression can be controlled with the \a quality parameter which must be between 0 and 100 or
+  -1 to use the default setting.
+
+  Returns true on success. If this function fails, most likely the JPG format isn't supported by
+  the system, see Qt docs about QImageWriter::supportedImageFormats().
+
+  \see savePdf, savePng, saveBmp, saveRastered
+*/
+bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality)
+{
+  return saveRastered(fileName, width, height, scale, "JPG", quality);
+}
+
+/*!
+  Saves a BMP image file to \a fileName on disc. The output plot will have the dimensions \a width
+  and \a height in pixels. If either \a width or \a height is zero, the exported image will have
+  the same dimensions as the QCustomPlot widget currently has. Line widths and texts etc. are not
+  scaled up when larger widths/heights are used. If you want that effect, use the \a scale parameter.
+
+  For example, if you set both \a width and \a height to 100 and \a scale to 2, you will end up with an
+  image file of size 200*200 in which all graphical elements are scaled up by factor 2 (line widths,
+  texts, etc.). This scaling is not done by stretching a 100*100 image, the result will have full
+  200*200 pixel resolution.
+
+  If you use a high scaling factor, it is recommended to enable antialiasing for all elements via
+  temporarily setting \ref QCustomPlot::setAntialiasedElements to \ref QCP::aeAll as this allows
+  QCustomPlot to place objects with sub-pixel accuracy.
+
+  \warning If calling this function inside the constructor of the parent of the QCustomPlot widget
+  (i.e. the MainWindow constructor, if QCustomPlot is inside the MainWindow), always provide
+  explicit non-zero widths and heights. If you leave \a width or \a height as 0 (default), this
+  function uses the current width and height of the QCustomPlot widget. However, in Qt, these
+  aren't defined yet inside the constructor, so you would get an image that has strange
+  widths/heights.
+
+  The objects of the plot will appear in the current selection state. If you don't want any selected
+  objects to be painted in their selected look, deselect everything with \ref deselectAll before calling
+  this function.
+
+  Returns true on success. If this function fails, most likely the BMP format isn't supported by
+  the system, see Qt docs about QImageWriter::supportedImageFormats().
+
+  \see savePdf, savePng, saveJpg, saveRastered
+*/
+bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale)
+{
+  return saveRastered(fileName, width, height, scale, "BMP");
+}
+
+/*! \internal
+
+  Returns a minimum size hint that corresponds to the minimum size of the top level layout
+  (\ref plotLayout). To prevent QCustomPlot from being collapsed to size/width zero, set a minimum
+  size (setMinimumSize) either on the whole QCustomPlot or on any layout elements inside the plot.
+  This is especially important, when placed in a QLayout where other components try to take in as
+  much space as possible (e.g. QMdiArea).
+*/
+QSize QCustomPlot::minimumSizeHint() const
+{
+  return mPlotLayout->minimumSizeHint();
+}
+
+/*! \internal
+
+  Returns a size hint that is the same as \ref minimumSizeHint.
+
+*/
+QSize QCustomPlot::sizeHint() const
+{
+  return mPlotLayout->minimumSizeHint();
+}
+
+/*! \internal
+
+  Event handler for when the QCustomPlot widget needs repainting. This does not cause a \ref replot, but
+  draws the internal buffer on the widget surface.
+*/
+void QCustomPlot::paintEvent(QPaintEvent *event)
+{
+  Q_UNUSED(event);
+  QPainter painter(this);
+  painter.drawPixmap(0, 0, mPaintBuffer);
+}
+
+/*! \internal
+
+  Event handler for a resize of the QCustomPlot widget. Causes the internal buffer to be resized to
+  the new size. The viewport (which becomes the outer rect of mPlotLayout) is resized
+  appropriately. Finally a \ref replot is performed.
+*/
+void QCustomPlot::resizeEvent(QResizeEvent *event)
+{
+  // resize and repaint the buffer:
+  mPaintBuffer = QPixmap(event->size());
+  setViewport(rect());
+  replot(rpQueued); // queued update is important here, to prevent painting issues in some contexts
+}
+
+/*! \internal
+
+ Event handler for when a double click occurs. Emits the \ref mouseDoubleClick signal, then emits
+ the specialized signals when certain objecs are clicked (e.g. \ref plottableDoubleClick, \ref
+ axisDoubleClick, etc.). Finally determines the affected layout element and forwards the event to
+ it.
+
+ \see mousePressEvent, mouseReleaseEvent
+*/
+void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
+{
+  emit mouseDoubleClick(event);
+
+  QVariant details;
+  QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details);
+
+  // emit specialized object double click signals:
+  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
+    emit plottableDoubleClick(ap, event);
+  else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
+    emit axisDoubleClick(ax, details.value<QCPAxis::SelectablePart>(), event);
+  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
+    emit itemDoubleClick(ai, event);
+  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
+    emit legendDoubleClick(lg, 0, event);
+  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
+    emit legendDoubleClick(li->parentLegend(), li, event);
+  else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
+    emit titleDoubleClick(event, pt);
+
+  // call double click event of affected layout element:
+  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
+    el->mouseDoubleClickEvent(event);
+
+  // call release event of affected layout element (as in mouseReleaseEvent, since the mouseDoubleClick replaces the second release event in double click case):
+  if (mMouseEventElement)
+  {
+    mMouseEventElement->mouseReleaseEvent(event);
+    mMouseEventElement = 0;
+  }
+
+  //QWidget::mouseDoubleClickEvent(event); don't call base class implementation because it would just cause a mousePress/ReleaseEvent, which we don't want.
+}
+
+/*! \internal
+
+  Event handler for when a mouse button is pressed. Emits the mousePress signal. Then determines
+  the affected layout element and forwards the event to it.
+
+  \see mouseMoveEvent, mouseReleaseEvent
+*/
+void QCustomPlot::mousePressEvent(QMouseEvent *event)
+{
+  emit mousePress(event);
+  mMousePressPos = event->pos(); // need this to determine in releaseEvent whether it was a click (no position change between press and release)
+
+  // call event of affected layout element:
+  mMouseEventElement = layoutElementAt(event->pos());
+  if (mMouseEventElement)
+    mMouseEventElement->mousePressEvent(event);
+
+  QWidget::mousePressEvent(event);
+}
+
+/*! \internal
+
+  Event handler for when the cursor is moved. Emits the \ref mouseMove signal.
+
+  If a layout element has mouse capture focus (a mousePressEvent happened on top of the layout
+  element before), the mouseMoveEvent is forwarded to that element.
+
+  \see mousePressEvent, mouseReleaseEvent
+*/
+void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
+{
+  emit mouseMove(event);
+
+  // call event of affected layout element:
+  if (mMouseEventElement)
+    mMouseEventElement->mouseMoveEvent(event);
+
+  QWidget::mouseMoveEvent(event);
+}
+
+/*! \internal
+
+  Event handler for when a mouse button is released. Emits the \ref mouseRelease signal.
+
+  If the mouse was moved less than a certain threshold in any direction since the \ref
+  mousePressEvent, it is considered a click which causes the selection mechanism (if activated via
+  \ref setInteractions) to possibly change selection states accordingly. Further, specialized mouse
+  click signals are emitted (e.g. \ref plottableClick, \ref axisClick, etc.)
+
+  If a layout element has mouse capture focus (a \ref mousePressEvent happened on top of the layout
+  element before), the \ref mouseReleaseEvent is forwarded to that element.
+
+  \see mousePressEvent, mouseMoveEvent
+*/
+void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
+{
+  emit mouseRelease(event);
+  bool doReplot = false;
+
+  if ((mMousePressPos-event->pos()).manhattanLength() < 5) // determine whether it was a click operation
+  {
+    if (event->button() == Qt::LeftButton)
+    {
+      // handle selection mechanism:
+      QVariant details;
+      QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
+      bool selectionStateChanged = false;
+      bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
+      // deselect all other layerables if not additive selection:
+      if (!additive)
+      {
+        foreach (QCPLayer *layer, mLayers)
+        {
+          foreach (QCPLayerable *layerable, layer->children())
+          {
+            if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
+            {
+              bool selChanged = false;
+              layerable->deselectEvent(&selChanged);
+              selectionStateChanged |= selChanged;
+            }
+          }
+        }
+      }
+      if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
+      {
+        // a layerable was actually clicked, call its selectEvent:
+        bool selChanged = false;
+        clickedLayerable->selectEvent(event, additive, details, &selChanged);
+        selectionStateChanged |= selChanged;
+      }
+      if (selectionStateChanged)
+      {
+        doReplot = true;
+        emit selectionChangedByUser();
+      }
+    }
+
+    // emit specialized object click signals:
+    QVariant details;
+    QCPLayerable *clickedLayerable = layerableAt(event->pos(), false, &details); // for these signals, selectability is ignored, that's why we call this again with onlySelectable set to false
+    if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(clickedLayerable))
+      emit plottableClick(ap, event);
+    else if (QCPAxis *ax = qobject_cast<QCPAxis*>(clickedLayerable))
+      emit axisClick(ax, details.value<QCPAxis::SelectablePart>(), event);
+    else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(clickedLayerable))
+      emit itemClick(ai, event);
+    else if (QCPLegend *lg = qobject_cast<QCPLegend*>(clickedLayerable))
+      emit legendClick(lg, 0, event);
+    else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(clickedLayerable))
+      emit legendClick(li->parentLegend(), li, event);
+    else if (QCPPlotTitle *pt = qobject_cast<QCPPlotTitle*>(clickedLayerable))
+      emit titleClick(event, pt);
+  }
+
+  // call event of affected layout element:
+  if (mMouseEventElement)
+  {
+    mMouseEventElement->mouseReleaseEvent(event);
+    mMouseEventElement = 0;
+  }
+
+  if (doReplot || noAntialiasingOnDrag())
+    replot();
+
+  QWidget::mouseReleaseEvent(event);
+}
+
+/*! \internal
+
+  Event handler for mouse wheel events. First, the \ref mouseWheel signal is emitted. Then
+  determines the affected layout element and forwards the event to it.
+
+*/
+void QCustomPlot::wheelEvent(QWheelEvent *event)
+{
+  emit mouseWheel(event);
+
+  // call event of affected layout element:
+  if (QCPLayoutElement *el = layoutElementAt(event->pos()))
+    el->wheelEvent(event);
+
+  QWidget::wheelEvent(event);
+}
+
+/*! \internal
+
+  This is the main draw function. It draws the entire plot, including background pixmap, with the
+  specified \a painter. Note that it does not fill the background with the background brush (as the
+  user may specify with \ref setBackground(const QBrush &brush)), this is up to the respective
+  functions calling this method (e.g. \ref replot, \ref toPixmap and \ref toPainter).
+*/
+void QCustomPlot::draw(QCPPainter *painter)
+{
+  // run through layout phases:
+  mPlotLayout->update(QCPLayoutElement::upPreparation);
+  mPlotLayout->update(QCPLayoutElement::upMargins);
+  mPlotLayout->update(QCPLayoutElement::upLayout);
+
+  // draw viewport background pixmap:
+  drawBackground(painter);
+
+  // draw all layered objects (grid, axes, plottables, items, legend,...):
+  foreach (QCPLayer *layer, mLayers)
+  {
+    foreach (QCPLayerable *child, layer->children())
+    {
+      if (child->realVisibility())
+      {
+        painter->save();
+        painter->setClipRect(child->clipRect().translated(0, -1));
+        child->applyDefaultAntialiasingHint(painter);
+        child->draw(painter);
+        painter->restore();
+      }
+    }
+  }
+
+  /* Debug code to draw all layout element rects
+  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
+  {
+    painter->setBrush(Qt::NoBrush);
+    painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
+    painter->drawRect(el->rect());
+    painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
+    painter->drawRect(el->outerRect());
+  }
+  */
+}
+
+/*! \internal
+
+  Draws the viewport background pixmap of the plot.
+
+  If a pixmap was provided via \ref setBackground, this function buffers the scaled version
+  depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
+  the viewport with the provided \a painter. The scaled version is buffered in
+  mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
+  the axis rect has changed in a way that requires a rescale of the background pixmap (this is
+  dependent on the \ref setBackgroundScaledMode), or when a differend axis background pixmap was
+  set.
+
+  Note that this function does not draw a fill with the background brush (\ref setBackground(const
+  QBrush &brush)) beneath the pixmap.
+
+  \see setBackground, setBackgroundScaled, setBackgroundScaledMode
+*/
+void QCustomPlot::drawBackground(QCPPainter *painter)
+{
+  // Note: background color is handled in individual replot/save functions
+
+  // draw background pixmap (on top of fill, if brush specified):
+  if (!mBackgroundPixmap.isNull())
+  {
+    if (mBackgroundScaled)
+    {
+      // check whether mScaledBackground needs to be updated:
+      QSize scaledSize(mBackgroundPixmap.size());
+      scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
+      if (mScaledBackgroundPixmap.size() != scaledSize)
+        mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
+      painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
+    } else
+    {
+      painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
+    }
+  }
+}
+
+
+/*! \internal
+
+  This method is used by \ref QCPAxisRect::removeAxis to report removed axes to the QCustomPlot
+  so it may clear its QCustomPlot::xAxis, yAxis, xAxis2 and yAxis2 members accordingly.
+*/
+void QCustomPlot::axisRemoved(QCPAxis *axis)
+{
+  if (xAxis == axis)
+    xAxis = 0;
+  if (xAxis2 == axis)
+    xAxis2 = 0;
+  if (yAxis == axis)
+    yAxis = 0;
+  if (yAxis2 == axis)
+    yAxis2 = 0;
+
+  // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
+}
+
+/*! \internal
+
+  This method is used by the QCPLegend destructor to report legend removal to the QCustomPlot so
+  it may clear its QCustomPlot::legend member accordingly.
+*/
+void QCustomPlot::legendRemoved(QCPLegend *legend)
+{
+  if (this->legend == legend)
+    this->legend = 0;
+}
+
+/*! \internal
+
+  Assigns all layers their index (QCPLayer::mIndex) in the mLayers list. This method is thus called
+  after every operation that changes the layer indices, like layer removal, layer creation, layer
+  moving.
+*/
+void QCustomPlot::updateLayerIndices() const
+{
+  for (int i=0; i<mLayers.size(); ++i)
+    mLayers.at(i)->mIndex = i;
+}
+
+/*! \internal
+
+  Returns the layerable at pixel position \a pos. If \a onlySelectable is set to true, only those
+  layerables that are selectable will be considered. (Layerable subclasses communicate their
+  selectability via the QCPLayerable::selectTest method, by returning -1.)
+
+  \a selectionDetails is an output parameter that contains selection specifics of the affected
+  layerable. This is useful if the respective layerable shall be given a subsequent
+  QCPLayerable::selectEvent (like in \ref mouseReleaseEvent). \a selectionDetails usually contains
+  information about which part of the layerable was hit, in multi-part layerables (e.g.
+  QCPAxis::SelectablePart).
+*/
+QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
+{
+  for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
+  {
+    const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
+    double minimumDistance = selectionTolerance()*1.1;
+    QCPLayerable *minimumDistanceLayerable = 0;
+    for (int i=layerables.size()-1; i>=0; --i)
+    {
+      if (!layerables.at(i)->realVisibility())
+        continue;
+      QVariant details;
+      double dist = layerables.at(i)->selectTest(pos, onlySelectable, &details);
+      if (dist >= 0 && dist < minimumDistance)
+      {
+        minimumDistance = dist;
+        minimumDistanceLayerable = layerables.at(i);
+        if (selectionDetails) *selectionDetails = details;
+      }
+    }
+    if (minimumDistance < selectionTolerance())
+      return minimumDistanceLayerable;
+  }
+  return 0;
+}
+
+/*!
+  Saves the plot to a rastered image file \a fileName in the image format \a format. The plot is
+  sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and scale 2.0 lead
+  to a full resolution file with width 200.) If the \a format supports compression, \a quality may
+  be between 0 and 100 to control it.
+
+  Returns true on success. If this function fails, most likely the given \a format isn't supported
+  by the system, see Qt docs about QImageWriter::supportedImageFormats().
+
+  \see saveBmp, saveJpg, savePng, savePdf
+*/
+bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality)
+{
+  QPixmap buffer = toPixmap(width, height, scale);
+  if (!buffer.isNull())
+    return buffer.save(fileName, format, quality);
+  else
+    return false;
+}
+
+/*!
+  Renders the plot to a pixmap and returns it.
+
+  The plot is sized to \a width and \a height in pixels and scaled with \a scale. (width 100 and
+  scale 2.0 lead to a full resolution pixmap with width 200.)
+
+  \see toPainter, saveRastered, saveBmp, savePng, saveJpg, savePdf
+*/
+QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
+{
+  // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
+  int newWidth, newHeight;
+  if (width == 0 || height == 0)
+  {
+    newWidth = this->width();
+    newHeight = this->height();
+  } else
+  {
+    newWidth = width;
+    newHeight = height;
+  }
+  int scaledWidth = qRound(scale*newWidth);
+  int scaledHeight = qRound(scale*newHeight);
+
+  QPixmap result(scaledWidth, scaledHeight);
+  result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
+  QCPPainter painter;
+  painter.begin(&result);
+  if (painter.isActive())
+  {
+    QRect oldViewport = viewport();
+    setViewport(QRect(0, 0, newWidth, newHeight));
+    painter.setMode(QCPPainter::pmNoCaching);
+    if (!qFuzzyCompare(scale, 1.0))
+    {
+      if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
+        painter.setMode(QCPPainter::pmNonCosmetic);
+      painter.scale(scale, scale);
+    }
+    if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill
+      painter.fillRect(mViewport, mBackgroundBrush);
+    draw(&painter);
+    setViewport(oldViewport);
+    painter.end();
+  } else // might happen if pixmap has width or height zero
+  {
+    qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
+    return QPixmap();
+  }
+  return result;
+}
+
+/*!
+  Renders the plot using the passed \a painter.
+
+  The plot is sized to \a width and \a height in pixels. If the \a painter's scale is not 1.0, the resulting plot will
+  appear scaled accordingly.
+
+  \note If you are restricted to using a QPainter (instead of QCPPainter), create a temporary QPicture and open a QCPPainter
+  on it. Then call \ref toPainter with this QCPPainter. After ending the paint operation on the picture, draw it with
+  the QPainter. This will reproduce the painter actions the QCPPainter took, with a QPainter.
+
+  \see toPixmap
+*/
+void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
+{
+  // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
+  int newWidth, newHeight;
+  if (width == 0 || height == 0)
+  {
+    newWidth = this->width();
+    newHeight = this->height();
+  } else
+  {
+    newWidth = width;
+    newHeight = height;
+  }
+
+  if (painter->isActive())
+  {
+    QRect oldViewport = viewport();
+    setViewport(QRect(0, 0, newWidth, newHeight));
+    painter->setMode(QCPPainter::pmNoCaching);
+    if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here
+      painter->fillRect(mViewport, mBackgroundBrush);
+    draw(painter);
+    setViewport(oldViewport);
+  } else
+    qDebug() << Q_FUNC_INFO << "Passed painter is not active";
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPColorGradient
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPColorGradient
+  \brief Defines a color gradient for use with e.g. \ref QCPColorMap
+
+  This class describes a color gradient which can be used to encode data with color. For example,
+  QCPColorMap and QCPColorScale have \ref QCPColorMap::setGradient "setGradient" methods which
+  take an instance of this class. Colors are set with \ref setColorStopAt(double position, const QColor &color)
+  with a \a position from 0 to 1. In between these defined color positions, the
+  color will be interpolated linearly either in RGB or HSV space, see \ref setColorInterpolation.
+
+  Alternatively, load one of the preset color gradients shown in the image below, with \ref
+  loadPreset, or by directly specifying the preset in the constructor.
+
+  \image html QCPColorGradient.png
+
+  The fact that the \ref QCPColorGradient(GradientPreset preset) constructor allows directly
+  converting a \ref GradientPreset to a QCPColorGradient, you can also directly pass \ref
+  GradientPreset to all the \a setGradient methods, e.g.:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorgradient-setgradient
+
+  The total number of levels used in the gradient can be set with \ref setLevelCount. Whether the
+  color gradient shall be applied periodically (wrapping around) to data values that lie outside
+  the data range specified on the plottable instance can be controlled with \ref setPeriodic.
+*/
+
+/*!
+  Constructs a new QCPColorGradient initialized with the colors and color interpolation according
+  to \a preset.
+
+  The color level count is initialized to 350.
+*/
+QCPColorGradient::QCPColorGradient(GradientPreset preset) :
+  mLevelCount(350),
+  mColorInterpolation(ciRGB),
+  mPeriodic(false),
+  mColorBufferInvalidated(true)
+{
+  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
+  loadPreset(preset);
+}
+
+/* undocumented operator */
+bool QCPColorGradient::operator==(const QCPColorGradient &other) const
+{
+  return ((other.mLevelCount == this->mLevelCount) &&
+          (other.mColorInterpolation == this->mColorInterpolation) &&
+          (other.mPeriodic == this->mPeriodic) &&
+          (other.mColorStops == this->mColorStops));
+}
+
+/*!
+  Sets the number of discretization levels of the color gradient to \a n. The default is 350 which
+  is typically enough to create a smooth appearance.
+
+  \image html QCPColorGradient-levelcount.png
+*/
+void QCPColorGradient::setLevelCount(int n)
+{
+  if (n < 2)
+  {
+    qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
+    n = 2;
+  }
+  if (n != mLevelCount)
+  {
+    mLevelCount = n;
+    mColorBufferInvalidated = true;
+  }
+}
+
+/*!
+  Sets at which positions from 0 to 1 which color shall occur. The positions are the keys, the
+  colors are the values of the passed QMap \a colorStops. In between these color stops, the color
+  is interpolated according to \ref setColorInterpolation.
+
+  A more convenient way to create a custom gradient may be to clear all color stops with \ref
+  clearColorStops and then adding them one by one with \ref setColorStopAt.
+
+  \see clearColorStops
+*/
+void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
+{
+  mColorStops = colorStops;
+  mColorBufferInvalidated = true;
+}
+
+/*!
+  Sets the \a color the gradient will have at the specified \a position (from 0 to 1). In between
+  these color stops, the color is interpolated according to \ref setColorInterpolation.
+
+  \see setColorStops, clearColorStops
+*/
+void QCPColorGradient::setColorStopAt(double position, const QColor &color)
+{
+  mColorStops.insert(position, color);
+  mColorBufferInvalidated = true;
+}
+
+/*!
+  Sets whether the colors in between the configured color stops (see \ref setColorStopAt) shall be
+  interpolated linearly in RGB or in HSV color space.
+
+  For example, a sweep in RGB space from red to green will have a muddy brown intermediate color,
+  whereas in HSV space the intermediate color is yellow.
+*/
+void QCPColorGradient::setColorInterpolation(QCPColorGradient::ColorInterpolation interpolation)
+{
+  if (interpolation != mColorInterpolation)
+  {
+    mColorInterpolation = interpolation;
+    mColorBufferInvalidated = true;
+  }
+}
+
+/*!
+  Sets whether data points that are outside the configured data range (e.g. \ref
+  QCPColorMap::setDataRange) are colored by periodically repeating the color gradient or whether
+  they all have the same color, corresponding to the respective gradient boundary color.
+
+  \image html QCPColorGradient-periodic.png
+
+  As shown in the image above, gradients that have the same start and end color are especially
+  suitable for a periodic gradient mapping, since they produce smooth color transitions throughout
+  the color map. A preset that has this property is \ref gpHues.
+
+  In practice, using periodic color gradients makes sense when the data corresponds to a periodic
+  dimension, such as an angle or a phase. If this is not the case, the color encoding might become
+  ambiguous, because multiple different data values are shown as the same color.
+*/
+void QCPColorGradient::setPeriodic(bool enabled)
+{
+  mPeriodic = enabled;
+}
+
+/*!
+  This method is used to quickly convert a \a data array to colors. The colors will be output in
+  the array \a scanLine. Both \a data and \a scanLine must have the length \a n when passed to this
+  function. The data range that shall be used for mapping the data value to the gradient is passed
+  in \a range. \a logarithmic indicates whether the data values shall be mapped to colors
+  logarithmically.
+
+  if \a data actually contains 2D-data linearized via <tt>[row*columnCount + column]</tt>, you can
+  set \a dataIndexFactor to <tt>columnCount</tt> to convert a column instead of a row of the data
+  array, in \a scanLine. \a scanLine will remain a regular (1D) array. This works because \a data
+  is addressed <tt>data[i*dataIndexFactor]</tt>.
+*/
+void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
+{
+  // If you change something here, make sure to also adapt ::color()
+  if (!data)
+  {
+    qDebug() << Q_FUNC_INFO << "null pointer given as data";
+    return;
+  }
+  if (!scanLine)
+  {
+    qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
+    return;
+  }
+  if (mColorBufferInvalidated)
+    updateColorBuffer();
+
+  if (!logarithmic)
+  {
+    const double posToIndexFactor = (mLevelCount-1)/range.size();
+    if (mPeriodic)
+    {
+      for (int i=0; i<n; ++i)
+      {
+        int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
+        if (index < 0)
+          index += mLevelCount;
+        scanLine[i] = mColorBuffer.at(index);
+      }
+    } else
+    {
+      for (int i=0; i<n; ++i)
+      {
+        int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
+        if (index < 0)
+          index = 0;
+        else if (index >= mLevelCount)
+          index = mLevelCount-1;
+        scanLine[i] = mColorBuffer.at(index);
+      }
+    }
+  } else // logarithmic == true
+  {
+    if (mPeriodic)
+    {
+      for (int i=0; i<n; ++i)
+      {
+        int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
+        if (index < 0)
+          index += mLevelCount;
+        scanLine[i] = mColorBuffer.at(index);
+      }
+    } else
+    {
+      for (int i=0; i<n; ++i)
+      {
+        int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
+        if (index < 0)
+          index = 0;
+        else if (index >= mLevelCount)
+          index = mLevelCount-1;
+        scanLine[i] = mColorBuffer.at(index);
+      }
+    }
+  }
+}
+
+/*! \internal
+
+  This method is used to colorize a single data value given in \a position, to colors. The data
+  range that shall be used for mapping the data value to the gradient is passed in \a range. \a
+  logarithmic indicates whether the data value shall be mapped to a color logarithmically.
+
+  If an entire array of data values shall be converted, rather use \ref colorize, for better
+  performance.
+*/
+QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
+{
+  // If you change something here, make sure to also adapt ::colorize()
+  if (mColorBufferInvalidated)
+    updateColorBuffer();
+  int index = 0;
+  if (!logarithmic)
+    index = (position-range.lower)*(mLevelCount-1)/range.size();
+  else
+    index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
+  if (mPeriodic)
+  {
+    index = index % mLevelCount;
+    if (index < 0)
+      index += mLevelCount;
+  } else
+  {
+    if (index < 0)
+      index = 0;
+    else if (index >= mLevelCount)
+      index = mLevelCount-1;
+  }
+  return mColorBuffer.at(index);
+}
+
+/*!
+  Clears the current color stops and loads the specified \a preset. A preset consists of predefined
+  color stops and the corresponding color interpolation method.
+
+  The available presets are:
+  \image html QCPColorGradient.png
+*/
+void QCPColorGradient::loadPreset(GradientPreset preset)
+{
+  clearColorStops();
+  switch (preset)
+  {
+    case gpGrayscale:
+      setColorInterpolation(ciRGB);
+      setColorStopAt(0, Qt::black);
+      setColorStopAt(1, Qt::white);
+      break;
+    case gpHot:
+      setColorInterpolation(ciRGB);
+      setColorStopAt(0, QColor(50, 0, 0));
+      setColorStopAt(0.2, QColor(180, 10, 0));
+      setColorStopAt(0.4, QColor(245, 50, 0));
+      setColorStopAt(0.6, QColor(255, 150, 10));
+      setColorStopAt(0.8, QColor(255, 255, 50));
+      setColorStopAt(1, QColor(255, 255, 255));
+      break;
+    case gpCold:
+      setColorInterpolation(ciRGB);
+      setColorStopAt(0, QColor(0, 0, 50));
+      setColorStopAt(0.2, QColor(0, 10, 180));
+      setColorStopAt(0.4, QColor(0, 50, 245));
+      setColorStopAt(0.6, QColor(10, 150, 255));
+      setColorStopAt(0.8, QColor(50, 255, 255));
+      setColorStopAt(1, QColor(255, 255, 255));
+      break;
+    case gpNight:
+      setColorInterpolation(ciHSV);
+      setColorStopAt(0, QColor(10, 20, 30));
+      setColorStopAt(1, QColor(250, 255, 250));
+      break;
+    case gpCandy:
+      setColorInterpolation(ciHSV);
+      setColorStopAt(0, QColor(0, 0, 255));
+      setColorStopAt(1, QColor(255, 250, 250));
+      break;
+    case gpGeography:
+      setColorInterpolation(ciRGB);
+      setColorStopAt(0, QColor(70, 170, 210));
+      setColorStopAt(0.20, QColor(90, 160, 180));
+      setColorStopAt(0.25, QColor(45, 130, 175));
+      setColorStopAt(0.30, QColor(100, 140, 125));
+      setColorStopAt(0.5, QColor(100, 140, 100));
+      setColorStopAt(0.6, QColor(130, 145, 120));
+      setColorStopAt(0.7, QColor(140, 130, 120));
+      setColorStopAt(0.9, QColor(180, 190, 190));
+      setColorStopAt(1, QColor(210, 210, 230));
+      break;
+    case gpIon:
+      setColorInterpolation(ciHSV);
+      setColorStopAt(0, QColor(50, 10, 10));
+      setColorStopAt(0.45, QColor(0, 0, 255));
+      setColorStopAt(0.8, QColor(0, 255, 255));
+      setColorStopAt(1, QColor(0, 255, 0));
+      break;
+    case gpThermal:
+      setColorInterpolation(ciRGB);
+      setColorStopAt(0, QColor(0, 0, 50));
+      setColorStopAt(0.15, QColor(20, 0, 120));
+      setColorStopAt(0.33, QColor(200, 30, 140));
+      setColorStopAt(0.6, QColor(255, 100, 0));
+      setColorStopAt(0.85, QColor(255, 255, 40));
+      setColorStopAt(1, QColor(255, 255, 255));
+      break;
+    case gpPolar:
+      setColorInterpolation(ciRGB);
+      setColorStopAt(0, QColor(50, 255, 255));
+      setColorStopAt(0.18, QColor(10, 70, 255));
+      setColorStopAt(0.28, QColor(10, 10, 190));
+      setColorStopAt(0.5, QColor(0, 0, 0));
+      setColorStopAt(0.72, QColor(190, 10, 10));
+      setColorStopAt(0.82, QColor(255, 70, 10));
+      setColorStopAt(1, QColor(255, 255, 50));
+      break;
+    case gpSpectrum:
+      setColorInterpolation(ciHSV);
+      setColorStopAt(0, QColor(50, 0, 50));
+      setColorStopAt(0.15, QColor(0, 0, 255));
+      setColorStopAt(0.35, QColor(0, 255, 255));
+      setColorStopAt(0.6, QColor(255, 255, 0));
+      setColorStopAt(0.75, QColor(255, 30, 0));
+      setColorStopAt(1, QColor(50, 0, 0));
+      break;
+    case gpJet:
+      setColorInterpolation(ciRGB);
+      setColorStopAt(0, QColor(0, 0, 100));
+      setColorStopAt(0.15, QColor(0, 50, 255));
+      setColorStopAt(0.35, QColor(0, 255, 255));
+      setColorStopAt(0.65, QColor(255, 255, 0));
+      setColorStopAt(0.85, QColor(255, 30, 0));
+      setColorStopAt(1, QColor(100, 0, 0));
+      break;
+    case gpHues:
+      setColorInterpolation(ciHSV);
+      setColorStopAt(0, QColor(255, 0, 0));
+      setColorStopAt(1.0/3.0, QColor(0, 0, 255));
+      setColorStopAt(2.0/3.0, QColor(0, 255, 0));
+      setColorStopAt(1, QColor(255, 0, 0));
+      break;
+  }
+}
+
+/*!
+  Clears all color stops.
+
+  \see setColorStops, setColorStopAt
+*/
+void QCPColorGradient::clearColorStops()
+{
+  mColorStops.clear();
+  mColorBufferInvalidated = true;
+}
+
+/*!
+  Returns an inverted gradient. The inverted gradient has all properties as this \ref
+  QCPColorGradient, but the order of the color stops is inverted.
+
+  \see setColorStops, setColorStopAt
+*/
+QCPColorGradient QCPColorGradient::inverted() const
+{
+  QCPColorGradient result(*this);
+  result.clearColorStops();
+  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
+    result.setColorStopAt(1.0-it.key(), it.value());
+  return result;
+}
+
+/*! \internal
+
+  Updates the internal color buffer which will be used by \ref colorize and \ref color, to quickly
+  convert positions to colors. This is where the interpolation between color stops is calculated.
+*/
+void QCPColorGradient::updateColorBuffer()
+{
+  if (mColorBuffer.size() != mLevelCount)
+    mColorBuffer.resize(mLevelCount);
+  if (mColorStops.size() > 1)
+  {
+    double indexToPosFactor = 1.0/(double)(mLevelCount-1);
+    for (int i=0; i<mLevelCount; ++i)
+    {
+      double position = i*indexToPosFactor;
+      QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
+      if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
+      {
+        mColorBuffer[i] = (it-1).value().rgb();
+      } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
+      {
+        mColorBuffer[i] = it.value().rgb();
+      } else // position is in between stops (or on an intermediate stop), interpolate color
+      {
+        QMap<double, QColor>::const_iterator high = it;
+        QMap<double, QColor>::const_iterator low = it-1;
+        double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
+        switch (mColorInterpolation)
+        {
+          case ciRGB:
+          {
+            mColorBuffer[i] = qRgb((1-t)*low.value().red() + t*high.value().red(),
+                                   (1-t)*low.value().green() + t*high.value().green(),
+                                   (1-t)*low.value().blue() + t*high.value().blue());
+            break;
+          }
+          case ciHSV:
+          {
+            QColor lowHsv = low.value().toHsv();
+            QColor highHsv = high.value().toHsv();
+            double hue = 0;
+            double hueDiff = highHsv.hueF()-lowHsv.hueF();
+            if (hueDiff > 0.5)
+              hue = lowHsv.hueF() - t*(1.0-hueDiff);
+            else if (hueDiff < -0.5)
+              hue = lowHsv.hueF() + t*(1.0+hueDiff);
+            else
+              hue = lowHsv.hueF() + t*hueDiff;
+            if (hue < 0) hue += 1.0;
+            else if (hue >= 1.0) hue -= 1.0;
+            mColorBuffer[i] = QColor::fromHsvF(hue, (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(), (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
+            break;
+          }
+        }
+      }
+    }
+  } else if (mColorStops.size() == 1)
+  {
+    mColorBuffer.fill(mColorStops.constBegin().value().rgb());
+  } else // mColorStops is empty, fill color buffer with black
+  {
+    mColorBuffer.fill(qRgb(0, 0, 0));
+  }
+  mColorBufferInvalidated = false;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPAxisRect
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPAxisRect
+  \brief Holds multiple axes and arranges them in a rectangular shape.
+
+  This class represents an axis rect, a rectangular area that is bounded on all sides with an
+  arbitrary number of axes.
+
+  Initially QCustomPlot has one axis rect, accessible via QCustomPlot::axisRect(). However, the
+  layout system allows to have multiple axis rects, e.g. arranged in a grid layout
+  (QCustomPlot::plotLayout).
+
+  By default, QCPAxisRect comes with four axes, at bottom, top, left and right. They can be
+  accessed via \ref axis by providing the respective axis type (\ref QCPAxis::AxisType) and index.
+  If you need all axes in the axis rect, use \ref axes. The top and right axes are set to be
+  invisible initially (QCPAxis::setVisible). To add more axes to a side, use \ref addAxis or \ref
+  addAxes. To remove an axis, use \ref removeAxis.
+
+  The axis rect layerable itself only draws a background pixmap or color, if specified (\ref
+  setBackground). It is placed on the "background" layer initially (see \ref QCPLayer for an
+  explanation of the QCustomPlot layer system). The axes that are held by the axis rect can be
+  placed on other layers, independently of the axis rect.
+
+  Every axis rect has a child layout of type \ref QCPLayoutInset. It is accessible via \ref
+  insetLayout and can be used to have other layout elements (or even other layouts with multiple
+  elements) hovering inside the axis rect.
+
+  If an axis rect is clicked and dragged, it processes this by moving certain axis ranges. The
+  behaviour can be controlled with \ref setRangeDrag and \ref setRangeDragAxes. If the mouse wheel
+  is scrolled while the cursor is on the axis rect, certain axes are scaled. This is controllable
+  via \ref setRangeZoom, \ref setRangeZoomAxes and \ref setRangeZoomFactor. These interactions are
+  only enabled if \ref QCustomPlot::setInteractions contains \ref QCP::iRangeDrag and \ref
+  QCP::iRangeZoom.
+
+  \image html AxisRectSpacingOverview.png
+  <center>Overview of the spacings and paddings that define the geometry of an axis. The dashed
+  line on the far left indicates the viewport/widget border.</center>
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QCPLayoutInset *QCPAxisRect::insetLayout() const
+
+  Returns the inset layout of this axis rect. It can be used to place other layout elements (or
+  even layouts with multiple other elements) inside/on top of an axis rect.
+
+  \see QCPLayoutInset
+*/
+
+/*! \fn int QCPAxisRect::left() const
+
+  Returns the pixel position of the left border of this axis rect. Margins are not taken into
+  account here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPAxisRect::right() const
+
+  Returns the pixel position of the right border of this axis rect. Margins are not taken into
+  account here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPAxisRect::top() const
+
+  Returns the pixel position of the top border of this axis rect. Margins are not taken into
+  account here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPAxisRect::bottom() const
+
+  Returns the pixel position of the bottom border of this axis rect. Margins are not taken into
+  account here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPAxisRect::width() const
+
+  Returns the pixel width of this axis rect. Margins are not taken into account here, so the
+  returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn int QCPAxisRect::height() const
+
+  Returns the pixel height of this axis rect. Margins are not taken into account here, so the
+  returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QSize QCPAxisRect::size() const
+
+  Returns the pixel size of this axis rect. Margins are not taken into account here, so the
+  returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPAxisRect::topLeft() const
+
+  Returns the top left corner of this axis rect in pixels. Margins are not taken into account here,
+  so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPAxisRect::topRight() const
+
+  Returns the top right corner of this axis rect in pixels. Margins are not taken into account
+  here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPAxisRect::bottomLeft() const
+
+  Returns the bottom left corner of this axis rect in pixels. Margins are not taken into account
+  here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPAxisRect::bottomRight() const
+
+  Returns the bottom right corner of this axis rect in pixels. Margins are not taken into account
+  here, so the returned value is with respect to the inner \ref rect.
+*/
+
+/*! \fn QPoint QCPAxisRect::center() const
+
+  Returns the center of this axis rect in pixels. Margins are not taken into account here, so the
+  returned value is with respect to the inner \ref rect.
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Creates a QCPAxisRect instance and sets default values. An axis is added for each of the four
+  sides, the top and right axes are set invisible initially.
+*/
+QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
+  QCPLayoutElement(parentPlot),
+  mBackgroundBrush(Qt::NoBrush),
+  mBackgroundScaled(true),
+  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
+  mInsetLayout(new QCPLayoutInset),
+  mRangeDrag(Qt::Horizontal|Qt::Vertical),
+  mRangeZoom(Qt::Horizontal|Qt::Vertical),
+  mRangeZoomFactorHorz(0.85),
+  mRangeZoomFactorVert(0.85),
+  mDragging(false)
+{
+  mInsetLayout->initializeParentPlot(mParentPlot);
+  mInsetLayout->setParentLayerable(this);
+  mInsetLayout->setParent(this);
+
+  setMinimumSize(50, 50);
+  setMinimumMargins(QMargins(15, 15, 15, 15));
+  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
+  mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
+  mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
+  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
+
+  if (setupDefaultAxes)
+  {
+    QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
+    QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
+    QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
+    QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
+    setRangeDragAxes(xAxis, yAxis);
+    setRangeZoomAxes(xAxis, yAxis);
+    xAxis2->setVisible(false);
+    yAxis2->setVisible(false);
+    xAxis->grid()->setVisible(true);
+    yAxis->grid()->setVisible(true);
+    xAxis2->grid()->setVisible(false);
+    yAxis2->grid()->setVisible(false);
+    xAxis2->grid()->setZeroLinePen(Qt::NoPen);
+    yAxis2->grid()->setZeroLinePen(Qt::NoPen);
+    xAxis2->grid()->setVisible(false);
+    yAxis2->grid()->setVisible(false);
+  }
+}
+
+QCPAxisRect::~QCPAxisRect()
+{
+  delete mInsetLayout;
+  mInsetLayout = 0;
+
+  QList<QCPAxis*> axesList = axes();
+  for (int i=0; i<axesList.size(); ++i)
+    removeAxis(axesList.at(i));
+}
+
+/*!
+  Returns the number of axes on the axis rect side specified with \a type.
+
+  \see axis
+*/
+int QCPAxisRect::axisCount(QCPAxis::AxisType type) const
+{
+  return mAxes.value(type).size();
+}
+
+/*!
+  Returns the axis with the given \a index on the axis rect side specified with \a type.
+
+  \see axisCount, axes
+*/
+QCPAxis *QCPAxisRect::axis(QCPAxis::AxisType type, int index) const
+{
+  QList<QCPAxis*> ax(mAxes.value(type));
+  if (index >= 0 && index < ax.size())
+  {
+    return ax.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*!
+  Returns all axes on the axis rect sides specified with \a types.
+
+  \a types may be a single \ref QCPAxis::AxisType or an <tt>or</tt>-combination, to get the axes of
+  multiple sides.
+
+  \see axis
+*/
+QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
+{
+  QList<QCPAxis*> result;
+  if (types.testFlag(QCPAxis::atLeft))
+    result << mAxes.value(QCPAxis::atLeft);
+  if (types.testFlag(QCPAxis::atRight))
+    result << mAxes.value(QCPAxis::atRight);
+  if (types.testFlag(QCPAxis::atTop))
+    result << mAxes.value(QCPAxis::atTop);
+  if (types.testFlag(QCPAxis::atBottom))
+    result << mAxes.value(QCPAxis::atBottom);
+  return result;
+}
+
+/*! \overload
+
+  Returns all axes of this axis rect.
+*/
+QList<QCPAxis*> QCPAxisRect::axes() const
+{
+  QList<QCPAxis*> result;
+  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
+  while (it.hasNext())
+  {
+    it.next();
+    result << it.value();
+  }
+  return result;
+}
+
+/*!
+  Adds a new axis to the axis rect side specified with \a type, and returns it. If \a axis is 0, a
+  new QCPAxis instance is created internally.
+
+  You may inject QCPAxis instances (or sublasses of QCPAxis) by setting \a axis to an axis that was
+  previously created outside QCustomPlot. It is important to note that QCustomPlot takes ownership
+  of the axis, so you may not delete it afterwards. Further, the \a axis must have been created
+  with this axis rect as parent and with the same axis type as specified in \a type. If this is not
+  the case, a debug output is generated, the axis is not added, and the method returns 0.
+
+  This method can not be used to move \a axis between axis rects. The same \a axis instance must
+  not be added multiple times to the same or different axis rects.
+
+  If an axis rect side already contains one or more axes, the lower and upper endings of the new
+  axis (\ref QCPAxis::setLowerEnding, \ref QCPAxis::setUpperEnding) are set to \ref
+  QCPLineEnding::esHalfBar.
+
+  \see addAxes, setupFullAxesBox
+*/
+QCPAxis *QCPAxisRect::addAxis(QCPAxis::AxisType type, QCPAxis *axis)
+{
+  QCPAxis *newAxis = axis;
+  if (!newAxis)
+  {
+    newAxis = new QCPAxis(this, type);
+  } else // user provided existing axis instance, do some sanity checks
+  {
+    if (newAxis->axisType() != type)
+    {
+      qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
+      return 0;
+    }
+    if (newAxis->axisRect() != this)
+    {
+      qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
+      return 0;
+    }
+    if (axes().contains(newAxis))
+    {
+      qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
+      return 0;
+    }
+  }
+  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
+  {
+    bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
+    newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
+    newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
+  }
+  mAxes[type].append(newAxis);
+  return newAxis;
+}
+
+/*!
+  Adds a new axis with \ref addAxis to each axis rect side specified in \a types. This may be an
+  <tt>or</tt>-combination of QCPAxis::AxisType, so axes can be added to multiple sides at once.
+
+  Returns a list of the added axes.
+
+  \see addAxis, setupFullAxesBox
+*/
+QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
+{
+  QList<QCPAxis*> result;
+  if (types.testFlag(QCPAxis::atLeft))
+    result << addAxis(QCPAxis::atLeft);
+  if (types.testFlag(QCPAxis::atRight))
+    result << addAxis(QCPAxis::atRight);
+  if (types.testFlag(QCPAxis::atTop))
+    result << addAxis(QCPAxis::atTop);
+  if (types.testFlag(QCPAxis::atBottom))
+    result << addAxis(QCPAxis::atBottom);
+  return result;
+}
+
+/*!
+  Removes the specified \a axis from the axis rect and deletes it.
+
+  Returns true on success, i.e. if \a axis was a valid axis in this axis rect.
+
+  \see addAxis
+*/
+bool QCPAxisRect::removeAxis(QCPAxis *axis)
+{
+  // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
+  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
+  while (it.hasNext())
+  {
+    it.next();
+    if (it.value().contains(axis))
+    {
+      mAxes[it.key()].removeOne(axis);
+      if (qobject_cast<QCustomPlot*>(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot)
+        parentPlot()->axisRemoved(axis);
+      delete axis;
+      return true;
+    }
+  }
+  qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
+  return false;
+}
+
+/*!
+  Convenience function to create an axis on each side that doesn't have any axes yet and set their
+  visibility to true. Further, the top/right axes are assigned the following properties of the
+  bottom/left axes:
+
+  \li range (\ref QCPAxis::setRange)
+  \li range reversed (\ref QCPAxis::setRangeReversed)
+  \li scale type (\ref QCPAxis::setScaleType)
+  \li scale log base  (\ref QCPAxis::setScaleLogBase)
+  \li ticks (\ref QCPAxis::setTicks)
+  \li auto (major) tick count (\ref QCPAxis::setAutoTickCount)
+  \li sub tick count (\ref QCPAxis::setSubTickCount)
+  \li auto sub ticks (\ref QCPAxis::setAutoSubTicks)
+  \li tick step (\ref QCPAxis::setTickStep)
+  \li auto tick step (\ref QCPAxis::setAutoTickStep)
+  \li number format (\ref QCPAxis::setNumberFormat)
+  \li number precision (\ref QCPAxis::setNumberPrecision)
+  \li tick label type (\ref QCPAxis::setTickLabelType)
+  \li date time format (\ref QCPAxis::setDateTimeFormat)
+  \li date time spec (\ref QCPAxis::setDateTimeSpec)
+
+  Tick labels (\ref QCPAxis::setTickLabels) of the right and top axes are set to false.
+
+  If \a connectRanges is true, the \ref QCPAxis::rangeChanged "rangeChanged" signals of the bottom
+  and left axes are connected to the \ref QCPAxis::setRange slots of the top and right axes.
+*/
+void QCPAxisRect::setupFullAxesBox(bool connectRanges)
+{
+  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
+  if (axisCount(QCPAxis::atBottom) == 0)
+    xAxis = addAxis(QCPAxis::atBottom);
+  else
+    xAxis = axis(QCPAxis::atBottom);
+
+  if (axisCount(QCPAxis::atLeft) == 0)
+    yAxis = addAxis(QCPAxis::atLeft);
+  else
+    yAxis = axis(QCPAxis::atLeft);
+
+  if (axisCount(QCPAxis::atTop) == 0)
+    xAxis2 = addAxis(QCPAxis::atTop);
+  else
+    xAxis2 = axis(QCPAxis::atTop);
+
+  if (axisCount(QCPAxis::atRight) == 0)
+    yAxis2 = addAxis(QCPAxis::atRight);
+  else
+    yAxis2 = axis(QCPAxis::atRight);
+
+  xAxis->setVisible(true);
+  yAxis->setVisible(true);
+  xAxis2->setVisible(true);
+  yAxis2->setVisible(true);
+  xAxis2->setTickLabels(false);
+  yAxis2->setTickLabels(false);
+
+  xAxis2->setRange(xAxis->range());
+  xAxis2->setRangeReversed(xAxis->rangeReversed());
+  xAxis2->setScaleType(xAxis->scaleType());
+  xAxis2->setScaleLogBase(xAxis->scaleLogBase());
+  xAxis2->setTicks(xAxis->ticks());
+  xAxis2->setAutoTickCount(xAxis->autoTickCount());
+  xAxis2->setSubTickCount(xAxis->subTickCount());
+  xAxis2->setAutoSubTicks(xAxis->autoSubTicks());
+  xAxis2->setTickStep(xAxis->tickStep());
+  xAxis2->setAutoTickStep(xAxis->autoTickStep());
+  xAxis2->setNumberFormat(xAxis->numberFormat());
+  xAxis2->setNumberPrecision(xAxis->numberPrecision());
+  xAxis2->setTickLabelType(xAxis->tickLabelType());
+  xAxis2->setDateTimeFormat(xAxis->dateTimeFormat());
+  xAxis2->setDateTimeSpec(xAxis->dateTimeSpec());
+
+  yAxis2->setRange(yAxis->range());
+  yAxis2->setRangeReversed(yAxis->rangeReversed());
+  yAxis2->setScaleType(yAxis->scaleType());
+  yAxis2->setScaleLogBase(yAxis->scaleLogBase());
+  yAxis2->setTicks(yAxis->ticks());
+  yAxis2->setAutoTickCount(yAxis->autoTickCount());
+  yAxis2->setSubTickCount(yAxis->subTickCount());
+  yAxis2->setAutoSubTicks(yAxis->autoSubTicks());
+  yAxis2->setTickStep(yAxis->tickStep());
+  yAxis2->setAutoTickStep(yAxis->autoTickStep());
+  yAxis2->setNumberFormat(yAxis->numberFormat());
+  yAxis2->setNumberPrecision(yAxis->numberPrecision());
+  yAxis2->setTickLabelType(yAxis->tickLabelType());
+  yAxis2->setDateTimeFormat(yAxis->dateTimeFormat());
+  yAxis2->setDateTimeSpec(yAxis->dateTimeSpec());
+
+  if (connectRanges)
+  {
+    connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
+    connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
+  }
+}
+
+/*!
+  Returns a list of all the plottables that are associated with this axis rect.
+
+  A plottable is considered associated with an axis rect if its key or value axis (or both) is in
+  this axis rect.
+
+  \see graphs, items
+*/
+QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
+{
+  // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
+  QList<QCPAbstractPlottable*> result;
+  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
+  {
+    if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this ||mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
+      result.append(mParentPlot->mPlottables.at(i));
+  }
+  return result;
+}
+
+/*!
+  Returns a list of all the graphs that are associated with this axis rect.
+
+  A graph is considered associated with an axis rect if its key or value axis (or both) is in
+  this axis rect.
+
+  \see plottables, items
+*/
+QList<QCPGraph*> QCPAxisRect::graphs() const
+{
+  // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
+  QList<QCPGraph*> result;
+  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
+  {
+    if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
+      result.append(mParentPlot->mGraphs.at(i));
+  }
+  return result;
+}
+
+/*!
+  Returns a list of all the items that are associated with this axis rect.
+
+  An item is considered associated with an axis rect if any of its positions has key or value axis
+  set to an axis that is in this axis rect, or if any of its positions has \ref
+  QCPItemPosition::setAxisRect set to the axis rect, or if the clip axis rect (\ref
+  QCPAbstractItem::setClipAxisRect) is set to this axis rect.
+
+  \see plottables, graphs
+*/
+QList<QCPAbstractItem *> QCPAxisRect::items() const
+{
+  // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
+  //       and miss those items that have this axis rect as clipAxisRect.
+  QList<QCPAbstractItem*> result;
+  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
+  {
+    if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
+    {
+      result.append(mParentPlot->mItems.at(itemId));
+      continue;
+    }
+    QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
+    for (int posId=0; posId<positions.size(); ++posId)
+    {
+      if (positions.at(posId)->axisRect() == this ||
+          positions.at(posId)->keyAxis()->axisRect() == this ||
+          positions.at(posId)->valueAxis()->axisRect() == this)
+      {
+        result.append(mParentPlot->mItems.at(itemId));
+        break;
+      }
+    }
+  }
+  return result;
+}
+
+/*!
+  This method is called automatically upon replot and doesn't need to be called by users of
+  QCPAxisRect.
+
+  Calls the base class implementation to update the margins (see \ref QCPLayoutElement::update),
+  and finally passes the \ref rect to the inset layout (\ref insetLayout) and calls its
+  QCPInsetLayout::update function.
+*/
+void QCPAxisRect::update(UpdatePhase phase)
+{
+  QCPLayoutElement::update(phase);
+
+  switch (phase)
+  {
+    case upPreparation:
+    {
+      QList<QCPAxis*> allAxes = axes();
+      for (int i=0; i<allAxes.size(); ++i)
+        allAxes.at(i)->setupTickVectors();
+      break;
+    }
+    case upLayout:
+    {
+      mInsetLayout->setOuterRect(rect());
+      break;
+    }
+    default: break;
+  }
+
+  // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
+  mInsetLayout->update(phase);
+}
+
+/* inherits documentation from base class */
+QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
+{
+  QList<QCPLayoutElement*> result;
+  if (mInsetLayout)
+  {
+    result << mInsetLayout;
+    if (recursive)
+      result << mInsetLayout->elements(recursive);
+  }
+  return result;
+}
+
+/* inherits documentation from base class */
+void QCPAxisRect::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  painter->setAntialiasing(false);
+}
+
+/* inherits documentation from base class */
+void QCPAxisRect::draw(QCPPainter *painter)
+{
+  drawBackground(painter);
+}
+
+/*!
+  Sets \a pm as the axis background pixmap. The axis background pixmap will be drawn inside the
+  axis rect. Since axis rects place themselves on the "background" layer by default, the axis rect
+  backgrounds are usually drawn below everything else.
+
+  For cases where the provided pixmap doesn't have the same size as the axis rect, scaling can be
+  enabled with \ref setBackgroundScaled and the scaling mode (i.e. whether and how the aspect ratio
+  is preserved) can be set with \ref setBackgroundScaledMode. To set all these options in one call,
+  consider using the overloaded version of this function.
+
+  Below the pixmap, the axis rect may be optionally filled with a brush, if specified with \ref
+  setBackground(const QBrush &brush).
+
+  \see setBackgroundScaled, setBackgroundScaledMode, setBackground(const QBrush &brush)
+*/
+void QCPAxisRect::setBackground(const QPixmap &pm)
+{
+  mBackgroundPixmap = pm;
+  mScaledBackgroundPixmap = QPixmap();
+}
+
+/*! \overload
+
+  Sets \a brush as the background brush. The axis rect background will be filled with this brush.
+  Since axis rects place themselves on the "background" layer by default, the axis rect backgrounds
+  are usually drawn below everything else.
+
+  The brush will be drawn before (under) any background pixmap, which may be specified with \ref
+  setBackground(const QPixmap &pm).
+
+  To disable drawing of a background brush, set \a brush to Qt::NoBrush.
+
+  \see setBackground(const QPixmap &pm)
+*/
+void QCPAxisRect::setBackground(const QBrush &brush)
+{
+  mBackgroundBrush = brush;
+}
+
+/*! \overload
+
+  Allows setting the background pixmap of the axis rect, whether it shall be scaled and how it
+  shall be scaled in one call.
+
+  \see setBackground(const QPixmap &pm), setBackgroundScaled, setBackgroundScaledMode
+*/
+void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
+{
+  mBackgroundPixmap = pm;
+  mScaledBackgroundPixmap = QPixmap();
+  mBackgroundScaled = scaled;
+  mBackgroundScaledMode = mode;
+}
+
+/*!
+  Sets whether the axis background pixmap shall be scaled to fit the axis rect or not. If \a scaled
+  is set to true, you may control whether and how the aspect ratio of the original pixmap is
+  preserved with \ref setBackgroundScaledMode.
+
+  Note that the scaled version of the original pixmap is buffered, so there is no performance
+  penalty on replots. (Except when the axis rect dimensions are changed continuously.)
+
+  \see setBackground, setBackgroundScaledMode
+*/
+void QCPAxisRect::setBackgroundScaled(bool scaled)
+{
+  mBackgroundScaled = scaled;
+}
+
+/*!
+  If scaling of the axis background pixmap is enabled (\ref setBackgroundScaled), use this function to
+  define whether and how the aspect ratio of the original pixmap passed to \ref setBackground is preserved.
+  \see setBackground, setBackgroundScaled
+*/
+void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
+{
+  mBackgroundScaledMode = mode;
+}
+
+/*!
+  Returns the range drag axis of the \a orientation provided.
+
+  \see setRangeDragAxes
+*/
+QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
+{
+  return (orientation == Qt::Horizontal ? mRangeDragHorzAxis.data() : mRangeDragVertAxis.data());
+}
+
+/*!
+  Returns the range zoom axis of the \a orientation provided.
+
+  \see setRangeZoomAxes
+*/
+QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
+{
+  return (orientation == Qt::Horizontal ? mRangeZoomHorzAxis.data() : mRangeZoomVertAxis.data());
+}
+
+/*!
+  Returns the range zoom factor of the \a orientation provided.
+
+  \see setRangeZoomFactor
+*/
+double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
+{
+  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
+}
+
+/*!
+  Sets which axis orientation may be range dragged by the user with mouse interaction.
+  What orientation corresponds to which specific axis can be set with
+  \ref setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical). By
+  default, the horizontal axis is the bottom axis (xAxis) and the vertical axis
+  is the left axis (yAxis).
+
+  To disable range dragging entirely, pass 0 as \a orientations or remove \ref QCP::iRangeDrag from \ref
+  QCustomPlot::setInteractions. To enable range dragging for both directions, pass <tt>Qt::Horizontal |
+  Qt::Vertical</tt> as \a orientations.
+
+  In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
+  contains \ref QCP::iRangeDrag to enable the range dragging interaction.
+
+  \see setRangeZoom, setRangeDragAxes, QCustomPlot::setNoAntialiasingOnDrag
+*/
+void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
+{
+  mRangeDrag = orientations;
+}
+
+/*!
+  Sets which axis orientation may be zoomed by the user with the mouse wheel. What orientation
+  corresponds to which specific axis can be set with \ref setRangeZoomAxes(QCPAxis *horizontal,
+  QCPAxis *vertical). By default, the horizontal axis is the bottom axis (xAxis) and the vertical
+  axis is the left axis (yAxis).
+
+  To disable range zooming entirely, pass 0 as \a orientations or remove \ref QCP::iRangeZoom from \ref
+  QCustomPlot::setInteractions. To enable range zooming for both directions, pass <tt>Qt::Horizontal |
+  Qt::Vertical</tt> as \a orientations.
+
+  In addition to setting \a orientations to a non-zero value, make sure \ref QCustomPlot::setInteractions
+  contains \ref QCP::iRangeZoom to enable the range zooming interaction.
+
+  \see setRangeZoomFactor, setRangeZoomAxes, setRangeDrag
+*/
+void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
+{
+  mRangeZoom = orientations;
+}
+
+/*!
+  Sets the axes whose range will be dragged when \ref setRangeDrag enables mouse range dragging
+  on the QCustomPlot widget.
+
+  \see setRangeZoomAxes
+*/
+void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
+{
+  mRangeDragHorzAxis = horizontal;
+  mRangeDragVertAxis = vertical;
+}
+
+/*!
+  Sets the axes whose range will be zoomed when \ref setRangeZoom enables mouse wheel zooming on the
+  QCustomPlot widget. The two axes can be zoomed with different strengths, when different factors
+  are passed to \ref setRangeZoomFactor(double horizontalFactor, double verticalFactor).
+
+  \see setRangeDragAxes
+*/
+void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
+{
+  mRangeZoomHorzAxis = horizontal;
+  mRangeZoomVertAxis = vertical;
+}
+
+/*!
+  Sets how strong one rotation step of the mouse wheel zooms, when range zoom was activated with
+  \ref setRangeZoom. The two parameters \a horizontalFactor and \a verticalFactor provide a way to
+  let the horizontal axis zoom at different rates than the vertical axis. Which axis is horizontal
+  and which is vertical, can be set with \ref setRangeZoomAxes.
+
+  When the zoom factor is greater than one, scrolling the mouse wheel backwards (towards the user)
+  will zoom in (make the currently visible range smaller). For zoom factors smaller than one, the
+  same scrolling direction will zoom out.
+*/
+void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
+{
+  mRangeZoomFactorHorz = horizontalFactor;
+  mRangeZoomFactorVert = verticalFactor;
+}
+
+/*! \overload
+
+  Sets both the horizontal and vertical zoom \a factor.
+*/
+void QCPAxisRect::setRangeZoomFactor(double factor)
+{
+  mRangeZoomFactorHorz = factor;
+  mRangeZoomFactorVert = factor;
+}
+
+/*! \internal
+
+  Draws the background of this axis rect. It may consist of a background fill (a QBrush) and a
+  pixmap.
+
+  If a brush was given via \ref setBackground(const QBrush &brush), this function first draws an
+  according filling inside the axis rect with the provided \a painter.
+
+  Then, if a pixmap was provided via \ref setBackground, this function buffers the scaled version
+  depending on \ref setBackgroundScaled and \ref setBackgroundScaledMode and then draws it inside
+  the axis rect with the provided \a painter. The scaled version is buffered in
+  mScaledBackgroundPixmap to prevent expensive rescaling at every redraw. It is only updated, when
+  the axis rect has changed in a way that requires a rescale of the background pixmap (this is
+  dependant on the \ref setBackgroundScaledMode), or when a differend axis backgroud pixmap was
+  set.
+
+  \see setBackground, setBackgroundScaled, setBackgroundScaledMode
+*/
+void QCPAxisRect::drawBackground(QCPPainter *painter)
+{
+  // draw background fill:
+  if (mBackgroundBrush != Qt::NoBrush)
+    painter->fillRect(mRect, mBackgroundBrush);
+
+  // draw background pixmap (on top of fill, if brush specified):
+  if (!mBackgroundPixmap.isNull())
+  {
+    if (mBackgroundScaled)
+    {
+      // check whether mScaledBackground needs to be updated:
+      QSize scaledSize(mBackgroundPixmap.size());
+      scaledSize.scale(mRect.size(), mBackgroundScaledMode);
+      if (mScaledBackgroundPixmap.size() != scaledSize)
+        mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
+      painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
+    } else
+    {
+      painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
+    }
+  }
+}
+
+/*! \internal
+
+  This function makes sure multiple axes on the side specified with \a type don't collide, but are
+  distributed according to their respective space requirement (QCPAxis::calculateMargin).
+
+  It does this by setting an appropriate offset (\ref QCPAxis::setOffset) on all axes except the
+  one with index zero.
+
+  This function is called by \ref calculateAutoMargin.
+*/
+void QCPAxisRect::updateAxesOffset(QCPAxis::AxisType type)
+{
+  const QList<QCPAxis*> axesList = mAxes.value(type);
+  if (axesList.isEmpty())
+    return;
+
+  bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false
+  for (int i=1; i<axesList.size(); ++i)
+  {
+    int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
+    if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible)
+    {
+      if (!isFirstVisible)
+        offset += axesList.at(i)->tickLengthIn();
+      isFirstVisible = false;
+    }
+    axesList.at(i)->setOffset(offset);
+  }
+}
+
+/* inherits documentation from base class */
+int QCPAxisRect::calculateAutoMargin(QCP::MarginSide side)
+{
+  if (!mAutoMargins.testFlag(side))
+    qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
+
+  updateAxesOffset(QCPAxis::marginSideToAxisType(side));
+
+  // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
+  const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
+  if (axesList.size() > 0)
+    return axesList.last()->offset() + axesList.last()->calculateMargin();
+  else
+    return 0;
+}
+
+/*! \internal
+
+  Event handler for when a mouse button is pressed on the axis rect. If the left mouse button is
+  pressed, the range dragging interaction is initialized (the actual range manipulation happens in
+  the \ref mouseMoveEvent).
+
+  The mDragging flag is set to true and some anchor points are set that are needed to determine the
+  distance the mouse was dragged in the mouse move/release events later.
+
+  \see mouseMoveEvent, mouseReleaseEvent
+*/
+void QCPAxisRect::mousePressEvent(QMouseEvent *event)
+{
+  mDragStart = event->pos(); // need this even when not LeftButton is pressed, to determine in releaseEvent whether it was a full click (no position change between press and release)
+  if (event->buttons() & Qt::LeftButton)
+  {
+    mDragging = true;
+    // initialize antialiasing backup in case we start dragging:
+    if (mParentPlot->noAntialiasingOnDrag())
+    {
+      mAADragBackup = mParentPlot->antialiasedElements();
+      mNotAADragBackup = mParentPlot->notAntialiasedElements();
+    }
+    // Mouse range dragging interaction:
+    if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
+    {
+      if (mRangeDragHorzAxis)
+        mDragStartHorzRange = mRangeDragHorzAxis.data()->range();
+      if (mRangeDragVertAxis)
+        mDragStartVertRange = mRangeDragVertAxis.data()->range();
+    }
+  }
+}
+
+/*! \internal
+
+  Event handler for when the mouse is moved on the axis rect. If range dragging was activated in a
+  preceding \ref mousePressEvent, the range is moved accordingly.
+
+  \see mousePressEvent, mouseReleaseEvent
+*/
+void QCPAxisRect::mouseMoveEvent(QMouseEvent *event)
+{
+  // Mouse range dragging interaction:
+  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
+  {
+    if (mRangeDrag.testFlag(Qt::Horizontal))
+    {
+      if (QCPAxis *rangeDragHorzAxis = mRangeDragHorzAxis.data())
+      {
+        if (rangeDragHorzAxis->mScaleType == QCPAxis::stLinear)
+        {
+          double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) - rangeDragHorzAxis->pixelToCoord(event->pos().x());
+          rangeDragHorzAxis->setRange(mDragStartHorzRange.lower+diff, mDragStartHorzRange.upper+diff);
+        } else if (rangeDragHorzAxis->mScaleType == QCPAxis::stLogarithmic)
+        {
+          double diff = rangeDragHorzAxis->pixelToCoord(mDragStart.x()) / rangeDragHorzAxis->pixelToCoord(event->pos().x());
+          rangeDragHorzAxis->setRange(mDragStartHorzRange.lower*diff, mDragStartHorzRange.upper*diff);
+        }
+      }
+    }
+    if (mRangeDrag.testFlag(Qt::Vertical))
+    {
+      if (QCPAxis *rangeDragVertAxis = mRangeDragVertAxis.data())
+      {
+        if (rangeDragVertAxis->mScaleType == QCPAxis::stLinear)
+        {
+          double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) - rangeDragVertAxis->pixelToCoord(event->pos().y());
+          rangeDragVertAxis->setRange(mDragStartVertRange.lower+diff, mDragStartVertRange.upper+diff);
+        } else if (rangeDragVertAxis->mScaleType == QCPAxis::stLogarithmic)
+        {
+          double diff = rangeDragVertAxis->pixelToCoord(mDragStart.y()) / rangeDragVertAxis->pixelToCoord(event->pos().y());
+          rangeDragVertAxis->setRange(mDragStartVertRange.lower*diff, mDragStartVertRange.upper*diff);
+        }
+      }
+    }
+    if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
+    {
+      if (mParentPlot->noAntialiasingOnDrag())
+        mParentPlot->setNotAntialiasedElements(QCP::aeAll);
+      mParentPlot->replot();
+    }
+  }
+}
+
+/* inherits documentation from base class */
+void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event)
+{
+  Q_UNUSED(event)
+  mDragging = false;
+  if (mParentPlot->noAntialiasingOnDrag())
+  {
+    mParentPlot->setAntialiasedElements(mAADragBackup);
+    mParentPlot->setNotAntialiasedElements(mNotAADragBackup);
+  }
+}
+
+/*! \internal
+
+  Event handler for mouse wheel events. If rangeZoom is Qt::Horizontal, Qt::Vertical or both, the
+  ranges of the axes defined as rangeZoomHorzAxis and rangeZoomVertAxis are scaled. The center of
+  the scaling operation is the current cursor position inside the axis rect. The scaling factor is
+  dependant on the mouse wheel delta (which direction the wheel was rotated) to provide a natural
+  zooming feel. The Strength of the zoom can be controlled via \ref setRangeZoomFactor.
+
+  Note, that event->delta() is usually +/-120 for single rotation steps. However, if the mouse
+  wheel is turned rapidly, many steps may bunch up to one event, so the event->delta() may then be
+  multiples of 120. This is taken into account here, by calculating \a wheelSteps and using it as
+  exponent of the range zoom factor. This takes care of the wheel direction automatically, by
+  inverting the factor, when the wheel step is negative (f^-1 = 1/f).
+*/
+void QCPAxisRect::wheelEvent(QWheelEvent *event)
+{
+  // Mouse range zooming interaction:
+  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
+  {
+    if (mRangeZoom != 0)
+    {
+      double factor;
+      double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
+      if (mRangeZoom.testFlag(Qt::Horizontal))
+      {
+        factor = qPow(mRangeZoomFactorHorz, wheelSteps);
+        if (mRangeZoomHorzAxis.data())
+          mRangeZoomHorzAxis.data()->scaleRange(factor, mRangeZoomHorzAxis.data()->pixelToCoord(event->pos().x()));
+      }
+      if (mRangeZoom.testFlag(Qt::Vertical))
+      {
+        factor = qPow(mRangeZoomFactorVert, wheelSteps);
+        if (mRangeZoomVertAxis.data())
+          mRangeZoomVertAxis.data()->scaleRange(factor, mRangeZoomVertAxis.data()->pixelToCoord(event->pos().y()));
+      }
+      mParentPlot->replot();
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPAbstractLegendItem
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPAbstractLegendItem
+  \brief The abstract base class for all entries in a QCPLegend.
+
+  It defines a very basic interface for entries in a QCPLegend. For representing plottables in the
+  legend, the subclass \ref QCPPlottableLegendItem is more suitable.
+
+  Only derive directly from this class when you need absolute freedom (e.g. a custom legend entry
+  that's not even associated with a plottable).
+
+  You must implement the following pure virtual functions:
+  \li \ref draw (from QCPLayerable)
+
+  You inherit the following members you may use:
+  <table>
+    <tr>
+      <td>QCPLegend *\b mParentLegend</td>
+      <td>A pointer to the parent QCPLegend.</td>
+    </tr><tr>
+      <td>QFont \b mFont</td>
+      <td>The generic font of the item. You should use this font for all or at least the most prominent text of the item.</td>
+    </tr>
+  </table>
+*/
+
+/* start of documentation of signals */
+
+/*! \fn void QCPAbstractLegendItem::selectionChanged(bool selected)
+
+  This signal is emitted when the selection state of this legend item has changed, either by user
+  interaction or by a direct call to \ref setSelected.
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs a QCPAbstractLegendItem and associates it with the QCPLegend \a parent. This does not
+  cause the item to be added to \a parent, so \ref QCPLegend::addItem must be called separately.
+*/
+QCPAbstractLegendItem::QCPAbstractLegendItem(QCPLegend *parent) :
+  QCPLayoutElement(parent->parentPlot()),
+  mParentLegend(parent),
+  mFont(parent->font()),
+  mTextColor(parent->textColor()),
+  mSelectedFont(parent->selectedFont()),
+  mSelectedTextColor(parent->selectedTextColor()),
+  mSelectable(true),
+  mSelected(false)
+{
+  setLayer(QLatin1String("legend"));
+  setMargins(QMargins(8, 2, 8, 2));
+}
+
+/*!
+  Sets the default font of this specific legend item to \a font.
+
+  \see setTextColor, QCPLegend::setFont
+*/
+void QCPAbstractLegendItem::setFont(const QFont &font)
+{
+  mFont = font;
+}
+
+/*!
+  Sets the default text color of this specific legend item to \a color.
+
+  \see setFont, QCPLegend::setTextColor
+*/
+void QCPAbstractLegendItem::setTextColor(const QColor &color)
+{
+  mTextColor = color;
+}
+
+/*!
+  When this legend item is selected, \a font is used to draw generic text, instead of the normal
+  font set with \ref setFont.
+
+  \see setFont, QCPLegend::setSelectedFont
+*/
+void QCPAbstractLegendItem::setSelectedFont(const QFont &font)
+{
+  mSelectedFont = font;
+}
+
+/*!
+  When this legend item is selected, \a color is used to draw generic text, instead of the normal
+  color set with \ref setTextColor.
+
+  \see setTextColor, QCPLegend::setSelectedTextColor
+*/
+void QCPAbstractLegendItem::setSelectedTextColor(const QColor &color)
+{
+  mSelectedTextColor = color;
+}
+
+/*!
+  Sets whether this specific legend item is selectable.
+
+  \see setSelectedParts, QCustomPlot::setInteractions
+*/
+void QCPAbstractLegendItem::setSelectable(bool selectable)
+{
+  if (mSelectable != selectable)
+  {
+    mSelectable = selectable;
+    emit selectableChanged(mSelectable);
+  }
+}
+
+/*!
+  Sets whether this specific legend item is selected.
+
+  It is possible to set the selection state of this item by calling this function directly, even if
+  setSelectable is set to false.
+
+  \see setSelectableParts, QCustomPlot::setInteractions
+*/
+void QCPAbstractLegendItem::setSelected(bool selected)
+{
+  if (mSelected != selected)
+  {
+    mSelected = selected;
+    emit selectionChanged(mSelected);
+  }
+}
+
+/* inherits documentation from base class */
+double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (!mParentPlot) return -1;
+  if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
+    return -1;
+
+  if (mRect.contains(pos.toPoint()))
+    return mParentPlot->selectionTolerance()*0.99;
+  else
+    return -1;
+}
+
+/* inherits documentation from base class */
+void QCPAbstractLegendItem::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeLegendItems);
+}
+
+/* inherits documentation from base class */
+QRect QCPAbstractLegendItem::clipRect() const
+{
+  return mOuterRect;
+}
+
+/* inherits documentation from base class */
+void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
+{
+  Q_UNUSED(event)
+  Q_UNUSED(details)
+  if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
+  {
+    bool selBefore = mSelected;
+    setSelected(additive ? !mSelected : true);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelected != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
+{
+  if (mSelectable && mParentLegend->selectableParts().testFlag(QCPLegend::spItems))
+  {
+    bool selBefore = mSelected;
+    setSelected(false);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelected != selBefore;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPPlottableLegendItem
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPPlottableLegendItem
+  \brief A legend item representing a plottable with an icon and the plottable name.
+
+  This is the standard legend item for plottables. It displays an icon of the plottable next to the
+  plottable name. The icon is drawn by the respective plottable itself (\ref
+  QCPAbstractPlottable::drawLegendIcon), and tries to give an intuitive symbol for the plottable.
+  For example, the QCPGraph draws a centered horizontal line and/or a single scatter point in the
+  middle.
+
+  Legend items of this type are always associated with one plottable (retrievable via the
+  plottable() function and settable with the constructor). You may change the font of the plottable
+  name with \ref setFont. Icon padding and border pen is taken from the parent QCPLegend, see \ref
+  QCPLegend::setIconBorderPen and \ref QCPLegend::setIconTextPadding.
+
+  The function \ref QCPAbstractPlottable::addToLegend/\ref QCPAbstractPlottable::removeFromLegend
+  creates/removes legend items of this type in the default implementation. However, these functions
+  may be reimplemented such that a different kind of legend item (e.g a direct subclass of
+  QCPAbstractLegendItem) is used for that plottable.
+
+  Since QCPLegend is based on QCPLayoutGrid, a legend item itself is just a subclass of
+  QCPLayoutElement. While it could be added to a legend (or any other layout) via the normal layout
+  interface, QCPLegend has specialized functions for handling legend items conveniently, see the
+  documentation of \ref QCPLegend.
+*/
+
+/*!
+  Creates a new legend item associated with \a plottable.
+
+  Once it's created, it can be added to the legend via \ref QCPLegend::addItem.
+
+  A more convenient way of adding/removing a plottable to/from the legend is via the functions \ref
+  QCPAbstractPlottable::addToLegend and \ref QCPAbstractPlottable::removeFromLegend.
+*/
+QCPPlottableLegendItem::QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable) :
+  QCPAbstractLegendItem(parent),
+  mPlottable(plottable)
+{
+}
+
+/*! \internal
+
+  Returns the pen that shall be used to draw the icon border, taking into account the selection
+  state of this item.
+*/
+QPen QCPPlottableLegendItem::getIconBorderPen() const
+{
+  return mSelected ? mParentLegend->selectedIconBorderPen() : mParentLegend->iconBorderPen();
+}
+
+/*! \internal
+
+  Returns the text color that shall be used to draw text, taking into account the selection state
+  of this item.
+*/
+QColor QCPPlottableLegendItem::getTextColor() const
+{
+  return mSelected ? mSelectedTextColor : mTextColor;
+}
+
+/*! \internal
+
+  Returns the font that shall be used to draw text, taking into account the selection state of this
+  item.
+*/
+QFont QCPPlottableLegendItem::getFont() const
+{
+  return mSelected ? mSelectedFont : mFont;
+}
+
+/*! \internal
+
+  Draws the item with \a painter. The size and position of the drawn legend item is defined by the
+  parent layout (typically a \ref QCPLegend) and the \ref minimumSizeHint and \ref maximumSizeHint
+  of this legend item.
+*/
+void QCPPlottableLegendItem::draw(QCPPainter *painter)
+{
+  if (!mPlottable) return;
+  painter->setFont(getFont());
+  painter->setPen(QPen(getTextColor()));
+  QSizeF iconSize = mParentLegend->iconSize();
+  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
+  QRectF iconRect(mRect.topLeft(), iconSize);
+  int textHeight = qMax(textRect.height(), iconSize.height());  // if text has smaller height than icon, center text vertically in icon height, else align tops
+  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
+  // draw icon:
+  painter->save();
+  painter->setClipRect(iconRect, Qt::IntersectClip);
+  mPlottable->drawLegendIcon(painter, iconRect);
+  painter->restore();
+  // draw icon border:
+  if (getIconBorderPen().style() != Qt::NoPen)
+  {
+    painter->setPen(getIconBorderPen());
+    painter->setBrush(Qt::NoBrush);
+    painter->drawRect(iconRect);
+  }
+}
+
+/*! \internal
+
+  Calculates and returns the size of this item. This includes the icon, the text and the padding in
+  between.
+*/
+QSize QCPPlottableLegendItem::minimumSizeHint() const
+{
+  if (!mPlottable) return QSize();
+  QSize result(0, 0);
+  QRect textRect;
+  QFontMetrics fontMetrics(getFont());
+  QSize iconSize = mParentLegend->iconSize();
+  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
+  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width() + mMargins.left() + mMargins.right());
+  result.setHeight(qMax(textRect.height(), iconSize.height()) + mMargins.top() + mMargins.bottom());
+  return result;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPLegend
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPLegend
+  \brief Manages a legend inside a QCustomPlot.
+
+  A legend is a small box somewhere in the plot which lists plottables with their name and icon.
+
+  Normally, the legend is populated by calling \ref QCPAbstractPlottable::addToLegend. The
+  respective legend item can be removed with \ref QCPAbstractPlottable::removeFromLegend. However,
+  QCPLegend also offers an interface to add and manipulate legend items directly: \ref item, \ref
+  itemWithPlottable, \ref itemCount, \ref addItem, \ref removeItem, etc.
+
+  The QCPLegend derives from QCPLayoutGrid and as such can be placed in any position a
+  QCPLayoutElement may be positioned. The legend items are themselves QCPLayoutElements which are
+  placed in the grid layout of the legend. QCPLegend only adds an interface specialized for
+  handling child elements of type QCPAbstractLegendItem, as mentioned above. In principle, any
+  other layout elements may also be added to a legend via the normal \ref QCPLayoutGrid interface.
+  However, the QCPAbstractLegendItem-Interface will ignore those elements (e.g. \ref itemCount will
+  only return the number of items with QCPAbstractLegendItems type).
+
+  By default, every QCustomPlot has one legend (QCustomPlot::legend) which is placed in the inset
+  layout of the main axis rect (\ref QCPAxisRect::insetLayout). To move the legend to another
+  position inside the axis rect, use the methods of the \ref QCPLayoutInset. To move the legend
+  outside of the axis rect, place it anywhere else with the QCPLayout/QCPLayoutElement interface.
+*/
+
+/* start of documentation of signals */
+
+/*! \fn void QCPLegend::selectionChanged(QCPLegend::SelectableParts selection);
+
+  This signal is emitted when the selection state of this legend has changed.
+
+  \see setSelectedParts, setSelectableParts
+*/
+
+/* end of documentation of signals */
+
+/*!
+  Constructs a new QCPLegend instance with \a parentPlot as the containing plot and default values.
+
+  Note that by default, QCustomPlot already contains a legend ready to be used as
+  QCustomPlot::legend
+*/
+QCPLegend::QCPLegend()
+{
+  setRowSpacing(0);
+  setColumnSpacing(10);
+  setMargins(QMargins(2, 3, 2, 2));
+  setAntialiased(false);
+  setIconSize(32, 18);
+
+  setIconTextPadding(7);
+
+  setSelectableParts(spLegendBox | spItems);
+  setSelectedParts(spNone);
+
+  setBorderPen(QPen(Qt::black));
+  setSelectedBorderPen(QPen(Qt::blue, 2));
+  setIconBorderPen(Qt::NoPen);
+  setSelectedIconBorderPen(QPen(Qt::blue, 2));
+  setBrush(Qt::white);
+  setSelectedBrush(Qt::white);
+  setTextColor(Qt::black);
+  setSelectedTextColor(Qt::blue);
+}
+
+QCPLegend::~QCPLegend()
+{
+  clearItems();
+  if (qobject_cast<QCustomPlot*>(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot)
+    mParentPlot->legendRemoved(this);
+}
+
+/* no doc for getter, see setSelectedParts */
+QCPLegend::SelectableParts QCPLegend::selectedParts() const
+{
+  // check whether any legend elements selected, if yes, add spItems to return value
+  bool hasSelectedItems = false;
+  for (int i=0; i<itemCount(); ++i)
+  {
+    if (item(i) && item(i)->selected())
+    {
+      hasSelectedItems = true;
+      break;
+    }
+  }
+  if (hasSelectedItems)
+    return mSelectedParts | spItems;
+  else
+    return mSelectedParts & ~spItems;
+}
+
+/*!
+  Sets the pen, the border of the entire legend is drawn with.
+*/
+void QCPLegend::setBorderPen(const QPen &pen)
+{
+  mBorderPen = pen;
+}
+
+/*!
+  Sets the brush of the legend background.
+*/
+void QCPLegend::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the default font of legend text. Legend items that draw text (e.g. the name of a graph) will
+  use this font by default. However, a different font can be specified on a per-item-basis by
+  accessing the specific legend item.
+
+  This function will also set \a font on all already existing legend items.
+
+  \see QCPAbstractLegendItem::setFont
+*/
+void QCPLegend::setFont(const QFont &font)
+{
+  mFont = font;
+  for (int i=0; i<itemCount(); ++i)
+  {
+    if (item(i))
+      item(i)->setFont(mFont);
+  }
+}
+
+/*!
+  Sets the default color of legend text. Legend items that draw text (e.g. the name of a graph)
+  will use this color by default. However, a different colors can be specified on a per-item-basis
+  by accessing the specific legend item.
+
+  This function will also set \a color on all already existing legend items.
+
+  \see QCPAbstractLegendItem::setTextColor
+*/
+void QCPLegend::setTextColor(const QColor &color)
+{
+  mTextColor = color;
+  for (int i=0; i<itemCount(); ++i)
+  {
+    if (item(i))
+      item(i)->setTextColor(color);
+  }
+}
+
+/*!
+  Sets the size of legend icons. Legend items that draw an icon (e.g. a visual
+  representation of the graph) will use this size by default.
+*/
+void QCPLegend::setIconSize(const QSize &size)
+{
+  mIconSize = size;
+}
+
+/*! \overload
+*/
+void QCPLegend::setIconSize(int width, int height)
+{
+  mIconSize.setWidth(width);
+  mIconSize.setHeight(height);
+}
+
+/*!
+  Sets the horizontal space in pixels between the legend icon and the text next to it.
+  Legend items that draw an icon (e.g. a visual representation of the graph) and text (e.g. the
+  name of the graph) will use this space by default.
+*/
+void QCPLegend::setIconTextPadding(int padding)
+{
+  mIconTextPadding = padding;
+}
+
+/*!
+  Sets the pen used to draw a border around each legend icon. Legend items that draw an
+  icon (e.g. a visual representation of the graph) will use this pen by default.
+
+  If no border is wanted, set this to \a Qt::NoPen.
+*/
+void QCPLegend::setIconBorderPen(const QPen &pen)
+{
+  mIconBorderPen = pen;
+}
+
+/*!
+  Sets whether the user can (de-)select the parts in \a selectable by clicking on the QCustomPlot surface.
+  (When \ref QCustomPlot::setInteractions contains \ref QCP::iSelectLegend.)
+
+  However, even when \a selectable is set to a value not allowing the selection of a specific part,
+  it is still possible to set the selection of this part manually, by calling \ref setSelectedParts
+  directly.
+
+  \see SelectablePart, setSelectedParts
+*/
+void QCPLegend::setSelectableParts(const SelectableParts &selectable)
+{
+  if (mSelectableParts != selectable)
+  {
+    mSelectableParts = selectable;
+    emit selectableChanged(mSelectableParts);
+  }
+}
+
+/*!
+  Sets the selected state of the respective legend parts described by \ref SelectablePart. When a part
+  is selected, it uses a different pen/font and brush. If some legend items are selected and \a selected
+  doesn't contain \ref spItems, those items become deselected.
+
+  The entire selection mechanism is handled automatically when \ref QCustomPlot::setInteractions
+  contains iSelectLegend. You only need to call this function when you wish to change the selection
+  state manually.
+
+  This function can change the selection state of a part even when \ref setSelectableParts was set to a
+  value that actually excludes the part.
+
+  emits the \ref selectionChanged signal when \a selected is different from the previous selection state.
+
+  Note that it doesn't make sense to set the selected state \ref spItems here when it wasn't set
+  before, because there's no way to specify which exact items to newly select. Do this by calling
+  \ref QCPAbstractLegendItem::setSelected directly on the legend item you wish to select.
+
+  \see SelectablePart, setSelectableParts, selectTest, setSelectedBorderPen, setSelectedIconBorderPen, setSelectedBrush,
+  setSelectedFont
+*/
+void QCPLegend::setSelectedParts(const SelectableParts &selected)
+{
+  SelectableParts newSelected = selected;
+  mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
+
+  if (mSelectedParts != newSelected)
+  {
+    if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
+    {
+      qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
+      newSelected &= ~spItems;
+    }
+    if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
+    {
+      for (int i=0; i<itemCount(); ++i)
+      {
+        if (item(i))
+          item(i)->setSelected(false);
+      }
+    }
+    mSelectedParts = newSelected;
+    emit selectionChanged(mSelectedParts);
+  }
+}
+
+/*!
+  When the legend box is selected, this pen is used to draw the border instead of the normal pen
+  set via \ref setBorderPen.
+
+  \see setSelectedParts, setSelectableParts, setSelectedBrush
+*/
+void QCPLegend::setSelectedBorderPen(const QPen &pen)
+{
+  mSelectedBorderPen = pen;
+}
+
+/*!
+  Sets the pen legend items will use to draw their icon borders, when they are selected.
+
+  \see setSelectedParts, setSelectableParts, setSelectedFont
+*/
+void QCPLegend::setSelectedIconBorderPen(const QPen &pen)
+{
+  mSelectedIconBorderPen = pen;
+}
+
+/*!
+  When the legend box is selected, this brush is used to draw the legend background instead of the normal brush
+  set via \ref setBrush.
+
+  \see setSelectedParts, setSelectableParts, setSelectedBorderPen
+*/
+void QCPLegend::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/*!
+  Sets the default font that is used by legend items when they are selected.
+
+  This function will also set \a font on all already existing legend items.
+
+  \see setFont, QCPAbstractLegendItem::setSelectedFont
+*/
+void QCPLegend::setSelectedFont(const QFont &font)
+{
+  mSelectedFont = font;
+  for (int i=0; i<itemCount(); ++i)
+  {
+    if (item(i))
+      item(i)->setSelectedFont(font);
+  }
+}
+
+/*!
+  Sets the default text color that is used by legend items when they are selected.
+
+  This function will also set \a color on all already existing legend items.
+
+  \see setTextColor, QCPAbstractLegendItem::setSelectedTextColor
+*/
+void QCPLegend::setSelectedTextColor(const QColor &color)
+{
+  mSelectedTextColor = color;
+  for (int i=0; i<itemCount(); ++i)
+  {
+    if (item(i))
+      item(i)->setSelectedTextColor(color);
+  }
+}
+
+/*!
+  Returns the item with index \a i.
+
+  \see itemCount
+*/
+QCPAbstractLegendItem *QCPLegend::item(int index) const
+{
+  return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
+}
+
+/*!
+  Returns the QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
+  If such an item isn't in the legend, returns 0.
+
+  \see hasItemWithPlottable
+*/
+QCPPlottableLegendItem *QCPLegend::itemWithPlottable(const QCPAbstractPlottable *plottable) const
+{
+  for (int i=0; i<itemCount(); ++i)
+  {
+    if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
+    {
+      if (pli->plottable() == plottable)
+        return pli;
+    }
+  }
+  return 0;
+}
+
+/*!
+  Returns the number of items currently in the legend.
+  \see item
+*/
+int QCPLegend::itemCount() const
+{
+  return elementCount();
+}
+
+/*!
+  Returns whether the legend contains \a itm.
+*/
+bool QCPLegend::hasItem(QCPAbstractLegendItem *item) const
+{
+  for (int i=0; i<itemCount(); ++i)
+  {
+    if (item == this->item(i))
+        return true;
+  }
+  return false;
+}
+
+/*!
+  Returns whether the legend contains a QCPPlottableLegendItem which is associated with \a plottable (e.g. a \ref QCPGraph*).
+  If such an item isn't in the legend, returns false.
+
+  \see itemWithPlottable
+*/
+bool QCPLegend::hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
+{
+  return itemWithPlottable(plottable);
+}
+
+/*!
+  Adds \a item to the legend, if it's not present already.
+
+  Returns true on sucess, i.e. if the item wasn't in the list already and has been successfuly added.
+
+  The legend takes ownership of the item.
+*/
+bool QCPLegend::addItem(QCPAbstractLegendItem *item)
+{
+  if (!hasItem(item))
+  {
+    return addElement(rowCount(), 0, item);
+  } else
+    return false;
+}
+
+/*!
+  Removes the item with index \a index from the legend.
+
+  Returns true, if successful.
+
+  \see itemCount, clearItems
+*/
+bool QCPLegend::removeItem(int index)
+{
+  if (QCPAbstractLegendItem *ali = item(index))
+  {
+    bool success = remove(ali);
+    simplify();
+    return success;
+  } else
+    return false;
+}
+
+/*! \overload
+
+  Removes \a item from the legend.
+
+  Returns true, if successful.
+
+  \see clearItems
+*/
+bool QCPLegend::removeItem(QCPAbstractLegendItem *item)
+{
+  bool success = remove(item);
+  simplify();
+  return success;
+}
+
+/*!
+  Removes all items from the legend.
+*/
+void QCPLegend::clearItems()
+{
+  for (int i=itemCount()-1; i>=0; --i)
+    removeItem(i);
+}
+
+/*!
+  Returns the legend items that are currently selected. If no items are selected,
+  the list is empty.
+
+  \see QCPAbstractLegendItem::setSelected, setSelectable
+*/
+QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
+{
+  QList<QCPAbstractLegendItem*> result;
+  for (int i=0; i<itemCount(); ++i)
+  {
+    if (QCPAbstractLegendItem *ali = item(i))
+    {
+      if (ali->selected())
+        result.append(ali);
+    }
+  }
+  return result;
+}
+
+/*! \internal
+
+  A convenience function to easily set the QPainter::Antialiased hint on the provided \a painter
+  before drawing main legend elements.
+
+  This is the antialiasing state the painter passed to the \ref draw method is in by default.
+
+  This function takes into account the local setting of the antialiasing flag as well as the
+  overrides set with \ref QCustomPlot::setAntialiasedElements and \ref
+  QCustomPlot::setNotAntialiasedElements.
+
+  \see setAntialiased
+*/
+void QCPLegend::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeLegend);
+}
+
+/*! \internal
+
+  Returns the pen used to paint the border of the legend, taking into account the selection state
+  of the legend box.
+*/
+QPen QCPLegend::getBorderPen() const
+{
+  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
+}
+
+/*! \internal
+
+  Returns the brush used to paint the background of the legend, taking into account the selection
+  state of the legend box.
+*/
+QBrush QCPLegend::getBrush() const
+{
+  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
+}
+
+/*! \internal
+
+  Draws the legend box with the provided \a painter. The individual legend items are layerables
+  themselves, thus are drawn independently.
+*/
+void QCPLegend::draw(QCPPainter *painter)
+{
+  // draw background rect:
+  painter->setBrush(getBrush());
+  painter->setPen(getBorderPen());
+  painter->drawRect(mOuterRect);
+}
+
+/* inherits documentation from base class */
+double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  if (!mParentPlot) return -1;
+  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
+    return -1;
+
+  if (mOuterRect.contains(pos.toPoint()))
+  {
+    if (details) details->setValue(spLegendBox);
+    return mParentPlot->selectionTolerance()*0.99;
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
+{
+  Q_UNUSED(event)
+  mSelectedParts = selectedParts(); // in case item selection has changed
+  if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
+  {
+    SelectableParts selBefore = mSelectedParts;
+    setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent)
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelectedParts != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPLegend::deselectEvent(bool *selectionStateChanged)
+{
+  mSelectedParts = selectedParts(); // in case item selection has changed
+  if (mSelectableParts.testFlag(spLegendBox))
+  {
+    SelectableParts selBefore = mSelectedParts;
+    setSelectedParts(selectedParts() & ~spLegendBox);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelectedParts != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+QCP::Interaction QCPLegend::selectionCategory() const
+{
+  return QCP::iSelectLegend;
+}
+
+/* inherits documentation from base class */
+QCP::Interaction QCPAbstractLegendItem::selectionCategory() const
+{
+  return QCP::iSelectLegend;
+}
+
+/* inherits documentation from base class */
+void QCPLegend::parentPlotInitialized(QCustomPlot *parentPlot)
+{
+  Q_UNUSED(parentPlot)
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPPlotTitle
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPPlotTitle
+  \brief A layout element displaying a plot title text
+
+  The text may be specified with \ref setText, theformatting can be controlled with \ref setFont
+  and \ref setTextColor.
+
+  A plot title can be added as follows:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpplottitle-creation
+
+  Since a plot title is a common requirement, QCustomPlot offers specialized selection signals for
+  easy interaction with QCPPlotTitle. If a layout element of type QCPPlotTitle is clicked, the
+  signal \ref QCustomPlot::titleClick is emitted. A double click emits the \ref
+  QCustomPlot::titleDoubleClick signal.
+*/
+
+/* start documentation of signals */
+
+/*! \fn void QCPPlotTitle::selectionChanged(bool selected)
+
+  This signal is emitted when the selection state has changed to \a selected, either by user
+  interaction or by a direct call to \ref setSelected.
+
+  \see setSelected, setSelectable
+*/
+
+/* end documentation of signals */
+
+/*!
+  Creates a new QCPPlotTitle instance and sets default values. The initial text is empty (\ref setText).
+
+  To set the title text in the constructor, rather use \ref QCPPlotTitle(QCustomPlot *parentPlot, const QString &text).
+*/
+QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot) :
+  QCPLayoutElement(parentPlot),
+  mFont(QFont(QLatin1String("sans serif"), 13*1.5, QFont::Bold)),
+  mTextColor(Qt::black),
+  mSelectedFont(QFont(QLatin1String("sans serif"), 13*1.6, QFont::Bold)),
+  mSelectedTextColor(Qt::blue),
+  mSelectable(false),
+  mSelected(false)
+{
+  if (parentPlot)
+  {
+    setLayer(parentPlot->currentLayer());
+    mFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold);
+    mSelectedFont = QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold);
+  }
+  setMargins(QMargins(5, 5, 5, 0));
+}
+
+/*! \overload
+
+  Creates a new QCPPlotTitle instance and sets default values. The initial text is set to \a text.
+*/
+QCPPlotTitle::QCPPlotTitle(QCustomPlot *parentPlot, const QString &text) :
+  QCPLayoutElement(parentPlot),
+  mText(text),
+  mFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.5, QFont::Bold)),
+  mTextColor(Qt::black),
+  mSelectedFont(QFont(parentPlot->font().family(), parentPlot->font().pointSize()*1.6, QFont::Bold)),
+  mSelectedTextColor(Qt::blue),
+  mSelectable(false),
+  mSelected(false)
+{
+  setLayer(QLatin1String("axes"));
+  setMargins(QMargins(5, 5, 5, 0));
+}
+
+/*!
+  Sets the text that will be displayed to \a text. Multiple lines can be created by insertion of "\n".
+
+  \see setFont, setTextColor
+*/
+void QCPPlotTitle::setText(const QString &text)
+{
+  mText = text;
+}
+
+/*!
+  Sets the \a font of the title text.
+
+  \see setTextColor, setSelectedFont
+*/
+void QCPPlotTitle::setFont(const QFont &font)
+{
+  mFont = font;
+}
+
+/*!
+  Sets the \a color of the title text.
+
+  \see setFont, setSelectedTextColor
+*/
+void QCPPlotTitle::setTextColor(const QColor &color)
+{
+  mTextColor = color;
+}
+
+/*!
+  Sets the \a font of the title text that will be used if the plot title is selected (\ref setSelected).
+
+  \see setFont
+*/
+void QCPPlotTitle::setSelectedFont(const QFont &font)
+{
+  mSelectedFont = font;
+}
+
+/*!
+  Sets the \a color of the title text that will be used if the plot title is selected (\ref setSelected).
+
+  \see setTextColor
+*/
+void QCPPlotTitle::setSelectedTextColor(const QColor &color)
+{
+  mSelectedTextColor = color;
+}
+
+/*!
+  Sets whether the user may select this plot title to \a selectable.
+
+  Note that even when \a selectable is set to <tt>false</tt>, the selection state may be changed
+  programmatically via \ref setSelected.
+*/
+void QCPPlotTitle::setSelectable(bool selectable)
+{
+  if (mSelectable != selectable)
+  {
+    mSelectable = selectable;
+    emit selectableChanged(mSelectable);
+  }
+}
+
+/*!
+  Sets the selection state of this plot title to \a selected. If the selection has changed, \ref
+  selectionChanged is emitted.
+
+  Note that this function can change the selection state independently of the current \ref
+  setSelectable state.
+*/
+void QCPPlotTitle::setSelected(bool selected)
+{
+  if (mSelected != selected)
+  {
+    mSelected = selected;
+    emit selectionChanged(mSelected);
+  }
+}
+
+/* inherits documentation from base class */
+void QCPPlotTitle::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  applyAntialiasingHint(painter, mAntialiased, QCP::aeNone);
+}
+
+/* inherits documentation from base class */
+void QCPPlotTitle::draw(QCPPainter *painter)
+{
+  painter->setFont(mainFont());
+  painter->setPen(QPen(mainTextColor()));
+  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
+}
+
+/* inherits documentation from base class */
+QSize QCPPlotTitle::minimumSizeHint() const
+{
+  QFontMetrics metrics(mFont);
+  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
+  result.rwidth() += mMargins.left() + mMargins.right();
+  result.rheight() += mMargins.top() + mMargins.bottom();
+  return result;
+}
+
+/* inherits documentation from base class */
+QSize QCPPlotTitle::maximumSizeHint() const
+{
+  QFontMetrics metrics(mFont);
+  QSize result = metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size();
+  result.rheight() += mMargins.top() + mMargins.bottom();
+  result.setWidth(QWIDGETSIZE_MAX);
+  return result;
+}
+
+/* inherits documentation from base class */
+void QCPPlotTitle::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
+{
+  Q_UNUSED(event)
+  Q_UNUSED(details)
+  if (mSelectable)
+  {
+    bool selBefore = mSelected;
+    setSelected(additive ? !mSelected : true);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelected != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPPlotTitle::deselectEvent(bool *selectionStateChanged)
+{
+  if (mSelectable)
+  {
+    bool selBefore = mSelected;
+    setSelected(false);
+    if (selectionStateChanged)
+      *selectionStateChanged = mSelected != selBefore;
+  }
+}
+
+/* inherits documentation from base class */
+double QCPPlotTitle::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  if (mTextBoundingRect.contains(pos.toPoint()))
+    return mParentPlot->selectionTolerance()*0.99;
+  else
+    return -1;
+}
+
+/*! \internal
+
+  Returns the main font to be used. This is mSelectedFont if \ref setSelected is set to
+  <tt>true</tt>, else mFont is returned.
+*/
+QFont QCPPlotTitle::mainFont() const
+{
+  return mSelected ? mSelectedFont : mFont;
+}
+
+/*! \internal
+
+  Returns the main color to be used. This is mSelectedTextColor if \ref setSelected is set to
+  <tt>true</tt>, else mTextColor is returned.
+*/
+QColor QCPPlotTitle::mainTextColor() const
+{
+  return mSelected ? mSelectedTextColor : mTextColor;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPColorScale
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPColorScale
+  \brief A color scale for use with color coding data such as QCPColorMap
+
+  This layout element can be placed on the plot to correlate a color gradient with data values. It
+  is usually used in combination with one or multiple \ref QCPColorMap "QCPColorMaps".
+
+  \image html QCPColorScale.png
+
+  The color scale can be either horizontal or vertical, as shown in the image above. The
+  orientation and the side where the numbers appear is controlled with \ref setType.
+
+  Use \ref QCPColorMap::setColorScale to connect a color map with a color scale. Once they are
+  connected, they share their gradient, data range and data scale type (\ref setGradient, \ref
+  setDataRange, \ref setDataScaleType). Multiple color maps may be associated with a single color
+  scale, to make them all synchronize these properties.
+
+  To have finer control over the number display and axis behaviour, you can directly access the
+  \ref axis. See the documentation of QCPAxis for details about configuring axes. For example, if
+  you want to change the number of automatically generated ticks, call
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-autotickcount
+
+  Placing a color scale next to the main axis rect works like with any other layout element:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-creation
+  In this case we have placed it to the right of the default axis rect, so it wasn't necessary to
+  call \ref setType, since \ref QCPAxis::atRight is already the default. The text next to the color
+  scale can be set with \ref setLabel.
+
+  For optimum appearance (like in the image above), it may be desirable to line up the axis rect and
+  the borders of the color scale. Use a \ref QCPMarginGroup to achieve this:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolorscale-margingroup
+
+  Color scales are initialized with a non-zero minimum top and bottom margin (\ref
+  setMinimumMargins), because vertical color scales are most common and the minimum top/bottom
+  margin makes sure it keeps some distance to the top/bottom widget border. So if you change to a
+  horizontal color scale by setting \ref setType to \ref QCPAxis::atBottom or \ref QCPAxis::atTop, you
+  might want to also change the minimum margins accordingly, e.g. <tt>setMinimumMargins(QMargins(6, 0, 6, 0))</tt>.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QCPAxis *QCPColorScale::axis() const
+
+  Returns the internal \ref QCPAxis instance of this color scale. You can access it to alter the
+  appearance and behaviour of the axis. \ref QCPColorScale duplicates some properties in its
+  interface for convenience. Those are \ref setDataRange (\ref QCPAxis::setRange), \ref
+  setDataScaleType (\ref QCPAxis::setScaleType), and the method \ref setLabel (\ref
+  QCPAxis::setLabel). As they each are connected, it does not matter whether you use the method on
+  the QCPColorScale or on its QCPAxis.
+
+  If the type of the color scale is changed with \ref setType, the axis returned by this method
+  will change, too, to either the left, right, bottom or top axis, depending on which type was set.
+*/
+
+/* end documentation of signals */
+/* start documentation of signals */
+
+/*! \fn void QCPColorScale::dataRangeChanged(QCPRange newRange);
+
+  This signal is emitted when the data range changes.
+
+  \see setDataRange
+*/
+
+/*! \fn void QCPColorScale::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
+
+  This signal is emitted when the data scale type changes.
+
+  \see setDataScaleType
+*/
+
+/*! \fn void QCPColorScale::gradientChanged(QCPColorGradient newGradient);
+
+  This signal is emitted when the gradient changes.
+
+  \see setGradient
+*/
+
+/* end documentation of signals */
+
+/*!
+  Constructs a new QCPColorScale.
+*/
+QCPColorScale::QCPColorScale(QCustomPlot *parentPlot) :
+  QCPLayoutElement(parentPlot),
+  mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
+  mDataScaleType(QCPAxis::stLinear),
+  mBarWidth(20),
+  mAxisRect(new QCPColorScaleAxisRectPrivate(this))
+{
+  setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used)
+  setType(QCPAxis::atRight);
+  setDataRange(QCPRange(0, 6));
+}
+
+QCPColorScale::~QCPColorScale()
+{
+  delete mAxisRect;
+}
+
+/* undocumented getter */
+QString QCPColorScale::label() const
+{
+  if (!mColorAxis)
+  {
+    qDebug() << Q_FUNC_INFO << "internal color axis undefined";
+    return QString();
+  }
+
+  return mColorAxis.data()->label();
+}
+
+/* undocumented getter */
+bool QCPColorScale::rangeDrag() const
+{
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return false;
+  }
+
+  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
+      mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
+      mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
+}
+
+/* undocumented getter */
+bool QCPColorScale::rangeZoom() const
+{
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return false;
+  }
+
+  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
+      mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
+      mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
+}
+
+/*!
+  Sets at which side of the color scale the axis is placed, and thus also its orientation.
+
+  Note that after setting \a type to a different value, the axis returned by \ref axis() will
+  be a different one. The new axis will adopt the following properties from the previous axis: The
+  range, scale type, log base and label.
+*/
+void QCPColorScale::setType(QCPAxis::AxisType type)
+{
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return;
+  }
+  if (mType != type)
+  {
+    mType = type;
+    QCPRange rangeTransfer(0, 6);
+    double logBaseTransfer = 10;
+    QString labelTransfer;
+    // revert some settings on old axis:
+    if (mColorAxis)
+    {
+      rangeTransfer = mColorAxis.data()->range();
+      labelTransfer = mColorAxis.data()->label();
+      logBaseTransfer = mColorAxis.data()->scaleLogBase();
+      mColorAxis.data()->setLabel(QString());
+      disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
+      disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
+    }
+    QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
+    foreach (QCPAxis::AxisType atype, allAxisTypes)
+    {
+      mAxisRect.data()->axis(atype)->setTicks(atype == mType);
+      mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
+    }
+    // set new mColorAxis pointer:
+    mColorAxis = mAxisRect.data()->axis(mType);
+    // transfer settings to new axis:
+    mColorAxis.data()->setRange(rangeTransfer); // transfer range of old axis to new one (necessary if axis changes from vertical to horizontal or vice versa)
+    mColorAxis.data()->setLabel(labelTransfer);
+    mColorAxis.data()->setScaleLogBase(logBaseTransfer); // scaleType is synchronized among axes in realtime via signals (connected in QCPColorScale ctor), so we only need to take care of log base here
+    connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
+    connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
+    mAxisRect.data()->setRangeDragAxes(QCPAxis::orientation(mType) == Qt::Horizontal ? mColorAxis.data() : 0,
+                                       QCPAxis::orientation(mType) == Qt::Vertical ? mColorAxis.data() : 0);
+  }
+}
+
+/*!
+  Sets the range spanned by the color gradient and that is shown by the axis in the color scale.
+
+  It is equivalent to calling QCPColorMap::setDataRange on any of the connected color maps. It is
+  also equivalent to directly accessing the \ref axis and setting its range with \ref
+  QCPAxis::setRange.
+
+  \see setDataScaleType, setGradient, rescaleDataRange
+*/
+void QCPColorScale::setDataRange(const QCPRange &dataRange)
+{
+  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
+  {
+    mDataRange = dataRange;
+    if (mColorAxis)
+      mColorAxis.data()->setRange(mDataRange);
+    emit dataRangeChanged(mDataRange);
+  }
+}
+
+/*!
+  Sets the scale type of the color scale, i.e. whether values are linearly associated with colors
+  or logarithmically.
+
+  It is equivalent to calling QCPColorMap::setDataScaleType on any of the connected color maps. It is
+  also equivalent to directly accessing the \ref axis and setting its scale type with \ref
+  QCPAxis::setScaleType.
+
+  \see setDataRange, setGradient
+*/
+void QCPColorScale::setDataScaleType(QCPAxis::ScaleType scaleType)
+{
+  if (mDataScaleType != scaleType)
+  {
+    mDataScaleType = scaleType;
+    if (mColorAxis)
+      mColorAxis.data()->setScaleType(mDataScaleType);
+    if (mDataScaleType == QCPAxis::stLogarithmic)
+      setDataRange(mDataRange.sanitizedForLogScale());
+    emit dataScaleTypeChanged(mDataScaleType);
+  }
+}
+
+/*!
+  Sets the color gradient that will be used to represent data values.
+
+  It is equivalent to calling QCPColorMap::setGradient on any of the connected color maps.
+
+  \see setDataRange, setDataScaleType
+*/
+void QCPColorScale::setGradient(const QCPColorGradient &gradient)
+{
+  if (mGradient != gradient)
+  {
+    mGradient = gradient;
+    if (mAxisRect)
+      mAxisRect.data()->mGradientImageInvalidated = true;
+    emit gradientChanged(mGradient);
+  }
+}
+
+/*!
+  Sets the axis label of the color scale. This is equivalent to calling \ref QCPAxis::setLabel on
+  the internal \ref axis.
+*/
+void QCPColorScale::setLabel(const QString &str)
+{
+  if (!mColorAxis)
+  {
+    qDebug() << Q_FUNC_INFO << "internal color axis undefined";
+    return;
+  }
+
+  mColorAxis.data()->setLabel(str);
+}
+
+/*!
+  Sets the width (or height, for horizontal color scales) the bar where the gradient is displayed
+  will have.
+*/
+void QCPColorScale::setBarWidth(int width)
+{
+  mBarWidth = width;
+}
+
+/*!
+  Sets whether the user can drag the data range (\ref setDataRange).
+
+  Note that \ref QCP::iRangeDrag must be in the QCustomPlot's interactions (\ref
+  QCustomPlot::setInteractions) to allow range dragging.
+*/
+void QCPColorScale::setRangeDrag(bool enabled)
+{
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return;
+  }
+
+  if (enabled)
+    mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
+  else
+    mAxisRect.data()->setRangeDrag(0);
+}
+
+/*!
+  Sets whether the user can zoom the data range (\ref setDataRange) by scrolling the mouse wheel.
+
+  Note that \ref QCP::iRangeZoom must be in the QCustomPlot's interactions (\ref
+  QCustomPlot::setInteractions) to allow range dragging.
+*/
+void QCPColorScale::setRangeZoom(bool enabled)
+{
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return;
+  }
+
+  if (enabled)
+    mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
+  else
+    mAxisRect.data()->setRangeZoom(0);
+}
+
+/*!
+  Returns a list of all the color maps associated with this color scale.
+*/
+QList<QCPColorMap*> QCPColorScale::colorMaps() const
+{
+  QList<QCPColorMap*> result;
+  for (int i=0; i<mParentPlot->plottableCount(); ++i)
+  {
+    if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
+      if (cm->colorScale() == this)
+        result.append(cm);
+  }
+  return result;
+}
+
+/*!
+  Changes the data range such that all color maps associated with this color scale are fully mapped
+  to the gradient in the data dimension.
+
+  \see setDataRange
+*/
+void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
+{
+  QList<QCPColorMap*> maps = colorMaps();
+  QCPRange newRange;
+  bool haveRange = false;
+  int sign = 0; // TODO: should change this to QCPAbstractPlottable::SignDomain later (currently is protected, maybe move to QCP namespace)
+  if (mDataScaleType == QCPAxis::stLogarithmic)
+    sign = (mDataRange.upper < 0 ? -1 : 1);
+  for (int i=0; i<maps.size(); ++i)
+  {
+    if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
+      continue;
+    QCPRange mapRange;
+    if (maps.at(i)->colorScale() == this)
+    {
+      bool currentFoundRange = true;
+      mapRange = maps.at(i)->data()->dataBounds();
+      if (sign == 1)
+      {
+        if (mapRange.lower <= 0 && mapRange.upper > 0)
+          mapRange.lower = mapRange.upper*1e-3;
+        else if (mapRange.lower <= 0 && mapRange.upper <= 0)
+          currentFoundRange = false;
+      } else if (sign == -1)
+      {
+        if (mapRange.upper >= 0 && mapRange.lower < 0)
+          mapRange.upper = mapRange.lower*1e-3;
+        else if (mapRange.upper >= 0 && mapRange.lower >= 0)
+          currentFoundRange = false;
+      }
+      if (currentFoundRange)
+      {
+        if (!haveRange)
+          newRange = mapRange;
+        else
+          newRange.expand(mapRange);
+        haveRange = true;
+      }
+    }
+  }
+  if (haveRange)
+  {
+    if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data
+    {
+      double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
+      if (mDataScaleType == QCPAxis::stLinear)
+      {
+        newRange.lower = center-mDataRange.size()/2.0;
+        newRange.upper = center+mDataRange.size()/2.0;
+      } else // mScaleType == stLogarithmic
+      {
+        newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
+        newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
+      }
+    }
+    setDataRange(newRange);
+  }
+}
+
+/* inherits documentation from base class */
+void QCPColorScale::update(UpdatePhase phase)
+{
+  QCPLayoutElement::update(phase);
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return;
+  }
+
+  mAxisRect.data()->update(phase);
+
+  switch (phase)
+  {
+    case upMargins:
+    {
+      if (mType == QCPAxis::atBottom || mType == QCPAxis::atTop)
+      {
+        setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
+        setMinimumSize(0,               mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom()+margins().top()+margins().bottom());
+      } else
+      {
+        setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), QWIDGETSIZE_MAX);
+        setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right()+margins().left()+margins().right(), 0);
+      }
+      break;
+    }
+    case upLayout:
+    {
+      mAxisRect.data()->setOuterRect(rect());
+      break;
+    }
+    default: break;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPColorScale::applyDefaultAntialiasingHint(QCPPainter *painter) const
+{
+  painter->setAntialiasing(false);
+}
+
+/* inherits documentation from base class */
+void QCPColorScale::mousePressEvent(QMouseEvent *event)
+{
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return;
+  }
+  mAxisRect.data()->mousePressEvent(event);
+}
+
+/* inherits documentation from base class */
+void QCPColorScale::mouseMoveEvent(QMouseEvent *event)
+{
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return;
+  }
+  mAxisRect.data()->mouseMoveEvent(event);
+}
+
+/* inherits documentation from base class */
+void QCPColorScale::mouseReleaseEvent(QMouseEvent *event)
+{
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return;
+  }
+  mAxisRect.data()->mouseReleaseEvent(event);
+}
+
+/* inherits documentation from base class */
+void QCPColorScale::wheelEvent(QWheelEvent *event)
+{
+  if (!mAxisRect)
+  {
+    qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
+    return;
+  }
+  mAxisRect.data()->wheelEvent(event);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPColorScaleAxisRectPrivate
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPColorScaleAxisRectPrivate
+
+  \internal
+  \brief An axis rect subclass for use in a QCPColorScale
+
+  This is a private class and not part of the public QCustomPlot interface.
+
+  It provides the axis rect functionality for the QCPColorScale class.
+*/
+
+
+/*!
+  Creates a new instance, as a child of \a parentColorScale.
+*/
+QCPColorScaleAxisRectPrivate::QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale) :
+  QCPAxisRect(parentColorScale->parentPlot(), true),
+  mParentColorScale(parentColorScale),
+  mGradientImageInvalidated(true)
+{
+  setParentLayerable(parentColorScale);
+  setMinimumMargins(QMargins(0, 0, 0, 0));
+  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
+  foreach (QCPAxis::AxisType type, allAxisTypes)
+  {
+    axis(type)->setVisible(true);
+    axis(type)->grid()->setVisible(false);
+    axis(type)->setPadding(0);
+    connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
+    connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
+  }
+
+  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
+  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
+  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
+  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
+  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
+  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
+  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
+  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
+
+  // make layer transfers of color scale transfer to axis rect and axes
+  // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
+  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
+  foreach (QCPAxis::AxisType type, allAxisTypes)
+    connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
+}
+
+/*! \internal
+  Updates the color gradient image if necessary, by calling \ref updateGradientImage, then draws
+  it. Then the axes are drawn by calling the \ref QCPAxisRect::draw base class implementation.
+*/
+void QCPColorScaleAxisRectPrivate::draw(QCPPainter *painter)
+{
+  if (mGradientImageInvalidated)
+    updateGradientImage();
+
+  bool mirrorHorz = false;
+  bool mirrorVert = false;
+  if (mParentColorScale->mColorAxis)
+  {
+    mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
+    mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
+  }
+
+  painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
+  QCPAxisRect::draw(painter);
+}
+
+/*! \internal
+
+  Uses the current gradient of the parent \ref QCPColorScale (specified in the constructor) to
+  generate a gradient image. This gradient image will be used in the \ref draw method.
+*/
+void QCPColorScaleAxisRectPrivate::updateGradientImage()
+{
+  if (rect().isEmpty())
+    return;
+
+  int n = mParentColorScale->mGradient.levelCount();
+  int w, h;
+  QVector<double> data(n);
+  for (int i=0; i<n; ++i)
+    data[i] = i;
+  if (mParentColorScale->mType == QCPAxis::atBottom || mParentColorScale->mType == QCPAxis::atTop)
+  {
+    w = n;
+    h = rect().height();
+    mGradientImage = QImage(w, h, QImage::Format_RGB32);
+    QVector<QRgb*> pixels;
+    for (int y=0; y<h; ++y)
+      pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
+    mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
+    for (int y=1; y<h; ++y)
+      memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
+  } else
+  {
+    w = rect().width();
+    h = n;
+    mGradientImage = QImage(w, h, QImage::Format_RGB32);
+    for (int y=0; y<h; ++y)
+    {
+      QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
+      const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
+      for (int x=0; x<w; ++x)
+        pixels[x] = lineColor;
+    }
+  }
+  mGradientImageInvalidated = false;
+}
+
+/*! \internal
+
+  This slot is connected to the selectionChanged signals of the four axes in the constructor. It
+  synchronizes the selection state of the axes.
+*/
+void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
+{
+  // axis bases of four axes shall always (de-)selected synchronously:
+  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
+  foreach (QCPAxis::AxisType type, allAxisTypes)
+  {
+    if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
+      if (senderAxis->axisType() == type)
+        continue;
+
+    if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
+    {
+      if (selectedParts.testFlag(QCPAxis::spAxis))
+        axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
+      else
+        axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
+    }
+  }
+}
+
+/*! \internal
+
+  This slot is connected to the selectableChanged signals of the four axes in the constructor. It
+  synchronizes the selectability of the axes.
+*/
+void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
+{
+  // synchronize axis base selectability:
+  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
+  foreach (QCPAxis::AxisType type, allAxisTypes)
+  {
+    if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
+      if (senderAxis->axisType() == type)
+        continue;
+
+    if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
+    {
+      if (selectableParts.testFlag(QCPAxis::spAxis))
+        axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
+      else
+        axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
+    }
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPData
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPData
+  \brief Holds the data of one single data point for QCPGraph.
+
+  The container for storing multiple data points is \ref QCPDataMap.
+
+  The stored data is:
+  \li \a key: coordinate on the key axis of this data point
+  \li \a value: coordinate on the value axis of this data point
+  \li \a keyErrorMinus: negative error in the key dimension (for error bars)
+  \li \a keyErrorPlus: positive error in the key dimension (for error bars)
+  \li \a valueErrorMinus: negative error in the value dimension (for error bars)
+  \li \a valueErrorPlus: positive error in the value dimension (for error bars)
+
+  \see QCPDataMap
+*/
+
+/*!
+  Constructs a data point with key, value and all errors set to zero.
+*/
+QCPData::QCPData() :
+  key(0),
+  value(0),
+  keyErrorPlus(0),
+  keyErrorMinus(0),
+  valueErrorPlus(0),
+  valueErrorMinus(0)
+{
+}
+
+/*!
+  Constructs a data point with the specified \a key and \a value. All errors are set to zero.
+*/
+QCPData::QCPData(double key, double value) :
+  key(key),
+  value(value),
+  keyErrorPlus(0),
+  keyErrorMinus(0),
+  valueErrorPlus(0),
+  valueErrorMinus(0)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPGraph
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPGraph
+  \brief A plottable representing a graph in a plot.
+
+  \image html QCPGraph.png
+
+  Usually QCustomPlot creates graphs internally via QCustomPlot::addGraph and the resulting
+  instance is accessed via QCustomPlot::graph.
+
+  To plot data, assign it with the \ref setData or \ref addData functions. Alternatively, you can
+  also access and modify the graph's data via the \ref data method, which returns a pointer to the
+  internal \ref QCPDataMap.
+
+  Graphs are used to display single-valued data. Single-valued means that there should only be one
+  data point per unique key coordinate. In other words, the graph can't have \a loops. If you do
+  want to plot non-single-valued curves, rather use the QCPCurve plottable.
+
+  Gaps in the graph line can be created by adding data points with NaN as value
+  (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be
+  separated.
+
+  \section appearance Changing the appearance
+
+  The appearance of the graph is mainly determined by the line style, scatter style, brush and pen
+  of the graph (\ref setLineStyle, \ref setScatterStyle, \ref setBrush, \ref setPen).
+
+  \subsection filling Filling under or between graphs
+
+  QCPGraph knows two types of fills: Normal graph fills towards the zero-value-line parallel to
+  the key axis of the graph, and fills between two graphs, called channel fills. To enable a fill,
+  just set a brush with \ref setBrush which is neither Qt::NoBrush nor fully transparent.
+
+  By default, a normal fill towards the zero-value-line will be drawn. To set up a channel fill
+  between this graph and another one, call \ref setChannelFillGraph with the other graph as
+  parameter.
+
+  \see QCustomPlot::addGraph, QCustomPlot::graph
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn QCPDataMap *QCPGraph::data() const
+
+  Returns a pointer to the internal data storage of type \ref QCPDataMap. You may use it to
+  directly manipulate the data, which may be more convenient and faster than using the regular \ref
+  setData or \ref addData methods, in certain situations.
+*/
+
+/* end of documentation of inline functions */
+
+/*!
+  Constructs a graph which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
+  axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
+  the same orientation. If either of these restrictions is violated, a corresponding message is
+  printed to the debug output (qDebug), the construction is not aborted, though.
+
+  The constructed QCPGraph can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
+  then takes ownership of the graph.
+
+  To directly create a graph inside a plot, you can also use the simpler QCustomPlot::addGraph function.
+*/
+QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis)
+{
+  mData = new QCPDataMap;
+
+  setPen(QPen(Qt::blue, 0));
+  setErrorPen(QPen(Qt::black));
+  setBrush(Qt::NoBrush);
+  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
+  setSelectedBrush(Qt::NoBrush);
+
+  setLineStyle(lsLine);
+  setErrorType(etNone);
+  setErrorBarSize(6);
+  setErrorBarSkipSymbol(true);
+  setChannelFillGraph(0);
+  setAdaptiveSampling(true);
+}
+
+QCPGraph::~QCPGraph()
+{
+  delete mData;
+}
+
+/*!
+  Replaces the current data with the provided \a data.
+
+  If \a copy is set to true, data points in \a data will only be copied. if false, the graph
+  takes ownership of the passed data and replaces the internal data pointer with it. This is
+  significantly faster than copying for large datasets.
+
+  Alternatively, you can also access and modify the graph's data via the \ref data method, which
+  returns a pointer to the internal \ref QCPDataMap.
+*/
+void QCPGraph::setData(QCPDataMap *data, bool copy)
+{
+  if (mData == data)
+  {
+    qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
+    return;
+  }
+  if (copy)
+  {
+    *mData = *data;
+  } else
+  {
+    delete mData;
+    mData = data;
+  }
+}
+
+/*! \overload
+
+  Replaces the current data with the provided points in \a key and \a value pairs. The provided
+  vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+*/
+void QCPGraph::setData(const QVector<double> &key, const QVector<double> &value)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    mData->insertMulti(newData.key, newData);
+  }
+}
+
+/*!
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  symmetrical value error of the data points are set to the values in \a valueError.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+
+  For asymmetrical errors (plus different from minus), see the overloaded version of this function.
+*/
+void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, valueError.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.valueErrorMinus = valueError[i];
+    newData.valueErrorPlus = valueError[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  \overload
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  negative value error of the data points are set to the values in \a valueErrorMinus, the positive
+  value error to \a valueErrorPlus.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+*/
+void QCPGraph::setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, valueErrorMinus.size());
+  n = qMin(n, valueErrorPlus.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.valueErrorMinus = valueErrorMinus[i];
+    newData.valueErrorPlus = valueErrorPlus[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  symmetrical key error of the data points are set to the values in \a keyError.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+
+  For asymmetrical errors (plus different from minus), see the overloaded version of this function.
+*/
+void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, keyError.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.keyErrorMinus = keyError[i];
+    newData.keyErrorPlus = keyError[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  \overload
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  negative key error of the data points are set to the values in \a keyErrorMinus, the positive
+  key error to \a keyErrorPlus.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+*/
+void QCPGraph::setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, keyErrorMinus.size());
+  n = qMin(n, keyErrorPlus.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.keyErrorMinus = keyErrorMinus[i];
+    newData.keyErrorPlus = keyErrorPlus[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  symmetrical key and value errors of the data points are set to the values in \a keyError and \a valueError.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+
+  For asymmetrical errors (plus different from minus), see the overloaded version of this function.
+*/
+void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, valueError.size());
+  n = qMin(n, keyError.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.keyErrorMinus = keyError[i];
+    newData.keyErrorPlus = keyError[i];
+    newData.valueErrorMinus = valueError[i];
+    newData.valueErrorPlus = valueError[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+/*!
+  \overload
+  Replaces the current data with the provided points in \a key and \a value pairs. Additionally the
+  negative key and value errors of the data points are set to the values in \a keyErrorMinus and \a valueErrorMinus. The positive
+  key and value errors are set to the values in \a keyErrorPlus \a valueErrorPlus.
+  For error bars to show appropriately, see \ref setErrorType.
+  The provided vectors should have equal length. Else, the number of added points will be the size of the
+  smallest vector.
+*/
+void QCPGraph::setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  n = qMin(n, valueErrorMinus.size());
+  n = qMin(n, valueErrorPlus.size());
+  n = qMin(n, keyErrorMinus.size());
+  n = qMin(n, keyErrorPlus.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    newData.keyErrorMinus = keyErrorMinus[i];
+    newData.keyErrorPlus = keyErrorPlus[i];
+    newData.valueErrorMinus = valueErrorMinus[i];
+    newData.valueErrorPlus = valueErrorPlus[i];
+    mData->insertMulti(key[i], newData);
+  }
+}
+
+
+/*!
+  Sets how the single data points are connected in the plot. For scatter-only plots, set \a ls to
+  \ref lsNone and \ref setScatterStyle to the desired scatter style.
+
+  \see setScatterStyle
+*/
+void QCPGraph::setLineStyle(LineStyle ls)
+{
+  mLineStyle = ls;
+}
+
+/*!
+  Sets the visual appearance of single data points in the plot. If set to \ref QCPScatterStyle::ssNone, no scatter points
+  are drawn (e.g. for line-only-plots with appropriate line style).
+
+  \see QCPScatterStyle, setLineStyle
+*/
+void QCPGraph::setScatterStyle(const QCPScatterStyle &style)
+{
+  mScatterStyle = style;
+}
+
+/*!
+  Sets which kind of error bars (Key Error, Value Error or both) should be drawn on each data
+  point. If you set \a errorType to something other than \ref etNone, make sure to actually pass
+  error data via the specific setData functions along with the data points (e.g. \ref
+  setDataValueError, \ref setDataKeyError, \ref setDataBothError).
+
+  \see ErrorType
+*/
+void QCPGraph::setErrorType(ErrorType errorType)
+{
+  mErrorType = errorType;
+}
+
+/*!
+  Sets the pen with which the error bars will be drawn.
+  \see setErrorBarSize, setErrorType
+*/
+void QCPGraph::setErrorPen(const QPen &pen)
+{
+  mErrorPen = pen;
+}
+
+/*!
+  Sets the width of the handles at both ends of an error bar in pixels.
+*/
+void QCPGraph::setErrorBarSize(double size)
+{
+  mErrorBarSize = size;
+}
+
+/*!
+  If \a enabled is set to true, the error bar will not be drawn as a solid line under the scatter symbol but
+  leave some free space around the symbol.
+
+  This feature uses the current scatter size (\ref QCPScatterStyle::setSize) to determine the size
+  of the area to leave blank. So when drawing Pixmaps as scatter points (\ref
+  QCPScatterStyle::ssPixmap), the scatter size must be set manually to a value corresponding to the
+  size of the Pixmap, if the error bars should leave gaps to its boundaries.
+
+  \ref setErrorType, setErrorBarSize, setScatterStyle
+*/
+void QCPGraph::setErrorBarSkipSymbol(bool enabled)
+{
+  mErrorBarSkipSymbol = enabled;
+}
+
+/*!
+  Sets the target graph for filling the area between this graph and \a targetGraph with the current
+  brush (\ref setBrush).
+
+  When \a targetGraph is set to 0, a normal graph fill to the zero-value-line will be shown. To
+  disable any filling, set the brush to Qt::NoBrush.
+
+  \see setBrush
+*/
+void QCPGraph::setChannelFillGraph(QCPGraph *targetGraph)
+{
+  // prevent setting channel target to this graph itself:
+  if (targetGraph == this)
+  {
+    qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
+    mChannelFillGraph = 0;
+    return;
+  }
+  // prevent setting channel target to a graph not in the plot:
+  if (targetGraph && targetGraph->mParentPlot != mParentPlot)
+  {
+    qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
+    mChannelFillGraph = 0;
+    return;
+  }
+
+  mChannelFillGraph = targetGraph;
+}
+
+/*!
+  Sets whether adaptive sampling shall be used when plotting this graph. QCustomPlot's adaptive
+  sampling technique can drastically improve the replot performance for graphs with a larger number
+  of points (e.g. above 10,000), without notably changing the appearance of the graph.
+
+  By default, adaptive sampling is enabled. Even if enabled, QCustomPlot decides whether adaptive
+  sampling shall actually be used on a per-graph basis. So leaving adaptive sampling enabled has no
+  disadvantage in almost all cases.
+
+  \image html adaptive-sampling-line.png "A line plot of 500,000 points without and with adaptive sampling"
+
+  As can be seen, line plots experience no visual degradation from adaptive sampling. Outliers are
+  reproduced reliably, as well as the overall shape of the data set. The replot time reduces
+  dramatically though. This allows QCustomPlot to display large amounts of data in realtime.
+
+  \image html adaptive-sampling-scatter.png "A scatter plot of 100,000 points without and with adaptive sampling"
+
+  Care must be taken when using high-density scatter plots in combination with adaptive sampling.
+  The adaptive sampling algorithm treats scatter plots more carefully than line plots which still
+  gives a significant reduction of replot times, but not quite as much as for line plots. This is
+  because scatter plots inherently need more data points to be preserved in order to still resemble
+  the original, non-adaptive-sampling plot. As shown above, the results still aren't quite
+  identical, as banding occurs for the outer data points. This is in fact intentional, such that
+  the boundaries of the data cloud stay visible to the viewer. How strong the banding appears,
+  depends on the point density, i.e. the number of points in the plot.
+
+  For some situations with scatter plots it might thus be desirable to manually turn adaptive
+  sampling off. For example, when saving the plot to disk. This can be achieved by setting \a
+  enabled to false before issuing a command like \ref QCustomPlot::savePng, and setting \a enabled
+  back to true afterwards.
+*/
+void QCPGraph::setAdaptiveSampling(bool enabled)
+{
+  mAdaptiveSampling = enabled;
+}
+
+/*!
+  Adds the provided data points in \a dataMap to the current data.
+
+  Alternatively, you can also access and modify the graph's data via the \ref data method, which
+  returns a pointer to the internal \ref QCPDataMap.
+
+  \see removeData
+*/
+void QCPGraph::addData(const QCPDataMap &dataMap)
+{
+  mData->unite(dataMap);
+}
+
+/*! \overload
+  Adds the provided single data point in \a data to the current data.
+
+  Alternatively, you can also access and modify the graph's data via the \ref data method, which
+  returns a pointer to the internal \ref QCPDataMap.
+
+  \see removeData
+*/
+void QCPGraph::addData(const QCPData &data)
+{
+  mData->insertMulti(data.key, data);
+}
+
+/*! \overload
+  Adds the provided single data point as \a key and \a value pair to the current data.
+
+  Alternatively, you can also access and modify the graph's data via the \ref data method, which
+  returns a pointer to the internal \ref QCPDataMap.
+
+  \see removeData
+*/
+void QCPGraph::addData(double key, double value)
+{
+  QCPData newData;
+  newData.key = key;
+  newData.value = value;
+  mData->insertMulti(newData.key, newData);
+}
+
+/*! \overload
+  Adds the provided data points as \a key and \a value pairs to the current data.
+
+  Alternatively, you can also access and modify the graph's data via the \ref data method, which
+  returns a pointer to the internal \ref QCPDataMap.
+
+  \see removeData
+*/
+void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values)
+{
+  int n = qMin(keys.size(), values.size());
+  QCPData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = keys[i];
+    newData.value = values[i];
+    mData->insertMulti(newData.key, newData);
+  }
+}
+
+/*!
+  Removes all data points with keys smaller than \a key.
+  \see addData, clearData
+*/
+void QCPGraph::removeDataBefore(double key)
+{
+  QCPDataMap::iterator it = mData->begin();
+  while (it != mData->end() && it.key() < key)
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with keys greater than \a key.
+  \see addData, clearData
+*/
+void QCPGraph::removeDataAfter(double key)
+{
+  if (mData->isEmpty()) return;
+  QCPDataMap::iterator it = mData->upperBound(key);
+  while (it != mData->end())
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with keys between \a fromKey and \a toKey.
+  if \a fromKey is greater or equal to \a toKey, the function does nothing. To remove
+  a single data point with known key, use \ref removeData(double key).
+
+  \see addData, clearData
+*/
+void QCPGraph::removeData(double fromKey, double toKey)
+{
+  if (fromKey >= toKey || mData->isEmpty()) return;
+  QCPDataMap::iterator it = mData->upperBound(fromKey);
+  QCPDataMap::iterator itEnd = mData->upperBound(toKey);
+  while (it != itEnd)
+    it = mData->erase(it);
+}
+
+/*! \overload
+
+  Removes a single data point at \a key. If the position is not known with absolute precision,
+  consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval around
+  the suspected position, depeding on the precision with which the key is known.
+
+  \see addData, clearData
+*/
+void QCPGraph::removeData(double key)
+{
+  mData->remove(key);
+}
+
+/*!
+  Removes all data points.
+  \see removeData, removeDataAfter, removeDataBefore
+*/
+void QCPGraph::clearData()
+{
+  mData->clear();
+}
+
+/* inherits documentation from base class */
+double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if ((onlySelectable && !mSelectable) || mData->isEmpty())
+    return -1;
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
+
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+    return pointDistance(pos);
+  else
+    return -1;
+}
+
+/*! \overload
+
+  Allows to define whether error bars are taken into consideration when determining the new axis
+  range.
+
+  \see rescaleKeyAxis, rescaleValueAxis, QCPAbstractPlottable::rescaleAxes, QCustomPlot::rescaleAxes
+*/
+void QCPGraph::rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const
+{
+  rescaleKeyAxis(onlyEnlarge, includeErrorBars);
+  rescaleValueAxis(onlyEnlarge, includeErrorBars);
+}
+
+/*! \overload
+
+  Allows to define whether error bars (of kind \ref QCPGraph::etKey) are taken into consideration
+  when determining the new axis range.
+
+  \see rescaleAxes, QCPAbstractPlottable::rescaleKeyAxis
+*/
+void QCPGraph::rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const
+{
+  // this code is a copy of QCPAbstractPlottable::rescaleKeyAxis with the only change
+  // that getKeyRange is passed the includeErrorBars value.
+  if (mData->isEmpty()) return;
+
+  QCPAxis *keyAxis = mKeyAxis.data();
+  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
+
+  SignDomain signDomain = sdBoth;
+  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
+    signDomain = (keyAxis->range().upper < 0 ? sdNegative : sdPositive);
+
+  bool foundRange;
+  QCPRange newRange = getKeyRange(foundRange, signDomain, includeErrorBars);
+
+  if (foundRange)
+  {
+    if (onlyEnlarge)
+    {
+      if (keyAxis->range().lower < newRange.lower)
+        newRange.lower = keyAxis->range().lower;
+      if (keyAxis->range().upper > newRange.upper)
+        newRange.upper = keyAxis->range().upper;
+    }
+    keyAxis->setRange(newRange);
+  }
+}
+
+/*! \overload
+
+  Allows to define whether error bars (of kind \ref QCPGraph::etValue) are taken into consideration
+  when determining the new axis range.
+
+  \see rescaleAxes, QCPAbstractPlottable::rescaleValueAxis
+*/
+void QCPGraph::rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const
+{
+  // this code is a copy of QCPAbstractPlottable::rescaleValueAxis with the only change
+  // is that getValueRange is passed the includeErrorBars value.
+  if (mData->isEmpty()) return;
+
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!valueAxis) { qDebug() << Q_FUNC_INFO << "invalid value axis"; return; }
+
+  SignDomain signDomain = sdBoth;
+  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
+    signDomain = (valueAxis->range().upper < 0 ? sdNegative : sdPositive);
+
+  bool foundRange;
+  QCPRange newRange = getValueRange(foundRange, signDomain, includeErrorBars);
+
+  if (foundRange)
+  {
+    if (onlyEnlarge)
+    {
+      if (valueAxis->range().lower < newRange.lower)
+        newRange.lower = valueAxis->range().lower;
+      if (valueAxis->range().upper > newRange.upper)
+        newRange.upper = valueAxis->range().upper;
+    }
+    valueAxis->setRange(newRange);
+  }
+}
+
+/* inherits documentation from base class */
+void QCPGraph::draw(QCPPainter *painter)
+{
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  if (mKeyAxis.data()->range().size() <= 0 || mData->isEmpty()) return;
+  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
+
+  // allocate line and (if necessary) point vectors:
+  QVector<QPointF> *lineData = new QVector<QPointF>;
+  QVector<QCPData> *scatterData = 0;
+  if (!mScatterStyle.isNone())
+    scatterData = new QVector<QCPData>;
+
+  // fill vectors with data appropriate to plot style:
+  getPlotData(lineData, scatterData);
+
+  // check data validity if flag set:
+#ifdef QCUSTOMPLOT_CHECK_DATA
+  QCPDataMap::const_iterator it;
+  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
+  {
+    if (QCP::isInvalidData(it.value().key, it.value().value) ||
+        QCP::isInvalidData(it.value().keyErrorPlus, it.value().keyErrorMinus) ||
+        QCP::isInvalidData(it.value().valueErrorPlus, it.value().valueErrorPlus))
+      qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
+  }
+#endif
+
+  // draw fill of graph:
+  drawFill(painter, lineData);
+
+  // draw line:
+  if (mLineStyle == lsImpulse)
+    drawImpulsePlot(painter, lineData);
+  else if (mLineStyle != lsNone)
+    drawLinePlot(painter, lineData); // also step plots can be drawn as a line plot
+
+  // draw scatters:
+  if (scatterData)
+    drawScatterPlot(painter, scatterData);
+
+  // free allocated line and point vectors:
+  delete lineData;
+  if (scatterData)
+    delete scatterData;
+}
+
+/* inherits documentation from base class */
+void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
+{
+  // draw fill:
+  if (mBrush.style() != Qt::NoBrush)
+  {
+    applyFillAntialiasingHint(painter);
+    painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
+  }
+  // draw line vertically centered:
+  if (mLineStyle != lsNone)
+  {
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mPen);
+    painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
+  }
+  // draw scatter symbol:
+  if (!mScatterStyle.isNone())
+  {
+    applyScattersAntialiasingHint(painter);
+    // scale scatter pixmap if it's too large to fit in legend icon rect:
+    if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
+    {
+      QCPScatterStyle scaledStyle(mScatterStyle);
+      scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
+      scaledStyle.applyTo(painter, mPen);
+      scaledStyle.drawShape(painter, QRectF(rect).center());
+    } else
+    {
+      mScatterStyle.applyTo(painter, mPen);
+      mScatterStyle.drawShape(painter, QRectF(rect).center());
+    }
+  }
+}
+
+/*! \internal
+
+  This function branches out to the line style specific "get(...)PlotData" functions, according to
+  the line style of the graph.
+
+  \a lineData will be filled with raw points that will be drawn with the according draw functions,
+  e.g. \ref drawLinePlot and \ref drawImpulsePlot. These aren't necessarily the original data
+  points, since for step plots for example, additional points are needed for drawing lines that
+  make up steps. If the line style of the graph is \ref lsNone, the \a lineData vector will be left
+  untouched.
+
+  \a scatterData will be filled with the original data points so \ref drawScatterPlot can draw the
+  scatter symbols accordingly. If no scatters need to be drawn, i.e. the scatter style's shape is
+  \ref QCPScatterStyle::ssNone, pass 0 as \a scatterData, and this step will be skipped.
+
+  \see getScatterPlotData, getLinePlotData, getStepLeftPlotData, getStepRightPlotData,
+  getStepCenterPlotData, getImpulsePlotData
+*/
+void QCPGraph::getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const
+{
+  switch(mLineStyle)
+  {
+    case lsNone: getScatterPlotData(scatterData); break;
+    case lsLine: getLinePlotData(lineData, scatterData); break;
+    case lsStepLeft: getStepLeftPlotData(lineData, scatterData); break;
+    case lsStepRight: getStepRightPlotData(lineData, scatterData); break;
+    case lsStepCenter: getStepCenterPlotData(lineData, scatterData); break;
+    case lsImpulse: getImpulsePlotData(lineData, scatterData); break;
+  }
+}
+
+/*! \internal
+
+  If line style is \ref lsNone and the scatter style's shape is not \ref QCPScatterStyle::ssNone,
+  this function serves at providing the visible data points in \a scatterData, so the \ref
+  drawScatterPlot function can draw the scatter points accordingly.
+
+  If line style is not \ref lsNone, this function is not called and the data for the scatter points
+  are (if needed) calculated inside the corresponding other "get(...)PlotData" functions.
+
+  \see drawScatterPlot
+*/
+void QCPGraph::getScatterPlotData(QVector<QCPData> *scatterData) const
+{
+  getPreparedData(0, scatterData);
+}
+
+/*! \internal
+
+  Places the raw data points needed for a normal linearly connected graph in \a linePixelData.
+
+  As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
+  points that are visible for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
+  scatterData, and the function will skip filling the vector.
+
+  \see drawLinePlot
+*/
+void QCPGraph::getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
+
+  QVector<QCPData> lineData;
+  getPreparedData(&lineData, scatterData);
+  linePixelData->reserve(lineData.size()+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
+  linePixelData->resize(lineData.size());
+
+  // transform lineData points to pixels:
+  if (keyAxis->orientation() == Qt::Vertical)
+  {
+    for (int i=0; i<lineData.size(); ++i)
+    {
+      (*linePixelData)[i].setX(valueAxis->coordToPixel(lineData.at(i).value));
+      (*linePixelData)[i].setY(keyAxis->coordToPixel(lineData.at(i).key));
+    }
+  } else // key axis is horizontal
+  {
+    for (int i=0; i<lineData.size(); ++i)
+    {
+      (*linePixelData)[i].setX(keyAxis->coordToPixel(lineData.at(i).key));
+      (*linePixelData)[i].setY(valueAxis->coordToPixel(lineData.at(i).value));
+    }
+  }
+}
+
+/*!
+  \internal
+  Places the raw data points needed for a step plot with left oriented steps in \a lineData.
+
+  As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
+  points that are visible for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
+  scatterData, and the function will skip filling the vector.
+
+  \see drawLinePlot
+*/
+void QCPGraph::getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
+
+  QVector<QCPData> lineData;
+  getPreparedData(&lineData, scatterData);
+  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
+  linePixelData->resize(lineData.size()*2);
+
+  // calculate steps from lineData and transform to pixel coordinates:
+  if (keyAxis->orientation() == Qt::Vertical)
+  {
+    double lastValue = valueAxis->coordToPixel(lineData.first().value);
+    double key;
+    for (int i=0; i<lineData.size(); ++i)
+    {
+      key = keyAxis->coordToPixel(lineData.at(i).key);
+      (*linePixelData)[i*2+0].setX(lastValue);
+      (*linePixelData)[i*2+0].setY(key);
+      lastValue = valueAxis->coordToPixel(lineData.at(i).value);
+      (*linePixelData)[i*2+1].setX(lastValue);
+      (*linePixelData)[i*2+1].setY(key);
+    }
+  } else // key axis is horizontal
+  {
+    double lastValue = valueAxis->coordToPixel(lineData.first().value);
+    double key;
+    for (int i=0; i<lineData.size(); ++i)
+    {
+      key = keyAxis->coordToPixel(lineData.at(i).key);
+      (*linePixelData)[i*2+0].setX(key);
+      (*linePixelData)[i*2+0].setY(lastValue);
+      lastValue = valueAxis->coordToPixel(lineData.at(i).value);
+      (*linePixelData)[i*2+1].setX(key);
+      (*linePixelData)[i*2+1].setY(lastValue);
+    }
+  }
+}
+
+/*!
+  \internal
+  Places the raw data points needed for a step plot with right oriented steps in \a lineData.
+
+  As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
+  points that are visible for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
+  scatterData, and the function will skip filling the vector.
+
+  \see drawLinePlot
+*/
+void QCPGraph::getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
+
+  QVector<QCPData> lineData;
+  getPreparedData(&lineData, scatterData);
+  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
+  linePixelData->resize(lineData.size()*2);
+
+  // calculate steps from lineData and transform to pixel coordinates:
+  if (keyAxis->orientation() == Qt::Vertical)
+  {
+    double lastKey = keyAxis->coordToPixel(lineData.first().key);
+    double value;
+    for (int i=0; i<lineData.size(); ++i)
+    {
+      value = valueAxis->coordToPixel(lineData.at(i).value);
+      (*linePixelData)[i*2+0].setX(value);
+      (*linePixelData)[i*2+0].setY(lastKey);
+      lastKey = keyAxis->coordToPixel(lineData.at(i).key);
+      (*linePixelData)[i*2+1].setX(value);
+      (*linePixelData)[i*2+1].setY(lastKey);
+    }
+  } else // key axis is horizontal
+  {
+    double lastKey = keyAxis->coordToPixel(lineData.first().key);
+    double value;
+    for (int i=0; i<lineData.size(); ++i)
+    {
+      value = valueAxis->coordToPixel(lineData.at(i).value);
+      (*linePixelData)[i*2+0].setX(lastKey);
+      (*linePixelData)[i*2+0].setY(value);
+      lastKey = keyAxis->coordToPixel(lineData.at(i).key);
+      (*linePixelData)[i*2+1].setX(lastKey);
+      (*linePixelData)[i*2+1].setY(value);
+    }
+  }
+}
+
+/*!
+  \internal
+  Places the raw data points needed for a step plot with centered steps in \a lineData.
+
+  As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
+  points that are visible for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
+  scatterData, and the function will skip filling the vector.
+
+  \see drawLinePlot
+*/
+void QCPGraph::getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as lineData"; return; }
+
+  QVector<QCPData> lineData;
+  getPreparedData(&lineData, scatterData);
+  linePixelData->reserve(lineData.size()*2+2); // added 2 to reserve memory for lower/upper fill base points that might be needed for fill
+  linePixelData->resize(lineData.size()*2);
+  // calculate steps from lineData and transform to pixel coordinates:
+  if (keyAxis->orientation() == Qt::Vertical)
+  {
+    double lastKey = keyAxis->coordToPixel(lineData.first().key);
+    double lastValue = valueAxis->coordToPixel(lineData.first().value);
+    double key;
+    (*linePixelData)[0].setX(lastValue);
+    (*linePixelData)[0].setY(lastKey);
+    for (int i=1; i<lineData.size(); ++i)
+    {
+      key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
+      (*linePixelData)[i*2-1].setX(lastValue);
+      (*linePixelData)[i*2-1].setY(key);
+      lastValue = valueAxis->coordToPixel(lineData.at(i).value);
+      lastKey = keyAxis->coordToPixel(lineData.at(i).key);
+      (*linePixelData)[i*2+0].setX(lastValue);
+      (*linePixelData)[i*2+0].setY(key);
+    }
+    (*linePixelData)[lineData.size()*2-1].setX(lastValue);
+    (*linePixelData)[lineData.size()*2-1].setY(lastKey);
+  } else // key axis is horizontal
+  {
+    double lastKey = keyAxis->coordToPixel(lineData.first().key);
+    double lastValue = valueAxis->coordToPixel(lineData.first().value);
+    double key;
+    (*linePixelData)[0].setX(lastKey);
+    (*linePixelData)[0].setY(lastValue);
+    for (int i=1; i<lineData.size(); ++i)
+    {
+      key = (keyAxis->coordToPixel(lineData.at(i).key)+lastKey)*0.5;
+      (*linePixelData)[i*2-1].setX(key);
+      (*linePixelData)[i*2-1].setY(lastValue);
+      lastValue = valueAxis->coordToPixel(lineData.at(i).value);
+      lastKey = keyAxis->coordToPixel(lineData.at(i).key);
+      (*linePixelData)[i*2+0].setX(key);
+      (*linePixelData)[i*2+0].setY(lastValue);
+    }
+    (*linePixelData)[lineData.size()*2-1].setX(lastKey);
+    (*linePixelData)[lineData.size()*2-1].setY(lastValue);
+  }
+
+}
+
+/*!
+  \internal
+  Places the raw data points needed for an impulse plot in \a lineData.
+
+  As for all plot data retrieval functions, \a scatterData just contains all unaltered data (scatter)
+  points that are visible for drawing scatter points, if necessary. If drawing scatter points is
+  disabled (i.e. the scatter style's shape is \ref QCPScatterStyle::ssNone), pass 0 as \a
+  scatterData, and the function will skip filling the vector.
+
+  \see drawImpulsePlot
+*/
+void QCPGraph::getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  if (!linePixelData) { qDebug() << Q_FUNC_INFO << "null pointer passed as linePixelData"; return; }
+
+  QVector<QCPData> lineData;
+  getPreparedData(&lineData, scatterData);
+  linePixelData->resize(lineData.size()*2); // no need to reserve 2 extra points because impulse plot has no fill
+
+  // transform lineData points to pixels:
+  if (keyAxis->orientation() == Qt::Vertical)
+  {
+    double zeroPointX = valueAxis->coordToPixel(0);
+    double key;
+    for (int i=0; i<lineData.size(); ++i)
+    {
+      key = keyAxis->coordToPixel(lineData.at(i).key);
+      (*linePixelData)[i*2+0].setX(zeroPointX);
+      (*linePixelData)[i*2+0].setY(key);
+      (*linePixelData)[i*2+1].setX(valueAxis->coordToPixel(lineData.at(i).value));
+      (*linePixelData)[i*2+1].setY(key);
+    }
+  } else // key axis is horizontal
+  {
+    double zeroPointY = valueAxis->coordToPixel(0);
+    double key;
+    for (int i=0; i<lineData.size(); ++i)
+    {
+      key = keyAxis->coordToPixel(lineData.at(i).key);
+      (*linePixelData)[i*2+0].setX(key);
+      (*linePixelData)[i*2+0].setY(zeroPointY);
+      (*linePixelData)[i*2+1].setX(key);
+      (*linePixelData)[i*2+1].setY(valueAxis->coordToPixel(lineData.at(i).value));
+    }
+  }
+}
+
+/*! \internal
+
+  Draws the fill of the graph with the specified brush.
+
+  If the fill is a normal fill towards the zero-value-line, only the \a lineData is required (and
+  two extra points at the zero-value-line, which are added by \ref addFillBasePoints and removed by
+  \ref removeFillBasePoints after the fill drawing is done).
+
+  If the fill is a channel fill between this QCPGraph and another QCPGraph (mChannelFillGraph), the
+  more complex polygon is calculated with the \ref getChannelFillPolygon function.
+
+  \see drawLinePlot
+*/
+void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const
+{
+  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
+  if (mainBrush().style() == Qt::NoBrush || mainBrush().color().alpha() == 0) return;
+
+  applyFillAntialiasingHint(painter);
+  if (!mChannelFillGraph)
+  {
+    // draw base fill under graph, fill goes all the way to the zero-value-line:
+    addFillBasePoints(lineData);
+    painter->setPen(Qt::NoPen);
+    painter->setBrush(mainBrush());
+    painter->drawPolygon(QPolygonF(*lineData));
+    removeFillBasePoints(lineData);
+  } else
+  {
+    // draw channel fill between this graph and mChannelFillGraph:
+    painter->setPen(Qt::NoPen);
+    painter->setBrush(mainBrush());
+    painter->drawPolygon(getChannelFillPolygon(lineData));
+  }
+}
+
+/*! \internal
+
+  Draws scatter symbols at every data point passed in \a scatterData. scatter symbols are independent
+  of the line style and are always drawn if the scatter style's shape is not \ref
+  QCPScatterStyle::ssNone. Hence, the \a scatterData vector is outputted by all "get(...)PlotData"
+  functions, together with the (line style dependent) line data.
+
+  \see drawLinePlot, drawImpulsePlot
+*/
+void QCPGraph::drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+
+  // draw error bars:
+  if (mErrorType != etNone)
+  {
+    applyErrorBarsAntialiasingHint(painter);
+    painter->setPen(mErrorPen);
+    if (keyAxis->orientation() == Qt::Vertical)
+    {
+      for (int i=0; i<scatterData->size(); ++i)
+        drawError(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key), scatterData->at(i));
+    } else
+    {
+      for (int i=0; i<scatterData->size(); ++i)
+        drawError(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value), scatterData->at(i));
+    }
+  }
+
+  // draw scatter point symbols:
+  applyScattersAntialiasingHint(painter);
+  mScatterStyle.applyTo(painter, mPen);
+  if (keyAxis->orientation() == Qt::Vertical)
+  {
+    for (int i=0; i<scatterData->size(); ++i)
+      if (!qIsNaN(scatterData->at(i).value))
+        mScatterStyle.drawShape(painter, valueAxis->coordToPixel(scatterData->at(i).value), keyAxis->coordToPixel(scatterData->at(i).key));
+  } else
+  {
+    for (int i=0; i<scatterData->size(); ++i)
+      if (!qIsNaN(scatterData->at(i).value))
+        mScatterStyle.drawShape(painter, keyAxis->coordToPixel(scatterData->at(i).key), valueAxis->coordToPixel(scatterData->at(i).value));
+  }
+}
+
+/*!  \internal
+
+  Draws line graphs from the provided data. It connects all points in \a lineData, which was
+  created by one of the "get(...)PlotData" functions for line styles that require simple line
+  connections between the point vector they create. These are for example \ref getLinePlotData,
+  \ref getStepLeftPlotData, \ref getStepRightPlotData and \ref getStepCenterPlotData.
+
+  \see drawScatterPlot, drawImpulsePlot
+*/
+void QCPGraph::drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
+{
+  // draw line of graph:
+  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
+  {
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mainPen());
+    painter->setBrush(Qt::NoBrush);
+
+    /* Draws polyline in batches, currently not used:
+    int p = 0;
+    while (p < lineData->size())
+    {
+      int batch = qMin(25, lineData->size()-p);
+      if (p != 0)
+      {
+        ++batch;
+        --p; // to draw the connection lines between two batches
+      }
+      painter->drawPolyline(lineData->constData()+p, batch);
+      p += batch;
+    }
+    */
+
+    // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
+    if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
+        painter->pen().style() == Qt::SolidLine &&
+        !painter->modes().testFlag(QCPPainter::pmVectorized) &&
+        !painter->modes().testFlag(QCPPainter::pmNoCaching))
+    {
+      int i = 0;
+      bool lastIsNan = false;
+      const int lineDataSize = lineData->size();
+      while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
+        ++i;
+      ++i; // because drawing works in 1 point retrospect
+      while (i < lineDataSize)
+      {
+        if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
+        {
+          if (!lastIsNan)
+            painter->drawLine(lineData->at(i-1), lineData->at(i));
+          else
+            lastIsNan = false;
+        } else
+          lastIsNan = true;
+        ++i;
+      }
+    } else
+    {
+      int segmentStart = 0;
+      int i = 0;
+      const int lineDataSize = lineData->size();
+      while (i < lineDataSize)
+      {
+        if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
+        {
+          painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
+          segmentStart = i+1;
+        }
+        ++i;
+      }
+      // draw last segment:
+      painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
+    }
+  }
+}
+
+/*! \internal
+
+  Draws impulses from the provided data, i.e. it connects all line pairs in \a lineData, which was
+  created by \ref getImpulsePlotData.
+
+  \see drawScatterPlot, drawLinePlot
+*/
+void QCPGraph::drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const
+{
+  // draw impulses:
+  if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
+  {
+    applyDefaultAntialiasingHint(painter);
+    QPen pen = mainPen();
+    pen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
+    painter->setPen(pen);
+    painter->setBrush(Qt::NoBrush);
+    painter->drawLines(*lineData);
+  }
+}
+
+/*! \internal
+
+  Returns the \a lineData and \a scatterData that need to be plotted for this graph taking into
+  consideration the current axis ranges and, if \ref setAdaptiveSampling is enabled, local point
+  densities.
+
+  0 may be passed as \a lineData or \a scatterData to indicate that the respective dataset isn't
+  needed. For example, if the scatter style (\ref setScatterStyle) is \ref QCPScatterStyle::ssNone, \a
+  scatterData should be 0 to prevent unnecessary calculations.
+
+  This method is used by the various "get(...)PlotData" methods to get the basic working set of data.
+*/
+void QCPGraph::getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  // get visible data range:
+  QCPDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
+  getVisibleDataBounds(lower, upper);
+  if (lower == mData->constEnd() || upper == mData->constEnd())
+    return;
+
+  // count points in visible range, taking into account that we only need to count to the limit maxCount if using adaptive sampling:
+  int maxCount = std::numeric_limits<int>::max();
+  if (mAdaptiveSampling)
+  {
+    int keyPixelSpan = qAbs(keyAxis->coordToPixel(lower.key())-keyAxis->coordToPixel(upper.key()));
+    maxCount = 2*keyPixelSpan+2;
+  }
+  int dataCount = countDataInBounds(lower, upper, maxCount);
+
+  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
+  {
+    if (lineData)
+    {
+      QCPDataMap::const_iterator it = lower;
+      QCPDataMap::const_iterator upperEnd = upper+1;
+      double minValue = it.value().value;
+      double maxValue = it.value().value;
+      QCPDataMap::const_iterator currentIntervalFirstPoint = it;
+      int reversedFactor = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
+      int reversedRound = keyAxis->rangeReversed() != (keyAxis->orientation()==Qt::Vertical) ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
+      double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
+      double lastIntervalEndKey = currentIntervalStartKey;
+      double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
+      bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
+      int intervalDataCount = 1;
+      ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
+      while (it != upperEnd)
+      {
+        if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
+        {
+          if (it.value().value < minValue)
+            minValue = it.value().value;
+          else if (it.value().value > maxValue)
+            maxValue = it.value().value;
+          ++intervalDataCount;
+        } else // new pixel interval started
+        {
+          if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
+          {
+            if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
+              lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
+            lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
+            lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
+            if (it.key() > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point
+              lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.8, (it-1).value().value));
+          } else
+            lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
+          lastIntervalEndKey = (it-1).value().key;
+          minValue = it.value().value;
+          maxValue = it.value().value;
+          currentIntervalFirstPoint = it;
+          currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
+          if (keyEpsilonVariable)
+            keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
+          intervalDataCount = 1;
+        }
+        ++it;
+      }
+      // handle last interval:
+      if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
+      {
+        if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
+          lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint.value().value));
+        lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
+        lineData->append(QCPData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
+      } else
+        lineData->append(QCPData(currentIntervalFirstPoint.key(), currentIntervalFirstPoint.value().value));
+    }
+
+    if (scatterData)
+    {
+      double valueMaxRange = valueAxis->range().upper;
+      double valueMinRange = valueAxis->range().lower;
+      QCPDataMap::const_iterator it = lower;
+      QCPDataMap::const_iterator upperEnd = upper+1;
+      double minValue = it.value().value;
+      double maxValue = it.value().value;
+      QCPDataMap::const_iterator minValueIt = it;
+      QCPDataMap::const_iterator maxValueIt = it;
+      QCPDataMap::const_iterator currentIntervalStart = it;
+      int reversedFactor = keyAxis->rangeReversed() ? -1 : 1; // is used to calculate keyEpsilon pixel into the correct direction
+      int reversedRound = keyAxis->rangeReversed() ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
+      double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(lower.key())+reversedRound));
+      double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
+      bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
+      int intervalDataCount = 1;
+      ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
+      while (it != upperEnd)
+      {
+        if (it.key() < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
+        {
+          if (it.value().value < minValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
+          {
+            minValue = it.value().value;
+            minValueIt = it;
+          } else if (it.value().value > maxValue && it.value().value > valueMinRange && it.value().value < valueMaxRange)
+          {
+            maxValue = it.value().value;
+            maxValueIt = it;
+          }
+          ++intervalDataCount;
+        } else // new pixel started
+        {
+          if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
+          {
+            // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
+            double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
+            int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
+            QCPDataMap::const_iterator intervalIt = currentIntervalStart;
+            int c = 0;
+            while (intervalIt != it)
+            {
+              if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
+                scatterData->append(intervalIt.value());
+              ++c;
+              ++intervalIt;
+            }
+          } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
+            scatterData->append(currentIntervalStart.value());
+          minValue = it.value().value;
+          maxValue = it.value().value;
+          currentIntervalStart = it;
+          currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it.key())+reversedRound));
+          if (keyEpsilonVariable)
+            keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
+          intervalDataCount = 1;
+        }
+        ++it;
+      }
+      // handle last interval:
+      if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
+      {
+        // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
+        double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
+        int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
+        QCPDataMap::const_iterator intervalIt = currentIntervalStart;
+        int c = 0;
+        while (intervalIt != it)
+        {
+          if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt.value().value > valueMinRange && intervalIt.value().value < valueMaxRange)
+            scatterData->append(intervalIt.value());
+          ++c;
+          ++intervalIt;
+        }
+      } else if (currentIntervalStart.value().value > valueMinRange && currentIntervalStart.value().value < valueMaxRange)
+        scatterData->append(currentIntervalStart.value());
+    }
+  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the map into the output parameters
+  {
+    QVector<QCPData> *dataVector = 0;
+    if (lineData)
+      dataVector = lineData;
+    else if (scatterData)
+      dataVector = scatterData;
+    if (dataVector)
+    {
+      QCPDataMap::const_iterator it = lower;
+      QCPDataMap::const_iterator upperEnd = upper+1;
+      dataVector->reserve(dataCount+2); // +2 for possible fill end points
+      while (it != upperEnd)
+      {
+        dataVector->append(it.value());
+        ++it;
+      }
+    }
+    if (lineData && scatterData)
+      *scatterData = *dataVector;
+  }
+}
+
+/*!  \internal
+
+  called by the scatter drawing function (\ref drawScatterPlot) to draw the error bars on one data
+  point. \a x and \a y pixel positions of the data point are passed since they are already known in
+  pixel coordinates in the drawing function, so we save some extra coordToPixel transforms here. \a
+  data is therefore only used for the errors, not key and value.
+*/
+void QCPGraph::drawError(QCPPainter *painter, double x, double y, const QCPData &data) const
+{
+  if (qIsNaN(data.value))
+    return;
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+
+  double a, b; // positions of error bar bounds in pixels
+  double barWidthHalf = mErrorBarSize*0.5;
+  double skipSymbolMargin = mScatterStyle.size(); // pixels left blank per side, when mErrorBarSkipSymbol is true
+
+  if (keyAxis->orientation() == Qt::Vertical)
+  {
+    // draw key error vertically and value error horizontally
+    if (mErrorType == etKey || mErrorType == etBoth)
+    {
+      a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
+      b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
+      if (keyAxis->rangeReversed())
+        qSwap(a,b);
+      // draw spine:
+      if (mErrorBarSkipSymbol)
+      {
+        if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
+          painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
+        if (y-b > skipSymbolMargin)
+          painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
+      } else
+        painter->drawLine(QLineF(x, a, x, b));
+      // draw handles:
+      painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
+      painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
+    }
+    if (mErrorType == etValue || mErrorType == etBoth)
+    {
+      a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
+      b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
+      if (valueAxis->rangeReversed())
+        qSwap(a,b);
+      // draw spine:
+      if (mErrorBarSkipSymbol)
+      {
+        if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
+          painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
+        if (b-x > skipSymbolMargin)
+          painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
+      } else
+        painter->drawLine(QLineF(a, y, b, y));
+      // draw handles:
+      painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
+      painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
+    }
+  } else // mKeyAxis->orientation() is Qt::Horizontal
+  {
+    // draw value error vertically and key error horizontally
+    if (mErrorType == etKey || mErrorType == etBoth)
+    {
+      a = keyAxis->coordToPixel(data.key-data.keyErrorMinus);
+      b = keyAxis->coordToPixel(data.key+data.keyErrorPlus);
+      if (keyAxis->rangeReversed())
+        qSwap(a,b);
+      // draw spine:
+      if (mErrorBarSkipSymbol)
+      {
+        if (x-a > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
+          painter->drawLine(QLineF(a, y, x-skipSymbolMargin, y));
+        if (b-x > skipSymbolMargin)
+          painter->drawLine(QLineF(x+skipSymbolMargin, y, b, y));
+      } else
+        painter->drawLine(QLineF(a, y, b, y));
+      // draw handles:
+      painter->drawLine(QLineF(a, y-barWidthHalf, a, y+barWidthHalf));
+      painter->drawLine(QLineF(b, y-barWidthHalf, b, y+barWidthHalf));
+    }
+    if (mErrorType == etValue || mErrorType == etBoth)
+    {
+      a = valueAxis->coordToPixel(data.value-data.valueErrorMinus);
+      b = valueAxis->coordToPixel(data.value+data.valueErrorPlus);
+      if (valueAxis->rangeReversed())
+        qSwap(a,b);
+      // draw spine:
+      if (mErrorBarSkipSymbol)
+      {
+        if (a-y > skipSymbolMargin) // don't draw spine if error is so small it's within skipSymbolmargin
+          painter->drawLine(QLineF(x, a, x, y+skipSymbolMargin));
+        if (y-b > skipSymbolMargin)
+          painter->drawLine(QLineF(x, y-skipSymbolMargin, x, b));
+      } else
+        painter->drawLine(QLineF(x, a, x, b));
+      // draw handles:
+      painter->drawLine(QLineF(x-barWidthHalf, a, x+barWidthHalf, a));
+      painter->drawLine(QLineF(x-barWidthHalf, b, x+barWidthHalf, b));
+    }
+  }
+}
+
+/*!  \internal
+
+  called by \ref getPreparedData to determine which data (key) range is visible at the current key
+  axis range setting, so only that needs to be processed.
+
+  \a lower returns an iterator to the lowest data point that needs to be taken into account when
+  plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
+  lower may still be just outside the visible range.
+
+  \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
+  just outside of the visible range.
+
+  if the graph contains no data, both \a lower and \a upper point to constEnd.
+*/
+void QCPGraph::getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const
+{
+  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
+  if (mData->isEmpty())
+  {
+    lower = mData->constEnd();
+    upper = mData->constEnd();
+    return;
+  }
+
+  // get visible data range as QMap iterators
+  QCPDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
+  QCPDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
+  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
+  bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
+
+  lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
+  upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
+}
+
+/*!  \internal
+
+  Counts the number of data points between \a lower and \a upper (including them), up to a maximum
+  of \a maxCount.
+
+  This function is used by \ref getPreparedData to determine whether adaptive sampling shall be
+  used (if enabled via \ref setAdaptiveSampling) or not. This is also why counting of data points
+  only needs to be done until \a maxCount is reached, which should be set to the number of data
+  points at which adaptive sampling sets in.
+*/
+int QCPGraph::countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const
+{
+  if (upper == mData->constEnd() && lower == mData->constEnd())
+    return 0;
+  QCPDataMap::const_iterator it = lower;
+  int count = 1;
+  while (it != upper && count < maxCount)
+  {
+    ++it;
+    ++count;
+  }
+  return count;
+}
+
+/*! \internal
+
+  The line data vector generated by e.g. getLinePlotData contains only the line that connects the
+  data points. If the graph needs to be filled, two additional points need to be added at the
+  value-zero-line in the lower and upper key positions of the graph. This function calculates these
+  points and adds them to the end of \a lineData. Since the fill is typically drawn before the line
+  stroke, these added points need to be removed again after the fill is done, with the
+  removeFillBasePoints function.
+
+  The expanding of \a lineData by two points will not cause unnecessary memory reallocations,
+  because the data vector generation functions (getLinePlotData etc.) reserve two extra points when
+  they allocate memory for \a lineData.
+
+  \see removeFillBasePoints, lowerFillBasePoint, upperFillBasePoint
+*/
+void QCPGraph::addFillBasePoints(QVector<QPointF> *lineData) const
+{
+  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
+
+  // append points that close the polygon fill at the key axis:
+  if (mKeyAxis.data()->orientation() == Qt::Vertical)
+  {
+    *lineData << upperFillBasePoint(lineData->last().y());
+    *lineData << lowerFillBasePoint(lineData->first().y());
+  } else
+  {
+    *lineData << upperFillBasePoint(lineData->last().x());
+    *lineData << lowerFillBasePoint(lineData->first().x());
+  }
+}
+
+/*! \internal
+
+  removes the two points from \a lineData that were added by \ref addFillBasePoints.
+
+  \see addFillBasePoints, lowerFillBasePoint, upperFillBasePoint
+*/
+void QCPGraph::removeFillBasePoints(QVector<QPointF> *lineData) const
+{
+  lineData->remove(lineData->size()-2, 2);
+}
+
+/*! \internal
+
+  called by \ref addFillBasePoints to conveniently assign the point which closes the fill polygon
+  on the lower side of the zero-value-line parallel to the key axis. The logarithmic axis scale
+  case is a bit special, since the zero-value-line in pixel coordinates is in positive or negative
+  infinity. So this case is handled separately by just closing the fill polygon on the axis which
+  lies in the direction towards the zero value.
+
+  \a lowerKey will be the the key (in pixels) of the returned point. Depending on whether the key
+  axis is horizontal or vertical, \a lowerKey will end up as the x or y value of the returned
+  point, respectively.
+
+  \see upperFillBasePoint, addFillBasePoints
+*/
+QPointF QCPGraph::lowerFillBasePoint(double lowerKey) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
+
+  QPointF point;
+  if (valueAxis->scaleType() == QCPAxis::stLinear)
+  {
+    if (keyAxis->axisType() == QCPAxis::atLeft)
+    {
+      point.setX(valueAxis->coordToPixel(0));
+      point.setY(lowerKey);
+    } else if (keyAxis->axisType() == QCPAxis::atRight)
+    {
+      point.setX(valueAxis->coordToPixel(0));
+      point.setY(lowerKey);
+    } else if (keyAxis->axisType() == QCPAxis::atTop)
+    {
+      point.setX(lowerKey);
+      point.setY(valueAxis->coordToPixel(0));
+    } else if (keyAxis->axisType() == QCPAxis::atBottom)
+    {
+      point.setX(lowerKey);
+      point.setY(valueAxis->coordToPixel(0));
+    }
+  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
+  {
+    // In logarithmic scaling we can't just draw to value zero so we just fill all the way
+    // to the axis which is in the direction towards zero
+    if (keyAxis->orientation() == Qt::Vertical)
+    {
+      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
+          (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
+        point.setX(keyAxis->axisRect()->right());
+      else
+        point.setX(keyAxis->axisRect()->left());
+      point.setY(lowerKey);
+    } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
+    {
+      point.setX(lowerKey);
+      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
+          (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
+        point.setY(keyAxis->axisRect()->top());
+      else
+        point.setY(keyAxis->axisRect()->bottom());
+    }
+  }
+  return point;
+}
+
+/*! \internal
+
+  called by \ref addFillBasePoints to conveniently assign the point which closes the fill
+  polygon on the upper side of the zero-value-line parallel to the key axis. The logarithmic axis
+  scale case is a bit special, since the zero-value-line in pixel coordinates is in positive or
+  negative infinity. So this case is handled separately by just closing the fill polygon on the
+  axis which lies in the direction towards the zero value.
+
+  \a upperKey will be the the key (in pixels) of the returned point. Depending on whether the key
+  axis is horizontal or vertical, \a upperKey will end up as the x or y value of the returned
+  point, respectively.
+
+  \see lowerFillBasePoint, addFillBasePoints
+*/
+QPointF QCPGraph::upperFillBasePoint(double upperKey) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
+
+  QPointF point;
+  if (valueAxis->scaleType() == QCPAxis::stLinear)
+  {
+    if (keyAxis->axisType() == QCPAxis::atLeft)
+    {
+      point.setX(valueAxis->coordToPixel(0));
+      point.setY(upperKey);
+    } else if (keyAxis->axisType() == QCPAxis::atRight)
+    {
+      point.setX(valueAxis->coordToPixel(0));
+      point.setY(upperKey);
+    } else if (keyAxis->axisType() == QCPAxis::atTop)
+    {
+      point.setX(upperKey);
+      point.setY(valueAxis->coordToPixel(0));
+    } else if (keyAxis->axisType() == QCPAxis::atBottom)
+    {
+      point.setX(upperKey);
+      point.setY(valueAxis->coordToPixel(0));
+    }
+  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
+  {
+    // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
+    // to the axis which is in the direction towards 0
+    if (keyAxis->orientation() == Qt::Vertical)
+    {
+      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
+          (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
+        point.setX(keyAxis->axisRect()->right());
+      else
+        point.setX(keyAxis->axisRect()->left());
+      point.setY(upperKey);
+    } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
+    {
+      point.setX(upperKey);
+      if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
+          (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
+        point.setY(keyAxis->axisRect()->top());
+      else
+        point.setY(keyAxis->axisRect()->bottom());
+    }
+  }
+  return point;
+}
+
+/*! \internal
+
+  Generates the polygon needed for drawing channel fills between this graph (data passed via \a
+  lineData) and the graph specified by mChannelFillGraph (data generated by calling its \ref
+  getPlotData function). May return an empty polygon if the key ranges have no overlap or fill
+  target graph and this graph don't have same orientation (i.e. both key axes horizontal or both
+  key axes vertical). For increased performance (due to implicit sharing), keep the returned
+  QPolygonF const.
+*/
+const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *lineData) const
+{
+  if (!mChannelFillGraph)
+    return QPolygonF();
+
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
+  if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
+
+  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
+    return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)
+
+  if (lineData->isEmpty()) return QPolygonF();
+  QVector<QPointF> otherData;
+  mChannelFillGraph.data()->getPlotData(&otherData, 0);
+  if (otherData.isEmpty()) return QPolygonF();
+  QVector<QPointF> thisData;
+  thisData.reserve(lineData->size()+otherData.size()); // because we will join both vectors at end of this function
+  for (int i=0; i<lineData->size(); ++i) // don't use the vector<<(vector),  it squeezes internally, which ruins the performance tuning with reserve()
+    thisData << lineData->at(i);
+
+  // pointers to be able to swap them, depending which data range needs cropping:
+  QVector<QPointF> *staticData = &thisData;
+  QVector<QPointF> *croppedData = &otherData;
+
+  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
+  if (keyAxis->orientation() == Qt::Horizontal)
+  {
+    // x is key
+    // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
+    if (staticData->first().x() > staticData->last().x())
+    {
+      int size = staticData->size();
+      for (int i=0; i<size/2; ++i)
+        qSwap((*staticData)[i], (*staticData)[size-1-i]);
+    }
+    if (croppedData->first().x() > croppedData->last().x())
+    {
+      int size = croppedData->size();
+      for (int i=0; i<size/2; ++i)
+        qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
+    }
+    // crop lower bound:
+    if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
+      qSwap(staticData, croppedData);
+    int lowBound = findIndexBelowX(croppedData, staticData->first().x());
+    if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
+    croppedData->remove(0, lowBound);
+    // set lowest point of cropped data to fit exactly key position of first static data
+    // point via linear interpolation:
+    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
+    double slope;
+    if (croppedData->at(1).x()-croppedData->at(0).x() != 0)
+      slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
+    else
+      slope = 0;
+    (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
+    (*croppedData)[0].setX(staticData->first().x());
+
+    // crop upper bound:
+    if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
+      qSwap(staticData, croppedData);
+    int highBound = findIndexAboveX(croppedData, staticData->last().x());
+    if (highBound == -1) return QPolygonF(); // key ranges have no overlap
+    croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
+    // set highest point of cropped data to fit exactly key position of last static data
+    // point via linear interpolation:
+    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
+    int li = croppedData->size()-1; // last index
+    if (croppedData->at(li).x()-croppedData->at(li-1).x() != 0)
+      slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
+    else
+      slope = 0;
+    (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
+    (*croppedData)[li].setX(staticData->last().x());
+  } else // mKeyAxis->orientation() == Qt::Vertical
+  {
+    // y is key
+    // similar to "x is key" but switched x,y. Further, lower/upper meaning is inverted compared to x,
+    // because in pixel coordinates, y increases from top to bottom, not bottom to top like data coordinate.
+    // if an axis range is reversed, the data point keys will be descending. Reverse them, since following algorithm assumes ascending keys:
+    if (staticData->first().y() < staticData->last().y())
+    {
+      int size = staticData->size();
+      for (int i=0; i<size/2; ++i)
+        qSwap((*staticData)[i], (*staticData)[size-1-i]);
+    }
+    if (croppedData->first().y() < croppedData->last().y())
+    {
+      int size = croppedData->size();
+      for (int i=0; i<size/2; ++i)
+        qSwap((*croppedData)[i], (*croppedData)[size-1-i]);
+    }
+    // crop lower bound:
+    if (staticData->first().y() > croppedData->first().y()) // other one must be cropped
+      qSwap(staticData, croppedData);
+    int lowBound = findIndexAboveY(croppedData, staticData->first().y());
+    if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
+    croppedData->remove(0, lowBound);
+    // set lowest point of cropped data to fit exactly key position of first static data
+    // point via linear interpolation:
+    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
+    double slope;
+    if (croppedData->at(1).y()-croppedData->at(0).y() != 0) // avoid division by zero in step plots
+      slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
+    else
+      slope = 0;
+    (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
+    (*croppedData)[0].setY(staticData->first().y());
+
+    // crop upper bound:
+    if (staticData->last().y() < croppedData->last().y()) // other one must be cropped
+      qSwap(staticData, croppedData);
+    int highBound = findIndexBelowY(croppedData, staticData->last().y());
+    if (highBound == -1) return QPolygonF(); // key ranges have no overlap
+    croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
+    // set highest point of cropped data to fit exactly key position of last static data
+    // point via linear interpolation:
+    if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
+    int li = croppedData->size()-1; // last index
+    if (croppedData->at(li).y()-croppedData->at(li-1).y() != 0) // avoid division by zero in step plots
+      slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
+    else
+      slope = 0;
+    (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
+    (*croppedData)[li].setY(staticData->last().y());
+  }
+
+  // return joined:
+  for (int i=otherData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
+    thisData << otherData.at(i);
+  return QPolygonF(thisData);
+}
+
+/*! \internal
+
+  Finds the smallest index of \a data, whose points x value is just above \a x. Assumes x values in
+  \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
+
+  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
+*/
+int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
+{
+  for (int i=data->size()-1; i>=0; --i)
+  {
+    if (data->at(i).x() < x)
+    {
+      if (i<data->size()-1)
+        return i+1;
+      else
+        return data->size()-1;
+    }
+  }
+  return -1;
+}
+
+/*! \internal
+
+  Finds the highest index of \a data, whose points x value is just below \a x. Assumes x values in
+  \a data points are ordered ascending, as is the case when plotting with horizontal key axis.
+
+  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
+*/
+int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
+{
+  for (int i=0; i<data->size(); ++i)
+  {
+    if (data->at(i).x() > x)
+    {
+      if (i>0)
+        return i-1;
+      else
+        return 0;
+    }
+  }
+  return -1;
+}
+
+/*! \internal
+
+  Finds the smallest index of \a data, whose points y value is just above \a y. Assumes y values in
+  \a data points are ordered descending, as is the case when plotting with vertical key axis.
+
+  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
+*/
+int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
+{
+  for (int i=0; i<data->size(); ++i)
+  {
+    if (data->at(i).y() < y)
+    {
+      if (i>0)
+        return i-1;
+      else
+        return 0;
+    }
+  }
+  return -1;
+}
+
+/*! \internal
+
+  Calculates the (minimum) distance (in pixels) the graph's representation has from the given \a
+  pixelPoint in pixels. This is used to determine whether the graph was clicked or not, e.g. in
+  \ref selectTest.
+
+  If either the graph has no data or if the line style is \ref lsNone and the scatter style's shape
+  is \ref QCPScatterStyle::ssNone (i.e. there is no visual representation of the graph), returns -1.0.
+*/
+double QCPGraph::pointDistance(const QPointF &pixelPoint) const
+{
+  if (mData->isEmpty())
+    return -1.0;
+  if (mLineStyle == lsNone && mScatterStyle.isNone())
+    return -1.0;
+
+  // calculate minimum distances to graph representation:
+  if (mLineStyle == lsNone)
+  {
+    // no line displayed, only calculate distance to scatter points:
+    QVector<QCPData> scatterData;
+    getScatterPlotData(&scatterData);
+    if (scatterData.size() > 0)
+    {
+      double minDistSqr = std::numeric_limits<double>::max();
+      for (int i=0; i<scatterData.size(); ++i)
+      {
+        double currentDistSqr = QVector2D(coordsToPixels(scatterData.at(i).key, scatterData.at(i).value)-pixelPoint).lengthSquared();
+        if (currentDistSqr < minDistSqr)
+          minDistSqr = currentDistSqr;
+      }
+      return qSqrt(minDistSqr);
+    } else // no data available in view to calculate distance to
+      return -1.0;
+  } else
+  {
+    // line displayed, calculate distance to line segments:
+    QVector<QPointF> lineData;
+    getPlotData(&lineData, 0); // unlike with getScatterPlotData we get pixel coordinates here
+    if (lineData.size() > 1) // at least one line segment, compare distance to line segments
+    {
+      double minDistSqr = std::numeric_limits<double>::max();
+      if (mLineStyle == lsImpulse)
+      {
+        // impulse plot differs from other line styles in that the lineData points are only pairwise connected:
+        for (int i=0; i<lineData.size()-1; i+=2) // iterate pairs
+        {
+          double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
+          if (currentDistSqr < minDistSqr)
+            minDistSqr = currentDistSqr;
+        }
+      } else
+      {
+        // all other line plots (line and step) connect points directly:
+        for (int i=0; i<lineData.size()-1; ++i)
+        {
+          double currentDistSqr = distSqrToLine(lineData.at(i), lineData.at(i+1), pixelPoint);
+          if (currentDistSqr < minDistSqr)
+            minDistSqr = currentDistSqr;
+        }
+      }
+      return qSqrt(minDistSqr);
+    } else if (lineData.size() > 0) // only single data point, calculate distance to that point
+    {
+      return QVector2D(lineData.at(0)-pixelPoint).length();
+    } else // no data available in view to calculate distance to
+      return -1.0;
+  }
+}
+
+/*! \internal
+
+  Finds the highest index of \a data, whose points y value is just below \a y. Assumes y values in
+  \a data points are ordered descending, as is the case when plotting with vertical key axis (since
+  keys are ordered ascending).
+
+  Used to calculate the channel fill polygon, see \ref getChannelFillPolygon.
+*/
+int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
+{
+  for (int i=data->size()-1; i>=0; --i)
+  {
+    if (data->at(i).y() > y)
+    {
+      if (i<data->size()-1)
+        return i+1;
+      else
+        return data->size()-1;
+    }
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  // just call the specialized version which takes an additional argument whether error bars
+  // should also be taken into consideration for range calculation. We set this to true here.
+  return getKeyRange(foundRange, inSignDomain, true);
+}
+
+/* inherits documentation from base class */
+QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  // just call the specialized version which takes an additional argument whether error bars
+  // should also be taken into consideration for range calculation. We set this to true here.
+  return getValueRange(foundRange, inSignDomain, true);
+}
+
+/*! \overload
+
+  Allows to specify whether the error bars should be included in the range calculation.
+
+  \see getKeyRange(bool &foundRange, SignDomain inSignDomain)
+*/
+QCPRange QCPGraph::getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+
+  double current, currentErrorMinus, currentErrorPlus;
+
+  if (inSignDomain == sdBoth) // range may be anywhere
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      if (!qIsNaN(it.value().value))
+      {
+        current = it.value().key;
+        currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
+        currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
+        if (current-currentErrorMinus < range.lower || !haveLower)
+        {
+          range.lower = current-currentErrorMinus;
+          haveLower = true;
+        }
+        if (current+currentErrorPlus > range.upper || !haveUpper)
+        {
+          range.upper = current+currentErrorPlus;
+          haveUpper = true;
+        }
+      }
+      ++it;
+    }
+  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      if (!qIsNaN(it.value().value))
+      {
+        current = it.value().key;
+        currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
+        currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
+        if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
+        {
+          range.lower = current-currentErrorMinus;
+          haveLower = true;
+        }
+        if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
+        {
+          range.upper = current+currentErrorPlus;
+          haveUpper = true;
+        }
+        if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
+        {
+          if ((current < range.lower || !haveLower) && current < 0)
+          {
+            range.lower = current;
+            haveLower = true;
+          }
+          if ((current > range.upper || !haveUpper) && current < 0)
+          {
+            range.upper = current;
+            haveUpper = true;
+          }
+        }
+      }
+      ++it;
+    }
+  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      if (!qIsNaN(it.value().value))
+      {
+        current = it.value().key;
+        currentErrorMinus = (includeErrors ? it.value().keyErrorMinus : 0);
+        currentErrorPlus = (includeErrors ? it.value().keyErrorPlus : 0);
+        if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
+        {
+          range.lower = current-currentErrorMinus;
+          haveLower = true;
+        }
+        if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
+        {
+          range.upper = current+currentErrorPlus;
+          haveUpper = true;
+        }
+        if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
+        {
+          if ((current < range.lower || !haveLower) && current > 0)
+          {
+            range.lower = current;
+            haveLower = true;
+          }
+          if ((current > range.upper || !haveUpper) && current > 0)
+          {
+            range.upper = current;
+            haveUpper = true;
+          }
+        }
+      }
+      ++it;
+    }
+  }
+
+  foundRange = haveLower && haveUpper;
+  return range;
+}
+
+/*! \overload
+
+  Allows to specify whether the error bars should be included in the range calculation.
+
+  \see getValueRange(bool &foundRange, SignDomain inSignDomain)
+*/
+QCPRange QCPGraph::getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+
+  double current, currentErrorMinus, currentErrorPlus;
+
+  if (inSignDomain == sdBoth) // range may be anywhere
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      current = it.value().value;
+      if (!qIsNaN(current))
+      {
+        currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
+        currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
+        if (current-currentErrorMinus < range.lower || !haveLower)
+        {
+          range.lower = current-currentErrorMinus;
+          haveLower = true;
+        }
+        if (current+currentErrorPlus > range.upper || !haveUpper)
+        {
+          range.upper = current+currentErrorPlus;
+          haveUpper = true;
+        }
+      }
+      ++it;
+    }
+  } else if (inSignDomain == sdNegative) // range may only be in the negative sign domain
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      current = it.value().value;
+      if (!qIsNaN(current))
+      {
+        currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
+        currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
+        if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus < 0)
+        {
+          range.lower = current-currentErrorMinus;
+          haveLower = true;
+        }
+        if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus < 0)
+        {
+          range.upper = current+currentErrorPlus;
+          haveUpper = true;
+        }
+        if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to get that point.
+        {
+          if ((current < range.lower || !haveLower) && current < 0)
+          {
+            range.lower = current;
+            haveLower = true;
+          }
+          if ((current > range.upper || !haveUpper) && current < 0)
+          {
+            range.upper = current;
+            haveUpper = true;
+          }
+        }
+      }
+      ++it;
+    }
+  } else if (inSignDomain == sdPositive) // range may only be in the positive sign domain
+  {
+    QCPDataMap::const_iterator it = mData->constBegin();
+    while (it != mData->constEnd())
+    {
+      current = it.value().value;
+      if (!qIsNaN(current))
+      {
+        currentErrorMinus = (includeErrors ? it.value().valueErrorMinus : 0);
+        currentErrorPlus = (includeErrors ? it.value().valueErrorPlus : 0);
+        if ((current-currentErrorMinus < range.lower || !haveLower) && current-currentErrorMinus > 0)
+        {
+          range.lower = current-currentErrorMinus;
+          haveLower = true;
+        }
+        if ((current+currentErrorPlus > range.upper || !haveUpper) && current+currentErrorPlus > 0)
+        {
+          range.upper = current+currentErrorPlus;
+          haveUpper = true;
+        }
+        if (includeErrors) // in case point is in valid sign domain but errobars stretch beyond it, we still want to geht that point.
+        {
+          if ((current < range.lower || !haveLower) && current > 0)
+          {
+            range.lower = current;
+            haveLower = true;
+          }
+          if ((current > range.upper || !haveUpper) && current > 0)
+          {
+            range.upper = current;
+            haveUpper = true;
+          }
+        }
+      }
+      ++it;
+    }
+  }
+
+  foundRange = haveLower && haveUpper;
+  return range;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPCurveData
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPCurveData
+  \brief Holds the data of one single data point for QCPCurve.
+
+  The container for storing multiple data points is \ref QCPCurveDataMap.
+
+  The stored data is:
+  \li \a t: the free parameter of the curve at this curve point (cp. the mathematical vector <em>(x(t), y(t))</em>)
+  \li \a key: coordinate on the key axis of this curve point
+  \li \a value: coordinate on the value axis of this curve point
+
+  \see QCPCurveDataMap
+*/
+
+/*!
+  Constructs a curve data point with t, key and value set to zero.
+*/
+QCPCurveData::QCPCurveData() :
+  t(0),
+  key(0),
+  value(0)
+{
+}
+
+/*!
+  Constructs a curve data point with the specified \a t, \a key and \a value.
+*/
+QCPCurveData::QCPCurveData(double t, double key, double value) :
+  t(t),
+  key(key),
+  value(value)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPCurve
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPCurve
+  \brief A plottable representing a parametric curve in a plot.
+
+  \image html QCPCurve.png
+
+  Unlike QCPGraph, plottables of this type may have multiple points with the same key coordinate,
+  so their visual representation can have \a loops. This is realized by introducing a third
+  coordinate \a t, which defines the order of the points described by the other two coordinates \a
+  x and \a y.
+
+  To plot data, assign it with the \ref setData or \ref addData functions.
+
+  Gaps in the curve can be created by adding data points with NaN as key and value
+  (<tt>qQNaN()</tt> or <tt>std::numeric_limits<double>::quiet_NaN()</tt>) in between the two data points that shall be
+  separated.
+
+  \section appearance Changing the appearance
+
+  The appearance of the curve is determined by the pen and the brush (\ref setPen, \ref setBrush).
+  \section usage Usage
+
+  Like all data representing objects in QCustomPlot, the QCPCurve is a plottable (QCPAbstractPlottable). So
+  the plottable-interface of QCustomPlot applies (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
+
+  Usually, you first create an instance and add it to the customPlot:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-1
+  and then modify the properties of the newly created plottable, e.g.:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcurve-creation-2
+*/
+
+/*!
+  Constructs a curve which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
+  axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
+  the same orientation. If either of these restrictions is violated, a corresponding message is
+  printed to the debug output (qDebug), the construction is not aborted, though.
+
+  The constructed QCPCurve can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
+  then takes ownership of the graph.
+*/
+QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis)
+{
+  mData = new QCPCurveDataMap;
+  mPen.setColor(Qt::blue);
+  mPen.setStyle(Qt::SolidLine);
+  mBrush.setColor(Qt::blue);
+  mBrush.setStyle(Qt::NoBrush);
+  mSelectedPen = mPen;
+  mSelectedPen.setWidthF(2.5);
+  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
+  mSelectedBrush = mBrush;
+
+  setScatterStyle(QCPScatterStyle());
+  setLineStyle(lsLine);
+}
+
+QCPCurve::~QCPCurve()
+{
+  delete mData;
+}
+
+/*!
+  Replaces the current data with the provided \a data.
+
+  If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
+  takes ownership of the passed data and replaces the internal data pointer with it. This is
+  significantly faster than copying for large datasets.
+*/
+void QCPCurve::setData(QCPCurveDataMap *data, bool copy)
+{
+  if (mData == data)
+  {
+    qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
+    return;
+  }
+  if (copy)
+  {
+    *mData = *data;
+  } else
+  {
+    delete mData;
+    mData = data;
+  }
+}
+
+/*! \overload
+
+  Replaces the current data with the provided points in \a t, \a key and \a value tuples. The
+  provided vectors should have equal length. Else, the number of added points will be the size of
+  the smallest vector.
+*/
+void QCPCurve::setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value)
+{
+  mData->clear();
+  int n = t.size();
+  n = qMin(n, key.size());
+  n = qMin(n, value.size());
+  QCPCurveData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.t = t[i];
+    newData.key = key[i];
+    newData.value = value[i];
+    mData->insertMulti(newData.t, newData);
+  }
+}
+
+/*! \overload
+
+  Replaces the current data with the provided \a key and \a value pairs. The t parameter
+  of each data point will be set to the integer index of the respective key/value pair.
+*/
+void QCPCurve::setData(const QVector<double> &key, const QVector<double> &value)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  QCPCurveData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.t = i; // no t vector given, so we assign t the index of the key/value pair
+    newData.key = key[i];
+    newData.value = value[i];
+    mData->insertMulti(newData.t, newData);
+  }
+}
+
+/*!
+  Sets the visual appearance of single data points in the plot. If set to \ref
+  QCPScatterStyle::ssNone, no scatter points are drawn (e.g. for line-only plots with appropriate
+  line style).
+
+  \see QCPScatterStyle, setLineStyle
+*/
+void QCPCurve::setScatterStyle(const QCPScatterStyle &style)
+{
+  mScatterStyle = style;
+}
+
+/*!
+  Sets how the single data points are connected in the plot or how they are represented visually
+  apart from the scatter symbol. For scatter-only plots, set \a style to \ref lsNone and \ref
+  setScatterStyle to the desired scatter style.
+
+  \see setScatterStyle
+*/
+void QCPCurve::setLineStyle(QCPCurve::LineStyle style)
+{
+  mLineStyle = style;
+}
+
+/*!
+  Adds the provided data points in \a dataMap to the current data.
+  \see removeData
+*/
+void QCPCurve::addData(const QCPCurveDataMap &dataMap)
+{
+  mData->unite(dataMap);
+}
+
+/*! \overload
+  Adds the provided single data point in \a data to the current data.
+  \see removeData
+*/
+void QCPCurve::addData(const QCPCurveData &data)
+{
+  mData->insertMulti(data.t, data);
+}
+
+/*! \overload
+  Adds the provided single data point as \a t, \a key and \a value tuple to the current data
+  \see removeData
+*/
+void QCPCurve::addData(double t, double key, double value)
+{
+  QCPCurveData newData;
+  newData.t = t;
+  newData.key = key;
+  newData.value = value;
+  mData->insertMulti(newData.t, newData);
+}
+
+/*! \overload
+
+  Adds the provided single data point as \a key and \a value pair to the current data The t
+  parameter of the data point is set to the t of the last data point plus 1. If there is no last
+  data point, t will be set to 0.
+
+  \see removeData
+*/
+void QCPCurve::addData(double key, double value)
+{
+  QCPCurveData newData;
+  if (!mData->isEmpty())
+    newData.t = (mData->constEnd()-1).key()+1;
+  else
+    newData.t = 0;
+  newData.key = key;
+  newData.value = value;
+  mData->insertMulti(newData.t, newData);
+}
+
+/*! \overload
+  Adds the provided data points as \a t, \a key and \a value tuples to the current data.
+  \see removeData
+*/
+void QCPCurve::addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values)
+{
+  int n = ts.size();
+  n = qMin(n, keys.size());
+  n = qMin(n, values.size());
+  QCPCurveData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.t = ts[i];
+    newData.key = keys[i];
+    newData.value = values[i];
+    mData->insertMulti(newData.t, newData);
+  }
+}
+
+/*!
+  Removes all data points with curve parameter t smaller than \a t.
+  \see addData, clearData
+*/
+void QCPCurve::removeDataBefore(double t)
+{
+  QCPCurveDataMap::iterator it = mData->begin();
+  while (it != mData->end() && it.key() < t)
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with curve parameter t greater than \a t.
+  \see addData, clearData
+*/
+void QCPCurve::removeDataAfter(double t)
+{
+  if (mData->isEmpty()) return;
+  QCPCurveDataMap::iterator it = mData->upperBound(t);
+  while (it != mData->end())
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with curve parameter t between \a fromt and \a tot. if \a fromt is
+  greater or equal to \a tot, the function does nothing. To remove a single data point with known
+  t, use \ref removeData(double t).
+
+  \see addData, clearData
+*/
+void QCPCurve::removeData(double fromt, double tot)
+{
+  if (fromt >= tot || mData->isEmpty()) return;
+  QCPCurveDataMap::iterator it = mData->upperBound(fromt);
+  QCPCurveDataMap::iterator itEnd = mData->upperBound(tot);
+  while (it != itEnd)
+    it = mData->erase(it);
+}
+
+/*! \overload
+
+  Removes a single data point at curve parameter \a t. If the position is not known with absolute
+  precision, consider using \ref removeData(double fromt, double tot) with a small fuzziness
+  interval around the suspected position, depeding on the precision with which the curve parameter
+  is known.
+
+  \see addData, clearData
+*/
+void QCPCurve::removeData(double t)
+{
+  mData->remove(t);
+}
+
+/*!
+  Removes all data points.
+  \see removeData, removeDataAfter, removeDataBefore
+*/
+void QCPCurve::clearData()
+{
+  mData->clear();
+}
+
+/* inherits documentation from base class */
+double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if ((onlySelectable && !mSelectable) || mData->isEmpty())
+    return -1;
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
+
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+    return pointDistance(pos);
+  else
+    return -1;
+}
+
+/* inherits documentation from base class */
+void QCPCurve::draw(QCPPainter *painter)
+{
+  if (mData->isEmpty()) return;
+
+  // allocate line vector:
+  QVector<QPointF> *lineData = new QVector<QPointF>;
+
+  // fill with curve data:
+  getCurveData(lineData);
+
+  // check data validity if flag set:
+#ifdef QCUSTOMPLOT_CHECK_DATA
+  QCPCurveDataMap::const_iterator it;
+  for (it = mData->constBegin(); it != mData->constEnd(); ++it)
+  {
+    if (QCP::isInvalidData(it.value().t) ||
+        QCP::isInvalidData(it.value().key, it.value().value))
+      qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "invalid." << "Plottable name:" << name();
+  }
+#endif
+
+  // draw curve fill:
+  if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
+  {
+    applyFillAntialiasingHint(painter);
+    painter->setPen(Qt::NoPen);
+    painter->setBrush(mainBrush());
+    painter->drawPolygon(QPolygonF(*lineData));
+  }
+
+  // draw curve line:
+  if (mLineStyle != lsNone && mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
+  {
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mainPen());
+    painter->setBrush(Qt::NoBrush);
+    // if drawing solid line and not in PDF, use much faster line drawing instead of polyline:
+    if (mParentPlot->plottingHints().testFlag(QCP::phFastPolylines) &&
+        painter->pen().style() == Qt::SolidLine &&
+        !painter->modes().testFlag(QCPPainter::pmVectorized) &&
+        !painter->modes().testFlag(QCPPainter::pmNoCaching))
+    {
+      int i = 0;
+      bool lastIsNan = false;
+      const int lineDataSize = lineData->size();
+      while (i < lineDataSize && (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x()))) // make sure first point is not NaN
+        ++i;
+      ++i; // because drawing works in 1 point retrospect
+      while (i < lineDataSize)
+      {
+        if (!qIsNaN(lineData->at(i).y()) && !qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
+        {
+          if (!lastIsNan)
+            painter->drawLine(lineData->at(i-1), lineData->at(i));
+          else
+            lastIsNan = false;
+        } else
+          lastIsNan = true;
+        ++i;
+      }
+    } else
+    {
+      int segmentStart = 0;
+      int i = 0;
+      const int lineDataSize = lineData->size();
+      while (i < lineDataSize)
+      {
+        if (qIsNaN(lineData->at(i).y()) || qIsNaN(lineData->at(i).x())) // NaNs create a gap in the line
+        {
+          painter->drawPolyline(lineData->constData()+segmentStart, i-segmentStart); // i, because we don't want to include the current NaN point
+          segmentStart = i+1;
+        }
+        ++i;
+      }
+      // draw last segment:
+      painter->drawPolyline(lineData->constData()+segmentStart, lineDataSize-segmentStart);
+    }
+  }
+
+  // draw scatters:
+  if (!mScatterStyle.isNone())
+    drawScatterPlot(painter, lineData);
+
+  // free allocated line data:
+  delete lineData;
+}
+
+/* inherits documentation from base class */
+void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
+{
+  // draw fill:
+  if (mBrush.style() != Qt::NoBrush)
+  {
+    applyFillAntialiasingHint(painter);
+    painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
+  }
+  // draw line vertically centered:
+  if (mLineStyle != lsNone)
+  {
+    applyDefaultAntialiasingHint(painter);
+    painter->setPen(mPen);
+    painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
+  }
+  // draw scatter symbol:
+  if (!mScatterStyle.isNone())
+  {
+    applyScattersAntialiasingHint(painter);
+    // scale scatter pixmap if it's too large to fit in legend icon rect:
+    if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
+    {
+      QCPScatterStyle scaledStyle(mScatterStyle);
+      scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
+      scaledStyle.applyTo(painter, mPen);
+      scaledStyle.drawShape(painter, QRectF(rect).center());
+    } else
+    {
+      mScatterStyle.applyTo(painter, mPen);
+      mScatterStyle.drawShape(painter, QRectF(rect).center());
+    }
+  }
+}
+
+/*! \internal
+
+  Draws scatter symbols at every data point passed in \a pointData. scatter symbols are independent of
+  the line style and are always drawn if scatter shape is not \ref QCPScatterStyle::ssNone.
+*/
+void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const
+{
+  // draw scatter point symbols:
+  applyScattersAntialiasingHint(painter);
+  mScatterStyle.applyTo(painter, mPen);
+  for (int i=0; i<pointData->size(); ++i)
+    if (!qIsNaN(pointData->at(i).x()) && !qIsNaN(pointData->at(i).y()))
+      mScatterStyle.drawShape(painter,  pointData->at(i));
+}
+
+/*! \internal
+
+  called by QCPCurve::draw to generate a point vector (in pixel coordinates) which represents the
+  line of the curve.
+
+  Line segments that aren't visible in the current axis rect are handled in an optimized way. They
+  are projected onto a rectangle slightly larger than the visible axis rect and simplified
+  regarding point count. The algorithm makes sure to preserve appearance of lines and fills inside
+  the visible axis rect by generating new temporary points on the outer rect if necessary.
+
+  Methods that are also involved in the algorithm are: \ref getRegion, \ref getOptimizedPoint, \ref
+  getOptimizedCornerPoints \ref mayTraverse, \ref getTraverse, \ref getTraverseCornerPoints.
+*/
+void QCPCurve::getCurveData(QVector<QPointF> *lineData) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+
+  // add margins to rect to compensate for stroke width
+  double strokeMargin = qMax(qreal(1.0), qreal(mainPen().widthF()*0.75)); // stroke radius + 50% safety
+  if (!mScatterStyle.isNone())
+    strokeMargin = qMax(strokeMargin, mScatterStyle.size());
+  double rectLeft = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
+  double rectRight = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*((keyAxis->orientation()==Qt::Vertical)!=keyAxis->rangeReversed()?-1:1));
+  double rectBottom = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)+strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
+  double rectTop = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)-strokeMargin*((valueAxis->orientation()==Qt::Horizontal)!=valueAxis->rangeReversed()?-1:1));
+  int currentRegion;
+  QCPCurveDataMap::const_iterator it = mData->constBegin();
+  QCPCurveDataMap::const_iterator prevIt = mData->constEnd()-1;
+  int prevRegion = getRegion(prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom);
+  QVector<QPointF> trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right)
+  while (it != mData->constEnd())
+  {
+    currentRegion = getRegion(it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
+    if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
+    {
+      if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
+      {
+        QPointF crossA, crossB;
+        if (prevRegion == 5) // we're coming from R, so add this point optimized
+        {
+          lineData->append(getOptimizedPoint(currentRegion, it.value().key, it.value().value, prevIt.value().key, prevIt.value().value, rectLeft, rectTop, rectRight, rectBottom));
+          // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point
+          *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
+        } else if (mayTraverse(prevRegion, currentRegion) &&
+                   getTraverse(prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom, crossA, crossB))
+        {
+          // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
+          QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
+          getTraverseCornerPoints(prevRegion, currentRegion, rectLeft, rectTop, rectRight, rectBottom, beforeTraverseCornerPoints, afterTraverseCornerPoints);
+          if (it != mData->constBegin())
+          {
+            *lineData << beforeTraverseCornerPoints;
+            lineData->append(crossA);
+            lineData->append(crossB);
+            *lineData << afterTraverseCornerPoints;
+          } else
+          {
+            lineData->append(crossB);
+            *lineData << afterTraverseCornerPoints;
+            trailingPoints << beforeTraverseCornerPoints << crossA ;
+          }
+        } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s)
+        {
+          *lineData << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
+        }
+      } else // segment does end in R, so we add previous point optimized and this point at original position
+      {
+        if (it == mData->constBegin()) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end
+          trailingPoints << getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom);
+        else
+          lineData->append(getOptimizedPoint(prevRegion, prevIt.value().key, prevIt.value().value, it.value().key, it.value().value, rectLeft, rectTop, rectRight, rectBottom));
+        lineData->append(coordsToPixels(it.value().key, it.value().value));
+      }
+    } else // region didn't change
+    {
+      if (currentRegion == 5) // still in R, keep adding original points
+      {
+        lineData->append(coordsToPixels(it.value().key, it.value().value));
+      } else // still outside R, no need to add anything
+      {
+        // see how this is not doing anything? That's the main optimization...
+      }
+    }
+    prevIt = it;
+    prevRegion = currentRegion;
+    ++it;
+  }
+  *lineData << trailingPoints;
+}
+
+/*! \internal
+
+  This function is part of the curve optimization algorithm of \ref getCurveData.
+
+  It returns the region of the given point (\a x, \a y) with respect to a rectangle defined by \a
+  rectLeft, \a rectTop, \a rectRight, and \a rectBottom.
+
+  The regions are enumerated from top to bottom and left to right:
+
+  <table style="width:10em; text-align:center">
+    <tr><td>1</td><td>4</td><td>7</td></tr>
+    <tr><td>2</td><td style="border:1px solid black">5</td><td>8</td></tr>
+    <tr><td>3</td><td>6</td><td>9</td></tr>
+  </table>
+
+  With the rectangle being region 5, and the outer regions extending infinitely outwards. In the
+  curve optimization algorithm, region 5 is considered to be the visible portion of the plot.
+*/
+int QCPCurve::getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const
+{
+  if (x < rectLeft) // region 123
+  {
+    if (y > rectTop)
+      return 1;
+    else if (y < rectBottom)
+      return 3;
+    else
+      return 2;
+  } else if (x > rectRight) // region 789
+  {
+    if (y > rectTop)
+      return 7;
+    else if (y < rectBottom)
+      return 9;
+    else
+      return 8;
+  } else // region 456
+  {
+    if (y > rectTop)
+      return 4;
+    else if (y < rectBottom)
+      return 6;
+    else
+      return 5;
+  }
+}
+
+/*! \internal
+
+  This function is part of the curve optimization algorithm of \ref getCurveData.
+
+  This method is used in case the current segment passes from inside the visible rect (region 5,
+  see \ref getRegion) to any of the outer regions (\a otherRegion). The current segment is given by
+  the line connecting (\a key, \a value) with (\a otherKey, \a otherValue).
+
+  It returns the intersection point of the segment with the border of region 5.
+
+  For this function it doesn't matter whether (\a key, \a value) is the point inside region 5 or
+  whether it's (\a otherKey, \a otherValue), i.e. whether the segment is coming from region 5 or
+  leaving it. It is important though that \a otherRegion correctly identifies the other region not
+  equal to 5.
+*/
+QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
+{
+  double intersectKey = rectLeft; // initial value is just fail-safe
+  double intersectValue = rectTop; // initial value is just fail-safe
+  switch (otherRegion)
+  {
+    case 1: // top and left edge
+    {
+      intersectValue = rectTop;
+      intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
+      if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
+      {
+        intersectKey = rectLeft;
+        intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
+      }
+      break;
+    }
+    case 2: // left edge
+    {
+      intersectKey = rectLeft;
+      intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
+      break;
+    }
+    case 3: // bottom and left edge
+    {
+      intersectValue = rectBottom;
+      intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
+      if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
+      {
+        intersectKey = rectLeft;
+        intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
+      }
+      break;
+    }
+    case 4: // top edge
+    {
+      intersectValue = rectTop;
+      intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
+      break;
+    }
+    case 5:
+    {
+      break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
+    }
+    case 6: // bottom edge
+    {
+      intersectValue = rectBottom;
+      intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
+      break;
+    }
+    case 7: // top and right edge
+    {
+      intersectValue = rectTop;
+      intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
+      if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
+      {
+        intersectKey = rectRight;
+        intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
+      }
+      break;
+    }
+    case 8: // right edge
+    {
+      intersectKey = rectRight;
+      intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
+      break;
+    }
+    case 9: // bottom and right edge
+    {
+      intersectValue = rectBottom;
+      intersectKey = otherKey + (key-otherKey)/(value-otherValue)*(intersectValue-otherValue);
+      if (intersectKey < rectLeft || intersectKey > rectRight) // doesn't intersect, so must intersect other:
+      {
+        intersectKey = rectRight;
+        intersectValue = otherValue + (value-otherValue)/(key-otherKey)*(intersectKey-otherKey);
+      }
+      break;
+    }
+  }
+  return coordsToPixels(intersectKey, intersectValue);
+}
+
+/*! \internal
+
+  This function is part of the curve optimization algorithm of \ref getCurveData.
+
+  In situations where a single segment skips over multiple regions it might become necessary to add
+  extra points at the corners of region 5 (see \ref getRegion) such that the optimized segment
+  doesn't unintentionally cut through the visible area of the axis rect and create plot artifacts.
+  This method provides these points that must be added, assuming the original segment doesn't
+  start, end, or traverse region 5. (Corner points where region 5 is traversed are calculated by
+  \ref getTraverseCornerPoints.)
+
+  For example, consider a segment which directly goes from region 4 to 2 but originally is far out
+  to the top left such that it doesn't cross region 5. Naively optimizing these points by
+  projecting them on the top and left borders of region 5 will create a segment that surely crosses
+  5, creating a visual artifact in the plot. This method prevents this by providing extra points at
+  the top left corner, making the optimized curve correctly pass from region 4 to 1 to 2 without
+  traversing 5.
+*/
+QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const
+{
+  QVector<QPointF> result;
+  switch (prevRegion)
+  {
+    case 1:
+    {
+      switch (currentRegion)
+      {
+        case 2: { result << coordsToPixels(rectLeft, rectTop); break; }
+        case 4: { result << coordsToPixels(rectLeft, rectTop); break; }
+        case 3: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); break; }
+        case 7: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); break; }
+        case 6: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
+        case 8: { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
+        case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
+          if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
+          { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
+          else
+          { result << coordsToPixels(rectLeft, rectTop) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); }
+          break;
+        }
+      }
+      break;
+    }
+    case 2:
+    {
+      switch (currentRegion)
+      {
+        case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
+        case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
+        case 4: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
+        case 6: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
+        case 7: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
+        case 9: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
+      }
+      break;
+    }
+    case 3:
+    {
+      switch (currentRegion)
+      {
+        case 2: { result << coordsToPixels(rectLeft, rectBottom); break; }
+        case 6: { result << coordsToPixels(rectLeft, rectBottom); break; }
+        case 1: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); break; }
+        case 9: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); break; }
+        case 4: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
+        case 8: { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
+        case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
+          if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
+          { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
+          else
+          { result << coordsToPixels(rectLeft, rectBottom) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); }
+          break;
+        }
+      }
+      break;
+    }
+    case 4:
+    {
+      switch (currentRegion)
+      {
+        case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
+        case 7: { result << coordsToPixels(rectRight, rectTop); break; }
+        case 2: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
+        case 8: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
+        case 3: { result << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
+        case 9: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectRight, rectBottom); break; }
+      }
+      break;
+    }
+    case 5:
+    {
+      switch (currentRegion)
+      {
+        case 1: { result << coordsToPixels(rectLeft, rectTop); break; }
+        case 7: { result << coordsToPixels(rectRight, rectTop); break; }
+        case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
+        case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
+      }
+      break;
+    }
+    case 6:
+    {
+      switch (currentRegion)
+      {
+        case 3: { result << coordsToPixels(rectLeft, rectBottom); break; }
+        case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
+        case 2: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
+        case 8: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
+        case 1: { result << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
+        case 7: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectRight, rectTop); break; }
+      }
+      break;
+    }
+    case 7:
+    {
+      switch (currentRegion)
+      {
+        case 4: { result << coordsToPixels(rectRight, rectTop); break; }
+        case 8: { result << coordsToPixels(rectRight, rectTop); break; }
+        case 1: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); break; }
+        case 9: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); break; }
+        case 2: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); break; }
+        case 6: { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
+        case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
+          if ((value-prevValue)/(key-prevKey)*(rectRight-key)+value < rectBottom) // segment passes below R
+          { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
+          else
+          { result << coordsToPixels(rectRight, rectTop) << coordsToPixels(rectLeft, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); }
+          break;
+        }
+      }
+      break;
+    }
+    case 8:
+    {
+      switch (currentRegion)
+      {
+        case 7: { result << coordsToPixels(rectRight, rectTop); break; }
+        case 9: { result << coordsToPixels(rectRight, rectBottom); break; }
+        case 4: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
+        case 6: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); break; }
+        case 1: { result << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); break; }
+        case 3: { result << coordsToPixels(rectRight, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectBottom); break; }
+      }
+      break;
+    }
+    case 9:
+    {
+      switch (currentRegion)
+      {
+        case 6: { result << coordsToPixels(rectRight, rectBottom); break; }
+        case 8: { result << coordsToPixels(rectRight, rectBottom); break; }
+        case 3: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); break; }
+        case 7: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); break; }
+        case 2: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); break; }
+        case 4: { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); break; }
+        case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
+          if ((value-prevValue)/(key-prevKey)*(rectLeft-key)+value < rectBottom) // segment passes below R
+          { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectLeft, rectBottom); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
+          else
+          { result << coordsToPixels(rectRight, rectBottom) << coordsToPixels(rectRight, rectTop); result.append(result.last()); result << coordsToPixels(rectLeft, rectTop); }
+          break;
+        }
+      }
+      break;
+    }
+  }
+  return result;
+}
+
+/*! \internal
+
+  This function is part of the curve optimization algorithm of \ref getCurveData.
+
+  This method returns whether a segment going from \a prevRegion to \a currentRegion (see \ref
+  getRegion) may traverse the visible region 5. This function assumes that neither \a prevRegion
+  nor \a currentRegion is 5 itself.
+
+  If this method returns false, the segment for sure doesn't pass region 5. If it returns true, the
+  segment may or may not pass region 5 and a more fine-grained calculation must be used (\ref
+  getTraverse).
+*/
+bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
+{
+  switch (prevRegion)
+  {
+    case 1:
+    {
+      switch (currentRegion)
+      {
+        case 4:
+        case 7:
+        case 2:
+        case 3: return false;
+        default: return true;
+      }
+    }
+    case 2:
+    {
+      switch (currentRegion)
+      {
+        case 1:
+        case 3: return false;
+        default: return true;
+      }
+    }
+    case 3:
+    {
+      switch (currentRegion)
+      {
+        case 1:
+        case 2:
+        case 6:
+        case 9: return false;
+        default: return true;
+      }
+    }
+    case 4:
+    {
+      switch (currentRegion)
+      {
+        case 1:
+        case 7: return false;
+        default: return true;
+      }
+    }
+    case 5: return false; // should never occur
+    case 6:
+    {
+      switch (currentRegion)
+      {
+        case 3:
+        case 9: return false;
+        default: return true;
+      }
+    }
+    case 7:
+    {
+      switch (currentRegion)
+      {
+        case 1:
+        case 4:
+        case 8:
+        case 9: return false;
+        default: return true;
+      }
+    }
+    case 8:
+    {
+      switch (currentRegion)
+      {
+        case 7:
+        case 9: return false;
+        default: return true;
+      }
+    }
+    case 9:
+    {
+      switch (currentRegion)
+      {
+        case 3:
+        case 6:
+        case 8:
+        case 7: return false;
+        default: return true;
+      }
+    }
+    default: return true;
+  }
+}
+
+
+/*! \internal
+
+  This function is part of the curve optimization algorithm of \ref getCurveData.
+
+  This method assumes that the \ref mayTraverse test has returned true, so there is a chance the
+  segment defined by (\a prevKey, \a prevValue) and (\a key, \a value) goes through the visible
+  region 5.
+
+  The return value of this method indicates whether the segment actually traverses region 5 or not.
+
+  If the segment traverses 5, the output parameters \a crossA and \a crossB indicate the entry and
+  exit points of region 5. They will become the optimized points for that segment.
+*/
+bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const
+{
+  QList<QPointF> intersections; // x of QPointF corresponds to key and y to value
+  if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
+  {
+    // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
+    intersections.append(QPointF(key, rectBottom)); // direction will be taken care of at end of method
+    intersections.append(QPointF(key, rectTop));
+  } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
+  {
+    // due to region filter in mayTraverseR(), if line is parallel to value or key axis, R is traversed here
+    intersections.append(QPointF(rectLeft, value)); // direction will be taken care of at end of method
+    intersections.append(QPointF(rectRight, value));
+  } else // line is skewed
+  {
+    double gamma;
+    double keyPerValue = (key-prevKey)/(value-prevValue);
+    // check top of rect:
+    gamma = prevKey + (rectTop-prevValue)*keyPerValue;
+    if (gamma >= rectLeft && gamma <= rectRight)
+      intersections.append(QPointF(gamma, rectTop));
+    // check bottom of rect:
+    gamma = prevKey + (rectBottom-prevValue)*keyPerValue;
+    if (gamma >= rectLeft && gamma <= rectRight)
+      intersections.append(QPointF(gamma, rectBottom));
+    double valuePerKey = 1.0/keyPerValue;
+    // check left of rect:
+    gamma = prevValue + (rectLeft-prevKey)*valuePerKey;
+    if (gamma >= rectBottom && gamma <= rectTop)
+      intersections.append(QPointF(rectLeft, gamma));
+    // check right of rect:
+    gamma = prevValue + (rectRight-prevKey)*valuePerKey;
+    if (gamma >= rectBottom && gamma <= rectTop)
+      intersections.append(QPointF(rectRight, gamma));
+  }
+
+  // handle cases where found points isn't exactly 2:
+  if (intersections.size() > 2)
+  {
+    // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
+    double distSqrMax = 0;
+    QPointF pv1, pv2;
+    for (int i=0; i<intersections.size()-1; ++i)
+    {
+      for (int k=i+1; k<intersections.size(); ++k)
+      {
+        QPointF distPoint = intersections.at(i)-intersections.at(k);
+        double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
+        if (distSqr > distSqrMax)
+        {
+          pv1 = intersections.at(i);
+          pv2 = intersections.at(k);
+          distSqrMax = distSqr;
+        }
+      }
+    }
+    intersections = QList<QPointF>() << pv1 << pv2;
+  } else if (intersections.size() != 2)
+  {
+    // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
+    return false;
+  }
+
+  // possibly re-sort points so optimized point segment has same direction as original segment:
+  if ((key-prevKey)*(intersections.at(1).x()-intersections.at(0).x()) + (value-prevValue)*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction
+    intersections.move(0, 1);
+  crossA = coordsToPixels(intersections.at(0).x(), intersections.at(0).y());
+  crossB = coordsToPixels(intersections.at(1).x(), intersections.at(1).y());
+  return true;
+}
+
+/*! \internal
+
+  This function is part of the curve optimization algorithm of \ref getCurveData.
+
+  This method assumes that the \ref getTraverse test has returned true, so the segment definitely
+  traverses the visible region 5 when going from \a prevRegion to \a currentRegion.
+
+  In certain situations it is not sufficient to merely generate the entry and exit points of the
+  segment into/out of region 5, as \ref getTraverse provides. It may happen that a single segment, in
+  addition to traversing region 5, skips another region outside of region 5, which makes it
+  necessary to add an optimized corner point there (very similar to the job \ref
+  getOptimizedCornerPoints does for segments that are completely in outside regions and don't
+  traverse 5).
+
+  As an example, consider a segment going from region 1 to region 6, traversing the lower left
+  corner of region 5. In this configuration, the segment additionally crosses the border between
+  region 1 and 2 before entering region 5. This makes it necessary to add an additional point in
+  the top left corner, before adding the optimized traverse points. So in this case, the output
+  parameter \a beforeTraverse will contain the top left corner point, and \a afterTraverse will be
+  empty.
+
+  In some cases, such as when going from region 1 to 9, it may even be necessary to add additional
+  corner points before and after the traverse. Then both \a beforeTraverse and \a afterTraverse
+  return the respective corner points.
+*/
+void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
+{
+  switch (prevRegion)
+  {
+    case 1:
+    {
+      switch (currentRegion)
+      {
+        case 6: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
+        case 9: { beforeTraverse << coordsToPixels(rectLeft, rectTop); afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
+        case 8: { beforeTraverse << coordsToPixels(rectLeft, rectTop); break; }
+      }
+      break;
+    }
+    case 2:
+    {
+      switch (currentRegion)
+      {
+        case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
+        case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
+      }
+      break;
+    }
+    case 3:
+    {
+      switch (currentRegion)
+      {
+        case 4: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
+        case 7: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); afterTraverse << coordsToPixels(rectRight, rectTop); break; }
+        case 8: { beforeTraverse << coordsToPixels(rectLeft, rectBottom); break; }
+      }
+      break;
+    }
+    case 4:
+    {
+      switch (currentRegion)
+      {
+        case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
+        case 9: { afterTraverse << coordsToPixels(rectRight, rectBottom); break; }
+      }
+      break;
+    }
+    case 5: { break; } // shouldn't happen because this method only handles full traverses
+    case 6:
+    {
+      switch (currentRegion)
+      {
+        case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
+        case 7: { afterTraverse << coordsToPixels(rectRight, rectTop); break; }
+      }
+      break;
+    }
+    case 7:
+    {
+      switch (currentRegion)
+      {
+        case 2: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
+        case 3: { beforeTraverse << coordsToPixels(rectRight, rectTop); afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
+        case 6: { beforeTraverse << coordsToPixels(rectRight, rectTop); break; }
+      }
+      break;
+    }
+    case 8:
+    {
+      switch (currentRegion)
+      {
+        case 1: { afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
+        case 3: { afterTraverse << coordsToPixels(rectLeft, rectBottom); break; }
+      }
+      break;
+    }
+    case 9:
+    {
+      switch (currentRegion)
+      {
+        case 2: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
+        case 1: { beforeTraverse << coordsToPixels(rectRight, rectBottom); afterTraverse << coordsToPixels(rectLeft, rectTop); break; }
+        case 4: { beforeTraverse << coordsToPixels(rectRight, rectBottom); break; }
+      }
+      break;
+    }
+  }
+}
+
+/*! \internal
+
+  Calculates the (minimum) distance (in pixels) the curve's representation has from the given \a
+  pixelPoint in pixels. This is used to determine whether the curve was clicked or not, e.g. in
+  \ref selectTest.
+*/
+double QCPCurve::pointDistance(const QPointF &pixelPoint) const
+{
+  if (mData->isEmpty())
+  {
+    qDebug() << Q_FUNC_INFO << "requested point distance on curve" << mName << "without data";
+    return 500;
+  }
+  if (mData->size() == 1)
+  {
+    QPointF dataPoint = coordsToPixels(mData->constBegin().key(), mData->constBegin().value().value);
+    return QVector2D(dataPoint-pixelPoint).length();
+  }
+
+  // calculate minimum distance to line segments:
+  QVector<QPointF> *lineData = new QVector<QPointF>;
+  getCurveData(lineData);
+  double minDistSqr = std::numeric_limits<double>::max();
+  for (int i=0; i<lineData->size()-1; ++i)
+  {
+    double currentDistSqr = distSqrToLine(lineData->at(i), lineData->at(i+1), pixelPoint);
+    if (currentDistSqr < minDistSqr)
+      minDistSqr = currentDistSqr;
+  }
+  delete lineData;
+  return qSqrt(minDistSqr);
+}
+
+/* inherits documentation from base class */
+QCPRange QCPCurve::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+
+  double current;
+
+  QCPCurveDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    current = it.value().key;
+    if (!qIsNaN(current) && !qIsNaN(it.value().value))
+    {
+      if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
+      {
+        if (current < range.lower || !haveLower)
+        {
+          range.lower = current;
+          haveLower = true;
+        }
+        if (current > range.upper || !haveUpper)
+        {
+          range.upper = current;
+          haveUpper = true;
+        }
+      }
+    }
+    ++it;
+  }
+
+  foundRange = haveLower && haveUpper;
+  return range;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPCurve::getValueRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+
+  double current;
+
+  QCPCurveDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    current = it.value().value;
+    if (!qIsNaN(current) && !qIsNaN(it.value().key))
+    {
+      if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
+      {
+        if (current < range.lower || !haveLower)
+        {
+          range.lower = current;
+          haveLower = true;
+        }
+        if (current > range.upper || !haveUpper)
+        {
+          range.upper = current;
+          haveUpper = true;
+        }
+      }
+    }
+    ++it;
+  }
+
+  foundRange = haveLower && haveUpper;
+  return range;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPBarsGroup
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPBarsGroup
+  \brief Groups multiple QCPBars together so they appear side by side
+
+  \image html QCPBarsGroup.png
+
+  When showing multiple QCPBars in one plot which have bars at identical keys, it may be desirable
+  to have them appearing next to each other at each key. This is what adding the respective QCPBars
+  plottables to a QCPBarsGroup achieves. (An alternative approach is to stack them on top of each
+  other, see \ref QCPBars::moveAbove.)
+
+  \section qcpbarsgroup-usage Usage
+
+  To add a QCPBars plottable to the group, create a new group and then add the respective bars
+  intances:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbarsgroup-creation
+  Alternatively to appending to the group like shown above, you can also set the group on the
+  QCPBars plottable via \ref QCPBars::setBarsGroup.
+
+  The spacing between the bars can be configured via \ref setSpacingType and \ref setSpacing. The
+  bars in this group appear in the plot in the order they were appended. To insert a bars plottable
+  at a certain index position, or to reposition a bars plottable which is already in the group, use
+  \ref insert.
+
+  To remove specific bars from the group, use either \ref remove or call \ref
+  QCPBars::setBarsGroup "QCPBars::setBarsGroup(0)" on the respective bars plottable.
+
+  To clear the entire group, call \ref clear, or simply delete the group.
+
+  \section qcpbarsgroup-example Example
+
+  The image above is generated with the following code:
+  \snippet documentation/doc-image-generator/mainwindow.cpp qcpbarsgroup-example
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn QList<QCPBars*> QCPBarsGroup::bars() const
+
+  Returns all bars currently in this group.
+
+  \see bars(int index)
+*/
+
+/*! \fn int QCPBarsGroup::size() const
+
+  Returns the number of QCPBars plottables that are part of this group.
+
+*/
+
+/*! \fn bool QCPBarsGroup::isEmpty() const
+
+  Returns whether this bars group is empty.
+
+  \see size
+*/
+
+/*! \fn bool QCPBarsGroup::contains(QCPBars *bars)
+
+  Returns whether the specified \a bars plottable is part of this group.
+
+*/
+
+/* end of documentation of inline functions */
+
+/*!
+  Constructs a new bars group for the specified QCustomPlot instance.
+*/
+QCPBarsGroup::QCPBarsGroup(QCustomPlot *parentPlot) :
+  QObject(parentPlot),
+  mParentPlot(parentPlot),
+  mSpacingType(stAbsolute),
+  mSpacing(4)
+{
+}
+
+QCPBarsGroup::~QCPBarsGroup()
+{
+  clear();
+}
+
+/*!
+  Sets how the spacing between adjacent bars is interpreted. See \ref SpacingType.
+
+  The actual spacing can then be specified with \ref setSpacing.
+
+  \see setSpacing
+*/
+void QCPBarsGroup::setSpacingType(SpacingType spacingType)
+{
+  mSpacingType = spacingType;
+}
+
+/*!
+  Sets the spacing between adjacent bars. What the number passed as \a spacing actually means, is
+  defined by the current \ref SpacingType, which can be set with \ref setSpacingType.
+
+  \see setSpacingType
+*/
+void QCPBarsGroup::setSpacing(double spacing)
+{
+  mSpacing = spacing;
+}
+
+/*!
+  Returns the QCPBars instance with the specified \a index in this group. If no such QCPBars
+  exists, returns 0.
+
+  \see bars(), size
+*/
+QCPBars *QCPBarsGroup::bars(int index) const
+{
+  if (index >= 0 && index < mBars.size())
+  {
+    return mBars.at(index);
+  } else
+  {
+    qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
+    return 0;
+  }
+}
+
+/*!
+  Removes all QCPBars plottables from this group.
+
+  \see isEmpty
+*/
+void QCPBarsGroup::clear()
+{
+  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
+    bars->setBarsGroup(0); // removes itself via removeBars
+}
+
+/*!
+  Adds the specified \a bars plottable to this group. Alternatively, you can also use \ref
+  QCPBars::setBarsGroup on the \a bars instance.
+
+  \see insert, remove
+*/
+void QCPBarsGroup::append(QCPBars *bars)
+{
+  if (!bars)
+  {
+    qDebug() << Q_FUNC_INFO << "bars is 0";
+    return;
+  }
+
+  if (!mBars.contains(bars))
+    bars->setBarsGroup(this);
+  else
+    qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
+}
+
+/*!
+  Inserts the specified \a bars plottable into this group at the specified index position \a i.
+  This gives you full control over the ordering of the bars.
+
+  \a bars may already be part of this group. In that case, \a bars is just moved to the new index
+  position.
+
+  \see append, remove
+*/
+void QCPBarsGroup::insert(int i, QCPBars *bars)
+{
+  if (!bars)
+  {
+    qDebug() << Q_FUNC_INFO << "bars is 0";
+    return;
+  }
+
+  // first append to bars list normally:
+  if (!mBars.contains(bars))
+    bars->setBarsGroup(this);
+  // then move to according position:
+  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
+}
+
+/*!
+  Removes the specified \a bars plottable from this group.
+
+  \see contains, clear
+*/
+void QCPBarsGroup::remove(QCPBars *bars)
+{
+  if (!bars)
+  {
+    qDebug() << Q_FUNC_INFO << "bars is 0";
+    return;
+  }
+
+  if (mBars.contains(bars))
+    bars->setBarsGroup(0);
+  else
+    qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
+}
+
+/*! \internal
+
+  Adds the specified \a bars to the internal mBars list of bars. This method does not change the
+  barsGroup property on \a bars.
+
+  \see unregisterBars
+*/
+void QCPBarsGroup::registerBars(QCPBars *bars)
+{
+  if (!mBars.contains(bars))
+    mBars.append(bars);
+}
+
+/*! \internal
+
+  Removes the specified \a bars from the internal mBars list of bars. This method does not change
+  the barsGroup property on \a bars.
+
+  \see registerBars
+*/
+void QCPBarsGroup::unregisterBars(QCPBars *bars)
+{
+  mBars.removeOne(bars);
+}
+
+/*! \internal
+
+  Returns the pixel offset in the key dimension the specified \a bars plottable should have at the
+  given key coordinate \a keyCoord. The offset is relative to the pixel position of the key
+  coordinate \a keyCoord.
+*/
+double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
+{
+  // find list of all base bars in case some mBars are stacked:
+  QList<const QCPBars*> baseBars;
+  foreach (const QCPBars *b, mBars)
+  {
+    while (b->barBelow())
+      b = b->barBelow();
+    if (!baseBars.contains(b))
+      baseBars.append(b);
+  }
+  // find base bar this "bars" is stacked on:
+  const QCPBars *thisBase = bars;
+  while (thisBase->barBelow())
+    thisBase = thisBase->barBelow();
+
+  // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
+  double result = 0;
+  int index = baseBars.indexOf(thisBase);
+  if (index >= 0)
+  {
+    int startIndex;
+    double lowerPixelWidth, upperPixelWidth;
+    if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
+    {
+      return result;
+    } else if (index < (baseBars.size()-1)/2.0) // bar is to the left of center
+    {
+      if (baseBars.size() % 2 == 0) // even number of bars
+      {
+        startIndex = baseBars.size()/2-1;
+        result -= getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
+      } else // uneven number of bars
+      {
+        startIndex = (baseBars.size()-1)/2-1;
+        baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
+        result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
+        result -= getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
+      }
+      for (int i=startIndex; i>index; --i) // add widths and spacings of bars in between center and our bars
+      {
+        baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
+        result -= qAbs(upperPixelWidth-lowerPixelWidth);
+        result -= getPixelSpacing(baseBars.at(i), keyCoord);
+      }
+      // finally half of our bars width:
+      baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
+      result -= qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
+    } else // bar is to the right of center
+    {
+      if (baseBars.size() % 2 == 0) // even number of bars
+      {
+        startIndex = baseBars.size()/2;
+        result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
+      } else // uneven number of bars
+      {
+        startIndex = (baseBars.size()-1)/2+1;
+        baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
+        result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
+        result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
+      }
+      for (int i=startIndex; i<index; ++i) // add widths and spacings of bars in between center and our bars
+      {
+        baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
+        result += qAbs(upperPixelWidth-lowerPixelWidth);
+        result += getPixelSpacing(baseBars.at(i), keyCoord);
+      }
+      // finally half of our bars width:
+      baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
+      result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
+    }
+  }
+  return result;
+}
+
+/*! \internal
+
+  Returns the spacing in pixels which is between this \a bars and the following one, both at the
+  key coordinate \a keyCoord.
+
+  \note Typically the returned value doesn't depend on \a bars or \a keyCoord. \a bars is only
+  needed to get acces to the key axis transformation and axis rect for the modes \ref
+  stAxisRectRatio and \ref stPlotCoords. The \a keyCoord is only relevant for spacings given in
+  \ref stPlotCoords on a logarithmic axis.
+*/
+double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
+{
+  switch (mSpacingType)
+  {
+    case stAbsolute:
+    {
+      return mSpacing;
+    }
+    case stAxisRectRatio:
+    {
+      if (bars->keyAxis()->orientation() == Qt::Horizontal)
+        return bars->keyAxis()->axisRect()->width()*mSpacing;
+      else
+        return bars->keyAxis()->axisRect()->height()*mSpacing;
+    }
+    case stPlotCoords:
+    {
+      double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
+      return bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel;
+    }
+  }
+  return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPBarData
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPBarData
+  \brief Holds the data of one single data point (one bar) for QCPBars.
+
+  The container for storing multiple data points is \ref QCPBarDataMap.
+
+  The stored data is:
+  \li \a key: coordinate on the key axis of this bar
+  \li \a value: height coordinate on the value axis of this bar
+
+  \see QCPBarDataaMap
+*/
+
+/*!
+  Constructs a bar data point with key and value set to zero.
+*/
+QCPBarData::QCPBarData() :
+  key(0),
+  value(0)
+{
+}
+
+/*!
+  Constructs a bar data point with the specified \a key and \a value.
+*/
+QCPBarData::QCPBarData(double key, double value) :
+  key(key),
+  value(value)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPBars
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPBars
+  \brief A plottable representing a bar chart in a plot.
+
+  \image html QCPBars.png
+
+  To plot data, assign it with the \ref setData or \ref addData functions.
+
+  \section appearance Changing the appearance
+
+  The appearance of the bars is determined by the pen and the brush (\ref setPen, \ref setBrush).
+  The width of the individual bars can be controlled with \ref setWidthType and \ref setWidth.
+
+  Bar charts are stackable. This means, two QCPBars plottables can be placed on top of each other
+  (see \ref QCPBars::moveAbove). So when two bars are at the same key position, they will appear
+  stacked.
+
+  If you would like to group multiple QCPBars plottables together so they appear side by side as
+  shown below, use QCPBarsGroup.
+
+  \image html QCPBarsGroup.png
+
+  \section usage Usage
+
+  Like all data representing objects in QCustomPlot, the QCPBars is a plottable
+  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
+  (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
+
+  Usually, you first create an instance:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-1
+  add it to the customPlot with QCustomPlot::addPlottable:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-2
+  and then modify the properties of the newly created plottable, e.g.:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpbars-creation-3
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn QCPBars *QCPBars::barBelow() const
+  Returns the bars plottable that is directly below this bars plottable.
+  If there is no such plottable, returns 0.
+
+  \see barAbove, moveBelow, moveAbove
+*/
+
+/*! \fn QCPBars *QCPBars::barAbove() const
+  Returns the bars plottable that is directly above this bars plottable.
+  If there is no such plottable, returns 0.
+
+  \see barBelow, moveBelow, moveAbove
+*/
+
+/* end of documentation of inline functions */
+
+/*!
+  Constructs a bar chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
+  axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
+  the same orientation. If either of these restrictions is violated, a corresponding message is
+  printed to the debug output (qDebug), the construction is not aborted, though.
+
+  The constructed QCPBars can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
+  then takes ownership of the bar chart.
+*/
+QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis),
+  mData(new QCPBarDataMap),
+  mWidth(0.75),
+  mWidthType(wtPlotCoords),
+  mBarsGroup(0),
+  mBaseValue(0)
+{
+  // modify inherited properties from abstract plottable:
+  mPen.setColor(Qt::blue);
+  mPen.setStyle(Qt::SolidLine);
+  mBrush.setColor(QColor(40, 50, 255, 30));
+  mBrush.setStyle(Qt::SolidPattern);
+  mSelectedPen = mPen;
+  mSelectedPen.setWidthF(2.5);
+  mSelectedPen.setColor(QColor(80, 80, 255)); // lighter than Qt::blue of mPen
+  mSelectedBrush = mBrush;
+}
+
+QCPBars::~QCPBars()
+{
+  setBarsGroup(0);
+  if (mBarBelow || mBarAbove)
+    connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
+  delete mData;
+}
+
+/*!
+  Sets the width of the bars.
+
+  How the number passed as \a width is interpreted (e.g. screen pixels, plot coordinates,...),
+  depends on the currently set width type, see \ref setWidthType and \ref WidthType.
+*/
+void QCPBars::setWidth(double width)
+{
+  mWidth = width;
+}
+
+/*!
+  Sets how the width of the bars is defined. See the documentation of \ref WidthType for an
+  explanation of the possible values for \a widthType.
+
+  The default value is \ref wtPlotCoords.
+
+  \see setWidth
+*/
+void QCPBars::setWidthType(QCPBars::WidthType widthType)
+{
+  mWidthType = widthType;
+}
+
+/*!
+  Sets to which QCPBarsGroup this QCPBars instance belongs to. Alternatively, you can also use \ref
+  QCPBarsGroup::append.
+
+  To remove this QCPBars from any group, set \a barsGroup to 0.
+*/
+void QCPBars::setBarsGroup(QCPBarsGroup *barsGroup)
+{
+  // deregister at old group:
+  if (mBarsGroup)
+    mBarsGroup->unregisterBars(this);
+  mBarsGroup = barsGroup;
+  // register at new group:
+  if (mBarsGroup)
+    mBarsGroup->registerBars(this);
+}
+
+/*!
+  Sets the base value of this bars plottable.
+
+  The base value defines where on the value coordinate the bars start. How far the bars extend from
+  the base value is given by their individual value data. For example, if the base value is set to
+  1, a bar with data value 2 will have its lowest point at value coordinate 1 and highest point at
+  3.
+
+  For stacked bars, only the base value of the bottom-most QCPBars has meaning.
+
+  The default base value is 0.
+*/
+void QCPBars::setBaseValue(double baseValue)
+{
+  mBaseValue = baseValue;
+}
+
+/*!
+  Replaces the current data with the provided \a data.
+
+  If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
+  takes ownership of the passed data and replaces the internal data pointer with it. This is
+  significantly faster than copying for large datasets.
+*/
+void QCPBars::setData(QCPBarDataMap *data, bool copy)
+{
+  if (mData == data)
+  {
+    qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
+    return;
+  }
+  if (copy)
+  {
+    *mData = *data;
+  } else
+  {
+    delete mData;
+    mData = data;
+  }
+}
+
+/*! \overload
+
+  Replaces the current data with the provided points in \a key and \a value tuples. The
+  provided vectors should have equal length. Else, the number of added points will be the size of
+  the smallest vector.
+*/
+void QCPBars::setData(const QVector<double> &key, const QVector<double> &value)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, value.size());
+  QCPBarData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = key[i];
+    newData.value = value[i];
+    mData->insertMulti(newData.key, newData);
+  }
+}
+
+/*!
+  Moves this bars plottable below \a bars. In other words, the bars of this plottable will appear
+  below the bars of \a bars. The move target \a bars must use the same key and value axis as this
+  plottable.
+
+  Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
+  has a bars object below itself, this bars object is inserted between the two. If this bars object
+  is already between two other bars, the two other bars will be stacked on top of each other after
+  the operation.
+
+  To remove this bars plottable from any stacking, set \a bars to 0.
+
+  \see moveBelow, barAbove, barBelow
+*/
+void QCPBars::moveBelow(QCPBars *bars)
+{
+  if (bars == this) return;
+  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
+  {
+    qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
+    return;
+  }
+  // remove from stacking:
+  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
+  // if new bar given, insert this bar below it:
+  if (bars)
+  {
+    if (bars->mBarBelow)
+      connectBars(bars->mBarBelow.data(), this);
+    connectBars(this, bars);
+  }
+}
+
+/*!
+  Moves this bars plottable above \a bars. In other words, the bars of this plottable will appear
+  above the bars of \a bars. The move target \a bars must use the same key and value axis as this
+  plottable.
+
+  Inserting into and removing from existing bar stacking is handled gracefully. If \a bars already
+  has a bars object below itself, this bars object is inserted between the two. If this bars object
+  is already between two other bars, the two other bars will be stacked on top of each other after
+  the operation.
+
+  To remove this bars plottable from any stacking, set \a bars to 0.
+
+  \see moveBelow, barBelow, barAbove
+*/
+void QCPBars::moveAbove(QCPBars *bars)
+{
+  if (bars == this) return;
+  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
+  {
+    qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
+    return;
+  }
+  // remove from stacking:
+  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
+  // if new bar given, insert this bar above it:
+  if (bars)
+  {
+    if (bars->mBarAbove)
+      connectBars(this, bars->mBarAbove.data());
+    connectBars(bars, this);
+  }
+}
+
+/*!
+  Adds the provided data points in \a dataMap to the current data.
+  \see removeData
+*/
+void QCPBars::addData(const QCPBarDataMap &dataMap)
+{
+  mData->unite(dataMap);
+}
+
+/*! \overload
+  Adds the provided single data point in \a data to the current data.
+  \see removeData
+*/
+void QCPBars::addData(const QCPBarData &data)
+{
+  mData->insertMulti(data.key, data);
+}
+
+/*! \overload
+  Adds the provided single data point as \a key and \a value tuple to the current data
+  \see removeData
+*/
+void QCPBars::addData(double key, double value)
+{
+  QCPBarData newData;
+  newData.key = key;
+  newData.value = value;
+  mData->insertMulti(newData.key, newData);
+}
+
+/*! \overload
+  Adds the provided data points as \a key and \a value tuples to the current data.
+  \see removeData
+*/
+void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values)
+{
+  int n = keys.size();
+  n = qMin(n, values.size());
+  QCPBarData newData;
+  for (int i=0; i<n; ++i)
+  {
+    newData.key = keys[i];
+    newData.value = values[i];
+    mData->insertMulti(newData.key, newData);
+  }
+}
+
+/*!
+  Removes all data points with key smaller than \a key.
+  \see addData, clearData
+*/
+void QCPBars::removeDataBefore(double key)
+{
+  QCPBarDataMap::iterator it = mData->begin();
+  while (it != mData->end() && it.key() < key)
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with key greater than \a key.
+  \see addData, clearData
+*/
+void QCPBars::removeDataAfter(double key)
+{
+  if (mData->isEmpty()) return;
+  QCPBarDataMap::iterator it = mData->upperBound(key);
+  while (it != mData->end())
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with key between \a fromKey and \a toKey. if \a fromKey is
+  greater or equal to \a toKey, the function does nothing. To remove a single data point with known
+  key, use \ref removeData(double key).
+
+  \see addData, clearData
+*/
+void QCPBars::removeData(double fromKey, double toKey)
+{
+  if (fromKey >= toKey || mData->isEmpty()) return;
+  QCPBarDataMap::iterator it = mData->upperBound(fromKey);
+  QCPBarDataMap::iterator itEnd = mData->upperBound(toKey);
+  while (it != itEnd)
+    it = mData->erase(it);
+}
+
+/*! \overload
+
+  Removes a single data point at \a key. If the position is not known with absolute precision,
+  consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
+  around the suspected position, depeding on the precision with which the key is known.
+
+  \see addData, clearData
+*/
+void QCPBars::removeData(double key)
+{
+  mData->remove(key);
+}
+
+/*!
+  Removes all data points.
+  \see removeData, removeDataAfter, removeDataBefore
+*/
+void QCPBars::clearData()
+{
+  mData->clear();
+}
+
+/* inherits documentation from base class */
+double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
+
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  {
+    QCPBarDataMap::ConstIterator it;
+    for (it = mData->constBegin(); it != mData->constEnd(); ++it)
+    {
+      if (getBarPolygon(it.value().key, it.value().value).boundingRect().contains(pos))
+        return mParentPlot->selectionTolerance()*0.99;
+    }
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+void QCPBars::draw(QCPPainter *painter)
+{
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+  if (mData->isEmpty()) return;
+
+  QCPBarDataMap::const_iterator it, lower, upperEnd;
+  getVisibleDataBounds(lower, upperEnd);
+  for (it = lower; it != upperEnd; ++it)
+  {
+    // check data validity if flag set:
+#ifdef QCUSTOMPLOT_CHECK_DATA
+    if (QCP::isInvalidData(it.value().key, it.value().value))
+      qDebug() << Q_FUNC_INFO << "Data point at" << it.key() << "of drawn range invalid." << "Plottable name:" << name();
+#endif
+    QPolygonF barPolygon = getBarPolygon(it.key(), it.value().value);
+    // draw bar fill:
+    if (mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0)
+    {
+      applyFillAntialiasingHint(painter);
+      painter->setPen(Qt::NoPen);
+      painter->setBrush(mainBrush());
+      painter->drawPolygon(barPolygon);
+    }
+    // draw bar line:
+    if (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0)
+    {
+      applyDefaultAntialiasingHint(painter);
+      painter->setPen(mainPen());
+      painter->setBrush(Qt::NoBrush);
+      painter->drawPolyline(barPolygon);
+    }
+  }
+}
+
+/* inherits documentation from base class */
+void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
+{
+  // draw filled rect:
+  applyDefaultAntialiasingHint(painter);
+  painter->setBrush(mBrush);
+  painter->setPen(mPen);
+  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
+  r.moveCenter(rect.center());
+  painter->drawRect(r);
+}
+
+/*!  \internal
+
+  called by \ref draw to determine which data (key) range is visible at the current key axis range
+  setting, so only that needs to be processed. It also takes into account the bar width.
+
+  \a lower returns an iterator to the lowest data point that needs to be taken into account when
+  plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
+  lower may still be just outside the visible range.
+
+  \a upperEnd returns an iterator one higher than the highest visible data point. Same as before, \a
+  upperEnd may also lie just outside of the visible range.
+
+  if the bars plottable contains no data, both \a lower and \a upperEnd point to constEnd.
+*/
+void QCPBars::getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const
+{
+  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
+  if (mData->isEmpty())
+  {
+    lower = mData->constEnd();
+    upperEnd = mData->constEnd();
+    return;
+  }
+
+  // get visible data range as QMap iterators
+  lower = mData->lowerBound(mKeyAxis.data()->range().lower);
+  upperEnd = mData->upperBound(mKeyAxis.data()->range().upper);
+  double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
+  double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
+  bool isVisible = false;
+  // walk left from lbound to find lower bar that actually is completely outside visible pixel range:
+  QCPBarDataMap::const_iterator it = lower;
+  while (it != mData->constBegin())
+  {
+    --it;
+    QRectF barBounds = getBarPolygon(it.value().key, it.value().value).boundingRect();
+    if (mKeyAxis.data()->orientation() == Qt::Horizontal)
+      isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.left() <= lowerPixelBound));
+    else // keyaxis is vertical
+      isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= lowerPixelBound));
+    if (isVisible)
+      lower = it;
+    else
+      break;
+  }
+  // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
+  it = upperEnd;
+  while (it != mData->constEnd())
+  {
+    QRectF barBounds = getBarPolygon(upperEnd.value().key, upperEnd.value().value).boundingRect();
+    if (mKeyAxis.data()->orientation() == Qt::Horizontal)
+      isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.right() >= upperPixelBound));
+    else // keyaxis is vertical
+      isVisible = ((!mKeyAxis.data()->rangeReversed() && barBounds.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barBounds.top() <= upperPixelBound));
+    if (isVisible)
+      upperEnd = it+1;
+    else
+      break;
+    ++it;
+  }
+}
+
+/*! \internal
+
+  Returns the polygon of a single bar with \a key and \a value. The Polygon is open at the bottom
+  and shifted according to the bar stacking (see \ref moveAbove) and base value (see \ref
+  setBaseValue).
+*/
+QPolygonF QCPBars::getBarPolygon(double key, double value) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
+
+  QPolygonF result;
+  double lowerPixelWidth, upperPixelWidth;
+  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
+  double base = getStackedBaseValue(key, value >= 0);
+  double basePixel = valueAxis->coordToPixel(base);
+  double valuePixel = valueAxis->coordToPixel(base+value);
+  double keyPixel = keyAxis->coordToPixel(key);
+  if (mBarsGroup)
+    keyPixel += mBarsGroup->keyPixelOffset(this, key);
+  if (keyAxis->orientation() == Qt::Horizontal)
+  {
+    result << QPointF(keyPixel+lowerPixelWidth, basePixel);
+    result << QPointF(keyPixel+lowerPixelWidth, valuePixel);
+    result << QPointF(keyPixel+upperPixelWidth, valuePixel);
+    result << QPointF(keyPixel+upperPixelWidth, basePixel);
+  } else
+  {
+    result << QPointF(basePixel, keyPixel+lowerPixelWidth);
+    result << QPointF(valuePixel, keyPixel+lowerPixelWidth);
+    result << QPointF(valuePixel, keyPixel+upperPixelWidth);
+    result << QPointF(basePixel, keyPixel+upperPixelWidth);
+  }
+  return result;
+}
+
+/*! \internal
+
+  This function is used to determine the width of the bar at coordinate \a key, according to the
+  specified width (\ref setWidth) and width type (\ref setWidthType).
+
+  The output parameters \a lower and \a upper return the number of pixels the bar extends to lower
+  and higher keys, relative to the \a key coordinate (so with a non-reversed horizontal axis, \a
+  lower is negative and \a upper positive).
+*/
+void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
+{
+  switch (mWidthType)
+  {
+    case wtAbsolute:
+    {
+      upper = mWidth*0.5;
+      lower = -upper;
+      if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
+        qSwap(lower, upper);
+      break;
+    }
+    case wtAxisRectRatio:
+    {
+      if (mKeyAxis && mKeyAxis.data()->axisRect())
+      {
+        if (mKeyAxis.data()->orientation() == Qt::Horizontal)
+          upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5;
+        else
+          upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5;
+        lower = -upper;
+        if (mKeyAxis && (mKeyAxis.data()->rangeReversed() ^ (mKeyAxis.data()->orientation() == Qt::Vertical)))
+          qSwap(lower, upper);
+      } else
+        qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
+      break;
+    }
+    case wtPlotCoords:
+    {
+      if (mKeyAxis)
+      {
+        double keyPixel = mKeyAxis.data()->coordToPixel(key);
+        upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
+        lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
+        // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
+        // coordinate transform which includes range direction
+      } else
+        qDebug() << Q_FUNC_INFO << "No key axis defined";
+      break;
+    }
+  }
+}
+
+/*! \internal
+
+  This function is called to find at which value to start drawing the base of a bar at \a key, when
+  it is stacked on top of another QCPBars (e.g. with \ref moveAbove).
+
+  positive and negative bars are separated per stack (positive are stacked above baseValue upwards,
+  negative are stacked below baseValue downwards). This can be indicated with \a positive. So if the
+  bar for which we need the base value is negative, set \a positive to false.
+*/
+double QCPBars::getStackedBaseValue(double key, bool positive) const
+{
+  if (mBarBelow)
+  {
+    double max = 0; // don't use mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
+    // find bars of mBarBelow that are approximately at key and find largest one:
+    double epsilon = qAbs(key)*1e-6; // should be safe even when changed to use float at some point
+    if (key == 0)
+      epsilon = 1e-6;
+    QCPBarDataMap::const_iterator it = mBarBelow.data()->mData->lowerBound(key-epsilon);
+    QCPBarDataMap::const_iterator itEnd = mBarBelow.data()->mData->upperBound(key+epsilon);
+    while (it != itEnd)
+    {
+      if ((positive && it.value().value > max) ||
+          (!positive && it.value().value < max))
+        max = it.value().value;
+      ++it;
+    }
+    // recurse down the bar-stack to find the total height:
+    return max + mBarBelow.data()->getStackedBaseValue(key, positive);
+  } else
+    return mBaseValue;
+}
+
+/*! \internal
+
+  Connects \a below and \a above to each other via their mBarAbove/mBarBelow properties. The bar(s)
+  currently above lower and below upper will become disconnected to lower/upper.
+
+  If lower is zero, upper will be disconnected at the bottom.
+  If upper is zero, lower will be disconnected at the top.
+*/
+void QCPBars::connectBars(QCPBars *lower, QCPBars *upper)
+{
+  if (!lower && !upper) return;
+
+  if (!lower) // disconnect upper at bottom
+  {
+    // disconnect old bar below upper:
+    if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
+      upper->mBarBelow.data()->mBarAbove = 0;
+    upper->mBarBelow = 0;
+  } else if (!upper) // disconnect lower at top
+  {
+    // disconnect old bar above lower:
+    if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
+      lower->mBarAbove.data()->mBarBelow = 0;
+    lower->mBarAbove = 0;
+  } else // connect lower and upper
+  {
+    // disconnect old bar above lower:
+    if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
+      lower->mBarAbove.data()->mBarBelow = 0;
+    // disconnect old bar below upper:
+    if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
+      upper->mBarBelow.data()->mBarAbove = 0;
+    lower->mBarAbove = upper;
+    upper->mBarBelow = lower;
+  }
+}
+
+/* inherits documentation from base class */
+QCPRange QCPBars::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+
+  double current;
+  QCPBarDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    current = it.value().key;
+    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
+    {
+      if (current < range.lower || !haveLower)
+      {
+        range.lower = current;
+        haveLower = true;
+      }
+      if (current > range.upper || !haveUpper)
+      {
+        range.upper = current;
+        haveUpper = true;
+      }
+    }
+    ++it;
+  }
+  // determine exact range of bars by including bar width and barsgroup offset:
+  if (haveLower && mKeyAxis)
+  {
+    double lowerPixelWidth, upperPixelWidth, keyPixel;
+    getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
+    keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
+    if (mBarsGroup)
+      keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
+    range.lower = mKeyAxis.data()->pixelToCoord(keyPixel);
+  }
+  if (haveUpper && mKeyAxis)
+  {
+    double lowerPixelWidth, upperPixelWidth, keyPixel;
+    getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
+    keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
+    if (mBarsGroup)
+      keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
+    range.upper = mKeyAxis.data()->pixelToCoord(keyPixel);
+  }
+  foundRange = haveLower && haveUpper;
+  return range;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPBars::getValueRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  QCPRange range;
+  range.lower = mBaseValue;
+  range.upper = mBaseValue;
+  bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
+  bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
+  double current;
+
+  QCPBarDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    current = it.value().value + getStackedBaseValue(it.value().key, it.value().value >= 0);
+    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
+    {
+      if (current < range.lower || !haveLower)
+      {
+        range.lower = current;
+        haveLower = true;
+      }
+      if (current > range.upper || !haveUpper)
+      {
+        range.upper = current;
+        haveUpper = true;
+      }
+    }
+    ++it;
+  }
+
+  foundRange = true; // return true because bar charts always have the 0-line visible
+  return range;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPStatisticalBox
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPStatisticalBox
+  \brief A plottable representing a single statistical box in a plot.
+
+  \image html QCPStatisticalBox.png
+
+  To plot data, assign it with the individual parameter functions or use \ref setData to set all
+  parameters at once. The individual functions are:
+  \li \ref setMinimum
+  \li \ref setLowerQuartile
+  \li \ref setMedian
+  \li \ref setUpperQuartile
+  \li \ref setMaximum
+
+  Additionally you can define a list of outliers, drawn as scatter datapoints:
+  \li \ref setOutliers
+
+  \section appearance Changing the appearance
+
+  The appearance of the box itself is controlled via \ref setPen and \ref setBrush. You may change
+  the width of the box with \ref setWidth in plot coordinates (not pixels).
+
+  Analog functions exist for the minimum/maximum-whiskers: \ref setWhiskerPen, \ref
+  setWhiskerBarPen, \ref setWhiskerWidth. The whisker width is the width of the bar at the top
+  (maximum) and bottom (minimum).
+
+  The median indicator line has its own pen, \ref setMedianPen.
+
+  If the whisker backbone pen is changed, make sure to set the capStyle to Qt::FlatCap. Else, the
+  backbone line might exceed the whisker bars by a few pixels due to the pen cap being not
+  perfectly flat.
+
+  The Outlier data points are drawn as normal scatter points. Their look can be controlled with
+  \ref setOutlierStyle
+
+  \section usage Usage
+
+  Like all data representing objects in QCustomPlot, the QCPStatisticalBox is a plottable.
+  Usually, you first create an instance and add it to the customPlot:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-1
+  and then modify the properties of the newly created plottable, e.g.:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpstatisticalbox-creation-2
+*/
+
+/*!
+  Constructs a statistical box which uses \a keyAxis as its key axis ("x") and \a valueAxis as its
+  value axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and
+  not have the same orientation. If either of these restrictions is violated, a corresponding
+  message is printed to the debug output (qDebug), the construction is not aborted, though.
+
+  The constructed statistical box can be added to the plot with QCustomPlot::addPlottable,
+  QCustomPlot then takes ownership of the statistical box.
+*/
+QCPStatisticalBox::QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis),
+  mKey(0),
+  mMinimum(0),
+  mLowerQuartile(0),
+  mMedian(0),
+  mUpperQuartile(0),
+  mMaximum(0)
+{
+  setOutlierStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::blue, 6));
+  setWhiskerWidth(0.2);
+  setWidth(0.5);
+
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue, 2.5));
+  setMedianPen(QPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap));
+  setWhiskerPen(QPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap));
+  setWhiskerBarPen(QPen(Qt::black));
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+}
+
+/*!
+  Sets the key coordinate of the statistical box.
+*/
+void QCPStatisticalBox::setKey(double key)
+{
+  mKey = key;
+}
+
+/*!
+  Sets the parameter "minimum" of the statistical box plot. This is the position of the lower
+  whisker, typically the minimum measurement of the sample that's not considered an outlier.
+
+  \see setMaximum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
+*/
+void QCPStatisticalBox::setMinimum(double value)
+{
+  mMinimum = value;
+}
+
+/*!
+  Sets the parameter "lower Quartile" of the statistical box plot. This is the lower end of the
+  box. The lower and the upper quartiles are the two statistical quartiles around the median of the
+  sample, they contain 50% of the sample data.
+
+  \see setUpperQuartile, setPen, setBrush, setWidth
+*/
+void QCPStatisticalBox::setLowerQuartile(double value)
+{
+  mLowerQuartile = value;
+}
+
+/*!
+  Sets the parameter "median" of the statistical box plot. This is the value of the median mark
+  inside the quartile box. The median separates the sample data in half (50% of the sample data is
+  below/above the median).
+
+  \see setMedianPen
+*/
+void QCPStatisticalBox::setMedian(double value)
+{
+  mMedian = value;
+}
+
+/*!
+  Sets the parameter "upper Quartile" of the statistical box plot. This is the upper end of the
+  box. The lower and the upper quartiles are the two statistical quartiles around the median of the
+  sample, they contain 50% of the sample data.
+
+  \see setLowerQuartile, setPen, setBrush, setWidth
+*/
+void QCPStatisticalBox::setUpperQuartile(double value)
+{
+  mUpperQuartile = value;
+}
+
+/*!
+  Sets the parameter "maximum" of the statistical box plot. This is the position of the upper
+  whisker, typically the maximum measurement of the sample that's not considered an outlier.
+
+  \see setMinimum, setWhiskerPen, setWhiskerBarPen, setWhiskerWidth
+*/
+void QCPStatisticalBox::setMaximum(double value)
+{
+  mMaximum = value;
+}
+
+/*!
+  Sets a vector of outlier values that will be drawn as scatters. Any data points in the sample
+  that are not within the whiskers (\ref setMinimum, \ref setMaximum) should be considered outliers
+  and displayed as such.
+
+  \see setOutlierStyle
+*/
+void QCPStatisticalBox::setOutliers(const QVector<double> &values)
+{
+  mOutliers = values;
+}
+
+/*!
+  Sets all parameters of the statistical box plot at once.
+
+  \see setKey, setMinimum, setLowerQuartile, setMedian, setUpperQuartile, setMaximum
+*/
+void QCPStatisticalBox::setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum)
+{
+  setKey(key);
+  setMinimum(minimum);
+  setLowerQuartile(lowerQuartile);
+  setMedian(median);
+  setUpperQuartile(upperQuartile);
+  setMaximum(maximum);
+}
+
+/*!
+  Sets the width of the box in key coordinates.
+
+  \see setWhiskerWidth
+*/
+void QCPStatisticalBox::setWidth(double width)
+{
+  mWidth = width;
+}
+
+/*!
+  Sets the width of the whiskers (\ref setMinimum, \ref setMaximum) in key coordinates.
+
+  \see setWidth
+*/
+void QCPStatisticalBox::setWhiskerWidth(double width)
+{
+  mWhiskerWidth = width;
+}
+
+/*!
+  Sets the pen used for drawing the whisker backbone (That's the line parallel to the value axis).
+
+  Make sure to set the \a pen capStyle to Qt::FlatCap to prevent the whisker backbone from reaching
+  a few pixels past the whisker bars, when using a non-zero pen width.
+
+  \see setWhiskerBarPen
+*/
+void QCPStatisticalBox::setWhiskerPen(const QPen &pen)
+{
+  mWhiskerPen = pen;
+}
+
+/*!
+  Sets the pen used for drawing the whisker bars (Those are the lines parallel to the key axis at
+  each end of the whisker backbone).
+
+  \see setWhiskerPen
+*/
+void QCPStatisticalBox::setWhiskerBarPen(const QPen &pen)
+{
+  mWhiskerBarPen = pen;
+}
+
+/*!
+  Sets the pen used for drawing the median indicator line inside the statistical box.
+*/
+void QCPStatisticalBox::setMedianPen(const QPen &pen)
+{
+  mMedianPen = pen;
+}
+
+/*!
+  Sets the appearance of the outlier data points.
+
+  \see setOutliers
+*/
+void QCPStatisticalBox::setOutlierStyle(const QCPScatterStyle &style)
+{
+  mOutlierStyle = style;
+}
+
+/* inherits documentation from base class */
+void QCPStatisticalBox::clearData()
+{
+  setOutliers(QVector<double>());
+  setKey(0);
+  setMinimum(0);
+  setLowerQuartile(0);
+  setMedian(0);
+  setUpperQuartile(0);
+  setMaximum(0);
+}
+
+/* inherits documentation from base class */
+double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
+
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  {
+    double posKey, posValue;
+    pixelsToCoords(pos, posKey, posValue);
+    // quartile box:
+    QCPRange keyRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
+    QCPRange valueRange(mLowerQuartile, mUpperQuartile);
+    if (keyRange.contains(posKey) && valueRange.contains(posValue))
+      return mParentPlot->selectionTolerance()*0.99;
+
+    // min/max whiskers:
+    if (QCPRange(mMinimum, mMaximum).contains(posValue))
+      return qAbs(mKeyAxis.data()->coordToPixel(mKey)-mKeyAxis.data()->coordToPixel(posKey));
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+void QCPStatisticalBox::draw(QCPPainter *painter)
+{
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+
+  // check data validity if flag set:
+#ifdef QCUSTOMPLOT_CHECK_DATA
+  if (QCP::isInvalidData(mKey, mMedian) ||
+      QCP::isInvalidData(mLowerQuartile, mUpperQuartile) ||
+      QCP::isInvalidData(mMinimum, mMaximum))
+    qDebug() << Q_FUNC_INFO << "Data point at" << mKey << "of drawn range has invalid data." << "Plottable name:" << name();
+  for (int i=0; i<mOutliers.size(); ++i)
+    if (QCP::isInvalidData(mOutliers.at(i)))
+      qDebug() << Q_FUNC_INFO << "Data point outlier at" << mKey << "of drawn range invalid." << "Plottable name:" << name();
+#endif
+
+  QRectF quartileBox;
+  drawQuartileBox(painter, &quartileBox);
+
+  painter->save();
+  painter->setClipRect(quartileBox, Qt::IntersectClip);
+  drawMedian(painter);
+  painter->restore();
+
+  drawWhiskers(painter);
+  drawOutliers(painter);
+}
+
+/* inherits documentation from base class */
+void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
+{
+  // draw filled rect:
+  applyDefaultAntialiasingHint(painter);
+  painter->setPen(mPen);
+  painter->setBrush(mBrush);
+  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
+  r.moveCenter(rect.center());
+  painter->drawRect(r);
+}
+
+/*! \internal
+
+  Draws the quartile box. \a box is an output parameter that returns the quartile box (in pixel
+  coordinates) which is used to set the clip rect of the painter before calling \ref drawMedian (so
+  the median doesn't draw outside the quartile box).
+*/
+void QCPStatisticalBox::drawQuartileBox(QCPPainter *painter, QRectF *quartileBox) const
+{
+  QRectF box;
+  box.setTopLeft(coordsToPixels(mKey-mWidth*0.5, mUpperQuartile));
+  box.setBottomRight(coordsToPixels(mKey+mWidth*0.5, mLowerQuartile));
+  applyDefaultAntialiasingHint(painter);
+  painter->setPen(mainPen());
+  painter->setBrush(mainBrush());
+  painter->drawRect(box);
+  if (quartileBox)
+    *quartileBox = box;
+}
+
+/*! \internal
+
+  Draws the median line inside the quartile box.
+*/
+void QCPStatisticalBox::drawMedian(QCPPainter *painter) const
+{
+  QLineF medianLine;
+  medianLine.setP1(coordsToPixels(mKey-mWidth*0.5, mMedian));
+  medianLine.setP2(coordsToPixels(mKey+mWidth*0.5, mMedian));
+  applyDefaultAntialiasingHint(painter);
+  painter->setPen(mMedianPen);
+  painter->drawLine(medianLine);
+}
+
+/*! \internal
+
+  Draws both whisker backbones and bars.
+*/
+void QCPStatisticalBox::drawWhiskers(QCPPainter *painter) const
+{
+  QLineF backboneMin, backboneMax, barMin, barMax;
+  backboneMax.setPoints(coordsToPixels(mKey, mUpperQuartile), coordsToPixels(mKey, mMaximum));
+  backboneMin.setPoints(coordsToPixels(mKey, mLowerQuartile), coordsToPixels(mKey, mMinimum));
+  barMax.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMaximum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMaximum));
+  barMin.setPoints(coordsToPixels(mKey-mWhiskerWidth*0.5, mMinimum), coordsToPixels(mKey+mWhiskerWidth*0.5, mMinimum));
+  applyErrorBarsAntialiasingHint(painter);
+  painter->setPen(mWhiskerPen);
+  painter->drawLine(backboneMin);
+  painter->drawLine(backboneMax);
+  painter->setPen(mWhiskerBarPen);
+  painter->drawLine(barMin);
+  painter->drawLine(barMax);
+}
+
+/*! \internal
+
+  Draws the outlier scatter points.
+*/
+void QCPStatisticalBox::drawOutliers(QCPPainter *painter) const
+{
+  applyScattersAntialiasingHint(painter);
+  mOutlierStyle.applyTo(painter, mPen);
+  for (int i=0; i<mOutliers.size(); ++i)
+    mOutlierStyle.drawShape(painter, coordsToPixels(mKey, mOutliers.at(i)));
+}
+
+/* inherits documentation from base class */
+QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  foundRange = true;
+  if (inSignDomain == sdBoth)
+  {
+    return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
+  } else if (inSignDomain == sdNegative)
+  {
+    if (mKey+mWidth*0.5 < 0)
+      return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
+    else if (mKey < 0)
+      return QCPRange(mKey-mWidth*0.5, mKey);
+    else
+    {
+      foundRange = false;
+      return QCPRange();
+    }
+  } else if (inSignDomain == sdPositive)
+  {
+    if (mKey-mWidth*0.5 > 0)
+      return QCPRange(mKey-mWidth*0.5, mKey+mWidth*0.5);
+    else if (mKey > 0)
+      return QCPRange(mKey, mKey+mWidth*0.5);
+    else
+    {
+      foundRange = false;
+      return QCPRange();
+    }
+  }
+  foundRange = false;
+  return QCPRange();
+}
+
+/* inherits documentation from base class */
+QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  QVector<double> values; // values that must be considered (i.e. all outliers and the five box-parameters)
+  values.reserve(mOutliers.size() + 5);
+  values << mMaximum << mUpperQuartile << mMedian << mLowerQuartile << mMinimum;
+  values << mOutliers;
+  // go through values and find the ones in legal range:
+  bool haveUpper = false;
+  bool haveLower = false;
+  double upper = 0;
+  double lower = 0;
+  for (int i=0; i<values.size(); ++i)
+  {
+    if ((inSignDomain == sdNegative && values.at(i) < 0) ||
+        (inSignDomain == sdPositive && values.at(i) > 0) ||
+        (inSignDomain == sdBoth))
+    {
+      if (values.at(i) > upper || !haveUpper)
+      {
+        upper = values.at(i);
+        haveUpper = true;
+      }
+      if (values.at(i) < lower || !haveLower)
+      {
+        lower = values.at(i);
+        haveLower = true;
+      }
+    }
+  }
+  // return the bounds if we found some sensible values:
+  if (haveLower && haveUpper)
+  {
+    foundRange = true;
+    return QCPRange(lower, upper);
+  } else // might happen if all values are in other sign domain
+  {
+    foundRange = false;
+    return QCPRange();
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPColorMapData
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPColorMapData
+  \brief Holds the two-dimensional data of a QCPColorMap plottable.
+
+  This class is a data storage for \ref QCPColorMap. It holds a two-dimensional array, which \ref
+  QCPColorMap then displays as a 2D image in the plot, where the array values are represented by a
+  color, depending on the value.
+
+  The size of the array can be controlled via \ref setSize (or \ref setKeySize, \ref setValueSize).
+  Which plot coordinates these cells correspond to can be configured with \ref setRange (or \ref
+  setKeyRange, \ref setValueRange).
+
+  The data cells can be accessed in two ways: They can be directly addressed by an integer index
+  with \ref setCell. This is the fastest method. Alternatively, they can be addressed by their plot
+  coordinate with \ref setData. plot coordinate to cell index transformations and vice versa are
+  provided by the functions \ref coordToCell and \ref cellToCoord.
+
+  This class also buffers the minimum and maximum values that are in the data set, to provide
+  QCPColorMap::rescaleDataRange with the necessary information quickly. Setting a cell to a value
+  that is greater than the current maximum increases this maximum to the new value. However,
+  setting the cell that currently holds the maximum value to a smaller value doesn't decrease the
+  maximum again, because finding the true new maximum would require going through the entire data
+  array, which might be time consuming. The same holds for the data minimum. This functionality is
+  given by \ref recalculateDataBounds, such that you can decide when it is sensible to find the
+  true current minimum and maximum. The method QCPColorMap::rescaleDataRange offers a convenience
+  parameter \a recalculateDataBounds which may be set to true to automatically call \ref
+  recalculateDataBounds internally.
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn bool QCPColorMapData::isEmpty() const
+
+  Returns whether this instance carries no data. This is equivalent to having a size where at least
+  one of the dimensions is 0 (see \ref setSize).
+*/
+
+/* end of documentation of inline functions */
+
+/*!
+  Constructs a new QCPColorMapData instance. The instance has \a keySize cells in the key direction
+  and \a valueSize cells in the value direction. These cells will be displayed by the \ref QCPColorMap
+  at the coordinates \a keyRange and \a valueRange.
+
+  \see setSize, setKeySize, setValueSize, setRange, setKeyRange, setValueRange
+*/
+QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
+  mKeySize(0),
+  mValueSize(0),
+  mKeyRange(keyRange),
+  mValueRange(valueRange),
+  mIsEmpty(true),
+  mData(0),
+  mDataModified(true)
+{
+  setSize(keySize, valueSize);
+  fill(0);
+}
+
+QCPColorMapData::~QCPColorMapData()
+{
+  if (mData)
+    delete[] mData;
+}
+
+/*!
+  Constructs a new QCPColorMapData instance copying the data and range of \a other.
+*/
+QCPColorMapData::QCPColorMapData(const QCPColorMapData &other) :
+  mKeySize(0),
+  mValueSize(0),
+  mIsEmpty(true),
+  mData(0),
+  mDataModified(true)
+{
+  *this = other;
+}
+
+/*!
+  Overwrites this color map data instance with the data stored in \a other.
+*/
+QCPColorMapData &QCPColorMapData::operator=(const QCPColorMapData &other)
+{
+  if (&other != this)
+  {
+    const int keySize = other.keySize();
+    const int valueSize = other.valueSize();
+    setSize(keySize, valueSize);
+    setRange(other.keyRange(), other.valueRange());
+    if (!mIsEmpty)
+      memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
+    mDataBounds = other.mDataBounds;
+    mDataModified = true;
+  }
+  return *this;
+}
+
+/* undocumented getter */
+double QCPColorMapData::data(double key, double value)
+{
+  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
+  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
+  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
+    return mData[valueCell*mKeySize + keyCell];
+  else
+    return 0;
+}
+
+/* undocumented getter */
+double QCPColorMapData::cell(int keyIndex, int valueIndex)
+{
+  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
+    return mData[valueIndex*mKeySize + keyIndex];
+  else
+    return 0;
+}
+
+/*!
+  Resizes the data array to have \a keySize cells in the key dimension and \a valueSize cells in
+  the value dimension.
+
+  The current data is discarded and the map cells are set to 0, unless the map had already the
+  requested size.
+
+  Setting at least one of \a keySize or \a valueSize to zero frees the internal data array and \ref
+  isEmpty returns true.
+
+  \see setRange, setKeySize, setValueSize
+*/
+void QCPColorMapData::setSize(int keySize, int valueSize)
+{
+  if (keySize != mKeySize || valueSize != mValueSize)
+  {
+    mKeySize = keySize;
+    mValueSize = valueSize;
+    if (mData)
+      delete[] mData;
+    mIsEmpty = mKeySize == 0 || mValueSize == 0;
+    if (!mIsEmpty)
+    {
+#ifdef __EXCEPTIONS
+      try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
+#endif
+      mData = new double[mKeySize*mValueSize];
+#ifdef __EXCEPTIONS
+      } catch (...) { mData = 0; }
+#endif
+      if (mData)
+        fill(0);
+      else
+        qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
+    } else
+      mData = 0;
+    mDataModified = true;
+  }
+}
+
+/*!
+  Resizes the data array to have \a keySize cells in the key dimension.
+
+  The current data is discarded and the map cells are set to 0, unless the map had already the
+  requested size.
+
+  Setting \a keySize to zero frees the internal data array and \ref isEmpty returns true.
+
+  \see setKeyRange, setSize, setValueSize
+*/
+void QCPColorMapData::setKeySize(int keySize)
+{
+  setSize(keySize, mValueSize);
+}
+
+/*!
+  Resizes the data array to have \a valueSize cells in the value dimension.
+
+  The current data is discarded and the map cells are set to 0, unless the map had already the
+  requested size.
+
+  Setting \a valueSize to zero frees the internal data array and \ref isEmpty returns true.
+
+  \see setValueRange, setSize, setKeySize
+*/
+void QCPColorMapData::setValueSize(int valueSize)
+{
+  setSize(mKeySize, valueSize);
+}
+
+/*!
+  Sets the coordinate ranges the data shall be distributed over. This defines the rectangular area
+  covered by the color map in plot coordinates.
+
+  The outer cells will be centered on the range boundaries given to this function. For example, if
+  the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
+  be cells centered on the key coordinates 2, 2.5 and 3.
+
+  \see setSize
+*/
+void QCPColorMapData::setRange(const QCPRange &keyRange, const QCPRange &valueRange)
+{
+  setKeyRange(keyRange);
+  setValueRange(valueRange);
+}
+
+/*!
+  Sets the coordinate range the data shall be distributed over in the key dimension. Together with
+  the value range, This defines the rectangular area covered by the color map in plot coordinates.
+
+  The outer cells will be centered on the range boundaries given to this function. For example, if
+  the key size (\ref setKeySize) is 3 and \a keyRange is set to <tt>QCPRange(2, 3)</tt> there will
+  be cells centered on the key coordinates 2, 2.5 and 3.
+
+  \see setRange, setValueRange, setSize
+*/
+void QCPColorMapData::setKeyRange(const QCPRange &keyRange)
+{
+  mKeyRange = keyRange;
+}
+
+/*!
+  Sets the coordinate range the data shall be distributed over in the value dimension. Together with
+  the key range, This defines the rectangular area covered by the color map in plot coordinates.
+
+  The outer cells will be centered on the range boundaries given to this function. For example, if
+  the value size (\ref setValueSize) is 3 and \a valueRange is set to <tt>QCPRange(2, 3)</tt> there
+  will be cells centered on the value coordinates 2, 2.5 and 3.
+
+  \see setRange, setKeyRange, setSize
+*/
+void QCPColorMapData::setValueRange(const QCPRange &valueRange)
+{
+  mValueRange = valueRange;
+}
+
+/*!
+  Sets the data of the cell, which lies at the plot coordinates given by \a key and \a value, to \a
+  z.
+
+  \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
+  value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
+  you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
+  determine the cell index. Rather directly access the cell index with \ref
+  QCPColorMapData::setCell.
+
+  \see setCell, setRange
+*/
+void QCPColorMapData::setData(double key, double value, double z)
+{
+  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
+  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
+  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
+  {
+    mData[valueCell*mKeySize + keyCell] = z;
+    if (z < mDataBounds.lower)
+      mDataBounds.lower = z;
+    if (z > mDataBounds.upper)
+      mDataBounds.upper = z;
+     mDataModified = true;
+  }
+}
+
+/*!
+  Sets the data of the cell with indices \a keyIndex and \a valueIndex to \a z. The indices
+  enumerate the cells starting from zero, up to the map's size-1 in the respective dimension (see
+  \ref setSize).
+
+  In the standard plot configuration (horizontal key axis and vertical value axis, both not
+  range-reversed), the cell with indices (0, 0) is in the bottom left corner and the cell with
+  indices (keySize-1, valueSize-1) is in the top right corner of the color map.
+
+  \see setData, setSize
+*/
+void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
+{
+  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
+  {
+    mData[valueIndex*mKeySize + keyIndex] = z;
+    if (z < mDataBounds.lower)
+      mDataBounds.lower = z;
+    if (z > mDataBounds.upper)
+      mDataBounds.upper = z;
+     mDataModified = true;
+  }
+}
+
+/*!
+  Goes through the data and updates the buffered minimum and maximum data values.
+
+  Calling this method is only advised if you are about to call \ref QCPColorMap::rescaleDataRange
+  and can not guarantee that the cells holding the maximum or minimum data haven't been overwritten
+  with a smaller or larger value respectively, since the buffered maximum/minimum values have been
+  updated the last time. Why this is the case is explained in the class description (\ref
+  QCPColorMapData).
+
+  Note that the method \ref QCPColorMap::rescaleDataRange provides a parameter \a
+  recalculateDataBounds for convenience. Setting this to true will call this method for you, before
+  doing the rescale.
+*/
+void QCPColorMapData::recalculateDataBounds()
+{
+  if (mKeySize > 0 && mValueSize > 0)
+  {
+    double minHeight = mData[0];
+    double maxHeight = mData[0];
+    const int dataCount = mValueSize*mKeySize;
+    for (int i=0; i<dataCount; ++i)
+    {
+      if (mData[i] > maxHeight)
+        maxHeight = mData[i];
+      if (mData[i] < minHeight)
+        minHeight = mData[i];
+    }
+    mDataBounds.lower = minHeight;
+    mDataBounds.upper = maxHeight;
+  }
+}
+
+/*!
+  Frees the internal data memory.
+
+  This is equivalent to calling \ref setSize "setSize(0, 0)".
+*/
+void QCPColorMapData::clear()
+{
+  setSize(0, 0);
+}
+
+/*!
+  Sets all cells to the value \a z.
+*/
+void QCPColorMapData::fill(double z)
+{
+  const int dataCount = mValueSize*mKeySize;
+  for (int i=0; i<dataCount; ++i)
+    mData[i] = z;
+  mDataBounds = QCPRange(z, z);
+  mDataModified = true;
+}
+
+/*!
+  Transforms plot coordinates given by \a key and \a value to cell indices of this QCPColorMapData
+  instance. The resulting cell indices are returned via the output parameters \a keyIndex and \a
+  valueIndex.
+
+  The retrieved key/value cell indices can then be used for example with \ref setCell.
+
+  If you are only interested in a key or value index, you may pass 0 as \a valueIndex or \a
+  keyIndex.
+
+  \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
+  value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
+  you shouldn't use the \ref QCPColorMapData::coordToCell method as it uses a linear transformation to
+  determine the cell index.
+
+  \see cellToCoord, QCPAxis::coordToPixel
+*/
+void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
+{
+  if (keyIndex)
+    *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
+  if (valueIndex)
+    *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
+}
+
+/*!
+  Transforms cell indices given by \a keyIndex and \a valueIndex to cell indices of this QCPColorMapData
+  instance. The resulting coordinates are returned via the output parameters \a key and \a
+  value.
+
+  If you are only interested in a key or value coordinate, you may pass 0 as \a key or \a
+  value.
+
+  \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
+  value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
+  you shouldn't use the \ref QCPColorMapData::cellToCoord method as it uses a linear transformation to
+  determine the cell index.
+
+  \see coordToCell, QCPAxis::pixelToCoord
+*/
+void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
+{
+  if (key)
+    *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
+  if (value)
+    *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPColorMap
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPColorMap
+  \brief A plottable representing a two-dimensional color map in a plot.
+
+  \image html QCPColorMap.png
+
+  The data is stored in the class \ref QCPColorMapData, which can be accessed via the data()
+  method.
+
+  A color map has three dimensions to represent a data point: The \a key dimension, the \a value
+  dimension and the \a data dimension. As with other plottables such as graphs, \a key and \a value
+  correspond to two orthogonal axes on the QCustomPlot surface that you specify in the QColorMap
+  constructor. The \a data dimension however is encoded as the color of the point at (\a key, \a
+  value).
+
+  Set the number of points (or \a cells) in the key/value dimension via \ref
+  QCPColorMapData::setSize. The plot coordinate range over which these points will be displayed is
+  specified via \ref QCPColorMapData::setRange. The first cell will be centered on the lower range
+  boundary and the last cell will be centered on the upper range boundary. The data can be set by
+  either accessing the cells directly with QCPColorMapData::setCell or by addressing the cells via
+  their plot coordinates with \ref QCPColorMapData::setData. If possible, you should prefer
+  setCell, since it doesn't need to do any coordinate transformation and thus performs a bit
+  better.
+
+  The cell with index (0, 0) is at the bottom left, if the color map uses normal (i.e. not reversed)
+  key and value axes.
+
+  To show the user which colors correspond to which \a data values, a \ref QCPColorScale is
+  typically placed to the right of the axis rect. See the documentation there for details on how to
+  add and use a color scale.
+
+  \section appearance Changing the appearance
+
+  The central part of the appearance is the color gradient, which can be specified via \ref
+  setGradient. See the documentation of \ref QCPColorGradient for details on configuring a color
+  gradient.
+
+  The \a data range that is mapped to the colors of the gradient can be specified with \ref
+  setDataRange. To make the data range encompass the whole data set minimum to maximum, call \ref
+  rescaleDataRange.
+
+  \section usage Usage
+
+  Like all data representing objects in QCustomPlot, the QCPColorMap is a plottable
+  (QCPAbstractPlottable). So the plottable-interface of QCustomPlot applies
+  (QCustomPlot::plottable, QCustomPlot::addPlottable, QCustomPlot::removePlottable, etc.)
+
+  Usually, you first create an instance and add it to the customPlot:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-1
+  and then modify the properties of the newly created color map, e.g.:
+  \snippet documentation/doc-code-snippets/mainwindow.cpp qcpcolormap-creation-2
+
+  \note The QCPColorMap always displays the data at equal key/value intervals, even if the key or
+  value axis is set to a logarithmic scaling. If you want to use QCPColorMap with logarithmic axes,
+  you shouldn't use the \ref QCPColorMapData::setData method as it uses a linear transformation to
+  determine the cell index. Rather directly access the cell index with \ref
+  QCPColorMapData::setCell.
+*/
+
+/* start documentation of inline functions */
+
+/*! \fn QCPColorMapData *QCPColorMap::data() const
+
+  Returns a pointer to the internal data storage of type \ref QCPColorMapData. Access this to
+  modify data points (cells) and the color map key/value range.
+
+  \see setData
+*/
+
+/* end documentation of inline functions */
+
+/* start documentation of signals */
+
+/*! \fn void QCPColorMap::dataRangeChanged(QCPRange newRange);
+
+  This signal is emitted when the data range changes.
+
+  \see setDataRange
+*/
+
+/*! \fn void QCPColorMap::dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
+
+  This signal is emitted when the data scale type changes.
+
+  \see setDataScaleType
+*/
+
+/*! \fn void QCPColorMap::gradientChanged(QCPColorGradient newGradient);
+
+  This signal is emitted when the gradient changes.
+
+  \see setGradient
+*/
+
+/* end documentation of signals */
+
+/*!
+  Constructs a color map with the specified \a keyAxis and \a valueAxis.
+
+  The constructed QCPColorMap can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
+  then takes ownership of the color map.
+*/
+QCPColorMap::QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis),
+  mDataScaleType(QCPAxis::stLinear),
+  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
+  mInterpolate(true),
+  mTightBoundary(false),
+  mMapImageInvalidated(true)
+{
+}
+
+QCPColorMap::~QCPColorMap()
+{
+  delete mMapData;
+}
+
+/*!
+  Replaces the current \ref data with the provided \a data.
+
+  If \a copy is set to true, the \a data object will only be copied. if false, the color map
+  takes ownership of the passed data and replaces the internal data pointer with it. This is
+  significantly faster than copying for large datasets.
+*/
+void QCPColorMap::setData(QCPColorMapData *data, bool copy)
+{
+  if (mMapData == data)
+  {
+    qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
+    return;
+  }
+  if (copy)
+  {
+    *mMapData = *data;
+  } else
+  {
+    delete mMapData;
+    mMapData = data;
+  }
+  mMapImageInvalidated = true;
+}
+
+/*!
+  Sets the data range of this color map to \a dataRange. The data range defines which data values
+  are mapped to the color gradient.
+
+  To make the data range span the full range of the data set, use \ref rescaleDataRange.
+
+  \see QCPColorScale::setDataRange
+*/
+void QCPColorMap::setDataRange(const QCPRange &dataRange)
+{
+  if (!QCPRange::validRange(dataRange)) return;
+  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
+  {
+    if (mDataScaleType == QCPAxis::stLogarithmic)
+      mDataRange = dataRange.sanitizedForLogScale();
+    else
+      mDataRange = dataRange.sanitizedForLinScale();
+    mMapImageInvalidated = true;
+    emit dataRangeChanged(mDataRange);
+  }
+}
+
+/*!
+  Sets whether the data is correlated with the color gradient linearly or logarithmically.
+
+  \see QCPColorScale::setDataScaleType
+*/
+void QCPColorMap::setDataScaleType(QCPAxis::ScaleType scaleType)
+{
+  if (mDataScaleType != scaleType)
+  {
+    mDataScaleType = scaleType;
+    mMapImageInvalidated = true;
+    emit dataScaleTypeChanged(mDataScaleType);
+    if (mDataScaleType == QCPAxis::stLogarithmic)
+      setDataRange(mDataRange.sanitizedForLogScale());
+  }
+}
+
+/*!
+  Sets the color gradient that is used to represent the data. For more details on how to create an
+  own gradient or use one of the preset gradients, see \ref QCPColorGradient.
+
+  The colors defined by the gradient will be used to represent data values in the currently set
+  data range, see \ref setDataRange. Data points that are outside this data range will either be
+  colored uniformly with the respective gradient boundary color, or the gradient will repeat,
+  depending on \ref QCPColorGradient::setPeriodic.
+
+  \see QCPColorScale::setGradient
+*/
+void QCPColorMap::setGradient(const QCPColorGradient &gradient)
+{
+  if (mGradient != gradient)
+  {
+    mGradient = gradient;
+    mMapImageInvalidated = true;
+    emit gradientChanged(mGradient);
+  }
+}
+
+/*!
+  Sets whether the color map image shall use bicubic interpolation when displaying the color map
+  shrinked or expanded, and not at a 1:1 pixel-to-data scale.
+
+  \image html QCPColorMap-interpolate.png "A 10*10 color map, with interpolation and without interpolation enabled"
+*/
+void QCPColorMap::setInterpolate(bool enabled)
+{
+  mInterpolate = enabled;
+  mMapImageInvalidated = true; // because oversampling factors might need to change
+}
+
+/*!
+  Sets whether the outer most data rows and columns are clipped to the specified key and value
+  range (see \ref QCPColorMapData::setKeyRange, \ref QCPColorMapData::setValueRange).
+
+  if \a enabled is set to false, the data points at the border of the color map are drawn with the
+  same width and height as all other data points. Since the data points are represented by
+  rectangles of one color centered on the data coordinate, this means that the shown color map
+  extends by half a data point over the specified key/value range in each direction.
+
+  \image html QCPColorMap-tightboundary.png "A color map, with tight boundary enabled and disabled"
+*/
+void QCPColorMap::setTightBoundary(bool enabled)
+{
+  mTightBoundary = enabled;
+}
+
+/*!
+  Associates the color scale \a colorScale with this color map.
+
+  This means that both the color scale and the color map synchronize their gradient, data range and
+  data scale type (\ref setGradient, \ref setDataRange, \ref setDataScaleType). Multiple color maps
+  can be associated with one single color scale. This causes the color maps to also synchronize
+  those properties, via the mutual color scale.
+
+  This function causes the color map to adopt the current color gradient, data range and data scale
+  type of \a colorScale. After this call, you may change these properties at either the color map
+  or the color scale, and the setting will be applied to both.
+
+  Pass 0 as \a colorScale to disconnect the color scale from this color map again.
+*/
+void QCPColorMap::setColorScale(QCPColorScale *colorScale)
+{
+  if (mColorScale) // unconnect signals from old color scale
+  {
+    disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
+    disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
+    disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
+    disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
+    disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
+    disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
+  }
+  mColorScale = colorScale;
+  if (mColorScale) // connect signals to new color scale
+  {
+    setGradient(mColorScale.data()->gradient());
+    setDataRange(mColorScale.data()->dataRange());
+    setDataScaleType(mColorScale.data()->dataScaleType());
+    connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
+    connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
+    connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
+    connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
+    connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
+    connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
+  }
+}
+
+/*!
+  Sets the data range (\ref setDataRange) to span the minimum and maximum values that occur in the
+  current data set. This corresponds to the \ref rescaleKeyAxis or \ref rescaleValueAxis methods,
+  only for the third data dimension of the color map.
+
+  The minimum and maximum values of the data set are buffered in the internal QCPColorMapData
+  instance (\ref data). As data is updated via its \ref QCPColorMapData::setCell or \ref
+  QCPColorMapData::setData, the buffered minimum and maximum values are updated, too. For
+  performance reasons, however, they are only updated in an expanding fashion. So the buffered
+  maximum can only increase and the buffered minimum can only decrease. In consequence, changes to
+  the data that actually lower the maximum of the data set (by overwriting the cell holding the
+  current maximum with a smaller value), aren't recognized and the buffered maximum overestimates
+  the true maximum of the data set. The same happens for the buffered minimum. To recalculate the
+  true minimum and maximum by explicitly looking at each cell, the method
+  QCPColorMapData::recalculateDataBounds can be used. For convenience, setting the parameter \a
+  recalculateDataBounds calls this method before setting the data range to the buffered minimum and
+  maximum.
+
+  \see setDataRange
+*/
+void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
+{
+  if (recalculateDataBounds)
+    mMapData->recalculateDataBounds();
+  setDataRange(mMapData->dataBounds());
+}
+
+/*!
+  Takes the current appearance of the color map and updates the legend icon, which is used to
+  represent this color map in the legend (see \ref QCPLegend).
+
+  The \a transformMode specifies whether the rescaling is done by a faster, low quality image
+  scaling algorithm (Qt::FastTransformation) or by a slower, higher quality algorithm
+  (Qt::SmoothTransformation).
+
+  The current color map appearance is scaled down to \a thumbSize. Ideally, this should be equal to
+  the size of the legend icon (see \ref QCPLegend::setIconSize). If it isn't exactly the configured
+  legend icon size, the thumb will be rescaled during drawing of the legend item.
+
+  \see setDataRange
+*/
+void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
+{
+  if (mMapImage.isNull() && !data()->isEmpty())
+    updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
+
+  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
+  {
+    bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
+    bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
+    mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
+  }
+}
+
+/*!
+  Clears the colormap data by calling \ref QCPColorMapData::clear() on the internal data. This also
+  resizes the map to 0x0 cells.
+*/
+void QCPColorMap::clearData()
+{
+  mMapData->clear();
+}
+
+/* inherits documentation from base class */
+double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
+
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  {
+    double posKey, posValue;
+    pixelsToCoords(pos, posKey, posValue);
+    if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
+      return mParentPlot->selectionTolerance()*0.99;
+  }
+  return -1;
+}
+
+/*! \internal
+
+  Updates the internal map image buffer by going through the internal \ref QCPColorMapData and
+  turning the data values into color pixels with \ref QCPColorGradient::colorize.
+
+  This method is called by \ref QCPColorMap::draw if either the data has been modified or the map image
+  has been invalidated for a different reason (e.g. a change of the data range with \ref
+  setDataRange).
+
+  If the map cell count is low, the image created will be oversampled in order to avoid a
+  QPainter::drawImage bug which makes inner pixel boundaries jitter when stretch-drawing images
+  without smooth transform enabled. Accordingly, oversampling isn't performed if \ref
+  setInterpolate is true.
+*/
+void QCPColorMap::updateMapImage()
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  if (!keyAxis) return;
+  if (mMapData->isEmpty()) return;
+
+  const int keySize = mMapData->keySize();
+  const int valueSize = mMapData->valueSize();
+  int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
+  int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
+
+  // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
+  if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
+    mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), QImage::Format_RGB32);
+  else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
+    mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), QImage::Format_RGB32);
+
+  QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage
+  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
+  {
+    // resize undersampled map image to actual key/value cell sizes:
+    if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
+      mUndersampledMapImage = QImage(QSize(keySize, valueSize), QImage::Format_RGB32);
+    else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
+      mUndersampledMapImage = QImage(QSize(valueSize, keySize), QImage::Format_RGB32);
+    localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image
+  } else if (!mUndersampledMapImage.isNull())
+    mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it
+
+  const double *rawData = mMapData->mData;
+  if (keyAxis->orientation() == Qt::Horizontal)
+  {
+    const int lineCount = valueSize;
+    const int rowCount = keySize;
+    for (int line=0; line<lineCount; ++line)
+    {
+      QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
+      mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
+    }
+  } else // keyAxis->orientation() == Qt::Vertical
+  {
+    const int lineCount = keySize;
+    const int rowCount = valueSize;
+    for (int line=0; line<lineCount; ++line)
+    {
+      QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
+      mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
+    }
+  }
+
+  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
+  {
+    if (keyAxis->orientation() == Qt::Horizontal)
+      mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
+    else
+      mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
+  }
+  mMapData->mDataModified = false;
+  mMapImageInvalidated = false;
+}
+
+/* inherits documentation from base class */
+void QCPColorMap::draw(QCPPainter *painter)
+{
+  if (mMapData->isEmpty()) return;
+  if (!mKeyAxis || !mValueAxis) return;
+  applyDefaultAntialiasingHint(painter);
+
+  if (mMapData->mDataModified || mMapImageInvalidated)
+    updateMapImage();
+
+  // use buffer if painting vectorized (PDF):
+  bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
+  QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized
+  QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in
+  QPixmap mapBuffer;
+  double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps
+  if (useBuffer)
+  {
+    mapBufferTarget = painter->clipRegion().boundingRect();
+    mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
+    mapBuffer.fill(Qt::transparent);
+    localPainter = new QCPPainter(&mapBuffer);
+    localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
+    localPainter->translate(-mapBufferTarget.topLeft());
+  }
+
+  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
+                            coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
+  // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):
+  double halfCellWidth = 0; // in pixels
+  double halfCellHeight = 0; // in pixels
+  if (keyAxis()->orientation() == Qt::Horizontal)
+  {
+    if (mMapData->keySize() > 1)
+      halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
+    if (mMapData->valueSize() > 1)
+      halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
+  } else // keyAxis orientation is Qt::Vertical
+  {
+    if (mMapData->keySize() > 1)
+      halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
+    if (mMapData->valueSize() > 1)
+      halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
+  }
+  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
+  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
+  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
+  bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
+  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
+  QRegion clipBackup;
+  if (mTightBoundary)
+  {
+    clipBackup = localPainter->clipRegion();
+    QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
+                                  coordsToPixels(mMapData->keyRange().upper, mMapData->valueRange().upper)).normalized();
+    localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
+  }
+  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
+  if (mTightBoundary)
+    localPainter->setClipRegion(clipBackup);
+  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
+
+  if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter
+  {
+    delete localPainter;
+    painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
+  }
+}
+
+/* inherits documentation from base class */
+void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
+{
+  applyDefaultAntialiasingHint(painter);
+  // draw map thumbnail:
+  if (!mLegendIcon.isNull())
+  {
+    QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
+    QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
+    iconRect.moveCenter(rect.center());
+    painter->drawPixmap(iconRect.topLeft(), scaledIcon);
+  }
+  /*
+  // draw frame:
+  painter->setBrush(Qt::NoBrush);
+  painter->setPen(Qt::black);
+  painter->drawRect(rect.adjusted(1, 1, 0, 0));
+  */
+}
+
+/* inherits documentation from base class */
+QCPRange QCPColorMap::getKeyRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  foundRange = true;
+  QCPRange result = mMapData->keyRange();
+  result.normalize();
+  if (inSignDomain == QCPAbstractPlottable::sdPositive)
+  {
+    if (result.lower <= 0 && result.upper > 0)
+      result.lower = result.upper*1e-3;
+    else if (result.lower <= 0 && result.upper <= 0)
+      foundRange = false;
+  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
+  {
+    if (result.upper >= 0 && result.lower < 0)
+      result.upper = result.lower*1e-3;
+    else if (result.upper >= 0 && result.lower >= 0)
+      foundRange = false;
+  }
+  return result;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPColorMap::getValueRange(bool &foundRange, SignDomain inSignDomain) const
+{
+  foundRange = true;
+  QCPRange result = mMapData->valueRange();
+  result.normalize();
+  if (inSignDomain == QCPAbstractPlottable::sdPositive)
+  {
+    if (result.lower <= 0 && result.upper > 0)
+      result.lower = result.upper*1e-3;
+    else if (result.lower <= 0 && result.upper <= 0)
+      foundRange = false;
+  } else if (inSignDomain == QCPAbstractPlottable::sdNegative)
+  {
+    if (result.upper >= 0 && result.lower < 0)
+      result.upper = result.lower*1e-3;
+    else if (result.upper >= 0 && result.lower >= 0)
+      foundRange = false;
+  }
+  return result;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPFinancialData
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPFinancialData
+  \brief Holds the data of one single data point for QCPFinancial.
+
+  The container for storing multiple data points is \ref QCPFinancialDataMap.
+
+  The stored data is:
+  \li \a key: coordinate on the key axis of this data point
+  \li \a open: The opening value at the data point
+  \li \a high: The high/maximum value at the data point
+  \li \a low: The low/minimum value at the data point
+  \li \a close: The closing value at the data point
+
+  \see QCPFinancialDataMap
+*/
+
+/*!
+  Constructs a data point with key and all values set to zero.
+*/
+QCPFinancialData::QCPFinancialData() :
+  key(0),
+  open(0),
+  high(0),
+  low(0),
+  close(0)
+{
+}
+
+/*!
+  Constructs a data point with the specified \a key and OHLC values.
+*/
+QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
+  key(key),
+  open(open),
+  high(high),
+  low(low),
+  close(close)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPFinancial
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPFinancial
+  \brief A plottable representing a financial stock chart
+
+  \image html QCPFinancial.png
+
+  This plottable represents time series data binned to certain intervals, mainly used for stock
+  charts. The two common representations OHLC (Open-High-Low-Close) bars and Candlesticks can be
+  set via \ref setChartStyle.
+
+  The data is passed via \ref setData as a set of open/high/low/close values at certain keys
+  (typically times). This means the data must be already binned appropriately. If data is only
+  available as a series of values (e.g. \a price against \a time), you can use the static
+  convenience function \ref timeSeriesToOhlc to generate binned OHLC-data which can then be passed
+  to \ref setData.
+
+  The width of the OHLC bars/candlesticks can be controlled with \ref setWidth and is given in plot
+  key coordinates. A typical choice is to set it to (or slightly less than) one bin interval width.
+
+  \section appearance Changing the appearance
+
+  Charts can be either single- or two-colored (\ref setTwoColored). If set to be single-colored,
+  lines are drawn with the plottable's pen (\ref setPen) and fills with the brush (\ref setBrush).
+
+  If set to two-colored, positive changes of the value during an interval (\a close >= \a open) are
+  represented with a different pen and brush than negative changes (\a close < \a open). These can
+  be configured with \ref setPenPositive, \ref setPenNegative, \ref setBrushPositive, and \ref
+  setBrushNegative. In two-colored mode, the normal plottable pen/brush is ignored. Upon selection
+  however, the normal selected pen/brush (\ref setSelectedPen, \ref setSelectedBrush) is used,
+  irrespective of whether the chart is single- or two-colored.
+
+*/
+
+/* start of documentation of inline functions */
+
+/*! \fn QCPFinancialDataMap *QCPFinancial::data() const
+
+  Returns a pointer to the internal data storage of type \ref QCPFinancialDataMap. You may use it to
+  directly manipulate the data, which may be more convenient and faster than using the regular \ref
+  setData or \ref addData methods, in certain situations.
+*/
+
+/* end of documentation of inline functions */
+
+/*!
+  Constructs a financial chart which uses \a keyAxis as its key axis ("x") and \a valueAxis as its value
+  axis ("y"). \a keyAxis and \a valueAxis must reside in the same QCustomPlot instance and not have
+  the same orientation. If either of these restrictions is violated, a corresponding message is
+  printed to the debug output (qDebug), the construction is not aborted, though.
+
+  The constructed QCPFinancial can be added to the plot with QCustomPlot::addPlottable, QCustomPlot
+  then takes ownership of the financial chart.
+*/
+QCPFinancial::QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis) :
+  QCPAbstractPlottable(keyAxis, valueAxis),
+  mData(0),
+  mChartStyle(csOhlc),
+  mWidth(0.5),
+  mTwoColored(false),
+  mBrushPositive(QBrush(QColor(210, 210, 255))),
+  mBrushNegative(QBrush(QColor(255, 210, 210))),
+  mPenPositive(QPen(QColor(10, 40, 180))),
+  mPenNegative(QPen(QColor(180, 40, 10)))
+{
+  mData = new QCPFinancialDataMap;
+
+  setSelectedPen(QPen(QColor(80, 80, 255), 2.5));
+  setSelectedBrush(QBrush(QColor(80, 80, 255)));
+}
+
+QCPFinancial::~QCPFinancial()
+{
+  delete mData;
+}
+
+/*!
+  Replaces the current data with the provided \a data.
+
+  If \a copy is set to true, data points in \a data will only be copied. if false, the plottable
+  takes ownership of the passed data and replaces the internal data pointer with it. This is
+  significantly faster than copying for large datasets.
+
+  Alternatively, you can also access and modify the plottable's data via the \ref data method, which
+  returns a pointer to the internal \ref QCPFinancialDataMap.
+
+  \see timeSeriesToOhlc
+*/
+void QCPFinancial::setData(QCPFinancialDataMap *data, bool copy)
+{
+  if (mData == data)
+  {
+    qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
+    return;
+  }
+  if (copy)
+  {
+    *mData = *data;
+  } else
+  {
+    delete mData;
+    mData = data;
+  }
+}
+
+/*! \overload
+
+  Replaces the current data with the provided open/high/low/close data. The provided vectors should
+  have equal length. Else, the number of added points will be the size of the smallest vector.
+
+  \see timeSeriesToOhlc
+*/
+void QCPFinancial::setData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
+{
+  mData->clear();
+  int n = key.size();
+  n = qMin(n, open.size());
+  n = qMin(n, high.size());
+  n = qMin(n, low.size());
+  n = qMin(n, close.size());
+  for (int i=0; i<n; ++i)
+  {
+    mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
+  }
+}
+
+/*!
+  Sets which representation style shall be used to display the OHLC data.
+*/
+void QCPFinancial::setChartStyle(QCPFinancial::ChartStyle style)
+{
+  mChartStyle = style;
+}
+
+/*!
+  Sets the width of the individual bars/candlesticks to \a width in plot key coordinates.
+
+  A typical choice is to set it to (or slightly less than) one bin interval width.
+*/
+void QCPFinancial::setWidth(double width)
+{
+  mWidth = width;
+}
+
+/*!
+  Sets whether this chart shall contrast positive from negative trends per data point by using two
+  separate colors to draw the respective bars/candlesticks.
+
+  If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
+  setBrush).
+
+  \see setPenPositive, setPenNegative, setBrushPositive, setBrushNegative
+*/
+void QCPFinancial::setTwoColored(bool twoColored)
+{
+  mTwoColored = twoColored;
+}
+
+/*!
+  If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
+  of data points with a positive trend (i.e. bars/candlesticks with close >= open).
+
+  If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
+  setBrush).
+
+  \see setBrushNegative, setPenPositive, setPenNegative
+*/
+void QCPFinancial::setBrushPositive(const QBrush &brush)
+{
+  mBrushPositive = brush;
+}
+
+/*!
+  If \ref setTwoColored is set to true, this function controls the brush that is used to draw fills
+  of data points with a negative trend (i.e. bars/candlesticks with close < open).
+
+  If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
+  setBrush).
+
+  \see setBrushPositive, setPenNegative, setPenPositive
+*/
+void QCPFinancial::setBrushNegative(const QBrush &brush)
+{
+  mBrushNegative = brush;
+}
+
+/*!
+  If \ref setTwoColored is set to true, this function controls the pen that is used to draw
+  outlines of data points with a positive trend (i.e. bars/candlesticks with close >= open).
+
+  If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
+  setBrush).
+
+  \see setPenNegative, setBrushPositive, setBrushNegative
+*/
+void QCPFinancial::setPenPositive(const QPen &pen)
+{
+  mPenPositive = pen;
+}
+
+/*!
+  If \ref setTwoColored is set to true, this function controls the pen that is used to draw
+  outlines of data points with a negative trend (i.e. bars/candlesticks with close < open).
+
+  If \a twoColored is false, the normal plottable's pen and brush are used (\ref setPen, \ref
+  setBrush).
+
+  \see setPenPositive, setBrushNegative, setBrushPositive
+*/
+void QCPFinancial::setPenNegative(const QPen &pen)
+{
+  mPenNegative = pen;
+}
+
+/*!
+  Adds the provided data points in \a dataMap to the current data.
+
+  Alternatively, you can also access and modify the data via the \ref data method, which returns a
+  pointer to the internal \ref QCPFinancialDataMap.
+
+  \see removeData
+*/
+void QCPFinancial::addData(const QCPFinancialDataMap &dataMap)
+{
+  mData->unite(dataMap);
+}
+
+/*! \overload
+
+  Adds the provided single data point in \a data to the current data.
+
+  Alternatively, you can also access and modify the data via the \ref data method, which returns a
+  pointer to the internal \ref QCPFinancialData.
+
+  \see removeData
+*/
+void QCPFinancial::addData(const QCPFinancialData &data)
+{
+  mData->insertMulti(data.key, data);
+}
+
+/*! \overload
+
+  Adds the provided single data point given by \a key, \a open, \a high, \a low, and \a close to
+  the current data.
+
+  Alternatively, you can also access and modify the data via the \ref data method, which returns a
+  pointer to the internal \ref QCPFinancialData.
+
+  \see removeData
+*/
+void QCPFinancial::addData(double key, double open, double high, double low, double close)
+{
+  mData->insertMulti(key, QCPFinancialData(key, open, high, low, close));
+}
+
+/*! \overload
+
+  Adds the provided open/high/low/close data to the current data.
+
+  Alternatively, you can also access and modify the data via the \ref data method, which returns a
+  pointer to the internal \ref QCPFinancialData.
+
+  \see removeData
+*/
+void QCPFinancial::addData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close)
+{
+  int n = key.size();
+  n = qMin(n, open.size());
+  n = qMin(n, high.size());
+  n = qMin(n, low.size());
+  n = qMin(n, close.size());
+  for (int i=0; i<n; ++i)
+  {
+    mData->insertMulti(key[i], QCPFinancialData(key[i], open[i], high[i], low[i], close[i]));
+  }
+}
+
+/*!
+  Removes all data points with keys smaller than \a key.
+
+  \see addData, clearData
+*/
+void QCPFinancial::removeDataBefore(double key)
+{
+  QCPFinancialDataMap::iterator it = mData->begin();
+  while (it != mData->end() && it.key() < key)
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with keys greater than \a key.
+
+  \see addData, clearData
+*/
+void QCPFinancial::removeDataAfter(double key)
+{
+  if (mData->isEmpty()) return;
+  QCPFinancialDataMap::iterator it = mData->upperBound(key);
+  while (it != mData->end())
+    it = mData->erase(it);
+}
+
+/*!
+  Removes all data points with keys between \a fromKey and \a toKey. if \a fromKey is greater or
+  equal to \a toKey, the function does nothing. To remove a single data point with known key, use
+  \ref removeData(double key).
+
+  \see addData, clearData
+*/
+void QCPFinancial::removeData(double fromKey, double toKey)
+{
+  if (fromKey >= toKey || mData->isEmpty()) return;
+  QCPFinancialDataMap::iterator it = mData->upperBound(fromKey);
+  QCPFinancialDataMap::iterator itEnd = mData->upperBound(toKey);
+  while (it != itEnd)
+    it = mData->erase(it);
+}
+
+/*! \overload
+
+  Removes a single data point at \a key. If the position is not known with absolute precision,
+  consider using \ref removeData(double fromKey, double toKey) with a small fuzziness interval
+  around the suspected position, depeding on the precision with which the key is known.
+
+  \see addData, clearData
+*/
+void QCPFinancial::removeData(double key)
+{
+  mData->remove(key);
+}
+
+/*!
+  Removes all data points.
+
+  \see removeData, removeDataAfter, removeDataBefore
+*/
+void QCPFinancial::clearData()
+{
+  mData->clear();
+}
+
+/* inherits documentation from base class */
+double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
+
+  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
+  {
+    // get visible data range:
+    QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
+    getVisibleDataBounds(lower, upper);
+    if (lower == mData->constEnd() || upper == mData->constEnd())
+      return -1;
+    // perform select test according to configured style:
+    switch (mChartStyle)
+    {
+      case QCPFinancial::csOhlc:
+        return ohlcSelectTest(pos, lower, upper+1); break;
+      case QCPFinancial::csCandlestick:
+        return candlestickSelectTest(pos, lower, upper+1); break;
+    }
+  }
+  return -1;
+}
+
+/*!
+  A convenience function that converts time series data (\a value against \a time) to OHLC binned
+  data points. The return value can then be passed on to \ref setData.
+
+  The size of the bins can be controlled with \a timeBinSize in the same units as \a time is given.
+  For example, if the unit of \a time is seconds and single OHLC/Candlesticks should span an hour
+  each, set \a timeBinSize to 3600.
+
+  \a timeBinOffset allows to control precisely at what \a time coordinate a bin should start. The
+  value passed as \a timeBinOffset doesn't need to be in the range encompassed by the \a time keys.
+  It merely defines the mathematical offset/phase of the bins that will be used to process the
+  data.
+*/
+QCPFinancialDataMap QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
+{
+  QCPFinancialDataMap map;
+  int count = qMin(time.size(), value.size());
+  if (count == 0)
+    return QCPFinancialDataMap();
+
+  QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
+  int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
+  for (int i=0; i<count; ++i)
+  {
+    int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
+    if (currentBinIndex == index) // data point still in current bin, extend high/low:
+    {
+      if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
+      if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
+      if (i == count-1) // last data point is in current bin, finalize bin:
+      {
+        currentBinData.close = value.at(i);
+        currentBinData.key = timeBinOffset+(index)*timeBinSize;
+        map.insert(currentBinData.key, currentBinData);
+      }
+    } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
+    {
+      // finalize current bin:
+      currentBinData.close = value.at(i-1);
+      currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
+      map.insert(currentBinData.key, currentBinData);
+      // start next bin:
+      currentBinIndex = index;
+      currentBinData.open = value.at(i);
+      currentBinData.high = value.at(i);
+      currentBinData.low = value.at(i);
+    }
+  }
+
+  return map;
+}
+
+/* inherits documentation from base class */
+void QCPFinancial::draw(QCPPainter *painter)
+{
+  // get visible data range:
+  QCPFinancialDataMap::const_iterator lower, upper; // note that upper is the actual upper point, and not 1 step after the upper point
+  getVisibleDataBounds(lower, upper);
+  if (lower == mData->constEnd() || upper == mData->constEnd())
+    return;
+
+  // draw visible data range according to configured style:
+  switch (mChartStyle)
+  {
+    case QCPFinancial::csOhlc:
+      drawOhlcPlot(painter, lower, upper+1); break;
+    case QCPFinancial::csCandlestick:
+      drawCandlestickPlot(painter, lower, upper+1); break;
+  }
+}
+
+/* inherits documentation from base class */
+void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
+{
+  painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
+  if (mChartStyle == csOhlc)
+  {
+    if (mTwoColored)
+    {
+      // draw upper left half icon with positive color:
+      painter->setBrush(mBrushPositive);
+      painter->setPen(mPenPositive);
+      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
+      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
+      // draw bottom right hald icon with negative color:
+      painter->setBrush(mBrushNegative);
+      painter->setPen(mPenNegative);
+      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
+      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
+    } else
+    {
+      painter->setBrush(mBrush);
+      painter->setPen(mPen);
+      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
+    }
+  } else if (mChartStyle == csCandlestick)
+  {
+    if (mTwoColored)
+    {
+      // draw upper left half icon with positive color:
+      painter->setBrush(mBrushPositive);
+      painter->setPen(mPenPositive);
+      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
+      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
+      // draw bottom right hald icon with negative color:
+      painter->setBrush(mBrushNegative);
+      painter->setPen(mPenNegative);
+      painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
+      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
+    } else
+    {
+      painter->setBrush(mBrush);
+      painter->setPen(mPen);
+      painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
+      painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
+    }
+  }
+}
+
+/* inherits documentation from base class */
+QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+
+  double current;
+  QCPFinancialDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    current = it.value().key;
+    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && current < 0) || (inSignDomain == sdPositive && current > 0))
+    {
+      if (current < range.lower || !haveLower)
+      {
+        range.lower = current;
+        haveLower = true;
+      }
+      if (current > range.upper || !haveUpper)
+      {
+        range.upper = current;
+        haveUpper = true;
+      }
+    }
+    ++it;
+  }
+  // determine exact range by including width of bars/flags:
+  if (haveLower && mKeyAxis)
+    range.lower = range.lower-mWidth*0.5;
+  if (haveUpper && mKeyAxis)
+    range.upper = range.upper+mWidth*0.5;
+  foundRange = haveLower && haveUpper;
+  return range;
+}
+
+/* inherits documentation from base class */
+QCPRange QCPFinancial::getValueRange(bool &foundRange, QCPAbstractPlottable::SignDomain inSignDomain) const
+{
+  QCPRange range;
+  bool haveLower = false;
+  bool haveUpper = false;
+
+  QCPFinancialDataMap::const_iterator it = mData->constBegin();
+  while (it != mData->constEnd())
+  {
+    // high:
+    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().high < 0) || (inSignDomain == sdPositive && it.value().high > 0))
+    {
+      if (it.value().high < range.lower || !haveLower)
+      {
+        range.lower = it.value().high;
+        haveLower = true;
+      }
+      if (it.value().high > range.upper || !haveUpper)
+      {
+        range.upper = it.value().high;
+        haveUpper = true;
+      }
+    }
+    // low:
+    if (inSignDomain == sdBoth || (inSignDomain == sdNegative && it.value().low < 0) || (inSignDomain == sdPositive && it.value().low > 0))
+    {
+      if (it.value().low < range.lower || !haveLower)
+      {
+        range.lower = it.value().low;
+        haveLower = true;
+      }
+      if (it.value().low > range.upper || !haveUpper)
+      {
+        range.upper = it.value().low;
+        haveUpper = true;
+      }
+    }
+    ++it;
+  }
+
+  foundRange = haveLower && haveUpper;
+  return range;
+}
+
+/*! \internal
+
+  Draws the data from \a begin to \a end as OHLC bars with the provided \a painter.
+
+  This method is a helper function for \ref draw. It is used when the chart style is \ref csOhlc.
+*/
+void QCPFinancial::drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+
+  QPen linePen;
+
+  if (keyAxis->orientation() == Qt::Horizontal)
+  {
+    for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
+    {
+      if (mSelected)
+        linePen = mSelectedPen;
+      else if (mTwoColored)
+        linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
+      else
+        linePen = mPen;
+      painter->setPen(linePen);
+      double keyPixel = keyAxis->coordToPixel(it.value().key);
+      double openPixel = valueAxis->coordToPixel(it.value().open);
+      double closePixel = valueAxis->coordToPixel(it.value().close);
+      // draw backbone:
+      painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)));
+      // draw open:
+      double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
+      painter->drawLine(QPointF(keyPixel-keyWidthPixels, openPixel), QPointF(keyPixel, openPixel));
+      // draw close:
+      painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+keyWidthPixels, closePixel));
+    }
+  } else
+  {
+    for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
+    {
+      if (mSelected)
+        linePen = mSelectedPen;
+      else if (mTwoColored)
+        linePen = it.value().close >= it.value().open ? mPenPositive : mPenNegative;
+      else
+        linePen = mPen;
+      painter->setPen(linePen);
+      double keyPixel = keyAxis->coordToPixel(it.value().key);
+      double openPixel = valueAxis->coordToPixel(it.value().open);
+      double closePixel = valueAxis->coordToPixel(it.value().close);
+      // draw backbone:
+      painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel));
+      // draw open:
+      double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5); // sign of this makes sure open/close are on correct sides
+      painter->drawLine(QPointF(openPixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel));
+      // draw close:
+      painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+keyWidthPixels));
+    }
+  }
+}
+
+/*! \internal
+
+  Draws the data from \a begin to \a end as Candlesticks with the provided \a painter.
+
+  This method is a helper function for \ref draw. It is used when the chart style is \ref csCandlestick.
+*/
+void QCPFinancial::drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end)
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
+
+  QPen linePen;
+  QBrush boxBrush;
+
+  if (keyAxis->orientation() == Qt::Horizontal)
+  {
+    for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
+    {
+      if (mSelected)
+      {
+        linePen = mSelectedPen;
+        boxBrush = mSelectedBrush;
+      } else if (mTwoColored)
+      {
+        if (it.value().close >= it.value().open)
+        {
+          linePen = mPenPositive;
+          boxBrush = mBrushPositive;
+        } else
+        {
+          linePen = mPenNegative;
+          boxBrush = mBrushNegative;
+        }
+      } else
+      {
+        linePen = mPen;
+        boxBrush = mBrush;
+      }
+      painter->setPen(linePen);
+      painter->setBrush(boxBrush);
+      double keyPixel = keyAxis->coordToPixel(it.value().key);
+      double openPixel = valueAxis->coordToPixel(it.value().open);
+      double closePixel = valueAxis->coordToPixel(it.value().close);
+      // draw high:
+      painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))));
+      // draw low:
+      painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))));
+      // draw open-close box:
+      double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
+      painter->drawRect(QRectF(QPointF(keyPixel-keyWidthPixels, closePixel), QPointF(keyPixel+keyWidthPixels, openPixel)));
+    }
+  } else // keyAxis->orientation() == Qt::Vertical
+  {
+    for (QCPFinancialDataMap::const_iterator it = begin; it != end; ++it)
+    {
+      if (mSelected)
+      {
+        linePen = mSelectedPen;
+        boxBrush = mSelectedBrush;
+      } else if (mTwoColored)
+      {
+        if (it.value().close >= it.value().open)
+        {
+          linePen = mPenPositive;
+          boxBrush = mBrushPositive;
+        } else
+        {
+          linePen = mPenNegative;
+          boxBrush = mBrushNegative;
+        }
+      } else
+      {
+        linePen = mPen;
+        boxBrush = mBrush;
+      }
+      painter->setPen(linePen);
+      painter->setBrush(boxBrush);
+      double keyPixel = keyAxis->coordToPixel(it.value().key);
+      double openPixel = valueAxis->coordToPixel(it.value().open);
+      double closePixel = valueAxis->coordToPixel(it.value().close);
+      // draw high:
+      painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel));
+      // draw low:
+      painter->drawLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel));
+      // draw open-close box:
+      double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it.value().key-mWidth*0.5);
+      painter->drawRect(QRectF(QPointF(closePixel, keyPixel-keyWidthPixels), QPointF(openPixel, keyPixel+keyWidthPixels)));
+    }
+  }
+}
+
+/*! \internal
+
+  This method is a helper function for \ref selectTest. It is used to test for selection when the
+  chart style is \ref csOhlc. It only tests against the data points between \a begin and \a end.
+*/
+double QCPFinancial::ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
+
+  double minDistSqr = std::numeric_limits<double>::max();
+  QCPFinancialDataMap::const_iterator it;
+  if (keyAxis->orientation() == Qt::Horizontal)
+  {
+    for (it = begin; it != end; ++it)
+    {
+      double keyPixel = keyAxis->coordToPixel(it.value().key);
+      // calculate distance to backbone:
+      double currentDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), pos);
+      if (currentDistSqr < minDistSqr)
+        minDistSqr = currentDistSqr;
+    }
+  } else // keyAxis->orientation() == Qt::Vertical
+  {
+    for (it = begin; it != end; ++it)
+    {
+      double keyPixel = keyAxis->coordToPixel(it.value().key);
+      // calculate distance to backbone:
+      double currentDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), pos);
+      if (currentDistSqr < minDistSqr)
+        minDistSqr = currentDistSqr;
+    }
+  }
+  return qSqrt(minDistSqr);
+}
+
+/*! \internal
+
+  This method is a helper function for \ref selectTest. It is used to test for selection when the
+  chart style is \ref csCandlestick. It only tests against the data points between \a begin and \a
+  end.
+*/
+double QCPFinancial::candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const
+{
+  QCPAxis *keyAxis = mKeyAxis.data();
+  QCPAxis *valueAxis = mValueAxis.data();
+  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
+
+  double minDistSqr = std::numeric_limits<double>::max();
+  QCPFinancialDataMap::const_iterator it;
+  if (keyAxis->orientation() == Qt::Horizontal)
+  {
+    for (it = begin; it != end; ++it)
+    {
+      double currentDistSqr;
+      // determine whether pos is in open-close-box:
+      QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
+      QCPRange boxValueRange(it.value().close, it.value().open);
+      double posKey, posValue;
+      pixelsToCoords(pos, posKey, posValue);
+      if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
+      {
+        currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
+      } else
+      {
+        // calculate distance to high/low lines:
+        double keyPixel = keyAxis->coordToPixel(it.value().key);
+        double highLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it.value().open, it.value().close))), pos);
+        double lowLineDistSqr = distSqrToLine(QPointF(keyPixel, valueAxis->coordToPixel(it.value().low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it.value().open, it.value().close))), pos);
+        currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
+      }
+      if (currentDistSqr < minDistSqr)
+        minDistSqr = currentDistSqr;
+    }
+  } else // keyAxis->orientation() == Qt::Vertical
+  {
+    for (it = begin; it != end; ++it)
+    {
+      double currentDistSqr;
+      // determine whether pos is in open-close-box:
+      QCPRange boxKeyRange(it.value().key-mWidth*0.5, it.value().key+mWidth*0.5);
+      QCPRange boxValueRange(it.value().close, it.value().open);
+      double posKey, posValue;
+      pixelsToCoords(pos, posKey, posValue);
+      if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
+      {
+        currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
+      } else
+      {
+        // calculate distance to high/low lines:
+        double keyPixel = keyAxis->coordToPixel(it.value().key);
+        double highLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it.value().open, it.value().close)), keyPixel), pos);
+        double lowLineDistSqr = distSqrToLine(QPointF(valueAxis->coordToPixel(it.value().low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it.value().open, it.value().close)), keyPixel), pos);
+        currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
+      }
+      if (currentDistSqr < minDistSqr)
+        minDistSqr = currentDistSqr;
+    }
+  }
+  return qSqrt(minDistSqr);
+}
+
+/*!  \internal
+
+  called by the drawing methods to determine which data (key) range is visible at the current key
+  axis range setting, so only that needs to be processed.
+
+  \a lower returns an iterator to the lowest data point that needs to be taken into account when
+  plotting. Note that in order to get a clean plot all the way to the edge of the axis rect, \a
+  lower may still be just outside the visible range.
+
+  \a upper returns an iterator to the highest data point. Same as before, \a upper may also lie
+  just outside of the visible range.
+
+  if the plottable contains no data, both \a lower and \a upper point to constEnd.
+
+  \see QCPGraph::getVisibleDataBounds
+*/
+void QCPFinancial::getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const
+{
+  if (!mKeyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
+  if (mData->isEmpty())
+  {
+    lower = mData->constEnd();
+    upper = mData->constEnd();
+    return;
+  }
+
+  // get visible data range as QMap iterators
+  QCPFinancialDataMap::const_iterator lbound = mData->lowerBound(mKeyAxis.data()->range().lower);
+  QCPFinancialDataMap::const_iterator ubound = mData->upperBound(mKeyAxis.data()->range().upper);
+  bool lowoutlier = lbound != mData->constBegin(); // indicates whether there exist points below axis range
+  bool highoutlier = ubound != mData->constEnd(); // indicates whether there exist points above axis range
+
+  lower = (lowoutlier ? lbound-1 : lbound); // data point range that will be actually drawn
+  upper = (highoutlier ? ubound : ubound-1); // data point range that will be actually drawn
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemStraightLine
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemStraightLine
+  \brief A straight line that spans infinitely in both directions
+
+  \image html QCPItemStraightLine.png "Straight line example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a point1 and \a point2, which define the straight line.
+*/
+
+/*!
+  Creates a straight line item and sets default values.
+
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemStraightLine::QCPItemStraightLine(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  point1(createPosition(QLatin1String("point1"))),
+  point2(createPosition(QLatin1String("point2")))
+{
+  point1->setCoords(0, 0);
+  point2->setCoords(1, 1);
+
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue,2));
+}
+
+QCPItemStraightLine::~QCPItemStraightLine()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line
+
+  \see setSelectedPen
+*/
+void QCPItemStraightLine::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line when selected
+
+  \see setPen, setSelected
+*/
+void QCPItemStraightLine::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/* inherits documentation from base class */
+double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  return distToStraightLine(QVector2D(point1->pixelPoint()), QVector2D(point2->pixelPoint()-point1->pixelPoint()), QVector2D(pos));
+}
+
+/* inherits documentation from base class */
+void QCPItemStraightLine::draw(QCPPainter *painter)
+{
+  QVector2D start(point1->pixelPoint());
+  QVector2D end(point2->pixelPoint());
+  // get visible segment of straight line inside clipRect:
+  double clipPad = mainPen().widthF();
+  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
+  // paint visible segment, if existent:
+  if (!line.isNull())
+  {
+    painter->setPen(mainPen());
+    painter->drawLine(line);
+  }
+}
+
+/*! \internal
+
+  finds the shortest distance of \a point to the straight line defined by the base point \a
+  base and the direction vector \a vec.
+
+  This is a helper function for \ref selectTest.
+*/
+double QCPItemStraightLine::distToStraightLine(const QVector2D &base, const QVector2D &vec, const QVector2D &point) const
+{
+  return qAbs((base.y()-point.y())*vec.x()-(base.x()-point.x())*vec.y())/vec.length();
+}
+
+/*! \internal
+
+  Returns the section of the straight line defined by \a base and direction vector \a
+  vec, that is visible in the specified \a rect.
+
+  This is a helper function for \ref draw.
+*/
+QLineF QCPItemStraightLine::getRectClippedStraightLine(const QVector2D &base, const QVector2D &vec, const QRect &rect) const
+{
+  double bx, by;
+  double gamma;
+  QLineF result;
+  if (vec.x() == 0 && vec.y() == 0)
+    return result;
+  if (qFuzzyIsNull(vec.x())) // line is vertical
+  {
+    // check top of rect:
+    bx = rect.left();
+    by = rect.top();
+    gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
+    if (gamma >= 0 && gamma <= rect.width())
+      result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
+  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
+  {
+    // check left of rect:
+    bx = rect.left();
+    by = rect.top();
+    gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
+    if (gamma >= 0 && gamma <= rect.height())
+      result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
+  } else // line is skewed
+  {
+    QList<QVector2D> pointVectors;
+    // check top of rect:
+    bx = rect.left();
+    by = rect.top();
+    gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
+    if (gamma >= 0 && gamma <= rect.width())
+      pointVectors.append(QVector2D(bx+gamma, by));
+    // check bottom of rect:
+    bx = rect.left();
+    by = rect.bottom();
+    gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
+    if (gamma >= 0 && gamma <= rect.width())
+      pointVectors.append(QVector2D(bx+gamma, by));
+    // check left of rect:
+    bx = rect.left();
+    by = rect.top();
+    gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
+    if (gamma >= 0 && gamma <= rect.height())
+      pointVectors.append(QVector2D(bx, by+gamma));
+    // check right of rect:
+    bx = rect.right();
+    by = rect.top();
+    gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
+    if (gamma >= 0 && gamma <= rect.height())
+      pointVectors.append(QVector2D(bx, by+gamma));
+
+    // evaluate points:
+    if (pointVectors.size() == 2)
+    {
+      result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
+    } else if (pointVectors.size() > 2)
+    {
+      // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
+      double distSqrMax = 0;
+      QVector2D pv1, pv2;
+      for (int i=0; i<pointVectors.size()-1; ++i)
+      {
+        for (int k=i+1; k<pointVectors.size(); ++k)
+        {
+          double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
+          if (distSqr > distSqrMax)
+          {
+            pv1 = pointVectors.at(i);
+            pv2 = pointVectors.at(k);
+            distSqrMax = distSqr;
+          }
+        }
+      }
+      result.setPoints(pv1.toPointF(), pv2.toPointF());
+    }
+  }
+  return result;
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the
+  item is not selected and mSelectedPen when it is.
+*/
+QPen QCPItemStraightLine::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemLine
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemLine
+  \brief A line from one point to another
+
+  \image html QCPItemLine.png "Line example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a start and \a end, which define the end points of the line.
+
+  With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an arrow.
+*/
+
+/*!
+  Creates a line item and sets default values.
+
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemLine::QCPItemLine(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  start(createPosition(QLatin1String("start"))),
+  end(createPosition(QLatin1String("end")))
+{
+  start->setCoords(0, 0);
+  end->setCoords(1, 1);
+
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue,2));
+}
+
+QCPItemLine::~QCPItemLine()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line
+
+  \see setSelectedPen
+*/
+void QCPItemLine::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line when selected
+
+  \see setPen, setSelected
+*/
+void QCPItemLine::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the line ending style of the head. The head corresponds to the \a end position.
+
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify
+  a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
+
+  \see setTail
+*/
+void QCPItemLine::setHead(const QCPLineEnding &head)
+{
+  mHead = head;
+}
+
+/*!
+  Sets the line ending style of the tail. The tail corresponds to the \a start position.
+
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify
+  a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
+
+  \see setHead
+*/
+void QCPItemLine::setTail(const QCPLineEnding &tail)
+{
+  mTail = tail;
+}
+
+/* inherits documentation from base class */
+double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  return qSqrt(distSqrToLine(start->pixelPoint(), end->pixelPoint(), pos));
+}
+
+/* inherits documentation from base class */
+void QCPItemLine::draw(QCPPainter *painter)
+{
+  QVector2D startVec(start->pixelPoint());
+  QVector2D endVec(end->pixelPoint());
+  if (startVec.toPoint() == endVec.toPoint())
+    return;
+  // get visible segment of straight line inside clipRect:
+  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
+  clipPad = qMax(clipPad, (double)mainPen().widthF());
+  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
+  // paint visible segment, if existent:
+  if (!line.isNull())
+  {
+    painter->setPen(mainPen());
+    painter->drawLine(line);
+    painter->setBrush(Qt::SolidPattern);
+    if (mTail.style() != QCPLineEnding::esNone)
+      mTail.draw(painter, startVec, startVec-endVec);
+    if (mHead.style() != QCPLineEnding::esNone)
+      mHead.draw(painter, endVec, endVec-startVec);
+  }
+}
+
+/*! \internal
+
+  Returns the section of the line defined by \a start and \a end, that is visible in the specified
+  \a rect.
+
+  This is a helper function for \ref draw.
+*/
+QLineF QCPItemLine::getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const
+{
+  bool containsStart = rect.contains(start.x(), start.y());
+  bool containsEnd = rect.contains(end.x(), end.y());
+  if (containsStart && containsEnd)
+    return QLineF(start.toPointF(), end.toPointF());
+
+  QVector2D base = start;
+  QVector2D vec = end-start;
+  double bx, by;
+  double gamma, mu;
+  QLineF result;
+  QList<QVector2D> pointVectors;
+
+  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
+  {
+    // check top of rect:
+    bx = rect.left();
+    by = rect.top();
+    mu = (by-base.y())/vec.y();
+    if (mu >= 0 && mu <= 1)
+    {
+      gamma = base.x()-bx + mu*vec.x();
+      if (gamma >= 0 && gamma <= rect.width())
+        pointVectors.append(QVector2D(bx+gamma, by));
+    }
+    // check bottom of rect:
+    bx = rect.left();
+    by = rect.bottom();
+    mu = (by-base.y())/vec.y();
+    if (mu >= 0 && mu <= 1)
+    {
+      gamma = base.x()-bx + mu*vec.x();
+      if (gamma >= 0 && gamma <= rect.width())
+        pointVectors.append(QVector2D(bx+gamma, by));
+    }
+  }
+  if (!qFuzzyIsNull(vec.x())) // line is not vertical
+  {
+    // check left of rect:
+    bx = rect.left();
+    by = rect.top();
+    mu = (bx-base.x())/vec.x();
+    if (mu >= 0 && mu <= 1)
+    {
+      gamma = base.y()-by + mu*vec.y();
+      if (gamma >= 0 && gamma <= rect.height())
+        pointVectors.append(QVector2D(bx, by+gamma));
+    }
+    // check right of rect:
+    bx = rect.right();
+    by = rect.top();
+    mu = (bx-base.x())/vec.x();
+    if (mu >= 0 && mu <= 1)
+    {
+      gamma = base.y()-by + mu*vec.y();
+      if (gamma >= 0 && gamma <= rect.height())
+        pointVectors.append(QVector2D(bx, by+gamma));
+    }
+  }
+
+  if (containsStart)
+    pointVectors.append(start);
+  if (containsEnd)
+    pointVectors.append(end);
+
+  // evaluate points:
+  if (pointVectors.size() == 2)
+  {
+    result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
+  } else if (pointVectors.size() > 2)
+  {
+    // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
+    double distSqrMax = 0;
+    QVector2D pv1, pv2;
+    for (int i=0; i<pointVectors.size()-1; ++i)
+    {
+      for (int k=i+1; k<pointVectors.size(); ++k)
+      {
+        double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
+        if (distSqr > distSqrMax)
+        {
+          pv1 = pointVectors.at(i);
+          pv2 = pointVectors.at(k);
+          distSqrMax = distSqr;
+        }
+      }
+    }
+    result.setPoints(pv1.toPointF(), pv2.toPointF());
+  }
+  return result;
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the
+  item is not selected and mSelectedPen when it is.
+*/
+QPen QCPItemLine::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemCurve
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemCurve
+  \brief A curved line from one point to another
+
+  \image html QCPItemCurve.png "Curve example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has four positions, \a start and \a end, which define the end points of the line, and two
+  control points which define the direction the line exits from the start and the direction from
+  which it approaches the end: \a startDir and \a endDir.
+
+  With \ref setHead and \ref setTail you may set different line ending styles, e.g. to create an
+  arrow.
+
+  Often it is desirable for the control points to stay at fixed relative positions to the start/end
+  point. This can be achieved by setting the parent anchor e.g. of \a startDir simply to \a start,
+  and then specify the desired pixel offset with QCPItemPosition::setCoords on \a startDir.
+*/
+
+/*!
+  Creates a curve item and sets default values.
+
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemCurve::QCPItemCurve(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  start(createPosition(QLatin1String("start"))),
+  startDir(createPosition(QLatin1String("startDir"))),
+  endDir(createPosition(QLatin1String("endDir"))),
+  end(createPosition(QLatin1String("end")))
+{
+  start->setCoords(0, 0);
+  startDir->setCoords(0.5, 0);
+  endDir->setCoords(0, 0.5);
+  end->setCoords(1, 1);
+
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue,2));
+}
+
+QCPItemCurve::~QCPItemCurve()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line
+
+  \see setSelectedPen
+*/
+void QCPItemCurve::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line when selected
+
+  \see setPen, setSelected
+*/
+void QCPItemCurve::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the line ending style of the head. The head corresponds to the \a end position.
+
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify
+  a QCPLineEnding::EndingStyle here, e.g. \code setHead(QCPLineEnding::esSpikeArrow) \endcode
+
+  \see setTail
+*/
+void QCPItemCurve::setHead(const QCPLineEnding &head)
+{
+  mHead = head;
+}
+
+/*!
+  Sets the line ending style of the tail. The tail corresponds to the \a start position.
+
+  Note that due to the overloaded QCPLineEnding constructor, you may directly specify
+  a QCPLineEnding::EndingStyle here, e.g. \code setTail(QCPLineEnding::esSpikeArrow) \endcode
+
+  \see setHead
+*/
+void QCPItemCurve::setTail(const QCPLineEnding &tail)
+{
+  mTail = tail;
+}
+
+/* inherits documentation from base class */
+double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  QPointF startVec(start->pixelPoint());
+  QPointF startDirVec(startDir->pixelPoint());
+  QPointF endDirVec(endDir->pixelPoint());
+  QPointF endVec(end->pixelPoint());
+
+  QPainterPath cubicPath(startVec);
+  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
+
+  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
+  double minDistSqr = std::numeric_limits<double>::max();
+  for (int i=1; i<polygon.size(); ++i)
+  {
+    double distSqr = distSqrToLine(polygon.at(i-1), polygon.at(i), pos);
+    if (distSqr < minDistSqr)
+      minDistSqr = distSqr;
+  }
+  return qSqrt(minDistSqr);
+}
+
+/* inherits documentation from base class */
+void QCPItemCurve::draw(QCPPainter *painter)
+{
+  QPointF startVec(start->pixelPoint());
+  QPointF startDirVec(startDir->pixelPoint());
+  QPointF endDirVec(endDir->pixelPoint());
+  QPointF endVec(end->pixelPoint());
+  if (QVector2D(endVec-startVec).length() > 1e10f) // too large curves cause crash
+    return;
+
+  QPainterPath cubicPath(startVec);
+  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
+
+  // paint visible segment, if existent:
+  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
+  QRect cubicRect = cubicPath.controlPointRect().toRect();
+  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
+    cubicRect.adjust(0, 0, 1, 1);
+  if (clip.intersects(cubicRect))
+  {
+    painter->setPen(mainPen());
+    painter->drawPath(cubicPath);
+    painter->setBrush(Qt::SolidPattern);
+    if (mTail.style() != QCPLineEnding::esNone)
+      mTail.draw(painter, QVector2D(startVec), M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
+    if (mHead.style() != QCPLineEnding::esNone)
+      mHead.draw(painter, QVector2D(endVec), -cubicPath.angleAtPercent(1)/180.0*M_PI);
+  }
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the
+  item is not selected and mSelectedPen when it is.
+*/
+QPen QCPItemCurve::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemRect
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemRect
+  \brief A rectangle
+
+  \image html QCPItemRect.png "Rectangle example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a topLeft and \a bottomRight, which define the rectangle.
+*/
+
+/*!
+  Creates a rectangle item and sets default values.
+
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemRect::QCPItemRect(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  topLeft(createPosition(QLatin1String("topLeft"))),
+  bottomRight(createPosition(QLatin1String("bottomRight"))),
+  top(createAnchor(QLatin1String("top"), aiTop)),
+  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
+  right(createAnchor(QLatin1String("right"), aiRight)),
+  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
+  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
+  left(createAnchor(QLatin1String("left"), aiLeft))
+{
+  topLeft->setCoords(0, 1);
+  bottomRight->setCoords(1, 0);
+
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue,2));
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+}
+
+QCPItemRect::~QCPItemRect()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the rectangle
+
+  \see setSelectedPen, setBrush
+*/
+void QCPItemRect::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the rectangle when selected
+
+  \see setPen, setSelected
+*/
+void QCPItemRect::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the brush that will be used to fill the rectangle. To disable filling, set \a brush to
+  Qt::NoBrush.
+
+  \see setSelectedBrush, setPen
+*/
+void QCPItemRect::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the brush that will be used to fill the rectangle when selected. To disable filling, set \a
+  brush to Qt::NoBrush.
+
+  \see setBrush
+*/
+void QCPItemRect::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/* inherits documentation from base class */
+double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint()).normalized();
+  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
+  return rectSelectTest(rect, pos, filledRect);
+}
+
+/* inherits documentation from base class */
+void QCPItemRect::draw(QCPPainter *painter)
+{
+  QPointF p1 = topLeft->pixelPoint();
+  QPointF p2 = bottomRight->pixelPoint();
+  if (p1.toPoint() == p2.toPoint())
+    return;
+  QRectF rect = QRectF(p1, p2).normalized();
+  double clipPad = mainPen().widthF();
+  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
+  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
+  {
+    painter->setPen(mainPen());
+    painter->setBrush(mainBrush());
+    painter->drawRect(rect);
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemRect::anchorPixelPoint(int anchorId) const
+{
+  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
+  switch (anchorId)
+  {
+    case aiTop:         return (rect.topLeft()+rect.topRight())*0.5;
+    case aiTopRight:    return rect.topRight();
+    case aiRight:       return (rect.topRight()+rect.bottomRight())*0.5;
+    case aiBottom:      return (rect.bottomLeft()+rect.bottomRight())*0.5;
+    case aiBottomLeft:  return rect.bottomLeft();
+    case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;
+  }
+
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemRect::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
+  is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPItemRect::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemText
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemText
+  \brief A text label
+
+  \image html QCPItemText.png "Text example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  Its position is defined by the member \a position and the setting of \ref setPositionAlignment.
+  The latter controls which part of the text rect shall be aligned with \a position.
+
+  The text alignment itself (i.e. left, center, right) can be controlled with \ref
+  setTextAlignment.
+
+  The text may be rotated around the \a position point with \ref setRotation.
+*/
+
+/*!
+  Creates a text item and sets default values.
+
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemText::QCPItemText(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  position(createPosition(QLatin1String("position"))),
+  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
+  top(createAnchor(QLatin1String("top"), aiTop)),
+  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
+  right(createAnchor(QLatin1String("right"), aiRight)),
+  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
+  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
+  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
+  left(createAnchor(QLatin1String("left"), aiLeft))
+{
+  position->setCoords(0, 0);
+
+  setRotation(0);
+  setTextAlignment(Qt::AlignTop|Qt::AlignHCenter);
+  setPositionAlignment(Qt::AlignCenter);
+  setText(QLatin1String("text"));
+
+  setPen(Qt::NoPen);
+  setSelectedPen(Qt::NoPen);
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+  setColor(Qt::black);
+  setSelectedColor(Qt::blue);
+}
+
+QCPItemText::~QCPItemText()
+{
+}
+
+/*!
+  Sets the color of the text.
+*/
+void QCPItemText::setColor(const QColor &color)
+{
+  mColor = color;
+}
+
+/*!
+  Sets the color of the text that will be used when the item is selected.
+*/
+void QCPItemText::setSelectedColor(const QColor &color)
+{
+  mSelectedColor = color;
+}
+
+/*!
+  Sets the pen that will be used do draw a rectangular border around the text. To disable the
+  border, set \a pen to Qt::NoPen.
+
+  \see setSelectedPen, setBrush, setPadding
+*/
+void QCPItemText::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used do draw a rectangular border around the text, when the item is
+  selected. To disable the border, set \a pen to Qt::NoPen.
+
+  \see setPen
+*/
+void QCPItemText::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the brush that will be used do fill the background of the text. To disable the
+  background, set \a brush to Qt::NoBrush.
+
+  \see setSelectedBrush, setPen, setPadding
+*/
+void QCPItemText::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the brush that will be used do fill the background of the text, when the item is selected. To disable the
+  background, set \a brush to Qt::NoBrush.
+
+  \see setBrush
+*/
+void QCPItemText::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/*!
+  Sets the font of the text.
+
+  \see setSelectedFont, setColor
+*/
+void QCPItemText::setFont(const QFont &font)
+{
+  mFont = font;
+}
+
+/*!
+  Sets the font of the text that will be used when the item is selected.
+
+  \see setFont
+*/
+void QCPItemText::setSelectedFont(const QFont &font)
+{
+  mSelectedFont = font;
+}
+
+/*!
+  Sets the text that will be displayed. Multi-line texts are supported by inserting a line break
+  character, e.g. '\n'.
+
+  \see setFont, setColor, setTextAlignment
+*/
+void QCPItemText::setText(const QString &text)
+{
+  mText = text;
+}
+
+/*!
+  Sets which point of the text rect shall be aligned with \a position.
+
+  Examples:
+  \li If \a alignment is <tt>Qt::AlignHCenter | Qt::AlignTop</tt>, the text will be positioned such
+  that the top of the text rect will be horizontally centered on \a position.
+  \li If \a alignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt>, \a position will indicate the
+  bottom left corner of the text rect.
+
+  If you want to control the alignment of (multi-lined) text within the text rect, use \ref
+  setTextAlignment.
+*/
+void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
+{
+  mPositionAlignment = alignment;
+}
+
+/*!
+  Controls how (multi-lined) text is aligned inside the text rect (typically Qt::AlignLeft, Qt::AlignCenter or Qt::AlignRight).
+*/
+void QCPItemText::setTextAlignment(Qt::Alignment alignment)
+{
+  mTextAlignment = alignment;
+}
+
+/*!
+  Sets the angle in degrees by which the text (and the text rectangle, if visible) will be rotated
+  around \a position.
+*/
+void QCPItemText::setRotation(double degrees)
+{
+  mRotation = degrees;
+}
+
+/*!
+  Sets the distance between the border of the text rectangle and the text. The appearance (and
+  visibility) of the text rectangle can be controlled with \ref setPen and \ref setBrush.
+*/
+void QCPItemText::setPadding(const QMargins &padding)
+{
+  mPadding = padding;
+}
+
+/* inherits documentation from base class */
+double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  // The rect may be rotated, so we transform the actual clicked pos to the rotated
+  // coordinate system, so we can use the normal rectSelectTest function for non-rotated rects:
+  QPointF positionPixels(position->pixelPoint());
+  QTransform inputTransform;
+  inputTransform.translate(positionPixels.x(), positionPixels.y());
+  inputTransform.rotate(-mRotation);
+  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
+  QPointF rotatedPos = inputTransform.map(pos);
+  QFontMetrics fontMetrics(mFont);
+  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
+  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
+  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
+  textBoxRect.moveTopLeft(textPos.toPoint());
+
+  return rectSelectTest(textBoxRect, rotatedPos, true);
+}
+
+/* inherits documentation from base class */
+void QCPItemText::draw(QCPPainter *painter)
+{
+  QPointF pos(position->pixelPoint());
+  QTransform transform = painter->transform();
+  transform.translate(pos.x(), pos.y());
+  if (!qFuzzyIsNull(mRotation))
+    transform.rotate(mRotation);
+  painter->setFont(mainFont());
+  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
+  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
+  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
+  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
+  textBoxRect.moveTopLeft(textPos.toPoint());
+  double clipPad = mainPen().widthF();
+  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
+  if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
+  {
+    painter->setTransform(transform);
+    if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
+        (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
+    {
+      painter->setPen(mainPen());
+      painter->setBrush(mainBrush());
+      painter->drawRect(textBoxRect);
+    }
+    painter->setBrush(Qt::NoBrush);
+    painter->setPen(QPen(mainColor()));
+    painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemText::anchorPixelPoint(int anchorId) const
+{
+  // get actual rect points (pretty much copied from draw function):
+  QPointF pos(position->pixelPoint());
+  QTransform transform;
+  transform.translate(pos.x(), pos.y());
+  if (!qFuzzyIsNull(mRotation))
+    transform.rotate(mRotation);
+  QFontMetrics fontMetrics(mainFont());
+  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
+  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
+  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
+  textBoxRect.moveTopLeft(textPos.toPoint());
+  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
+
+  switch (anchorId)
+  {
+    case aiTopLeft:     return rectPoly.at(0);
+    case aiTop:         return (rectPoly.at(0)+rectPoly.at(1))*0.5;
+    case aiTopRight:    return rectPoly.at(1);
+    case aiRight:       return (rectPoly.at(1)+rectPoly.at(2))*0.5;
+    case aiBottomRight: return rectPoly.at(2);
+    case aiBottom:      return (rectPoly.at(2)+rectPoly.at(3))*0.5;
+    case aiBottomLeft:  return rectPoly.at(3);
+    case aiLeft:        return (rectPoly.at(3)+rectPoly.at(0))*0.5;
+  }
+
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Returns the point that must be given to the QPainter::drawText function (which expects the top
+  left point of the text rect), according to the position \a pos, the text bounding box \a rect and
+  the requested \a positionAlignment.
+
+  For example, if \a positionAlignment is <tt>Qt::AlignLeft | Qt::AlignBottom</tt> the returned point
+  will be shifted upward by the height of \a rect, starting from \a pos. So if the text is finally
+  drawn at that point, the lower left corner of the resulting text rect is at \a pos.
+*/
+QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
+{
+  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
+    return pos;
+
+  QPointF result = pos; // start at top left
+  if (positionAlignment.testFlag(Qt::AlignHCenter))
+    result.rx() -= rect.width()/2.0;
+  else if (positionAlignment.testFlag(Qt::AlignRight))
+    result.rx() -= rect.width();
+  if (positionAlignment.testFlag(Qt::AlignVCenter))
+    result.ry() -= rect.height()/2.0;
+  else if (positionAlignment.testFlag(Qt::AlignBottom))
+    result.ry() -= rect.height();
+  return result;
+}
+
+/*! \internal
+
+  Returns the font that should be used for drawing text. Returns mFont when the item is not selected
+  and mSelectedFont when it is.
+*/
+QFont QCPItemText::mainFont() const
+{
+  return mSelected ? mSelectedFont : mFont;
+}
+
+/*! \internal
+
+  Returns the color that should be used for drawing text. Returns mColor when the item is not
+  selected and mSelectedColor when it is.
+*/
+QColor QCPItemText::mainColor() const
+{
+  return mSelected ? mSelectedColor : mColor;
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemText::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
+  is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPItemText::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemEllipse
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemEllipse
+  \brief An ellipse
+
+  \image html QCPItemEllipse.png "Ellipse example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a topLeft and \a bottomRight, which define the rect the ellipse will be drawn in.
+*/
+
+/*!
+  Creates an ellipse item and sets default values.
+
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemEllipse::QCPItemEllipse(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  topLeft(createPosition(QLatin1String("topLeft"))),
+  bottomRight(createPosition(QLatin1String("bottomRight"))),
+  topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
+  top(createAnchor(QLatin1String("top"), aiTop)),
+  topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
+  right(createAnchor(QLatin1String("right"), aiRight)),
+  bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
+  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
+  bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
+  left(createAnchor(QLatin1String("left"), aiLeft)),
+  center(createAnchor(QLatin1String("center"), aiCenter))
+{
+  topLeft->setCoords(0, 1);
+  bottomRight->setCoords(1, 0);
+
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue, 2));
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+}
+
+QCPItemEllipse::~QCPItemEllipse()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the ellipse
+
+  \see setSelectedPen, setBrush
+*/
+void QCPItemEllipse::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the ellipse when selected
+
+  \see setPen, setSelected
+*/
+void QCPItemEllipse::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the brush that will be used to fill the ellipse. To disable filling, set \a brush to
+  Qt::NoBrush.
+
+  \see setSelectedBrush, setPen
+*/
+void QCPItemEllipse::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the brush that will be used to fill the ellipse when selected. To disable filling, set \a
+  brush to Qt::NoBrush.
+
+  \see setBrush
+*/
+void QCPItemEllipse::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/* inherits documentation from base class */
+double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  double result = -1;
+  QPointF p1 = topLeft->pixelPoint();
+  QPointF p2 = bottomRight->pixelPoint();
+  QPointF center((p1+p2)/2.0);
+  double a = qAbs(p1.x()-p2.x())/2.0;
+  double b = qAbs(p1.y()-p2.y())/2.0;
+  double x = pos.x()-center.x();
+  double y = pos.y()-center.y();
+
+  // distance to border:
+  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
+  result = qAbs(c-1)*qSqrt(x*x+y*y);
+  // filled ellipse, allow click inside to count as hit:
+  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
+  {
+    if (x*x/(a*a) + y*y/(b*b) <= 1)
+      result = mParentPlot->selectionTolerance()*0.99;
+  }
+  return result;
+}
+
+/* inherits documentation from base class */
+void QCPItemEllipse::draw(QCPPainter *painter)
+{
+  QPointF p1 = topLeft->pixelPoint();
+  QPointF p2 = bottomRight->pixelPoint();
+  if (p1.toPoint() == p2.toPoint())
+    return;
+  QRectF ellipseRect = QRectF(p1, p2).normalized();
+  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
+  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
+  {
+    painter->setPen(mainPen());
+    painter->setBrush(mainBrush());
+#ifdef __EXCEPTIONS
+    try // drawEllipse sometimes throws exceptions if ellipse is too big
+    {
+#endif
+      painter->drawEllipse(ellipseRect);
+#ifdef __EXCEPTIONS
+    } catch (...)
+    {
+      qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
+      setVisible(false);
+    }
+#endif
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemEllipse::anchorPixelPoint(int anchorId) const
+{
+  QRectF rect = QRectF(topLeft->pixelPoint(), bottomRight->pixelPoint());
+  switch (anchorId)
+  {
+    case aiTopLeftRim:     return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
+    case aiTop:            return (rect.topLeft()+rect.topRight())*0.5;
+    case aiTopRightRim:    return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
+    case aiRight:          return (rect.topRight()+rect.bottomRight())*0.5;
+    case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
+    case aiBottom:         return (rect.bottomLeft()+rect.bottomRight())*0.5;
+    case aiBottomLeftRim:  return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
+    case aiLeft:           return (rect.topLeft()+rect.bottomLeft())*0.5;
+    case aiCenter:         return (rect.topLeft()+rect.bottomRight())*0.5;
+  }
+
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemEllipse::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
+  is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPItemEllipse::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemPixmap
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemPixmap
+  \brief An arbitrary pixmap
+
+  \image html QCPItemPixmap.png "Pixmap example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a topLeft and \a bottomRight, which define the rectangle the pixmap will
+  be drawn in. Depending on the scale setting (\ref setScaled), the pixmap will be either scaled to
+  fit the rectangle or be drawn aligned to the topLeft position.
+
+  If scaling is enabled and \a topLeft is further to the bottom/right than \a bottomRight (as shown
+  on the right side of the example image), the pixmap will be flipped in the respective
+  orientations.
+*/
+
+/*!
+  Creates a rectangle item and sets default values.
+
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemPixmap::QCPItemPixmap(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  topLeft(createPosition(QLatin1String("topLeft"))),
+  bottomRight(createPosition(QLatin1String("bottomRight"))),
+  top(createAnchor(QLatin1String("top"), aiTop)),
+  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
+  right(createAnchor(QLatin1String("right"), aiRight)),
+  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
+  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
+  left(createAnchor(QLatin1String("left"), aiLeft))
+{
+  topLeft->setCoords(0, 1);
+  bottomRight->setCoords(1, 0);
+
+  setPen(Qt::NoPen);
+  setSelectedPen(QPen(Qt::blue));
+  setScaled(false, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+}
+
+QCPItemPixmap::~QCPItemPixmap()
+{
+}
+
+/*!
+  Sets the pixmap that will be displayed.
+*/
+void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
+{
+  mPixmap = pixmap;
+  if (mPixmap.isNull())
+    qDebug() << Q_FUNC_INFO << "pixmap is null";
+}
+
+/*!
+  Sets whether the pixmap will be scaled to fit the rectangle defined by the \a topLeft and \a
+  bottomRight positions.
+*/
+void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
+{
+  mScaled = scaled;
+  mAspectRatioMode = aspectRatioMode;
+  mTransformationMode = transformationMode;
+  updateScaledPixmap();
+}
+
+/*!
+  Sets the pen that will be used to draw a border around the pixmap.
+
+  \see setSelectedPen, setBrush
+*/
+void QCPItemPixmap::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw a border around the pixmap when selected
+
+  \see setPen, setSelected
+*/
+void QCPItemPixmap::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/* inherits documentation from base class */
+double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  return rectSelectTest(getFinalRect(), pos, true);
+}
+
+/* inherits documentation from base class */
+void QCPItemPixmap::draw(QCPPainter *painter)
+{
+  bool flipHorz = false;
+  bool flipVert = false;
+  QRect rect = getFinalRect(&flipHorz, &flipVert);
+  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
+  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
+  if (boundingRect.intersects(clipRect()))
+  {
+    updateScaledPixmap(rect, flipHorz, flipVert);
+    painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
+    QPen pen = mainPen();
+    if (pen.style() != Qt::NoPen)
+    {
+      painter->setPen(pen);
+      painter->setBrush(Qt::NoBrush);
+      painter->drawRect(rect);
+    }
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemPixmap::anchorPixelPoint(int anchorId) const
+{
+  bool flipHorz;
+  bool flipVert;
+  QRect rect = getFinalRect(&flipHorz, &flipVert);
+  // we actually want denormal rects (negative width/height) here, so restore
+  // the flipped state:
+  if (flipHorz)
+    rect.adjust(rect.width(), 0, -rect.width(), 0);
+  if (flipVert)
+    rect.adjust(0, rect.height(), 0, -rect.height());
+
+  switch (anchorId)
+  {
+    case aiTop:         return (rect.topLeft()+rect.topRight())*0.5;
+    case aiTopRight:    return rect.topRight();
+    case aiRight:       return (rect.topRight()+rect.bottomRight())*0.5;
+    case aiBottom:      return (rect.bottomLeft()+rect.bottomRight())*0.5;
+    case aiBottomLeft:  return rect.bottomLeft();
+    case aiLeft:        return (rect.topLeft()+rect.bottomLeft())*0.5;;
+  }
+
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Creates the buffered scaled image (\a mScaledPixmap) to fit the specified \a finalRect. The
+  parameters \a flipHorz and \a flipVert control whether the resulting image shall be flipped
+  horizontally or vertically. (This is used when \a topLeft is further to the bottom/right than \a
+  bottomRight.)
+
+  This function only creates the scaled pixmap when the buffered pixmap has a different size than
+  the expected result, so calling this function repeatedly, e.g. in the \ref draw function, does
+  not cause expensive rescaling every time.
+
+  If scaling is disabled, sets mScaledPixmap to a null QPixmap.
+*/
+void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
+{
+  if (mPixmap.isNull())
+    return;
+
+  if (mScaled)
+  {
+    if (finalRect.isNull())
+      finalRect = getFinalRect(&flipHorz, &flipVert);
+    if (finalRect.size() != mScaledPixmap.size())
+    {
+      mScaledPixmap = mPixmap.scaled(finalRect.size(), mAspectRatioMode, mTransformationMode);
+      if (flipHorz || flipVert)
+        mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
+    }
+  } else if (!mScaledPixmap.isNull())
+    mScaledPixmap = QPixmap();
+}
+
+/*! \internal
+
+  Returns the final (tight) rect the pixmap is drawn in, depending on the current item positions
+  and scaling settings.
+
+  The output parameters \a flippedHorz and \a flippedVert return whether the pixmap should be drawn
+  flipped horizontally or vertically in the returned rect. (The returned rect itself is always
+  normalized, i.e. the top left corner of the rect is actually further to the top/left than the
+  bottom right corner). This is the case when the item position \a topLeft is further to the
+  bottom/right than \a bottomRight.
+
+  If scaling is disabled, returns a rect with size of the original pixmap and the top left corner
+  aligned with the item position \a topLeft. The position \a bottomRight is ignored.
+*/
+QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
+{
+  QRect result;
+  bool flipHorz = false;
+  bool flipVert = false;
+  QPoint p1 = topLeft->pixelPoint().toPoint();
+  QPoint p2 = bottomRight->pixelPoint().toPoint();
+  if (p1 == p2)
+    return QRect(p1, QSize(0, 0));
+  if (mScaled)
+  {
+    QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
+    QPoint topLeft = p1;
+    if (newSize.width() < 0)
+    {
+      flipHorz = true;
+      newSize.rwidth() *= -1;
+      topLeft.setX(p2.x());
+    }
+    if (newSize.height() < 0)
+    {
+      flipVert = true;
+      newSize.rheight() *= -1;
+      topLeft.setY(p2.y());
+    }
+    QSize scaledSize = mPixmap.size();
+    scaledSize.scale(newSize, mAspectRatioMode);
+    result = QRect(topLeft, scaledSize);
+  } else
+  {
+    result = QRect(p1, mPixmap.size());
+  }
+  if (flippedHorz)
+    *flippedHorz = flipHorz;
+  if (flippedVert)
+    *flippedVert = flipVert;
+  return result;
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemPixmap::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemTracer
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemTracer
+  \brief Item that sticks to QCPGraph data points
+
+  \image html QCPItemTracer.png "Tracer example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  The tracer can be connected with a QCPGraph via \ref setGraph. Then it will automatically adopt
+  the coordinate axes of the graph and update its \a position to be on the graph's data. This means
+  the key stays controllable via \ref setGraphKey, but the value will follow the graph data. If a
+  QCPGraph is connected, note that setting the coordinates of the tracer item directly via \a
+  position will have no effect because they will be overriden in the next redraw (this is when the
+  coordinate update happens).
+
+  If the specified key in \ref setGraphKey is outside the key bounds of the graph, the tracer will
+  stay at the corresponding end of the graph.
+
+  With \ref setInterpolating you may specify whether the tracer may only stay exactly on data
+  points or whether it interpolates data points linearly, if given a key that lies between two data
+  points of the graph.
+
+  The tracer has different visual styles, see \ref setStyle. It is also possible to make the tracer
+  have no own visual appearance (set the style to \ref tsNone), and just connect other item
+  positions to the tracer \a position (used as an anchor) via \ref
+  QCPItemPosition::setParentAnchor.
+
+  \note The tracer position is only automatically updated upon redraws. So when the data of the
+  graph changes and immediately afterwards (without a redraw) the a position coordinates of the
+  tracer are retrieved, they will not reflect the updated data of the graph. In this case \ref
+  updatePosition must be called manually, prior to reading the tracer coordinates.
+*/
+
+/*!
+  Creates a tracer item and sets default values.
+
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemTracer::QCPItemTracer(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  position(createPosition(QLatin1String("position"))),
+  mGraph(0)
+{
+  position->setCoords(0, 0);
+
+  setBrush(Qt::NoBrush);
+  setSelectedBrush(Qt::NoBrush);
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue, 2));
+  setStyle(tsCrosshair);
+  setSize(6);
+  setInterpolating(false);
+  setGraphKey(0);
+}
+
+QCPItemTracer::~QCPItemTracer()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the tracer
+
+  \see setSelectedPen, setBrush
+*/
+void QCPItemTracer::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the line of the tracer when selected
+
+  \see setPen, setSelected
+*/
+void QCPItemTracer::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the brush that will be used to draw any fills of the tracer
+
+  \see setSelectedBrush, setPen
+*/
+void QCPItemTracer::setBrush(const QBrush &brush)
+{
+  mBrush = brush;
+}
+
+/*!
+  Sets the brush that will be used to draw any fills of the tracer, when selected.
+
+  \see setBrush, setSelected
+*/
+void QCPItemTracer::setSelectedBrush(const QBrush &brush)
+{
+  mSelectedBrush = brush;
+}
+
+/*!
+  Sets the size of the tracer in pixels, if the style supports setting a size (e.g. \ref tsSquare
+  does, \ref tsCrosshair does not).
+*/
+void QCPItemTracer::setSize(double size)
+{
+  mSize = size;
+}
+
+/*!
+  Sets the style/visual appearance of the tracer.
+
+  If you only want to use the tracer \a position as an anchor for other items, set \a style to
+  \ref tsNone.
+*/
+void QCPItemTracer::setStyle(QCPItemTracer::TracerStyle style)
+{
+  mStyle = style;
+}
+
+/*!
+  Sets the QCPGraph this tracer sticks to. The tracer \a position will be set to type
+  QCPItemPosition::ptPlotCoords and the axes will be set to the axes of \a graph.
+
+  To free the tracer from any graph, set \a graph to 0. The tracer \a position can then be placed
+  freely like any other item position. This is the state the tracer will assume when its graph gets
+  deleted while still attached to it.
+
+  \see setGraphKey
+*/
+void QCPItemTracer::setGraph(QCPGraph *graph)
+{
+  if (graph)
+  {
+    if (graph->parentPlot() == mParentPlot)
+    {
+      position->setType(QCPItemPosition::ptPlotCoords);
+      position->setAxes(graph->keyAxis(), graph->valueAxis());
+      mGraph = graph;
+      updatePosition();
+    } else
+      qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
+  } else
+  {
+    mGraph = 0;
+  }
+}
+
+/*!
+  Sets the key of the graph's data point the tracer will be positioned at. This is the only free
+  coordinate of a tracer when attached to a graph.
+
+  Depending on \ref setInterpolating, the tracer will be either positioned on the data point
+  closest to \a key, or will stay exactly at \a key and interpolate the value linearly.
+
+  \see setGraph, setInterpolating
+*/
+void QCPItemTracer::setGraphKey(double key)
+{
+  mGraphKey = key;
+}
+
+/*!
+  Sets whether the value of the graph's data points shall be interpolated, when positioning the
+  tracer.
+
+  If \a enabled is set to false and a key is given with \ref setGraphKey, the tracer is placed on
+  the data point of the graph which is closest to the key, but which is not necessarily exactly
+  there. If \a enabled is true, the tracer will be positioned exactly at the specified key, and
+  the appropriate value will be interpolated from the graph's data points linearly.
+
+  \see setGraph, setGraphKey
+*/
+void QCPItemTracer::setInterpolating(bool enabled)
+{
+  mInterpolating = enabled;
+}
+
+/* inherits documentation from base class */
+double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  QPointF center(position->pixelPoint());
+  double w = mSize/2.0;
+  QRect clip = clipRect();
+  switch (mStyle)
+  {
+    case tsNone: return -1;
+    case tsPlus:
+    {
+      if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+        return qSqrt(qMin(distSqrToLine(center+QPointF(-w, 0), center+QPointF(w, 0), pos),
+                          distSqrToLine(center+QPointF(0, -w), center+QPointF(0, w), pos)));
+      break;
+    }
+    case tsCrosshair:
+    {
+      return qSqrt(qMin(distSqrToLine(QPointF(clip.left(), center.y()), QPointF(clip.right(), center.y()), pos),
+                        distSqrToLine(QPointF(center.x(), clip.top()), QPointF(center.x(), clip.bottom()), pos)));
+    }
+    case tsCircle:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+      {
+        // distance to border:
+        double centerDist = QVector2D(center-pos).length();
+        double circleLine = w;
+        double result = qAbs(centerDist-circleLine);
+        // filled ellipse, allow click inside to count as hit:
+        if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
+        {
+          if (centerDist <= circleLine)
+            result = mParentPlot->selectionTolerance()*0.99;
+        }
+        return result;
+      }
+      break;
+    }
+    case tsSquare:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+      {
+        QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
+        bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
+        return rectSelectTest(rect, pos, filledRect);
+      }
+      break;
+    }
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+void QCPItemTracer::draw(QCPPainter *painter)
+{
+  updatePosition();
+  if (mStyle == tsNone)
+    return;
+
+  painter->setPen(mainPen());
+  painter->setBrush(mainBrush());
+  QPointF center(position->pixelPoint());
+  double w = mSize/2.0;
+  QRect clip = clipRect();
+  switch (mStyle)
+  {
+    case tsNone: return;
+    case tsPlus:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+      {
+        painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
+        painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
+      }
+      break;
+    }
+    case tsCrosshair:
+    {
+      if (center.y() > clip.top() && center.y() < clip.bottom())
+        painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
+      if (center.x() > clip.left() && center.x() < clip.right())
+        painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
+      break;
+    }
+    case tsCircle:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+        painter->drawEllipse(center, w, w);
+      break;
+    }
+    case tsSquare:
+    {
+      if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
+        painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
+      break;
+    }
+  }
+}
+
+/*!
+  If the tracer is connected with a graph (\ref setGraph), this function updates the tracer's \a
+  position to reside on the graph data, depending on the configured key (\ref setGraphKey).
+
+  It is called automatically on every redraw and normally doesn't need to be called manually. One
+  exception is when you want to read the tracer coordinates via \a position and are not sure that
+  the graph's data (or the tracer key with \ref setGraphKey) hasn't changed since the last redraw.
+  In that situation, call this function before accessing \a position, to make sure you don't get
+  out-of-date coordinates.
+
+  If there is no graph set on this tracer, this function does nothing.
+*/
+void QCPItemTracer::updatePosition()
+{
+  if (mGraph)
+  {
+    if (mParentPlot->hasPlottable(mGraph))
+    {
+      if (mGraph->data()->size() > 1)
+      {
+        QCPDataMap::const_iterator first = mGraph->data()->constBegin();
+        QCPDataMap::const_iterator last = mGraph->data()->constEnd()-1;
+        if (mGraphKey < first.key())
+          position->setCoords(first.key(), first.value().value);
+        else if (mGraphKey > last.key())
+          position->setCoords(last.key(), last.value().value);
+        else
+        {
+          QCPDataMap::const_iterator it = mGraph->data()->lowerBound(mGraphKey);
+          if (it != first) // mGraphKey is somewhere between iterators
+          {
+            QCPDataMap::const_iterator prevIt = it-1;
+            if (mInterpolating)
+            {
+              // interpolate between iterators around mGraphKey:
+              double slope = 0;
+              if (!qFuzzyCompare((double)it.key(), (double)prevIt.key()))
+                slope = (it.value().value-prevIt.value().value)/(it.key()-prevIt.key());
+              position->setCoords(mGraphKey, (mGraphKey-prevIt.key())*slope+prevIt.value().value);
+            } else
+            {
+              // find iterator with key closest to mGraphKey:
+              if (mGraphKey < (prevIt.key()+it.key())*0.5)
+                it = prevIt;
+              position->setCoords(it.key(), it.value().value);
+            }
+          } else // mGraphKey is exactly on first iterator
+            position->setCoords(it.key(), it.value().value);
+        }
+      } else if (mGraph->data()->size() == 1)
+      {
+        QCPDataMap::const_iterator it = mGraph->data()->constBegin();
+        position->setCoords(it.key(), it.value().value);
+      } else
+        qDebug() << Q_FUNC_INFO << "graph has no data";
+    } else
+      qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
+  }
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the item is not selected
+  and mSelectedPen when it is.
+*/
+QPen QCPItemTracer::mainPen() const
+{
+  return mSelected ? mSelectedPen : mPen;
+}
+
+/*! \internal
+
+  Returns the brush that should be used for drawing fills of the item. Returns mBrush when the item
+  is not selected and mSelectedBrush when it is.
+*/
+QBrush QCPItemTracer::mainBrush() const
+{
+  return mSelected ? mSelectedBrush : mBrush;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////// QCPItemBracket
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*! \class QCPItemBracket
+  \brief A bracket for referencing/highlighting certain parts in the plot.
+
+  \image html QCPItemBracket.png "Bracket example. Blue dotted circles are anchors, solid blue discs are positions."
+
+  It has two positions, \a left and \a right, which define the span of the bracket. If \a left is
+  actually farther to the left than \a right, the bracket is opened to the bottom, as shown in the
+  example image.
+
+  The bracket supports multiple styles via \ref setStyle. The length, i.e. how far the bracket
+  stretches away from the embraced span, can be controlled with \ref setLength.
+
+  \image html QCPItemBracket-length.png
+  <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
+  bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
+
+  It provides an anchor \a center, to allow connection of other items, e.g. an arrow (QCPItemLine
+  or QCPItemCurve) or a text label (QCPItemText), to the bracket.
+*/
+
+/*!
+  Creates a bracket item and sets default values.
+
+  The constructed item can be added to the plot with QCustomPlot::addItem.
+*/
+QCPItemBracket::QCPItemBracket(QCustomPlot *parentPlot) :
+  QCPAbstractItem(parentPlot),
+  left(createPosition(QLatin1String("left"))),
+  right(createPosition(QLatin1String("right"))),
+  center(createAnchor(QLatin1String("center"), aiCenter))
+{
+  left->setCoords(0, 0);
+  right->setCoords(1, 1);
+
+  setPen(QPen(Qt::black));
+  setSelectedPen(QPen(Qt::blue, 2));
+  setLength(8);
+  setStyle(bsCalligraphic);
+}
+
+QCPItemBracket::~QCPItemBracket()
+{
+}
+
+/*!
+  Sets the pen that will be used to draw the bracket.
+
+  Note that when the style is \ref bsCalligraphic, only the color will be taken from the pen, the
+  stroke and width are ignored. To change the apparent stroke width of a calligraphic bracket, use
+  \ref setLength, which has a similar effect.
+
+  \see setSelectedPen
+*/
+void QCPItemBracket::setPen(const QPen &pen)
+{
+  mPen = pen;
+}
+
+/*!
+  Sets the pen that will be used to draw the bracket when selected
+
+  \see setPen, setSelected
+*/
+void QCPItemBracket::setSelectedPen(const QPen &pen)
+{
+  mSelectedPen = pen;
+}
+
+/*!
+  Sets the \a length in pixels how far the bracket extends in the direction towards the embraced
+  span of the bracket (i.e. perpendicular to the <i>left</i>-<i>right</i>-direction)
+
+  \image html QCPItemBracket-length.png
+  <center>Demonstrating the effect of different values for \ref setLength, for styles \ref
+  bsCalligraphic and \ref bsSquare. Anchors and positions are displayed for reference.</center>
+*/
+void QCPItemBracket::setLength(double length)
+{
+  mLength = length;
+}
+
+/*!
+  Sets the style of the bracket, i.e. the shape/visual appearance.
+
+  \see setPen
+*/
+void QCPItemBracket::setStyle(QCPItemBracket::BracketStyle style)
+{
+  mStyle = style;
+}
+
+/* inherits documentation from base class */
+double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
+{
+  Q_UNUSED(details)
+  if (onlySelectable && !mSelectable)
+    return -1;
+
+  QVector2D leftVec(left->pixelPoint());
+  QVector2D rightVec(right->pixelPoint());
+  if (leftVec.toPoint() == rightVec.toPoint())
+    return -1;
+
+  QVector2D widthVec = (rightVec-leftVec)*0.5f;
+  QVector2D lengthVec(-widthVec.y(), widthVec.x());
+  lengthVec = lengthVec.normalized()*mLength;
+  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
+
+  switch (mStyle)
+  {
+    case QCPItemBracket::bsSquare:
+    case QCPItemBracket::bsRound:
+    {
+      double a = distSqrToLine((centerVec-widthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
+      double b = distSqrToLine((centerVec-widthVec+lengthVec).toPointF(), (centerVec-widthVec).toPointF(), pos);
+      double c = distSqrToLine((centerVec+widthVec+lengthVec).toPointF(), (centerVec+widthVec).toPointF(), pos);
+      return qSqrt(qMin(qMin(a, b), c));
+    }
+    case QCPItemBracket::bsCurly:
+    case QCPItemBracket::bsCalligraphic:
+    {
+      double a = distSqrToLine((centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
+      double b = distSqrToLine((centerVec-widthVec+lengthVec*0.7f).toPointF(), (centerVec-widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
+      double c = distSqrToLine((centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), (centerVec+lengthVec*0.3f).toPointF(), pos);
+      double d = distSqrToLine((centerVec+widthVec+lengthVec*0.7f).toPointF(), (centerVec+widthVec*0.75f+lengthVec*0.15f).toPointF(), pos);
+      return qSqrt(qMin(qMin(a, b), qMin(c, d)));
+    }
+  }
+  return -1;
+}
+
+/* inherits documentation from base class */
+void QCPItemBracket::draw(QCPPainter *painter)
+{
+  QVector2D leftVec(left->pixelPoint());
+  QVector2D rightVec(right->pixelPoint());
+  if (leftVec.toPoint() == rightVec.toPoint())
+    return;
+
+  QVector2D widthVec = (rightVec-leftVec)*0.5f;
+  QVector2D lengthVec(-widthVec.y(), widthVec.x());
+  lengthVec = lengthVec.normalized()*mLength;
+  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
+
+  QPolygon boundingPoly;
+  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
+               << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
+  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
+  if (clip.intersects(boundingPoly.boundingRect()))
+  {
+    painter->setPen(mainPen());
+    switch (mStyle)
+    {
+      case bsSquare:
+      {
+        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
+        painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
+        painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        break;
+      }
+      case bsRound:
+      {
+        painter->setBrush(Qt::NoBrush);
+        QPainterPath path;
+        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
+        path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
+        path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        painter->drawPath(path);
+        break;
+      }
+      case bsCurly:
+      {
+        painter->setBrush(Qt::NoBrush);
+        QPainterPath path;
+        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
+        path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+lengthVec).toPointF(), centerVec.toPointF());
+        path.cubicTo((centerVec-0.4f*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+        painter->drawPath(path);
+        break;
+      }
+      case bsCalligraphic:
+      {
+        painter->setPen(Qt::NoPen);
+        painter->setBrush(QBrush(mainPen().color()));
+        QPainterPath path;
+        path.moveTo((centerVec+widthVec+lengthVec).toPointF());
+
+        path.cubicTo((centerVec+widthVec-lengthVec*0.8f).toPointF(), (centerVec+0.4f*widthVec+0.8f*lengthVec).toPointF(), centerVec.toPointF());
+        path.cubicTo((centerVec-0.4f*widthVec+0.8f*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8f).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
+
+        path.cubicTo((centerVec-widthVec-lengthVec*0.5f).toPointF(), (centerVec-0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+lengthVec*0.2f).toPointF());
+        path.cubicTo((centerVec+0.2f*widthVec+1.2f*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5f).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
+
+        painter->drawPath(path);
+        break;
+      }
+    }
+  }
+}
+
+/* inherits documentation from base class */
+QPointF QCPItemBracket::anchorPixelPoint(int anchorId) const
+{
+  QVector2D leftVec(left->pixelPoint());
+  QVector2D rightVec(right->pixelPoint());
+  if (leftVec.toPoint() == rightVec.toPoint())
+    return leftVec.toPointF();
+
+  QVector2D widthVec = (rightVec-leftVec)*0.5f;
+  QVector2D lengthVec(-widthVec.y(), widthVec.x());
+  lengthVec = lengthVec.normalized()*mLength;
+  QVector2D centerVec = (rightVec+leftVec)*0.5f-lengthVec;
+
+  switch (anchorId)
+  {
+    case aiCenter:
+      return centerVec.toPointF();
+  }
+  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
+  return QPointF();
+}
+
+/*! \internal
+
+  Returns the pen that should be used for drawing lines. Returns mPen when the
+  item is not selected and mSelectedPen when it is.
+*/
+QPen QCPItemBracket::mainPen() const
+{
+    return mSelected ? mSelectedPen : mPen;
+}
+
diff --git a/Code/src/qcustomplot.h b/Code/src/qcustomplot.h
new file mode 100644
index 0000000000000000000000000000000000000000..0531091a1e5dc89a89c108d670099d5395e1e453
--- /dev/null
+++ b/Code/src/qcustomplot.h
@@ -0,0 +1,3767 @@
+/***************************************************************************
+**                                                                        **
+**  QCustomPlot, an easy to use, modern plotting widget for Qt            **
+**  Copyright (C) 2011-2015 Emanuel Eichhammer                            **
+**                                                                        **
+**  This program is free software: you can redistribute it and/or modify  **
+**  it under the terms of the GNU General Public License as published by  **
+**  the Free Software Foundation, either version 3 of the License, or     **
+**  (at your option) any later version.                                   **
+**                                                                        **
+**  This program is distributed in the hope that it will be useful,       **
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of        **
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         **
+**  GNU General Public License for more details.                          **
+**                                                                        **
+**  You should have received a copy of the GNU General Public License     **
+**  along with this program.  If not, see http://www.gnu.org/licenses/.   **
+**                                                                        **
+****************************************************************************
+**           Author: Emanuel Eichhammer                                   **
+**  Website/Contact: http://www.qcustomplot.com/                          **
+**             Date: 25.04.15                                             **
+**          Version: 1.3.1                                                **
+****************************************************************************/
+
+#ifndef QCUSTOMPLOT_H
+#define QCUSTOMPLOT_H
+
+#include <QObject>
+#include <QPointer>
+#include <QWidget>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QMouseEvent>
+#include <QPixmap>
+#include <QVector>
+#include <QString>
+#include <QDateTime>
+#include <QMultiMap>
+#include <QFlags>
+#include <QDebug>
+#include <QVector2D>
+#include <QStack>
+#include <QCache>
+#include <QMargins>
+#include <qmath.h>
+#include <limits>
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+#  include <qnumeric.h>
+#  include <QPrinter>
+#  include <QPrintEngine>
+#else
+#  include <QtNumeric>
+#  include <QtPrintSupport>
+#endif
+
+class QCPPainter;
+class QCustomPlot;
+class QCPLayerable;
+class QCPLayoutElement;
+class QCPLayout;
+class QCPAxis;
+class QCPAxisRect;
+class QCPAxisPainterPrivate;
+class QCPAbstractPlottable;
+class QCPGraph;
+class QCPAbstractItem;
+class QCPItemPosition;
+class QCPLayer;
+class QCPPlotTitle;
+class QCPLegend;
+class QCPAbstractLegendItem;
+class QCPColorMap;
+class QCPColorScale;
+class QCPBars;
+
+
+/*! \file */
+
+
+// decl definitions for shared library compilation/usage:
+#if defined(QCUSTOMPLOT_COMPILE_LIBRARY)
+#  define QCP_LIB_DECL Q_DECL_EXPORT
+#elif defined(QCUSTOMPLOT_USE_LIBRARY)
+#  define QCP_LIB_DECL Q_DECL_IMPORT
+#else
+#  define QCP_LIB_DECL
+#endif
+
+/*!
+  The QCP Namespace contains general enums and QFlags used throughout the QCustomPlot library
+*/
+namespace QCP
+{
+/*!
+  Defines the sides of a rectangular entity to which margins can be applied.
+
+  \see QCPLayoutElement::setAutoMargins, QCPAxisRect::setAutoMargins
+*/
+enum MarginSide { msLeft     = 0x01 ///< <tt>0x01</tt> left margin
+                  ,msRight   = 0x02 ///< <tt>0x02</tt> right margin
+                  ,msTop     = 0x04 ///< <tt>0x04</tt> top margin
+                  ,msBottom  = 0x08 ///< <tt>0x08</tt> bottom margin
+                  ,msAll     = 0xFF ///< <tt>0xFF</tt> all margins
+                  ,msNone    = 0x00 ///< <tt>0x00</tt> no margin
+                };
+Q_DECLARE_FLAGS(MarginSides, MarginSide)
+
+/*!
+  Defines what objects of a plot can be forcibly drawn antialiased/not antialiased. If an object is
+  neither forcibly drawn antialiased nor forcibly drawn not antialiased, it is up to the respective
+  element how it is drawn. Typically it provides a \a setAntialiased function for this.
+
+  \c AntialiasedElements is a flag of or-combined elements of this enum type.
+
+  \see QCustomPlot::setAntialiasedElements, QCustomPlot::setNotAntialiasedElements
+*/
+enum AntialiasedElement { aeAxes           = 0x0001 ///< <tt>0x0001</tt> Axis base line and tick marks
+                          ,aeGrid          = 0x0002 ///< <tt>0x0002</tt> Grid lines
+                          ,aeSubGrid       = 0x0004 ///< <tt>0x0004</tt> Sub grid lines
+                          ,aeLegend        = 0x0008 ///< <tt>0x0008</tt> Legend box
+                          ,aeLegendItems   = 0x0010 ///< <tt>0x0010</tt> Legend items
+                          ,aePlottables    = 0x0020 ///< <tt>0x0020</tt> Main lines of plottables (excluding error bars, see element \ref aeErrorBars)
+                          ,aeItems         = 0x0040 ///< <tt>0x0040</tt> Main lines of items
+                          ,aeScatters      = 0x0080 ///< <tt>0x0080</tt> Scatter symbols of plottables (excluding scatter symbols of type ssPixmap)
+                          ,aeErrorBars     = 0x0100 ///< <tt>0x0100</tt> Error bars
+                          ,aeFills         = 0x0200 ///< <tt>0x0200</tt> Borders of fills (e.g. under or between graphs)
+                          ,aeZeroLine      = 0x0400 ///< <tt>0x0400</tt> Zero-lines, see \ref QCPGrid::setZeroLinePen
+                          ,aeAll           = 0xFFFF ///< <tt>0xFFFF</tt> All elements
+                          ,aeNone          = 0x0000 ///< <tt>0x0000</tt> No elements
+                        };
+Q_DECLARE_FLAGS(AntialiasedElements, AntialiasedElement)
+
+/*!
+  Defines plotting hints that control various aspects of the quality and speed of plotting.
+
+  \see QCustomPlot::setPlottingHints
+*/
+enum PlottingHint { phNone            = 0x000 ///< <tt>0x000</tt> No hints are set
+                    ,phFastPolylines  = 0x001 ///< <tt>0x001</tt> Graph/Curve lines are drawn with a faster method. This reduces the quality
+                                              ///<                especially of the line segment joins. (Only relevant for solid line pens.)
+                    ,phForceRepaint   = 0x002 ///< <tt>0x002</tt> causes an immediate repaint() instead of a soft update() when QCustomPlot::replot() is called with parameter \ref QCustomPlot::rpHint.
+                                              ///<                This is set by default to prevent the plot from freezing on fast consecutive replots (e.g. user drags ranges with mouse).
+                    ,phCacheLabels    = 0x004 ///< <tt>0x004</tt> axis (tick) labels will be cached as pixmaps, increasing replot performance.
+                  };
+Q_DECLARE_FLAGS(PlottingHints, PlottingHint)
+
+/*!
+  Defines the mouse interactions possible with QCustomPlot.
+
+  \c Interactions is a flag of or-combined elements of this enum type.
+
+  \see QCustomPlot::setInteractions
+*/
+enum Interaction { iRangeDrag         = 0x001 ///< <tt>0x001</tt> Axis ranges are draggable (see \ref QCPAxisRect::setRangeDrag, \ref QCPAxisRect::setRangeDragAxes)
+                   ,iRangeZoom        = 0x002 ///< <tt>0x002</tt> Axis ranges are zoomable with the mouse wheel (see \ref QCPAxisRect::setRangeZoom, \ref QCPAxisRect::setRangeZoomAxes)
+                   ,iMultiSelect      = 0x004 ///< <tt>0x004</tt> The user can select multiple objects by holding the modifier set by \ref QCustomPlot::setMultiSelectModifier while clicking
+                   ,iSelectPlottables = 0x008 ///< <tt>0x008</tt> Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable)
+                   ,iSelectAxes       = 0x010 ///< <tt>0x010</tt> Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)
+                   ,iSelectLegend     = 0x020 ///< <tt>0x020</tt> Legends are selectable (or their child items, see QCPLegend::setSelectableParts)
+                   ,iSelectItems      = 0x040 ///< <tt>0x040</tt> Items are selectable (Rectangles, Arrows, Textitems, etc. see \ref QCPAbstractItem)
+                   ,iSelectOther      = 0x080 ///< <tt>0x080</tt> All other objects are selectable (e.g. your own derived layerables, the plot title,...)
+                 };
+Q_DECLARE_FLAGS(Interactions, Interaction)
+
+/*! \internal
+
+  Returns whether the specified \a value is considered an invalid data value for plottables (i.e.
+  is \e nan or \e +/-inf). This function is used to check data validity upon replots, when the
+  compiler flag \c QCUSTOMPLOT_CHECK_DATA is set.
+*/
+inline bool isInvalidData(double value)
+{
+  return qIsNaN(value) || qIsInf(value);
+}
+
+/*! \internal
+  \overload
+
+  Checks two arguments instead of one.
+*/
+inline bool isInvalidData(double value1, double value2)
+{
+  return isInvalidData(value1) || isInvalidData(value2);
+}
+
+/*! \internal
+
+  Sets the specified \a side of \a margins to \a value
+
+  \see getMarginValue
+*/
+inline void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
+{
+  switch (side)
+  {
+    case QCP::msLeft: margins.setLeft(value); break;
+    case QCP::msRight: margins.setRight(value); break;
+    case QCP::msTop: margins.setTop(value); break;
+    case QCP::msBottom: margins.setBottom(value); break;
+    case QCP::msAll: margins = QMargins(value, value, value, value); break;
+    default: break;
+  }
+}
+
+/*! \internal
+
+  Returns the value of the specified \a side of \a margins. If \a side is \ref QCP::msNone or
+  \ref QCP::msAll, returns 0.
+
+  \see setMarginValue
+*/
+inline int getMarginValue(const QMargins &margins, QCP::MarginSide side)
+{
+  switch (side)
+  {
+    case QCP::msLeft: return margins.left();
+    case QCP::msRight: return margins.right();
+    case QCP::msTop: return margins.top();
+    case QCP::msBottom: return margins.bottom();
+    default: break;
+  }
+  return 0;
+}
+
+} // end of namespace QCP
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::AntialiasedElements)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::PlottingHints)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::MarginSides)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCP::Interactions)
+
+
+class QCP_LIB_DECL QCPScatterStyle
+{
+  Q_GADGET
+public:
+  /*!
+    Defines the shape used for scatter points.
+
+    On plottables/items that draw scatters, the sizes of these visualizations (with exception of
+    \ref ssDot and \ref ssPixmap) can be controlled with the \ref setSize function. Scatters are
+    drawn with the pen and brush specified with \ref setPen and \ref setBrush.
+  */
+  Q_ENUMS(ScatterShape)
+  enum ScatterShape { ssNone       ///< no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines)
+                      ,ssDot       ///< \enumimage{ssDot.png} a single pixel (use \ref ssDisc or \ref ssCircle if you want a round shape with a certain radius)
+                      ,ssCross     ///< \enumimage{ssCross.png} a cross
+                      ,ssPlus      ///< \enumimage{ssPlus.png} a plus
+                      ,ssCircle    ///< \enumimage{ssCircle.png} a circle
+                      ,ssDisc      ///< \enumimage{ssDisc.png} a circle which is filled with the pen's color (not the brush as with ssCircle)
+                      ,ssSquare    ///< \enumimage{ssSquare.png} a square
+                      ,ssDiamond   ///< \enumimage{ssDiamond.png} a diamond
+                      ,ssStar      ///< \enumimage{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
+                      ,ssTriangle  ///< \enumimage{ssTriangle.png} an equilateral triangle, standing on baseline
+                      ,ssTriangleInverted ///< \enumimage{ssTriangleInverted.png} an equilateral triangle, standing on corner
+                      ,ssCrossSquare      ///< \enumimage{ssCrossSquare.png} a square with a cross inside
+                      ,ssPlusSquare       ///< \enumimage{ssPlusSquare.png} a square with a plus inside
+                      ,ssCrossCircle      ///< \enumimage{ssCrossCircle.png} a circle with a cross inside
+                      ,ssPlusCircle       ///< \enumimage{ssPlusCircle.png} a circle with a plus inside
+                      ,ssPeace     ///< \enumimage{ssPeace.png} a circle, with one vertical and two downward diagonal lines
+                      ,ssPixmap    ///< a custom pixmap specified by \ref setPixmap, centered on the data point coordinates
+                      ,ssCustom    ///< custom painter operations are performed per scatter (As QPainterPath, see \ref setCustomPath)
+                    };
+
+  QCPScatterStyle();
+  QCPScatterStyle(ScatterShape shape, double size=6);
+  QCPScatterStyle(ScatterShape shape, const QColor &color, double size);
+  QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size);
+  QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size);
+  QCPScatterStyle(const QPixmap &pixmap);
+  QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush=Qt::NoBrush, double size=6);
+
+  // getters:
+  double size() const { return mSize; }
+  ScatterShape shape() const { return mShape; }
+  QPen pen() const { return mPen; }
+  QBrush brush() const { return mBrush; }
+  QPixmap pixmap() const { return mPixmap; }
+  QPainterPath customPath() const { return mCustomPath; }
+
+  // setters:
+  void setSize(double size);
+  void setShape(ScatterShape shape);
+  void setPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setPixmap(const QPixmap &pixmap);
+  void setCustomPath(const QPainterPath &customPath);
+
+  // non-property methods:
+  bool isNone() const { return mShape == ssNone; }
+  bool isPenDefined() const { return mPenDefined; }
+  void applyTo(QCPPainter *painter, const QPen &defaultPen) const;
+  void drawShape(QCPPainter *painter, QPointF pos) const;
+  void drawShape(QCPPainter *painter, double x, double y) const;
+
+protected:
+  // property members:
+  double mSize;
+  ScatterShape mShape;
+  QPen mPen;
+  QBrush mBrush;
+  QPixmap mPixmap;
+  QPainterPath mCustomPath;
+
+  // non-property members:
+  bool mPenDefined;
+};
+Q_DECLARE_TYPEINFO(QCPScatterStyle, Q_MOVABLE_TYPE);
+
+
+class QCP_LIB_DECL QCPPainter : public QPainter
+{
+  Q_GADGET
+public:
+  /*!
+    Defines special modes the painter can operate in. They disable or enable certain subsets of features/fixes/workarounds,
+    depending on whether they are wanted on the respective output device.
+  */
+  enum PainterMode { pmDefault       = 0x00   ///< <tt>0x00</tt> Default mode for painting on screen devices
+                     ,pmVectorized   = 0x01   ///< <tt>0x01</tt> Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fixes.
+                     ,pmNoCaching    = 0x02   ///< <tt>0x02</tt> Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixmap labels
+                     ,pmNonCosmetic  = 0x04   ///< <tt>0x04</tt> Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width 1 pixel in the vector image/pdf viewer, independent of zoom.)
+                   };
+  Q_FLAGS(PainterMode PainterModes)
+  Q_DECLARE_FLAGS(PainterModes, PainterMode)
+
+  QCPPainter();
+  QCPPainter(QPaintDevice *device);
+  ~QCPPainter();
+
+  // getters:
+  bool antialiasing() const { return testRenderHint(QPainter::Antialiasing); }
+  PainterModes modes() const { return mModes; }
+
+  // setters:
+  void setAntialiasing(bool enabled);
+  void setMode(PainterMode mode, bool enabled=true);
+  void setModes(PainterModes modes);
+
+  // methods hiding non-virtual base class functions (QPainter bug workarounds):
+  bool begin(QPaintDevice *device);
+  void setPen(const QPen &pen);
+  void setPen(const QColor &color);
+  void setPen(Qt::PenStyle penStyle);
+  void drawLine(const QLineF &line);
+  void drawLine(const QPointF &p1, const QPointF &p2) {drawLine(QLineF(p1, p2));}
+  void save();
+  void restore();
+
+  // non-virtual methods:
+  void makeNonCosmetic();
+
+protected:
+  // property members:
+  PainterModes mModes;
+  bool mIsAntialiasing;
+
+  // non-property members:
+  QStack<bool> mAntialiasingStack;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCPPainter::PainterModes)
+
+
+class QCP_LIB_DECL QCPLayer : public QObject
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot)
+  Q_PROPERTY(QString name READ name)
+  Q_PROPERTY(int index READ index)
+  Q_PROPERTY(QList<QCPLayerable*> children READ children)
+  Q_PROPERTY(bool visible READ visible WRITE setVisible)
+  /// \endcond
+public:
+  QCPLayer(QCustomPlot* parentPlot, const QString &layerName);
+  ~QCPLayer();
+
+  // getters:
+  QCustomPlot *parentPlot() const { return mParentPlot; }
+  QString name() const { return mName; }
+  int index() const { return mIndex; }
+  QList<QCPLayerable*> children() const { return mChildren; }
+  bool visible() const { return mVisible; }
+
+  // setters:
+  void setVisible(bool visible);
+
+protected:
+  // property members:
+  QCustomPlot *mParentPlot;
+  QString mName;
+  int mIndex;
+  QList<QCPLayerable*> mChildren;
+  bool mVisible;
+
+  // non-virtual methods:
+  void addChild(QCPLayerable *layerable, bool prepend);
+  void removeChild(QCPLayerable *layerable);
+
+private:
+  Q_DISABLE_COPY(QCPLayer)
+
+  friend class QCustomPlot;
+  friend class QCPLayerable;
+};
+
+class QCP_LIB_DECL QCPLayerable : public QObject
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(bool visible READ visible WRITE setVisible)
+  Q_PROPERTY(QCustomPlot* parentPlot READ parentPlot)
+  Q_PROPERTY(QCPLayerable* parentLayerable READ parentLayerable)
+  Q_PROPERTY(QCPLayer* layer READ layer WRITE setLayer NOTIFY layerChanged)
+  Q_PROPERTY(bool antialiased READ antialiased WRITE setAntialiased)
+  /// \endcond
+public:
+  QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0);
+  ~QCPLayerable();
+
+  // getters:
+  bool visible() const { return mVisible; }
+  QCustomPlot *parentPlot() const { return mParentPlot; }
+  QCPLayerable *parentLayerable() const { return mParentLayerable.data(); }
+  QCPLayer *layer() const { return mLayer; }
+  bool antialiased() const { return mAntialiased; }
+
+  // setters:
+  void setVisible(bool on);
+  Q_SLOT bool setLayer(QCPLayer *layer);
+  bool setLayer(const QString &layerName);
+  void setAntialiased(bool enabled);
+
+  // introduced virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  // non-property methods:
+  bool realVisibility() const;
+
+signals:
+  void layerChanged(QCPLayer *newLayer);
+
+protected:
+  // property members:
+  bool mVisible;
+  QCustomPlot *mParentPlot;
+  QPointer<QCPLayerable> mParentLayerable;
+  QCPLayer *mLayer;
+  bool mAntialiased;
+
+  // introduced virtual methods:
+  virtual void parentPlotInitialized(QCustomPlot *parentPlot);
+  virtual QCP::Interaction selectionCategory() const;
+  virtual QRect clipRect() const;
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const = 0;
+  virtual void draw(QCPPainter *painter) = 0;
+  // events:
+  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
+  virtual void deselectEvent(bool *selectionStateChanged);
+
+  // non-property methods:
+  void initializeParentPlot(QCustomPlot *parentPlot);
+  void setParentLayerable(QCPLayerable* parentLayerable);
+  bool moveToLayer(QCPLayer *layer, bool prepend);
+  void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const;
+
+private:
+  Q_DISABLE_COPY(QCPLayerable)
+
+  friend class QCustomPlot;
+  friend class QCPAxisRect;
+};
+
+
+class QCP_LIB_DECL QCPRange
+{
+public:
+  double lower, upper;
+
+  QCPRange();
+  QCPRange(double lower, double upper);
+
+  bool operator==(const QCPRange& other) const { return lower == other.lower && upper == other.upper; }
+  bool operator!=(const QCPRange& other) const { return !(*this == other); }
+
+  QCPRange &operator+=(const double& value) { lower+=value; upper+=value; return *this; }
+  QCPRange &operator-=(const double& value) { lower-=value; upper-=value; return *this; }
+  QCPRange &operator*=(const double& value) { lower*=value; upper*=value; return *this; }
+  QCPRange &operator/=(const double& value) { lower/=value; upper/=value; return *this; }
+  friend inline const QCPRange operator+(const QCPRange&, double);
+  friend inline const QCPRange operator+(double, const QCPRange&);
+  friend inline const QCPRange operator-(const QCPRange& range, double value);
+  friend inline const QCPRange operator*(const QCPRange& range, double value);
+  friend inline const QCPRange operator*(double value, const QCPRange& range);
+  friend inline const QCPRange operator/(const QCPRange& range, double value);
+
+  double size() const;
+  double center() const;
+  void normalize();
+  void expand(const QCPRange &otherRange);
+  QCPRange expanded(const QCPRange &otherRange) const;
+  QCPRange sanitizedForLogScale() const;
+  QCPRange sanitizedForLinScale() const;
+  bool contains(double value) const;
+
+  static bool validRange(double lower, double upper);
+  static bool validRange(const QCPRange &range);
+  static const double minRange; //1e-280;
+  static const double maxRange; //1e280;
+
+};
+Q_DECLARE_TYPEINFO(QCPRange, Q_MOVABLE_TYPE);
+
+/* documentation of inline functions */
+
+/*! \fn QCPRange &QCPRange::operator+=(const double& value)
+
+  Adds \a value to both boundaries of the range.
+*/
+
+/*! \fn QCPRange &QCPRange::operator-=(const double& value)
+
+  Subtracts \a value from both boundaries of the range.
+*/
+
+/*! \fn QCPRange &QCPRange::operator*=(const double& value)
+
+  Multiplies both boundaries of the range by \a value.
+*/
+
+/*! \fn QCPRange &QCPRange::operator/=(const double& value)
+
+  Divides both boundaries of the range by \a value.
+*/
+
+/* end documentation of inline functions */
+
+/*!
+  Adds \a value to both boundaries of the range.
+*/
+inline const QCPRange operator+(const QCPRange& range, double value)
+{
+  QCPRange result(range);
+  result += value;
+  return result;
+}
+
+/*!
+  Adds \a value to both boundaries of the range.
+*/
+inline const QCPRange operator+(double value, const QCPRange& range)
+{
+  QCPRange result(range);
+  result += value;
+  return result;
+}
+
+/*!
+  Subtracts \a value from both boundaries of the range.
+*/
+inline const QCPRange operator-(const QCPRange& range, double value)
+{
+  QCPRange result(range);
+  result -= value;
+  return result;
+}
+
+/*!
+  Multiplies both boundaries of the range by \a value.
+*/
+inline const QCPRange operator*(const QCPRange& range, double value)
+{
+  QCPRange result(range);
+  result *= value;
+  return result;
+}
+
+/*!
+  Multiplies both boundaries of the range by \a value.
+*/
+inline const QCPRange operator*(double value, const QCPRange& range)
+{
+  QCPRange result(range);
+  result *= value;
+  return result;
+}
+
+/*!
+  Divides both boundaries of the range by \a value.
+*/
+inline const QCPRange operator/(const QCPRange& range, double value)
+{
+  QCPRange result(range);
+  result /= value;
+  return result;
+}
+
+
+class QCP_LIB_DECL QCPMarginGroup : public QObject
+{
+  Q_OBJECT
+public:
+  QCPMarginGroup(QCustomPlot *parentPlot);
+  ~QCPMarginGroup();
+
+  // non-virtual methods:
+  QList<QCPLayoutElement*> elements(QCP::MarginSide side) const { return mChildren.value(side); }
+  bool isEmpty() const;
+  void clear();
+
+protected:
+  // non-property members:
+  QCustomPlot *mParentPlot;
+  QHash<QCP::MarginSide, QList<QCPLayoutElement*> > mChildren;
+
+  // non-virtual methods:
+  int commonMargin(QCP::MarginSide side) const;
+  void addChild(QCP::MarginSide side, QCPLayoutElement *element);
+  void removeChild(QCP::MarginSide side, QCPLayoutElement *element);
+
+private:
+  Q_DISABLE_COPY(QCPMarginGroup)
+
+  friend class QCPLayoutElement;
+};
+
+
+class QCP_LIB_DECL QCPLayoutElement : public QCPLayerable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QCPLayout* layout READ layout)
+  Q_PROPERTY(QRect rect READ rect)
+  Q_PROPERTY(QRect outerRect READ outerRect WRITE setOuterRect)
+  Q_PROPERTY(QMargins margins READ margins WRITE setMargins)
+  Q_PROPERTY(QMargins minimumMargins READ minimumMargins WRITE setMinimumMargins)
+  Q_PROPERTY(QSize minimumSize READ minimumSize WRITE setMinimumSize)
+  Q_PROPERTY(QSize maximumSize READ maximumSize WRITE setMaximumSize)
+  /// \endcond
+public:
+  /*!
+    Defines the phases of the update process, that happens just before a replot. At each phase,
+    \ref update is called with the according UpdatePhase value.
+  */
+  enum UpdatePhase { upPreparation ///< Phase used for any type of preparation that needs to be done before margin calculation and layout
+                     ,upMargins    ///< Phase in which the margins are calculated and set
+                     ,upLayout     ///< Final phase in which the layout system places the rects of the elements
+                   };
+  Q_ENUMS(UpdatePhase)
+
+  explicit QCPLayoutElement(QCustomPlot *parentPlot=0);
+  virtual ~QCPLayoutElement();
+
+  // getters:
+  QCPLayout *layout() const { return mParentLayout; }
+  QRect rect() const { return mRect; }
+  QRect outerRect() const { return mOuterRect; }
+  QMargins margins() const { return mMargins; }
+  QMargins minimumMargins() const { return mMinimumMargins; }
+  QCP::MarginSides autoMargins() const { return mAutoMargins; }
+  QSize minimumSize() const { return mMinimumSize; }
+  QSize maximumSize() const { return mMaximumSize; }
+  QCPMarginGroup *marginGroup(QCP::MarginSide side) const { return mMarginGroups.value(side, (QCPMarginGroup*)0); }
+  QHash<QCP::MarginSide, QCPMarginGroup*> marginGroups() const { return mMarginGroups; }
+
+  // setters:
+  void setOuterRect(const QRect &rect);
+  void setMargins(const QMargins &margins);
+  void setMinimumMargins(const QMargins &margins);
+  void setAutoMargins(QCP::MarginSides sides);
+  void setMinimumSize(const QSize &size);
+  void setMinimumSize(int width, int height);
+  void setMaximumSize(const QSize &size);
+  void setMaximumSize(int width, int height);
+  void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group);
+
+  // introduced virtual methods:
+  virtual void update(UpdatePhase phase);
+  virtual QSize minimumSizeHint() const;
+  virtual QSize maximumSizeHint() const;
+  virtual QList<QCPLayoutElement*> elements(bool recursive) const;
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+protected:
+  // property members:
+  QCPLayout *mParentLayout;
+  QSize mMinimumSize, mMaximumSize;
+  QRect mRect, mOuterRect;
+  QMargins mMargins, mMinimumMargins;
+  QCP::MarginSides mAutoMargins;
+  QHash<QCP::MarginSide, QCPMarginGroup*> mMarginGroups;
+
+  // introduced virtual methods:
+  virtual int calculateAutoMargin(QCP::MarginSide side);
+  // events:
+  virtual void mousePressEvent(QMouseEvent *event) {Q_UNUSED(event)}
+  virtual void mouseMoveEvent(QMouseEvent *event) {Q_UNUSED(event)}
+  virtual void mouseReleaseEvent(QMouseEvent *event) {Q_UNUSED(event)}
+  virtual void mouseDoubleClickEvent(QMouseEvent *event) {Q_UNUSED(event)}
+  virtual void wheelEvent(QWheelEvent *event) {Q_UNUSED(event)}
+
+  // reimplemented virtual methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const { Q_UNUSED(painter) }
+  virtual void draw(QCPPainter *painter) { Q_UNUSED(painter) }
+  virtual void parentPlotInitialized(QCustomPlot *parentPlot);
+
+private:
+  Q_DISABLE_COPY(QCPLayoutElement)
+
+  friend class QCustomPlot;
+  friend class QCPLayout;
+  friend class QCPMarginGroup;
+};
+
+
+class QCP_LIB_DECL QCPLayout : public QCPLayoutElement
+{
+  Q_OBJECT
+public:
+  explicit QCPLayout();
+
+  // reimplemented virtual methods:
+  virtual void update(UpdatePhase phase);
+  virtual QList<QCPLayoutElement*> elements(bool recursive) const;
+
+  // introduced virtual methods:
+  virtual int elementCount() const = 0;
+  virtual QCPLayoutElement* elementAt(int index) const = 0;
+  virtual QCPLayoutElement* takeAt(int index) = 0;
+  virtual bool take(QCPLayoutElement* element) = 0;
+  virtual void simplify();
+
+  // non-virtual methods:
+  bool removeAt(int index);
+  bool remove(QCPLayoutElement* element);
+  void clear();
+
+protected:
+  // introduced virtual methods:
+  virtual void updateLayout();
+
+  // non-virtual methods:
+  void sizeConstraintsChanged() const;
+  void adoptElement(QCPLayoutElement *el);
+  void releaseElement(QCPLayoutElement *el);
+  QVector<int> getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const;
+
+private:
+  Q_DISABLE_COPY(QCPLayout)
+  friend class QCPLayoutElement;
+};
+
+
+class QCP_LIB_DECL QCPLayoutGrid : public QCPLayout
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(int rowCount READ rowCount)
+  Q_PROPERTY(int columnCount READ columnCount)
+  Q_PROPERTY(QList<double> columnStretchFactors READ columnStretchFactors WRITE setColumnStretchFactors)
+  Q_PROPERTY(QList<double> rowStretchFactors READ rowStretchFactors WRITE setRowStretchFactors)
+  Q_PROPERTY(int columnSpacing READ columnSpacing WRITE setColumnSpacing)
+  Q_PROPERTY(int rowSpacing READ rowSpacing WRITE setRowSpacing)
+  /// \endcond
+public:
+  explicit QCPLayoutGrid();
+  virtual ~QCPLayoutGrid();
+
+  // getters:
+  int rowCount() const;
+  int columnCount() const;
+  QList<double> columnStretchFactors() const { return mColumnStretchFactors; }
+  QList<double> rowStretchFactors() const { return mRowStretchFactors; }
+  int columnSpacing() const { return mColumnSpacing; }
+  int rowSpacing() const { return mRowSpacing; }
+
+  // setters:
+  void setColumnStretchFactor(int column, double factor);
+  void setColumnStretchFactors(const QList<double> &factors);
+  void setRowStretchFactor(int row, double factor);
+  void setRowStretchFactors(const QList<double> &factors);
+  void setColumnSpacing(int pixels);
+  void setRowSpacing(int pixels);
+
+  // reimplemented virtual methods:
+  virtual void updateLayout();
+  virtual int elementCount() const;
+  virtual QCPLayoutElement* elementAt(int index) const;
+  virtual QCPLayoutElement* takeAt(int index);
+  virtual bool take(QCPLayoutElement* element);
+  virtual QList<QCPLayoutElement*> elements(bool recursive) const;
+  virtual void simplify();
+  virtual QSize minimumSizeHint() const;
+  virtual QSize maximumSizeHint() const;
+
+  // non-virtual methods:
+  QCPLayoutElement *element(int row, int column) const;
+  bool addElement(int row, int column, QCPLayoutElement *element);
+  bool hasElement(int row, int column);
+  void expandTo(int newRowCount, int newColumnCount);
+  void insertRow(int newIndex);
+  void insertColumn(int newIndex);
+
+protected:
+  // property members:
+  QList<QList<QCPLayoutElement*> > mElements;
+  QList<double> mColumnStretchFactors;
+  QList<double> mRowStretchFactors;
+  int mColumnSpacing, mRowSpacing;
+
+  // non-virtual methods:
+  void getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const;
+  void getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const;
+
+private:
+  Q_DISABLE_COPY(QCPLayoutGrid)
+};
+
+
+class QCP_LIB_DECL QCPLayoutInset : public QCPLayout
+{
+  Q_OBJECT
+public:
+  /*!
+    Defines how the placement and sizing is handled for a certain element in a QCPLayoutInset.
+  */
+  enum InsetPlacement { ipFree            ///< The element may be positioned/sized arbitrarily, see \ref setInsetRect
+                        ,ipBorderAligned  ///< The element is aligned to one of the layout sides, see \ref setInsetAlignment
+                      };
+
+  explicit QCPLayoutInset();
+  virtual ~QCPLayoutInset();
+
+  // getters:
+  InsetPlacement insetPlacement(int index) const;
+  Qt::Alignment insetAlignment(int index) const;
+  QRectF insetRect(int index) const;
+
+  // setters:
+  void setInsetPlacement(int index, InsetPlacement placement);
+  void setInsetAlignment(int index, Qt::Alignment alignment);
+  void setInsetRect(int index, const QRectF &rect);
+
+  // reimplemented virtual methods:
+  virtual void updateLayout();
+  virtual int elementCount() const;
+  virtual QCPLayoutElement* elementAt(int index) const;
+  virtual QCPLayoutElement* takeAt(int index);
+  virtual bool take(QCPLayoutElement* element);
+  virtual void simplify() {}
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  // non-virtual methods:
+  void addElement(QCPLayoutElement *element, Qt::Alignment alignment);
+  void addElement(QCPLayoutElement *element, const QRectF &rect);
+
+protected:
+  // property members:
+  QList<QCPLayoutElement*> mElements;
+  QList<InsetPlacement> mInsetPlacement;
+  QList<Qt::Alignment> mInsetAlignment;
+  QList<QRectF> mInsetRect;
+
+private:
+  Q_DISABLE_COPY(QCPLayoutInset)
+};
+
+
+class QCP_LIB_DECL QCPLineEnding
+{
+  Q_GADGET
+public:
+  /*!
+    Defines the type of ending decoration for line-like items, e.g. an arrow.
+
+    \image html QCPLineEnding.png
+
+    The width and length of these decorations can be controlled with the functions \ref setWidth
+    and \ref setLength. Some decorations like \ref esDisc, \ref esSquare, \ref esDiamond and \ref esBar only
+    support a width, the length property is ignored.
+
+    \see QCPItemLine::setHead, QCPItemLine::setTail, QCPItemCurve::setHead, QCPItemCurve::setTail, QCPAxis::setLowerEnding, QCPAxis::setUpperEnding
+  */
+  Q_ENUMS(EndingStyle)
+  enum EndingStyle { esNone          ///< No ending decoration
+                     ,esFlatArrow    ///< A filled arrow head with a straight/flat back (a triangle)
+                     ,esSpikeArrow   ///< A filled arrow head with an indented back
+                     ,esLineArrow    ///< A non-filled arrow head with open back
+                     ,esDisc         ///< A filled circle
+                     ,esSquare       ///< A filled square
+                     ,esDiamond      ///< A filled diamond (45° rotated square)
+                     ,esBar          ///< A bar perpendicular to the line
+                     ,esHalfBar      ///< A bar perpendicular to the line, pointing out to only one side (to which side can be changed with \ref setInverted)
+                     ,esSkewedBar    ///< A bar that is skewed (skew controllable via \ref setLength)
+                   };
+
+  QCPLineEnding();
+  QCPLineEnding(EndingStyle style, double width=8, double length=10, bool inverted=false);
+
+  // getters:
+  EndingStyle style() const { return mStyle; }
+  double width() const { return mWidth; }
+  double length() const { return mLength; }
+  bool inverted() const { return mInverted; }
+
+  // setters:
+  void setStyle(EndingStyle style);
+  void setWidth(double width);
+  void setLength(double length);
+  void setInverted(bool inverted);
+
+  // non-property methods:
+  double boundingDistance() const;
+  double realLength() const;
+  void draw(QCPPainter *painter, const QVector2D &pos, const QVector2D &dir) const;
+  void draw(QCPPainter *painter, const QVector2D &pos, double angle) const;
+
+protected:
+  // property members:
+  EndingStyle mStyle;
+  double mWidth, mLength;
+  bool mInverted;
+};
+Q_DECLARE_TYPEINFO(QCPLineEnding, Q_MOVABLE_TYPE);
+
+
+class QCP_LIB_DECL QCPGrid :public QCPLayerable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(bool subGridVisible READ subGridVisible WRITE setSubGridVisible)
+  Q_PROPERTY(bool antialiasedSubGrid READ antialiasedSubGrid WRITE setAntialiasedSubGrid)
+  Q_PROPERTY(bool antialiasedZeroLine READ antialiasedZeroLine WRITE setAntialiasedZeroLine)
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen subGridPen READ subGridPen WRITE setSubGridPen)
+  Q_PROPERTY(QPen zeroLinePen READ zeroLinePen WRITE setZeroLinePen)
+  /// \endcond
+public:
+  QCPGrid(QCPAxis *parentAxis);
+
+  // getters:
+  bool subGridVisible() const { return mSubGridVisible; }
+  bool antialiasedSubGrid() const { return mAntialiasedSubGrid; }
+  bool antialiasedZeroLine() const { return mAntialiasedZeroLine; }
+  QPen pen() const { return mPen; }
+  QPen subGridPen() const { return mSubGridPen; }
+  QPen zeroLinePen() const { return mZeroLinePen; }
+
+  // setters:
+  void setSubGridVisible(bool visible);
+  void setAntialiasedSubGrid(bool enabled);
+  void setAntialiasedZeroLine(bool enabled);
+  void setPen(const QPen &pen);
+  void setSubGridPen(const QPen &pen);
+  void setZeroLinePen(const QPen &pen);
+
+protected:
+  // property members:
+  bool mSubGridVisible;
+  bool mAntialiasedSubGrid, mAntialiasedZeroLine;
+  QPen mPen, mSubGridPen, mZeroLinePen;
+  // non-property members:
+  QCPAxis *mParentAxis;
+
+  // reimplemented virtual methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter);
+
+  // non-virtual methods:
+  void drawGridLines(QCPPainter *painter) const;
+  void drawSubGridLines(QCPPainter *painter) const;
+
+  friend class QCPAxis;
+};
+
+
+class QCP_LIB_DECL QCPAxis : public QCPLayerable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(AxisType axisType READ axisType)
+  Q_PROPERTY(QCPAxisRect* axisRect READ axisRect)
+  Q_PROPERTY(ScaleType scaleType READ scaleType WRITE setScaleType NOTIFY scaleTypeChanged)
+  Q_PROPERTY(double scaleLogBase READ scaleLogBase WRITE setScaleLogBase)
+  Q_PROPERTY(QCPRange range READ range WRITE setRange NOTIFY rangeChanged)
+  Q_PROPERTY(bool rangeReversed READ rangeReversed WRITE setRangeReversed)
+  Q_PROPERTY(bool autoTicks READ autoTicks WRITE setAutoTicks)
+  Q_PROPERTY(int autoTickCount READ autoTickCount WRITE setAutoTickCount)
+  Q_PROPERTY(bool autoTickLabels READ autoTickLabels WRITE setAutoTickLabels)
+  Q_PROPERTY(bool autoTickStep READ autoTickStep WRITE setAutoTickStep)
+  Q_PROPERTY(bool autoSubTicks READ autoSubTicks WRITE setAutoSubTicks)
+  Q_PROPERTY(bool ticks READ ticks WRITE setTicks)
+  Q_PROPERTY(bool tickLabels READ tickLabels WRITE setTickLabels)
+  Q_PROPERTY(int tickLabelPadding READ tickLabelPadding WRITE setTickLabelPadding)
+  Q_PROPERTY(LabelType tickLabelType READ tickLabelType WRITE setTickLabelType)
+  Q_PROPERTY(QFont tickLabelFont READ tickLabelFont WRITE setTickLabelFont)
+  Q_PROPERTY(QColor tickLabelColor READ tickLabelColor WRITE setTickLabelColor)
+  Q_PROPERTY(double tickLabelRotation READ tickLabelRotation WRITE setTickLabelRotation)
+  Q_PROPERTY(LabelSide tickLabelSide READ tickLabelSide WRITE setTickLabelSide)
+  Q_PROPERTY(QString dateTimeFormat READ dateTimeFormat WRITE setDateTimeFormat)
+  Q_PROPERTY(Qt::TimeSpec dateTimeSpec READ dateTimeSpec WRITE setDateTimeSpec)
+  Q_PROPERTY(QString numberFormat READ numberFormat WRITE setNumberFormat)
+  Q_PROPERTY(int numberPrecision READ numberPrecision WRITE setNumberPrecision)
+  Q_PROPERTY(double tickStep READ tickStep WRITE setTickStep)
+  Q_PROPERTY(QVector<double> tickVector READ tickVector WRITE setTickVector)
+  Q_PROPERTY(QVector<QString> tickVectorLabels READ tickVectorLabels WRITE setTickVectorLabels)
+  Q_PROPERTY(int tickLengthIn READ tickLengthIn WRITE setTickLengthIn)
+  Q_PROPERTY(int tickLengthOut READ tickLengthOut WRITE setTickLengthOut)
+  Q_PROPERTY(int subTickCount READ subTickCount WRITE setSubTickCount)
+  Q_PROPERTY(int subTickLengthIn READ subTickLengthIn WRITE setSubTickLengthIn)
+  Q_PROPERTY(int subTickLengthOut READ subTickLengthOut WRITE setSubTickLengthOut)
+  Q_PROPERTY(QPen basePen READ basePen WRITE setBasePen)
+  Q_PROPERTY(QPen tickPen READ tickPen WRITE setTickPen)
+  Q_PROPERTY(QPen subTickPen READ subTickPen WRITE setSubTickPen)
+  Q_PROPERTY(QFont labelFont READ labelFont WRITE setLabelFont)
+  Q_PROPERTY(QColor labelColor READ labelColor WRITE setLabelColor)
+  Q_PROPERTY(QString label READ label WRITE setLabel)
+  Q_PROPERTY(int labelPadding READ labelPadding WRITE setLabelPadding)
+  Q_PROPERTY(int padding READ padding WRITE setPadding)
+  Q_PROPERTY(int offset READ offset WRITE setOffset)
+  Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectionChanged)
+  Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectableChanged)
+  Q_PROPERTY(QFont selectedTickLabelFont READ selectedTickLabelFont WRITE setSelectedTickLabelFont)
+  Q_PROPERTY(QFont selectedLabelFont READ selectedLabelFont WRITE setSelectedLabelFont)
+  Q_PROPERTY(QColor selectedTickLabelColor READ selectedTickLabelColor WRITE setSelectedTickLabelColor)
+  Q_PROPERTY(QColor selectedLabelColor READ selectedLabelColor WRITE setSelectedLabelColor)
+  Q_PROPERTY(QPen selectedBasePen READ selectedBasePen WRITE setSelectedBasePen)
+  Q_PROPERTY(QPen selectedTickPen READ selectedTickPen WRITE setSelectedTickPen)
+  Q_PROPERTY(QPen selectedSubTickPen READ selectedSubTickPen WRITE setSelectedSubTickPen)
+  Q_PROPERTY(QCPLineEnding lowerEnding READ lowerEnding WRITE setLowerEnding)
+  Q_PROPERTY(QCPLineEnding upperEnding READ upperEnding WRITE setUpperEnding)
+  Q_PROPERTY(QCPGrid* grid READ grid)
+  /// \endcond
+public:
+  /*!
+    Defines at which side of the axis rect the axis will appear. This also affects how the tick
+    marks are drawn, on which side the labels are placed etc.
+  */
+  enum AxisType { atLeft    = 0x01  ///< <tt>0x01</tt> Axis is vertical and on the left side of the axis rect
+                  ,atRight  = 0x02  ///< <tt>0x02</tt> Axis is vertical and on the right side of the axis rect
+                  ,atTop    = 0x04  ///< <tt>0x04</tt> Axis is horizontal and on the top side of the axis rect
+                  ,atBottom = 0x08  ///< <tt>0x08</tt> Axis is horizontal and on the bottom side of the axis rect
+                };
+  Q_FLAGS(AxisType AxisTypes)
+  Q_DECLARE_FLAGS(AxisTypes, AxisType)
+  /*!
+    When automatic tick label generation is enabled (\ref setAutoTickLabels), defines how the
+    coordinate of the tick is interpreted, i.e. translated into a string.
+
+    \see setTickLabelType
+  */
+  enum LabelType { ltNumber    ///< Tick coordinate is regarded as normal number and will be displayed as such. (see \ref setNumberFormat)
+                   ,ltDateTime ///< Tick coordinate is regarded as a date/time (seconds since 1970-01-01T00:00:00 UTC) and will be displayed and formatted as such. (for details, see \ref setDateTimeFormat)
+                 };
+  Q_ENUMS(LabelType)
+  /*!
+    Defines on which side of the axis the tick labels (numbers) shall appear.
+
+    \see setTickLabelSide
+  */
+  enum LabelSide { lsInside    ///< Tick labels will be displayed inside the axis rect and clipped to the inner axis rect
+                   ,lsOutside  ///< Tick labels will be displayed outside the axis rect
+                 };
+  Q_ENUMS(LabelSide)
+  /*!
+    Defines the scale of an axis.
+    \see setScaleType
+  */
+  enum ScaleType { stLinear       ///< Linear scaling
+                   ,stLogarithmic ///< Logarithmic scaling with correspondingly transformed plots and (major) tick marks at every base power (see \ref setScaleLogBase).
+                 };
+  Q_ENUMS(ScaleType)
+  /*!
+    Defines the selectable parts of an axis.
+    \see setSelectableParts, setSelectedParts
+  */
+  enum SelectablePart { spNone        = 0      ///< None of the selectable parts
+                        ,spAxis       = 0x001  ///< The axis backbone and tick marks
+                        ,spTickLabels = 0x002  ///< Tick labels (numbers) of this axis (as a whole, not individually)
+                        ,spAxisLabel  = 0x004  ///< The axis label
+                      };
+  Q_FLAGS(SelectablePart SelectableParts)
+  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
+
+  explicit QCPAxis(QCPAxisRect *parent, AxisType type);
+  virtual ~QCPAxis();
+
+  // getters:
+  AxisType axisType() const { return mAxisType; }
+  QCPAxisRect *axisRect() const { return mAxisRect; }
+  ScaleType scaleType() const { return mScaleType; }
+  double scaleLogBase() const { return mScaleLogBase; }
+  const QCPRange range() const { return mRange; }
+  bool rangeReversed() const { return mRangeReversed; }
+  bool autoTicks() const { return mAutoTicks; }
+  int autoTickCount() const { return mAutoTickCount; }
+  bool autoTickLabels() const { return mAutoTickLabels; }
+  bool autoTickStep() const { return mAutoTickStep; }
+  bool autoSubTicks() const { return mAutoSubTicks; }
+  bool ticks() const { return mTicks; }
+  bool tickLabels() const { return mTickLabels; }
+  int tickLabelPadding() const;
+  LabelType tickLabelType() const { return mTickLabelType; }
+  QFont tickLabelFont() const { return mTickLabelFont; }
+  QColor tickLabelColor() const { return mTickLabelColor; }
+  double tickLabelRotation() const;
+  LabelSide tickLabelSide() const;
+  QString dateTimeFormat() const { return mDateTimeFormat; }
+  Qt::TimeSpec dateTimeSpec() const { return mDateTimeSpec; }
+  QString numberFormat() const;
+  int numberPrecision() const { return mNumberPrecision; }
+  double tickStep() const { return mTickStep; }
+  QVector<double> tickVector() const { return mTickVector; }
+  QVector<QString> tickVectorLabels() const { return mTickVectorLabels; }
+  int tickLengthIn() const;
+  int tickLengthOut() const;
+  int subTickCount() const { return mSubTickCount; }
+  int subTickLengthIn() const;
+  int subTickLengthOut() const;
+  QPen basePen() const { return mBasePen; }
+  QPen tickPen() const { return mTickPen; }
+  QPen subTickPen() const { return mSubTickPen; }
+  QFont labelFont() const { return mLabelFont; }
+  QColor labelColor() const { return mLabelColor; }
+  QString label() const { return mLabel; }
+  int labelPadding() const;
+  int padding() const { return mPadding; }
+  int offset() const;
+  SelectableParts selectedParts() const { return mSelectedParts; }
+  SelectableParts selectableParts() const { return mSelectableParts; }
+  QFont selectedTickLabelFont() const { return mSelectedTickLabelFont; }
+  QFont selectedLabelFont() const { return mSelectedLabelFont; }
+  QColor selectedTickLabelColor() const { return mSelectedTickLabelColor; }
+  QColor selectedLabelColor() const { return mSelectedLabelColor; }
+  QPen selectedBasePen() const { return mSelectedBasePen; }
+  QPen selectedTickPen() const { return mSelectedTickPen; }
+  QPen selectedSubTickPen() const { return mSelectedSubTickPen; }
+  QCPLineEnding lowerEnding() const;
+  QCPLineEnding upperEnding() const;
+  QCPGrid *grid() const { return mGrid; }
+
+  // setters:
+  Q_SLOT void setScaleType(QCPAxis::ScaleType type);
+  void setScaleLogBase(double base);
+  Q_SLOT void setRange(const QCPRange &range);
+  void setRange(double lower, double upper);
+  void setRange(double position, double size, Qt::AlignmentFlag alignment);
+  void setRangeLower(double lower);
+  void setRangeUpper(double upper);
+  void setRangeReversed(bool reversed);
+  void setAutoTicks(bool on);
+  void setAutoTickCount(int approximateCount);
+  void setAutoTickLabels(bool on);
+  void setAutoTickStep(bool on);
+  void setAutoSubTicks(bool on);
+  void setTicks(bool show);
+  void setTickLabels(bool show);
+  void setTickLabelPadding(int padding);
+  void setTickLabelType(LabelType type);
+  void setTickLabelFont(const QFont &font);
+  void setTickLabelColor(const QColor &color);
+  void setTickLabelRotation(double degrees);
+  void setTickLabelSide(LabelSide side);
+  void setDateTimeFormat(const QString &format);
+  void setDateTimeSpec(const Qt::TimeSpec &timeSpec);
+  void setNumberFormat(const QString &formatCode);
+  void setNumberPrecision(int precision);
+  void setTickStep(double step);
+  void setTickVector(const QVector<double> &vec);
+  void setTickVectorLabels(const QVector<QString> &vec);
+  void setTickLength(int inside, int outside=0);
+  void setTickLengthIn(int inside);
+  void setTickLengthOut(int outside);
+  void setSubTickCount(int count);
+  void setSubTickLength(int inside, int outside=0);
+  void setSubTickLengthIn(int inside);
+  void setSubTickLengthOut(int outside);
+  void setBasePen(const QPen &pen);
+  void setTickPen(const QPen &pen);
+  void setSubTickPen(const QPen &pen);
+  void setLabelFont(const QFont &font);
+  void setLabelColor(const QColor &color);
+  void setLabel(const QString &str);
+  void setLabelPadding(int padding);
+  void setPadding(int padding);
+  void setOffset(int offset);
+  void setSelectedTickLabelFont(const QFont &font);
+  void setSelectedLabelFont(const QFont &font);
+  void setSelectedTickLabelColor(const QColor &color);
+  void setSelectedLabelColor(const QColor &color);
+  void setSelectedBasePen(const QPen &pen);
+  void setSelectedTickPen(const QPen &pen);
+  void setSelectedSubTickPen(const QPen &pen);
+  Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts);
+  Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts);
+  void setLowerEnding(const QCPLineEnding &ending);
+  void setUpperEnding(const QCPLineEnding &ending);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  // non-property methods:
+  Qt::Orientation orientation() const { return mOrientation; }
+  void moveRange(double diff);
+  void scaleRange(double factor, double center);
+  void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0);
+  void rescale(bool onlyVisiblePlottables=false);
+  double pixelToCoord(double value) const;
+  double coordToPixel(double value) const;
+  SelectablePart getPartAt(const QPointF &pos) const;
+  QList<QCPAbstractPlottable*> plottables() const;
+  QList<QCPGraph*> graphs() const;
+  QList<QCPAbstractItem*> items() const;
+
+  static AxisType marginSideToAxisType(QCP::MarginSide side);
+  static Qt::Orientation orientation(AxisType type) { return type==atBottom||type==atTop ? Qt::Horizontal : Qt::Vertical; }
+  static AxisType opposite(AxisType type);
+
+signals:
+  void ticksRequest();
+  void rangeChanged(const QCPRange &newRange);
+  void rangeChanged(const QCPRange &newRange, const QCPRange &oldRange);
+  void scaleTypeChanged(QCPAxis::ScaleType scaleType);
+  void selectionChanged(const QCPAxis::SelectableParts &parts);
+  void selectableChanged(const QCPAxis::SelectableParts &parts);
+
+protected:
+  // property members:
+  // axis base:
+  AxisType mAxisType;
+  QCPAxisRect *mAxisRect;
+  //int mOffset; // in QCPAxisPainter
+  int mPadding;
+  Qt::Orientation mOrientation;
+  SelectableParts mSelectableParts, mSelectedParts;
+  QPen mBasePen, mSelectedBasePen;
+  //QCPLineEnding mLowerEnding, mUpperEnding; // in QCPAxisPainter
+  // axis label:
+  //int mLabelPadding; // in QCPAxisPainter
+  QString mLabel;
+  QFont mLabelFont, mSelectedLabelFont;
+  QColor mLabelColor, mSelectedLabelColor;
+  // tick labels:
+  //int mTickLabelPadding; // in QCPAxisPainter
+  bool mTickLabels, mAutoTickLabels;
+  //double mTickLabelRotation; // in QCPAxisPainter
+  LabelType mTickLabelType;
+  QFont mTickLabelFont, mSelectedTickLabelFont;
+  QColor mTickLabelColor, mSelectedTickLabelColor;
+  QString mDateTimeFormat;
+  Qt::TimeSpec mDateTimeSpec;
+  int mNumberPrecision;
+  QLatin1Char mNumberFormatChar;
+  bool mNumberBeautifulPowers;
+  //bool mNumberMultiplyCross; // QCPAxisPainter
+  // ticks and subticks:
+  bool mTicks;
+  double mTickStep;
+  int mSubTickCount, mAutoTickCount;
+  bool mAutoTicks, mAutoTickStep, mAutoSubTicks;
+  //int mTickLengthIn, mTickLengthOut, mSubTickLengthIn, mSubTickLengthOut; // QCPAxisPainter
+  QPen mTickPen, mSelectedTickPen;
+  QPen mSubTickPen, mSelectedSubTickPen;
+  // scale and range:
+  QCPRange mRange;
+  bool mRangeReversed;
+  ScaleType mScaleType;
+  double mScaleLogBase, mScaleLogBaseLogInv;
+
+  // non-property members:
+  QCPGrid *mGrid;
+  QCPAxisPainterPrivate *mAxisPainter;
+  int mLowestVisibleTick, mHighestVisibleTick;
+  QVector<double> mTickVector;
+  QVector<QString> mTickVectorLabels;
+  QVector<double> mSubTickVector;
+  bool mCachedMarginValid;
+  int mCachedMargin;
+
+  // introduced virtual methods:
+  virtual void setupTickVectors();
+  virtual void generateAutoTicks();
+  virtual int calculateAutoSubTickCount(double tickStep) const;
+  virtual int calculateMargin();
+
+  // reimplemented virtual methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter);
+  virtual QCP::Interaction selectionCategory() const;
+  // events:
+  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
+  virtual void deselectEvent(bool *selectionStateChanged);
+
+  // non-virtual methods:
+  void visibleTickBounds(int &lowIndex, int &highIndex) const;
+  double baseLog(double value) const;
+  double basePow(double value) const;
+  QPen getBasePen() const;
+  QPen getTickPen() const;
+  QPen getSubTickPen() const;
+  QFont getTickLabelFont() const;
+  QFont getLabelFont() const;
+  QColor getTickLabelColor() const;
+  QColor getLabelColor() const;
+
+private:
+  Q_DISABLE_COPY(QCPAxis)
+
+  friend class QCustomPlot;
+  friend class QCPGrid;
+  friend class QCPAxisRect;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::SelectableParts)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCPAxis::AxisTypes)
+Q_DECLARE_METATYPE(QCPAxis::SelectablePart)
+
+
+class QCPAxisPainterPrivate
+{
+public:
+  explicit QCPAxisPainterPrivate(QCustomPlot *parentPlot);
+  virtual ~QCPAxisPainterPrivate();
+
+  virtual void draw(QCPPainter *painter);
+  virtual int size() const;
+  void clearCache();
+
+  QRect axisSelectionBox() const { return mAxisSelectionBox; }
+  QRect tickLabelsSelectionBox() const { return mTickLabelsSelectionBox; }
+  QRect labelSelectionBox() const { return mLabelSelectionBox; }
+
+  // public property members:
+  QCPAxis::AxisType type;
+  QPen basePen;
+  QCPLineEnding lowerEnding, upperEnding; // directly accessed by QCPAxis setters/getters
+  int labelPadding; // directly accessed by QCPAxis setters/getters
+  QFont labelFont;
+  QColor labelColor;
+  QString label;
+  int tickLabelPadding; // directly accessed by QCPAxis setters/getters
+  double tickLabelRotation; // directly accessed by QCPAxis setters/getters
+  QCPAxis::LabelSide tickLabelSide; // directly accessed by QCPAxis setters/getters
+  bool substituteExponent;
+  bool numberMultiplyCross; // directly accessed by QCPAxis setters/getters
+  int tickLengthIn, tickLengthOut, subTickLengthIn, subTickLengthOut; // directly accessed by QCPAxis setters/getters
+  QPen tickPen, subTickPen;
+  QFont tickLabelFont;
+  QColor tickLabelColor;
+  QRect axisRect, viewportRect;
+  double offset; // directly accessed by QCPAxis setters/getters
+  bool abbreviateDecimalPowers;
+  bool reversedEndings;
+
+  QVector<double> subTickPositions;
+  QVector<double> tickPositions;
+  QVector<QString> tickLabels;
+
+protected:
+  struct CachedLabel
+  {
+    QPointF offset;
+    QPixmap pixmap;
+  };
+  struct TickLabelData
+  {
+    QString basePart, expPart;
+    QRect baseBounds, expBounds, totalBounds, rotatedTotalBounds;
+    QFont baseFont, expFont;
+  };
+  QCustomPlot *mParentPlot;
+  QByteArray mLabelParameterHash; // to determine whether mLabelCache needs to be cleared due to changed parameters
+  QCache<QString, CachedLabel> mLabelCache;
+  QRect mAxisSelectionBox, mTickLabelsSelectionBox, mLabelSelectionBox;
+
+  virtual QByteArray generateLabelParameterHash() const;
+
+  virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize);
+  virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const;
+  virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const;
+  virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const;
+  virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const;
+};
+
+
+class QCP_LIB_DECL QCPAbstractPlottable : public QCPLayerable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QString name READ name WRITE setName)
+  Q_PROPERTY(bool antialiasedFill READ antialiasedFill WRITE setAntialiasedFill)
+  Q_PROPERTY(bool antialiasedScatters READ antialiasedScatters WRITE setAntialiasedScatters)
+  Q_PROPERTY(bool antialiasedErrorBars READ antialiasedErrorBars WRITE setAntialiasedErrorBars)
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
+  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
+  Q_PROPERTY(QCPAxis* keyAxis READ keyAxis WRITE setKeyAxis)
+  Q_PROPERTY(QCPAxis* valueAxis READ valueAxis WRITE setValueAxis)
+  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
+  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
+  /// \endcond
+public:
+  QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis);
+
+  // getters:
+  QString name() const { return mName; }
+  bool antialiasedFill() const { return mAntialiasedFill; }
+  bool antialiasedScatters() const { return mAntialiasedScatters; }
+  bool antialiasedErrorBars() const { return mAntialiasedErrorBars; }
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  QCPAxis *keyAxis() const { return mKeyAxis.data(); }
+  QCPAxis *valueAxis() const { return mValueAxis.data(); }
+  bool selectable() const { return mSelectable; }
+  bool selected() const { return mSelected; }
+
+  // setters:
+  void setName(const QString &name);
+  void setAntialiasedFill(bool enabled);
+  void setAntialiasedScatters(bool enabled);
+  void setAntialiasedErrorBars(bool enabled);
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+  void setKeyAxis(QCPAxis *axis);
+  void setValueAxis(QCPAxis *axis);
+  Q_SLOT void setSelectable(bool selectable);
+  Q_SLOT void setSelected(bool selected);
+
+  // introduced virtual methods:
+  virtual void clearData() = 0;
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0;
+  virtual bool addToLegend();
+  virtual bool removeFromLegend() const;
+
+  // non-property methods:
+  void rescaleAxes(bool onlyEnlarge=false) const;
+  void rescaleKeyAxis(bool onlyEnlarge=false) const;
+  void rescaleValueAxis(bool onlyEnlarge=false) const;
+
+signals:
+  void selectionChanged(bool selected);
+  void selectableChanged(bool selectable);
+
+protected:
+  /*!
+    Represents negative and positive sign domain for passing to \ref getKeyRange and \ref getValueRange.
+  */
+  enum SignDomain { sdNegative  ///< The negative sign domain, i.e. numbers smaller than zero
+                    ,sdBoth     ///< Both sign domains, including zero, i.e. all (rational) numbers
+                    ,sdPositive ///< The positive sign domain, i.e. numbers greater than zero
+                  };
+
+  // property members:
+  QString mName;
+  bool mAntialiasedFill, mAntialiasedScatters, mAntialiasedErrorBars;
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+  QPointer<QCPAxis> mKeyAxis, mValueAxis;
+  bool mSelectable, mSelected;
+
+  // reimplemented virtual methods:
+  virtual QRect clipRect() const;
+  virtual void draw(QCPPainter *painter) = 0;
+  virtual QCP::Interaction selectionCategory() const;
+  void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  // events:
+  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
+  virtual void deselectEvent(bool *selectionStateChanged);
+
+  // introduced virtual methods:
+  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const = 0;
+  virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0;
+  virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const = 0;
+
+  // non-virtual methods:
+  void coordsToPixels(double key, double value, double &x, double &y) const;
+  const QPointF coordsToPixels(double key, double value) const;
+  void pixelsToCoords(double x, double y, double &key, double &value) const;
+  void pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const;
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+  void applyFillAntialiasingHint(QCPPainter *painter) const;
+  void applyScattersAntialiasingHint(QCPPainter *painter) const;
+  void applyErrorBarsAntialiasingHint(QCPPainter *painter) const;
+  double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const;
+
+private:
+  Q_DISABLE_COPY(QCPAbstractPlottable)
+
+  friend class QCustomPlot;
+  friend class QCPAxis;
+  friend class QCPPlottableLegendItem;
+};
+
+
+class QCP_LIB_DECL QCPItemAnchor
+{
+public:
+  QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name, int anchorId=-1);
+  virtual ~QCPItemAnchor();
+
+  // getters:
+  QString name() const { return mName; }
+  virtual QPointF pixelPoint() const;
+
+protected:
+  // property members:
+  QString mName;
+
+  // non-property members:
+  QCustomPlot *mParentPlot;
+  QCPAbstractItem *mParentItem;
+  int mAnchorId;
+  QSet<QCPItemPosition*> mChildrenX, mChildrenY;
+
+  // introduced virtual methods:
+  virtual QCPItemPosition *toQCPItemPosition() { return 0; }
+
+  // non-virtual methods:
+  void addChildX(QCPItemPosition* pos); // called from pos when this anchor is set as parent
+  void removeChildX(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted
+  void addChildY(QCPItemPosition* pos); // called from pos when this anchor is set as parent
+  void removeChildY(QCPItemPosition *pos); // called from pos when its parent anchor is reset or pos deleted
+
+private:
+  Q_DISABLE_COPY(QCPItemAnchor)
+
+  friend class QCPItemPosition;
+};
+
+
+
+class QCP_LIB_DECL QCPItemPosition : public QCPItemAnchor
+{
+public:
+  /*!
+    Defines the ways an item position can be specified. Thus it defines what the numbers passed to
+    \ref setCoords actually mean.
+
+    \see setType
+  */
+  enum PositionType { ptAbsolute        ///< Static positioning in pixels, starting from the top left corner of the viewport/widget.
+                      ,ptViewportRatio  ///< Static positioning given by a fraction of the viewport size. For example, if you call setCoords(0, 0), the position will be at the top
+                                        ///< left corner of the viewport/widget. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and
+                                        ///< vertically at the top of the viewport/widget, etc.
+                      ,ptAxisRectRatio  ///< Static positioning given by a fraction of the axis rect size (see \ref setAxisRect). For example, if you call setCoords(0, 0), the position will be at the top
+                                        ///< left corner of the axis rect. setCoords(1, 1) will be at the bottom right corner, setCoords(0.5, 0) will be horizontally centered and
+                                        ///< vertically at the top of the axis rect, etc. You can also go beyond the axis rect by providing negative coordinates or coordinates larger than 1.
+                      ,ptPlotCoords     ///< Dynamic positioning at a plot coordinate defined by two axes (see \ref setAxes).
+                    };
+
+  QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString name);
+  virtual ~QCPItemPosition();
+
+  // getters:
+  PositionType type() const { return typeX(); }
+  PositionType typeX() const { return mPositionTypeX; }
+  PositionType typeY() const { return mPositionTypeY; }
+  QCPItemAnchor *parentAnchor() const { return parentAnchorX(); }
+  QCPItemAnchor *parentAnchorX() const { return mParentAnchorX; }
+  QCPItemAnchor *parentAnchorY() const { return mParentAnchorY; }
+  double key() const { return mKey; }
+  double value() const { return mValue; }
+  QPointF coords() const { return QPointF(mKey, mValue); }
+  QCPAxis *keyAxis() const { return mKeyAxis.data(); }
+  QCPAxis *valueAxis() const { return mValueAxis.data(); }
+  QCPAxisRect *axisRect() const;
+  virtual QPointF pixelPoint() const;
+
+  // setters:
+  void setType(PositionType type);
+  void setTypeX(PositionType type);
+  void setTypeY(PositionType type);
+  bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);
+  bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);
+  bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false);
+  void setCoords(double key, double value);
+  void setCoords(const QPointF &coords);
+  void setAxes(QCPAxis* keyAxis, QCPAxis* valueAxis);
+  void setAxisRect(QCPAxisRect *axisRect);
+  void setPixelPoint(const QPointF &pixelPoint);
+
+protected:
+  // property members:
+  PositionType mPositionTypeX, mPositionTypeY;
+  QPointer<QCPAxis> mKeyAxis, mValueAxis;
+  QPointer<QCPAxisRect> mAxisRect;
+  double mKey, mValue;
+  QCPItemAnchor *mParentAnchorX, *mParentAnchorY;
+
+  // reimplemented virtual methods:
+  virtual QCPItemPosition *toQCPItemPosition() { return this; }
+
+private:
+  Q_DISABLE_COPY(QCPItemPosition)
+
+};
+
+
+class QCP_LIB_DECL QCPAbstractItem : public QCPLayerable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(bool clipToAxisRect READ clipToAxisRect WRITE setClipToAxisRect)
+  Q_PROPERTY(QCPAxisRect* clipAxisRect READ clipAxisRect WRITE setClipAxisRect)
+  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
+  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
+  /// \endcond
+public:
+  QCPAbstractItem(QCustomPlot *parentPlot);
+  virtual ~QCPAbstractItem();
+
+  // getters:
+  bool clipToAxisRect() const { return mClipToAxisRect; }
+  QCPAxisRect *clipAxisRect() const;
+  bool selectable() const { return mSelectable; }
+  bool selected() const { return mSelected; }
+
+  // setters:
+  void setClipToAxisRect(bool clip);
+  void setClipAxisRect(QCPAxisRect *rect);
+  Q_SLOT void setSelectable(bool selectable);
+  Q_SLOT void setSelected(bool selected);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const = 0;
+
+  // non-virtual methods:
+  QList<QCPItemPosition*> positions() const { return mPositions; }
+  QList<QCPItemAnchor*> anchors() const { return mAnchors; }
+  QCPItemPosition *position(const QString &name) const;
+  QCPItemAnchor *anchor(const QString &name) const;
+  bool hasAnchor(const QString &name) const;
+
+signals:
+  void selectionChanged(bool selected);
+  void selectableChanged(bool selectable);
+
+protected:
+  // property members:
+  bool mClipToAxisRect;
+  QPointer<QCPAxisRect> mClipAxisRect;
+  QList<QCPItemPosition*> mPositions;
+  QList<QCPItemAnchor*> mAnchors;
+  bool mSelectable, mSelected;
+
+  // reimplemented virtual methods:
+  virtual QCP::Interaction selectionCategory() const;
+  virtual QRect clipRect() const;
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter) = 0;
+  // events:
+  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
+  virtual void deselectEvent(bool *selectionStateChanged);
+
+  // introduced virtual methods:
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+
+  // non-virtual methods:
+  double distSqrToLine(const QPointF &start, const QPointF &end, const QPointF &point) const;
+  double rectSelectTest(const QRectF &rect, const QPointF &pos, bool filledRect) const;
+  QCPItemPosition *createPosition(const QString &name);
+  QCPItemAnchor *createAnchor(const QString &name, int anchorId);
+
+private:
+  Q_DISABLE_COPY(QCPAbstractItem)
+
+  friend class QCustomPlot;
+  friend class QCPItemAnchor;
+};
+
+
+class QCP_LIB_DECL QCustomPlot : public QWidget
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QRect viewport READ viewport WRITE setViewport)
+  Q_PROPERTY(QPixmap background READ background WRITE setBackground)
+  Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled)
+  Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode)
+  Q_PROPERTY(QCPLayoutGrid* plotLayout READ plotLayout)
+  Q_PROPERTY(bool autoAddPlottableToLegend READ autoAddPlottableToLegend WRITE setAutoAddPlottableToLegend)
+  Q_PROPERTY(int selectionTolerance READ selectionTolerance WRITE setSelectionTolerance)
+  Q_PROPERTY(bool noAntialiasingOnDrag READ noAntialiasingOnDrag WRITE setNoAntialiasingOnDrag)
+  Q_PROPERTY(Qt::KeyboardModifier multiSelectModifier READ multiSelectModifier WRITE setMultiSelectModifier)
+  /// \endcond
+public:
+  /*!
+    Defines how a layer should be inserted relative to an other layer.
+
+    \see addLayer, moveLayer
+  */
+  enum LayerInsertMode { limBelow  ///< Layer is inserted below other layer
+                         ,limAbove ///< Layer is inserted above other layer
+                       };
+  Q_ENUMS(LayerInsertMode)
+
+  /*!
+    Defines with what timing the QCustomPlot surface is refreshed after a replot.
+
+    \see replot
+  */
+  enum RefreshPriority { rpImmediate ///< The QCustomPlot surface is immediately refreshed, by calling QWidget::repaint() after the replot
+                         ,rpQueued   ///< Queues the refresh such that it is performed at a slightly delayed point in time after the replot, by calling QWidget::update() after the replot
+                         ,rpHint     ///< Whether to use immediate repaint or queued update depends on whether the plotting hint \ref QCP::phForceRepaint is set, see \ref setPlottingHints.
+                       };
+
+  explicit QCustomPlot(QWidget *parent = 0);
+  virtual ~QCustomPlot();
+
+  // getters:
+  QRect viewport() const { return mViewport; }
+  QPixmap background() const { return mBackgroundPixmap; }
+  bool backgroundScaled() const { return mBackgroundScaled; }
+  Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }
+  QCPLayoutGrid *plotLayout() const { return mPlotLayout; }
+  QCP::AntialiasedElements antialiasedElements() const { return mAntialiasedElements; }
+  QCP::AntialiasedElements notAntialiasedElements() const { return mNotAntialiasedElements; }
+  bool autoAddPlottableToLegend() const { return mAutoAddPlottableToLegend; }
+  const QCP::Interactions interactions() const { return mInteractions; }
+  int selectionTolerance() const { return mSelectionTolerance; }
+  bool noAntialiasingOnDrag() const { return mNoAntialiasingOnDrag; }
+  QCP::PlottingHints plottingHints() const { return mPlottingHints; }
+  Qt::KeyboardModifier multiSelectModifier() const { return mMultiSelectModifier; }
+
+  // setters:
+  void setViewport(const QRect &rect);
+  void setBackground(const QPixmap &pm);
+  void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);
+  void setBackground(const QBrush &brush);
+  void setBackgroundScaled(bool scaled);
+  void setBackgroundScaledMode(Qt::AspectRatioMode mode);
+  void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements);
+  void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true);
+  void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements);
+  void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true);
+  void setAutoAddPlottableToLegend(bool on);
+  void setInteractions(const QCP::Interactions &interactions);
+  void setInteraction(const QCP::Interaction &interaction, bool enabled=true);
+  void setSelectionTolerance(int pixels);
+  void setNoAntialiasingOnDrag(bool enabled);
+  void setPlottingHints(const QCP::PlottingHints &hints);
+  void setPlottingHint(QCP::PlottingHint hint, bool enabled=true);
+  void setMultiSelectModifier(Qt::KeyboardModifier modifier);
+
+  // non-property methods:
+  // plottable interface:
+  QCPAbstractPlottable *plottable(int index);
+  QCPAbstractPlottable *plottable();
+  bool addPlottable(QCPAbstractPlottable *plottable);
+  bool removePlottable(QCPAbstractPlottable *plottable);
+  bool removePlottable(int index);
+  int clearPlottables();
+  int plottableCount() const;
+  QList<QCPAbstractPlottable*> selectedPlottables() const;
+  QCPAbstractPlottable *plottableAt(const QPointF &pos, bool onlySelectable=false) const;
+  bool hasPlottable(QCPAbstractPlottable *plottable) const;
+
+  // specialized interface for QCPGraph:
+  QCPGraph *graph(int index) const;
+  QCPGraph *graph() const;
+  QCPGraph *addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0);
+  bool removeGraph(QCPGraph *graph);
+  bool removeGraph(int index);
+  int clearGraphs();
+  int graphCount() const;
+  QList<QCPGraph*> selectedGraphs() const;
+
+  // item interface:
+  QCPAbstractItem *item(int index) const;
+  QCPAbstractItem *item() const;
+  bool addItem(QCPAbstractItem* item);
+  bool removeItem(QCPAbstractItem *item);
+  bool removeItem(int index);
+  int clearItems();
+  int itemCount() const;
+  QList<QCPAbstractItem*> selectedItems() const;
+  QCPAbstractItem *itemAt(const QPointF &pos, bool onlySelectable=false) const;
+  bool hasItem(QCPAbstractItem *item) const;
+
+  // layer interface:
+  QCPLayer *layer(const QString &name) const;
+  QCPLayer *layer(int index) const;
+  QCPLayer *currentLayer() const;
+  bool setCurrentLayer(const QString &name);
+  bool setCurrentLayer(QCPLayer *layer);
+  int layerCount() const;
+  bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove);
+  bool removeLayer(QCPLayer *layer);
+  bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove);
+
+  // axis rect/layout interface:
+  int axisRectCount() const;
+  QCPAxisRect* axisRect(int index=0) const;
+  QList<QCPAxisRect*> axisRects() const;
+  QCPLayoutElement* layoutElementAt(const QPointF &pos) const;
+  Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false);
+
+  QList<QCPAxis*> selectedAxes() const;
+  QList<QCPLegend*> selectedLegends() const;
+  Q_SLOT void deselectAll();
+
+  bool savePdf(const QString &fileName, bool noCosmeticPen=false, int width=0, int height=0, const QString &pdfCreator=QString(), const QString &pdfTitle=QString());
+  bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
+  bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1);
+  bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0);
+  bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1);
+  QPixmap toPixmap(int width=0, int height=0, double scale=1.0);
+  void toPainter(QCPPainter *painter, int width=0, int height=0);
+  Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpHint);
+
+  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
+  QCPLegend *legend;
+
+signals:
+  void mouseDoubleClick(QMouseEvent *event);
+  void mousePress(QMouseEvent *event);
+  void mouseMove(QMouseEvent *event);
+  void mouseRelease(QMouseEvent *event);
+  void mouseWheel(QWheelEvent *event);
+
+  void plottableClick(QCPAbstractPlottable *plottable, QMouseEvent *event);
+  void plottableDoubleClick(QCPAbstractPlottable *plottable, QMouseEvent *event);
+  void itemClick(QCPAbstractItem *item, QMouseEvent *event);
+  void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event);
+  void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);
+  void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);
+  void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event);
+  void legendDoubleClick(QCPLegend *legend,  QCPAbstractLegendItem *item, QMouseEvent *event);
+  void titleClick(QMouseEvent *event, QCPPlotTitle *title);
+  void titleDoubleClick(QMouseEvent *event, QCPPlotTitle *title);
+
+  void selectionChangedByUser();
+  void beforeReplot();
+  void afterReplot();
+
+protected:
+  // property members:
+  QRect mViewport;
+  QCPLayoutGrid *mPlotLayout;
+  bool mAutoAddPlottableToLegend;
+  QList<QCPAbstractPlottable*> mPlottables;
+  QList<QCPGraph*> mGraphs; // extra list of plottables also in mPlottables that are of type QCPGraph
+  QList<QCPAbstractItem*> mItems;
+  QList<QCPLayer*> mLayers;
+  QCP::AntialiasedElements mAntialiasedElements, mNotAntialiasedElements;
+  QCP::Interactions mInteractions;
+  int mSelectionTolerance;
+  bool mNoAntialiasingOnDrag;
+  QBrush mBackgroundBrush;
+  QPixmap mBackgroundPixmap;
+  QPixmap mScaledBackgroundPixmap;
+  bool mBackgroundScaled;
+  Qt::AspectRatioMode mBackgroundScaledMode;
+  QCPLayer *mCurrentLayer;
+  QCP::PlottingHints mPlottingHints;
+  Qt::KeyboardModifier mMultiSelectModifier;
+
+  // non-property members:
+  QPixmap mPaintBuffer;
+  QPoint mMousePressPos;
+  QPointer<QCPLayoutElement> mMouseEventElement;
+  bool mReplotting;
+
+  // reimplemented virtual methods:
+  virtual QSize minimumSizeHint() const;
+  virtual QSize sizeHint() const;
+  virtual void paintEvent(QPaintEvent *event);
+  virtual void resizeEvent(QResizeEvent *event);
+  virtual void mouseDoubleClickEvent(QMouseEvent *event);
+  virtual void mousePressEvent(QMouseEvent *event);
+  virtual void mouseMoveEvent(QMouseEvent *event);
+  virtual void mouseReleaseEvent(QMouseEvent *event);
+  virtual void wheelEvent(QWheelEvent *event);
+
+  // introduced virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual void axisRemoved(QCPAxis *axis);
+  virtual void legendRemoved(QCPLegend *legend);
+
+  // non-virtual methods:
+  void updateLayerIndices() const;
+  QCPLayerable *layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const;
+  void drawBackground(QCPPainter *painter);
+
+  friend class QCPLegend;
+  friend class QCPAxis;
+  friend class QCPLayer;
+  friend class QCPAxisRect;
+};
+
+
+class QCP_LIB_DECL QCPColorGradient
+{
+  Q_GADGET
+public:
+  /*!
+    Defines the color spaces in which color interpolation between gradient stops can be performed.
+
+    \see setColorInterpolation
+  */
+  enum ColorInterpolation { ciRGB  ///< Color channels red, green and blue are linearly interpolated
+                            ,ciHSV ///< Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the shortest angle distance)
+                          };
+  Q_ENUMS(ColorInterpolation)
+
+  /*!
+    Defines the available presets that can be loaded with \ref loadPreset. See the documentation
+    there for an image of the presets.
+  */
+  enum GradientPreset { gpGrayscale  ///< Continuous lightness from black to white (suited for non-biased data representation)
+                        ,gpHot       ///< Continuous lightness from black over firey colors to white (suited for non-biased data representation)
+                        ,gpCold      ///< Continuous lightness from black over icey colors to white (suited for non-biased data representation)
+                        ,gpNight     ///< Continuous lightness from black over weak blueish colors to white (suited for non-biased data representation)
+                        ,gpCandy     ///< Blue over pink to white
+                        ,gpGeography ///< Colors suitable to represent different elevations on geographical maps
+                        ,gpIon       ///< Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allows more precise magnitude estimates)
+                        ,gpThermal   ///< Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white
+                        ,gpPolar     ///< Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle and red for positive values
+                        ,gpSpectrum  ///< An approximation of the visible light spectrum (creates banding illusion but allows more precise magnitude estimates)
+                        ,gpJet       ///< Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion but allows more precise magnitude estimates)
+                        ,gpHues      ///< Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and phases, see \ref setPeriodic)
+                      };
+  Q_ENUMS(GradientPreset)
+
+  QCPColorGradient(GradientPreset preset=gpCold);
+  bool operator==(const QCPColorGradient &other) const;
+  bool operator!=(const QCPColorGradient &other) const { return !(*this == other); }
+
+  // getters:
+  int levelCount() const { return mLevelCount; }
+  QMap<double, QColor> colorStops() const { return mColorStops; }
+  ColorInterpolation colorInterpolation() const { return mColorInterpolation; }
+  bool periodic() const { return mPeriodic; }
+
+  // setters:
+  void setLevelCount(int n);
+  void setColorStops(const QMap<double, QColor> &colorStops);
+  void setColorStopAt(double position, const QColor &color);
+  void setColorInterpolation(ColorInterpolation interpolation);
+  void setPeriodic(bool enabled);
+
+  // non-property methods:
+  void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false);
+  QRgb color(double position, const QCPRange &range, bool logarithmic=false);
+  void loadPreset(GradientPreset preset);
+  void clearColorStops();
+  QCPColorGradient inverted() const;
+
+protected:
+  void updateColorBuffer();
+
+  // property members:
+  int mLevelCount;
+  QMap<double, QColor> mColorStops;
+  ColorInterpolation mColorInterpolation;
+  bool mPeriodic;
+
+  // non-property members:
+  QVector<QRgb> mColorBuffer;
+  bool mColorBufferInvalidated;
+};
+
+
+class QCP_LIB_DECL QCPAxisRect : public QCPLayoutElement
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPixmap background READ background WRITE setBackground)
+  Q_PROPERTY(bool backgroundScaled READ backgroundScaled WRITE setBackgroundScaled)
+  Q_PROPERTY(Qt::AspectRatioMode backgroundScaledMode READ backgroundScaledMode WRITE setBackgroundScaledMode)
+  Q_PROPERTY(Qt::Orientations rangeDrag READ rangeDrag WRITE setRangeDrag)
+  Q_PROPERTY(Qt::Orientations rangeZoom READ rangeZoom WRITE setRangeZoom)
+  /// \endcond
+public:
+  explicit QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true);
+  virtual ~QCPAxisRect();
+
+  // getters:
+  QPixmap background() const { return mBackgroundPixmap; }
+  bool backgroundScaled() const { return mBackgroundScaled; }
+  Qt::AspectRatioMode backgroundScaledMode() const { return mBackgroundScaledMode; }
+  Qt::Orientations rangeDrag() const { return mRangeDrag; }
+  Qt::Orientations rangeZoom() const { return mRangeZoom; }
+  QCPAxis *rangeDragAxis(Qt::Orientation orientation);
+  QCPAxis *rangeZoomAxis(Qt::Orientation orientation);
+  double rangeZoomFactor(Qt::Orientation orientation);
+
+  // setters:
+  void setBackground(const QPixmap &pm);
+  void setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode=Qt::KeepAspectRatioByExpanding);
+  void setBackground(const QBrush &brush);
+  void setBackgroundScaled(bool scaled);
+  void setBackgroundScaledMode(Qt::AspectRatioMode mode);
+  void setRangeDrag(Qt::Orientations orientations);
+  void setRangeZoom(Qt::Orientations orientations);
+  void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical);
+  void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical);
+  void setRangeZoomFactor(double horizontalFactor, double verticalFactor);
+  void setRangeZoomFactor(double factor);
+
+  // non-property methods:
+  int axisCount(QCPAxis::AxisType type) const;
+  QCPAxis *axis(QCPAxis::AxisType type, int index=0) const;
+  QList<QCPAxis*> axes(QCPAxis::AxisTypes types) const;
+  QList<QCPAxis*> axes() const;
+  QCPAxis *addAxis(QCPAxis::AxisType type, QCPAxis *axis=0);
+  QList<QCPAxis*> addAxes(QCPAxis::AxisTypes types);
+  bool removeAxis(QCPAxis *axis);
+  QCPLayoutInset *insetLayout() const { return mInsetLayout; }
+
+  void setupFullAxesBox(bool connectRanges=false);
+  QList<QCPAbstractPlottable*> plottables() const;
+  QList<QCPGraph*> graphs() const;
+  QList<QCPAbstractItem*> items() const;
+
+  // read-only interface imitating a QRect:
+  int left() const { return mRect.left(); }
+  int right() const { return mRect.right(); }
+  int top() const { return mRect.top(); }
+  int bottom() const { return mRect.bottom(); }
+  int width() const { return mRect.width(); }
+  int height() const { return mRect.height(); }
+  QSize size() const { return mRect.size(); }
+  QPoint topLeft() const { return mRect.topLeft(); }
+  QPoint topRight() const { return mRect.topRight(); }
+  QPoint bottomLeft() const { return mRect.bottomLeft(); }
+  QPoint bottomRight() const { return mRect.bottomRight(); }
+  QPoint center() const { return mRect.center(); }
+
+  // reimplemented virtual methods:
+  virtual void update(UpdatePhase phase);
+  virtual QList<QCPLayoutElement*> elements(bool recursive) const;
+
+protected:
+  // property members:
+  QBrush mBackgroundBrush;
+  QPixmap mBackgroundPixmap;
+  QPixmap mScaledBackgroundPixmap;
+  bool mBackgroundScaled;
+  Qt::AspectRatioMode mBackgroundScaledMode;
+  QCPLayoutInset *mInsetLayout;
+  Qt::Orientations mRangeDrag, mRangeZoom;
+  QPointer<QCPAxis> mRangeDragHorzAxis, mRangeDragVertAxis, mRangeZoomHorzAxis, mRangeZoomVertAxis;
+  double mRangeZoomFactorHorz, mRangeZoomFactorVert;
+  // non-property members:
+  QCPRange mDragStartHorzRange, mDragStartVertRange;
+  QCP::AntialiasedElements mAADragBackup, mNotAADragBackup;
+  QPoint mDragStart;
+  bool mDragging;
+  QHash<QCPAxis::AxisType, QList<QCPAxis*> > mAxes;
+
+  // reimplemented virtual methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter);
+  virtual int calculateAutoMargin(QCP::MarginSide side);
+  // events:
+  virtual void mousePressEvent(QMouseEvent *event);
+  virtual void mouseMoveEvent(QMouseEvent *event);
+  virtual void mouseReleaseEvent(QMouseEvent *event);
+  virtual void wheelEvent(QWheelEvent *event);
+
+  // non-property methods:
+  void drawBackground(QCPPainter *painter);
+  void updateAxesOffset(QCPAxis::AxisType type);
+
+private:
+  Q_DISABLE_COPY(QCPAxisRect)
+
+  friend class QCustomPlot;
+};
+
+
+class QCP_LIB_DECL QCPAbstractLegendItem : public QCPLayoutElement
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QCPLegend* parentLegend READ parentLegend)
+  Q_PROPERTY(QFont font READ font WRITE setFont)
+  Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
+  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
+  Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
+  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectionChanged)
+  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectableChanged)
+  /// \endcond
+public:
+  explicit QCPAbstractLegendItem(QCPLegend *parent);
+
+  // getters:
+  QCPLegend *parentLegend() const { return mParentLegend; }
+  QFont font() const { return mFont; }
+  QColor textColor() const { return mTextColor; }
+  QFont selectedFont() const { return mSelectedFont; }
+  QColor selectedTextColor() const { return mSelectedTextColor; }
+  bool selectable() const { return mSelectable; }
+  bool selected() const { return mSelected; }
+
+  // setters:
+  void setFont(const QFont &font);
+  void setTextColor(const QColor &color);
+  void setSelectedFont(const QFont &font);
+  void setSelectedTextColor(const QColor &color);
+  Q_SLOT void setSelectable(bool selectable);
+  Q_SLOT void setSelected(bool selected);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+signals:
+  void selectionChanged(bool selected);
+  void selectableChanged(bool selectable);
+
+protected:
+  // property members:
+  QCPLegend *mParentLegend;
+  QFont mFont;
+  QColor mTextColor;
+  QFont mSelectedFont;
+  QColor mSelectedTextColor;
+  bool mSelectable, mSelected;
+
+  // reimplemented virtual methods:
+  virtual QCP::Interaction selectionCategory() const;
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual QRect clipRect() const;
+  virtual void draw(QCPPainter *painter) = 0;
+  // events:
+  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
+  virtual void deselectEvent(bool *selectionStateChanged);
+
+private:
+  Q_DISABLE_COPY(QCPAbstractLegendItem)
+
+  friend class QCPLegend;
+};
+
+
+class QCP_LIB_DECL QCPPlottableLegendItem : public QCPAbstractLegendItem
+{
+  Q_OBJECT
+public:
+  QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable);
+
+  // getters:
+  QCPAbstractPlottable *plottable() { return mPlottable; }
+
+protected:
+  // property members:
+  QCPAbstractPlottable *mPlottable;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual QSize minimumSizeHint() const;
+
+  // non-virtual methods:
+  QPen getIconBorderPen() const;
+  QColor getTextColor() const;
+  QFont getFont() const;
+};
+
+
+class QCP_LIB_DECL QCPLegend : public QCPLayoutGrid
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPen borderPen READ borderPen WRITE setBorderPen)
+  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
+  Q_PROPERTY(QFont font READ font WRITE setFont)
+  Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
+  Q_PROPERTY(QSize iconSize READ iconSize WRITE setIconSize)
+  Q_PROPERTY(int iconTextPadding READ iconTextPadding WRITE setIconTextPadding)
+  Q_PROPERTY(QPen iconBorderPen READ iconBorderPen WRITE setIconBorderPen)
+  Q_PROPERTY(SelectableParts selectableParts READ selectableParts WRITE setSelectableParts NOTIFY selectionChanged)
+  Q_PROPERTY(SelectableParts selectedParts READ selectedParts WRITE setSelectedParts NOTIFY selectableChanged)
+  Q_PROPERTY(QPen selectedBorderPen READ selectedBorderPen WRITE setSelectedBorderPen)
+  Q_PROPERTY(QPen selectedIconBorderPen READ selectedIconBorderPen WRITE setSelectedIconBorderPen)
+  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
+  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
+  Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
+  /// \endcond
+public:
+  /*!
+    Defines the selectable parts of a legend
+
+    \see setSelectedParts, setSelectableParts
+  */
+  enum SelectablePart { spNone       = 0x000  ///< <tt>0x000</tt> None
+                        ,spLegendBox  = 0x001 ///< <tt>0x001</tt> The legend box (frame)
+                        ,spItems      = 0x002 ///< <tt>0x002</tt> Legend items individually (see \ref selectedItems)
+                      };
+  Q_FLAGS(SelectablePart SelectableParts)
+  Q_DECLARE_FLAGS(SelectableParts, SelectablePart)
+
+  explicit QCPLegend();
+  virtual ~QCPLegend();
+
+  // getters:
+  QPen borderPen() const { return mBorderPen; }
+  QBrush brush() const { return mBrush; }
+  QFont font() const { return mFont; }
+  QColor textColor() const { return mTextColor; }
+  QSize iconSize() const { return mIconSize; }
+  int iconTextPadding() const { return mIconTextPadding; }
+  QPen iconBorderPen() const { return mIconBorderPen; }
+  SelectableParts selectableParts() const { return mSelectableParts; }
+  SelectableParts selectedParts() const;
+  QPen selectedBorderPen() const { return mSelectedBorderPen; }
+  QPen selectedIconBorderPen() const { return mSelectedIconBorderPen; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  QFont selectedFont() const { return mSelectedFont; }
+  QColor selectedTextColor() const { return mSelectedTextColor; }
+
+  // setters:
+  void setBorderPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setFont(const QFont &font);
+  void setTextColor(const QColor &color);
+  void setIconSize(const QSize &size);
+  void setIconSize(int width, int height);
+  void setIconTextPadding(int padding);
+  void setIconBorderPen(const QPen &pen);
+  Q_SLOT void setSelectableParts(const SelectableParts &selectableParts);
+  Q_SLOT void setSelectedParts(const SelectableParts &selectedParts);
+  void setSelectedBorderPen(const QPen &pen);
+  void setSelectedIconBorderPen(const QPen &pen);
+  void setSelectedBrush(const QBrush &brush);
+  void setSelectedFont(const QFont &font);
+  void setSelectedTextColor(const QColor &color);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  // non-virtual methods:
+  QCPAbstractLegendItem *item(int index) const;
+  QCPPlottableLegendItem *itemWithPlottable(const QCPAbstractPlottable *plottable) const;
+  int itemCount() const;
+  bool hasItem(QCPAbstractLegendItem *item) const;
+  bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const;
+  bool addItem(QCPAbstractLegendItem *item);
+  bool removeItem(int index);
+  bool removeItem(QCPAbstractLegendItem *item);
+  void clearItems();
+  QList<QCPAbstractLegendItem*> selectedItems() const;
+
+signals:
+  void selectionChanged(QCPLegend::SelectableParts parts);
+  void selectableChanged(QCPLegend::SelectableParts parts);
+
+protected:
+  // property members:
+  QPen mBorderPen, mIconBorderPen;
+  QBrush mBrush;
+  QFont mFont;
+  QColor mTextColor;
+  QSize mIconSize;
+  int mIconTextPadding;
+  SelectableParts mSelectedParts, mSelectableParts;
+  QPen mSelectedBorderPen, mSelectedIconBorderPen;
+  QBrush mSelectedBrush;
+  QFont mSelectedFont;
+  QColor mSelectedTextColor;
+
+  // reimplemented virtual methods:
+  virtual void parentPlotInitialized(QCustomPlot *parentPlot);
+  virtual QCP::Interaction selectionCategory() const;
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter);
+  // events:
+  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
+  virtual void deselectEvent(bool *selectionStateChanged);
+
+  // non-virtual methods:
+  QPen getBorderPen() const;
+  QBrush getBrush() const;
+
+private:
+  Q_DISABLE_COPY(QCPLegend)
+
+  friend class QCustomPlot;
+  friend class QCPAbstractLegendItem;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCPLegend::SelectableParts)
+Q_DECLARE_METATYPE(QCPLegend::SelectablePart)
+
+
+class QCP_LIB_DECL QCPPlotTitle : public QCPLayoutElement
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QString text READ text WRITE setText)
+  Q_PROPERTY(QFont font READ font WRITE setFont)
+  Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor)
+  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
+  Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor)
+  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY selectableChanged)
+  Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
+  /// \endcond
+public:
+  explicit QCPPlotTitle(QCustomPlot *parentPlot);
+  explicit QCPPlotTitle(QCustomPlot *parentPlot, const QString &text);
+
+  // getters:
+  QString text() const { return mText; }
+  QFont font() const { return mFont; }
+  QColor textColor() const { return mTextColor; }
+  QFont selectedFont() const { return mSelectedFont; }
+  QColor selectedTextColor() const { return mSelectedTextColor; }
+  bool selectable() const { return mSelectable; }
+  bool selected() const { return mSelected; }
+
+  // setters:
+  void setText(const QString &text);
+  void setFont(const QFont &font);
+  void setTextColor(const QColor &color);
+  void setSelectedFont(const QFont &font);
+  void setSelectedTextColor(const QColor &color);
+  Q_SLOT void setSelectable(bool selectable);
+  Q_SLOT void setSelected(bool selected);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+signals:
+  void selectionChanged(bool selected);
+  void selectableChanged(bool selectable);
+
+protected:
+  // property members:
+  QString mText;
+  QFont mFont;
+  QColor mTextColor;
+  QFont mSelectedFont;
+  QColor mSelectedTextColor;
+  QRect mTextBoundingRect;
+  bool mSelectable, mSelected;
+
+  // reimplemented virtual methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  virtual void draw(QCPPainter *painter);
+  virtual QSize minimumSizeHint() const;
+  virtual QSize maximumSizeHint() const;
+  // events:
+  virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged);
+  virtual void deselectEvent(bool *selectionStateChanged);
+
+  // non-virtual methods:
+  QFont mainFont() const;
+  QColor mainTextColor() const;
+
+private:
+  Q_DISABLE_COPY(QCPPlotTitle)
+};
+
+
+class QCPColorScaleAxisRectPrivate : public QCPAxisRect
+{
+  Q_OBJECT
+public:
+  explicit QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale);
+protected:
+  QCPColorScale *mParentColorScale;
+  QImage mGradientImage;
+  bool mGradientImageInvalidated;
+  // re-using some methods of QCPAxisRect to make them available to friend class QCPColorScale
+  using QCPAxisRect::calculateAutoMargin;
+  using QCPAxisRect::mousePressEvent;
+  using QCPAxisRect::mouseMoveEvent;
+  using QCPAxisRect::mouseReleaseEvent;
+  using QCPAxisRect::wheelEvent;
+  using QCPAxisRect::update;
+  virtual void draw(QCPPainter *painter);
+  void updateGradientImage();
+  Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts);
+  Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts);
+  friend class QCPColorScale;
+};
+
+
+class QCP_LIB_DECL QCPColorScale : public QCPLayoutElement
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QCPAxis::AxisType type READ type WRITE setType)
+  Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged)
+  Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged)
+  Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged)
+  Q_PROPERTY(QString label READ label WRITE setLabel)
+  Q_PROPERTY(int barWidth READ barWidth WRITE setBarWidth)
+  Q_PROPERTY(bool rangeDrag READ rangeDrag WRITE setRangeDrag)
+  Q_PROPERTY(bool rangeZoom READ rangeZoom WRITE setRangeZoom)
+  /// \endcond
+public:
+  explicit QCPColorScale(QCustomPlot *parentPlot);
+  virtual ~QCPColorScale();
+
+  // getters:
+  QCPAxis *axis() const { return mColorAxis.data(); }
+  QCPAxis::AxisType type() const { return mType; }
+  QCPRange dataRange() const { return mDataRange; }
+  QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; }
+  QCPColorGradient gradient() const { return mGradient; }
+  QString label() const;
+  int barWidth () const { return mBarWidth; }
+  bool rangeDrag() const;
+  bool rangeZoom() const;
+
+  // setters:
+  void setType(QCPAxis::AxisType type);
+  Q_SLOT void setDataRange(const QCPRange &dataRange);
+  Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType);
+  Q_SLOT void setGradient(const QCPColorGradient &gradient);
+  void setLabel(const QString &str);
+  void setBarWidth(int width);
+  void setRangeDrag(bool enabled);
+  void setRangeZoom(bool enabled);
+
+  // non-property methods:
+  QList<QCPColorMap*> colorMaps() const;
+  void rescaleDataRange(bool onlyVisibleMaps);
+
+  // reimplemented virtual methods:
+  virtual void update(UpdatePhase phase);
+
+signals:
+  void dataRangeChanged(QCPRange newRange);
+  void dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
+  void gradientChanged(QCPColorGradient newGradient);
+
+protected:
+  // property members:
+  QCPAxis::AxisType mType;
+  QCPRange mDataRange;
+  QCPAxis::ScaleType mDataScaleType;
+  QCPColorGradient mGradient;
+  int mBarWidth;
+
+  // non-property members:
+  QPointer<QCPColorScaleAxisRectPrivate> mAxisRect;
+  QPointer<QCPAxis> mColorAxis;
+
+  // reimplemented virtual methods:
+  virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const;
+  // events:
+  virtual void mousePressEvent(QMouseEvent *event);
+  virtual void mouseMoveEvent(QMouseEvent *event);
+  virtual void mouseReleaseEvent(QMouseEvent *event);
+  virtual void wheelEvent(QWheelEvent *event);
+
+private:
+  Q_DISABLE_COPY(QCPColorScale)
+
+  friend class QCPColorScaleAxisRectPrivate;
+};
+
+
+/*! \file */
+
+
+
+class QCP_LIB_DECL QCPData
+{
+public:
+  QCPData();
+  QCPData(double key, double value);
+  double key, value;
+  double keyErrorPlus, keyErrorMinus;
+  double valueErrorPlus, valueErrorMinus;
+};
+Q_DECLARE_TYPEINFO(QCPData, Q_MOVABLE_TYPE);
+
+/*! \typedef QCPDataMap
+  Container for storing \ref QCPData items in a sorted fashion. The key of the map
+  is the key member of the QCPData instance.
+
+  This is the container in which QCPGraph holds its data.
+  \see QCPData, QCPGraph::setData
+*/
+typedef QMap<double, QCPData> QCPDataMap;
+typedef QMapIterator<double, QCPData> QCPDataMapIterator;
+typedef QMutableMapIterator<double, QCPData> QCPDataMutableMapIterator;
+
+
+class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle)
+  Q_PROPERTY(QCPScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle)
+  Q_PROPERTY(ErrorType errorType READ errorType WRITE setErrorType)
+  Q_PROPERTY(QPen errorPen READ errorPen WRITE setErrorPen)
+  Q_PROPERTY(double errorBarSize READ errorBarSize WRITE setErrorBarSize)
+  Q_PROPERTY(bool errorBarSkipSymbol READ errorBarSkipSymbol WRITE setErrorBarSkipSymbol)
+  Q_PROPERTY(QCPGraph* channelFillGraph READ channelFillGraph WRITE setChannelFillGraph)
+  Q_PROPERTY(bool adaptiveSampling READ adaptiveSampling WRITE setAdaptiveSampling)
+  /// \endcond
+public:
+  /*!
+    Defines how the graph's line is represented visually in the plot. The line is drawn with the
+    current pen of the graph (\ref setPen).
+    \see setLineStyle
+  */
+  enum LineStyle { lsNone        ///< data points are not connected with any lines (e.g. data only represented
+                                 ///< with symbols according to the scatter style, see \ref setScatterStyle)
+                   ,lsLine       ///< data points are connected by a straight line
+                   ,lsStepLeft   ///< line is drawn as steps where the step height is the value of the left data point
+                   ,lsStepRight  ///< line is drawn as steps where the step height is the value of the right data point
+                   ,lsStepCenter ///< line is drawn as steps where the step is in between two data points
+                   ,lsImpulse    ///< each data point is represented by a line parallel to the value axis, which reaches from the data point to the zero-value-line
+                 };
+  Q_ENUMS(LineStyle)
+  /*!
+    Defines what kind of error bars are drawn for each data point
+  */
+  enum ErrorType { etNone   ///< No error bars are shown
+                   ,etKey   ///< Error bars for the key dimension of the data point are shown
+                   ,etValue ///< Error bars for the value dimension of the data point are shown
+                   ,etBoth  ///< Error bars for both key and value dimensions of the data point are shown
+                 };
+  Q_ENUMS(ErrorType)
+
+  explicit QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPGraph();
+
+  // getters:
+  QCPDataMap *data() const { return mData; }
+  LineStyle lineStyle() const { return mLineStyle; }
+  QCPScatterStyle scatterStyle() const { return mScatterStyle; }
+  ErrorType errorType() const { return mErrorType; }
+  QPen errorPen() const { return mErrorPen; }
+  double errorBarSize() const { return mErrorBarSize; }
+  bool errorBarSkipSymbol() const { return mErrorBarSkipSymbol; }
+  QCPGraph *channelFillGraph() const { return mChannelFillGraph.data(); }
+  bool adaptiveSampling() const { return mAdaptiveSampling; }
+
+  // setters:
+  void setData(QCPDataMap *data, bool copy=false);
+  void setData(const QVector<double> &key, const QVector<double> &value);
+  void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError);
+  void setDataKeyError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus);
+  void setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueError);
+  void setDataValueError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus);
+  void setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyError, const QVector<double> &valueError);
+  void setDataBothError(const QVector<double> &key, const QVector<double> &value, const QVector<double> &keyErrorMinus, const QVector<double> &keyErrorPlus, const QVector<double> &valueErrorMinus, const QVector<double> &valueErrorPlus);
+  void setLineStyle(LineStyle ls);
+  void setScatterStyle(const QCPScatterStyle &style);
+  void setErrorType(ErrorType errorType);
+  void setErrorPen(const QPen &pen);
+  void setErrorBarSize(double size);
+  void setErrorBarSkipSymbol(bool enabled);
+  void setChannelFillGraph(QCPGraph *targetGraph);
+  void setAdaptiveSampling(bool enabled);
+
+  // non-property methods:
+  void addData(const QCPDataMap &dataMap);
+  void addData(const QCPData &data);
+  void addData(double key, double value);
+  void addData(const QVector<double> &keys, const QVector<double> &values);
+  void removeDataBefore(double key);
+  void removeDataAfter(double key);
+  void removeData(double fromKey, double toKey);
+  void removeData(double key);
+
+  // reimplemented virtual methods:
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+  using QCPAbstractPlottable::rescaleAxes;
+  using QCPAbstractPlottable::rescaleKeyAxis;
+  using QCPAbstractPlottable::rescaleValueAxis;
+  void rescaleAxes(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
+  void rescaleKeyAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
+  void rescaleValueAxis(bool onlyEnlarge, bool includeErrorBars) const; // overloads base class interface
+
+protected:
+  // property members:
+  QCPDataMap *mData;
+  QPen mErrorPen;
+  LineStyle mLineStyle;
+  QCPScatterStyle mScatterStyle;
+  ErrorType mErrorType;
+  double mErrorBarSize;
+  bool mErrorBarSkipSymbol;
+  QPointer<QCPGraph> mChannelFillGraph;
+  bool mAdaptiveSampling;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
+  virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
+  virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain, bool includeErrors) const; // overloads base class interface
+
+  // introduced virtual methods:
+  virtual void drawFill(QCPPainter *painter, QVector<QPointF> *lineData) const;
+  virtual void drawScatterPlot(QCPPainter *painter, QVector<QCPData> *scatterData) const;
+  virtual void drawLinePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
+  virtual void drawImpulsePlot(QCPPainter *painter, QVector<QPointF> *lineData) const;
+
+  // non-virtual methods:
+  void getPreparedData(QVector<QCPData> *lineData, QVector<QCPData> *scatterData) const;
+  void getPlotData(QVector<QPointF> *lineData, QVector<QCPData> *scatterData) const;
+  void getScatterPlotData(QVector<QCPData> *scatterData) const;
+  void getLinePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
+  void getStepLeftPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
+  void getStepRightPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
+  void getStepCenterPlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
+  void getImpulsePlotData(QVector<QPointF> *linePixelData, QVector<QCPData> *scatterData) const;
+  void drawError(QCPPainter *painter, double x, double y, const QCPData &data) const;
+  void getVisibleDataBounds(QCPDataMap::const_iterator &lower, QCPDataMap::const_iterator &upper) const;
+  int countDataInBounds(const QCPDataMap::const_iterator &lower, const QCPDataMap::const_iterator &upper, int maxCount) const;
+  void addFillBasePoints(QVector<QPointF> *lineData) const;
+  void removeFillBasePoints(QVector<QPointF> *lineData) const;
+  QPointF lowerFillBasePoint(double lowerKey) const;
+  QPointF upperFillBasePoint(double upperKey) const;
+  const QPolygonF getChannelFillPolygon(const QVector<QPointF> *lineData) const;
+  int findIndexBelowX(const QVector<QPointF> *data, double x) const;
+  int findIndexAboveX(const QVector<QPointF> *data, double x) const;
+  int findIndexBelowY(const QVector<QPointF> *data, double y) const;
+  int findIndexAboveY(const QVector<QPointF> *data, double y) const;
+  double pointDistance(const QPointF &pixelPoint) const;
+
+  friend class QCustomPlot;
+  friend class QCPLegend;
+};
+
+
+/*! \file */
+
+
+
+class QCP_LIB_DECL QCPCurveData
+{
+public:
+  QCPCurveData();
+  QCPCurveData(double t, double key, double value);
+  double t, key, value;
+};
+Q_DECLARE_TYPEINFO(QCPCurveData, Q_MOVABLE_TYPE);
+
+/*! \typedef QCPCurveDataMap
+  Container for storing \ref QCPCurveData items in a sorted fashion. The key of the map
+  is the t member of the QCPCurveData instance.
+
+  This is the container in which QCPCurve holds its data.
+  \see QCPCurveData, QCPCurve::setData
+*/
+
+typedef QMap<double, QCPCurveData> QCPCurveDataMap;
+typedef QMapIterator<double, QCPCurveData> QCPCurveDataMapIterator;
+typedef QMutableMapIterator<double, QCPCurveData> QCPCurveDataMutableMapIterator;
+
+
+class QCP_LIB_DECL QCPCurve : public QCPAbstractPlottable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QCPScatterStyle scatterStyle READ scatterStyle WRITE setScatterStyle)
+  Q_PROPERTY(LineStyle lineStyle READ lineStyle WRITE setLineStyle)
+  /// \endcond
+public:
+  /*!
+    Defines how the curve's line is represented visually in the plot. The line is drawn with the
+    current pen of the curve (\ref setPen).
+    \see setLineStyle
+  */
+  enum LineStyle { lsNone  ///< No line is drawn between data points (e.g. only scatters)
+                   ,lsLine ///< Data points are connected with a straight line
+                 };
+  explicit QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPCurve();
+
+  // getters:
+  QCPCurveDataMap *data() const { return mData; }
+  QCPScatterStyle scatterStyle() const { return mScatterStyle; }
+  LineStyle lineStyle() const { return mLineStyle; }
+
+  // setters:
+  void setData(QCPCurveDataMap *data, bool copy=false);
+  void setData(const QVector<double> &t, const QVector<double> &key, const QVector<double> &value);
+  void setData(const QVector<double> &key, const QVector<double> &value);
+  void setScatterStyle(const QCPScatterStyle &style);
+  void setLineStyle(LineStyle style);
+
+  // non-property methods:
+  void addData(const QCPCurveDataMap &dataMap);
+  void addData(const QCPCurveData &data);
+  void addData(double t, double key, double value);
+  void addData(double key, double value);
+  void addData(const QVector<double> &ts, const QVector<double> &keys, const QVector<double> &values);
+  void removeDataBefore(double t);
+  void removeDataAfter(double t);
+  void removeData(double fromt, double tot);
+  void removeData(double t);
+
+  // reimplemented virtual methods:
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+protected:
+  // property members:
+  QCPCurveDataMap *mData;
+  QCPScatterStyle mScatterStyle;
+  LineStyle mLineStyle;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
+  virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+
+  // introduced virtual methods:
+  virtual void drawScatterPlot(QCPPainter *painter, const QVector<QPointF> *pointData) const;
+
+  // non-virtual methods:
+  void getCurveData(QVector<QPointF> *lineData) const;
+  int getRegion(double x, double y, double rectLeft, double rectTop, double rectRight, double rectBottom) const;
+  QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const;
+  QVector<QPointF> getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom) const;
+  bool mayTraverse(int prevRegion, int currentRegion) const;
+  bool getTraverse(double prevKey, double prevValue, double key, double value, double rectLeft, double rectTop, double rectRight, double rectBottom, QPointF &crossA, QPointF &crossB) const;
+  void getTraverseCornerPoints(int prevRegion, int currentRegion, double rectLeft, double rectTop, double rectRight, double rectBottom, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const;
+  double pointDistance(const QPointF &pixelPoint) const;
+
+  friend class QCustomPlot;
+  friend class QCPLegend;
+};
+
+
+/*! \file */
+
+
+
+class QCP_LIB_DECL QCPBarsGroup : public QObject
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(SpacingType spacingType READ spacingType WRITE setSpacingType)
+  Q_PROPERTY(double spacing READ spacing WRITE setSpacing)
+  /// \endcond
+public:
+  /*!
+    Defines the ways the spacing between bars in the group can be specified. Thus it defines what
+    the number passed to \ref setSpacing actually means.
+
+    \see setSpacingType, setSpacing
+  */
+  enum SpacingType { stAbsolute       ///< Bar spacing is in absolute pixels
+                     ,stAxisRectRatio ///< Bar spacing is given by a fraction of the axis rect size
+                     ,stPlotCoords    ///< Bar spacing is in key coordinates and thus scales with the key axis range
+                 };
+  QCPBarsGroup(QCustomPlot *parentPlot);
+  ~QCPBarsGroup();
+
+  // getters:
+  SpacingType spacingType() const { return mSpacingType; }
+  double spacing() const { return mSpacing; }
+
+  // setters:
+  void setSpacingType(SpacingType spacingType);
+  void setSpacing(double spacing);
+
+  // non-virtual methods:
+  QList<QCPBars*> bars() const { return mBars; }
+  QCPBars* bars(int index) const;
+  int size() const { return mBars.size(); }
+  bool isEmpty() const { return mBars.isEmpty(); }
+  void clear();
+  bool contains(QCPBars *bars) const { return mBars.contains(bars); }
+  void append(QCPBars *bars);
+  void insert(int i, QCPBars *bars);
+  void remove(QCPBars *bars);
+
+protected:
+  // non-property members:
+  QCustomPlot *mParentPlot;
+  SpacingType mSpacingType;
+  double mSpacing;
+  QList<QCPBars*> mBars;
+
+  // non-virtual methods:
+  void registerBars(QCPBars *bars);
+  void unregisterBars(QCPBars *bars);
+
+  // virtual methods:
+  double keyPixelOffset(const QCPBars *bars, double keyCoord);
+  double getPixelSpacing(const QCPBars *bars, double keyCoord);
+
+private:
+  Q_DISABLE_COPY(QCPBarsGroup)
+
+  friend class QCPBars;
+};
+
+
+class QCP_LIB_DECL QCPBarData
+{
+public:
+  QCPBarData();
+  QCPBarData(double key, double value);
+  double key, value;
+};
+Q_DECLARE_TYPEINFO(QCPBarData, Q_MOVABLE_TYPE);
+
+/*! \typedef QCPBarDataMap
+  Container for storing \ref QCPBarData items in a sorted fashion. The key of the map
+  is the key member of the QCPBarData instance.
+
+  This is the container in which QCPBars holds its data.
+  \see QCPBarData, QCPBars::setData
+*/
+typedef QMap<double, QCPBarData> QCPBarDataMap;
+typedef QMapIterator<double, QCPBarData> QCPBarDataMapIterator;
+typedef QMutableMapIterator<double, QCPBarData> QCPBarDataMutableMapIterator;
+
+
+class QCP_LIB_DECL QCPBars : public QCPAbstractPlottable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(double width READ width WRITE setWidth)
+  Q_PROPERTY(WidthType widthType READ widthType WRITE setWidthType)
+  Q_PROPERTY(QCPBarsGroup* barsGroup READ barsGroup WRITE setBarsGroup)
+  Q_PROPERTY(double baseValue READ baseValue WRITE setBaseValue)
+  Q_PROPERTY(QCPBars* barBelow READ barBelow)
+  Q_PROPERTY(QCPBars* barAbove READ barAbove)
+  /// \endcond
+public:
+  /*!
+    Defines the ways the width of the bar can be specified. Thus it defines what the number passed
+    to \ref setWidth actually means.
+
+    \see setWidthType, setWidth
+  */
+  enum WidthType { wtAbsolute       ///< Bar width is in absolute pixels
+                   ,wtAxisRectRatio ///< Bar width is given by a fraction of the axis rect size
+                   ,wtPlotCoords    ///< Bar width is in key coordinates and thus scales with the key axis range
+                 };
+   Q_ENUMS(WidthType)
+
+  explicit QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPBars();
+
+  // getters:
+  double width() const { return mWidth; }
+  WidthType widthType() const { return mWidthType; }
+  QCPBarsGroup *barsGroup() const { return mBarsGroup; }
+  double baseValue() const { return mBaseValue; }
+  QCPBars *barBelow() const { return mBarBelow.data(); }
+  QCPBars *barAbove() const { return mBarAbove.data(); }
+  QCPBarDataMap *data() const { return mData; }
+
+  // setters:
+  void setWidth(double width);
+  void setWidthType(WidthType widthType);
+  void setBarsGroup(QCPBarsGroup *barsGroup);
+  void setBaseValue(double baseValue);
+  void setData(QCPBarDataMap *data, bool copy=false);
+  void setData(const QVector<double> &key, const QVector<double> &value);
+
+  // non-property methods:
+  void moveBelow(QCPBars *bars);
+  void moveAbove(QCPBars *bars);
+  void addData(const QCPBarDataMap &dataMap);
+  void addData(const QCPBarData &data);
+  void addData(double key, double value);
+  void addData(const QVector<double> &keys, const QVector<double> &values);
+  void removeDataBefore(double key);
+  void removeDataAfter(double key);
+  void removeData(double fromKey, double toKey);
+  void removeData(double key);
+
+  // reimplemented virtual methods:
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+protected:
+  // property members:
+  QCPBarDataMap *mData;
+  double mWidth;
+  WidthType mWidthType;
+  QCPBarsGroup *mBarsGroup;
+  double mBaseValue;
+  QPointer<QCPBars> mBarBelow, mBarAbove;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
+  virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+
+  // non-virtual methods:
+  void getVisibleDataBounds(QCPBarDataMap::const_iterator &lower, QCPBarDataMap::const_iterator &upperEnd) const;
+  QPolygonF getBarPolygon(double key, double value) const;
+  void getPixelWidth(double key, double &lower, double &upper) const;
+  double getStackedBaseValue(double key, bool positive) const;
+  static void connectBars(QCPBars* lower, QCPBars* upper);
+
+  friend class QCustomPlot;
+  friend class QCPLegend;
+  friend class QCPBarsGroup;
+};
+
+
+/*! \file */
+
+
+
+class QCP_LIB_DECL QCPStatisticalBox : public QCPAbstractPlottable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(double key READ key WRITE setKey)
+  Q_PROPERTY(double minimum READ minimum WRITE setMinimum)
+  Q_PROPERTY(double lowerQuartile READ lowerQuartile WRITE setLowerQuartile)
+  Q_PROPERTY(double median READ median WRITE setMedian)
+  Q_PROPERTY(double upperQuartile READ upperQuartile WRITE setUpperQuartile)
+  Q_PROPERTY(double maximum READ maximum WRITE setMaximum)
+  Q_PROPERTY(QVector<double> outliers READ outliers WRITE setOutliers)
+  Q_PROPERTY(double width READ width WRITE setWidth)
+  Q_PROPERTY(double whiskerWidth READ whiskerWidth WRITE setWhiskerWidth)
+  Q_PROPERTY(QPen whiskerPen READ whiskerPen WRITE setWhiskerPen)
+  Q_PROPERTY(QPen whiskerBarPen READ whiskerBarPen WRITE setWhiskerBarPen)
+  Q_PROPERTY(QPen medianPen READ medianPen WRITE setMedianPen)
+  Q_PROPERTY(QCPScatterStyle outlierStyle READ outlierStyle WRITE setOutlierStyle)
+  /// \endcond
+public:
+  explicit QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis);
+
+  // getters:
+  double key() const { return mKey; }
+  double minimum() const { return mMinimum; }
+  double lowerQuartile() const { return mLowerQuartile; }
+  double median() const { return mMedian; }
+  double upperQuartile() const { return mUpperQuartile; }
+  double maximum() const { return mMaximum; }
+  QVector<double> outliers() const { return mOutliers; }
+  double width() const { return mWidth; }
+  double whiskerWidth() const { return mWhiskerWidth; }
+  QPen whiskerPen() const { return mWhiskerPen; }
+  QPen whiskerBarPen() const { return mWhiskerBarPen; }
+  QPen medianPen() const { return mMedianPen; }
+  QCPScatterStyle outlierStyle() const { return mOutlierStyle; }
+
+  // setters:
+  void setKey(double key);
+  void setMinimum(double value);
+  void setLowerQuartile(double value);
+  void setMedian(double value);
+  void setUpperQuartile(double value);
+  void setMaximum(double value);
+  void setOutliers(const QVector<double> &values);
+  void setData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum);
+  void setWidth(double width);
+  void setWhiskerWidth(double width);
+  void setWhiskerPen(const QPen &pen);
+  void setWhiskerBarPen(const QPen &pen);
+  void setMedianPen(const QPen &pen);
+  void setOutlierStyle(const QCPScatterStyle &style);
+
+  // non-property methods:
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+protected:
+  // property members:
+  QVector<double> mOutliers;
+  double mKey, mMinimum, mLowerQuartile, mMedian, mUpperQuartile, mMaximum;
+  double mWidth;
+  double mWhiskerWidth;
+  QPen mWhiskerPen, mWhiskerBarPen, mMedianPen;
+  QCPScatterStyle mOutlierStyle;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
+  virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+
+  // introduced virtual methods:
+  virtual void drawQuartileBox(QCPPainter *painter, QRectF *quartileBox=0) const;
+  virtual void drawMedian(QCPPainter *painter) const;
+  virtual void drawWhiskers(QCPPainter *painter) const;
+  virtual void drawOutliers(QCPPainter *painter) const;
+
+  friend class QCustomPlot;
+  friend class QCPLegend;
+};
+
+
+class QCP_LIB_DECL QCPColorMapData
+{
+public:
+  QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange);
+  ~QCPColorMapData();
+  QCPColorMapData(const QCPColorMapData &other);
+  QCPColorMapData &operator=(const QCPColorMapData &other);
+
+  // getters:
+  int keySize() const { return mKeySize; }
+  int valueSize() const { return mValueSize; }
+  QCPRange keyRange() const { return mKeyRange; }
+  QCPRange valueRange() const { return mValueRange; }
+  QCPRange dataBounds() const { return mDataBounds; }
+  double data(double key, double value);
+  double cell(int keyIndex, int valueIndex);
+
+  // setters:
+  void setSize(int keySize, int valueSize);
+  void setKeySize(int keySize);
+  void setValueSize(int valueSize);
+  void setRange(const QCPRange &keyRange, const QCPRange &valueRange);
+  void setKeyRange(const QCPRange &keyRange);
+  void setValueRange(const QCPRange &valueRange);
+  void setData(double key, double value, double z);
+  void setCell(int keyIndex, int valueIndex, double z);
+
+  // non-property methods:
+  void recalculateDataBounds();
+  void clear();
+  void fill(double z);
+  bool isEmpty() const { return mIsEmpty; }
+  void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const;
+  void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const;
+
+protected:
+  // property members:
+  int mKeySize, mValueSize;
+  QCPRange mKeyRange, mValueRange;
+  bool mIsEmpty;
+  // non-property members:
+  double *mData;
+  QCPRange mDataBounds;
+  bool mDataModified;
+
+  friend class QCPColorMap;
+};
+
+
+class QCP_LIB_DECL QCPColorMap : public QCPAbstractPlottable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QCPRange dataRange READ dataRange WRITE setDataRange NOTIFY dataRangeChanged)
+  Q_PROPERTY(QCPAxis::ScaleType dataScaleType READ dataScaleType WRITE setDataScaleType NOTIFY dataScaleTypeChanged)
+  Q_PROPERTY(QCPColorGradient gradient READ gradient WRITE setGradient NOTIFY gradientChanged)
+  Q_PROPERTY(bool interpolate READ interpolate WRITE setInterpolate)
+  Q_PROPERTY(bool tightBoundary READ tightBoundary WRITE setTightBoundary)
+  Q_PROPERTY(QCPColorScale* colorScale READ colorScale WRITE setColorScale)
+  /// \endcond
+public:
+  explicit QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPColorMap();
+
+  // getters:
+  QCPColorMapData *data() const { return mMapData; }
+  QCPRange dataRange() const { return mDataRange; }
+  QCPAxis::ScaleType dataScaleType() const { return mDataScaleType; }
+  bool interpolate() const { return mInterpolate; }
+  bool tightBoundary() const { return mTightBoundary; }
+  QCPColorGradient gradient() const { return mGradient; }
+  QCPColorScale *colorScale() const { return mColorScale.data(); }
+
+  // setters:
+  void setData(QCPColorMapData *data, bool copy=false);
+  Q_SLOT void setDataRange(const QCPRange &dataRange);
+  Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType);
+  Q_SLOT void setGradient(const QCPColorGradient &gradient);
+  void setInterpolate(bool enabled);
+  void setTightBoundary(bool enabled);
+  void setColorScale(QCPColorScale *colorScale);
+
+  // non-property methods:
+  void rescaleDataRange(bool recalculateDataBounds=false);
+  Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18));
+
+  // reimplemented virtual methods:
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+signals:
+  void dataRangeChanged(QCPRange newRange);
+  void dataScaleTypeChanged(QCPAxis::ScaleType scaleType);
+  void gradientChanged(QCPColorGradient newGradient);
+
+protected:
+  // property members:
+  QCPRange mDataRange;
+  QCPAxis::ScaleType mDataScaleType;
+  QCPColorMapData *mMapData;
+  QCPColorGradient mGradient;
+  bool mInterpolate;
+  bool mTightBoundary;
+  QPointer<QCPColorScale> mColorScale;
+  // non-property members:
+  QImage mMapImage, mUndersampledMapImage;
+  QPixmap mLegendIcon;
+  bool mMapImageInvalidated;
+
+  // introduced virtual methods:
+  virtual void updateMapImage();
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
+  virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+
+  friend class QCustomPlot;
+  friend class QCPLegend;
+};
+
+
+/*! \file */
+
+
+
+class QCP_LIB_DECL QCPFinancialData
+{
+public:
+  QCPFinancialData();
+  QCPFinancialData(double key, double open, double high, double low, double close);
+  double key, open, high, low, close;
+};
+Q_DECLARE_TYPEINFO(QCPFinancialData, Q_MOVABLE_TYPE);
+
+/*! \typedef QCPFinancialDataMap
+  Container for storing \ref QCPFinancialData items in a sorted fashion. The key of the map
+  is the key member of the QCPFinancialData instance.
+
+  This is the container in which QCPFinancial holds its data.
+  \see QCPFinancial, QCPFinancial::setData
+*/
+typedef QMap<double, QCPFinancialData> QCPFinancialDataMap;
+typedef QMapIterator<double, QCPFinancialData> QCPFinancialDataMapIterator;
+typedef QMutableMapIterator<double, QCPFinancialData> QCPFinancialDataMutableMapIterator;
+
+
+class QCP_LIB_DECL QCPFinancial : public QCPAbstractPlottable
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(ChartStyle chartStyle READ chartStyle WRITE setChartStyle)
+  Q_PROPERTY(double width READ width WRITE setWidth)
+  Q_PROPERTY(bool twoColored READ twoColored WRITE setTwoColored)
+  Q_PROPERTY(QBrush brushPositive READ brushPositive WRITE setBrushPositive)
+  Q_PROPERTY(QBrush brushNegative READ brushNegative WRITE setBrushNegative)
+  Q_PROPERTY(QPen penPositive READ penPositive WRITE setPenPositive)
+  Q_PROPERTY(QPen penNegative READ penNegative WRITE setPenNegative)
+  /// \endcond
+public:
+  /*!
+    Defines the possible representations of OHLC data in the plot.
+
+    \see setChartStyle
+  */
+  enum ChartStyle { csOhlc         ///< Open-High-Low-Close bar representation
+                   ,csCandlestick  ///< Candlestick representation
+                  };
+  Q_ENUMS(ChartStyle)
+
+  explicit QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis);
+  virtual ~QCPFinancial();
+
+  // getters:
+  QCPFinancialDataMap *data() const { return mData; }
+  ChartStyle chartStyle() const { return mChartStyle; }
+  double width() const { return mWidth; }
+  bool twoColored() const { return mTwoColored; }
+  QBrush brushPositive() const { return mBrushPositive; }
+  QBrush brushNegative() const { return mBrushNegative; }
+  QPen penPositive() const { return mPenPositive; }
+  QPen penNegative() const { return mPenNegative; }
+
+
+  // setters:
+  void setData(QCPFinancialDataMap *data, bool copy=false);
+  void setData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close);
+  void setChartStyle(ChartStyle style);
+  void setWidth(double width);
+  void setTwoColored(bool twoColored);
+  void setBrushPositive(const QBrush &brush);
+  void setBrushNegative(const QBrush &brush);
+  void setPenPositive(const QPen &pen);
+  void setPenNegative(const QPen &pen);
+
+  // non-property methods:
+  void addData(const QCPFinancialDataMap &dataMap);
+  void addData(const QCPFinancialData &data);
+  void addData(double key, double open, double high, double low, double close);
+  void addData(const QVector<double> &key, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close);
+  void removeDataBefore(double key);
+  void removeDataAfter(double key);
+  void removeData(double fromKey, double toKey);
+  void removeData(double key);
+
+  // reimplemented virtual methods:
+  virtual void clearData();
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  // static methods:
+  static QCPFinancialDataMap timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset = 0);
+
+protected:
+  // property members:
+  QCPFinancialDataMap *mData;
+  ChartStyle mChartStyle;
+  double mWidth;
+  bool mTwoColored;
+  QBrush mBrushPositive, mBrushNegative;
+  QPen mPenPositive, mPenNegative;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const;
+  virtual QCPRange getKeyRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+  virtual QCPRange getValueRange(bool &foundRange, SignDomain inSignDomain=sdBoth) const;
+
+  // non-virtual methods:
+  void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end);
+  void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end);
+  double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const;
+  double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataMap::const_iterator &begin, const QCPFinancialDataMap::const_iterator &end) const;
+  void getVisibleDataBounds(QCPFinancialDataMap::const_iterator &lower, QCPFinancialDataMap::const_iterator &upper) const;
+
+  friend class QCustomPlot;
+  friend class QCPLegend;
+};
+
+
+class QCP_LIB_DECL QCPItemStraightLine : public QCPAbstractItem
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  /// \endcond
+public:
+  QCPItemStraightLine(QCustomPlot *parentPlot);
+  virtual ~QCPItemStraightLine();
+
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  QCPItemPosition * const point1;
+  QCPItemPosition * const point2;
+
+protected:
+  // property members:
+  QPen mPen, mSelectedPen;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+
+  // non-virtual methods:
+  double distToStraightLine(const QVector2D &point1, const QVector2D &vec, const QVector2D &point) const;
+  QLineF getRectClippedStraightLine(const QVector2D &point1, const QVector2D &vec, const QRect &rect) const;
+  QPen mainPen() const;
+};
+
+
+class QCP_LIB_DECL QCPItemLine : public QCPAbstractItem
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  Q_PROPERTY(QCPLineEnding head READ head WRITE setHead)
+  Q_PROPERTY(QCPLineEnding tail READ tail WRITE setTail)
+  /// \endcond
+public:
+  QCPItemLine(QCustomPlot *parentPlot);
+  virtual ~QCPItemLine();
+
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QCPLineEnding head() const { return mHead; }
+  QCPLineEnding tail() const { return mTail; }
+
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setHead(const QCPLineEnding &head);
+  void setTail(const QCPLineEnding &tail);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  QCPItemPosition * const start;
+  QCPItemPosition * const end;
+
+protected:
+  // property members:
+  QPen mPen, mSelectedPen;
+  QCPLineEnding mHead, mTail;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+
+  // non-virtual methods:
+  QLineF getRectClippedLine(const QVector2D &start, const QVector2D &end, const QRect &rect) const;
+  QPen mainPen() const;
+};
+
+
+class QCP_LIB_DECL QCPItemCurve : public QCPAbstractItem
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  Q_PROPERTY(QCPLineEnding head READ head WRITE setHead)
+  Q_PROPERTY(QCPLineEnding tail READ tail WRITE setTail)
+  /// \endcond
+public:
+  QCPItemCurve(QCustomPlot *parentPlot);
+  virtual ~QCPItemCurve();
+
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QCPLineEnding head() const { return mHead; }
+  QCPLineEnding tail() const { return mTail; }
+
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setHead(const QCPLineEnding &head);
+  void setTail(const QCPLineEnding &tail);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  QCPItemPosition * const start;
+  QCPItemPosition * const startDir;
+  QCPItemPosition * const endDir;
+  QCPItemPosition * const end;
+
+protected:
+  // property members:
+  QPen mPen, mSelectedPen;
+  QCPLineEnding mHead, mTail;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+
+  // non-virtual methods:
+  QPen mainPen() const;
+};
+
+
+class QCP_LIB_DECL QCPItemRect : public QCPAbstractItem
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
+  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
+  /// \endcond
+public:
+  QCPItemRect(QCustomPlot *parentPlot);
+  virtual ~QCPItemRect();
+
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  QCPItemPosition * const topLeft;
+  QCPItemPosition * const bottomRight;
+  QCPItemAnchor * const top;
+  QCPItemAnchor * const topRight;
+  QCPItemAnchor * const right;
+  QCPItemAnchor * const bottom;
+  QCPItemAnchor * const bottomLeft;
+  QCPItemAnchor * const left;
+
+protected:
+  enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};
+
+  // property members:
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+
+  // non-virtual methods:
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+};
+
+
+class QCP_LIB_DECL QCPItemText : public QCPAbstractItem
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QColor color READ color WRITE setColor)
+  Q_PROPERTY(QColor selectedColor READ selectedColor WRITE setSelectedColor)
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
+  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
+  Q_PROPERTY(QFont font READ font WRITE setFont)
+  Q_PROPERTY(QFont selectedFont READ selectedFont WRITE setSelectedFont)
+  Q_PROPERTY(QString text READ text WRITE setText)
+  Q_PROPERTY(Qt::Alignment positionAlignment READ positionAlignment WRITE setPositionAlignment)
+  Q_PROPERTY(Qt::Alignment textAlignment READ textAlignment WRITE setTextAlignment)
+  Q_PROPERTY(double rotation READ rotation WRITE setRotation)
+  Q_PROPERTY(QMargins padding READ padding WRITE setPadding)
+  /// \endcond
+public:
+  QCPItemText(QCustomPlot *parentPlot);
+  virtual ~QCPItemText();
+
+  // getters:
+  QColor color() const { return mColor; }
+  QColor selectedColor() const { return mSelectedColor; }
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  QFont font() const { return mFont; }
+  QFont selectedFont() const { return mSelectedFont; }
+  QString text() const { return mText; }
+  Qt::Alignment positionAlignment() const { return mPositionAlignment; }
+  Qt::Alignment textAlignment() const { return mTextAlignment; }
+  double rotation() const { return mRotation; }
+  QMargins padding() const { return mPadding; }
+
+  // setters;
+  void setColor(const QColor &color);
+  void setSelectedColor(const QColor &color);
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+  void setFont(const QFont &font);
+  void setSelectedFont(const QFont &font);
+  void setText(const QString &text);
+  void setPositionAlignment(Qt::Alignment alignment);
+  void setTextAlignment(Qt::Alignment alignment);
+  void setRotation(double degrees);
+  void setPadding(const QMargins &padding);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  QCPItemPosition * const position;
+  QCPItemAnchor * const topLeft;
+  QCPItemAnchor * const top;
+  QCPItemAnchor * const topRight;
+  QCPItemAnchor * const right;
+  QCPItemAnchor * const bottomRight;
+  QCPItemAnchor * const bottom;
+  QCPItemAnchor * const bottomLeft;
+  QCPItemAnchor * const left;
+
+protected:
+  enum AnchorIndex {aiTopLeft, aiTop, aiTopRight, aiRight, aiBottomRight, aiBottom, aiBottomLeft, aiLeft};
+
+  // property members:
+  QColor mColor, mSelectedColor;
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+  QFont mFont, mSelectedFont;
+  QString mText;
+  Qt::Alignment mPositionAlignment;
+  Qt::Alignment mTextAlignment;
+  double mRotation;
+  QMargins mPadding;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+
+  // non-virtual methods:
+  QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const;
+  QFont mainFont() const;
+  QColor mainColor() const;
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+};
+
+
+class QCP_LIB_DECL QCPItemEllipse : public QCPAbstractItem
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
+  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
+  /// \endcond
+public:
+  QCPItemEllipse(QCustomPlot *parentPlot);
+  virtual ~QCPItemEllipse();
+
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  QCPItemPosition * const topLeft;
+  QCPItemPosition * const bottomRight;
+  QCPItemAnchor * const topLeftRim;
+  QCPItemAnchor * const top;
+  QCPItemAnchor * const topRightRim;
+  QCPItemAnchor * const right;
+  QCPItemAnchor * const bottomRightRim;
+  QCPItemAnchor * const bottom;
+  QCPItemAnchor * const bottomLeftRim;
+  QCPItemAnchor * const left;
+  QCPItemAnchor * const center;
+
+protected:
+  enum AnchorIndex {aiTopLeftRim, aiTop, aiTopRightRim, aiRight, aiBottomRightRim, aiBottom, aiBottomLeftRim, aiLeft, aiCenter};
+
+  // property members:
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+
+  // non-virtual methods:
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+};
+
+
+class QCP_LIB_DECL QCPItemPixmap : public QCPAbstractItem
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
+  Q_PROPERTY(bool scaled READ scaled WRITE setScaled)
+  Q_PROPERTY(Qt::AspectRatioMode aspectRatioMode READ aspectRatioMode)
+  Q_PROPERTY(Qt::TransformationMode transformationMode READ transformationMode)
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  /// \endcond
+public:
+  QCPItemPixmap(QCustomPlot *parentPlot);
+  virtual ~QCPItemPixmap();
+
+  // getters:
+  QPixmap pixmap() const { return mPixmap; }
+  bool scaled() const { return mScaled; }
+  Qt::AspectRatioMode aspectRatioMode() const { return mAspectRatioMode; }
+  Qt::TransformationMode transformationMode() const { return mTransformationMode; }
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+
+  // setters;
+  void setPixmap(const QPixmap &pixmap);
+  void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation);
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  QCPItemPosition * const topLeft;
+  QCPItemPosition * const bottomRight;
+  QCPItemAnchor * const top;
+  QCPItemAnchor * const topRight;
+  QCPItemAnchor * const right;
+  QCPItemAnchor * const bottom;
+  QCPItemAnchor * const bottomLeft;
+  QCPItemAnchor * const left;
+
+protected:
+  enum AnchorIndex {aiTop, aiTopRight, aiRight, aiBottom, aiBottomLeft, aiLeft};
+
+  // property members:
+  QPixmap mPixmap;
+  QPixmap mScaledPixmap;
+  bool mScaled;
+  Qt::AspectRatioMode mAspectRatioMode;
+  Qt::TransformationMode mTransformationMode;
+  QPen mPen, mSelectedPen;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+
+  // non-virtual methods:
+  void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false);
+  QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const;
+  QPen mainPen() const;
+};
+
+
+class QCP_LIB_DECL QCPItemTracer : public QCPAbstractItem
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  Q_PROPERTY(QBrush brush READ brush WRITE setBrush)
+  Q_PROPERTY(QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush)
+  Q_PROPERTY(double size READ size WRITE setSize)
+  Q_PROPERTY(TracerStyle style READ style WRITE setStyle)
+  Q_PROPERTY(QCPGraph* graph READ graph WRITE setGraph)
+  Q_PROPERTY(double graphKey READ graphKey WRITE setGraphKey)
+  Q_PROPERTY(bool interpolating READ interpolating WRITE setInterpolating)
+  /// \endcond
+public:
+  /*!
+    The different visual appearances a tracer item can have. Some styles size may be controlled with \ref setSize.
+
+    \see setStyle
+  */
+  enum TracerStyle { tsNone        ///< The tracer is not visible
+                     ,tsPlus       ///< A plus shaped crosshair with limited size
+                     ,tsCrosshair  ///< A plus shaped crosshair which spans the complete axis rect
+                     ,tsCircle     ///< A circle
+                     ,tsSquare     ///< A square
+                   };
+  Q_ENUMS(TracerStyle)
+
+  QCPItemTracer(QCustomPlot *parentPlot);
+  virtual ~QCPItemTracer();
+
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  QBrush brush() const { return mBrush; }
+  QBrush selectedBrush() const { return mSelectedBrush; }
+  double size() const { return mSize; }
+  TracerStyle style() const { return mStyle; }
+  QCPGraph *graph() const { return mGraph; }
+  double graphKey() const { return mGraphKey; }
+  bool interpolating() const { return mInterpolating; }
+
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setBrush(const QBrush &brush);
+  void setSelectedBrush(const QBrush &brush);
+  void setSize(double size);
+  void setStyle(TracerStyle style);
+  void setGraph(QCPGraph *graph);
+  void setGraphKey(double key);
+  void setInterpolating(bool enabled);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  // non-virtual methods:
+  void updatePosition();
+
+  QCPItemPosition * const position;
+
+protected:
+  // property members:
+  QPen mPen, mSelectedPen;
+  QBrush mBrush, mSelectedBrush;
+  double mSize;
+  TracerStyle mStyle;
+  QCPGraph *mGraph;
+  double mGraphKey;
+  bool mInterpolating;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+
+  // non-virtual methods:
+  QPen mainPen() const;
+  QBrush mainBrush() const;
+};
+
+
+class QCP_LIB_DECL QCPItemBracket : public QCPAbstractItem
+{
+  Q_OBJECT
+  /// \cond INCLUDE_QPROPERTIES
+  Q_PROPERTY(QPen pen READ pen WRITE setPen)
+  Q_PROPERTY(QPen selectedPen READ selectedPen WRITE setSelectedPen)
+  Q_PROPERTY(double length READ length WRITE setLength)
+  Q_PROPERTY(BracketStyle style READ style WRITE setStyle)
+  /// \endcond
+public:
+  enum BracketStyle { bsSquare  ///< A brace with angled edges
+                      ,bsRound  ///< A brace with round edges
+                      ,bsCurly  ///< A curly brace
+                      ,bsCalligraphic ///< A curly brace with varying stroke width giving a calligraphic impression
+  };
+
+  QCPItemBracket(QCustomPlot *parentPlot);
+  virtual ~QCPItemBracket();
+
+  // getters:
+  QPen pen() const { return mPen; }
+  QPen selectedPen() const { return mSelectedPen; }
+  double length() const { return mLength; }
+  BracketStyle style() const { return mStyle; }
+
+  // setters;
+  void setPen(const QPen &pen);
+  void setSelectedPen(const QPen &pen);
+  void setLength(double length);
+  void setStyle(BracketStyle style);
+
+  // reimplemented virtual methods:
+  virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const;
+
+  QCPItemPosition * const left;
+  QCPItemPosition * const right;
+  QCPItemAnchor * const center;
+
+protected:
+  // property members:
+  enum AnchorIndex {aiCenter};
+  QPen mPen, mSelectedPen;
+  double mLength;
+  BracketStyle mStyle;
+
+  // reimplemented virtual methods:
+  virtual void draw(QCPPainter *painter);
+  virtual QPointF anchorPixelPoint(int anchorId) const;
+
+  // non-virtual methods:
+  QPen mainPen() const;
+};
+
+#endif // QCUSTOMPLOT_H
+
diff --git a/Code/src/sed.cpp b/Code/src/sed.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f9be04e424d42850441deb4d8c0b5df9f258dc8a
--- /dev/null
+++ b/Code/src/sed.cpp
@@ -0,0 +1,186 @@
+#include "sed.h"
+#include <QDebug>
+
+SED::SED(QObject *parent) :
+    QObject(parent)
+{
+    rootSetted=false;
+}
+
+void SED::setRootNode(SEDNode *node)
+{
+    rootNode=node;
+    rootSetted=true;
+}
+
+void SED::updateMaxFlux(double f)
+{
+    if (f>maxFlux)
+        maxFlux=f;
+}
+
+void SED::scaleFlux()
+{
+    /*
+     * Alle tre lunghezze d’onda (250, 350, 500) si calcola il beam di Herschel come
+     *
+     *  beam(lambda)=lambda/14.
+     * - Alle tre lunghezze d’onda si “circolarizza” la gaussiana come segue:
+     *
+     * fwhm_circ(lambda) = SQRT(fwhm_1(lambda)*fwhm_2(lambda))
+     *
+     * - Alle tre lunghezze d’onda si calcola il size deconvoluto della sorgente:
+     *  fwhm_decon(lambda) = SQRT(fwhm_circ(lambda)^2-beam(lambda)^2).
+     *  Occhio, noi questo passaggio lo facciamo in genere solo quando fwhm_circ(lambda) > SQRT(2.)*beam(lambda)
+     *   altrimenti diciamo che la sorgente non è completamente risolta (e non facciamo lo scaling).
+     *   - Infine, alle ultime due lunghezze d’onda (350, 500) operiamo lo scaling,
+     *   a partire dal flusso originale dei cataloghi a singola banda:
+     *    F_scal(lambda)= fwhm_decon(250)/fwhm_decon(lambda)*F_orig(lambda).
+     *   In pratica, il fattore di scaling è il rapporto tra i size
+     *   (circolarizzati e deconvoluti) a 250 e a 350-oppure-500.
+     *   Questo rapporto è atteso essere < 1, ma in Hi-GAL capita di trovare di tutto,
+     *   per cui quando il rapporto è > 1 lasciamo il flusso invariato e amen.
+     */
+
+    if(setted250)
+    {
+
+    }
+}
+
+/*
+ void SED::printAll(SEDNode* node){
+    if(node->getChild().count()!=0){
+        qDebug()<<node->getDesignation();
+        for(int i=0;i<node->getChild().count();i++)
+        {
+            printAll(node->getChild().values()[i]);
+        }
+    }else{
+        qDebug()<<node->getDesignation();
+    }
+}
+*/
+
+void SED::printAll(SEDNode* node){
+    QList<SEDNode *> stack;
+    stack.push_back(node);
+    while(!stack.isEmpty()){
+        SEDNode * curr=stack.at(0);
+        stack.removeFirst();
+        qDebug()<<curr->getDesignation();
+        for(int i=0;i<curr->getChild().count();i++)
+        {
+            stack.push_back(curr->getChild().values()[i]);
+        }
+    }
+}
+
+
+void SED::printSelf()
+{
+    printAll(rootNode);
+    /*
+    qDebug()<<"**********";
+    qDebug()<<rootNode->getDesignation();
+    for(int i=0;i<rootNode->getChild().count();i++ )
+    {
+        qDebug()<<"\t"<<rootNode->getChild().values()[i]->getDesignation();
+        for(int j=0;j<rootNode->getChild().values()[i]->getChild().count();j++ )
+        {
+            qDebug()<<"\t\t"<<rootNode->getChild().values()[i]->getChild().values()[j]->getDesignation();
+
+        }
+    }*/
+}
+
+QDataStream &operator<<(QDataStream &out, SED* sed)
+{
+    qDebug() << "printing SED";
+    /*
+    out << sed->getRootNode();
+    out << sed->getRootNode()->getChild().count();
+    qDebug()<<"RootNode has childs: "<<QString::number(sed->getRootNode()->getChild().count());
+
+     for(int i=0;i<sed->getRootNode()->getChild().count();i++ )
+    {
+        out<<sed->getRootNode()->getChild().values()[i];
+
+        out<<sed->getRootNode()->getChild().values()[i]->getChild().count();
+
+        qDebug()<<"Child "<<QString::number(i)<<" of RootNode has childs: "<<QString::number(sed->getRootNode()->getChild().values()[i]->getChild().count());
+        for(int j=0;j<sed->getRootNode()->getChild().values()[i]->getChild().count();j++ )
+        {
+            out<<sed->getRootNode()->getChild().values()[i]->getChild().values()[j];
+        }
+    }
+    */
+
+
+    QList<SEDNode *> stack;
+    stack.push_back(sed->getRootNode());
+    out<<sed->getRootNode();
+
+    while(!stack.isEmpty()){
+        SEDNode * curr=stack.at(0);
+        qDebug()<<curr->getDesignation();
+        out<<curr->getChild().count();
+        qDebug()<<"child: "<<curr->getChild().count();
+        stack.removeFirst();
+
+        for(int i=0;i<curr->getChild().count();i++)
+        {
+            out<<curr->getChild().values()[i];
+            stack.push_back(curr->getChild().values()[i]);
+        }
+    }
+    return out;
+}
+
+
+QDataStream &operator>>(QDataStream &in, SED* sed)
+{
+    qDebug()<<"reading SED";
+    SEDNode *rootNode=new SEDNode();
+    //sed=new SED();
+    in >> rootNode;
+    sed->setRootNode(rootNode);
+    int count, count2;
+    /*
+    qDebug()<<sed->getRootNode()->getDesignation();
+
+    in >> count;
+    qDebug()<<"RootNode has childs: "<<QString::number(count);
+    for(int i=0;i<count;i++ )
+    {
+        SEDNode *childNode=new SEDNode();
+        in>>childNode;
+        sed->getRootNode()->setChild(childNode);
+        in >> count2;
+        qDebug()<<"Child "<<QString::number(i)<<" of RootNode has childs: "<<QString::number(count2);
+        for(int j=0;j<count2;j++ )
+        {
+            SEDNode *childNode=new SEDNode();
+            in>>childNode;
+            sed->getRootNode()->getChild().values()[i]->setChild(childNode);
+        }
+    }
+    */
+
+    QList<SEDNode *> stack;
+    stack.push_back(sed->getRootNode());
+    while(!stack.isEmpty()){
+        SEDNode * curr=stack.at(0);
+        in>>count;
+        stack.removeFirst();
+        for(int i=0;i<count;i++)
+        {
+            SEDNode* temp=new SEDNode();
+            in >> temp;
+            curr->setChild(temp);
+            stack.push_back(temp);
+        }
+    }
+    return in;
+}
+
diff --git a/Code/src/sed.h b/Code/src/sed.h
new file mode 100644
index 0000000000000000000000000000000000000000..c02009a47429ec7a1f6af0c5449a95d2e8af888f
--- /dev/null
+++ b/Code/src/sed.h
@@ -0,0 +1,72 @@
+#ifndef SED_H
+#define SED_H
+
+#include <QObject>
+#include <QHash>
+#include <QDataStream>
+#include "sednode.h"
+
+class SED : public QObject
+{
+    Q_OBJECT
+public:
+    explicit SED(QObject *parent = 0);
+    void set21a( QHash <QString, int> bm){bm21a=bm;}
+    void set21c( QHash <QString, int> bm){bm21c=bm;}
+    void set21d( QHash <QString, int> bm){bm21d=bm;}
+    void set21e( QHash <QString, int> bm){bm21e=bm;}
+    void set21( QHash <QString, int> bm){bm21=bm;}
+    void set22( QHash <QString, int> bm){bm22=bm;}
+    void set24( QHash <QString, int> bm){bm24=bm;}
+    void set70 ( QHash <QString, int> bm){bm70=bm; }
+    void set160( QHash <QString, int> bm){bm160=bm;}
+    void set250( QHash <QString, int> bm){bm250=bm;}
+    void set350( QHash <QString, int> bm){bm350=bm;}
+    void set500( QHash <QString, int> bm){bm500=bm;}
+    void set870( QHash <QString, int> bm){bm870=bm;}
+    void set1100( QHash <QString, int> bm){bm1100=bm;}
+    void setRootNode(SEDNode *node);
+    bool hasRoot(){return rootSetted;}
+    bool has250(){return setted250;}
+    void set250node( ){setted250=true;}
+    void updateMaxFlux(double f);
+    void printSelf();
+    double getMaxFlux(){return maxFlux;}
+    void scaleFlux();
+    void printAll(SEDNode* node);
+
+    SEDNode *   getRootNode(){return rootNode;}
+    SEDNode *rootNode;
+signals:
+
+public slots:
+
+private:
+    QHash <QString, int> bm1100;
+    QHash <QString, int> bm870;
+    QHash <QString, int> bm500;
+    QHash <QString, int> bm350;
+    QHash <QString, int> bm250;
+    QHash <QString, int> bm160;
+    QHash <QString, int> bm70;
+    QHash <QString, int> bm24;
+    QHash <QString, int> bm22;
+    QHash <QString, int> bm21;
+    QHash <QString, int> bm21e;
+    QHash <QString, int> bm21d;
+    QHash <QString, int> bm21c;
+    QHash <QString, int> bm21a;
+
+
+
+    bool rootSetted;
+    bool setted250;
+    double maxFlux;
+
+
+};
+
+QDataStream &operator<<(QDataStream& out, SED* sed);
+QDataStream &operator>>(QDataStream& in, SED* sed);
+
+#endif // SED_H
diff --git a/Code/src/sedfitgrid_thick.cpp b/Code/src/sedfitgrid_thick.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6fa14526a8cb592f608c64637dbfebc7483a12cc
--- /dev/null
+++ b/Code/src/sedfitgrid_thick.cpp
@@ -0,0 +1,20 @@
+#include "sedfitgrid_thick.h"
+#include "ui_sedfitgrid_thick.h"
+
+SedFitgrid_thick::SedFitgrid_thick(SEDVisualizerPlot *s, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::SedFitgrid_thick)
+{
+    ui->setupUi(this);
+     sedwin=s;
+}
+
+SedFitgrid_thick::~SedFitgrid_thick()
+{
+    delete ui;
+}
+
+void SedFitgrid_thick::closeEvent(QCloseEvent *event)
+{
+    sedwin->activateWindow();
+}
diff --git a/Code/src/sedfitgrid_thick.h b/Code/src/sedfitgrid_thick.h
new file mode 100644
index 0000000000000000000000000000000000000000..e1f7e6129fa6398c70cfbfab3b3d990d54cd44c2
--- /dev/null
+++ b/Code/src/sedfitgrid_thick.h
@@ -0,0 +1,29 @@
+#ifndef SEDFITGRID_THICK_H
+#define SEDFITGRID_THICK_H
+
+#include <QWidget>
+#include "sedvisualizerplot.h"
+
+class SEDVisualizerPlot;
+
+namespace Ui {
+class SedFitgrid_thick;
+}
+
+class SedFitgrid_thick : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit SedFitgrid_thick(SEDVisualizerPlot *s, QWidget *parent = 0);
+    ~SedFitgrid_thick();
+    Ui::SedFitgrid_thick *ui;
+
+private slots:
+    void closeEvent(QCloseEvent *event);
+
+private:
+    SEDVisualizerPlot *sedwin;
+};
+
+#endif // SEDFITGRID_THICK_H
diff --git a/Code/src/sedfitgrid_thin.cpp b/Code/src/sedfitgrid_thin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b355239f4a5960b542650644ad6d5966f4017585
--- /dev/null
+++ b/Code/src/sedfitgrid_thin.cpp
@@ -0,0 +1,25 @@
+#include "sedfitgrid_thin.h"
+#include "ui_sedfitgrid_thin.h"
+
+SedFitGrid_thin::SedFitGrid_thin(SEDVisualizerPlot *s, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::SedFitGrid_thin)
+{
+    ui->setupUi(this);
+    sedwin=s;
+}
+
+SedFitGrid_thin::~SedFitGrid_thin()
+{
+    delete ui;
+}
+
+void SedFitGrid_thin::on_pushButton_clicked()
+{
+
+}
+
+void SedFitGrid_thin::closeEvent(QCloseEvent *event)
+{
+    sedwin->activateWindow();
+}
diff --git a/Code/src/sedfitgrid_thin.h b/Code/src/sedfitgrid_thin.h
new file mode 100644
index 0000000000000000000000000000000000000000..ca077e09d233d87c0384aa481861fbb91c7326bd
--- /dev/null
+++ b/Code/src/sedfitgrid_thin.h
@@ -0,0 +1,32 @@
+#ifndef SEDFITGRID_THIN_H
+#define SEDFITGRID_THIN_H
+
+#include <QWidget>
+#include "sedvisualizerplot.h"
+
+class SEDVisualizerPlot;
+
+namespace Ui {
+class SedFitGrid_thin;
+}
+
+class SedFitGrid_thin : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit SedFitGrid_thin(SEDVisualizerPlot *s, QWidget *parent = 0);
+    ~SedFitGrid_thin();
+    Ui::SedFitGrid_thin *ui;
+
+
+private slots:
+    void on_pushButton_clicked();
+    void closeEvent(QCloseEvent *event);
+
+
+private:
+    SEDVisualizerPlot *sedwin;
+};
+
+#endif // SEDFITGRID_THIN_H
diff --git a/Code/src/sednode.cpp b/Code/src/sednode.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc89bb8d78de1adfe23f9a7f881407642e9467b4
--- /dev/null
+++ b/Code/src/sednode.cpp
@@ -0,0 +1,101 @@
+#include "sednode.h"
+#include <qdebug.h>
+#include <QDataStream>
+
+SEDNode::SEDNode(QObject *parent) :
+    QObject(parent)
+{
+
+}
+
+void SEDNode::setSky(float lon, float lat)
+{
+
+    glon=lon;
+    glat=lat;
+
+}
+
+void SEDNode::setXY(double x, double y)
+{
+    image_x=x;
+    image_y=y;
+}
+
+void SEDNode::setChild(SEDNode *node)
+{
+    childNodes.insert(node->getDesignation(),node);
+}
+
+void SEDNode::setParent(SEDNode *node)
+{
+    parentNodes.insert(node->getDesignation(),node);
+}
+
+void SEDNode::setErrFlux(double f)
+{
+    e_flux=f;
+}
+
+void SEDNode::setFlux(double f)
+{
+    flux=f;
+}
+
+bool SEDNode::hasChild()
+{
+    return childNodes.count()>0;
+}
+
+void  SEDNode::setEllipse( double smin, double smax,double a, double ar)
+{
+
+
+    semiMajorAxisLength= smax;
+    semiMinorAxisLength=smin;
+    angle=a;
+    arcpixel=ar;
+
+}
+
+QDataStream &operator<<(QDataStream &out, SEDNode* node)
+{
+    qDebug()<< "printing SEDNode";
+    out << node->getDesignation();
+    qDebug()<<node->getDesignation();
+    out << node->getWavelength();
+    out << node->getFlux();
+    out << node->getErrFlux();
+    out << node->getLat();
+    out << node->getLon();
+    return out;
+}
+
+
+QDataStream &operator>>(QDataStream &in, SEDNode* node)
+{
+    qDebug()<< "reading SEDNode";
+    QString d;
+    int w;
+    double f, ef;
+    float lat, lon;
+    in >> d;
+    in >> w;
+    in >> f;
+    in >> ef;
+    in >> lat;
+    in >> lon;
+
+    //node=new SEDNode();
+    node->setDesignation(d);
+    node->setWavelength(w);
+    node->setFlux(f);
+    node->setErrFlux(ef);
+    node->setSky(lon, lat);
+    qDebug()<<node->getDesignation();
+    //in >> node->getWavelength();
+    //in >> node->getFlux();
+    return in;
+}
+
+
diff --git a/Code/src/sednode.h b/Code/src/sednode.h
new file mode 100644
index 0000000000000000000000000000000000000000..5071213e67d67a906f802fa6eec00a4a9ebae862
--- /dev/null
+++ b/Code/src/sednode.h
@@ -0,0 +1,72 @@
+#ifndef SEDNODE_H
+#define SEDNODE_H
+
+#include <QObject>
+#include <QHash>
+#include <QDataStream>
+
+class SEDNode : public QObject
+{
+    Q_OBJECT
+public:
+    explicit SEDNode(QObject *parent = 0);
+    void setDesignation(QString d){designation=d;}
+    QString getDesignation(){return designation;}
+    void setChild(SEDNode *node);
+    void setParent(SEDNode *node);
+
+
+    QHash < QString,SEDNode*> getChild(){return childNodes;}
+    QHash < QString,SEDNode*> getParent(){return parentNodes;}
+    void setFlux(double f);
+    void setErrFlux(double f);
+    double getFlux() {return flux;}
+    double getErrFlux() {return e_flux;}
+    double getWavelength() {return wavelength;}
+    void setWavelength(double w ) {wavelength=w;}
+    bool hasChild();
+
+    void setSky(float lon, float lat);
+    void setXY(double x, double y);
+    float getLon() {return glon;}
+    float getLat() {return glat;}
+    double getX() {return image_x;}
+    double getY() {return image_y;}
+
+    double getSemiMajorAxisLength() {return semiMajorAxisLength;}
+    double getSemiMinorAxisLength() {return semiMinorAxisLength;}
+    double getAngle() {return angle;}
+    double getArcpix() {return arcpixel;}
+
+    void setEllipse( double smin, double smax,double a,double ar);
+    QString designation;
+
+
+signals:
+
+public slots:
+
+
+private:
+    QHash < QString,SEDNode*> parentNodes;
+    QHash < QString,SEDNode*> childNodes;
+    double flux;
+    double e_flux;
+    double wavelength;
+
+    float glon;//=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("GLON")][j].c_str());
+    float glat;//dec=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("GLAT")][j].c_str());
+    double image_x;
+    double image_y;
+
+    double semiMajorAxisLength;
+    double semiMinorAxisLength;
+    double angle;
+    double arcpixel;
+
+};
+
+QDataStream &operator<<(QDataStream& out, SEDNode* node);
+QDataStream &operator>>(QDataStream& in, SEDNode* node);
+
+#endif // SEDNODE_H
diff --git a/Code/src/sedplotpointcustom.cpp b/Code/src/sedplotpointcustom.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb5de577bfd4787529ea89508101093e74aaa369
--- /dev/null
+++ b/Code/src/sedplotpointcustom.cpp
@@ -0,0 +1,309 @@
+#include "sedplotpointcustom.h"
+#include <QMouseEvent>
+#include <QToolTip>
+#include "vtkellipse.h"
+#include "vtkCleanPolyData.h"
+#include "vialacteastringdictwidget.h"
+#include "singleton.h"
+
+SEDPlotPointCustom::SEDPlotPointCustom(QCustomPlot *parentPlot, double halfSize, vtkwindow_new *v)
+    : QCPItemEllipse(parentPlot)
+    , mCenterTracer(new QCPItemTracer(parentPlot))
+    , mGripDelta()
+    , mInitialPos()
+    , mLastWantedPos()
+    , mMoveTimer(new QTimer(this))
+    , mCurWantedPosPx()
+{
+    mCenterTracer->setStyle(QCPItemTracer::tsNone);
+
+    topLeft->setParentAnchor(mCenterTracer->position);
+    bottomRight->setParentAnchor(mCenterTracer->position);
+    topLeft->setType(QCPItemPosition::ptAbsolute);
+    bottomRight->setType(QCPItemPosition::ptAbsolute);
+
+    topLeft->setCoords(-halfSize, -halfSize);
+    bottomRight->setCoords(halfSize, halfSize);
+
+    setSelectable(true); // plot moves only selectable points, see Plot::mouseMoveEvent
+    setColor(QColor(Qt::yellow));
+    setPen(QPen(Qt::black));
+    setSelectedPen(QPen(Qt::red, halfSize));
+
+    vtkwin = v;
+
+    stringDictWidget = &Singleton<VialacteaStringDictWidget>::Instance();
+
+    connect(this,SIGNAL(selectionChanged(bool)),this, SLOT(selected(bool)));
+
+}
+
+void SEDPlotPointCustom::selected(bool s)
+{
+    vtkEllipse *el;
+    if(s)
+    {
+
+        qDebug()<<"semiMajorAxisLength: "<<semiMajorAxisLength;
+        qDebug()<<"semiMinorAxisLength: "<<semiMinorAxisLength;
+        qDebug()<<"angle: "<<angle;
+        qDebug()<<"image_x: "<<image_x;
+        qDebug()<<"image_y: "<<image_y;
+        qDebug()<<"arcpix: "<<arcpix;
+        qDebug()<<"designation: "<<designation;
+        qDebug()<<"error: "<<designation;
+
+        if(image_x!=0 && image_y!=0 && designation.compare("")!=0)
+        {
+            QString wav=QString::number(pos().x());
+            qDebug()<<"vlkb_compactsources.sed_view_final.designation."<<wav <<" "<<stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designation"+wav);
+
+            //QToolTip::showText(QCursor::pos(), QString("designation: %1\nwavelength: %2\nfint: %3\nerr_fint: %8\nglon: %4\nglat: %5\nx: %6\ny:%7").arg(designation).arg(pos().x()).arg(pos().y()).arg(glon).arg(glat).arg(image_x).arg(image_y).arg(error_flux));
+            QToolTip::showText(QCursor::pos(), QString(stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designation"+wav)+
+                                                       ":\n%1\nwavelength: %2\nfint: %3\nerr_fint: %8\nglon: %4\nglat: %5\nx: %6\ny:%7").arg(designation).arg(pos().x()).arg(pos().y()).arg(glon).arg(glat).arg(image_x).arg(image_y).arg(error_flux));
+            el= new vtkEllipse(semiMajorAxisLength/2,semiMinorAxisLength/2,angle, image_x, image_y, arcpix, 0,0, designation, NULL);
+            drawSingleEllipse(el);
+        }
+        else
+        {
+            QToolTip::showText(QCursor::pos(), QString("wavelength: %1\nfint: %2\nerr_fin: %3").arg(pos().x()).arg(pos().y()).arg(error_flux));
+        }
+    }
+    else
+    {
+        if(vtkwin!=0){
+        vtkwin->removeSingleEllipse(ellipseActor);
+        }
+    }
+    isSelected=s;
+}
+
+void SEDPlotPointCustom::drawSingleEllipse(vtkEllipse * ellipse )
+{
+
+    vtkSmartPointer<vtkCleanPolyData> cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
+#if VTK_MAJOR_VERSION <= 5
+    cleanFilter->SetInput(ellipse->getPolyData());
+#else
+    cleanFilter->SetInputData(ellipse->getPolyData());
+#endif
+
+    cleanFilter->Update();
+
+    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputConnection(cleanFilter->GetOutputPort());
+
+    ellipseActor = vtkSmartPointer<vtkLODActor>::New();
+    ellipseActor->SetMapper(mapper);
+
+    ellipseActor->GetProperty()->SetColor(0, 0, 0);
+
+    if(vtkwin!=0){
+    vtkwin->drawSingleEllipse(ellipseActor);
+    }
+
+
+}
+
+QPointF SEDPlotPointCustom::pos() const
+{
+    return mCenterTracer->position->coords();
+}
+
+const QColor &SEDPlotPointCustom::color() const
+{
+    return brush().color();
+}
+
+void SEDPlotPointCustom::setColor(const QColor& color)
+{
+    setBrush(color);
+    setSelectedBrush(color);
+}
+
+void SEDPlotPointCustom::startMoving(const QPointF &mousePos, bool shiftIsPressed)
+{
+    /*
+    qDebug()<<"startMoving";
+    mGripDelta.setX(parentPlot()->xAxis->coordToPixel(mCenterTracer->position->key()) - mousePos.x());
+    mGripDelta.setY(parentPlot()->yAxis->coordToPixel(mCenterTracer->position->value()) - mousePos.y());
+
+    mInitialPos = pos();
+    mLastWantedPos = mInitialPos;
+    mCurWantedPosPx = QPointF();
+    mIsChangingOnlyOneCoordinate = shiftIsPressed;
+
+    mMoveTimer->start();
+
+    QCPItemStraightLine *horizontal = new QCPItemStraightLine(parentPlot());
+    horizontal->setAntialiased(false);
+    horizontal->point1->setCoords(mInitialPos.x(), mInitialPos.y());
+    horizontal->point2->setCoords(mInitialPos.x() + 1, mInitialPos.y());
+    parentPlot()->addItem(horizontal);
+
+    QCPItemStraightLine *vertical = new QCPItemStraightLine(parentPlot());
+    vertical->setAntialiased(false);
+    vertical->point1->setCoords(mInitialPos.x(), mInitialPos.y());
+    vertical->point2->setCoords(mInitialPos.x(), mInitialPos.y() + 1);
+    parentPlot()->addItem(vertical);
+
+    static const QPen linesPen(Qt::darkGray, 0, Qt::DashLine);
+    horizontal->setPen(linesPen);
+    vertical->setPen(linesPen);
+
+    mHelperItems << vertical << horizontal;
+
+    if (!mIsChangingOnlyOneCoordinate) {
+        vertical->setVisible(false);
+        horizontal->setVisible(false);
+    }
+
+    connect(parentPlot(), SIGNAL(mouseMove(QMouseEvent*)),
+            this, SLOT(onMouseMove(QMouseEvent*)));
+
+    connect(parentPlot(), SIGNAL(mouseRelease(QMouseEvent*)),
+            this, SLOT(stopMoving()));
+
+    connect(parentPlot(), SIGNAL(shiftStateChanged(bool)),
+            this, SLOT(onShiftStateChanged(bool)));
+
+    parentPlot()->grabKeyboard();
+    QApplication::setOverrideCursor(Qt::ClosedHandCursor);
+    */
+}
+
+void SEDPlotPointCustom::setVisible(bool on)
+{
+    setSelectable(on);  // we are movable only when visible
+    QCPItemEllipse::setVisible(on);
+}
+
+void SEDPlotPointCustom::stopMoving()
+{
+    /*
+    disconnect(parentPlot(), SIGNAL(mouseMove(QMouseEvent*)),
+            this, SLOT(onMouseMove(QMouseEvent*)));
+
+    disconnect(parentPlot(), SIGNAL(mouseRelease(QMouseEvent*)),
+            this, SLOT(stopMoving()));
+
+    disconnect(parentPlot(), SIGNAL(shiftStateChanged(bool)),
+            this, SLOT(onShiftStateChanged(bool)));
+
+    mMoveTimer->stop();
+    moveToWantedPos();
+
+    if (!mHelperItems.isEmpty()) {
+        while (!mHelperItems.isEmpty()) {
+            QCPAbstractItem *item = mHelperItems.takeFirst();
+            mParentPlot->removeItem(item);
+        }
+
+        mParentPlot->replot();
+    }
+
+    parentPlot()->releaseKeyboard();
+    QApplication::restoreOverrideCursor();
+
+    emit stoppedMoving();
+    */
+}
+
+void SEDPlotPointCustom::setPos(double x, double y)
+{
+    mCenterTracer->position->setCoords(x, y);
+    parentPlot()->replot();
+}
+void SEDPlotPointCustom::move(double x, double y, bool signalNeeded)
+{
+    /*
+    mLastWantedPos.setX(x);
+    mLastWantedPos.setY(y);
+    if (mIsChangingOnlyOneCoordinate) {
+        double x1 = parentPlot()->xAxis->coordToPixel(x);
+        double x2 = parentPlot()->xAxis->coordToPixel(mInitialPos.x());
+        double y1 = parentPlot()->yAxis->coordToPixel(y);
+        double y2 = parentPlot()->yAxis->coordToPixel(mInitialPos.y());
+        if (qAbs(x1 - x2) < qAbs(y1 - y2)) {
+            x = mInitialPos.x();
+        } else {
+            y = mInitialPos.y();
+        }
+    }
+
+    mCenterTracer->position->setCoords(x, y);
+
+    parentPlot()->replot();
+
+    if(signalNeeded) {
+        emit moved(QPointF(x, y));
+    }
+    */
+}
+
+void SEDPlotPointCustom::movePx(double x, double y)
+{
+    move(parentPlot()->xAxis->pixelToCoord(x),
+         parentPlot()->yAxis->pixelToCoord(y));
+}
+
+void SEDPlotPointCustom::setActive(bool isActive)
+{
+    /*
+    qDebug()<<"setActive";
+
+    setSelected(isActive);
+    emit (isActive ? activated() : disactivated());
+*/
+}
+
+bool SEDPlotPointCustom::selected(){
+    return isSelected;
+}
+
+void SEDPlotPointCustom::onMousePress(QMouseEvent *event){
+    selected(true);
+    bool b=this->selected();
+    qDebug()<<"Click on SEDPlotPointCustom "<<b;
+}
+
+void SEDPlotPointCustom::onMouseMove(QMouseEvent *event)
+{
+    /*
+
+    qDebug()<<"mousemove";
+
+    mCurWantedPosPx = QPointF(event->pos().x() + mGripDelta.x(),
+                              event->pos().y() + mGripDelta.y());
+                              */
+}
+
+void SEDPlotPointCustom::moveToWantedPos()
+{
+    /*
+    if (!mCurWantedPosPx.isNull()) {
+        movePx(mCurWantedPosPx.x(), mCurWantedPosPx.y());
+        mCurWantedPosPx = QPointF();
+    }
+    */
+}
+
+void SEDPlotPointCustom::onShiftStateChanged(bool shiftPressed)
+{
+    if (shiftPressed != mIsChangingOnlyOneCoordinate) {
+        mIsChangingOnlyOneCoordinate = shiftPressed;
+        foreach (QCPAbstractItem *item, mHelperItems) {
+            item->setVisible(shiftPressed);
+        }
+        move(mLastWantedPos.x(), mLastWantedPos.y());
+    }
+}
+
+
+void  SEDPlotPointCustom::setEllipse(double smin, double smax, double a, double ar)
+{
+    semiMajorAxisLength= smin;
+    semiMinorAxisLength=smax;
+    angle=a;
+    arcpix=ar;
+}
diff --git a/Code/src/sedplotpointcustom.h b/Code/src/sedplotpointcustom.h
new file mode 100644
index 0000000000000000000000000000000000000000..f29112a0408619c93703792e609795150189723e
--- /dev/null
+++ b/Code/src/sedplotpointcustom.h
@@ -0,0 +1,96 @@
+#ifndef SEDPLOTPOINTCUSTOM_H
+#define SEDPLOTPOINTCUSTOM_H
+
+#include "qcustomplot.h"
+#include <QTimer>
+#include <QApplication>
+#include <QMouseEvent>
+#include "vtkwindow_new.h"
+#include "vtkellipse.h"
+#include "sednode.h"
+#include "vialacteastringdictwidget.h"
+
+class SEDPlotPointCustom : public QCPItemEllipse
+{
+    Q_OBJECT
+public:
+    explicit SEDPlotPointCustom(QCustomPlot *parentPlot, double halfSize = 5, vtkwindow_new *v=NULL);
+
+    QPointF pos() const;
+    const QColor &color() const;
+    void setColor(const QColor &color);
+    void startMoving(const QPointF &mousePos, bool shiftIsPressed);
+    void setDesignation(QString d){designation=d;}
+    void setLon(double lon){glon=lon;}
+    void setLat(double lat){glat=lat;}
+    void setX(double x){image_x=x;}
+    void setY(double y){image_y=y;}
+    void setErrorFlux(double ef){error_flux=ef;}
+    void setNode(SEDNode *n){sedNode=n;}
+
+    double getSemiMajorAxisLength(){return semiMajorAxisLength;}
+    double getSemiMinorAxisLength(){return semiMinorAxisLength;}
+    double getAngle(){return angle;}
+    SEDNode * getNode(){return sedNode;}
+
+    void setEllipse( double smin, double smax,double a, double ar);
+
+
+public slots:
+    void setVisible(bool on);
+
+signals:
+    void activated(); ///< emitted on mouse over
+    void disactivated(); ///< emitted when cursor leave us
+
+    void moved(const QPointF &pos);
+    void stoppedMoving();
+
+public slots:
+    void move(double x, double y, bool signalNeeded = true);
+    void movePx(double x, double y);
+    void setActive(bool isActive);
+    void setPos(double x, double y);
+
+
+private slots:
+    void onMouseMove(QMouseEvent *event);
+    void stopMoving();
+    void moveToWantedPos();
+    void onShiftStateChanged(bool shiftPressed);
+    void selected(bool s);
+    bool selected();
+    void onMousePress(QMouseEvent *event);
+
+private:
+    QCPItemTracer *mCenterTracer;
+    QPointF mGripDelta;
+    QPointF mInitialPos;
+    bool mIsChangingOnlyOneCoordinate;
+    QList<QCPAbstractItem *> mHelperItems;
+    QPointF mLastWantedPos;
+    QTimer *mMoveTimer;
+    QPointF mCurWantedPosPx;
+    vtkwindow_new *vtkwin;
+    QString designation;
+    void drawSingleEllipse(vtkEllipse *el);
+    vtkSmartPointer<vtkLODActor> ellipseActor;
+
+    double image_x;
+    double image_y;
+    double glon;
+    double glat;
+    double error_flux;
+
+    double semiMajorAxisLength;
+    double semiMinorAxisLength;
+    double angle;
+    double arcpix;
+
+    SEDNode *sedNode;
+    bool isSelected;
+
+    VialacteaStringDictWidget *stringDictWidget;
+};
+
+#endif // SEDPLOTPOINTCUSTOM_H
diff --git a/Code/src/sedvisualizerplot.cpp b/Code/src/sedvisualizerplot.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bab2b108290daac1c09460b21c6cb92635025c21
--- /dev/null
+++ b/Code/src/sedvisualizerplot.cpp
@@ -0,0 +1,2549 @@
+#include "sedvisualizerplot.h"
+#include "ui_sedvisualizerplot.h"
+#include <QInputDialog>
+#include "sedplotpointcustom.h"
+#include "loadingwidget.h"
+#include "visivoimporterdesktop.h"
+#include "vlkbquery.h"
+#include <QFileDialog>
+#include "sedfitgrid_thin.h"
+#include "ui_sedfitgrid_thin.h"
+#include "ui_sedfitgrid_thick.h"
+#include <QSignalMapper>
+#include <QCheckBox>
+#include <QtConcurrent>
+
+SEDVisualizerPlot::SEDVisualizerPlot(QList<SED *> s, vtkwindow_new *v, QWidget *parent) :
+    QMainWindow(parent),
+    ui(new Ui::SEDVisualizerPlot)
+{
+    ////qDebug()<<"SEDVisualizerPlot created";
+    ui->setupUi(this);
+
+
+    ui->theorGroupBox->hide();
+    ui->greyBodyGroupBox->hide();
+
+
+    // QString m_sSettingsFile = QApplication::applicationDirPath()+"/setting.ini";
+    QString  m_sSettingsFile = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini");
+
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+    idlPath = settings.value("idlpath", "").toString();
+
+
+    ui->customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes | QCP::iSelectLegend | QCP::iSelectPlottables| QCP::iMultiSelect | QCP::iSelectItems);
+
+    sed_list=s;
+    vtkwin=v;
+
+    ui->resultsTableWidget->hide();
+    ui->resultsTableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
+    QAction* addFitAction = new QAction("Add this fit", ui->resultsTableWidget);
+    ui->resultsTableWidget->addAction(addFitAction);
+    connect(addFitAction, SIGNAL(triggered()), this, SLOT(addNewTheoreticalFit()));
+
+
+    ui->customPlot->yAxis->setScaleType(QCPAxis::stLogarithmic);
+    ui->customPlot->yAxis->setScaleLogBase(10);
+
+
+    ui->customPlot->xAxis->setScaleType(QCPAxis::stLogarithmic);
+    ui->customPlot->xAxis->setScaleLogBase(10);
+
+    ui->customPlot->plotLayout()->insertRow(0);
+    ui->customPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(ui->customPlot, "SED"));
+
+    ui->customPlot->xAxis->setLabel("Wavelength ["+QString::fromUtf8("\u00b5")+"m]");
+    ui->customPlot->yAxis->setLabel("Flux [Jy]");
+
+    minWavelen=std::numeric_limits<int>::max();;
+    maxWavelen= std::numeric_limits<int>::min();
+
+    minFlux=std::numeric_limits<int>::max();
+    maxFlux= std::numeric_limits<int>::min();
+
+    QString name;
+
+
+   // drawNode(s.at(0)->getRootNode());
+
+    for (sedCount=0;sedCount<s.count();sedCount++)
+    {
+        //qDebug()<<"SEDVisualizerPlot drawing sed "<<QString::number(sedCount);
+        sed=s.at(sedCount);
+        drawNode(sed->getRootNode());
+    }
+
+    double x_deltaRange=(maxWavelen-minWavelen)*0.02;
+    double y_deltaRange=(maxFlux-minFlux)*0.02;
+
+    // ui->customPlot->xAxis->setRange(minWavelen-x_deltaRange, maxWavelen+x_deltaRange);
+    // ui->customPlot->yAxis->setRange(minFlux-y_deltaRange, maxFlux+y_deltaRange);
+    ui->customPlot->yAxis->setRange(minFlux-y_deltaRange, maxFlux+y_deltaRange);
+    ui->customPlot->xAxis->setRange(minWavelen-x_deltaRange, maxWavelen+x_deltaRange);
+
+
+    //qDebug()<<"Wavelen: "<<maxWavelen-minWavelen<<" "<<x_deltaRange;
+    //qDebug()<<"Flux: "<<maxFlux-minFlux<<" "<<y_deltaRange;
+
+    // connect slot that ties some axis selections together (especially opposite axes):
+    connect(ui->customPlot, SIGNAL(selectionChangedByUser()), this, SLOT(selectionChanged()));
+    // connect slots that takes care that when an axis is selected, only that direction can be dragged and zoomed:
+    connect(ui->customPlot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePress()));
+    connect(ui->customPlot, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheel()));
+    connect(ui->customPlot, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseRelease()));
+
+    // make bottom and left axes transfer their ranges to top and right axes:
+    connect(ui->customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->xAxis2, SLOT(setRange(QCPRange)));
+    connect(ui->customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), ui->customPlot->yAxis2, SLOT(setRange(QCPRange)));
+
+    // connect some interaction slots:
+    connect(ui->customPlot, SIGNAL(titleDoubleClick(QMouseEvent*,QCPPlotTitle*)), this, SLOT(titleDoubleClick(QMouseEvent*,QCPPlotTitle*)));
+    connect(ui->customPlot, SIGNAL(axisDoubleClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)), this, SLOT(axisLabelDoubleClick(QCPAxis*,QCPAxis::SelectablePart)));
+    //    connect(ui->customPlot, SIGNAL(legendDoubleClick(QCPLegend*,QCPAbstractLegendItem*,QMouseEvent*)), this, SLOT(legendDoubleClick(QCPLegend*,QCPAbstractLegendItem*)));
+
+    // connect slot that shows a message in the status bar when a graph is clicked:
+    connect(ui->customPlot, SIGNAL(plottableClick(QCPAbstractPlottable*,QMouseEvent*)), this, SLOT(graphClicked(QCPAbstractPlottable*)));
+
+    // setup policy and connect slot for context menu popup:
+    ui->customPlot->setContextMenuPolicy(Qt::CustomContextMenu);
+    connect(ui->customPlot, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));
+
+
+
+    modelFitBands.insert("wise1",3.37);
+    modelFitBands.insert("i1",3.6);
+    modelFitBands.insert("i2",4.5);
+    modelFitBands.insert("wise2",4.62);
+    modelFitBands.insert("i3",5.8);
+    modelFitBands.insert("i4",8);
+    modelFitBands.insert("wise3",12.08);
+    modelFitBands.insert("wise4",22.194);
+    modelFitBands.insert("m1",24);
+    modelFitBands.insert("pacs1",70);
+    modelFitBands.insert("pacs2",100);
+    modelFitBands.insert("pacs3",160);
+    modelFitBands.insert("spir1",250);
+    modelFitBands.insert("spir2",350);
+    modelFitBands.insert("spir3",500);
+    modelFitBands.insert("laboc",870);
+    modelFitBands.insert("bol11",1100);
+
+    columnNames.append("id");
+    columnNames.append("clump_mass");
+    columnNames.append("compact_mass_fraction");
+    columnNames.append("clump_upper_age");
+    columnNames.append("dust_temp");
+    columnNames.append("bolometric_luminosity");
+    columnNames.append("n_star_tot");
+    columnNames.append("m_star_tot");
+    columnNames.append("n_star_zams");
+    columnNames.append("m_star_zams");
+    columnNames.append("l_star_tot");
+    columnNames.append("l_star_zams");
+    columnNames.append("zams_luminosity_1");
+    columnNames.append("zams_mass_1");
+    columnNames.append("zams_temperature_1");
+    columnNames.append("zams_luminosity_2");
+    columnNames.append("zams_mass_2");
+    columnNames.append("zams_temperature_2");
+    columnNames.append("zams_luminosity_3");
+    columnNames.append("zams_mass_3");
+    columnNames.append("zams_temperature_3");
+    columnNames.append("m1");
+    columnNames.append("pacs1");
+    columnNames.append("pacs2");
+    columnNames.append("pacs3");
+    columnNames.append("spir1");
+    columnNames.append("spir2");
+    columnNames.append("spir3");
+    columnNames.append("laboc");
+    columnNames.append("bol11");
+    columnNames.append("CHI2");
+    columnNames.append("DIST");
+
+
+    resultsOutputColumn.insert("id",-1);
+    resultsOutputColumn.insert("clump_mass",-1);
+    resultsOutputColumn.insert("compact_mass_fraction",-1);
+    resultsOutputColumn.insert("clump_upper_age",-1);
+    resultsOutputColumn.insert("dust_temp",-1);
+    resultsOutputColumn.insert("bolometric_luminosity",-1);
+    resultsOutputColumn.insert("n_star_tot",-1);
+    resultsOutputColumn.insert("m_star_tot",-1);
+    resultsOutputColumn.insert("n_star_zams",-1);
+    resultsOutputColumn.insert("m_star_zams",-1);
+    resultsOutputColumn.insert("l_star_tot",-1);
+    resultsOutputColumn.insert("l_star_zams",-1);
+    resultsOutputColumn.insert("zams_luminosity_1",-1);
+    resultsOutputColumn.insert("zams_mass_1",-1);
+    resultsOutputColumn.insert("zams_temperature_1",-1);
+    resultsOutputColumn.insert("zams_luminosity_2",-1);
+    resultsOutputColumn.insert("zams_mass_2",-1);
+    resultsOutputColumn.insert("zams_temperature_2",-1);
+    resultsOutputColumn.insert("zams_luminosity_3",-1);
+    resultsOutputColumn.insert("zams_mass_3",-1);
+    resultsOutputColumn.insert("zams_temperature_3",-1);
+    resultsOutputColumn.insert("m1",-1);
+    resultsOutputColumn.insert("pacs1",-1);
+    resultsOutputColumn.insert("pacs2",-1);
+    resultsOutputColumn.insert("pacs3",-1);
+    resultsOutputColumn.insert("spir1",-1);
+    resultsOutputColumn.insert("spir2",-1);
+    resultsOutputColumn.insert("spir3",-1);
+    resultsOutputColumn.insert("laboc",-1);
+    resultsOutputColumn.insert("bol11",-1);
+    resultsOutputColumn.insert("CHI2",-1);
+    resultsOutputColumn.insert("DIST",-1);
+
+    QString libPath= QFileInfo(idlPath).absolutePath().append("/../lib/")+":"+qApp->applicationDirPath().append("/script_idl/")+":"+qApp->applicationDirPath().append("/script_idl/astrolib")  ;
+    setenv("IDL_PATH",libPath.toStdString().c_str(),0);
+
+    ui->generatedSedBox->setHidden(true);
+    nSED=0;
+    multiSelectMOD=false;
+    temporaryMOD=false;
+    doubleClicked=false;
+    temporaryRow=0;
+    this->setFocus();
+    this->activateWindow();
+
+    //Save QList<SED *> s to a file to be loaded later on
+
+    QFile sedFile(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SEDList.dat"));
+    if(!sedFile.exists()){
+        sedFile.open(QIODevice::WriteOnly);
+        QDataStream out(&sedFile);
+        out << sed_list;   // serialize the object
+    }
+    sedFile.flush();
+    sedFile.close();
+
+    for(int i=0;i<ui->customPlot->graphCount();i++)
+    {
+        originalGraphs.push_back(ui->customPlot->graph(i));
+    }
+
+    ui->histogramPlot->hide();
+    connect(ui->resultsTableWidget->horizontalHeader(), SIGNAL( sectionClicked( int ) ), this, SLOT( sectionClicked( int ) ) );
+
+}
+
+QDataStream &operator<<(QDataStream &out, QList<SED *> &sedlist)
+{
+    out<<sedlist.count();
+    foreach(SED* sed, sedlist){
+        out<<sed;
+    }
+    return out;
+}
+
+void SEDVisualizerPlot::setTitle(QString t)
+{
+    this->setWindowTitle(t);
+
+}
+
+
+void SEDVisualizerPlot::setDistances(double dist)
+{
+    ui->distTheoLineEdit->setText(QString::number(dist));
+}
+
+void SEDVisualizerPlot::sectionClicked ( int index )
+{
+    if(index>=1&&index<=4){
+        ui->histogramPlot->clearPlottables();
+        //qDebug() << "Clicked Index "<<index;
+        QVector<double> values=sedFitValues.value(index+1);
+        double min,max,bin_width;
+        QString title;
+        if(index==1){//clump_mass
+            //min=10;
+            //max=200000;
+            min=1;
+            max=5.3;
+            bin_width=0.1;
+            title="clump_mass (log scale)";
+        }
+        if(index==2){//compact_mass_fraction
+            min=0;
+            max=0.5;
+            bin_width=0.025;
+            title="compact_mass_fraction";
+        }
+        if(index==3){//clump_upper_age
+            min=4;
+            max=5.7;
+            bin_width=0.25;
+            title="clump_upper_age (log scale)";
+        }
+        if(index==4){//dust_temp
+            min=5;
+            max=50;
+            bin_width=1;
+            title="dust_temp";
+        }
+        ui->histogramPlot->show();
+        int bin_count=(max-min)/bin_width;
+        QVector<double> x1(bin_count), y1(bin_count);
+        for (int i=0; i<values.size(); ++i)
+        {
+            int bin;
+            if(index==1){
+                bin = (int)((log10(values.at(i)) - min) / bin_width);
+            }else if(index==3){
+                bin = (int)((log10(values.at(i)*100000) - min) / bin_width);
+            }else{
+                bin = (int)((values.at(i) - min) / bin_width);
+            }
+            if(bin>=0 && bin<bin_count){
+                y1[bin]++;
+            }
+        }
+        x1[0]=min;
+        for (int i=1; i<bin_count; ++i)
+        {
+            x1[i]=x1[i-1]+bin_width;
+        }
+
+        //qDebug()<<x1.size();
+
+        QCPBars *bars1 = new QCPBars(ui->histogramPlot->xAxis, ui->histogramPlot->yAxis);
+        bars1->setWidth(bin_width);
+        bars1->setData(x1, y1);
+        ui->histogramPlot->xAxis->setRange(min, max);
+        ui->histogramPlot->yAxis->setRange(0,values.size());
+        //if(index==1||index==3){
+        //ui->histogramPlot->xAxis->setScaleType(QCPAxis::stLogarithmic);
+        //ui->histogramPlot->xAxis->setScaleLogBase(10);
+
+        //}else{
+        //    ui->histogramPlot->xAxis->setScaleType(QCPAxis::stLinear);
+        //}
+        //ui->histogramPlot->plotLayout()->insertRow(0);
+        //ui->histogramPlot->plotLayout()->addElement(0, 0, new QCPTextElement(customPlot, title, QFont("sans", 12, QFont::Bold)));
+        ui->histogramPlot->xAxis->setLabel(title);
+        ui->histogramPlot->yAxis->setAutoTickStep(false);
+        ui->histogramPlot->yAxis->setTickStep(1);
+        ui->histogramPlot->addPlottable(bars1);
+        ui->histogramPlot->replot();
+    }else{
+        QMessageBox msgBox;
+        msgBox.setText("No histogram available for this values.");
+        msgBox.exec();
+    }
+}
+
+
+
+QDataStream &operator>>(QDataStream &in, QList<SED *> &sedlist)
+{
+    //qDebug()<< "reading SED list";
+    int count;
+    in >> count;
+    //qDebug()<< "SED list size : "<<QString::number(count);
+    for(int i=0; i<count; i++){
+        SED * sed=new SED();
+        in >> sed;
+        sedlist.push_back(sed);
+    }
+    //qDebug()<< "SED list size : "<<QString::number(sedlist.count());
+    return in;
+}
+
+
+
+void SEDVisualizerPlot::drawNode( SEDNode *node)
+{
+
+    //qDebug()<<"drawNode "<<node->getDesignation()<< " esiste "<<visualnode_hash.contains(node->getDesignation());
+    if(!visualnode_hash.contains(node->getDesignation()))
+    {
+        ////qDebug()<<"drawNode inside if";
+        SEDPlotPointCustom *cp = new SEDPlotPointCustom(ui->customPlot,3.5,vtkwin);
+
+        if(vtkwin!=0){
+
+            if (vtkwin->getDesignation2fileMap().values(node->getDesignation()).length() >0)
+            {
+                double r= vtkwin->getEllipseActorList().value( vtkwin->getDesignation2fileMap().value(node->getDesignation()) )->GetProperty()->GetColor()[0];
+                double g= vtkwin->getEllipseActorList().value( vtkwin->getDesignation2fileMap().value(node->getDesignation()) )->GetProperty()->GetColor()[1];
+                double b= vtkwin->getEllipseActorList().value( vtkwin->getDesignation2fileMap().value(node->getDesignation()) )->GetProperty()->GetColor()[2];
+
+                cp->setColor(QColor(r*255,g*255,b*255));
+
+            }
+        }
+        cp->setAntialiased(true);
+        cp->setPos(node->getWavelength(), node->getFlux());
+        cp->setDesignation(node->getDesignation());
+        cp->setX(node->getX());
+        cp->setY(node->getY());
+        cp->setLat(node->getLat());
+        cp->setLon(node->getLon());
+        cp->setErrorFlux(node->getErrFlux());
+
+        cp->setEllipse(node->getSemiMinorAxisLength(),node->getSemiMajorAxisLength(),node->getAngle(),node->getArcpix());
+
+        cp->setNode(node);
+        ui->customPlot->addItem(cp);
+        //connect(ui->customPlot,SIGNAL(mousePress(QMouseEvent*)),cp,SLOT(onMousePress(QMouseEvent*)));
+
+        visualnode_hash.insert(node->getDesignation(),cp);
+
+        if (node->getWavelength()>maxWavelen)
+            maxWavelen=node->getWavelength();
+        if ( node->getWavelength() < minWavelen)
+            minWavelen=node->getWavelength();
+
+
+        if (node->getFlux()>maxFlux)
+            maxFlux=node->getFlux();
+        if ( node->getFlux() < minFlux)
+        {
+            minFlux=node->getFlux();
+        }
+
+
+        all_sed_node.insert(node->getWavelength(),node);
+    }
+
+   // //qDebug()<<"drawNode outside if";
+
+
+    QVector<double> x(2), y(2), y_err(2);
+
+    x[0]=node->getWavelength();
+    y[0]=node->getFlux();
+    y_err[0]=node->getErrFlux();
+
+   // //qDebug()<<"drawNode child count "<<QString::number(node->getChild().count());
+
+    for (int i=0;i<node->getChild().count();i++)
+    {
+        drawNode(node->getChild().values()[i]);
+
+        x[1]=node->getChild().values()[i]->getWavelength();
+        y[1]=node->getChild().values()[i]->getFlux();
+        y_err[1]=node->getChild().values()[i]->getErrFlux();
+
+        ui->customPlot->addGraph();
+
+        ui->customPlot->graph()->setDataValueError(x, y, y_err);
+        ui->customPlot->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot, 1));
+
+        ui->customPlot->graph()->setPen(QPen(Qt::black));
+        ui->customPlot->graph()->setSelectedPen(QPen(Qt::red));
+        ui->customPlot->graph()->setAntialiased(true);
+        ui->customPlot->graph()->setErrorType(QCPGraph::etBoth);
+
+    }
+
+
+}
+
+
+void SEDVisualizerPlot::titleDoubleClick(QMouseEvent* event, QCPPlotTitle* title)
+{
+    Q_UNUSED(event)
+    // Set the plot title by double clicking on it
+    bool ok;
+    QString newTitle = QInputDialog::getText(this, "SED Title", "New SED title:", QLineEdit::Normal, title->text(), &ok);
+    if (ok)
+    {
+        title->setText(newTitle);
+        ui->customPlot->replot();
+    }
+}
+
+void SEDVisualizerPlot::axisLabelDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part)
+{
+    // Set an axis label by double clicking on it
+    if (part == QCPAxis::spAxisLabel) // only react when the actual axis label is clicked, not tick label or axis backbone
+    {
+        bool ok;
+        QString newLabel = QInputDialog::getText(this, "QCustomPlot example", "New axis label:", QLineEdit::Normal, axis->label(), &ok);
+        if (ok)
+        {
+            axis->setLabel(newLabel);
+            ui->customPlot->replot();
+        }
+    }
+}
+
+void SEDVisualizerPlot::legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item)
+{
+    // Rename a graph by double clicking on its legend item
+    Q_UNUSED(legend)
+    if (item) // only react if item was clicked (user could have clicked on border padding of legend where there is no item, then item is 0)
+    {
+        QCPPlottableLegendItem *plItem = qobject_cast<QCPPlottableLegendItem*>(item);
+        bool ok;
+        QString newName = QInputDialog::getText(this, "QCustomPlot example", "New graph name:", QLineEdit::Normal, plItem->plottable()->name(), &ok);
+        if (ok)
+        {
+            plItem->plottable()->setName(newName);
+            ui->customPlot->replot();
+        }
+    }
+}
+
+void SEDVisualizerPlot::selectionChanged()
+{
+    if(multiSelectMOD){ // on "multi select mode" prevent a graph to be clicked. Only nodes can be selected.
+        // //qDebug()<<"selected graph count "<<ui->customPlot->selectedGraphs().count();
+        for(int i = 0; i < ui->customPlot->graphCount(); ++i)
+            ui->customPlot->graph(i)->setSelected(false);
+    }
+
+}
+
+void SEDVisualizerPlot::mouseRelease()
+{
+
+}
+
+void SEDVisualizerPlot::mousePress()
+{
+    if(multiSelectMOD){
+        QList<QCPAbstractItem *> list_items=ui->customPlot->selectedItems();
+        ////qDebug()<<"Selected Items : "<<list_items.size();
+    }
+    else{
+
+        // //qDebug()<<"pre - # selzionato: " <<ui->customPlot->selectedGraphs().count();
+
+        for(int i = 0; i < ui->customPlot->graphCount(); ++i)
+            ui->customPlot->graph(i)->setSelected(true);
+
+        ui->customPlot->replot();
+
+
+        // //qDebug()<<"post - # selzionato: " <<ui->customPlot->selectedGraphs().count();
+        // //qDebug()<<"# grap: "<<ui->customPlot->graphCount();
+    }
+
+    // if an axis is selected, only allow the direction of that axis to be dragged
+    // if no axis is selected, both directions may be dragged
+
+    if (ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
+        ui->customPlot->axisRect()->setRangeDrag(ui->customPlot->xAxis->orientation());
+    else if (ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis))
+        ui->customPlot->axisRect()->setRangeDrag(ui->customPlot->yAxis->orientation());
+    else
+        ui->customPlot->axisRect()->setRangeDrag(Qt::Horizontal|Qt::Vertical);
+}
+
+void SEDVisualizerPlot::mouseWheel()
+{
+    // if an axis is selected, only allow the direction of that axis to be zoomed
+    // if no axis is selected, both directions may be zoomed
+
+    if (ui->customPlot->xAxis->selectedParts().testFlag(QCPAxis::spAxis))
+        ui->customPlot->axisRect()->setRangeZoom(ui->customPlot->xAxis->orientation());
+    else if (ui->customPlot->yAxis->selectedParts().testFlag(QCPAxis::spAxis))
+        ui->customPlot->axisRect()->setRangeZoom(ui->customPlot->yAxis->orientation());
+    else
+        ui->customPlot->axisRect()->setRangeZoom(Qt::Horizontal|Qt::Vertical);
+}
+
+
+void SEDVisualizerPlot::removeSelectedGraph()
+{
+    if (ui->customPlot->selectedGraphs().size() > 0)
+    {
+        ui->customPlot->removeGraph(ui->customPlot->selectedGraphs().first());
+        ui->customPlot->replot();
+    }
+}
+
+void SEDVisualizerPlot::removeAllGraphs()
+{
+    ui->customPlot->clearGraphs();
+    ui->customPlot->replot();
+}
+
+void SEDVisualizerPlot::contextMenuRequest(QPoint pos)
+{
+
+
+    QMenu *menu = new QMenu(this);
+    menu->setAttribute(Qt::WA_DeleteOnClose);
+
+    if (ui->customPlot->legend->selectTest(pos, false) >= 0) // context menu on legend requested
+    {
+        menu->addAction("Move to top left", this, SLOT(moveLegend()))->setData((int)(Qt::AlignTop|Qt::AlignLeft));
+        menu->addAction("Move to top center", this, SLOT(moveLegend()))->setData((int)(Qt::AlignTop|Qt::AlignHCenter));
+        menu->addAction("Move to top right", this, SLOT(moveLegend()))->setData((int)(Qt::AlignTop|Qt::AlignRight));
+        menu->addAction("Move to bottom right", this, SLOT(moveLegend()))->setData((int)(Qt::AlignBottom|Qt::AlignRight));
+        menu->addAction("Move to bottom left", this, SLOT(moveLegend()))->setData((int)(Qt::AlignBottom|Qt::AlignLeft));
+    }
+    else  // general context menu on graphs requested
+    {
+        if (ui->customPlot->selectedGraphs().size() > 0)
+            menu->addAction("Remove selected connection", this, SLOT(removeSelectedGraph()));
+    }
+
+    menu->popup(ui->customPlot->mapToGlobal(pos));
+}
+
+
+void SEDVisualizerPlot::graphClicked(QCPAbstractPlottable *plottable)
+{
+    //ui->statusbar->showMessage(QString("Clicked on graph '%1'.").arg(plottable->name()), 1000);
+}
+
+SEDVisualizerPlot::~SEDVisualizerPlot()
+{
+    delete ui;
+}
+
+
+
+void SEDVisualizerPlot::on_actionEdit_triggered()
+{
+
+}
+
+void SEDVisualizerPlot::on_actionFit_triggered()
+{
+
+
+}
+
+bool SEDVisualizerPlot::prepareInputForSedFit( SEDNode *node)
+{
+    bool validFit=true;
+
+    //qDebug()<<"Selected Items no multi : ";
+
+    sedFitInput.insert(node->getWavelength(),node->getFlux());
+    sedFitInputF= QString::number( node->getFlux() )+sedFitInputF;
+    sedFitInputErrF= QString::number( node->getErrFlux() )+sedFitInputErrF;
+    sedFitInputW= QString::number( node->getWavelength() )+sedFitInputW;
+
+    sedFitInputFflag ="1"+sedFitInputFflag;
+
+
+
+
+    for (int i=0; i<node->getChild().count(); i++)
+    {
+
+
+
+        sedFitInputF= ","+sedFitInputF;
+        sedFitInputErrF= ","+sedFitInputErrF;
+        sedFitInputW= ","+sedFitInputW;
+        sedFitInputFflag= ","+sedFitInputFflag;
+        prepareInputForSedFit(node->getChild().values()[i]);
+
+
+    }
+
+    return    validFit;
+
+
+}
+
+bool SEDVisualizerPlot::prepareSelectedInputForSedFit()
+{
+    bool validFit=false;
+
+    QMap <double, SEDNode *> selected_sed_map;
+    QList<QCPAbstractItem *> list_items=ui->customPlot->selectedItems();
+    for(int i=0;i<list_items.size();i++){
+        QString className=QString::fromUtf8(list_items.at(i)->metaObject()->className());
+        QString refName="SEDPlotPointCustom";
+        ////qDebug()<<"Class name :"<<className<<" "<<QString::compare(className,refName);
+        if(QString::compare(className,refName)==0){
+            ////qDebug()<<"Insert item";
+            SEDPlotPointCustom * cp=qobject_cast<SEDPlotPointCustom *>(list_items.at(i));
+            selected_sed_map.insert(cp->getNode()->getWavelength(),cp->getNode());
+        }
+    }
+    //qDebug()<<"Selected Items : "<<selected_sed_map.size();
+
+    if(selected_sed_map.size()>=2){
+        QMap<double, SEDNode *>::iterator iter;
+        iter=selected_sed_map.begin();
+        SEDNode *node=iter.value();
+        sedFitInput.insert(node->getWavelength(),node->getFlux());
+        sedFitInputF= QString::number( node->getFlux() )+sedFitInputF;
+        sedFitInputErrF= QString::number( node->getErrFlux() )+sedFitInputErrF;
+        sedFitInputW= QString::number( node->getWavelength() )+sedFitInputW;
+        sedFitInputFflag ="1"+sedFitInputFflag;
+        ++iter;
+        for (; iter!=selected_sed_map.end(); ++iter)
+        {
+            node=iter.value();
+            sedFitInput.insert(node->getWavelength(),node->getFlux());
+            sedFitInputF= QString::number( node->getFlux() )+","+sedFitInputF;
+            sedFitInputErrF= QString::number( node->getErrFlux() )+","+sedFitInputErrF;
+            sedFitInputW= QString::number( node->getWavelength() )+","+sedFitInputW;
+            sedFitInputFflag ="1,"+sedFitInputFflag;
+        }
+        validFit=true;
+    }
+    else
+    {
+        QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr("Could not execute SED fit with less than 2 points selected.\n\rPlease select more points and try again."));
+        validFit=false;
+    }
+
+    return validFit;
+}
+
+
+void SEDVisualizerPlot::on_actionLocal_triggered()
+{
+
+
+}
+
+void SEDVisualizerPlot::readSedFitResultsHeader(QString header)
+{
+    ////qDebug()<<header;
+    QList<QString> line_list_string=header.split(',');
+    QString field;
+    for(int i=0; i<line_list_string.size();i++){
+        field=line_list_string.at(i).simplified();
+        ////qDebug()<<field;
+        if(resultsOutputColumn.contains(field)){
+            resultsOutputColumn.insert(field,i);
+            ////qDebug()<<field<<"->"<<resultsOutputColumn.value(field);
+        }
+    }
+}
+
+void SEDVisualizerPlot::readSedFitOutput(QString filename)
+{
+    int id;
+    double chi2=99999999999;
+    double tmp;
+    QString chi_str,id_str,dist_str,clamp_mass_str,clamp_upper_age_str, compact_mass_fraction_str, dust_temp_str;
+    QMap <double, int> results;
+    QMap <double, QList<QByteArray> > resultsLines;
+    QVector<double> clamp_mass;
+    QVector<double> clamp_upper_age;
+    QVector<double> compact_mass_fraction;
+    QVector<double> dust_temp;
+
+    //qDebug()<<"filename: "<<filename;
+    QFile file(filename);
+    // QFile file(qApp->applicationDirPath()+"/sedfit_output.dat");
+    if (!file.open(QIODevice::ReadOnly)) {
+        //qDebug() << file.errorString();
+        return ;
+    }
+    //read, and skip the first line (header)
+    //file.readLine();
+    QString header=file.readLine();
+
+    outputSedResults.append(header);
+    readSedFitResultsHeader(header);
+    //  QStringList wordList;
+    int i=0;
+    ui->resultsTableWidget->clearContents();
+    ui->resultsTableWidget->setRowCount(0);
+    ui->resultsTableWidget->setColumnCount(columnNames.size());
+    ui->resultsTableWidget->setHorizontalHeaderLabels(columnNames);
+
+    while (!file.atEnd()) {
+        QByteArray line = file.readLine();
+        //wordList.append(line.split(',').first());
+        outputSedResults.append(line);
+
+
+
+        QList<QByteArray> line_list_string=line.split(',');
+
+        ////qDebug()<<i<<" line size = "<<line_list_string.length();
+
+
+        QList<QByteArray> tmp_string=line_list_string;
+        id_str=line_list_string.first().simplified();
+        dist_str=line_list_string.last().simplified();
+        line_list_string.removeLast();
+        chi_str=line_list_string.last().simplified();
+        resultsLines.insert(chi_str.toDouble(),tmp_string);
+        clamp_mass_str=line_list_string.at(2).simplified();
+        clamp_upper_age_str=line_list_string.at(4).simplified();
+        compact_mass_fraction_str=line_list_string.at(3).simplified();
+        dust_temp_str=line_list_string.at(5).simplified();
+        //    //qDebug()<<"id_ "<<id_str<<" dist_ "<<dist_str<<" chi2 "<<chi_str;
+        //   //qDebug()<<"clamp_mass_str "<<clamp_mass_str;
+        clamp_mass.append(clamp_mass_str.toDouble());
+        clamp_upper_age.append(clamp_upper_age_str.toDouble());
+        dust_temp.append(dust_temp_str.toDouble());
+        compact_mass_fraction.append(compact_mass_fraction_str.toDouble());
+
+        results.insert(chi_str.toDouble(), id_str.toInt());
+        if (chi_str.toDouble()<chi2)
+        {
+            id=id_str.toInt();
+            chi2=chi_str.toDouble();
+            dist=dist_str.toDouble();
+        }
+    }
+    sedFitValues.insert(2,clamp_mass);
+    sedFitValues.insert(4,clamp_upper_age);
+    sedFitValues.insert(3,compact_mass_fraction);
+    sedFitValues.insert(5,dust_temp);
+
+
+    int threshold = 5;
+    int nFitsResults=results.size()*threshold/100;
+    if(nFitsResults<1&&results.size()>=1){
+        nFitsResults=1;
+    }
+    if(nFitsResults<=10){
+        nFitsResults=results.size();
+    }
+
+    QMap<double, int>::iterator iter;
+    QMap <double, QList<QByteArray> >::iterator iterLines;
+    iter=results.begin();
+    iterLines=resultsLines.begin();
+    for(int i=0;i<nFitsResults;i++,iter++,iterLines++){
+        //for(int i=0;i<results.size();i++,iter++,iterLines++){
+        int new_id=iter.value();
+        if(i==0){ // Plot only the best one
+            QString query="SELECT * FROM vlkb_compactsources.sed_models where id="+ QString::number(new_id);
+            //   //qDebug()<<"query: " << query;
+            //   //qDebug()<<"color pre : blue " << Qt::blue;
+            new VLKBQuery(query,vtkwin, "model", this, Qt::blue);
+        }
+        /*
+        else{
+            if(i<10){
+            QString query="SELECT * FROM vlkb_compactsources.sed_models where id="+ QString::number(new_id);
+            new VLKBQuery(query,vtkwin, "model", this, Qt::cyan);
+
+            }
+        }*/
+        QList<QByteArray> line_list_string=iterLines.value();
+        ui->resultsTableWidget->insertRow(i);
+        for(int j=0;j<columnNames.length();j++){
+            if(resultsOutputColumn.value(columnNames.at(j))!=-1){
+                ui->resultsTableWidget->setItem(i, j, new QTableWidgetItem(QString(line_list_string.at(resultsOutputColumn.value(columnNames.at(j))).simplified())));
+            }
+        }
+    }
+    ui->resultsTableWidget->resizeColumnsToContents();
+    ui->resultsTableWidget->resizeRowsToContents();
+    loading->close();
+}
+
+void SEDVisualizerPlot::loadSedFitOutput(QString filename){
+    int id;
+    double chi2=99999999999;
+    double tmp;
+    QString chi_str,id_str,dist_str;
+    QMap <double, int> results;
+    QMap <double, QList<QByteArray> > resultsLines;
+
+    //   //qDebug()<<"filename: "<<filename;
+    QFile file(filename);
+    // QFile file(qApp->applicationDirPath()+"/sedfit_output.dat");
+    if (!file.open(QIODevice::ReadOnly)) {
+        //  //qDebug() << file.errorString();
+        return ;
+    }
+    //read, and skip the first line (header)
+    //file.readLine();
+    QString header=file.readLine();
+
+    outputSedResults.append(header);
+    readSedFitResultsHeader(header);
+    //  QStringList wordList;
+    int i=0;
+    ui->resultsTableWidget->clearContents();
+    ui->resultsTableWidget->setRowCount(0);
+    ui->resultsTableWidget->setColumnCount(columnNames.size());
+    ui->resultsTableWidget->setHorizontalHeaderLabels(columnNames);
+    while (!file.atEnd()) {
+        QByteArray line = file.readLine();
+        //wordList.append(line.split(',').first());
+        outputSedResults.append(line);
+
+
+
+        QList<QByteArray> line_list_string=line.split(',');
+
+        ////qDebug()<<i<<" line size = "<<line_list_string.length();
+
+        /*
+            ui->resultsTableWidget->insertRow(i);
+            for(int j=0;j<columnNames.length();j++){
+                if(resultsOutputColumn.value(columnNames.at(j))!=-1){
+                    ui->resultsTableWidget->setItem(i, j, new QTableWidgetItem(QString(line_list_string.at(resultsOutputColumn.value(columnNames.at(j))).simplified())));
+                }
+            }
+            i++;
+            */
+        QList<QByteArray> tmp_string=line_list_string;
+        id_str=line_list_string.first().simplified();
+        dist_str=line_list_string.last().simplified();
+        line_list_string.removeLast();
+        chi_str=line_list_string.last().simplified();
+        //  //qDebug()<<"id_ "<<id_str<<" dist_ "<<dist_str<<" chi2 "<<chi_str;
+        results.insert(chi_str.toDouble(), id_str.toInt());
+        resultsLines.insert(chi_str.toDouble(),tmp_string);
+        if (chi_str.toDouble()<chi2)
+        {
+            id=id_str.toInt();
+            chi2=chi_str.toDouble();
+            dist=dist_str.toDouble();
+        }
+    }
+
+
+    //qDebug()<<"id: "<<id<<" chi2: "<<chi2<<"dist: "<<dist;
+    //qDebug()<<"results chi2 :"<<results.firstKey();
+    int threshold = 5;
+    int nFitsResults=results.size()*threshold/100;
+    if(nFitsResults<1&&results.size()>=1){
+        nFitsResults=1;
+    }
+    if(nFitsResults<=10){
+        nFitsResults=results.size();
+    }
+    //qDebug()<<"All Fits :"<<QString::number(results.size());
+    //qDebug()<<"results chi2 :"<<results.firstKey()<<" nFitsResults: "<<QString::number(nFitsResults);
+    //qDebug()<<"resultsLines chi2 :"<<resultsLines.firstKey();
+    //  //qDebug() << wordList;
+    QMap<double, int>::iterator iter;
+    QMap <double, QList<QByteArray> >::iterator iterLines;
+    iter=results.begin();
+    iterLines=resultsLines.begin();
+    for(int i=0;i<nFitsResults;i++,iter++,iterLines++){
+        //for(int i=0;i<results.size();i++,iter++,iterLines++){
+        QList<QByteArray> line_list_string=iterLines.value();
+        ui->resultsTableWidget->insertRow(i);
+        for(int j=0;j<columnNames.length();j++){
+            if(resultsOutputColumn.value(columnNames.at(j))!=-1){
+                ui->resultsTableWidget->setItem(i, j, new QTableWidgetItem(QString(line_list_string.at(resultsOutputColumn.value(columnNames.at(j))).simplified())));
+            }
+        }
+    }
+    ui->resultsTableWidget->resizeColumnsToContents();
+}
+
+void SEDVisualizerPlot::loadSedFitThin(QString filename){
+    //qDebug()<<"filename: "<<filename;
+    ui->resultsTableWidget->clearContents();
+    ui->resultsTableWidget->setColumnCount(6);
+    ui->resultsTableWidget->setRowCount(0);
+    ui->resultsTableWidget->setHorizontalHeaderLabels(QStringList() << "fmass" << "dmass" << "ftemp" << "dtemp" << "fbeta" << "lum");
+    ui->resultsTableWidget->insertRow(0);
+
+    QFile filepar(filename);
+    if (!filepar.open(QIODevice::ReadOnly)) {
+        //qDebug() << filepar.errorString();
+        //qDebug() << "ERRORE Parametri";
+        return ;
+    }
+    QString line=filepar.readLine();
+    outputSedResults.append(line);
+    filepar.close();
+    QList<QString> line_list_string=line.split(',');
+    for(int i=0; i<line_list_string.size();i++){
+        ui->resultsTableWidget->setItem(0,i,new QTableWidgetItem(line_list_string.at(i).trimmed()));
+    }
+    ui->resultsTableWidget->resizeColumnsToContents();
+}
+
+void SEDVisualizerPlot::loadSedFitThick(QString filename){
+    ui->resultsTableWidget->clearContents();
+    ui->resultsTableWidget->setColumnCount(9);
+    ui->resultsTableWidget->setRowCount(0);
+    ui->resultsTableWidget->setHorizontalHeaderLabels(QStringList() << "mass" << "dmass" << "ftemp" << "dtemp" << "fbeta" << "fl0" << "dlam0" << "sizesec" << "lum");
+    ui->resultsTableWidget->insertRow(0);
+    QFile filepar(filename);
+    if (!filepar.open(QIODevice::ReadOnly)) {
+        //qDebug() << filepar.errorString();
+        return ;
+    }
+    QString line=filepar.readLine();
+    outputSedResults.append(line);
+    filepar.close();
+    QList<QString> line_list_string=line.split(',');
+    for(int i=0; i<line_list_string.size();i++){
+        ui->resultsTableWidget->setItem(0,i,new QTableWidgetItem(line_list_string.at(i).trimmed()));
+    }
+    ui->resultsTableWidget->resizeColumnsToContents();
+}
+
+void SEDVisualizerPlot::setModelFitValue(QVector<QStringList> headerAndValueList, Qt::GlobalColor color)
+{
+    //qDebug()<<"color set model fit : " << color;
+    QFile modelFile(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_sedfit_model.dat"));
+    if(!modelFile.exists()){
+        modelFile.open(QIODevice::WriteOnly);
+        QDataStream out(&modelFile);
+        out << headerAndValueList;   // serialize the object
+    }
+    /*
+        QVectorIterator<QStringList> i(headerAndValueList);
+        int count=0;
+        while (i.hasNext()){
+            //qDebug()<<"List: "<<QString::number(count);
+            QStringList list= i.next();
+            foreach (const QString &str, list) {
+                //qDebug()<<str;
+            }
+            count++;
+        }
+            //modelFile.write()
+    }*/
+    QVector<double> x, y;
+    double scale;
+    QCPGraph *graph=ui->customPlot->addGraph();
+    QList<SEDPlotPointCustom *> points;
+    for( QMap<QString, double>::iterator it = modelFitBands.begin(); it != modelFitBands.end(); ++it)
+    {
+
+        //  scale= 1/sqrt(dist);
+        scale = 1000/pow(dist,2);
+        //scale=1;
+        x.append(it.value());
+        int index=headerAndValueList.at(0).indexOf(it.key());
+        y.append(headerAndValueList.at(1).at(index).toDouble()*scale);
+        //y.append(headerAndValueList.at(1).at(index).toDouble());
+
+        SEDPlotPointCustom *cp = new SEDPlotPointCustom(ui->customPlot,3,vtkwin);
+        cp->setAntialiased(true);
+
+        //cp->setPos(it.value(), headerAndValueList.at(1).at(index).toDouble()*scale);
+        cp->setPos(it.value(), headerAndValueList.at(1).at(index).toDouble()*scale);
+
+        cp->setDesignation("");
+        cp->setX(0);
+        cp->setY(0);
+        cp->setLat(0);
+        cp->setLon(0);
+        points.push_back(cp);
+        //cp->setParent(graph);
+        ui->customPlot->addItem(cp);
+    }
+
+
+
+    //qDebug()<<"x:\n"<<x;
+    //qDebug()<<"y:\n"<<y;
+
+
+
+    //ui->customPlot->addGraph();
+
+    //qDebug()<<"temporaryMOD : "<<temporaryMOD;
+
+    if(color==Qt::blue || doubleClicked){
+        addNewSEDCheckBox("Theoretical Fit");
+        //qDebug()<<"Adding points of SED "<<QString::number(nSED-1);
+        sedGraphPoints.insert(nSED-1,points);
+        sedGraphs.push_back(graph);
+        doubleClicked=false;
+    }
+
+    if(temporaryMOD){
+        //qDebug()<<"Temporary MOD";
+        temporaryGraph=graph;
+        temporaryGraphPoints=points;
+    }
+
+    ui->customPlot->graph()->setData(x, y);
+    // ui->customPlot->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 6));
+    ui->customPlot->graph()->setPen(QPen(color));
+    ui->customPlot->graph()->setScatterStyle( QCPScatterStyle::ssNone);
+    ui->customPlot->replot();
+    this->setFocus();
+    this->activateWindow();
+}
+
+
+void SEDVisualizerPlot::on_actionScreenshot_triggered()
+{
+    QPixmap qPixMap = QPixmap::grabWidget(this);  // *this* is window pointer, the snippet     is in the mainwindow.cpp file
+
+    QImage qImage = qPixMap.toImage();
+
+    QString fileName = QFileDialog::getSaveFileName(this,"Save screenshot", "", ".png");
+    //qDebug()<<"pre "<<fileName;
+    if(!fileName.endsWith(".png",Qt::CaseInsensitive) )
+        fileName.append(".png");
+    bool b = qImage.save(fileName);
+}
+
+void SEDVisualizerPlot::on_actionCollapse_triggered()
+{
+
+    QPen pen;
+    pen.setStyle(Qt::DashLine);
+    pen.setColor(Qt::lightGray);
+
+    for(int i=0;i<originalGraphs.size();i++)
+    {
+        //originalGraphs.push_back(ui->customPlot->graph(i));
+        originalGraphs.at(i)->setPen(pen);
+    }
+
+    SED *coll_sed= new SED();
+    QVector<double> x, y;
+    SEDNode *old_tmp_node;
+    SEDNode *tmp_node;
+
+    int cnt=0;
+    double j;
+    foreach( j, all_sed_node.uniqueKeys() )
+    {
+        //qDebug() <<"j: "<< j;
+        QList<SEDNode*> node_list_tmp=all_sed_node.values(j);
+        double flux_sum=0;
+        double err_flux_sum=0;
+
+        if(cnt!=0)
+        {
+            old_tmp_node=tmp_node;
+        }
+
+        tmp_node=new SEDNode();
+
+        for (int z=0;z<node_list_tmp.count(); z++ )
+        {
+            //qDebug() <<"\t"<< node_list_tmp.at(z)->getDesignation();
+            flux_sum+=node_list_tmp.at(z)->getFlux();
+            err_flux_sum+=node_list_tmp.at(z)->getErrFlux();
+        }
+
+        tmp_node->setDesignation("");
+        tmp_node->setFlux(flux_sum);
+        tmp_node->setErrFlux(err_flux_sum);
+        tmp_node->setWavelength(j);
+
+        if(cnt!=0)
+        {
+            old_tmp_node->setParent(tmp_node);
+            tmp_node->setChild(old_tmp_node);
+        }
+
+
+
+        x.append(j);
+        y.append(flux_sum);
+
+
+        SEDPlotPointCustom *cp = new SEDPlotPointCustom(ui->customPlot,3,vtkwin);
+        cp->setAntialiased(true);
+        cp->setPos(j, flux_sum);
+        cp->setDesignation("");
+        cp->setX(0);
+        cp->setY(0);
+        cp->setLat(0);
+        cp->setLon(0);
+        cp->setErrorFlux(err_flux_sum);
+        cp->setNode(tmp_node);
+        collapsedGraphPoints.push_back(cp);
+        ui->customPlot->addItem(cp);
+
+        cnt++;
+    }
+
+    //qDebug()<<"x:\n"<<x;
+    //qDebug()<<"y:\n"<<y;
+
+
+    coll_sed->setRootNode(tmp_node);
+
+    collapsedGraph=ui->customPlot->addGraph();
+    ui->customPlot->graph()->setData(x, y);
+    ui->customPlot->graph()->setPen(QPen(Qt::red)); // line color red for second graph
+    ui->customPlot->graph()->setScatterStyle( QCPScatterStyle::ssNone);
+
+    ui->customPlot->replot();
+
+    //qDebug()<<sed_list.count();
+    sed_list.insert(0,coll_sed);
+    //qDebug()<<sed_list.count();
+
+
+}
+
+
+void SEDVisualizerPlot::on_TheoreticalLocaleFit_triggered()
+{
+    sedFitInputFflag = "]";
+    sedFitInputF= "]";
+    sedFitInputW= "]";
+
+    sedFitInputErrF= "]";
+
+
+    sedFitInput.clear();
+
+    bool validFit=false;
+
+    //  if(multiSelectMOD){
+    validFit=prepareSelectedInputForSedFit();
+    /*}else{
+        validFit=prepareInputForSedFit(sed_list.at(0)->getRootNode());
+    }
+*/
+    if(validFit)
+    {
+        sedFitInputF ="["+sedFitInputF ;
+        sedFitInputW ="["+sedFitInputW;
+        sedFitInputFflag = "["+sedFitInputFflag;
+        sedFitInputErrF ="["+sedFitInputErrF ;
+
+        loading = new LoadingWidget();
+        loading->init();
+        loading->setFileName("Executing vialactea_tap_sedfit");
+        loading ->show();
+        loading->activateWindow();
+        loading->setFocus();
+
+        ui->outputTextBrowser->setText("");
+
+        process= new QProcess();
+
+        process->setProcessChannelMode(QProcess::MergedChannels);
+
+        //qDebug()<<idlPath+" -e \"sedpar=vialactea_tap_sedfit_v7("+sedFitInputW+","+sedFitInputF+","+sedFitInputErrF+","+sedFitInputFflag+","+ui->distTheoLineEdit->text()+","+ui->prefilterLineEdit->text()+",sed_weights=["+ui->mid_irLineEdit->text()+","+ui->far_irLineEdit->text()+","+ui->submmLineEdit->text()+"],use_wave="+sedFitInputW+",outdir='"+QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED))+"_',delta_chi2="+ui->delta_chi2_lineEdit->text()+")\" ";
+
+        connect(process,SIGNAL(readyReadStandardError()),this,SLOT(onReadyReadStdOutput()));
+        connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadyReadStdOutput()));
+        connect(process,SIGNAL(finished(int)),this,SLOT(finishedTheoreticalLocaleFit()));
+
+        process->start (idlPath+" -e \"sedpar=vialactea_tap_sedfit_v7("+sedFitInputW+","+sedFitInputF+","+sedFitInputErrF+","+sedFitInputFflag+","+ui->distTheoLineEdit->text()+","+ui->prefilterLineEdit->text()+",sed_weights=["+ui->mid_irLineEdit->text()+","+ui->far_irLineEdit->text()+","+ui->submmLineEdit->text()+"],use_wave="+sedFitInputW+",outdir='"+QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED))+"_',delta_chi2="+ui->delta_chi2_lineEdit->text()+")\" ");
+    }
+}
+
+void SEDVisualizerPlot::finishedTheoreticalLocaleFit()
+{
+    /*
+    QString output(process.readAll());
+    //qDebug()<<output;
+    outputSedLog=output;
+
+    ui->outputTextBrowser->setText(output);
+  */
+    readSedFitOutput(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_sedfit_output.dat"));
+
+    //addNewSEDCheckBox("Theoretical Fit");
+    this->setFocus();
+    this->activateWindow();
+}
+
+
+void SEDVisualizerPlot::finishedTheoreticalRemoteFit()
+{
+    QDir dir (QApplication::applicationDirPath());
+
+    dir.rename("sedfit_output.dat",QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_sedfit_output.dat"));
+    readSedFitOutput(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_sedfit_output.dat"));
+
+    this->setFocus();
+    this->activateWindow();
+}
+
+void SEDVisualizerPlot::onReadyReadStdOutput()
+{
+    //  process.setReadChannel(QProcess::readAllStandardError());
+    QTextStream stream(process);
+    while (!stream.atEnd()) {
+        QString line = stream.readLine();
+        // extract progress info from line and etc.
+        ui->outputTextBrowser->append(line);
+    }
+}
+
+//Add a new sed checkbox to the group of SED
+void SEDVisualizerPlot::addNewSEDCheckBox(QString label, int nSED){
+    //qDebug()<<"Adding new SED Check Box "+QString::number(nSED);
+    QSignalMapper* mapper = new QSignalMapper(this);
+    ui->generatedSedBox->setVisible(true);
+    QCheckBox *newSed = new QCheckBox(label+" "+QString::number(nSED+1));
+    newSed->setChecked(true);
+    connect(newSed, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+    mapper->setMapping(newSed, nSED);
+    connect(mapper, SIGNAL(mapped(int)), this, SLOT(on_SEDCheckboxClicked(int)));
+    ui->generatedSedBox->layout()->addWidget(newSed);
+    //nSED++;
+}
+
+
+//Add a new sed checkbox to the group of SED
+void SEDVisualizerPlot::addNewSEDCheckBox(QString label){
+    //qDebug()<<"Adding new SED Check Box "+QString::number(nSED);
+    QSignalMapper* mapper = new QSignalMapper(this);
+    ui->generatedSedBox->setVisible(true);
+    QCheckBox *newSed = new QCheckBox(QString::number(nSED+1)+":"+label);
+    newSed->setChecked(true);
+    connect(newSed, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+    mapper->setMapping(newSed, nSED);
+    connect(mapper, SIGNAL(mapped(int)), this, SLOT(on_SEDCheckboxClicked(int)));
+    ui->generatedSedBox->layout()->addWidget(newSed);
+    nSED++;
+    plottedSedLabels.append(label);
+}
+
+void SEDVisualizerPlot::on_SEDCheckboxClicked(int sedN)
+{
+    //qDebug()<<"processing checkbox "<<sedN;
+    //qDebug()<<"sedGraphs.size() "<<QString::number(sedGraphs.size());
+    QCPGraph * graph=sedGraphs.at(sedN);
+    if(graph->visible()){
+        //qDebug()<<"Remove Graph "<<sedGraphPoints.contains(sedN);
+        graph->setVisible(false);
+        //temporaryGraph->setVisible(false);
+        //qDebug()<<"graph visibility "<<graph->visible();
+        if(sedGraphPoints.contains(sedN)){
+            QList<SEDPlotPointCustom *> points=sedGraphPoints.value(sedN);
+            for (int i = 0; i < points.size(); ++i)
+            {
+                SEDPlotPointCustom *cp;
+                cp = points.at(i);
+                cp->setVisible(false);
+                //qDebug()<<"points visibility "<<cp->visible();
+            }
+        }
+        //        ui->customPlot->removePlottable(graph);
+        ui->resultsTableWidget->clearContents();
+        ui->resultsTableWidget->setRowCount(0);
+    }else{
+        ////qDebug()<<"Add Graph";
+        //ui->customPlot->addGraph();
+        //ui->customPlot->graph();
+        graph->setVisible(true);
+        //qDebug()<<"graph visibility "<<graph->visible();
+        if(sedGraphPoints.contains(sedN)){ // This is a theoretical SED fit
+            QList<SEDPlotPointCustom *> points=sedGraphPoints.value(sedN);
+            for (int i = 0; i < points.size(); ++i)
+            {
+                SEDPlotPointCustom *cp;
+                cp = points.at(i);
+                cp->setVisible(true);
+            }
+            loadSedFitOutput(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(sedN)+"_sedfit_output.dat"));
+        }else{ // This is a Thin or Thick SED fit
+            QString label=plottedSedLabels.at(sedN);
+            if(label.contains("Thick")){
+                loadSedFitThick(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(sedN)+"_thickfile.csv.par"));
+            }else{
+                loadSedFitThin(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(sedN)+"_thinfile.csv.par"));
+
+            }
+        }
+
+    }
+    ui->customPlot->replot();
+}
+
+
+void SEDVisualizerPlot::on_ThinLocalFit_triggered()
+{
+
+    sedFitInputF= "]";
+    sedFitInputErrF= "]";
+    sedFitInputW= "]";
+
+    sedFitInput.clear();
+    /*  if(multiSelectMOD){
+        prepareSelectedInputForSedFit();
+    }else{
+        prepareInputForSedFit(sed_list.at(0)->getRootNode());
+    }
+    */
+    bool validFit=false;
+
+    validFit=prepareSelectedInputForSedFit();
+
+    if(validFit)
+    {
+
+
+        sedFitInputF ="["+sedFitInputF ;
+        sedFitInputErrF ="["+sedFitInputErrF ;
+        sedFitInputW ="["+sedFitInputW;
+
+
+        QString f=sedFitInputW.mid(1,sedFitInputW.length()-2);
+
+        QStringList wave = f.split(",");
+
+        QSignalMapper* mapper = new QSignalMapper(this);
+
+        sd_thin = new SedFitGrid_thin(this);
+        sd_thin->ui->distLineEdit->setText(ui->distTheoLineEdit->text());
+        sedFitInputUlimitString="[";
+
+        for(int i=0;i<wave.length();i++)
+        {
+            //qDebug()<<wave.at(i);
+            sedFitInputUlimit.insert(i,"0");
+
+            sedFitInputUlimitString+="0";
+            if(i<wave.size()-1)
+                sedFitInputUlimitString+=",";
+
+            int row= sd_thin->ui->tableWidget->model()->rowCount();
+            sd_thin->ui->tableWidget->insertRow(row);
+
+            QCheckBox  *cb1= new QCheckBox();
+            cb1->setChecked(false);
+            sd_thin->ui->tableWidget->setCellWidget(row,1,cb1);
+
+            connect(cb1, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+            mapper->setMapping(cb1, row);
+
+            QTableWidgetItem *item_1 = new QTableWidgetItem();
+            item_1->setText(wave.at(i));
+
+            sd_thin->ui->tableWidget->setItem(row,0,item_1);
+
+        }
+        sedFitInputUlimitString+="]";
+        connect(mapper, SIGNAL(mapped(int)), this, SLOT(checkboxClicked(int)));
+
+        //qDebug()<<sedFitInputUlimitString;
+
+
+
+        sd_thin->show();
+
+        connect( sd_thin->ui->pushButton ,SIGNAL(clicked()),this,SLOT(doThinLocalFit()));
+    }
+}
+
+
+void SEDVisualizerPlot::checkboxClicked(int cb)
+{
+
+    sedFitInputUlimit[cb].compare("0")==0? sedFitInputUlimit[cb]="1" : sedFitInputUlimit[cb]="0";
+
+
+    sedFitInputUlimitString="[";
+
+    for(int i=0;i<sedFitInputUlimit.size();i++)
+    {
+        sedFitInputUlimitString+=sedFitInputUlimit[i];
+        if(i<sedFitInputUlimit.size()-1)
+            sedFitInputUlimitString+=",";
+    }
+    sedFitInputUlimitString+="]";
+
+
+}
+
+void SEDVisualizerPlot::doThinLocalFit()
+{
+    sd_thin->close();
+
+    ui->outputTextBrowser->setText("");
+
+    process= new QProcess();
+    connect(process,SIGNAL(readyReadStandardError()),this,SLOT(onReadyReadStdOutput()));
+    connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadyReadStdOutput()));
+    connect(process,SIGNAL(finished(int)),this,SLOT(finishedThinLocalFit()));
+
+    process->setProcessChannelMode(QProcess::MergedChannels);
+
+    QString mrange="["+ sd_thin->ui->minMassLineEdit->text()+", "+sd_thin->ui->maxMassLineEdit->text()+", "+ sd_thin->ui->stepMassLineEdit->text() +"]";
+    QString trange="["+ sd_thin->ui->tempMinLineEdit->text()+", "+sd_thin->ui->tempMaxLineEdit->text()+", "+ sd_thin->ui->tempStepLineEdit->text() +"]";
+    QString brange="["+ sd_thin->ui->betaMinLineEdit->text()+", "+sd_thin->ui->betaMaxLineEdit->text()+", "+ sd_thin->ui->betaStepLineEdit->text() +"]";
+    QString srefRange="["+ sd_thin->ui->srefOpacityLineEdit->text()+", "+ sd_thin->ui->srefWavelengthLineEdit->text() +"]";
+
+    qDebug()<<idlPath<<" -e \"sedfitgrid_engine_thin_vialactea,"<<sedFitInputW<<", "<<sedFitInputF<<","<<mrange<<","<<trange<<" ,"<<brange<<" ,"<<sd_thin->ui->distLineEdit->text()<<","<<srefRange<<",lambdatn,flussotn,mtn,ttn,btn,ltn,dmtn,dttn,errorbars="<<sedFitInputErrF<<",ulimit="<<sedFitInputUlimitString<<",printfile= '"<<QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thinfile.csv")<<"'";
+    process->start (idlPath+" -e \"sedfitgrid_engine_thin_vialactea,"+sedFitInputW+","+sedFitInputF+","+mrange+","+trange+" ,"+brange+" ,"+sd_thin->ui->distLineEdit->text()+","+srefRange+",lambdatn,flussotn,mtn,ttn,btn,ltn,dmtn,dttn,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= '"+QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thinfile.csv")+"'");
+
+    // process.waitForFinished(); // sets current thread to sleep and waits for process end
+}
+
+void SEDVisualizerPlot::doThinRemoteFit()
+
+{
+    sd_thin->close();
+
+    ui->outputTextBrowser->setText("");
+
+    process= new QProcess();
+    connect(process,SIGNAL(readyReadStandardError()),this,SLOT(onReadyReadStdOutput()));
+    connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadyReadStdOutput()));
+    connect(process,SIGNAL(finished(int)),this,SLOT(finishedThinRemoteFit()));
+
+    process->setProcessChannelMode(QProcess::MergedChannels);
+
+    QString mrange="["+ sd_thin->ui->minMassLineEdit->text()+", "+sd_thin->ui->maxMassLineEdit->text()+", "+ sd_thin->ui->stepMassLineEdit->text() +"]";
+    QString trange="["+ sd_thin->ui->tempMinLineEdit->text()+", "+sd_thin->ui->tempMaxLineEdit->text()+", "+ sd_thin->ui->tempStepLineEdit->text() +"]";
+    QString brange="["+ sd_thin->ui->betaMinLineEdit->text()+", "+sd_thin->ui->betaMaxLineEdit->text()+", "+ sd_thin->ui->betaStepLineEdit->text() +"]";
+    QString srefRange="["+ sd_thin->ui->srefOpacityLineEdit->text()+", "+ sd_thin->ui->srefWavelengthLineEdit->text() +"]";
+
+    ////qDebug()<<"java -jar  \"sedfitgrid_engine_thin_vialactea,"<<sedFitInputW<<", "<<sedFitInputF<<","<<mrange<<","<<trange<<" ,"<<brange<<" ,"<<sd_thin->ui->distLineEdit->text()<<","<<srefRange<<",lambdatn,flussotn,mtn,ttn,btn,ltn,dmtn,dttn,errorbars="<<sedFitInputErrF<<",ulimit="<<sedFitInputUlimitString<<",printfile= '"<<QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thinfile.csv")<<"'";
+    //process->start (idlPath+" -e \"sedfitgrid_engine_thin_vialactea,"+sedFitInputW+","+sedFitInputF+","+mrange+","+trange+" ,"+brange+" ,"+sd_thin->ui->distLineEdit->text()+","+srefRange+",lambdatn,flussotn,mtn,ttn,btn,ltn,dmtn,dttn,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= '"+QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thinfile.csv")+"'");
+
+
+
+
+    //qDebug()<<" java -jar "<<QApplication::applicationDirPath().append("/vsh-ws-client.jar")<<" \"sedfitgrid_engine_thin_vialactea,"+sedFitInputW+","+sedFitInputF+","+mrange+","+trange+","+brange+","+sd_thin->ui->distLineEdit->text()+","+srefRange+",lambdatn,flussotn,mtn,ttn,btn,ltn,dmtn,dttn,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= 'sedfit_output.dat'";
+
+    process->setWorkingDirectory(QApplication::applicationDirPath());
+
+    //qDebug()<<"DIR: "<<process->workingDirectory();
+    //qDebug() << "App path : " << qApp->applicationDirPath();
+    //qDebug()<<"QDir::currentPath() :"<<QDir::currentPath() ;
+
+    process->start ("java -jar "+QApplication::applicationDirPath().append("/vsh-ws-client.jar")+" \"sedfitgrid_engine_thin_vialactea,"+sedFitInputW+","+sedFitInputF+","+mrange+","+trange+","+brange+","+sd_thin->ui->distLineEdit->text()+","+srefRange+",lambdatn,flussotn,mtn,ttn,btn,ltn,dmtn,dttn,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= 'sedfit_output.dat'\" ");
+    qDebug()<<"sedfitgrid_engine_thin_vialactea,"+sedFitInputW+","+sedFitInputF+","+mrange+","+trange+","+brange+","+sd_thin->ui->distLineEdit->text()+","+srefRange+",lambdatn,flussotn,mtn,ttn,btn,ltn,dmtn,dttn,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= 'sedfit_output.dat'\"";
+
+
+    ui->outputTextBrowser->append("java -jar "+QApplication::applicationDirPath().append("/vsh-ws-client.jar")+" \"sedfitgrid_engine_thin_vialactea,"+sedFitInputW+","+sedFitInputF+","+mrange+","+trange+","+brange+","+sd_thin->ui->distLineEdit->text()+","+srefRange+",lambdatn,flussotn,mtn,ttn,btn,ltn,dmtn,dttn,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= 'sedfit_output.dat'\" ");
+    ui->outputTextBrowser->append("\nDIR: "+process->workingDirectory());
+    ui->outputTextBrowser->append("\nApp path : " +qApp->applicationDirPath());
+    ui->outputTextBrowser->append("\nQDir::currentPath() :"+QDir::currentPath());
+
+
+    // process.waitForFinished(); // sets current thread to sleep and waits for process end
+}
+
+
+void SEDVisualizerPlot::doThickRemoteFit()
+{
+
+
+    sd_thick->close();
+    ui->outputTextBrowser->setText("");
+
+    process= new QProcess();
+    connect(process,SIGNAL(readyReadStandardError()),this,SLOT(onReadyReadStdOutput()));
+    connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadyReadStdOutput()));
+    connect(process,SIGNAL(finished(int)),this,SLOT(finishedThickRemoteFit()));
+
+    process->setProcessChannelMode(QProcess::MergedChannels);
+    QString trange="["+ sd_thick->ui->tempMinLineEdit->text()+", "+sd_thick->ui->tempMaxLineEdit->text()+", "+ sd_thick->ui->tempStepLineEdit->text() +"]";
+    QString brange="["+ sd_thick->ui->betaMinLineEdit->text()+", "+sd_thick->ui->betaMaxLineEdit->text()+", "+ sd_thick->ui->betaStepLineEdit->text() +"]";
+    QString l0range="["+ sd_thick->ui->minLambda_0LineEdit->text()+", "+sd_thick->ui->maxLambda_0LineEdit->text()+", "+ sd_thick->ui->stepLambda_0LineEdit->text() +"]";
+    QString sfactrange="["+ sd_thick->ui->scaleMinLineEdit->text()+", "+sd_thick->ui->scaleMaxLineEdit->text()+", "+ sd_thick->ui->scaleStepLineEdit->text() +"]";
+    QString srefRange="["+ sd_thick->ui->srefOpacityLineEdit->text()+", "+ sd_thick->ui->srefWavelengthLineEdit->text() +"]";
+
+    // process->start (idlPath+" -e \"sedfitgrid_engine_thick_vialactea,"+sedFitInputW+","+sedFitInputF+","+sd_thick->ui->sizeLineEdit->text()+","+trange+" ,"+brange+" ,"+l0range+", "+sfactrange+","+sd_thick->ui->distLineEdit->text()+","+srefRange+",lambdagb,flussogb,mtk,ttk,btk,l0,sizesec,ltk,dmtk,dtk,dl0,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= '"+QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thickfile.csv")+"'");
+
+    process->setWorkingDirectory(QApplication::applicationDirPath());
+    process->start ("java -jar "+QApplication::applicationDirPath().append("/vsh-ws-client.jar")+" \"sedfitgrid_engine_thick_vialactea,"+sedFitInputW+","+sedFitInputF+","+sd_thick->ui->sizeLineEdit->text()+","+trange+" ,"+brange+" ,"+l0range+", "+sfactrange+","+sd_thick->ui->distLineEdit->text()+","+srefRange+",lambdagb,flussogb,mtk,ttk,btk,l0,sizesec,ltk,dmtk,dtk,dl0,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= 'sedfit_output.dat'\" ");
+
+    qDebug()<<"sedfitgrid_engine_thick_vialactea,"+sedFitInputW+","+sedFitInputF+","+sd_thick->ui->sizeLineEdit->text()+","+trange+" ,"+brange+" ,"+l0range+", "+sfactrange+","+sd_thick->ui->distLineEdit->text()+","+srefRange+",lambdagb,flussogb,mtk,ttk,btk,l0,sizesec,ltk,dmtk,dtk,dl0,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= 'sedfit_output.dat'\" ";
+
+
+
+}
+
+void SEDVisualizerPlot::finishedThinLocalFit()
+{
+    readThinFit(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thinfile.csv"));
+    addNewSEDCheckBox("Thin Fit");
+}
+
+void SEDVisualizerPlot::readThinFit(QString resultPath){
+    QFile file(resultPath);
+    if (!file.open(QIODevice::ReadOnly)) {
+        //qDebug() << file.errorString();
+        //qDebug() << "ERRORE Disegno FIT";
+        return ;
+    }
+
+    QVector<double> x, y;
+
+    while (!file.atEnd()) {
+        QByteArray line = file.readLine();
+
+        x.append( line.split(',').first().simplified().toDouble() );
+        y.append( line.split(',').last().simplified().toDouble() );
+
+    }
+
+    ui->customPlot->addGraph();
+    ui->customPlot->graph()->setData(x, y);
+    ui->customPlot->graph()->setScatterStyle( QCPScatterStyle::ssNone);
+    sedGraphs.push_back(ui->customPlot->graph());
+    ui->customPlot->replot();
+
+    ui->resultsTableWidget->clearContents();
+    ui->resultsTableWidget->setColumnCount(6);
+    ui->resultsTableWidget->setRowCount(0);
+    ui->resultsTableWidget->setHorizontalHeaderLabels(QStringList() << "fmass" << "dmass" << "ftemp" << "dtemp" << "fbeta" << "lum");
+    ui->resultsTableWidget->insertRow(0);
+
+    QFile filepar(resultPath+".par");
+    if (!filepar.open(QIODevice::ReadOnly)) {
+        //qDebug() << file.errorString();
+        //qDebug() << "ERRORE Parametri";
+        return ;
+    }
+    QString line=filepar.readLine();
+    filepar.close();
+    QList<QString> line_list_string=line.split(',');
+    for(int i=0; i<line_list_string.size();i++){
+        ui->resultsTableWidget->setItem(0,i,new QTableWidgetItem(line_list_string.at(i).trimmed()));
+    }
+    ui->resultsTableWidget->resizeColumnsToContents();
+
+}
+
+void SEDVisualizerPlot::readThickFit(QString resultPath){
+    QFile file(resultPath);
+    if (!file.open(QIODevice::ReadOnly)) {
+        //qDebug() << file.errorString();
+        //qDebug() << "ERRORE Disegno FIT";
+        return ;
+    }
+
+    QVector<double> x, y;
+
+    while (!file.atEnd()) {
+        QByteArray line = file.readLine();
+
+        x.append( line.split(',').first().simplified().toDouble() );
+        y.append( line.split(',').last().simplified().toDouble() );
+
+    }
+
+    ui->customPlot->addGraph();
+    ui->customPlot->graph()->setData(x, y);
+    ui->customPlot->graph()->setScatterStyle( QCPScatterStyle::ssNone);
+    sedGraphs.push_back(ui->customPlot->graph());
+    ui->customPlot->replot();
+
+    ui->resultsTableWidget->clearContents();
+    ui->resultsTableWidget->setColumnCount(9);
+    ui->resultsTableWidget->setRowCount(0);
+    ui->resultsTableWidget->setHorizontalHeaderLabels(QStringList() << "mass" << "dmass" << "ftemp" << "dtemp" << "fbeta" << "fl0" << "dlam0" << "sizesec" << "lum");
+    ui->resultsTableWidget->insertRow(0);
+    QFile filepar(resultPath+".par");
+    if (!filepar.open(QIODevice::ReadOnly)) {
+        //qDebug() << filepar.errorString();
+        return ;
+    }
+    QString line=filepar.readLine();
+    filepar.close();
+    QList<QString> line_list_string=line.split(',');
+    for(int i=0; i<line_list_string.size();i++){
+        ui->resultsTableWidget->setItem(0,i,new QTableWidgetItem(line_list_string.at(i).trimmed()));
+    }
+    ui->resultsTableWidget->resizeColumnsToContents();
+
+}
+
+void SEDVisualizerPlot::doThickLocalFit()
+{
+
+    sd_thick->close();
+    ui->outputTextBrowser->setText("");
+
+    process= new QProcess();
+    connect(process,SIGNAL(readyReadStandardError()),this,SLOT(onReadyReadStdOutput()));
+    connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadyReadStdOutput()));
+    connect(process,SIGNAL(finished(int)),this,SLOT(finishedThickLocalFit()));
+
+    process->setProcessChannelMode(QProcess::MergedChannels);
+    QString trange="["+ sd_thick->ui->tempMinLineEdit->text()+", "+sd_thick->ui->tempMaxLineEdit->text()+", "+ sd_thick->ui->tempStepLineEdit->text() +"]";
+    QString brange="["+ sd_thick->ui->betaMinLineEdit->text()+", "+sd_thick->ui->betaMaxLineEdit->text()+", "+ sd_thick->ui->betaStepLineEdit->text() +"]";
+    QString l0range="["+ sd_thick->ui->minLambda_0LineEdit->text()+", "+sd_thick->ui->maxLambda_0LineEdit->text()+", "+ sd_thick->ui->stepLambda_0LineEdit->text() +"]";
+    QString sfactrange="["+ sd_thick->ui->scaleMinLineEdit->text()+", "+sd_thick->ui->scaleMaxLineEdit->text()+", "+ sd_thick->ui->scaleStepLineEdit->text() +"]";
+    QString srefRange="["+ sd_thick->ui->srefOpacityLineEdit->text()+", "+ sd_thick->ui->srefWavelengthLineEdit->text() +"]";
+
+    //qDebug()<<"sedfitgrid_engine_thick_vialactea,"+sedFitInputW+","+sedFitInputF+","+sd_thick->ui->sizeLineEdit->text()+","+trange+" ,"+brange+" ,"+l0range+", "+sfactrange+","+sd_thick->ui->distLineEdit->text()+","+srefRange+",lambdagb,flussogb,mtk,ttk,btk,l0,sizesec,ltk,dmtk,dtk,dl0,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString;
+
+    process->start (idlPath+" -e \"sedfitgrid_engine_thick_vialactea,"+sedFitInputW+","+sedFitInputF+","+sd_thick->ui->sizeLineEdit->text()+","+trange+" ,"+brange+" ,"+l0range+", "+sfactrange+","+sd_thick->ui->distLineEdit->text()+","+srefRange+",lambdagb,flussogb,mtk,ttk,btk,l0,sizesec,ltk,dmtk,dtk,dl0,errorbars="+sedFitInputErrF+",ulimit="+sedFitInputUlimitString+",printfile= '"+QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thickfile.csv")+"'");
+
+
+
+}
+
+
+
+
+void SEDVisualizerPlot::finishedThinRemoteFit()
+{
+
+
+    QDir dir (QApplication::applicationDirPath());
+
+    dir.rename("sedfit_output.dat",QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thickfile.csv"));
+    dir.rename("sedfit_output.dat.par",QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thickfile.csv.par"));
+
+
+    // readSedFitOutput(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_sedfit_output.dat"));
+
+
+    readThickFit(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thickfile.csv"));
+
+    addNewSEDCheckBox("Thin Fit");
+
+}
+
+void SEDVisualizerPlot::finishedThickRemoteFit()
+{
+
+
+    QDir dir (QApplication::applicationDirPath());
+
+    dir.rename("sedfit_output.dat",QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thickfile.csv"));
+    dir.rename("sedfit_output.dat.par",QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thickfile.csv.par"));
+
+
+    readThickFit(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thickfile.csv"));
+
+    addNewSEDCheckBox("Thick Fit");
+
+}
+
+void SEDVisualizerPlot::finishedThickLocalFit()
+{
+
+
+
+
+
+    readThickFit(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/SED"+QString::number(nSED)+"_thickfile.csv"));
+    addNewSEDCheckBox("Thick Fit");
+    
+}
+
+
+void SEDVisualizerPlot::on_ThickLocalFit_triggered()
+{
+    sedFitInputF= "]";
+    sedFitInputErrF= "]";
+    sedFitInputW= "]";
+
+    sedFitInput.clear();
+    /*  if(multiSelectMOD){
+        prepareSelectedInputForSedFit();
+    }else{
+        prepareInputForSedFit(sed_list.at(0)->getRootNode());
+    }
+    */
+
+    bool validFit=false;
+
+    validFit=prepareSelectedInputForSedFit();
+
+    if(validFit)
+    {
+
+
+        sedFitInputF ="["+sedFitInputF ;
+        sedFitInputErrF ="["+sedFitInputErrF ;
+        sedFitInputW ="["+sedFitInputW;
+
+        //qDebug() << "f: " <<sedFitInputF;
+        //qDebug() << "e_f: " <<sedFitInputErrF;
+        //qDebug() << "w: " <<sedFitInputW;
+
+
+        QString f=sedFitInputW.mid(1,sedFitInputW.length()-2);
+
+        QStringList wave = f.split(",");
+
+        QSignalMapper* mapper = new QSignalMapper(this);
+
+        sd_thick = new SedFitgrid_thick(this);
+        sd_thick->ui->distLineEdit->setText(ui->distTheoLineEdit->text());
+
+        sedFitInputUlimitString="[";
+
+        for(int i=0;i<wave.length();i++)
+        {
+            //qDebug()<<wave.at(i);
+            sedFitInputUlimit.insert(i,"0");
+
+            sedFitInputUlimitString+="0";
+            if(i<wave.size()-1)
+                sedFitInputUlimitString+=",";
+
+            int row= sd_thick->ui->tableWidget->model()->rowCount();
+            sd_thick->ui->tableWidget->insertRow(row);
+
+            QCheckBox  *cb1= new QCheckBox();
+            cb1->setChecked(false);
+            sd_thick->ui->tableWidget->setCellWidget(row,1,cb1);
+
+            connect(cb1, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+            mapper->setMapping(cb1, row);
+
+            QTableWidgetItem *item_1 = new QTableWidgetItem();
+            item_1->setText(wave.at(i));
+
+            sd_thick->ui->tableWidget->setItem(row,0,item_1);
+
+        }
+        sedFitInputUlimitString+="]";
+        connect(mapper, SIGNAL(mapped(int)), this, SLOT(checkboxClicked(int)));
+
+        sd_thick->show();
+        connect( sd_thick->ui->pushButton ,SIGNAL(clicked()),this,SLOT(doThickLocalFit()));
+    }
+}
+
+void SEDVisualizerPlot::executeRemoteCall(QString sedFitInputW, QString sedFitInputF,QString sedFitInputErrF, QString sedFitInputFflag, QMap<double,double> sedFitInput){
+    //qDebug()<<"Sono it thread executeRemoteCall"<<QThread::currentThread();
+
+    QString filename="sed_fit/execute.bin";
+    QFile file( filename );
+
+    if ( file.open(QIODevice::ReadWrite | QIODevice::Truncate) )
+    {
+        QTextStream stream( &file );
+        stream.reset();
+        stream <<"#!/bin/bash"<<endl;
+        stream <<"unzip scripts.zip"<<endl;
+        //stream <<"/usr/local/bin/idl -e \"sedpar=vialactea_tap_sedfit("+sedFitInputW+","+sedFitInputF+","+sedFitInputFflag+",1000.,0.2,sed_weights=[1.,1.,1.],use_wave="+QString::number(sedFitInput.keys()[0])+")\"" << endl;
+        stream <<"/usr/local/bin/idl -e \"sedpar=vialactea_tap_sedfit_v3("+sedFitInputW+","+sedFitInputF+","+sedFitInputErrF+","+sedFitInputFflag+",2000.,0.8,sed_weights=[1.,1.,1.],use_wave="+sedFitInputW+")\" &> log.dat"<< endl;
+        stream <<"zip output.zip *dat"<<endl;
+    }
+
+
+    //qDebug()<<"/usr/local/bin/idl -e \"sedpar=vialactea_tap_sedfit_v3("+sedFitInputW+","+sedFitInputF+","+sedFitInputErrF+","+sedFitInputFflag+",2000.,0.8,sed_weights=[1.,1.,1.],use_wave="+sedFitInputW+")\"  &> log.dat"<< endl;
+
+
+    QProcess process_zip;
+    process_zip.start ("zip -j sed_fit/inputs.zip sed_fit/scripts.zip sed_fit/execute.bin ");
+    process_zip.waitForFinished(); // sets current thread to sleep and waits for process end
+
+    //curl -k -F m=submit -F pass=hp39A11  -F wfdesc=@workflow.xml -F inputzip=@inputs.zip -F portmapping=@portmapping.txt -F certs=@certs.zip http://via-lactea-sg00.iaps.inaf.it:8080/wspgrade/RemoteServlet
+    QProcess process;
+    process.start ("curl -k -F m=submit -F pass=hp39A11  -F wfdesc=@sed_fit/workflow.xml -F inputzip=@sed_fit/inputs.zip -F portmapping=@sed_fit/portmapping.txt -F certs=@sed_fit/certs.zip http://via-lactea-sg00.iaps.inaf.it:8080/wspgrade/RemoteServlet");
+    //qDebug()<<"curl -k -F m=submit -F pass=hp39A11  -F wfdesc=@sed_fit/workflow.xml -F inputzip=@sed_fit/inputs.zip -F portmapping=@sed_fit/portmapping.txt -F certs=@sed_fit/certs.zip http://via-lactea-sg00.iaps.inaf.it:8080/wspgrade/RemoteServlet";
+    process.waitForFinished(); // sets current thread to sleep and waits for process end
+    QString output(process.readAll());
+    //qDebug()<<output.simplified();
+
+    /*
+    QString output_status="";
+
+    QProcess process_status;
+    //output_status.compare("finished")!=0 ||
+
+    while (output_status.compare("finished")!=0)
+    {
+        process_status.start ("curl -k -F m=info -F pass=hp39A11 -F ID="+output.simplified()+" http://via-lactea-sg00.iaps.inaf.it:8080/wspgrade/RemoteServlet");
+        process_status.waitForFinished(); // sets current thread to sleep and waits for process end
+        output_status= process_status.readAll().simplified();
+        //qDebug()<<output_status;
+        if(output_status.compare("error")==0){
+            return;
+        }
+    }
+    */
+    //extern bool checkIDRemoteCall(QString id);
+    QString id = output.simplified();
+    QFuture<bool> future = QtConcurrent::run(checkIDRemoteCall, id);
+    //    future.waitForFinished();
+    bool finished=future.result();
+    if(finished){
+
+
+        QProcess process_download;
+        process_download.start ("curl -k -F m=download -F ID="+output.simplified()+" -F pass=hp39A11 -o sed_fit/output.zip http://via-lactea-sg00.iaps.inaf.it:8080/wspgrade/RemoteServlet");
+        process_download.waitForFinished(); // sets current thread to sleep and waits for process end
+        QString output_download(process_download.readAll());
+        //qDebug()<<output_download.simplified();
+
+
+        QProcess process_unzip;
+        //process_unzip.start ("unzip ""sed_fit/output.zip");
+        process_unzip.start ("unzip sed_fit/output.zip -d "+QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/"));
+        process_unzip.waitForFinished(); // sets current thread to sleep and waits for process end
+        QString output_unzip(process_unzip.readAll());
+        //qDebug()<<output_unzip.simplified();
+
+        QStringList pieces = output_unzip.split( " " );
+        //qDebug()<<pieces[pieces.length()-3];
+        QString path=pieces[pieces.length()-3];
+
+        path.replace(path.indexOf("guse.jsdl"),path.size(), QLatin1String("output.zip"));
+        //qDebug() << path;
+
+        //qDebug()<<"---- "<<"unzip "+path+" -d "+QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/");
+        QProcess process_unzip_2;
+        process_unzip_2.start ("unzip "+path+" -d "+QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/"));
+        process_unzip_2.waitForFinished(); // sets current thread to sleep and waits for process end
+        QString output_unzip_2(process_unzip_2.readAll());
+        //qDebug()<<output_unzip_2.simplified();
+
+
+        //    readSedFitOutput(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/sedfit_output.dat"));
+
+        //    QFile file_log( QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/log.dat"));
+        //    if (!file_log.open(QFile::ReadOnly | QFile::Text)) return;
+        //    QTextStream in(&file_log);
+        //    outputSedLog=in.readAll();
+        //    ui->outputTextBrowser->setText(outputSedLog);
+        //    //qDebug()<<"****LOG***"<<outputSedLog;
+    }
+
+}
+
+bool SEDVisualizerPlot::checkIDRemoteCall(QString id){
+
+    //qDebug()<<"Sono it thread "<<QThread::currentThread();
+    QString output_status="";
+
+    QProcess process_status;
+
+    while (output_status.compare("finished")!=0)
+    {
+        process_status.start ("curl -k -F m=info -F pass=hp39A11 -F ID="+id+" http://via-lactea-sg00.iaps.inaf.it:8080/wspgrade/RemoteServlet");
+        process_status.waitForFinished(); // sets current thread to sleep and waits for process end
+        output_status= process_status.readAll().simplified();
+        ////qDebug()<<output_status;
+        if(output_status.compare("error")==0){
+            return false;
+        }
+    }
+    return true;
+}
+
+void SEDVisualizerPlot::on_TheoreticalRemoteFit_triggered()
+{
+
+    bool validFit=false;
+
+    sedFitInputFflag = "]";
+    sedFitInputF= "]";
+    sedFitInputW= "]";
+
+    sedFitInputErrF= "]";
+
+
+    sedFitInput.clear();
+
+    // if(multiSelectMOD){
+    validFit=prepareSelectedInputForSedFit();
+    // }
+    /* else{
+        validFit=prepareInputForSedFit(sed_list.at(0)->getRootNode());
+    }
+    */
+
+    if(validFit)
+    {
+        sedFitInputF ="["+sedFitInputF ;
+        sedFitInputW ="["+sedFitInputW;
+        sedFitInputFflag = "["+sedFitInputFflag;
+        sedFitInputErrF ="["+sedFitInputErrF ;
+
+        loading = new LoadingWidget();
+        loading->init();
+        loading->setFileName("Executing vialactea_tap_sedfit");
+        loading ->show();
+        loading->activateWindow();
+        loading->setFocus();
+
+        ui->outputTextBrowser->setText("");
+
+        process= new QProcess();
+
+        process->setProcessChannelMode(QProcess::MergedChannels);
+
+        //qDebug()<<"sedFitInputErrF: "<<sedFitInputErrF;
+
+        //qDebug()<<" java -jar "<<QApplication::applicationDirPath().append("/vsh-ws-client.jar")<<" \"sedpar=vialactea_tap_sedfit_v7("+sedFitInputW+","+sedFitInputF+","+sedFitInputErrF+","+sedFitInputFflag+","+ui->distTheoLineEdit->text()+","+ui->prefilterLineEdit->text()+",sed_weights=["+ui->mid_irLineEdit->text()+","+ui->far_irLineEdit->text()+","+ui->submmLineEdit->text()+"],use_wave="+sedFitInputW+",outdir='./',delta_chi2="+ui->delta_chi2_lineEdit->text()+")\" ";
+
+
+
+        connect(process,SIGNAL(readyReadStandardError()),this,SLOT(onReadyReadStdOutput()));
+        connect(process,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadyReadStdOutput()));
+        connect(process,SIGNAL(finished(int)),this,SLOT(finishedTheoreticalRemoteFit()));
+
+
+        process->setWorkingDirectory(QApplication::applicationDirPath());
+        process->start ("java -jar "+QApplication::applicationDirPath().append("/vsh-ws-client.jar")+" \"sedpar=vialactea_tap_sedfit_v7("+sedFitInputW+","+sedFitInputF+","+sedFitInputErrF+","+sedFitInputFflag+","+ui->distTheoLineEdit->text()+","+ui->prefilterLineEdit->text()+",sed_weights=["+ui->mid_irLineEdit->text()+","+ui->far_irLineEdit->text()+","+ui->submmLineEdit->text()+"],use_wave="+sedFitInputW+",outdir='./',delta_chi2="+ui->delta_chi2_lineEdit->text()+")\" ");
+
+        qDebug()<<"vialactea_tap_sedfit_v7("+sedFitInputW+","+sedFitInputF+","+sedFitInputErrF+","+sedFitInputFflag+","+ui->distTheoLineEdit->text()+","+ui->prefilterLineEdit->text()+",sed_weights=["+ui->mid_irLineEdit->text()+","+ui->far_irLineEdit->text()+","+ui->submmLineEdit->text()+"],use_wave="+sedFitInputW+",outdir='./',delta_chi2="+ui->delta_chi2_lineEdit->text()+")\" ";
+    }
+}
+
+
+
+void SEDVisualizerPlot::handleFinished(){
+    //qDebug()<<"handleFinished";
+    readSedFitOutput(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/sedfit_output.dat"));
+    QFile file_log( QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download/log.dat"));
+    if (!file_log.open(QFile::ReadOnly | QFile::Text)) return;
+    QTextStream in(&file_log);
+    outputSedLog=in.readAll();
+    ui->outputTextBrowser->setText(outputSedLog);
+    //qDebug()<<"****LOG***"<<outputSedLog;
+    loading->close();
+    addNewSEDCheckBox("Theoretical Fit");
+}
+
+void SEDVisualizerPlot::on_logRadioButton_toggled(bool checked)
+{
+    if(checked==true){
+        //Show log file
+        ui->outputTextBrowser->setText(outputSedLog);
+        ui->resultsTableWidget->hide();
+        ui->outputTextBrowser->show();
+        if(temporaryRow!=0){
+            ui->customPlot->removeGraph(temporaryGraph);
+            for (int i = 0; i < temporaryGraphPoints.size(); ++i)
+            {
+                SEDPlotPointCustom *cp;
+                cp = temporaryGraphPoints.at(i);
+                ui->customPlot->removeItem(cp);
+            }
+            temporaryGraphPoints.clear();
+            temporaryRow=0;
+        }
+
+    }else{
+        //Show results
+        ui->outputTextBrowser->setText(outputSedResults);
+        ui->resultsTableWidget->show();
+        ui->outputTextBrowser->hide();
+        //ui->resultsTableWidget->setColumnCount(2);
+        //ui->resultsTableWidget->setRowCount(2);
+        //ui->resultsTableWidget->setItem(0, 1, new QTableWidgetItem("Hello"));
+    }
+}
+
+void SEDVisualizerPlot::on_clearAllButton_clicked()
+{
+    //qDebug()<<"Graphs size "<<sedGraphs.size();
+    int size=sedGraphs.size();
+    for (int i = 0; i < size; ++i)
+    {
+        QCPGraph *qcp;
+        qcp = sedGraphs.at(i);
+        ui->customPlot->removeGraph(qcp);
+        //sedGraphs.removeAt(i);
+    }
+    //TODO: To check if items are removed from memory!
+    sedGraphs.clear();
+    //qDebug()<<"Graphs size "<<sedGraphs.size();
+    //
+    //qDebug()<<"Graph Points size "<<sedGraphPoints.size();
+    QMap <int, QList<SEDPlotPointCustom *> >::iterator iter;
+    for (iter = sedGraphPoints.begin(); iter != sedGraphPoints.end(); ++iter){
+        //qDebug()<<"Processing Graph: "<<iter.key();
+        QList<SEDPlotPointCustom *> points=sedGraphPoints.value(iter.key());
+        //qDebug()<<"Points size: "<<points.size();
+        for (int i = 0; i < points.size(); ++i)
+        {
+            SEDPlotPointCustom *cp;
+            cp = points.at(i);
+            ui->customPlot->removeItem(cp);
+        }
+    }
+    sedGraphPoints.clear();
+
+    if(temporaryRow!=0){//there is a temporary graph still drawn
+        ui->customPlot->removeGraph(temporaryGraph);
+        for (int i = 0; i < temporaryGraphPoints.size(); ++i)
+        {
+            SEDPlotPointCustom *cp;
+            cp = temporaryGraphPoints.at(i);
+            ui->customPlot->removeItem(cp);
+        }
+        temporaryGraphPoints.clear();
+    }
+
+    ui->customPlot->replot();
+    nSED=0;
+    temporaryMOD=false;
+    doubleClicked=false;
+    temporaryRow=0;
+
+    QLayoutItem *item;
+
+    //the key point here is that the layout items are stored inside the layout in a stack
+    while((item = ui->generatedSedBox->layout()->takeAt(1)) != 0) {
+        if (item->widget()) {
+            //if(item->widget()->metaObject()->className()=="QCheckBox"){
+            ui->generatedSedBox->layout()->removeWidget(item->widget());
+            delete item->widget();
+            //}
+        }
+        delete item;
+    }
+    //removeAllGraphs();
+    ui->resultsTableWidget->clearContents();
+    ui->resultsTableWidget->setRowCount(0);
+    ui->outputTextBrowser->setText("");
+    outputSedLog="";
+    QDir tmp_download(QDir::homePath()+"/VisIVODesktopTemp/tmp_download");
+    QStringList filters;
+    filters << "SED*";
+    tmp_download.setNameFilters(filters);
+    QStringList dirList=tmp_download.entryList();
+    //qDebug()<<"Dir size "<<dirList.size();
+    for(int i=0; i<dirList.size(); i++){
+        //qDebug()<<dirList.at(i);
+        QFile sedFile(QDir::homePath()+"/VisIVODesktopTemp/tmp_download/"+dirList.at(i));
+        sedFile.remove();
+    }
+    //tmp_download.removeRecursively();
+    plottedSedLabels.clear();
+}
+
+void SEDVisualizerPlot::on_normalRadioButton_toggled(bool selectNormal)
+{
+    if(selectNormal==true){
+        multiSelectMOD=false;
+        ui->customPlot->setMultiSelectModifier(Qt::ControlModifier);
+        QList<QCPAbstractItem *> list_items=ui->customPlot->selectedItems();
+        for(int i=0;i<list_items.size();i++){
+            list_items.at(i)->setSelected(false);
+        }
+        ui->customPlot->replot();
+    }else{
+        multiSelectMOD=true;
+        ui->customPlot->setMultiSelectModifier(Qt::NoModifier);
+    }
+    //qDebug()<<"multiSelectMOD: "<<multiSelectMOD;
+}
+
+void SEDVisualizerPlot::on_actionSave_triggered()
+{
+    QProcess process_zip;
+    QDir tmp_download(QDir::homePath()+"/VisIVODesktopTemp/tmp_download");
+    QStringList filters;
+    filters << "SED*";
+    tmp_download.setNameFilters(filters);
+    QStringList dirList=tmp_download.entryList();
+    //qDebug()<<"Dir size "<<dirList.size();
+    QString sedZipPath=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/SED.zip";
+    for(int i=0; i<dirList.size(); i++){
+        QString sedPath=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/"+dirList.at(i);
+        process_zip.start ("zip -j "+sedZipPath+" "+sedPath);
+        //qDebug()<<"zip -j "+sedZipPath+" "+sedPath;
+        process_zip.waitForFinished(); // sets current thread to sleep and waits for process end
+        QString output_zip(process_zip.readAll());
+        //qDebug()<<output_zip.simplified();
+
+        //FolderCompressor *fc=new FolderCompressor();
+        //fc->compressFolder(QDir::homePath()+"/VisIVODesktopTemp/tmp_download/"+dirList.at(i), sedZipPath);
+    }
+
+    QString fileName = QFileDialog::getSaveFileName(this,tr("Save SED fits"), QDir::homePath(), tr("*.zip"));
+    if(!fileName.endsWith(".zip",Qt::CaseInsensitive) )
+        fileName.append(".zip");
+    //qDebug()<<"Path: "<<fileName;
+    QFile::copy(sedZipPath, fileName);
+}
+
+void SEDVisualizerPlot::on_actionLoad_triggered()
+{
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Load SED fits"), QDir::homePath(), tr("Archive (*.zip)"));
+    QString sedZipPath=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/SED.zip";
+    QFile::copy(fileName, sedZipPath);
+    QProcess process_unzip;
+    process_unzip.start ("unzip "+sedZipPath+" -d "+QDir::homePath()+"/VisIVODesktopTemp/tmp_download");
+    //qDebug()<<"unzip "+sedZipPath+" -d "+QDir::homePath()+"/VisIVODesktopTemp/tmp_download";
+    process_unzip.waitForFinished(); // sets current thread to sleep and waits for process end
+    QString output_unzip(process_unzip.readAll());
+    //qDebug()<<output_unzip.simplified();
+
+    QDir tmp_download(QDir::homePath()+"/VisIVODesktopTemp/tmp_download");
+    QStringList filters;
+    filters << "SED*";
+    tmp_download.setNameFilters(filters);
+    QStringList dirList=tmp_download.entryList();
+    //qDebug()<<"Dir size "<<dirList.size();
+    for(int i=0; i<dirList.size(); i++){
+        QString sedPath=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/"+dirList.at(i);
+        QString sedNumber = dirList.at(i).mid(3,1); // substring after "SED";
+        //qDebug()<<"SED Number "<<sedNumber;
+        bool ok;
+        int value = sedNumber.toInt(&ok);
+        if (ok) { // is an integer
+            if(sedPath.endsWith("sedfit_model.dat")){
+                //qDebug()<<"sedGraphs.size() before : "<<sedGraphs.size();
+                //qDebug()<<"sedGraphPoints.size() before : "<<sedGraphPoints.size();
+                //qDebug()<<"Drawing "+sedPath;
+                addNewSEDCheckBox("Theoretical Fit");
+                QFile file(sedPath);
+                file.open(QIODevice::ReadOnly);
+                QDataStream in(&file);    // read the data serialized from the file
+                QVector<QStringList> headerAndValueList;
+                in >> headerAndValueList;           // extract data
+                setModelFitValue(headerAndValueList, Qt::blue);
+            }
+            if(sedPath.endsWith("sedfit_output.dat")){
+                loadSedFitOutput(sedPath);
+            }
+            if(sedPath.endsWith("thinfile.csv")){
+                //qDebug()<<"sedGraphs.size() before : "<<sedGraphs.size();
+                //qDebug()<<"Drawing "+sedPath;
+                readThinFit(sedPath);
+                addNewSEDCheckBox("Thin Fit");
+                //qDebug()<<"sedGraphs.size() after : "<<sedGraphs.size();
+            }
+            if(sedPath.endsWith("thickfile.csv")){
+                //qDebug()<<"sedGraphs.size() before : "<<sedGraphs.size();
+                //qDebug()<<"Drawing "+sedPath;
+                readThinFit(sedPath);
+                addNewSEDCheckBox("Thick Fit");
+                //qDebug()<<"sedGraphs.size() after : "<<sedGraphs.size();
+            }
+        }
+    }
+
+}
+
+void SEDVisualizerPlot::loadSavedSED(QStringList dirList){
+    for(int i=0; i<dirList.size(); i++){
+        QString sedPath=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/"+dirList.at(i);
+        QString sedNumber = dirList.at(i).mid(3,1); // substring after "SED";
+        //qDebug()<<"SED Number "<<sedNumber;
+        bool ok;
+        int value = sedNumber.toInt(&ok);
+        if (ok) { // is an integer
+            if(sedPath.endsWith("sedfit_model.dat")){
+                //qDebug()<<"sedGraphs.size() before : "<<sedGraphs.size();
+                //qDebug()<<"sedGraphPoints.size() before : "<<sedGraphPoints.size();
+                //qDebug()<<"Drawing "+sedPath;
+                QString sedOutput=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/SED"+QString::number(value)+"_sedfit_output.dat";
+                loadSedFitOutput(sedOutput);
+                //addNewSEDCheckBox("Theoretical Fit");
+                QFile file(sedPath);
+                file.open(QIODevice::ReadOnly);
+                QDataStream in(&file);    // read the data serialized from the file
+                QVector<QStringList> headerAndValueList;
+                in >> headerAndValueList;           // extract data
+                setModelFitValue(headerAndValueList, Qt::blue);
+            }
+            /*if(sedPath.endsWith("sedfit_output.dat")){
+                loadSedFitOutput(sedPath);
+            }*/
+            if(sedPath.endsWith("thinfile.csv")){
+                //qDebug()<<"sedGraphs.size() before : "<<sedGraphs.size();
+                //qDebug()<<"Drawing "+sedPath;
+                readThinFit(sedPath);
+                addNewSEDCheckBox("Thin Fit");
+                //qDebug()<<"sedGraphs.size() after : "<<sedGraphs.size();
+            }
+            if(sedPath.endsWith("thickfile.csv")){
+                //qDebug()<<"sedGraphs.size() before : "<<sedGraphs.size();
+                //qDebug()<<"Drawing "+sedPath;
+                readThinFit(sedPath);
+                addNewSEDCheckBox("Thick Fit");
+                //qDebug()<<"sedGraphs.size() after : "<<sedGraphs.size();
+            }
+        }
+    }
+}
+
+void SEDVisualizerPlot::on_collapseCheckBox_toggled(bool checked)
+{
+    if(checked){
+        this->on_actionCollapse_triggered();
+    }else{
+        //qDebug()<<"Return previous state";
+        QPen pen;
+        pen.setStyle(Qt::SolidLine);
+        pen.setColor(Qt::black);
+        for(int i=0;i<originalGraphs.size();i++)
+        {
+            originalGraphs.at(i)->setPen(pen);
+        }
+        //originalGraphs.clear();
+        ui->customPlot->removeGraph(collapsedGraph);
+        for (int i = 0; i < collapsedGraphPoints.size(); ++i)
+        {
+            SEDPlotPointCustom *cp;
+            cp = collapsedGraphPoints.at(i);
+            ui->customPlot->removeItem(cp);
+        }
+        collapsedGraphPoints.clear();
+        ui->customPlot->replot();
+    }
+}
+
+void SEDVisualizerPlot::on_multiSelectCheckBox_toggled(bool checked)
+{
+    if(checked==true){
+        multiSelectMOD=true;
+        ui->customPlot->setMultiSelectModifier(Qt::NoModifier);
+    }else{
+        multiSelectMOD=false;
+        ui->customPlot->setMultiSelectModifier(Qt::ControlModifier);
+        QList<QCPAbstractItem *> list_items=ui->customPlot->selectedItems();
+        for(int i=0;i<list_items.size();i++){
+            list_items.at(i)->setSelected(false);
+        }
+        ui->customPlot->replot();
+    }
+    //qDebug()<<"multiSelectMOD: "<<multiSelectMOD;
+}
+
+void SEDVisualizerPlot::addNewTheoreticalFit(){
+    QModelIndex index= ui->resultsTableWidget->selectionModel()->selectedIndexes().first();
+    //qDebug()<<"Clicked Menu ";
+    doubleClicked=true;
+    temporaryMOD=false;
+    //qDebug()<<"Double Cliked Row "+QString::number(index.row());
+    int id=ui->resultsTableWidget->item(index.row(),0)->text().toInt();
+    //qDebug()<<"Selected id "+QString::number(id);
+    if(index.row()==0){
+        // Do nothing on the first row which is already drawn by default
+    }
+    else{
+
+        QString query="SELECT * FROM vlkb_compactsources.sed_models where id="+ QString::number(id);
+        //qDebug()<<"query: " << query;
+        new VLKBQuery(query,vtkwin, "model", this, Qt::cyan);
+        if(temporaryRow!=0){
+            ui->customPlot->removeGraph(temporaryGraph);
+            for (int i = 0; i < temporaryGraphPoints.size(); ++i)
+            {
+                SEDPlotPointCustom *cp;
+                cp = temporaryGraphPoints.at(i);
+                ui->customPlot->removeItem(cp);
+            }
+            temporaryGraphPoints.clear();
+            temporaryRow=0;
+        }
+    }
+    this->setFocus();
+    this->activateWindow();
+}
+
+
+void SEDVisualizerPlot::on_resultsTableWidget_clicked(const QModelIndex &index)
+{
+    if(!doubleClicked){
+        temporaryMOD=true;
+        //qDebug()<<"Cliked Row "+QString::number(index.row());
+        int id=ui->resultsTableWidget->item(index.row(),0)->text().toInt();
+        //qDebug()<<"Selected id "+QString::number(id);
+        if(index.row()==0){
+            // Do nothing on the first row which is already drawn by default
+            // if a temporary graph was shown delete it
+            //qDebug()<<temporaryGraphPoints.size();
+            if(temporaryGraphPoints.size()!=0){
+                ui->customPlot->removeGraph(temporaryGraph);
+                for (int i = 0; i < temporaryGraphPoints.size(); ++i)
+                {
+                    SEDPlotPointCustom *cp;
+                    cp = temporaryGraphPoints.at(i);
+                    ui->customPlot->removeItem(cp);
+                }
+                temporaryGraphPoints.clear();
+                temporaryRow=0;
+                ui->customPlot->replot();
+            }
+        }
+        else if(index.row()!=temporaryRow){
+            if(temporaryRow!=0){
+                ui->customPlot->removeGraph(temporaryGraph);
+                for (int i = 0; i < temporaryGraphPoints.size(); ++i)
+                {
+                    SEDPlotPointCustom *cp;
+                    cp = temporaryGraphPoints.at(i);
+                    ui->customPlot->removeItem(cp);
+                }
+                temporaryGraphPoints.clear();
+            }
+
+            QString query="SELECT * FROM vlkb_compactsources.sed_models where id="+ QString::number(id);
+            //qDebug()<<"query: " << query;
+            new VLKBQuery(query,vtkwin, "model", this, Qt::lightGray);
+            temporaryRow=index.row();
+        }
+        doubleClicked=false;
+    }
+    this->setFocus();
+    this->activateWindow();
+}
+
+void SEDVisualizerPlot::on_theoreticalPushButton_clicked()
+{
+    ui->theorGroupBox->show();
+    ui->greyBodyGroupBox->hide();
+}
+
+void SEDVisualizerPlot::on_theorConfirmPushButton_clicked()
+{
+    /*
+    if(false)
+        QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr("Could not execute SED fit with less than 2 points selected.\n\rPlease select more points and try again."));
+*/
+
+    on_TheoreticalRemoteFit_triggered();
+}
+
+void SEDVisualizerPlot::on_greyBodyPushButton_clicked()
+{
+    ui->theorGroupBox->hide();
+    ui->greyBodyGroupBox->show();
+
+}
+
+void SEDVisualizerPlot::on_ThinRemore_triggered()
+{
+
+
+    sedFitInputF= "]";
+    sedFitInputErrF= "]";
+    sedFitInputW= "]";
+
+    sedFitInput.clear();
+    /*  if(multiSelectMOD){
+        prepareSelectedInputForSedFit();
+    }else{
+        prepareInputForSedFit(sed_list.at(0)->getRootNode());
+    }
+    */
+    bool validFit=false;
+
+    validFit=prepareSelectedInputForSedFit();
+
+    if(validFit)
+    {
+
+
+        sedFitInputF ="["+sedFitInputF ;
+        sedFitInputErrF ="["+sedFitInputErrF ;
+        sedFitInputW ="["+sedFitInputW;
+
+
+        QString f=sedFitInputW.mid(1,sedFitInputW.length()-2);
+
+        QStringList wave = f.split(",");
+
+        QSignalMapper* mapper = new QSignalMapper(this);
+
+        sd_thin = new SedFitGrid_thin(this);
+        sd_thin->ui->distLineEdit->setText(ui->distTheoLineEdit->text());
+        sedFitInputUlimitString="[";
+
+        for(int i=0;i<wave.length();i++)
+        {
+            //qDebug()<<wave.at(i);
+            sedFitInputUlimit.insert(i,"0");
+
+            sedFitInputUlimitString+="0";
+            if(i<wave.size()-1)
+                sedFitInputUlimitString+=",";
+
+            int row= sd_thin->ui->tableWidget->model()->rowCount();
+            sd_thin->ui->tableWidget->insertRow(row);
+
+            QCheckBox  *cb1= new QCheckBox();
+            cb1->setChecked(false);
+            sd_thin->ui->tableWidget->setCellWidget(row,1,cb1);
+
+            connect(cb1, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+            mapper->setMapping(cb1, row);
+
+            QTableWidgetItem *item_1 = new QTableWidgetItem();
+            item_1->setText(wave.at(i));
+
+            sd_thin->ui->tableWidget->setItem(row,0,item_1);
+
+        }
+        sedFitInputUlimitString+="]";
+        connect(mapper, SIGNAL(mapped(int)), this, SLOT(checkboxClicked(int)));
+
+        //qDebug()<<sedFitInputUlimitString;
+
+
+
+        sd_thin->show();
+
+        connect( sd_thin->ui->pushButton ,SIGNAL(clicked()),this,SLOT(doThinRemoteFit()));
+    }
+}
+
+void SEDVisualizerPlot::on_ThickRemote_triggered()
+{
+    sedFitInputF= "]";
+    sedFitInputErrF= "]";
+    sedFitInputW= "]";
+
+    sedFitInput.clear();
+    /*
+    if(multiSelectMOD){
+        prepareSelectedInputForSedFit();
+    }else{
+        prepareInputForSedFit(sed_list.at(0)->getRootNode());
+    }
+*/
+    bool validFit=true;
+
+    validFit=prepareSelectedInputForSedFit();
+
+    if(validFit)
+    {
+
+
+        sedFitInputF ="["+sedFitInputF ;
+        sedFitInputErrF ="["+sedFitInputErrF ;
+        sedFitInputW ="["+sedFitInputW;
+
+        //qDebug() << "f: " <<sedFitInputF;
+        //qDebug() << "e_f: " <<sedFitInputErrF;
+        //qDebug() << "w: " <<sedFitInputW;
+
+
+        QString f=sedFitInputW.mid(1,sedFitInputW.length()-2);
+
+        QStringList wave = f.split(",");
+
+        QSignalMapper* mapper = new QSignalMapper(this);
+
+        sd_thick = new SedFitgrid_thick(this);
+        sd_thick->ui->distLineEdit->setText(ui->distTheoLineEdit->text());
+
+        sedFitInputUlimitString="[";
+
+        for(int i=0;i<wave.length();i++)
+        {
+            //qDebug()<<wave.at(i);
+            sedFitInputUlimit.insert(i,"0");
+
+            sedFitInputUlimitString+="0";
+            if(i<wave.size()-1)
+                sedFitInputUlimitString+=",";
+
+            int row= sd_thick->ui->tableWidget->model()->rowCount();
+            sd_thick->ui->tableWidget->insertRow(row);
+
+            QCheckBox  *cb1= new QCheckBox();
+            cb1->setChecked(false);
+            sd_thick->ui->tableWidget->setCellWidget(row,1,cb1);
+
+            connect(cb1, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+            mapper->setMapping(cb1, row);
+
+            QTableWidgetItem *item_1 = new QTableWidgetItem();
+            item_1->setText(wave.at(i));
+
+            sd_thick->ui->tableWidget->setItem(row,0,item_1);
+
+        }
+        sedFitInputUlimitString+="]";
+        connect(mapper, SIGNAL(mapped(int)), this, SLOT(checkboxClicked(int)));
+
+        sd_thick->show();
+        connect( sd_thick->ui->pushButton ,SIGNAL(clicked()),this,SLOT(doThickRemoteFit()));
+    }
+
+}
+
+
+void SEDVisualizerPlot::on_Thick_clicked()
+{
+    on_ThickRemote_triggered();
+}
+
+void SEDVisualizerPlot::on_thinButton_clicked()
+{
+    on_ThinRemore_triggered();
+}
diff --git a/Code/src/sedvisualizerplot.h b/Code/src/sedvisualizerplot.h
new file mode 100644
index 0000000000000000000000000000000000000000..31e62156c14ea5867b621af2b232253b12debc5c
--- /dev/null
+++ b/Code/src/sedvisualizerplot.h
@@ -0,0 +1,162 @@
+#ifndef SEDVISUALIZERPLOT_H
+#define SEDVISUALIZERPLOT_H
+
+#include "sed.h"
+#include <QMainWindow>
+#include "qcustomplot.h"
+#include "sednode.h"
+#include "sedplotpointcustom.h"
+#include "vtkwindow_new.h"
+#include "sedfitgrid_thin.h"
+#include "sedfitgrid_thick.h"
+
+namespace Ui {
+class SEDVisualizerPlot;
+}
+
+class SedFitGrid_thin;
+
+class SedFitgrid_thick;
+
+class SEDVisualizerPlot : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit SEDVisualizerPlot(QList<SED *> s, vtkwindow_new *v,QWidget *parent = 0);
+    ~SEDVisualizerPlot();
+    void setModelFitValue(QVector<QStringList> headerAndValueList, Qt::GlobalColor color);
+    void loadSavedSED(QStringList dirList);
+    void setDistances(double dist);
+    void setTitle(QString t);
+private:
+    Ui::SEDVisualizerPlot *ui;
+    SED *sed;
+    QHash <QString, SEDPlotPointCustom*> visualnode_hash;
+    double minFlux,maxFlux, minWavelen, maxWavelen;
+    vtkwindow_new *vtkwin;
+    int sedCount;
+    QList<SED *> sed_list;
+    QList<SEDNode *> selected_sed_list;
+    bool prepareInputForSedFit( SEDNode *node);
+    bool prepareSelectedInputForSedFit();
+    QMap<double,double> sedFitInput;
+    QMap<int,QVector<double> > sedFitValues;
+    QString sedFitInputF;
+    QString sedFitInputErrF;
+    QString sedFitInputW;
+    QString sedFitInputFflag;
+    QString sedFitInputUlimitString;
+    double dist;
+    QString idlPath;
+    QString outputSedLog;
+    QString outputSedResults;
+
+    QStringList plottedSedLabels;
+
+    void readSedFitResultsHeader(QString header);
+    void readSedFitOutput(QString filename);
+    void loadSedFitOutput(QString filename);
+    void loadSedFitThin(QString filename);
+    void loadSedFitThick(QString filename);
+    void readThinFit(QString resultPath);
+    void readThickFit(QString resultPath);
+
+    QMap <QString, double> modelFitBands;
+
+    QStringList columnNames;
+    QMap <QString, int> resultsOutputColumn;
+    QMap <double, double> collapsed_flux;
+    QMultiMap <double ,SEDNode*> all_sed_node;
+    SedFitGrid_thin *sd_thin;
+    SedFitgrid_thick *sd_thick;
+    QVector<QString > sedFitInputUlimit;
+    QFutureWatcher<void> watcher;
+    LoadingWidget *loading;
+    int nSED;
+    void addNewSEDCheckBox(QString label, int newSED);
+    void addNewSEDCheckBox(QString label);
+    QList<QCPGraph *> sedGraphs;
+    QList<QCPGraph *> originalGraphs;
+    QCPGraph* collapsedGraph;
+    QList<SEDPlotPointCustom *> collapsedGraphPoints;
+    QMap <int, QList<SEDPlotPointCustom *> > sedGraphPoints;
+    bool multiSelectMOD;
+    bool temporaryMOD;
+    int temporaryRow;
+    QCPGraph *temporaryGraph;
+    QList<SEDPlotPointCustom *> temporaryGraphPoints;
+    bool doubleClicked;
+
+    QProcess *process;
+
+private slots:
+    void titleDoubleClick(QMouseEvent* event, QCPPlotTitle* title);
+    void axisLabelDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part);
+    void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item);
+    void selectionChanged();
+    void mousePress();
+    void mouseWheel();
+    void mouseRelease();
+    void checkboxClicked(int cb);
+
+    void removeSelectedGraph();
+    void removeAllGraphs();
+    void contextMenuRequest(QPoint pos);
+    void graphClicked(QCPAbstractPlottable *plottable);
+    void drawNode( SEDNode *node);
+    void doThinLocalFit();
+    void doThickLocalFit();
+
+    void doThinRemoteFit();
+    void doThickRemoteFit();
+
+
+    void on_actionEdit_triggered();
+    void on_actionFit_triggered();
+    void on_actionLocal_triggered();
+    void on_actionScreenshot_triggered();
+    void on_actionCollapse_triggered();
+    void on_TheoreticalLocaleFit_triggered();
+    void on_ThinLocalFit_triggered();
+    void on_ThickLocalFit_triggered();
+    void on_TheoreticalRemoteFit_triggered();
+
+    void on_logRadioButton_toggled(bool checked);
+
+    static bool checkIDRemoteCall(QString id);
+    static void executeRemoteCall(QString sedFitInputW, QString sedFitInputF,QString sedFitInputErrF, QString sedFitInputFflag, QMap<double,double> sedFitInput);
+    void handleFinished();
+    void on_clearAllButton_clicked();
+    void on_SEDCheckboxClicked(int n);
+    void on_normalRadioButton_toggled(bool checked);
+    void on_actionSave_triggered();
+    void on_actionLoad_triggered();
+    void on_collapseCheckBox_toggled(bool checked);
+    void on_multiSelectCheckBox_toggled(bool checked);
+    void on_resultsTableWidget_clicked(const QModelIndex &index);
+    void addNewTheoreticalFit();
+    void sectionClicked(int index);
+    void finishedTheoreticalLocaleFit();
+    void finishedTheoreticalRemoteFit();
+    void finishedThickLocalFit();
+    void finishedThickRemoteFit();
+
+    void onReadyReadStdOutput();
+    void finishedThinLocalFit();
+    void finishedThinRemoteFit();
+
+    void on_theoreticalPushButton_clicked();
+    void on_theorConfirmPushButton_clicked();
+    void on_greyBodyPushButton_clicked();
+    void on_ThinRemore_triggered();
+    void on_ThickRemote_triggered();
+    void on_Thick_clicked();
+    void on_thinButton_clicked();
+};
+
+QDataStream &operator<<(QDataStream &out, QList<SED *> &sedlist);
+QDataStream &operator>>(QDataStream &in, QList<SED *> &sedlist);
+
+
+#endif // SEDVISUALIZERPLOT_H
diff --git a/Code/src/selectedsourcefieldsselect.cpp b/Code/src/selectedsourcefieldsselect.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c20390643825ea8dab06fea61514cb650e88b960
--- /dev/null
+++ b/Code/src/selectedsourcefieldsselect.cpp
@@ -0,0 +1,98 @@
+#include "selectedsourcefieldsselect.h"
+#include "ui_selectedsourcefieldsselect.h"
+#include "qsignalmapper.h"
+#include <QDebug>
+#include <QCheckBox>
+
+#include "viewselectedsourcedataset.h"
+
+selectedSourceFieldsSelect::selectedSourceFieldsSelect(vtkwindow_new *v, QList<QListWidgetItem *> sel, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::selectedSourceFieldsSelect)
+{
+    ui->setupUi(this);
+    vtkwin=v;
+
+    selectedSources=sel;
+
+
+    //Solo per la prima?
+    table=vtkwin->getEllipseList().value(selectedSources.at(0)->text())->getTable();
+
+    QHeaderView* header = ui->fieldListTableWidget->horizontalHeader();
+    header->setVisible(true);
+    header->sectionResizeMode(QHeaderView::Stretch);
+
+
+    int row;
+    QSignalMapper* mapper = new QSignalMapper(this);
+
+    for (int i=0;i<table->getNumberOfColumns();i++)
+    {
+
+
+        row= ui->fieldListTableWidget->model()->rowCount();
+
+
+        ui->fieldListTableWidget->insertRow(row);
+        QCheckBox  *cb1= new QCheckBox();
+        cb1->setChecked(true);
+        ui->fieldListTableWidget->setCellWidget(row,0,cb1);
+        connect(cb1, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+        mapper->setMapping(cb1, row);
+
+        checkboxList.append(cb1);
+
+        selectedFields.insert(i,QString::fromStdString(table->getColName(i)));
+
+        QTableWidgetItem *item_1 = new QTableWidgetItem();
+        item_1->setText(QString::fromStdString(table->getColName(i)));
+        ui->fieldListTableWidget->setItem(row,1,item_1);
+
+        connect(mapper, SIGNAL(mapped(int)), this, SLOT(checkboxClicked(int)));
+    }
+
+
+}
+
+void selectedSourceFieldsSelect::checkboxClicked(int cb)
+{
+    if(checkboxList.at(cb)->isChecked())
+        selectedFields.insert(cb,QString::fromStdString(table->getColName(cb)));
+    else
+        selectedFields.remove(cb);
+}
+
+selectedSourceFieldsSelect::~selectedSourceFieldsSelect()
+{
+    delete ui;
+}
+
+void selectedSourceFieldsSelect::on_selectAllButton_clicked()
+{
+    for (int i=0;i<checkboxList.size();i++)
+    {
+        checkboxList.at(i)->setChecked(true);
+        selectedFields.insert(i,QString::fromStdString(table->getColName(i)));
+
+    }
+}
+
+void selectedSourceFieldsSelect::on_deselectAllButton_clicked()
+{
+
+    for (int i=0;i<checkboxList.size();i++)
+    {
+        checkboxList.at(i)->setChecked(false);
+        selectedFields.remove(i);
+    }
+}
+
+void selectedSourceFieldsSelect::on_okButton_clicked()
+{
+
+    ViewSelectedSourceDataset *selDataset =new ViewSelectedSourceDataset(vtkwin,selectedFields,selectedSources);
+    this->close();
+
+    selDataset->show();
+}
diff --git a/Code/src/selectedsourcefieldsselect.h b/Code/src/selectedsourcefieldsselect.h
new file mode 100644
index 0000000000000000000000000000000000000000..35b4b7a32d7b48cafc208099b2d8f9ebf9915b2d
--- /dev/null
+++ b/Code/src/selectedsourcefieldsselect.h
@@ -0,0 +1,40 @@
+#ifndef SELECTEDSOURCEFIELDSSELECT_H
+#define SELECTEDSOURCEFIELDSSELECT_H
+
+#include <QWidget>
+#include "vstabledesktop.h"
+#include <QCheckBox>
+#include <QHash>
+#include <QListWidgetItem>
+#include "vtkwindow_new.h"
+
+namespace Ui {
+class selectedSourceFieldsSelect;
+}
+
+class selectedSourceFieldsSelect : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit selectedSourceFieldsSelect(vtkwindow_new *v, QList<QListWidgetItem*> sel, QWidget *parent = 0);
+    ~selectedSourceFieldsSelect();
+
+private slots:
+    void checkboxClicked(int cb);
+    void on_selectAllButton_clicked();
+
+    void on_deselectAllButton_clicked();
+
+    void on_okButton_clicked();
+
+private:
+    Ui::selectedSourceFieldsSelect *ui;
+    VSTableDesktop *table;
+    QVector<QCheckBox *> checkboxList;
+    QHash <int,QString> selectedFields;
+     QList<QListWidgetItem*> selectedSources;
+    vtkwindow_new *vtkwin;
+};
+
+#endif // SELECTEDSOURCEFIELDSSECECT_H
diff --git a/Code/src/selectedsourcesform.cpp b/Code/src/selectedsourcesform.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d294c3606a338b0d026c5ffcbf286b439b9ffd2
--- /dev/null
+++ b/Code/src/selectedsourcesform.cpp
@@ -0,0 +1,14 @@
+#include "selectedsourcesform.h"
+#include "ui_selectedsourcesform.h"
+
+SelectedSourcesForm::SelectedSourcesForm(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::SelectedSourcesForm)
+{
+    ui->setupUi(this);
+}
+
+SelectedSourcesForm::~SelectedSourcesForm()
+{
+    delete ui;
+}
diff --git a/Code/src/selectedsourcesform.h b/Code/src/selectedsourcesform.h
new file mode 100644
index 0000000000000000000000000000000000000000..4d23f12b8a6073e119012623ee64e4e48e233a11
--- /dev/null
+++ b/Code/src/selectedsourcesform.h
@@ -0,0 +1,22 @@
+#ifndef SELECTEDSOURCESFORM_H
+#define SELECTEDSOURCESFORM_H
+
+#include <QWidget>
+
+namespace Ui {
+class SelectedSourcesForm;
+}
+
+class SelectedSourcesForm : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit SelectedSourcesForm(QWidget *parent = 0);
+    ~SelectedSourcesForm();
+
+private:
+    Ui::SelectedSourcesForm *ui;
+};
+
+#endif // SELECTEDSOURCESFORM_H
diff --git a/Code/src/settingform.cpp b/Code/src/settingform.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2d91423ca9c3e404d806b77dc8bc7378345cddf
--- /dev/null
+++ b/Code/src/settingform.cpp
@@ -0,0 +1,166 @@
+#include "settingform.h"
+#include "ui_settingform.h"
+#include <QSettings>
+#include <QApplication>
+#include <QDebug>
+#include <QFileDialog>
+#include "vialactea.h"
+#include "singleton.h"
+
+SettingForm::SettingForm(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::SettingForm)
+{
+    ui->setupUi(this);
+
+    this->setWindowFlags(Qt::WindowStaysOnTopHint);
+
+    m_sSettingsFile = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini");
+
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+    QString tilePath = settings.value("tilepath", "").toString();
+    ui->TileLineEdit->setText(tilePath);
+
+    QString idlPath = settings.value("idlpath", "").toString();
+    ui->IdlLineEdit->setText(idlPath);
+
+    QString glyphmax = settings.value("glyphmax", "").toString();
+    ui->glyphLineEdit->setText(glyphmax);
+    ui->groupBox_4->hide();
+
+
+    if(settings.value("vlkbtype", "public")=="public")
+    {
+        ui->publicVLKB_radioButton->setChecked(true);
+
+        ui->username_LineEdit->hide();
+        ui->password_LineEdit->hide();
+        ui->userLabel->hide();
+        ui->passLabel->hide();
+    }
+    else if(settings.value("vlkbtype", "public")=="private")
+    {
+        ui->privateVLKB_radioButton->setChecked(true);
+        ui->username_LineEdit->show();
+        ui->password_LineEdit->show();
+        ui->userLabel->show();
+        ui->passLabel->show();
+
+        ui->username_LineEdit->setText(settings.value("vlkbuser", "").toString());
+        ui->password_LineEdit->setText(settings.value("vlkbpass", "").toString());
+    }
+
+    if (settings.value("online",false) == true)
+    {
+        ui->checkBox->setChecked(true);
+
+    }
+
+
+    ui->urlLineEdit->setText(settings.value("onlinetilepath", "http://visivo.oact.inaf.it/vialacteatiles/openlayers.html").toString());
+
+
+}
+
+SettingForm::~SettingForm()
+{
+    delete ui;
+}
+
+void SettingForm::on_IdlPushButton_clicked()
+{
+    QString fn = QFileDialog::getOpenFileName(this, "IDL bin", QString(), "idl");
+
+    if (!fn.isEmpty() )
+    {
+        ui->IdlLineEdit->setText(fn);
+    }
+}
+
+void SettingForm::on_TilePushButton_clicked()
+{
+    QString fn = QFileDialog::getOpenFileName(this, "Html file", QString(), "openlayers.html");
+
+    if (!fn.isEmpty() )
+    {
+        ui->TileLineEdit->setText(fn);
+    }
+}
+
+void SettingForm::on_OkPushButton_clicked()
+{
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+
+    settings.setValue("tilepath",  ui->TileLineEdit->text());
+    settings.setValue("idlpath", ui->IdlLineEdit->text());
+    settings.setValue("glyphmax", ui->glyphLineEdit->text());
+    if(ui->privateVLKB_radioButton->isChecked())
+        settings.setValue("vlkbtype", "private");
+    else
+        settings.setValue("vlkbtype", "public");
+
+
+    settings.setValue("vlkbuser", ui->username_LineEdit->text());
+    settings.setValue("vlkbpass", ui->password_LineEdit->text());
+  //  settings.setValue("workdir",  ui->lineEdit_2->text());
+
+    settings.setValue("online", ui->checkBox->isChecked());
+    settings.setValue("onlinetilepath", ui->urlLineEdit->text());
+
+
+    this->close();
+    ViaLactea *vialactealWin = &Singleton<ViaLactea>::Instance();
+    vialactealWin->reload();
+}
+
+void SettingForm::on_pushButton_clicked()
+{
+    this->close();
+}
+
+void SettingForm::on_checkBox_clicked(bool checked)
+{
+    qDebug()<<"checked: "<<checked;
+}
+
+void SettingForm::on_privateVLKB_radioButton_toggled(bool checked)
+{
+    if (checked)
+    {
+
+        ui->username_LineEdit->show();
+        ui->password_LineEdit->show();
+        ui->userLabel->show();
+        ui->passLabel->show();
+
+
+    }
+    else
+    {
+
+        ui->username_LineEdit->hide();
+        ui->password_LineEdit->hide();
+        ui->userLabel->hide();
+        ui->passLabel->hide();
+    }
+}
+
+void SettingForm::on_workdirButton_clicked()
+{
+    QString fn =QFileDialog::getExistingDirectory(this, tr("Open Directory"),
+                                                  "~",
+                                                  QFileDialog::ShowDirsOnly
+                                                  | QFileDialog::DontResolveSymlinks);
+
+    //QFileDialog::getOpenFileName(this, "Html file", QString(), "openlayers.html");
+
+    if (!fn.isEmpty() )
+    {
+        ui->lineEdit_2->setText(fn);
+    }
+}
+
+void SettingForm::on_checkBox_clicked()
+{
+
+}
diff --git a/Code/src/settingform.h b/Code/src/settingform.h
new file mode 100644
index 0000000000000000000000000000000000000000..32b3b17f47f9570039bb7d28a8ae2cdbc0a1e9e7
--- /dev/null
+++ b/Code/src/settingform.h
@@ -0,0 +1,41 @@
+#ifndef SETTINGFORM_H
+#define SETTINGFORM_H
+
+#include <QWidget>
+
+namespace Ui {
+class SettingForm;
+}
+
+class SettingForm : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit SettingForm(QWidget *parent = 0);
+
+    ~SettingForm();
+
+private slots:
+    void on_IdlPushButton_clicked();
+
+    void on_TilePushButton_clicked();
+
+
+    void on_pushButton_clicked();
+
+    void on_checkBox_clicked(bool checked);
+
+    void on_privateVLKB_radioButton_toggled(bool checked);
+    void on_workdirButton_clicked();
+
+    void on_checkBox_clicked();
+    void on_OkPushButton_clicked();
+
+
+private:
+    Ui::SettingForm *ui;
+    QString m_sSettingsFile;
+};
+
+#endif // SETTINGFORM_H
diff --git a/Code/src/singleton.h b/Code/src/singleton.h
new file mode 100644
index 0000000000000000000000000000000000000000..00fc46512e0e47655e2e5484fe07210ba9a1f0d7
--- /dev/null
+++ b/Code/src/singleton.h
@@ -0,0 +1,22 @@
+#ifndef SINGLETON_H
+#define SINGLETON_H
+
+#include <QObject>
+
+template <class T>
+class Singleton
+{
+public:
+    static T& Instance()
+    {
+        static T _instance; // create static instance of our class
+        return _instance;   // return it
+    }
+
+private:
+    Singleton();	// hide constructor
+    ~Singleton();	// hide destructor
+    Singleton(const Singleton &); // hide copy constructor
+    Singleton& operator=(const Singleton &); // hide assign op
+};
+#endif
diff --git a/Code/src/treeitem.cpp b/Code/src/treeitem.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bd340abc3fafa7d83f41a6d4750200514bdb178e
--- /dev/null
+++ b/Code/src/treeitem.cpp
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial Usage
+** Licensees holding valid Qt Commercial licenses may use this file in
+** accordance with the Qt Commercial License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Nokia.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*
+    treeitem.cpp
+
+    A container for items of data supplied by the simple tree model.
+*/
+
+#include <QStringList>
+
+#include "treeitem.h"
+#include "vispoint.h"
+
+TreeItem::TreeItem(const QVector<QVariant> &data, TreeItem *parent)
+{
+    m_parentItem = parent;
+    itemData = data;
+}
+
+TreeItem::~TreeItem()
+{
+    qDeleteAll(childItems);
+}
+
+TreeItem *TreeItem::child(int number)
+{
+    return childItems.value(number);
+}
+
+int TreeItem::childCount() const
+{
+    return childItems.count();
+}
+
+int TreeItem::childNumber() const
+{
+    if (m_parentItem)
+        return m_parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
+
+    return 0;
+}
+
+int TreeItem::columnCount() const
+{
+    return itemData.count();
+}
+
+QVariant TreeItem::data(int column) const
+{
+    return itemData.value(column);
+}
+
+bool TreeItem::insertChildren(int position, int count, int columns)
+{
+    if (position < 0 || position > childItems.size())
+        return false;
+
+    for (int row = 0; row < count; ++row) {
+        QVector<QVariant> data(columns);
+        TreeItem *item = new TreeItem(data, this);
+        childItems.insert(position, item);
+    }
+
+    return true;
+}
+
+bool TreeItem::insertColumns(int position, int columns)
+{
+    if (position < 0 || position > itemData.size())
+        return false;
+
+    for (int column = 0; column < columns; ++column)
+        itemData.insert(position, QVariant());
+
+    foreach (TreeItem *child, childItems)
+        child->insertColumns(position, columns);
+
+    return true;
+}
+
+TreeItem *TreeItem::parent()
+{
+    return m_parentItem;
+}
+
+bool TreeItem::removeChildren(int position, int count)
+{
+    if (position < 0 || position + count > childItems.size())
+        return false;
+
+    for (int row = 0; row < count; ++row)
+        delete childItems.takeAt(position);
+
+    return true;
+}
+
+bool TreeItem::removeColumns(int position, int columns)
+{
+    if (position < 0 || position + columns > itemData.size())
+        return false;
+
+    for (int column = 0; column < columns; ++column)
+        itemData.remove(position);
+
+    foreach (TreeItem *child, childItems)
+        child->removeColumns(position, columns);
+
+    return true;
+}
+
+bool TreeItem::setData(int column, const QVariant &value)
+{
+    if (column < 0 || column >= itemData.size())
+        return false;
+
+    itemData[column] = value;
+    return true;
+}
+bool TreeItem::setTable(VSTableDesktop *table)
+{
+    if (table->tableExist()){
+        m_table = table;
+        if(table->getIsVolume())
+        {
+            m_Type=VolumeTable;
+        }
+        else
+        {
+            m_Type=PointTable;
+        }
+        return true;
+    }
+    else return false;
+}
+
+bool TreeItem::setVisualObject(VisPoint *vis)
+{
+    if (vis->isOriginSpecified()){
+        m_Type=VisualObject;
+        m_visualObject=vis;
+        return true;
+    }
+    else return false;
+}
+bool TreeItem::setVTI(vtkVolume *volume)
+{
+    m_volume=volume;
+    m_Type=VTI;
+    return true;
+}
+
+bool TreeItem::setVTP(vtkLODActor *pActor)
+{
+    m_pActor=pActor;
+    m_Type=VTP;
+    return true;
+}
+
+//bool TreeItem::setFITSIMG(vtkImageActor *pActor)
+bool TreeItem::setFITSIMG(vtkSmartPointer<vtkFitsReader> fitsReader)
+{
+
+    m_Type=FITSIMAGE;
+    m_fitsImg= fitsReader;
+    return true;
+}
+
+VSTableDesktop *TreeItem::getTable()
+{
+    return m_table;
+}
+
+VisPoint *TreeItem::getVisualObject()
+{
+    return m_visualObject;
+}
+
+
+//vtkImageActor *TreeItem::getFITSIMG()
+vtkSmartPointer<vtkFitsReader> TreeItem::getFITSIMG()
+{
+
+    return m_fitsImg;
+}
+
+vtkVolume *TreeItem::getVTI()
+{
+    return m_volume;
+}
+
+vtkLODActor *TreeItem::getVTP()
+{
+    return m_pActor;
+}
+
+TreeItem::TreeItemType TreeItem::getType()
+{
+    return m_Type;
+}
diff --git a/Code/src/treeitem.h b/Code/src/treeitem.h
new file mode 100644
index 0000000000000000000000000000000000000000..cea8e620a89d91206e31d451e2e0c35405198f15
--- /dev/null
+++ b/Code/src/treeitem.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ ** All rights reserved.
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
+ **
+ ** This file is part of the examples of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:LGPL$
+ ** Commercial Usage
+ ** Licensees holding valid Qt Commercial licenses may use this file in
+ ** accordance with the Qt Commercial License Agreement provided with the
+ ** Software or, alternatively, in accordance with the terms contained in
+ ** a written agreement between you and Nokia.
+ **
+ ** GNU Lesser General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
+ ** General Public License version 2.1 as published by the Free Software
+ ** Foundation and appearing in the file LICENSE.LGPL included in the
+ ** packaging of this file.  Please review the following information to
+ ** ensure the GNU Lesser General Public License version 2.1 requirements
+ ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ **
+ ** In addition, as a special exception, Nokia gives you certain additional
+ ** rights.  These rights are described in the Nokia Qt LGPL Exception
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+ **
+ ** GNU General Public License Usage
+ ** Alternatively, this file may be used under the terms of the GNU
+ ** General Public License version 3.0 as published by the Free Software
+ ** Foundation and appearing in the file LICENSE.GPL included in the
+ ** packaging of this file.  Please review the following information to
+ ** ensure the GNU General Public License version 3.0 requirements will be
+ ** met: http://www.gnu.org/copyleft/gpl.html.
+ **
+ ** If you have questions regarding the use of this file, please contact
+ ** Nokia at qt-info@nokia.com.
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+
+#ifndef TREEITEM_H
+#define TREEITEM_H
+
+#include <QList>
+#include <QVariant>
+#include <QVector>
+
+#include "vtkLODActor.h"
+#include "vtkVolume.h"
+#include "vstabledesktop.h"
+#include "vtkImageActor.h"
+#include "vtkImageViewer2.h"
+#include <vtkSmartPointer.h>
+#include "vtkfitsreader.h"
+
+class VisPoint;
+
+class TreeItem
+{
+public
+    :TreeItem(const QVector<QVariant> &data, TreeItem *parent = 0);
+    enum TreeItemType {Null,PointTable,VolumeTable,VTI,VTP,VisualObject,FITSIMAGE};
+
+    ~TreeItem();
+
+    TreeItem *child(int number);
+    int childCount() const;
+    int columnCount() const;
+    QVariant data(int column) const;
+    bool insertChildren(int position, int count, int columns);
+    bool insertColumns(int position, int columns);
+    TreeItem *parent();
+    bool removeChildren(int position, int count);
+    bool removeColumns(int position, int columns);
+    int childNumber() const;
+    bool setData(int column, const QVariant &value);
+
+    /*VisIVOItem specific method */
+    bool setTable(VSTableDesktop *table);
+    bool setVTP(vtkLODActor *pActor);
+ //   bool setFITSIMG(vtkImageActor *pActor);
+    bool setFITSIMG(vtkSmartPointer<vtkFitsReader> pActor);
+    bool setVTI(vtkVolume *volume);
+    bool setVisualObject(VisPoint* vis);
+    VSTableDesktop     *getTable();
+    vtkLODActor *getVTP();
+    vtkVolume   *getVTI();
+    //vtkImageActor *getFITSIMG();
+    vtkSmartPointer<vtkFitsReader> getFITSIMG();
+    VisPoint    *getVisualObject();
+    /*end VisIVOItem specific method */
+
+
+private:
+    QList<TreeItem*> childItems;
+    QVector<QVariant> itemData;
+    /*VisIVOItem specific*/
+    enum TreeItemType m_Type;
+    TreeItem    *m_parentItem;
+    VSTableDesktop     *m_table;
+    vtkLODActor *m_pActor;
+   // vtkImageActor *m_imgActor;
+    vtkSmartPointer<vtkFitsReader> m_fitsImg;
+    vtkVolume   *m_volume;
+    VisPoint    *m_visualObject;
+    /*end VisIVOItem specific*/
+public:
+    enum TreeItemType  getType();
+};
+
+#endif
diff --git a/Code/src/treemodel.cpp b/Code/src/treemodel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c7ed0dd048840aeef251e0f3aed602d46cd882ca
--- /dev/null
+++ b/Code/src/treemodel.cpp
@@ -0,0 +1,408 @@
+/*
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (qt-info@nokia.com)
+ *
+ * This file is part of the examples of the Qt Toolkit.
+ *
+ * $QT_BEGIN_LICENSE:LGPL$
+ * Modified by Alessandro Costa 2011
+ *
+ */
+
+#include <QtGui>
+
+#include "treeitem.h"
+#include "treemodel.h"
+#include "vispoint.h"
+
+
+TreeModel::TreeModel(const QStringList &headers, const QString &data,
+                     QObject *parent)
+    : QAbstractItemModel(parent)
+{
+    /**
+ * Constructor.
+ *
+ * The constructor creates a root item and initializes
+ * it with the header data supplied
+ *
+ * @param headers   headers for the object initialization
+ * @param data textual data being converted to a data structure
+ * we can use with the model
+ * @param parent
+ *
+ */
+    QVector<QVariant> rootData;
+    foreach (QString header, headers)
+        rootData << header;
+
+    rootItem = new TreeItem(rootData);
+    setupModelData(data.split(QString("\n")), rootItem);
+    m_activeViewIndexList = QModelIndexList();
+}
+
+TreeModel::~TreeModel()
+{
+    delete rootItem;
+}
+
+QModelIndex TreeModel::getLastInsertItem()
+{
+    return lastInsertItem;
+}
+
+void  TreeModel::setLastInsertItem(QModelIndex item)
+{
+    lastInsertItem=item;
+}
+
+int TreeModel::columnCount(const QModelIndex & /* parent */) const
+{
+    return rootItem->columnCount();
+}
+
+QVariant TreeModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid())
+        return QVariant();
+
+    if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::DecorationRole)
+        return QVariant();
+
+    TreeItem *item = getItem(index);
+
+    return item->data(index.column());
+}
+
+
+
+TreeItem *TreeModel::getItem(const QModelIndex &index) const
+{
+    if (index.isValid()) {
+        TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
+        if (item) return item;
+    }
+    return rootItem;
+}
+
+QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
+                               int role) const
+{
+    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+        return rootItem->data(section);
+
+    return QVariant();
+}
+
+QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
+{
+    if (parent.isValid() && parent.column() != 0)
+        return QModelIndex();
+
+    TreeItem *parentItem = getItem(parent);
+
+    TreeItem *childItem = parentItem->child(row);
+    if (childItem)
+        return createIndex(row, column, childItem);
+    else
+        return QModelIndex();
+}
+
+bool TreeModel::insertColumns(int position, int columns, const QModelIndex &parent)
+{
+    bool success;
+
+    beginInsertColumns(parent, position, position + columns - 1);
+    success = rootItem->insertColumns(position, columns);
+    endInsertColumns();
+
+    return success;
+}
+
+bool TreeModel::insertRows(int position, int rows, const QModelIndex &parent)
+{
+    TreeItem *parentItem = getItem(parent);
+    bool success;
+
+    beginInsertRows(parent, position, position + rows - 1);
+    success = parentItem->insertChildren(position, rows, rootItem->columnCount());
+    endInsertRows();
+
+
+
+    return success;
+}
+
+QModelIndex TreeModel::parent(const QModelIndex &index) const
+{
+    if (!index.isValid())
+        return QModelIndex();
+
+    TreeItem *childItem = getItem(index);
+    TreeItem *parentItem = childItem->parent();
+
+    if (parentItem == rootItem)
+        return QModelIndex();
+
+    return createIndex(parentItem->childNumber(), 0, parentItem);
+}
+
+bool TreeModel::removeColumns(int position, int columns, const QModelIndex &parent)
+{
+    bool success;
+
+    beginRemoveColumns(parent, position, position + columns - 1);
+    success = rootItem->removeColumns(position, columns);
+    endRemoveColumns();
+
+    if (rootItem->columnCount() == 0)
+        removeRows(0, rowCount());
+
+    return success;
+}
+
+bool TreeModel::removeRows(int position, int rows, const QModelIndex &parent)
+{
+    TreeItem *parentItem = getItem(parent);
+    bool success = true;
+
+    beginRemoveRows(parent, position, position + rows - 1);
+    success = parentItem->removeChildren(position, rows);
+    endRemoveRows();
+
+    return success;
+}
+
+int TreeModel::rowCount(const QModelIndex &parent) const
+{
+    TreeItem *parentItem = getItem(parent);
+
+    return parentItem->childCount();
+}
+
+bool TreeModel::setData(const QModelIndex &index, const QVariant &value,
+                        int role)
+{
+    if (role != Qt::EditRole)
+        return false;
+
+    TreeItem *item = getItem(index);
+    bool result = item->setData(index.column(), value);
+
+    if (result)
+        emit dataChanged(index, index);
+
+    return result;
+}
+
+bool TreeModel::setHeaderData(int section, Qt::Orientation orientation,
+                              const QVariant &value, int role)
+{
+    if (role != Qt::EditRole || orientation != Qt::Horizontal)
+        return false;
+
+    bool result = rootItem->setData(section, value);
+
+    if (result)
+        emit headerDataChanged(orientation, section, section);
+
+    return result;
+}
+
+void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
+{
+    QList<TreeItem*> parents;
+    QList<int> indentations;
+    parents << parent;
+    indentations << 0;
+
+    int number = 0;
+
+    while (number < lines.count()) {
+        int position = 0;
+        while (position < lines[number].length()) {
+            if (lines[number].mid(position, 1) != " ")
+                break;
+            position++;
+        }
+
+        QString lineData = lines[number].mid(position).trimmed();
+
+        if (!lineData.isEmpty()) {
+            // Read the column data from the rest of the line.
+            QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
+            QVector<QVariant> columnData;
+            for (int column = 0; column < columnStrings.count(); ++column)
+                columnData << columnStrings[column];
+
+            if (position > indentations.last()) {
+                // The last child of the current parent is now the new parent
+                // unless the current parent has no children.
+
+                if (parents.last()->childCount() > 0) {
+                    parents << parents.last()->child(parents.last()->childCount()-1);
+                    indentations << position;
+                }
+            } else {
+                while (position < indentations.last() && parents.count() > 0) {
+                    parents.pop_back();
+                    indentations.pop_back();
+                }
+            }
+
+            // Append a new item to the current parent's list of children.
+            TreeItem *parent = parents.last();
+            parent->insertChildren(parent->childCount(), 1, rootItem->columnCount());
+            for (int column = 0; column < columnData.size(); ++column)
+                parent->child(parent->childCount() - 1)->setData(column, columnData[column]);
+        }
+
+        number++;
+    }
+}
+//****************
+
+Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
+{
+    if (!index.isValid())
+        return 0;
+    /*!
+    Qt.ItemFlag
+    This enum describes the properties of an item:
+
+    Qt:ItemIsSelectable          	It can be selected.
+    Qt:ItemIsEditable            	It can be edited.
+    Qt:ItemIsDragEnabled         	It can be dragged.
+    Qt:ItemIsDropEnabled         	It can be used as a drop target.
+    Qt:ItemIsUserCheckable       	It can be checked or unchecked by the user.
+    Qt:ItemIsEnabled             	The user can interact with the item.
+    Qt:ItemIsTristate            	The item is checkable with three separate states.
+*/
+    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;// | Qt::ItemIsUserCheckable;
+}
+bool TreeModel::setTable(const QModelIndex &index, VSTableDesktop *table)
+{
+    TreeItem *item = getItem(index);
+    bool result = item->setTable(table);
+    if (result)
+        emit dataChanged(index, index);
+    return result;
+}
+
+bool TreeModel::setVisualObject(const QModelIndex &index, VisPoint *vis)
+{
+    TreeItem *item = getItem(index);
+    bool result =item->setVisualObject(vis);
+    if (result)
+        emit dataChanged(index, index);
+    return result;
+}
+
+bool TreeModel::setVTP(const QModelIndex &index, vtkLODActor *pActor)
+{
+    TreeItem *item = getItem(index);
+    bool result = item->setVTP(pActor);
+    if (result)
+        emit dataChanged(index, index);
+    return result;
+}
+
+//bool TreeModel::setFITSIMG(const QModelIndex &index, vtkSmartPointer<vtkImageActor> imgActor)
+bool TreeModel::setFITSIMG(const QModelIndex &index, vtkSmartPointer<vtkFitsReader> fitsReader)
+{
+    TreeItem *item = getItem(index);
+
+    bool result = item->setFITSIMG(fitsReader);
+
+    if (result)
+        emit dataChanged(index, index);
+    return result;
+}
+
+bool TreeModel::setVTI(const QModelIndex &index, vtkVolume *volume)
+{
+    TreeItem *item = getItem(index);
+    bool result = item->setVTI(volume);
+    if (result)
+        emit dataChanged(index, index);
+    return result;
+}
+
+VSTableDesktop *TreeModel::getTable(const QModelIndex &index)
+{
+    TreeItem *item = getItem(index);
+    if (item->getType() == TreeItem::VisualObject) return item->getVisualObject()->getOrigin();
+    return item->getTable();
+}
+
+VisPoint*TreeModel::getVisualObject(const QModelIndex &index)
+{
+    TreeItem *item = getItem(index);
+    return item->getVisualObject();
+}
+vtkLODActor *TreeModel::getVTP(const QModelIndex &index)
+{
+    TreeItem *item = getItem(index);
+    return item->getVTP();
+}
+
+//vtkImageActor *TreeModel::getFITSIMG(const QModelIndex &index)
+
+vtkSmartPointer<vtkFitsReader> TreeModel::getFITSIMG(const QModelIndex &index)
+{
+    TreeItem *item = getItem(index);
+    return item->getFITSIMG();
+}
+
+vtkVolume *TreeModel::getVTI(const QModelIndex &index)
+{
+    TreeItem *item = getItem(index);
+    return item->getVTI();
+}
+
+TreeItem::TreeItemType TreeModel::getType(const QModelIndex &index)
+{
+    TreeItem *item = getItem(index);
+    return item->getType();
+}
+QModelIndexList TreeModel::getActiveViewList()
+{
+    /**
+      * It returns a QModelIndexList containing
+      * the whole list of VisIVOObject with m_isSetOrigin == true
+      * this list is passed to the rendering window (VisIVORW) for
+      * the visualization
+      */
+
+    m_activeViewIndexList.clear();
+    int rows = this->rowCount();
+    for (int i=0;i<rows;i++)
+    {
+        activeViewListTraverse(this->index(i,0));
+    }
+    return m_activeViewIndexList;
+}
+void TreeModel::activeViewListTraverse(QModelIndex index)
+{
+    TreeItem *currentItem = this->getItem(index);
+    TreeItem::TreeItemType currentType = this->getItem(index)->getType();
+
+    if (currentType==TreeItem::VisualObject)
+    {
+        if(currentItem->getVisualObject()->isVisible()) // if the index item is a VisualObject;
+            m_activeViewIndexList.append(index);
+    }
+    int rows = this->getItem(index)->childCount();
+    if (rows > 0)
+    {
+        for (int i=0;i<rows;i++)
+        {
+            //TreeItem nextItem = this->getItem(index)->child(i);
+            QModelIndex  nextIndex = this->index(i,0,index);
+            activeViewListTraverse(nextIndex);
+        }
+    }
+}
+
diff --git a/Code/src/treemodel.h b/Code/src/treemodel.h
new file mode 100644
index 0000000000000000000000000000000000000000..669a466602d702b43514cb008a799f19c73fab39
--- /dev/null
+++ b/Code/src/treemodel.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (qt-info@nokia.com)
+ *
+ * This file is part of the examples of the Qt Toolkit.
+ *
+ * $QT_BEGIN_LICENSE:LGPL$
+ * Modified by Alessandro Costa 2011
+ *
+ */
+
+
+
+#ifndef TREEMODEL_H
+#define TREEMODEL_H
+
+#include <QStandardItemModel>
+#include <QModelIndex>
+#include <QVariant>
+#include "treeitem.h"
+#include <vtkSmartPointer.h>
+#include "vtkfitsreader.h"
+
+class  VisPoint;
+class TreeItem;
+
+class TreeModel : public QAbstractItemModel
+{
+    Q_OBJECT
+    QModelIndexList m_activeViewIndexList;
+public:
+    TreeModel(const QStringList &headers, const QString &data,
+              QObject *parent = 0);
+    ~TreeModel();
+
+    QVariant data(const QModelIndex &index, int role) const;
+    QVariant headerData(int section, Qt::Orientation orientation,
+                        int role = Qt::DisplayRole) const;
+
+    QModelIndex index(int row, int column,
+                      const QModelIndex &parent = QModelIndex()) const;
+    QModelIndex parent(const QModelIndex &index) const;
+
+    int rowCount(const QModelIndex &parent = QModelIndex()) const;
+    int columnCount(const QModelIndex &parent = QModelIndex()) const;
+
+    Qt::ItemFlags flags(const QModelIndex &index) const;
+    bool setData(const QModelIndex &index, const QVariant &value,
+                 int role = Qt::EditRole);
+    bool setHeaderData(int section, Qt::Orientation orientation,
+                       const QVariant &value, int role = Qt::EditRole);
+
+    bool insertColumns(int position, int columns,
+                       const QModelIndex &parent = QModelIndex());
+    bool removeColumns(int position, int columns,
+                       const QModelIndex &parent = QModelIndex());
+    bool insertRows(int position, int rows,
+                    const QModelIndex &parent = QModelIndex());
+    bool removeRows(int position, int rows,
+                    const QModelIndex &parent = QModelIndex());
+    QModelIndexList getActiveViewList();
+    void activeViewListTraverse(QModelIndex index);
+
+    /*VisIVO*/
+    bool setTable(const QModelIndex &index, VSTableDesktop *table);
+    bool setVTI(const QModelIndex &index, vtkVolume *volume);
+    bool setVTP(const QModelIndex &index, vtkLODActor *pActor);
+    bool setVisualObject(const QModelIndex &index, VisPoint* vis );
+   // bool setFITSIMG(const QModelIndex &index, vtkSmartPointer<vtkImageActor> imgActor);
+    bool setFITSIMG(const QModelIndex &index, vtkSmartPointer<vtkFitsReader> fitsReader);
+    VisPoint* getVisualObject(const QModelIndex &index);
+    VSTableDesktop *getTable(const QModelIndex &index);
+    vtkLODActor *getVTP(const QModelIndex &index);
+    vtkVolume *getVTI(const QModelIndex &index);
+    vtkSmartPointer<vtkFitsReader> getFITSIMG(const QModelIndex &index);
+
+   // vtkImageActor *getFITSIMG(const QModelIndex &index);
+    void setupModelData(const QStringList &lines, TreeItem *parent);
+    TreeItem *getItem(const QModelIndex &index) const;
+    TreeItem::TreeItemType getType(const QModelIndex &index);
+    QModelIndex  getLastInsertItem();
+    void  setLastInsertItem(QModelIndex item);
+
+
+
+private:
+
+    TreeItem *rootItem;
+    QModelIndex  lastInsertItem;
+};
+
+#endif
+
+
+
diff --git a/Code/src/vialactea.cpp b/Code/src/vialactea.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d87162d070216103232a7fffc91b97dbfcdcaa9
--- /dev/null
+++ b/Code/src/vialactea.cpp
@@ -0,0 +1,463 @@
+#include "vialactea.h"
+#include "ui_vialactea.h"
+#include <QWebFrame>
+#include <QWebElement>
+#include "vialacteainitialquery.h"
+#include <QFileDialog>
+#include <QMessageBox>
+#include "mainwindow.h"
+#include "singleton.h"
+#include <QSettings>
+#include "settingform.h"
+#include "aboutform.h"
+#include "vlkbsimplequerycomposer.h"
+#include "sed.h"
+#include "sedvisualizerplot.h"
+#include "vialacteastringdictwidget.h"
+
+
+ViaLactea::ViaLactea(QWidget *parent) :
+    QMainWindow(parent),
+    ui(new Ui::ViaLactea)
+{
+    ui->setupUi(this);
+
+    ui->saveToDiskCheckBox->setVisible(false);
+    ui->fileNameLineEdit->setVisible(false);
+    ui->selectFsPushButton->setVisible(false);
+
+
+
+    //Svuoto i file temp del precedente run
+
+    QDir dir_tmp(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp/tmp_download"));
+    foreach(QString dirFile, dir_tmp.entryList())
+    {
+        dir_tmp.remove(dirFile);
+    }
+
+    //end
+
+
+    m_sSettingsFile = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini");
+
+
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+
+    if (settings.value("vlkbtype", "public").toString()=="public")
+    {
+        qDebug()<<"public access to vlkb";
+        settings.setValue("vlkburl","http://ia2-vialactea.oats.inaf.it/libjnifitsdb-1.0.2p/");
+        settings.setValue("vlkbtableurl","http://ia2-vialactea.oats.inaf.it/vlkb/catalogues/tap");
+
+
+
+    }
+    else if (settings.value("vlkbtype", "public").toString()=="private")
+    {
+        qDebug()<<"private access to vlkb";
+
+
+        QString user= settings.value("vlkbuser", "").toString();
+        QString pass = settings.value("vlkbpass", "").toString();
+
+
+       // settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-0.23.2/");
+      //  settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-0.23.16/");
+        settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-1.0.2/");
+        settings.setValue("vlkbtableurl","http://ia2-vialactea.oats.inaf.it:8080/vlkb");
+
+
+    }
+
+    if (settings.value("online",true) == true)
+    {
+        tilePath = settings.value("onlinetilepath", "http://visivo.oact.inaf.it/vialacteatiles/openlayers.html").toString();
+        ui->webView->load(QUrl(tilePath));
+
+    }
+    else
+    {
+       tilePath = settings.value("tilepath", "").toString();
+       ui->webView->load(QUrl::fromLocalFile(tilePath));
+
+    }
+    ui->webView->setContextMenuPolicy(Qt::CustomContextMenu);
+
+    connect(ui->webView, SIGNAL(clicked()), this, SLOT(on_queryPushButton_clicked()));
+    QObject::connect( this, SIGNAL(destroyed()), qApp, SLOT(quit()) );
+/*
+    if (tilePath=="")
+    {
+        on_actionSettings_triggered();
+    }
+*/
+
+
+    VialacteaStringDictWidget *stringDictWidget = &Singleton<VialacteaStringDictWidget>::Instance();
+    stringDictWidget->buildDict();
+
+    qDebug()<<"----------tilePath: "<<tilePath;
+
+    mapSurvey.insert(0,QPair <QString, QString>("MIPSGAL","24 um"));
+    mapSurvey.insert(1,QPair <QString, QString>("GLIMPSE I","8.0 um"));
+    mapSurvey.insert(2,QPair <QString, QString>("GLIMPSE I","5.8 um"));
+    mapSurvey.insert(3,QPair <QString, QString>("GLIMPSE I","4.5 um"));
+    mapSurvey.insert(4,QPair <QString, QString>("GLIMPSE I","3.6 um"));
+    mapSurvey.insert(5,QPair <QString, QString>("Hi-GAL","500 um"));
+    mapSurvey.insert(6,QPair <QString, QString>("Hi-GAL","350 um"));
+    mapSurvey.insert(7,QPair <QString, QString>("Hi-GAL","250 um"));
+    mapSurvey.insert(8,QPair <QString, QString>("Hi-GAL","70 um"));
+    mapSurvey.insert(9,QPair <QString, QString>("Hi-GAL","160 um"));
+    mapSurvey.insert(10,QPair <QString, QString>("WISE","22 um"));
+    mapSurvey.insert(11,QPair <QString, QString>("WISE","12 um"));
+    mapSurvey.insert(12,QPair <QString, QString>("WISE","4.6 um"));
+    mapSurvey.insert(13,QPair <QString, QString>("WISE","3.4 um"));
+    mapSurvey.insert(14,QPair <QString, QString>("ATLASGAL","870 um"));
+    mapSurvey.insert(15,QPair <QString, QString>("CSO BGPS","1.1 mm"));
+    mapSurvey.insert(16,QPair <QString, QString>("CORNISH","5 GHz"));
+
+
+
+}
+
+ViaLactea::~ViaLactea()
+{
+    delete ui;
+}
+
+
+void ViaLactea::updateVLKBSetting()
+{
+
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+
+    if (settings.value("vlkbtype", "").toString()=="public")
+    {
+        qDebug()<<"public access to vlkb";
+        //settings.setValue("vlkburl","http://ia2-vialactea.oats.inaf.it/publicfitsdb-0.23.12/");
+       // settings.setValue("vlkburl","http://ia2-vialactea.oats.inaf.it/publicfitsdb-0.23.16/");
+        settings.setValue("vlkburl","http://ia2-vialactea.oats.inaf.it/libjnifitsdb-1.0.2p/");
+
+
+
+        //settings.setValue("vlkbtableurl","http://ia2-vialactea.oats.inaf.it:8080/vlkb");
+        settings.setValue("vlkbtableurl","http://ia2-vialactea.oats.inaf.it/vlkb/catalogues/tap");
+
+    }
+    else if (settings.value("vlkbtype", "").toString()=="private")
+    {
+        qDebug()<<"private access to vlkb";
+
+
+        QString user= settings.value("vlkbuser", "").toString();
+        QString pass = settings.value("vlkbpass", "").toString();
+
+
+        //settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-0.23.16/");
+        settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-1.0.2/");
+        settings.setValue("vlkbtableurl","http://ia2-vialactea.oats.inaf.it:8080/vlkb");
+
+
+    }
+
+}
+
+void ViaLactea::on_PLW_checkBox_clicked()
+{
+
+}
+
+void ViaLactea::on_queryPushButton_clicked()
+{
+
+
+    VialacteaInitialQuery *vq;
+    if (ui->saveToDiskCheckBox->isChecked())
+    {
+        if( ui->fileNameLineEdit->text()!="" )
+            vq= new VialacteaInitialQuery(ui->fileNameLineEdit->text());
+        else
+        {
+            QMessageBox::critical(this,"Error", "Insert filename");
+            return;
+        }
+    }
+    else
+        vq= new VialacteaInitialQuery();
+
+    vq->setL(ui->glonLineEdit->text());
+    vq->setB(ui->glatLineEdit->text());
+    if (ui->radiumLineEdit->text()!="")
+        vq->setR(ui->radiumLineEdit->text());
+    else
+    {
+        vq->setDeltaRect(ui->dlLineEdit->text(),ui->dbLineEdit->text());
+    }
+
+    QList < QPair<QString, QString> > selectedSurvey;
+
+    QList<QCheckBox *> allButtons = ui->surveySelectorGroupBox->findChildren<QCheckBox *>();
+    for(int i = 0; i < allButtons.size(); ++i)
+    {
+        qDebug()<<"i: "<<i<<" "<<allButtons.at(i);
+
+        if(allButtons.at(i)->isChecked())
+        {
+
+
+            selectedSurvey.append(mapSurvey.value(i));
+        }
+    }
+
+    //connettere la banda selezionata
+    vq->setSpecies("Continuum");
+    vq->setSurveyname(selectedSurvey.at(0).first);
+    vq->setTransition(selectedSurvey.at(0).second);
+    vq->setSelectedSurveyMap(selectedSurvey);
+    vq->on_queryPushButton_clicked();
+
+
+}
+
+
+void ViaLactea::on_noneRadioButton_clicked(bool checked)
+{
+    if(checked)
+    {
+        ui->webView->page()->mainFrame()->evaluateJavaScript( "activatePointSelection(false)" );
+        ui->webView->page()->mainFrame()->evaluateJavaScript( "activateRectangularSelection(false)" );
+    }
+}
+
+void ViaLactea::on_saveToDiskCheckBox_clicked(bool checked)
+{
+    ui->fileNameLineEdit->setEnabled(checked);
+    ui->selectFsPushButton->setEnabled(checked);
+}
+
+void ViaLactea::on_selectFsPushButton_clicked()
+{
+    QString fn = QFileDialog::getSaveFileName(this, "Save as...", QString(), "Fits images (*.fits)");
+
+    if (!fn.isEmpty() && ! fn.endsWith(".fits", Qt::CaseInsensitive)  )
+        fn += ".fits"; // default
+    ui->fileNameLineEdit->setText(fn);
+
+
+}
+
+
+void ViaLactea::on_webView_statusBarMessage(const QString &text)
+{
+
+    QWebElement e = ui->webView->page()->mainFrame()->findFirstElement("div#selected_point");
+
+    //  qDebug()<<"e: "<<e.toPlainText();
+
+    if (e.toPlainText()!="")
+    {
+        QStringList pieces = e.toPlainText().split( "," );
+        ui->glatLineEdit->setText(QString::number( pieces[1].toDouble(), 'f', 4 ));
+        ui->glonLineEdit->setText(QString::number( pieces[0].toDouble(), 'f', 4 ));
+        if(ui->radiumLineEdit->text()=="")
+            ui->radiumLineEdit->setText("0.1");
+        ui->dlLineEdit->setText("");
+        ui->dbLineEdit->setText("");
+
+        ui->noneRadioButton->setChecked(true);
+        on_noneRadioButton_clicked(true);
+    }
+
+    QWebElement e_radius = ui->webView->page()->mainFrame()->findFirstElement("div#selected_radius");
+    // qDebug()<<"e_radius: "<<e_radius.toPlainText();
+    if (e_radius.toPlainText()!="")
+    {
+        QStringList pieces = e_radius.toPlainText().split( "," );
+        // qDebug()<<pieces;
+        QString dl=QString::number( pieces[0].toDouble(), 'f', 4 );
+        if(dl.toDouble()>4.0)
+            dl=QString::number(4.0, 'f', 4 );
+        QString db=QString::number( pieces[1].toDouble(), 'f', 4 );
+        if(db.toDouble()>4.0)
+            db=QString::number(4.0, 'f', 4 );
+        ui->dlLineEdit->setText(dl);
+        ui->dbLineEdit->setText(db);
+        //  if(ui->radiumLineEdit->text()=="")
+        ui->radiumLineEdit->setText("");
+        //  ui->noneRadioButton->setChecked(true);
+        //  on_noneRadioButton_clicked(true);
+    }
+
+
+}
+
+void ViaLactea::on_glonLineEdit_textChanged(const QString &arg1)
+{
+    queryButtonStatusOnOff();
+}
+
+void ViaLactea::on_glatLineEdit_textChanged(const QString &arg1)
+{
+    queryButtonStatusOnOff();
+}
+
+void ViaLactea::on_radiumLineEdit_textChanged(const QString &arg1)
+{
+    queryButtonStatusOnOff();
+}
+
+void ViaLactea::queryButtonStatusOnOff()
+{
+    if (ui->glatLineEdit->text()!="" && ui->glonLineEdit->text()!="" && (ui->radiumLineEdit->text()!="" || (ui->dlLineEdit->text()!="" && ui->dbLineEdit->text()!="") ) )
+        ui->queryPushButton->setEnabled(true);
+    else
+        ui->queryPushButton->setEnabled(false);
+}
+
+void ViaLactea::on_openLocalImagePushButton_clicked()
+{
+    QString fn = QFileDialog::getOpenFileName(this, "Open image file", QString(), "Fits images (*.fits)");
+
+    if (!fn.isEmpty() )
+    {
+        MainWindow *w = &Singleton<MainWindow>::Instance();
+        w->importFitsImage(fn);
+    }
+}
+
+
+
+void ViaLactea::on_actionSettings_triggered()
+{
+    SettingForm *s = &Singleton<SettingForm>::Instance();
+    s->show();
+
+    s->activateWindow();
+
+}
+
+void ViaLactea::reload()
+{
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+
+
+    //tilePath = settings.value("tilepath", "").toString();
+
+    if (settings.value("online",false) == true)
+    {
+        tilePath = settings.value("onlinetilepath", "").toString();
+        ui->webView->load(QUrl(tilePath));
+
+    }
+    else
+    {
+       tilePath = settings.value("tilepath", "").toString();
+       ui->webView->load(QUrl::fromLocalFile(tilePath));
+
+
+    }
+    updateVLKBSetting();
+
+
+
+}
+
+void ViaLactea::on_localDCPushButton_clicked()
+{
+
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+    w->on_actionTEST_DC3D_triggered();
+}
+
+void ViaLactea::on_actionExit_triggered()
+{
+    // QCoreApplication::exit(0);
+    this->close();
+}
+
+void   ViaLactea::closeEvent(QCloseEvent*)
+{
+    qApp->closeAllWindows();
+    //    qApp->quit();
+}
+
+void ViaLactea::on_actionAbout_triggered()
+{
+    AboutForm *w = &Singleton<AboutForm>::Instance();
+    w->show();
+}
+
+void ViaLactea::on_select3dPushButton_clicked()
+{
+    VLKBSimpleQueryComposer *skyregionquery = new VLKBSimpleQueryComposer(NULL);
+    skyregionquery->setIs3dSelections();
+
+    skyregionquery->setLongitude(0,360);
+    skyregionquery->setLatitude(-1,1);
+    skyregionquery->show();
+}
+
+void ViaLactea::on_actionLoad_SED_2_triggered()
+{
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Load SED fits"), QDir::homePath(), tr("Archive (*.zip)"));
+    QString sedZipPath=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/SED.zip";
+    QFile::copy(fileName, sedZipPath);
+    QProcess process_unzip;
+    process_unzip.start ("unzip "+sedZipPath+" -d "+QDir::homePath()+"/VisIVODesktopTemp/tmp_download");
+
+    process_unzip.waitForFinished(); // sets current thread to sleep and waits for process end
+    QString output_unzip(process_unzip.readAll());
+
+
+    QDir tmp_download(QDir::homePath()+"/VisIVODesktopTemp/tmp_download");
+    QStringList filters;
+    filters << "SED*";
+    tmp_download.setNameFilters(filters);
+    QStringList dirList=tmp_download.entryList();
+
+
+    QFile sedFile(QDir::homePath().append(QDir::separator()).append("/VisIVODesktopTemp/tmp_download/SEDList.dat"));
+    sedFile.open(QIODevice::ReadOnly);
+    QDataStream in(&sedFile);    // read the data serialized from the file
+    QList<SED *> sed_list2;
+    in >> sed_list2;
+    /*
+    foreach(SED* sed, sed_list2){
+        qDebug()<<"SED Designation";
+        qDebug()<<sed->getRootNode()->getDesignation();
+    }
+*/
+
+    ViaLactea *vialactealWin = &Singleton<ViaLactea>::Instance();
+    SEDVisualizerPlot *sedv= new SEDVisualizerPlot(sed_list2,0,vialactealWin);
+    sedv->show();
+    sedv->loadSavedSED(dirList);
+}
+
+
+void ViaLactea::on_pointRadioButton_clicked(bool checked)
+{
+    if(checked)
+    {
+        ui->webView->page()->mainFrame()->evaluateJavaScript( "activatePointSelection(true)" );
+    }
+}
+
+void ViaLactea::on_rectRadioButton_clicked(bool checked)
+{
+    if(checked)
+    {
+        ui->webView->page()->mainFrame()->evaluateJavaScript( "activateRectangularSelection(true)" );
+    }
+}
+
+void ViaLactea::on_dlLineEdit_textChanged(const QString &arg1)
+{
+    queryButtonStatusOnOff();
+}
+
+void ViaLactea::on_dbLineEdit_textChanged(const QString &arg1)
+{
+    queryButtonStatusOnOff();
+}
diff --git a/Code/src/vialactea.h b/Code/src/vialactea.h
new file mode 100644
index 0000000000000000000000000000000000000000..3d2f66e6bc89106ac9ed3b924aed157f1ad726fc
--- /dev/null
+++ b/Code/src/vialactea.h
@@ -0,0 +1,65 @@
+#ifndef VIALACTEA_H
+#define VIALACTEA_H
+
+#include <QMainWindow>
+#include <QMap>
+#include <QPair>
+
+namespace Ui {
+class ViaLactea;
+}
+
+class ViaLactea : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit ViaLactea(QWidget *parent = 0);
+    void reload();
+
+    ~ViaLactea();
+
+private slots:
+    void on_PLW_checkBox_clicked();
+    void on_queryPushButton_clicked();
+    void on_noneRadioButton_clicked(bool checked);
+    void on_saveToDiskCheckBox_clicked(bool checked);
+    void on_selectFsPushButton_clicked();
+    void on_webView_statusBarMessage(const QString &text);
+    void on_glonLineEdit_textChanged(const QString &arg1);
+    void on_glatLineEdit_textChanged(const QString &arg1);
+    void on_radiumLineEdit_textChanged(const QString &arg1);
+    void queryButtonStatusOnOff();
+    void on_openLocalImagePushButton_clicked();
+    void on_actionSettings_triggered();
+    void on_localDCPushButton_clicked();
+    void on_actionExit_triggered();
+
+    void on_actionAbout_triggered();
+
+    void on_select3dPushButton_clicked();
+
+    void on_actionLoad_SED_2_triggered();
+
+    void on_pointRadioButton_clicked(bool checked);
+
+    void on_rectRadioButton_clicked(bool checked);
+
+    void on_dlLineEdit_textChanged(const QString &arg1);
+
+    void on_dbLineEdit_textChanged(const QString &arg1);
+    void updateVLKBSetting();
+
+private:
+    Ui::ViaLactea *ui;
+    QString selectedBand;
+    QString m_sSettingsFile;
+    QString tilePath;
+    QMap <int, QPair<QString, QString> > mapSurvey;
+
+protected:
+    void  closeEvent(QCloseEvent*);
+
+};
+
+#endif // VIALACTEA_H
diff --git a/Code/src/vialactea_fileload.cpp b/Code/src/vialactea_fileload.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..461080eefd7ed666cbefb26c0404147aa5322383
--- /dev/null
+++ b/Code/src/vialactea_fileload.cpp
@@ -0,0 +1,159 @@
+#include "vialactea_fileload.h"
+#include "ui_vialactea_fileload.h"
+#include "mainwindow.h"
+#include "singleton.h"
+//#include "vialactea.h"
+#include <QDebug>
+#include "astroutils.h"
+#include <QSettings>
+
+Vialactea_FileLoad::Vialactea_FileLoad(QString f, bool silent, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::Vialactea_FileLoad)
+{
+    ui->setupUi(this);
+
+
+
+
+    silentLoad=silent;
+
+    connect( ui->bandmergedradioButton, SIGNAL( toggled(bool) ), this, SLOT( on_groupBox_toggled(bool) ) );
+    connect( ui->mapRadioButton, SIGNAL( toggled(bool) ), this, SLOT( on_groupBox_toggled(bool) ) );
+    connect( ui->catalogueRadioButton, SIGNAL( toggled(bool) ), this, SLOT( on_groupBox_toggled(bool) ) );
+
+    init(f);
+
+}
+
+Vialactea_FileLoad::Vialactea_FileLoad(QString f, vtkwindow_new *v,QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::Vialactea_FileLoad)
+{
+    ui->setupUi(this);
+
+    vtkwin=v;
+    init(f);
+}
+
+
+
+
+void Vialactea_FileLoad::init(QString f)
+{
+    filename=f;
+
+    ui->filenameLabel->setText(filename);
+
+    if(filename.endsWith(".fits",Qt::CaseInsensitive))
+    {
+        ui->mapRadioButton->setChecked(true);
+        ui->waveLabel->setVisible(false);
+        ui->waveLineEdit->setVisible(false);
+
+    }
+}
+
+Vialactea_FileLoad::~Vialactea_FileLoad()
+{
+    delete ui;
+}
+
+void Vialactea_FileLoad::setWavelength(QString w)
+{
+    ui->waveLineEdit->setText(w);
+}
+
+void Vialactea_FileLoad::setCatalogueActive()
+{
+    ui->catalogueRadioButton->setChecked(true);
+}
+
+void Vialactea_FileLoad::on_okPushButton_clicked()
+{
+
+
+
+    if(ui->mapRadioButton->isChecked())
+    {
+        /*
+
+        vtkSmartPointer<vtkFitsReader> fitsReader = vtkSmartPointer<vtkFitsReader>::New();
+        fitsReader->SetFileName(filename.toLocal8Bit().data());
+        QFileInfo infoFile = QFileInfo(filename);
+
+        ViaLactea *vialacteaWindow = &Singleton<ViaLactea>::Instance();
+
+        // Modify the TreeModel
+        int rows = vialacteaWindow->getTreeModel()->rowCount();
+        vialacteaWindow->getTreeModel()->insertRow(rows);
+        vialacteaWindow->getTreeModel()->setFITSIMG(vialacteaWindow->getTreeModel()->index(rows,0),fitsReader);
+        vialacteaWindow->getTreeModel()->setData(vialacteaWindow->getTreeModel()->index(rows,0),QIcon( ":/icons/VME_IMAGE.bmp" ));
+        vialacteaWindow->getTreeModel()->setData(vialacteaWindow->getTreeModel()->index(rows,1),infoFile.fileName());
+        // end: Modify the TreeModel
+
+
+        this->close();
+*/
+    }
+    else if(ui->catalogueRadioButton->isChecked())
+    {
+        qDebug()<<"catalogue "<<silentLoad;
+        MainWindow *w = &Singleton<MainWindow>::Instance();
+        w->importAscii( filename,ui->waveLineEdit->text(),true,false,vtkwin );
+        if(!silentLoad)
+            this->close();
+
+    }
+
+    else if(ui->bandmergedradioButton->isChecked())
+    {
+        importBandMerged();
+    }
+}
+
+void Vialactea_FileLoad::importBandMerged()
+{
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+    w->importAscii(filename,"",true,true,vtkwin);
+    this->close();
+}
+
+void Vialactea_FileLoad::importFilaments()
+{
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+    w->importAsciiFilaments(filename,vtkwin);
+    // w->importAscii(filename,"",true,true,vtkwin);
+    this->close();
+}
+
+void Vialactea_FileLoad::importBubbles()
+{
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+    w->importAsciiBubbles(filename,vtkwin);
+    this->close();
+}
+
+void Vialactea_FileLoad::import3dSelection()
+{
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+    w->importAscii3dSelection(filename,vtkwin);
+    // w->importAscii(filename,"",true,true,vtkwin);
+    this->close();
+}
+
+void Vialactea_FileLoad::on_groupBox_toggled(bool arg1)
+{
+    if(ui->catalogueRadioButton->isChecked())
+    {
+
+        ui->waveLabel->setVisible(true);
+        ui->waveLineEdit->setVisible(true);
+    }
+    else
+    {
+        ui->waveLabel->setVisible(false);
+        ui->waveLineEdit->setVisible(false);
+    }
+
+}
diff --git a/Code/src/vialactea_fileload.h b/Code/src/vialactea_fileload.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea731ff1c51cc4ea65491dd734d086bc18729ad3
--- /dev/null
+++ b/Code/src/vialactea_fileload.h
@@ -0,0 +1,42 @@
+#ifndef VIALACTEA_FILELOAD_H
+#define VIALACTEA_FILELOAD_H
+
+#include <QWidget>
+#include "vtkwindow_new.h"
+namespace Ui {
+class Vialactea_FileLoad;
+}
+
+class Vialactea_FileLoad : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit Vialactea_FileLoad(QString f, bool silent=false, QWidget *parent = 0);
+    explicit Vialactea_FileLoad(QString f, vtkwindow_new *v,QWidget *parent = 0);
+    void init(QString f);
+    void setVtkWin(vtkwindow_new *v){vtkwin=v;}
+    void importBandMerged();
+    void setCatalogueActive();
+    void setWavelength(QString w);
+    void importFilaments();
+    void import3dSelection();
+    void importBubbles();
+
+
+    QString filename;
+    ~Vialactea_FileLoad();
+
+public slots:
+    void on_okPushButton_clicked();
+
+private slots:
+    void on_groupBox_toggled(bool arg1);
+
+private:
+    Ui::Vialactea_FileLoad *ui;
+    vtkwindow_new *vtkwin;
+    bool silentLoad;
+};
+
+#endif // VIALACTEA_FILELOAD_H
diff --git a/Code/src/vialacteainitialquery.cpp b/Code/src/vialacteainitialquery.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e25d457bdab48c1eedc8a86984eaa9549531c98b
--- /dev/null
+++ b/Code/src/vialacteainitialquery.cpp
@@ -0,0 +1,604 @@
+#include "vialacteainitialquery.h"
+#include "ui_vialacteainitialquery.h"
+#include <QUrl>
+#include <QDebug>
+#include <QNetworkAccessManager>
+#include <QXmlStreamReader>
+#include "downloadmanager.h"
+#include <QMessageBox>
+#include <QFile>
+#include <QDir>
+#include "mainwindow.h"
+#include "singleton.h"
+#include <QSettings>
+#include <QUrlQuery>
+
+VialacteaInitialQuery::VialacteaInitialQuery(QString fn, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::VialacteaInitialQuery)
+{
+    ui->setupUi(this);
+
+    ui->rectGroupBox->hide();
+
+    QSettings settings(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini"), QSettings::NativeFormat);
+    vlkbUrl= settings.value("vlkburl", "").toString();
+
+
+
+
+    if (settings.value("vlkbtype", "public").toString()=="public")
+    {
+        qDebug()<<"public access to vlkb";
+        settings.setValue("vlkburl","http://ia2-vialactea.oats.inaf.it/libjnifitsdb-1.0.2p/");
+        settings.setValue("vlkbtableurl","http://ia2-vialactea.oats.inaf.it/vlkb/catalogues/tap");
+ url_prefix="http://";
+
+
+    }
+    else if (settings.value("vlkbtype", "public").toString()=="private")
+    {
+        qDebug()<<"private access to vlkb";
+
+
+        QString user= settings.value("vlkbuser", "").toString();
+        QString pass = settings.value("vlkbpass", "").toString();
+
+
+       // settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-0.23.2/");
+      //  settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-0.23.16/");
+        settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-1.0.2/");
+        settings.setValue("vlkbtableurl","http://ia2-vialactea.oats.inaf.it:8080/vlkb");
+        url_prefix="http://"+user+":"+pass+"@";
+
+
+    }
+
+
+
+
+
+  //  QString user= settings.value("vlkbuser", "").toString();
+  //  QString pass = settings.value("vlkbpass", "").toString();
+
+    // settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-0.23.16/");
+    //settings.setValue("vlkburl","http://"+user+":"+pass+"@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-1.0.2/");
+
+
+    parser =new xmlparser();
+    loading = new LoadingWidget();
+    outputFile=fn;
+    species="";
+    p=parent;
+    myCallingVtkWindow=0;
+
+    transitions.insert("PLW","500um");
+    transitions.insert("PMW","350um");
+    transitions.insert("PSW","250um");
+    transitions.insert("red","160um");
+    transitions.insert("blue","70um");
+
+
+
+}
+
+VialacteaInitialQuery::~VialacteaInitialQuery()
+{
+    delete ui;
+}
+
+void VialacteaInitialQuery::on_pushButton_clicked()
+{
+    this->close();
+}
+
+
+void VialacteaInitialQuery::on_pointRadioButton_toggled(bool checked)
+{
+    if(checked)
+    {
+        ui->rectGroupBox->hide();
+        ui->pointGroupBox->show();
+    }
+    else
+    {
+        ui->rectGroupBox->show();
+        ui->pointGroupBox->hide();
+    }
+}
+
+void VialacteaInitialQuery::setL(QString l)
+{
+    ui->l_lineEdit->setText(l);
+}
+void VialacteaInitialQuery::setB(QString b)
+{
+    ui->b_lineEdit->setText(b.replace(" ",""));
+}
+void VialacteaInitialQuery::setR(QString r)
+{
+    isRadius=true;
+    ui->r_lineEdit->setText(r);
+}
+
+void VialacteaInitialQuery::setDeltaRect(QString dl,QString db)
+{
+    isRadius=false;
+    ui->dlLineEdit->setText(dl);
+    ui->dbLineEdit->setText(db);
+
+
+}
+void VialacteaInitialQuery::setSurveyname(QString s)
+{
+    //surveyname="HI-Gal%20mosaic%20"+s;//PSW";
+    // surveyname="HI-Gal%20mosaic";
+    surveyname=s;
+    // transition=transitions.value(s);
+}
+
+void VialacteaInitialQuery::setTransition(QString s)
+{
+    transition=s;
+
+}
+
+
+
+void VialacteaInitialQuery::cutoutRequest(QString url, QList< QMap<QString,QString> > el, int pos)
+{
+    loading ->show();
+    loading->setFileName("Querying cutout services");
+    elementsOnDb=el;
+    species=elementsOnDb.at(pos).value("Species");
+    pubdid=elementsOnDb.at(pos).value("PublisherDID");
+    surveyname=elementsOnDb.at(pos).value("Survey");
+    descriptionFromDB=elementsOnDb.at(pos).value("Description");
+    transition=elementsOnDb.at(pos).value("Transition");
+    velfrom=elementsOnDb.at(pos).value("from");
+    velto=elementsOnDb.at(pos).value("to");
+
+    velocityUnit=elementsOnDb.at(pos).value("VelocityUnit");
+    nam = new QNetworkAccessManager(this);
+    QObject::connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(finishedSlot(QNetworkReply*)));
+
+
+    url=url.replace("http://",url_prefix);
+
+    QUrl url_enc=QUrl(url);
+    QList<QPair<QString, QString> > urlQuery = QUrlQuery(url_enc).queryItems();
+    QList<QPair<QString, QString> > ::iterator j;
+    for (j = urlQuery.begin(); j != urlQuery.end(); ++j)
+    {
+        (*j).second=QUrl::toPercentEncoding((*j).second);
+    }
+
+    QUrlQuery q;
+    q.setQueryItems(urlQuery);
+    url_enc.setQuery(q);
+
+    nam->get(QNetworkRequest(url_enc));
+
+}
+
+void VialacteaInitialQuery::selectedStartingLayersRequest(QUrl url)
+{
+    qDebug()<<"1) L "<<ui->l_lineEdit->text()<<" B "<<ui->b_lineEdit->text()<<" DL "<<ui->dlLineEdit->text()<<" DB "<<ui->dbLineEdit->text()<<" R "<<ui->r_lineEdit->text();
+    qDebug()<<"\t"<<" species"<<species<<" trans "<<transition<<" survey "<<surveyname;
+    loading ->show();
+    loading->setFileName("Querying cutout services");
+
+    nam = new QNetworkAccessManager(this);
+    QObject::connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(finishedSlot(QNetworkReply*)));
+
+    qDebug()<<url;
+    nam->get(QNetworkRequest(url));
+
+}
+
+
+void VialacteaInitialQuery::on_queryPushButton_clicked()
+{
+
+
+    loading ->show();
+    loading->activateWindow();
+    loading->setFileName("Querying cutout services");
+
+
+    nam = new QNetworkAccessManager(this);
+    QObject::connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(finishedSlot(QNetworkReply*)));
+
+    QString urlString=vlkbUrl+"/vlkb_search?l="+ui->l_lineEdit->text()+"&b="+ui->b_lineEdit->text();//+"&species="+species;
+    if(isRadius)
+    {
+        urlString+="&r="+ui->r_lineEdit->text();
+    }
+    else
+        urlString+="&dl="+ui->dlLineEdit->text()+"&db="+ui->dbLineEdit->text();
+
+    urlString+="&vl=-500000&vu=500000";
+
+    QUrl url2 (urlString);
+
+    nam->get(QNetworkRequest(url2));
+
+    qDebug()<<"INITIAL QUERY:\n"<<urlString;
+}
+
+
+void VialacteaInitialQuery::finishedSlot(QNetworkReply* reply)
+{
+    QString string;
+    QString file;
+
+
+    qDebug()<<reply->errorString();
+
+    qDebug()<<"2) L "<<ui->l_lineEdit->text()<<" B "<<ui->b_lineEdit->text()<<" DL "<<ui->dlLineEdit->text()<<" DB "<<ui->dbLineEdit->text()<<" R "<<ui->r_lineEdit->text();
+
+    qDebug ()<<reply;
+
+
+    if (reply->error() == QNetworkReply::NoError)
+    {
+
+        QXmlStreamReader xml(reply);
+        QString url=reply->request().url().toString();
+
+        /*       if( url.contains("vlkb_search")  )
+        {
+
+            parser->parseXML_fitsDownload(xml, string);
+
+
+        }
+*/
+        //seconda query per cercare il fits corretto
+        /*   if( url.contains("vlkb_search") && url.contains("surveyname") )
+        {
+
+            parser->parseXML_fitsDownload(xml, string);
+            if(string.compare("NULL")!=0)
+            {
+                loading->setFileName("Fits image found");
+
+                QString urlStringCutout=vlkbUrl+"/vlkb_cutout?pubdid="+QUrl::toPercentEncoding(string)+"&l="+ui->l_lineEdit->text()+"&b="+ui->b_lineEdit->text();
+                if(isRadius)
+                {
+                    urlStringCutout+="&r="+ui->r_lineEdit->text();
+                }
+                else
+                    urlStringCutout+="&dl="+ui->dlLineEdit->text()+"&db="+ui->dbLineEdit->text();
+
+                urlStringCutout+="&vl=0&vu=0&nullvals";
+
+                qDebug()<<urlStringCutout;
+                QUrl url2 (urlStringCutout);
+
+
+                //nam->get(QNetworkRequest(url2));
+            }
+            else
+            {
+                loading->setFileName("No fits image on this region");
+                loading->loadingEnded();
+                loading->hide();
+                QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr("No fits image on this region - Try again"));
+            }
+        }
+
+        */
+
+        if (url.contains("vlkb_cutout") || url.contains("vlkb_merge")  )
+        {
+            parser->parseXML(xml, string);
+
+            if(!string.contains("NULL"))
+            {
+                loading->setFileName("Datacube found");
+                DownloadManager *manager= new DownloadManager();
+                QString urlString=string.trimmed();
+                QUrl url3(urlString);
+
+                //segnale tra due oggetti:
+                connect(manager, SIGNAL(downloadCompleted()),this, SLOT(on_download_completed()));
+
+                file=manager->doDownload(url3,outputFile);
+                loading->loadingEnded();
+                loading->hide();
+                downloadedFile=file;
+
+            }
+            else
+            {
+                QStringList result_splitted=string.split(" ");
+                QString msg="Inconsistent data (PubDID vs Region only partially overlap)";
+                if(result_splitted.length()>1)
+                    msg="Null values percentage is "+ result_splitted[1]+" (greater than 95%)";
+                QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr(msg.toStdString().c_str()));
+
+                loading->loadingEnded();
+                loading->hide();
+
+                //  p->show();
+            }
+
+        }
+
+        //La query iniziale
+        if( url.contains("vlkb_search") && !url.contains("surveyname") )
+        {
+            elementsOnDb= parser->parseXmlAndGetList(xml);
+            QString best_url;
+            int best_code=4;
+            QList< QMap<QString,QString> >::iterator j;
+             qDebug()<<"CERCO: "<<species<<" "<<" "<<surveyname<<" "<< transition;
+            for (j = elementsOnDb.begin(); j != elementsOnDb.end(); ++j)
+            {
+
+                 qDebug()<<"ANALIZZO: "<<(*j).value("Species")<<" "<<(*j).value("Survey")<<" "<< (*j).value("Transition")<<" "<<(*j).value("code")<<(*j).value("VelocityUnit")<<" ";
+                if( (*j).value("Species").compare(species)==0 && (*j).value("Survey").contains(surveyname)  && (*j).value("Transition").compare(transition) ==0  && (*j).value("code").toInt()<best_code  )
+                {
+                    // qDebug()<<(*j).value("Species")<<" "<<(*j).value("Survey")<<" "<<surveyname<<" "<<(*j).value("Transition") << " code "<< (*j).value("code");
+                    best_url=(*j).value("URL");
+                    best_code=(*j).value("code").toInt();
+                }
+            }
+
+
+
+
+
+            QUrl url (best_url);
+
+            QList<QPair<QString, QString> > urlQuery = QUrlQuery(url).queryItems();
+            QList<QPair<QString, QString> > ::iterator i;
+            for (i = urlQuery.begin(); i != urlQuery.end(); ++i)
+            {
+                (*i).second=QUrl::toPercentEncoding((*i).second);
+            }
+
+            QUrlQuery q;
+            q.setQueryItems(urlQuery);
+            url.setQuery(q);
+
+
+            nam->get(QNetworkRequest(url));
+
+            /*
+        QString urlString=vlkbUrl+"/vlkb_search?surveyname="+QUrl::toPercentEncoding(surveyname)+"&l="+ui->l_lineEdit->text()+"&b="+ui->b_lineEdit->text()+"&species="+QUrl::toPercentEncoding(species)+"&transition="+QUrl::toPercentEncoding(transition);
+        if(isRadius)
+        {
+            urlString+="&r="+ui->r_lineEdit->text();
+        }
+        else
+            urlString+="&dl="+ui->dlLineEdit->text()+"&db="+ui->dbLineEdit->text();
+
+        urlString+="&vl=0&vu=0";
+
+
+        qDebug()<<"******°°°°°°° "<<urlString;
+        QUrl url (urlString);
+ nam->get(QNetworkRequest(url));
+
+        */
+        }
+
+        if( url.contains("vlkb_search") && url.contains("surveyname") )
+        {
+            QList< QMap<QString,QString> > elementsOnDb_tmp= parser->parseXmlAndGetList(xml);
+
+            QString best_url;
+            int best_code=4;
+
+            qDebug()<<"CERCO: "<<species<<" "<<" "<<surveyname<<" "<< transition;
+            qDebug()<<"aaa "<<elementsOnDb_tmp.size();
+            QList< QMap<QString,QString> >::iterator j;
+            for (j = elementsOnDb_tmp.begin(); j != elementsOnDb_tmp.end(); ++j)
+            {
+                //qDebug()<<(*j)<;
+                qDebug()<<"ANALIZZO: "<<(*j).value("Species")<<" "<<(*j).value("Survey")<<" "<< (*j).value("Transition")<<" "<<(*j).value("code");
+
+                if( (*j).value("Species").compare(species)==0 && (*j).value("Survey").contains(surveyname) && (*j).value("Transition").compare(transition) ==0  && (*j).value("code").toInt()<best_code  )
+                {
+
+                    // qDebug()<<(*j).value("Species")<<" "<<(*j).value("Survey")<<" "<<surveyname<<" "<<(*j).value("Transition") << " code "<< (*j).value("code");
+                    best_url=(*j).value("URL");
+                    best_code=(*j).value("code").toInt();
+                }
+            }
+
+            qDebug()<<"parrrrarararar B:    "<<best_url;
+
+
+
+
+            QUrl url (best_url);
+
+            QList<QPair<QString, QString> > urlQuery = QUrlQuery(url).queryItems();
+            QList<QPair<QString, QString> > ::iterator i;
+            for (i = urlQuery.begin(); i != urlQuery.end(); ++i)
+            {
+                (*i).second=QUrl::toPercentEncoding((*i).second);
+            }
+
+            QUrlQuery q;
+            q.setQueryItems(urlQuery);
+            url.setQuery(q);
+
+
+            nam->get(QNetworkRequest(url));
+        }
+
+
+        /*
+    if (reply->error() == QNetworkReply::NoError)
+    {
+
+        QXmlStreamReader xml(reply);
+        QString url=reply->request().url().toString();
+        qDebug()<<"finishedSlot ";
+        if( url.contains("vlkb_search")  )
+        {
+
+            parser->parseXML_fitsDownload(xml, string);
+            qDebug()<<"STRING:\n"<<string;
+
+
+        }
+*/
+        /*
+        //seconda query per cercare il fits corretto
+        if( url.contains("vlkb_search") && url.contains("surveyname") )
+        {
+
+            parser->parseXML_fitsDownload(xml, string);
+            qDebug()<<string;
+            if(string.compare("NULL")!=0)
+            {
+                loading->setFileName("Fits image found");
+
+                QString urlStringCutout=vlkbUrl+"/vlkb_cutout?pubdid="+QUrl::toPercentEncoding(string)+"&l="+ui->l_lineEdit->text()+"&b="+ui->b_lineEdit->text();
+                if(isRadius)
+                {
+                    urlStringCutout+="&r="+ui->r_lineEdit->text();
+                }
+                else
+                    urlStringCutout+="&dl="+ui->dlLineEdit->text()+"&db="+ui->dbLineEdit->text();
+
+                urlStringCutout+="&vl=0&vu=0&nullvals";
+
+                qDebug()<<urlStringCutout;
+                QUrl url2 (urlStringCutout);
+
+
+                nam->get(QNetworkRequest(url2));
+            }
+            else
+            {
+                loading->setFileName("No fits image on this region");
+                loading->loadingEnded();
+                loading->hide();
+                QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr("No fits image on this region - Try again"));
+            }
+        }
+
+        if (url.contains("vlkb_cutout") )
+        {
+            parser->parseXML(xml, string);
+
+            if(!string.contains("NULL"))
+            {
+                loading->setFileName("Datacube found");
+                DownloadManager *manager= new DownloadManager();
+                QString urlString=string.trimmed();
+                QUrl url3(urlString);
+
+                //segnale tra due oggetti:
+                connect(manager, SIGNAL(downloadCompleted()),this, SLOT(on_download_completed()));
+
+                file=manager->doDownload(url3,outputFile);
+                loading->loadingEnded();
+                loading->hide();
+                downloadedFile=file;
+
+            }
+            else
+            {
+                QStringList result_splitted=string.split(" ");
+                QString msg="Inconsistent data (PubDID vs Region only partially overlap)";
+                if(result_splitted.length()>1)
+                    msg="Null values percentage is "+ result_splitted[1]+" (greater than 95%)";
+                QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr(msg.toStdString().c_str()));
+
+                loading->loadingEnded();
+                loading->hide();
+
+                //  p->show();
+            }
+
+        }
+
+        //La query iniziale
+        if( url.contains("vlkb_search") && !url.contains("surveyname") )
+        {
+            elementsOnDb= parser->parseXmlAndGetList(xml);
+
+            QString urlString=vlkbUrl+"/vlkb_search?surveyname="+QUrl::toPercentEncoding(surveyname)+"&l="+ui->l_lineEdit->text()+"&b="+ui->b_lineEdit->text()+"&species="+QUrl::toPercentEncoding(species)+"&transition="+QUrl::toPercentEncoding(transition);
+            if(isRadius)
+            {
+                urlString+="&r="+ui->r_lineEdit->text();
+            }
+            else
+                urlString+="&dl="+ui->dlLineEdit->text()+"&db="+ui->dbLineEdit->text();
+
+            urlString+="&vl=0&vu=0";
+
+
+            qDebug()<<"******°°°°°°° "<<urlString;
+            QUrl url (urlString);
+            nam->get(QNetworkRequest(url));
+        }
+
+        */
+    }
+
+    else
+    {
+        //handle errors here
+        loading->loadingEnded();
+        loading->hide();
+        QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr("Server is not replying"));
+
+    }
+    //delete reply;
+    reply->deleteLater();
+
+}
+
+
+void VialacteaInitialQuery::on_download_completed()
+{
+    int test_flag_nanten=-1;
+    if(pubdid.compare("")!=0)
+        test_flag_nanten = pubdid.split("_", QString::SkipEmptyParts).last().toInt();
+
+    QString currentPath;
+    if (outputFile=="")
+        currentPath=downloadedFile;
+    else
+        currentPath=downloadedFile;
+
+    this->close();
+
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+
+    //if( (velfrom.compare("0.0") ==0 && velto.compare("0.0")==0 )  || ( surveyname.compare("NANTEN")==0 && test_flag_nanten==2) || QString::compare("cornish",surveyname, Qt::CaseInsensitive)==0|| QString::compare("cornish2d",surveyname, Qt::CaseInsensitive)==0 || QString::compare("magpis",surveyname, Qt::CaseInsensitive)==0  )
+
+    if ( (velfrom.compare("0.0") ==0 && velto.compare("0.0")==0 ) || species.compare("dust") ==0 || species.compare("Continuum")==0 || ( surveyname.compare("NANTEN")==0 && test_flag_nanten==2) || QString::compare("cornish",surveyname, Qt::CaseInsensitive)==0|| QString::compare("cornish2d",surveyname, Qt::CaseInsensitive)==0 || QString::compare("magpis",surveyname, Qt::CaseInsensitive)==0  )
+    {
+        bool l =false;
+
+        //Per layer fits decommetante FV
+
+        if(myCallingVtkWindow!=0)
+        {
+            l=true;
+        }
+
+
+        w->setSurvey(surveyname);
+        w->setSpecies(species);
+        w->setTransition(transition);
+        w->setCallingVtkWindow(myCallingVtkWindow);
+        w->setSelectedSurveyMap(selectedSurvey);
+        w->importFitsImage(currentPath, elementsOnDb,ui->l_lineEdit->text(),ui->b_lineEdit->text(),ui->r_lineEdit->text(), ui->dbLineEdit->text(), ui->dlLineEdit->text(), l);
+    }
+    else
+    {
+        myCallingVtkWindow->setSelectedCubeVelocityUnit(velocityUnit);
+        w->setCallingVtkWindow(myCallingVtkWindow);
+        w->importFitsDC(currentPath);
+    }
+}
diff --git a/Code/src/vialacteainitialquery.h b/Code/src/vialacteainitialquery.h
new file mode 100644
index 0000000000000000000000000000000000000000..a9837a3b05d1e97b85ffe820bb8576bbe1ad8e74
--- /dev/null
+++ b/Code/src/vialacteainitialquery.h
@@ -0,0 +1,72 @@
+#ifndef VIALACTEAINITIALQUERY_H
+#define VIALACTEAINITIALQUERY_H
+
+#include <QWidget>
+#include <QNetworkReply>
+#include "xmlparser.h"
+#include "loadingwidget.h"
+#include "vtkwindow_new.h"
+
+namespace Ui {
+class VialacteaInitialQuery;
+}
+
+class VialacteaInitialQuery : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit VialacteaInitialQuery(QString fn="",QWidget *parent = 0);
+    ~VialacteaInitialQuery();
+    void setL(QString l);
+    void setB(QString b);
+    void setR(QString r);
+    void setDeltaRect(QString dl,QString db);
+
+    void setSurveyname(QString s);
+    void setSpecies(QString s){species=s;}
+    void setTransition(QString s);
+    void setCallingVtkWindow(vtkwindow_new *v){myCallingVtkWindow=v;}
+    void setSelectedSurveyMap(QList < QPair<QString, QString> > s){selectedSurvey=s;}
+
+
+private slots:
+    void on_pushButton_clicked();
+    void on_pointRadioButton_toggled(bool checked);
+    void finishedSlot(QNetworkReply* reply);
+    void on_download_completed();
+
+public slots:
+    void on_queryPushButton_clicked();
+   // void cutoutRequest(QUrl url, QList< QMap<QString,QString> > el =QList< QMap<QString,QString> >(), int pos=0);
+    void cutoutRequest(QString url, QList< QMap<QString,QString> > el, int pos);
+    void selectedStartingLayersRequest(QUrl url);
+
+private:
+    Ui::VialacteaInitialQuery *ui;
+    QString surveyname;
+    xmlparser *parser;
+    LoadingWidget *loading;
+    QNetworkAccessManager *nam;
+    QString downloadedFile;
+    QString currentPath;
+    QString outputFile;
+    QString descriptionFromDB;
+    QList< QMap<QString,QString> > elementsOnDb;
+    QString species, transition,velfrom,velto;
+    QMap<QString,QString> transitions;
+    QList < QPair<QString, QString> > selectedSurvey;
+    QString url_prefix;
+
+
+    QWidget *p;
+    QString pubdid;
+    QString vlkbUrl;
+    QString vlkbsearchUrl;
+    QString vlkbcutoutUrl;
+    QString velocityUnit;
+    vtkwindow_new *myCallingVtkWindow;
+    bool isRadius;
+};
+
+#endif // VIALACTEAINITIALQUERY_H
diff --git a/Code/src/vialacteasource.cpp b/Code/src/vialacteasource.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..575825b432731c9e5e67e8433ee7c6c9400d4422
--- /dev/null
+++ b/Code/src/vialacteasource.cpp
@@ -0,0 +1,384 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Fabio Vitello *
+ *  fabio.vitello@oact.inaf.it *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#include <cstdlib>
+#include <cstring>
+
+
+#include "vialacteasource.h"
+
+#include "visivoutilsdesktop.h"
+#include "base64.h"
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+
+#include <QDebug>
+#include <boost/array.hpp>
+
+
+
+VialacteaSource::VialacteaSource(std::string name)
+{
+    m_pointsFileName=name;
+    readHeader();
+    readData();
+}
+
+//---------------------------------------------------------------------
+int VialacteaSource::readData( )
+//---------------------------------------------------------------------
+{
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    //	in case of missing data we put 0 as default value
+    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+    int i = 0;
+    int j = 0;
+
+    MISSING_VALUE="missing";
+
+    std::string::size_type index = std::string::npos;
+    std::string fields;
+
+    std::vector<std::string> lineData; //!will contain each row of file
+
+    std::ifstream inFile;
+
+    unsigned long long int  sum=0;
+    std::ios::off_type pos=0;
+
+    inFile.open(m_pointsFileName.c_str());
+
+    if(!inFile)
+        return 1;
+
+
+    std::string tmp = "";
+    bool discardLine=true;
+
+    getline(inFile, tmp); //!read first line
+    tmp = trimDesktop(tmp);
+
+    int indexp = tmp.find('#');
+
+    while(indexp == 0)
+    {
+        getline(inFile, tmp);
+        indexp = tmp.find('#');
+    }
+
+    findAndReplaceDesktop(tmp, '\t', ' ');
+    findAndReplaceDesktop(tmp, '#', ' ');
+    tmp = trimDesktop(tmp);
+
+
+    if(tmp.compare(""))
+        fields=tmp;
+
+    std::string data;
+    std::stringstream ss;
+    ss << fields;
+    int suffix=1;
+    while(!ss.eof()) //!fill m_fieldNames: name of columns
+    {
+        ss >> data;
+
+        if(data.compare(""))
+        {
+            for(int k=0;k< m_fieldNames.size();k++)
+            {
+                if(data==m_fieldNames[k])
+                {
+                    std::cerr<<"Warning. Duplicate column name "<<data;
+                    std::stringstream ksst;
+                    ksst<<suffix;
+                    data=data+"_"+ksst.str();
+                    suffix++;
+                    std::cerr<<" is changed with "<<data<<std::endl;
+                }
+            }
+            m_fieldNames.push_back(data.c_str());
+            data = "";
+        }
+    }
+
+    m_nCols=m_fieldNames.size();
+
+    int nLoad=(int)(10000000/m_nCols);  //!nLoad is the number of rows that I will read
+
+    try
+    {
+        matrix = new std::string*[m_nCols];
+    }
+    catch (std::bad_alloc e)
+    {
+        return 1;
+    }
+
+    for(i = 0; i < m_nCols; i++)
+
+    {
+        try
+        {
+            matrix[i] = new std::string[m_nRows];
+        }
+        catch (std::bad_alloc e)
+        {
+            return 1;
+        }
+    }
+
+    while(!inFile.eof())  //!read file content
+    {
+        tmp = "";
+
+        getline(inFile, tmp); //!read row
+        findAndReplaceDesktop(tmp, '\t', ' ');
+        tmp = trimDesktop(tmp);
+
+        indexp = tmp.find('#');
+
+        if(indexp == 0)
+            continue;
+
+        if(tmp.compare(""))
+            lineData.push_back(tmp);  //!fill lineData with the row
+
+
+
+        if( inFile.eof())  //!arrived to maximum allowed
+        {
+            for(i = 0; i <lineData.size(); i++)  //!for each vector element
+            {
+                std::stringstream ss;
+                ss << lineData[i];  //!line extraction
+                for(j = 0; j < m_nCols; j++)
+                {
+                    if(ss.eof())
+                    {
+
+                        matrix[j][i]=MISSING_VALUE;
+
+
+                        continue;
+                    }
+
+                    std::string data;
+                    ss>>data;
+
+                    char *cstr;
+                    cstr = new char [data.size()+1];
+                    strcpy (cstr, data.c_str());
+
+                    matrix[j][i]= data.c_str();
+                    // matrix[j].push_back(data.c_str());
+
+                    if(data.length()==0)
+                    {
+                        std::cerr<<"WARNIG. Missing value "<<data<<" is found. Replaced with VALUE="<<MISSING_VALUE <<std::endl;
+                        matrix[j][i]=MISSING_VALUE;
+
+                    }
+                }
+
+            }
+
+            sum=sum+lineData.size();
+            lineData.clear();
+        }
+
+
+
+    }
+   /*
+    if(matrix)  //! delete matrix
+    {
+        for(int i = 0; i < m_nCols; i++)
+        {
+            if(matrix[i])
+            {
+                delete [] matrix[i];
+            }
+        }
+
+        delete [] matrix;
+
+    }
+*/
+
+    inFile.close();
+
+
+
+
+
+    //makeHeader(sum,m_pointsBinaryName,m_fieldNames,m_cellSize,m_cellComp,m_volumeOrTable); //!create header file (in utility)
+
+    return 1;
+}
+
+void VialacteaSource::computeHistogram()
+{
+    // Calculating bin number
+    m_binNumber=-1;
+    if (m_nRows/30 < 1000 ) m_binNumber=(int)m_nRows/30;
+    else m_binNumber=1000;
+
+
+    try{
+        m_histogramArray = new int*[m_nCols];
+        m_histogramValueArray = new float*[m_nCols];
+    }
+    catch(std::bad_alloc & e)
+    {
+        m_histogramArray=NULL;
+        m_histogramValueArray=NULL;
+    }
+    if(m_histogramArray==NULL)
+        qDebug() << "int ** m_histogramArray allocation:  out of memory";
+
+    for(int i=0; i<m_nCols; ++i)
+    {
+        try{
+            m_histogramArray[i]=new int[m_binNumber];
+            m_histogramValueArray[i]=new float[m_binNumber];
+        }
+        catch(std::bad_alloc & e)
+        {
+            m_histogramArray[i]=NULL;
+            m_histogramValueArray[i]=NULL;
+        }
+        if(m_histogramArray[i]==NULL)
+            qDebug() << "int * m_histogramArray allocation:  out of memory";
+    }
+    try{
+        m_rangeArray = new float*[m_nCols];
+    }
+    catch(std::bad_alloc & e)
+    {
+        m_rangeArray=NULL;
+    }
+    if(m_rangeArray==NULL)
+        qDebug() << "float ** m_rangeArray allocation:  out of memory";
+    for(int i=0; i<m_nCols; ++i)
+    {
+        try{
+            m_rangeArray[i]=new float[3];
+        }
+        catch(std::bad_alloc & e)
+        {
+            m_rangeArray[i]=NULL;
+        }
+        if (m_rangeArray[i]==NULL)
+            qDebug() << "float * m_rangeArray allocation:  out of memory";
+    }
+
+    //da qui
+/*    float  binInterval=((float)(maxMaxValue-minMinValue))/(float) m_binNumber;
+
+    counterCols=-1;
+    offset=minMinValue;
+    if(range) offset=minRange;
+    for(iter = colNumberSet.begin(); iter != end; iter++)
+    {
+        unsigned long long int totEle=nOfEle;
+        unsigned long long int fromRow, toRow, startCounter=0;
+        unsigned int colList[1];
+        colList[0]=*iter;
+        counterCols++;
+        totEle=nOfEle;
+        startCounter=0;
+        int histoIndex;
+        while(totEle!=0)
+        {
+            fromRow=startCounter;
+            toRow=fromRow+maxEle-1;
+            if(toRow>totRows-1)toRow=totRows-1;
+            m_tables[0]->getColumn(colList, m_nOfCol, fromRow, toRow, m_fArray);
+            for(unsigned int j=0;j<(toRow-fromRow+1);j++)
+            {
+                if(range && (m_fArray[0][j]>maxRange || m_fArray[0][j]<minRange)) continue;
+                if(binInterval==0)
+                    histoIndex=0;
+                else
+                    histoIndex=(int) ((m_fArray[0][j]-offset)/binInterval);
+                if(histoIndex>numberOfBin)
+                    histoIndex=numberOfBin;
+                m_histogram[counterCols][histoIndex]++;
+            }
+            startCounter=toRow+1;
+            totEle=totEle-(toRow-fromRow+1);
+        }
+        m_histogram[counterCols][numberOfBin-1]=m_histogram[counterCols][numberOfBin-1]+m_histogram[counterCols][numberOfBin];
+    }
+*/
+
+}
+
+//---------------------------------------------------------------------
+int VialacteaSource::readHeader()
+//---------------------------------------------------------------------
+{
+
+    m_fieldNames.clear();
+
+    int i = 0;
+    int rows=-1;
+
+    std::vector<std::string> counting;
+    std::string::size_type index = std::string::npos;
+
+    std::ifstream inFile;
+    inFile.open(m_pointsFileName.c_str());  if(!inFile) return 1;
+
+    std::string tmp = "";
+
+    while(!inFile.eof())  //! count the number of row: rows variable is the toltal numer (excluding the first one
+    {
+        getline(inFile, tmp);
+
+        index = tmp.find('#');
+
+        if(index != std::string::npos)
+        {
+            tmp.erase(index);
+            tmp = trimRightDesktop(tmp);
+        }
+
+        if(tmp.compare(""))
+        {
+
+            counting.push_back(tmp);
+            ++rows;
+            counting.clear();
+        }
+    }
+    m_nRows=rows;
+    //std::clog<<"rowsheader="<<m_nRows<<endl;
+    //!tmp now is the line containing the field names
+
+    inFile.close();
+
+    if(rows==0)
+        return 1;
+
+    return 0;
+}
+
diff --git a/Code/src/vialacteasource.h b/Code/src/vialacteasource.h
new file mode 100644
index 0000000000000000000000000000000000000000..de17ac113d421c842fd7a9a27caabdc17e1316b4
--- /dev/null
+++ b/Code/src/vialacteasource.h
@@ -0,0 +1,48 @@
+#ifndef VIALACTEASOURCE_H
+#define VIALACTEASOURCE_H
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+class VialacteaSource
+{
+public:
+    VialacteaSource(std::string name);
+    int readHeader();
+    int readData();
+    int getNumberOfColumns(){return m_nCols;}
+    int getNumberOfRows(){return m_nRows;}
+    int getNumberOfBins(){return m_binNumber;}
+    void computeHistogram();
+
+    int ** getHistogramArray(){return m_histogramArray;}
+    float ** getRangeArray(){return m_rangeArray;}
+    float ** getHistogramValueArray(){return m_histogramValueArray;}
+
+    std::string** getData(){return matrix;}
+    //  std::vector< std::vector<std::string> > getData(){return matrix;}
+
+    std::vector<std::string> getColumnsNames(){return m_fieldNames;}
+
+
+private:
+    std::string m_pointsFileName;
+    std::string m_pointsBinaryName;
+    double m_cellSize[3], m_cellComp[3];
+    std::string m_volumeOrTable;
+    std::string **matrix;
+    //std::vector< std::vector<std::string> > matrix; // default construction
+
+    std::vector<std::string> m_fieldNames;  //!column List
+    int m_nCols;
+    unsigned long long int m_nRows;
+    int m_binNumber; //! Each table has its bin number.
+    std::string MISSING_VALUE; //! a negative value used in case of missing data
+    int ** m_histogramArray;
+    float ** m_rangeArray;
+    float ** m_histogramValueArray;
+
+};
+
+#endif // VIALACTEASOURCE_H
diff --git a/Code/src/vialacteastringdict.cpp b/Code/src/vialacteastringdict.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a20b04e7b6e8833ef258c90de0fbfb4be8a73dc1
--- /dev/null
+++ b/Code/src/vialacteastringdict.cpp
@@ -0,0 +1,18 @@
+#include "vialacteastringdict.h"
+#include "vlkbquery.h"
+#include <QUrl>
+#include <QNetworkRequest>
+#include <QDomNodeList>
+
+VialacteaStringDict::VialacteaStringDict()
+{
+
+
+
+}
+
+VialacteaStringDict::~VialacteaStringDict()
+{
+
+}
+
diff --git a/Code/src/vialacteastringdict.h b/Code/src/vialacteastringdict.h
new file mode 100644
index 0000000000000000000000000000000000000000..d8290123191a95d7db718febd468622b47f1446e
--- /dev/null
+++ b/Code/src/vialacteastringdict.h
@@ -0,0 +1,34 @@
+#ifndef VIALACTEASTRINGDICT_H
+#define VIALACTEASTRINGDICT_H
+
+#include <QObject>
+#include <QHash>
+#include <QWidget>
+
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+
+
+class VialacteaStringDict
+{
+
+public:
+    VialacteaStringDict();
+    ~VialacteaStringDict();
+private:
+    QHash<QString, QString> tableUtypeStringDict;
+    QHash<QString, QString> tableDescStringDict;
+
+    QHash<QString, QString> colUtypeStringDict;
+    QHash<QString, QString> colDescStringDict;
+
+    QNetworkAccessManager *manager;
+
+private slots:
+    void availReplyFinished (QNetworkReply *reply);
+    void tableReplyFinished (QNetworkReply *reply);
+
+
+};
+
+#endif // VIALACTEASTRINGDICT_H
diff --git a/Code/src/vialacteastringdictwidget.cpp b/Code/src/vialacteastringdictwidget.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d9a811a656b709d37781aa8d72de33b26f644982
--- /dev/null
+++ b/Code/src/vialacteastringdictwidget.cpp
@@ -0,0 +1,298 @@
+#include "vialacteastringdictwidget.h"
+#include "ui_vialacteastringdictwidget.h"
+
+#include <QSettings>
+#include <QDir>
+#include <QNetworkReply>
+#include <QMessageBox>
+#include <QDomDocument>
+#include <QAuthenticator>
+#include <QNetworkRequest>
+
+VialacteaStringDictWidget::VialacteaStringDictWidget(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::VialacteaStringDictWidget)
+{
+    ui->setupUi(this);
+
+}
+
+void VialacteaStringDictWidget::buildDict()
+{
+    m_sSettingsFile = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini");
+
+
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+    manager = new QNetworkAccessManager(this);
+
+    connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(availReplyFinished(QNetworkReply*)));
+
+    QNetworkRequest request(QUrl(settings.value("vlkbtableurl", "").toString()+"/availability"));
+//    QNetworkRequest request(QUrl("http://ia2-vialactea.oats.inaf.it/vlkb/availability"));
+
+
+    request.setHeader(QNetworkRequest::ContentTypeHeader,
+                      QStringLiteral("text/html; charset=utf-8"));
+    manager->get(request);
+
+
+
+}
+
+
+
+
+void VialacteaStringDictWidget::availReplyFinished (QNetworkReply *reply)
+{
+
+
+    if(reply->error())
+    {
+
+        QMessageBox::critical(this,"Error", "Error: \n"+reply->errorString());
+
+
+
+    }
+    else
+    {
+        QDomDocument doc;
+        doc.setContent(reply->readAll());
+        QDomNodeList list=doc.elementsByTagName("vosi:available");
+
+        if(list.at(0).toElement().text()=="true")
+        {
+
+            executeQueryTapSchemaTables();
+            executeQueryTapSchemaColumns();
+
+        }
+    }
+
+    reply->deleteLater();
+
+}
+
+void VialacteaStringDictWidget::executeQueryTapSchemaTables()
+{
+
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+
+    QString query="SELECT * FROM TAP_SCHEMA.tables";
+
+    manager = new QNetworkAccessManager(this);
+
+    QByteArray postData;
+
+    postData.append("REQUEST=doQuery&");
+    postData.append("VERSION=1.0&");
+    postData.append("LANG=ADQL&");
+    postData.append("FORMAT=tsv&");
+
+
+    postData.append("QUERY="+QUrl::toPercentEncoding(query));
+
+
+    connect ( manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(onAuthenticationRequestSlot(QNetworkReply*,QAuthenticator*)) );
+
+
+    connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(queryReplyFinishedTapSchemaTables(QNetworkReply*)));
+
+
+    QString url = settings.value("vlkbtableurl", "").toString()+"/sync";
+   // QString url = "http://ia2-vialactea.oats.inaf.it:8080/vlkb/sync";
+
+
+    QNetworkRequest request(url);
+    request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
+
+    manager->post(request,postData);
+
+}
+
+void VialacteaStringDictWidget::executeQueryTapSchemaColumns()
+{
+
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+
+    QString query="SELECT * FROM TAP_SCHEMA.columns";
+
+    manager = new QNetworkAccessManager(this);
+
+    QByteArray postData;
+
+    postData.append("REQUEST=doQuery&");
+    postData.append("VERSION=1.0&");
+    postData.append("LANG=ADQL&");
+    postData.append("FORMAT=tsv&");
+
+
+    postData.append("QUERY="+QUrl::toPercentEncoding(query));
+
+
+    connect ( manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), this, SLOT(onAuthenticationRequestSlot(QNetworkReply*,QAuthenticator*)) );
+
+
+    connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(queryReplyFinishedTapSchemaColumns(QNetworkReply*)));
+
+
+    QString url = settings.value("vlkbtableurl", "").toString()+"/sync";
+   // QString url = "http://ia2-vialactea.oats.inaf.it:8080/vlkb/sync";
+
+    qDebug()<<url;
+    QNetworkRequest request(url);
+    request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
+    manager->post(request,postData);
+
+}
+
+
+void VialacteaStringDictWidget::queryReplyFinishedTapSchemaTables (QNetworkReply *reply)
+{
+    if(reply->error())
+    {
+        QMessageBox::critical(this,"Error", "Error: \n"+reply->errorString());
+    }
+    else
+    {
+        QVariant possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+
+        /* We'll deduct if the redirection is valid in the redirectUrl function */
+        urlRedirectedTo = redirectUrl(possibleRedirectUrl.toUrl(), urlRedirectedTo);
+
+        /* If the URL is not empty, we're being redirected. */
+        if(!urlRedirectedTo.isEmpty())
+        {
+            /* We'll do another request to the redirection url. */
+            manager->get(QNetworkRequest(urlRedirectedTo));
+        }
+        else
+        {
+
+            QByteArray bytes = reply->readLine();
+            QString s_data = QString::fromLatin1(bytes.data());
+            //se inizia per '<' è un xml, se è xml c'e' stato un errore
+            if(QString::compare(s_data.at(0), "<", Qt::CaseInsensitive) == 0)
+            {
+                QMessageBox::critical(this,"Error", "Error: \n"+bytes);
+            }
+            else
+            {
+                QString line_data;
+                while(! reply->atEnd())
+                {
+                     line_data = QString::fromLatin1(reply->readLine().data());
+
+                     QStringList list2=line_data.split('\t');
+
+                     tableUtypeStringDict.insert(list2[3],list2[5].remove(QRegExp("[\n\t\r]")));
+                     tableDescStringDict.insert(list2[3],list2[0].remove(QRegExp("[\n\t\r]")));
+
+
+
+                }
+
+             //   qDebug()<<tableUtypeStringDict.value("vlkb_compactsources.band500um");
+             //   qDebug()<<tableDescStringDict.value("vlkb_compactsources.band500um");
+
+
+
+
+
+
+
+            }
+
+        }
+        /* Clean up. */
+        reply->deleteLater();
+
+    }
+
+
+}
+
+void VialacteaStringDictWidget::queryReplyFinishedTapSchemaColumns (QNetworkReply *reply)
+{
+    if(reply->error())
+    {
+        QMessageBox::critical(this,"Error", "Error: \n"+reply->errorString());
+    }
+    else
+    {
+        QVariant possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+
+        /* We'll deduct if the redirection is valid in the redirectUrl function */
+        urlRedirectedTo = redirectUrl(possibleRedirectUrl.toUrl(), urlRedirectedTo);
+
+        /* If the URL is not empty, we're being redirected. */
+        if(!urlRedirectedTo.isEmpty())
+        {
+            /* We'll do another request to the redirection url. */
+            manager->get(QNetworkRequest(urlRedirectedTo));
+        }
+        else
+        {
+
+            QByteArray bytes = reply->readLine();
+            QString s_data = QString::fromLatin1(bytes.data());
+            //se inizia per '<' è un xml, se è xml c'e' stato un errore
+            if(QString::compare(s_data.at(0), "<", Qt::CaseInsensitive) == 0)
+            {
+                QMessageBox::critical(this,"Error", "Error: \n"+bytes);
+            }
+            else
+            {
+                QString line_data;
+                while(! reply->atEnd())
+                {
+                     line_data = QString::fromLatin1(reply->readLine().data());
+
+                     QStringList list2=line_data.split('\t');
+
+
+                     colUtypeStringDict.insert(list2[8] +"."+list2[1],list2[11].remove(QRegExp("[\n\t\r]")));
+                     colDescStringDict.insert(list2[8] +"."+list2[1],list2[3].remove(QRegExp("[\n\t\r]")));
+
+                }
+
+            //     qDebug()<<"AUUUUUUUUUUUUUU"<<colUtypeStringDict.value("vlkb_filaments.branches.contour");
+            //     qDebug()<<"AUUUUUUUUUUUUUU"<<colDescStringDict.value("vlkb_filaments.branches.contour");
+
+
+            }
+
+        }
+        reply->deleteLater();
+
+    }
+
+
+}
+
+
+QUrl VialacteaStringDictWidget::redirectUrl(const QUrl& possibleRedirectUrl,   const QUrl& oldRedirectUrl) const {
+    QUrl redirectUrl;
+
+    if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl) {
+        redirectUrl = possibleRedirectUrl;
+    }
+
+    return redirectUrl;
+}
+
+
+void VialacteaStringDictWidget::onAuthenticationRequestSlot(QNetworkReply *aReply, QAuthenticator *aAuthenticator)
+{
+    aAuthenticator->setUser("vialactea");
+    aAuthenticator->setPassword("ia2vlkb");
+}
+
+
+
+
+
+VialacteaStringDictWidget::~VialacteaStringDictWidget()
+{
+    delete ui;
+}
diff --git a/Code/src/vialacteastringdictwidget.h b/Code/src/vialacteastringdictwidget.h
new file mode 100644
index 0000000000000000000000000000000000000000..79cf164fa3490f23f022eb351ac521c47c51bf69
--- /dev/null
+++ b/Code/src/vialacteastringdictwidget.h
@@ -0,0 +1,53 @@
+#ifndef VIALACTEASTRINGDICTWIDGET_H
+#define VIALACTEASTRINGDICTWIDGET_H
+
+#include <QHash>
+#include <QWidget>
+
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+
+namespace Ui {
+class VialacteaStringDictWidget;
+}
+
+class VialacteaStringDictWidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit VialacteaStringDictWidget(QWidget *parent = 0);
+    void buildDict();
+    ~VialacteaStringDictWidget();
+    QHash<QString, QString> getColUtypeStringDict(){return colUtypeStringDict;}
+    QHash<QString, QString> getColDescStringDict(){return colDescStringDict;}
+    QHash<QString, QString> getTableDescStringDict(){return tableDescStringDict;}
+    QHash<QString, QString> getTableUtypeStringDict(){return tableUtypeStringDict;}
+    QHash<QString, QString> tableUtypeStringDict;
+    QHash<QString, QString> tableDescStringDict;
+
+    QHash<QString, QString> colUtypeStringDict;
+    QHash<QString, QString> colDescStringDict;
+
+private:
+    Ui::VialacteaStringDictWidget *ui;
+
+    QUrl urlRedirectedTo;
+    QNetworkAccessManager *manager;
+    QString m_sSettingsFile;
+
+private slots:
+    void availReplyFinished (QNetworkReply *reply);
+    void  executeQueryTapSchemaTables();
+    void executeQueryTapSchemaColumns();
+    void onAuthenticationRequestSlot(QNetworkReply *aReply, QAuthenticator *aAuthenticator);
+    void queryReplyFinishedTapSchemaTables (QNetworkReply *reply);
+    void queryReplyFinishedTapSchemaColumns(QNetworkReply *reply);
+    QUrl redirectUrl(const QUrl& possibleRedirectUrl, const QUrl& oldRedirectUrl) const;
+
+
+
+
+};
+
+#endif // VIALACTEASTRINGDICTWIDGET_H
diff --git a/Code/src/vialacteastringdictwidget.ui b/Code/src/vialacteastringdictwidget.ui
new file mode 100644
index 0000000000000000000000000000000000000000..e395cc8006f89fd2c34fc9931d0f3ca88ea7ff10
--- /dev/null
+++ b/Code/src/vialacteastringdictwidget.ui
@@ -0,0 +1,21 @@
+<ui version="4.0">
+ <author/>
+ <comment/>
+ <exportmacro/>
+ <class>VialacteaStringDictWidget</class>
+ <widget class="QWidget" name="VialacteaStringDictWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+ </widget>
+ <pixmapfunction/>
+ <connections/>
+</ui>
diff --git a/Code/src/viewselectedsourcedataset.cpp b/Code/src/viewselectedsourcedataset.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..900be379306f55e493bcb303b881c1faa7624d61
--- /dev/null
+++ b/Code/src/viewselectedsourcedataset.cpp
@@ -0,0 +1,48 @@
+#include "viewselectedsourcedataset.h"
+#include "ui_viewselectedsourcedataset.h"
+#include <QDebug>
+
+ViewSelectedSourceDataset::ViewSelectedSourceDataset(vtkwindow_new *v, QHash <int,QString> selFields, QList<QListWidgetItem*> selSources, QWidget *parent) :
+    QWidget(parent),ui(new Ui::ViewSelectedSourceDataset)
+{
+    ui->setupUi(this);
+    selectedFields=selFields;
+    selectedSources=selSources;
+    vtkwin=v;
+
+    table=vtkwin->getEllipseList().value(selectedSources.at(0)->text())->getTable();
+
+    QHeaderView* header = ui->datasetTableWidget->horizontalHeader();
+    header->setVisible(true);
+
+    ui->datasetTableWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
+
+    ui->datasetTableWidget->setColumnCount( selFields.size() );
+
+    for (int j=0;j<selFields.size();j++)
+    {
+        ui->datasetTableWidget->setHorizontalHeaderItem(j, new QTableWidgetItem(selFields.values().at(j)) );
+    }
+
+    int row;
+    for (int i=0;i<selSources.size();i++)
+    {
+        row = vtkwin->getEllipseList().value(selectedSources.at(i)->text())->getRowInTable();
+        ui->datasetTableWidget->insertRow(i);
+
+        for (int j=0;j<selFields.size();j++)
+        {
+            QTableWidgetItem *item=new QTableWidgetItem (QString::fromStdString(table->getTableData()[selFields.keys()[j]][row]) );
+            ui->datasetTableWidget->setItem(i,j,item);
+        }
+    }
+
+    ui->datasetTableWidget->resizeColumnsToContents();
+
+
+}
+
+ViewSelectedSourceDataset::~ViewSelectedSourceDataset()
+{
+    delete ui;
+}
diff --git a/Code/src/viewselectedsourcedataset.h b/Code/src/viewselectedsourcedataset.h
new file mode 100644
index 0000000000000000000000000000000000000000..bbfe092a5ec4e81a6221fa2e06d5eefe0378df9b
--- /dev/null
+++ b/Code/src/viewselectedsourcedataset.h
@@ -0,0 +1,28 @@
+#ifndef VIEWSELECTEDSOURCEDATASET_H
+#define VIEWSELECTEDSOURCEDATASET_H
+
+#include <QWidget>
+#include <QListWidgetItem>
+#include "vstabledesktop.h"
+#include "vtkwindow_new.h"
+namespace Ui {
+class ViewSelectedSourceDataset;
+}
+
+class ViewSelectedSourceDataset : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit ViewSelectedSourceDataset(vtkwindow_new *v, QHash <int,QString> selFields, QList<QListWidgetItem*> selSources, QWidget *parent = 0);
+    ~ViewSelectedSourceDataset();
+
+private:
+    Ui::ViewSelectedSourceDataset *ui;
+    QHash <int,QString> selectedFields;
+     QList<QListWidgetItem*> selectedSources;
+     VSTableDesktop *table;
+     vtkwindow_new *vtkwin;
+};
+
+#endif // VIEWSELECTEDSOURCEDATASET_H
diff --git a/Code/src/visivofilterdesktop.cpp b/Code/src/visivofilterdesktop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..14967ea9aa8f1bfed5cb5ba4defef4788695709a
--- /dev/null
+++ b/Code/src/visivofilterdesktop.cpp
@@ -0,0 +1,133 @@
+#include "visivofilterdesktop.h"
+#include <QDebug>
+#include "singleton.h"
+#include "mainwindow.h"
+extern "C" {
+#include "visivo.h"
+}
+
+VisIVOFilterDesktop::VisIVOFilterDesktop()
+{
+}
+
+void VisIVOFilterDesktop::runFilter(int index)
+{
+
+    switch (index) {
+    //add identifier
+    case 0:
+        addIdentifier();
+        break;
+        //Append
+    case 1:
+        break;
+        //Cartesian 2 Polar
+    case 2:
+        break;
+        //cut
+    case 3:
+        break;
+        //Decimator
+    case 4:
+        break;
+        //Extract List
+    case 5:
+        break;
+        //Extraction
+    case 6:
+        break;
+        //Grid 2 Point
+    case 7:
+        break;
+        //Include
+    case 8:
+        break;
+        //Math Operation
+    case 9:
+        break;
+        //Merge
+    case 10:
+        break;
+        //Module
+    case 11:
+        break;
+        //Multi Resolution
+    case 12:
+        break;
+        //Point distribute
+    case 13:
+        break;
+        //Point Property
+    case 14:
+        break;
+        //Randomizer
+    case 15:
+        break;
+        //Scalar distribution
+    case 16:
+        break;
+        //Select Columns
+    case 17:
+        break;
+        //Select Rows
+    case 18:
+        break;
+        //Sigma Contours
+    case 19:
+        break;
+        //Split tables
+    case 20:
+        break;
+        //Write VO Table
+    case 21:
+        break;
+    default:
+        break;
+    }
+
+
+}
+
+VisIVOFilterDesktop::~VisIVOFilterDesktop()
+{
+
+}
+
+
+
+
+
+
+void VisIVOFilterDesktop::addIdentifier()
+{
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+
+    QModelIndex  selectedItemIndex  = w->ui->treeView->selectionModel()->selectedIndexes()[0];
+
+
+    char *filepath = new char[w->selectedFile.toStdString().length() + 1];
+    std::strcpy(filepath,w->selectedFile.toStdString().c_str());
+
+    char *colname = new char[w->ui->addIdNewColName->text().toStdString().length() + 1];
+    std::strcpy(colname,w->ui->addIdNewColName->text().toStdString().c_str());
+
+    char *startnumber = new char[w->ui->addIdStartNumber->text().toStdString().length() + 1];
+    std::strcpy(startnumber,w->ui->addIdStartNumber->text().toStdString().c_str());
+
+    VisIVOFilter env;
+    int init= VF_Init(&env);
+    VF_SetAtt(&env, VF_SET_OPERATION,"addId");
+    VF_SetAtt(&env, VF_SET_ADDIDSTART,startnumber);
+    VF_SetAtt(&env, VF_SET_OUTCOL,colname);
+    VF_SetAtt(&env, VF_SET_FILEVBT,filepath);
+    VF_Filter(&env);
+
+
+    w->m_VisIVOTreeModel->getTable(selectedItemIndex)->readHeader();
+    w->m_VisIVOTreeModel->getTable(selectedItemIndex)->readStatistics();
+
+    w->itemSelected();
+    w->ui->tabWidget->setCurrentWidget(w->ui->tabObjectTree);
+
+
+}
diff --git a/Code/src/visivofilterdesktop.h b/Code/src/visivofilterdesktop.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b49ca3a3572067867350381629f3b77a5daef82
--- /dev/null
+++ b/Code/src/visivofilterdesktop.h
@@ -0,0 +1,16 @@
+#ifndef VISIVOFILTERDESKTOP_H
+#define VISIVOFILTERDESKTOP_H
+
+
+class VisIVOFilterDesktop
+{
+public:
+    VisIVOFilterDesktop();
+    static void runFilter(int index);
+    ~VisIVOFilterDesktop();
+private:
+    static void addIdentifier();
+
+};
+
+#endif // VISIVOFILTERDESKTOP_H
diff --git a/Code/src/visivoimporterdesktop.cpp b/Code/src/visivoimporterdesktop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..39c6deaeca9759551b4e1e35df51d4abdc9e8dfe
--- /dev/null
+++ b/Code/src/visivoimporterdesktop.cpp
@@ -0,0 +1,1632 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Alessandro Costa                                *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include "visivoimporterdesktop.h"
+#include <//qDebug>
+#include <QDialog>
+#include <QFileDialog>
+#include <QTreeView>
+#include <QFile>
+#include <QDir>
+#include <QProcess>
+#include <QFileInfo>
+#include "vtkwindow_new.h"
+#include "mainwindow.h"
+#include "treemodel.h"
+#include "vtkXMLPolyDataReader.h"
+#include "vtkXMLImageDataReader.h"
+#include "vtkXMLPolyDataReader.h"
+#include "vtkSmartPointer.h"
+#include "vtkFixedPointVolumeRayCastMapper.h"
+#include "vtkColorTransferFunction.h"
+#include "vtkVolume.h"
+#include "vtkPiecewiseFunction.h"
+#include "vtkVolumeProperty.h"
+#include "singleton.h"
+#include "vtkfitsreader.h"
+#include "vtkImageViewer2.h"
+#include "vialactea.h"
+#include "astroutils.h"
+#include "base64.h"
+
+#include "vialacteasource.h"
+#include "sed.h"
+#include "sednode.h"
+#include "vlkbquery.h"
+#include <math.h>
+
+#include "sedvisualizerplot.h"
+
+#include <QtConcurrent>
+
+
+extern "C" {
+#include "visivo.h"
+}
+
+
+VisIVOImporterDesktop::VisIVOImporterDesktop(QString f, TreeModel * m, bool isBandMergedCatalogue, vtkwindow_new *v, QString wavelen)
+{
+
+    VialacteaSource *vialactea_source= new VialacteaSource(f.toStdString());
+
+    m_VisIVOTable = new VSTableDesktop();
+
+    m_VisIVOTable->setNumberOfColumns(vialactea_source->getNumberOfColumns());
+    m_VisIVOTable->setNumberOfRows(vialactea_source->getNumberOfRows());
+    m_VisIVOTable->setColsNames(vialactea_source->getColumnsNames());
+
+    m_VisIVOTable->setWavelength(wavelen.toInt());
+
+    QFileInfo infoFile = QFileInfo(f);
+
+    queue = &Singleton<OperationQueue>::Instance();
+    operation_queue_row=queue->addOperation("Importing "+infoFile.fileName());
+    // queue->show();
+
+
+    m_VisIVOTable->setName(infoFile.fileName().toStdString());
+    m_VisIVOTable->setTableData(vialactea_source->getData());
+
+    vtkwin=v;
+
+    QString m_sSettingsFile = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini");
+
+
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+    settings.setValue("vlkburl","http://vialactea:ia2vlkb@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-1.0.2/");
+    //    settings.setValue("vlkburl","http://vialactea:ia2vlkb@ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-0.22.0/");
+    float  flux22Multiplier= 1;
+
+    if(!isBandMergedCatalogue)
+    {
+        if(wavelen.compare("all")==0)
+            vtkwin->addSourcesFromBM(m_VisIVOTable);
+        else
+            vtkwin->addSources(m_VisIVOTable);
+    }
+    else
+    {
+        firstSEDNode=true;
+        double distances;
+
+//        for(int  i = 0; i < 3; i++)
+
+        for(int  i = 0; i < m_VisIVOTable->getNumberOfRows()/2; i++)
+        {
+
+            firstSEDNode=true;
+
+
+            SED *sed=new SED();
+
+            SEDNode *node;
+            SEDNode *tmp_node;
+
+            QString tmp;
+            double flux,e_flux;
+
+            double minAxes, maxAxes, angle, arcpix;
+
+            double glon, glat;
+            double *coord= new double[3];
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation1100")][i]);
+            //qDebug()<<"tmp 1100: "<<tmp;
+            if(tmp!="missing")
+            {
+                firstSEDNode=false;
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux1100")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux1100")][i].c_str());
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp);
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setWavelength(1100);
+                    sed->updateMaxFlux(node->getFlux());
+                    sed->setRootNode(node);
+
+                    glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon1100")][i].c_str());
+                    glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat1100")][i].c_str());
+                    node->setSky( glon , glat );
+                    AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma1100")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb1100")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa1100")][i].c_str());
+
+
+
+
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                }
+            }
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation870")][i]);
+            //qDebug()<<"tmp 870: "<<tmp;
+            if(tmp.compare("missing")!=0)
+            {
+                //qDebug()<<"iun";
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux870")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux870")][i].c_str());
+                tmp_node=node;
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp);
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setWavelength(870);
+                    sed->updateMaxFlux(node->getFlux());
+
+
+
+                    if(!firstSEDNode)
+                    {
+
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                    {
+                        firstSEDNode=false;
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<<" è radice";
+                    }
+
+
+                    glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon870")][i].c_str());
+                    glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat870")][i].c_str());
+                    node->setSky( glon , glat );
+
+                    AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+                    node->setXY( coord[0] , coord[1] );
+
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma870")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb870")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa870")][i].c_str());
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+
+                    ////qDebug()<<"870_ flux:"<<flux;
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+                }
+
+            }
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation500")][i]);
+            //qDebug()<<"tmp 500: "<<tmp;
+            if(tmp!="missing")
+            {
+                //firstSEDNode=false;
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux500")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux500")][i].c_str());
+                tmp_node=node;
+
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp);
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setWavelength(500);
+                    sed->updateMaxFlux(node->getFlux());
+                    //sed->setRootNode(node);
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+                    glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon500")][i].c_str());
+                    glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat500")][i].c_str());
+                    node->setSky( glon , glat );
+                    AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma500")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb500")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa500")][i].c_str());
+
+
+                    ////qDebug()<<"500_  flux:"<<flux;
+
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+                }
+            }
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation350")][i]);
+            //qDebug()<<"tmp 350: "<<tmp;
+            if(tmp!="missing")
+            {
+
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux350")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux350")][i].c_str());
+                tmp_node=node;
+
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp);
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setWavelength(350);
+                    sed->updateMaxFlux(node->getFlux());
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+                    glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon350")][i].c_str());
+                    glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat350")][i].c_str());
+                    node->setSky( glon , glat );
+
+                    AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+                    node->setXY( coord[0] , coord[1] );
+
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma350")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb350")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa350")][i].c_str());
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+                    ////qDebug()<<"350_  flux:"<<flux;
+                }
+            }
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation250")][i]);
+            //qDebug()<<"tmp 250: "<<tmp;
+            if(tmp!="missing")
+            {
+
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux250")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux250")][i].c_str());
+                tmp_node=node;
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp);
+
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setWavelength(250);
+                    sed->updateMaxFlux(node->getFlux());
+                    sed->set250node();
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+                    glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon250")][i].c_str());
+                    glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat250")][i].c_str());
+                    node->setSky( glon , glat );
+                    AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+                    node->setXY( coord[0] , coord[1] );
+
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma250")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb250")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa250")][i].c_str());
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+                    ////qDebug()<<"250_  flux:"<<flux;
+                }
+            }
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation160")][i]);
+            //qDebug()<<"tmp 160: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux160")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux160")][i].c_str());
+
+                tmp_node=node;
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp);
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setWavelength(160);
+
+                    sed->updateMaxFlux(node->getFlux());
+
+                    glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon160")][i].c_str());
+                    glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat160")][i].c_str());
+                    node->setSky( glon , glat );
+                    AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+                    node->setXY( coord[0] , coord[1] );
+
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma160")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb160")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa160")][i].c_str());
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+                    ////qDebug()<<"160_  flux:"<<flux;
+                }
+
+            }
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation70")][i]);
+            //qDebug()<<"tmp 70: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux70")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux70")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon70")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat70")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                tmp_node=node;
+
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp);
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma70")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb70")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa70")][i].c_str());
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(70);
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"70_  flux:"<<flux;
+
+                }
+
+            }
+
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation24")][i]);
+            //qDebug()<<"tmp 24: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux24")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux24")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon24")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat24")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                tmp_node=node;
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp);
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma24")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb24")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa24")][i].c_str());
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(24);
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"24_  flux:"<<flux;
+                }
+
+            }
+
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation22")][i]);
+            //qDebug()<<"tmp 22: "<<tmp;
+            if(tmp!="missing")
+            {
+
+                flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux22")][i].c_str());
+                //flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux34")][i].c_str());
+                //e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux34")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux22")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon22")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat22")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                tmp_node=node;
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp);
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma22")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb22")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa22")][i].c_str());
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(22);
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"22_  flux:"<<flux;
+                }
+            }
+
+
+
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation21")][i]);
+            //qDebug()<<"tmp 21um: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux21_e")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux21_e")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon21")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat21")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                tmp_node=node;
+
+                ////qDebug()<<"flux_e "<<flux;
+                if (flux>0)
+                {
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_e");
+
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma21")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb21")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa21")][i].c_str());
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(21);
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"21_  flux:"<<flux;
+                }
+
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux21_d")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux21_d")][i].c_str());
+
+                ////qDebug()<<"flux_e "<<flux;
+
+                tmp_node=node;
+                if (flux>0)
+                {
+
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_d");
+
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(14.7);
+
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"14.7_  flux:"<<flux;
+                }
+            }
+
+
+            //12.1um
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation21")][i]);
+            //qDebug()<<"tmp 12.1: "<<tmp;
+
+            if(tmp!="missing")
+            {
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux21_c")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux21_c")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon21")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat21")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                ////qDebug()<<"flux_c "<<flux;
+
+                tmp_node=node;
+                if (flux>0)
+                {
+
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_c");
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(12.1);
+                    if(!firstSEDNode)
+                    {
+
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"12.1_  flux:"<<flux;
+                }
+            }
+
+
+            //12um
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation22")][i]);
+            //qDebug()<<"tmp 12: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux12")][i].c_str());
+                // flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux46")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux12")][i].c_str());
+                // e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux46")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon22")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat22")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                tmp_node=node;
+                if (flux>0)
+                {
+
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_12um");
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma22")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb22")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa22")][i].c_str());
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(12);
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"12_  flux:"<<flux;
+                }
+
+            }
+
+
+            //8.3um
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation21")][i]);
+            //qDebug()<<"tmp 8.3: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux21_a")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux21_a")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon21")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat21")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                ////qDebug()<<"flux_a "<<flux;
+
+                tmp_node=node;
+                if (flux>0)
+                {
+
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_a");
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma21")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb21")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa21")][i].c_str());
+
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(8.3);
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"8.3_  flux:"<<flux;
+                }
+            }
+
+            //4.6um
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation22")][i]);
+            //qDebug()<<"tmp 4.6: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux46")][i].c_str());
+                //flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux12")][i].c_str());
+                //  e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux12")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux46")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon22")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat22")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                tmp_node=node;
+                if (flux>0)
+                {
+
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_4.6um");
+
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma22")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb22")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa22")][i].c_str());
+
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(4.6);
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"4.6_  flux:"<<flux;
+                }
+
+            }
+
+            //3.4um
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation22")][i]);
+            //qDebug()<<"tmp 3.4: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux34")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux34")][i].c_str());
+                // flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux22")][i].c_str());
+                // e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("err_flux22")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon22")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat22")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+
+                tmp_node=node;
+                if (flux>0)
+                {
+
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_3.4um");
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma22")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb22")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa22")][i].c_str());
+
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(3.4);
+
+
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"3.4_  flux:"<<flux;
+                }
+
+            }
+
+            //2.2um
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation22")][i]);
+            //qDebug()<<"tmp 2.2: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux2M_K")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux2M_K")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon22")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat22")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                tmp_node=node;
+                if (flux>0)
+                {
+
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_2.2um");
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma22")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb22")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa22")][i].c_str());
+
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(2.2);
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"2.2_  flux:"<<flux;
+                }
+
+            }
+
+
+            //1.65um
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation22")][i]);
+            //qDebug()<<"tmp 1.65: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux2M_H")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux2M_H")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon22")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat22")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                tmp_node=node;
+                if (flux>0)
+                {
+
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_1.65um");
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma22")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb22")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa22")][i].c_str());
+
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(1.65);
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"1.65_  flux:"<<flux;
+                }
+
+            }
+
+
+            //1.25um
+            tmp=QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("designation22")][i]);
+            //qDebug()<<"tmp 1.25: "<<tmp;
+
+            if(tmp!="missing")
+            {
+
+                flux= flux22Multiplier*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux2M_J")][i].c_str());
+                e_flux= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flux2M_J")][i].c_str());
+                glon=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon22")][i].c_str());
+                glat=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat22")][i].c_str());
+                AstroUtils().sky2xy(vtkwin->filenameWithPath,glon,glat,coord);
+
+                tmp_node=node;
+                if (flux>0)
+                {
+
+                    node=new SEDNode();
+                    node->setDesignation(tmp+"_1.25um");
+                    node->setFlux(flux);
+                    node->setErrFlux(e_flux);
+                    node->setSky( glon , glat );
+                    node->setXY( coord[0] , coord[1] );
+                    minAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhma22")][i].c_str());
+                    maxAxes=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("fwhmb22")][i].c_str());
+                    angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("pa22")][i].c_str());
+
+                    node->setEllipse(minAxes,maxAxes,angle,coord[2]);
+                    node->setWavelength(1.25);
+                    if(!firstSEDNode)
+                    {
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<node->getDesignation()<< " ha come padre "<<tmp_node->getDesignation();
+                        //qDebug ()<<node->getWavelength()<<" Il nodo "<<tmp_node->getDesignation()<< " ha come figlio "<<node->getDesignation();
+                        //qDebug ()<<" *****";
+                        node->setParent(tmp_node);
+                        tmp_node->setChild(node);
+                    }
+                    else
+                        firstSEDNode=false;
+
+
+
+
+                    if(!sed->hasRoot())
+                        sed->setRootNode(node);
+
+                    ////qDebug()<<"1.25_  flux:"<<flux;
+                }
+
+            }
+
+
+            sedlist.push_back(sed);
+            distances= 1000*atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("distance")][i].c_str());
+
+
+
+        }
+
+
+
+
+        ViaLactea *vialactealWin = &Singleton<ViaLactea>::Instance();
+        SEDVisualizerPlot *sedv= new SEDVisualizerPlot(sedlist,vtkwin,vialactealWin);
+        //qDebug()<<"distances is "<<distances;
+        if (distances==-9999)
+            distances=2000;
+        sedv->setDistances(distances);
+        //SEDVisualizerPlot *sedv= new SEDVisualizerPlot(sed_list2,vtkwin,vialactealWin);
+        sedv->show();
+
+
+        // SEDVisualizer *sedv= new SEDVisualizer(sedlist);
+        //  sedv->showMaximized();
+    }
+
+    queue->editOperation(operation_queue_row,"Completed");
+    queue->close();
+}
+
+SEDNode* VisIVOImporterDesktop::bmContainsNode(QString nodename)
+{
+    SEDNode *node;
+    if(!nodelist.contains(nodename))
+    {
+        node=new SEDNode();
+        node->setDesignation(nodename);
+        nodelist.insert(nodename, node);
+    }
+    else
+    {
+        node=nodelist.value(nodename);
+    }
+    return node;
+}
+
+VisIVOImporterDesktop::VisIVOImporterDesktop(QString t, QString f, TreeModel *m )
+{
+    m_impStatus = -1;
+    /*  m_impStatus:
+        1 : an error occurred
+        0 : importing OK
+    */
+
+    model=m;
+    fileName=f;
+    infoFile = QFileInfo(fileName);
+    type=t;
+    isHigal=false;
+    queue = &Singleton<OperationQueue>::Instance();
+
+}
+
+VisIVOImporterDesktop::VisIVOImporterDesktop(QString f, vtkwindow_new* v, bool isFilament, bool isBubble)
+{
+
+
+    QFileInfo infoFile = QFileInfo(f);
+
+
+    //QUI
+
+
+    headerFileName.append(QDir::homePath()+"/VisIVODesktopTemp/tmp_download/").append(infoFile.baseName()).append(".bin.head");
+    binaryFileName.append(QDir::homePath()+"/VisIVODesktopTemp/tmp_download/").append(infoFile.baseName()).append(".bin");
+
+    headerFile = new QFile(headerFileName);
+    importedFile = new QFile(fileName);
+    binaryFile = new QFile(binaryFileName);
+
+
+    QString type="ascii";
+    int errorCode;
+
+
+    char *filepath = new char[f.toStdString().length() + 1];
+    std::strcpy(filepath,f.toStdString().c_str());
+
+    char *fileformat = new char[type.toStdString().length() + 1];
+    std::strcpy(fileformat,type.toStdString().c_str());
+
+    VisIVOImporter envVI1;
+    VI_Init(&envVI1);
+
+    QString outputPathString=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/"+infoFile.baseName()+".bin";
+
+    char *outputPath = new char[outputPathString.toStdString().length() + 1];
+    std::strcpy(outputPath,outputPathString.toStdString().c_str());
+
+    errorCode=VI_SetAtt(&envVI1,VI_SET_FFORMAT,fileformat);
+    errorCode=VI_SetAtt(&envVI1,VI_SET_FILEPATH, filepath);
+    errorCode=VI_SetAtt(&envVI1,VI_SET_OUTFILEVBT,outputPath);
+    VI_Import(&envVI1);
+
+
+    VialacteaSource *vialactea_source= new VialacteaSource(f.toStdString());
+    m_VisIVOTable = new VSTableDesktop();
+
+    m_VisIVOTable->setLocator(outputPathString.toStdString());
+
+    m_VisIVOTable->setNumberOfColumns(vialactea_source->getNumberOfColumns());
+    m_VisIVOTable->setNumberOfRows(vialactea_source->getNumberOfRows());
+    m_VisIVOTable->setColsNames(vialactea_source->getColumnsNames());
+    m_VisIVOTable->readStatistics();
+    vialactea_source->computeHistogram();
+
+    m_VisIVOTable->setNumberOfBins(vialactea_source->getNumberOfBins());
+    m_VisIVOTable->setHistogramArray(vialactea_source->getHistogramArray());
+    m_VisIVOTable->setHistogramValueArray(vialactea_source->getHistogramValueArray());
+
+
+
+    queue = &Singleton<OperationQueue>::Instance();
+    operation_queue_row=queue->addOperation("Importing "+infoFile.fileName());
+
+
+    m_VisIVOTable->setName(infoFile.fileName().toStdString());
+    m_VisIVOTable->setTableData(vialactea_source->getData());
+
+    vtkwin=v;
+    ////qDebug()<<"num#### "<<m_VisIVOTable->getNumberOfRows();
+
+    if (isFilament)
+    {
+
+
+        vtkwin->addFilaments(m_VisIVOTable);
+    }
+    else if (isBubble)
+    {
+        vtkwin->addBubble(m_VisIVOTable);
+    }
+    else
+    {
+
+        VisPoint *m_VisPointsObject = new VisPoint(m_VisIVOTable);
+
+        m_VisPointsObject->setX("x");
+        m_VisPointsObject->setY("y");
+        m_VisPointsObject->setZ("z");
+        m_VisPointsObject->setScale(true);
+
+        vtkwindow_new *m_OldRenderingWindow = new vtkwindow_new(this, m_VisPointsObject);
+
+
+    }
+
+}
+
+void VisIVOImporterDesktop::setVtkWin(vtkwindow_new* v)
+{
+    vtkwin=v;
+}
+
+void VisIVOImporterDesktop::setBm(bool bm)
+{
+    isBandMergedCatalogue=bm;
+}
+
+void VisIVOImporterDesktop::doImport(QString wavelen, bool usingAPI)
+{
+
+    wavelength=wavelen;
+    operation_queue_row=queue->addOperation("Importing "+fileName);
+
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+    if(!usingAPI)
+    {
+        if(type=="FITSIMG")
+        {
+
+
+            vtkSmartPointer<vtkFitsReader> fitsReader = vtkSmartPointer<vtkFitsReader>::New();
+            fitsReader->SetFileName(fileName.toStdString());
+
+
+
+            // Modify the TreeModel
+            int rows = model->rowCount();
+            model->insertRow(rows);
+            // model->setFITSIMG(model->index(rows,0),imageActor);
+            model->setFITSIMG(model->index(rows,0),fitsReader);
+            model->setData(model->index(rows,0),QIcon( ":/icons/VME_IMAGE.bmp" ));
+            model->setData(model->index(rows,1),fileName);
+            // end: Modify the TreeModel
+
+            queue->editOperation(operation_queue_row,"Completed");
+
+            /*
+        if(isHigal)
+        {
+            HiGal *higalWin = &Singleton<HiGal>::Instance();
+
+            QTableWidget *table=higalWin->getTable(3);
+
+            // Do whatever you want with it...
+            // table->setPlainText("Updated Plain Text Edit);
+
+            int id= table->rowCount();
+            table->insertRow(id);
+
+
+            QTableWidgetItem* wavelen = new QTableWidgetItem(wavelength);
+            QTableWidgetItem* nameItem = new QTableWidgetItem(infoFile.fileName());
+            table->setItem(id, 0, nameItem);
+            table->setItem(id, 1, wavelen);
+
+
+            higalWin->mapsImageData.insert(id,fitsReader);
+
+        }
+*/
+
+            return;
+        }
+        if (type=="VTP")
+        {
+            queue->show();
+
+            vtkSmartPointer<vtkXMLPolyDataReader> vtpReader =    vtkSmartPointer<vtkXMLPolyDataReader>::New();
+            vtkSmartPointer<vtkPolyData> pPolyData =  vtkSmartPointer<vtkPolyData>::New();
+            vtkSmartPointer<vtkPolyDataMapper> pMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+            vtkSmartPointer<vtkLODActor> pActor = vtkSmartPointer<vtkLODActor>::New();
+
+            vtpReader->SetFileName(fileName.toStdString().c_str());
+            vtpReader->Update();
+            pPolyData=vtpReader->GetOutput();
+            //  pMapper->SetInput(pPolyData);
+            pMapper->SetInputData(pPolyData);
+            pActor->SetMapper(pMapper);
+            // willing to be moved
+
+            // end: willing to be moved
+            return;
+        }
+        if (type=="VTI")
+        {
+            queue->show();
+
+            vtkSmartPointer<vtkXMLImageDataReader> vtiReader =    vtkSmartPointer<vtkXMLImageDataReader>::New();
+            vtkSmartPointer<vtkFixedPointVolumeRayCastMapper> volumeMapper = vtkSmartPointer<vtkFixedPointVolumeRayCastMapper>::New();
+            vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
+
+            vtkSmartPointer<vtkColorTransferFunction> colorTransferFunction = vtkSmartPointer<vtkColorTransferFunction>::New();
+            vtkSmartPointer<vtkPiecewiseFunction> opacityTransferFunction = vtkSmartPointer<vtkPiecewiseFunction>::New();
+            vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
+
+
+            vtiReader->SetFileName(fileName.toStdString().c_str());
+            vtiReader->Update();
+#if VTK_MAJOR_VERSION <= 5
+            volumeMapper->SetInput(vtiReader->GetOutput());
+#else
+            volumeMapper->SetInputConnection(vtiReader->GetOutputPort());
+#endif
+
+            volumeMapper->SetImageSampleDistance(0.5);
+            volume->SetMapper(volumeMapper);
+            // willing to be moved
+            // A smart Color map ...8  points...
+            colorTransferFunction->AddRGBPoint(0,0,0,0);
+            colorTransferFunction->AddRGBPoint(0.396415,1,0,0);
+            colorTransferFunction->AddRGBPoint(0.531872,1,0,0);
+            colorTransferFunction->AddRGBPoint(0.697211,0.901961,0,0);
+            colorTransferFunction->AddRGBPoint(0.76494,0.901961,0.831891,0);
+            colorTransferFunction->AddRGBPoint(0.824701,0.901961,0.831891,0);
+            colorTransferFunction->AddRGBPoint(0.888446,0.901961,0.901961,0);
+            colorTransferFunction->AddRGBPoint(1,1,1,1);
+            /*
+                         opacityTransferFunction->AddPoint(0,0);
+                         opacityTransferFunction->AddPoint(0.396415,0);
+                         opacityTransferFunction->AddPoint(0.531872,0.06);
+                         opacityTransferFunction->AddPoint(0.697211,0.2);
+                         opacityTransferFunction->AddPoint(0.76494,0.5);
+                         opacityTransferFunction->AddPoint(0.824701,0.8);
+                         opacityTransferFunction->AddPoint(0.888446,0.9);
+                         opacityTransferFunction->AddPoint(1,1);
+                      */
+            double step=1.0/256;
+            double opValue=0;
+            for (double i=0; i<=1; i+=step)
+            {
+                opValue=(tanh(5*i-3)+1)/2.5;
+                opacityTransferFunction->AddPoint(i,opValue);
+            }
+            volumeProperty->SetScalarOpacity(opacityTransferFunction);
+            volumeProperty->ShadeOff();
+            volumeProperty->SetInterpolationTypeToLinear();
+            volumeProperty->SetColor(colorTransferFunction);
+            volume->SetProperty(volumeProperty);
+
+            // Modify the TreeModel
+            int rows = model->rowCount();
+            model->insertRow(rows);
+            model->setVTI(model->index(rows,0),volume);
+            model->setData(model->index(rows,0),QIcon( ":/icons/VME_VOLUME.bmp" ));
+            model->setData(model->index(rows,1),fileName);
+            // end: Modify the TreeModel
+            return;
+        }
+
+        headerFileName.append(w->workingPath).append(QDir::separator()).append(infoFile.baseName()).append(".bin.head");
+        binaryFileName.append(w->workingPath).append(QDir::separator()).append(infoFile.baseName()).append(".bin");
+
+        headerFile = new QFile(headerFileName);
+        importedFile = new QFile(fileName);
+        binaryFile = new QFile(binaryFileName);
+
+
+        if (!importedFile->exists()) {
+            ////qDebug() << "VisIVOImporterDesktop::VisIVOImporterDesktop:  I can't open file";
+            m_impStatus = 1;
+            return;
+        }
+        else
+        {
+            //We 're importing a supported VisIVO File by using the canonical importer
+            //The result if m_impStatus==0 will be VBT (VisIVO Binary Table)
+
+            QProcess *myProcess = new QProcess(this->parent());
+            QStringList arguments;
+            connect(myProcess, SIGNAL(finished(int, QProcess::ExitStatus)),this, SLOT(onProcessFinished(int, QProcess::ExitStatus)));
+
+            arguments << "--fformat" << type << "--out"<< binaryFileName << fileName;
+
+            ////qDebug()<< "--fformat" << type << "--out"<< binaryFileName << fileName;
+
+            myProcess->setStandardErrorFile(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("log_error.txt"), QIODevice::Append);
+            myProcess->setStandardOutputFile(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("log_output.txt"), QIODevice::Append);
+            myProcess->start(w->importer,arguments);
+
+        }
+
+    }
+    else
+    {
+
+
+        headerFileName.append(QDir::homePath()+"/VisIVODesktopTemp/tmp_download/").append(infoFile.baseName()).append(".bin.head");
+        binaryFileName.append(QDir::homePath()+"/VisIVODesktopTemp/tmp_download/").append(infoFile.baseName()).append(".bin");
+
+        headerFile = new QFile(headerFileName);
+        importedFile = new QFile(fileName);
+        binaryFile = new QFile(binaryFileName);
+
+
+        if (!importedFile->exists()) {
+            ////qDebug() << "VisIVOImporterDesktop::VisIVOImporterDesktop:  I can't open file";
+            m_impStatus = 1;
+            return;
+        }
+        else
+        {
+            int errorCode;
+
+
+            char *filepath = new char[fileName.toStdString().length() + 1];
+            std::strcpy(filepath,fileName.toStdString().c_str());
+
+            char *fileformat = new char[type.toStdString().length() + 1];
+            std::strcpy(fileformat,type.toStdString().c_str());
+            /*
+            VisIVOImporter envVI1;
+            VI_Init(&envVI1);
+
+            QString outputPathString=QDir::homePath()+"/VisIVODesktopTemp/tmp_download/"+infoFile.baseName()+".bin";
+
+            char *outputPath = new char[outputPathString.toStdString().length() + 1];
+            std::strcpy(outputPath,outputPathString.toStdString().c_str());
+
+            errorCode=VI_SetAtt(&envVI1,VI_SET_FFORMAT,fileformat);
+            errorCode=VI_SetAtt(&envVI1,VI_SET_FILEPATH, filepath);
+            errorCode=VI_SetAtt(&envVI1,VI_SET_OUTFILEVBT,outputPath);
+            VI_Import(&envVI1);
+y
+            onProcessFinished(0,QProcess::NormalExit);
+*/
+
+        }
+
+
+    }
+}
+
+
+
+void VisIVOImporterDesktop::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+    m_impStatus = exitStatus;
+
+    ////qDebug()<<"dentro onProcessFinished m_impStatus: "<<m_impStatus;
+
+    if (!headerFile->exists() ) {
+        ////qDebug() << "VisIVOImporterDesktop::VisIVOImporterDesktop: The header file wasn't generate" << headerFile->fileName();
+        m_impStatus = 1;
+        return;
+    }
+    if (!binaryFile->exists() ) {
+        ////qDebug() << "VisIVOImporterDesktop::VisIVOImporterDesktop: The binary file wasn't generate" << binaryFile->fileName();
+        m_impStatus = 1;
+        return;
+    }
+
+
+    /*  m_impStatus:
+                1 : an error occurred
+                0 : OK
+                */
+    if ( !m_impStatus)
+    {
+
+
+        std::string file = binaryFileName.toStdString();
+        std::string name = infoFile.baseName().toStdString();
+        //   m_VisIVOTable = new VSTableDesktop(file,name,"" ,!isHigal);
+        m_VisIVOTable = new VSTableDesktop(file,name,"" ,true);
+        //    VSTableDesktop(std::string locator, std::string name = "", std::string description = "", bool statistic=true);
+
+
+
+
+        // Modify the TreeModel
+        QString tableName;
+        bool result;
+        QModelIndex  parentIndex;
+        tableName=QString(m_VisIVOTable->getName().c_str());
+        //parentIndex = model->index(0,0);
+
+        ////qDebug()<<tableName;
+        parentIndex=QModelIndex();
+        int rows = model->rowCount(parentIndex);
+        if (model->insertRows(rows,1,parentIndex)){
+            result = model->setTable(model->index(rows,0,parentIndex),m_VisIVOTable); // We append a VSTable to the item
+            if (!m_VisIVOTable->getIsVolume())  // The table is a Point Table
+                result = model->setData(model->index(rows,0,parentIndex),QIcon( ":/icons/VBT_Table.bmp" ));
+            else                        // The table is a Volume Table VBT_Volume
+                result = model->setData(model->index(rows,0,parentIndex),QIcon( ":/icons/VBT_Volume.bmp" ));
+
+            result = model->setData(model->index(rows,1,parentIndex),tableName);
+
+
+            model->setLastInsertItem(model->index(rows,0,parentIndex));
+
+            MainWindow *w = &Singleton<MainWindow>::Instance();
+            w->ui->tabWidget->setCurrentWidget(w->ui->tabObjectTree);
+
+
+        }
+
+        //VIALACTEA
+        if(isHigal)
+        {
+
+            //  ViaLactea *vialacteaWindow = &Singleton<ViaLactea>::Instance();
+
+            if(!isBandMergedCatalogue)
+                vtkwin->addSources(model->getTable(model->getLastInsertItem()));
+            else
+            {
+                //tabella bm!
+                ////qDebug()<<"tabella bm!";
+
+                unsigned int *colList;
+                colList = new unsigned int [5];
+
+
+                // char **m_tableData=NULL;
+                std::string **m_tableData=NULL;
+
+                m_tableData = new std::string*[5];
+
+
+                for(int  i = 0; i < 5; i++)
+                {
+                    m_tableData[i] = new std::string[m_VisIVOTable->getNumberOfRows()];
+                }
+
+
+
+                colList[0]=m_VisIVOTable->getColId("N70");
+                colList[1]=m_VisIVOTable->getColId("N160");
+                colList[2]=m_VisIVOTable->getColId("N250");
+                colList[3]=m_VisIVOTable->getColId("N350");
+                colList[4]=m_VisIVOTable->getColId("N500");
+
+                m_VisIVOTable->getColumnString(colList,5,0,m_VisIVOTable->getNumberOfRows(),m_tableData);
+
+
+                QHash <QString, int> bm500;
+                QHash <QString, int> bm350;
+                QHash <QString, int> bm250;
+                QHash <QString, int> bm160;
+                QHash <QString, int> bm70;
+
+                for(int  i = 0; i < m_VisIVOTable->getNumberOfRows(); i++)
+                {
+
+                    bm500.insert( QString::fromStdString( m_tableData[4][i] ), i);
+                    bm350.insert( QString::fromStdString( m_tableData[3][i] ), i);
+                    bm250.insert( QString::fromStdString( m_tableData[2][i] ), i);
+                    bm160.insert( QString::fromStdString( m_tableData[1][i] ), i);
+                    bm70.insert ( QString::fromStdString( m_tableData[0][i] ), i);
+
+                }
+
+                m_VisIVOTable->setBandMerged500(bm500);
+                m_VisIVOTable->setBandMerged350(bm350);
+                m_VisIVOTable->setBandMerged250(bm250);
+                m_VisIVOTable->setBandMerged160(bm160);
+                m_VisIVOTable->setBandMerged70(bm70);
+
+
+                //ViaLactea *vialactealWin = &Singleton<ViaLactea>::Instance();
+                //vialactealWin->setBmTable(m_VisIVOTable);
+
+
+
+
+            }
+
+
+
+        }
+
+
+        queue->editOperation(operation_queue_row,"Completed");
+
+    }
+
+
+}
+
+
+VSTableDesktop *VisIVOImporterDesktop::getTable()
+{
+    return m_VisIVOTable;
+}
+int VisIVOImporterDesktop::getStatus()
+{
+    return m_impStatus;
+}
diff --git a/Code/src/visivoimporterdesktop.h b/Code/src/visivoimporterdesktop.h
new file mode 100644
index 0000000000000000000000000000000000000000..e7a2eaa3754f1cd05032891ada827aae4ae19f7d
--- /dev/null
+++ b/Code/src/visivoimporterdesktop.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Alessandro Costa                                *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+class TreeModel;
+
+#ifndef VISIVOIMPORTERDESKTOP_H
+#define VISIVOIMPORTERDESKTOP_H
+
+/**
+        @author Alessandro Costa <alessandro.costa@oact.inaf.it>
+        @author Ugo Becciani <ugo.becciani@oact.inaf.it>
+*/
+
+#include <QWidget>
+#include "vstabledesktop.h"
+#include <QFileInfo>
+#include <QProcess>
+
+#include "operationqueue.h"
+#include "vtkwindow_new.h"
+#include "sednode.h"
+#include "sed.h"
+
+class VisIVOImporterDesktop : public QWidget
+{
+    Q_OBJECT
+public:
+    VisIVOImporterDesktop(QString t, QString f, TreeModel * m=NULL);
+    VisIVOImporterDesktop(QString f, TreeModel * m=NULL, bool isBandMergedCatalogue=false, vtkwindow_new *v=NULL, QString wavelen="");
+    VisIVOImporterDesktop(QString f, vtkwindow_new* v, bool isFilament=true, bool isBubble=false);
+    void doImport(QString wavelen="", bool usingAPI=false);
+    int getStatus();
+    VSTableDesktop *getTable();
+    void setVtkWin(vtkwindow_new* v);
+    void setBm(bool bm);
+
+
+
+signals:
+
+private:
+    //QFileInfo infoFile;
+    int m_impStatus;
+    VSTableDesktop *m_VisIVOTable;
+    TreeModel * model;
+    QString type;
+    QString fileName;
+    QString headerFileName;
+    QString binaryFileName;
+    QFile *headerFile;
+    QFile *importedFile;
+    QFile *binaryFile;
+    QFileInfo infoFile;
+    OperationQueue *queue;
+    int operation_queue_row;
+    QString wavelength;
+    bool isHigal;
+    bool isBandMergedCatalogue;
+    QString outputName_atlas;
+    QString outputName_designation;
+    vtkwindow_new *vtkwin;
+    QHash < QString,SEDNode*> nodelist;
+    SEDNode* bmContainsNode(QString nodename);
+    QList <SED* > sedlist;
+    bool firstSEDNode;
+
+    QHash < int,SED*> sedMap;
+
+public slots:
+
+private slots:
+    void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
+
+};
+
+
+#endif // VISIVOIMPORTERDESKTOP_H
diff --git a/Code/src/visivoutilsdesktop.cpp b/Code/src/visivoutilsdesktop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9a1b6812fd106a1d3f696fa716c8bcd042516d50
--- /dev/null
+++ b/Code/src/visivoutilsdesktop.cpp
@@ -0,0 +1,462 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+ *   ugo.becciani@oact.inaf.it                                             *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+//#include "VisIVOFiltersConfigure.h"
+
+#define M_PI 3.14159265358979323846f
+#define M_PI_2 (M_PI/2.F)
+#define DEG_TO_RAD_FACTOR (M_PI/180.F)
+
+#include "visivoutilsdesktop.h"
+#include <fstream>
+#include <sstream>
+#include <iostream>
+#include <cassert>
+#include <math.h>
+
+#include <cstring>
+#include <cstdlib>
+#include <time.h>
+
+
+
+static size_t writeDataDesktop(void *buffer, size_t size, size_t nMemb, void *userP);
+
+//---------------------------------------------------------------------
+double masToRadDesktop(double mas)
+//---------------------------------------------------------------------
+{
+    double seconds = mas/1000;
+    double degs = seconds/3600;
+
+    return degs*DEG_TO_RAD_FACTOR;
+}
+
+//---------------------------------------------------------------------
+void masToRadDesktop(double *mas, int n)
+//---------------------------------------------------------------------
+{
+    int i = 0;
+
+    double seconds = 0;
+    double degs = 0;
+
+    for(i = 0; i < n; i++)
+    {
+        seconds = mas[i]/1000;
+        degs = seconds/3600;
+        mas[i] = degs * DEG_TO_RAD_FACTOR;
+    }
+
+    return;
+}
+
+
+//---------------------------------------------------------------------
+double degToRadDesktop(double deg)
+//---------------------------------------------------------------------
+{
+    return deg*DEG_TO_RAD_FACTOR;
+}
+
+//---------------------------------------------------------------------
+void degToRadDesktop(double *deg, int n)
+//---------------------------------------------------------------------
+{
+    for(int i = 0; i < n; i++)
+        deg[i] *= DEG_TO_RAD_FACTOR;
+
+    return;
+}
+
+//---------------------------------------------------------------------
+double hmsToRadDesktop(char *hms)
+//---------------------------------------------------------------------
+{
+    double hours = 0;
+    double minutes = 0;
+    double seconds = 0;
+
+    std::stringstream tmpStream;
+
+    tmpStream << hms;
+
+    tmpStream >> hours;
+    tmpStream >> minutes;
+    tmpStream >> seconds;
+
+    double outcome = hours + (minutes/60) + (seconds/3600);
+    outcome = (outcome/24)*360;
+    outcome = outcome*DEG_TO_RAD_FACTOR;
+
+    return outcome;
+}
+
+//---------------------------------------------------------------------
+void hmsToRadDesktop(char **hmsIn, double *radOut, int n)
+//---------------------------------------------------------------------
+{
+    double hours = 0;
+    double minutes = 0;
+    double seconds = 0;
+
+    for(int i = 0; i < n; i++)
+    {
+        std::stringstream tmpStream;
+
+        tmpStream << hmsIn[i];
+
+        tmpStream >> hours;
+        tmpStream >> minutes;
+        tmpStream >> seconds;
+
+        radOut[i] = hours + (minutes/60) + (seconds/3600);
+        radOut[i] = (radOut[i]/24)*360;
+        radOut[i] = radOut[i]*DEG_TO_RAD_FACTOR;
+    }
+
+    return;
+}
+
+//---------------------------------------------------------------------
+double dmsToRadDesktop(char *dms)
+//---------------------------------------------------------------------
+{
+    double degs = 0;
+    double minutes = 0;
+    double seconds = 0;
+
+    std::stringstream tmpStream;
+
+    tmpStream << dms;
+
+    tmpStream >> degs;
+    tmpStream >> minutes;
+    tmpStream >> seconds;
+
+    if(degs < 0)
+    {
+        minutes = -minutes;
+        seconds = -seconds;
+    }
+
+    double outcome = degs + (minutes/60) + (seconds/3600);
+    outcome = outcome*DEG_TO_RAD_FACTOR;
+
+    return outcome;
+}
+
+
+//---------------------------------------------------------------------
+void dmsToRadDesktop(char **dmsIn, double *radOut, int n)
+//---------------------------------------------------------------------
+{
+    double degs = 0;
+    double minutes = 0;
+    double seconds = 0;
+
+    for(int i = 0; i < n; i++)
+    {
+        std::stringstream tmpStream;
+
+        tmpStream << dmsIn[i];
+
+        tmpStream >> degs;
+        tmpStream >> minutes;
+        tmpStream >> seconds;
+
+        if(degs < 0)
+        {
+            minutes = -minutes;
+            seconds = -seconds;
+        }
+
+        radOut[i] = degs + (minutes/60) + (seconds/3600);
+        radOut[i] = radOut[i]*DEG_TO_RAD_FACTOR;
+    }
+
+    return;
+}
+
+//---------------------------------------------------------------------
+int iCompareDesktop(std::string str1, std::string str2)
+//---------------------------------------------------------------------
+{
+#ifdef WIN32
+    return strcmpi(str1.c_str(), str2.c_str());
+#else
+    return strcasecmp(str1.c_str(), str2.c_str());
+#endif
+}
+
+//---------------------------------------------------------------------
+std::string getTempFileNameDesktop(std::string suffix)
+//---------------------------------------------------------------------
+{
+    std::stringstream tmpFileName;
+
+#ifdef WIN32
+    char *tempDir = getenv("TEMP");
+
+    if(!tempDir)
+        tempDir = getenv("TMP");
+
+    if(tempDir)
+    {
+        tmpFileName << tempDir;
+        tmpFileName << "\\";
+    }
+#else
+
+    tmpFileName << "/tmp/";
+
+#endif
+
+    tmpFileName << "VisIVOTmp" << rand() << suffix;
+
+    return tmpFileName.str();
+}
+
+//---------------------------------------------------------------------
+std::string getDirDesktop(std::string path)
+//---------------------------------------------------------------------
+{
+#ifdef _WIN32
+    int idx = path.rfind('\\');
+#else
+    int idx = path.rfind('/');
+#endif
+    if(idx >= 0)
+        path.erase(idx + 1, path.length() - (idx + 1));
+    else
+        path = "";
+
+    return path;
+}
+
+//---------------------------------------------------------------------
+std::string getExtDesktop(std::string path)
+//---------------------------------------------------------------------
+{
+    int idx = path.rfind('.');
+
+    if(idx < 0)
+        return "";
+
+    return path.erase(0, idx + 1);
+}
+
+//---------------------------------------------------------------------
+std::string trimRightDesktop(const std::string & source, const std::string & t /*= " "*/)
+//---------------------------------------------------------------------
+{
+    std::string str = source;
+    return str.erase(str.find_last_not_of(t) + 1);
+}
+
+//---------------------------------------------------------------------
+std::string trimLeftDesktop(const std::string & source, const std::string & t /*= " "*/)
+//---------------------------------------------------------------------
+{
+    std::string str = source;
+    return str.erase(0, str.find_first_not_of(t));
+}
+
+//---------------------------------------------------------------------
+std::string trimDesktop(const std::string & source, const std::string & t /*= " "*/)
+//---------------------------------------------------------------------
+{
+    std::string str = source;
+    return trimLeftDesktop(trimRightDesktop(str, t), t);
+}
+
+//---------------------------------------------------------------------
+void findAndReplaceDesktop(std::string &str, char find, char replace)
+//---------------------------------------------------------------------
+{
+    size_t i;
+
+    for(; (i = str.find(find)) != std::string::npos;)
+        str.replace(i, 1, 1, replace);
+
+    return;
+}
+
+//---------------------------------------------------------------------
+static size_t writeDataDesktop(void *buffer, size_t size, size_t nMemb, void *userP)
+//---------------------------------------------------------------------
+{
+    unsigned int byteToWrite = size*nMemb;
+
+    std::ofstream *out = (std::ofstream *)userP;
+    out->write((char *)buffer, byteToWrite);
+
+    return byteToWrite;
+}
+
+//---------------------------------------------------------------------
+std::string getNameDesktop(std::string path)
+//---------------------------------------------------------------------
+{
+#ifdef _WIN32
+    int idx = path.rfind('\\');
+#else
+    int idx = path.rfind('/');
+#endif
+    if(idx >= 0)
+        path.erase(0, (idx + 1));
+    else
+        path = "";
+
+    return path;
+}
+//----------------------------
+float floatSwapDesktop(char *value)
+
+//---------------------------
+{
+
+    int size =sizeof(float);
+    float swapped;
+    char *buffer;
+    buffer = new char [sizeof(float)];
+
+    for (int i=0; i<size; i++)
+        buffer[ i ] = value[ size-1-i ];
+
+    swapped= *( (float *) buffer );
+    delete [] buffer;
+    return swapped;
+
+}
+
+//----------------------------
+double doubleSwapDesktop(char *value)
+
+//---------------------------
+{
+    int size =sizeof(double);
+    char *buffer;
+    double swapped;
+    buffer = new char[sizeof(double)];
+
+    for (int i=0; i<size; i++)
+        buffer[ i ] = value[ size-1-i ];
+
+
+    swapped= *( (double *) buffer );
+    delete [] buffer;
+    return swapped;
+
+}
+//----------------------------
+long double longdoubleSwapDesktop(char *value)
+
+//---------------------------
+{
+    int size =sizeof(long double);
+    char *buffer;
+    long double swapped;
+    buffer = new char[sizeof(long double)];
+
+    for (int i=0; i<size; i++)
+        buffer[ i ] = value[ size-1-i ];
+
+
+    swapped= *( (long double *) buffer );
+    delete [] buffer;
+    return swapped;
+}
+//----------------------------
+int intSwapDesktop(char *value)
+
+//---------------------------
+{
+    int size =sizeof(int);
+    char *buffer;
+    int swapped;
+    buffer = new char[sizeof(int)];
+
+    for (int i=0; i<size; i++)
+        buffer[ i ] = value[ size-1-i ];
+
+
+    swapped= *( (int *) buffer );
+    delete [] buffer;
+    return swapped;
+}
+//----------------------------
+long int longintSwapDesktop(char *value)
+
+//---------------------------
+{
+    int size =sizeof(long int);
+    char *buffer;
+    long int swapped;
+    buffer = new char[sizeof(long int)];
+
+    for (int i=0; i<size; i++)
+        buffer[ i ] = value[ size-1-i ];
+
+
+    swapped= *( (long int *) buffer );
+    delete [] buffer;
+    return swapped;
+}
+//----------------------------
+long long int longlongintSwapDesktop(char *value)
+
+//---------------------------
+{
+    int size =sizeof(long long int);
+    char *buffer;
+    long long int swapped;
+    buffer = new char[sizeof(long long int)];
+
+    for (int i=0; i<size; i++)
+        buffer[ i ] = value[ size-1-i ];
+
+
+    swapped= *( (long long int *) &buffer );
+    delete [] buffer;
+    return swapped;
+}
+//----------------------------
+
+void sortarrayDesktop(int *vect, int elements)
+
+//---------------------------
+{
+    for(int i=elements-1;i>=0;i=i-1)
+    {
+        for(int j=1;j<=i;j++)
+        {
+            if(vect[j-1] > vect[j])
+            {
+                int temp=vect[j];
+                vect[j]=vect[j-1];
+                vect[j-1]=temp;
+
+            }
+
+        }
+    }
+    return;
+}
diff --git a/Code/src/visivoutilsdesktop.h b/Code/src/visivoutilsdesktop.h
new file mode 100644
index 0000000000000000000000000000000000000000..c3d80886c39bbf946859b58d06811f21e2c997eb
--- /dev/null
+++ b/Code/src/visivoutilsdesktop.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+ *   ugo.becciani@oact.inaf.it                                             *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef VISIVOUTILSDESKTOP_H
+#define VISIVOUTILSDESKTOP_H
+
+/**
+        @author Alessandro Costa <alessandro.costa@oact.inaf.it>
+        @author Ugo Becciani <ugo.becciani@oact.inaf.it>
+*/
+
+#include <string>
+#include <QString>
+#include <vector>
+
+
+double masToRadDesktop(double mas);
+void masToRadDesktop(double *mas, int n);
+double degToRadDesktop(double deg);
+void degToRadDesktop(double *deg, int n);
+double hmsToRadDesktop(char *hms);
+void hmsToRadDesktop(char **hmsIn, double *radOut, int n);
+double dmsToRadDesktop(char *dms);
+void dmsToRadDesktop(char **dmsIn, double *radOut, int n);
+
+int iCompareDesktop(std::string str1, std::string str2);
+
+std::string getTempFileNameDesktop(std::string suffix);
+std::string getDirDesktop(std::string path);
+std::string getExtDesktop(std::string path);
+std::string getNameDesktop(std::string path);
+
+std::string trimRightDesktop(const std::string & source, const std::string & t = " ");
+std::string trimLeftDesktop(const std::string & source, const std::string & t = " ");
+std::string trimDesktop(const std::string & source, const std::string & t = " ");
+
+void findAndReplaceDesktop(std::string &str, char find, char replace);
+
+
+//int VisIVORemoteLoadToFile(std::string path, std::string tempFileName);
+ double doubleSwapDesktop(char *value);
+ long double longdoubleSwapDesktop(char *value);
+ float floatSwapDesktop(char *value);
+ int intSwapDesktop(char *value);
+ long int longintSwapDesktop(char *value);
+ long long int longlongintSwapDesktop(char *value);
+ void sortarrayDesktop(int *vect, int elements);
+#endif
+
diff --git a/Code/src/vispoint.cpp b/Code/src/vispoint.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a3085359aa0a0396e2ad0db79762aabbb03851fe
--- /dev/null
+++ b/Code/src/vispoint.cpp
@@ -0,0 +1,183 @@
+#include "vispoint.h"
+#include "vstabledesktop.h"
+#include "treeitem.h"
+
+
+VisPoint::VisPoint()
+{
+    setX("");
+    setY("");
+    setZ("");
+    setVectorX("");
+    setVectorY("");
+    setVectorZ("");
+    setLutScalar("");
+    setRadiusScalar("");
+    setHeightScalar("");
+    setShowPoints(true);
+    setShowVectors(false);
+    setShowAxes(true);
+    setScale(true);
+    setShowBox(true);
+    setLogX(false);
+    setLogY(false);
+    setLogZ(false);
+    setName("Visual Object");
+    m_Table=NULL;
+    m_isSetOrigin=false;
+}
+VisPoint::VisPoint(VSTableDesktop *table)
+{
+    m_OriginType = TreeItem::PointTable;
+    setX("");
+    setY("");
+    setZ("");
+    setVectorX("");
+    setVectorY("");
+    setVectorZ("");
+    setLutScalar("");
+    setRadiusScalar("");
+    setHeightScalar("");
+    setShowPoints(true);
+    setShowVectors(false);
+    setShowAxes(true);
+    setScale(true);
+    setShowBox(true);
+    setLogX(false);
+    setLogY(false);
+    setLogZ(false);
+    setName(QString(table->getName().c_str()));
+    m_Table=table;
+    m_isSetOrigin=true;
+}
+void  VisPoint::setX(QString value)
+{
+    m_X=value;
+}
+void  VisPoint::setY(QString value)
+{
+    m_Y=value;
+}
+void  VisPoint::setZ(QString value)
+{
+    m_Z=value;
+}
+void  VisPoint::setVectorX(QString value)
+{
+    m_VectorX=value;
+}
+void  VisPoint::setVectorY(QString value)
+{
+    m_VectorY=value;
+}
+void  VisPoint::setVectorZ(QString value)
+{
+    m_VectorZ=value;
+}
+void  VisPoint::setLutScalar(QString value)
+{
+    m_LutScalar=value;
+}
+void  VisPoint::setRadiusScalar(QString value)
+{
+    m_RadiusScalar=value;
+}
+void  VisPoint::setHeightScalar(QString value)
+{
+    m_HeightScalar=value;
+}
+void  VisPoint::setLut(vtkLookupTable* lut)
+{
+    m_lut=lut;
+}
+
+
+void VisPoint::setShowPoints(bool value)
+{
+    m_ShowPoints=value;
+}
+void VisPoint::setShowVectors(bool value)
+{
+    m_ShowVectors=value;
+}
+void VisPoint::setScale(bool value)
+{
+    m_Scale=value;
+}
+void VisPoint::setLogX(bool value)
+{
+    m_LogX=value;
+}
+void VisPoint::setLogY(bool value)
+{
+    m_LogY=value;
+}
+void VisPoint::setLogZ(bool value)
+{
+    m_LogY=value;
+}
+QString  VisPoint::getX()
+{
+    return m_X;
+}
+QString  VisPoint::getY()
+{
+    return m_Y;
+}
+QString  VisPoint::getZ()
+{
+    return m_Z;
+}
+QString  VisPoint::getVectorX()
+{
+    return m_VectorX;
+}
+QString  VisPoint::getVectorY()
+{
+    return m_VectorY;
+}
+QString VisPoint::getVectorZ()
+{
+    return m_VectorZ;
+}
+QString VisPoint::getLutScalar()
+{
+    return m_LutScalar;
+}
+QString VisPoint::getRadiusScalar()
+{
+    return m_RadiusScalar;
+}
+QString VisPoint::getHeightScalar()
+{
+    return m_HeightScalar;
+}
+bool VisPoint::isEnabledShowPoints()
+{
+    return  m_ShowPoints;
+}
+bool VisPoint::isEnabledShowVectors()
+{
+    return  m_ShowVectors;
+}
+bool VisPoint::isEnabledScale()
+{
+    return  m_Scale;
+}
+
+bool VisPoint::isEnabledLogX()
+{
+    return  m_LogX;
+}
+bool VisPoint::isEnabledLogY()
+{
+    return  m_LogY;
+}
+bool VisPoint::isEnabledLogZ()
+{
+    return  m_LogZ;
+}
+VSTableDesktop *VisPoint::getOrigin()
+{
+    return m_Table;
+}
diff --git a/Code/src/vispoint.h b/Code/src/vispoint.h
new file mode 100644
index 0000000000000000000000000000000000000000..74ff1668a838d24f0cff2bb51168929c18fe62c1
--- /dev/null
+++ b/Code/src/vispoint.h
@@ -0,0 +1,107 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Alessandro Costa                                *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include <QString>
+#include "vtkLookupTable.h"
+#include "observedobject.h"
+#ifndef VISPOINT_H
+#define VISPOINT_H
+class VSTableDesktop;
+class TreeItem;
+
+class VisPoint: public ObservedObject
+
+{
+    QString m_X;
+    QString m_Y;
+    QString m_Z;
+
+    QString m_VectorX;
+    QString m_VectorY;
+    QString m_VectorZ;
+
+    QString m_LutScalar;
+    QString m_RadiusScalar;
+    QString m_HeightScalar;
+
+    enum m_GLYPH
+    {
+        POINTS,
+        SPHERES,
+        CYLINDERS,
+        CONES
+    };
+
+    bool m_ShowPoints;
+    bool m_ShowVectors;
+    bool m_Scale;
+    bool m_LogX;
+    bool m_LogY;
+    bool m_LogZ;
+    vtkLookupTable* m_lut;
+    VSTableDesktop * m_Table;
+
+
+
+public:
+    VisPoint();
+    VisPoint(VSTableDesktop * table);
+    VSTableDesktop* getOrigin();
+    void setX(QString value);
+    void setY(QString value);
+    void setZ(QString value);
+    void setVectorX(QString value);
+    void setVectorY(QString value);
+    void setVectorZ(QString value);
+    void setLutScalar(QString value);
+    void setRadiusScalar(QString value);
+    void setHeightScalar(QString value);
+
+    void setShowPoints(bool value);
+    void setShowVectors(bool value);
+    void setScale(bool value);
+    void setLogX(bool value);
+    void setLogY(bool value);
+    void setLogZ(bool value);
+
+    QString getX();
+    QString getY();
+    QString getZ();
+    QString getVectorX();
+    QString getVectorY();
+    QString getVectorZ();
+    QString getLutScalar();
+    QString getRadiusScalar();
+    QString getHeightScalar();
+    bool isEnabledShowPoints();
+    bool isEnabledShowVectors();
+    bool isEnabledScale();
+    bool isEnabledLogX();
+    bool isEnabledLogY();
+    bool isEnabledLogZ();
+
+
+
+    vtkLookupTable*getLut();
+    void setLut(vtkLookupTable* lut);
+
+};
+
+#endif // VISPOINT_H
diff --git a/Code/src/vlkbquery.cpp b/Code/src/vlkbquery.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..33c974d6cb51d4d57808ed77f38155378a862167
--- /dev/null
+++ b/Code/src/vlkbquery.cpp
@@ -0,0 +1,337 @@
+#include "vlkbquery.h"
+#include "ui_vlkbquery.h"
+#include <QDebug>
+#include <QDomDocument>
+#include <QDir>
+#include "vialactea_fileload.h"
+#include <QMessageBox>
+#include "sedvisualizerplot.h"
+
+VLKBQuery::VLKBQuery(QString q, vtkwindow_new *v, QString w, QWidget *parent, Qt::GlobalColor color) :
+    QWidget(parent),
+    ui(new Ui::VLKBQuery)
+{
+    ui->setupUi(this);
+
+    if (parent != 0)
+    {
+        sd = dynamic_cast<SEDVisualizerPlot*>(parent);
+    }
+
+
+    query=q;//QUrl::toPercentEncoding(q);
+
+    loading = new LoadingWidget();
+    loading->init();
+    loading->setFileName("Querying VLKB..");
+    loading ->show();
+    loading->activateWindow();
+
+    vtkwin=v;
+    what=w;
+    modelColor=color;
+    connectToVlkb();
+
+}
+
+void VLKBQuery::connectToVlkb()
+{
+    qDebug()<<"connectToVlkb";
+    url= "http://ia2-vialactea.oats.inaf.it:8080/vlkb";
+    manager = new QNetworkAccessManager(this);
+    qDebug()<<"connect";
+    connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(availReplyFinished(QNetworkReply*)));
+    manager->get(QNetworkRequest(QUrl(url+"/availability")));
+//    manager->get(QNetworkRequest(QUrl("http://ia2-vialactea.oats.inaf.it/vlkb/availability")));
+
+    qDebug()<<"connected";
+
+}
+
+void VLKBQuery::availReplyFinished (QNetworkReply *reply)
+{
+    qDebug()<<"availReplyFinished";
+    if(reply->error())
+    {
+        qDebug() << "ERROR!";
+        qDebug() << reply->errorString();
+        available=false;
+    }
+    else
+    {
+        QDomDocument doc;
+        doc.setContent(reply->readAll());
+        QDomNodeList list=doc.elementsByTagName("vosi:available");
+        if(list.at(0).toElement().text()=="true")
+        {
+
+            available=true;
+            executeQuery();
+        }
+    }
+    reply->deleteLater();
+}
+
+void VLKBQuery::executeQuery()
+{
+
+    qDebug()<<"executeQuery";
+    qDebug()<<query;
+    manager = new QNetworkAccessManager(this);
+
+    QByteArray postData;
+
+    postData.append("REQUEST=doQuery&");
+    postData.append("VERSION=1.0&");
+    postData.append("LANG=ADQL&");
+    postData.append("FORMAT=tsv&");
+    postData.append("QUERY="+QUrl::toPercentEncoding(query));
+
+    connect ( manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
+              this,
+              SLOT(onAuthenticationRequestSlot(QNetworkReply*,QAuthenticator*)) );
+    // connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(queryReplyFinished(QNetworkReply*)));
+    if(what.compare("bm")==0)
+        connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(queryReplyFinishedBM(QNetworkReply*)));
+    else if(what.compare("model")==0)
+        connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(queryReplyFinishedModel(QNetworkReply*)));
+
+    manager->post(QNetworkRequest(url+"/sync"),postData);
+    //manager->post(QNetworkRequest(QUrl("http://ia2-vialactea.oats.inaf.it:8080/vlkb/sync")),postData);
+
+
+}
+
+
+void VLKBQuery::executoSyncQuery()
+{
+
+    QByteArray postData;
+
+    postData.append("REQUEST=doQuery&");
+    postData.append("VERSION=1.0&");
+    postData.append("LANG=ADQL&");
+    postData.append("FORMAT=tsv&");
+
+
+    QNetworkAccessManager *networkMgr = new QNetworkAccessManager(this);
+    connect ( networkMgr, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
+              this,
+              SLOT(onAuthenticationRequestSlot(QNetworkReply*,QAuthenticator*)) );
+
+    QNetworkReply *reply = networkMgr->get(QNetworkRequest(url+"/sync?REQUEST=doQuery&VERSION=1.0&LANG=ADQL&FORMAT=tsv&QUERY="+QUrl::toPercentEncoding(query)));
+
+    qDebug()<<"pre loop >>> ";
+    QEventLoop loop;
+    QObject::connect(reply, SIGNAL(readyRead()), &loop, SLOT(quit()));
+
+    // Execute the event loop here, now we will wait here until readyRead() signal is emitted
+    // which in turn will trigger event loop quit.
+    loop.exec();
+    qDebug()<<"post loop";
+
+    // Lets print the HTTP response.
+    qDebug( reply->readAll() );
+}
+
+
+void VLKBQuery::onAuthenticationRequestSlot(QNetworkReply *aReply, QAuthenticator *aAuthenticator)
+{
+    qDebug() <<"auth";
+    aAuthenticator->setUser("vialactea");
+    aAuthenticator->setPassword("ia2vlkb");
+}
+
+void VLKBQuery::queryReplyFinishedModel (QNetworkReply *reply)
+{
+
+    qDebug()<<"MODEL QUERY";
+
+    if(reply->error())
+    {
+        qDebug() << "ERROR!";
+        qDebug() << reply->errorString();
+    }
+    else
+    {
+
+
+        QVariant possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+
+        /* We'll deduct if the redirection is valid in the redirectUrl function */
+        urlRedirectedTo = redirectUrl(possibleRedirectUrl.toUrl(), urlRedirectedTo);
+
+        /* If the URL is not empty, we're being redirected. */
+        if(!urlRedirectedTo.isEmpty())
+        {
+
+            qDebug()<<"URL REDIREZIONE: "<< urlRedirectedTo.toString();
+
+            /* We'll do another request to the redirection url. */
+            manager->get(QNetworkRequest(urlRedirectedTo));
+        }
+        else
+        {
+
+            QByteArray bytes = reply->readAll();
+
+            loading->loadingEnded();
+            loading->hide();
+
+
+
+            QVector<QStringList> headerAndValueList;
+            QList<QByteArray> lines = bytes.split('\r\n');
+
+            foreach ( const QByteArray &line, lines)
+            {
+                QString myString (line);
+                headerAndValueList.append( myString.split("\t") );
+            }
+
+            qDebug()<<"headerAndValueList: "<< headerAndValueList;
+
+            sd->setModelFitValue(headerAndValueList, modelColor);
+            /*
+            QString s_data = QString::fromAscii(bytes.data());
+
+            //se inizia per < è un xml, se è xml c'e' stato un errore
+            if(QString::compare(s_data.at(0), "<", Qt::CaseInsensitive) == 0)
+            {
+                QMessageBox::critical(this,"Error", "Error: \n"+bytes);
+            }
+            //Fare un controllo più serio, su quando non ci sono sed restituite
+            else if (bytes.size()!=1187)
+            {
+                QString output_file=QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp");
+                output_file.append("/").append("test_model").append(".dat");
+
+                QFile file(output_file);
+                file.open(QIODevice::WriteOnly);
+                file.write(bytes);
+                file.close();
+
+//                Vialactea_FileLoad *fileload = new Vialactea_FileLoad(output_file,vtkwin);
+  //              fileload->importBandMerged();
+            }
+            else
+            {
+                QMessageBox::information(this,"Alert", "No SED");
+
+            }
+            loading->loadingEnded();
+            loading->hide();
+
+            /*
+               Vialactea_FileLoad *fileload = new Vialactea_FileLoad(output_file);
+               fileload->setVtkWin(vtkwin);
+               fileload->show();
+*/
+
+        }
+        /* Clean up. */
+        reply->deleteLater();
+    }
+
+
+}
+
+void VLKBQuery::queryReplyFinishedBM (QNetworkReply *reply)
+{
+
+    qDebug()<<"BM QUERY";
+
+
+    if(reply->error())
+    {
+        qDebug() << "ERROR!";
+        qDebug() << reply->errorString();
+    }
+    else
+    {
+
+
+        QVariant possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+
+        /* We'll deduct if the redirection is valid in the redirectUrl function */
+        urlRedirectedTo = redirectUrl(possibleRedirectUrl.toUrl(), urlRedirectedTo);
+
+        /* If the URL is not empty, we're being redirected. */
+        if(!urlRedirectedTo.isEmpty())
+        {
+
+            qDebug()<<"URL REDIREZIONE: "<< urlRedirectedTo.toString();
+
+            /* We'll do another request to the redirection url. */
+            manager->get(QNetworkRequest(urlRedirectedTo));
+        }
+        else
+        {
+
+            QByteArray bytes = reply->readAll();
+            qDebug()<<"Size: "<< bytes.size();
+
+            QString s_data = QString::fromLatin1(bytes.data());
+
+            //se inizia per < è un xml, se è xml c'e' stato un errore
+            if(QString::compare(s_data.at(0), "<", Qt::CaseInsensitive) == 0)
+            {
+                QMessageBox::critical(this,"Error", "Error: \n"+bytes);
+            }
+            //Fare un controllo più serio, su quando non ci sono sed restituite
+            else if (bytes.size()!=1187)
+            {
+                QString output_file=QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp");
+                output_file.append("/tmp_download/").append("test_bm").append(".dat");
+
+                QFile file(output_file);
+                file.open(QIODevice::WriteOnly);
+                file.write(bytes);
+                file.close();
+
+                Vialactea_FileLoad *fileload = new Vialactea_FileLoad(output_file,vtkwin);
+                fileload->importBandMerged();
+            }
+            else
+            {
+                QMessageBox::information(this,"Alert", "No SED");
+
+            }
+            loading->loadingEnded();
+            loading->hide();
+
+            /*
+               Vialactea_FileLoad *fileload = new Vialactea_FileLoad(output_file);
+               fileload->setVtkWin(vtkwin);
+               fileload->show();
+*/
+
+        }
+        /* Clean up. */
+        reply->deleteLater();
+    }
+
+}
+
+QUrl VLKBQuery::redirectUrl(const QUrl& possibleRedirectUrl,   const QUrl& oldRedirectUrl) const {
+    QUrl redirectUrl;
+    /*
+* Check if the URL is empty and
+* that we aren't being fooled into a infinite redirect loop.
+* We could also keep track of how many redirects we have been to
+* and set a limit to it, but we'll leave that to you.
+*/
+    if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl) {
+        redirectUrl = possibleRedirectUrl;
+    }
+
+    return redirectUrl;
+}
+
+
+
+
+VLKBQuery::~VLKBQuery()
+{
+    delete ui;
+}
diff --git a/Code/src/vlkbquery.h b/Code/src/vlkbquery.h
new file mode 100644
index 0000000000000000000000000000000000000000..5f0bf983e4753753b2bcea5a4d6966292f82ffaf
--- /dev/null
+++ b/Code/src/vlkbquery.h
@@ -0,0 +1,51 @@
+#ifndef VLKBQUERY_H
+#define VLKBQUERY_H
+
+#include <QWidget>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QAuthenticator>
+#include "loadingwidget.h"
+#include "sedvisualizerplot.h"
+
+namespace Ui {
+class VLKBQuery;
+}
+
+class VLKBQuery : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit VLKBQuery(QString q, vtkwindow_new *v, QString w="bm", QWidget *parent = 0, Qt::GlobalColor color = Qt::blue);
+    ~VLKBQuery();
+
+private:
+    Ui::VLKBQuery *ui;
+    QString query;
+    QString url;
+    QNetworkAccessManager *manager;
+    void connectToVlkb();
+    bool available;
+    void executeQuery();
+    void on_queryPushButton_clicked();
+    QUrl redirectUrl(const QUrl& possibleRedirectUrl, const QUrl& oldRedirectUrl) const;
+    QUrl urlRedirectedTo;
+    void executoSyncQuery();
+    LoadingWidget *loading;
+    vtkwindow_new *vtkwin;
+    QString what;
+    SEDVisualizerPlot *sd;
+    Qt::GlobalColor modelColor;
+
+
+private slots:
+    void availReplyFinished (QNetworkReply *reply);
+    //   void queryReplyFinished (QNetworkReply *reply);
+    void queryReplyFinishedBM (QNetworkReply *reply);
+    void queryReplyFinishedModel (QNetworkReply *reply);
+    void onAuthenticationRequestSlot(QNetworkReply *aReply, QAuthenticator *aAuthenticator);
+
+};
+
+#endif // VLKBQUERY_H
diff --git a/Code/src/vlkbquerycomposer.cpp b/Code/src/vlkbquerycomposer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aa9497702b68ac6fa87fab8fc70d06f104ceaa9d
--- /dev/null
+++ b/Code/src/vlkbquerycomposer.cpp
@@ -0,0 +1,316 @@
+#include "vlkbquerycomposer.h"
+#include "ui_vlkbquerycomposer.h"
+#include <QNetworkAccessManager>
+#include <QUrl>
+#include <QNetworkRequest>
+#include <QDebug>
+#include <QDateTime>
+#include <QFile>
+#include <QDomDocument>
+#include <QDomNodeList>
+#include "vlkbtable.h"
+#include <QMessageBox>
+#include <QDir>
+#include <QAuthenticator>
+#include <QSettings>
+
+VLKBQueryComposer::VLKBQueryComposer(QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::VLKBQueryComposer)
+{
+    ui->setupUi(this);
+
+    QHeaderView* header = ui->columnTableWidget->horizontalHeader();
+    header->setVisible(true);
+    header->sectionResizeMode(QHeaderView::Stretch);
+    
+    m_sSettingsFile = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini");
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+    
+    
+    url=settings.value("vlkbtableurl", "").toString();
+
+
+}
+
+
+void VLKBQueryComposer::tableReplyFinished (QNetworkReply *reply)
+{
+
+    if(reply->error())
+    {
+        qDebug() << "ERROR!";
+        qDebug() << reply->errorString();
+    }
+    else
+    {
+        QDomDocument doc;
+        doc.setContent(reply->readAll());
+        QDomNodeList list=doc.elementsByTagName("table");
+
+        for(int i=0;i<list.size();i++)
+        {
+            VlkbTable *table= new VlkbTable();
+            QDomElement node = list.at(i).toElement();
+            for (int j=0;j<node.childNodes().size();j++)
+            {
+                if(node.childNodes().at(j).toElement().tagName()=="name")
+                {
+                    table->setName(node.childNodes().at(j).toElement().text());
+                }
+                else if(node.childNodes().at(j).toElement().tagName()=="column")
+                {
+                    table->addColumn(node.childNodes().at(j).toElement().childNodes().at(0).toElement().text(),node.childNodes().at(j).toElement().childNodes().at(1).toElement().text());
+                }
+            }
+
+            table_list.append(table);
+            ui->tableListComboBox->addItem(table->getName());
+
+        }
+
+        /*
+        QFile *file = new QFile("/Users/fxbio6600/test.xml");
+        if(file->open(QFile::Append))
+        {
+            file->write(reply->readAll());
+            file->flush();
+            file->close();
+        }
+        delete file;
+*/
+
+    }
+
+    reply->deleteLater();
+}
+
+VLKBQueryComposer::~VLKBQueryComposer()
+{
+    delete ui;
+}
+void VLKBQueryComposer::availReplyFinished (QNetworkReply *reply)
+{
+    if(reply->error())
+    {
+        qDebug() << "ERROR!";
+        qDebug() << reply->errorString();
+
+        ui->tableListComboBox->setEnabled(false);
+        ui->columnTableWidget->setEnabled(false);
+        ui->syncCheckBox->setEnabled(false);
+        ui->queryLangComboBox->setEnabled(false);
+        ui->queryTextEdit->setEnabled(false);
+        ui->okButton->setEnabled(false);
+
+        available=false;
+
+    }
+    else
+    {
+        QDomDocument doc;
+        doc.setContent(reply->readAll());
+
+        QDomNodeList list=doc.elementsByTagName("vosi:available");
+        if(list.at(0).toElement().text()=="true")
+        {
+            ui->tableListComboBox->setEnabled(true);
+            ui->columnTableWidget->setEnabled(true);
+            ui->syncCheckBox->setEnabled(true);
+            ui->queryLangComboBox->setEnabled(true);
+            ui->queryTextEdit->setEnabled(true);
+            ui->okButton->setEnabled(true);
+            available=true;
+            QNetworkAccessManager * manager = new QNetworkAccessManager(this);
+            connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(tableReplyFinished(QNetworkReply*)));
+            manager->get(QNetworkRequest(QUrl(url+"/tables")));
+           // manager->get(QNetworkRequest(QUrl("http://ia2-vialactea.oats.inaf.it:8080/vlkb/tables")),postData);
+
+        }
+    }
+
+    reply->deleteLater();
+
+}
+
+void VLKBQueryComposer::checkAvailability()
+{
+
+    //url=ui->tapUrlLineEdit->text();
+
+    manager = new QNetworkAccessManager(this);
+
+    connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(availReplyFinished(QNetworkReply*)));
+    manager->get(QNetworkRequest(QUrl(url+"/availability")));
+   // manager->get(QNetworkRequest(QUrl("http://ia2-vialactea.oats.inaf.it/vlkb/availability")));
+}
+
+void VLKBQueryComposer::on_connectButton_clicked()
+{
+
+    //url=ui->tapUrlLineEdit->text();
+
+    manager = new QNetworkAccessManager(this);
+
+    connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(availReplyFinished(QNetworkReply*)));
+    manager->get(QNetworkRequest(QUrl(url+"/availability")));
+   // manager->get(QNetworkRequest(QUrl("http://ia2-vialactea.oats.inaf.it/vlkb/availability")));
+
+
+    /*
+     *
+    checkAvailability();
+    qDebug()<<"############################################# pre Available";
+
+    if (available)
+    {
+        qDebug()<<"#############################################Available";
+        QNetworkAccessManager * manager = new QNetworkAccessManager(this);
+        connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(tableReplyFinished(QNetworkReply*)));
+        manager->get(QNetworkRequest(QUrl(url+"/tables")));
+    }
+*/
+}
+
+void VLKBQueryComposer::on_tableListComboBox_currentIndexChanged(int index)
+{
+
+    int row;
+
+    for(int i=0;i<table_list.at(index)->getColumnList().size();i++)
+    {
+
+        row= ui->columnTableWidget->model()->rowCount();
+        ui->columnTableWidget->insertRow(row);
+
+
+        QTableWidgetItem *name_item = new QTableWidgetItem();
+        name_item->setText(table_list.at(index)->getColumnList().at(i).name );
+        ui->columnTableWidget->setItem(row,0,name_item);
+
+
+        QTableWidgetItem *datatype_item = new QTableWidgetItem();
+        datatype_item->setText(table_list.at(index)->getColumnList().at(i).type );
+        ui->columnTableWidget->setItem(row,1,datatype_item);
+
+
+
+    }
+}
+
+void VLKBQueryComposer::on_okButton_clicked()
+{
+
+    manager = new QNetworkAccessManager(this);
+
+    QByteArray postData;
+
+    postData.append("REQUEST=doQuery&");
+    postData.append("VERSION=1.0&");
+    postData.append("LANG=ADQL&");
+    postData.append("FORMAT=tsv&");
+
+    //WHERE (glon >=316.313 and glon <=317.131 ) AND (glat>=-0.34358 and glat <= 0.3113)
+
+    postData.append("QUERY=SELECT%20*%20FROM%20vlkb_compactsources.band500um%20WHERE%20(glon>=316.009%20and%20glon<=317.178)%20AND%20(glat>=-0.78796%20and%20glat<=0.68552)");
+
+    connect ( manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
+              this,
+              SLOT(onAuthenticationRequestSlot(QNetworkReply*,QAuthenticator*)) );
+    connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(queryReplyFinished(QNetworkReply*)));
+    manager->post(QNetworkRequest(url+"/sync"),postData);
+   // manager->post(QNetworkRequest(QUrl("http://ia2-vialactea.oats.inaf.it:8080/vlkb/sync")),postData);
+
+
+}
+
+void VLKBQueryComposer::onAuthenticationRequestSlot(QNetworkReply *aReply, QAuthenticator *aAuthenticator)
+{
+    qDebug() <<"auth";
+    aAuthenticator->setUser("vialactea");
+    aAuthenticator->setPassword("ia2vlkb");
+}
+
+
+void VLKBQueryComposer::queryReplyFinished (QNetworkReply *reply)
+{
+    if(reply->error())
+    {
+        qDebug() << "ERROR!";
+        qDebug() << reply->errorString();
+    }
+    else
+    {
+
+        qDebug()<<"else";
+
+        QVariant possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+
+        /* We'll deduct if the redirection is valid in the redirectUrl function */
+        urlRedirectedTo = redirectUrl(possibleRedirectUrl.toUrl(), urlRedirectedTo);
+
+        /* If the URL is not empty, we're being redirected. */
+        if(!urlRedirectedTo.isEmpty())
+        {
+
+            qDebug()<<"URL REDIREZIONE: "<< urlRedirectedTo.toString();
+
+            /* We'll do another request to the redirection url. */
+            manager->get(QNetworkRequest(urlRedirectedTo));
+        }
+        else
+        {
+            qDebug()<<"END REDIRECT";
+
+            QByteArray bytes = reply->readAll();
+
+            /*
+                QString str(bytes);
+
+               // qDebug()<<str.size();
+
+                str.remove(QChar::Null);
+
+                qDebug()<<str;*/
+
+            qDebug()<< bytes;
+
+
+            QFile file("/Users/fxbio6600/test_out.txt");
+            file.open(QIODevice::WriteOnly);
+            file.write(bytes);
+            file.close();
+
+            /*
+                    QString str = QString::fromUtf8(bytes.data(), bytes.size());
+
+                    for(int i=0;i<bytes.size();i++)
+                        qDebug()<<"i: "<<i<<" - "<<bytes[i];
+                */
+
+        }
+        /* Clean up. */
+        reply->deleteLater();
+
+    }
+
+}
+
+
+
+
+
+QUrl VLKBQueryComposer::redirectUrl(const QUrl& possibleRedirectUrl,   const QUrl& oldRedirectUrl) const {
+    QUrl redirectUrl;
+    /*
+* Check if the URL is empty and
+* that we aren't being fooled into a infinite redirect loop.
+* We could also keep track of how many redirects we have been to
+* and set a limit to it, but we'll leave that to you.
+*/
+    if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl) {
+        redirectUrl = possibleRedirectUrl;
+    }
+
+    return redirectUrl;
+}
diff --git a/Code/src/vlkbquerycomposer.h b/Code/src/vlkbquerycomposer.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a92b5012468aa232d93c97773e47d7945ac0b1d
--- /dev/null
+++ b/Code/src/vlkbquerycomposer.h
@@ -0,0 +1,46 @@
+#ifndef VLKBQUERYCOMPOSER_H
+#define VLKBQUERYCOMPOSER_H
+
+#include <QWidget>
+#include <QNetworkReply>
+#include "vlkbtable.h"
+
+namespace Ui {
+class VLKBQueryComposer;
+}
+
+class VLKBQueryComposer : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit VLKBQueryComposer(QWidget *parent = 0);
+    ~VLKBQueryComposer();
+
+private:
+    Ui::VLKBQueryComposer *ui;
+    QString url;
+    QString m_sSettingsFile;
+    bool available=false;
+    QList<VlkbTable *> table_list;
+     QNetworkAccessManager * manager;
+     QUrl urlRedirectedTo;
+
+public slots:
+    void tableReplyFinished (QNetworkReply *reply);
+    void availReplyFinished (QNetworkReply *reply);
+
+
+private slots:
+    void on_connectButton_clicked();
+    void checkAvailability();
+
+    void on_tableListComboBox_currentIndexChanged(int index);
+    void on_okButton_clicked();
+    void onAuthenticationRequestSlot(QNetworkReply *aReply, QAuthenticator *aAuthenticator);
+    void queryReplyFinished (QNetworkReply *reply);
+    QUrl redirectUrl(const QUrl& possibleRedirectUrl, const QUrl& oldRedirectUrl) const;
+
+};
+
+#endif // VLKBQUERYCOMPOSER_H
diff --git a/Code/src/vlkbsimplequerycomposer.cpp b/Code/src/vlkbsimplequerycomposer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..372b3b01993ea8092375070b9899ee6e3f52a077
--- /dev/null
+++ b/Code/src/vlkbsimplequerycomposer.cpp
@@ -0,0 +1,618 @@
+#include "vlkbsimplequerycomposer.h"
+#include "ui_vlkbsimplequerycomposer.h"
+#include <QUrl>
+#include <QNetworkRequest>
+#include <QtDebug>
+#include <QDomDocument>
+#include <QAuthenticator>
+#include <QFile>
+#include <QDir>
+#include "vialactea_fileload.h"
+#include "singleton.h"
+#include <QMessageBox>
+#include <QFileDialog>
+#include <QSettings>
+
+VLKBSimpleQueryComposer::VLKBSimpleQueryComposer(vtkwindow_new *v, QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::VLKBSimpleQueryComposer)
+{
+    ui->setupUi(this);
+    vtkwin=v;
+    loading = new LoadingWidget();
+    isConnected=false;
+    isFilament=false;
+    is3dSelections=false;
+    isBubble=false;
+
+    m_sSettingsFile = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini");
+    QSettings settings(m_sSettingsFile, QSettings::NativeFormat);
+
+
+    url=settings.value("vlkbtableurl", "").toString();
+    ui->vlkbUrlLineEdit->setText(url);
+
+    on_connectPushButton_clicked();
+
+}
+
+VLKBSimpleQueryComposer::~VLKBSimpleQueryComposer()
+{
+    delete ui;
+}
+
+void VLKBSimpleQueryComposer::setVtkWindow(vtkwindow_new *v)
+{
+    vtkwin=v;
+}
+void VLKBSimpleQueryComposer::setIsFilament()
+{
+    isFilament=true;
+    ui->tableGroupBox->hide();
+}
+
+void VLKBSimpleQueryComposer::setIsBubble()
+{
+    isBubble=true;
+    ui->tableGroupBox->hide();
+}
+
+
+void VLKBSimpleQueryComposer::setIs3dSelections()
+{
+    is3dSelections=true;
+    ui->tableGroupBox->hide();
+}
+
+void VLKBSimpleQueryComposer::setLongitude(float long1, float long2)
+{
+    float min=long1;
+    float max=long2;
+
+    if(long1>=long2)
+    {
+        max=long1;
+        min=long2;
+    }
+
+    ui->longMaxLineEdit->setText( QString::number( max) );
+    ui->longMinLineEdit->setText( QString::number(min) );
+
+}
+
+void VLKBSimpleQueryComposer::setLatitude(float lat1, float lat2)
+{
+    float min=lat1;
+    float max=lat2;
+
+    if(lat1>=lat2)
+    {
+        max=lat1;
+        min=lat2;
+    }
+
+    ui->latMaxLineEdit->setText( QString::number( max) );
+    ui->latMinLineEdit->setText( QString::number(min) );
+
+}
+
+
+void VLKBSimpleQueryComposer::on_connectPushButton_clicked()
+{
+
+    if(!isConnected)
+    {
+
+        loading = new LoadingWidget();
+        loading->init();
+        loading->setFileName("Connecting to VLKB...");
+        loading ->show();
+        loading->activateWindow();
+        loading->setFocus();
+
+        //url=ui->vlkbUrlLineEdit->text();
+
+
+
+        manager = new QNetworkAccessManager(this);
+
+        connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(availReplyFinished(QNetworkReply*)));
+        manager->get(QNetworkRequest(QUrl(url+"/availability")));
+       // manager->get(QNetworkRequest(QUrl("http://ia2-vialactea.oats.inaf.it/vlkb/availability")));
+
+    }
+    else
+    {
+
+        ui->connectPushButton->setText("Connect");
+        ui->vlkbUrlLineEdit->setEnabled(true);
+
+
+        ui->tableComboBox->setEnabled(false);
+        ui->longMaxLineEdit->setEnabled(false);
+        ui->longMaxLineEdit->setEnabled(false);
+        ui->latMinLineEdit->setEnabled(false);
+        ui->latMaxLineEdit->setEnabled(false);
+        ui->queryPushButton->setEnabled(false);
+        ui->outputNameLineEdit->setEnabled(false);
+
+        isConnected=false;
+    }
+
+}
+
+void VLKBSimpleQueryComposer::availReplyFinished (QNetworkReply *reply)
+{
+
+    qDebug()<<"availReplyFinished";
+
+    if(reply->error())
+    {
+
+        QMessageBox::critical(this,"Error", "Error: \n"+reply->errorString());
+
+        ui->tableComboBox->setEnabled(false);
+        ui->longMaxLineEdit->setEnabled(false);
+        ui->longMaxLineEdit->setEnabled(false);
+        ui->latMinLineEdit->setEnabled(false);
+        ui->latMaxLineEdit->setEnabled(false);
+        ui->queryPushButton->setEnabled(false);
+        ui->outputNameLineEdit->setEnabled(false);
+
+        available=false;
+
+    }
+    else
+    {
+        QDomDocument doc;
+        doc.setContent(reply->readAll());
+        qDebug()<<doc.toByteArray();
+        QDomNodeList list=doc.elementsByTagName("vosi:available");
+        qDebug()<<list.at(0).toElement().text();
+
+        if(list.at(0).toElement().text()=="true")
+        {
+            qDebug()<<"entro";
+
+            available=true;
+            qDebug()<<"isFilament "<<isFilament;
+
+            if(!isFilament)
+            {
+                qDebug()<<"cerco tabelle";
+                QNetworkAccessManager * manager = new QNetworkAccessManager(this);
+
+                connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(tableReplyFinished(QNetworkReply*)));
+                manager->get(QNetworkRequest(QUrl(url+"/tables")));
+                //manager->get(QNetworkRequest(QUrl("http://ia2-vialactea.oats.inaf.it:8080/vlkb/tables")),postData);
+
+
+            }
+            else
+            {
+                ui->tableComboBox->setEnabled(true);
+                ui->longMaxLineEdit->setEnabled(true);
+                ui->longMinLineEdit->setEnabled(true);
+                ui->latMinLineEdit->setEnabled(true);
+                ui->latMaxLineEdit->setEnabled(true);
+                ui->queryPushButton->setEnabled(true);
+
+                ui->connectPushButton->setText("Disconnect");
+                ui->vlkbUrlLineEdit->setEnabled(false);
+                isConnected=true;
+
+                loading->close();
+                reply->deleteLater();
+            }
+        }
+    }
+
+    reply->deleteLater();
+
+}
+
+
+void VLKBSimpleQueryComposer::tableReplyFinished (QNetworkReply *reply)
+{
+
+    qDebug()<<"tableReplyFinished";
+    if(reply->error())
+    {
+        QMessageBox::critical(this,"Error", "Error: \n"+reply->errorString());
+    }
+    else
+    {
+        QDomDocument doc;
+        doc.setContent(reply->readAll());
+        QDomNodeList list=doc.elementsByTagName("table");
+
+        ui->tableComboBox->clear();
+        for(int i=0;i<list.size();i++)
+        {
+            VlkbTable *table= new VlkbTable();
+            QDomElement node = list.at(i).toElement();
+            QString bandTableName;
+
+            for (int j=0;j<node.childNodes().size();j++)
+            {
+                if(node.childNodes().at(j).toElement().tagName()=="name")
+                {
+                    table->setName(node.childNodes().at(j).toElement().text());
+                    if(table->getName().contains("vlkb_compactsources.band") && ( table->getName().right(2).compare("um") ==0) )
+                    {
+                        bandTableName = table->getName().mid( table->getName().lastIndexOf(".")+1 );
+                        table->setShortname( bandTableName );
+                        ui->tableComboBox->addItem(table->getShortName());
+
+                        qDebug()<<""<<table->getShortName();
+
+
+                    }
+                }
+                else if(node.childNodes().at(j).toElement().tagName()=="column")
+                {
+                    table->addColumn(node.childNodes().at(j).toElement().childNodes().at(0).toElement().text(),node.childNodes().at(j).toElement().childNodes().at(1).toElement().text());
+                }
+            }
+
+            table_list.append(table);
+            //  ui->tableComboBox->addItem(table->getName());
+
+        }
+
+        ui->tableComboBox->addItem("bandmerged");
+
+        int index =  ui->tableComboBox->findText("bandmerged");
+        if ( index != -1 ) { // -1 for not found
+            ui->tableComboBox->setCurrentIndex(index);
+        }
+
+
+        ui->tableComboBox->setEnabled(true);
+        ui->longMaxLineEdit->setEnabled(true);
+        ui->longMinLineEdit->setEnabled(true);
+        ui->latMinLineEdit->setEnabled(true);
+        ui->latMaxLineEdit->setEnabled(true);
+        ui->queryPushButton->setEnabled(true);
+
+        ui->connectPushButton->setText("Disconnect");
+        ui->vlkbUrlLineEdit->setEnabled(false);
+        isConnected=true;
+        //non funziona, da verificare
+        //ui->tableComboBox->addItem("AllBands");
+
+    }
+    loading->close();
+
+    reply->deleteLater();
+
+}
+
+void VLKBSimpleQueryComposer::on_queryPushButton_clicked()
+{
+    doQuery(ui->tableComboBox->currentText());
+    this->close();
+
+    /*
+    for(int i=0; i<ui->tableComboBox->count(); i++)
+    {
+       qDebug()<<ui->tableComboBox->itemText(i)<<" -"<<ui->tableComboBox->itemData(i).value<bool>();
+       if(ui->tableComboBox->itemData(i).value<bool>())
+            doQuery(ui->tableComboBox->itemText(i));
+    }
+    */
+}
+
+void VLKBSimpleQueryComposer::doQuery(QString band)
+{
+    loading->init();
+    loading->setFileName("Retrieving dataset from VLKB");
+
+    loading ->show();
+    // loading->activateWindow();
+
+    manager = new QNetworkAccessManager(this);
+
+    QByteArray postData;
+
+    postData.append("REQUEST=doQuery&");
+    postData.append("VERSION=1.0&");
+    postData.append("LANG=ADQL&");
+    postData.append("FORMAT=tsv&");
+
+    if(isFilament)
+    {
+/*
+
+        //query funzionante
+        QString query="SELECT * FROM vlkb_filaments.filaments WHERE ( glon>="+ui->longMinLineEdit->text()+" and glon<="+ui->longMaxLineEdit->text()+") AND ";
+        query+= "(glat>="+ui->latMinLineEdit->text()+" and glat<="+ui->latMaxLineEdit->text()+")";
+
+*/
+
+        QString query="SELECT f.idfil_mos as idfil_mos, f.contour as contour, f.glon as glon, f.glat as glat, b.contour as branches_contour, b.contour1d as branches_contour1d, b.contour_new as branches_contour_new, b.flagspine as flagspine_branches  FROM vlkb_filaments.filaments as f JOIN vlkb_filaments.branches as b on f.idfil_mos = b.idfil_mos";
+        query += " WHERE( glon>="+ui->longMinLineEdit->text()+" and glon<="+ui->longMaxLineEdit->text()+") AND ";
+        query += "(glat>="+ui->latMinLineEdit->text()+" and glat<="+ui->latMaxLineEdit->text()+")";
+
+        postData.append("QUERY="+QUrl::toPercentEncoding(query));
+
+        qDebug()<<query;
+    }
+    else if(isBubble)
+    {
+        QString query="SELECT * FROM vlkb_filaments.bubbles WHERE ( glon_cen>="+ui->longMinLineEdit->text()+" and glon_cen<="+ui->longMaxLineEdit->text()+") AND ";
+        query+= "(glat_cen>="+ui->latMinLineEdit->text()+" and glat_cen<="+ui->latMaxLineEdit->text()+")";
+        postData.append("QUERY="+QUrl::toPercentEncoding(query));
+
+        qDebug()<<query;
+    }
+    else if(is3dSelections)
+    {
+        //old
+        /*
+        QString query="SELECT * FROM vlkb_compactsources.bandmerged_sed_view WHERE ";
+        query += "(( glon250>="+ui->longMinLineEdit->text()+" and glon250<="+ui->longMaxLineEdit->text()+") AND ";
+        query+= "(glat250>="+ui->latMinLineEdit->text()+" and glat250<="+ui->latMaxLineEdit->text()+")";
+        query += " AND x !=0 )";
+
+        qDebug()<<query;
+        postData.append("QUERY="+QUrl::toPercentEncoding(query));
+        */
+
+        QString query="SELECT dist*cos(radians(glon)+3.14159265359/2) as x, dist*sin(radians(glon)+3.14159265359/2) as y,dist*tan(radians(glat)) as z, * FROM vlkb_compactsources.props_dist WHERE ";
+        query += "(( glon>="+ui->longMinLineEdit->text()+" and glon<="+ui->longMaxLineEdit->text()+") AND ";
+        query+= "(glat>="+ui->latMinLineEdit->text()+" and glat<="+ui->latMaxLineEdit->text()+"))";
+
+        qDebug()<<query;
+        postData.append("QUERY="+QUrl::toPercentEncoding(query));
+
+
+    }
+    else
+    {
+        if(band.compare("bandmerged") ==0)
+        {
+            isBandmerged=true;
+           /*
+           // QString query="SELECT DISTINCT * FROM vlkb_compactsources.bandmerged_sed_view WHERE";
+            query+= "(( glon500>="+ui->longMinLineEdit->text()+" and glon500<="+ui->longMaxLineEdit->text()+") AND ";
+            query+= "(glat500>="+ui->latMinLineEdit->text()+" and glat500<="+ui->latMaxLineEdit->text()+"))";
+
+            query+= " OR (( glon350>="+ui->longMinLineEdit->text()+" and glon350<="+ui->longMaxLineEdit->text()+") AND ";
+            query+= "(glat350>="+ui->latMinLineEdit->text()+" and glat350<="+ui->latMaxLineEdit->text()+"))";
+
+            query+= " OR (( glon250>="+ui->longMinLineEdit->text()+" and glon250<="+ui->longMaxLineEdit->text()+") AND ";
+            query+= "(glat250>="+ui->latMinLineEdit->text()+" and glat250<="+ui->latMaxLineEdit->text()+"))";
+
+            query+= " OR (( glon160>="+ui->longMinLineEdit->text()+" and glon160<="+ui->longMaxLineEdit->text()+") AND ";
+            query+= "(glat160>="+ui->latMinLineEdit->text()+" and glat160<="+ui->latMaxLineEdit->text()+"))";
+
+            query+= " OR (( glon70>="+ui->longMinLineEdit->text()+" and glon70<="+ui->longMaxLineEdit->text()+") AND ";
+            query+= "(glat70>="+ui->latMinLineEdit->text()+" and glat70<="+ui->latMaxLineEdit->text()+"))";
+*/
+
+            QString query="SELECT DISTINCT * FROM vlkb_compactsources.sed_view_final WHERE ";
+            query+= "(( glonft>="+ui->longMinLineEdit->text()+" and glonft<="+ui->longMaxLineEdit->text()+") AND ";
+            query+= "(glatft>="+ui->latMinLineEdit->text()+" and glatft<="+ui->latMaxLineEdit->text()+"))";
+
+            qDebug()<<" query REMOTE: "<<query;
+
+            postData.append("QUERY="+QUrl::toPercentEncoding(query));
+
+        }
+        else
+        {
+            isBandmerged=false;
+
+            //aggiungo al table name 'vlkb_compactsources.'
+            QString query="SELECT * FROM vlkb_compactsources."+band+" WHERE ( glon>="+ui->longMinLineEdit->text()+" and glon<="+ui->longMaxLineEdit->text()+") AND ";
+            query+= "(glat>="+ui->latMinLineEdit->text()+" and glat<="+ui->latMaxLineEdit->text()+")";
+            postData.append("QUERY="+QUrl::toPercentEncoding(query));
+        }
+    }
+    connect ( manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
+              this,
+              SLOT(onAuthenticationRequestSlot(QNetworkReply*,QAuthenticator*)) );
+
+    connect(manager, SIGNAL(finished(QNetworkReply*)),  this, SLOT(queryReplyFinished(QNetworkReply*)));
+
+    manager->post(QNetworkRequest(url+"/sync"),postData);
+    //manager->post(QNetworkRequest(QUrl("http://ia2-vialactea.oats.inaf.it:8080/vlkb/sync")),postData);
+    qDebug()<<" query url: "<<url;
+
+}
+
+
+void VLKBSimpleQueryComposer::onAuthenticationRequestSlot(QNetworkReply *aReply, QAuthenticator *aAuthenticator)
+{
+    aAuthenticator->setUser("vialactea");
+    aAuthenticator->setPassword("ia2vlkb");
+}
+
+
+void VLKBSimpleQueryComposer::queryReplyFinished (QNetworkReply *reply)
+{
+
+    if(reply->error())
+    {
+        QMessageBox::critical(this,"Error", "Error: \n"+reply->errorString());
+    }
+    else
+    {
+        QVariant possibleRedirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+
+        /* We'll deduct if the redirection is valid in the redirectUrl function */
+        urlRedirectedTo = redirectUrl(possibleRedirectUrl.toUrl(), urlRedirectedTo);
+
+        /* If the URL is not empty, we're being redirected. */
+        if(!urlRedirectedTo.isEmpty())
+        {
+            /* We'll do another request to the redirection url. */
+            manager->get(QNetworkRequest(urlRedirectedTo));
+        }
+        else
+        {
+
+            QByteArray bytes = reply->readAll();
+            QString s_data = QString::fromLatin1(bytes.data());
+
+            //se inizia per '<' è un xml, se è xml c'e' stato un errore
+            if(QString::compare(s_data.at(0), "<", Qt::CaseInsensitive) == 0)
+            {
+                QMessageBox::critical(this,"Error", "Error: \n"+bytes);
+            }
+            else
+            {
+
+                QString output_file;
+                if ( ui->savedatasetCheckBox->isChecked())
+                {
+                    output_file=ui->outputNameLineEdit->text();
+                }
+                else
+                {
+                    output_file=QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp");
+                    output_file.append("/tmp_download/").append("temp_dataset.dat");
+                }
+
+                QFile file(output_file);
+                file.open(QIODevice::WriteOnly);
+                file.write(bytes);
+                file.close();
+
+                QFile fileRead(output_file);
+                fileRead.open(QIODevice::ReadOnly);
+                fileRead.readLine();
+                QString check=fileRead.readLine();
+                fileRead.close();
+
+                if (check!="")
+                {
+                    Vialactea_FileLoad *fileload = new Vialactea_FileLoad(output_file,true);
+                    fileload->setVtkWin(vtkwin);
+
+                    if(isFilament)
+                    {
+                        fileload->importFilaments();
+                    }
+                    else if(isBubble)
+                    {
+                        fileload->importBubbles();
+                    }
+                    else if (is3dSelections)
+                    {
+                        fileload->import3dSelection();
+                    }
+                    else
+                    {
+                        if( !isBandmerged )
+                        {
+                            qDebug()<<"******1";
+                            fileload->setCatalogueActive();
+
+                            QString w=  ui->tableComboBox->currentText().replace("band","").replace("um","");
+
+                            fileload->setWavelength(w);
+                            fileload->on_okPushButton_clicked();
+                            //fileload->show();
+                        }
+                        else
+                        {
+                            qDebug()<<"******2";
+
+                            qDebug()<<"else";
+                            //fileload->importBandMerged();
+                            fileload->setCatalogueActive();
+                            fileload->setWavelength("all");
+
+                            fileload->on_okPushButton_clicked();
+
+                        }
+                    }
+                    //delete temp file
+                    if (! ui->savedatasetCheckBox->isChecked())
+                        QFile::remove(output_file);
+                }
+                else
+                {
+                    QMessageBox::critical(this,"Error", "Empty table. Table contained no rows");
+                }
+
+            }
+            loading->loadingEnded();
+            loading->hide();
+
+        }
+        /* Clean up. */
+        reply->deleteLater();
+
+    }
+
+}
+
+QUrl VLKBSimpleQueryComposer::redirectUrl(const QUrl& possibleRedirectUrl,   const QUrl& oldRedirectUrl) const {
+    QUrl redirectUrl;
+    /*
+* Check if the URL is empty and
+* that we aren't being fooled into a infinite redirect loop.
+* We could also keep track of how many redirects we have been to
+* and set a limit to it, but we'll leave that to you.
+*/
+    if(!possibleRedirectUrl.isEmpty() && possibleRedirectUrl != oldRedirectUrl) {
+        redirectUrl = possibleRedirectUrl;
+    }
+
+    return redirectUrl;
+}
+
+
+void VLKBSimpleQueryComposer::on_tableComboBox_currentIndexChanged(int index)
+{
+    ui->outputNameLineEdit->setText( ui->tableComboBox->itemText(index)+".dat" );
+    ui->outputNameLineEdit->setFocus();
+}
+
+void VLKBSimpleQueryComposer::on_savedatasetCheckBox_clicked(bool checked)
+{
+    if (checked)
+    {
+        ui->navigateFSButtono->setEnabled(true);
+        ui->outputNameLabel->setEnabled(true);
+        ui->outputNameLineEdit->setEnabled(true);
+    }
+    else
+    {
+        ui->navigateFSButtono->setEnabled(false);
+        ui->outputNameLabel->setEnabled(false);
+        ui->outputNameLineEdit->setEnabled(false);
+
+    }
+
+}
+
+void VLKBSimpleQueryComposer::on_navigateFSButtono_clicked()
+{
+
+    QString fn = QFileDialog::getSaveFileName(this, "Save as...", QString(), "dataset files (*.dat)");
+
+    if (!fn.isEmpty() && ! fn.endsWith(".dat", Qt::CaseInsensitive)  )
+        fn += ".dat"; // default
+    ui->outputNameLineEdit->setText(fn);
+
+}
+
+void VLKBSimpleQueryComposer::closeSlot()
+{
+    qDebug()<<"close slot";
+}
+
+void VLKBSimpleQueryComposer::closeEvent ( QCloseEvent * event )
+{
+    //if(!is3dSelections)
+    //{
+    if(vtkwin!=0){
+        vtkwin->activateWindow();
+    }
+    //    vtkwin->show();
+    //}
+}
diff --git a/Code/src/vlkbsimplequerycomposer.h b/Code/src/vlkbsimplequerycomposer.h
new file mode 100644
index 0000000000000000000000000000000000000000..c9a93da6a06adc80746cb9f1cab82b7a4b808b25
--- /dev/null
+++ b/Code/src/vlkbsimplequerycomposer.h
@@ -0,0 +1,62 @@
+#ifndef VLKBSIMPLEQUERYCOMPOSER_H
+#define VLKBSIMPLEQUERYCOMPOSER_H
+
+#include <QWidget>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include "vlkbtable.h"
+#include "loadingwidget.h"
+#include "vtkwindow_new.h"
+namespace Ui {
+class VLKBSimpleQueryComposer;
+}
+
+class VLKBSimpleQueryComposer : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit VLKBSimpleQueryComposer(vtkwindow_new *v, QWidget *parent = 0);
+    ~VLKBSimpleQueryComposer();
+    void setLongitude(float long1, float long2);
+    void setLatitude(float lat1, float lat2);
+    void setVtkWindow(vtkwindow_new *v);
+    void setIsFilament();
+    void setIs3dSelections();
+    void setIsBubble();
+
+
+private slots:
+    void on_connectPushButton_clicked();
+    void availReplyFinished (QNetworkReply *reply);
+    void tableReplyFinished (QNetworkReply *reply);
+    void queryReplyFinished (QNetworkReply *reply);
+    void onAuthenticationRequestSlot(QNetworkReply *aReply, QAuthenticator *aAuthenticator);
+    void on_queryPushButton_clicked();
+    QUrl redirectUrl(const QUrl& possibleRedirectUrl, const QUrl& oldRedirectUrl) const;
+    void on_tableComboBox_currentIndexChanged(int index);
+    void on_savedatasetCheckBox_clicked(bool checked);
+    void on_navigateFSButtono_clicked();
+    void closeSlot();
+    void closeEvent ( QCloseEvent * event );
+
+private:
+    Ui::VLKBSimpleQueryComposer *ui;
+    QNetworkAccessManager *manager;
+    QString url;
+    QString m_sSettingsFile;
+
+    bool available;
+    QList<VlkbTable *> table_list;
+    QUrl urlRedirectedTo;
+    LoadingWidget *loading;
+    vtkwindow_new *vtkwin;
+    void doQuery(QString band);
+    bool isConnected;
+    bool isBandmerged;
+    bool isFilament;
+    bool isBubble;
+    bool is3dSelections;
+};
+
+#endif // VLKBSIMPLEQUERYCOMPOSER_H
diff --git a/Code/src/vlkbtable.cpp b/Code/src/vlkbtable.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..776e9fff21d93e238f00f6d5fc4c3f799a21cf46
--- /dev/null
+++ b/Code/src/vlkbtable.cpp
@@ -0,0 +1,18 @@
+#include "vlkbtable.h"
+
+VlkbTable::VlkbTable()
+{
+
+
+}
+
+void VlkbTable::addColumn(QString name, QString type)
+{
+    struct column c;
+    c.name=name;
+    c.type=type;
+
+    column_list.append(c);
+
+}
+
diff --git a/Code/src/vlkbtable.h b/Code/src/vlkbtable.h
new file mode 100644
index 0000000000000000000000000000000000000000..40a2512fab4e390f69753fbca9ebcc87c3513f91
--- /dev/null
+++ b/Code/src/vlkbtable.h
@@ -0,0 +1,29 @@
+#ifndef VLKBTABLE_H
+#define VLKBTABLE_H
+#include <QString>
+#include <QList>
+
+struct column{
+    QString name;
+    QString type;
+};
+
+class VlkbTable
+{
+public:
+    VlkbTable();
+    void setName(QString n){name=n;}
+    void setShortname(QString n){shortname=n;}
+    QString getName(){return name;}
+    QString getShortName(){return shortname;}
+
+    void addColumn(QString name, QString type);
+    QList<column> getColumnList(){return column_list; }
+private:
+    QString name;
+    QString shortname;
+    QList<column> column_list;
+
+};
+
+#endif // VLKBTABLE_H
diff --git a/Code/src/vosamp.cpp b/Code/src/vosamp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0dafdb13bebe01dad3750224c9aabf5d363ed626
--- /dev/null
+++ b/Code/src/vosamp.cpp
@@ -0,0 +1,114 @@
+#include "vosamp.h"
+#include "libsamp/samp.h"
+#include <QDebug>
+
+
+#define	opt(s)		(s?s:"INDEF")
+
+int	sampH = 0;			/* samp struct handle	*/
+
+static char *name	= "VisIVO";		/* metadata		*/
+static char *descr	= "VisIVO - Vialactea";
+char   *filt_sender	= NULL;
+char   *filt_mtype	= NULL;
+char   *mtype		= NULL;
+extern "C"
+{
+static char *vos_rewriteURL (char *in);
+extern char *vos_toURL (char *arg);
+}
+
+void msg_handler (char *sender, char *msg_id, int params)
+{
+    qDebug()<<"sender "<<sender;
+    qDebug()<<"msg_id "<<msg_id;
+    qDebug()<<"params "<<params;
+/*
+    if (filt_sender && strcasecmp (filt_sender, sender))
+        return;
+    if (filt_mtype && strcasecmp (filt_mtype, mtype))
+        return;
+
+    samp_printMessage (mtype, sender, msg_id, params);
+*/
+  }
+
+
+
+vosamp::vosamp()
+{
+
+    sampH = sampInit (name, descr);
+    /*
+     * Set up some local application metadata values.  These tell the
+     *  Hub and other applications who we are.
+     */
+    samp_Metadata (sampH, "author.email", "fabio.vitello@inaf.it");
+    samp_Metadata (sampH, "author.name", "Fabio Vitello");
+    samp_Metadata (sampH, "samp.icon.url", "http://visivo.oact.inaf.it/logo.png");
+
+
+    /*
+     *  Subscribe to all message types and install the message handler.
+     */
+    samp_Subscribe (sampH, "table.load.votable", (void *)msg_handler);
+
+    /*
+     * Register with the Hub and begin messaging .....
+     */
+    sampStartup (sampH);
+
+    int stat=0;
+    /*
+
+    qDebug()<<"send status: "<<stat;
+    stat = samp_tableLoadFITS (samp, "all",
+                               "http://visivo.oact.inaf.it/thyco9999.fits",
+                               "test1",
+                               "thyco9999.fits");
+
+
+    qDebug()<<"send status: "<<stat;
+*/
+
+
+    //    samp_Unsubscribe(samp, "*");
+
+}
+
+
+
+vosamp::~vosamp()
+{
+
+}
+
+void vosamp::unregister()
+{
+    qDebug()<<" unsubscrive from samp hub";
+    samp_Unsubscribe(sampH, "*");
+    sampShutdown(sampH);
+
+}
+
+void vosamp::sendFitsImage(char* f, char* t)
+{
+    int  stat = samp_imageLoadFITS (sampH, t,
+                                    vos_toURL(f),		/* URL/file 	*/
+                                    "imgId", 		/* imgId	*/
+                                    "name");		/* name		*/
+}
+
+char* vosamp::getClientsList()
+{
+    char *clist = NULL;
+
+    samp_mapClients (sampH);
+
+    clist = samp_getClients (sampH);
+    return clist;
+
+}
+
+
+
diff --git a/Code/src/vosamp.h b/Code/src/vosamp.h
new file mode 100644
index 0000000000000000000000000000000000000000..b9e4c0e53492d8d0655bbd943809ad128cffd3a1
--- /dev/null
+++ b/Code/src/vosamp.h
@@ -0,0 +1,17 @@
+#ifndef VOSAMP_H
+#define VOSAMP_H
+
+
+class vosamp
+{
+public:
+    vosamp();
+    ~vosamp();
+    void unregister();
+    void sendFitsImage(char *f, char *t="all");
+    char* getClientsList();
+
+
+};
+
+#endif // VOSAMP_H
diff --git a/Code/src/vsobjectdesktop.cpp b/Code/src/vsobjectdesktop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ad43571752954dc285b0b1c5e71131f52c1fc467
--- /dev/null
+++ b/Code/src/vsobjectdesktop.cpp
@@ -0,0 +1,52 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+ *   ugo.becciani@oact.inaf.it                                             *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include <cstdlib>
+#include <cstring>
+#include "vsobjectdesktop.h"
+
+#include <iostream>
+
+VSObjectDesktop::VSObjectDesktop()
+{
+    m_description = "";
+    m_name = "";
+}
+
+
+VSObjectDesktop::VSObjectDesktop(std::string name, std::string description /*= ""*/)
+{
+    m_description = description;
+    m_name = name;
+}
+
+
+VSObjectDesktop::~VSObjectDesktop()
+{
+}
+
+void VSObjectDesktop::printSelf()
+{
+    std::clog << "Name: "        << m_name        << std::endl;
+    std::clog << "Description: " << m_description << std::endl;
+
+    return;
+}
diff --git a/Code/src/vsobjectdesktop.h b/Code/src/vsobjectdesktop.h
new file mode 100644
index 0000000000000000000000000000000000000000..906156762bbabb895df88b7d8fb74bac76a21bb0
--- /dev/null
+++ b/Code/src/vsobjectdesktop.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+ *   ugo.becciani@oact.inaf.it                                             *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifndef VSOBJECTDESKTOP_H
+#define VSOBJECTDESKTOP_H
+
+/**
+        @author Alessandro Costa <alessandro.costa@oact.inaf.it>
+        @author Ugo Becciani <ugo.becciani@oact.inaf.it>
+*/
+
+#include <string>
+#include <QString>
+
+class VSObjectDesktop
+{
+    std::string m_description;
+    std::string m_name;
+
+public:
+    VSObjectDesktop();
+    VSObjectDesktop(std::string name, std::string description = "");
+
+    ~VSObjectDesktop();
+    virtual void printSelf();
+    std::string getDescription() {return m_description;};
+    std::string getName()        {return m_name;};
+
+    void setDescription(std::string description) {m_description = description;};
+    void setName(std::string name) {m_name = name;};
+};
+
+#endif
diff --git a/Code/src/vstabledesktop.cpp b/Code/src/vstabledesktop.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..749bdf5c606460afdda64000f8f0d171af5c1770
--- /dev/null
+++ b/Code/src/vstabledesktop.cpp
@@ -0,0 +1,1354 @@
+/***************************************************************************
+     *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+     *   ugo.becciani@oact.inaf.it                                             *
+     *   alessandro.costa@oact.inaf.it                                         *
+     *                                                                         *
+     *   This program is free software; you can redistribute it and/or modify  *
+     *   it under the terms of the GNU General Public License as published by  *
+     *   the Free Software Foundation; either version 2 of the License, or     *
+     *   (at your option) any later version.                                   *
+     *                                                                         *
+     *   This program is distributed in the hope that it will be useful,       *
+     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+     *   GNU General Public License for more details.                          *
+     *                                                                         *
+     *   You should have received a copy of the GNU General Public License     *
+     *   along with this program; if not, write to the                         *
+     *   Free Software Foundation, Inc.,                                       *
+     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+     ***************************************************************************/
+
+#include "vstabledesktop.h"
+#include <QObject>
+#include <QDebug>
+#include <QProcess>
+#include <QDir>
+#include <QStringList>
+#include "mainwindow.h"
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <sstream>
+#include "singleton.h"
+#include <QDateTime>
+#include "base64.h"
+#include "visivoutilsdesktop.h"
+
+#include <boost/algorithm/string.hpp>
+
+
+
+
+const unsigned long long int VSTableDesktop::MAX_NUMBER_TO_SKIP = 1000000000;
+//const unsigned int VSTable::MAX_NUMBER_ROW_REQUEST = 2147483647;
+//const unsigned int VSTable::MAX_NUMBER_TO_SKIP = 250000000;
+const unsigned long long int VSTableDesktop::MAX_NUMBER_ROW_REQUEST = 250000000;
+
+VSTableDesktop::VSTableDesktop()
+{
+    m_locator = "";
+    m_endiannes = "unknown";
+    m_type      = "unknown";
+
+    m_nCols = 0;
+    m_nRows = 0;
+    m_tableExist=false;
+
+    m_isVolume = false;
+
+    for(int i = 0; i < 3; ++i)
+    {
+        m_cellNumber[i] = 0;
+        m_cellSize[i] = 0;
+    }
+}
+
+VSTableDesktop::VSTableDesktop(std::string locator, std::string name /*= ""*/, std::string description /*= ""*/, bool statistic) //QUI modificata per letture header etc
+    : VSObjectDesktop(name, description)
+{
+    bool headerExist=false;
+    bool statisticsOk=false;
+
+    setLocator(locator);
+    m_endiannes = "unknown";
+    m_type      = "unknown";
+    m_tableExist=false;
+
+    m_nCols = 0;
+    m_nRows = 0;
+
+    m_isVolume = false;
+
+    for(int i = 0; i < 3; ++i)
+    {
+        m_cellNumber[i] = 0;
+        m_cellSize[i] = 0;
+    }
+
+    headerExist=readHeader();
+    if(!headerExist)
+    {
+        std::cerr<<"Invalid table header "<<m_locator<<std::endl;
+        return;
+    } else
+    {
+        std::ifstream inTable(m_locator.c_str());
+        if(!inTable)
+        {
+            std::cerr<<"Invalid table "<<m_locator<<std::endl;
+            return;
+        }
+        inTable.close();
+        m_tableExist=true;
+    }
+
+    // Calculating bin number
+    m_binNumber=-1;
+    if (m_nRows/30 < 1000 ) m_binNumber=(int)m_nRows/30;
+    else m_binNumber=1000;
+
+    // allocating data structures
+
+    try{
+        m_histogramArray = new int*[m_nCols];
+        m_histogramValueArray = new float*[m_nCols];
+    }
+    catch(std::bad_alloc & e)
+    {
+        m_histogramArray=NULL;
+        m_histogramValueArray=NULL;
+    }
+    if(m_histogramArray==NULL)
+        qDebug() << "int ** m_histogramArray allocation:  out of memory";
+
+    for(int i=0; i<m_nCols; ++i)
+    {
+        try{
+            m_histogramArray[i]=new int[m_binNumber];
+            m_histogramValueArray[i]=new float[m_binNumber];
+        }
+        catch(std::bad_alloc & e)
+        {
+            m_histogramArray[i]=NULL;
+            m_histogramValueArray[i]=NULL;
+        }
+        if(m_histogramArray[i]==NULL)
+            qDebug() << "int * m_histogramArray allocation:  out of memory";
+    }
+    /*
+    //Moved in ReadStatistic()
+    try{
+        m_rangeArray = new float*[m_nCols];
+    }
+    catch(std::bad_alloc & e)
+    {
+        m_rangeArray=NULL;
+    }
+    if(m_rangeArray==NULL)
+        qDebug() << "float ** m_rangeArray allocation:  out of memory";
+    for(int i=0; i<m_nCols; ++i)
+    {
+        try{
+            m_rangeArray[i]=new float[3];
+        }
+        catch(std::bad_alloc & e)
+        {
+            m_rangeArray[i]=NULL;
+        }
+        if (m_rangeArray[i]==NULL)
+            qDebug() << "float * m_rangeArray allocation:  out of memory";
+    }
+    */
+    //
+    // Once instantiated you  can access the data structure
+    // by using  m_histogramArray[ColNumber][value]
+    //
+    // and by using  min =  m_rangeArray[ColNumber][0]
+    //               max =  m_rangeArray[ColNumber][1]
+    //
+    //  If enabled some problems appear ! please investigate on
+
+    if(statistic)
+    {
+        statisticsOk=readStatistics();
+        if(!statisticsOk)
+        {
+            std::cerr<<"Prorblems in reading statistics: "<<m_locator<<std::endl;
+            return;
+        }
+    }
+
+}
+
+VSTableDesktop::~VSTableDesktop()
+{
+}
+
+bool VSTableDesktop::setEndiannes(std::string endiannes)
+{
+    if(endiannes == "big" || endiannes == "b")
+    {
+        m_endiannes = "big";
+
+        return true;
+    }
+    else if(endiannes == "little" || endiannes == "l")
+    {
+        m_endiannes = "little";
+
+        return true;
+    }
+
+    m_endiannes = "unknown";
+
+    return false;
+}
+
+bool VSTableDesktop::setType(std::string type)
+{
+    if(type == "float" || type == "f")
+    {
+        m_type = "float";
+
+        return true;
+    }
+    else if(type == "double" || type == "d")
+    {
+        m_type = "double";
+
+        return true;
+    }
+
+    m_type = "unknown";
+
+    return false;
+}
+
+bool VSTableDesktop::addCol(std::string name)
+{
+    if(name == "")
+        return false;
+
+    unsigned int size = m_colVector.size();
+
+    for(unsigned int i = 0; i < size; ++i)
+    {
+        if(m_colVector[i] == name)
+            return false;
+    }
+
+    m_colVector.push_back(name);
+    m_nCols++;
+
+    return true;
+}
+
+void VSTableDesktop::setCellSize(float xCellSize, float yCellSize, float zCellSize)
+{
+    m_cellSize[0] = xCellSize;
+    m_cellSize[1] = yCellSize;
+    m_cellSize[2] = zCellSize;
+
+    return;
+}
+
+void VSTableDesktop::setLocator(std::string locator)
+{
+    if(locator.find(".bin") != std::string::npos)
+        m_locator=locator;
+    else
+        m_locator = locator + ".bin";
+
+
+}
+
+void VSTableDesktop::setCellNumber(unsigned int xCellNumber, unsigned int yCellNumber, unsigned int zCellNumber)
+{
+    m_cellNumber[0] = xCellNumber;
+    m_cellNumber[1] = yCellNumber;
+    m_cellNumber[2] = zCellNumber;
+
+    return;
+}
+
+bool VSTableDesktop::readHeader()
+{
+    unsigned int i = 0;
+
+    std::string headerFileName = m_locator + ".head";
+    std::ifstream inHeader(headerFileName.c_str());
+
+    if(!inHeader)
+        return false;
+
+    //reads the number of cols and rows
+
+    std::string dummy;
+    std::string type;
+    std::string endiannes;
+
+    unsigned int nCols;
+    m_nCols=0;
+
+    inHeader >> type;
+    if(!(type == "float" || type =="f"))
+    {
+        std::cerr<<"Invalid input not float table in  VisIVO Filters. Please use VisIVO Importer to convert it in float table "<<std::endl;
+        exit(1);
+    }
+    inHeader >> nCols;
+
+    std::string tmp = "";
+
+    getline(inHeader, tmp); //to remove the carrige return character  (\r) from the last line
+    getline(inHeader, tmp);
+
+    std::stringstream sstmp(tmp);
+
+    sstmp >> m_nRows;
+
+    if(!(sstmp.eof()))
+    {
+        m_isVolume=true;
+
+        sstmp >> m_cellNumber[0];
+        sstmp >> m_cellNumber[1];
+        sstmp >> m_cellNumber[2];
+
+        sstmp >> m_cellSize[0];
+        sstmp >> m_cellSize[1];
+        sstmp >> m_cellSize[2];
+    }
+
+    inHeader >> endiannes;
+
+    setType(type);
+    setEndiannes(endiannes);
+
+    m_colVector.clear();
+
+
+
+    std::string name = "";
+
+    for(i = 0; i < nCols; ++i)
+    {
+        inHeader >> name;
+
+        addCol(name);
+    }
+
+    inHeader.close();
+    if((m_colVector.size() != nCols )||(m_cellNumber[0] >0 && m_cellNumber[0] * m_cellNumber[1] * m_cellNumber[2] != m_nRows))
+    {
+        m_nCols = 0;
+        m_colVector.clear();
+
+        return false;
+    }
+    return true;
+}
+
+void VSTableDesktop::printSelf()
+{
+    //VisIVOMetadata::printSelf();
+
+    std::clog << "Locator: "     << m_locator   << std::endl;
+    std::clog << "Columns: "     << m_nCols     << std::endl;
+    std::clog << "Rows: "        << m_nRows     << std::endl;
+    std::clog << "Type: "        << m_type      << std::endl;
+    std::clog << "Endiannes: "   << m_endiannes << std::endl;
+
+    std::clog << "Column names:" << std::endl;
+
+    unsigned int size = m_colVector.size();
+
+    for(unsigned int i = 0; i < size; ++i)
+        std::clog << m_colVector[i] << std::endl;
+
+    return;
+}
+
+std::string VSTableDesktop::getColName(unsigned int i)
+{
+    if(i < m_colVector.size())
+    {
+        return m_colVector[i];
+    }
+
+    return "";
+}
+
+void VSTableDesktop::setColName(unsigned int i, std::string newColName)
+{
+    if(i < m_colVector.size())
+    {
+        m_colVector[i]=newColName;
+    }
+
+    return;
+}
+
+
+int VSTableDesktop::getColId(std::string name)
+{
+    unsigned int size = m_colVector.size();
+
+    for(unsigned int i = 0; i < size; ++i)
+    {
+        //if(m_colVector[i] == name)
+        if (boost::iequals(m_colVector[i], name))
+            return i;
+    }
+
+    return -1;
+}
+
+bool VSTableDesktop::createTable()
+{
+    if(m_tableExist)
+    {
+        std::cerr<<"Table "<<m_locator<<" already exist"<<std::endl;
+        return false;
+    }
+    std::string fileName = m_locator;
+    std::ifstream checkTable(fileName.c_str());
+    if(checkTable)
+    {
+        std::cerr<<"Cannot create table. File "<<m_locator<<" already exist"<<std::endl;
+        return false;
+    }
+    std::ofstream outTable(fileName.c_str());
+    if(!outTable)
+        return false;
+
+    std::ios::off_type indexOffset;
+    unsigned long long int totNumberToSkip;
+    unsigned int nSkip;
+    float zero=0;
+
+    totNumberToSkip = m_nRows*m_nCols-1;
+    while(totNumberToSkip!=0)
+    {
+        nSkip=MAX_NUMBER_TO_SKIP;
+        if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+        indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+        outTable.seekp(indexOffset,std::ios::cur);
+        totNumberToSkip=totNumberToSkip-nSkip;
+    }
+    outTable.write((char *) &zero,1*sizeof(float));
+    outTable.close();
+    m_tableExist=true;
+    return true;
+}
+
+bool VSTableDesktop::createTable(float **fArray)
+{
+    if(m_tableExist)
+    {
+        std::cerr<<"Table "<<m_locator<<" already exist"<<std::endl;
+        return false;
+    }
+    std::string fileName = m_locator;
+    std::ifstream checkTable(fileName.c_str());
+    if(checkTable)
+    {
+        std::cerr<<"Cannot create table. File "<<m_locator<<" already exist"<<std::endl;
+        return false;
+    }
+    std::ofstream outTable(fileName.c_str());
+    if(!outTable)
+        return false;
+
+    for(unsigned int j=0;j<m_nCols;j++)
+    {
+        //std::ios::off_type indexOffset;
+        unsigned long long int totNumberToWrite;
+        unsigned long long int nWrite;
+        float *fPointer=&fArray[j][0];
+        totNumberToWrite = m_nRows;
+        while(totNumberToWrite!=0)
+        {
+            nWrite=MAX_NUMBER_TO_SKIP;
+            if(nWrite > totNumberToWrite) nWrite=(int) totNumberToWrite;
+            outTable.write((char *) fPointer,nWrite*sizeof(float));
+            fPointer=fPointer+nWrite;
+            totNumberToWrite=totNumberToWrite-nWrite;
+        }
+
+    }
+    outTable.close();
+    m_tableExist=true;
+    return true;
+}
+
+
+bool VSTableDesktop::writeTable()
+{
+    if(m_tableExist)
+    {
+        std::cerr<<"Table "<<m_locator<<" already exist"<<std::endl;
+        return false;
+    }
+
+    bool wrHeader=writeHeader();
+    if(!wrHeader)
+    {
+        std::cerr<<"No Table header was written: "<<m_locator<<".head"<<std::endl;
+        return false;
+    }
+    return wrHeader;
+}
+
+bool VSTableDesktop::writeTable(float **fArray)
+{
+    if(m_tableExist)
+    {
+        std::cerr<<"Table "<<m_locator<<" already exist"<<std::endl;
+        return false;
+    }
+
+    bool wrHeader=writeHeader();
+    if(!wrHeader)
+    {
+        std::cerr<<"No Table header was written: "<<m_locator<<".head"<<std::endl;
+        return false;
+    }
+    bool wrTable=createTable(fArray);
+    if(!wrTable)
+    {
+        std::cerr<<"No Table  was written: "<<m_locator<<".head"<<std::endl;
+        return false;
+    }
+    return true;
+}
+
+
+bool VSTableDesktop::writeHeader()
+{
+    int i = 0;
+
+    std::string headerFileName = m_locator + ".head";
+
+    std::ofstream outHeader(headerFileName.c_str());
+
+    if(!outHeader)
+        return false;
+
+    outHeader << m_type << std::endl;
+    outHeader << m_nCols << std::endl;
+    outHeader << m_nRows;
+
+    if(m_isVolume)
+    {
+        for(i = 0; i < 3; ++i)
+            outHeader << " " << m_cellNumber[i];
+
+        for(i = 0; i < 3; ++i)
+            outHeader << " " << m_cellSize[i];
+    }
+
+    outHeader << std::endl;
+
+    outHeader << m_endiannes << std::endl;
+
+    unsigned int size = m_colVector.size();
+
+    for(unsigned int i = 0; i < size; ++i)
+        outHeader << m_colVector[i] << std::endl;
+
+    outHeader.close();
+
+    //  createTable(); //QUI introdotto MA occorre inserire verifca che lheader non esiste gia'
+
+    return true;
+}
+
+
+int VSTableDesktop::getColumnString(unsigned int *colList, unsigned int nOfCol, unsigned long long int fromRow, unsigned long long int toRow, std::string **matrix)
+{
+
+
+    if((toRow-fromRow+1) > MAX_NUMBER_ROW_REQUEST)
+    {
+        std::cerr<<"Cannot be requested more than "<< MAX_NUMBER_ROW_REQUEST <<" row lines"<<std::endl;
+        return -3;
+    }
+
+    std::ifstream fileInput(m_locator.c_str(), std::ios::in | std::ios::binary);
+    if(!fileInput)
+    {
+        std::cerr<<"Cannot open binary file"<< m_locator <<std::endl;
+        return -2;
+    }
+
+    std::ios::off_type indexOffset;
+    unsigned long long int totNumberToSkip;
+    unsigned long long int nSkip;
+    unsigned int column;
+    int nLoad;
+
+    for(unsigned int j=0; j<nOfCol;j++)
+    {
+        column=colList[j];
+
+        if(column > m_nCols-1)
+        {
+            std::cerr << "Invalid Column Id"<< std::endl;
+            continue;
+        }
+        if(toRow > m_nRows-1)
+        {
+            std::cerr << "Warning: Invalid request of toRow:" << toRow <<" lowered to "<<m_nRows -1<< std::endl;
+            toRow=m_nRows-1;
+        }
+        nLoad = (int) (toRow-fromRow+1);
+        if(nLoad < 0)
+        {
+            std::cerr << "Error: Invalid range fromRow: " << fromRow <<" toRow: " << toRow << std::endl;
+            return -3;
+        }
+
+        fileInput.seekg(std::ios::beg);
+
+        for(int i=0; i < m_nRows* column; ++i)
+        {
+            fileInput.ignore(std::numeric_limits<std::streamsize>::max(),'\n');
+        }
+
+        for(int i=0;i<m_nRows;i++)
+        {
+            std::string line8;
+            fileInput >> line8;
+            matrix[j][i]=line8;
+            //qDebug()<<QString::fromStdString(line8);
+        }
+
+
+
+    }
+
+    fileInput.close();
+
+    return nLoad;
+}
+
+int VSTableDesktop::getColumnStringToFloat(unsigned int *colList, unsigned int nOfCol, unsigned long long int fromRow, unsigned long long int toRow, float **fArray)
+{
+
+    if((toRow-fromRow+1) > MAX_NUMBER_ROW_REQUEST)
+    {
+        std::cerr<<"Cannot be requested more than "<< MAX_NUMBER_ROW_REQUEST <<" row lines"<<std::endl;
+        return -3;
+    }
+
+    std::ifstream fileInput(m_locator.c_str(), std::ios::in | std::ios::binary);
+    if(!fileInput)
+    {
+        std::cerr<<"Cannot open binary file"<< m_locator <<std::endl;
+        return -2;
+    }
+
+
+
+    std::ios::off_type indexOffset;
+    unsigned long long int totNumberToSkip;
+    unsigned long long int nSkip;
+    unsigned int column;
+    int nLoad;
+    int length = 32;
+
+
+    for(unsigned int j=0; j<nOfCol;j++)
+    {
+        column=colList[j];
+        if(column > m_nCols-1)
+        {
+            std::cerr << "Invalid Column Id"<< std::endl;
+            continue;
+        }
+        if(toRow > m_nRows-1)
+        {
+            std::cerr << "Warning: Invalid request of toRow:" << toRow <<" lowered to "<<m_nRows -1<< std::endl;
+            toRow=m_nRows-1;
+        }
+        nLoad = (int) (toRow-fromRow+1);
+        if(nLoad < 0)
+        {
+            std::cerr << "Error: Invalid range fromRow: " << fromRow <<" toRow: " << toRow << std::endl;
+            return -3;
+        }
+
+
+        fileInput.seekg (column*m_nRows*length, fileInput.beg);
+
+        char * buffer = new char [length];
+        float *tmp=new float[m_nRows];
+
+        for(int i=0;i<m_nRows;i++)
+        {
+            fileInput.read (buffer,length);
+            std::string str(buffer);
+            tmp[i]=atof(base64_decode(str).c_str());
+        }
+        fArray[j]=tmp;
+    }
+    qDebug()<<"end";
+
+    return 0;
+
+}
+
+int VSTableDesktop::getColumn(unsigned int *colList, unsigned int nOfCol, unsigned long long int fromRow, unsigned long long int toRow, float **fArray)
+// Get all the values of the table columns listed in colList, fromRow toRow
+// i list array of columns of nOfCol elements. 0 Is the first column
+// from -to. fArray must be allocated by the calling program and must contain all requested elements.Extreme values fromRow  and to Row are included. fromRom=0 toRow=10 are 11 elements 0,1,..10.
+// the method return the number of elements in each column j of fArray[j][i] (i= to-from)
+// colList from 0 to m_nCols -1
+{
+    if((toRow-fromRow+1) > MAX_NUMBER_ROW_REQUEST)
+    {
+        std::cerr<<"Cannot be requested more than "<< MAX_NUMBER_ROW_REQUEST <<" row lines"<<std::endl;
+        return -3;
+    }
+
+    std::ifstream fileInput(m_locator.c_str(), std::ios::in | std::ios::binary);
+    if(!fileInput)
+    {
+        std::cerr<<"Cannot open binary file"<< m_locator <<std::endl;
+        return -2;
+    }
+
+    std::ios::off_type indexOffset;
+    unsigned long long int totNumberToSkip;
+    unsigned long long int nSkip;
+    unsigned int column;
+    int nLoad;
+
+    for(unsigned int j=0; j<nOfCol;j++)
+    {
+        column=colList[j];
+        if(column > m_nCols-1)
+        {
+            std::cerr << "Invalid Column Id"<< std::endl;
+            continue;
+        }
+        if(toRow > m_nRows-1)
+        {
+            std::cerr << "Warning: Invalid request of toRow:" << toRow <<" lowered to "<<m_nRows -1<< std::endl;
+            toRow=m_nRows-1;
+        }
+        nLoad = (int) (toRow-fromRow+1);
+        if(nLoad < 0)
+        {
+            std::cerr << "Error: Invalid range fromRow: " << fromRow <<" toRow: " << toRow << std::endl;
+            return -3;
+        }
+
+        if (j==0) totNumberToSkip = colList[j]*m_nRows+fromRow;
+        else
+        {
+            if(colList[j]>=colList[j-1])
+                totNumberToSkip = (colList[j]-colList[j-1]-1)*m_nRows+fromRow+(m_nRows-toRow)-1;
+            else
+            {
+                indexOffset=0;
+                fileInput.seekg(indexOffset,std::ios::beg);
+                totNumberToSkip = colList[j]*m_nRows+fromRow;
+
+            }
+        }
+        while(totNumberToSkip!=0)
+        {
+            nSkip=MAX_NUMBER_TO_SKIP;
+            if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+            indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+            fileInput.seekg(indexOffset,std::ios::cur);
+            totNumberToSkip=totNumberToSkip-nSkip;
+        }
+        fileInput.read((char *) &fArray[j][0],nLoad*sizeof(float));
+    }
+    fileInput.close();
+#ifdef VSBIGENDIAN
+    std::string endianism="big";
+#else
+    std::string endianism="little";
+#endif
+
+    if((endianism=="big" && m_endiannes=="little") || (endianism=="little" && m_endiannes=="big"))
+    {
+        std::cerr<<"Warning: endianism swap executed on table "<<m_locator<<std::endl;
+        for(unsigned int j=0; j<nOfCol;j++)
+            for(int k=0;k<nLoad;k++)
+                fArray[j][k]=floatSwapDesktop((char *)(&fArray[j][k]));
+    }
+
+    return nLoad;
+}
+
+
+int VSTableDesktop::getColumnList(unsigned int *colList,unsigned int nOfCol, unsigned long long int *list, unsigned int long long nOfEle, float **fArray)
+// Get only the values listed in list of the table columns listed in colList
+// colList list array of columns of nOfCol elements. 0 Is the first column
+//  list is an nOfEle array elements containing requested number of rows
+// the method return the number of elements in each column j of fArray[j][i] (i= 0-nOfEle)
+
+{
+
+    if(nOfEle > MAX_NUMBER_ROW_REQUEST)
+    {
+        std::cerr<<"Cannot be requested more than "<< MAX_NUMBER_ROW_REQUEST <<" row lines"<<std::endl;
+        return -3;
+    }
+
+    std::ifstream fileInput(m_locator.c_str(), std::ios::in | std::ios::binary);
+    if(!fileInput)
+    {
+        std::cerr<<"Cannot open binary file"<<m_locator<<std::endl;
+        return -2;
+    }
+    unsigned long long int indexLast=0,stepToSkip;
+    std::ios::off_type indexOffset;
+    unsigned long long int totNumberToSkip;
+    unsigned long long int nSkip;
+    unsigned int column;
+    int nLoad=1;
+    int totLoad=0;
+
+
+    for(unsigned int k=0; k<nOfCol;k++)
+    {
+        column=colList[k];
+        if(column > m_nCols-1)
+        {
+            std::cerr << "Invalid Column Id"<< std::endl;
+            continue;
+        }
+
+        // going at the beginning of column
+        if(k==0)
+        {
+            totNumberToSkip=colList[k]*m_nRows;
+            while(totNumberToSkip!=0)
+            {
+                nSkip=MAX_NUMBER_TO_SKIP;
+                if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+                indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+                fileInput.seekg(indexOffset,std::ios::cur);
+                totNumberToSkip=totNumberToSkip-nSkip;
+            }
+        }
+        if(k>0 && colList[k]<colList[k-1])
+        {
+            indexOffset=0;
+            fileInput.seekg(indexOffset,std::ios::beg);
+
+            totNumberToSkip=colList[k]*m_nRows;
+            while(totNumberToSkip!=0)
+            {
+                nSkip=MAX_NUMBER_TO_SKIP;
+                if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+                indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+                fileInput.seekg(indexOffset,std::ios::cur);
+                totNumberToSkip=totNumberToSkip-nSkip;
+            }
+        }
+        if(k>0 && colList[k]>=colList[k-1])
+        {
+            totNumberToSkip=(colList[k]-colList[k-1]-1)*m_nRows + m_nRows- indexLast-1;
+            while(totNumberToSkip!=0)
+            {
+                nSkip=MAX_NUMBER_TO_SKIP;
+                if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+                indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+                fileInput.seekg(indexOffset,std::ios::cur);
+                totNumberToSkip=totNumberToSkip-nSkip;
+            }
+        }
+        //
+
+        for(unsigned long long int j=0;j<nOfEle;j++)
+        {
+            if(list[j] > m_nRows-1)
+            {
+                std::cerr << "Warning: Invalid list request:" << list[j] << std::endl;
+                continue;
+            }
+            indexLast=list[j];
+            if(j==0)
+            {
+                totNumberToSkip=list[j];
+                stepToSkip=0;
+            }
+            if(j>0)
+            {
+                stepToSkip=(list[j]-list[j-1]-1);
+                if(list[j-1]>list[j]-1)
+                {
+                    totNumberToSkip=colList[k]*m_nRows+list[j];
+                    indexOffset=0;
+                    fileInput.seekg(indexOffset,std::ios::beg);
+                } else
+                    totNumberToSkip=stepToSkip;
+            }
+            while(totNumberToSkip!=0)
+            {
+                nSkip=MAX_NUMBER_TO_SKIP;
+                if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+                indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+                fileInput.seekg(indexOffset,std::ios::cur);
+                totNumberToSkip=totNumberToSkip-nSkip;
+            }
+            fileInput.read((char *) &fArray[k][j],nLoad*sizeof(float));
+            totLoad++;
+        }
+
+    }
+
+#ifdef VSBIGENDIAN
+    std::string endianism="big";
+#else
+    std::string endianism="little";
+#endif
+
+    if((endianism=="big" && m_endiannes=="little") || (endianism=="little" && m_endiannes=="big"))
+    {
+        std::cerr<<"Warning: endianism swap executed on table "<<m_locator<<std::endl;
+        for(unsigned int k=0; k<nOfCol;k++)
+            for(unsigned long long int j=0;j<nOfEle;j++)
+                fArray[k][j]=floatSwapDesktop((char *)(&fArray[k][j]));
+    }
+    return totLoad;
+}
+
+int VSTableDesktop::putColumn(unsigned int *colList, unsigned int nOfCol, unsigned long long int fromRow, unsigned long long int toRow, float **fArray)
+// i list array of columns of nOfCol elements. 0 Is the first column
+// from -to. fArray must be allocated by the calling program and must contain all requested elements. Extreme values fromRow  and to Row are included. fromRom=0 toRow=10 are 11 elements 0,1,..10.
+// the method return the number of elements in each column j of fArray[j][i] (i= to-from)
+// colList from 0 to m_nCols -1
+{
+    if((toRow-fromRow+1) > MAX_NUMBER_ROW_REQUEST)
+    {
+        std::cerr<<"Cannot be put more than "<< MAX_NUMBER_ROW_REQUEST <<" row lines"<<std::endl;
+        return -3;
+    }
+
+    std::ifstream fileInput(m_locator.c_str(), std::ios::in | std::ios::binary);
+    if(!fileInput)
+    {
+        std::ofstream fileOutput(m_locator.c_str(), std::ios::out | std::ios::binary);
+        fileOutput.close();
+    } else
+        fileInput.close();
+
+    std::fstream fileOutput(m_locator.c_str(), std::ios::in |  std::ios::out | std::ios::binary);
+
+    if(!fileOutput)
+    {
+        std::cerr<<"Cannot open binary file"<< m_locator <<std::endl;
+        return -2;
+    }
+    //unsigned long long int indexSeek;
+    std::ios::off_type indexOffset;
+    unsigned long long int totNumberToSkip;
+    unsigned long long int nSkip;
+    unsigned int column;
+    int nLoad;
+
+    for(unsigned int j=0; j<nOfCol;j++)
+    {
+        column=colList[j];
+        if(column > m_nCols+1)
+        {
+            std::cerr << "Invalid Column Id"<< std::endl;
+            continue;
+        }
+        if(toRow > m_nRows-1)
+        {
+            std::cerr << "Warning: Invalid request of toRow:" << toRow <<" lowered to "<<m_nRows -1<< std::endl;
+            toRow=m_nRows-1;
+        }
+        nLoad = (int) (toRow-fromRow+1);
+        if(nLoad <= 0)
+        {
+            std::cerr << "Error: Invalid range fromRow: " << fromRow <<" toRow: " << toRow << std::endl;
+            return -3;
+        }
+
+        if (j==0) totNumberToSkip = colList[j]*m_nRows+fromRow;
+        else
+        {
+            if(colList[j]>=colList[j-1])
+                totNumberToSkip = (colList[j]-colList[j-1]-1)*m_nRows+fromRow+(m_nRows-toRow)-1;
+            else
+            {
+                indexOffset=0;
+                fileOutput.seekp(indexOffset,std::ios::beg);
+                totNumberToSkip = colList[j]*m_nRows+fromRow;
+
+            }
+        }
+        while(totNumberToSkip!=0)
+        {
+            nSkip=MAX_NUMBER_TO_SKIP;
+            if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+            indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+            fileOutput.seekp(indexOffset,std::ios::cur);
+            totNumberToSkip=totNumberToSkip-nSkip;
+        }
+        fileOutput.write((char *) &fArray[j][0],nLoad*sizeof(float));
+    }
+    fileOutput.close();
+    return nLoad;
+}
+
+
+int VSTableDesktop::putColumnList(unsigned int *colList, unsigned int nOfCol, unsigned long long int *list,unsigned long long int nOfEle, float **fArray)
+// colList list array of columns of nOfCol elements. 0 Is the first column
+//  list is an nOfEle array elements containing requested number of rows
+// the method return the number of elements in each column j of fArray[j][i] (i= 0-nOfEle)
+
+{
+    if(nOfEle > MAX_NUMBER_ROW_REQUEST)
+    {
+        std::cerr<<"Cannot be put more than "<< MAX_NUMBER_ROW_REQUEST <<" row lines"<<std::endl;
+        return -3;
+    }
+
+    std::fstream fileOutput(m_locator.c_str(), std::ios::in |  std::ios::out | std::ios::binary);
+    if(!fileOutput)
+    {
+        std::cerr<<"Cannot open binary file"<<m_locator<<std::endl;
+        return -2;
+    }
+    unsigned long long int  indexLast=0,stepToSkip;
+    std::ios::off_type indexOffset;
+    unsigned long long int totNumberToSkip;
+    unsigned long long int nSkip;
+    unsigned int column;
+    int nLoad=1;
+    int totLoad=0;
+
+
+    for(unsigned int k=0; k<nOfCol;k++)
+    {
+        column=colList[k];
+        if(column > m_nCols-1)
+        {
+            std::cerr << "Invalid Column Id"<< std::endl;
+            continue;
+        }
+
+        // going at the beginning of column
+        if(k==0)
+        {
+            totNumberToSkip=colList[k]*m_nRows;
+            while(totNumberToSkip!=0)
+            {
+                nSkip=MAX_NUMBER_TO_SKIP;
+                if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+                indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+                fileOutput.seekp(indexOffset,std::ios::cur);
+                totNumberToSkip=totNumberToSkip-nSkip;
+            }
+        }
+        if(k>0 && colList[k]<colList[k-1])
+        {
+            indexOffset=0;
+            fileOutput.seekp(indexOffset,std::ios::beg);
+
+            totNumberToSkip=colList[k]*m_nRows;
+            while(totNumberToSkip!=0)
+            {
+                nSkip=MAX_NUMBER_TO_SKIP;
+                if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+                indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+                fileOutput.seekp(indexOffset,std::ios::cur);
+                totNumberToSkip=totNumberToSkip-nSkip;
+            }
+        }
+        if(k>0 && colList[k]>=colList[k-1])
+        {
+            totNumberToSkip=(colList[k]-colList[k-1]-1)*m_nRows + m_nRows- indexLast-1;
+            while(totNumberToSkip!=0)
+            {
+                nSkip=MAX_NUMBER_TO_SKIP;
+                if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+                indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+                fileOutput.seekp(indexOffset,std::ios::cur);
+                totNumberToSkip=totNumberToSkip-nSkip;
+            }
+        }
+        //
+
+        for(unsigned int j=0;j<nOfEle;j++)
+        {
+            if(list[j] > m_nRows-1)
+            {
+                std::cerr << "Warning: Invalid list request:" << list[j] << std::endl;
+                continue;
+            }
+            indexLast=list[j];
+            if(j==0)
+            {
+                totNumberToSkip=list[j];
+                stepToSkip=0;
+            }
+            if(j>0)
+            {
+                stepToSkip=(list[j]-list[j-1]-1);
+                if(list[j-1]>list[j]-1)
+                {
+                    totNumberToSkip=colList[k]*m_nRows+list[j];
+                    indexOffset=0;
+                    fileOutput.seekp(indexOffset,std::ios::beg);
+                } else
+                    totNumberToSkip=stepToSkip;
+            }
+            while(totNumberToSkip!=0)
+            {
+                nSkip=MAX_NUMBER_TO_SKIP;
+                if(nSkip > totNumberToSkip) nSkip=(int) totNumberToSkip;
+                indexOffset=(std::ios::off_type) nSkip*sizeof(float);
+                fileOutput.seekp(indexOffset,std::ios::cur);
+                totNumberToSkip=totNumberToSkip-nSkip;
+            }
+            fileOutput.write((char *) &fArray[k][j],nLoad*sizeof(float));
+            totLoad++;
+        }
+
+    }
+    return totLoad;
+}
+
+void VSTableDesktop::getRange(unsigned int i, float *range)
+{
+    range[0]=m_rangeArray[i][0];
+    range[1]=m_rangeArray[i][1];
+    range[2]=m_rangeArray[i][2];
+}
+
+QVector<double> VSTableDesktop::getHistogram(unsigned int i)
+{
+
+    QVector<double> val(m_binNumber);
+    for (int j = 0; j < m_binNumber ; j++){
+        val[j] = m_histogramArray[i][j];
+        //  qDebug()<<"get Hist--- "<<j<<"="<<val[j];
+    }
+
+    return val;
+
+}
+
+QVector<double> VSTableDesktop::getHistogramValue(unsigned int i )
+{
+
+    QVector<double> val(m_binNumber);
+    for (int j = 0; j < m_binNumber ; j++){
+        val[j] = m_histogramValueArray[i][j];
+        // qDebug()<<"getValue--- "<<j<<"="<<val[j];
+    }
+
+    return val;
+}
+
+int VSTableDesktop::getbinNumber()
+{return m_binNumber;}
+
+bool VSTableDesktop::readStatistics()
+{
+
+    qDebug()<<"*************LEGGO STATISTICHE ";
+
+
+    int impStatus;
+    MainWindow *w = &Singleton<MainWindow>::Instance();
+
+
+    qDebug()<<"Statistiche #colonne << "<<m_nCols;
+    std::cout<<" file: "<<m_locator<<std::endl;
+
+
+    char *file = new char[m_locator.length() + 1];
+    std::strcpy(file,m_locator.c_str());
+
+    int res= VS_VBTMetaData(&myVBT, 1 , file);
+
+
+
+    try{
+        m_rangeArray = new float*[m_nCols];
+    }
+    catch(std::bad_alloc & e)
+    {
+        m_rangeArray=NULL;
+    }
+    if(m_rangeArray==NULL)
+        qDebug() << "float ** m_rangeArray allocation:  out of memory";
+    for(int i=0; i<m_nCols; ++i)
+    {
+        try{
+            m_rangeArray[i]=new float[3];
+        }
+        catch(std::bad_alloc & e)
+        {
+            m_rangeArray[i]=NULL;
+        }
+        if (m_rangeArray[i]==NULL)
+            qDebug() << "float * m_rangeArray allocation:  out of memory";
+    }
+
+    for (int i=0; i<m_nCols; i++)
+    {
+
+        qDebug()<<"colonna: "<<i <<" myVBT "<<myVBT.statistic[i][0]<<" "<<myVBT.statistic[i][1];
+
+
+        this->m_rangeArray[i][0]=myVBT.statistic[i][1];
+        this->m_rangeArray[i][1]=myVBT.statistic[i][0];
+
+        qDebug()<<"colonna: "<<i <<" m_rangeArray "<<m_rangeArray[i][0]<<" "<<m_rangeArray[i][1];
+
+       // this->m_rangeArray[i][2]=hist_max;
+
+
+    }
+    /*
+       float min=0;
+        float max=0;
+        float value=0;
+        int hist_max=0;
+        int j=0;
+        QStringList arguments;
+        //!Read from a VBT min max and the whole histogram for the scalar i
+        QProcess *myProcess = new QProcess();
+
+
+
+        QString histogramFilePath = "";
+        histogramFilePath.append(w->workingPath).append(QDir::separator()).append("profile.ascii");
+
+        QDateTime dt = QDateTime::currentDateTime ();
+        uint posixDT = dt.toMSecsSinceEpoch  ();
+
+        histogramFilePath.append(QString::number(posixDT));
+
+
+        QString locatorQtString = QString(this->m_locator.c_str());
+        QString qtColName = QString(this->getColName(i).c_str());
+        arguments<< "--op";
+        arguments.append("statistic");
+        arguments.append("--histogram");
+        arguments.append(QString::number(getbinNumber()));
+        arguments.append("--list");
+        arguments.append(qtColName);
+        arguments.append("--out");
+        arguments.append(histogramFilePath);
+        arguments.append(locatorQtString);
+
+       // qDebug()<<arguments;
+
+        myProcess->setStandardErrorFile(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append(QDir::separator()).append("log_error.txt"), QIODevice::Append);
+        myProcess->setStandardOutputFile(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append(QDir::separator()).append("log_output.txt"), QIODevice::Append);
+
+        myProcess->start(w->filter,arguments);
+        myProcess->waitForFinished();
+        impStatus = myProcess->exitStatus();
+/*
+
+        /*  impStatus:
+            1 : an error occurred
+            0 : OK
+        */
+    /*
+        if (impStatus) return false;
+        std::string line,subFirst,subSecond;
+        ifstream myfile (histogramFilePath.toLatin1());
+        if (myfile.is_open())
+        {
+            while (! myfile.eof() )
+            {
+                // the file is an histogram, it has n lines containing 2 strings
+                // in the form of X(float) N(int)
+                getline (myfile,line);
+                std::istringstream iss(line);
+                iss>>subFirst;
+                iss>>subSecond;
+                value=atof(subFirst.c_str());
+                if (value<min)  min=value;
+                if (value>max)  max=value;
+                if (atoi(subSecond.c_str())>hist_max) hist_max=atoi(subSecond.c_str());
+                this->m_histogramValueArray[i][j] =value;
+                this->m_histogramArray[i][j] = atoi(subSecond.c_str());
+                j++;
+            }
+            myfile.close();
+            QFile::remove(histogramFilePath.toLatin1());
+        }
+        this->m_rangeArray[i][0]=min;
+        this->m_rangeArray[i][1]=max;
+        this->m_rangeArray[i][2]=hist_max;
+
+    }
+
+    */
+    return true;
+}
+
+
+
+QHash <QString, int> VSTableDesktop::getBandMerged500()
+{
+
+    return n500;
+}
+
+QHash <QString, int> VSTableDesktop::getBandMerged350()
+{
+    return n350;
+}
+
+QHash <QString, int> VSTableDesktop::getBandMerged250()
+{
+    return n250;
+}
+
+QHash <QString, int> VSTableDesktop::getBandMerged160()
+{
+    return n160;
+}
+
+QHash <QString, int> VSTableDesktop::getBandMerged70()
+{
+    return n70;
+}
+
+void VSTableDesktop::setBandMerged500(QHash <QString, int> bm)
+{
+
+    n500=bm;
+}
+
+void VSTableDesktop::setBandMerged350(QHash <QString, int> bm)
+{
+    n350=bm;
+}
+
+void VSTableDesktop::setBandMerged250(QHash <QString, int> bm)
+{
+    n250=bm;
+}
+
+void VSTableDesktop::setBandMerged160(QHash <QString, int> bm)
+{
+    n160=bm;
+}
+
+void VSTableDesktop::setBandMerged70(QHash <QString, int> bm)
+{
+    n70=bm;
+}
diff --git a/Code/src/vstabledesktop.h b/Code/src/vstabledesktop.h
new file mode 100644
index 0000000000000000000000000000000000000000..78b773450e61822c054207e8179dd21c4b4c5773
--- /dev/null
+++ b/Code/src/vstabledesktop.h
@@ -0,0 +1,177 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Ugo Becciani, Alessandro Costa                  *
+ *   ugo.becciani@oact.inaf.it                                             *
+ *   alessandro.costa@oact.inaf.it                                         *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef VSTABLEDESKTOP_H
+#define VSTABLEDESKTOP_H
+
+/**
+        @author Alessandro Costa <alessandro.costa@oact.inaf.it>
+        @author Ugo Becciani <ugo.becciani@oact.inaf.it>
+*/
+
+#include "vsobjectdesktop.h"
+#include <string>
+#include <vector>
+#include <QWidget>
+#include <QProcess>
+#include <QHash>
+
+extern "C" {
+    #include "visivo.h"
+}
+
+class VSTableDesktop : public VSObjectDesktop, QWidget
+{
+public:
+    bool readHeader(); //! fuction that read  the header table (filling the above values)
+private:
+    std::string m_locator;    //! table path
+    std::string m_endiannes;  //! endianism
+    std::string m_type;       //! data format (float, double ecc..)
+
+    static const unsigned long long int MAX_NUMBER_TO_SKIP; //! maximum number to skip in a single skip
+    static const unsigned long long int MAX_NUMBER_ROW_REQUEST; //! maximum number of rows for each request
+    static const unsigned long long int MAX_NUMBER_OF_BYTES; //! maximum number of bytes for each request
+
+    unsigned int m_nCols;  //! number of columns
+    unsigned long long int m_nRows; //! number of Rows
+    bool m_tableExist; //! flag that is true if tabnle already exist (written on disk)
+
+    bool m_isVolume;  //! true if table represent a volumetrivìc data
+    unsigned int m_cellNumber[3];  //! number of cell of the volume (mesh)
+    float m_cellSize[3]; //!cell geometry
+
+
+    std::vector<std::string> m_colVector; //! vector containing columns names std::vector
+
+    int m_binNumber; //! Each table has its bin number.
+    int ** m_histogramArray;
+    float ** m_rangeArray;
+    float ** m_histogramValueArray;
+
+    std::string **m_tableData;
+    //std::vector< std::vector<std::string> > m_tableData;
+
+    QHash <QString, int> n500;
+    QHash <QString, int> n350;
+    QHash <QString, int> n250;
+    QHash <QString, int> n160;
+    QHash <QString, int> n70;
+    int m_vialactea_wavelen;
+
+    VBT myVBT;
+
+public:
+    VSTableDesktop();
+    VSTableDesktop(std::string locator, std::string name = "", std::string description = "", bool statistic=true);
+    int getColumnString(unsigned int *colList, unsigned int nOfCol, unsigned long long int fromRow, unsigned long long int toRow, std::string **matrix);
+    void setNumberOfBins(int b){m_binNumber=b;}
+    ~VSTableDesktop();
+
+    void printSelf();  //!  write in stdlog the header of the table
+
+    std::string getLocator() {return m_locator;}  //! return the path
+
+    unsigned int getNumberOfColumns() { return m_nCols;}
+    unsigned long long int getNumberOfRows() { return m_nRows;}
+    QString histogramFilePath;
+    void setIsVolume(bool isVolume) { m_isVolume = isVolume; }
+    bool getIsVolume() { return m_isVolume; }
+    const unsigned int* getCellNumber() {return m_cellNumber;}
+    const float* getCellSize() {return m_cellSize;}
+
+    std::string getEndiannes() { return m_endiannes;}
+    std::string getType() { return m_type;}
+
+    bool setEndiannes(std::string endiannes);
+    bool setType(std::string type);
+    void setLocator(std::string locator);  //! set the table path (used in writeheader and readheader)
+    void setNumberOfRows(unsigned long long int nRows) {m_nRows = nRows;}
+    void setNumberOfColumns( int nCols) {m_nCols = nCols;}
+    void setColsNames(std::vector<std::string> colName) {m_colVector =colName ;}
+    void setTableData(std::string **tableData) {m_tableData =tableData ;}
+    void setWavelength(int wavelen) {m_vialactea_wavelen =wavelen ;}
+    int getWavelength() {return m_vialactea_wavelen;}
+    //void setTableData( std::vector< std::vector<std::string> > tableData) {m_tableData =tableData ;}
+
+
+    std::string** getTableData() {return m_tableData;}
+   // std::vector< std::vector<std::string> > getTableData() {return m_tableData;}
+    void setCellNumber(unsigned int xCellNumber, unsigned int yCellNumber, unsigned int zCellNumber);
+    void setCellSize(float xCellSize, float yCellSize, float zCellSize);
+    //void SetNumberOfCols(unsigned int nCols) {m_nCols = nCols;};
+    bool addCol(std::string name); //! add an element (a column name) to the vector m_colVector
+
+
+    void setColName(unsigned int i, std::string newColName); //! change the column name in the i position. The first column is 0.
+    std::string getColName(unsigned int i); //! return the column name in the i position
+
+    int getColId(std::string name); //! return the position of the column with specified name. The first column is 0.
+
+    bool writeHeader(); //! write a new table header with the given member variables
+
+    int getColumn(unsigned int *colList, unsigned int nOfCol, unsigned long long int fromRow, unsigned long long int toRow, float **fArray); //! colList is a vector of nOfCol Elements, listing the columns ID I want to read. fArray is a 2D allocated array fArray[nOfCol,(toRow-fromRow+1)]: the method fill fArray with the column listed in colList (first column is 0: colList contains data in the range  0 - m_nCols -1) and with the postion specified in fromRow toRow. fromRow starts from 0.
+    int getColumnList(unsigned int *colList, unsigned  int nOfCol, unsigned long long int *list,unsigned long long int nOfEle, float **fArray);//! colList is a vector of nOfCol Elements, listing the columns ID I want to read. First column is 0. list is a vectorn of nOfEle elements I want to read from the table. fArray is a 2D allocated array fArray[nOfCol,nOfEle]: the method fill fArray with the column listed in colList (first column is 0: colList contains data in the range  0 - m_nCols -1) and with elements listed in list
+    int putColumn(unsigned int *colList, unsigned int nOfCol, unsigned long long int fromRow, unsigned long long int toRow, float **fArray);  //!  put data (see getColumn)
+    int putColumnList(unsigned int *colList,unsigned int nOfCol, unsigned long long int *list, unsigned long long int nOfEle, float **fArray);//! put data (see getColumnList)
+
+    bool writeTable(float **fArray);  //!write header and table (fArray must contain all data table)
+    bool createTable(float **fArray); //!write data (header already exist) in the table
+    bool writeTable();  //!write only header
+    bool createTable(); //!write a zero filled table
+    bool tableExist() { return m_tableExist;} //! table already exist
+    bool checkTable() { return true;} //! Integrity check --- to be implemented!
+
+
+    void setHistogramArray(int ** h){m_histogramArray=h;}
+    void setRangeArray(float ** r){ m_rangeArray=r;}
+    void setHistogramValueArray(float ** h){m_histogramValueArray=h;}
+
+
+    void getRange(unsigned int i, float *array);
+    //void getHistogram(unsigned int i, int *histogram);
+    QVector<double> getHistogram(unsigned int i);
+   // void getHistogramValue(unsigned int i, float *histogram);
+    QVector<double> getHistogramValue(unsigned int i);
+    int getColumnStringToFloat(unsigned int *colList, unsigned int nOfCol, unsigned long long int fromRow, unsigned long long int toRow, float **fArray);
+
+
+    bool readStatistics(); //! fuction that read  the  table statistic
+    int getbinNumber();
+
+    QHash <QString, int> getBandMerged500();
+    QHash <QString, int> getBandMerged350();
+    QHash <QString, int> getBandMerged250();
+    QHash <QString, int> getBandMerged160();
+    QHash <QString, int> getBandMerged70();
+
+    void setBandMerged500(QHash <QString, int> bm);
+    void setBandMerged350(QHash <QString, int> bm);
+    void setBandMerged250(QHash <QString, int> bm);
+    void setBandMerged160(QHash <QString, int> bm);
+    void setBandMerged70(QHash <QString, int> bm);
+
+private slots:
+    void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
+
+};
+
+#endif
diff --git a/Code/src/vtkLegendScaleActor.cpp b/Code/src/vtkLegendScaleActor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3f4dcba6f29c401f1c408c2b832fe5e2a3d478e2
--- /dev/null
+++ b/Code/src/vtkLegendScaleActor.cpp
@@ -0,0 +1,633 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkLegendScaleActor.cxx
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+#include "vtkLegendScaleActor.h"
+#include "vtkAxisActor2D.h"
+#include "vtkCommand.h"
+#include "vtkObjectFactory.h"
+#include "vtkMath.h"
+#include "vtkCamera.h"
+#include "vtkRenderer.h"
+#include "vtkWindow.h"
+#include "vtkPolyData.h"
+#include "vtkPoints.h"
+#include "vtkCellArray.h"
+#include "vtkUnsignedCharArray.h"
+#include "vtkActor2D.h"
+#include "vtkPolyDataMapper2D.h"
+#include "vtkTextMapper.h"
+#include "vtkTextProperty.h"
+#include "vtkCellData.h"
+#include "vtkCoordinate.h"
+#include "qdebug.h"
+#include "astroutils.h"
+#include "vtkFitsReader.h"
+#include "vtkSmartPointer.h"
+
+vtkStandardNewMacro(vtkLegendScaleActor);
+
+
+//----------------------------------------------------------------------
+vtkLegendScaleActor::vtkLegendScaleActor()
+{
+    //this->LabelMode = DISTANCE;
+    this->LabelMode = XY_COORDINATES;
+    this->RightBorderOffset = 100;
+    this->TopBorderOffset = 30;
+    this->LeftBorderOffset = 100;
+    this->BottomBorderOffset = 30;
+    this->CornerOffsetFactor = 2.0;
+
+    this->RightAxis = vtkAxisActor2D::New();
+    this->RightAxis->GetPositionCoordinate()->SetCoordinateSystemToViewport();
+    this->RightAxis->GetPosition2Coordinate()->SetCoordinateSystemToViewport();
+    this->RightAxis->GetPositionCoordinate()->SetReferenceCoordinate(NULL);
+    this->RightAxis->SetFontFactor(0.6);
+    this->RightAxis->SetNumberOfLabels(5);
+    this->RightAxis->SetLabelFormat("%6.4f");
+    this->RightAxis->AdjustLabelsOff();
+
+    this->TopAxis = vtkAxisActor2D::New();
+    this->TopAxis->GetPositionCoordinate()->SetCoordinateSystemToViewport();
+    this->TopAxis->GetPosition2Coordinate()->SetCoordinateSystemToViewport();
+    this->TopAxis->GetPositionCoordinate()->SetReferenceCoordinate(NULL);
+    this->TopAxis->SetFontFactor(0.6);
+    this->TopAxis->SetNumberOfLabels(5);
+    this->TopAxis->SetLabelFormat("%6.4f");
+    this->TopAxis->AdjustLabelsOff();
+
+    this->LeftAxis = vtkAxisActor2D::New();
+    this->LeftAxis->GetPositionCoordinate()->SetCoordinateSystemToViewport();
+    this->LeftAxis->GetPosition2Coordinate()->SetCoordinateSystemToViewport();
+    this->LeftAxis->GetPositionCoordinate()->SetReferenceCoordinate(NULL);
+    this->LeftAxis->SetFontFactor(0.6);
+    this->LeftAxis->SetNumberOfLabels(5);
+    this->LeftAxis->SetLabelFormat("%6.4f");
+    this->LeftAxis->AdjustLabelsOff();
+
+    this->BottomAxis = vtkAxisActor2D::New();
+    this->BottomAxis->GetPositionCoordinate()->SetCoordinateSystemToViewport();
+    this->BottomAxis->GetPosition2Coordinate()->SetCoordinateSystemToViewport();
+    this->BottomAxis->GetPositionCoordinate()->SetReferenceCoordinate(NULL);
+    this->BottomAxis->SetFontFactor(0.6);
+    this->BottomAxis->SetNumberOfLabels(5);
+    this->BottomAxis->SetLabelFormat("%6.4f");
+    this->BottomAxis->AdjustLabelsOff();
+
+    this->RightAxisVisibility = 1;
+    this->TopAxisVisibility = 1;
+    this->LeftAxisVisibility = 1;
+    this->BottomAxisVisibility = 1;
+
+    this->LegendVisibility = 1;
+    this->Legend = vtkPolyData::New();
+    this->LegendPoints = vtkPoints::New();
+    this->Legend->SetPoints(this->LegendPoints);
+    this->LegendMapper = vtkPolyDataMapper2D::New();
+    this->LegendMapper->SetInputData(this->Legend);
+    this->LegendActor = vtkActor2D::New();
+    this->LegendActor->SetMapper(this->LegendMapper);
+
+    // Create the legend
+    vtkIdType pts[4];
+    this->LegendPoints->SetNumberOfPoints(10);
+    vtkCellArray *legendPolys = vtkCellArray::New();
+    legendPolys->Allocate(legendPolys->EstimateSize(4,4));
+    pts[0] = 0; pts[1] = 1; pts[2] = 6; pts[3] = 5;
+    legendPolys->InsertNextCell(4,pts);
+    pts[0] = 1; pts[1] = 2; pts[2] = 7; pts[3] = 6;
+    legendPolys->InsertNextCell(4,pts);
+    pts[0] = 2; pts[1] = 3; pts[2] = 8; pts[3] = 7;
+    legendPolys->InsertNextCell(4,pts);
+    pts[0] = 3; pts[1] = 4; pts[2] = 9; pts[3] = 8;
+    legendPolys->InsertNextCell(4,pts);
+    this->Legend->SetPolys(legendPolys);
+    legendPolys->Delete();
+
+    // Create the cell data
+    vtkUnsignedCharArray *colors = vtkUnsignedCharArray::New();
+    colors->SetNumberOfComponents(3);
+    colors->SetNumberOfTuples(4);
+    colors->SetTuple3(0,0,0,0);
+    colors->SetTuple3(1,255,255,255);
+    colors->SetTuple3(2,0,0,0);
+    colors->SetTuple3(3,255,255,255);
+    this->Legend->GetCellData()->SetScalars(colors);
+    colors->Delete();
+
+    // Now the text. The first five are for the 0,1/4,1/2,3/4,1 labels.
+    this->LegendTitleProperty = vtkTextProperty::New();
+    this->LegendTitleProperty->SetJustificationToCentered();
+    this->LegendTitleProperty->SetVerticalJustificationToBottom();
+    this->LegendTitleProperty->SetBold(1);
+    this->LegendTitleProperty->SetItalic(1);
+    this->LegendTitleProperty->SetShadow(1);
+    this->LegendTitleProperty->SetFontFamilyToArial();
+    this->LegendTitleProperty->SetFontSize(10);
+    this->LegendLabelProperty = vtkTextProperty::New();
+    this->LegendLabelProperty->SetJustificationToCentered();
+    this->LegendLabelProperty->SetVerticalJustificationToTop();
+    this->LegendLabelProperty->SetBold(1);
+    this->LegendLabelProperty->SetItalic(1);
+    this->LegendLabelProperty->SetShadow(1);
+    this->LegendLabelProperty->SetFontFamilyToArial();
+    this->LegendLabelProperty->SetFontSize(8);
+    for (int i=0; i<6; i++)
+    {
+        this->LabelMappers[i] = vtkTextMapper::New();
+        this->LabelMappers[i]->SetTextProperty(this->LegendLabelProperty);
+        this->LabelActors[i] = vtkActor2D::New();
+        this->LabelActors[i]->SetMapper(this->LabelMappers[i]);
+    }
+    this->LabelMappers[5]->SetTextProperty(this->LegendTitleProperty);
+    this->LabelMappers[0]->SetInput("0");
+    this->LabelMappers[1]->SetInput("1/4");
+    this->LabelMappers[2]->SetInput("1/2");
+    this->LabelMappers[3]->SetInput("3/4");
+    this->LabelMappers[4]->SetInput("1");
+
+    this->Coordinate = vtkCoordinate::New();
+    this->Coordinate->SetCoordinateSystemToDisplay();
+}
+
+//----------------------------------------------------------------------
+vtkLegendScaleActor::~vtkLegendScaleActor()
+{
+    this->RightAxis->Delete();
+    this->TopAxis->Delete();
+    this->LeftAxis->Delete();
+    this->BottomAxis->Delete();
+
+    this->Legend->Delete();
+    this->LegendPoints->Delete();
+    this->LegendMapper->Delete();
+    this->LegendActor->Delete();
+
+    for (int i=0; i<6; i++)
+    {
+        this->LabelMappers[i]->Delete();
+        this->LabelActors[i]->Delete();
+    }
+    this->LegendTitleProperty->Delete();
+    this->LegendLabelProperty->Delete();
+    this->Coordinate->Delete();
+}
+
+//----------------------------------------------------------------------
+void vtkLegendScaleActor::GetActors2D(vtkPropCollection *pc)
+{
+    pc->AddItem(this->RightAxis);
+    pc->AddItem(this->TopAxis);
+    pc->AddItem(this->LeftAxis);
+    pc->AddItem(this->BottomAxis);
+}
+
+//----------------------------------------------------------------------
+void vtkLegendScaleActor::ReleaseGraphicsResources(vtkWindow *w)
+{
+    this->RightAxis->ReleaseGraphicsResources(w);
+    this->TopAxis->ReleaseGraphicsResources(w);
+    this->LeftAxis->ReleaseGraphicsResources(w);
+    this->BottomAxis->ReleaseGraphicsResources(w);
+
+    this->LegendActor->ReleaseGraphicsResources(w);
+
+    for (int i=0; i<6; i++)
+    {
+        this->LabelActors[i]->ReleaseGraphicsResources(w);
+    }
+}
+
+//----------------------------------------------------------------------
+int vtkLegendScaleActor::RenderOpaqueGeometry(vtkViewport *viewport)
+{
+    this->BuildRepresentation(viewport);
+
+    int renderedSomething=0;
+    if ( this->RightAxisVisibility )
+    {
+        renderedSomething = this->RightAxis->RenderOpaqueGeometry(viewport);
+    }
+    if ( this->TopAxisVisibility )
+    {
+        renderedSomething += this->TopAxis->RenderOpaqueGeometry(viewport);
+    }
+    if ( this->LeftAxisVisibility )
+    {
+        renderedSomething += this->LeftAxis->RenderOpaqueGeometry(viewport);
+    }
+    if ( this->BottomAxisVisibility )
+    {
+        renderedSomething += this->BottomAxis->RenderOpaqueGeometry(viewport);
+    }
+    if ( this->LegendVisibility )
+    {
+        renderedSomething += this->LegendActor->RenderOpaqueGeometry(viewport);
+        renderedSomething += this->LabelActors[0]->RenderOpaqueGeometry(viewport);
+        renderedSomething += this->LabelActors[1]->RenderOpaqueGeometry(viewport);
+        renderedSomething += this->LabelActors[2]->RenderOpaqueGeometry(viewport);
+        renderedSomething += this->LabelActors[3]->RenderOpaqueGeometry(viewport);
+        renderedSomething += this->LabelActors[4]->RenderOpaqueGeometry(viewport);
+        renderedSomething += this->LabelActors[5]->RenderOpaqueGeometry(viewport);
+    }
+
+    return renderedSomething;
+}
+
+//----------------------------------------------------------------------
+int vtkLegendScaleActor::RenderOverlay(vtkViewport *viewport)
+{
+    int renderedSomething=0;
+    if ( this->RightAxisVisibility )
+    {
+        renderedSomething = this->RightAxis->RenderOverlay(viewport);
+    }
+    if ( this->TopAxisVisibility )
+    {
+        renderedSomething += this->TopAxis->RenderOverlay(viewport);
+    }
+    if ( this->LeftAxisVisibility )
+    {
+        renderedSomething += this->LeftAxis->RenderOverlay(viewport);
+    }
+    if ( this->BottomAxisVisibility )
+    {
+        renderedSomething += this->BottomAxis->RenderOverlay(viewport);
+    }
+    if ( this->LegendVisibility )
+    {
+        renderedSomething += this->LegendActor->RenderOverlay(viewport);
+        renderedSomething += this->LabelActors[0]->RenderOverlay(viewport);
+        renderedSomething += this->LabelActors[1]->RenderOverlay(viewport);
+        renderedSomething += this->LabelActors[2]->RenderOverlay(viewport);
+        renderedSomething += this->LabelActors[3]->RenderOverlay(viewport);
+        renderedSomething += this->LabelActors[4]->RenderOverlay(viewport);
+        renderedSomething += this->LabelActors[5]->RenderOverlay(viewport);
+    }
+
+    return renderedSomething;
+}
+
+
+void vtkLegendScaleActor::setFitsFile(vtkSmartPointer<vtkFitsReader> fits )
+{
+    myfits=fits;
+}
+
+//----------------------------------------------------------------------
+void vtkLegendScaleActor::BuildRepresentation(vtkViewport *viewport)
+{
+    if ( 1 ) //it's probably best just to rerender every time
+        //   if ( this->GetMTime() > this->BuildTime ||
+        //        (this->Renderer && this->Renderer->GetVTKWindow() &&
+        //         this->Renderer->GetVTKWindow()->GetMTime() > this->BuildTime) )
+    {
+        // Specify the locations of the axes.
+        int *size = viewport->GetSize();
+
+        this->RightAxis->GetPositionCoordinate()-> SetValue(size[0]-this->RightBorderOffset, this->CornerOffsetFactor*this->BottomBorderOffset,0.0);
+        this->RightAxis->GetPosition2Coordinate()->SetValue(size[0]-this->RightBorderOffset, size[1]-this->CornerOffsetFactor*this->TopBorderOffset,0.0);
+        this->TopAxis->GetPositionCoordinate()->
+                SetValue(size[0]-this->CornerOffsetFactor*this->RightBorderOffset,
+                size[1]-this->TopBorderOffset,0.0);
+        this->TopAxis->GetPosition2Coordinate()->
+                SetValue(this->CornerOffsetFactor*this->LeftBorderOffset,
+                         size[1]-this->TopBorderOffset,0.0);
+
+        this->LeftAxis->GetPositionCoordinate()->
+                SetValue(this->LeftBorderOffset,
+                         size[1]-this->CornerOffsetFactor*this->TopBorderOffset,0.0);
+        this->LeftAxis->GetPosition2Coordinate()->
+                SetValue(this->LeftBorderOffset,
+                         this->CornerOffsetFactor*this->BottomBorderOffset,0.0);
+
+        if ( this->LegendVisibility )
+        {
+            this->BottomAxis->GetPositionCoordinate()->
+                    SetValue(this->CornerOffsetFactor*this->LeftBorderOffset,
+                             2*this->BottomBorderOffset,0.0);
+            this->BottomAxis->GetPosition2Coordinate()->
+                    SetValue(size[0]-this->CornerOffsetFactor*this->RightBorderOffset,
+                    2*this->BottomBorderOffset,0.0);
+        }
+        else
+        {
+            this->BottomAxis->GetPositionCoordinate()->
+                    SetValue(this->CornerOffsetFactor*this->LeftBorderOffset,
+                             this->BottomBorderOffset,0.0);
+            this->BottomAxis->GetPosition2Coordinate()->
+                    SetValue(size[0]-this->CornerOffsetFactor*this->RightBorderOffset,
+                    this->BottomBorderOffset,0.0);
+        }
+
+
+        // Now specify the axis values
+        if ( this->LabelMode == XY_COORDINATES )
+        {
+
+            double *sky_coord_gal = new double[2];
+            double lo,up;
+
+            double *xL = this->RightAxis->GetPositionCoordinate()-> GetComputedWorldValue(viewport);
+            double *xR = this->RightAxis->GetPosition2Coordinate()-> GetComputedWorldValue(viewport);
+
+            AstroUtils().xy2sky(myfits->GetFileName(),xR[1],xL[1],sky_coord_gal,3);
+            lo=sky_coord_gal[1];
+
+            AstroUtils().xy2sky(myfits->GetFileName(),xL[1],xR[1],sky_coord_gal,3);
+            up=sky_coord_gal[1];
+
+           this->RightAxis->SetRange(lo,up);
+
+            xL = this->LeftAxis->GetPositionCoordinate()-> GetComputedWorldValue(viewport);
+            xR = this->LeftAxis->GetPosition2Coordinate()-> GetComputedWorldValue(viewport);
+            this->LeftAxis->SetRange(up,lo);
+
+            xL = this->TopAxis->GetPositionCoordinate()-> GetComputedWorldValue(viewport);
+            xR = this->TopAxis->GetPosition2Coordinate()-> GetComputedWorldValue(viewport);
+
+            xL = this->BottomAxis->GetPositionCoordinate()->GetComputedWorldValue(viewport);
+            xR = this->BottomAxis->GetPosition2Coordinate()->GetComputedWorldValue(viewport);
+
+            AstroUtils().xy2sky(myfits->GetFileName(),xL[0],xR[0],sky_coord_gal,3);
+            lo=sky_coord_gal[0];
+
+            AstroUtils().xy2sky(myfits->GetFileName(),xR[0],xL[0],sky_coord_gal,3);
+            up=sky_coord_gal[0];
+
+
+            this->BottomAxis->SetRange(lo,up);
+            this->TopAxis->SetRange(up, lo);
+
+
+        }
+        else //distance between points
+        {
+            double d;
+
+            double *xL = this->RightAxis->GetPositionCoordinate()->
+                    GetComputedWorldValue(viewport);
+            double *xR = this->RightAxis->GetPosition2Coordinate()->
+                    GetComputedWorldValue(viewport);
+            d = sqrt (vtkMath::Distance2BetweenPoints(xL,xR));
+            this->RightAxis->SetRange(-d/2.0,d/2.0);
+
+            xL = this->TopAxis->GetPositionCoordinate()->
+                    GetComputedWorldValue(viewport);
+            xR = this->TopAxis->GetPosition2Coordinate()->
+                    GetComputedWorldValue(viewport);
+            d = sqrt (vtkMath::Distance2BetweenPoints(xL,xR));
+            this->TopAxis->SetRange(d/2.0,-d/2.0);
+
+            xL = this->LeftAxis->GetPositionCoordinate()->
+                    GetComputedWorldValue(viewport);
+            xR = this->LeftAxis->GetPosition2Coordinate()->
+                    GetComputedWorldValue(viewport);
+            d = sqrt (vtkMath::Distance2BetweenPoints(xL,xR));
+            this->LeftAxis->SetRange(d/2.0,-d/2.0);
+
+            xL = this->BottomAxis->GetPositionCoordinate()->
+                    GetComputedWorldValue(viewport);
+            xR = this->BottomAxis->GetPosition2Coordinate()->
+                    GetComputedWorldValue(viewport);
+            d = sqrt (vtkMath::Distance2BetweenPoints(xL,xR));
+            this->BottomAxis->SetRange(-d/2.0,d/2.0);
+        }
+
+        if ( this->LegendVisibility )
+        {
+            // Update the position
+            double x1 = 0.33333*size[0];
+            double delX = x1/4.0;
+
+            this->LegendPoints->SetPoint(0, x1,10,0);
+            this->LegendPoints->SetPoint(1, x1+delX,10,0);
+            this->LegendPoints->SetPoint(2, x1+2*delX,10,0);
+            this->LegendPoints->SetPoint(3, x1+3*delX,10,0);
+            this->LegendPoints->SetPoint(4, x1+4*delX,10,0);
+            this->LegendPoints->SetPoint(5, x1,20,0);
+            this->LegendPoints->SetPoint(6, x1+delX,20,0);
+            this->LegendPoints->SetPoint(7, x1+2*delX,20,0);
+            this->LegendPoints->SetPoint(8, x1+3*delX,20,0);
+            this->LegendPoints->SetPoint(9, x1+4*delX,20,0);
+
+            // Specify the position of the legend title
+            this->LabelActors[5]->SetPosition(0.5*size[0],22);
+            this->Coordinate->SetValue(0.33333*size[0],15,0.0);
+            double *x = this->Coordinate->GetComputedWorldValue(viewport);
+            double xL[3]; xL[0]=x[0];xL[1]=x[1];xL[2]=x[2];
+            this->Coordinate->SetValue(0.66667*size[0],15,0.0);
+            x = this->Coordinate->GetComputedWorldValue(viewport);
+            double xR[3]; xR[0]=x[0];xR[1]=x[1];xR[2]=x[2];
+            double len = sqrt(vtkMath::Distance2BetweenPoints(xL,xR));
+            char buf[256];
+            sprintf(buf,"Scale 1 : %g",len);
+            this->LabelMappers[5]->SetInput(buf);
+
+            // Now specify the position of the legend labels
+            x = this->LegendPoints->GetPoint(0);
+            this->LabelActors[0]->SetPosition(x[0],x[1]-1);
+            x = this->LegendPoints->GetPoint(1);
+            this->LabelActors[1]->SetPosition(x[0],x[1]-1);
+            x = this->LegendPoints->GetPoint(2);
+            this->LabelActors[2]->SetPosition(x[0],x[1]-1);
+            x = this->LegendPoints->GetPoint(3);
+            this->LabelActors[3]->SetPosition(x[0],x[1]-1);
+            x = this->LegendPoints->GetPoint(4);
+            this->LabelActors[4]->SetPosition(x[0],x[1]-1);
+        }
+
+        this->BuildTime.Modified();
+    }
+}
+
+//----------------------------------------------------------------------
+void vtkLegendScaleActor::AllAnnotationsOn()
+{
+    if ( this->RightAxisVisibility && this->TopAxisVisibility &&
+         this->LeftAxisVisibility && this->BottomAxisVisibility &&
+         this->LegendVisibility )
+    {
+        return;
+    }
+
+    // If here, we are modified and something gets turned on
+    this->RightAxisVisibility = 1;
+    this->TopAxisVisibility = 1;
+    this->LeftAxisVisibility = 1;
+    this->BottomAxisVisibility = 1;
+    this->LegendVisibility = 1;
+    this->Modified();
+}
+
+//----------------------------------------------------------------------
+void vtkLegendScaleActor::AllAnnotationsOff()
+{
+    if ( !this->RightAxisVisibility && !this->TopAxisVisibility &&
+         !this->LeftAxisVisibility && !this->BottomAxisVisibility &&
+         !this->LegendVisibility )
+    {
+        return;
+    }
+
+    // If here, we are modified and something gets turned off
+    this->RightAxisVisibility = 0;
+    this->TopAxisVisibility = 0;
+    this->LeftAxisVisibility = 0;
+    this->BottomAxisVisibility = 0;
+    this->LegendVisibility = 0;
+    this->Modified();
+}
+
+//----------------------------------------------------------------------
+void vtkLegendScaleActor::AllAxesOn()
+{
+    if ( this->RightAxisVisibility && this->TopAxisVisibility &&
+         this->LeftAxisVisibility && this->BottomAxisVisibility )
+    {
+        return;
+    }
+
+    // If here, we are modified and something gets turned on
+    this->RightAxisVisibility = 1;
+    this->TopAxisVisibility = 1;
+    this->LeftAxisVisibility = 1;
+    this->BottomAxisVisibility = 1;
+    this->Modified();
+}
+
+//----------------------------------------------------------------------
+void vtkLegendScaleActor::AllAxesOff()
+{
+    if ( !this->RightAxisVisibility && !this->TopAxisVisibility &&
+         !this->LeftAxisVisibility && !this->BottomAxisVisibility )
+    {
+        return;
+    }
+
+    // If here, we are modified and something gets turned off
+    this->RightAxisVisibility = 0;
+    this->TopAxisVisibility = 0;
+    this->LeftAxisVisibility = 0;
+    this->BottomAxisVisibility = 0;
+    this->Modified();
+}
+/*
+//----------------------------------------------------------------------
+void vtkLegendScaleActor::PrintSelf(ostream& os, vtkIndent indent)
+{
+    //Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h
+    this->Superclass::PrintSelf(os,indent);
+
+    os << indent << "Label Mode: ";
+    if ( this->LabelMode == DISTANCE )
+    {
+        os << "Distance\n";
+    }
+    else //if ( this->LabelMode == DISTANCE )
+    {
+        os << "XY_Coordinates\n";
+    }
+
+    os << indent << "Right Axis Visibility: "
+       << (this->RightAxisVisibility ? "On\n" : "Off\n");
+    os << indent << "Top Axis Visibility: "
+       << (this->TopAxisVisibility ? "On\n" : "Off\n");
+    os << indent << "Left Axis Visibility: "
+       << (this->LeftAxisVisibility ? "On\n" : "Off\n");
+    os << indent << "Bottom Axis Visibility: "
+       << (this->BottomAxisVisibility ? "On\n" : "Off\n");
+    os << indent << "Legend Visibility: "
+       << (this->LegendVisibility ? "On\n" : "Off\n");
+    os << indent << "Corner Offset Factor: " << this->CornerOffsetFactor << "\n";
+
+    os << indent << "Right Border Offset: " << this->RightBorderOffset << "\n";
+    os << indent << "Top Border Offset: " << this->TopBorderOffset << "\n";
+    os << indent << "Left Border Offset: " << this->LeftBorderOffset << "\n";
+    os << indent << "Bottom Border Offset: " << this->BottomBorderOffset << "\n";
+
+    os << indent << "Legend Title Property: ";
+    if ( this->LegendTitleProperty )
+    {
+        os << this->LegendTitleProperty << "\n";
+    }
+    else
+    {
+        os << "(none)\n";
+    }
+    os << indent << "Legend Label Property: ";
+    if ( this->LegendLabelProperty )
+    {
+        os << this->LegendLabelProperty << "\n";
+    }
+    else
+    {
+        os << "(none)\n";
+    }
+
+    os << indent << "Right Axis: ";
+    if ( this->RightAxis )
+    {
+        os << this->RightAxis << "\n";
+    }
+    else
+    {
+        os << "(none)\n";
+    }
+    os << indent << "Top Axis: ";
+    if ( this->TopAxis )
+    {
+        os << this->TopAxis << "\n";
+    }
+    else
+    {
+        os << "(none)\n";
+    }
+    os << indent << "Left Axis: ";
+    if ( this->LeftAxis )
+    {
+        os << this->LeftAxis << "\n";
+    }
+    else
+    {
+        os << "(none)\n";
+    }
+    os << indent << "Bottom Axis: ";
+    if ( this->BottomAxis )
+    {
+        os << this->BottomAxis << "\n";
+    }
+    else
+    {
+        os << "(none)\n";
+    }
+}
+
+//----------------------------------------------------------------------------
+*/
+void vtkLegendScaleActor::PrintSelf(ostream& os, vtkIndent indent)
+{
+    // this->Superclass::PrintSelf(os, indent);
+}
+
+void vtkLegendScaleActor::PrintHeader(ostream& os, vtkIndent indent)
+{
+    // this->Superclass::PrintHeader(os, indent);
+
+}
+
+void vtkLegendScaleActor::PrintTrailer(std::ostream& os , vtkIndent indent)
+{
+    // this->Superclass::PrintTrailer(os, indent);
+}
diff --git a/Code/src/vtkLegendScaleActor.h b/Code/src/vtkLegendScaleActor.h
new file mode 100644
index 0000000000000000000000000000000000000000..d176d3930f02c7c6af93c864ea03a6e6d5be4a82
--- /dev/null
+++ b/Code/src/vtkLegendScaleActor.h
@@ -0,0 +1,218 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkLegendScaleActor.h
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+// .NAME vtkLegendScaleActor - annotate the render window with scale and distance information
+// .SECTION Description
+// This class is used to annotate the render window. Its basic goal is to
+// provide an indication of the scale of the scene. Four axes surrounding the
+// render window indicate (in a variety of ways) the scale of what the camera
+// is viewing. An option also exists for displaying a scale legend.
+//
+// The axes can be programmed either to display distance scales or x-y
+// coordinate values. By default, the scales display a distance. However,
+// if you know that the view is down the z-axis, the scales can be programmed
+// to display x-y coordinate values.
+//
+// .SECTION Caveats
+// Please be aware that the axes and scale values are subject to perspective
+// effects. The distances are computed in the focal plane of the camera.
+// When there are large view angles (i.e., perspective projection), the
+// computed distances may provide users the wrong sense of scale. These
+// effects are not present when parallel projection is enabled.
+
+#ifndef vtkLegendScaleActor_h
+#define vtkLegendScaleActor_h
+
+#include "vtkRenderingAnnotationModule.h" // For export macro
+#include "vtkProp.h"
+#include "vtkCoordinate.h" // For vtkViewportCoordinateMacro
+#include "vtkSmartPointer.h"
+#include "vtkFitsReader.h"
+#include "vtkImageActor.h"
+
+class vtkAxisActor2D;
+class vtkTextProperty;
+class vtkPolyData;
+class vtkPolyDataMapper2D;
+class vtkActor2D;
+class vtkTextMapper;
+class vtkPoints;
+class vtkCoordinate;
+
+class VTKRENDERINGANNOTATION_EXPORT vtkLegendScaleActor : public vtkProp
+{
+public:
+  // Description:
+  // Instantiate the class.
+  static vtkLegendScaleActor *New();
+  void setFitsFile(vtkSmartPointer<vtkFitsReader> fits);
+
+  // Description:
+  // Standard methods for the class.
+  vtkTypeMacro(vtkLegendScaleActor,vtkProp);
+ // void PrintSelf(ostream& os, vtkIndent indent);
+  void PrintHeader(ostream& os, vtkIndent indent);
+  void PrintTrailer(std::ostream& os , vtkIndent indent);
+  void PrintSelf(ostream& os, vtkIndent indent);
+//BTX
+  enum AttributeLocation
+  {
+    DISTANCE=0,
+    XY_COORDINATES=1
+  };
+//ETX
+
+  // Description:
+  // Specify the mode for labeling the scale axes. By default, the axes are
+  // labeled with the distance between points (centered at a distance of
+  // 0.0). Alternatively if you know that the view is down the z-axis; the
+  // axes can be labeled with x-y coordinate values.
+  vtkSetClampMacro(LabelMode,int,DISTANCE,XY_COORDINATES);
+  vtkGetMacro(LabelMode,int);
+  void SetLabelModeToDistance() {this->SetLabelMode(DISTANCE);}
+  void SetLabelModeToXYCoordinates() {this->SetLabelMode(XY_COORDINATES);}
+
+  // Description:
+  // Set/Get the flags that control which of the four axes to display (top,
+  // bottom, left and right). By default, all the axes are displayed.
+  vtkSetMacro(RightAxisVisibility,int);
+  vtkGetMacro(RightAxisVisibility,int);
+  vtkBooleanMacro(RightAxisVisibility,int);
+  vtkSetMacro(TopAxisVisibility,int);
+  vtkGetMacro(TopAxisVisibility,int);
+  vtkBooleanMacro(TopAxisVisibility,int);
+  vtkSetMacro(LeftAxisVisibility,int);
+  vtkGetMacro(LeftAxisVisibility,int);
+  vtkBooleanMacro(LeftAxisVisibility,int);
+  vtkSetMacro(BottomAxisVisibility,int);
+  vtkGetMacro(BottomAxisVisibility,int);
+  vtkBooleanMacro(BottomAxisVisibility,int);
+
+  // Description:
+  // Indicate whether the legend scale should be displayed or not.
+  // The default is On.
+  vtkSetMacro(LegendVisibility,int);
+  vtkGetMacro(LegendVisibility,int);
+  vtkBooleanMacro(LegendVisibility,int);
+
+  // Description:
+  // Convenience method that turns all the axes either on or off.
+  void AllAxesOn();
+  void AllAxesOff();
+
+  // Description:
+  // Convenience method that turns all the axes and the legend scale.
+  void AllAnnotationsOn();
+  void AllAnnotationsOff();
+
+  // Description:
+  // Set/Get the offset of the right axis from the border. This number is expressed in
+  // pixels, and represents the approximate distance of the axes from the sides
+  // of the renderer. The default is 50.
+  vtkSetClampMacro(RightBorderOffset,int,5,VTK_INT_MAX);
+  vtkGetMacro(RightBorderOffset,int);
+
+  // Description:
+  // Set/Get the offset of the top axis from the border. This number is expressed in
+  // pixels, and represents the approximate distance of the axes from the sides
+  // of the renderer. The default is 30.
+  vtkSetClampMacro(TopBorderOffset,int,5,VTK_INT_MAX);
+  vtkGetMacro(TopBorderOffset,int);
+
+  // Description:
+  // Set/Get the offset of the left axis from the border. This number is expressed in
+  // pixels, and represents the approximate distance of the axes from the sides
+  // of the renderer. The default is 50.
+  vtkSetClampMacro(LeftBorderOffset,int,5,VTK_INT_MAX);
+  vtkGetMacro(LeftBorderOffset,int);
+
+  // Description:
+  // Set/Get the offset of the bottom axis from the border. This number is expressed in
+  // pixels, and represents the approximate distance of the axes from the sides
+  // of the renderer. The default is 30.
+  vtkSetClampMacro(BottomBorderOffset,int,5,VTK_INT_MAX);
+  vtkGetMacro(BottomBorderOffset,int);
+
+  // Description:
+  // Get/Set the corner offset. This is the offset factor used to offset the
+  // axes at the corners. Default value is 2.0.
+  vtkSetClampMacro(CornerOffsetFactor, double, 1.0, 10.0);
+  vtkGetMacro(CornerOffsetFactor, double);
+
+  // Description:
+  // Set/Get the labels text properties for the legend title and labels.
+  vtkGetObjectMacro(LegendTitleProperty,vtkTextProperty);
+  vtkGetObjectMacro(LegendLabelProperty,vtkTextProperty);
+
+  // Description:
+  // These are methods to retrieve the vtkAxisActors used to represent
+  // the four axes that form this representation. Users may retrieve and
+  // then modify these axes to control their appearance.
+  vtkGetObjectMacro(RightAxis,vtkAxisActor2D);
+  vtkGetObjectMacro(TopAxis,vtkAxisActor2D);
+  vtkGetObjectMacro(LeftAxis,vtkAxisActor2D);
+  vtkGetObjectMacro(BottomAxis,vtkAxisActor2D);
+
+  // Decsription:
+  // Standard methods supporting the rendering process.
+  virtual void BuildRepresentation(vtkViewport *viewport);
+  virtual void GetActors2D(vtkPropCollection*);
+  virtual void ReleaseGraphicsResources(vtkWindow*);
+  virtual int RenderOverlay(vtkViewport*);
+  virtual int RenderOpaqueGeometry(vtkViewport*);
+
+protected:
+  vtkLegendScaleActor();
+  ~vtkLegendScaleActor();
+
+  int    LabelMode;
+  int    RightBorderOffset;
+  int    TopBorderOffset;
+  int    LeftBorderOffset;
+  int    BottomBorderOffset;
+  double CornerOffsetFactor;
+
+  // The four axes around the borders of the renderer
+  vtkAxisActor2D *RightAxis;
+  vtkAxisActor2D *TopAxis;
+  vtkAxisActor2D *LeftAxis;
+  vtkAxisActor2D *BottomAxis;
+
+  // Control the display of the axes
+  int RightAxisVisibility;
+  int TopAxisVisibility;
+  int LeftAxisVisibility;
+  int BottomAxisVisibility;
+
+  // Support for the legend.
+  int                  LegendVisibility;
+  vtkPolyData         *Legend;
+  vtkPoints           *LegendPoints;
+  vtkPolyDataMapper2D *LegendMapper;
+  vtkActor2D          *LegendActor;
+  vtkTextMapper       *LabelMappers[6];
+  vtkActor2D          *LabelActors[6];
+  vtkTextProperty     *LegendTitleProperty;
+  vtkTextProperty     *LegendLabelProperty;
+  vtkCoordinate       *Coordinate;
+
+  vtkTimeStamp         BuildTime;
+
+private:
+  vtkLegendScaleActor(const vtkLegendScaleActor&);  //Not implemented
+  void operator=(const vtkLegendScaleActor&);  //Not implemented
+  vtkSmartPointer<vtkFitsReader> myfits;
+};
+
+#endif
diff --git a/Code/src/vtkellipse.cpp b/Code/src/vtkellipse.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..52599f38b5e25869fdd2dff1f1405c12257f736e
--- /dev/null
+++ b/Code/src/vtkellipse.cpp
@@ -0,0 +1,214 @@
+#include "vtkellipse.h"
+#include "vtkMath.h"
+#include "vtkPoints.h"
+#include "vtkCellArray.h"
+#include "vtkPolyData.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkProperty.h"
+#include "qdebug.h"
+#include <cmath>
+#include "vtkImageViewer2.h"
+#include "vtkImageData.h"
+#include <vtkWindowToImageFilter.h>
+#include <vtkPolyDataToImageStencil.h>
+#include <vtkImageStencil.h>
+#include <vtkTransform.h>
+#include "vtkTransformPolyDataFilter.h"
+#include "vtkRotationFilter.h"
+
+
+vtkEllipse::vtkEllipse(float SemiMajorAxisLength, float SemiMinorAxisLength, float angle, float c_x, float c_y,float arcpixel,int band,int row,QString  name, VSTableDesktop *table, int numid, int numid_in )
+//vtkEllipse::vtkEllipse(float SemiMajorAxisLength, float SemiMinorAxisLength, float angle, float c_x, float c_y,float arcpixel,int band,int row, VSTable *table )
+{
+
+    idSingleBand=band;
+    rowInTable=row;
+
+    vstable=table;
+    sourceName=name;
+
+    numidtree=numid;
+    numid_intree=numid_in;
+
+    int NumberOfSteps=20;
+
+    center_x=c_x;
+    center_y=c_y;
+
+    //double arcpixel=6;
+
+    double interval = 2 * vtkMath::Pi() / NumberOfSteps;
+    double alpha, sinalpha, cosalpha;
+    double pt[3];
+    pt[2] = 0.0;
+
+    vtkPoints *new_points;
+    new_points = vtkPoints::New();
+    new_points->Allocate(NumberOfSteps);
+    vtkCellArray *new_lines;
+    new_lines = vtkCellArray::New();
+    new_lines->Allocate(NumberOfSteps);
+
+    vtkIdType prev_pt=0, cur_pt=0, pts[2];
+
+
+
+    // calculate points
+    for (int i = 0; i < NumberOfSteps; i++)
+    {
+        /*
+       alpha = i * interval + angle/arcpixel;//+41.7; // current angle
+       sinalpha = sin(alpha);
+       cosalpha = cos(alpha);
+
+
+       pt[0] = SemiMajorAxisLength/arcpixel * cosalpha + c_x;//150;
+       pt[1] = SemiMinorAxisLength/arcpixel * sinalpha + c_y;//1197;
+
+
+       prev_pt = cur_pt;
+       cur_pt = new_points->InsertNextPoint(pt);
+
+
+       if (i > 0)
+       {
+         pts[0] = prev_pt;
+         pts[1] = cur_pt;
+         new_lines->InsertNextCell(2, pts);
+       }
+        */
+
+
+
+        //double alpha2 = ((angle * (vtkMath::Pi() / 180.0) ) / arcpixel);
+
+        alpha = i * interval; // current angle
+
+
+        sinalpha = sin(alpha);
+        cosalpha = cos(alpha);
+
+        //        pt[0] = SemiMajorAxisLength/arcpixel  * cosalpha + c_x;
+        //        pt[1] = SemiMinorAxisLength/arcpixel  * sinalpha + c_y;
+
+        double x = SemiMajorAxisLength/arcpixel  * cosalpha + c_x;
+        double y = SemiMinorAxisLength/arcpixel  * sinalpha +c_y;
+
+        pt[0] =c_x + (x-c_x) * cos (angle) - (y-c_y) * sin (angle);
+        pt[1] =c_y + (y-c_y) * cos (angle) + (x-c_x) * sin (angle);
+
+
+
+        prev_pt = cur_pt;
+        cur_pt = new_points->InsertNextPoint(pt);
+
+        if (i > 0)
+        {
+            pts[0] = prev_pt;
+            pts[1] = cur_pt;
+            new_lines->InsertNextCell(2, pts);
+        }
+
+
+
+    }
+
+    // connect up the last segment
+    pts[0] = pts[1];
+    pts[1] = 0;
+    new_lines->InsertNextCell(2, pts);
+
+
+    output = vtkSmartPointer<vtkPolyData>::New();
+    output->SetPoints(new_points);
+    output->SetLines(new_lines);
+
+    new_points->Delete();
+    new_lines->Delete();
+}
+
+vtkSmartPointer<vtkPolyData> vtkEllipse::getPolyData()
+{
+    return output;
+}
+
+vtkSmartPointer<vtkTransformPolyDataFilter> vtkEllipse::getTransformFilter()
+{
+    return transformFilter;
+}
+
+
+
+VSTableDesktop* vtkEllipse::getTable()
+{
+    return vstable;
+}
+
+
+vtkSmartPointer<vtkImageActor> vtkEllipse::getActor()
+{
+
+    return ellipseActor;
+}
+
+bool vtkEllipse::isInsideRect(vtkRectf *rect)
+{
+
+    float x1 = rect->GetX();
+    float y1 = rect->GetY();
+
+    float x2 = rect->GetX() + rect->GetWidth();
+    float y2 = rect->GetY();
+
+    float x3 = rect->GetX() + rect->GetWidth();
+    float y3 = rect->GetY() + rect->GetHeight();
+
+    float x4 = rect->GetX();
+    float y4 = rect->GetY()+rect->GetHeight();
+
+    //START
+    //1 - center - 4
+    float area_triangolo_1 = fabs (0.5*( x1*(center_y-y4) + center_x*(y4-y1) + x4*(y1-center_y)) );
+    //4 - center - 3
+    float area_triangolo_2 = fabs (0.5*( x4*(center_y-y3) + center_x*(y3-y4) + x3*(y4-center_y)) );
+    //3 - center - 2
+    float area_triangolo_3 = fabs (0.5*( x3*(center_y-y2) + center_x*(y2-y3) + x2*(y3-center_y)) );
+    //center - 2 - 1
+    float area_triangolo_4 = fabs( 0.5*( center_x*(y2-y1) + x2*(y1-center_y) + x1*(center_y-y2)) );
+
+    float area_rettangolo = fabs( rect->GetWidth()*rect->GetHeight());
+
+    float area_sum=area_triangolo_1+area_triangolo_2+area_triangolo_3+area_triangolo_4;
+
+    if (fabs( (area_rettangolo - area_sum)/area_rettangolo ) < 0.1)
+        return true;
+    else
+        return false;
+    //END
+
+}
+
+QString vtkEllipse::getSourceName()
+{
+    return sourceName;
+}
+
+int vtkEllipse::getNumidtree()
+{
+    return numidtree;
+}
+
+int vtkEllipse::getNumid_intree()
+{
+    return numid_intree;
+}
+
+
+vtkEllipse::~vtkEllipse()
+{
+
+    output->Delete();
+
+
+}
+
diff --git a/Code/src/vtkellipse.h b/Code/src/vtkellipse.h
new file mode 100644
index 0000000000000000000000000000000000000000..a7af518ad288af00eeff4e8e4caee5c99d667334
--- /dev/null
+++ b/Code/src/vtkellipse.h
@@ -0,0 +1,48 @@
+#ifndef VTKELLIPSE_H
+#define VTKELLIPSE_H
+
+#include "vtkActor.h"
+#include "vtkSmartPointer.h"
+#include "vtkLODActor.h"
+#include "vtkRect.h"
+#include "qstring.h"
+#include "vstabledesktop.h"
+#include "vtkActor2D.h"
+#include "vtkLODProp3D.h"
+#include "vtkImageActor.h"
+#include "vtkPolyData.h"
+#include "vtkTransformPolyDataFilter.h"
+#include "vtkRotationFilter.h"
+
+class vtkEllipse
+{
+public:
+    //vtkEllipse(float SemiMajorAxisLength,float SemiMinorAxisLength,float angle, float c_x, float c_y, float arcpixel,int band,int row, VSTable *table);
+    vtkEllipse(float SemiMajorAxisLength,float SemiMinorAxisLength,float angle, float c_x, float c_y, float arcpixel,int band,int row,QString  name, VSTableDesktop *table, int numid=0, int numid_in=0);
+    ~vtkEllipse();
+    vtkSmartPointer<vtkImageActor> getActor();
+    bool isInsideRect(vtkRectf *rect);
+    QString getSourceName();
+    VSTableDesktop* getTable();
+    int getRowInTable(){return rowInTable;}
+
+    vtkSmartPointer<vtkPolyData> getPolyData();
+    vtkSmartPointer<vtkTransformPolyDataFilter> getTransformFilter();
+    int getNumidtree();
+    int getNumid_intree();
+
+private:
+    vtkSmartPointer<vtkImageActor> ellipseActor;
+    float center_x;
+    float center_y;
+    int idSingleBand;
+    int rowInTable;
+    QString sourceName;
+    VSTableDesktop *vstable;
+    vtkSmartPointer<vtkPolyData> output;
+    vtkSmartPointer<vtkTransformPolyDataFilter> transformFilter ;
+    int numidtree;
+    int numid_intree;
+};
+
+#endif // VTKELLIPSE_H
diff --git a/Code/src/vtkextracthistogram.cpp b/Code/src/vtkextracthistogram.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eeded1a0e3a9821f463ac669d7f943f816c8fcc4
--- /dev/null
+++ b/Code/src/vtkextracthistogram.cpp
@@ -0,0 +1,474 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkExtractHistogram.cxx
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+#include "vtkExtractHistogram.h"
+
+#include "vtkCellData.h"
+#include "vtkCompositeDataIterator.h"
+#include "vtkCompositeDataSet.h"
+#include "vtkDataSet.h"
+#include "vtkDoubleArray.h"
+#include "vtkGraph.h"
+#include "vtkInformation.h"
+#include "vtkInformationVector.h"
+#include "vtkIntArray.h"
+#include "vtkIOStream.h"
+#include "vtkMath.h"
+#include "vtkObjectFactory.h"
+#include "vtkPointData.h"
+#include "vtkSmartPointer.h"
+#include "vtkStreamingDemandDrivenPipeline.h"
+#include "vtkTable.h"
+
+#include <vector>
+#include <map>
+#include <string>
+
+struct vtkEHInternals
+{
+  vtkEHInternals() : FieldAssociation(-1) {}
+  struct ArrayValuesType
+    {
+    // The total of the values per bin - the second vector
+    // is for arrays with multiple components
+    std::vector<std::vector<double> > TotalValues;
+    };
+  typedef std::map<std::string, ArrayValuesType> ArrayMapType;
+  ArrayMapType ArrayValues;
+  int FieldAssociation;
+};
+
+vtkStandardNewMacro(vtkExtractHistogram);
+//-----------------------------------------------------------------------------
+vtkExtractHistogram::vtkExtractHistogram() :
+  Component(0),
+  BinCount(10)
+{
+  this->SetInputArrayToProcess(
+    0,
+    0,
+    0,
+    vtkDataObject::FIELD_ASSOCIATION_POINTS_THEN_CELLS,
+    vtkDataSetAttributes::SCALARS);
+  this->Internal = new vtkEHInternals;
+  this->CalculateAverages = 0;
+  this->UseCustomBinRanges = false;
+  this->CustomBinRanges[0] = 0;
+  this->CustomBinRanges[1] = 100;
+}
+
+//-----------------------------------------------------------------------------
+vtkExtractHistogram::~vtkExtractHistogram()
+{
+  delete this->Internal;
+}
+
+//-----------------------------------------------------------------------------
+/*
+void vtkExtractHistogram::PrintSelf(ostream& os, vtkIndent indent)
+{
+  this->Superclass::PrintSelf(os,indent);
+
+  os << indent << "Component: " << this->Component << "\n";
+  os << indent << "BinCount: " << this->BinCount << "\n";
+  os << indent << "UseCustomBinRanges: " << this->UseCustomBinRanges << endl;
+  os << indent << "CustomBinRanges: " <<
+    this->CustomBinRanges[0] << ", " << this->CustomBinRanges[1] << endl;
+}
+*/
+//-----------------------------------------------------------------------------
+int vtkExtractHistogram::FillInputPortInformation (int port,
+                                                   vtkInformation *info)
+{
+  this->Superclass::FillInputPortInformation(port, info);
+
+  info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataObject");
+  return 1;
+}
+
+//-----------------------------------------------------------------------------
+int vtkExtractHistogram::GetInputFieldAssociation()
+{
+  vtkInformationVector *inArrayVec =
+    this->Information->Get(INPUT_ARRAYS_TO_PROCESS());
+  vtkInformation *inArrayInfo = inArrayVec->GetInformationObject(0);
+  return inArrayInfo->Get(vtkDataObject::FIELD_ASSOCIATION());
+}
+
+//-----------------------------------------------------------------------------
+vtkFieldData* vtkExtractHistogram::GetInputFieldData(vtkDataObject* input)
+{
+  if (this->Internal->FieldAssociation < 0)
+    {
+    this->Internal->FieldAssociation = this->GetInputFieldAssociation();
+    }
+
+  switch (this->Internal->FieldAssociation)
+    {
+    case vtkDataObject::FIELD_ASSOCIATION_POINTS:
+    case vtkDataObject::FIELD_ASSOCIATION_POINTS_THEN_CELLS:
+      return vtkDataSet::SafeDownCast(input)->GetPointData();
+      break;
+    case vtkDataObject::FIELD_ASSOCIATION_CELLS:
+      return vtkDataSet::SafeDownCast(input)->GetCellData();
+      break;
+    case vtkDataObject::FIELD_ASSOCIATION_NONE:
+      return input->GetFieldData();
+      break;
+    case vtkDataObject::FIELD_ASSOCIATION_VERTICES:
+      return vtkGraph::SafeDownCast(input)->GetVertexData();
+      break;
+    case vtkDataObject::FIELD_ASSOCIATION_EDGES:
+      return vtkGraph::SafeDownCast(input)->GetEdgeData();
+      break;
+    case vtkDataObject::FIELD_ASSOCIATION_ROWS:
+      return vtkTable::SafeDownCast(input)->GetRowData();
+      break;
+    }
+  return 0;
+}
+
+//-----------------------------------------------------------------------------
+bool vtkExtractHistogram::GetInputArrayRange(
+  vtkInformationVector** inputVector, double range[2])
+{
+  range[0] = VTK_DOUBLE_MAX;
+  range[1] = VTK_DOUBLE_MIN;
+
+  //obtain a pointer to the name of the vtkDataArray to bin up
+  //and find the range of the data values within it
+  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
+  vtkDataObject *input = inInfo->Get(vtkDataObject::DATA_OBJECT());
+  vtkCompositeDataSet *cdin = vtkCompositeDataSet::SafeDownCast(input);
+  if (cdin)
+    {
+    //for composite datasets, visit each leaf data set and compute the total
+    //range
+    vtkCompositeDataIterator *cdit = cdin->NewIterator();
+    cdit->InitTraversal();
+    bool foundone = false;
+    while(!cdit->IsDoneWithTraversal())
+      {
+      vtkDataObject *dObj = cdit->GetCurrentDataObject();
+      vtkDataArray* data_array = this->GetInputArrayToProcess(0, dObj);
+      if (data_array &&
+        this->Component >= 0 &&
+        this->Component < data_array->GetNumberOfComponents())
+        {
+        foundone = true;
+        double tRange[2];
+        data_array->GetRange(tRange, this->Component);
+        range[0] = (tRange[0] < range[0])? tRange[0] : range[0];
+        range[1] = (tRange[1] > range[1])? tRange[1] : range[1];
+        }
+      cdit->GoToNextItem();
+      }
+    cdit->Delete();
+
+    if (!foundone)
+      {
+      return false;
+      }
+    }
+  else
+    {
+    vtkDataArray* data_array = this->GetInputArrayToProcess(0, inputVector);
+    if (!data_array)
+      {
+      return false;
+      }
+    // If the requested component is out-of-range for the input, we return an
+    // empty dataset
+    if(this->Component < 0 &&
+      this->Component >= data_array->GetNumberOfComponents())
+      {
+      vtkWarningMacro("Requested component "
+        <<  this->Component << " is not available.");
+      return false;
+      }
+    data_array->GetRange(range, this->Component);
+    }
+
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+bool vtkExtractHistogram::InitializeBinExtents(
+  vtkInformationVector** inputVector,
+  vtkDoubleArray* bin_extents,
+  double& min, double& max)
+{
+  double range[2];
+  range[0] = VTK_DOUBLE_MAX;
+  range[1] = -VTK_DOUBLE_MAX;
+
+  // Keeping the column name constant causes less issues in the GUI.
+  bin_extents->SetName("bin_extents");
+
+  if (this->UseCustomBinRanges)
+    {
+    range[0] = this->CustomBinRanges[0];
+    range[1] = this->CustomBinRanges[1];
+    }
+  else if (!this->GetInputArrayRange(inputVector, range) ||
+    (range[0] > range[1]))
+    {
+    // We don't flag this as error since the array may just be missing for
+    // current time-step (BUG #12290).
+    vtkDebugMacro("Could not determine array range. "
+      "The chosen array or component may not be available or "
+      "has invalid range");
+    return false;
+    }
+
+  // Calculate the extents of each bin, based on the range of values in the
+  // input ...
+  if (range[0] == range[1])
+    {
+    // Give it some width.
+    range[1] = range[0]+1;
+    }
+
+  min = range[0];
+  max = range[1];
+  this->FillBinExtents(bin_extents, min, max);
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+void vtkExtractHistogram::FillBinExtents(vtkDoubleArray* bin_extents,
+  double min, double max)
+{
+  // Calculate the extents of each bin, based on the range of values in the
+  // input ...
+  if (min == max)
+    {
+    // Give it some width.
+    max = min + 1;
+    }
+
+  bin_extents->SetNumberOfComponents(1);
+  bin_extents->SetNumberOfTuples(this->BinCount);
+  double bin_delta = (max - min) / this->BinCount;
+  double half_delta = bin_delta/2.0;
+  for(int i = 0; i < this->BinCount; ++i)
+    {
+    bin_extents->SetValue(i, min + (i * bin_delta) + half_delta);
+    }
+}
+
+
+inline int vtkExtractHistogramClamp(int value, int min, int max)
+{
+  value = value < min ? min : value;
+  value = value > max ? max : value;
+  return value;
+}
+
+//-----------------------------------------------------------------------------
+void vtkExtractHistogram::BinAnArray(vtkDataArray *data_array,
+                                     vtkIntArray *bin_values,
+                                     double min, double max,
+                                     vtkFieldData* field)
+{
+  // If the requested component is out-of-range for the input,
+  // the bin_values will be 0, so no need to do any actual counting.
+  if(data_array == NULL ||
+    this->Component < 0 ||
+    this->Component >= data_array->GetNumberOfComponents())
+    {
+    return;
+    }
+
+  int num_of_tuples = data_array->GetNumberOfTuples();
+  double bin_delta = (max-min)/this->BinCount;
+  for(int i = 0; i != num_of_tuples; ++i)
+    {
+    if (i%1000 == 0)
+      {
+      this->UpdateProgress(0.10 + 0.90*i/num_of_tuples);
+      }
+    const double value = data_array->GetComponent(i, this->Component);
+    int index = static_cast<int>((value - min) / bin_delta);
+    // If the value is equal to max, include it in the last bin.
+    index = ::vtkExtractHistogramClamp(index, 0, this->BinCount-1);
+    bin_values->SetValue(index, bin_values->GetValue(index)+1);
+
+    if (this->CalculateAverages)
+      {
+      // Get all other arrays, add their value to the bin
+      // For each bin, we will need 2 values per array ->
+      // total, num. elements
+      // at the end, divide each total by num. elements
+      int num_arrays = field->GetNumberOfArrays();
+      for (int idx=0; idx<num_arrays; idx++)
+        {
+        vtkDataArray* array = field->GetArray(idx);
+        if (array && array != data_array && array->GetName())
+          {
+          vtkEHInternals::ArrayValuesType& arrayValues =
+            this->Internal->ArrayValues[array->GetName()];
+          arrayValues.TotalValues.resize(this->BinCount);
+          int numComps = array->GetNumberOfComponents();
+          arrayValues.TotalValues[index].resize(numComps);
+          for (int comp=0; comp<numComps; comp++)
+            {
+            arrayValues.TotalValues[index][comp] +=
+              array->GetComponent(i, comp);
+            }
+          }
+        }
+      }
+    }
+}
+
+//-----------------------------------------------------------------------------
+int vtkExtractHistogram::RequestData(vtkInformation* /*request*/,
+                                     vtkInformationVector** inputVector,
+                                     vtkInformationVector* outputVector)
+{
+  // Build an empty output grid in advance, so we can bail-out if we
+  // encounter any problems
+  vtkTable* const output_data = vtkTable::GetData(outputVector, 0);
+  output_data->Initialize();
+
+  if (this->UseCustomBinRanges &&
+      this->CustomBinRanges[1] < this->CustomBinRanges[0])
+    {
+    double min = this->CustomBinRanges[1];
+    double max = this->CustomBinRanges[0];
+    this->CustomBinRanges[0] = min;
+    this->CustomBinRanges[1] = max;
+    vtkWarningMacro("Custom bin range adjusted to keep min <= max value");
+    }
+
+  // These are the mid-points for each of the bins
+  vtkSmartPointer<vtkDoubleArray> bin_extents =
+    vtkSmartPointer<vtkDoubleArray>::New();
+  bin_extents->SetNumberOfComponents(1);
+  bin_extents->SetNumberOfTuples(this->BinCount);
+  bin_extents->SetName("bin_extents");
+  bin_extents->FillComponent(0, 0.0);
+
+  // Insert values into bins ...
+  vtkSmartPointer<vtkIntArray> bin_values =
+    vtkSmartPointer<vtkIntArray>::New();
+  bin_values->SetNumberOfComponents(1);
+  bin_values->SetNumberOfTuples(this->BinCount);
+  bin_values->SetName("bin_values");
+  bin_values->FillComponent(0, 0.0);
+
+  // Initializes the bin_extents array.
+  double min, max;
+  if (!this->InitializeBinExtents(inputVector, bin_extents, min, max))
+    {
+    this->Internal->ArrayValues.clear();
+    return 1;
+    }
+
+  output_data->GetRowData()->AddArray(bin_extents);
+  output_data->GetRowData()->AddArray(bin_values);
+
+  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
+  vtkDataObject *input = inInfo->Get(vtkDataObject::DATA_OBJECT());
+  vtkCompositeDataSet *cdin = vtkCompositeDataSet::SafeDownCast(input);
+  if (cdin)
+    {
+    //for composite datasets visit each leaf dataset and add in its counts
+    vtkCompositeDataIterator *cdit = cdin->NewIterator();
+    cdit->InitTraversal();
+    while(!cdit->IsDoneWithTraversal())
+      {
+      vtkDataObject *dObj = cdit->GetCurrentDataObject();
+      vtkDataArray* data_array = this->GetInputArrayToProcess(0, dObj);
+      this->BinAnArray(data_array, bin_values, min, max,
+        this->GetInputFieldData(dObj));
+      cdit->GoToNextItem();
+      }
+    cdit->Delete();
+    }
+  else
+    {
+    vtkDataArray* data_array = this->GetInputArrayToProcess(0, inputVector);
+    this->BinAnArray(data_array, bin_values, min, max,
+      this->GetInputFieldData(input));
+    }
+
+  if (this->CalculateAverages)
+    {
+    vtkEHInternals::ArrayMapType::iterator iter =
+      this->Internal->ArrayValues.begin();
+    for(; iter != this->Internal->ArrayValues.end(); iter++)
+      {
+      vtkSmartPointer<vtkDoubleArray> da =
+        vtkSmartPointer<vtkDoubleArray>::New();
+      std::string newname = iter->first + "_total";
+      da->SetName(newname.c_str());
+      vtkSmartPointer<vtkDoubleArray> aa =
+        vtkSmartPointer<vtkDoubleArray>::New();
+      std::string newname2 = iter->first + "_average";
+      aa->SetName(newname2.c_str());
+      int numComps = static_cast<int>(iter->second.TotalValues[0].size());
+      da->SetNumberOfComponents(numComps);
+      da->SetNumberOfTuples(this->BinCount);
+      aa->SetNumberOfComponents(numComps);
+      aa->SetNumberOfTuples(this->BinCount);
+      for (vtkIdType i=0; i<this->BinCount; i++)
+        {
+        for (int j=0; j<numComps; j++)
+          {
+          if (iter->second.TotalValues[i].size() == (unsigned int)numComps)
+            {
+            da->SetValue(i*numComps+j, iter->second.TotalValues[i][j]);
+            if (bin_values->GetValue(i))
+              {
+              aa->SetValue(i*numComps+j,
+                iter->second.TotalValues[i][j]/bin_values->GetValue(i));
+              }
+            else
+              {
+              aa->SetValue(i*numComps+j, 0);
+              }
+            }
+          else
+            {
+            da->SetValue(i*numComps+j, 0);
+            aa->SetValue(i*numComps+j, 0);
+            }
+          }
+        }
+      output_data->GetRowData()->AddArray(da);
+      output_data->GetRowData()->AddArray(aa);
+      }
+
+    this->Internal->ArrayValues.clear();
+    }
+
+  return 1;
+}
+void vtkExtractHistogram::PrintSelf(ostream& os, vtkIndent indent)
+{
+    // this->Superclass::PrintSelf(os, indent);
+}
+
+void vtkExtractHistogram::PrintHeader(ostream& os, vtkIndent indent)
+{
+    // this->Superclass::PrintHeader(os, indent);
+
+}
+
+void vtkExtractHistogram::PrintTrailer(std::ostream& os , vtkIndent indent)
+{
+    // this->Superclass::PrintTrailer(os, indent);
+}
diff --git a/Code/src/vtkextracthistogram.h b/Code/src/vtkextracthistogram.h
new file mode 100644
index 0000000000000000000000000000000000000000..131d07676f29f009a3bd9857e18562c17d228ce4
--- /dev/null
+++ b/Code/src/vtkextracthistogram.h
@@ -0,0 +1,127 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkExtractHistogram.h
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+
+#ifndef vtkExtractHistogram_h
+#define vtkExtractHistogram_h
+
+//#include "vtkPVVTKExtensionsDefaultModule.h" //needed for exports
+#include "vtkTableAlgorithm.h"
+
+//BTX
+class vtkDoubleArray;
+class vtkFieldData;
+class vtkIntArray;
+struct vtkEHInternals;
+//ETX
+
+// .NAME vtkExtractHistogram - Extract histogram data (binned values) from any
+// dataset
+// .SECTION Description
+// vtkExtractHistogram accepts any vtkDataSet as input and produces a
+// vtkPolyData containing histogram data as output.  The output vtkPolyData
+// will have contain a vtkDoubleArray named "bin_extents" which contains
+// the boundaries between each histogram bin, and a vtkUnsignedLongArray
+// named "bin_values" which will contain the value for each bin.
+
+class  vtkExtractHistogram : public vtkTableAlgorithm
+{
+public:
+  static vtkExtractHistogram* New();
+  vtkTypeMacro(vtkExtractHistogram, vtkTableAlgorithm);
+  void PrintHeader(ostream& os, vtkIndent indent);
+  void PrintTrailer(std::ostream& os , vtkIndent indent);
+  void PrintSelf(ostream& os, vtkIndent indent);
+  // Description:
+  // Controls which input data component should be binned, for input arrays
+  // with more-than-one component
+  vtkSetClampMacro(Component, int, 0, VTK_INT_MAX);
+  vtkGetMacro(Component, int);
+
+  // Description:
+  // Controls the number of bins N in the output histogram data
+  vtkSetClampMacro(BinCount, int, 1, VTK_INT_MAX);
+  vtkGetMacro(BinCount, int);
+
+  // Description:
+  // Get/Set custom bin ranges to use. These are used only when
+  // UseCustomBinRanges is set to true.
+  vtkSetVector2Macro(CustomBinRanges, double);
+  vtkGetVector2Macro(CustomBinRanges, double);
+
+  // Description:
+  // When set to true, CustomBinRanges will  be used instead of using the full
+  // range for the selected array. By default, set to false.
+  vtkSetMacro(UseCustomBinRanges, bool);
+  vtkGetMacro(UseCustomBinRanges, bool);
+  vtkBooleanMacro(UseCustomBinRanges, bool);
+
+  // Description:
+  // This option controls whether the algorithm calculates averages
+  // of variables other than the primary variable that fall into each
+  // bin. False by default.
+  vtkSetMacro(CalculateAverages, int);
+  vtkGetMacro(CalculateAverages, int);
+  vtkBooleanMacro(CalculateAverages, int);
+
+protected:
+  vtkExtractHistogram();
+  ~vtkExtractHistogram();
+
+  // Description:
+  // Returns the data range for the input array to process.
+  // This method is not called with this->UseCustomBinRanges is true.
+  // Returns true is range could be determined correctly, otherwise returns
+  // false and range is set to {VTK_DOUBLE_MAX, VTK_DOUBLE_MIN}. When returning
+  // true, the actual data range is returned (without any extra padding).
+  virtual bool GetInputArrayRange(vtkInformationVector** inputVector, double range[2]);
+
+  virtual int FillInputPortInformation (int port, vtkInformation *info);
+
+  virtual int RequestData(vtkInformation *request,
+                          vtkInformationVector **inputVector,
+                          vtkInformationVector *outputVector);
+
+  // Initialize the bin_extents using the data range for the selected
+  // array.
+  virtual bool InitializeBinExtents(
+    vtkInformationVector** inputVector,
+    vtkDoubleArray* bin_extents,
+    double& min, double& max);
+
+  void BinAnArray(
+    vtkDataArray *src,
+    vtkIntArray *vals,
+    double min, double max,
+    vtkFieldData* field);
+
+  void FillBinExtents(vtkDoubleArray* bin_extents, double min, double max);
+
+  double CustomBinRanges[2];
+  bool UseCustomBinRanges;
+  int Component;
+  int BinCount;
+  int CalculateAverages;
+
+  vtkEHInternals* Internal;
+
+private:
+  void operator=(const vtkExtractHistogram&); // Not implemented
+  vtkExtractHistogram(const vtkExtractHistogram&); // Not implemented
+
+  int GetInputFieldAssociation();
+  vtkFieldData* GetInputFieldData(vtkDataObject* input);
+};
+
+#endif
diff --git a/Code/src/vtkfitsreader.cpp b/Code/src/vtkfitsreader.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b6610d12280bcb3e10b80b33741ae079ccb01a7c
--- /dev/null
+++ b/Code/src/vtkfitsreader.cpp
@@ -0,0 +1,895 @@
+#include "vtkfitsreader.h"
+
+#include "vtkCommand.h"
+#include "vtkInformation.h"
+#include "vtkInformationVector.h"
+#include "vtkObjectFactory.h"
+#include "vtkStreamingDemandDrivenPipeline.h"
+#include "vtkFloatArray.h"
+#include <cmath>
+#include "vtkPointData.h"
+#include "qdebug.h"
+#include <qmath.h>
+
+
+vtkCxxRevisionMacro(vtkFitsReader, "$Revision: 1.1 $");
+vtkStandardNewMacro(vtkFitsReader);
+
+//----------------------------------------------------------------------------
+vtkFitsReader::vtkFitsReader()
+{
+    this->filename[0]='\0';
+    this->xStr[0]='\0';
+    this->yStr[0]='\0';
+    this->zStr[0]='\0';
+    this->title[0]='\0';
+    this->SetNumberOfInputPorts( 0 );
+    this->SetNumberOfOutputPorts( 1 );
+    //this->is3D=is3D;
+
+    for (int i=0; i<3; i++)
+    {
+        crval[i]=0;
+        cpix[i]=0;
+        cdelt[i]=0;
+        naxes[i]= 10;
+    }
+    
+    this->is3D=false;
+
+    qDebug()<<"New.vtkFitsReader";
+
+}
+
+//----------------------------------------------------------------------------
+vtkFitsReader::~vtkFitsReader()
+{
+}
+
+void vtkFitsReader::SetFileName(std::string name) {
+
+
+    if (name.empty()) {
+        vtkErrorMacro(<<"Null Datafile!");
+        return;
+    }
+
+
+    filename= name;
+    this->Modified();
+
+    qDebug()<<"SetFileName.vtkFitsReader";
+
+}
+//----------------------------------------------------------------------------
+void vtkFitsReader::PrintSelf(ostream& os, vtkIndent indent)
+{
+    // this->Superclass::PrintSelf(os, indent);
+}
+
+void vtkFitsReader::PrintHeader(ostream& os, vtkIndent indent)
+{
+    // this->Superclass::PrintHeader(os, indent);
+
+}
+
+void vtkFitsReader::PrintTrailer(std::ostream& os , vtkIndent indent)
+{
+    // this->Superclass::PrintTrailer(os, indent);
+}
+
+//----------------------------------------------------------------------------
+vtkStructuredPoints* vtkFitsReader::GetOutput()
+{
+    return this->GetOutput(0);
+}
+
+//----------------------------------------------------------------------------
+vtkStructuredPoints* vtkFitsReader::GetOutput(int port)
+{
+    return vtkStructuredPoints::SafeDownCast(this->GetOutputDataObject(port));
+}
+
+//----------------------------------------------------------------------------
+void vtkFitsReader::SetOutput(vtkDataObject* d)
+{
+    this->GetExecutive()->SetOutputData(0, d);
+}
+
+
+//----------------------------------------------------------------------------
+int vtkFitsReader::ProcessRequest(vtkInformation* request,
+                                  vtkInformationVector** inputVector,
+                                  vtkInformationVector* outputVector)
+{
+    // Create an output object of the correct type.
+    if(request->Has(vtkDemandDrivenPipeline::REQUEST_DATA_OBJECT()))
+    {
+        return this->RequestDataObject(request, inputVector, outputVector);
+    }
+    // generate the data
+    if(request->Has(vtkDemandDrivenPipeline::REQUEST_DATA()))
+    {
+        return this->RequestData(request, inputVector, outputVector);
+    }
+
+    if(request->Has(vtkStreamingDemandDrivenPipeline::REQUEST_UPDATE_EXTENT()))
+    {
+        return this->RequestUpdateExtent(request, inputVector, outputVector);
+    }
+
+    // execute information
+    if(request->Has(vtkDemandDrivenPipeline::REQUEST_INFORMATION()))
+    {
+        return this->RequestInformation(request, inputVector, outputVector);
+    }
+
+    return this->Superclass::ProcessRequest(request, inputVector, outputVector);
+}
+
+//----------------------------------------------------------------------------
+int vtkFitsReader::FillOutputPortInformation(
+        int vtkNotUsed(port), vtkInformation* info)
+{
+    // now add our info
+    info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkStructuredPoints");
+    return 1;
+}
+
+
+//----------------------------------------------------------------------------
+int vtkFitsReader::RequestDataObject(
+        vtkInformation* vtkNotUsed(request),
+        vtkInformationVector** vtkNotUsed(inputVector),
+        vtkInformationVector* outputVector )
+{
+
+    qDebug()<<"RequestDataObject.vtkFitsReader";
+
+    for ( int i = 0; i < this->GetNumberOfOutputPorts(); ++i )
+    {
+        
+        ReadHeader();
+        fitsfile *fptr;
+        int status = 0, nfound = 0, anynull = 0;
+        long  fpixel, nbuffer, npixels, ii;
+        const int buffsize = 1000;
+
+        float nullval, buffer[buffsize];
+        vtkFloatArray *scalars = vtkFloatArray::New();
+
+        qDebug()<<"for.RequestDataObject.vtkFitsReader";
+        vtkInformation* outInfo = outputVector->GetInformationObject( i );
+        vtkStructuredPoints* output = vtkStructuredPoints::SafeDownCast(
+                    outInfo->Get( vtkDataObject::DATA_OBJECT() ) );
+        if ( ! output )
+        {
+            qDebug()<<"START-!output.for.RequestDataObject.vtkFitsReader";
+            output = vtkStructuredPoints::New();
+            outInfo->Set( vtkDataObject::DATA_OBJECT(), output );
+            output->FastDelete();
+
+            //FITS READER CORE
+
+            char *fn=new char[filename.length() + 1];;
+            strcpy(fn, filename.c_str());
+
+            if ( fits_open_file(&fptr, fn, READONLY, &status) )
+                printerror( status );
+
+            delete []fn;
+
+            qDebug()<<"*******----------------- is3D "<<is3D;
+
+            if(! (this->is3D) )
+            {
+                qDebug()<<"!is3D.RequestDataObject.vtkFitsReader";
+
+
+                /* read the NAXIS1 and NAXIS2 keyword to get image size */
+                // if ( fits_read_keys_lng(fptr, "NAXIS", 1, 3, naxes, &nfound, &status) )
+                if ( fits_read_keys_lng(fptr, "NAXIS", 1, 2, naxes, &nfound, &status) )
+                    printerror( status );
+
+                npixels  = naxes[0] * naxes[1]; /* num of pixels in the image */
+                fpixel   = 1;
+                nullval  = 0;                /* don't check for null values in the image */
+                datamin  = 1.0E30;
+                datamax  = -1.0E30;
+
+                output->SetDimensions(naxes[0], naxes[1],1);
+
+                // output->SetOrigin(0.0, 0.0, 0.0);
+                output->SetOrigin(1.0, 1.0, 0.0);
+
+                scalars->Allocate(npixels);
+
+                while (npixels > 0) {
+
+                    nbuffer = npixels;
+                    if (npixels > buffsize)
+                        nbuffer = buffsize;
+
+                    if ( fits_read_img(fptr, TFLOAT, fpixel, nbuffer, &nullval,
+                                       buffer, &anynull, &status) )
+                        printerror( status );
+
+                    for (ii = 0; ii < nbuffer; ii++)
+                    {
+
+                        // if (isnanf(buffer[ii])) buffer[ii] = -1000000.0; // hack for now
+                        if (std::isnan(buffer[ii])) buffer[ii] = -1000000.0; // hack for now
+
+                        scalars->InsertNextValue(buffer[ii]);
+
+
+                        if ( buffer[ii] < datamin  &&  buffer[ii]!=-1000000.0)
+                            datamin = buffer[ii];
+                        if ( buffer[ii] > datamax  &&  buffer[ii]!=-1000000.0 )
+                            datamax = buffer[ii];
+                    }
+
+                    npixels -= nbuffer;    /* increment remaining number of pixels */
+                    fpixel  += nbuffer;    /* next pixel to be read in image */
+                }
+            }
+            else
+            {
+                this->CalculateRMS();
+                qDebug()<<"Dopo calculate RMS: "<< this->GetRMS();
+                qDebug()<<"datamin: "<<datamin;
+
+                if ( fits_read_keys_lng(fptr, "NAXIS", 1, 3, naxes, &nfound, &status) )
+                    printerror( status );
+
+
+
+                npixels  = naxes[0] * naxes[1] * naxes[2];
+                npix=npixels;
+                fpixel   = 1;
+                nullval  = 0;
+                //datamin  = 1.0E30;
+                //datamax  = -1.0E30;
+
+                QString xtrim(this->xStr);
+                QString ytrim(this->yStr);
+                bool swapped=false;
+                if (xtrim.split("-")[0].toLower().compare("glat") ==0 && ytrim.split("-")[0].toLower().compare("glon") ==0 )
+                {
+                    qDebug()<<"INSIDE: "<<xtrim.split("-")[0]<<" - "<<ytrim.split("-")[0];
+
+                    output->SetDimensions(naxes[1], naxes[0], naxes[2]);
+                   // swapped=true;
+                }
+                else
+                    output->SetDimensions(naxes[0], naxes[1], naxes[2]);
+                //                output->SetOrigin(0.0, 0.0, 0.0);
+                output->SetOrigin(1.0, 1.0, 1.0);
+
+
+                //vtkFloatScalars *scalars = new vtkFloatScalars(npixels);
+                //vtkFloatScalars *scalars = vtkFloatScalars::New();
+
+                //vtkFloatArray *scalars = vtkFloatArray::New();
+                fitsScalars= vtkFloatArray::New();
+                fitsScalars->Allocate(npixels);
+                scalars->Allocate(npixels);
+
+                if (swapped)
+                {
+
+                    npixels=naxes[1];
+                    fpixel=naxes[0]+1;
+
+                    qDebug()<<"npixels "<<npixels<<" pixel "<<fpixel;
+                    //For every pixel
+                    while (npixels > 0) {
+
+                        nbuffer = npixels;
+                        if (npixels > buffsize)
+                            nbuffer = buffsize;
+
+
+                        if ( fits_read_img(fptr, TFLOAT, fpixel, nbuffer, &nullval,
+                                           buffer, &anynull, &status) )
+                            printerror( status );
+                        for (ii = 0; ii < nbuffer; ii++)
+                        {
+
+                            if (std::isnan(buffer[ii]))
+                            {
+
+
+                                buffer[ii] = -1000000.0; // hack for now
+                            }
+                            //conversion
+                            //CVAL3 + (X - CPIX3)*CDEL3
+
+                            buffer[ii]=crval[2]/1000+(buffer[ii]-cpix[2])*cdelt[2]/1000;
+
+
+
+                            scalars->InsertNextValue(buffer[ii]);
+                            fitsScalars->InsertNextValue(buffer[ii]);
+
+                        }
+
+                        npixels -= nbuffer;
+                        fpixel  += nbuffer;
+                    }
+
+
+                    npixels=naxes[0];
+                    fpixel=1;
+                    qDebug()<<"npixels "<<npixels<<" pixel "<<fpixel;
+
+                    //For every pixel
+                    while (npixels > 0) {
+
+                        nbuffer = npixels;
+                        if (npixels > buffsize)
+                            nbuffer = buffsize;
+
+
+                        if ( fits_read_img(fptr, TFLOAT, fpixel, nbuffer, &nullval,
+                                           buffer, &anynull, &status) )
+                            printerror( status );
+                        for (ii = 0; ii < nbuffer; ii++)
+                        {
+
+                            if (std::isnan(buffer[ii]))
+                            {
+
+
+                                buffer[ii] = -1000000.0; // hack for now
+                            }
+                            //conversion
+                            //CVAL3 + (X - CPIX3)*CDEL3
+
+                            buffer[ii]=crval[2]/1000+(buffer[ii]-cpix[2])*cdelt[2]/1000;
+
+
+
+                            scalars->InsertNextValue(buffer[ii]);
+                            fitsScalars->InsertNextValue(buffer[ii]);
+
+                        }
+
+                        npixels -= nbuffer;
+                        fpixel  += nbuffer;
+                    }
+
+
+                    npixels=naxes[2];
+                    fpixel=naxes[0]+naxes[1]+1;
+                    qDebug()<<"npixels "<<npixels<<" pixel "<<fpixel;
+
+                    //For every pixel
+                    while (npixels > 0) {
+
+                        nbuffer = npixels;
+                        if (npixels > buffsize)
+                            nbuffer = buffsize;
+
+
+                        if ( fits_read_img(fptr, TFLOAT, fpixel, nbuffer, &nullval,
+                                           buffer, &anynull, &status) )
+                            printerror( status );
+                        for (ii = 0; ii < nbuffer; ii++)
+                        {
+
+                            if (std::isnan(buffer[ii]))
+                            {
+
+
+                                buffer[ii] = -1000000.0; // hack for now
+                            }
+                            //conversion
+                            //CVAL3 + (X - CPIX3)*CDEL3
+
+                            buffer[ii]=crval[2]/1000+(buffer[ii]-cpix[2])*cdelt[2]/1000;
+
+
+
+                            scalars->InsertNextValue(buffer[ii]);
+                            fitsScalars->InsertNextValue(buffer[ii]);
+
+                        }
+
+                        npixels -= nbuffer;
+                        fpixel  += nbuffer;
+                    }
+
+
+                }
+                else
+                {
+                    //For every pixel
+                    while (npixels > 0) {
+
+                        nbuffer = npixels;
+                        if (npixels > buffsize)
+                            nbuffer = buffsize;
+
+
+                        if ( fits_read_img(fptr, TFLOAT, fpixel, nbuffer, &nullval,
+                                           buffer, &anynull, &status) )
+                            printerror( status );
+                        float tmp;
+                        int index;
+                        for (ii = 0; ii < nbuffer; ii++)
+                        {
+
+                            //if (isnanf(buffer[ii])) buffer[ii] = -1000000.0; // hack for now
+
+
+                            if (std::isnan(buffer[ii]))
+                            {
+
+
+                                buffer[ii] = -1000000.0; // hack for now
+                            }
+                            //conversion
+                            //CVAL3 + (X - CPIX3)*CDEL3
+
+                            buffer[ii]=crval[2]/1000+(buffer[ii]-cpix[2])*cdelt[2]/1000;
+
+
+
+                            scalars->InsertNextValue(buffer[ii]);
+                            fitsScalars->InsertNextValue(buffer[ii]);
+
+                            //  qDebug()<<"buffer["<<ii<<"]"<<buffer[ii];
+                            //  if ( buffer[ii] < datamin )
+                            //  datamin = buffer[ii];
+                            //  if ( buffer[ii] > datamax )
+                            //  datamax = buffer[ii];
+                        }
+
+                        npixels -= nbuffer;
+                        fpixel  += nbuffer;
+                    }
+                } //end else
+            }
+            
+        }
+
+
+        // cerr << "min: " << datamin << " max: " << datamax << endl;
+
+        if ( fits_close_file(fptr, &status) )
+            printerror( status );
+
+        output->GetPointData()->SetScalars(scalars);
+
+        //END FITS READ CORE
+        this->GetOutputPortInformation( i )->Set(vtkDataObject::DATA_EXTENT_TYPE(), output->GetExtentType() );
+
+        qDebug()<<"END-!output.for.RequestDataObject.vtkFitsReader";
+
+    }
+
+    return 1;
+}
+
+//----------------------------------------------------------------------------
+int vtkFitsReader::RequestInformation(
+        vtkInformation* vtkNotUsed(request),
+        vtkInformationVector** vtkNotUsed(inputVector),
+        vtkInformationVector* vtkNotUsed(outputVector))
+{
+    // do nothing let subclasses handle it
+    return 1;
+}
+
+//----------------------------------------------------------------------------
+int vtkFitsReader::RequestUpdateExtent(
+        vtkInformation* vtkNotUsed(request),
+        vtkInformationVector** inputVector,
+        vtkInformationVector* vtkNotUsed(outputVector))
+{
+    qDebug()<<" \t \t **RequestUpdateExtent.vtkFitsReader";
+    int numInputPorts = this->GetNumberOfInputPorts();
+    for (int i=0; i<numInputPorts; i++)
+    {
+        int numInputConnections = this->GetNumberOfInputConnections(i);
+        for (int j=0; j<numInputConnections; j++)
+        {
+            vtkInformation* inputInfo = inputVector[i]->GetInformationObject(j);
+            inputInfo->Set(vtkStreamingDemandDrivenPipeline::EXACT_EXTENT(), 1);
+        }
+    }
+    return 1;
+}
+
+//----------------------------------------------------------------------------
+// This is the superclasses style of Execute method.  Convert it into
+// an imaging style Execute method.
+int vtkFitsReader::RequestData(
+        vtkInformation* vtkNotUsed(request),
+        vtkInformationVector** vtkNotUsed( inputVector ),
+        vtkInformationVector* vtkNotUsed(outputVector) )
+{
+    qDebug()<<"\t\t *** RequestData.vtkFitsReader";
+
+    // do nothing let subclasses handle it
+    return 1;
+}
+
+void vtkFitsReader::ReadHeader() {
+
+
+
+    fitsfile *fptr;       /* pointer to the FITS file, defined in fitsio.h */
+
+    int status, nkeys, keypos, hdutype, ii, jj;
+    char card[FLEN_CARD];   /* standard string lengths defined in fitsioc.h */
+    
+    
+    char crval1[80];
+    char crval2[80];
+    char crval3[80];
+    char crpix1[80];
+    char crpix2[80];
+    char crpix3[80];
+    char cdelt1[80];
+    char cdelt2[80];
+    char cdelt3[80];
+    char naxis1[80];
+    char naxis2[80];
+    char naxis3[80];
+    
+    
+    crval1[0] ='\0';
+    crval2[0] ='\0';
+    crval3[0] ='\0';
+    crpix1[0] ='\0';
+    crpix2[0] ='\0';
+    crpix3[0] ='\0';
+    cdelt1[0] ='\0';
+    cdelt2[0] ='\0';
+    cdelt3[0] ='\0';
+    
+    QString val1, val2, val3, pix1,pix2, pix3, delt1, delt2, delt3, nax1, nax2, nax3;
+
+    status = 0;
+
+
+    char *fn=new char[filename.length() + 1];;
+    strcpy(fn, filename.c_str());
+
+    if ( fits_open_file(&fptr, fn, READONLY, &status) )
+        printerror( status );
+    delete []fn;
+
+    /* attempt to move to next HDU, until we get an EOF error */
+    for (ii = 1; !(fits_movabs_hdu(fptr, ii, &hdutype, &status) ); ii++)
+    {
+
+        /* get no. of keywords */
+        if (fits_get_hdrpos(fptr, &nkeys, &keypos, &status) )
+            printerror( status );
+
+        for (jj = 1; jj <= nkeys; jj++)  {
+
+            if ( fits_read_record(fptr, jj, card, &status) )
+                printerror( status );
+
+            if (!strncmp(card, "CTYPE", 5)) {
+                // cerr << card << endl;
+                char *first = strchr(card, '\'');
+                char *last = strrchr(card, '\'');
+
+                *last = '\0';
+                if (card[5] == '1')
+                    strcpy(xStr, first+1);
+                if (card[5] == '2')
+                    strcpy(yStr, first+1);
+                if (card[5] == '3')
+                    strcpy(zStr, first+1);
+
+                qDebug()<<"xStr: "<<xStr;
+                qDebug()<<"yStr: "<<yStr;
+            }
+
+            if (!strncmp(card, "OBJECT", 6)) {
+                cerr << card << endl;
+                char *first = strchr(card, '\'');
+                char *last = strrchr(card, '\'');
+                *last = '\0';
+                strcpy(title, first+1);
+            }
+
+            if (!strncmp(card, "CRVAL", 5)) {
+                char *first = strchr(card, '=');
+                char *last = strrchr(card, '=');
+                *last = '\0';
+
+                // char *last = strrchr(card, '/');
+                //*last = '\0';
+
+                if (card[5] == '1')
+                {
+                    strcpy(crval1, first+1);
+                    char *pch = strtok (crval1," ,");
+                    strcpy(crval1, pch);
+                    
+                }
+                
+                if (card[5] == '2')
+                {
+                    strcpy(crval2, first+1);
+                    char *pch = strtok (crval2," ,");
+                    strcpy(crval2, pch);
+
+                }
+                
+                if (card[5] == '3')
+                {
+                    strcpy(crval3, first+1);
+                    char *pch = strtok (crval3," ,");
+                    strcpy(crval3, pch);
+
+                }
+            }
+
+            if (!strncmp(card, "CRPIX", 5)) {
+                char *first = strchr(card, '=');
+                char *last = strrchr(card, '=');
+                *last = '\0';
+                
+                
+                if (card[5] == '1')
+                {
+                    strcpy(crpix1, first+1);
+
+                    char *pch = strtok (crpix1," ,");
+                    strcpy(crpix1, pch);
+                }
+                
+                if (card[5] == '2')
+                {
+                    strcpy(crpix2, first+1);
+
+                    char *pch = strtok (crpix2," ,");
+                    strcpy(crpix2, pch);
+                }
+                if (card[5] == '3')
+                {
+                    strcpy(crpix3, first+1);
+
+                    char *pch = strtok (crpix3," ,");
+                    strcpy(crpix3, pch);
+                }
+            }
+
+            if (!strncmp(card, "CDELT", 5)) {
+                char *first = strchr(card, '=');
+                char *last = strrchr(card, '=');
+                *last = '\0';
+                
+                if (card[5] == '1')
+                {
+                    strcpy(cdelt1, first+1);
+                    char *pch = strtok (cdelt1," ,");
+                    strcpy(cdelt1, pch);
+                    
+                }
+                
+                if (card[5] == '2')
+                {
+                    strcpy(cdelt2, first+1);
+                    char *pch = strtok (cdelt2," ,");
+                    strcpy(cdelt2, pch);
+                }
+                
+                if (card[5] == '3')
+                {
+                    strcpy(cdelt3, first+1);
+                    char *pch = strtok (cdelt3," ,");
+                    strcpy(cdelt3, pch);
+                }
+            }
+            
+            
+
+        }
+    }
+
+
+    val1=crval1;
+    val2=crval2;
+    val3=crval3;
+    pix1=crpix1;
+    pix2=crpix2;
+    pix3=crpix3;
+    delt1=cdelt1;
+    delt2=cdelt2;
+    delt3=cdelt3;
+
+
+    
+    crval[0]=val1.toDouble(); //problema
+    crval[1]=val2.toDouble();
+    crval[2]=val3.toDouble();
+    cpix[0]=pix1.toDouble();
+    cpix[1]=pix2.toDouble();
+    cpix[2]=pix3.toDouble();
+    cdelt[0]=delt1.toDouble();
+    cdelt[1]=delt2.toDouble();
+    cdelt[2]=delt3.toDouble();
+
+    initSlice=crval[2]-(cdelt[2]*(cpix[2]-1));
+    
+    
+
+    
+
+    
+}
+
+// Note: from cookbook.c in fitsio distribution.
+void vtkFitsReader::printerror(int status) {
+
+    cerr << "vtkFitsReader ERROR.";
+    if (status) {
+        fits_report_error(stderr, status); /* print error report */
+        exit( status );    /* terminate the program, returning error status */
+    }
+    return;
+}
+
+
+// Note: This function adapted from readimage() from cookbook.c in
+// fitsio distribution.
+void vtkFitsReader::CalculateRMS() {
+    
+
+    ReadHeader();
+    
+    vtkStructuredPoints *output = (vtkStructuredPoints *) this->GetOutput();
+    fitsfile *fptr;
+    int status = 0, nfound = 0, anynull = 0;
+    long fpixel, nbuffer, npixels, ii, n=0;
+    double meansquare=0;
+    const int buffsize = 1000;
+    
+    
+    float nullval, buffer[buffsize];
+    char *fn=new char[filename.length() + 1];
+    strcpy(fn, filename.c_str());
+    
+    if ( fits_open_file(&fptr, fn, READONLY, &status) )
+        printerror( status );
+    
+    delete []fn;
+    vtkFloatArray *scalars = vtkFloatArray::New();
+    if ( fits_read_keys_lng(fptr, "NAXIS", 1, 3, naxes, &nfound, &status) )
+        printerror( status );
+    
+    npixels  = naxes[0] * naxes[1] * naxes[2];
+    n=npixels;
+    
+    fpixel   = 1;
+    nullval  = 0;
+    datamin  = 1.0E30;
+    datamax  = -1.0E30;
+    /*
+    cerr << "\nvtkFitsReader: calculating the RMS" << this->filename << endl;
+    cerr << "Dim: " << naxes[0] << " " << naxes[1] << " " << naxes[2] << endl;
+    cerr << "points: " << npixels << endl;
+    cerr << "creating vtk structured points dataset" << endl;
+   */
+    output->SetDimensions(naxes[0], naxes[1], naxes[2]);
+    output->SetOrigin(0.0, 0.0, 0.0);
+    
+    scalars->Allocate(npixels);
+    int bad=0;
+    int slice;
+    int num=0;
+
+
+
+    minmaxslice=new float*[naxes[2]];
+    for(int i=0;i< naxes[2];i++)
+    {
+
+        minmaxslice[i] = new float[2];
+
+        minmaxslice[i][0]= 1.0E30;
+        minmaxslice[i][1]= -1.0E30;
+
+    }
+
+    //For every pixel
+    while (npixels > 0) {
+
+
+
+
+        nbuffer = npixels;
+        if (npixels > buffsize)
+            nbuffer = buffsize;
+        
+        if ( fits_read_img(fptr, TFLOAT, fpixel, nbuffer, &nullval,
+                           buffer, &anynull, &status) )
+            printerror( status );
+
+
+        for (ii = 0; ii < nbuffer; ii++)  {
+            // slice= (num/(naxes[0]*naxes[1]))%(naxes[0]*naxes[1]);
+            slice= (num/ (naxes[0]*naxes[1]) );
+            num++;
+
+            // qDebug()<<"npixel: "<<num <<" è sulla slice "<< slice <<" x: "<<naxes[0]<<" y: "<<naxes[1]<<" z: "<<naxes[2];
+
+            if (std::isnan(buffer[ii]))
+                buffer[ii] = -1000000.0;
+            scalars->InsertNextValue(buffer[ii]);
+
+            if ( buffer[ii]!=-1000000.0)
+            {
+                if ( buffer[ii] < datamin )
+                    datamin = buffer[ii];
+                if ( buffer[ii] > datamax   )
+                    datamax = buffer[ii];
+
+                //qDebug()<<"poreeeee "<<slice;
+                if ( buffer[ii] < minmaxslice[slice][0] )
+                    minmaxslice[slice][0] = buffer[ii];
+                if ( buffer[ii] > minmaxslice[slice][1]   )
+                    minmaxslice[slice][1] = buffer[ii];
+
+                //meansquare+=buffer[ii]*buffer[ii];
+                //  media+=buffer[ii];
+                meansquare+=buffer[ii]*buffer[ii];
+
+            }
+            else
+                bad++;
+        }
+        
+        npixels -= nbuffer;
+        fpixel  += nbuffer;
+    }
+    /*
+    media=media/n;
+    float diff;
+    for(ii=0; ii<n; ii++)
+    {
+        if (scalars->GetValue(ii)!=-1000000.0)
+        {
+            meansquare+=scalars->GetValue(ii)*scalars->GetValue(ii);
+            diff=scalars->GetValue(ii)-media;
+            sigma+=qPow(diff, 2);
+        }
+        else
+            bad++;
+    }
+    
+*/
+    n=n-bad;
+    double means=meansquare/n;
+    rms=qSqrt(means);
+    // sigma=qSqrt(sigma/n);
+    qDebug()<<"rms: "<<rms<<" badpixel: "<<bad<<" x: "<<naxes[0]<<" y: "<<naxes[1]<<" z: "<<naxes[2]<<" Npixels: "<< naxes[0] * naxes[1] * naxes[2];
+
+    if ( fits_close_file(fptr, &status) )
+        printerror( status );
+    
+    output->GetPointData()->SetScalars(scalars);
+
+    return;
+}
+int vtkFitsReader::GetNaxes(int i)
+{
+
+    return naxes[i];
+
+}
+float* vtkFitsReader::GetRangeSlice(int i)
+{
+
+    return minmaxslice[i];
+
+}
diff --git a/Code/src/vtkfitsreader.h b/Code/src/vtkfitsreader.h
new file mode 100644
index 0000000000000000000000000000000000000000..3ae6ab16261221f20d4add95c7694a80b57f91ee
--- /dev/null
+++ b/Code/src/vtkfitsreader.h
@@ -0,0 +1,160 @@
+// .NAME vtkFitsReader - read structured points from FITS file.
+// .SECTION Description
+// vtkFitsReader is a source object that reads FITS data files
+// .SECTION Caveats
+// Uses CFITSIO v2.0 (http://heasarc.gsfc.nasa.gov/docs/software/fitsio)
+
+#ifndef __vtkFitsReader_h
+#define __vtkFitsReader_h
+
+
+#include "vtkAlgorithm.h"
+#include "vtkStructuredPoints.h"
+#include "vtkFloatArray.h"
+
+#include <QString>
+
+extern "C" {
+#include "fitsio.h"
+}
+
+
+//class VTK_EXPORT vtkFitsReader : public vtkStructuredPointsSource
+class VTK_EXPORT vtkFitsReader : public vtkAlgorithm
+{
+public:
+    //    vtkFitsReader();
+    //  static vtkFitsReader *New() {return new vtkFitsReader;}
+
+    static vtkFitsReader *New();
+    vtkFitsReader();
+    ~vtkFitsReader();
+
+    void SetFileName(std::string name);
+    std::string GetFileName(){return filename;}
+
+    vtkTypeRevisionMacro(vtkFitsReader,vtkAlgorithm);
+    void PrintSelf(ostream& os, vtkIndent indent);
+    void PrintHeader(ostream& os, vtkIndent indent);
+    void PrintTrailer(std::ostream& os , vtkIndent indent);
+    double getInitSlice(){return initSlice;}
+    
+    bool is3D;
+    void CalculateRMS();
+    void CalculateMedia();
+    void PrintDetails();
+    double GetSigma(){return sigma;}
+    double GetRMS(){return rms;}
+    double GetMedia(){return media;}
+    long GetEntries(){return npix;}
+    float* GetRangeSlice(int i);
+
+    float GetMin(){return datamin;}
+    float GetMax(){return datamax;}
+    
+    float GetCrval(int i){
+        if(i<3)
+            return crval[i];
+        else return -1;
+    }
+    float GetCpix(int i){
+        if(i<3) return cpix[i];
+        else return -1;}
+    float GetCdelt(int i){if(i<3) return cdelt[i];
+        else return -1;}
+
+    int GetNaxes(int i);
+    
+    vtkFloatArray *fitsScalars;
+
+    // Description:
+    // Get the output data object for a port on this algorithm.
+    vtkStructuredPoints* GetOutput();
+    vtkStructuredPoints* GetOutput(int);
+    virtual void SetOutput(vtkDataObject* d);
+    // Description:
+    // see vtkAlgorithm for details
+    virtual int ProcessRequest(vtkInformation*,
+                               vtkInformationVector**,
+                               vtkInformationVector*);
+
+    QString getSpecies() {return species;};
+    QString getTransition() {return transition;};
+    QString getSurvey() {return survey;};
+
+    void setSpecies(QString s) {species=s;};
+    void setTransition(QString s) {transition=s;};
+    void setSurvey(QString s) {survey=s;};
+
+protected:
+    QString survey;
+    QString species;
+    QString transition;
+
+    float datamin;
+    float datamax;
+    float epoch;   // Part of FITS Header file
+    int *dimensions;   // [x,y,z]
+    int naxis;
+    int points;
+    double crval[3];
+    double cpix[3];
+    double cdelt[3];
+    double sigma=0;
+    double media=0;
+    double rms=0;
+    long npix=0;
+    long naxes[3];
+
+    float **minmaxslice;
+
+    // Description:
+    // This is called by the superclass.
+    // This is the method you should override.
+    virtual int RequestDataObject(
+            vtkInformation* request,
+            vtkInformationVector** inputVector,
+            vtkInformationVector* outputVector );
+
+    // convenience method
+    virtual int RequestInformation(
+            vtkInformation* request,
+            vtkInformationVector** inputVector,
+            vtkInformationVector* outputVector );
+
+    // Description:
+    // This is called by the superclass.
+    // This is the method you should override.
+    virtual int RequestData(
+            vtkInformation* request,
+            vtkInformationVector** inputVector,
+            vtkInformationVector* outputVector );
+
+    // Description:
+    // This is called by the superclass.
+    // This is the method you should override.
+    virtual int RequestUpdateExtent(
+            vtkInformation*,
+            vtkInformationVector**,
+            vtkInformationVector* );
+
+    virtual int FillOutputPortInformation( int port, vtkInformation* info );
+
+private:
+    //  vtkFitsReader( const vtkFitsReader& ); // Not implemented.
+    //  void operator = ( const vtkFitsReader& );  // Not implemented.
+    std::string filename;
+    char title[80];
+    char xStr[80];
+    char yStr[80];
+    char zStr[80];
+//    char cunit3[1][200];
+
+    void ReadHeader();
+    void printerror(int status); // from fitsio distribution
+    double initSlice;
+};
+
+
+
+#endif
diff --git a/Code/src/vtkfitstoolswidget.cpp b/Code/src/vtkfitstoolswidget.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e0fb91a979f2b8079d75fa0e670b72d9f693470
--- /dev/null
+++ b/Code/src/vtkfitstoolswidget.cpp
@@ -0,0 +1,342 @@
+#include "vtkfitstoolswidget.h"
+#include "ui_vtkfitstoolswidget.h"
+#include <vtkCallbackCommand.h>
+#include "vtkInteractorStyleRubberBand2D.h"
+#include <QSlider>
+#include <QCheckBox>
+#include <QSignalMapper>
+#include "vialactea_fileload.h"
+#include <QFileDialog>
+#include <QColorDialog>
+#include <QShortcut>
+#include <QDebug>
+#include "singleton.h"
+
+#include "dbquery.h"
+
+static void SelectionChangedCallbackFunction (
+        vtkObject* caller,
+        long unsigned int eventId,
+        void* clientData,
+        void* callData );
+
+
+vtkfitstoolswidget::vtkfitstoolswidget(vtkwindow_new *v, QWidget *parent) : QWidget(parent), ui(new Ui::vtkfitstoolswidget)
+{
+    ui->setupUi(this);
+
+    vtkwin=v;
+
+    QHeaderView* header = ui->addedSourcesListWidget->horizontalHeader();
+    header->setVisible(true);
+    header->sectionResizeMode(QHeaderView::Stretch);
+
+    updateList();
+
+    QShortcut *shortcut_local = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_L) , this);
+    QObject::connect(shortcut_local, SIGNAL(activated()), this, SLOT(on_addLocalSourcesPushButton_clicked()));
+
+    QShortcut *shortcut_remote = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_R) , this);
+    QObject::connect(shortcut_remote, SIGNAL(activated()), this, SLOT(on_addRemoteSourcesPushButton_clicked()));
+
+    /*
+    QShortcut *shortcut_dataCube_remote = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_D) , this);
+    QObject::connect(shortcut_dataCube_remote, SIGNAL(activated()), this, SLOT(on_datacubeButton_clicked()));
+*/
+
+    ui->addedSourcesListWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+    connect( ui->addedSourcesListWidget, SIGNAL(customContextMenuRequested(QPoint)), SLOT(customMenuRequested(QPoint)));
+
+
+}
+
+vtkfitstoolswidget::~vtkfitstoolswidget()
+{
+    delete ui;
+}
+
+void vtkfitstoolswidget::customMenuRequested(QPoint pos)
+{
+
+
+    QItemSelectionModel *select = ui->addedSourcesListWidget->selectionModel();
+
+    if(!select->selectedRows().isEmpty())
+    {
+        QModelIndex index=  ui->addedSourcesListWidget->indexAt(pos);
+
+        QMenu *menu=new QMenu(this);
+        QAction *viewAct = new QAction(tr("&Delete"), this);
+
+        //viewAct->setStatusTip(tr("Visualize selected map file"));
+        connect(viewAct, SIGNAL(triggered()), this, SLOT(deleteSelected()));
+
+        menu->addAction(viewAct);
+
+        menu->popup(ui->addedSourcesListWidget->viewport()->mapToGlobal(pos));
+
+    }
+}
+
+void vtkfitstoolswidget::deleteSelected()
+{
+    QItemSelectionModel *select = ui->addedSourcesListWidget->selectionModel();
+
+    if(!select->selectedRows().isEmpty())
+    {
+
+        int row= select->selectedRows().first().row();
+        vtkwin->removeSingleEllipse( vtkwin->getEllipseActorList().value( ui->addedSourcesListWidget->item(row, 1)->text() ) );
+        ui->addedSourcesListWidget->removeRow(row);
+
+    }
+}
+
+int vtkfitstoolswidget::getNumOfElements()
+{
+    return ui->addedSourcesListWidget->model()->rowCount();
+
+}
+
+void vtkfitstoolswidget::addToList(QString name,  vtkSmartPointer<vtkLODActor> actor)
+{
+
+    QSignalMapper* mapper = new QSignalMapper(this);
+    QSignalMapper* mapper_slider = new QSignalMapper(this);
+
+
+
+    int row= ui->addedSourcesListWidget->model()->rowCount();
+
+
+    ui->addedSourcesListWidget->insertRow(row);
+
+    QCheckBox  *cb1= new QCheckBox();
+
+    cb1->setChecked(true);
+
+    double r=actor->GetProperty()->GetColor()[0]*255;
+    double g=actor->GetProperty()->GetColor()[1]*255;
+    double b=actor->GetProperty()->GetColor()[2]*255;
+
+    cb1->setStyleSheet("background-color: rgb("+QString::number(r)+","+QString::number(g)+" ,"+QString::number(b)+")");
+
+    ui->addedSourcesListWidget->setCellWidget(row,0,cb1);
+
+    connect(cb1, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+    mapper->setMapping(cb1, row);
+
+
+    QSlider *slider=new QSlider(Qt::Horizontal);
+    sliders.append(slider);
+    slider->setRange(0,255);
+    slider->setValue(255);
+    ui->addedSourcesListWidget->setCellWidget(row,2,slider);
+    connect(slider, SIGNAL(sliderReleased()), mapper_slider, SLOT(map()));
+    mapper_slider->setMapping(slider, row);
+
+    QTableWidgetItem *item_1 = new QTableWidgetItem();
+    item_1->setText(name);
+
+    //  item_1->setTextColor(QColor(r*255, g*255, b*255));
+    ui->addedSourcesListWidget->setItem(row,1,item_1);
+
+    connect(mapper, SIGNAL(mapped(int)), this, SLOT(checkboxClicked(int)));
+    connect(mapper_slider, SIGNAL(mapped(int)), this, SLOT(opacityValueChanged(int)));
+
+
+}
+
+
+void vtkfitstoolswidget::updateList()
+{
+
+
+    QSignalMapper* mapper = new QSignalMapper(this);
+    QSignalMapper* mapper_slider = new QSignalMapper(this);
+
+
+
+    QHash<QString,  vtkSmartPointer<vtkLODActor> >::iterator i;
+    QHash<QString,  vtkSmartPointer<vtkLODActor> >tmp=vtkwin->getEllipseActorList();
+
+
+
+    int row=0;
+
+    for (i = tmp.begin(); i != tmp.end(); ++i)
+    {
+
+        ui->addedSourcesListWidget->insertRow(row);
+
+        QCheckBox  *cb1= new QCheckBox();
+
+        cb1->setChecked(true);
+
+
+        double r=i.value()->GetProperty()->GetColor()[0]*255;
+        double g=i.value()->GetProperty()->GetColor()[1]*255;
+        double b=i.value()->GetProperty()->GetColor()[2]*255;
+
+
+        cb1->setStyleSheet("background-color: rgb("+QString::number(r)+","+QString::number(g)+" ,"+QString::number(b)+")");
+
+        ui->addedSourcesListWidget->setCellWidget(row,0,cb1);
+
+        connect(cb1, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+        mapper->setMapping(cb1, row);
+
+
+        QSlider *slider=new QSlider(Qt::Horizontal);
+        sliders.append(slider);
+        slider->setRange(0,255);
+        slider->setValue(255);
+        ui->addedSourcesListWidget->setCellWidget(row,2,slider);
+        connect(slider, SIGNAL(sliderReleased()), mapper_slider, SLOT(map()));
+        mapper_slider->setMapping(slider, row);
+
+        QTableWidgetItem *item_1 = new QTableWidgetItem();
+        item_1->setText(i.key());
+
+        ui->addedSourcesListWidget->setItem(row,1,item_1);
+        row++;
+    }
+
+    connect(mapper, SIGNAL(mapped(int)), this, SLOT(checkboxClicked(int)));
+    connect(mapper_slider, SIGNAL(mapped(int)), this, SLOT(opacityValueChanged(int)));
+
+    vtkwin->update();
+
+}
+
+
+void vtkfitstoolswidget::opacityValueChanged(int o)
+{
+    double opacity=(double)sliders.at(o)->value()/255;
+    vtkwin->getEllipseActorList().value(ui->addedSourcesListWidget->item(o, 1)->text())->GetProperty()->SetOpacity(opacity);
+    vtkwin->update();
+}
+
+void vtkfitstoolswidget::checkboxClicked(int cb)
+{
+
+    if(vtkwin->getEllipseActorList().value(ui->addedSourcesListWidget->item(cb, 1)->text())->GetVisibility())
+        vtkwin->getEllipseActorList().value(ui->addedSourcesListWidget->item(cb, 1)->text())->VisibilityOff();
+    else
+        vtkwin->getEllipseActorList().value(ui->addedSourcesListWidget->item(cb, 1)->text())->VisibilityOn();
+
+    vtkwin->update();
+}
+
+void vtkfitstoolswidget::on_selectButton_clicked()
+{
+
+    vtkwin->setSelectionFitsViewerInteractorStyle();
+    vtkwin->activateWindow();
+
+}
+
+void SelectionChangedCallbackFunction (vtkObject* vtkNotUsed(caller), long unsigned int vtkNotUsed(eventId), void* vtkNotUsed(clientData),void* callData )
+{
+    std::cout << "Selection changed callback" << std::endl;
+
+    unsigned int* rect = reinterpret_cast<unsigned int*> ( callData );
+    unsigned int pos1X = rect[0];
+    unsigned int pos1Y = rect[1];
+    unsigned int pos2X = rect[2];
+    unsigned int pos2Y = rect[3];
+
+    std::cout << "Start x: " << pos1X
+              << " Start y: " << pos1Y
+              << " End x: " << pos2X
+              << " End y: " << pos2Y << std::endl;
+}
+
+
+void vtkfitstoolswidget::on_noSelectButton_clicked()
+{
+    vtkwin->setVtkInteractorStyleImage();
+}
+
+void vtkfitstoolswidget::on_lutComboBox_currentIndexChanged(int index)
+{
+    
+    //changeLut(ui->lutComboBox->currentText().toStdString().c_str());
+
+    changeLutScale(ui->lutComboBox->currentText().toStdString().c_str(), ui->scaleComboBox->currentText().toStdString().c_str());
+    
+}
+
+void vtkfitstoolswidget::on_scaleComboBox_currentIndexChanged(int index)
+{
+    changeLutScale(ui->lutComboBox->currentText().toStdString().c_str(), ui->scaleComboBox->currentText().toStdString().c_str());
+}
+
+void vtkfitstoolswidget::changeLutScale(std::string palette, std::string scale)
+{
+    vtkwin->changeFitsScale(palette, scale);
+}
+
+void vtkfitstoolswidget::changeLut(std::string palette)
+{
+    vtkwin->changeFitsPalette(palette);
+}
+
+void vtkfitstoolswidget::on_addLocalSourcesPushButton_clicked()
+{
+    QString fileName = QFileDialog::getOpenFileName(this,tr("Open file"), "", tr("(*.*)"));
+
+    Vialactea_FileLoad *fileload = new Vialactea_FileLoad(fileName,vtkwin);
+    fileload->show();
+}
+
+void vtkfitstoolswidget::on_addRemoteSourcesPushButton_clicked()
+{
+
+
+    vtkwin->setSkyRegionSelectorInteractorStyle();
+    vtkwin->activateWindow();
+}
+
+void vtkfitstoolswidget::on_galacticRadioButton_toggled(bool checked)
+{
+    vtkwin->changeWCS(checked);
+}
+
+void vtkfitstoolswidget::on_eclipticRadioButton_toggled(bool checked)
+{
+}
+
+void vtkfitstoolswidget::on_addedSourcesListWidget_doubleClicked(const QModelIndex &index)
+{
+
+    if(index.column()==0)
+    {
+
+        //Initial color
+        double r=vtkwin->getEllipseActorList().value(ui->addedSourcesListWidget->item( index.row(), 1)->text())->GetProperty()->GetColor()[0]*255;
+        double g=vtkwin->getEllipseActorList().value(ui->addedSourcesListWidget->item(index.row() , 1)->text())->GetProperty()->GetColor()[1]*255;
+        double b=vtkwin->getEllipseActorList().value(ui->addedSourcesListWidget->item( index.row() , 1)->text())->GetProperty()->GetColor()[2]*255;
+
+        QColor color = QColorDialog::getColor(QColor(r,g,b), this);
+
+        vtkwin->getEllipseActorList().value(ui->addedSourcesListWidget->item( index.row(), 1)->text())->GetProperty()->SetColor(color.redF(), color.greenF(), color.blueF());
+        vtkwin->update();
+
+        //update color on table
+        ui->addedSourcesListWidget->cellWidget(index.row(),0)->setStyleSheet("background-color: rgb("+QString::number(color.redF()*255)+","+QString::number(color.greenF()*255)+" ,"+QString::number(color.blueF()*255)+")");
+
+
+    }
+}
+
+
+void vtkfitstoolswidget::on_datacubeButton_clicked()
+{
+    qDebug()<<"datacube";
+    dbquery *queryWindow = &Singleton<dbquery>::Instance();
+    queryWindow->show();
+
+}
+
+
diff --git a/Code/src/vtkfitstoolswidget.h b/Code/src/vtkfitstoolswidget.h
new file mode 100644
index 0000000000000000000000000000000000000000..ce6d95046b22b3b8698de025c02b13e50afe1ffb
--- /dev/null
+++ b/Code/src/vtkfitstoolswidget.h
@@ -0,0 +1,51 @@
+#ifndef VTKFITSTOOLSWIDGET_H
+#define VTKFITSTOOLSWIDGET_H
+
+#include <QWidget>
+#include "vtkwindow_new.h"
+#include <QTableWidgetItem>
+
+namespace Ui {
+class vtkfitstoolswidget;
+}
+
+class vtkfitstoolswidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit vtkfitstoolswidget(vtkwindow_new* v=0,QWidget *parent = 0 );
+    ~vtkfitstoolswidget();
+    void updateList();
+    QVector<QSlider*> sliders;
+    void changeLut(std::string palette);
+    void changeLutScale(std::string palette, std::string scale);
+    void addToList(QString name, vtkSmartPointer<vtkLODActor> actor);
+    int getNumOfElements();
+
+
+private slots:
+    void on_selectButton_clicked();
+    void on_noSelectButton_clicked();
+    void checkboxClicked(int cb);
+    void opacityValueChanged(int o);
+    void on_lutComboBox_currentIndexChanged(int index);
+    void on_addRemoteSourcesPushButton_clicked();
+    void on_galacticRadioButton_toggled(bool checked);
+    void on_eclipticRadioButton_toggled(bool checked);
+    void on_addedSourcesListWidget_doubleClicked(const QModelIndex &index);
+    void customMenuRequested(QPoint pos);
+    void deleteSelected();
+    void on_scaleComboBox_currentIndexChanged(int index);
+    void on_datacubeButton_clicked();
+
+public slots:
+    void on_addLocalSourcesPushButton_clicked();
+
+private:
+    Ui::vtkfitstoolswidget *ui;
+    vtkwindow_new *vtkwin;
+
+};
+
+#endif // VTKFITSTOOLSWIDGET_H
diff --git a/Code/src/vtkfitstoolwidget_new.cpp b/Code/src/vtkfitstoolwidget_new.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3a25ed7fdd0d43ed77ca9ab61396196b3010d250
--- /dev/null
+++ b/Code/src/vtkfitstoolwidget_new.cpp
@@ -0,0 +1,91 @@
+#include "vtkfitstoolwidget_new.h"
+#include "ui_vtkfitstoolwidget_new.h"
+#include <QFileInfo>
+#include <QDebug>
+#include "vtkProperty.h"
+
+vtkfitstoolwidget_new::vtkfitstoolwidget_new(QWidget *parent) :
+    QMainWindow(parent),
+    ui(new Ui::vtkfitstoolwidget_new)
+{
+    ui->setupUi(this);
+    ui->deletePushButton->setStyleSheet("background-color: red");
+}
+
+vtkfitstoolwidget_new::~vtkfitstoolwidget_new()
+{
+    delete ui;
+}
+
+void vtkfitstoolwidget_new::addLayer(vtkfitstoolwidgetobject *o)
+{
+
+    if (o->getType() == 0)
+    {
+        QTreeWidgetItem *treeItem=addTreeRoot(o->getName());
+        o->setTreeWidgetItem( treeItem );
+    }
+
+    if (o->getType() == 1)
+    {
+
+        double r=o->getActor()->GetProperty()->GetColor()[0]*255;
+        double g=o->getActor()->GetProperty()->GetColor()[1]*255;
+        double b=o->getActor()->GetProperty()->GetColor()[2]*255;
+
+        QBrush brush (QColor(r,g,b));
+        addTreeChild(o->getParent()->getTreeWidgetItem(),  o->getName(),brush);
+    }
+
+
+    layerList.append(o);
+}
+
+QTreeWidgetItem* vtkfitstoolwidget_new::addTreeRoot(QString name)
+{
+    QTreeWidgetItem *treeItem = new QTreeWidgetItem(ui->layerTreeWidget);
+    treeItem->setText(1, name);
+
+    return treeItem;
+}
+
+void vtkfitstoolwidget_new::addTreeChild(QTreeWidgetItem *parent, QString name, QBrush brush)
+{
+
+    QTreeWidgetItem *treeItem = new QTreeWidgetItem();
+
+
+
+    treeItem->setBackground(0,brush);
+
+    treeItem->setText(1, name);
+
+    parent->addChild(treeItem);
+}
+
+
+
+void vtkfitstoolwidget_new::setWavelength(QString w)
+{
+    wavelength=w;
+}
+
+void vtkfitstoolwidget_new::on_layerTreeWidget_itemSelectionChanged()
+{
+    ui->layerTreeWidget->selectionModel()->select(ui->layerTreeWidget->model()->index(ui->layerTreeWidget ->indexOfTopLevelItem(ui->layerTreeWidget->currentItem()),0),QItemSelectionModel::Select);
+    int pos = ui->layerTreeWidget->currentIndex().row()+ qAbs(ui->layerTreeWidget->indexOfTopLevelItem ( ui->layerTreeWidget->currentItem()));
+    ui->nameLineEdit->setText( layerList.at(pos)->getName() );
+    ui->wavelenghtLineEdit->setText( layerList.at(pos)->getWavelength() );
+
+}
+
+void vtkfitstoolwidget_new::on_savePushButton_clicked()
+{
+    int pos = ui->layerTreeWidget->currentIndex().row()+ qAbs(ui->layerTreeWidget->indexOfTopLevelItem ( ui->layerTreeWidget->currentItem()));
+    qDebug()<<"pos: "<<pos;
+    qDebug()<<"type: "<<layerList.at(pos)->getType();
+    qDebug()<<"name: "<<layerList.at(pos)->getName();
+
+    layerList.at(pos)->setName(ui->nameLineEdit->text());
+    layerList.at(pos)->setWavelength(ui->wavelenghtLineEdit->text());
+}
diff --git a/Code/src/vtkfitstoolwidget_new.h b/Code/src/vtkfitstoolwidget_new.h
new file mode 100644
index 0000000000000000000000000000000000000000..b84e3156a43f29423fc308c906e53ecce8a5d9b4
--- /dev/null
+++ b/Code/src/vtkfitstoolwidget_new.h
@@ -0,0 +1,41 @@
+#ifndef VTKFITSTOOLWIDGET_NEW_H
+#define VTKFITSTOOLWIDGET_NEW_H
+
+#include <QMainWindow>
+#include "vtkfitstoolwidgetobject.h"
+#include <QTreeWidgetItem>
+
+namespace Ui {
+class vtkfitstoolwidget_new;
+}
+
+class vtkfitstoolwidget_new : public QMainWindow
+{
+    Q_OBJECT
+
+public:
+    explicit vtkfitstoolwidget_new(QWidget *parent = 0);
+    ~vtkfitstoolwidget_new();
+    void addLayer(vtkfitstoolwidgetobject *o);
+//    void setName(QString n);
+    void setWavelength(QString w);
+
+
+private slots:
+    void on_layerTreeWidget_itemSelectionChanged();
+
+    void on_savePushButton_clicked();
+
+private:
+    Ui::vtkfitstoolwidget_new *ui;
+    QList<vtkfitstoolwidgetobject*> layerList;
+    QString wavelength;
+    //void addTreeRoot(QString name );
+    QTreeWidgetItem* addTreeRoot(QString name );
+
+    void addTreeChild(QTreeWidgetItem *parent, QString name, QBrush brush);
+
+
+};
+
+#endif // VTKFITSTOOLWIDGET_NEW_H
diff --git a/Code/src/vtkfitstoolwidgetobject.cpp b/Code/src/vtkfitstoolwidgetobject.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e8fca1728153e2e825bdca94cde656b3db3de51
--- /dev/null
+++ b/Code/src/vtkfitstoolwidgetobject.cpp
@@ -0,0 +1,51 @@
+#include "vtkfitstoolwidgetobject.h"
+#include <QFileInfo>
+
+/*
+ * type
+ * 0 = image
+ * 1 = sigleband
+ * 2 = centroid
+ * 3 = isocontour
+*/
+
+vtkfitstoolwidgetobject::vtkfitstoolwidgetobject(int t)
+{
+    type=t;
+    wavelength="";
+    name="";
+}
+
+void vtkfitstoolwidgetobject::setTreeWidgetItem( QTreeWidgetItem *i)
+{
+    item=i;
+}
+
+void vtkfitstoolwidgetobject::setParentItem( vtkfitstoolwidgetobject *p)
+{
+    parent=p;
+}
+
+void vtkfitstoolwidgetobject::setName( QString n)
+{
+    if(type == 0)
+    {
+        path=n;
+        QFileInfo fileInfo(n);
+        name =fileInfo.fileName();
+    }
+    else
+        name=n;
+}
+
+void vtkfitstoolwidgetobject::setWavelength(QString w)
+{
+    wavelength=w;
+}
+
+void vtkfitstoolwidgetobject::setActor(vtkSmartPointer<vtkLODActor> a)
+{
+    actor=a;
+}
+
+
diff --git a/Code/src/vtkfitstoolwidgetobject.h b/Code/src/vtkfitstoolwidgetobject.h
new file mode 100644
index 0000000000000000000000000000000000000000..5839e9299a8e1c66246fa93496d8281a4b19f7f9
--- /dev/null
+++ b/Code/src/vtkfitstoolwidgetobject.h
@@ -0,0 +1,60 @@
+#ifndef VTKFITSTOOLWIDGETOBJECT_H
+#define VTKFITSTOOLWIDGETOBJECT_H
+
+#include <QString>
+#include <QTreeWidgetItem>
+#include "vtkSmartPointer.h"
+#include "vtkLODActor.h"
+#include "vtkfitsreader.h"
+
+class vtkfitstoolwidgetobject
+{
+public:
+    vtkfitstoolwidgetobject(int t);
+    int getType() {return type;}
+    vtkfitstoolwidgetobject* getParent() {return parent;}
+    void setTreeWidgetItem( QTreeWidgetItem *i);
+    QTreeWidgetItem* getTreeWidgetItem( ){return item;}
+    void setParentItem( vtkfitstoolwidgetobject *p);
+    void setName( QString n);
+    QString getName() {return name;}
+    void setWavelength(QString w);
+    QString getWavelength(){return wavelength;}
+    void setActor(vtkSmartPointer<vtkLODActor> a);
+    void setFitsReader(vtkSmartPointer<vtkFitsReader> f){fits=f;}
+    vtkSmartPointer<vtkLODActor> getActor(){return actor;}
+    vtkSmartPointer<vtkFitsReader> getFits(){return fits;}
+    QString getLutScale() {return lutScale;}
+    QString getLutType() {return lutType;}
+    int getLayerNumber(){return layerNumber;}
+    void setLutScale( QString l) {lutScale=l;}
+    void setLutType( QString l) {lutType=l;}
+    void setLayerNumber( int l) {layerNumber=l;}
+    void setSpecies( QString l) {species=l;}
+    void setTransition( QString l) {transition=l;}
+    void setSurvey(QString l) {survey=l;}
+
+
+    QString getSpecies( ) {return species;}
+    QString getTransition( ) {return transition;}
+    QString getSurvey() {return survey;}
+
+private:
+    int type;
+    int layerNumber;
+    QString name;
+    QString species;
+    QString transition;
+    QString survey;
+    QString path;
+    QString wavelength;
+    QTreeWidgetItem *item;
+    vtkfitstoolwidgetobject *parent;
+    vtkSmartPointer<vtkLODActor> actor;
+    vtkSmartPointer<vtkFitsReader> fits;
+    QString lutType;
+    QString lutScale;
+
+};
+
+#endif // VTKFITSTOOLWIDGETOBJECT_H
diff --git a/Code/src/vtktoolswidget.cpp b/Code/src/vtktoolswidget.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9a7f105ba5cceba4703af787ce83e7204ffe4bb1
--- /dev/null
+++ b/Code/src/vtktoolswidget.cpp
@@ -0,0 +1,299 @@
+#include "vtktoolswidget.h"
+#include "ui_vtktoolswidget.h"
+#include "qdebug.h"
+
+#include "vtkSmartPointer.h"
+
+#include <vtkXYPlotActor.h>
+#include <vtkDataSet.h>
+#include "vtkProperty2D.h"
+
+
+
+
+vtktoolswidget::vtktoolswidget(vtkwindow_new  *v,QWidget *parent) :
+    QWidget(parent),
+    ui(new Ui::vtktoolswidget)
+{
+
+    ui->setupUi(this);
+    vtkwin=v;
+
+    qDebug()<<"#col: "<<vtkwin->table->getNumberOfColumns();
+
+    for (unsigned int i =0; i < vtkwin->table->getNumberOfColumns();i++)
+    {
+        QString field =  QString(vtkwin->table->getColName(i).c_str());
+        ui->scalarComboBox->addItem(field);
+    }
+}
+
+
+
+vtktoolswidget::~vtktoolswidget()
+{
+    delete ui;
+}
+
+
+void vtktoolswidget::plotHistogram(int i)
+{
+
+    QVector<double> y(vtkwin->table->getbinNumber());
+    qDebug()<<"y: "<<y;
+
+    y=vtkwin->table->getHistogram(i);
+    qDebug()<<"y post: "<<y;
+
+
+    QVector<double> x(vtkwin->table->getbinNumber());
+    qDebug()<<"x "<<vtkwin->table->getbinNumber();
+
+    x=vtkwin->table->getHistogramValue(i);
+
+    range=new float[3];
+    qDebug()<<"dddd";
+
+    vtkwin->table->getRange(i,range);
+    qDebug()<<"d2";
+/*
+    // create graph and assign data to it:
+    ui->histogramWidget->addGraph();
+    ui->histogramWidget->graph(0)->setData(x, y);
+    // give the axes some labels:
+    // ui->histogramWidget->xAxis->setTickLabels(false);
+    ui->histogramWidget->yAxis->setTickLabels(false);
+
+
+    // set axes ranges, so we see all data:
+    ui->histogramWidget->xAxis->setRange(range[0],range[1]);
+    ui->histogramWidget->yAxis->setRange(0, range[2]);
+    ui->histogramWidget->replot();
+
+*/
+
+
+}
+
+void vtktoolswidget::drawLine(double from, double to)
+{
+
+    if(fromLine!=0 && toLine!=0 )
+    {
+
+        ui->histogramWidget->removeItem(fromLine);
+        ui->histogramWidget->removeItem(toLine);
+    }
+
+
+    fromLine = new QCPItemLine(ui->histogramWidget);
+
+    toLine = new QCPItemLine(ui->histogramWidget);
+
+
+
+    QPen pen;
+    pen.setColor( Qt::red );
+
+    fromLine->setPen( pen );
+    pen.setColor( Qt::green );
+    toLine->setPen( pen);
+
+    fromLine->start->setCoords(from, 0);
+    fromLine->end->setCoords(from, range[2]);
+
+    toLine->start->setCoords(to, 0);
+    toLine->end->setCoords(to, range[2]);
+
+    ui->histogramWidget->addItem(fromLine);
+    ui->histogramWidget->addItem(toLine);
+
+    ui->histogramWidget->replot();
+
+
+    vtkwin->pp->setLookupTable(from,to);
+
+}
+
+void vtktoolswidget::on_ShowBoxCheckBox_clicked(bool checked)
+{
+    vtkwin->showBox(checked);
+}
+
+void vtktoolswidget::on_ShowAxesCheckBox_clicked(bool checked)
+{
+    vtkwin->showAxes(checked);
+
+}
+
+void vtktoolswidget::on_activateLutCheckBox_clicked(bool checked)
+{
+    ui->lutLabel->setEnabled(checked);
+    ui->lutComboBox->setEnabled(checked);
+    ui->scalarLabel->setEnabled(checked);
+    ui->scalarComboBox->setEnabled(checked);
+
+    std::string palette="AllWhite";
+    if (checked)
+    {
+        palette=ui->lutComboBox->currentText().toStdString().c_str();
+    }
+
+    changeLut(palette);
+}
+
+
+void vtktoolswidget::changeLut(std::string palette)
+{
+    vtkwin->changePalette(palette);
+}
+
+void vtktoolswidget::changeScalar(std::string scalar)
+{
+    vtkwin->changeScalar(scalar);
+}
+
+void vtktoolswidget::on_lutComboBox_currentIndexChanged(const QString &arg1)
+{
+    changeLut(ui->lutComboBox->currentText().toStdString().c_str());
+}
+
+void vtktoolswidget::on_ShowColorbarCheckBox_clicked(bool checked)
+{
+    vtkwin->showColorbar(checked);
+}
+
+void vtktoolswidget::on_scalarComboBox_currentIndexChanged(const QString &arg1)
+{
+    qDebug()<<"on_scalarComboBox_currentIndexChanged: "<<ui->scalarComboBox->currentText().toStdString().c_str();
+  //  changeScalar(ui->scalarComboBox->currentText().toStdString().c_str());
+}
+
+void vtktoolswidget::on_scalarComboBox_currentIndexChanged(int index)
+{
+
+    qDebug()<<"bbbb";
+
+
+   plotHistogram(index);
+    qDebug()<<"range[0]: "<<range[0];
+    qDebug()<<"range[1]: "<<range[1];
+
+
+    /*
+    drawLine(range[0],range[1]);
+
+    ui->fromValue->setText(QString::number(range[0]));
+    ui->toValue->setText(QString::number(range[1]));
+
+
+
+    ui->fromSlider->setMinimum(range[0]);
+    ui->fromSlider->setMaximum(range[1]);
+
+    ui->toSlider->setMinimum(range[0]);
+    ui->toSlider->setMaximum(range[1]);
+
+    ui->fromSlider->setValue(range[0]);
+    ui->toSlider->setValue(range[1]);
+*/
+
+}
+
+void vtktoolswidget::on_fromSlider_sliderMoved(int position)
+{
+
+
+    ui->fromValue->setText(QString::number(position));
+
+    drawLine(position,ui->toValue->text().toDouble());
+
+
+}
+
+void vtktoolswidget::on_toSlider_sliderMoved(int position)
+{
+
+
+    ui->toValue->setText(QString::number(position));
+
+    drawLine(ui->fromValue->text().toDouble(),position);
+
+}
+
+void vtktoolswidget::on_fromValue_textChanged(const QString &arg1)
+{
+    ui->fromSlider->setValue(arg1.toDouble());
+
+    drawLine(ui->fromValue->text().toDouble(),ui->toValue->text().toDouble());
+
+}
+
+void vtktoolswidget::on_toValue_textChanged(const QString &arg1)
+{
+    ui->toSlider->setValue(arg1.toDouble());
+
+    drawLine(ui->fromValue->text().toDouble(),ui->toValue->text().toDouble());
+}
+
+
+void vtktoolswidget::on_scaleCheckBox_clicked(bool checked)
+{
+    vtkwin->scale(checked);
+}
+
+void vtktoolswidget::on_cameraLeft_clicked()
+{
+    vtkwin->setCameraAzimuth(-90);
+}
+
+void vtktoolswidget::on_cameraBack_clicked()
+{
+    vtkwin->setCameraAzimuth(-180);
+
+}
+
+void vtktoolswidget::on_cameraRight_clicked()
+{
+    vtkwin->setCameraAzimuth(90);
+
+}
+
+void vtktoolswidget::on_frontCamera_clicked()
+{
+    vtkwin->setCameraAzimuth(0);
+
+}
+
+void vtktoolswidget::on_topCamera_clicked()
+{
+    vtkwin->setCameraElevation(90);
+
+}
+
+void vtktoolswidget::on_bottomCamera_clicked()
+{
+    vtkwin->setCameraElevation(-90);
+
+}
+
+void vtktoolswidget::on_gridCheckBox_clicked(bool checked)
+{
+
+    vtkwin->showGrid(checked);
+}
+
+void vtktoolswidget::on_scalarComboBox_activated(const QString &arg1)
+{
+
+}
+
+void vtktoolswidget::on_scaleCheckBox_clicked()
+{
+
+}
+
+void vtktoolswidget::on_ShowColorbarCheckBox_clicked()
+{
+
+}
diff --git a/Code/src/vtktoolswidget.h b/Code/src/vtktoolswidget.h
new file mode 100644
index 0000000000000000000000000000000000000000..5481ff754b159adfb9f61238a03327366d5f64d0
--- /dev/null
+++ b/Code/src/vtktoolswidget.h
@@ -0,0 +1,64 @@
+#ifndef VTKTOOLSWIDGET_H
+#define VTKTOOLSWIDGET_H
+
+#include <QWidget>
+#include "vtkwindow_new.h"
+#include "qcustomplot.h"
+namespace Ui {
+class vtktoolswidget;
+}
+
+class vtktoolswidget : public QWidget
+{
+    Q_OBJECT
+
+public:
+    explicit vtktoolswidget( vtkwindow_new* v=0,QWidget *parent = 0 );
+    ~vtktoolswidget();
+    float *range;
+
+private slots:
+    void on_ShowBoxCheckBox_clicked(bool checked);
+    void on_ShowAxesCheckBox_clicked(bool checked);
+    void on_activateLutCheckBox_clicked(bool checked);
+    void on_lutComboBox_currentIndexChanged(const QString &arg1);
+    void changeLut(std::string palette);
+    void on_ShowColorbarCheckBox_clicked(bool checked);
+    void on_scalarComboBox_currentIndexChanged(const QString &arg1);
+    void changeScalar(std::string scalar);
+    void plotHistogram(int i);
+    void on_scalarComboBox_currentIndexChanged(int index);
+    void drawLine(double from, double to);
+    void on_fromSlider_sliderMoved(int position);
+    void on_toSlider_sliderMoved(int position);
+    void on_fromValue_textChanged(const QString &arg1);
+    void on_toValue_textChanged(const QString &arg1);
+    void on_scaleCheckBox_clicked(bool checked);
+
+    void on_cameraLeft_clicked();
+
+    void on_cameraBack_clicked();
+
+    void on_cameraRight_clicked();
+
+    void on_frontCamera_clicked();
+
+    void on_topCamera_clicked();
+
+    void on_bottomCamera_clicked();
+
+    void on_gridCheckBox_clicked(bool checked);
+    void on_scalarComboBox_activated(const QString &arg1);
+
+    void on_scaleCheckBox_clicked();
+
+    void on_ShowColorbarCheckBox_clicked();
+
+private:
+    Ui::vtktoolswidget *ui;
+    vtkwindow_new *vtkwin;
+    QCPItemLine *fromLine ;
+    QCPItemLine *toLine;
+};
+
+#endif // VTKTOOLSWIDGET_H
diff --git a/Code/src/vtkwindow_new.cpp b/Code/src/vtkwindow_new.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eea3d501cacbbba486bf05e96055bb3ae7fbe9da
--- /dev/null
+++ b/Code/src/vtkwindow_new.cpp
@@ -0,0 +1,4896 @@
+#include "vtkwindow_new.h"
+#include "ui_vtkwindow_new.h"
+#include "qdebug.h"
+#include "vtkOutlineFilter.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkProperty.h"
+#include "vtkImageDataGeometryFilter.h"
+#include "vtkPlanes.h"
+#include "vtkClipPolyData.h"
+#include "vtkPlane.h"
+#include "vtkCutter.h"
+#include "vtkStripper.h"
+#include "vtkFrustumSource.h"
+#include "vtkfitstoolswidget.h"
+#include "luteditor.h"
+#include "vtkImageMapToWindowLevelColors.h"
+#include "vtkTextProperty.h"
+#include "vtkCaptionActor2D.h"
+#include "vtkTextActor.h"
+#include "astroutils.h"
+#include "vtkAppendPolyData.h"
+#include "vtkCleanPolyData.h"
+#include "vtkRendererCollection.h"
+#include "vtkImageActorPointPlacer.h"
+#include <QSignalMapper>
+#include "dbquery.h"
+#include "fitsimagestatisiticinfo.h"
+#include "vispoint.h"
+#include <QDir>
+#include "vtktoolswidget.h"
+#include "vtkInteractorStyleDrawPolygon.h"
+#include "vtkNew.h"
+#include "vtkHardwareSelector.h"
+#include "vtkSelection.h"
+#include "vtkCollection.h"
+#include "vtkExtractSelection.h"
+#include "vtkUnstructuredGrid.h"
+#include "vtkGeometryFilter.h"
+#include "vtkMath.h"
+#include "vtkObjectFactory.h"
+#include "vtkInteractorStyleRubberBandPick.h"
+#include "vtkAreaPicker.h"
+#include "vtkAbstractPicker.h"
+#include "vtkExtractGeometry.h"
+#include "vtkInteractorStyleRubberBand2D.h"
+#include "vtkVertexGlyphFilter.h"
+#include "vtkInteractorStyleImage.h"
+#include "higalselectedsources.h"
+#include "ui_higalselectedsources.h"
+#include "vlkbsimplequerycomposer.h"
+#include "vtkCellPicker.h"
+#include "vtkCubeAxesActor2D.h"
+#include "vtkTransform.h"
+#include "selectedsourcefieldsselect.h"
+#include "vtkImageShiftScale.h"
+#include "vtkCornerAnnotation.h"
+#include "vtkfitstoolwidget_new.h"
+#include "vtkfitstoolwidgetobject.h"
+#include "singleton.h"
+#include <vtkAutoInit.h>
+#include "vtkPolyLine.h"
+#include "vialacteainitialquery.h"
+#include "selectedsourcesform.h"
+#include "vtkContourFilter.h"
+#include "vtkLegendScaleActor.h"
+#include "vtkAxisActor2D.h"
+#include "vtkCubeAxesActor.h"
+#include "lutcustomize.h"
+#include "vtkExtractHistogram.h"
+#include "vtkDoubleArray.h"
+#include "vtkTable.h"
+#include <vtkGlyph2D.h>
+#include "vtkRegularPolygonSource.h"
+#include <QSettings>
+#include "vtkImageBlend.h"
+#include "vtkDataSetMapper.h"
+#include "vtkProperty2D.h"
+#include "extendedglyph3d.h"
+#include "vtkImageSliceMapper.h"
+#include "vtkImageProperty.h"
+#include "vtkImageStack.h"
+#include "vtkProp3D.h"
+#include "vtkImageChangeInformation.h"
+#include "vtkImageResize.h"
+#include "vtkImageSliceCollection.h"
+
+#include "filtercustomize.h"
+
+#include "vosamp.h"
+
+#include "vtkAssembly.h"
+
+
+
+VTK_MODULE_INIT(vtkRenderingOpenGL)
+VTK_MODULE_INIT(vtkInteractionStyle)
+VTK_MODULE_INIT(vtkRenderingFreeType)
+VTK_MODULE_INIT(vtkRenderingFreeTypeOpenGL)
+VTK_MODULE_INIT(vtkRenderingVolumeOpenGL)
+
+#define VTK_NEW(type, instance)  vtkSmartPointer<type> instance = vtkSmartPointer<type>::New();
+
+#ifdef Q_OS_OSX
+#include "osxHelper.h"
+#endif
+
+class InteractorStyleFreeHandOn3DVisualization : public vtkInteractorStyleDrawPolygon
+{
+                                                            private:
+                                                            vtkwindow_new *vtkwin;
+vtkSmartPointer<vtkPolyData> Points;
+vtkSmartPointer<vtkPolyData> Points_ori;
+vtkSmartPointer<vtkActor> SelectedActor;
+vtkSmartPointer<vtkPolyDataMapper> SelectedMapper;
+
+public:
+static InteractorStyleFreeHandOn3DVisualization* New();
+vtkTypeMacro(InteractorStyleFreeHandOn3DVisualization,vtkInteractorStyleDrawPolygon)
+
+InteractorStyleFreeHandOn3DVisualization()
+{
+    this->SelectedMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    this->SelectedActor = vtkSmartPointer<vtkActor>::New();
+    this->SelectedActor->SetMapper(SelectedMapper);
+
+}
+
+virtual void OnLeftButtonUp()
+{
+
+    vtkInteractorStyleDrawPolygon::OnLeftButtonUp();
+    std::vector<vtkVector2i> points = this->GetPolygonPoints();
+
+    if(points.size() >= 3)
+    {
+        vtkNew<vtkIntArray> polygonPointsArray;
+        polygonPointsArray->SetNumberOfComponents(2);
+        polygonPointsArray->SetNumberOfTuples(points.size());
+        for (unsigned int j = 0; j < points.size(); ++j)
+        {
+            const vtkVector2i &v = points[j];
+            int pos[2] = {v[0], v[1]};
+            polygonPointsArray->SetTupleValue(j, pos);
+        }
+
+        vtkNew<vtkHardwareSelector> hardSel;
+        hardSel->SetRenderer(this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+        int* wsize = this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->GetSize();
+        int* origin = this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->GetOrigin();
+        hardSel->SetArea(origin[0], origin[1], origin[0]+wsize[0]-1, origin[1]+wsize[1]-1);
+        hardSel->SetFieldAssociation(vtkDataObject::FIELD_ASSOCIATION_POINTS );
+
+        if (hardSel->CaptureBuffers())
+        {
+
+            vtkSelection* psel = hardSel->GeneratePolygonSelection(
+                        polygonPointsArray->GetPointer(0),
+                        polygonPointsArray->GetNumberOfTuples()*2);
+            hardSel->ClearBuffers();
+
+            vtkSmartPointer<vtkSelection> sel;
+            sel.TakeReference(psel);
+
+            vtkSmartPointer<vtkExtractSelection> extractSelection =
+                    vtkSmartPointer<vtkExtractSelection>::New();
+
+            extractSelection->SetInputData(this->Points);
+#if VTK_MAJOR_VERSION <= 5
+            extractSelection->SetInput(1, selection);
+#else
+            extractSelection->SetInputData(1, sel);
+#endif
+            extractSelection->Update();
+
+            // In selection
+            vtkSmartPointer<vtkUnstructuredGrid> selected =
+                    vtkSmartPointer<vtkUnstructuredGrid>::New();
+            selected->ShallowCopy(extractSelection->GetOutput());
+
+            std::cout << "There are " << selected->GetNumberOfPoints()
+                      << " points in the selection." << std::endl;
+            std::cout << "There are " << selected->GetNumberOfCells()
+                      << " cells in the selection." << std::endl;
+
+            vtkSmartPointer<vtkGeometryFilter> geometryFilter =  vtkSmartPointer<vtkGeometryFilter>::New();
+#if VTK_MAJOR_VERSION <= 5
+            geometryFilter->SetInput(selected);
+#else
+            geometryFilter->SetInputData(selected);
+#endif
+            geometryFilter->Update();
+            vtkPolyData*  selected_poly = geometryFilter->GetOutput();
+
+#if VTK_MAJOR_VERSION <= 5
+            this->SelectedMapper->SetInput(selected_poly);
+#else
+            this->SelectedMapper->SetInputData(selected_poly);
+#endif
+
+            this->SelectedMapper->ScalarVisibilityOff();
+
+            double r=vtkMath::Random(0.0,1.0);
+            double g=vtkMath::Random(0.0,1.0);
+            double b=vtkMath::Random(0.0,1.0);
+
+            //START
+            std::cout << "Selected " << selected->GetNumberOfPoints() << " points." << std::endl;
+            std::cout << "Selected " << selected->GetNumberOfCells() << " cells." << std::endl;
+            //END
+
+            this->SelectedActor->GetProperty()->SetColor(r, g, b); //(R,G,B)
+            this->SelectedActor->GetProperty()->SetPointSize(3);
+            this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->AddActor(SelectedActor);
+            this->GetInteractor()->GetRenderWindow()->Render();
+            this->HighlightProp(NULL);
+
+            /*
+                if(selected->GetNumberOfPoints()>0)
+                {
+                    this->CurrentRenderer->RemoveActor(vtkwin->selectedActor);
+                    vtkwin->setSelectedActor(SelectedActor);
+                    vtkwin->setVtkInteractorStyle3DFreehand(selected_poly);
+                }
+            */
+        }
+    }
+
+}
+
+void SetPoints(vtkSmartPointer<vtkPolyData> points)
+{
+    this->Points = points;
+    this->Points_ori=points;
+
+
+}
+
+void setVtkWin(vtkwindow_new *w)
+{
+    vtkwin=w;
+
+}
+
+virtual void PrintSelf(std::ostream& os, vtkIndent indent) {}
+
+virtual void PrintHeader(ostream& os, vtkIndent indent){    }
+
+virtual void PrintTrailer(std::ostream& os , vtkIndent indent) {}
+virtual void CollectRevisions(std::ostream& os ) {}
+
+};
+vtkStandardNewMacro(InteractorStyleFreeHandOn3DVisualization);
+
+class InteractorStyleSelctionPointOn3DVisualization : public vtkInteractorStyleRubberBandPick
+{
+private:
+    vtkwindow_new *vtkwin;
+    vtkSmartPointer<vtkPolyData> Points;
+    vtkSmartPointer<vtkPolyData> Points_ori;
+    vtkSmartPointer<vtkActor> SelectedActor;
+    vtkSmartPointer<vtkPolyDataMapper> SelectedMapper;
+
+public:
+    static InteractorStyleSelctionPointOn3DVisualization* New();
+    vtkTypeMacro(InteractorStyleSelctionPointOn3DVisualization,vtkInteractorStyleRubberBandPick);
+
+    InteractorStyleSelctionPointOn3DVisualization()
+    {
+        //this->SelectedMapper = vtkSmartPointer<vtkDataSetMapper>::New();
+        this->SelectedMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+        this->SelectedActor = vtkSmartPointer<vtkActor>::New();
+        this->SelectedActor->SetMapper(SelectedMapper);
+    }
+
+    virtual void OnLeftButtonUp()
+    {
+
+        vtkInteractorStyleRubberBandPick::OnLeftButtonUp();
+
+        // Forward events
+        vtkPlanes* frustum = static_cast<vtkAreaPicker*>(this->GetInteractor()->GetPicker())->GetFrustum();
+
+        vtkSmartPointer<vtkExtractGeometry> extractGeometry = vtkSmartPointer<vtkExtractGeometry>::New();
+        extractGeometry->SetImplicitFunction(frustum);
+
+#if VTK_MAJOR_VERSION <= 5
+        extractGeometry->SetInput(this->Points);
+#else
+        extractGeometry->SetInputData(this->Points);
+#endif
+        extractGeometry->Update();
+
+        vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();
+        glyphFilter->SetInputConnection(extractGeometry->GetOutputPort());
+        glyphFilter->Update();
+
+        vtkPolyData*  selected = glyphFilter->GetOutput();
+
+#if VTK_MAJOR_VERSION <= 5
+        this->SelectedMapper->SetInput(selected);
+#else
+        this->SelectedMapper->SetInputData(selected);
+#endif
+
+        this->SelectedMapper->ScalarVisibilityOff();
+
+        double r=vtkMath::Random(0.0,1.0);
+        double g=vtkMath::Random(0.0,1.0);
+        double b=vtkMath::Random(0.0,1.0);
+
+        //START
+        std::cout << "Selected " << selected->GetNumberOfPoints() << " points." << std::endl;
+        std::cout << "Selected " << selected->GetNumberOfCells() << " cells." << std::endl;
+/*
+            vtkIdTypeArray* ids = vtkIdTypeArray::SafeDownCast(selected->GetPointData()->GetArray("ids"));
+            for(vtkIdType i = 0; i < ids->GetNumberOfTuples(); i++)
+              {
+              std::cout << "Id " << i << " : " << ids->GetValue(i) << std::endl;
+              }
+*/
+        //END
+
+        this->SelectedActor->GetProperty()->SetColor(r, g, b); //(R,G,B)
+        this->SelectedActor->GetProperty()->SetPointSize(3);
+        this->CurrentRenderer->AddActor(SelectedActor);
+        // this->GetInteractor()->GetRenderWindow()->Render();
+        this->HighlightProp(NULL);
+
+        if(selected->GetNumberOfPoints()>0){
+
+            this->CurrentRenderer->RemoveActor(vtkwin->selectedActor);
+            this->GetInteractor()->GetRenderWindow()->Render();
+            vtkwin->setSelectedActor(SelectedActor);
+            vtkwin->setVtkInteractorStyle3DPicker(selected);
+
+
+        }
+        //  this->GetInteractor()->GetRenderWindow()->Render();
+
+
+        /*
+        // Visualize
+        vtkSmartPointer<vtkRenderer> renderer =
+          vtkSmartPointer<vtkRenderer>::New();
+        vtkSmartPointer<vtkRenderWindow> renderWindow =
+          vtkSmartPointer<vtkRenderWindow>::New();
+        renderWindow->AddRenderer(renderer);
+        vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
+          vtkSmartPointer<vtkRenderWindowInteractor>::New();
+        renderWindowInteractor->SetRenderWindow(renderWindow);
+        renderer->AddActor(SelectedActor);
+        renderWindow->Render();
+        renderWindowInteractor->Start();
+*/
+    }
+
+    void SetPoints(vtkSmartPointer<vtkPolyData> points)
+    {
+        this->Points = points;
+        this->Points_ori=points;
+
+    }
+
+    void setVtkWin(vtkwindow_new *w)
+    {
+        vtkwin=w;
+
+    }
+
+    virtual void PrintSelf(std::ostream& os, vtkIndent indent) {}
+
+    virtual void PrintHeader(ostream& os, vtkIndent indent){    }
+
+    virtual void PrintTrailer(std::ostream& os , vtkIndent indent) {}
+    virtual void CollectRevisions(std::ostream& os ) {}
+
+};
+vtkStandardNewMacro(InteractorStyleSelctionPointOn3DVisualization);
+
+class MyRubberBand : public vtkInteractorStyleRubberBand2D
+{
+private:
+    vtkwindow_new *vtkwin;
+public:
+    static MyRubberBand* New();
+    vtkTypeMacro(MyRubberBand, vtkInteractorStyleRubberBand2D);
+
+
+    void setVtkWin(vtkwindow_new *w)
+    {
+        vtkwin=w;
+
+    }
+
+    virtual void OnMouseMove()
+    {
+
+        // Forward events
+        vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
+        coordinate->SetCoordinateSystemToDisplay();
+        coordinate->SetValue(this->GetInteractor()->GetEventPosition()[0],this->GetInteractor()->GetEventPosition()[1],0);
+
+        double* world_coord = coordinate->GetComputedWorldValue(this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+        double *sky_coord = new double[2];
+        double *sky_coord_gal = new double[2];
+        double *sky_coord_fk5 = new double[2];
+
+        QString statusBarText="";
+        float* pixel;
+        pixel=static_cast< float*>(vtkwin->getFitsImage()->GetOutput()->GetScalarPointer(world_coord[0],world_coord[1],0));
+
+        statusBarText = "<value> ";
+        if(pixel!=NULL)
+            statusBarText +=QString::number(pixel[0]);
+        else
+            statusBarText+="NaN";
+
+        statusBarText+= " <image> X: "+QString::number(world_coord[0])+" Y: "+QString::number(world_coord[1]);
+
+        //WCS_GALACTIC = 3
+        AstroUtils().xy2sky(vtkwin->filenameWithPath,world_coord[0],world_coord[1],sky_coord_gal,3);
+        statusBarText+=" <galactic> GLON: "+QString::number(sky_coord_gal[0])+" GLAT: "+QString::number(sky_coord_gal[1]);
+
+        //WCS_J2000 = 1
+        AstroUtils().xy2sky(vtkwin->filenameWithPath,world_coord[0],world_coord[1],sky_coord_fk5,1);
+        statusBarText+=" <fk5> RA: "+QString::number(sky_coord_fk5[0])+" DEC: "+QString::number(sky_coord_fk5[1]);
+        AstroUtils().xy2sky(vtkwin->filenameWithPath,world_coord[0],world_coord[1],sky_coord);
+        statusBarText+=" <ecliptic> RA: "+QString::number(sky_coord[0])+" DEC: "+QString::number(sky_coord[1]);
+
+
+
+        vtkwin->ui->statusbar->showMessage(statusBarText);
+        vtkInteractorStyleRubberBand2D::OnMouseMove();
+
+    }
+
+
+    virtual void OnLeftButtonUp()
+    {
+
+        // Forward events
+        vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
+        coordinate->SetCoordinateSystemToDisplay();
+        coordinate->SetValue(this->StartPosition[0],this->StartPosition[1],0);
+
+        double* world_start = coordinate->GetComputedWorldValue(this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+        vtkSmartPointer<vtkCoordinate> coordinate_end = vtkSmartPointer<vtkCoordinate>::New();
+        coordinate_end->SetCoordinateSystemToDisplay();
+        coordinate_end->SetValue(this->EndPosition[0],this->EndPosition[1],0);
+
+        double* world_end = coordinate_end->GetComputedWorldValue(this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+
+        double w= world_end[0] - world_start[0];
+        double h= world_end[1] - world_start[1] ;
+        vtkRectf *rect=new vtkRectf(world_start[0],world_start[1], w, h);
+
+
+
+
+        HigalSelectedSources *selectedSources = new HigalSelectedSources(vtkwin);
+
+
+        QHash<QString,  vtkSmartPointer<vtkLODActor> >::iterator i;
+        QHash<QString,  vtkSmartPointer<vtkLODActor> >tmp=vtkwin->getEllipseActorList();
+
+        QHash<QString,QListWidget *> listWidget_list;
+
+
+        for (i = tmp.begin(); i != tmp.end(); ++i)
+        {
+
+            QListWidget *tmpWidget=new QListWidget();
+            tmpWidget->setSelectionMode(QAbstractItemView::ExtendedSelection );
+            listWidget_list.insert(i.key(), tmpWidget);
+            selectedSources->ui->tabWidget->addTab( tmpWidget , i.key().split("_").at(0));
+            selectedSources->setConnect(tmpWidget);
+        }
+
+
+        bool empty=true;
+        vtkEllipse *el;
+
+//quifv
+        foreach( el, vtkwin->getEllipseList() )
+            // foreach( el, vtkwin->getFtEllipseList() )
+        {
+
+            if ( el->isInsideRect(rect))
+            {
+
+                qDebug()<<el->getSourceName();
+                QListWidgetItem *newItem = new QListWidgetItem;
+                newItem->setText(el->getSourceName());
+                QString name=vtkwin->getDesignation2fileMap().value(el->getSourceName());
+
+                int row = listWidget_list.value(name)->row(listWidget_list.value(name)->currentItem());
+
+                listWidget_list.value(name)->insertItem(row, newItem);
+                empty=false;
+            }
+
+        }
+
+
+        //rimuovo i tab vuoti
+        QHash<QString,QListWidget *>::iterator it;
+
+        int index;
+
+        for (it = listWidget_list.begin(); it != listWidget_list.end(); ++it)
+        {
+            if (it.value()->count()==0)
+            {
+                index=selectedSources->ui->tabWidget->indexOf(it.value());
+                selectedSources->ui->tabWidget->removeTab(index);
+            }
+
+
+        }
+
+        if(empty)
+            delete selectedSources;
+        else
+            selectedSources -> show();
+
+
+        vtkInteractorStyleRubberBand2D::OnLeftButtonUp();
+        vtkwin->setVtkInteractorStyleImage();
+
+    }
+
+
+
+
+    virtual void PrintSelf(std::ostream& os, vtkIndent indent) {}
+    virtual void PrintHeader(ostream& os, vtkIndent indent){}
+    virtual void PrintTrailer(std::ostream& os , vtkIndent indent) {}
+    virtual void CollectRevisions(std::ostream& os ) {}
+
+};
+vtkStandardNewMacro(MyRubberBand);
+
+class myVtkInteractorContourWindow : public vtkInteractorStyleImage
+{
+private:
+    vtkwindow_new *vtkwin;
+
+
+public:
+    static myVtkInteractorContourWindow* New();
+    double * startPosition;
+    double * endPosition;
+    vtkSmartPointer<vtkActor> lineActor;
+
+
+    void setVtkWin(vtkwindow_new *w)
+    {
+        vtkwin=w;
+        startPosition=new double[3];
+        endPosition=new double[3];
+    }
+
+
+    virtual void OnMouseMove()
+    {
+
+
+    }
+
+    virtual void OnLeftButtonDown()
+    {
+
+        vtkwin->removeActor(lineActor);
+        startPosition[0]=this->Interactor->GetEventPosition()[0];
+        startPosition[1]=this->Interactor->GetEventPosition()[1];
+
+
+
+    }
+
+    virtual void OnLeftButtonUp()
+    {
+
+
+        endPosition[0]=this->Interactor->GetEventPosition()[0];
+        endPosition[1]=this->Interactor->GetEventPosition()[1];
+
+
+        // Forward events
+        vtkSmartPointer<vtkCoordinate> coordinate_start = vtkSmartPointer<vtkCoordinate>::New();
+        coordinate_start->SetCoordinateSystemToDisplay();
+        coordinate_start->SetValue(this->startPosition[0],this->startPosition[1],0);
+
+        double* world_start = coordinate_start->GetComputedWorldValue(this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+        vtkSmartPointer<vtkCoordinate> coordinate_end = vtkSmartPointer<vtkCoordinate>::New();
+        coordinate_end->SetCoordinateSystemToDisplay();
+        coordinate_end->SetValue(this->endPosition[0],this->endPosition[1],0);
+
+        double* world_end = coordinate_end->GetComputedWorldValue(this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+        vtkSmartPointer<vtkLineSource> lineSource =
+                vtkSmartPointer<vtkLineSource>::New();
+
+
+        double deltaX=abs(world_end[0]-world_start[0]);
+        double deltaY=abs(world_end[1]-world_start[1]);
+        int resolution;
+
+        double lineLenght=qSqrt(deltaX*deltaX*+deltaY*deltaY);
+        if(deltaX>deltaY)  resolution=(int) deltaX;
+        else resolution=(int) deltaY;
+        //int resolution=(int) lineLenght;
+
+
+
+
+        lineSource->SetResolution(resolution);
+        lineSource->SetPoint1(world_start);
+        lineSource->SetPoint2(world_end);
+        lineSource->Update();
+
+
+        vtkSmartPointer<vtkPoints> points= vtkSmartPointer<vtkPoints>::New();
+        //        points=lineSource->GetPoints();
+
+        vtkPolyData* polydata = lineSource->GetOutput();
+        // Write all of the coordinates of the points in the vtkPolyData to the console.
+        for(vtkIdType i = 0; i < polydata->GetNumberOfPoints(); i++)
+        {
+            double p[3];
+            polydata->GetPoint(i,p);
+            // This is identical to:
+            // polydata->GetPoints()->GetPoint(i,p);
+            std::cout << "Point " << i << " : (" << p[0] << " " << p[1] << " " << p[2] << ")" << std::endl;
+        }
+
+
+        //  double *point=vtkwin->lineSource->GetPoint1();
+        //  qDebug()<<"point: "<<point[0]<<point[1];
+
+        //vtkIdType num= points->GetNumberOfPoints();
+
+
+
+
+        /* //LINE WIDGET
+         vtkSmartPointer<vtkLineWidget2> lineWidget =
+         vtkSmartPointer<vtkLineWidget2>::New();
+
+         //lineWidget->SetInteractor(vtkwin->ui->qVTK1->GetRenderWindow()->GetInteractor());
+         lineWidget->SetInteractor(this->Interactor);
+
+
+
+         //lineWidget->CreateDefaultRepresentation();
+
+
+         vtkSmartPointer<vtkLineRepresentation> lineRepresentation =vtkSmartPointer<vtkLineRepresentation>::New();
+         lineRepresentation->SetPoint1WorldPosition(world_start);
+         lineRepresentation->SetPoint2WorldPosition(world_end);
+         lineRepresentation->SetLineColor(102, 0,102);
+
+
+         lineWidget->SetRepresentation(lineRepresentation);
+
+         qDebug()<<"lineWidget world 0"<<static_cast <vtkLineRepresentation *>(lineWidget->GetRepresentation())->GetPoint1WorldPosition()[0];
+         qDebug()<<"lineWidget world 1"<<static_cast <vtkLineRepresentation *>(lineWidget->GetRepresentation())->GetPoint1WorldPosition()[1];
+
+
+         qDebug()<<"world_start 0"<<world_start[0];
+         qDebug()<<"world_start 1"<<world_start[1];
+
+
+
+
+         vtkwin->ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->Render();
+         lineWidget->On();
+         */
+
+
+
+        //        // Get the actual box coordinates of the line
+        //             vtkSmartPointer<vtkPolyData> polydata =
+        //                 vtkSmartPointer<vtkPolyData>::New();
+        //             static_cast<vtkLineRepresentation*>(lineWidget->GetRepresentation())->GetPolyData (polydata);
+
+        //             //Visualize
+        //             vtkSmartPointer<vtkPolyDataMapper> mapper =
+        //                            vtkSmartPointer<vtkPolyDataMapper>::New();
+        //                    //mapper->SetInputConnection(lineWidget->GetOutputPort());
+        //             mapper->SetInput(polydata);
+
+        //                    vtkSmartPointer<vtkActor> actor =
+        //                            vtkSmartPointer<vtkActor>::New();
+        //                    actor->SetMapper(mapper);
+        //                    actor->GetProperty()->SetLineWidth(2);
+        //                    actor->GetProperty()->SetColor(51, 0,102);
+        //                    vtkwin->addActor(actor);
+
+        // Visualize
+        vtkSmartPointer<vtkPolyDataMapper> mapper =
+                vtkSmartPointer<vtkPolyDataMapper>::New();
+        mapper->SetInputConnection(lineSource->GetOutputPort());
+
+        lineActor=vtkSmartPointer<vtkActor>::New();
+        lineActor->SetMapper(mapper);
+        lineActor->GetProperty()->SetLineWidth(1);
+        lineActor->GetProperty()->SetColor(102,0,102);
+        vtkwin->addActor(lineActor);
+        vtkwin->ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->Render();
+
+
+
+    }
+
+    virtual void OnChar()
+    {
+    }
+
+    virtual void PrintSelf(std::ostream& os, vtkIndent indent) {}
+    virtual void PrintHeader(ostream& os, vtkIndent indent){    }
+    virtual void PrintTrailer(std::ostream& os , vtkIndent indent) {}
+    virtual void CollectRevisions(std::ostream& os ) {}
+
+};
+vtkStandardNewMacro(myVtkInteractorContourWindow);
+
+class myVtkInteractorStyleImage : public vtkInteractorStyleImage
+{
+private:
+    vtkwindow_new *vtkwin;
+    bool isSlice=false;
+
+public:
+    static myVtkInteractorStyleImage* New();
+
+    void setVtkWin(vtkwindow_new *w)
+    {
+        vtkwin=w;
+    }
+
+    void setIsSlice()
+    {
+        isSlice=true;
+    }
+
+    virtual void OnMouseMove()
+    {
+
+        // Forward events
+        vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
+        coordinate->SetCoordinateSystemToDisplay();
+        coordinate->SetValue(this->GetInteractor()->GetEventPosition()[0],this->GetInteractor()->GetEventPosition()[1],0);
+
+        double* world_coord = coordinate->GetComputedWorldValue(this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+        double *sky_coord = new double[2];
+        double *sky_coord_gal = new double[2];
+        double *sky_coord_fk5 = new double[2];
+
+        QString statusBarText="";
+        vtkSmartPointer<vtkFitsReader> fits;
+        if(!isSlice)
+        {
+            fits= vtkwin->getLayerListImages().at(0)->getFits();
+
+            if (vtkwin->ui->listWidget->selectionModel()->selectedRows().count()!=0 && vtkwin->getLayerListImages().at(vtkwin->ui->listWidget->selectionModel()->selectedRows().at(0).row())->getType()==0 )
+            {
+                fits=vtkwin->getLayerListImages().at(vtkwin->ui->listWidget->selectionModel()->selectedRows().at(0).row())->getFits();
+            }
+        }
+        else
+        {
+            fits=vtkwin->getFitsImage();
+        }
+
+        float* pixel;
+        if(!isSlice)
+            pixel=static_cast< float*>(fits->GetOutput()->GetScalarPointer(world_coord[0],world_coord[1],0));
+        else
+            pixel=static_cast< float*>(fits->GetOutput()->GetScalarPointer(world_coord[0],world_coord[1],vtkwin->viewer->GetSlice()));
+
+        statusBarText = "<value> ";
+        if(pixel!=NULL)
+            statusBarText +=QString::number(pixel[0]);
+        else
+            statusBarText+="NaN";
+
+        statusBarText+= " <image> X: "+QString::number(world_coord[0])+" Y: "+QString::number(world_coord[1]);
+
+        //WCS_GALACTIC = 3
+        AstroUtils().xy2sky(vtkwin->filenameWithPath,world_coord[0],world_coord[1],sky_coord_gal,3);
+
+
+        statusBarText+=" <galactic> GLON: "+QString::number(sky_coord_gal[0])+" GLAT: "+QString::number(sky_coord_gal[1]);
+
+        //WCS_J2000 = 1
+        AstroUtils().xy2sky(vtkwin->filenameWithPath,world_coord[0],world_coord[1],sky_coord_fk5,1);
+
+        statusBarText+=" <fk5> RA: "+QString::number(sky_coord_fk5[0])+" DEC: "+QString::number(sky_coord_fk5[1]);
+
+        AstroUtils().xy2sky(vtkwin->filenameWithPath,world_coord[0],world_coord[1],sky_coord);
+        statusBarText+=" <ecliptic> RA: "+QString::number(sky_coord[0])+" DEC: "+QString::number(sky_coord[1]);
+
+
+
+        vtkwin->ui->statusbar->showMessage(statusBarText);
+
+        /*
+
+        vtkSmartPointer<vtkImageActorPointPlacer> pointPlacer = vtkSmartPointer<vtkImageActorPointPlacer>::New();
+        if (!isSlice)
+            pointPlacer->SetImageActor(vtkwin->imageViewer->GetImageActor());
+        else
+            pointPlacer->SetImageActor(vtkwin->viewer->GetImageActor());
+
+
+        QString statusBarText="";
+
+        if(pointPlacer->ValidateWorldPosition(world_coord)==1)
+        {
+
+            float* pixel;
+            if(!isSlice)
+                pixel=static_cast< float*>(vtkwin->getFitsImage()->GetOutput()->GetScalarPointer(world_coord[0],world_coord[1],0));
+            else
+                pixel=static_cast< float*>(vtkwin->getFitsImage()->GetOutput()->GetScalarPointer(world_coord[0],world_coord[1],vtkwin->viewer->GetSlice()));
+
+
+            statusBarText = "<value> "+QString::number(pixel[0]);
+            statusBarText+= " <image> X: "+QString::number(world_coord[0])+" Y: "+QString::number(world_coord[1]);
+
+            //WCS_GALACTIC = 3
+            //  AstroUtils().xy2sky(vtkwin->getFilenameWithPath(),world_coord[0],world_coord[1],sky_coord_gal,3);
+            AstroUtils().xy2sky(vtkwin->filenameWithPath,world_coord[0],world_coord[1],sky_coord_gal,3);
+
+
+            statusBarText+=" <galactic> GLON: "+QString::number(sky_coord_gal[0])+" GLAT: "+QString::number(sky_coord_gal[1]);
+
+            //WCS_J2000 = 1
+            AstroUtils().xy2sky(vtkwin->filenameWithPath,world_coord[0],world_coord[1],sky_coord_fk5,1);
+            //  AstroUtils().xy2sky(vtkwin->getFilenameWithPath(),world_coord[0],world_coord[1],sky_coord_fk5,1);
+
+            statusBarText+=" <fk5> RA: "+QString::number(sky_coord_fk5[0])+" DEC: "+QString::number(sky_coord_fk5[1]);
+
+
+            //  AstroUtils().xy2sky(vtkwin->getFilenameWithPath(),world_coord[0],world_coord[1],sky_coord);
+            AstroUtils().xy2sky(vtkwin->filenameWithPath,world_coord[0],world_coord[1],sky_coord);
+            statusBarText+=" <ecliptic> RA: "+QString::number(sky_coord[0])+" DEC: "+QString::number(sky_coord[1]);
+
+        }
+        else
+        {
+            statusBarText="";
+        }
+
+        vtkwin->ui->statusbar->showMessage(statusBarText);
+ */
+        vtkInteractorStyleImage::OnMouseMove();
+
+    }
+
+    virtual void OnChar()
+    {
+    }
+
+    virtual void PrintSelf(std::ostream& os, vtkIndent indent) {}
+    virtual void PrintHeader(ostream& os, vtkIndent indent){    }
+    virtual void PrintTrailer(std::ostream& os , vtkIndent indent) {}
+    virtual void CollectRevisions(std::ostream& os ) {}
+
+
+
+};
+vtkStandardNewMacro(myVtkInteractorStyleImage);
+
+class SkyRegionSelector : public vtkInteractorStyleRubberBand2D
+{
+private:
+    vtkwindow_new *vtkwin;
+    bool is3D, isFilament,is3dSelections,isBubble;
+public:
+    static SkyRegionSelector* New();
+    vtkTypeMacro(SkyRegionSelector, vtkInteractorStyleRubberBand2D);
+
+
+    SkyRegionSelector()
+    {
+        is3D=false;
+        isFilament=false;
+        is3dSelections=false;
+        isBubble=false;
+    }
+
+    void setIsFilament(){
+        isFilament=true;
+    }
+
+    void setIsBubble(){
+        isBubble=true;
+    }
+
+
+    void setIs3dSelections()
+    {
+        is3dSelections=true;
+    }
+
+    void setIs3D(){
+        is3D=true;
+    }
+
+    void setVtkWin(vtkwindow_new *w)
+    {
+        vtkwin=w;
+    }
+
+    vtkwindow_new* getVtkWin()
+    {
+        return vtkwin;
+    }
+
+    virtual void OnLeftButtonUp()
+    {
+
+        // Forward events
+        vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
+        coordinate->SetCoordinateSystemToDisplay();
+        coordinate->SetValue(this->StartPosition[0],this->StartPosition[1],0);
+
+        double* world_start = coordinate->GetComputedWorldValue(this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+        vtkSmartPointer<vtkCoordinate> coordinate_end = vtkSmartPointer<vtkCoordinate>::New();
+        coordinate_end->SetCoordinateSystemToDisplay();
+        coordinate_end->SetValue(this->EndPosition[0],this->EndPosition[1],0);
+
+        double* world_end = coordinate_end->GetComputedWorldValue(this->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+        double *coor_start = new double[2];
+        double *coor_end = new double[2];
+
+        AstroUtils().xy2sky( vtkwin->filenameWithPath,world_start[0],world_start[1],coor_start,3 );
+        AstroUtils().xy2sky( vtkwin->filenameWithPath,world_end[0],world_end[1],coor_end,3 );
+
+        vtkInteractorStyleRubberBand2D::OnLeftButtonUp();
+        vtkwin->ui->rectangularSelectionCS->setStyleSheet("");
+        vtkwin->ui->fil_rectPushButton->setStyleSheet("");
+        vtkwin->ui->tdRectPushButton->setStyleSheet("");
+        vtkwin->ui->bubblePushButton->setStyleSheet("");
+
+
+        if(!(vtkwin->isDatacube))
+        {
+
+            VLKBSimpleQueryComposer *skyregionquery = new VLKBSimpleQueryComposer(vtkwin);
+
+            if(isFilament)
+            {
+                skyregionquery->setIsFilament();
+            }
+            else if(is3dSelections)
+            {
+                skyregionquery->setIs3dSelections();
+            }
+            else if(isBubble)
+            {
+                skyregionquery->setIsBubble();
+            }
+
+
+
+            skyregionquery->setLongitude(coor_start[0],coor_end[0]);
+            skyregionquery->setLatitude(coor_start[1],coor_end[1]);
+            skyregionquery->show();
+
+
+            vtkwin->setVtkInteractorStyleImage();
+        }
+        else {
+
+            dbquery *queryWindow=new dbquery();
+            QString glong,glat;
+
+            glong=QString::number(coor_end[0]+(coor_start[0]-coor_end[0])/2);
+            glat=QString::number(coor_end[1]+(coor_start[1]-coor_end[1])/2);
+
+            double width= coor_start[0]-coor_end[0];
+            double lenght= coor_start[1]-coor_end[1];
+            vtkRect<double> *rect=new vtkRect<double>(coor_start[0], coor_start[1], width,lenght);
+
+            queryWindow->setCoordinate(glong,glat);
+            queryWindow->show();
+        }
+
+    }
+
+    virtual void PrintSelf(std::ostream& os, vtkIndent indent) {}
+    virtual void PrintHeader(ostream& os, vtkIndent indent){    }
+    virtual void PrintTrailer(std::ostream& os , vtkIndent indent) {}
+    virtual void CollectRevisions(std::ostream& os ) {}
+};
+vtkStandardNewMacro(SkyRegionSelector);
+
+
+vtkwindow_new::~vtkwindow_new()
+{
+    delete ui;
+}
+
+vtkwindow_new::vtkwindow_new(QWidget *parent, VisPoint * vis) : QMainWindow(parent),ui(new Ui::vtkwindow_new)
+{
+
+    ui->setupUi(this);
+#ifdef Q_OS_OSX
+    disableGLHiDPI(ui->qVTK1->winId());
+#endif
+
+
+    stringDictWidget = &Singleton<VialacteaStringDictWidget>::Instance();
+
+    ui->ElementListWidget->hide();
+    ui->tableWidget->hide();
+    ui->listWidget->hide();
+    ui->toolsGroupBox->hide();
+    ui->datacubeGroupBox->hide();
+    ui->tdGroupBox->hide();
+    ui->filamentsGroupBox->hide();
+    ui->bubbleGroupBox->hide();
+    ui->compactSourcesGroupBox->hide();
+    ui->PVPlotPushButton->hide();
+    ui->PVPlot_radioButton->hide();
+    ui->label_survey->hide();
+    ui->lineEdit_survey->hide();
+    ui->label_species->hide();
+    ui->lineEdit_species->hide();
+    ui->label_transition->hide();
+    ui->lineEdit_transition->hide();
+    ui->contourGroupBox->hide();
+    ui->ThresholdGroupBox->hide();
+    ui->cuttingPlaneGroupBox->hide();
+    ui->spinBox_channels->hide();
+    ui->label_channels->hide();
+    ui->spinBox_cuttingPlane->hide();
+    ui->label_channels->hide();
+    ui->selectionGroupBox->hide();
+    ui->isocontourVtkWin->hide();
+    ui->valueGroupBox->hide();
+    ui->actionTools->setVisible(true);
+
+    //ui->glyphGroupBox->hide();
+    //ui->glyphScalarComboBox->hide();
+
+
+    fitsViewer = false;
+    m_Ren1 = vtkRenderer::New();
+    renwin = vtkRenderWindow::New();
+    renwin->AddRenderer(m_Ren1);
+    renwin->SetInteractor(ui->qVTK1->GetInteractor());
+    ui->qVTK1->SetRenderWindow(renwin);
+
+    m_Ren1->GlobalWarningDisplayOff();
+    loadObservedObject(vis);
+
+    //add lut on main window FV
+    for (unsigned int i =0; i < vis->getOrigin()->getNumberOfColumns();i++)
+    {
+        QString field =  QString(vis->getOrigin()->getColName(i).c_str());
+        ui->scalarComboBox->addItem(field);
+        ui->glyphScalarComboBox->addItem(field);
+    }
+    //end add lut on main window FV
+
+
+    vtkAxes = vtkSmartPointer<vtkAxesActor>::New();
+    vtkAxesWidget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
+    vtkAxesWidget->SetInteractor(ui->qVTK1->GetRenderWindow()->GetInteractor());
+
+    vtkAxesWidget->SetOrientationMarker(vtkAxes);
+
+    vtkAxesWidget->SetOutlineColor( 0.9300, 0.5700, 0.1300 );
+    vtkAxesWidget->SetViewport( 0.0, 0.0, 0.2, 0.2 );
+    vtkAxesWidget->SetEnabled(1);
+    vtkAxesWidget->InteractiveOff();
+
+    update();
+
+    pp->getRenderer()->GetActiveCamera( )->GetPosition(cam_init_pos);
+    pp->getRenderer()->GetActiveCamera( )->GetFocalPoint(cam_init_foc);
+
+    scaleActivate=true;
+    isDatacube=false;
+
+}
+
+
+vtkwindow_new::vtkwindow_new(QWidget *parent, vtkSmartPointer<vtkFitsReader> vis, int b, vtkwindow_new *p) : QMainWindow(parent),ui(new Ui::vtkwindow_new)
+{
+    QSettings settings(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini"), QSettings::NativeFormat);
+    vlkbUrl= settings.value("vlkburl", "").toString();
+    stringDictWidget = &Singleton<VialacteaStringDictWidget>::Instance();
+
+    myfits=vis;
+    filenameWithPath =  vis->GetFileName();
+    myParentVtkWindow=p;
+    vtkwintype=b;
+
+    imageObject=new vtkfitstoolwidgetobject(0);
+    imageObject->setName(QString::fromUtf8(myfits->GetFileName().c_str()));
+    imageObject->setFitsReader(myfits);
+    //setto specie e transition
+    imageObject->setSpecies(vis->getSpecies());
+    imageObject->setSurvey(vis->getSurvey().replace("%20"," "));
+    imageObject->setTransition(vis->getTransition());
+
+
+    selected_scale="Log";
+
+
+
+    switch (b) {
+    case 0:
+    {
+        ui->setupUi(this);
+
+        this->setWindowTitle(myfits->GetFileName().c_str());
+        ui->cameraControlgroupBox->hide();
+
+        ui->selectionGroupBox->hide();
+        ui->ThresholdGroupBox->hide();
+        ui->valueGroupBox->hide();
+
+        ui->cuttingPlaneGroupBox->hide();
+
+        ui->spinBox_cuttingPlane->hide();
+        ui->spinBox_channels->hide();
+        ui->label_channels->hide();
+
+        ui->label_survey->hide();
+        ui->label_species->hide();
+        ui->label_transition->hide();
+        ui->lineEdit_survey->hide();
+        ui->lineEdit_species->hide();
+        ui->lineEdit_transition->hide();
+        ui->contourGroupBox->hide();
+        ui->PVPlotPushButton->hide();
+        ui->PVPlot_radioButton->hide();
+        ui->datacubeGroupBox->hide();
+        ui->lut3dGroupBox->hide();
+        ui->glyphGroupBox->hide();
+
+        ui->filterGroupBox->hide();
+
+       ui->bubbleGroupBox->hide();
+
+
+        ui->ElementListWidget->installEventFilter(this);
+
+        this->isDatacube=false;
+
+
+        ui->isocontourVtkWin->hide();
+        ui->ElementListWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
+        ui->tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
+        ui->tableWidget->setSelectionMode( QAbstractItemView::SingleSelection );
+
+        ui->listWidget->setDragDropMode(QAbstractItemView::InternalMove );
+        connect(ui->listWidget->model(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(movedLayersRow(QModelIndex,int,int,QModelIndex,int)) );
+
+
+
+#ifdef Q_OS_OSX
+        disableGLHiDPI(ui->qVTK1->winId());
+#endif
+
+        m_Ren1 = vtkRenderer::New();
+        renwin = vtkRenderWindow::New();
+        renwin->AddRenderer(m_Ren1);
+        ui->qVTK1->SetRenderWindow(renwin);
+
+        m_Ren1->GlobalWarningDisplayOff();
+        m_Ren1->SetBackground(0.21,0.23,0.25);
+
+
+        QAction* select = new QAction("Select",this);
+        select->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+
+        connect(select,SIGNAL(triggered()), this,SLOT(setSelectionFitsViewerInteractorStyle()));
+        ui->menuWindow->addAction(select);
+
+        QMenu *compact = ui->menuFile->addMenu("Add compact sources");
+
+        QAction* local = new QAction("Local",this);
+        local->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L));
+        connect(local,SIGNAL(triggered()), this,SLOT(addLocalSources()));
+
+        QAction* remote = new QAction("Remote",this);
+        remote->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
+        connect(remote,SIGNAL(triggered()), this,SLOT(setSkyRegionSelectorInteractorStyle()));
+
+        QAction* normal_selector = new QAction("Normal",this);
+        normal_selector->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N));
+        connect(normal_selector,SIGNAL(triggered()), this,SLOT(setVtkInteractorStyleImage()));
+
+        QAction* selector_3D = new QAction("3D",this);
+        selector_3D->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_3));
+        connect(selector_3D,SIGNAL(triggered()), this,SLOT(setSkyRegionSelectorInteractorStyleFor3D()));
+
+        compact->addAction(local);
+        compact->addAction(remote);
+        compact->addAction(selector_3D);
+        compact->addAction(normal_selector);
+        /*
+        ui->qVTK1->setContextMenuPolicy(Qt::CustomContextMenu);
+        this->Connections = vtkSmartPointer<vtkEventQtSlotConnect>::New();
+        this->Connections->Connect( ui->qVTK1->GetRenderWindow()->GetInteractor(), vtkCommand::RightButtonPressEvent ,this,SLOT(slot_clicked(vtkObject*, unsigned long, void*, void*)));
+*/
+
+
+        setVtkInteractorStyleImage();
+
+        double* range = vis->GetOutput()->GetScalarRange();
+
+        //   qDebug()<<" r: "<<range[0]<<" .. "<<range[1];
+        vtkSmartPointer<vtkImageShiftScale> resultScale = vtkSmartPointer<vtkImageShiftScale>::New();
+        resultScale->SetOutputScalarTypeToUnsignedChar();
+        resultScale->SetInputData( vis->GetOutput() );
+
+        resultScale->Update();
+
+        vtkSmartPointer<vtkLookupTable> lut = vtkSmartPointer<vtkLookupTable>::New();
+        lut->SetScaleToLog10();
+        min=myfits->GetMin();
+        if ( min <= 0 )
+            min=1;
+        lut->SetTableRange( min, myfits->GetMax() );
+
+        SelectLookTable("Gray",lut);
+        imageObject->setLutScale("Log");
+        imageObject->setLutType("Gray");
+
+
+
+        vtkSmartPointer<vtkImageMapToColors> colors =  vtkSmartPointer<vtkImageMapToColors>::New();
+        colors->SetInputData(vis->GetOutput());
+        colors->SetLookupTable(lut);
+        colors->Update();
+
+        vtkSmartPointer<vtkImageSliceMapper> imageSliceMapperBase = vtkSmartPointer<vtkImageSliceMapper>::New();
+
+        imageSliceMapperBase->SetInputData(colors->GetOutput());
+        //  imageSliceMapperBase->SetInputData(vis->GetOutput());
+
+        vtkSmartPointer<vtkImageSlice> imageSliceBase = vtkSmartPointer<vtkImageSlice>::New();
+        imageSliceBase->SetMapper(imageSliceMapperBase);
+        imageSliceBase->GetProperty()->SetInterpolationTypeToNearest();
+
+        imageSliceBase->GetProperty()->SetLayerNumber(0);
+
+
+        // Stack
+        imageStack = vtkSmartPointer<vtkImageStack>::New();
+        imageStack->AddImage(imageSliceBase);
+
+
+        vtkSmartPointer<vtkLegendScaleActor> legendScaleActorImage =  vtkSmartPointer<vtkLegendScaleActor>::New();
+        legendScaleActorImage->LegendVisibilityOff();
+        legendScaleActorImage->setFitsFile(myfits);
+
+        m_Ren1->AddActor(legendScaleActorImage);
+        m_Ren1->AddViewProp(imageStack);
+        m_Ren1->ResetCamera();
+
+        addLayer(imageObject);
+
+        ui->listWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+
+
+
+        createInfoWindow();
+        showMaximized();
+        activateWindow();
+
+        break;
+    }
+    case 1:
+    {
+
+
+        vis->CalculateRMS();
+
+        isDatacube=true;
+        ui->setupUi(this);
+
+#ifdef Q_OS_OSX
+        disableGLHiDPI(ui->qVTK1->winId());
+        disableGLHiDPI(ui->isocontourVtkWin->winId());
+#endif
+
+        m_Ren1 = vtkRenderer::New();
+        renwin = vtkRenderWindow::New();
+        renwin->AddRenderer(m_Ren1);
+        ui->qVTK1->SetRenderWindow(renwin);
+
+        m_Ren2 = vtkRenderer::New();
+        renwin2 = vtkRenderWindow::New();
+        renwin2->SetNumberOfLayers(2);
+        renwin2->AddRenderer(m_Ren2);
+
+        m_Ren2->SetBackground(0.21,0.23,0.25);
+
+        m_Ren1->GlobalWarningDisplayOff();
+        m_Ren2->GlobalWarningDisplayOff();
+        ui->isocontourVtkWin->SetRenderWindow(renwin2);
+
+        ui->splitter->hide();
+        ui->ElementListWidget->hide();
+        ui->tableWidget->hide();
+        ui->listWidget->hide();
+        ui->compactSourcesGroupBox->hide();
+        ui->datacubeGroupBox->hide();;
+        ui->toolsGroupBox->hide();
+        ui->filamentsGroupBox->hide();
+        ui->bubbleGroupBox->hide();
+        ui->tdGroupBox->hide();
+        ui->label_survey->hide();
+        ui->label_species->hide();
+        ui->label_transition->hide();
+        ui->lineEdit_survey->hide();
+        ui->lineEdit_species->hide();
+        ui->lineEdit_transition->hide();
+        ui->spinBox_channels->hide();
+        ui->label_channels->hide();
+        ui->PVPlotPushButton->hide();
+        ui->selectionGroupBox->hide();
+        ui->PVPlot_radioButton->hide();
+        ui->lut3dGroupBox->hide();
+        ui->glyphGroupBox->hide();
+        ui->filterGroupBox->hide();
+
+        this->max=vis->GetMax();
+        this->min=vis->GetMin();
+        this->naxis3=vis->GetNaxes(2);
+
+        ui->minLineEdit->setText(QString::number(min,'f',4));
+        ui->maxLineEdit->setText(QString::number(max,'f',4));
+        ui->RmsLineEdit->setText(QString::number(vis->GetRMS(),'f',4));
+        ui->thresholdValueLineEdit->setText( QString::number(3*vis->GetRMS(),'f',4) );
+
+        ui->lowerBoundLineEdit->setText(QString::number(3*vis->GetRMS(),'f',4));
+        ui->upperBoundLineEdit->setText(QString::number(max,'f',4));
+
+
+        // outline
+        vtkOutlineFilter *outlineF = vtkOutlineFilter::New();
+        outlineF->SetInputData(vis->GetOutput());
+
+
+
+        vtkPolyDataMapper *outlineM = vtkPolyDataMapper::New();
+        outlineM->SetInputConnection(outlineF->GetOutputPort());
+        outlineM->ScalarVisibilityOff();
+
+        vtkActor *outlineA = vtkActor::New();
+        outlineA->SetMapper(outlineM);
+
+        // isosurface
+        shellE = vtkMarchingCubes::New();
+        shellE->SetInputData(vis->GetOutput());
+        shellE->ComputeNormalsOn();
+
+        shellE->SetValue(0, 3*vis->GetRMS());
+
+        vtkPolyDataMapper *shellM = vtkPolyDataMapper::New();
+        shellM->SetInputConnection(shellE->GetOutputPort());
+        shellM->ScalarVisibilityOff();
+
+        vtkActor *shellA = vtkActor::New();
+        shellA->SetMapper(shellM);
+        shellA->GetProperty()->SetColor(1.0, 0.5, 1.0);
+
+        vtkPlanes *sliceE= vtkPlanes::New();
+        sliceE->SetBounds(vis->GetOutput()->GetBounds()[0], vis->GetOutput()->GetBounds()[1], vis->GetOutput()->GetBounds()[2], vis->GetOutput()->GetBounds()[3], 0, 1);
+
+        vtkSmartPointer<vtkFrustumSource> frustumSource = vtkSmartPointer<vtkFrustumSource>::New();
+        frustumSource->ShowLinesOff();
+        frustumSource->SetPlanes(sliceE);
+        frustumSource->Update();
+
+        vtkPolyData* frustum = frustumSource->GetOutput();
+
+        vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+#if VTK_MAJOR_VERSION <= 5
+        mapper->SetInput(frustum);
+#else
+        mapper->SetInputData(frustum);
+#endif
+
+        sliceA = vtkActor::New();
+        sliceA->SetMapper(mapper);
+
+        // add actors to renderer
+
+        m_Ren1->AddActor(outlineA);
+        m_Ren1->AddActor(shellA);
+        m_Ren1->AddActor(sliceA);
+
+
+        vtkAxes = vtkSmartPointer<vtkAxesActor>::New();
+        vtkAxes->SetXAxisLabelText("X");
+        vtkAxes->SetYAxisLabelText("Y");
+        vtkAxes->SetZAxisLabelText("Z");
+        vtkAxes->DragableOn();
+
+        vtkSmartPointer<vtkTextProperty> tprop = vtkSmartPointer<vtkTextProperty>::New();
+        tprop->SetFontSize(0);
+        tprop->SetOpacity(0);
+
+        vtkAxes->GetZAxisCaptionActor2D()->GetTextActor()->GetScaledTextProperty();
+        vtkAxes->GetZAxisCaptionActor2D()->GetTextActor()->SetTextScaleMode(0.1);
+
+        vtkAxes->GetXAxisCaptionActor2D()->GetTextActor()->GetScaledTextProperty();
+        vtkAxes->GetXAxisCaptionActor2D()->GetTextActor()->SetTextScaleMode(0.1);
+
+        vtkAxes->GetYAxisCaptionActor2D()->GetTextActor()->GetScaledTextProperty();
+        vtkAxes->GetYAxisCaptionActor2D()->GetTextActor()->SetTextScaleMode(0.1);
+
+
+        vtkAxesWidget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
+        vtkAxesWidget->SetInteractor(ui->qVTK1->GetRenderWindow()->GetInteractor());
+
+        vtkAxesWidget->SetOrientationMarker(vtkAxes);
+
+        vtkAxesWidget->SetOutlineColor( 0.9300, 0.5700, 0.1300 );
+        vtkAxesWidget->SetViewport( 0.0, 0.0, 0.2, 0.2 );
+        vtkAxesWidget->SetEnabled(1);
+        vtkAxesWidget->InteractiveOff();
+
+
+        m_Ren1->GetActiveCamera( )->GetPosition(cam_init_pos);
+        m_Ren1->GetActiveCamera( )->GetFocalPoint(cam_init_foc);
+
+
+        vtkSmartPointer<vtkLegendScaleActor> legendScaleActor3d =  vtkSmartPointer<vtkLegendScaleActor>::New();
+
+        legendScaleActor3d->LegendVisibilityOff();
+        legendScaleActor3d->setFitsFile(myfits);
+
+        m_Ren1->AddActor(legendScaleActor3d);
+        //end coordinate e assi
+
+        //start FV double vtkwin in one window
+
+        // A yellow-to-blue colormap defined by individually setting all values
+        vtkSmartPointer<vtkLookupTable> lutSlice = vtkSmartPointer<vtkLookupTable>::New();
+        lutSlice->SetTableRange( myfits->GetRangeSlice(0)[0], myfits->GetRangeSlice(0)[1] );
+        SelectLookTable("Gray",lutSlice);
+
+        setVtkInteractorStyleImageContour();
+
+
+        viewer  =vtkSmartPointer<vtkResliceImageViewer>::New();
+        viewer->SetInputData(vis->GetOutput());
+        viewer->GetWindowLevel()->SetOutputFormatToRGB();
+        viewer->GetWindowLevel()->SetLookupTable(lutSlice);
+        viewer->GetImageActor()->InterpolateOff();
+
+        viewer->SetRenderer(ui->isocontourVtkWin->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+        viewer->SetRenderWindow(ui->isocontourVtkWin->GetRenderWindow());
+
+        m_Ren2->SetBackground(0.21,0.23,0.25);
+        currentContourActor = vtkSmartPointer<vtkLODActor>::New();
+        currentContourActorForMainWindow = vtkSmartPointer<vtkLODActor>::New();
+
+        ui->cuttingPlane_Slider->setRange(1, vis->GetNaxes(2));
+        ui->spinBox_cuttingPlane->setRange(1,vis->GetNaxes(2));
+        setSliceDatacube(1);
+        setSliceDatacube(0);
+
+        vtkSmartPointer<vtkLegendScaleActor> legendScaleActorImage =  vtkSmartPointer<vtkLegendScaleActor>::New();
+
+        legendScaleActorImage->LegendVisibilityOff();
+        legendScaleActorImage->setFitsFile(myfits);
+
+        m_Ren2->AddActor(legendScaleActorImage);
+
+        //end FV
+
+        this->setWindowName("Datacube visualization");
+        showMaximized();
+        activateWindow();
+
+
+        break;
+    }
+    case 2:
+    {
+        qDebug()<<"case 2";
+
+        vis->CalculateRMS();
+
+        isDatacube=true;
+        vis->is3D=true;
+        vis->GetOutput();
+        ui->setupUi(this);
+        this->setWindowName("Datacubes slices visualization");
+        contourWin = new contour();
+
+        m_Ren1 = vtkRenderer::New();
+        m_Ren1->GlobalWarningDisplayOff();
+        renwin = vtkRenderWindow::New();
+        renwin->AddRenderer(m_Ren1);
+        ui->qVTK1->SetRenderWindow(renwin);
+        ui->filamentsGroupBox->hide();
+        ui->bubbleGroupBox->hide();
+
+        ui->compactSourcesGroupBox->hide();
+        ui->datacubeGroupBox->hide();
+        ui->toolsGroupBox->hide();
+        ui->tdGroupBox->hide();
+
+        ui->splitter->hide();
+        ui->ElementListWidget->hide();
+        ui->tableWidget->hide();
+        ui->listWidget->hide();
+        ui->glyphGroupBox->hide();
+
+
+        ui->cameraControlgroupBox->hide();
+        ui->ThresholdGroupBox->hide();
+
+        ui->cuttingPlaneGroupBox->hide();
+        ui->spinBox_cuttingPlane->hide();
+        ui->lineEdit_species->setText(species);
+        ui->lineEdit_survey->setText(survey);
+        ui->lineEdit_transition->setText(transition);
+        ui->lineEdit_species->setEnabled(false);
+        ui->lineEdit_survey->setEnabled(false);
+        ui->lineEdit_transition->setEnabled(false);
+        ui->selectionGroupBox->hide();
+        ui->filterGroupBox->hide();
+
+
+
+        naxis3=vis->GetNaxes(2);
+
+
+        fitsViewer=true;
+        filenameWithPath =  vis->GetFileName();
+
+        //MODIFICHE
+        //commentato: setVtkInteractorStyleImage();
+
+
+        // A yellow-to-blue colormap defined by individually setting all values
+        vtkSmartPointer<vtkLookupTable> lut = vtkSmartPointer<vtkLookupTable>::New();
+        //lut->SetTableRange( 0, 255 );
+        lut->SetScaleToLog10();
+
+        SelectLookTable("Gray",lut);
+
+        double* range = vis->GetOutput()->GetScalarRange();
+
+        // The image viewers and writers are only happy with unsigned char
+        // images.  This will convert the floats into that format.
+        vtkSmartPointer<vtkImageShiftScale> resultScale = vtkSmartPointer<vtkImageShiftScale>::New();
+        resultScale->SetOutputScalarTypeToUnsignedChar();
+        resultScale->SetShift(0);
+        resultScale->SetScale(range[1]-range[0]);
+        resultScale->SetInputData( vis->GetOutput() );
+        //resultScale->SetInputConnection( vis->GetOutputPort() );
+
+        resultScale->Update();
+
+        //commentato qui, provo reslice
+        imageViewer = vtkSmartPointer<vtkImageViewer2>::New();
+        imageViewer->SetInputData(resultScale->GetOutput());
+
+        // Set Color level and window
+        imageViewer->SetColorLevel(0.5* (range[1]+range[0]));
+        imageViewer->SetColorWindow(range[1]-range[0]);
+
+        imageViewer->SetupInteractor(ui->qVTK1->GetRenderWindow()->GetInteractor());
+        imageViewer->GetInteractorStyle()->AutoAdjustCameraClippingRangeOn();
+
+        imageViewer->SetRenderer(m_Ren1);
+        imageViewer->SetRenderWindow(renwin);
+
+        imageViewer->GetWindowLevel()->SetLookupTable(lut);
+
+
+        viewer  =vtkSmartPointer<vtkResliceImageViewer>::New();
+        viewer->SetRenderer(ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+        viewer->SetRenderWindow(ui->qVTK1->GetRenderWindow());
+        viewer->SetupInteractor(ui->qVTK1->GetRenderWindow()->GetInteractor());
+        viewer->SetInputData(vis->GetOutput());
+        viewer->SetSlice(1);
+
+        double *pos=m_Ren1->GetActiveCamera()->GetPosition();
+        cam_init_pos[0]=pos[0];
+        cam_init_pos[1]=pos[1];
+        cam_init_pos[2]=pos[2];
+
+
+
+        vtkImageActor* imageActor = viewer->GetImageActor();
+        m_Ren1->AddActor(imageActor);
+        m_Ren1->SetBackground(0.21,0.23,0.25);
+
+
+        update();
+
+
+        break;
+
+    }
+    default:
+        break;
+
+    }
+
+}
+
+
+void vtkwindow_new::setWindowName(QString name)
+{
+    this->setWindowTitle(name);
+}
+
+QString vtkwindow_new::getWindowName()
+{
+    return this->windowTitle();
+}
+
+void vtkwindow_new::on_horizontalSlider_threshold_sliderReleased()
+{
+    float value=(ui->horizontalSlider_threshold->value()*(myfits->GetMax() - 3*myfits->GetRMS()) /100)+3*myfits->GetRMS();
+
+    ui->thresholdValueLineEdit->setText(QString::number(value,'f',4) );
+    shellE->SetValue(0,  value);
+    ui->qVTK1->update();
+}
+
+
+void vtkwindow_new::on_cuttingPlane_Slider_valueChanged(int value)
+{
+
+    qDebug()<<"slide: "<<value;
+    setSliceDatacube(value-1);
+
+    QString velocityUnit;
+    if(myParentVtkWindow!=0){
+        velocityUnit=myParentVtkWindow->selectedCubeVelocityUnit;
+    }else{
+        velocityUnit="km/s";
+    }
+
+    double velocityValue = myfits->getInitSlice()+myfits->GetCdelt(2)*(value-1);
+    qDebug()<<"velocityUnit: "<<velocityUnit;
+    if(velocityUnit.startsWith("m")){
+        // Returns value in km/s
+        velocityValue=velocityValue/1000;
+    }
+    ui->velocityLineEdit->setText(QString::number(velocityValue)+" Km/s");
+
+    ui->spinBox_cuttingPlane->setValue(value);
+    sliceA->SetPosition (0,0,value);
+    ui->qVTK1->update();
+}
+
+
+void vtkwindow_new::on_spinBox_cuttingPlane_valueChanged(int arg1)
+{
+    ui->cuttingPlane_Slider->setValue(arg1);
+}
+
+void vtkwindow_new::on_cameraLeft_clicked()
+{
+    setCameraAzimuth(-90);
+}
+
+void vtkwindow_new::on_bottomCamera_clicked()
+{
+    setCameraElevation(-90);
+}
+
+void vtkwindow_new::on_topCamera_clicked()
+{
+    setCameraElevation(90);
+}
+
+void vtkwindow_new::on_frontCamera_clicked()
+{
+    setCameraAzimuth(0);
+}
+
+void vtkwindow_new::on_cameraRight_clicked()
+{
+    setCameraAzimuth(90);
+}
+
+void vtkwindow_new::on_cameraBack_clicked()
+{
+    setCameraAzimuth(-180);
+}
+
+void vtkwindow_new::setCameraAzimuth(double az)
+{
+
+    resetCamera();
+    //    pp->getRenderer()->GetActiveCamera()->Azimuth(az);
+    m_Ren1->GetActiveCamera()->Azimuth(az);
+
+    ui->qVTK1->update();
+
+    if(!isDatacube)
+        scale(scaleActivate);
+    else
+        this->updateScene();
+
+}
+
+void vtkwindow_new::setCameraElevation(double el)
+{
+
+    resetCamera();
+
+    m_Ren1->GetActiveCamera()->Elevation(el);
+    ui->qVTK1->update();
+
+    if(!isDatacube)
+        scale(scaleActivate);
+    else
+        this->updateScene();
+
+}
+
+void vtkwindow_new::resetCamera()
+{
+    //MARI
+    m_Ren1->GetActiveCamera()->SetViewUp( 0, 1, 0 );
+    m_Ren1->GetActiveCamera()->SetFocalPoint( cam_init_foc );
+    m_Ren1->GetActiveCamera()->SetPosition( cam_init_pos);
+
+    /*
+    pp->getRenderer()->GetActiveCamera()->SetViewUp( 0, 1, 0 );
+    pp->getRenderer()->GetActiveCamera()->SetFocalPoint( cam_init_foc );
+    pp->getRenderer()->GetActiveCamera()->SetPosition( cam_init_pos);
+*/
+}
+
+void vtkwindow_new::scale(bool checked)
+{
+    scaleActivate=checked;
+    pp->activateScale(checked);
+}
+
+void vtkwindow_new::updateScene()
+{
+    m_Ren1->ResetCamera();
+    ui->qVTK1->update();
+}
+
+void vtkwindow_new::addBubble(VSTableDesktop* m_VisIVOTable)
+{
+
+    qDebug()<<"ok fino a qui";
+
+    float centroid_glat;
+    float centroid_glon;
+    QString contour;
+
+    double *coord= new double[3];
+    vtkSmartPointer<vtkAppendPolyData> appendFilter =vtkSmartPointer<vtkAppendPolyData>::New();
+
+    bool isOut;
+
+
+
+    for (unsigned long long j=0;j<m_VisIVOTable->getNumberOfRows();j++)
+    {
+        isOut=false;
+        centroid_glat= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat_cen")][j].c_str());
+        centroid_glon= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon_cen")][j].c_str());
+        contour = QString::fromUtf8(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("contour")][j].c_str());
+
+        QStringList pieces = contour.split( "," );
+        QStringList fil_glon = pieces.at(0).split("_");
+        QStringList fil_glat = pieces.at(1).split("_");
+
+        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
+
+        vtkSmartPointer<vtkPolyLine> polyLine =  vtkSmartPointer<vtkPolyLine>::New();
+
+        polyLine->GetPointIds()->SetNumberOfIds(fil_glat.size());
+        //draw filament contour
+        for (int i=0;i<fil_glat.size()-1;i++)
+        {
+            if (AstroUtils().sky2xy(filenameWithPath, fil_glon.at(i).toDouble(), fil_glat.at(i).toDouble(), coord))
+            {
+                points->InsertNextPoint(coord);
+                polyLine->GetPointIds()->SetId(i, i);
+
+            }
+            else
+            {
+                isOut=true;
+                break;
+            }
+
+        }
+        if (!isOut)
+        {
+            if (AstroUtils().sky2xy(filenameWithPath, fil_glon.at(0).toDouble(), fil_glat.at(0).toDouble(), coord))
+            {
+                points->InsertNextPoint(coord);
+                polyLine->GetPointIds()->SetId(fil_glat.size()-1, fil_glat.size()-1);
+
+            }
+
+
+            vtkSmartPointer<vtkCellArray> cells =
+                    vtkSmartPointer<vtkCellArray>::New();
+            cells->InsertNextCell(polyLine);
+
+            // Create a polydata to store everything in
+            vtkSmartPointer<vtkPolyData> polyData =
+                    vtkSmartPointer<vtkPolyData>::New();
+
+            // Add the points to the dataset
+            polyData->SetPoints(points);
+
+            // Add the lines to the dataset
+            polyData->SetLines(cells);
+
+#if VTK_MAJOR_VERSION <= 5
+            appendFilter->AddInputConnection(polyData->GetProducerPort());
+#else
+            appendFilter->AddInputData(polyData);
+#endif
+
+
+
+        }
+    }
+    vtkSmartPointer<vtkCleanPolyData> cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
+    cleanFilter->SetInputConnection(appendFilter->GetOutputPort());
+    cleanFilter->Update();
+
+
+    // Visualize
+    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputConnection(cleanFilter->GetOutputPort());
+
+
+    vtkSmartPointer<vtkLODActor> actor = vtkSmartPointer<vtkLODActor>::New();
+    actor->SetMapper(mapper);
+    actor->GetProperty()->SetColor(1, 0,0);
+
+    m_Ren1->AddActor(actor);
+    ui->qVTK1->update();
+
+    QString name="Bubbles_"+QString::number( visualized_actor_list.count() );
+
+    visualized_actor_list.insert(name,actor);
+
+
+
+    vtkfitstoolwidgetobject* filamentObject=new vtkfitstoolwidgetobject(2);
+    filamentObject->setName(name);
+    filamentObject->setActor(actor);
+    addLayer(filamentObject);
+
+
+}
+
+void vtkwindow_new::addFilaments(VSTableDesktop* m_VisIVOTable)
+{
+
+    float centroid_glat;
+    float centroid_glon;
+    QString contour;
+    QString branches_contour1d;
+    QString branches_flagspine;
+    QString branches_contour_new;
+    QString branches_contour;
+    double *coord= new double[3];
+    double *branches_contour1d_coord= new double[3];
+    double *branches_contour_new_coord= new double[3];
+    double *branches_contour_coord= new double[3];
+    vtkSmartPointer<vtkAppendPolyData> appendFilter =vtkSmartPointer<vtkAppendPolyData>::New();
+    vtkSmartPointer<vtkAppendPolyData> branches_contour1d_appendFilter =vtkSmartPointer<vtkAppendPolyData>::New();
+    vtkSmartPointer<vtkAppendPolyData> branches_contour1d_appendFilter_S =vtkSmartPointer<vtkAppendPolyData>::New();
+    vtkSmartPointer<vtkAppendPolyData> branches_contour_new_appendFilter =vtkSmartPointer<vtkAppendPolyData>::New();
+    vtkSmartPointer<vtkAppendPolyData> branches_contour_appendFilter =vtkSmartPointer<vtkAppendPolyData>::New();
+
+    bool isOut;
+    bool is_S;
+
+
+    for (unsigned long long j=0;j<m_VisIVOTable->getNumberOfRows();j++)
+    {
+        isOut=false;
+        is_S=false;
+        centroid_glat= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glat")][j].c_str());
+        centroid_glon= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("glon")][j].c_str());
+        contour = QString::fromUtf8(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("contour")][j].c_str());
+
+        branches_contour1d = QString::fromUtf8(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("branches_contour1d")][j].c_str());
+        branches_flagspine = QString::fromUtf8(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("flagspine_branches")][j].c_str());
+        branches_contour_new = QString::fromUtf8(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("branches_contour_new")][j].c_str());
+        branches_contour = QString::fromUtf8(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("branches_contour")][j].c_str());
+
+        QStringList pieces = contour.split( "," );
+        QStringList fil_glon = pieces.at(0).split("_");
+        QStringList fil_glat = pieces.at(1).split("_");
+
+
+        QStringList branches_contour1d_pieces = branches_contour1d.split( "," );
+        QStringList branches_contour1d_glon = branches_contour1d_pieces.at(0).split("_");
+        QStringList branches_contour1d_glat = branches_contour1d_pieces.at(1).split("_");
+
+        QStringList branches_contour_new_pieces = branches_contour_new.split( "," );
+        QStringList branches_contour_new_glon = branches_contour_new_pieces.at(0).split("_");
+        QStringList branches_contour_new_glat = branches_contour_new_pieces.at(1).split("_");
+
+        QStringList branches_contour_pieces = branches_contour.split( "," );
+        QStringList branches_contour_glon = branches_contour_pieces.at(0).split("_");
+        QStringList branches_contour_glat = branches_contour_pieces.at(1).split("_");
+
+
+        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkPolyLine> polyLine =  vtkSmartPointer<vtkPolyLine>::New();
+        polyLine->GetPointIds()->SetNumberOfIds(fil_glat.size()-1);
+
+
+        vtkSmartPointer<vtkPoints> branches_contour1d_points = vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkPolyLine> branches_contour1d_polyLine =  vtkSmartPointer<vtkPolyLine>::New();
+        branches_contour1d_polyLine->GetPointIds()->SetNumberOfIds(branches_contour1d_glat.size()-1);
+
+        vtkSmartPointer<vtkPoints> branches_contour1d_points_S = vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkPolyLine> branches_contour1d_polyLine_S =  vtkSmartPointer<vtkPolyLine>::New();
+        branches_contour1d_polyLine_S->GetPointIds()->SetNumberOfIds(branches_contour1d_glat.size()-1);
+
+        vtkSmartPointer<vtkPoints> branches_contour_new_points = vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkPolyLine> branches_contour_new_polyLine =  vtkSmartPointer<vtkPolyLine>::New();
+        branches_contour_new_polyLine->GetPointIds()->SetNumberOfIds(branches_contour_new_glat.size()-1);
+
+        vtkSmartPointer<vtkPoints> branches_contour_points = vtkSmartPointer<vtkPoints>::New();
+        vtkSmartPointer<vtkPolyLine> branches_contour_polyLine =  vtkSmartPointer<vtkPolyLine>::New();
+        branches_contour_polyLine->GetPointIds()->SetNumberOfIds(branches_contour_glat.size()-1);
+
+
+        //draw filament contour
+        //!!!!!!!!!!!!!!!!!! si dovrebbe skippare per quelli già visualizzati controllare idfil_mos
+        for (int i=0;i<fil_glat.size()-1;i++)
+        {
+            if (AstroUtils().sky2xy(filenameWithPath, fil_glon.at(i).toDouble(), fil_glat.at(i).toDouble(), coord))
+            {
+                points->InsertNextPoint(coord);
+                polyLine->GetPointIds()->SetId(i, i);
+
+            }
+            else
+            {
+                isOut=true;
+                break;
+            }
+
+        }
+        if (!isOut)
+        {
+            /*
+            if (AstroUtils().sky2xy(filenameWithPath, fil_glon.at(0).toDouble(), fil_glat.at(0).toDouble(), coord))
+            {
+
+               // qDebug()<<"j: "<<j<<" fil_glon.at(0).toDouble(): "<<fil_glon.at(0).toDouble()<<" fil_glat.at(0).toDouble(): "<<fil_glat.at(0).toDouble()<<" coord: "<<coord[0]<<" "<<coord[1]<<" "<<coord[2]<<" "<<fil_glat.size()-1<<" "<<fil_glat.size()-1;
+
+                points->InsertNextPoint(coord);
+                polyLine->GetPointIds()->SetId(fil_glat.size()-1, fil_glat.size()-1);
+
+            }
+*/
+            vtkSmartPointer<vtkCellArray> cells =
+                    vtkSmartPointer<vtkCellArray>::New();
+            cells->InsertNextCell(polyLine);
+
+            // Create a polydata to store everything in
+            vtkSmartPointer<vtkPolyData> polyData =
+                    vtkSmartPointer<vtkPolyData>::New();
+
+            // Add the points to the dataset
+            polyData->SetPoints(points);
+
+            // Add the lines to the dataset
+            polyData->SetLines(cells);
+
+#if VTK_MAJOR_VERSION <= 5
+            appendFilter->AddInputConnection(polyData->GetProducerPort());
+#else
+            appendFilter->AddInputData(polyData);
+#endif
+
+
+
+        }
+
+        //draw branches - contour1d
+        isOut=false;
+        for (int i=0;i<branches_contour1d_glat.size()-1;i++)
+        {
+            if (AstroUtils().sky2xy(filenameWithPath, branches_contour1d_glon.at(i).toDouble(), branches_contour1d_glat.at(i).toDouble(), branches_contour1d_coord))
+            {
+                branches_contour1d_points->InsertNextPoint(branches_contour1d_coord);
+                branches_contour1d_polyLine->GetPointIds()->SetId(i, i);
+
+                if ( branches_flagspine.compare("s",Qt::CaseInsensitive) ==0 )
+                {
+                    is_S=true;
+                    branches_contour1d_points_S ->InsertNextPoint(branches_contour1d_coord);
+                    branches_contour1d_polyLine_S ->GetPointIds()->SetId(i, i);
+
+                }
+
+            }
+            else
+            {
+                isOut=true;
+                break;
+            }
+
+        }
+        if (!isOut)
+        {
+
+            vtkSmartPointer<vtkCellArray> branches_contour1d_cells = vtkSmartPointer<vtkCellArray>::New();
+            branches_contour1d_cells->InsertNextCell(branches_contour1d_polyLine);
+
+            // Create a polydata to store everything in
+            vtkSmartPointer<vtkPolyData> branches_contour1d_polyData =
+                    vtkSmartPointer<vtkPolyData>::New();
+
+            // Add the points to the dataset
+            branches_contour1d_polyData->SetPoints(branches_contour1d_points);
+
+            // Add the lines to the dataset
+            branches_contour1d_polyData->SetLines(branches_contour1d_cells);
+
+#if VTK_MAJOR_VERSION <= 5
+            branches_contour1d_appendFilter->AddInputConnection(branches_contour1d_polyData->GetProducerPort());
+#else
+            branches_contour1d_appendFilter->AddInputData(branches_contour1d_polyData);
+#endif
+
+            if (is_S)
+            {
+                //BRANCHES_S
+                vtkSmartPointer<vtkCellArray> branches_contour1d_cells_S = vtkSmartPointer<vtkCellArray>::New();
+                branches_contour1d_cells_S->InsertNextCell(branches_contour1d_polyLine_S);
+
+                // Create a polydata to store everything in
+                vtkSmartPointer<vtkPolyData> branches_contour1d_polyData_S = vtkSmartPointer<vtkPolyData>::New();
+
+                // Add the points to the dataset
+                branches_contour1d_polyData_S->SetPoints(branches_contour1d_points_S);
+
+                // Add the lines to the dataset
+                branches_contour1d_polyData_S->SetLines(branches_contour1d_cells_S);
+#if VTK_MAJOR_VERSION <= 5
+                branches_contour1d_appendFilter_S->AddInputConnection(branches_contour1d_polyData_S->GetProducerPort());
+#else
+                branches_contour1d_appendFilter_S->AddInputData(branches_contour1d_polyData_S);
+#endif
+            }
+
+        }
+
+        //draw Branches - contour_new
+        for (int i=0;i<branches_contour_new_glat.size()-1;i++)
+        {
+            if (AstroUtils().sky2xy(filenameWithPath, branches_contour_new_glon.at(i).toDouble(), branches_contour_new_glat.at(i).toDouble(), branches_contour_new_coord))
+            {
+                branches_contour_new_points->InsertNextPoint(branches_contour_new_coord);
+                branches_contour_new_polyLine->GetPointIds()->SetId(i, i);
+            }
+            else
+            {
+                isOut=true;
+                break;
+            }
+
+        }
+        if (!isOut)
+        {
+            vtkSmartPointer<vtkCellArray> branches_contour_new_cells = vtkSmartPointer<vtkCellArray>::New();
+            branches_contour_new_cells->InsertNextCell(branches_contour_new_polyLine);
+
+            // Create a polydata to store everything in
+            vtkSmartPointer<vtkPolyData> branches_contour_new_polyData =
+                    vtkSmartPointer<vtkPolyData>::New();
+
+            // Add the points to the dataset
+            branches_contour_new_polyData->SetPoints(branches_contour_new_points);
+
+            // Add the lines to the dataset
+            branches_contour_new_polyData->SetLines(branches_contour_new_cells);
+
+#if VTK_MAJOR_VERSION <= 5
+            branches_contour_new_appendFilter->AddInputConnection(branches_contour_new_polyData->GetProducerPort());
+#else
+            branches_contour_new_appendFilter->AddInputData(branches_contour_new_polyData);
+#endif
+
+        }
+
+        //draw Branches - contour
+        for (int i=0;i<branches_contour_glat.size()-1;i++)
+        {
+            if (AstroUtils().sky2xy(filenameWithPath, branches_contour_glon.at(i).toDouble(), branches_contour_glat.at(i).toDouble(), branches_contour_coord))
+            {
+                branches_contour_points->InsertNextPoint(branches_contour_coord);
+                branches_contour_polyLine->GetPointIds()->SetId(i, i);
+            }
+            else
+            {
+                isOut=true;
+                break;
+            }
+
+        }
+        if (!isOut)
+        {
+            vtkSmartPointer<vtkCellArray> branches_contour_cells = vtkSmartPointer<vtkCellArray>::New();
+            branches_contour_cells->InsertNextCell(branches_contour_polyLine);
+
+            // Create a polydata to store everything in
+            vtkSmartPointer<vtkPolyData> branches_contour_polyData =
+                    vtkSmartPointer<vtkPolyData>::New();
+
+            // Add the points to the dataset
+            branches_contour_polyData->SetPoints(branches_contour_points);
+
+            // Add the lines to the dataset
+            branches_contour_polyData->SetLines(branches_contour_cells);
+
+#if VTK_MAJOR_VERSION <= 5
+            branches_contour_appendFilter->AddInputConnection(branches_contour_polyData->GetProducerPort());
+#else
+            branches_contour_appendFilter->AddInputData(branches_contour_polyData);
+#endif
+
+        }
+
+    }
+
+    //Contorno filamento
+    vtkSmartPointer<vtkCleanPolyData> cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
+    cleanFilter->SetInputConnection(appendFilter->GetOutputPort());
+    cleanFilter->Update();
+
+
+    // Visualize
+    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputConnection(cleanFilter->GetOutputPort());
+
+
+    vtkSmartPointer<vtkLODActor> actor = vtkSmartPointer<vtkLODActor>::New();
+    actor->SetMapper(mapper);
+    actor->GetProperty()->SetColor(0, 1,0);
+
+  //  m_Ren1->AddActor(actor);
+   // ui->qVTK1->update();
+
+    //  QString name="Filaments_"+QString::number( visualized_actor_list.count() );
+    QString name=stringDictWidget->getColDescStringDict().value("vlkb_filaments.filaments.contour");
+
+    addCombinedLayer(name,  actor,2, true);
+
+/*visualized_actor_list.insert(name,actor);
+
+
+
+    vtkfitstoolwidgetobject* filamentObject=new vtkfitstoolwidgetobject(2);
+
+    filamentObject->setName(name);
+    filamentObject->setActor(actor);
+
+    addLayer(filamentObject);
+*/
+
+
+    //branches Contour1d
+    vtkSmartPointer<vtkCleanPolyData> branches_contour1d_cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
+    branches_contour1d_cleanFilter->SetInputConnection(branches_contour1d_appendFilter->GetOutputPort());
+    branches_contour1d_cleanFilter->Update();
+
+
+    // Visualize
+    vtkSmartPointer<vtkPolyDataMapper> branches_contour1d_mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    branches_contour1d_mapper->SetInputConnection(branches_contour1d_cleanFilter->GetOutputPort());
+
+
+    vtkSmartPointer<vtkLODActor> branches_contour1d_actor = vtkSmartPointer<vtkLODActor>::New();
+    branches_contour1d_actor->SetMapper(branches_contour1d_mapper);
+    branches_contour1d_actor->GetProperty()->SetColor(1, 0,0);
+
+   // m_Ren1->AddActor(branches_contour1d_actor);
+   // ui->qVTK1->update();
+
+    //name="Filaments_branches_contour1d_"+QString::number( visualized_actor_list.count() );
+    name=stringDictWidget->getColDescStringDict().value("vlkb_filaments.branches.contour1d");
+
+   // visualized_actor_list.insert(name,branches_contour1d_actor);
+
+    addCombinedLayer(name,  branches_contour1d_actor,2, true);
+
+
+/*
+    vtkfitstoolwidgetobject* branches_contour1d_filamentObject=new vtkfitstoolwidgetobject(2);
+    branches_contour1d_filamentObject->setName(name);
+    branches_contour1d_filamentObject->setActor(branches_contour1d_actor);
+    addLayer(branches_contour1d_filamentObject);
+*/
+
+/*
+    //branches Contour1d_S
+    vtkSmartPointer<vtkCleanPolyData> branches_contour1d_cleanFilter_S = vtkSmartPointer<vtkCleanPolyData>::New();
+    branches_contour1d_cleanFilter_S->SetInputConnection(branches_contour1d_appendFilter_S->GetOutputPort());
+    branches_contour1d_cleanFilter_S->Update();
+    // Visualize
+    vtkSmartPointer<vtkPolyDataMapper> branches_contour1d_mapper_S = vtkSmartPointer<vtkPolyDataMapper>::New();
+    branches_contour1d_mapper_S->SetInputConnection(branches_contour1d_cleanFilter_S->GetOutputPort());
+
+    vtkSmartPointer<vtkLODActor> branches_contour1d_actor_S = vtkSmartPointer<vtkLODActor>::New();
+    branches_contour1d_actor_S->SetMapper(branches_contour1d_mapper_S);
+    branches_contour1d_actor_S->GetProperty()->SetColor(0, 0,1);
+
+    m_Ren1->AddActor(branches_contour1d_actor_S);
+    ui->qVTK1->update();
+
+    name="Filaments_branches_contour1d_S_"+QString::number( visualized_actor_list.count() );
+
+    visualized_actor_list.insert(name,branches_contour1d_actor_S);
+
+
+    vtkfitstoolwidgetobject* branches_contour1d_filamentObject_S=new vtkfitstoolwidgetobject(2);
+    branches_contour1d_filamentObject_S->setName(name);
+    branches_contour1d_filamentObject_S->setActor(branches_contour1d_actor_S);
+    addLayer(branches_contour1d_filamentObject_S);
+*/
+/*
+    //branches Contour_new
+    vtkSmartPointer<vtkCleanPolyData> branches_contour_new_cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
+    branches_contour_new_cleanFilter->SetInputConnection(branches_contour_new_appendFilter->GetOutputPort());
+    branches_contour_new_cleanFilter->Update();
+
+    // Visualize
+    vtkSmartPointer<vtkPolyDataMapper> branches_contour_new_mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    branches_contour_new_mapper->SetInputConnection(branches_contour_new_cleanFilter->GetOutputPort());
+
+    vtkSmartPointer<vtkLODActor> branches_contour_new_actor = vtkSmartPointer<vtkLODActor>::New();
+    branches_contour_new_actor->SetMapper(branches_contour_new_mapper);
+    branches_contour_new_actor->GetProperty()->SetColor(1, 0,1);
+    branches_contour_new_actor->VisibilityOff();
+    //vtkImageSlice::SafeDownCast(imageStack->GetImages()->GetItemAsObject(cb))->VisibilityOff();
+
+    m_Ren1->AddActor(branches_contour_new_actor);
+    ui->qVTK1->update();
+
+    name="Filaments_branches_contour_new_"+QString::number( visualized_actor_list.count() );
+    visualized_actor_list.insert(name,branches_contour_new_actor);
+
+    vtkfitstoolwidgetobject* branches_contour_new_filamentObject=new vtkfitstoolwidgetobject(2);
+    branches_contour_new_filamentObject->setName(name);
+    branches_contour_new_filamentObject->setActor(branches_contour_new_actor);
+    addLayer(branches_contour_new_filamentObject,false);
+*/
+    //branches Contour
+    vtkSmartPointer<vtkCleanPolyData> branches_contour_cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
+    branches_contour_cleanFilter->SetInputConnection(branches_contour_appendFilter->GetOutputPort());
+    branches_contour_cleanFilter->Update();
+
+    // Visualize
+    vtkSmartPointer<vtkPolyDataMapper> branches_contour_mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    branches_contour_mapper->SetInputConnection(branches_contour_cleanFilter->GetOutputPort());
+
+    vtkSmartPointer<vtkLODActor> branches_contour_actor = vtkSmartPointer<vtkLODActor>::New();
+    branches_contour_actor->SetMapper(branches_contour_mapper);
+    branches_contour_actor->GetProperty()->SetColor(0, 1,1);
+
+//    m_Ren1->AddActor(branches_contour_actor);
+ //   ui->qVTK1->update();
+
+//    name="Filaments_branches_contour_"+QString::number( visualized_actor_list.count() );
+    name=stringDictWidget->getColDescStringDict().value("vlkb_filaments.branches.contour");
+
+   // visualized_actor_list.insert(name,branches_contour_actor);
+
+
+
+/*
+    vtkfitstoolwidgetobject* branches_contour_filamentObject=new vtkfitstoolwidgetobject(2);
+    branches_contour_filamentObject->setName(name);
+    branches_contour_filamentObject->setActor(branches_contour_actor);
+    branches_contour_actor->VisibilityOff();
+    addLayer(branches_contour_filamentObject,false);
+ */
+    addCombinedLayer(name,  branches_contour_actor,2, false);
+
+
+
+}
+
+
+void vtkwindow_new::addCombinedLayer(QString name,  vtkSmartPointer<vtkLODActor>actor,int objtype, bool active)
+
+{
+    if ( VisualizedEllipseSourcesList.contains(name) )
+    {
+        vtkSmartPointer<vtkAppendPolyData> appendFilter2 =vtkSmartPointer<vtkAppendPolyData>::New();
+
+    #if VTK_MAJOR_VERSION <= 5
+        appendFilter2->AddInputConnection(VisualizedEllipseSourcesList.value(name)->GetMapper()->GetInputAsDataSet());
+    #else
+        appendFilter2->AddInputData( vtkPolyData::SafeDownCast(VisualizedEllipseSourcesList.value(name)->GetMapper()->GetInputAsDataSet()));
+    #endif
+
+
+    #if VTK_MAJOR_VERSION <= 5
+        appendFilter2->AddInputConnection(vtkPolyData::SafeDownCast(actor->GetMapper()->GetInputAsDataSet())) ;
+    #else
+        appendFilter2->AddInputData(vtkPolyData::SafeDownCast(actor->GetMapper()->GetInputAsDataSet())) ;
+    #endif
+
+    // Remove any duplicate points.
+    vtkSmartPointer<vtkCleanPolyData> cleanFilter2 = vtkSmartPointer<vtkCleanPolyData>::New();
+    cleanFilter2->SetInputConnection(appendFilter2->GetOutputPort());
+    cleanFilter2->Update();
+
+
+    vtkSmartPointer<vtkPolyDataMapper> mapper2 = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper2->SetInputConnection(cleanFilter2->GetOutputPort());
+
+    VisualizedEllipseSourcesList.value(name)->SetMapper(mapper2);
+
+    m_Ren1->AddActor(VisualizedEllipseSourcesList.value(name));
+    }
+
+    else
+    {
+
+        visualized_actor_list.insert(name,actor);
+
+        vtkfitstoolwidgetobject* singleBandObject=new vtkfitstoolwidgetobject(objtype);
+        //singleBandObject->setParentItem(imageObject);
+        singleBandObject->setName(name);
+
+        //actor->GetProperty()->SetColor(0, 1,0);
+
+        VisualizedEllipseSourcesList.insert(name,actor);
+        m_Ren1->AddActor(actor);
+        singleBandObject->setActor(actor);
+
+
+
+        addLayer(singleBandObject,active);
+        if(!active)
+            actor->VisibilityOff();
+
+    }
+//end mod fv
+
+
+}
+
+void vtkwindow_new::addSources(VSTableDesktop* m_VisIVOTable)
+{
+    QHash<QString, vtkEllipse* > ellipse_list;
+
+    float semiMajorAxisLength;
+    float semiMinorAxisLength;
+    float angle;
+    float ra;
+    float dec;
+    QString sourceName;
+    double *coord= new double[3];
+
+
+
+    for (unsigned long long j=0;j<m_VisIVOTable->getNumberOfRows();j++)
+    {
+        semiMajorAxisLength= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("FWHMA")][j].c_str())/2;
+        semiMinorAxisLength= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("FWHMB")][j].c_str())/2;
+        angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("PA")][j].c_str());
+        ra=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("GLON")][j].c_str());
+        dec=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("GLAT")][j].c_str());
+        sourceName = QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("DESIGNATION")][j] );
+
+        if (AstroUtils().sky2xy(filenameWithPath ,ra,dec,coord))
+        {
+
+            ellipse_list.insert( sourceName,new vtkEllipse(semiMajorAxisLength,semiMinorAxisLength, angle, coord[0], coord[1], coord[2], 0,j, sourceName, m_VisIVOTable));
+
+            /*
+        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
+        const float p[3] = {coord[0], coord[1], 0};
+        vtkSmartPointer<vtkCellArray> vertices =          vtkSmartPointer<vtkCellArray>::New();
+        vtkIdType pid[1];
+        pid[0] = points->InsertNextPoint(p);
+        vertices->InsertNextCell(1,pid);
+        // Create a polydata object
+        vtkSmartPointer<vtkPolyData> point =
+                vtkSmartPointer<vtkPolyData>::New();
+
+        // Set the points and vertices we created as the geometry and topology of the polydata
+        point->SetPoints(points);
+        point->SetVerts(vertices);
+
+        // Visualize
+        vtkSmartPointer<vtkPolyDataMapper> mapper =
+                vtkSmartPointer<vtkPolyDataMapper>::New();
+
+#if VTK_MAJOR_VERSION <= 5
+        mapper->SetInput(point);
+#else
+        mapper->SetInputData(point);
+#endif
+
+        vtkSmartPointer<vtkActor> actor =
+                vtkSmartPointer<vtkActor>::New();
+        actor->SetMapper(mapper);
+        actor->GetProperty()->SetPointSize(2);
+        actor->GetProperty()->SetColor(1.0,0.0,0.0);
+
+
+        m_Ren1->AddActor(actor);
+
+*/
+
+
+        }
+
+    }
+
+    file_wavelength.insert(QString::fromStdString(m_VisIVOTable->getName()),m_VisIVOTable->getWavelength());
+    drawEllipse(ellipse_list,  QString::fromStdString(m_VisIVOTable->getName()) );
+
+    this->update();
+
+
+}
+
+void vtkwindow_new::closeEvent(QCloseEvent *event)
+{
+    if(vtkwintype==1)
+        removeContour();
+    this->~vtkwindow_new();
+
+}
+
+void vtkwindow_new::updateSpecies(){
+
+    ui->lineEdit_species->setText(species);
+}
+
+void vtkwindow_new::updateSurvey(){
+
+    ui->lineEdit_survey->setText(survey);
+}
+
+void vtkwindow_new::updateTransition(){
+    ui->lineEdit_transition->setText(transition);
+}
+
+void vtkwindow_new::setSpecies(QString q){
+    species=q;
+    vtkcontourwindow->species=q;
+    emit speciesChanged();
+
+}
+void vtkwindow_new::setSurvey(QString q){
+    survey=q;
+    vtkcontourwindow->survey=q;
+    emit surveyChanged();
+
+}
+
+void vtkwindow_new::setTransition(QString q){
+    transition=q;
+    vtkcontourwindow->transition=q;
+    emit transitionChanged();
+}
+
+/*
+ void vtkwindow_new::drawEllipse(QHash<QString,vtkEllipse *> ellipse, QString sourceFilename )
+{
+    QString ori_sourceFilename=sourceFilename;
+    //    sourceFilename=sourceFilename+"_"+QString::number( visualized_actor_list.count() );
+    sourceFilename=sourceFilename+"_"+QString::number( visualized_actor_list.count() );
+
+    vtkSmartPointer<vtkAppendPolyData> appendFilter =vtkSmartPointer<vtkAppendPolyData>::New();
+    ellipse_list.unite(ellipse);
+
+    foreach(vtkEllipse *el, ellipse )
+    {
+#if VTK_MAJOR_VERSION <= 5
+        appendFilter->AddInputConnection(el->getPolyData()->GetProducerPort());
+#else
+        appendFilter->AddInputData(el->getPolyData());
+        //  appendFilter->AddInputConnection(el->getPolyData()->GetProducerPort());
+#endif
+
+
+        designation2fileMap.insert(el->getSourceName(), sourceFilename);
+
+    }
+
+
+    appendFilter->Update();
+
+    // Remove any duplicate points.
+    vtkSmartPointer<vtkCleanPolyData> cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
+    cleanFilter->SetInputConnection(appendFilter->GetOutputPort());
+    cleanFilter->Update();
+
+
+    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputConnection(cleanFilter->GetOutputPort());
+
+
+
+    vtkSmartPointer<vtkLODActor> ellipseActor = vtkSmartPointer<vtkLODActor>::New();
+    ellipseActor->SetMapper(mapper);
+
+    double numOfLayer = visualized_actor_list.count();
+    //Color of ellipse actor, iterate throght Qt::GlobalColor
+    QColor c= QColor( Qt::GlobalColor( Qt::red+numOfLayer ) );
+    ellipseActor->GetProperty()->SetColor(c.redF(), c.greenF(), c.blueF());
+
+    m_Ren1->AddActor(ellipseActor);
+    if(ori_sourceFilename.compare(stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designationft"))!=0)
+        ellipseActor->VisibilityOff();
+
+    qDebug()<<"ADD: "<<ori_sourceFilename;
+
+
+
+    ellipse_actor_list.insert( sourceFilename ,ellipseActor);
+    visualized_actor_list.insert(sourceFilename,ellipseActor);
+
+    vtkfitstoolwidgetobject* singleBandObject=new vtkfitstoolwidgetobject(1);
+    singleBandObject->setParentItem(imageObject);
+    singleBandObject->setName(sourceFilename);
+    singleBandObject->setActor(ellipseActor);
+
+
+    if(ori_sourceFilename.compare(stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designationft"))==0)
+        addLayer(singleBandObject,true);
+    else
+        addLayer(singleBandObject,false);
+
+
+}
+*/
+
+void vtkwindow_new::drawEllipse(QHash<QString,vtkEllipse *> ellipse, QString sourceFilename )
+{
+    QString ori_sourceFilename=sourceFilename;
+    // sourceFilename=sourceFilename+"_"+QString::number( visualized_actor_list.count() );
+
+    vtkSmartPointer<vtkAppendPolyData> appendFilter =vtkSmartPointer<vtkAppendPolyData>::New();
+
+   //ellipse_list.unite(ellipse);
+
+ellipse_list=ellipse;
+
+
+    foreach(vtkEllipse *el, ellipse )
+    {
+#if VTK_MAJOR_VERSION <= 5
+        appendFilter->AddInputConnection(el->getPolyData()->GetProducerPort());
+#else
+        appendFilter->AddInputData(el->getPolyData());
+#endif
+
+
+        designation2fileMap.insert(el->getSourceName(), sourceFilename);
+
+    }
+
+
+    appendFilter->Update();
+
+    // Remove any duplicate points.
+    vtkSmartPointer<vtkCleanPolyData> cleanFilter = vtkSmartPointer<vtkCleanPolyData>::New();
+    cleanFilter->SetInputConnection(appendFilter->GetOutputPort());
+    cleanFilter->Update();
+
+
+    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputConnection(cleanFilter->GetOutputPort());
+
+
+    vtkSmartPointer<vtkLODActor> ellipseActor = vtkSmartPointer<vtkLODActor>::New();
+    ellipseActor->SetMapper(mapper);
+
+    //mod fv  accorpamento layers
+
+    if ( VisualizedEllipseSourcesList.contains(ori_sourceFilename) )
+    {
+
+
+        vtkSmartPointer<vtkAppendPolyData> appendFilter2 =vtkSmartPointer<vtkAppendPolyData>::New();
+
+#if VTK_MAJOR_VERSION <= 5
+        appendFilter2->AddInputConnection(VisualizedEllipseSourcesList.value(ori_sourceFilename)->GetMapper()->GetInputAsDataSet());
+#else
+        appendFilter2->AddInputData( vtkPolyData::SafeDownCast(VisualizedEllipseSourcesList.value(ori_sourceFilename)->GetMapper()->GetInputAsDataSet()));
+#endif
+
+
+#if VTK_MAJOR_VERSION <= 5
+        appendFilter2->AddInputConnection(vtkPolyData::SafeDownCast(ellipseActor->GetMapper()->GetInputAsDataSet())) ;
+#else
+        appendFilter2->AddInputData(vtkPolyData::SafeDownCast(ellipseActor->GetMapper()->GetInputAsDataSet())) ;
+#endif
+
+        // Remove any duplicate points.
+        vtkSmartPointer<vtkCleanPolyData> cleanFilter2 = vtkSmartPointer<vtkCleanPolyData>::New();
+        cleanFilter2->SetInputConnection(appendFilter2->GetOutputPort());
+        cleanFilter2->Update();
+
+
+        vtkSmartPointer<vtkPolyDataMapper> mapper2 = vtkSmartPointer<vtkPolyDataMapper>::New();
+        mapper2->SetInputConnection(cleanFilter2->GetOutputPort());
+
+        VisualizedEllipseSourcesList.value(ori_sourceFilename)->SetMapper(mapper2);
+
+        m_Ren1->AddActor(VisualizedEllipseSourcesList.value(ori_sourceFilename));
+    }
+    else
+    {
+
+        ellipse_actor_list.insert( sourceFilename ,ellipseActor);
+        visualized_actor_list.insert(sourceFilename,ellipseActor);
+
+        vtkfitstoolwidgetobject* singleBandObject=new vtkfitstoolwidgetobject(1);
+        singleBandObject->setParentItem(imageObject);
+        singleBandObject->setName(sourceFilename);
+
+        QColor c= QColor( Qt::GlobalColor( Qt::red+VisualizedEllipseSourcesList.count() ) );
+        ellipseActor->GetProperty()->SetColor(c.redF(), c.greenF(), c.blueF());
+
+        VisualizedEllipseSourcesList.insert(ori_sourceFilename,ellipseActor);
+        m_Ren1->AddActor(ellipseActor);
+        singleBandObject->setActor(ellipseActor);
+
+
+        //if(ori_sourceFilename.compare(stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designationft"))==0)
+
+        if (ori_sourceFilename.compare("Band-merged Source Designation")==0)
+            addLayer(singleBandObject,true);
+        else
+        {
+            addLayer(singleBandObject,false);
+            ellipseActor->VisibilityOff();
+        }
+    }
+
+
+    //end mod fv accorpamento layers
+
+
+    /*
+    double numOfLayer = visualized_actor_list.count();
+
+    QColor c= QColor( Qt::GlobalColor( Qt::red+numOfLayer ) );
+    ellipseActor->GetProperty()->SetColor(c.redF(), c.greenF(), c.blueF());
+
+    m_Ren1->AddActor(ellipseActor);
+
+    if(ori_sourceFilename.compare(stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designationft"))!=0)
+        ellipseActor->VisibilityOff();
+
+    qDebug()<<"ADD: "<<ori_sourceFilename;
+
+    ellipse_actor_list.insert( sourceFilename ,ellipseActor);
+    visualized_actor_list.insert(sourceFilename,ellipseActor);
+
+    vtkfitstoolwidgetobject* singleBandObject=new vtkfitstoolwidgetobject(1);
+    singleBandObject->setParentItem(imageObject);
+    singleBandObject->setName(sourceFilename);
+    singleBandObject->setActor(ellipseActor);
+
+
+    if(ori_sourceFilename.compare(stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designationft"))==0)
+        addLayer(singleBandObject,true);
+    else
+        addLayer(singleBandObject,false);
+*/
+
+}
+
+void vtkwindow_new::removeActor(vtkProp *actor)
+{
+    ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->RemoveActor2D(actor);
+    ui->qVTK1->GetRenderWindow()->Render();
+
+}
+
+void vtkwindow_new::changeScalar(std::string scalar)
+{
+    pp->colorScalar=scalar;
+    pp->setLookupTable();
+    //FV
+    pp->setLookupTableScale();
+
+    qDebug()<<"Lut Scalar "<<QString::fromStdString(scalar);
+    vtkSmartPointer<vtkDataArray> data =pp->getPolyData()->GetPointData()->GetArray(scalar.c_str());
+    if(data!=0){
+        double range[2];
+        data->GetRange(range);
+        qDebug()<<range[0]<<" "<<range[1];
+    }
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::showColorbar(bool checked)
+{
+
+
+    pp->showColorBar=checked;
+    pp->scalarBar->SetVisibility(checked);
+
+
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::slot_clicked(vtkObject*, unsigned long, void*, void*)
+{
+
+    // Forward events
+    vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
+    coordinate->SetCoordinateSystemToDisplay();
+    coordinate->SetValue(ui->qVTK1->GetRenderWindow()->GetInteractor()->GetEventPosition()[0],ui->qVTK1->GetRenderWindow()->GetInteractor()->GetEventPosition()[1],0);
+
+    double* world_coord = coordinate->GetComputedWorldValue(ui->qVTK1->GetRenderWindow()->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+
+
+    vtkSmartPointer<vtkImageActorPointPlacer> pointPlacer = vtkSmartPointer<vtkImageActorPointPlacer>::New();
+    pointPlacer->SetImageActor(imageViewer->GetImageActor());
+
+    if(pointPlacer->ValidateWorldPosition(world_coord)==1)
+    {
+
+        //cerca le sorgenti che cadono dove hai cliccato
+
+        /*
+        vtkSmartPointer<vtkSelectEnclosedPoints> selectEnclosedPoints =
+            vtkSmartPointer<vtkSelectEnclosedPoints>::New();
+        selectEnclosedPoints->SetInput(pointsPolydata);
+*/
+
+        QSignalMapper* signalMapper = new QSignalMapper (this) ;
+
+
+        QMenu *menu=new QMenu(this);
+        //QAction *viewAct = new QAction(tr("&View"), this);
+        QAction *viewDC = new QAction(tr("&Cutout Datacube"), this);
+        //viewAct->setStatusTip(tr("Visualize selected map file"));
+        connect (viewDC, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
+
+
+        // Forward events
+        vtkSmartPointer<vtkCoordinate> coordinate = vtkSmartPointer<vtkCoordinate>::New();
+        coordinate->SetCoordinateSystemToDisplay();
+        coordinate->SetValue(ui->qVTK1->GetInteractor()->GetEventPosition()[0],ui->qVTK1->GetInteractor()->GetEventPosition()[1],0);
+
+        double* world_coord = coordinate->GetComputedWorldValue(ui->qVTK1->GetInteractor()->GetRenderWindow()->GetRenderers()->GetFirstRenderer());
+
+        double *sky_coord_gal = new double[2];
+
+        AstroUtils().xy2sky(filenameWithPath,world_coord[0],world_coord[1],sky_coord_gal,3);
+
+        signalMapper -> setMapping (viewDC, QString::number(sky_coord_gal[0])+";"+QString::number(sky_coord_gal[1]) ) ;
+
+        connect (signalMapper, SIGNAL(mapped(QString)), this, SLOT(cutoutDatacube(QString))) ;
+
+
+        menu->addAction(viewDC);
+
+        menu->popup(QCursor::pos());
+    }
+}
+
+void vtkwindow_new::changePalette(std::string palette)
+{
+    pp->palette=palette;
+    //  pp->setLookupTable();
+    pp->setLookupTableScale();
+
+    ui->qVTK1->update();
+
+}
+
+
+FitsImageStatisiticInfo* vtkwindow_new::getInfoWindow()
+{
+    return info;
+}
+
+void vtkwindow_new::cutoutDatacube(QString c )
+{
+    QStringList splittedStrings = c.split(";");
+
+
+    dbquery *queryWindow=new dbquery();
+    queryWindow->setCoordinate(splittedStrings.at(0),splittedStrings.at(1));
+    queryWindow->show();
+
+
+}
+
+void vtkwindow_new::addLocalSources()
+{
+    vtkfitstoolsw->on_addLocalSourcesPushButton_clicked();
+}
+
+void vtkwindow_new::changeFitsScale(std::string palette, std::string scale)
+{
+
+    vtkSmartPointer<vtkLookupTable> lut = vtkSmartPointer<vtkLookupTable>::New();
+
+    QString myscale=scale.c_str();
+
+    //applico la lut al livello selezionato nella tabella in basso adx, se nessuna selezione è fatta
+    //appico al livello 0
+    int pos=0;
+
+    if (ui->listWidget->selectionModel()->selectedRows().count()!=0 && imgLayerList.at(ui->listWidget->selectionModel()->selectedRows().at(0).row())->getType()==0 )
+    {
+        qDebug()<<"inside";
+        pos = ui->listWidget->selectionModel()->selectedRows().at(0).row();
+
+
+    }
+
+    float min= imgLayerList.at(  pos)->getFits()->GetMin() ;
+    if(min<=0)
+        min=1;
+    float max= imgLayerList.at(pos)->getFits()->GetMax();
+
+    lut->SetTableRange(  min , max );
+
+    imgLayerList.at(pos)->setLutScale(myscale);
+    imgLayerList.at(pos)->setLutType(QString::fromStdString(palette));
+
+
+    if(myscale=="Linear")lut->SetScaleToLinear();
+    else if(myscale=="Log")
+        lut->SetScaleToLog10();
+
+    SelectLookTable(palette.c_str(),lut);
+
+    vtkSmartPointer<vtkImageMapToColors> colors =  vtkSmartPointer<vtkImageMapToColors>::New();
+
+    colors->SetInputData( imgLayerList.at(pos)->getFits()->GetOutput());
+    colors->SetLookupTable(lut);
+    colors->Update();
+
+    vtkSmartPointer<vtkImageSliceMapper> imageSliceMapperLutModified = vtkSmartPointer<vtkImageSliceMapper>::New();
+    imageSliceMapperLutModified->SetInputData(colors->GetOutput());
+
+    // vtkImageSlice::SafeDownCast( imageStack->GetImages()->GetItemAsObject(imgLayerList.at(pos )->getLayerNumber()))->SetMapper(imageSliceMapperLutModified);
+    vtkImageSlice::SafeDownCast( imageStack->GetImages()->GetItemAsObject(pos) )->SetMapper(imageSliceMapperLutModified);
+    // vtkImageSlice::SafeDownCast(imageStack->GetImages()->GetItemAsObject(cb))->VisibilityOn();
+
+    ui->qVTK1->update();
+}
+
+
+void vtkwindow_new::addSourcesFromBM(VSTableDesktop* m_VisIVOTable)
+{
+
+
+    QStringList wavelen ;
+    // wavelen<<"ft"<<"1100"<<"870"<<"500"<<"350"<<"250"<<"160"<<"70"<<"24"<<"22"<<"21";
+    wavelen<<"1100"<<"870"<<"500"<<"350"<<"250"<<"160"<<"70"<<"24"<<"22"<<"21"<<"ft";
+
+
+    float semiMajorAxisLength;
+    float semiMinorAxisLength;
+    float angle;
+    float ra;
+    float dec;
+    int numidtree;
+    int numid_intree;
+    QString sourceName;
+    double *coord= new double[3];
+
+   // QHash<QString, vtkEllipse* > ft_ellipse_list;
+
+
+    //    int index_ft=0;
+    //   int old_index_ft=0;
+
+    for( int i=0; i < wavelen.size() ;i++ )
+    {
+
+
+        QHash<QString, vtkEllipse* > ellipse_list_local;
+        QList<QString>* list = new QList<QString>();
+
+        for (unsigned long long j=0;j<m_VisIVOTable->getNumberOfRows();j++)
+        {
+
+            sourceName = QString::fromStdString( m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("DESIGNATION"+wavelen[i].toStdString())][j] );
+            if (sourceName.compare("missing")!=0 &&  list->indexOf(sourceName) == -1 )
+            {
+                list->push_back(sourceName);
+                //finchè marco non mette la tabella
+                /*
+                if(wavelen[i].compare("ft")==0)
+                {
+
+                    old_index_ft=index_ft;
+                    index_ft=j;
+                    qDebug()<<"\t index_ft: "<<index_ft <<" "<<old_index_ft ;
+                    semiMajorAxisLength=maxSemiMajorAxisLength;
+                    semiMinorAxisLength=maxSemiMinorAxisLength;
+                    angle=maxAngle;
+
+                }
+                else
+                {
+                */
+                semiMajorAxisLength= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("FWHMA"+wavelen[i].toStdString())][j].c_str())/2;
+                semiMinorAxisLength= atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("FWHMB"+wavelen[i].toStdString())][j].c_str())/2;
+                angle=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("PA"+wavelen[i].toStdString())][j].c_str());
+                //  M_PI*semiMajorAxisLength*semiMinorAxisLength;
+                //qDebug()<<"\t\t index_ft: "<<index_ft <<" "<<old_index_ft ;
+
+                /*
+                    if (index_ft!= old_index_ft)
+                    {
+                        area=0;
+
+                    }
+                    if (M_PI*semiMajorAxisLength*semiMinorAxisLength>area)
+                    {
+                        maxSemiMajorAxisLength=semiMajorAxisLength;
+                        maxSemiMinorAxisLength=semiMinorAxisLength;
+                        maxAngle=angle;
+                        area=M_PI*semiMajorAxisLength*semiMinorAxisLength;
+
+                    }
+*/
+
+                // }
+                ra=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("GLON"+wavelen[i].toStdString())][j].c_str());
+                dec=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("GLAT"+wavelen[i].toStdString())][j].c_str());
+
+                numidtree=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("numidtree")][j].c_str());
+                numid_intree=atof(m_VisIVOTable->getTableData()[m_VisIVOTable->getColId("numid_intree")][j].c_str());
+
+
+                if (AstroUtils().sky2xy(filenameWithPath ,ra,dec,coord))
+                {
+
+                    qDebug()<<"insert: "<<sourceName;
+
+                    if(wavelen[i].compare("ft")==0)
+                    {
+                        ft_ellipse_list.insert(sourceName,new vtkEllipse(semiMajorAxisLength,semiMinorAxisLength,angle, coord[0], coord[1], coord[2], 0,j, sourceName, m_VisIVOTable,numidtree,numid_intree));
+                    }
+                    else
+                        ellipse_list_local.insert( sourceName,new vtkEllipse(semiMajorAxisLength,semiMinorAxisLength,angle, coord[0], coord[1], coord[2], 0,j, sourceName, m_VisIVOTable,numidtree,numid_intree));
+
+                }
+            }
+
+        }
+        // drawEllipse(ellipse_list,  QString::fromStdString(m_VisIVOTable->getName()) );
+
+
+
+
+        qDebug()<<"\t draw "<<wavelen[i]<<" - "<<stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designation"+wavelen[i]);
+        if(wavelen[i].compare("ft")==0)
+            drawEllipse(ft_ellipse_list,  stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designation"+wavelen[i]) );
+        else
+            drawEllipse(ellipse_list_local,  stringDictWidget->getColUtypeStringDict().value("vlkb_compactsources.sed_view_final.designation"+wavelen[i]) );
+        /*
+
+        if(wavelen[i].compare("ft")==0)
+            drawEllipse(ft_ellipse_list,  wavelen[i] );
+        else
+            drawEllipse(ellipse_list_local,  wavelen[i] );
+*/
+
+    }
+
+    file_wavelength.insert(QString::fromStdString(m_VisIVOTable->getName()),m_VisIVOTable->getWavelength());
+
+
+    //test window resize
+    ui->qVTK1->update();
+    this->update();
+
+}
+
+
+void vtkwindow_new::createInfoWindow()
+{
+
+    info= new FitsImageStatisiticInfo(this);
+
+}
+
+void vtkwindow_new::setSelectedActor(vtkSmartPointer<vtkActor> sel)
+{
+    selectedActor=sel;
+}
+
+void vtkwindow_new::setSliceDatacube(int i)
+{
+
+    vtkSmartPointer<vtkLookupTable> lutSlice = vtkSmartPointer<vtkLookupTable>::New();
+    lutSlice->SetTableRange( myfits->GetRangeSlice(i)[0], myfits->GetRangeSlice(i)[1] );
+    SelectLookTable("Gray",lutSlice);
+    viewer->SetLookupTable(lutSlice);
+    viewer->SetSlice(i);
+
+
+    if(ui->contourCheckBox->isChecked())
+        goContour();
+
+    ui->minSliceLineEdit->setText(QString::number(myfits->GetRangeSlice(i)[0]));
+    ui->maxSliceLineEdit->setText(QString::number(myfits->GetRangeSlice(i)[1]));
+
+    ui->isocontourVtkWin->update();
+    viewer->GetRenderer()->ResetCamera();
+
+
+}
+
+void vtkwindow_new::changeFitsPalette(std::string palette)
+{
+
+    vtkSmartPointer<vtkLookupTable> lut = vtkSmartPointer<vtkLookupTable>::New();
+    lut->SetTableRange( myfits->GetMin(), myfits->GetMax() );
+
+    SelectLookTable(palette.c_str(),lut);
+
+    imageViewer->GetWindowLevel()->SetLookupTable(lut);
+
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::drawSingleEllipse(vtkSmartPointer<vtkLODActor> ellipseActor)
+{
+    ellipseActor->GetProperty()->SetLighting(true);
+    ellipseActor->GetProperty()->SetLineWidth(3);
+    m_Ren1->AddActor(ellipseActor);
+
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::removeSingleEllipse(vtkSmartPointer<vtkLODActor> ellipseActor)
+{
+    m_Ren1->RemoveActor(ellipseActor);
+    ui->qVTK1->update();
+}
+
+void vtkwindow_new::loadObservedObject(VisPoint * vis)
+{
+    setWindowTitle(vis->getName());
+
+    vispoint=vis;
+    table = vispoint->getOrigin();
+    QString workingPath = QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp");
+    QString OutputFilenamePoints = workingPath;
+    QString OutputFilenameImage = workingPath;
+    OutputFilenamePoints.append(QDir::separator()).append(table->getName().c_str());
+    OutputFilenameImage.append(QDir::separator()).append(table->getName().c_str());
+
+    pp = new PointsPipe(table);
+
+    QStringList fieldList;
+    fieldList << vis->getX() << vis->getY() << vis->getZ();
+
+    pp->setVtkWindow(this);
+
+    pp->createPipeFor3dSelection();
+
+    showMaximized();
+    activateWindow();
+
+}
+
+QHash<QString,  vtkSmartPointer<vtkLODActor> > vtkwindow_new::getVisualizedActorList()
+{
+    return visualized_actor_list;
+}
+
+QHash<QString,  vtkSmartPointer<vtkLODActor> > vtkwindow_new::getEllipseActorList()
+{
+    return ellipse_actor_list;
+}
+
+void vtkwindow_new::setCuttingPlaneValue(int arg1)
+{
+    sliceA->SetPosition(0,0,arg1);
+    ui->qVTK1->update();
+    if(!isDatacube)
+        scale(scaleActivate);
+    else
+        this->updateScene();
+
+}
+
+void vtkwindow_new::on_actionInfo_triggered()
+{
+    if (fitsViewer)
+    {
+
+        info->setFilename();
+        info->show();
+
+    }
+}
+
+void vtkwindow_new::on_actionTools_triggered()
+{
+    if (fitsViewer)
+    {
+
+        vtkfitstoolsw->show();
+        vtkfitstoolsw->activateWindow();
+
+    }
+    else
+    {
+        vtktoolswidget *vtktoolsw= new vtktoolswidget(this);
+        vtktoolsw->show();
+
+    }
+}
+
+void vtkwindow_new::on_resetPushButton_clicked()
+{
+    setVtkInteractorStyle3DPicker(pp->getPolyData());
+    ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->RemoveActor(selectedActor);
+
+}
+
+void vtkwindow_new::on_confirmPushButton_clicked()
+{
+    setVtkInteractorStyle3DFreehand(pp->getPolyData());
+
+}
+
+void vtkwindow_new::on_rectSelection_clicked()
+{
+    setVtkInteractorStyle3DPicker(pp->getPolyData());
+
+}
+
+void vtkwindow_new::on_freehandPushButton_clicked()
+{
+    setVtkInteractorStyle3DFreehand(pp->getPolyData());
+}
+
+void vtkwindow_new::setVtkInteractorStyleImageContour()
+{
+    /*
+    Left Mouse button triggers window level events
+    CTRL Left Mouse spins the camera around its view plane normal
+    SHIFT Left Mouse pans the camera
+    CTRL SHIFT Left Mouse dollys (a positional zoom) the camera
+    Middle mouse button pans the camera
+    Right mouse button dollys the camera.
+    SHIFT Right Mouse triggers pick events
+    */
+
+
+    vtkSmartPointer<myVtkInteractorStyleImage> style =vtkSmartPointer<myVtkInteractorStyleImage>::New();
+
+    ui->isocontourVtkWin->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->isocontourVtkWin->GetRenderWindow()->GetInteractor()->SetRenderWindow(  ui->isocontourVtkWin->GetRenderWindow() );
+    style->setVtkWin(this);
+    style->setIsSlice();
+    ui->isocontourVtkWin->setCursor(Qt::ArrowCursor);
+}
+
+void vtkwindow_new::setVtkInteractorStyleImage()
+{
+    /*
+    Left Mouse button triggers window level events
+    CTRL Left Mouse spins the camera around its view plane normal
+    SHIFT Left Mouse pans the camera
+    CTRL SHIFT Left Mouse dollys (a positional zoom) the camera
+    Middle mouse button pans the camera
+    Right mouse button dollys the camera.
+    SHIFT Right Mouse triggers pick events
+    */
+
+
+    vtkSmartPointer<myVtkInteractorStyleImage> style =vtkSmartPointer<myVtkInteractorStyleImage>::New();
+
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetRenderWindow( ui->qVTK1->GetRenderWindow() );
+
+
+    style->setVtkWin(this);
+    ui->qVTK1->setCursor(Qt::ArrowCursor);
+
+
+}
+
+void vtkwindow_new::setSkyRegionSelectorInteractorStyleFor3D()
+{
+
+    vtkSmartPointer<SkyRegionSelector> style =vtkSmartPointer<SkyRegionSelector>::New();
+    style->setVtkWin(this);
+    style->setIs3D();
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->qVTK1->setCursor(Qt::CrossCursor);
+
+}
+
+void vtkwindow_new::setSkyRegionSelectorInteractorStyle()
+{
+
+    ui->rectangularSelectionCS->setStyleSheet("background-color: rgb(0, 0, 250);border-radius: 3px; border-width: 1px;width:100%;");
+    //    ui->rectangularSelectionCS->setStyleSheet("");
+
+    ui->bubblePushButton->setStyleSheet("");
+    ui->fil_rectPushButton->setStyleSheet("");
+    ui->tdRectPushButton->setStyleSheet("");
+
+    // on_actionTools_triggered();
+
+
+    vtkSmartPointer<SkyRegionSelector> style =vtkSmartPointer<SkyRegionSelector>::New();
+    style->setVtkWin(this);
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->qVTK1->setCursor(Qt::CrossCursor);
+
+
+}
+
+void vtkwindow_new::on_PVPlotPushButton_clicked()
+{
+    setVtkInteractorContourWindow();
+}
+
+void vtkwindow_new::on_PVPlot_radioButton_clicked(bool checked)
+{
+    if(checked==true)
+        setVtkInteractorContourWindow();
+    else setVtkInteractorStyleImage();
+}
+
+void vtkwindow_new::on_contour_pushButton_clicked()
+{
+    //if (contourWin==0)
+    //contourWinActivated=true;
+    //ui->isocontourVtkWin->
+    contourWin->setFitsReader(myfits, this);
+    contourWin->show();
+}
+
+void vtkwindow_new::setVtkInteractorContourWindow()
+{
+    /*
+     Left Mouse button triggers window level events
+     SHIFT Right Mouse triggers pick events
+     */
+
+
+    vtkSmartPointer<myVtkInteractorContourWindow> style =vtkSmartPointer<myVtkInteractorContourWindow>::New();
+
+    //style->AutoAdjustCameraClippingRangeOn();
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetRenderWindow( ui->qVTK1->GetRenderWindow() );
+    style->setVtkWin(this);
+    ui->qVTK1->setCursor(Qt::ArrowCursor);
+
+}
+
+void vtkwindow_new::setVtkInteractorStyle3DPicker(vtkSmartPointer<vtkPolyData> points)
+{
+    /*
+    vtkSmartPointer<InteractorStyle> style =zaz vtkSmartPointer<InteractorStyle>::New();
+    style->SetPoints(input);
+    renderWindowInteractor->SetInteractorStyle( style );
+*/
+
+
+    vtkSmartPointer<vtkAreaPicker> areaPicker = vtkSmartPointer<vtkAreaPicker>::New();
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetPicker(areaPicker);
+
+    vtkSmartPointer<InteractorStyleSelctionPointOn3DVisualization> style =vtkSmartPointer<InteractorStyleSelctionPointOn3DVisualization>::New();
+    style->setVtkWin(this);
+    style->SetPoints(points);
+
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+
+    update();
+
+}
+
+void vtkwindow_new::setVtkInteractorStyleFreehand()
+{
+
+
+    vtkSmartPointer<vtkInteractorStyleDrawPolygon> style =vtkSmartPointer<vtkInteractorStyleDrawPolygon>::New();
+
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetRenderWindow( ui->qVTK1->GetRenderWindow() );
+    // style->setVtkWin(this);
+    //  ui->qVTK1->setCursor(Qt::ArrowCursor);
+
+}
+
+void vtkwindow_new::on_spinBox_contour_valueChanged(int arg1)
+{
+
+
+    setSliceDatacube(arg1-1);
+
+
+}
+
+void vtkwindow_new::setVtkInteractorStyle3DFreehand(vtkSmartPointer<vtkPolyData> points)
+{
+
+
+    vtkSmartPointer<vtkCellPicker> areaPicker = vtkSmartPointer<vtkCellPicker>::New();
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetPicker(areaPicker);
+
+    vtkSmartPointer<InteractorStyleFreeHandOn3DVisualization> style =vtkSmartPointer<InteractorStyleFreeHandOn3DVisualization>::New();
+    style->setVtkWin(this);
+    style->SetPoints(points);
+
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+
+    update();
+
+}
+
+void vtkwindow_new::on_cuttingPlane_Slider_sliderMoved(int position)
+{
+
+}
+
+void vtkwindow_new::setSelectionFitsViewerInteractorStyle()
+{
+
+    vtkSmartPointer<MyRubberBand> style =vtkSmartPointer<MyRubberBand>::New();
+
+    style->setVtkWin(this);
+
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->qVTK1->setCursor(Qt::CrossCursor);
+
+    //update();
+
+}
+
+void vtkwindow_new::on_horizontalSlider_threshold_valueChanged(int value)
+{
+}
+
+void vtkwindow_new::showBox(bool checked)
+{
+    if(checked)
+        pp->outlineActor->SetVisibility(true);
+    else
+        pp->outlineActor->SetVisibility(false);
+
+    ui->qVTK1->update();
+}
+
+void vtkwindow_new::addActor(vtkProp *actor)
+{
+    //back_ren->AddActor2D(actor);
+    ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->AddActor2D(actor);
+    ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->GetActors2D();
+
+    ui->qVTK1->GetRenderWindow()->Render();
+
+}
+
+void vtkwindow_new::showAxes(bool checked)
+{
+    if(checked)
+        pp->axesActor->SetVisibility(true);
+    else
+        pp->axesActor->SetVisibility(false);
+
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::showGrid(bool checked)
+{
+    pp->activateGrid(checked);
+
+}
+
+void vtkwindow_new::changeWCS(bool galaptic)
+{
+
+    if(galaptic)
+    {
+        vtkSmartPointer<vtkTransform> transform =  vtkSmartPointer<vtkTransform>::New();
+        transform->RotateY(180 );
+        vtkAxes->SetUserTransform(transform);
+    }
+    else
+    {
+        vtkSmartPointer<vtkTransform> transform =  vtkSmartPointer<vtkTransform>::New();
+        transform->RotateY(-180);
+        transform->RotateZ(-23.5);
+        vtkAxes->SetUserTransform(transform);
+    }
+    update();
+}
+
+void vtkwindow_new::plotSlice(vtkSmartPointer<vtkFitsReader> visvis, int arg1)
+{
+
+}
+
+void vtkwindow_new::on_rectangularSelectionCS_clicked()
+{
+    setSkyRegionSelectorInteractorStyle();
+}
+
+void vtkwindow_new::on_colorPushButton_clicked()
+{
+
+    vtkfitstoolwindow->show();
+
+}
+
+void vtkwindow_new::addLayer(vtkfitstoolwidgetobject *o,bool enabled)
+{
+    QList<QTableWidgetItem*> elements = ui->ElementListWidget->selectedItems();
+
+    qDebug()<<"tableWidget "<<o->getName();
+    if(elements.size()>3 && o->getSurvey().compare("")==0){
+        o->setSurvey(elements.at(0)->text());
+        o->setSpecies(elements.at(1)->text());
+        o->setTransition(elements.at(2)->text());
+
+    }
+
+
+    if (o->getType() == 0)
+    {
+
+        addImageToList(o);
+    }
+    else if (o->getType() == 1)
+    {
+        addToList( o,enabled);
+    }
+    else if (o->getType() == 2)
+    {
+        addToList( o,enabled);
+    }
+    else  if (o->getType() == 3)
+    {
+        addToList(o,enabled);
+    }
+
+    // ui->tableWidget->horizontalHeader()->sectionResizeMode(QHeaderView::Stretch);
+
+    ui->tableWidget->resizeColumnsToContents();
+
+}
+
+QTreeWidgetItem* vtkwindow_new::addTreeRoot(QString name)
+{
+    QTreeWidgetItem * topLevel = new QTreeWidgetItem();
+
+    return topLevel;
+}
+
+void vtkwindow_new::addTreeChild(QTreeWidgetItem *parent, QString name, QBrush brush)
+{
+
+    QTreeWidgetItem *treeItem = new QTreeWidgetItem();
+
+
+    treeItem->setBackground(0,brush);
+    treeItem->setText(1, name);
+    //mod
+    treeItem->setFlags(treeItem->flags() | Qt::ItemIsUserCheckable);
+    treeItem->setCheckState(0,Qt::Checked);
+
+    //end
+    parent->addChild(treeItem);
+
+
+}
+
+void vtkwindow_new::addImageToList( vtkfitstoolwidgetobject *o)
+{
+    imgLayerList.append(o);
+
+
+    o->setLayerNumber(imageStack->GetImages()->GetNumberOfItems()-1);
+
+    QListWidgetItem* item = new QListWidgetItem(o->getSurvey()+"_"+o->getSpecies()+"_"+o->getTransition(), ui->listWidget);
+    item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag
+    item->setCheckState(Qt::Checked); // AND initialize check state
+
+
+
+
+
+
+}
+
+
+//QUI FV - Aggiunta livelli per sorgenti
+
+void vtkwindow_new::addToList(vtkfitstoolwidgetobject *o, bool enabled)
+{
+    elementLayerList.append(o);
+
+
+    QSignalMapper* mapper = new QSignalMapper(this);
+    QSignalMapper* mapper_slider = new QSignalMapper(this);
+
+    int row= ui->tableWidget->model()->rowCount();
+    ui->tableWidget->insertRow(row);
+
+    QCheckBox  *cb1= new QCheckBox();
+
+    if(enabled)
+        cb1->setChecked(true);
+    else
+        cb1->setChecked(false);
+
+    if (o->getType()!=3)
+    {
+        double r=o->getActor()->GetProperty()->GetColor()[0]*255;
+        double g=o->getActor()->GetProperty()->GetColor()[1]*255;
+        double b=o->getActor()->GetProperty()->GetColor()[2]*255;
+
+        cb1->setStyleSheet("background-color: rgb("+QString::number(r)+","+QString::number(g)+" ,"+QString::number(b)+")");
+
+        /*
+        //FV MODIFICARE PER AGGIUNGERE COLORE E GESTIONE CHECKBOX
+        QListWidgetItem* item = new QListWidgetItem(o->getName());
+        item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag
+        item->setCheckState(Qt::Checked); // AND initialize check state
+        QColor c1(r,g,b);
+        QBrush b1(c1);
+        item->setForeground(b1);
+        ui->listWidget->addItem(item);
+
+*/
+
+
+    }
+    ui->tableWidget->setCellWidget(row,0,cb1);
+
+    connect(cb1, SIGNAL(stateChanged(int)),mapper, SLOT(map()));
+    mapper->setMapping(cb1, row);
+
+
+    QTableWidgetItem *item_1 = new QTableWidgetItem();
+    item_1->setFlags(item_1->flags() ^ Qt::ItemIsEditable);
+
+    item_1->setText(o->getName() );
+
+    ui->tableWidget->setItem(row,1,item_1);
+
+    connect(mapper, SIGNAL(mapped(int)), this, SLOT(checkboxClicked(int)));
+
+}
+
+void vtkwindow_new::checkboxImageClicked(int cb)
+{
+    if( vtkImageSlice::SafeDownCast( imageStack->GetImages()->GetItemAsObject(cb))->GetVisibility())
+        vtkImageSlice::SafeDownCast(imageStack->GetImages()->GetItemAsObject(cb))->VisibilityOff();
+    else
+        vtkImageSlice::SafeDownCast(imageStack->GetImages()->GetItemAsObject(cb))->VisibilityOn();
+
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::checkboxClicked(int cb,bool status)
+{
+
+    /*
+    if (!status)
+        getVisualizedActorList().value(ui->listWidget->item(cb)->text())->VisibilityOff();
+    else
+        getVisualizedActorList().value(ui->listWidget->item(cb)->text())->VisibilityOn();
+
+    ui->qVTK1->update();
+*/
+
+    if(getVisualizedActorList().value(ui->tableWidget->item(cb, 1)->text())->GetVisibility())
+        getVisualizedActorList().value(ui->tableWidget->item(cb, 1)->text())->VisibilityOff();
+    else
+        getVisualizedActorList().value(ui->tableWidget->item(cb, 1)->text())->VisibilityOn();
+
+    ui->qVTK1->update();
+
+}
+
+
+void vtkwindow_new::on_tableWidget_doubleClicked(const QModelIndex &index)
+{
+
+
+    // if(index.column()==0)
+    if (elementLayerList.at(index.row())->getType()==0)
+    {
+        //settaggi dell'immagine selezionata
+    }
+    else
+    {
+
+        //Initial color
+        double r=getVisualizedActorList().value(ui->tableWidget->item( index.row(), 1)->text())->GetProperty()->GetColor()[0]*255;
+        double g=getVisualizedActorList().value(ui->tableWidget->item(index.row() , 1)->text())->GetProperty()->GetColor()[1]*255;
+        double b=getVisualizedActorList().value(ui->tableWidget->item( index.row() , 1)->text())->GetProperty()->GetColor()[2]*255;
+
+        QColor color = QColorDialog::getColor(QColor(r,g,b), this);
+        getVisualizedActorList().value(ui->tableWidget->item( index.row(), 1)->text())->GetProperty()->SetColor(color.redF(), color.greenF(), color.blueF());
+        ui->qVTK1->update();
+
+        //update color on table
+        ui->tableWidget->cellWidget(index.row(),0)->setStyleSheet("background-color: rgb("+QString::number(color.redF()*255)+","+QString::number(color.greenF()*255)+" ,"+QString::number(color.blueF()*255)+")");
+    }
+
+}
+
+void vtkwindow_new::on_fil_rectPushButton_clicked()
+{
+    ui->fil_rectPushButton->setStyleSheet("background-color: rgb(0, 0, 250);border-radius: 3px; border-width: 1px;");
+    ui->rectangularSelectionCS->setStyleSheet("");
+    ui->tdRectPushButton->setStyleSheet("");
+    ui->bubblePushButton->setStyleSheet("");
+
+    vtkSmartPointer<SkyRegionSelector> style =vtkSmartPointer<SkyRegionSelector>::New();
+    style->setVtkWin(this);
+    style->setIsFilament();
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->qVTK1->setCursor(Qt::CrossCursor);
+}
+
+void vtkwindow_new::setDbElements(QList<QMap<QString, QString> > elementsOnDb)
+{
+
+    classElementsOnDb=elementsOnDb;
+
+    int i=0;
+    while(!elementsOnDb.isEmpty())
+    {
+
+
+        QMap<QString,QString> datacube = elementsOnDb.takeFirst();
+
+        qDebug()<<datacube<<"\n\n\n";
+
+        //datacubes_list->append(datacube);
+
+        ui->ElementListWidget->insertRow(i);
+        QTableWidgetItem *item_0 = new QTableWidgetItem();
+        item_0->setFlags(item_0->flags() ^ Qt::ItemIsEditable);
+        item_0->setText(datacube["Survey"]+"\n"+datacube["Species"]);
+        ui->ElementListWidget->setItem(i,0,item_0);
+
+        QTableWidgetItem *item_1 = new QTableWidgetItem();
+        item_1->setFlags(item_1->flags() ^ Qt::ItemIsEditable);
+        QString codeString="";
+        switch (datacube["code"].toInt())
+        {
+            case 2:
+             codeString="The Region is completely inside the input";
+            break;
+        case 3:
+         codeString="Full Overlap";
+        break;
+        case 4:
+               codeString="Partial Overlap";
+                    break;
+        case 5:
+               codeString="The Regions are identical ";
+            break;
+         default :
+            codeString="Merge";
+            break;
+        }
+
+        item_1->setText(datacube["Transition"]+"\n"+codeString);
+
+     //   item_1->setText(datacube["Species"]);
+        ui->ElementListWidget->setItem(i,1,item_1);
+
+     /*   QTableWidgetItem *item_2 = new QTableWidgetItem();
+        item_2->setFlags(item_2->flags() ^ Qt::ItemIsEditable);
+       // item_2->setText(datacube["Transition"]);
+        ui->ElementListWidget->setItem(i,2,item_2);
+
+        QTableWidgetItem *item_3 = new QTableWidgetItem();
+        item_3->setFlags(item_3->flags() ^ Qt::ItemIsEditable);
+        item_3->setText(datacube["code"]);
+        ui->ElementListWidget->setItem(i,3,item_3);
+*/
+        if (datacube["code"].toDouble()==3)
+        {
+            ui->ElementListWidget->item(i, 0)->setBackground(Qt::green);
+            ui->ElementListWidget->item(i, 1)->setBackground(Qt::green);
+       //     ui->ElementListWidget->item(i, 2)->setBackground(Qt::green);
+        //    ui->ElementListWidget->item(i, 3)->setBackground(Qt::green);
+
+
+        }
+
+        item_0->setToolTip(datacube["Description"]);
+        item_1->setToolTip(datacube["Description"]);
+      //  item_2->setToolTip(datacube["Description"]);
+      //  item_3->setToolTip(datacube["Description"]);
+        i++;
+
+
+
+    }
+
+    ui->ElementListWidget->setWordWrap(true);
+
+    ui->ElementListWidget->setTextElideMode(Qt::ElideMiddle);
+
+    ui->ElementListWidget->resizeColumnsToContents();
+    ui->ElementListWidget->resizeRowsToContents();
+}
+
+
+void vtkwindow_new::addLayerImage(vtkSmartPointer<vtkFitsReader> vis, QString survey, QString species,QString transition)
+{
+    vtkSmartPointer<vtkImageShiftScale> resultScale = vtkSmartPointer<vtkImageShiftScale>::New();
+    resultScale->SetOutputScalarTypeToUnsignedChar();
+    resultScale->SetInputData( vis->GetOutput() );
+
+    resultScale->Update();
+
+    vtkSmartPointer<vtkLookupTable> lut = vtkSmartPointer<vtkLookupTable>::New();
+
+    min=vis->GetMin();
+    max= vis->GetMax();
+
+    if (min<=0)
+    {
+        min=1;
+
+    }
+
+    lut->SetTableRange(  min , max );
+
+    lut->SetScaleToLog10();
+
+    SelectLookTable("Gray",lut);
+    imageObject->setLutScale("Log");
+    imageObject->setLutType("Gray");
+
+
+    vtkfitstoolwidgetobject *img=new vtkfitstoolwidgetobject(0);
+
+    qDebug()<<"AGGIUNG_"<<QString::fromUtf8(vis->GetFileName().c_str()) << " s "<<survey<<" sp "<<species<<" t "<<transition;
+    img->setName(QString::fromUtf8(vis->GetFileName().c_str()));
+    img->setFitsReader(vis);
+    if (survey.compare("")!=0 && species.compare("")!=0 && transition.compare("")!=0)
+    {
+        img->setSpecies(species);
+        img->setTransition(transition);
+        img->setSurvey(survey);
+    }
+
+    double scaledPixel=AstroUtils().arcsecPixel(vis->GetFileName())/AstroUtils().arcsecPixel(myfits->GetFileName());
+    vis->GetOutput()->SetSpacing(scaledPixel,scaledPixel,1);
+
+
+    double *sky_coord_gal = new double[2];
+    AstroUtils().xy2sky(vis->GetFileName(),0,0,sky_coord_gal,3);
+    qDebug()<<"0.0 xy2sky "<<sky_coord_gal[0]<<sky_coord_gal[1];
+
+    double *coord= new double[3];
+    AstroUtils().sky2xy(myfits->GetFileName(), sky_coord_gal[0], sky_coord_gal[1], coord);
+    vis->GetOutput()->SetOrigin( coord[0],coord[1],0);
+    qDebug()<<"0.0 sky2xy "<<coord[0]<<coord[1];
+
+    vtkSmartPointer<vtkImageMapToColors> colors =  vtkSmartPointer<vtkImageMapToColors>::New();
+    colors->SetInputData(vis->GetOutput());
+    colors->SetLookupTable(lut);
+    colors->Update();
+
+    vtkSmartPointer<vtkImageSliceMapper> imageSliceMapperLayer = vtkSmartPointer<vtkImageSliceMapper>::New();
+    imageSliceMapperLayer->SetInputData(colors->GetOutput());
+
+    vtkSmartPointer<vtkImageSlice> imageSliceLayer = vtkSmartPointer<vtkImageSlice>::New();
+    imageSliceLayer->SetMapper(imageSliceMapperLayer);
+    imageSliceLayer->GetProperty()->SetOpacity(0.5);
+    imageSliceLayer->GetProperty()->SetInterpolationTypeToNearest();
+
+    double angle  = 0;
+
+    double x1=coord[0];
+    double y1=coord[1];
+
+    AstroUtils().xy2sky(vis->GetFileName(),0,100,sky_coord_gal,3);
+    AstroUtils().sky2xy(myfits->GetFileName(), sky_coord_gal[0], sky_coord_gal[1], coord);
+
+    qDebug()<<"0.100 xy2sky "<<sky_coord_gal[0]<<sky_coord_gal[1];
+    qDebug()<<"0.100 sky2xy "<<coord[0]<<coord[1];
+
+    if (x1!=coord[0])
+    {
+        double m = fabs((coord[1]-y1)/(coord[0]-x1));
+        angle =1*( 90 - atan(m)*180/M_PI);
+        qDebug()<<"m="<<m<<" angle "<<angle;
+
+    }
+
+    double bounds[6];
+
+    vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
+    vis->GetOutput()->GetBounds(bounds);
+
+    // Rotate about the origin point (world coordinates)
+    transform->Translate(bounds[0], bounds[2], bounds[4]);
+    transform->RotateWXYZ(angle, 0, 0, 1);
+    transform->Translate(-bounds[0], -bounds[2], -bounds[4]);
+    imageSliceLayer->SetUserTransform(transform);
+
+    imageSliceLayer->GetProperty()->SetLayerNumber(imageStack->GetImages()->GetNumberOfItems());
+
+    imageStack->AddImage(imageSliceLayer);
+
+    addLayer(img);
+
+    if(rectangleActor!=0){
+        m_Ren1->RemoveActor(rectangleActor);
+    }
+
+
+    this->update();
+    activateWindow();
+
+}
+
+void vtkwindow_new::downloadStartingLayers(QList < QPair<QString, QString> > selectedSurvey)
+{
+
+
+    for(int i=1;i<selectedSurvey.size();i++)
+    {
+        qDebug()<<selectedSurvey.at(i).first<< " "<<selectedSurvey.at(i).second;
+        VialacteaInitialQuery *vq= new VialacteaInitialQuery();
+        vq->setCallingVtkWindow(this);
+
+        vq->setL(called_l);
+        vq->setB(called_b);
+
+
+        QString sn=selectedSurvey.at(i).first;
+
+        qDebug()<<"SN: "<<sn;
+        if (sn.compare("Hi-GAL")==0)
+        {
+            //http://ia2-vialactea.oats.inaf.it:8080/libjnifitsdb-1.0.2/vlkb_search?surveyname=Hi-GAL%20Tiles%22%20OR%20name%20=%20%22Hi-GAL%20Mosaic&l=40&b=0&species=Continuum&transition=250%20um&r=0.3&vl=0&vu=0
+
+            sn="Hi-GAL Tiles\" OR name = \"Hi-GAL Mosaic";
+        }
+        qDebug()<<"Richiedo Layer";
+
+
+        QString urlString=vlkbUrl+"/vlkb_search?surveyname="+QUrl::toPercentEncoding(sn)+"&l="+called_l+"&b="+called_b+"&species=Continuum&transition="+QUrl::toPercentEncoding(selectedSurvey.at(i).second);
+        if(called_dl!="" && called_db!="")
+        {
+            urlString+="&dl="+called_dl+"&db="+called_db;
+            vq->setDeltaRect(called_dl, called_db);
+
+        }
+        else
+        {
+            urlString+="&r="+called_r;
+            vq->setR(called_r);
+
+        }
+        urlString+="&vl=0&vu=0";
+
+        qDebug()<<urlString;
+        QUrl url (urlString);
+
+
+
+
+
+        vq->setTransition(selectedSurvey.at(i).second);
+        vq->setSpecies("Continuum");
+        vq->setSurveyname(selectedSurvey.at(i).first);
+
+
+
+        vq->selectedStartingLayersRequest(url);
+
+        this->activateWindow();
+
+
+
+    }
+
+}
+
+
+void vtkwindow_new::handleButton(int i)
+{
+    QMap<QString,QString> datacube=classElementsOnDb[i];
+
+    qDebug()<<datacube;
+
+    QString url_string= datacube["URL"];
+
+      QUrl url (url_string);
+
+    VialacteaInitialQuery *vq= new VialacteaInitialQuery();
+    vq->setCallingVtkWindow(this);
+    vq->cutoutRequest(url_string,classElementsOnDb,i);
+    this->activateWindow();
+
+}
+
+void vtkwindow_new::on_lutComboBox_activated(const QString &arg1)
+{
+    changeFitsScale(ui->lutComboBox->currentText().toStdString().c_str(), selected_scale.toStdString().c_str());
+}
+
+void vtkwindow_new::on_logRadioButton_toggled(bool checked)
+{
+    if(checked)
+        selected_scale="Log";
+    else
+        selected_scale="Linear";
+
+    changeFitsScale(ui->lutComboBox->currentText().toStdString().c_str(), selected_scale.toStdString().c_str());
+
+}
+
+void vtkwindow_new::on_tdRectPushButton_clicked()
+{
+    ui->tdRectPushButton->setStyleSheet("background-color: rgb(0, 0, 250);border-radius: 3px; border-width: 1px;");
+    ui->fil_rectPushButton->setStyleSheet("");
+    ui->rectangularSelectionCS->setStyleSheet("");
+    ui->bubblePushButton->setStyleSheet("");
+
+    vtkSmartPointer<SkyRegionSelector> style =vtkSmartPointer<SkyRegionSelector>::New();
+    style->setVtkWin(this);
+    style->setIs3dSelections();
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->qVTK1->setCursor(Qt::CrossCursor);
+}
+
+void vtkwindow_new::on_ElementListWidget_doubleClicked(const QModelIndex &index)
+{
+    handleButton(index.row());
+}
+
+void vtkwindow_new::on_thresholdValueLineEdit_editingFinished()
+{
+
+    shellE->SetValue(0,  ui->thresholdValueLineEdit->text().toFloat());
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::on_upperBoundLineEdit_editingFinished()
+{
+    if(ui->contourCheckBox->isChecked())
+        goContour();
+}
+
+void vtkwindow_new::on_lowerBoundLineEdit_editingFinished()
+{
+    if(ui->contourCheckBox->isChecked())
+        goContour();
+}
+
+void vtkwindow_new::goContour()
+{
+    removeContour();
+
+    vtkSmartPointer<vtkPlane> plane = vtkSmartPointer<vtkPlane>::New();
+    plane->SetOrigin(0,0, viewer->GetSlice());
+    plane->SetNormal(0,0,1);
+
+    vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
+    cutter->SetCutFunction(plane);
+    cutter->SetInputData(myfits->GetOutput());
+    cutter->Update();
+
+    vtkSmartPointer<vtkPolyDataMapper> cutterMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    cutterMapper->SetInputConnection( cutter->GetOutputPort());
+
+    vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
+    vtkSmartPointer<vtkContourFilter> contoursFilter = vtkSmartPointer<vtkContourFilter>::New();
+    polyData = cutter->GetOutput();
+
+    contoursFilter->GenerateValues(ui->levelLineEdit->text().toInt(), ui->lowerBoundLineEdit->text().toDouble(), ui->upperBoundLineEdit->text().toDouble());
+    contoursFilter->SetInputConnection(cutter->GetOutputPort());
+
+    vtkSmartPointer<vtkPolyDataMapper> contourLineMapperer = vtkSmartPointer<vtkPolyDataMapper>::New();
+    contourLineMapperer->SetInputConnection(contoursFilter->GetOutputPort());
+    contourLineMapperer->SetScalarRange(ui->lowerBoundLineEdit->text().toDouble(), ui->upperBoundLineEdit->text().toDouble());
+    contourLineMapperer->ScalarVisibilityOn();
+    contourLineMapperer->SetScalarModeToUsePointData();
+    contourLineMapperer->SetColorModeToMapScalars();
+
+
+    currentContourActor->SetMapper(contourLineMapperer);
+    currentContourActor->GetProperty()->SetLineWidth(1);
+
+    ui->isocontourVtkWin->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->AddActor2D(currentContourActor);
+
+    //TEST FV
+
+
+    if(myParentVtkWindow!=0){
+
+        //Riporto i contorni su visualizzazione principale
+        vtkfitstoolwidgetobject *img=new vtkfitstoolwidgetobject(3);
+        img->setName("isocontour");
+
+
+
+        double *sky_coord_gal = new double[2];
+        AstroUtils().xy2sky(myfits->GetFileName(),0,0,sky_coord_gal,3);
+        double *coord= new double[3];
+        AstroUtils().sky2xy(myParentVtkWindow->myfits->GetFileName(), sky_coord_gal[0], sky_coord_gal[1], coord);
+
+        double angle  = 0;
+
+        double x1=coord[0];
+        double y1=coord[1];
+
+        AstroUtils().xy2sky(myfits->GetFileName(),0,100,sky_coord_gal,3);
+        AstroUtils().sky2xy(myParentVtkWindow->myfits->GetFileName(), sky_coord_gal[0], sky_coord_gal[1], coord);
+
+
+        if (x1!=coord[0])
+        {
+            double m = fabs((coord[1]-y1)/(coord[0]-x1));
+            angle =90- atan(m)*180/M_PI;
+        }
+
+        double bounds[6];
+
+        vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
+        myfits->GetOutput()->GetBounds(bounds);
+
+        // Rotate about the origin point (world coordinates)
+        transform->Translate( 0,0, -1*viewer->GetSlice());
+        transform->Translate(bounds[0], bounds[2], 0);
+        transform->RotateWXYZ(angle, 0, 0, 1);
+        transform->Translate(-bounds[0], -bounds[2], 0);
+        // transform->Translate( 0,0, -1*viewer->GetSlice());
+        double scaledPixel=AstroUtils().arcsecPixel(myfits->GetFileName())/AstroUtils().arcsecPixel(myParentVtkWindow->myfits->GetFileName());
+
+
+        /*
+
+
+    viewer->GetImageActor()->Update();
+    vtkSmartPointer<vtkImageData> imgDataContourForMainWindow=  vtkSmartPointer<vtkImageData>::New();
+    imgDataContourForMainWindow->ShallowCopy(viewer->GetImageActor()->GetInput());
+
+    double scaledPixel=AstroUtils().arcsecPixel(myfits->GetFileName())/AstroUtils().arcsecPixel(myParentVtkWindow->myfits->GetFileName());
+    imgDataContourForMainWindow->SetSpacing(scaledPixel,scaledPixel,1);
+
+
+    double *sky_coord_gal = new double[2];
+    AstroUtils().xy2sky(myfits->GetFileName(),0,0,sky_coord_gal,3);
+    double *coord= new double[3];
+    AstroUtils().sky2xy(myParentVtkWindow->myfits->GetFileName(), sky_coord_gal[0], sky_coord_gal[1], coord);
+
+
+    imgDataContourForMainWindow->SetOrigin( x1,y1,0);
+
+
+    vtkSmartPointer<vtkImageSliceMapper> imageSliceMapperLayer = vtkSmartPointer<vtkImageSliceMapper>::New();
+    imageSliceMapperLayer->SetInputData(imgDataContourForMainWindow);
+
+
+    vtkSmartPointer<vtkImageSlice> imageSliceLayer = vtkSmartPointer<vtkImageSlice>::New();
+    imageSliceLayer->SetMapper(imageSliceMapperLayer);
+    imageSliceLayer->GetProperty()->SetOpacity(1.0);
+    imageSliceLayer->GetProperty()->SetInterpolationTypeToNearest();
+
+
+    imageSliceLayer->SetUserTransform(transform);
+
+    vtkSmartPointer<vtkLookupTable> lut = vtkSmartPointer<vtkLookupTable>::New();
+    lut->SetScaleToLinear();
+    lut->SetTableRange( myfits->GetRangeSlice(viewer->GetSlice())[0], myfits->GetRangeSlice(viewer->GetSlice())[1] );
+
+    SelectLookTable("Gray",lut);
+
+    img->setLutScale("Log");
+    img->setLutType("Gray");
+
+    imageSliceLayer->GetProperty()->SetLookupTable(lut);
+    imageSliceLayer->GetProperty()->UseLookupTableScalarRangeOn();
+   // myParentVtkWindow->imageStack->AddImage(imageSliceLayer);
+
+    //myParentVtkWindow-> addLayer(img);
+
+
+*/
+
+        currentContourActorForMainWindow ->ShallowCopy(currentContourActor);
+        currentContourActorForMainWindow->SetScale(scaledPixel,scaledPixel,1);
+        // currentContourActorForMainWindow-> SetOrigin(x1,y1,0);
+        currentContourActorForMainWindow-> SetPosition(x1,y1,1);
+        currentContourActorForMainWindow->SetUserTransform(transform);
+
+        // myParentVtkWindow-> addLayer(img);
+
+
+        myParentVtkWindow->m_Ren1->AddActor2D(currentContourActorForMainWindow);
+        //myParentVtkWindow->m_Ren1->Render();
+        myParentVtkWindow->ui->qVTK1->update();
+    }
+
+
+
+
+    //END
+
+
+    // myParentVtkWindow->m_Ren1->AddActor(currentContourActor);
+    // myParentVtkWindow->imageViewer->GetImageActor()->add
+
+    // myParentVtkWindow->m_Ren1->AddActor(currentContourActor);
+
+    // myParentVtkWindow->imageViewer->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->AddActor2D(currentContourActor);
+    // ui->isocontourVtkWin->update();
+
+}
+
+void vtkwindow_new::on_levelLineEdit_editingFinished()
+{
+    if(ui->contourCheckBox->isChecked())
+        goContour();
+}
+
+void vtkwindow_new::removeContour()
+{
+    //ui->isocontourVtkWin->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->RemoveActor2D(currentContourActor);
+    m_Ren2->RemoveActor2D(currentContourActor);
+    if(myParentVtkWindow!=0){
+        myParentVtkWindow->m_Ren1->RemoveActor2D(currentContourActorForMainWindow);
+        myParentVtkWindow->ui->qVTK1->update();
+    }
+    ui->isocontourVtkWin->update();
+}
+
+void vtkwindow_new::on_contourCheckBox_clicked(bool checked)
+{
+    if(checked)
+        goContour();
+    else
+        removeContour();
+}
+
+void vtkwindow_new::on_lut3dActivateCheckBox_clicked(bool checked)
+{
+    ui->linear3dRadioButton->setEnabled(checked);
+    ui->log3dRadioButton->setEnabled(checked);
+    ui->lut3dComboBox->setEnabled(checked);
+    ui->scalarComboBox->setEnabled(checked);
+    ui->toolButton->setEnabled(checked);
+    //ui->glyphActivateCheckBox->setEnabled(checked);
+    selected_scale="Linear";
+    changeScalar(ui->scalarComboBox->currentText().toStdString().c_str());
+    // changePalette(ui->lut3dComboBox->currentText().toStdString().c_str());
+
+    showColorbar(checked);
+
+    if(ui->glyphActivateCheckBox->isChecked()){
+        ui->glyphShapeComboBox->activated(ui->glyphShapeComboBox->currentIndex());
+    }
+
+
+}
+
+void vtkwindow_new::on_scalarComboBox_activated(const QString &arg1)
+{
+    changeScalar(arg1.toStdString());
+    if(ui->glyphShapeComboBox->isEnabled()){
+        ui->glyphShapeComboBox->activated(ui->glyphShapeComboBox->currentIndex());
+    }
+}
+
+void vtkwindow_new::on_lut3dComboBox_activated(const QString &arg1)
+{
+    changePalette(ui->lut3dComboBox->currentText().toStdString().c_str());
+}
+
+void vtkwindow_new::on_toolButton_clicked()
+{
+    LutCustomize *lcustom= new LutCustomize(this);
+    lcustom->show();
+}
+
+void vtkwindow_new::on_glyphActivateCheckBox_clicked(bool checked)
+{
+
+    //    glyphLineEdit;
+
+    QSettings settings(QDir::homePath().append(QDir::separator()).append("VisIVODesktopTemp").append("/setting.ini"), QSettings::NativeFormat);
+
+    int maxpoint =  settings.value("glyphmax", "").toString().toInt() ;
+
+    if(checked)
+    {
+        int nPoints=pp->getRows();
+        if(nPoints<=maxpoint){
+            ui->glyphShapeComboBox->setEnabled(true);
+            ui->glyphScalarComboBox->setEnabled(true);
+            ui->glyphScalingLineEdit->setEnabled(true);
+            ui->glyphShapeComboBox->activated(ui->glyphShapeComboBox->currentIndex());
+        }else{
+            QMessageBox::critical(NULL, QObject::tr("Error"), QObject::tr("Too many points. Glyph will not be displayed."));
+            ui->glyphActivateCheckBox->setChecked(false);
+        }
+    }else{
+        if(glyph_actor!=0){
+
+            ui->glyphShapeComboBox->setEnabled(false);
+            ui->glyphScalarComboBox->setEnabled(false);
+            ui->glyphScalingLineEdit->setEnabled(false);
+            m_Ren1->RemoveActor(glyph_actor);
+            glyph_actor=0;
+            ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->Render();
+            ui->qVTK1->update();
+        }
+    }
+}
+
+void vtkwindow_new::on_linear3dRadioButton_toggled(bool checked)
+{
+    if(checked)
+        selected_scale="Linear";
+    else
+        selected_scale="Log";
+
+
+    pp->scale = selected_scale.toStdString();
+
+    pp->colorScalar = ui->scalarComboBox->currentText().toStdString();
+    pp->palette = ui->lut3dComboBox->currentText().toStdString();
+
+
+    pp->setLookupTableScale();
+
+    //  changeFitsScale(ui->lutComboBox->currentText().toStdString().c_str(), selected_scale.toStdString().c_str());
+
+}
+
+void vtkwindow_new::drawGlyphs(int index){
+    vtkSmartPointer<vtkGlyph3D> glyph3D =  vtkSmartPointer<vtkGlyph3D>::New();
+
+    if(index==0){
+
+        vtkSmartPointer<vtkSphereSource> sphereSource = vtkSmartPointer<vtkSphereSource>::New();
+        sphereSource->SetThetaResolution(20);
+        sphereSource->SetPhiResolution(10);
+        sphereSource->SetRadius(1);
+        glyph3D->SetSourceConnection(sphereSource->GetOutputPort());
+    }
+    if(index==1){
+        vtkSmartPointer<vtkConeSource> coneSource   = vtkSmartPointer<vtkConeSource>::New();
+        coneSource->SetResolution(10);
+        coneSource->SetRadius(1);
+        coneSource->SetHeight(1);
+        glyph3D->SetSourceConnection(coneSource->GetOutputPort());
+    }
+    if(index==2){
+        vtkSmartPointer<vtkCylinderSource> sourceCylinder   = vtkCylinderSource::New();
+        sourceCylinder->SetResolution(10);
+        sourceCylinder->SetRadius(1);
+        sourceCylinder->SetHeight(1);
+        glyph3D->SetSourceConnection(sourceCylinder->GetOutputPort());
+    }
+    if(index==3){
+        vtkSmartPointer<vtkCubeSource> cubeSource =  vtkSmartPointer<vtkCubeSource>::New();
+        cubeSource->SetXLength (1);
+        cubeSource->SetYLength (1);
+        cubeSource->SetZLength (1);
+        glyph3D->SetSourceConnection(cubeSource->GetOutputPort());
+    }
+    std::string glyph_scalar = ui->glyphScalarComboBox->currentText().toStdString();
+    std::string color_scalar = ui->scalarComboBox->currentText().toStdString().c_str();
+
+
+    pp->addScalar(glyph_scalar,false);
+    if(ui->lut3dActivateCheckBox->isChecked()){
+        //pp->colorScalar=color_scalar;
+        //pp->initLut();
+        pp->addScalar(color_scalar,true);
+    }
+
+
+    vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
+    polyData->SetPoints(pp->getPolyData()->GetPoints());
+    qDebug()<<"Glyph Scalar "<<QString::fromStdString(glyph_scalar);
+    vtkSmartPointer<vtkDataArray> data =pp->getPolyData()->GetPointData()->GetScalars("scaleGlyph");
+    if(data!=0){
+        double range[2];
+        data->GetRange(range);
+        qDebug()<<range[0]<<" "<<range[1];
+    }
+
+    polyData->GetPointData()->SetScalars(pp->getPolyData()->GetPointData()->GetScalars("scaleGlyph"));
+    if(ui->lut3dActivateCheckBox->isChecked()){
+        qDebug()<<"Color Scalar "<<QString::fromStdString(color_scalar);
+        vtkSmartPointer<vtkDataArray> data =pp->getPolyData()->GetPointData()->GetArray(color_scalar.c_str());
+        if(data!=0){
+            double range[2];
+            data->GetRange(range);
+            qDebug()<<range[0]<<" "<<range[1];
+        }
+        polyData->GetPointData()->AddArray(pp->getPolyData()->GetPointData()->GetArray(color_scalar.c_str()));
+        pp->getPolyData()->GetPointData()->RemoveArray(color_scalar.c_str());
+    }
+
+
+
+    //polyData->GetPointData()->SetActiveScalars(glyph_scalar.c_str());
+    glyph3D->SetInputData(polyData);
+
+    //glyph3D->SetInputData(pp->getPolyData());
+
+    //glyph3D->ClampingOn();
+    //glyph3D->SetRange(-0.5, 100.0);
+
+    glyph3D->ScalingOn();
+    //glyph3D->SetScaleModeToScaleByVector();
+
+    glyph3D->SetScaleModeToScaleByScalar();
+    glyph3D->SetInputArrayToProcess(0,0,0,0,"scaleGlyph");
+
+    if(ui->lut3dActivateCheckBox->isChecked()){
+        glyph3D->SetColorModeToColorByScalar();
+        glyph3D->SetInputArrayToProcess(3,0,0,0,color_scalar.c_str());
+    }
+
+
+    double scaleFactor=ui->glyphScalingLineEdit->text().toDouble();
+    glyph3D->SetScaleFactor(scaleFactor);
+
+
+    glyph3D->Update();
+
+    // Visualize
+    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputConnection(glyph3D->GetOutputPort());
+
+    if(ui->lut3dActivateCheckBox->isChecked()){
+        //ES
+        changeScalar(ui->scalarComboBox->currentText().toStdString().c_str());
+        changePalette(ui->lut3dComboBox->currentText().toStdString().c_str());
+        // End ES
+        vtkSmartPointer<vtkLookupTable> lut=pp->getLookupTable();
+        //double *prevRange=new double[2];
+        //prevRange=lut->GetRange();
+        //lut->SetRange(-0.5,100.0);
+
+        //mapper->SetColorModeToMapScalars();
+        //mapper->ScalarVisibilityOn();
+        //mapper->ColorByArrayComponent(ui->scalarComboBox->currentText().toStdString().c_str(),1);
+
+        mapper->SetLookupTable(lut);
+        mapper->SetScalarRange(lut->GetRange());
+
+        //mapper->SetScalarRange(prevRange);
+    }
+
+    mapper->Update();
+
+    if(glyph_actor!=0){
+        m_Ren1->RemoveActor(glyph_actor);
+    }
+    glyph_actor = vtkSmartPointer<vtkActor>::New();
+    glyph_actor->SetMapper(mapper);
+
+
+    m_Ren1->AddActor ( glyph_actor );
+
+    //FV
+
+    // ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->Render();
+    // ui->qVTK1->update();
+
+}
+
+
+/*
+void vtkwindow_new::drawGlyphs(int index){
+    vtkSmartPointer<vtkGlyph3D> glyph3D =  vtkSmartPointer<vtkGlyph3D>::New();
+    if(index==0){
+        qDebug()<<"Sphere ";
+        vtkSmartPointer<vtkSphereSource> sphereSource = vtkSmartPointer<vtkSphereSource>::New();
+        sphereSource->SetThetaResolution(20);
+        sphereSource->SetPhiResolution(10);
+        sphereSource->SetRadius(1);
+        glyph3D->SetSourceConnection(sphereSource->GetOutputPort());
+    }
+    if(index==1){
+        vtkSmartPointer<vtkConeSource> coneSource   = vtkSmartPointer<vtkConeSource>::New();
+        coneSource->SetResolution(10);
+        coneSource->SetRadius(1);
+        coneSource->SetHeight(1);
+        glyph3D->SetSourceConnection(coneSource->GetOutputPort());
+    }
+    if(index==2){
+        vtkSmartPointer<vtkCylinderSource> sourceCylinder   = vtkCylinderSource::New();
+        sourceCylinder->SetResolution(10);
+        sourceCylinder->SetRadius(1);
+        sourceCylinder->SetHeight(1);
+        glyph3D->SetSourceConnection(sourceCylinder->GetOutputPort());
+    }
+    if(index==3){
+        vtkSmartPointer<vtkCubeSource> cubeSource =  vtkSmartPointer<vtkCubeSource>::New();
+        cubeSource->SetXLength (1);
+        cubeSource->SetYLength (1);
+        cubeSource->SetZLength (1);
+        glyph3D->SetSourceConnection(cubeSource->GetOutputPort());
+    }
+
+    //vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
+    //polyData->SetPoints(pp->getPolyData()->GetPoints());
+    //std::string glyph_scalar = ui->glyphScalarComboBox->currentText().toStdString();
+    //qDebug()<<"Glyph Scalar "<<QString::fromStdString(glyph_scalar);
+    //polyData->GetPointData()->SetScalars(pp->getPolyData()->GetPointData()->GetScalars(glyph_scalar.c_str()));
+    ////polyData->GetPointData()->SetActiveScalars(glyph_scalar.c_str());
+    //glyph3D->SetInputData(polyData);
+
+    glyph3D->SetInputData(pp->getPolyData());
+
+
+    glyph3D->ScalingOn();
+    //glyph3D->SetScaleModeToScaleByVector();
+
+    glyph3D->SetScaleModeToScaleByScalar();
+
+    glyph3D->SetColorModeToColorByScalar();
+    //glyph3D->SetVectorModeToUseVector();
+    //glyph3D->SetScaleModeToDataScalingOff();
+    //glyph3D->OrientOn();
+
+    double scaleFactor=ui->glyphScalingLineEdit->text().toDouble();
+    glyph3D->SetScaleFactor(scaleFactor);
+    glyph3D->Update();
+
+    // Visualize
+    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputConnection(glyph3D->GetOutputPort());
+    vtkSmartPointer<vtkLookupTable> lut=pp->getLookupTable();
+
+    //        pp->setLookupTableScale();
+    //lut->SetValueRange(myfits->GetMin(), myfits->GetMax());
+
+    mapper->SetLookupTable(lut);
+    mapper->ScalarVisibilityOn();
+    mapper->SetScalarRange(lut->GetRange());
+    mapper->Update();
+
+    if(glyph_actor!=0){
+        m_Ren1->RemoveActor(glyph_actor);
+    }
+    glyph_actor = vtkSmartPointer<vtkActor>::New();
+    glyph_actor->SetMapper(mapper);
+
+
+    m_Ren1->AddActor ( glyph_actor );
+    ui->qVTK1->GetRenderWindow()->GetRenderers()->GetFirstRenderer()->Render();
+    ui->qVTK1->update();
+
+}
+*/
+
+//slider opacità
+void vtkwindow_new::on_horizontalSlider_valueChanged(int value)
+{
+
+    int pos=0;
+
+    //Se non è selezionata un immagine nella tabella in basso a dx cambio il settaggio dell'immagine base
+    if (ui->listWidget->selectionModel()->selectedRows().count()!=0 && imgLayerList.at(ui->listWidget->selectionModel()->selectedRows().at(0).row())->getType()==0 )
+    {
+        pos= ui->listWidget->selectionModel()->selectedRows().at(0).row()  ;
+        //  pos= imgLayerList.at(ui->listWidget->selectionModel()->selectedRows().at(0).row() )->getLayerNumber() ;
+    }
+
+    vtkImageSlice::SafeDownCast( imageStack->GetImages()->GetItemAsObject(pos))->GetProperty()->SetOpacity(value/100.0);
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::on_glyphShapeComboBox_activated(int index)
+{
+
+    this->drawGlyphs(index);
+    ui->qVTK1->update();
+
+    //FV
+    // m_Ren1->ResetCamera();
+
+}
+
+void vtkwindow_new::on_glyphScalarComboBox_activated(const QString &arg1)
+{
+    //pp->colorScalar=arg1.toStdString();
+    //pp->setActiveScalar();
+    //changeScalar(arg1.toStdString());
+    //if(ui->glyphShapeComboBox->isEnabled()){
+    ui->glyphShapeComboBox->activated(ui->glyphShapeComboBox->currentIndex());
+    ui->qVTK1->update();
+
+    //}
+}
+
+void vtkwindow_new::on_glyphScalingLineEdit_returnPressed()
+{
+    ui->glyphShapeComboBox->activated(ui->glyphShapeComboBox->currentIndex());
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::on_ElementListWidget_clicked(const QModelIndex &index)
+{
+    int row=index.row();
+
+    QMap<QString,QString> datacube=classElementsOnDb[row];
+
+    double points[8];
+    double longFrom, longTo, latFrom, latTo;
+    longFrom=datacube["longitudeFrom"].toDouble();
+    if (longFrom>180 )
+        longFrom=longFrom-360;
+    else if (longFrom<-180 )
+        longFrom=longFrom+360;
+    longTo=datacube["longitudeTo"].toDouble();
+    if (longTo>180 )
+        longTo=longTo-360;
+    else if (longTo<-180 )
+        longTo=longTo+360;
+
+    latFrom=datacube["latitudeFrom"].toDouble();
+    latTo=datacube["latitudeTo"].toDouble();
+    double deltal,deltab;
+
+    if(called_dl!="" && called_db!="")
+    {
+        deltal=called_dl.toDouble()/2;
+        deltab=called_db.toDouble()/2;
+    }
+    else
+    {
+        deltal=called_r.toDouble();
+        deltab=called_r.toDouble();
+    }
+
+
+    /*
+    qDebug()<<"longFrom"<<called_l.toDouble()-called_r.();
+    qDebug()<<"longTo"<<called_l.toDouble()+called_r.toDouble();
+
+    if(longFrom<(called_l.toDouble()-called_r.toDouble())){
+        longFrom=called_l.toDouble()-called_r.toDouble();
+    }
+
+    if(longTo>(called_l.toDouble()+called_r.toDouble())){
+        longTo=called_l.toDouble()+called_r.toDouble();
+    }
+    if(latFrom<(called_b.toDouble()-called_r.toDouble())){
+        latFrom=called_b.toDouble()-called_r.toDouble();
+    }
+    if(latTo>(called_b.toDouble()+called_r.toDouble())){
+        latTo=called_b.toDouble()+called_r.toDouble();
+    }
+*/
+
+    qDebug()<<"deltal "<<deltal<<" deltab "<<deltab;
+    qDebug()<<"called_l.toDouble()-deltal "<<called_l.toDouble()-deltal<<" called_l.toDouble()+deltal  "<<called_l.toDouble()+deltal;
+    qDebug()<<"called_b.toDouble()-deltab "<<called_b.toDouble()-deltab<<" called_b.toDouble()+deltab "<<called_b.toDouble()+deltab;
+
+
+    if(longFrom<(called_l.toDouble()-deltal)){
+        longFrom=called_l.toDouble()-deltal;
+    }
+
+    if(longTo>(called_l.toDouble()+deltal)){
+        longTo=called_l.toDouble()+deltal;
+    }
+    if(latFrom<(called_b.toDouble()-deltab)){
+        latFrom=called_b.toDouble()-deltab;
+    }
+    if(latTo>(called_b.toDouble()+deltab)){
+        latTo=called_b.toDouble()+deltab;
+    }
+    /*
+    points[0]=longFrom;
+    points[1]=latFrom;
+    points[2]=longTo;
+    points[3]=latFrom;
+    points[4]=longTo;
+    points[5]=latTo;
+    points[6]=longFrom;
+    points[7]=latTo;
+*/
+
+    points[0]=datacube["longitudeP1"].toDouble();
+    points[1]=datacube["latitudeP1"].toDouble();
+    points[2]=datacube["longitudeP2"].toDouble();;
+    points[3]=datacube["latitudeP2"].toDouble();
+    points[4]=datacube["longitudeP3"].toDouble();;
+    points[5]=datacube["latitudeP3"].toDouble();
+    points[6]=datacube["longitudeP4"].toDouble();
+    points[7]=datacube["latitudeP4"].toDouble();
+
+    qDebug()<<" P1: "<<points[0]<<" - "<<points[1];
+    qDebug()<<" P2: "<<points[2]<<" - "<<points[3];
+    qDebug()<<" P3: "<<points[4]<<" - "<<points[5];
+    qDebug()<<" P4: "<<points[6]<<" - "<<points[7];
+
+
+
+    double *coord= new double[3];
+    double xypoints[8];
+
+    for(int i=0; i<8;i=i+2){
+        AstroUtils().sky2xy(myfits->GetFileName(), points[i], points[i+1], coord);
+        xypoints[i]=coord[0];
+        xypoints[i+1]=coord[1];
+    }
+    drawRectangleFootprint(xypoints);
+}
+
+void vtkwindow_new::drawRectangleFootprint(double points[8]){
+    vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
+    pts->InsertNextPoint(points[0],points[1],0);
+    pts->InsertNextPoint(points[2],points[3],0);
+    pts->InsertNextPoint(points[4],points[5],0);
+    pts->InsertNextPoint(points[6],points[7],0);
+
+
+    vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
+    polydata->Allocate();
+    polydata->SetPoints(pts);
+    vtkIdType connectivity[8];
+    connectivity[0] = 0;
+    connectivity[1] = 1;
+    connectivity[2] = 1;
+    connectivity[3] = 2;
+    connectivity[4] = 2;
+    connectivity[5] = 3;
+    connectivity[6] = 3;
+    connectivity[7] = 0;
+
+    polydata->InsertNextCell(VTK_LINE,8,connectivity); //Connects the first and fourth point we inserted into a line
+
+    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
+    mapper->SetInputData(polydata);
+
+    if(rectangleActor!=0){
+        m_Ren1->RemoveActor(rectangleActor);
+    }
+    rectangleActor = vtkSmartPointer<vtkLODActor>::New();
+    rectangleActor->SetMapper(mapper);
+    rectangleActor->GetProperty()->SetColor(1.0, 0.0, 0.0);
+    m_Ren1->AddActor(rectangleActor);
+
+    ui->qVTK1->update();
+}
+
+bool vtkwindow_new::eventFilter(QObject *object, QEvent *event)
+{
+    if (event->type() == QEvent::FocusOut)
+    {
+        if(rectangleActor!=0){
+            m_Ren1->RemoveActor(rectangleActor);
+        }
+    }
+    return false;
+}
+
+void vtkwindow_new::on_listWidget_clicked(const QModelIndex &index)
+{
+
+    if( ui->listWidget->selectionModel()->selectedRows().count()!=0 && imgLayerList.at(index.row())->getType()==0 )
+    {
+        imageStack->SetActiveLayer( ui->listWidget->selectionModel()->selectedRows().at(0).row() );
+
+        ui->horizontalSlider->setValue(vtkImageSlice::SafeDownCast( imageStack->GetImages()->GetItemAsObject( ui->listWidget->selectionModel()->selectedRows().at(0).row() ))->GetProperty()->GetOpacity()*100.0);
+
+        ui->lutComboBox->setCurrentText(imgLayerList.at(ui->listWidget->selectionModel()->selectedRows().at(0).row())->getLutType());
+
+        if( imgLayerList.at(ui->listWidget->selectionModel()->selectedRows().at(0).row())->getLutScale() == "Linear")
+            ui->linearadioButton->setChecked(true);
+        else
+            ui->logRadioButton->setChecked(true);
+
+    }
+}
+
+void vtkwindow_new::on_listWidget_itemChanged(QListWidgetItem *item)
+{
+    //checkbox img
+    checkboxImageClicked(item->listWidget()->row(item));
+}
+
+void vtkwindow_new::movedLayersRow( const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationRow )
+{
+
+    if(sourceStart>destinationRow)
+    {//down
+
+        for(int i=sourceStart-1;i>=destinationRow;i--)
+        {
+            vtkImageSlice::SafeDownCast(imageStack->GetImages()->GetItemAsObject(i))->GetProperty()->SetLayerNumber(i+1);
+            imgLayerList.swap(i,i+1);
+        }
+
+        vtkImageSlice::SafeDownCast(imageStack->GetImages()->GetItemAsObject(sourceStart))->GetProperty()->SetLayerNumber(destinationRow);
+
+    }
+    else
+    {//up
+
+        for(int i=sourceStart+1;i<destinationRow;i++)
+        {
+            vtkImageSlice::SafeDownCast(imageStack->GetImages()->GetItemAsObject(i))->GetProperty()->SetLayerNumber(i-1);
+            imgLayerList.swap(i,i-1);
+
+        }
+        vtkImageSlice::SafeDownCast(imageStack->GetImages()->GetItemAsObject(sourceStart))->GetProperty()->SetLayerNumber(destinationRow-1);
+    }
+
+    ui->qVTK1->update();
+
+}
+
+void vtkwindow_new::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
+{
+    /*
+    // if(index.column()==0)
+    if (layerList.at(item->listWidget()->row(item))->getType()==0)
+    {
+        //settaggi dell'immagine selezionata
+    }
+    else
+    {
+
+        //Initial color
+        double r=getVisualizedActorList().value(ui->listWidget->item( item->listWidget()->row(item))->text())->GetProperty()->GetColor()[0]*255;
+        double g=getVisualizedActorList().value(ui->listWidget->item(item->listWidget()->row(item))->text())->GetProperty()->GetColor()[1]*255;
+        double b=getVisualizedActorList().value(ui->listWidget->item( item->listWidget()->row(item))->text())->GetProperty()->GetColor()[2]*255;
+
+        QColor color = QColorDialog::getColor(QColor(r,g,b), this);
+        getVisualizedActorList().value(ui->listWidget->item( item->listWidget()->row(item))->text())->GetProperty()->SetColor(color.redF(), color.greenF(), color.blueF());
+        ui->qVTK1->update();
+
+        //update color on table
+        ui->listWidget->item(item->listWidget()->row(item))->setForeground(QBrush(color));
+        //>setStyleSheet("background-color: rgb("+QString::number(color.redF()*255)+","+QString::number(color.greenF()*255)+" ,"+QString::number(color.blueF()*255)+")");
+    }
+*/
+}
+
+void vtkwindow_new::on_listWidget_customContextMenuRequested(const QPoint &pos)
+{
+    /*
+    vosamp *samp = &Singleton<vosamp>::Instance();
+    char* clist=samp->getClientsList();
+   //char* clist;
+    QStringList clientList= QString::fromLocal8Bit(clist).split(QRegExp("[\r\n]"),QString::SkipEmptyParts);
+
+    QHash<QString, QString> clientHashMap;
+
+    foreach (QString str, clientList)
+    {
+        clientHashMap.insert( str.section(" ",1), str.section(" ",0,0));
+    }
+
+
+    QSignalMapper* signalMapper = new QSignalMapper (this);
+
+    QPoint globalPos = ui->listWidget->mapToGlobal(pos);	// Map the global position to the userlist
+    QModelIndex t = ui->listWidget->indexAt(pos);
+    ui->listWidget->item(t.row())->setSelected(true);		// even a right click will select the item
+
+    QMenu *myMenu=new QMenu(this);
+    // connect (myMenu, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
+
+    bool none=true;
+    if (clientHashMap.contains("SAOImage DS9"))
+    {
+        qDebug()<<clientHashMap.value("SAOImage DS9");
+        none=false;
+        // myMenu.addAction("Send to DS9", this, SLOT(sendImageTo()));
+        //    signalMapper -> setMapping ( myMenu->addAction("Send to DS9"),clientHashMap.value("SAOImage DS9")  ) ;
+
+        QAction *send= new QAction("Send to DS9",this);
+        connect (send, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
+        signalMapper -> setMapping (send, clientHashMap.value("SAOImage DS9") ) ;
+        myMenu->addAction(send);
+
+
+    }
+    if (clientHashMap.contains("Aladin"))
+    {
+        none=false;
+
+        QAction *send= new QAction("Send to Aladin",this);
+        connect (send, SIGNAL(triggered()), signalMapper, SLOT(map())) ;
+        signalMapper -> setMapping (send, clientHashMap.value("Aladin") ) ;
+        myMenu->addAction(send);
+
+    }
+
+    if (none)
+        myMenu->addAction("No SAMP client available");
+
+    connect (signalMapper, SIGNAL(mapped(QString)), this, SLOT(sendImageTo(QString))) ;
+    //  myMenu->exec(globalPos);
+    myMenu->popup(globalPos);
+*/
+}
+
+void vtkwindow_new::sendImageTo(QString id)
+{
+
+    /*
+    qDebug()<<"CLIENT ID: "<<id;
+
+    char *file = strdup(imgLayerList.at(ui->listWidget->selectionModel()->selectedRows().at(0).row())->getFits()->GetFileName().c_str());
+    char *to = strdup(id.toStdString().c_str());
+
+    vosamp *samp = &Singleton<vosamp>::Instance();
+    samp->sendFitsImage(file,to);
+    free (file);
+
+*/
+}
+
+void vtkwindow_new::on_bubblePushButton_clicked()
+{
+    ui->bubblePushButton->setStyleSheet("background-color: rgb(0, 0, 250);border-radius: 3px; border-width: 1px;");
+    ui->rectangularSelectionCS->setStyleSheet("");
+    ui->tdRectPushButton->setStyleSheet("");
+    ui->fil_rectPushButton->setStyleSheet("");
+
+    vtkSmartPointer<SkyRegionSelector> style =vtkSmartPointer<SkyRegionSelector>::New();
+    style->setVtkWin(this);
+    style->setIsBubble();
+    ui->qVTK1->GetRenderWindow()->GetInteractor()->SetInteractorStyle( style );
+    ui->qVTK1->setCursor(Qt::CrossCursor);
+}
+
+void vtkwindow_new::on_filterMoreButton_clicked()
+{
+    FilterCustomize *filterCustomize= new FilterCustomize(this);
+    filterCustomize->show();
+
+}
diff --git a/Code/src/vtkwindow_new.h b/Code/src/vtkwindow_new.h
new file mode 100644
index 0000000000000000000000000000000000000000..c4f0317f06c965cf35bea9f84b92b1fb846f4796
--- /dev/null
+++ b/Code/src/vtkwindow_new.h
@@ -0,0 +1,382 @@
+#ifndef vtkwindow_new_H
+#define vtkwindow_new_H
+
+#include <QMainWindow>
+#include "pointspipe.h"
+#include "ui_vtkwindow_new.h"
+#include "vtkRenderWindow.h"
+#include "vtkRenderer.h"
+#include "vtkCommand.h"
+#include "vtkEventQtSlotConnect.h"
+#include "vtkConeSource.h"
+#include "vtkSphereSource.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkActor.h"
+#include "vtkSphereSource.h"
+#include "vtkConeSource.h"
+#include "vtkCylinderSource.h"
+#include "vtkCubeSource.h"
+#include "vtkMarchingCubes.h"
+
+#include "vtkCamera.h"
+#include "vtkPointData.h"
+#include "vtkCellData.h"
+#include "vtkLookupTable.h"
+#include "contour.h"
+#include "vtkLineSource.h"
+
+#include "vtkFloatArray.h"
+#include "vtkCellArray.h"
+#include"vtkGlyph3D.h"
+#include "vtkScalarBarActor.h"
+#include "vtkOutlineCornerFilter.h"
+#include "vtkProperty.h"
+
+#include "vtkGenericRenderWindowInteractor.h"
+#include "vtkRenderWindow.h"
+#include "vtkRenderer.h"
+#include "vtkActor.h"
+#include "vtkAxesActor.h"
+#include "vtkLODActor.h"
+#include <map>
+#include "vtkImageActor.h"
+#include "vtkImageViewer2.h"
+#include <vtkSmartPointer.h>
+#include "vtkfitsreader.h"
+#include "vtkellipse.h"
+#include "vtkOrientationMarkerWidget.h"
+#include "vtkAxes.h"
+#include "vtkEventQtSlotConnect.h"
+#include <QGestureEvent>
+#include "vtkResliceImageViewer.h"
+#include "vtkfitstoolwidget_new.h"
+#include "loadingwidget.h"
+#include <QNetworkReply>
+#include "vtkImageStack.h"
+
+#include "vialacteastringdictwidget.h"
+
+class vtkRenderer;
+class vtkEventQtSlotConnect;
+class vtkObject;
+class vtkCommand;
+class VSTableDesktop;
+class vtkPiecewiseFunction;
+class vtkColorTransferFunction;
+class VisPoint;
+class vtkfitstoolswidget;
+class FitsImageStatisiticInfo;
+class contour;
+
+namespace Ui {
+class vtkwindow_new;
+}
+
+class vtkwindow_new : public QMainWindow
+{
+    Q_OBJECT
+    vtkLODActor *m_pActor;
+    vtkVolumeProperty  *m_volumeProperty;
+    //vtkPiecewiseFunction *m_opacityTransferFunction;
+    vtkColorTransferFunction *m_colorTransferFunction;
+
+public:
+    explicit vtkwindow_new(QWidget *parent = 0, VisPoint *vis=0);
+    explicit vtkwindow_new(QWidget *parent = 0, vtkSmartPointer<vtkFitsReader> vis=0, int b=0, vtkwindow_new *p=0);
+    //explicit vtkwindow_new(QWidget *parent = 0, vtkImageActor *vis=0);
+    ~vtkwindow_new();
+    vtkRenderer* m_Ren1;
+    vtkRenderWindow* renwin;
+
+    vtkRenderer* m_Ren2;
+    vtkRenderer* m_Ren3;
+    vtkRenderWindow* renwin2;
+    vtkwindow_new *vtkcontourwindow;
+    vtkwindow_new *myParentVtkWindow;
+    contour *contourWin;
+    vtkSmartPointer<vtkLineSource> lineSource;
+
+    vtkMarchingCubes *shellE;
+    vtkActor *sliceA;
+
+    VSTableDesktop * table;
+    VisPoint * vispoint;
+    void changePalette(std::string palette);
+    void changeFitsPalette(std::string palette);
+    void changeFitsScale(std::string palette, std::string scale);
+    void changeScalar(std::string scalar);
+    PointsPipe * pp;
+    void resetCamera();
+    void drawEllipse(QHash<QString, vtkEllipse *> ellipse , QString sourceFilename);
+    void drawGlyphs(int index);
+    Ui::vtkwindow_new *ui;
+    static void SelectionChangedCallbackFunction (vtkObject* caller, long unsigned int eventId, void* clientData, void* callData );
+    QString getWindowName();
+    //  QString getFilenameWithPath();
+    void setWindowName(QString name);
+    //  void isVisible();
+    QHash<QString,  vtkSmartPointer<vtkLODActor> > getEllipseActorList();
+    QHash<QString,  vtkSmartPointer<vtkLODActor> > getVisualizedActorList();
+
+    double r;
+    double g;
+    double b;
+    bool isDatacube;
+    QString survey;
+    QString species;
+    QString transition;
+    double max;
+    double min;
+    long naxis3;
+    double *z_range;
+    vtkSmartPointer<vtkImageStack> imageStack ;
+
+    vtkSmartPointer<vtkResliceImageViewer> viewer;
+
+    vtkSmartPointer<vtkImageViewer2> imageViewer;
+    void addSources(VSTableDesktop* m_VisIVOTable);
+    void addFilaments(VSTableDesktop* m_VisIVOTable);
+    void addSourcesFromBM(VSTableDesktop* m_VisIVOTable);
+    void addBubble(VSTableDesktop* m_VisIVOTable);
+
+
+    //void drawSingleEllipse(QList<vtkEllipse *> ellipse, QString sourceFilename );
+    //void drawSingleEllipse(vtkEllipse * ellipse );
+    void drawSingleEllipse(vtkSmartPointer<vtkLODActor> ellipseActor);
+    void removeSingleEllipse(vtkSmartPointer<vtkLODActor> ellipseActor);
+    void setContourVisualized(bool s) {contourVisualized=s;}
+    bool getContourVisualized() {return contourVisualized;}
+    QList<vtkfitstoolwidgetobject*> getLayerListImages(){return imgLayerList;}
+    QList<vtkfitstoolwidgetobject*> getLayerListElements(){return elementLayerList;}
+
+    void addLayerImage(vtkSmartPointer<vtkFitsReader> vis, QString survey="", QString species="", QString transition="");
+
+    QString getSelectedScale(){return selected_scale;}
+
+    // QList<vtkEllipse*> ellipse_list;
+    QHash<QString, vtkEllipse* > getEllipseList(){return ellipse_list;}
+    QHash<QString, vtkEllipse* > getFtEllipseList(){return ft_ellipse_list;}
+    QHash<QString,  QString > getDesignation2fileMap() {return designation2fileMap;}
+
+    void printSelf();
+
+    void changeWCS(bool galaptic);
+    std::string filenameWithPath;
+
+
+
+
+    QHash<QString, int> file_wavelength;
+    vtkSmartPointer<vtkActor> selectedActor;
+    void addActor(vtkProp *actor);
+    void removeActor(vtkProp *actor);
+
+    void setCallingL(QString v){called_l=v;};
+    void setCallingB(QString v){called_b=v;};
+    void setCallingR(QString v){called_r=v;};
+    void setCallingDl(QString v){called_dl=v;};
+    void setCallingDb(QString v){called_db=v;};
+
+    vtkSmartPointer<vtkFitsReader> getFitsImage(){return myfits;};
+    vtkSmartPointer<vtkActor> getGlyphActor(){return glyph_actor;};
+
+signals:
+    void speciesChanged();
+    void surveyChanged();
+    void transitionChanged();
+
+
+private:
+    QHash<QString, QPair<double,double> > addedFilter;
+
+    QString selected_scale;
+    double cam_init_pos[3];
+    double cam_init_foc[3];
+    bool scaleActivate;
+    bool fitsViewer;
+    vtkSmartPointer<vtkRenderer> back_ren=0;
+    //contour *contourWin; //=0;
+    vtkSmartPointer<vtkFitsReader> myfits;
+    LoadingWidget *loading;
+    bool contourVisualized=false;
+    bool coloBarVisualized=false;
+    bool contourWinActivated=false;
+    vtkActor *outline_actor=0;
+    vtkActor *contour_actor=0;
+    vtkActor *data_geometry_actor=0;
+    vtkSmartPointer<vtkActor> glyph_actor=0;
+    QList<QMap<QString, QString> > classElementsOnDb;
+    QString called_l;
+    QString called_b;
+    QString called_r;
+    QString called_dl;
+    QString called_db;
+    int vtkwintype;
+
+    vtkfitstoolwidget_new *vtkfitstoolwindow;
+
+    QString windowName;
+    //QString filenameWithPath;
+    QHash<QString,  vtkSmartPointer<vtkLODActor> > ellipse_actor_list;
+    QHash<QString,  vtkSmartPointer<vtkLODActor> > visualized_actor_list;
+
+    QHash<QString,  QString > designation2fileMap;
+    QHash<QString,   vtkSmartPointer<vtkLODActor> > VisualizedEllipseSourcesList;
+    vtkfitstoolswidget *vtkfitstoolsw;
+    QHash<QString, vtkEllipse* > ellipse_list;
+    QHash<QString, vtkEllipse* > ft_ellipse_list;
+    vtkSmartPointer<vtkOrientationMarkerWidget> vtkAxesWidget;
+    vtkSmartPointer<vtkAxesActor> vtkAxes;
+    FitsImageStatisiticInfo *info;
+    vtkSmartPointer<vtkEventQtSlotConnect> Connections;
+    vtkfitstoolwidgetobject *imageObject;
+    QList<vtkfitstoolwidgetobject*> imgLayerList;
+    QList<vtkfitstoolwidgetobject*> elementLayerList;
+    vtkSmartPointer<vtkLODActor> rectangleActor=0;
+    vtkSmartPointer<vtkActor> currentContourActor;
+    vtkSmartPointer<vtkActor> currentContourActorForMainWindow;
+    QString vlkbUrl;
+    QString selectedCubeVelocityUnit;
+    void drawRectangleFootprint(double points[8]);
+    VialacteaStringDictWidget *stringDictWidget;
+    void addCombinedLayer(QString name,  vtkSmartPointer<vtkLODActor>actor, int objtype, bool active);
+
+public slots:
+    //void updateCoords(vtkObject*);
+    //void popup(vtkObject * obj, unsigned long, void * client_data, void *, vtkCommand * command);
+    void loadObservedObject(VisPoint *vis);
+    void updateScene();
+    void showBox(bool checked);
+    void showAxes(bool checked);
+    void showColorbar(bool checked);
+    void setCameraElevation(double el);
+    void setCameraAzimuth(double az);
+    void scale(bool checked);
+    void showGrid(bool checked);
+    FitsImageStatisiticInfo* getInfoWindow();
+    void createInfoWindow();
+    void setSelectionFitsViewerInteractorStyle();
+    void setSkyRegionSelectorInteractorStyle();
+    void slot_clicked(vtkObject*, unsigned long, void*, void*);
+    void setVtkInteractorStyleImage();
+    void setVtkInteractorStyleImageContour();
+
+    void setVtkInteractorStyle3DPicker(vtkSmartPointer<vtkPolyData> points);
+    void setSelectedActor(vtkSmartPointer<vtkActor> sel);
+    void setSkyRegionSelectorInteractorStyleFor3D();
+    void setVtkInteractorStyleFreehand();
+    void setVtkInteractorStyle3DFreehand(vtkSmartPointer<vtkPolyData> points);
+    void setVtkInteractorContourWindow();
+    void setSliceDatacube(int i);
+
+    void plotSlice(vtkSmartPointer<vtkFitsReader> visvis, int arg1);
+    void updateSpecies();
+    void updateSurvey();
+    void updateTransition();
+
+    void setSurvey(QString q);
+    void setSpecies(QString q);
+    void setTransition(QString q);
+
+    void setDbElements(QList<QMap<QString, QString> > elementsOnDb);
+    void setSelectedCubeVelocityUnit (QString v){selectedCubeVelocityUnit=v;}
+    //void setCuttingPlane(int value);
+    void downloadStartingLayers(QList<QPair<QString, QString> > selectedSurvey);
+
+
+
+
+protected:
+    //vtkEventQtSlotConnect* Connections;
+    vtkVolume *m_volume;
+    void closeEvent(QCloseEvent *event);
+
+private slots:
+    void addLayer(vtkfitstoolwidgetobject *o, bool enabled = true);
+    void addTreeChild(QTreeWidgetItem *parent, QString name, QBrush brush);
+    QTreeWidgetItem* addTreeRoot(QString name);
+
+    void on_actionTools_triggered();
+    void on_actionInfo_triggered();
+    void addLocalSources();
+    void cutoutDatacube(QString c );
+
+
+    void on_cameraLeft_clicked();
+    void on_bottomCamera_clicked();
+    void on_topCamera_clicked();
+    void on_frontCamera_clicked();
+    void on_cameraRight_clicked();
+    void on_cameraBack_clicked();
+    void on_resetPushButton_clicked();
+    void on_confirmPushButton_clicked();
+    void on_rectSelection_clicked();
+    void on_freehandPushButton_clicked();
+    void on_cuttingPlane_Slider_valueChanged(int value);
+    void on_spinBox_cuttingPlane_valueChanged(int arg1);
+    void handleButton(int i);
+
+    void setCuttingPlaneValue(int arg1);
+
+
+    //void on_cuttingPlane_Slider_sliderMoved(int position);
+    // void on_horizontalSlider_threshold_sliderMoved(int position);
+
+
+    void on_spinBox_contour_valueChanged(int arg1);
+    void on_contour_pushButton_clicked();
+    void on_cuttingPlane_Slider_sliderMoved(int position);
+    //void on_horizontalSlider_threshold_actionTriggered(int action);
+    void on_PVPlotPushButton_clicked();
+    //void on_generatePushButton_clicked();
+    void on_PVPlot_radioButton_clicked(bool checked);
+    void on_horizontalSlider_threshold_valueChanged(int value);
+    void on_horizontalSlider_threshold_sliderReleased();
+    void on_rectangularSelectionCS_clicked();
+    void on_colorPushButton_clicked();
+    void addToList(vtkfitstoolwidgetobject *o, bool enabled=true);
+    void addImageToList( vtkfitstoolwidgetobject *o);
+
+    void checkboxImageClicked(int cb);
+    void checkboxClicked(int cb, bool status =false);
+
+    void on_tableWidget_doubleClicked(const QModelIndex &index);
+    void on_fil_rectPushButton_clicked();
+
+
+    void on_lutComboBox_activated(const QString &arg1);
+    void on_logRadioButton_toggled(bool checked);
+    void on_tdRectPushButton_clicked();
+    void on_ElementListWidget_doubleClicked(const QModelIndex &index);
+    void on_thresholdValueLineEdit_editingFinished();
+    void on_upperBoundLineEdit_editingFinished();
+    void on_lowerBoundLineEdit_editingFinished();
+    void removeContour();
+    void goContour();
+
+    void on_levelLineEdit_editingFinished();
+    void on_contourCheckBox_clicked(bool checked);
+    void on_lut3dActivateCheckBox_clicked(bool checked);
+    void on_scalarComboBox_activated(const QString &arg1);
+    void on_lut3dComboBox_activated(const QString &arg1);
+    void on_toolButton_clicked();
+    void on_glyphActivateCheckBox_clicked(bool checked);
+    void on_linear3dRadioButton_toggled(bool checked);
+    void on_glyphShapeComboBox_activated(int index);
+    void on_horizontalSlider_valueChanged(int value);
+    void on_glyphScalarComboBox_activated(const QString &arg1);
+    void on_glyphScalingLineEdit_returnPressed();
+    void on_ElementListWidget_clicked(const QModelIndex &index);
+    bool eventFilter(QObject *object, QEvent *event);
+    void on_listWidget_clicked(const QModelIndex &index);
+    void on_listWidget_itemChanged(QListWidgetItem *item);
+    void movedLayersRow( const QModelIndex & sourceParent, int sourceStart, int sourceEnd, const QModelIndex & destinationParent, int destinationRow );
+    void on_listWidget_itemDoubleClicked(QListWidgetItem *item);
+    void on_listWidget_customContextMenuRequested(const QPoint &pos);
+    void sendImageTo(QString id);
+
+    void on_bubblePushButton_clicked();
+    void on_filterMoreButton_clicked();
+};
+
+#endif // vtkwindow_new_H
diff --git a/Code/src/xmlparser.cpp b/Code/src/xmlparser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3917d99bd7beffba79e4e083d6ecfda14a00dfbe
--- /dev/null
+++ b/Code/src/xmlparser.cpp
@@ -0,0 +1,843 @@
+#include "xmlparser.h"
+#include <QFile>
+#include <QMessageBox>
+#include <QMap>
+#include <QGroupBox>
+#include <QFormLayout>
+#include <QLineEdit>
+#include <QDebug>
+#include <qwidget.h>
+#include <QList>
+#include <QListWidgetItem>
+#include <QModelIndex>
+#include <QStandardItem>
+#include <QStringList>
+
+xmlparser::xmlparser()
+{
+    queryUrl="ia2-vo.oats.inaf.it/vialactea/cutouts/";
+}
+xmlparser::~xmlparser()
+{
+
+}
+
+
+void xmlparser::parseXML_fitsDownload(QXmlStreamReader & xml_reader, QString &stringa) {
+
+    //qDebug()<<"#reading the xml file";
+    QList< QMap<QString,QString> > datacubes;
+    QString url;
+    QMap<QString, QString> survey_map;
+
+    while(!xml_reader.atEnd() &&
+          !xml_reader.hasError()) {
+        /* Read next element.*/
+        QXmlStreamReader::TokenType token = xml_reader.readNext();
+
+        /* If token is just StartDocument, we'll go to next.*/
+        if(token == QXmlStreamReader::StartDocument) {
+            continue;
+        }
+
+        /* If token is StartElement, we'll see if we can read it.*/
+        if(token == QXmlStreamReader::StartElement) {
+            if(xml_reader.name() == "results") {
+                continue;
+            }
+            if(xml_reader.name() == "description") {
+                continue;
+            }
+            if(xml_reader.name() == "input") {
+                continue;
+            }
+            if(xml_reader.name() == "OriginFits") {
+                continue;
+            }
+            if(xml_reader.name() == "survey") {
+                survey_map=this->parseSurvey(xml_reader);
+
+
+            }
+
+
+            if(xml_reader.name() == "datacube") {
+                //datacubes.append(this->parseDatacube(xml_reader, stringa));
+
+
+                QMap<QString, QString> datacube_map=this->parseDatacube(xml_reader);
+                int test_flag_nanten=-1;
+                if(datacube_map["PublisherDID"].compare("")!=0)
+                    test_flag_nanten = datacube_map["PublisherDID"].split("_", QString::SkipEmptyParts).last().toInt();
+                if(datacube_map["code"].compare("1")==0||datacube_map["code"].compare("6")==0){
+                    qDebug()<<"Do not add datacube wiwh code "<<datacube_map["code"];
+                }else if(survey_map["Survey"].compare("NANTEN")==0 && test_flag_nanten==2){
+                    qDebug()<<"NANTEN 2d Element not added";
+                    qDebug()<<survey_map["Survey"]<<" "<<datacube_map["PublisherDID"]<<" "<<test_flag_nanten;
+                }else{
+                    if(survey_map["Survey"].compare("")==0){
+                        datacube_map.insert("Survey","Extinction_Map");
+                        datacube_map.insert("Species","dust");
+                        datacube_map.insert("Transition","visual");
+                    }
+                    else
+                    {
+                        datacube_map.insert("Survey",survey_map["Survey"]);
+                        datacube_map.insert("Species",survey_map["Species"]);
+                        datacube_map.insert("Transition",survey_map["Transition"]);
+                        datacube_map.insert("Description",survey_map["Description"]);
+                    }
+                    datacubes.append(datacube_map);
+                }
+
+            }
+            /*
+            if(xml_reader.name() == "datacube") {
+                //datacubes.append(this->parseDatacube(xml_reader, stringa));
+
+                QMap<QString, QString> datacube_map=this->parseDatacube(xml_reader);
+                int test_flag_nanten=-1;
+                if(datacube_map["PublisherDID"].compare("")!=0)
+                    test_flag_nanten = datacube_map["PublisherDID"].split("_", QString::SkipEmptyParts).last().toInt();
+                if(datacube_map["code"].compare("1")==0||datacube_map["code"].compare("6")==0){
+                    qDebug()<<"Do not add datacube wiwh code "<<datacube_map["code"];
+                }else if(datacube_map["Survey"].compare("NANTEN")==0 && test_flag_nanten==2){
+                    qDebug()<<"NANTEN 2d Element not added";
+                    qDebug()<<datacube_map["Survey"]<<" "<<datacube_map["PublisherDID"]<<" "<<test_flag_nanten;
+                }else{
+                    if(datacube_map["Survey"].compare("")==0){
+                        datacube_map.insert("Survey","Extinction_Map");
+                        datacube_map.insert("Species","dust");
+                        datacube_map.insert("Transition","visual");
+                    }
+                    datacubes.append(datacube_map);
+                }
+            }
+            */
+
+            if(xml_reader.name() == "URL") {
+                xml_reader.readNext();
+                if(xml_reader.tokenType() == QXmlStreamReader::Characters) {
+                    url=xml_reader.text().toString();
+
+                }
+            }
+        }
+    }
+    xml_reader.clear();
+    qDebug()<<"EoF";
+
+
+    //      stringa=this->extractPublisherDID(datacubes, datacube_element);
+    stringa=this->extractPublisherDID_fits(datacubes, datacube_element);
+
+    if(url.compare("")!=0)
+    {
+        //qDebug()<<"URL....."<<url;
+        stringa=url;
+    }
+}
+
+
+QList< QMap<QString,QString> > xmlparser::parseXmlAndGetList(QXmlStreamReader & xml_reader) {
+
+    //qDebug()<<"#reading the xml file";
+    QList< QMap<QString,QString> > datacubes;
+    QString url;
+    QMap<QString, QString> survey_map;
+
+    while(!xml_reader.atEnd() &&
+          !xml_reader.hasError()) {
+        /* Read next element.*/
+        QXmlStreamReader::TokenType token = xml_reader.readNext();
+
+        /* If token is just StartDocument, we'll go to next.*/
+        if(token == QXmlStreamReader::StartDocument) {
+            continue;
+        }
+
+        /* If token is StartElement, we'll see if we can read it.*/
+        if(token == QXmlStreamReader::StartElement) {
+            if(xml_reader.name() == "results") {
+                continue;
+            }
+            if(xml_reader.name() == "description") {
+                continue;
+            }
+            if(xml_reader.name() == "input") {
+                continue;
+            }
+            if(xml_reader.name() == "msg") {
+                continue;
+            }
+            if(xml_reader.name() == "OriginFits") {
+                continue;
+            }
+            if(xml_reader.name() == "DatacubeCount") {
+                continue;
+            }
+
+            if(xml_reader.name() == "survey") {
+                survey_map=this->parseSurvey(xml_reader);
+
+            }
+
+
+            if(xml_reader.name() == "VelocityUnit") {
+               this->parseVelocity(xml_reader);
+
+            }
+            if(xml_reader.name() == "datacube") {
+                //datacubes.append(this->parseDatacube(xml_reader, stringa));
+
+                QMap<QString, QString> datacube_map=this->parseDatacube(xml_reader);
+                int test_flag_nanten=-1;
+                if(datacube_map["PublisherDID"].compare("")!=0)
+                    test_flag_nanten = datacube_map["PublisherDID"].split("_", QString::SkipEmptyParts).last().toInt();
+                if(datacube_map["code"].compare("1")==0||datacube_map["code"].compare("6")==0){
+                    qDebug()<<"Do not add datacube wiwh code "<<datacube_map["code"];
+                }
+                else if( test_flag_nanten==2 || test_flag_nanten==3 ){//datacube_map["Survey"].compare("Cornish")==0){
+                    qDebug()<<"Element not added: "<<survey_map["Survey"]<<" "<<datacube_map["PublisherDID"]<<" "<<test_flag_nanten;
+                }
+                else{
+                    if(survey_map["Survey"].compare("")==0){
+                        datacube_map.insert("Survey","Extinction_Map");
+                        datacube_map.insert("Species","dust");
+                        datacube_map.insert("Transition","visual");
+                    }
+                    else
+                    {
+                        datacube_map.insert("Survey",survey_map["Survey"]);
+                        datacube_map.insert("Species",survey_map["Species"]);
+                        datacube_map.insert("Transition",survey_map["Transition"]);
+                        datacube_map.insert("Description",survey_map["Description"]);
+                    }
+                   qDebug()<<"Element added: " <<datacube_map["Survey"]<<" "<<datacube_map["PublisherDID"]<<" "<<datacube_map["Description"]<<" "<<datacube_map["from"]<<" "<<datacube_map["to"]<<" "<<test_flag_nanten;
+                    datacubes.append(datacube_map);
+                }
+            }
+
+
+            if(xml_reader.name() == "URL") {
+                xml_reader.readNext();
+                if(xml_reader.tokenType() == QXmlStreamReader::Characters) {
+                    //qDebug()<<"URL: "<<xml_reader.text().toString();
+                    url=xml_reader.text().toString();
+
+                }
+            }
+        }
+    }
+    xml_reader.clear();
+
+    qSort(datacubes);
+
+    return datacubes;
+
+
+}
+
+bool operator<(const QMap<QString, QString> &a,const QMap<QString, QString> &b){
+    return a.value("Survey") < b.value("Survey");
+}
+
+
+void xmlparser::parseXML(QXmlStreamReader & xml_reader, QString &stringa, bool dc) {
+
+    // qDebug()<<"#reading the xml file";
+    QList< QMap<QString,QString> > datacubes;
+    QString url;
+    double perc=0.0;
+
+    while(!xml_reader.atEnd() &&
+          !xml_reader.hasError()) {
+        /* Read next element.*/
+        QXmlStreamReader::TokenType token = xml_reader.readNext();
+
+        /* If token is just StartDocument, we'll go to next.*/
+        if(token == QXmlStreamReader::StartDocument) {
+            continue;
+        }
+
+        /* If token is StartElement, we'll see if we can read it.*/
+        if(token == QXmlStreamReader::StartElement) {
+            if(xml_reader.name() == "results") {
+                continue;
+            }
+            if(xml_reader.name() == "description") {
+                continue;
+            }
+            if(xml_reader.name() == "input") {
+                continue;
+            }
+            if(xml_reader.name() == "OriginFits") {
+                continue;
+            }
+
+            if(xml_reader.name() == "datacube") {
+                QMap<QString, QString> datacube_map=this->parseDatacube(xml_reader);
+
+
+
+
+
+                int test_flag_nanten=-1;
+                if(datacube_map["PublisherDID"].compare("")!=0)
+                    test_flag_nanten = datacube_map["PublisherDID"].split("_", QString::SkipEmptyParts).last().toInt();
+                if(datacube_map["code"].compare("1")==0||datacube_map["code"].compare("6")==0){
+                    qDebug()<<"Do not add datacube wiwh code "<<datacube_map["code"];
+                }else if(datacube_map["Survey"].compare("NANTEN")==0 && test_flag_nanten==2 || datacube_map["Survey"].compare("Cornish")==0){
+                    qDebug()<<"NANTEN 2d Element not added";
+                    qDebug()<<datacube_map["Survey"]<<" "<<datacube_map["PublisherDID"]<<" "<<test_flag_nanten;
+                }else{
+                    if(datacube_map["Survey"].compare("")==0){
+                        datacube_map.insert("Survey","Extinction_Map");
+                        datacube_map.insert("Species","dust");
+                        datacube_map.insert("Transition","visual");
+                    }
+                    datacubes.append(datacube_map);
+                }
+            }
+            /* Read here percentage of null values into the vlkb_cutout service*/
+            if(xml_reader.name() == "percent") {
+                xml_reader.readNext();
+                if(xml_reader.tokenType() == QXmlStreamReader::Characters){
+                    perc=xml_reader.text().toDouble();
+                    qDebug()<<"NullValues percentage is "<<perc;
+                }
+            }
+
+            if(xml_reader.name() == "URL") {
+                xml_reader.readNext();
+                if(xml_reader.tokenType() == QXmlStreamReader::Characters) {
+                    url=xml_reader.text().toString();
+
+                }
+            }
+        }
+    }
+    xml_reader.clear();
+    //qDebug()<<"EoF";
+
+    if(dc)
+        stringa=this->extractPublisherDID(datacubes, datacube_element);
+    else
+        ;//qDebug()<<datacubes;
+    if(url.compare("")!=0)
+    {
+        if ( perc < 95.0)
+            stringa=url;
+        else
+            stringa ="NULL "+QString::number(perc);
+    }
+
+}
+QMap<QString, QString> xmlparser::parseSurvey(QXmlStreamReader& xml) {
+    velocity="";
+    QMap<QString, QString> survey_map;
+
+    if(xml.tokenType() != QXmlStreamReader::StartElement && xml.name() == "RestFreq") {
+        return survey_map;
+    }
+
+    xml.readNext();
+
+
+   while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "RestFreq")) {
+
+        if(xml.tokenType() == QXmlStreamReader::StartElement) {
+
+            /* We've found Description. */
+            if(xml.name() == "Description") {
+                this->addDatacubeToMap(xml, survey_map);
+            }
+            /* We've found Survey. */
+            if(xml.name() == "Survey") {
+                this->addDatacubeToMap(xml, survey_map);
+            }
+            /* We've found Species. */
+            if(xml.name() == "Species") {
+                this->addDatacubeToMap(xml, survey_map);
+            }
+            /* We've found Transition. */
+            if(xml.name() == "Transition") {
+                this->addDatacubeToMap(xml, survey_map);
+            }
+
+
+            if(xml.name() == "VelocityUnit") {
+                qDebug()<<"ATTENZIONE: "<<xml.name();
+                this->parseVelocity(xml);
+
+              //  this->addDatacubeToMap(xml, survey_map);
+            }
+
+
+        }
+        /* ...and next... */
+        xml.readNext();
+    }
+
+    return survey_map;
+   }
+
+void xmlparser::parseVelocity(QXmlStreamReader& xml) {
+
+        /* We need a start element, like <foo> */
+        if(xml.tokenType() != QXmlStreamReader::StartElement) {
+            return;
+        }
+        /* Let's read the name... */
+        QString elementName = xml.name().toString();
+
+
+        /* ...go to the next. */
+        xml.readNext();
+        /*
+            * This elements needs to contain Characters so we know it's
+            * actually data, if it's not we'll leave.
+            */
+        if(xml.tokenType() != QXmlStreamReader::Characters) {
+            return;
+        }
+        /* Now we can add it to the map.*/
+       // map.insert(elementName, xml.text().toString());
+
+        qDebug()<<"quientroelementName "<<elementName<<" "<<xml.text().toString();
+
+        if(xml.text().toString().compare("")!=0)
+        {
+            qDebug()<<"AATTTEEENNTTIII";
+            velocity=xml.text().toString();
+        }
+            //return velocity;
+         qDebug()<<"quientrovelocity "<<velocity;
+
+    }
+
+
+
+
+
+
+QMap<QString, QString> xmlparser::parseDatacube(QXmlStreamReader& xml) {
+
+    QMap<QString, QString> datacube_map;
+    if(xml.tokenType() != QXmlStreamReader::StartElement &&
+            xml.name() == "datacube") {
+        return datacube_map;
+    }
+
+    xml.readNext();
+
+    while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "datacube")) {
+        if(xml.tokenType() == QXmlStreamReader::StartElement) {
+            /* We've found SQL. */
+            if(xml.name() == "SQL") {
+                this->addDatacubeToMap(xml, datacube_map);
+            }
+            /* We've found overlap. */
+            if(xml.name() == "overlap") {
+                parseOverlap(xml, datacube_map);
+            }
+            /* We've found PublisherDID. */
+            if(xml.name() == "PublisherDID") {
+                this->addDatacubeToMap(xml, datacube_map);
+            }
+            /* We've found Description. */
+            if(xml.name() == "Description") {
+                this->addDatacubeToMap(xml, datacube_map);
+            }
+            /* We've found Survey. */
+            if(xml.name() == "Survey") {
+                this->addDatacubeToMap(xml, datacube_map);
+            }
+            /* We've found Species. */
+            if(xml.name() == "Species") {
+                this->addDatacubeToMap(xml, datacube_map);
+            }
+            /* We've found Transition. */
+            if(xml.name() == "Transition") {
+                this->addDatacubeToMap(xml, datacube_map);
+            }
+            if(xml.name() == "URL") {
+                //temp fix: prendo solo cutout url
+                if ((xml.attributes().value("type").toString().compare("cutout") == 0) ||(xml.attributes().value("type").toString().compare("mosaic") == 0)  )
+                    this->addDatacubeToMap(xml, datacube_map);
+            }
+          /*
+            if(xml.name() == "velocity") {
+
+               parseSpectralCoordinate(xml, datacube_map);
+            }
+            */
+          /*
+            if (xml.name() =="VelocityUnit")
+            {
+               // qDebug()<<"quientro";
+                this->parseVelocity(xml);
+
+              // this->addDatacubeToMap(xml, datacube_map);
+            }
+
+            */
+           // if(xml.name() == "bounds") {
+            if(xml.name() == "vertices") {
+                parseBounds(xml, datacube_map);
+            }
+
+            datacube_map.insert("VelocityUnit", velocity);
+
+        }
+        /* ...and next... */
+        xml.readNext();
+    }
+    //    qDebug()<<datacube_map;
+
+    return datacube_map;
+}
+
+
+void xmlparser::parseSpectralCoordinate(QXmlStreamReader& xml,QMap<QString, QString> &datacube_map) {
+
+
+
+    if(xml.tokenType() != QXmlStreamReader::StartElement &&  xml.name() == "velocity") {
+        return ;
+    }
+
+    xml.readNext();
+
+    while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "velocity")) {
+        if(xml.tokenType() == QXmlStreamReader::StartElement) {
+
+            if(xml.name() == "from") {
+                this->addDatacubeToMap(xml, datacube_map);
+            }
+            if(xml.name() == "to") {
+                this->addDatacubeToMap(xml, datacube_map);
+            }
+
+        }
+        xml.readNext();
+    }
+}
+
+void xmlparser::parseOverlap(QXmlStreamReader& xml,QMap<QString, QString> &datacube_map) {
+
+
+
+    if(xml.tokenType() != QXmlStreamReader::StartElement &&  xml.name() == "overlap") {
+        return ;
+    }
+
+    xml.readNext();
+
+    while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "overlap")) {
+        if(xml.tokenType() == QXmlStreamReader::StartElement) {
+
+
+
+            if(xml.name() == "description") {
+                if(xml.tokenType() == QXmlStreamReader::StartElement){
+                    xml.readNext();
+                    datacube_map.insert("overlapDescription", xml.text().toString());
+                }
+            }
+            if(xml.name() == "code") {
+                this->addDatacubeToMap(xml, datacube_map);
+            }
+        }
+        xml.readNext();
+    }
+}
+/*
+void xmlparser::parseBounds(QXmlStreamReader& xml,QMap<QString, QString> &datacube_map) {
+
+    if(xml.tokenType() != QXmlStreamReader::StartElement &&  xml.name() == "bounds") {
+        return ;
+    }
+
+    xml.readNext();
+
+    while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "SkyCoordSystem")) {
+        if(xml.tokenType() == QXmlStreamReader::StartElement) {
+            if(xml.name() == "longitude") {
+                if(xml.tokenType() == QXmlStreamReader::StartElement){
+                    xml.readNext();
+                    xml.readNext();
+                    xml.readNext();
+                    datacube_map.insert("longitudeFrom", xml.text().toString());
+                    xml.readNext();
+                    xml.readNext();
+                    xml.readNext();
+                    xml.readNext();
+                    datacube_map.insert("longitudeTo", xml.text().toString());
+                }
+            }
+            if(xml.name() == "latitude") {
+                if(xml.tokenType() == QXmlStreamReader::StartElement){
+                    xml.readNext();
+                    xml.readNext();
+                    xml.readNext();
+                    datacube_map.insert("latitudeFrom", xml.text().toString());
+                    xml.readNext();
+                    xml.readNext();
+                    xml.readNext();
+                    xml.readNext();
+                    datacube_map.insert("latitudeTo", xml.text().toString());
+                }
+            }
+
+        }
+        xml.readNext();
+    }
+}
+
+*/
+void xmlparser::parseBounds(QXmlStreamReader& xml,QMap<QString, QString> &datacube_map) {
+
+    qDebug()<<"Leggo vertici";
+   if(xml.tokenType() != QXmlStreamReader::StartElement &&  xml.name() == "vertices") {
+        return ;
+    }
+
+    xml.readNext();
+
+    while(!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == "SkyCoordSystem")) {
+        if(xml.tokenType() == QXmlStreamReader::StartElement) {
+            if(xml.name() == "P1") {
+                if(xml.tokenType() == QXmlStreamReader::StartElement){
+
+                    xml.readNext();
+                    xml.readNext();
+                    xml.readNext();
+                    datacube_map.insert("longitudeP1", xml.text().toString());
+                    xml.readNext();
+                    xml.readNext();
+                    xml.readNext();
+                    xml.readNext();
+                    datacube_map.insert("latitudeP1", xml.text().toString());
+
+                }
+            }
+            if(xml.name() == "P2") {
+                if(xml.tokenType() == QXmlStreamReader::StartElement){
+
+                            xml.readNext();
+                            xml.readNext();
+                            xml.readNext();
+                            datacube_map.insert("longitudeP2", xml.text().toString());
+                            xml.readNext();
+                            xml.readNext();
+                            xml.readNext();
+                            xml.readNext();
+                            datacube_map.insert("latitudeP2", xml.text().toString());
+
+                }
+            }
+                if(xml.name() == "P3") {
+                    if(xml.tokenType() == QXmlStreamReader::StartElement){
+
+                        xml.readNext();
+                        xml.readNext();
+                        xml.readNext();
+                        datacube_map.insert("longitudeP3", xml.text().toString());
+                        xml.readNext();
+                        xml.readNext();
+                        xml.readNext();
+                        xml.readNext();
+                        datacube_map.insert("latitudeP3", xml.text().toString());
+                    }
+                }
+                    if(xml.name() == "P4") {
+                        if(xml.tokenType() == QXmlStreamReader::StartElement){
+
+                            xml.readNext();
+                            xml.readNext();
+                            xml.readNext();
+                            datacube_map.insert("longitudeP4", xml.text().toString());
+                            xml.readNext();
+                            xml.readNext();
+                            xml.readNext();
+                            xml.readNext();
+                            datacube_map.insert("latitudeP4", xml.text().toString());
+                        }
+            }
+
+        }
+        xml.readNext();
+    }
+}
+
+
+
+void xmlparser::addDatacubeToMap(QXmlStreamReader& xml,
+                                 QMap<QString, QString>& map) const {
+
+    /* We need a start element, like <foo> */
+    if(xml.tokenType() != QXmlStreamReader::StartElement) {
+        return;
+    }
+    /* Let's read the name... */
+    QString elementName = xml.name().toString();
+
+
+    /* ...go to the next. */
+    xml.readNext();
+    /*
+        * This elements needs to contain Characters so we know it's
+        * actually data, if it's not we'll leave.
+        */
+    if(xml.tokenType() != QXmlStreamReader::Characters) {
+        return;
+    }
+    /* Now we can add it to the map.*/
+    map.insert(elementName, xml.text().toString());
+
+    qDebug()<<"elementName "<<elementName<<" "<<xml.text().toString();
+
+}
+
+
+QString xmlparser::extractPublisherDID_fits(QList< QMap<QString,QString> >& datacubes, QMap<QString,QString> & element) {
+
+    // while(!datacubes.isEmpty()) {
+    if (!datacubes.isEmpty())
+    {
+
+        int bestOverlapIndex=0;
+        int bestOverlapCode=6;
+
+        int tmpIndex=0;
+
+        for(QList< QMap<QString,QString> >::iterator it = datacubes.begin(); it != datacubes.end(); ++it) {
+
+            QMap<QString,QString> datacube = *it;
+            if(datacube.value("code").toDouble()<bestOverlapCode)
+            {
+                bestOverlapIndex=tmpIndex;
+                bestOverlapCode=datacube.value("code").toDouble();
+            }
+
+            tmpIndex++;
+        }
+
+
+
+        //QMap<QString,QString> datacube = datacubes.takeFirst();
+        QMap<QString,QString> datacube = datacubes.at(bestOverlapIndex);
+        return datacube["PublisherDID"];
+
+    }
+    return "NULL";
+}
+
+
+
+QString xmlparser::extractPublisherDID(QList< QMap<QString,QString> >& datacubes, QMap<QString,QString> & element) {
+
+    while(!datacubes.isEmpty()) {
+        QMap<QString,QString> datacube = datacubes.takeFirst();
+
+        if(datacube["Survey"].compare(element["Survey"])==0 && datacube["Species"].compare(element["Species"])==0 && datacube["Transition"].compare(element["Transition"])==0)
+        {
+            return datacube["PublisherDID"];
+        }
+
+    }
+    return "NULL";
+}
+
+QString xmlparser::extractURL(QList< QMap<QString,QString> >& datacubes) {
+
+    while(!datacubes.isEmpty()) {
+        QMap<QString,QString> datacube = datacubes.takeFirst();
+        if(datacube["URL"].compare("")!=0)
+            return datacube["URL"];
+    }
+    return "NULL";
+}
+
+void xmlparser::addDatacubesToUI(QList< QMap<QString,QString> >& datacubes) {
+
+    while(!datacubes.isEmpty()) {
+
+        /*QGroupBox* datacubeGB = new QGroupBox("Datacube");
+            QFormLayout* layout = new QFormLayout;
+            QMap<QString,QString> datacube = datacubes.takeFirst();
+            layout->addRow("SQL", new QLineEdit(datacube["SQL"]));
+            layout->addRow("PublisherDID", new QLineEdit(datacube["PublisherDID"]));
+            layout->addRow("Survey", new QLineEdit(datacube["Survey"]));
+            layout->addRow("Species", new QLineEdit(datacube["Species"]));
+            layout->addRow("Transition", new QLineEdit(datacube["Transition"]));
+            datacubeGB->setLayout(layout);
+            //qDebug()<<"sono certa";
+            this->show();
+*/
+        //ui->listWidget_datacubes
+
+        //this->_layout->addWidget(datacubeGB);
+        //this->_layout->addWidget(datacubeGB);
+    }
+}
+
+void xmlparser::datacubeExtraction(QXmlStreamReader& xml_reader, QList< QMap<QString,QString> > &datacubes){
+
+
+    //qDebug()<<"#datacubes extraction";
+
+    while(!xml_reader.atEnd() &&
+          !xml_reader.hasError()) {
+        /* Read next element.*/
+        QXmlStreamReader::TokenType token = xml_reader.readNext();
+
+        /* If token is just StartDocument, we'll go to next.*/
+        if(token == QXmlStreamReader::StartDocument) {
+            continue;
+        }
+
+        /* If token is StartElement, we'll see if we can read it.*/
+        if(token == QXmlStreamReader::StartElement) {
+            if(xml_reader.name() == "results") {
+                continue;
+            }
+            if(xml_reader.name() == "description") {
+                continue;
+            }
+            if(xml_reader.name() == "input") {
+                continue;
+            }
+            if(xml_reader.name() == "OriginFits") {
+                continue;
+            }
+
+            if(xml_reader.name() == "datacube") {
+                //datacubes.append(this->parseDatacube(xml_reader, stringa));
+qDebug()<<"Datacubeaaaa";
+                QMap<QString, QString> datacube_map=this->parseDatacube(xml_reader);
+
+                int test_flag_nanten=-1;
+                if(datacube_map["PublisherDID"].compare("")!=0)
+                    test_flag_nanten = datacube_map["PublisherDID"].split("_", QString::SkipEmptyParts).last().toInt();
+                if(datacube_map["code"].compare("1")==0||datacube_map["code"].compare("6")==0){
+                    qDebug()<<"Do not add datacube wiwh code "<<datacube_map["code"];
+                }else if ( (datacube_map["Survey"].compare("NANTEN")==0 && test_flag_nanten==2) || datacube_map["Survey"].compare("Cornish")==0 ){
+                    qDebug()<<"NANTEN 2d Element not added";
+                    qDebug()<<datacube_map["Survey"]<<" "<<datacube_map["PublisherDID"]<<" "<<test_flag_nanten;
+                }else{
+                    if(datacube_map["Survey"].compare("")==0){
+                        datacube_map.insert("Survey","Extinction_Map");
+                        datacube_map.insert("Species","dust");
+                        datacube_map.insert("Transition","visual");
+                    }
+                    datacubes.append(datacube_map);
+                }
+            }
+
+        }
+    }
+    xml_reader.clear();
+}
+
diff --git a/Code/src/xmlparser.h b/Code/src/xmlparser.h
new file mode 100644
index 0000000000000000000000000000000000000000..6cf8a4b1dda19a18efd14b5741f3e5d9dcd32fa6
--- /dev/null
+++ b/Code/src/xmlparser.h
@@ -0,0 +1,52 @@
+#ifndef XMLPARSER_H
+#define XMLPARSER_H
+#include <QDialog>
+#include <QPointer>
+#include <QMainWindow>
+#include <QWidget>
+
+#include <QBoxLayout>
+#include <QXmlStreamReader>
+#include <QWidget>
+#include <QModelIndex>
+
+class xmlparser
+{
+
+public:
+    xmlparser();
+    ~xmlparser();
+    void parseXML(QXmlStreamReader& xml_reader, QString &stringa, bool dc=true);
+    QList< QMap<QString,QString> > parseXmlAndGetList(QXmlStreamReader & xml_reader);
+
+    void datacubeExtraction(QXmlStreamReader& xml_reader, QList< QMap<QString,QString> > &datacubes);
+    QMap<QString, QString> datacube_element;
+    void parseXML_fitsDownload(QXmlStreamReader & xml_reader, QString &stringa);
+
+private slots:
+    void parseSpectralCoordinate(QXmlStreamReader& xml, QMap<QString, QString> &datacube_map);
+    void parseOverlap(QXmlStreamReader& xml, QMap<QString, QString> &datacube_map);
+    void parseVelocity(QXmlStreamReader& xml);
+
+private:
+
+    QString queryUrl;
+    QPointer<QVBoxLayout> _layout;
+QString velocity;
+    void setupUI();
+    //QMap<QString, QString> parseDatacube(QXmlStreamReader& xml, QString &stringa);
+    QMap<QString, QString> parseDatacube(QXmlStreamReader& xml);
+    void addDatacubeToMap(QXmlStreamReader& xml,
+                             QMap<QString, QString>& map) const;
+
+    void addDatacubesToUI(QList< QMap<QString,QString> >& datacubes);
+
+    QString extractPublisherDID_fits(QList< QMap<QString,QString> >& datacubes, QMap<QString,QString>& element);
+    QString extractPublisherDID(QList< QMap<QString,QString> >& datacubes, QMap<QString,QString>& element);
+    QString extractURL(QList< QMap<QString,QString> >& datacubes);
+    void parseBounds(QXmlStreamReader& xml, QMap<QString, QString> &datacube_map);
+    QMap<QString, QString> parseSurvey(QXmlStreamReader& xml) ;
+
+
+};
+#endif // XMLPARSER_H
diff --git a/Code/ui/.DS_Store b/Code/ui/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..e5ff4550648024cf916c249093a070660c61e9df
Binary files /dev/null and b/Code/ui/.DS_Store differ
diff --git a/Code/ui/aboutform.ui b/Code/ui/aboutform.ui
new file mode 100644
index 0000000000000000000000000000000000000000..7bc8f467dd9fe4d063fa935a2e0408f893e9c886
--- /dev/null
+++ b/Code/ui/aboutform.ui
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AboutForm</class>
+ <widget class="QWidget" name="AboutForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>431</width>
+    <height>334</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>About</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="maximumSize">
+        <size>
+         <width>92</width>
+         <height>100</height>
+        </size>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="pixmap">
+        <pixmap resource="../visivo.qrc">:/icons/logo3b_vl.jpg</pixmap>
+       </property>
+       <property name="scaledContents">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Vialactea Visual Analytics Client</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>v 1.0.5</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_3">
+       <item>
+        <widget class="QLabel" name="label_7">
+         <property name="text">
+          <string>Contact:</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_5">
+         <property name="text">
+          <string>fabio.vitello@inaf.it</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_10">
+         <property name="text">
+          <string>eva.sciacca@inaf.it</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_8">
+         <property name="text">
+          <string>ugo.becciani@inaf.it</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_9">
+         <property name="text">
+          <string>alessandro.costa@inaf.it</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_11">
+         <property name="text">
+          <string>molinari@iaps.inaf.it </string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <item>
+        <widget class="QLabel" name="label_6">
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>76</height>
+          </size>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="pixmap">
+          <pixmap resource="../visivo.qrc">:/icons/visivo.png</pixmap>
+         </property>
+         <property name="scaledContents">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QLabel" name="label_4">
+         <property name="text">
+          <string>powered by VisIVO</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="../visivo.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/Code/ui/contour.ui b/Code/ui/contour.ui
new file mode 100644
index 0000000000000000000000000000000000000000..5dd127a020c6deb51bdf54b5ef73520474881669
--- /dev/null
+++ b/Code/ui/contour.ui
@@ -0,0 +1,214 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>contour</class>
+ <widget class="QWidget" name="contour">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>490</width>
+    <height>421</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Contour</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QCheckBox" name="activateCheckBox">
+       <property name="text">
+        <string>Activate Contours</string>
+       </property>
+       <property name="checked">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QCheckBox" name="LogLinearCheckBox">
+       <property name="text">
+        <string>Log/Linear</string>
+       </property>
+       <property name="checked">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QCheckBox" name="labelCheckBox">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>Value label</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QCheckBox" name="idCheckBox">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>Id label</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QLabel" name="label_contours">
+       <property name="text">
+        <string>#Contours</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="numOfContourText"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_min">
+       <property name="text">
+        <string>Lower Bound</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="minValueText"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_max">
+       <property name="text">
+        <string>Upper Bound</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="maxValueText"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_5">
+     <item>
+      <widget class="QLabel" name="label_5">
+       <property name="text">
+        <string>#Slices</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="slicesText"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_7">
+       <property name="text">
+        <string>Min</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="minFitsText"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_8">
+       <property name="text">
+        <string>Max</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="maxFitsText"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>RMS</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="RMSText"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Red</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDoubleSpinBox" name="redText"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Green</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDoubleSpinBox" name="greenText"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Blue</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QDoubleSpinBox" name="blueText"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_6">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="okButton">
+       <property name="text">
+        <string>OK</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Code/ui/dbquery.ui b/Code/ui/dbquery.ui
new file mode 100644
index 0000000000000000000000000000000000000000..5604afeb8ff23ccdc386e2d3174b0965b521d7fc
--- /dev/null
+++ b/Code/ui/dbquery.ui
@@ -0,0 +1,306 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>dbquery</class>
+ <widget class="QDialog" name="dbquery">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>495</width>
+    <height>592</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Datacube query</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QGroupBox" name="SpaceGroupBox">
+     <property name="title">
+      <string>Space axis</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_4">
+      <item>
+       <widget class="QLabel" name="label_9">
+        <property name="text">
+         <string>Galactic Coordinates</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <widget class="QLabel" name="label_5">
+          <property name="text">
+           <string>[l,b]</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="lineEdit_l">
+          <property name="text">
+           <string>180</string>
+          </property>
+          <property name="readOnly">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="lineEdit_b">
+          <property name="text">
+           <string>1</string>
+          </property>
+          <property name="readOnly">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="label_6">
+          <property name="text">
+           <string>[deg]</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_4">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_5">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <widget class="QLabel" name="label_7">
+          <property name="text">
+           <string>[r]</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="lineEdit_r">
+          <property name="text">
+           <string>0.1</string>
+          </property>
+          <property name="readOnly">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="label_8">
+          <property name="text">
+           <string>[deg]</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_2">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_3">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="SpectralGroupBox">
+     <property name="title">
+      <string>Spectral axis</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <item>
+       <widget class="QLabel" name="label_10">
+        <property name="text">
+         <string>Velocity bounds</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="label_11">
+          <property name="text">
+           <string>[vlow,vup]</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="lineEdit_vl">
+          <property name="text">
+           <string>-300.0</string>
+          </property>
+          <property name="readOnly">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="lineEdit_vu">
+          <property name="text">
+           <string>300.0</string>
+          </property>
+          <property name="readOnly">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="label_12">
+          <property name="text">
+           <string>Km/s</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_3">
+     <property name="title">
+      <string>Select</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <item>
+       <layout class="QHBoxLayout" name="SurveyHorizontalLayout">
+        <item>
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>Survey</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QComboBox" name="comboBox_surveys"/>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="SpeciesHorizontalLayout">
+        <item>
+         <widget class="QLabel" name="label_2">
+          <property name="text">
+           <string>Species</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QComboBox" name="comboBox_species"/>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="TransitionHorizontalLayout">
+        <item>
+         <widget class="QLabel" name="label_3">
+          <property name="text">
+           <string>Transition</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QComboBox" name="comboBox_transitions"/>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QPushButton" name="queryPushButton">
+        <property name="text">
+         <string>Query</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4"/>
+   </item>
+   <item>
+    <widget class="QTableWidget" name="datacube_tableWidget"/>
+   </item>
+   <item>
+    <widget class="QPushButton" name="pushButton_map">
+     <property name="text">
+      <string>Datacubes List</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Code/ui/filtercustomize.ui b/Code/ui/filtercustomize.ui
new file mode 100644
index 0000000000000000000000000000000000000000..48b77d7ce57883e4722c66bb728f5364ad43e7db
--- /dev/null
+++ b/Code/ui/filtercustomize.ui
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FilterCustomize</class>
+ <widget class="QWidget" name="FilterCustomize">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>479</width>
+    <height>453</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="1" column="0">
+      <widget class="QComboBox" name="fieldComboBox"/>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLineEdit" name="minValueLineEdit"/>
+     </item>
+     <item row="1" column="2">
+      <widget class="QLineEdit" name="maxValueLineEdit"/>
+     </item>
+     <item row="1" column="3">
+      <widget class="QPushButton" name="addPushButton">
+       <property name="text">
+        <string>Add</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Field</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Min Value</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Max Value</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QTableWidget" name="addedFilterTableWidget">
+     <column>
+      <property name="text">
+       <string>Field</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Min Value</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Max Value</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string/>
+      </property>
+     </column>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Code/ui/fitsimagestatisiticinfo.ui b/Code/ui/fitsimagestatisiticinfo.ui
new file mode 100644
index 0000000000000000000000000000000000000000..21dc78e8973b55201c361a6d53559da48e87cff1
--- /dev/null
+++ b/Code/ui/fitsimagestatisiticinfo.ui
@@ -0,0 +1,409 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>FitsImageStatisiticInfo</class>
+<widget class="QWidget" name="FitsImageStatisiticInfo">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>460</width>
+<height>337</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Form</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout_2">
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<widget class="QLabel" name="label">
+<property name="text">
+<string>File</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="filenameLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_5">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_2">
+<item>
+<widget class="QLabel" name="label_2">
+<property name="text">
+<string>Value</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line_6">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="valueLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_4">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_3">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_6">
+<item>
+<widget class="QLabel" name="label_7">
+<property name="text">
+<string>Fk5</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line_14">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_11">
+<property name="text">
+<string>α</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="raFk5Label">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line_16">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_14">
+<property name="text">
+<string>δ</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="decFk5Label">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_6">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_13">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_3">
+<item>
+<widget class="QLabel" name="label_3">
+<property name="text">
+<string>Galaptic</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line_7">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_13">
+<property name="text">
+<string>l</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="lGalapticLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line_12">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_15">
+<property name="text">
+<string>b</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="bGalapticLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_4">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_4">
+<item>
+<widget class="QLabel" name="label_5">
+<property name="text">
+<string>Ecliptic</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line_8">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_10">
+<property name="text">
+<string>λ</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="longEclipticLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line_11">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_12">
+<property name="text">
+<string>β</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="latEclipticLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_5">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_5">
+<item>
+<widget class="QLabel" name="label_6">
+<property name="text">
+<string>Image</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line_9">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_4">
+<property name="text">
+<string>X</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="xLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="Line" name="line_10">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_8">
+<property name="text">
+<string>Y</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="yLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_3">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+<item>
+<spacer name="verticalSpacer">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>20</width>
+<height>40</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</widget>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/higalselectedsources.ui b/Code/ui/higalselectedsources.ui
new file mode 100644
index 0000000000000000000000000000000000000000..56fedd591216fd89a087b5430e5e868baed1e9a3
--- /dev/null
+++ b/Code/ui/higalselectedsources.ui
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>HigalSelectedSources</class>
+<widget class="QWidget" name="HigalSelectedSources">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>402</width>
+<height>416</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Form</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout_2">
+<item>
+<widget class="QTabWidget" name="tabWidget">
+<property name="currentIndex">
+<number>-1</number>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<widget class="QPushButton" name="datasetButton">
+<property name="text">
+<string>Dataset</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="sedButton">
+<property name="text">
+<string>SED</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="plotButton">
+<property name="text">
+<string>Plot</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+</layout>
+</widget>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/loadingwidget.ui b/Code/ui/loadingwidget.ui
new file mode 100644
index 0000000000000000000000000000000000000000..6a3fcf0c33cbdb9c5cc1927a9439c9e9b53075ff
--- /dev/null
+++ b/Code/ui/loadingwidget.ui
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>LoadingWidget</class>
+<widget class="QWidget" name="LoadingWidget">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>431</width>
+<height>257</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Query</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout">
+<item>
+<widget class="QLabel" name="titleLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+<property name="alignment">
+<set>Qt::AlignCenter</set>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="verticalSpacer">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>20</width>
+<height>40</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QProgressBar" name="progressBar">
+<property name="maximum">
+<number>0</number>
+</property>
+<property name="value">
+<number>50</number>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="verticalSpacer_2">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>20</width>
+<height>40</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<spacer name="horizontalSpacer">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QPushButton" name="dismissPushButton">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<property name="text">
+<string>Dismiss</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+</layout>
+</widget>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/lutcustomize.ui b/Code/ui/lutcustomize.ui
new file mode 100644
index 0000000000000000000000000000000000000000..49dd9bdeda96bb0736b923f86498795505deb21c
--- /dev/null
+++ b/Code/ui/lutcustomize.ui
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LutCustomize</class>
+ <widget class="QWidget" name="LutCustomize">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>391</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QCustomPlot" name="histogramWidget" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>200</height>
+      </size>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>Range</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Min:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSlider" name="fromSlider">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="fromValue"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_5">
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Max:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSlider" name="toSlider">
+       <property name="sliderPosition">
+        <number>99</number>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="toValue"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="ShowColorbarCheckBox">
+     <property name="text">
+      <string>Show Colorbar</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="cancelPushButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="okPushButton">
+       <property name="text">
+        <string>Ok</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QCustomPlot</class>
+   <extends>QWidget</extends>
+   <header>src/qcustomplot.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Code/ui/mainwindow.ui b/Code/ui/mainwindow.ui
new file mode 100644
index 0000000000000000000000000000000000000000..c2e73255f96b7e46ad0125e21e084102df1d4a82
--- /dev/null
+++ b/Code/ui/mainwindow.ui
@@ -0,0 +1,1589 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="enabled">
+   <bool>true</bool>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>755</width>
+    <height>1000</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>VisIVO Desktop</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QVBoxLayout" name="verticalLayout_2">
+    <item>
+     <widget class="QFrame" name="frame">
+      <property name="maximumSize">
+       <size>
+        <width>326</width>
+        <height>16777215</height>
+       </size>
+      </property>
+      <property name="frameShape">
+       <enum>QFrame::StyledPanel</enum>
+      </property>
+      <property name="frameShadow">
+       <enum>QFrame::Raised</enum>
+      </property>
+      <layout class="QHBoxLayout" name="horizontalLayout">
+       <item>
+        <widget class="QToolButton" name="buttonUndo">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Undo</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/OP_UNDO.bmp</normaloff>:/icons/OP_UNDO.bmp</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonResetCameraAll">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Reset camera to fit all</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/ZOOM_ALL.bmp</normaloff>:/icons/ZOOM_ALL.bmp</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonResetCameraObject">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Reset camera to fit selected object</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/ZOOM.bmp</normaloff>:/icons/ZOOM.bmp</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonCreateDO">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Create a visual object from the selected Data Object</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/VBT_CLOUD.bmp</normaloff>:/icons/VBT_CLOUD.bmp</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonFilter">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/filter.png</normaloff>:/icons/filter.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonCreateGrid">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Create a grid object from the selected Data Object</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/VBT_Volume.bmp</normaloff>:/icons/VBT_Volume.bmp</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonDefineVector">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Define a vector in the Data Object</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/OPAddVector.png</normaloff>:/icons/OPAddVector.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonMathOp">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Perform a mathematical operation on a Data Object</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/OP_Parser.png</normaloff>:/icons/OP_Parser.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonPlot">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Create a plot from the Data Object</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/OP_MakePlot.png</normaloff>:/icons/OP_MakePlot.png</iconset>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="QToolButton" name="buttonMergeDO">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="toolTip">
+          <string>Merge two Data Objects to a new one</string>
+         </property>
+         <property name="text">
+          <string>...</string>
+         </property>
+         <property name="icon">
+          <iconset resource="../visivo.qrc">
+           <normaloff>:/icons/OP_Merger.png</normaloff>:/icons/OP_Merger.png</iconset>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item>
+     <widget class="QTabWidget" name="tabWidget">
+      <property name="currentIndex">
+       <number>2</number>
+      </property>
+      <widget class="QWidget" name="tabObjectTree">
+       <property name="focusPolicy">
+        <enum>Qt::TabFocus</enum>
+       </property>
+       <attribute name="title">
+        <string>Object Tree</string>
+       </attribute>
+       <layout class="QHBoxLayout" name="horizontalLayout_14">
+        <item>
+         <widget class="QSplitter" name="splitter">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <widget class="QTreeView" name="treeView"/>
+          <widget class="QTabWidget" name="dataEntity">
+           <property name="toolTip">
+            <string/>
+           </property>
+           <property name="currentIndex">
+            <number>0</number>
+           </property>
+           <widget class="QWidget" name="tabObjectInfo">
+            <property name="toolTip">
+             <string notr="true"/>
+            </property>
+            <attribute name="title">
+             <string>Object Info</string>
+            </attribute>
+            <widget class="QWidget" name="layoutWidget">
+             <property name="geometry">
+              <rect>
+               <x>21</x>
+               <y>20</y>
+               <width>271</width>
+               <height>131</height>
+              </rect>
+             </property>
+             <layout class="QVBoxLayout" name="verticalLayout_3">
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_13">
+                <item>
+                 <widget class="QLabel" name="typeLabel">
+                  <property name="text">
+                   <string/>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLabel" name="valueTypeLabel">
+                  <property name="text">
+                   <string/>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_10">
+                <item>
+                 <widget class="QLabel" name="nameLabel_2">
+                  <property name="text">
+                   <string/>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLabel" name="valueNameLabel">
+                  <property name="text">
+                   <string/>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_11">
+                <item>
+                 <widget class="QLabel" name="elementLabel">
+                  <property name="text">
+                   <string/>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLabel" name="valueElementLabel">
+                  <property name="text">
+                   <string/>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_12">
+                <item>
+                 <widget class="QLabel" name="fieldLabel">
+                  <property name="text">
+                   <string/>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLabel" name="valueFieldLabel">
+                  <property name="text">
+                   <string/>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+             </layout>
+            </widget>
+           </widget>
+           <widget class="QWidget" name="tabRendering">
+            <attribute name="title">
+             <string>Rendering</string>
+            </attribute>
+           </widget>
+           <widget class="QWidget" name="tabObjectFields">
+            <attribute name="title">
+             <string>Object Fields</string>
+            </attribute>
+            <layout class="QHBoxLayout" name="horizontalLayout_9">
+             <item>
+              <widget class="QTableWidget" name="fieldTable"/>
+             </item>
+            </layout>
+           </widget>
+          </widget>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tabViewSettings">
+       <property name="focusPolicy">
+        <enum>Qt::TabFocus</enum>
+       </property>
+       <property name="accessibleDescription">
+        <string/>
+       </property>
+       <attribute name="title">
+        <string>View Settings</string>
+       </attribute>
+       <layout class="QVBoxLayout" name="verticalLayout_7">
+        <item>
+         <widget class="QScrollArea" name="viewSettingArea">
+          <property name="widgetResizable">
+           <bool>true</bool>
+          </property>
+          <widget class="QWidget" name="scrollAreaWidgetContents">
+           <property name="geometry">
+            <rect>
+             <x>0</x>
+             <y>0</y>
+             <width>683</width>
+             <height>808</height>
+            </rect>
+           </property>
+           <layout class="QVBoxLayout" name="verticalLayout_6">
+            <item>
+             <layout class="QVBoxLayout" name="verticalLayout">
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_2">
+                <item>
+                 <widget class="QLabel" name="nameLabel">
+                  <property name="text">
+                   <string>Name</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLineEdit" name="namelineEdit">
+                  <property name="toolTip">
+                   <string>Choose a name for the Visual Object</string>
+                  </property>
+                  <property name="text">
+                   <string>Visual Object</string>
+                  </property>
+                  <property name="readOnly">
+                   <bool>true</bool>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_3">
+                <item>
+                 <widget class="QLabel" name="Xlabel">
+                  <property name="text">
+                   <string>X</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QComboBox" name="XcomboBox"/>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="XLogCheckBox">
+                  <property name="text">
+                   <string>Log</string>
+                  </property>
+                  <property name="checked">
+                   <bool>false</bool>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_4">
+                <item>
+                 <widget class="QLabel" name="Ylabel">
+                  <property name="text">
+                   <string>Y</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QComboBox" name="YcomboBox"/>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="YLogCheckBox">
+                  <property name="text">
+                   <string>Log</string>
+                  </property>
+                  <property name="checked">
+                   <bool>false</bool>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_5">
+                <item>
+                 <widget class="QLabel" name="label">
+                  <property name="text">
+                   <string>Z</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QComboBox" name="ZcomboBox"/>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="ZLogCheckBox">
+                  <property name="text">
+                   <string>Log</string>
+                  </property>
+                  <property name="checkable">
+                   <bool>true</bool>
+                  </property>
+                  <property name="checked">
+                   <bool>false</bool>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <widget class="QCheckBox" name="ShowPointsCheckBox">
+                <property name="text">
+                 <string>Show Points</string>
+                </property>
+                <property name="checked">
+                 <bool>false</bool>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+            <item>
+             <widget class="QPushButton" name="ViewSettingspushButton">
+              <property name="text">
+               <string>OK</string>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <layout class="QVBoxLayout" name="verticalLayout_5">
+              <item>
+               <widget class="QCheckBox" name="ScaleCheckBox">
+                <property name="text">
+                 <string>Scale</string>
+                </property>
+                <property name="checked">
+                 <bool>false</bool>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QCheckBox" name="ShowAxesCheckBox">
+                <property name="text">
+                 <string>Show Axes</string>
+                </property>
+                <property name="checked">
+                 <bool>false</bool>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QCheckBox" name="ShowBoxCheckBox">
+                <property name="text">
+                 <string>Show Box</string>
+                </property>
+                <property name="checked">
+                 <bool>false</bool>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+            <item>
+             <layout class="QVBoxLayout" name="VectorLayout">
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_8">
+                <item>
+                 <widget class="QLabel" name="XVectorsLabel">
+                  <property name="text">
+                   <string>Vector X</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QComboBox" name="XVectorsComboBox"/>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_7">
+                <item>
+                 <widget class="QLabel" name="YVectorsLabel">
+                  <property name="text">
+                   <string>Vector Y</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QComboBox" name="YVectorsComboBox"/>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_6">
+                <item>
+                 <widget class="QLabel" name="ZVectorsLabel">
+                  <property name="text">
+                   <string>Vector Z</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QComboBox" name="ZVectorsComboBox"/>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <widget class="QCheckBox" name="ShowVectorsCheckBox">
+                <property name="text">
+                 <string>Show Vectors</string>
+                </property>
+                <property name="checked">
+                 <bool>false</bool>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </widget>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tabOpParameters">
+       <property name="enabled">
+        <bool>true</bool>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>412</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="focusPolicy">
+        <enum>Qt::TabFocus</enum>
+       </property>
+       <attribute name="title">
+        <string>Op. Parameters</string>
+       </attribute>
+       <layout class="QVBoxLayout" name="verticalLayout_999">
+        <item>
+         <widget class="QGroupBox" name="importerGroupBox">
+          <property name="enabled">
+           <bool>true</bool>
+          </property>
+          <property name="title">
+           <string>Importer</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_4">
+           <item>
+            <layout class="QHBoxLayout" name="horizontalLayout_15">
+             <item>
+              <widget class="QLabel" name="label_2">
+               <property name="text">
+                <string>Missing Value</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <spacer name="horizontalSpacer_2">
+               <property name="orientation">
+                <enum>Qt::Horizontal</enum>
+               </property>
+               <property name="sizeHint" stdset="0">
+                <size>
+                 <width>40</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </spacer>
+             </item>
+             <item>
+              <widget class="QLineEdit" name="missingValueLineEdit"/>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <layout class="QHBoxLayout" name="horizontalLayout_16">
+             <item>
+              <widget class="QLabel" name="label_3">
+               <property name="text">
+                <string>Text Value      </string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <spacer name="horizontalSpacer">
+               <property name="orientation">
+                <enum>Qt::Horizontal</enum>
+               </property>
+               <property name="sizeHint" stdset="0">
+                <size>
+                 <width>40</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </spacer>
+             </item>
+             <item>
+              <widget class="QLineEdit" name="textValueLineEdit"/>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <layout class="QHBoxLayout" name="horizontalLayout_17">
+             <item>
+              <widget class="QRadioButton" name="tableRadioButton">
+               <property name="text">
+                <string>Table</string>
+               </property>
+               <property name="checked">
+                <bool>true</bool>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QRadioButton" name="volumeRadioButton">
+               <property name="text">
+                <string>Volume</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <widget class="QGroupBox" name="volumeGroupBox">
+             <property name="title">
+              <string>Volume</string>
+             </property>
+             <layout class="QVBoxLayout" name="verticalLayout_8">
+              <item>
+               <widget class="QLabel" name="label_4">
+                <property name="text">
+                 <string>Number of computational cells:</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <layout class="QGridLayout" name="gridLayout">
+                <item row="0" column="1">
+                 <widget class="QLabel" name="label_6">
+                  <property name="text">
+                   <string>Y</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="1" column="0">
+                 <widget class="QLineEdit" name="xComputationaCellLineEdit"/>
+                </item>
+                <item row="0" column="0">
+                 <widget class="QLabel" name="label_5">
+                  <property name="text">
+                   <string>X</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="1" column="1">
+                 <widget class="QLineEdit" name="yComputationaCellLineEdit"/>
+                </item>
+                <item row="0" column="2">
+                 <widget class="QLabel" name="label_7">
+                  <property name="text">
+                   <string>Z</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="1" column="2">
+                 <widget class="QLineEdit" name="zComputationaCellLineEdit"/>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <widget class="QLabel" name="label_8">
+                <property name="text">
+                 <string>Cell sizes:</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <layout class="QGridLayout" name="gridLayout_2">
+                <item row="0" column="1">
+                 <widget class="QLabel" name="label_10">
+                  <property name="text">
+                   <string>Y dim</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="0" column="2">
+                 <widget class="QLabel" name="label_11">
+                  <property name="text">
+                   <string>Z dim</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="0" column="0">
+                 <widget class="QLabel" name="label_9">
+                  <property name="text">
+                   <string>X dim</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="1" column="0">
+                 <widget class="QLineEdit" name="xCellSizeLineEdit"/>
+                </item>
+                <item row="1" column="1">
+                 <widget class="QLineEdit" name="yCellSizeLineEdit"/>
+                </item>
+                <item row="1" column="2">
+                 <widget class="QLineEdit" name="zCellSizeLineEdit"/>
+                </item>
+               </layout>
+              </item>
+             </layout>
+            </widget>
+           </item>
+           <item>
+            <widget class="QPushButton" name="importPushButton">
+             <property name="text">
+              <string>Import</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="filterGroupBox">
+          <property name="enabled">
+           <bool>true</bool>
+          </property>
+          <property name="title">
+           <string>Filter</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_9">
+           <item>
+            <widget class="QComboBox" name="selectFilterComboBox">
+             <item>
+              <property name="text">
+               <string>Add Identifier</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Append</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Cartesian 2 Polar</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Cut</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Decimator</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Extract List</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Extraction</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Grid 2 Point</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Include</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Math Op</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Merge</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Module</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Multi Resolution</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Point distribute</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Point Property</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Randomizer</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Scalar Distribution</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Select Columns</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Select Rows</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Sigma Contours</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Split Table</string>
+              </property>
+             </item>
+             <item>
+              <property name="text">
+               <string>Write VO Table</string>
+              </property>
+             </item>
+            </widget>
+           </item>
+           <item>
+            <widget class="QTextBrowser" name="filterDescriptionTextBrowser">
+             <property name="enabled">
+              <bool>false</bool>
+             </property>
+             <property name="html">
+              <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'.SF NS Text'; font-size:13pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Arial'; color:#232323;&quot;&gt;This filter adds a new column to the input table. The column name is given in Column Name field, with a sequence of value starting from a Start Number value (Default value is 0).&lt;/span&gt; &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QGroupBox" name="addIdentifierParameterGroupBox">
+             <property name="title">
+              <string>Parameter</string>
+             </property>
+             <layout class="QGridLayout" name="gridLayout_3">
+              <item row="0" column="1">
+               <widget class="QLineEdit" name="addIdNewColName">
+                <property name="text">
+                 <string>NewColumnName</string>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="0">
+               <widget class="QLabel" name="label_12">
+                <property name="text">
+                 <string>Start Number:</string>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="0">
+               <widget class="QLabel" name="label_13">
+                <property name="text">
+                 <string>New Column:</string>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="1">
+               <widget class="QLineEdit" name="addIdStartNumber">
+                <property name="text">
+                 <string>0</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+           </item>
+           <item>
+            <widget class="QGroupBox" name="appendParameterGroupBox">
+             <property name="title">
+              <string>Parameter</string>
+             </property>
+             <layout class="QVBoxLayout" name="verticalLayout_11">
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_19">
+                <item>
+                 <widget class="QLabel" name="selectedVBT">
+                  <property name="text">
+                   <string/>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_18">
+                <item>
+                 <widget class="QListWidget" name="availableVBTlistWidget"/>
+                </item>
+                <item>
+                 <layout class="QVBoxLayout" name="verticalLayout_10">
+                  <item>
+                   <widget class="QPushButton" name="addVBTpushButton">
+                    <property name="text">
+                     <string>&gt;&gt;</string>
+                    </property>
+                   </widget>
+                  </item>
+                  <item>
+                   <widget class="QPushButton" name="removeVBTpushButton">
+                    <property name="text">
+                     <string>&lt;&lt;</string>
+                    </property>
+                   </widget>
+                  </item>
+                 </layout>
+                </item>
+                <item>
+                 <widget class="QListWidget" name="selectedVBTlistWidget"/>
+                </item>
+               </layout>
+              </item>
+             </layout>
+            </widget>
+           </item>
+           <item>
+            <widget class="QPushButton" name="runFilterPushButton">
+             <property name="text">
+              <string>Run filter</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <spacer name="verticalSpacer_2">
+             <property name="orientation">
+              <enum>Qt::Vertical</enum>
+             </property>
+             <property name="sizeHint" stdset="0">
+              <size>
+               <width>20</width>
+               <height>40</height>
+              </size>
+             </property>
+            </spacer>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>755</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <widget class="QMenu" name="menuImport">
+     <property name="title">
+      <string>Import</string>
+     </property>
+     <addaction name="actionImportAscii"/>
+     <addaction name="actionCsv"/>
+     <addaction name="actionImportBinary"/>
+     <addaction name="actionImportVoTable"/>
+     <addaction name="actionImportRawGrid"/>
+     <addaction name="actionImportRawPoints"/>
+     <addaction name="actionImportFlyOutput"/>
+     <addaction name="actionImportHDF5"/>
+     <addaction name="actionImportGadget"/>
+     <addaction name="actionImportFits"/>
+     <addaction name="actionImportFitsImage"/>
+     <addaction name="actionDatacube"/>
+     <addaction name="separator"/>
+     <addaction name="actionVTK_ImageData"/>
+     <addaction name="actionVTK_PolyData"/>
+     <addaction name="actionTEST_DC3D"/>
+    </widget>
+    <widget class="QMenu" name="menuExport">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="title">
+      <string>Export</string>
+     </property>
+     <addaction name="actionExportAscii"/>
+     <addaction name="actionExportBinary"/>
+     <addaction name="actionExportVoTable"/>
+    </widget>
+    <addaction name="actionNew"/>
+    <addaction name="separator"/>
+    <addaction name="actionOpen"/>
+    <addaction name="actionSave"/>
+    <addaction name="actionSaveAs"/>
+    <addaction name="separator"/>
+    <addaction name="menuImport"/>
+    <addaction name="menuExport"/>
+    <addaction name="separator"/>
+    <addaction name="actionPrint"/>
+    <addaction name="actionPrintPreview"/>
+    <addaction name="actionClose"/>
+   </widget>
+   <widget class="QMenu" name="menuEdit">
+    <property name="enabled">
+     <bool>true</bool>
+    </property>
+    <property name="title">
+     <string>Edit</string>
+    </property>
+    <addaction name="actionUndo"/>
+    <addaction name="actionDelete"/>
+   </widget>
+   <widget class="QMenu" name="menuView">
+    <property name="enabled">
+     <bool>false</bool>
+    </property>
+    <property name="title">
+     <string>View</string>
+    </property>
+    <widget class="QMenu" name="menuAddView">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="title">
+      <string>Add View</string>
+     </property>
+     <addaction name="action2DView"/>
+     <addaction name="action3DView"/>
+     <addaction name="actionOrthoSlices"/>
+     <addaction name="actionPlotVIew"/>
+    </widget>
+    <addaction name="menuAddView"/>
+   </widget>
+   <widget class="QMenu" name="menuTools">
+    <property name="enabled">
+     <bool>true</bool>
+    </property>
+    <property name="title">
+     <string>Tools</string>
+    </property>
+    <addaction name="actionVialactea"/>
+   </widget>
+   <widget class="QMenu" name="menuWindow">
+    <property name="title">
+     <string>Window</string>
+    </property>
+    <addaction name="actionOperation_queue"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuEdit"/>
+   <addaction name="menuView"/>
+   <addaction name="menuTools"/>
+   <addaction name="menuWindow"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionNew">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>New</string>
+   </property>
+  </action>
+  <action name="actionOpen">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Open</string>
+   </property>
+  </action>
+  <action name="actionSave">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Save</string>
+   </property>
+  </action>
+  <action name="actionSaveAs">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Save as</string>
+   </property>
+  </action>
+  <action name="actionPrint">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Print</string>
+   </property>
+  </action>
+  <action name="actionPrintPreview">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Print Preview</string>
+   </property>
+  </action>
+  <action name="actionPage_Setup">
+   <property name="text">
+    <string>Page Setup</string>
+   </property>
+  </action>
+  <action name="actionRecent_Files">
+   <property name="text">
+    <string>Recent Files</string>
+   </property>
+  </action>
+  <action name="actionImportAscii">
+   <property name="text">
+    <string>Ascii</string>
+   </property>
+  </action>
+  <action name="actionImportBinary">
+   <property name="text">
+    <string>Binary</string>
+   </property>
+  </action>
+  <action name="actionImportRawGrid">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Raw Grid</string>
+   </property>
+  </action>
+  <action name="actionImportRawPoints">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Raw Points</string>
+   </property>
+  </action>
+  <action name="actionImportFlyOutput">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Fly Output</string>
+   </property>
+  </action>
+  <action name="actionImportHDF5">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>HDF5</string>
+   </property>
+  </action>
+  <action name="actionImportGadget">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Gadget</string>
+   </property>
+  </action>
+  <action name="actionImportFits">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Fits</string>
+   </property>
+  </action>
+  <action name="actionImportVoTable">
+   <property name="text">
+    <string>VoTable</string>
+   </property>
+  </action>
+  <action name="actionExportAscii">
+   <property name="text">
+    <string>Ascii</string>
+   </property>
+  </action>
+  <action name="actionExportBinary">
+   <property name="text">
+    <string>Binary</string>
+   </property>
+  </action>
+  <action name="actionExportVoTable">
+   <property name="text">
+    <string>VoTable</string>
+   </property>
+  </action>
+  <action name="actionUndo">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Undo</string>
+   </property>
+  </action>
+  <action name="actionDelete">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Delete</string>
+   </property>
+  </action>
+  <action name="action2DView">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>2D View</string>
+   </property>
+  </action>
+  <action name="action3DView">
+   <property name="text">
+    <string>3D View</string>
+   </property>
+  </action>
+  <action name="actionOrthoSlices">
+   <property name="text">
+    <string>Ortho Slices</string>
+   </property>
+  </action>
+  <action name="actionPlotVIew">
+   <property name="text">
+    <string>Plot VIew</string>
+   </property>
+  </action>
+  <action name="action2DFunctions">
+   <property name="text">
+    <string>2D Functions</string>
+   </property>
+  </action>
+  <action name="actionSendVOTable">
+   <property name="text">
+    <string>Send VOTable</string>
+   </property>
+  </action>
+  <action name="actionShowObjects">
+   <property name="text">
+    <string>Show Objects</string>
+   </property>
+  </action>
+  <action name="actionPointDistribute">
+   <property name="text">
+    <string>Point Distribute</string>
+   </property>
+  </action>
+  <action name="actionSubsampler">
+   <property name="text">
+    <string>Subsampler</string>
+   </property>
+  </action>
+  <action name="actionPicker">
+   <property name="text">
+    <string>Picker</string>
+   </property>
+  </action>
+  <action name="actionExtractCluster">
+   <property name="text">
+    <string>Extract Cluster</string>
+   </property>
+  </action>
+  <action name="actionSmoothSurface">
+   <property name="text">
+    <string>Smooth Surface</string>
+   </property>
+  </action>
+  <action name="actionRandomize">
+   <property name="text">
+    <string>Randomize</string>
+   </property>
+  </action>
+  <action name="actionCreateVisualObject">
+   <property name="text">
+    <string>Create Visual Object</string>
+   </property>
+  </action>
+  <action name="actionCreateGridObject">
+   <property name="text">
+    <string>Create Grid Object</string>
+   </property>
+  </action>
+  <action name="actionCreateImageObject">
+   <property name="text">
+    <string>Create Image Object</string>
+   </property>
+  </action>
+  <action name="actionAddVector">
+   <property name="text">
+    <string>Add Vector</string>
+   </property>
+  </action>
+  <action name="actionMathOp">
+   <property name="text">
+    <string>Math Op.</string>
+   </property>
+  </action>
+  <action name="actionCreatePlot">
+   <property name="text">
+    <string>Create Plot</string>
+   </property>
+  </action>
+  <action name="actionExtractIsoSurfaces">
+   <property name="text">
+    <string>Extract IsoSurfaces</string>
+   </property>
+  </action>
+  <action name="actionAboutVisIVO">
+   <property name="text">
+    <string>about VisIVO</string>
+   </property>
+  </action>
+  <action name="actionVTK_ImageData">
+   <property name="text">
+    <string>VTK ImageData </string>
+   </property>
+  </action>
+  <action name="actionVTK_PolyData">
+   <property name="text">
+    <string>VTK PolyData</string>
+   </property>
+  </action>
+  <action name="actionBye">
+   <property name="text">
+    <string>Quit</string>
+   </property>
+  </action>
+  <action name="actionCiao">
+   <property name="text">
+    <string>Quit</string>
+   </property>
+  </action>
+  <action name="actionExit">
+   <property name="text">
+    <string>Close</string>
+   </property>
+  </action>
+  <action name="actionClose">
+   <property name="text">
+    <string>Close</string>
+   </property>
+  </action>
+  <action name="actionHi_Gal">
+   <property name="text">
+    <string>Hi-Gal</string>
+   </property>
+  </action>
+  <action name="actionOperation_queue">
+   <property name="text">
+    <string>Operation queue</string>
+   </property>
+  </action>
+  <action name="actionImportFitsImage">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Fits Image</string>
+   </property>
+  </action>
+  <action name="actionVialactea">
+   <property name="text">
+    <string>Vialactea</string>
+   </property>
+  </action>
+  <action name="actionDatacube">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Datacube</string>
+   </property>
+  </action>
+  <action name="actionCsv">
+   <property name="text">
+    <string>Csv</string>
+   </property>
+  </action>
+  <action name="actionTEST_DC3D">
+   <property name="text">
+    <string>TEST DC3D</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+D</string>
+   </property>
+  </action>
+ </widget>
+ <resources>
+  <include location="../visivo.qrc"/>
+ </resources>
+ <connections>
+  <connection>
+   <sender>buttonCreateDO</sender>
+   <signal>clicked(bool)</signal>
+   <receiver>MainWindow</receiver>
+   <slot>viewSetting()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>140</x>
+     <y>60</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>268</x>
+     <y>446</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>treeView</sender>
+   <signal>doubleClicked(QModelIndex)</signal>
+   <receiver>treeView</receiver>
+   <slot>expand(QModelIndex)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>174</x>
+     <y>247</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>268</x>
+     <y>307</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>ViewSettingspushButton</sender>
+   <signal>clicked()</signal>
+   <receiver>MainWindow</receiver>
+   <slot>viewSettingOk()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>123</x>
+     <y>721</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>268</x>
+     <y>446</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>tabWidget</sender>
+   <signal>currentChanged(int)</signal>
+   <receiver>MainWindow</receiver>
+   <slot>switchTab(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>268</x>
+     <y>479</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>268</x>
+     <y>446</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>treeView</sender>
+   <signal>clicked(QModelIndex)</signal>
+   <receiver>MainWindow</receiver>
+   <slot>itemSelected()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>174</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>174</x>
+     <y>345</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/Code/ui/operationqueue.ui b/Code/ui/operationqueue.ui
new file mode 100644
index 0000000000000000000000000000000000000000..3fa563051e02d4569209af97104efd59157d0b08
--- /dev/null
+++ b/Code/ui/operationqueue.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>OperationQueue</class>
+<widget class="QWidget" name="OperationQueue">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>685</width>
+<height>551</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Queue</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout">
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<widget class="QPushButton" name="pushButton_2">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<property name="text">
+<string>Refresh</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="pushButton">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<property name="text">
+<string>Clear</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="QTableWidget" name="tableWidget">
+<attribute name="horizontalHeaderVisible">
+<bool>true</bool>
+</attribute>
+<attribute name="horizontalHeaderCascadingSectionResizes">
+<bool>false</bool>
+</attribute>
+<column>
+<property name="text">
+<string>Id</string>
+</property>
+</column>
+<column>
+<property name="text">
+<string>Name</string>
+</property>
+</column>
+<column>
+<property name="text">
+<string>Status</string>
+</property>
+</column>
+</widget>
+</item>
+</layout>
+</widget>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/plotwindow.ui b/Code/ui/plotwindow.ui
new file mode 100644
index 0000000000000000000000000000000000000000..605a37e9040decd08069e88c9f0e1c5ee1cdb65a
--- /dev/null
+++ b/Code/ui/plotwindow.ui
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>PlotWindow</class>
+<widget class="QWidget" name="PlotWindow">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>729</width>
+<height>597</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Form</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout_2">
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<widget class="QLabel" name="label_2">
+<property name="text">
+<string>X:</string>
+</property>
+<property name="alignment">
+<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QComboBox" name="xComboBox"/>
+</item>
+<item>
+<widget class="QCheckBox" name="logxCheckBox">
+<property name="text">
+<string>Log</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QLabel" name="label">
+<property name="text">
+<string>Y:</string>
+</property>
+<property name="alignment">
+<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QComboBox" name="yComboBox"/>
+</item>
+<item>
+<widget class="QCheckBox" name="logyCheckBox">
+<property name="text">
+<string>Log</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QPushButton" name="plotButton">
+<property name="text">
+<string>Plot</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="QCustomPlot" name="customPlot" native="true"/>
+</item>
+</layout>
+</widget>
+<customwidgets>
+<customwidget>
+<class>QCustomPlot</class>
+<extends>QWidget</extends>
+<header>src/qcustomplot.h</header>
+<container>1</container>
+</customwidget>
+</customwidgets>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/sedfitgrid_thick.ui b/Code/ui/sedfitgrid_thick.ui
new file mode 100644
index 0000000000000000000000000000000000000000..1b3bdd454caba346e6a98f61437a42b3a1d4ad88
--- /dev/null
+++ b/Code/ui/sedfitgrid_thick.ui
@@ -0,0 +1,396 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>SedFitgrid_thick</class>
+<widget class="QWidget" name="SedFitgrid_thick">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>489</width>
+<height>640</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Form</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout">
+<item>
+<widget class="QLabel" name="label">
+<property name="text">
+<string>Mass</string>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<widget class="QLabel" name="label_2">
+<property name="text">
+<string>lambda_0</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="minLambda_0LineEdit">
+<property name="text">
+<string>10</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_3">
+<property name="text">
+<string>Max: </string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="maxLambda_0LineEdit">
+<property name="text">
+<string>300</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_4">
+<property name="text">
+<string>Step:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="stepLambda_0LineEdit">
+<property name="text">
+<string>100</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_5">
+<property name="text">
+<string>Temperature</string>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_3">
+<item>
+<widget class="QLabel" name="label_6">
+<property name="text">
+<string>Min:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="tempMinLineEdit">
+<property name="text">
+<string>7</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_7">
+<property name="text">
+<string>Max:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="tempMaxLineEdit">
+<property name="text">
+<string>40</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_8">
+<property name="text">
+<string>Step:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="tempStepLineEdit">
+<property name="text">
+<string>50</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_9">
+<property name="text">
+<string>Beta</string>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_4">
+<item>
+<widget class="QLabel" name="label_10">
+<property name="text">
+<string>Min:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="betaMinLineEdit">
+<property name="text">
+<string>2</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_11">
+<property name="text">
+<string>Max:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="betaMaxLineEdit">
+<property name="text">
+<string>2</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_12">
+<property name="text">
+<string>Step:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="betaStepLineEdit">
+<property name="text">
+<string>1</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_3">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_6">
+<item>
+<widget class="QLabel" name="label_13">
+<property name="text">
+<string>Size:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="sizeLineEdit">
+<property name="text">
+<string>20</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_3">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QLabel" name="label_20">
+<property name="text">
+<string>Dist</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="distLineEdit">
+<property name="text">
+<string>1000</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="QLabel" name="label_16">
+<property name="text">
+<string>Scale Factor</string>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_5">
+<item>
+<widget class="QLabel" name="label_17">
+<property name="text">
+<string>Min:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="scaleMinLineEdit">
+<property name="text">
+<string>1</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_18">
+<property name="text">
+<string>Max:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="scaleMaxLineEdit">
+<property name="text">
+<string>1</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_19">
+<property name="text">
+<string>Step:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="scaleStepLineEdit">
+<property name="text">
+<string>1</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_4">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_7">
+<item>
+<widget class="QLabel" name="label_14">
+<property name="text">
+<string>sref_opacity:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="srefOpacityLineEdit">
+<property name="text">
+<string>0.1</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_15">
+<property name="text">
+<string>sref_wavelength:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="srefWavelengthLineEdit">
+<property name="text">
+<string>300</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_5">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QTableWidget" name="tableWidget">
+<column>
+<property name="text">
+<string>Wavelength</string>
+</property>
+</column>
+<column>
+<property name="text">
+<string>Ulimit</string>
+</property>
+</column>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_2">
+<item>
+<spacer name="horizontalSpacer">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QPushButton" name="pushButton">
+<property name="text">
+<string>Fit</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+</layout>
+</widget>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/sedfitgrid_thin.ui b/Code/ui/sedfitgrid_thin.ui
new file mode 100644
index 0000000000000000000000000000000000000000..8f49b0759e4925d99890d4969dcb91d5ca2989ac
--- /dev/null
+++ b/Code/ui/sedfitgrid_thin.ui
@@ -0,0 +1,305 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SedFitGrid_thin</class>
+ <widget class="QWidget" name="SedFitGrid_thin">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>534</width>
+    <height>508</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Mass</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Min: </string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="minMassLineEdit">
+       <property name="text">
+        <string>10</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Max: </string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="maxMassLineEdit">
+       <property name="text">
+        <string>5000</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>Step:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="stepMassLineEdit">
+       <property name="text">
+        <string>100</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_5">
+     <property name="text">
+      <string>Temperature</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QLabel" name="label_6">
+       <property name="text">
+        <string>Min:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="tempMinLineEdit">
+       <property name="text">
+        <string>7</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_7">
+       <property name="text">
+        <string>Max:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="tempMaxLineEdit">
+       <property name="text">
+        <string>40</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_8">
+       <property name="text">
+        <string>Step:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="tempStepLineEdit">
+       <property name="text">
+        <string>50</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line_2">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_9">
+     <property name="text">
+      <string>Beta</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QLabel" name="label_10">
+       <property name="text">
+        <string>Min:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="betaMinLineEdit">
+       <property name="text">
+        <string>2</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_11">
+       <property name="text">
+        <string>Max:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="betaMaxLineEdit">
+       <property name="text">
+        <string>2</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_12">
+       <property name="text">
+        <string>Step:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="betaStepLineEdit">
+       <property name="text">
+        <string>1</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line_3">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_6">
+     <item>
+      <widget class="QLabel" name="label_13">
+       <property name="text">
+        <string>Dist:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="distLineEdit">
+       <property name="text">
+        <string>1000</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_14">
+       <property name="text">
+        <string>sref_opacity:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="srefOpacityLineEdit">
+       <property name="text">
+        <string>0.1</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_15">
+       <property name="text">
+        <string>sref_wavelength:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="srefWavelengthLineEdit">
+       <property name="text">
+        <string>300</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line_4">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QTableWidget" name="tableWidget">
+     <column>
+      <property name="text">
+       <string>Wavelength</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Ulimit</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="pushButton">
+       <property name="text">
+        <string>Fit</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Code/ui/sedvisualizerplot.ui b/Code/ui/sedvisualizerplot.ui
new file mode 100644
index 0000000000000000000000000000000000000000..92cce6a87715cb2aaff1ef0ebe390b9039d6516a
--- /dev/null
+++ b/Code/ui/sedvisualizerplot.ui
@@ -0,0 +1,774 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SEDVisualizerPlot</class>
+ <widget class="QMainWindow" name="SEDVisualizerPlot">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>932</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="widget">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout_2">
+    <item>
+     <widget class="QSplitter" name="splitter_2">
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+      <widget class="QSplitter" name="splitter">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
+         <horstretch>1</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>590</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <widget class="QTabWidget" name="atabWidget">
+        <property name="enabled">
+         <bool>true</bool>
+        </property>
+        <property name="minimumSize">
+         <size>
+          <width>0</width>
+          <height>600</height>
+         </size>
+        </property>
+        <property name="focusPolicy">
+         <enum>Qt::NoFocus</enum>
+        </property>
+        <property name="currentIndex">
+         <number>0</number>
+        </property>
+        <widget class="QCustomPlot" name="customPlot">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+           <horstretch>0</horstretch>
+           <verstretch>100</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+         <attribute name="title">
+          <string>SED</string>
+         </attribute>
+        </widget>
+        <widget class="QCustomPlot" name="histogramPlot">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+           <horstretch>0</horstretch>
+           <verstretch>100</verstretch>
+          </sizepolicy>
+         </property>
+         <attribute name="title">
+          <string>Histogram</string>
+         </attribute>
+        </widget>
+       </widget>
+       <widget class="QTextBrowser" name="outputTextBrowser">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
+          <horstretch>0</horstretch>
+          <verstretch>1</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="minimumSize">
+         <size>
+          <width>0</width>
+          <height>10</height>
+         </size>
+        </property>
+        <property name="maximumSize">
+         <size>
+          <width>16777215</width>
+          <height>16777215</height>
+         </size>
+        </property>
+       </widget>
+       <widget class="QTableWidget" name="resultsTableWidget">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
+          <horstretch>0</horstretch>
+          <verstretch>1</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="minimumSize">
+         <size>
+          <width>0</width>
+          <height>10</height>
+         </size>
+        </property>
+        <property name="editTriggers">
+         <set>QAbstractItemView::NoEditTriggers</set>
+        </property>
+        <property name="selectionMode">
+         <enum>QAbstractItemView::SingleSelection</enum>
+        </property>
+        <property name="selectionBehavior">
+         <enum>QAbstractItemView::SelectRows</enum>
+        </property>
+        <column>
+         <property name="text">
+          <string>id</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>clump_mass</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>compact_mass_fraction</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>clump_upper_age</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>dust_temp</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>bolometric_luminosity</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>n_start_tot</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>m_star_tot</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>n_star_zams</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>m_star_zams</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>l_star_tot</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>l_star_zams</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>zams_luminosity_1</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>zams_mass_1</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>zams_temperature_1</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>zams_luminosity_2</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>zams_mass_2</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>zams_temperature_2</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>zams_luminosity_3</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>zams_mass_3</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>zams_temperature_3</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>m1</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>pacs1</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>pacs2</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>pacs3</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>spir1</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>spir2</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>spir3</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>laboc</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>bol11</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>CHI2</string>
+         </property>
+        </column>
+        <column>
+         <property name="text">
+          <string>DIST</string>
+         </property>
+        </column>
+       </widget>
+      </widget>
+      <widget class="QWidget" name="widget_2" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>10</width>
+         <height>0</height>
+        </size>
+       </property>
+       <layout class="QVBoxLayout" name="verticalLayout_3">
+        <item>
+         <widget class="QGroupBox" name="visualizationModeBox">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="minimumSize">
+           <size>
+            <width>10</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="title">
+           <string>Visualization mode:</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout">
+           <item>
+            <widget class="QCheckBox" name="multiSelectCheckBox">
+             <property name="text">
+              <string>Multi Select</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="showOutputBox">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="minimumSize">
+           <size>
+            <width>10</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="title">
+           <string>Select SED output to view:</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_4">
+           <item>
+            <widget class="QRadioButton" name="logRadioButton">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>10</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Log</string>
+             </property>
+             <property name="checked">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QRadioButton" name="resultsRadioButton">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>10</width>
+               <height>0</height>
+              </size>
+             </property>
+             <property name="text">
+              <string>Results</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="generatedSedBox">
+          <property name="enabled">
+           <bool>true</bool>
+          </property>
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="minimumSize">
+           <size>
+            <width>10</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="title">
+           <string>Generated Fit:</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_5">
+           <item>
+            <widget class="QPushButton" name="clearAllButton">
+             <property name="text">
+              <string>Clear All</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="collapseCheckBox">
+          <property name="text">
+           <string>Collapse All</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox_2">
+          <property name="title">
+           <string>Fit</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_7">
+           <item>
+            <widget class="QPushButton" name="theoreticalPushButton">
+             <property name="text">
+              <string>Theoretical</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QPushButton" name="greyBodyPushButton">
+             <property name="text">
+              <string>Grey-body</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="theorGroupBox">
+          <property name="enabled">
+           <bool>true</bool>
+          </property>
+          <property name="title">
+           <string>Theoretical</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_6">
+           <item>
+            <widget class="QLabel" name="label_6">
+             <property name="text">
+              <string>Delta chi2</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="delta_chi2_lineEdit">
+             <property name="text">
+              <string>3</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="label">
+             <property name="text">
+              <string>Dist</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="distTheoLineEdit">
+             <property name="text">
+              <string>2000</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="label_2">
+             <property name="text">
+              <string>prefilter threshold</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="prefilterLineEdit">
+             <property name="text">
+              <string>0.8</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="label_3">
+             <property name="text">
+              <string>mid_ir</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="mid_irLineEdit">
+             <property name="text">
+              <string>1</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="label_4">
+             <property name="text">
+              <string>far_ir</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="far_irLineEdit">
+             <property name="text">
+              <string>1</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="label_5">
+             <property name="text">
+              <string>submm</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="submmLineEdit">
+             <property name="text">
+              <string>1</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QPushButton" name="theorConfirmPushButton">
+             <property name="text">
+              <string>Confirm</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="greyBodyGroupBox">
+          <property name="title">
+           <string>Grey-Body</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_8">
+           <item>
+            <widget class="QPushButton" name="thinButton">
+             <property name="text">
+              <string>Thin</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QPushButton" name="Thick">
+             <property name="text">
+              <string>Thick</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeType">
+           <enum>QSizePolicy::Expanding</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionScreenshot"/>
+    <addaction name="actionSave"/>
+    <addaction name="actionLoad"/>
+   </widget>
+   <widget class="QMenu" name="menuMode">
+    <property name="title">
+     <string>Mode</string>
+    </property>
+    <addaction name="actionEdit"/>
+    <addaction name="actionSelect"/>
+   </widget>
+   <widget class="QMenu" name="menuAction">
+    <property name="title">
+     <string>Action</string>
+    </property>
+    <widget class="QMenu" name="menuFit">
+     <property name="title">
+      <string>Fit</string>
+     </property>
+     <widget class="QMenu" name="menuTheoretical_model">
+      <property name="title">
+       <string>Theoretical model</string>
+      </property>
+      <addaction name="TheoreticalLocaleFit"/>
+      <addaction name="TheoreticalRemoteFit"/>
+     </widget>
+     <widget class="QMenu" name="menuGrey_body">
+      <property name="title">
+       <string>Grey-body</string>
+      </property>
+      <widget class="QMenu" name="menuThin">
+       <property name="title">
+        <string>Thin</string>
+       </property>
+       <addaction name="ThinLocalFit"/>
+       <addaction name="ThinRemore"/>
+      </widget>
+      <widget class="QMenu" name="menuThick">
+       <property name="title">
+        <string>Thick</string>
+       </property>
+       <addaction name="ThickLocalFit"/>
+       <addaction name="ThickRemote"/>
+      </widget>
+      <addaction name="menuThin"/>
+      <addaction name="menuThick"/>
+     </widget>
+     <addaction name="menuTheoretical_model"/>
+     <addaction name="menuGrey_body"/>
+    </widget>
+    <addaction name="menuFit"/>
+    <addaction name="actionCollapse"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuMode"/>
+   <addaction name="menuAction"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionEdit">
+   <property name="text">
+    <string>Edit</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+E</string>
+   </property>
+  </action>
+  <action name="actionSelect">
+   <property name="text">
+    <string>Select</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+S</string>
+   </property>
+  </action>
+  <action name="actionLocal">
+   <property name="text">
+    <string>Local</string>
+   </property>
+  </action>
+  <action name="actionRemote">
+   <property name="text">
+    <string>Remote</string>
+   </property>
+  </action>
+  <action name="actionScreenshot">
+   <property name="text">
+    <string>Screenshot</string>
+   </property>
+  </action>
+  <action name="actionCollapse">
+   <property name="text">
+    <string>Collapse</string>
+   </property>
+  </action>
+  <action name="TheoreticalLocaleFit">
+   <property name="text">
+    <string>Local</string>
+   </property>
+  </action>
+  <action name="TheoreticalRemoteFit">
+   <property name="text">
+    <string>Remote</string>
+   </property>
+  </action>
+  <action name="ThinLocalFit">
+   <property name="text">
+    <string>Local</string>
+   </property>
+  </action>
+  <action name="ThickLocalFit">
+   <property name="text">
+    <string>Local</string>
+   </property>
+  </action>
+  <action name="actionSave">
+   <property name="text">
+    <string>Save</string>
+   </property>
+  </action>
+  <action name="actionLoad">
+   <property name="text">
+    <string>Load</string>
+   </property>
+  </action>
+  <action name="ThinRemore">
+   <property name="text">
+    <string>Remote</string>
+   </property>
+  </action>
+  <action name="ThickRemote">
+   <property name="text">
+    <string>Remote</string>
+   </property>
+  </action>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QCustomPlot</class>
+   <extends>QWidget</extends>
+   <header>src/qcustomplot.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Code/ui/selectedsourcefieldsselect.ui b/Code/ui/selectedsourcefieldsselect.ui
new file mode 100644
index 0000000000000000000000000000000000000000..bd3b9f5876f5b7dd5abdaf7b1ae3c50d924f4d1f
--- /dev/null
+++ b/Code/ui/selectedsourcefieldsselect.ui
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>selectedSourceFieldsSelect</class>
+<widget class="QWidget" name="selectedSourceFieldsSelect">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>498</width>
+<height>490</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Form</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout_2">
+<item>
+<widget class="QTableWidget" name="fieldListTableWidget">
+<column>
+<property name="text">
+<string>Selected</string>
+</property>
+</column>
+<column>
+<property name="text">
+<string>Column</string>
+</property>
+</column>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<widget class="QPushButton" name="selectAllButton">
+<property name="text">
+<string>Select all</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="deselectAllButton">
+<property name="text">
+<string>Deselect all</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="QPushButton" name="okButton">
+<property name="text">
+<string>Ok</string>
+</property>
+</widget>
+</item>
+</layout>
+</widget>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/selectedsourcesform.ui b/Code/ui/selectedsourcesform.ui
new file mode 100644
index 0000000000000000000000000000000000000000..4171e8b0b9db93bd3b62dde057f231411661ddab
--- /dev/null
+++ b/Code/ui/selectedsourcesform.ui
@@ -0,0 +1,21 @@
+<ui version="4.0">
+ <author/>
+ <comment/>
+ <exportmacro/>
+ <class>SelectedSourcesForm</class>
+ <widget name="SelectedSourcesForm" class="QWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+ </widget>
+ <pixmapfunction/>
+ <connections/>
+</ui>
diff --git a/Code/ui/settingform.ui b/Code/ui/settingform.ui
new file mode 100644
index 0000000000000000000000000000000000000000..9f5198bf53251fab9a14dfbd0811b5d0a5e89f45
--- /dev/null
+++ b/Code/ui/settingform.ui
@@ -0,0 +1,280 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SettingForm</class>
+ <widget class="QWidget" name="SettingForm">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>727</width>
+    <height>501</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QGroupBox" name="IdkGroupBox">
+     <property name="title">
+      <string>IDL/GDL executable</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_2">
+      <item>
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>Path</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="IdlLineEdit"/>
+      </item>
+      <item>
+       <widget class="QPushButton" name="IdlPushButton">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../visivo.qrc">
+          <normaloff>:/icons/FILE_OPEN.bmp</normaloff>:/icons/FILE_OPEN.bmp</iconset>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Tile</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_4">
+        <item>
+         <widget class="QLabel" name="label_2">
+          <property name="text">
+           <string>Path</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="TileLineEdit"/>
+        </item>
+        <item>
+         <widget class="QPushButton" name="TilePushButton">
+          <property name="text">
+           <string/>
+          </property>
+          <property name="icon">
+           <iconset resource="../visivo.qrc">
+            <normaloff>:/icons/FILE_OPEN.bmp</normaloff>:/icons/FILE_OPEN.bmp</iconset>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <widget class="QCheckBox" name="checkBox">
+          <property name="text">
+           <string>Online</string>
+          </property>
+          <property name="checked">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="urlLineEdit">
+          <property name="text">
+           <string>http://visivo.oact.inaf.it/vialacteatiles/openlayers.html</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Glyph</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_5">
+      <item>
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>max</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="glyphLineEdit">
+        <property name="text">
+         <string>1000</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_3">
+     <property name="title">
+      <string>VLKB</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_6">
+        <item>
+         <widget class="QRadioButton" name="publicVLKB_radioButton">
+          <property name="text">
+           <string>Public</string>
+          </property>
+          <property name="checked">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="privateVLKB_radioButton">
+          <property name="text">
+           <string>Private</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="userPassLayout">
+        <item>
+         <widget class="QLabel" name="userLabel">
+          <property name="text">
+           <string>username</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="username_LineEdit"/>
+        </item>
+        <item>
+         <spacer name="horizontalSpacer_2">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QLabel" name="passLabel">
+          <property name="text">
+           <string>password</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="password_LineEdit">
+          <property name="echoMode">
+           <enum>QLineEdit::Password</enum>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox_4">
+     <property name="title">
+      <string>Working directory</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_7">
+      <item>
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Path</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="lineEdit_2">
+        <property name="text">
+         <string>~/VisIVODesktopTemp/tmp_download</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="workdirButton">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../visivo.qrc">
+          <normaloff>:/icons/FILE_OPEN.bmp</normaloff>:/icons/FILE_OPEN.bmp</iconset>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="pushButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="OkPushButton">
+       <property name="text">
+        <string>Ok</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="../visivo.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/Code/ui/vialactea.ui b/Code/ui/vialactea.ui
new file mode 100644
index 0000000000000000000000000000000000000000..7705c1113d676d996d12a80b2e05bcca24fe195e
--- /dev/null
+++ b/Code/ui/vialactea.ui
@@ -0,0 +1,634 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ViaLactea</class>
+ <widget class="QMainWindow" name="ViaLactea">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1266</width>
+    <height>835</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Vialactea - Visual Analytics client</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QHBoxLayout" name="horizontalLayout">
+    <item>
+     <widget class="QSplitter" name="splitter">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>0</height>
+       </size>
+      </property>
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+      <widget class="QWebView" name="webView" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>800</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="url" stdset="0">
+        <url>
+         <string>about:blank</string>
+        </url>
+       </property>
+      </widget>
+      <widget class="QWidget" name="layoutWidget">
+       <layout class="QVBoxLayout" name="verticalLayout">
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_5">
+          <item>
+           <widget class="QGroupBox" name="groupBox_2">
+            <property name="title">
+             <string>Local image</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_3">
+             <item>
+              <widget class="QPushButton" name="openLocalImagePushButton">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/FILE_OPEN.bmp</normaloff>:/icons/FILE_OPEN.bmp</iconset>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="groupBox_3">
+            <property name="title">
+             <string>Local DC</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_6">
+             <item>
+              <widget class="QPushButton" name="localDCPushButton">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/FILE_OPEN.bmp</normaloff>:/icons/FILE_OPEN.bmp</iconset>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="groupBox_4">
+            <property name="title">
+             <string>3D</string>
+            </property>
+            <layout class="QVBoxLayout" name="verticalLayout_5">
+             <item>
+              <widget class="QPushButton" name="select3dPushButton">
+               <property name="text">
+                <string>Select</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="surveySelectorGroupBox">
+          <property name="title">
+           <string>Survey Selector</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_11">
+           <item>
+            <layout class="QGridLayout" name="gridLayout_2">
+             <item row="1" column="2">
+              <widget class="QGroupBox" name="groupBox_5">
+               <property name="title">
+                <string>GLIMPSE/MIPSGAL</string>
+               </property>
+               <layout class="QVBoxLayout" name="verticalLayout_6">
+                <item>
+                 <widget class="QCheckBox" name="glimpse_24_checkBox">
+                  <property name="text">
+                   <string>24 μm</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="glimpse_8_checkBox">
+                  <property name="text">
+                   <string>8.0 μm</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="glimpse_58_checkBox">
+                  <property name="text">
+                   <string>5.8 μm</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="glimpse_45_checkBox">
+                  <property name="text">
+                   <string>4.5 μm</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="glimpse_36checkBox">
+                  <property name="text">
+                   <string>3.6 μm</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </widget>
+             </item>
+             <item row="1" column="0">
+              <widget class="QGroupBox" name="groupBox">
+               <property name="enabled">
+                <bool>true</bool>
+               </property>
+               <property name="title">
+                <string>Hi-GAL</string>
+               </property>
+               <layout class="QVBoxLayout" name="verticalLayout_2">
+                <item>
+                 <widget class="QCheckBox" name="PLW_checkBox_2">
+                  <property name="text">
+                   <string>500 μm</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="PMW_checkBox">
+                  <property name="text">
+                   <string>350 μm </string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="PSW_checkBox_12">
+                  <property name="text">
+                   <string>250 μm </string>
+                  </property>
+                  <property name="checked">
+                   <bool>true</bool>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="blue_checkBox_2">
+                  <property name="text">
+                   <string>70 μm </string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="red_checkBox">
+                  <property name="text">
+                   <string>160 μm </string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </widget>
+             </item>
+             <item row="1" column="3">
+              <widget class="QGroupBox" name="groupBox_6">
+               <property name="title">
+                <string>WISE</string>
+               </property>
+               <layout class="QVBoxLayout" name="verticalLayout_7">
+                <item>
+                 <widget class="QCheckBox" name="wise_22_checkBox">
+                  <property name="text">
+                   <string>22 μm</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="wise_12_checkBox">
+                  <property name="text">
+                   <string>12 μm</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="wise_46_checkBox">
+                  <property name="text">
+                   <string>4.6 μm</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QCheckBox" name="wise_34_checkBox">
+                  <property name="text">
+                   <string>3.4 μm</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <layout class="QGridLayout" name="gridLayout_3">
+             <item row="0" column="0">
+              <widget class="QGroupBox" name="groupBox_7">
+               <property name="title">
+                <string>ATLASGAL</string>
+               </property>
+               <layout class="QVBoxLayout" name="verticalLayout_8">
+                <item>
+                 <widget class="QCheckBox" name="atlasgal_870_checkBox">
+                  <property name="text">
+                   <string>870 μm</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </widget>
+             </item>
+             <item row="0" column="1">
+              <widget class="QGroupBox" name="groupBox_8">
+               <property name="title">
+                <string>BOLOCAM GPS</string>
+               </property>
+               <layout class="QVBoxLayout" name="verticalLayout_9">
+                <item>
+                 <widget class="QCheckBox" name="bolocam_11_checkBox">
+                  <property name="text">
+                   <string>1.1 mm</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </widget>
+             </item>
+             <item row="0" column="2">
+              <widget class="QGroupBox" name="groupBox_9">
+               <property name="title">
+                <string>CORNISH</string>
+               </property>
+               <layout class="QVBoxLayout" name="verticalLayout_10">
+                <item>
+                 <widget class="QCheckBox" name="cornish_6_checkBox">
+                  <property name="text">
+                   <string>5 GHz</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </widget>
+             </item>
+            </layout>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="selectionGroupBox">
+          <property name="title">
+           <string>Selection</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_3">
+           <item>
+            <widget class="QRadioButton" name="noneRadioButton">
+             <property name="text">
+              <string>None</string>
+             </property>
+             <property name="checked">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QRadioButton" name="pointRadioButton">
+             <property name="text">
+              <string>Point</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QRadioButton" name="rectRadioButton">
+             <property name="text">
+              <string>Rectangular</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="coordinateGroupBox">
+          <property name="title">
+           <string>Coordinates (center of selection)</string>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_4">
+           <item>
+            <widget class="QLabel" name="label">
+             <property name="text">
+              <string>glon</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="glonLineEdit">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>30</width>
+               <height>0</height>
+              </size>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLabel" name="label_3">
+             <property name="text">
+              <string>glat</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QLineEdit" name="glatLineEdit">
+             <property name="sizePolicy">
+              <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+               <horstretch>0</horstretch>
+               <verstretch>0</verstretch>
+              </sizepolicy>
+             </property>
+             <property name="minimumSize">
+              <size>
+               <width>30</width>
+               <height>0</height>
+              </size>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <layout class="QGridLayout" name="gridLayout">
+             <item row="0" column="1">
+              <widget class="QLabel" name="dlLabel">
+               <property name="text">
+                <string>dl</string>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="0">
+              <widget class="QLabel" name="label_2">
+               <property name="text">
+                <string>radius</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="0">
+              <widget class="QLineEdit" name="radiumLineEdit">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="minimumSize">
+                <size>
+                 <width>10</width>
+                 <height>0</height>
+                </size>
+               </property>
+               <property name="maximumSize">
+                <size>
+                 <width>300</width>
+                 <height>16777215</height>
+                </size>
+               </property>
+              </widget>
+             </item>
+             <item row="0" column="2">
+              <widget class="QLabel" name="dbLabel">
+               <property name="text">
+                <string>db</string>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="1">
+              <widget class="QLineEdit" name="dlLineEdit">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="minimumSize">
+                <size>
+                 <width>10</width>
+                 <height>0</height>
+                </size>
+               </property>
+               <property name="maximumSize">
+                <size>
+                 <width>300</width>
+                 <height>16777215</height>
+                </size>
+               </property>
+              </widget>
+             </item>
+             <item row="1" column="2">
+              <widget class="QLineEdit" name="dbLineEdit">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="minimumSize">
+                <size>
+                 <width>10</width>
+                 <height>0</height>
+                </size>
+               </property>
+               <property name="maximumSize">
+                <size>
+                 <width>300</width>
+                 <height>16777215</height>
+                </size>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QCheckBox" name="saveToDiskCheckBox">
+          <property name="enabled">
+           <bool>true</bool>
+          </property>
+          <property name="text">
+           <string>Save to disk</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_2">
+          <item>
+           <widget class="QLineEdit" name="fileNameLineEdit">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="minimumSize">
+             <size>
+              <width>30</width>
+              <height>0</height>
+             </size>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="selectFsPushButton">
+            <property name="enabled">
+             <bool>false</bool>
+            </property>
+            <property name="text">
+             <string/>
+            </property>
+            <property name="icon">
+             <iconset resource="../visivo.qrc">
+              <normaloff>:/icons/FILE_OPEN.bmp</normaloff>:/icons/FILE_OPEN.bmp</iconset>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QPushButton" name="queryPushButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="text">
+           <string>Query</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1266</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <property name="nativeMenuBar">
+    <bool>false</bool>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionSettings"/>
+    <addaction name="actionExit"/>
+    <addaction name="actionLoad_SED_2"/>
+   </widget>
+   <widget class="QMenu" name="menuAiuto">
+    <property name="title">
+     <string>Help</string>
+    </property>
+    <addaction name="actionAbout"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuAiuto"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionSettings">
+   <property name="text">
+    <string>Settings</string>
+   </property>
+  </action>
+  <action name="actionExit">
+   <property name="text">
+    <string>Exit</string>
+   </property>
+  </action>
+  <action name="actionAbout">
+   <property name="text">
+    <string>About</string>
+   </property>
+  </action>
+  <action name="actionLoad_SED">
+   <property name="text">
+    <string>Load SED</string>
+   </property>
+  </action>
+  <action name="actionLoad_SED_2">
+   <property name="text">
+    <string>Load SED</string>
+   </property>
+  </action>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QWebView</class>
+   <extends>QWidget</extends>
+   <header>QtWebKitWidgets/QWebView</header>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="../visivo.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/Code/ui/vialactea_fileload.ui b/Code/ui/vialactea_fileload.ui
new file mode 100644
index 0000000000000000000000000000000000000000..3bfdb9d8a9cf5b1f4e1809ecf22e4ada225c8920
--- /dev/null
+++ b/Code/ui/vialactea_fileload.ui
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>Vialactea_FileLoad</class>
+<widget class="QWidget" name="Vialactea_FileLoad">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>400</width>
+<height>300</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Form</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout_2">
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<widget class="QLabel" name="label">
+<property name="text">
+<string>Filename:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="filenameLabel">
+<property name="text">
+<string>TextLabel</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+<item>
+<widget class="QGroupBox" name="groupBox">
+<property name="title">
+<string>Type:</string>
+</property>
+<layout class="QHBoxLayout" name="horizontalLayout_2">
+<item>
+<widget class="QRadioButton" name="catalogueRadioButton">
+<property name="text">
+<string>Catalogue</string>
+</property>
+<property name="checked">
+<bool>true</bool>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QRadioButton" name="bandmergedradioButton">
+<property name="text">
+<string>Band-merged</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QRadioButton" name="mapRadioButton">
+<property name="text">
+<string>Map</string>
+</property>
+</widget>
+</item>
+</layout>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="wavelenLayout">
+<item>
+<widget class="QLabel" name="waveLabel">
+<property name="text">
+<string>Wavelength: </string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="waveLineEdit"/>
+</item>
+<item>
+<spacer name="horizontalSpacer_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+<item>
+<spacer name="verticalSpacer">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>20</width>
+<height>40</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QPushButton" name="okPushButton">
+<property name="focusPolicy">
+<enum>Qt::ClickFocus</enum>
+</property>
+<property name="text">
+<string>Ok</string>
+</property>
+</widget>
+</item>
+</layout>
+</widget>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/vialacteainitialquery.ui b/Code/ui/vialacteainitialquery.ui
new file mode 100644
index 0000000000000000000000000000000000000000..30ffe529a3e7aea2e6f312ec62670a1024ea91d4
--- /dev/null
+++ b/Code/ui/vialacteainitialquery.ui
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>VialacteaInitialQuery</class>
+ <widget class="QWidget" name="VialacteaInitialQuery">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>652</width>
+    <height>312</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QGroupBox" name="selectioTypeGroupBox">
+       <property name="title">
+        <string>Selection type</string>
+       </property>
+       <layout class="QHBoxLayout" name="horizontalLayout_5">
+        <item>
+         <widget class="QRadioButton" name="pointRadioButton">
+          <property name="text">
+           <string>Point</string>
+          </property>
+          <property name="checked">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="rectRadioButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="text">
+           <string>Rectangular</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="pointGroupBox">
+     <property name="title">
+      <string>Point selection</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_6">
+      <item>
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>glon</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="l_lineEdit">
+        <property name="text">
+         <string>0</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>glat</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="b_lineEdit">
+        <property name="text">
+         <string>0</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>r [deg]</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="r_lineEdit">
+        <property name="text">
+         <string>0.1</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="rectGroupBox">
+     <property name="title">
+      <string>Rectangular  selection</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_7">
+      <item>
+       <layout class="QVBoxLayout" name="boxQueryLayout">
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_3">
+          <item>
+           <widget class="QLabel" name="label_7">
+            <property name="text">
+             <string>dl</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLineEdit" name="dlLineEdit"/>
+          </item>
+          <item>
+           <widget class="QLabel" name="label_6">
+            <property name="text">
+             <string>db</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLineEdit" name="dbLineEdit"/>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_2"/>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QPushButton" name="pushButton">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="queryPushButton">
+       <property name="text">
+        <string>Query</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Code/ui/viewselectedsourcedataset.ui b/Code/ui/viewselectedsourcedataset.ui
new file mode 100644
index 0000000000000000000000000000000000000000..8bd937e6f78d18e67fdbb03b8fae233c1496771f
--- /dev/null
+++ b/Code/ui/viewselectedsourcedataset.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>ViewSelectedSourceDataset</class>
+<widget class="QWidget" name="ViewSelectedSourceDataset">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>811</width>
+<height>246</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Form</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout_2">
+<item>
+<widget class="QTableWidget" name="datasetTableWidget"/>
+</item>
+</layout>
+</widget>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/vlkbquery.ui b/Code/ui/vlkbquery.ui
new file mode 100644
index 0000000000000000000000000000000000000000..536fecb0b9451dcc999afd3c0feeabf8578bfb61
--- /dev/null
+++ b/Code/ui/vlkbquery.ui
@@ -0,0 +1,21 @@
+<ui version="4.0">
+<author/>
+<comment/>
+<exportmacro/>
+<class>VLKBQuery</class>
+<widget class="QWidget" name="VLKBQuery">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>400</width>
+<height>300</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Form</string>
+</property>
+</widget>
+<pixmapfunction/>
+<connections/>
+</ui>
diff --git a/Code/ui/vlkbquerycomposer.ui b/Code/ui/vlkbquerycomposer.ui
new file mode 100644
index 0000000000000000000000000000000000000000..4732d43153b50be3d4659ec56fe9a3f27b263e63
--- /dev/null
+++ b/Code/ui/vlkbquerycomposer.ui
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>VLKBQueryComposer</class>
+<widget class="QWidget" name="VLKBQueryComposer">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>733</width>
+<height>461</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>Form</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout_2">
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<widget class="QLabel" name="label">
+<property name="text">
+<string>TAP URL:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="tapUrlLineEdit">
+<property name="text">
+<string>http://palantir19.oats.inaf.it:8080/vlkb/</string>
+</property>
+<property name="alignment">
+<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="connectButton">
+<property name="text">
+<string>Connect</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_4">
+<item>
+<widget class="QLabel" name="label_2">
+<property name="text">
+<string>Table:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QComboBox" name="tableListComboBox">
+<property name="enabled">
+<bool>false</bool>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="QTableWidget" name="columnTableWidget">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<column>
+<property name="text">
+<string>Name</string>
+</property>
+</column>
+<column>
+<property name="text">
+<string>DataType</string>
+</property>
+</column>
+</widget>
+</item>
+<item>
+<spacer name="verticalSpacer">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>20</width>
+<height>40</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_3">
+<item>
+<widget class="QCheckBox" name="syncCheckBox">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<property name="text">
+<string>Synchronous</string>
+</property>
+<property name="checked">
+<bool>true</bool>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_3">
+<property name="text">
+<string>Query Languages</string>
+</property>
+<property name="alignment">
+<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QComboBox" name="queryLangComboBox">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<item>
+<property name="text">
+<string>ADQL</string>
+</property>
+</item>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="QTextEdit" name="queryTextEdit">
+<property name="enabled">
+<bool>false</bool>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_2">
+<item>
+<spacer name="horizontalSpacer">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QPushButton" name="okButton">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<property name="text">
+<string>Ok</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</item>
+</layout>
+</widget>
+<resources/>
+<connections/>
+</ui>
diff --git a/Code/ui/vlkbsimplequerycomposer.ui b/Code/ui/vlkbsimplequerycomposer.ui
new file mode 100644
index 0000000000000000000000000000000000000000..22f9902ef5f2178f655a8231766aeae1c423a3ef
--- /dev/null
+++ b/Code/ui/vlkbsimplequerycomposer.ui
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>VLKBSimpleQueryComposer</class>
+ <widget class="QWidget" name="VLKBSimpleQueryComposer">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>485</width>
+    <height>395</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Add compact sources to visualization</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>VLKB URL:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="vlkbUrlLineEdit">
+       <property name="text">
+        <string>http://ia2-vialactea.oats.inaf.it:8080/vlkb/</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="connectPushButton">
+       <property name="text">
+        <string>Connect</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="tableGroupBox">
+     <property name="title">
+      <string>Table selection:</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout_6">
+      <item>
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Table</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QComboBox" name="tableComboBox">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="tableListGroupBox">
+     <property name="title">
+      <string>Coordinates</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Longitude</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <widget class="QLabel" name="label_4">
+          <property name="text">
+           <string>min:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="longMinLineEdit">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="label_3">
+          <property name="text">
+           <string>max:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="longMaxLineEdit">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>Latitude</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_4">
+        <item>
+         <widget class="QLabel" name="label_8">
+          <property name="text">
+           <string>min:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="latMinLineEdit">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="label_7">
+          <property name="text">
+           <string>max:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="latMaxLineEdit">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="savedatasetCheckBox">
+     <property name="text">
+      <string>Save dataset to disk</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_5">
+     <item>
+      <widget class="QLabel" name="outputNameLabel">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>Output name prefix:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="outputNameLineEdit">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="navigateFSButtono">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="icon">
+        <iconset resource="../visivo.qrc">
+         <normaloff>:/icons/FILE_OPEN.bmp</normaloff>:/icons/FILE_OPEN.bmp</iconset>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QPushButton" name="queryPushButton">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
+     <property name="text">
+      <string>Query</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="../visivo.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/Code/ui/vtkfitstoolswidget.ui b/Code/ui/vtkfitstoolswidget.ui
new file mode 100644
index 0000000000000000000000000000000000000000..4107833ac80744a2939a8ddffe4132f120f4b1fe
--- /dev/null
+++ b/Code/ui/vtkfitstoolswidget.ui
@@ -0,0 +1,365 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>vtkfitstoolswidget</class>
+ <widget class="QWidget" name="vtkfitstoolswidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>564</width>
+    <height>713</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="selectButton">
+       <property name="text">
+        <string>start select</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="noSelectButton">
+       <property name="text">
+        <string>stop select</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Sources</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QPushButton" name="addLocalSourcesPushButton">
+       <property name="text">
+        <string>Add Local </string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="addRemoteSourcesPushButton">
+       <property name="text">
+        <string>Add Remote</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Datacube</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_6">
+     <item>
+      <widget class="QPushButton" name="datacubeButton">
+       <property name="text">
+        <string>Add Remote</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QTableWidget" name="addedSourcesListWidget">
+     <column>
+      <property name="text">
+       <string>Selected</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Name</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Opacity</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <widget class="Line" name="line_2">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Palette</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="lutComboBox">
+       <property name="currentIndex">
+        <number>3</number>
+       </property>
+       <item>
+        <property name="text">
+         <string>Default</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Default Step</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>EField</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Glow</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Gray</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>MinMax</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>PhysicsContour</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>PureRed</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>PureGreen</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>PureBlue</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Run1</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Run2</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Sar</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Temperature</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>TenStep</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>VolRenGlow</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>VolRenGreen</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>VolRenRGB</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>VolRenTwoLev</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>AllYellow</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>AllCyan</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>AllViolet</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>AllWhite</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>AllBlack</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>AllRed</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>AllGreen</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>AllBlu</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_5">
+       <property name="text">
+        <string>Scale</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="scaleComboBox">
+       <property name="currentText">
+        <string>Linear</string>
+       </property>
+       <property name="currentIndex">
+        <number>0</number>
+       </property>
+       <item>
+        <property name="text">
+         <string>Linear</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Log</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="Line" name="line_3">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Wcs</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QGroupBox" name="groupBox">
+       <property name="title">
+        <string/>
+       </property>
+       <layout class="QHBoxLayout" name="horizontalLayout_5">
+        <item>
+         <widget class="QRadioButton" name="galacticRadioButton">
+          <property name="text">
+           <string>Galactic</string>
+          </property>
+          <property name="checked">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QRadioButton" name="eclipticRadioButton">
+          <property name="text">
+           <string>Ecliptic</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Code/ui/vtkfitstoolwidget_new.ui b/Code/ui/vtkfitstoolwidget_new.ui
new file mode 100644
index 0000000000000000000000000000000000000000..bad1568a411a205a8b3d9798751b12dce7d9951f
--- /dev/null
+++ b/Code/ui/vtkfitstoolwidget_new.ui
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>vtkfitstoolwidget_new</class>
+ <widget class="QMainWindow" name="vtkfitstoolwidget_new">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QVBoxLayout" name="verticalLayout">
+    <item>
+     <widget class="QToolBox" name="toolBox">
+      <property name="currentIndex">
+       <number>0</number>
+      </property>
+      <widget class="QWidget" name="page">
+       <property name="geometry">
+        <rect>
+         <x>0</x>
+         <y>0</y>
+         <width>776</width>
+         <height>499</height>
+        </rect>
+       </property>
+       <attribute name="label">
+        <string>Layers</string>
+       </attribute>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QTreeWidget" name="layerTreeWidget">
+          <property name="headerHidden">
+           <bool>true</bool>
+          </property>
+          <column>
+           <property name="text">
+            <string notr="true">color</string>
+           </property>
+          </column>
+          <column>
+           <property name="text">
+            <string>name</string>
+           </property>
+          </column>
+         </widget>
+        </item>
+        <item>
+         <widget class="QWidget" name="widget" native="true">
+          <layout class="QVBoxLayout" name="verticalLayout_2">
+           <item>
+            <widget class="QGroupBox" name="infoGroupBox">
+             <property name="title">
+              <string>Layer info</string>
+             </property>
+             <layout class="QVBoxLayout" name="verticalLayout_3">
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_4">
+                <item>
+                 <widget class="QLabel" name="label">
+                  <property name="text">
+                   <string>Name:</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLineEdit" name="nameLineEdit"/>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_2">
+                <item>
+                 <widget class="QLabel" name="label_2">
+                  <property name="text">
+                   <string>Wavelength:</string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QLineEdit" name="wavelenghtLineEdit"/>
+                </item>
+               </layout>
+              </item>
+             </layout>
+            </widget>
+           </item>
+           <item>
+            <widget class="QGroupBox" name="regulationGroupBox">
+             <property name="title">
+              <string>Regulation</string>
+             </property>
+             <layout class="QVBoxLayout" name="verticalLayout_4">
+              <item>
+               <widget class="QCheckBox" name="checkBox">
+                <property name="text">
+                 <string>Visible</string>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <layout class="QHBoxLayout" name="horizontalLayout_6">
+                <item>
+                 <widget class="QLabel" name="label_3">
+                  <property name="text">
+                   <string>Transparency: </string>
+                  </property>
+                 </widget>
+                </item>
+                <item>
+                 <widget class="QSlider" name="horizontalSlider">
+                  <property name="orientation">
+                   <enum>Qt::Horizontal</enum>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item>
+               <widget class="QPushButton" name="pushButton_4">
+                <property name="text">
+                 <string>Change color</string>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+           </item>
+           <item>
+            <spacer name="verticalSpacer">
+             <property name="orientation">
+              <enum>Qt::Vertical</enum>
+             </property>
+             <property name="sizeHint" stdset="0">
+              <size>
+               <width>20</width>
+               <height>40</height>
+              </size>
+             </property>
+            </spacer>
+           </item>
+           <item>
+            <layout class="QHBoxLayout" name="horizontalLayout_3">
+             <item>
+              <widget class="QPushButton" name="pushButton_2">
+               <property name="text">
+                <string>Cancel</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="savePushButton">
+               <property name="text">
+                <string>Save</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </item>
+           <item>
+            <widget class="QPushButton" name="deletePushButton">
+             <property name="autoFillBackground">
+              <bool>false</bool>
+             </property>
+             <property name="text">
+              <string>Delete Layer</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>22</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Code/ui/vtktoolswidget.ui b/Code/ui/vtktoolswidget.ui
new file mode 100644
index 0000000000000000000000000000000000000000..0ea8d346c6cd2c99dde4ee7674a0e936999ce1b1
--- /dev/null
+++ b/Code/ui/vtktoolswidget.ui
@@ -0,0 +1,507 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+<class>vtktoolswidget</class>
+<widget class="QWidget" name="vtktoolswidget">
+<property name="geometry">
+<rect>
+<x>0</x>
+<y>0</y>
+<width>496</width>
+<height>628</height>
+</rect>
+</property>
+<property name="windowTitle">
+<string>View Setting</string>
+</property>
+<layout class="QVBoxLayout" name="verticalLayout_2">
+<item>
+<widget class="QLabel" name="label_3">
+<property name="text">
+<string>Camera</string>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout">
+<item>
+<widget class="QPushButton" name="cameraLeft">
+<property name="text">
+<string/>
+</property>
+<property name="icon">
+<iconset resource="../visivo.qrc">
+<normaloff>:/icons/PIC_LEFT.bmp</normaloff>:/icons/PIC_LEFT.bmp</iconset>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="cameraBack">
+<property name="text">
+<string/>
+</property>
+<property name="icon">
+<iconset resource="../visivo.qrc">
+<normaloff>:/icons/PIC_BACK.bmp</normaloff>:/icons/PIC_BACK.bmp</iconset>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="cameraRight">
+<property name="text">
+<string/>
+</property>
+<property name="icon">
+<iconset resource="../visivo.qrc">
+<normaloff>:/icons/PIC_RIGHT.bmp</normaloff>:/icons/PIC_RIGHT.bmp</iconset>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="frontCamera">
+<property name="text">
+<string/>
+</property>
+<property name="icon">
+<iconset resource="../visivo.qrc">
+<normaloff>:/icons/PIC_FRONT.bmp</normaloff>:/icons/PIC_FRONT.bmp</iconset>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="topCamera">
+<property name="text">
+<string/>
+</property>
+<property name="icon">
+<iconset resource="../visivo.qrc">
+<normaloff>:/icons/PIC_TOP.bmp</normaloff>:/icons/PIC_TOP.bmp</iconset>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QPushButton" name="bottomCamera">
+<property name="text">
+<string/>
+</property>
+<property name="icon">
+<iconset resource="../visivo.qrc">
+<normaloff>:/icons/PIC_BOTTOM.bmp</normaloff>:/icons/PIC_BOTTOM.bmp</iconset>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_2">
+<item>
+<widget class="QCheckBox" name="ShowBoxCheckBox">
+<property name="text">
+<string>Show Box</string>
+</property>
+<property name="checked">
+<bool>true</bool>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QCheckBox" name="ShowAxesCheckBox">
+<property name="text">
+<string>Show Axes</string>
+</property>
+<property name="checked">
+<bool>true</bool>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QCheckBox" name="ShowColorbarCheckBox">
+<property name="text">
+<string>Show Colorbar</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_2">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_6">
+<item>
+<widget class="QCheckBox" name="scaleCheckBox">
+<property name="text">
+<string>Scale</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QCheckBox" name="gridCheckBox">
+<property name="text">
+<string>Grid</string>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_4">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLabel" name="label_5">
+<property name="text">
+<string>Look Up Table</string>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_3">
+<item>
+<widget class="QCheckBox" name="activateLutCheckBox">
+<property name="text">
+<string>Activate LUT</string>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_4">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QLabel" name="scalarLabel">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<property name="text">
+<string>Scalar</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QComboBox" name="scalarComboBox">
+<property name="enabled">
+<bool>false</bool>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="horizontalSpacer_3">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>40</width>
+<height>20</height>
+</size>
+</property>
+</spacer>
+</item>
+<item>
+<widget class="QLabel" name="lutLabel">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<property name="text">
+<string>LUT</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QComboBox" name="lutComboBox">
+<property name="enabled">
+<bool>false</bool>
+</property>
+<item>
+<property name="text">
+<string>Default</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>Default Step</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>EField</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>Glow</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>Gray</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>MinMax</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>PhysicsContour</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>PureRed</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>PureGreen</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>Blue</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>Run1</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>Run2</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>Sar</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>Temperature</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>TenStep</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>VolRenGlow</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>VolRenGreen</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>VolRenRGB</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>VolRenTwoLev</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>AllYellow</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>AllCyan</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>AllViolet</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>AllWhite</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>AllBlack</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>AllRed</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>AllGreen</string>
+</property>
+</item>
+<item>
+<property name="text">
+<string>AllBlue</string>
+</property>
+</item>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<layout class="QVBoxLayout" name="verticalLayout">
+<item>
+<widget class="QCustomPlot" name="histogramWidget" native="true">
+<property name="sizePolicy">
+<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+<horstretch>0</horstretch>
+<verstretch>0</verstretch>
+</sizepolicy>
+</property>
+</widget>
+</item>
+</layout>
+</item>
+<item>
+<widget class="QLabel" name="label_4">
+<property name="text">
+<string>Range</string>
+</property>
+</widget>
+</item>
+<item>
+<layout class="QVBoxLayout" name="verticalLayout_3">
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_4">
+<item>
+<widget class="QLabel" name="label">
+<property name="text">
+<string>Min:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QSlider" name="fromSlider">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="fromValue"/>
+</item>
+</layout>
+</item>
+<item>
+<layout class="QHBoxLayout" name="horizontalLayout_5">
+<item>
+<widget class="QLabel" name="label_2">
+<property name="text">
+<string>Max:</string>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QSlider" name="toSlider">
+<property name="sliderPosition">
+<number>99</number>
+</property>
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<widget class="QLineEdit" name="toValue"/>
+</item>
+</layout>
+</item>
+</layout>
+</item>
+<item>
+<widget class="Line" name="line_3">
+<property name="orientation">
+<enum>Qt::Horizontal</enum>
+</property>
+</widget>
+</item>
+<item>
+<spacer name="verticalSpacer">
+<property name="orientation">
+<enum>Qt::Vertical</enum>
+</property>
+<property name="sizeHint" stdset="0">
+<size>
+<width>20</width>
+<height>40</height>
+</size>
+</property>
+</spacer>
+</item>
+</layout>
+</widget>
+<customwidgets>
+<customwidget>
+<class>QCustomPlot</class>
+<extends>QWidget</extends>
+<header>src/qcustomplot.h</header>
+<container>1</container>
+</customwidget>
+</customwidgets>
+<resources>
+<include location="../visivo.qrc"/>
+</resources>
+<connections/>
+</ui>
diff --git a/Code/ui/vtkwindow.ui b/Code/ui/vtkwindow.ui
new file mode 100644
index 0000000000000000000000000000000000000000..0f3b89de0a4b87e022028737ad82a755931ae8db
--- /dev/null
+++ b/Code/ui/vtkwindow.ui
@@ -0,0 +1,338 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>vtkwindow</class>
+ <widget class="QMainWindow" name="vtkwindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1893</width>
+    <height>767</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="win">
+   <layout class="QVBoxLayout" name="verticalLayout_2">
+    <item>
+     <layout class="QHBoxLayout" name="horizontalLayout">
+      <item>
+       <spacer name="horizontalSpacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QRadioButton" name="PVPlot_radioButton">
+        <property name="text">
+         <string>PV plot</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="PVPlotPushButton">
+        <property name="text">
+         <string>PV plot</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="generatePushButton">
+        <property name="text">
+         <string>Gen</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="cameraLeft">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../visivo.qrc">
+          <normaloff>:/icons/PIC_LEFT.bmp</normaloff>:/icons/PIC_LEFT.bmp</iconset>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_survey">
+        <property name="text">
+         <string>Survey</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="lineEdit_survey"/>
+      </item>
+      <item>
+       <widget class="QPushButton" name="cameraBack">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../visivo.qrc">
+          <normaloff>:/icons/PIC_BACK.bmp</normaloff>:/icons/PIC_BACK.bmp</iconset>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_species">
+        <property name="text">
+         <string>Species</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="lineEdit_species"/>
+      </item>
+      <item>
+       <widget class="QPushButton" name="cameraRight">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../visivo.qrc">
+          <normaloff>:/icons/PIC_RIGHT.bmp</normaloff>:/icons/PIC_RIGHT.bmp</iconset>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="frontCamera">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../visivo.qrc">
+          <normaloff>:/icons/PIC_FRONT.bmp</normaloff>:/icons/PIC_FRONT.bmp</iconset>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_transition">
+        <property name="text">
+         <string>Transition</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLineEdit" name="lineEdit_transition"/>
+      </item>
+      <item>
+       <widget class="QPushButton" name="topCamera">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../visivo.qrc">
+          <normaloff>:/icons/PIC_TOP.bmp</normaloff>:/icons/PIC_TOP.bmp</iconset>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="contour_pushButton">
+        <property name="text">
+         <string>Contour</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="bottomCamera">
+        <property name="text">
+         <string/>
+        </property>
+        <property name="icon">
+         <iconset resource="../visivo.qrc">
+          <normaloff>:/icons/PIC_BOTTOM.bmp</normaloff>:/icons/PIC_BOTTOM.bmp</iconset>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_threshold">
+        <property name="text">
+         <string>Threshold</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QSlider" name="horizontalSlider_threshold">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="tickPosition">
+         <enum>QSlider::TicksAbove</enum>
+        </property>
+        <property name="tickInterval">
+         <number>1</number>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>Cutting plane</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QSlider" name="cuttingPlane_Slider">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="tickPosition">
+         <enum>QSlider::TicksAbove</enum>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QSpinBox" name="spinBox_cuttingPlane"/>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_channel">
+        <property name="text">
+         <string>Channel</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QSpinBox" name="spinBox_contour"/>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_channels">
+        <property name="text">
+         <string>Range</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QSpinBox" name="spinBox_channels"/>
+      </item>
+      <item>
+       <widget class="QLabel" name="selectionLabel">
+        <property name="text">
+         <string>Selection:</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="rectSelection">
+        <property name="text">
+         <string>Rect</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="freehandPushButton">
+        <property name="text">
+         <string>Free</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer_2">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QPushButton" name="resetPushButton">
+        <property name="text">
+         <string>Reset</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="confirmPushButton">
+        <property name="text">
+         <string>Confirm</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </item>
+    <item>
+     <widget class="QVTKWidget" name="qVTK1" native="true">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="mouseTracking">
+       <bool>false</bool>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1893</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuWindow">
+    <property name="title">
+     <string>Window</string>
+    </property>
+    <addaction name="actionTools"/>
+    <addaction name="actionInfo"/>
+   </widget>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuWindow"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionTools">
+   <property name="text">
+    <string>Tools</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+T</string>
+   </property>
+  </action>
+  <action name="actionClose">
+   <property name="text">
+    <string>Close</string>
+   </property>
+  </action>
+  <action name="actionInfo">
+   <property name="text">
+    <string>Info</string>
+   </property>
+  </action>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QVTKWidget</class>
+   <extends>QWidget</extends>
+   <header>QVTKWidget.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="../visivo.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/Code/ui/vtkwindow_new.ui b/Code/ui/vtkwindow_new.ui
new file mode 100644
index 0000000000000000000000000000000000000000..cf7709a8127e852c42c70a8e3846fd31b6e11fd2
--- /dev/null
+++ b/Code/ui/vtkwindow_new.ui
@@ -0,0 +1,1542 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>vtkwindow_new</class>
+ <widget class="QMainWindow" name="vtkwindow_new">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>3990</width>
+    <height>625</height>
+   </rect>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>11</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QHBoxLayout" name="horizontalLayout_6">
+    <item>
+     <widget class="QSplitter" name="splitter_2">
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+      <widget class="QWidget" name="layoutWidget">
+       <layout class="QVBoxLayout" name="verticalLayout_6">
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <item>
+           <widget class="QGroupBox" name="compactSourcesGroupBox">
+            <property name="title">
+             <string>Compact souces</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_2">
+             <item>
+              <widget class="QPushButton" name="rectangularSelectionCS">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/rect-select.png</normaloff>:/icons/rect-select.png</iconset>
+               </property>
+               <property name="iconSize">
+                <size>
+                 <width>50</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="datacubeGroupBox">
+            <property name="title">
+             <string>Datacubes</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_3">
+             <item>
+              <widget class="QPushButton" name="pushButton_2">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/rect-select.png</normaloff>:/icons/rect-select.png</iconset>
+               </property>
+               <property name="iconSize">
+                <size>
+                 <width>50</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="pushButton_3">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/ellipse-select.png</normaloff>:/icons/ellipse-select.png</iconset>
+               </property>
+               <property name="iconSize">
+                <size>
+                 <width>50</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="filamentsGroupBox">
+            <property name="title">
+             <string>Filaments</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_8">
+             <item>
+              <widget class="QPushButton" name="fil_rectPushButton">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/rect-select.png</normaloff>:/icons/rect-select.png</iconset>
+               </property>
+               <property name="iconSize">
+                <size>
+                 <width>50</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="bubbleGroupBox">
+            <property name="title">
+             <string>Bubble</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_4">
+             <item>
+              <widget class="QPushButton" name="bubblePushButton">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/rect-select.png</normaloff>:/icons/rect-select.png</iconset>
+               </property>
+               <property name="iconSize">
+                <size>
+                 <width>50</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="tdGroupBox">
+            <property name="title">
+             <string>3D</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_9">
+             <item>
+              <widget class="QPushButton" name="tdRectPushButton">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/rect-select.png</normaloff>:/icons/rect-select.png</iconset>
+               </property>
+               <property name="iconSize">
+                <size>
+                 <width>50</width>
+                 <height>20</height>
+                </size>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="toolsGroupBox">
+            <property name="title">
+             <string>Layer setting</string>
+            </property>
+            <layout class="QVBoxLayout" name="verticalLayout_12">
+             <item>
+              <layout class="QHBoxLayout" name="LutLayout">
+               <item>
+                <widget class="QComboBox" name="lutComboBox">
+                 <item>
+                  <property name="text">
+                   <string>Gray</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>Default</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>Default Step</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>EField</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>Glow</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>MinMax</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>PhysicsContour</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>PureRed</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>PureGreen</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>PureBlue</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>Run1</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>Run2</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>Sar</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>Temperature</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>TenStep</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>VolRenGlow</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>VolRenGreen</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>VolRenRGB</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>VolRenTwoLev</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>AllYellow</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>AllCyan</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>AllViolet</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>AllWhite</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>AllBlack</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>AllRed</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>AllGreen</string>
+                  </property>
+                 </item>
+                 <item>
+                  <property name="text">
+                   <string>AllBlu</string>
+                  </property>
+                 </item>
+                </widget>
+               </item>
+               <item>
+                <widget class="QRadioButton" name="linearadioButton">
+                 <property name="text">
+                  <string>Linear</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="QRadioButton" name="logRadioButton">
+                 <property name="text">
+                  <string>Log</string>
+                 </property>
+                 <property name="checked">
+                  <bool>true</bool>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </item>
+             <item>
+              <layout class="QHBoxLayout" name="horizontalLayout_19">
+               <item>
+                <widget class="QSlider" name="horizontalSlider">
+                 <property name="value">
+                  <number>99</number>
+                 </property>
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="lut3dGroupBox">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>400</width>
+              <height>16777215</height>
+             </size>
+            </property>
+            <property name="title">
+             <string>Lut</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_16">
+             <item>
+              <widget class="QComboBox" name="scalarComboBox">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QComboBox" name="lut3dComboBox">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <item>
+                <property name="text">
+                 <string>Gray</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Default</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Default Step</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>EField</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Glow</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>MinMax</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>PhysicsContour</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>PureRed</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>PureGreen</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>PureBlue</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Run1</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Run2</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Sar</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Temperature</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>TenStep</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>VolRenGlow</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>VolRenGreen</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>VolRenRGB</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>VolRenTwoLev</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>AllYellow</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>AllCyan</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>AllViolet</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>AllWhite</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>AllBlack</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>AllRed</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>AllGreen</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>AllBlu</string>
+                </property>
+               </item>
+              </widget>
+             </item>
+             <item>
+              <widget class="QRadioButton" name="linear3dRadioButton">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="text">
+                <string>Linear</string>
+               </property>
+               <property name="checked">
+                <bool>true</bool>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QRadioButton" name="log3dRadioButton">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="text">
+                <string>Log</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QToolButton" name="toolButton">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="text">
+                <string>...</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QCheckBox" name="lut3dActivateCheckBox">
+               <property name="text">
+                <string/>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="glyphGroupBox">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>400</width>
+              <height>16777215</height>
+             </size>
+            </property>
+            <property name="title">
+             <string>Glyph</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_18">
+             <item>
+              <widget class="QCheckBox" name="glyphActivateCheckBox">
+               <property name="enabled">
+                <bool>true</bool>
+               </property>
+               <property name="text">
+                <string/>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QComboBox" name="glyphShapeComboBox">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <item>
+                <property name="text">
+                 <string>Sphere</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Cone</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Cylinder</string>
+                </property>
+               </item>
+               <item>
+                <property name="text">
+                 <string>Cube</string>
+                </property>
+               </item>
+              </widget>
+             </item>
+             <item>
+              <widget class="QComboBox" name="glyphScalarComboBox">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QLabel" name="label_9">
+               <property name="text">
+                <string>Scaling Factor:</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QLineEdit" name="glyphScalingLineEdit">
+               <property name="enabled">
+                <bool>false</bool>
+               </property>
+               <property name="toolTip">
+                <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Glyph Scaling Factor&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+               </property>
+               <property name="text">
+                <string>0.5</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="filterGroupBox">
+            <property name="title">
+             <string>Filter</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_20">
+             <item>
+              <widget class="QToolButton" name="filterMoreButton">
+               <property name="enabled">
+                <bool>true</bool>
+               </property>
+               <property name="text">
+                <string>...</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QRadioButton" name="PVPlot_radioButton">
+            <property name="text">
+             <string>PV plot</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="PVPlotPushButton">
+            <property name="text">
+             <string>PV plot</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="label_survey">
+            <property name="text">
+             <string>Survey</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLineEdit" name="lineEdit_survey"/>
+          </item>
+          <item>
+           <widget class="QLabel" name="label_species">
+            <property name="text">
+             <string>Species</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLineEdit" name="lineEdit_species"/>
+          </item>
+          <item>
+           <widget class="QLabel" name="label_transition">
+            <property name="text">
+             <string>Transition</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLineEdit" name="lineEdit_transition"/>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="cameraControlgroupBox">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>200</width>
+              <height>16777215</height>
+             </size>
+            </property>
+            <property name="title">
+             <string>Camera control</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_5">
+             <item>
+              <widget class="QPushButton" name="cameraLeft">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/PIC_LEFT.bmp</normaloff>:/icons/PIC_LEFT.bmp</iconset>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="cameraRight">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/PIC_RIGHT.bmp</normaloff>:/icons/PIC_RIGHT.bmp</iconset>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="cameraBack">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/PIC_BACK.bmp</normaloff>:/icons/PIC_BACK.bmp</iconset>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="frontCamera">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/PIC_FRONT.bmp</normaloff>:/icons/PIC_FRONT.bmp</iconset>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="topCamera">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/PIC_TOP.bmp</normaloff>:/icons/PIC_TOP.bmp</iconset>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="bottomCamera">
+               <property name="text">
+                <string/>
+               </property>
+               <property name="icon">
+                <iconset resource="../visivo.qrc">
+                 <normaloff>:/icons/PIC_BOTTOM.bmp</normaloff>:/icons/PIC_BOTTOM.bmp</iconset>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="ThresholdGroupBox">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>200</width>
+              <height>16777215</height>
+             </size>
+            </property>
+            <property name="title">
+             <string>Threshold</string>
+            </property>
+            <layout class="QVBoxLayout" name="verticalLayout_8">
+             <item>
+              <widget class="QSlider" name="horizontalSlider_threshold">
+               <property name="orientation">
+                <enum>Qt::Horizontal</enum>
+               </property>
+               <property name="tickPosition">
+                <enum>QSlider::TicksAbove</enum>
+               </property>
+               <property name="tickInterval">
+                <number>0</number>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <layout class="QHBoxLayout" name="horizontalLayout_13">
+               <item>
+                <spacer name="horizontalSpacer_3">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>40</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLineEdit" name="thresholdValueLineEdit">
+                 <property name="enabled">
+                  <bool>true</bool>
+                 </property>
+                 <property name="sizePolicy">
+                  <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                   <horstretch>0</horstretch>
+                   <verstretch>0</verstretch>
+                  </sizepolicy>
+                 </property>
+                 <property name="maximumSize">
+                  <size>
+                   <width>50</width>
+                   <height>16777215</height>
+                  </size>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_4">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>40</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+              </layout>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="cuttingPlaneGroupBox">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>200</width>
+              <height>16777215</height>
+             </size>
+            </property>
+            <property name="title">
+             <string>Cutting plane</string>
+            </property>
+            <layout class="QVBoxLayout" name="verticalLayout_9">
+             <item>
+              <widget class="QSlider" name="cuttingPlane_Slider">
+               <property name="orientation">
+                <enum>Qt::Horizontal</enum>
+               </property>
+               <property name="tickPosition">
+                <enum>QSlider::TicksAbove</enum>
+               </property>
+               <property name="tickInterval">
+                <number>0</number>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <layout class="QHBoxLayout" name="horizontalLayout_11">
+               <item>
+                <spacer name="horizontalSpacer">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>40</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QSpinBox" name="spinBox_cuttingPlane">
+                 <property name="sizePolicy">
+                  <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                   <horstretch>0</horstretch>
+                   <verstretch>0</verstretch>
+                  </sizepolicy>
+                 </property>
+                 <property name="maximumSize">
+                  <size>
+                   <width>80</width>
+                   <height>16777215</height>
+                  </size>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="QLineEdit" name="velocityLineEdit">
+                 <property name="enabled">
+                  <bool>false</bool>
+                 </property>
+                 <property name="sizePolicy">
+                  <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                   <horstretch>0</horstretch>
+                   <verstretch>0</verstretch>
+                  </sizepolicy>
+                 </property>
+                 <property name="maximumSize">
+                  <size>
+                   <width>90</width>
+                   <height>16777215</height>
+                  </size>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_2">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>40</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+              </layout>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="contourGroupBox">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>250</width>
+              <height>16777215</height>
+             </size>
+            </property>
+            <property name="font">
+             <font>
+              <pointsize>11</pointsize>
+             </font>
+            </property>
+            <property name="title">
+             <string>Contours</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_14">
+             <item>
+              <layout class="QVBoxLayout" name="verticalLayout_4">
+               <item>
+                <widget class="QLabel" name="label_4">
+                 <property name="sizePolicy">
+                  <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+                   <horstretch>0</horstretch>
+                   <verstretch>0</verstretch>
+                  </sizepolicy>
+                 </property>
+                 <property name="maximumSize">
+                  <size>
+                   <width>60</width>
+                   <height>16777215</height>
+                  </size>
+                 </property>
+                 <property name="font">
+                  <font>
+                   <pointsize>11</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string>Level</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="QLineEdit" name="levelLineEdit">
+                 <property name="maximumSize">
+                  <size>
+                   <width>60</width>
+                   <height>16777215</height>
+                  </size>
+                 </property>
+                 <property name="text">
+                  <string>15</string>
+                 </property>
+                 <property name="maxLength">
+                  <number>4</number>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </item>
+             <item>
+              <layout class="QVBoxLayout" name="verticalLayout_5">
+               <item>
+                <widget class="QLabel" name="label_5">
+                 <property name="sizePolicy">
+                  <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+                   <horstretch>0</horstretch>
+                   <verstretch>0</verstretch>
+                  </sizepolicy>
+                 </property>
+                 <property name="maximumSize">
+                  <size>
+                   <width>80</width>
+                   <height>16777215</height>
+                  </size>
+                 </property>
+                 <property name="font">
+                  <font>
+                   <pointsize>11</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string>Lower bound</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="QLineEdit" name="lowerBoundLineEdit">
+                 <property name="maximumSize">
+                  <size>
+                   <width>80</width>
+                   <height>16777215</height>
+                  </size>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </item>
+             <item>
+              <layout class="QVBoxLayout" name="verticalLayout_7">
+               <item>
+                <widget class="QLabel" name="label_6">
+                 <property name="maximumSize">
+                  <size>
+                   <width>80</width>
+                   <height>16777215</height>
+                  </size>
+                 </property>
+                 <property name="font">
+                  <font>
+                   <pointsize>11</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string>Upper bound</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <widget class="QLineEdit" name="upperBoundLineEdit">
+                 <property name="maximumSize">
+                  <size>
+                   <width>80</width>
+                   <height>16777215</height>
+                  </size>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </item>
+             <item>
+              <widget class="QCheckBox" name="contourCheckBox">
+               <property name="text">
+                <string/>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="label_channels">
+            <property name="text">
+             <string>Range</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QSpinBox" name="spinBox_channels"/>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="valueGroupBox">
+            <property name="title">
+             <string>Value</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_12">
+             <item>
+              <widget class="QGroupBox" name="groupBox">
+               <property name="title">
+                <string>Datacube</string>
+               </property>
+               <layout class="QHBoxLayout" name="horizontalLayout_15">
+                <item>
+                 <layout class="QVBoxLayout" name="verticalLayout_2">
+                  <item>
+                   <widget class="QLabel" name="label">
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                    <property name="text">
+                     <string>Min</string>
+                    </property>
+                   </widget>
+                  </item>
+                  <item>
+                   <widget class="QLineEdit" name="minLineEdit">
+                    <property name="enabled">
+                     <bool>false</bool>
+                    </property>
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                   </widget>
+                  </item>
+                 </layout>
+                </item>
+                <item>
+                 <layout class="QVBoxLayout" name="verticalLayout">
+                  <item>
+                   <widget class="QLabel" name="label_3">
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                    <property name="text">
+                     <string>Max</string>
+                    </property>
+                   </widget>
+                  </item>
+                  <item>
+                   <widget class="QLineEdit" name="maxLineEdit">
+                    <property name="enabled">
+                     <bool>false</bool>
+                    </property>
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                   </widget>
+                  </item>
+                 </layout>
+                </item>
+                <item>
+                 <layout class="QVBoxLayout" name="verticalLayout_3">
+                  <item>
+                   <widget class="QLabel" name="label_2">
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                    <property name="text">
+                     <string>RMS</string>
+                    </property>
+                   </widget>
+                  </item>
+                  <item>
+                   <widget class="QLineEdit" name="RmsLineEdit">
+                    <property name="enabled">
+                     <bool>false</bool>
+                    </property>
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                   </widget>
+                  </item>
+                 </layout>
+                </item>
+               </layout>
+              </widget>
+             </item>
+             <item>
+              <widget class="QGroupBox" name="groupBox_2">
+               <property name="title">
+                <string>Slice</string>
+               </property>
+               <layout class="QHBoxLayout" name="horizontalLayout_17">
+                <item>
+                 <layout class="QVBoxLayout" name="verticalLayout_11">
+                  <item>
+                   <widget class="QLabel" name="label_8">
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                    <property name="text">
+                     <string>Min</string>
+                    </property>
+                   </widget>
+                  </item>
+                  <item>
+                   <widget class="QLineEdit" name="minSliceLineEdit">
+                    <property name="enabled">
+                     <bool>false</bool>
+                    </property>
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                   </widget>
+                  </item>
+                 </layout>
+                </item>
+                <item>
+                 <layout class="QVBoxLayout" name="verticalLayout_10">
+                  <item>
+                   <widget class="QLabel" name="label_7">
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                    <property name="text">
+                     <string>Max</string>
+                    </property>
+                   </widget>
+                  </item>
+                  <item>
+                   <widget class="QLineEdit" name="maxSliceLineEdit">
+                    <property name="enabled">
+                     <bool>false</bool>
+                    </property>
+                    <property name="maximumSize">
+                     <size>
+                      <width>80</width>
+                      <height>16777215</height>
+                     </size>
+                    </property>
+                   </widget>
+                  </item>
+                 </layout>
+                </item>
+               </layout>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+          <item>
+           <widget class="QGroupBox" name="selectionGroupBox">
+            <property name="title">
+             <string>Selection</string>
+            </property>
+            <layout class="QHBoxLayout" name="horizontalLayout_10">
+             <item>
+              <widget class="QPushButton" name="rectSelection">
+               <property name="text">
+                <string>Rect</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="freehandPushButton">
+               <property name="text">
+                <string>Free</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="confirmPushButton">
+               <property name="text">
+                <string>Confirm</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="resetPushButton">
+               <property name="text">
+                <string>Reset</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="horizontalLayout_7">
+          <item>
+           <widget class="QVTKWidget" name="qVTK1" native="true">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QVTKWidget" name="isocontourVtkWin" native="true">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+    <item>
+     <widget class="QSplitter" name="splitter">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <widget class="QTableWidget" name="ElementListWidget">
+       <property name="columnCount">
+        <number>2</number>
+       </property>
+       <attribute name="horizontalHeaderVisible">
+        <bool>false</bool>
+       </attribute>
+       <attribute name="horizontalHeaderStretchLastSection">
+        <bool>false</bool>
+       </attribute>
+       <attribute name="verticalHeaderVisible">
+        <bool>false</bool>
+       </attribute>
+       <column>
+        <property name="text">
+         <string>Survey</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>Species</string>
+        </property>
+       </column>
+      </widget>
+      <widget class="QTableWidget" name="tableWidget">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <attribute name="horizontalHeaderVisible">
+        <bool>false</bool>
+       </attribute>
+       <attribute name="horizontalHeaderStretchLastSection">
+        <bool>false</bool>
+       </attribute>
+       <attribute name="verticalHeaderStretchLastSection">
+        <bool>false</bool>
+       </attribute>
+       <column>
+        <property name="text">
+         <string>Selected</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>Name</string>
+        </property>
+       </column>
+      </widget>
+      <widget class="QListWidget" name="listWidget">
+       <property name="alternatingRowColors">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>3990</width>
+     <height>22</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+   </widget>
+   <widget class="QMenu" name="menuWindow">
+    <property name="title">
+     <string>Window</string>
+    </property>
+    <addaction name="actionTools"/>
+    <addaction name="actionInfo"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuWindow"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionTools">
+   <property name="text">
+    <string>Tools</string>
+   </property>
+   <property name="visible">
+    <bool>false</bool>
+   </property>
+  </action>
+  <action name="actionInfo">
+   <property name="text">
+    <string>Info</string>
+   </property>
+  </action>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QVTKWidget</class>
+   <extends>QWidget</extends>
+   <header>QVTKWidget.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="../visivo.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/Code/visivo.qrc b/Code/visivo.qrc
new file mode 100644
index 0000000000000000000000000000000000000000..99fc99bae9a603a8de25f98ab50753f76e9d63f9
--- /dev/null
+++ b/Code/visivo.qrc
@@ -0,0 +1,66 @@
+<RCC>
+    <qresource prefix="/">
+        <file>icons/FILE_NEW.bmp</file>
+        <file>icons/FILE_SAVE.bmp</file>
+        <file>icons/FLYTO.bmp</file>
+        <file>icons/NODE_BLUE.bmp</file>
+        <file>icons/OP_Merger.png</file>
+        <file>icons/PIC_TOP.bmp</file>
+        <file>icons/PRINT_PREVIEW.bmp</file>
+        <file>icons/PRINT.bmp</file>
+        <file>icons/RADIO_OFF.bmp</file>
+        <file>icons/RADIO_ON.bmp</file>
+        <file>icons/ROLLOUT_CLOSE.bmp</file>
+        <file>icons/TIME_BEGIN.bmp</file>
+        <file>icons/TIME_NEXT.bmp</file>
+        <file>icons/VisIVODesktop.icns</file>
+        <file>icons/VME_SURFACE.bmp</file>
+        <file>icons/ZOOM_SEL.bmp</file>
+        <file>icons/CHECK_OFF.bmp</file>
+        <file>icons/CHECK_ON.bmp</file>
+        <file>icons/CLOSE_SASH.bmp</file>
+        <file>icons/DISABLED.bmp</file>
+        <file>icons/FILE_OPEN.bmp</file>
+        <file>icons/INFO.bmp</file>
+        <file>icons/mafVMEVector.bmp</file>
+        <file>icons/MOVIE_RECORD.bmp</file>
+        <file>icons/NODE_GRAY.bmp</file>
+        <file>icons/NODE_RED.bmp</file>
+        <file>icons/NODE_YELLOW.bmp</file>
+        <file>icons/OP_COPY.bmp</file>
+        <file>icons/OP_CUT.bmp</file>
+        <file>icons/OP_MakePlot.png</file>
+        <file>icons/OP_Parser.png</file>
+        <file>icons/OP_PASTE.bmp</file>
+        <file>icons/OP_REDO.bmp</file>
+        <file>icons/OP_UNDO.bmp</file>
+        <file>icons/OPAddVector.png</file>
+        <file>icons/PIC_BACK.bmp</file>
+        <file>icons/PIC_BOTTOM.bmp</file>
+        <file>icons/PIC_FRONT.bmp</file>
+        <file>icons/PIC_LEFT.bmp</file>
+        <file>icons/PIC_RIGHT.bmp</file>
+        <file>icons/ROLLOUT_OPEN.bmp</file>
+        <file>icons/TIME_END.bmp</file>
+        <file>icons/TIME_PLAY.bmp</file>
+        <file>icons/TIME_PREV.bmp</file>
+        <file>icons/TIME_STOP.bmp</file>
+        <file>icons/VBT_CLOUD.bmp</file>
+        <file>icons/VBT_Table.bmp</file>
+        <file>icons/VBT_Volume.bmp</file>
+        <file>icons/VME_FEM.bmp</file>
+        <file>icons/VME_IMAGE.bmp</file>
+        <file>icons/VME_LANDMARK.bmp</file>
+        <file>icons/VME_VOLUME.bmp</file>
+        <file>icons/ZOOM_ALL.bmp</file>
+        <file>icons/ZOOM.bmp</file>
+        <file>icons/OPEN_REMOTE.png</file>
+        <file>icons/OPEN_LOCAL.png</file>
+        <file>icons/rect-select.png</file>
+        <file>icons/ellipse-select.png</file>
+        <file>icons/colorize.png</file>
+        <file>icons/logo3b_vl.jpg</file>
+        <file>icons/visivo.png</file>
+        <file>icons/filter.png</file>
+    </qresource>
+</RCC>